agentplane 0.1.2 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,6 +1,20 @@
1
1
  # agentplane
2
2
 
3
- AgentPlane is an offline-first CLI for managing agent workflows (tasks, guardrails, recipes).
3
+ [![npm](https://img.shields.io/npm/v/agentplane.svg)](https://www.npmjs.com/package/agentplane)
4
+ [![Downloads](https://img.shields.io/npm/dm/agentplane.svg)](https://www.npmjs.com/package/agentplane)
5
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://github.com/basilisk-labs/agentplane/blob/main/LICENSE)
6
+ [![Node.js 20+](https://img.shields.io/badge/Node.js-20%2B-3c873a.svg)](https://github.com/basilisk-labs/agentplane/blob/main/docs/user/prerequisites.mdx)
7
+
8
+ Agent Plane is an offline-first CLI for running policy-driven agent workflows inside real repositories.
9
+ It turns "AI magic" into a predictable process with approvals, role boundaries, and audit-friendly artifacts.
10
+
11
+ ## Features
12
+
13
+ - Policy-first execution with explicit approvals and guardrails.
14
+ - Role-based workflows for teams: ORCHESTRATOR, PLANNER, CREATOR, INTEGRATOR, etc.
15
+ - Two workflow modes: `direct` (single checkout) and `branch_pr` (worktrees + integration).
16
+ - Task tracking, verification, and exports baked in.
17
+ - Recipes for repeatable setup and automation.
4
18
 
5
19
  ## Install
6
20
 
@@ -8,12 +22,71 @@ AgentPlane is an offline-first CLI for managing agent workflows (tasks, guardrai
8
22
  npm install -g agentplane
9
23
  ```
10
24
 
25
+ Or run without installing:
26
+
27
+ ```bash
28
+ npx agentplane --help
29
+ ```
30
+
11
31
  ## Requirements
12
32
 
13
33
  - Node.js >= 20
14
34
 
15
- ## CLI
35
+ ## Quickstart
36
+
37
+ Initialize a repository:
38
+
39
+ ```bash
40
+ npx agentplane init
41
+ ```
42
+
43
+ See the built-in quickstart guide:
44
+
45
+ ```bash
46
+ npx agentplane quickstart
47
+ ```
48
+
49
+ Switch workflow mode if you need a structured team flow:
50
+
51
+ ```bash
52
+ npx agentplane config set workflow_mode branch_pr
53
+ ```
54
+
55
+ ## Common Commands
16
56
 
17
57
  ```bash
18
58
  agentplane --help
59
+ agentplane quickstart
60
+ agentplane config show
61
+ agentplane task list
62
+ agentplane task new --title "..." --description "..." --priority med --owner ORCHESTRATOR --tag docs
63
+ agentplane verify <task-id>
64
+ agentplane finish <task-id>
65
+ agentplane recipes list
19
66
  ```
67
+
68
+ ## Docs and Guides
69
+
70
+ - Documentation index: https://github.com/basilisk-labs/agentplane/tree/main/docs
71
+ - Workflow overview: https://github.com/basilisk-labs/agentplane/blob/main/docs/user/workflow.mdx
72
+ - CLI commands: https://github.com/basilisk-labs/agentplane/blob/main/docs/user/commands.mdx
73
+ - Project layout: https://github.com/basilisk-labs/agentplane/blob/main/docs/developer/project-layout.mdx
74
+ - Recipes: https://github.com/basilisk-labs/agentplane/tree/main/agentplane-recipes
75
+
76
+ ## How it works
77
+
78
+ Agent Plane expects a repository policy file (`AGENTS.md`) plus a project config (`.agentplane/config.json`).
79
+ Together, they define role boundaries, approval gates, and the execution pipeline:
80
+
81
+ ```text
82
+ Preflight -> Plan -> Approval -> Tasks -> Verify -> Finish -> Export
83
+ ```
84
+
85
+ ## Support
86
+
87
+ - Issues: https://github.com/basilisk-labs/agentplane/issues
88
+ - Contributing: https://github.com/basilisk-labs/agentplane/blob/main/CONTRIBUTING.md
89
+
90
+ ## License
91
+
92
+ MIT
package/assets/AGENTS.md CHANGED
@@ -163,6 +163,7 @@ Every task must have these sections in its README or task doc:
163
163
 
164
164
  ## Updating task docs
165
165
 
166
+ - Workflow/task artifacts (task READMEs, PR artifacts, task exports) must be updated via `agentplane` commands, not manual edits.
166
167
  - Task README updates must be done via `agentplane task doc set ...`
167
168
  - Manual edits to `.agentplane/tasks/<task-id>/README.md` are prohibited.
168
169
 
@@ -170,6 +171,8 @@ Every task must have these sections in its README or task doc:
170
171
 
171
172
  # COMMIT WORKFLOW
172
173
 
174
+ - Commits and pushes must go through `agentplane` commands (no direct `git commit`/`git push`). If a push is needed but no `agentplane` command exists, ask for guidance.
175
+
173
176
  ## Commit message semantics (canonical)
174
177
 
175
178
  There are two supported modes:
@@ -1 +1 @@
1
- {"version":3,"file":"command-guide.d.ts","sourceRoot":"","sources":["../src/command-guide.ts"],"names":[],"mappings":"AA4KA,wBAAgB,SAAS,IAAI,MAAM,EAAE,CAEpC;AAED,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAOzD;AAED,wBAAgB,gBAAgB,IAAI,MAAM,CA2EzC"}
1
+ {"version":3,"file":"command-guide.d.ts","sourceRoot":"","sources":["../src/command-guide.ts"],"names":[],"mappings":"AA4KA,wBAAgB,SAAS,IAAI,MAAM,EAAE,CAEpC;AAED,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAOzD;AAED,wBAAgB,gBAAgB,IAAI,MAAM,CA4EzC"}
@@ -228,6 +228,7 @@ export function renderQuickstart() {
228
228
  "- `--json`: emit JSON-formatted errors",
229
229
  "- `--help` / `-h`: show help",
230
230
  "- `--version`: show version",
231
+ "- `--no-update-check`: skip checking npm for a newer CLI version",
231
232
  "",
232
233
  "Notes:",
233
234
  "- `.env` at the repo root is loaded automatically (without overwriting existing environment variables).",
@@ -1 +1 @@
1
- {"version":3,"file":"help.d.ts","sourceRoot":"","sources":["../src/help.ts"],"names":[],"mappings":"AAAA,wBAAgB,UAAU,IAAI,MAAM,CA2HnC"}
1
+ {"version":3,"file":"help.d.ts","sourceRoot":"","sources":["../src/help.ts"],"names":[],"mappings":"AAAA,wBAAgB,UAAU,IAAI,MAAM,CA4HnC"}
package/dist/help.js CHANGED
@@ -3,15 +3,16 @@ export function renderHelp() {
3
3
  "agentplane (v1 prototype)",
4
4
  "",
5
5
  "Usage:",
6
- " agentplane [--root <path>] [--json] <namespace> <command> [options]",
7
- " agentplane [--root <path>] [--json] <command> [options]",
6
+ " agentplane [--root <path>] [--json] [--no-update-check] <namespace> <command> [options]",
7
+ " agentplane [--root <path>] [--json] [--no-update-check] <command> [options]",
8
8
  "",
9
9
  "Core namespaces (implemented in this prototype): config, mode, task, ide, recipes, backend",
10
10
  "",
11
11
  "Flags:",
12
- " --help, -h Show help",
13
- " --version Show version",
14
- " --root <path> Treat <path> as project root",
12
+ " --help, -h Show help",
13
+ " --version Show version",
14
+ " --root <path> Treat <path> as project root",
15
+ " --no-update-check Skip checking npm for a newer CLI version",
15
16
  "",
16
17
  "Quickstart commands:",
17
18
  " agentplane quickstart",
@@ -1 +1 @@
1
- {"version":3,"file":"run-cli.d.ts","sourceRoot":"","sources":["../src/run-cli.ts"],"names":[],"mappings":"AAgtQA,wBAAsB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAu/D5D"}
1
+ {"version":3,"file":"run-cli.d.ts","sourceRoot":"","sources":["../src/run-cli.ts"],"names":[],"mappings":"AAo8QA,wBAAsB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CA4/D5D"}
package/dist/run-cli.js CHANGED
@@ -30,6 +30,7 @@ function gitEnv() {
30
30
  function parseGlobalArgs(argv) {
31
31
  let help = false;
32
32
  let version = false;
33
+ let noUpdateCheck = false;
33
34
  let jsonErrors = false;
34
35
  let root;
35
36
  const rest = [];
@@ -45,6 +46,10 @@ function parseGlobalArgs(argv) {
45
46
  version = true;
46
47
  continue;
47
48
  }
49
+ if (arg === "--no-update-check") {
50
+ noUpdateCheck = true;
51
+ continue;
52
+ }
48
53
  if (arg === "--json") {
49
54
  jsonErrors = true;
50
55
  continue;
@@ -63,7 +68,7 @@ function parseGlobalArgs(argv) {
63
68
  }
64
69
  rest.push(arg);
65
70
  }
66
- return { globals: { help, version, root, jsonErrors }, rest };
71
+ return { globals: { help, version, noUpdateCheck, root, jsonErrors }, rest };
67
72
  }
68
73
  function writeError(err, jsonErrors) {
69
74
  const hint = renderErrorHint(err);
@@ -157,6 +162,79 @@ function usageMessage(usage, example) {
157
162
  function backendNotSupportedMessage(feature) {
158
163
  return `Backend does not support ${feature}`;
159
164
  }
165
+ const UPDATE_CHECK_PACKAGE = "agentplane";
166
+ const UPDATE_CHECK_URL = `https://registry.npmjs.org/${UPDATE_CHECK_PACKAGE}/latest`;
167
+ const UPDATE_CHECK_TIMEOUT_MS = 1500;
168
+ function parseVersionParts(version) {
169
+ const cleaned = version.trim().replace(/^v/i, "").split("+")[0] ?? "";
170
+ const [mainRaw, prereleaseRaw] = cleaned.split("-", 2);
171
+ const main = (mainRaw ?? "")
172
+ .split(".")
173
+ .filter((part) => part.length > 0)
174
+ .map((part) => {
175
+ const parsed = Number.parseInt(part, 10);
176
+ return Number.isFinite(parsed) ? parsed : 0;
177
+ });
178
+ return { main, prerelease: prereleaseRaw ? prereleaseRaw.trim() : null };
179
+ }
180
+ function compareVersions(left, right) {
181
+ const a = parseVersionParts(left);
182
+ const b = parseVersionParts(right);
183
+ const length = Math.max(a.main.length, b.main.length);
184
+ for (let i = 0; i < length; i++) {
185
+ const partA = a.main[i] ?? 0;
186
+ const partB = b.main[i] ?? 0;
187
+ if (partA !== partB)
188
+ return partA > partB ? 1 : -1;
189
+ }
190
+ if (a.prerelease === b.prerelease)
191
+ return 0;
192
+ if (a.prerelease === null)
193
+ return 1;
194
+ if (b.prerelease === null)
195
+ return -1;
196
+ return a.prerelease.localeCompare(b.prerelease);
197
+ }
198
+ function isTruthyEnv(value) {
199
+ if (!value)
200
+ return false;
201
+ const normalized = value.trim().toLowerCase();
202
+ return normalized === "1" || normalized === "true" || normalized === "yes" || normalized === "on";
203
+ }
204
+ async function fetchLatestNpmVersion() {
205
+ const controller = new AbortController();
206
+ const timeout = setTimeout(() => controller.abort(), UPDATE_CHECK_TIMEOUT_MS);
207
+ try {
208
+ const res = await fetch(UPDATE_CHECK_URL, {
209
+ headers: { "User-Agent": "agentplane" },
210
+ signal: controller.signal,
211
+ });
212
+ if (!res.ok)
213
+ return null;
214
+ const data = (await res.json());
215
+ const version = typeof data.version === "string" ? data.version.trim() : "";
216
+ return version.length > 0 ? version : null;
217
+ }
218
+ catch {
219
+ return null;
220
+ }
221
+ finally {
222
+ clearTimeout(timeout);
223
+ }
224
+ }
225
+ async function maybeWarnOnUpdate(opts) {
226
+ if (opts.skip || opts.jsonErrors)
227
+ return;
228
+ if (isTruthyEnv(process.env.AGENTPLANE_NO_UPDATE_CHECK))
229
+ return;
230
+ const latest = await fetchLatestNpmVersion();
231
+ if (!latest)
232
+ return;
233
+ if (compareVersions(latest, opts.currentVersion) <= 0)
234
+ return;
235
+ const message = `Update available: ${UPDATE_CHECK_PACKAGE} ${opts.currentVersion} → ${latest}. Run: npm i -g ${UPDATE_CHECK_PACKAGE}@latest`;
236
+ process.stderr.write(`${warnMessage(message)}\n`);
237
+ }
160
238
  function workflowModeMessage(actual, expected) {
161
239
  return `Invalid workflow_mode: ${actual ?? "unknown"} (expected ${expected})`;
162
240
  }
@@ -359,7 +437,7 @@ const BRANCH_STATUS_USAGE_EXAMPLE = "agentplane branch status --base main";
359
437
  const BRANCH_REMOVE_USAGE = "Usage: agentplane branch remove [--branch <name>] [--worktree <path>] [--force] [--quiet]";
360
438
  const BRANCH_REMOVE_USAGE_EXAMPLE = "agentplane branch remove --branch task/20260203-F1Q8AB --worktree .agentplane/worktrees/task";
361
439
  const UPGRADE_USAGE = "Usage: agentplane upgrade [--tag <tag>] [--dry-run] [--no-backup] [--source <repo-url>] [--bundle <path|url>] [--checksum <path|url>]";
362
- const UPGRADE_USAGE_EXAMPLE = "agentplane upgrade --tag v0.1.2 --dry-run";
440
+ const UPGRADE_USAGE_EXAMPLE = "agentplane upgrade --tag v0.1.3 --dry-run";
363
441
  const INIT_USAGE = "Usage: agentplane init --ide <...> --workflow <...> --hooks <...> --require-plan-approval <...> --require-network-approval <...> [--recipes <...>] [--yes] [--force|--backup]";
364
442
  const INIT_USAGE_EXAMPLE = "agentplane init --ide codex --workflow direct --hooks false --require-plan-approval true --require-network-approval true --yes";
365
443
  const CONFIG_SET_USAGE = "Usage: agentplane config set <key> <value>";
@@ -1485,6 +1563,7 @@ async function cmdInit(opts) {
1485
1563
  let recipes = flags.recipes ?? defaults.recipes;
1486
1564
  let requirePlanApproval = flags.requirePlanApproval ?? defaults.requirePlanApproval;
1487
1565
  let requireNetworkApproval = flags.requireNetworkApproval ?? defaults.requireNetworkApproval;
1566
+ const isInteractive = process.stdin.isTTY && !flags.yes;
1488
1567
  if (!process.stdin.isTTY &&
1489
1568
  !flags.yes &&
1490
1569
  (!flags.workflow ||
@@ -1497,7 +1576,7 @@ async function cmdInit(opts) {
1497
1576
  message: usageMessage(INIT_USAGE, INIT_USAGE_EXAMPLE),
1498
1577
  });
1499
1578
  }
1500
- if (process.stdin.isTTY && !flags.yes) {
1579
+ if (isInteractive) {
1501
1580
  ide = flags.ide ?? defaults.ide;
1502
1581
  if (!flags.workflow) {
1503
1582
  const choice = await promptChoice("Select workflow mode", ["direct", "branch_pr"], workflow);
@@ -1534,7 +1613,9 @@ async function cmdInit(opts) {
1534
1613
  validateBundledRecipesSelection(recipes);
1535
1614
  try {
1536
1615
  const initRoot = path.resolve(opts.rootOverride ?? opts.cwd);
1537
- let gitRoot = await findGitRoot(initRoot);
1616
+ const existingGitRoot = await findGitRoot(initRoot);
1617
+ const gitRootExisted = Boolean(existingGitRoot);
1618
+ let gitRoot = existingGitRoot;
1538
1619
  const baseBranchFallback = defaultConfig().base_branch;
1539
1620
  if (!gitRoot) {
1540
1621
  await gitInitRepo(initRoot, baseBranchFallback);
@@ -1544,7 +1625,13 @@ async function cmdInit(opts) {
1544
1625
  cwd: gitRoot,
1545
1626
  rootOverride: gitRoot,
1546
1627
  });
1547
- const initBaseBranch = await resolveInitBaseBranch(resolved.gitRoot, baseBranchFallback);
1628
+ let initBaseBranch = await resolveInitBaseBranch(resolved.gitRoot, baseBranchFallback);
1629
+ if (isInteractive && workflow === "branch_pr" && gitRootExisted) {
1630
+ initBaseBranch = await promptInitBaseBranch({
1631
+ gitRoot: resolved.gitRoot,
1632
+ fallback: initBaseBranch,
1633
+ });
1634
+ }
1548
1635
  const configPath = path.join(resolved.agentplaneDir, "config.json");
1549
1636
  const backendPath = path.join(resolved.agentplaneDir, "backends", "local", "backend.json");
1550
1637
  const initDirs = [
@@ -1658,6 +1745,7 @@ async function cmdInit(opts) {
1658
1745
  baseBranch: initBaseBranch,
1659
1746
  installPaths,
1660
1747
  version: getVersion(),
1748
+ skipHooks: hooks,
1661
1749
  });
1662
1750
  process.stdout.write(`${path.relative(resolved.gitRoot, resolved.agentplaneDir)}\n`);
1663
1751
  return 0;
@@ -4472,8 +4560,12 @@ async function gitAddPaths(cwd, paths) {
4472
4560
  return;
4473
4561
  await execFileAsync("git", ["add", "--", ...paths], { cwd, env: gitEnv() });
4474
4562
  }
4475
- async function gitCommit(cwd, message) {
4476
- await execFileAsync("git", ["commit", "-m", message], { cwd, env: gitEnv() });
4563
+ async function gitCommit(cwd, message, opts) {
4564
+ const args = ["commit", "-m", message];
4565
+ if (opts?.skipHooks)
4566
+ args.push("--no-verify");
4567
+ const env = opts?.env ? { ...gitEnv(), ...opts.env } : gitEnv();
4568
+ await execFileAsync("git", args, { cwd, env });
4477
4569
  }
4478
4570
  async function resolveInitBaseBranch(gitRoot, fallback) {
4479
4571
  let current = null;
@@ -4495,6 +4587,47 @@ async function resolveInitBaseBranch(gitRoot, fallback) {
4495
4587
  }
4496
4588
  return fallback;
4497
4589
  }
4590
+ async function promptInitBaseBranch(opts) {
4591
+ const branches = await gitListBranches(opts.gitRoot);
4592
+ let current = null;
4593
+ try {
4594
+ current = await gitCurrentBranch(opts.gitRoot);
4595
+ }
4596
+ catch {
4597
+ current = null;
4598
+ }
4599
+ const promptNewBranch = async (hasBranches) => {
4600
+ const raw = await promptInput(`Enter new base branch name (default ${opts.fallback}): `);
4601
+ const candidate = raw.trim() || opts.fallback;
4602
+ if (!candidate) {
4603
+ throw new CliError({
4604
+ exitCode: 2,
4605
+ code: "E_USAGE",
4606
+ message: "Base branch name cannot be empty",
4607
+ });
4608
+ }
4609
+ if (await gitBranchExists(opts.gitRoot, candidate))
4610
+ return candidate;
4611
+ try {
4612
+ await execFileAsync("git", hasBranches ? ["branch", candidate] : ["checkout", "-q", "-b", candidate], { cwd: opts.gitRoot, env: gitEnv() });
4613
+ }
4614
+ catch (err) {
4615
+ const message = err instanceof Error ? err.message : `Failed to create branch ${candidate}`;
4616
+ throw new CliError({ exitCode: 5, code: "E_GIT", message });
4617
+ }
4618
+ return candidate;
4619
+ };
4620
+ if (branches.length === 0) {
4621
+ return await promptNewBranch(false);
4622
+ }
4623
+ const createLabel = "Create new branch";
4624
+ const defaultChoice = current && branches.includes(current) ? current : (branches[0] ?? opts.fallback);
4625
+ const choice = await promptChoice("Select base branch", [...branches, createLabel], defaultChoice);
4626
+ if (choice === createLabel) {
4627
+ return await promptNewBranch(true);
4628
+ }
4629
+ return choice;
4630
+ }
4498
4631
  async function ensureInitCommit(opts) {
4499
4632
  const stagedBefore = await gitStagedPaths(opts.gitRoot);
4500
4633
  if (stagedBefore.length > 0) {
@@ -4515,7 +4648,7 @@ async function ensureInitCommit(opts) {
4515
4648
  if (staged.length === 0)
4516
4649
  return;
4517
4650
  const message = `chore: install agentplane ${opts.version}`;
4518
- await gitCommit(opts.gitRoot, message);
4651
+ await gitCommit(opts.gitRoot, message, { skipHooks: opts.skipHooks });
4519
4652
  }
4520
4653
  function toGitPath(filePath) {
4521
4654
  return filePath.split(path.sep).join("/");
@@ -6935,26 +7068,111 @@ function setMarkdownSection(body, section, text) {
6935
7068
  const out = [...lines.slice(0, start + 1), ...replacement, ...lines.slice(nextHeading)];
6936
7069
  return `${out.join("\n")}\n`;
6937
7070
  }
7071
+ function normalizeDocSectionName(section) {
7072
+ return section.trim().replaceAll(/\s+/g, " ").toLowerCase();
7073
+ }
7074
+ function splitCombinedHeadingLines(doc) {
7075
+ const lines = doc.replaceAll("\r\n", "\n").split("\n");
7076
+ const out = [];
7077
+ let inFence = false;
7078
+ for (const line of lines) {
7079
+ const trimmed = line.trimStart();
7080
+ if (trimmed.startsWith("```")) {
7081
+ inFence = !inFence;
7082
+ out.push(line);
7083
+ continue;
7084
+ }
7085
+ if (!inFence && line.includes("## ")) {
7086
+ const matches = [...line.matchAll(/##\s+/g)];
7087
+ if (matches.length > 1 && matches[0]?.index === 0) {
7088
+ let start = 0;
7089
+ for (let i = 1; i < matches.length; i += 1) {
7090
+ const idx = matches[i]?.index ?? 0;
7091
+ const chunk = line.slice(start, idx).trimEnd();
7092
+ if (chunk)
7093
+ out.push(chunk);
7094
+ start = idx;
7095
+ }
7096
+ const last = line.slice(start).trimEnd();
7097
+ if (last)
7098
+ out.push(last);
7099
+ continue;
7100
+ }
7101
+ }
7102
+ out.push(line);
7103
+ }
7104
+ return out;
7105
+ }
7106
+ function normalizeDocSections(doc, required) {
7107
+ const lines = splitCombinedHeadingLines(doc);
7108
+ const sections = new Map();
7109
+ const order = [];
7110
+ const pendingSeparator = new Set();
7111
+ let currentKey = null;
7112
+ for (const line of lines) {
7113
+ const match = /^##\s+(.*)$/.exec(line.trim());
7114
+ if (match) {
7115
+ const title = match[1]?.trim() ?? "";
7116
+ const key = normalizeDocSectionName(title);
7117
+ if (key) {
7118
+ const existing = sections.get(key);
7119
+ if (existing) {
7120
+ if (existing.lines.some((entry) => entry.trim() !== "")) {
7121
+ pendingSeparator.add(key);
7122
+ }
7123
+ }
7124
+ else {
7125
+ sections.set(key, { title, lines: [] });
7126
+ order.push(key);
7127
+ }
7128
+ currentKey = key;
7129
+ continue;
7130
+ }
7131
+ }
7132
+ if (currentKey) {
7133
+ const entry = sections.get(currentKey);
7134
+ if (!entry)
7135
+ continue;
7136
+ if (pendingSeparator.has(currentKey) && line.trim() !== "") {
7137
+ entry.lines.push("");
7138
+ pendingSeparator.delete(currentKey);
7139
+ }
7140
+ entry.lines.push(line);
7141
+ }
7142
+ }
7143
+ const out = [];
7144
+ const seen = new Set(order);
7145
+ for (const key of order) {
7146
+ const section = sections.get(key);
7147
+ if (!section)
7148
+ continue;
7149
+ out.push(`## ${section.title}`);
7150
+ if (section.lines.length > 0) {
7151
+ out.push(...section.lines);
7152
+ }
7153
+ else {
7154
+ out.push("");
7155
+ }
7156
+ out.push("");
7157
+ }
7158
+ for (const requiredSection of required) {
7159
+ const requiredKey = normalizeDocSectionName(requiredSection);
7160
+ if (seen.has(requiredKey))
7161
+ continue;
7162
+ out.push(`## ${requiredSection}`, "", "");
7163
+ }
7164
+ return `${out.join("\n").trimEnd()}\n`;
7165
+ }
6938
7166
  function ensureDocSections(doc, required) {
6939
7167
  const trimmed = doc.trim();
6940
7168
  if (!trimmed) {
6941
7169
  const blocks = required.map((section) => `## ${section}\n`);
6942
7170
  return `${blocks.join("\n").trimEnd()}\n`;
6943
7171
  }
6944
- let out = doc.endsWith("\n") ? doc : `${doc}\n`;
6945
- for (const section of required) {
6946
- const needle = `## ${section}`;
6947
- if (!out.split("\n").some((line) => line.trim() === needle)) {
6948
- out = `${out.trimEnd()}\n\n${needle}\n`;
6949
- }
6950
- }
6951
- return out.endsWith("\n") ? out : `${out}\n`;
6952
- }
6953
- function normalizeDocSectionName(section) {
6954
- return section.trim().replaceAll(/\s+/g, " ").toLowerCase();
7172
+ return normalizeDocSections(doc, required);
6955
7173
  }
6956
7174
  function parseDocSections(doc) {
6957
- const lines = doc.replaceAll("\r\n", "\n").split("\n");
7175
+ const lines = splitCombinedHeadingLines(doc);
6958
7176
  const sections = new Map();
6959
7177
  const order = [];
6960
7178
  let currentKey = null;
@@ -7098,9 +7316,17 @@ async function cmdTaskDocSet(opts) {
7098
7316
  message: usageMessage(TASK_DOC_SET_USAGE, TASK_DOC_SET_USAGE_EXAMPLE),
7099
7317
  });
7100
7318
  }
7101
- const updatedBy = (flags.updatedBy ?? "agentplane").trim();
7102
- if (updatedBy.length === 0) {
7103
- throw new CliError({ exitCode: 2, code: "E_USAGE", message: "--updated-by must be non-empty" });
7319
+ let updatedBy;
7320
+ if (flags.updatedBy !== undefined) {
7321
+ const trimmed = flags.updatedBy.trim();
7322
+ if (trimmed.length === 0) {
7323
+ throw new CliError({
7324
+ exitCode: 2,
7325
+ code: "E_USAGE",
7326
+ message: "--updated-by must be non-empty",
7327
+ });
7328
+ }
7329
+ updatedBy = trimmed;
7104
7330
  }
7105
7331
  let text = flags.text ?? "";
7106
7332
  if (hasFile) {
@@ -7164,7 +7390,8 @@ async function cmdTaskDocSet(opts) {
7164
7390
  }
7165
7391
  }
7166
7392
  const nextDoc = setMarkdownSection(baseDoc, flags.section, nextText);
7167
- await backend.setTaskDoc(opts.taskId, nextDoc, updatedBy);
7393
+ const normalized = ensureDocSections(nextDoc, config.tasks.doc.required_sections);
7394
+ await backend.setTaskDoc(opts.taskId, normalized, updatedBy);
7168
7395
  }
7169
7396
  const tasksDir = path.join(resolved.gitRoot, config.paths.workflow_dir);
7170
7397
  process.stdout.write(`${path.join(tasksDir, opts.taskId, "README.md")}\n`);
@@ -7234,6 +7461,11 @@ export async function runCli(argv) {
7234
7461
  return 0;
7235
7462
  }
7236
7463
  await maybeLoadDotEnv({ cwd: process.cwd(), rootOverride: globals.root });
7464
+ await maybeWarnOnUpdate({
7465
+ currentVersion: getVersion(),
7466
+ skip: globals.noUpdateCheck,
7467
+ jsonErrors: globals.jsonErrors,
7468
+ });
7237
7469
  const [namespace, command, ...args] = rest;
7238
7470
  if (namespace === "init") {
7239
7471
  const initArgs = command ? [command, ...args] : [];
@@ -1 +1 @@
1
- {"version":3,"file":"run-cli.test-helpers.d.ts","sourceRoot":"","sources":["../src/run-cli.test-helpers.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAiCpD,wBAAgB,sBAAsB,IAAI,IAAI,CAgB7C;AAED,wBAAgB,iBAAiB,IAAI,MAAM,GAAG,IAAI,CAEjD;AAED,wBAAgB,YAAY;;;;EAgC3B;AAED,wBAAsB,aAAa,IAAI,OAAO,CAAC,MAAM,CAAC,CAKrD;AAED,wBAAsB,SAAS,IAAI,OAAO,CAAC,MAAM,CAAC,CAEjD;AAED,wBAAsB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAKpE;AAED,wBAAsB,WAAW,CAC/B,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,UAAU,CAAC,OAAO,aAAa,CAAC,GACvC,OAAO,CAAC,IAAI,CAAC,CAKf;AAED,wBAAsB,0BAA0B,IAAI,OAAO,CAAC,IAAI,CAAC,CAKhE;AAED,wBAAsB,mBAAmB,CAAC,IAAI,CAAC,EAAE;IAC/C,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,MAAM,CAAC,EAAE,KAAK,GAAG,KAAK,CAAC;IACvB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB,GAAG,OAAO,CAAC;IAAE,WAAW,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,CAAC,CA+FtE;AAED,wBAAsB,+BAA+B,CAAC,IAAI,EAAE;IAC1D,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/B,MAAM,CAAC,EAAE,KAAK,GAAG,KAAK,CAAC;IACvB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB,GAAG,OAAO,CAAC,MAAM,CAAC,CAyBlB;AAED,wBAAsB,mBAAmB,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC;IAChF,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;CACtB,CAAC,CAiBD;AAED,wBAAsB,uBAAuB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAI7E;AAED,wBAAsB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAGlE;AAED,wBAAgB,WAAW,IAAI,MAAM,CAAC,UAAU,CAS/C;AAED,wBAAsB,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAOnE;AAED,wBAAsB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAYpF;AAED,wBAAsB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAG5E;AAED,wBAAsB,uBAAuB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAIzE"}
1
+ {"version":3,"file":"run-cli.test-helpers.d.ts","sourceRoot":"","sources":["../src/run-cli.test-helpers.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAkCpD,wBAAgB,sBAAsB,IAAI,IAAI,CAsB7C;AAED,wBAAgB,iBAAiB,IAAI,MAAM,GAAG,IAAI,CAEjD;AAED,wBAAgB,YAAY;;;;EAgC3B;AAED,wBAAsB,aAAa,IAAI,OAAO,CAAC,MAAM,CAAC,CAKrD;AAED,wBAAsB,SAAS,IAAI,OAAO,CAAC,MAAM,CAAC,CAEjD;AAED,wBAAsB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAKpE;AAED,wBAAsB,WAAW,CAC/B,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,UAAU,CAAC,OAAO,aAAa,CAAC,GACvC,OAAO,CAAC,IAAI,CAAC,CAKf;AAED,wBAAsB,0BAA0B,IAAI,OAAO,CAAC,IAAI,CAAC,CAKhE;AAED,wBAAsB,mBAAmB,CAAC,IAAI,CAAC,EAAE;IAC/C,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,MAAM,CAAC,EAAE,KAAK,GAAG,KAAK,CAAC;IACvB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB,GAAG,OAAO,CAAC;IAAE,WAAW,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,CAAC,CA+FtE;AAED,wBAAsB,+BAA+B,CAAC,IAAI,EAAE;IAC1D,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/B,MAAM,CAAC,EAAE,KAAK,GAAG,KAAK,CAAC;IACvB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB,GAAG,OAAO,CAAC,MAAM,CAAC,CAyBlB;AAED,wBAAsB,mBAAmB,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC;IAChF,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;CACtB,CAAC,CAiBD;AAED,wBAAsB,uBAAuB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAI7E;AAED,wBAAsB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAGlE;AAED,wBAAgB,WAAW,IAAI,MAAM,CAAC,UAAU,CAS/C;AAED,wBAAsB,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAOnE;AAED,wBAAsB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAYpF;AAED,wBAAsB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAG5E;AAED,wBAAsB,uBAAuB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAIzE"}
@@ -9,6 +9,7 @@ import { defaultConfig } from "@agentplaneorg/core";
9
9
  const execFileAsync = promisify(execFile);
10
10
  let agentplaneHome = null;
11
11
  const originalAgentplaneHome = process.env.AGENTPLANE_HOME;
12
+ const originalNoUpdateCheck = process.env.AGENTPLANE_NO_UPDATE_CHECK;
12
13
  const recipeArchiveCache = new Map();
13
14
  let gitTemplateRoot = null;
14
15
  let gitTemplatePromise = null;
@@ -31,6 +32,7 @@ export function registerAgentplaneHome() {
31
32
  beforeAll(async () => {
32
33
  agentplaneHome = await mkdtemp(path.join(os.tmpdir(), "agentplane-home-"));
33
34
  process.env.AGENTPLANE_HOME = agentplaneHome;
35
+ process.env.AGENTPLANE_NO_UPDATE_CHECK = "1";
34
36
  });
35
37
  afterAll(async () => {
36
38
  if (agentplaneHome) {
@@ -42,6 +44,12 @@ export function registerAgentplaneHome() {
42
44
  else {
43
45
  process.env.AGENTPLANE_HOME = originalAgentplaneHome;
44
46
  }
47
+ if (originalNoUpdateCheck === undefined) {
48
+ delete process.env.AGENTPLANE_NO_UPDATE_CHECK;
49
+ }
50
+ else {
51
+ process.env.AGENTPLANE_NO_UPDATE_CHECK = originalNoUpdateCheck;
52
+ }
45
53
  });
46
54
  }
47
55
  export function getAgentplaneHome() {
@@ -1 +1 @@
1
- {"version":3,"file":"task-backend.d.ts","sourceRoot":"","sources":["../src/task-backend.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"task-backend.d.ts","sourceRoot":"","sources":["../src/task-backend.ts"],"names":[],"mappings":"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"}
@@ -6,6 +6,7 @@ import { loadDotEnv } from "./env.js";
6
6
  const ID_ALPHABET = "0123456789ABCDEFGHJKMNPQRSTVWXYZ";
7
7
  const TASK_ID_RE = new RegExp(String.raw `^\d{12}-[${ID_ALPHABET}]{4,}$`);
8
8
  const DOC_SECTION_HEADER = "## Summary";
9
+ const DOC_SECTION_HEADER_RE = /^##\s+Summary(?:\s|$|#)/;
9
10
  const AUTO_SUMMARY_HEADER = "## Changes Summary (auto)";
10
11
  const DEFAULT_DOC_UPDATED_BY = "agentplane";
11
12
  const DOC_VERSION = 2;
@@ -49,19 +50,70 @@ function ensureDocMetadata(task, updatedBy) {
49
50
  task.doc_updated_at = nowIso();
50
51
  task.doc_updated_by = updatedBy ?? DEFAULT_DOC_UPDATED_BY;
51
52
  }
53
+ function lastCommentAuthor(comments) {
54
+ if (!Array.isArray(comments))
55
+ return null;
56
+ const entries = comments;
57
+ for (let i = entries.length - 1; i >= 0; i -= 1) {
58
+ const entry = entries[i];
59
+ if (!isRecord(entry))
60
+ continue;
61
+ const author = entry.author;
62
+ if (typeof author === "string") {
63
+ const trimmed = author.trim();
64
+ if (trimmed)
65
+ return trimmed;
66
+ }
67
+ }
68
+ return null;
69
+ }
70
+ function resolveDocUpdatedByFromFrontmatter(frontmatter, updatedBy, fallback) {
71
+ if (updatedBy !== undefined) {
72
+ const trimmed = updatedBy.trim();
73
+ return trimmed.length > 0 ? trimmed : fallback;
74
+ }
75
+ const author = lastCommentAuthor(frontmatter.comments);
76
+ if (author)
77
+ return author;
78
+ const existing = frontmatter.doc_updated_by;
79
+ if (typeof existing === "string") {
80
+ const trimmed = existing.trim();
81
+ if (trimmed)
82
+ return trimmed;
83
+ }
84
+ return fallback;
85
+ }
86
+ function resolveDocUpdatedByFromTask(task, fallback) {
87
+ const author = lastCommentAuthor(task.comments);
88
+ if (author)
89
+ return author;
90
+ const existing = task.doc_updated_by;
91
+ if (typeof existing === "string") {
92
+ const trimmed = existing.trim();
93
+ if (trimmed)
94
+ return trimmed;
95
+ }
96
+ return fallback;
97
+ }
98
+ function isDocSectionHeader(line) {
99
+ return DOC_SECTION_HEADER_RE.test(line.trim());
100
+ }
52
101
  export function extractTaskDoc(body) {
53
102
  if (!body)
54
103
  return "";
55
104
  const lines = body.split("\n");
56
105
  let startIdx = null;
57
106
  for (const [idx, line] of lines.entries()) {
58
- if (line.trim() === DOC_SECTION_HEADER) {
107
+ if (isDocSectionHeader(line)) {
59
108
  startIdx = idx;
60
109
  break;
61
110
  }
62
111
  }
63
112
  if (startIdx === null)
64
113
  return "";
114
+ if (lines[startIdx]?.trim() !== DOC_SECTION_HEADER) {
115
+ lines[startIdx] = DOC_SECTION_HEADER;
116
+ }
65
117
  let endIdx = lines.length;
66
118
  for (let idx = startIdx + 1; idx < lines.length; idx++) {
67
119
  if (lines[idx]?.trim() === AUTO_SUMMARY_HEADER) {
@@ -77,7 +129,7 @@ export function mergeTaskDoc(body, doc) {
77
129
  const lines = body ? body.split("\n") : [];
78
130
  let prefixIdx = null;
79
131
  for (const [idx, line] of lines.entries()) {
80
- if (line.trim() === DOC_SECTION_HEADER) {
132
+ if (isDocSectionHeader(line)) {
81
133
  prefixIdx = idx;
82
134
  break;
83
135
  }
@@ -383,7 +435,7 @@ export class LocalBackend {
383
435
  if (docChanged(existingDoc, docText)) {
384
436
  payload.doc_version = DOC_VERSION;
385
437
  payload.doc_updated_at = nowIso();
386
- payload.doc_updated_by = this.updatedBy;
438
+ payload.doc_updated_by = resolveDocUpdatedByFromTask(task, this.updatedBy);
387
439
  }
388
440
  }
389
441
  if (payload.doc_version !== DOC_VERSION) {
@@ -409,7 +461,7 @@ export class LocalBackend {
409
461
  if (docChanged(extractTaskDoc(parsed.body), docText) || !frontmatter.doc_updated_at) {
410
462
  frontmatter.doc_version = DOC_VERSION;
411
463
  frontmatter.doc_updated_at = nowIso();
412
- frontmatter.doc_updated_by = updatedBy ?? this.updatedBy;
464
+ frontmatter.doc_updated_by = resolveDocUpdatedByFromFrontmatter(frontmatter, updatedBy, this.updatedBy);
413
465
  }
414
466
  if (frontmatter.doc_version !== DOC_VERSION) {
415
467
  frontmatter.doc_version = DOC_VERSION;
@@ -424,7 +476,7 @@ export class LocalBackend {
424
476
  const frontmatter = { ...parsed.frontmatter };
425
477
  frontmatter.doc_version = DOC_VERSION;
426
478
  frontmatter.doc_updated_at = nowIso();
427
- frontmatter.doc_updated_by = updatedBy ?? this.updatedBy;
479
+ frontmatter.doc_updated_by = resolveDocUpdatedByFromFrontmatter(frontmatter, updatedBy, this.updatedBy);
428
480
  const next = renderTaskReadme(frontmatter, parsed.body || "");
429
481
  await writeFile(readme, next.endsWith("\n") ? next : `${next}\n`, "utf8");
430
482
  }
package/dist/version.js CHANGED
@@ -1,3 +1,3 @@
1
1
  export function getVersion() {
2
- return "0.1.2";
2
+ return "0.1.3";
3
3
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentplane",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "description": "Agent Plane CLI for task workflows, recipes, and project automation.",
5
5
  "keywords": [
6
6
  "agentplane",
@@ -54,6 +54,6 @@
54
54
  "prepublishOnly": "npm run prepack"
55
55
  },
56
56
  "dependencies": {
57
- "@agentplaneorg/core": "0.1.2"
57
+ "@agentplaneorg/core": "0.1.3"
58
58
  }
59
59
  }