feat(#2873): detect cross-scope shadowing and reach the local spec tree
4a - the detection floor. A shadowed install now reports which triggers are shadowed and which scope wins, at install time and through a new W028 /gsd-health diagnostic. Exit codes are untouched: a shadowed install is a warning, not a failure. Only triggers whose stem exists at BOTH scopes are reported, so a global full profile beside a local core profile no longer names local artifacts the user does not have. 4b - spec-root reachability, claude runtime and global scope only. The winning global skill stops carrying a static workflow @-include and instead resolves its spec at runtime: prefer the project-local copy, fall back to the global one, stop if neither exists. Every other @-include stays static, and the local emission is byte-identical. It runs after the staged-skills rewrite pass, whose claude branch would otherwise mangle the literal tilde path into an undocumented $HOME form. Also fixed inline: readInstallManifest classified a top-level JSON array as an installed v1 manifest, because typeof [] is object. Refs #2873
This commit is contained in:
@@ -395,6 +395,16 @@ function skillsKind(
|
||||
// undefined here); `isGlobalScope` projects it to the boolean
|
||||
// `realConverter`'s positional `isGlobal` arg requires.
|
||||
const isGlobal = isGlobalScope(scope);
|
||||
// #2873 (4b): spec-root reachability is applied LATER in the pipeline —
|
||||
// see `rewriteStagedSkillBodies` in runtime-artifact-conversion.cts, not
|
||||
// here. This stage() closure runs BEFORE the staged directory's generic
|
||||
// path-prefix rewrite pass (`applyRuntimeContentRewritesInPlace`'s
|
||||
// `case 'claude'`), which unconditionally rewrites any bare (non-`@`)
|
||||
// `~/.claude/` substring to the undocumented `$HOME/.claude/` form and
|
||||
// only restores the `@`-prefixed form. Emitting the imperative
|
||||
// tilde-path prose here would get silently mangled by that later pass;
|
||||
// it must run AFTER it instead, once the `@`-include is in its final
|
||||
// rewritten shape.
|
||||
const wrappedConverter = (content: string, skillName: string): string =>
|
||||
realConverter(content, skillName, runtime, cmdNames, isGlobal);
|
||||
return stageSkillsForRuntimeAsSkills(findInstallSourceRoot(configDir), resolved, wrappedConverter, prefix, nested, capabilityRegistry);
|
||||
|
||||
Reference in New Issue
Block a user