@agentplaneorg/core 0.1.4 → 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 (51) hide show
  1. package/dist/commit/commit-policy.d.ts.map +1 -0
  2. package/dist/{config.d.ts → config/config.d.ts} +1 -0
  3. package/dist/config/config.d.ts.map +1 -0
  4. package/dist/config/config.js +124 -0
  5. package/dist/fs/atomic-write.d.ts +2 -0
  6. package/dist/fs/atomic-write.d.ts.map +1 -0
  7. package/dist/fs/atomic-write.js +9 -0
  8. package/dist/git/base-branch.d.ts.map +1 -0
  9. package/dist/{base-branch.js → git/base-branch.js} +1 -1
  10. package/dist/git/git-utils.d.ts.map +1 -0
  11. package/dist/{git-utils.js → git/git-utils.js} +1 -1
  12. package/dist/index.d.ts +12 -9
  13. package/dist/index.d.ts.map +1 -1
  14. package/dist/index.js +12 -9
  15. package/dist/project/project-root.d.ts.map +1 -0
  16. package/dist/tasks/task-doc.d.ts +16 -0
  17. package/dist/tasks/task-doc.d.ts.map +1 -0
  18. package/dist/tasks/task-doc.js +326 -0
  19. package/dist/tasks/task-id.d.ts +9 -0
  20. package/dist/tasks/task-id.d.ts.map +1 -0
  21. package/dist/tasks/task-id.js +28 -0
  22. package/dist/tasks/task-readme.d.ts.map +1 -0
  23. package/dist/{task-store.d.ts → tasks/task-store.d.ts} +2 -0
  24. package/dist/tasks/task-store.d.ts.map +1 -0
  25. package/dist/tasks/task-store.js +212 -0
  26. package/dist/tasks/tasks-export.d.ts.map +1 -0
  27. package/dist/{tasks-export.js → tasks/tasks-export.js} +5 -4
  28. package/dist/{tasks-lint.d.ts → tasks/tasks-lint.d.ts} +1 -1
  29. package/dist/tasks/tasks-lint.d.ts.map +1 -0
  30. package/dist/{tasks-lint.js → tasks/tasks-lint.js} +2 -2
  31. package/package.json +3 -1
  32. package/dist/base-branch.d.ts.map +0 -1
  33. package/dist/commit-policy.d.ts.map +0 -1
  34. package/dist/config.d.ts.map +0 -1
  35. package/dist/config.js +0 -237
  36. package/dist/git-utils.d.ts.map +0 -1
  37. package/dist/project-root.d.ts.map +0 -1
  38. package/dist/task-readme.d.ts.map +0 -1
  39. package/dist/task-store.d.ts.map +0 -1
  40. package/dist/task-store.js +0 -359
  41. package/dist/tasks-export.d.ts.map +0 -1
  42. package/dist/tasks-lint.d.ts.map +0 -1
  43. /package/dist/{commit-policy.d.ts → commit/commit-policy.d.ts} +0 -0
  44. /package/dist/{commit-policy.js → commit/commit-policy.js} +0 -0
  45. /package/dist/{base-branch.d.ts → git/base-branch.d.ts} +0 -0
  46. /package/dist/{git-utils.d.ts → git/git-utils.d.ts} +0 -0
  47. /package/dist/{project-root.d.ts → project/project-root.d.ts} +0 -0
  48. /package/dist/{project-root.js → project/project-root.js} +0 -0
  49. /package/dist/{task-readme.d.ts → tasks/task-readme.d.ts} +0 -0
  50. /package/dist/{task-readme.js → tasks/task-readme.js} +0 -0
  51. /package/dist/{tasks-export.d.ts → tasks/tasks-export.d.ts} +0 -0
@@ -1,359 +0,0 @@
1
- import { mkdir, readdir, readFile, writeFile } from "node:fs/promises";
2
- import path from "node:path";
3
- import { loadConfig } from "./config.js";
4
- import { resolveProject } from "./project-root.js";
5
- import { parseTaskReadme, renderTaskReadme } from "./task-readme.js";
6
- function nowIso() {
7
- return new Date().toISOString();
8
- }
9
- function isRecord(value) {
10
- return !!value && typeof value === "object" && !Array.isArray(value);
11
- }
12
- export function validateTaskDocMetadata(frontmatter) {
13
- const errors = [];
14
- if (frontmatter.doc_version !== 2)
15
- errors.push("doc_version must be 2");
16
- const updatedAt = frontmatter.doc_updated_at;
17
- if (typeof updatedAt !== "string" || Number.isNaN(Date.parse(updatedAt))) {
18
- errors.push("doc_updated_at must be an ISO timestamp");
19
- }
20
- const updatedBy = frontmatter.doc_updated_by;
21
- if (typeof updatedBy !== "string" || updatedBy.trim().length === 0) {
22
- errors.push("doc_updated_by must be a non-empty string");
23
- }
24
- return errors;
25
- }
26
- const ID_ALPHABET = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789";
27
- function randomSuffix(length) {
28
- let out = "";
29
- for (let i = 0; i < length; i++) {
30
- out += ID_ALPHABET[Math.floor(Math.random() * ID_ALPHABET.length)];
31
- }
32
- return out;
33
- }
34
- function timestampIdPrefix(date) {
35
- const yyyy = String(date.getUTCFullYear()).padStart(4, "0");
36
- const mm = String(date.getUTCMonth() + 1).padStart(2, "0");
37
- const dd = String(date.getUTCDate()).padStart(2, "0");
38
- const hh = String(date.getUTCHours()).padStart(2, "0");
39
- const min = String(date.getUTCMinutes()).padStart(2, "0");
40
- return `${yyyy}${mm}${dd}${hh}${min}`;
41
- }
42
- async function fileExists(filePath) {
43
- try {
44
- await readFile(filePath, "utf8");
45
- return true;
46
- }
47
- catch {
48
- return false;
49
- }
50
- }
51
- export async function getTasksDir(opts) {
52
- const resolved = await resolveProject({ cwd: opts.cwd, rootOverride: opts.rootOverride ?? null });
53
- const loaded = await loadConfig(resolved.agentplaneDir);
54
- const tasksDir = path.join(resolved.gitRoot, loaded.config.paths.workflow_dir);
55
- return {
56
- gitRoot: resolved.gitRoot,
57
- tasksDir,
58
- idSuffixLengthDefault: loaded.config.tasks.id_suffix_length_default,
59
- };
60
- }
61
- export function taskReadmePath(tasksDir, taskId) {
62
- return path.join(tasksDir, taskId, "README.md");
63
- }
64
- function defaultTaskBody() {
65
- return [
66
- "## Summary",
67
- "",
68
- "",
69
- "## Scope",
70
- "",
71
- "",
72
- "## Risks",
73
- "",
74
- "",
75
- "## Verify Steps",
76
- "",
77
- "",
78
- "## Rollback Plan",
79
- "",
80
- ].join("\n");
81
- }
82
- function escapeRegExp(text) {
83
- return text.replaceAll(/[.*+?^${}()|[\]\\]/g, String.raw `\\$&`);
84
- }
85
- function setMarkdownSection(body, section, text) {
86
- const lines = body.replaceAll("\r\n", "\n").split("\n");
87
- const headingRe = new RegExp(String.raw `^##\s+${escapeRegExp(section)}\s*$`);
88
- let start = -1;
89
- let nextHeading = lines.length;
90
- for (const [i, line] of lines.entries()) {
91
- if (!line.startsWith("## "))
92
- continue;
93
- if (start === -1) {
94
- if (headingRe.test(line))
95
- start = i;
96
- continue;
97
- }
98
- nextHeading = i;
99
- break;
100
- }
101
- const newTextLines = text.replaceAll("\r\n", "\n").split("\n");
102
- const replacement = ["", ...newTextLines, ""];
103
- if (start === -1) {
104
- const out = [...lines];
105
- if (out.length > 0 && out.at(-1)?.trim() !== "")
106
- out.push("");
107
- out.push(`## ${section}`, ...replacement);
108
- return `${out.join("\n")}\n`;
109
- }
110
- const out = [...lines.slice(0, start + 1), ...replacement, ...lines.slice(nextHeading)];
111
- return `${out.join("\n")}\n`;
112
- }
113
- function normalizeDocSectionName(section) {
114
- return section.trim().replaceAll(/\s+/g, " ").toLowerCase();
115
- }
116
- function getLastCommentAuthor(frontmatter) {
117
- const comments = frontmatter.comments;
118
- if (!Array.isArray(comments))
119
- return null;
120
- const entries = comments;
121
- for (let i = entries.length - 1; i >= 0; i -= 1) {
122
- const entry = entries[i];
123
- if (!isRecord(entry))
124
- continue;
125
- const author = entry.author;
126
- if (typeof author === "string") {
127
- const trimmed = author.trim();
128
- if (trimmed)
129
- return trimmed;
130
- }
131
- }
132
- return null;
133
- }
134
- function resolveDocUpdatedBy(frontmatter, updatedBy) {
135
- if (updatedBy != null) {
136
- const explicit = updatedBy.trim();
137
- if (explicit.length === 0) {
138
- throw new Error("doc_updated_by must be a non-empty string");
139
- }
140
- return explicit;
141
- }
142
- const lastAuthor = getLastCommentAuthor(frontmatter);
143
- if (lastAuthor)
144
- return lastAuthor;
145
- const existing = frontmatter.doc_updated_by;
146
- if (typeof existing === "string") {
147
- const trimmed = existing.trim();
148
- if (trimmed && trimmed.toLowerCase() !== "agentplane")
149
- return trimmed;
150
- }
151
- const owner = frontmatter.owner;
152
- if (typeof owner === "string") {
153
- const trimmed = owner.trim();
154
- if (trimmed)
155
- return trimmed;
156
- }
157
- return "agentplane";
158
- }
159
- function splitCombinedHeadingLines(doc) {
160
- const lines = doc.replaceAll("\r\n", "\n").split("\n");
161
- const out = [];
162
- let inFence = false;
163
- for (const line of lines) {
164
- const trimmed = line.trimStart();
165
- if (trimmed.startsWith("```")) {
166
- inFence = !inFence;
167
- out.push(line);
168
- continue;
169
- }
170
- if (!inFence && line.includes("## ")) {
171
- const matches = [...line.matchAll(/##\s+/g)];
172
- if (matches.length > 1 && matches[0]?.index === 0) {
173
- let start = 0;
174
- for (let i = 1; i < matches.length; i += 1) {
175
- const idx = matches[i]?.index ?? 0;
176
- const chunk = line.slice(start, idx).trimEnd();
177
- if (chunk)
178
- out.push(chunk);
179
- start = idx;
180
- }
181
- const last = line.slice(start).trimEnd();
182
- if (last)
183
- out.push(last);
184
- continue;
185
- }
186
- }
187
- out.push(line);
188
- }
189
- return out;
190
- }
191
- function normalizeDocSections(doc, required) {
192
- const lines = splitCombinedHeadingLines(doc);
193
- const sections = new Map();
194
- const order = [];
195
- const pendingSeparator = new Set();
196
- let currentKey = null;
197
- for (const line of lines) {
198
- const match = /^##\s+(.*)$/.exec(line.trim());
199
- if (match) {
200
- const title = match[1]?.trim() ?? "";
201
- const key = normalizeDocSectionName(title);
202
- if (key) {
203
- const existing = sections.get(key);
204
- if (existing) {
205
- if (existing.lines.some((entry) => entry.trim() !== "")) {
206
- pendingSeparator.add(key);
207
- }
208
- }
209
- else {
210
- sections.set(key, { title, lines: [] });
211
- order.push(key);
212
- }
213
- currentKey = key;
214
- continue;
215
- }
216
- }
217
- if (currentKey) {
218
- const entry = sections.get(currentKey);
219
- if (!entry)
220
- continue;
221
- if (pendingSeparator.has(currentKey) && line.trim() !== "") {
222
- entry.lines.push("");
223
- pendingSeparator.delete(currentKey);
224
- }
225
- entry.lines.push(line);
226
- }
227
- }
228
- const out = [];
229
- const seen = new Set(order);
230
- for (const key of order) {
231
- const section = sections.get(key);
232
- if (!section)
233
- continue;
234
- out.push(`## ${section.title}`);
235
- if (section.lines.length > 0) {
236
- out.push(...section.lines);
237
- }
238
- else {
239
- out.push("");
240
- }
241
- out.push("");
242
- }
243
- for (const requiredSection of required) {
244
- const requiredKey = normalizeDocSectionName(requiredSection);
245
- if (seen.has(requiredKey))
246
- continue;
247
- out.push(`## ${requiredSection}`, "", "");
248
- }
249
- return `${out.join("\n").trimEnd()}\n`;
250
- }
251
- function ensureDocSections(doc, required) {
252
- const trimmed = doc.trim();
253
- if (!trimmed) {
254
- const blocks = required.map((section) => `## ${section}\n`);
255
- return `${blocks.join("\n").trimEnd()}\n`;
256
- }
257
- return normalizeDocSections(doc, required);
258
- }
259
- export async function createTask(opts) {
260
- const { tasksDir, idSuffixLengthDefault } = await getTasksDir({
261
- cwd: opts.cwd,
262
- rootOverride: opts.rootOverride ?? null,
263
- });
264
- await mkdir(tasksDir, { recursive: true });
265
- const suffixLength = idSuffixLengthDefault;
266
- const base = timestampIdPrefix(new Date());
267
- for (let attempt = 0; attempt < 20; attempt++) {
268
- const id = `${base}-${randomSuffix(suffixLength)}`;
269
- const readmePath = taskReadmePath(tasksDir, id);
270
- if (await fileExists(readmePath))
271
- continue;
272
- const frontmatter = {
273
- id,
274
- title: opts.title,
275
- status: "TODO",
276
- priority: opts.priority,
277
- owner: opts.owner,
278
- depends_on: opts.dependsOn,
279
- tags: opts.tags,
280
- verify: opts.verify,
281
- comments: [],
282
- doc_version: 2,
283
- doc_updated_at: nowIso(),
284
- doc_updated_by: opts.owner,
285
- description: opts.description,
286
- };
287
- const body = defaultTaskBody();
288
- const text = renderTaskReadme(frontmatter, body);
289
- await mkdir(path.dirname(readmePath), { recursive: true });
290
- await writeFile(readmePath, text, "utf8");
291
- return { id, readmePath };
292
- }
293
- throw new Error("Failed to generate a unique task id");
294
- }
295
- export async function setTaskDocSection(opts) {
296
- const resolved = await resolveProject({ cwd: opts.cwd, rootOverride: opts.rootOverride ?? null });
297
- const loaded = await loadConfig(resolved.agentplaneDir);
298
- const allowed = loaded.config.tasks.doc.sections;
299
- if (!allowed.includes(opts.section)) {
300
- throw new Error(`Unknown doc section: ${opts.section}`);
301
- }
302
- const tasksDir = path.join(resolved.gitRoot, loaded.config.paths.workflow_dir);
303
- const readmePath = taskReadmePath(tasksDir, opts.taskId);
304
- const original = await readFile(readmePath, "utf8");
305
- const parsed = parseTaskReadme(original);
306
- const updatedBy = resolveDocUpdatedBy(parsed.frontmatter, opts.updatedBy);
307
- const nextFrontmatter = {
308
- ...parsed.frontmatter,
309
- doc_version: 2,
310
- doc_updated_at: nowIso(),
311
- doc_updated_by: updatedBy,
312
- };
313
- const baseDoc = ensureDocSections(parsed.body, loaded.config.tasks.doc.required_sections);
314
- const nextBody = ensureDocSections(setMarkdownSection(baseDoc, opts.section, opts.text), loaded.config.tasks.doc.required_sections);
315
- const nextText = renderTaskReadme(nextFrontmatter, nextBody);
316
- await writeFile(readmePath, nextText, "utf8");
317
- return { readmePath };
318
- }
319
- export async function readTask(opts) {
320
- const { tasksDir } = await getTasksDir({
321
- cwd: opts.cwd,
322
- rootOverride: opts.rootOverride ?? null,
323
- });
324
- const readmePath = taskReadmePath(tasksDir, opts.taskId);
325
- const text = await readFile(readmePath, "utf8");
326
- const parsed = parseTaskReadme(text);
327
- return {
328
- id: opts.taskId,
329
- frontmatter: parsed.frontmatter,
330
- body: parsed.body,
331
- readmePath,
332
- };
333
- }
334
- export async function listTasks(opts) {
335
- const { tasksDir } = await getTasksDir({
336
- cwd: opts.cwd,
337
- rootOverride: opts.rootOverride ?? null,
338
- });
339
- const entries = await readdir(tasksDir, { withFileTypes: true }).catch(() => []);
340
- const ids = entries.filter((e) => e.isDirectory()).map((e) => e.name);
341
- const tasks = [];
342
- for (const id of ids) {
343
- const readmePath = taskReadmePath(tasksDir, id);
344
- try {
345
- const text = await readFile(readmePath, "utf8");
346
- const parsed = parseTaskReadme(text);
347
- tasks.push({
348
- id,
349
- frontmatter: parsed.frontmatter,
350
- body: parsed.body,
351
- readmePath,
352
- });
353
- }
354
- catch {
355
- // Skip unreadable/broken tasks for now; lint will handle this later.
356
- }
357
- }
358
- return tasks.toSorted((a, b) => a.id.localeCompare(b.id));
359
- }
@@ -1 +0,0 @@
1
- {"version":3,"file":"tasks-export.d.ts","sourceRoot":"","sources":["../src/tasks-export.ts"],"names":[],"mappings":"AAYA,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 +0,0 @@
1
- {"version":3,"file":"tasks-lint.d.ts","sourceRoot":"","sources":["../src/tasks-lint.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAGpD,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"}
File without changes
File without changes
File without changes