@nodeint/migent 0.1.3 → 0.1.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.
Files changed (43) hide show
  1. package/README.md +57 -41
  2. package/dist/commands/agent.js +111 -59
  3. package/dist/commands/config.js +20 -8
  4. package/dist/commands/execute.d.ts +6 -0
  5. package/dist/commands/execute.js +30 -14
  6. package/dist/commands/init.d.ts +2 -0
  7. package/dist/commands/init.js +91 -87
  8. package/dist/commands/prepare.d.ts +1 -0
  9. package/dist/commands/prepare.js +14 -0
  10. package/dist/commands/repair.d.ts +1 -0
  11. package/dist/commands/repair.js +22 -0
  12. package/dist/commands/service.js +5 -4
  13. package/dist/commands/sessions.d.ts +24 -1
  14. package/dist/commands/sessions.js +165 -13
  15. package/dist/commands/skill.js +51 -32
  16. package/dist/commands/watch.d.ts +2 -1
  17. package/dist/commands/watch.js +130 -67
  18. package/dist/commands/workspace-registry.js +10 -1
  19. package/dist/display.js +21 -9
  20. package/dist/errors.d.ts +3 -0
  21. package/dist/errors.js +6 -0
  22. package/dist/exits.d.ts +10 -0
  23. package/dist/exits.js +26 -0
  24. package/dist/follow-log.d.ts +29 -0
  25. package/dist/follow-log.js +172 -0
  26. package/dist/fs-utils.d.ts +3 -0
  27. package/dist/fs-utils.js +26 -0
  28. package/dist/index.js +5 -1
  29. package/dist/migrate.js +2 -17
  30. package/dist/program.js +388 -168
  31. package/dist/session-row.d.ts +20 -0
  32. package/dist/session-row.js +21 -0
  33. package/dist/signals.d.ts +3 -0
  34. package/dist/signals.js +56 -0
  35. package/dist/templates/migent/SKILL.md +159 -50
  36. package/dist/templates/migent/references/schema-config.yaml +35 -0
  37. package/dist/templates/migent/references/schema-workflow.yaml +61 -0
  38. package/dist/text.d.ts +4 -0
  39. package/dist/text.js +13 -0
  40. package/dist/version.d.ts +1 -0
  41. package/dist/version.js +3 -0
  42. package/dist/workspace.js +5 -12
  43. package/package.json +4 -4
package/README.md CHANGED
@@ -17,9 +17,9 @@ where the process is.
17
17
  - [Supported CLIs](#supported-clis)
18
18
  - [Quickstart](#quickstart)
19
19
  - [Install](#install)
20
- - [Upgrade](#upgrade)
21
20
  - [Create a project](#create-a-project)
22
21
  - [Run](#run)
22
+ - [Next](#next)
23
23
  - [Concepts](#concepts)
24
24
  - [Current scope](#current-scope)
25
25
  - [Documentation](#documentation)
@@ -49,7 +49,7 @@ the process, while each provider CLI remains responsible for authentication,
49
49
  agent execution, and its own tools and options.
50
50
 
51
51
  ```mermaid
52
- flowchart LR
52
+ flowchart TD
53
53
  A[Task] --> B[Planner - Codex]
54
54
  B -->|plan.md| C[Reviewer - Grok]
55
55
  C -->|revise| B
@@ -61,77 +61,92 @@ flowchart LR
61
61
  Migent drives each provider through its headless CLI. Install and sign in to the
62
62
  ones your configuration names.
63
63
 
64
- | Provider | CLI |
65
- | -------- | -------- |
66
- | Claude | `claude` |
67
- | Codex | `codex` |
68
- | Grok | `grok` |
64
+ | Provider | CLI |
65
+ | -------------------| -------- |
66
+ | Google Antigravity | `agy` |
67
+ | Claude | `claude` |
68
+ | Codex | `codex` |
69
+ | Grok | `grok` |
69
70
 
70
71
  ## Quickstart
71
72
 
72
- Node.js 22+ on macOS, Linux, or Windows, and authenticated provider CLIs named by your
73
- configuration.
73
+ Requires Node.js 22+ (macOS, Linux, or Windows) and the provider CLIs your
74
+ config uses, already signed in.
74
75
 
75
76
  ### Install
76
77
 
77
78
  ```bash
78
79
  npm install -g @nodeint/migent
80
+ migent --version
79
81
  ```
80
82
 
81
- Verify with `migent --version`.
82
-
83
- ### Upgrade
84
-
85
- ```bash
86
- npm install -g @nodeint/migent@latest
87
- ```
83
+ Upgrade with `npm install -g @nodeint/migent@latest`.
88
84
 
89
85
  ### Create a project
90
86
 
91
- From the project where agents will work:
87
+ From the repository where agents will work:
92
88
 
93
89
  ```bash
94
- migent init --preset plan
90
+ migent init
95
91
  ```
96
92
 
97
- `init` writes the config, a plan-and-review workflow, and a gitignore for local
98
- session state:
93
+ In a terminal, `init` walks you through the provider, model, and options, then
94
+ offers to install the Migent skill so agents in the project know how to run it.
95
+ Without a terminal, pass `--provider` and `--model` together. Pass `--skill` to
96
+ install the skill without the prompt.
97
+
98
+ It creates:
99
99
 
100
100
  ```text
101
101
  .migent/
102
- ├── config.yaml
103
- ├── .gitignore
102
+ ├── config.yaml # models and roles
103
+ ├── .gitignore # keeps sessions/ untracked
104
104
  └── workflows/
105
- └── plan.yaml
106
- ```
105
+ └── plan.yaml # plan-and-review workflow
107
106
 
108
- In a terminal, pick the provider, model, and prompted options from the lists.
109
- Pass `--provider` and `--model` together when input is not a terminal. Commit
110
- `config.yaml` and `workflows/`. Leave `.migent/sessions/` untracked.
107
+ .agents/skills/migent/ # only if you install the skill
108
+ ```
111
109
 
112
- Model and role shape is in [Providers and model configuration](https://github.com/nodeint/migent/blob/main/docs/providers.md).
113
- A full plan workflow is in [Writing workflows](https://github.com/nodeint/migent/blob/main/docs/workflows.md#complete-example).
110
+ Commit `config.yaml` and `workflows/`. To add the skill later, run
111
+ `migent skill install`.
114
112
 
115
113
  ### Run
116
114
 
117
- From that project or any of its subdirectories:
115
+ From the project or any subdirectory:
118
116
 
119
117
  ```bash
120
- migent start plan --task "Plan the account settings redesign"
118
+ migent session run --workflow plan --task "Plan the account settings redesign"
121
119
  ```
122
120
 
123
- Migent runs the next eligible stage until the workflow completes or stops.
124
- Progress goes to stderr. Stdout is one JSON result.
121
+ `--task` is one line and is stored in the session as `task.md`. For a longer
122
+ task, create the session first, write the file, then start it:
125
123
 
126
124
  ```bash
127
- migent sessions
128
- migent watch <session-id>
129
- migent continue <session-id>
125
+ migent session prepare wave-3 --workflow plan
126
+ migent session run --session wave-3 --workflow plan
130
127
  ```
131
128
 
132
- Command fields and exit codes are in
133
- [CLI commands and execution controls](https://github.com/nodeint/migent/blob/main/docs/cli.md). Session files are in
134
- [Sessions and workflow composition](https://github.com/nodeint/migent/blob/main/docs/sessions.md).
129
+ Migent runs each eligible stage until the workflow completes or stops. Omit
130
+ `--workflow` and pass `--role` to run one role instead. A workflow run prints
131
+ one JSON result with the session id, status, stop reason, decision, and output
132
+ paths. A role run prints a table unless `--json`. Stderr prints one line when
133
+ a stage starts and one when it finishes. Then inspect or resume:
134
+
135
+ ```bash
136
+ migent session list
137
+ migent session list --latest --json
138
+ migent session follow <session-id>
139
+ migent session continue <session-id>
140
+ ```
141
+
142
+ `session list --json` and a JSON snapshot omit the task text.
143
+
144
+ ### Next
145
+
146
+ - [Providers and model configuration](https://github.com/nodeint/migent/blob/main/docs/providers.md)
147
+ - [Writing workflows](https://github.com/nodeint/migent/blob/main/docs/workflows.md#complete-example)
148
+ - [CLI commands and exit codes](https://github.com/nodeint/migent/blob/main/docs/cli.md)
149
+ - [Sessions and workflow composition](https://github.com/nodeint/migent/blob/main/docs/sessions.md)
135
150
 
136
151
  ## Concepts
137
152
 
@@ -147,8 +162,9 @@ A workflow connects stages through dependencies and explicit decision routes.
147
162
  - A **session** records executions, events, decisions, and artifacts so work can
148
163
  be inspected or resumed.
149
164
 
150
- Use `depends_on` to give a stage upstream artifacts. Use `session.resume_from`
151
- when the stage must also continue a provider's prior conversational session.
165
+ Use `depends_on` to give a stage upstream artifacts. Every visit of a stage
166
+ continues that stage's provider agent. Use `session.resume_from` when a stage
167
+ must continue a different stage's provider session.
152
168
 
153
169
  ## Current scope
154
170
 
@@ -1,7 +1,9 @@
1
- import { AgentToolRunner, CancellationRequested, ConfigurationError, createAgentSession, log, raiseCancellation, requireAgentSession, resolveRoleTarget, SessionStore, } from "@nodeint/migent-kernel";
1
+ import path from "node:path";
2
+ import { AgentToolRunner, CancellationRequested, ConfigurationError, createAgentSession, loadRoleInstructions, loadYamlMapping, log, prependRoleInstructions, requireAgentSession, isSessionLockLost, repairSessionUnlocked, SessionLock, resolveRoleTarget, SessionStore, } from "@nodeint/migent-kernel";
2
3
  import { printAgent, printError, printNotice } from "../display.js";
3
4
  import { cliHooks } from "../hooks.js";
4
5
  import { requireCurrentSchema } from "../migrate.js";
6
+ import { bindCancellationSignals } from "../signals.js";
5
7
  import { adapterMap, parseOptions } from "../provider.js";
6
8
  import { resolvePrompt } from "../workspace.js";
7
9
  export async function dispatchAgent(workspace, args) {
@@ -13,76 +15,126 @@ export async function dispatchAgent(workspace, args) {
13
15
  options: Object.keys(parsed).length > 0 ? parsed : null,
14
16
  });
15
17
  const [resolvedSessionId, createdSession] = resolveSession(workspace, args.sessionId ?? null, args.role, args.task ?? null);
16
- let resumeExecutionId = null;
17
- if (!createdSession) {
18
- const previous = new SessionStore(workspace).latestExecution(resolvedSessionId);
19
- if (previous !== null)
20
- resumeExecutionId = previous.executionId;
18
+ if (createdSession)
19
+ log(`session_id: ${resolvedSessionId}`);
20
+ let lock;
21
+ try {
22
+ lock = SessionLock.acquire(path.join(workspace, ".migent", "sessions", resolvedSessionId));
21
23
  }
22
- const optionLabel = Object.entries(target.options)
23
- .map(([key, value]) => `${key}: ${value}`)
24
- .join(", ") || "default";
25
- printNotice(`agent ${args.role} · ${target.provider}/${target.model} (${optionLabel}).`);
26
- const onSigint = () => {
27
- try {
28
- raiseCancellation(2);
29
- }
30
- catch {
31
- // The runner observes the pending cancellation.
32
- }
33
- };
34
- const onSigterm = () => {
35
- try {
36
- raiseCancellation(15);
37
- }
38
- catch {
39
- // The runner observes the pending cancellation.
24
+ catch (error) {
25
+ if (error instanceof Error) {
26
+ printError(error.message);
27
+ return 2;
40
28
  }
41
- };
42
- process.on("SIGINT", onSigint);
43
- process.on("SIGTERM", onSigterm);
44
- let result;
29
+ throw error;
30
+ }
45
31
  try {
46
- const runner = args.runner ?? cliHooks.runner ?? new AgentToolRunner(adapterMap());
47
- result = await runner.run({
48
- provider: target.provider,
49
- model: target.model,
50
- options: { ...target.options },
51
- prompt: resolvePrompt({
52
- prompt: args.prompt ?? null,
53
- promptFile: args.promptFile ?? null,
54
- promptStdin: args.promptStdin ?? false,
55
- }),
56
- workspace,
57
- sessionId: resolvedSessionId,
58
- resumeExecutionId,
59
- stageId: args.role,
60
- });
32
+ return await runLocked();
61
33
  }
62
34
  catch (error) {
63
- if (error instanceof CancellationRequested) {
64
- log(error.message);
65
- return 130;
66
- }
67
- if (error instanceof ConfigurationError)
68
- throw error;
69
- if (error instanceof Error) {
35
+ if (isSessionLockLost(error)) {
70
36
  printError(error.message);
71
37
  return 2;
72
38
  }
73
39
  throw error;
74
40
  }
75
41
  finally {
76
- process.off("SIGINT", onSigint);
77
- process.off("SIGTERM", onSigterm);
42
+ lock.release();
43
+ }
44
+ async function runLocked() {
45
+ let resumeExecutionId = null;
46
+ let resumeCancelled = false;
47
+ if (!createdSession) {
48
+ repairSessionUnlocked(workspace, resolvedSessionId);
49
+ const store = new SessionStore(workspace);
50
+ if (store.readSessionStatus(resolvedSessionId).status === "cancelled") {
51
+ resumeCancelled = true;
52
+ }
53
+ else {
54
+ const previous = store.latestExecution(resolvedSessionId);
55
+ if (previous !== null)
56
+ resumeExecutionId = previous.executionId;
57
+ }
58
+ }
59
+ const optionLabel = Object.entries(target.options)
60
+ .map(([key, value]) => `${key}: ${value}`)
61
+ .join(", ") || "default";
62
+ printNotice(`agent ${args.role} · ${target.provider}/${target.model} (${optionLabel}).`);
63
+ const unbind = bindCancellationSignals();
64
+ let result;
65
+ let resumedMany = null;
66
+ try {
67
+ const runner = args.runner ?? cliHooks.runner ?? new AgentToolRunner(adapterMap());
68
+ if (resumeCancelled) {
69
+ const reopened = new SessionStore(workspace).reopenCancelledExecutions(resolvedSessionId);
70
+ for (const item of reopened) {
71
+ log(`session ${resolvedSessionId} was cancelled during ${item.executionId}. Resuming that execution.`);
72
+ }
73
+ const settled = await Promise.allSettled(reopened.map((item) => runner.continueCancelled(workspace, resolvedSessionId, item.executionId)));
74
+ const rejected = settled.find((item) => item.status === "rejected");
75
+ if (rejected && rejected.status === "rejected")
76
+ throw rejected.reason;
77
+ const continued = settled.flatMap((item) => (item.status === "fulfilled" ? [item.value] : []));
78
+ if (continued.length === 0) {
79
+ throw new ConfigurationError(`Session ${resolvedSessionId} is cancelled without an execution.`);
80
+ }
81
+ if (continued.length === 1)
82
+ result = continued[0];
83
+ else
84
+ resumedMany = continued;
85
+ }
86
+ else {
87
+ result = await runner.run({
88
+ provider: target.provider,
89
+ model: target.model,
90
+ options: { ...target.options },
91
+ prompt: prependRoleInstructions(loadRoleInstructions(loadYamlMapping(path.join(workspace, ".migent", "config.yaml")), args.role), resolvePrompt({
92
+ prompt: args.prompt ?? null,
93
+ promptFile: args.promptFile ?? null,
94
+ promptStdin: args.promptStdin ?? false,
95
+ })),
96
+ workspace,
97
+ sessionId: resolvedSessionId,
98
+ resumeExecutionId,
99
+ stageId: args.role,
100
+ });
101
+ }
102
+ }
103
+ catch (error) {
104
+ if (error instanceof CancellationRequested) {
105
+ log(error.message);
106
+ return 130;
107
+ }
108
+ if (error instanceof ConfigurationError)
109
+ throw error;
110
+ if (error instanceof Error) {
111
+ printError(error.message);
112
+ return 2;
113
+ }
114
+ throw error;
115
+ }
116
+ finally {
117
+ unbind();
118
+ }
119
+ if (resumedMany !== null) {
120
+ if (args.asJson)
121
+ process.stdout.write(`${JSON.stringify(resumedMany)}\n`);
122
+ else {
123
+ for (const item of resumedMany)
124
+ printAgent({ ...item }, args.role, target.provider, target.model);
125
+ }
126
+ if (resumedMany.some((item) => item.failure_reason === "verify_exhausted"))
127
+ return 6;
128
+ return resumedMany.every((item) => item.outcome_status === "complete") ? 0 : 1;
129
+ }
130
+ if (result === undefined)
131
+ throw new Error("Agent run produced no result.");
132
+ if (args.asJson)
133
+ process.stdout.write(`${JSON.stringify(result)}\n`);
134
+ else
135
+ printAgent({ ...result }, args.role, target.provider, target.model);
136
+ return result.outcome_status === "complete" ? 0 : 1;
78
137
  }
79
- if (createdSession)
80
- printNotice(`session_id: ${result.session_id}`);
81
- if (args.asJson)
82
- process.stdout.write(`${JSON.stringify(result)}\n`);
83
- else
84
- printAgent({ ...result }, args.role, target.provider, target.model);
85
- return result.outcome_status === "complete" ? 0 : 1;
86
138
  }
87
139
  function resolveSession(workspace, sessionId, role, task) {
88
140
  if (sessionId !== null && typeof task === "string" && task.trim() !== "") {
@@ -3,6 +3,7 @@ import path from "node:path";
3
3
  import { confirm as clackConfirm, isCancel, select as clackSelect, text as clackText } from "@clack/prompts";
4
4
  import { ConfigEditError, ConfigurationError, assignRoles, describeChange, diagnose, inspectConfig, loadModelTarget, loadWorkflowCatalog, loadYamlMapping, matchingModelName, patchConfigText, placeRoleModel, resolveStageSpec, setRoleOptions, upsertModel, whichSync, } from "@nodeint/migent-kernel";
5
5
  import { printError } from "../display.js";
6
+ import { PromptCancelled } from "../errors.js";
6
7
  import { cliHooks } from "../hooks.js";
7
8
  import { requireCurrentSchema } from "../migrate.js";
8
9
  import { acceptedOptions, adapterMap, asCatalog, detected, knownModel, loadCatalog, loadOptionValues, parseOptions, requireAdapter, runProviderCommand, } from "../provider.js";
@@ -25,6 +26,8 @@ export async function runConfigModel(workspace, options = {}) {
25
26
  });
26
27
  }
27
28
  catch (error) {
29
+ if (error instanceof PromptCancelled)
30
+ throw error;
28
31
  if (error instanceof ConfigurationError || error instanceof ConfigEditError || error instanceof Error) {
29
32
  printError(error.message);
30
33
  return 2;
@@ -43,6 +46,8 @@ export async function runConfigRole(workspace, options = {}) {
43
46
  });
44
47
  }
45
48
  catch (error) {
49
+ if (error instanceof PromptCancelled)
50
+ throw error;
46
51
  if (error instanceof ConfigurationError || error instanceof ConfigEditError || error instanceof Error) {
47
52
  printError(error.message);
48
53
  return 2;
@@ -382,7 +387,7 @@ function optionUpdatesOf(options, clearOptions) {
382
387
  async function selectProvider(prompter, adapters, which, message, defaultValue) {
383
388
  const rows = detected(adapters, which);
384
389
  if (rows.length === 0)
385
- throw new Error("No providers are available.");
390
+ throw new ConfigurationError("No providers are available.");
386
391
  const names = new Set(rows.map(([name]) => name));
387
392
  return prompter.select(message, rows.map(([name, found]) => [`${name} ${found ?? "not found"}`, name]), defaultValue !== null && names.has(defaultValue) ? defaultValue : rows[0]?.[0]);
388
393
  }
@@ -486,6 +491,7 @@ function roleJson(role) {
486
491
  provider: role.provider,
487
492
  model: role.model,
488
493
  options: { ...role.options },
494
+ instructions: [...role.instructions],
489
495
  shared_with: [...role.sharedWith],
490
496
  stages: role.stages.map((stage) => ({
491
497
  workflow: stage.workflow,
@@ -586,7 +592,7 @@ function isRecord(value) {
586
592
  class ClackConfigPrompter {
587
593
  async select(message, choices, defaultValue) {
588
594
  if (choices.length === 0)
589
- throw new Error("Choose a value.");
595
+ throw new ConfigurationError("Choose a value.");
590
596
  const values = new Set(choices.map(([, value]) => value));
591
597
  const initial = defaultValue != null && values.has(defaultValue) ? defaultValue : choices[0]?.[1];
592
598
  const result = await clackSelect({
@@ -594,20 +600,26 @@ class ClackConfigPrompter {
594
600
  options: choices.map(([label, value]) => ({ label, value })),
595
601
  initialValue: initial,
596
602
  });
597
- if (isCancel(result) || typeof result !== "string" || !values.has(result))
598
- throw new Error("Choose a value.");
603
+ if (isCancel(result))
604
+ throw new PromptCancelled();
605
+ if (typeof result !== "string" || !values.has(result))
606
+ throw new ConfigurationError("Choose a value.");
599
607
  return result;
600
608
  }
601
609
  async confirm(message) {
602
610
  const result = await clackConfirm({ message, initialValue: true });
603
- if (isCancel(result) || typeof result !== "boolean")
604
- throw new Error("Answer yes or no.");
611
+ if (isCancel(result))
612
+ throw new PromptCancelled();
613
+ if (typeof result !== "boolean")
614
+ throw new ConfigurationError("Answer yes or no.");
605
615
  return result;
606
616
  }
607
617
  async text(message, defaultValue = "") {
608
618
  const result = await clackText({ message, initialValue: defaultValue });
609
- if (isCancel(result) || typeof result !== "string")
610
- throw new Error("Enter a value.");
619
+ if (isCancel(result))
620
+ throw new PromptCancelled();
621
+ if (typeof result !== "string")
622
+ throw new ConfigurationError("Enter a value.");
611
623
  return result;
612
624
  }
613
625
  }
@@ -7,6 +7,12 @@ export interface ExecuteWorkflowOptions {
7
7
  priorSessionId?: string | null;
8
8
  maxAttempts?: number | null;
9
9
  maxDispatches?: number | null;
10
+ stageId?: string | null;
10
11
  runner?: AgentToolRunner | null;
12
+ verbose?: boolean;
13
+ itemProgress?: boolean;
14
+ startPrepared?: boolean;
15
+ refreshWorkflow?: boolean;
16
+ reply?: string | null;
11
17
  }
12
18
  export declare function executeWorkflow(workspace: string, options: ExecuteWorkflowOptions): Promise<number>;
@@ -2,38 +2,54 @@ import { AgentToolRunner, DEFAULT_MAX_DISPATCHES, executeNamedWorkflow, } from "
2
2
  import { cliHooks } from "../hooks.js";
3
3
  import { requireCurrentSchema } from "../migrate.js";
4
4
  import { adapterMap } from "../provider.js";
5
+ import { text } from "../text.js";
5
6
  export async function executeWorkflow(workspace, options) {
6
7
  requireCurrentSchema(workspace);
7
8
  const once = options.action === "stage";
8
- const creating = options.action === "start" || (once && text(options.sessionId) === null);
9
+ const startPrepared = options.startPrepared === true;
10
+ const sessionId = text(options.sessionId);
11
+ const creating = !startPrepared && (options.action === "start" || (once && sessionId === null));
9
12
  const maxAttempts = options.maxAttempts === undefined ? 3 : options.maxAttempts;
10
13
  const maxDispatches = options.maxDispatches === undefined ? DEFAULT_MAX_DISPATCHES : options.maxDispatches;
11
14
  const runner = options.runner ?? cliHooks.runner ?? new AgentToolRunner(adapterMap());
15
+ runner.itemProgress = options.itemProgress === true;
12
16
  const result = await executeNamedWorkflow(workspace, {
13
- workflowId: creating ? (options.workflowId ?? null) : null,
17
+ workflowId: creating || startPrepared ? (options.workflowId ?? null) : null,
14
18
  task: creating ? (options.task ?? null) : null,
15
- sessionId: creating ? null : (options.sessionId ?? null),
19
+ sessionId: creating ? null : sessionId,
16
20
  priorSessionId: options.priorSessionId ?? null,
21
+ startPrepared,
17
22
  maxAttempts: once ? null : maxAttempts,
18
23
  maxDispatches: once ? null : maxDispatches,
24
+ stageId: options.stageId ?? null,
19
25
  once,
20
26
  resetRevisions: options.action === "continue",
27
+ refreshWorkflow: options.refreshWorkflow === true,
28
+ reply: options.reply ?? null,
21
29
  }, runner);
22
- printResult(result);
30
+ printResult(result, options.verbose === true);
31
+ if (result.stopReason === "retry_exhausted" ||
32
+ result.stopReason === "revisions_exhausted" ||
33
+ result.stopReason === "dispatch_limit" ||
34
+ result.stopReason === "verify_exhausted") {
35
+ process.stderr.write(`Resume with: migent session continue ${result.sessionId}\n`);
36
+ }
23
37
  return result.code;
24
38
  }
25
- function printResult(result) {
26
- process.stdout.write(`${JSON.stringify({
39
+ const INSPECT_EXIT_CODES = new Set([1, 3, 6]);
40
+ function printResult(result, verbose) {
41
+ const payload = {
27
42
  session_id: result.sessionId,
28
43
  session_status: result.status,
29
44
  stop_reason: result.stopReason,
45
+ latest_decision: result.latestDecision,
30
46
  outputs: result.outputs,
31
- stages: result.stages,
32
- })}\n`);
33
- }
34
- function text(value) {
35
- if (typeof value !== "string")
36
- return null;
37
- const stripped = value.trim();
38
- return stripped === "" ? null : stripped;
47
+ };
48
+ if (INSPECT_EXIT_CODES.has(result.code) && result.latestExecutionId) {
49
+ payload.latest_execution_id = result.latestExecutionId;
50
+ payload.response_path = `executions/${result.latestExecutionId}/response.md`;
51
+ }
52
+ if (verbose)
53
+ payload.stages = result.stages;
54
+ process.stdout.write(`${JSON.stringify(payload)}\n`);
39
55
  }
@@ -18,6 +18,8 @@ export declare function runInit(workspace: string, options?: {
18
18
  model?: string | null;
19
19
  options?: readonly string[];
20
20
  preset?: string;
21
+ presetExplicit?: boolean;
22
+ skill?: boolean;
21
23
  interactive?: boolean;
22
24
  adapters?: Record<string, AgentAdapter> | null;
23
25
  which?: Which | null;