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.
Files changed (138) 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/backends/task-backend.d.ts +16 -0
  5. package/dist/backends/task-backend.d.ts.map +1 -1
  6. package/dist/backends/task-backend.js +44 -0
  7. package/dist/backends/task-index.d.ts.map +1 -1
  8. package/dist/backends/task-index.js +3 -6
  9. package/dist/cli/command-guide.d.ts.map +1 -1
  10. package/dist/cli/command-guide.js +4 -4
  11. package/dist/cli/help.d.ts.map +1 -1
  12. package/dist/cli/help.js +7 -5
  13. package/dist/cli/run-cli.d.ts.map +1 -1
  14. package/dist/cli/run-cli.js +39 -78
  15. package/dist/commands/backend.d.ts.map +1 -1
  16. package/dist/commands/backend.js +17 -2
  17. package/dist/commands/branch/index.d.ts +60 -0
  18. package/dist/commands/branch/index.d.ts.map +1 -0
  19. package/dist/commands/branch/index.js +513 -0
  20. package/dist/commands/guard/index.d.ts +67 -0
  21. package/dist/commands/guard/index.d.ts.map +1 -0
  22. package/dist/commands/guard/index.js +367 -0
  23. package/dist/commands/hooks/index.d.ts +18 -0
  24. package/dist/commands/hooks/index.d.ts.map +1 -0
  25. package/dist/commands/hooks/index.js +290 -0
  26. package/dist/commands/pr/index.d.ts +46 -0
  27. package/dist/commands/pr/index.d.ts.map +1 -0
  28. package/dist/commands/pr/index.js +857 -0
  29. package/dist/commands/recipes.d.ts.map +1 -1
  30. package/dist/commands/recipes.js +67 -23
  31. package/dist/commands/shared/git-diff.d.ts +9 -0
  32. package/dist/commands/shared/git-diff.d.ts.map +1 -0
  33. package/dist/commands/shared/git-diff.js +41 -0
  34. package/dist/commands/shared/git-ops.d.ts +24 -0
  35. package/dist/commands/shared/git-ops.d.ts.map +1 -0
  36. package/dist/commands/shared/git-ops.js +181 -0
  37. package/dist/commands/shared/git-worktree.d.ts +8 -0
  38. package/dist/commands/shared/git-worktree.d.ts.map +1 -0
  39. package/dist/commands/shared/git-worktree.js +48 -0
  40. package/dist/commands/shared/git.d.ts +4 -0
  41. package/dist/commands/shared/git.d.ts.map +1 -0
  42. package/dist/commands/shared/git.js +14 -0
  43. package/dist/commands/shared/network-approval.d.ts +8 -0
  44. package/dist/commands/shared/network-approval.d.ts.map +1 -0
  45. package/dist/commands/shared/network-approval.js +25 -0
  46. package/dist/commands/shared/path.d.ts +3 -0
  47. package/dist/commands/shared/path.d.ts.map +1 -0
  48. package/dist/commands/shared/path.js +14 -0
  49. package/dist/commands/shared/pr-meta.d.ts +21 -0
  50. package/dist/commands/shared/pr-meta.d.ts.map +1 -0
  51. package/dist/commands/shared/pr-meta.js +72 -0
  52. package/dist/commands/shared/task-backend.d.ts +15 -0
  53. package/dist/commands/shared/task-backend.d.ts.map +1 -0
  54. package/dist/commands/shared/task-backend.js +61 -0
  55. package/dist/commands/task/add.d.ts +8 -0
  56. package/dist/commands/task/add.d.ts.map +1 -0
  57. package/dist/commands/task/add.js +164 -0
  58. package/dist/commands/task/block.d.ts +19 -0
  59. package/dist/commands/task/block.d.ts.map +1 -0
  60. package/dist/commands/task/block.js +86 -0
  61. package/dist/commands/task/comment.d.ts +8 -0
  62. package/dist/commands/task/comment.d.ts.map +1 -0
  63. package/dist/commands/task/comment.js +29 -0
  64. package/dist/commands/task/doc.d.ts +17 -0
  65. package/dist/commands/task/doc.d.ts.map +1 -0
  66. package/dist/commands/task/doc.js +220 -0
  67. package/dist/commands/task/export.d.ts +5 -0
  68. package/dist/commands/task/export.d.ts.map +1 -0
  69. package/dist/commands/task/export.js +27 -0
  70. package/dist/commands/task/finish.d.ts +27 -0
  71. package/dist/commands/task/finish.d.ts.map +1 -0
  72. package/dist/commands/task/finish.js +132 -0
  73. package/dist/commands/task/index.d.ts +26 -0
  74. package/dist/commands/task/index.d.ts.map +1 -0
  75. package/dist/commands/task/index.js +25 -0
  76. package/dist/commands/task/lint.d.ts +5 -0
  77. package/dist/commands/task/lint.d.ts.map +1 -0
  78. package/dist/commands/task/lint.js +22 -0
  79. package/dist/commands/task/list.d.ts +11 -0
  80. package/dist/commands/task/list.d.ts.map +1 -0
  81. package/dist/commands/task/list.js +54 -0
  82. package/dist/commands/task/migrate-doc.d.ts +8 -0
  83. package/dist/commands/task/migrate-doc.d.ts.map +1 -0
  84. package/dist/commands/task/migrate-doc.js +147 -0
  85. package/dist/commands/task/migrate.d.ts +6 -0
  86. package/dist/commands/task/migrate.d.ts.map +1 -0
  87. package/dist/commands/task/migrate.js +70 -0
  88. package/dist/commands/task/new.d.ts +8 -0
  89. package/dist/commands/task/new.d.ts.map +1 -0
  90. package/dist/commands/task/new.js +117 -0
  91. package/dist/commands/task/next.d.ts +6 -0
  92. package/dist/commands/task/next.d.ts.map +1 -0
  93. package/dist/commands/task/next.js +45 -0
  94. package/dist/commands/task/normalize.d.ts +6 -0
  95. package/dist/commands/task/normalize.d.ts.map +1 -0
  96. package/dist/commands/task/normalize.js +46 -0
  97. package/dist/commands/task/plan.d.ts +14 -0
  98. package/dist/commands/task/plan.d.ts.map +1 -0
  99. package/dist/commands/task/plan.js +217 -0
  100. package/dist/commands/task/ready.d.ts +6 -0
  101. package/dist/commands/task/ready.d.ts.map +1 -0
  102. package/dist/commands/task/ready.js +57 -0
  103. package/dist/commands/task/scaffold.d.ts +8 -0
  104. package/dist/commands/task/scaffold.d.ts.map +1 -0
  105. package/dist/commands/task/scaffold.js +142 -0
  106. package/dist/commands/task/scrub.d.ts +8 -0
  107. package/dist/commands/task/scrub.d.ts.map +1 -0
  108. package/dist/commands/task/scrub.js +121 -0
  109. package/dist/commands/task/search.d.ts +7 -0
  110. package/dist/commands/task/search.d.ts.map +1 -0
  111. package/dist/commands/task/search.js +79 -0
  112. package/dist/commands/task/set-status.d.ts +19 -0
  113. package/dist/commands/task/set-status.d.ts.map +1 -0
  114. package/dist/commands/task/set-status.js +123 -0
  115. package/dist/commands/task/shared.d.ts +48 -0
  116. package/dist/commands/task/shared.d.ts.map +1 -0
  117. package/dist/commands/task/shared.js +312 -0
  118. package/dist/commands/task/show.d.ts +6 -0
  119. package/dist/commands/task/show.d.ts.map +1 -0
  120. package/dist/commands/task/show.js +35 -0
  121. package/dist/commands/task/start.d.ts +19 -0
  122. package/dist/commands/task/start.d.ts.map +1 -0
  123. package/dist/commands/task/start.js +110 -0
  124. package/dist/commands/task/update.d.ts +8 -0
  125. package/dist/commands/task/update.d.ts.map +1 -0
  126. package/dist/commands/task/update.js +144 -0
  127. package/dist/commands/task/verify-record.d.ts +16 -0
  128. package/dist/commands/task/verify-record.d.ts.map +1 -0
  129. package/dist/commands/task/verify-record.js +277 -0
  130. package/dist/commands/task/verify.d.ts +2 -0
  131. package/dist/commands/task/verify.d.ts.map +1 -0
  132. package/dist/commands/task/verify.js +1 -0
  133. package/dist/commands/upgrade.d.ts.map +1 -1
  134. package/dist/commands/upgrade.js +17 -2
  135. package/dist/commands/workflow.d.ts +5 -364
  136. package/dist/commands/workflow.d.ts.map +1 -1
  137. package/dist/commands/workflow.js +6 -4617
  138. package/package.json +2 -2
@@ -0,0 +1,110 @@
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, ensurePlanApprovedIfRequired, 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
+ ensurePlanApprovedIfRequired(task, loaded.config);
34
+ const currentStatus = String(task.status || "TODO").toUpperCase();
35
+ if (!opts.force && !isTransitionAllowed(currentStatus, "DOING")) {
36
+ throw new CliError({
37
+ exitCode: 2,
38
+ code: "E_USAGE",
39
+ message: `Refusing status transition ${currentStatus} -> DOING (use --force to override)`,
40
+ });
41
+ }
42
+ if (!opts.force) {
43
+ const allTasks = await backend.listTasks();
44
+ const depState = buildDependencyState(allTasks);
45
+ const dep = depState.get(task.id);
46
+ if (dep && (dep.missing.length > 0 || dep.incomplete.length > 0)) {
47
+ if (!opts.quiet) {
48
+ if (dep.missing.length > 0) {
49
+ process.stderr.write(`${warnMessage(`missing deps: ${dep.missing.join(", ")}`)}\n`);
50
+ }
51
+ if (dep.incomplete.length > 0) {
52
+ process.stderr.write(`${warnMessage(`incomplete deps: ${dep.incomplete.join(", ")}`)}\n`);
53
+ }
54
+ }
55
+ throw new CliError({
56
+ exitCode: 2,
57
+ code: "E_USAGE",
58
+ message: `Task is not ready: ${task.id} (use --force to override)`,
59
+ });
60
+ }
61
+ }
62
+ const formattedComment = opts.commitFromComment
63
+ ? formatCommentBodyForCommit(opts.body, loaded.config)
64
+ : null;
65
+ const commentBody = formattedComment ?? opts.body;
66
+ const existingComments = Array.isArray(task.comments)
67
+ ? task.comments.filter((item) => !!item && typeof item.author === "string" && typeof item.body === "string")
68
+ : [];
69
+ const commentsValue = [
70
+ ...existingComments,
71
+ { author: opts.author, body: commentBody },
72
+ ];
73
+ const nextTask = {
74
+ ...task,
75
+ status: "DOING",
76
+ comments: commentsValue,
77
+ doc_version: 2,
78
+ doc_updated_at: nowIso(),
79
+ doc_updated_by: opts.author,
80
+ };
81
+ await backend.writeTask(nextTask);
82
+ let commitInfo = null;
83
+ if (opts.commitFromComment) {
84
+ commitInfo = await commitFromComment({
85
+ cwd: opts.cwd,
86
+ rootOverride: opts.rootOverride,
87
+ taskId: opts.taskId,
88
+ commentBody: opts.body,
89
+ formattedComment,
90
+ emoji: opts.commitEmoji ?? "🚧",
91
+ allow: opts.commitAllow,
92
+ autoAllow: opts.commitAutoAllow || opts.commitAllow.length === 0,
93
+ allowTasks: opts.commitAllowTasks,
94
+ requireClean: opts.commitRequireClean,
95
+ quiet: opts.quiet,
96
+ config: loaded.config,
97
+ });
98
+ }
99
+ if (!opts.quiet) {
100
+ const suffix = commitInfo ? ` (commit=${commitInfo.hash.slice(0, 12)})` : "";
101
+ process.stdout.write(`${successMessage("started", `${opts.taskId}${suffix}`)}\n`);
102
+ }
103
+ return 0;
104
+ }
105
+ catch (err) {
106
+ if (err instanceof CliError)
107
+ throw err;
108
+ throw mapBackendError(err, { command: "start", root: opts.rootOverride ?? null });
109
+ }
110
+ }
@@ -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,16 @@
1
+ export declare const TASK_VERIFY_USAGE = "Usage: agentplane task verify <ok|rework> <task-id> --by <id> --note <text> [--details <text> | --file <path>]";
2
+ export declare const TASK_VERIFY_USAGE_EXAMPLE = "agentplane task verify ok 202602030608-F1Q8AB --by REVIEWER --note \"Looks good\"";
3
+ export declare const VERIFY_USAGE = "Usage: agentplane verify <task-id> (--ok | --rework) --by <id> --note <text> [--details <text> | --file <path>] [--quiet]";
4
+ export declare const VERIFY_USAGE_EXAMPLE = "agentplane verify 202602030608-F1Q8AB --ok --by REVIEWER --note \"Looks good\"";
5
+ export declare function cmdTaskVerify(opts: {
6
+ cwd: string;
7
+ rootOverride?: string;
8
+ args: string[];
9
+ }): Promise<number>;
10
+ export declare function cmdVerify(opts: {
11
+ cwd: string;
12
+ rootOverride?: string;
13
+ taskId: string;
14
+ args: string[];
15
+ }): Promise<number>;
16
+ //# sourceMappingURL=verify-record.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"verify-record.d.ts","sourceRoot":"","sources":["../../../src/commands/task/verify-record.ts"],"names":[],"mappings":"AAiBA,eAAO,MAAM,iBAAiB,mHACoF,CAAC;AACnH,eAAO,MAAM,yBAAyB,sFAC6C,CAAC;AAEpF,eAAO,MAAM,YAAY,8HACoG,CAAC;AAC9H,eAAO,MAAM,oBAAoB,mFAC+C,CAAC;AA0MjF,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;AAED,wBAAsB,SAAS,CAAC,IAAI,EAAE;IACpC,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,EAAE,CAAC;CAChB,GAAG,OAAO,CAAC,MAAM,CAAC,CA8ClB"}
@@ -0,0 +1,277 @@
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
+ const RESULTS_BEGIN = "<!-- BEGIN VERIFICATION RESULTS -->";
10
+ const RESULTS_END = "<!-- END VERIFICATION RESULTS -->";
11
+ export const TASK_VERIFY_USAGE = "Usage: agentplane task verify <ok|rework> <task-id> --by <id> --note <text> [--details <text> | --file <path>]";
12
+ export const TASK_VERIFY_USAGE_EXAMPLE = 'agentplane task verify ok 202602030608-F1Q8AB --by REVIEWER --note "Looks good"';
13
+ export const VERIFY_USAGE = "Usage: agentplane verify <task-id> (--ok | --rework) --by <id> --note <text> [--details <text> | --file <path>] [--quiet]";
14
+ export const VERIFY_USAGE_EXAMPLE = 'agentplane verify 202602030608-F1Q8AB --ok --by REVIEWER --note "Looks good"';
15
+ function extractDocSection(doc, sectionName) {
16
+ const lines = doc.replaceAll("\r\n", "\n").split("\n");
17
+ let capturing = false;
18
+ const out = [];
19
+ for (const line of lines) {
20
+ const match = /^##\s+(.*)$/.exec(line.trim());
21
+ if (match) {
22
+ if (capturing)
23
+ break;
24
+ capturing = (match[1] ?? "").trim() === sectionName;
25
+ continue;
26
+ }
27
+ if (capturing)
28
+ out.push(line);
29
+ }
30
+ if (!capturing)
31
+ return null;
32
+ return out.join("\n").trimEnd();
33
+ }
34
+ function ensureVerificationResultsMarkers(sectionText) {
35
+ const normalized = sectionText.replaceAll("\r\n", "\n").trimEnd();
36
+ if (!normalized) {
37
+ return ["### Plan", "", "", "### Results", "", "", RESULTS_BEGIN, RESULTS_END].join("\n");
38
+ }
39
+ const hasBegin = normalized.includes(RESULTS_BEGIN);
40
+ const hasEnd = normalized.includes(RESULTS_END);
41
+ if (hasBegin && hasEnd)
42
+ return normalized;
43
+ const out = [normalized];
44
+ if (!normalized.endsWith("\n"))
45
+ out.push("");
46
+ out.push("", RESULTS_BEGIN, RESULTS_END);
47
+ return out.join("\n").trimEnd();
48
+ }
49
+ function appendBetweenMarkers(sectionText, entryText) {
50
+ const ensured = ensureVerificationResultsMarkers(sectionText);
51
+ const beginIdx = ensured.indexOf(RESULTS_BEGIN);
52
+ const endIdx = ensured.indexOf(RESULTS_END);
53
+ if (beginIdx === -1 || endIdx === -1 || endIdx <= beginIdx) {
54
+ throw new Error("Verification results markers are malformed");
55
+ }
56
+ const beforeEnd = ensured.slice(0, endIdx).trimEnd();
57
+ const afterEnd = ensured.slice(endIdx).trimStart();
58
+ const entry = entryText.trimEnd();
59
+ const parts = [
60
+ beforeEnd,
61
+ ...(beforeEnd.endsWith(RESULTS_BEGIN) ? [] : [""]),
62
+ entry,
63
+ "",
64
+ afterEnd,
65
+ ];
66
+ return parts.join("\n").trimEnd();
67
+ }
68
+ function renderVerificationEntry(opts) {
69
+ const lines = [
70
+ `#### ${opts.at} — VERIFY — ${opts.state}`,
71
+ "",
72
+ `By: ${opts.by}`,
73
+ "",
74
+ `Note: ${opts.note}`,
75
+ ];
76
+ const details = (opts.details ?? "").trim();
77
+ if (details) {
78
+ lines.push("", "Details:", "", details);
79
+ }
80
+ return `${lines.join("\n").trimEnd()}\n`;
81
+ }
82
+ function parseVerifyRecordFlags(args) {
83
+ const out = { quiet: false, ok: false, rework: false };
84
+ for (let i = 0; i < args.length; i++) {
85
+ const arg = args[i];
86
+ if (!arg)
87
+ continue;
88
+ if (arg === "--ok") {
89
+ out.ok = true;
90
+ continue;
91
+ }
92
+ if (arg === "--rework") {
93
+ out.rework = true;
94
+ continue;
95
+ }
96
+ if (arg === "--quiet") {
97
+ out.quiet = true;
98
+ continue;
99
+ }
100
+ if (!arg.startsWith("--")) {
101
+ throw new CliError({ exitCode: 2, code: "E_USAGE", message: `Unexpected argument: ${arg}` });
102
+ }
103
+ const next = args[i + 1];
104
+ if (!next) {
105
+ throw new CliError({ exitCode: 2, code: "E_USAGE", message: missingValueMessage(arg) });
106
+ }
107
+ switch (arg) {
108
+ case "--by": {
109
+ out.by = next;
110
+ break;
111
+ }
112
+ case "--note": {
113
+ out.note = next;
114
+ break;
115
+ }
116
+ case "--details": {
117
+ out.details = next;
118
+ break;
119
+ }
120
+ case "--file": {
121
+ out.file = next;
122
+ break;
123
+ }
124
+ default: {
125
+ throw new CliError({ exitCode: 2, code: "E_USAGE", message: `Unknown flag: ${arg}` });
126
+ }
127
+ }
128
+ i++;
129
+ }
130
+ return out;
131
+ }
132
+ async function recordVerificationResult(opts) {
133
+ const { backend, config, resolved, task } = await loadBackendTask({
134
+ cwd: opts.cwd,
135
+ rootOverride: opts.rootOverride,
136
+ taskId: opts.taskId,
137
+ });
138
+ if (!backend.getTaskDoc || !backend.writeTask) {
139
+ throw new CliError({
140
+ exitCode: 2,
141
+ code: "E_USAGE",
142
+ message: backendNotSupportedMessage("task docs"),
143
+ });
144
+ }
145
+ const existingDoc = (typeof task.doc === "string" ? task.doc : "") || (await backend.getTaskDoc(task.id));
146
+ const baseDoc = ensureDocSections(existingDoc ?? "", config.tasks.doc.required_sections);
147
+ const verificationSection = extractDocSection(baseDoc, "Verification") ?? "";
148
+ const at = nowIso();
149
+ const entry = renderVerificationEntry({
150
+ at,
151
+ state: opts.state,
152
+ by: opts.by,
153
+ note: opts.note,
154
+ details: opts.details ?? null,
155
+ });
156
+ const nextVerification = appendBetweenMarkers(verificationSection, entry);
157
+ const nextDoc = ensureDocSections(setMarkdownSection(baseDoc, "Verification", nextVerification), config.tasks.doc.required_sections);
158
+ await backend.writeTask({
159
+ ...task,
160
+ status: opts.state === "needs_rework" ? "DOING" : task.status,
161
+ commit: opts.state === "needs_rework" ? null : (task.commit ?? null),
162
+ doc: nextDoc,
163
+ doc_updated_by: opts.by,
164
+ verification: {
165
+ state: opts.state,
166
+ updated_at: at,
167
+ updated_by: opts.by,
168
+ note: opts.note,
169
+ },
170
+ });
171
+ if (!opts.quiet) {
172
+ const readmePath = path.join(resolved.gitRoot, config.paths.workflow_dir, task.id, "README.md");
173
+ process.stdout.write(`${readmePath}\n`);
174
+ }
175
+ }
176
+ export async function cmdTaskVerify(opts) {
177
+ const [subcommand, taskId, ...restArgs] = opts.args;
178
+ if (!subcommand || !taskId || (subcommand !== "ok" && subcommand !== "rework")) {
179
+ throw new CliError({
180
+ exitCode: 2,
181
+ code: "E_USAGE",
182
+ message: usageMessage(TASK_VERIFY_USAGE, TASK_VERIFY_USAGE_EXAMPLE),
183
+ });
184
+ }
185
+ const flags = parseVerifyRecordFlags(restArgs);
186
+ const by = (flags.by ?? "").trim();
187
+ const note = (flags.note ?? "").trim();
188
+ if (flags.details && flags.file) {
189
+ throw new CliError({
190
+ exitCode: 2,
191
+ code: "E_USAGE",
192
+ message: usageMessage(TASK_VERIFY_USAGE, TASK_VERIFY_USAGE_EXAMPLE),
193
+ });
194
+ }
195
+ if (!by || !note) {
196
+ throw new CliError({
197
+ exitCode: 2,
198
+ code: "E_USAGE",
199
+ message: usageMessage(TASK_VERIFY_USAGE, TASK_VERIFY_USAGE_EXAMPLE),
200
+ });
201
+ }
202
+ let details = flags.details ?? null;
203
+ if (flags.file) {
204
+ try {
205
+ details = await readFile(path.resolve(opts.cwd, flags.file), "utf8");
206
+ }
207
+ catch (err) {
208
+ throw mapCoreError(err, { command: "task verify", filePath: flags.file });
209
+ }
210
+ }
211
+ try {
212
+ await recordVerificationResult({
213
+ cwd: opts.cwd,
214
+ rootOverride: opts.rootOverride,
215
+ taskId,
216
+ state: subcommand === "ok" ? "ok" : "needs_rework",
217
+ by,
218
+ note,
219
+ details,
220
+ quiet: flags.quiet,
221
+ });
222
+ return 0;
223
+ }
224
+ catch (err) {
225
+ if (err instanceof CliError)
226
+ throw err;
227
+ throw mapBackendError(err, { command: "task verify", root: opts.rootOverride ?? null });
228
+ }
229
+ }
230
+ export async function cmdVerify(opts) {
231
+ const flags = parseVerifyRecordFlags(opts.args);
232
+ const by = (flags.by ?? "").trim();
233
+ const note = (flags.note ?? "").trim();
234
+ const ok = flags.ok;
235
+ const rework = flags.rework;
236
+ if (flags.details && flags.file) {
237
+ throw new CliError({
238
+ exitCode: 2,
239
+ code: "E_USAGE",
240
+ message: usageMessage(VERIFY_USAGE, VERIFY_USAGE_EXAMPLE),
241
+ });
242
+ }
243
+ if ((ok && rework) || (!ok && !rework) || !by || !note) {
244
+ throw new CliError({
245
+ exitCode: 2,
246
+ code: "E_USAGE",
247
+ message: usageMessage(VERIFY_USAGE, VERIFY_USAGE_EXAMPLE),
248
+ });
249
+ }
250
+ let details = flags.details ?? null;
251
+ if (flags.file) {
252
+ try {
253
+ details = await readFile(path.resolve(opts.cwd, flags.file), "utf8");
254
+ }
255
+ catch (err) {
256
+ throw mapCoreError(err, { command: "verify", filePath: flags.file });
257
+ }
258
+ }
259
+ try {
260
+ await recordVerificationResult({
261
+ cwd: opts.cwd,
262
+ rootOverride: opts.rootOverride,
263
+ taskId: opts.taskId,
264
+ state: ok ? "ok" : "needs_rework",
265
+ by,
266
+ note,
267
+ details,
268
+ quiet: flags.quiet,
269
+ });
270
+ return 0;
271
+ }
272
+ catch (err) {
273
+ if (err instanceof CliError)
274
+ throw err;
275
+ throw mapBackendError(err, { command: "verify", root: opts.rootOverride ?? null });
276
+ }
277
+ }
@@ -0,0 +1,2 @@
1
+ export { VERIFY_USAGE, VERIFY_USAGE_EXAMPLE, cmdVerify } from "./verify-record.js";
2
+ //# sourceMappingURL=verify.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"verify.d.ts","sourceRoot":"","sources":["../../../src/commands/task/verify.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,oBAAoB,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC"}
@@ -0,0 +1 @@
1
+ export { VERIFY_USAGE, VERIFY_USAGE_EXAMPLE, cmdVerify } from "./verify-record.js";
@@ -1 +1 @@
1
- {"version":3,"file":"upgrade.d.ts","sourceRoot":"","sources":["../../src/commands/upgrade.ts"],"names":[],"mappings":"AAwJA,wBAAsB,UAAU,CAAC,IAAI,EAAE;IACrC,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,IAAI,EAAE,MAAM,EAAE,CAAC;CAChB,GAAG,OAAO,CAAC,MAAM,CAAC,CA2KlB"}
1
+ {"version":3,"file":"upgrade.d.ts","sourceRoot":"","sources":["../../src/commands/upgrade.ts"],"names":[],"mappings":"AA8JA,wBAAsB,UAAU,CAAC,IAAI,EAAE;IACrC,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,IAAI,EAAE,MAAM,EAAE,CAAC;CAChB,GAAG,OAAO,CAAC,MAAM,CAAC,CAsLlB"}
@@ -9,12 +9,13 @@ import { extractArchive } from "../cli/archive.js";
9
9
  import { invalidFieldMessage, invalidValueMessage, missingValueMessage, requiredFieldMessage, usageMessage, } from "../cli/output.js";
10
10
  import { exitCodeForError } from "../cli/exit-codes.js";
11
11
  import { CliError } from "../shared/errors.js";
12
+ import { ensureNetworkApproved } from "./shared/network-approval.js";
12
13
  const DEFAULT_UPGRADE_ASSET = "agentplane-upgrade.tar.gz";
13
14
  const DEFAULT_UPGRADE_CHECKSUM_ASSET = "agentplane-upgrade.tar.gz.sha256";
14
- const UPGRADE_USAGE = "Usage: agentplane upgrade [--tag <tag>] [--dry-run] [--no-backup] [--source <repo-url>] [--bundle <path|url>] [--checksum <path|url>]";
15
+ const UPGRADE_USAGE = "Usage: agentplane upgrade [--tag <tag>] [--dry-run] [--no-backup] [--source <repo-url>] [--bundle <path|url>] [--checksum <path|url>] [--yes]";
15
16
  const UPGRADE_USAGE_EXAMPLE = "agentplane upgrade --tag v0.1.4 --dry-run";
16
17
  function parseUpgradeFlags(args) {
17
- const out = { dryRun: false, backup: true };
18
+ const out = { dryRun: false, backup: true, yes: false };
18
19
  for (let i = 0; i < args.length; i++) {
19
20
  const arg = args[i];
20
21
  if (!arg)
@@ -34,6 +35,10 @@ function parseUpgradeFlags(args) {
34
35
  out.backup = false;
35
36
  continue;
36
37
  }
38
+ if (arg === "--yes") {
39
+ out.yes = true;
40
+ continue;
41
+ }
37
42
  const next = args[i + 1];
38
43
  if (!next) {
39
44
  throw new CliError({ exitCode: 2, code: "E_USAGE", message: missingValueMessage(arg) });
@@ -136,6 +141,13 @@ export async function cmdUpgrade(opts) {
136
141
  });
137
142
  const loaded = await loadConfig(resolved.agentplaneDir);
138
143
  const source = flags.source ?? loaded.config.framework.source;
144
+ let networkApproved = false;
145
+ const ensureApproved = async (reason) => {
146
+ if (networkApproved)
147
+ return;
148
+ await ensureNetworkApproved({ config: loaded.config, yes: flags.yes, reason });
149
+ networkApproved = true;
150
+ };
139
151
  let tempRoot = null;
140
152
  let extractRoot = null;
141
153
  try {
@@ -146,6 +158,7 @@ export async function cmdUpgrade(opts) {
146
158
  const isUrl = flags.bundle.startsWith("http://") || flags.bundle.startsWith("https://");
147
159
  bundlePath = isUrl ? path.join(tempRoot, "bundle.tar.gz") : path.resolve(flags.bundle);
148
160
  if (isUrl) {
161
+ await ensureApproved("upgrade downloads the bundle/checksum from the network");
149
162
  await downloadToFile(flags.bundle, bundlePath);
150
163
  }
151
164
  const checksumValue = flags.checksum ?? "";
@@ -154,6 +167,7 @@ export async function cmdUpgrade(opts) {
154
167
  ? path.join(tempRoot, "bundle.tar.gz.sha256")
155
168
  : path.resolve(checksumValue);
156
169
  if (checksumIsUrl) {
170
+ await ensureApproved("upgrade downloads the bundle/checksum from the network");
157
171
  await downloadToFile(checksumValue, checksumPath);
158
172
  }
159
173
  }
@@ -162,6 +176,7 @@ export async function cmdUpgrade(opts) {
162
176
  const releaseUrl = flags.tag
163
177
  ? `https://api.github.com/repos/${owner}/${repo}/releases/tags/${flags.tag}`
164
178
  : `https://api.github.com/repos/${owner}/${repo}/releases/latest`;
179
+ await ensureApproved("upgrade fetches release metadata and downloads assets from the network");
165
180
  const release = (await fetchJson(releaseUrl));
166
181
  const assets = Array.isArray(release.assets) ? release.assets : [];
167
182
  const assetName = flags.asset ?? DEFAULT_UPGRADE_ASSET;