* test(#2755): failing-first coverage for per-runtime kimi hooks root Install/uninstall filesystem-shape rows over a sandbox HOME (no permission tricks) plus resolver unit rows. Covers both uninstall directions, which is where a fix applied only to the install call site would drift. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(#2755): resolve the kimi hooks-TOML root per runtime resolveKimiHooksTomlDir took no runtime argument and hardcoded ~/.kimi, but both kimi and kimi-code route through the single hooksSurface=kimi-hooks-toml branch. A --kimi-code install therefore wrote its [[hooks]] block, hook bundle and CommonJS marker into Kimi CLI's config file, and a --kimi-code uninstall stripped Kimi CLI's block. Adds a runtime selector to the resolver -- kimi keeps ~/.kimi + KIMI_SHARE_DIR, kimi-code gets ~/.kimi-code + KIMI_CODE_HOME, per Kimi Code's own upstream data-locations and hooks docs -- and passes the runtime at both the install and uninstall call sites. An omitted or unrecognized runtime still resolves ~/.kimi, so the exported no-arg contract is unchanged. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * test(#2755): use centralized helpers and add a divergence guard Review findings, all fixed in-PR: - The new test block reimplemented runMinimalInstall, createTempDir and toPosixPath. Extends runMinimalInstall with optional root/extraEnv instead (back-compat: every existing caller passes neither) and uses the centralized helpers, per CONTRIBUTING's Use Centralized Test Helpers rule. - Adds a parity assertion between the capability registry and the resolver: a third runtime declaring hooksSurface kimi-hooks-toml would silently inherit ~/.kimi, re-creating this very defect. The guard fires the moment those two surfaces drift. - Adds an installer-level test proving KIMI_SHARE_DIR and KIMI_CODE_HOME do not interfere when both are set, which only the resolver unit covered before. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * test(#2755): track the kimi-code hooks root in the emitted-artifact gates The remote runner caught a real ripple: moving kimi-code hooks to ~/.kimi-code made 31 emitted paths unattributable and 58 emitted hashes unexplained, because three parallel surfaces keyed on the literal .kimi path. - HOOK_CONFIG_RELATIVE_PATHS excluded only .kimi/config.toml, so kimi-code's config.toml became manifest-visible; it embeds a platform-varying node-runner command and must stay out for both products. - HOOKS_ROOTS, the package.json-marker branch and the synthesized-install-metadata pattern each named .kimi only. - tests/fixtures/install-tree/kimi-code.json still recorded the old paths; regenerated via gen:install-tree. Adds the per-PR drift acknowledgment for the 58 paths whose bytes are unchanged but whose destination moved - a ripple no source diff can show, since no hook script was edited. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(#2755): clear production-tree security advisories The remote runner's npm-integrity gate reported 2 high advisories in the production dependency tree. My diff touches neither package.json nor package-lock.json, so these come from the base -- but a red gate is not something to wave off as pre-existing, so it is fixed here rather than deferred. Lockfile-only, semver-in-range, via npm audit fix: fast-uri 3.1.4 -> 3.1.5 (host confusion via backslash authority introducer) ip-address 10.2.0 -> 10.4.0 (three SSRF / trust-boundary bypasses) hono 4.12.31 -> 4.13.0 (moderate; reverting it traded a high for a moderate, so the full remedy is taken) npm audit now reports 0 vulnerabilities at every severity, npm ci installs clean from the updated lockfile, and the build and the kimi behavior both re-verified afterwards. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * chore(#2755): backfill changeset pr numbers Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -27,6 +27,7 @@ const {
|
||||
getGlobalSkillsBase,
|
||||
resolveAntigravityGlobalDir,
|
||||
resolveKimiGlobalDir,
|
||||
resolveKimiHooksTomlDir,
|
||||
resolveConfigHomeFromDescriptor,
|
||||
resolveSkillsBaseFromDescriptor,
|
||||
detectAntigravityDirAmbiguity,
|
||||
@@ -1226,3 +1227,95 @@ describe('bug #3126: init.cjs uses runtime-homes not hardcoded .claude', () => {
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// ── resolveKimiHooksTomlDir: per-runtime hooks root (#2755) ──────────────────
|
||||
|
||||
describe('resolveKimiHooksTomlDir — per-runtime hooks root (#2755)', () => {
|
||||
// Pure path computation; the fixture home never needs to exist on disk.
|
||||
const FIXTURE_HOME = path.join(os.tmpdir(), 'gsd-2755-home-fixture');
|
||||
const at = (...seg) => path.join(FIXTURE_HOME, ...seg);
|
||||
|
||||
test('a bare call does not throw', () => {
|
||||
assert.doesNotThrow(() => resolveKimiHooksTomlDir());
|
||||
});
|
||||
|
||||
test('an omitted runtime still resolves Kimi CLI\'s ~/.kimi (back-compat)', () => {
|
||||
// The function is exported; callers and tests outside this diff pass no
|
||||
// runtime, and their destination must not move.
|
||||
assert.equal(resolveKimiHooksTomlDir({ home: FIXTURE_HOME, env: {} }), at('.kimi'));
|
||||
});
|
||||
|
||||
test('runtime "kimi" resolves ~/.kimi', () => {
|
||||
assert.equal(
|
||||
resolveKimiHooksTomlDir({ home: FIXTURE_HOME, env: {}, runtime: 'kimi' }),
|
||||
at('.kimi'),
|
||||
);
|
||||
});
|
||||
|
||||
test('runtime "kimi-code" resolves ~/.kimi-code', () => {
|
||||
assert.equal(
|
||||
resolveKimiHooksTomlDir({ home: FIXTURE_HOME, env: {}, runtime: 'kimi-code' }),
|
||||
at('.kimi-code'),
|
||||
);
|
||||
});
|
||||
|
||||
test('KIMI_SHARE_DIR overrides the kimi root', () => {
|
||||
assert.equal(
|
||||
String(resolveKimiHooksTomlDir({ home: FIXTURE_HOME, env: { KIMI_SHARE_DIR: '/share' }, runtime: 'kimi' })).replace(/\\/g, '/'),
|
||||
'/share',
|
||||
);
|
||||
});
|
||||
|
||||
test('KIMI_CODE_HOME overrides the kimi-code root', () => {
|
||||
assert.equal(
|
||||
String(resolveKimiHooksTomlDir({ home: FIXTURE_HOME, env: { KIMI_CODE_HOME: '/kcode' }, runtime: 'kimi-code' })).replace(/\\/g, '/'),
|
||||
'/kcode',
|
||||
);
|
||||
});
|
||||
|
||||
test('KIMI_SHARE_DIR does not hijack the kimi-code root', () => {
|
||||
// KIMI_SHARE_DIR is Kimi CLI's own upstream var. Before #2755 it silently
|
||||
// redirected kimi-code too — that accident was the issue's suggested
|
||||
// workaround, and the fix must make it inert.
|
||||
assert.equal(
|
||||
resolveKimiHooksTomlDir({ home: FIXTURE_HOME, env: { KIMI_SHARE_DIR: '/share' }, runtime: 'kimi-code' }),
|
||||
at('.kimi-code'),
|
||||
);
|
||||
});
|
||||
|
||||
test('KIMI_CODE_HOME does not hijack the kimi root', () => {
|
||||
assert.equal(
|
||||
resolveKimiHooksTomlDir({ home: FIXTURE_HOME, env: { KIMI_CODE_HOME: '/kcode' }, runtime: 'kimi' }),
|
||||
at('.kimi'),
|
||||
);
|
||||
});
|
||||
|
||||
test('an unknown runtime falls back to ~/.kimi', () => {
|
||||
assert.equal(
|
||||
resolveKimiHooksTomlDir({ home: FIXTURE_HOME, env: {}, runtime: 'not-a-kimi' }),
|
||||
at('.kimi'),
|
||||
);
|
||||
});
|
||||
|
||||
test('every kimi-hooks-toml runtime resolves a distinct hooks root', () => {
|
||||
// Divergence guard (CLAUDE.md → Generative Fix Divergence). The resolver
|
||||
// hardcodes the two Kimi roots while the capability registry independently
|
||||
// decides which runtimes use the kimi-hooks-toml surface. If a third one is
|
||||
// ever added without teaching the resolver its root, it silently inherits
|
||||
// ~/.kimi — which IS the #2755 defect, re-created. This fails the moment
|
||||
// those two surfaces drift apart.
|
||||
const capsDir = path.join(ROOT, 'capabilities');
|
||||
const ids = fs.readdirSync(capsDir).filter((id) => {
|
||||
const file = path.join(capsDir, id, 'capability.json');
|
||||
if (!fs.existsSync(file)) return false;
|
||||
return JSON.parse(fs.readFileSync(file, 'utf8'))?.runtime?.hooksSurface === 'kimi-hooks-toml';
|
||||
});
|
||||
|
||||
assert.deepEqual(ids.sort(), ['kimi', 'kimi-code'],
|
||||
'the kimi-hooks-toml runtime set changed — teach resolveKimiHooksTomlDir the new root, then update this list');
|
||||
|
||||
const roots = ids.map((id) => resolveKimiHooksTomlDir({ home: FIXTURE_HOME, env: {}, runtime: id }));
|
||||
assert.equal(new Set(roots).size, roots.length,
|
||||
`each kimi-hooks-toml runtime must resolve its own root; got ${JSON.stringify(roots)}`);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user