agentplane 0.1.6 → 0.1.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/assets/AGENTS.md +1 -1
- package/assets/agents/ORCHESTRATOR.json +1 -1
- package/assets/agents/UPGRADER.json +1 -1
- package/dist/backends/task-backend.d.ts +16 -0
- package/dist/backends/task-backend.d.ts.map +1 -1
- package/dist/backends/task-backend.js +44 -0
- package/dist/backends/task-index.d.ts.map +1 -1
- package/dist/backends/task-index.js +3 -6
- package/dist/cli/command-guide.d.ts.map +1 -1
- package/dist/cli/command-guide.js +4 -4
- package/dist/cli/help.d.ts.map +1 -1
- package/dist/cli/help.js +7 -5
- package/dist/cli/run-cli.d.ts.map +1 -1
- package/dist/cli/run-cli.js +39 -78
- package/dist/commands/backend.d.ts.map +1 -1
- package/dist/commands/backend.js +17 -2
- package/dist/commands/branch/index.d.ts +60 -0
- package/dist/commands/branch/index.d.ts.map +1 -0
- package/dist/commands/branch/index.js +513 -0
- package/dist/commands/guard/index.d.ts +67 -0
- package/dist/commands/guard/index.d.ts.map +1 -0
- package/dist/commands/guard/index.js +367 -0
- package/dist/commands/hooks/index.d.ts +18 -0
- package/dist/commands/hooks/index.d.ts.map +1 -0
- package/dist/commands/hooks/index.js +290 -0
- package/dist/commands/pr/index.d.ts +46 -0
- package/dist/commands/pr/index.d.ts.map +1 -0
- package/dist/commands/pr/index.js +857 -0
- package/dist/commands/recipes.d.ts.map +1 -1
- package/dist/commands/recipes.js +67 -23
- package/dist/commands/shared/git-diff.d.ts +9 -0
- package/dist/commands/shared/git-diff.d.ts.map +1 -0
- package/dist/commands/shared/git-diff.js +41 -0
- package/dist/commands/shared/git-ops.d.ts +24 -0
- package/dist/commands/shared/git-ops.d.ts.map +1 -0
- package/dist/commands/shared/git-ops.js +181 -0
- package/dist/commands/shared/git-worktree.d.ts +8 -0
- package/dist/commands/shared/git-worktree.d.ts.map +1 -0
- package/dist/commands/shared/git-worktree.js +48 -0
- package/dist/commands/shared/git.d.ts +4 -0
- package/dist/commands/shared/git.d.ts.map +1 -0
- package/dist/commands/shared/git.js +14 -0
- package/dist/commands/shared/network-approval.d.ts +8 -0
- package/dist/commands/shared/network-approval.d.ts.map +1 -0
- package/dist/commands/shared/network-approval.js +25 -0
- package/dist/commands/shared/path.d.ts +3 -0
- package/dist/commands/shared/path.d.ts.map +1 -0
- package/dist/commands/shared/path.js +14 -0
- package/dist/commands/shared/pr-meta.d.ts +21 -0
- package/dist/commands/shared/pr-meta.d.ts.map +1 -0
- package/dist/commands/shared/pr-meta.js +72 -0
- package/dist/commands/shared/task-backend.d.ts +15 -0
- package/dist/commands/shared/task-backend.d.ts.map +1 -0
- package/dist/commands/shared/task-backend.js +61 -0
- package/dist/commands/task/add.d.ts +8 -0
- package/dist/commands/task/add.d.ts.map +1 -0
- package/dist/commands/task/add.js +164 -0
- package/dist/commands/task/block.d.ts +19 -0
- package/dist/commands/task/block.d.ts.map +1 -0
- package/dist/commands/task/block.js +86 -0
- package/dist/commands/task/comment.d.ts +8 -0
- package/dist/commands/task/comment.d.ts.map +1 -0
- package/dist/commands/task/comment.js +29 -0
- package/dist/commands/task/doc.d.ts +17 -0
- package/dist/commands/task/doc.d.ts.map +1 -0
- package/dist/commands/task/doc.js +220 -0
- package/dist/commands/task/export.d.ts +5 -0
- package/dist/commands/task/export.d.ts.map +1 -0
- package/dist/commands/task/export.js +27 -0
- package/dist/commands/task/finish.d.ts +27 -0
- package/dist/commands/task/finish.d.ts.map +1 -0
- package/dist/commands/task/finish.js +132 -0
- package/dist/commands/task/index.d.ts +26 -0
- package/dist/commands/task/index.d.ts.map +1 -0
- package/dist/commands/task/index.js +25 -0
- package/dist/commands/task/lint.d.ts +5 -0
- package/dist/commands/task/lint.d.ts.map +1 -0
- package/dist/commands/task/lint.js +22 -0
- package/dist/commands/task/list.d.ts +11 -0
- package/dist/commands/task/list.d.ts.map +1 -0
- package/dist/commands/task/list.js +54 -0
- package/dist/commands/task/migrate-doc.d.ts +8 -0
- package/dist/commands/task/migrate-doc.d.ts.map +1 -0
- package/dist/commands/task/migrate-doc.js +147 -0
- package/dist/commands/task/migrate.d.ts +6 -0
- package/dist/commands/task/migrate.d.ts.map +1 -0
- package/dist/commands/task/migrate.js +70 -0
- package/dist/commands/task/new.d.ts +8 -0
- package/dist/commands/task/new.d.ts.map +1 -0
- package/dist/commands/task/new.js +117 -0
- package/dist/commands/task/next.d.ts +6 -0
- package/dist/commands/task/next.d.ts.map +1 -0
- package/dist/commands/task/next.js +45 -0
- package/dist/commands/task/normalize.d.ts +6 -0
- package/dist/commands/task/normalize.d.ts.map +1 -0
- package/dist/commands/task/normalize.js +46 -0
- package/dist/commands/task/plan.d.ts +14 -0
- package/dist/commands/task/plan.d.ts.map +1 -0
- package/dist/commands/task/plan.js +217 -0
- package/dist/commands/task/ready.d.ts +6 -0
- package/dist/commands/task/ready.d.ts.map +1 -0
- package/dist/commands/task/ready.js +57 -0
- package/dist/commands/task/scaffold.d.ts +8 -0
- package/dist/commands/task/scaffold.d.ts.map +1 -0
- package/dist/commands/task/scaffold.js +142 -0
- package/dist/commands/task/scrub.d.ts +8 -0
- package/dist/commands/task/scrub.d.ts.map +1 -0
- package/dist/commands/task/scrub.js +121 -0
- package/dist/commands/task/search.d.ts +7 -0
- package/dist/commands/task/search.d.ts.map +1 -0
- package/dist/commands/task/search.js +79 -0
- package/dist/commands/task/set-status.d.ts +19 -0
- package/dist/commands/task/set-status.d.ts.map +1 -0
- package/dist/commands/task/set-status.js +123 -0
- package/dist/commands/task/shared.d.ts +48 -0
- package/dist/commands/task/shared.d.ts.map +1 -0
- package/dist/commands/task/shared.js +312 -0
- package/dist/commands/task/show.d.ts +6 -0
- package/dist/commands/task/show.d.ts.map +1 -0
- package/dist/commands/task/show.js +35 -0
- package/dist/commands/task/start.d.ts +19 -0
- package/dist/commands/task/start.d.ts.map +1 -0
- package/dist/commands/task/start.js +110 -0
- package/dist/commands/task/update.d.ts +8 -0
- package/dist/commands/task/update.d.ts.map +1 -0
- package/dist/commands/task/update.js +144 -0
- package/dist/commands/task/verify-record.d.ts +16 -0
- package/dist/commands/task/verify-record.d.ts.map +1 -0
- package/dist/commands/task/verify-record.js +277 -0
- package/dist/commands/task/verify.d.ts +2 -0
- package/dist/commands/task/verify.d.ts.map +1 -0
- package/dist/commands/task/verify.js +1 -0
- package/dist/commands/upgrade.d.ts.map +1 -1
- package/dist/commands/upgrade.js +17 -2
- package/dist/commands/workflow.d.ts +5 -364
- package/dist/commands/workflow.d.ts.map +1 -1
- package/dist/commands/workflow.js +6 -4617
- package/package.json +2 -2
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { ensureDocSections, setMarkdownSection } from "@agentplaneorg/core";
|
|
4
|
+
import { mapBackendError, mapCoreError } from "../../cli/error-map.js";
|
|
5
|
+
import { backendNotSupportedMessage, missingValueMessage, usageMessage } from "../../cli/output.js";
|
|
6
|
+
import { CliError } from "../../shared/errors.js";
|
|
7
|
+
import { loadBackendTask } from "../shared/task-backend.js";
|
|
8
|
+
import { nowIso } from "./shared.js";
|
|
9
|
+
export const TASK_PLAN_USAGE = "Usage: agentplane task plan <set|approve|reject> <task-id> [flags]";
|
|
10
|
+
export const TASK_PLAN_USAGE_EXAMPLE = 'agentplane task plan set 202602030608-F1Q8AB --text "..."';
|
|
11
|
+
export const TASK_PLAN_SET_USAGE = "Usage: agentplane task plan set <task-id> (--text <text> | --file <path>) [--updated-by <id>]";
|
|
12
|
+
export const TASK_PLAN_SET_USAGE_EXAMPLE = String.raw `agentplane task plan set 202602030608-F1Q8AB --text "1) ...\n2) ..." --updated-by ORCHESTRATOR`;
|
|
13
|
+
export const TASK_PLAN_APPROVE_USAGE = "Usage: agentplane task plan approve <task-id> --by <id> [--note <text>]";
|
|
14
|
+
export const TASK_PLAN_APPROVE_USAGE_EXAMPLE = 'agentplane task plan approve 202602030608-F1Q8AB --by USER --note "OK"';
|
|
15
|
+
export const TASK_PLAN_REJECT_USAGE = "Usage: agentplane task plan reject <task-id> --by <id> --note <text>";
|
|
16
|
+
export const TASK_PLAN_REJECT_USAGE_EXAMPLE = 'agentplane task plan reject 202602030608-F1Q8AB --by USER --note "Need clarification"';
|
|
17
|
+
function extractDocSection(doc, sectionName) {
|
|
18
|
+
const lines = doc.replaceAll("\r\n", "\n").split("\n");
|
|
19
|
+
let capturing = false;
|
|
20
|
+
const out = [];
|
|
21
|
+
for (const line of lines) {
|
|
22
|
+
const match = /^##\s+(.*)$/.exec(line.trim());
|
|
23
|
+
if (match) {
|
|
24
|
+
if (capturing)
|
|
25
|
+
break;
|
|
26
|
+
capturing = (match[1] ?? "").trim() === sectionName;
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
29
|
+
if (capturing)
|
|
30
|
+
out.push(line);
|
|
31
|
+
}
|
|
32
|
+
if (!capturing)
|
|
33
|
+
return null;
|
|
34
|
+
return out.join("\n").trimEnd();
|
|
35
|
+
}
|
|
36
|
+
function parsePlanSetFlags(args) {
|
|
37
|
+
const out = {};
|
|
38
|
+
for (let i = 0; i < args.length; i++) {
|
|
39
|
+
const arg = args[i];
|
|
40
|
+
if (!arg)
|
|
41
|
+
continue;
|
|
42
|
+
if (!arg.startsWith("--")) {
|
|
43
|
+
throw new CliError({ exitCode: 2, code: "E_USAGE", message: `Unexpected argument: ${arg}` });
|
|
44
|
+
}
|
|
45
|
+
const next = args[i + 1];
|
|
46
|
+
if (!next) {
|
|
47
|
+
throw new CliError({ exitCode: 2, code: "E_USAGE", message: missingValueMessage(arg) });
|
|
48
|
+
}
|
|
49
|
+
switch (arg) {
|
|
50
|
+
case "--text": {
|
|
51
|
+
out.text = next;
|
|
52
|
+
break;
|
|
53
|
+
}
|
|
54
|
+
case "--file": {
|
|
55
|
+
out.file = next;
|
|
56
|
+
break;
|
|
57
|
+
}
|
|
58
|
+
case "--updated-by": {
|
|
59
|
+
out.updatedBy = next;
|
|
60
|
+
break;
|
|
61
|
+
}
|
|
62
|
+
default: {
|
|
63
|
+
throw new CliError({ exitCode: 2, code: "E_USAGE", message: `Unknown flag: ${arg}` });
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
i++;
|
|
67
|
+
}
|
|
68
|
+
return out;
|
|
69
|
+
}
|
|
70
|
+
function parsePlanDecisionFlags(args) {
|
|
71
|
+
const out = {};
|
|
72
|
+
for (let i = 0; i < args.length; i++) {
|
|
73
|
+
const arg = args[i];
|
|
74
|
+
if (!arg)
|
|
75
|
+
continue;
|
|
76
|
+
if (!arg.startsWith("--")) {
|
|
77
|
+
throw new CliError({ exitCode: 2, code: "E_USAGE", message: `Unexpected argument: ${arg}` });
|
|
78
|
+
}
|
|
79
|
+
const next = args[i + 1];
|
|
80
|
+
if (!next) {
|
|
81
|
+
throw new CliError({ exitCode: 2, code: "E_USAGE", message: missingValueMessage(arg) });
|
|
82
|
+
}
|
|
83
|
+
switch (arg) {
|
|
84
|
+
case "--by": {
|
|
85
|
+
out.by = next;
|
|
86
|
+
break;
|
|
87
|
+
}
|
|
88
|
+
case "--note": {
|
|
89
|
+
out.note = next;
|
|
90
|
+
break;
|
|
91
|
+
}
|
|
92
|
+
default: {
|
|
93
|
+
throw new CliError({ exitCode: 2, code: "E_USAGE", message: `Unknown flag: ${arg}` });
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
i++;
|
|
97
|
+
}
|
|
98
|
+
return out;
|
|
99
|
+
}
|
|
100
|
+
export async function cmdTaskPlan(opts) {
|
|
101
|
+
const [subcommand, taskId, ...restArgs] = opts.args;
|
|
102
|
+
if (!subcommand || !taskId) {
|
|
103
|
+
throw new CliError({
|
|
104
|
+
exitCode: 2,
|
|
105
|
+
code: "E_USAGE",
|
|
106
|
+
message: usageMessage(TASK_PLAN_USAGE, TASK_PLAN_USAGE_EXAMPLE),
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
try {
|
|
110
|
+
const { backend, config, resolved, task } = await loadBackendTask({
|
|
111
|
+
cwd: opts.cwd,
|
|
112
|
+
rootOverride: opts.rootOverride,
|
|
113
|
+
taskId,
|
|
114
|
+
});
|
|
115
|
+
if (!backend.getTaskDoc || !backend.setTaskDoc || !backend.writeTask) {
|
|
116
|
+
throw new CliError({
|
|
117
|
+
exitCode: 2,
|
|
118
|
+
code: "E_USAGE",
|
|
119
|
+
message: backendNotSupportedMessage("task docs"),
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
if (subcommand === "set") {
|
|
123
|
+
const flags = parsePlanSetFlags(restArgs);
|
|
124
|
+
const hasText = flags.text !== undefined;
|
|
125
|
+
const hasFile = flags.file !== undefined;
|
|
126
|
+
if (hasText === hasFile) {
|
|
127
|
+
throw new CliError({
|
|
128
|
+
exitCode: 2,
|
|
129
|
+
code: "E_USAGE",
|
|
130
|
+
message: usageMessage(TASK_PLAN_SET_USAGE, TASK_PLAN_SET_USAGE_EXAMPLE),
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
let updatedBy;
|
|
134
|
+
if (flags.updatedBy !== undefined) {
|
|
135
|
+
const trimmed = flags.updatedBy.trim();
|
|
136
|
+
if (!trimmed) {
|
|
137
|
+
throw new CliError({
|
|
138
|
+
exitCode: 2,
|
|
139
|
+
code: "E_USAGE",
|
|
140
|
+
message: "--updated-by must be non-empty",
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
updatedBy = trimmed;
|
|
144
|
+
}
|
|
145
|
+
let text = flags.text ?? "";
|
|
146
|
+
if (hasFile) {
|
|
147
|
+
try {
|
|
148
|
+
text = await readFile(path.resolve(opts.cwd, flags.file ?? ""), "utf8");
|
|
149
|
+
}
|
|
150
|
+
catch (err) {
|
|
151
|
+
throw mapCoreError(err, { command: "task plan set", filePath: flags.file ?? "" });
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
const existingDoc = (typeof task.doc === "string" ? task.doc : "") || (await backend.getTaskDoc(task.id));
|
|
155
|
+
const baseDoc = ensureDocSections(existingDoc ?? "", config.tasks.doc.required_sections);
|
|
156
|
+
const nextDoc = ensureDocSections(setMarkdownSection(baseDoc, "Plan", text), config.tasks.doc.required_sections);
|
|
157
|
+
await backend.writeTask({
|
|
158
|
+
...task,
|
|
159
|
+
doc: nextDoc,
|
|
160
|
+
plan_approval: { state: "pending", updated_at: null, updated_by: null, note: null },
|
|
161
|
+
...(updatedBy ? { doc_updated_by: updatedBy } : {}),
|
|
162
|
+
});
|
|
163
|
+
const readmePath = path.join(resolved.gitRoot, config.paths.workflow_dir, task.id, "README.md");
|
|
164
|
+
process.stdout.write(`${readmePath}\n`);
|
|
165
|
+
return 0;
|
|
166
|
+
}
|
|
167
|
+
if (subcommand === "approve" || subcommand === "reject") {
|
|
168
|
+
const flags = parsePlanDecisionFlags(restArgs);
|
|
169
|
+
const by = (flags.by ?? "").trim();
|
|
170
|
+
if (!by) {
|
|
171
|
+
const usage = subcommand === "approve" ? TASK_PLAN_APPROVE_USAGE : TASK_PLAN_REJECT_USAGE;
|
|
172
|
+
const example = subcommand === "approve"
|
|
173
|
+
? TASK_PLAN_APPROVE_USAGE_EXAMPLE
|
|
174
|
+
: TASK_PLAN_REJECT_USAGE_EXAMPLE;
|
|
175
|
+
throw new CliError({ exitCode: 2, code: "E_USAGE", message: usageMessage(usage, example) });
|
|
176
|
+
}
|
|
177
|
+
const note = typeof flags.note === "string" ? flags.note.trim() : "";
|
|
178
|
+
if (subcommand === "reject" && note.length === 0) {
|
|
179
|
+
throw new CliError({
|
|
180
|
+
exitCode: 2,
|
|
181
|
+
code: "E_USAGE",
|
|
182
|
+
message: usageMessage(TASK_PLAN_REJECT_USAGE, TASK_PLAN_REJECT_USAGE_EXAMPLE),
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
const existingDoc = (typeof task.doc === "string" ? task.doc : "") || (await backend.getTaskDoc(task.id));
|
|
186
|
+
const baseDoc = ensureDocSections(existingDoc ?? "", config.tasks.doc.required_sections);
|
|
187
|
+
const plan = extractDocSection(baseDoc, "Plan");
|
|
188
|
+
if (!plan || plan.trim().length === 0) {
|
|
189
|
+
throw new CliError({
|
|
190
|
+
exitCode: 3,
|
|
191
|
+
code: "E_VALIDATION",
|
|
192
|
+
message: `${task.id}: cannot ${subcommand} plan: ## Plan section is missing or empty`,
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
await backend.writeTask({
|
|
196
|
+
...task,
|
|
197
|
+
plan_approval: {
|
|
198
|
+
state: subcommand === "approve" ? "approved" : "rejected",
|
|
199
|
+
updated_at: nowIso(),
|
|
200
|
+
updated_by: by,
|
|
201
|
+
note: note || null,
|
|
202
|
+
},
|
|
203
|
+
});
|
|
204
|
+
return 0;
|
|
205
|
+
}
|
|
206
|
+
throw new CliError({
|
|
207
|
+
exitCode: 2,
|
|
208
|
+
code: "E_USAGE",
|
|
209
|
+
message: usageMessage(TASK_PLAN_USAGE, TASK_PLAN_USAGE_EXAMPLE),
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
catch (err) {
|
|
213
|
+
if (err instanceof CliError)
|
|
214
|
+
throw err;
|
|
215
|
+
throw mapBackendError(err, { command: "task plan", root: opts.rootOverride ?? null });
|
|
216
|
+
}
|
|
217
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ready.d.ts","sourceRoot":"","sources":["../../../src/commands/task/ready.ts"],"names":[],"mappings":"AAMA,wBAAsB,QAAQ,CAAC,IAAI,EAAE;IACnC,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;CAChB,GAAG,OAAO,CAAC,MAAM,CAAC,CAqDlB"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { loadTaskBackend } from "../../backends/task-backend.js";
|
|
2
|
+
import { mapBackendError } from "../../cli/error-map.js";
|
|
3
|
+
import { successMessage, unknownEntityMessage, warnMessage } from "../../cli/output.js";
|
|
4
|
+
import { buildDependencyState } from "./shared.js";
|
|
5
|
+
export async function cmdReady(opts) {
|
|
6
|
+
try {
|
|
7
|
+
const { backend } = await loadTaskBackend({
|
|
8
|
+
cwd: opts.cwd,
|
|
9
|
+
rootOverride: opts.rootOverride ?? null,
|
|
10
|
+
});
|
|
11
|
+
const tasks = await backend.listTasks();
|
|
12
|
+
const depState = buildDependencyState(tasks);
|
|
13
|
+
const task = tasks.find((item) => item.id === opts.taskId);
|
|
14
|
+
const warnings = [];
|
|
15
|
+
if (task) {
|
|
16
|
+
const dep = depState.get(task.id);
|
|
17
|
+
const missing = dep?.missing ?? [];
|
|
18
|
+
const incomplete = dep?.incomplete ?? [];
|
|
19
|
+
if (missing.length > 0) {
|
|
20
|
+
warnings.push(`${task.id}: missing deps: ${missing.join(", ")}`);
|
|
21
|
+
}
|
|
22
|
+
if (incomplete.length > 0) {
|
|
23
|
+
warnings.push(`${task.id}: incomplete deps: ${incomplete.join(", ")}`);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
warnings.push(unknownEntityMessage("task id", opts.taskId));
|
|
28
|
+
}
|
|
29
|
+
for (const warning of warnings) {
|
|
30
|
+
process.stdout.write(`${warnMessage(warning)}\n`);
|
|
31
|
+
}
|
|
32
|
+
if (task) {
|
|
33
|
+
const status = String(task.status || "TODO").toUpperCase();
|
|
34
|
+
const title = task.title?.trim() || "(untitled task)";
|
|
35
|
+
const owner = task.owner?.trim() || "-";
|
|
36
|
+
const dep = depState.get(task.id);
|
|
37
|
+
const dependsOn = dep?.dependsOn ?? [];
|
|
38
|
+
process.stdout.write(`Task: ${task.id} [${status}] ${title}\n`);
|
|
39
|
+
process.stdout.write(`Owner: ${owner}\n`);
|
|
40
|
+
process.stdout.write(`Depends on: ${dependsOn.length > 0 ? dependsOn.join(", ") : "-"}\n`);
|
|
41
|
+
const missing = dep?.missing ?? [];
|
|
42
|
+
const incomplete = dep?.incomplete ?? [];
|
|
43
|
+
if (missing.length > 0) {
|
|
44
|
+
process.stdout.write(`${warnMessage(`missing deps: ${missing.join(", ")}`)}\n`);
|
|
45
|
+
}
|
|
46
|
+
if (incomplete.length > 0) {
|
|
47
|
+
process.stdout.write(`${warnMessage(`incomplete deps: ${incomplete.join(", ")}`)}\n`);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
const ready = warnings.length === 0;
|
|
51
|
+
process.stdout.write(`${ready ? successMessage("ready") : warnMessage("not ready")}` + "\n");
|
|
52
|
+
return ready ? 0 : 2;
|
|
53
|
+
}
|
|
54
|
+
catch (err) {
|
|
55
|
+
throw mapBackendError(err, { command: "ready", root: opts.rootOverride ?? null });
|
|
56
|
+
}
|
|
57
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export declare const TASK_SCAFFOLD_USAGE = "Usage: agentplane task scaffold <task-id> [--title <text>] [--overwrite] [--force]";
|
|
2
|
+
export declare const TASK_SCAFFOLD_USAGE_EXAMPLE = "agentplane task scaffold 202602030608-F1Q8AB";
|
|
3
|
+
export declare function cmdTaskScaffold(opts: {
|
|
4
|
+
cwd: string;
|
|
5
|
+
rootOverride?: string;
|
|
6
|
+
args: string[];
|
|
7
|
+
}): Promise<number>;
|
|
8
|
+
//# sourceMappingURL=scaffold.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scaffold.d.ts","sourceRoot":"","sources":["../../../src/commands/task/scaffold.ts"],"names":[],"mappings":"AAuBA,eAAO,MAAM,mBAAmB,uFACsD,CAAC;AACvF,eAAO,MAAM,2BAA2B,iDAAiD,CAAC;AAyD1F,wBAAsB,eAAe,CAAC,IAAI,EAAE;IAC1C,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,IAAI,EAAE,MAAM,EAAE,CAAC;CAChB,GAAG,OAAO,CAAC,MAAM,CAAC,CAwFlB"}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { mkdir, readFile } from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { atomicWriteFile, ensureDocSections, renderTaskReadme, setMarkdownSection, taskReadmePath, } from "@agentplaneorg/core";
|
|
4
|
+
import { loadTaskBackend } from "../../backends/task-backend.js";
|
|
5
|
+
import { mapBackendError } from "../../cli/error-map.js";
|
|
6
|
+
import { missingValueMessage, successMessage, unknownEntityMessage, usageMessage, } from "../../cli/output.js";
|
|
7
|
+
import { CliError } from "../../shared/errors.js";
|
|
8
|
+
import { taskDataToFrontmatter } from "../shared/task-backend.js";
|
|
9
|
+
import { nowIso } from "./shared.js";
|
|
10
|
+
export const TASK_SCAFFOLD_USAGE = "Usage: agentplane task scaffold <task-id> [--title <text>] [--overwrite] [--force]";
|
|
11
|
+
export const TASK_SCAFFOLD_USAGE_EXAMPLE = "agentplane task scaffold 202602030608-F1Q8AB";
|
|
12
|
+
function parseTaskScaffoldFlags(args) {
|
|
13
|
+
const [taskId, ...rest] = args;
|
|
14
|
+
if (!taskId) {
|
|
15
|
+
throw new CliError({
|
|
16
|
+
exitCode: 2,
|
|
17
|
+
code: "E_USAGE",
|
|
18
|
+
message: usageMessage(TASK_SCAFFOLD_USAGE, TASK_SCAFFOLD_USAGE_EXAMPLE),
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
const out = { taskId, overwrite: false, force: false, quiet: false };
|
|
22
|
+
for (let i = 0; i < rest.length; i++) {
|
|
23
|
+
const arg = rest[i];
|
|
24
|
+
if (!arg)
|
|
25
|
+
continue;
|
|
26
|
+
if (arg === "--overwrite") {
|
|
27
|
+
out.overwrite = true;
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
if (arg === "--force") {
|
|
31
|
+
out.force = true;
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
if (arg === "--quiet") {
|
|
35
|
+
out.quiet = true;
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
if (arg === "--title") {
|
|
39
|
+
const next = rest[i + 1];
|
|
40
|
+
if (!next) {
|
|
41
|
+
throw new CliError({
|
|
42
|
+
exitCode: 2,
|
|
43
|
+
code: "E_USAGE",
|
|
44
|
+
message: missingValueMessage("--title"),
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
out.title = next;
|
|
48
|
+
i++;
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
throw new CliError({
|
|
52
|
+
exitCode: 2,
|
|
53
|
+
code: "E_USAGE",
|
|
54
|
+
message: `Unknown flag: ${arg}`,
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
return out;
|
|
58
|
+
}
|
|
59
|
+
export async function cmdTaskScaffold(opts) {
|
|
60
|
+
const flags = parseTaskScaffoldFlags(opts.args);
|
|
61
|
+
try {
|
|
62
|
+
const { backend, resolved, config } = await loadTaskBackend({
|
|
63
|
+
cwd: opts.cwd,
|
|
64
|
+
rootOverride: opts.rootOverride ?? null,
|
|
65
|
+
});
|
|
66
|
+
const task = await backend.getTask(flags.taskId);
|
|
67
|
+
if (!task && !flags.force) {
|
|
68
|
+
throw new CliError({
|
|
69
|
+
exitCode: 2,
|
|
70
|
+
code: "E_USAGE",
|
|
71
|
+
message: unknownEntityMessage("task id", flags.taskId),
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
const readmePath = taskReadmePath(path.join(resolved.gitRoot, config.paths.workflow_dir), flags.taskId);
|
|
75
|
+
try {
|
|
76
|
+
await readFile(readmePath, "utf8");
|
|
77
|
+
if (!flags.overwrite) {
|
|
78
|
+
throw new CliError({
|
|
79
|
+
exitCode: 2,
|
|
80
|
+
code: "E_USAGE",
|
|
81
|
+
message: `File already exists: ${readmePath}`,
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
catch (err) {
|
|
86
|
+
const code = err?.code;
|
|
87
|
+
if (code !== "ENOENT") {
|
|
88
|
+
if (err instanceof CliError)
|
|
89
|
+
throw err;
|
|
90
|
+
throw err;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
const baseTask = task ??
|
|
94
|
+
{
|
|
95
|
+
id: flags.taskId,
|
|
96
|
+
title: flags.title ?? "",
|
|
97
|
+
description: "",
|
|
98
|
+
status: "TODO",
|
|
99
|
+
priority: "med",
|
|
100
|
+
owner: "",
|
|
101
|
+
depends_on: [],
|
|
102
|
+
tags: [],
|
|
103
|
+
verify: [],
|
|
104
|
+
comments: [],
|
|
105
|
+
doc_version: 2,
|
|
106
|
+
doc_updated_at: nowIso(),
|
|
107
|
+
doc_updated_by: "UNKNOWN",
|
|
108
|
+
};
|
|
109
|
+
if (flags.title)
|
|
110
|
+
baseTask.title = flags.title;
|
|
111
|
+
if (typeof baseTask.doc_updated_by !== "string" ||
|
|
112
|
+
baseTask.doc_updated_by.trim().length === 0 ||
|
|
113
|
+
baseTask.doc_updated_by.trim().toLowerCase() === "agentplane") {
|
|
114
|
+
baseTask.doc_updated_by = baseTask.owner?.trim() ? baseTask.owner : "UNKNOWN";
|
|
115
|
+
}
|
|
116
|
+
const frontmatter = taskDataToFrontmatter(baseTask);
|
|
117
|
+
const verificationTemplate = [
|
|
118
|
+
"### Plan",
|
|
119
|
+
"",
|
|
120
|
+
"",
|
|
121
|
+
"### Results",
|
|
122
|
+
"",
|
|
123
|
+
"",
|
|
124
|
+
"<!-- BEGIN VERIFICATION RESULTS -->",
|
|
125
|
+
"<!-- END VERIFICATION RESULTS -->",
|
|
126
|
+
].join("\n");
|
|
127
|
+
const baseDoc = ensureDocSections("", config.tasks.doc.required_sections);
|
|
128
|
+
const body = setMarkdownSection(baseDoc, "Verification", verificationTemplate);
|
|
129
|
+
const text = renderTaskReadme(frontmatter, body);
|
|
130
|
+
await mkdir(path.dirname(readmePath), { recursive: true });
|
|
131
|
+
await atomicWriteFile(readmePath, text.endsWith("\n") ? text : `${text}\n`);
|
|
132
|
+
if (!flags.quiet) {
|
|
133
|
+
process.stdout.write(`${successMessage("wrote", path.relative(resolved.gitRoot, readmePath))}\n`);
|
|
134
|
+
}
|
|
135
|
+
return 0;
|
|
136
|
+
}
|
|
137
|
+
catch (err) {
|
|
138
|
+
if (err instanceof CliError)
|
|
139
|
+
throw err;
|
|
140
|
+
throw mapBackendError(err, { command: "task scaffold", root: opts.rootOverride ?? null });
|
|
141
|
+
}
|
|
142
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export declare const TASK_SCRUB_USAGE = "Usage: agentplane task scrub --find <text> --replace <text> [flags]";
|
|
2
|
+
export declare const TASK_SCRUB_USAGE_EXAMPLE = "agentplane task scrub --find \"agentctl\" --replace \"agentplane\" --dry-run";
|
|
3
|
+
export declare function cmdTaskScrub(opts: {
|
|
4
|
+
cwd: string;
|
|
5
|
+
rootOverride?: string;
|
|
6
|
+
args: string[];
|
|
7
|
+
}): Promise<number>;
|
|
8
|
+
//# sourceMappingURL=scrub.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scrub.d.ts","sourceRoot":"","sources":["../../../src/commands/task/scrub.ts"],"names":[],"mappings":"AAUA,eAAO,MAAM,gBAAgB,wEAC0C,CAAC;AACxE,eAAO,MAAM,wBAAwB,iFACuC,CAAC;AAgE7E,wBAAsB,YAAY,CAAC,IAAI,EAAE;IACvC,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,IAAI,EAAE,MAAM,EAAE,CAAC;CAChB,GAAG,OAAO,CAAC,MAAM,CAAC,CAwDlB"}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { loadTaskBackend } from "../../backends/task-backend.js";
|
|
2
|
+
import { mapBackendError } from "../../cli/error-map.js";
|
|
3
|
+
import { infoMessage, missingValueMessage, successMessage, usageMessage, } from "../../cli/output.js";
|
|
4
|
+
import { CliError } from "../../shared/errors.js";
|
|
5
|
+
export const TASK_SCRUB_USAGE = "Usage: agentplane task scrub --find <text> --replace <text> [flags]";
|
|
6
|
+
export const TASK_SCRUB_USAGE_EXAMPLE = 'agentplane task scrub --find "agentctl" --replace "agentplane" --dry-run';
|
|
7
|
+
function parseTaskScrubFlags(args) {
|
|
8
|
+
const out = {
|
|
9
|
+
find: "",
|
|
10
|
+
replace: "",
|
|
11
|
+
dryRun: false,
|
|
12
|
+
quiet: false,
|
|
13
|
+
};
|
|
14
|
+
for (let i = 0; i < args.length; i++) {
|
|
15
|
+
const arg = args[i];
|
|
16
|
+
if (!arg)
|
|
17
|
+
continue;
|
|
18
|
+
if (arg === "--dry-run") {
|
|
19
|
+
out.dryRun = true;
|
|
20
|
+
continue;
|
|
21
|
+
}
|
|
22
|
+
if (arg === "--quiet") {
|
|
23
|
+
out.quiet = true;
|
|
24
|
+
continue;
|
|
25
|
+
}
|
|
26
|
+
if (!arg.startsWith("--")) {
|
|
27
|
+
throw new CliError({
|
|
28
|
+
exitCode: 2,
|
|
29
|
+
code: "E_USAGE",
|
|
30
|
+
message: usageMessage(TASK_SCRUB_USAGE, TASK_SCRUB_USAGE_EXAMPLE),
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
const next = args[i + 1];
|
|
34
|
+
if (!next) {
|
|
35
|
+
throw new CliError({ exitCode: 2, code: "E_USAGE", message: missingValueMessage(arg) });
|
|
36
|
+
}
|
|
37
|
+
if (arg === "--find") {
|
|
38
|
+
out.find = next;
|
|
39
|
+
}
|
|
40
|
+
else if (arg === "--replace") {
|
|
41
|
+
out.replace = next;
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
throw new CliError({ exitCode: 2, code: "E_USAGE", message: `Unknown flag: ${arg}` });
|
|
45
|
+
}
|
|
46
|
+
i++;
|
|
47
|
+
}
|
|
48
|
+
return out;
|
|
49
|
+
}
|
|
50
|
+
function scrubValue(value, find, replace) {
|
|
51
|
+
if (typeof value === "string")
|
|
52
|
+
return value.replaceAll(find, replace);
|
|
53
|
+
if (Array.isArray(value))
|
|
54
|
+
return value.map((item) => scrubValue(item, find, replace));
|
|
55
|
+
if (value && typeof value === "object") {
|
|
56
|
+
const entries = Object.entries(value);
|
|
57
|
+
const out = {};
|
|
58
|
+
for (const [key, val] of entries) {
|
|
59
|
+
out[key] = scrubValue(val, find, replace);
|
|
60
|
+
}
|
|
61
|
+
return out;
|
|
62
|
+
}
|
|
63
|
+
return value;
|
|
64
|
+
}
|
|
65
|
+
export async function cmdTaskScrub(opts) {
|
|
66
|
+
const flags = parseTaskScrubFlags(opts.args);
|
|
67
|
+
if (!flags.find) {
|
|
68
|
+
throw new CliError({
|
|
69
|
+
exitCode: 2,
|
|
70
|
+
code: "E_USAGE",
|
|
71
|
+
message: usageMessage(TASK_SCRUB_USAGE, TASK_SCRUB_USAGE_EXAMPLE),
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
try {
|
|
75
|
+
const { backend } = await loadTaskBackend({
|
|
76
|
+
cwd: opts.cwd,
|
|
77
|
+
rootOverride: opts.rootOverride ?? null,
|
|
78
|
+
});
|
|
79
|
+
const tasks = await backend.listTasks();
|
|
80
|
+
const updated = [];
|
|
81
|
+
const changedIds = new Set();
|
|
82
|
+
for (const task of tasks) {
|
|
83
|
+
const before = JSON.stringify(task);
|
|
84
|
+
const scrubbed = scrubValue(task, flags.find, flags.replace);
|
|
85
|
+
if (scrubbed && typeof scrubbed === "object") {
|
|
86
|
+
const next = scrubbed;
|
|
87
|
+
updated.push(next);
|
|
88
|
+
const after = JSON.stringify(next);
|
|
89
|
+
if (before !== after)
|
|
90
|
+
changedIds.add(next.id);
|
|
91
|
+
}
|
|
92
|
+
else {
|
|
93
|
+
updated.push(task);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
if (flags.dryRun) {
|
|
97
|
+
if (!flags.quiet) {
|
|
98
|
+
process.stdout.write(`${infoMessage(`dry-run: would update ${changedIds.size} task(s)`)}` + "\n");
|
|
99
|
+
for (const id of [...changedIds].toSorted()) {
|
|
100
|
+
process.stdout.write(`${id}\n`);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return 0;
|
|
104
|
+
}
|
|
105
|
+
if (backend.writeTasks) {
|
|
106
|
+
await backend.writeTasks(updated);
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
for (const task of updated) {
|
|
110
|
+
await backend.writeTask(task);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
if (!flags.quiet) {
|
|
114
|
+
process.stdout.write(`${successMessage("updated tasks", undefined, `count=${changedIds.size}`)}` + "\n");
|
|
115
|
+
}
|
|
116
|
+
return 0;
|
|
117
|
+
}
|
|
118
|
+
catch (err) {
|
|
119
|
+
throw mapBackendError(err, { command: "task scrub", root: opts.rootOverride ?? null });
|
|
120
|
+
}
|
|
121
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../../../src/commands/task/search.ts"],"names":[],"mappings":"AAcA,wBAAsB,aAAa,CAAC,IAAI,EAAE;IACxC,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,EAAE,CAAC;CAChB,GAAG,OAAO,CAAC,MAAM,CAAC,CAqElB"}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { loadTaskBackend } from "../../backends/task-backend.js";
|
|
2
|
+
import { mapBackendError } from "../../cli/error-map.js";
|
|
3
|
+
import { invalidValueMessage } from "../../cli/output.js";
|
|
4
|
+
import { CliError } from "../../shared/errors.js";
|
|
5
|
+
import { buildDependencyState, dedupeStrings, formatTaskLine, parseTaskListFilters, taskTextBlob, toStringArray, } from "./shared.js";
|
|
6
|
+
export async function cmdTaskSearch(opts) {
|
|
7
|
+
const query = opts.query.trim();
|
|
8
|
+
if (!query) {
|
|
9
|
+
throw new CliError({
|
|
10
|
+
exitCode: 2,
|
|
11
|
+
code: "E_USAGE",
|
|
12
|
+
message: "Missing query (expected non-empty text)",
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
let regex = false;
|
|
16
|
+
const restArgs = [...opts.args];
|
|
17
|
+
if (restArgs.includes("--regex")) {
|
|
18
|
+
regex = true;
|
|
19
|
+
restArgs.splice(restArgs.indexOf("--regex"), 1);
|
|
20
|
+
}
|
|
21
|
+
const filters = parseTaskListFilters(restArgs, { allowLimit: true });
|
|
22
|
+
try {
|
|
23
|
+
const { backend } = await loadTaskBackend({
|
|
24
|
+
cwd: opts.cwd,
|
|
25
|
+
rootOverride: opts.rootOverride ?? null,
|
|
26
|
+
});
|
|
27
|
+
const tasks = await backend.listTasks();
|
|
28
|
+
const depState = buildDependencyState(tasks);
|
|
29
|
+
let filtered = tasks;
|
|
30
|
+
if (filters.status.length > 0) {
|
|
31
|
+
const wanted = new Set(filters.status.map((s) => s.trim().toUpperCase()));
|
|
32
|
+
filtered = filtered.filter((task) => wanted.has(String(task.status || "TODO").toUpperCase()));
|
|
33
|
+
}
|
|
34
|
+
if (filters.owner.length > 0) {
|
|
35
|
+
const wanted = new Set(filters.owner.map((o) => o.trim().toUpperCase()));
|
|
36
|
+
filtered = filtered.filter((task) => wanted.has(String(task.owner || "").toUpperCase()));
|
|
37
|
+
}
|
|
38
|
+
if (filters.tag.length > 0) {
|
|
39
|
+
const wanted = new Set(filters.tag.map((t) => t.trim()).filter(Boolean));
|
|
40
|
+
filtered = filtered.filter((task) => {
|
|
41
|
+
const tags = dedupeStrings(toStringArray(task.tags));
|
|
42
|
+
return tags.some((tag) => wanted.has(tag));
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
let matches = [];
|
|
46
|
+
if (regex) {
|
|
47
|
+
let pattern;
|
|
48
|
+
try {
|
|
49
|
+
pattern = new RegExp(query, "i");
|
|
50
|
+
}
|
|
51
|
+
catch (err) {
|
|
52
|
+
const message = err instanceof Error ? err.message : "Invalid regex";
|
|
53
|
+
throw new CliError({
|
|
54
|
+
exitCode: 2,
|
|
55
|
+
code: "E_USAGE",
|
|
56
|
+
message: invalidValueMessage("regex", message, "valid pattern"),
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
matches = filtered.filter((task) => pattern.test(taskTextBlob(task)));
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
const needle = query.toLowerCase();
|
|
63
|
+
matches = filtered.filter((task) => taskTextBlob(task).toLowerCase().includes(needle));
|
|
64
|
+
}
|
|
65
|
+
if (filters.limit !== undefined && filters.limit >= 0) {
|
|
66
|
+
matches = matches.slice(0, filters.limit);
|
|
67
|
+
}
|
|
68
|
+
const sorted = matches.toSorted((a, b) => a.id.localeCompare(b.id));
|
|
69
|
+
for (const task of sorted) {
|
|
70
|
+
process.stdout.write(`${formatTaskLine(task, depState.get(task.id))}\n`);
|
|
71
|
+
}
|
|
72
|
+
return 0;
|
|
73
|
+
}
|
|
74
|
+
catch (err) {
|
|
75
|
+
if (err instanceof CliError)
|
|
76
|
+
throw err;
|
|
77
|
+
throw mapBackendError(err, { command: "task search", root: opts.rootOverride ?? null });
|
|
78
|
+
}
|
|
79
|
+
}
|