@nodeint/migent 0.1.6 → 0.1.8
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 +246 -154
- package/dist/commands/agent.js +6 -6
- package/dist/commands/config.d.ts +3 -3
- package/dist/commands/config.js +66 -59
- package/dist/commands/doctor.js +3 -2
- package/dist/commands/execute.js +2 -2
- package/dist/commands/init.d.ts +3 -3
- package/dist/commands/init.js +21 -21
- 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 +1 -0
- package/dist/display.d.ts +16 -0
- package/dist/display.js +42 -1
- 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 +16 -3
- 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 +13 -6
- package/dist/templates/migent/references/config.md +13 -0
- package/dist/templates/migent/references/schema-config.yaml +13 -1
- package/dist/templates/migent/references/schema-workflow.yaml +1 -1
- package/package.json +4 -4
package/dist/commands/config.js
CHANGED
|
@@ -1,25 +1,30 @@
|
|
|
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, isRoleStage, resolveStageSpec, setRoleOptions, upsertModel, whichSync, } from "@nodeint/migent-kernel";
|
|
4
|
+
import { ConfigEditError, ConfigurationError, LOCAL_CONFIG_FILE, assignRoles, configPath, describeChange, diagnose, hasLocalConfig, inspectConfig, loadModelTarget, loadWorkflowCatalog, loadWorkspaceConfig, loadYamlMapping, matchingModelName, patchConfigText, placeRoleModel, isRoleStage, resolveStageSpec, setRoleOptions, stringifyConfig, 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");
|
|
13
13
|
export function runConfigShow(workspace) {
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
if (!hasLocalConfig(workspace)) {
|
|
15
|
+
process.stdout.write(readFileSync(configPath(workspace), "utf8"));
|
|
16
|
+
return 0;
|
|
17
|
+
}
|
|
18
|
+
process.stdout.write(stringifyConfig(loadWorkspaceConfig(workspace)));
|
|
19
|
+
process.stderr.write(`Merged with .migent/${LOCAL_CONFIG_FILE}.
|
|
20
|
+
`);
|
|
16
21
|
return 0;
|
|
17
22
|
}
|
|
18
23
|
export async function runConfigModel(workspace, options = {}) {
|
|
19
24
|
try {
|
|
20
25
|
return await runModel(workspace, {
|
|
21
26
|
...options,
|
|
22
|
-
|
|
27
|
+
providers: options.providers ?? providerMap(),
|
|
23
28
|
which: options.which ?? cliHooks.which ?? whichSync,
|
|
24
29
|
prompter: options.prompter ?? new ClackConfigPrompter(),
|
|
25
30
|
runCommand: options.runCommand ?? runProviderCommand,
|
|
@@ -39,7 +44,7 @@ export async function runConfigRole(workspace, options = {}) {
|
|
|
39
44
|
try {
|
|
40
45
|
return await runRole(workspace, {
|
|
41
46
|
...options,
|
|
42
|
-
|
|
47
|
+
providers: options.providers ?? providerMap(),
|
|
43
48
|
which: options.which ?? cliHooks.which ?? whichSync,
|
|
44
49
|
prompter: options.prompter ?? new ClackConfigPrompter(),
|
|
45
50
|
runCommand: options.runCommand ?? runProviderCommand,
|
|
@@ -69,7 +74,7 @@ async function runModel(workspace, args) {
|
|
|
69
74
|
const mutating = provider !== null || optionUpdates !== UNSET;
|
|
70
75
|
if (key === null && !mutating) {
|
|
71
76
|
if (interactive) {
|
|
72
|
-
return interactiveModels(workspace, config, workflows, view, args.
|
|
77
|
+
return interactiveModels(workspace, config, workflows, view, args.providers, args.which, args.prompter, args.runCommand, asJson);
|
|
73
78
|
}
|
|
74
79
|
emitView(view, asJson, true, false);
|
|
75
80
|
return 0;
|
|
@@ -78,19 +83,19 @@ async function runModel(workspace, args) {
|
|
|
78
83
|
throw new ConfigEditError("Pass a name.");
|
|
79
84
|
if (!mutating) {
|
|
80
85
|
if (interactive) {
|
|
81
|
-
return editModel(workspace, config, workflows, view, key, args.
|
|
86
|
+
return editModel(workspace, config, workflows, view, key, args.providers, args.which, args.prompter, args.runCommand, asJson);
|
|
82
87
|
}
|
|
83
88
|
emitModels(selectedModels(view, [key]), asJson);
|
|
84
89
|
return 0;
|
|
85
90
|
}
|
|
86
91
|
let catalog = null;
|
|
87
92
|
if (provider !== null && model !== null) {
|
|
88
|
-
[provider, model, catalog] = await catalogModel(args.
|
|
93
|
+
[provider, model, catalog] = await catalogModel(args.providers, args.which, args.runCommand, provider, model);
|
|
89
94
|
}
|
|
90
95
|
if (optionUpdates !== UNSET) {
|
|
91
96
|
const currentProvider = provider ?? modelProvider(config, key);
|
|
92
97
|
const currentModel = model ?? loadModelTarget(config, key).model;
|
|
93
|
-
optionUpdates = await checkedOptions(args.
|
|
98
|
+
optionUpdates = await checkedOptions(args.providers, args.which, args.runCommand, currentProvider, currentModel, optionUpdates, provider !== null ? catalog : null);
|
|
94
99
|
}
|
|
95
100
|
const change = upsertModel(config, key, {
|
|
96
101
|
provider,
|
|
@@ -98,7 +103,7 @@ async function runModel(workspace, args) {
|
|
|
98
103
|
...(optionUpdates === UNSET ? {} : { options: optionUpdates }),
|
|
99
104
|
workflows,
|
|
100
105
|
});
|
|
101
|
-
validateChange(config, change.config, workflows, args.
|
|
106
|
+
validateChange(config, change.config, workflows, args.providers);
|
|
102
107
|
return await commit(workspace, change, { asJson, which: args.which });
|
|
103
108
|
}
|
|
104
109
|
async function runRole(workspace, args) {
|
|
@@ -119,7 +124,7 @@ async function runRole(workspace, args) {
|
|
|
119
124
|
const mutating = modelName !== null || provider !== null || optionUpdates !== UNSET;
|
|
120
125
|
if (roles.length === 0 && !mutating) {
|
|
121
126
|
if (interactive) {
|
|
122
|
-
return interactiveRoles(workspace, config, workflows, view, args.
|
|
127
|
+
return interactiveRoles(workspace, config, workflows, view, args.providers, args.which, args.prompter, args.runCommand, asJson);
|
|
123
128
|
}
|
|
124
129
|
emitView(view, asJson, false, true);
|
|
125
130
|
return 0;
|
|
@@ -132,7 +137,7 @@ async function runRole(workspace, args) {
|
|
|
132
137
|
if (!mutating) {
|
|
133
138
|
if (interactive && roles.length === 1) {
|
|
134
139
|
knownRole(view, roles[0] ?? "");
|
|
135
|
-
return pointRole(workspace, config, workflows, view, roles[0] ?? "", args.
|
|
140
|
+
return pointRole(workspace, config, workflows, view, roles[0] ?? "", args.providers, args.which, args.prompter, args.runCommand, asJson);
|
|
136
141
|
}
|
|
137
142
|
emitRoles(selectedRoles(view, roles), asJson);
|
|
138
143
|
return 0;
|
|
@@ -140,9 +145,9 @@ async function runRole(workspace, args) {
|
|
|
140
145
|
let change;
|
|
141
146
|
if (provider !== null && model !== null) {
|
|
142
147
|
let catalog;
|
|
143
|
-
[provider, model, catalog] = await catalogModel(args.
|
|
148
|
+
[provider, model, catalog] = await catalogModel(args.providers, args.which, args.runCommand, provider, model);
|
|
144
149
|
if (optionUpdates !== UNSET) {
|
|
145
|
-
optionUpdates = await checkedOptions(args.
|
|
150
|
+
optionUpdates = await checkedOptions(args.providers, args.which, args.runCommand, provider, model, optionUpdates, catalog);
|
|
146
151
|
}
|
|
147
152
|
change = placeRoleModel(config, roles[0] ?? "", {
|
|
148
153
|
provider,
|
|
@@ -155,7 +160,7 @@ async function runRole(workspace, args) {
|
|
|
155
160
|
change = assignRoles(config, roles, modelName, { workflows });
|
|
156
161
|
if (optionUpdates !== UNSET) {
|
|
157
162
|
const target = loadModelTarget(change.config, modelName);
|
|
158
|
-
optionUpdates = await checkedOptions(args.
|
|
163
|
+
optionUpdates = await checkedOptions(args.providers, args.which, args.runCommand, target.provider, target.model, optionUpdates);
|
|
159
164
|
const follow = setRoleOptions(change.config, roles[0] ?? "", optionUpdates, { workflows });
|
|
160
165
|
change = describeChange(config, follow.config, workflows);
|
|
161
166
|
}
|
|
@@ -164,40 +169,40 @@ async function runRole(workspace, args) {
|
|
|
164
169
|
const target = roleModel(config, roles[0] ?? "");
|
|
165
170
|
if (optionUpdates === UNSET)
|
|
166
171
|
throw new ConfigEditError("Pass a role.");
|
|
167
|
-
optionUpdates = await checkedOptions(args.
|
|
172
|
+
optionUpdates = await checkedOptions(args.providers, args.which, args.runCommand, target.provider, target.model, optionUpdates);
|
|
168
173
|
change = setRoleOptions(config, roles[0] ?? "", optionUpdates, { workflows });
|
|
169
174
|
}
|
|
170
|
-
validateChange(config, change.config, workflows, args.
|
|
175
|
+
validateChange(config, change.config, workflows, args.providers);
|
|
171
176
|
return await commit(workspace, change, { asJson, which: args.which });
|
|
172
177
|
}
|
|
173
|
-
async function interactiveModels(workspace, config, workflows, view,
|
|
178
|
+
async function interactiveModels(workspace, config, workflows, view, providers, which, prompter, runCommand, asJson) {
|
|
174
179
|
const choices = view.models.map((model) => [modelLabel(model), model.key]);
|
|
175
180
|
choices.push(["New model", NEW]);
|
|
176
181
|
const selected = await prompter.select("Model", choices, view.models[0]?.key ?? NEW);
|
|
177
182
|
if (selected === NEW)
|
|
178
|
-
return addModel(workspace, config, workflows, null,
|
|
179
|
-
return editModel(workspace, config, workflows, view, selected,
|
|
183
|
+
return addModel(workspace, config, workflows, null, providers, which, prompter, runCommand, asJson);
|
|
184
|
+
return editModel(workspace, config, workflows, view, selected, providers, which, prompter, runCommand, asJson);
|
|
180
185
|
}
|
|
181
|
-
async function editModel(workspace, config, workflows, view, key,
|
|
186
|
+
async function editModel(workspace, config, workflows, view, key, providers, which, prompter, runCommand, asJson) {
|
|
182
187
|
const current = selectedModels(view, [key])[0];
|
|
183
188
|
if (current === undefined)
|
|
184
189
|
throw new ConfigEditError(`Unknown name: ${key}.`);
|
|
185
|
-
const provider = await selectProvider(prompter,
|
|
186
|
-
const
|
|
187
|
-
const catalog = await loadCatalog(
|
|
190
|
+
const provider = await selectProvider(prompter, providers, which, "Provider", current.provider);
|
|
191
|
+
const impl = requireProvider(providers, provider);
|
|
192
|
+
const catalog = await loadCatalog(impl, which, runCommand);
|
|
188
193
|
const initial = catalog.ids.includes(current.model) ? current.model : (catalog.defaultId ?? undefined);
|
|
189
194
|
let modelId = knownModel(await prompter.select("Model", catalog.ids.map((item) => [item, item]), initial), catalog);
|
|
190
|
-
const stored = await promptedOptions(prompter,
|
|
195
|
+
const stored = await promptedOptions(prompter, impl, modelId, catalog, which, runCommand, current.options);
|
|
191
196
|
const change = upsertModel(config, key, { provider, model: modelId, options: stored, workflows });
|
|
192
|
-
validateChange(config, change.config, workflows,
|
|
197
|
+
validateChange(config, change.config, workflows, providers);
|
|
193
198
|
return confirmAndCommit(workspace, change, prompter, asJson, which);
|
|
194
199
|
}
|
|
195
|
-
async function addModel(workspace, config, workflows, role,
|
|
196
|
-
const provider = await selectProvider(prompter,
|
|
197
|
-
const
|
|
198
|
-
const catalog = await loadCatalog(
|
|
200
|
+
async function addModel(workspace, config, workflows, role, providers, which, prompter, runCommand, asJson) {
|
|
201
|
+
const provider = await selectProvider(prompter, providers, which, "Provider", null);
|
|
202
|
+
const impl = requireProvider(providers, provider);
|
|
203
|
+
const catalog = await loadCatalog(impl, which, runCommand);
|
|
199
204
|
const modelId = knownModel(await prompter.select("Model", catalog.ids.map((item) => [item, item]), catalog.defaultId), catalog);
|
|
200
|
-
const options = await promptedOptions(prompter,
|
|
205
|
+
const options = await promptedOptions(prompter, impl, modelId, catalog, which, runCommand, {});
|
|
201
206
|
const present = presentOptions(options);
|
|
202
207
|
const existing = matchingModelName(config, { provider, model: modelId, options: present });
|
|
203
208
|
let change;
|
|
@@ -224,10 +229,10 @@ async function addModel(workspace, config, workflows, role, adapters, which, pro
|
|
|
224
229
|
change = describeChange(config, assigned.config, workflows);
|
|
225
230
|
}
|
|
226
231
|
}
|
|
227
|
-
validateChange(config, change.config, workflows,
|
|
232
|
+
validateChange(config, change.config, workflows, providers);
|
|
228
233
|
return confirmAndCommit(workspace, change, prompter, asJson, which);
|
|
229
234
|
}
|
|
230
|
-
async function interactiveRoles(workspace, config, workflows, view,
|
|
235
|
+
async function interactiveRoles(workspace, config, workflows, view, providers, which, prompter, runCommand, asJson) {
|
|
231
236
|
const choices = view.roles.map((role) => [roleLabel(role), role.role]);
|
|
232
237
|
choices.push(["New role", NEW]);
|
|
233
238
|
const selected = await prompter.select("Role", choices, view.roles[0]?.role ?? NEW);
|
|
@@ -237,20 +242,20 @@ async function interactiveRoles(workspace, config, workflows, view, adapters, wh
|
|
|
237
242
|
throw new ConfigEditError("Role name is required.");
|
|
238
243
|
if (view.roles.some((role) => role.role === name))
|
|
239
244
|
throw new ConfigEditError(`Role ${name} already exists.`);
|
|
240
|
-
return pointRole(workspace, config, workflows, view, name,
|
|
245
|
+
return pointRole(workspace, config, workflows, view, name, providers, which, prompter, runCommand, asJson);
|
|
241
246
|
}
|
|
242
|
-
return pointRole(workspace, config, workflows, view, selected,
|
|
247
|
+
return pointRole(workspace, config, workflows, view, selected, providers, which, prompter, runCommand, asJson);
|
|
243
248
|
}
|
|
244
|
-
async function pointRole(workspace, config, workflows, view, role,
|
|
249
|
+
async function pointRole(workspace, config, workflows, view, role, providers, which, prompter, runCommand, asJson) {
|
|
245
250
|
const current = view.roles.find((item) => item.role === role) ?? null;
|
|
246
251
|
const choices = view.models.map((model) => [modelLabel(model), model.key]);
|
|
247
252
|
choices.push(["New model", NEW]);
|
|
248
253
|
const selected = await prompter.select("Model", choices, current?.modelName ?? NEW);
|
|
249
254
|
if (selected === NEW) {
|
|
250
|
-
return addModel(workspace, config, workflows, role,
|
|
255
|
+
return addModel(workspace, config, workflows, role, providers, which, prompter, runCommand, asJson);
|
|
251
256
|
}
|
|
252
257
|
const change = assignRoles(config, [role], selected, { workflows });
|
|
253
|
-
validateChange(config, change.config, workflows,
|
|
258
|
+
validateChange(config, change.config, workflows, providers);
|
|
254
259
|
return confirmAndCommit(workspace, change, prompter, asJson, which);
|
|
255
260
|
}
|
|
256
261
|
async function confirmAndCommit(workspace, change, prompter, asJson, which) {
|
|
@@ -262,7 +267,8 @@ async function confirmAndCommit(workspace, change, prompter, asJson, which) {
|
|
|
262
267
|
return commit(workspace, change, { asJson, which, echo: false });
|
|
263
268
|
}
|
|
264
269
|
async function commit(workspace, change, args) {
|
|
265
|
-
|
|
270
|
+
// Edits patch config.yaml only, so config.local.yaml values never reach the committed file.
|
|
271
|
+
const filePath = configPath(workspace);
|
|
266
272
|
const original = readFileSync(filePath, "utf8");
|
|
267
273
|
const updated = patchConfigText(original, change.config);
|
|
268
274
|
if (updated === original) {
|
|
@@ -274,7 +280,7 @@ async function commit(workspace, change, args) {
|
|
|
274
280
|
}
|
|
275
281
|
writeFileSync(filePath, updated, "utf8");
|
|
276
282
|
if (args.asJson) {
|
|
277
|
-
const checks = diagnose(workspace, {
|
|
283
|
+
const checks = diagnose(workspace, { providers: providerMap(), which: args.which });
|
|
278
284
|
const doctor = doctorPayload(checks);
|
|
279
285
|
process.stdout.write(`${JSON.stringify(changePayload(change, true, doctor))}\n`);
|
|
280
286
|
return doctor.ok ? 0 : 1;
|
|
@@ -283,7 +289,7 @@ async function commit(workspace, change, args) {
|
|
|
283
289
|
printChange(change);
|
|
284
290
|
return runDoctor(workspace, { which: args.which });
|
|
285
291
|
}
|
|
286
|
-
function validateChange(before, after, workflows,
|
|
292
|
+
function validateChange(before, after, workflows, providers) {
|
|
287
293
|
const beforeModels = isRecord(before.models) ? before.models : {};
|
|
288
294
|
const afterModels = isRecord(after.models) ? after.models : {};
|
|
289
295
|
const changed = new Set();
|
|
@@ -294,7 +300,7 @@ function validateChange(before, after, workflows, adapters) {
|
|
|
294
300
|
}
|
|
295
301
|
for (const key of changed) {
|
|
296
302
|
const target = loadModelTarget(after, key);
|
|
297
|
-
|
|
303
|
+
requireProvider(providers, target.provider).validateOptions(target.options);
|
|
298
304
|
}
|
|
299
305
|
const beforeRoles = isRecord(before.roles) ? before.roles : {};
|
|
300
306
|
const afterRoles = isRecord(after.roles) ? after.roles : {};
|
|
@@ -312,7 +318,7 @@ function validateChange(before, after, workflows, adapters) {
|
|
|
312
318
|
options[optionName] = value.trim();
|
|
313
319
|
}
|
|
314
320
|
}
|
|
315
|
-
|
|
321
|
+
requireProvider(providers, target.provider).validateOptions(options);
|
|
316
322
|
}
|
|
317
323
|
for (const document of workflows) {
|
|
318
324
|
for (const stage of Object.values(document.stages)) {
|
|
@@ -331,35 +337,35 @@ function validateChange(before, after, workflows, adapters) {
|
|
|
331
337
|
}
|
|
332
338
|
if (sameTarget(beforeTarget, afterTarget))
|
|
333
339
|
continue;
|
|
334
|
-
|
|
340
|
+
requireProvider(providers, afterTarget.provider).validateOptions(afterTarget.options);
|
|
335
341
|
}
|
|
336
342
|
}
|
|
337
343
|
}
|
|
338
|
-
async function catalogModel(
|
|
339
|
-
const
|
|
340
|
-
const catalog = await loadCatalog(
|
|
344
|
+
async function catalogModel(providers, which, runCommand, provider, model) {
|
|
345
|
+
const impl = requireProvider(providers, provider);
|
|
346
|
+
const catalog = await loadCatalog(impl, which, runCommand);
|
|
341
347
|
return [provider, knownModel(model, catalog), catalog];
|
|
342
348
|
}
|
|
343
|
-
async function checkedOptions(
|
|
344
|
-
const
|
|
345
|
-
const resolved = catalog ?? (await loadCatalog(
|
|
349
|
+
async function checkedOptions(providers, which, runCommand, provider, model, updates, catalog = null) {
|
|
350
|
+
const impl = requireProvider(providers, provider);
|
|
351
|
+
const resolved = catalog ?? (await loadCatalog(impl, which, runCommand));
|
|
346
352
|
const present = {};
|
|
347
353
|
for (const [key, value] of Object.entries(updates)) {
|
|
348
354
|
if (value !== null)
|
|
349
355
|
present[key] = value;
|
|
350
356
|
}
|
|
351
357
|
if (Object.keys(present).length > 0) {
|
|
352
|
-
await acceptedOptions(
|
|
358
|
+
await acceptedOptions(impl, model, resolved, which, runCommand, present);
|
|
353
359
|
}
|
|
354
360
|
return { ...updates };
|
|
355
361
|
}
|
|
356
|
-
async function promptedOptions(prompter,
|
|
362
|
+
async function promptedOptions(prompter, provider, model, catalog, which, runCommand, current) {
|
|
357
363
|
const stored = {};
|
|
358
|
-
const
|
|
359
|
-
for (const spec of
|
|
364
|
+
const catalogProvider = asCatalog(provider);
|
|
365
|
+
for (const spec of catalogProvider.providerOptions()) {
|
|
360
366
|
if (!spec.prompt)
|
|
361
367
|
continue;
|
|
362
|
-
const values = await loadOptionValues(
|
|
368
|
+
const values = await loadOptionValues(provider, spec.name, model, catalog, which, runCommand);
|
|
363
369
|
if (values === null || values.length === 0)
|
|
364
370
|
continue;
|
|
365
371
|
const choices = values.map((value) => [value, value]);
|
|
@@ -386,8 +392,8 @@ function optionUpdatesOf(options, clearOptions) {
|
|
|
386
392
|
}
|
|
387
393
|
return updates;
|
|
388
394
|
}
|
|
389
|
-
async function selectProvider(prompter,
|
|
390
|
-
const rows = detected(
|
|
395
|
+
async function selectProvider(prompter, providers, which, message, defaultValue) {
|
|
396
|
+
const rows = detected(providers, which);
|
|
391
397
|
if (rows.length === 0)
|
|
392
398
|
throw new ConfigurationError("No providers are available.");
|
|
393
399
|
const names = new Set(rows.map(([name]) => name));
|
|
@@ -395,7 +401,8 @@ async function selectProvider(prompter, adapters, which, message, defaultValue)
|
|
|
395
401
|
}
|
|
396
402
|
function loaded(workspace) {
|
|
397
403
|
requireCurrentSchema(workspace);
|
|
398
|
-
|
|
404
|
+
// Base file only: this snapshot feeds validation and patching of config.yaml.
|
|
405
|
+
const config = loadYamlMapping(configPath(workspace));
|
|
399
406
|
const workflows = Object.values(loadWorkflowCatalog(workspace).documents);
|
|
400
407
|
return [config, workflows, inspectConfig(config, workflows)];
|
|
401
408
|
}
|
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
|
|
@@ -21,6 +21,7 @@ function payload(checks) {
|
|
|
21
21
|
ok: check.ok,
|
|
22
22
|
items: [...check.items],
|
|
23
23
|
problems: [...check.problems],
|
|
24
|
+
warnings: [...check.warnings],
|
|
24
25
|
})),
|
|
25
26
|
};
|
|
26
27
|
}
|
package/dist/commands/execute.js
CHANGED
|
@@ -4,7 +4,7 @@ import { AgentToolRunner, DEFAULT_MAX_DISPATCHES, executeNamedWorkflow, formatDu
|
|
|
4
4
|
import { printResumeCommand } from "../display.js";
|
|
5
5
|
import { cliHooks } from "../hooks.js";
|
|
6
6
|
import { requireCurrentSchema } from "../migrate.js";
|
|
7
|
-
import {
|
|
7
|
+
import { providerMap } from "../provider.js";
|
|
8
8
|
import { text } from "../text.js";
|
|
9
9
|
export async function executeWorkflow(workspace, options) {
|
|
10
10
|
requireCurrentSchema(workspace);
|
|
@@ -13,7 +13,7 @@ export async function executeWorkflow(workspace, options) {
|
|
|
13
13
|
const sessionId = text(options.sessionId);
|
|
14
14
|
const creating = !startPrepared && (options.action === "start" || (once && sessionId === null));
|
|
15
15
|
const maxDispatches = options.maxDispatches === undefined ? DEFAULT_MAX_DISPATCHES : options.maxDispatches;
|
|
16
|
-
const runner = options.runner ?? cliHooks.runner ?? new AgentToolRunner(
|
|
16
|
+
const runner = options.runner ?? cliHooks.runner ?? new AgentToolRunner(providerMap());
|
|
17
17
|
runner.itemProgress = options.itemProgress === true;
|
|
18
18
|
const result = await executeNamedWorkflow(workspace, {
|
|
19
19
|
workflowId: creating || startPrepared ? (options.workflowId ?? null) : null,
|
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: [] };
|
|
@@ -168,7 +168,7 @@ function writeProject(workspace, preset, primary) {
|
|
|
168
168
|
writeFileSync(path.join(root, "config.yaml"), renderConfig(preset, primary), "utf8");
|
|
169
169
|
const workflowName = `${preset}.yaml`;
|
|
170
170
|
writeFileSync(path.join(workflows, workflowName), renderWorkflow(preset), "utf8");
|
|
171
|
-
writeFileSync(path.join(root, ".gitignore"), "sessions/\n", "utf8");
|
|
171
|
+
writeFileSync(path.join(root, ".gitignore"), "sessions/\nconfig.local.yaml\n", "utf8");
|
|
172
172
|
return [".migent/config.yaml", `.migent/workflows/${workflowName}`, ".migent/.gitignore"];
|
|
173
173
|
}
|
|
174
174
|
function renderConfig(preset, primary) {
|
|
@@ -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>;
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { whichSync } from "@nodeint/migent-kernel";
|
|
2
|
+
import { printProviders } from "../display.js";
|
|
3
|
+
import { cliHooks } from "../hooks.js";
|
|
4
|
+
import { providerMap, loadCatalog, runProviderCommand, } from "../provider.js";
|
|
5
|
+
function mapProviderOption(opt) {
|
|
6
|
+
return {
|
|
7
|
+
name: opt.name,
|
|
8
|
+
prompt: opt.prompt === true,
|
|
9
|
+
allow_default: opt.allowDefault === true,
|
|
10
|
+
overridable: opt.overridable === true,
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
function getProviderOptions(provider) {
|
|
14
|
+
const candidate = provider;
|
|
15
|
+
if (typeof candidate.providerOptions === "function") {
|
|
16
|
+
return candidate.providerOptions().map(mapProviderOption);
|
|
17
|
+
}
|
|
18
|
+
return [];
|
|
19
|
+
}
|
|
20
|
+
export async function collectProviders(providers, which, runCommand) {
|
|
21
|
+
// A provider without a default client has no CLI of its own to probe.
|
|
22
|
+
const names = Object.keys(providers).filter((name) => providers[name].requiresClient !== true).sort();
|
|
23
|
+
const rows = [];
|
|
24
|
+
for (const name of names) {
|
|
25
|
+
const provider = providers[name];
|
|
26
|
+
const path = which(provider.command);
|
|
27
|
+
const options = getProviderOptions(provider);
|
|
28
|
+
const problems = [];
|
|
29
|
+
let models = [];
|
|
30
|
+
let default_model = null;
|
|
31
|
+
if (path === null) {
|
|
32
|
+
problems.push(`${provider.command} is not on PATH.`);
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
try {
|
|
36
|
+
const catalog = await loadCatalog(provider, which, runCommand);
|
|
37
|
+
models = [...catalog.ids];
|
|
38
|
+
default_model = catalog.defaultId;
|
|
39
|
+
}
|
|
40
|
+
catch (error) {
|
|
41
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
42
|
+
problems.push(message);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
rows.push({
|
|
46
|
+
name,
|
|
47
|
+
command: provider.command,
|
|
48
|
+
path,
|
|
49
|
+
available: path !== null,
|
|
50
|
+
models,
|
|
51
|
+
default_model,
|
|
52
|
+
options,
|
|
53
|
+
problems,
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
return rows;
|
|
57
|
+
}
|
|
58
|
+
function providerPayload(rows) {
|
|
59
|
+
return {
|
|
60
|
+
ok: rows.every((row) => row.problems.length === 0),
|
|
61
|
+
providers: rows.map((row) => ({ ...row })),
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
export async function runProviderList(options = {}) {
|
|
65
|
+
const providers = options.providers ?? providerMap();
|
|
66
|
+
const which = options.which ?? cliHooks.which ?? whichSync;
|
|
67
|
+
const runCommand = options.runCommand ?? cliHooks.runCommand ?? runProviderCommand;
|
|
68
|
+
const rows = await collectProviders(providers, which, runCommand);
|
|
69
|
+
if (options.asJson) {
|
|
70
|
+
process.stdout.write(`${JSON.stringify(providerPayload(rows))}\n`);
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
printProviders(rows);
|
|
74
|
+
}
|
|
75
|
+
const hasAnyModels = rows.some((row) => row.models.length > 0);
|
|
76
|
+
return hasAnyModels ? 0 : 1;
|
|
77
|
+
}
|
package/dist/display.d.ts
CHANGED
|
@@ -11,3 +11,19 @@ export declare function printAgent(result: Record<string, unknown>, role: string
|
|
|
11
11
|
export declare function printWatchSnapshot(snapshot: Record<string, unknown>): void;
|
|
12
12
|
export declare function printWatchEvent(event: Record<string, unknown>): void;
|
|
13
13
|
export declare function formatWatchTimestamp(timestamp: unknown): string;
|
|
14
|
+
export interface ProviderRow {
|
|
15
|
+
name: string;
|
|
16
|
+
command: string;
|
|
17
|
+
path: string | null;
|
|
18
|
+
available: boolean;
|
|
19
|
+
models: string[];
|
|
20
|
+
default_model: string | null;
|
|
21
|
+
options: Array<{
|
|
22
|
+
name: string;
|
|
23
|
+
prompt: boolean;
|
|
24
|
+
allow_default: boolean;
|
|
25
|
+
overridable: boolean;
|
|
26
|
+
}>;
|
|
27
|
+
problems: string[];
|
|
28
|
+
}
|
|
29
|
+
export declare function printProviders(rows: readonly ProviderRow[]): void;
|