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
14 changes: 14 additions & 0 deletions docs/codebuddy-integration.md
Original file line number Diff line number Diff line change
Expand Up @@ -123,11 +123,25 @@ expected:

- `doctor codebuddy` reports `health: ok`
- the hook entry in `~/.codebuddy/settings.json` has `matcher: "Bash"` and a command containing `codebuddy-pre-tool-use --wrap-launcher ...`
- if CodeBuddy was already running when tokenjuice wrote the settings file, restart CodeBuddy or open `/hooks` and confirm the external settings change before expecting the hook to appear or fire in that session. CodeBuddy snapshots hook settings at startup and does not hot-load externally edited hooks.
- `uninstall codebuddy` removes tokenjuice-managed `PreToolUse` and legacy `PostToolUse` entries while preserving unrelated hooks
- wrapped shell commands show nested normalized command/argv in trace
- `--raw` keeps `ratio = 1`
- non-raw wraps usually produce `ratio < 1`

## runtime reload behavior

`tokenjuice install codebuddy` edits `~/.codebuddy/settings.json` from outside
CodeBuddy. CodeBuddy can read that file (`codebuddy config get hooks` shows the
installed `PreToolUse` entry), but an already-running session keeps the hook
snapshot it loaded at startup. This means `tokenjuice doctor codebuddy` can
report `health: ok` while the current CodeBuddy `/hooks` panel still does not
show the new tokenjuice entry.

After installing from another terminal, start a new CodeBuddy session. If you
intentionally edit hooks while a session is open, use `/hooks` to review and
accept the external settings change before relying on it.

## environment variables

| variable | effect |
Expand Down
2 changes: 1 addition & 1 deletion docs/spec.md
Original file line number Diff line number Diff line change
Expand Up @@ -378,7 +378,7 @@ supported host hooks:
| Codegen | `tokenjuice install codegen` | `AGENTS.md` | ✴️ Beta. Inserts a marker-delimited instruction block into the current git/project root that tells Codegen agents to use `tokenjuice wrap` for noisy terminal commands and `tokenjuice wrap --raw -- <command>` only when raw bytes are needed; guidance-only, because Codegen rule files do not intercept command output; see `docs/codegen-integration.md` |
| Coder Agents | `tokenjuice install coder-agents` | `.agents/skills/tokenjuice/SKILL.md` | ✴️ Beta. Writes a Coder workspace skill with `name: tokenjuice` frontmatter that tells Coder Agents to use `tokenjuice wrap` for noisy terminal commands and `tokenjuice wrap --raw -- <command>` only when raw bytes are needed; guidance-only, because Coder Agents skills do not intercept command output; see `docs/coder-agents-integration.md` |
| CodeRabbit | `tokenjuice install coderabbit` | `.coderabbit.yaml` | ✴️ Beta. Inserts marker-delimited `reviews.path_instructions` guidance that tells CodeRabbit review, finishing-touch, chat, and tool workflows to use `tokenjuice wrap` for noisy terminal commands and `tokenjuice wrap --raw -- <command>` only when raw bytes are needed; guidance-only, because CodeRabbit config does not intercept review comments or command output; see `docs/coderabbit-integration.md` |
| CodeBuddy (Linux/macOS/WSL) | `tokenjuice install codebuddy` | `~/.codebuddy/settings.json` | Uses `PreToolUse` shell input rewriting (same pattern as Cursor) to route Bash commands through `tokenjuice wrap`; preserves unrelated hooks that share a matcher group with the tokenjuice entry; `tokenjuice install codebuddy --local` is available for repo-local verification; native Windows shell interception is intentionally blocked for now; see `docs/codebuddy-integration.md` |
| CodeBuddy (Linux/macOS/WSL) | `tokenjuice install codebuddy` | `~/.codebuddy/settings.json` | Uses `PreToolUse` shell input rewriting (same pattern as Cursor) to route Bash commands through `tokenjuice wrap`; preserves unrelated hooks that share a matcher group with the tokenjuice entry; restart CodeBuddy after install, or use `/hooks` to confirm external settings changes in an already-running session; `tokenjuice install codebuddy --local` is available for repo-local verification; native Windows shell interception is intentionally blocked for now; see `docs/codebuddy-integration.md` |
| Command Code | `tokenjuice install command-code` | `~/.commandcode/settings.json` / `.commandcode/settings.json` | ✴️ Beta. Uses a `PostToolUse` hook with matcher `shell`; compacted context is injected through `hookSpecificOutput.additionalContext` alongside the original shell output; `tokenjuice install command-code --local` is available for repo-local verification; see `docs/command-code-integration.md` |
| Codex CLI | `tokenjuice install codex` | `~/.codex/hooks.json` | `tokenjuice install codex --local` is available for repo-local verification |
| Continue | `tokenjuice install continue` | `.continue/rules/tokenjuice.md` | ✴️ Beta. Installs a workspace rule that tells Continue agents to use `tokenjuice wrap` for noisy terminal commands and `tokenjuice wrap --raw -- <command>` only when raw bytes are needed; guidance-only, because Continue rules do not intercept tool output; see `docs/continue-integration.md` |
Expand Down
28 changes: 22 additions & 6 deletions src/cli/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { parseReduceJsonRequest } from "../core/json-protocol.js";
import { WRAP_AUTHORITATIVE_FOOTER } from "../core/compaction-metadata.js";
import { reduceExecution } from "../core/reduce.js";
import { verifyRules } from "../core/rules.js";
import { UNKNOWN_ARTIFACT_SOURCE } from "../core/source.js";
import { runWrappedCommand } from "../core/wrap.js";
import type { WrapResult } from "../types.js";
import { doctorAdalInstructions, installAdalInstructions, uninstallAdalInstructions } from "../hosts/adal/index.js";
Expand Down Expand Up @@ -1451,12 +1452,19 @@ async function runInstall(args: ParsedArgs): Promise<number> {
return 0;
}

process.stdout.write(`installed codebuddy hook: ${result.settingsPath}\n`);
process.stdout.write(`command: ${result.command}\n`);
const details = [
{ label: "Settings", value: result.settingsPath },
{ label: "Command", value: result.command },
// CodeBuddy snapshots hook settings at session startup. When tokenjuice is
// installed from another terminal, a running CodeBuddy session will not
// show or execute the new hook until the user reloads that snapshot.
{ label: "Reload", value: "restart CodeBuddy, or open /hooks and confirm the external settings change" },
{ label: "Verify", value: `tokenjuice doctor hooks${args.local ? " --local" : ""}` },
];
if (result.backupPath) {
process.stdout.write(`backup: ${result.backupPath}\n`);
details.push({ label: "Backup", value: result.backupPath });
}
process.stdout.write(`doctor: tokenjuice doctor hooks${args.local ? " --local" : ""}\n`);
process.stdout.write(formatInstallSuccess("codebuddy", "hook", details));
return 0;
}

Expand Down Expand Up @@ -7315,9 +7323,14 @@ async function runStats(args: ParsedArgs): Promise<number> {

if (report.sources && report.sources.length > 0) {
process.stdout.write("sources:\n");
for (const source of report.sources) {
const sourcesByObservedCost = [...report.sources].sort((left, right) =>
right.totals.rawChars - left.totals.rawChars
|| right.totals.savedChars - left.totals.savedChars
|| left.source.localeCompare(right.source)
);
for (const source of sourcesByObservedCost) {
process.stdout.write(
`source ${source.source}: entries=${formatMetric(source.totals.entries)} saved=${formatMetric(source.totals.savedChars)} avgRatio=${formatRatio(source.totals.avgRatio)}\n`,
`source ${source.source}: entries=${formatMetric(source.totals.entries)} raw=${formatMetric(source.totals.rawChars)} reduced=${formatMetric(source.totals.reducedChars)} saved=${formatMetric(source.totals.savedChars)} avgRatio=${formatRatio(source.totals.avgRatio)}\n`,
);
if (source.reducers.length > 0) {
process.stdout.write(
Expand All @@ -7330,6 +7343,9 @@ async function runStats(args: ParsedArgs): Promise<number> {
);
}
}
if (sourcesByObservedCost.some((source) => source.source === UNKNOWN_ARTIFACT_SOURCE)) {
process.stdout.write("note: source unknown means stored artifacts have missing or invalid source metadata, usually from older tokenjuice versions.\n");
}
}

return 0;
Expand Down
Loading