@agentplaneorg/core 0.1.6 → 0.1.8
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/dist/config/config.d.ts +0 -1
- package/dist/config/config.d.ts.map +1 -1
- package/dist/config/config.js +37 -7
- package/dist/git/base-branch.d.ts +11 -0
- package/dist/git/base-branch.d.ts.map +1 -1
- package/dist/git/base-branch.js +52 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/tasks/task-readme-io.d.ts +7 -0
- package/dist/tasks/task-readme-io.d.ts.map +1 -0
- package/dist/tasks/task-readme-io.js +14 -0
- package/dist/tasks/task-readme.d.ts.map +1 -1
- package/dist/tasks/task-readme.js +62 -38
- package/dist/tasks/task-store.d.ts +12 -0
- package/dist/tasks/task-store.d.ts.map +1 -1
- package/dist/tasks/task-store.js +40 -18
- package/package.json +1 -1
- package/schemas/config.schema.json +6 -11
package/dist/config/config.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/config/config.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/config/config.ts"],"names":[],"mappings":"AAWA,MAAM,MAAM,YAAY,GAAG,QAAQ,GAAG,WAAW,CAAC;AAClD,MAAM,MAAM,kBAAkB,GAAG,KAAK,GAAG,MAAM,GAAG,SAAS,CAAC;AAE5D,MAAM,MAAM,gBAAgB,GAAG;IAC7B,cAAc,EAAE,CAAC,CAAC;IAClB,aAAa,EAAE,YAAY,CAAC;IAC5B,oBAAoB,EAAE,kBAAkB,CAAC;IACzC,yBAAyB,EAAE,OAAO,CAAC;IACnC,MAAM,CAAC,EAAE;QACP,SAAS,EAAE;YACT,YAAY,EAAE,OAAO,CAAC;YACtB,eAAe,EAAE,OAAO,CAAC;YACzB,cAAc,EAAE,OAAO,CAAC;SACzB,CAAC;KACH,CAAC;IACF,OAAO,CAAC,EAAE;QACR,eAAe,EAAE,MAAM,GAAG,MAAM,GAAG,QAAQ,CAAC;KAC7C,CAAC;IACF,KAAK,EAAE;QACL,UAAU,EAAE,MAAM,CAAC;QACnB,UAAU,EAAE,MAAM,CAAC;QACnB,YAAY,EAAE,MAAM,CAAC;QACrB,aAAa,EAAE,MAAM,CAAC;KACvB,CAAC;IACF,MAAM,EAAE;QAAE,WAAW,EAAE,MAAM,CAAA;KAAE,CAAC;IAChC,SAAS,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC;IAC1D,KAAK,EAAE;QACL,wBAAwB,EAAE,MAAM,CAAC;QACjC,MAAM,EAAE;YAAE,aAAa,EAAE,MAAM,EAAE,CAAA;SAAE,CAAC;QACpC,GAAG,EAAE;YAAE,QAAQ,EAAE,MAAM,EAAE,CAAC;YAAC,iBAAiB,EAAE,MAAM,EAAE,CAAA;SAAE,CAAC;QACzD,QAAQ,EAAE;YACR,KAAK,EAAE;gBAAE,MAAM,EAAE,MAAM,CAAC;gBAAC,SAAS,EAAE,MAAM,CAAA;aAAE,CAAC;YAC7C,OAAO,EAAE;gBAAE,MAAM,EAAE,MAAM,CAAC;gBAAC,SAAS,EAAE,MAAM,CAAA;aAAE,CAAC;YAC/C,QAAQ,EAAE;gBAAE,MAAM,EAAE,MAAM,CAAC;gBAAC,SAAS,EAAE,MAAM,CAAA;aAAE,CAAC;SACjD,CAAC;KACH,CAAC;IACF,MAAM,EAAE;QAAE,cAAc,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC;IACrC,aAAa,EAAE;QAAE,WAAW,EAAE,MAAM,CAAA;KAAE,CAAC;IACvC,gCAAgC,EAAE,OAAO,CAAC;CAC3C,CAAC;AAEF,wBAAgB,aAAa,IAAI,gBAAgB,CAEhD;AAkED,wBAAgB,cAAc,CAAC,GAAG,EAAE,OAAO,GAAG,gBAAgB,CAa7D;AAID,MAAM,MAAM,YAAY,GAAG;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,OAAO,CAAC;IAChB,MAAM,EAAE,gBAAgB,CAAC;IACzB,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC9B,CAAC;AAQF,wBAAsB,UAAU,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CA8B7E;AAkBD,wBAAgB,cAAc,CAC5B,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC5B,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,MAAM,GACZ,IAAI,CAgBN;AAED,wBAAsB,UAAU,CAC9B,aAAa,EAAE,MAAM,EACrB,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC3B,OAAO,CAAC,gBAAgB,CAAC,CAS3B"}
|
package/dist/config/config.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { readFileSync } from "node:fs";
|
|
2
|
-
import { mkdir, readFile
|
|
2
|
+
import { mkdir, readFile } from "node:fs/promises";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
5
5
|
import AjvModule from "ajv";
|
|
6
6
|
import AjvFormatsModule from "ajv-formats";
|
|
7
|
+
import { atomicWriteFile } from "../fs/atomic-write.js";
|
|
7
8
|
export function defaultConfig() {
|
|
8
9
|
return structuredClone(DEFAULT_CONFIG);
|
|
9
10
|
}
|
|
@@ -29,8 +30,28 @@ function formatSchemaErrors(errors) {
|
|
|
29
30
|
return "config schema validation failed";
|
|
30
31
|
return AJV.errorsText(errors, { dataVar: "config" });
|
|
31
32
|
}
|
|
33
|
+
const DEPRECATED_CONFIG_KEYS = ["base_branch"];
|
|
34
|
+
function stripDeprecatedConfigKeys(raw) {
|
|
35
|
+
const sanitized = { ...raw };
|
|
36
|
+
const removed = [];
|
|
37
|
+
for (const key of DEPRECATED_CONFIG_KEYS) {
|
|
38
|
+
if (key in sanitized) {
|
|
39
|
+
delete sanitized[key];
|
|
40
|
+
removed.push(key);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return { sanitized, removed };
|
|
44
|
+
}
|
|
45
|
+
function warnDeprecatedConfigKeys(keys) {
|
|
46
|
+
for (const key of keys) {
|
|
47
|
+
console.warn(`config key "${key}" is deprecated and ignored`);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
32
50
|
export function validateConfig(raw) {
|
|
33
|
-
|
|
51
|
+
let candidate = raw && typeof raw === "object" ? structuredClone(raw) : raw;
|
|
52
|
+
if (isRecord(candidate)) {
|
|
53
|
+
candidate = stripDeprecatedConfigKeys(candidate).sanitized;
|
|
54
|
+
}
|
|
34
55
|
if (!validateSchema(candidate)) {
|
|
35
56
|
throw new Error(formatSchemaErrors(validateSchema.errors));
|
|
36
57
|
}
|
|
@@ -52,12 +73,18 @@ export async function loadConfig(agentplaneDir) {
|
|
|
52
73
|
try {
|
|
53
74
|
const rawText = await readFile(filePath, "utf8");
|
|
54
75
|
const parsed = JSON.parse(rawText);
|
|
55
|
-
const
|
|
76
|
+
const rawRecord = isRecord(parsed) ? parsed : null;
|
|
77
|
+
const sanitized = rawRecord
|
|
78
|
+
? stripDeprecatedConfigKeys(rawRecord)
|
|
79
|
+
: { sanitized: parsed, removed: [] };
|
|
80
|
+
if (sanitized.removed.length > 0)
|
|
81
|
+
warnDeprecatedConfigKeys(sanitized.removed);
|
|
82
|
+
const validated = validateConfig(sanitized.sanitized);
|
|
56
83
|
return {
|
|
57
84
|
path: filePath,
|
|
58
85
|
exists: true,
|
|
59
86
|
config: validated,
|
|
60
|
-
raw: parsed,
|
|
87
|
+
raw: (sanitized.sanitized ?? parsed),
|
|
61
88
|
};
|
|
62
89
|
}
|
|
63
90
|
catch (err) {
|
|
@@ -115,10 +142,13 @@ export function setByDottedKey(obj, dottedKey, value) {
|
|
|
115
142
|
current[last] = parseScalar(value);
|
|
116
143
|
}
|
|
117
144
|
export async function saveConfig(agentplaneDir, raw) {
|
|
118
|
-
const
|
|
145
|
+
const sanitized = stripDeprecatedConfigKeys(raw);
|
|
146
|
+
if (sanitized.removed.length > 0)
|
|
147
|
+
warnDeprecatedConfigKeys(sanitized.removed);
|
|
148
|
+
const validated = validateConfig(sanitized.sanitized);
|
|
119
149
|
await mkdir(agentplaneDir, { recursive: true });
|
|
120
150
|
const filePath = path.join(agentplaneDir, "config.json");
|
|
121
|
-
const text = `${JSON.stringify(
|
|
122
|
-
await
|
|
151
|
+
const text = `${JSON.stringify(sanitized.sanitized, null, 2)}\n`;
|
|
152
|
+
await atomicWriteFile(filePath, text, "utf8");
|
|
123
153
|
return validated;
|
|
124
154
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { WorkflowMode } from "../config/config.js";
|
|
1
2
|
export declare function getPinnedBaseBranch(opts: {
|
|
2
3
|
cwd: string;
|
|
3
4
|
rootOverride?: string | null;
|
|
@@ -11,4 +12,14 @@ export declare function setPinnedBaseBranch(opts: {
|
|
|
11
12
|
rootOverride?: string | null;
|
|
12
13
|
value: string;
|
|
13
14
|
}): Promise<string>;
|
|
15
|
+
export declare function clearPinnedBaseBranch(opts: {
|
|
16
|
+
cwd: string;
|
|
17
|
+
rootOverride?: string | null;
|
|
18
|
+
}): Promise<boolean>;
|
|
19
|
+
export declare function resolveBaseBranch(opts: {
|
|
20
|
+
cwd: string;
|
|
21
|
+
rootOverride?: string | null;
|
|
22
|
+
cliBaseOpt?: string | null;
|
|
23
|
+
mode: WorkflowMode;
|
|
24
|
+
}): Promise<string | null>;
|
|
14
25
|
//# sourceMappingURL=base-branch.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"base-branch.d.ts","sourceRoot":"","sources":["../../src/git/base-branch.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"base-branch.d.ts","sourceRoot":"","sources":["../../src/git/base-branch.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAiDxD,wBAAsB,mBAAmB,CAAC,IAAI,EAAE;IAC9C,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAGzB;AAED,wBAAsB,aAAa,CAAC,IAAI,EAAE;IACxC,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B,GAAG,OAAO,CAAC,MAAM,CAAC,CAGlB;AAED,wBAAsB,mBAAmB,CAAC,IAAI,EAAE;IAC9C,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,KAAK,EAAE,MAAM,CAAC;CACf,GAAG,OAAO,CAAC,MAAM,CAAC,CAMlB;AAED,wBAAsB,qBAAqB,CAAC,IAAI,EAAE;IAChD,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B,GAAG,OAAO,CAAC,OAAO,CAAC,CAGnB;AAED,wBAAsB,iBAAiB,CAAC,IAAI,EAAE;IAC5C,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,IAAI,EAAE,YAAY,CAAC;CACpB,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAgBzB"}
|
package/dist/git/base-branch.js
CHANGED
|
@@ -20,6 +20,35 @@ async function gitConfigGet(cwd, key) {
|
|
|
20
20
|
async function gitConfigSet(cwd, key, value) {
|
|
21
21
|
await execFileAsync("git", ["config", "--local", key, value], { cwd });
|
|
22
22
|
}
|
|
23
|
+
async function gitConfigUnset(cwd, key) {
|
|
24
|
+
try {
|
|
25
|
+
await execFileAsync("git", ["config", "--local", "--unset", key], { cwd });
|
|
26
|
+
return true;
|
|
27
|
+
}
|
|
28
|
+
catch (err) {
|
|
29
|
+
const code = err?.code;
|
|
30
|
+
if (code === 1 || code === 5)
|
|
31
|
+
return false;
|
|
32
|
+
throw err;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
async function gitCurrentBranch(cwd) {
|
|
36
|
+
try {
|
|
37
|
+
const { stdout } = await execFileAsync("git", ["symbolic-ref", "--short", "HEAD"], { cwd });
|
|
38
|
+
const trimmed = stdout.trim();
|
|
39
|
+
if (trimmed)
|
|
40
|
+
return trimmed;
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
// fall through
|
|
44
|
+
}
|
|
45
|
+
const { stdout } = await execFileAsync("git", ["rev-parse", "--abbrev-ref", "HEAD"], { cwd });
|
|
46
|
+
const trimmed = stdout.trim();
|
|
47
|
+
if (!trimmed || trimmed === "HEAD") {
|
|
48
|
+
throw new Error("Failed to resolve current branch");
|
|
49
|
+
}
|
|
50
|
+
return trimmed;
|
|
51
|
+
}
|
|
23
52
|
export async function getPinnedBaseBranch(opts) {
|
|
24
53
|
const resolved = await resolveProject({ cwd: opts.cwd, rootOverride: opts.rootOverride ?? null });
|
|
25
54
|
return await gitConfigGet(resolved.gitRoot, GIT_CONFIG_BASE_BRANCH_KEY);
|
|
@@ -36,3 +65,26 @@ export async function setPinnedBaseBranch(opts) {
|
|
|
36
65
|
await gitConfigSet(resolved.gitRoot, GIT_CONFIG_BASE_BRANCH_KEY, trimmed);
|
|
37
66
|
return trimmed;
|
|
38
67
|
}
|
|
68
|
+
export async function clearPinnedBaseBranch(opts) {
|
|
69
|
+
const resolved = await resolveProject({ cwd: opts.cwd, rootOverride: opts.rootOverride ?? null });
|
|
70
|
+
return await gitConfigUnset(resolved.gitRoot, GIT_CONFIG_BASE_BRANCH_KEY);
|
|
71
|
+
}
|
|
72
|
+
export async function resolveBaseBranch(opts) {
|
|
73
|
+
const explicit = (opts.cliBaseOpt ?? "").trim();
|
|
74
|
+
if (explicit.length > 0)
|
|
75
|
+
return explicit;
|
|
76
|
+
const pinned = await getPinnedBaseBranch({
|
|
77
|
+
cwd: opts.cwd,
|
|
78
|
+
rootOverride: opts.rootOverride ?? null,
|
|
79
|
+
});
|
|
80
|
+
if (pinned)
|
|
81
|
+
return pinned;
|
|
82
|
+
if (opts.mode === "branch_pr") {
|
|
83
|
+
const resolved = await resolveProject({
|
|
84
|
+
cwd: opts.cwd,
|
|
85
|
+
rootOverride: opts.rootOverride ?? null,
|
|
86
|
+
});
|
|
87
|
+
return await gitCurrentBranch(resolved.gitRoot);
|
|
88
|
+
}
|
|
89
|
+
return null;
|
|
90
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -2,13 +2,14 @@ export declare const CORE_VERSION = "0.0.0";
|
|
|
2
2
|
export { findGitRoot, resolveProject, type ResolvedProject, type ResolveProjectOptions, } from "./project/project-root.js";
|
|
3
3
|
export { defaultConfig, loadConfig, saveConfig, setByDottedKey, validateConfig, type AgentplaneConfig, type LoadedConfig, type StatusCommitPolicy, type WorkflowMode, } from "./config/config.js";
|
|
4
4
|
export { parseTaskReadme, renderTaskFrontmatter, renderTaskReadme, type ParsedTaskReadme, } from "./tasks/task-readme.js";
|
|
5
|
+
export { readTaskReadme, updateTaskReadmeAtomic } from "./tasks/task-readme-io.js";
|
|
5
6
|
export { docChanged, ensureDocSections, extractTaskDoc, mergeTaskDoc, normalizeDocSectionName, normalizeTaskDoc, parseDocSections, setMarkdownSection, splitCombinedHeadingLines, } from "./tasks/task-doc.js";
|
|
6
7
|
export { atomicWriteFile } from "./fs/atomic-write.js";
|
|
7
8
|
export { generateTaskId, timestampIdPrefix, TASK_ID_ALPHABET } from "./tasks/task-id.js";
|
|
8
9
|
export { createTask, getTasksDir, listTasks, readTask, setTaskDocSection, taskReadmePath, validateTaskDocMetadata, type TaskFrontmatter, type TaskPriority, type TaskRecord, type TaskStatus, } from "./tasks/task-store.js";
|
|
9
10
|
export { buildTasksExportSnapshot, canonicalTasksPayload, canonicalizeJson, computeTasksChecksum, writeTasksExport, type TasksExportMeta, type TasksExportSnapshot, type TasksExportTask, } from "./tasks/tasks-export.js";
|
|
10
11
|
export { lintTasksFile, lintTasksSnapshot, readTasksExport, type TasksLintResult, } from "./tasks/tasks-lint.js";
|
|
11
|
-
export { getBaseBranch, getPinnedBaseBranch, setPinnedBaseBranch } from "./git/base-branch.js";
|
|
12
|
+
export { clearPinnedBaseBranch, getBaseBranch, getPinnedBaseBranch, resolveBaseBranch, setPinnedBaseBranch, } from "./git/base-branch.js";
|
|
12
13
|
export { extractTaskSuffix, isGenericSubject, validateCommitSubject, type CommitPolicyResult, } from "./commit/commit-policy.js";
|
|
13
14
|
export { getStagedFiles, getUnstagedFiles } from "./git/git-utils.js";
|
|
14
15
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,YAAY,UAAU,CAAC;AAEpC,OAAO,EACL,WAAW,EACX,cAAc,EACd,KAAK,eAAe,EACpB,KAAK,qBAAqB,GAC3B,MAAM,2BAA2B,CAAC;AAEnC,OAAO,EACL,aAAa,EACb,UAAU,EACV,UAAU,EACV,cAAc,EACd,cAAc,EACd,KAAK,gBAAgB,EACrB,KAAK,YAAY,EACjB,KAAK,kBAAkB,EACvB,KAAK,YAAY,GAClB,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EACL,eAAe,EACf,qBAAqB,EACrB,gBAAgB,EAChB,KAAK,gBAAgB,GACtB,MAAM,wBAAwB,CAAC;AAEhC,OAAO,EACL,UAAU,EACV,iBAAiB,EACjB,cAAc,EACd,YAAY,EACZ,uBAAuB,EACvB,gBAAgB,EAChB,gBAAgB,EAChB,kBAAkB,EAClB,yBAAyB,GAC1B,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAEvD,OAAO,EAAE,cAAc,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAEzF,OAAO,EACL,UAAU,EACV,WAAW,EACX,SAAS,EACT,QAAQ,EACR,iBAAiB,EACjB,cAAc,EACd,uBAAuB,EACvB,KAAK,eAAe,EACpB,KAAK,YAAY,EACjB,KAAK,UAAU,EACf,KAAK,UAAU,GAChB,MAAM,uBAAuB,CAAC;AAE/B,OAAO,EACL,wBAAwB,EACxB,qBAAqB,EACrB,gBAAgB,EAChB,oBAAoB,EACpB,gBAAgB,EAChB,KAAK,eAAe,EACpB,KAAK,mBAAmB,EACxB,KAAK,eAAe,GACrB,MAAM,yBAAyB,CAAC;AAEjC,OAAO,EACL,aAAa,EACb,iBAAiB,EACjB,eAAe,EACf,KAAK,eAAe,GACrB,MAAM,uBAAuB,CAAC;AAE/B,OAAO,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,YAAY,UAAU,CAAC;AAEpC,OAAO,EACL,WAAW,EACX,cAAc,EACd,KAAK,eAAe,EACpB,KAAK,qBAAqB,GAC3B,MAAM,2BAA2B,CAAC;AAEnC,OAAO,EACL,aAAa,EACb,UAAU,EACV,UAAU,EACV,cAAc,EACd,cAAc,EACd,KAAK,gBAAgB,EACrB,KAAK,YAAY,EACjB,KAAK,kBAAkB,EACvB,KAAK,YAAY,GAClB,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EACL,eAAe,EACf,qBAAqB,EACrB,gBAAgB,EAChB,KAAK,gBAAgB,GACtB,MAAM,wBAAwB,CAAC;AAEhC,OAAO,EAAE,cAAc,EAAE,sBAAsB,EAAE,MAAM,2BAA2B,CAAC;AAEnF,OAAO,EACL,UAAU,EACV,iBAAiB,EACjB,cAAc,EACd,YAAY,EACZ,uBAAuB,EACvB,gBAAgB,EAChB,gBAAgB,EAChB,kBAAkB,EAClB,yBAAyB,GAC1B,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAEvD,OAAO,EAAE,cAAc,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAEzF,OAAO,EACL,UAAU,EACV,WAAW,EACX,SAAS,EACT,QAAQ,EACR,iBAAiB,EACjB,cAAc,EACd,uBAAuB,EACvB,KAAK,eAAe,EACpB,KAAK,YAAY,EACjB,KAAK,UAAU,EACf,KAAK,UAAU,GAChB,MAAM,uBAAuB,CAAC;AAE/B,OAAO,EACL,wBAAwB,EACxB,qBAAqB,EACrB,gBAAgB,EAChB,oBAAoB,EACpB,gBAAgB,EAChB,KAAK,eAAe,EACpB,KAAK,mBAAmB,EACxB,KAAK,eAAe,GACrB,MAAM,yBAAyB,CAAC;AAEjC,OAAO,EACL,aAAa,EACb,iBAAiB,EACjB,eAAe,EACf,KAAK,eAAe,GACrB,MAAM,uBAAuB,CAAC;AAE/B,OAAO,EACL,qBAAqB,EACrB,aAAa,EACb,mBAAmB,EACnB,iBAAiB,EACjB,mBAAmB,GACpB,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EACL,iBAAiB,EACjB,gBAAgB,EAChB,qBAAqB,EACrB,KAAK,kBAAkB,GACxB,MAAM,2BAA2B,CAAC;AAEnC,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -2,12 +2,13 @@ export const CORE_VERSION = "0.0.0";
|
|
|
2
2
|
export { findGitRoot, resolveProject, } from "./project/project-root.js";
|
|
3
3
|
export { defaultConfig, loadConfig, saveConfig, setByDottedKey, validateConfig, } from "./config/config.js";
|
|
4
4
|
export { parseTaskReadme, renderTaskFrontmatter, renderTaskReadme, } from "./tasks/task-readme.js";
|
|
5
|
+
export { readTaskReadme, updateTaskReadmeAtomic } from "./tasks/task-readme-io.js";
|
|
5
6
|
export { docChanged, ensureDocSections, extractTaskDoc, mergeTaskDoc, normalizeDocSectionName, normalizeTaskDoc, parseDocSections, setMarkdownSection, splitCombinedHeadingLines, } from "./tasks/task-doc.js";
|
|
6
7
|
export { atomicWriteFile } from "./fs/atomic-write.js";
|
|
7
8
|
export { generateTaskId, timestampIdPrefix, TASK_ID_ALPHABET } from "./tasks/task-id.js";
|
|
8
9
|
export { createTask, getTasksDir, listTasks, readTask, setTaskDocSection, taskReadmePath, validateTaskDocMetadata, } from "./tasks/task-store.js";
|
|
9
10
|
export { buildTasksExportSnapshot, canonicalTasksPayload, canonicalizeJson, computeTasksChecksum, writeTasksExport, } from "./tasks/tasks-export.js";
|
|
10
11
|
export { lintTasksFile, lintTasksSnapshot, readTasksExport, } from "./tasks/tasks-lint.js";
|
|
11
|
-
export { getBaseBranch, getPinnedBaseBranch, setPinnedBaseBranch } from "./git/base-branch.js";
|
|
12
|
+
export { clearPinnedBaseBranch, getBaseBranch, getPinnedBaseBranch, resolveBaseBranch, setPinnedBaseBranch, } from "./git/base-branch.js";
|
|
12
13
|
export { extractTaskSuffix, isGenericSubject, validateCommitSubject, } from "./commit/commit-policy.js";
|
|
13
14
|
export { getStagedFiles, getUnstagedFiles } from "./git/git-utils.js";
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { type ParsedTaskReadme } from "./task-readme.js";
|
|
2
|
+
export declare function readTaskReadme(readmePath: string): Promise<ParsedTaskReadme>;
|
|
3
|
+
export declare function updateTaskReadmeAtomic(readmePath: string, updater: (parsed: ParsedTaskReadme) => {
|
|
4
|
+
frontmatter: Record<string, unknown>;
|
|
5
|
+
body: string;
|
|
6
|
+
}): Promise<void>;
|
|
7
|
+
//# sourceMappingURL=task-readme-io.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"task-readme-io.d.ts","sourceRoot":"","sources":["../../src/tasks/task-readme-io.ts"],"names":[],"mappings":"AAIA,OAAO,EAAqC,KAAK,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAE5F,wBAAsB,cAAc,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAGlF;AAED,wBAAsB,sBAAsB,CAC1C,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,CAAC,MAAM,EAAE,gBAAgB,KAAK;IAAE,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAC5F,OAAO,CAAC,IAAI,CAAC,CAMf"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
import { atomicWriteFile } from "../fs/atomic-write.js";
|
|
3
|
+
import { parseTaskReadme, renderTaskReadme } from "./task-readme.js";
|
|
4
|
+
export async function readTaskReadme(readmePath) {
|
|
5
|
+
const text = await readFile(readmePath, "utf8");
|
|
6
|
+
return parseTaskReadme(text);
|
|
7
|
+
}
|
|
8
|
+
export async function updateTaskReadmeAtomic(readmePath, updater) {
|
|
9
|
+
const text = await readFile(readmePath, "utf8");
|
|
10
|
+
const parsed = parseTaskReadme(text);
|
|
11
|
+
const next = updater(parsed);
|
|
12
|
+
const rendered = renderTaskReadme(next.frontmatter, next.body);
|
|
13
|
+
await atomicWriteFile(readmePath, rendered.endsWith("\n") ? rendered : `${rendered}\n`);
|
|
14
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"task-readme.d.ts","sourceRoot":"","sources":["../../src/tasks/task-readme.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"task-readme.d.ts","sourceRoot":"","sources":["../../src/tasks/task-readme.ts"],"names":[],"mappings":"AAqBA,MAAM,MAAM,gBAAgB,GAAG;IAC7B,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACrC,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAqBF,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,gBAAgB,CAYlE;AAkFD,wBAAgB,qBAAqB,CAAC,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAiClF;AAED,wBAAgB,gBAAgB,CAAC,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAE3F"}
|
|
@@ -2,6 +2,18 @@ import { parse as parseYaml } from "yaml";
|
|
|
2
2
|
function isRecord(value) {
|
|
3
3
|
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
4
4
|
}
|
|
5
|
+
function orderedKeys(value, preferredKeyOrder) {
|
|
6
|
+
const keys = Object.keys(value);
|
|
7
|
+
const ordered = [];
|
|
8
|
+
if (preferredKeyOrder) {
|
|
9
|
+
for (const k of preferredKeyOrder)
|
|
10
|
+
if (k in value)
|
|
11
|
+
ordered.push(k);
|
|
12
|
+
}
|
|
13
|
+
const remaining = keys.filter((k) => !ordered.includes(k)).toSorted((a, b) => a.localeCompare(b));
|
|
14
|
+
ordered.push(...remaining);
|
|
15
|
+
return ordered;
|
|
16
|
+
}
|
|
5
17
|
function stripLeadingFrontmatterBlocks(body) {
|
|
6
18
|
let next = body.replaceAll("\r\n", "\n");
|
|
7
19
|
while (true) {
|
|
@@ -44,58 +56,69 @@ function renderScalar(value) {
|
|
|
44
56
|
return value ? "true" : "false";
|
|
45
57
|
throw new TypeError(`Unsupported scalar type: ${typeof value}`);
|
|
46
58
|
}
|
|
47
|
-
function renderInlineMap(value, preferredKeyOrder) {
|
|
48
|
-
const keys = Object.keys(value);
|
|
49
|
-
const ordered = [];
|
|
50
|
-
if (preferredKeyOrder) {
|
|
51
|
-
for (const k of preferredKeyOrder)
|
|
52
|
-
if (k in value)
|
|
53
|
-
ordered.push(k);
|
|
54
|
-
}
|
|
55
|
-
const remaining = keys.filter((k) => !ordered.includes(k)).toSorted((a, b) => a.localeCompare(b));
|
|
56
|
-
ordered.push(...remaining);
|
|
57
|
-
const parts = ordered.map((k) => {
|
|
58
|
-
const v = value[k];
|
|
59
|
-
if (Array.isArray(v))
|
|
60
|
-
return `${k}: ${renderFlowSeq(v)}`;
|
|
61
|
-
if (isRecord(v))
|
|
62
|
-
return `${k}: ${renderInlineMap(v, null)}`;
|
|
63
|
-
return `${k}: ${renderScalar(v)}`;
|
|
64
|
-
});
|
|
65
|
-
return `{ ${parts.join(", ")} }`;
|
|
66
|
-
}
|
|
67
59
|
function renderFlowSeq(value) {
|
|
68
60
|
const parts = value.map((v) => {
|
|
69
61
|
if (Array.isArray(v))
|
|
70
62
|
return renderFlowSeq(v);
|
|
71
63
|
if (isRecord(v))
|
|
72
|
-
return
|
|
64
|
+
return `{ ${orderedKeys(v, null)
|
|
65
|
+
.map((k) => `${k}: ${renderScalar(v[k])}`)
|
|
66
|
+
.join(", ")} }`;
|
|
73
67
|
return renderScalar(v);
|
|
74
68
|
});
|
|
75
69
|
return `[${parts.join(", ")}]`;
|
|
76
70
|
}
|
|
77
|
-
function
|
|
71
|
+
function renderMapLines(value, indent, preferredKeyOrder) {
|
|
72
|
+
const keys = orderedKeys(value, preferredKeyOrder);
|
|
73
|
+
const lines = [];
|
|
74
|
+
for (const k of keys) {
|
|
75
|
+
const v = value[k];
|
|
76
|
+
lines.push(...renderValueLines(k, v, indent));
|
|
77
|
+
}
|
|
78
|
+
return lines;
|
|
79
|
+
}
|
|
80
|
+
function isStringArray(value) {
|
|
81
|
+
return value.every((v) => typeof v === "string");
|
|
82
|
+
}
|
|
83
|
+
function renderValueLines(key, value, indent) {
|
|
78
84
|
if (Array.isArray(value)) {
|
|
79
85
|
if (value.length === 0)
|
|
80
|
-
return [`${key}: []`];
|
|
86
|
+
return [`${indent}${key}: []`];
|
|
87
|
+
if (isStringArray(value)) {
|
|
88
|
+
return [`${indent}${key}:`, ...value.map((item) => `${indent} - ${renderScalar(item)}`)];
|
|
89
|
+
}
|
|
81
90
|
const allObjects = value.every((v) => isRecord(v));
|
|
82
91
|
if (!allObjects)
|
|
83
|
-
return [`${key}: ${renderFlowSeq(value)}`];
|
|
92
|
+
return [`${indent}${key}: ${renderFlowSeq(value)}`];
|
|
84
93
|
return [
|
|
85
|
-
`${key}:`,
|
|
86
|
-
...value.
|
|
94
|
+
`${indent}${key}:`,
|
|
95
|
+
...value.flatMap((item) => {
|
|
87
96
|
if (!isRecord(item))
|
|
88
97
|
throw new TypeError("Expected an object item in YAML sequence");
|
|
89
98
|
const preferred = key === "comments" ? ["author", "body"] : null;
|
|
90
|
-
|
|
99
|
+
const itemLines = renderMapLines(item, `${indent} `, preferred);
|
|
100
|
+
if (itemLines.length === 0)
|
|
101
|
+
return [`${indent} - {}`];
|
|
102
|
+
return [`${indent} -`, ...itemLines];
|
|
91
103
|
}),
|
|
92
104
|
];
|
|
93
105
|
}
|
|
94
106
|
if (isRecord(value)) {
|
|
95
|
-
const preferred = key === "
|
|
96
|
-
|
|
107
|
+
const preferred = key === "origin"
|
|
108
|
+
? ["system", "issue_id", "url"]
|
|
109
|
+
: key === "plan_approval"
|
|
110
|
+
? ["state", "updated_at", "updated_by", "note"]
|
|
111
|
+
: key === "verification"
|
|
112
|
+
? ["state", "updated_at", "updated_by", "note"]
|
|
113
|
+
: key === "commit"
|
|
114
|
+
? ["hash", "message"]
|
|
115
|
+
: null;
|
|
116
|
+
const inner = renderMapLines(value, `${indent} `, preferred);
|
|
117
|
+
if (inner.length === 0)
|
|
118
|
+
return [`${indent}${key}: {}`];
|
|
119
|
+
return [`${indent}${key}:`, ...inner];
|
|
97
120
|
}
|
|
98
|
-
return [`${key}: ${renderScalar(value)}`];
|
|
121
|
+
return [`${indent}${key}: ${renderScalar(value)}`];
|
|
99
122
|
}
|
|
100
123
|
export function renderTaskFrontmatter(frontmatter) {
|
|
101
124
|
const preferredKeyOrder = [
|
|
@@ -104,26 +127,27 @@ export function renderTaskFrontmatter(frontmatter) {
|
|
|
104
127
|
"status",
|
|
105
128
|
"priority",
|
|
106
129
|
"owner",
|
|
130
|
+
"created_at",
|
|
131
|
+
"created_by",
|
|
132
|
+
"origin",
|
|
107
133
|
"depends_on",
|
|
108
134
|
"tags",
|
|
109
135
|
"verify",
|
|
136
|
+
"plan_approval",
|
|
137
|
+
"verification",
|
|
110
138
|
"commit",
|
|
111
139
|
"comments",
|
|
112
140
|
"doc_version",
|
|
113
141
|
"doc_updated_at",
|
|
114
142
|
"doc_updated_by",
|
|
115
143
|
"description",
|
|
144
|
+
"id_source",
|
|
145
|
+
"dirty",
|
|
116
146
|
];
|
|
117
|
-
const
|
|
118
|
-
const ordered = [];
|
|
119
|
-
for (const k of preferredKeyOrder)
|
|
120
|
-
if (k in frontmatter)
|
|
121
|
-
ordered.push(k);
|
|
122
|
-
const remaining = keys.filter((k) => !ordered.includes(k)).toSorted((a, b) => a.localeCompare(b));
|
|
123
|
-
ordered.push(...remaining);
|
|
147
|
+
const ordered = orderedKeys(frontmatter, preferredKeyOrder);
|
|
124
148
|
const lines = [];
|
|
125
149
|
for (const k of ordered) {
|
|
126
|
-
lines.push(...
|
|
150
|
+
lines.push(...renderValueLines(k, frontmatter[k], ""));
|
|
127
151
|
}
|
|
128
152
|
return `---\n${lines.join("\n")}\n---\n`;
|
|
129
153
|
}
|
|
@@ -10,6 +10,18 @@ export type TaskFrontmatter = {
|
|
|
10
10
|
depends_on: string[];
|
|
11
11
|
tags: string[];
|
|
12
12
|
verify: string[];
|
|
13
|
+
plan_approval?: {
|
|
14
|
+
state: "pending" | "approved" | "rejected";
|
|
15
|
+
updated_at: string | null;
|
|
16
|
+
updated_by: string | null;
|
|
17
|
+
note: string | null;
|
|
18
|
+
};
|
|
19
|
+
verification?: {
|
|
20
|
+
state: "pending" | "ok" | "needs_rework";
|
|
21
|
+
updated_at: string | null;
|
|
22
|
+
updated_by: string | null;
|
|
23
|
+
note: string | null;
|
|
24
|
+
};
|
|
13
25
|
comments: {
|
|
14
26
|
author: string;
|
|
15
27
|
body: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"task-store.d.ts","sourceRoot":"","sources":["../../src/tasks/task-store.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"task-store.d.ts","sourceRoot":"","sources":["../../src/tasks/task-store.ts"],"names":[],"mappings":"AASA,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAE9C,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,GAAG,SAAS,CAAC;AAC/D,MAAM,MAAM,YAAY,GAAG,KAAK,GAAG,QAAQ,GAAG,KAAK,GAAG,MAAM,CAAC;AAE7D,MAAM,MAAM,eAAe,GAAG;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,UAAU,CAAC;IACnB,QAAQ,EAAE,YAAY,CAAC;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,aAAa,CAAC,EAAE;QACd,KAAK,EAAE,SAAS,GAAG,UAAU,GAAG,UAAU,CAAC;QAC3C,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;QAC1B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;QAC1B,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;KACrB,CAAC;IACF,YAAY,CAAC,EAAE;QACb,KAAK,EAAE,SAAS,GAAG,IAAI,GAAG,cAAc,CAAC;QACzC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;QAC1B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;QAC1B,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;KACrB,CAAC;IACF,QAAQ,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAC7C,WAAW,EAAE,CAAC,CAAC;IACf,cAAc,EAAE,MAAM,CAAC;IACvB,cAAc,EAAE,MAAM,CAAC;IACvB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;CACnD,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,WAAW,EAAE,eAAe,CAAC;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;CACpB,CAAC;AAUF,wBAAgB,uBAAuB,CAAC,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,EAAE,CAgBtF;AAWD,wBAAsB,WAAW,CAAC,IAAI,EAAE;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,GAAG,OAAO,CAAC;IAC9F,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,qBAAqB,EAAE,MAAM,CAAC;CAC/B,CAAC,CASD;AAED,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAEvE;AA4ED,wBAAsB,UAAU,CAAC,IAAI,EAAE;IACrC,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,YAAY,CAAC;IACvB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,WAAW,CAAC,EAAE,OAAO,cAAc,CAAC;CACrC,GAAG,OAAO,CAAC;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,CAAC,CA+C9C;AAED,wBAAsB,iBAAiB,CAAC,IAAI,EAAE;IAC5C,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B,GAAG,OAAO,CAAC;IAAE,UAAU,EAAE,MAAM,CAAA;CAAE,CAAC,CA2BlC;AAED,wBAAsB,QAAQ,CAAC,IAAI,EAAE;IACnC,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,MAAM,EAAE,MAAM,CAAC;CAChB,GAAG,OAAO,CAAC,UAAU,CAAC,CActB;AAED,wBAAsB,SAAS,CAAC,IAAI,EAAE;IACpC,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC,CA0BxB"}
|
package/dist/tasks/task-store.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
|
-
import { mkdir, readdir, readFile
|
|
1
|
+
import { mkdir, readdir, readFile } from "node:fs/promises";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { loadConfig } from "../config/config.js";
|
|
4
|
+
import { atomicWriteFile } from "../fs/atomic-write.js";
|
|
4
5
|
import { resolveProject } from "../project/project-root.js";
|
|
5
6
|
import { parseTaskReadme, renderTaskReadme } from "./task-readme.js";
|
|
7
|
+
import { updateTaskReadmeAtomic } from "./task-readme-io.js";
|
|
6
8
|
import { ensureDocSections, setMarkdownSection } from "./task-doc.js";
|
|
7
9
|
import { generateTaskId } from "./task-id.js";
|
|
8
10
|
function nowIso() {
|
|
@@ -55,15 +57,25 @@ function defaultTaskBody() {
|
|
|
55
57
|
"## Scope",
|
|
56
58
|
"",
|
|
57
59
|
"",
|
|
58
|
-
"##
|
|
60
|
+
"## Plan",
|
|
59
61
|
"",
|
|
60
62
|
"",
|
|
61
|
-
"##
|
|
63
|
+
"## Risks",
|
|
62
64
|
"",
|
|
63
65
|
"",
|
|
64
66
|
"## Verification",
|
|
65
67
|
"",
|
|
66
68
|
"",
|
|
69
|
+
"### Plan",
|
|
70
|
+
"",
|
|
71
|
+
"",
|
|
72
|
+
"### Results",
|
|
73
|
+
"",
|
|
74
|
+
"",
|
|
75
|
+
"<!-- BEGIN VERIFICATION RESULTS -->",
|
|
76
|
+
"<!-- END VERIFICATION RESULTS -->",
|
|
77
|
+
"",
|
|
78
|
+
"",
|
|
67
79
|
"## Rollback Plan",
|
|
68
80
|
"",
|
|
69
81
|
].join("\n");
|
|
@@ -133,6 +145,18 @@ export async function createTask(opts) {
|
|
|
133
145
|
depends_on: opts.dependsOn,
|
|
134
146
|
tags: opts.tags,
|
|
135
147
|
verify: opts.verify,
|
|
148
|
+
plan_approval: {
|
|
149
|
+
state: "pending",
|
|
150
|
+
updated_at: null,
|
|
151
|
+
updated_by: null,
|
|
152
|
+
note: null,
|
|
153
|
+
},
|
|
154
|
+
verification: {
|
|
155
|
+
state: "pending",
|
|
156
|
+
updated_at: null,
|
|
157
|
+
updated_by: null,
|
|
158
|
+
note: null,
|
|
159
|
+
},
|
|
136
160
|
comments: [],
|
|
137
161
|
doc_version: 2,
|
|
138
162
|
doc_updated_at: nowIso(),
|
|
@@ -141,8 +165,7 @@ export async function createTask(opts) {
|
|
|
141
165
|
};
|
|
142
166
|
const body = defaultTaskBody();
|
|
143
167
|
const text = renderTaskReadme(frontmatter, body);
|
|
144
|
-
await
|
|
145
|
-
await writeFile(readmePath, text, "utf8");
|
|
168
|
+
await atomicWriteFile(readmePath, text, "utf8");
|
|
146
169
|
return { id, readmePath };
|
|
147
170
|
}
|
|
148
171
|
export async function setTaskDocSection(opts) {
|
|
@@ -154,19 +177,18 @@ export async function setTaskDocSection(opts) {
|
|
|
154
177
|
}
|
|
155
178
|
const tasksDir = path.join(resolved.gitRoot, loaded.config.paths.workflow_dir);
|
|
156
179
|
const readmePath = taskReadmePath(tasksDir, opts.taskId);
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
await writeFile(readmePath, nextText, "utf8");
|
|
180
|
+
await updateTaskReadmeAtomic(readmePath, (parsed) => {
|
|
181
|
+
const updatedBy = resolveDocUpdatedBy(parsed.frontmatter, opts.updatedBy);
|
|
182
|
+
const nextFrontmatter = {
|
|
183
|
+
...parsed.frontmatter,
|
|
184
|
+
doc_version: 2,
|
|
185
|
+
doc_updated_at: nowIso(),
|
|
186
|
+
doc_updated_by: updatedBy,
|
|
187
|
+
};
|
|
188
|
+
const baseDoc = ensureDocSections(parsed.body, loaded.config.tasks.doc.required_sections);
|
|
189
|
+
const nextBody = ensureDocSections(setMarkdownSection(baseDoc, opts.section, opts.text), loaded.config.tasks.doc.required_sections);
|
|
190
|
+
return { frontmatter: nextFrontmatter, body: nextBody };
|
|
191
|
+
});
|
|
170
192
|
return { readmePath };
|
|
171
193
|
}
|
|
172
194
|
export async function readTask(opts) {
|
package/package.json
CHANGED
|
@@ -18,7 +18,6 @@
|
|
|
18
18
|
"default": "warn"
|
|
19
19
|
},
|
|
20
20
|
"finish_auto_status_commit": { "type": "boolean", "default": true },
|
|
21
|
-
"base_branch": { "type": "string", "minLength": 1, "default": "main" },
|
|
22
21
|
"agents": {
|
|
23
22
|
"type": "object",
|
|
24
23
|
"additionalProperties": true,
|
|
@@ -112,6 +111,7 @@
|
|
|
112
111
|
"Summary",
|
|
113
112
|
"Context",
|
|
114
113
|
"Scope",
|
|
114
|
+
"Plan",
|
|
115
115
|
"Risks",
|
|
116
116
|
"Verify Steps",
|
|
117
117
|
"Verification",
|
|
@@ -121,8 +121,8 @@
|
|
|
121
121
|
"required_sections": [
|
|
122
122
|
"Summary",
|
|
123
123
|
"Scope",
|
|
124
|
+
"Plan",
|
|
124
125
|
"Risks",
|
|
125
|
-
"Verify Steps",
|
|
126
126
|
"Verification",
|
|
127
127
|
"Rollback Plan"
|
|
128
128
|
]
|
|
@@ -163,6 +163,7 @@
|
|
|
163
163
|
"Summary",
|
|
164
164
|
"Context",
|
|
165
165
|
"Scope",
|
|
166
|
+
"Plan",
|
|
166
167
|
"Risks",
|
|
167
168
|
"Verify Steps",
|
|
168
169
|
"Verification",
|
|
@@ -172,8 +173,8 @@
|
|
|
172
173
|
"required_sections": [
|
|
173
174
|
"Summary",
|
|
174
175
|
"Scope",
|
|
176
|
+
"Plan",
|
|
175
177
|
"Risks",
|
|
176
|
-
"Verify Steps",
|
|
177
178
|
"Verification",
|
|
178
179
|
"Rollback Plan"
|
|
179
180
|
]
|
|
@@ -186,6 +187,7 @@
|
|
|
186
187
|
"Summary",
|
|
187
188
|
"Context",
|
|
188
189
|
"Scope",
|
|
190
|
+
"Plan",
|
|
189
191
|
"Risks",
|
|
190
192
|
"Verify Steps",
|
|
191
193
|
"Verification",
|
|
@@ -196,14 +198,7 @@
|
|
|
196
198
|
"required_sections": {
|
|
197
199
|
"type": "array",
|
|
198
200
|
"items": { "type": "string", "minLength": 1 },
|
|
199
|
-
"default": [
|
|
200
|
-
"Summary",
|
|
201
|
-
"Scope",
|
|
202
|
-
"Risks",
|
|
203
|
-
"Verify Steps",
|
|
204
|
-
"Verification",
|
|
205
|
-
"Rollback Plan"
|
|
206
|
-
]
|
|
201
|
+
"default": ["Summary", "Scope", "Plan", "Risks", "Verification", "Rollback Plan"]
|
|
207
202
|
}
|
|
208
203
|
}
|
|
209
204
|
},
|