agentplane 0.1.2 → 0.1.4

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,70 @@
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 a policy-driven framework for running LLM agents inside real repositories. It turns "AI magic" into an engineering process: explicit approvals, role boundaries, and a reproducible execution pipeline. The goal is simple: make agents boring, safe, and auditable.
9
+
10
+ ## Why Agent Plane
11
+
12
+ - You want agents that behave predictably inside real repos.
13
+ - You need human approvals, clear roles, and traceable artifacts.
14
+ - You want guardrails by default, not optional add-ons.
15
+ - You want an offline-first CLI that keeps state local and inspectable.
16
+
17
+ ## 5-minute start
18
+
19
+ Install and initialize the CLI:
20
+
21
+ ```bash
22
+ npm install -g agentplane
23
+ agentplane init
24
+ agentplane quickstart
25
+ ```
26
+
27
+ Create your first task and run the workflow:
28
+
29
+ ```bash
30
+ agentplane task new --title "First task" --description "Describe the change" --priority med --owner ORCHESTRATOR --tag docs
31
+ agentplane verify <task-id>
32
+ agentplane finish <task-id>
33
+ ```
34
+
35
+ Prefer `npx` instead of a global install?
36
+
37
+ ```bash
38
+ npx agentplane init
39
+ npx agentplane quickstart
40
+ ```
41
+
42
+ ## What gets installed automatically
43
+
44
+ - `.agentplane/` is created with config, tasks, agents, and caches.
45
+ - `AGENTS.md` is created if missing and defines the policy/guardrails.
46
+ - Built-in agent definitions are copied into `.agentplane/agents/`.
47
+ - Optional recipes can install additional agents when you run `agentplane recipes install ...`.
48
+
49
+ ## Guardrails and artifacts
50
+
51
+ - Approval gates for plans and network access (configured in `.agentplane/config.json`).
52
+ - Role boundaries (ORCHESTRATOR, PLANNER, CODER, INTEGRATOR, etc.).
53
+ - Agent definitions in `.agentplane/agents/`.
54
+ - Task records in `.agentplane/tasks/` with a snapshot export in `.agentplane/tasks.json`.
55
+ - A visible, reproducible pipeline:
56
+
57
+ ```text
58
+ Preflight -> Plan -> Approval -> Tasks -> Verify -> Finish -> Export
59
+ ```
60
+
61
+ ## Features
62
+
63
+ - Policy-first execution with explicit approvals and guardrails.
64
+ - Role-based workflows for teams: ORCHESTRATOR, PLANNER, CREATOR, INTEGRATOR, etc.
65
+ - Two workflow modes: `direct` (single checkout) and `branch_pr` (worktrees + integration).
66
+ - Task tracking, verification, and exports baked in.
67
+ - Recipes for repeatable setup and automation.
4
68
 
5
69
  ## Install
6
70
 
@@ -8,12 +72,42 @@ AgentPlane is an offline-first CLI for managing agent workflows (tasks, guardrai
8
72
  npm install -g agentplane
9
73
  ```
10
74
 
75
+ Or run without installing:
76
+
77
+ ```bash
78
+ npx agentplane --help
79
+ ```
80
+
11
81
  ## Requirements
12
82
 
13
83
  - Node.js >= 20
14
84
 
15
- ## CLI
85
+ ## Common Commands
16
86
 
17
87
  ```bash
18
88
  agentplane --help
89
+ agentplane quickstart
90
+ agentplane config show
91
+ agentplane task list
92
+ agentplane task new --title "..." --description "..." --priority med --owner ORCHESTRATOR --tag docs
93
+ agentplane verify <task-id>
94
+ agentplane finish <task-id>
95
+ agentplane recipes list
19
96
  ```
97
+
98
+ ## Docs and Guides
99
+
100
+ - Documentation index: https://github.com/basilisk-labs/agentplane/tree/main/docs
101
+ - Workflow overview: https://github.com/basilisk-labs/agentplane/blob/main/docs/user/workflow.mdx
102
+ - CLI commands: https://github.com/basilisk-labs/agentplane/blob/main/docs/user/commands.mdx
103
+ - Project layout: https://github.com/basilisk-labs/agentplane/blob/main/docs/developer/project-layout.mdx
104
+ - Recipes: https://github.com/basilisk-labs/agentplane/tree/main/agentplane-recipes
105
+
106
+ ## Support
107
+
108
+ - Issues: https://github.com/basilisk-labs/agentplane/issues
109
+ - Contributing: https://github.com/basilisk-labs/agentplane/blob/main/CONTRIBUTING.md
110
+
111
+ ## License
112
+
113
+ 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":"AA69QA,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.4 --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;
@@ -2928,7 +3016,7 @@ async function cmdTaskNew(opts) {
2928
3016
  comments: [],
2929
3017
  doc_version: 2,
2930
3018
  doc_updated_at: nowIso(),
2931
- doc_updated_by: "agentplane",
3019
+ doc_updated_by: flags.owner,
2932
3020
  id_source: "generated",
2933
3021
  };
2934
3022
  if (requiresVerify(flags.tags, config.tasks.verify.required_tags) &&
@@ -3061,6 +3149,7 @@ async function cmdTaskAdd(opts) {
3061
3149
  const tags = dedupeStrings(flags.tags);
3062
3150
  const dependsOn = dedupeStrings(flags.dependsOn);
3063
3151
  const verify = dedupeStrings(flags.verify);
3152
+ const docUpdatedBy = (flags.commentAuthor ?? "").trim() || flags.owner;
3064
3153
  if (requiresVerify(tags, config.tasks.verify.required_tags) && verify.length === 0) {
3065
3154
  throw new CliError({
3066
3155
  exitCode: 2,
@@ -3083,7 +3172,7 @@ async function cmdTaskAdd(opts) {
3083
3172
  : [],
3084
3173
  doc_version: 2,
3085
3174
  doc_updated_at: nowIso(),
3086
- doc_updated_by: "agentplane",
3175
+ doc_updated_by: docUpdatedBy,
3087
3176
  id_source: "explicit",
3088
3177
  }));
3089
3178
  if (backend.writeTasks) {
@@ -3756,10 +3845,15 @@ async function cmdTaskScaffold(opts) {
3756
3845
  comments: [],
3757
3846
  doc_version: 2,
3758
3847
  doc_updated_at: nowIso(),
3759
- doc_updated_by: "agentplane",
3848
+ doc_updated_by: "UNKNOWN",
3760
3849
  };
3761
3850
  if (flags.title)
3762
3851
  baseTask.title = flags.title;
3852
+ if (typeof baseTask.doc_updated_by !== "string" ||
3853
+ baseTask.doc_updated_by.trim().length === 0 ||
3854
+ baseTask.doc_updated_by.trim().toLowerCase() === "agentplane") {
3855
+ baseTask.doc_updated_by = baseTask.owner?.trim() ? baseTask.owner : "UNKNOWN";
3856
+ }
3763
3857
  const frontmatter = taskDataToFrontmatter(baseTask);
3764
3858
  const body = ensureDocSections("", config.tasks.doc.required_sections);
3765
3859
  const text = renderTaskReadme(frontmatter, body);
@@ -3895,7 +3989,7 @@ async function cmdTaskComment(opts) {
3895
3989
  comments: [...existing, { author: opts.author, body: opts.body }],
3896
3990
  doc_version: 2,
3897
3991
  doc_updated_at: nowIso(),
3898
- doc_updated_by: "agentplane",
3992
+ doc_updated_by: opts.author,
3899
3993
  };
3900
3994
  await backend.writeTask(next);
3901
3995
  process.stdout.write(`${successMessage("commented", opts.taskId)}\n`);
@@ -3998,7 +4092,7 @@ async function cmdTaskSetStatus(opts) {
3998
4092
  comments,
3999
4093
  doc_version: 2,
4000
4094
  doc_updated_at: nowIso(),
4001
- doc_updated_by: "agentplane",
4095
+ doc_updated_by: resolveDocUpdatedBy(task, opts.author),
4002
4096
  };
4003
4097
  if (opts.commit) {
4004
4098
  const commitInfo = await readCommitInfo(resolved.gitRoot, opts.commit);
@@ -4472,8 +4566,12 @@ async function gitAddPaths(cwd, paths) {
4472
4566
  return;
4473
4567
  await execFileAsync("git", ["add", "--", ...paths], { cwd, env: gitEnv() });
4474
4568
  }
4475
- async function gitCommit(cwd, message) {
4476
- await execFileAsync("git", ["commit", "-m", message], { cwd, env: gitEnv() });
4569
+ async function gitCommit(cwd, message, opts) {
4570
+ const args = ["commit", "-m", message];
4571
+ if (opts?.skipHooks)
4572
+ args.push("--no-verify");
4573
+ const env = opts?.env ? { ...gitEnv(), ...opts.env } : gitEnv();
4574
+ await execFileAsync("git", args, { cwd, env });
4477
4575
  }
4478
4576
  async function resolveInitBaseBranch(gitRoot, fallback) {
4479
4577
  let current = null;
@@ -4495,6 +4593,47 @@ async function resolveInitBaseBranch(gitRoot, fallback) {
4495
4593
  }
4496
4594
  return fallback;
4497
4595
  }
4596
+ async function promptInitBaseBranch(opts) {
4597
+ const branches = await gitListBranches(opts.gitRoot);
4598
+ let current = null;
4599
+ try {
4600
+ current = await gitCurrentBranch(opts.gitRoot);
4601
+ }
4602
+ catch {
4603
+ current = null;
4604
+ }
4605
+ const promptNewBranch = async (hasBranches) => {
4606
+ const raw = await promptInput(`Enter new base branch name (default ${opts.fallback}): `);
4607
+ const candidate = raw.trim() || opts.fallback;
4608
+ if (!candidate) {
4609
+ throw new CliError({
4610
+ exitCode: 2,
4611
+ code: "E_USAGE",
4612
+ message: "Base branch name cannot be empty",
4613
+ });
4614
+ }
4615
+ if (await gitBranchExists(opts.gitRoot, candidate))
4616
+ return candidate;
4617
+ try {
4618
+ await execFileAsync("git", hasBranches ? ["branch", candidate] : ["checkout", "-q", "-b", candidate], { cwd: opts.gitRoot, env: gitEnv() });
4619
+ }
4620
+ catch (err) {
4621
+ const message = err instanceof Error ? err.message : `Failed to create branch ${candidate}`;
4622
+ throw new CliError({ exitCode: 5, code: "E_GIT", message });
4623
+ }
4624
+ return candidate;
4625
+ };
4626
+ if (branches.length === 0) {
4627
+ return await promptNewBranch(false);
4628
+ }
4629
+ const createLabel = "Create new branch";
4630
+ const defaultChoice = current && branches.includes(current) ? current : (branches[0] ?? opts.fallback);
4631
+ const choice = await promptChoice("Select base branch", [...branches, createLabel], defaultChoice);
4632
+ if (choice === createLabel) {
4633
+ return await promptNewBranch(true);
4634
+ }
4635
+ return choice;
4636
+ }
4498
4637
  async function ensureInitCommit(opts) {
4499
4638
  const stagedBefore = await gitStagedPaths(opts.gitRoot);
4500
4639
  if (stagedBefore.length > 0) {
@@ -4515,7 +4654,7 @@ async function ensureInitCommit(opts) {
4515
4654
  if (staged.length === 0)
4516
4655
  return;
4517
4656
  const message = `chore: install agentplane ${opts.version}`;
4518
- await gitCommit(opts.gitRoot, message);
4657
+ await gitCommit(opts.gitRoot, message, { skipHooks: opts.skipHooks });
4519
4658
  }
4520
4659
  function toGitPath(filePath) {
4521
4660
  return filePath.split(path.sep).join("/");
@@ -5232,7 +5371,7 @@ async function cmdStart(opts) {
5232
5371
  comments: commentsValue,
5233
5372
  doc_version: 2,
5234
5373
  doc_updated_at: nowIso(),
5235
- doc_updated_by: "agentplane",
5374
+ doc_updated_by: opts.author,
5236
5375
  };
5237
5376
  await backend.writeTask(nextTask);
5238
5377
  let commitInfo = null;
@@ -5308,7 +5447,7 @@ async function cmdBlock(opts) {
5308
5447
  comments: commentsValue,
5309
5448
  doc_version: 2,
5310
5449
  doc_updated_at: nowIso(),
5311
- doc_updated_by: "agentplane",
5450
+ doc_updated_by: opts.author,
5312
5451
  };
5313
5452
  await backend.writeTask(nextTask);
5314
5453
  let commitInfo = null;
@@ -5409,7 +5548,7 @@ async function cmdFinish(opts) {
5409
5548
  comments: commentsValue,
5410
5549
  doc_version: 2,
5411
5550
  doc_updated_at: nowIso(),
5412
- doc_updated_by: "agentplane",
5551
+ doc_updated_by: opts.author,
5413
5552
  };
5414
5553
  await backend.writeTask(nextTask);
5415
5554
  }
@@ -6935,26 +7074,128 @@ function setMarkdownSection(body, section, text) {
6935
7074
  const out = [...lines.slice(0, start + 1), ...replacement, ...lines.slice(nextHeading)];
6936
7075
  return `${out.join("\n")}\n`;
6937
7076
  }
7077
+ function normalizeDocSectionName(section) {
7078
+ return section.trim().replaceAll(/\s+/g, " ").toLowerCase();
7079
+ }
7080
+ function normalizeDocUpdatedBy(value) {
7081
+ const trimmed = value?.trim() ?? "";
7082
+ if (!trimmed)
7083
+ return "";
7084
+ if (trimmed.toLowerCase() === "agentplane")
7085
+ return "";
7086
+ return trimmed;
7087
+ }
7088
+ function resolveDocUpdatedBy(task, author) {
7089
+ const fromAuthor = normalizeDocUpdatedBy(author);
7090
+ if (fromAuthor)
7091
+ return fromAuthor;
7092
+ const fromTask = normalizeDocUpdatedBy(typeof task.doc_updated_by === "string" ? task.doc_updated_by : undefined);
7093
+ if (fromTask)
7094
+ return fromTask;
7095
+ return normalizeDocUpdatedBy(typeof task.owner === "string" ? task.owner : undefined);
7096
+ }
7097
+ function splitCombinedHeadingLines(doc) {
7098
+ const lines = doc.replaceAll("\r\n", "\n").split("\n");
7099
+ const out = [];
7100
+ let inFence = false;
7101
+ for (const line of lines) {
7102
+ const trimmed = line.trimStart();
7103
+ if (trimmed.startsWith("```")) {
7104
+ inFence = !inFence;
7105
+ out.push(line);
7106
+ continue;
7107
+ }
7108
+ if (!inFence && line.includes("## ")) {
7109
+ const matches = [...line.matchAll(/##\s+/g)];
7110
+ if (matches.length > 1 && matches[0]?.index === 0) {
7111
+ let start = 0;
7112
+ for (let i = 1; i < matches.length; i += 1) {
7113
+ const idx = matches[i]?.index ?? 0;
7114
+ const chunk = line.slice(start, idx).trimEnd();
7115
+ if (chunk)
7116
+ out.push(chunk);
7117
+ start = idx;
7118
+ }
7119
+ const last = line.slice(start).trimEnd();
7120
+ if (last)
7121
+ out.push(last);
7122
+ continue;
7123
+ }
7124
+ }
7125
+ out.push(line);
7126
+ }
7127
+ return out;
7128
+ }
7129
+ function normalizeDocSections(doc, required) {
7130
+ const lines = splitCombinedHeadingLines(doc);
7131
+ const sections = new Map();
7132
+ const order = [];
7133
+ const pendingSeparator = new Set();
7134
+ let currentKey = null;
7135
+ for (const line of lines) {
7136
+ const match = /^##\s+(.*)$/.exec(line.trim());
7137
+ if (match) {
7138
+ const title = match[1]?.trim() ?? "";
7139
+ const key = normalizeDocSectionName(title);
7140
+ if (key) {
7141
+ const existing = sections.get(key);
7142
+ if (existing) {
7143
+ if (existing.lines.some((entry) => entry.trim() !== "")) {
7144
+ pendingSeparator.add(key);
7145
+ }
7146
+ }
7147
+ else {
7148
+ sections.set(key, { title, lines: [] });
7149
+ order.push(key);
7150
+ }
7151
+ currentKey = key;
7152
+ continue;
7153
+ }
7154
+ }
7155
+ if (currentKey) {
7156
+ const entry = sections.get(currentKey);
7157
+ if (!entry)
7158
+ continue;
7159
+ if (pendingSeparator.has(currentKey) && line.trim() !== "") {
7160
+ entry.lines.push("");
7161
+ pendingSeparator.delete(currentKey);
7162
+ }
7163
+ entry.lines.push(line);
7164
+ }
7165
+ }
7166
+ const out = [];
7167
+ const seen = new Set(order);
7168
+ for (const key of order) {
7169
+ const section = sections.get(key);
7170
+ if (!section)
7171
+ continue;
7172
+ out.push(`## ${section.title}`);
7173
+ if (section.lines.length > 0) {
7174
+ out.push(...section.lines);
7175
+ }
7176
+ else {
7177
+ out.push("");
7178
+ }
7179
+ out.push("");
7180
+ }
7181
+ for (const requiredSection of required) {
7182
+ const requiredKey = normalizeDocSectionName(requiredSection);
7183
+ if (seen.has(requiredKey))
7184
+ continue;
7185
+ out.push(`## ${requiredSection}`, "", "");
7186
+ }
7187
+ return `${out.join("\n").trimEnd()}\n`;
7188
+ }
6938
7189
  function ensureDocSections(doc, required) {
6939
7190
  const trimmed = doc.trim();
6940
7191
  if (!trimmed) {
6941
7192
  const blocks = required.map((section) => `## ${section}\n`);
6942
7193
  return `${blocks.join("\n").trimEnd()}\n`;
6943
7194
  }
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();
7195
+ return normalizeDocSections(doc, required);
6955
7196
  }
6956
7197
  function parseDocSections(doc) {
6957
- const lines = doc.replaceAll("\r\n", "\n").split("\n");
7198
+ const lines = splitCombinedHeadingLines(doc);
6958
7199
  const sections = new Map();
6959
7200
  const order = [];
6960
7201
  let currentKey = null;
@@ -7098,9 +7339,17 @@ async function cmdTaskDocSet(opts) {
7098
7339
  message: usageMessage(TASK_DOC_SET_USAGE, TASK_DOC_SET_USAGE_EXAMPLE),
7099
7340
  });
7100
7341
  }
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" });
7342
+ let updatedBy;
7343
+ if (flags.updatedBy !== undefined) {
7344
+ const trimmed = flags.updatedBy.trim();
7345
+ if (trimmed.length === 0) {
7346
+ throw new CliError({
7347
+ exitCode: 2,
7348
+ code: "E_USAGE",
7349
+ message: "--updated-by must be non-empty",
7350
+ });
7351
+ }
7352
+ updatedBy = trimmed;
7104
7353
  }
7105
7354
  let text = flags.text ?? "";
7106
7355
  if (hasFile) {
@@ -7164,7 +7413,8 @@ async function cmdTaskDocSet(opts) {
7164
7413
  }
7165
7414
  }
7166
7415
  const nextDoc = setMarkdownSection(baseDoc, flags.section, nextText);
7167
- await backend.setTaskDoc(opts.taskId, nextDoc, updatedBy);
7416
+ const normalized = ensureDocSections(nextDoc, config.tasks.doc.required_sections);
7417
+ await backend.setTaskDoc(opts.taskId, normalized, updatedBy);
7168
7418
  }
7169
7419
  const tasksDir = path.join(resolved.gitRoot, config.paths.workflow_dir);
7170
7420
  process.stdout.write(`${path.join(tasksDir, opts.taskId, "README.md")}\n`);
@@ -7234,6 +7484,11 @@ export async function runCli(argv) {
7234
7484
  return 0;
7235
7485
  }
7236
7486
  await maybeLoadDotEnv({ cwd: process.cwd(), rootOverride: globals.root });
7487
+ await maybeWarnOnUpdate({
7488
+ currentVersion: getVersion(),
7489
+ skip: globals.noUpdateCheck,
7490
+ jsonErrors: globals.jsonErrors,
7491
+ });
7237
7492
  const [namespace, command, ...args] = rest;
7238
7493
  if (namespace === "init") {
7239
7494
  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;
@@ -41,13 +42,209 @@ function normalizeDoc(text) {
41
42
  .join("\n")
42
43
  .trim();
43
44
  }
45
+ function normalizeDocSectionName(section) {
46
+ return section.trim().replaceAll(/\s+/g, " ").toLowerCase();
47
+ }
48
+ function normalizeSectionLines(lines) {
49
+ const trimmedLines = [...lines];
50
+ while (trimmedLines.length > 0 && trimmedLines[0]?.trim() === "")
51
+ trimmedLines.shift();
52
+ while (trimmedLines.length > 0 && trimmedLines.at(-1)?.trim() === "")
53
+ trimmedLines.pop();
54
+ const out = [];
55
+ let inFence = false;
56
+ let pendingBlank = false;
57
+ for (const line of trimmedLines) {
58
+ const fenceCheck = line.trimStart();
59
+ if (fenceCheck.startsWith("```")) {
60
+ if (pendingBlank) {
61
+ out.push("");
62
+ pendingBlank = false;
63
+ }
64
+ out.push(line);
65
+ inFence = !inFence;
66
+ continue;
67
+ }
68
+ if (inFence) {
69
+ out.push(line);
70
+ continue;
71
+ }
72
+ if (line.trim() === "") {
73
+ pendingBlank = true;
74
+ continue;
75
+ }
76
+ if (pendingBlank) {
77
+ out.push("");
78
+ pendingBlank = false;
79
+ }
80
+ out.push(line);
81
+ }
82
+ return out;
83
+ }
84
+ function splitCombinedHeadingLines(doc) {
85
+ const lines = doc.replaceAll("\r\n", "\n").split("\n");
86
+ const out = [];
87
+ let inFence = false;
88
+ for (const line of lines) {
89
+ const trimmed = line.trimStart();
90
+ if (trimmed.startsWith("```")) {
91
+ inFence = !inFence;
92
+ out.push(line);
93
+ continue;
94
+ }
95
+ if (!inFence && line.includes("## ")) {
96
+ const matches = [...line.matchAll(/##\s+/g)];
97
+ if (matches.length > 1 && matches[0]?.index === 0) {
98
+ let start = 0;
99
+ for (let i = 1; i < matches.length; i += 1) {
100
+ const idx = matches[i]?.index ?? 0;
101
+ const chunk = line.slice(start, idx).trimEnd();
102
+ if (chunk)
103
+ out.push(chunk);
104
+ start = idx;
105
+ }
106
+ const last = line.slice(start).trimEnd();
107
+ if (last)
108
+ out.push(last);
109
+ continue;
110
+ }
111
+ }
112
+ out.push(line);
113
+ }
114
+ return out;
115
+ }
116
+ function normalizeTaskDoc(doc) {
117
+ const normalized = doc.replaceAll("\r\n", "\n");
118
+ const trimmed = normalized.replaceAll(/^\n+|\n+$/g, "");
119
+ if (!trimmed)
120
+ return "";
121
+ const lines = splitCombinedHeadingLines(trimmed);
122
+ const sections = new Map();
123
+ const order = [];
124
+ const pendingSeparator = new Set();
125
+ let currentKey = null;
126
+ for (const line of lines) {
127
+ const match = /^##\s+(.*)$/.exec(line.trim());
128
+ if (match) {
129
+ const title = match[1]?.trim() ?? "";
130
+ const key = normalizeDocSectionName(title);
131
+ if (key) {
132
+ const existing = sections.get(key);
133
+ if (existing) {
134
+ if (existing.lines.some((entry) => entry.trim() !== "")) {
135
+ pendingSeparator.add(key);
136
+ }
137
+ }
138
+ else {
139
+ sections.set(key, { title, lines: [] });
140
+ order.push(key);
141
+ }
142
+ currentKey = key;
143
+ continue;
144
+ }
145
+ }
146
+ if (currentKey) {
147
+ const entry = sections.get(currentKey);
148
+ if (!entry)
149
+ continue;
150
+ if (pendingSeparator.has(currentKey) && line.trim() !== "") {
151
+ entry.lines.push("");
152
+ pendingSeparator.delete(currentKey);
153
+ }
154
+ entry.lines.push(line);
155
+ }
156
+ }
157
+ if (order.length === 0)
158
+ return trimmed;
159
+ const out = [];
160
+ for (const key of order) {
161
+ const section = sections.get(key);
162
+ if (!section)
163
+ continue;
164
+ const normalizedLines = normalizeSectionLines(section.lines);
165
+ if (normalizedLines.length > 0) {
166
+ out.push(`## ${section.title}`, "", ...normalizedLines, "");
167
+ }
168
+ else {
169
+ out.push(`## ${section.title}`, "", "");
170
+ }
171
+ }
172
+ return out.join("\n").trimEnd();
173
+ }
44
174
  function docChanged(existing, updated) {
45
175
  return normalizeDoc(existing) !== normalizeDoc(updated);
46
176
  }
177
+ function normalizeUpdatedBy(value) {
178
+ if (typeof value !== "string")
179
+ return "";
180
+ const trimmed = value.trim();
181
+ if (!trimmed)
182
+ return "";
183
+ if (trimmed.toLowerCase() === DEFAULT_DOC_UPDATED_BY.toLowerCase())
184
+ return "";
185
+ return trimmed;
186
+ }
47
187
  function ensureDocMetadata(task, updatedBy) {
48
188
  task.doc_version = DOC_VERSION;
49
189
  task.doc_updated_at = nowIso();
50
- task.doc_updated_by = updatedBy ?? DEFAULT_DOC_UPDATED_BY;
190
+ const explicit = normalizeUpdatedBy(updatedBy);
191
+ if (updatedBy !== undefined) {
192
+ task.doc_updated_by =
193
+ explicit || resolveDocUpdatedByFromTask(task, DEFAULT_DOC_UPDATED_BY);
194
+ return;
195
+ }
196
+ task.doc_updated_by = resolveDocUpdatedByFromTask(task, DEFAULT_DOC_UPDATED_BY);
197
+ }
198
+ function lastCommentAuthor(comments) {
199
+ if (!Array.isArray(comments))
200
+ return null;
201
+ const entries = comments;
202
+ for (let i = entries.length - 1; i >= 0; i -= 1) {
203
+ const entry = entries[i];
204
+ if (!isRecord(entry))
205
+ continue;
206
+ const author = entry.author;
207
+ if (typeof author === "string") {
208
+ const trimmed = author.trim();
209
+ if (trimmed)
210
+ return trimmed;
211
+ }
212
+ }
213
+ return null;
214
+ }
215
+ function resolveDocUpdatedByFromFrontmatter(frontmatter, updatedBy, fallback) {
216
+ if (updatedBy !== undefined) {
217
+ const explicit = normalizeUpdatedBy(updatedBy);
218
+ if (explicit)
219
+ return explicit;
220
+ }
221
+ const author = lastCommentAuthor(frontmatter.comments);
222
+ if (author)
223
+ return author;
224
+ const existing = normalizeUpdatedBy(frontmatter.doc_updated_by);
225
+ if (existing)
226
+ return existing;
227
+ const owner = normalizeUpdatedBy(frontmatter.owner);
228
+ if (owner)
229
+ return owner;
230
+ const fallbackValue = normalizeUpdatedBy(fallback);
231
+ return fallbackValue || fallback;
232
+ }
233
+ function resolveDocUpdatedByFromTask(task, fallback) {
234
+ const author = lastCommentAuthor(task.comments);
235
+ if (author)
236
+ return author;
237
+ const existing = normalizeUpdatedBy(task.doc_updated_by);
238
+ if (existing)
239
+ return existing;
240
+ const owner = normalizeUpdatedBy(task.owner);
241
+ if (owner)
242
+ return owner;
243
+ const fallbackValue = normalizeUpdatedBy(fallback);
244
+ return fallbackValue || fallback;
245
+ }
246
+ function isDocSectionHeader(line) {
247
+ return DOC_SECTION_HEADER_RE.test(line.trim());
51
248
  }
52
249
  export function extractTaskDoc(body) {
53
250
  if (!body)
@@ -55,13 +252,16 @@ export function extractTaskDoc(body) {
55
252
  const lines = body.split("\n");
56
253
  let startIdx = null;
57
254
  for (const [idx, line] of lines.entries()) {
58
- if (line.trim() === DOC_SECTION_HEADER) {
255
+ if (isDocSectionHeader(line)) {
59
256
  startIdx = idx;
60
257
  break;
61
258
  }
62
259
  }
63
260
  if (startIdx === null)
64
261
  return "";
262
+ if (lines[startIdx]?.trim() !== DOC_SECTION_HEADER) {
263
+ lines[startIdx] = DOC_SECTION_HEADER;
264
+ }
65
265
  let endIdx = lines.length;
66
266
  for (let idx = startIdx + 1; idx < lines.length; idx++) {
67
267
  if (lines[idx]?.trim() === AUTO_SUMMARY_HEADER) {
@@ -69,15 +269,16 @@ export function extractTaskDoc(body) {
69
269
  break;
70
270
  }
71
271
  }
72
- return lines.slice(startIdx, endIdx).join("\n").trimEnd();
272
+ const doc = lines.slice(startIdx, endIdx).join("\n").trimEnd();
273
+ return normalizeTaskDoc(doc);
73
274
  }
74
275
  export function mergeTaskDoc(body, doc) {
75
- const docText = String(doc ?? "").replaceAll(/^\n+|\n+$/g, "");
276
+ const docText = normalizeTaskDoc(String(doc ?? ""));
76
277
  if (docText) {
77
278
  const lines = body ? body.split("\n") : [];
78
279
  let prefixIdx = null;
79
280
  for (const [idx, line] of lines.entries()) {
80
- if (line.trim() === DOC_SECTION_HEADER) {
281
+ if (isDocSectionHeader(line)) {
81
282
  prefixIdx = idx;
82
283
  break;
83
284
  }
@@ -211,7 +412,7 @@ function taskDataToExport(task) {
211
412
  : [],
212
413
  doc_version: task.doc_version ?? DOC_VERSION,
213
414
  doc_updated_at: task.doc_updated_at ?? "",
214
- doc_updated_by: task.doc_updated_by ?? DEFAULT_DOC_UPDATED_BY,
415
+ doc_updated_by: resolveDocUpdatedByFromTask(task, DEFAULT_DOC_UPDATED_BY),
215
416
  dirty: Boolean(task.dirty),
216
417
  id_source: task.id_source ?? "generated",
217
418
  };
@@ -383,7 +584,7 @@ export class LocalBackend {
383
584
  if (docChanged(existingDoc, docText)) {
384
585
  payload.doc_version = DOC_VERSION;
385
586
  payload.doc_updated_at = nowIso();
386
- payload.doc_updated_by = this.updatedBy;
587
+ payload.doc_updated_by = resolveDocUpdatedByFromTask(task, this.updatedBy);
387
588
  }
388
589
  }
389
590
  if (payload.doc_version !== DOC_VERSION) {
@@ -393,7 +594,7 @@ export class LocalBackend {
393
594
  payload.doc_updated_at = nowIso();
394
595
  }
395
596
  if (payload.doc_updated_by === undefined || payload.doc_updated_by === "") {
396
- payload.doc_updated_by = this.updatedBy;
597
+ payload.doc_updated_by = resolveDocUpdatedByFromTask(task, this.updatedBy);
397
598
  }
398
599
  await mkdir(path.dirname(readme), { recursive: true });
399
600
  const text = renderTaskReadme(payload, body || "");
@@ -409,7 +610,7 @@ export class LocalBackend {
409
610
  if (docChanged(extractTaskDoc(parsed.body), docText) || !frontmatter.doc_updated_at) {
410
611
  frontmatter.doc_version = DOC_VERSION;
411
612
  frontmatter.doc_updated_at = nowIso();
412
- frontmatter.doc_updated_by = updatedBy ?? this.updatedBy;
613
+ frontmatter.doc_updated_by = resolveDocUpdatedByFromFrontmatter(frontmatter, updatedBy, this.updatedBy);
413
614
  }
414
615
  if (frontmatter.doc_version !== DOC_VERSION) {
415
616
  frontmatter.doc_version = DOC_VERSION;
@@ -424,7 +625,7 @@ export class LocalBackend {
424
625
  const frontmatter = { ...parsed.frontmatter };
425
626
  frontmatter.doc_version = DOC_VERSION;
426
627
  frontmatter.doc_updated_at = nowIso();
427
- frontmatter.doc_updated_by = updatedBy ?? this.updatedBy;
628
+ frontmatter.doc_updated_by = resolveDocUpdatedByFromFrontmatter(frontmatter, updatedBy, this.updatedBy);
428
629
  const next = renderTaskReadme(frontmatter, parsed.body || "");
429
630
  await writeFile(readme, next.endsWith("\n") ? next : `${next}\n`, "utf8");
430
631
  }
package/dist/version.js CHANGED
@@ -1,3 +1,3 @@
1
1
  export function getVersion() {
2
- return "0.1.2";
2
+ return "0.1.4";
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.4",
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.4"
58
58
  }
59
59
  }