agentplane 0.2.24 → 0.2.26

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 (187) hide show
  1. package/bin/agentplane.js +91 -54
  2. package/dist/.build-manifest.json +11 -0
  3. package/dist/backends/task-backend/local-backend.d.ts +2 -0
  4. package/dist/backends/task-backend/local-backend.d.ts.map +1 -1
  5. package/dist/backends/task-backend/local-backend.js +12 -1
  6. package/dist/backends/task-backend/redmine/mapping.d.ts.map +1 -1
  7. package/dist/backends/task-backend/redmine/mapping.js +26 -1
  8. package/dist/backends/task-backend/redmine-backend.d.ts +4 -0
  9. package/dist/backends/task-backend/redmine-backend.d.ts.map +1 -1
  10. package/dist/backends/task-backend/redmine-backend.js +92 -9
  11. package/dist/backends/task-backend/shared/types.d.ts +1 -0
  12. package/dist/backends/task-backend/shared/types.d.ts.map +1 -1
  13. package/dist/backends/task-index.d.ts.map +1 -1
  14. package/dist/backends/task-index.js +8 -1
  15. package/dist/cli/command-guide.d.ts.map +1 -1
  16. package/dist/cli/command-guide.js +21 -8
  17. package/dist/cli/command-snippets.d.ts +24 -0
  18. package/dist/cli/command-snippets.d.ts.map +1 -0
  19. package/dist/cli/command-snippets.js +23 -0
  20. package/dist/cli/reason-codes.d.ts +9 -0
  21. package/dist/cli/reason-codes.d.ts.map +1 -0
  22. package/dist/cli/reason-codes.js +79 -0
  23. package/dist/cli/recipes-bundled.d.ts +1 -0
  24. package/dist/cli/recipes-bundled.d.ts.map +1 -1
  25. package/dist/cli/recipes-bundled.js +4 -1
  26. package/dist/cli/run-cli/command-catalog.d.ts +1 -1
  27. package/dist/cli/run-cli/command-catalog.d.ts.map +1 -1
  28. package/dist/cli/run-cli/command-catalog.js +40 -1
  29. package/dist/cli/run-cli/commands/config.d.ts +5 -0
  30. package/dist/cli/run-cli/commands/config.d.ts.map +1 -1
  31. package/dist/cli/run-cli/commands/config.js +86 -1
  32. package/dist/cli/run-cli/commands/core.d.ts.map +1 -1
  33. package/dist/cli/run-cli/commands/core.js +55 -0
  34. package/dist/cli/run-cli/commands/init/recipes.d.ts +5 -1
  35. package/dist/cli/run-cli/commands/init/recipes.d.ts.map +1 -1
  36. package/dist/cli/run-cli/commands/init/recipes.js +24 -4
  37. package/dist/cli/run-cli/commands/init/write-workflow.d.ts +7 -0
  38. package/dist/cli/run-cli/commands/init/write-workflow.d.ts.map +1 -0
  39. package/dist/cli/run-cli/commands/init/write-workflow.js +52 -0
  40. package/dist/cli/run-cli/commands/init.d.ts +2 -1
  41. package/dist/cli/run-cli/commands/init.d.ts.map +1 -1
  42. package/dist/cli/run-cli/commands/init.js +104 -54
  43. package/dist/cli/run-cli.d.ts.map +1 -1
  44. package/dist/cli/run-cli.js +70 -1
  45. package/dist/commands/backend/sync.command.d.ts.map +1 -1
  46. package/dist/commands/backend/sync.command.js +7 -6
  47. package/dist/commands/backend.d.ts.map +1 -1
  48. package/dist/commands/backend.js +2 -0
  49. package/dist/commands/block.spec.d.ts.map +1 -1
  50. package/dist/commands/block.spec.js +23 -2
  51. package/dist/commands/commit.spec.d.ts.map +1 -1
  52. package/dist/commands/commit.spec.js +18 -6
  53. package/dist/commands/doctor.run.d.ts.map +1 -1
  54. package/dist/commands/doctor.run.js +96 -10
  55. package/dist/commands/finish.spec.d.ts.map +1 -1
  56. package/dist/commands/finish.spec.js +53 -4
  57. package/dist/commands/guard/commit.command.d.ts.map +1 -1
  58. package/dist/commands/guard/commit.command.js +26 -20
  59. package/dist/commands/guard/impl/allow.d.ts.map +1 -1
  60. package/dist/commands/guard/impl/allow.js +8 -1
  61. package/dist/commands/guard/impl/commands.d.ts.map +1 -1
  62. package/dist/commands/guard/impl/commands.js +19 -21
  63. package/dist/commands/guard/impl/comment-commit.d.ts.map +1 -1
  64. package/dist/commands/guard/impl/comment-commit.js +8 -17
  65. package/dist/commands/recipes/impl/commands/install.d.ts.map +1 -1
  66. package/dist/commands/recipes/impl/commands/install.js +36 -13
  67. package/dist/commands/recipes/impl/scenario.d.ts.map +1 -1
  68. package/dist/commands/recipes/impl/scenario.js +25 -0
  69. package/dist/commands/recipes/impl/types.d.ts +4 -0
  70. package/dist/commands/recipes/impl/types.d.ts.map +1 -1
  71. package/dist/commands/scenario/impl/commands.d.ts.map +1 -1
  72. package/dist/commands/scenario/impl/commands.js +74 -3
  73. package/dist/commands/scenario/impl/report.d.ts +8 -0
  74. package/dist/commands/scenario/impl/report.d.ts.map +1 -1
  75. package/dist/commands/scenario/impl/report.js +1 -0
  76. package/dist/commands/shared/reconcile-check.d.ts +7 -0
  77. package/dist/commands/shared/reconcile-check.d.ts.map +1 -0
  78. package/dist/commands/shared/reconcile-check.js +60 -0
  79. package/dist/commands/start.spec.d.ts.map +1 -1
  80. package/dist/commands/start.spec.js +23 -2
  81. package/dist/commands/sync.command.d.ts.map +1 -1
  82. package/dist/commands/sync.command.js +9 -2
  83. package/dist/commands/task/finish.d.ts.map +1 -1
  84. package/dist/commands/task/finish.js +34 -10
  85. package/dist/commands/task/list.d.ts.map +1 -1
  86. package/dist/commands/task/list.js +2 -1
  87. package/dist/commands/task/list.spec.d.ts.map +1 -1
  88. package/dist/commands/task/list.spec.js +7 -0
  89. package/dist/commands/task/next.d.ts.map +1 -1
  90. package/dist/commands/task/next.js +2 -1
  91. package/dist/commands/task/next.spec.d.ts.map +1 -1
  92. package/dist/commands/task/next.spec.js +7 -0
  93. package/dist/commands/task/search.d.ts.map +1 -1
  94. package/dist/commands/task/search.js +2 -1
  95. package/dist/commands/task/search.spec.d.ts.map +1 -1
  96. package/dist/commands/task/search.spec.js +7 -0
  97. package/dist/commands/task/set-status.command.d.ts.map +1 -1
  98. package/dist/commands/task/set-status.command.js +22 -2
  99. package/dist/commands/task/shared.d.ts +7 -0
  100. package/dist/commands/task/shared.d.ts.map +1 -1
  101. package/dist/commands/task/shared.js +21 -1
  102. package/dist/commands/task/verify-record.d.ts.map +1 -1
  103. package/dist/commands/task/verify-record.js +2 -0
  104. package/dist/commands/workflow-build.command.d.ts +8 -0
  105. package/dist/commands/workflow-build.command.d.ts.map +1 -0
  106. package/dist/commands/workflow-build.command.js +96 -0
  107. package/dist/commands/workflow-playbook.command.d.ts +10 -0
  108. package/dist/commands/workflow-playbook.command.d.ts.map +1 -0
  109. package/dist/commands/workflow-playbook.command.js +174 -0
  110. package/dist/commands/workflow-restore.command.d.ts +5 -0
  111. package/dist/commands/workflow-restore.command.d.ts.map +1 -0
  112. package/dist/commands/workflow-restore.command.js +30 -0
  113. package/dist/commands/workflow.command.d.ts +6 -0
  114. package/dist/commands/workflow.command.d.ts.map +1 -0
  115. package/dist/commands/workflow.command.js +36 -0
  116. package/dist/harness/dynamic-tool-contract.d.ts +29 -0
  117. package/dist/harness/dynamic-tool-contract.d.ts.map +1 -0
  118. package/dist/harness/dynamic-tool-contract.js +86 -0
  119. package/dist/harness/hooks-lifecycle.d.ts +27 -0
  120. package/dist/harness/hooks-lifecycle.d.ts.map +1 -0
  121. package/dist/harness/hooks-lifecycle.js +67 -0
  122. package/dist/harness/index.d.ts +9 -0
  123. package/dist/harness/index.d.ts.map +1 -0
  124. package/dist/harness/index.js +8 -0
  125. package/dist/harness/reconcile.d.ts +37 -0
  126. package/dist/harness/reconcile.d.ts.map +1 -0
  127. package/dist/harness/reconcile.js +42 -0
  128. package/dist/harness/retry-policy.d.ts +31 -0
  129. package/dist/harness/retry-policy.d.ts.map +1 -0
  130. package/dist/harness/retry-policy.js +33 -0
  131. package/dist/harness/scheduler.d.ts +18 -0
  132. package/dist/harness/scheduler.d.ts.map +1 -0
  133. package/dist/harness/scheduler.js +55 -0
  134. package/dist/harness/state-machine.d.ts +17 -0
  135. package/dist/harness/state-machine.d.ts.map +1 -0
  136. package/dist/harness/state-machine.js +70 -0
  137. package/dist/harness/token-accounting.d.ts +19 -0
  138. package/dist/harness/token-accounting.d.ts.map +1 -0
  139. package/dist/harness/token-accounting.js +77 -0
  140. package/dist/harness/workspace-safety.d.ts +14 -0
  141. package/dist/harness/workspace-safety.d.ts.map +1 -0
  142. package/dist/harness/workspace-safety.js +62 -0
  143. package/dist/policy/rules/allowlist.d.ts.map +1 -1
  144. package/dist/policy/rules/allowlist.js +9 -0
  145. package/dist/recipes/bundled-recipes.d.ts +4 -0
  146. package/dist/recipes/bundled-recipes.d.ts.map +1 -1
  147. package/dist/recipes/bundled-recipes.js +11 -0
  148. package/dist/shared/allow-prefix-policy.d.ts +3 -0
  149. package/dist/shared/allow-prefix-policy.d.ts.map +1 -0
  150. package/dist/shared/allow-prefix-policy.js +8 -0
  151. package/dist/shared/errors.d.ts +6 -0
  152. package/dist/shared/errors.d.ts.map +1 -1
  153. package/dist/shared/errors.js +1 -0
  154. package/dist/workflow-runtime/build.d.ts +4 -0
  155. package/dist/workflow-runtime/build.d.ts.map +1 -0
  156. package/dist/workflow-runtime/build.js +114 -0
  157. package/dist/workflow-runtime/enforcement.d.ts +3 -0
  158. package/dist/workflow-runtime/enforcement.d.ts.map +1 -0
  159. package/dist/workflow-runtime/enforcement.js +10 -0
  160. package/dist/workflow-runtime/file-ops.d.ts +11 -0
  161. package/dist/workflow-runtime/file-ops.d.ts.map +1 -0
  162. package/dist/workflow-runtime/file-ops.js +248 -0
  163. package/dist/workflow-runtime/fix.d.ts +9 -0
  164. package/dist/workflow-runtime/fix.d.ts.map +1 -0
  165. package/dist/workflow-runtime/fix.js +107 -0
  166. package/dist/workflow-runtime/index.d.ts +11 -0
  167. package/dist/workflow-runtime/index.d.ts.map +1 -0
  168. package/dist/workflow-runtime/index.js +10 -0
  169. package/dist/workflow-runtime/markdown.d.ts +10 -0
  170. package/dist/workflow-runtime/markdown.d.ts.map +1 -0
  171. package/dist/workflow-runtime/markdown.js +147 -0
  172. package/dist/workflow-runtime/observability.d.ts +12 -0
  173. package/dist/workflow-runtime/observability.d.ts.map +1 -0
  174. package/dist/workflow-runtime/observability.js +14 -0
  175. package/dist/workflow-runtime/paths.d.ts +3 -0
  176. package/dist/workflow-runtime/paths.d.ts.map +1 -0
  177. package/dist/workflow-runtime/paths.js +11 -0
  178. package/dist/workflow-runtime/template.d.ts +7 -0
  179. package/dist/workflow-runtime/template.d.ts.map +1 -0
  180. package/dist/workflow-runtime/template.js +94 -0
  181. package/dist/workflow-runtime/types.d.ts +68 -0
  182. package/dist/workflow-runtime/types.d.ts.map +1 -0
  183. package/dist/workflow-runtime/types.js +1 -0
  184. package/dist/workflow-runtime/validate.d.ts +8 -0
  185. package/dist/workflow-runtime/validate.d.ts.map +1 -0
  186. package/dist/workflow-runtime/validate.js +331 -0
  187. package/package.json +3 -3
@@ -1,4 +1,5 @@
1
1
  import { usageError } from "../cli/spec/errors.js";
2
+ import { findRepoWideAllowPrefixes, repoWideAllowPrefixMessage, } from "../shared/allow-prefix-policy.js";
2
3
  export const commitSpec = {
3
4
  id: ["commit"],
4
5
  group: "Guard",
@@ -35,13 +36,14 @@ export const commitSpec = {
35
36
  name: "allow",
36
37
  valueHint: "<path-prefix>",
37
38
  repeatable: true,
38
- description: "Repeatable. Allowed path prefix (git-path).",
39
+ description: "Repeatable. Allowed path prefix (git-path). Use minimal prefixes; repo-wide '.' is rejected (tip: `agentplane guard suggest-allow --format args`).",
39
40
  },
40
41
  {
41
42
  kind: "boolean",
42
43
  name: "auto-allow",
43
44
  default: false,
44
- description: "Infer --allow prefixes from staged paths (only when no explicit --allow is provided).",
45
+ description: "Deprecated. Disabled for safety; pass explicit --allow prefixes.",
46
+ deprecated: "disabled",
45
47
  },
46
48
  {
47
49
  kind: "boolean",
@@ -77,10 +79,6 @@ export const commitSpec = {
77
79
  cmd: 'agentplane commit 202602030608-F1Q8AB -m "✨ F1Q8AB task: implement allowlist guard" --allow packages/agentplane',
78
80
  why: "Create a commit after validating allowlist and subject policy.",
79
81
  },
80
- {
81
- cmd: 'agentplane commit 202602030608-F1Q8AB -m "✨ F1Q8AB task: implement allowlist guard" --auto-allow',
82
- why: "Infer allowlist prefixes from staged paths.",
83
- },
84
82
  {
85
83
  cmd: "agentplane commit 202602030608-F1Q8AB --close",
86
84
  why: "Create a close commit for the task README using a deterministic message builder.",
@@ -120,6 +118,20 @@ export const commitSpec = {
120
118
  if (Array.isArray(allow) && allow.some((s) => typeof s === "string" && s.trim() === "")) {
121
119
  throw usageError({ spec: commitSpec, message: "Invalid value for --allow: empty." });
122
120
  }
121
+ const allowList = Array.isArray(allow)
122
+ ? allow
123
+ : typeof allow === "string"
124
+ ? [allow]
125
+ : [];
126
+ if (findRepoWideAllowPrefixes(allowList).length > 0) {
127
+ throw usageError({ spec: commitSpec, message: repoWideAllowPrefixMessage("--allow") });
128
+ }
129
+ if (raw.opts["auto-allow"] === true) {
130
+ throw usageError({
131
+ spec: commitSpec,
132
+ message: "--auto-allow is disabled; pass explicit --allow <path-prefix>.",
133
+ });
134
+ }
123
135
  },
124
136
  parse: (raw) => ({
125
137
  taskId: String(raw.args["task-id"]),
@@ -1 +1 @@
1
- {"version":3,"file":"doctor.run.d.ts","sourceRoot":"","sources":["../../src/commands/doctor.run.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAK1D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAsPrD,eAAO,MAAM,SAAS,EAAE,cAAc,CAAC,YAAY,CAwBlD,CAAC"}
1
+ {"version":3,"file":"doctor.run.d.ts","sourceRoot":"","sources":["../../src/commands/doctor.run.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAK1D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAqUrD,eAAO,MAAM,SAAS,EAAE,cAAc,CAAC,YAAY,CAmDlD,CAAC"}
@@ -5,6 +5,7 @@ import { warnMessage, successMessage } from "../cli/output.js";
5
5
  import { RUNTIME_GITIGNORE_LINES } from "../shared/runtime-artifacts.js";
6
6
  import { execFileAsync, gitEnv } from "./shared/git.js";
7
7
  import { loadCommandContext } from "./shared/task-backend.js";
8
+ import { emitWorkflowEvent, isWorkflowEnforcementDisabled, resolveWorkflowPaths, safeAutofixWorkflowText, validateWorkflowAtPath, workflowEnforcementEnvHint, } from "../workflow-runtime/index.js";
8
9
  async function listTsFiles(rootDir) {
9
10
  const out = [];
10
11
  async function walk(absDir) {
@@ -160,6 +161,75 @@ async function safeFixTaskIndex(repoRoot) {
160
161
  return { changed: false, note: "Skip: could not rebuild tasks index cache." };
161
162
  }
162
163
  }
164
+ async function checkWorkflowContract(repoRoot) {
165
+ const result = await validateWorkflowAtPath(repoRoot);
166
+ const findings = result.diagnostics.map((d) => `[${d.severity}] ${d.code} ${d.path}: ${d.message}`);
167
+ emitWorkflowEvent({
168
+ event: "workflow_doctor_check",
169
+ details: { ok: result.ok, findings: result.diagnostics.length },
170
+ });
171
+ return findings;
172
+ }
173
+ function findingSeverity(problem) {
174
+ const normalized = problem.trimStart();
175
+ if (normalized.startsWith("[WARN]"))
176
+ return "WARN";
177
+ if (normalized.startsWith("[INFO]"))
178
+ return "INFO";
179
+ if (normalized.startsWith("[ERROR]"))
180
+ return "ERROR";
181
+ return "ERROR";
182
+ }
183
+ async function safeFixWorkflow(repoRoot) {
184
+ const paths = resolveWorkflowPaths(repoRoot);
185
+ let current = "";
186
+ let sourcePath = paths.workflowPath;
187
+ try {
188
+ current = await fs.readFile(paths.workflowPath, "utf8");
189
+ }
190
+ catch {
191
+ try {
192
+ current = await fs.readFile(paths.legacyWorkflowPath, "utf8");
193
+ sourcePath = paths.legacyWorkflowPath;
194
+ }
195
+ catch {
196
+ return { changed: false, note: "Skip: workflow contract file not found." };
197
+ }
198
+ }
199
+ const fixed = safeAutofixWorkflowText(current);
200
+ if (fixed.diagnostics.some((d) => d.code === "WF_FIX_SKIPPED_UNSAFE")) {
201
+ const details = fixed.diagnostics.map((d) => `${d.path}`).join(", ");
202
+ return {
203
+ changed: false,
204
+ note: `Skip: unsafe workflow autofix required (unknown keys). Proposed manual review: ${details}`,
205
+ };
206
+ }
207
+ if (!fixed.changed) {
208
+ if (sourcePath === paths.workflowPath) {
209
+ return { changed: false, note: "OK: workflow contract already normalized." };
210
+ }
211
+ await fs.mkdir(path.dirname(paths.workflowPath), { recursive: true });
212
+ await fs.writeFile(paths.workflowPath, current, "utf8");
213
+ await fs.rm(paths.legacyWorkflowPath, { force: true });
214
+ await fs.mkdir(paths.workflowDir, { recursive: true });
215
+ await fs.copyFile(paths.workflowPath, paths.lastKnownGoodPath);
216
+ return {
217
+ changed: true,
218
+ note: "Fixed: moved legacy WORKFLOW.md into .agentplane and refreshed last-known-good.",
219
+ };
220
+ }
221
+ await fs.mkdir(path.dirname(paths.workflowPath), { recursive: true });
222
+ await fs.writeFile(paths.workflowPath, fixed.text, "utf8");
223
+ if (sourcePath === paths.legacyWorkflowPath) {
224
+ await fs.rm(paths.legacyWorkflowPath, { force: true });
225
+ }
226
+ await fs.mkdir(paths.workflowDir, { recursive: true });
227
+ await fs.copyFile(paths.workflowPath, paths.lastKnownGoodPath);
228
+ return {
229
+ changed: true,
230
+ note: "Fixed: normalized workflow contract and refreshed last-known-good.",
231
+ };
232
+ }
163
233
  async function checkDoneTaskCommitInvariants(repoRoot) {
164
234
  const tasksPath = path.join(repoRoot, ".agentplane", "tasks.json");
165
235
  let raw = "";
@@ -228,22 +298,38 @@ async function checkDoneTaskCommitInvariants(repoRoot) {
228
298
  export const runDoctor = async (ctx, p) => {
229
299
  const resolved = await resolveProject({ cwd: ctx.cwd, rootOverride: ctx.rootOverride ?? null });
230
300
  const repoRoot = resolved.gitRoot;
231
- const problems = await checkWorkspace(repoRoot);
232
- problems.push(...(await checkDoneTaskCommitInvariants(repoRoot)));
233
- if (p.dev) {
234
- problems.push(...(await checkLayering(repoRoot)));
235
- }
236
- if (problems.length > 0) {
237
- console.error(warnMessage(`doctor found ${problems.length} problem(s):`));
238
- for (const prob of problems)
239
- console.error(`- ${prob}`);
240
- return 1;
301
+ const runChecks = async () => {
302
+ const checks = await checkWorkspace(repoRoot);
303
+ checks.push(...(await checkDoneTaskCommitInvariants(repoRoot)));
304
+ if (!isWorkflowEnforcementDisabled()) {
305
+ checks.push(...(await checkWorkflowContract(repoRoot)));
306
+ }
307
+ if (p.dev) {
308
+ checks.push(...(await checkLayering(repoRoot)));
309
+ }
310
+ return checks;
311
+ };
312
+ if (isWorkflowEnforcementDisabled()) {
313
+ console.log(successMessage("doctor", undefined, `workflow contract checks disabled via ${workflowEnforcementEnvHint()}.`));
241
314
  }
242
315
  if (p.fix) {
243
316
  const fix = await safeFixGitignore(repoRoot);
244
317
  console.log(successMessage("doctor fix", undefined, fix.note));
245
318
  const idx = await safeFixTaskIndex(repoRoot);
246
319
  console.log(successMessage("doctor fix", undefined, idx.note));
320
+ const workflowFix = await safeFixWorkflow(repoRoot);
321
+ console.log(successMessage("doctor fix", undefined, workflowFix.note));
322
+ }
323
+ const problems = await runChecks();
324
+ const errors = problems.filter((problem) => findingSeverity(problem) === "ERROR");
325
+ if (problems.length > 0) {
326
+ const warningCount = problems.filter((problem) => findingSeverity(problem) === "WARN").length;
327
+ const infoCount = problems.filter((problem) => findingSeverity(problem) === "INFO").length;
328
+ console.error(warnMessage(`doctor findings: errors=${errors.length} warnings=${warningCount} info=${infoCount}`));
329
+ for (const prob of problems)
330
+ console.error(`- ${prob}`);
331
+ if (errors.length > 0)
332
+ return 1;
247
333
  }
248
334
  console.log(successMessage("doctor", undefined, "OK"));
249
335
  return 0;
@@ -1 +1 @@
1
- {"version":3,"file":"finish.spec.d.ts","sourceRoot":"","sources":["../../src/commands/finish.spec.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAKvD,MAAM,MAAM,YAAY,GAAG;IACzB,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,EAAE,OAAO,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,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,EAAE,OAAO,CAAC;IACrB,kBAAkB,EAAE,OAAO,CAAC;IAC5B,KAAK,EAAE,OAAO,CAAC;CAChB,CAAC;AAEF,eAAO,MAAM,UAAU,EAAE,WAAW,CAAC,YAAY,CAqPhD,CAAC"}
1
+ {"version":3,"file":"finish.spec.d.ts","sourceRoot":"","sources":["../../src/commands/finish.spec.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AASvD,MAAM,MAAM,YAAY,GAAG;IACzB,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,EAAE,OAAO,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,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,EAAE,OAAO,CAAC;IACrB,kBAAkB,EAAE,OAAO,CAAC;IAC5B,KAAK,EAAE,OAAO,CAAC;CAChB,CAAC;AAEF,eAAO,MAAM,UAAU,EAAE,WAAW,CAAC,YAAY,CAsShD,CAAC"}
@@ -1,5 +1,6 @@
1
1
  import { usageError } from "../cli/spec/errors.js";
2
2
  import { toStringList } from "../cli/spec/parse-utils.js";
3
+ import { findRepoWideAllowPrefixes, repoWideAllowPrefixMessage, } from "../shared/allow-prefix-policy.js";
3
4
  export const finishSpec = {
4
5
  id: ["finish"],
5
6
  group: "Lifecycle",
@@ -77,13 +78,14 @@ export const finishSpec = {
77
78
  name: "commit-allow",
78
79
  valueHint: "<path-prefix>",
79
80
  repeatable: true,
80
- description: "Repeatable. Allowlist path prefixes to stage for the commit (used with --commit-from-comment).",
81
+ description: "Repeatable. Allowlist path prefixes to stage for the commit (used with --commit-from-comment). Use minimal prefixes; '.' is rejected.",
81
82
  },
82
83
  {
83
84
  kind: "boolean",
84
85
  name: "commit-auto-allow",
85
86
  default: false,
86
- description: "Auto-allow inferred allowlist paths if none are provided (used with --commit-from-comment).",
87
+ description: "Deprecated. Disabled for safety; pass explicit --commit-allow prefixes.",
88
+ deprecated: "disabled",
87
89
  },
88
90
  {
89
91
  kind: "boolean",
@@ -116,13 +118,14 @@ export const finishSpec = {
116
118
  name: "status-commit-allow",
117
119
  valueHint: "<path-prefix>",
118
120
  repeatable: true,
119
- description: "Repeatable. Allowlist path prefixes to stage for the status commit (used with --status-commit).",
121
+ description: "Repeatable. Allowlist path prefixes to stage for the status commit (used with --status-commit). Use minimal prefixes; '.' is rejected.",
120
122
  },
121
123
  {
122
124
  kind: "boolean",
123
125
  name: "status-commit-auto-allow",
124
126
  default: false,
125
- description: "Auto-allow inferred allowlist paths if none are provided (used with --status-commit).",
127
+ description: "Deprecated. Disabled for safety; pass explicit --status-commit-allow prefixes.",
128
+ deprecated: "disabled",
126
129
  },
127
130
  {
128
131
  kind: "boolean",
@@ -165,6 +168,38 @@ export const finishSpec = {
165
168
  const taskIds = Array.isArray(ids) ? ids : [];
166
169
  const commitFromComment = raw.opts["commit-from-comment"] === true;
167
170
  const statusCommit = raw.opts["status-commit"] === true;
171
+ const commitAllow = toStringList(raw.opts["commit-allow"]);
172
+ const statusCommitAllow = toStringList(raw.opts["status-commit-allow"]);
173
+ const commitAutoAllow = raw.opts["commit-auto-allow"] === true;
174
+ const statusCommitAutoAllow = raw.opts["status-commit-auto-allow"] === true;
175
+ if (findRepoWideAllowPrefixes(commitAllow).length > 0) {
176
+ throw usageError({
177
+ spec: finishSpec,
178
+ command: "finish",
179
+ message: repoWideAllowPrefixMessage("--commit-allow"),
180
+ });
181
+ }
182
+ if (findRepoWideAllowPrefixes(statusCommitAllow).length > 0) {
183
+ throw usageError({
184
+ spec: finishSpec,
185
+ command: "finish",
186
+ message: repoWideAllowPrefixMessage("--status-commit-allow"),
187
+ });
188
+ }
189
+ if (commitAutoAllow) {
190
+ throw usageError({
191
+ spec: finishSpec,
192
+ command: "finish",
193
+ message: "--commit-auto-allow is disabled; pass explicit --commit-allow <path-prefix>.",
194
+ });
195
+ }
196
+ if (statusCommitAutoAllow) {
197
+ throw usageError({
198
+ spec: finishSpec,
199
+ command: "finish",
200
+ message: "--status-commit-auto-allow is disabled; pass explicit --status-commit-allow <path-prefix>.",
201
+ });
202
+ }
168
203
  if ((commitFromComment || statusCommit) && taskIds.length !== 1) {
169
204
  throw usageError({
170
205
  spec: finishSpec,
@@ -172,6 +207,20 @@ export const finishSpec = {
172
207
  message: "--commit-from-comment/--status-commit requires exactly one task id",
173
208
  });
174
209
  }
210
+ if (commitFromComment && commitAllow.length === 0) {
211
+ throw usageError({
212
+ spec: finishSpec,
213
+ command: "finish",
214
+ message: "--commit-from-comment requires --commit-allow <path-prefix> (tip: `agentplane guard suggest-allow --format args`).",
215
+ });
216
+ }
217
+ if (statusCommit && statusCommitAllow.length === 0) {
218
+ throw usageError({
219
+ spec: finishSpec,
220
+ command: "finish",
221
+ message: "--status-commit requires --status-commit-allow <path-prefix> (tip: `agentplane guard suggest-allow --format args`).",
222
+ });
223
+ }
175
224
  if (raw.opts["close-commit"] === true && taskIds.length !== 1) {
176
225
  throw usageError({
177
226
  spec: finishSpec,
@@ -1 +1 @@
1
- {"version":3,"file":"commit.command.d.ts","sourceRoot":"","sources":["../../../src/commands/guard/commit.command.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAGtE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAIhE,MAAM,MAAM,iBAAiB,GAAG;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,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,CAAC;AAEF,eAAO,MAAM,eAAe,EAAE,WAAW,CAAC,iBAAiB,CA+F1D,CAAC;AAEF,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,cAAc,CAAC,IAC1E,KAAK,UAAU,EAAE,GAAG,iBAAiB,KAAG,OAAO,CAAC,MAAM,CAAC,CAkCtE"}
1
+ {"version":3,"file":"commit.command.d.ts","sourceRoot":"","sources":["../../../src/commands/guard/commit.command.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAOtE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAIhE,MAAM,MAAM,iBAAiB,GAAG;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,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,CAAC;AAEF,eAAO,MAAM,eAAe,EAAE,WAAW,CAAC,iBAAiB,CA0G1D,CAAC;AAEF,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,cAAc,CAAC,IAC1E,KAAK,UAAU,EAAE,GAAG,iBAAiB,KAAG,OAAO,CAAC,MAAM,CAAC,CA2BtE"}
@@ -1,6 +1,7 @@
1
1
  import { usageError } from "../../cli/spec/errors.js";
2
2
  import { CliError } from "../../shared/errors.js";
3
- import { cmdGuardCommit, suggestAllowPrefixes } from "./index.js";
3
+ import { findRepoWideAllowPrefixes, repoWideAllowPrefixMessage, } from "../../shared/allow-prefix-policy.js";
4
+ import { cmdGuardCommit } from "./index.js";
4
5
  export const guardCommitSpec = {
5
6
  id: ["guard", "commit"],
6
7
  group: "Guard",
@@ -20,13 +21,14 @@ export const guardCommitSpec = {
20
21
  name: "allow",
21
22
  valueHint: "<path-prefix>",
22
23
  repeatable: true,
23
- description: "Repeatable. Allowed path prefix (git-path).",
24
+ description: "Repeatable. Allowed path prefix (git-path). Use minimal prefixes; repo-wide '.' is rejected (tip: `agentplane guard suggest-allow --format args`).",
24
25
  },
25
26
  {
26
27
  kind: "boolean",
27
28
  name: "auto-allow",
28
29
  default: false,
29
- description: "Infer --allow prefixes from staged paths (only when no explicit --allow is provided).",
30
+ description: "Deprecated. Disabled for safety; pass explicit --allow prefixes.",
31
+ deprecated: "disabled",
30
32
  },
31
33
  {
32
34
  kind: "boolean",
@@ -62,10 +64,6 @@ export const guardCommitSpec = {
62
64
  cmd: 'agentplane guard commit 202602030608-F1Q8AB -m "✨ F1Q8AB task: implement allowlist guard" --allow packages/agentplane',
63
65
  why: "Validate staged changes are covered by allowlist and policy.",
64
66
  },
65
- {
66
- cmd: 'agentplane guard commit 202602030608-F1Q8AB -m "✨ F1Q8AB task: implement allowlist guard" --auto-allow',
67
- why: "Infer allowlist prefixes from staged paths.",
68
- },
69
67
  ],
70
68
  validateRaw: (raw) => {
71
69
  const msg = typeof raw.opts.message === "string" ? raw.opts.message.trim() : "";
@@ -76,6 +74,20 @@ export const guardCommitSpec = {
76
74
  if (Array.isArray(allow) && allow.some((s) => typeof s === "string" && s.trim() === "")) {
77
75
  throw usageError({ spec: guardCommitSpec, message: "Invalid value for --allow: empty." });
78
76
  }
77
+ const allowList = Array.isArray(allow)
78
+ ? allow
79
+ : typeof allow === "string"
80
+ ? [allow]
81
+ : [];
82
+ if (findRepoWideAllowPrefixes(allowList).length > 0) {
83
+ throw usageError({ spec: guardCommitSpec, message: repoWideAllowPrefixMessage("--allow") });
84
+ }
85
+ if (raw.opts["auto-allow"] === true) {
86
+ throw usageError({
87
+ spec: guardCommitSpec,
88
+ message: "--auto-allow is disabled; pass explicit --allow <path-prefix>.",
89
+ });
90
+ }
79
91
  },
80
92
  parse: (raw) => ({
81
93
  taskId: String(raw.args["task-id"]),
@@ -99,18 +111,12 @@ export const guardCommitSpec = {
99
111
  export function makeRunGuardCommitHandler(getCtx) {
100
112
  return async (ctx, p) => {
101
113
  const cmdCtx = await getCtx("guard commit");
102
- let allow = p.allow;
103
- if (p.autoAllow && allow.length === 0) {
104
- const staged = await cmdCtx.git.statusStagedPaths();
105
- const prefixes = suggestAllowPrefixes(staged);
106
- if (prefixes.length === 0) {
107
- throw new CliError({
108
- exitCode: 5,
109
- code: "E_GIT",
110
- message: "No staged files (git index empty)",
111
- });
112
- }
113
- allow = prefixes;
114
+ if (p.autoAllow) {
115
+ throw new CliError({
116
+ exitCode: 2,
117
+ code: "E_USAGE",
118
+ message: "--auto-allow is disabled; pass explicit --allow <path-prefix>.",
119
+ });
114
120
  }
115
121
  return await cmdGuardCommit({
116
122
  ctx: cmdCtx,
@@ -118,7 +124,7 @@ export function makeRunGuardCommitHandler(getCtx) {
118
124
  rootOverride: ctx.rootOverride,
119
125
  taskId: p.taskId,
120
126
  message: p.message,
121
- allow,
127
+ allow: p.allow,
122
128
  allowBase: p.allowBase,
123
129
  allowTasks: p.allowTasks,
124
130
  allowPolicy: p.allowPolicy,
@@ -1 +1 @@
1
- {"version":3,"file":"allow.d.ts","sourceRoot":"","sources":["../../../../src/commands/guard/impl/allow.ts"],"names":[],"mappings":"AAMA,OAAO,EAAsB,KAAK,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAkBvF,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,CAe9D;AAED,wBAAsB,qBAAqB,CAAC,IAAI,EAAE;IAChD,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAOpB;AAED,wBAAsB,cAAc,CAAC,IAAI,EAAE;IACzC,GAAG,CAAC,EAAE,cAAc,CAAC;IACrB,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB,GAAG,OAAO,CAAC,IAAI,CAAC,CAqBhB;AAED,wBAAsB,cAAc,CAAC,IAAI,EAAE;IACzC,GAAG,EAAE,cAAc,CAAC;IACpB,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,UAAU,EAAE,OAAO,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;CACnB,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CA2CpB"}
1
+ {"version":3,"file":"allow.d.ts","sourceRoot":"","sources":["../../../../src/commands/guard/impl/allow.ts"],"names":[],"mappings":"AAMA,OAAO,EAAsB,KAAK,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAkBvF,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,CAe9D;AAED,wBAAsB,qBAAqB,CAAC,IAAI,EAAE;IAChD,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAOpB;AAED,wBAAsB,cAAc,CAAC,IAAI,EAAE;IACzC,GAAG,CAAC,EAAE,cAAc,CAAC;IACrB,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB,GAAG,OAAO,CAAC,IAAI,CAAC,CAqBhB;AAED,wBAAsB,cAAc,CAAC,IAAI,EAAE;IACzC,GAAG,EAAE,cAAc,CAAC;IACpB,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,UAAU,EAAE,OAAO,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;CACnB,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAkDpB"}
@@ -82,6 +82,13 @@ export async function stageAllowlist(opts) {
82
82
  message: "Provide at least one allowed prefix",
83
83
  });
84
84
  }
85
+ if (allow.includes(".")) {
86
+ throw new CliError({
87
+ exitCode: 2,
88
+ code: "E_USAGE",
89
+ message: "Repo-wide allowlist ('.') is not allowed; choose minimal prefixes (tip: `agentplane guard suggest-allow --format args`).",
90
+ });
91
+ }
85
92
  const denied = new Set();
86
93
  if (!opts.allowTasks)
87
94
  denied.add(opts.tasksPath);
@@ -98,7 +105,7 @@ export async function stageAllowlist(opts) {
98
105
  throw new CliError({
99
106
  exitCode: 2,
100
107
  code: "E_USAGE",
101
- message: "No changes matched allowed prefixes (use --commit-auto-allow or update --commit-allow)",
108
+ message: "No changes matched allowed prefixes (update --commit-allow)",
102
109
  });
103
110
  }
104
111
  // `git add <pathspec>` is not reliable for staging deletes/renames across versions/configs.
@@ -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;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"}
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;AAQvF,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,CAa9E;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,CAmJlB"}
@@ -4,6 +4,7 @@ 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
6
  import { execFileAsync, gitEnv } from "../../shared/git.js";
7
+ import { ensureReconciledBeforeMutation } from "../../shared/reconcile-check.js";
7
8
  import { suggestAllowPrefixes } from "./allow.js";
8
9
  import { buildCloseCommitMessage, taskReadmePathForTask } from "./close-message.js";
9
10
  import { buildGitCommitEnv } from "./env.js";
@@ -111,7 +112,10 @@ export async function cmdGuardSuggestAllow(opts) {
111
112
  }
112
113
  export async function cmdGuardCommit(opts) {
113
114
  try {
114
- await guardCommitCheck(opts);
115
+ const ctx = opts.ctx ??
116
+ (await loadCommandContext({ cwd: opts.cwd, rootOverride: opts.rootOverride ?? null }));
117
+ await ensureReconciledBeforeMutation({ ctx, command: "guard commit" });
118
+ await guardCommitCheck({ ...opts, ctx });
115
119
  if (!opts.quiet)
116
120
  process.stdout.write("OK\n");
117
121
  return 0;
@@ -124,9 +128,12 @@ export async function cmdGuardCommit(opts) {
124
128
  }
125
129
  export async function cmdCommit(opts) {
126
130
  try {
131
+ const ctx = opts.ctx ??
132
+ (await loadCommandContext({ cwd: opts.cwd, rootOverride: opts.rootOverride ?? null }));
127
133
  if (opts.close) {
128
- const ctx = opts.ctx ??
129
- (await loadCommandContext({ cwd: opts.cwd, rootOverride: opts.rootOverride ?? null }));
134
+ if (!opts.closeCheckOnly) {
135
+ await ensureReconciledBeforeMutation({ ctx, command: "commit" });
136
+ }
130
137
  // Make the close commit deterministic: start from a clean index unless --unstage-others is used.
131
138
  let staged = await ctx.git.statusStagedPaths();
132
139
  if (staged.length > 0 && opts.closeUnstageOthers) {
@@ -201,28 +208,21 @@ export async function cmdCommit(opts) {
201
208
  }
202
209
  return 0;
203
210
  }
204
- let allow = opts.allow;
205
- if (opts.autoAllow && allow.length === 0) {
206
- const ctx = opts.ctx ??
207
- (await loadCommandContext({ cwd: opts.cwd, rootOverride: opts.rootOverride ?? null }));
208
- const staged = await ctx.git.statusStagedPaths();
209
- const prefixes = suggestAllowPrefixes(staged);
210
- if (prefixes.length === 0) {
211
- throw new CliError({
212
- exitCode: 5,
213
- code: "E_GIT",
214
- message: "No staged files (git index empty)",
215
- });
216
- }
217
- allow = prefixes;
211
+ if (opts.autoAllow) {
212
+ throw new CliError({
213
+ exitCode: 2,
214
+ code: "E_USAGE",
215
+ message: "--auto-allow is disabled; pass explicit --allow <path-prefix>.",
216
+ });
218
217
  }
218
+ await ensureReconciledBeforeMutation({ ctx, command: "commit" });
219
219
  await guardCommitCheck({
220
- ctx: opts.ctx,
220
+ ctx,
221
221
  cwd: opts.cwd,
222
222
  rootOverride: opts.rootOverride,
223
223
  taskId: opts.taskId,
224
224
  message: opts.message,
225
- allow,
225
+ allow: opts.allow,
226
226
  allowBase: opts.allowBase,
227
227
  allowTasks: opts.allowTasks,
228
228
  allowPolicy: opts.allowPolicy,
@@ -232,8 +232,6 @@ export async function cmdCommit(opts) {
232
232
  requireClean: opts.requireClean,
233
233
  quiet: opts.quiet,
234
234
  });
235
- const ctx = opts.ctx ??
236
- (await loadCommandContext({ cwd: opts.cwd, rootOverride: opts.rootOverride ?? null }));
237
235
  const env = buildGitCommitEnv({
238
236
  taskId: opts.taskId,
239
237
  allowTasks: opts.allowTasks,
@@ -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;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"}
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;AAQ/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,CAyF/D"}
@@ -2,10 +2,9 @@ import { extractTaskSuffix } from "@agentplaneorg/core";
2
2
  import { invalidValueMessage, successMessage } from "../../../cli/output.js";
3
3
  import { CliError } from "../../../shared/errors.js";
4
4
  import { formatCommentBodyForCommit, normalizeCommentBodyForCommit, } from "../../../shared/comment-format.js";
5
- import { protectedPathKindForFile } from "../../../shared/protected-paths.js";
6
5
  import { loadCommandContext } from "../../shared/task-backend.js";
7
6
  import { buildGitCommitEnv } from "./env.js";
8
- import { stageAllowlist, suggestAllowPrefixes } from "./allow.js";
7
+ import { stageAllowlist } from "./allow.js";
9
8
  import { guardCommitCheck } from "./policy.js";
10
9
  function deriveCommitMessageFromComment(opts) {
11
10
  const prefix = opts.emoji.trim();
@@ -49,27 +48,19 @@ function deriveCommitBodyFromComment(opts) {
49
48
  export async function commitFromComment(opts) {
50
49
  const ctx = opts.ctx ??
51
50
  (await loadCommandContext({ cwd: opts.cwd, rootOverride: opts.rootOverride ?? null }));
52
- let allowPrefixes = opts.allow.map((prefix) => prefix.trim()).filter(Boolean);
53
- if (opts.autoAllow && allowPrefixes.length === 0) {
54
- const changed = await ctx.git.statusChangedPaths();
55
- const tasksPath = opts.config.paths.tasks_path;
56
- // Auto-allow is for ergonomic status commits. It must never silently
57
- // broaden into policy/config/CI changes (those require explicit intent).
58
- const eligible = changed.filter((filePath) => {
59
- const kind = protectedPathKindForFile({ filePath, tasksPath });
60
- if (!kind)
61
- return true;
62
- if (kind === "tasks")
63
- return opts.allowTasks;
64
- return false;
51
+ if (opts.autoAllow) {
52
+ throw new CliError({
53
+ exitCode: 2,
54
+ code: "E_USAGE",
55
+ message: "--commit-auto-allow is disabled; pass explicit --commit-allow <path-prefix>.",
65
56
  });
66
- allowPrefixes = suggestAllowPrefixes(eligible);
67
57
  }
58
+ let allowPrefixes = opts.allow.map((prefix) => prefix.trim()).filter(Boolean);
68
59
  if (allowPrefixes.length === 0) {
69
60
  throw new CliError({
70
61
  exitCode: 2,
71
62
  code: "E_USAGE",
72
- message: "Provide at least one --commit-allow prefix or enable --commit-auto-allow",
63
+ message: "Provide at least one --commit-allow prefix",
73
64
  });
74
65
  }
75
66
  const staged = await stageAllowlist({
@@ -1 +1 @@
1
- {"version":3,"file":"install.d.ts","sourceRoot":"","sources":["../../../../../src/commands/recipes/impl/commands/install.ts"],"names":[],"mappings":"AA8BA,OAAO,KAAK,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAM3E,wBAAsB,gBAAgB,CAAC,IAAI,EAAE;IAC3C,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,mBAAmB,CAAC;IAC5B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,OAAO,CAAC;IACjB,UAAU,EAAE,kBAAkB,CAAC;IAC/B,GAAG,EAAE,OAAO,CAAC;CACd,GAAG,OAAO,CAAC,MAAM,CAAC,CAkMlB"}
1
+ {"version":3,"file":"install.d.ts","sourceRoot":"","sources":["../../../../../src/commands/recipes/impl/commands/install.ts"],"names":[],"mappings":"AAkCA,OAAO,KAAK,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAM3E,wBAAsB,gBAAgB,CAAC,IAAI,EAAE;IAC3C,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,mBAAmB,CAAC;IAC5B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,OAAO,CAAC;IACjB,UAAU,EAAE,kBAAkB,CAAC;IAC/B,GAAG,EAAE,OAAO,CAAC;CACd,GAAG,OAAO,CAAC,MAAM,CAAC,CA6NlB"}