agentplane 0.2.23 → 0.2.25
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 +44 -30
- package/assets/agents/CODER.json +1 -1
- package/assets/agents/DOCS.json +1 -1
- package/assets/agents/ORCHESTRATOR.json +10 -9
- package/assets/agents/PLANNER.json +9 -4
- package/assets/agents/TESTER.json +1 -1
- package/dist/cli/command-guide.js +7 -7
- 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 +80 -37
- package/dist/cli/run-cli.test-helpers.d.ts.map +1 -1
- package/dist/cli/run-cli.test-helpers.js +4 -5
- package/dist/commands/block.spec.d.ts.map +1 -1
- package/dist/commands/block.spec.js +23 -2
- package/dist/commands/commit.spec.d.ts.map +1 -1
- package/dist/commands/commit.spec.js +18 -6
- package/dist/commands/finish.spec.d.ts.map +1 -1
- package/dist/commands/finish.spec.js +53 -4
- package/dist/commands/guard/commit.command.d.ts.map +1 -1
- package/dist/commands/guard/commit.command.js +26 -20
- package/dist/commands/guard/impl/allow.d.ts.map +1 -1
- package/dist/commands/guard/impl/allow.js +8 -1
- package/dist/commands/guard/impl/commands.d.ts.map +1 -1
- package/dist/commands/guard/impl/commands.js +7 -15
- package/dist/commands/guard/impl/comment-commit.d.ts.map +1 -1
- package/dist/commands/guard/impl/comment-commit.js +8 -17
- package/dist/commands/hooks/index.d.ts.map +1 -1
- package/dist/commands/hooks/index.js +20 -6
- package/dist/commands/release/apply.command.d.ts.map +1 -1
- package/dist/commands/release/apply.command.js +18 -3
- package/dist/commands/release/plan.command.js +1 -1
- package/dist/commands/scenario/impl/commands.d.ts +18 -0
- package/dist/commands/scenario/impl/commands.d.ts.map +1 -1
- package/dist/commands/scenario/impl/commands.js +37 -5
- package/dist/commands/shared/pr-meta.d.ts +5 -0
- package/dist/commands/shared/pr-meta.d.ts.map +1 -1
- package/dist/commands/shared/pr-meta.js +11 -1
- package/dist/commands/start.spec.d.ts.map +1 -1
- package/dist/commands/start.spec.js +23 -2
- package/dist/commands/task/finish.d.ts.map +1 -1
- package/dist/commands/task/finish.js +32 -10
- package/dist/commands/task/new.d.ts.map +1 -1
- package/dist/commands/task/new.js +110 -7
- package/dist/commands/task/new.spec.d.ts.map +1 -1
- package/dist/commands/task/new.spec.js +2 -1
- package/dist/commands/task/set-status.command.d.ts.map +1 -1
- package/dist/commands/task/set-status.command.js +22 -2
- package/dist/commands/task/shared.d.ts +5 -0
- package/dist/commands/task/shared.d.ts.map +1 -1
- package/dist/commands/task/shared.js +68 -4
- package/dist/commands/task/update.d.ts.map +1 -1
- package/dist/commands/task/update.js +6 -1
- package/dist/policy/rules/allowlist.d.ts.map +1 -1
- package/dist/policy/rules/allowlist.js +9 -0
- package/dist/shared/allow-prefix-policy.d.ts +3 -0
- package/dist/shared/allow-prefix-policy.d.ts.map +1 -0
- package/dist/shared/allow-prefix-policy.js +8 -0
- package/package.json +2 -2
|
@@ -11,6 +11,15 @@ import { CliError } from "../../../shared/errors.js";
|
|
|
11
11
|
import { RECIPES_DIR_NAME, RECIPES_SCENARIOS_DIR_NAME, RECIPES_SCENARIOS_INDEX_NAME, normalizeScenarioToolStep, readInstalledRecipesFile, readRecipeManifest, readScenarioDefinition, readScenarioIndex, resolveInstalledRecipeDir, resolveInstalledRecipesPath, resolveProjectRecipesCacheDir, } from "../../recipes.js";
|
|
12
12
|
import { collectScenarioEnvKeys, getGitDiffSummary, redactArgs, writeScenarioReport, } from "./report.js";
|
|
13
13
|
const execFileAsync = promisify(execFile);
|
|
14
|
+
export function resolveRecipeToolInvocation(runtime, entrypoint, args) {
|
|
15
|
+
if (runtime === "node") {
|
|
16
|
+
return { command: "node", args: [entrypoint, ...args] };
|
|
17
|
+
}
|
|
18
|
+
return {
|
|
19
|
+
command: "bash",
|
|
20
|
+
args: [entrypoint, ...args],
|
|
21
|
+
};
|
|
22
|
+
}
|
|
14
23
|
export async function cmdScenarioListParsed(opts) {
|
|
15
24
|
try {
|
|
16
25
|
const installed = await readInstalledRecipesFile(resolveInstalledRecipesPath());
|
|
@@ -130,25 +139,46 @@ export async function cmdScenarioInfoParsed(opts) {
|
|
|
130
139
|
throw mapCoreError(err, { command: "scenario info", root: opts.rootOverride ?? null });
|
|
131
140
|
}
|
|
132
141
|
}
|
|
133
|
-
async function executeRecipeTool(opts) {
|
|
142
|
+
export async function executeRecipeTool(opts) {
|
|
143
|
+
const { command, args } = resolveRecipeToolInvocation(opts.runtime, opts.entrypoint, opts.args);
|
|
134
144
|
try {
|
|
135
|
-
const
|
|
136
|
-
const { stdout, stderr } = await execFileAsync(command, [opts.entrypoint, ...opts.args], {
|
|
145
|
+
const { stdout, stderr } = await execFileAsync(command, args, {
|
|
137
146
|
cwd: opts.cwd,
|
|
138
147
|
env: opts.env,
|
|
139
148
|
});
|
|
140
149
|
return { exitCode: 0, stdout: String(stdout), stderr: String(stderr) };
|
|
141
150
|
}
|
|
142
151
|
catch (err) {
|
|
152
|
+
const rawCode = err && typeof err === "object" && "code" in err
|
|
153
|
+
? err.code
|
|
154
|
+
: undefined;
|
|
155
|
+
const code = typeof rawCode === "number" ? rawCode : undefined;
|
|
156
|
+
const isCommandNotFound = rawCode === "ENOENT" || code === 127;
|
|
143
157
|
let execErr = null;
|
|
144
158
|
if (err && typeof err === "object") {
|
|
145
159
|
execErr = err;
|
|
146
160
|
}
|
|
147
161
|
const exitCode = typeof execErr?.code === "number" ? execErr.code : 1;
|
|
162
|
+
let stderrText = String(execErr?.stderr ?? "");
|
|
163
|
+
const isMissingNodeEntrypoint = command === "node" &&
|
|
164
|
+
/Cannot find module/i.test(stderrText) &&
|
|
165
|
+
(stderrText.includes(opts.entrypoint) || stderrText.includes(`${opts.entrypoint}.js`));
|
|
166
|
+
if (isMissingNodeEntrypoint && !isCommandNotFound) {
|
|
167
|
+
const runtimeLabel = opts.entrypoint.replace(/\.(js|mjs|cjs)$/, "");
|
|
168
|
+
stderrText = `Runtime command not found: ${runtimeLabel}`;
|
|
169
|
+
return {
|
|
170
|
+
exitCode: 1,
|
|
171
|
+
stdout: "",
|
|
172
|
+
stderr: stderrText,
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
if (isCommandNotFound && !stderrText) {
|
|
176
|
+
stderrText = `Runtime command not found: ${command}`;
|
|
177
|
+
}
|
|
148
178
|
return {
|
|
149
179
|
exitCode,
|
|
150
180
|
stdout: String(execErr?.stdout ?? ""),
|
|
151
|
-
stderr:
|
|
181
|
+
stderr: stderrText,
|
|
152
182
|
};
|
|
153
183
|
}
|
|
154
184
|
}
|
|
@@ -223,7 +253,9 @@ export async function cmdScenarioRunParsed(opts) {
|
|
|
223
253
|
message: `Tool not found in recipe manifest: ${step.tool}`,
|
|
224
254
|
});
|
|
225
255
|
}
|
|
226
|
-
const runtime = toolEntry.runtime === "node" || toolEntry.runtime === "bash"
|
|
256
|
+
const runtime = toolEntry.runtime === "node" || toolEntry.runtime === "bash"
|
|
257
|
+
? toolEntry.runtime
|
|
258
|
+
: "";
|
|
227
259
|
const entrypoint = typeof toolEntry.entrypoint === "string" ? toolEntry.entrypoint : "";
|
|
228
260
|
if (!runtime || !entrypoint) {
|
|
229
261
|
throw new CliError({
|
|
@@ -11,6 +11,11 @@ export type PrMeta = {
|
|
|
11
11
|
command?: string;
|
|
12
12
|
};
|
|
13
13
|
};
|
|
14
|
+
export type ShellInvocation = {
|
|
15
|
+
command: string;
|
|
16
|
+
args: string[];
|
|
17
|
+
};
|
|
18
|
+
export declare function resolveShellInvocation(command: string): ShellInvocation;
|
|
14
19
|
export declare function parsePrMeta(raw: string, taskId: string): PrMeta;
|
|
15
20
|
export declare function extractLastVerifiedSha(logText: string): string | null;
|
|
16
21
|
export declare function appendVerifyLog(logPath: string, header: string, content: string): Promise<void>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pr-meta.d.ts","sourceRoot":"","sources":["../../../src/commands/shared/pr-meta.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"pr-meta.d.ts","sourceRoot":"","sources":["../../../src/commands/shared/pr-meta.ts"],"names":[],"mappings":"AAcA,MAAM,MAAM,MAAM,GAAG;IACnB,cAAc,EAAE,CAAC,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,MAAM,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CACrE,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,EAAE,CAAC;CAChB,CAAC;AAEF,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,MAAM,GAAG,eAAe,CAQvE;AAED,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAc/D;AAED,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAQrE;AAED,wBAAsB,eAAe,CACnC,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,IAAI,CAAC,CAMf;AAED,wBAAsB,eAAe,CACnC,OAAO,EAAE,MAAM,EACf,GAAG,EAAE,MAAM,GACV,OAAO,CAAC;IACT,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC,CAqBD"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { mkdir, writeFile } from "node:fs/promises";
|
|
2
|
+
import os from "node:os";
|
|
2
3
|
import path from "node:path";
|
|
3
4
|
import { execFileAsync } from "./git.js";
|
|
4
5
|
function isRecord(value) {
|
|
@@ -7,6 +8,14 @@ function isRecord(value) {
|
|
|
7
8
|
function isIsoDate(value) {
|
|
8
9
|
return typeof value === "string" && !Number.isNaN(Date.parse(value));
|
|
9
10
|
}
|
|
11
|
+
export function resolveShellInvocation(command) {
|
|
12
|
+
if (os.platform() === "win32") {
|
|
13
|
+
const rawComspec = process.env.ComSpec ?? process.env.COMSPEC;
|
|
14
|
+
const shellCommand = rawComspec && rawComspec !== "undefined" && rawComspec !== "null" ? rawComspec : "cmd.exe";
|
|
15
|
+
return { command: shellCommand, args: ["/d", "/s", "/c", command] };
|
|
16
|
+
}
|
|
17
|
+
return { command: "sh", args: ["-lc", command] };
|
|
18
|
+
}
|
|
10
19
|
export function parsePrMeta(raw, taskId) {
|
|
11
20
|
let parsed;
|
|
12
21
|
try {
|
|
@@ -46,8 +55,9 @@ export async function appendVerifyLog(logPath, header, content) {
|
|
|
46
55
|
await writeFile(logPath, `${lines.join("\n")}\n`, { flag: "a" });
|
|
47
56
|
}
|
|
48
57
|
export async function runShellCommand(command, cwd) {
|
|
58
|
+
const invocation = resolveShellInvocation(command);
|
|
49
59
|
try {
|
|
50
|
-
const { stdout, stderr } = await execFileAsync(
|
|
60
|
+
const { stdout, stderr } = await execFileAsync(invocation.command, invocation.args, {
|
|
51
61
|
cwd,
|
|
52
62
|
env: process.env,
|
|
53
63
|
maxBuffer: 10 * 1024 * 1024,
|
|
@@ -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;
|
|
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;AASvD,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,CA8I9C,CAAC"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { usageError } from "../cli/spec/errors.js";
|
|
2
|
+
import { findRepoWideAllowPrefixes, repoWideAllowPrefixMessage, } from "../shared/allow-prefix-policy.js";
|
|
2
3
|
import { toStringList } from "../cli/spec/parse-utils.js";
|
|
3
4
|
export const startSpec = {
|
|
4
5
|
id: ["start"],
|
|
@@ -44,13 +45,14 @@ export const startSpec = {
|
|
|
44
45
|
name: "commit-allow",
|
|
45
46
|
valueHint: "<path-prefix>",
|
|
46
47
|
repeatable: true,
|
|
47
|
-
description: "Repeatable. Allowlist path prefixes to stage for the status commit (used with --commit-from-comment).",
|
|
48
|
+
description: "Repeatable. Allowlist path prefixes to stage for the status commit (used with --commit-from-comment). Use minimal prefixes; '.' is rejected.",
|
|
48
49
|
},
|
|
49
50
|
{
|
|
50
51
|
kind: "boolean",
|
|
51
52
|
name: "commit-auto-allow",
|
|
52
53
|
default: false,
|
|
53
|
-
description: "
|
|
54
|
+
description: "Deprecated. Disabled for safety; pass explicit --commit-allow prefixes.",
|
|
55
|
+
deprecated: "disabled",
|
|
54
56
|
},
|
|
55
57
|
{
|
|
56
58
|
kind: "boolean",
|
|
@@ -99,10 +101,29 @@ export const startSpec = {
|
|
|
99
101
|
validateRaw: (raw) => {
|
|
100
102
|
const author = typeof raw.opts.author === "string" ? raw.opts.author.trim() : "";
|
|
101
103
|
const body = typeof raw.opts.body === "string" ? raw.opts.body.trim() : "";
|
|
104
|
+
const commitAllow = toStringList(raw.opts["commit-allow"]);
|
|
102
105
|
if (!author)
|
|
103
106
|
throw usageError({ spec: startSpec, message: "Invalid value for --author: empty." });
|
|
104
107
|
if (!body)
|
|
105
108
|
throw usageError({ spec: startSpec, message: "Invalid value for --body: empty." });
|
|
109
|
+
if (findRepoWideAllowPrefixes(commitAllow).length > 0) {
|
|
110
|
+
throw usageError({
|
|
111
|
+
spec: startSpec,
|
|
112
|
+
message: repoWideAllowPrefixMessage("--commit-allow"),
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
if (raw.opts["commit-auto-allow"] === true) {
|
|
116
|
+
throw usageError({
|
|
117
|
+
spec: startSpec,
|
|
118
|
+
message: "--commit-auto-allow is disabled; pass explicit --commit-allow <path-prefix>.",
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
if (raw.opts["commit-from-comment"] === true && commitAllow.length === 0) {
|
|
122
|
+
throw usageError({
|
|
123
|
+
spec: startSpec,
|
|
124
|
+
message: "--commit-from-comment requires --commit-allow <path-prefix> (tip: `agentplane guard suggest-allow --format args`).",
|
|
125
|
+
});
|
|
126
|
+
}
|
|
106
127
|
},
|
|
107
128
|
parse: (raw) => ({
|
|
108
129
|
taskId: typeof raw.args["task-id"] === "string" ? raw.args["task-id"] : "",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"finish.d.ts","sourceRoot":"","sources":["../../../src/commands/task/finish.ts"],"names":[],"mappings":"AAUA,OAAO,EAGL,KAAK,cAAc,EACpB,MAAM,2BAA2B,CAAC;AAmCnC,wBAAsB,SAAS,CAAC,IAAI,EAAE;IACpC,GAAG,CAAC,EAAE,cAAc,CAAC;IACrB,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,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,CAAC,EAAE,OAAO,CAAC;IACd,iBAAiB,EAAE,OAAO,CAAC;IAC3B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,eAAe,EAAE,OAAO,CAAC;IACzB,gBAAgB,EAAE,OAAO,CAAC;IAC1B,kBAAkB,EAAE,OAAO,CAAC;IAC5B,YAAY,EAAE,OAAO,CAAC;IACtB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,iBAAiB,EAAE,MAAM,EAAE,CAAC;IAC5B,qBAAqB,EAAE,OAAO,CAAC;IAC/B,wBAAwB,EAAE,OAAO,CAAC;IAClC,mBAAmB,EAAE,OAAO,CAAC;IAC7B,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,KAAK,EAAE,OAAO,CAAC;CAChB,GAAG,OAAO,CAAC,MAAM,CAAC,
|
|
1
|
+
{"version":3,"file":"finish.d.ts","sourceRoot":"","sources":["../../../src/commands/task/finish.ts"],"names":[],"mappings":"AAUA,OAAO,EAGL,KAAK,cAAc,EACpB,MAAM,2BAA2B,CAAC;AAmCnC,wBAAsB,SAAS,CAAC,IAAI,EAAE;IACpC,GAAG,CAAC,EAAE,cAAc,CAAC;IACrB,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,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,CAAC,EAAE,OAAO,CAAC;IACd,iBAAiB,EAAE,OAAO,CAAC;IAC3B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,eAAe,EAAE,OAAO,CAAC;IACzB,gBAAgB,EAAE,OAAO,CAAC;IAC1B,kBAAkB,EAAE,OAAO,CAAC;IAC5B,YAAY,EAAE,OAAO,CAAC;IACtB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,iBAAiB,EAAE,MAAM,EAAE,CAAC;IAC5B,qBAAqB,EAAE,OAAO,CAAC;IAC/B,wBAAwB,EAAE,OAAO,CAAC;IAClC,mBAAmB,EAAE,OAAO,CAAC;IAC7B,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,KAAK,EAAE,OAAO,CAAC;CAChB,GAAG,OAAO,CAAC,MAAM,CAAC,CA2VlB"}
|
|
@@ -40,13 +40,7 @@ export async function cmdFinish(opts) {
|
|
|
40
40
|
}
|
|
41
41
|
const { prefix, min_chars: minChars } = ctx.config.tasks.comments.verified;
|
|
42
42
|
requireStructuredComment(opts.body, prefix, minChars);
|
|
43
|
-
const
|
|
44
|
-
ctx.config.commit_automation === "finish_only") &&
|
|
45
|
-
!opts.commitFromComment &&
|
|
46
|
-
!opts.statusCommit &&
|
|
47
|
-
opts.closeCommit !== true &&
|
|
48
|
-
opts.taskIds.length === 1;
|
|
49
|
-
const statusCommitRequested = opts.statusCommit || autoStatusCommit;
|
|
43
|
+
const statusCommitRequested = opts.statusCommit;
|
|
50
44
|
if ((opts.commitFromComment || statusCommitRequested) && opts.taskIds.length !== 1) {
|
|
51
45
|
throw new CliError({
|
|
52
46
|
exitCode: 2,
|
|
@@ -76,6 +70,34 @@ export async function cmdFinish(opts) {
|
|
|
76
70
|
message: "--commit-from-comment/--status-commit requires exactly one task id",
|
|
77
71
|
});
|
|
78
72
|
}
|
|
73
|
+
if (opts.commitAutoAllow) {
|
|
74
|
+
throw new CliError({
|
|
75
|
+
exitCode: 2,
|
|
76
|
+
code: "E_USAGE",
|
|
77
|
+
message: "--commit-auto-allow is disabled; pass explicit --commit-allow <path-prefix>.",
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
if (opts.statusCommitAutoAllow) {
|
|
81
|
+
throw new CliError({
|
|
82
|
+
exitCode: 2,
|
|
83
|
+
code: "E_USAGE",
|
|
84
|
+
message: "--status-commit-auto-allow is disabled; pass explicit --status-commit-allow <path-prefix>.",
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
if (opts.commitFromComment && opts.commitAllow.length === 0) {
|
|
88
|
+
throw new CliError({
|
|
89
|
+
exitCode: 2,
|
|
90
|
+
code: "E_USAGE",
|
|
91
|
+
message: "--commit-from-comment requires --commit-allow <path-prefix>",
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
if (statusCommitRequested && opts.statusCommitAllow.length === 0) {
|
|
95
|
+
throw new CliError({
|
|
96
|
+
exitCode: 2,
|
|
97
|
+
code: "E_USAGE",
|
|
98
|
+
message: "--status-commit requires --status-commit-allow <path-prefix>",
|
|
99
|
+
});
|
|
100
|
+
}
|
|
79
101
|
const gitRoot = ctx.resolvedProject.gitRoot;
|
|
80
102
|
const commitInfo = opts.commit
|
|
81
103
|
? await readCommitInfo(gitRoot, opts.commit)
|
|
@@ -168,7 +190,7 @@ export async function cmdFinish(opts) {
|
|
|
168
190
|
enforceStatusCommitPolicy({
|
|
169
191
|
policy: ctx.config.status_commit_policy,
|
|
170
192
|
action: "finish",
|
|
171
|
-
confirmed: opts.confirmStatusCommit
|
|
193
|
+
confirmed: opts.confirmStatusCommit,
|
|
172
194
|
quiet: opts.quiet,
|
|
173
195
|
statusFrom: primaryStatusFrom ?? "UNKNOWN",
|
|
174
196
|
statusTo: "DONE",
|
|
@@ -208,7 +230,7 @@ export async function cmdFinish(opts) {
|
|
|
208
230
|
formattedComment: formatCommentBodyForCommit(opts.body, ctx.config),
|
|
209
231
|
emoji: opts.commitEmoji ?? defaultCommitEmojiForStatus("DONE"),
|
|
210
232
|
allow: opts.commitAllow,
|
|
211
|
-
autoAllow:
|
|
233
|
+
autoAllow: false,
|
|
212
234
|
allowTasks: opts.commitAllowTasks,
|
|
213
235
|
requireClean: opts.commitRequireClean,
|
|
214
236
|
quiet: opts.quiet,
|
|
@@ -269,7 +291,7 @@ export async function cmdFinish(opts) {
|
|
|
269
291
|
formattedComment: formatCommentBodyForCommit(opts.body, ctx.config),
|
|
270
292
|
emoji: opts.statusCommitEmoji ?? defaultCommitEmojiForStatus("DONE"),
|
|
271
293
|
allow: opts.statusCommitAllow,
|
|
272
|
-
autoAllow:
|
|
294
|
+
autoAllow: false,
|
|
273
295
|
allowTasks: true,
|
|
274
296
|
requireClean: opts.statusCommitRequireClean,
|
|
275
297
|
quiet: opts.quiet,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"new.d.ts","sourceRoot":"","sources":["../../../src/commands/task/new.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"new.d.ts","sourceRoot":"","sources":["../../../src/commands/task/new.ts"],"names":[],"mappings":"AAMA,OAAO,EAAsB,KAAK,cAAc,EAAE,MAAM,2BAA2B,CAAC;AASpF,MAAM,MAAM,aAAa,GAAG;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,KAAK,GAAG,QAAQ,GAAG,KAAK,GAAG,MAAM,CAAC;IAC5C,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB,CAAC;AAuIF,wBAAsB,gBAAgB,CAAC,IAAI,EAAE;IAC3C,GAAG,CAAC,EAAE,cAAc,CAAC;IACrB,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,aAAa,CAAC;CACvB,GAAG,OAAO,CAAC,MAAM,CAAC,CAgFlB"}
|
|
@@ -1,15 +1,112 @@
|
|
|
1
|
+
import { ensureDocSections, setMarkdownSection } from "@agentplaneorg/core";
|
|
1
2
|
import { mapBackendError } from "../../cli/error-map.js";
|
|
2
3
|
import { backendNotSupportedMessage, warnMessage } from "../../cli/output.js";
|
|
3
4
|
import { CliError } from "../../shared/errors.js";
|
|
4
5
|
import { loadCommandContext } from "../shared/task-backend.js";
|
|
5
|
-
import { nowIso, requiresVerifyStepsByPrimary, resolvePrimaryTag, warnIfUnknownOwner, } from "./shared.js";
|
|
6
|
-
function
|
|
6
|
+
import { ensureTaskDependsOnGraphIsAcyclic, nowIso, requiresVerifyStepsByPrimary, resolvePrimaryTag, warnIfUnknownOwner, } from "./shared.js";
|
|
7
|
+
function dedupeTrimmed(values) {
|
|
8
|
+
const seen = new Set();
|
|
9
|
+
const out = [];
|
|
10
|
+
for (const raw of values) {
|
|
11
|
+
const value = String(raw ?? "").trim();
|
|
12
|
+
if (!value || seen.has(value))
|
|
13
|
+
continue;
|
|
14
|
+
seen.add(value);
|
|
15
|
+
out.push(value);
|
|
16
|
+
}
|
|
17
|
+
return out;
|
|
18
|
+
}
|
|
19
|
+
function sanitizeTaskNewParsed(p) {
|
|
20
|
+
const title = p.title.trim();
|
|
21
|
+
if (!title)
|
|
22
|
+
throw new CliError({
|
|
23
|
+
exitCode: 2,
|
|
24
|
+
code: "E_USAGE",
|
|
25
|
+
message: "Invalid value for --title: empty.",
|
|
26
|
+
});
|
|
27
|
+
const description = p.description.trim();
|
|
28
|
+
if (!description) {
|
|
29
|
+
throw new CliError({
|
|
30
|
+
exitCode: 2,
|
|
31
|
+
code: "E_USAGE",
|
|
32
|
+
message: "Invalid value for --description: empty.",
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
const owner = p.owner.trim();
|
|
36
|
+
if (!owner)
|
|
37
|
+
throw new CliError({
|
|
38
|
+
exitCode: 2,
|
|
39
|
+
code: "E_USAGE",
|
|
40
|
+
message: "Invalid value for --owner: empty.",
|
|
41
|
+
});
|
|
42
|
+
const tags = dedupeTrimmed(p.tags);
|
|
43
|
+
if (tags.length === 0) {
|
|
44
|
+
throw new CliError({
|
|
45
|
+
exitCode: 2,
|
|
46
|
+
code: "E_USAGE",
|
|
47
|
+
message: "Invalid value for --tag: provide at least one non-empty tag.",
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
const dependsOn = dedupeTrimmed(p.dependsOn);
|
|
51
|
+
const verify = dedupeTrimmed(p.verify);
|
|
52
|
+
return { ...p, title, description, owner, tags, dependsOn, verify };
|
|
53
|
+
}
|
|
54
|
+
function insertMarkdownSectionBefore(opts) {
|
|
55
|
+
const normalized = opts.body.replaceAll("\r\n", "\n");
|
|
56
|
+
if (normalized.includes(`## ${opts.section}`)) {
|
|
57
|
+
return setMarkdownSection(normalized, opts.section, opts.text);
|
|
58
|
+
}
|
|
59
|
+
const lines = normalized.split("\n");
|
|
60
|
+
const beforeHeading = `## ${opts.beforeSection}`;
|
|
61
|
+
const beforeIdx = lines.findIndex((line) => line.trim() === beforeHeading);
|
|
62
|
+
if (beforeIdx === -1)
|
|
63
|
+
return setMarkdownSection(normalized, opts.section, opts.text);
|
|
64
|
+
const textLines = opts.text.replaceAll("\r\n", "\n").split("\n");
|
|
65
|
+
const sectionLines = [`## ${opts.section}`, "", ...textLines, "", ""];
|
|
66
|
+
const out = [...lines.slice(0, beforeIdx), ...sectionLines, ...lines.slice(beforeIdx)];
|
|
67
|
+
return `${out.join("\n").trimEnd()}\n`;
|
|
68
|
+
}
|
|
69
|
+
function defaultTaskDoc(requiredSections) {
|
|
70
|
+
const verifyStepsTemplate = [
|
|
71
|
+
"<!-- TODO: FILL VERIFY STEPS -->",
|
|
72
|
+
"",
|
|
73
|
+
"### Scope",
|
|
74
|
+
"",
|
|
75
|
+
"",
|
|
76
|
+
"### Checks",
|
|
77
|
+
"",
|
|
78
|
+
"",
|
|
79
|
+
"### Evidence / Commands",
|
|
80
|
+
"",
|
|
81
|
+
"",
|
|
82
|
+
"### Pass criteria",
|
|
83
|
+
"",
|
|
84
|
+
"",
|
|
85
|
+
].join("\n");
|
|
86
|
+
const verificationTemplate = [
|
|
87
|
+
"### Plan",
|
|
88
|
+
"",
|
|
89
|
+
"",
|
|
90
|
+
"### Results",
|
|
91
|
+
"",
|
|
92
|
+
"",
|
|
93
|
+
"<!-- BEGIN VERIFICATION RESULTS -->",
|
|
94
|
+
"<!-- END VERIFICATION RESULTS -->",
|
|
95
|
+
].join("\n");
|
|
96
|
+
const baseDoc = ensureDocSections("", requiredSections);
|
|
97
|
+
const withVerifySteps = insertMarkdownSectionBefore({
|
|
98
|
+
body: baseDoc,
|
|
99
|
+
section: "Verify Steps",
|
|
100
|
+
text: verifyStepsTemplate,
|
|
101
|
+
beforeSection: "Verification",
|
|
102
|
+
});
|
|
103
|
+
return setMarkdownSection(withVerifySteps, "Verification", verificationTemplate);
|
|
104
|
+
}
|
|
105
|
+
function buildDefaultVerifyStepsSection(opts) {
|
|
7
106
|
const checks = opts.verifyCommands.length > 0
|
|
8
107
|
? opts.verifyCommands.map((command) => `- \`${command}\``).join("\n")
|
|
9
108
|
: "- Add explicit checks/commands for this task before approval.";
|
|
10
109
|
return [
|
|
11
|
-
"## Verify Steps",
|
|
12
|
-
"",
|
|
13
110
|
"### Scope",
|
|
14
111
|
`- Primary tag: \`${opts.primary}\``,
|
|
15
112
|
"",
|
|
@@ -25,7 +122,7 @@ function buildDefaultVerifyStepsDoc(opts) {
|
|
|
25
122
|
].join("\n");
|
|
26
123
|
}
|
|
27
124
|
export async function runTaskNewParsed(opts) {
|
|
28
|
-
const p = opts.parsed;
|
|
125
|
+
const p = sanitizeTaskNewParsed(opts.parsed);
|
|
29
126
|
try {
|
|
30
127
|
const ctx = opts.ctx ??
|
|
31
128
|
(await loadCommandContext({ cwd: opts.cwd, rootOverride: opts.rootOverride ?? null }));
|
|
@@ -53,6 +150,7 @@ export async function runTaskNewParsed(opts) {
|
|
|
53
150
|
doc_updated_at: nowIso(),
|
|
54
151
|
doc_updated_by: p.owner,
|
|
55
152
|
id_source: "generated",
|
|
153
|
+
doc: defaultTaskDoc(ctx.config.tasks.doc.required_sections),
|
|
56
154
|
};
|
|
57
155
|
const spikeTag = (ctx.config.tasks.verify.spike_tag ?? "spike").trim().toLowerCase();
|
|
58
156
|
const primary = resolvePrimaryTag(p.tags, ctx);
|
|
@@ -61,11 +159,16 @@ export async function runTaskNewParsed(opts) {
|
|
|
61
159
|
}
|
|
62
160
|
const requiresVerifySteps = requiresVerifyStepsByPrimary(p.tags, ctx.config);
|
|
63
161
|
await warnIfUnknownOwner(ctx, p.owner);
|
|
162
|
+
await ensureTaskDependsOnGraphIsAcyclic({
|
|
163
|
+
backend: ctx.taskBackend,
|
|
164
|
+
taskId,
|
|
165
|
+
dependsOn: p.dependsOn,
|
|
166
|
+
});
|
|
64
167
|
if (requiresVerifySteps) {
|
|
65
|
-
task.doc =
|
|
168
|
+
task.doc = setMarkdownSection(task.doc ?? "", "Verify Steps", buildDefaultVerifyStepsSection({
|
|
66
169
|
primary: primary.primary,
|
|
67
170
|
verifyCommands: p.verify,
|
|
68
|
-
});
|
|
171
|
+
}));
|
|
69
172
|
process.stderr.write(`${warnMessage("task requires Verify Steps by primary tag; seeded a default ## Verify Steps section in README (review and refine before approval/start)")}\n`);
|
|
70
173
|
}
|
|
71
174
|
const hasSpike = p.tags.some((tag) => tag.trim().toLowerCase() === spikeTag);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"new.spec.d.ts","sourceRoot":"","sources":["../../../src/commands/task/new.spec.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAE1D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAG9C,eAAO,MAAM,WAAW,EAAE,WAAW,CAAC,aAAa,
|
|
1
|
+
{"version":3,"file":"new.spec.d.ts","sourceRoot":"","sources":["../../../src/commands/task/new.spec.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAE1D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAG9C,eAAO,MAAM,WAAW,EAAE,WAAW,CAAC,aAAa,CA+ElD,CAAC"}
|
|
@@ -3,7 +3,7 @@ export const taskNewSpec = {
|
|
|
3
3
|
id: ["task", "new"],
|
|
4
4
|
group: "Task",
|
|
5
5
|
summary: "Create a new task (prints the generated task id).",
|
|
6
|
-
description: "Creates a TODO task with doc_version=2 and writes it via the configured task backend.",
|
|
6
|
+
description: "Creates a TODO task with doc_version=2, seeds standard README sections, and writes it via the configured task backend.",
|
|
7
7
|
options: [
|
|
8
8
|
{
|
|
9
9
|
kind: "string",
|
|
@@ -65,6 +65,7 @@ export const taskNewSpec = {
|
|
|
65
65
|
},
|
|
66
66
|
],
|
|
67
67
|
notes: [
|
|
68
|
+
"Task README scaffolding is applied automatically during creation.",
|
|
68
69
|
"For verify-required primary tags, this command seeds a default ## Verify Steps section in README.",
|
|
69
70
|
],
|
|
70
71
|
parse: (raw) => ({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"set-status.command.d.ts","sourceRoot":"","sources":["../../../src/commands/task/set-status.command.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"set-status.command.d.ts","sourceRoot":"","sources":["../../../src/commands/task/set-status.command.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAOtE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAIhE,MAAM,MAAM,mBAAmB,GAAG;IAChC,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,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,mBAAmB,EAAE,OAAO,CAAC;IAC7B,KAAK,EAAE,OAAO,CAAC;CAChB,CAAC;AAEF,eAAO,MAAM,iBAAiB,EAAE,WAAW,CAAC,mBAAmB,CAyK9D,CAAC;AAEF,wBAAgB,2BAA2B,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,cAAc,CAAC,IAC5E,KAAK,UAAU,EAAE,GAAG,mBAAmB,KAAG,OAAO,CAAC,MAAM,CAAC,CAsBxE"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { usageError } from "../../cli/spec/errors.js";
|
|
2
2
|
import { toStringList } from "../../cli/spec/parse-utils.js";
|
|
3
|
+
import { findRepoWideAllowPrefixes, repoWideAllowPrefixMessage, } from "../../shared/allow-prefix-policy.js";
|
|
3
4
|
import { cmdTaskSetStatus } from "./set-status.js";
|
|
4
5
|
export const taskSetStatusSpec = {
|
|
5
6
|
id: ["task", "set-status"],
|
|
@@ -57,13 +58,14 @@ export const taskSetStatusSpec = {
|
|
|
57
58
|
name: "commit-allow",
|
|
58
59
|
valueHint: "<path-prefix>",
|
|
59
60
|
repeatable: true,
|
|
60
|
-
description: "Repeatable. Allowlist prefix for commit-from-comment staging.",
|
|
61
|
+
description: "Repeatable. Allowlist prefix for commit-from-comment staging. Use minimal prefixes; '.' is rejected.",
|
|
61
62
|
},
|
|
62
63
|
{
|
|
63
64
|
kind: "boolean",
|
|
64
65
|
name: "commit-auto-allow",
|
|
65
66
|
default: false,
|
|
66
|
-
description: "
|
|
67
|
+
description: "Deprecated. Disabled for safety; pass explicit --commit-allow prefixes.",
|
|
68
|
+
deprecated: "disabled",
|
|
67
69
|
},
|
|
68
70
|
{
|
|
69
71
|
kind: "boolean",
|
|
@@ -125,6 +127,24 @@ export const taskSetStatusSpec = {
|
|
|
125
127
|
message: "Invalid value for --commit-allow: empty.",
|
|
126
128
|
});
|
|
127
129
|
}
|
|
130
|
+
if (findRepoWideAllowPrefixes(allow).length > 0) {
|
|
131
|
+
throw usageError({
|
|
132
|
+
spec: taskSetStatusSpec,
|
|
133
|
+
message: repoWideAllowPrefixMessage("--commit-allow"),
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
if (raw.opts["commit-auto-allow"] === true) {
|
|
137
|
+
throw usageError({
|
|
138
|
+
spec: taskSetStatusSpec,
|
|
139
|
+
message: "--commit-auto-allow is disabled; pass explicit --commit-allow <path-prefix>.",
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
if (raw.opts["commit-from-comment"] === true && allow.length === 0) {
|
|
143
|
+
throw usageError({
|
|
144
|
+
spec: taskSetStatusSpec,
|
|
145
|
+
message: "--commit-from-comment requires --commit-allow <path-prefix> (tip: `agentplane guard suggest-allow --format args`).",
|
|
146
|
+
});
|
|
147
|
+
}
|
|
128
148
|
},
|
|
129
149
|
parse: (raw) => {
|
|
130
150
|
const commitAllow = toStringList(raw.opts["commit-allow"]);
|
|
@@ -37,6 +37,11 @@ export type DependencyState = {
|
|
|
37
37
|
missing: string[];
|
|
38
38
|
incomplete: string[];
|
|
39
39
|
};
|
|
40
|
+
export declare function ensureTaskDependsOnGraphIsAcyclic(opts: {
|
|
41
|
+
backend: Pick<TaskBackend, "listTasks">;
|
|
42
|
+
taskId: string;
|
|
43
|
+
dependsOn: string[];
|
|
44
|
+
}): Promise<void>;
|
|
40
45
|
export declare function resolveTaskDependencyState(task: TaskData, backend: Pick<TaskBackend, "getTask" | "getTasks">): Promise<DependencyState>;
|
|
41
46
|
export declare function buildDependencyState(tasks: TaskData[]): Map<string, DependencyState>;
|
|
42
47
|
export declare function formatTaskLine(task: TaskData, depState?: DependencyState): string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shared.d.ts","sourceRoot":"","sources":["../../../src/commands/task/shared.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAK9C,OAAO,EAAE,KAAK,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAW5D,OAAO,EAAE,KAAK,WAAW,EAAE,KAAK,QAAQ,EAAE,KAAK,SAAS,EAAE,MAAM,gCAAgC,CAAC;AAKjG,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAEhE,OAAO,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAExD,eAAO,MAAM,aAAa,+BAAsB,CAAC;AAiBjD,wBAAgB,MAAM,IAAI,MAAM,CAE/B;AAED,eAAO,MAAM,wBAAwB,qCAAqC,CAAC;AAE3E,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAiBjF;AAED,wBAAgB,mBAAmB,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAKvE;AAED,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAI/D;AAID,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAczD;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,EAAE,CAKtD;AAED,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,YAAY,EAAE,MAAM,EAAE,GAAG,OAAO,CAI9E;AAED,MAAM,MAAM,oBAAoB,GAAG;IACjC,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,YAAY,EAAE,OAAO,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG;IAC1B,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B,aAAa,EAAE,OAAO,CAAC;IACvB,eAAe,EAAE,MAAM,CAAC;IACxB,mBAAmB,EAAE,OAAO,CAAC;CAC9B,CAAC;AAsCF,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,cAAc,GAAG,gBAAgB,GAAG,aAAa,CAkBzF;AAoCD,wBAAgB,2BAA2B,CACzC,IAAI,EAAE,MAAM,EAAE,EACd,MAAM,EAAE,gBAAgB,GACvB,oBAAoB,CAgDtB;AAED,wBAAgB,4BAA4B,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,gBAAgB,GAAG,OAAO,CAG9F;AAED,wBAAgB,6BAA6B,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,gBAAgB,GAAG,OAAO,CAG/F;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,GAAG,EAAE,cAAc,GAAG,oBAAoB,CAE3F;AAED,wBAAsB,kBAAkB,CAAC,GAAG,EAAE,cAAc,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"shared.d.ts","sourceRoot":"","sources":["../../../src/commands/task/shared.ts"],"names":[],"mappings":"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"}
|
|
@@ -204,10 +204,14 @@ export async function warnIfUnknownOwner(ctx, owner) {
|
|
|
204
204
|
const ids = await listAgentIdsMemo(ctx);
|
|
205
205
|
if (ids.length === 0)
|
|
206
206
|
return;
|
|
207
|
-
if (
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
207
|
+
if (ids.includes(trimmed))
|
|
208
|
+
return;
|
|
209
|
+
throw new CliError({
|
|
210
|
+
exitCode: 3,
|
|
211
|
+
code: "E_VALIDATION",
|
|
212
|
+
message: `unknown task owner id: ${trimmed} (not found under ${ctx.config.paths.agents_dir}; ` +
|
|
213
|
+
`pick an existing agent id or create ${ctx.config.paths.agents_dir}/${trimmed}.json)`,
|
|
214
|
+
});
|
|
211
215
|
}
|
|
212
216
|
export function appendTaskEvent(task, event) {
|
|
213
217
|
const existing = Array.isArray(task.events)
|
|
@@ -249,6 +253,66 @@ export function ensureVerificationSatisfiedIfRequired(task, config) {
|
|
|
249
253
|
`(verification.state=${JSON.stringify(state)}; ${hint} or set agents.approvals.require_verify=false).`,
|
|
250
254
|
});
|
|
251
255
|
}
|
|
256
|
+
function hasDependsOnCycle(dependsOnMap) {
|
|
257
|
+
const visiting = new Set();
|
|
258
|
+
const visited = new Set();
|
|
259
|
+
const stack = [];
|
|
260
|
+
function dfs(taskId) {
|
|
261
|
+
if (visited.has(taskId))
|
|
262
|
+
return null;
|
|
263
|
+
if (visiting.has(taskId)) {
|
|
264
|
+
const start = stack.indexOf(taskId);
|
|
265
|
+
return start === -1 ? [taskId] : [...stack.slice(start), taskId];
|
|
266
|
+
}
|
|
267
|
+
visiting.add(taskId);
|
|
268
|
+
stack.push(taskId);
|
|
269
|
+
const deps = dependsOnMap.get(taskId) ?? [];
|
|
270
|
+
for (const depId of deps) {
|
|
271
|
+
const cycle = dfs(depId);
|
|
272
|
+
if (cycle)
|
|
273
|
+
return cycle;
|
|
274
|
+
}
|
|
275
|
+
stack.pop();
|
|
276
|
+
visiting.delete(taskId);
|
|
277
|
+
visited.add(taskId);
|
|
278
|
+
return null;
|
|
279
|
+
}
|
|
280
|
+
for (const taskId of dependsOnMap.keys()) {
|
|
281
|
+
const cycle = dfs(taskId);
|
|
282
|
+
if (cycle)
|
|
283
|
+
return cycle;
|
|
284
|
+
}
|
|
285
|
+
return null;
|
|
286
|
+
}
|
|
287
|
+
export async function ensureTaskDependsOnGraphIsAcyclic(opts) {
|
|
288
|
+
const nextDepends = dedupeStrings(opts.dependsOn);
|
|
289
|
+
if (nextDepends.includes(opts.taskId)) {
|
|
290
|
+
throw new CliError({
|
|
291
|
+
exitCode: 2,
|
|
292
|
+
code: "E_USAGE",
|
|
293
|
+
message: `depends_on cannot include task itself (${opts.taskId})`,
|
|
294
|
+
});
|
|
295
|
+
}
|
|
296
|
+
const allTasks = await opts.backend.listTasks();
|
|
297
|
+
const depMap = new Map();
|
|
298
|
+
for (const task of allTasks) {
|
|
299
|
+
const taskId = String(task.id || "").trim();
|
|
300
|
+
if (!taskId)
|
|
301
|
+
continue;
|
|
302
|
+
if (taskId === opts.taskId)
|
|
303
|
+
continue;
|
|
304
|
+
depMap.set(taskId, dedupeStrings(toStringArray(task.depends_on)));
|
|
305
|
+
}
|
|
306
|
+
depMap.set(opts.taskId, nextDepends);
|
|
307
|
+
const cycle = hasDependsOnCycle(depMap);
|
|
308
|
+
if (!cycle)
|
|
309
|
+
return;
|
|
310
|
+
throw new CliError({
|
|
311
|
+
exitCode: 2,
|
|
312
|
+
code: "E_USAGE",
|
|
313
|
+
message: `depends_on cycle detected: ${cycle.join(" -> ")}`,
|
|
314
|
+
});
|
|
315
|
+
}
|
|
252
316
|
export async function resolveTaskDependencyState(task, backend) {
|
|
253
317
|
const dependsOn = dedupeStrings(toStringArray(task.depends_on));
|
|
254
318
|
if (dependsOn.length === 0) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"update.d.ts","sourceRoot":"","sources":["../../../src/commands/task/update.ts"],"names":[],"mappings":"AAIA,OAAO,EAAsB,KAAK,cAAc,EAAE,MAAM,2BAA2B,CAAC;
|
|
1
|
+
{"version":3,"file":"update.d.ts","sourceRoot":"","sources":["../../../src/commands/task/update.ts"],"names":[],"mappings":"AAIA,OAAO,EAAsB,KAAK,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAWpF,wBAAsB,aAAa,CAAC,IAAI,EAAE;IACxC,GAAG,CAAC,EAAE,cAAc,CAAC;IACrB,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,WAAW,EAAE,OAAO,CAAC;IACrB,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,gBAAgB,EAAE,OAAO,CAAC;IAC1B,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,aAAa,EAAE,OAAO,CAAC;IACvB,kBAAkB,CAAC,EAAE,OAAO,CAAC;CAC9B,GAAG,OAAO,CAAC,MAAM,CAAC,CA6ElB"}
|