agentplane 0.3.5 → 0.3.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 (256) hide show
  1. package/README.md +103 -75
  2. package/assets/AGENTS.md +4 -2
  3. package/bin/dist-guard.js +13 -3
  4. package/bin/runtime-watch.d.ts +1 -0
  5. package/bin/runtime-watch.js +22 -5
  6. package/bin/stale-dist-policy.js +9 -2
  7. package/dist/.build-manifest.json +251 -821
  8. package/dist/adapters/task-backend/task-backend-adapter.d.ts +2 -2
  9. package/dist/adapters/task-backend/task-backend-adapter.d.ts.map +1 -1
  10. package/dist/adapters/task-backend/task-backend-adapter.js +2 -2
  11. package/dist/backends/task-backend/local-backend.d.ts +7 -5
  12. package/dist/backends/task-backend/local-backend.d.ts.map +1 -1
  13. package/dist/backends/task-backend/local-backend.js +79 -7
  14. package/dist/backends/task-backend/redmine/env.d.ts +1 -1
  15. package/dist/backends/task-backend/redmine/env.d.ts.map +1 -1
  16. package/dist/backends/task-backend/redmine/env.js +3 -0
  17. package/dist/backends/task-backend/redmine/inspect.d.ts +11 -0
  18. package/dist/backends/task-backend/redmine/inspect.d.ts.map +1 -0
  19. package/dist/backends/task-backend/redmine/inspect.js +75 -0
  20. package/dist/backends/task-backend/redmine/mapping.d.ts.map +1 -1
  21. package/dist/backends/task-backend/redmine/mapping.js +21 -2
  22. package/dist/backends/task-backend/redmine/state.d.ts +17 -0
  23. package/dist/backends/task-backend/redmine/state.d.ts.map +1 -0
  24. package/dist/backends/task-backend/redmine/state.js +95 -0
  25. package/dist/backends/task-backend/redmine-backend.d.ts +10 -16
  26. package/dist/backends/task-backend/redmine-backend.d.ts.map +1 -1
  27. package/dist/backends/task-backend/redmine-backend.js +205 -15
  28. package/dist/backends/task-backend/shared/constants.d.ts +1 -1
  29. package/dist/backends/task-backend/shared/constants.js +1 -1
  30. package/dist/backends/task-backend/shared/record.d.ts.map +1 -1
  31. package/dist/backends/task-backend/shared/record.js +20 -1
  32. package/dist/backends/task-backend/shared/types.d.ts +42 -4
  33. package/dist/backends/task-backend/shared/types.d.ts.map +1 -1
  34. package/dist/backends/task-backend/shared.d.ts +1 -1
  35. package/dist/backends/task-backend/shared.d.ts.map +1 -1
  36. package/dist/backends/task-backend.d.ts +1 -1
  37. package/dist/backends/task-backend.d.ts.map +1 -1
  38. package/dist/backends/task-backend.test-helpers.d.ts +4 -0
  39. package/dist/backends/task-backend.test-helpers.d.ts.map +1 -0
  40. package/dist/backends/task-backend.test-helpers.js +33 -0
  41. package/dist/backends/task-index.d.ts.map +1 -1
  42. package/dist/backends/task-index.js +1 -0
  43. package/dist/cli/bootstrap-guide.d.ts.map +1 -1
  44. package/dist/cli/bootstrap-guide.js +1 -0
  45. package/dist/cli/command-guide.d.ts.map +1 -1
  46. package/dist/cli/command-guide.js +3 -2
  47. package/dist/cli/reason-codes.d.ts.map +1 -1
  48. package/dist/cli/reason-codes.js +30 -0
  49. package/dist/cli/run-cli/command-catalog/core.d.ts +3 -0
  50. package/dist/cli/run-cli/command-catalog/core.d.ts.map +1 -0
  51. package/dist/cli/run-cli/command-catalog/core.js +137 -0
  52. package/dist/cli/run-cli/command-catalog/lifecycle.d.ts +3 -0
  53. package/dist/cli/run-cli/command-catalog/lifecycle.d.ts.map +1 -0
  54. package/dist/cli/run-cli/command-catalog/lifecycle.js +52 -0
  55. package/dist/cli/run-cli/command-catalog/project.d.ts +3 -0
  56. package/dist/cli/run-cli/command-catalog/project.d.ts.map +1 -0
  57. package/dist/cli/run-cli/command-catalog/project.js +80 -0
  58. package/dist/cli/run-cli/command-catalog/shared.d.ts +19 -0
  59. package/dist/cli/run-cli/command-catalog/shared.d.ts.map +1 -0
  60. package/dist/cli/run-cli/command-catalog/shared.js +9 -0
  61. package/dist/cli/run-cli/command-catalog/task.d.ts +3 -0
  62. package/dist/cli/run-cli/command-catalog/task.d.ts.map +1 -0
  63. package/dist/cli/run-cli/command-catalog/task.js +85 -0
  64. package/dist/cli/run-cli/command-catalog.d.ts +3 -18
  65. package/dist/cli/run-cli/command-catalog.d.ts.map +1 -1
  66. package/dist/cli/run-cli/command-catalog.js +8 -337
  67. package/dist/cli/run-cli/commands/ide.d.ts.map +1 -1
  68. package/dist/cli/run-cli/commands/ide.js +64 -2
  69. package/dist/cli/run-cli/commands/init/ui.d.ts.map +1 -1
  70. package/dist/cli/run-cli/commands/init/ui.js +33 -13
  71. package/dist/cli/run-cli/commands/init/write-env.d.ts.map +1 -1
  72. package/dist/cli/run-cli/commands/init/write-env.js +12 -0
  73. package/dist/cli/run-cli.core.pr-flow.test-helpers.d.ts +3 -0
  74. package/dist/cli/run-cli.core.pr-flow.test-helpers.d.ts.map +1 -0
  75. package/dist/cli/run-cli.core.pr-flow.test-helpers.js +41 -0
  76. package/dist/cli/run-cli.core.tasks.test-helpers.d.ts +2 -0
  77. package/dist/cli/run-cli.core.tasks.test-helpers.d.ts.map +1 -0
  78. package/dist/cli/run-cli.core.tasks.test-helpers.js +6 -0
  79. package/dist/cli/run-cli.test-helpers.d.ts +3 -0
  80. package/dist/cli/run-cli.test-helpers.d.ts.map +1 -1
  81. package/dist/cli/run-cli.test-helpers.js +140 -6
  82. package/dist/commands/backend/sync.command.d.ts +5 -1
  83. package/dist/commands/backend/sync.command.d.ts.map +1 -1
  84. package/dist/commands/backend/sync.command.js +67 -3
  85. package/dist/commands/backend.d.ts +22 -0
  86. package/dist/commands/backend.d.ts.map +1 -1
  87. package/dist/commands/backend.js +110 -1
  88. package/dist/commands/commit.spec.d.ts.map +1 -1
  89. package/dist/commands/commit.spec.js +31 -7
  90. package/dist/commands/doctor/runtime.d.ts.map +1 -1
  91. package/dist/commands/doctor/runtime.js +3 -6
  92. package/dist/commands/doctor/workspace.d.ts +8 -0
  93. package/dist/commands/doctor/workspace.d.ts.map +1 -1
  94. package/dist/commands/doctor/workspace.js +127 -3
  95. package/dist/commands/guard/commit.command.d.ts.map +1 -1
  96. package/dist/commands/guard/commit.command.js +30 -6
  97. package/dist/commands/guard/impl/allow.d.ts +9 -0
  98. package/dist/commands/guard/impl/allow.d.ts.map +1 -1
  99. package/dist/commands/guard/impl/allow.js +26 -10
  100. package/dist/commands/guard/impl/commands.d.ts.map +1 -1
  101. package/dist/commands/guard/impl/commands.js +146 -18
  102. package/dist/commands/guard/impl/comment-commit.d.ts.map +1 -1
  103. package/dist/commands/guard/impl/comment-commit.js +2 -0
  104. package/dist/commands/hooks/index.d.ts.map +1 -1
  105. package/dist/commands/hooks/index.js +8 -35
  106. package/dist/commands/recipes/impl/apply.d.ts +4 -0
  107. package/dist/commands/recipes/impl/apply.d.ts.map +1 -1
  108. package/dist/commands/recipes/impl/apply.js +34 -0
  109. package/dist/commands/recipes/impl/commands/explain.d.ts.map +1 -1
  110. package/dist/commands/recipes/impl/commands/explain.js +70 -11
  111. package/dist/commands/recipes/impl/commands/info.d.ts.map +1 -1
  112. package/dist/commands/recipes/impl/commands/info.js +24 -12
  113. package/dist/commands/recipes/impl/commands/install.d.ts.map +1 -1
  114. package/dist/commands/recipes/impl/commands/install.js +32 -36
  115. package/dist/commands/recipes/impl/commands/list.d.ts.map +1 -1
  116. package/dist/commands/recipes/impl/commands/list.js +7 -4
  117. package/dist/commands/recipes/impl/commands/remove.d.ts.map +1 -1
  118. package/dist/commands/recipes/impl/commands/remove.js +9 -11
  119. package/dist/commands/recipes/impl/constants.d.ts +2 -0
  120. package/dist/commands/recipes/impl/constants.d.ts.map +1 -1
  121. package/dist/commands/recipes/impl/constants.js +2 -0
  122. package/dist/commands/recipes/impl/manifest.d.ts.map +1 -1
  123. package/dist/commands/recipes/impl/manifest.js +219 -23
  124. package/dist/commands/recipes/impl/normalize.d.ts +3 -0
  125. package/dist/commands/recipes/impl/normalize.d.ts.map +1 -1
  126. package/dist/commands/recipes/impl/normalize.js +28 -24
  127. package/dist/commands/recipes/impl/paths.d.ts +9 -0
  128. package/dist/commands/recipes/impl/paths.d.ts.map +1 -1
  129. package/dist/commands/recipes/impl/paths.js +10 -1
  130. package/dist/commands/recipes/impl/project-installed-recipes.d.ts +7 -0
  131. package/dist/commands/recipes/impl/project-installed-recipes.d.ts.map +1 -0
  132. package/dist/commands/recipes/impl/project-installed-recipes.js +102 -0
  133. package/dist/commands/recipes/impl/resolver.d.ts +20 -0
  134. package/dist/commands/recipes/impl/resolver.d.ts.map +1 -0
  135. package/dist/commands/recipes/impl/resolver.js +220 -0
  136. package/dist/commands/recipes/impl/scenario.d.ts.map +1 -1
  137. package/dist/commands/recipes/impl/scenario.js +40 -11
  138. package/dist/commands/recipes/impl/types.d.ts +145 -16
  139. package/dist/commands/recipes/impl/types.d.ts.map +1 -1
  140. package/dist/commands/recipes/install.spec.d.ts.map +1 -1
  141. package/dist/commands/recipes/install.spec.js +3 -2
  142. package/dist/commands/recipes.d.ts +6 -4
  143. package/dist/commands/recipes.d.ts.map +1 -1
  144. package/dist/commands/recipes.js +5 -3
  145. package/dist/commands/recipes.test-helpers.d.ts +185 -0
  146. package/dist/commands/recipes.test-helpers.d.ts.map +1 -0
  147. package/dist/commands/recipes.test-helpers.js +339 -0
  148. package/dist/commands/scenario/impl/commands.d.ts.map +1 -1
  149. package/dist/commands/scenario/impl/commands.js +192 -336
  150. package/dist/commands/scenario/info.command.d.ts.map +1 -1
  151. package/dist/commands/scenario/info.command.js +7 -2
  152. package/dist/commands/scenario/list.command.js +2 -2
  153. package/dist/commands/scenario/run.command.d.ts.map +1 -1
  154. package/dist/commands/scenario/run.command.js +7 -2
  155. package/dist/commands/shared/reconcile-check.d.ts.map +1 -1
  156. package/dist/commands/shared/reconcile-check.js +77 -2
  157. package/dist/commands/shared/task-backend.d.ts +1 -1
  158. package/dist/commands/shared/task-backend.d.ts.map +1 -1
  159. package/dist/commands/shared/task-backend.js +9 -0
  160. package/dist/commands/shared/task-store.d.ts +92 -2
  161. package/dist/commands/shared/task-store.d.ts.map +1 -1
  162. package/dist/commands/shared/task-store.js +405 -43
  163. package/dist/commands/task/block.d.ts.map +1 -1
  164. package/dist/commands/task/block.js +84 -46
  165. package/dist/commands/task/close-duplicate.d.ts.map +1 -1
  166. package/dist/commands/task/close-duplicate.js +12 -37
  167. package/dist/commands/task/close-noop.d.ts.map +1 -1
  168. package/dist/commands/task/close-noop.js +12 -30
  169. package/dist/commands/task/close-shared.d.ts +14 -0
  170. package/dist/commands/task/close-shared.d.ts.map +1 -0
  171. package/dist/commands/task/close-shared.js +73 -0
  172. package/dist/commands/task/comment.d.ts.map +1 -1
  173. package/dist/commands/task/comment.js +34 -21
  174. package/dist/commands/task/derive.command.d.ts +1 -0
  175. package/dist/commands/task/derive.command.d.ts.map +1 -1
  176. package/dist/commands/task/derive.command.js +15 -2
  177. package/dist/commands/task/derive.d.ts +1 -0
  178. package/dist/commands/task/derive.d.ts.map +1 -1
  179. package/dist/commands/task/derive.js +27 -4
  180. package/dist/commands/task/doc-set.command.d.ts +2 -1
  181. package/dist/commands/task/doc-set.command.d.ts.map +1 -1
  182. package/dist/commands/task/doc-set.command.js +36 -4
  183. package/dist/commands/task/doc-template.d.ts.map +1 -1
  184. package/dist/commands/task/doc-template.js +2 -7
  185. package/dist/commands/task/doc.command.js +1 -1
  186. package/dist/commands/task/doc.d.ts +2 -1
  187. package/dist/commands/task/doc.d.ts.map +1 -1
  188. package/dist/commands/task/doc.js +139 -76
  189. package/dist/commands/task/finish.d.ts.map +1 -1
  190. package/dist/commands/task/finish.js +142 -80
  191. package/dist/commands/task/migrate-doc.d.ts +15 -0
  192. package/dist/commands/task/migrate-doc.d.ts.map +1 -1
  193. package/dist/commands/task/migrate-doc.js +128 -43
  194. package/dist/commands/task/new.d.ts.map +1 -1
  195. package/dist/commands/task/new.js +3 -1
  196. package/dist/commands/task/plan-set.command.js +1 -1
  197. package/dist/commands/task/plan.command.d.ts +8 -0
  198. package/dist/commands/task/plan.command.d.ts.map +1 -0
  199. package/dist/commands/task/plan.command.js +37 -0
  200. package/dist/commands/task/plan.d.ts.map +1 -1
  201. package/dist/commands/task/plan.js +198 -101
  202. package/dist/commands/task/set-status.command.d.ts.map +1 -1
  203. package/dist/commands/task/set-status.command.js +1 -1
  204. package/dist/commands/task/set-status.d.ts.map +1 -1
  205. package/dist/commands/task/set-status.js +115 -35
  206. package/dist/commands/task/shared/dependencies.d.ts +1 -0
  207. package/dist/commands/task/shared/dependencies.d.ts.map +1 -1
  208. package/dist/commands/task/shared/dependencies.js +10 -0
  209. package/dist/commands/task/shared/docs.d.ts +1 -0
  210. package/dist/commands/task/shared/docs.d.ts.map +1 -1
  211. package/dist/commands/task/shared/docs.js +8 -1
  212. package/dist/commands/task/shared/transitions.d.ts +17 -2
  213. package/dist/commands/task/shared/transitions.d.ts.map +1 -1
  214. package/dist/commands/task/shared/transitions.js +20 -13
  215. package/dist/commands/task/shared.d.ts +3 -3
  216. package/dist/commands/task/shared.d.ts.map +1 -1
  217. package/dist/commands/task/shared.js +3 -3
  218. package/dist/commands/task/start.d.ts.map +1 -1
  219. package/dist/commands/task/start.js +101 -71
  220. package/dist/commands/task/task.command.d.ts +8 -0
  221. package/dist/commands/task/task.command.d.ts.map +1 -0
  222. package/dist/commands/task/task.command.js +71 -0
  223. package/dist/commands/task/verify-command-shared.d.ts +16 -0
  224. package/dist/commands/task/verify-command-shared.d.ts.map +1 -0
  225. package/dist/commands/task/verify-command-shared.js +53 -0
  226. package/dist/commands/task/verify-ok.command.d.ts +2 -6
  227. package/dist/commands/task/verify-ok.command.d.ts.map +1 -1
  228. package/dist/commands/task/verify-ok.command.js +8 -50
  229. package/dist/commands/task/verify-record.d.ts.map +1 -1
  230. package/dist/commands/task/verify-record.js +124 -145
  231. package/dist/commands/task/verify-rework.command.d.ts +2 -6
  232. package/dist/commands/task/verify-rework.command.d.ts.map +1 -1
  233. package/dist/commands/task/verify-rework.command.js +8 -50
  234. package/dist/commands/upgrade/apply.d.ts +2 -0
  235. package/dist/commands/upgrade/apply.d.ts.map +1 -1
  236. package/dist/commands/upgrade/apply.js +33 -1
  237. package/dist/commands/upgrade.command.d.ts.map +1 -1
  238. package/dist/commands/upgrade.command.js +25 -0
  239. package/dist/commands/upgrade.d.ts +1 -0
  240. package/dist/commands/upgrade.d.ts.map +1 -1
  241. package/dist/commands/upgrade.js +34 -0
  242. package/dist/commands/verify.spec.d.ts.map +1 -1
  243. package/dist/commands/verify.spec.js +3 -12
  244. package/dist/policy/rules/allowlist.d.ts.map +1 -1
  245. package/dist/policy/rules/allowlist.js +16 -4
  246. package/dist/policy/rules/protected-paths.d.ts.map +1 -1
  247. package/dist/policy/rules/protected-paths.js +6 -1
  248. package/dist/ports/task-backend-port.d.ts +2 -2
  249. package/dist/ports/task-backend-port.d.ts.map +1 -1
  250. package/dist/shared/agent-emoji.d.ts.map +1 -1
  251. package/dist/shared/protected-paths.d.ts +17 -0
  252. package/dist/shared/protected-paths.d.ts.map +1 -1
  253. package/dist/shared/protected-paths.js +59 -10
  254. package/dist/shared/repo-cli-version.d.ts.map +1 -1
  255. package/dist/shared/repo-cli-version.js +9 -3
  256. package/package.json +2 -2
@@ -1,24 +1,36 @@
1
1
  import { readFile } from "node:fs/promises";
2
2
  import path from "node:path";
3
- import { ensureDocSections, normalizeDocSectionName, parseDocSections, setMarkdownSection, } from "@agentplaneorg/core";
3
+ import { normalizeDocSectionName, normalizeTaskDoc, parseDocSections, renderTaskDocFromSections, setMarkdownSection, taskDocToSectionMap, ensureDocSections, } from "@agentplaneorg/core";
4
4
  import { mapBackendError, mapCoreError } from "../../cli/error-map.js";
5
5
  import { infoMessage, unknownEntityMessage, backendNotSupportedMessage, warnMessage, } from "../../cli/output.js";
6
6
  import { CliError } from "../../shared/errors.js";
7
7
  import { loadCommandContext } from "../shared/task-backend.js";
8
8
  import { backendIsLocalFileBackend, getTaskStore } from "../shared/task-store.js";
9
- function normalizeOutcomeText(value) {
10
- return value
11
- .replaceAll("\r\n", "\n")
12
- .split("\n")
13
- .map((line) => line.trimEnd())
14
- .join("\n")
15
- .trim();
16
- }
17
- function readSectionContent(doc, section) {
18
- const sectionKey = normalizeDocSectionName(section);
9
+ import { decodeEscapedTaskTextNewlines } from "./shared.js";
10
+ function extractSectionTextForPatch(doc, section) {
19
11
  const { sections } = parseDocSections(doc);
20
- const lines = sections.get(sectionKey)?.lines ?? [];
21
- return normalizeOutcomeText(lines.join("\n"));
12
+ const entry = sections.get(normalizeDocSectionName(section));
13
+ return entry ? entry.lines.join("\n").trimEnd() : null;
14
+ }
15
+ function buildUpdatedTaskDoc(opts) {
16
+ const baseDoc = ensureDocSections(opts.baseDocRaw, opts.requiredSections);
17
+ if (opts.requestMode === "full-doc") {
18
+ return ensureDocSections(opts.text, opts.requiredSections);
19
+ }
20
+ let nextText = opts.text;
21
+ if (opts.headingKeys.size > 0 && opts.headingKeys.has(opts.targetKey)) {
22
+ const lines = nextText.replaceAll("\r\n", "\n").split("\n");
23
+ let firstContent = 0;
24
+ while (firstContent < lines.length && lines[firstContent]?.trim() === "")
25
+ firstContent++;
26
+ if ((lines[firstContent]?.trim() ?? "") === `## ${opts.section}`) {
27
+ lines.splice(firstContent, 1);
28
+ if (lines[firstContent]?.trim() === "")
29
+ lines.splice(firstContent, 1);
30
+ nextText = lines.join("\n");
31
+ }
32
+ }
33
+ return ensureDocSections(setMarkdownSection(baseDoc, opts.section ?? "", nextText), opts.requiredSections);
22
34
  }
23
35
  export async function cmdTaskDocSet(opts) {
24
36
  let updatedBy;
@@ -43,6 +55,9 @@ export async function cmdTaskDocSet(opts) {
43
55
  });
44
56
  }
45
57
  let text = opts.text ?? "";
58
+ if (hasText) {
59
+ text = decodeEscapedTaskTextNewlines(text);
60
+ }
46
61
  if (hasFile) {
47
62
  try {
48
63
  text = await readFile(path.resolve(opts.cwd, opts.file ?? ""), "utf8");
@@ -67,15 +82,29 @@ export async function cmdTaskDocSet(opts) {
67
82
  const useStore = backendIsLocalFileBackend(ctx);
68
83
  const store = useStore ? getTaskStore(ctx) : null;
69
84
  const allowed = config.tasks.doc.sections;
70
- if (!allowed.includes(opts.section)) {
85
+ if (!opts.fullDoc && !opts.section) {
86
+ throw new CliError({
87
+ exitCode: 2,
88
+ code: "E_USAGE",
89
+ message: "Missing required option: --section (or pass --full-doc)",
90
+ });
91
+ }
92
+ if (opts.fullDoc && opts.section) {
93
+ throw new CliError({
94
+ exitCode: 2,
95
+ code: "E_USAGE",
96
+ message: "Use either --section or --full-doc (not both)",
97
+ });
98
+ }
99
+ if (!opts.fullDoc && !allowed.includes(opts.section ?? "")) {
71
100
  throw new CliError({
72
101
  exitCode: 2,
73
102
  code: "E_USAGE",
74
- message: unknownEntityMessage("doc section", opts.section),
103
+ message: unknownEntityMessage("doc section", opts.section ?? ""),
75
104
  });
76
105
  }
77
106
  const normalizedAllowed = new Set(allowed.map((section) => normalizeDocSectionName(section)));
78
- const targetKey = normalizeDocSectionName(opts.section);
107
+ const targetKey = normalizeDocSectionName(opts.section ?? "");
79
108
  const headingKeys = new Set();
80
109
  for (const line of text.replaceAll("\r\n", "\n").split("\n")) {
81
110
  const match = /^##\s+(.*)$/.exec(line.trim());
@@ -85,68 +114,91 @@ export async function cmdTaskDocSet(opts) {
85
114
  if (key && normalizedAllowed.has(key))
86
115
  headingKeys.add(key);
87
116
  }
88
- const storeTask = useStore ? await store.get(opts.taskId) : null;
89
- const baseDocRaw = useStore
90
- ? String(storeTask.doc ?? "")
91
- : ((await backend.getTaskDoc(opts.taskId)) ?? "");
92
- const baseDoc = ensureDocSections(baseDocRaw, config.tasks.doc.required_sections);
93
- const baseSection = readSectionContent(baseDoc, opts.section);
94
- const requestMode = headingKeys.size > 0 && (headingKeys.size > 1 || !headingKeys.has(targetKey))
117
+ const requestMode = opts.fullDoc
95
118
  ? "full-doc"
96
- : "section";
97
- if (headingKeys.size > 0 && (headingKeys.size > 1 || !headingKeys.has(targetKey))) {
98
- const fullDoc = ensureDocSections(text, config.tasks.doc.required_sections);
99
- await (useStore
100
- ? store.update(opts.taskId, (current) => ({
101
- ...current,
102
- doc: fullDoc,
103
- ...(updatedBy ? { doc_updated_by: updatedBy } : {}),
104
- }))
105
- : backend.setTaskDoc(opts.taskId, fullDoc, updatedBy));
119
+ : headingKeys.size > 0 && (headingKeys.size > 1 || !headingKeys.has(targetKey))
120
+ ? "full-doc"
121
+ : "section";
122
+ let changed = false;
123
+ if (useStore) {
124
+ let expectedCurrentDoc;
125
+ let expectedCurrentSectionText;
126
+ const result = await store.patch(opts.taskId, (current) => {
127
+ const currentDocRaw = String(current.doc ?? "");
128
+ const nextDoc = buildUpdatedTaskDoc({
129
+ baseDocRaw: currentDocRaw,
130
+ section: opts.section,
131
+ text,
132
+ requestMode,
133
+ requiredSections: config.tasks.doc.required_sections,
134
+ headingKeys,
135
+ targetKey,
136
+ });
137
+ const docChanged = normalizeTaskDoc(currentDocRaw) !== normalizeTaskDoc(nextDoc);
138
+ const shouldWrite = docChanged || updatedBy !== undefined;
139
+ if (!shouldWrite)
140
+ return null;
141
+ if (!docChanged) {
142
+ return {
143
+ docMeta: {
144
+ touch: true,
145
+ ...(updatedBy ? { updatedBy } : {}),
146
+ },
147
+ };
148
+ }
149
+ if (requestMode === "full-doc") {
150
+ expectedCurrentDoc ??= currentDocRaw;
151
+ return {
152
+ doc: {
153
+ kind: "replace-doc",
154
+ doc: nextDoc,
155
+ expectedCurrentDoc,
156
+ },
157
+ ...(updatedBy ? { docMeta: { updatedBy } } : {}),
158
+ };
159
+ }
160
+ expectedCurrentSectionText ??= extractSectionTextForPatch(currentDocRaw, opts.section ?? "");
161
+ return {
162
+ doc: {
163
+ kind: "set-section",
164
+ section: opts.section ?? "",
165
+ text: extractSectionTextForPatch(nextDoc, opts.section ?? "") ?? "",
166
+ requiredSections: config.tasks.doc.required_sections,
167
+ expectedCurrentText: expectedCurrentSectionText,
168
+ },
169
+ ...(updatedBy ? { docMeta: { updatedBy } } : {}),
170
+ };
171
+ });
172
+ changed = result.changed;
106
173
  }
107
174
  else {
108
- let nextText = text;
109
- if (headingKeys.size > 0 && headingKeys.has(targetKey)) {
110
- const lines = nextText.replaceAll("\r\n", "\n").split("\n");
111
- let firstContent = 0;
112
- while (firstContent < lines.length && lines[firstContent]?.trim() === "")
113
- firstContent++;
114
- if ((lines[firstContent]?.trim() ?? "") === `## ${opts.section}`) {
115
- lines.splice(firstContent, 1);
116
- if (lines[firstContent]?.trim() === "")
117
- lines.splice(firstContent, 1);
118
- nextText = lines.join("\n");
119
- }
175
+ const baseDocRaw = (await backend.getTaskDoc(opts.taskId)) ?? "";
176
+ const nextDoc = buildUpdatedTaskDoc({
177
+ baseDocRaw,
178
+ section: opts.section,
179
+ text,
180
+ requestMode,
181
+ requiredSections: config.tasks.doc.required_sections,
182
+ headingKeys,
183
+ targetKey,
184
+ });
185
+ const docChanged = normalizeTaskDoc(baseDocRaw) !== normalizeTaskDoc(nextDoc);
186
+ const shouldWrite = docChanged || updatedBy !== undefined;
187
+ if (shouldWrite) {
188
+ await backend.setTaskDoc(opts.taskId, nextDoc, updatedBy);
120
189
  }
121
- const nextDoc = setMarkdownSection(baseDoc, opts.section, nextText);
122
- const normalized = ensureDocSections(nextDoc, config.tasks.doc.required_sections);
123
- await (useStore
124
- ? store.update(opts.taskId, (current) => ({
125
- ...current,
126
- doc: normalized,
127
- ...(updatedBy ? { doc_updated_by: updatedBy } : {}),
128
- }))
129
- : backend.setTaskDoc(opts.taskId, normalized, updatedBy));
130
- }
131
- const updatedTask = useStore ? await store.get(opts.taskId) : null;
132
- const updatedDocRaw = useStore
133
- ? String(updatedTask?.doc ?? "")
134
- : ((await backend.getTaskDoc(opts.taskId)) ?? "");
135
- const updatedDoc = ensureDocSections(updatedDocRaw, config.tasks.doc.required_sections);
136
- const updatedSection = readSectionContent(updatedDoc, opts.section);
137
- const docChanged = normalizeOutcomeText(baseDoc) !== normalizeOutcomeText(updatedDoc);
138
- const sectionChanged = baseSection !== updatedSection;
190
+ changed = shouldWrite;
191
+ }
192
+ const outcome = changed
193
+ ? requestMode === "full-doc"
194
+ ? "full-doc-updated"
195
+ : "section-updated"
196
+ : "no-change";
139
197
  const tasksDir = path.join(resolved.gitRoot, config.paths.workflow_dir);
140
198
  process.stdout.write(`${path.join(tasksDir, opts.taskId, "README.md")}\n`);
141
- if (!docChanged) {
142
- process.stderr.write(`${warnMessage(`task doc set wrote no effective README change for section ${opts.section}`)}\n`);
143
- }
144
- else if (requestMode === "full-doc") {
145
- process.stderr.write(`${infoMessage(`task doc set applied a full-doc update; target section ${opts.section} ${sectionChanged ? "changed" : "did not change"}`)}\n`);
146
- }
147
- else {
148
- process.stderr.write(`${infoMessage(`task doc set updated section ${opts.section}${sectionChanged ? "" : " (effective section content unchanged)"}`)}\n`);
149
- }
199
+ const outcomeTarget = opts.fullDoc ? "<full-doc>" : (opts.section ?? "");
200
+ const outcomeText = `task doc set outcome=${outcome} section=${outcomeTarget}`;
201
+ process.stderr.write(`${outcome === "no-change" ? warnMessage(outcomeText) : infoMessage(outcomeText)}\n`);
150
202
  return 0;
151
203
  }
152
204
  catch (err) {
@@ -170,19 +222,30 @@ export async function cmdTaskDocShow(opts) {
170
222
  const useStore = backendIsLocalFileBackend(ctx);
171
223
  const storeTask = useStore ? await getTaskStore(ctx).get(opts.taskId) : null;
172
224
  const doc = useStore
173
- ? String(storeTask.doc ?? "")
225
+ ? storeTask?.sections && Object.keys(storeTask.sections).length > 0
226
+ ? renderTaskDocFromSections(storeTask.sections)
227
+ : String(storeTask?.doc ?? "")
174
228
  : ((await backend.getTaskDoc(opts.taskId)) ?? "");
229
+ const canonicalSections = new Map();
230
+ for (const [title, text] of Object.entries(useStore && storeTask?.sections && Object.keys(storeTask.sections).length > 0
231
+ ? storeTask.sections
232
+ : taskDocToSectionMap(doc))) {
233
+ canonicalSections.set(normalizeDocSectionName(title), {
234
+ title,
235
+ lines: String(text ?? "").split("\n"),
236
+ });
237
+ }
175
238
  if (opts.section) {
176
239
  const sectionKey = normalizeDocSectionName(opts.section);
177
- const { sections } = parseDocSections(doc);
178
- const entry = sections.get(sectionKey);
240
+ const entry = canonicalSections.get(sectionKey);
179
241
  const content = entry?.lines ?? [];
180
- if (content.length > 0) {
242
+ if (content.some((line) => line.trim().length > 0)) {
181
243
  process.stdout.write(`${content.join("\n").trimEnd()}\n`);
182
244
  return 0;
183
245
  }
184
246
  if (!opts.quiet) {
185
247
  process.stdout.write(`${infoMessage(`section has no content: ${opts.section}`)}\n`);
248
+ return 0;
186
249
  }
187
250
  return 0;
188
251
  }
@@ -1 +1 @@
1
- {"version":3,"file":"finish.d.ts","sourceRoot":"","sources":["../../../src/commands/task/finish.ts"],"names":[],"mappings":"AAYA,OAAO,EAGL,KAAK,cAAc,EACpB,MAAM,2BAA2B,CAAC;AAqCnC,wBAAsB,SAAS,CAAC,IAAI,EAAE;IACpC,GAAG,CAAC,EAAE,cAAc,CAAC;IACrB,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,KAAK,GAAG,KAAK,GAAG,MAAM,CAAC;IAC9B,QAAQ,EAAE,OAAO,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,OAAO,CAAC;IACf,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,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,YAAY,EAAE,OAAO,CAAC;IACtB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,iBAAiB,EAAE,MAAM,EAAE,CAAC;IAC5B,qBAAqB,EAAE,OAAO,CAAC;IAC/B,wBAAwB,EAAE,OAAO,CAAC;IAClC,mBAAmB,EAAE,OAAO,CAAC;IAC7B,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,KAAK,EAAE,OAAO,CAAC;CAChB,GAAG,OAAO,CAAC,MAAM,CAAC,CA4XlB"}
1
+ {"version":3,"file":"finish.d.ts","sourceRoot":"","sources":["../../../src/commands/task/finish.ts"],"names":[],"mappings":"AAYA,OAAO,EAGL,KAAK,cAAc,EACpB,MAAM,2BAA2B,CAAC;AAgGnC,wBAAsB,SAAS,CAAC,IAAI,EAAE;IACpC,GAAG,CAAC,EAAE,cAAc,CAAC;IACrB,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,KAAK,GAAG,KAAK,GAAG,MAAM,CAAC;IAC9B,QAAQ,EAAE,OAAO,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,OAAO,CAAC;IACf,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,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,YAAY,EAAE,OAAO,CAAC;IACtB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,iBAAiB,EAAE,MAAM,EAAE,CAAC;IAC5B,qBAAqB,EAAE,OAAO,CAAC;IAC/B,wBAAwB,EAAE,OAAO,CAAC;IAClC,mBAAmB,EAAE,OAAO,CAAC;IAC7B,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,KAAK,EAAE,OAAO,CAAC;CAChB,GAAG,OAAO,CAAC,MAAM,CAAC,CA8YlB"}
@@ -9,7 +9,7 @@ import { buildGitCommitEnv, cmdCommit, commitFromComment } from "../guard/index.
9
9
  import { ensureActionApproved } from "../shared/approval-requirements.js";
10
10
  import { ensureReconciledBeforeMutation } from "../shared/reconcile-check.js";
11
11
  import { loadCommandContext, loadTaskFromContext, } from "../shared/task-backend.js";
12
- import { backendIsLocalFileBackend, getTaskStore } from "../shared/task-store.js";
12
+ import { appendTaskCommentIntent, appendTaskEventIntent, backendIsLocalFileBackend, getTaskStore, mutateTaskStore, setTaskFieldsIntent, touchTaskDocMetaIntent, } from "../shared/task-store.js";
13
13
  import { readDirectWorkLock } from "../../shared/direct-work-lock.js";
14
14
  import { appendTaskEvent, defaultCommitEmojiForStatus, enforceStatusCommitPolicy, ensureAgentFilledRequiredDocSections, ensureVerificationSatisfiedIfRequired, normalizeTaskDocVersion, nowIso, readCommitInfo, readHeadCommit, requireStructuredComment, resolvePrimaryTag, toStringArray, } from "./shared.js";
15
15
  async function clearDirectWorkLockIfMatches(opts) {
@@ -28,6 +28,43 @@ async function clearDirectWorkLockIfMatches(opts) {
28
28
  // best-effort
29
29
  }
30
30
  }
31
+ function assertTaskCanFinish(opts) {
32
+ if (!opts.force && String(opts.task.status || "TODO").toUpperCase() === "DONE") {
33
+ throw new CliError({
34
+ exitCode: 2,
35
+ code: "E_USAGE",
36
+ message: `Task is already DONE: ${opts.task.id} (use --force to override)`,
37
+ });
38
+ }
39
+ ensureVerificationSatisfiedIfRequired(opts.task, opts.config);
40
+ const normalizedDoc = ensureDocSections(typeof opts.task.doc === "string" ? opts.task.doc : "", opts.config.tasks.doc.required_sections);
41
+ ensureAgentFilledRequiredDocSections({
42
+ task: opts.task,
43
+ config: opts.config,
44
+ doc: normalizedDoc,
45
+ action: "finish task",
46
+ });
47
+ if (!opts.isMetaTask)
48
+ return;
49
+ const tags = Array.isArray(opts.task.tags)
50
+ ? opts.task.tags.filter((t) => typeof t === "string")
51
+ : [];
52
+ const isSpike = tags.includes("spike");
53
+ if (!isSpike && opts.taskCount === 1 && !opts.resultSummary) {
54
+ throw new CliError({
55
+ exitCode: 2,
56
+ code: "E_USAGE",
57
+ message: "Missing required --result for non-spike tasks.",
58
+ });
59
+ }
60
+ if (opts.resultProvided && !opts.resultSummary) {
61
+ throw new CliError({
62
+ exitCode: 2,
63
+ code: "E_USAGE",
64
+ message: "Invalid value for --result: empty.",
65
+ });
66
+ }
67
+ }
31
68
  export async function cmdFinish(opts) {
32
69
  try {
33
70
  const ctx = opts.ctx ??
@@ -123,6 +160,7 @@ export async function cmdFinish(opts) {
123
160
  const shouldCloseCommit = opts.closeCommit === true || (defaultDirectCloseCommit && opts.noCloseCommit !== true);
124
161
  const metaTaskId = opts.taskIds.length === 1 ? (opts.taskIds[0] ?? "") : "";
125
162
  const wantMeta = typeof opts.result === "string" || typeof opts.risk === "string" || opts.breaking === true;
163
+ const resultProvided = typeof opts.result === "string";
126
164
  if (wantMeta && opts.taskIds.length !== 1) {
127
165
  throw new CliError({
128
166
  exitCode: 2,
@@ -137,78 +175,88 @@ export async function cmdFinish(opts) {
137
175
  let primaryTag = null;
138
176
  for (const taskId of opts.taskIds) {
139
177
  const task = useStore ? await store.get(taskId) : await loadTaskFromContext({ ctx, taskId });
140
- if (!opts.force) {
141
- const currentStatus = String(task.status || "TODO").toUpperCase();
142
- if (currentStatus === "DONE") {
143
- throw new CliError({
144
- exitCode: 2,
145
- code: "E_USAGE",
146
- message: `Task is already DONE: ${task.id} (use --force to override)`,
147
- });
178
+ if (!useStore) {
179
+ assertTaskCanFinish({
180
+ task,
181
+ config: ctx.config,
182
+ taskCount: opts.taskIds.length,
183
+ isMetaTask: taskId === metaTaskId,
184
+ resultProvided,
185
+ resultSummary,
186
+ force: opts.force,
187
+ });
188
+ if (taskId === primaryTaskId && (opts.commitFromComment || statusCommitRequested)) {
189
+ primaryStatusFrom = String(task.status || "TODO").toUpperCase();
190
+ primaryTag = resolvePrimaryTag(toStringArray(task.tags), ctx).primary;
148
191
  }
149
192
  }
150
- if (taskId === primaryTaskId &&
151
- (opts.commitFromComment || statusCommitRequested) &&
152
- primaryStatusFrom === null) {
153
- primaryStatusFrom = String(task.status || "TODO").toUpperCase();
154
- primaryTag = resolvePrimaryTag(toStringArray(task.tags), ctx).primary;
193
+ const at = nowIso();
194
+ if (useStore) {
195
+ await mutateTaskStore(store, taskId, (current) => {
196
+ assertTaskCanFinish({
197
+ task: current,
198
+ config: ctx.config,
199
+ taskCount: opts.taskIds.length,
200
+ isMetaTask: taskId === metaTaskId,
201
+ resultProvided,
202
+ resultSummary,
203
+ force: opts.force,
204
+ });
205
+ const currentStatus = String(current.status || "TODO").toUpperCase();
206
+ if (taskId === primaryTaskId && (opts.commitFromComment || statusCommitRequested)) {
207
+ primaryStatusFrom = currentStatus;
208
+ primaryTag = resolvePrimaryTag(toStringArray(current.tags), ctx).primary;
209
+ }
210
+ return [
211
+ setTaskFieldsIntent({
212
+ status: "DONE",
213
+ commit: { hash: commitInfo.hash, message: commitInfo.message },
214
+ ...(taskId === metaTaskId && resultSummary ? { result_summary: resultSummary } : {}),
215
+ ...(taskId === metaTaskId && riskLevel ? { risk_level: riskLevel } : {}),
216
+ ...(taskId === metaTaskId && breaking ? { breaking: true } : {}),
217
+ }),
218
+ appendTaskCommentIntent({ author: opts.author, body: opts.body }),
219
+ appendTaskEventIntent({
220
+ type: "status",
221
+ at,
222
+ author: opts.author,
223
+ from: currentStatus,
224
+ to: "DONE",
225
+ note: opts.body,
226
+ }),
227
+ touchTaskDocMetaIntent({
228
+ updatedBy: opts.author,
229
+ version: normalizeTaskDocVersion(current.doc_version),
230
+ }),
231
+ ];
232
+ });
155
233
  }
156
- ensureVerificationSatisfiedIfRequired(task, ctx.config);
157
- const normalizedDoc = ensureDocSections(typeof task.doc === "string" ? task.doc : "", ctx.config.tasks.doc.required_sections);
158
- ensureAgentFilledRequiredDocSections({
159
- task,
160
- config: ctx.config,
161
- doc: normalizedDoc,
162
- action: "finish task",
163
- });
164
- if (taskId === metaTaskId) {
165
- const tags = Array.isArray(task.tags)
166
- ? task.tags.filter((t) => typeof t === "string")
234
+ else {
235
+ const existingComments = Array.isArray(task.comments)
236
+ ? task.comments.filter((item) => !!item && typeof item.author === "string" && typeof item.body === "string")
167
237
  : [];
168
- const isSpike = tags.includes("spike");
169
- if (!isSpike && opts.taskIds.length === 1 && !resultSummary) {
170
- throw new CliError({
171
- exitCode: 2,
172
- code: "E_USAGE",
173
- message: "Missing required --result for non-spike tasks.",
174
- });
175
- }
176
- if (typeof opts.result === "string" && !resultSummary) {
177
- throw new CliError({
178
- exitCode: 2,
179
- code: "E_USAGE",
180
- message: "Invalid value for --result: empty.",
181
- });
182
- }
238
+ const nextTask = {
239
+ ...task,
240
+ status: "DONE",
241
+ commit: { hash: commitInfo.hash, message: commitInfo.message },
242
+ comments: [...existingComments, { author: opts.author, body: opts.body }],
243
+ events: appendTaskEvent(task, {
244
+ type: "status",
245
+ at,
246
+ author: opts.author,
247
+ from: String(task.status || "TODO").toUpperCase(),
248
+ to: "DONE",
249
+ note: opts.body,
250
+ }),
251
+ result_summary: taskId === metaTaskId && resultSummary ? resultSummary : task.result_summary,
252
+ risk_level: taskId === metaTaskId && riskLevel ? riskLevel : task.risk_level,
253
+ breaking: taskId === metaTaskId && breaking ? true : task.breaking,
254
+ doc_version: normalizeTaskDocVersion(task.doc_version),
255
+ doc_updated_at: at,
256
+ doc_updated_by: opts.author,
257
+ };
258
+ await ctx.taskBackend.writeTask(nextTask);
183
259
  }
184
- const existingComments = Array.isArray(task.comments)
185
- ? task.comments.filter((item) => !!item && typeof item.author === "string" && typeof item.body === "string")
186
- : [];
187
- const commentsValue = [...existingComments, { author: opts.author, body: opts.body }];
188
- const at = nowIso();
189
- const nextTask = {
190
- ...task,
191
- status: "DONE",
192
- commit: { hash: commitInfo.hash, message: commitInfo.message },
193
- comments: commentsValue,
194
- events: appendTaskEvent(task, {
195
- type: "status",
196
- at,
197
- author: opts.author,
198
- from: String(task.status || "TODO").toUpperCase(),
199
- to: "DONE",
200
- note: opts.body,
201
- }),
202
- result_summary: taskId === metaTaskId && resultSummary ? resultSummary : task.result_summary,
203
- risk_level: taskId === metaTaskId && riskLevel ? riskLevel : task.risk_level,
204
- breaking: taskId === metaTaskId && breaking ? true : task.breaking,
205
- doc_version: normalizeTaskDocVersion(task.doc_version),
206
- doc_updated_at: at,
207
- doc_updated_by: opts.author,
208
- };
209
- await (useStore
210
- ? store.update(taskId, () => nextTask)
211
- : ctx.taskBackend.writeTask(nextTask));
212
260
  }
213
261
  if (opts.commitFromComment || statusCommitRequested) {
214
262
  enforceStatusCommitPolicy({
@@ -233,6 +281,9 @@ export async function cmdFinish(opts) {
233
281
  }
234
282
  }
235
283
  if (opts.commitFromComment) {
284
+ if (!opts.quiet) {
285
+ process.stdout.write(`${infoMessage("task marked DONE; creating commit from verification comment")}\n`);
286
+ }
236
287
  if (typeof opts.commitEmoji === "string" && opts.commitEmoji.trim() !== "✅") {
237
288
  throw new CliError({
238
289
  exitCode: 2,
@@ -263,19 +314,27 @@ export async function cmdFinish(opts) {
263
314
  // commitFromComment creates the git commit and returns the actual head hash/subject.
264
315
  // Refresh task commit metadata to this hash and amend the same commit in local mode so
265
316
  // "task done" metadata does not require a manual follow-up close commit.
266
- const taskAfterCommit = useStore
267
- ? await store.get(primaryTaskId)
268
- : await loadTaskFromContext({ ctx, taskId: primaryTaskId });
269
- const updatedAfterCommit = {
270
- ...taskAfterCommit,
271
- commit: { hash: committed.hash, message: committed.message },
272
- doc_version: normalizeTaskDocVersion(taskAfterCommit.doc_version),
273
- doc_updated_at: nowIso(),
274
- doc_updated_by: opts.author,
275
- };
276
317
  await (useStore
277
- ? store.update(primaryTaskId, () => updatedAfterCommit)
278
- : ctx.taskBackend.writeTask(updatedAfterCommit));
318
+ ? mutateTaskStore(store, primaryTaskId, (current) => [
319
+ setTaskFieldsIntent({
320
+ commit: { hash: committed.hash, message: committed.message },
321
+ }),
322
+ touchTaskDocMetaIntent({
323
+ updatedBy: opts.author,
324
+ version: normalizeTaskDocVersion(current.doc_version),
325
+ }),
326
+ ])
327
+ : (async () => {
328
+ const taskAfterCommit = await loadTaskFromContext({ ctx, taskId: primaryTaskId });
329
+ const updatedAfterCommit = {
330
+ ...taskAfterCommit,
331
+ commit: { hash: committed.hash, message: committed.message },
332
+ doc_version: normalizeTaskDocVersion(taskAfterCommit.doc_version),
333
+ doc_updated_at: nowIso(),
334
+ doc_updated_by: opts.author,
335
+ };
336
+ await ctx.taskBackend.writeTask(updatedAfterCommit);
337
+ })());
279
338
  if (backendWritesTaskReadmes) {
280
339
  const workflowReadmeRelPath = path.join(ctx.config.paths.workflow_dir, primaryTaskId, "README.md");
281
340
  await ctx.git.stage([workflowReadmeRelPath]);
@@ -294,6 +353,9 @@ export async function cmdFinish(opts) {
294
353
  }
295
354
  }
296
355
  if (statusCommitRequested) {
356
+ if (!opts.quiet) {
357
+ process.stdout.write(`${infoMessage("task marked DONE; creating status commit")}\n`);
358
+ }
297
359
  if (typeof opts.statusCommitEmoji === "string" && opts.statusCommitEmoji.trim() !== "✅") {
298
360
  throw new CliError({
299
361
  exitCode: 2,
@@ -1,3 +1,18 @@
1
+ import { resolveProject, type AgentplaneConfig } from "@agentplaneorg/core";
2
+ import { type CommandContext } from "../shared/task-backend.js";
3
+ export type TaskDocMigrationResult = {
4
+ changed: number;
5
+ changedPaths: string[];
6
+ };
7
+ export declare function migrateTaskDocsInWorkspace(opts: {
8
+ cwd: string;
9
+ rootOverride?: string | null;
10
+ all: boolean;
11
+ taskIds: string[];
12
+ resolvedProject?: Awaited<ReturnType<typeof resolveProject>>;
13
+ config?: AgentplaneConfig;
14
+ ctx?: CommandContext;
15
+ }): Promise<TaskDocMigrationResult>;
1
16
  export declare function cmdTaskMigrateDoc(opts: {
2
17
  cwd: string;
3
18
  rootOverride?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"migrate-doc.d.ts","sourceRoot":"","sources":["../../../src/commands/task/migrate-doc.ts"],"names":[],"mappings":"AAiSA,wBAAsB,iBAAiB,CAAC,IAAI,EAAE;IAC5C,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,GAAG,EAAE,OAAO,CAAC;IACb,KAAK,EAAE,OAAO,CAAC;IACf,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB,GAAG,OAAO,CAAC,MAAM,CAAC,CAqDlB"}
1
+ {"version":3,"file":"migrate-doc.d.ts","sourceRoot":"","sources":["../../../src/commands/task/migrate-doc.ts"],"names":[],"mappings":"AAGA,OAAO,EAUL,cAAc,EAGd,KAAK,gBAAgB,EACtB,MAAM,qBAAqB,CAAC;AAQ7B,OAAO,EAGL,KAAK,cAAc,EACpB,MAAM,2BAA2B,CAAC;AAanC,MAAM,MAAM,sBAAsB,GAAG;IACnC,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,EAAE,CAAC;CACxB,CAAC;AA+TF,wBAAsB,0BAA0B,CAAC,IAAI,EAAE;IACrD,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,GAAG,EAAE,OAAO,CAAC;IACb,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,eAAe,CAAC,EAAE,OAAO,CAAC,UAAU,CAAC,OAAO,cAAc,CAAC,CAAC,CAAC;IAC7D,MAAM,CAAC,EAAE,gBAAgB,CAAC;IAC1B,GAAG,CAAC,EAAE,cAAc,CAAC;CACtB,GAAG,OAAO,CAAC,sBAAsB,CAAC,CA4DlC;AAED,wBAAsB,iBAAiB,CAAC,IAAI,EAAE;IAC5C,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,GAAG,EAAE,OAAO,CAAC;IACb,KAAK,EAAE,OAAO,CAAC;IACf,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB,GAAG,OAAO,CAAC,MAAM,CAAC,CAoBlB"}