@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/display.js
CHANGED
|
@@ -41,6 +41,8 @@ export function printDoctor(checks) {
|
|
|
41
41
|
}
|
|
42
42
|
for (const problem of check.problems)
|
|
43
43
|
process.stdout.write(` ${problem}\n`);
|
|
44
|
+
for (const warning of check.warnings)
|
|
45
|
+
process.stdout.write(` warning: ${warning}\n`);
|
|
44
46
|
}
|
|
45
47
|
}
|
|
46
48
|
export function printSessions(rows) {
|
|
@@ -50,7 +52,8 @@ export function printSessions(rows) {
|
|
|
50
52
|
}
|
|
51
53
|
for (const row of rows) {
|
|
52
54
|
const fields = sessionFields(row);
|
|
53
|
-
|
|
55
|
+
const identity = fields.title !== "" && fields.title !== fields.session ? `${fields.title} ${fields.session}` : fields.session;
|
|
56
|
+
process.stdout.write(`${identity} ${fields.workflow} ${fields.status} ${fields.execution} ${fields.decision} ${fields.outputs} ${fields.task}\n`);
|
|
54
57
|
}
|
|
55
58
|
}
|
|
56
59
|
export function printAgent(result, role, provider, model) {
|
|
@@ -73,6 +76,10 @@ export function printWatchSnapshot(snapshot) {
|
|
|
73
76
|
["session_id", String(snapshot.session_id ?? "--")],
|
|
74
77
|
["status", String(snapshot.status ?? "unknown")],
|
|
75
78
|
];
|
|
79
|
+
const title = snapshot.title;
|
|
80
|
+
if (typeof title === "string" && title !== "" && title !== lines[0]?.[1]) {
|
|
81
|
+
lines.splice(1, 0, ["title", title]);
|
|
82
|
+
}
|
|
76
83
|
if (typeof snapshot.workflow_id === "string" && snapshot.workflow_id) {
|
|
77
84
|
lines.push(["workflow", snapshot.workflow_id]);
|
|
78
85
|
}
|
|
@@ -133,6 +140,40 @@ export function formatWatchTimestamp(timestamp) {
|
|
|
133
140
|
return timestamp.trim();
|
|
134
141
|
return parsed.toISOString().replace("T", " ").replace(/\.\d+Z$/, "+00:00").replace("Z", "+00:00");
|
|
135
142
|
}
|
|
143
|
+
export function printProviders(rows) {
|
|
144
|
+
const tty = Boolean(process.stdout.isTTY);
|
|
145
|
+
for (const row of rows) {
|
|
146
|
+
const ok = row.problems.length === 0 && row.models.length > 0;
|
|
147
|
+
const status = ok ? (tty ? pc.green("ok") : "ok") : tty ? pc.red("failed") : "failed";
|
|
148
|
+
process.stdout.write(`${row.name.padEnd(12)}${status}\n`);
|
|
149
|
+
if (row.path !== null) {
|
|
150
|
+
const line = ` path: ${row.path}`;
|
|
151
|
+
process.stdout.write(`${tty ? pc.dim(line) : line}\n`);
|
|
152
|
+
}
|
|
153
|
+
else {
|
|
154
|
+
const line = ` path: not found`;
|
|
155
|
+
process.stdout.write(`${tty ? pc.dim(line) : line}\n`);
|
|
156
|
+
}
|
|
157
|
+
if (row.models.length > 0) {
|
|
158
|
+
process.stdout.write(` models:\n`);
|
|
159
|
+
for (const model of row.models) {
|
|
160
|
+
const marked = model === row.default_model ? `${model} (default)` : model;
|
|
161
|
+
const line = ` ${marked}`;
|
|
162
|
+
process.stdout.write(`${tty ? pc.dim(line) : line}\n`);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
if (row.options.length > 0) {
|
|
166
|
+
process.stdout.write(` options:\n`);
|
|
167
|
+
for (const opt of row.options) {
|
|
168
|
+
const line = ` ${opt.name}`;
|
|
169
|
+
process.stdout.write(`${tty ? pc.dim(line) : line}\n`);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
for (const problem of row.problems) {
|
|
173
|
+
process.stdout.write(` ${problem}\n`);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
136
177
|
function isRecord(value) {
|
|
137
178
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
138
179
|
}
|
package/dist/hooks.d.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import type { AgentToolRunner } from "@nodeint/migent-kernel";
|
|
2
|
+
import type { RunCommand } from "./provider.js";
|
|
2
3
|
/** Test seams. Production leaves both null. */
|
|
3
4
|
export declare const cliHooks: {
|
|
4
5
|
which: ((command: string) => string | null) | null;
|
|
5
6
|
runner: AgentToolRunner | null;
|
|
7
|
+
runCommand: RunCommand | null;
|
|
6
8
|
};
|
package/dist/hooks.js
CHANGED
package/dist/index.js
CHANGED
package/dist/migrate.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { type
|
|
1
|
+
import { type MigrationProvider } from "@nodeint/migent-kernel";
|
|
2
2
|
export declare function requireCurrentSchema(workspace: string): void;
|
|
3
3
|
export declare function runMigrate(workspace: string, options?: {
|
|
4
4
|
assumeYes?: boolean;
|
|
5
5
|
confirm?: ((message: string) => boolean | Promise<boolean>) | null;
|
|
6
|
-
|
|
6
|
+
providers?: Record<string, MigrationProvider> | null;
|
|
7
7
|
}): Promise<number>;
|
|
8
|
-
export declare function
|
|
8
|
+
export declare function migrationProviders(providers?: Record<string, unknown>): Record<string, MigrationProvider>;
|
package/dist/migrate.js
CHANGED
|
@@ -4,29 +4,31 @@ import { confirm as clackConfirm, isCancel } from "@clack/prompts";
|
|
|
4
4
|
import { SCHEMA_VERSION, documentVersion, loadYamlMapping, migrateConfig, migrateWorkflow, patchConfigText, patchWorkflowText, workflowsDirectory, ConfigurationError, } from "@nodeint/migent-kernel";
|
|
5
5
|
import { printError, printNotice } from "./display.js";
|
|
6
6
|
import { isDirectory, isFile } from "./fs-utils.js";
|
|
7
|
-
import {
|
|
8
|
-
|
|
7
|
+
import { providerMap } from "./provider.js";
|
|
8
|
+
function versionList(entries) {
|
|
9
|
+
const width = Math.max(...entries.map(([label]) => label.length));
|
|
10
|
+
return entries.map(([label, version]) => ` ${label.padEnd(width)} v${version}`).join("\n");
|
|
11
|
+
}
|
|
9
12
|
export function requireCurrentSchema(workspace) {
|
|
10
13
|
const newer = [];
|
|
11
|
-
|
|
14
|
+
const older = [];
|
|
12
15
|
for (const [label, filePath] of schemaFiles(workspace)) {
|
|
13
16
|
const version = storedVersion(filePath);
|
|
14
17
|
if (version === null || version === SCHEMA_VERSION)
|
|
15
18
|
continue;
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
}
|
|
20
|
-
|
|
19
|
+
(version > SCHEMA_VERSION ? newer : older).push([label, version]);
|
|
20
|
+
}
|
|
21
|
+
if (older.length > 0) {
|
|
22
|
+
throw new ConfigurationError(`Schema is outdated (this Migent uses v${SCHEMA_VERSION}).\n${versionList(older)}\n\n` +
|
|
23
|
+
"Run `migent migrate` to update.");
|
|
24
|
+
}
|
|
25
|
+
if (newer.length > 0) {
|
|
26
|
+
throw new ConfigurationError(`Schema is newer than this Migent supports (v${SCHEMA_VERSION}).\n${versionList(newer)}`);
|
|
21
27
|
}
|
|
22
|
-
if (outdated)
|
|
23
|
-
throw new ConfigurationError(OUTDATED);
|
|
24
|
-
if (newer.length > 0)
|
|
25
|
-
throw new ConfigurationError(newer.join(" "));
|
|
26
28
|
}
|
|
27
29
|
export async function runMigrate(workspace, options = {}) {
|
|
28
|
-
const
|
|
29
|
-
const rewrites = pendingRewrites(workspace,
|
|
30
|
+
const providers = options.providers ?? migrationProviders();
|
|
31
|
+
const rewrites = pendingRewrites(workspace, providers);
|
|
30
32
|
if (rewrites.length === 0) {
|
|
31
33
|
process.stdout.write("Schema is current.\n");
|
|
32
34
|
return 0;
|
|
@@ -43,10 +45,10 @@ export async function runMigrate(workspace, options = {}) {
|
|
|
43
45
|
process.stdout.write(`Upgraded schema to version ${SCHEMA_VERSION}.\n`);
|
|
44
46
|
return 0;
|
|
45
47
|
}
|
|
46
|
-
export function
|
|
48
|
+
export function migrationProviders(providers = providerMap()) {
|
|
47
49
|
const mapped = {};
|
|
48
|
-
for (const [name,
|
|
49
|
-
const candidate =
|
|
50
|
+
for (const [name, provider] of Object.entries(providers)) {
|
|
51
|
+
const candidate = provider;
|
|
50
52
|
mapped[name] = {
|
|
51
53
|
modelMigrations: candidate.modelMigrations ?? {},
|
|
52
54
|
roleMigrations: candidate.roleMigrations ?? {},
|
|
@@ -74,17 +76,17 @@ async function accepted(confirm) {
|
|
|
74
76
|
}
|
|
75
77
|
return true;
|
|
76
78
|
}
|
|
77
|
-
function pendingRewrites(workspace,
|
|
79
|
+
function pendingRewrites(workspace, providers) {
|
|
78
80
|
const configPath = path.join(workspace, ".migent", "config.yaml");
|
|
79
81
|
if (!isFile(configPath))
|
|
80
82
|
return [];
|
|
81
|
-
const [config, configRewrite] = configRewriteOf(configPath,
|
|
83
|
+
const [config, configRewrite] = configRewriteOf(configPath, providers);
|
|
82
84
|
if (config === null)
|
|
83
85
|
return [];
|
|
84
86
|
const rewrites = [];
|
|
85
87
|
if (configRewrite !== null)
|
|
86
88
|
rewrites.push(configRewrite);
|
|
87
|
-
rewrites.push(...workflowRewrites(workspace, config,
|
|
89
|
+
rewrites.push(...workflowRewrites(workspace, config, providers));
|
|
88
90
|
return rewrites;
|
|
89
91
|
}
|
|
90
92
|
function schemaFiles(workspace) {
|
|
@@ -109,7 +111,7 @@ function storedVersion(filePath) {
|
|
|
109
111
|
return null;
|
|
110
112
|
}
|
|
111
113
|
}
|
|
112
|
-
function configRewriteOf(filePath,
|
|
114
|
+
function configRewriteOf(filePath, providers) {
|
|
113
115
|
const original = readFileSync(filePath, "utf8");
|
|
114
116
|
let parsed;
|
|
115
117
|
try {
|
|
@@ -121,7 +123,7 @@ function configRewriteOf(filePath, adapters) {
|
|
|
121
123
|
const updated = structuredClone(parsed);
|
|
122
124
|
let notes;
|
|
123
125
|
try {
|
|
124
|
-
notes = migrateConfig(updated,
|
|
126
|
+
notes = migrateConfig(updated, providers);
|
|
125
127
|
}
|
|
126
128
|
catch (error) {
|
|
127
129
|
if (error instanceof ConfigurationError)
|
|
@@ -136,7 +138,7 @@ function configRewriteOf(filePath, adapters) {
|
|
|
136
138
|
const labeled = notes.map((note) => note.startsWith("schema_version") ? `Migrated .migent/config.yaml ${note}` : note);
|
|
137
139
|
return [updated, { path: filePath, text: rewritten, notes: labeled }];
|
|
138
140
|
}
|
|
139
|
-
function workflowRewrites(workspace, config,
|
|
141
|
+
function workflowRewrites(workspace, config, providers) {
|
|
140
142
|
const directory = workflowsDirectory(workspace);
|
|
141
143
|
if (!isDirectory(directory))
|
|
142
144
|
return [];
|
|
@@ -156,7 +158,7 @@ function workflowRewrites(workspace, config, adapters) {
|
|
|
156
158
|
const updated = structuredClone(parsed);
|
|
157
159
|
let changed;
|
|
158
160
|
try {
|
|
159
|
-
changed = migrateWorkflow(updated, config,
|
|
161
|
+
changed = migrateWorkflow(updated, config, providers);
|
|
160
162
|
}
|
|
161
163
|
catch (error) {
|
|
162
164
|
if (error instanceof ConfigurationError)
|
package/dist/program.js
CHANGED
|
@@ -11,6 +11,7 @@ import { runInit } from "./commands/init.js";
|
|
|
11
11
|
import { runSessionMetrics, runSessions } from "./commands/sessions.js";
|
|
12
12
|
import { parsePort, runServiceStart, runServiceStatus, runServiceStop } from "./commands/service.js";
|
|
13
13
|
import { installSkill, updateSkill } from "./commands/skill.js";
|
|
14
|
+
import { runProviderList } from "./commands/provider.js";
|
|
14
15
|
import { runRegister, runRemove, runWorkspaceList } from "./commands/workspace-registry.js";
|
|
15
16
|
import { printError } from "./display.js";
|
|
16
17
|
import { LOOP_EXITS, STAGE_EXITS } from "./exits.js";
|
|
@@ -127,6 +128,18 @@ export function createProgram(status) {
|
|
|
127
128
|
.option("--json", "Print the checks as one JSON object.")
|
|
128
129
|
.addHelpText("after", exitText(["Exit 0 when every check passes.", "Exit 1 when a check fails."]))
|
|
129
130
|
.action((options) => settle(status, () => runDoctor(findWorkspace(invocationCwdPath()), { asJson: options.json === true })));
|
|
131
|
+
const provider = program.command("provider").description("List registered provider providers and their models.");
|
|
132
|
+
provider
|
|
133
|
+
.command("list")
|
|
134
|
+
.description("List each registered provider, whether its CLI is on PATH, and the models it reports.")
|
|
135
|
+
.option("--json", "Print the provider list as one JSON object.")
|
|
136
|
+
.addHelpText("after", exitText([
|
|
137
|
+
"Exit 0 when the command ran and at least one provider returned models.",
|
|
138
|
+
"Exit 1 when no provider returned any models.",
|
|
139
|
+
"A missing optional CLI is not an error.",
|
|
140
|
+
]))
|
|
141
|
+
.action((options) => settle(status, () => runProviderList({ asJson: options.json === true })));
|
|
142
|
+
showHelpWhenBare(status, provider);
|
|
130
143
|
program
|
|
131
144
|
.command("migrate")
|
|
132
145
|
.description("Upgrade config and workflows to the current schema version.")
|
|
@@ -220,15 +233,15 @@ function registerSessionCommand(program, status) {
|
|
|
220
233
|
.option("--file <file>", "Read the role prompt from this file.")
|
|
221
234
|
.option("--stdin", "Read the role prompt from stdin.")
|
|
222
235
|
.option("--json", "Print the role result as one JSON object.")
|
|
223
|
-
.addHelpText("after", `With --workflow:\n${exitText(LOOP_EXITS)}With --workflow and --once:\n${exitText(STAGE_EXITS)}Without --workflow:\n${exitText(ROLE_EXITS)}Examples:\n migent session run --workflow plan --task "<one line>"\n migent session run --session
|
|
236
|
+
.addHelpText("after", `With --workflow:\n${exitText(LOOP_EXITS)}With --workflow and --once:\n${exitText(STAGE_EXITS)}Without --workflow:\n${exitText(ROLE_EXITS)}Examples:\n migent session run --workflow plan --task "<one line>"\n migent session run --session <session> --workflow plan\n migent session run --role reviewer --prompt "<prompt>"\n`)
|
|
224
237
|
.action((options) => settle(status, () => runSessionCommand(options)));
|
|
225
238
|
session
|
|
226
239
|
.command("prepare")
|
|
227
240
|
.description("Create a prepared session and exit. Write task.md, then session run --session <id> --workflow <workflow>.")
|
|
228
|
-
.argument("<
|
|
241
|
+
.argument("<title>", "Session title shown in the list, such as \"some task title\". The session id is a UUID.")
|
|
229
242
|
.requiredOption("--workflow <workflow>", "Workflow id, or a path to a workflow file.")
|
|
230
243
|
.option("--prior <session>", PRIOR_HELP)
|
|
231
|
-
.addHelpText("after", `${exitText(["Exit 0 when the session is prepared.", "Exit 2 on a configuration error."])}Examples:\n migent session prepare
|
|
244
|
+
.addHelpText("after", `${exitText(["Exit 0 when the session is prepared.", "Exit 2 on a configuration error."])}Examples:\n migent session prepare "some task title" --workflow plan\n`)
|
|
232
245
|
.action((id, options) => settle(status, () => prepareWorkflowSession(findWorkspace(invocationCwdPath()), id, String(options.workflow), optionalText(options.prior))));
|
|
233
246
|
const continueCommand = session
|
|
234
247
|
.command("continue")
|
package/dist/provider.d.ts
CHANGED
|
@@ -1,23 +1,25 @@
|
|
|
1
1
|
import { ModelCatalog } from "@nodeint/migent-adapters";
|
|
2
|
-
import { type
|
|
3
|
-
export interface
|
|
2
|
+
import { type Provider, type ProviderOption } from "@nodeint/migent-kernel";
|
|
3
|
+
export interface CatalogProvider extends Provider {
|
|
4
4
|
modelCatalogCommand(): string[];
|
|
5
|
+
/** Written to the catalog command's stdin when the CLI reads its request there. */
|
|
6
|
+
modelCatalogInput?(): string;
|
|
5
7
|
parseModelCatalog(stdout: string): ModelCatalog;
|
|
6
8
|
optionValuesCommand(name: string, model: string): string[] | null;
|
|
7
9
|
parseOptionValues(name: string, text: string, model?: string): readonly string[];
|
|
8
10
|
providerOptions(): readonly ProviderOption[];
|
|
9
11
|
}
|
|
10
12
|
export type Which = (command: string) => string | null;
|
|
11
|
-
export type RunCommand = (argv: string[]) => string | Promise<string>;
|
|
13
|
+
export type RunCommand = (argv: string[], input?: string) => string | Promise<string>;
|
|
12
14
|
export declare class ProviderCommandFailed extends Error {
|
|
13
15
|
}
|
|
14
|
-
export declare function
|
|
15
|
-
export declare function asCatalog(
|
|
16
|
-
export declare function
|
|
17
|
-
export declare function detected(
|
|
18
|
-
export declare function loadCatalog(
|
|
16
|
+
export declare function providerMap(): Record<string, Provider>;
|
|
17
|
+
export declare function asCatalog(provider: Provider): CatalogProvider;
|
|
18
|
+
export declare function requireProvider(providers: Record<string, Provider>, provider: string): Provider;
|
|
19
|
+
export declare function detected(providers: Record<string, Provider>, which: Which): Array<[string, string | null, Provider]>;
|
|
20
|
+
export declare function loadCatalog(provider: Provider, which: Which, runCommand: RunCommand): Promise<ModelCatalog>;
|
|
19
21
|
export declare function knownModel(model: string, catalog: ModelCatalog): string;
|
|
20
22
|
export declare function parseOptions(pairs: readonly string[]): Record<string, string>;
|
|
21
|
-
export declare function loadOptionValues(
|
|
22
|
-
export declare function acceptedOptions(
|
|
23
|
-
export declare function runProviderCommand(argv: string[]): string;
|
|
23
|
+
export declare function loadOptionValues(provider: Provider, name: string, model: string, catalog: ModelCatalog, which: Which, runCommand: RunCommand): Promise<readonly string[] | null>;
|
|
24
|
+
export declare function acceptedOptions(provider: Provider, model: string, catalog: ModelCatalog, which: Which, runCommand: RunCommand, options: Readonly<Record<string, string>>): Promise<Record<string, string>>;
|
|
25
|
+
export declare function runProviderCommand(argv: string[], input?: string): string;
|
package/dist/provider.js
CHANGED
|
@@ -1,27 +1,27 @@
|
|
|
1
|
-
import { ModelCatalog,
|
|
1
|
+
import { ModelCatalog, defaultProviders } from "@nodeint/migent-adapters";
|
|
2
2
|
import { spawnProviderSync } from "@nodeint/migent-kernel";
|
|
3
3
|
export class ProviderCommandFailed extends Error {
|
|
4
4
|
}
|
|
5
|
-
export function
|
|
6
|
-
return
|
|
5
|
+
export function providerMap() {
|
|
6
|
+
return defaultProviders();
|
|
7
7
|
}
|
|
8
|
-
export function asCatalog(
|
|
9
|
-
const candidate =
|
|
8
|
+
export function asCatalog(provider) {
|
|
9
|
+
const candidate = provider;
|
|
10
10
|
if (typeof candidate.modelCatalogCommand !== "function" || typeof candidate.parseModelCatalog !== "function") {
|
|
11
|
-
throw new ProviderCommandFailed(`${
|
|
11
|
+
throw new ProviderCommandFailed(`${provider.command} does not list models.`);
|
|
12
12
|
}
|
|
13
13
|
return candidate;
|
|
14
14
|
}
|
|
15
|
-
export function
|
|
16
|
-
const
|
|
17
|
-
if (
|
|
18
|
-
const supported = Object.keys(
|
|
15
|
+
export function requireProvider(providers, provider) {
|
|
16
|
+
const impl = providers[provider];
|
|
17
|
+
if (impl === undefined) {
|
|
18
|
+
const supported = Object.keys(providers).sort().join(", ");
|
|
19
19
|
throw new ProviderCommandFailed(`Unknown provider: ${provider}. Supported: ${supported}.`);
|
|
20
20
|
}
|
|
21
|
-
return
|
|
21
|
+
return impl;
|
|
22
22
|
}
|
|
23
|
-
export function detected(
|
|
24
|
-
const rows = Object.entries(
|
|
23
|
+
export function detected(providers, which) {
|
|
24
|
+
const rows = Object.entries(providers).filter(([, provider]) => provider.requiresClient !== true).map(([name, provider]) => [name, which(provider.command), provider]);
|
|
25
25
|
rows.sort((left, right) => {
|
|
26
26
|
const leftMissing = left[1] === null ? 1 : 0;
|
|
27
27
|
const rightMissing = right[1] === null ? 1 : 0;
|
|
@@ -35,24 +35,24 @@ export function detected(adapters, which) {
|
|
|
35
35
|
});
|
|
36
36
|
return rows;
|
|
37
37
|
}
|
|
38
|
-
export async function loadCatalog(
|
|
39
|
-
if (which(
|
|
40
|
-
throw new ProviderCommandFailed(`${
|
|
38
|
+
export async function loadCatalog(provider, which, runCommand) {
|
|
39
|
+
if (which(provider.command) === null) {
|
|
40
|
+
throw new ProviderCommandFailed(`${provider.command} is not on PATH, so its models could not be listed.`);
|
|
41
41
|
}
|
|
42
|
-
const
|
|
42
|
+
const catalogProvider = asCatalog(provider);
|
|
43
43
|
let stdout;
|
|
44
44
|
try {
|
|
45
|
-
stdout = await runCommand(
|
|
45
|
+
stdout = await runCommand(catalogProvider.modelCatalogCommand(), catalogProvider.modelCatalogInput?.());
|
|
46
46
|
}
|
|
47
47
|
catch (error) {
|
|
48
48
|
if (error instanceof ProviderCommandFailed)
|
|
49
49
|
throw error;
|
|
50
50
|
const reason = error instanceof Error ? error.message : String(error);
|
|
51
|
-
throw new ProviderCommandFailed(`Could not list ${
|
|
51
|
+
throw new ProviderCommandFailed(`Could not list ${provider.command} models: ${reason}`);
|
|
52
52
|
}
|
|
53
|
-
const catalog =
|
|
53
|
+
const catalog = catalogProvider.parseModelCatalog(stdout);
|
|
54
54
|
if (catalog.ids.length === 0)
|
|
55
|
-
throw new ProviderCommandFailed(`${
|
|
55
|
+
throw new ProviderCommandFailed(`${provider.command} returned no models.`);
|
|
56
56
|
return catalog;
|
|
57
57
|
}
|
|
58
58
|
export function knownModel(model, catalog) {
|
|
@@ -75,16 +75,16 @@ export function parseOptions(pairs) {
|
|
|
75
75
|
}
|
|
76
76
|
return parsed;
|
|
77
77
|
}
|
|
78
|
-
export async function loadOptionValues(
|
|
78
|
+
export async function loadOptionValues(provider, name, model, catalog, which, runCommand) {
|
|
79
79
|
const found = catalog.valuesFor(model, name);
|
|
80
80
|
if (found !== null)
|
|
81
81
|
return found;
|
|
82
|
-
const
|
|
83
|
-
const argv =
|
|
82
|
+
const catalogProvider = asCatalog(provider);
|
|
83
|
+
const argv = catalogProvider.optionValuesCommand(name, model);
|
|
84
84
|
if (argv === null)
|
|
85
85
|
return null;
|
|
86
|
-
if (which(
|
|
87
|
-
throw new ProviderCommandFailed(`${
|
|
86
|
+
if (which(provider.command) === null) {
|
|
87
|
+
throw new ProviderCommandFailed(`${provider.command} is not on PATH, so its ${name} values could not be listed.`);
|
|
88
88
|
}
|
|
89
89
|
let text;
|
|
90
90
|
try {
|
|
@@ -95,18 +95,18 @@ export async function loadOptionValues(adapter, name, model, catalog, which, run
|
|
|
95
95
|
text = error.message;
|
|
96
96
|
else {
|
|
97
97
|
const reason = error instanceof Error ? error.message : String(error);
|
|
98
|
-
throw new ProviderCommandFailed(`Could not list ${
|
|
98
|
+
throw new ProviderCommandFailed(`Could not list ${provider.command} ${name} values for ${model}: ${reason}`);
|
|
99
99
|
}
|
|
100
100
|
}
|
|
101
|
-
return
|
|
101
|
+
return catalogProvider.parseOptionValues(name, text, model);
|
|
102
102
|
}
|
|
103
|
-
export async function acceptedOptions(
|
|
103
|
+
export async function acceptedOptions(provider, model, catalog, which, runCommand, options) {
|
|
104
104
|
if (Object.keys(options).length === 0)
|
|
105
105
|
return {};
|
|
106
|
-
|
|
106
|
+
provider.validateOptions(options);
|
|
107
107
|
const stored = {};
|
|
108
108
|
for (const [name, value] of Object.entries(options)) {
|
|
109
|
-
const values = await loadOptionValues(
|
|
109
|
+
const values = await loadOptionValues(provider, name, model, catalog, which, runCommand);
|
|
110
110
|
if (values === null) {
|
|
111
111
|
stored[name] = value;
|
|
112
112
|
continue;
|
|
@@ -120,11 +120,16 @@ export async function acceptedOptions(adapter, model, catalog, which, runCommand
|
|
|
120
120
|
}
|
|
121
121
|
return stored;
|
|
122
122
|
}
|
|
123
|
-
export function runProviderCommand(argv) {
|
|
123
|
+
export function runProviderCommand(argv, input) {
|
|
124
124
|
const [command, ...args] = argv;
|
|
125
125
|
if (command === undefined)
|
|
126
126
|
throw new ProviderCommandFailed("Provider command is empty.");
|
|
127
|
-
const completed = spawnProviderSync(command, args, {
|
|
127
|
+
const completed = spawnProviderSync(command, args, {
|
|
128
|
+
encoding: "utf8",
|
|
129
|
+
timeout: 60_000,
|
|
130
|
+
windowsHide: true,
|
|
131
|
+
...(input === undefined ? {} : { input }),
|
|
132
|
+
});
|
|
128
133
|
if (completed.error) {
|
|
129
134
|
throw new Error(completed.error.message);
|
|
130
135
|
}
|
package/dist/session-row.d.ts
CHANGED
|
@@ -7,9 +7,11 @@ export declare const SESSION_COLUMN: {
|
|
|
7
7
|
readonly decision: 5;
|
|
8
8
|
readonly outputs: 6;
|
|
9
9
|
readonly task: 7;
|
|
10
|
+
readonly title: 8;
|
|
10
11
|
};
|
|
11
12
|
export interface SessionFields {
|
|
12
13
|
session: string;
|
|
14
|
+
title: string;
|
|
13
15
|
workflow: string;
|
|
14
16
|
status: string;
|
|
15
17
|
execution: string;
|
package/dist/session-row.js
CHANGED
|
@@ -7,10 +7,12 @@ export const SESSION_COLUMN = {
|
|
|
7
7
|
decision: 5,
|
|
8
8
|
outputs: 6,
|
|
9
9
|
task: 7,
|
|
10
|
+
title: 8,
|
|
10
11
|
};
|
|
11
12
|
export function sessionFields(row) {
|
|
12
13
|
return {
|
|
13
14
|
session: row[SESSION_COLUMN.session] ?? "",
|
|
15
|
+
title: row[SESSION_COLUMN.title] ?? "",
|
|
14
16
|
workflow: row[SESSION_COLUMN.workflow] ?? "",
|
|
15
17
|
status: row[SESSION_COLUMN.status] ?? "",
|
|
16
18
|
execution: row[SESSION_COLUMN.execution] ?? "",
|
|
@@ -21,6 +21,9 @@ This skill is already in the prompt. Do not open its file again.
|
|
|
21
21
|
and `schema-workflow.yaml` in the same folder. A key absent from them is
|
|
22
22
|
ignored. Then run `migent doctor --json`; if it reports an outdated schema,
|
|
23
23
|
run `migent migrate --yes`.
|
|
24
|
+
- `.migent/config.local.yaml`, when present, overrides `config.yaml` on this
|
|
25
|
+
machine. It is not committed. Read both, and edit shared settings in
|
|
26
|
+
`config.yaml`, not the local file.
|
|
24
27
|
- You have no terminal for prompts. Pass `--no-input` (or set
|
|
25
28
|
`MIGENT_NO_INPUT=1`) to `init`, `config model`, and `config role`, and give
|
|
26
29
|
every value as a flag.
|
|
@@ -41,18 +44,18 @@ One-line task. The command runs until the session stops:
|
|
|
41
44
|
migent session run --workflow <workflow> --task "<task>"
|
|
42
45
|
```
|
|
43
46
|
|
|
44
|
-
Longer task. `<
|
|
47
|
+
Longer task. `<title>` is a display label such as `some task title`. The command prints a UUID `session_id`:
|
|
45
48
|
|
|
46
49
|
```
|
|
47
|
-
migent session prepare <
|
|
50
|
+
migent session prepare "<title>" --workflow <workflow>
|
|
48
51
|
```
|
|
49
52
|
|
|
50
|
-
Write the task, unchanged, to
|
|
51
|
-
|
|
53
|
+
Write the task, unchanged, to the printed `task_path` (plus any files next to it).
|
|
54
|
+
Do not edit `task.md` once the first stage has started.
|
|
52
55
|
Then:
|
|
53
56
|
|
|
54
57
|
```
|
|
55
|
-
migent session run --session <
|
|
58
|
+
migent session run --session <session-id> --workflow <workflow>
|
|
56
59
|
```
|
|
57
60
|
|
|
58
61
|
Dispatch only one stage: add `--once`.
|
|
@@ -122,18 +125,22 @@ migent session list --latest --json
|
|
|
122
125
|
```
|
|
123
126
|
|
|
124
127
|
`show` gives status, latest execution, decision, and outputs. `list --latest`
|
|
125
|
-
is the newest session, or `null`.
|
|
128
|
+
is the newest session, or `null`. Both include `title` when the session has one.
|
|
129
|
+
Report the session ID, title, status, stop reason,
|
|
126
130
|
decisions, and the outputs that matter. Output paths are relative to
|
|
127
131
|
`.migent/sessions/<session-id>/`; use the paths Migent reports.
|
|
128
132
|
|
|
129
133
|
## Set up a project
|
|
130
134
|
|
|
131
135
|
```
|
|
136
|
+
migent provider list --json
|
|
132
137
|
migent init --no-input --provider <provider> --model <model-id> [--preset implement|plan] [--skill]
|
|
133
138
|
migent doctor --json
|
|
134
139
|
```
|
|
135
140
|
|
|
136
141
|
`--provider` and `--model` go together. The default preset is `implement`.
|
|
142
|
+
`provider list --json` shows which provider CLIs are on PATH and what model
|
|
143
|
+
ids each supports. Use it to pick a `<provider>` and `<model-id>` for `init`.
|
|
137
144
|
`--skill` installs this skill; `migent skill update` refreshes it.
|
|
138
145
|
|
|
139
146
|
## Change models
|
|
@@ -2,6 +2,19 @@
|
|
|
2
2
|
|
|
3
3
|
Write commands take `--no-input --json`. The JSON includes the doctor result.
|
|
4
4
|
|
|
5
|
+
Run `migent provider list --json` to see which provider CLIs are available
|
|
6
|
+
and what model ids each supports. Use that output to pick a provider and
|
|
7
|
+
model id before using `config model` or `init`.
|
|
8
|
+
|
|
9
|
+
## Per-machine override
|
|
10
|
+
|
|
11
|
+
`.migent/config.local.yaml` is an optional, uncommitted file with the same keys
|
|
12
|
+
as `config.yaml`. Mappings merge key by key; scalars and lists, such as
|
|
13
|
+
`instructions`, replace the value from `config.yaml`. Its `schema_version` is
|
|
14
|
+
ignored. Runs, `doctor`, and `config show` read the merged result.
|
|
15
|
+
`config model` and `config role` edit `config.yaml` only, never the local file.
|
|
16
|
+
`doctor` warns if git tracks the local file.
|
|
17
|
+
|
|
5
18
|
## Run one role
|
|
6
19
|
|
|
7
20
|
Without `--workflow`, `session run` runs a single role:
|
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
# A missing schema_version is 0. Commands refuse the file while it is older than this Migent.
|
|
2
2
|
schema_version: 2
|
|
3
|
+
# .migent/config.local.yaml is an optional per-machine override with the same keys. It is not committed.
|
|
4
|
+
# Mappings merge key by key; scalars and lists replace the value from config.yaml. Its schema_version is ignored.
|
|
5
|
+
# config model and config role edit config.yaml only.
|
|
3
6
|
|
|
4
7
|
# models.<name>.model is the provider model id.
|
|
8
|
+
# models.<name>.client is optional: the CLI client id (agy, claude-code, codex, grok, opencode); required for provider byteplus
|
|
9
|
+
# that drives this model. Omit it to use the provider's own client.
|
|
5
10
|
# roles.<name>.default_model and a stage model are this name.
|
|
6
11
|
# Options merge in order: model, then role, then stage.
|
|
7
|
-
# Only keys the
|
|
12
|
+
# Only keys the provider marks overridable are stored on the provider session.
|
|
8
13
|
models:
|
|
9
14
|
planner:
|
|
10
15
|
provider: grok
|
|
@@ -19,9 +24,16 @@ models:
|
|
|
19
24
|
# disallowed_tools, max_budget_usd, fallback_model, and append_system_prompt.
|
|
20
25
|
# codex accepts model_reasoning_effort (overridable). Any other key of letters, digits,
|
|
21
26
|
# dots, underscores, and hyphens is passed through as codex exec -c.
|
|
27
|
+
# opencode accepts agent and thinking (true sends --thinking, false sends nothing). Neither is overridable.
|
|
22
28
|
options:
|
|
23
29
|
reasoning-effort: high
|
|
24
30
|
|
|
31
|
+
coder:
|
|
32
|
+
provider: byteplus
|
|
33
|
+
# byteplus has no CLI of its own, so client is required and names the CLI that runs it.
|
|
34
|
+
client: opencode
|
|
35
|
+
model: coding-plan/deepseek-v4-pro
|
|
36
|
+
|
|
25
37
|
roles:
|
|
26
38
|
planner:
|
|
27
39
|
default_model: planner
|
|
@@ -27,7 +27,7 @@ stages:
|
|
|
27
27
|
# model is a name under models in config.yaml, not a provider model id.
|
|
28
28
|
model: planner
|
|
29
29
|
# Options merge in order: model, then role, then stage.
|
|
30
|
-
# Only keys the
|
|
30
|
+
# Only keys the provider marks overridable are stored on the provider session.
|
|
31
31
|
options:
|
|
32
32
|
reasoning-effort: high
|
|
33
33
|
depends_on:
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nodeint/migent",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.8",
|
|
4
4
|
"description": "Compose AI coding agents into repeatable engineering workflows.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ai",
|
|
@@ -43,9 +43,9 @@
|
|
|
43
43
|
},
|
|
44
44
|
"dependencies": {
|
|
45
45
|
"@clack/prompts": "^1.8.1",
|
|
46
|
-
"@nodeint/migent-adapters": "0.1.
|
|
47
|
-
"@nodeint/migent-kernel": "0.1.
|
|
48
|
-
"@nodeint/migent-registry": "0.1.
|
|
46
|
+
"@nodeint/migent-adapters": "0.1.8",
|
|
47
|
+
"@nodeint/migent-kernel": "0.1.8",
|
|
48
|
+
"@nodeint/migent-registry": "0.1.8",
|
|
49
49
|
"commander": "^14.0.2",
|
|
50
50
|
"picocolors": "^1.1.1"
|
|
51
51
|
},
|