agentplane 0.2.25 → 0.3.1
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 +3 -1
- package/assets/AGENTS.md +123 -526
- package/assets/agents/UPGRADER.json +10 -9
- package/assets/framework.manifest.json +112 -7
- package/assets/policy/check-routing.mjs +180 -0
- package/assets/policy/dod.code.md +25 -0
- package/assets/policy/dod.core.md +32 -0
- package/assets/policy/dod.docs.md +32 -0
- package/assets/policy/examples/migration-note.md +6 -0
- package/assets/policy/examples/pr-note.md +16 -0
- package/assets/policy/examples/unit-test-pattern.md +19 -0
- package/assets/policy/governance.md +37 -0
- package/assets/policy/incidents.md +36 -0
- package/assets/policy/security.must.md +7 -0
- package/assets/policy/workflow.branch_pr.md +34 -0
- package/assets/policy/workflow.direct.md +46 -0
- package/assets/policy/workflow.md +9 -0
- package/assets/policy/workflow.release.md +31 -0
- package/assets/policy/workflow.upgrade.md +20 -0
- package/bin/agentplane.js +47 -57
- package/bin/dist-guard.js +124 -0
- package/dist/.build-manifest.json +11 -0
- package/dist/agents/agents-template.d.ts +7 -0
- package/dist/agents/agents-template.d.ts.map +1 -1
- package/dist/agents/agents-template.js +41 -2
- package/dist/backends/task-backend/local-backend.d.ts +2 -0
- package/dist/backends/task-backend/local-backend.d.ts.map +1 -1
- package/dist/backends/task-backend/local-backend.js +12 -1
- package/dist/backends/task-backend/redmine/mapping.d.ts.map +1 -1
- package/dist/backends/task-backend/redmine/mapping.js +26 -1
- package/dist/backends/task-backend/redmine-backend.d.ts +4 -0
- package/dist/backends/task-backend/redmine-backend.d.ts.map +1 -1
- package/dist/backends/task-backend/redmine-backend.js +92 -9
- package/dist/backends/task-backend/shared/types.d.ts +1 -0
- package/dist/backends/task-backend/shared/types.d.ts.map +1 -1
- package/dist/backends/task-index.d.ts.map +1 -1
- package/dist/backends/task-index.js +8 -1
- package/dist/cli/command-guide.d.ts.map +1 -1
- package/dist/cli/command-guide.js +39 -17
- package/dist/cli/command-snippets.d.ts +24 -0
- package/dist/cli/command-snippets.d.ts.map +1 -0
- package/dist/cli/command-snippets.js +23 -0
- package/dist/cli/reason-codes.d.ts +9 -0
- package/dist/cli/reason-codes.d.ts.map +1 -0
- package/dist/cli/reason-codes.js +79 -0
- package/dist/cli/recipes-bundled.d.ts +1 -0
- package/dist/cli/recipes-bundled.d.ts.map +1 -1
- package/dist/cli/recipes-bundled.js +4 -1
- package/dist/cli/run-cli/command-catalog.d.ts +1 -1
- package/dist/cli/run-cli/command-catalog.d.ts.map +1 -1
- package/dist/cli/run-cli/command-catalog.js +40 -1
- package/dist/cli/run-cli/commands/config.d.ts +5 -0
- package/dist/cli/run-cli/commands/config.d.ts.map +1 -1
- package/dist/cli/run-cli/commands/config.js +86 -1
- package/dist/cli/run-cli/commands/core.d.ts.map +1 -1
- package/dist/cli/run-cli/commands/core.js +57 -2
- package/dist/cli/run-cli/commands/ide.d.ts.map +1 -1
- package/dist/cli/run-cli/commands/ide.js +8 -3
- package/dist/cli/run-cli/commands/init/recipes.d.ts +5 -1
- package/dist/cli/run-cli/commands/init/recipes.d.ts.map +1 -1
- package/dist/cli/run-cli/commands/init/recipes.js +24 -4
- package/dist/cli/run-cli/commands/init/ui.d.ts.map +1 -1
- package/dist/cli/run-cli/commands/init/ui.js +1 -2
- package/dist/cli/run-cli/commands/init/write-agents.d.ts +2 -0
- package/dist/cli/run-cli/commands/init/write-agents.d.ts.map +1 -1
- package/dist/cli/run-cli/commands/init/write-agents.js +24 -5
- package/dist/cli/run-cli/commands/init/write-workflow.d.ts +12 -0
- package/dist/cli/run-cli/commands/init/write-workflow.d.ts.map +1 -0
- package/dist/cli/run-cli/commands/init/write-workflow.js +58 -0
- package/dist/cli/run-cli/commands/init.d.ts +4 -1
- package/dist/cli/run-cli/commands/init.d.ts.map +1 -1
- package/dist/cli/run-cli/commands/init.js +126 -48
- package/dist/cli/run-cli.d.ts.map +1 -1
- package/dist/cli/run-cli.js +195 -8
- package/dist/commands/backend/sync.command.d.ts.map +1 -1
- package/dist/commands/backend/sync.command.js +7 -6
- package/dist/commands/backend.d.ts.map +1 -1
- package/dist/commands/backend.js +2 -0
- package/dist/commands/doctor.run.d.ts.map +1 -1
- package/dist/commands/doctor.run.js +107 -16
- package/dist/commands/guard/impl/commands.d.ts.map +1 -1
- package/dist/commands/guard/impl/commands.js +12 -6
- package/dist/commands/recipes/impl/commands/install.d.ts.map +1 -1
- package/dist/commands/recipes/impl/commands/install.js +36 -13
- package/dist/commands/recipes/impl/scenario.d.ts.map +1 -1
- package/dist/commands/recipes/impl/scenario.js +25 -0
- package/dist/commands/recipes/impl/types.d.ts +4 -0
- package/dist/commands/recipes/impl/types.d.ts.map +1 -1
- package/dist/commands/release/apply.command.d.ts.map +1 -1
- package/dist/commands/release/apply.command.js +9 -4
- package/dist/commands/release/plan.command.d.ts.map +1 -1
- package/dist/commands/release/plan.command.js +9 -3
- package/dist/commands/scenario/impl/commands.d.ts.map +1 -1
- package/dist/commands/scenario/impl/commands.js +74 -3
- package/dist/commands/scenario/impl/report.d.ts +8 -0
- package/dist/commands/scenario/impl/report.d.ts.map +1 -1
- package/dist/commands/scenario/impl/report.js +1 -0
- package/dist/commands/shared/reconcile-check.d.ts +7 -0
- package/dist/commands/shared/reconcile-check.d.ts.map +1 -0
- package/dist/commands/shared/reconcile-check.js +60 -0
- package/dist/commands/sync.command.d.ts.map +1 -1
- package/dist/commands/sync.command.js +9 -2
- package/dist/commands/task/add.d.ts.map +1 -1
- package/dist/commands/task/add.js +32 -0
- package/dist/commands/task/doc.command.d.ts.map +1 -1
- package/dist/commands/task/doc.command.js +1 -0
- package/dist/commands/task/finish.d.ts.map +1 -1
- package/dist/commands/task/finish.js +11 -1
- package/dist/commands/task/list.d.ts.map +1 -1
- package/dist/commands/task/list.js +2 -1
- package/dist/commands/task/list.spec.d.ts.map +1 -1
- package/dist/commands/task/list.spec.js +7 -0
- package/dist/commands/task/new.d.ts.map +1 -1
- package/dist/commands/task/new.js +41 -4
- package/dist/commands/task/next.d.ts.map +1 -1
- package/dist/commands/task/next.js +2 -1
- package/dist/commands/task/next.spec.d.ts.map +1 -1
- package/dist/commands/task/next.spec.js +7 -0
- package/dist/commands/task/plan.d.ts.map +1 -1
- package/dist/commands/task/plan.js +7 -1
- package/dist/commands/task/search.d.ts.map +1 -1
- package/dist/commands/task/search.js +2 -1
- package/dist/commands/task/search.spec.d.ts.map +1 -1
- package/dist/commands/task/search.spec.js +7 -0
- package/dist/commands/task/shared.d.ts +14 -0
- package/dist/commands/task/shared.d.ts.map +1 -1
- package/dist/commands/task/shared.js +58 -1
- package/dist/commands/task/start-ready.js +1 -1
- package/dist/commands/task/verify-record.d.ts.map +1 -1
- package/dist/commands/task/verify-record.js +2 -0
- package/dist/commands/upgrade.command.d.ts.map +1 -1
- package/dist/commands/upgrade.command.js +2 -2
- package/dist/commands/upgrade.d.ts.map +1 -1
- package/dist/commands/upgrade.js +263 -294
- package/dist/commands/workflow-build.command.d.ts +8 -0
- package/dist/commands/workflow-build.command.d.ts.map +1 -0
- package/dist/commands/workflow-build.command.js +103 -0
- package/dist/commands/workflow-playbook.command.d.ts +10 -0
- package/dist/commands/workflow-playbook.command.d.ts.map +1 -0
- package/dist/commands/workflow-playbook.command.js +173 -0
- package/dist/commands/workflow-restore.command.d.ts +5 -0
- package/dist/commands/workflow-restore.command.d.ts.map +1 -0
- package/dist/commands/workflow-restore.command.js +30 -0
- package/dist/commands/workflow.command.d.ts +6 -0
- package/dist/commands/workflow.command.d.ts.map +1 -0
- package/dist/commands/workflow.command.js +36 -0
- package/dist/harness/dynamic-tool-contract.d.ts +29 -0
- package/dist/harness/dynamic-tool-contract.d.ts.map +1 -0
- package/dist/harness/dynamic-tool-contract.js +86 -0
- package/dist/harness/hooks-lifecycle.d.ts +27 -0
- package/dist/harness/hooks-lifecycle.d.ts.map +1 -0
- package/dist/harness/hooks-lifecycle.js +67 -0
- package/dist/harness/index.d.ts +9 -0
- package/dist/harness/index.d.ts.map +1 -0
- package/dist/harness/index.js +8 -0
- package/dist/harness/reconcile.d.ts +37 -0
- package/dist/harness/reconcile.d.ts.map +1 -0
- package/dist/harness/reconcile.js +42 -0
- package/dist/harness/retry-policy.d.ts +31 -0
- package/dist/harness/retry-policy.d.ts.map +1 -0
- package/dist/harness/retry-policy.js +33 -0
- package/dist/harness/scheduler.d.ts +18 -0
- package/dist/harness/scheduler.d.ts.map +1 -0
- package/dist/harness/scheduler.js +55 -0
- package/dist/harness/state-machine.d.ts +17 -0
- package/dist/harness/state-machine.d.ts.map +1 -0
- package/dist/harness/state-machine.js +70 -0
- package/dist/harness/token-accounting.d.ts +19 -0
- package/dist/harness/token-accounting.d.ts.map +1 -0
- package/dist/harness/token-accounting.js +77 -0
- package/dist/harness/workspace-safety.d.ts +14 -0
- package/dist/harness/workspace-safety.d.ts.map +1 -0
- package/dist/harness/workspace-safety.js +62 -0
- package/dist/recipes/bundled-recipes.d.ts +4 -0
- package/dist/recipes/bundled-recipes.d.ts.map +1 -1
- package/dist/recipes/bundled-recipes.js +11 -0
- package/dist/shared/errors.d.ts +6 -0
- package/dist/shared/errors.d.ts.map +1 -1
- package/dist/shared/errors.js +1 -0
- package/dist/shared/policy-gateway.d.ts +15 -0
- package/dist/shared/policy-gateway.d.ts.map +1 -0
- package/dist/shared/policy-gateway.js +49 -0
- package/dist/shared/protected-paths.d.ts.map +1 -1
- package/dist/shared/protected-paths.js +1 -0
- package/dist/shared/runtime-artifacts.d.ts +2 -2
- package/dist/shared/runtime-artifacts.d.ts.map +1 -1
- package/dist/shared/runtime-artifacts.js +4 -0
- package/dist/workflow-runtime/build.d.ts +4 -0
- package/dist/workflow-runtime/build.d.ts.map +1 -0
- package/dist/workflow-runtime/build.js +126 -0
- package/dist/workflow-runtime/enforcement.d.ts +3 -0
- package/dist/workflow-runtime/enforcement.d.ts.map +1 -0
- package/dist/workflow-runtime/enforcement.js +10 -0
- package/dist/workflow-runtime/file-ops.d.ts +11 -0
- package/dist/workflow-runtime/file-ops.d.ts.map +1 -0
- package/dist/workflow-runtime/file-ops.js +248 -0
- package/dist/workflow-runtime/fix.d.ts +9 -0
- package/dist/workflow-runtime/fix.d.ts.map +1 -0
- package/dist/workflow-runtime/fix.js +107 -0
- package/dist/workflow-runtime/index.d.ts +11 -0
- package/dist/workflow-runtime/index.d.ts.map +1 -0
- package/dist/workflow-runtime/index.js +10 -0
- package/dist/workflow-runtime/markdown.d.ts +10 -0
- package/dist/workflow-runtime/markdown.d.ts.map +1 -0
- package/dist/workflow-runtime/markdown.js +147 -0
- package/dist/workflow-runtime/observability.d.ts +12 -0
- package/dist/workflow-runtime/observability.d.ts.map +1 -0
- package/dist/workflow-runtime/observability.js +14 -0
- package/dist/workflow-runtime/paths.d.ts +3 -0
- package/dist/workflow-runtime/paths.d.ts.map +1 -0
- package/dist/workflow-runtime/paths.js +11 -0
- package/dist/workflow-runtime/template.d.ts +7 -0
- package/dist/workflow-runtime/template.d.ts.map +1 -0
- package/dist/workflow-runtime/template.js +94 -0
- package/dist/workflow-runtime/types.d.ts +68 -0
- package/dist/workflow-runtime/types.d.ts.map +1 -0
- package/dist/workflow-runtime/types.js +1 -0
- package/dist/workflow-runtime/validate.d.ts +8 -0
- package/dist/workflow-runtime/validate.d.ts.map +1 -0
- package/dist/workflow-runtime/validate.js +331 -0
- package/package.json +3 -3
package/dist/cli/run-cli.js
CHANGED
|
@@ -15,15 +15,18 @@ import { helpSpec } from "./spec/help.js";
|
|
|
15
15
|
import { usageError } from "./spec/errors.js";
|
|
16
16
|
import { suggestOne } from "./spec/suggest.js";
|
|
17
17
|
import { COMMANDS } from "./run-cli/command-catalog.js";
|
|
18
|
+
import { getReasonCodeMeta } from "./reason-codes.js";
|
|
18
19
|
const GLOBAL_FLAGS = [
|
|
19
20
|
{ key: "help", forms: ["--help", "-h"], takesValue: false, scoped: false },
|
|
20
21
|
{ key: "version", forms: ["--version", "-v"], takesValue: false, scoped: false },
|
|
21
22
|
{ key: "noUpdateCheck", forms: ["--no-update-check"], takesValue: false, scoped: false },
|
|
22
23
|
{ key: "allowNetwork", forms: ["--allow-network"], takesValue: false, scoped: true },
|
|
23
24
|
{ key: "jsonErrors", forms: ["--json-errors"], takesValue: false, scoped: true },
|
|
25
|
+
{ key: "outputMode", forms: ["--output"], takesValue: true, scoped: false },
|
|
24
26
|
{ key: "root", forms: ["--root"], takesValue: true, scoped: false },
|
|
25
27
|
];
|
|
26
28
|
const GLOBAL_FLAG_FORMS = new Map(GLOBAL_FLAGS.flatMap((def) => def.forms.map((form) => [form, def])));
|
|
29
|
+
const HELP_TAIL_OPTIONS = new Set(["--compact", "--json"]);
|
|
27
30
|
function prescanJsonErrors(argv) {
|
|
28
31
|
// If parseGlobalArgs throws (e.g. missing --root value), we still want to honor
|
|
29
32
|
// `--json-errors` in the "scoped global" zone (before the command id).
|
|
@@ -54,6 +57,7 @@ function parseGlobalArgs(argv) {
|
|
|
54
57
|
let jsonErrors = false;
|
|
55
58
|
let root;
|
|
56
59
|
let allowNetwork = false;
|
|
60
|
+
let outputMode;
|
|
57
61
|
const rest = [];
|
|
58
62
|
for (let i = 0; i < argv.length; i++) {
|
|
59
63
|
const arg = argv[i];
|
|
@@ -103,6 +107,27 @@ function parseGlobalArgs(argv) {
|
|
|
103
107
|
i++;
|
|
104
108
|
break;
|
|
105
109
|
}
|
|
110
|
+
case "outputMode": {
|
|
111
|
+
const next = argv[i + 1];
|
|
112
|
+
if (!next) {
|
|
113
|
+
throw new CliError({
|
|
114
|
+
exitCode: 2,
|
|
115
|
+
code: "E_USAGE",
|
|
116
|
+
message: "Missing value after --output (expected text|json)",
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
const normalized = next.trim().toLowerCase();
|
|
120
|
+
if (normalized !== "text" && normalized !== "json") {
|
|
121
|
+
throw new CliError({
|
|
122
|
+
exitCode: 2,
|
|
123
|
+
code: "E_USAGE",
|
|
124
|
+
message: `Invalid value for --output: ${next} (expected text|json)`,
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
outputMode = normalized;
|
|
128
|
+
i++;
|
|
129
|
+
break;
|
|
130
|
+
}
|
|
106
131
|
default: {
|
|
107
132
|
// Exhaustive by construction; keep a defensive fallback.
|
|
108
133
|
rest.push(arg);
|
|
@@ -110,7 +135,85 @@ function parseGlobalArgs(argv) {
|
|
|
110
135
|
}
|
|
111
136
|
}
|
|
112
137
|
}
|
|
113
|
-
return {
|
|
138
|
+
return {
|
|
139
|
+
globals: { help, version, noUpdateCheck, root, jsonErrors, allowNetwork, outputMode },
|
|
140
|
+
rest,
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
const OUTPUT_MODE_ENV = "AGENTPLANE_OUTPUT";
|
|
144
|
+
function resolveOutputMode(modeFromFlag) {
|
|
145
|
+
if (modeFromFlag)
|
|
146
|
+
return modeFromFlag;
|
|
147
|
+
const fromEnv = process.env[OUTPUT_MODE_ENV]?.trim().toLowerCase();
|
|
148
|
+
if (!fromEnv || fromEnv === "text")
|
|
149
|
+
return "text";
|
|
150
|
+
if (fromEnv === "json")
|
|
151
|
+
return "json";
|
|
152
|
+
throw new CliError({
|
|
153
|
+
exitCode: 2,
|
|
154
|
+
code: "E_USAGE",
|
|
155
|
+
message: `Invalid ${OUTPUT_MODE_ENV}: ${fromEnv} (expected text|json)`,
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
function chunkToString(chunk, encoding) {
|
|
159
|
+
if (typeof chunk === "string")
|
|
160
|
+
return chunk;
|
|
161
|
+
if (chunk instanceof Uint8Array)
|
|
162
|
+
return Buffer.from(chunk).toString(encoding);
|
|
163
|
+
return String(chunk);
|
|
164
|
+
}
|
|
165
|
+
async function runWithOutputMode(opts) {
|
|
166
|
+
if (opts.mode === "text")
|
|
167
|
+
return await opts.run();
|
|
168
|
+
const stdoutWrite = process.stdout.write.bind(process.stdout);
|
|
169
|
+
const stderrWrite = process.stderr.write.bind(process.stderr);
|
|
170
|
+
let stdout = "";
|
|
171
|
+
let stderr = "";
|
|
172
|
+
process.stdout.write = ((chunk, ...rest) => {
|
|
173
|
+
const encoding = typeof rest[0] === "string" ? rest[0] : undefined;
|
|
174
|
+
stdout += chunkToString(chunk, encoding);
|
|
175
|
+
const callback = rest.find((item) => typeof item === "function");
|
|
176
|
+
callback?.(null);
|
|
177
|
+
return true;
|
|
178
|
+
});
|
|
179
|
+
process.stderr.write = ((chunk, ...rest) => {
|
|
180
|
+
const encoding = typeof rest[0] === "string" ? rest[0] : undefined;
|
|
181
|
+
stderr += chunkToString(chunk, encoding);
|
|
182
|
+
const callback = rest.find((item) => typeof item === "function");
|
|
183
|
+
callback?.(null);
|
|
184
|
+
return true;
|
|
185
|
+
});
|
|
186
|
+
try {
|
|
187
|
+
const exitCode = await opts.run();
|
|
188
|
+
let parsed;
|
|
189
|
+
const trimmedStdout = stdout.trim();
|
|
190
|
+
if (trimmedStdout.length > 0) {
|
|
191
|
+
try {
|
|
192
|
+
parsed = JSON.parse(trimmedStdout);
|
|
193
|
+
}
|
|
194
|
+
catch {
|
|
195
|
+
parsed = undefined;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
const payload = {
|
|
199
|
+
schema_version: 1,
|
|
200
|
+
mode: "agent_json_v1",
|
|
201
|
+
command: opts.command,
|
|
202
|
+
ok: exitCode === 0,
|
|
203
|
+
exit_code: exitCode,
|
|
204
|
+
stdout: trimmedStdout,
|
|
205
|
+
stderr: stderr.trim(),
|
|
206
|
+
};
|
|
207
|
+
if (parsed !== undefined) {
|
|
208
|
+
payload.data = parsed;
|
|
209
|
+
}
|
|
210
|
+
stdoutWrite(`${JSON.stringify(payload, null, 2)}\n`);
|
|
211
|
+
return exitCode;
|
|
212
|
+
}
|
|
213
|
+
finally {
|
|
214
|
+
process.stdout.write = stdoutWrite;
|
|
215
|
+
process.stderr.write = stderrWrite;
|
|
216
|
+
}
|
|
114
217
|
}
|
|
115
218
|
function matchCommandCatalog(tokens) {
|
|
116
219
|
let best = null;
|
|
@@ -135,8 +238,15 @@ function matchCommandCatalog(tokens) {
|
|
|
135
238
|
}
|
|
136
239
|
function writeError(err, jsonErrors) {
|
|
137
240
|
const guidance = resolveErrorGuidance(err);
|
|
241
|
+
const contextReasonCode = typeof err.context?.reason_code === "string" ? String(err.context.reason_code) : undefined;
|
|
242
|
+
const reasonCode = contextReasonCode ?? guidance.nextAction?.reasonCode;
|
|
243
|
+
const reasonDecode = getReasonCodeMeta(reasonCode);
|
|
138
244
|
if (jsonErrors) {
|
|
139
|
-
process.stdout.write(`${formatJsonError(err, {
|
|
245
|
+
process.stdout.write(`${formatJsonError(err, {
|
|
246
|
+
hint: guidance.hint,
|
|
247
|
+
nextAction: guidance.nextAction,
|
|
248
|
+
reasonDecode,
|
|
249
|
+
})}\n`);
|
|
140
250
|
}
|
|
141
251
|
else {
|
|
142
252
|
const header = `error [${err.code}]`;
|
|
@@ -152,6 +262,10 @@ function writeError(err, jsonErrors) {
|
|
|
152
262
|
if (guidance.nextAction) {
|
|
153
263
|
process.stderr.write(`next_action: ${guidance.nextAction.command} (${guidance.nextAction.reason})\n`);
|
|
154
264
|
}
|
|
265
|
+
if (reasonDecode) {
|
|
266
|
+
process.stderr.write(`reason_code: ${reasonDecode.code} [${reasonDecode.category}] ${reasonDecode.summary}\n`);
|
|
267
|
+
process.stderr.write(`reason_action: ${reasonDecode.action}\n`);
|
|
268
|
+
}
|
|
155
269
|
}
|
|
156
270
|
}
|
|
157
271
|
const AGENTPLANE_HOME_ENV = "AGENTPLANE_HOME";
|
|
@@ -163,9 +277,20 @@ function resolveAgentplaneHome() {
|
|
|
163
277
|
}
|
|
164
278
|
function resolveErrorGuidance(err) {
|
|
165
279
|
const command = typeof err.context?.command === "string" ? err.context.command : undefined;
|
|
280
|
+
const reasonCode = typeof err.context?.reason_code === "string" ? String(err.context.reason_code) : undefined;
|
|
166
281
|
const usage = command ? `agentplane help ${command} --compact` : "agentplane help";
|
|
167
282
|
switch (err.code) {
|
|
168
283
|
case "E_USAGE": {
|
|
284
|
+
if (reasonCode === "sync_backend_mismatch") {
|
|
285
|
+
return {
|
|
286
|
+
hint: "Configured backend id mismatch. Check active backend and retry with a matching id.",
|
|
287
|
+
nextAction: {
|
|
288
|
+
command: "agentplane config show",
|
|
289
|
+
reason: "inspect active backend id before running sync",
|
|
290
|
+
reasonCode: "sync_backend_mismatch",
|
|
291
|
+
},
|
|
292
|
+
};
|
|
293
|
+
}
|
|
169
294
|
return {
|
|
170
295
|
hint: `See \`${usage}\` for usage.`,
|
|
171
296
|
nextAction: {
|
|
@@ -176,6 +301,16 @@ function resolveErrorGuidance(err) {
|
|
|
176
301
|
};
|
|
177
302
|
}
|
|
178
303
|
case "E_GIT": {
|
|
304
|
+
if (reasonCode === "reconcile_git_state_unreadable") {
|
|
305
|
+
return {
|
|
306
|
+
hint: "Reconcile check could not read git state.",
|
|
307
|
+
nextAction: {
|
|
308
|
+
command: "git status --short --untracked-files=no",
|
|
309
|
+
reason: "confirm repository state is readable before mutating commands",
|
|
310
|
+
reasonCode: "reconcile_git_state_unreadable",
|
|
311
|
+
},
|
|
312
|
+
};
|
|
313
|
+
}
|
|
179
314
|
if (command?.startsWith("branch")) {
|
|
180
315
|
return {
|
|
181
316
|
hint: "Check git repo/branch; run `git branch` or pass --root <path>.",
|
|
@@ -236,6 +371,17 @@ function resolveErrorGuidance(err) {
|
|
|
236
371
|
};
|
|
237
372
|
}
|
|
238
373
|
case "E_VALIDATION": {
|
|
374
|
+
if (reasonCode === "reconcile_task_scan_failed" ||
|
|
375
|
+
reasonCode === "reconcile_task_scan_incomplete") {
|
|
376
|
+
return {
|
|
377
|
+
hint: "Reconcile check failed due to task scan drift or parse/read errors.",
|
|
378
|
+
nextAction: {
|
|
379
|
+
command: "agentplane task list --strict-read",
|
|
380
|
+
reason: "surface task scan/read failures before retrying mutating commands",
|
|
381
|
+
reasonCode: reasonCode,
|
|
382
|
+
},
|
|
383
|
+
};
|
|
384
|
+
}
|
|
239
385
|
return {
|
|
240
386
|
hint: "Fix invalid config/input shape and rerun.",
|
|
241
387
|
nextAction: {
|
|
@@ -361,10 +507,17 @@ export async function runCli(argv) {
|
|
|
361
507
|
let jsonErrors = prescanJsonErrors(argv);
|
|
362
508
|
try {
|
|
363
509
|
const { globals, rest } = parseGlobalArgs(argv);
|
|
364
|
-
|
|
510
|
+
const outputMode = resolveOutputMode(globals.outputMode);
|
|
511
|
+
jsonErrors = globals.jsonErrors || outputMode === "json";
|
|
365
512
|
if (globals.version) {
|
|
366
|
-
|
|
367
|
-
|
|
513
|
+
return await runWithOutputMode({
|
|
514
|
+
mode: outputMode,
|
|
515
|
+
command: "version",
|
|
516
|
+
run: () => {
|
|
517
|
+
process.stdout.write(`${getVersion()}\n`);
|
|
518
|
+
return Promise.resolve(0);
|
|
519
|
+
},
|
|
520
|
+
});
|
|
368
521
|
}
|
|
369
522
|
const runCli2HelpFast = async (helpArgv) => {
|
|
370
523
|
const { buildRegistry } = await import("./run-cli/registry.run.js");
|
|
@@ -384,12 +537,42 @@ export async function runCli(argv) {
|
|
|
384
537
|
}
|
|
385
538
|
const tail = helpArgv.slice(match.consumed);
|
|
386
539
|
const parsed = parseCommandArgv(match.spec, tail).parsed;
|
|
387
|
-
return await
|
|
540
|
+
return await runWithOutputMode({
|
|
541
|
+
mode: outputMode,
|
|
542
|
+
command: match.spec.id.join(" "),
|
|
543
|
+
run: async () => {
|
|
544
|
+
return await match.handler({ cwd: process.cwd(), rootOverride: globals.root }, parsed);
|
|
545
|
+
},
|
|
546
|
+
});
|
|
388
547
|
};
|
|
389
548
|
// `--help` is treated as an alias for `help` and supports per-command help:
|
|
390
549
|
// - agentplane --help
|
|
391
550
|
// - agentplane <cmd...> --help [--compact|--json]
|
|
392
551
|
if (globals.help) {
|
|
552
|
+
const matchedHelp = matchCommandCatalog(rest);
|
|
553
|
+
if (matchedHelp) {
|
|
554
|
+
const rawHelpTail = rest.slice(matchedHelp.consumed);
|
|
555
|
+
const commandFlags = new Set();
|
|
556
|
+
for (const opt of matchedHelp.entry.spec.options ?? []) {
|
|
557
|
+
const optRecord = opt;
|
|
558
|
+
const long = typeof optRecord.name === "string" ? optRecord.name.trim() : "";
|
|
559
|
+
if (long)
|
|
560
|
+
commandFlags.add(`--${long}`);
|
|
561
|
+
const short = typeof optRecord.alias === "string" ? optRecord.alias.trim() : "";
|
|
562
|
+
if (short)
|
|
563
|
+
commandFlags.add(`-${short}`);
|
|
564
|
+
}
|
|
565
|
+
const invalidHelpTail = rawHelpTail.filter((token) => token.startsWith("-") && !HELP_TAIL_OPTIONS.has(token) && !commandFlags.has(token));
|
|
566
|
+
if (invalidHelpTail.length > 0) {
|
|
567
|
+
throw usageError({
|
|
568
|
+
spec: helpSpec,
|
|
569
|
+
command: "help",
|
|
570
|
+
message: `Unsupported flag(s) after --help: ${invalidHelpTail.join(", ")}.`,
|
|
571
|
+
});
|
|
572
|
+
}
|
|
573
|
+
const helpTail = rawHelpTail.filter((token) => token.startsWith("-") && HELP_TAIL_OPTIONS.has(token));
|
|
574
|
+
return await runCli2HelpFast(["help", ...matchedHelp.entry.spec.id, ...helpTail]);
|
|
575
|
+
}
|
|
393
576
|
return await runCli2HelpFast(["help", ...rest]);
|
|
394
577
|
}
|
|
395
578
|
if (rest.length === 0) {
|
|
@@ -451,7 +634,7 @@ export async function runCli(argv) {
|
|
|
451
634
|
await maybeWarnOnUpdate({
|
|
452
635
|
currentVersion: getVersion(),
|
|
453
636
|
skip: globals.noUpdateCheck || skipUpdateCheckForPolicy || matched?.entry.needsConfig === false,
|
|
454
|
-
jsonErrors
|
|
637
|
+
jsonErrors,
|
|
455
638
|
});
|
|
456
639
|
let ctxPromise = null;
|
|
457
640
|
const getCtx = async (commandForErrorContext) => {
|
|
@@ -493,7 +676,11 @@ export async function runCli(argv) {
|
|
|
493
676
|
if (match) {
|
|
494
677
|
const tail = rest.slice(match.consumed);
|
|
495
678
|
const parsed = parseCommandArgv(match.spec, tail).parsed;
|
|
496
|
-
return await
|
|
679
|
+
return await runWithOutputMode({
|
|
680
|
+
mode: outputMode,
|
|
681
|
+
command: match.spec.id.join(" "),
|
|
682
|
+
run: async () => await match.handler({ cwd, rootOverride: globals.root }, parsed),
|
|
683
|
+
});
|
|
497
684
|
}
|
|
498
685
|
const input = rest.join(" ");
|
|
499
686
|
const fullCandidates = registry.list().map((e) => e.spec.id.join(" "));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sync.command.d.ts","sourceRoot":"","sources":["../../../src/commands/backend/sync.command.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"sync.command.d.ts","sourceRoot":"","sources":["../../../src/commands/backend/sync.command.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAItE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAChE,OAAO,EAAwB,KAAK,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAE7E,KAAK,iBAAiB,GAAG;IAAE,GAAG,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC;AAE3C,eAAO,MAAM,WAAW,EAAE,WAAW,CAAC,iBAAiB,CAQtD,CAAC;AAEF,eAAO,MAAM,eAAe,EAAE,WAAW,CAAC,iBAAiB,CAuC1D,CAAC;AAEF,iBAAS,mBAAmB,CAAC,IAAI,EAAE,UAAU,EAAE,CAAC,EAAE,iBAAiB,GAAG,OAAO,CAAC,MAAM,CAAC,CAUpF;AAED,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,cAAc,CAAC,8BAEtF;AAED,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,cAAc,CAAC,IAC1E,KAAK,UAAU,EAAE,GAAG,iBAAiB,KAAG,OAAO,CAAC,MAAM,CAAC,CAStE"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { COMMAND_SNIPPETS } from "../../cli/command-snippets.js";
|
|
1
2
|
import { usageError } from "../../cli/spec/errors.js";
|
|
2
3
|
import { suggestOne } from "../../cli/spec/suggest.js";
|
|
3
4
|
import { cmdBackendSyncParsed } from "../backend.js";
|
|
@@ -7,7 +8,7 @@ export const backendSpec = {
|
|
|
7
8
|
summary: "Backend-related operations.",
|
|
8
9
|
description: "This is a command group. Use a subcommand such as `agentplane backend sync ...`.",
|
|
9
10
|
args: [{ name: "cmd", required: false, variadic: true, valueHint: "<cmd>" }],
|
|
10
|
-
examples: [{ cmd:
|
|
11
|
+
examples: [{ cmd: COMMAND_SNIPPETS.backendSync.pullLocal, why: "Sync the backend." }],
|
|
11
12
|
parse: (raw) => ({ cmd: (raw.args.cmd ?? []) }),
|
|
12
13
|
};
|
|
13
14
|
export const backendSyncSpec = {
|
|
@@ -21,8 +22,8 @@ export const backendSyncSpec = {
|
|
|
21
22
|
name: "direction",
|
|
22
23
|
valueHint: "<push|pull>",
|
|
23
24
|
choices: ["push", "pull"],
|
|
24
|
-
|
|
25
|
-
description: "Sync direction.",
|
|
25
|
+
default: "push",
|
|
26
|
+
description: "Sync direction (default: push).",
|
|
26
27
|
},
|
|
27
28
|
{
|
|
28
29
|
kind: "string",
|
|
@@ -36,15 +37,15 @@ export const backendSyncSpec = {
|
|
|
36
37
|
{ kind: "boolean", name: "quiet", default: false, description: "Reduce output noise." },
|
|
37
38
|
],
|
|
38
39
|
examples: [
|
|
39
|
-
{ cmd:
|
|
40
|
+
{ cmd: COMMAND_SNIPPETS.backendSync.pullLocal, why: "Pull from backend." },
|
|
40
41
|
{
|
|
41
|
-
cmd:
|
|
42
|
+
cmd: COMMAND_SNIPPETS.backendSync.pushRedmineWithYes,
|
|
42
43
|
why: "Push to a networked backend with explicit approval.",
|
|
43
44
|
},
|
|
44
45
|
],
|
|
45
46
|
parse: (raw) => ({
|
|
46
47
|
backendId: String(raw.args.id),
|
|
47
|
-
direction: raw.opts.direction,
|
|
48
|
+
direction: (raw.opts.direction ?? "push"),
|
|
48
49
|
conflict: (raw.opts.conflict ?? "diff"),
|
|
49
50
|
yes: raw.opts.yes === true,
|
|
50
51
|
quiet: raw.opts.quiet === true,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"backend.d.ts","sourceRoot":"","sources":["../../src/commands/backend.ts"],"names":[],"mappings":"AAGA,OAAO,EAAsB,KAAK,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAGnF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,GAAG,MAAM,CAAC;IAC3B,QAAQ,EAAE,MAAM,GAAG,cAAc,GAAG,eAAe,GAAG,MAAM,CAAC;IAC7D,GAAG,EAAE,OAAO,CAAC;IACb,KAAK,EAAE,OAAO,CAAC;CAChB,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG;IACvB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,SAAS,EAAE,MAAM,GAAG,MAAM,CAAC;IAC3B,QAAQ,EAAE,MAAM,GAAG,cAAc,GAAG,eAAe,GAAG,MAAM,CAAC;IAC7D,GAAG,EAAE,OAAO,CAAC;IACb,KAAK,EAAE,OAAO,CAAC;CAChB,CAAC;AAEF,wBAAsB,oBAAoB,CAAC,IAAI,EAAE;IAC/C,GAAG,CAAC,EAAE,cAAc,CAAC;IACrB,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,iBAAiB,CAAC;CAC1B,GAAG,OAAO,CAAC,MAAM,CAAC,
|
|
1
|
+
{"version":3,"file":"backend.d.ts","sourceRoot":"","sources":["../../src/commands/backend.ts"],"names":[],"mappings":"AAGA,OAAO,EAAsB,KAAK,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAGnF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,GAAG,MAAM,CAAC;IAC3B,QAAQ,EAAE,MAAM,GAAG,cAAc,GAAG,eAAe,GAAG,MAAM,CAAC;IAC7D,GAAG,EAAE,OAAO,CAAC;IACb,KAAK,EAAE,OAAO,CAAC;CAChB,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG;IACvB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,SAAS,EAAE,MAAM,GAAG,MAAM,CAAC;IAC3B,QAAQ,EAAE,MAAM,GAAG,cAAc,GAAG,eAAe,GAAG,MAAM,CAAC;IAC7D,GAAG,EAAE,OAAO,CAAC;IACb,KAAK,EAAE,OAAO,CAAC;CAChB,CAAC;AAEF,wBAAsB,oBAAoB,CAAC,IAAI,EAAE;IAC/C,GAAG,CAAC,EAAE,cAAc,CAAC;IACrB,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,iBAAiB,CAAC;CAC1B,GAAG,OAAO,CAAC,MAAM,CAAC,CAyClB;AAED,wBAAsB,aAAa,CAAC,IAAI,EAAE;IACxC,GAAG,CAAC,EAAE,cAAc,CAAC;IACrB,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,UAAU,CAAC;CACnB,GAAG,OAAO,CAAC,MAAM,CAAC,CAyClB"}
|
package/dist/commands/backend.js
CHANGED
|
@@ -15,6 +15,7 @@ export async function cmdBackendSyncParsed(opts) {
|
|
|
15
15
|
exitCode: 2,
|
|
16
16
|
code: "E_USAGE",
|
|
17
17
|
message: `Configured backend is "${backendId}", not "${opts.flags.backendId}"`,
|
|
18
|
+
context: { command: "backend sync", reason_code: "sync_backend_mismatch" },
|
|
18
19
|
});
|
|
19
20
|
}
|
|
20
21
|
if (!backend.sync) {
|
|
@@ -57,6 +58,7 @@ export async function cmdSyncParsed(opts) {
|
|
|
57
58
|
exitCode: 2,
|
|
58
59
|
code: "E_USAGE",
|
|
59
60
|
message: `Configured backend is "${backendId}", not "${opts.flags.backendId}"`,
|
|
61
|
+
context: { command: "sync", reason_code: "sync_backend_mismatch" },
|
|
60
62
|
});
|
|
61
63
|
}
|
|
62
64
|
if (!backend.sync) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"doctor.run.d.ts","sourceRoot":"","sources":["../../src/commands/doctor.run.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"doctor.run.d.ts","sourceRoot":"","sources":["../../src/commands/doctor.run.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAM1D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAyUrD,eAAO,MAAM,SAAS,EAAE,cAAc,CAAC,YAAY,CAmDlD,CAAC"}
|
|
@@ -3,8 +3,10 @@ import path from "node:path";
|
|
|
3
3
|
import { resolveProject } from "@agentplaneorg/core";
|
|
4
4
|
import { warnMessage, successMessage } from "../cli/output.js";
|
|
5
5
|
import { RUNTIME_GITIGNORE_LINES } from "../shared/runtime-artifacts.js";
|
|
6
|
+
import { resolvePolicyGatewayForRepo } from "../shared/policy-gateway.js";
|
|
6
7
|
import { execFileAsync, gitEnv } from "./shared/git.js";
|
|
7
8
|
import { loadCommandContext } from "./shared/task-backend.js";
|
|
9
|
+
import { emitWorkflowEvent, isWorkflowEnforcementDisabled, resolveWorkflowPaths, safeAutofixWorkflowText, validateWorkflowAtPath, workflowEnforcementEnvHint, } from "../workflow-runtime/index.js";
|
|
8
10
|
async function listTsFiles(rootDir) {
|
|
9
11
|
const out = [];
|
|
10
12
|
async function walk(absDir) {
|
|
@@ -57,15 +59,19 @@ async function isDirectory(absPath) {
|
|
|
57
59
|
}
|
|
58
60
|
async function checkWorkspace(repoRoot) {
|
|
59
61
|
const problems = [];
|
|
60
|
-
const requiredFiles = [
|
|
61
|
-
path.join(repoRoot, "AGENTS.md"),
|
|
62
|
-
path.join(repoRoot, ".agentplane", "config.json"),
|
|
63
|
-
];
|
|
62
|
+
const requiredFiles = [path.join(repoRoot, ".agentplane", "config.json")];
|
|
64
63
|
for (const filePath of requiredFiles) {
|
|
65
64
|
if (!(await pathExists(filePath))) {
|
|
66
65
|
problems.push(`Missing required file: ${path.relative(repoRoot, filePath)}`);
|
|
67
66
|
}
|
|
68
67
|
}
|
|
68
|
+
const gateway = await resolvePolicyGatewayForRepo({
|
|
69
|
+
gitRoot: repoRoot,
|
|
70
|
+
fallbackFlavor: "codex",
|
|
71
|
+
});
|
|
72
|
+
if (!(await pathExists(gateway.absPath))) {
|
|
73
|
+
problems.push("Missing required policy gateway file: AGENTS.md or CLAUDE.md");
|
|
74
|
+
}
|
|
69
75
|
const agentsDir = path.join(repoRoot, ".agentplane", "agents");
|
|
70
76
|
if (!(await isDirectory(agentsDir))) {
|
|
71
77
|
problems.push("Missing required directory: .agentplane/agents");
|
|
@@ -160,6 +166,75 @@ async function safeFixTaskIndex(repoRoot) {
|
|
|
160
166
|
return { changed: false, note: "Skip: could not rebuild tasks index cache." };
|
|
161
167
|
}
|
|
162
168
|
}
|
|
169
|
+
async function checkWorkflowContract(repoRoot) {
|
|
170
|
+
const result = await validateWorkflowAtPath(repoRoot);
|
|
171
|
+
const findings = result.diagnostics.map((d) => `[${d.severity}] ${d.code} ${d.path}: ${d.message}`);
|
|
172
|
+
emitWorkflowEvent({
|
|
173
|
+
event: "workflow_doctor_check",
|
|
174
|
+
details: { ok: result.ok, findings: result.diagnostics.length },
|
|
175
|
+
});
|
|
176
|
+
return findings;
|
|
177
|
+
}
|
|
178
|
+
function findingSeverity(problem) {
|
|
179
|
+
const normalized = problem.trimStart();
|
|
180
|
+
if (normalized.startsWith("[WARN]"))
|
|
181
|
+
return "WARN";
|
|
182
|
+
if (normalized.startsWith("[INFO]"))
|
|
183
|
+
return "INFO";
|
|
184
|
+
if (normalized.startsWith("[ERROR]"))
|
|
185
|
+
return "ERROR";
|
|
186
|
+
return "ERROR";
|
|
187
|
+
}
|
|
188
|
+
async function safeFixWorkflow(repoRoot) {
|
|
189
|
+
const paths = resolveWorkflowPaths(repoRoot);
|
|
190
|
+
let current = "";
|
|
191
|
+
let sourcePath = paths.workflowPath;
|
|
192
|
+
try {
|
|
193
|
+
current = await fs.readFile(paths.workflowPath, "utf8");
|
|
194
|
+
}
|
|
195
|
+
catch {
|
|
196
|
+
try {
|
|
197
|
+
current = await fs.readFile(paths.legacyWorkflowPath, "utf8");
|
|
198
|
+
sourcePath = paths.legacyWorkflowPath;
|
|
199
|
+
}
|
|
200
|
+
catch {
|
|
201
|
+
return { changed: false, note: "Skip: workflow contract file not found." };
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
const fixed = safeAutofixWorkflowText(current);
|
|
205
|
+
if (fixed.diagnostics.some((d) => d.code === "WF_FIX_SKIPPED_UNSAFE")) {
|
|
206
|
+
const details = fixed.diagnostics.map((d) => `${d.path}`).join(", ");
|
|
207
|
+
return {
|
|
208
|
+
changed: false,
|
|
209
|
+
note: `Skip: unsafe workflow autofix required (unknown keys). Proposed manual review: ${details}`,
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
if (!fixed.changed) {
|
|
213
|
+
if (sourcePath === paths.workflowPath) {
|
|
214
|
+
return { changed: false, note: "OK: workflow contract already normalized." };
|
|
215
|
+
}
|
|
216
|
+
await fs.mkdir(path.dirname(paths.workflowPath), { recursive: true });
|
|
217
|
+
await fs.writeFile(paths.workflowPath, current, "utf8");
|
|
218
|
+
await fs.rm(paths.legacyWorkflowPath, { force: true });
|
|
219
|
+
await fs.mkdir(paths.workflowDir, { recursive: true });
|
|
220
|
+
await fs.copyFile(paths.workflowPath, paths.lastKnownGoodPath);
|
|
221
|
+
return {
|
|
222
|
+
changed: true,
|
|
223
|
+
note: "Fixed: moved legacy WORKFLOW.md into .agentplane and refreshed last-known-good.",
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
await fs.mkdir(path.dirname(paths.workflowPath), { recursive: true });
|
|
227
|
+
await fs.writeFile(paths.workflowPath, fixed.text, "utf8");
|
|
228
|
+
if (sourcePath === paths.legacyWorkflowPath) {
|
|
229
|
+
await fs.rm(paths.legacyWorkflowPath, { force: true });
|
|
230
|
+
}
|
|
231
|
+
await fs.mkdir(paths.workflowDir, { recursive: true });
|
|
232
|
+
await fs.copyFile(paths.workflowPath, paths.lastKnownGoodPath);
|
|
233
|
+
return {
|
|
234
|
+
changed: true,
|
|
235
|
+
note: "Fixed: normalized workflow contract and refreshed last-known-good.",
|
|
236
|
+
};
|
|
237
|
+
}
|
|
163
238
|
async function checkDoneTaskCommitInvariants(repoRoot) {
|
|
164
239
|
const tasksPath = path.join(repoRoot, ".agentplane", "tasks.json");
|
|
165
240
|
let raw = "";
|
|
@@ -216,11 +291,11 @@ async function checkDoneTaskCommitInvariants(repoRoot) {
|
|
|
216
291
|
continue;
|
|
217
292
|
const subject = subjectByHash.get(hash) ?? "";
|
|
218
293
|
if (!subject) {
|
|
219
|
-
problems.push(`DONE task references unknown commit hash: ${id} -> ${hash}`);
|
|
294
|
+
problems.push(`[WARN] DONE task references unknown commit hash: ${id} -> ${hash}`);
|
|
220
295
|
continue;
|
|
221
296
|
}
|
|
222
297
|
if (/\bclose:/iu.test(subject)) {
|
|
223
|
-
problems.push(`DONE task implementation commit points to a close commit: ${id} -> ${hash} (${subject})`);
|
|
298
|
+
problems.push(`[WARN] DONE task implementation commit points to a close commit: ${id} -> ${hash} (${subject})`);
|
|
224
299
|
}
|
|
225
300
|
}
|
|
226
301
|
return problems;
|
|
@@ -228,22 +303,38 @@ async function checkDoneTaskCommitInvariants(repoRoot) {
|
|
|
228
303
|
export const runDoctor = async (ctx, p) => {
|
|
229
304
|
const resolved = await resolveProject({ cwd: ctx.cwd, rootOverride: ctx.rootOverride ?? null });
|
|
230
305
|
const repoRoot = resolved.gitRoot;
|
|
231
|
-
const
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
return
|
|
306
|
+
const runChecks = async () => {
|
|
307
|
+
const checks = await checkWorkspace(repoRoot);
|
|
308
|
+
checks.push(...(await checkDoneTaskCommitInvariants(repoRoot)));
|
|
309
|
+
if (!isWorkflowEnforcementDisabled()) {
|
|
310
|
+
checks.push(...(await checkWorkflowContract(repoRoot)));
|
|
311
|
+
}
|
|
312
|
+
if (p.dev) {
|
|
313
|
+
checks.push(...(await checkLayering(repoRoot)));
|
|
314
|
+
}
|
|
315
|
+
return checks;
|
|
316
|
+
};
|
|
317
|
+
if (isWorkflowEnforcementDisabled()) {
|
|
318
|
+
console.log(successMessage("doctor", undefined, `workflow contract checks disabled via ${workflowEnforcementEnvHint()}.`));
|
|
241
319
|
}
|
|
242
320
|
if (p.fix) {
|
|
243
321
|
const fix = await safeFixGitignore(repoRoot);
|
|
244
322
|
console.log(successMessage("doctor fix", undefined, fix.note));
|
|
245
323
|
const idx = await safeFixTaskIndex(repoRoot);
|
|
246
324
|
console.log(successMessage("doctor fix", undefined, idx.note));
|
|
325
|
+
const workflowFix = await safeFixWorkflow(repoRoot);
|
|
326
|
+
console.log(successMessage("doctor fix", undefined, workflowFix.note));
|
|
327
|
+
}
|
|
328
|
+
const problems = await runChecks();
|
|
329
|
+
const errors = problems.filter((problem) => findingSeverity(problem) === "ERROR");
|
|
330
|
+
if (problems.length > 0) {
|
|
331
|
+
const warningCount = problems.filter((problem) => findingSeverity(problem) === "WARN").length;
|
|
332
|
+
const infoCount = problems.filter((problem) => findingSeverity(problem) === "INFO").length;
|
|
333
|
+
console.error(warnMessage(`doctor findings: errors=${errors.length} warnings=${warningCount} info=${infoCount}`));
|
|
334
|
+
for (const prob of problems)
|
|
335
|
+
console.error(`- ${prob}`);
|
|
336
|
+
if (errors.length > 0)
|
|
337
|
+
return 1;
|
|
247
338
|
}
|
|
248
339
|
console.log(successMessage("doctor", undefined, "OK"));
|
|
249
340
|
return 0;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"commands.d.ts","sourceRoot":"","sources":["../../../../src/commands/guard/impl/commands.ts"],"names":[],"mappings":"AAGA,OAAO,EAAsB,KAAK,cAAc,EAAE,MAAM,8BAA8B,CAAC;
|
|
1
|
+
{"version":3,"file":"commands.d.ts","sourceRoot":"","sources":["../../../../src/commands/guard/impl/commands.ts"],"names":[],"mappings":"AAGA,OAAO,EAAsB,KAAK,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAQvF,OAAO,EAAoB,KAAK,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAyDxE,wBAAsB,aAAa,CAAC,IAAI,EAAE;IACxC,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,OAAO,CAAC;CAChB,GAAG,OAAO,CAAC,MAAM,CAAC,CAsBlB;AAED,wBAAsB,oBAAoB,CAAC,IAAI,EAAE;IAC/C,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,OAAO,GAAG,MAAM,CAAC;CAC1B,GAAG,OAAO,CAAC,MAAM,CAAC,CAyBlB;AAED,wBAAsB,cAAc,CAAC,IAAI,EAAE,kBAAkB,GAAG,OAAO,CAAC,MAAM,CAAC,CAa9E;AAED,wBAAsB,SAAS,CAAC,IAAI,EAAE;IACpC,GAAG,CAAC,EAAE,cAAc,CAAC;IACrB,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,OAAO,CAAC;IACf,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,SAAS,EAAE,OAAO,CAAC;IACnB,UAAU,EAAE,OAAO,CAAC;IACpB,SAAS,EAAE,OAAO,CAAC;IACnB,WAAW,EAAE,OAAO,CAAC;IACrB,WAAW,EAAE,OAAO,CAAC;IACrB,UAAU,EAAE,OAAO,CAAC;IACpB,OAAO,EAAE,OAAO,CAAC;IACjB,YAAY,EAAE,OAAO,CAAC;IACtB,KAAK,EAAE,OAAO,CAAC;IACf,kBAAkB,EAAE,OAAO,CAAC;IAC5B,cAAc,EAAE,OAAO,CAAC;CACzB,GAAG,OAAO,CAAC,MAAM,CAAC,CAmJlB"}
|
|
@@ -4,6 +4,7 @@ import { CliError } from "../../../shared/errors.js";
|
|
|
4
4
|
import { loadCommandContext } from "../../shared/task-backend.js";
|
|
5
5
|
import { loadTaskFromContext } from "../../shared/task-backend.js";
|
|
6
6
|
import { execFileAsync, gitEnv } from "../../shared/git.js";
|
|
7
|
+
import { ensureReconciledBeforeMutation } from "../../shared/reconcile-check.js";
|
|
7
8
|
import { suggestAllowPrefixes } from "./allow.js";
|
|
8
9
|
import { buildCloseCommitMessage, taskReadmePathForTask } from "./close-message.js";
|
|
9
10
|
import { buildGitCommitEnv } from "./env.js";
|
|
@@ -111,7 +112,10 @@ export async function cmdGuardSuggestAllow(opts) {
|
|
|
111
112
|
}
|
|
112
113
|
export async function cmdGuardCommit(opts) {
|
|
113
114
|
try {
|
|
114
|
-
|
|
115
|
+
const ctx = opts.ctx ??
|
|
116
|
+
(await loadCommandContext({ cwd: opts.cwd, rootOverride: opts.rootOverride ?? null }));
|
|
117
|
+
await ensureReconciledBeforeMutation({ ctx, command: "guard commit" });
|
|
118
|
+
await guardCommitCheck({ ...opts, ctx });
|
|
115
119
|
if (!opts.quiet)
|
|
116
120
|
process.stdout.write("OK\n");
|
|
117
121
|
return 0;
|
|
@@ -124,9 +128,12 @@ export async function cmdGuardCommit(opts) {
|
|
|
124
128
|
}
|
|
125
129
|
export async function cmdCommit(opts) {
|
|
126
130
|
try {
|
|
131
|
+
const ctx = opts.ctx ??
|
|
132
|
+
(await loadCommandContext({ cwd: opts.cwd, rootOverride: opts.rootOverride ?? null }));
|
|
127
133
|
if (opts.close) {
|
|
128
|
-
|
|
129
|
-
|
|
134
|
+
if (!opts.closeCheckOnly) {
|
|
135
|
+
await ensureReconciledBeforeMutation({ ctx, command: "commit" });
|
|
136
|
+
}
|
|
130
137
|
// Make the close commit deterministic: start from a clean index unless --unstage-others is used.
|
|
131
138
|
let staged = await ctx.git.statusStagedPaths();
|
|
132
139
|
if (staged.length > 0 && opts.closeUnstageOthers) {
|
|
@@ -208,8 +215,9 @@ export async function cmdCommit(opts) {
|
|
|
208
215
|
message: "--auto-allow is disabled; pass explicit --allow <path-prefix>.",
|
|
209
216
|
});
|
|
210
217
|
}
|
|
218
|
+
await ensureReconciledBeforeMutation({ ctx, command: "commit" });
|
|
211
219
|
await guardCommitCheck({
|
|
212
|
-
ctx
|
|
220
|
+
ctx,
|
|
213
221
|
cwd: opts.cwd,
|
|
214
222
|
rootOverride: opts.rootOverride,
|
|
215
223
|
taskId: opts.taskId,
|
|
@@ -224,8 +232,6 @@ export async function cmdCommit(opts) {
|
|
|
224
232
|
requireClean: opts.requireClean,
|
|
225
233
|
quiet: opts.quiet,
|
|
226
234
|
});
|
|
227
|
-
const ctx = opts.ctx ??
|
|
228
|
-
(await loadCommandContext({ cwd: opts.cwd, rootOverride: opts.rootOverride ?? null }));
|
|
229
235
|
const env = buildGitCommitEnv({
|
|
230
236
|
taskId: opts.taskId,
|
|
231
237
|
allowTasks: opts.allowTasks,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"install.d.ts","sourceRoot":"","sources":["../../../../../src/commands/recipes/impl/commands/install.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"install.d.ts","sourceRoot":"","sources":["../../../../../src/commands/recipes/impl/commands/install.ts"],"names":[],"mappings":"AAkCA,OAAO,KAAK,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAM3E,wBAAsB,gBAAgB,CAAC,IAAI,EAAE;IAC3C,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,mBAAmB,CAAC;IAC5B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,OAAO,CAAC;IACjB,UAAU,EAAE,kBAAkB,CAAC;IAC/B,GAAG,EAAE,OAAO,CAAC;CACd,GAAG,OAAO,CAAC,MAAM,CAAC,CA6NlB"}
|