agentplane 0.2.25 → 0.2.26

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 (166) hide show
  1. package/bin/agentplane.js +91 -54
  2. package/dist/.build-manifest.json +11 -0
  3. package/dist/backends/task-backend/local-backend.d.ts +2 -0
  4. package/dist/backends/task-backend/local-backend.d.ts.map +1 -1
  5. package/dist/backends/task-backend/local-backend.js +12 -1
  6. package/dist/backends/task-backend/redmine/mapping.d.ts.map +1 -1
  7. package/dist/backends/task-backend/redmine/mapping.js +26 -1
  8. package/dist/backends/task-backend/redmine-backend.d.ts +4 -0
  9. package/dist/backends/task-backend/redmine-backend.d.ts.map +1 -1
  10. package/dist/backends/task-backend/redmine-backend.js +92 -9
  11. package/dist/backends/task-backend/shared/types.d.ts +1 -0
  12. package/dist/backends/task-backend/shared/types.d.ts.map +1 -1
  13. package/dist/backends/task-index.d.ts.map +1 -1
  14. package/dist/backends/task-index.js +8 -1
  15. package/dist/cli/command-guide.d.ts.map +1 -1
  16. package/dist/cli/command-guide.js +21 -8
  17. package/dist/cli/command-snippets.d.ts +24 -0
  18. package/dist/cli/command-snippets.d.ts.map +1 -0
  19. package/dist/cli/command-snippets.js +23 -0
  20. package/dist/cli/reason-codes.d.ts +9 -0
  21. package/dist/cli/reason-codes.d.ts.map +1 -0
  22. package/dist/cli/reason-codes.js +79 -0
  23. package/dist/cli/recipes-bundled.d.ts +1 -0
  24. package/dist/cli/recipes-bundled.d.ts.map +1 -1
  25. package/dist/cli/recipes-bundled.js +4 -1
  26. package/dist/cli/run-cli/command-catalog.d.ts +1 -1
  27. package/dist/cli/run-cli/command-catalog.d.ts.map +1 -1
  28. package/dist/cli/run-cli/command-catalog.js +40 -1
  29. package/dist/cli/run-cli/commands/config.d.ts +5 -0
  30. package/dist/cli/run-cli/commands/config.d.ts.map +1 -1
  31. package/dist/cli/run-cli/commands/config.js +86 -1
  32. package/dist/cli/run-cli/commands/core.d.ts.map +1 -1
  33. package/dist/cli/run-cli/commands/core.js +55 -0
  34. package/dist/cli/run-cli/commands/init/recipes.d.ts +5 -1
  35. package/dist/cli/run-cli/commands/init/recipes.d.ts.map +1 -1
  36. package/dist/cli/run-cli/commands/init/recipes.js +24 -4
  37. package/dist/cli/run-cli/commands/init/write-workflow.d.ts +7 -0
  38. package/dist/cli/run-cli/commands/init/write-workflow.d.ts.map +1 -0
  39. package/dist/cli/run-cli/commands/init/write-workflow.js +52 -0
  40. package/dist/cli/run-cli/commands/init.d.ts +2 -1
  41. package/dist/cli/run-cli/commands/init.d.ts.map +1 -1
  42. package/dist/cli/run-cli/commands/init.js +104 -54
  43. package/dist/cli/run-cli.d.ts.map +1 -1
  44. package/dist/cli/run-cli.js +70 -1
  45. package/dist/commands/backend/sync.command.d.ts.map +1 -1
  46. package/dist/commands/backend/sync.command.js +7 -6
  47. package/dist/commands/backend.d.ts.map +1 -1
  48. package/dist/commands/backend.js +2 -0
  49. package/dist/commands/doctor.run.d.ts.map +1 -1
  50. package/dist/commands/doctor.run.js +96 -10
  51. package/dist/commands/guard/impl/commands.d.ts.map +1 -1
  52. package/dist/commands/guard/impl/commands.js +12 -6
  53. package/dist/commands/recipes/impl/commands/install.d.ts.map +1 -1
  54. package/dist/commands/recipes/impl/commands/install.js +36 -13
  55. package/dist/commands/recipes/impl/scenario.d.ts.map +1 -1
  56. package/dist/commands/recipes/impl/scenario.js +25 -0
  57. package/dist/commands/recipes/impl/types.d.ts +4 -0
  58. package/dist/commands/recipes/impl/types.d.ts.map +1 -1
  59. package/dist/commands/scenario/impl/commands.d.ts.map +1 -1
  60. package/dist/commands/scenario/impl/commands.js +74 -3
  61. package/dist/commands/scenario/impl/report.d.ts +8 -0
  62. package/dist/commands/scenario/impl/report.d.ts.map +1 -1
  63. package/dist/commands/scenario/impl/report.js +1 -0
  64. package/dist/commands/shared/reconcile-check.d.ts +7 -0
  65. package/dist/commands/shared/reconcile-check.d.ts.map +1 -0
  66. package/dist/commands/shared/reconcile-check.js +60 -0
  67. package/dist/commands/sync.command.d.ts.map +1 -1
  68. package/dist/commands/sync.command.js +9 -2
  69. package/dist/commands/task/finish.d.ts.map +1 -1
  70. package/dist/commands/task/finish.js +2 -0
  71. package/dist/commands/task/list.d.ts.map +1 -1
  72. package/dist/commands/task/list.js +2 -1
  73. package/dist/commands/task/list.spec.d.ts.map +1 -1
  74. package/dist/commands/task/list.spec.js +7 -0
  75. package/dist/commands/task/next.d.ts.map +1 -1
  76. package/dist/commands/task/next.js +2 -1
  77. package/dist/commands/task/next.spec.d.ts.map +1 -1
  78. package/dist/commands/task/next.spec.js +7 -0
  79. package/dist/commands/task/search.d.ts.map +1 -1
  80. package/dist/commands/task/search.js +2 -1
  81. package/dist/commands/task/search.spec.d.ts.map +1 -1
  82. package/dist/commands/task/search.spec.js +7 -0
  83. package/dist/commands/task/shared.d.ts +7 -0
  84. package/dist/commands/task/shared.d.ts.map +1 -1
  85. package/dist/commands/task/shared.js +21 -1
  86. package/dist/commands/task/verify-record.d.ts.map +1 -1
  87. package/dist/commands/task/verify-record.js +2 -0
  88. package/dist/commands/workflow-build.command.d.ts +8 -0
  89. package/dist/commands/workflow-build.command.d.ts.map +1 -0
  90. package/dist/commands/workflow-build.command.js +96 -0
  91. package/dist/commands/workflow-playbook.command.d.ts +10 -0
  92. package/dist/commands/workflow-playbook.command.d.ts.map +1 -0
  93. package/dist/commands/workflow-playbook.command.js +174 -0
  94. package/dist/commands/workflow-restore.command.d.ts +5 -0
  95. package/dist/commands/workflow-restore.command.d.ts.map +1 -0
  96. package/dist/commands/workflow-restore.command.js +30 -0
  97. package/dist/commands/workflow.command.d.ts +6 -0
  98. package/dist/commands/workflow.command.d.ts.map +1 -0
  99. package/dist/commands/workflow.command.js +36 -0
  100. package/dist/harness/dynamic-tool-contract.d.ts +29 -0
  101. package/dist/harness/dynamic-tool-contract.d.ts.map +1 -0
  102. package/dist/harness/dynamic-tool-contract.js +86 -0
  103. package/dist/harness/hooks-lifecycle.d.ts +27 -0
  104. package/dist/harness/hooks-lifecycle.d.ts.map +1 -0
  105. package/dist/harness/hooks-lifecycle.js +67 -0
  106. package/dist/harness/index.d.ts +9 -0
  107. package/dist/harness/index.d.ts.map +1 -0
  108. package/dist/harness/index.js +8 -0
  109. package/dist/harness/reconcile.d.ts +37 -0
  110. package/dist/harness/reconcile.d.ts.map +1 -0
  111. package/dist/harness/reconcile.js +42 -0
  112. package/dist/harness/retry-policy.d.ts +31 -0
  113. package/dist/harness/retry-policy.d.ts.map +1 -0
  114. package/dist/harness/retry-policy.js +33 -0
  115. package/dist/harness/scheduler.d.ts +18 -0
  116. package/dist/harness/scheduler.d.ts.map +1 -0
  117. package/dist/harness/scheduler.js +55 -0
  118. package/dist/harness/state-machine.d.ts +17 -0
  119. package/dist/harness/state-machine.d.ts.map +1 -0
  120. package/dist/harness/state-machine.js +70 -0
  121. package/dist/harness/token-accounting.d.ts +19 -0
  122. package/dist/harness/token-accounting.d.ts.map +1 -0
  123. package/dist/harness/token-accounting.js +77 -0
  124. package/dist/harness/workspace-safety.d.ts +14 -0
  125. package/dist/harness/workspace-safety.d.ts.map +1 -0
  126. package/dist/harness/workspace-safety.js +62 -0
  127. package/dist/recipes/bundled-recipes.d.ts +4 -0
  128. package/dist/recipes/bundled-recipes.d.ts.map +1 -1
  129. package/dist/recipes/bundled-recipes.js +11 -0
  130. package/dist/shared/errors.d.ts +6 -0
  131. package/dist/shared/errors.d.ts.map +1 -1
  132. package/dist/shared/errors.js +1 -0
  133. package/dist/workflow-runtime/build.d.ts +4 -0
  134. package/dist/workflow-runtime/build.d.ts.map +1 -0
  135. package/dist/workflow-runtime/build.js +114 -0
  136. package/dist/workflow-runtime/enforcement.d.ts +3 -0
  137. package/dist/workflow-runtime/enforcement.d.ts.map +1 -0
  138. package/dist/workflow-runtime/enforcement.js +10 -0
  139. package/dist/workflow-runtime/file-ops.d.ts +11 -0
  140. package/dist/workflow-runtime/file-ops.d.ts.map +1 -0
  141. package/dist/workflow-runtime/file-ops.js +248 -0
  142. package/dist/workflow-runtime/fix.d.ts +9 -0
  143. package/dist/workflow-runtime/fix.d.ts.map +1 -0
  144. package/dist/workflow-runtime/fix.js +107 -0
  145. package/dist/workflow-runtime/index.d.ts +11 -0
  146. package/dist/workflow-runtime/index.d.ts.map +1 -0
  147. package/dist/workflow-runtime/index.js +10 -0
  148. package/dist/workflow-runtime/markdown.d.ts +10 -0
  149. package/dist/workflow-runtime/markdown.d.ts.map +1 -0
  150. package/dist/workflow-runtime/markdown.js +147 -0
  151. package/dist/workflow-runtime/observability.d.ts +12 -0
  152. package/dist/workflow-runtime/observability.d.ts.map +1 -0
  153. package/dist/workflow-runtime/observability.js +14 -0
  154. package/dist/workflow-runtime/paths.d.ts +3 -0
  155. package/dist/workflow-runtime/paths.d.ts.map +1 -0
  156. package/dist/workflow-runtime/paths.js +11 -0
  157. package/dist/workflow-runtime/template.d.ts +7 -0
  158. package/dist/workflow-runtime/template.d.ts.map +1 -0
  159. package/dist/workflow-runtime/template.js +94 -0
  160. package/dist/workflow-runtime/types.d.ts +68 -0
  161. package/dist/workflow-runtime/types.d.ts.map +1 -0
  162. package/dist/workflow-runtime/types.js +1 -0
  163. package/dist/workflow-runtime/validate.d.ts +8 -0
  164. package/dist/workflow-runtime/validate.d.ts.map +1 -0
  165. package/dist/workflow-runtime/validate.js +331 -0
  166. package/package.json +3 -3
@@ -13,51 +13,46 @@ import { collectInitConflicts, handleInitConflicts } from "./init/conflicts.js";
13
13
  import { ensureGitRoot } from "./init/git.js";
14
14
  import { maybeSyncIde } from "./init/ide-sync.js";
15
15
  import { maybeInstallBundledRecipes } from "./init/recipes.js";
16
+ import { ensureInitWorkflow } from "./init/write-workflow.js";
16
17
  import { ensureAgentplaneDirs, writeBackendStubs, writeInitConfig } from "./init/write-config.js";
17
18
  import { ensureAgentsFiles } from "./init/write-agents.js";
18
19
  import { ensureInitGitignore } from "./init/write-gitignore.js";
19
20
  import { ensureInitRedmineEnvTemplate } from "./init/write-env.js";
20
21
  import { renderInitSection, renderInitWelcome } from "./init/ui.js";
22
+ import { fileExists } from "../../fs-utils.js";
21
23
  const setupProfilePresets = {
22
- developer: {
23
- mode: "full",
24
- description: "I am Developer (full setup questionnaire; hooks on; explicit unsafe confirmations on).",
25
- defaultHooks: true,
26
- defaultStrictUnsafeConfirm: true,
27
- defaultRequirePlanApproval: true,
28
- defaultRequireNetworkApproval: true,
29
- defaultRequireVerifyApproval: true,
30
- defaultExecutionProfile: "balanced",
31
- },
32
- vibecoder: {
24
+ light: {
33
25
  mode: "compact",
34
- description: "I am Vibecoder (compact setup; hooks off; approvals off; aggressive execution defaults).",
35
- defaultHooks: false,
26
+ description: "Light profile (maximum flexibility, minimal enforcement, hooks still mandatory).",
27
+ defaultHooks: true,
36
28
  defaultStrictUnsafeConfirm: false,
37
29
  defaultRequirePlanApproval: false,
38
30
  defaultRequireNetworkApproval: false,
39
31
  defaultRequireVerifyApproval: false,
40
32
  defaultExecutionProfile: "aggressive",
33
+ defaultRecipes: [],
41
34
  },
42
- manager: {
35
+ normal: {
43
36
  mode: "compact",
44
- description: "I am Manager / Product owner (compact setup; oversight defaults with approvals on, hooks off).",
45
- defaultHooks: false,
37
+ description: "Normal profile (balanced defaults and approvals enabled for standard team workflows).",
38
+ defaultHooks: true,
46
39
  defaultStrictUnsafeConfirm: false,
47
40
  defaultRequirePlanApproval: true,
48
41
  defaultRequireNetworkApproval: true,
49
42
  defaultRequireVerifyApproval: true,
50
43
  defaultExecutionProfile: "balanced",
44
+ defaultRecipes: [],
51
45
  },
52
- enterprise: {
46
+ "full-harness": {
53
47
  mode: "full",
54
- description: "I am Enterprise / Regulated team (full setup; strict approvals, hooks on, conservative execution).",
48
+ description: "Full Harness profile (strict guardrails, explicit confirmations, conservative execution).",
55
49
  defaultHooks: true,
56
50
  defaultStrictUnsafeConfirm: true,
57
51
  defaultRequirePlanApproval: true,
58
52
  defaultRequireNetworkApproval: true,
59
53
  defaultRequireVerifyApproval: true,
60
54
  defaultExecutionProfile: "conservative",
55
+ defaultRecipes: [],
61
56
  },
62
57
  };
63
58
  function parseBooleanValueForInit(flag, value) {
@@ -81,6 +76,22 @@ function parseRecipesSelectionForInit(value) {
81
76
  .map((item) => item.trim())
82
77
  .filter(Boolean);
83
78
  }
79
+ function normalizeSetupProfile(raw) {
80
+ if (!raw)
81
+ return undefined;
82
+ const value = raw.trim().toLowerCase();
83
+ if (value === "developer")
84
+ return "full-harness";
85
+ if (value === "enterprise")
86
+ return "full-harness";
87
+ if (value === "manager")
88
+ return "normal";
89
+ if (value === "vibecoder")
90
+ return "light";
91
+ if (value === "light" || value === "normal" || value === "full-harness")
92
+ return value;
93
+ return undefined;
94
+ }
84
95
  export const initSpec = {
85
96
  id: ["init"],
86
97
  group: "Setup",
@@ -90,9 +101,17 @@ export const initSpec = {
90
101
  {
91
102
  kind: "string",
92
103
  name: "setup-profile",
93
- valueHint: "<developer|vibecoder|manager|enterprise>",
94
- choices: ["developer", "vibecoder", "manager", "enterprise"],
95
- description: "Persona preset for init defaults and dialog depth (compact vs full questionnaire).",
104
+ valueHint: "<light|normal|full-harness>",
105
+ choices: [
106
+ "light",
107
+ "normal",
108
+ "full-harness",
109
+ "developer",
110
+ "vibecoder",
111
+ "manager",
112
+ "enterprise",
113
+ ],
114
+ description: "Setup profile preset. Preferred values: light, normal, full-harness.",
96
115
  },
97
116
  {
98
117
  kind: "string",
@@ -121,7 +140,7 @@ export const initSpec = {
121
140
  kind: "string",
122
141
  name: "hooks",
123
142
  valueHint: "<true|false>",
124
- description: "Install git hooks (non-interactive requires an explicit value).",
143
+ description: "Hooks are mandatory; only true is accepted.",
125
144
  },
126
145
  {
127
146
  kind: "string",
@@ -188,11 +207,11 @@ export const initSpec = {
188
207
  examples: [
189
208
  { cmd: "agentplane init", why: "Interactive setup (prompts for missing values)." },
190
209
  {
191
- cmd: "agentplane init --setup-profile vibecoder --yes",
192
- why: "Non-interactive fast setup for autonomous defaults (hooks off, approvals off).",
210
+ cmd: "agentplane init --setup-profile light --yes",
211
+ why: "Non-interactive setup with flexible defaults.",
193
212
  },
194
213
  {
195
- cmd: "agentplane init --workflow direct --backend local --hooks false --require-plan-approval true --require-network-approval true --require-verify-approval true --yes",
214
+ cmd: "agentplane init --workflow direct --backend local --hooks true --require-plan-approval true --require-network-approval true --require-verify-approval true --yes",
196
215
  why: "Non-interactive setup with explicit policy flags.",
197
216
  },
198
217
  {
@@ -220,7 +239,7 @@ export const initSpec = {
220
239
  const requireVerifyRaw = raw.opts["require-verify-approval"];
221
240
  const recipesRaw = raw.opts.recipes;
222
241
  return {
223
- setupProfile: raw.opts["setup-profile"],
242
+ setupProfile: normalizeSetupProfile(raw.opts["setup-profile"]),
224
243
  ide: raw.opts.ide,
225
244
  workflow: raw.opts.workflow,
226
245
  backend: raw.opts.backend,
@@ -253,6 +272,13 @@ export const initSpec = {
253
272
  message: "Use either --force or --backup (not both).",
254
273
  });
255
274
  }
275
+ if (p.hooks === false) {
276
+ throw usageError({
277
+ spec: initSpec,
278
+ command: "init",
279
+ message: "Hooks installation is mandatory. Use --hooks true (or omit the flag).",
280
+ });
281
+ }
256
282
  },
257
283
  };
258
284
  export const runInit = (ctx, flags) => cmdInit({ cwd: ctx.cwd, rootOverride: ctx.rootOverride, flags });
@@ -262,7 +288,6 @@ async function cmdInit(opts) {
262
288
  ide: "codex",
263
289
  workflow: "direct",
264
290
  backend: "local",
265
- hooks: false,
266
291
  recipes: [],
267
292
  requirePlanApproval: true,
268
293
  requireNetworkApproval: true,
@@ -273,7 +298,6 @@ async function cmdInit(opts) {
273
298
  let ide = flags.ide ?? defaults.ide;
274
299
  let workflow = flags.workflow ?? defaults.workflow;
275
300
  let backend = flags.backend ?? defaults.backend;
276
- let hooks = flags.hooks ?? defaults.hooks;
277
301
  let recipes = flags.recipes ?? defaults.recipes;
278
302
  let requirePlanApproval = flags.requirePlanApproval ?? defaults.requirePlanApproval;
279
303
  let requireNetworkApproval = flags.requireNetworkApproval ?? defaults.requireNetworkApproval;
@@ -283,19 +307,18 @@ async function cmdInit(opts) {
283
307
  let setupProfile = flags.setupProfile
284
308
  ? setupProfilePresets[flags.setupProfile].mode
285
309
  : "compact";
286
- let setupProfilePreset = flags.setupProfile ?? "manager";
310
+ let setupProfilePreset = flags.setupProfile ?? "normal";
287
311
  const isInteractive = process.stdin.isTTY && !flags.yes;
288
312
  if (!process.stdin.isTTY &&
289
313
  !flags.yes &&
290
314
  (!flags.workflow ||
291
- flags.hooks === undefined ||
292
315
  flags.requirePlanApproval === undefined ||
293
316
  flags.requireNetworkApproval === undefined ||
294
317
  flags.requireVerifyApproval === undefined)) {
295
318
  throw usageError({
296
319
  spec: initSpec,
297
320
  command: "init",
298
- message: "Non-interactive init requires --yes or explicit values for: --workflow, --hooks, --require-plan-approval, --require-network-approval, --require-verify-approval.",
321
+ message: "Non-interactive init requires --yes or explicit values for: --workflow, --require-plan-approval, --require-network-approval, --require-verify-approval.",
299
322
  });
300
323
  }
301
324
  if (isInteractive) {
@@ -316,19 +339,17 @@ async function cmdInit(opts) {
316
339
  };
317
340
  process.stdout.write(renderInitWelcome());
318
341
  const presetLines = Object.entries(setupProfilePresets).map(([id, preset]) => `- ${id}: ${preset.description}`);
319
- process.stdout.write(renderInitSection("Who Are You?", "Pick the persona that best matches your working style. This sets defaults and controls compact vs full questionnaire."));
342
+ process.stdout.write(renderInitSection("Setup Profile", "Pick one of three setup profiles. This controls policy strictness and questionnaire depth."));
320
343
  process.stdout.write(`${presetLines.join("\n")}\n\n`);
321
344
  if (flags.setupProfile) {
322
345
  setupProfilePreset = flags.setupProfile;
323
346
  }
324
347
  else {
325
- const selected = await askChoice("Who are you?", ["developer", "vibecoder", "manager", "enterprise"], "manager");
348
+ const selected = await askChoice("Setup profile", ["light", "normal", "full-harness"], "normal");
326
349
  setupProfilePreset = selected;
327
350
  }
328
351
  const selectedPreset = setupProfilePresets[setupProfilePreset];
329
352
  setupProfile = selectedPreset.mode;
330
- if (flags.hooks === undefined)
331
- hooks = selectedPreset.defaultHooks;
332
353
  if (flags.strictUnsafeConfirm === undefined) {
333
354
  strictUnsafeConfirm = selectedPreset.defaultStrictUnsafeConfirm;
334
355
  }
@@ -360,9 +381,7 @@ async function cmdInit(opts) {
360
381
  backend = choice === "redmine" ? "redmine" : "local";
361
382
  }
362
383
  if (setupProfile === "full") {
363
- if (flags.hooks === undefined) {
364
- hooks = await askYesNo("Install managed git hooks now?", hooks);
365
- }
384
+ process.stdout.write(renderInitSection("Hooks", "Managed git hooks are mandatory and will be installed automatically."));
366
385
  process.stdout.write(renderInitSection("Execution Profile", "Set default autonomy/effort for agents. You can change this later in config."));
367
386
  if (!flags.executionProfile) {
368
387
  executionProfile = (await askChoice("Execution profile", ["conservative", "balanced", "aggressive"], executionProfile));
@@ -383,18 +402,27 @@ async function cmdInit(opts) {
383
402
  process.stdout.write(renderInitSection("Recipes", "Optional: install recipe packs now (comma-separated IDs) or choose none."));
384
403
  if (!flags.recipes) {
385
404
  process.stdout.write(`${renderBundledRecipesHint()}\n`);
386
- const answer = await askInput("Install optional recipes (comma separated, or none): ");
387
- recipes = answer
388
- ? answer
405
+ const defaultRecipesLabel = selectedPreset.defaultRecipes.length > 0
406
+ ? selectedPreset.defaultRecipes.join(", ")
407
+ : "none";
408
+ const answer = await askInput(`Install optional recipes (comma separated, or none) [default: ${defaultRecipesLabel}]: `);
409
+ const normalized = answer.trim().toLowerCase();
410
+ if (normalized === "") {
411
+ recipes = [...selectedPreset.defaultRecipes];
412
+ }
413
+ else if (normalized === "none") {
414
+ recipes = [];
415
+ }
416
+ else {
417
+ recipes = answer
389
418
  .split(",")
390
419
  .map((item) => item.trim())
391
- .filter(Boolean)
392
- : [];
420
+ .filter(Boolean);
421
+ }
393
422
  }
394
423
  }
395
424
  else {
396
- hooks = flags.hooks ?? selectedPreset.defaultHooks;
397
- recipes = flags.recipes ?? defaults.recipes;
425
+ recipes = flags.recipes ?? selectedPreset.defaultRecipes;
398
426
  requirePlanApproval = flags.requirePlanApproval ?? selectedPreset.defaultRequirePlanApproval;
399
427
  requireNetworkApproval =
400
428
  flags.requireNetworkApproval ?? selectedPreset.defaultRequireNetworkApproval;
@@ -402,7 +430,7 @@ async function cmdInit(opts) {
402
430
  flags.requireVerifyApproval ?? selectedPreset.defaultRequireVerifyApproval;
403
431
  executionProfile = flags.executionProfile ?? selectedPreset.defaultExecutionProfile;
404
432
  strictUnsafeConfirm = flags.strictUnsafeConfirm ?? selectedPreset.defaultStrictUnsafeConfirm;
405
- process.stdout.write(renderInitSection("Defaults Applied", `Using compact ${setupProfilePreset} defaults for hooks, approvals, execution profile, and recipes.`));
433
+ process.stdout.write(renderInitSection("Defaults Applied", `Using compact ${setupProfilePreset} defaults for approvals, execution profile, and recipes. Hooks remain mandatory.`));
406
434
  }
407
435
  }
408
436
  if (flags.yes) {
@@ -410,8 +438,7 @@ async function cmdInit(opts) {
410
438
  ide = flags.ide ?? defaults.ide;
411
439
  workflow = flags.workflow ?? defaults.workflow;
412
440
  backend = flags.backend ?? defaults.backend;
413
- hooks = flags.hooks ?? yesPreset.defaultHooks;
414
- recipes = flags.recipes ?? defaults.recipes;
441
+ recipes = flags.recipes ?? yesPreset.defaultRecipes;
415
442
  requirePlanApproval = flags.requirePlanApproval ?? yesPreset.defaultRequirePlanApproval;
416
443
  requireNetworkApproval =
417
444
  flags.requireNetworkApproval ?? yesPreset.defaultRequireNetworkApproval;
@@ -446,6 +473,20 @@ async function cmdInit(opts) {
446
473
  ];
447
474
  const initFiles = [configPath, backendPath];
448
475
  const conflicts = await collectInitConflicts({ initDirs, initFiles });
476
+ const agentsPath = path.join(resolved.gitRoot, "AGENTS.md");
477
+ const agentsMissing = !(await fileExists(agentsPath));
478
+ if (conflicts.length > 0 && agentsMissing) {
479
+ // Recovery path: if a repo already contains conflicting .agentplane config/backend files,
480
+ // still materialize missing policy/agent templates before reporting conflicts.
481
+ await ensureAgentplaneDirs(resolved.agentplaneDir, backend);
482
+ await ensureAgentsFiles({
483
+ gitRoot: resolved.gitRoot,
484
+ agentplaneDir: resolved.agentplaneDir,
485
+ workflow,
486
+ configPathAbs: configPath,
487
+ backendPathAbs: backendPath,
488
+ });
489
+ }
449
490
  await handleInitConflicts({
450
491
  gitRoot: resolved.gitRoot,
451
492
  conflicts,
@@ -483,6 +524,13 @@ async function cmdInit(opts) {
483
524
  includeAgentPromptFiles: flags.gitignoreAgents === true,
484
525
  });
485
526
  installPaths.push(".gitignore");
527
+ const workflowInit = await ensureInitWorkflow({
528
+ gitRoot: resolved.gitRoot,
529
+ workflowMode: workflow,
530
+ });
531
+ for (const abs of workflowInit.installPaths) {
532
+ installPaths.push(path.relative(resolved.gitRoot, abs));
533
+ }
486
534
  if (flags.gitignoreAgents) {
487
535
  await setPinnedBaseBranch({
488
536
  cwd: resolved.gitRoot,
@@ -490,10 +538,8 @@ async function cmdInit(opts) {
490
538
  value: initBaseBranch,
491
539
  });
492
540
  }
493
- if (hooks) {
494
- await cmdHooksInstall({ cwd: opts.cwd, rootOverride: opts.rootOverride, quiet: true });
495
- installPaths.push(".agentplane/bin/agentplane");
496
- }
541
+ await cmdHooksInstall({ cwd: opts.cwd, rootOverride: opts.rootOverride, quiet: true });
542
+ installPaths.push(".agentplane/bin/agentplane");
497
543
  const ideRes = await maybeSyncIde({
498
544
  cwd: opts.cwd,
499
545
  rootOverride: opts.rootOverride,
@@ -501,14 +547,18 @@ async function cmdInit(opts) {
501
547
  gitRoot: resolved.gitRoot,
502
548
  });
503
549
  installPaths.push(...ideRes.installPaths);
504
- maybeInstallBundledRecipes(recipes);
550
+ await maybeInstallBundledRecipes({
551
+ recipes,
552
+ cwd: opts.cwd,
553
+ rootOverride: opts.rootOverride,
554
+ });
505
555
  if (!flags.gitignoreAgents) {
506
556
  await ensureInitCommit({
507
557
  gitRoot: resolved.gitRoot,
508
558
  baseBranch: initBaseBranch,
509
559
  installPaths,
510
560
  version: getVersion(),
511
- skipHooks: hooks,
561
+ skipHooks: true,
512
562
  });
513
563
  }
514
564
  process.stdout.write(`${path.relative(resolved.gitRoot, resolved.agentplaneDir)}\n`);
@@ -1 +1 @@
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,CAiL5D"}
1
+ {"version":3,"file":"run-cli.d.ts","sourceRoot":"","sources":["../../src/cli/run-cli.ts"],"names":[],"mappings":"AAoeA,wBAAsB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CA4M5D"}
@@ -15,6 +15,7 @@ import { helpSpec } from "./spec/help.js";
15
15
  import { usageError } from "./spec/errors.js";
16
16
  import { suggestOne } from "./spec/suggest.js";
17
17
  import { COMMANDS } from "./run-cli/command-catalog.js";
18
+ import { getReasonCodeMeta } from "./reason-codes.js";
18
19
  const GLOBAL_FLAGS = [
19
20
  { key: "help", forms: ["--help", "-h"], takesValue: false, scoped: false },
20
21
  { key: "version", forms: ["--version", "-v"], takesValue: false, scoped: false },
@@ -24,6 +25,7 @@ const GLOBAL_FLAGS = [
24
25
  { key: "root", forms: ["--root"], takesValue: true, scoped: false },
25
26
  ];
26
27
  const GLOBAL_FLAG_FORMS = new Map(GLOBAL_FLAGS.flatMap((def) => def.forms.map((form) => [form, def])));
28
+ const HELP_TAIL_OPTIONS = new Set(["--compact", "--json"]);
27
29
  function prescanJsonErrors(argv) {
28
30
  // If parseGlobalArgs throws (e.g. missing --root value), we still want to honor
29
31
  // `--json-errors` in the "scoped global" zone (before the command id).
@@ -135,8 +137,15 @@ function matchCommandCatalog(tokens) {
135
137
  }
136
138
  function writeError(err, jsonErrors) {
137
139
  const guidance = resolveErrorGuidance(err);
140
+ const contextReasonCode = typeof err.context?.reason_code === "string" ? String(err.context.reason_code) : undefined;
141
+ const reasonCode = contextReasonCode ?? guidance.nextAction?.reasonCode;
142
+ const reasonDecode = getReasonCodeMeta(reasonCode);
138
143
  if (jsonErrors) {
139
- process.stdout.write(`${formatJsonError(err, { hint: guidance.hint, nextAction: guidance.nextAction })}\n`);
144
+ process.stdout.write(`${formatJsonError(err, {
145
+ hint: guidance.hint,
146
+ nextAction: guidance.nextAction,
147
+ reasonDecode,
148
+ })}\n`);
140
149
  }
141
150
  else {
142
151
  const header = `error [${err.code}]`;
@@ -152,6 +161,10 @@ function writeError(err, jsonErrors) {
152
161
  if (guidance.nextAction) {
153
162
  process.stderr.write(`next_action: ${guidance.nextAction.command} (${guidance.nextAction.reason})\n`);
154
163
  }
164
+ if (reasonDecode) {
165
+ process.stderr.write(`reason_code: ${reasonDecode.code} [${reasonDecode.category}] ${reasonDecode.summary}\n`);
166
+ process.stderr.write(`reason_action: ${reasonDecode.action}\n`);
167
+ }
155
168
  }
156
169
  }
157
170
  const AGENTPLANE_HOME_ENV = "AGENTPLANE_HOME";
@@ -163,9 +176,20 @@ function resolveAgentplaneHome() {
163
176
  }
164
177
  function resolveErrorGuidance(err) {
165
178
  const command = typeof err.context?.command === "string" ? err.context.command : undefined;
179
+ const reasonCode = typeof err.context?.reason_code === "string" ? String(err.context.reason_code) : undefined;
166
180
  const usage = command ? `agentplane help ${command} --compact` : "agentplane help";
167
181
  switch (err.code) {
168
182
  case "E_USAGE": {
183
+ if (reasonCode === "sync_backend_mismatch") {
184
+ return {
185
+ hint: "Configured backend id mismatch. Check active backend and retry with a matching id.",
186
+ nextAction: {
187
+ command: "agentplane config show",
188
+ reason: "inspect active backend id before running sync",
189
+ reasonCode: "sync_backend_mismatch",
190
+ },
191
+ };
192
+ }
169
193
  return {
170
194
  hint: `See \`${usage}\` for usage.`,
171
195
  nextAction: {
@@ -176,6 +200,16 @@ function resolveErrorGuidance(err) {
176
200
  };
177
201
  }
178
202
  case "E_GIT": {
203
+ if (reasonCode === "reconcile_git_state_unreadable") {
204
+ return {
205
+ hint: "Reconcile check could not read git state.",
206
+ nextAction: {
207
+ command: "git status --short --untracked-files=no",
208
+ reason: "confirm repository state is readable before mutating commands",
209
+ reasonCode: "reconcile_git_state_unreadable",
210
+ },
211
+ };
212
+ }
179
213
  if (command?.startsWith("branch")) {
180
214
  return {
181
215
  hint: "Check git repo/branch; run `git branch` or pass --root <path>.",
@@ -236,6 +270,17 @@ function resolveErrorGuidance(err) {
236
270
  };
237
271
  }
238
272
  case "E_VALIDATION": {
273
+ if (reasonCode === "reconcile_task_scan_failed" ||
274
+ reasonCode === "reconcile_task_scan_incomplete") {
275
+ return {
276
+ hint: "Reconcile check failed due to task scan drift or parse/read errors.",
277
+ nextAction: {
278
+ command: "agentplane task list --strict-read",
279
+ reason: "surface task scan/read failures before retrying mutating commands",
280
+ reasonCode: reasonCode,
281
+ },
282
+ };
283
+ }
239
284
  return {
240
285
  hint: "Fix invalid config/input shape and rerun.",
241
286
  nextAction: {
@@ -390,6 +435,30 @@ export async function runCli(argv) {
390
435
  // - agentplane --help
391
436
  // - agentplane <cmd...> --help [--compact|--json]
392
437
  if (globals.help) {
438
+ const matchedHelp = matchCommandCatalog(rest);
439
+ if (matchedHelp) {
440
+ const rawHelpTail = rest.slice(matchedHelp.consumed);
441
+ const commandFlags = new Set();
442
+ for (const opt of matchedHelp.entry.spec.options ?? []) {
443
+ const optRecord = opt;
444
+ const long = typeof optRecord.name === "string" ? optRecord.name.trim() : "";
445
+ if (long)
446
+ commandFlags.add(`--${long}`);
447
+ const short = typeof optRecord.alias === "string" ? optRecord.alias.trim() : "";
448
+ if (short)
449
+ commandFlags.add(`-${short}`);
450
+ }
451
+ const invalidHelpTail = rawHelpTail.filter((token) => token.startsWith("-") && !HELP_TAIL_OPTIONS.has(token) && !commandFlags.has(token));
452
+ if (invalidHelpTail.length > 0) {
453
+ throw usageError({
454
+ spec: helpSpec,
455
+ command: "help",
456
+ message: `Unsupported flag(s) after --help: ${invalidHelpTail.join(", ")}.`,
457
+ });
458
+ }
459
+ const helpTail = rawHelpTail.filter((token) => token.startsWith("-") && HELP_TAIL_OPTIONS.has(token));
460
+ return await runCli2HelpFast(["help", ...matchedHelp.entry.spec.id, ...helpTail]);
461
+ }
393
462
  return await runCli2HelpFast(["help", ...rest]);
394
463
  }
395
464
  if (rest.length === 0) {
@@ -1 +1 @@
1
- {"version":3,"file":"sync.command.d.ts","sourceRoot":"","sources":["../../../src/commands/backend/sync.command.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAGtE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAChE,OAAO,EAAwB,KAAK,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAE7E,KAAK,iBAAiB,GAAG;IAAE,GAAG,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC;AAE3C,eAAO,MAAM,WAAW,EAAE,WAAW,CAAC,iBAAiB,CAQtD,CAAC;AAEF,eAAO,MAAM,eAAe,EAAE,WAAW,CAAC,iBAAiB,CAuC1D,CAAC;AAEF,iBAAS,mBAAmB,CAAC,IAAI,EAAE,UAAU,EAAE,CAAC,EAAE,iBAAiB,GAAG,OAAO,CAAC,MAAM,CAAC,CAUpF;AAED,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,cAAc,CAAC,8BAEtF;AAED,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,cAAc,CAAC,IAC1E,KAAK,UAAU,EAAE,GAAG,iBAAiB,KAAG,OAAO,CAAC,MAAM,CAAC,CAStE"}
1
+ {"version":3,"file":"sync.command.d.ts","sourceRoot":"","sources":["../../../src/commands/backend/sync.command.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAItE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAChE,OAAO,EAAwB,KAAK,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAE7E,KAAK,iBAAiB,GAAG;IAAE,GAAG,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC;AAE3C,eAAO,MAAM,WAAW,EAAE,WAAW,CAAC,iBAAiB,CAQtD,CAAC;AAEF,eAAO,MAAM,eAAe,EAAE,WAAW,CAAC,iBAAiB,CAuC1D,CAAC;AAEF,iBAAS,mBAAmB,CAAC,IAAI,EAAE,UAAU,EAAE,CAAC,EAAE,iBAAiB,GAAG,OAAO,CAAC,MAAM,CAAC,CAUpF;AAED,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,cAAc,CAAC,8BAEtF;AAED,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,cAAc,CAAC,IAC1E,KAAK,UAAU,EAAE,GAAG,iBAAiB,KAAG,OAAO,CAAC,MAAM,CAAC,CAStE"}
@@ -1,3 +1,4 @@
1
+ import { COMMAND_SNIPPETS } from "../../cli/command-snippets.js";
1
2
  import { usageError } from "../../cli/spec/errors.js";
2
3
  import { suggestOne } from "../../cli/spec/suggest.js";
3
4
  import { cmdBackendSyncParsed } from "../backend.js";
@@ -7,7 +8,7 @@ export const backendSpec = {
7
8
  summary: "Backend-related operations.",
8
9
  description: "This is a command group. Use a subcommand such as `agentplane backend sync ...`.",
9
10
  args: [{ name: "cmd", required: false, variadic: true, valueHint: "<cmd>" }],
10
- examples: [{ cmd: "agentplane backend sync local --direction pull", why: "Sync the backend." }],
11
+ examples: [{ cmd: COMMAND_SNIPPETS.backendSync.pullLocal, why: "Sync the backend." }],
11
12
  parse: (raw) => ({ cmd: (raw.args.cmd ?? []) }),
12
13
  };
13
14
  export const backendSyncSpec = {
@@ -21,8 +22,8 @@ export const backendSyncSpec = {
21
22
  name: "direction",
22
23
  valueHint: "<push|pull>",
23
24
  choices: ["push", "pull"],
24
- required: true,
25
- description: "Sync direction.",
25
+ default: "push",
26
+ description: "Sync direction (default: push).",
26
27
  },
27
28
  {
28
29
  kind: "string",
@@ -36,15 +37,15 @@ export const backendSyncSpec = {
36
37
  { kind: "boolean", name: "quiet", default: false, description: "Reduce output noise." },
37
38
  ],
38
39
  examples: [
39
- { cmd: "agentplane backend sync local --direction pull", why: "Pull from backend." },
40
+ { cmd: COMMAND_SNIPPETS.backendSync.pullLocal, why: "Pull from backend." },
40
41
  {
41
- cmd: "agentplane backend sync redmine --direction push --yes",
42
+ cmd: COMMAND_SNIPPETS.backendSync.pushRedmineWithYes,
42
43
  why: "Push to a networked backend with explicit approval.",
43
44
  },
44
45
  ],
45
46
  parse: (raw) => ({
46
47
  backendId: String(raw.args.id),
47
- direction: raw.opts.direction,
48
+ direction: (raw.opts.direction ?? "push"),
48
49
  conflict: (raw.opts.conflict ?? "diff"),
49
50
  yes: raw.opts.yes === true,
50
51
  quiet: raw.opts.quiet === true,
@@ -1 +1 @@
1
- {"version":3,"file":"backend.d.ts","sourceRoot":"","sources":["../../src/commands/backend.ts"],"names":[],"mappings":"AAGA,OAAO,EAAsB,KAAK,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAGnF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,GAAG,MAAM,CAAC;IAC3B,QAAQ,EAAE,MAAM,GAAG,cAAc,GAAG,eAAe,GAAG,MAAM,CAAC;IAC7D,GAAG,EAAE,OAAO,CAAC;IACb,KAAK,EAAE,OAAO,CAAC;CAChB,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG;IACvB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,SAAS,EAAE,MAAM,GAAG,MAAM,CAAC;IAC3B,QAAQ,EAAE,MAAM,GAAG,cAAc,GAAG,eAAe,GAAG,MAAM,CAAC;IAC7D,GAAG,EAAE,OAAO,CAAC;IACb,KAAK,EAAE,OAAO,CAAC;CAChB,CAAC;AAEF,wBAAsB,oBAAoB,CAAC,IAAI,EAAE;IAC/C,GAAG,CAAC,EAAE,cAAc,CAAC;IACrB,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,iBAAiB,CAAC;CAC1B,GAAG,OAAO,CAAC,MAAM,CAAC,CAwClB;AAED,wBAAsB,aAAa,CAAC,IAAI,EAAE;IACxC,GAAG,CAAC,EAAE,cAAc,CAAC;IACrB,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,UAAU,CAAC;CACnB,GAAG,OAAO,CAAC,MAAM,CAAC,CAwClB"}
1
+ {"version":3,"file":"backend.d.ts","sourceRoot":"","sources":["../../src/commands/backend.ts"],"names":[],"mappings":"AAGA,OAAO,EAAsB,KAAK,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAGnF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,GAAG,MAAM,CAAC;IAC3B,QAAQ,EAAE,MAAM,GAAG,cAAc,GAAG,eAAe,GAAG,MAAM,CAAC;IAC7D,GAAG,EAAE,OAAO,CAAC;IACb,KAAK,EAAE,OAAO,CAAC;CAChB,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG;IACvB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,SAAS,EAAE,MAAM,GAAG,MAAM,CAAC;IAC3B,QAAQ,EAAE,MAAM,GAAG,cAAc,GAAG,eAAe,GAAG,MAAM,CAAC;IAC7D,GAAG,EAAE,OAAO,CAAC;IACb,KAAK,EAAE,OAAO,CAAC;CAChB,CAAC;AAEF,wBAAsB,oBAAoB,CAAC,IAAI,EAAE;IAC/C,GAAG,CAAC,EAAE,cAAc,CAAC;IACrB,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,iBAAiB,CAAC;CAC1B,GAAG,OAAO,CAAC,MAAM,CAAC,CAyClB;AAED,wBAAsB,aAAa,CAAC,IAAI,EAAE;IACxC,GAAG,CAAC,EAAE,cAAc,CAAC;IACrB,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,UAAU,CAAC;CACnB,GAAG,OAAO,CAAC,MAAM,CAAC,CAyClB"}
@@ -15,6 +15,7 @@ export async function cmdBackendSyncParsed(opts) {
15
15
  exitCode: 2,
16
16
  code: "E_USAGE",
17
17
  message: `Configured backend is "${backendId}", not "${opts.flags.backendId}"`,
18
+ context: { command: "backend sync", reason_code: "sync_backend_mismatch" },
18
19
  });
19
20
  }
20
21
  if (!backend.sync) {
@@ -57,6 +58,7 @@ export async function cmdSyncParsed(opts) {
57
58
  exitCode: 2,
58
59
  code: "E_USAGE",
59
60
  message: `Configured backend is "${backendId}", not "${opts.flags.backendId}"`,
61
+ context: { command: "sync", reason_code: "sync_backend_mismatch" },
60
62
  });
61
63
  }
62
64
  if (!backend.sync) {
@@ -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;AAK1D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAsPrD,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;AAqUrD,eAAO,MAAM,SAAS,EAAE,cAAc,CAAC,YAAY,CAmDlD,CAAC"}