@kisev/skills-opencode 1.2.0 → 2.0.0
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 +134 -112
- package/README.ru.md +242 -0
- package/dist/agent-profiles.d.ts +1 -1
- package/dist/agent-profiles.js +18 -9
- package/dist/assets/agents/architect.md +67 -0
- package/{assets → dist/assets}/agents/critic.md +17 -2
- package/dist/assets/agents/manager.md +36 -0
- package/{assets → dist/assets}/agents/mapper.md +2 -2
- package/dist/assets/agents/review.md +25 -0
- package/{assets → dist/assets}/agents/worker.md +7 -5
- package/dist/assets/commands/agent-profiles.md +9 -0
- package/dist/assets/commands/agents-md.md +10 -0
- package/dist/assets/commands/askme.md +10 -0
- package/dist/assets/commands/ast-grep.md +10 -0
- package/dist/assets/commands/capabilities.md +9 -0
- package/dist/assets/commands/code-explain.md +10 -0
- package/dist/assets/commands/code-review.md +10 -0
- package/dist/assets/commands/commit-msg.md +10 -0
- package/dist/assets/commands/docs-prepare.md +10 -0
- package/dist/assets/commands/docs-review.md +10 -0
- package/dist/assets/commands/doctor.md +9 -0
- package/dist/assets/commands/doit.md +10 -0
- package/dist/assets/commands/goal.md +10 -0
- package/dist/assets/commands/humanize.md +10 -0
- package/dist/assets/commands/lsp-report.md +10 -0
- package/dist/assets/commands/mattermost.md +10 -0
- package/dist/assets/commands/mr-prepare.md +10 -0
- package/dist/assets/commands/reconcile.md +9 -0
- package/dist/assets/commands/release-prepare.md +10 -0
- package/dist/assets/commands/release-review.md +10 -0
- package/dist/assets/commands/rtk.md +10 -0
- package/dist/assets/commands/skill-improve.md +10 -0
- package/dist/assets/commands/slides-prompts-prepare.md +10 -0
- package/dist/assets/commands/spec-manage.md +10 -0
- package/dist/assets/commands/stopit.md +10 -0
- package/dist/assets/commands/summary.md +10 -0
- package/dist/assets/commands/task-prepare.md +10 -0
- package/dist/assets/commands/task-review.md +10 -0
- package/dist/assets/commands/task-triage.md +10 -0
- package/dist/assets/commands/team-retro.md +10 -0
- package/dist/assets/commands/team-roadmap.md +10 -0
- package/dist/assets/commands/team-sprint-close.md +10 -0
- package/dist/assets/commands/team-sprint-start.md +10 -0
- package/dist/assets/lsp-catalog.json +30 -0
- package/dist/assets/migration-inventory.json +294 -0
- package/dist/catalog.d.ts +8 -0
- package/dist/catalog.js +38 -0
- package/dist/cli-output.d.ts +7 -0
- package/dist/cli-output.js +68 -1
- package/dist/cli.js +136 -10
- package/dist/contracts.d.ts +52 -0
- package/dist/contracts.js +98 -0
- package/dist/doctor.d.ts +44 -0
- package/dist/doctor.js +595 -0
- package/dist/generate-assets.js +17 -2
- package/dist/index.d.ts +80 -56
- package/dist/index.js +162 -37
- package/dist/installer.d.ts +33 -5
- package/dist/installer.js +252 -28
- package/dist/lifecycle.d.ts +3 -0
- package/dist/lifecycle.js +49 -17
- package/dist/plugins/rtk.js +4 -1
- package/dist/reconcile.d.ts +43 -0
- package/dist/reconcile.js +507 -0
- package/dist/registry.d.ts +7 -4
- package/dist/registry.js +28 -88
- package/dist/routing.d.ts +39 -2
- package/dist/routing.js +339 -27
- package/dist/runtime/worktree.d.ts +33 -0
- package/dist/runtime/worktree.js +220 -0
- package/package.json +10 -26
- package/assets/agents/architect.md +0 -33
- package/assets/agents/manager.md +0 -82
- package/assets/agents/review.md +0 -25
- package/assets/commands/agent-list.md +0 -9
- package/assets/commands/agent-model-set.md +0 -9
- package/assets/commands/askme.md +0 -10
- package/assets/commands/ast-grep-rewrite.md +0 -10
- package/assets/commands/ast-grep-search.md +0 -10
- package/assets/commands/attempt-cancel.md +0 -10
- package/assets/commands/attempt-list.md +0 -10
- package/assets/commands/attempt-result.md +0 -10
- package/assets/commands/attempt-show.md +0 -10
- package/assets/commands/capabilities.md +0 -9
- package/assets/commands/code-review.md +0 -10
- package/assets/commands/commit-msg.md +0 -10
- package/assets/commands/critic-add.md +0 -9
- package/assets/commands/critic-remove.md +0 -9
- package/assets/commands/docs-prepare.md +0 -10
- package/assets/commands/docs-review.md +0 -10
- package/assets/commands/doctor.md +0 -9
- package/assets/commands/doit.md +0 -10
- package/assets/commands/goal-list.md +0 -10
- package/assets/commands/goal-pause.md +0 -10
- package/assets/commands/goal-prepare.md +0 -10
- package/assets/commands/goal-remove.md +0 -10
- package/assets/commands/goal-show.md +0 -10
- package/assets/commands/goal-start.md +0 -10
- package/assets/commands/lsp-report.md +0 -10
- package/assets/commands/mattermost.md +0 -10
- package/assets/commands/mr-prepare.md +0 -10
- package/assets/commands/mr-review.md +0 -10
- package/assets/commands/multi-run-cancel.md +0 -10
- package/assets/commands/multi-run-compare.md +0 -10
- package/assets/commands/multi-run-fusion.md +0 -10
- package/assets/commands/multi-run-start.md +0 -10
- package/assets/commands/multi-run-status.md +0 -10
- package/assets/commands/overview.md +0 -10
- package/assets/commands/release-prepare.md +0 -10
- package/assets/commands/release-review.md +0 -10
- package/assets/commands/route.md +0 -9
- package/assets/commands/schedule-add.md +0 -10
- package/assets/commands/schedule-disable.md +0 -10
- package/assets/commands/schedule-enable.md +0 -10
- package/assets/commands/schedule-list.md +0 -10
- package/assets/commands/schedule-remove.md +0 -10
- package/assets/commands/schedule-status.md +0 -10
- package/assets/commands/skill-improver.md +0 -10
- package/assets/commands/spec-audit.md +0 -10
- package/assets/commands/spec-init.md +0 -10
- package/assets/commands/spec-onboard.md +0 -10
- package/assets/commands/spec-update.md +0 -10
- package/assets/commands/stopit.md +0 -10
- package/assets/commands/summary.md +0 -10
- package/assets/commands/task-prepare-batch.md +0 -10
- package/assets/commands/task-prepare.md +0 -10
- package/assets/commands/task-review.md +0 -10
- package/assets/commands/task-triage.md +0 -10
- package/assets/commands/team-planning.md +0 -10
- package/assets/commands/team-retro.md +0 -10
- package/assets/commands/team-roadmap.md +0 -10
- package/assets/commands/team-slides-prompts.md +0 -10
- package/assets/commands/team-sprint-close.md +0 -10
- package/assets/commands/team-sprint-status.md +0 -10
- package/assets/commands/walkthrough.md +0 -10
- package/assets/plugins/autonomy-policy.js +0 -3
- package/assets/plugins/background-attempts.js +0 -3
- package/assets/plugins/goal-loop.js +0 -3
- package/assets/plugins/schedule.js +0 -3
- package/assets/plugins/zed-clickable-paths.js +0 -3
- package/dist/plugins/autonomy-policy.d.ts +0 -21
- package/dist/plugins/autonomy-policy.js +0 -71
- package/dist/plugins/background-attempts.d.ts +0 -76
- package/dist/plugins/background-attempts.js +0 -124
- package/dist/plugins/goal-loop.d.ts +0 -62
- package/dist/plugins/goal-loop.js +0 -99
- package/dist/plugins/schedule.d.ts +0 -36
- package/dist/plugins/schedule.js +0 -100
- package/dist/plugins/zed-clickable-paths.d.ts +0 -11
- package/dist/plugins/zed-clickable-paths.js +0 -10
- /package/{assets → dist/assets}/plugins/rtk.js +0 -0
- /package/{assets → dist/assets}/plugins/rules-injector.js +0 -0
- /package/{assets → dist/assets}/plugins/zed-bell.js +0 -0
package/dist/cli.js
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { AgentProfileError, applyAgentProfileChange, availableModels, availableModelVariants, FIXED_AGENT_ROLES, listAgentProfiles, previewAgentProfileChange, validateAgentName, validateModel, validateVariant, } from "./agent-profiles.js";
|
|
3
|
-
import { renderInventory, renderPlan, shellCommand, terminalSafe } from "./cli-output.js";
|
|
4
|
-
import {
|
|
3
|
+
import { renderDoctor, renderInventory, renderPlan, renderReconcile, shellCommand, terminalSafe } from "./cli-output.js";
|
|
4
|
+
import { collectDoctorFacts, doctorExitCode } from "./doctor.js";
|
|
5
|
+
import { CATALOG } from "./catalog.js";
|
|
6
|
+
import { apply, defaultSelection, InstallerError, normalizeSelection, PACKAGE_COMMANDS, preview, SELECTABLE_PLUGINS, SKILL_COMMANDS } from "./installer.js";
|
|
5
7
|
import { LifecycleError } from "./lifecycle.js";
|
|
8
|
+
import { applyReconcile, previewReconcile } from "./reconcile.js";
|
|
6
9
|
import { promptText, selectOption } from "./terminal-wizard.js";
|
|
7
|
-
function parseOptions(values) {
|
|
8
|
-
const options = { dryRun: false, json: false };
|
|
10
|
+
function parseOptions(values, requireScope = true) {
|
|
11
|
+
const options = { dryRun: false, json: false, selectionFlag: false };
|
|
9
12
|
for (let index = 0; index < values.length; index += 1) {
|
|
10
13
|
const value = values[index];
|
|
11
14
|
if (!value.startsWith("--")) {
|
|
@@ -58,14 +61,78 @@ function parseOptions(values) {
|
|
|
58
61
|
throw new InstallerError("invalid_input", "--json may be supplied once");
|
|
59
62
|
options.json = true;
|
|
60
63
|
}
|
|
64
|
+
else if (["--commands", "--skill-commands", "--package-commands", "--agents", "--plugins"].includes(value)) {
|
|
65
|
+
const raw = values[++index];
|
|
66
|
+
if (!raw)
|
|
67
|
+
throw new InstallerError("invalid_input", `${value} requires a comma-separated value`);
|
|
68
|
+
const target = value === "--agents" ? "agents" : value === "--plugins" ? "plugins" : "commands";
|
|
69
|
+
const names = raw === "none" ? [] : raw.split(",").map((item) => item.trim()).filter(Boolean);
|
|
70
|
+
options.selectionFlag = true;
|
|
71
|
+
if (value === "--package-commands") {
|
|
72
|
+
options.commands = [...(options.commands ?? []), ...names];
|
|
73
|
+
}
|
|
74
|
+
else if (value === "--skill-commands") {
|
|
75
|
+
options.commands = [...(options.commands ?? []), ...names];
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
options[target] = names;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
61
81
|
else {
|
|
62
82
|
throw new InstallerError("invalid_input", `Unknown argument: ${value}`);
|
|
63
83
|
}
|
|
64
84
|
}
|
|
65
|
-
if (!options.scope)
|
|
85
|
+
if (requireScope && !options.scope)
|
|
66
86
|
throw new InstallerError("invalid_input", "--scope is required");
|
|
67
87
|
return options;
|
|
68
88
|
}
|
|
89
|
+
function help() {
|
|
90
|
+
return [
|
|
91
|
+
"Usage: skills-opencode <command> [options]",
|
|
92
|
+
"",
|
|
93
|
+
"Commands: install, uninstall, doctor, capabilities, reconcile, agent list|configure|model-set|reconcile, critic add|remove",
|
|
94
|
+
"Mutations: use --dry-run for preview, then --confirm <digest>.",
|
|
95
|
+
"Install selection: --commands, --package-commands, --agents, --plugins (comma-separated or none).",
|
|
96
|
+
"Read-only: --json is stable machine-readable output; --help and --version need no scope.",
|
|
97
|
+
"",
|
|
98
|
+
].join("\n");
|
|
99
|
+
}
|
|
100
|
+
async function interactiveInstallerSelection() {
|
|
101
|
+
if (!process.stdin.isTTY || !process.stderr.isTTY)
|
|
102
|
+
throw new InstallerError("terminal_required", "install requires explicit selection flags outside a terminal");
|
|
103
|
+
const group = async (label, names, initial) => {
|
|
104
|
+
const choices = ["Select all", "Select none", ...names];
|
|
105
|
+
const selected = await selectOption(label, choices, process.stdin, process.stderr, initial);
|
|
106
|
+
if (selected === null)
|
|
107
|
+
throw new InstallerError("cancelled", "Wizard cancelled");
|
|
108
|
+
if (selected === 0)
|
|
109
|
+
return [...names];
|
|
110
|
+
if (selected === 1)
|
|
111
|
+
return [];
|
|
112
|
+
return [names[selected - 2]];
|
|
113
|
+
};
|
|
114
|
+
const commands = [
|
|
115
|
+
...(await group("Skill commands", SKILL_COMMANDS, 0)),
|
|
116
|
+
...(await group("Package commands", PACKAGE_COMMANDS, 0)),
|
|
117
|
+
];
|
|
118
|
+
const agents = await group("Fixed agents", defaultSelection().agents, 0);
|
|
119
|
+
const plugins = await group("Selectable plugins (none selected by default)", SELECTABLE_PLUGINS, 1);
|
|
120
|
+
return normalizeSelection({ commands, agents: agents, plugins: plugins });
|
|
121
|
+
}
|
|
122
|
+
function installerSelection(options) {
|
|
123
|
+
return normalizeSelection({
|
|
124
|
+
commands: options.commands,
|
|
125
|
+
agents: options.agents,
|
|
126
|
+
plugins: options.plugins,
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
function selectionArguments(selection) {
|
|
130
|
+
return [
|
|
131
|
+
"--commands", selection.commands.join(",") || "none",
|
|
132
|
+
"--agents", selection.agents.join(",") || "none",
|
|
133
|
+
"--plugins", selection.plugins.join(",") || "none",
|
|
134
|
+
];
|
|
135
|
+
}
|
|
69
136
|
function requireConfirmationMode(options) {
|
|
70
137
|
if (options.dryRun === Boolean(options.confirm))
|
|
71
138
|
throw new InstallerError("invalid_input", "Use exactly one of --dry-run or --confirm <digest>");
|
|
@@ -234,7 +301,61 @@ function profileConfirmationArguments(request, scope, digest) {
|
|
|
234
301
|
return command;
|
|
235
302
|
}
|
|
236
303
|
async function run(arguments_) {
|
|
304
|
+
if (arguments_.includes("--help") || arguments_.length === 0) {
|
|
305
|
+
process.stdout.write(help());
|
|
306
|
+
return;
|
|
307
|
+
}
|
|
308
|
+
if (arguments_.includes("--version")) {
|
|
309
|
+
process.stdout.write(arguments_.includes("--json")
|
|
310
|
+
? `${JSON.stringify({ status: "ok", version: CATALOG.version })}\n`
|
|
311
|
+
: `${CATALOG.version}\n`);
|
|
312
|
+
return;
|
|
313
|
+
}
|
|
237
314
|
const [domain, operation, ...rest] = arguments_;
|
|
315
|
+
if (domain === "doctor") {
|
|
316
|
+
if (operation)
|
|
317
|
+
rest.unshift(operation);
|
|
318
|
+
const options = parseOptions(rest);
|
|
319
|
+
if (options.dryRun || options.confirm || options.name || options.provider || options.model || options.variant !== undefined)
|
|
320
|
+
throw new InstallerError("invalid_input", "doctor accepts only --scope and --json");
|
|
321
|
+
const report = await collectDoctorFacts(options.scope);
|
|
322
|
+
if (options.json)
|
|
323
|
+
process.stdout.write(`${JSON.stringify(report, null, 2)}\n`);
|
|
324
|
+
else
|
|
325
|
+
process.stdout.write(renderDoctor(report));
|
|
326
|
+
process.exitCode = doctorExitCode(report);
|
|
327
|
+
return;
|
|
328
|
+
}
|
|
329
|
+
if (domain === "capabilities") {
|
|
330
|
+
const options = parseOptions(rest, false);
|
|
331
|
+
if (options.dryRun || options.confirm || options.name || options.provider || options.model || options.variant !== undefined || options.selectionFlag)
|
|
332
|
+
throw new InstallerError("invalid_input", "capabilities accepts only --scope and --json");
|
|
333
|
+
process.stdout.write(`${JSON.stringify({ schema_version: 1, status: "ok", ...CATALOG }, null, 2)}\n`);
|
|
334
|
+
return;
|
|
335
|
+
}
|
|
336
|
+
if (domain === "reconcile") {
|
|
337
|
+
if (operation)
|
|
338
|
+
rest.unshift(operation);
|
|
339
|
+
const options = parseOptions(rest);
|
|
340
|
+
if (options.name || options.provider || options.model || options.variant !== undefined)
|
|
341
|
+
throw new InstallerError("invalid_input", "reconcile accepts only scope and confirmation options");
|
|
342
|
+
requireConfirmationMode(options);
|
|
343
|
+
if (options.dryRun) {
|
|
344
|
+
const plan = await previewReconcile(options.scope);
|
|
345
|
+
if (options.json)
|
|
346
|
+
process.stdout.write(`${JSON.stringify({ status: "ok", applied: false, plan }, null, 2)}\n`);
|
|
347
|
+
else
|
|
348
|
+
process.stdout.write(renderReconcile(plan, { applied: false, confirmationCommand: shellCommand(["reconcile", "--scope", options.scope, "--confirm", plan.digest]) }));
|
|
349
|
+
}
|
|
350
|
+
else {
|
|
351
|
+
const applied = await applyReconcile(options.scope, options.confirm);
|
|
352
|
+
if (options.json)
|
|
353
|
+
process.stdout.write(`${JSON.stringify(applied, null, 2)}\n`);
|
|
354
|
+
else
|
|
355
|
+
process.stdout.write(renderReconcile(applied.plan, { applied: true }));
|
|
356
|
+
}
|
|
357
|
+
return;
|
|
358
|
+
}
|
|
238
359
|
if (domain === "install" || domain === "uninstall") {
|
|
239
360
|
if (operation?.startsWith("--") || operation === undefined)
|
|
240
361
|
rest.unshift(...(operation ? [operation] : []));
|
|
@@ -243,17 +364,22 @@ async function run(arguments_) {
|
|
|
243
364
|
const options = parseOptions(rest);
|
|
244
365
|
if (options.name || options.provider || options.model || options.variant !== undefined)
|
|
245
366
|
throw new InstallerError("invalid_input", "Installer accepts only scope and confirmation options");
|
|
246
|
-
requireConfirmationMode(options);
|
|
247
367
|
const action = domain;
|
|
368
|
+
if (action === "install" && options.selectionFlag && (options.commands === undefined || options.agents === undefined || options.plugins === undefined))
|
|
369
|
+
throw new InstallerError("invalid_input", "Non-TTY install requires --commands, --agents, and --plugins");
|
|
370
|
+
const selection = action === "install"
|
|
371
|
+
? options.selectionFlag ? installerSelection(options) : await interactiveInstallerSelection()
|
|
372
|
+
: undefined;
|
|
373
|
+
requireConfirmationMode(options);
|
|
248
374
|
if (options.dryRun) {
|
|
249
|
-
const plan = await preview(action, options.scope);
|
|
375
|
+
const plan = await preview(action, options.scope, process.cwd(), undefined, selection);
|
|
250
376
|
if (options.json)
|
|
251
|
-
process.stdout.write(`${JSON.stringify({ status: "ok", applied: false, requires_restart:
|
|
377
|
+
process.stdout.write(`${JSON.stringify({ status: "ok", applied: false, requires_restart: plan.requires_restart, plan }, null, 2)}\n`);
|
|
252
378
|
else
|
|
253
|
-
process.stdout.write(renderPlan(plan, { applied: false, confirmationCommand: shellCommand([action, "--scope", options.scope, "--confirm", plan.digest]) }));
|
|
379
|
+
process.stdout.write(renderPlan(plan, { applied: false, confirmationCommand: shellCommand([action, "--scope", options.scope, ...selectionArguments(plan.selection), "--confirm", plan.digest]) }));
|
|
254
380
|
}
|
|
255
381
|
else {
|
|
256
|
-
const plan = await apply(action, options.scope, options.confirm);
|
|
382
|
+
const plan = await apply(action, options.scope, options.confirm, process.cwd(), undefined, {}, selection);
|
|
257
383
|
if (options.json)
|
|
258
384
|
process.stdout.write(`${JSON.stringify({ status: "ok", applied: true, requires_restart: plan.requires_restart, plan }, null, 2)}\n`);
|
|
259
385
|
else
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { type ExecutionCard } from "./routing.js";
|
|
2
|
+
export declare const CONTRACT_SCHEMA_VERSION: 1;
|
|
3
|
+
export type AgentReportStatus = "COMPLETED" | "BLOCKED" | "FAILED" | "REJECTED_PLAN" | "APPROVED" | "CHANGES_REQUIRED";
|
|
4
|
+
export type MapperReport = {
|
|
5
|
+
mapper_report: {
|
|
6
|
+
schema_version: 1;
|
|
7
|
+
paths: Array<Record<string, string>>;
|
|
8
|
+
callers: Array<Record<string, string>>;
|
|
9
|
+
tests: Array<Record<string, string>>;
|
|
10
|
+
patterns: Array<Record<string, string>>;
|
|
11
|
+
evidence_gaps: string[];
|
|
12
|
+
};
|
|
13
|
+
};
|
|
14
|
+
export type WorkerReport = {
|
|
15
|
+
worker_report: {
|
|
16
|
+
schema_version: 1;
|
|
17
|
+
status: Extract<AgentReportStatus, "COMPLETED" | "BLOCKED" | "FAILED" | "REJECTED_PLAN">;
|
|
18
|
+
card_id: string | null;
|
|
19
|
+
revision: number | null;
|
|
20
|
+
changed_files: string[];
|
|
21
|
+
checks: Array<{
|
|
22
|
+
command: string;
|
|
23
|
+
status: string;
|
|
24
|
+
}>;
|
|
25
|
+
writes_performed: boolean;
|
|
26
|
+
risks: string[];
|
|
27
|
+
};
|
|
28
|
+
};
|
|
29
|
+
export type ReviewReport = {
|
|
30
|
+
review_report: {
|
|
31
|
+
schema_version: 1;
|
|
32
|
+
status: "APPROVED" | "CHANGES_REQUIRED";
|
|
33
|
+
target: string;
|
|
34
|
+
findings: Array<Record<string, unknown>>;
|
|
35
|
+
evidence: string[];
|
|
36
|
+
checks: string[];
|
|
37
|
+
risks: string[];
|
|
38
|
+
};
|
|
39
|
+
};
|
|
40
|
+
export type CriticReport = {
|
|
41
|
+
critic_report: {
|
|
42
|
+
schema_version: 1;
|
|
43
|
+
status: "APPROVED" | "CHANGES_REQUIRED";
|
|
44
|
+
card_id: string;
|
|
45
|
+
revision: number;
|
|
46
|
+
findings: Array<Record<string, unknown>>;
|
|
47
|
+
evidence: string[];
|
|
48
|
+
unrun_checks: string[];
|
|
49
|
+
risks: string[];
|
|
50
|
+
};
|
|
51
|
+
};
|
|
52
|
+
export declare function validateAgentReport(agent: string, report: unknown, card?: ExecutionCard): void;
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { validateExecutionCard } from "./routing.js";
|
|
2
|
+
export const CONTRACT_SCHEMA_VERSION = 1;
|
|
3
|
+
function object(value) {
|
|
4
|
+
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
5
|
+
}
|
|
6
|
+
function strings(value) {
|
|
7
|
+
return Array.isArray(value) && value.every((item) => typeof item === "string");
|
|
8
|
+
}
|
|
9
|
+
export function validateAgentReport(agent, report, card) {
|
|
10
|
+
if (!object(report))
|
|
11
|
+
throw new Error("agent report must be an object");
|
|
12
|
+
const reportKey = agent === "architect" ? "execution_card" : `${agent}_report`;
|
|
13
|
+
if (Object.keys(report).length !== 1 || !(reportKey in report))
|
|
14
|
+
throw new Error(`${agent} report has an invalid envelope`);
|
|
15
|
+
const value = report[reportKey];
|
|
16
|
+
if (!object(value))
|
|
17
|
+
throw new Error(`missing ${agent}_report`);
|
|
18
|
+
if (value.schema_version !== CONTRACT_SCHEMA_VERSION)
|
|
19
|
+
throw new Error(`${agent} report schema version is unsupported`);
|
|
20
|
+
if (agent === "mapper") {
|
|
21
|
+
for (const key of ["paths", "callers", "tests", "patterns"])
|
|
22
|
+
if (!Array.isArray(value[key]))
|
|
23
|
+
throw new Error(`mapper report field is invalid: ${key}`);
|
|
24
|
+
if (Object.keys(value).some((key) => !["schema_version", "paths", "callers", "tests", "patterns", "evidence_gaps"].includes(key)))
|
|
25
|
+
throw new Error("mapper report contains unknown fields");
|
|
26
|
+
if (!strings(value.evidence_gaps))
|
|
27
|
+
throw new Error("mapper report evidence_gaps is invalid");
|
|
28
|
+
}
|
|
29
|
+
else if (agent === "architect") {
|
|
30
|
+
if (value.status === "NEEDS_EVIDENCE")
|
|
31
|
+
return;
|
|
32
|
+
const result = validateExecutionCard(value);
|
|
33
|
+
if (!result.valid)
|
|
34
|
+
throw new Error(`architect execution card is invalid: ${result.failedField}`);
|
|
35
|
+
}
|
|
36
|
+
else if (agent === "worker") {
|
|
37
|
+
if (!strings(value.changed_files) ||
|
|
38
|
+
!Array.isArray(value.checks) ||
|
|
39
|
+
typeof value.writes_performed !== "boolean" ||
|
|
40
|
+
!strings(value.risks))
|
|
41
|
+
throw new Error("worker report is invalid");
|
|
42
|
+
if (Object.keys(value).some((key) => ![
|
|
43
|
+
"schema_version",
|
|
44
|
+
"status",
|
|
45
|
+
"card_id",
|
|
46
|
+
"revision",
|
|
47
|
+
"changed_files",
|
|
48
|
+
"checks",
|
|
49
|
+
"writes_performed",
|
|
50
|
+
"risks",
|
|
51
|
+
].includes(key)))
|
|
52
|
+
throw new Error("worker report contains unknown fields");
|
|
53
|
+
if (value.status === "COMPLETED" &&
|
|
54
|
+
(value.writes_performed !== true ||
|
|
55
|
+
value.checks.some((check) => check.status !== "passed")))
|
|
56
|
+
throw new Error("completed worker report has incomplete checks");
|
|
57
|
+
if (card && value.changed_files.some((path) => !card.write_set.includes(path)))
|
|
58
|
+
throw new Error("worker report writes outside execution card");
|
|
59
|
+
}
|
|
60
|
+
else if (agent === "review" || agent === "critic") {
|
|
61
|
+
if (!["APPROVED", "CHANGES_REQUIRED"].includes(String(value.status)) ||
|
|
62
|
+
!Array.isArray(value.findings) ||
|
|
63
|
+
(typeof value.target !== "string" && agent === "review"))
|
|
64
|
+
throw new Error(`${agent} report is invalid`);
|
|
65
|
+
if (agent === "review" &&
|
|
66
|
+
(!Array.isArray(value.checks) ||
|
|
67
|
+
!strings(value.checks) ||
|
|
68
|
+
!strings(value.risks) ||
|
|
69
|
+
!strings(value.evidence) ||
|
|
70
|
+
!value.target))
|
|
71
|
+
throw new Error("review report fields are invalid");
|
|
72
|
+
if (agent === "critic" &&
|
|
73
|
+
(typeof value.card_id !== "string" ||
|
|
74
|
+
!Number.isInteger(value.revision) ||
|
|
75
|
+
!strings(value.unrun_checks) ||
|
|
76
|
+
!strings(value.risks) ||
|
|
77
|
+
!strings(value.evidence)))
|
|
78
|
+
throw new Error("critic report fields are invalid");
|
|
79
|
+
const allowed = agent === "review"
|
|
80
|
+
? ["schema_version", "status", "target", "findings", "evidence", "checks", "risks"]
|
|
81
|
+
: [
|
|
82
|
+
"schema_version",
|
|
83
|
+
"status",
|
|
84
|
+
"card_id",
|
|
85
|
+
"revision",
|
|
86
|
+
"findings",
|
|
87
|
+
"evidence",
|
|
88
|
+
"unrun_checks",
|
|
89
|
+
"risks",
|
|
90
|
+
];
|
|
91
|
+
if (Object.keys(value).some((key) => !allowed.includes(key)))
|
|
92
|
+
throw new Error(`${agent} report contains unknown fields`);
|
|
93
|
+
}
|
|
94
|
+
else
|
|
95
|
+
throw new Error(`unknown report agent: ${agent}`);
|
|
96
|
+
if (card && (value.card_id !== card.card_id || value.revision !== card.revision))
|
|
97
|
+
throw new Error("agent report does not match execution card");
|
|
98
|
+
}
|
package/dist/doctor.d.ts
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { type Scope } from "./lifecycle.js";
|
|
2
|
+
export type DoctorCheckStatus = "pass" | "warn" | "fail" | "incomplete";
|
|
3
|
+
export type DoctorCheck = {
|
|
4
|
+
id: string;
|
|
5
|
+
status: DoctorCheckStatus;
|
|
6
|
+
summary: string;
|
|
7
|
+
evidence: Record<string, string | number | boolean | string[]>;
|
|
8
|
+
remediation?: string[];
|
|
9
|
+
};
|
|
10
|
+
export type DoctorHost = {
|
|
11
|
+
config?: () => Promise<unknown>;
|
|
12
|
+
lsp?: () => Promise<unknown>;
|
|
13
|
+
};
|
|
14
|
+
export type DoctorReport = {
|
|
15
|
+
schema_version: 1;
|
|
16
|
+
status: "clean" | "problems" | "incomplete" | "error";
|
|
17
|
+
mutations: false;
|
|
18
|
+
scope: Scope;
|
|
19
|
+
roots: {
|
|
20
|
+
project: string;
|
|
21
|
+
deployment: string;
|
|
22
|
+
lifecycle: string;
|
|
23
|
+
};
|
|
24
|
+
versions: {
|
|
25
|
+
package: string | null;
|
|
26
|
+
catalog: string;
|
|
27
|
+
installed_manifest: string | null;
|
|
28
|
+
opencode: string | null;
|
|
29
|
+
};
|
|
30
|
+
checks: DoctorCheck[];
|
|
31
|
+
counts: Record<DoctorCheckStatus, number>;
|
|
32
|
+
partial: string[];
|
|
33
|
+
unavailable: string[];
|
|
34
|
+
lsp: Record<string, unknown>;
|
|
35
|
+
retired: Record<string, unknown>;
|
|
36
|
+
conflicts: Array<Record<string, unknown>>;
|
|
37
|
+
archive: {
|
|
38
|
+
path: string;
|
|
39
|
+
entries: number;
|
|
40
|
+
valid: boolean;
|
|
41
|
+
};
|
|
42
|
+
};
|
|
43
|
+
export declare function collectDoctorFacts(scope: Scope, cwd?: string, home?: string, host?: DoctorHost): Promise<DoctorReport>;
|
|
44
|
+
export declare function doctorExitCode(report: DoctorReport): number;
|