@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.
- package/CHANGELOG.md +34 -0
- package/dist/types/cli/launch-disposition.d.ts +23 -0
- 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 +85 -10
- package/dist/types/gjc-runtime/gc-runtime.d.ts +9 -0
- package/dist/types/gjc-runtime/state-argv.d.ts +38 -0
- package/dist/types/main.d.ts +1 -0
- package/dist/types/modes/components/custom-editor.d.ts +8 -3
- package/dist/types/modes/components/tool-transcript-format.d.ts +19 -0
- package/dist/types/modes/controllers/input-controller.d.ts +8 -2
- package/dist/types/modes/prompt-action-autocomplete.d.ts +4 -0
- 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/index.d.ts +2 -0
- package/dist/types/sdk/session.d.ts +2 -0
- package/dist/types/tools/ask.d.ts +39 -12
- 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/path-utils.d.ts +9 -14
- package/dist/types/tools/read.d.ts +2 -0
- package/dist/types/tools/render-utils.d.ts +1 -8
- package/dist/types/tools/resource-gc.d.ts +14 -0
- package/dist/types/tools/subagent.d.ts +1 -0
- package/dist/types/utils/file-mentions.d.ts +3 -3
- 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 +24 -8
- package/scripts/run-threat-model-check.ts +60 -0
- package/src/cli/launch-disposition.ts +42 -0
- 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 +3 -2
- package/src/commands/worktree.ts +10 -53
- package/src/config/model-profiles.ts +15 -15
- package/src/config/settings-schema.ts +55 -10
- package/src/eval/js/shared/runtime.ts +1 -1
- package/src/gjc-runtime/gc-render.ts +19 -1
- package/src/gjc-runtime/gc-runtime.ts +68 -3
- package/src/gjc-runtime/state-argv.ts +292 -0
- package/src/gjc-runtime/state-runtime.ts +51 -212
- package/src/main.ts +99 -22
- package/src/modes/components/custom-editor.ts +103 -32
- package/src/modes/components/custom-provider-wizard.ts +26 -1
- package/src/modes/components/session-observer-overlay.ts +10 -23
- package/src/modes/components/tool-transcript-format.ts +72 -0
- package/src/modes/components/transcript-viewer-overlay.ts +42 -2
- package/src/modes/controllers/input-controller.ts +98 -37
- package/src/modes/controllers/selector-controller.ts +1 -1
- package/src/modes/interactive-mode.ts +13 -2
- package/src/modes/prompt-action-autocomplete.ts +7 -0
- 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 +228 -15
- package/src/sdk/broker/state-version.ts +5 -2
- package/src/sdk/bus/index.ts +4 -1
- package/src/sdk/bus/telegram-daemon.ts +50 -10
- package/src/sdk/host/host.ts +13 -1
- package/src/sdk/session.ts +4 -1
- package/src/session/internal/managed-session-scope.ts +15 -1
- package/src/tools/ask.ts +216 -41
- package/src/tools/bash-allowed-prefixes.ts +79 -29
- 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/output-meta.ts +6 -2
- package/src/tools/path-utils.ts +42 -35
- package/src/tools/read.ts +189 -64
- package/src/tools/render-utils.ts +1 -27
- package/src/tools/resource-gc.ts +123 -34
- package/src/tools/subagent.ts +43 -2
- package/src/utils/file-mentions.ts +4 -4
- 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
|
@@ -42,6 +42,7 @@ import {
|
|
|
42
42
|
SessionResolutionError,
|
|
43
43
|
writeSessionActivityMarker,
|
|
44
44
|
} from "./session-resolution";
|
|
45
|
+
import { classifyStateArgv, firstStateFlagValue, type StateAction, type StateArgvClassification } from "./state-argv";
|
|
45
46
|
import { renderStateGraph, type StateGraphFormat } from "./state-graph";
|
|
46
47
|
import { migrateAndPersistLegacyState, migrateWorkflowState } from "./state-migrations";
|
|
47
48
|
import {
|
|
@@ -72,7 +73,7 @@ import {
|
|
|
72
73
|
writeGuardedWorkflowEnvelopeAtomic,
|
|
73
74
|
} from "./state-writer";
|
|
74
75
|
import { assertSafePathComponent, CommandError, flagValue, hasFlag, isPlainObject } from "./workflow-cli-common";
|
|
75
|
-
import { getSkillManifest, isKnownWorkflowState, isValidTransition
|
|
76
|
+
import { getSkillManifest, isKnownWorkflowState, isValidTransition } from "./workflow-manifest";
|
|
76
77
|
|
|
77
78
|
/**
|
|
78
79
|
* Native implementation of the `gjc state read|write|clear` command surface.
|
|
@@ -99,136 +100,9 @@ class StateCommandError extends CommandError {
|
|
|
99
100
|
}
|
|
100
101
|
|
|
101
102
|
const GRAPH_FORMATS = new Set(["ascii", "mermaid", "dot"]);
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
"--session-id",
|
|
106
|
-
"--thread-id",
|
|
107
|
-
"--turn-id",
|
|
108
|
-
"--to",
|
|
109
|
-
"--skill",
|
|
110
|
-
"--format",
|
|
111
|
-
"--older-than",
|
|
112
|
-
"--status",
|
|
113
|
-
"--fields",
|
|
114
|
-
"--since",
|
|
115
|
-
"--limit",
|
|
116
|
-
]);
|
|
117
|
-
const ACTION_NAMES = new Set([
|
|
118
|
-
"read",
|
|
119
|
-
"write",
|
|
120
|
-
"clear",
|
|
121
|
-
"contract",
|
|
122
|
-
"handoff",
|
|
123
|
-
"graph",
|
|
124
|
-
"prune",
|
|
125
|
-
"gc",
|
|
126
|
-
"migrate",
|
|
127
|
-
"status",
|
|
128
|
-
"doctor",
|
|
129
|
-
]);
|
|
130
|
-
const BOOLEAN_FLAGS = new Set([
|
|
131
|
-
"--json",
|
|
132
|
-
"--replace",
|
|
133
|
-
"--hard",
|
|
134
|
-
"--dry-run",
|
|
135
|
-
"--migrate",
|
|
136
|
-
"--compact",
|
|
137
|
-
"--history",
|
|
138
|
-
"--force",
|
|
139
|
-
]);
|
|
140
|
-
const VERB_SPECIFIC_FLAGS = new Set([
|
|
141
|
-
"--skill",
|
|
142
|
-
"--format",
|
|
143
|
-
"--older-than",
|
|
144
|
-
"--status",
|
|
145
|
-
"--fields",
|
|
146
|
-
"--since",
|
|
147
|
-
"--limit",
|
|
148
|
-
"--history",
|
|
149
|
-
]);
|
|
150
|
-
|
|
151
|
-
function flagName(arg: string): string | undefined {
|
|
152
|
-
if (!arg.startsWith("--")) return undefined;
|
|
153
|
-
const equalsIndex = arg.indexOf("=");
|
|
154
|
-
return equalsIndex >= 0 ? arg.slice(0, equalsIndex) : arg;
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
function manifestFlagNames(action: ParsedInvocation["action"], positionalSkill: string | undefined): Set<string> {
|
|
158
|
-
const names = new Set<string>();
|
|
159
|
-
const skills =
|
|
160
|
-
positionalSkill && KNOWN_MODES.includes(positionalSkill)
|
|
161
|
-
? [positionalSkill as CanonicalGjcWorkflowSkill]
|
|
162
|
-
: CANONICAL_GJC_WORKFLOW_SKILLS;
|
|
163
|
-
for (const skill of skills) {
|
|
164
|
-
for (const arg of typedArgsFor(skill, action)) names.add(`--${arg.name}`);
|
|
165
|
-
}
|
|
166
|
-
return names;
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
function assertKnownFlags(args: readonly string[], parsed: ParsedInvocation): void {
|
|
170
|
-
const manifestFlags = manifestFlagNames(parsed.action, parsed.positionalSkill);
|
|
171
|
-
for (const arg of args) {
|
|
172
|
-
const flag = flagName(arg);
|
|
173
|
-
if (!flag) continue;
|
|
174
|
-
if (
|
|
175
|
-
FLAGS_WITH_VALUES.has(flag) ||
|
|
176
|
-
BOOLEAN_FLAGS.has(flag) ||
|
|
177
|
-
VERB_SPECIFIC_FLAGS.has(flag) ||
|
|
178
|
-
manifestFlags.has(flag)
|
|
179
|
-
) {
|
|
180
|
-
continue;
|
|
181
|
-
}
|
|
182
|
-
throw new StateCommandError(2, `unknown gjc state flag: ${flag}`);
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
interface ParsedInvocation {
|
|
187
|
-
action:
|
|
188
|
-
| "read"
|
|
189
|
-
| "write"
|
|
190
|
-
| "clear"
|
|
191
|
-
| "contract"
|
|
192
|
-
| "handoff"
|
|
193
|
-
| "graph"
|
|
194
|
-
| "prune"
|
|
195
|
-
| "gc"
|
|
196
|
-
| "migrate"
|
|
197
|
-
| "status"
|
|
198
|
-
| "doctor";
|
|
199
|
-
positionalSkill?: string;
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
function parsePositionalArgs(args: readonly string[]): ParsedInvocation {
|
|
203
|
-
let skipNext = false;
|
|
204
|
-
const positional: string[] = [];
|
|
205
|
-
for (const arg of args) {
|
|
206
|
-
if (skipNext) {
|
|
207
|
-
skipNext = false;
|
|
208
|
-
continue;
|
|
209
|
-
}
|
|
210
|
-
if (FLAGS_WITH_VALUES.has(arg)) {
|
|
211
|
-
skipNext = true;
|
|
212
|
-
continue;
|
|
213
|
-
}
|
|
214
|
-
if (!arg.startsWith("-")) positional.push(arg);
|
|
215
|
-
}
|
|
216
|
-
// Documented argv shapes:
|
|
217
|
-
// gjc state read|write|clear|contract ...
|
|
218
|
-
// gjc state <skill> read|write|contract ...
|
|
219
|
-
const first = positional[0];
|
|
220
|
-
const second = positional[1];
|
|
221
|
-
if (first && ACTION_NAMES.has(first)) {
|
|
222
|
-
return { action: first as ParsedInvocation["action"], positionalSkill: second };
|
|
223
|
-
}
|
|
224
|
-
if (first && second && ACTION_NAMES.has(second)) {
|
|
225
|
-
return { action: second as ParsedInvocation["action"], positionalSkill: first };
|
|
226
|
-
}
|
|
227
|
-
// `gjc state <skill>` alone defaults to read for that skill.
|
|
228
|
-
if (first && !second) {
|
|
229
|
-
return { action: "read", positionalSkill: first };
|
|
230
|
-
}
|
|
231
|
-
return { action: "read" };
|
|
103
|
+
function assertKnownFlags(classification: StateArgvClassification): void {
|
|
104
|
+
const [unknownFlag] = classification.unknownFlags;
|
|
105
|
+
if (unknownFlag) throw new StateCommandError(2, `unknown gjc state flag: ${unknownFlag}`);
|
|
232
106
|
}
|
|
233
107
|
|
|
234
108
|
function isKnownMode(mode: string): mode is CanonicalGjcWorkflowSkill {
|
|
@@ -279,19 +153,16 @@ interface ResolvedSelectors {
|
|
|
279
153
|
// `clear` resolves like a read (explicit -> payload -> env -> latest-activity marker)
|
|
280
154
|
// per the spec: read/status/clear may fall back to the most-recent session. Commands
|
|
281
155
|
// that create or mutate new state roots still require an explicit/env session id.
|
|
282
|
-
const WRITE_SESSION_ACTIONS = new Set<
|
|
156
|
+
const WRITE_SESSION_ACTIONS = new Set<StateAction>(["write", "handoff", "prune", "migrate"]);
|
|
283
157
|
|
|
284
|
-
async function resolveSelectors(
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
positionalSkill: string | undefined,
|
|
288
|
-
action: ParsedInvocation["action"],
|
|
289
|
-
): Promise<ResolvedSelectors> {
|
|
290
|
-
const payload = await readInputJson(flagValue(args, "--input"), cwd);
|
|
158
|
+
async function resolveSelectors(args: readonly string[], cwd: string, action: StateAction): Promise<ResolvedSelectors> {
|
|
159
|
+
const classification = classifyStateArgv(args);
|
|
160
|
+
const payload = await readInputJson(firstStateFlagValue(classification, "--input"), cwd);
|
|
291
161
|
|
|
162
|
+
const [modeCandidate, positionalCandidate] = classification.runtimeSelectorCandidates;
|
|
292
163
|
const candidates: Array<string | undefined> = [
|
|
293
|
-
|
|
294
|
-
|
|
164
|
+
modeCandidate?.value,
|
|
165
|
+
positionalCandidate?.value,
|
|
295
166
|
typeof payload?.mode === "string" ? (payload.mode as string).trim() || undefined : undefined,
|
|
296
167
|
typeof payload?.skill === "string" ? (payload.skill as string).trim() || undefined : undefined,
|
|
297
168
|
];
|
|
@@ -1232,12 +1103,8 @@ export async function readWorkflowStateJson(
|
|
|
1232
1103
|
return (await readJsonFile(modeStateFile(cwd, skill, session.gjcSessionId))) ?? {};
|
|
1233
1104
|
}
|
|
1234
1105
|
|
|
1235
|
-
async function handleRead(
|
|
1236
|
-
args
|
|
1237
|
-
cwd: string,
|
|
1238
|
-
positionalSkill: string | undefined,
|
|
1239
|
-
): Promise<StateCommandResult> {
|
|
1240
|
-
const selectors = await resolveSelectors(args, cwd, positionalSkill, "read");
|
|
1106
|
+
async function handleRead(args: readonly string[], cwd: string): Promise<StateCommandResult> {
|
|
1107
|
+
const selectors = await resolveSelectors(args, cwd, "read");
|
|
1241
1108
|
const mode = selectors.mode ?? (await inferModeFromActiveState(cwd, selectors.gjcSessionId));
|
|
1242
1109
|
const fields = parseFieldsFlag(args);
|
|
1243
1110
|
if (mode) {
|
|
@@ -1276,12 +1143,8 @@ async function handleRead(
|
|
|
1276
1143
|
return { status: 0, stdout: `${JSON.stringify(existing ?? {}, null, 2)}\n` };
|
|
1277
1144
|
}
|
|
1278
1145
|
|
|
1279
|
-
async function handleStatus(
|
|
1280
|
-
args
|
|
1281
|
-
cwd: string,
|
|
1282
|
-
positionalSkill: string | undefined,
|
|
1283
|
-
): Promise<StateCommandResult> {
|
|
1284
|
-
const selectors = await resolveSelectors(args, cwd, positionalSkill, "read");
|
|
1146
|
+
async function handleStatus(args: readonly string[], cwd: string): Promise<StateCommandResult> {
|
|
1147
|
+
const selectors = await resolveSelectors(args, cwd, "read");
|
|
1285
1148
|
const mode = selectors.mode ?? (await inferModeFromActiveState(cwd, selectors.gjcSessionId));
|
|
1286
1149
|
if (!mode) {
|
|
1287
1150
|
throw new StateCommandError(
|
|
@@ -1303,12 +1166,8 @@ async function handleStatus(
|
|
|
1303
1166
|
};
|
|
1304
1167
|
}
|
|
1305
1168
|
|
|
1306
|
-
async function handleWrite(
|
|
1307
|
-
args
|
|
1308
|
-
cwd: string,
|
|
1309
|
-
positionalSkill: string | undefined,
|
|
1310
|
-
): Promise<StateCommandResult> {
|
|
1311
|
-
const selectors = await resolveSelectors(args, cwd, positionalSkill, "write");
|
|
1169
|
+
async function handleWrite(args: readonly string[], cwd: string): Promise<StateCommandResult> {
|
|
1170
|
+
const selectors = await resolveSelectors(args, cwd, "write");
|
|
1312
1171
|
const { gjcSessionId: sessionId, threadId, turnId, payload } = selectors;
|
|
1313
1172
|
if (!payload) throw new StateCommandError(2, "gjc state write requires --input '<json>'");
|
|
1314
1173
|
const mode = selectors.mode ?? (await inferModeFromActiveState(cwd, sessionId));
|
|
@@ -1465,12 +1324,8 @@ async function handleWrite(
|
|
|
1465
1324
|
);
|
|
1466
1325
|
}
|
|
1467
1326
|
|
|
1468
|
-
async function handleClear(
|
|
1469
|
-
args
|
|
1470
|
-
cwd: string,
|
|
1471
|
-
positionalSkill: string | undefined,
|
|
1472
|
-
): Promise<StateCommandResult> {
|
|
1473
|
-
const selectors = await resolveSelectors(args, cwd, positionalSkill, "clear");
|
|
1327
|
+
async function handleClear(args: readonly string[], cwd: string): Promise<StateCommandResult> {
|
|
1328
|
+
const selectors = await resolveSelectors(args, cwd, "clear");
|
|
1474
1329
|
const { gjcSessionId: sessionId, threadId, turnId } = selectors;
|
|
1475
1330
|
const mode = selectors.mode ?? (await inferModeFromActiveState(cwd, sessionId));
|
|
1476
1331
|
if (!mode)
|
|
@@ -1631,12 +1486,8 @@ async function assertDeepInterviewHandoffReady(state: Record<string, unknown>):
|
|
|
1631
1486
|
* the phase remains in `skill-active-state.json` until a chain call (or
|
|
1632
1487
|
* explicit `clear`) demotes it.
|
|
1633
1488
|
*/
|
|
1634
|
-
async function handleHandoffUnlocked(
|
|
1635
|
-
args
|
|
1636
|
-
cwd: string,
|
|
1637
|
-
positionalSkill: string | undefined,
|
|
1638
|
-
): Promise<StateCommandResult> {
|
|
1639
|
-
const selectors = await resolveSelectors(args, cwd, positionalSkill, "handoff");
|
|
1489
|
+
async function handleHandoffUnlocked(args: readonly string[], cwd: string): Promise<StateCommandResult> {
|
|
1490
|
+
const selectors = await resolveSelectors(args, cwd, "handoff");
|
|
1640
1491
|
const { gjcSessionId: sessionId, threadId, turnId } = selectors;
|
|
1641
1492
|
const caller = selectors.mode ?? (await inferModeFromActiveState(cwd, sessionId));
|
|
1642
1493
|
if (!caller) {
|
|
@@ -1944,23 +1795,22 @@ async function handleHandoffUnlocked(
|
|
|
1944
1795
|
};
|
|
1945
1796
|
}
|
|
1946
1797
|
|
|
1947
|
-
async function handleHandoff(
|
|
1948
|
-
args
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1798
|
+
async function handleHandoff(args: readonly string[], cwd: string): Promise<StateCommandResult> {
|
|
1799
|
+
const selectors = await resolveSelectors(args, cwd, "handoff");
|
|
1800
|
+
// Serialize concurrent handoffs on a dedicated sentinel lock, NOT on the
|
|
1801
|
+
// derived `skill-active-state.json` cache. The inner transaction
|
|
1802
|
+
// (applyHandoffToActiveState / syncSkillActiveState -> rebuildActiveSnapshot)
|
|
1803
|
+
// re-locks that cache file, and `withFileLock` is not reentrant: holding the
|
|
1804
|
+
// active-state lock here made the inner rebuild self-contend and fail after
|
|
1805
|
+
// all retries whenever `cwd === process.cwd()` (the real CLI case). Pass
|
|
1806
|
+
// `{ cwd }` so the sentinel resolves against the handoff cwd rather than
|
|
1807
|
+
// `process.cwd()`.
|
|
1808
|
+
const handoffLock = path.join(sessionStateDir(cwd, selectors.gjcSessionId), "handoff");
|
|
1809
|
+
return withWorkflowStateLock(handoffLock, async () => handleHandoffUnlocked(args, cwd), { cwd });
|
|
1956
1810
|
}
|
|
1957
1811
|
|
|
1958
|
-
async function handleContract(
|
|
1959
|
-
args
|
|
1960
|
-
cwd: string,
|
|
1961
|
-
positionalSkill: string | undefined,
|
|
1962
|
-
): Promise<StateCommandResult> {
|
|
1963
|
-
const { mode } = await resolveSelectors(args, cwd, positionalSkill, "read");
|
|
1812
|
+
async function handleContract(args: readonly string[], cwd: string): Promise<StateCommandResult> {
|
|
1813
|
+
const { mode } = await resolveSelectors(args, cwd, "read");
|
|
1964
1814
|
if (!mode) {
|
|
1965
1815
|
throw new StateCommandError(2, "gjc state contract requires --mode <skill>, positional <skill>, or input.skill");
|
|
1966
1816
|
}
|
|
@@ -2188,12 +2038,8 @@ async function handleGraph(
|
|
|
2188
2038
|
};
|
|
2189
2039
|
}
|
|
2190
2040
|
|
|
2191
|
-
async function handlePrune(
|
|
2192
|
-
args
|
|
2193
|
-
cwd: string,
|
|
2194
|
-
positionalSkill: string | undefined,
|
|
2195
|
-
): Promise<StateCommandResult> {
|
|
2196
|
-
const selectors = await resolveSelectors(args, cwd, positionalSkill, "prune");
|
|
2041
|
+
async function handlePrune(args: readonly string[], cwd: string): Promise<StateCommandResult> {
|
|
2042
|
+
const selectors = await resolveSelectors(args, cwd, "prune");
|
|
2197
2043
|
const mode = selectors.mode ?? (await inferModeFromActiveState(cwd, selectors.gjcSessionId));
|
|
2198
2044
|
if (!mode) {
|
|
2199
2045
|
throw new StateCommandError(
|
|
@@ -2258,12 +2104,8 @@ async function handleGc(
|
|
|
2258
2104
|
return { status: 0, stdout: `${JSON.stringify(summary, null, 2)}\n` };
|
|
2259
2105
|
}
|
|
2260
2106
|
|
|
2261
|
-
async function handleMigrate(
|
|
2262
|
-
args
|
|
2263
|
-
cwd: string,
|
|
2264
|
-
positionalSkill: string | undefined,
|
|
2265
|
-
): Promise<StateCommandResult> {
|
|
2266
|
-
const selectors = await resolveSelectors(args, cwd, positionalSkill, "migrate");
|
|
2107
|
+
async function handleMigrate(args: readonly string[], cwd: string): Promise<StateCommandResult> {
|
|
2108
|
+
const selectors = await resolveSelectors(args, cwd, "migrate");
|
|
2267
2109
|
const mode = selectors.mode ?? (await inferModeFromActiveState(cwd, selectors.gjcSessionId));
|
|
2268
2110
|
if (!mode) {
|
|
2269
2111
|
throw new StateCommandError(
|
|
@@ -2294,34 +2136,31 @@ async function handleMigrate(
|
|
|
2294
2136
|
|
|
2295
2137
|
export async function runNativeStateCommand(args: string[], cwd = process.cwd()): Promise<StateCommandResult> {
|
|
2296
2138
|
try {
|
|
2297
|
-
const parsed =
|
|
2298
|
-
assertKnownFlags(
|
|
2299
|
-
switch (parsed.
|
|
2139
|
+
const parsed = classifyStateArgv(args);
|
|
2140
|
+
assertKnownFlags(parsed);
|
|
2141
|
+
switch (parsed.effectiveAction) {
|
|
2300
2142
|
case "read":
|
|
2301
|
-
|
|
2302
|
-
return await handleRead(args, cwd, parsed.positionalSkill);
|
|
2143
|
+
return await handleRead(args, cwd);
|
|
2303
2144
|
case "write":
|
|
2304
|
-
return await handleWrite(args, cwd
|
|
2145
|
+
return await handleWrite(args, cwd);
|
|
2305
2146
|
case "clear":
|
|
2306
|
-
return await handleClear(args, cwd
|
|
2147
|
+
return await handleClear(args, cwd);
|
|
2307
2148
|
case "contract":
|
|
2308
|
-
return await handleContract(args, cwd
|
|
2149
|
+
return await handleContract(args, cwd);
|
|
2309
2150
|
case "status":
|
|
2310
|
-
return await handleStatus(args, cwd
|
|
2151
|
+
return await handleStatus(args, cwd);
|
|
2311
2152
|
case "doctor":
|
|
2312
2153
|
return await handleDoctor(args, cwd, parsed.positionalSkill);
|
|
2313
2154
|
case "handoff":
|
|
2314
|
-
return await handleHandoff(args, cwd
|
|
2155
|
+
return await handleHandoff(args, cwd);
|
|
2315
2156
|
case "graph":
|
|
2316
2157
|
return await handleGraph(args, cwd, parsed.positionalSkill);
|
|
2317
2158
|
case "prune":
|
|
2318
|
-
return await handlePrune(args, cwd
|
|
2159
|
+
return await handlePrune(args, cwd);
|
|
2319
2160
|
case "gc":
|
|
2320
2161
|
return await handleGc(args, cwd, parsed.positionalSkill);
|
|
2321
2162
|
case "migrate":
|
|
2322
|
-
return await handleMigrate(args, cwd
|
|
2323
|
-
default:
|
|
2324
|
-
return { status: 2, stderr: `Unknown gjc state command: ${parsed.action}\n` };
|
|
2163
|
+
return await handleMigrate(args, cwd);
|
|
2325
2164
|
}
|
|
2326
2165
|
} catch (error) {
|
|
2327
2166
|
if (error instanceof CommandError) return { status: error.exitStatus, stderr: `${error.message}\n` };
|
package/src/main.ts
CHANGED
|
@@ -23,6 +23,7 @@ import chalk from "chalk";
|
|
|
23
23
|
import type { Args } from "./cli/args";
|
|
24
24
|
import { processFileArguments } from "./cli/file-processor";
|
|
25
25
|
import { buildInitialMessage } from "./cli/initial-message";
|
|
26
|
+
import { resolveLaunchDisposition } from "./cli/launch-disposition";
|
|
26
27
|
import { runListModelsCommand } from "./cli/list-models";
|
|
27
28
|
import { selectSession } from "./cli/session-picker";
|
|
28
29
|
import { findConfigFile } from "./config";
|
|
@@ -282,7 +283,7 @@ export function resolveAcpStartupOptions(
|
|
|
282
283
|
}
|
|
283
284
|
|
|
284
285
|
async function readPipedInput(): Promise<string | undefined> {
|
|
285
|
-
if (process.stdin.isTTY
|
|
286
|
+
if (process.stdin.isTTY === true) return undefined;
|
|
286
287
|
try {
|
|
287
288
|
const text = await Bun.stdin.text();
|
|
288
289
|
if (text.trim().length === 0) return undefined;
|
|
@@ -292,6 +293,30 @@ async function readPipedInput(): Promise<string | undefined> {
|
|
|
292
293
|
}
|
|
293
294
|
}
|
|
294
295
|
|
|
296
|
+
async function readPipedInputIfReady(): Promise<string | undefined> {
|
|
297
|
+
if (process.stdin.isTTY === true) return undefined;
|
|
298
|
+
|
|
299
|
+
const ready = Promise.withResolvers<void>();
|
|
300
|
+
const onReadable = () => ready.resolve();
|
|
301
|
+
const onEnd = () => ready.resolve();
|
|
302
|
+
const onError = () => ready.resolve();
|
|
303
|
+
process.stdin.once("readable", onReadable);
|
|
304
|
+
process.stdin.once("end", onEnd);
|
|
305
|
+
process.stdin.once("error", onError);
|
|
306
|
+
await Promise.race([ready.promise, new Promise<void>(resolve => setImmediate(resolve))]);
|
|
307
|
+
process.stdin.removeListener("readable", onReadable);
|
|
308
|
+
process.stdin.removeListener("end", onEnd);
|
|
309
|
+
process.stdin.removeListener("error", onError);
|
|
310
|
+
|
|
311
|
+
const chunks: Buffer[] = [];
|
|
312
|
+
for (let chunk = process.stdin.read(); chunk !== null; chunk = process.stdin.read()) {
|
|
313
|
+
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
314
|
+
}
|
|
315
|
+
if (chunks.length === 0) return undefined;
|
|
316
|
+
const text = Buffer.concat(chunks).toString("utf8");
|
|
317
|
+
return text.trim() ? text : undefined;
|
|
318
|
+
}
|
|
319
|
+
|
|
295
320
|
export interface InteractiveModeNotify {
|
|
296
321
|
kind: "warn" | "error" | "info";
|
|
297
322
|
message: string;
|
|
@@ -1014,6 +1039,7 @@ export interface RunRootCommandDependencies {
|
|
|
1014
1039
|
startupUpdate?: { check: () => Promise<string | undefined> };
|
|
1015
1040
|
initTheme?: typeof initTheme;
|
|
1016
1041
|
readPipedInput?: typeof readPipedInput;
|
|
1042
|
+
stdinIsTTY?: boolean;
|
|
1017
1043
|
runStartupCredentialAutoImportIfNeeded?: typeof runStartupCredentialAutoImportIfNeeded;
|
|
1018
1044
|
getChangelogForDisplay?: typeof getChangelogForDisplay;
|
|
1019
1045
|
createInteractiveMode?: CreateInteractiveMode;
|
|
@@ -1164,8 +1190,16 @@ export async function runRootCommand(
|
|
|
1164
1190
|
if (parsedArgs.noTitle || parsedArgs.mode === "acp") {
|
|
1165
1191
|
Bun.env.PI_NO_TITLE = "1";
|
|
1166
1192
|
}
|
|
1193
|
+
const hasPreparedInput = parsedArgs.messages.length > 0 || parsedArgs.fileArgs.length > 0;
|
|
1167
1194
|
const { pipedInput, fileText, fileImages } = await logger.time("prepareInitialMessage", async () => {
|
|
1168
|
-
const pipedInput =
|
|
1195
|
+
const pipedInput =
|
|
1196
|
+
parsedArgs.mode === "acp"
|
|
1197
|
+
? undefined
|
|
1198
|
+
: deps.readPipedInput
|
|
1199
|
+
? await deps.readPipedInput()
|
|
1200
|
+
: hasPreparedInput
|
|
1201
|
+
? await readPipedInputIfReady()
|
|
1202
|
+
: await readPipedInput();
|
|
1169
1203
|
if (parsedArgs.fileArgs.length === 0) {
|
|
1170
1204
|
return { pipedInput, fileText: undefined, fileImages: undefined };
|
|
1171
1205
|
}
|
|
@@ -1180,14 +1214,25 @@ export async function runRootCommand(
|
|
|
1180
1214
|
fileImages,
|
|
1181
1215
|
stdinContent: pipedInput,
|
|
1182
1216
|
});
|
|
1183
|
-
const
|
|
1217
|
+
const disposition = resolveLaunchDisposition({
|
|
1218
|
+
stdinIsTTY: deps.stdinIsTTY ?? process.stdin.isTTY,
|
|
1219
|
+
pipedInput,
|
|
1220
|
+
hasPreparedInput,
|
|
1221
|
+
print: Boolean(parsedArgs.print),
|
|
1222
|
+
mode: parsedArgs.mode,
|
|
1223
|
+
});
|
|
1224
|
+
if (disposition.nonInteractiveError) {
|
|
1225
|
+
process.stderr.write(`${chalk.red(disposition.nonInteractiveError)}\n`);
|
|
1226
|
+
process.exit(1);
|
|
1227
|
+
}
|
|
1228
|
+
const autoPrint = disposition.autoPrint;
|
|
1184
1229
|
const startupUpdateRoute = classifyStartupUpdateRoute(parsedArgs, autoPrint);
|
|
1185
1230
|
const startupUpdate = new StartupUpdateOrchestrator(
|
|
1186
1231
|
startupUpdateRoute,
|
|
1187
1232
|
() => settingsInstance.get("startup.checkUpdate"),
|
|
1188
1233
|
deps.startupUpdate?.check ?? (() => checkForNewVersion(VERSION)),
|
|
1189
1234
|
);
|
|
1190
|
-
const isInteractive =
|
|
1235
|
+
const isInteractive = disposition.isInteractive;
|
|
1191
1236
|
const mode = parsedArgs.mode || "text";
|
|
1192
1237
|
|
|
1193
1238
|
// Initialize discovery system with settings for provider persistence
|
|
@@ -1319,6 +1364,7 @@ export async function runRootCommand(
|
|
|
1319
1364
|
sessionOptions.modelRegistry = modelRegistry;
|
|
1320
1365
|
sessionOptions.hasUI = isInteractive;
|
|
1321
1366
|
sessionOptions.notificationHostModeSupported = isInteractive;
|
|
1367
|
+
sessionOptions.sdkHostModeSupported = isInteractive;
|
|
1322
1368
|
sessionOptions.settings = settingsInstance;
|
|
1323
1369
|
const hasRootStartupProfile = Boolean(settingsInstance.get("modelProfile.default") || parsedArgs.mpreset);
|
|
1324
1370
|
|
|
@@ -1351,6 +1397,30 @@ export async function runRootCommand(
|
|
|
1351
1397
|
applyCliRuntimeApiKeyOverride(authStorage, parsedArgs.apiKey, sessionOptions.model);
|
|
1352
1398
|
}
|
|
1353
1399
|
|
|
1400
|
+
if (
|
|
1401
|
+
!deps.suppressProcessExit &&
|
|
1402
|
+
deps.createAgentSession === undefined &&
|
|
1403
|
+
parsedArgs.noSession === true &&
|
|
1404
|
+
!parsedArgs.continue &&
|
|
1405
|
+
parsedArgs.resume === undefined &&
|
|
1406
|
+
parsedArgs.fork === undefined &&
|
|
1407
|
+
!isInteractive &&
|
|
1408
|
+
mode !== "acp" &&
|
|
1409
|
+
!hasRootStartupProfile &&
|
|
1410
|
+
!sessionOptions.model &&
|
|
1411
|
+
!sessionOptions.modelPattern &&
|
|
1412
|
+
modelRegistry.getAvailable().length === 0
|
|
1413
|
+
) {
|
|
1414
|
+
process.stderr.write(`${chalk.red(`No models available. ${formatModelOnboardingGuidance()}`)}\n`);
|
|
1415
|
+
process.stderr.write(
|
|
1416
|
+
`${chalk.yellow(`\nAdvanced manual config remains available at ${ModelsConfigFile.path()}`)}\n`,
|
|
1417
|
+
);
|
|
1418
|
+
process.exitCode = 1;
|
|
1419
|
+
authStorage.close();
|
|
1420
|
+
stopThemeWatcher();
|
|
1421
|
+
await postmortem.cleanup();
|
|
1422
|
+
return;
|
|
1423
|
+
}
|
|
1354
1424
|
const createAgentSessionImpl = deps.createAgentSession ?? createAgentSession;
|
|
1355
1425
|
const createSession: CreateSessionForMain = async (options, context): Promise<CreateAgentSessionResult> => {
|
|
1356
1426
|
const result = await logger.time("createAgentSession", createAgentSessionImpl, options);
|
|
@@ -1365,10 +1435,16 @@ export async function runRootCommand(
|
|
|
1365
1435
|
};
|
|
1366
1436
|
|
|
1367
1437
|
if (mode === "acp") {
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1438
|
+
try {
|
|
1439
|
+
await (deps.runAcpMode ?? (await import("./modes/acp")).runAcpMode)({
|
|
1440
|
+
agentDir: settingsInstance.getAgentDir(),
|
|
1441
|
+
...(acpStartupOptions ? { startupOptions: acpStartupOptions } : {}),
|
|
1442
|
+
});
|
|
1443
|
+
} finally {
|
|
1444
|
+
authStorage.close();
|
|
1445
|
+
stopThemeWatcher();
|
|
1446
|
+
if (!deps.suppressProcessExit) await postmortem.cleanup();
|
|
1447
|
+
}
|
|
1372
1448
|
} else {
|
|
1373
1449
|
const { session, setToolUIContext, modelFallbackMessage, lspServers, mcpManager, eventBus } = await createSession(
|
|
1374
1450
|
sessionOptions,
|
|
@@ -1503,20 +1579,21 @@ export async function runRootCommand(
|
|
|
1503
1579
|
}
|
|
1504
1580
|
} else {
|
|
1505
1581
|
const runPrint = deps.runPrintMode ?? (await import("./modes/print-mode")).runPrintMode;
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1582
|
+
try {
|
|
1583
|
+
await runPrint(session, {
|
|
1584
|
+
mode,
|
|
1585
|
+
messages: parsedArgs.messages,
|
|
1586
|
+
initialMessage,
|
|
1587
|
+
initialImages,
|
|
1588
|
+
suppressProcessExit: deps.suppressProcessExit,
|
|
1589
|
+
});
|
|
1590
|
+
if ($env.PI_TIMING) {
|
|
1591
|
+
logger.printTimings();
|
|
1592
|
+
}
|
|
1593
|
+
} finally {
|
|
1594
|
+
stopThemeWatcher();
|
|
1595
|
+
authStorage.close();
|
|
1596
|
+
if (!deps.suppressProcessExit) await postmortem.cleanup();
|
|
1520
1597
|
}
|
|
1521
1598
|
}
|
|
1522
1599
|
}
|