@agentplaneorg/core 0.1.1 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +65 -2
- package/dist/task-readme.d.ts.map +1 -1
- package/dist/task-readme.js +15 -1
- package/dist/task-store.d.ts.map +1 -1
- package/dist/task-store.js +146 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,5 +1,68 @@
|
|
|
1
1
|
# @agentplaneorg/core
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://www.npmjs.com/package/@agentplaneorg/core)
|
|
4
|
+
[](https://github.com/basilisk-labs/agentplane/blob/main/LICENSE)
|
|
5
|
+
[](https://github.com/basilisk-labs/agentplane/blob/main/docs/user/prerequisites.mdx)
|
|
4
6
|
|
|
5
|
-
|
|
7
|
+
Core utilities and models used by the `agentplane` CLI. This package exposes the reusable building blocks
|
|
8
|
+
for project discovery, config handling, task readme parsing, and task export/linting.
|
|
9
|
+
|
|
10
|
+
If you are an end-user, install the CLI instead: https://www.npmjs.com/package/agentplane
|
|
11
|
+
|
|
12
|
+
## Install
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
npm install @agentplaneorg/core
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Requirements
|
|
19
|
+
|
|
20
|
+
- Node.js >= 20
|
|
21
|
+
- ESM-only (`type: module`)
|
|
22
|
+
|
|
23
|
+
## Usage
|
|
24
|
+
|
|
25
|
+
```ts
|
|
26
|
+
import {
|
|
27
|
+
resolveProject,
|
|
28
|
+
loadConfig,
|
|
29
|
+
listTasks,
|
|
30
|
+
readTask,
|
|
31
|
+
buildTasksExportSnapshot,
|
|
32
|
+
} from "@agentplaneorg/core";
|
|
33
|
+
|
|
34
|
+
const project = await resolveProject(process.cwd());
|
|
35
|
+
const config = await loadConfig(project.root);
|
|
36
|
+
const tasks = await listTasks(project.root);
|
|
37
|
+
const task = await readTask(project.root, tasks[0]?.id ?? "");
|
|
38
|
+
const snapshot = await buildTasksExportSnapshot(project.root);
|
|
39
|
+
|
|
40
|
+
console.log(config.data.workflow_mode, task?.id, snapshot.meta.version);
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Exported Modules
|
|
44
|
+
|
|
45
|
+
| Area | Highlights |
|
|
46
|
+
| ----------------- | -------------------------------------------------------------- |
|
|
47
|
+
| Project discovery | `resolveProject`, `findGitRoot` |
|
|
48
|
+
| Config | `loadConfig`, `saveConfig`, `setByDottedKey`, `validateConfig` |
|
|
49
|
+
| Task README | `parseTaskReadme`, `renderTaskReadme` |
|
|
50
|
+
| Task store | `createTask`, `listTasks`, `readTask`, `setTaskDocSection` |
|
|
51
|
+
| Exports | `buildTasksExportSnapshot`, `writeTasksExport`, checksums |
|
|
52
|
+
| Linting | `lintTasksFile`, `lintTasksSnapshot` |
|
|
53
|
+
| Git | `getStagedFiles`, `getUnstagedFiles`, base branch helpers |
|
|
54
|
+
| Commit policy | `validateCommitSubject`, `extractTaskSuffix` |
|
|
55
|
+
|
|
56
|
+
## Stability
|
|
57
|
+
|
|
58
|
+
This package is versioned alongside the CLI and is primarily used by `agentplane`. The API is stable for
|
|
59
|
+
current use cases, but expect changes as the CLI evolves.
|
|
60
|
+
|
|
61
|
+
## Docs
|
|
62
|
+
|
|
63
|
+
- Repository: https://github.com/basilisk-labs/agentplane
|
|
64
|
+
- Developer docs: https://github.com/basilisk-labs/agentplane/tree/main/docs
|
|
65
|
+
|
|
66
|
+
## License
|
|
67
|
+
|
|
68
|
+
MIT
|
|
@@ -1 +1 @@
|
|
|
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;
|
|
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"}
|
package/dist/task-readme.js
CHANGED
|
@@ -2,11 +2,25 @@ 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 stripLeadingFrontmatterBlocks(body) {
|
|
6
|
+
let next = body.replaceAll("\r\n", "\n");
|
|
7
|
+
while (true) {
|
|
8
|
+
const trimmed = next.replace(/^\n+/, "");
|
|
9
|
+
if (!trimmed.startsWith("---\n"))
|
|
10
|
+
break;
|
|
11
|
+
const end = trimmed.indexOf("\n---\n", 4);
|
|
12
|
+
if (end === -1)
|
|
13
|
+
break;
|
|
14
|
+
next = trimmed.slice(end + 5);
|
|
15
|
+
}
|
|
16
|
+
return next;
|
|
17
|
+
}
|
|
5
18
|
function splitFrontmatter(markdown) {
|
|
6
19
|
const match = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?/.exec(markdown);
|
|
7
20
|
if (!match)
|
|
8
21
|
return { frontmatterText: null, body: markdown };
|
|
9
|
-
|
|
22
|
+
const body = stripLeadingFrontmatterBlocks(markdown.slice(match[0].length));
|
|
23
|
+
return { frontmatterText: match[1] ?? null, body };
|
|
10
24
|
}
|
|
11
25
|
export function parseTaskReadme(markdown) {
|
|
12
26
|
const { frontmatterText, body } = splitFrontmatter(markdown);
|
package/dist/task-store.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"task-store.d.ts","sourceRoot":"","sources":["../src/task-store.ts"],"names":[],"mappings":"AAOA,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,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;
|
|
1
|
+
{"version":3,"file":"task-store.d.ts","sourceRoot":"","sources":["../src/task-store.ts"],"names":[],"mappings":"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"}
|
package/dist/task-store.js
CHANGED
|
@@ -6,6 +6,9 @@ import { parseTaskReadme, renderTaskReadme } from "./task-readme.js";
|
|
|
6
6
|
function nowIso() {
|
|
7
7
|
return new Date().toISOString();
|
|
8
8
|
}
|
|
9
|
+
function isRecord(value) {
|
|
10
|
+
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
11
|
+
}
|
|
9
12
|
export function validateTaskDocMetadata(frontmatter) {
|
|
10
13
|
const errors = [];
|
|
11
14
|
if (frontmatter.doc_version !== 2)
|
|
@@ -107,6 +110,146 @@ function setMarkdownSection(body, section, text) {
|
|
|
107
110
|
const out = [...lines.slice(0, start + 1), ...replacement, ...lines.slice(nextHeading)];
|
|
108
111
|
return `${out.join("\n")}\n`;
|
|
109
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)
|
|
149
|
+
return trimmed;
|
|
150
|
+
}
|
|
151
|
+
return "agentplane";
|
|
152
|
+
}
|
|
153
|
+
function splitCombinedHeadingLines(doc) {
|
|
154
|
+
const lines = doc.replaceAll("\r\n", "\n").split("\n");
|
|
155
|
+
const out = [];
|
|
156
|
+
let inFence = false;
|
|
157
|
+
for (const line of lines) {
|
|
158
|
+
const trimmed = line.trimStart();
|
|
159
|
+
if (trimmed.startsWith("```")) {
|
|
160
|
+
inFence = !inFence;
|
|
161
|
+
out.push(line);
|
|
162
|
+
continue;
|
|
163
|
+
}
|
|
164
|
+
if (!inFence && line.includes("## ")) {
|
|
165
|
+
const matches = [...line.matchAll(/##\s+/g)];
|
|
166
|
+
if (matches.length > 1 && matches[0]?.index === 0) {
|
|
167
|
+
let start = 0;
|
|
168
|
+
for (let i = 1; i < matches.length; i += 1) {
|
|
169
|
+
const idx = matches[i]?.index ?? 0;
|
|
170
|
+
const chunk = line.slice(start, idx).trimEnd();
|
|
171
|
+
if (chunk)
|
|
172
|
+
out.push(chunk);
|
|
173
|
+
start = idx;
|
|
174
|
+
}
|
|
175
|
+
const last = line.slice(start).trimEnd();
|
|
176
|
+
if (last)
|
|
177
|
+
out.push(last);
|
|
178
|
+
continue;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
out.push(line);
|
|
182
|
+
}
|
|
183
|
+
return out;
|
|
184
|
+
}
|
|
185
|
+
function normalizeDocSections(doc, required) {
|
|
186
|
+
const lines = splitCombinedHeadingLines(doc);
|
|
187
|
+
const sections = new Map();
|
|
188
|
+
const order = [];
|
|
189
|
+
const pendingSeparator = new Set();
|
|
190
|
+
let currentKey = null;
|
|
191
|
+
for (const line of lines) {
|
|
192
|
+
const match = /^##\s+(.*)$/.exec(line.trim());
|
|
193
|
+
if (match) {
|
|
194
|
+
const title = match[1]?.trim() ?? "";
|
|
195
|
+
const key = normalizeDocSectionName(title);
|
|
196
|
+
if (key) {
|
|
197
|
+
const existing = sections.get(key);
|
|
198
|
+
if (existing) {
|
|
199
|
+
if (existing.lines.some((entry) => entry.trim() !== "")) {
|
|
200
|
+
pendingSeparator.add(key);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
else {
|
|
204
|
+
sections.set(key, { title, lines: [] });
|
|
205
|
+
order.push(key);
|
|
206
|
+
}
|
|
207
|
+
currentKey = key;
|
|
208
|
+
continue;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
if (currentKey) {
|
|
212
|
+
const entry = sections.get(currentKey);
|
|
213
|
+
if (!entry)
|
|
214
|
+
continue;
|
|
215
|
+
if (pendingSeparator.has(currentKey) && line.trim() !== "") {
|
|
216
|
+
entry.lines.push("");
|
|
217
|
+
pendingSeparator.delete(currentKey);
|
|
218
|
+
}
|
|
219
|
+
entry.lines.push(line);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
const out = [];
|
|
223
|
+
const seen = new Set(order);
|
|
224
|
+
for (const key of order) {
|
|
225
|
+
const section = sections.get(key);
|
|
226
|
+
if (!section)
|
|
227
|
+
continue;
|
|
228
|
+
out.push(`## ${section.title}`);
|
|
229
|
+
if (section.lines.length > 0) {
|
|
230
|
+
out.push(...section.lines);
|
|
231
|
+
}
|
|
232
|
+
else {
|
|
233
|
+
out.push("");
|
|
234
|
+
}
|
|
235
|
+
out.push("");
|
|
236
|
+
}
|
|
237
|
+
for (const requiredSection of required) {
|
|
238
|
+
const requiredKey = normalizeDocSectionName(requiredSection);
|
|
239
|
+
if (seen.has(requiredKey))
|
|
240
|
+
continue;
|
|
241
|
+
out.push(`## ${requiredSection}`, "", "");
|
|
242
|
+
}
|
|
243
|
+
return `${out.join("\n").trimEnd()}\n`;
|
|
244
|
+
}
|
|
245
|
+
function ensureDocSections(doc, required) {
|
|
246
|
+
const trimmed = doc.trim();
|
|
247
|
+
if (!trimmed) {
|
|
248
|
+
const blocks = required.map((section) => `## ${section}\n`);
|
|
249
|
+
return `${blocks.join("\n").trimEnd()}\n`;
|
|
250
|
+
}
|
|
251
|
+
return normalizeDocSections(doc, required);
|
|
252
|
+
}
|
|
110
253
|
export async function createTask(opts) {
|
|
111
254
|
const { tasksDir, idSuffixLengthDefault } = await getTasksDir({
|
|
112
255
|
cwd: opts.cwd,
|
|
@@ -154,16 +297,15 @@ export async function setTaskDocSection(opts) {
|
|
|
154
297
|
const readmePath = taskReadmePath(tasksDir, opts.taskId);
|
|
155
298
|
const original = await readFile(readmePath, "utf8");
|
|
156
299
|
const parsed = parseTaskReadme(original);
|
|
157
|
-
const updatedBy = (opts.updatedBy
|
|
158
|
-
if (updatedBy.length === 0)
|
|
159
|
-
throw new Error("doc_updated_by must be a non-empty string");
|
|
300
|
+
const updatedBy = resolveDocUpdatedBy(parsed.frontmatter, opts.updatedBy);
|
|
160
301
|
const nextFrontmatter = {
|
|
161
302
|
...parsed.frontmatter,
|
|
162
303
|
doc_version: 2,
|
|
163
304
|
doc_updated_at: nowIso(),
|
|
164
305
|
doc_updated_by: updatedBy,
|
|
165
306
|
};
|
|
166
|
-
const
|
|
307
|
+
const baseDoc = ensureDocSections(parsed.body, loaded.config.tasks.doc.required_sections);
|
|
308
|
+
const nextBody = ensureDocSections(setMarkdownSection(baseDoc, opts.section, opts.text), loaded.config.tasks.doc.required_sections);
|
|
167
309
|
const nextText = renderTaskReadme(nextFrontmatter, nextBody);
|
|
168
310
|
await writeFile(readmePath, nextText, "utf8");
|
|
169
311
|
return { readmePath };
|