-
Notifications
You must be signed in to change notification settings - Fork 0
fix(skills): compile the pack's own fills from --pack-dir, not the installed cache #75
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -229,6 +229,19 @@ function enumerateSkillEntries(root: string, into: Map<string, SkillEntry> = new | |
| return into; | ||
| } | ||
|
|
||
| /** The pack's own plugin identity, when it is one (a pack without a manifest is not a plugin root). */ | ||
| function packPluginIdentity(packDir: string): { name: string; version: string } | null { | ||
| const manifestPath = join(packDir, ".claude-plugin", "plugin.json"); | ||
| if (!existsSync(manifestPath)) return null; | ||
| try { | ||
| const parsed = JSON.parse(readFileSync(manifestPath, "utf8")) as { name?: unknown; version?: unknown }; | ||
| if (typeof parsed.name !== "string" || !parsed.name) return null; | ||
| return { name: parsed.name, version: typeof parsed.version === "string" ? parsed.version : "" }; | ||
| } catch { | ||
| return null; | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * A plugin may register more than one skills root (plugin.json `skills`, e.g. | ||
| * ["./skills/review", "./plugin/skills"]); the registered surface is the union | ||
|
|
@@ -472,6 +485,12 @@ async function resolve(flags: Flags): Promise<Resolved> { | |
| : flags.mattstackDir | ||
| ? resolvePluginRootsFromDir(mattstackRoot) | ||
| : resolvePluginRoots(); | ||
| // The pack being compiled is the plugin its own fills are bound as. The installed | ||
| // cache is the previous release of it, so reading fills from there inlines stale | ||
| // bodies and pins their version token one release behind -- every `check` after a | ||
| // release would report stale until the next bump. | ||
| const self = packPluginIdentity(packDir); | ||
| if (self && fullRoster.length > 0) pluginRoots.byName[self.name] = { dir: packDir, version: self.version }; | ||
|
Comment on lines
+488
to
+493
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -eu
printf '%s\n' '--- target ---'
sed -n '450,530p' commands/skills.ts
printf '%s\n' '--- resolve references ---'
sed -n '660,705p' commands/skills.ts
sed -n '780,810p' commands/skills.ts
sed -n '1100,1125p' commands/skills.ts
sed -n '1680,1715p' commands/skills.ts
printf '%s\n' '--- source loading ---'
fd -i 'sources.ts' . -x sh -c 'echo "--- $1"; sed -n "140,255p" "$1"' sh {}
printf '%s\n' '--- identity and mattstack references ---'
rg -n -C 4 'packPluginIdentity|mattstackSha|mattstackDirty|byName|name:.*mattstack|mattstack' commands lib test tests 2>/dev/null || trueRepository: m4ttstack/rt Length of output: 50369 🏁 Script executed: #!/bin/bash
set -eu
python3 - <<'PY'
from pathlib import Path
p = Path("commands/skills.ts")
text = p.read_text()
for needle in ["function packPluginIdentity", "const packPluginIdentity", "packPluginIdentity ="]:
i = text.find(needle)
if i >= 0:
start = text.rfind("\n", 0, i-1200)
print(text[start:i+2500])
break
PYRepository: m4ttstack/rt Length of output: 3952 🏁 Script executed: #!/bin/bash
set -eu
printf '%s\n' '--- resolve target ---'
sed -n '472,520p' commands/skills.ts
printf '%s\n' '--- provenance construction ---'
sed -n '500,518p' commands/skills.ts
printf '%s\n' '--- mattstack consumers ---'
rg -n -C 8 'loadStepSource|mattstackSha|mattstackDirty|pluginRoots\.byName|byName\.mattstack' commands/skills.ts lib/skills/sources.ts lib/skills --glob '*.ts'
printf '%s\n' '--- reserved-name declarations ---'
rg -n -C 5 'OWN_NAMESPACE|reserved|plugin.*name|name.*mattstack|mattstack.*name' commands lib --glob '*.ts' --glob '*.json' --glob '*.jsonc' | head -n 300
printf '%s\n' '--- skills tests ---'
fd -i 'skills' . -t f | grep -E '(__tests__|test|spec)' | head -n 100Repository: m4ttstack/rt Length of output: 50370 🏁 Script executed: #!/bin/bash
set -eu
printf '%s\n' '--- plugin roots type and initialization ---'
rg -n -C 10 'type Plugin|interface Plugin|pluginRoots|fullRoster|installed|resolvePlugin|roots' commands/skills.ts | head -n 500
printf '%s\n' '--- all manifest fixtures mentioning mattstack ---'
rg -l -i 'mattstack' . --glob 'plugin.json' --glob '*.test.ts' --glob '*.spec.ts' --glob '*.json' --glob '*.jsonc' | head -n 200Repository: m4ttstack/rt Length of output: 28947 🏁 Script executed: #!/bin/bash
set -eu
printf '%s\n' '--- root resolution implementation ---'
sed -n '330,357p' commands/skills.ts
rg -n -C 12 'function resolvePluginRoots|function buildPluginRoots|export function buildPluginRoots|type PluginRoots|interface PluginRoots' lib/skills/sources.ts
printf '%s\n' '--- command skills tests around resolution ---'
rg -n -C 10 'resolvePluginRootsFromDir|packPluginIdentity|plugin.json|mattstack|mattstackSha|mattstackDirty|--mattstack-dir|resolve\(' commands/__tests__/skills.test.ts commands/__tests__/skills-bind.test.ts commands/__tests__/skills-surface.test.ts
printf '%s\n' '--- repository-wide explicit reservation evidence ---'
rg -n -i -C 4 'reserved|own namespace|own plugin|core plugin|mattstack plugin|plugin.*mattstack|mattstack.*plugin' README.md docs commands lib marketplace packages scripts e2e --glob '*.ts' --glob '*.md' --glob '*.json' --glob '*.jsonc' 2>/dev/null | head -n 500Repository: m4ttstack/rt Length of output: 50369 🏁 Script executed: #!/bin/bash
set -eu
python3 - <<'PY'
from pathlib import Path
for name in [
"commands/__tests__/skills.test.ts",
"commands/__tests__/skills-bind.test.ts",
"commands/__tests__/skills-surface.test.ts",
"lib/skills/sources.ts",
]:
p = Path(name)
print(f"--- {name} ({sum(1 for _ in p.open())} lines) ---")
text = p.read_text()
needles = ["plugin.json", "mattstack", "resolvePluginRoots", "buildPluginRoots", "PluginRoots"]
shown = set()
lines = text.splitlines()
for i, line in enumerate(lines):
if any(n in line for n in needles):
a=max(0,i-8); b=min(len(lines),i+14)
key=(a,b)
if key not in shown:
print("\n".join(f"{j+1}: {lines[j]}" for j in range(a,b)))
print()
shown.add(key)
PYRepository: m4ttstack/rt Length of output: 50369 🏁 Script executed: #!/bin/bash
set -eu
printf '%s\n' '--- exact reservation and plugin identity references ---'
rg -n -i 'reserved|namespace|own namespace|own plugin|core plugin|plugin\.json|plugin identity|pack.*plugin|plugin.*pack' \
README.md docs commands lib marketplace packages scripts e2e \
--glob '*.md' --glob '*.ts' --glob '*.json' --glob '*.jsonc' 2>/dev/null \
| grep -Ei 'mattstack|reserved|namespace|plugin\.json|identity|core|own' | head -n 400
printf '%s\n' '--- plugin package and marketplace metadata ---'
rg -n -i -C 8 '"name"\s*:\s*"mattstack"|name:\s*mattstack|\.claude-plugin/plugin\.json|plugin\.json' \
marketplace package.json lib commands e2e --glob '*.json' --glob '*.jsonc' --glob '*.ts' --glob '*.md' 2>/dev/null | head -n 500
printf '%s\n' '--- relevant source tests only ---'
rg -n -C 8 'packPluginIdentity|plugin identity|own fills|previous release|stale bodies|mattstack root|rosterless pack|pack named' \
commands/__tests__ lib/skills/__tests__ commands/skills.tsRepository: m4ttstack/rt Length of output: 50368 🏁 Script executed: #!/bin/bash
set -eu
python3 - <<'PY'
from pathlib import Path
src = Path("commands/skills.ts").read_text()
needles = [
'const self = packPluginIdentity(packDir);',
'pluginRoots.byName[self.name] = { dir: packDir, version: self.version };',
'const mattstackPlugin = pluginRoots.byName.mattstack;',
]
for n in needles:
print(f"{n!r}: {src.count(n)} occurrence(s)")
i = src.index('const self = packPluginIdentity(packDir);')
j = src.index('const invocable =', i)
print("--- assignment block ---")
print(src[i:j])
PYRepository: m4ttstack/rt Length of output: 556 Reject
🤖 Prompt for AI Agents |
||
| const invocable = fullRoster.length === 0 ? new Set<string>() : invocableRoster(pluginRoots); | ||
| const surface = readSurface(packDir); | ||
| const internalRoster = computeInternalRoster(team, packDir, surface, fullRoster); | ||
|
|
||
This file was deleted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win
Fail closed when an existing pack manifest is invalid.
If
.claude-plugin/plugin.jsonexists but is unreadable or has no validname, Lines [235-241] returnnull. Line [493] then leaves the installed plugin root selected.compileandcheckcan use the previous release’s fills again instead of reporting the pack manifest error. A present non-stringversionis also converted to""at Line [239], which can emit an empty version token throughlib/skills/sources.tsLines [238-314]. Keep the legacy fallback only for a missing manifest. Reject invalid contents before resolving plugin roots.🤖 Prompt for AI Agents