@gajae-code/coding-agent 0.11.2 → 0.11.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (101) hide show
  1. package/CHANGELOG.md +34 -0
  2. package/dist/types/cli/launch-disposition.d.ts +23 -0
  3. package/dist/types/cli/worktree-cli.d.ts +8 -25
  4. package/dist/types/commands/gc.d.ts +4 -0
  5. package/dist/types/commands/worktree.d.ts +7 -32
  6. package/dist/types/config/settings-schema.d.ts +85 -10
  7. package/dist/types/gjc-runtime/gc-runtime.d.ts +9 -0
  8. package/dist/types/gjc-runtime/state-argv.d.ts +38 -0
  9. package/dist/types/main.d.ts +1 -0
  10. package/dist/types/modes/components/custom-editor.d.ts +8 -3
  11. package/dist/types/modes/components/tool-transcript-format.d.ts +19 -0
  12. package/dist/types/modes/controllers/input-controller.d.ts +8 -2
  13. package/dist/types/modes/prompt-action-autocomplete.d.ts +4 -0
  14. package/dist/types/sdk/broker/broker.d.ts +1 -0
  15. package/dist/types/sdk/broker/discovery.d.ts +10 -0
  16. package/dist/types/sdk/broker/ensure.d.ts +24 -1
  17. package/dist/types/sdk/broker/internal.d.ts +3 -0
  18. package/dist/types/sdk/broker/session-index.d.ts +12 -0
  19. package/dist/types/sdk/broker/state-version.d.ts +2 -1
  20. package/dist/types/sdk/bus/index.d.ts +2 -0
  21. package/dist/types/sdk/session.d.ts +2 -0
  22. package/dist/types/tools/ask.d.ts +39 -12
  23. package/dist/types/tools/browser/screenshot-format.d.ts +8 -0
  24. package/dist/types/tools/browser/tab-protocol.d.ts +11 -0
  25. package/dist/types/tools/browser/tab-supervisor.d.ts +1 -1
  26. package/dist/types/tools/browser/tab-worker-path-resolver.d.ts +2 -0
  27. package/dist/types/tools/browser/tab-worker-smoke.d.ts +16 -0
  28. package/dist/types/tools/path-utils.d.ts +9 -14
  29. package/dist/types/tools/read.d.ts +2 -0
  30. package/dist/types/tools/render-utils.d.ts +1 -8
  31. package/dist/types/tools/resource-gc.d.ts +14 -0
  32. package/dist/types/tools/subagent.d.ts +1 -0
  33. package/dist/types/utils/file-mentions.d.ts +3 -3
  34. package/dist/types/utils/image-loading.d.ts +23 -0
  35. package/dist/types/utils/image-resize.d.ts +2 -0
  36. package/dist/types/utils/pasted-image-loading.d.ts +39 -0
  37. package/dist/types/utils/pasted-image-path.d.ts +21 -19
  38. package/dist/types/web/scrapers/html-to-markdown.d.ts +5 -0
  39. package/dist/types/web/scrapers/types.d.ts +1 -5
  40. package/package.json +24 -8
  41. package/scripts/run-threat-model-check.ts +60 -0
  42. package/src/cli/launch-disposition.ts +42 -0
  43. package/src/cli/worktree-cli.ts +9 -287
  44. package/src/cli.ts +10 -9
  45. package/src/commands/gc.ts +11 -1
  46. package/src/commands/sdk.ts +3 -2
  47. package/src/commands/worktree.ts +10 -53
  48. package/src/config/model-profiles.ts +15 -15
  49. package/src/config/settings-schema.ts +55 -10
  50. package/src/eval/js/shared/runtime.ts +1 -1
  51. package/src/gjc-runtime/gc-render.ts +19 -1
  52. package/src/gjc-runtime/gc-runtime.ts +68 -3
  53. package/src/gjc-runtime/state-argv.ts +292 -0
  54. package/src/gjc-runtime/state-runtime.ts +51 -212
  55. package/src/main.ts +99 -22
  56. package/src/modes/components/custom-editor.ts +103 -32
  57. package/src/modes/components/custom-provider-wizard.ts +26 -1
  58. package/src/modes/components/session-observer-overlay.ts +10 -23
  59. package/src/modes/components/tool-transcript-format.ts +72 -0
  60. package/src/modes/components/transcript-viewer-overlay.ts +42 -2
  61. package/src/modes/controllers/input-controller.ts +98 -37
  62. package/src/modes/controllers/selector-controller.ts +1 -1
  63. package/src/modes/interactive-mode.ts +13 -2
  64. package/src/modes/prompt-action-autocomplete.ts +7 -0
  65. package/src/sdk/broker/broker.ts +139 -32
  66. package/src/sdk/broker/discovery.ts +125 -0
  67. package/src/sdk/broker/ensure.ts +103 -19
  68. package/src/sdk/broker/internal.ts +10 -0
  69. package/src/sdk/broker/session-index.ts +228 -15
  70. package/src/sdk/broker/state-version.ts +5 -2
  71. package/src/sdk/bus/index.ts +4 -1
  72. package/src/sdk/bus/telegram-daemon.ts +50 -10
  73. package/src/sdk/host/host.ts +13 -1
  74. package/src/sdk/session.ts +4 -1
  75. package/src/session/internal/managed-session-scope.ts +15 -1
  76. package/src/tools/ask.ts +216 -41
  77. package/src/tools/bash-allowed-prefixes.ts +79 -29
  78. package/src/tools/browser/launch.ts +3 -1
  79. package/src/tools/browser/readable.ts +1 -1
  80. package/src/tools/browser/screenshot-format.ts +34 -0
  81. package/src/tools/browser/tab-protocol.ts +3 -0
  82. package/src/tools/browser/tab-supervisor.ts +125 -122
  83. package/src/tools/browser/tab-worker-entry.ts +33 -14
  84. package/src/tools/browser/tab-worker-path-resolver.ts +67 -0
  85. package/src/tools/browser/tab-worker-smoke.ts +87 -0
  86. package/src/tools/browser/tab-worker.ts +7 -5
  87. package/src/tools/output-meta.ts +6 -2
  88. package/src/tools/path-utils.ts +42 -35
  89. package/src/tools/read.ts +189 -64
  90. package/src/tools/render-utils.ts +1 -27
  91. package/src/tools/resource-gc.ts +123 -34
  92. package/src/tools/subagent.ts +43 -2
  93. package/src/utils/file-mentions.ts +4 -4
  94. package/src/utils/image-loading.ts +85 -34
  95. package/src/utils/image-resize.ts +16 -9
  96. package/src/utils/pasted-image-loading.ts +514 -0
  97. package/src/utils/pasted-image-path.ts +223 -98
  98. package/src/web/scrapers/html-to-markdown.ts +68 -0
  99. package/src/web/scrapers/readthedocs.ts +1 -1
  100. package/src/web/scrapers/types.ts +1 -68
  101. package/src/web/search/providers/openai-compatible.ts +76 -23
@@ -1,291 +1,13 @@
1
1
  /**
2
- * CLI handler for `gjc worktree` — list and clean up agent-managed worktrees.
2
+ * Tombstone for the removed legacy worktree cleanup API.
3
3
  *
4
- * Layout under `~/.gjc/wt/`:
5
- *
6
- * - **PR-checkout worktrees** (`tools/gh.ts`): a regular git worktree dir
7
- * containing a `.git` *file* that points back at
8
- * `<parent-repo>/.git/worktrees/<name>/`.
9
- * - **Task-isolation dirs** (`task/worktree.ts`): a wrapper dir with a
10
- * `merged` subdir mounted/cloned by `natives.isoStart`. These are ephemeral
11
- * — `ensureIsolation` always `rm -rf`s the base before re-creating it, so
12
- * any leftover on disk is a leak from a crashed run.
13
- *
14
- * Legacy entries from before the encoding change keep working because git still
15
- * tracks them by branch name. This command exists to GC them on demand.
4
+ * The recursive-deletion implementation that previously lived here
5
+ * (`listWorktrees`/`clearWorktrees`) was deliberately removed and does not
6
+ * come back through this subpath. This module exists only to fail imports
7
+ * with actionable migration guidance.
16
8
  */
17
- import * as fs from "node:fs/promises";
18
- import * as path from "node:path";
19
- import { getWorktreesDir, isEnoent } from "@gajae-code/utils";
20
- import chalk from "chalk";
21
- import * as git from "../utils/git";
22
-
23
- type WorktreeKind = "pr-checkout" | "task-isolation" | "empty" | "stray";
24
-
25
- export interface WorktreeEntry {
26
- /** Absolute path to the worktree dir (or stray container) under `~/.gjc/wt/`. */
27
- path: string;
28
- /** Classification of what we found on disk. */
29
- kind: WorktreeKind;
30
- /** Parent repo root, when this is a registered git worktree. */
31
- parentRepo?: string;
32
- /** Branch name extracted from the parent's tracking file, when available. */
33
- branch?: string;
34
- /** When set, the entry is unhealthy and `gjc worktree clear` will remove it. */
35
- orphanReason?: string;
36
- }
37
-
38
- export interface ListWorktreesOptions {
39
- json: boolean;
40
- }
41
-
42
- export interface ClearWorktreesOptions {
43
- /** Remove every entry, including live PR-checkout worktrees. */
44
- all: boolean;
45
- /** Print what would be removed without touching the filesystem. */
46
- dryRun: boolean;
47
- json: boolean;
48
- }
49
-
50
- export async function listWorktrees(options: ListWorktreesOptions): Promise<void> {
51
- const entries = await scanWorktrees();
52
- if (options.json) {
53
- console.log(JSON.stringify(entries, null, 2));
54
- return;
55
- }
56
- if (entries.length === 0) {
57
- console.log(chalk.dim(`No agent-managed worktrees found under ${getWorktreesDir()}.`));
58
- return;
59
- }
60
- let live = 0;
61
- let orphaned = 0;
62
- for (const entry of entries) {
63
- const tag = entry.orphanReason ? chalk.yellow("orphaned") : chalk.green("live ");
64
- const detail = formatEntryDetail(entry);
65
- console.log(`${tag} ${entry.path}`);
66
- if (detail) console.log(` ${chalk.dim(detail)}`);
67
- if (entry.orphanReason) orphaned += 1;
68
- else live += 1;
69
- }
70
- console.log(chalk.dim(`\n${live} live · ${orphaned} orphaned · ${entries.length} total`));
71
- }
72
-
73
- export async function clearWorktrees(options: ClearWorktreesOptions): Promise<void> {
74
- const entries = await scanWorktrees();
75
- const targets = options.all ? entries : entries.filter(entry => entry.orphanReason !== undefined);
76
-
77
- if (targets.length === 0) {
78
- if (options.json) {
79
- console.log(JSON.stringify({ removed: 0, kept: entries.length }));
80
- } else {
81
- console.log(chalk.dim(options.all ? "No worktrees to remove." : "No orphaned worktrees to remove."));
82
- }
83
- return;
84
- }
85
-
86
- if (options.dryRun) {
87
- if (options.json) {
88
- console.log(JSON.stringify({ wouldRemove: targets.map(t => t.path) }, null, 2));
89
- } else {
90
- for (const target of targets) {
91
- console.log(`${chalk.yellow("would remove")} ${target.path}`);
92
- }
93
- console.log(chalk.dim(`\n${targets.length} dir${targets.length === 1 ? "" : "s"} would be removed.`));
94
- }
95
- return;
96
- }
97
-
98
- const results: { path: string; ok: boolean; error?: string }[] = [];
99
- const parentsToPrune = new Set<string>();
100
- for (const target of targets) {
101
- try {
102
- if (target.kind === "pr-checkout" && target.parentRepo && !target.orphanReason) {
103
- // Live worktree: ask git to remove it cleanly. If git refuses (locked,
104
- // dirty, etc.), fall back to fs.rm and rely on `worktree prune` to
105
- // clean the bookkeeping on the parent side.
106
- const removed = await git.worktree.tryRemove(target.parentRepo, target.path, { force: true });
107
- if (!removed) {
108
- await fs.rm(target.path, { recursive: true, force: true });
109
- parentsToPrune.add(target.parentRepo);
110
- }
111
- } else {
112
- await fs.rm(target.path, { recursive: true, force: true });
113
- if (target.parentRepo) parentsToPrune.add(target.parentRepo);
114
- }
115
- results.push({ path: target.path, ok: true });
116
- } catch (err) {
117
- results.push({ path: target.path, ok: false, error: err instanceof Error ? err.message : String(err) });
118
- }
119
- }
120
-
121
- // Best-effort: drop stale entries from each affected parent's `.git/worktrees/`.
122
- for (const parent of parentsToPrune) {
123
- try {
124
- await git.worktree.prune(parent);
125
- } catch {
126
- /* parent repo may already be gone or pruned — ignore */
127
- }
128
- }
129
-
130
- const succeeded = results.filter(r => r.ok).length;
131
- const failed = results.length - succeeded;
132
-
133
- if (options.json) {
134
- console.log(JSON.stringify({ removed: succeeded, failed, results }, null, 2));
135
- if (failed > 0) process.exitCode = 1;
136
- return;
137
- }
138
-
139
- for (const result of results) {
140
- if (result.ok) {
141
- console.log(`${chalk.green("removed")} ${result.path}`);
142
- } else {
143
- console.log(`${chalk.red("failed ")} ${result.path}`);
144
- if (result.error) console.log(` ${chalk.dim(result.error)}`);
145
- }
146
- }
147
- console.log(chalk.dim(`\n${succeeded} removed${failed > 0 ? ` · ${chalk.red(`${failed} failed`)}` : ""}`));
148
- if (failed > 0) process.exitCode = 1;
149
- }
150
-
151
- // ───────────────────────────────────────────────────────────────────────────
152
- // Scanner
153
- // ───────────────────────────────────────────────────────────────────────────
154
-
155
- async function scanWorktrees(): Promise<WorktreeEntry[]> {
156
- const root = getWorktreesDir();
157
- let topLevel: string[];
158
- try {
159
- topLevel = await fs.readdir(root);
160
- } catch (err) {
161
- if (isEnoent(err)) return [];
162
- throw err;
163
- }
164
-
165
- const entries: WorktreeEntry[] = [];
166
- for (const name of topLevel) {
167
- const dir = path.join(root, name);
168
- const stat = await fs.stat(dir).catch(() => null);
169
- if (!stat?.isDirectory()) continue;
170
-
171
- const direct = await classifyDir(dir);
172
- if (direct) {
173
- entries.push(direct);
174
- continue;
175
- }
176
-
177
- // Legacy nesting: ~/.gjc/wt/<encoded-project>/<branch-or-id>
178
- let children: string[];
179
- try {
180
- children = await fs.readdir(dir);
181
- } catch {
182
- continue;
183
- }
184
- let nested = 0;
185
- for (const child of children) {
186
- const childDir = path.join(dir, child);
187
- const childStat = await fs.stat(childDir).catch(() => null);
188
- if (!childStat?.isDirectory()) continue;
189
- const childClassified = await classifyDir(childDir);
190
- if (childClassified) {
191
- entries.push(childClassified);
192
- nested += 1;
193
- }
194
- }
195
- if (nested === 0) {
196
- entries.push({
197
- path: dir,
198
- kind: children.length === 0 ? "empty" : "stray",
199
- orphanReason: children.length === 0 ? "empty directory" : "no recognizable worktree contents",
200
- });
201
- }
202
- }
203
- return entries;
204
- }
205
-
206
- async function classifyDir(dir: string): Promise<WorktreeEntry | null> {
207
- const gitEntry = path.join(dir, ".git");
208
- const gitStat = await fs.stat(gitEntry).catch(() => null);
209
- if (gitStat?.isFile()) {
210
- return classifyPrCheckout(dir, gitEntry);
211
- }
212
- const mergedStat = await fs.stat(path.join(dir, "merged")).catch(() => null);
213
- if (mergedStat?.isDirectory()) {
214
- return {
215
- path: dir,
216
- kind: "task-isolation",
217
- orphanReason: "task-isolation leftover (no live task owns it)",
218
- };
219
- }
220
- return null;
221
- }
222
-
223
- async function classifyPrCheckout(dir: string, gitEntry: string): Promise<WorktreeEntry> {
224
- let contents: string;
225
- try {
226
- contents = await fs.readFile(gitEntry, "utf8");
227
- } catch (err) {
228
- return {
229
- path: dir,
230
- kind: "pr-checkout",
231
- orphanReason: `cannot read .git file: ${err instanceof Error ? err.message : String(err)}`,
232
- };
233
- }
234
- const match = /^gitdir:\s*(.+?)\s*$/m.exec(contents);
235
- const parentGitDir = match?.[1];
236
- if (!parentGitDir) {
237
- return { path: dir, kind: "pr-checkout", orphanReason: "malformed .git file (no gitdir line)" };
238
- }
239
- // parentGitDir is `<parent-repo>/.git/worktrees/<name>`; back out the repo root.
240
- const parentRepo = path.dirname(path.dirname(path.dirname(parentGitDir)));
241
- const branch = await readWorktreeBranch(path.join(parentGitDir, "HEAD"));
242
-
243
- const parentDirStat = await fs.stat(parentGitDir).catch(() => null);
244
- if (!parentDirStat?.isDirectory()) {
245
- return {
246
- path: dir,
247
- kind: "pr-checkout",
248
- parentRepo,
249
- branch,
250
- orphanReason: "parent repo no longer tracks this worktree",
251
- };
252
- }
253
- const parentRepoStat = await fs.stat(parentRepo).catch(() => null);
254
- if (!parentRepoStat?.isDirectory()) {
255
- return {
256
- path: dir,
257
- kind: "pr-checkout",
258
- parentRepo,
259
- branch,
260
- orphanReason: "parent repo missing",
261
- };
262
- }
263
- return { path: dir, kind: "pr-checkout", parentRepo, branch };
264
- }
265
-
266
- async function readWorktreeBranch(headFile: string): Promise<string | undefined> {
267
- try {
268
- const head = (await fs.readFile(headFile, "utf8")).trim();
269
- const refMatch = /^ref:\s*refs\/heads\/(.+)$/.exec(head);
270
- return refMatch?.[1];
271
- } catch {
272
- return undefined;
273
- }
274
- }
9
+ export {};
275
10
 
276
- function formatEntryDetail(entry: WorktreeEntry): string {
277
- const parts: string[] = [];
278
- if (entry.kind === "pr-checkout") {
279
- const repo = entry.parentRepo ? path.basename(entry.parentRepo) : "unknown repo";
280
- const branch = entry.branch ?? "unknown branch";
281
- parts.push(`${repo} · ${branch}`);
282
- } else if (entry.kind === "task-isolation") {
283
- parts.push("task-isolation sandbox");
284
- } else if (entry.kind === "empty") {
285
- parts.push("legacy project shell");
286
- } else {
287
- parts.push("unrecognized contents");
288
- }
289
- if (entry.orphanReason) parts.push(entry.orphanReason);
290
- return parts.join(" — ");
291
- }
11
+ throw new Error(
12
+ "@gajae-code/coding-agent/cli/worktree-cli was deliberately removed: the legacy worktree cleanup API (listWorktrees/clearWorktrees) is gone. Inspect leftover managed worktrees under ~/.gjc/wt manually and use `git worktree remove` or `git worktree prune` instead.",
13
+ );
package/src/cli.ts CHANGED
@@ -9,6 +9,7 @@ import { Args, type CliConfig, Command, type CommandEntry, Flags, run } from "@g
9
9
  import { APP_NAME, formatBunRuntimeError, MIN_BUN_VERSION, VERSION } from "@gajae-code/utils/dirs";
10
10
  import { runFixtureReport } from "./cli/fixture-report";
11
11
  import { isTmuxOwnerIsolationCliArgv, runTmuxOwnerIsolationCliFromStdin } from "./gjc-runtime/tmux-owner-isolation-cli";
12
+ import { smokeTestTabWorker } from "./tools/browser/tab-worker-smoke";
12
13
 
13
14
  if (Bun.semver.order(Bun.version, MIN_BUN_VERSION) < 0) {
14
15
  process.stderr.write(
@@ -252,16 +253,15 @@ function isSubcommand(first: string | undefined): boolean {
252
253
  }
253
254
 
254
255
  /**
255
- * Smoke-test entry. Spawns the stats sync worker, pings it, exits.
256
+ * Smoke-test entry. Spawns the stats sync worker and the browser tab worker, then verifies their protocol.
256
257
  *
257
- * Purpose: catch the silent worker-load regressions that hit compiled
258
- * binaries (issues #1011 and #1027). Neither `--version` nor
259
- * `stats --summary` actually spawns a Worker on a fresh install — the
260
- * sync path early-returns when no session files exist. This probe is the
261
- * minimal end-to-end test that proves `new Worker(...)` resolves and the
262
- * bundled worker module evaluates successfully. Wired into
263
- * `scripts/install-tests/run-ci.sh` so binary / source-link / tarball
264
- * installs all exercise it on every CI run.
258
+ * Purpose: catch silent compiled-worker load regressions (issues #1011,
259
+ * #1027, and #2598). Neither `--version` nor `stats --summary` spawns both
260
+ * worker entries on a fresh install. This probe proves each bundled worker
261
+ * module resolves and evaluates; the tab-worker probe also completes its
262
+ * bootstrap/closed protocol without launching a browser. Wired into
263
+ * `scripts/install-tests/run-ci.sh` so binary / source-link / tarball installs
264
+ * exercise it on every CI run.
265
265
  */
266
266
  async function runSmokeTest(): Promise<void> {
267
267
  const { smokeTestSyncWorker } = await import("@gajae-code/stats");
@@ -278,6 +278,7 @@ async function runSmokeTest(): Promise<void> {
278
278
  if (typeof h02ScoreSequenceFuzzy !== "function" || typeof h01FindBestFuzzyMatch !== "function") {
279
279
  throw new Error("smoke-test: native fuzzy exports missing from embedded addon");
280
280
  }
281
+ await smokeTestTabWorker();
281
282
  process.stdout.write("smoke-test: ok\n");
282
283
  }
283
284
 
@@ -9,9 +9,19 @@ export default class Gc extends Command {
9
9
  prune: Flags.boolean({ description: "Remove stale records (default: report only)", default: false }),
10
10
  force: Flags.boolean({ description: "Alias for --prune (eligible records only)", default: false }),
11
11
  "dry-run": Flags.boolean({ description: "Force report-only mode", default: false }),
12
+ "repair-session-index": Flags.boolean({
13
+ description: "Quarantine a corrupt session-index suffix and retain its valid prefix",
14
+ default: false,
15
+ }),
12
16
  };
13
17
 
14
- static examples = ["gjc gc", "gjc gc --json", "gjc gc --prune", "gjc gc --prune --json"];
18
+ static examples = [
19
+ "gjc gc",
20
+ "gjc gc --json",
21
+ "gjc gc --prune",
22
+ "gjc gc --prune --json",
23
+ "gjc gc --repair-session-index --json",
24
+ ];
15
25
 
16
26
  async run(): Promise<void> {
17
27
  const result = await runGjcGcCommand(this.argv, process.cwd(), process.env);
@@ -7,6 +7,7 @@ import { Settings } from "../config/settings";
7
7
  import { applyStartupModelProfiles, createSessionManager } from "../main";
8
8
  import { initializeExtensions } from "../modes/runtime-init";
9
9
  import { Broker } from "../sdk/broker/broker";
10
+ import { completeBrokerProcess } from "../sdk/broker/internal";
10
11
  import {
11
12
  type LifecycleTranscriptEvidence,
12
13
  readSessionLifecycleLaunchRequest,
@@ -439,9 +440,9 @@ export default class Sdk extends Command {
439
440
  });
440
441
  await broker.start();
441
442
  if (!broker.ownsDiscovery) return;
442
- const stop = () => void broker.stop().finally(() => process.exit(0));
443
+ const stop = () => void broker.stop();
443
444
  process.once("SIGTERM", stop);
444
445
  process.once("SIGINT", stop);
445
- await new Promise<void>(() => {});
446
+ await completeBrokerProcess(broker);
446
447
  }
447
448
  }
@@ -1,56 +1,13 @@
1
1
  /**
2
- * List and clean up agent-managed git worktrees under `~/.gjc/wt`.
2
+ * Tombstone for the removed legacy `worktree` CLI command module.
3
+ *
4
+ * The `gjc worktree`/`wt` command was unregistered during the
5
+ * workflow-surface narrowing and its implementation was deliberately
6
+ * removed. This module exists only to fail imports with actionable
7
+ * migration guidance.
3
8
  */
4
- import { Args, Command, Flags } from "@gajae-code/utils/cli";
5
- import { clearWorktrees, listWorktrees } from "../cli/worktree-cli";
9
+ export {};
6
10
 
7
- export default class Worktree extends Command {
8
- static description = "List or clear agent-managed git worktrees (~/.gjc/wt)";
9
-
10
- static aliases = ["wt"];
11
-
12
- static args = {
13
- // `list` (default) inspects the worktree dir; `clear` removes entries.
14
- // A positional action keeps `gjc worktree` (the no-arg form) useful.
15
- action: Args.string({
16
- description: "list (default) or clear",
17
- required: false,
18
- options: ["list", "clear"],
19
- default: "list",
20
- }),
21
- };
22
-
23
- static flags = {
24
- all: Flags.boolean({
25
- description: "Clear every entry, including live PR-checkout worktrees (clear)",
26
- default: false,
27
- }),
28
- "dry-run": Flags.boolean({
29
- char: "n",
30
- description: "Print what would be removed without touching the filesystem (clear)",
31
- default: false,
32
- }),
33
- json: Flags.boolean({ char: "j", description: "Emit machine-readable JSON", default: false }),
34
- };
35
-
36
- static examples = [
37
- "gjc worktree",
38
- "gjc worktree list --json",
39
- "gjc worktree clear",
40
- "gjc worktree clear --dry-run",
41
- "gjc worktree clear --all",
42
- ];
43
-
44
- async run(): Promise<void> {
45
- const { args, flags } = await this.parse(Worktree);
46
- if (args.action === "clear") {
47
- await clearWorktrees({
48
- all: flags.all ?? false,
49
- dryRun: flags["dry-run"] ?? false,
50
- json: flags.json ?? false,
51
- });
52
- return;
53
- }
54
- await listWorktrees({ json: flags.json ?? false });
55
- }
56
- }
11
+ throw new Error(
12
+ "@gajae-code/coding-agent/commands/worktree was deliberately removed: the `gjc worktree` command and its cleanup implementation are gone. Inspect leftover managed worktrees under ~/.gjc/wt manually and use `git worktree remove` or `git worktree prune` instead.",
13
+ );
@@ -136,25 +136,25 @@ export const BUILTIN_MODEL_PROFILES: readonly ModelProfileDefinition[] = [
136
136
  architect: "zai/glm-5.2:xhigh",
137
137
  }),
138
138
  profile("kimi-coding-plan-eco", ["kimi-code"], {
139
- default: "kimi-code/kimi-k2.7-code:low",
140
- executor: "kimi-code/kimi-k2.7-code:minimal",
141
- planner: "kimi-code/kimi-k2.7-code:low",
142
- critic: "kimi-code/kimi-k2.7-code:medium",
143
- architect: "kimi-code/kimi-k2.7-code:high",
139
+ default: "kimi-code/k3:low",
140
+ executor: "kimi-code/k3:low",
141
+ planner: "kimi-code/k3:low",
142
+ critic: "kimi-code/k3:high",
143
+ architect: "kimi-code/k3:high",
144
144
  }),
145
145
  profile("kimi-coding-plan-medium", ["kimi-code"], {
146
- default: "kimi-code/kimi-k2.7-code:medium",
147
- executor: "kimi-code/kimi-k2.7-code:low",
148
- planner: "kimi-code/kimi-k2.7-code:medium",
149
- critic: "kimi-code/kimi-k2.7-code:high",
150
- architect: "kimi-code/kimi-k2.7-code:xhigh",
146
+ default: "kimi-code/k3:high",
147
+ executor: "kimi-code/k3:low",
148
+ planner: "kimi-code/k3:high",
149
+ critic: "kimi-code/k3:high",
150
+ architect: "kimi-code/k3:max",
151
151
  }),
152
152
  profile("kimi-coding-plan-pro", ["kimi-code"], {
153
- default: "kimi-code/kimi-k2.7-code:xhigh",
154
- executor: "kimi-code/kimi-k2.7-code:medium",
155
- planner: "kimi-code/kimi-k2.7-code:high",
156
- critic: "kimi-code/kimi-k2.7-code:xhigh",
157
- architect: "kimi-code/kimi-k2.7-code:xhigh",
153
+ default: "kimi-code/k3:max",
154
+ executor: "kimi-code/k3:high",
155
+ planner: "kimi-code/k3:high",
156
+ critic: "kimi-code/k3:max",
157
+ architect: "kimi-code/k3:max",
158
158
  }),
159
159
  profile("mimo-eco", ["xiaomi"], {
160
160
  default: "xiaomi/mimo-v2.5-pro:low",
@@ -669,17 +669,17 @@ export const SETTINGS_SCHEMA = {
669
669
 
670
670
  "tools.readArtifactSpillThreshold": {
671
671
  type: "number",
672
- default: 0,
672
+ default: 256,
673
673
  ui: {
674
674
  tab: "tools",
675
- label: "Experimental read-tool artifact spill threshold (KB)",
675
+ label: "Read artifact spill threshold (KB)",
676
676
  description:
677
- "Advanced opt-in only: combined-size cap for `read` output across all requested ranges. Above this the full output is saved as an artifact and a bounded head+tail snippet is kept inline. Live layofflabs/gpt-5.5 medium evidence on 2026-07-07 found no token savings and a lower cache-hit rate at 50 KB, so normal coding sessions should leave this Off unless intentionally measuring large-read behavior.",
677
+ "Explicit large reads above this combined size are saved as an artifact with a bounded head-and-tail snippet inline. Bare reads, directories, and converted-document receipts remain inline.",
678
678
  options: [
679
- { value: "0", label: "Off", description: "Default; no read-specific spill (backstop only)" },
679
+ { value: "0", label: "Off", description: "No read-specific spill (backstop only)" },
680
680
  { value: "50", label: "50 KB", description: "~12.5K tokens" },
681
681
  { value: "100", label: "100 KB", description: "~25K tokens" },
682
- { value: "256", label: "256 KB", description: "~64K tokens" },
682
+ { value: "256", label: "256 KB", description: "Default; ~64K tokens" },
683
683
  { value: "512", label: "512 KB", description: "~128K tokens" },
684
684
  { value: "1000", label: "1 MB", description: "~250K tokens" },
685
685
  ],
@@ -688,17 +688,17 @@ export const SETTINGS_SCHEMA = {
688
688
 
689
689
  "tools.fileMentionInlineBytes": {
690
690
  type: "number",
691
- default: 20,
691
+ default: 10,
692
692
  ui: {
693
693
  tab: "tools",
694
694
  label: "File-mention inline cap (KB)",
695
695
  description:
696
- "Inline byte cap for auto-read `@path` file mentions, deliberately below the read-tool cap so an incidental mention injects a smaller snippet than an explicit read. The full file is still available via the read tool.",
696
+ "Inline byte cap for auto-read `@path` file mentions, aligned with the 10 KiB bare-read receipt so incidental mentions stay within the same bounded context budget. The full file is still available via the read tool.",
697
697
  options: [
698
698
  { value: "5", label: "5 KB", description: "~1.25K tokens" },
699
- { value: "10", label: "10 KB", description: "~2.5K tokens" },
700
- { value: "20", label: "20 KB", description: "Default; ~5K tokens" },
701
- { value: "50", label: "50 KB", description: "~12.5K tokens (matches read cap)" },
699
+ { value: "10", label: "10 KB", description: "Default; ~2.5K tokens" },
700
+ { value: "20", label: "20 KB", description: "~5K tokens" },
701
+ { value: "50", label: "50 KB", description: "~12.5K tokens (matches bare-read receipt)" },
702
702
  ],
703
703
  },
704
704
  },
@@ -2016,6 +2016,51 @@ export const SETTINGS_SCHEMA = {
2016
2016
  ],
2017
2017
  },
2018
2018
  },
2019
+ "read.receiptBudgetLines": {
2020
+ type: "number",
2021
+ default: 50,
2022
+ ui: {
2023
+ tab: "editing",
2024
+ label: "Read Receipt Line Budget",
2025
+ description: "Maximum lines included in a bare read receipt before a selector footer is shown",
2026
+ options: [
2027
+ { value: "25", label: "25 lines" },
2028
+ { value: "50", label: "50 lines", description: "Default" },
2029
+ { value: "100", label: "100 lines" },
2030
+ { value: "200", label: "200 lines" },
2031
+ ],
2032
+ },
2033
+ },
2034
+ "read.receiptBudgetBytes": {
2035
+ type: "number",
2036
+ default: 10,
2037
+ ui: {
2038
+ tab: "editing",
2039
+ label: "Read Receipt Byte Budget (KB)",
2040
+ description: "Maximum UTF-8 body size for a bare read receipt before a selector footer is shown",
2041
+ options: [
2042
+ { value: "5", label: "5 KB", description: "~1.25K tokens" },
2043
+ { value: "10", label: "10 KB", description: "Default; ~2.5K tokens" },
2044
+ { value: "20", label: "20 KB", description: "~5K tokens" },
2045
+ { value: "50", label: "50 KB", description: "~12.5K tokens" },
2046
+ ],
2047
+ },
2048
+ },
2049
+ "read.summaryMaxBytes": {
2050
+ type: "number",
2051
+ default: 20,
2052
+ ui: {
2053
+ tab: "editing",
2054
+ label: "Read Summary Size Budget (KB)",
2055
+ description: "Maximum UTF-8 size for a structural read summary before additional units are elided",
2056
+ options: [
2057
+ { value: "10", label: "10 KB", description: "~2.5K tokens" },
2058
+ { value: "20", label: "20 KB", description: "Default; ~5K tokens" },
2059
+ { value: "50", label: "50 KB", description: "~12.5K tokens" },
2060
+ { value: "100", label: "100 KB", description: "~25K tokens" },
2061
+ ],
2062
+ },
2063
+ },
2019
2064
 
2020
2065
  "read.summarize.enabled": {
2021
2066
  type: "boolean",
@@ -4,7 +4,7 @@ import * as path from "node:path";
4
4
  import { pathToFileURL } from "node:url";
5
5
  import * as util from "node:util";
6
6
 
7
- import { logger } from "@gajae-code/utils";
7
+ import * as logger from "@gajae-code/utils/logger";
8
8
 
9
9
  import { ToolError } from "../../../tools/tool-errors";
10
10
  import { createHelpers, type HelperBundle } from "./helpers";
@@ -39,7 +39,11 @@ function renderRecord(record: GcRecord): string {
39
39
 
40
40
  export function buildGcReportText(report: GcReport): string {
41
41
  const lines: string[] = [];
42
- lines.push(report.dry_run ? "gjc gc — dry run (no changes made; pass --prune to remove)" : "gjc gc — prune");
42
+ if (report.operation === "repair_session_index") {
43
+ lines.push("gjc gc — session-index repair (other stores are report-only)");
44
+ } else {
45
+ lines.push(report.dry_run ? "gjc gc — dry run (no changes made; pass --prune to remove)" : "gjc gc — prune");
46
+ }
43
47
  lines.push("");
44
48
 
45
49
  for (const store of GC_STORES) {
@@ -53,6 +57,20 @@ export function buildGcReportText(report: GcReport): string {
53
57
  lines.push("");
54
58
  }
55
59
 
60
+ if (report.session_index) {
61
+ const index = report.session_index;
62
+ lines.push(`Session index: ${index.status}; valid prefix sequence=${index.valid_prefix_seq}`);
63
+ if (index.quarantine_path) lines.push(` Quarantined suffix: ${index.quarantine_path}`);
64
+ if (index.reason) lines.push(` ${index.reason}`);
65
+ if (index.status === "corrupt")
66
+ lines.push(" Run `gjc gc --repair-session-index` to quarantine the corrupt suffix.");
67
+ if (index.status === "unsupported")
68
+ lines.push(" Upgrade GJC before attempting a repair; no index data was changed.");
69
+ if (index.status === "repaired")
70
+ lines.push(" Restart or re-register hosts whose only registration was in the quarantined suffix.");
71
+ lines.push("");
72
+ }
73
+
56
74
  if (report.errors.length > 0) {
57
75
  lines.push(`Errors (${report.errors.length})`);
58
76
  for (const err of report.errors) lines.push(` [${err.store}/${err.scope}] ${err.message}`);