@agentplaneorg/core 0.1.3 → 0.1.5

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 (52) hide show
  1. package/README.md +16 -3
  2. package/dist/commit/commit-policy.d.ts.map +1 -0
  3. package/dist/{config.d.ts → config/config.d.ts} +1 -0
  4. package/dist/config/config.d.ts.map +1 -0
  5. package/dist/config/config.js +124 -0
  6. package/dist/fs/atomic-write.d.ts +2 -0
  7. package/dist/fs/atomic-write.d.ts.map +1 -0
  8. package/dist/fs/atomic-write.js +9 -0
  9. package/dist/git/base-branch.d.ts.map +1 -0
  10. package/dist/{base-branch.js → git/base-branch.js} +1 -1
  11. package/dist/git/git-utils.d.ts.map +1 -0
  12. package/dist/{git-utils.js → git/git-utils.js} +1 -1
  13. package/dist/index.d.ts +12 -9
  14. package/dist/index.d.ts.map +1 -1
  15. package/dist/index.js +12 -9
  16. package/dist/project/project-root.d.ts.map +1 -0
  17. package/dist/tasks/task-doc.d.ts +16 -0
  18. package/dist/tasks/task-doc.d.ts.map +1 -0
  19. package/dist/tasks/task-doc.js +326 -0
  20. package/dist/tasks/task-id.d.ts +9 -0
  21. package/dist/tasks/task-id.d.ts.map +1 -0
  22. package/dist/tasks/task-id.js +28 -0
  23. package/dist/tasks/task-readme.d.ts.map +1 -0
  24. package/dist/{task-store.d.ts → tasks/task-store.d.ts} +2 -0
  25. package/dist/tasks/task-store.d.ts.map +1 -0
  26. package/dist/tasks/task-store.js +212 -0
  27. package/dist/tasks/tasks-export.d.ts.map +1 -0
  28. package/dist/{tasks-export.js → tasks/tasks-export.js} +5 -4
  29. package/dist/{tasks-lint.d.ts → tasks/tasks-lint.d.ts} +1 -1
  30. package/dist/tasks/tasks-lint.d.ts.map +1 -0
  31. package/dist/{tasks-lint.js → tasks/tasks-lint.js} +2 -2
  32. package/package.json +3 -1
  33. package/dist/base-branch.d.ts.map +0 -1
  34. package/dist/commit-policy.d.ts.map +0 -1
  35. package/dist/config.d.ts.map +0 -1
  36. package/dist/config.js +0 -237
  37. package/dist/git-utils.d.ts.map +0 -1
  38. package/dist/project-root.d.ts.map +0 -1
  39. package/dist/task-readme.d.ts.map +0 -1
  40. package/dist/task-store.d.ts.map +0 -1
  41. package/dist/task-store.js +0 -353
  42. package/dist/tasks-export.d.ts.map +0 -1
  43. package/dist/tasks-lint.d.ts.map +0 -1
  44. /package/dist/{commit-policy.d.ts → commit/commit-policy.d.ts} +0 -0
  45. /package/dist/{commit-policy.js → commit/commit-policy.js} +0 -0
  46. /package/dist/{base-branch.d.ts → git/base-branch.d.ts} +0 -0
  47. /package/dist/{git-utils.d.ts → git/git-utils.d.ts} +0 -0
  48. /package/dist/{project-root.d.ts → project/project-root.d.ts} +0 -0
  49. /package/dist/{project-root.js → project/project-root.js} +0 -0
  50. /package/dist/{task-readme.d.ts → tasks/task-readme.d.ts} +0 -0
  51. /package/dist/{task-readme.js → tasks/task-readme.js} +0 -0
  52. /package/dist/{tasks-export.d.ts → tasks/tasks-export.d.ts} +0 -0
@@ -0,0 +1,28 @@
1
+ import { randomInt } from "node:crypto";
2
+ export const TASK_ID_ALPHABET = "0123456789ABCDEFGHJKMNPQRSTVWXYZ";
3
+ export function timestampIdPrefix(date) {
4
+ const yyyy = String(date.getUTCFullYear()).padStart(4, "0");
5
+ const mm = String(date.getUTCMonth() + 1).padStart(2, "0");
6
+ const dd = String(date.getUTCDate()).padStart(2, "0");
7
+ const hh = String(date.getUTCHours()).padStart(2, "0");
8
+ const min = String(date.getUTCMinutes()).padStart(2, "0");
9
+ return `${yyyy}${mm}${dd}${hh}${min}`;
10
+ }
11
+ export function generateTaskId(opts) {
12
+ const attempts = Math.max(1, opts.attempts);
13
+ const length = opts.length;
14
+ const isAvailable = opts.isAvailable ?? ((taskId) => Promise.resolve(taskId.length > 0));
15
+ return (async () => {
16
+ for (let i = 0; i < attempts; i += 1) {
17
+ const now = opts.date ?? new Date();
18
+ let suffix = "";
19
+ for (let j = 0; j < length; j += 1) {
20
+ suffix += TASK_ID_ALPHABET[randomInt(0, TASK_ID_ALPHABET.length)];
21
+ }
22
+ const taskId = `${timestampIdPrefix(now)}-${suffix}`;
23
+ if (await isAvailable(taskId))
24
+ return taskId;
25
+ }
26
+ throw new Error("Failed to generate a unique task id (exhausted attempts)");
27
+ })();
28
+ }
@@ -0,0 +1 @@
1
+ {"version":3,"file":"task-readme.d.ts","sourceRoot":"","sources":["../../src/tasks/task-readme.ts"],"names":[],"mappings":"AAMA,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;AAiED,wBAAgB,qBAAqB,CAAC,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CA8BlF;AAED,wBAAgB,gBAAgB,CAAC,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAE3F"}
@@ -1,3 +1,4 @@
1
+ import { generateTaskId } from "./task-id.js";
1
2
  export type TaskStatus = "TODO" | "DOING" | "DONE" | "BLOCKED";
2
3
  export type TaskPriority = "low" | "normal" | "med" | "high";
3
4
  export type TaskFrontmatter = {
@@ -48,6 +49,7 @@ export declare function createTask(opts: {
48
49
  tags: string[];
49
50
  dependsOn: string[];
50
51
  verify: string[];
52
+ idGenerator?: typeof generateTaskId;
51
53
  }): Promise<{
52
54
  id: string;
53
55
  readmePath: string;
@@ -0,0 +1 @@
1
+ {"version":3,"file":"task-store.d.ts","sourceRoot":"","sources":["../../src/tasks/task-store.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,212 @@
1
+ import { mkdir, readdir, readFile, writeFile } from "node:fs/promises";
2
+ import path from "node:path";
3
+ import { loadConfig } from "../config/config.js";
4
+ import { resolveProject } from "../project/project-root.js";
5
+ import { parseTaskReadme, renderTaskReadme } from "./task-readme.js";
6
+ import { ensureDocSections, setMarkdownSection } from "./task-doc.js";
7
+ import { generateTaskId } from "./task-id.js";
8
+ function nowIso() {
9
+ return new Date().toISOString();
10
+ }
11
+ function isRecord(value) {
12
+ return !!value && typeof value === "object" && !Array.isArray(value);
13
+ }
14
+ export function validateTaskDocMetadata(frontmatter) {
15
+ const errors = [];
16
+ if (frontmatter.doc_version !== 2)
17
+ errors.push("doc_version must be 2");
18
+ const updatedAt = frontmatter.doc_updated_at;
19
+ if (typeof updatedAt !== "string" || Number.isNaN(Date.parse(updatedAt))) {
20
+ errors.push("doc_updated_at must be an ISO timestamp");
21
+ }
22
+ const updatedBy = frontmatter.doc_updated_by;
23
+ if (typeof updatedBy !== "string" || updatedBy.trim().length === 0) {
24
+ errors.push("doc_updated_by must be a non-empty string");
25
+ }
26
+ return errors;
27
+ }
28
+ async function fileExists(filePath) {
29
+ try {
30
+ await readFile(filePath, "utf8");
31
+ return true;
32
+ }
33
+ catch {
34
+ return false;
35
+ }
36
+ }
37
+ export async function getTasksDir(opts) {
38
+ const resolved = await resolveProject({ cwd: opts.cwd, rootOverride: opts.rootOverride ?? null });
39
+ const loaded = await loadConfig(resolved.agentplaneDir);
40
+ const tasksDir = path.join(resolved.gitRoot, loaded.config.paths.workflow_dir);
41
+ return {
42
+ gitRoot: resolved.gitRoot,
43
+ tasksDir,
44
+ idSuffixLengthDefault: loaded.config.tasks.id_suffix_length_default,
45
+ };
46
+ }
47
+ export function taskReadmePath(tasksDir, taskId) {
48
+ return path.join(tasksDir, taskId, "README.md");
49
+ }
50
+ function defaultTaskBody() {
51
+ return [
52
+ "## Summary",
53
+ "",
54
+ "",
55
+ "## Scope",
56
+ "",
57
+ "",
58
+ "## Risks",
59
+ "",
60
+ "",
61
+ "## Verify Steps",
62
+ "",
63
+ "",
64
+ "## Verification",
65
+ "",
66
+ "",
67
+ "## Rollback Plan",
68
+ "",
69
+ ].join("\n");
70
+ }
71
+ function getLastCommentAuthor(frontmatter) {
72
+ const comments = frontmatter.comments;
73
+ if (!Array.isArray(comments))
74
+ return null;
75
+ const entries = comments;
76
+ for (let i = entries.length - 1; i >= 0; i -= 1) {
77
+ const entry = entries[i];
78
+ if (!isRecord(entry))
79
+ continue;
80
+ const author = entry.author;
81
+ if (typeof author === "string") {
82
+ const trimmed = author.trim();
83
+ if (trimmed)
84
+ return trimmed;
85
+ }
86
+ }
87
+ return null;
88
+ }
89
+ function resolveDocUpdatedBy(frontmatter, updatedBy) {
90
+ if (updatedBy != null) {
91
+ const explicit = updatedBy.trim();
92
+ if (explicit.length === 0) {
93
+ throw new Error("doc_updated_by must be a non-empty string");
94
+ }
95
+ return explicit;
96
+ }
97
+ const lastAuthor = getLastCommentAuthor(frontmatter);
98
+ if (lastAuthor)
99
+ return lastAuthor;
100
+ const existing = frontmatter.doc_updated_by;
101
+ if (typeof existing === "string") {
102
+ const trimmed = existing.trim();
103
+ if (trimmed && trimmed.toLowerCase() !== "agentplane")
104
+ return trimmed;
105
+ }
106
+ const owner = frontmatter.owner;
107
+ if (typeof owner === "string") {
108
+ const trimmed = owner.trim();
109
+ if (trimmed)
110
+ return trimmed;
111
+ }
112
+ return "agentplane";
113
+ }
114
+ export async function createTask(opts) {
115
+ const { tasksDir, idSuffixLengthDefault } = await getTasksDir({
116
+ cwd: opts.cwd,
117
+ rootOverride: opts.rootOverride ?? null,
118
+ });
119
+ await mkdir(tasksDir, { recursive: true });
120
+ const suffixLength = idSuffixLengthDefault;
121
+ const id = await (opts.idGenerator ?? generateTaskId)({
122
+ length: suffixLength,
123
+ attempts: 20,
124
+ isAvailable: async (taskId) => !(await fileExists(taskReadmePath(tasksDir, taskId))),
125
+ });
126
+ const readmePath = taskReadmePath(tasksDir, id);
127
+ const frontmatter = {
128
+ id,
129
+ title: opts.title,
130
+ status: "TODO",
131
+ priority: opts.priority,
132
+ owner: opts.owner,
133
+ depends_on: opts.dependsOn,
134
+ tags: opts.tags,
135
+ verify: opts.verify,
136
+ comments: [],
137
+ doc_version: 2,
138
+ doc_updated_at: nowIso(),
139
+ doc_updated_by: opts.owner,
140
+ description: opts.description,
141
+ };
142
+ const body = defaultTaskBody();
143
+ const text = renderTaskReadme(frontmatter, body);
144
+ await mkdir(path.dirname(readmePath), { recursive: true });
145
+ await writeFile(readmePath, text, "utf8");
146
+ return { id, readmePath };
147
+ }
148
+ export async function setTaskDocSection(opts) {
149
+ const resolved = await resolveProject({ cwd: opts.cwd, rootOverride: opts.rootOverride ?? null });
150
+ const loaded = await loadConfig(resolved.agentplaneDir);
151
+ const allowed = loaded.config.tasks.doc.sections;
152
+ if (!allowed.includes(opts.section)) {
153
+ throw new Error(`Unknown doc section: ${opts.section}`);
154
+ }
155
+ const tasksDir = path.join(resolved.gitRoot, loaded.config.paths.workflow_dir);
156
+ const readmePath = taskReadmePath(tasksDir, opts.taskId);
157
+ const original = await readFile(readmePath, "utf8");
158
+ const parsed = parseTaskReadme(original);
159
+ const updatedBy = resolveDocUpdatedBy(parsed.frontmatter, opts.updatedBy);
160
+ const nextFrontmatter = {
161
+ ...parsed.frontmatter,
162
+ doc_version: 2,
163
+ doc_updated_at: nowIso(),
164
+ doc_updated_by: updatedBy,
165
+ };
166
+ const baseDoc = ensureDocSections(parsed.body, loaded.config.tasks.doc.required_sections);
167
+ const nextBody = ensureDocSections(setMarkdownSection(baseDoc, opts.section, opts.text), loaded.config.tasks.doc.required_sections);
168
+ const nextText = renderTaskReadme(nextFrontmatter, nextBody);
169
+ await writeFile(readmePath, nextText, "utf8");
170
+ return { readmePath };
171
+ }
172
+ export async function readTask(opts) {
173
+ const { tasksDir } = await getTasksDir({
174
+ cwd: opts.cwd,
175
+ rootOverride: opts.rootOverride ?? null,
176
+ });
177
+ const readmePath = taskReadmePath(tasksDir, opts.taskId);
178
+ const text = await readFile(readmePath, "utf8");
179
+ const parsed = parseTaskReadme(text);
180
+ return {
181
+ id: opts.taskId,
182
+ frontmatter: parsed.frontmatter,
183
+ body: parsed.body,
184
+ readmePath,
185
+ };
186
+ }
187
+ export async function listTasks(opts) {
188
+ const { tasksDir } = await getTasksDir({
189
+ cwd: opts.cwd,
190
+ rootOverride: opts.rootOverride ?? null,
191
+ });
192
+ const entries = await readdir(tasksDir, { withFileTypes: true }).catch(() => []);
193
+ const ids = entries.filter((e) => e.isDirectory()).map((e) => e.name);
194
+ const tasks = [];
195
+ for (const id of ids) {
196
+ const readmePath = taskReadmePath(tasksDir, id);
197
+ try {
198
+ const text = await readFile(readmePath, "utf8");
199
+ const parsed = parseTaskReadme(text);
200
+ tasks.push({
201
+ id,
202
+ frontmatter: parsed.frontmatter,
203
+ body: parsed.body,
204
+ readmePath,
205
+ });
206
+ }
207
+ catch {
208
+ // Skip unreadable/broken tasks for now; lint will handle this later.
209
+ }
210
+ }
211
+ return tasks.toSorted((a, b) => a.id.localeCompare(b.id));
212
+ }
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tasks-export.d.ts","sourceRoot":"","sources":["../../src/tasks/tasks-export.ts"],"names":[],"mappings":"AAaA,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAWxD;AAED,MAAM,MAAM,eAAe,GAAG;IAC5B,cAAc,EAAE,CAAC,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,QAAQ,CAAC;IACxB,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,MAAM,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IACjD,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,KAAK,EAAE,OAAO,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAChC,KAAK,EAAE,eAAe,EAAE,CAAC;IACzB,IAAI,EAAE,eAAe,CAAC;CACvB,CAAC;AAEF,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,eAAe,EAAE,GAAG,MAAM,CAEtE;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,eAAe,EAAE,GAAG,MAAM,CAGrE;AAED,wBAAsB,wBAAwB,CAAC,IAAI,EAAE;IACnD,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAqE/B;AAED,wBAAsB,gBAAgB,CAAC,IAAI,EAAE;IAC3C,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B,GAAG,OAAO,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,mBAAmB,CAAA;CAAE,CAAC,CAW3D"}
@@ -1,8 +1,9 @@
1
1
  import { createHash } from "node:crypto";
2
- import { mkdir, writeFile } from "node:fs/promises";
2
+ import { mkdir } from "node:fs/promises";
3
3
  import path from "node:path";
4
- import { loadConfig } from "./config.js";
5
- import { resolveProject } from "./project-root.js";
4
+ import { loadConfig } from "../config/config.js";
5
+ import { atomicWriteFile } from "../fs/atomic-write.js";
6
+ import { resolveProject } from "../project/project-root.js";
6
7
  import { listTasks } from "./task-store.js";
7
8
  function isRecord(value) {
8
9
  return !!value && typeof value === "object" && !Array.isArray(value);
@@ -91,6 +92,6 @@ export async function writeTasksExport(opts) {
91
92
  const outPath = path.join(resolved.gitRoot, loaded.config.paths.tasks_path);
92
93
  const snapshot = await buildTasksExportSnapshot(opts);
93
94
  await mkdir(path.dirname(outPath), { recursive: true });
94
- await writeFile(outPath, `${JSON.stringify(snapshot, null, 2)}\n`, "utf8");
95
+ await atomicWriteFile(outPath, `${JSON.stringify(snapshot, null, 2)}\n`);
95
96
  return { path: outPath, snapshot };
96
97
  }
@@ -1,4 +1,4 @@
1
- import type { AgentplaneConfig } from "./config.js";
1
+ import type { AgentplaneConfig } from "../config/config.js";
2
2
  import { type TasksExportSnapshot } from "./tasks-export.js";
3
3
  export type TasksLintResult = {
4
4
  errors: string[];
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tasks-lint.d.ts","sourceRoot":"","sources":["../../src/tasks/tasks-lint.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAG5D,OAAO,EAEL,KAAK,mBAAmB,EAEzB,MAAM,mBAAmB,CAAC;AAE3B,MAAM,MAAM,eAAe,GAAG;IAC5B,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB,CAAC;AA2DF,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,mBAAmB,EAC7B,MAAM,EAAE,gBAAgB,GACvB,eAAe,CA4GjB;AAED,wBAAsB,eAAe,CAAC,IAAI,EAAE;IAC1C,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B,GAAG,OAAO,CAAC;IAAE,QAAQ,EAAE,mBAAmB,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,gBAAgB,CAAA;CAAE,CAAC,CAOrF;AAED,wBAAsB,aAAa,CAAC,IAAI,EAAE;IACxC,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B,GAAG,OAAO,CAAC,eAAe,CAAC,CAG3B"}
@@ -1,7 +1,7 @@
1
1
  import { readFile } from "node:fs/promises";
2
2
  import path from "node:path";
3
- import { loadConfig } from "./config.js";
4
- import { resolveProject } from "./project-root.js";
3
+ import { loadConfig } from "../config/config.js";
4
+ import { resolveProject } from "../project/project-root.js";
5
5
  import { computeTasksChecksum, } from "./tasks-export.js";
6
6
  function isRecord(value) {
7
7
  return !!value && typeof value === "object" && !Array.isArray(value);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentplaneorg/core",
3
- "version": "0.1.3",
3
+ "version": "0.1.5",
4
4
  "description": "Core utilities and models for the Agent Plane CLI.",
5
5
  "keywords": [
6
6
  "agentplane",
@@ -43,6 +43,8 @@
43
43
  "prepack": "bun run clean && bun run build"
44
44
  },
45
45
  "dependencies": {
46
+ "ajv": "^8.17.1",
47
+ "ajv-formats": "^3.0.1",
46
48
  "yaml": "^2.8.2"
47
49
  }
48
50
  }
@@ -1 +0,0 @@
1
- {"version":3,"file":"base-branch.d.ts","sourceRoot":"","sources":["../src/base-branch.ts"],"names":[],"mappings":"AA0BA,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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"commit-policy.d.ts","sourceRoot":"","sources":["../src/commit-policy.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,kBAAkB,GAAG;IAC/B,EAAE,EAAE,OAAO,CAAC;IACZ,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB,CAAC;AAMF,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAGxD;AAED,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,GAAG,OAAO,CAOlF;AAED,wBAAgB,qBAAqB,CAAC,IAAI,EAAE;IAC1C,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,EAAE,MAAM,EAAE,CAAC;CACzB,GAAG,kBAAkB,CAerB"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAGA,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,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE;QACP,SAAS,EAAE;YACT,YAAY,EAAE,OAAO,CAAC;YACtB,eAAe,EAAE,OAAO,CAAC;SAC1B,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,CAmDhD;AAMD,wBAAgB,cAAc,CAAC,GAAG,EAAE,OAAO,GAAG,gBAAgB,CAiG7D;AAED,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,CAyB7E;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,CAO3B"}
package/dist/config.js DELETED
@@ -1,237 +0,0 @@
1
- import { mkdir, readFile, writeFile } from "node:fs/promises";
2
- import path from "node:path";
3
- export function defaultConfig() {
4
- return {
5
- schema_version: 1,
6
- workflow_mode: "direct",
7
- status_commit_policy: "warn",
8
- finish_auto_status_commit: true,
9
- base_branch: "main",
10
- agents: {
11
- approvals: {
12
- require_plan: true,
13
- require_network: true,
14
- },
15
- },
16
- recipes: {
17
- storage_default: "link",
18
- },
19
- paths: {
20
- agents_dir: ".agentplane/agents",
21
- tasks_path: ".agentplane/tasks.json",
22
- workflow_dir: ".agentplane/tasks",
23
- worktrees_dir: ".agentplane/worktrees",
24
- },
25
- branch: { task_prefix: "task" },
26
- framework: { source: "https://github.com/basilisk-labs/agent-plane", last_update: null },
27
- tasks: {
28
- id_suffix_length_default: 6,
29
- verify: { required_tags: ["code", "backend", "frontend"] },
30
- doc: {
31
- sections: [
32
- "Summary",
33
- "Context",
34
- "Scope",
35
- "Risks",
36
- "Verify Steps",
37
- "Rollback Plan",
38
- "Notes",
39
- ],
40
- required_sections: ["Summary", "Scope", "Risks", "Verify Steps", "Rollback Plan"],
41
- },
42
- comments: {
43
- start: { prefix: "Start:", min_chars: 40 },
44
- blocked: { prefix: "Blocked:", min_chars: 40 },
45
- verified: { prefix: "Verified:", min_chars: 60 },
46
- },
47
- },
48
- commit: {
49
- generic_tokens: ["start", "status", "mark", "done", "wip", "update", "tasks", "task"],
50
- },
51
- tasks_backend: { config_path: ".agentplane/backends/local/backend.json" },
52
- closure_commit_requires_approval: false,
53
- };
54
- }
55
- function isRecord(value) {
56
- return !!value && typeof value === "object" && !Array.isArray(value);
57
- }
58
- export function validateConfig(raw) {
59
- if (!isRecord(raw))
60
- throw new TypeError("config must be an object");
61
- if (raw.schema_version !== 1)
62
- throw new Error("config.schema_version must be 1");
63
- if (raw.workflow_mode !== "direct" && raw.workflow_mode !== "branch_pr") {
64
- throw new Error("config.workflow_mode must be 'direct' or 'branch_pr'");
65
- }
66
- if (raw.status_commit_policy !== "off" &&
67
- raw.status_commit_policy !== "warn" &&
68
- raw.status_commit_policy !== "confirm") {
69
- throw new Error("config.status_commit_policy must be 'off' | 'warn' | 'confirm'");
70
- }
71
- if (typeof raw.finish_auto_status_commit !== "boolean")
72
- throw new Error("config.finish_auto_status_commit must be boolean");
73
- if (typeof raw.base_branch !== "string" || raw.base_branch.length === 0)
74
- throw new Error("config.base_branch must be string");
75
- if (raw.agents !== undefined) {
76
- if (!isRecord(raw.agents))
77
- throw new Error("config.agents must be object");
78
- if (!isRecord(raw.agents.approvals))
79
- throw new Error("config.agents.approvals must be object");
80
- if (typeof raw.agents.approvals.require_plan !== "boolean") {
81
- throw new Error("config.agents.approvals.require_plan must be boolean");
82
- }
83
- if (typeof raw.agents.approvals.require_network !== "boolean") {
84
- throw new Error("config.agents.approvals.require_network must be boolean");
85
- }
86
- }
87
- if (raw.recipes !== undefined) {
88
- if (!isRecord(raw.recipes))
89
- throw new Error("config.recipes must be object");
90
- const storageDefault = raw.recipes.storage_default;
91
- if (storageDefault !== "link" && storageDefault !== "copy" && storageDefault !== "global") {
92
- throw new Error("config.recipes.storage_default must be 'link' | 'copy' | 'global'");
93
- }
94
- }
95
- if (!isRecord(raw.paths))
96
- throw new Error("config.paths must be object");
97
- if (!isRecord(raw.branch))
98
- throw new Error("config.branch must be object");
99
- if (!isRecord(raw.framework))
100
- throw new Error("config.framework must be object");
101
- if (!isRecord(raw.tasks))
102
- throw new Error("config.tasks must be object");
103
- if (!isRecord(raw.commit))
104
- throw new Error("config.commit must be object");
105
- if (!isRecord(raw.tasks_backend))
106
- throw new Error("config.tasks_backend must be object");
107
- if (typeof raw.closure_commit_requires_approval !== "boolean") {
108
- throw new Error("config.closure_commit_requires_approval must be boolean");
109
- }
110
- // Minimal path fields validation.
111
- for (const key of ["agents_dir", "tasks_path", "workflow_dir", "worktrees_dir"]) {
112
- const v = raw.paths[key];
113
- if (typeof v !== "string" || v.length === 0)
114
- throw new Error(`config.paths.${key} must be string`);
115
- }
116
- if (typeof raw.branch.task_prefix !== "string" || raw.branch.task_prefix.length === 0) {
117
- throw new Error("config.branch.task_prefix must be string");
118
- }
119
- if (typeof raw.framework.source !== "string" || raw.framework.source.length === 0) {
120
- throw new Error("config.framework.source must be string");
121
- }
122
- if (raw.framework.last_update !== null && typeof raw.framework.last_update !== "string") {
123
- throw new Error("config.framework.last_update must be string or null");
124
- }
125
- if (!isRecord(raw.tasks.verify) || !Array.isArray(raw.tasks.verify.required_tags)) {
126
- throw new Error("config.tasks.verify.required_tags must be array");
127
- }
128
- if (typeof raw.tasks.id_suffix_length_default !== "number" ||
129
- !Number.isInteger(raw.tasks.id_suffix_length_default)) {
130
- throw new Error("config.tasks.id_suffix_length_default must be integer");
131
- }
132
- if (!isRecord(raw.tasks.doc) ||
133
- !Array.isArray(raw.tasks.doc.sections) ||
134
- !Array.isArray(raw.tasks.doc.required_sections)) {
135
- throw new Error("config.tasks.doc.sections and required_sections must be arrays");
136
- }
137
- if (!isRecord(raw.tasks.comments))
138
- throw new Error("config.tasks.comments must be object");
139
- for (const k of ["start", "blocked", "verified"]) {
140
- const policy = raw.tasks.comments[k];
141
- if (!isRecord(policy) ||
142
- typeof policy.prefix !== "string" ||
143
- typeof policy.min_chars !== "number") {
144
- throw new Error(`config.tasks.comments.${k} must have prefix/min_chars`);
145
- }
146
- }
147
- if (!Array.isArray(raw.commit.generic_tokens))
148
- throw new Error("config.commit.generic_tokens must be array");
149
- if (typeof raw.tasks_backend.config_path !== "string" ||
150
- raw.tasks_backend.config_path.length === 0) {
151
- throw new Error("config.tasks_backend.config_path must be string");
152
- }
153
- // At this point, raw satisfies the minimal contract; keep unknown fields by returning it as-is.
154
- return raw;
155
- }
156
- function toErrnoException(err) {
157
- if (!err || typeof err !== "object")
158
- return null;
159
- if (!("code" in err))
160
- return null;
161
- return err;
162
- }
163
- export async function loadConfig(agentplaneDir) {
164
- const filePath = path.join(agentplaneDir, "config.json");
165
- try {
166
- const rawText = await readFile(filePath, "utf8");
167
- const parsed = JSON.parse(rawText);
168
- const validated = validateConfig(parsed);
169
- return {
170
- path: filePath,
171
- exists: true,
172
- config: validated,
173
- raw: parsed,
174
- };
175
- }
176
- catch (err) {
177
- const errno = toErrnoException(err);
178
- if (errno?.code === "ENOENT") {
179
- const def = defaultConfig();
180
- return {
181
- path: filePath,
182
- exists: false,
183
- config: def,
184
- raw: def,
185
- };
186
- }
187
- throw err;
188
- }
189
- }
190
- function parseScalar(value) {
191
- const trimmed = value.trim();
192
- if (trimmed === "true")
193
- return true;
194
- if (trimmed === "false")
195
- return false;
196
- if (trimmed === "null")
197
- return null;
198
- if (trimmed.startsWith("{") || trimmed.startsWith("[")) {
199
- try {
200
- return JSON.parse(trimmed);
201
- }
202
- catch {
203
- return value;
204
- }
205
- }
206
- if (/^-?\d+(?:\.\d+)?$/.test(trimmed))
207
- return Number(trimmed);
208
- return value;
209
- }
210
- export function setByDottedKey(obj, dottedKey, value) {
211
- const parts = dottedKey.split(".").filter(Boolean);
212
- if (parts.length === 0)
213
- throw new Error("config key must be non-empty");
214
- let current = obj;
215
- for (let i = 0; i < parts.length - 1; i++) {
216
- const part = parts[i];
217
- if (!part)
218
- continue;
219
- const next = current[part];
220
- if (!isRecord(next)) {
221
- current[part] = {};
222
- }
223
- current = current[part];
224
- }
225
- const last = parts.at(-1);
226
- if (!last)
227
- throw new Error("config key must be non-empty");
228
- current[last] = parseScalar(value);
229
- }
230
- export async function saveConfig(agentplaneDir, raw) {
231
- const validated = validateConfig(raw);
232
- await mkdir(agentplaneDir, { recursive: true });
233
- const filePath = path.join(agentplaneDir, "config.json");
234
- const text = `${JSON.stringify(raw, null, 2)}\n`;
235
- await writeFile(filePath, text, "utf8");
236
- return validated;
237
- }
@@ -1 +0,0 @@
1
- {"version":3,"file":"git-utils.d.ts","sourceRoot":"","sources":["../src/git-utils.ts"],"names":[],"mappings":"AAeA,wBAAsB,cAAc,CAAC,IAAI,EAAE;IACzC,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAGpB;AAED,wBAAsB,gBAAgB,CAAC,IAAI,EAAE;IAC3C,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAcpB"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"project-root.d.ts","sourceRoot":"","sources":["../src/project-root.ts"],"names":[],"mappings":"AAiBA,wBAAsB,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAU1E;AAED,MAAM,MAAM,qBAAqB,GAAG;IAClC,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,aAAa,EAAE,MAAM,CAAC;CACvB,CAAC;AAEF,wBAAsB,cAAc,CAAC,IAAI,EAAE,qBAAqB,GAAG,OAAO,CAAC,eAAe,CAAC,CAU1F"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"task-readme.d.ts","sourceRoot":"","sources":["../src/task-readme.ts"],"names":[],"mappings":"AAMA,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;AAiED,wBAAgB,qBAAqB,CAAC,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CA8BlF;AAED,wBAAgB,gBAAgB,CAAC,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAE3F"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"task-store.d.ts","sourceRoot":"","sources":["../src/task-store.ts"],"names":[],"mappings":"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"}