agentplane 0.2.19 → 0.2.21

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 (103) hide show
  1. package/assets/AGENTS.md +4 -3
  2. package/bin/agentplane.js +24 -0
  3. package/dist/backends/task-backend/load.d.ts +2 -0
  4. package/dist/backends/task-backend/load.d.ts.map +1 -1
  5. package/dist/backends/task-backend/load.js +38 -18
  6. package/dist/cli/command-guide.d.ts.map +1 -1
  7. package/dist/cli/command-guide.js +12 -11
  8. package/dist/cli/run-cli/command-catalog.d.ts +1 -1
  9. package/dist/cli/run-cli/command-catalog.d.ts.map +1 -1
  10. package/dist/cli/run-cli/command-catalog.js +12 -1
  11. package/dist/cli/run-cli/commands/core.d.ts +9 -1
  12. package/dist/cli/run-cli/commands/core.d.ts.map +1 -1
  13. package/dist/cli/run-cli/commands/core.js +268 -8
  14. package/dist/cli/run-cli/commands/init/write-config.d.ts.map +1 -1
  15. package/dist/cli/run-cli/commands/init/write-config.js +2 -0
  16. package/dist/cli/run-cli/commands/init/write-gitignore.d.ts.map +1 -1
  17. package/dist/cli/run-cli/commands/init/write-gitignore.js +3 -18
  18. package/dist/cli/run-cli/commands/init.d.ts +1 -0
  19. package/dist/cli/run-cli/commands/init.d.ts.map +1 -1
  20. package/dist/cli/run-cli/commands/init.js +55 -30
  21. package/dist/cli/run-cli.d.ts.map +1 -1
  22. package/dist/cli/run-cli.js +85 -14
  23. package/dist/commands/commit.command.d.ts.map +1 -1
  24. package/dist/commands/commit.command.js +2 -0
  25. package/dist/commands/commit.spec.d.ts +2 -0
  26. package/dist/commands/commit.spec.d.ts.map +1 -1
  27. package/dist/commands/commit.spec.js +26 -0
  28. package/dist/commands/doctor.run.d.ts.map +1 -1
  29. package/dist/commands/doctor.run.js +15 -6
  30. package/dist/commands/finish.run.d.ts.map +1 -1
  31. package/dist/commands/finish.run.js +2 -0
  32. package/dist/commands/finish.spec.d.ts +2 -0
  33. package/dist/commands/finish.spec.d.ts.map +1 -1
  34. package/dist/commands/finish.spec.js +36 -0
  35. package/dist/commands/guard/impl/allow.d.ts.map +1 -1
  36. package/dist/commands/guard/impl/allow.js +33 -7
  37. package/dist/commands/guard/impl/commands.d.ts +2 -0
  38. package/dist/commands/guard/impl/commands.d.ts.map +1 -1
  39. package/dist/commands/guard/impl/commands.js +24 -4
  40. package/dist/commands/guard/impl/comment-commit.d.ts +1 -0
  41. package/dist/commands/guard/impl/comment-commit.d.ts.map +1 -1
  42. package/dist/commands/guard/impl/comment-commit.js +16 -24
  43. package/dist/commands/release/apply.command.d.ts.map +1 -1
  44. package/dist/commands/release/apply.command.js +51 -3
  45. package/dist/commands/release/plan.command.d.ts.map +1 -1
  46. package/dist/commands/release/plan.command.js +25 -1
  47. package/dist/commands/shared/task-backend.d.ts +3 -0
  48. package/dist/commands/shared/task-backend.d.ts.map +1 -1
  49. package/dist/commands/shared/task-backend.js +4 -1
  50. package/dist/commands/task/block.d.ts.map +1 -1
  51. package/dist/commands/task/block.js +12 -9
  52. package/dist/commands/task/close-duplicate.command.d.ts +14 -0
  53. package/dist/commands/task/close-duplicate.command.d.ts.map +1 -0
  54. package/dist/commands/task/close-duplicate.command.js +102 -0
  55. package/dist/commands/task/close-duplicate.d.ts +14 -0
  56. package/dist/commands/task/close-duplicate.d.ts.map +1 -0
  57. package/dist/commands/task/close-duplicate.js +90 -0
  58. package/dist/commands/task/close-noop.command.d.ts +14 -0
  59. package/dist/commands/task/close-noop.command.d.ts.map +1 -0
  60. package/dist/commands/task/close-noop.command.js +77 -0
  61. package/dist/commands/task/close-noop.d.ts +13 -0
  62. package/dist/commands/task/close-noop.d.ts.map +1 -0
  63. package/dist/commands/task/close-noop.js +77 -0
  64. package/dist/commands/task/finish.d.ts +2 -0
  65. package/dist/commands/task/finish.d.ts.map +1 -1
  66. package/dist/commands/task/finish.js +52 -10
  67. package/dist/commands/task/index.d.ts +3 -0
  68. package/dist/commands/task/index.d.ts.map +1 -1
  69. package/dist/commands/task/index.js +3 -0
  70. package/dist/commands/task/new.d.ts.map +1 -1
  71. package/dist/commands/task/new.js +34 -6
  72. package/dist/commands/task/new.spec.js +1 -1
  73. package/dist/commands/task/plan.d.ts.map +1 -1
  74. package/dist/commands/task/plan.js +2 -3
  75. package/dist/commands/task/set-status.d.ts.map +1 -1
  76. package/dist/commands/task/set-status.js +12 -9
  77. package/dist/commands/task/shared.d.ts +19 -0
  78. package/dist/commands/task/shared.d.ts.map +1 -1
  79. package/dist/commands/task/shared.js +137 -0
  80. package/dist/commands/task/start-ready.command.d.ts +14 -0
  81. package/dist/commands/task/start-ready.command.d.ts.map +1 -0
  82. package/dist/commands/task/start-ready.command.js +77 -0
  83. package/dist/commands/task/start-ready.d.ts +13 -0
  84. package/dist/commands/task/start-ready.d.ts.map +1 -0
  85. package/dist/commands/task/start-ready.js +46 -0
  86. package/dist/commands/task/start.d.ts.map +1 -1
  87. package/dist/commands/task/start.js +13 -11
  88. package/dist/commands/task/update.command.d.ts +1 -0
  89. package/dist/commands/task/update.command.d.ts.map +1 -1
  90. package/dist/commands/task/update.command.js +8 -0
  91. package/dist/commands/task/update.d.ts +1 -0
  92. package/dist/commands/task/update.d.ts.map +1 -1
  93. package/dist/commands/task/update.js +19 -3
  94. package/dist/shared/errors.d.ts +9 -1
  95. package/dist/shared/errors.d.ts.map +1 -1
  96. package/dist/shared/errors.js +3 -1
  97. package/dist/shared/runtime-artifacts.d.ts +3 -0
  98. package/dist/shared/runtime-artifacts.d.ts.map +1 -0
  99. package/dist/shared/runtime-artifacts.js +18 -0
  100. package/dist/usecases/context/resolve-context.d.ts +3 -0
  101. package/dist/usecases/context/resolve-context.d.ts.map +1 -1
  102. package/dist/usecases/context/resolve-context.js +6 -1
  103. package/package.json +2 -2
@@ -28,5 +28,7 @@ export declare function cmdCommit(opts: {
28
28
  allowCI: boolean;
29
29
  requireClean: boolean;
30
30
  quiet: boolean;
31
+ closeUnstageOthers: boolean;
32
+ closeCheckOnly: boolean;
31
33
  }): Promise<number>;
32
34
  //# sourceMappingURL=commands.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"commands.d.ts","sourceRoot":"","sources":["../../../../src/commands/guard/impl/commands.ts"],"names":[],"mappings":"AAGA,OAAO,EAAsB,KAAK,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAMvF,OAAO,EAAoB,KAAK,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAyDxE,wBAAsB,aAAa,CAAC,IAAI,EAAE;IACxC,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,OAAO,CAAC;CAChB,GAAG,OAAO,CAAC,MAAM,CAAC,CAsBlB;AAED,wBAAsB,oBAAoB,CAAC,IAAI,EAAE;IAC/C,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,OAAO,GAAG,MAAM,CAAC;CAC1B,GAAG,OAAO,CAAC,MAAM,CAAC,CAyBlB;AAED,wBAAsB,cAAc,CAAC,IAAI,EAAE,kBAAkB,GAAG,OAAO,CAAC,MAAM,CAAC,CAS9E;AAED,wBAAsB,SAAS,CAAC,IAAI,EAAE;IACpC,GAAG,CAAC,EAAE,cAAc,CAAC;IACrB,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,OAAO,CAAC;IACf,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,SAAS,EAAE,OAAO,CAAC;IACnB,UAAU,EAAE,OAAO,CAAC;IACpB,SAAS,EAAE,OAAO,CAAC;IACnB,WAAW,EAAE,OAAO,CAAC;IACrB,WAAW,EAAE,OAAO,CAAC;IACrB,UAAU,EAAE,OAAO,CAAC;IACpB,OAAO,EAAE,OAAO,CAAC;IACjB,YAAY,EAAE,OAAO,CAAC;IACtB,KAAK,EAAE,OAAO,CAAC;CAChB,GAAG,OAAO,CAAC,MAAM,CAAC,CAkIlB"}
1
+ {"version":3,"file":"commands.d.ts","sourceRoot":"","sources":["../../../../src/commands/guard/impl/commands.ts"],"names":[],"mappings":"AAGA,OAAO,EAAsB,KAAK,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAOvF,OAAO,EAAoB,KAAK,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAyDxE,wBAAsB,aAAa,CAAC,IAAI,EAAE;IACxC,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,OAAO,CAAC;CAChB,GAAG,OAAO,CAAC,MAAM,CAAC,CAsBlB;AAED,wBAAsB,oBAAoB,CAAC,IAAI,EAAE;IAC/C,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,OAAO,GAAG,MAAM,CAAC;CAC1B,GAAG,OAAO,CAAC,MAAM,CAAC,CAyBlB;AAED,wBAAsB,cAAc,CAAC,IAAI,EAAE,kBAAkB,GAAG,OAAO,CAAC,MAAM,CAAC,CAS9E;AAED,wBAAsB,SAAS,CAAC,IAAI,EAAE;IACpC,GAAG,CAAC,EAAE,cAAc,CAAC;IACrB,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,OAAO,CAAC;IACf,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,SAAS,EAAE,OAAO,CAAC;IACnB,UAAU,EAAE,OAAO,CAAC;IACpB,SAAS,EAAE,OAAO,CAAC;IACnB,WAAW,EAAE,OAAO,CAAC;IACrB,WAAW,EAAE,OAAO,CAAC;IACrB,UAAU,EAAE,OAAO,CAAC;IACpB,OAAO,EAAE,OAAO,CAAC;IACjB,YAAY,EAAE,OAAO,CAAC;IACtB,KAAK,EAAE,OAAO,CAAC;IACf,kBAAkB,EAAE,OAAO,CAAC;IAC5B,cAAc,EAAE,OAAO,CAAC;CACzB,GAAG,OAAO,CAAC,MAAM,CAAC,CAyJlB"}
@@ -3,6 +3,7 @@ import { successMessage } from "../../../cli/output.js";
3
3
  import { CliError } from "../../../shared/errors.js";
4
4
  import { loadCommandContext } from "../../shared/task-backend.js";
5
5
  import { loadTaskFromContext } from "../../shared/task-backend.js";
6
+ import { execFileAsync, gitEnv } from "../../shared/git.js";
6
7
  import { suggestAllowPrefixes } from "./allow.js";
7
8
  import { buildCloseCommitMessage, taskReadmePathForTask } from "./close-message.js";
8
9
  import { buildGitCommitEnv } from "./env.js";
@@ -126,13 +127,22 @@ export async function cmdCommit(opts) {
126
127
  if (opts.close) {
127
128
  const ctx = opts.ctx ??
128
129
  (await loadCommandContext({ cwd: opts.cwd, rootOverride: opts.rootOverride ?? null }));
129
- // Make the close commit deterministic: require empty index, then stage only the task README.
130
- const staged = await ctx.git.statusStagedPaths();
131
- if (staged.length > 0) {
130
+ // Make the close commit deterministic: start from a clean index unless --unstage-others is used.
131
+ let staged = await ctx.git.statusStagedPaths();
132
+ if (staged.length > 0 && opts.closeUnstageOthers) {
133
+ if (!opts.closeCheckOnly) {
134
+ await execFileAsync("git", ["restore", "--staged", "--", "."], {
135
+ cwd: ctx.resolvedProject.gitRoot,
136
+ env: gitEnv(),
137
+ });
138
+ }
139
+ staged = opts.closeCheckOnly ? staged : await ctx.git.statusStagedPaths();
140
+ }
141
+ if (staged.length > 0 && !opts.closeUnstageOthers) {
132
142
  throw new CliError({
133
143
  exitCode: 5,
134
144
  code: "E_GIT",
135
- message: "Staged files exist (close commit requires an empty index)",
145
+ message: "Staged files exist (close commit requires an empty index; rerun with --unstage-others to auto-unstage).",
136
146
  });
137
147
  }
138
148
  const task = await loadTaskFromContext({ ctx, taskId: opts.taskId });
@@ -145,6 +155,16 @@ export async function cmdCommit(opts) {
145
155
  const readmeRel = readmeAbs.startsWith(ctx.resolvedProject.gitRoot)
146
156
  ? readmeAbs.slice(ctx.resolvedProject.gitRoot.length + 1)
147
157
  : readmeAbs;
158
+ if (opts.closeCheckOnly) {
159
+ if (!opts.quiet) {
160
+ const stagedCount = staged.length;
161
+ const suffix = stagedCount > 0 && opts.closeUnstageOthers
162
+ ? `; would unstage ${stagedCount} path(s)`
163
+ : "";
164
+ process.stdout.write(`${successMessage("close preflight", opts.taskId, `subject=${msg.subject}${suffix}`)}\n`);
165
+ }
166
+ return 0;
167
+ }
148
168
  await ctx.git.stage([readmeRel]);
149
169
  // Close commits should not require manual --allow flags:
150
170
  // the command stages exactly one task README under workflow_dir.
@@ -5,6 +5,7 @@ export declare function commitFromComment(opts: {
5
5
  cwd: string;
6
6
  rootOverride?: string;
7
7
  taskId: string;
8
+ primaryTag: string;
8
9
  executorAgent?: string;
9
10
  author?: string;
10
11
  statusFrom?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"comment-commit.d.ts","sourceRoot":"","sources":["../../../../src/commands/guard/impl/comment-commit.ts"],"names":[],"mappings":"AAAA,OAAO,EAAqB,KAAK,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAS/E,OAAO,EAAsB,KAAK,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAsEvF,wBAAsB,iBAAiB,CAAC,IAAI,EAAE;IAC5C,GAAG,CAAC,EAAE,cAAc,CAAC;IACrB,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,SAAS,EAAE,OAAO,CAAC;IACnB,UAAU,EAAE,OAAO,CAAC;IACpB,YAAY,EAAE,OAAO,CAAC;IACtB,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,EAAE,gBAAgB,CAAC;CAC1B,GAAG,OAAO,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC,CA+F/D"}
1
+ {"version":3,"file":"comment-commit.d.ts","sourceRoot":"","sources":["../../../../src/commands/guard/impl/comment-commit.ts"],"names":[],"mappings":"AAAA,OAAO,EAAqB,KAAK,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAS/E,OAAO,EAAsB,KAAK,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAiEvF,wBAAsB,iBAAiB,CAAC,IAAI,EAAE;IAC5C,GAAG,CAAC,EAAE,cAAc,CAAC;IACrB,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,SAAS,EAAE,OAAO,CAAC;IACnB,UAAU,EAAE,OAAO,CAAC;IACpB,YAAY,EAAE,OAAO,CAAC;IACtB,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,EAAE,gBAAgB,CAAC;CAC1B,GAAG,OAAO,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC,CA+F/D"}
@@ -8,24 +8,6 @@ import { buildGitCommitEnv } from "./env.js";
8
8
  import { stageAllowlist, suggestAllowPrefixes } from "./allow.js";
9
9
  import { guardCommitCheck } from "./policy.js";
10
10
  function deriveCommitMessageFromComment(opts) {
11
- const raw = (opts.formattedComment ?? formatCommentBodyForCommit(opts.body, opts.config))
12
- .trim()
13
- .replaceAll(/\s+/g, " ");
14
- if (!raw) {
15
- throw new CliError({
16
- exitCode: 2,
17
- code: "E_USAGE",
18
- message: "Comment body is required to build a commit message from the task comment",
19
- });
20
- }
21
- const summary = raw.replace(/^(start|blocked|verified):\s*/i, "").trim();
22
- if (!summary) {
23
- throw new CliError({
24
- exitCode: 2,
25
- code: "E_USAGE",
26
- message: "Comment body is required to build a commit message from the task comment",
27
- });
28
- }
29
11
  const prefix = opts.emoji.trim();
30
12
  if (!prefix) {
31
13
  throw new CliError({
@@ -42,15 +24,25 @@ function deriveCommitMessageFromComment(opts) {
42
24
  message: invalidValueMessage("task id", opts.taskId, "valid task id"),
43
25
  });
44
26
  }
45
- return `${prefix} ${suffix} task: ${summary}`;
27
+ const primary = opts.primaryTag.trim().toLowerCase();
28
+ if (!primary) {
29
+ throw new CliError({
30
+ exitCode: 2,
31
+ code: "E_USAGE",
32
+ message: "Primary tag is required when deriving commit messages from task comments",
33
+ });
34
+ }
35
+ const status = (opts.statusTo?.trim().toLowerCase() ?? "status-transition").replaceAll(/\s+/g, "-");
36
+ return `${prefix} ${suffix} ${primary}: ${status}`;
46
37
  }
47
38
  function deriveCommitBodyFromComment(opts) {
48
39
  const lines = [
49
40
  `Task: ${opts.taskId}`,
41
+ `Primary: ${opts.primaryTag}`,
50
42
  ...(opts.executorAgent ? [`Agent: ${opts.executorAgent}`] : []),
51
43
  ...(opts.author ? [`Author: ${opts.author}`] : []),
52
- ...(opts.statusFrom && opts.statusTo ? [`Status: ${opts.statusFrom} -> ${opts.statusTo}`] : []),
53
- `Comment: ${normalizeCommentBodyForCommit(opts.formattedComment || opts.commentBody)}`,
44
+ ...(opts.statusTo ? [`Status: ${opts.statusTo}`] : []),
45
+ `Comment: ${normalizeCommentBodyForCommit(opts.formattedComment ?? opts.commentBody)}`,
54
46
  ];
55
47
  return lines.join("\n").trimEnd();
56
48
  }
@@ -88,14 +80,14 @@ export async function commitFromComment(opts) {
88
80
  });
89
81
  const message = deriveCommitMessageFromComment({
90
82
  taskId: opts.taskId,
91
- body: opts.commentBody,
83
+ primaryTag: opts.primaryTag,
84
+ statusTo: opts.statusTo,
92
85
  emoji: opts.emoji,
93
- formattedComment: opts.formattedComment,
94
- config: opts.config,
95
86
  });
96
87
  const formattedComment = opts.formattedComment ?? formatCommentBodyForCommit(opts.commentBody, opts.config);
97
88
  const body = deriveCommitBodyFromComment({
98
89
  taskId: opts.taskId,
90
+ primaryTag: opts.primaryTag,
99
91
  executorAgent: opts.executorAgent,
100
92
  author: opts.author,
101
93
  statusFrom: opts.statusFrom,
@@ -1 +1 @@
1
- {"version":3,"file":"apply.command.d.ts","sourceRoot":"","sources":["../../../src/commands/release/apply.command.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAU1E,MAAM,MAAM,iBAAiB,GAAG;IAC9B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,OAAO,CAAC;IACb,IAAI,EAAE,OAAO,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG,iBAAiB,CAAC;AAuSnD,eAAO,MAAM,gBAAgB,EAAE,WAAW,CAAC,kBAAkB,CAwE5D,CAAC;AAEF,eAAO,MAAM,eAAe,EAAE,cAAc,CAAC,kBAAkB,CA0J9D,CAAC"}
1
+ {"version":3,"file":"apply.command.d.ts","sourceRoot":"","sources":["../../../src/commands/release/apply.command.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAU1E,MAAM,MAAM,iBAAiB,GAAG;IAC9B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,OAAO,CAAC;IACb,IAAI,EAAE,OAAO,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG,iBAAiB,CAAC;AAsVnD,eAAO,MAAM,gBAAgB,EAAE,WAAW,CAAC,kBAAkB,CAwE5D,CAAC;AAEF,eAAO,MAAM,eAAe,EAAE,cAAc,CAAC,kBAAkB,CAuK9D,CAAC"}
@@ -82,6 +82,20 @@ async function readPackageVersion(pkgJsonPath) {
82
82
  }
83
83
  return version;
84
84
  }
85
+ async function readCoreDependencyVersion(pkgJsonPath) {
86
+ const raw = JSON.parse(await readFile(pkgJsonPath, "utf8"));
87
+ const value = raw.dependencies?.["@agentplaneorg/core"];
88
+ const version = typeof value === "string" ? value.trim() : "";
89
+ if (!version) {
90
+ throw new CliError({
91
+ exitCode: exitCodeForError("E_VALIDATION"),
92
+ code: "E_VALIDATION",
93
+ message: `Missing dependency @agentplaneorg/core in ${pkgJsonPath}. ` +
94
+ "Release parity requires packages/agentplane to pin @agentplaneorg/core to the same version.",
95
+ });
96
+ }
97
+ return version;
98
+ }
85
99
  async function replacePackageVersionInFile(pkgJsonPath, nextVersion) {
86
100
  const text = await readFile(pkgJsonPath, "utf8");
87
101
  const replaced = text.replace(/"version"\s*:\s*"[^"]*"/u, `"version": "${nextVersion}"`);
@@ -94,6 +108,27 @@ async function replacePackageVersionInFile(pkgJsonPath, nextVersion) {
94
108
  }
95
109
  await writeFile(pkgJsonPath, replaced, "utf8");
96
110
  }
111
+ async function replaceAgentplanePackageMetadata(pkgJsonPath, nextVersion) {
112
+ const text = await readFile(pkgJsonPath, "utf8");
113
+ const withVersion = text.replace(/"version"\s*:\s*"[^"]*"/u, `"version": "${nextVersion}"`);
114
+ if (withVersion === text) {
115
+ throw new CliError({
116
+ exitCode: exitCodeForError("E_VALIDATION"),
117
+ code: "E_VALIDATION",
118
+ message: `Failed to update version in ${pkgJsonPath} (missing "version" field).`,
119
+ });
120
+ }
121
+ const withDependency = withVersion.replace(/("@agentplaneorg\/core"\s*:\s*")[^"]*(")/u, `$1${nextVersion}$2`);
122
+ if (withDependency === withVersion) {
123
+ throw new CliError({
124
+ exitCode: exitCodeForError("E_VALIDATION"),
125
+ code: "E_VALIDATION",
126
+ message: `Failed to update @agentplaneorg/core dependency in ${pkgJsonPath}. ` +
127
+ "Ensure packages/agentplane/package.json declares this dependency.",
128
+ });
129
+ }
130
+ await writeFile(pkgJsonPath, withDependency, "utf8");
131
+ }
97
132
  function cleanHookEnv() {
98
133
  const env = { ...gitEnv() };
99
134
  delete env.AGENTPLANE_TASK_ID;
@@ -359,9 +394,10 @@ export const runReleaseApply = async (ctx, flags) => {
359
394
  await validateReleaseNotes(notesPath);
360
395
  const corePkgPath = path.join(gitRoot, "packages", "core", "package.json");
361
396
  const agentplanePkgPath = path.join(gitRoot, "packages", "agentplane", "package.json");
362
- const [coreVersion, agentplaneVersion] = await Promise.all([
397
+ const [coreVersion, agentplaneVersion, coreDependencyVersion] = await Promise.all([
363
398
  readPackageVersion(corePkgPath),
364
399
  readPackageVersion(agentplanePkgPath),
400
+ readCoreDependencyVersion(agentplanePkgPath),
365
401
  ]);
366
402
  if (coreVersion !== agentplaneVersion) {
367
403
  throw new CliError({
@@ -371,6 +407,15 @@ export const runReleaseApply = async (ctx, flags) => {
371
407
  `packages/core=${coreVersion} packages/agentplane=${agentplaneVersion}`,
372
408
  });
373
409
  }
410
+ if (coreDependencyVersion !== coreVersion) {
411
+ throw new CliError({
412
+ exitCode: exitCodeForError("E_VALIDATION"),
413
+ code: "E_VALIDATION",
414
+ message: "Release dependency parity check failed before apply. " +
415
+ `packages/agentplane dependency @agentplaneorg/core=${coreDependencyVersion} ` +
416
+ `must match packages/core version ${coreVersion}.`,
417
+ });
418
+ }
374
419
  const git = new GitContext({ gitRoot });
375
420
  await ensureCleanTrackedTree(gitRoot);
376
421
  await ensureTagDoesNotExist(gitRoot, plan.nextTag);
@@ -391,10 +436,13 @@ export const runReleaseApply = async (ctx, flags) => {
391
436
  if (coreVersion === plan.prevVersion) {
392
437
  await Promise.all([
393
438
  replacePackageVersionInFile(corePkgPath, plan.nextVersion),
394
- replacePackageVersionInFile(agentplanePkgPath, plan.nextVersion),
439
+ replaceAgentplanePackageMetadata(agentplanePkgPath, plan.nextVersion),
395
440
  ]);
396
441
  }
397
- else if (coreVersion !== plan.nextVersion) {
442
+ else if (coreVersion === plan.nextVersion) {
443
+ await replaceAgentplanePackageMetadata(agentplanePkgPath, plan.nextVersion);
444
+ }
445
+ else {
398
446
  throw new CliError({
399
447
  exitCode: exitCodeForError("E_VALIDATION"),
400
448
  code: "E_VALIDATION",
@@ -1 +1 @@
1
- {"version":3,"file":"plan.command.d.ts","sourceRoot":"","sources":["../../../src/commands/release/plan.command.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAM1E,KAAK,QAAQ,GAAG,OAAO,GAAG,OAAO,GAAG,OAAO,CAAC;AAE5C,MAAM,MAAM,gBAAgB,GAAG;IAC7B,IAAI,EAAE,QAAQ,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,OAAO,CAAC;CACd,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG,gBAAgB,CAAC;AAmHjD,eAAO,MAAM,eAAe,EAAE,WAAW,CAAC,iBAAiB,CA4E1D,CAAC;AAEF,eAAO,MAAM,cAAc,EAAE,cAAc,CAAC,iBAAiB,CAyD5D,CAAC"}
1
+ {"version":3,"file":"plan.command.d.ts","sourceRoot":"","sources":["../../../src/commands/release/plan.command.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAM1E,KAAK,QAAQ,GAAG,OAAO,GAAG,OAAO,GAAG,OAAO,CAAC;AAE5C,MAAM,MAAM,gBAAgB,GAAG;IAC7B,IAAI,EAAE,QAAQ,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,OAAO,CAAC;CACd,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG,gBAAgB,CAAC;AAqIjD,eAAO,MAAM,eAAe,EAAE,WAAW,CAAC,iBAAiB,CA4E1D,CAAC;AAEF,eAAO,MAAM,cAAc,EAAE,cAAc,CAAC,iBAAiB,CAoE5D,CAAC"}
@@ -43,6 +43,20 @@ async function readPackageVersion(pkgJsonPath) {
43
43
  }
44
44
  return version;
45
45
  }
46
+ async function readCoreDependencyVersion(pkgJsonPath) {
47
+ const raw = JSON.parse(await readFile(pkgJsonPath, "utf8"));
48
+ const value = raw.dependencies?.["@agentplaneorg/core"];
49
+ const version = typeof value === "string" ? value.trim() : "";
50
+ if (!version) {
51
+ throw new CliError({
52
+ exitCode: exitCodeForError("E_VALIDATION"),
53
+ code: "E_VALIDATION",
54
+ message: `Missing dependency @agentplaneorg/core in ${pkgJsonPath}. ` +
55
+ "Release planning requires packages/agentplane to pin @agentplaneorg/core to the same version.",
56
+ });
57
+ }
58
+ return version;
59
+ }
46
60
  async function getLatestSemverTag(gitRoot) {
47
61
  try {
48
62
  const { stdout } = await execFileAsync("git", ["describe", "--tags", "--abbrev=0", "--match", "v[0-9]*.[0-9]*.[0-9]*"], { cwd: gitRoot, env: gitEnv() });
@@ -176,9 +190,10 @@ export const runReleasePlan = async (ctx, flags) => {
176
190
  const gitRoot = resolved.gitRoot;
177
191
  const corePkgPath = path.join(gitRoot, "packages", "core", "package.json");
178
192
  const agentplanePkgPath = path.join(gitRoot, "packages", "agentplane", "package.json");
179
- const [coreVersion, agentplaneVersion] = await Promise.all([
193
+ const [coreVersion, agentplaneVersion, coreDependencyVersion] = await Promise.all([
180
194
  readPackageVersion(corePkgPath),
181
195
  readPackageVersion(agentplanePkgPath),
196
+ readCoreDependencyVersion(agentplanePkgPath),
182
197
  ]);
183
198
  if (coreVersion !== agentplaneVersion) {
184
199
  throw new CliError({
@@ -188,6 +203,15 @@ export const runReleasePlan = async (ctx, flags) => {
188
203
  `packages/core=${coreVersion} packages/agentplane=${agentplaneVersion}`,
189
204
  });
190
205
  }
206
+ if (coreDependencyVersion !== coreVersion) {
207
+ throw new CliError({
208
+ exitCode: exitCodeForError("E_VALIDATION"),
209
+ code: "E_VALIDATION",
210
+ message: "Release dependency parity check failed before planning. " +
211
+ `packages/agentplane dependency @agentplaneorg/core=${coreDependencyVersion} ` +
212
+ `must match packages/core version ${coreVersion}.`,
213
+ });
214
+ }
191
215
  const prevTag = flags.since ?? (await getLatestSemverTag(gitRoot));
192
216
  const nextVersion = bumpVersion(coreVersion, flags.bump);
193
217
  const nextTag = `v${nextVersion}`;
@@ -1,3 +1,4 @@
1
+ import type { AgentplaneConfig, ResolvedProject } from "@agentplaneorg/core";
1
2
  import { loadTaskBackend, type TaskData } from "../../backends/task-backend.js";
2
3
  import { GitContext } from "./git-context.js";
3
4
  export type CommandMemo = {
@@ -22,6 +23,8 @@ export declare function taskDataToFrontmatter(task: TaskData): Record<string, un
22
23
  export declare function loadCommandContext(opts: {
23
24
  cwd: string;
24
25
  rootOverride?: string | null;
26
+ resolvedProject?: ResolvedProject;
27
+ config?: AgentplaneConfig;
25
28
  }): Promise<CommandContext>;
26
29
  export declare function loadTaskFromContext(opts: {
27
30
  ctx: CommandContext;
@@ -1 +1 @@
1
- {"version":3,"file":"task-backend.d.ts","sourceRoot":"","sources":["../../../src/commands/shared/task-backend.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,eAAe,EAAE,KAAK,QAAQ,EAAE,MAAM,gCAAgC,CAAC;AAChF,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAE9C,MAAM,MAAM,WAAW,GAAG;IACxB,QAAQ,CAAC,EAAE,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC;IAC/B,YAAY,CAAC,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IACjC,UAAU,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;IAC7B,QAAQ,CAAC,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;CAC9B,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG;IAC3B,eAAe,EAAE,OAAO,CAAC,UAAU,CAAC,OAAO,eAAe,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC;IACzE,MAAM,EAAE,OAAO,CAAC,UAAU,CAAC,OAAO,eAAe,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;IAC9D,WAAW,EAAE,OAAO,CAAC,UAAU,CAAC,OAAO,eAAe,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;IACpE,SAAS,EAAE,MAAM,CAAC;IAClB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,GAAG,EAAE,UAAU,CAAC;IAEhB,IAAI,EAAE,WAAW,CAAC;IAGlB,QAAQ,EAAE,cAAc,CAAC,iBAAiB,CAAC,CAAC;IAC5C,OAAO,EAAE,cAAc,CAAC,aAAa,CAAC,CAAC;CACxC,CAAC;AASF,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAQ3E;AAED,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,QAAQ,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CA+B7E;AAED,wBAAsB,kBAAkB,CAAC,IAAI,EAAE;IAC7C,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B,GAAG,OAAO,CAAC,cAAc,CAAC,CAgB1B;AAED,wBAAsB,mBAAmB,CAAC,IAAI,EAAE;IAC9C,GAAG,EAAE,cAAc,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;CAChB,GAAG,OAAO,CAAC,QAAQ,CAAC,CAepB;AAED,wBAAsB,eAAe,CAAC,IAAI,EAAE;IAC1C,GAAG,CAAC,EAAE,cAAc,CAAC;IACrB,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,MAAM,EAAE,MAAM,CAAC;CAChB,GAAG,OAAO,CAAC;IACV,OAAO,EAAE,cAAc,CAAC,aAAa,CAAC,CAAC;IACvC,SAAS,EAAE,MAAM,CAAC;IAClB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,QAAQ,EAAE,cAAc,CAAC,iBAAiB,CAAC,CAAC;IAC5C,MAAM,EAAE,cAAc,CAAC,QAAQ,CAAC,CAAC;IACjC,IAAI,EAAE,QAAQ,CAAC;CAChB,CAAC,CAaD;AAED,wBAAsB,aAAa,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC,CAG5E"}
1
+ {"version":3,"file":"task-backend.d.ts","sourceRoot":"","sources":["../../../src/commands/shared/task-backend.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAG7E,OAAO,EAAE,eAAe,EAAE,KAAK,QAAQ,EAAE,MAAM,gCAAgC,CAAC;AAChF,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAE9C,MAAM,MAAM,WAAW,GAAG;IACxB,QAAQ,CAAC,EAAE,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC;IAC/B,YAAY,CAAC,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IACjC,UAAU,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;IAC7B,QAAQ,CAAC,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;CAC9B,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG;IAC3B,eAAe,EAAE,OAAO,CAAC,UAAU,CAAC,OAAO,eAAe,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC;IACzE,MAAM,EAAE,OAAO,CAAC,UAAU,CAAC,OAAO,eAAe,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;IAC9D,WAAW,EAAE,OAAO,CAAC,UAAU,CAAC,OAAO,eAAe,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;IACpE,SAAS,EAAE,MAAM,CAAC;IAClB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,GAAG,EAAE,UAAU,CAAC;IAEhB,IAAI,EAAE,WAAW,CAAC;IAGlB,QAAQ,EAAE,cAAc,CAAC,iBAAiB,CAAC,CAAC;IAC5C,OAAO,EAAE,cAAc,CAAC,aAAa,CAAC,CAAC;CACxC,CAAC;AASF,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAQ3E;AAED,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,QAAQ,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CA+B7E;AAED,wBAAsB,kBAAkB,CAAC,IAAI,EAAE;IAC7C,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,eAAe,CAAC,EAAE,eAAe,CAAC;IAClC,MAAM,CAAC,EAAE,gBAAgB,CAAC;CAC3B,GAAG,OAAO,CAAC,cAAc,CAAC,CAmB1B;AAED,wBAAsB,mBAAmB,CAAC,IAAI,EAAE;IAC9C,GAAG,EAAE,cAAc,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;CAChB,GAAG,OAAO,CAAC,QAAQ,CAAC,CAepB;AAED,wBAAsB,eAAe,CAAC,IAAI,EAAE;IAC1C,GAAG,CAAC,EAAE,cAAc,CAAC;IACrB,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,MAAM,EAAE,MAAM,CAAC;CAChB,GAAG,OAAO,CAAC;IACV,OAAO,EAAE,cAAc,CAAC,aAAa,CAAC,CAAC;IACvC,SAAS,EAAE,MAAM,CAAC;IAClB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,QAAQ,EAAE,cAAc,CAAC,iBAAiB,CAAC,CAAC;IAC5C,MAAM,EAAE,cAAc,CAAC,QAAQ,CAAC,CAAC;IACjC,IAAI,EAAE,QAAQ,CAAC;CAChB,CAAC,CAaD;AAED,wBAAsB,aAAa,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC,CAG5E"}
@@ -50,10 +50,13 @@ export function taskDataToFrontmatter(task) {
50
50
  };
51
51
  }
52
52
  export async function loadCommandContext(opts) {
53
- const { backend, backendId, backendConfigPath, resolved, config } = await loadTaskBackend({
53
+ const backendLoaded = await loadTaskBackend({
54
54
  cwd: opts.cwd,
55
55
  rootOverride: opts.rootOverride ?? null,
56
+ resolvedProject: opts.resolvedProject,
57
+ config: opts.config,
56
58
  });
59
+ const { backend, backendId, backendConfigPath, resolved, config } = backendLoaded;
57
60
  return {
58
61
  resolvedProject: resolved,
59
62
  config,
@@ -1 +1 @@
1
- {"version":3,"file":"block.d.ts","sourceRoot":"","sources":["../../../src/commands/task/block.ts"],"names":[],"mappings":"AAQA,OAAO,EAGL,KAAK,cAAc,EACpB,MAAM,2BAA2B,CAAC;AAcnC,wBAAsB,QAAQ,CAAC,IAAI,EAAE;IACnC,GAAG,CAAC,EAAE,cAAc,CAAC;IACrB,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,iBAAiB,EAAE,OAAO,CAAC;IAC3B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,eAAe,EAAE,OAAO,CAAC;IACzB,gBAAgB,EAAE,OAAO,CAAC;IAC1B,kBAAkB,EAAE,OAAO,CAAC;IAC5B,mBAAmB,EAAE,OAAO,CAAC;IAC7B,KAAK,EAAE,OAAO,CAAC;IACf,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,KAAK,EAAE,OAAO,CAAC;CAChB,GAAG,OAAO,CAAC,MAAM,CAAC,CAgIlB"}
1
+ {"version":3,"file":"block.d.ts","sourceRoot":"","sources":["../../../src/commands/task/block.ts"],"names":[],"mappings":"AAQA,OAAO,EAGL,KAAK,cAAc,EACpB,MAAM,2BAA2B,CAAC;AAgBnC,wBAAsB,QAAQ,CAAC,IAAI,EAAE;IACnC,GAAG,CAAC,EAAE,cAAc,CAAC;IACrB,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,iBAAiB,EAAE,OAAO,CAAC;IAC3B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,eAAe,EAAE,OAAO,CAAC;IACzB,gBAAgB,EAAE,OAAO,CAAC;IAC1B,kBAAkB,EAAE,OAAO,CAAC;IAC5B,mBAAmB,EAAE,OAAO,CAAC;IAC7B,KAAK,EAAE,OAAO,CAAC;IACf,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,KAAK,EAAE,OAAO,CAAC;CAChB,GAAG,OAAO,CAAC,MAAM,CAAC,CAmIlB"}
@@ -7,7 +7,7 @@ import { ensureActionApproved } from "../shared/approval-requirements.js";
7
7
  import { loadCommandContext, loadTaskFromContext, } from "../shared/task-backend.js";
8
8
  import { backendIsLocalFileBackend, getTaskStore } from "../shared/task-store.js";
9
9
  import { readDirectWorkLock } from "../../shared/direct-work-lock.js";
10
- import { appendTaskEvent, defaultCommitEmojiForAgentId, enforceStatusCommitPolicy, isTransitionAllowed, nowIso, requireStructuredComment, } from "./shared.js";
10
+ import { appendTaskEvent, defaultCommitEmojiForAgentId, enforceStatusCommitPolicy, isTransitionAllowed, nowIso, requireStructuredComment, resolvePrimaryTag, toStringArray, } from "./shared.js";
11
11
  export async function cmdBlock(opts) {
12
12
  try {
13
13
  const ctx = opts.ctx ??
@@ -20,14 +20,6 @@ export async function cmdBlock(opts) {
20
20
  reason: "block --force",
21
21
  });
22
22
  }
23
- if (opts.commitFromComment) {
24
- enforceStatusCommitPolicy({
25
- policy: ctx.config.status_commit_policy,
26
- action: "block",
27
- confirmed: opts.confirmStatusCommit,
28
- quiet: opts.quiet,
29
- });
30
- }
31
23
  const { prefix, min_chars: minChars } = ctx.config.tasks.comments.blocked;
32
24
  requireStructuredComment(opts.body, prefix, minChars);
33
25
  const useStore = backendIsLocalFileBackend(ctx);
@@ -43,6 +35,16 @@ export async function cmdBlock(opts) {
43
35
  message: `Refusing status transition ${currentStatus} -> BLOCKED (use --force to override)`,
44
36
  });
45
37
  }
38
+ if (opts.commitFromComment) {
39
+ enforceStatusCommitPolicy({
40
+ policy: ctx.config.status_commit_policy,
41
+ action: "block",
42
+ confirmed: opts.confirmStatusCommit,
43
+ quiet: opts.quiet,
44
+ statusFrom: currentStatus,
45
+ statusTo: "BLOCKED",
46
+ });
47
+ }
46
48
  const formattedComment = opts.commitFromComment
47
49
  ? formatCommentBodyForCommit(opts.body, ctx.config)
48
50
  : null;
@@ -94,6 +96,7 @@ export async function cmdBlock(opts) {
94
96
  cwd: opts.cwd,
95
97
  rootOverride: opts.rootOverride,
96
98
  taskId: opts.taskId,
99
+ primaryTag: resolvePrimaryTag(toStringArray(task.tags), ctx).primary,
97
100
  executorAgent,
98
101
  author: opts.author,
99
102
  statusFrom: currentStatus,
@@ -0,0 +1,14 @@
1
+ import type { CommandCtx, CommandSpec } from "../../cli/spec/spec.js";
2
+ import type { CommandContext } from "../shared/task-backend.js";
3
+ export type TaskCloseDuplicateParsed = {
4
+ taskId: string;
5
+ duplicateOf: string;
6
+ author: string;
7
+ note?: string;
8
+ force: boolean;
9
+ yes: boolean;
10
+ quiet: boolean;
11
+ };
12
+ export declare const taskCloseDuplicateSpec: CommandSpec<TaskCloseDuplicateParsed>;
13
+ export declare function makeRunTaskCloseDuplicateHandler(getCtx: (cmd: string) => Promise<CommandContext>): (ctx: CommandCtx, p: TaskCloseDuplicateParsed) => Promise<number>;
14
+ //# sourceMappingURL=close-duplicate.command.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"close-duplicate.command.d.ts","sourceRoot":"","sources":["../../../src/commands/task/close-duplicate.command.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAEtE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAGhE,MAAM,MAAM,wBAAwB,GAAG;IACrC,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,OAAO,CAAC;IACf,GAAG,EAAE,OAAO,CAAC;IACb,KAAK,EAAE,OAAO,CAAC;CAChB,CAAC;AAEF,eAAO,MAAM,sBAAsB,EAAE,WAAW,CAAC,wBAAwB,CAmFxE,CAAC;AAEF,wBAAgB,gCAAgC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,cAAc,CAAC,IACjF,KAAK,UAAU,EAAE,GAAG,wBAAwB,KAAG,OAAO,CAAC,MAAM,CAAC,CAc7E"}
@@ -0,0 +1,102 @@
1
+ import { usageError } from "../../cli/spec/errors.js";
2
+ import { cmdTaskCloseDuplicate } from "./close-duplicate.js";
3
+ export const taskCloseDuplicateSpec = {
4
+ id: ["task", "close-duplicate"],
5
+ group: "Task",
6
+ summary: "Close a task as a duplicate of another task with no-op bookkeeping metadata.",
7
+ args: [{ name: "task-id", required: true, valueHint: "<task-id>" }],
8
+ options: [
9
+ {
10
+ kind: "string",
11
+ name: "of",
12
+ valueHint: "<task-id>",
13
+ required: true,
14
+ description: "Canonical task id that this task duplicates.",
15
+ },
16
+ {
17
+ kind: "string",
18
+ name: "author",
19
+ valueHint: "<id>",
20
+ required: true,
21
+ description: "Comment author id (e.g. ORCHESTRATOR).",
22
+ },
23
+ {
24
+ kind: "string",
25
+ name: "note",
26
+ valueHint: "<text>",
27
+ description: "Optional short reason appended to the closure comment.",
28
+ },
29
+ { kind: "boolean", name: "force", default: false, description: "Force closure despite gates." },
30
+ {
31
+ kind: "boolean",
32
+ name: "yes",
33
+ default: false,
34
+ description: "Auto-approve force-action approval checks when required.",
35
+ },
36
+ { kind: "boolean", name: "quiet", default: false, description: "Suppress output." },
37
+ ],
38
+ examples: [
39
+ {
40
+ cmd: "agentplane task close-duplicate 202602120845-VKGC27 --of 202602120845-RWJ8K3 --author ORCHESTRATOR",
41
+ why: "Close accidental duplicate task in one command.",
42
+ },
43
+ ],
44
+ validateRaw: (raw) => {
45
+ const taskId = typeof raw.args["task-id"] === "string" ? raw.args["task-id"].trim() : "";
46
+ const of = typeof raw.opts.of === "string" ? raw.opts.of.trim() : "";
47
+ const author = typeof raw.opts.author === "string" ? raw.opts.author.trim() : "";
48
+ const note = raw.opts.note;
49
+ if (!taskId) {
50
+ throw usageError({
51
+ spec: taskCloseDuplicateSpec,
52
+ message: "Invalid value for task-id: empty.",
53
+ });
54
+ }
55
+ if (!of) {
56
+ throw usageError({ spec: taskCloseDuplicateSpec, message: "Invalid value for --of: empty." });
57
+ }
58
+ if (!author) {
59
+ throw usageError({
60
+ spec: taskCloseDuplicateSpec,
61
+ message: "Invalid value for --author: empty.",
62
+ });
63
+ }
64
+ if (typeof note === "string" && note.trim().length === 0) {
65
+ throw usageError({
66
+ spec: taskCloseDuplicateSpec,
67
+ message: "Invalid value for --note: empty.",
68
+ });
69
+ }
70
+ if (taskId === of) {
71
+ throw usageError({
72
+ spec: taskCloseDuplicateSpec,
73
+ message: "Duplicate target must differ from task-id.",
74
+ });
75
+ }
76
+ },
77
+ parse: (raw) => ({
78
+ taskId: String(raw.args["task-id"]),
79
+ duplicateOf: String(raw.opts.of),
80
+ author: String(raw.opts.author),
81
+ note: typeof raw.opts.note === "string" ? raw.opts.note : undefined,
82
+ force: raw.opts.force === true,
83
+ yes: raw.opts.yes === true,
84
+ quiet: raw.opts.quiet === true,
85
+ }),
86
+ };
87
+ export function makeRunTaskCloseDuplicateHandler(getCtx) {
88
+ return async (ctx, p) => {
89
+ return await cmdTaskCloseDuplicate({
90
+ ctx: await getCtx("task close-duplicate"),
91
+ cwd: ctx.cwd,
92
+ rootOverride: ctx.rootOverride,
93
+ taskId: p.taskId,
94
+ duplicateOf: p.duplicateOf,
95
+ author: p.author,
96
+ note: p.note,
97
+ force: p.force,
98
+ yes: p.yes,
99
+ quiet: p.quiet,
100
+ });
101
+ };
102
+ }
@@ -0,0 +1,14 @@
1
+ import { type CommandContext } from "../shared/task-backend.js";
2
+ export declare function cmdTaskCloseDuplicate(opts: {
3
+ ctx: CommandContext;
4
+ cwd: string;
5
+ rootOverride?: string;
6
+ taskId: string;
7
+ duplicateOf: string;
8
+ author: string;
9
+ note?: string;
10
+ force: boolean;
11
+ yes: boolean;
12
+ quiet: boolean;
13
+ }): Promise<number>;
14
+ //# sourceMappingURL=close-duplicate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"close-duplicate.d.ts","sourceRoot":"","sources":["../../../src/commands/task/close-duplicate.ts"],"names":[],"mappings":"AAIA,OAAO,EAAuB,KAAK,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAIrF,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,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,OAAO,CAAC;IACf,GAAG,EAAE,OAAO,CAAC;IACb,KAAK,EAAE,OAAO,CAAC;CAChB,GAAG,OAAO,CAAC,MAAM,CAAC,CAuFlB"}
@@ -0,0 +1,90 @@
1
+ import { mapBackendError } from "../../cli/error-map.js";
2
+ import { CliError } from "../../shared/errors.js";
3
+ import { ensureActionApproved } from "../shared/approval-requirements.js";
4
+ import { loadTaskFromContext } from "../shared/task-backend.js";
5
+ import { backendIsLocalFileBackend, getTaskStore } from "../shared/task-store.js";
6
+ import { appendTaskEvent, nowIso, requireStructuredComment } from "./shared.js";
7
+ export async function cmdTaskCloseDuplicate(opts) {
8
+ try {
9
+ const sourceId = opts.taskId.trim();
10
+ const duplicateOf = opts.duplicateOf.trim();
11
+ if (!sourceId || !duplicateOf) {
12
+ throw new CliError({
13
+ exitCode: 2,
14
+ code: "E_USAGE",
15
+ message: "Both task id and --of must be non-empty.",
16
+ });
17
+ }
18
+ if (sourceId === duplicateOf) {
19
+ throw new CliError({
20
+ exitCode: 2,
21
+ code: "E_USAGE",
22
+ message: `Duplicate target must differ from task id (${sourceId}).`,
23
+ });
24
+ }
25
+ if (opts.force) {
26
+ await ensureActionApproved({
27
+ action: "force_action",
28
+ config: opts.ctx.config,
29
+ yes: opts.yes,
30
+ reason: "task close-duplicate --force",
31
+ });
32
+ }
33
+ const canonical = await loadTaskFromContext({ ctx: opts.ctx, taskId: duplicateOf });
34
+ const useStore = backendIsLocalFileBackend(opts.ctx);
35
+ const store = useStore ? getTaskStore(opts.ctx) : null;
36
+ const task = useStore
37
+ ? await store.get(sourceId)
38
+ : await loadTaskFromContext({ ctx: opts.ctx, taskId: sourceId });
39
+ if (!opts.force && String(task.status || "TODO").toUpperCase() === "DONE") {
40
+ throw new CliError({
41
+ exitCode: 2,
42
+ code: "E_USAGE",
43
+ message: `Task is already DONE: ${sourceId} (use --force to override)`,
44
+ });
45
+ }
46
+ const reason = opts.note?.trim();
47
+ const canonicalTitle = canonical.title?.trim() ? ` (${canonical.title.trim()})` : "";
48
+ const baseBody = `Verified: ${sourceId} is a bookkeeping duplicate of ${duplicateOf}${canonicalTitle}; ` +
49
+ "no code/config changes are expected in this task and closure is recorded as no-op.";
50
+ const body = reason ? `${baseBody}\n\nReason: ${reason}` : baseBody;
51
+ const verifiedCfg = opts.ctx.config.tasks.comments.verified;
52
+ requireStructuredComment(body, verifiedCfg.prefix, verifiedCfg.min_chars);
53
+ const at = nowIso();
54
+ const next = {
55
+ ...task,
56
+ status: "DONE",
57
+ comments: [
58
+ ...(Array.isArray(task.comments) ? task.comments : []),
59
+ { author: opts.author, body },
60
+ ],
61
+ events: appendTaskEvent(task, {
62
+ type: "status",
63
+ at,
64
+ author: opts.author,
65
+ from: String(task.status || "TODO").toUpperCase(),
66
+ to: "DONE",
67
+ note: body,
68
+ }),
69
+ result_summary: `Closed as duplicate of ${duplicateOf}.`,
70
+ risk_level: "low",
71
+ breaking: false,
72
+ doc_version: 2,
73
+ doc_updated_at: at,
74
+ doc_updated_by: opts.author,
75
+ };
76
+ await (useStore ? store.update(sourceId, () => next) : opts.ctx.taskBackend.writeTask(next));
77
+ if (!opts.quiet) {
78
+ process.stdout.write(`task.done: ${sourceId} (duplicate of ${duplicateOf})\n`);
79
+ }
80
+ return 0;
81
+ }
82
+ catch (err) {
83
+ if (err instanceof CliError)
84
+ throw err;
85
+ throw mapBackendError(err, {
86
+ command: "task close-duplicate",
87
+ root: opts.rootOverride ?? null,
88
+ });
89
+ }
90
+ }