agentplane 0.2.18 → 0.2.21

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 (103) hide show
  1. package/assets/AGENTS.md +4 -3
  2. package/bin/agentplane.js +24 -0
  3. package/dist/backends/task-backend/load.d.ts +2 -0
  4. package/dist/backends/task-backend/load.d.ts.map +1 -1
  5. package/dist/backends/task-backend/load.js +38 -18
  6. package/dist/cli/command-guide.d.ts.map +1 -1
  7. package/dist/cli/command-guide.js +12 -11
  8. package/dist/cli/run-cli/command-catalog.d.ts +1 -1
  9. package/dist/cli/run-cli/command-catalog.d.ts.map +1 -1
  10. package/dist/cli/run-cli/command-catalog.js +12 -1
  11. package/dist/cli/run-cli/commands/core.d.ts +9 -1
  12. package/dist/cli/run-cli/commands/core.d.ts.map +1 -1
  13. package/dist/cli/run-cli/commands/core.js +268 -8
  14. package/dist/cli/run-cli/commands/init/write-config.d.ts.map +1 -1
  15. package/dist/cli/run-cli/commands/init/write-config.js +2 -0
  16. package/dist/cli/run-cli/commands/init/write-gitignore.d.ts.map +1 -1
  17. package/dist/cli/run-cli/commands/init/write-gitignore.js +3 -18
  18. package/dist/cli/run-cli/commands/init.d.ts +1 -0
  19. package/dist/cli/run-cli/commands/init.d.ts.map +1 -1
  20. package/dist/cli/run-cli/commands/init.js +55 -30
  21. package/dist/cli/run-cli.d.ts.map +1 -1
  22. package/dist/cli/run-cli.js +85 -14
  23. package/dist/commands/commit.command.d.ts.map +1 -1
  24. package/dist/commands/commit.command.js +2 -0
  25. package/dist/commands/commit.spec.d.ts +2 -0
  26. package/dist/commands/commit.spec.d.ts.map +1 -1
  27. package/dist/commands/commit.spec.js +26 -0
  28. package/dist/commands/doctor.run.d.ts.map +1 -1
  29. package/dist/commands/doctor.run.js +15 -6
  30. package/dist/commands/finish.run.d.ts.map +1 -1
  31. package/dist/commands/finish.run.js +2 -0
  32. package/dist/commands/finish.spec.d.ts +2 -0
  33. package/dist/commands/finish.spec.d.ts.map +1 -1
  34. package/dist/commands/finish.spec.js +36 -0
  35. package/dist/commands/guard/impl/allow.d.ts.map +1 -1
  36. package/dist/commands/guard/impl/allow.js +33 -7
  37. package/dist/commands/guard/impl/commands.d.ts +2 -0
  38. package/dist/commands/guard/impl/commands.d.ts.map +1 -1
  39. package/dist/commands/guard/impl/commands.js +24 -4
  40. package/dist/commands/guard/impl/comment-commit.d.ts +1 -0
  41. package/dist/commands/guard/impl/comment-commit.d.ts.map +1 -1
  42. package/dist/commands/guard/impl/comment-commit.js +16 -24
  43. package/dist/commands/release/apply.command.d.ts.map +1 -1
  44. package/dist/commands/release/apply.command.js +79 -3
  45. package/dist/commands/release/plan.command.d.ts.map +1 -1
  46. package/dist/commands/release/plan.command.js +25 -1
  47. package/dist/commands/shared/task-backend.d.ts +3 -0
  48. package/dist/commands/shared/task-backend.d.ts.map +1 -1
  49. package/dist/commands/shared/task-backend.js +4 -1
  50. package/dist/commands/task/block.d.ts.map +1 -1
  51. package/dist/commands/task/block.js +12 -9
  52. package/dist/commands/task/close-duplicate.command.d.ts +14 -0
  53. package/dist/commands/task/close-duplicate.command.d.ts.map +1 -0
  54. package/dist/commands/task/close-duplicate.command.js +102 -0
  55. package/dist/commands/task/close-duplicate.d.ts +14 -0
  56. package/dist/commands/task/close-duplicate.d.ts.map +1 -0
  57. package/dist/commands/task/close-duplicate.js +90 -0
  58. package/dist/commands/task/close-noop.command.d.ts +14 -0
  59. package/dist/commands/task/close-noop.command.d.ts.map +1 -0
  60. package/dist/commands/task/close-noop.command.js +77 -0
  61. package/dist/commands/task/close-noop.d.ts +13 -0
  62. package/dist/commands/task/close-noop.d.ts.map +1 -0
  63. package/dist/commands/task/close-noop.js +77 -0
  64. package/dist/commands/task/finish.d.ts +2 -0
  65. package/dist/commands/task/finish.d.ts.map +1 -1
  66. package/dist/commands/task/finish.js +52 -10
  67. package/dist/commands/task/index.d.ts +3 -0
  68. package/dist/commands/task/index.d.ts.map +1 -1
  69. package/dist/commands/task/index.js +3 -0
  70. package/dist/commands/task/new.d.ts.map +1 -1
  71. package/dist/commands/task/new.js +34 -6
  72. package/dist/commands/task/new.spec.js +1 -1
  73. package/dist/commands/task/plan.d.ts.map +1 -1
  74. package/dist/commands/task/plan.js +2 -3
  75. package/dist/commands/task/set-status.d.ts.map +1 -1
  76. package/dist/commands/task/set-status.js +12 -9
  77. package/dist/commands/task/shared.d.ts +19 -0
  78. package/dist/commands/task/shared.d.ts.map +1 -1
  79. package/dist/commands/task/shared.js +137 -0
  80. package/dist/commands/task/start-ready.command.d.ts +14 -0
  81. package/dist/commands/task/start-ready.command.d.ts.map +1 -0
  82. package/dist/commands/task/start-ready.command.js +77 -0
  83. package/dist/commands/task/start-ready.d.ts +13 -0
  84. package/dist/commands/task/start-ready.d.ts.map +1 -0
  85. package/dist/commands/task/start-ready.js +46 -0
  86. package/dist/commands/task/start.d.ts.map +1 -1
  87. package/dist/commands/task/start.js +13 -11
  88. package/dist/commands/task/update.command.d.ts +1 -0
  89. package/dist/commands/task/update.command.d.ts.map +1 -1
  90. package/dist/commands/task/update.command.js +8 -0
  91. package/dist/commands/task/update.d.ts +1 -0
  92. package/dist/commands/task/update.d.ts.map +1 -1
  93. package/dist/commands/task/update.js +19 -3
  94. package/dist/shared/errors.d.ts +9 -1
  95. package/dist/shared/errors.d.ts.map +1 -1
  96. package/dist/shared/errors.js +3 -1
  97. package/dist/shared/runtime-artifacts.d.ts +3 -0
  98. package/dist/shared/runtime-artifacts.d.ts.map +1 -0
  99. package/dist/shared/runtime-artifacts.js +18 -0
  100. package/dist/usecases/context/resolve-context.d.ts +3 -0
  101. package/dist/usecases/context/resolve-context.d.ts.map +1 -1
  102. package/dist/usecases/context/resolve-context.js +6 -1
  103. package/package.json +5 -4
@@ -1 +1 @@
1
- {"version":3,"file":"write-config.d.ts","sourceRoot":"","sources":["../../../../../src/cli/run-cli/commands/init/write-config.ts"],"names":[],"mappings":"AAOA,MAAM,MAAM,mBAAmB,GAAG;IAChC,OAAO,EAAE,cAAc,GAAG,UAAU,GAAG,YAAY,CAAC;IACpD,gBAAgB,EAAE,KAAK,GAAG,QAAQ,GAAG,MAAM,CAAC;IAC5C,WAAW,EAAE;QACX,SAAS,EAAE,MAAM,CAAC;QAClB,cAAc,EAAE,MAAM,CAAC;QACvB,YAAY,EAAE,MAAM,CAAC;KACtB,CAAC;IACF,eAAe,EAAE,MAAM,EAAE,CAAC;IAC1B,kBAAkB,EAAE,MAAM,EAAE,CAAC;IAC7B,yCAAyC,EAAE,MAAM,EAAE,CAAC;CACrD,CAAC;AAEF,wBAAsB,oBAAoB,CACxC,aAAa,EAAE,MAAM,EACrB,OAAO,EAAE,OAAO,GAAG,SAAS,GAC3B,OAAO,CAAC,IAAI,CAAC,CAOf;AAED,wBAAsB,eAAe,CAAC,IAAI,EAAE;IAC1C,aAAa,EAAE,MAAM,CAAC;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,QAAQ,GAAG,WAAW,CAAC;IACjC,oBAAoB,EAAE,MAAM,CAAC;IAC7B,mBAAmB,EAAE,OAAO,CAAC;IAC7B,sBAAsB,EAAE,OAAO,CAAC;IAChC,qBAAqB,EAAE,OAAO,CAAC;IAC/B,SAAS,EAAE,mBAAmB,CAAC;CAChC,GAAG,OAAO,CAAC,IAAI,CAAC,CAiBhB;AAED,wBAAsB,iBAAiB,CAAC,IAAI,EAAE;IAC5C,OAAO,EAAE,OAAO,GAAG,SAAS,CAAC;IAC7B,WAAW,EAAE,MAAM,CAAC;CACrB,GAAG,OAAO,CAAC,IAAI,CAAC,CAgBhB"}
1
+ {"version":3,"file":"write-config.d.ts","sourceRoot":"","sources":["../../../../../src/cli/run-cli/commands/init/write-config.ts"],"names":[],"mappings":"AAOA,MAAM,MAAM,mBAAmB,GAAG;IAChC,OAAO,EAAE,cAAc,GAAG,UAAU,GAAG,YAAY,CAAC;IACpD,gBAAgB,EAAE,KAAK,GAAG,QAAQ,GAAG,MAAM,CAAC;IAC5C,WAAW,EAAE;QACX,SAAS,EAAE,MAAM,CAAC;QAClB,cAAc,EAAE,MAAM,CAAC;QACvB,YAAY,EAAE,MAAM,CAAC;KACtB,CAAC;IACF,eAAe,EAAE,MAAM,EAAE,CAAC;IAC1B,kBAAkB,EAAE,MAAM,EAAE,CAAC;IAC7B,yCAAyC,EAAE,MAAM,EAAE,CAAC;CACrD,CAAC;AAEF,wBAAsB,oBAAoB,CACxC,aAAa,EAAE,MAAM,EACrB,OAAO,EAAE,OAAO,GAAG,SAAS,GAC3B,OAAO,CAAC,IAAI,CAAC,CAOf;AAED,wBAAsB,eAAe,CAAC,IAAI,EAAE;IAC1C,aAAa,EAAE,MAAM,CAAC;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,QAAQ,GAAG,WAAW,CAAC;IACjC,oBAAoB,EAAE,MAAM,CAAC;IAC7B,mBAAmB,EAAE,OAAO,CAAC;IAC7B,sBAAsB,EAAE,OAAO,CAAC;IAChC,qBAAqB,EAAE,OAAO,CAAC;IAC/B,SAAS,EAAE,mBAAmB,CAAC;CAChC,GAAG,OAAO,CAAC,IAAI,CAAC,CAuBhB;AAED,wBAAsB,iBAAiB,CAAC,IAAI,EAAE;IAC5C,OAAO,EAAE,OAAO,GAAG,SAAS,CAAC;IAC7B,WAAW,EAAE,MAAM,CAAC;CACrB,GAAG,OAAO,CAAC,IAAI,CAAC,CAgBhB"}
@@ -13,6 +13,8 @@ export async function ensureAgentplaneDirs(agentplaneDir, backend) {
13
13
  export async function writeInitConfig(opts) {
14
14
  const rawConfig = defaultConfig();
15
15
  setByDottedKey(rawConfig, "workflow_mode", opts.workflow);
16
+ setByDottedKey(rawConfig, "status_commit_policy", opts.workflow === "branch_pr" ? "confirm" : "warn");
17
+ setByDottedKey(rawConfig, "finish_auto_status_commit", String(opts.workflow === "branch_pr"));
16
18
  setByDottedKey(rawConfig, "tasks_backend.config_path", path.relative(opts.gitRoot, opts.backendConfigPathAbs));
17
19
  setByDottedKey(rawConfig, "agents.approvals.require_plan", String(opts.requirePlanApproval));
18
20
  setByDottedKey(rawConfig, "agents.approvals.require_network", String(opts.requireNetworkApproval));
@@ -1 +1 @@
1
- {"version":3,"file":"write-gitignore.d.ts","sourceRoot":"","sources":["../../../../../src/cli/run-cli/commands/init/write-gitignore.ts"],"names":[],"mappings":"AAiCA,wBAAsB,mBAAmB,CAAC,IAAI,EAAE;IAC9C,OAAO,EAAE,MAAM,CAAC;IAChB,uBAAuB,EAAE,OAAO,CAAC;CAClC,GAAG,OAAO,CAAC,IAAI,CAAC,CAoBhB"}
1
+ {"version":3,"file":"write-gitignore.d.ts","sourceRoot":"","sources":["../../../../../src/cli/run-cli/commands/init/write-gitignore.ts"],"names":[],"mappings":"AAmBA,wBAAsB,mBAAmB,CAAC,IAAI,EAAE;IAC9C,OAAO,EAAE,MAAM,CAAC;IAChB,uBAAuB,EAAE,OAAO,CAAC;CAClC,GAAG,OAAO,CAAC,IAAI,CAAC,CAoBhB"}
@@ -1,5 +1,6 @@
1
1
  import path from "node:path";
2
2
  import { readFile } from "node:fs/promises";
3
+ import { AGENT_PROMPT_GITIGNORE_LINES, RUNTIME_GITIGNORE_LINES, } from "../../../../shared/runtime-artifacts.js";
3
4
  import { writeTextIfChanged } from "../../../../shared/write-if-changed.js";
4
5
  async function readTextIfExists(filePath) {
5
6
  try {
@@ -12,28 +13,12 @@ async function readTextIfExists(filePath) {
12
13
  throw err;
13
14
  }
14
15
  }
15
- const RUNTIME_IGNORE_LINES = [
16
- "# agentplane: ignore runtime/transient workspace artifacts",
17
- ".env",
18
- ".agentplane/worktrees",
19
- ".agentplane/cache",
20
- ".agentplane/recipes-cache",
21
- ".agentplane/.upgrade",
22
- ".agentplane/.release",
23
- ".agentplane/upgrade",
24
- ".agentplane/tasks.json",
25
- ];
26
- const AGENT_PROMPT_IGNORE_LINES = [
27
- "# agentplane: ignore local agent prompts/templates",
28
- "AGENTS.md",
29
- ".agentplane/agents/",
30
- ];
31
16
  export async function ensureInitGitignore(opts) {
32
17
  const gitignorePath = path.join(opts.gitRoot, ".gitignore");
33
18
  const existing = (await readTextIfExists(gitignorePath)) ?? "";
34
19
  const ensuredLines = opts.includeAgentPromptFiles
35
- ? [...RUNTIME_IGNORE_LINES, ...AGENT_PROMPT_IGNORE_LINES]
36
- : [...RUNTIME_IGNORE_LINES];
20
+ ? [...RUNTIME_GITIGNORE_LINES, ...AGENT_PROMPT_GITIGNORE_LINES]
21
+ : [...RUNTIME_GITIGNORE_LINES];
37
22
  const existingLines = existing.split(/\r?\n/);
38
23
  const existingSet = new Set(existingLines.map((line) => line.trimEnd()));
39
24
  const missing = ensuredLines.filter((line) => !existingSet.has(line));
@@ -1,6 +1,7 @@
1
1
  import type { CommandHandler, CommandSpec } from "../../spec/spec.js";
2
2
  import { type ExecutionProfile } from "@agentplaneorg/core";
3
3
  type InitFlags = {
4
+ setupProfile?: "prod" | "dev";
4
5
  ide?: "codex" | "cursor" | "windsurf";
5
6
  workflow?: "direct" | "branch_pr";
6
7
  backend?: "local" | "redmine";
@@ -1 +1 @@
1
- {"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../../../src/cli/run-cli/commands/init.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAItE,OAAO,EAGL,KAAK,gBAAgB,EACtB,MAAM,qBAAqB,CAAC;AAa7B,KAAK,SAAS,GAAG;IACf,GAAG,CAAC,EAAE,OAAO,GAAG,QAAQ,GAAG,UAAU,CAAC;IACtC,QAAQ,CAAC,EAAE,QAAQ,GAAG,WAAW,CAAC;IAClC,OAAO,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAC9B,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,sBAAsB,CAAC,EAAE,OAAO,CAAC;IACjC,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,gBAAgB,CAAC,EAAE,gBAAgB,CAAC;IACpC,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,GAAG,EAAE,OAAO,CAAC;CACd,CAAC;AAsBF,KAAK,UAAU,GAAG,IAAI,CAAC,SAAS,EAAE,KAAK,CAAC,GAAG;IAAE,GAAG,EAAE,OAAO,CAAA;CAAE,CAAC;AAE5D,eAAO,MAAM,QAAQ,EAAE,WAAW,CAAC,UAAU,CA2K5C,CAAC;AAEF,eAAO,MAAM,OAAO,EAAE,cAAc,CAAC,UAAU,CACmB,CAAC"}
1
+ {"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../../../src/cli/run-cli/commands/init.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAItE,OAAO,EAGL,KAAK,gBAAgB,EACtB,MAAM,qBAAqB,CAAC;AAa7B,KAAK,SAAS,GAAG;IACf,YAAY,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC;IAC9B,GAAG,CAAC,EAAE,OAAO,GAAG,QAAQ,GAAG,UAAU,CAAC;IACtC,QAAQ,CAAC,EAAE,QAAQ,GAAG,WAAW,CAAC;IAClC,OAAO,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAC9B,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,sBAAsB,CAAC,EAAE,OAAO,CAAC;IACjC,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,gBAAgB,CAAC,EAAE,gBAAgB,CAAC;IACpC,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,GAAG,EAAE,OAAO,CAAC;CACd,CAAC;AAsBF,KAAK,UAAU,GAAG,IAAI,CAAC,SAAS,EAAE,KAAK,CAAC,GAAG;IAAE,GAAG,EAAE,OAAO,CAAA;CAAE,CAAC;AAE5D,eAAO,MAAM,QAAQ,EAAE,WAAW,CAAC,UAAU,CAoL5C,CAAC;AAEF,eAAO,MAAM,OAAO,EAAE,cAAc,CAAC,UAAU,CACmB,CAAC"}
@@ -45,6 +45,13 @@ export const initSpec = {
45
45
  summary: "Initialize agentplane project files under .agentplane/.",
46
46
  description: "Creates .agentplane/ config, backend stubs, and agent templates in the target directory. If the target directory is not a git repository, it initializes one and (by default) writes an initial install commit. Use --gitignore-agents to keep agent templates local (gitignored) and skip the install commit. In interactive mode it prompts for missing inputs; use --yes for non-interactive mode.",
47
47
  options: [
48
+ {
49
+ kind: "string",
50
+ name: "setup-profile",
51
+ valueHint: "<prod|dev>",
52
+ choices: ["prod", "dev"],
53
+ description: "Interactive preset. prod asks only essential questions; dev asks the full setup questionnaire.",
54
+ },
48
55
  {
49
56
  kind: "string",
50
57
  name: "ide",
@@ -167,6 +174,7 @@ export const initSpec = {
167
174
  const requireVerifyRaw = raw.opts["require-verify-approval"];
168
175
  const recipesRaw = raw.opts.recipes;
169
176
  return {
177
+ setupProfile: raw.opts["setup-profile"],
170
178
  ide: raw.opts.ide,
171
179
  workflow: raw.opts.workflow,
172
180
  backend: raw.opts.backend,
@@ -226,6 +234,7 @@ async function cmdInit(opts) {
226
234
  let requireVerifyApproval = flags.requireVerifyApproval ?? defaults.requireVerifyApproval;
227
235
  let executionProfile = flags.executionProfile ?? defaults.executionProfile;
228
236
  let strictUnsafeConfirm = flags.strictUnsafeConfirm ?? defaults.strictUnsafeConfirm;
237
+ let setupProfile = flags.setupProfile ?? "prod";
229
238
  const isInteractive = process.stdin.isTTY && !flags.yes;
230
239
  if (!process.stdin.isTTY &&
231
240
  !flags.yes &&
@@ -257,9 +266,13 @@ async function cmdInit(opts) {
257
266
  return result;
258
267
  };
259
268
  process.stdout.write(renderInitWelcome());
269
+ process.stdout.write(renderInitSection("Setup Profile", "Choose how much init should ask. prod keeps the flow compact; dev exposes full controls."));
270
+ if (!flags.setupProfile) {
271
+ setupProfile = (await askChoice("Setup profile", ["prod", "dev"], setupProfile));
272
+ }
260
273
  process.stdout.write(renderInitSection("Workflow", "Choose how branches/backends/approvals should be initialized for this repository."));
261
274
  ide = flags.ide ?? defaults.ide;
262
- if (!flags.workflow) {
275
+ if (!flags.workflow && setupProfile === "dev") {
263
276
  const choice = await askChoice("Workflow mode", ["direct", "branch_pr"], workflow);
264
277
  workflow = choice === "branch_pr" ? "branch_pr" : "direct";
265
278
  }
@@ -267,36 +280,48 @@ async function cmdInit(opts) {
267
280
  const choice = await askChoice("Task backend", ["local", "redmine"], backend);
268
281
  backend = choice === "redmine" ? "redmine" : "local";
269
282
  }
270
- if (flags.hooks === undefined) {
271
- hooks = await askYesNo("Install managed git hooks now?", hooks);
272
- }
273
- process.stdout.write(renderInitSection("Execution Profile", "Set default autonomy/effort for agents. You can change this later in config."));
274
- if (!flags.executionProfile) {
275
- executionProfile = (await askChoice("Execution profile", ["conservative", "balanced", "aggressive"], executionProfile));
276
- }
277
- if (flags.strictUnsafeConfirm === undefined) {
278
- strictUnsafeConfirm = await askYesNo("Require strict explicit confirmation for extra unsafe actions?", strictUnsafeConfirm);
279
- }
280
- process.stdout.write(renderInitSection("Approvals", "Control whether plan/network/verification actions require explicit approval by default."));
281
- if (flags.requirePlanApproval === undefined) {
282
- requirePlanApproval = await askYesNo("Require plan approval before work starts?", requirePlanApproval);
283
- }
284
- if (flags.requireNetworkApproval === undefined) {
285
- requireNetworkApproval = await askYesNo("Require explicit approval for network actions?", requireNetworkApproval);
286
- }
287
- if (flags.requireVerifyApproval === undefined) {
288
- requireVerifyApproval = await askYesNo("Require explicit approval before recording verification?", requireVerifyApproval);
283
+ if (setupProfile === "dev") {
284
+ if (flags.hooks === undefined) {
285
+ hooks = await askYesNo("Install managed git hooks now?", hooks);
286
+ }
287
+ process.stdout.write(renderInitSection("Execution Profile", "Set default autonomy/effort for agents. You can change this later in config."));
288
+ if (!flags.executionProfile) {
289
+ executionProfile = (await askChoice("Execution profile", ["conservative", "balanced", "aggressive"], executionProfile));
290
+ }
291
+ if (flags.strictUnsafeConfirm === undefined) {
292
+ strictUnsafeConfirm = await askYesNo("Require strict explicit confirmation for extra unsafe actions?", strictUnsafeConfirm);
293
+ }
294
+ process.stdout.write(renderInitSection("Approvals", "Control whether plan/network/verification actions require explicit approval by default."));
295
+ if (flags.requirePlanApproval === undefined) {
296
+ requirePlanApproval = await askYesNo("Require plan approval before work starts?", requirePlanApproval);
297
+ }
298
+ if (flags.requireNetworkApproval === undefined) {
299
+ requireNetworkApproval = await askYesNo("Require explicit approval for network actions?", requireNetworkApproval);
300
+ }
301
+ if (flags.requireVerifyApproval === undefined) {
302
+ requireVerifyApproval = await askYesNo("Require explicit approval before recording verification?", requireVerifyApproval);
303
+ }
304
+ process.stdout.write(renderInitSection("Recipes", "Optional: install recipe packs now (comma-separated IDs) or choose none."));
305
+ if (!flags.recipes) {
306
+ process.stdout.write(`${renderBundledRecipesHint()}\n`);
307
+ const answer = await askInput("Install optional recipes (comma separated, or none): ");
308
+ recipes = answer
309
+ ? answer
310
+ .split(",")
311
+ .map((item) => item.trim())
312
+ .filter(Boolean)
313
+ : [];
314
+ }
289
315
  }
290
- process.stdout.write(renderInitSection("Recipes", "Optional: install recipe packs now (comma-separated IDs) or choose none."));
291
- if (!flags.recipes) {
292
- process.stdout.write(`${renderBundledRecipesHint()}\n`);
293
- const answer = await askInput("Install optional recipes (comma separated, or none): ");
294
- recipes = answer
295
- ? answer
296
- .split(",")
297
- .map((item) => item.trim())
298
- .filter(Boolean)
299
- : [];
316
+ else {
317
+ hooks = flags.hooks ?? defaults.hooks;
318
+ recipes = flags.recipes ?? defaults.recipes;
319
+ requirePlanApproval = flags.requirePlanApproval ?? defaults.requirePlanApproval;
320
+ requireNetworkApproval = flags.requireNetworkApproval ?? defaults.requireNetworkApproval;
321
+ requireVerifyApproval = flags.requireVerifyApproval ?? defaults.requireVerifyApproval;
322
+ executionProfile = flags.executionProfile ?? defaults.executionProfile;
323
+ strictUnsafeConfirm = flags.strictUnsafeConfirm ?? defaults.strictUnsafeConfirm;
324
+ process.stdout.write(renderInitSection("Defaults Applied", "Using compact prod defaults for hooks, approvals, execution profile, and recipes."));
300
325
  }
301
326
  }
302
327
  if (flags.yes) {
@@ -1 +1 @@
1
- {"version":3,"file":"run-cli.d.ts","sourceRoot":"","sources":["../../src/cli/run-cli.ts"],"names":[],"mappings":"AAoWA,wBAAsB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CA0K5D"}
1
+ {"version":3,"file":"run-cli.d.ts","sourceRoot":"","sources":["../../src/cli/run-cli.ts"],"names":[],"mappings":"AAibA,wBAAsB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAmL5D"}
@@ -134,9 +134,9 @@ function matchCommandCatalog(tokens) {
134
134
  return best;
135
135
  }
136
136
  function writeError(err, jsonErrors) {
137
- const hint = renderErrorHint(err);
137
+ const guidance = resolveErrorGuidance(err);
138
138
  if (jsonErrors) {
139
- process.stdout.write(`${formatJsonError(err)}\n`);
139
+ process.stdout.write(`${formatJsonError(err, { hint: guidance.hint, nextAction: guidance.nextAction })}\n`);
140
140
  }
141
141
  else {
142
142
  const header = `error [${err.code}]`;
@@ -146,8 +146,11 @@ function writeError(err, jsonErrors) {
146
146
  else {
147
147
  process.stderr.write(`${header}: ${err.message}\n`);
148
148
  }
149
- if (hint) {
150
- process.stderr.write(`hint: ${hint}\n`);
149
+ if (guidance.hint) {
150
+ process.stderr.write(`hint: ${guidance.hint}\n`);
151
+ }
152
+ if (guidance.nextAction) {
153
+ process.stderr.write(`next_action: ${guidance.nextAction.command} (${guidance.nextAction.reason})\n`);
151
154
  }
152
155
  }
153
156
  }
@@ -158,33 +161,92 @@ function resolveAgentplaneHome() {
158
161
  return overridden;
159
162
  return path.join(os.homedir(), ".agentplane");
160
163
  }
161
- function renderErrorHint(err) {
164
+ function resolveErrorGuidance(err) {
162
165
  const command = typeof err.context?.command === "string" ? err.context.command : undefined;
163
166
  const usage = command ? `agentplane help ${command} --compact` : "agentplane help";
164
167
  switch (err.code) {
165
168
  case "E_USAGE": {
166
- return `See \`${usage}\` for usage.`;
169
+ return {
170
+ hint: `See \`${usage}\` for usage.`,
171
+ nextAction: {
172
+ command: usage,
173
+ reason: "inspect required arguments and flags",
174
+ reasonCode: "usage_help",
175
+ },
176
+ };
167
177
  }
168
178
  case "E_GIT": {
169
179
  if (command?.startsWith("branch")) {
170
- return "Check git repo/branch; run `git branch` or pass --root <path>.";
180
+ return {
181
+ hint: "Check git repo/branch; run `git branch` or pass --root <path>.",
182
+ nextAction: {
183
+ command: "git branch",
184
+ reason: "inspect repository branch state",
185
+ reasonCode: "git_branch_state",
186
+ },
187
+ };
171
188
  }
172
189
  if (command === "guard commit" || command === "commit") {
173
- return "Check git status/index; stage changes and retry.";
190
+ return {
191
+ hint: "Check git status/index; stage changes and retry.",
192
+ nextAction: {
193
+ command: "git status --short",
194
+ reason: "inspect staged/unstaged changes before commit",
195
+ reasonCode: "git_index_state",
196
+ },
197
+ };
174
198
  }
175
- return "Check git repo context; pass --root <path> if needed.";
199
+ return {
200
+ hint: "Check git repo context; pass --root <path> if needed.",
201
+ nextAction: {
202
+ command: "git status --short --untracked-files=no",
203
+ reason: "confirm repository context and tracked changes",
204
+ reasonCode: "git_context",
205
+ },
206
+ };
176
207
  }
177
208
  case "E_NETWORK": {
178
- return "Check network access and credentials.";
209
+ return {
210
+ hint: "Check network access and credentials.",
211
+ nextAction: {
212
+ command: "agentplane preflight --json",
213
+ reason: "recheck approvals and network requirements",
214
+ reasonCode: "network_gate",
215
+ },
216
+ };
179
217
  }
180
218
  case "E_BACKEND": {
181
219
  if (command?.includes("sync")) {
182
- return "Check backend config under .agentplane/backends and retry.";
220
+ return {
221
+ hint: "Check backend config under .agentplane/backends and retry.",
222
+ nextAction: {
223
+ command: "agentplane config show",
224
+ reason: "verify backend config path and active settings",
225
+ reasonCode: "backend_sync_config",
226
+ },
227
+ };
183
228
  }
184
- return "Check backend config under .agentplane/backends.";
229
+ return {
230
+ hint: "Check backend config under .agentplane/backends.",
231
+ nextAction: {
232
+ command: "agentplane config show",
233
+ reason: "inspect backend configuration",
234
+ reasonCode: "backend_config",
235
+ },
236
+ };
237
+ }
238
+ case "E_VALIDATION": {
239
+ return {
240
+ hint: "Fix invalid config/input shape and rerun.",
241
+ nextAction: {
242
+ command: "agentplane preflight --json",
243
+ reason: "pinpoint validation failure in one report",
244
+ reasonCode: "validation_preflight",
245
+ },
246
+ };
185
247
  }
186
248
  default: {
187
- return undefined;
249
+ return {};
188
250
  }
189
251
  }
190
252
  }
@@ -395,7 +457,16 @@ export async function runCli(argv) {
395
457
  });
396
458
  let ctxPromise = null;
397
459
  const getCtx = async (commandForErrorContext) => {
398
- ctxPromise ??= resolveContext({ cwd, rootOverride: globals.root ?? null });
460
+ ctxPromise ??= (async () => {
461
+ const resolvedProject = await getResolvedProject(commandForErrorContext);
462
+ const loadedConfig = await getLoadedConfig(commandForErrorContext);
463
+ return await resolveContext({
464
+ cwd,
465
+ rootOverride: globals.root ?? null,
466
+ resolvedProject,
467
+ config: loadedConfig.config,
468
+ });
469
+ })();
399
470
  try {
400
471
  return await ctxPromise;
401
472
  }
@@ -1 +1 @@
1
- {"version":3,"file":"commit.command.d.ts","sourceRoot":"","sources":["../../src/commands/commit.command.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AACtD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAI/D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAE9C,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,cAAc,CAAC,IACrE,KAAK,UAAU,EAAE,GAAG,YAAY,KAAG,OAAO,CAAC,MAAM,CAAC,CAoBjE"}
1
+ {"version":3,"file":"commit.command.d.ts","sourceRoot":"","sources":["../../src/commands/commit.command.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AACtD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAI/D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAE9C,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,cAAc,CAAC,IACrE,KAAK,UAAU,EAAE,GAAG,YAAY,KAAG,OAAO,CAAC,MAAM,CAAC,CAsBjE"}
@@ -19,6 +19,8 @@ export function makeRunCommitHandler(getCtx) {
19
19
  allowCI: p.allowCI,
20
20
  requireClean: p.requireClean,
21
21
  quiet: p.quiet,
22
+ closeUnstageOthers: p.closeUnstageOthers,
23
+ closeCheckOnly: p.closeCheckOnly,
22
24
  });
23
25
  };
24
26
  }
@@ -13,6 +13,8 @@ export type CommitParsed = {
13
13
  allowCI: boolean;
14
14
  requireClean: boolean;
15
15
  quiet: boolean;
16
+ closeUnstageOthers: boolean;
17
+ closeCheckOnly: boolean;
16
18
  };
17
19
  export declare const commitSpec: CommandSpec<CommitParsed>;
18
20
  //# sourceMappingURL=commit.spec.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"commit.spec.d.ts","sourceRoot":"","sources":["../../src/commands/commit.spec.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAGvD,MAAM,MAAM,YAAY,GAAG;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,OAAO,CAAC;IACf,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,SAAS,EAAE,OAAO,CAAC;IACnB,UAAU,EAAE,OAAO,CAAC;IACpB,SAAS,EAAE,OAAO,CAAC;IACnB,WAAW,EAAE,OAAO,CAAC;IACrB,WAAW,EAAE,OAAO,CAAC;IACrB,UAAU,EAAE,OAAO,CAAC;IACpB,OAAO,EAAE,OAAO,CAAC;IACjB,YAAY,EAAE,OAAO,CAAC;IACtB,KAAK,EAAE,OAAO,CAAC;CAChB,CAAC;AAEF,eAAO,MAAM,UAAU,EAAE,WAAW,CAAC,YAAY,CAuHhD,CAAC"}
1
+ {"version":3,"file":"commit.spec.d.ts","sourceRoot":"","sources":["../../src/commands/commit.spec.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAGvD,MAAM,MAAM,YAAY,GAAG;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,OAAO,CAAC;IACf,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,SAAS,EAAE,OAAO,CAAC;IACnB,UAAU,EAAE,OAAO,CAAC;IACpB,SAAS,EAAE,OAAO,CAAC;IACnB,WAAW,EAAE,OAAO,CAAC;IACrB,WAAW,EAAE,OAAO,CAAC;IACrB,UAAU,EAAE,OAAO,CAAC;IACpB,OAAO,EAAE,OAAO,CAAC;IACjB,YAAY,EAAE,OAAO,CAAC;IACtB,KAAK,EAAE,OAAO,CAAC;IACf,kBAAkB,EAAE,OAAO,CAAC;IAC5B,cAAc,EAAE,OAAO,CAAC;CACzB,CAAC;AAEF,eAAO,MAAM,UAAU,EAAE,WAAW,CAAC,YAAY,CAmJhD,CAAC"}
@@ -18,6 +18,18 @@ export const commitSpec = {
18
18
  default: false,
19
19
  description: "Generate a deterministic close commit message from task snapshot + verification + recorded implementation commit; stages only the task README.",
20
20
  },
21
+ {
22
+ kind: "boolean",
23
+ name: "unstage-others",
24
+ default: false,
25
+ description: "With --close: unstage any currently staged paths before staging the task README.",
26
+ },
27
+ {
28
+ kind: "boolean",
29
+ name: "check-only",
30
+ default: false,
31
+ description: "With --close: run preflight checks and print the planned close commit subject without creating a commit.",
32
+ },
21
33
  {
22
34
  kind: "string",
23
35
  name: "allow",
@@ -92,6 +104,18 @@ export const commitSpec = {
92
104
  message: "Use either --message or --close (not both).",
93
105
  });
94
106
  }
107
+ if (!close && raw.opts["unstage-others"] === true) {
108
+ throw usageError({
109
+ spec: commitSpec,
110
+ message: "--unstage-others requires --close.",
111
+ });
112
+ }
113
+ if (!close && raw.opts["check-only"] === true) {
114
+ throw usageError({
115
+ spec: commitSpec,
116
+ message: "--check-only requires --close.",
117
+ });
118
+ }
95
119
  const allow = raw.opts.allow;
96
120
  if (Array.isArray(allow) && allow.some((s) => typeof s === "string" && s.trim() === "")) {
97
121
  throw usageError({ spec: commitSpec, message: "Invalid value for --allow: empty." });
@@ -115,5 +139,7 @@ export const commitSpec = {
115
139
  allowCI: raw.opts["allow-ci"] === true,
116
140
  requireClean: raw.opts["require-clean"] === true,
117
141
  quiet: raw.opts.quiet === true,
142
+ closeUnstageOthers: raw.opts["unstage-others"] === true,
143
+ closeCheckOnly: raw.opts["check-only"] === true,
118
144
  }),
119
145
  };
@@ -1 +1 @@
1
- {"version":3,"file":"doctor.run.d.ts","sourceRoot":"","sources":["../../src/commands/doctor.run.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAI1D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AA+OrD,eAAO,MAAM,SAAS,EAAE,cAAc,CAAC,YAAY,CAwBlD,CAAC"}
1
+ {"version":3,"file":"doctor.run.d.ts","sourceRoot":"","sources":["../../src/commands/doctor.run.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAK1D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAsPrD,eAAO,MAAM,SAAS,EAAE,cAAc,CAAC,YAAY,CAwBlD,CAAC"}
@@ -2,6 +2,7 @@ import fs from "node:fs/promises";
2
2
  import path from "node:path";
3
3
  import { resolveProject } from "@agentplaneorg/core";
4
4
  import { warnMessage, successMessage } from "../cli/output.js";
5
+ import { RUNTIME_GITIGNORE_LINES } from "../shared/runtime-artifacts.js";
5
6
  import { execFileAsync, gitEnv } from "./shared/git.js";
6
7
  import { loadCommandContext } from "./shared/task-backend.js";
7
8
  async function listTsFiles(rootDir) {
@@ -132,13 +133,21 @@ async function safeFixGitignore(repoRoot) {
132
133
  return { changed: false, note: "Skip: .gitignore not found." };
133
134
  }
134
135
  const lines = existing.split(/\r?\n/);
135
- const entry = ".agentplane/.upgrade/";
136
- if (lines.some((l) => l.trim() === entry)) {
137
- return { changed: false, note: "OK: .gitignore already ignores .agentplane/.upgrade/." };
138
- }
139
- const next = `${existing.trimEnd()}\n${entry}\n`;
136
+ const existingSet = new Set(lines.map((line) => line.trimEnd()));
137
+ const missing = RUNTIME_GITIGNORE_LINES.filter((line) => !existingSet.has(line));
138
+ if (missing.length === 0) {
139
+ return { changed: false, note: "OK: .gitignore already contains agentplane runtime ignores." };
140
+ }
141
+ const nextLines = [...lines];
142
+ if (nextLines.length > 0 && nextLines.at(-1) !== "")
143
+ nextLines.push("");
144
+ nextLines.push(...missing, "");
145
+ const next = `${nextLines.join("\n")}`.replaceAll(/\n{2,}$/g, "\n");
140
146
  await fs.writeFile(gitignorePath, next, "utf8");
141
- return { changed: true, note: "Fixed: added .agentplane/.upgrade/ to .gitignore." };
147
+ return {
148
+ changed: true,
149
+ note: `Fixed: added agentplane runtime ignores to .gitignore (${missing.length} lines).`,
150
+ };
142
151
  }
143
152
  async function safeFixTaskIndex(repoRoot) {
144
153
  try {
@@ -1 +1 @@
1
- {"version":3,"file":"finish.run.d.ts","sourceRoot":"","sources":["../../src/commands/finish.run.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAEtD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAG/D,OAAO,EAAc,KAAK,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAEjE,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,cAAc,CAAC,IACrE,KAAK,UAAU,EAAE,GAAG,YAAY,KAAG,OAAO,CAAC,MAAM,CAAC,CAoCjE"}
1
+ {"version":3,"file":"finish.run.d.ts","sourceRoot":"","sources":["../../src/commands/finish.run.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAEtD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAG/D,OAAO,EAAc,KAAK,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAEjE,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,cAAc,CAAC,IACrE,KAAK,UAAU,EAAE,GAAG,YAAY,KAAG,OAAO,CAAC,MAAM,CAAC,CAsCjE"}
@@ -35,6 +35,8 @@ export function makeRunFinishHandler(getCtx) {
35
35
  statusCommitAutoAllow: p.statusCommitAutoAllow,
36
36
  statusCommitRequireClean: p.statusCommitRequireClean,
37
37
  confirmStatusCommit: p.confirmStatusCommit,
38
+ closeCommit: p.closeCommit,
39
+ closeUnstageOthers: p.closeUnstageOthers,
38
40
  quiet: p.quiet,
39
41
  });
40
42
  };
@@ -21,6 +21,8 @@ export type FinishParsed = {
21
21
  statusCommitAutoAllow: boolean;
22
22
  statusCommitRequireClean: boolean;
23
23
  confirmStatusCommit: boolean;
24
+ closeCommit: boolean;
25
+ closeUnstageOthers: boolean;
24
26
  quiet: boolean;
25
27
  };
26
28
  export declare const finishSpec: CommandSpec<FinishParsed>;
@@ -1 +1 @@
1
- {"version":3,"file":"finish.spec.d.ts","sourceRoot":"","sources":["../../src/commands/finish.spec.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAKvD,MAAM,MAAM,YAAY,GAAG;IACzB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,KAAK,GAAG,KAAK,GAAG,MAAM,CAAC;IAC9B,QAAQ,EAAE,OAAO,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,OAAO,CAAC;IACf,GAAG,EAAE,OAAO,CAAC;IACb,iBAAiB,EAAE,OAAO,CAAC;IAC3B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,eAAe,EAAE,OAAO,CAAC;IACzB,gBAAgB,EAAE,OAAO,CAAC;IAC1B,kBAAkB,EAAE,OAAO,CAAC;IAC5B,YAAY,EAAE,OAAO,CAAC;IACtB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,iBAAiB,EAAE,MAAM,EAAE,CAAC;IAC5B,qBAAqB,EAAE,OAAO,CAAC;IAC/B,wBAAwB,EAAE,OAAO,CAAC;IAClC,mBAAmB,EAAE,OAAO,CAAC;IAC7B,KAAK,EAAE,OAAO,CAAC;CAChB,CAAC;AAEF,eAAO,MAAM,UAAU,EAAE,WAAW,CAAC,YAAY,CA6MhD,CAAC"}
1
+ {"version":3,"file":"finish.spec.d.ts","sourceRoot":"","sources":["../../src/commands/finish.spec.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAKvD,MAAM,MAAM,YAAY,GAAG;IACzB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,KAAK,GAAG,KAAK,GAAG,MAAM,CAAC;IAC9B,QAAQ,EAAE,OAAO,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,OAAO,CAAC;IACf,GAAG,EAAE,OAAO,CAAC;IACb,iBAAiB,EAAE,OAAO,CAAC;IAC3B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,eAAe,EAAE,OAAO,CAAC;IACzB,gBAAgB,EAAE,OAAO,CAAC;IAC1B,kBAAkB,EAAE,OAAO,CAAC;IAC5B,YAAY,EAAE,OAAO,CAAC;IACtB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,iBAAiB,EAAE,MAAM,EAAE,CAAC;IAC5B,qBAAqB,EAAE,OAAO,CAAC;IAC/B,wBAAwB,EAAE,OAAO,CAAC;IAClC,mBAAmB,EAAE,OAAO,CAAC;IAC7B,WAAW,EAAE,OAAO,CAAC;IACrB,kBAAkB,EAAE,OAAO,CAAC;IAC5B,KAAK,EAAE,OAAO,CAAC;CAChB,CAAC;AAEF,eAAO,MAAM,UAAU,EAAE,WAAW,CAAC,YAAY,CAqPhD,CAAC"}
@@ -136,6 +136,18 @@ export const finishSpec = {
136
136
  default: false,
137
137
  description: "Confirm status commit creation when status_commit_policy=confirm (used with --commit-from-comment or --status-commit).",
138
138
  },
139
+ {
140
+ kind: "boolean",
141
+ name: "close-commit",
142
+ default: false,
143
+ description: "After finishing, run a deterministic close commit for the task README (single-task only).",
144
+ },
145
+ {
146
+ kind: "boolean",
147
+ name: "close-unstage-others",
148
+ default: false,
149
+ description: "With --close-commit: unstage any currently staged paths before staging the task README.",
150
+ },
139
151
  { kind: "boolean", name: "quiet", default: false, description: "Suppress output." },
140
152
  ],
141
153
  examples: [
@@ -160,6 +172,28 @@ export const finishSpec = {
160
172
  message: "--commit-from-comment/--status-commit requires exactly one task id",
161
173
  });
162
174
  }
175
+ if (raw.opts["close-commit"] === true && taskIds.length !== 1) {
176
+ throw usageError({
177
+ spec: finishSpec,
178
+ command: "finish",
179
+ message: "--close-commit requires exactly one task id",
180
+ });
181
+ }
182
+ if (raw.opts["close-commit"] === true &&
183
+ (raw.opts["commit-from-comment"] === true || raw.opts["status-commit"] === true)) {
184
+ throw usageError({
185
+ spec: finishSpec,
186
+ command: "finish",
187
+ message: "--close-commit cannot be combined with --commit-from-comment/--status-commit",
188
+ });
189
+ }
190
+ if (raw.opts["close-unstage-others"] === true && raw.opts["close-commit"] !== true) {
191
+ throw usageError({
192
+ spec: finishSpec,
193
+ command: "finish",
194
+ message: "--close-unstage-others requires --close-commit",
195
+ });
196
+ }
163
197
  const hasMeta = typeof raw.opts.result === "string" ||
164
198
  typeof raw.opts.risk === "string" ||
165
199
  raw.opts.breaking === true;
@@ -195,6 +229,8 @@ export const finishSpec = {
195
229
  statusCommitAutoAllow: raw.opts["status-commit-auto-allow"] === true,
196
230
  statusCommitRequireClean: raw.opts["status-commit-require-clean"] === true,
197
231
  confirmStatusCommit: raw.opts["confirm-status-commit"] === true,
232
+ closeCommit: raw.opts["close-commit"] === true,
233
+ closeUnstageOthers: raw.opts["close-unstage-others"] === true,
198
234
  quiet: raw.opts.quiet === true,
199
235
  }),
200
236
  };
@@ -1 +1 @@
1
- {"version":3,"file":"allow.d.ts","sourceRoot":"","sources":["../../../../src/commands/guard/impl/allow.ts"],"names":[],"mappings":"AAMA,OAAO,EAAsB,KAAK,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAEvF,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,CAS9D;AAED,wBAAsB,qBAAqB,CAAC,IAAI,EAAE;IAChD,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAOpB;AAED,wBAAsB,cAAc,CAAC,IAAI,EAAE;IACzC,GAAG,CAAC,EAAE,cAAc,CAAC;IACrB,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB,GAAG,OAAO,CAAC,IAAI,CAAC,CAqBhB;AAED,wBAAsB,cAAc,CAAC,IAAI,EAAE;IACzC,GAAG,EAAE,cAAc,CAAC;IACpB,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,UAAU,EAAE,OAAO,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;CACnB,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAoCpB"}
1
+ {"version":3,"file":"allow.d.ts","sourceRoot":"","sources":["../../../../src/commands/guard/impl/allow.ts"],"names":[],"mappings":"AAMA,OAAO,EAAsB,KAAK,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAkBvF,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,CAe9D;AAED,wBAAsB,qBAAqB,CAAC,IAAI,EAAE;IAChD,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAOpB;AAED,wBAAsB,cAAc,CAAC,IAAI,EAAE;IACzC,GAAG,CAAC,EAAE,cAAc,CAAC;IACrB,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB,GAAG,OAAO,CAAC,IAAI,CAAC,CAqBhB;AAED,wBAAsB,cAAc,CAAC,IAAI,EAAE;IACzC,GAAG,EAAE,cAAc,CAAC;IACpB,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,UAAU,EAAE,OAAO,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;CACnB,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CA2CpB"}
@@ -4,18 +4,37 @@ import { gitPathIsUnderPrefix, normalizeGitPathPrefix } from "../../../shared/gi
4
4
  import { CliError } from "../../../shared/errors.js";
5
5
  import { GitContext } from "../../shared/git-context.js";
6
6
  import { loadCommandContext } from "../../shared/task-backend.js";
7
+ function normalizeAllowPrefixes(prefixes) {
8
+ const unique = [
9
+ ...new Set(prefixes.map((prefix) => normalizeGitPathPrefix(prefix)).filter(Boolean)),
10
+ ].toSorted((a, b) => (a.length === b.length ? a.localeCompare(b) : a.length - b.length));
11
+ // "." is repo-wide and makes all other prefixes redundant.
12
+ if (unique.includes("."))
13
+ return ["."];
14
+ const compact = [];
15
+ for (const prefix of unique) {
16
+ if (compact.some((existing) => gitPathIsUnderPrefix(prefix, existing)))
17
+ continue;
18
+ compact.push(prefix);
19
+ }
20
+ return compact;
21
+ }
7
22
  export function suggestAllowPrefixes(paths) {
8
23
  const out = new Set();
9
24
  for (const filePath of paths) {
10
25
  if (!filePath)
11
26
  continue;
12
- const idx = filePath.lastIndexOf("/");
13
- if (idx <= 0)
14
- out.add(filePath);
15
- else
16
- out.add(filePath.slice(0, idx));
27
+ const trimmed = filePath.trim();
28
+ if (!trimmed)
29
+ continue;
30
+ const idx = trimmed.lastIndexOf("/");
31
+ const rawPrefix = idx <= 0 ? trimmed : trimmed.slice(0, idx);
32
+ const normalized = normalizeGitPathPrefix(rawPrefix);
33
+ if (!normalized)
34
+ continue;
35
+ out.add(rawPrefix.startsWith("/") && !normalized.startsWith("/") ? `/${normalized}` : normalized);
17
36
  }
18
- return [...out].toSorted((a, b) => a.localeCompare(b));
37
+ return [...out].filter(Boolean).toSorted((a, b) => a.localeCompare(b));
19
38
  }
20
39
  export async function gitStatusChangedPaths(opts) {
21
40
  const resolved = await resolveProject({
@@ -55,7 +74,14 @@ export async function stageAllowlist(opts) {
55
74
  message: "No changes to stage (working tree clean)",
56
75
  });
57
76
  }
58
- const allow = opts.allow.map((prefix) => normalizeGitPathPrefix(prefix));
77
+ const allow = normalizeAllowPrefixes(opts.allow);
78
+ if (allow.length === 0) {
79
+ throw new CliError({
80
+ exitCode: 2,
81
+ code: "E_USAGE",
82
+ message: "Provide at least one allowed prefix",
83
+ });
84
+ }
59
85
  const denied = new Set();
60
86
  if (!opts.allowTasks)
61
87
  denied.add(opts.tasksPath);