fix: gate Kimi Code scanner on its own install marker - #18
Conversation
~/.agents/skills is not Kimi's own directory — it is the shared install target skills.sh (vercel-labs) uses for several non-Kimi tools (Cline, Warp, Zed, Dexto, Loaf). The Kimi Code scanner attributed anything found there to Kimi Code unconditionally, with no check that Kimi was actually installed, so a user with one of those tools and no Kimi saw a phantom "Kimi Code" section. At user scope this was usually masked by scan-order dedupe (Claude Code's link farm wins); at project scope there was no dedupe to mask it, and the existing test suite already proved the misattribution. detectKimiCode() now runs only when its own install marker exists on disk — ~/.kimi-code at user scope, <cwd>/.kimi-code at project scope — checked independently per scope with a pure stat, no file reads. Without the marker, Kimi Code contributes nothing: no section in any report, no path in the empty-state "Looked in" list, no entry in --json paths_checked. With the marker present, output is unchanged from 0.2.3. Version 0.2.4. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
Caution The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased. |
…, doc claim
Four items from the cross-review verdict:
1. README.md's dedupe-order bullet said a Kimi-Code-only skill "still
appears under that client" unconditionally — false in the no-marker
state, which is this release's entire point. Added the qualifier
("Kimi Code: only when installed").
2. The README's terminal block, its --json block, and
assets/devcat-report.svg were regenerated from one real run of the
built 0.2.4 binary against the same 26-tool fixture the 0.2.3 example
used (same tool inventory, verified against the fixture reproducing
the current README byte-for-byte via the real published 0.2.3
tarball). This also surfaced a pre-existing staleness unrelated to
this fix: "locations checked" was already wrong on main (documented
as 14; a real 0.2.3 run reports 19). 0.2.4 correctly shows 16 — the
same 19 minus the 3 phantom project-scope Kimi paths this fix stops
claiming to have checked. Nothing else in the example changed.
3. dirExists() now reuses the existing stat() result's isDirectory(),
so a stray file named .kimi-code no longer opens the gate. No extra
syscall; a symlink to a real directory still passes (stat follows
symlinks, unchanged).
4. Corrected the PR body: 15 new tests total (8 in kimi.marker.test.ts,
6 in kimi-marker-gate.test.ts, 1 in kimi.skills.test.ts), not "13
across the two new files" as originally stated.
No version bump — still 0.2.4, unpublished.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
Caution The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: d1055d909c
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| /** Pure existence check — no file is read, matching this scanner's names-only philosophy. */ | ||
| async function kimiInstalled(opts: { cwd?: string; scope: 'project' | 'user' }): Promise<boolean> { | ||
| if (opts.scope === 'user') return dirExists(join(homedir(), '.kimi-code')); | ||
| return opts.cwd != null && (await dirExists(join(opts.cwd, '.kimi-code'))); |
There was a problem hiding this comment.
Preserve upward discovery when checking the project marker
When DevCat runs from a subdirectory of a Kimi project, this exact-cwd check returns false even if the project root contains .kimi-code/skills. The detector below deliberately uses findUpwardDir() for both Kimi skill roots because Kimi resolves them from the project root, so the early return prevents that existing upward scan and silently omits valid project skills. The project gate should recognize the marker at the same upward-resolved root rather than requiring <cwd>/.kimi-code.
Useful? React with 👍 / 👎.
Summary
~/.agents/skills/<cwd>/.agents/skillsto Kimi Code unconditionally, with no check that Kimi Code was actually installed. That directory is the shared global install target skills.sh uses for several non-Kimi tools (Cline, Warp, Zed, Dexto, Loaf) — a user with one of those and no Kimi Code saw a phantom "Kimi Code" section. At user scope it was usually masked by scan-order dedupe (Claude Code's link farm wins); at project scope there was no dedupe to mask it, and the existing test suite already proved the misattribution.detectKimiCode()insrc/manifest/kimi.tsnow runs only when its own install marker exists on disk —~/.kimi-codeat user scope,<cwd>/.kimi-codeat project scope — checked independently per scope with a purestat, no file reads. No marker → zero contribution anywhere: no section, no path in the empty-state "Looked in" list, no entry in--jsonpaths_checked. The skill goes undetected rather than misattributed (undercount-honest). Marker present → output is unchanged from 0.2.3.isDirectory()on that samestat()result — a stray file named.kimi-codeno longer opens the gate. A symlink to a real directory still does (statfollows symlinks; unchanged).package.json, bothpackage-lock.jsonversion fields, andsrc/version.ts(enforced by the existingversion.parity.test.ts).--jsonblock, andassets/devcat-report.svgare all re-captured from one real run of the built 0.2.4 binary against the same 26-tool fixture the 0.2.3 example used. This also caught a pre-existing staleness unrelated to this fix: "locations checked" was already wrong onmain(documented as 14; a real run of the publisheddevcat-cli@0.2.3against a fixture reproducing the exact same tool inventory reports 19 — confirmed against the npm registry tarball). The 0.2.4 regeneration correctly shows 16: same 19, minus the 3 phantom project-scope Kimi paths this fix stops claiming to have checked (this fixture has no project-level.kimi-code). No other number in the example changed.Test plan
test/unit/manifest/kimi.marker.test.ts(new, 8 tests) — the gate itself: no marker → zero tools + zero paths at each scope independently; bare marker → normal scan runs; project-only / user-only marker → the other scope stays empty; same fixture with the marker toggled mid-test shows only the gated paths change; a stray file named.kimi-codedoes not open the gate; a symlink to a real directory doestest/integration/kimi-marker-gate.test.ts(new, 6 tests) — full pipeline proof across the terminal report,--markdown,--json, and the empty-state "Looked in" list, for: a skills.sh-only fixture with no marker (empty stack), the same fixture mixed with a real Claude Code tool (no Kimi Code section anywhere), marker present at both scopes (Kimi Code appears, byte-consistent with unconditional scanning), and project-only marker with the user marker absenttest/unit/manifest/kimi.test.ts/kimi.skills.test.ts(existing files, 1 new test total) — existing fixtures updated to declare the install marker wherever their intent is "Kimi is genuinely installed" (preserves original coverage/assertions unchanged); added one new inverse test directly next to the original bug-shaped test (~/.agents/skills-only skill, no marker → invisible, not attributed to Kimi Code)kimi.marker.test.ts, 6 inkimi-marker-gate.test.ts, 1 inkimi.skills.test.ts(not 13 across just the two new files, as an earlier version of this description said)npm run lint,npm run build,npm test— all green (321/321 tests, 37 files)npm pack --dry-runfile list diffed byte-for-byte against the publisheddevcat-cli@0.2.3tarball from the npm registry — identical set of 69 files, only content sizes changed where expected (kimi.js,README.md,package.jsonversion)<tspan>s, equals the plain capture byte-for-byte; and feeding the same generator the real 0.2.3 capture reproduces the currently-committed SVG byte-for-byte except for the one already-known-stale line, which independently confirms the fixture and generator are both faithful🤖 Generated with Claude Code