Skip to content

v1.8.0 — Your Sub-Agents Follow You To Kimi

Latest

Choose a tag to compare

@SeanningTatum SeanningTatum released this 06 Aug 22:54
· 1 commit to main since this release
2fac879

🔗 Your Sub-Agents Follow You To Kimi

The ten harness operators in this repo now run under Kimi Code as well as Claude Code — from one set of files. ui-builder still carries the frontend rules, effect-ts-enforcer still carries the five non-negotiables, verify-done-runner still carries the checklist. Switch CLI and they're all still there, because there is no second copy to keep in sync.

One symlink does it: .kimi-code/agents.claude/agents. The same trick CLAUDE.mdAGENTS.md has always used for instructions, now applied to the roster.

What you get

  • 🔗 One roster, two CLIs.kimi-code/agents is a symlink, committed at git mode 120000, so a fresh clone gets the link and not a snapshot. Add an agent under .claude/agents/ and kimi sees it with no second step.
  • 🧭 The full harness, verbatimbrain-navigator, ui-builder, design-critic, recipe-runner, test-author, span-instrumenter, effect-ts-enforcer, verify-done-runner, feature-tracker, feature-verifier. Claude's frontmatter loads as-is: Kimi parses name, description, tools (comma-string or list), disallowedTools, subagents, override, and skips unknown keys rather than rejecting the file. Read/Write/Edit/Glob/Grep/Bash are named identically in both, so the read-only operators stay read-only.
  • 🚦 harness-check check D2 — asserts the symlink resolves onto .claude/agents. Not "does the path exist" — identity. A copied directory passes an existence test right up until someone edits one side, and drift is the entire failure being prevented.
  • 🙈 .gitignore shares exactly one path/.kimi-code/* ignored, !/.kimi-code/agents un-ignored. The roster is shared; per-machine Kimi session state never enters the repo.

Five files, 55 insertions. The capability is a symlink; the rest is making sure it can't quietly stop being one.

Why not just copy the files

Kimi ships its own importer, /import-from-cc-codex, and reading its instructions is what settled the design. It copies skills, and it explicitly refuses to migrate custom agents at all"Do not migrate … custom agents/subagents."

So the supported path gives you nothing here, and the obvious workaround gives you two rosters that agree only until the next edit. A symlink has no such window. pushExistingRoot resolves every agent root through fs.realpath before use, which is exactly why pointing one at another directory works.

Found the hard way: the first draft of D2 compared $(cd .kimi-code/agents) against $(cd .claude/agents) — and when the symlink dangles, both cd calls fail, both substitutions come back empty, and "" = "" reports PASS while Kimi can load nothing at all. A false green inside the check written to prevent silent drift. Greptile caught it pre-PR; each side now resolves into its own variable and must be non-empty before comparison, and the invariant was driven into all five states — dangling, wrong target, real directory, missing, correct — with only the last one passing.

For the agent-first crowd

  • 🤖 The discovery path isn't in Kimi's public docs. It was read out of the binary — PROJECT_BRAND_DIRS = [".kimi-code/agents"], roots resolved via realpath, frontmatter parsed by parseAgentFileText — then confirmed empirically with kimi --agent <bogus>, which prints the discovered roster and costs no model call. A useful probe: it's the cheapest way to ask any Kimi workspace what it can actually see.
  • 📎 Two things deliberately don't carry over, documented in .claude/agents/README.md instead of papered over. Claude's model: is ignored — Kimi's field is model_preference — so test-author runs on Kimi's configured model rather than opus. And README.md has no frontmatter, so Kimi logs one Skipping invalid agent file WARN per session. Neither is worth editing ten agent files to satisfy a second consumer.
  • 🚧 Slash commands, hooks and skills are not shared — those are Claude-specific formats. Root AGENTS.md needed no work at all; Kimi reads it natively, which is the whole reason that file is the source of truth and CLAUDE.md the symlink.
  • ✅ Gates: harness-check 11 passed, 0 failed. The one is the pre-existing --strict grandfather ratchet — 7 shipped features still awaiting receipts, unchanged by this release. No typecheck/test run and none claimed: nothing under app/ moved, and the pre-commit hook reached that conclusion on its own ("no code changes — skipping typecheck/test"). A shell gate gets verified by being driven into every failure state, which is what happened.

Full diff: v1.7.0...v1.8.0