agentplane 0.2.26 → 0.3.1

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 (85) hide show
  1. package/README.md +3 -1
  2. package/assets/AGENTS.md +123 -526
  3. package/assets/agents/UPGRADER.json +10 -9
  4. package/assets/framework.manifest.json +112 -7
  5. package/assets/policy/check-routing.mjs +180 -0
  6. package/assets/policy/dod.code.md +25 -0
  7. package/assets/policy/dod.core.md +32 -0
  8. package/assets/policy/dod.docs.md +32 -0
  9. package/assets/policy/examples/migration-note.md +6 -0
  10. package/assets/policy/examples/pr-note.md +16 -0
  11. package/assets/policy/examples/unit-test-pattern.md +19 -0
  12. package/assets/policy/governance.md +37 -0
  13. package/assets/policy/incidents.md +36 -0
  14. package/assets/policy/security.must.md +7 -0
  15. package/assets/policy/workflow.branch_pr.md +34 -0
  16. package/assets/policy/workflow.direct.md +46 -0
  17. package/assets/policy/workflow.md +9 -0
  18. package/assets/policy/workflow.release.md +31 -0
  19. package/assets/policy/workflow.upgrade.md +20 -0
  20. package/bin/agentplane.js +41 -88
  21. package/bin/dist-guard.js +124 -0
  22. package/dist/.build-manifest.json +5 -5
  23. package/dist/agents/agents-template.d.ts +7 -0
  24. package/dist/agents/agents-template.d.ts.map +1 -1
  25. package/dist/agents/agents-template.js +41 -2
  26. package/dist/cli/command-guide.d.ts.map +1 -1
  27. package/dist/cli/command-guide.js +18 -9
  28. package/dist/cli/command-snippets.d.ts +1 -1
  29. package/dist/cli/command-snippets.js +1 -1
  30. package/dist/cli/run-cli/commands/core.js +2 -2
  31. package/dist/cli/run-cli/commands/ide.d.ts.map +1 -1
  32. package/dist/cli/run-cli/commands/ide.js +8 -3
  33. package/dist/cli/run-cli/commands/init/ui.d.ts.map +1 -1
  34. package/dist/cli/run-cli/commands/init/ui.js +1 -2
  35. package/dist/cli/run-cli/commands/init/write-agents.d.ts +2 -0
  36. package/dist/cli/run-cli/commands/init/write-agents.d.ts.map +1 -1
  37. package/dist/cli/run-cli/commands/init/write-agents.js +24 -5
  38. package/dist/cli/run-cli/commands/init/write-workflow.d.ts +5 -0
  39. package/dist/cli/run-cli/commands/init/write-workflow.d.ts.map +1 -1
  40. package/dist/cli/run-cli/commands/init/write-workflow.js +6 -0
  41. package/dist/cli/run-cli/commands/init.d.ts +2 -0
  42. package/dist/cli/run-cli/commands/init.d.ts.map +1 -1
  43. package/dist/cli/run-cli/commands/init.js +47 -19
  44. package/dist/cli/run-cli.d.ts.map +1 -1
  45. package/dist/cli/run-cli.js +125 -7
  46. package/dist/commands/doctor.run.d.ts.map +1 -1
  47. package/dist/commands/doctor.run.js +11 -6
  48. package/dist/commands/release/apply.command.d.ts.map +1 -1
  49. package/dist/commands/release/apply.command.js +9 -4
  50. package/dist/commands/release/plan.command.d.ts.map +1 -1
  51. package/dist/commands/release/plan.command.js +9 -3
  52. package/dist/commands/task/add.d.ts.map +1 -1
  53. package/dist/commands/task/add.js +32 -0
  54. package/dist/commands/task/doc.command.d.ts.map +1 -1
  55. package/dist/commands/task/doc.command.js +1 -0
  56. package/dist/commands/task/finish.d.ts.map +1 -1
  57. package/dist/commands/task/finish.js +9 -1
  58. package/dist/commands/task/new.d.ts.map +1 -1
  59. package/dist/commands/task/new.js +41 -4
  60. package/dist/commands/task/plan.d.ts.map +1 -1
  61. package/dist/commands/task/plan.js +7 -1
  62. package/dist/commands/task/shared.d.ts +7 -0
  63. package/dist/commands/task/shared.d.ts.map +1 -1
  64. package/dist/commands/task/shared.js +37 -0
  65. package/dist/commands/task/start-ready.js +1 -1
  66. package/dist/commands/upgrade.command.d.ts.map +1 -1
  67. package/dist/commands/upgrade.command.js +2 -2
  68. package/dist/commands/upgrade.d.ts.map +1 -1
  69. package/dist/commands/upgrade.js +263 -294
  70. package/dist/commands/workflow-build.command.d.ts.map +1 -1
  71. package/dist/commands/workflow-build.command.js +7 -0
  72. package/dist/commands/workflow-playbook.command.d.ts.map +1 -1
  73. package/dist/commands/workflow-playbook.command.js +0 -1
  74. package/dist/shared/policy-gateway.d.ts +15 -0
  75. package/dist/shared/policy-gateway.d.ts.map +1 -0
  76. package/dist/shared/policy-gateway.js +49 -0
  77. package/dist/shared/protected-paths.d.ts.map +1 -1
  78. package/dist/shared/protected-paths.js +1 -0
  79. package/dist/shared/runtime-artifacts.d.ts +2 -2
  80. package/dist/shared/runtime-artifacts.d.ts.map +1 -1
  81. package/dist/shared/runtime-artifacts.js +4 -0
  82. package/dist/workflow-runtime/build.d.ts +1 -1
  83. package/dist/workflow-runtime/build.d.ts.map +1 -1
  84. package/dist/workflow-runtime/build.js +14 -2
  85. package/package.json +2 -2
@@ -0,0 +1,31 @@
1
+ # Workflow: release
2
+
3
+ Use this module when task touches release/version/publish flows.
4
+
5
+ ## Required sequence
6
+
7
+ 1. CHECKPOINT A: confirm clean tracked tree and approved scope.
8
+ 2. CHECKPOINT B: generate release plan and freeze version/tag target.
9
+ 3. Generate release notes with complete human-readable coverage of all task-level changes.
10
+ 4. Run release prepublish checks.
11
+ 5. CHECKPOINT C: apply release and push/tag only after all gates pass.
12
+ 6. Record release evidence (commands, outputs, resulting version/tag).
13
+
14
+ ## Command contract
15
+
16
+ ```bash
17
+ git status --short --untracked-files=no
18
+ agentplane task plan set <task-id> --text "Release plan: version=<v>, tag=<t>, scope=<...>" --updated-by <ROLE>
19
+ agentplane task plan approve <task-id> --by ORCHESTRATOR
20
+ agentplane release plan --patch
21
+ agentplane release apply --push --yes
22
+ agentplane verify <task-id> --ok|--rework --by <ROLE> --note "Release checks: ..."
23
+ agentplane finish <task-id> --author <ROLE> --body "Verified: release" --result "Release <v> published" --commit <git-rev> --close-commit
24
+ ```
25
+
26
+ ## Constraints
27
+
28
+ - MUST NOT perform irreversible release actions before explicit approval.
29
+ - MUST NOT skip parity/version checks.
30
+ - MUST NOT bypass required notes validation.
31
+ - MUST stop and request re-approval if release scope/tag/version changes.
@@ -0,0 +1,20 @@
1
+ # Workflow: upgrade
2
+
3
+ Use this module when task runs `agentplane upgrade` or touches `.agentplane/.upgrade/**`.
4
+
5
+ ## Required sequence
6
+
7
+ 1. Run upgrade command and capture run directory.
8
+ 2. Read upgrade review report:
9
+ - agent mode: `.agentplane/.upgrade/agent/<runId>/review.json`
10
+ - auto mode: `.agentplane/.upgrade/last-review.json`
11
+ 3. Apply upgrade as replace-all for managed files (`agentplane upgrade --auto`), excluding task data paths.
12
+ 4. For `.agentplane/policy/incidents.md`, keep existing local content and append incoming policy content (never replace non-empty local incidents file).
13
+ 5. Ensure the upgrade produced a dedicated upgrade commit with version in commit message.
14
+ 6. Verify policy/agent consistency and routing checks.
15
+ 7. Record run path and reviewed files in task notes.
16
+
17
+ ## Minimum verification
18
+
19
+ - `node .agentplane/policy/check-routing.mjs`
20
+ - `agentplane agents`
package/bin/agentplane.js CHANGED
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env node
2
- import { execFileSync } from "node:child_process";
3
2
  import path from "node:path";
4
- import { readFile, stat } from "node:fs/promises";
3
+ import { stat } from "node:fs/promises";
5
4
  import { fileURLToPath } from "node:url";
5
+ import { distExists, isPackageBuildFresh } from "./dist-guard.js";
6
6
 
7
7
  async function exists(p) {
8
8
  try {
@@ -36,88 +36,18 @@ async function maybeWarnGlobalBinaryInRepoCheckout() {
36
36
  );
37
37
  }
38
38
 
39
- async function readJsonIfExists(p) {
40
- let raw = "";
41
- try {
42
- raw = await readFile(p, "utf8");
43
- } catch {
44
- return null;
45
- }
46
- try {
47
- return JSON.parse(raw);
48
- } catch {
49
- return null;
50
- }
51
- }
52
-
53
- function resolveGitHead(cwd) {
54
- try {
55
- return execFileSync("git", ["rev-parse", "HEAD"], { cwd, encoding: "utf8" }).trim() || null;
56
- } catch {
57
- return null;
58
- }
59
- }
60
-
61
- function hasSrcChanges(cwd) {
62
- try {
63
- const out = execFileSync(
64
- "git",
65
- ["status", "--porcelain", "--untracked-files=all", "--", "src"],
66
- {
67
- cwd,
68
- encoding: "utf8",
69
- },
70
- );
71
- return out.trim().length > 0;
72
- } catch {
73
- return false;
74
- }
75
- }
76
-
77
- async function fileMtimeMs(p) {
78
- try {
79
- const s = await stat(p);
80
- if (!s.isFile()) return null;
81
- return s.mtimeMs;
82
- } catch {
83
- return null;
39
+ function isHooksRunCommitMsgInvocation(argv) {
40
+ const args = argv.slice(2).map((value) => String(value ?? "").trim());
41
+ for (let i = 0; i < args.length; i += 1) {
42
+ if (args[i] !== "hooks") continue;
43
+ return args[i + 1] === "run" && args[i + 2] === "commit-msg";
84
44
  }
45
+ return false;
85
46
  }
86
47
 
87
- async function isPackageBuildFresh(packageRoot) {
88
- const manifestPath = path.join(packageRoot, "dist", ".build-manifest.json");
89
- const manifest = await readJsonIfExists(manifestPath);
90
- if (!manifest || manifest.schema_version !== 1) {
91
- return { ok: false, reason: "manifest_missing" };
92
- }
93
-
94
- const currentHead = resolveGitHead(packageRoot);
95
- if (manifest.git_head && currentHead && manifest.git_head !== currentHead) {
96
- return { ok: false, reason: "git_head_changed" };
97
- }
98
-
99
- if (hasSrcChanges(packageRoot)) {
100
- return { ok: false, reason: "src_dirty" };
101
- }
102
-
103
- const srcCliMtimeMs = await fileMtimeMs(path.join(packageRoot, "src", "cli.ts"));
104
- const srcIndexMtimeMs = await fileMtimeMs(path.join(packageRoot, "src", "index.ts"));
105
- if (
106
- typeof manifest.src_cli_mtime_ms === "number" &&
107
- typeof srcCliMtimeMs === "number" &&
108
- srcCliMtimeMs > manifest.src_cli_mtime_ms
109
- ) {
110
- return { ok: false, reason: "src_cli_newer_than_manifest" };
111
- }
112
- if (
113
- typeof manifest.src_index_mtime_ms === "number" &&
114
- typeof srcIndexMtimeMs === "number" &&
115
- srcIndexMtimeMs > manifest.src_index_mtime_ms
116
- ) {
117
- return { ok: false, reason: "src_index_newer_than_manifest" };
118
- }
119
-
120
- return { ok: true, reason: "fresh" };
48
+ function isPathInside(baseDir, targetPath) {
49
+ const rel = path.relative(path.resolve(baseDir), path.resolve(targetPath));
50
+ return rel === "" || (!rel.startsWith("..") && !path.isAbsolute(rel));
121
51
  }
122
52
 
123
53
  async function assertDistUpToDate() {
@@ -127,8 +57,7 @@ async function assertDistUpToDate() {
127
57
  if (!inRepo) return true;
128
58
 
129
59
  const allowStale = (process.env.AGENTPLANE_DEV_ALLOW_STALE_DIST ?? "").trim() === "1";
130
- const agentplaneDistDir = path.join(agentplaneRoot, "dist");
131
- if (!(await exists(agentplaneDistDir))) {
60
+ if (!(await distExists(agentplaneRoot))) {
132
61
  process.stderr.write(
133
62
  "error: agentplane dist is missing for this repo checkout.\n" +
134
63
  "Fix:\n" +
@@ -141,16 +70,40 @@ async function assertDistUpToDate() {
141
70
 
142
71
  const repoRoot = path.resolve(agentplaneRoot, "..", "..");
143
72
  const coreRoot = path.join(repoRoot, "packages", "core");
144
- const checks = [{ name: "agentplane", root: agentplaneRoot }];
145
- if (await exists(path.join(coreRoot, "src"))) checks.push({ name: "core", root: coreRoot });
73
+ const checks = [
74
+ {
75
+ name: "agentplane",
76
+ root: agentplaneRoot,
77
+ watchedPaths: ["src", "bin/agentplane.js", "bin/dist-guard.js"],
78
+ },
79
+ ];
80
+ if (await exists(path.join(coreRoot, "src")))
81
+ checks.push({ name: "core", root: coreRoot, watchedPaths: ["src"] });
146
82
 
147
83
  const staleReasons = [];
148
84
  for (const check of checks) {
149
- const result = await isPackageBuildFresh(check.root);
150
- if (!result.ok) staleReasons.push(`${check.name}:${result.reason}`);
85
+ const result = await isPackageBuildFresh(check.root, { watchedPaths: check.watchedPaths });
86
+ if (!result.ok) {
87
+ const detail =
88
+ Array.isArray(result.changedPaths) && result.changedPaths.length > 0
89
+ ? `(${result.changedPaths.slice(0, 5).join(", ")}${result.changedPaths.length > 5 ? ", ..." : ""})`
90
+ : "";
91
+ staleReasons.push(`${check.name}:${result.reason}${detail}`);
92
+ }
151
93
  }
152
94
 
153
95
  if (staleReasons.length > 0 && !allowStale) {
96
+ const runningInsideCheckout = isPathInside(repoRoot, process.cwd());
97
+ if (!runningInsideCheckout) {
98
+ process.stderr.write(
99
+ "warning: linked development binary has a stale build, but current working directory is outside the agentplane checkout.\n" +
100
+ "proceeding with existing dist output.\n" +
101
+ `detected: ${staleReasons.join(", ")}\n` +
102
+ "tip: rebuild (`bun run --filter=@agentplaneorg/core build && bun run --filter=agentplane build`) or reinstall from npm for stable global usage.\n",
103
+ );
104
+ return true;
105
+ }
106
+
154
107
  process.stderr.write(
155
108
  "error: refusing to run a stale repo build (manifest/git quick-check failed).\n" +
156
109
  "Fix:\n" +
@@ -167,5 +120,5 @@ async function assertDistUpToDate() {
167
120
  }
168
121
 
169
122
  await maybeWarnGlobalBinaryInRepoCheckout();
170
- const ok = await assertDistUpToDate();
123
+ const ok = isHooksRunCommitMsgInvocation(process.argv) ? true : await assertDistUpToDate();
171
124
  if (ok) await import("../dist/cli.js");
@@ -0,0 +1,124 @@
1
+ import { execFileSync } from "node:child_process";
2
+ import path from "node:path";
3
+ import { readFile, stat } from "node:fs/promises";
4
+
5
+ async function exists(p) {
6
+ try {
7
+ await stat(p);
8
+ return true;
9
+ } catch {
10
+ return false;
11
+ }
12
+ }
13
+
14
+ async function readJsonIfExists(p) {
15
+ let raw = "";
16
+ try {
17
+ raw = await readFile(p, "utf8");
18
+ } catch {
19
+ return null;
20
+ }
21
+ try {
22
+ return JSON.parse(raw);
23
+ } catch {
24
+ return null;
25
+ }
26
+ }
27
+
28
+ function resolveGitHead(cwd) {
29
+ try {
30
+ return execFileSync("git", ["rev-parse", "HEAD"], { cwd, encoding: "utf8" }).trim() || null;
31
+ } catch {
32
+ return null;
33
+ }
34
+ }
35
+
36
+ function listGitPaths(cwd, args) {
37
+ try {
38
+ const out = execFileSync("git", args, { cwd, encoding: "utf8" });
39
+ return out
40
+ .split(/\r?\n/u)
41
+ .map((line) => line.trim())
42
+ .filter(Boolean);
43
+ } catch {
44
+ return [];
45
+ }
46
+ }
47
+
48
+ function uniqueSorted(values) {
49
+ return [...new Set(values)].toSorted((a, b) => a.localeCompare(b));
50
+ }
51
+
52
+ function workingTreeChangedPaths(cwd, watchedPaths) {
53
+ const lines = listGitPaths(cwd, [
54
+ "status",
55
+ "--porcelain",
56
+ "--untracked-files=all",
57
+ "--",
58
+ ...watchedPaths,
59
+ ]);
60
+ return uniqueSorted(lines.map((line) => line.slice(3).trim()).filter(Boolean));
61
+ }
62
+
63
+ function committedChangedPathsSince(cwd, fromGitHead, watchedPaths) {
64
+ if (!fromGitHead) return [];
65
+ return uniqueSorted(
66
+ listGitPaths(cwd, ["diff", "--name-only", `${fromGitHead}..HEAD`, "--", ...watchedPaths]),
67
+ );
68
+ }
69
+
70
+ async function fileMtimeMs(p) {
71
+ try {
72
+ const s = await stat(p);
73
+ if (!s.isFile()) return null;
74
+ return s.mtimeMs;
75
+ } catch {
76
+ return null;
77
+ }
78
+ }
79
+
80
+ export async function isPackageBuildFresh(packageRoot, options = {}) {
81
+ const watchedPaths = options.watchedPaths ?? ["src"];
82
+ const manifestPath = path.join(packageRoot, "dist", ".build-manifest.json");
83
+ const manifest = await readJsonIfExists(manifestPath);
84
+ if (!manifest || manifest.schema_version !== 1) {
85
+ return { ok: false, reason: "manifest_missing", changedPaths: [] };
86
+ }
87
+
88
+ const currentHead = resolveGitHead(packageRoot);
89
+ const changedPaths = uniqueSorted([
90
+ ...committedChangedPathsSince(packageRoot, manifest.git_head, watchedPaths),
91
+ ...workingTreeChangedPaths(packageRoot, watchedPaths),
92
+ ]);
93
+
94
+ if (changedPaths.length > 0) {
95
+ return { ok: false, reason: "watched_paths_changed", changedPaths };
96
+ }
97
+
98
+ const srcCliMtimeMs = await fileMtimeMs(path.join(packageRoot, "src", "cli.ts"));
99
+ const srcIndexMtimeMs = await fileMtimeMs(path.join(packageRoot, "src", "index.ts"));
100
+ if (
101
+ typeof manifest.src_cli_mtime_ms === "number" &&
102
+ typeof srcCliMtimeMs === "number" &&
103
+ srcCliMtimeMs > manifest.src_cli_mtime_ms
104
+ ) {
105
+ return { ok: false, reason: "src_cli_newer_than_manifest", changedPaths: [] };
106
+ }
107
+ if (
108
+ typeof manifest.src_index_mtime_ms === "number" &&
109
+ typeof srcIndexMtimeMs === "number" &&
110
+ srcIndexMtimeMs > manifest.src_index_mtime_ms
111
+ ) {
112
+ return { ok: false, reason: "src_index_newer_than_manifest", changedPaths: [] };
113
+ }
114
+
115
+ if (manifest.git_head && currentHead && manifest.git_head !== currentHead) {
116
+ return { ok: true, reason: "fresh_after_non_runtime_head_change", changedPaths: [] };
117
+ }
118
+
119
+ return { ok: true, reason: "fresh", changedPaths: [] };
120
+ }
121
+
122
+ export async function distExists(packageRoot) {
123
+ return await exists(path.join(packageRoot, "dist"));
124
+ }
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "schema_version": 1,
3
3
  "package_dir": "/home/runner/work/agentplane/agentplane/packages/agentplane",
4
- "generated_at": "2026-03-05T13:42:04.968Z",
5
- "git_head": "7fdf3f088d7d98748b9ef5db06f5bef3766b534d",
6
- "src_cli_mtime_ms": 1772718067546.3232,
4
+ "generated_at": "2026-03-06T15:25:37.651Z",
5
+ "git_head": "1be96fc47916b76925f9bc59ce7d24176216a87f",
6
+ "src_cli_mtime_ms": 1772810682734.0005,
7
7
  "src_index_mtime_ms": null,
8
- "dist_cli_mtime_ms": 1772718124490.9443,
8
+ "dist_cli_mtime_ms": 1772810737168.81,
9
9
  "dist_index_mtime_ms": null,
10
- "tsbuildinfo_mtime_ms": 1772718124554.9443
10
+ "tsbuildinfo_mtime_ms": 1772810737234.8098
11
11
  }
@@ -1,10 +1,17 @@
1
+ import { type PolicyGatewayFlavor } from "../shared/policy-gateway.js";
1
2
  export type WorkflowMode = "direct" | "branch_pr";
2
3
  type AgentTemplate = {
3
4
  fileName: string;
4
5
  contents: string;
5
6
  };
7
+ export type PolicyTemplate = {
8
+ relativePath: string;
9
+ contents: string;
10
+ };
6
11
  export declare function loadAgentsTemplate(): Promise<string>;
7
12
  export declare function loadAgentTemplates(): Promise<AgentTemplate[]>;
13
+ export declare function loadPolicyTemplates(): Promise<PolicyTemplate[]>;
8
14
  export declare function filterAgentsByWorkflow(template: string, workflow: WorkflowMode): string;
15
+ export declare function loadPolicyGatewayTemplate(flavor: PolicyGatewayFlavor): Promise<string>;
9
16
  export {};
10
17
  //# sourceMappingURL=agents-template.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"agents-template.d.ts","sourceRoot":"","sources":["../../src/agents/agents-template.ts"],"names":[],"mappings":"AAWA,MAAM,MAAM,YAAY,GAAG,QAAQ,GAAG,WAAW,CAAC;AAElD,KAAK,aAAa,GAAG;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC;AAwC5D,wBAAsB,kBAAkB,IAAI,OAAO,CAAC,MAAM,CAAC,CAG1D;AAED,wBAAsB,kBAAkB,IAAI,OAAO,CAAC,aAAa,EAAE,CAAC,CAanE;AAED,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,YAAY,GAAG,MAAM,CASvF"}
1
+ {"version":3,"file":"agents-template.d.ts","sourceRoot":"","sources":["../../src/agents/agents-template.ts"],"names":[],"mappings":"AAIA,OAAO,EAGL,KAAK,mBAAmB,EACzB,MAAM,6BAA6B,CAAC;AAUrC,MAAM,MAAM,YAAY,GAAG,QAAQ,GAAG,WAAW,CAAC;AAElD,KAAK,aAAa,GAAG;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC;AAC5D,MAAM,MAAM,cAAc,GAAG;IAAE,YAAY,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC;AAwCxE,wBAAsB,kBAAkB,IAAI,OAAO,CAAC,MAAM,CAAC,CAE1D;AAED,wBAAsB,kBAAkB,IAAI,OAAO,CAAC,aAAa,EAAE,CAAC,CAanE;AAsBD,wBAAsB,mBAAmB,IAAI,OAAO,CAAC,cAAc,EAAE,CAAC,CAerE;AAED,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,YAAY,GAAG,MAAM,CASvF;AAED,wBAAsB,yBAAyB,CAAC,MAAM,EAAE,mBAAmB,GAAG,OAAO,CAAC,MAAM,CAAC,CAI5F"}
@@ -1,8 +1,10 @@
1
1
  import { readdir, readFile } from "node:fs/promises";
2
2
  import path from "node:path";
3
3
  import { fileURLToPath } from "node:url";
4
+ import { policyGatewayFileName, renderPolicyGatewayTemplateText, } from "../shared/policy-gateway.js";
4
5
  const AGENTS_TEMPLATE_URL = new URL("../../assets/AGENTS.md", import.meta.url);
5
6
  const AGENTS_DIR_URL = new URL("../../assets/agents/", import.meta.url);
7
+ const POLICY_DIR_URL = new URL("../../assets/policy/", import.meta.url);
6
8
  const HEADING_RE = /^(#+)\s+(.*)$/;
7
9
  function ensureTrailingNewline(text) {
8
10
  return text.endsWith("\n") ? text : `${text}\n`;
@@ -41,8 +43,7 @@ function removeSections(lines, titles) {
41
43
  return lines.filter((_line, index) => !shouldRemove.has(index));
42
44
  }
43
45
  export async function loadAgentsTemplate() {
44
- const text = await readFile(AGENTS_TEMPLATE_URL, "utf8");
45
- return ensureTrailingNewline(text.trimEnd());
46
+ return loadPolicyGatewayTemplate("codex");
46
47
  }
47
48
  export async function loadAgentTemplates() {
48
49
  const dirPath = fileURLToPath(AGENTS_DIR_URL);
@@ -56,6 +57,39 @@ export async function loadAgentTemplates() {
56
57
  }
57
58
  return templates;
58
59
  }
60
+ async function listFilesRecursive(dirPath, relPrefix = "") {
61
+ const entries = await readdir(dirPath, { withFileTypes: true });
62
+ const sorted = entries.toSorted((a, b) => a.name.localeCompare(b.name));
63
+ const files = [];
64
+ for (const entry of sorted) {
65
+ // Ignore editor/OS hidden metadata files.
66
+ if (entry.name.startsWith("."))
67
+ continue;
68
+ const absPath = path.join(dirPath, entry.name);
69
+ const relPath = relPrefix ? `${relPrefix}/${entry.name}` : entry.name;
70
+ if (entry.isDirectory()) {
71
+ files.push(...(await listFilesRecursive(absPath, relPath)));
72
+ continue;
73
+ }
74
+ if (entry.isFile())
75
+ files.push(relPath);
76
+ }
77
+ return files;
78
+ }
79
+ export async function loadPolicyTemplates() {
80
+ const dirPath = fileURLToPath(POLICY_DIR_URL);
81
+ const relFiles = await listFilesRecursive(dirPath);
82
+ const templates = [];
83
+ for (const relFile of relFiles) {
84
+ const filePath = path.join(dirPath, relFile);
85
+ const contents = await readFile(filePath, "utf8");
86
+ templates.push({
87
+ relativePath: relFile.replaceAll("\\", "/"),
88
+ contents: ensureTrailingNewline(contents.trimEnd()),
89
+ });
90
+ }
91
+ return templates;
92
+ }
59
93
  export function filterAgentsByWorkflow(template, workflow) {
60
94
  const lines = template.replaceAll("\r\n", "\n").split("\n");
61
95
  const removeTitles = workflow === "direct"
@@ -64,3 +98,8 @@ export function filterAgentsByWorkflow(template, workflow) {
64
98
  const filtered = removeSections(lines, removeTitles);
65
99
  return ensureTrailingNewline(filtered.join("\n").trimEnd());
66
100
  }
101
+ export async function loadPolicyGatewayTemplate(flavor) {
102
+ const text = await readFile(AGENTS_TEMPLATE_URL, "utf8");
103
+ const rendered = renderPolicyGatewayTemplateText(text, policyGatewayFileName(flavor));
104
+ return ensureTrailingNewline(rendered.trimEnd());
105
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"command-guide.d.ts","sourceRoot":"","sources":["../../src/cli/command-guide.ts"],"names":[],"mappings":"AAkMA,wBAAgB,SAAS,IAAI,MAAM,EAAE,CAEpC;AAED,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAOzD;AAED,wBAAgB,gBAAgB,IAAI,MAAM,CA2FzC"}
1
+ {"version":3,"file":"command-guide.d.ts","sourceRoot":"","sources":["../../src/cli/command-guide.ts"],"names":[],"mappings":"AA0MA,wBAAgB,SAAS,IAAI,MAAM,EAAE,CAEpC;AAED,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAOzD;AAED,wBAAgB,gBAAgB,IAAI,MAAM,CA6FzC"}
@@ -32,6 +32,10 @@ const CHEAT_SHEET_ROWS = [
32
32
  operation: "CODER/TESTER/DOCS: start checkout (branch_pr)",
33
33
  command: "`agentplane work start <task-id> --agent <ROLE> --slug <slug> --worktree`",
34
34
  },
35
+ {
36
+ operation: "CODER/TESTER: start task deterministically",
37
+ command: '`agentplane task start-ready <task-id> --author <ROLE> --body "Start: ..."` (run after successful `task plan approve`; do not run in parallel)',
38
+ },
35
39
  {
36
40
  operation: "CODER/TESTER/DOCS: update PR artifacts",
37
41
  command: "`agentplane pr update <task-id>`",
@@ -84,15 +88,17 @@ const ROLE_GUIDES = [
84
88
  "- `task new` auto-seeds README sections; use `task scaffold` only for backfill/import/manual repair.",
85
89
  '- Plan lifecycle: `agentplane task plan set <task-id> --text "..." --updated-by <ROLE>` -> `agentplane task plan approve <task-id> --by <id>`',
86
90
  "- Verify Steps discipline: if a task primary tag is verify-required (default: code/data/ops), fill `## Verify Steps` before plan approval.",
87
- '- Task docs (when planning needs it): `agentplane task doc set <task-id> --section Summary --text "..."`',
91
+ "- Doc quality gate: `task plan approve` and `finish` fail when required agent-filled sections (`Summary/Scope/Plan/Risks/Rollback Plan`, per config) are empty/TODO placeholders.",
92
+ '- Task docs (when planning needs it): `agentplane task doc set <task-id> --section Summary --text "..."`; if `--text`/`--file` contains multiple known headings, the command applies it as one batched full-doc update.',
88
93
  ],
89
94
  },
90
95
  {
91
96
  role: "CODER",
92
97
  lines: [
93
- "- direct mode: single-stream in the current checkout; `agentplane work start <task-id> --agent <ROLE> --slug <slug>` records the active task and keeps the current branch (no task branches). Use `task doc set` / `task plan set` for normal docs updates.",
98
+ "- direct mode: single-stream in the current checkout; `agentplane work start <task-id> --agent <ROLE> --slug <slug>` records the active task and keeps the current branch (no task branches). Use `task doc set` / `task plan set` for normal docs updates; batched doc updates are allowed before approval.",
94
99
  "- branch_pr: `agentplane work start <task-id> --agent <ROLE> --slug <slug> --worktree`",
95
- '- Status updates: `agentplane start <task-id> --author <ROLE> --body "Start: ..."` / `agentplane block <task-id> --author <ROLE> --body "Blocked: ..."`',
100
+ '- Start status (deterministic): `agentplane task start-ready <task-id> --author <ROLE> --body "Start: ..."` (after `task plan approve`, sequential only).',
101
+ '- Other status updates: `agentplane block <task-id> --author <ROLE> --body "Blocked: ..."`',
96
102
  "- Verify Steps: `agentplane task verify-show <task-id>` (use as the verification contract before recording results).",
97
103
  '- Verify: `agentplane verify <task-id> --ok|--rework --by <ROLE> --note "..."`',
98
104
  '- PR artifacts (branch_pr): `agentplane pr open <task-id> --branch task/<task-id>/<slug> --author <ROLE>` / `agentplane pr update <task-id>` / `agentplane pr note <task-id> --author <ROLE> --body "..."`',
@@ -102,9 +108,10 @@ const ROLE_GUIDES = [
102
108
  {
103
109
  role: "TESTER",
104
110
  lines: [
105
- "- direct mode: single-stream in the current checkout; `agentplane work start <task-id> --agent <ROLE> --slug <slug>` records the active task and keeps the current branch (no task branches). Use `task doc set` / `task plan set` for normal docs updates.",
111
+ "- direct mode: single-stream in the current checkout; `agentplane work start <task-id> --agent <ROLE> --slug <slug>` records the active task and keeps the current branch (no task branches). Use `task doc set` / `task plan set` for normal docs updates; batched doc updates are allowed before approval.",
106
112
  "- branch_pr: `agentplane work start <task-id> --agent <ROLE> --slug <slug> --worktree`",
107
- '- Status updates: `agentplane start <task-id> --author <ROLE> --body "Start: ..."` / `agentplane block <task-id> --author <ROLE> --body "Blocked: ..."`',
113
+ '- Start status (deterministic): `agentplane task start-ready <task-id> --author <ROLE> --body "Start: ..."` (after `task plan approve`, sequential only).',
114
+ '- Other status updates: `agentplane block <task-id> --author <ROLE> --body "Blocked: ..."`',
108
115
  "- Verify Steps: `agentplane task verify-show <task-id>` (treat as the verification contract).",
109
116
  '- Verify: `agentplane verify <task-id> --ok|--rework --by <ROLE> --note "..."`',
110
117
  '- PR artifacts (branch_pr): `agentplane pr open <task-id> --branch task/<task-id>/<slug> --author <ROLE>` / `agentplane pr update <task-id>` / `agentplane pr note <task-id> --author <ROLE> --body "..."`',
@@ -114,7 +121,7 @@ const ROLE_GUIDES = [
114
121
  {
115
122
  role: "DOCS",
116
123
  lines: [
117
- '- Task docs: `agentplane task doc set <task-id> --section Summary --text "..."` (repeat per section or use `--file`)',
124
+ '- Task docs: `agentplane task doc set <task-id> --section Summary --text "..."` (repeat per section, or send one multi-heading full-doc payload via `--text`/`--file`)',
118
125
  '- PR notes: `agentplane pr note <task-id> --author DOCS --body "..."`',
119
126
  '- Commit: `agentplane guard commit <task-id> -m "<emoji> <suffix> <scope>: <summary>"` / `agentplane commit <task-id> -m "<emoji> <suffix> <scope>: <summary>" --allow <path-prefix>` / preferred close path: `agentplane finish <task-id> --close-commit [--close-unstage-others]`',
120
127
  ],
@@ -137,7 +144,7 @@ const ROLE_GUIDES = [
137
144
  {
138
145
  role: "CREATOR",
139
146
  lines: [
140
- '- Task bookkeeping: `agentplane task update <task-id> ...` / `agentplane start <task-id> --author CREATOR --body "Start: ..."`',
147
+ '- Task bookkeeping: `agentplane task update <task-id> ...` / `agentplane task start-ready <task-id> --author CREATOR --body "Start: ..."`',
141
148
  '- Commits: `agentplane guard commit <task-id> -m "<emoji> <suffix> <scope>: <summary>"` / `agentplane commit <task-id> -m "<emoji> <suffix> <scope>: <summary>" --allow <path-prefix>`',
142
149
  ],
143
150
  },
@@ -188,10 +195,10 @@ export function renderQuickstart() {
188
195
  return [
189
196
  "# agentplane quickstart",
190
197
  "",
191
- "AGENTS.md is the source of truth for the workflow/process policy; quickstart and role output are the source of truth for CLI syntax and artifacts.",
198
+ "The policy gateway file (AGENTS.md or CLAUDE.md) is the source of truth for workflow/process policy; quickstart and role output are the source of truth for CLI syntax and artifacts.",
192
199
  "Do not edit `.agentplane/tasks.json` by hand.",
193
200
  "",
194
- "- See AGENTS.md for the canonical workflow policy and approval gates.",
201
+ "- See your policy gateway file (AGENTS.md or CLAUDE.md) for canonical workflow policy and approval gates.",
195
202
  "",
196
203
  "## Project setup",
197
204
  "",
@@ -257,12 +264,14 @@ export function renderQuickstart() {
257
264
  "## Global flags",
258
265
  "",
259
266
  "- `--root <path>`: treat <path> as project root",
267
+ "- `--output <text|json>`: force global CLI output mode (`json` is agent-oriented envelope mode)",
260
268
  "- `--json-errors`: emit JSON-formatted errors",
261
269
  "- `--help` / `-h`: show help",
262
270
  "- `--version`: show version",
263
271
  "- `--no-update-check`: skip checking npm for a newer CLI version",
264
272
  "",
265
273
  "Notes:",
274
+ "- `AGENTPLANE_OUTPUT=json` enables global JSON output mode for agent runtimes.",
266
275
  "- `.env` at the repo root is loaded automatically (without overwriting existing environment variables).",
267
276
  "",
268
277
  "## Commit message format",
@@ -3,7 +3,7 @@ export declare const COMMAND_SNIPPETS: {
3
3
  readonly taskList: "agentplane task list";
4
4
  readonly taskShow: "agentplane task show <task-id>";
5
5
  readonly taskNew: "agentplane task new --title \"...\" --description \"...\" --priority med --owner CODER --tag <tag>";
6
- readonly startTask: "agentplane start <task-id> --author <ROLE> --body \"Start: ...\"";
6
+ readonly startTask: "agentplane task start-ready <task-id> --author <ROLE> --body \"Start: ...\"";
7
7
  readonly verifyTask: "agentplane verify <task-id> --ok|--rework --by <ROLE> --note \"...\"";
8
8
  readonly finishTask: "agentplane finish <task-id> --author <ROLE> --body \"Verified: ...\" --commit <git-rev> --close-commit";
9
9
  readonly quickstart: "agentplane quickstart";
@@ -3,7 +3,7 @@ export const COMMAND_SNIPPETS = {
3
3
  taskList: "agentplane task list",
4
4
  taskShow: "agentplane task show <task-id>",
5
5
  taskNew: 'agentplane task new --title "..." --description "..." --priority med --owner CODER --tag <tag>',
6
- startTask: 'agentplane start <task-id> --author <ROLE> --body "Start: ..."',
6
+ startTask: 'agentplane task start-ready <task-id> --author <ROLE> --body "Start: ..."',
7
7
  verifyTask: 'agentplane verify <task-id> --ok|--rework --by <ROLE> --note "..."',
8
8
  finishTask: 'agentplane finish <task-id> --author <ROLE> --body "Verified: ..." --commit <git-rev> --close-commit',
9
9
  quickstart: "agentplane quickstart",
@@ -37,7 +37,7 @@ async function cmdQuickstart(opts) {
37
37
  .filter((line) => line.length > 0);
38
38
  const payload = {
39
39
  source_of_truth: {
40
- workflow_policy: "AGENTS.md",
40
+ workflow_policy: "AGENTS.md|CLAUDE.md",
41
41
  cli_syntax: "quickstart/role output",
42
42
  },
43
43
  lines,
@@ -437,7 +437,7 @@ function renderAgentProfileBlock(opts) {
437
437
  ...(permissions.length > 0 ? ["", "Permissions:", ...permissions.map((s) => `- ${s}`)] : []),
438
438
  ...(workflow.length > 0 ? ["", "Workflow:", ...workflow.map((s) => `- ${s}`)] : []),
439
439
  "",
440
- `Source: .agentplane/agents/${opts.filename} (lower priority; see AGENTS.md)`,
440
+ `Source: .agentplane/agents/${opts.filename} (lower priority; see policy gateway file AGENTS.md or CLAUDE.md)`,
441
441
  ];
442
442
  return lines.join("\n").trimEnd();
443
443
  }
@@ -1 +1 @@
1
- {"version":3,"file":"ide.d.ts","sourceRoot":"","sources":["../../../../src/cli/run-cli/commands/ide.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACtE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAGrD,KAAK,aAAa,GAAG;IAAE,GAAG,CAAC,EAAE,QAAQ,GAAG,UAAU,CAAA;CAAE,CAAC;AAErD,eAAO,MAAM,WAAW,EAAE,WAAW,CAAC,aAAa,CAkBlD,CAAC;AAEF,wBAAsB,UAAU,CAAC,IAAI,EAAE;IACrC,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,GAAG,CAAC,EAAE,QAAQ,GAAG,UAAU,CAAC;IAC5B,IAAI,EAAE,OAAO,CAAC;CACf,GAAG,OAAO,CAAC,MAAM,CAAC,CAwClB;AAED,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,OAAO,GAAG,cAAc,CAAC,aAAa,CAAC,CAElF"}
1
+ {"version":3,"file":"ide.d.ts","sourceRoot":"","sources":["../../../../src/cli/run-cli/commands/ide.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACtE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAGrD,KAAK,aAAa,GAAG;IAAE,GAAG,CAAC,EAAE,QAAQ,GAAG,UAAU,CAAA;CAAE,CAAC;AAErD,eAAO,MAAM,WAAW,EAAE,WAAW,CAAC,aAAa,CAkBlD,CAAC;AAEF,wBAAsB,UAAU,CAAC,IAAI,EAAE;IACrC,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,GAAG,CAAC,EAAE,QAAQ,GAAG,UAAU,CAAC;IAC5B,IAAI,EAAE,OAAO,CAAC;CACf,GAAG,OAAO,CAAC,MAAM,CAAC,CA4ClB;AAED,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,OAAO,GAAG,cAAc,CAAC,aAAa,CAAC,CAElF"}
@@ -1,11 +1,12 @@
1
1
  import { mkdir, readFile } from "node:fs/promises";
2
2
  import path from "node:path";
3
3
  import { writeTextIfChanged } from "../../../shared/write-if-changed.js";
4
+ import { resolvePolicyGatewayForRepo } from "../../../shared/policy-gateway.js";
4
5
  import { wrapCommand } from "./wrap-command.js";
5
6
  export const ideSyncSpec = {
6
7
  id: ["ide", "sync"],
7
8
  group: "IDE",
8
- summary: "Generate IDE entrypoints from AGENTS.md.",
9
+ summary: "Generate IDE entrypoints from policy gateway file (AGENTS.md or CLAUDE.md).",
9
10
  options: [
10
11
  {
11
12
  kind: "string",
@@ -24,13 +25,17 @@ export const ideSyncSpec = {
24
25
  export async function cmdIdeSync(opts) {
25
26
  return wrapCommand({ command: "ide sync", rootOverride: opts.rootOverride }, async () => {
26
27
  const resolved = await opts.deps.getResolvedProject("ide sync");
27
- const agentsPath = path.join(resolved.gitRoot, "AGENTS.md");
28
+ const gateway = await resolvePolicyGatewayForRepo({
29
+ gitRoot: resolved.gitRoot,
30
+ fallbackFlavor: "codex",
31
+ });
32
+ const agentsPath = gateway.absPath;
28
33
  const agentsText = await readFile(agentsPath, "utf8");
29
34
  const header = [
30
35
  "<!--",
31
36
  " AUTOGENERATED by agentplane ide sync.",
32
37
  " DO NOT EDIT MANUALLY.",
33
- " Source: AGENTS.md",
38
+ ` Source: ${gateway.fileName}`,
34
39
  "-->",
35
40
  "",
36
41
  ].join("\n");
@@ -1 +1 @@
1
- {"version":3,"file":"ui.d.ts","sourceRoot":"","sources":["../../../../../src/cli/run-cli/commands/init/ui.ts"],"names":[],"mappings":"AAyBA,wBAAgB,iBAAiB,IAAI,MAAM,CAY1C;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,MAAM,CAG5E"}
1
+ {"version":3,"file":"ui.d.ts","sourceRoot":"","sources":["../../../../../src/cli/run-cli/commands/init/ui.ts"],"names":[],"mappings":"AAyBA,wBAAgB,iBAAiB,IAAI,MAAM,CAW1C;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,MAAM,CAG5E"}