agentplane 0.3.22 → 0.3.24

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 (62) hide show
  1. package/assets/policy/incidents.md +1 -0
  2. package/dist/.build-manifest.json +83 -28
  3. package/dist/cli/run-cli/commands/init/orchestrate-v2.d.ts.map +1 -1
  4. package/dist/cli/run-cli/commands/init/orchestrate-v2.js +10 -2
  5. package/dist/cli/run-cli/commands/init/orchestrate.d.ts.map +1 -1
  6. package/dist/cli/run-cli/commands/init/orchestrate.js +10 -2
  7. package/dist/cli/run-cli/commands/init/recipes.d.ts.map +1 -1
  8. package/dist/cli/run-cli/commands/init/recipes.js +3 -1
  9. package/dist/cli.js +337 -337
  10. package/dist/commands/guard/impl/commit-refresh.d.ts +25 -0
  11. package/dist/commands/guard/impl/commit-refresh.d.ts.map +1 -0
  12. package/dist/commands/guard/impl/commit-refresh.js +116 -0
  13. package/dist/commands/guard/impl/commit-stage.d.ts +21 -0
  14. package/dist/commands/guard/impl/commit-stage.d.ts.map +1 -0
  15. package/dist/commands/guard/impl/commit-stage.js +43 -0
  16. package/dist/commands/guard/impl/commit.d.ts.map +1 -1
  17. package/dist/commands/guard/impl/commit.js +10 -150
  18. package/dist/commands/hooks/install.d.ts +4 -0
  19. package/dist/commands/hooks/install.d.ts.map +1 -1
  20. package/dist/commands/hooks/install.js +19 -0
  21. package/dist/commands/hooks/run.pre-push.d.ts.map +1 -1
  22. package/dist/commands/hooks/run.pre-push.js +5 -2
  23. package/dist/commands/recipes/impl/installed-recipes.d.ts +5 -1
  24. package/dist/commands/recipes/impl/installed-recipes.d.ts.map +1 -1
  25. package/dist/commands/recipes/impl/installed-recipes.js +38 -19
  26. package/dist/commands/release/apply.mutation.d.ts.map +1 -1
  27. package/dist/commands/release/apply.mutation.js +5 -4
  28. package/dist/commands/release/apply.pipeline/mutation.d.ts.map +1 -1
  29. package/dist/commands/release/apply.pipeline/mutation.js +6 -1
  30. package/dist/commands/release/apply.preflight.d.ts +4 -29
  31. package/dist/commands/release/apply.preflight.d.ts.map +1 -1
  32. package/dist/commands/release/apply.preflight.git.d.ts +7 -0
  33. package/dist/commands/release/apply.preflight.git.d.ts.map +1 -0
  34. package/dist/commands/release/apply.preflight.git.js +138 -0
  35. package/dist/commands/release/apply.preflight.js +4 -368
  36. package/dist/commands/release/apply.preflight.package.d.ts +7 -0
  37. package/dist/commands/release/apply.preflight.package.d.ts.map +1 -0
  38. package/dist/commands/release/apply.preflight.package.js +69 -0
  39. package/dist/commands/release/apply.preflight.plan.d.ts +16 -0
  40. package/dist/commands/release/apply.preflight.plan.d.ts.map +1 -0
  41. package/dist/commands/release/apply.preflight.plan.js +92 -0
  42. package/dist/commands/release/apply.preflight.publish.d.ts +4 -0
  43. package/dist/commands/release/apply.preflight.publish.d.ts.map +1 -0
  44. package/dist/commands/release/apply.preflight.publish.js +81 -0
  45. package/dist/commands/task/migrate-doc.d.ts.map +1 -1
  46. package/dist/commands/task/migrate-doc.js +25 -248
  47. package/dist/commands/task/migrate-doc.readme.d.ts +8 -0
  48. package/dist/commands/task/migrate-doc.readme.d.ts.map +1 -0
  49. package/dist/commands/task/migrate-doc.readme.js +225 -0
  50. package/dist/runtime/prompt-modules/index.d.ts +3 -0
  51. package/dist/runtime/prompt-modules/index.d.ts.map +1 -0
  52. package/dist/runtime/prompt-modules/index.js +1 -0
  53. package/dist/runtime/prompt-modules/model.d.ts +89 -0
  54. package/dist/runtime/prompt-modules/model.d.ts.map +1 -0
  55. package/dist/runtime/prompt-modules/model.js +1 -0
  56. package/dist/runtime/prompt-modules/mutations.d.ts +103 -0
  57. package/dist/runtime/prompt-modules/mutations.d.ts.map +1 -0
  58. package/dist/runtime/prompt-modules/mutations.js +1 -0
  59. package/dist/shared/runtime-env.d.ts +3 -0
  60. package/dist/shared/runtime-env.d.ts.map +1 -0
  61. package/dist/shared/runtime-env.js +73 -0
  62. package/package.json +3 -3
@@ -0,0 +1,16 @@
1
+ import type { PlanChange, ReleaseVersionPlan } from "./apply.types.js";
2
+ export declare function fileExists(p: string): Promise<boolean>;
3
+ export declare function readJsonFile<T>(p: string): Promise<T>;
4
+ export declare function parseVersionPlan(raw: unknown): ReleaseVersionPlan;
5
+ export declare function findLatestPlanDir(gitRoot: string): Promise<string>;
6
+ export declare function loadReleasePlan(opts: {
7
+ gitRoot: string;
8
+ planOverride?: string;
9
+ }): Promise<{
10
+ planDir: string;
11
+ versionJsonPath: string;
12
+ plan: ReleaseVersionPlan;
13
+ changes: PlanChange[];
14
+ minBullets: number;
15
+ }>;
16
+ //# sourceMappingURL=apply.preflight.plan.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"apply.preflight.plan.d.ts","sourceRoot":"","sources":["../../../src/commands/release/apply.preflight.plan.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,UAAU,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAEvE,wBAAsB,UAAU,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAO5D;AAED,wBAAsB,YAAY,CAAC,CAAC,EAAE,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC,CAE3D;AAaD,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,OAAO,GAAG,kBAAkB,CAuBjE;AAED,wBAAsB,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAiBxE;AAED,wBAAsB,eAAe,CAAC,IAAI,EAAE;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,YAAY,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAAC;IAC/F,OAAO,EAAE,MAAM,CAAC;IAChB,eAAe,EAAE,MAAM,CAAC;IACxB,IAAI,EAAE,kBAAkB,CAAC;IACzB,OAAO,EAAE,UAAU,EAAE,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;CACpB,CAAC,CAwBD"}
@@ -0,0 +1,92 @@
1
+ import { readFile, readdir } from "node:fs/promises";
2
+ import path from "node:path";
3
+ import { exitCodeForError } from "../../cli/exit-codes.js";
4
+ import { CliError } from "../../shared/errors.js";
5
+ export async function fileExists(p) {
6
+ try {
7
+ await readFile(p, "utf8");
8
+ return true;
9
+ }
10
+ catch {
11
+ return false;
12
+ }
13
+ }
14
+ export async function readJsonFile(p) {
15
+ return JSON.parse(await readFile(p, "utf8"));
16
+ }
17
+ function assertNonEmptyString(value, label) {
18
+ if (typeof value !== "string" || !value.trim()) {
19
+ throw new CliError({
20
+ exitCode: exitCodeForError("E_VALIDATION"),
21
+ code: "E_VALIDATION",
22
+ message: `Invalid ${label} (expected non-empty string).`,
23
+ });
24
+ }
25
+ return value.trim();
26
+ }
27
+ export function parseVersionPlan(raw) {
28
+ if (!raw || typeof raw !== "object") {
29
+ throw new CliError({
30
+ exitCode: exitCodeForError("E_VALIDATION"),
31
+ code: "E_VALIDATION",
32
+ message: "Invalid version.json (expected object).",
33
+ });
34
+ }
35
+ const obj = raw;
36
+ const bumpRaw = assertNonEmptyString(obj.bump, "bump");
37
+ if (bumpRaw !== "patch" && bumpRaw !== "minor" && bumpRaw !== "major") {
38
+ throw new CliError({
39
+ exitCode: exitCodeForError("E_VALIDATION"),
40
+ code: "E_VALIDATION",
41
+ message: `Invalid bump in version.json: ${bumpRaw}`,
42
+ });
43
+ }
44
+ const prevTagVal = obj.prevTag;
45
+ const prevTag = prevTagVal === null ? null : typeof prevTagVal === "string" ? prevTagVal : null;
46
+ const prevVersion = assertNonEmptyString(obj.prevVersion, "prevVersion");
47
+ const nextTag = assertNonEmptyString(obj.nextTag, "nextTag");
48
+ const nextVersion = assertNonEmptyString(obj.nextVersion, "nextVersion");
49
+ return { prevTag, prevVersion, nextTag, nextVersion, bump: bumpRaw };
50
+ }
51
+ export async function findLatestPlanDir(gitRoot) {
52
+ const base = path.join(gitRoot, ".agentplane", ".release", "plan");
53
+ const runNames = await readdir(base);
54
+ const runs = runNames
55
+ .map((s) => s.trim())
56
+ .filter(Boolean)
57
+ .toSorted();
58
+ const latest = runs.at(-1);
59
+ if (!latest) {
60
+ throw new CliError({
61
+ exitCode: exitCodeForError("E_IO"),
62
+ code: "E_IO",
63
+ message: "No release plan runs found under .agentplane/.release/plan/. Run `agentplane release plan` first.",
64
+ });
65
+ }
66
+ return path.join(base, latest);
67
+ }
68
+ export async function loadReleasePlan(opts) {
69
+ const planDir = opts.planOverride
70
+ ? path.resolve(opts.gitRoot, opts.planOverride)
71
+ : await findLatestPlanDir(opts.gitRoot);
72
+ const versionJsonPath = path.join(planDir, "version.json");
73
+ if (!(await fileExists(versionJsonPath))) {
74
+ throw new CliError({
75
+ exitCode: exitCodeForError("E_IO"),
76
+ code: "E_IO",
77
+ message: `Missing version.json in plan dir: ${path.relative(opts.gitRoot, versionJsonPath)}`,
78
+ });
79
+ }
80
+ const plan = parseVersionPlan(await readJsonFile(versionJsonPath));
81
+ const changesJsonPath = path.join(planDir, "changes.json");
82
+ const changes = (await fileExists(changesJsonPath))
83
+ ? await readJsonFile(changesJsonPath)
84
+ : [];
85
+ return {
86
+ planDir,
87
+ versionJsonPath,
88
+ plan,
89
+ changes,
90
+ minBullets: Math.max(1, Array.isArray(changes) ? changes.length : 0),
91
+ };
92
+ }
@@ -0,0 +1,4 @@
1
+ import { type ReleaseCommandLabel } from "./apply.preflight.git.js";
2
+ export declare function ensureNpmVersionsAvailable(gitRoot: string, version: string, commandLabel?: ReleaseCommandLabel): Promise<void>;
3
+ export declare function runReleasePrepublishGate(gitRoot: string, commandLabel?: ReleaseCommandLabel): Promise<void>;
4
+ //# sourceMappingURL=apply.preflight.publish.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"apply.preflight.publish.d.ts","sourceRoot":"","sources":["../../../src/commands/release/apply.preflight.publish.ts"],"names":[],"mappings":"AAWA,OAAO,EAA0B,KAAK,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAE5F,wBAAsB,0BAA0B,CAC9C,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,EACf,YAAY,GAAE,mBAAqC,GAClD,OAAO,CAAC,IAAI,CAAC,CAuCf;AAgBD,wBAAsB,wBAAwB,CAC5C,OAAO,EAAE,MAAM,EACf,YAAY,GAAE,mBAAqC,GAClD,OAAO,CAAC,IAAI,CAAC,CAsCf"}
@@ -0,0 +1,81 @@
1
+ import path from "node:path";
2
+ import { exitCodeForError } from "../../cli/exit-codes.js";
3
+ import { withDiagnosticContext } from "../shared/diagnostics.js";
4
+ import { CliError } from "../../shared/errors.js";
5
+ import { resolvePreferredNodeExecutable, withPreferredRuntimePath, } from "../../shared/runtime-env.js";
6
+ import { execFileAsync } from "@agentplaneorg/core/process";
7
+ import { releasePushDescription } from "./apply.preflight.git.js";
8
+ export async function ensureNpmVersionsAvailable(gitRoot, version, commandLabel = "release apply") {
9
+ const scriptPath = path.join(gitRoot, "scripts", "check-npm-version-availability.mjs");
10
+ try {
11
+ await execFileAsync(resolvePreferredNodeExecutable(process.env), [scriptPath, "--version", version], {
12
+ cwd: gitRoot,
13
+ env: withPreferredRuntimePath(process.env),
14
+ maxBuffer: 10 * 1024 * 1024,
15
+ });
16
+ }
17
+ catch (err) {
18
+ const details = String(err?.stderr ?? "").trim();
19
+ throw new CliError({
20
+ exitCode: exitCodeForError("E_VALIDATION"),
21
+ code: "E_VALIDATION",
22
+ message: `Pre-publish npm check failed for version ${version}. ` +
23
+ "Ensure this version is not already published for @agentplaneorg/core and agentplane." +
24
+ (details ? `\n\n${details}` : ""),
25
+ context: withDiagnosticContext({ command: commandLabel }, {
26
+ state: "the target npm version is not publishable",
27
+ likelyCause: "that version is already burned in npm history for one of the published packages, even if it is no longer the current dist-tag",
28
+ nextAction: {
29
+ command: `node scripts/check-npm-version-availability.mjs --version ${version}`,
30
+ reason: "inspect which package already consumed the target version before choosing a new release number",
31
+ reasonCode: "release_npm_version_burned",
32
+ },
33
+ }),
34
+ });
35
+ }
36
+ }
37
+ async function runReleasePrepublishPhase(gitRoot, phase) {
38
+ await execFileAsync("bun", ["run", `release:prepublish:${phase}`], {
39
+ cwd: gitRoot,
40
+ env: withPreferredRuntimePath({
41
+ ...process.env,
42
+ GIT_AUTHOR_NAME: process.env.GIT_AUTHOR_NAME ?? "agentplane-release",
43
+ GIT_AUTHOR_EMAIL: process.env.GIT_AUTHOR_EMAIL ?? "agentplane-release@example.com",
44
+ GIT_COMMITTER_NAME: process.env.GIT_COMMITTER_NAME ?? "agentplane-release",
45
+ GIT_COMMITTER_EMAIL: process.env.GIT_COMMITTER_EMAIL ?? "agentplane-release@example.com",
46
+ }),
47
+ maxBuffer: 200 * 1024 * 1024,
48
+ });
49
+ }
50
+ export async function runReleasePrepublishGate(gitRoot, commandLabel = "release apply") {
51
+ for (const phase of ["fast", "heavy"]) {
52
+ try {
53
+ await runReleasePrepublishPhase(gitRoot, phase);
54
+ }
55
+ catch (err) {
56
+ const details = String(err?.stderr ??
57
+ err?.stdout ??
58
+ err?.message ??
59
+ "").trim();
60
+ throw new CliError({
61
+ exitCode: exitCodeForError("E_VALIDATION"),
62
+ code: "E_VALIDATION",
63
+ message: `Release prepublish ${phase} phase failed. \`agentplane ${commandLabel} --push\` requires a successful local \`bun run release:prepublish:${phase}\` run before ${releasePushDescription(commandLabel)}.` +
64
+ (details ? `\n\n${details}` : ""),
65
+ context: withDiagnosticContext({ command: commandLabel }, {
66
+ state: `release prepublish ${phase} validation failed before ${releasePushDescription(commandLabel)}`,
67
+ likelyCause: phase === "fast"
68
+ ? "a lightweight publish-readiness check rejected the current release payload before the expensive validation route started"
69
+ : "the expensive release validation route rejected the current repository state after the fast publish-readiness checks passed",
70
+ nextAction: {
71
+ command: `bun run release:prepublish:${phase}`,
72
+ reason: phase === "fast"
73
+ ? `rerun the fast prepublish phase to fix the exact payload or packaging problem before retrying ${commandLabel}`
74
+ : `rerun the heavy prepublish phase to inspect and fix the expensive validation failure before retrying ${commandLabel}`,
75
+ reasonCode: `release_prepublish_${phase}_failed`,
76
+ },
77
+ }),
78
+ });
79
+ }
80
+ }
81
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"migrate-doc.d.ts","sourceRoot":"","sources":["../../../src/commands/task/migrate-doc.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;AAEnE,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAqB7D,OAAO,EAGL,KAAK,cAAc,EACpB,MAAM,2BAA2B,CAAC;AAanC,MAAM,MAAM,sBAAsB,GAAG;IACnC,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,EAAE,CAAC;CACxB,CAAC;AA+TF,wBAAsB,0BAA0B,CAAC,IAAI,EAAE;IACrD,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,GAAG,EAAE,OAAO,CAAC;IACb,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,eAAe,CAAC,EAAE,OAAO,CAAC,UAAU,CAAC,OAAO,cAAc,CAAC,CAAC,CAAC;IAC7D,MAAM,CAAC,EAAE,gBAAgB,CAAC;IAC1B,GAAG,CAAC,EAAE,cAAc,CAAC;CACtB,GAAG,OAAO,CAAC,sBAAsB,CAAC,CA4DlC;AAED,wBAAsB,iBAAiB,CAAC,IAAI,EAAE;IAC5C,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,GAAG,EAAE,OAAO,CAAC;IACb,KAAK,EAAE,OAAO,CAAC;IACf,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB,GAAG,OAAO,CAAC,MAAM,CAAC,CAoBlB"}
1
+ {"version":3,"file":"migrate-doc.d.ts","sourceRoot":"","sources":["../../../src/commands/task/migrate-doc.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;AAInE,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAO7D,OAAO,EAGL,KAAK,cAAc,EACpB,MAAM,2BAA2B,CAAC;AAKnC,MAAM,MAAM,sBAAsB,GAAG;IACnC,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,EAAE,CAAC;CACxB,CAAC;AAqEF,wBAAsB,0BAA0B,CAAC,IAAI,EAAE;IACrD,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,GAAG,EAAE,OAAO,CAAC;IACb,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,eAAe,CAAC,EAAE,OAAO,CAAC,UAAU,CAAC,OAAO,cAAc,CAAC,CAAC,CAAC;IAC7D,MAAM,CAAC,EAAE,gBAAgB,CAAC;IAC1B,GAAG,CAAC,EAAE,cAAc,CAAC;CACtB,GAAG,OAAO,CAAC,sBAAsB,CAAC,CA4DlC;AAED,wBAAsB,iBAAiB,CAAC,IAAI,EAAE;IAC5C,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,GAAG,EAAE,OAAO,CAAC;IACb,KAAK,EAAE,OAAO,CAAC;IACf,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB,GAAG,OAAO,CAAC,MAAM,CAAC,CAoBlB"}
@@ -1,239 +1,16 @@
1
1
  import { readdir, readFile } from "node:fs/promises";
2
2
  import path from "node:path";
3
3
  import { loadConfig } from "@agentplaneorg/core/config";
4
+ import { gitEnv } from "@agentplaneorg/core/git";
5
+ import { execFileAsync } from "@agentplaneorg/core/process";
4
6
  import { resolveProject } from "@agentplaneorg/core/project";
5
- import { atomicWriteFile } from "@agentplaneorg/core/fs";
6
- import { ensureDocSections, extractTaskDoc, mergeTaskDoc, normalizeTaskDoc, parseTaskReadme, renderTaskDocFromSections, renderTaskReadme, setMarkdownSection, taskDocToSectionMap, } from "@agentplaneorg/core/tasks";
7
7
  import { mapCoreError } from "../../cli/error-map.js";
8
8
  import { exitCodeForError } from "../../cli/exit-codes.js";
9
9
  import { fileExists, getPathKind } from "../../cli/fs-utils.js";
10
10
  import { successMessage } from "../../cli/output.js";
11
11
  import { CliError } from "../../shared/errors.js";
12
- import { execFileAsync } from "@agentplaneorg/core/process";
13
- import { gitEnv } from "@agentplaneorg/core/git";
14
12
  import { exportTaskProjectionSnapshot, loadCommandContext, } from "../shared/task-backend.js";
15
- import { extractDocSection, extractTaskObservationSection, decodeEscapedTaskTextNewlines, normalizeTaskDocVersion, normalizeVerificationSectionLayout, } from "./shared/docs.js";
16
- import { defaultTaskDocV3 } from "./doc-template.js";
17
- const V3_CANONICAL_ORDER = [
18
- "Summary",
19
- "Scope",
20
- "Plan",
21
- "Verify Steps",
22
- "Verification",
23
- "Rollback Plan",
24
- "Findings",
25
- ];
26
- const HUMAN_TEXT_SECTIONS = new Set(["summary", "context", "scope", "plan", "findings", "notes"]);
27
- function isRecord(value) {
28
- return Boolean(value) && typeof value === "object" && !Array.isArray(value);
29
- }
30
- function normalizeRevision(value) {
31
- return Number.isInteger(value) && typeof value === "number" && value > 0 ? value : null;
32
- }
33
- function normalizeCanonicalSections(value) {
34
- if (!isRecord(value))
35
- return null;
36
- const out = {};
37
- for (const [title, text] of Object.entries(value)) {
38
- const normalizedTitle = title.trim();
39
- if (!normalizedTitle || typeof text !== "string")
40
- continue;
41
- out[normalizedTitle] = text.replaceAll("\r\n", "\n").trimEnd();
42
- }
43
- return Object.keys(out).length > 0 ? out : null;
44
- }
45
- function normalizeSectionKey(section) {
46
- return section.trim().replaceAll(/\s+/g, " ").toLowerCase();
47
- }
48
- function parseMarkdownSections(doc) {
49
- const lines = doc.replaceAll("\r\n", "\n").split("\n");
50
- const sections = [];
51
- let current = null;
52
- for (const line of lines) {
53
- const match = /^##\s+(.*)$/.exec(line.trim());
54
- if (match) {
55
- if (current) {
56
- current.text = current.text.trimEnd();
57
- sections.push(current);
58
- }
59
- current = { title: (match[1] ?? "").trim(), text: "" };
60
- continue;
61
- }
62
- if (current) {
63
- current.text = current.text.length > 0 ? `${current.text}\n${line}` : line;
64
- }
65
- }
66
- if (current) {
67
- current.text = current.text.trimEnd();
68
- sections.push(current);
69
- }
70
- return sections;
71
- }
72
- function renderMarkdownSections(sections) {
73
- return sections
74
- .map((section) => {
75
- const text = section.text.trimEnd();
76
- return text ? `## ${section.title}\n\n${text}` : `## ${section.title}\n`;
77
- })
78
- .join("\n\n")
79
- .trimEnd();
80
- }
81
- function normalizeLiteralNewlinesInHumanSection(title, text) {
82
- if (!HUMAN_TEXT_SECTIONS.has(normalizeSectionKey(title)))
83
- return text.trimEnd();
84
- return decodeEscapedTaskTextNewlines(text).trimEnd();
85
- }
86
- function firstSectionText(sections, title) {
87
- const target = normalizeSectionKey(title);
88
- return sections.find((section) => normalizeSectionKey(section.title) === target)?.text ?? null;
89
- }
90
- function mergeObservationText(doc, version) {
91
- const preferred = extractTaskObservationSection(doc, version)?.trim() ?? "";
92
- const notes = extractDocSection(doc, "Notes")?.trim() ?? "";
93
- const findings = extractDocSection(doc, "Findings")?.trim() ?? "";
94
- const out = [];
95
- for (const candidate of [preferred, findings, notes]) {
96
- if (!candidate)
97
- continue;
98
- if (out.includes(candidate))
99
- continue;
100
- out.push(candidate);
101
- }
102
- return out.join("\n\n").trim();
103
- }
104
- function migrateDocToV3(opts) {
105
- const currentSections = parseMarkdownSections(opts.doc);
106
- const defaultSections = parseMarkdownSections(defaultTaskDocV3({ title: opts.title, description: opts.description }));
107
- const emitted = new Set();
108
- const nextSections = [];
109
- const observationText = mergeObservationText(opts.doc, 2);
110
- for (const title of V3_CANONICAL_ORDER) {
111
- const key = normalizeSectionKey(title);
112
- const currentText = firstSectionText(currentSections, title);
113
- const defaultText = firstSectionText(defaultSections, title) ?? "";
114
- let nextText = currentText ?? defaultText;
115
- if (title === "Verification") {
116
- nextText = normalizeVerificationSectionLayout(currentText ?? defaultText, 3);
117
- }
118
- if (title === "Findings") {
119
- nextText = observationText || defaultText;
120
- }
121
- if (nextText === null)
122
- continue;
123
- nextText = normalizeLiteralNewlinesInHumanSection(title, nextText);
124
- nextSections.push({ title, text: nextText.trimEnd() });
125
- emitted.add(key);
126
- if (title === "Findings")
127
- emitted.add("notes");
128
- }
129
- for (const section of currentSections) {
130
- const key = normalizeSectionKey(section.title);
131
- if (emitted.has(key))
132
- continue;
133
- nextSections.push({ title: section.title, text: section.text.trimEnd() });
134
- emitted.add(key);
135
- }
136
- return renderMarkdownSections(nextSections);
137
- }
138
- function ensurePlanApprovalFrontmatter(frontmatter) {
139
- const raw = frontmatter.plan_approval;
140
- if (isRecord(raw) && typeof raw.state === "string")
141
- return;
142
- frontmatter.plan_approval = {
143
- state: "pending",
144
- updated_at: null,
145
- updated_by: null,
146
- note: null,
147
- };
148
- }
149
- function ensureVerificationFrontmatter(frontmatter) {
150
- const raw = frontmatter.verification;
151
- if (isRecord(raw) && typeof raw.state === "string")
152
- return;
153
- frontmatter.verification = {
154
- state: "pending",
155
- updated_at: null,
156
- updated_by: null,
157
- note: null,
158
- };
159
- }
160
- const DATE_ONLY_ON_RE = /\bon (\d{4}-\d{2}-\d{2})(?!T)\b/g;
161
- function isIsoTimestamp(value) {
162
- if (!value.includes("T") || !value.endsWith("Z"))
163
- return false;
164
- const ms = Date.parse(value);
165
- return Number.isFinite(ms);
166
- }
167
- function normalizeNoteTimestamp(opts) {
168
- if (!isIsoTimestamp(opts.updatedAt))
169
- return opts.note;
170
- const updatedDate = opts.updatedAt.slice(0, 10);
171
- return opts.note.replaceAll(DATE_ONLY_ON_RE, (match, date) => {
172
- if (date !== updatedDate)
173
- return match;
174
- return `on ${opts.updatedAt}`;
175
- });
176
- }
177
- function normalizeFrontmatterNoteTimestamps(frontmatter) {
178
- const plan = frontmatter.plan_approval;
179
- if (isRecord(plan) && typeof plan.note === "string" && typeof plan.updated_at === "string") {
180
- plan.note = normalizeNoteTimestamp({ note: plan.note, updatedAt: plan.updated_at });
181
- }
182
- const verification = frontmatter.verification;
183
- if (isRecord(verification) &&
184
- typeof verification.note === "string" &&
185
- typeof verification.updated_at === "string") {
186
- verification.note = normalizeNoteTimestamp({
187
- note: verification.note,
188
- updatedAt: verification.updated_at,
189
- });
190
- }
191
- }
192
- async function migrateTaskReadmeDoc(opts) {
193
- const originalRaw = await readFile(opts.readmePath, "utf8");
194
- const original = originalRaw.endsWith("\n") ? originalRaw : `${originalRaw}\n`;
195
- const parsed = parseTaskReadme(original);
196
- const frontmatter = { ...parsed.frontmatter };
197
- ensurePlanApprovalFrontmatter(frontmatter);
198
- ensureVerificationFrontmatter(frontmatter);
199
- normalizeFrontmatterNoteTimestamps(frontmatter);
200
- const canonicalSections = normalizeCanonicalSections(frontmatter.sections);
201
- const required = opts.config.tasks.doc.required_sections;
202
- const extracted = extractTaskDoc(parsed.body);
203
- const baseDoc = canonicalSections === null
204
- ? extracted || parsed.body
205
- : renderTaskDocFromSections(canonicalSections);
206
- let nextDoc = normalizeTaskDoc(ensureDocSections(baseDoc, required));
207
- const docVersion = normalizeTaskDocVersion(frontmatter.doc_version);
208
- if (docVersion === 2) {
209
- frontmatter.doc_version = 3;
210
- nextDoc = migrateDocToV3({
211
- title: typeof frontmatter.title === "string" ? frontmatter.title : "",
212
- description: typeof frontmatter.description === "string" ? frontmatter.description : "",
213
- doc: nextDoc,
214
- });
215
- }
216
- else {
217
- const verificationSection = extractDocSection(nextDoc, "Verification");
218
- const normalizedVerification = normalizeVerificationSectionLayout(verificationSection, docVersion);
219
- nextDoc = setMarkdownSection(nextDoc, "Verification", normalizedVerification);
220
- for (const sectionTitle of ["Summary", "Context", "Scope", "Plan", "Findings", "Notes"]) {
221
- const sectionText = extractDocSection(nextDoc, sectionTitle);
222
- if (sectionText == null)
223
- continue;
224
- nextDoc = setMarkdownSection(nextDoc, sectionTitle, normalizeLiteralNewlinesInHumanSection(sectionTitle, sectionText));
225
- }
226
- }
227
- const nextBody = extracted ? mergeTaskDoc(parsed.body, nextDoc) : nextDoc;
228
- frontmatter.sections = taskDocToSectionMap(nextDoc);
229
- frontmatter.revision = normalizeRevision(frontmatter.revision) ?? 1;
230
- const rendered = renderTaskReadme(frontmatter, nextBody);
231
- const next = rendered.endsWith("\n") ? rendered : `${rendered}\n`;
232
- if (next === original)
233
- return { changed: false };
234
- await atomicWriteFile(opts.readmePath, next, "utf8");
235
- return { changed: true };
236
- }
13
+ import { migrateTaskReadmeDoc } from "./migrate-doc.readme.js";
237
14
  async function resolveReadmePaths(opts) {
238
15
  if (!opts.params.all) {
239
16
  return opts.params.taskIds.map((taskId) => path.join(opts.tasksDir, taskId, "README.md"));
@@ -251,6 +28,28 @@ async function resolveReadmePaths(opts) {
251
28
  }
252
29
  return out;
253
30
  }
31
+ async function canStageGitPath(gitRoot, relPath) {
32
+ try {
33
+ await execFileAsync("git", ["ls-files", "--error-unmatch", "--", relPath], {
34
+ cwd: gitRoot,
35
+ env: gitEnv(),
36
+ });
37
+ return true;
38
+ }
39
+ catch {
40
+ // Continue below: untracked paths may still be stageable when they are not ignored.
41
+ }
42
+ try {
43
+ await execFileAsync("git", ["check-ignore", "--quiet", "--", relPath], {
44
+ cwd: gitRoot,
45
+ env: gitEnv(),
46
+ });
47
+ return false;
48
+ }
49
+ catch {
50
+ return true;
51
+ }
52
+ }
254
53
  async function exportProjectionSnapshotIfChanged(opts) {
255
54
  if (!(opts.ctx.taskBackend.exportProjectionSnapshot || opts.ctx.taskBackend.exportTasksJson)) {
256
55
  return [];
@@ -276,28 +75,6 @@ async function exportProjectionSnapshotIfChanged(opts) {
276
75
  return [];
277
76
  return (await canStageGitPath(opts.resolvedGitRoot, relOutputPath)) ? [relOutputPath] : [];
278
77
  }
279
- async function canStageGitPath(gitRoot, relPath) {
280
- try {
281
- await execFileAsync("git", ["ls-files", "--error-unmatch", "--", relPath], {
282
- cwd: gitRoot,
283
- env: gitEnv(),
284
- });
285
- return true;
286
- }
287
- catch {
288
- // Continue below: untracked paths may still be stageable when they are not ignored.
289
- }
290
- try {
291
- await execFileAsync("git", ["check-ignore", "--quiet", "--", relPath], {
292
- cwd: gitRoot,
293
- env: gitEnv(),
294
- });
295
- return false;
296
- }
297
- catch {
298
- return true;
299
- }
300
- }
301
78
  export async function migrateTaskDocsInWorkspace(opts) {
302
79
  const resolved = opts.resolvedProject ??
303
80
  (await resolveProject({
@@ -0,0 +1,8 @@
1
+ import type { AgentplaneConfig } from "@agentplaneorg/core/config";
2
+ export declare function migrateTaskReadmeDoc(opts: {
3
+ readmePath: string;
4
+ config: AgentplaneConfig;
5
+ }): Promise<{
6
+ changed: boolean;
7
+ }>;
8
+ //# sourceMappingURL=migrate-doc.readme.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"migrate-doc.readme.d.ts","sourceRoot":"","sources":["../../../src/commands/task/migrate-doc.readme.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;AA4NnE,wBAAsB,oBAAoB,CAAC,IAAI,EAAE;IAC/C,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,gBAAgB,CAAC;CAC1B,GAAG,OAAO,CAAC;IAAE,OAAO,EAAE,OAAO,CAAA;CAAE,CAAC,CAuDhC"}