agentplane 0.1.6 → 0.1.7

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.
Files changed (111) hide show
  1. package/assets/AGENTS.md +1 -1
  2. package/assets/agents/ORCHESTRATOR.json +1 -1
  3. package/assets/agents/UPGRADER.json +1 -1
  4. package/dist/cli/run-cli.d.ts.map +1 -1
  5. package/dist/cli/run-cli.js +22 -7
  6. package/dist/commands/branch/index.d.ts +60 -0
  7. package/dist/commands/branch/index.d.ts.map +1 -0
  8. package/dist/commands/branch/index.js +511 -0
  9. package/dist/commands/guard/index.d.ts +67 -0
  10. package/dist/commands/guard/index.d.ts.map +1 -0
  11. package/dist/commands/guard/index.js +367 -0
  12. package/dist/commands/hooks/index.d.ts +18 -0
  13. package/dist/commands/hooks/index.d.ts.map +1 -0
  14. package/dist/commands/hooks/index.js +290 -0
  15. package/dist/commands/pr/index.d.ts +46 -0
  16. package/dist/commands/pr/index.d.ts.map +1 -0
  17. package/dist/commands/pr/index.js +854 -0
  18. package/dist/commands/shared/git-diff.d.ts +9 -0
  19. package/dist/commands/shared/git-diff.d.ts.map +1 -0
  20. package/dist/commands/shared/git-diff.js +41 -0
  21. package/dist/commands/shared/git-ops.d.ts +24 -0
  22. package/dist/commands/shared/git-ops.d.ts.map +1 -0
  23. package/dist/commands/shared/git-ops.js +181 -0
  24. package/dist/commands/shared/git-worktree.d.ts +8 -0
  25. package/dist/commands/shared/git-worktree.d.ts.map +1 -0
  26. package/dist/commands/shared/git-worktree.js +48 -0
  27. package/dist/commands/shared/git.d.ts +4 -0
  28. package/dist/commands/shared/git.d.ts.map +1 -0
  29. package/dist/commands/shared/git.js +14 -0
  30. package/dist/commands/shared/path.d.ts +3 -0
  31. package/dist/commands/shared/path.d.ts.map +1 -0
  32. package/dist/commands/shared/path.js +14 -0
  33. package/dist/commands/shared/pr-meta.d.ts +21 -0
  34. package/dist/commands/shared/pr-meta.d.ts.map +1 -0
  35. package/dist/commands/shared/pr-meta.js +72 -0
  36. package/dist/commands/shared/task-backend.d.ts +15 -0
  37. package/dist/commands/shared/task-backend.d.ts.map +1 -0
  38. package/dist/commands/shared/task-backend.js +55 -0
  39. package/dist/commands/task/add.d.ts +8 -0
  40. package/dist/commands/task/add.d.ts.map +1 -0
  41. package/dist/commands/task/add.js +164 -0
  42. package/dist/commands/task/block.d.ts +19 -0
  43. package/dist/commands/task/block.d.ts.map +1 -0
  44. package/dist/commands/task/block.js +86 -0
  45. package/dist/commands/task/comment.d.ts +8 -0
  46. package/dist/commands/task/comment.d.ts.map +1 -0
  47. package/dist/commands/task/comment.js +29 -0
  48. package/dist/commands/task/doc.d.ts +17 -0
  49. package/dist/commands/task/doc.d.ts.map +1 -0
  50. package/dist/commands/task/doc.js +220 -0
  51. package/dist/commands/task/export.d.ts +5 -0
  52. package/dist/commands/task/export.d.ts.map +1 -0
  53. package/dist/commands/task/export.js +27 -0
  54. package/dist/commands/task/finish.d.ts +27 -0
  55. package/dist/commands/task/finish.d.ts.map +1 -0
  56. package/dist/commands/task/finish.js +131 -0
  57. package/dist/commands/task/index.d.ts +23 -0
  58. package/dist/commands/task/index.d.ts.map +1 -0
  59. package/dist/commands/task/index.js +22 -0
  60. package/dist/commands/task/lint.d.ts +5 -0
  61. package/dist/commands/task/lint.d.ts.map +1 -0
  62. package/dist/commands/task/lint.js +22 -0
  63. package/dist/commands/task/list.d.ts +11 -0
  64. package/dist/commands/task/list.d.ts.map +1 -0
  65. package/dist/commands/task/list.js +54 -0
  66. package/dist/commands/task/migrate.d.ts +6 -0
  67. package/dist/commands/task/migrate.d.ts.map +1 -0
  68. package/dist/commands/task/migrate.js +70 -0
  69. package/dist/commands/task/new.d.ts +8 -0
  70. package/dist/commands/task/new.d.ts.map +1 -0
  71. package/dist/commands/task/new.js +117 -0
  72. package/dist/commands/task/next.d.ts +6 -0
  73. package/dist/commands/task/next.d.ts.map +1 -0
  74. package/dist/commands/task/next.js +45 -0
  75. package/dist/commands/task/normalize.d.ts +6 -0
  76. package/dist/commands/task/normalize.d.ts.map +1 -0
  77. package/dist/commands/task/normalize.js +46 -0
  78. package/dist/commands/task/ready.d.ts +6 -0
  79. package/dist/commands/task/ready.d.ts.map +1 -0
  80. package/dist/commands/task/ready.js +57 -0
  81. package/dist/commands/task/scaffold.d.ts +8 -0
  82. package/dist/commands/task/scaffold.d.ts.map +1 -0
  83. package/dist/commands/task/scaffold.js +131 -0
  84. package/dist/commands/task/scrub.d.ts +8 -0
  85. package/dist/commands/task/scrub.d.ts.map +1 -0
  86. package/dist/commands/task/scrub.js +121 -0
  87. package/dist/commands/task/search.d.ts +7 -0
  88. package/dist/commands/task/search.d.ts.map +1 -0
  89. package/dist/commands/task/search.js +79 -0
  90. package/dist/commands/task/set-status.d.ts +19 -0
  91. package/dist/commands/task/set-status.d.ts.map +1 -0
  92. package/dist/commands/task/set-status.js +123 -0
  93. package/dist/commands/task/shared.d.ts +46 -0
  94. package/dist/commands/task/shared.d.ts.map +1 -0
  95. package/dist/commands/task/shared.js +283 -0
  96. package/dist/commands/task/show.d.ts +6 -0
  97. package/dist/commands/task/show.d.ts.map +1 -0
  98. package/dist/commands/task/show.js +35 -0
  99. package/dist/commands/task/start.d.ts +19 -0
  100. package/dist/commands/task/start.d.ts.map +1 -0
  101. package/dist/commands/task/start.js +109 -0
  102. package/dist/commands/task/update.d.ts +8 -0
  103. package/dist/commands/task/update.d.ts.map +1 -0
  104. package/dist/commands/task/update.js +144 -0
  105. package/dist/commands/task/verify.d.ts +14 -0
  106. package/dist/commands/task/verify.d.ts.map +1 -0
  107. package/dist/commands/task/verify.js +362 -0
  108. package/dist/commands/workflow.d.ts +5 -364
  109. package/dist/commands/workflow.d.ts.map +1 -1
  110. package/dist/commands/workflow.js +6 -4617
  111. package/package.json +2 -2
@@ -0,0 +1,283 @@
1
+ import { execFile } from "node:child_process";
2
+ import { promisify } from "node:util";
3
+ import { invalidValueForFlag, invalidValueMessage, missingValueMessage, warnMessage, } from "../../cli/output.js";
4
+ import { CliError } from "../../shared/errors.js";
5
+ export const execFileAsync = promisify(execFile);
6
+ export function nowIso() {
7
+ return new Date().toISOString();
8
+ }
9
+ export function normalizeDependsOnInput(value) {
10
+ const trimmed = value.trim();
11
+ if (!trimmed || trimmed === "[]")
12
+ return [];
13
+ return [trimmed];
14
+ }
15
+ const ALLOWED_TASK_STATUSES = new Set(["TODO", "DOING", "DONE", "BLOCKED"]);
16
+ export function normalizeTaskStatus(value) {
17
+ const normalized = value.trim().toUpperCase();
18
+ if (!ALLOWED_TASK_STATUSES.has(normalized)) {
19
+ throw new CliError({
20
+ exitCode: 2,
21
+ code: "E_USAGE",
22
+ message: invalidValueMessage("status", value, `one of ${[...ALLOWED_TASK_STATUSES].join(", ")}`),
23
+ });
24
+ }
25
+ return normalized;
26
+ }
27
+ export function dedupeStrings(items) {
28
+ const seen = new Set();
29
+ const out = [];
30
+ for (const item of items) {
31
+ const trimmed = item.trim();
32
+ if (!trimmed)
33
+ continue;
34
+ if (seen.has(trimmed))
35
+ continue;
36
+ seen.add(trimmed);
37
+ out.push(trimmed);
38
+ }
39
+ return out;
40
+ }
41
+ export function toStringArray(value) {
42
+ if (!Array.isArray(value))
43
+ return [];
44
+ return value
45
+ .filter((item) => typeof item === "string")
46
+ .map((item) => item.trim());
47
+ }
48
+ export function requiresVerify(tags, requiredTags) {
49
+ const required = new Set(requiredTags.map((tag) => tag.trim().toLowerCase()).filter(Boolean));
50
+ if (required.size === 0)
51
+ return false;
52
+ return tags.some((tag) => required.has(tag.trim().toLowerCase()));
53
+ }
54
+ export function buildDependencyState(tasks) {
55
+ const byId = new Map(tasks.map((task) => [task.id, task]));
56
+ const state = new Map();
57
+ for (const task of tasks) {
58
+ const dependsOn = dedupeStrings(toStringArray(task.depends_on));
59
+ const missing = [];
60
+ const incomplete = [];
61
+ for (const depId of dependsOn) {
62
+ const dep = byId.get(depId);
63
+ if (!dep) {
64
+ missing.push(depId);
65
+ continue;
66
+ }
67
+ const status = String(dep.status || "TODO").toUpperCase();
68
+ if (status !== "DONE") {
69
+ incomplete.push(depId);
70
+ }
71
+ }
72
+ state.set(task.id, { dependsOn, missing, incomplete });
73
+ }
74
+ return state;
75
+ }
76
+ function formatDepsSummary(dep) {
77
+ if (!dep)
78
+ return null;
79
+ if (dep.dependsOn.length === 0)
80
+ return "deps=none";
81
+ if (dep.missing.length === 0 && dep.incomplete.length === 0)
82
+ return "deps=ready";
83
+ const parts = [];
84
+ if (dep.missing.length > 0) {
85
+ parts.push(`missing:${dep.missing.join(",")}`);
86
+ }
87
+ if (dep.incomplete.length > 0) {
88
+ parts.push(`wait:${dep.incomplete.join(",")}`);
89
+ }
90
+ return `deps=${parts.join(",")}`;
91
+ }
92
+ export function formatTaskLine(task, depState) {
93
+ const status = String(task.status || "TODO").toUpperCase();
94
+ const title = task.title?.trim() || "(untitled task)";
95
+ const extras = [];
96
+ if (task.owner?.trim())
97
+ extras.push(`owner=${task.owner.trim()}`);
98
+ if (task.priority !== undefined && String(task.priority).trim()) {
99
+ extras.push(`prio=${String(task.priority).trim()}`);
100
+ }
101
+ const depsSummary = formatDepsSummary(depState);
102
+ if (depsSummary)
103
+ extras.push(depsSummary);
104
+ const tags = dedupeStrings(toStringArray(task.tags));
105
+ if (tags.length > 0)
106
+ extras.push(`tags=${tags.join(",")}`);
107
+ const verify = dedupeStrings(toStringArray(task.verify));
108
+ if (verify.length > 0)
109
+ extras.push(`verify=${verify.length}`);
110
+ const suffix = extras.length > 0 ? ` (${extras.join(", ")})` : "";
111
+ return `${task.id} [${status}] ${title}${suffix}`;
112
+ }
113
+ export function isTransitionAllowed(current, next) {
114
+ if (current === next)
115
+ return true;
116
+ if (current === "TODO")
117
+ return next === "DOING" || next === "BLOCKED";
118
+ if (current === "DOING")
119
+ return next === "DONE" || next === "BLOCKED";
120
+ if (current === "BLOCKED")
121
+ return next === "TODO" || next === "DOING";
122
+ if (current === "DONE")
123
+ return false;
124
+ return false;
125
+ }
126
+ export function requireStructuredComment(body, prefix, minChars) {
127
+ const normalized = body.trim();
128
+ if (!normalized.toLowerCase().startsWith(prefix.toLowerCase())) {
129
+ throw new CliError({
130
+ exitCode: 2,
131
+ code: "E_USAGE",
132
+ message: `Comment body must start with ${prefix}`,
133
+ });
134
+ }
135
+ if (normalized.length < minChars) {
136
+ throw new CliError({
137
+ exitCode: 2,
138
+ code: "E_USAGE",
139
+ message: `Comment body must be at least ${minChars} characters`,
140
+ });
141
+ }
142
+ }
143
+ export async function readHeadCommit(cwd) {
144
+ const { stdout } = await execFileAsync("git", ["log", "-1", "--pretty=%H:%s"], { cwd });
145
+ const trimmed = stdout.trim();
146
+ const [hash, message] = trimmed.split(":", 2);
147
+ if (!hash || !message) {
148
+ throw new Error("Unable to read git HEAD commit");
149
+ }
150
+ return { hash, message };
151
+ }
152
+ export function enforceStatusCommitPolicy(opts) {
153
+ if (opts.policy === "off")
154
+ return;
155
+ if (opts.policy === "warn") {
156
+ if (!opts.quiet && !opts.confirmed) {
157
+ process.stderr.write(`${warnMessage(`${opts.action}: status/comment-driven commit requested; policy=warn (pass --confirm-status-commit to acknowledge)`)}\n`);
158
+ }
159
+ return;
160
+ }
161
+ if (opts.policy === "confirm" && !opts.confirmed) {
162
+ throw new CliError({
163
+ exitCode: 2,
164
+ code: "E_USAGE",
165
+ message: `${opts.action}: status/comment-driven commit blocked by status_commit_policy='confirm' ` +
166
+ "(pass --confirm-status-commit to proceed)",
167
+ });
168
+ }
169
+ }
170
+ export async function readCommitInfo(cwd, rev) {
171
+ const { stdout } = await execFileAsync("git", ["log", "-1", "--pretty=%H:%s", rev], { cwd });
172
+ const trimmed = stdout.trim();
173
+ const [hash, message] = trimmed.split(":", 2);
174
+ if (!hash || !message) {
175
+ throw new Error("Unable to read git commit");
176
+ }
177
+ return { hash, message };
178
+ }
179
+ export function defaultCommitEmojiForStatus(status) {
180
+ const normalized = status.trim().toUpperCase();
181
+ if (normalized === "DOING")
182
+ return "🚧";
183
+ if (normalized === "DONE")
184
+ return "✅";
185
+ if (normalized === "BLOCKED")
186
+ return "⛔";
187
+ return "🧩";
188
+ }
189
+ export function parseTaskListFilters(args, opts) {
190
+ const out = { status: [], owner: [], tag: [], quiet: false };
191
+ for (let i = 0; i < args.length; i++) {
192
+ const arg = args[i];
193
+ if (!arg)
194
+ continue;
195
+ if (arg === "--quiet") {
196
+ out.quiet = true;
197
+ continue;
198
+ }
199
+ if (arg === "--status") {
200
+ const next = args[i + 1];
201
+ if (!next) {
202
+ throw new CliError({
203
+ exitCode: 2,
204
+ code: "E_USAGE",
205
+ message: missingValueMessage("--status"),
206
+ });
207
+ }
208
+ out.status.push(next);
209
+ i++;
210
+ continue;
211
+ }
212
+ if (arg === "--owner") {
213
+ const next = args[i + 1];
214
+ if (!next) {
215
+ throw new CliError({
216
+ exitCode: 2,
217
+ code: "E_USAGE",
218
+ message: missingValueMessage("--owner"),
219
+ });
220
+ }
221
+ out.owner.push(next);
222
+ i++;
223
+ continue;
224
+ }
225
+ if (arg === "--tag") {
226
+ const next = args[i + 1];
227
+ if (!next) {
228
+ throw new CliError({ exitCode: 2, code: "E_USAGE", message: missingValueMessage("--tag") });
229
+ }
230
+ out.tag.push(next);
231
+ i++;
232
+ continue;
233
+ }
234
+ if (opts?.allowLimit && arg === "--limit") {
235
+ const next = args[i + 1];
236
+ if (!next) {
237
+ throw new CliError({
238
+ exitCode: 2,
239
+ code: "E_USAGE",
240
+ message: missingValueMessage("--limit"),
241
+ });
242
+ }
243
+ const parsed = Number.parseInt(next, 10);
244
+ if (!Number.isFinite(parsed)) {
245
+ throw new CliError({
246
+ exitCode: 2,
247
+ code: "E_USAGE",
248
+ message: invalidValueForFlag("--limit", next, "integer"),
249
+ });
250
+ }
251
+ out.limit = parsed;
252
+ i++;
253
+ continue;
254
+ }
255
+ if (arg.startsWith("--")) {
256
+ throw new CliError({ exitCode: 2, code: "E_USAGE", message: `Unknown flag: ${arg}` });
257
+ }
258
+ }
259
+ return out;
260
+ }
261
+ export function taskTextBlob(task) {
262
+ const parts = [];
263
+ for (const key of ["id", "title", "description", "status", "priority", "owner"]) {
264
+ const value = task[key];
265
+ if (typeof value === "string" && value.trim())
266
+ parts.push(value.trim());
267
+ }
268
+ const tags = toStringArray(task.tags);
269
+ parts.push(...tags.filter(Boolean));
270
+ const comments = Array.isArray(task.comments) ? task.comments : [];
271
+ for (const comment of comments) {
272
+ if (comment && typeof comment.author === "string")
273
+ parts.push(comment.author);
274
+ if (comment && typeof comment.body === "string")
275
+ parts.push(comment.body);
276
+ }
277
+ const commit = task.commit ?? null;
278
+ if (commit && typeof commit.hash === "string")
279
+ parts.push(commit.hash);
280
+ if (commit && typeof commit.message === "string")
281
+ parts.push(commit.message);
282
+ return parts.join("\n");
283
+ }
@@ -0,0 +1,6 @@
1
+ export declare function cmdTaskShow(opts: {
2
+ cwd: string;
3
+ rootOverride?: string;
4
+ taskId: string;
5
+ }): Promise<number>;
6
+ //# sourceMappingURL=show.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"show.d.ts","sourceRoot":"","sources":["../../../src/commands/task/show.ts"],"names":[],"mappings":"AAOA,wBAAsB,WAAW,CAAC,IAAI,EAAE;IACtC,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;CAChB,GAAG,OAAO,CAAC,MAAM,CAAC,CA4BlB"}
@@ -0,0 +1,35 @@
1
+ import { validateTaskDocMetadata } from "@agentplaneorg/core";
2
+ import { mapBackendError } from "../../cli/error-map.js";
3
+ import { CliError } from "../../shared/errors.js";
4
+ import { loadBackendTask, taskDataToFrontmatter } from "../shared/task-backend.js";
5
+ export async function cmdTaskShow(opts) {
6
+ try {
7
+ const { task, backendId } = await loadBackendTask({
8
+ cwd: opts.cwd,
9
+ rootOverride: opts.rootOverride,
10
+ taskId: opts.taskId,
11
+ });
12
+ const frontmatter = taskDataToFrontmatter(task);
13
+ if (backendId === "local") {
14
+ const metadataErrors = validateTaskDocMetadata(frontmatter);
15
+ if (metadataErrors.length > 0) {
16
+ throw new CliError({
17
+ exitCode: 3,
18
+ code: "E_VALIDATION",
19
+ message: `Invalid task README metadata: ${metadataErrors.join("; ")}`,
20
+ });
21
+ }
22
+ }
23
+ process.stdout.write(`${JSON.stringify(frontmatter, null, 2)}\n`);
24
+ return 0;
25
+ }
26
+ catch (err) {
27
+ if (err instanceof CliError)
28
+ throw err;
29
+ throw mapBackendError(err, {
30
+ command: "task show",
31
+ root: opts.rootOverride ?? null,
32
+ taskId: opts.taskId,
33
+ });
34
+ }
35
+ }
@@ -0,0 +1,19 @@
1
+ export declare const START_USAGE = "Usage: agentplane start <task-id> --author <id> --body <text> [flags]";
2
+ export declare const START_USAGE_EXAMPLE = "agentplane start 202602030608-F1Q8AB --author CODER --body \"Start: ...\"";
3
+ export declare function cmdStart(opts: {
4
+ cwd: string;
5
+ rootOverride?: string;
6
+ taskId: string;
7
+ author: string;
8
+ body: string;
9
+ commitFromComment: boolean;
10
+ commitEmoji?: string;
11
+ commitAllow: string[];
12
+ commitAutoAllow: boolean;
13
+ commitAllowTasks: boolean;
14
+ commitRequireClean: boolean;
15
+ confirmStatusCommit: boolean;
16
+ force: boolean;
17
+ quiet: boolean;
18
+ }): Promise<number>;
19
+ //# sourceMappingURL=start.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"start.d.ts","sourceRoot":"","sources":["../../../src/commands/task/start.ts"],"names":[],"mappings":"AAmBA,eAAO,MAAM,WAAW,0EAA0E,CAAC;AACnG,eAAO,MAAM,mBAAmB,8EAC2C,CAAC;AAE5E,wBAAsB,QAAQ,CAAC,IAAI,EAAE;IACnC,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,iBAAiB,EAAE,OAAO,CAAC;IAC3B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,eAAe,EAAE,OAAO,CAAC;IACzB,gBAAgB,EAAE,OAAO,CAAC;IAC1B,kBAAkB,EAAE,OAAO,CAAC;IAC5B,mBAAmB,EAAE,OAAO,CAAC;IAC7B,KAAK,EAAE,OAAO,CAAC;IACf,KAAK,EAAE,OAAO,CAAC;CAChB,GAAG,OAAO,CAAC,MAAM,CAAC,CAgHlB"}
@@ -0,0 +1,109 @@
1
+ import { loadConfig, resolveProject } from "@agentplaneorg/core";
2
+ import { mapBackendError } from "../../cli/error-map.js";
3
+ import { successMessage, warnMessage } from "../../cli/output.js";
4
+ import { formatCommentBodyForCommit } from "../../shared/comment-format.js";
5
+ import { CliError } from "../../shared/errors.js";
6
+ import { commitFromComment } from "../guard/index.js";
7
+ import { loadBackendTask } from "../shared/task-backend.js";
8
+ import { buildDependencyState, enforceStatusCommitPolicy, isTransitionAllowed, nowIso, requireStructuredComment, } from "./shared.js";
9
+ export const START_USAGE = "Usage: agentplane start <task-id> --author <id> --body <text> [flags]";
10
+ export const START_USAGE_EXAMPLE = 'agentplane start 202602030608-F1Q8AB --author CODER --body "Start: ..."';
11
+ export async function cmdStart(opts) {
12
+ try {
13
+ const resolved = await resolveProject({
14
+ cwd: opts.cwd,
15
+ rootOverride: opts.rootOverride ?? null,
16
+ });
17
+ const loaded = await loadConfig(resolved.agentplaneDir);
18
+ if (opts.commitFromComment) {
19
+ enforceStatusCommitPolicy({
20
+ policy: loaded.config.status_commit_policy,
21
+ action: "start",
22
+ confirmed: opts.confirmStatusCommit,
23
+ quiet: opts.quiet,
24
+ });
25
+ }
26
+ const { prefix, min_chars: minChars } = loaded.config.tasks.comments.start;
27
+ requireStructuredComment(opts.body, prefix, minChars);
28
+ const { backend, task } = await loadBackendTask({
29
+ cwd: opts.cwd,
30
+ rootOverride: opts.rootOverride,
31
+ taskId: opts.taskId,
32
+ });
33
+ const currentStatus = String(task.status || "TODO").toUpperCase();
34
+ if (!opts.force && !isTransitionAllowed(currentStatus, "DOING")) {
35
+ throw new CliError({
36
+ exitCode: 2,
37
+ code: "E_USAGE",
38
+ message: `Refusing status transition ${currentStatus} -> DOING (use --force to override)`,
39
+ });
40
+ }
41
+ if (!opts.force) {
42
+ const allTasks = await backend.listTasks();
43
+ const depState = buildDependencyState(allTasks);
44
+ const dep = depState.get(task.id);
45
+ if (dep && (dep.missing.length > 0 || dep.incomplete.length > 0)) {
46
+ if (!opts.quiet) {
47
+ if (dep.missing.length > 0) {
48
+ process.stderr.write(`${warnMessage(`missing deps: ${dep.missing.join(", ")}`)}\n`);
49
+ }
50
+ if (dep.incomplete.length > 0) {
51
+ process.stderr.write(`${warnMessage(`incomplete deps: ${dep.incomplete.join(", ")}`)}\n`);
52
+ }
53
+ }
54
+ throw new CliError({
55
+ exitCode: 2,
56
+ code: "E_USAGE",
57
+ message: `Task is not ready: ${task.id} (use --force to override)`,
58
+ });
59
+ }
60
+ }
61
+ const formattedComment = opts.commitFromComment
62
+ ? formatCommentBodyForCommit(opts.body, loaded.config)
63
+ : null;
64
+ const commentBody = formattedComment ?? opts.body;
65
+ const existingComments = Array.isArray(task.comments)
66
+ ? task.comments.filter((item) => !!item && typeof item.author === "string" && typeof item.body === "string")
67
+ : [];
68
+ const commentsValue = [
69
+ ...existingComments,
70
+ { author: opts.author, body: commentBody },
71
+ ];
72
+ const nextTask = {
73
+ ...task,
74
+ status: "DOING",
75
+ comments: commentsValue,
76
+ doc_version: 2,
77
+ doc_updated_at: nowIso(),
78
+ doc_updated_by: opts.author,
79
+ };
80
+ await backend.writeTask(nextTask);
81
+ let commitInfo = null;
82
+ if (opts.commitFromComment) {
83
+ commitInfo = await commitFromComment({
84
+ cwd: opts.cwd,
85
+ rootOverride: opts.rootOverride,
86
+ taskId: opts.taskId,
87
+ commentBody: opts.body,
88
+ formattedComment,
89
+ emoji: opts.commitEmoji ?? "🚧",
90
+ allow: opts.commitAllow,
91
+ autoAllow: opts.commitAutoAllow || opts.commitAllow.length === 0,
92
+ allowTasks: opts.commitAllowTasks,
93
+ requireClean: opts.commitRequireClean,
94
+ quiet: opts.quiet,
95
+ config: loaded.config,
96
+ });
97
+ }
98
+ if (!opts.quiet) {
99
+ const suffix = commitInfo ? ` (commit=${commitInfo.hash.slice(0, 12)})` : "";
100
+ process.stdout.write(`${successMessage("started", `${opts.taskId}${suffix}`)}\n`);
101
+ }
102
+ return 0;
103
+ }
104
+ catch (err) {
105
+ if (err instanceof CliError)
106
+ throw err;
107
+ throw mapBackendError(err, { command: "start", root: opts.rootOverride ?? null });
108
+ }
109
+ }
@@ -0,0 +1,8 @@
1
+ export declare const TASK_UPDATE_USAGE = "Usage: agentplane task update <task-id> [flags]";
2
+ export declare const TASK_UPDATE_USAGE_EXAMPLE = "agentplane task update 202602030608-F1Q8AB --title \"...\" --owner CODER";
3
+ export declare function cmdTaskUpdate(opts: {
4
+ cwd: string;
5
+ rootOverride?: string;
6
+ args: string[];
7
+ }): Promise<number>;
8
+ //# sourceMappingURL=update.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"update.d.ts","sourceRoot":"","sources":["../../../src/commands/task/update.ts"],"names":[],"mappings":"AAWA,eAAO,MAAM,iBAAiB,oDAAoD,CAAC;AACnF,eAAO,MAAM,yBAAyB,6EACoC,CAAC;AAuG3E,wBAAsB,aAAa,CAAC,IAAI,EAAE;IACxC,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,IAAI,EAAE,MAAM,EAAE,CAAC;CAChB,GAAG,OAAO,CAAC,MAAM,CAAC,CAoDlB"}
@@ -0,0 +1,144 @@
1
+ import { loadTaskBackend } from "../../backends/task-backend.js";
2
+ import { mapBackendError } from "../../cli/error-map.js";
3
+ import { missingValueMessage, successMessage, unknownEntityMessage, usageMessage, } from "../../cli/output.js";
4
+ import { CliError } from "../../shared/errors.js";
5
+ import { dedupeStrings, normalizeDependsOnInput, requiresVerify, toStringArray } from "./shared.js";
6
+ export const TASK_UPDATE_USAGE = "Usage: agentplane task update <task-id> [flags]";
7
+ export const TASK_UPDATE_USAGE_EXAMPLE = 'agentplane task update 202602030608-F1Q8AB --title "..." --owner CODER';
8
+ function parseTaskUpdateFlags(args) {
9
+ const [taskId, ...rest] = args;
10
+ if (!taskId) {
11
+ throw new CliError({
12
+ exitCode: 2,
13
+ code: "E_USAGE",
14
+ message: usageMessage(TASK_UPDATE_USAGE, TASK_UPDATE_USAGE_EXAMPLE),
15
+ });
16
+ }
17
+ const out = {
18
+ taskId,
19
+ tags: [],
20
+ replaceTags: false,
21
+ dependsOn: [],
22
+ replaceDependsOn: false,
23
+ verify: [],
24
+ replaceVerify: false,
25
+ };
26
+ for (let i = 0; i < rest.length; i++) {
27
+ const arg = rest[i];
28
+ if (!arg)
29
+ continue;
30
+ if (arg === "--replace-tags") {
31
+ out.replaceTags = true;
32
+ continue;
33
+ }
34
+ if (arg === "--replace-depends-on") {
35
+ out.replaceDependsOn = true;
36
+ continue;
37
+ }
38
+ if (arg === "--replace-verify") {
39
+ out.replaceVerify = true;
40
+ continue;
41
+ }
42
+ if (!arg.startsWith("--")) {
43
+ throw new CliError({
44
+ exitCode: 2,
45
+ code: "E_USAGE",
46
+ message: usageMessage(TASK_UPDATE_USAGE, TASK_UPDATE_USAGE_EXAMPLE),
47
+ });
48
+ }
49
+ const next = rest[i + 1];
50
+ if (!next) {
51
+ throw new CliError({ exitCode: 2, code: "E_USAGE", message: missingValueMessage(arg) });
52
+ }
53
+ switch (arg) {
54
+ case "--title": {
55
+ out.title = next;
56
+ break;
57
+ }
58
+ case "--description": {
59
+ out.description = next;
60
+ break;
61
+ }
62
+ case "--priority": {
63
+ out.priority = next;
64
+ break;
65
+ }
66
+ case "--owner": {
67
+ out.owner = next;
68
+ break;
69
+ }
70
+ case "--tag": {
71
+ out.tags.push(next);
72
+ break;
73
+ }
74
+ case "--depends-on": {
75
+ out.dependsOn.push(...normalizeDependsOnInput(next));
76
+ break;
77
+ }
78
+ case "--verify": {
79
+ out.verify.push(next);
80
+ break;
81
+ }
82
+ default: {
83
+ throw new CliError({
84
+ exitCode: 2,
85
+ code: "E_USAGE",
86
+ message: `Unknown flag: ${arg}`,
87
+ });
88
+ }
89
+ }
90
+ i++;
91
+ }
92
+ return out;
93
+ }
94
+ export async function cmdTaskUpdate(opts) {
95
+ const flags = parseTaskUpdateFlags(opts.args);
96
+ try {
97
+ const { backend, config } = await loadTaskBackend({
98
+ cwd: opts.cwd,
99
+ rootOverride: opts.rootOverride ?? null,
100
+ });
101
+ const task = await backend.getTask(flags.taskId);
102
+ if (!task) {
103
+ throw new CliError({
104
+ exitCode: 2,
105
+ code: "E_USAGE",
106
+ message: unknownEntityMessage("task id", flags.taskId),
107
+ });
108
+ }
109
+ const next = { ...task };
110
+ if (flags.title !== undefined)
111
+ next.title = flags.title;
112
+ if (flags.description !== undefined)
113
+ next.description = flags.description;
114
+ if (flags.priority !== undefined)
115
+ next.priority = flags.priority;
116
+ if (flags.owner !== undefined)
117
+ next.owner = flags.owner;
118
+ const existingTags = flags.replaceTags ? [] : dedupeStrings(toStringArray(next.tags));
119
+ const mergedTags = dedupeStrings([...existingTags, ...flags.tags]);
120
+ next.tags = mergedTags;
121
+ const existingDepends = flags.replaceDependsOn
122
+ ? []
123
+ : dedupeStrings(toStringArray(next.depends_on));
124
+ const mergedDepends = dedupeStrings([...existingDepends, ...flags.dependsOn]);
125
+ next.depends_on = mergedDepends;
126
+ const existingVerify = flags.replaceVerify ? [] : dedupeStrings(toStringArray(next.verify));
127
+ const mergedVerify = dedupeStrings([...existingVerify, ...flags.verify]);
128
+ next.verify = mergedVerify;
129
+ if (requiresVerify(mergedTags, config.tasks.verify.required_tags) &&
130
+ mergedVerify.length === 0) {
131
+ throw new CliError({
132
+ exitCode: 2,
133
+ code: "E_USAGE",
134
+ message: "verify commands are required for tasks with code/backend/frontend tags",
135
+ });
136
+ }
137
+ await backend.writeTask(next);
138
+ process.stdout.write(`${successMessage("updated", flags.taskId)}\n`);
139
+ return 0;
140
+ }
141
+ catch (err) {
142
+ throw mapBackendError(err, { command: "task update", root: opts.rootOverride ?? null });
143
+ }
144
+ }
@@ -0,0 +1,14 @@
1
+ export declare const VERIFY_USAGE = "Usage: agentplane verify <task-id> [--cwd <path>] [--log <path>] [--skip-if-unchanged] [--quiet] [--require] [--yes]";
2
+ export declare const VERIFY_USAGE_EXAMPLE = "agentplane verify 202602030608-F1Q8AB";
3
+ export declare function cmdVerify(opts: {
4
+ cwd: string;
5
+ rootOverride?: string;
6
+ taskId: string;
7
+ execCwd?: string;
8
+ logPath?: string;
9
+ skipIfUnchanged: boolean;
10
+ quiet: boolean;
11
+ require: boolean;
12
+ yes: boolean;
13
+ }): Promise<number>;
14
+ //# sourceMappingURL=verify.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"verify.d.ts","sourceRoot":"","sources":["../../../src/commands/task/verify.ts"],"names":[],"mappings":"AAoCA,eAAO,MAAM,YAAY,yHAC+F,CAAC;AACzH,eAAO,MAAM,oBAAoB,0CAA0C,CAAC;AAsG5E,wBAAsB,SAAS,CAAC,IAAI,EAAE;IACpC,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,eAAe,EAAE,OAAO,CAAC;IACzB,KAAK,EAAE,OAAO,CAAC;IACf,OAAO,EAAE,OAAO,CAAC;IACjB,GAAG,EAAE,OAAO,CAAC;CACd,GAAG,OAAO,CAAC,MAAM,CAAC,CA4RlB"}