Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 27 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ supported integrations:
| <img width="48px" src="docs/client-cursor.jpg" alt="Cursor" /> | [Cursor](https://cursor.com/docs/hooks) | `tokenjuice install cursor` | `~/.cursor/hooks.json` |
| <img width="48px" src="docs/client-droid.png" alt="Droid" /> | [Droid (Factory CLI)](https://docs.factory.ai/cli/configuration/hooks-guide) | `tokenjuice install droid` | `~/.factory/settings.json` |
| <img width="48px" src="docs/client-copilot.png" alt="GitHub Copilot CLI" /> | [GitHub Copilot CLI](https://github.com/github/copilot-cli) | `tokenjuice install copilot-cli` | `~/.copilot/hooks/tokenjuice-cli.json` |
| <img width="48px" src="docs/client-kiro.svg" alt="Kiro" /> | [Kiro CLI 2.x](https://kiro.dev/) ([integration notes](docs/kiro-integration.md)) | `tokenjuice install kiro` | `.kiro/agents/tokenjuice.json` + `.kiro/steering/tokenjuice.md` |
| <img width="48px" src="docs/client-openclaw.jpg" alt="OpenClaw" /> | [OpenClaw](https://openclaw.ai/) | `openclaw config set plugins.entries.tokenjuice.enabled true` | `~/.openclaw/openclaw.json` |
| <img width="48px" src="docs/client-opencode.png" alt="OpenCode" /> | [OpenCode](https://opencode.ai/) | `tokenjuice install opencode` | `~/.config/opencode/plugins/tokenjuice.js` |
| <img width="48px" src="docs/client-pi.png" alt="pi" /> | [pi](https://github.com/badlogic/pi-mono/tree/main/packages/coding-agent) | `tokenjuice install pi` | `~/.pi/agent/extensions/tokenjuice.js` |
Expand Down Expand Up @@ -89,7 +90,6 @@ beta integrations:
| <img width="48px" src="docs/client-jules.svg" alt="Jules" /> | [Jules](https://jules.google/docs/) | `tokenjuice install jules` | `AGENTS.md` |
| <img width="48px" src="docs/client-leanctl.svg" alt="LeanCTL" /> | [LeanCTL](https://leanctl.com/docs/configuration) | `tokenjuice install leanctl` | `.leanctl/instructions.md` |
| <img width="48px" src="docs/client-kimi.svg" alt="Kimi" /> | [Kimi Code CLI](https://moonshotai.github.io/kimi-cli/en/) | `tokenjuice install kimi` | `~/.kimi/config.toml` |
| <img width="48px" src="docs/client-kiro.svg" alt="Kiro" /> | [Kiro](https://kiro.dev/) | `tokenjuice install kiro` | `.kiro/steering/tokenjuice.md` |
| <img width="48px" src="docs/client-kilo.svg" alt="Kilo Code" /> | [Kilo Code](https://kilocode.ai/) | `tokenjuice install kilo` | `kilo.jsonc` or `.kilo/kilo.jsonc` + `.kilo/rules/tokenjuice.md` |
| <img width="48px" src="docs/client-localcode.svg" alt="LocalCode" /> | [LocalCode](https://www.localcode.codes/) | `tokenjuice install localcode` | `~/.localcode/plugins/tokenjuice/` |
| <img width="48px" src="docs/client-mcp-agent.svg" alt="mcp-agent" /> | [mcp-agent](https://docs.mcp-agent.com/) | `tokenjuice install mcp-agent` | `.mcp-agent/agents/tokenjuice.md` |
Expand Down Expand Up @@ -153,6 +153,32 @@ openclaw config set plugins.entries.tokenjuice.enabled true

this requires OpenClaw `2026.4.22` or newer.

### kiro cli

from the workspace root, install and validate the Kiro CLI 2.x agent, then start
Kiro with that agent:

```bash
tokenjuice install kiro
tokenjuice doctor kiro
kiro-cli agent validate --path .kiro/agents/tokenjuice.json
kiro-cli chat --agent tokenjuice
```

while that custom agent is active, its native built-in shell guard requires
`tokenjuice wrap -- <command>`. it blocks unwrapped commands and returns an exact
wrapped retry command. use `tokenjuice wrap --raw -- <command>` as the raw escape
hatch.

if Kiro inherits `TOKENJUICE_NO_OMISSION=1`, lossless normalization and
structured rewrites still run, but filtering, adjacent dedupe, clipping,
head/tail omission, and lossy summaries do not. unset it and restart Kiro when
large outputs such as `git ls-files` need omission-based savings; already-compact
input may not get smaller.

this adapter does not install Kiro CLI 3's incompatible standalone hook schema;
see the [integration notes](docs/kiro-integration.md) for details.

## commands

```bash
Expand Down
16 changes: 11 additions & 5 deletions docs/client-kiro.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
86 changes: 69 additions & 17 deletions docs/kiro-integration.md
Original file line number Diff line number Diff line change
@@ -1,31 +1,83 @@
# Kiro integration
# Kiro CLI integration

Kiro support is beta.
Kiro CLI support uses a native `preToolUse` hook in a workspace custom agent.
The current adapter targets the Kiro CLI 2.x custom-agent hook schema.

`tokenjuice install kiro` writes an always-included steering file to
`.kiro/steering/tokenjuice.md` in the current workspace. Kiro loads steering
files from `.kiro/steering/`, so this gives Kiro IDE, CLI, and Web stable
guidance for using tokenjuice when they run terminal commands.
`tokenjuice install kiro` writes both:

- `.kiro/agents/tokenjuice.json` — a workspace agent with a native shell
`preToolUse` guard.
- `.kiro/steering/tokenjuice.md` — always-included guidance that helps the
model issue a wrapped command on its first attempt.

## Install

```bash
tokenjuice install kiro
tokenjuice doctor kiro
kiro-cli agent validate --path .kiro/agents/tokenjuice.json
```

When validating the current repository build instead of an installed
`tokenjuice` launcher, use:

```bash
pnpm build
node dist/cli/main.js install kiro --local
node dist/cli/main.js doctor kiro --local
```

Existing tokenjuice agent and steering files are backed up before install.
`KIRO_PROJECT_DIR` can override the workspace root for tests and scripted
installs.

## Use

Start Kiro CLI with the installed workspace agent:

```bash
kiro-cli chat --agent tokenjuice
```

The hook applies only while that custom agent is active. Kiro IDE and Web can
still consume the companion steering file, but the native CLI guard is scoped
to the `tokenjuice` custom agent.

## Behavior

- The steering file tells Kiro to prefer `tokenjuice wrap -- <command>` for
noisy terminal commands.
- The steering file tells Kiro to treat compacted output as authoritative.
- The only documented escape hatch is `tokenjuice wrap --raw -- <command>`.
- Existing `.kiro/steering/tokenjuice.md` content is backed up before install.
- `KIRO_PROJECT_DIR` can override the workspace root for tests and scripted
installs.
1. The steering tells Kiro to route shell commands through
`tokenjuice wrap -- <command>`.
2. The native `preToolUse` hook inspects the Kiro shell event before execution.
3. A safely wrapped command is allowed to run.
4. An unwrapped command is blocked with exit code 2. The hook returns an exact,
shell-quoted retry command that runs the complete original command through
`tokenjuice wrap --source kiro`.
5. Wrapper-external shell operators and command substitutions are rejected to
prevent a suffix from bypassing compaction. Put compound commands inside the
wrapped shell instead:

```bash
tokenjuice wrap -- sh -lc 'command-one && command-two'
```

Raw output remains available through the single escape hatch:

```bash
tokenjuice wrap --raw -- <command>
```

## Kiro hook limitation

Kiro CLI 2.x `preToolUse` hooks can allow or block a tool call, but cannot
rewrite `tool_input.command` in place. Its `postToolUse` hooks also cannot
replace the tool output returned to the model. The adapter therefore combines
first-attempt steering with a fail-closed native guard: normally the model
issues a wrapped command immediately; if it does not, Kiro blocks the command
and the model retries with the exact wrapped command from the hook error.

## Current beta caveat
This is a Kiro agent guardrail, not an operating-system sandbox. Other agents,
manual terminal commands, and command-capable MCP tools are outside the built-in
`shell` hook's scope.

Kiro steering files are prompt guidance, not command hooks. Kiro CLI hooks can
observe tool calls and block pre-tool usage, but their documented post-tool
behavior does not replace the tool output returned to the agent.
Kiro CLI 3 uses a different standalone `.kiro/hooks/*.json` schema. This 2.x
adapter intentionally does not emit that incompatible format.
2 changes: 1 addition & 1 deletion docs/spec.md
Original file line number Diff line number Diff line change
Expand Up @@ -408,7 +408,7 @@ supported host hooks:
| Jules | `tokenjuice install jules` | `AGENTS.md` | ✴️ Beta. Inserts a marker-delimited instruction block into the current git/project root that tells Jules to use `tokenjuice wrap` for noisy terminal commands and `tokenjuice wrap --raw -- <command>` only when raw bytes are needed; guidance-only, because Jules root `AGENTS.md` instructions do not intercept command output; see `docs/jules-integration.md` |
| LeanCTL | `tokenjuice install leanctl` | `.leanctl/instructions.md` | ✴️ Beta. Writes project instructions that tell LeanCTL to use `tokenjuice wrap` for noisy terminal commands and `tokenjuice wrap --raw -- <command>` only when raw bytes are needed; guidance-only, because LeanCTL project instructions do not intercept command output; see `docs/leanctl-integration.md` |
| Kimi Code CLI | `tokenjuice install kimi` | `~/.kimi/config.toml` | ✴️ Beta. Uses a `PostToolUse` hook for the `Shell` tool; compacted context is injected alongside the original output; honors `KIMI_SHARE_DIR`; `tokenjuice install kimi --local` is available for repo-local verification; see `docs/kimi-integration.md` |
| Kiro | `tokenjuice install kiro` | `.kiro/steering/tokenjuice.md` | ✴️ Beta. Installs an always-included steering file that tells Kiro to use `tokenjuice wrap` for noisy terminal commands and `tokenjuice wrap --raw -- <command>` only when raw bytes are needed; guidance-only, because Kiro hooks do not replace terminal command output; see `docs/kiro-integration.md` |
| Kiro CLI | `tokenjuice install kiro` | `.kiro/agents/tokenjuice.json` + `.kiro/steering/tokenjuice.md` | ✴️ Beta. Installs a Kiro CLI 2.x workspace agent with a native `preToolUse` shell guard plus always-included retry guidance. Start with `kiro-cli chat --agent tokenjuice`; safely wrapped commands run directly, while unwrapped commands are blocked and retried through `tokenjuice wrap --source kiro`. Kiro hooks cannot rewrite tool input in place or replace post-tool output; see `docs/kiro-integration.md` |
| Kilo Code | `tokenjuice install kilo` | `kilo.jsonc` or `.kilo/kilo.jsonc` + `.kilo/rules/tokenjuice.md` | ✴️ Beta. Registers a workspace rule that tells Kilo Code to use `tokenjuice wrap` for noisy terminal commands and `tokenjuice wrap --raw -- <command>` only when raw bytes are needed; guidance-only, because Kilo Code rules do not intercept tool output; see `docs/kilo-integration.md` |
| LocalCode | `tokenjuice install localcode` | `~/.localcode/plugins/tokenjuice/` | ✴️ Beta. Installs a LocalCode plugin that exposes `/tokenjuice` and `tokenjuice_compact_terminal_output` for compacting provided terminal output through `tokenjuice reduce-json`; it does not execute command strings or intercept LocalCode shell output; see `docs/localcode-integration.md` |
| mcp-agent | `tokenjuice install mcp-agent` | `.mcp-agent/agents/tokenjuice.md` | ✴️ Beta. Installs a Markdown agent definition that tells mcp-agent workflows and subagents to use `tokenjuice wrap` for noisy terminal commands and `tokenjuice wrap --raw -- <command>` only when raw bytes are needed; enable `.mcp-agent/agents` in `mcp_agent.config.yaml` `agents.search_paths`; see `docs/mcp-agent-integration.md` |
Expand Down
36 changes: 25 additions & 11 deletions src/cli/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ import { doctorJunieInstructions, installJunieInstructions, uninstallJunieInstru
import { doctorJulesInstructions, installJulesInstructions, uninstallJulesInstructions } from "../hosts/jules/index.js";
import { doctorLeanCtlInstructions, installLeanCtlInstructions, uninstallLeanCtlInstructions } from "../hosts/leanctl/index.js";
import { doctorKimiHook, installKimiHook, runKimiPostToolUseHook, uninstallKimiHook } from "../hosts/kimi/index.js";
import { doctorKiroSteering, installKiroSteering, uninstallKiroSteering } from "../hosts/kiro/index.js";
import { doctorKiroHook, installKiroHook, runKiroPreToolUseHook, uninstallKiroHook } from "../hosts/kiro/index.js";
import { doctorKiloRule, installKiloRule, uninstallKiloRule } from "../hosts/kilo/index.js";
import { doctorKnownsInstructions, installKnownsInstructions, uninstallKnownsInstructions } from "../hosts/knowns/index.js";
import { doctorLocalCodePlugin, installLocalCodePlugin, uninstallLocalCodePlugin } from "../hosts/localcode/index.js";
Expand Down Expand Up @@ -250,7 +250,7 @@ function printUsage(): void {
" tokenjuice install jules",
" tokenjuice install leanctl",
" tokenjuice install kimi [--local]",
" tokenjuice install kiro",
" tokenjuice install kiro [--local]",
" tokenjuice install kilo",
" tokenjuice install localcode",
" tokenjuice install mcp-agent",
Expand Down Expand Up @@ -2057,22 +2057,27 @@ async function runInstall(args: ParsedArgs): Promise<number> {
}

if (target === "kiro") {
const result = await installKiroSteering();
const result = await installKiroHook(undefined, { local: args.local });
if (args.format === "json") {
process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
return 0;
}

const details = [
{ label: "Agent", value: result.agentPath },
{ label: "Steering", value: result.steeringPath },
{ label: "Beta", value: "steering-based guidance; Kiro still owns command execution" },
{ label: "Verify", value: "tokenjuice doctor kiro" },
{ label: "Hook", value: "native Kiro PreToolUse shell guard" },
{ label: "Use", value: "kiro-cli chat --agent tokenjuice" },
{ label: "Verify", value: `tokenjuice doctor kiro${args.local ? " --local" : ""}` },
{ label: "Escape hatch", value: "tokenjuice wrap --raw -- <command>" },
];
if (result.backupPath) {
details.push({ label: "Backup", value: result.backupPath });
if (result.agentBackupPath) {
details.push({ label: "Agent backup", value: result.agentBackupPath });
}
process.stdout.write(formatInstallSuccess("kiro", "steering", details));
if (result.steeringBackupPath) {
details.push({ label: "Steering backup", value: result.steeringBackupPath });
}
process.stdout.write(formatInstallSuccess("kiro", "native hook", details));
return 0;
}

Expand Down Expand Up @@ -3579,13 +3584,15 @@ async function runUninstall(args: ParsedArgs): Promise<number> {
}

if (target === "kiro") {
const result = await uninstallKiroSteering();
const result = await uninstallKiroHook();
if (args.format === "json") {
process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
return 0;
}

process.stdout.write(`removed kiro steering: ${result.removed ? "yes" : "no"}\n`);
process.stdout.write(`removed kiro native agent: ${result.removedAgent ? "yes" : "no"}\n`);
process.stdout.write(`agent path: ${result.agentPath}\n`);
process.stdout.write(`removed kiro steering: ${result.removedSteering ? "yes" : "no"}\n`);
process.stdout.write(`steering path: ${result.steeringPath}\n`);
process.stdout.write("enable: tokenjuice install kiro\n");
return 0;
Expand Down Expand Up @@ -6857,15 +6864,20 @@ async function runDoctor(args: ParsedArgs): Promise<number> {
}

if (args.positionals[0] === "kiro") {
const report = await doctorKiroSteering();
const report = await doctorKiroHook(undefined, { local: args.local });

if (args.format === "json") {
process.stdout.write(`${JSON.stringify(report, null, 2)}\n`);
return report.status === "broken" ? 1 : 0;
}

process.stdout.write(`agent path: ${report.agentPath}\n`);
process.stdout.write(`steering path: ${report.steeringPath}\n`);
process.stdout.write(`health: ${report.status}\n`);
process.stdout.write(`expected command: ${report.expectedCommand}\n`);
if (report.detectedCommand) {
process.stdout.write(`configured command: ${report.detectedCommand}\n`);
}
if (report.issues.length > 0) {
process.stdout.write("issues:\n");
for (const issue of report.issues) {
Expand Down Expand Up @@ -7391,6 +7403,8 @@ async function main(argv = process.argv.slice(2)): Promise<number> {
return await runGeminiCliAfterToolHook(await readStdin(args.maxInputBytes));
case "grok-cli-post-tool-use":
return await runGrokCliPostToolUseHook(await readStdin(args.maxInputBytes));
case "kiro-pre-tool-use":
return await runKiroPreToolUseHook(await readStdin(args.maxInputBytes), args.wrapLauncher);
case "kimi-post-tool-use":
return await runKimiPostToolUseHook(await readStdin(args.maxInputBytes));
case "mux-post-tool-use":
Expand Down
Loading