agentplane 0.2.16 → 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.
- package/assets/AGENTS.md +14 -3
- package/dist/backends/task-backend/redmine/env.d.ts +16 -0
- package/dist/backends/task-backend/redmine/env.d.ts.map +1 -0
- package/dist/backends/task-backend/redmine/env.js +61 -0
- package/dist/backends/task-backend/redmine/mapping.d.ts.map +1 -1
- package/dist/backends/task-backend/redmine/mapping.js +25 -3
- package/dist/backends/task-backend/redmine-backend.d.ts +1 -1
- package/dist/backends/task-backend/redmine-backend.d.ts.map +1 -1
- package/dist/backends/task-backend/redmine-backend.js +31 -21
- package/dist/backends/task-backend/shared/errors.d.ts +2 -1
- package/dist/backends/task-backend/shared/errors.d.ts.map +1 -1
- package/dist/backends/task-backend/shared/errors.js +6 -2
- package/dist/cli/run-cli/commands/init/ui.d.ts.map +1 -1
- package/dist/cli/run-cli/commands/init/ui.js +5 -3
- package/dist/cli/run-cli/commands/init/write-env.d.ts.map +1 -1
- package/dist/cli/run-cli/commands/init/write-env.js +14 -6
- package/dist/cli/run-cli/commands/init.d.ts +1 -1
- package/dist/cli/run-cli/commands/init.d.ts.map +1 -1
- package/dist/cli/run-cli/commands/init.js +7 -68
- package/dist/cli/run-cli.d.ts.map +1 -1
- package/dist/cli/run-cli.js +5 -1
- package/dist/cli/run-cli.test-helpers.d.ts.map +1 -1
- package/dist/cli/run-cli.test-helpers.js +14 -2
- package/dist/cli/shared/ansi.d.ts +3 -0
- package/dist/cli/shared/ansi.d.ts.map +1 -0
- package/dist/cli/shared/ansi.js +19 -0
- package/dist/commands/block.run.d.ts.map +1 -1
- package/dist/commands/block.run.js +1 -0
- package/dist/commands/block.spec.d.ts +1 -0
- package/dist/commands/block.spec.d.ts.map +1 -1
- package/dist/commands/block.spec.js +7 -0
- package/dist/commands/branch/remove.command.d.ts +1 -0
- package/dist/commands/branch/remove.command.d.ts.map +1 -1
- package/dist/commands/branch/remove.command.js +8 -0
- package/dist/commands/branch/remove.d.ts +1 -0
- package/dist/commands/branch/remove.d.ts.map +1 -1
- package/dist/commands/branch/remove.js +9 -0
- package/dist/commands/doctor.run.d.ts.map +1 -1
- package/dist/commands/doctor.run.js +67 -0
- package/dist/commands/finish.run.d.ts.map +1 -1
- package/dist/commands/finish.run.js +1 -0
- package/dist/commands/finish.spec.d.ts +1 -0
- package/dist/commands/finish.spec.d.ts.map +1 -1
- package/dist/commands/finish.spec.js +7 -0
- package/dist/commands/guard/impl/commands.d.ts.map +1 -1
- package/dist/commands/guard/impl/commands.js +50 -0
- package/dist/commands/guard/impl/comment-commit.js +1 -1
- package/dist/commands/release/apply.command.d.ts.map +1 -1
- package/dist/commands/release/apply.command.js +107 -16
- package/dist/commands/shared/approval-requirements.d.ts +18 -0
- package/dist/commands/shared/approval-requirements.d.ts.map +1 -0
- package/dist/commands/shared/approval-requirements.js +77 -0
- package/dist/commands/shared/network-approval.d.ts.map +1 -1
- package/dist/commands/shared/network-approval.js +8 -23
- package/dist/commands/start.run.d.ts.map +1 -1
- package/dist/commands/start.run.js +1 -0
- package/dist/commands/start.spec.d.ts +1 -0
- package/dist/commands/start.spec.d.ts.map +1 -1
- package/dist/commands/start.spec.js +7 -0
- package/dist/commands/task/block.d.ts +1 -0
- package/dist/commands/task/block.d.ts.map +1 -1
- package/dist/commands/task/block.js +9 -0
- package/dist/commands/task/finish.d.ts +1 -0
- package/dist/commands/task/finish.d.ts.map +1 -1
- package/dist/commands/task/finish.js +9 -0
- package/dist/commands/task/migrate.command.d.ts +1 -0
- package/dist/commands/task/migrate.command.d.ts.map +1 -1
- package/dist/commands/task/migrate.command.js +8 -0
- package/dist/commands/task/migrate.d.ts +1 -0
- package/dist/commands/task/migrate.d.ts.map +1 -1
- package/dist/commands/task/migrate.js +9 -3
- package/dist/commands/task/normalize.command.d.ts +1 -0
- package/dist/commands/task/normalize.command.d.ts.map +1 -1
- package/dist/commands/task/normalize.command.js +8 -0
- package/dist/commands/task/normalize.d.ts +1 -0
- package/dist/commands/task/normalize.d.ts.map +1 -1
- package/dist/commands/task/normalize.js +9 -3
- package/dist/commands/task/scaffold.command.d.ts +1 -0
- package/dist/commands/task/scaffold.command.d.ts.map +1 -1
- package/dist/commands/task/scaffold.command.js +8 -0
- package/dist/commands/task/scaffold.d.ts +1 -0
- package/dist/commands/task/scaffold.d.ts.map +1 -1
- package/dist/commands/task/scaffold.js +9 -0
- package/dist/commands/task/set-status.command.d.ts +1 -0
- package/dist/commands/task/set-status.command.d.ts.map +1 -1
- package/dist/commands/task/set-status.command.js +8 -0
- package/dist/commands/task/set-status.d.ts +1 -0
- package/dist/commands/task/set-status.d.ts.map +1 -1
- package/dist/commands/task/set-status.js +7 -8
- package/dist/commands/task/start.d.ts +1 -0
- package/dist/commands/task/start.d.ts.map +1 -1
- package/dist/commands/task/start.js +9 -0
- 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,
|
|
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"}
|
|
@@ -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,
|
|
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
|
};
|
|
@@ -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,
|
|
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
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"remove.d.ts","sourceRoot":"","sources":["../../../src/commands/branch/remove.ts"],"names":[],"mappings":"
|
|
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;
|
|
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,
|
|
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"}
|
|
@@ -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,
|
|
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;
|
|
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
|
|
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;
|
|
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,11 +1,12 @@
|
|
|
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
|
-
import { resolveProject } from "@agentplaneorg/core";
|
|
3
|
+
import { resolveProject, loadConfig } from "@agentplaneorg/core";
|
|
4
4
|
import { usageError } from "../../cli/spec/errors.js";
|
|
5
5
|
import { exitCodeForError } from "../../cli/exit-codes.js";
|
|
6
6
|
import { CliError } from "../../shared/errors.js";
|
|
7
7
|
import { execFileAsync, gitEnv } from "../shared/git.js";
|
|
8
8
|
import { GitContext } from "../shared/git-context.js";
|
|
9
|
+
import { ensureNetworkApproved } from "../shared/network-approval.js";
|
|
9
10
|
async function fileExists(p) {
|
|
10
11
|
try {
|
|
11
12
|
await readFile(p, "utf8");
|
|
@@ -154,6 +155,74 @@ async function maybeUpdateBunLockfile(gitRoot) {
|
|
|
154
155
|
});
|
|
155
156
|
}
|
|
156
157
|
}
|
|
158
|
+
async function ensureCleanTrackedTree(gitRoot) {
|
|
159
|
+
const { stdout } = await execFileAsync("git", ["status", "--short", "--untracked-files=no"], {
|
|
160
|
+
cwd: gitRoot,
|
|
161
|
+
env: gitEnv(),
|
|
162
|
+
maxBuffer: 10 * 1024 * 1024,
|
|
163
|
+
});
|
|
164
|
+
const dirty = String(stdout ?? "")
|
|
165
|
+
.split(/\r?\n/u)
|
|
166
|
+
.map((line) => line.trimEnd())
|
|
167
|
+
.filter((line) => line.length > 0);
|
|
168
|
+
if (dirty.length === 0)
|
|
169
|
+
return;
|
|
170
|
+
throw new CliError({
|
|
171
|
+
exitCode: exitCodeForError("E_GIT"),
|
|
172
|
+
code: "E_GIT",
|
|
173
|
+
message: "Release apply requires a clean tracked working tree.\n" +
|
|
174
|
+
`Found tracked changes:\n${dirty.map((line) => ` ${line}`).join("\n")}`,
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
async function ensureTagDoesNotExist(gitRoot, tag) {
|
|
178
|
+
try {
|
|
179
|
+
await execFileAsync("git", ["rev-parse", "-q", "--verify", `refs/tags/${tag}`], {
|
|
180
|
+
cwd: gitRoot,
|
|
181
|
+
env: gitEnv(),
|
|
182
|
+
});
|
|
183
|
+
throw new CliError({
|
|
184
|
+
exitCode: exitCodeForError("E_GIT"),
|
|
185
|
+
code: "E_GIT",
|
|
186
|
+
message: `Tag already exists: ${tag}`,
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
catch (err) {
|
|
190
|
+
const code = err?.code;
|
|
191
|
+
if (code !== 1)
|
|
192
|
+
throw err;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
async function ensureNpmVersionsAvailable(gitRoot, version) {
|
|
196
|
+
const scriptPath = path.join(gitRoot, "scripts", "check-npm-version-availability.mjs");
|
|
197
|
+
try {
|
|
198
|
+
await execFileAsync("node", [scriptPath, "--version", version], {
|
|
199
|
+
cwd: gitRoot,
|
|
200
|
+
env: process.env,
|
|
201
|
+
maxBuffer: 10 * 1024 * 1024,
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
catch (err) {
|
|
205
|
+
const details = String(err?.stderr ?? "").trim();
|
|
206
|
+
throw new CliError({
|
|
207
|
+
exitCode: exitCodeForError("E_VALIDATION"),
|
|
208
|
+
code: "E_VALIDATION",
|
|
209
|
+
message: `Pre-publish npm check failed for version ${version}. ` +
|
|
210
|
+
"Ensure this version is not already published for @agentplaneorg/core and agentplane." +
|
|
211
|
+
(details ? `\n\n${details}` : ""),
|
|
212
|
+
});
|
|
213
|
+
}
|
|
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
|
+
}
|
|
157
226
|
export const releaseApplySpec = {
|
|
158
227
|
id: ["release", "apply"],
|
|
159
228
|
group: "Release",
|
|
@@ -276,6 +345,21 @@ export const runReleaseApply = async (ctx, flags) => {
|
|
|
276
345
|
});
|
|
277
346
|
}
|
|
278
347
|
const git = new GitContext({ gitRoot });
|
|
348
|
+
await ensureCleanTrackedTree(gitRoot);
|
|
349
|
+
await ensureTagDoesNotExist(gitRoot, plan.nextTag);
|
|
350
|
+
let npmVersionChecked = false;
|
|
351
|
+
if (flags.push) {
|
|
352
|
+
const loaded = await loadConfig(resolved.agentplaneDir);
|
|
353
|
+
await ensureNetworkApproved({
|
|
354
|
+
config: loaded.config,
|
|
355
|
+
yes: flags.yes,
|
|
356
|
+
reason: "release apply --push validates npm version availability and pushes over network",
|
|
357
|
+
interactive: Boolean(process.stdin.isTTY),
|
|
358
|
+
});
|
|
359
|
+
await ensureNpmVersionsAvailable(gitRoot, plan.nextVersion);
|
|
360
|
+
npmVersionChecked = true;
|
|
361
|
+
}
|
|
362
|
+
let releaseCommit = null;
|
|
279
363
|
if (coreVersion === plan.prevVersion) {
|
|
280
364
|
await Promise.all([
|
|
281
365
|
replacePackageVersionInFile(corePkgPath, plan.nextVersion),
|
|
@@ -308,23 +392,11 @@ export const runReleaseApply = async (ctx, flags) => {
|
|
|
308
392
|
else {
|
|
309
393
|
const subject = `✨ release: ${plan.nextTag}`;
|
|
310
394
|
await git.commit({ message: subject, env: cleanHookEnv() });
|
|
311
|
-
|
|
312
|
-
// Create tag (idempotency: refuse to overwrite).
|
|
313
|
-
try {
|
|
314
|
-
await execFileAsync("git", ["rev-parse", "-q", "--verify", `refs/tags/${plan.nextTag}`], {
|
|
395
|
+
const { stdout: headHash } = await execFileAsync("git", ["rev-parse", "HEAD"], {
|
|
315
396
|
cwd: gitRoot,
|
|
316
397
|
env: gitEnv(),
|
|
317
398
|
});
|
|
318
|
-
|
|
319
|
-
exitCode: exitCodeForError("E_GIT"),
|
|
320
|
-
code: "E_GIT",
|
|
321
|
-
message: `Tag already exists: ${plan.nextTag}`,
|
|
322
|
-
});
|
|
323
|
-
}
|
|
324
|
-
catch (err) {
|
|
325
|
-
const code = err?.code;
|
|
326
|
-
if (code !== 1)
|
|
327
|
-
throw err;
|
|
399
|
+
releaseCommit = { hash: String(headHash ?? "").trim(), subject };
|
|
328
400
|
}
|
|
329
401
|
await execFileAsync("git", ["tag", plan.nextTag], { cwd: gitRoot, env: gitEnv() });
|
|
330
402
|
process.stdout.write(`Release tag created: ${plan.nextTag}\n`);
|
|
@@ -339,5 +411,24 @@ export const runReleaseApply = async (ctx, flags) => {
|
|
|
339
411
|
else {
|
|
340
412
|
process.stdout.write(`Next: git push <remote> HEAD && git push <remote> ${plan.nextTag}\n`);
|
|
341
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`);
|
|
342
433
|
return 0;
|
|
343
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;
|
|
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"}
|