@nodeint/migent 0.1.5 → 0.1.7
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/README.md +195 -151
- package/dist/commands/agent.d.ts +8 -1
- package/dist/commands/agent.js +292 -19
- package/dist/commands/config.d.ts +3 -3
- package/dist/commands/config.js +57 -55
- package/dist/commands/doctor.js +2 -2
- package/dist/commands/execute.d.ts +1 -2
- package/dist/commands/execute.js +95 -26
- package/dist/commands/init.d.ts +3 -3
- package/dist/commands/init.js +20 -20
- package/dist/commands/prepare.js +1 -0
- package/dist/commands/provider.d.ts +10 -0
- package/dist/commands/provider.js +77 -0
- package/dist/commands/sessions.js +11 -10
- package/dist/commands/skill.js +8 -1
- package/dist/display.d.ts +17 -0
- package/dist/display.js +43 -1
- package/dist/exits.d.ts +3 -1
- package/dist/exits.js +9 -2
- package/dist/hooks.d.ts +2 -0
- package/dist/hooks.js +1 -0
- package/dist/index.js +1 -0
- package/dist/migrate.d.ts +3 -3
- package/dist/migrate.js +26 -24
- package/dist/program.js +78 -31
- package/dist/provider.d.ts +13 -11
- package/dist/provider.js +38 -33
- package/dist/session-row.d.ts +2 -0
- package/dist/session-row.js +2 -0
- package/dist/templates/migent/SKILL.md +159 -157
- package/dist/templates/migent/references/config.md +56 -0
- package/dist/templates/migent/references/inspect.md +32 -0
- package/dist/templates/migent/references/schema-config.yaml +10 -1
- package/dist/templates/migent/references/schema-workflow.yaml +12 -2
- package/dist/templates/migent/references/sessions.md +65 -0
- package/package.json +4 -4
package/dist/commands/config.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { readFileSync, writeFileSync } from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { confirm as clackConfirm, isCancel, select as clackSelect, text as clackText } from "@clack/prompts";
|
|
4
|
-
import { ConfigEditError, ConfigurationError, assignRoles, describeChange, diagnose, inspectConfig, loadModelTarget, loadWorkflowCatalog, loadYamlMapping, matchingModelName, patchConfigText, placeRoleModel, resolveStageSpec, setRoleOptions, upsertModel, whichSync, } from "@nodeint/migent-kernel";
|
|
4
|
+
import { ConfigEditError, ConfigurationError, assignRoles, describeChange, diagnose, inspectConfig, loadModelTarget, loadWorkflowCatalog, loadYamlMapping, matchingModelName, patchConfigText, placeRoleModel, isRoleStage, resolveStageSpec, setRoleOptions, upsertModel, whichSync, } from "@nodeint/migent-kernel";
|
|
5
5
|
import { printError } from "../display.js";
|
|
6
6
|
import { PromptCancelled } from "../errors.js";
|
|
7
7
|
import { cliHooks } from "../hooks.js";
|
|
8
8
|
import { requireCurrentSchema } from "../migrate.js";
|
|
9
|
-
import { acceptedOptions,
|
|
9
|
+
import { acceptedOptions, providerMap, asCatalog, detected, knownModel, loadCatalog, loadOptionValues, parseOptions, requireProvider, runProviderCommand, } from "../provider.js";
|
|
10
10
|
import { runDoctor } from "./doctor.js";
|
|
11
11
|
const NEW = "__new__";
|
|
12
12
|
const UNSET = Symbol("unset");
|
|
@@ -19,7 +19,7 @@ export async function runConfigModel(workspace, options = {}) {
|
|
|
19
19
|
try {
|
|
20
20
|
return await runModel(workspace, {
|
|
21
21
|
...options,
|
|
22
|
-
|
|
22
|
+
providers: options.providers ?? providerMap(),
|
|
23
23
|
which: options.which ?? cliHooks.which ?? whichSync,
|
|
24
24
|
prompter: options.prompter ?? new ClackConfigPrompter(),
|
|
25
25
|
runCommand: options.runCommand ?? runProviderCommand,
|
|
@@ -39,7 +39,7 @@ export async function runConfigRole(workspace, options = {}) {
|
|
|
39
39
|
try {
|
|
40
40
|
return await runRole(workspace, {
|
|
41
41
|
...options,
|
|
42
|
-
|
|
42
|
+
providers: options.providers ?? providerMap(),
|
|
43
43
|
which: options.which ?? cliHooks.which ?? whichSync,
|
|
44
44
|
prompter: options.prompter ?? new ClackConfigPrompter(),
|
|
45
45
|
runCommand: options.runCommand ?? runProviderCommand,
|
|
@@ -69,7 +69,7 @@ async function runModel(workspace, args) {
|
|
|
69
69
|
const mutating = provider !== null || optionUpdates !== UNSET;
|
|
70
70
|
if (key === null && !mutating) {
|
|
71
71
|
if (interactive) {
|
|
72
|
-
return interactiveModels(workspace, config, workflows, view, args.
|
|
72
|
+
return interactiveModels(workspace, config, workflows, view, args.providers, args.which, args.prompter, args.runCommand, asJson);
|
|
73
73
|
}
|
|
74
74
|
emitView(view, asJson, true, false);
|
|
75
75
|
return 0;
|
|
@@ -78,19 +78,19 @@ async function runModel(workspace, args) {
|
|
|
78
78
|
throw new ConfigEditError("Pass a name.");
|
|
79
79
|
if (!mutating) {
|
|
80
80
|
if (interactive) {
|
|
81
|
-
return editModel(workspace, config, workflows, view, key, args.
|
|
81
|
+
return editModel(workspace, config, workflows, view, key, args.providers, args.which, args.prompter, args.runCommand, asJson);
|
|
82
82
|
}
|
|
83
83
|
emitModels(selectedModels(view, [key]), asJson);
|
|
84
84
|
return 0;
|
|
85
85
|
}
|
|
86
86
|
let catalog = null;
|
|
87
87
|
if (provider !== null && model !== null) {
|
|
88
|
-
[provider, model, catalog] = await catalogModel(args.
|
|
88
|
+
[provider, model, catalog] = await catalogModel(args.providers, args.which, args.runCommand, provider, model);
|
|
89
89
|
}
|
|
90
90
|
if (optionUpdates !== UNSET) {
|
|
91
91
|
const currentProvider = provider ?? modelProvider(config, key);
|
|
92
92
|
const currentModel = model ?? loadModelTarget(config, key).model;
|
|
93
|
-
optionUpdates = await checkedOptions(args.
|
|
93
|
+
optionUpdates = await checkedOptions(args.providers, args.which, args.runCommand, currentProvider, currentModel, optionUpdates, provider !== null ? catalog : null);
|
|
94
94
|
}
|
|
95
95
|
const change = upsertModel(config, key, {
|
|
96
96
|
provider,
|
|
@@ -98,7 +98,7 @@ async function runModel(workspace, args) {
|
|
|
98
98
|
...(optionUpdates === UNSET ? {} : { options: optionUpdates }),
|
|
99
99
|
workflows,
|
|
100
100
|
});
|
|
101
|
-
validateChange(config, change.config, workflows, args.
|
|
101
|
+
validateChange(config, change.config, workflows, args.providers);
|
|
102
102
|
return await commit(workspace, change, { asJson, which: args.which });
|
|
103
103
|
}
|
|
104
104
|
async function runRole(workspace, args) {
|
|
@@ -119,7 +119,7 @@ async function runRole(workspace, args) {
|
|
|
119
119
|
const mutating = modelName !== null || provider !== null || optionUpdates !== UNSET;
|
|
120
120
|
if (roles.length === 0 && !mutating) {
|
|
121
121
|
if (interactive) {
|
|
122
|
-
return interactiveRoles(workspace, config, workflows, view, args.
|
|
122
|
+
return interactiveRoles(workspace, config, workflows, view, args.providers, args.which, args.prompter, args.runCommand, asJson);
|
|
123
123
|
}
|
|
124
124
|
emitView(view, asJson, false, true);
|
|
125
125
|
return 0;
|
|
@@ -132,7 +132,7 @@ async function runRole(workspace, args) {
|
|
|
132
132
|
if (!mutating) {
|
|
133
133
|
if (interactive && roles.length === 1) {
|
|
134
134
|
knownRole(view, roles[0] ?? "");
|
|
135
|
-
return pointRole(workspace, config, workflows, view, roles[0] ?? "", args.
|
|
135
|
+
return pointRole(workspace, config, workflows, view, roles[0] ?? "", args.providers, args.which, args.prompter, args.runCommand, asJson);
|
|
136
136
|
}
|
|
137
137
|
emitRoles(selectedRoles(view, roles), asJson);
|
|
138
138
|
return 0;
|
|
@@ -140,9 +140,9 @@ async function runRole(workspace, args) {
|
|
|
140
140
|
let change;
|
|
141
141
|
if (provider !== null && model !== null) {
|
|
142
142
|
let catalog;
|
|
143
|
-
[provider, model, catalog] = await catalogModel(args.
|
|
143
|
+
[provider, model, catalog] = await catalogModel(args.providers, args.which, args.runCommand, provider, model);
|
|
144
144
|
if (optionUpdates !== UNSET) {
|
|
145
|
-
optionUpdates = await checkedOptions(args.
|
|
145
|
+
optionUpdates = await checkedOptions(args.providers, args.which, args.runCommand, provider, model, optionUpdates, catalog);
|
|
146
146
|
}
|
|
147
147
|
change = placeRoleModel(config, roles[0] ?? "", {
|
|
148
148
|
provider,
|
|
@@ -155,7 +155,7 @@ async function runRole(workspace, args) {
|
|
|
155
155
|
change = assignRoles(config, roles, modelName, { workflows });
|
|
156
156
|
if (optionUpdates !== UNSET) {
|
|
157
157
|
const target = loadModelTarget(change.config, modelName);
|
|
158
|
-
optionUpdates = await checkedOptions(args.
|
|
158
|
+
optionUpdates = await checkedOptions(args.providers, args.which, args.runCommand, target.provider, target.model, optionUpdates);
|
|
159
159
|
const follow = setRoleOptions(change.config, roles[0] ?? "", optionUpdates, { workflows });
|
|
160
160
|
change = describeChange(config, follow.config, workflows);
|
|
161
161
|
}
|
|
@@ -164,40 +164,40 @@ async function runRole(workspace, args) {
|
|
|
164
164
|
const target = roleModel(config, roles[0] ?? "");
|
|
165
165
|
if (optionUpdates === UNSET)
|
|
166
166
|
throw new ConfigEditError("Pass a role.");
|
|
167
|
-
optionUpdates = await checkedOptions(args.
|
|
167
|
+
optionUpdates = await checkedOptions(args.providers, args.which, args.runCommand, target.provider, target.model, optionUpdates);
|
|
168
168
|
change = setRoleOptions(config, roles[0] ?? "", optionUpdates, { workflows });
|
|
169
169
|
}
|
|
170
|
-
validateChange(config, change.config, workflows, args.
|
|
170
|
+
validateChange(config, change.config, workflows, args.providers);
|
|
171
171
|
return await commit(workspace, change, { asJson, which: args.which });
|
|
172
172
|
}
|
|
173
|
-
async function interactiveModels(workspace, config, workflows, view,
|
|
173
|
+
async function interactiveModels(workspace, config, workflows, view, providers, which, prompter, runCommand, asJson) {
|
|
174
174
|
const choices = view.models.map((model) => [modelLabel(model), model.key]);
|
|
175
175
|
choices.push(["New model", NEW]);
|
|
176
176
|
const selected = await prompter.select("Model", choices, view.models[0]?.key ?? NEW);
|
|
177
177
|
if (selected === NEW)
|
|
178
|
-
return addModel(workspace, config, workflows, null,
|
|
179
|
-
return editModel(workspace, config, workflows, view, selected,
|
|
178
|
+
return addModel(workspace, config, workflows, null, providers, which, prompter, runCommand, asJson);
|
|
179
|
+
return editModel(workspace, config, workflows, view, selected, providers, which, prompter, runCommand, asJson);
|
|
180
180
|
}
|
|
181
|
-
async function editModel(workspace, config, workflows, view, key,
|
|
181
|
+
async function editModel(workspace, config, workflows, view, key, providers, which, prompter, runCommand, asJson) {
|
|
182
182
|
const current = selectedModels(view, [key])[0];
|
|
183
183
|
if (current === undefined)
|
|
184
184
|
throw new ConfigEditError(`Unknown name: ${key}.`);
|
|
185
|
-
const provider = await selectProvider(prompter,
|
|
186
|
-
const
|
|
187
|
-
const catalog = await loadCatalog(
|
|
185
|
+
const provider = await selectProvider(prompter, providers, which, "Provider", current.provider);
|
|
186
|
+
const impl = requireProvider(providers, provider);
|
|
187
|
+
const catalog = await loadCatalog(impl, which, runCommand);
|
|
188
188
|
const initial = catalog.ids.includes(current.model) ? current.model : (catalog.defaultId ?? undefined);
|
|
189
189
|
let modelId = knownModel(await prompter.select("Model", catalog.ids.map((item) => [item, item]), initial), catalog);
|
|
190
|
-
const stored = await promptedOptions(prompter,
|
|
190
|
+
const stored = await promptedOptions(prompter, impl, modelId, catalog, which, runCommand, current.options);
|
|
191
191
|
const change = upsertModel(config, key, { provider, model: modelId, options: stored, workflows });
|
|
192
|
-
validateChange(config, change.config, workflows,
|
|
192
|
+
validateChange(config, change.config, workflows, providers);
|
|
193
193
|
return confirmAndCommit(workspace, change, prompter, asJson, which);
|
|
194
194
|
}
|
|
195
|
-
async function addModel(workspace, config, workflows, role,
|
|
196
|
-
const provider = await selectProvider(prompter,
|
|
197
|
-
const
|
|
198
|
-
const catalog = await loadCatalog(
|
|
195
|
+
async function addModel(workspace, config, workflows, role, providers, which, prompter, runCommand, asJson) {
|
|
196
|
+
const provider = await selectProvider(prompter, providers, which, "Provider", null);
|
|
197
|
+
const impl = requireProvider(providers, provider);
|
|
198
|
+
const catalog = await loadCatalog(impl, which, runCommand);
|
|
199
199
|
const modelId = knownModel(await prompter.select("Model", catalog.ids.map((item) => [item, item]), catalog.defaultId), catalog);
|
|
200
|
-
const options = await promptedOptions(prompter,
|
|
200
|
+
const options = await promptedOptions(prompter, impl, modelId, catalog, which, runCommand, {});
|
|
201
201
|
const present = presentOptions(options);
|
|
202
202
|
const existing = matchingModelName(config, { provider, model: modelId, options: present });
|
|
203
203
|
let change;
|
|
@@ -224,10 +224,10 @@ async function addModel(workspace, config, workflows, role, adapters, which, pro
|
|
|
224
224
|
change = describeChange(config, assigned.config, workflows);
|
|
225
225
|
}
|
|
226
226
|
}
|
|
227
|
-
validateChange(config, change.config, workflows,
|
|
227
|
+
validateChange(config, change.config, workflows, providers);
|
|
228
228
|
return confirmAndCommit(workspace, change, prompter, asJson, which);
|
|
229
229
|
}
|
|
230
|
-
async function interactiveRoles(workspace, config, workflows, view,
|
|
230
|
+
async function interactiveRoles(workspace, config, workflows, view, providers, which, prompter, runCommand, asJson) {
|
|
231
231
|
const choices = view.roles.map((role) => [roleLabel(role), role.role]);
|
|
232
232
|
choices.push(["New role", NEW]);
|
|
233
233
|
const selected = await prompter.select("Role", choices, view.roles[0]?.role ?? NEW);
|
|
@@ -237,20 +237,20 @@ async function interactiveRoles(workspace, config, workflows, view, adapters, wh
|
|
|
237
237
|
throw new ConfigEditError("Role name is required.");
|
|
238
238
|
if (view.roles.some((role) => role.role === name))
|
|
239
239
|
throw new ConfigEditError(`Role ${name} already exists.`);
|
|
240
|
-
return pointRole(workspace, config, workflows, view, name,
|
|
240
|
+
return pointRole(workspace, config, workflows, view, name, providers, which, prompter, runCommand, asJson);
|
|
241
241
|
}
|
|
242
|
-
return pointRole(workspace, config, workflows, view, selected,
|
|
242
|
+
return pointRole(workspace, config, workflows, view, selected, providers, which, prompter, runCommand, asJson);
|
|
243
243
|
}
|
|
244
|
-
async function pointRole(workspace, config, workflows, view, role,
|
|
244
|
+
async function pointRole(workspace, config, workflows, view, role, providers, which, prompter, runCommand, asJson) {
|
|
245
245
|
const current = view.roles.find((item) => item.role === role) ?? null;
|
|
246
246
|
const choices = view.models.map((model) => [modelLabel(model), model.key]);
|
|
247
247
|
choices.push(["New model", NEW]);
|
|
248
248
|
const selected = await prompter.select("Model", choices, current?.modelName ?? NEW);
|
|
249
249
|
if (selected === NEW) {
|
|
250
|
-
return addModel(workspace, config, workflows, role,
|
|
250
|
+
return addModel(workspace, config, workflows, role, providers, which, prompter, runCommand, asJson);
|
|
251
251
|
}
|
|
252
252
|
const change = assignRoles(config, [role], selected, { workflows });
|
|
253
|
-
validateChange(config, change.config, workflows,
|
|
253
|
+
validateChange(config, change.config, workflows, providers);
|
|
254
254
|
return confirmAndCommit(workspace, change, prompter, asJson, which);
|
|
255
255
|
}
|
|
256
256
|
async function confirmAndCommit(workspace, change, prompter, asJson, which) {
|
|
@@ -274,7 +274,7 @@ async function commit(workspace, change, args) {
|
|
|
274
274
|
}
|
|
275
275
|
writeFileSync(filePath, updated, "utf8");
|
|
276
276
|
if (args.asJson) {
|
|
277
|
-
const checks = diagnose(workspace, {
|
|
277
|
+
const checks = diagnose(workspace, { providers: providerMap(), which: args.which });
|
|
278
278
|
const doctor = doctorPayload(checks);
|
|
279
279
|
process.stdout.write(`${JSON.stringify(changePayload(change, true, doctor))}\n`);
|
|
280
280
|
return doctor.ok ? 0 : 1;
|
|
@@ -283,7 +283,7 @@ async function commit(workspace, change, args) {
|
|
|
283
283
|
printChange(change);
|
|
284
284
|
return runDoctor(workspace, { which: args.which });
|
|
285
285
|
}
|
|
286
|
-
function validateChange(before, after, workflows,
|
|
286
|
+
function validateChange(before, after, workflows, providers) {
|
|
287
287
|
const beforeModels = isRecord(before.models) ? before.models : {};
|
|
288
288
|
const afterModels = isRecord(after.models) ? after.models : {};
|
|
289
289
|
const changed = new Set();
|
|
@@ -294,7 +294,7 @@ function validateChange(before, after, workflows, adapters) {
|
|
|
294
294
|
}
|
|
295
295
|
for (const key of changed) {
|
|
296
296
|
const target = loadModelTarget(after, key);
|
|
297
|
-
|
|
297
|
+
requireProvider(providers, target.provider).validateOptions(target.options);
|
|
298
298
|
}
|
|
299
299
|
const beforeRoles = isRecord(before.roles) ? before.roles : {};
|
|
300
300
|
const afterRoles = isRecord(after.roles) ? after.roles : {};
|
|
@@ -312,10 +312,12 @@ function validateChange(before, after, workflows, adapters) {
|
|
|
312
312
|
options[optionName] = value.trim();
|
|
313
313
|
}
|
|
314
314
|
}
|
|
315
|
-
|
|
315
|
+
requireProvider(providers, target.provider).validateOptions(options);
|
|
316
316
|
}
|
|
317
317
|
for (const document of workflows) {
|
|
318
318
|
for (const stage of Object.values(document.stages)) {
|
|
319
|
+
if (!isRoleStage(stage))
|
|
320
|
+
continue;
|
|
319
321
|
let beforeTarget;
|
|
320
322
|
let afterTarget;
|
|
321
323
|
try {
|
|
@@ -329,35 +331,35 @@ function validateChange(before, after, workflows, adapters) {
|
|
|
329
331
|
}
|
|
330
332
|
if (sameTarget(beforeTarget, afterTarget))
|
|
331
333
|
continue;
|
|
332
|
-
|
|
334
|
+
requireProvider(providers, afterTarget.provider).validateOptions(afterTarget.options);
|
|
333
335
|
}
|
|
334
336
|
}
|
|
335
337
|
}
|
|
336
|
-
async function catalogModel(
|
|
337
|
-
const
|
|
338
|
-
const catalog = await loadCatalog(
|
|
338
|
+
async function catalogModel(providers, which, runCommand, provider, model) {
|
|
339
|
+
const impl = requireProvider(providers, provider);
|
|
340
|
+
const catalog = await loadCatalog(impl, which, runCommand);
|
|
339
341
|
return [provider, knownModel(model, catalog), catalog];
|
|
340
342
|
}
|
|
341
|
-
async function checkedOptions(
|
|
342
|
-
const
|
|
343
|
-
const resolved = catalog ?? (await loadCatalog(
|
|
343
|
+
async function checkedOptions(providers, which, runCommand, provider, model, updates, catalog = null) {
|
|
344
|
+
const impl = requireProvider(providers, provider);
|
|
345
|
+
const resolved = catalog ?? (await loadCatalog(impl, which, runCommand));
|
|
344
346
|
const present = {};
|
|
345
347
|
for (const [key, value] of Object.entries(updates)) {
|
|
346
348
|
if (value !== null)
|
|
347
349
|
present[key] = value;
|
|
348
350
|
}
|
|
349
351
|
if (Object.keys(present).length > 0) {
|
|
350
|
-
await acceptedOptions(
|
|
352
|
+
await acceptedOptions(impl, model, resolved, which, runCommand, present);
|
|
351
353
|
}
|
|
352
354
|
return { ...updates };
|
|
353
355
|
}
|
|
354
|
-
async function promptedOptions(prompter,
|
|
356
|
+
async function promptedOptions(prompter, provider, model, catalog, which, runCommand, current) {
|
|
355
357
|
const stored = {};
|
|
356
|
-
const
|
|
357
|
-
for (const spec of
|
|
358
|
+
const catalogProvider = asCatalog(provider);
|
|
359
|
+
for (const spec of catalogProvider.providerOptions()) {
|
|
358
360
|
if (!spec.prompt)
|
|
359
361
|
continue;
|
|
360
|
-
const values = await loadOptionValues(
|
|
362
|
+
const values = await loadOptionValues(provider, spec.name, model, catalog, which, runCommand);
|
|
361
363
|
if (values === null || values.length === 0)
|
|
362
364
|
continue;
|
|
363
365
|
const choices = values.map((value) => [value, value]);
|
|
@@ -384,8 +386,8 @@ function optionUpdatesOf(options, clearOptions) {
|
|
|
384
386
|
}
|
|
385
387
|
return updates;
|
|
386
388
|
}
|
|
387
|
-
async function selectProvider(prompter,
|
|
388
|
-
const rows = detected(
|
|
389
|
+
async function selectProvider(prompter, providers, which, message, defaultValue) {
|
|
390
|
+
const rows = detected(providers, which);
|
|
389
391
|
if (rows.length === 0)
|
|
390
392
|
throw new ConfigurationError("No providers are available.");
|
|
391
393
|
const names = new Set(rows.map(([name]) => name));
|
package/dist/commands/doctor.js
CHANGED
|
@@ -2,11 +2,11 @@ import { diagnose, whichSync } from "@nodeint/migent-kernel";
|
|
|
2
2
|
import { printDoctor } from "../display.js";
|
|
3
3
|
import { cliHooks } from "../hooks.js";
|
|
4
4
|
import { requireCurrentSchema } from "../migrate.js";
|
|
5
|
-
import {
|
|
5
|
+
import { providerMap } from "../provider.js";
|
|
6
6
|
export async function runDoctor(workspace, options = {}) {
|
|
7
7
|
requireCurrentSchema(workspace);
|
|
8
8
|
const which = options.which ?? cliHooks.which ?? whichSync;
|
|
9
|
-
const checks = diagnose(workspace, {
|
|
9
|
+
const checks = diagnose(workspace, { providers: providerMap(), which });
|
|
10
10
|
if (options.asJson)
|
|
11
11
|
process.stdout.write(`${JSON.stringify(payload(checks))}\n`);
|
|
12
12
|
else
|
|
@@ -5,11 +5,10 @@ export interface ExecuteWorkflowOptions {
|
|
|
5
5
|
task?: string | null;
|
|
6
6
|
sessionId?: string | null;
|
|
7
7
|
priorSessionId?: string | null;
|
|
8
|
-
maxAttempts?: number | null;
|
|
9
8
|
maxDispatches?: number | null;
|
|
9
|
+
continueFailed?: boolean;
|
|
10
10
|
stageId?: string | null;
|
|
11
11
|
runner?: AgentToolRunner | null;
|
|
12
|
-
verbose?: boolean;
|
|
13
12
|
itemProgress?: boolean;
|
|
14
13
|
startPrepared?: boolean;
|
|
15
14
|
refreshWorkflow?: boolean;
|
package/dist/commands/execute.js
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { readdirSync, readFileSync, realpathSync } from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { AgentToolRunner, DEFAULT_MAX_DISPATCHES, executeNamedWorkflow, formatDuration, log, } from "@nodeint/migent-kernel";
|
|
4
|
+
import { printResumeCommand } from "../display.js";
|
|
2
5
|
import { cliHooks } from "../hooks.js";
|
|
3
6
|
import { requireCurrentSchema } from "../migrate.js";
|
|
4
|
-
import {
|
|
7
|
+
import { providerMap } from "../provider.js";
|
|
5
8
|
import { text } from "../text.js";
|
|
6
9
|
export async function executeWorkflow(workspace, options) {
|
|
7
10
|
requireCurrentSchema(workspace);
|
|
@@ -9,9 +12,8 @@ export async function executeWorkflow(workspace, options) {
|
|
|
9
12
|
const startPrepared = options.startPrepared === true;
|
|
10
13
|
const sessionId = text(options.sessionId);
|
|
11
14
|
const creating = !startPrepared && (options.action === "start" || (once && sessionId === null));
|
|
12
|
-
const maxAttempts = options.maxAttempts === undefined ? 3 : options.maxAttempts;
|
|
13
15
|
const maxDispatches = options.maxDispatches === undefined ? DEFAULT_MAX_DISPATCHES : options.maxDispatches;
|
|
14
|
-
const runner = options.runner ?? cliHooks.runner ?? new AgentToolRunner(
|
|
16
|
+
const runner = options.runner ?? cliHooks.runner ?? new AgentToolRunner(providerMap());
|
|
15
17
|
runner.itemProgress = options.itemProgress === true;
|
|
16
18
|
const result = await executeNamedWorkflow(workspace, {
|
|
17
19
|
workflowId: creating || startPrepared ? (options.workflowId ?? null) : null,
|
|
@@ -19,37 +21,104 @@ export async function executeWorkflow(workspace, options) {
|
|
|
19
21
|
sessionId: creating ? null : sessionId,
|
|
20
22
|
priorSessionId: options.priorSessionId ?? null,
|
|
21
23
|
startPrepared,
|
|
22
|
-
maxAttempts: once ? null : maxAttempts,
|
|
23
24
|
maxDispatches: once ? null : maxDispatches,
|
|
25
|
+
continueFailed: options.continueFailed === true,
|
|
24
26
|
stageId: options.stageId ?? null,
|
|
25
27
|
once,
|
|
26
28
|
resetRevisions: options.action === "continue",
|
|
27
29
|
refreshWorkflow: options.refreshWorkflow === true,
|
|
28
30
|
reply: options.reply ?? null,
|
|
29
31
|
}, runner);
|
|
30
|
-
printResult(
|
|
31
|
-
|
|
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
|
-
}
|
|
32
|
+
printResult(workspace, result);
|
|
33
|
+
printResumeHint(result);
|
|
37
34
|
return result.code;
|
|
38
35
|
}
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
36
|
+
function printResumeHint(result) {
|
|
37
|
+
if (result.sessionId === "" || result.status === "completed" || result.stopReason === "completed")
|
|
38
|
+
return;
|
|
39
|
+
if (result.stopReason === "configuration")
|
|
40
|
+
return;
|
|
41
|
+
printResumeCommand(result.sessionId);
|
|
42
|
+
}
|
|
43
|
+
const INSPECT_EXIT_CODES = new Set([1, 3, 6, 8]);
|
|
44
|
+
function printResult(workspace, result) {
|
|
45
|
+
if (result.status === "completed" || result.stopReason === "completed") {
|
|
46
|
+
const duration = sessionDurationLabel(workspace, result.sessionId);
|
|
47
|
+
log(duration === null ? "completed" : `completed · ${duration}`);
|
|
48
|
+
for (const filePath of artifactPaths(workspace, result.sessionId, result.outputs)) {
|
|
49
|
+
log(`artifact: ${filePath}`);
|
|
50
|
+
}
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
48
53
|
if (INSPECT_EXIT_CODES.has(result.code) && result.latestExecutionId) {
|
|
49
|
-
|
|
50
|
-
|
|
54
|
+
log(`response: executions/${result.latestExecutionId}/response.md`);
|
|
55
|
+
}
|
|
56
|
+
if (result.code === 7 && result.latestExecutionId) {
|
|
57
|
+
log(`verify: executions/${result.latestExecutionId}/turns/01/verify.md`);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
function artifactPaths(workspace, sessionId, outputs) {
|
|
61
|
+
if (sessionId === "")
|
|
62
|
+
return [];
|
|
63
|
+
const sessionDir = path.resolve(workspace, ".migent", "sessions", sessionId);
|
|
64
|
+
const paths = [];
|
|
65
|
+
for (const [name, value] of Object.entries(outputs)) {
|
|
66
|
+
const record = isRecord(value) ? value : {};
|
|
67
|
+
const relative = typeof record.path === "string" && record.path !== "" ? record.path : path.join("outputs", name);
|
|
68
|
+
paths.push(absoluteFromRoot(path.resolve(sessionDir, relative)));
|
|
69
|
+
}
|
|
70
|
+
return paths;
|
|
71
|
+
}
|
|
72
|
+
function absoluteFromRoot(target) {
|
|
73
|
+
try {
|
|
74
|
+
return realpathSync(target);
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
return path.resolve(target);
|
|
51
78
|
}
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
79
|
+
}
|
|
80
|
+
function sessionDurationLabel(workspace, sessionId) {
|
|
81
|
+
if (sessionId === "")
|
|
82
|
+
return null;
|
|
83
|
+
const executions = path.join(workspace, ".migent", "sessions", sessionId, "executions");
|
|
84
|
+
let names;
|
|
85
|
+
try {
|
|
86
|
+
names = readdirSync(executions);
|
|
87
|
+
}
|
|
88
|
+
catch {
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
91
|
+
let total = 0;
|
|
92
|
+
let counted = false;
|
|
93
|
+
for (const name of names) {
|
|
94
|
+
const elapsed = executionElapsedMs(path.join(executions, name, "execution.json"));
|
|
95
|
+
if (elapsed === null)
|
|
96
|
+
continue;
|
|
97
|
+
total += elapsed;
|
|
98
|
+
counted = true;
|
|
99
|
+
}
|
|
100
|
+
return counted ? formatDuration(total) : null;
|
|
101
|
+
}
|
|
102
|
+
function executionElapsedMs(filePath) {
|
|
103
|
+
try {
|
|
104
|
+
const metadata = JSON.parse(readFileSync(filePath, "utf8"));
|
|
105
|
+
if (!isRecord(metadata))
|
|
106
|
+
return null;
|
|
107
|
+
const started = parsedTime(metadata.created_at);
|
|
108
|
+
if (started === null)
|
|
109
|
+
return null;
|
|
110
|
+
return (parsedTime(metadata.completed_at) ?? Date.now()) - started;
|
|
111
|
+
}
|
|
112
|
+
catch {
|
|
113
|
+
return null;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
function parsedTime(value) {
|
|
117
|
+
if (typeof value !== "string" || value === "")
|
|
118
|
+
return null;
|
|
119
|
+
const parsed = Date.parse(value);
|
|
120
|
+
return Number.isNaN(parsed) ? null : parsed;
|
|
121
|
+
}
|
|
122
|
+
function isRecord(value) {
|
|
123
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
55
124
|
}
|
package/dist/commands/init.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ModelCatalog } from "@nodeint/migent-adapters";
|
|
2
|
-
import { type
|
|
2
|
+
import { type Provider } from "@nodeint/migent-kernel";
|
|
3
3
|
import { type RunCommand, type Which } from "../provider.js";
|
|
4
4
|
export declare const PRESETS: readonly ["implement", "plan"];
|
|
5
5
|
export interface ModelChoice {
|
|
@@ -8,7 +8,7 @@ export interface ModelChoice {
|
|
|
8
8
|
options: Record<string, string>;
|
|
9
9
|
}
|
|
10
10
|
export interface Prompter {
|
|
11
|
-
selectProvider(rows: ReadonlyArray<readonly [string, string | null,
|
|
11
|
+
selectProvider(rows: ReadonlyArray<readonly [string, string | null, Provider]>, message: string): string | Promise<string>;
|
|
12
12
|
selectModel(provider: string, catalog: ModelCatalog, message: string): string | Promise<string>;
|
|
13
13
|
selectOption(name: string, values: readonly string[], allowDefault: boolean): string | Promise<string>;
|
|
14
14
|
confirm(message: string): boolean | Promise<boolean>;
|
|
@@ -21,7 +21,7 @@ export declare function runInit(workspace: string, options?: {
|
|
|
21
21
|
presetExplicit?: boolean;
|
|
22
22
|
skill?: boolean;
|
|
23
23
|
interactive?: boolean;
|
|
24
|
-
|
|
24
|
+
providers?: Record<string, Provider> | null;
|
|
25
25
|
which?: Which | null;
|
|
26
26
|
prompter?: Prompter | null;
|
|
27
27
|
runCommand?: RunCommand | null;
|
package/dist/commands/init.js
CHANGED
|
@@ -8,7 +8,7 @@ import { printAlreadyInitialized, printCreated, printError, printInitConflicts,
|
|
|
8
8
|
import { cliHooks } from "../hooks.js";
|
|
9
9
|
import { runDoctor } from "./doctor.js";
|
|
10
10
|
import { installSkill } from "./skill.js";
|
|
11
|
-
import { acceptedOptions,
|
|
11
|
+
import { acceptedOptions, providerMap, asCatalog, detected, knownModel, loadCatalog, loadOptionValues, parseOptions, requireProvider, runProviderCommand, } from "../provider.js";
|
|
12
12
|
export const PRESETS = ["implement", "plan"];
|
|
13
13
|
export async function runInit(workspace, options = {}) {
|
|
14
14
|
const provider = options.provider ?? null;
|
|
@@ -16,7 +16,7 @@ export async function runInit(workspace, options = {}) {
|
|
|
16
16
|
const preset = options.preset ?? "implement";
|
|
17
17
|
const skill = options.skill === true;
|
|
18
18
|
const interactive = options.interactive ?? false;
|
|
19
|
-
const chosen = options.
|
|
19
|
+
const chosen = options.providers ?? providerMap();
|
|
20
20
|
const resolve = options.which ?? cliHooks.which ?? whichSync;
|
|
21
21
|
const prompt = options.prompter ?? new ClackPrompter();
|
|
22
22
|
const execute = options.runCommand ?? runProviderCommand;
|
|
@@ -94,42 +94,42 @@ export async function runInit(workspace, options = {}) {
|
|
|
94
94
|
}
|
|
95
95
|
return 0;
|
|
96
96
|
}
|
|
97
|
-
function writeDoctorProblems(workspace,
|
|
98
|
-
const problems = diagnose(workspace, {
|
|
97
|
+
function writeDoctorProblems(workspace, providers, which) {
|
|
98
|
+
const problems = diagnose(workspace, { providers, which }).flatMap((check) => check.problems);
|
|
99
99
|
if (problems.length === 0)
|
|
100
100
|
return;
|
|
101
101
|
process.stderr.write("Doctor reported problems:\n");
|
|
102
102
|
for (const problem of problems)
|
|
103
103
|
process.stderr.write(`${problem}\n`);
|
|
104
104
|
}
|
|
105
|
-
async function choices(
|
|
105
|
+
async function choices(providers, which, prompt, args) {
|
|
106
106
|
let provider = args.provider;
|
|
107
107
|
let model = args.model;
|
|
108
108
|
let stored;
|
|
109
109
|
if (provider === null) {
|
|
110
|
-
const rows = detected(
|
|
110
|
+
const rows = detected(providers, which);
|
|
111
111
|
provider = await prompt.selectProvider(rows, "Default provider");
|
|
112
|
-
const
|
|
113
|
-
if (
|
|
112
|
+
const impl = providers[provider];
|
|
113
|
+
if (impl === undefined)
|
|
114
114
|
throw new ConfigurationError(`Unknown provider: ${provider}.`);
|
|
115
|
-
const catalog = await loadCatalog(
|
|
115
|
+
const catalog = await loadCatalog(impl, which, args.runCommand);
|
|
116
116
|
model = await prompt.selectModel(provider, catalog, "Model");
|
|
117
|
-
stored = await promptOptions(
|
|
117
|
+
stored = await promptOptions(impl, model, catalog, which, args.runCommand, prompt);
|
|
118
118
|
}
|
|
119
119
|
else {
|
|
120
|
-
const
|
|
120
|
+
const impl = requireProvider(providers, provider);
|
|
121
121
|
if (model === null)
|
|
122
122
|
throw new ConfigurationError("Model is required.");
|
|
123
|
-
const catalog = await loadCatalog(
|
|
123
|
+
const catalog = await loadCatalog(impl, which, args.runCommand);
|
|
124
124
|
model = knownModel(model, catalog);
|
|
125
|
-
stored = await acceptedOptions(
|
|
125
|
+
stored = await acceptedOptions(impl, model, catalog, which, args.runCommand, args.options);
|
|
126
126
|
}
|
|
127
127
|
if (model.trim() === "")
|
|
128
128
|
throw new ConfigurationError("Model is required.");
|
|
129
129
|
const primary = { provider, model: model.trim(), options: stored };
|
|
130
130
|
return primary;
|
|
131
131
|
}
|
|
132
|
-
function classify(workspace,
|
|
132
|
+
function classify(workspace, providers, which) {
|
|
133
133
|
const root = path.join(workspace, ".migent");
|
|
134
134
|
const config = path.join(root, "config.yaml");
|
|
135
135
|
const workflows = path.join(root, "workflows");
|
|
@@ -154,7 +154,7 @@ function classify(workspace, adapters, which) {
|
|
|
154
154
|
? readdirSync(workflows).filter((name) => name.endsWith(".yaml") && isFile(path.join(workflows, name)))
|
|
155
155
|
: [];
|
|
156
156
|
if (isFile(config) && yamlFiles.length > 0) {
|
|
157
|
-
const checks = diagnose(workspace, {
|
|
157
|
+
const checks = diagnose(workspace, { providers, which });
|
|
158
158
|
const byName = new Map(checks.map((check) => [check.name, check]));
|
|
159
159
|
if (byName.get("config")?.ok && byName.get("workflows")?.ok)
|
|
160
160
|
return { state: "ready", conflicts: [] };
|
|
@@ -236,12 +236,12 @@ function renderWorkflow(preset) {
|
|
|
236
236
|
" from_stage: plan\n" +
|
|
237
237
|
" artifact: plan.md\n");
|
|
238
238
|
}
|
|
239
|
-
async function promptOptions(
|
|
239
|
+
async function promptOptions(provider, model, catalog, which, runCommand, prompt) {
|
|
240
240
|
const stored = {};
|
|
241
|
-
for (const spec of providerOptions(
|
|
241
|
+
for (const spec of providerOptions(provider)) {
|
|
242
242
|
if (!spec.prompt)
|
|
243
243
|
continue;
|
|
244
|
-
const values = await loadOptionValues(
|
|
244
|
+
const values = await loadOptionValues(provider, spec.name, model, catalog, which, runCommand);
|
|
245
245
|
if (values === null || values.length === 0)
|
|
246
246
|
continue;
|
|
247
247
|
const picked = await prompt.selectOption(spec.name, values, spec.allowDefault ?? false);
|
|
@@ -250,8 +250,8 @@ async function promptOptions(adapter, model, catalog, which, runCommand, prompt)
|
|
|
250
250
|
}
|
|
251
251
|
return stored;
|
|
252
252
|
}
|
|
253
|
-
function providerOptions(
|
|
254
|
-
return asCatalog(
|
|
253
|
+
function providerOptions(provider) {
|
|
254
|
+
return asCatalog(provider).providerOptions();
|
|
255
255
|
}
|
|
256
256
|
class ClackPrompter {
|
|
257
257
|
async selectProvider(rows, message) {
|
package/dist/commands/prepare.js
CHANGED
|
@@ -5,6 +5,7 @@ export function prepareWorkflowSession(workspace, sessionId, workflowId, priorSe
|
|
|
5
5
|
const id = createPreparedSession(workspace, sessionId, workflowId, priorSessionId);
|
|
6
6
|
const payload = {
|
|
7
7
|
session_id: id,
|
|
8
|
+
title: sessionId.trim(),
|
|
8
9
|
status: "prepared",
|
|
9
10
|
task_path: relativeTaskPath(id),
|
|
10
11
|
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type Provider } from "@nodeint/migent-kernel";
|
|
2
|
+
import { type ProviderRow } from "../display.js";
|
|
3
|
+
import { type RunCommand, type Which } from "../provider.js";
|
|
4
|
+
export declare function collectProviders(providers: Record<string, Provider>, which: Which, runCommand: RunCommand): Promise<ProviderRow[]>;
|
|
5
|
+
export declare function runProviderList(options?: {
|
|
6
|
+
asJson?: boolean;
|
|
7
|
+
providers?: Record<string, Provider> | null;
|
|
8
|
+
which?: Which | null;
|
|
9
|
+
runCommand?: RunCommand | null;
|
|
10
|
+
}): Promise<number>;
|