@brawnen/agent-harness-cli 0.1.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/LICENSE +21 -0
- package/README.md +224 -0
- package/README.zh-CN.md +232 -0
- package/bin/agent-harness.js +6 -0
- package/package.json +46 -0
- package/src/commands/audit.js +110 -0
- package/src/commands/delivery.js +497 -0
- package/src/commands/docs.js +251 -0
- package/src/commands/gate.js +236 -0
- package/src/commands/init.js +711 -0
- package/src/commands/report.js +272 -0
- package/src/commands/state.js +274 -0
- package/src/commands/status.js +493 -0
- package/src/commands/task.js +316 -0
- package/src/commands/verify.js +173 -0
- package/src/index.js +101 -0
- package/src/lib/audit-store.js +80 -0
- package/src/lib/delivery-policy.js +219 -0
- package/src/lib/output-policy.js +266 -0
- package/src/lib/project-config.js +235 -0
- package/src/lib/runtime-paths.js +46 -0
- package/src/lib/state-store.js +510 -0
- package/src/lib/task-core.js +490 -0
- package/src/lib/workflow-policy.js +307 -0
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
|
|
4
|
+
import { readAuditEntries } from "../lib/audit-store.js";
|
|
5
|
+
import { evaluateTaskDeliveryReadiness, normalizeDeliveryPolicy } from "../lib/delivery-policy.js";
|
|
6
|
+
import { normalizeOutputPolicy, validateTaskOutputArtifacts } from "../lib/output-policy.js";
|
|
7
|
+
import { loadProjectConfig } from "../lib/project-config.js";
|
|
8
|
+
import { requireTaskState, resolveTaskId, updateTaskState } from "../lib/state-store.js";
|
|
9
|
+
import { buildWorkflowWarning, evaluateTaskWorkflowDecision, normalizeWorkflowPolicy } from "../lib/workflow-policy.js";
|
|
10
|
+
import { verifyTaskState } from "./verify.js";
|
|
11
|
+
|
|
12
|
+
const SCHEMA_VERSION = "0.3";
|
|
13
|
+
|
|
14
|
+
export function runReport(argv) {
|
|
15
|
+
const parsed = parseReportArgs(argv);
|
|
16
|
+
if (!parsed.ok) {
|
|
17
|
+
console.error(parsed.error);
|
|
18
|
+
return 1;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
try {
|
|
22
|
+
const cwd = process.cwd();
|
|
23
|
+
const taskId = resolveTaskId(cwd, parsed.options.taskId);
|
|
24
|
+
const taskState = requireTaskState(cwd, taskId);
|
|
25
|
+
const projectConfig = loadProjectConfig(cwd);
|
|
26
|
+
const outputPolicy = normalizeOutputPolicy(projectConfig?.output_policy);
|
|
27
|
+
const reportPolicy = outputPolicy.report;
|
|
28
|
+
const verification = verifyTaskState(taskState, { reportPolicy });
|
|
29
|
+
|
|
30
|
+
if (!verification.allowed) {
|
|
31
|
+
console.log(`${JSON.stringify(verification, null, 2)}\n`);
|
|
32
|
+
return 1;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
let outputArtifacts;
|
|
36
|
+
try {
|
|
37
|
+
outputArtifacts = validateTaskOutputArtifacts(cwd, taskState, outputPolicy, {
|
|
38
|
+
adr: parsed.options.adr,
|
|
39
|
+
changelog: parsed.options.changelogFile,
|
|
40
|
+
design_note: parsed.options.designNote
|
|
41
|
+
});
|
|
42
|
+
} catch (error) {
|
|
43
|
+
if (error?.code === "MISSING_OUTPUT_ARTIFACTS") {
|
|
44
|
+
console.error(buildMissingArtifactsMessage(taskId, error, outputPolicy));
|
|
45
|
+
return 1;
|
|
46
|
+
}
|
|
47
|
+
throw error;
|
|
48
|
+
}
|
|
49
|
+
const deliveryReadiness = evaluateTaskDeliveryReadiness(cwd, taskState, {
|
|
50
|
+
deliveryPolicy: normalizeDeliveryPolicy(projectConfig?.delivery_policy),
|
|
51
|
+
reportPolicy,
|
|
52
|
+
reportWillBeGenerated: true
|
|
53
|
+
});
|
|
54
|
+
const workflowDecision = evaluateTaskWorkflowDecision(taskState, {
|
|
55
|
+
workflowPolicy: normalizeWorkflowPolicy(projectConfig?.workflow_policy),
|
|
56
|
+
outputPolicy,
|
|
57
|
+
actualScope: parsed.options.actualScope,
|
|
58
|
+
outputArtifacts,
|
|
59
|
+
previousDecision: taskState.workflow_decision
|
|
60
|
+
});
|
|
61
|
+
const workflowWarning = buildWorkflowWarning(workflowDecision);
|
|
62
|
+
const report = buildReport(cwd, taskState, parsed.options, outputArtifacts, deliveryReadiness, workflowDecision, workflowWarning);
|
|
63
|
+
validateReportAgainstPolicy(report, reportPolicy);
|
|
64
|
+
writeReport(cwd, report, reportPolicy);
|
|
65
|
+
|
|
66
|
+
updateTaskState(cwd, taskId, {
|
|
67
|
+
current_phase: "close",
|
|
68
|
+
current_state: "done",
|
|
69
|
+
workflow_decision: workflowDecision
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
if (workflowWarning) {
|
|
73
|
+
console.error(`workflow warning: ${workflowWarning}`);
|
|
74
|
+
}
|
|
75
|
+
console.log(`${JSON.stringify(report, null, 2)}\n`);
|
|
76
|
+
return 0;
|
|
77
|
+
} catch (error) {
|
|
78
|
+
console.error(error.message);
|
|
79
|
+
return 1;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function parseReportArgs(argv) {
|
|
84
|
+
const options = {
|
|
85
|
+
adr: null,
|
|
86
|
+
actualScope: [],
|
|
87
|
+
changelogFile: null,
|
|
88
|
+
conclusion: null,
|
|
89
|
+
designNote: null,
|
|
90
|
+
nextSteps: [],
|
|
91
|
+
remainingRisks: [],
|
|
92
|
+
scopeDeviation: null,
|
|
93
|
+
taskId: null
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
97
|
+
const arg = argv[index];
|
|
98
|
+
|
|
99
|
+
if (arg === "--task-id") {
|
|
100
|
+
options.taskId = argv[index + 1] ?? null;
|
|
101
|
+
index += 1;
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (arg === "--conclusion") {
|
|
106
|
+
options.conclusion = argv[index + 1] ?? null;
|
|
107
|
+
index += 1;
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if (arg === "--changelog-file") {
|
|
112
|
+
options.changelogFile = argv[index + 1] ?? null;
|
|
113
|
+
index += 1;
|
|
114
|
+
continue;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
if (arg === "--design-note") {
|
|
118
|
+
options.designNote = argv[index + 1] ?? null;
|
|
119
|
+
index += 1;
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
if (arg === "--adr") {
|
|
124
|
+
options.adr = argv[index + 1] ?? null;
|
|
125
|
+
index += 1;
|
|
126
|
+
continue;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
if (arg === "--actual-scope") {
|
|
130
|
+
options.actualScope.push(argv[index + 1] ?? "");
|
|
131
|
+
index += 1;
|
|
132
|
+
continue;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
if (arg === "--scope-deviation") {
|
|
136
|
+
options.scopeDeviation = argv[index + 1] ?? null;
|
|
137
|
+
index += 1;
|
|
138
|
+
continue;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
if (arg === "--risk") {
|
|
142
|
+
options.remainingRisks.push(argv[index + 1] ?? "");
|
|
143
|
+
index += 1;
|
|
144
|
+
continue;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
if (arg === "--next-step") {
|
|
148
|
+
options.nextSteps.push(argv[index + 1] ?? "");
|
|
149
|
+
index += 1;
|
|
150
|
+
continue;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
return { ok: false, error: `未知参数: ${arg}` };
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
if (!options.conclusion) {
|
|
157
|
+
return { ok: false, error: "需要 --conclusion 参数" };
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
return { ok: true, options };
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function buildReport(cwd, taskState, options, outputArtifacts, deliveryReadiness, workflowDecision, workflowWarning) {
|
|
164
|
+
const contract = taskState.confirmed_contract ?? {};
|
|
165
|
+
const draft = taskState.task_draft ?? {};
|
|
166
|
+
const evidence = Array.isArray(taskState.evidence) ? taskState.evidence : [];
|
|
167
|
+
const auditEntries = readAuditEntries(cwd, taskState.task_id);
|
|
168
|
+
|
|
169
|
+
return {
|
|
170
|
+
schema_version: SCHEMA_VERSION,
|
|
171
|
+
task_id: taskState.task_id,
|
|
172
|
+
intent: contract.intent ?? draft.intent ?? "unknown",
|
|
173
|
+
conclusion: options.conclusion,
|
|
174
|
+
actual_scope: options.actualScope.length > 0 ? options.actualScope : (contract.scope ?? draft.scope ?? []),
|
|
175
|
+
scope_deviation: options.scopeDeviation ?? null,
|
|
176
|
+
evidence_summary: evidence.map((item) => {
|
|
177
|
+
const summary = {
|
|
178
|
+
type: item.type,
|
|
179
|
+
result: item.content
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
if (typeof item.passed === "boolean") {
|
|
183
|
+
summary.passed = item.passed;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
return summary;
|
|
187
|
+
}),
|
|
188
|
+
remaining_risks: options.remainingRisks,
|
|
189
|
+
overrides_used: auditEntries
|
|
190
|
+
.filter((entry) => entry.event_type === "force_override" || entry.event_type === "manual_confirmation")
|
|
191
|
+
.map((entry) => entry.description),
|
|
192
|
+
output_artifacts: outputArtifacts,
|
|
193
|
+
delivery_readiness: deliveryReadiness,
|
|
194
|
+
workflow_decision: workflowDecision,
|
|
195
|
+
workflow_warning: workflowWarning,
|
|
196
|
+
next_steps: options.nextSteps,
|
|
197
|
+
completed_at: new Date().toISOString()
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function validateReportAgainstPolicy(report, reportPolicy) {
|
|
202
|
+
if (reportPolicy.format !== "json") {
|
|
203
|
+
throw new Error(`当前仅支持 json 报告格式,收到: ${reportPolicy.format}`);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
const missingSections = [];
|
|
207
|
+
for (const section of reportPolicy.required_sections) {
|
|
208
|
+
if (!isReportSectionSatisfied(report, section)) {
|
|
209
|
+
missingSections.push(section);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
if (missingSections.length > 0) {
|
|
214
|
+
throw new Error(`报告缺少必需 section: ${missingSections.join(", ")}`);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
function isReportSectionSatisfied(report, section) {
|
|
219
|
+
if (section === "task_conclusion") {
|
|
220
|
+
return typeof report.conclusion === "string" && report.conclusion.trim().length > 0;
|
|
221
|
+
}
|
|
222
|
+
if (section === "actual_scope") {
|
|
223
|
+
return Array.isArray(report.actual_scope) && report.actual_scope.length > 0;
|
|
224
|
+
}
|
|
225
|
+
if (section === "verification_evidence") {
|
|
226
|
+
return Array.isArray(report.evidence_summary) && report.evidence_summary.length > 0;
|
|
227
|
+
}
|
|
228
|
+
if (section === "remaining_risks") {
|
|
229
|
+
return Array.isArray(report.remaining_risks);
|
|
230
|
+
}
|
|
231
|
+
if (section === "next_steps") {
|
|
232
|
+
return Array.isArray(report.next_steps);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
throw new Error(`未知的 output_policy.report.required_sections 配置项: ${section}`);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
function writeReport(cwd, report, reportPolicy) {
|
|
239
|
+
const reportsDir = path.join(cwd, reportPolicy.directory);
|
|
240
|
+
fs.mkdirSync(reportsDir, { recursive: true });
|
|
241
|
+
const reportPath = path.join(reportsDir, `${report.task_id}.json`);
|
|
242
|
+
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`, "utf8");
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
function buildMissingArtifactsMessage(taskId, error, outputPolicy) {
|
|
246
|
+
const missing = Array.isArray(error?.missing_required) ? error.missing_required : [];
|
|
247
|
+
const lines = [
|
|
248
|
+
`缺少必需输出工件: ${missing.join(", ")}`
|
|
249
|
+
];
|
|
250
|
+
|
|
251
|
+
for (const artifact of missing) {
|
|
252
|
+
if (artifact === "changelog") {
|
|
253
|
+
lines.push(`- changelog: 请更新 ${outputPolicy.changelog.file},然后重新执行 report`);
|
|
254
|
+
continue;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
if (artifact === "design_note") {
|
|
258
|
+
const suggestedPath = path.posix.join(outputPolicy.design_note.directory, `${taskId}-design-note.md`);
|
|
259
|
+
lines.push(`- design_note: 可先执行 \`node packages/cli/bin/agent-harness.js docs scaffold --type design-note --task-id ${taskId} --path ${suggestedPath}\``);
|
|
260
|
+
lines.push(` 然后在 report 中补上 \`--design-note ${suggestedPath}\``);
|
|
261
|
+
continue;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
if (artifact === "adr") {
|
|
265
|
+
const suggestedPath = path.posix.join(outputPolicy.adr.directory, `${taskId}-adr.md`);
|
|
266
|
+
lines.push(`- adr: 可先执行 \`node packages/cli/bin/agent-harness.js docs scaffold --type adr --task-id ${taskId} --path ${suggestedPath}\``);
|
|
267
|
+
lines.push(` 然后在 report 中补上 \`--adr ${suggestedPath}\``);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
return `${lines.join("\n")}\n`;
|
|
272
|
+
}
|
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
getActiveTask,
|
|
5
|
+
getTaskState,
|
|
6
|
+
initTaskState,
|
|
7
|
+
loadStateIndex,
|
|
8
|
+
resolveTaskId,
|
|
9
|
+
updateTaskState
|
|
10
|
+
} from "../lib/state-store.js";
|
|
11
|
+
|
|
12
|
+
export function runState(argv) {
|
|
13
|
+
const [subcommand, ...rest] = argv;
|
|
14
|
+
|
|
15
|
+
if (!subcommand) {
|
|
16
|
+
console.error("缺少 state 子命令。可用: init, get, update, active");
|
|
17
|
+
return 1;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
if (subcommand === "init") {
|
|
21
|
+
return runStateInit(rest);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
if (subcommand === "get") {
|
|
25
|
+
return runStateGet(rest);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (subcommand === "update") {
|
|
29
|
+
return runStateUpdate(rest);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (subcommand === "active") {
|
|
33
|
+
return runStateActive(rest);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
console.error(`未知 state 子命令: ${subcommand}。可用: init, get, update, active`);
|
|
37
|
+
return 1;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function runStateInit(argv) {
|
|
41
|
+
const parsed = parseStateInitArgs(argv);
|
|
42
|
+
if (!parsed.ok) {
|
|
43
|
+
console.error(parsed.error);
|
|
44
|
+
return 1;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
try {
|
|
48
|
+
const taskDraft = loadDraft(parsed.options);
|
|
49
|
+
const result = initTaskState(process.cwd(), {
|
|
50
|
+
taskDraft,
|
|
51
|
+
taskId: parsed.options.taskId
|
|
52
|
+
});
|
|
53
|
+
printJson(result);
|
|
54
|
+
return 0;
|
|
55
|
+
} catch (error) {
|
|
56
|
+
console.error(error.message);
|
|
57
|
+
return 1;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function runStateGet(argv) {
|
|
62
|
+
const parsed = parseTaskIdArgs(argv);
|
|
63
|
+
if (!parsed.ok) {
|
|
64
|
+
console.error(parsed.error);
|
|
65
|
+
return 1;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
try {
|
|
69
|
+
const taskId = resolveTaskId(process.cwd(), parsed.options.taskId);
|
|
70
|
+
const result = getTaskState(process.cwd(), taskId);
|
|
71
|
+
if (!result) {
|
|
72
|
+
console.error(`任务不存在: ${taskId}`);
|
|
73
|
+
return 1;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
printJson(result);
|
|
77
|
+
return 0;
|
|
78
|
+
} catch (error) {
|
|
79
|
+
console.error(error.message);
|
|
80
|
+
return 1;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function runStateUpdate(argv) {
|
|
85
|
+
const parsed = parseStateUpdateArgs(argv);
|
|
86
|
+
if (!parsed.ok) {
|
|
87
|
+
console.error(parsed.error);
|
|
88
|
+
return 1;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
try {
|
|
92
|
+
const taskId = resolveTaskId(process.cwd(), parsed.options.taskId);
|
|
93
|
+
const changes = {};
|
|
94
|
+
|
|
95
|
+
if (parsed.options.phase) {
|
|
96
|
+
changes.current_phase = parsed.options.phase;
|
|
97
|
+
}
|
|
98
|
+
if (parsed.options.state) {
|
|
99
|
+
changes.current_state = parsed.options.state;
|
|
100
|
+
}
|
|
101
|
+
if (parsed.options.evidence) {
|
|
102
|
+
changes.evidence = [JSON.parse(parsed.options.evidence)];
|
|
103
|
+
} else if (parsed.options.tool) {
|
|
104
|
+
changes.evidence = [{
|
|
105
|
+
type: "command_result",
|
|
106
|
+
content: `Tool: ${parsed.options.tool}`,
|
|
107
|
+
exit_code: parsed.options.exitCode ?? 0,
|
|
108
|
+
timestamp: new Date().toISOString()
|
|
109
|
+
}];
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const result = updateTaskState(process.cwd(), taskId, changes);
|
|
113
|
+
printJson(result);
|
|
114
|
+
return 0;
|
|
115
|
+
} catch (error) {
|
|
116
|
+
console.error(error.message);
|
|
117
|
+
return 1;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function runStateActive(argv) {
|
|
122
|
+
if (argv.length > 0) {
|
|
123
|
+
console.error(`state active 不接受额外参数: ${argv.join(" ")}`);
|
|
124
|
+
return 1;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
try {
|
|
128
|
+
const result = getActiveTask(process.cwd());
|
|
129
|
+
if (!result) {
|
|
130
|
+
const index = loadStateIndex(process.cwd());
|
|
131
|
+
if (!index.active_task_id) {
|
|
132
|
+
console.error("当前无活跃任务");
|
|
133
|
+
return 1;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
printJson(result);
|
|
138
|
+
return 0;
|
|
139
|
+
} catch (error) {
|
|
140
|
+
console.error(error.message);
|
|
141
|
+
return 1;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function parseStateInitArgs(argv) {
|
|
146
|
+
const options = {
|
|
147
|
+
draft: null,
|
|
148
|
+
draftFile: null,
|
|
149
|
+
taskId: null
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
153
|
+
const arg = argv[index];
|
|
154
|
+
|
|
155
|
+
if (arg === "--draft") {
|
|
156
|
+
options.draft = argv[index + 1] ?? null;
|
|
157
|
+
index += 1;
|
|
158
|
+
continue;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
if (arg === "--draft-file") {
|
|
162
|
+
options.draftFile = argv[index + 1] ?? null;
|
|
163
|
+
index += 1;
|
|
164
|
+
continue;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
if (arg === "--task-id") {
|
|
168
|
+
options.taskId = argv[index + 1] ?? null;
|
|
169
|
+
index += 1;
|
|
170
|
+
continue;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
return { ok: false, error: `未知参数: ${arg}` };
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
if (!options.draft && !options.draftFile) {
|
|
177
|
+
return { ok: false, error: "需要 --draft 或 --draft-file 参数" };
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
if (options.draft && options.draftFile) {
|
|
181
|
+
return { ok: false, error: "--draft 与 --draft-file 不能同时使用" };
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
return { ok: true, options };
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
function parseTaskIdArgs(argv) {
|
|
188
|
+
const options = { taskId: null };
|
|
189
|
+
|
|
190
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
191
|
+
const arg = argv[index];
|
|
192
|
+
if (arg === "--task-id") {
|
|
193
|
+
options.taskId = argv[index + 1] ?? null;
|
|
194
|
+
index += 1;
|
|
195
|
+
continue;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
return { ok: false, error: `未知参数: ${arg}` };
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
return { ok: true, options };
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
function parseStateUpdateArgs(argv) {
|
|
205
|
+
const options = {
|
|
206
|
+
evidence: null,
|
|
207
|
+
exitCode: null,
|
|
208
|
+
phase: null,
|
|
209
|
+
state: null,
|
|
210
|
+
taskId: null,
|
|
211
|
+
tool: null
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
215
|
+
const arg = argv[index];
|
|
216
|
+
|
|
217
|
+
if (arg === "--task-id") {
|
|
218
|
+
options.taskId = argv[index + 1] ?? null;
|
|
219
|
+
index += 1;
|
|
220
|
+
continue;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
if (arg === "--tool") {
|
|
224
|
+
options.tool = argv[index + 1] ?? null;
|
|
225
|
+
index += 1;
|
|
226
|
+
continue;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
if (arg === "--exit-code") {
|
|
230
|
+
const value = argv[index + 1];
|
|
231
|
+
options.exitCode = value == null ? null : Number(value);
|
|
232
|
+
index += 1;
|
|
233
|
+
continue;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
if (arg === "--phase") {
|
|
237
|
+
options.phase = argv[index + 1] ?? null;
|
|
238
|
+
index += 1;
|
|
239
|
+
continue;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
if (arg === "--state") {
|
|
243
|
+
options.state = argv[index + 1] ?? null;
|
|
244
|
+
index += 1;
|
|
245
|
+
continue;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
if (arg === "--evidence") {
|
|
249
|
+
options.evidence = argv[index + 1] ?? null;
|
|
250
|
+
index += 1;
|
|
251
|
+
continue;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
return { ok: false, error: `未知参数: ${arg}` };
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
return { ok: true, options };
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
function loadDraft(options) {
|
|
261
|
+
if (options.draft) {
|
|
262
|
+
return JSON.parse(options.draft);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
try {
|
|
266
|
+
return JSON.parse(fs.readFileSync(options.draftFile, "utf8"));
|
|
267
|
+
} catch {
|
|
268
|
+
throw new Error(`无法读取 task draft: ${options.draftFile}`);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
function printJson(value) {
|
|
273
|
+
console.log(`${JSON.stringify(value, null, 2)}\n`);
|
|
274
|
+
}
|