Repository navigation
Description
The problem
The operator runs the same ytstack-tracked projects (e.g. meinkontext, active .ytstack/ with M034 pointer) from four agents: Claude Code, Codex, Kimi Code and OpenCode (#28). ytstack has official adapters for the first two; Kimi Code CLI had no documented path, so a Kimi session started with zero ytstack context — no using-ytstack directive, no STATE.md snapshot, no greenfield routing — the exact "remind me what we were working on" ritual the SessionStart hook exists to prevent.
Motivating session: 2026-11-09, Kimi Code CLI in a .ytstack/ project, ytstack v0.4.2 (pinned checkout), macOS.
What works today (verified 2026-11-09)
Kimi Code CLI offers three integration surfaces; all three needed only thin wiring:
-
Skills — native, zero adaptation. Kimi scans user-declared
extra_skill_dirs(config.toml) for agentskills-styleSKILL.mdtrees and hot-reloads the catalog mid-session (verified: all 31 v0.4.2 skills appeared in the running session's catalog within a minute). The extra ytstack frontmatter fields (tier,version,allowed-tools,user-invocable,kind) are ignored gracefully; onlyname+descriptionmatter, and ytstack's description-driven selection maps 1:1 onto Kimi's model-invoked skills. -
Lifecycle hooks — three user-level wrappers. Kimi's hook system has
SessionStart,PreCompact,SessionEndevents whose stdin payload ({hook_event_name, session_id, cwd, ...}, snake_case) is field-compatible with the hooks' existing stdin contract. Config in~/.kimi-code/config.toml:
extra_skill_dirs = ["~/.kimi-code/ytstack/skills"]
[[hooks]]
event = "SessionStart"
command = "bash /Users/alex/.kimi-code/hooks/ytstack-session-start.sh"
timeout = 15
[[hooks]]
event = "PreCompact"
command = "bash /Users/alex/.kimi-code/hooks/ytstack-pre-compact.sh"
timeout = 10
[[hooks]]
event = "SessionEnd"
command = "bash /Users/alex/.kimi-code/hooks/ytstack-session-end.sh"
timeout = 10The wrappers delegate to the plugin's own helper; only SessionStart needs an unwrap, because Kimi appends raw stdout to context (see friction 1):
# ~/.kimi-code/hooks/ytstack-session-start.sh
out=$(bash "$HOME/.kimi-code/ytstack/bin/ytstack-codex" hook session-start 2>/dev/null) || exit 0
[ -n "$out" ] || exit 0
printf '%s' "$out" | jq -r '.hookSpecificOutput.additionalContext // empty'pre-compact and session-end run through the same helper with stdout discarded (they only write HANDOFF.md / journal JSONL). Kimi hooks are fail-open like ytstack's, so a missing plugin is a silent no-op.
- Same install source as feat: OpenCode host support — official adapter for skills + session-start/pre-compact hooks (working bridge attached) #28. Symlink
~/.kimi-code/ytstack -> ~/.local/share/ytstack(pinned v0.4.2 checkout). An earlier attempt pointed at the Codex plugin cache (.../0.1.6), which goes stale on every plugin update — same finding as feat: OpenCode host support — official adapter for skills + session-start/pre-compact hooks (working bridge attached) #28.
Measured: SessionStart wrapper output is ~14.4k chars on the meinkontext repo (plugin root + adapter banner + using-ytstack directive + STATE.md snapshot). Claude Code caps SessionStart additionalContext at 10k (per the v0.4.2 hook comment); Kimi documents no cap.
Friction points (suggestions)
additionalContextJSON vs plain-stdout hosts.hooks/session-startalways emits Claude's{"hookSpecificOutput":{"hookEventName":"SessionStart","additionalContext":...}}envelope. Every non-Claude host needs an unwrap step (the OpenCode bridge in feat: OpenCode host support — official adapter for skills + session-start/pre-compact hooks (working bridge attached) #28 does the same). Suggest: honor an env switch such asYTSTACK_HOOK_FORMAT=plain(orYTSTACK_HOST=kimi|opencode) and emit plain text — one branch inemit_json, removes a wrapper from every future adapter.- Injected host context is Codex-specific.
bin/ytstack-codex hook session-startprependsYTSTACK_HOST_CONTEXTbuilt fromcodex/adapter.md— instructions for Codex's skill picker ($ytstack:<name>) that do not map to Kimi (/skill:<name>invocation,Skilltool). The override mechanism exists but the helper overwrites it. Suggest: shipkimi/adapter.md, or make the banner host-neutral ("resolveytstack:<name>to this plugin'sskills/<name>/SKILL.mdvia your host's skill mechanism"). Cosmetic: the banner prints the unresolved symlink path (~/.kimi-code/ytstack) rather than the real checkout —pwd -Pin the helper would fix it. - No stable, documented plugin path per host. Both the Claude plugin cache and the Codex cache are version-pinned; any third host ends up inventing a pinned checkout plus symlink (feat: OpenCode host support — official adapter for skills + session-start/pre-compact hooks (working bridge attached) #28 and this issue did so independently). Suggest: document the pinned-checkout model as the canonical multi-host install.
- Flat skill names outside Claude. Without the plugin namespace, Kimi registers
plan-task,summarize-task, ... globally; a future user skill with the same name collides silently (first directory wins). Low risk today (the 31 curated names don't collide with the operator's existing skill set), worth a naming note in host docs. - Kimi hooks are user-level only. Kimi has no project-level hook file, so ytstack activation is per-user config, not per-repo: cloning a ytstack project onto a fresh machine does not activate hooks the way a Claude plugin install does. Host docs should state this activation/trust model.
- Unverified: Kimi's docs say SessionStart stdout "may be appended to context"; whether the directive lands in every session's context is confirmed at the next session start. Fallback is unchanged:
resume-session/ readingSTATE.mdmanually.
Out of scope (noted for completeness)
Kimi Code CLI is also an MCP client (mcp.json, HTTP transport, OAuth login). The meinkontext context MCP server (use_skill / remember / recall) could attach the same way Codex attaches it, but that is meinkontext integration, not ytstack.
Alternatives considered
- Hook-free mode (read
using-ytstack+STATE.mdby hand at session start): works, documented for Codex; rejected as the primary path because it relies on agent discipline instead of the system. - Staying on the Codex cache 0.1.6: rejected, three months stale vs v0.4.2.
Scope check
- It does not replicate a vendored superpowers / gstack skill.
- It is host plumbing (adapter + docs), in line with feat: OpenCode host support — official adapter for skills + session-start/pre-compact hooks (working bridge attached) #28 (OpenCode) and Vibe (Mistral Vibe CLI) support: skills port cleanly as a native plugin; hooks, resolver blocks and gstack-skill references do not #29 (Vibe).
- No new skill; the
.ytstack/state model is untouched. - UX contracts preserved (no skill content changed).
Activity