agentplane 0.1.6 → 0.1.7
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 +1 -1
- package/assets/agents/ORCHESTRATOR.json +1 -1
- package/assets/agents/UPGRADER.json +1 -1
- package/dist/cli/run-cli.d.ts.map +1 -1
- package/dist/cli/run-cli.js +22 -7
- package/dist/commands/branch/index.d.ts +60 -0
- package/dist/commands/branch/index.d.ts.map +1 -0
- package/dist/commands/branch/index.js +511 -0
- package/dist/commands/guard/index.d.ts +67 -0
- package/dist/commands/guard/index.d.ts.map +1 -0
- package/dist/commands/guard/index.js +367 -0
- package/dist/commands/hooks/index.d.ts +18 -0
- package/dist/commands/hooks/index.d.ts.map +1 -0
- package/dist/commands/hooks/index.js +290 -0
- package/dist/commands/pr/index.d.ts +46 -0
- package/dist/commands/pr/index.d.ts.map +1 -0
- package/dist/commands/pr/index.js +854 -0
- package/dist/commands/shared/git-diff.d.ts +9 -0
- package/dist/commands/shared/git-diff.d.ts.map +1 -0
- package/dist/commands/shared/git-diff.js +41 -0
- package/dist/commands/shared/git-ops.d.ts +24 -0
- package/dist/commands/shared/git-ops.d.ts.map +1 -0
- package/dist/commands/shared/git-ops.js +181 -0
- package/dist/commands/shared/git-worktree.d.ts +8 -0
- package/dist/commands/shared/git-worktree.d.ts.map +1 -0
- package/dist/commands/shared/git-worktree.js +48 -0
- package/dist/commands/shared/git.d.ts +4 -0
- package/dist/commands/shared/git.d.ts.map +1 -0
- package/dist/commands/shared/git.js +14 -0
- package/dist/commands/shared/path.d.ts +3 -0
- package/dist/commands/shared/path.d.ts.map +1 -0
- package/dist/commands/shared/path.js +14 -0
- package/dist/commands/shared/pr-meta.d.ts +21 -0
- package/dist/commands/shared/pr-meta.d.ts.map +1 -0
- package/dist/commands/shared/pr-meta.js +72 -0
- package/dist/commands/shared/task-backend.d.ts +15 -0
- package/dist/commands/shared/task-backend.d.ts.map +1 -0
- package/dist/commands/shared/task-backend.js +55 -0
- package/dist/commands/task/add.d.ts +8 -0
- package/dist/commands/task/add.d.ts.map +1 -0
- package/dist/commands/task/add.js +164 -0
- package/dist/commands/task/block.d.ts +19 -0
- package/dist/commands/task/block.d.ts.map +1 -0
- package/dist/commands/task/block.js +86 -0
- package/dist/commands/task/comment.d.ts +8 -0
- package/dist/commands/task/comment.d.ts.map +1 -0
- package/dist/commands/task/comment.js +29 -0
- package/dist/commands/task/doc.d.ts +17 -0
- package/dist/commands/task/doc.d.ts.map +1 -0
- package/dist/commands/task/doc.js +220 -0
- package/dist/commands/task/export.d.ts +5 -0
- package/dist/commands/task/export.d.ts.map +1 -0
- package/dist/commands/task/export.js +27 -0
- package/dist/commands/task/finish.d.ts +27 -0
- package/dist/commands/task/finish.d.ts.map +1 -0
- package/dist/commands/task/finish.js +131 -0
- package/dist/commands/task/index.d.ts +23 -0
- package/dist/commands/task/index.d.ts.map +1 -0
- package/dist/commands/task/index.js +22 -0
- package/dist/commands/task/lint.d.ts +5 -0
- package/dist/commands/task/lint.d.ts.map +1 -0
- package/dist/commands/task/lint.js +22 -0
- package/dist/commands/task/list.d.ts +11 -0
- package/dist/commands/task/list.d.ts.map +1 -0
- package/dist/commands/task/list.js +54 -0
- package/dist/commands/task/migrate.d.ts +6 -0
- package/dist/commands/task/migrate.d.ts.map +1 -0
- package/dist/commands/task/migrate.js +70 -0
- package/dist/commands/task/new.d.ts +8 -0
- package/dist/commands/task/new.d.ts.map +1 -0
- package/dist/commands/task/new.js +117 -0
- package/dist/commands/task/next.d.ts +6 -0
- package/dist/commands/task/next.d.ts.map +1 -0
- package/dist/commands/task/next.js +45 -0
- package/dist/commands/task/normalize.d.ts +6 -0
- package/dist/commands/task/normalize.d.ts.map +1 -0
- package/dist/commands/task/normalize.js +46 -0
- package/dist/commands/task/ready.d.ts +6 -0
- package/dist/commands/task/ready.d.ts.map +1 -0
- package/dist/commands/task/ready.js +57 -0
- package/dist/commands/task/scaffold.d.ts +8 -0
- package/dist/commands/task/scaffold.d.ts.map +1 -0
- package/dist/commands/task/scaffold.js +131 -0
- package/dist/commands/task/scrub.d.ts +8 -0
- package/dist/commands/task/scrub.d.ts.map +1 -0
- package/dist/commands/task/scrub.js +121 -0
- package/dist/commands/task/search.d.ts +7 -0
- package/dist/commands/task/search.d.ts.map +1 -0
- package/dist/commands/task/search.js +79 -0
- package/dist/commands/task/set-status.d.ts +19 -0
- package/dist/commands/task/set-status.d.ts.map +1 -0
- package/dist/commands/task/set-status.js +123 -0
- package/dist/commands/task/shared.d.ts +46 -0
- package/dist/commands/task/shared.d.ts.map +1 -0
- package/dist/commands/task/shared.js +283 -0
- package/dist/commands/task/show.d.ts +6 -0
- package/dist/commands/task/show.d.ts.map +1 -0
- package/dist/commands/task/show.js +35 -0
- package/dist/commands/task/start.d.ts +19 -0
- package/dist/commands/task/start.d.ts.map +1 -0
- package/dist/commands/task/start.js +109 -0
- package/dist/commands/task/update.d.ts +8 -0
- package/dist/commands/task/update.d.ts.map +1 -0
- package/dist/commands/task/update.js +144 -0
- package/dist/commands/task/verify.d.ts +14 -0
- package/dist/commands/task/verify.d.ts.map +1 -0
- package/dist/commands/task/verify.js +362 -0
- package/dist/commands/workflow.d.ts +5 -364
- package/dist/commands/workflow.d.ts.map +1 -1
- package/dist/commands/workflow.js +6 -4617
- package/package.json +2 -2
|
@@ -0,0 +1,511 @@
|
|
|
1
|
+
import { mkdir, rename } from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { clearPinnedBaseBranch, getBaseBranch, getPinnedBaseBranch, loadConfig, resolveBaseBranch, resolveProject, setPinnedBaseBranch, } from "@agentplaneorg/core";
|
|
4
|
+
import { loadTaskBackend } from "../../backends/task-backend.js";
|
|
5
|
+
import { mapBackendError, mapCoreError } from "../../cli/error-map.js";
|
|
6
|
+
import { fileExists } from "../../cli/fs-utils.js";
|
|
7
|
+
import { successMessage, unknownEntityMessage, usageMessage, workflowModeMessage, } from "../../cli/output.js";
|
|
8
|
+
import { CliError } from "../../shared/errors.js";
|
|
9
|
+
import { ensureGitClean } from "../guard/index.js";
|
|
10
|
+
import { execFileAsync, gitEnv } from "../shared/git.js";
|
|
11
|
+
import { gitAheadBehind, gitDiffNames } from "../shared/git-diff.js";
|
|
12
|
+
import { gitBranchExists, gitCurrentBranch } from "../shared/git-ops.js";
|
|
13
|
+
import { findWorktreeForBranch, gitListTaskBranches, parseTaskIdFromBranch, } from "../shared/git-worktree.js";
|
|
14
|
+
import { isPathWithin, resolvePathFallback } from "../shared/path.js";
|
|
15
|
+
import { loadBackendTask } from "../shared/task-backend.js";
|
|
16
|
+
export { gitInitRepo, resolveInitBaseBranch, promptInitBaseBranch, ensureInitCommit, } from "../shared/git-ops.js";
|
|
17
|
+
export const BRANCH_BASE_USAGE = "Usage: agentplane branch base get|set|clear|explain [<name>|--current]";
|
|
18
|
+
export const BRANCH_BASE_USAGE_EXAMPLE = "agentplane branch base set --current";
|
|
19
|
+
export const BRANCH_STATUS_USAGE = "Usage: agentplane branch status [--branch <name>] [--base <name>]";
|
|
20
|
+
export const BRANCH_STATUS_USAGE_EXAMPLE = "agentplane branch status --base main";
|
|
21
|
+
export const BRANCH_REMOVE_USAGE = "Usage: agentplane branch remove [--branch <name>] [--worktree <path>] [--force] [--quiet]";
|
|
22
|
+
export const BRANCH_REMOVE_USAGE_EXAMPLE = "agentplane branch remove --branch task/20260203-F1Q8AB --worktree .agentplane/worktrees/task";
|
|
23
|
+
export const WORK_START_USAGE = "Usage: agentplane work start <task-id> --agent <id> --slug <slug> [--worktree]";
|
|
24
|
+
export const WORK_START_USAGE_EXAMPLE = "agentplane work start 202602030608-F1Q8AB --agent CODER --slug cli --worktree";
|
|
25
|
+
export const CLEANUP_MERGED_USAGE = "Usage: agentplane cleanup merged [--base <name>] [--yes] [--archive] [--quiet]";
|
|
26
|
+
export const CLEANUP_MERGED_USAGE_EXAMPLE = "agentplane cleanup merged --yes";
|
|
27
|
+
function validateWorkSlug(slug) {
|
|
28
|
+
const trimmed = slug.trim();
|
|
29
|
+
if (!trimmed) {
|
|
30
|
+
throw new CliError({
|
|
31
|
+
exitCode: 2,
|
|
32
|
+
code: "E_USAGE",
|
|
33
|
+
message: usageMessage(WORK_START_USAGE, WORK_START_USAGE_EXAMPLE),
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
if (!/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(trimmed)) {
|
|
37
|
+
throw new CliError({
|
|
38
|
+
exitCode: 2,
|
|
39
|
+
code: "E_USAGE",
|
|
40
|
+
message: "Slug must be lowercase kebab-case (a-z, 0-9, hyphen)",
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
function validateWorkAgent(agent) {
|
|
45
|
+
const trimmed = agent.trim();
|
|
46
|
+
if (!trimmed) {
|
|
47
|
+
throw new CliError({
|
|
48
|
+
exitCode: 2,
|
|
49
|
+
code: "E_USAGE",
|
|
50
|
+
message: usageMessage(WORK_START_USAGE, WORK_START_USAGE_EXAMPLE),
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
if (!/^[A-Z0-9_]+$/.test(trimmed)) {
|
|
54
|
+
throw new CliError({
|
|
55
|
+
exitCode: 2,
|
|
56
|
+
code: "E_USAGE",
|
|
57
|
+
message: "Agent id must be uppercase letters, numbers, or underscores",
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
async function archivePrArtifacts(taskDir) {
|
|
62
|
+
const prDir = path.join(taskDir, "pr");
|
|
63
|
+
if (!(await fileExists(prDir)))
|
|
64
|
+
return null;
|
|
65
|
+
const archiveRoot = path.join(taskDir, "pr-archive");
|
|
66
|
+
await mkdir(archiveRoot, { recursive: true });
|
|
67
|
+
const stamp = new Date().toISOString().replaceAll(/[:.]/g, "");
|
|
68
|
+
let dest = path.join(archiveRoot, stamp);
|
|
69
|
+
if (await fileExists(dest)) {
|
|
70
|
+
dest = path.join(archiveRoot, `${stamp}-${Math.random().toString(36).slice(2, 8)}`);
|
|
71
|
+
}
|
|
72
|
+
await rename(prDir, dest);
|
|
73
|
+
return dest;
|
|
74
|
+
}
|
|
75
|
+
export async function cmdWorkStart(opts) {
|
|
76
|
+
try {
|
|
77
|
+
validateWorkAgent(opts.agent);
|
|
78
|
+
validateWorkSlug(opts.slug);
|
|
79
|
+
const resolved = await resolveProject({
|
|
80
|
+
cwd: opts.cwd,
|
|
81
|
+
rootOverride: opts.rootOverride ?? null,
|
|
82
|
+
});
|
|
83
|
+
const loaded = await loadConfig(resolved.agentplaneDir);
|
|
84
|
+
const mode = loaded.config.workflow_mode;
|
|
85
|
+
if (mode === "branch_pr" && !opts.worktree) {
|
|
86
|
+
throw new CliError({
|
|
87
|
+
exitCode: 2,
|
|
88
|
+
code: "E_USAGE",
|
|
89
|
+
message: usageMessage(WORK_START_USAGE, WORK_START_USAGE_EXAMPLE),
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
await loadBackendTask({
|
|
93
|
+
cwd: opts.cwd,
|
|
94
|
+
rootOverride: opts.rootOverride,
|
|
95
|
+
taskId: opts.taskId,
|
|
96
|
+
});
|
|
97
|
+
const currentBranch = await gitCurrentBranch(resolved.gitRoot);
|
|
98
|
+
let baseRef = currentBranch;
|
|
99
|
+
if (mode === "branch_pr") {
|
|
100
|
+
const baseBranch = await resolveBaseBranch({
|
|
101
|
+
cwd: opts.cwd,
|
|
102
|
+
rootOverride: opts.rootOverride ?? null,
|
|
103
|
+
cliBaseOpt: null,
|
|
104
|
+
mode,
|
|
105
|
+
});
|
|
106
|
+
if (!baseBranch) {
|
|
107
|
+
throw new CliError({
|
|
108
|
+
exitCode: 2,
|
|
109
|
+
code: "E_USAGE",
|
|
110
|
+
message: "Base branch could not be resolved (use `agentplane branch base set`).",
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
if (currentBranch !== baseBranch) {
|
|
114
|
+
throw new CliError({
|
|
115
|
+
exitCode: 5,
|
|
116
|
+
code: "E_GIT",
|
|
117
|
+
message: `work start must be run on base branch ${baseBranch} (current: ${currentBranch})`,
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
baseRef = baseBranch;
|
|
121
|
+
}
|
|
122
|
+
const prefix = loaded.config.branch.task_prefix;
|
|
123
|
+
const branchName = `${prefix}/${opts.taskId}/${opts.slug.trim()}`;
|
|
124
|
+
const branchExists = await gitBranchExists(resolved.gitRoot, branchName);
|
|
125
|
+
let worktreePath = "";
|
|
126
|
+
if (opts.worktree) {
|
|
127
|
+
const worktreesDir = path.resolve(resolved.gitRoot, loaded.config.paths.worktrees_dir);
|
|
128
|
+
if (!isPathWithin(resolved.gitRoot, worktreesDir)) {
|
|
129
|
+
throw new CliError({
|
|
130
|
+
exitCode: 5,
|
|
131
|
+
code: "E_GIT",
|
|
132
|
+
message: `worktrees_dir must be inside the repo: ${worktreesDir}`,
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
worktreePath = path.join(worktreesDir, `${opts.taskId}-${opts.slug.trim()}`);
|
|
136
|
+
if (await fileExists(worktreePath)) {
|
|
137
|
+
throw new CliError({
|
|
138
|
+
exitCode: 5,
|
|
139
|
+
code: "E_GIT",
|
|
140
|
+
message: `Worktree path already exists: ${worktreePath}`,
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
await mkdir(worktreesDir, { recursive: true });
|
|
144
|
+
const worktreeArgs = branchExists
|
|
145
|
+
? ["worktree", "add", worktreePath, branchName]
|
|
146
|
+
: ["worktree", "add", "-b", branchName, worktreePath, baseRef];
|
|
147
|
+
await execFileAsync("git", worktreeArgs, { cwd: resolved.gitRoot, env: gitEnv() });
|
|
148
|
+
}
|
|
149
|
+
else {
|
|
150
|
+
if (branchExists) {
|
|
151
|
+
if (currentBranch !== branchName) {
|
|
152
|
+
await execFileAsync("git", ["checkout", "-q", branchName], {
|
|
153
|
+
cwd: resolved.gitRoot,
|
|
154
|
+
env: gitEnv(),
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
else {
|
|
159
|
+
await execFileAsync("git", ["checkout", "-q", "-b", branchName, baseRef], {
|
|
160
|
+
cwd: resolved.gitRoot,
|
|
161
|
+
env: gitEnv(),
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
process.stdout.write(`${successMessage("work start", branchName, opts.worktree ? `worktree=${path.relative(resolved.gitRoot, worktreePath)}` : "")}\n`);
|
|
166
|
+
return 0;
|
|
167
|
+
}
|
|
168
|
+
catch (err) {
|
|
169
|
+
if (err instanceof CliError)
|
|
170
|
+
throw err;
|
|
171
|
+
throw mapBackendError(err, { command: "work start", root: opts.rootOverride ?? null });
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
export async function cmdCleanupMerged(opts) {
|
|
175
|
+
try {
|
|
176
|
+
const resolved = await resolveProject({
|
|
177
|
+
cwd: opts.cwd,
|
|
178
|
+
rootOverride: opts.rootOverride ?? null,
|
|
179
|
+
});
|
|
180
|
+
const loaded = await loadConfig(resolved.agentplaneDir);
|
|
181
|
+
if (loaded.config.workflow_mode !== "branch_pr") {
|
|
182
|
+
throw new CliError({
|
|
183
|
+
exitCode: 2,
|
|
184
|
+
code: "E_USAGE",
|
|
185
|
+
message: workflowModeMessage(loaded.config.workflow_mode, "branch_pr"),
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
await ensureGitClean({ cwd: opts.cwd, rootOverride: opts.rootOverride });
|
|
189
|
+
const baseBranch = await resolveBaseBranch({
|
|
190
|
+
cwd: opts.cwd,
|
|
191
|
+
rootOverride: opts.rootOverride ?? null,
|
|
192
|
+
cliBaseOpt: opts.base ?? null,
|
|
193
|
+
mode: loaded.config.workflow_mode,
|
|
194
|
+
});
|
|
195
|
+
if (!baseBranch) {
|
|
196
|
+
throw new CliError({
|
|
197
|
+
exitCode: 2,
|
|
198
|
+
code: "E_USAGE",
|
|
199
|
+
message: usageMessage(CLEANUP_MERGED_USAGE, CLEANUP_MERGED_USAGE_EXAMPLE),
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
if (!(await gitBranchExists(resolved.gitRoot, baseBranch))) {
|
|
203
|
+
throw new CliError({
|
|
204
|
+
exitCode: 5,
|
|
205
|
+
code: "E_GIT",
|
|
206
|
+
message: unknownEntityMessage("base branch", baseBranch),
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
const currentBranch = await gitCurrentBranch(resolved.gitRoot);
|
|
210
|
+
if (currentBranch !== baseBranch) {
|
|
211
|
+
throw new CliError({
|
|
212
|
+
exitCode: 5,
|
|
213
|
+
code: "E_GIT",
|
|
214
|
+
message: `cleanup merged must run on base branch ${baseBranch} (current: ${currentBranch})`,
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
const repoRoot = await resolvePathFallback(resolved.gitRoot);
|
|
218
|
+
const { backend } = await loadTaskBackend({
|
|
219
|
+
cwd: opts.cwd,
|
|
220
|
+
rootOverride: opts.rootOverride ?? null,
|
|
221
|
+
});
|
|
222
|
+
const tasks = await backend.listTasks();
|
|
223
|
+
const tasksById = new Map(tasks.map((task) => [task.id, task]));
|
|
224
|
+
const prefix = loaded.config.branch.task_prefix;
|
|
225
|
+
const branches = await gitListTaskBranches(resolved.gitRoot, prefix);
|
|
226
|
+
const candidates = [];
|
|
227
|
+
for (const branch of branches) {
|
|
228
|
+
if (branch === baseBranch)
|
|
229
|
+
continue;
|
|
230
|
+
const taskId = parseTaskIdFromBranch(prefix, branch);
|
|
231
|
+
if (!taskId)
|
|
232
|
+
continue;
|
|
233
|
+
const task = tasksById.get(taskId);
|
|
234
|
+
if (!task)
|
|
235
|
+
continue;
|
|
236
|
+
const status = String(task.status || "").toUpperCase();
|
|
237
|
+
if (status !== "DONE")
|
|
238
|
+
continue;
|
|
239
|
+
const diff = await gitDiffNames(resolved.gitRoot, baseBranch, branch);
|
|
240
|
+
if (diff.length > 0)
|
|
241
|
+
continue;
|
|
242
|
+
const worktreePath = await findWorktreeForBranch(resolved.gitRoot, branch);
|
|
243
|
+
candidates.push({ taskId, branch, worktreePath });
|
|
244
|
+
}
|
|
245
|
+
const sortedCandidates = candidates.toSorted((a, b) => a.taskId.localeCompare(b.taskId));
|
|
246
|
+
if (!opts.quiet) {
|
|
247
|
+
const archiveLabel = opts.archive ? " archive=on" : "";
|
|
248
|
+
process.stdout.write(`cleanup merged (base=${baseBranch}${archiveLabel})\n`);
|
|
249
|
+
if (sortedCandidates.length === 0) {
|
|
250
|
+
process.stdout.write("no candidates\n");
|
|
251
|
+
return 0;
|
|
252
|
+
}
|
|
253
|
+
for (const item of sortedCandidates) {
|
|
254
|
+
process.stdout.write(`- ${item.taskId}: branch=${item.branch} worktree=${item.worktreePath ?? "-"}\n`);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
if (!opts.yes) {
|
|
258
|
+
if (!opts.quiet) {
|
|
259
|
+
process.stdout.write("Re-run with --yes to delete these branches/worktrees.\n");
|
|
260
|
+
}
|
|
261
|
+
return 0;
|
|
262
|
+
}
|
|
263
|
+
for (const item of sortedCandidates) {
|
|
264
|
+
const worktreePath = item.worktreePath ? await resolvePathFallback(item.worktreePath) : null;
|
|
265
|
+
if (worktreePath) {
|
|
266
|
+
if (!isPathWithin(repoRoot, worktreePath)) {
|
|
267
|
+
throw new CliError({
|
|
268
|
+
exitCode: 5,
|
|
269
|
+
code: "E_GIT",
|
|
270
|
+
message: `Refusing to remove worktree outside repo: ${worktreePath}`,
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
if (worktreePath === repoRoot) {
|
|
274
|
+
throw new CliError({
|
|
275
|
+
exitCode: 5,
|
|
276
|
+
code: "E_GIT",
|
|
277
|
+
message: "Refusing to remove the current worktree",
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
if (opts.archive) {
|
|
282
|
+
const taskDir = path.join(resolved.gitRoot, loaded.config.paths.workflow_dir, item.taskId);
|
|
283
|
+
await archivePrArtifacts(taskDir);
|
|
284
|
+
}
|
|
285
|
+
if (worktreePath) {
|
|
286
|
+
await execFileAsync("git", ["worktree", "remove", "--force", worktreePath], {
|
|
287
|
+
cwd: resolved.gitRoot,
|
|
288
|
+
env: gitEnv(),
|
|
289
|
+
});
|
|
290
|
+
}
|
|
291
|
+
await execFileAsync("git", ["branch", "-D", item.branch], {
|
|
292
|
+
cwd: resolved.gitRoot,
|
|
293
|
+
env: gitEnv(),
|
|
294
|
+
});
|
|
295
|
+
}
|
|
296
|
+
if (!opts.quiet) {
|
|
297
|
+
process.stdout.write(`${successMessage("cleanup merged", undefined, `deleted=${candidates.length}`)}\n`);
|
|
298
|
+
}
|
|
299
|
+
return 0;
|
|
300
|
+
}
|
|
301
|
+
catch (err) {
|
|
302
|
+
if (err instanceof CliError)
|
|
303
|
+
throw err;
|
|
304
|
+
throw mapBackendError(err, { command: "cleanup merged", root: opts.rootOverride ?? null });
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
export async function cmdBranchBaseGet(opts) {
|
|
308
|
+
try {
|
|
309
|
+
const value = await getBaseBranch({ cwd: opts.cwd, rootOverride: opts.rootOverride ?? null });
|
|
310
|
+
process.stdout.write(`${value}\n`);
|
|
311
|
+
return 0;
|
|
312
|
+
}
|
|
313
|
+
catch (err) {
|
|
314
|
+
throw mapCoreError(err, { command: "branch base get", root: opts.rootOverride ?? null });
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
export async function cmdBranchBaseSet(opts) {
|
|
318
|
+
const trimmed = (opts.value ?? "").trim();
|
|
319
|
+
if (trimmed.length === 0 && !opts.useCurrent) {
|
|
320
|
+
throw new CliError({
|
|
321
|
+
exitCode: 2,
|
|
322
|
+
code: "E_USAGE",
|
|
323
|
+
message: usageMessage(BRANCH_BASE_USAGE, BRANCH_BASE_USAGE_EXAMPLE),
|
|
324
|
+
});
|
|
325
|
+
}
|
|
326
|
+
try {
|
|
327
|
+
let nextValue = trimmed;
|
|
328
|
+
if (opts.useCurrent) {
|
|
329
|
+
const resolved = await resolveProject({
|
|
330
|
+
cwd: opts.cwd,
|
|
331
|
+
rootOverride: opts.rootOverride ?? null,
|
|
332
|
+
});
|
|
333
|
+
nextValue = await gitCurrentBranch(resolved.gitRoot);
|
|
334
|
+
}
|
|
335
|
+
const value = await setPinnedBaseBranch({
|
|
336
|
+
cwd: opts.cwd,
|
|
337
|
+
rootOverride: opts.rootOverride ?? null,
|
|
338
|
+
value: nextValue,
|
|
339
|
+
});
|
|
340
|
+
process.stdout.write(`${value}\n`);
|
|
341
|
+
return 0;
|
|
342
|
+
}
|
|
343
|
+
catch (err) {
|
|
344
|
+
throw mapCoreError(err, { command: "branch base set", root: opts.rootOverride ?? null });
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
export async function cmdBranchBaseClear(opts) {
|
|
348
|
+
try {
|
|
349
|
+
const cleared = await clearPinnedBaseBranch({
|
|
350
|
+
cwd: opts.cwd,
|
|
351
|
+
rootOverride: opts.rootOverride ?? null,
|
|
352
|
+
});
|
|
353
|
+
process.stdout.write(`${cleared ? "cleared" : "no-op"}\n`);
|
|
354
|
+
return 0;
|
|
355
|
+
}
|
|
356
|
+
catch (err) {
|
|
357
|
+
throw mapCoreError(err, { command: "branch base clear", root: opts.rootOverride ?? null });
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
export async function cmdBranchBaseExplain(opts) {
|
|
361
|
+
try {
|
|
362
|
+
const resolved = await resolveProject({
|
|
363
|
+
cwd: opts.cwd,
|
|
364
|
+
rootOverride: opts.rootOverride ?? null,
|
|
365
|
+
});
|
|
366
|
+
const loaded = await loadConfig(resolved.agentplaneDir);
|
|
367
|
+
let current = null;
|
|
368
|
+
try {
|
|
369
|
+
current = await gitCurrentBranch(resolved.gitRoot);
|
|
370
|
+
}
|
|
371
|
+
catch {
|
|
372
|
+
current = null;
|
|
373
|
+
}
|
|
374
|
+
const pinned = await getPinnedBaseBranch({
|
|
375
|
+
cwd: opts.cwd,
|
|
376
|
+
rootOverride: opts.rootOverride ?? null,
|
|
377
|
+
});
|
|
378
|
+
const effective = await resolveBaseBranch({
|
|
379
|
+
cwd: opts.cwd,
|
|
380
|
+
rootOverride: opts.rootOverride ?? null,
|
|
381
|
+
cliBaseOpt: null,
|
|
382
|
+
mode: loaded.config.workflow_mode,
|
|
383
|
+
});
|
|
384
|
+
const warnings = [];
|
|
385
|
+
if (pinned && !(await gitBranchExists(resolved.gitRoot, pinned))) {
|
|
386
|
+
warnings.push(`Pinned base branch not found: ${pinned}`);
|
|
387
|
+
}
|
|
388
|
+
if (effective && !(await gitBranchExists(resolved.gitRoot, effective))) {
|
|
389
|
+
warnings.push(`Effective base branch not found: ${effective}`);
|
|
390
|
+
}
|
|
391
|
+
process.stdout.write(`current_branch=${current ?? "-"}\n`);
|
|
392
|
+
process.stdout.write(`pinned_base=${pinned ?? "-"}\n`);
|
|
393
|
+
process.stdout.write(`effective_base=${effective ?? "-"}\n`);
|
|
394
|
+
if (warnings.length > 0) {
|
|
395
|
+
for (const warning of warnings) {
|
|
396
|
+
process.stdout.write(`warning=${warning}\n`);
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
return 0;
|
|
400
|
+
}
|
|
401
|
+
catch (err) {
|
|
402
|
+
throw mapCoreError(err, { command: "branch base explain", root: opts.rootOverride ?? null });
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
export async function cmdBranchStatus(opts) {
|
|
406
|
+
try {
|
|
407
|
+
const resolved = await resolveProject({
|
|
408
|
+
cwd: opts.cwd,
|
|
409
|
+
rootOverride: opts.rootOverride ?? null,
|
|
410
|
+
});
|
|
411
|
+
const loaded = await loadConfig(resolved.agentplaneDir);
|
|
412
|
+
const branch = (opts.branch ?? (await gitCurrentBranch(resolved.gitRoot))).trim();
|
|
413
|
+
const base = await resolveBaseBranch({
|
|
414
|
+
cwd: opts.cwd,
|
|
415
|
+
rootOverride: opts.rootOverride ?? null,
|
|
416
|
+
cliBaseOpt: opts.base ?? null,
|
|
417
|
+
mode: loaded.config.workflow_mode,
|
|
418
|
+
});
|
|
419
|
+
if (!branch || !base) {
|
|
420
|
+
throw new CliError({
|
|
421
|
+
exitCode: 2,
|
|
422
|
+
code: "E_USAGE",
|
|
423
|
+
message: usageMessage(BRANCH_STATUS_USAGE, BRANCH_STATUS_USAGE_EXAMPLE),
|
|
424
|
+
});
|
|
425
|
+
}
|
|
426
|
+
if (!(await gitBranchExists(resolved.gitRoot, branch))) {
|
|
427
|
+
throw new CliError({
|
|
428
|
+
exitCode: 2,
|
|
429
|
+
code: "E_USAGE",
|
|
430
|
+
message: unknownEntityMessage("branch", branch),
|
|
431
|
+
});
|
|
432
|
+
}
|
|
433
|
+
if (!(await gitBranchExists(resolved.gitRoot, base))) {
|
|
434
|
+
throw new CliError({
|
|
435
|
+
exitCode: 2,
|
|
436
|
+
code: "E_USAGE",
|
|
437
|
+
message: unknownEntityMessage("base branch", base),
|
|
438
|
+
});
|
|
439
|
+
}
|
|
440
|
+
const taskId = parseTaskIdFromBranch(loaded.config.branch.task_prefix, branch);
|
|
441
|
+
const worktree = await findWorktreeForBranch(resolved.gitRoot, branch);
|
|
442
|
+
const { ahead, behind } = await gitAheadBehind(resolved.gitRoot, base, branch);
|
|
443
|
+
process.stdout.write(`branch=${branch} base=${base} ahead=${ahead} behind=${behind} task_id=${taskId ?? "-"}\n`);
|
|
444
|
+
if (worktree) {
|
|
445
|
+
process.stdout.write(`worktree=${worktree}\n`);
|
|
446
|
+
}
|
|
447
|
+
return 0;
|
|
448
|
+
}
|
|
449
|
+
catch (err) {
|
|
450
|
+
if (err instanceof CliError)
|
|
451
|
+
throw err;
|
|
452
|
+
throw mapCoreError(err, { command: "branch status", root: opts.rootOverride ?? null });
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
export async function cmdBranchRemove(opts) {
|
|
456
|
+
const branch = (opts.branch ?? "").trim();
|
|
457
|
+
const worktree = (opts.worktree ?? "").trim();
|
|
458
|
+
if (!branch && !worktree) {
|
|
459
|
+
throw new CliError({
|
|
460
|
+
exitCode: 2,
|
|
461
|
+
code: "E_USAGE",
|
|
462
|
+
message: usageMessage(BRANCH_REMOVE_USAGE, BRANCH_REMOVE_USAGE_EXAMPLE),
|
|
463
|
+
});
|
|
464
|
+
}
|
|
465
|
+
try {
|
|
466
|
+
const resolved = await resolveProject({
|
|
467
|
+
cwd: opts.cwd,
|
|
468
|
+
rootOverride: opts.rootOverride ?? null,
|
|
469
|
+
});
|
|
470
|
+
const loaded = await loadConfig(resolved.agentplaneDir);
|
|
471
|
+
if (worktree) {
|
|
472
|
+
const worktreePath = path.isAbsolute(worktree)
|
|
473
|
+
? await resolvePathFallback(worktree)
|
|
474
|
+
: await resolvePathFallback(path.join(resolved.gitRoot, worktree));
|
|
475
|
+
const worktreesRoot = path.resolve(resolved.gitRoot, loaded.config.paths.worktrees_dir);
|
|
476
|
+
if (!isPathWithin(worktreesRoot, worktreePath)) {
|
|
477
|
+
throw new CliError({
|
|
478
|
+
exitCode: 2,
|
|
479
|
+
code: "E_USAGE",
|
|
480
|
+
message: `Refusing to remove worktree outside ${worktreesRoot}: ${worktreePath}`,
|
|
481
|
+
});
|
|
482
|
+
}
|
|
483
|
+
await execFileAsync("git", ["worktree", "remove", ...(opts.force ? ["--force"] : []), worktreePath], { cwd: resolved.gitRoot, env: gitEnv() });
|
|
484
|
+
if (!opts.quiet) {
|
|
485
|
+
process.stdout.write(`${successMessage("removed worktree", worktreePath)}\n`);
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
if (branch) {
|
|
489
|
+
if (!(await gitBranchExists(resolved.gitRoot, branch))) {
|
|
490
|
+
throw new CliError({
|
|
491
|
+
exitCode: 2,
|
|
492
|
+
code: "E_USAGE",
|
|
493
|
+
message: unknownEntityMessage("branch", branch),
|
|
494
|
+
});
|
|
495
|
+
}
|
|
496
|
+
await execFileAsync("git", ["branch", opts.force ? "-D" : "-d", branch], {
|
|
497
|
+
cwd: resolved.gitRoot,
|
|
498
|
+
env: gitEnv(),
|
|
499
|
+
});
|
|
500
|
+
if (!opts.quiet) {
|
|
501
|
+
process.stdout.write(`${successMessage("removed branch", branch)}\n`);
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
return 0;
|
|
505
|
+
}
|
|
506
|
+
catch (err) {
|
|
507
|
+
if (err instanceof CliError)
|
|
508
|
+
throw err;
|
|
509
|
+
throw mapCoreError(err, { command: "branch remove", root: opts.rootOverride ?? null });
|
|
510
|
+
}
|
|
511
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { loadConfig } from "@agentplaneorg/core";
|
|
2
|
+
export declare function suggestAllowPrefixes(paths: string[]): string[];
|
|
3
|
+
export declare const GUARD_COMMIT_USAGE = "Usage: agentplane guard commit <task-id> -m <message> --allow <path> [--allow <path>...] [--auto-allow] [--allow-tasks] [--require-clean] [--quiet]";
|
|
4
|
+
export declare const GUARD_COMMIT_USAGE_EXAMPLE = "agentplane guard commit 202602030608-F1Q8AB -m \"\u2728 F1Q8AB update\" --allow packages/agentplane";
|
|
5
|
+
export declare const COMMIT_USAGE = "Usage: agentplane commit <task-id> -m <message>";
|
|
6
|
+
export declare const COMMIT_USAGE_EXAMPLE = "agentplane commit 202602030608-F1Q8AB -m \"\u2728 F1Q8AB update\"";
|
|
7
|
+
type GuardCommitOptions = {
|
|
8
|
+
cwd: string;
|
|
9
|
+
rootOverride?: string;
|
|
10
|
+
taskId: string;
|
|
11
|
+
message: string;
|
|
12
|
+
allow: string[];
|
|
13
|
+
allowTasks: boolean;
|
|
14
|
+
requireClean: boolean;
|
|
15
|
+
quiet: boolean;
|
|
16
|
+
};
|
|
17
|
+
export declare function gitStatusChangedPaths(opts: {
|
|
18
|
+
cwd: string;
|
|
19
|
+
rootOverride?: string;
|
|
20
|
+
}): Promise<string[]>;
|
|
21
|
+
export declare function ensureGitClean(opts: {
|
|
22
|
+
cwd: string;
|
|
23
|
+
rootOverride?: string;
|
|
24
|
+
}): Promise<void>;
|
|
25
|
+
export declare function commitFromComment(opts: {
|
|
26
|
+
cwd: string;
|
|
27
|
+
rootOverride?: string;
|
|
28
|
+
taskId: string;
|
|
29
|
+
commentBody: string;
|
|
30
|
+
formattedComment: string | null;
|
|
31
|
+
emoji: string;
|
|
32
|
+
allow: string[];
|
|
33
|
+
autoAllow: boolean;
|
|
34
|
+
allowTasks: boolean;
|
|
35
|
+
requireClean: boolean;
|
|
36
|
+
quiet: boolean;
|
|
37
|
+
config: Awaited<ReturnType<typeof loadConfig>>["config"];
|
|
38
|
+
}): Promise<{
|
|
39
|
+
hash: string;
|
|
40
|
+
message: string;
|
|
41
|
+
staged: string[];
|
|
42
|
+
}>;
|
|
43
|
+
export declare function cmdGuardClean(opts: {
|
|
44
|
+
cwd: string;
|
|
45
|
+
rootOverride?: string;
|
|
46
|
+
quiet: boolean;
|
|
47
|
+
}): Promise<number>;
|
|
48
|
+
export declare function cmdGuardSuggestAllow(opts: {
|
|
49
|
+
cwd: string;
|
|
50
|
+
rootOverride?: string;
|
|
51
|
+
format: "lines" | "args";
|
|
52
|
+
}): Promise<number>;
|
|
53
|
+
export declare function cmdGuardCommit(opts: GuardCommitOptions): Promise<number>;
|
|
54
|
+
export declare function cmdCommit(opts: {
|
|
55
|
+
cwd: string;
|
|
56
|
+
rootOverride?: string;
|
|
57
|
+
taskId: string;
|
|
58
|
+
message: string;
|
|
59
|
+
allow: string[];
|
|
60
|
+
autoAllow: boolean;
|
|
61
|
+
allowTasks: boolean;
|
|
62
|
+
allowBase: boolean;
|
|
63
|
+
requireClean: boolean;
|
|
64
|
+
quiet: boolean;
|
|
65
|
+
}): Promise<number>;
|
|
66
|
+
export {};
|
|
67
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/commands/guard/index.ts"],"names":[],"mappings":"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"}
|