agentplane 0.2.17 → 0.2.18

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 (93) hide show
  1. package/assets/AGENTS.md +14 -3
  2. package/dist/backends/task-backend/redmine/env.d.ts +16 -0
  3. package/dist/backends/task-backend/redmine/env.d.ts.map +1 -0
  4. package/dist/backends/task-backend/redmine/env.js +61 -0
  5. package/dist/backends/task-backend/redmine/mapping.d.ts.map +1 -1
  6. package/dist/backends/task-backend/redmine/mapping.js +25 -3
  7. package/dist/backends/task-backend/redmine-backend.d.ts +1 -1
  8. package/dist/backends/task-backend/redmine-backend.d.ts.map +1 -1
  9. package/dist/backends/task-backend/redmine-backend.js +31 -21
  10. package/dist/backends/task-backend/shared/errors.d.ts +2 -1
  11. package/dist/backends/task-backend/shared/errors.d.ts.map +1 -1
  12. package/dist/backends/task-backend/shared/errors.js +6 -2
  13. package/dist/cli/run-cli/commands/init/ui.d.ts.map +1 -1
  14. package/dist/cli/run-cli/commands/init/ui.js +5 -3
  15. package/dist/cli/run-cli/commands/init/write-env.d.ts.map +1 -1
  16. package/dist/cli/run-cli/commands/init/write-env.js +14 -6
  17. package/dist/cli/run-cli/commands/init.d.ts +1 -1
  18. package/dist/cli/run-cli/commands/init.d.ts.map +1 -1
  19. package/dist/cli/run-cli/commands/init.js +6 -68
  20. package/dist/cli/run-cli.d.ts.map +1 -1
  21. package/dist/cli/run-cli.js +5 -1
  22. package/dist/cli/run-cli.test-helpers.d.ts.map +1 -1
  23. package/dist/cli/run-cli.test-helpers.js +14 -2
  24. package/dist/cli/shared/ansi.d.ts +3 -0
  25. package/dist/cli/shared/ansi.d.ts.map +1 -0
  26. package/dist/cli/shared/ansi.js +19 -0
  27. package/dist/commands/block.run.d.ts.map +1 -1
  28. package/dist/commands/block.run.js +1 -0
  29. package/dist/commands/block.spec.d.ts +1 -0
  30. package/dist/commands/block.spec.d.ts.map +1 -1
  31. package/dist/commands/block.spec.js +7 -0
  32. package/dist/commands/branch/remove.command.d.ts +1 -0
  33. package/dist/commands/branch/remove.command.d.ts.map +1 -1
  34. package/dist/commands/branch/remove.command.js +8 -0
  35. package/dist/commands/branch/remove.d.ts +1 -0
  36. package/dist/commands/branch/remove.d.ts.map +1 -1
  37. package/dist/commands/branch/remove.js +9 -0
  38. package/dist/commands/doctor.run.d.ts.map +1 -1
  39. package/dist/commands/doctor.run.js +67 -0
  40. package/dist/commands/finish.run.d.ts.map +1 -1
  41. package/dist/commands/finish.run.js +1 -0
  42. package/dist/commands/finish.spec.d.ts +1 -0
  43. package/dist/commands/finish.spec.d.ts.map +1 -1
  44. package/dist/commands/finish.spec.js +7 -0
  45. package/dist/commands/guard/impl/commands.d.ts.map +1 -1
  46. package/dist/commands/guard/impl/commands.js +50 -0
  47. package/dist/commands/guard/impl/comment-commit.js +1 -1
  48. package/dist/commands/release/apply.command.d.ts.map +1 -1
  49. package/dist/commands/release/apply.command.js +39 -1
  50. package/dist/commands/shared/approval-requirements.d.ts +18 -0
  51. package/dist/commands/shared/approval-requirements.d.ts.map +1 -0
  52. package/dist/commands/shared/approval-requirements.js +77 -0
  53. package/dist/commands/shared/network-approval.d.ts.map +1 -1
  54. package/dist/commands/shared/network-approval.js +8 -23
  55. package/dist/commands/start.run.d.ts.map +1 -1
  56. package/dist/commands/start.run.js +1 -0
  57. package/dist/commands/start.spec.d.ts +1 -0
  58. package/dist/commands/start.spec.d.ts.map +1 -1
  59. package/dist/commands/start.spec.js +7 -0
  60. package/dist/commands/task/block.d.ts +1 -0
  61. package/dist/commands/task/block.d.ts.map +1 -1
  62. package/dist/commands/task/block.js +9 -0
  63. package/dist/commands/task/finish.d.ts +1 -0
  64. package/dist/commands/task/finish.d.ts.map +1 -1
  65. package/dist/commands/task/finish.js +9 -0
  66. package/dist/commands/task/migrate.command.d.ts +1 -0
  67. package/dist/commands/task/migrate.command.d.ts.map +1 -1
  68. package/dist/commands/task/migrate.command.js +8 -0
  69. package/dist/commands/task/migrate.d.ts +1 -0
  70. package/dist/commands/task/migrate.d.ts.map +1 -1
  71. package/dist/commands/task/migrate.js +9 -3
  72. package/dist/commands/task/normalize.command.d.ts +1 -0
  73. package/dist/commands/task/normalize.command.d.ts.map +1 -1
  74. package/dist/commands/task/normalize.command.js +8 -0
  75. package/dist/commands/task/normalize.d.ts +1 -0
  76. package/dist/commands/task/normalize.d.ts.map +1 -1
  77. package/dist/commands/task/normalize.js +9 -3
  78. package/dist/commands/task/scaffold.command.d.ts +1 -0
  79. package/dist/commands/task/scaffold.command.d.ts.map +1 -1
  80. package/dist/commands/task/scaffold.command.js +8 -0
  81. package/dist/commands/task/scaffold.d.ts +1 -0
  82. package/dist/commands/task/scaffold.d.ts.map +1 -1
  83. package/dist/commands/task/scaffold.js +9 -0
  84. package/dist/commands/task/set-status.command.d.ts +1 -0
  85. package/dist/commands/task/set-status.command.d.ts.map +1 -1
  86. package/dist/commands/task/set-status.command.js +8 -0
  87. package/dist/commands/task/set-status.d.ts +1 -0
  88. package/dist/commands/task/set-status.d.ts.map +1 -1
  89. package/dist/commands/task/set-status.js +7 -8
  90. package/dist/commands/task/start.d.ts +1 -0
  91. package/dist/commands/task/start.d.ts.map +1 -1
  92. package/dist/commands/task/start.js +9 -0
  93. package/package.json +2 -2
@@ -0,0 +1,19 @@
1
+ export function stripAnsi(text) {
2
+ if (!text)
3
+ return "";
4
+ let out = "";
5
+ for (let i = 0; i < text.length; i += 1) {
6
+ const ch = text.codePointAt(i);
7
+ if (ch === 27 && text[i + 1] === "[") {
8
+ i += 2;
9
+ while (i < text.length && text[i] !== "m")
10
+ i += 1;
11
+ continue;
12
+ }
13
+ out += text[i] ?? "";
14
+ }
15
+ return out;
16
+ }
17
+ export function visibleLen(text) {
18
+ return stripAnsi(text).length;
19
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"block.run.d.ts","sourceRoot":"","sources":["../../src/commands/block.run.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AACtD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAG/D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAEnD,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,cAAc,CAAC,IACpE,KAAK,UAAU,EAAE,GAAG,WAAW,KAAG,OAAO,CAAC,MAAM,CAAC,CAmBhE"}
1
+ {"version":3,"file":"block.run.d.ts","sourceRoot":"","sources":["../../src/commands/block.run.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AACtD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAG/D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAEnD,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,cAAc,CAAC,IACpE,KAAK,UAAU,EAAE,GAAG,WAAW,KAAG,OAAO,CAAC,MAAM,CAAC,CAoBhE"}
@@ -16,6 +16,7 @@ export function makeRunBlockHandler(getCtx) {
16
16
  commitRequireClean: p.commitRequireClean,
17
17
  confirmStatusCommit: p.confirmStatusCommit,
18
18
  force: p.force,
19
+ yes: p.yes,
19
20
  quiet: p.quiet,
20
21
  });
21
22
  };
@@ -11,6 +11,7 @@ export type BlockParsed = {
11
11
  commitRequireClean: boolean;
12
12
  confirmStatusCommit: boolean;
13
13
  force: boolean;
14
+ yes: boolean;
14
15
  quiet: boolean;
15
16
  };
16
17
  export declare const blockSpec: CommandSpec<BlockParsed>;
@@ -1 +1 @@
1
- {"version":3,"file":"block.spec.d.ts","sourceRoot":"","sources":["../../src/commands/block.spec.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAKvD,MAAM,MAAM,WAAW,GAAG;IACxB,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,KAAK,EAAE,OAAO,CAAC;CAChB,CAAC;AAEF,eAAO,MAAM,SAAS,EAAE,WAAW,CAAC,WAAW,CAmH9C,CAAC"}
1
+ {"version":3,"file":"block.spec.d.ts","sourceRoot":"","sources":["../../src/commands/block.spec.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAKvD,MAAM,MAAM,WAAW,GAAG;IACxB,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,EAAE,OAAO,CAAC;IACb,KAAK,EAAE,OAAO,CAAC;CAChB,CAAC;AAEF,eAAO,MAAM,SAAS,EAAE,WAAW,CAAC,WAAW,CA0H9C,CAAC"}
@@ -78,6 +78,12 @@ export const blockSpec = {
78
78
  default: false,
79
79
  description: "Override status transition restrictions.",
80
80
  },
81
+ {
82
+ kind: "boolean",
83
+ name: "yes",
84
+ default: false,
85
+ description: "Auto-approve force-action approval checks when required.",
86
+ },
81
87
  { kind: "boolean", name: "quiet", default: false, description: "Suppress output." },
82
88
  ],
83
89
  examples: [
@@ -110,6 +116,7 @@ export const blockSpec = {
110
116
  commitRequireClean: raw.opts["commit-require-clean"] === true,
111
117
  confirmStatusCommit: raw.opts["confirm-status-commit"] === true,
112
118
  force: raw.opts.force === true,
119
+ yes: raw.opts.yes === true,
113
120
  quiet: raw.opts.quiet === true,
114
121
  }),
115
122
  };
@@ -3,6 +3,7 @@ export type BranchRemoveParsed = {
3
3
  branch: string | null;
4
4
  worktree: string | null;
5
5
  force: boolean;
6
+ yes: boolean;
6
7
  quiet: boolean;
7
8
  };
8
9
  export declare const branchRemoveSpec: CommandSpec<BranchRemoveParsed>;
@@ -1 +1 @@
1
- {"version":3,"file":"remove.command.d.ts","sourceRoot":"","sources":["../../../src/commands/branch/remove.command.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAK1E,MAAM,MAAM,kBAAkB,GAAG;IAC/B,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,KAAK,EAAE,OAAO,CAAC;IACf,KAAK,EAAE,OAAO,CAAC;CAChB,CAAC;AAEF,eAAO,MAAM,gBAAgB,EAAE,WAAW,CAAC,kBAAkB,CAkD5D,CAAC;AAEF,eAAO,MAAM,eAAe,EAAE,cAAc,CAAC,kBAAkB,CAS9D,CAAC"}
1
+ {"version":3,"file":"remove.command.d.ts","sourceRoot":"","sources":["../../../src/commands/branch/remove.command.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAK1E,MAAM,MAAM,kBAAkB,GAAG;IAC/B,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,KAAK,EAAE,OAAO,CAAC;IACf,GAAG,EAAE,OAAO,CAAC;IACb,KAAK,EAAE,OAAO,CAAC;CAChB,CAAC;AAEF,eAAO,MAAM,gBAAgB,EAAE,WAAW,CAAC,kBAAkB,CAyD5D,CAAC;AAEF,eAAO,MAAM,eAAe,EAAE,cAAc,CAAC,kBAAkB,CAU9D,CAAC"}
@@ -18,6 +18,12 @@ export const branchRemoveSpec = {
18
18
  description: "Worktree path to remove (relative to repo root or absolute).",
19
19
  },
20
20
  { kind: "boolean", name: "force", default: false, description: "Force deletion." },
21
+ {
22
+ kind: "boolean",
23
+ name: "yes",
24
+ default: false,
25
+ description: "Auto-approve force-action approval checks when required.",
26
+ },
21
27
  { kind: "boolean", name: "quiet", default: false, description: "Reduce output noise." },
22
28
  ],
23
29
  examples: [
@@ -48,6 +54,7 @@ export const branchRemoveSpec = {
48
54
  branch: typeof raw.opts.branch === "string" ? raw.opts.branch : null,
49
55
  worktree: typeof raw.opts.worktree === "string" ? raw.opts.worktree : null,
50
56
  force: raw.opts.force === true,
57
+ yes: raw.opts.yes === true,
51
58
  quiet: raw.opts.quiet === true,
52
59
  }),
53
60
  };
@@ -58,6 +65,7 @@ export const runBranchRemove = async (ctx, p) => {
58
65
  branch: p.branch ?? undefined,
59
66
  worktree: p.worktree ?? undefined,
60
67
  force: p.force,
68
+ yes: p.yes,
61
69
  quiet: p.quiet,
62
70
  });
63
71
  };
@@ -4,6 +4,7 @@ export declare function cmdBranchRemove(opts: {
4
4
  branch?: string;
5
5
  worktree?: string;
6
6
  force: boolean;
7
+ yes?: boolean;
7
8
  quiet: boolean;
8
9
  }): Promise<number>;
9
10
  //# sourceMappingURL=remove.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"remove.d.ts","sourceRoot":"","sources":["../../../src/commands/branch/remove.ts"],"names":[],"mappings":"AAWA,wBAAsB,eAAe,CAAC,IAAI,EAAE;IAC1C,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,OAAO,CAAC;IACf,KAAK,EAAE,OAAO,CAAC;CAChB,GAAG,OAAO,CAAC,MAAM,CAAC,CA4DlB"}
1
+ {"version":3,"file":"remove.d.ts","sourceRoot":"","sources":["../../../src/commands/branch/remove.ts"],"names":[],"mappings":"AAYA,wBAAsB,eAAe,CAAC,IAAI,EAAE;IAC1C,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,OAAO,CAAC;IACf,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,KAAK,EAAE,OAAO,CAAC;CAChB,GAAG,OAAO,CAAC,MAAM,CAAC,CAoElB"}
@@ -3,6 +3,7 @@ import { loadConfig, resolveProject } from "@agentplaneorg/core";
3
3
  import { mapCoreError } from "../../cli/error-map.js";
4
4
  import { successMessage, unknownEntityMessage } from "../../cli/output.js";
5
5
  import { CliError } from "../../shared/errors.js";
6
+ import { ensureActionApproved } from "../shared/approval-requirements.js";
6
7
  import { execFileAsync, gitEnv } from "../shared/git.js";
7
8
  import { gitBranchExists } from "../shared/git-ops.js";
8
9
  import { isPathWithin, resolvePathFallback } from "../shared/path.js";
@@ -22,6 +23,14 @@ export async function cmdBranchRemove(opts) {
22
23
  rootOverride: opts.rootOverride ?? null,
23
24
  });
24
25
  const loaded = await loadConfig(resolved.agentplaneDir);
26
+ if (opts.force) {
27
+ await ensureActionApproved({
28
+ action: "force_action",
29
+ config: loaded.config,
30
+ yes: opts.yes === true,
31
+ reason: "branch remove --force",
32
+ });
33
+ }
25
34
  if (worktree) {
26
35
  const worktreePath = path.isAbsolute(worktree)
27
36
  ? await resolvePathFallback(worktree)
@@ -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;AAG1D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAoKrD,eAAO,MAAM,SAAS,EAAE,cAAc,CAAC,YAAY,CAuBlD,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;AAI1D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AA+OrD,eAAO,MAAM,SAAS,EAAE,cAAc,CAAC,YAAY,CAwBlD,CAAC"}
@@ -2,6 +2,7 @@ import fs from "node:fs/promises";
2
2
  import path from "node:path";
3
3
  import { resolveProject } from "@agentplaneorg/core";
4
4
  import { warnMessage, successMessage } from "../cli/output.js";
5
+ import { execFileAsync, gitEnv } from "./shared/git.js";
5
6
  import { loadCommandContext } from "./shared/task-backend.js";
6
7
  async function listTsFiles(rootDir) {
7
8
  const out = [];
@@ -150,10 +151,76 @@ async function safeFixTaskIndex(repoRoot) {
150
151
  return { changed: false, note: "Skip: could not rebuild tasks index cache." };
151
152
  }
152
153
  }
154
+ async function checkDoneTaskCommitInvariants(repoRoot) {
155
+ const tasksPath = path.join(repoRoot, ".agentplane", "tasks.json");
156
+ let raw = "";
157
+ try {
158
+ raw = await fs.readFile(tasksPath, "utf8");
159
+ }
160
+ catch {
161
+ return [];
162
+ }
163
+ let parsed;
164
+ try {
165
+ parsed = JSON.parse(raw);
166
+ }
167
+ catch {
168
+ return [`Invalid JSON snapshot: ${path.relative(repoRoot, tasksPath)}`];
169
+ }
170
+ const all = Array.isArray(parsed.tasks) ? parsed.tasks : [];
171
+ const done = all.filter((t) => {
172
+ const status = typeof t.status === "string" ? t.status : "";
173
+ return status.toUpperCase() === "DONE";
174
+ });
175
+ if (done.length === 0)
176
+ return [];
177
+ const problems = [];
178
+ const hashes = new Set();
179
+ for (const task of done) {
180
+ const id = typeof task.id === "string" ? task.id : "<unknown>";
181
+ const hash = typeof task.commit?.hash === "string" ? task.commit.hash.trim() : "";
182
+ if (!hash) {
183
+ problems.push(`DONE task is missing implementation commit hash: ${id} (finish with --commit <hash>).`);
184
+ continue;
185
+ }
186
+ hashes.add(hash);
187
+ }
188
+ if (hashes.size === 0)
189
+ return problems;
190
+ const subjectByHash = new Map();
191
+ for (const hash of hashes) {
192
+ try {
193
+ const { stdout } = await execFileAsync("git", ["show", "-s", "--format=%s", hash], {
194
+ cwd: repoRoot,
195
+ env: gitEnv(),
196
+ });
197
+ subjectByHash.set(hash, String(stdout ?? "").trim());
198
+ }
199
+ catch {
200
+ subjectByHash.set(hash, "");
201
+ }
202
+ }
203
+ for (const task of done) {
204
+ const id = typeof task.id === "string" ? task.id : "<unknown>";
205
+ const hash = typeof task.commit?.hash === "string" ? task.commit.hash.trim() : "";
206
+ if (!hash)
207
+ continue;
208
+ const subject = subjectByHash.get(hash) ?? "";
209
+ if (!subject) {
210
+ problems.push(`DONE task references unknown commit hash: ${id} -> ${hash}`);
211
+ continue;
212
+ }
213
+ if (/\bclose:/iu.test(subject)) {
214
+ problems.push(`DONE task implementation commit points to a close commit: ${id} -> ${hash} (${subject})`);
215
+ }
216
+ }
217
+ return problems;
218
+ }
153
219
  export const runDoctor = async (ctx, p) => {
154
220
  const resolved = await resolveProject({ cwd: ctx.cwd, rootOverride: ctx.rootOverride ?? null });
155
221
  const repoRoot = resolved.gitRoot;
156
222
  const problems = await checkWorkspace(repoRoot);
223
+ problems.push(...(await checkDoneTaskCommitInvariants(repoRoot)));
157
224
  if (p.dev) {
158
225
  problems.push(...(await checkLayering(repoRoot)));
159
226
  }
@@ -1 +1 @@
1
- {"version":3,"file":"finish.run.d.ts","sourceRoot":"","sources":["../../src/commands/finish.run.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAEtD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAG/D,OAAO,EAAc,KAAK,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAEjE,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,cAAc,CAAC,IACrE,KAAK,UAAU,EAAE,GAAG,YAAY,KAAG,OAAO,CAAC,MAAM,CAAC,CAmCjE"}
1
+ {"version":3,"file":"finish.run.d.ts","sourceRoot":"","sources":["../../src/commands/finish.run.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAEtD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAG/D,OAAO,EAAc,KAAK,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAEjE,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,cAAc,CAAC,IACrE,KAAK,UAAU,EAAE,GAAG,YAAY,KAAG,OAAO,CAAC,MAAM,CAAC,CAoCjE"}
@@ -22,6 +22,7 @@ export function makeRunFinishHandler(getCtx) {
22
22
  breaking: p.breaking,
23
23
  commit: p.commit,
24
24
  force: p.force,
25
+ yes: p.yes,
25
26
  commitFromComment: p.commitFromComment,
26
27
  commitEmoji: p.commitEmoji,
27
28
  commitAllow: p.commitAllow,
@@ -8,6 +8,7 @@ export type FinishParsed = {
8
8
  breaking: boolean;
9
9
  commit?: string;
10
10
  force: boolean;
11
+ yes: boolean;
11
12
  commitFromComment: boolean;
12
13
  commitEmoji?: string;
13
14
  commitAllow: string[];
@@ -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,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,KAAK,EAAE,OAAO,CAAC;CAChB,CAAC;AAEF,eAAO,MAAM,UAAU,EAAE,WAAW,CAAC,YAAY,CAsMhD,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;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,KAAK,EAAE,OAAO,CAAC;CAChB,CAAC;AAEF,eAAO,MAAM,UAAU,EAAE,WAAW,CAAC,YAAY,CA6MhD,CAAC"}
@@ -54,6 +54,12 @@ export const finishSpec = {
54
54
  description: "Commit hash to record on the task (optional).",
55
55
  },
56
56
  { kind: "boolean", name: "force", default: false, description: "Force finish despite gates." },
57
+ {
58
+ kind: "boolean",
59
+ name: "yes",
60
+ default: false,
61
+ description: "Auto-approve force-action approval checks when required.",
62
+ },
57
63
  {
58
64
  kind: "boolean",
59
65
  name: "commit-from-comment",
@@ -176,6 +182,7 @@ export const finishSpec = {
176
182
  breaking: raw.opts.breaking === true,
177
183
  commit: raw.opts.commit,
178
184
  force: raw.opts.force === true,
185
+ yes: raw.opts.yes === true,
179
186
  commitFromComment: raw.opts["commit-from-comment"] === true,
180
187
  commitEmoji: raw.opts["commit-emoji"],
181
188
  commitAllow: toStringList(raw.opts["commit-allow"]),
@@ -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;AAExE,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,CAgIlB"}
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"}
@@ -7,6 +7,53 @@ import { suggestAllowPrefixes } from "./allow.js";
7
7
  import { buildCloseCommitMessage, taskReadmePathForTask } from "./close-message.js";
8
8
  import { buildGitCommitEnv } from "./env.js";
9
9
  import { guardCommitCheck } from "./policy.js";
10
+ function readText(value) {
11
+ if (typeof value === "string")
12
+ return value;
13
+ if (Buffer.isBuffer(value))
14
+ return value.toString("utf8");
15
+ return "";
16
+ }
17
+ function summarizeOutput(raw) {
18
+ const lines = raw
19
+ .replaceAll("\r\n", "\n")
20
+ .split("\n")
21
+ .map((line) => line.trimEnd())
22
+ .filter((line) => line.trim().length > 0)
23
+ .map((line) => (line.length > 180 ? `${line.slice(0, 180)} [truncated]` : line));
24
+ if (lines.length <= 12)
25
+ return lines;
26
+ const head = lines.slice(0, 6);
27
+ const tail = lines.slice(-6);
28
+ return [...head, `[${lines.length - 12} lines omitted]`, ...tail];
29
+ }
30
+ function asCommitFailure(err) {
31
+ if (err instanceof Error) {
32
+ const e = err;
33
+ const cmd = typeof e.cmd === "string" ? e.cmd : "";
34
+ if (cmd.startsWith("git commit")) {
35
+ const output = [readText(e.stderr), readText(e.stdout)]
36
+ .filter((part) => part.length > 0)
37
+ .join("\n");
38
+ const summary = summarizeOutput(output);
39
+ const code = typeof e.code === "number" ? e.code : null;
40
+ const lines = ["git commit failed (hook or commit policy).", `command: ${cmd}`];
41
+ if (typeof code === "number") {
42
+ lines.push(`exit_code: ${code}`);
43
+ }
44
+ if (summary.length > 0)
45
+ lines.push("output_summary:");
46
+ lines.push(...summary.map((line) => ` ${line}`));
47
+ return new CliError({
48
+ exitCode: 5,
49
+ code: "E_GIT",
50
+ message: lines.join("\n"),
51
+ });
52
+ }
53
+ return null;
54
+ }
55
+ return null;
56
+ }
10
57
  export async function cmdGuardClean(opts) {
11
58
  try {
12
59
  const ctx = await loadCommandContext({
@@ -186,6 +233,9 @@ export async function cmdCommit(opts) {
186
233
  catch (err) {
187
234
  if (err instanceof CliError)
188
235
  throw err;
236
+ const commitFailure = asCommitFailure(err);
237
+ if (commitFailure)
238
+ throw commitFailure;
189
239
  throw mapCoreError(err, { command: "commit", root: opts.rootOverride ?? null });
190
240
  }
191
241
  }
@@ -18,7 +18,7 @@ function deriveCommitMessageFromComment(opts) {
18
18
  message: "Comment body is required to build a commit message from the task comment",
19
19
  });
20
20
  }
21
- const summary = raw.replace(/^(start|blocked|verified):\s+/i, "").trim();
21
+ const summary = raw.replace(/^(start|blocked|verified):\s*/i, "").trim();
22
22
  if (!summary) {
23
23
  throw new CliError({
24
24
  exitCode: 2,
@@ -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;AAuOnD,eAAO,MAAM,gBAAgB,EAAE,WAAW,CAAC,kBAAkB,CAwE5D,CAAC;AAEF,eAAO,MAAM,eAAe,EAAE,cAAc,CAAC,kBAAkB,CA6H9D,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;AAyQnD,eAAO,MAAM,gBAAgB,EAAE,WAAW,CAAC,kBAAkB,CAwE5D,CAAC;AAEF,eAAO,MAAM,eAAe,EAAE,cAAc,CAAC,kBAAkB,CAyJ9D,CAAC"}
@@ -1,4 +1,4 @@
1
- import { readFile, writeFile, readdir } from "node:fs/promises";
1
+ import { readFile, writeFile, readdir, mkdir } from "node:fs/promises";
2
2
  import path from "node:path";
3
3
  import { resolveProject, loadConfig } from "@agentplaneorg/core";
4
4
  import { usageError } from "../../cli/spec/errors.js";
@@ -212,6 +212,17 @@ async function ensureNpmVersionsAvailable(gitRoot, version) {
212
212
  });
213
213
  }
214
214
  }
215
+ async function writeReleaseApplyReport(gitRoot, report) {
216
+ const runId = new Date().toISOString().replaceAll(":", "-").replaceAll(".", "-");
217
+ const dir = path.join(gitRoot, ".agentplane", ".release", "apply");
218
+ await mkdir(dir, { recursive: true });
219
+ const reportPath = path.join(dir, `${runId}.json`);
220
+ const latestPath = path.join(dir, "latest.json");
221
+ const text = `${JSON.stringify(report, null, 2)}\n`;
222
+ await writeFile(reportPath, text, "utf8");
223
+ await writeFile(latestPath, text, "utf8");
224
+ return reportPath;
225
+ }
215
226
  export const releaseApplySpec = {
216
227
  id: ["release", "apply"],
217
228
  group: "Release",
@@ -336,6 +347,7 @@ export const runReleaseApply = async (ctx, flags) => {
336
347
  const git = new GitContext({ gitRoot });
337
348
  await ensureCleanTrackedTree(gitRoot);
338
349
  await ensureTagDoesNotExist(gitRoot, plan.nextTag);
350
+ let npmVersionChecked = false;
339
351
  if (flags.push) {
340
352
  const loaded = await loadConfig(resolved.agentplaneDir);
341
353
  await ensureNetworkApproved({
@@ -345,7 +357,9 @@ export const runReleaseApply = async (ctx, flags) => {
345
357
  interactive: Boolean(process.stdin.isTTY),
346
358
  });
347
359
  await ensureNpmVersionsAvailable(gitRoot, plan.nextVersion);
360
+ npmVersionChecked = true;
348
361
  }
362
+ let releaseCommit = null;
349
363
  if (coreVersion === plan.prevVersion) {
350
364
  await Promise.all([
351
365
  replacePackageVersionInFile(corePkgPath, plan.nextVersion),
@@ -378,6 +392,11 @@ export const runReleaseApply = async (ctx, flags) => {
378
392
  else {
379
393
  const subject = `✨ release: ${plan.nextTag}`;
380
394
  await git.commit({ message: subject, env: cleanHookEnv() });
395
+ const { stdout: headHash } = await execFileAsync("git", ["rev-parse", "HEAD"], {
396
+ cwd: gitRoot,
397
+ env: gitEnv(),
398
+ });
399
+ releaseCommit = { hash: String(headHash ?? "").trim(), subject };
381
400
  }
382
401
  await execFileAsync("git", ["tag", plan.nextTag], { cwd: gitRoot, env: gitEnv() });
383
402
  process.stdout.write(`Release tag created: ${plan.nextTag}\n`);
@@ -392,5 +411,24 @@ export const runReleaseApply = async (ctx, flags) => {
392
411
  else {
393
412
  process.stdout.write(`Next: git push <remote> HEAD && git push <remote> ${plan.nextTag}\n`);
394
413
  }
414
+ const reportPath = await writeReleaseApplyReport(gitRoot, {
415
+ applied_at: new Date().toISOString(),
416
+ plan_dir: path.relative(gitRoot, planDir),
417
+ notes_path: path.relative(gitRoot, notesPath),
418
+ prev_version: plan.prevVersion,
419
+ next_version: plan.nextVersion,
420
+ prev_tag: plan.prevTag,
421
+ next_tag: plan.nextTag,
422
+ bump: plan.bump,
423
+ checks: {
424
+ clean_tracked_tree: true,
425
+ tag_absent: true,
426
+ notes_validated: true,
427
+ npm_version_available_checked: npmVersionChecked,
428
+ },
429
+ commit: releaseCommit,
430
+ push: { requested: flags.push, remote: flags.remote, performed: flags.push },
431
+ });
432
+ process.stdout.write(`Release report: ${path.relative(gitRoot, reportPath)}\n`);
395
433
  return 0;
396
434
  };
@@ -0,0 +1,18 @@
1
+ import type { AgentplaneConfig } from "@agentplaneorg/core";
2
+ export type ApprovalAction = "network_access" | "force_action" | "policy_write" | "config_write" | "dangerous_fs" | "git_push";
3
+ export type ApprovalRequirement = {
4
+ required: boolean;
5
+ reason: string;
6
+ };
7
+ export declare function getApprovalRequirements(opts: {
8
+ config: AgentplaneConfig;
9
+ action: ApprovalAction;
10
+ }): ApprovalRequirement;
11
+ export declare function ensureActionApproved(opts: {
12
+ action: ApprovalAction;
13
+ config: AgentplaneConfig;
14
+ yes: boolean;
15
+ reason: string;
16
+ interactive?: boolean;
17
+ }): Promise<void>;
18
+ //# sourceMappingURL=approval-requirements.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"approval-requirements.d.ts","sourceRoot":"","sources":["../../../src/commands/shared/approval-requirements.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAK5D,MAAM,MAAM,cAAc,GACtB,gBAAgB,GAChB,cAAc,GACd,cAAc,GACd,cAAc,GACd,cAAc,GACd,UAAU,CAAC;AAEf,MAAM,MAAM,mBAAmB,GAAG;IAChC,QAAQ,EAAE,OAAO,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAmCF,wBAAgB,uBAAuB,CAAC,IAAI,EAAE;IAC5C,MAAM,EAAE,gBAAgB,CAAC;IACzB,MAAM,EAAE,cAAc,CAAC;CACxB,GAAG,mBAAmB,CAkCtB;AAED,wBAAsB,oBAAoB,CAAC,IAAI,EAAE;IAC/C,MAAM,EAAE,cAAc,CAAC;IACvB,MAAM,EAAE,gBAAgB,CAAC;IACzB,GAAG,EAAE,OAAO,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB,GAAG,OAAO,CAAC,IAAI,CAAC,CAsBhB"}
@@ -0,0 +1,77 @@
1
+ import { promptYesNo } from "../../cli/prompts.js";
2
+ import { CliError } from "../../shared/errors.js";
3
+ function resolveEffectiveApprovals(config) {
4
+ const approvals = config.agents?.approvals;
5
+ const base = {
6
+ require_plan: approvals?.require_plan === true,
7
+ require_network: approvals?.require_network === true,
8
+ require_verify: approvals?.require_verify === true,
9
+ // Backward-compatible with older @agentplaneorg/core typings where require_force is absent.
10
+ require_force: approvals?.require_force === true,
11
+ };
12
+ if (config.execution.profile === "conservative") {
13
+ return {
14
+ ...base,
15
+ require_network: true,
16
+ require_force: true,
17
+ };
18
+ }
19
+ return base;
20
+ }
21
+ export function getApprovalRequirements(opts) {
22
+ const effectiveApprovals = resolveEffectiveApprovals(opts.config);
23
+ switch (opts.action) {
24
+ case "network_access": {
25
+ const required = effectiveApprovals.require_network === true;
26
+ return {
27
+ required,
28
+ reason: "Network access requires explicit approval",
29
+ };
30
+ }
31
+ case "force_action": {
32
+ return {
33
+ required: effectiveApprovals.require_force === true,
34
+ reason: "Force action requires explicit approval",
35
+ };
36
+ }
37
+ case "policy_write": {
38
+ return { required: false, reason: "Policy writes require explicit approval" };
39
+ }
40
+ case "config_write": {
41
+ return { required: false, reason: "Config writes require explicit approval" };
42
+ }
43
+ case "dangerous_fs": {
44
+ return { required: false, reason: "Potentially dangerous file operations require approval" };
45
+ }
46
+ case "git_push": {
47
+ return { required: false, reason: "Git push requires explicit approval" };
48
+ }
49
+ default: {
50
+ const exhaustive = opts.action;
51
+ return exhaustive;
52
+ }
53
+ }
54
+ }
55
+ export async function ensureActionApproved(opts) {
56
+ const req = getApprovalRequirements({ config: opts.config, action: opts.action });
57
+ if (!req.required)
58
+ return;
59
+ if (opts.yes)
60
+ return;
61
+ const interactive = opts.interactive ?? Boolean(process.stdin.isTTY);
62
+ if (!interactive) {
63
+ throw new CliError({
64
+ exitCode: 3,
65
+ code: "E_VALIDATION",
66
+ message: `${req.reason} (pass --yes): ${opts.reason}`,
67
+ });
68
+ }
69
+ const approved = await promptYesNo(`Allow ${opts.action}? ${opts.reason}`, false);
70
+ if (!approved) {
71
+ throw new CliError({
72
+ exitCode: 3,
73
+ code: "E_VALIDATION",
74
+ message: `${req.reason} denied: ${opts.reason}`,
75
+ });
76
+ }
77
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"network-approval.d.ts","sourceRoot":"","sources":["../../../src/commands/shared/network-approval.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAK5D,wBAAsB,qBAAqB,CAAC,IAAI,EAAE;IAChD,MAAM,EAAE,gBAAgB,CAAC;IACzB,GAAG,EAAE,OAAO,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB,GAAG,OAAO,CAAC,IAAI,CAAC,CAsBhB"}
1
+ {"version":3,"file":"network-approval.d.ts","sourceRoot":"","sources":["../../../src/commands/shared/network-approval.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAI5D,wBAAsB,qBAAqB,CAAC,IAAI,EAAE;IAChD,MAAM,EAAE,gBAAgB,CAAC;IACzB,GAAG,EAAE,OAAO,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB,GAAG,OAAO,CAAC,IAAI,CAAC,CAQhB"}
@@ -1,25 +1,10 @@
1
- import { promptYesNo } from "../../cli/prompts.js";
2
- import { CliError } from "../../shared/errors.js";
1
+ import { ensureActionApproved } from "./approval-requirements.js";
3
2
  export async function ensureNetworkApproved(opts) {
4
- const requireNetwork = opts.config.agents?.approvals.require_network === true;
5
- if (!requireNetwork)
6
- return;
7
- if (opts.yes)
8
- return;
9
- const interactive = opts.interactive ?? Boolean(process.stdin.isTTY);
10
- if (!interactive) {
11
- throw new CliError({
12
- exitCode: 3,
13
- code: "E_VALIDATION",
14
- message: `Network access requires explicit approval (pass --yes): ${opts.reason}`,
15
- });
16
- }
17
- const approved = await promptYesNo(`Allow network access? ${opts.reason}`, false);
18
- if (!approved) {
19
- throw new CliError({
20
- exitCode: 3,
21
- code: "E_VALIDATION",
22
- message: `Network access denied: ${opts.reason}`,
23
- });
24
- }
3
+ await ensureActionApproved({
4
+ action: "network_access",
5
+ config: opts.config,
6
+ yes: opts.yes,
7
+ reason: opts.reason,
8
+ interactive: opts.interactive,
9
+ });
25
10
  }
@@ -1 +1 @@
1
- {"version":3,"file":"start.run.d.ts","sourceRoot":"","sources":["../../src/commands/start.run.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AACtD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAG/D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAEnD,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,cAAc,CAAC,IACpE,KAAK,UAAU,EAAE,GAAG,WAAW,KAAG,OAAO,CAAC,MAAM,CAAC,CAmBhE"}
1
+ {"version":3,"file":"start.run.d.ts","sourceRoot":"","sources":["../../src/commands/start.run.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AACtD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAG/D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAEnD,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,cAAc,CAAC,IACpE,KAAK,UAAU,EAAE,GAAG,WAAW,KAAG,OAAO,CAAC,MAAM,CAAC,CAoBhE"}
@@ -16,6 +16,7 @@ export function makeRunStartHandler(getCtx) {
16
16
  commitRequireClean: p.commitRequireClean,
17
17
  confirmStatusCommit: p.confirmStatusCommit,
18
18
  force: p.force,
19
+ yes: p.yes,
19
20
  quiet: p.quiet,
20
21
  });
21
22
  };
@@ -11,6 +11,7 @@ export type StartParsed = {
11
11
  commitRequireClean: boolean;
12
12
  confirmStatusCommit: boolean;
13
13
  force: boolean;
14
+ yes: boolean;
14
15
  quiet: boolean;
15
16
  };
16
17
  export declare const startSpec: CommandSpec<StartParsed>;
@@ -1 +1 @@
1
- {"version":3,"file":"start.spec.d.ts","sourceRoot":"","sources":["../../src/commands/start.spec.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAKvD,MAAM,MAAM,WAAW,GAAG;IACxB,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,KAAK,EAAE,OAAO,CAAC;CAChB,CAAC;AAEF,eAAO,MAAM,SAAS,EAAE,WAAW,CAAC,WAAW,CAmH9C,CAAC"}
1
+ {"version":3,"file":"start.spec.d.ts","sourceRoot":"","sources":["../../src/commands/start.spec.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAKvD,MAAM,MAAM,WAAW,GAAG;IACxB,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,EAAE,OAAO,CAAC;IACb,KAAK,EAAE,OAAO,CAAC;CAChB,CAAC;AAEF,eAAO,MAAM,SAAS,EAAE,WAAW,CAAC,WAAW,CA0H9C,CAAC"}