@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
@@ -13,6 +13,10 @@
13
13
  * - Dry-run by default: nothing is deleted unless `--prune`/`--force`.
14
14
  */
15
15
 
16
+ import { getAgentDir } from "@gajae-code/utils";
17
+ import { SessionIndex } from "../sdk/broker/session-index";
18
+ import { UnsupportedStateVersionError } from "../sdk/broker/state-version";
19
+
16
20
  import { buildGcReportText } from "./gc-render";
17
21
 
18
22
  export type GcStore = "harness_leases" | "team_workers" | "file_locks" | "tmux_sessions" | "registry_entries";
@@ -115,11 +119,21 @@ export interface GcCounts {
115
119
  >;
116
120
  }
117
121
 
122
+ export interface GcSessionIndexHealth {
123
+ status: "healthy" | "corrupt" | "repaired" | "unsupported" | "repair_failed";
124
+ valid_prefix_seq: number;
125
+ snapshot_seq?: number;
126
+ reason?: string;
127
+ quarantine_path?: string;
128
+ }
129
+
118
130
  export interface GcReport {
119
131
  dry_run: boolean;
132
+ operation?: "dry_run" | "prune" | "repair_session_index";
120
133
  stores: Record<GcStore, GcRecord[]>;
121
134
  counts: GcCounts;
122
135
  errors: GcError[];
136
+ session_index?: GcSessionIndexHealth;
123
137
  }
124
138
 
125
139
  export interface GcRunResult {
@@ -225,6 +239,7 @@ function computeCounts(stores: Record<GcStore, GcRecord[]>, errors: GcError[]):
225
239
  interface ParsedGcArgs {
226
240
  json: boolean;
227
241
  prune: boolean;
242
+ repairSessionIndex: boolean;
228
243
  help: boolean;
229
244
  }
230
245
 
@@ -233,6 +248,8 @@ class GcUsageError extends Error {}
233
248
  function parseGcArgs(argv: string[]): ParsedGcArgs {
234
249
  let json = false;
235
250
  let prune = false;
251
+ let repairSessionIndex = false;
252
+
236
253
  let dryRun = false;
237
254
  let help = false;
238
255
  for (const arg of argv) {
@@ -245,6 +262,10 @@ function parseGcArgs(argv: string[]): ParsedGcArgs {
245
262
  case "--force":
246
263
  prune = true;
247
264
  break;
265
+ case "--repair-session-index":
266
+ repairSessionIndex = true;
267
+ break;
268
+
248
269
  case "--dry-run":
249
270
  dryRun = true;
250
271
  break;
@@ -256,9 +277,11 @@ function parseGcArgs(argv: string[]): ParsedGcArgs {
256
277
  throw new GcUsageError(`unknown_flag:${arg}`);
257
278
  }
258
279
  }
280
+ if (repairSessionIndex && prune) throw new GcUsageError("repair_session_index_cannot_combine_with_prune");
281
+ if (repairSessionIndex && dryRun) throw new GcUsageError("repair_session_index_cannot_combine_with_dry_run");
259
282
  // Explicit --dry-run always wins over --prune/--force.
260
283
  if (dryRun) prune = false;
261
- return { json, prune, help };
284
+ return { json, prune, repairSessionIndex, help };
262
285
  }
263
286
 
264
287
  /**
@@ -336,6 +359,40 @@ export function computeExitCode(report: GcReport): number {
336
359
  return 0;
337
360
  }
338
361
 
362
+ function resolveGcAgentDir(env: NodeJS.ProcessEnv): string {
363
+ return env.GJC_CODING_AGENT_DIR?.trim() || env.PI_CODING_AGENT_DIR?.trim() || getAgentDir();
364
+ }
365
+
366
+ async function collectSessionIndexHealth(repair: boolean, agentDir: string): Promise<GcSessionIndexHealth> {
367
+ const index = new SessionIndex(agentDir);
368
+ try {
369
+ if (repair) {
370
+ const result = await index.repair();
371
+ return {
372
+ status: result.status === "unsupported" ? "unsupported" : result.repaired ? "repaired" : "healthy",
373
+ valid_prefix_seq: result.validPrefixSeq,
374
+ snapshot_seq: result.snapshotSeq,
375
+ ...(result.reason ? { reason: result.reason } : {}),
376
+ ...(result.quarantinePath ? { quarantine_path: result.quarantinePath } : {}),
377
+ };
378
+ }
379
+ const diagnosis = await index.diagnose();
380
+ return {
381
+ status: diagnosis.status,
382
+ valid_prefix_seq: diagnosis.validPrefixSeq,
383
+ snapshot_seq: diagnosis.snapshotSeq,
384
+ ...(diagnosis.reason ? { reason: diagnosis.reason } : {}),
385
+ };
386
+ } catch (error) {
387
+ const reason = error instanceof Error ? error.message : String(error);
388
+ return {
389
+ status: error instanceof UnsupportedStateVersionError ? "unsupported" : "repair_failed",
390
+ valid_prefix_seq: 0,
391
+ reason,
392
+ };
393
+ }
394
+ }
395
+
339
396
  export async function runGjcGcCommand(
340
397
  argv: string[],
341
398
  cwd: string = process.cwd(),
@@ -357,7 +414,13 @@ export async function runGjcGcCommand(
357
414
  const resolvedAdapters = adapters ?? (await defaultGcAdapters());
358
415
  const ctx: GcContext = { probe: gcPidProbe, force: parsed.prune, env, cwd };
359
416
  const report = await collectGcReport(resolvedAdapters, ctx, parsed.prune);
360
- const status = computeExitCode(report);
417
+ report.operation = parsed.repairSessionIndex ? "repair_session_index" : parsed.prune ? "prune" : "dry_run";
418
+ report.session_index = await collectSessionIndexHealth(parsed.repairSessionIndex, resolveGcAgentDir(env));
419
+ const sessionIndexFailed =
420
+ report.session_index?.status === "corrupt" ||
421
+ report.session_index?.status === "unsupported" ||
422
+ report.session_index?.status === "repair_failed";
423
+ const status = sessionIndexFailed ? 1 : computeExitCode(report);
361
424
  const stdout = parsed.json ? `${JSON.stringify(report, null, 2)}\n` : buildGcReportText(report);
362
425
  return { stdout, stderr: "", status };
363
426
  }
@@ -367,12 +430,14 @@ export function gcHelpText(): string {
367
430
  "gjc gc - garbage-collect stale GJC session/PID records",
368
431
  "",
369
432
  "USAGE",
370
- " $ gjc gc [--prune|--force] [--json]",
433
+ " $ gjc gc [--prune|--force] [--repair-session-index] [--json]",
434
+
371
435
  "",
372
436
  "FLAGS",
373
437
  " --prune, --force Actually remove stale records (default: dry-run report only)",
374
438
  " --dry-run Force report-only mode (overrides --prune/--force)",
375
439
  " -j, --json Emit machine-readable JSON",
440
+ " --repair-session-index Explicitly quarantine a corrupt session-index suffix and retain its valid prefix",
376
441
  "",
377
442
  "Liveness-only: a record is removed only when its owning process is dead",
378
443
  "(ESRCH). Live / permission-denied / unknown processes are always kept.",
@@ -0,0 +1,292 @@
1
+ import { CANONICAL_GJC_WORKFLOW_SKILLS, type CanonicalGjcWorkflowSkill } from "../skill-state/canonical-skills";
2
+ import { typedArgsFor } from "./workflow-manifest";
3
+
4
+ export type StateAction =
5
+ | "read"
6
+ | "write"
7
+ | "clear"
8
+ | "contract"
9
+ | "handoff"
10
+ | "graph"
11
+ | "prune"
12
+ | "gc"
13
+ | "migrate"
14
+ | "status"
15
+ | "doctor";
16
+
17
+ export const STATE_ACTION_NAMES: ReadonlySet<StateAction> = new Set([
18
+ "read",
19
+ "write",
20
+ "clear",
21
+ "contract",
22
+ "handoff",
23
+ "graph",
24
+ "prune",
25
+ "gc",
26
+ "migrate",
27
+ "status",
28
+ "doctor",
29
+ ]);
30
+
31
+ export const STATE_FLAGS_WITH_VALUES: ReadonlySet<string> = new Set([
32
+ "--input",
33
+ "--mode",
34
+ "--session-id",
35
+ "--thread-id",
36
+ "--turn-id",
37
+ "--to",
38
+ "--skill",
39
+ "--format",
40
+ "--older-than",
41
+ "--status",
42
+ "--fields",
43
+ "--since",
44
+ "--limit",
45
+ ]);
46
+
47
+ export const STATE_BOOLEAN_FLAGS: ReadonlySet<string> = new Set([
48
+ "--json",
49
+ "--replace",
50
+ "--hard",
51
+ "--dry-run",
52
+ "--migrate",
53
+ "--compact",
54
+ "--history",
55
+ "--force",
56
+ ]);
57
+
58
+ export interface StateFlagOccurrence {
59
+ name: string;
60
+ index: number;
61
+ value?: string;
62
+ arity: "boolean" | "value";
63
+ form: "separate" | "attached";
64
+ malformed: boolean;
65
+ }
66
+
67
+ export interface StateSelectorCandidate {
68
+ source: "mode" | "positional" | "input.mode" | "input.skill";
69
+ value: string | undefined;
70
+ index: number;
71
+ }
72
+
73
+ export type StateInputKind = "empty" | "inline" | "file" | "invalid";
74
+
75
+ export interface StateInputClassification {
76
+ index: number;
77
+ raw: string;
78
+ kind: StateInputKind;
79
+ selectors: StateSelectorCandidate[];
80
+ }
81
+
82
+ export interface StateArgvClassification {
83
+ argv: readonly string[];
84
+ action: StateAction;
85
+ effectiveAction: StateAction;
86
+ positionalSkill?: string;
87
+ positionals: readonly string[];
88
+ flags: readonly StateFlagOccurrence[];
89
+ unknownFlags: readonly string[];
90
+ inputs: readonly StateInputClassification[];
91
+ runtimeSelectorCandidates: readonly StateSelectorCandidate[];
92
+ selectorCandidates: readonly StateSelectorCandidate[];
93
+ }
94
+
95
+ function normalizedFlagName(arg: string): string | undefined {
96
+ if (!arg.startsWith("--")) return undefined;
97
+ const equalsIndex = arg.indexOf("=");
98
+ return equalsIndex < 0 ? arg : arg.slice(0, equalsIndex);
99
+ }
100
+
101
+ function normalizedSelector(value: unknown): string | undefined {
102
+ return typeof value === "string" ? value.trim() || undefined : undefined;
103
+ }
104
+
105
+ interface StatePositional {
106
+ value: string;
107
+ index: number;
108
+ }
109
+
110
+ function parseRuntimePositionals(argv: readonly string[]): StatePositional[] {
111
+ const positionals: StatePositional[] = [];
112
+ let skipNext = false;
113
+ for (let index = 0; index < argv.length; index += 1) {
114
+ const arg = argv[index]!;
115
+ if (skipNext) {
116
+ skipNext = false;
117
+ continue;
118
+ }
119
+ if (STATE_FLAGS_WITH_VALUES.has(arg)) {
120
+ skipNext = true;
121
+ continue;
122
+ }
123
+ if (!arg.startsWith("-")) positionals.push({ value: arg, index });
124
+ }
125
+ return positionals;
126
+ }
127
+
128
+ function parseAction(positionals: readonly StatePositional[]): {
129
+ action: StateAction;
130
+ positionalSkill?: string;
131
+ positionalSkillIndex: number;
132
+ } {
133
+ const [first, second] = positionals;
134
+ if (first?.value && STATE_ACTION_NAMES.has(first.value as StateAction)) {
135
+ return {
136
+ action: first.value as StateAction,
137
+ ...(second?.value ? { positionalSkill: second.value } : {}),
138
+ positionalSkillIndex: second?.value ? second.index : -1,
139
+ };
140
+ }
141
+ if (first?.value && second?.value && STATE_ACTION_NAMES.has(second.value as StateAction)) {
142
+ return { action: second.value as StateAction, positionalSkill: first.value, positionalSkillIndex: first.index };
143
+ }
144
+ if (first?.value && !second?.value) {
145
+ return { action: "read", positionalSkill: first.value, positionalSkillIndex: first.index };
146
+ }
147
+ return { action: "read", positionalSkillIndex: -1 };
148
+ }
149
+
150
+ function stateFlagArities(
151
+ action: StateAction,
152
+ positionalSkill: string | undefined,
153
+ ): ReadonlyMap<string, "boolean" | "value"> {
154
+ const arities = new Map<string, "boolean" | "value">();
155
+ for (const flag of STATE_FLAGS_WITH_VALUES) arities.set(flag, "value");
156
+ for (const flag of STATE_BOOLEAN_FLAGS) arities.set(flag, "boolean");
157
+
158
+ const skills =
159
+ positionalSkill && CANONICAL_GJC_WORKFLOW_SKILLS.includes(positionalSkill as CanonicalGjcWorkflowSkill)
160
+ ? [positionalSkill as CanonicalGjcWorkflowSkill]
161
+ : CANONICAL_GJC_WORKFLOW_SKILLS;
162
+ for (const skill of skills) {
163
+ for (const arg of typedArgsFor(skill, action)) {
164
+ const name = `--${arg.name}`;
165
+ if (!arities.has(name)) arities.set(name, arg.type === "boolean" ? "boolean" : "value");
166
+ }
167
+ }
168
+ return arities;
169
+ }
170
+
171
+ function classifyInput(occurrence: StateFlagOccurrence): StateInputClassification {
172
+ const raw = occurrence.value ?? "";
173
+ const trimmed = raw.trim();
174
+ if (!trimmed) return { index: occurrence.index, raw, kind: "empty", selectors: [] };
175
+ if (trimmed.startsWith("@")) return { index: occurrence.index, raw, kind: "file", selectors: [] };
176
+ try {
177
+ const payload = JSON.parse(trimmed) as unknown;
178
+ if (!payload || typeof payload !== "object" || Array.isArray(payload)) {
179
+ return { index: occurrence.index, raw, kind: "invalid", selectors: [] };
180
+ }
181
+ const record = payload as Record<string, unknown>;
182
+ return {
183
+ index: occurrence.index,
184
+ raw,
185
+ kind: "inline",
186
+ selectors: [
187
+ { source: "input.mode", value: normalizedSelector(record.mode), index: occurrence.index },
188
+ { source: "input.skill", value: normalizedSelector(record.skill), index: occurrence.index },
189
+ ],
190
+ };
191
+ } catch {
192
+ return { index: occurrence.index, raw, kind: "invalid", selectors: [] };
193
+ }
194
+ }
195
+
196
+ export function firstStateFlagValue(classification: StateArgvClassification, name: string): string | undefined {
197
+ const index = classification.argv.indexOf(name);
198
+ return index < 0 ? undefined : classification.argv[index + 1];
199
+ }
200
+
201
+ export function classifyStateArgv(argv: readonly string[]): StateArgvClassification {
202
+ const preservedArgv = [...argv];
203
+ const positionalEntries = parseRuntimePositionals(preservedArgv);
204
+ const positionals = positionalEntries.map(positional => positional.value);
205
+ const { action, positionalSkill, positionalSkillIndex } = parseAction(positionalEntries);
206
+ const flagArities = stateFlagArities(action, positionalSkill);
207
+ const flags: StateFlagOccurrence[] = [];
208
+ const unknownFlags: string[] = [];
209
+
210
+ for (let index = 0; index < preservedArgv.length; index += 1) {
211
+ const arg = preservedArgv[index]!;
212
+ const name = normalizedFlagName(arg);
213
+ if (!name) continue;
214
+ const arity = flagArities.get(name);
215
+ if (!arity) {
216
+ unknownFlags.push(name);
217
+ continue;
218
+ }
219
+ if (arg !== name) {
220
+ flags.push({
221
+ name,
222
+ index,
223
+ value: arg.slice(name.length + 1),
224
+ arity,
225
+ form: "attached",
226
+ malformed: true,
227
+ });
228
+ continue;
229
+ }
230
+ if (arity === "boolean") {
231
+ flags.push({ name, index, arity, form: "separate", malformed: false });
232
+ continue;
233
+ }
234
+ const value = preservedArgv[index + 1];
235
+ flags.push({
236
+ name,
237
+ index,
238
+ ...(value === undefined ? {} : { value }),
239
+ arity,
240
+ form: "separate",
241
+ malformed: value === undefined || value.startsWith("--"),
242
+ });
243
+ }
244
+ const effectiveAction = action === "read" && preservedArgv.includes("--migrate") ? "migrate" : action;
245
+
246
+ const modeFlags = flags.filter(flag => flag.name === "--mode" && flag.form === "separate");
247
+ const inputFlags = flags.filter(flag => flag.name === "--input" && flag.form === "separate");
248
+ const inputs = inputFlags.map(classifyInput);
249
+ const positionalCandidate: StateSelectorCandidate = {
250
+ source: "positional",
251
+ value: normalizedSelector(positionalSkill),
252
+ index: positionalSkillIndex,
253
+ };
254
+ const modeCandidates = modeFlags.map(flag => ({
255
+ source: "mode" as const,
256
+ value: normalizedSelector(flag.value),
257
+ index: flag.index,
258
+ }));
259
+ const selectorCandidates = [positionalCandidate, ...modeCandidates, ...inputs.flatMap(input => input.selectors)];
260
+ const firstModeIndex = preservedArgv.indexOf("--mode");
261
+ const firstModeCandidate: StateSelectorCandidate = {
262
+ source: "mode",
263
+ value: normalizedSelector(firstModeIndex < 0 ? undefined : preservedArgv[firstModeIndex + 1]),
264
+ index: firstModeIndex,
265
+ };
266
+ const firstInputIndex = preservedArgv.indexOf("--input");
267
+ const firstInput =
268
+ firstInputIndex < 0
269
+ ? undefined
270
+ : classifyInput({
271
+ name: "--input",
272
+ index: firstInputIndex,
273
+ value: preservedArgv[firstInputIndex + 1],
274
+ arity: "value",
275
+ form: "separate",
276
+ malformed: false,
277
+ });
278
+ const runtimeSelectorCandidates = [firstModeCandidate, positionalCandidate, ...(firstInput?.selectors ?? [])];
279
+
280
+ return {
281
+ argv: preservedArgv,
282
+ action,
283
+ effectiveAction,
284
+ ...(positionalSkill === undefined ? {} : { positionalSkill }),
285
+ positionals,
286
+ flags,
287
+ unknownFlags,
288
+ inputs,
289
+ runtimeSelectorCandidates,
290
+ selectorCandidates,
291
+ };
292
+ }