@gajae-code/coding-agent 0.11.3 → 0.11.5
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.
- package/CHANGELOG.md +47 -0
- package/dist/types/cli/notify-cli.d.ts +5 -1
- package/dist/types/cli/worktree-cli.d.ts +8 -25
- package/dist/types/commands/gc.d.ts +4 -0
- package/dist/types/commands/worktree.d.ts +7 -32
- package/dist/types/config/settings-schema.d.ts +20 -0
- package/dist/types/export/html/template.generated.d.ts +1 -1
- package/dist/types/gjc-runtime/deep-interview-state.d.ts +28 -0
- package/dist/types/gjc-runtime/gc-runtime.d.ts +9 -0
- package/dist/types/gjc-runtime/launch-tmux.d.ts +3 -0
- package/dist/types/gjc-runtime/managed-owner-admission.d.ts +42 -0
- package/dist/types/gjc-runtime/managed-owner-supervisor.d.ts +41 -0
- package/dist/types/gjc-runtime/state-argv.d.ts +38 -0
- package/dist/types/gjc-runtime/tmux-owner-isolation.d.ts +9 -0
- package/dist/types/gjc-runtime/ultragoal-owner-loss-recovery.d.ts +94 -0
- package/dist/types/gjc-runtime/ultragoal-runtime.d.ts +1 -0
- package/dist/types/harness-control-plane/owner.d.ts +3 -1
- package/dist/types/internal-urls/local-protocol.d.ts +18 -0
- package/dist/types/main.d.ts +2 -2
- package/dist/types/modes/components/ansi-display-validator.d.ts +2 -0
- package/dist/types/modes/components/custom-editor.d.ts +8 -3
- package/dist/types/modes/components/notifications-settings-editor.d.ts +4 -0
- package/dist/types/modes/components/session-observer-overlay.d.ts +3 -1
- package/dist/types/modes/components/settings-selector.d.ts +2 -0
- package/dist/types/modes/components/tool-transcript-format.d.ts +77 -0
- package/dist/types/modes/components/transcript-viewer-overlay.d.ts +3 -0
- package/dist/types/modes/controllers/goal-mode-controller.d.ts +8 -1
- package/dist/types/modes/controllers/input-controller.d.ts +8 -2
- package/dist/types/modes/controllers/selector-controller.d.ts +15 -0
- package/dist/types/modes/shared/agent-wire/workflow-gate-broker.d.ts +3 -0
- package/dist/types/modes/shared/agent-wire/workflow-gate-types.d.ts +16 -0
- package/dist/types/plan-mode/approved-plan.d.ts +1 -0
- package/dist/types/runtime-mcp/manager.d.ts +2 -0
- package/dist/types/runtime-mcp/oauth-discovery.d.ts +12 -1
- package/dist/types/sdk/broker/broker.d.ts +1 -0
- package/dist/types/sdk/broker/discovery.d.ts +10 -0
- package/dist/types/sdk/broker/ensure.d.ts +24 -1
- package/dist/types/sdk/broker/internal.d.ts +3 -0
- package/dist/types/sdk/broker/session-index.d.ts +12 -0
- package/dist/types/sdk/broker/state-version.d.ts +2 -1
- package/dist/types/sdk/bus/chat-daemon-cli.d.ts +3 -1
- package/dist/types/sdk/bus/chat-daemon-control.d.ts +39 -4
- package/dist/types/sdk/bus/config-commands.d.ts +8 -0
- package/dist/types/sdk/bus/config.d.ts +22 -0
- package/dist/types/sdk/bus/index.d.ts +162 -2
- package/dist/types/sdk/bus/notification-service.d.ts +71 -8
- package/dist/types/sdk/bus/session-control.d.ts +11 -8
- package/dist/types/sdk/bus/telegram-daemon-cli.d.ts +5 -0
- package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +6 -3
- package/dist/types/sdk/bus/telegram-daemon-control.d.ts +23 -4
- package/dist/types/sdk/bus/telegram-daemon.d.ts +214 -20
- package/dist/types/sdk/session.d.ts +16 -1
- package/dist/types/secrets/index.d.ts +4 -2
- package/dist/types/secrets/obfuscator.d.ts +1 -1
- package/dist/types/session/agent-session.d.ts +31 -2
- package/dist/types/session/artifacts.d.ts +30 -8
- package/dist/types/session/internal/managed-session-scope.d.ts +7 -0
- package/dist/types/session/internal/managed-session-storage.d.ts +63 -6
- package/dist/types/session/session-manager.d.ts +88 -18
- package/dist/types/session/session-storage.d.ts +27 -0
- package/dist/types/session/streaming-output.d.ts +4 -0
- package/dist/types/task/executor.d.ts +9 -0
- package/dist/types/tool-discovery/tool-index.d.ts +2 -0
- package/dist/types/tools/ask.d.ts +87 -12
- package/dist/types/tools/bash-skill-urls.d.ts +2 -2
- package/dist/types/tools/browser/screenshot-format.d.ts +8 -0
- package/dist/types/tools/browser/tab-protocol.d.ts +11 -0
- package/dist/types/tools/browser/tab-supervisor.d.ts +1 -1
- package/dist/types/tools/browser/tab-worker-path-resolver.d.ts +2 -0
- package/dist/types/tools/browser/tab-worker-smoke.d.ts +16 -0
- package/dist/types/tools/index.d.ts +4 -0
- package/dist/types/tools/path-utils.d.ts +9 -14
- package/dist/types/tools/render-utils.d.ts +1 -8
- package/dist/types/tools/resource-gc.d.ts +14 -0
- package/dist/types/utils/image-loading.d.ts +23 -0
- package/dist/types/utils/image-resize.d.ts +2 -0
- package/dist/types/utils/pasted-image-loading.d.ts +39 -0
- package/dist/types/utils/pasted-image-path.d.ts +21 -19
- package/dist/types/web/scrapers/html-to-markdown.d.ts +5 -0
- package/dist/types/web/scrapers/types.d.ts +1 -5
- package/package.json +25 -9
- package/scripts/generate-sdk-operation-inventory.ts +2 -0
- package/scripts/run-threat-model-check.ts +60 -0
- package/src/cli/notify-cli.ts +40 -18
- package/src/cli/worktree-cli.ts +9 -287
- package/src/cli.ts +10 -9
- package/src/commands/gc.ts +11 -1
- package/src/commands/sdk.ts +10 -4
- package/src/commands/worktree.ts +10 -53
- package/src/config/model-profiles.ts +15 -15
- package/src/config/settings-schema.ts +17 -0
- package/src/daemon/runtime.ts +2 -2
- package/src/defaults/gjc/skills/deep-interview/SKILL.md +70 -49
- package/src/defaults/gjc/skills/ralplan/SKILL.md +0 -6
- package/src/defaults/gjc/skills/team/SKILL.md +7 -9
- package/src/defaults/gjc/skills/ultragoal/SKILL.md +0 -2
- package/src/eval/js/shared/runtime.ts +1 -1
- package/src/eval/py/index.ts +2 -2
- package/src/export/html/template.generated.ts +1 -1
- package/src/extensibility/plugins/marketplace/manager.ts +94 -10
- package/src/gjc-runtime/deep-interview-recorder.ts +7 -0
- package/src/gjc-runtime/deep-interview-runtime.ts +10 -0
- package/src/gjc-runtime/deep-interview-state.ts +127 -3
- package/src/gjc-runtime/gc-render.ts +19 -1
- package/src/gjc-runtime/gc-runtime.ts +68 -3
- package/src/gjc-runtime/launch-tmux.ts +61 -6
- package/src/gjc-runtime/managed-owner-admission.ts +352 -0
- package/src/gjc-runtime/managed-owner-supervisor.ts +162 -0
- package/src/gjc-runtime/state-argv.ts +292 -0
- package/src/gjc-runtime/state-runtime.ts +80 -222
- package/src/gjc-runtime/team-runtime.ts +1 -1
- package/src/gjc-runtime/tmux-owner-isolation.ts +117 -0
- package/src/gjc-runtime/ultragoal-owner-loss-recovery.ts +328 -0
- package/src/gjc-runtime/ultragoal-runtime.ts +14 -0
- package/src/harness-control-plane/owner.ts +59 -3
- package/src/hindsight/client.ts +65 -2
- package/src/internal-urls/agent-protocol.ts +76 -5
- package/src/internal-urls/docs-index.generated.ts +7 -6
- package/src/internal-urls/local-protocol.ts +416 -18
- package/src/lsp/index.ts +4 -88
- package/src/main.ts +37 -13
- package/src/modes/acp/acp-agent.ts +8 -0
- package/src/modes/components/ansi-display-validator.ts +54 -0
- package/src/modes/components/custom-editor.ts +103 -32
- package/src/modes/components/custom-provider-wizard.ts +26 -1
- package/src/modes/components/notifications-settings-editor.ts +26 -2
- package/src/modes/components/session-observer-overlay.ts +40 -37
- package/src/modes/components/settings-selector.ts +3 -1
- package/src/modes/components/tool-transcript-format.ts +393 -0
- package/src/modes/components/transcript-viewer-overlay.ts +63 -1
- package/src/modes/controllers/goal-mode-controller.ts +20 -1
- package/src/modes/controllers/input-controller.ts +98 -37
- package/src/modes/controllers/plan-mode-controller.ts +2 -0
- package/src/modes/controllers/runtime-mcp-command-controller.ts +3 -0
- package/src/modes/controllers/selector-controller.ts +71 -9
- package/src/modes/interactive-mode.ts +14 -2
- package/src/modes/shared/agent-wire/deep-interview-gate.ts +31 -1
- package/src/modes/shared/agent-wire/workflow-gate-broker.ts +17 -1
- package/src/modes/shared/agent-wire/workflow-gate-schema.ts +4 -2
- package/src/modes/shared/agent-wire/workflow-gate-types.ts +120 -3
- package/src/plan-mode/approved-plan.ts +3 -1
- package/src/prompts/agents/architect.md +1 -0
- package/src/prompts/agents/critic.md +1 -0
- package/src/prompts/system/custom-system-prompt.md +1 -1
- package/src/prompts/system/system-prompt.md +2 -1
- package/src/prompts/tools/lsp.md +3 -3
- package/src/runtime-mcp/manager.ts +16 -1
- package/src/runtime-mcp/oauth-discovery.ts +116 -26
- package/src/sdk/broker/broker.ts +139 -32
- package/src/sdk/broker/discovery.ts +125 -0
- package/src/sdk/broker/ensure.ts +103 -19
- package/src/sdk/broker/internal.ts +10 -0
- package/src/sdk/broker/session-index.ts +309 -38
- package/src/sdk/broker/state-version.ts +5 -2
- package/src/sdk/bus/chat-daemon-cli.ts +72 -10
- package/src/sdk/bus/chat-daemon-control.ts +526 -130
- package/src/sdk/bus/chat-daemon-runtime.ts +1 -0
- package/src/sdk/bus/config-commands.ts +22 -0
- package/src/sdk/bus/config.ts +37 -0
- package/src/sdk/bus/index.ts +384 -92
- package/src/sdk/bus/lifecycle-control-runtime.ts +53 -3
- package/src/sdk/bus/notification-service.ts +284 -62
- package/src/sdk/bus/recent-activity.ts +14 -5
- package/src/sdk/bus/session-control.ts +150 -44
- package/src/sdk/bus/telegram-daemon-cli.ts +20 -14
- package/src/sdk/bus/telegram-daemon-contract.ts +6 -3
- package/src/sdk/bus/telegram-daemon-control.ts +294 -76
- package/src/sdk/bus/telegram-daemon.ts +1842 -281
- package/src/sdk/protocol/operation-inventory.generated.json +11 -0
- package/src/sdk/session.ts +155 -27
- package/src/secrets/index.ts +53 -10
- package/src/secrets/obfuscator.ts +21 -18
- package/src/secrets/regex.ts +2 -0
- package/src/session/agent-session.ts +495 -253
- package/src/session/artifacts.ts +188 -23
- package/src/session/context-estimation.ts +7 -2
- package/src/session/internal/managed-session-scope.ts +215 -39
- package/src/session/internal/managed-session-storage.ts +1152 -46
- package/src/session/session-manager.ts +1055 -148
- package/src/session/session-storage.ts +109 -11
- package/src/session/streaming-output.ts +6 -0
- package/src/task/executor.ts +71 -26
- package/src/task/index.ts +63 -20
- package/src/tool-discovery/tool-index.ts +18 -0
- package/src/tools/ask.ts +329 -57
- package/src/tools/bash-allowed-prefixes.ts +79 -29
- package/src/tools/bash-skill-urls.ts +14 -3
- package/src/tools/bash.ts +2 -2
- package/src/tools/browser/launch.ts +3 -1
- package/src/tools/browser/readable.ts +1 -1
- package/src/tools/browser/screenshot-format.ts +34 -0
- package/src/tools/browser/tab-protocol.ts +3 -0
- package/src/tools/browser/tab-supervisor.ts +125 -122
- package/src/tools/browser/tab-worker-entry.ts +33 -14
- package/src/tools/browser/tab-worker-path-resolver.ts +67 -0
- package/src/tools/browser/tab-worker-smoke.ts +87 -0
- package/src/tools/browser/tab-worker.ts +7 -5
- package/src/tools/index.ts +4 -0
- package/src/tools/path-utils.ts +42 -35
- package/src/tools/plan-mode-guard.ts +1 -0
- package/src/tools/read.ts +7 -1
- package/src/tools/render-utils.ts +1 -27
- package/src/tools/resource-gc.ts +123 -34
- package/src/tools/sqlite-reader.ts +3 -2
- package/src/tools/subagent.ts +2 -0
- package/src/utils/image-loading.ts +85 -34
- package/src/utils/image-resize.ts +16 -9
- package/src/utils/pasted-image-loading.ts +514 -0
- package/src/utils/pasted-image-path.ts +223 -98
- package/src/web/scrapers/html-to-markdown.ts +68 -0
- package/src/web/scrapers/readthedocs.ts +1 -1
- package/src/web/scrapers/types.ts +1 -68
- package/src/web/search/providers/openai-compatible.ts +76 -23
|
@@ -14,6 +14,61 @@ function assert(condition: unknown, message: string): asserts condition {
|
|
|
14
14
|
if (!condition) throw new Error(message);
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
+
async function sourceFiles(root: string): Promise<string[]> {
|
|
18
|
+
const files: string[] = [];
|
|
19
|
+
for (const entry of await fs.readdir(root, { withFileTypes: true })) {
|
|
20
|
+
const file = path.join(root, entry.name);
|
|
21
|
+
if (entry.isDirectory()) files.push(...(await sourceFiles(file)));
|
|
22
|
+
else if (/\.(?:ts|tsx|js|mjs|cjs)$/.test(entry.name)) files.push(file);
|
|
23
|
+
}
|
|
24
|
+
return files;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function hasDiscoverySignalAuthority(source: string): boolean {
|
|
28
|
+
return /(?:process\.)?kill\s*\(\s*(?:discovery|brokerDiscovery|record)\.pid\s*,\s*["']SIG(?:TERM|KILL)["']/m.test(
|
|
29
|
+
source,
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
async function assertNoDiscoverySignalAuthority(): Promise<void> {
|
|
34
|
+
const roots = [path.resolve(import.meta.dir, "../src/sdk"), path.resolve(import.meta.dir, "../test")];
|
|
35
|
+
const offenders: string[] = [];
|
|
36
|
+
for (const root of roots) {
|
|
37
|
+
for (const file of await sourceFiles(root)) {
|
|
38
|
+
if (hasDiscoverySignalAuthority(await fs.readFile(file, "utf8")))
|
|
39
|
+
offenders.push(path.relative(process.cwd(), file));
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
assert(offenders.length === 0, `Discovery-derived TERM/KILL authority remains in: ${offenders.join(", ")}`);
|
|
43
|
+
assert(
|
|
44
|
+
hasDiscoverySignalAuthority('process.kill(discovery.pid, "SIGTERM")'),
|
|
45
|
+
"Synthetic forbidden discovery signal was not detected",
|
|
46
|
+
);
|
|
47
|
+
assert(
|
|
48
|
+
!hasDiscoverySignalAuthority("process.kill(discovery.pid, 0)"),
|
|
49
|
+
"PID-zero observation was escalated to signal authority",
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
async function assertFixtureControlsStayTestOnly(): Promise<void> {
|
|
54
|
+
const productionRoot = path.resolve(import.meta.dir, "../src");
|
|
55
|
+
const offenders: string[] = [];
|
|
56
|
+
for (const file of await sourceFiles(productionRoot)) {
|
|
57
|
+
const source = await fs.readFile(file, "utf8");
|
|
58
|
+
if (/\b(?:GSF1|SSH1)\b/.test(source)) offenders.push(path.relative(process.cwd(), file));
|
|
59
|
+
}
|
|
60
|
+
assert(
|
|
61
|
+
offenders.length === 0,
|
|
62
|
+
`Fixture control frames are reachable from production source: ${offenders.join(", ")}`,
|
|
63
|
+
);
|
|
64
|
+
const ensureSource = await fs.readFile(path.resolve(import.meta.dir, "../src/sdk/broker/ensure.ts"), "utf8");
|
|
65
|
+
assert(
|
|
66
|
+
ensureSource.includes("startFixtureBrokerCommandWithLeaseForTest"),
|
|
67
|
+
"Dedicated fixture launch boundary is missing its explicit ForTest label",
|
|
68
|
+
);
|
|
69
|
+
assert(!/GJC_.*(?:GSF1|SSH1|SELF_REAP)/.test(ensureSource), "Production environment can select fixture controls");
|
|
70
|
+
}
|
|
71
|
+
|
|
17
72
|
async function rejectedWebSocket(url: string): Promise<void> {
|
|
18
73
|
const ws = new WebSocket(url);
|
|
19
74
|
await new Promise<void>((resolve, reject) => {
|
|
@@ -132,6 +187,11 @@ async function main(): Promise<void> {
|
|
|
132
187
|
assert(!discovery.url.includes("0.0.0.0"), "non-loopback broker URL exists");
|
|
133
188
|
await rejectedWebSocket(`${discovery.url}/?token=wrong-token`);
|
|
134
189
|
},
|
|
190
|
+
"broker-discovery-derived-signal-authority-forbidden": assertNoDiscoverySignalAuthority,
|
|
191
|
+
"broker-pid-zero-observation-allowed": async () => {
|
|
192
|
+
assert(!hasDiscoverySignalAuthority("process.kill(discovery.pid, 0)"), "PID-zero observation was rejected");
|
|
193
|
+
},
|
|
194
|
+
"broker-fixture-control-nonproduction-reachability": assertFixtureControlsStayTestOnly,
|
|
135
195
|
};
|
|
136
196
|
for (const check of manifest.checks) {
|
|
137
197
|
try {
|
package/src/cli/notify-cli.ts
CHANGED
|
@@ -7,6 +7,7 @@ import { createInterface } from "node:readline/promises";
|
|
|
7
7
|
import { APP_NAME } from "@gajae-code/utils/dirs";
|
|
8
8
|
import chalk from "chalk";
|
|
9
9
|
import { Settings, type SettingsAtomicPatch } from "../config/settings";
|
|
10
|
+
import { isProcessIncarnation, processIncarnation } from "../sdk/broker/process-incarnation";
|
|
10
11
|
import { type EnsureChatDaemonResult, ensureDiscordDaemon, ensureSlackDaemon } from "../sdk/bus/chat-daemon-control";
|
|
11
12
|
import { getNotificationConfig, maskToken, tokenFingerprint } from "../sdk/bus/config";
|
|
12
13
|
import {
|
|
@@ -29,7 +30,11 @@ import {
|
|
|
29
30
|
sanitizeDiagnostic,
|
|
30
31
|
sendNotificationTest,
|
|
31
32
|
} from "../sdk/bus/notification-service";
|
|
32
|
-
import {
|
|
33
|
+
import {
|
|
34
|
+
type EnsureTelegramDaemonDetailedResult,
|
|
35
|
+
ensureTelegramDaemonRunningDetailed,
|
|
36
|
+
readDaemonState,
|
|
37
|
+
} from "../sdk/bus/telegram-daemon";
|
|
33
38
|
import { runDaemonInternal } from "../sdk/bus/telegram-daemon-cli";
|
|
34
39
|
import {
|
|
35
40
|
runTelegramSetup as runTelegramPairingSetup,
|
|
@@ -82,10 +87,10 @@ export interface NotifyCommandDeps {
|
|
|
82
87
|
setupTimers?: TelegramSetupTimers;
|
|
83
88
|
setupAbortSignal?: AbortSignal;
|
|
84
89
|
setupPidAlive?: (pid: number) => boolean;
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
) => Promise<
|
|
90
|
+
/** Injectable process-start provenance reader for ambient Telegram setup preflight. */
|
|
91
|
+
setupPidIncarnation?: (pid: number) => string | undefined;
|
|
92
|
+
ensureProviderDaemon?: (provider: "discord" | "slack", settings: Settings) => Promise<EnsureChatDaemonResult>;
|
|
93
|
+
ensureTelegramDaemon?: (settings: Settings) => Promise<EnsureTelegramDaemonDetailedResult>;
|
|
89
94
|
}
|
|
90
95
|
|
|
91
96
|
export function parseNotifyArgs(args: string[]): NotifyCommandArgs | undefined {
|
|
@@ -313,12 +318,22 @@ async function ensureConfiguredProviderDaemon(
|
|
|
313
318
|
provider: "discord" | "slack",
|
|
314
319
|
settings: Settings,
|
|
315
320
|
deps: NotifyCommandDeps,
|
|
316
|
-
): Promise<EnsureChatDaemonResult
|
|
321
|
+
): Promise<EnsureChatDaemonResult> {
|
|
317
322
|
try {
|
|
318
323
|
if (deps.ensureProviderDaemon) return await deps.ensureProviderDaemon(provider, settings);
|
|
319
324
|
return provider === "discord" ? await ensureDiscordDaemon(settings) : await ensureSlackDaemon(settings);
|
|
320
|
-
} catch {
|
|
321
|
-
|
|
325
|
+
} catch (error) {
|
|
326
|
+
const cfg = getNotificationConfig(settings);
|
|
327
|
+
const detail = sanitizeDiagnostic(
|
|
328
|
+
sanitizeDiagnostic(
|
|
329
|
+
error instanceof Error ? error.message : String(error),
|
|
330
|
+
provider === "discord" ? cfg.discord.botToken : cfg.slack.appToken,
|
|
331
|
+
),
|
|
332
|
+
provider === "discord" ? cfg.discord.botToken : cfg.slack.botToken,
|
|
333
|
+
);
|
|
334
|
+
throw new Error(`${provider === "discord" ? "Discord" : "Slack"} daemon did not become ready: ${detail}`, {
|
|
335
|
+
cause: error,
|
|
336
|
+
});
|
|
322
337
|
}
|
|
323
338
|
}
|
|
324
339
|
|
|
@@ -393,11 +408,13 @@ async function runTelegramSetup(cmd: NotifyCommandArgs, deps: NotifyCommandDeps)
|
|
|
393
408
|
reconcileCurrentSession: async () => undefined,
|
|
394
409
|
},
|
|
395
410
|
reconnect: async () =>
|
|
396
|
-
|
|
397
|
-
settings
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
411
|
+
deps.ensureTelegramDaemon
|
|
412
|
+
? await deps.ensureTelegramDaemon(settings)
|
|
413
|
+
: await ensureTelegramDaemonRunningDetailed({
|
|
414
|
+
settings,
|
|
415
|
+
cwd: process.cwd(),
|
|
416
|
+
sessionId: `notify-cli-${process.pid}`,
|
|
417
|
+
}),
|
|
401
418
|
persistInactive: async marker => await persistTelegramActivationMarker(settings, marker),
|
|
402
419
|
clearInactive: async marker => await clearTelegramActivationMarker(settings, marker),
|
|
403
420
|
marker: activationMarker,
|
|
@@ -446,14 +463,19 @@ async function resolveSetupPreflight(settings: Settings, deps: NotifyCommandDeps
|
|
|
446
463
|
const state = await readDaemonState(settings);
|
|
447
464
|
if (!state) return { storedChatId: cfg.chatId };
|
|
448
465
|
const validPid = Number.isSafeInteger(state.pid) && state.pid > 0;
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
if (
|
|
466
|
+
if (!validPid || !(deps.setupPidAlive ?? daemonPidAlive)(state.pid)) return { storedChatId: cfg.chatId };
|
|
467
|
+
const persistedIncarnation = state.incarnation;
|
|
468
|
+
const currentIncarnation = (deps.setupPidIncarnation ?? processIncarnation)(state.pid);
|
|
469
|
+
if (
|
|
470
|
+
!isProcessIncarnation(persistedIncarnation) ||
|
|
471
|
+
!isProcessIncarnation(currentIncarnation) ||
|
|
472
|
+
persistedIncarnation !== currentIncarnation
|
|
473
|
+
)
|
|
474
|
+
return { storedChatId: cfg.chatId };
|
|
453
475
|
return {
|
|
454
476
|
storedChatId: cfg.chatId,
|
|
455
477
|
daemon: {
|
|
456
|
-
live:
|
|
478
|
+
live: true,
|
|
457
479
|
tokenFingerprint: typeof state.tokenFingerprint === "string" ? state.tokenFingerprint : undefined,
|
|
458
480
|
chatId: typeof state.chatId === "string" ? state.chatId : undefined,
|
|
459
481
|
},
|
package/src/cli/worktree-cli.ts
CHANGED
|
@@ -1,291 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Tombstone for the removed legacy worktree cleanup API.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
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
|
-
|
|
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
|
-
|
|
277
|
-
|
|
278
|
-
|
|
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
|
|
256
|
+
* Smoke-test entry. Spawns the stats sync worker and the browser tab worker, then verifies their protocol.
|
|
256
257
|
*
|
|
257
|
-
* Purpose: catch
|
|
258
|
-
*
|
|
259
|
-
*
|
|
260
|
-
*
|
|
261
|
-
*
|
|
262
|
-
*
|
|
263
|
-
*
|
|
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
|
|
package/src/commands/gc.ts
CHANGED
|
@@ -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 = [
|
|
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);
|
package/src/commands/sdk.ts
CHANGED
|
@@ -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,
|
|
@@ -166,7 +167,7 @@ export async function openLifecycleSessionManager(
|
|
|
166
167
|
if (request.operation === "session.resume") {
|
|
167
168
|
const opened = await SessionManager.openExistingStrict(
|
|
168
169
|
snapshot.identity,
|
|
169
|
-
|
|
170
|
+
SessionManager.managedDestination(cwd, agentDir),
|
|
170
171
|
undefined,
|
|
171
172
|
migrationPolicy,
|
|
172
173
|
);
|
|
@@ -180,7 +181,12 @@ export async function openLifecycleSessionManager(
|
|
|
180
181
|
throw error;
|
|
181
182
|
}
|
|
182
183
|
} else {
|
|
183
|
-
const forked = await SessionManager.forkFromCaptured(
|
|
184
|
+
const forked = await SessionManager.forkFromCaptured(
|
|
185
|
+
snapshot,
|
|
186
|
+
cwd,
|
|
187
|
+
SessionManager.managedDestination(cwd, agentDir),
|
|
188
|
+
migrationPolicy,
|
|
189
|
+
);
|
|
184
190
|
if (forked.kind === "error")
|
|
185
191
|
throw new Error("Lifecycle saved session authority changed while the session host forked it.");
|
|
186
192
|
sessionManager = forked.manager;
|
|
@@ -439,9 +445,9 @@ export default class Sdk extends Command {
|
|
|
439
445
|
});
|
|
440
446
|
await broker.start();
|
|
441
447
|
if (!broker.ownsDiscovery) return;
|
|
442
|
-
const stop = () => void broker.stop()
|
|
448
|
+
const stop = () => void broker.stop();
|
|
443
449
|
process.once("SIGTERM", stop);
|
|
444
450
|
process.once("SIGINT", stop);
|
|
445
|
-
await
|
|
451
|
+
await completeBrokerProcess(broker);
|
|
446
452
|
}
|
|
447
453
|
}
|
package/src/commands/worktree.ts
CHANGED
|
@@ -1,56 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
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
|
-
|
|
5
|
-
import { clearWorktrees, listWorktrees } from "../cli/worktree-cli";
|
|
9
|
+
export {};
|
|
6
10
|
|
|
7
|
-
|
|
8
|
-
|
|
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
|
+
);
|