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.
Files changed (58) hide show
  1. package/assets/AGENTS.md +44 -30
  2. package/assets/agents/CODER.json +1 -1
  3. package/assets/agents/DOCS.json +1 -1
  4. package/assets/agents/ORCHESTRATOR.json +10 -9
  5. package/assets/agents/PLANNER.json +9 -4
  6. package/assets/agents/TESTER.json +1 -1
  7. package/dist/cli/command-guide.js +7 -7
  8. package/dist/cli/run-cli/commands/init.d.ts +1 -1
  9. package/dist/cli/run-cli/commands/init.d.ts.map +1 -1
  10. package/dist/cli/run-cli/commands/init.js +80 -37
  11. package/dist/cli/run-cli.test-helpers.d.ts.map +1 -1
  12. package/dist/cli/run-cli.test-helpers.js +4 -5
  13. package/dist/commands/block.spec.d.ts.map +1 -1
  14. package/dist/commands/block.spec.js +23 -2
  15. package/dist/commands/commit.spec.d.ts.map +1 -1
  16. package/dist/commands/commit.spec.js +18 -6
  17. package/dist/commands/finish.spec.d.ts.map +1 -1
  18. package/dist/commands/finish.spec.js +53 -4
  19. package/dist/commands/guard/commit.command.d.ts.map +1 -1
  20. package/dist/commands/guard/commit.command.js +26 -20
  21. package/dist/commands/guard/impl/allow.d.ts.map +1 -1
  22. package/dist/commands/guard/impl/allow.js +8 -1
  23. package/dist/commands/guard/impl/commands.d.ts.map +1 -1
  24. package/dist/commands/guard/impl/commands.js +7 -15
  25. package/dist/commands/guard/impl/comment-commit.d.ts.map +1 -1
  26. package/dist/commands/guard/impl/comment-commit.js +8 -17
  27. package/dist/commands/hooks/index.d.ts.map +1 -1
  28. package/dist/commands/hooks/index.js +20 -6
  29. package/dist/commands/release/apply.command.d.ts.map +1 -1
  30. package/dist/commands/release/apply.command.js +18 -3
  31. package/dist/commands/release/plan.command.js +1 -1
  32. package/dist/commands/scenario/impl/commands.d.ts +18 -0
  33. package/dist/commands/scenario/impl/commands.d.ts.map +1 -1
  34. package/dist/commands/scenario/impl/commands.js +37 -5
  35. package/dist/commands/shared/pr-meta.d.ts +5 -0
  36. package/dist/commands/shared/pr-meta.d.ts.map +1 -1
  37. package/dist/commands/shared/pr-meta.js +11 -1
  38. package/dist/commands/start.spec.d.ts.map +1 -1
  39. package/dist/commands/start.spec.js +23 -2
  40. package/dist/commands/task/finish.d.ts.map +1 -1
  41. package/dist/commands/task/finish.js +32 -10
  42. package/dist/commands/task/new.d.ts.map +1 -1
  43. package/dist/commands/task/new.js +110 -7
  44. package/dist/commands/task/new.spec.d.ts.map +1 -1
  45. package/dist/commands/task/new.spec.js +2 -1
  46. package/dist/commands/task/set-status.command.d.ts.map +1 -1
  47. package/dist/commands/task/set-status.command.js +22 -2
  48. package/dist/commands/task/shared.d.ts +5 -0
  49. package/dist/commands/task/shared.d.ts.map +1 -1
  50. package/dist/commands/task/shared.js +68 -4
  51. package/dist/commands/task/update.d.ts.map +1 -1
  52. package/dist/commands/task/update.js +6 -1
  53. package/dist/policy/rules/allowlist.d.ts.map +1 -1
  54. package/dist/policy/rules/allowlist.js +9 -0
  55. package/dist/shared/allow-prefix-policy.d.ts +3 -0
  56. package/dist/shared/allow-prefix-policy.d.ts.map +1 -0
  57. package/dist/shared/allow-prefix-policy.js +8 -0
  58. 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 command = opts.runtime === "node" ? "node" : "bash";
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: String(execErr?.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" ? toolEntry.runtime : "";
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":"AAaA,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,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,CAoBD"}
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("sh", ["-lc", command], {
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;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"}
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: "Auto-allow inferred allowlist paths if none are provided (used with --commit-from-comment).",
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,CAqUlB"}
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 autoStatusCommit = (ctx.config.finish_auto_status_commit === true ||
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 || autoStatusCommit,
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: opts.commitAutoAllow || opts.commitAllow.length === 0,
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: opts.statusCommitAutoAllow || opts.statusCommitAllow.length === 0,
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":"AAIA,OAAO,EAAsB,KAAK,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAQpF,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;AAyBF,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,CAsElB"}
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 buildDefaultVerifyStepsDoc(opts) {
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 = buildDefaultVerifyStepsDoc({
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,CA8ElD,CAAC"}
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;AAGtE,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,CAoJ9D,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
+ {"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: "Auto-derive allowlist prefixes from staged paths (commit-from-comment).",
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":"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"}
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,CAgB1F;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,SAAS,GAAG,SAAS,EAAE,CAW7E;AAED,wBAAgB,4BAA4B,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,gBAAgB,GAAG,IAAI,CAc3F;AAED,wBAAgB,qCAAqC,CACnD,IAAI,EAAE,QAAQ,EACd,MAAM,EAAE,gBAAgB,GACvB,IAAI,CAkBN;AAED,MAAM,MAAM,eAAe,GAAG;IAC5B,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,UAAU,EAAE,MAAM,EAAE,CAAC;CACtB,CAAC;AAkCF,wBAAsB,iCAAiC,CAAC,IAAI,EAAE;IAC5D,OAAO,EAAE,IAAI,CAAC,WAAW,EAAE,WAAW,CAAC,CAAC;IACxC,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,EAAE,CAAC;CACrB,GAAG,OAAO,CAAC,IAAI,CAAC,CA2BhB;AAED,wBAAsB,0BAA0B,CAC9C,IAAI,EAAE,QAAQ,EACd,OAAO,EAAE,IAAI,CAAC,WAAW,EAAE,SAAS,GAAG,UAAU,CAAC,GACjD,OAAO,CAAC,eAAe,CAAC,CAuB1B;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,QAAQ,EAAE,GAAG,GAAG,CAAC,MAAM,EAAE,eAAe,CAAC,CAqBpF;AAgBD,wBAAgB,cAAc,CAAC,IAAI,EAAE,QAAQ,EAAE,QAAQ,CAAC,EAAE,eAAe,GAAG,MAAM,CAgBjF;AAED,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAO1E;AAED,wBAAgB,6BAA6B,CAAC,IAAI,EAAE;IAClD,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,OAAO,CAAC;CAChB,GAAG,IAAI,CAUP;AAED,wBAAgB,0BAA0B,CAAC,IAAI,EAAE;IAC/C,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,gBAAgB,CAAC;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,OAAO,CAAC;IACnB,KAAK,EAAE,OAAO,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;CAClB,GAAG,IAAI,CAmBP;AAED,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAgB7F;AAED,wBAAsB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC,CAI5F;AAED,wBAAgB,yBAAyB,CAAC,IAAI,EAAE;IAC9C,MAAM,EAAE,gBAAgB,CAAC,sBAAsB,CAAC,CAAC;IACjD,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,OAAO,CAAC;IACnB,KAAK,EAAE,OAAO,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;CAClB,GAAG,IAAI,CA8BP;AAUD,wBAAgB,6BAA6B,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAK3F;AAED,wBAAsB,cAAc,CAClC,GAAG,EAAE,MAAM,EACX,GAAG,EAAE,MAAM,GACV,OAAO,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC,CAI5C;AAED,wBAAgB,2BAA2B,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAMlE;AAED,wBAAsB,4BAA4B,CAChD,GAAG,EAAE,cAAc,EACnB,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,MAAM,CAAC,CAGjB;AAED,MAAM,MAAM,eAAe,GAAG;IAC5B,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,GAAG,EAAE,MAAM,EAAE,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,OAAO,CAAC;CAChB,CAAC;AAEF,wBAAgB,oBAAoB,CAClC,IAAI,EAAE,MAAM,EAAE,EACd,IAAI,CAAC,EAAE;IAAE,UAAU,CAAC,EAAE,OAAO,CAAA;CAAE,GAC9B,eAAe,CA8EjB;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,QAAQ,GAAG,MAAM,CAiBnD"}
@@ -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 (!ids.includes(trimmed)) {
208
- process.stderr.write(`${warnMessage(`unknown task owner id: ${trimmed} (not found under ${ctx.config.paths.agents_dir}; ` +
209
- `pick an existing agent id or create ${ctx.config.paths.agents_dir}/${trimmed}.json)`)}\n`);
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;AAUpF,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,CAwElB"}
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"}