agentplane 0.3.10 → 0.3.11

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 (228) hide show
  1. package/assets/policy/governance.md +3 -4
  2. package/assets/policy/incidents.md +19 -88
  3. package/assets/policy/workflow.branch_pr.md +1 -1
  4. package/assets/policy/workflow.direct.md +2 -2
  5. package/bin/agentplane.js +56 -1
  6. package/bin/runtime-watch.js +1 -0
  7. package/bin/stale-dist-policy.d.ts +1 -1
  8. package/bin/stale-dist-policy.js +13 -0
  9. package/dist/.build-manifest.json +219 -154
  10. package/dist/cli/bootstrap-guide.d.ts.map +1 -1
  11. package/dist/cli/bootstrap-guide.js +3 -2
  12. package/dist/cli/command-guide.d.ts.map +1 -1
  13. package/dist/cli/command-guide.js +2 -1
  14. package/dist/cli/command-invocations.d.ts.map +1 -1
  15. package/dist/cli/command-invocations.js +4 -1
  16. package/dist/cli/run-cli/command-catalog/project.d.ts +1 -1
  17. package/dist/cli/run-cli/command-catalog/project.d.ts.map +1 -1
  18. package/dist/cli/run-cli/command-catalog/project.js +3 -1
  19. package/dist/cli/run-cli/command-catalog/task.d.ts +1 -1
  20. package/dist/cli/run-cli/command-catalog/task.d.ts.map +1 -1
  21. package/dist/cli/run-cli/command-catalog/task.js +10 -0
  22. package/dist/cli/run-cli/command-catalog.d.ts +1 -1
  23. package/dist/cli/run-cli/command-catalog.d.ts.map +1 -1
  24. package/dist/cli/run-cli/commands/core/preflight.d.ts.map +1 -1
  25. package/dist/cli/run-cli/commands/core/preflight.js +44 -1
  26. package/dist/cli/run-cli.test-helpers.d.ts.map +1 -1
  27. package/dist/cli/run-cli.test-helpers.js +12 -0
  28. package/dist/commands/branch/cleanup-merged.d.ts +2 -0
  29. package/dist/commands/branch/cleanup-merged.d.ts.map +1 -1
  30. package/dist/commands/branch/cleanup-merged.js +132 -28
  31. package/dist/commands/branch/work-start.d.ts.map +1 -1
  32. package/dist/commands/branch/work-start.js +60 -1
  33. package/dist/commands/cleanup/merged.command.d.ts +2 -0
  34. package/dist/commands/cleanup/merged.command.d.ts.map +1 -1
  35. package/dist/commands/cleanup/merged.command.js +24 -0
  36. package/dist/commands/doctor/branch-pr.d.ts +4 -0
  37. package/dist/commands/doctor/branch-pr.d.ts.map +1 -0
  38. package/dist/commands/doctor/branch-pr.js +96 -0
  39. package/dist/commands/doctor/fixes.d.ts +5 -0
  40. package/dist/commands/doctor/fixes.d.ts.map +1 -1
  41. package/dist/commands/doctor/fixes.js +70 -0
  42. package/dist/commands/doctor.run.d.ts.map +1 -1
  43. package/dist/commands/doctor.run.js +6 -1
  44. package/dist/commands/finish.run.d.ts.map +1 -1
  45. package/dist/commands/finish.run.js +11 -0
  46. package/dist/commands/finish.spec.d.ts +11 -0
  47. package/dist/commands/finish.spec.d.ts.map +1 -1
  48. package/dist/commands/finish.spec.js +51 -0
  49. package/dist/commands/guard/impl/close-message.d.ts.map +1 -1
  50. package/dist/commands/guard/impl/close-message.js +23 -6
  51. package/dist/commands/guard/impl/commands.d.ts.map +1 -1
  52. package/dist/commands/guard/impl/commands.js +24 -2
  53. package/dist/commands/guard/impl/env.d.ts +1 -0
  54. package/dist/commands/guard/impl/env.d.ts.map +1 -1
  55. package/dist/commands/guard/impl/env.js +1 -0
  56. package/dist/commands/hooks/index.d.ts.map +1 -1
  57. package/dist/commands/hooks/index.js +98 -1
  58. package/dist/commands/incidents/collect.command.d.ts.map +1 -1
  59. package/dist/commands/incidents/collect.command.js +12 -7
  60. package/dist/commands/incidents/incidents.command.js +1 -1
  61. package/dist/commands/incidents/shared.d.ts +34 -0
  62. package/dist/commands/incidents/shared.d.ts.map +1 -1
  63. package/dist/commands/incidents/shared.js +166 -12
  64. package/dist/commands/pr/check.d.ts.map +1 -1
  65. package/dist/commands/pr/check.js +238 -135
  66. package/dist/commands/pr/close-superseded.d.ts +9 -0
  67. package/dist/commands/pr/close-superseded.d.ts.map +1 -0
  68. package/dist/commands/pr/close-superseded.js +129 -0
  69. package/dist/commands/pr/close.d.ts +11 -0
  70. package/dist/commands/pr/close.d.ts.map +1 -0
  71. package/dist/commands/pr/close.js +116 -0
  72. package/dist/commands/pr/index.d.ts +2 -0
  73. package/dist/commands/pr/index.d.ts.map +1 -1
  74. package/dist/commands/pr/index.js +2 -0
  75. package/dist/commands/pr/integrate/artifacts.d.ts +7 -0
  76. package/dist/commands/pr/integrate/artifacts.d.ts.map +1 -1
  77. package/dist/commands/pr/integrate/artifacts.js +66 -1
  78. package/dist/commands/pr/integrate/cmd.d.ts.map +1 -1
  79. package/dist/commands/pr/integrate/cmd.js +16 -0
  80. package/dist/commands/pr/integrate/internal/bootstrap-guidance.d.ts +8 -0
  81. package/dist/commands/pr/integrate/internal/bootstrap-guidance.d.ts.map +1 -0
  82. package/dist/commands/pr/integrate/internal/bootstrap-guidance.js +59 -0
  83. package/dist/commands/pr/integrate/internal/finalize.d.ts.map +1 -1
  84. package/dist/commands/pr/integrate/internal/finalize.js +40 -12
  85. package/dist/commands/pr/integrate/internal/merge.d.ts.map +1 -1
  86. package/dist/commands/pr/integrate/internal/merge.js +36 -13
  87. package/dist/commands/pr/integrate/internal/post-integrate-bootstrap.d.ts +13 -0
  88. package/dist/commands/pr/integrate/internal/post-integrate-bootstrap.d.ts.map +1 -0
  89. package/dist/commands/pr/integrate/internal/post-integrate-bootstrap.js +25 -0
  90. package/dist/commands/pr/integrate/internal/prepare.d.ts +3 -2
  91. package/dist/commands/pr/integrate/internal/prepare.d.ts.map +1 -1
  92. package/dist/commands/pr/integrate/internal/prepare.js +101 -38
  93. package/dist/commands/pr/internal/freshness.d.ts +20 -0
  94. package/dist/commands/pr/internal/freshness.d.ts.map +1 -0
  95. package/dist/commands/pr/internal/freshness.js +50 -0
  96. package/dist/commands/pr/internal/gh-api.d.ts +6 -0
  97. package/dist/commands/pr/internal/gh-api.d.ts.map +1 -0
  98. package/dist/commands/pr/internal/gh-api.js +80 -0
  99. package/dist/commands/pr/internal/pr-paths.d.ts +10 -0
  100. package/dist/commands/pr/internal/pr-paths.d.ts.map +1 -1
  101. package/dist/commands/pr/internal/pr-paths.js +10 -0
  102. package/dist/commands/pr/internal/review-template.d.ts.map +1 -1
  103. package/dist/commands/pr/internal/review-template.js +37 -4
  104. package/dist/commands/pr/internal/sync.d.ts +9 -0
  105. package/dist/commands/pr/internal/sync.d.ts.map +1 -1
  106. package/dist/commands/pr/internal/sync.js +462 -122
  107. package/dist/commands/pr/open.d.ts +1 -0
  108. package/dist/commands/pr/open.d.ts.map +1 -1
  109. package/dist/commands/pr/open.js +13 -2
  110. package/dist/commands/pr/pr.command.d.ts +15 -0
  111. package/dist/commands/pr/pr.command.d.ts.map +1 -1
  112. package/dist/commands/pr/pr.command.js +118 -2
  113. package/dist/commands/pr/update.d.ts.map +1 -1
  114. package/dist/commands/pr/update.js +59 -1
  115. package/dist/commands/release/apply.command.d.ts.map +1 -1
  116. package/dist/commands/release/apply.command.js +3 -17
  117. package/dist/commands/release/apply.preflight.d.ts.map +1 -1
  118. package/dist/commands/release/apply.preflight.js +1 -1
  119. package/dist/commands/shared/gh-transport.d.ts +16 -0
  120. package/dist/commands/shared/gh-transport.d.ts.map +1 -0
  121. package/dist/commands/shared/gh-transport.js +71 -0
  122. package/dist/commands/shared/git-diff.d.ts +3 -1
  123. package/dist/commands/shared/git-diff.d.ts.map +1 -1
  124. package/dist/commands/shared/git-diff.js +10 -2
  125. package/dist/commands/shared/git-ops.d.ts +1 -0
  126. package/dist/commands/shared/git-ops.d.ts.map +1 -1
  127. package/dist/commands/shared/git-ops.js +15 -0
  128. package/dist/commands/shared/git-worktree.d.ts +2 -0
  129. package/dist/commands/shared/git-worktree.d.ts.map +1 -1
  130. package/dist/commands/shared/git-worktree.js +22 -2
  131. package/dist/commands/shared/post-commit-pr-artifacts.d.ts +9 -0
  132. package/dist/commands/shared/post-commit-pr-artifacts.d.ts.map +1 -0
  133. package/dist/commands/shared/post-commit-pr-artifacts.js +22 -0
  134. package/dist/commands/shared/pr-meta.d.ts +20 -0
  135. package/dist/commands/shared/pr-meta.d.ts.map +1 -1
  136. package/dist/commands/shared/pr-meta.js +125 -0
  137. package/dist/commands/shared/task-backend.d.ts +7 -0
  138. package/dist/commands/shared/task-backend.d.ts.map +1 -1
  139. package/dist/commands/shared/task-backend.js +34 -22
  140. package/dist/commands/task/close-duplicate.d.ts.map +1 -1
  141. package/dist/commands/task/close-duplicate.js +34 -1
  142. package/dist/commands/task/derive.js +1 -1
  143. package/dist/commands/task/doc-template.d.ts.map +1 -1
  144. package/dist/commands/task/doc-template.js +7 -11
  145. package/dist/commands/task/findings-add.command.d.ts +20 -0
  146. package/dist/commands/task/findings-add.command.d.ts.map +1 -0
  147. package/dist/commands/task/findings-add.command.js +165 -0
  148. package/dist/commands/task/findings.command.d.ts +7 -0
  149. package/dist/commands/task/findings.command.d.ts.map +1 -0
  150. package/dist/commands/task/findings.command.js +20 -0
  151. package/dist/commands/task/findings.d.ts +63 -0
  152. package/dist/commands/task/findings.d.ts.map +1 -0
  153. package/dist/commands/task/findings.js +188 -0
  154. package/dist/commands/task/finish-shared.d.ts +1 -0
  155. package/dist/commands/task/finish-shared.d.ts.map +1 -1
  156. package/dist/commands/task/finish-shared.js +55 -1
  157. package/dist/commands/task/finish.d.ts +10 -0
  158. package/dist/commands/task/finish.d.ts.map +1 -1
  159. package/dist/commands/task/finish.js +125 -6
  160. package/dist/commands/task/hosted-close-pr.command.d.ts +11 -0
  161. package/dist/commands/task/hosted-close-pr.command.d.ts.map +1 -0
  162. package/dist/commands/task/hosted-close-pr.command.js +414 -0
  163. package/dist/commands/task/hosted-close.command.d.ts.map +1 -1
  164. package/dist/commands/task/hosted-close.command.js +49 -1
  165. package/dist/commands/task/hosted-merge-sync.d.ts +38 -0
  166. package/dist/commands/task/hosted-merge-sync.d.ts.map +1 -1
  167. package/dist/commands/task/hosted-merge-sync.js +249 -17
  168. package/dist/commands/task/index.d.ts +1 -0
  169. package/dist/commands/task/index.d.ts.map +1 -1
  170. package/dist/commands/task/index.js +1 -0
  171. package/dist/commands/task/new.d.ts +1 -0
  172. package/dist/commands/task/new.d.ts.map +1 -1
  173. package/dist/commands/task/new.js +71 -1
  174. package/dist/commands/task/new.spec.d.ts.map +1 -1
  175. package/dist/commands/task/new.spec.js +7 -0
  176. package/dist/commands/task/normalize.command.d.ts +2 -0
  177. package/dist/commands/task/normalize.command.d.ts.map +1 -1
  178. package/dist/commands/task/normalize.command.js +45 -0
  179. package/dist/commands/task/normalize.d.ts +2 -0
  180. package/dist/commands/task/normalize.d.ts.map +1 -1
  181. package/dist/commands/task/normalize.js +85 -8
  182. package/dist/commands/task/plan.d.ts.map +1 -1
  183. package/dist/commands/task/plan.js +7 -10
  184. package/dist/commands/task/shared/docs.d.ts +6 -0
  185. package/dist/commands/task/shared/docs.d.ts.map +1 -1
  186. package/dist/commands/task/shared/docs.js +14 -0
  187. package/dist/commands/task/shared/transitions.d.ts.map +1 -1
  188. package/dist/commands/task/shared/transitions.js +11 -1
  189. package/dist/commands/task/shared.d.ts +1 -1
  190. package/dist/commands/task/shared.d.ts.map +1 -1
  191. package/dist/commands/task/shared.js +1 -1
  192. package/dist/commands/task/start-ready.d.ts.map +1 -1
  193. package/dist/commands/task/start-ready.js +86 -0
  194. package/dist/commands/task/start.d.ts.map +1 -1
  195. package/dist/commands/task/start.js +7 -10
  196. package/dist/commands/task/task.command.d.ts.map +1 -1
  197. package/dist/commands/task/task.command.js +4 -0
  198. package/dist/commands/task/verify-command-shared.d.ts +19 -0
  199. package/dist/commands/task/verify-command-shared.d.ts.map +1 -1
  200. package/dist/commands/task/verify-command-shared.js +152 -1
  201. package/dist/commands/task/verify-ok.command.d.ts.map +1 -1
  202. package/dist/commands/task/verify-ok.command.js +15 -2
  203. package/dist/commands/task/verify-record.d.ts +36 -0
  204. package/dist/commands/task/verify-record.d.ts.map +1 -1
  205. package/dist/commands/task/verify-record.js +166 -11
  206. package/dist/commands/task/verify-rework.command.d.ts.map +1 -1
  207. package/dist/commands/task/verify-rework.command.js +15 -2
  208. package/dist/commands/task/verify-show.command.d.ts +1 -1
  209. package/dist/commands/task/verify-show.command.d.ts.map +1 -1
  210. package/dist/commands/task/verify-show.command.js +28 -1
  211. package/dist/commands/verify.run.d.ts.map +1 -1
  212. package/dist/commands/verify.run.js +12 -0
  213. package/dist/commands/verify.spec.d.ts +2 -6
  214. package/dist/commands/verify.spec.d.ts.map +1 -1
  215. package/dist/commands/verify.spec.js +30 -3
  216. package/dist/runtime/incidents/index.d.ts +1 -1
  217. package/dist/runtime/incidents/index.d.ts.map +1 -1
  218. package/dist/runtime/incidents/resolve.d.ts.map +1 -1
  219. package/dist/runtime/incidents/resolve.js +319 -73
  220. package/dist/runtime/incidents/types.d.ts +14 -2
  221. package/dist/runtime/incidents/types.d.ts.map +1 -1
  222. package/dist/shared/env.d.ts +1 -0
  223. package/dist/shared/env.d.ts.map +1 -1
  224. package/dist/shared/env.js +22 -1
  225. package/dist/shared/protected-paths.d.ts +1 -1
  226. package/dist/shared/protected-paths.d.ts.map +1 -1
  227. package/dist/shared/protected-paths.js +4 -0
  228. package/package.json +2 -2
@@ -0,0 +1 @@
1
+ {"version":3,"file":"findings.command.d.ts","sourceRoot":"","sources":["../../../src/commands/task/findings.command.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAGtE,MAAM,MAAM,kBAAkB,GAAG;IAC/B,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,eAAO,MAAM,gBAAgB,EAAE,WAAW,CAAC,kBAAkB,CAY5D,CAAC;AAEF,wBAAsB,eAAe,CAAC,IAAI,EAAE,UAAU,EAAE,CAAC,EAAE,kBAAkB,GAAG,OAAO,CAAC,MAAM,CAAC,CAM9F"}
@@ -0,0 +1,20 @@
1
+ import { loadDirectSubcommandNames, throwGroupCommandUsage } from "../../cli/group-command.js";
2
+ export const taskFindingsSpec = {
3
+ id: ["task", "findings"],
4
+ group: "Task",
5
+ summary: "Structured Findings/Notes commands (incident promotion is default unless --local-only).",
6
+ synopsis: [
7
+ "agentplane task findings add <task-id> --observation <text> --impact <text> --resolution <text> [--local-only] [--incident-scope <text>] [--incident-tag <tag>] [--incident-match <term>] [--incident-advice <text>] [--incident-rule <text>] [--updated-by <id>]",
8
+ ],
9
+ args: [{ name: "subcommand", required: false, valueHint: "<add>" }],
10
+ parse: (raw) => ({
11
+ subcommand: typeof raw.args.subcommand === "string" ? raw.args.subcommand : undefined,
12
+ }),
13
+ };
14
+ export async function runTaskFindings(_ctx, p) {
15
+ throwGroupCommandUsage({
16
+ spec: taskFindingsSpec,
17
+ cmd: p.subcommand ? [p.subcommand] : [],
18
+ subcommands: await loadDirectSubcommandNames(["task", "findings"]),
19
+ });
20
+ }
@@ -0,0 +1,63 @@
1
+ import { type AgentplaneConfig } from "@agentplaneorg/core";
2
+ import type { TaskData } from "../../backends/task-backend.js";
3
+ import { type CommandContext } from "../shared/task-backend.js";
4
+ import { type TaskStoreIntent } from "../shared/task-store.js";
5
+ export declare function renderStructuredFindingBlock(opts: {
6
+ observation: string;
7
+ impact: string;
8
+ resolution: string;
9
+ promote: boolean;
10
+ external: boolean;
11
+ fixability?: "repo-fixable" | null;
12
+ incidentScope?: string;
13
+ incidentTags: readonly string[];
14
+ incidentMatch: readonly string[];
15
+ incidentAdvice?: string;
16
+ incidentRule?: string;
17
+ }): string;
18
+ export type StructuredFindingMutationPlan = {
19
+ targetSection: string;
20
+ expectedCurrentText: string | null;
21
+ intents: readonly TaskStoreIntent[];
22
+ };
23
+ export declare function buildStructuredFindingMutationPlan(opts: {
24
+ current: TaskData;
25
+ config: AgentplaneConfig;
26
+ observation: string;
27
+ impact: string;
28
+ resolution: string;
29
+ promote: boolean;
30
+ external: boolean;
31
+ fixability?: "repo-fixable" | null;
32
+ incidentScope?: string;
33
+ incidentTags: readonly string[];
34
+ incidentMatch: readonly string[];
35
+ incidentAdvice?: string;
36
+ incidentRule?: string;
37
+ updatedBy?: string;
38
+ }): StructuredFindingMutationPlan | null;
39
+ export declare function renderFindingsAddRegistryNote(opts: {
40
+ promote: boolean;
41
+ external: boolean;
42
+ taskId: string;
43
+ branchPr?: boolean;
44
+ }): string;
45
+ export declare function cmdTaskFindingsAdd(opts: {
46
+ ctx?: CommandContext;
47
+ cwd: string;
48
+ rootOverride?: string;
49
+ taskId: string;
50
+ observation: string;
51
+ impact: string;
52
+ resolution: string;
53
+ promote: boolean;
54
+ external: boolean;
55
+ fixability?: "repo-fixable" | null;
56
+ incidentScope?: string;
57
+ incidentTags: string[];
58
+ incidentMatch: string[];
59
+ incidentAdvice?: string;
60
+ incidentRule?: string;
61
+ updatedBy?: string;
62
+ }): Promise<number>;
63
+ //# sourceMappingURL=findings.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"findings.d.ts","sourceRoot":"","sources":["../../../src/commands/task/findings.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,gBAAgB,EACtB,MAAM,qBAAqB,CAAC;AAE7B,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gCAAgC,CAAC;AAI/D,OAAO,EAAsB,KAAK,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAEpF,OAAO,EAGL,KAAK,eAAe,EACrB,MAAM,yBAAyB,CAAC;AAoCjC,wBAAgB,4BAA4B,CAAC,IAAI,EAAE;IACjD,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,OAAO,CAAC;IAClB,UAAU,CAAC,EAAE,cAAc,GAAG,IAAI,CAAC;IACnC,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,YAAY,EAAE,SAAS,MAAM,EAAE,CAAC;IAChC,aAAa,EAAE,SAAS,MAAM,EAAE,CAAC;IACjC,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB,GAAG,MAAM,CAkBT;AAQD,MAAM,MAAM,6BAA6B,GAAG;IAC1C,aAAa,EAAE,MAAM,CAAC;IACtB,mBAAmB,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,OAAO,EAAE,SAAS,eAAe,EAAE,CAAC;CACrC,CAAC;AAEF,wBAAgB,kCAAkC,CAAC,IAAI,EAAE;IACvD,OAAO,EAAE,QAAQ,CAAC;IAClB,MAAM,EAAE,gBAAgB,CAAC;IACzB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,OAAO,CAAC;IAClB,UAAU,CAAC,EAAE,cAAc,GAAG,IAAI,CAAC;IACnC,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,YAAY,EAAE,SAAS,MAAM,EAAE,CAAC;IAChC,aAAa,EAAE,SAAS,MAAM,EAAE,CAAC;IACjC,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,GAAG,6BAA6B,GAAG,IAAI,CA0CvC;AAED,wBAAgB,6BAA6B,CAAC,IAAI,EAAE;IAClD,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,OAAO,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB,GAAG,MAAM,CAcT;AAED,wBAAsB,kBAAkB,CAAC,IAAI,EAAE;IAC7C,GAAG,CAAC,EAAE,cAAc,CAAC;IACrB,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,OAAO,CAAC;IAClB,UAAU,CAAC,EAAE,cAAc,GAAG,IAAI,CAAC;IACnC,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,GAAG,OAAO,CAAC,MAAM,CAAC,CA4ElB"}
@@ -0,0 +1,188 @@
1
+ import { ensureDocSections, normalizeTaskDoc, setMarkdownSection, } from "@agentplaneorg/core";
2
+ import { mapBackendError } from "../../cli/error-map.js";
3
+ import { infoMessage } from "../../cli/output.js";
4
+ import { CliError } from "../../shared/errors.js";
5
+ import { loadCommandContext } from "../shared/task-backend.js";
6
+ import { applyTaskMutation } from "../shared/task-mutation.js";
7
+ import { setTaskSectionIntent, touchTaskDocMetaIntent, } from "../shared/task-store.js";
8
+ import { resolveWritableDocSections } from "./shared.js";
9
+ import { extractDocSection, extractTaskObservationSection, normalizeTaskDocVersion, taskObservationSectionName, } from "./shared/docs.js";
10
+ function ensureNonEmptyFlag(name, value) {
11
+ const trimmed = value.trim();
12
+ if (!trimmed) {
13
+ throw new CliError({
14
+ exitCode: 2,
15
+ code: "E_USAGE",
16
+ message: `Invalid value for --${name}: empty.`,
17
+ });
18
+ }
19
+ return trimmed;
20
+ }
21
+ function dedupeTrimmed(values) {
22
+ const seen = new Set();
23
+ const out = [];
24
+ for (const raw of values) {
25
+ const trimmed = raw.trim();
26
+ if (!trimmed)
27
+ continue;
28
+ const key = trimmed.toLowerCase();
29
+ if (seen.has(key))
30
+ continue;
31
+ seen.add(key);
32
+ out.push(trimmed);
33
+ }
34
+ return out;
35
+ }
36
+ export function renderStructuredFindingBlock(opts) {
37
+ const fixability = opts.fixability ?? (opts.external ? "external" : null);
38
+ const lines = [
39
+ `- Observation: ${ensureNonEmptyFlag("observation", opts.observation)}`,
40
+ ` Impact: ${ensureNonEmptyFlag("impact", opts.impact)}`,
41
+ ` Resolution: ${ensureNonEmptyFlag("resolution", opts.resolution)}`,
42
+ ];
43
+ if (opts.promote)
44
+ lines.push(" Promotion: incident-candidate");
45
+ if (fixability === "external")
46
+ lines.push(" Fixability: external");
47
+ if (fixability === "repo-fixable")
48
+ lines.push(" Fixability: repo-fixable");
49
+ if (opts.incidentScope?.trim())
50
+ lines.push(` IncidentScope: ${opts.incidentScope.trim()}`);
51
+ const tags = dedupeTrimmed(opts.incidentTags);
52
+ if (tags.length > 0)
53
+ lines.push(` IncidentTags: ${tags.join(", ")}`);
54
+ const match = dedupeTrimmed(opts.incidentMatch);
55
+ if (match.length > 0)
56
+ lines.push(` IncidentMatch: ${match.join(", ")}`);
57
+ if (opts.incidentAdvice?.trim())
58
+ lines.push(` IncidentAdvice: ${opts.incidentAdvice.trim()}`);
59
+ if (opts.incidentRule?.trim())
60
+ lines.push(` IncidentRule: ${opts.incidentRule.trim()}`);
61
+ return lines.join("\n");
62
+ }
63
+ function appendFindingBlock(existingSection, block) {
64
+ const current = (existingSection ?? "").trim();
65
+ if (!current)
66
+ return block;
67
+ return `${current}\n\n${block}`;
68
+ }
69
+ export function buildStructuredFindingMutationPlan(opts) {
70
+ const currentDocRaw = typeof opts.current.doc === "string" ? opts.current.doc : "";
71
+ const docVersion = normalizeTaskDocVersion(opts.current.doc_version);
72
+ const targetSection = taskObservationSectionName(docVersion);
73
+ const sectionOrder = resolveWritableDocSections({
74
+ allowedSections: opts.config.tasks.doc.sections,
75
+ requiredSections: opts.config.tasks.doc.required_sections,
76
+ targetSection,
77
+ });
78
+ const existingSection = extractTaskObservationSection(currentDocRaw, docVersion);
79
+ const block = renderStructuredFindingBlock({
80
+ observation: opts.observation,
81
+ impact: opts.impact,
82
+ resolution: opts.resolution,
83
+ promote: opts.promote,
84
+ external: opts.external,
85
+ fixability: opts.fixability ?? null,
86
+ incidentScope: opts.incidentScope,
87
+ incidentTags: opts.incidentTags,
88
+ incidentMatch: opts.incidentMatch,
89
+ incidentAdvice: opts.incidentAdvice,
90
+ incidentRule: opts.incidentRule,
91
+ });
92
+ const nextSectionText = appendFindingBlock(existingSection, block);
93
+ const nextDoc = ensureDocSections(setMarkdownSection(currentDocRaw, targetSection, nextSectionText), sectionOrder);
94
+ if (normalizeTaskDoc(currentDocRaw) === normalizeTaskDoc(nextDoc) && !opts.updatedBy) {
95
+ return null;
96
+ }
97
+ const expectedCurrentText = extractDocSection(currentDocRaw, targetSection);
98
+ const intents = [
99
+ setTaskSectionIntent({
100
+ section: targetSection,
101
+ text: nextSectionText,
102
+ requiredSections: sectionOrder,
103
+ expectedCurrentText,
104
+ }),
105
+ ...(opts.updatedBy ? [touchTaskDocMetaIntent({ updatedBy: opts.updatedBy })] : []),
106
+ ];
107
+ return { targetSection, expectedCurrentText, intents };
108
+ }
109
+ export function renderFindingsAddRegistryNote(opts) {
110
+ if (opts.promote && opts.external) {
111
+ if (opts.branchPr) {
112
+ return (`incident candidate recorded for ${opts.taskId}; ` +
113
+ "incidents.md updates later during finish, `verify --collect-incidents`, or `agentplane incidents collect <task-id>`; task-local until base-branch promotion in the current task worktree");
114
+ }
115
+ return (`incident candidate recorded for ${opts.taskId}; ` +
116
+ "incidents.md updates later during finish, `verify --collect-incidents`, or `agentplane incidents collect <task-id>`; task-local in the current checkout until promotion");
117
+ }
118
+ return `task-local finding recorded for ${opts.taskId}; incidents.md unchanged in the current checkout`;
119
+ }
120
+ export async function cmdTaskFindingsAdd(opts) {
121
+ let updatedBy;
122
+ if (opts.updatedBy !== undefined) {
123
+ updatedBy = ensureNonEmptyFlag("updated-by", opts.updatedBy);
124
+ }
125
+ try {
126
+ const ctx = opts.ctx ??
127
+ (await loadCommandContext({ cwd: opts.cwd, rootOverride: opts.rootOverride ?? null }));
128
+ const backend = ctx.taskBackend;
129
+ const config = ctx.config;
130
+ const resolved = ctx.resolvedProject;
131
+ if (!backend.getTaskDoc || !backend.writeTask) {
132
+ throw new CliError({
133
+ exitCode: 2,
134
+ code: "E_USAGE",
135
+ message: "Configured task backend does not support task docs.",
136
+ });
137
+ }
138
+ let targetSection = "Findings";
139
+ await applyTaskMutation({
140
+ ctx,
141
+ taskId: opts.taskId,
142
+ build: (current) => {
143
+ const plan = buildStructuredFindingMutationPlan({
144
+ current,
145
+ config,
146
+ observation: opts.observation,
147
+ impact: opts.impact,
148
+ resolution: opts.resolution,
149
+ promote: opts.promote,
150
+ external: opts.external,
151
+ fixability: opts.fixability ?? null,
152
+ incidentScope: opts.incidentScope,
153
+ incidentTags: opts.incidentTags,
154
+ incidentMatch: opts.incidentMatch,
155
+ incidentAdvice: opts.incidentAdvice,
156
+ incidentRule: opts.incidentRule,
157
+ updatedBy,
158
+ });
159
+ if (!plan) {
160
+ return null;
161
+ }
162
+ targetSection = plan.targetSection;
163
+ return {
164
+ intents: plan.intents,
165
+ writeOptions: {
166
+ expectedCurrentText: plan.expectedCurrentText,
167
+ expectedSection: targetSection,
168
+ },
169
+ };
170
+ },
171
+ });
172
+ const tasksDir = `${resolved.gitRoot}/${config.paths.workflow_dir}`;
173
+ process.stdout.write(`${tasksDir}/${opts.taskId}/README.md\n`);
174
+ process.stderr.write(`${infoMessage(`task findings add outcome=entry-appended section=${targetSection}`)}\n`);
175
+ process.stderr.write(`${infoMessage(renderFindingsAddRegistryNote({
176
+ promote: opts.promote,
177
+ external: opts.external,
178
+ taskId: opts.taskId,
179
+ branchPr: config.workflow_mode === "branch_pr",
180
+ }))}\n`);
181
+ return 0;
182
+ }
183
+ catch (err) {
184
+ if (err instanceof CliError)
185
+ throw err;
186
+ throw mapBackendError(err, { command: "task findings add", root: opts.rootOverride ?? null });
187
+ }
188
+ }
@@ -56,5 +56,6 @@ export declare function createTaskCloseCommit(opts: {
56
56
  baseBranchOverride?: string;
57
57
  quiet: boolean;
58
58
  closeUnstageOthers?: boolean;
59
+ allowPolicy?: boolean;
59
60
  }): Promise<void>;
60
61
  //# sourceMappingURL=finish-shared.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"finish-shared.d.ts","sourceRoot":"","sources":["../../../src/commands/task/finish-shared.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gCAAgC,CAAC;AAG/D,OAAO,EAAuB,KAAK,cAAc,EAAE,MAAM,2BAA2B,CAAC;AACrF,OAAO,EAA6B,YAAY,EAAmB,MAAM,yBAAyB,CAAC;AAWnG,MAAM,MAAM,kBAAkB,GAAG;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,QAAQ,CAAC;CAChB,CAAC;AAEF,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,QAAQ,GAAG,kBAAkB,GAAG,IAAI,CAK5E;AAED,wBAAgB,mBAAmB,CAAC,IAAI,EAAE;IACxC,IAAI,EAAE,QAAQ,CAAC;IACf,MAAM,EAAE,cAAc,CAAC,QAAQ,CAAC,CAAC;IACjC,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,OAAO,CAAC;IACpB,cAAc,EAAE,OAAO,CAAC;IACxB,aAAa,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,OAAO,CAAC;CAChB,GAAG,IAAI,CAyCP;AAED,wBAAsB,iBAAiB,CAAC,IAAI,EAAE;IAC5C,GAAG,EAAE,cAAc,CAAC;IACpB,KAAK,EAAE,UAAU,CAAC,OAAO,YAAY,CAAC,GAAG,IAAI,CAAC;IAC9C,QAAQ,EAAE,OAAO,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE,OAAO,CAAC;IACxB,aAAa,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,OAAO,CAAC;IACf,2BAA2B,EAAE,OAAO,CAAC;CACtC,GAAG,OAAO,CAAC;IACV,MAAM,EAAE,gBAAgB,CAAC;IACzB,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B,CAAC,CAuDD;AAED,wBAAsB,kBAAkB,CAAC,IAAI,EAAE;IAC7C,GAAG,EAAE,cAAc,CAAC;IACpB,WAAW,EAAE,gBAAgB,EAAE,CAAC;IAChC,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,OAAO,CAAC;IACf,cAAc,EAAE,OAAO,CAAC;IACxB,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,KAAK,GAAG,KAAK,GAAG,MAAM,CAAC;IACnC,QAAQ,EAAE,OAAO,CAAC;IAClB,cAAc,EAAE,kBAAkB,GAAG,IAAI,CAAC;CAC3C,GAAG,OAAO,CAAC,IAAI,CAAC,CAuDhB;AAED,wBAAsB,qBAAqB,CAAC,IAAI,EAAE;IAChD,GAAG,EAAE,cAAc,CAAC;IACpB,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,KAAK,EAAE,OAAO,CAAC;IACf,kBAAkB,CAAC,EAAE,OAAO,CAAC;CAC9B,GAAG,OAAO,CAAC,IAAI,CAAC,CAuBhB"}
1
+ {"version":3,"file":"finish-shared.d.ts","sourceRoot":"","sources":["../../../src/commands/task/finish-shared.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gCAAgC,CAAC;AAG/D,OAAO,EAAuB,KAAK,cAAc,EAAE,MAAM,2BAA2B,CAAC;AACrF,OAAO,EAA6B,YAAY,EAAmB,MAAM,yBAAyB,CAAC;AAWnG,MAAM,MAAM,kBAAkB,GAAG;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,QAAQ,CAAC;CAChB,CAAC;AA0CF,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,QAAQ,GAAG,kBAAkB,GAAG,IAAI,CAK5E;AAED,wBAAgB,mBAAmB,CAAC,IAAI,EAAE;IACxC,IAAI,EAAE,QAAQ,CAAC;IACf,MAAM,EAAE,cAAc,CAAC,QAAQ,CAAC,CAAC;IACjC,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,OAAO,CAAC;IACpB,cAAc,EAAE,OAAO,CAAC;IACxB,aAAa,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,OAAO,CAAC;CAChB,GAAG,IAAI,CAyCP;AAED,wBAAsB,iBAAiB,CAAC,IAAI,EAAE;IAC5C,GAAG,EAAE,cAAc,CAAC;IACpB,KAAK,EAAE,UAAU,CAAC,OAAO,YAAY,CAAC,GAAG,IAAI,CAAC;IAC9C,QAAQ,EAAE,OAAO,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE,OAAO,CAAC;IACxB,aAAa,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,OAAO,CAAC;IACf,2BAA2B,EAAE,OAAO,CAAC;CACtC,GAAG,OAAO,CAAC;IACV,MAAM,EAAE,gBAAgB,CAAC;IACzB,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B,CAAC,CAuDD;AAED,wBAAsB,kBAAkB,CAAC,IAAI,EAAE;IAC7C,GAAG,EAAE,cAAc,CAAC;IACpB,WAAW,EAAE,gBAAgB,EAAE,CAAC;IAChC,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,OAAO,CAAC;IACf,cAAc,EAAE,OAAO,CAAC;IACxB,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,KAAK,GAAG,KAAK,GAAG,MAAM,CAAC;IACnC,QAAQ,EAAE,OAAO,CAAC;IAClB,cAAc,EAAE,kBAAkB,GAAG,IAAI,CAAC;CAC3C,GAAG,OAAO,CAAC,IAAI,CAAC,CAuEhB;AAED,wBAAsB,qBAAqB,CAAC,IAAI,EAAE;IAChD,GAAG,EAAE,cAAc,CAAC;IACpB,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,KAAK,EAAE,OAAO,CAAC;IACf,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB,GAAG,OAAO,CAAC,IAAI,CAAC,CAuBhB"}
@@ -4,6 +4,46 @@ import { cmdCommit } from "../guard/index.js";
4
4
  import { loadTaskFromContext } from "../shared/task-backend.js";
5
5
  import { backendIsLocalFileBackend, getTaskStore, mutateTaskStore } from "../shared/task-store.js";
6
6
  import { ensureAgentFilledRequiredDocSections, executeTaskStatusTransitionRequest, ensureVerificationSatisfiedIfRequired, nowIso, resolvePrimaryTag, toStringArray, } from "./shared.js";
7
+ function normalizeCommentBody(value) {
8
+ return typeof value === "string" ? value.trim() : "";
9
+ }
10
+ function commitInfoMatches(current, requested) {
11
+ if (!requested)
12
+ return true;
13
+ const existing = existingCommitInfo(current);
14
+ if (!existing)
15
+ return false;
16
+ return existing.hash === requested.hash && existing.message === requested.message;
17
+ }
18
+ function isIdempotentDoneRetry(opts) {
19
+ if (String(opts.task.status ?? "TODO").toUpperCase() !== "DONE")
20
+ return false;
21
+ const lastComment = opts.task.comments?.at(-1) ?? null;
22
+ const lastEvent = opts.task.events?.at(-1) ?? null;
23
+ if (lastComment?.author !== opts.author)
24
+ return false;
25
+ if (normalizeCommentBody(lastComment.body) !== normalizeCommentBody(opts.body))
26
+ return false;
27
+ if (lastEvent?.type !== "status" || lastEvent.author !== opts.author)
28
+ return false;
29
+ if (String(lastEvent.from ?? "").toUpperCase() !== "DOING")
30
+ return false;
31
+ if (String(lastEvent.to ?? "").toUpperCase() !== "DONE")
32
+ return false;
33
+ if (normalizeCommentBody(lastEvent.note) !== normalizeCommentBody(opts.body))
34
+ return false;
35
+ if (!commitInfoMatches(opts.task, opts.taskCommitInfo))
36
+ return false;
37
+ if (opts.taskId === opts.metaTaskId) {
38
+ if ((opts.task.result_summary ?? "") !== opts.resultSummary)
39
+ return false;
40
+ if ((opts.task.risk_level ?? undefined) !== opts.riskLevel)
41
+ return false;
42
+ if (Boolean(opts.task.breaking) !== opts.breaking)
43
+ return false;
44
+ }
45
+ return true;
46
+ }
7
47
  export function existingCommitInfo(task) {
8
48
  const hash = typeof task.commit?.hash === "string" ? task.commit.hash.trim() : "";
9
49
  if (!hash)
@@ -120,6 +160,20 @@ export async function writeFinishedTasks(opts) {
120
160
  resultSummary: opts.resultSummary,
121
161
  force: opts.force,
122
162
  });
163
+ if (opts.force &&
164
+ isIdempotentDoneRetry({
165
+ task: currentTask,
166
+ author: opts.author,
167
+ body: opts.body,
168
+ resultSummary: opts.resultSummary,
169
+ metaTaskId: opts.metaTaskId,
170
+ taskId,
171
+ riskLevel: opts.riskLevel,
172
+ breaking: opts.breaking,
173
+ taskCommitInfo: opts.taskCommitInfo,
174
+ })) {
175
+ return { intents: [], nextTask: currentTask };
176
+ }
123
177
  return await executeTaskStatusTransitionRequest({
124
178
  task: currentTask,
125
179
  backend: opts.ctx.taskBackend,
@@ -171,7 +225,7 @@ export async function createTaskCloseCommit(opts) {
171
225
  autoAllow: false,
172
226
  allowTasks: true,
173
227
  allowBase: opts.ctx.config.workflow_mode === "branch_pr",
174
- allowPolicy: false,
228
+ allowPolicy: opts.allowPolicy === true,
175
229
  allowConfig: false,
176
230
  allowHooks: false,
177
231
  allowCI: false,
@@ -28,6 +28,16 @@ export declare function cmdFinish(opts: {
28
28
  noCloseCommit?: boolean;
29
29
  closeUnstageOthers?: boolean;
30
30
  baseBranchOverride?: string;
31
+ observation?: string;
32
+ impact?: string;
33
+ resolution?: string;
34
+ localOnly?: boolean;
35
+ repoFixable?: boolean;
36
+ incidentScope?: string;
37
+ incidentTags?: string[];
38
+ incidentMatch?: string[];
39
+ incidentAdvice?: string;
40
+ incidentRule?: string;
31
41
  quiet: boolean;
32
42
  }): Promise<number>;
33
43
  //# sourceMappingURL=finish.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"finish.d.ts","sourceRoot":"","sources":["../../../src/commands/task/finish.ts"],"names":[],"mappings":"AAQA,OAAO,EAAsB,KAAK,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAsCpF,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,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,KAAK,EAAE,OAAO,CAAC;CAChB,GAAG,OAAO,CAAC,MAAM,CAAC,CAuUlB"}
1
+ {"version":3,"file":"finish.d.ts","sourceRoot":"","sources":["../../../src/commands/task/finish.ts"],"names":[],"mappings":"AAYA,OAAO,EAAsB,KAAK,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAsHpF,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,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;IACzB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,OAAO,CAAC;CAChB,GAAG,OAAO,CAAC,MAAM,CAAC,CA0YlB"}
@@ -1,14 +1,19 @@
1
+ import { resolveBaseBranch } from "@agentplaneorg/core";
1
2
  import { mapBackendError } from "../../cli/error-map.js";
3
+ import { exitCodeForError } from "../../cli/exit-codes.js";
2
4
  import { infoMessage, invalidValueMessage, successMessage } from "../../cli/output.js";
3
5
  import { CliError } from "../../shared/errors.js";
4
6
  import { readFile, rm } from "node:fs/promises";
5
7
  import path from "node:path";
6
8
  import { ensureActionApproved } from "../shared/approval-requirements.js";
7
9
  import { ensureReconciledBeforeMutation } from "../shared/reconcile-check.js";
10
+ import { gitCurrentBranch } from "../shared/git-ops.js";
8
11
  import { loadCommandContext } from "../shared/task-backend.js";
9
12
  import { backendIsLocalFileBackend, getTaskStore } from "../shared/task-store.js";
10
- import { collectTaskIncidents } from "../incidents/shared.js";
13
+ import { applyTaskMutation } from "../shared/task-mutation.js";
14
+ import { collectTaskIncidents, renderIncidentCollectionPlanOutcome } from "../incidents/shared.js";
11
15
  import { createTaskCloseCommit, existingCommitInfo, loadTaskForFinish, writeFinishedTasks, } from "./finish-shared.js";
16
+ import { buildStructuredFindingMutationPlan } from "./findings.js";
12
17
  import { defaultCommitEmojiForStatus, enforceStatusCommitPolicy, prepareTaskTransitionComment, readCommitInfo, requireStructuredComment, runTaskTransitionCommentCommit, } from "./shared.js";
13
18
  async function clearDirectWorkLockIfMatches(opts) {
14
19
  const lockPath = path.join(opts.agentplaneDir, "cache", "direct-work.json");
@@ -26,11 +31,68 @@ async function clearDirectWorkLockIfMatches(opts) {
26
31
  // best-effort
27
32
  }
28
33
  }
34
+ async function ensureFinishRunsOnBaseBranch(opts) {
35
+ if (opts.ctx.config.workflow_mode !== "branch_pr")
36
+ return;
37
+ const baseBranch = await resolveBaseBranch({
38
+ cwd: opts.cwd,
39
+ rootOverride: opts.rootOverride ?? null,
40
+ cliBaseOpt: opts.baseBranchOverride ?? null,
41
+ mode: opts.ctx.config.workflow_mode,
42
+ });
43
+ if (!baseBranch) {
44
+ throw new CliError({
45
+ exitCode: exitCodeForError("E_USAGE"),
46
+ code: "E_USAGE",
47
+ message: "Base branch could not be resolved (use `agentplane branch base set` or --base).",
48
+ });
49
+ }
50
+ const currentBranch = await gitCurrentBranch(opts.ctx.resolvedProject.gitRoot);
51
+ if (currentBranch === baseBranch)
52
+ return;
53
+ throw new CliError({
54
+ exitCode: exitCodeForError("E_GIT"),
55
+ code: "E_GIT",
56
+ message: `finish must run on base branch ${baseBranch} in branch_pr mode ` +
57
+ `(current: ${currentBranch}); integrate first or reconcile from the base checkout.`,
58
+ });
59
+ }
60
+ async function appendFinishStructuredFinding(opts) {
61
+ await applyTaskMutation({
62
+ ctx: opts.ctx,
63
+ taskId: opts.taskId,
64
+ build: (current) => {
65
+ const plan = buildStructuredFindingMutationPlan({
66
+ current,
67
+ config: opts.ctx.config,
68
+ observation: opts.finding.observation,
69
+ impact: opts.finding.impact,
70
+ resolution: opts.finding.resolution,
71
+ promote: !opts.finding.localOnly,
72
+ external: !opts.finding.localOnly,
73
+ fixability: opts.finding.repoFixable ? "repo-fixable" : null,
74
+ incidentScope: opts.finding.incidentScope,
75
+ incidentTags: opts.finding.incidentTags,
76
+ incidentMatch: opts.finding.incidentMatch,
77
+ incidentAdvice: opts.finding.incidentAdvice,
78
+ incidentRule: opts.finding.incidentRule,
79
+ updatedBy: opts.author,
80
+ });
81
+ return plan ? { intents: plan.intents } : null;
82
+ },
83
+ });
84
+ }
29
85
  export async function cmdFinish(opts) {
30
86
  try {
31
87
  const ctx = opts.ctx ??
32
88
  (await loadCommandContext({ cwd: opts.cwd, rootOverride: opts.rootOverride ?? null }));
33
89
  await ensureReconciledBeforeMutation({ ctx, command: "finish" });
90
+ await ensureFinishRunsOnBaseBranch({
91
+ ctx,
92
+ cwd: opts.cwd,
93
+ rootOverride: opts.rootOverride,
94
+ baseBranchOverride: opts.baseBranchOverride,
95
+ });
34
96
  if (opts.force) {
35
97
  await ensureActionApproved({
36
98
  action: "force_action",
@@ -140,6 +202,49 @@ export async function cmdFinish(opts) {
140
202
  const resultSummary = typeof opts.result === "string" ? opts.result.trim() : "";
141
203
  const riskLevel = opts.risk;
142
204
  const breaking = opts.breaking === true;
205
+ const finishFinding = typeof opts.observation === "string" &&
206
+ typeof opts.impact === "string" &&
207
+ typeof opts.resolution === "string"
208
+ ? {
209
+ observation: opts.observation,
210
+ impact: opts.impact,
211
+ resolution: opts.resolution,
212
+ localOnly: opts.localOnly === true,
213
+ repoFixable: opts.repoFixable === true,
214
+ incidentScope: opts.incidentScope,
215
+ incidentTags: opts.incidentTags ?? [],
216
+ incidentMatch: opts.incidentMatch ?? [],
217
+ incidentAdvice: opts.incidentAdvice,
218
+ incidentRule: opts.incidentRule,
219
+ }
220
+ : null;
221
+ if (finishFinding && opts.taskIds.length !== 1) {
222
+ throw new CliError({
223
+ exitCode: 2,
224
+ code: "E_USAGE",
225
+ message: "--observation/--impact/--resolution and incident finding options require exactly one task id",
226
+ });
227
+ }
228
+ if (finishFinding && primaryTaskId) {
229
+ await loadTaskForFinish({
230
+ ctx,
231
+ store,
232
+ useStore,
233
+ taskId: primaryTaskId,
234
+ taskCount: opts.taskIds.length,
235
+ metaTaskId,
236
+ resultProvided,
237
+ resultSummary,
238
+ force: opts.force,
239
+ capturePrimaryLifecycleMeta: false,
240
+ });
241
+ await appendFinishStructuredFinding({
242
+ ctx,
243
+ taskId: primaryTaskId,
244
+ author: opts.author,
245
+ finding: finishFinding,
246
+ });
247
+ }
143
248
  let primaryStatusFrom = null;
144
249
  let primaryTag = null;
145
250
  const loadedTasks = [];
@@ -273,7 +378,8 @@ export async function cmdFinish(opts) {
273
378
  breaking,
274
379
  taskCommitInfo,
275
380
  });
276
- let promotedIncidents = 0;
381
+ const incidentPlans = [];
382
+ const incidentRegistryPaths = [];
277
383
  for (const taskId of opts.taskIds) {
278
384
  const loadedTask = loadedTasks.find((candidate) => candidate.taskId === taskId) ?? null;
279
385
  const collected = await collectTaskIncidents({
@@ -282,8 +388,10 @@ export async function cmdFinish(opts) {
282
388
  task: loadedTask?.task ?? null,
283
389
  write: true,
284
390
  });
285
- promotedIncidents += collected.plan.promotable.length;
391
+ incidentPlans.push(collected.plan);
392
+ incidentRegistryPaths.push(collected.registryPaths);
286
393
  }
394
+ const promotedIncidents = incidentPlans.reduce((sum, plan) => sum + plan.promotable.length, 0);
287
395
  // tasks.json is export-only; generated via `agentplane task export`.
288
396
  if (shouldCloseCommit && primaryTaskId) {
289
397
  if (!opts.quiet) {
@@ -297,6 +405,7 @@ export async function cmdFinish(opts) {
297
405
  baseBranchOverride: opts.baseBranchOverride,
298
406
  quiet: opts.quiet,
299
407
  closeUnstageOthers: opts.closeCommit === true && opts.closeUnstageOthers === true,
408
+ allowPolicy: promotedIncidents > 0,
300
409
  });
301
410
  }
302
411
  if (ctx.config.workflow_mode === "direct") {
@@ -306,9 +415,19 @@ export async function cmdFinish(opts) {
306
415
  });
307
416
  }
308
417
  if (!opts.quiet) {
309
- if (promotedIncidents > 0) {
310
- process.stdout.write(`${infoMessage(`incident registry updated (${promotedIncidents} promoted)`)}\n`);
311
- }
418
+ const incidentPlan = incidentPlans[0] ?? {
419
+ candidates: [],
420
+ skipped: [],
421
+ promotable: [],
422
+ duplicates: [],
423
+ };
424
+ process.stdout.write(`${infoMessage(renderIncidentCollectionPlanOutcome(incidentPlan, {
425
+ wrote: promotedIncidents > 0,
426
+ context: "finish",
427
+ promotedIds: incidentPlan.promotable.map((item) => item.entry.id),
428
+ registryPaths: incidentRegistryPaths[0] ?? [],
429
+ taskId: opts.taskIds[0] ?? null,
430
+ }))}\n`);
312
431
  process.stdout.write(`${successMessage("finished")}\n`);
313
432
  }
314
433
  return 0;
@@ -0,0 +1,11 @@
1
+ import type { CommandCtx, CommandSpec } from "../../cli/spec/spec.js";
2
+ import { type CommandContext } from "../shared/task-backend.js";
3
+ type TaskHostedClosePrParsed = {
4
+ taskIds: string[];
5
+ branch: string | null;
6
+ repo: string | null;
7
+ };
8
+ export declare const taskHostedClosePrSpec: CommandSpec<TaskHostedClosePrParsed>;
9
+ export declare function makeRunTaskHostedClosePrHandler(getCtx: (cmd: string) => Promise<CommandContext>): (ctx: CommandCtx, p: TaskHostedClosePrParsed) => Promise<number>;
10
+ export {};
11
+ //# sourceMappingURL=hosted-close-pr.command.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hosted-close-pr.command.d.ts","sourceRoot":"","sources":["../../../src/commands/task/hosted-close-pr.command.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAQtE,OAAO,EAGL,KAAK,cAAc,EACpB,MAAM,2BAA2B,CAAC;AAQnC,KAAK,uBAAuB,GAAG;IAC7B,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;CACrB,CAAC;AAgBF,eAAO,MAAM,qBAAqB,EAAE,WAAW,CAAC,uBAAuB,CAoEtE,CAAC;AAmYF,wBAAgB,+BAA+B,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,cAAc,CAAC,IAChF,KAAK,UAAU,EAAE,GAAG,uBAAuB,KAAG,OAAO,CAAC,MAAM,CAAC,CAsB5E"}