agentplane 0.1.4 → 0.1.6

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 (85) hide show
  1. package/dist/agents/agents-template.d.ts.map +1 -0
  2. package/dist/{agents-template.js → agents/agents-template.js} +2 -2
  3. package/dist/{task-backend.d.ts → backends/task-backend.d.ts} +5 -3
  4. package/dist/backends/task-backend.d.ts.map +1 -0
  5. package/dist/{task-backend.js → backends/task-backend.js} +102 -268
  6. package/dist/backends/task-index.d.ts +16 -0
  7. package/dist/backends/task-index.d.ts.map +1 -0
  8. package/dist/backends/task-index.js +84 -0
  9. package/dist/cli/archive.d.ts +16 -0
  10. package/dist/cli/archive.d.ts.map +1 -0
  11. package/dist/cli/archive.js +149 -0
  12. package/dist/cli/checksum.d.ts +3 -0
  13. package/dist/cli/checksum.d.ts.map +1 -0
  14. package/dist/cli/checksum.js +12 -0
  15. package/dist/cli/command-guide.d.ts.map +1 -0
  16. package/dist/cli/error-map.d.ts +4 -0
  17. package/dist/cli/error-map.d.ts.map +1 -0
  18. package/dist/cli/error-map.js +42 -0
  19. package/dist/cli/exit-codes.d.ts +3 -0
  20. package/dist/cli/exit-codes.d.ts.map +1 -0
  21. package/dist/cli/exit-codes.js +12 -0
  22. package/dist/cli/help.d.ts.map +1 -0
  23. package/dist/{help.js → cli/help.js} +1 -1
  24. package/dist/cli/http.d.ts +4 -0
  25. package/dist/cli/http.d.ts.map +1 -0
  26. package/dist/cli/http.js +95 -0
  27. package/dist/cli/output.d.ts +16 -0
  28. package/dist/cli/output.d.ts.map +1 -0
  29. package/dist/cli/output.js +47 -0
  30. package/dist/cli/recipes-bundled.js +2 -2
  31. package/dist/cli/run-cli.d.ts.map +1 -0
  32. package/dist/cli/run-cli.js +2681 -0
  33. package/dist/{run-cli.test-helpers.d.ts → cli/run-cli.test-helpers.d.ts} +6 -0
  34. package/dist/cli/run-cli.test-helpers.d.ts.map +1 -0
  35. package/dist/{run-cli.test-helpers.js → cli/run-cli.test-helpers.js} +67 -0
  36. package/dist/cli/update-check.d.ts +31 -0
  37. package/dist/cli/update-check.d.ts.map +1 -0
  38. package/dist/cli/update-check.js +86 -0
  39. package/dist/cli.js +1 -1
  40. package/dist/commands/backend.d.ts +15 -0
  41. package/dist/commands/backend.d.ts.map +1 -0
  42. package/dist/commands/backend.js +211 -0
  43. package/dist/commands/recipes.d.ts +13 -0
  44. package/dist/commands/recipes.d.ts.map +1 -0
  45. package/dist/commands/recipes.js +1919 -0
  46. package/dist/commands/upgrade.d.ts +6 -0
  47. package/dist/commands/upgrade.d.ts.map +1 -0
  48. package/dist/commands/upgrade.js +291 -0
  49. package/dist/commands/workflow.d.ts +367 -0
  50. package/dist/commands/workflow.d.ts.map +1 -0
  51. package/dist/commands/workflow.js +4619 -0
  52. package/dist/meta/version.d.ts.map +1 -0
  53. package/dist/meta/version.js +20 -0
  54. package/dist/recipes/bundled-recipes.d.ts.map +1 -0
  55. package/dist/shared/comment-format.d.ts.map +1 -0
  56. package/dist/shared/env.d.ts.map +1 -0
  57. package/dist/{errors.d.ts → shared/errors.d.ts} +1 -3
  58. package/dist/shared/errors.d.ts.map +1 -0
  59. package/dist/{errors.js → shared/errors.js} +1 -3
  60. package/package.json +6 -2
  61. package/dist/agents-template.d.ts.map +0 -1
  62. package/dist/bundled-recipes.d.ts.map +0 -1
  63. package/dist/command-guide.d.ts.map +0 -1
  64. package/dist/comment-format.d.ts.map +0 -1
  65. package/dist/env.d.ts.map +0 -1
  66. package/dist/errors.d.ts.map +0 -1
  67. package/dist/help.d.ts.map +0 -1
  68. package/dist/run-cli.d.ts.map +0 -1
  69. package/dist/run-cli.js +0 -9454
  70. package/dist/run-cli.test-helpers.d.ts.map +0 -1
  71. package/dist/task-backend.d.ts.map +0 -1
  72. package/dist/version.d.ts.map +0 -1
  73. package/dist/version.js +0 -3
  74. /package/dist/{agents-template.d.ts → agents/agents-template.d.ts} +0 -0
  75. /package/dist/{command-guide.d.ts → cli/command-guide.d.ts} +0 -0
  76. /package/dist/{command-guide.js → cli/command-guide.js} +0 -0
  77. /package/dist/{help.d.ts → cli/help.d.ts} +0 -0
  78. /package/dist/{run-cli.d.ts → cli/run-cli.d.ts} +0 -0
  79. /package/dist/{version.d.ts → meta/version.d.ts} +0 -0
  80. /package/dist/{bundled-recipes.d.ts → recipes/bundled-recipes.d.ts} +0 -0
  81. /package/dist/{bundled-recipes.js → recipes/bundled-recipes.js} +0 -0
  82. /package/dist/{comment-format.d.ts → shared/comment-format.d.ts} +0 -0
  83. /package/dist/{comment-format.js → shared/comment-format.js} +0 -0
  84. /package/dist/{env.d.ts → shared/env.d.ts} +0 -0
  85. /package/dist/{env.js → shared/env.js} +0 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agents-template.d.ts","sourceRoot":"","sources":["../../src/agents/agents-template.ts"],"names":[],"mappings":"AAWA,MAAM,MAAM,YAAY,GAAG,QAAQ,GAAG,WAAW,CAAC;AAElD,KAAK,aAAa,GAAG;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC;AAwC5D,wBAAsB,kBAAkB,IAAI,OAAO,CAAC,MAAM,CAAC,CAG1D;AAED,wBAAsB,kBAAkB,IAAI,OAAO,CAAC,aAAa,EAAE,CAAC,CAanE;AAED,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,YAAY,GAAG,MAAM,CASvF"}
@@ -1,8 +1,8 @@
1
1
  import { readdir, readFile } from "node:fs/promises";
2
2
  import path from "node:path";
3
3
  import { fileURLToPath } from "node:url";
4
- const AGENTS_TEMPLATE_URL = new URL("../assets/AGENTS.md", import.meta.url);
5
- const AGENTS_DIR_URL = new URL("../assets/agents/", import.meta.url);
4
+ const AGENTS_TEMPLATE_URL = new URL("../../assets/AGENTS.md", import.meta.url);
5
+ const AGENTS_DIR_URL = new URL("../../assets/agents/", import.meta.url);
6
6
  const HEADING_RE = /^(#+)\s+(.*)$/;
7
7
  function ensureTrailingNewline(text) {
8
8
  return text.endsWith("\n") ? text : `${text}\n`;
@@ -1,6 +1,9 @@
1
1
  import { type AgentplaneConfig, type ResolvedProject, type TaskRecord } from "@agentplaneorg/core";
2
- export declare function extractTaskDoc(body: string): string;
3
- export declare function mergeTaskDoc(body: string, doc: string): string;
2
+ type ExtractTaskDoc = (body: string) => string;
3
+ type MergeTaskDoc = (body: string, doc: string) => string;
4
+ declare const extractTaskDoc: ExtractTaskDoc;
5
+ declare const mergeTaskDoc: MergeTaskDoc;
6
+ export { extractTaskDoc, mergeTaskDoc };
4
7
  export type TaskData = {
5
8
  id: string;
6
9
  title: string;
@@ -171,5 +174,4 @@ export declare function loadTaskBackend(opts: {
171
174
  config: AgentplaneConfig;
172
175
  backendConfigPath: string;
173
176
  }>;
174
- export {};
175
177
  //# sourceMappingURL=task-backend.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"task-backend.d.ts","sourceRoot":"","sources":["../../src/backends/task-backend.ts"],"names":[],"mappings":"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"}
@@ -1,15 +1,16 @@
1
- import { createHash, randomInt } from "node:crypto";
2
- import { mkdir, readdir, readFile, writeFile } from "node:fs/promises";
1
+ import { createHash } from "node:crypto";
2
+ import { mkdir, readdir, readFile, stat } from "node:fs/promises";
3
3
  import path from "node:path";
4
- import { canonicalizeJson, loadConfig, parseTaskReadme, renderTaskReadme, resolveProject, taskReadmePath, } from "@agentplaneorg/core";
5
- import { loadDotEnv } from "./env.js";
6
- const ID_ALPHABET = "0123456789ABCDEFGHJKMNPQRSTVWXYZ";
7
- const TASK_ID_RE = new RegExp(String.raw `^\d{12}-[${ID_ALPHABET}]{4,}$`);
8
- const DOC_SECTION_HEADER = "## Summary";
9
- const DOC_SECTION_HEADER_RE = /^##\s+Summary(?:\s|$|#)/;
10
- const AUTO_SUMMARY_HEADER = "## Changes Summary (auto)";
4
+ import { atomicWriteFile as atomicWriteFileCore, canonicalizeJson, docChanged, extractTaskDoc as extractTaskDocCore, generateTaskId as generateTaskIdCore, loadConfig, mergeTaskDoc as mergeTaskDocCore, parseTaskReadme, renderTaskReadme, resolveProject, TASK_ID_ALPHABET, taskReadmePath, } from "@agentplaneorg/core";
5
+ import { loadDotEnv } from "../shared/env.js";
6
+ import { buildTaskIndexEntry, loadTaskIndex, resolveTaskIndexPath, saveTaskIndex, } from "./task-index.js";
7
+ const TASK_ID_RE = new RegExp(String.raw `^\d{12}-[${TASK_ID_ALPHABET}]{4,}$`);
11
8
  const DEFAULT_DOC_UPDATED_BY = "agentplane";
12
9
  const DOC_VERSION = 2;
10
+ const atomicWriteFile = atomicWriteFileCore;
11
+ const extractTaskDoc = extractTaskDocCore;
12
+ const mergeTaskDoc = mergeTaskDocCore;
13
+ const generateTaskId = generateTaskIdCore;
13
14
  function nowIso() {
14
15
  return new Date().toISOString();
15
16
  }
@@ -35,145 +36,6 @@ function firstNonEmptyString(...values) {
35
36
  function sleep(ms) {
36
37
  return new Promise((resolve) => setTimeout(resolve, ms));
37
38
  }
38
- function normalizeDoc(text) {
39
- return (text ?? "")
40
- .split("\n")
41
- .map((line) => line.replaceAll(/\s+$/gu, ""))
42
- .join("\n")
43
- .trim();
44
- }
45
- function normalizeDocSectionName(section) {
46
- return section.trim().replaceAll(/\s+/g, " ").toLowerCase();
47
- }
48
- function normalizeSectionLines(lines) {
49
- const trimmedLines = [...lines];
50
- while (trimmedLines.length > 0 && trimmedLines[0]?.trim() === "")
51
- trimmedLines.shift();
52
- while (trimmedLines.length > 0 && trimmedLines.at(-1)?.trim() === "")
53
- trimmedLines.pop();
54
- const out = [];
55
- let inFence = false;
56
- let pendingBlank = false;
57
- for (const line of trimmedLines) {
58
- const fenceCheck = line.trimStart();
59
- if (fenceCheck.startsWith("```")) {
60
- if (pendingBlank) {
61
- out.push("");
62
- pendingBlank = false;
63
- }
64
- out.push(line);
65
- inFence = !inFence;
66
- continue;
67
- }
68
- if (inFence) {
69
- out.push(line);
70
- continue;
71
- }
72
- if (line.trim() === "") {
73
- pendingBlank = true;
74
- continue;
75
- }
76
- if (pendingBlank) {
77
- out.push("");
78
- pendingBlank = false;
79
- }
80
- out.push(line);
81
- }
82
- return out;
83
- }
84
- function splitCombinedHeadingLines(doc) {
85
- const lines = doc.replaceAll("\r\n", "\n").split("\n");
86
- const out = [];
87
- let inFence = false;
88
- for (const line of lines) {
89
- const trimmed = line.trimStart();
90
- if (trimmed.startsWith("```")) {
91
- inFence = !inFence;
92
- out.push(line);
93
- continue;
94
- }
95
- if (!inFence && line.includes("## ")) {
96
- const matches = [...line.matchAll(/##\s+/g)];
97
- if (matches.length > 1 && matches[0]?.index === 0) {
98
- let start = 0;
99
- for (let i = 1; i < matches.length; i += 1) {
100
- const idx = matches[i]?.index ?? 0;
101
- const chunk = line.slice(start, idx).trimEnd();
102
- if (chunk)
103
- out.push(chunk);
104
- start = idx;
105
- }
106
- const last = line.slice(start).trimEnd();
107
- if (last)
108
- out.push(last);
109
- continue;
110
- }
111
- }
112
- out.push(line);
113
- }
114
- return out;
115
- }
116
- function normalizeTaskDoc(doc) {
117
- const normalized = doc.replaceAll("\r\n", "\n");
118
- const trimmed = normalized.replaceAll(/^\n+|\n+$/g, "");
119
- if (!trimmed)
120
- return "";
121
- const lines = splitCombinedHeadingLines(trimmed);
122
- const sections = new Map();
123
- const order = [];
124
- const pendingSeparator = new Set();
125
- let currentKey = null;
126
- for (const line of lines) {
127
- const match = /^##\s+(.*)$/.exec(line.trim());
128
- if (match) {
129
- const title = match[1]?.trim() ?? "";
130
- const key = normalizeDocSectionName(title);
131
- if (key) {
132
- const existing = sections.get(key);
133
- if (existing) {
134
- if (existing.lines.some((entry) => entry.trim() !== "")) {
135
- pendingSeparator.add(key);
136
- }
137
- }
138
- else {
139
- sections.set(key, { title, lines: [] });
140
- order.push(key);
141
- }
142
- currentKey = key;
143
- continue;
144
- }
145
- }
146
- if (currentKey) {
147
- const entry = sections.get(currentKey);
148
- if (!entry)
149
- continue;
150
- if (pendingSeparator.has(currentKey) && line.trim() !== "") {
151
- entry.lines.push("");
152
- pendingSeparator.delete(currentKey);
153
- }
154
- entry.lines.push(line);
155
- }
156
- }
157
- if (order.length === 0)
158
- return trimmed;
159
- const out = [];
160
- for (const key of order) {
161
- const section = sections.get(key);
162
- if (!section)
163
- continue;
164
- const normalizedLines = normalizeSectionLines(section.lines);
165
- if (normalizedLines.length > 0) {
166
- out.push(`## ${section.title}`, "", ...normalizedLines, "");
167
- }
168
- else {
169
- out.push(`## ${section.title}`, "", "");
170
- }
171
- }
172
- return out.join("\n").trimEnd();
173
- }
174
- function docChanged(existing, updated) {
175
- return normalizeDoc(existing) !== normalizeDoc(updated);
176
- }
177
39
  function normalizeUpdatedBy(value) {
178
40
  if (typeof value !== "string")
179
41
  return "";
@@ -243,67 +105,7 @@ function resolveDocUpdatedByFromTask(task, fallback) {
243
105
  const fallbackValue = normalizeUpdatedBy(fallback);
244
106
  return fallbackValue || fallback;
245
107
  }
246
- function isDocSectionHeader(line) {
247
- return DOC_SECTION_HEADER_RE.test(line.trim());
248
- }
249
- export function extractTaskDoc(body) {
250
- if (!body)
251
- return "";
252
- const lines = body.split("\n");
253
- let startIdx = null;
254
- for (const [idx, line] of lines.entries()) {
255
- if (isDocSectionHeader(line)) {
256
- startIdx = idx;
257
- break;
258
- }
259
- }
260
- if (startIdx === null)
261
- return "";
262
- if (lines[startIdx]?.trim() !== DOC_SECTION_HEADER) {
263
- lines[startIdx] = DOC_SECTION_HEADER;
264
- }
265
- let endIdx = lines.length;
266
- for (let idx = startIdx + 1; idx < lines.length; idx++) {
267
- if (lines[idx]?.trim() === AUTO_SUMMARY_HEADER) {
268
- endIdx = idx;
269
- break;
270
- }
271
- }
272
- const doc = lines.slice(startIdx, endIdx).join("\n").trimEnd();
273
- return normalizeTaskDoc(doc);
274
- }
275
- export function mergeTaskDoc(body, doc) {
276
- const docText = normalizeTaskDoc(String(doc ?? ""));
277
- if (docText) {
278
- const lines = body ? body.split("\n") : [];
279
- let prefixIdx = null;
280
- for (const [idx, line] of lines.entries()) {
281
- if (isDocSectionHeader(line)) {
282
- prefixIdx = idx;
283
- break;
284
- }
285
- }
286
- const prefixText = prefixIdx === null ? "" : lines.slice(0, prefixIdx).join("\n").trimEnd();
287
- let autoIdx = null;
288
- for (const [idx, line] of lines.entries()) {
289
- if (line.trim() === AUTO_SUMMARY_HEADER) {
290
- autoIdx = idx;
291
- break;
292
- }
293
- }
294
- const autoBlock = autoIdx === null ? "" : lines.slice(autoIdx).join("\n").trimEnd();
295
- const parts = [];
296
- if (prefixText) {
297
- parts.push(prefixText, "");
298
- }
299
- parts.push(docText.trimEnd());
300
- if (autoBlock) {
301
- parts.push("", autoBlock);
302
- }
303
- return `${parts.join("\n").trimEnd()}\n`;
304
- }
305
- return body;
306
- }
108
+ export { extractTaskDoc, mergeTaskDoc };
307
109
  function validateTaskId(taskId) {
308
110
  if (TASK_ID_RE.test(taskId))
309
111
  return;
@@ -341,6 +143,14 @@ function toStringArray(value) {
341
143
  return [];
342
144
  return value.filter((v) => typeof v === "string");
343
145
  }
146
+ function normalizeDependsOn(value) {
147
+ if (Array.isArray(value)) {
148
+ return value.filter((v) => typeof v === "string" && v.trim() !== "[]");
149
+ }
150
+ if (typeof value === "string" && value.trim() === "[]")
151
+ return [];
152
+ return [];
153
+ }
344
154
  function normalizePriority(value) {
345
155
  const raw = toStringSafe(value).trim().toLowerCase();
346
156
  if (!raw)
@@ -378,7 +188,7 @@ export function taskRecordToData(record) {
378
188
  status: typeof fm.status === "string" ? fm.status : "TODO",
379
189
  priority: typeof fm.priority === "string" || typeof fm.priority === "number" ? fm.priority : "",
380
190
  owner: typeof fm.owner === "string" ? fm.owner : "",
381
- depends_on: toStringArray(fm.depends_on),
191
+ depends_on: normalizeDependsOn(fm.depends_on),
382
192
  tags: toStringArray(fm.tags),
383
193
  verify: toStringArray(fm.verify),
384
194
  commit,
@@ -435,7 +245,7 @@ export function buildTasksExportSnapshotFromTasks(tasks) {
435
245
  export async function writeTasksExportFromTasks(opts) {
436
246
  const snapshot = buildTasksExportSnapshotFromTasks(opts.tasks);
437
247
  await mkdir(path.dirname(opts.outputPath), { recursive: true });
438
- await writeFile(opts.outputPath, `${JSON.stringify(snapshot, null, 2)}\n`, "utf8");
248
+ await atomicWriteFile(opts.outputPath, `${JSON.stringify(snapshot, null, 2)}\n`);
439
249
  }
440
250
  export class LocalBackend {
441
251
  id = "local";
@@ -450,40 +260,64 @@ export class LocalBackend {
450
260
  if (length < 4)
451
261
  throw new Error(invalidLengthMessage(length, 4));
452
262
  const attempts = Math.max(1, opts.attempts);
453
- for (let i = 0; i < attempts; i++) {
454
- const now = new Date();
455
- const yyyy = String(now.getUTCFullYear()).padStart(4, "0");
456
- const mm = String(now.getUTCMonth() + 1).padStart(2, "0");
457
- const dd = String(now.getUTCDate()).padStart(2, "0");
458
- const hh = String(now.getUTCHours()).padStart(2, "0");
459
- const min = String(now.getUTCMinutes()).padStart(2, "0");
460
- let suffix = "";
461
- for (let j = 0; j < length; j++) {
462
- suffix += ID_ALPHABET[randomInt(0, ID_ALPHABET.length)];
463
- }
464
- const taskId = `${yyyy}${mm}${dd}${hh}${min}-${suffix}`;
465
- const readmePath = taskReadmePath(this.root, taskId);
466
- try {
467
- await readFile(readmePath, "utf8");
468
- continue;
469
- }
470
- catch (err) {
471
- const code = err?.code;
472
- if (code === "ENOENT")
473
- return taskId;
474
- throw err;
475
- }
476
- }
477
- throw new Error("Failed to generate a unique task id (exhausted attempts)");
263
+ return await generateTaskId({
264
+ length,
265
+ attempts,
266
+ isAvailable: async (taskId) => {
267
+ const readmePath = taskReadmePath(this.root, taskId);
268
+ try {
269
+ await readFile(readmePath, "utf8");
270
+ return false;
271
+ }
272
+ catch (err) {
273
+ const code = err?.code;
274
+ if (code === "ENOENT")
275
+ return true;
276
+ throw err;
277
+ }
278
+ },
279
+ });
478
280
  }
479
281
  async listTasks() {
480
282
  const tasks = [];
481
283
  const entries = await readdir(this.root, { withFileTypes: true }).catch(() => []);
284
+ const indexPath = resolveTaskIndexPath(this.root);
285
+ const cachedIndex = await loadTaskIndex(indexPath);
286
+ const cachedByPath = new Map();
287
+ if (cachedIndex) {
288
+ for (const entry of cachedIndex.tasks) {
289
+ cachedByPath.set(entry.readmePath, entry);
290
+ }
291
+ }
292
+ const nextIndex = [];
482
293
  const seen = new Set();
483
294
  for (const entry of entries) {
484
295
  if (!entry.isDirectory())
485
296
  continue;
486
297
  const readme = path.join(this.root, entry.name, "README.md");
298
+ let stats;
299
+ try {
300
+ stats = await stat(readme);
301
+ }
302
+ catch {
303
+ continue;
304
+ }
305
+ if (!stats.isFile())
306
+ continue;
307
+ const cached = cachedByPath.get(readme);
308
+ if (cached?.mtimeMs === stats.mtimeMs) {
309
+ const taskId = cached.task.id.trim();
310
+ if (taskId) {
311
+ validateTaskId(taskId);
312
+ if (seen.has(taskId)) {
313
+ throw new Error(`Duplicate task id in local backend: ${taskId}`);
314
+ }
315
+ seen.add(taskId);
316
+ }
317
+ tasks.push(cached.task);
318
+ nextIndex.push(cached);
319
+ continue;
320
+ }
487
321
  let text = "";
488
322
  try {
489
323
  text = await readFile(readme, "utf8");
@@ -516,6 +350,13 @@ export class LocalBackend {
516
350
  readmePath: readme,
517
351
  });
518
352
  tasks.push(task);
353
+ nextIndex.push(buildTaskIndexEntry(task, readme, stats.mtimeMs));
354
+ }
355
+ try {
356
+ await saveTaskIndex(indexPath, { schema_version: 1, tasks: nextIndex });
357
+ }
358
+ catch {
359
+ // Best-effort cache; ignore failures.
519
360
  }
520
361
  return tasks;
521
362
  }
@@ -598,7 +439,7 @@ export class LocalBackend {
598
439
  }
599
440
  await mkdir(path.dirname(readme), { recursive: true });
600
441
  const text = renderTaskReadme(payload, body || "");
601
- await writeFile(readme, text.endsWith("\n") ? text : `${text}\n`, "utf8");
442
+ await atomicWriteFile(readme, text.endsWith("\n") ? text : `${text}\n`);
602
443
  }
603
444
  async setTaskDoc(taskId, doc, updatedBy) {
604
445
  const readme = taskReadmePath(this.root, taskId);
@@ -616,7 +457,7 @@ export class LocalBackend {
616
457
  frontmatter.doc_version = DOC_VERSION;
617
458
  }
618
459
  const next = renderTaskReadme(frontmatter, body);
619
- await writeFile(readme, next.endsWith("\n") ? next : `${next}\n`, "utf8");
460
+ await atomicWriteFile(readme, next.endsWith("\n") ? next : `${next}\n`);
620
461
  }
621
462
  async touchTaskDocMetadata(taskId, updatedBy) {
622
463
  const readme = taskReadmePath(this.root, taskId);
@@ -627,7 +468,7 @@ export class LocalBackend {
627
468
  frontmatter.doc_updated_at = nowIso();
628
469
  frontmatter.doc_updated_by = resolveDocUpdatedByFromFrontmatter(frontmatter, updatedBy, this.updatedBy);
629
470
  const next = renderTaskReadme(frontmatter, parsed.body || "");
630
- await writeFile(readme, next.endsWith("\n") ? next : `${next}\n`, "utf8");
471
+ await atomicWriteFile(readme, next.endsWith("\n") ? next : `${next}\n`);
631
472
  }
632
473
  async writeTasks(tasks) {
633
474
  for (const task of tasks) {
@@ -654,11 +495,11 @@ export class RedmineBackend {
654
495
  issueCache = new Map();
655
496
  reverseStatus = new Map();
656
497
  constructor(settings, opts) {
657
- const envUrl = firstNonEmptyString(process.env.CODEXSWARM_REDMINE_URL);
658
- const envApiKey = firstNonEmptyString(process.env.CODEXSWARM_REDMINE_API_KEY);
659
- const envProjectId = firstNonEmptyString(process.env.CODEXSWARM_REDMINE_PROJECT_ID);
660
- const envAssignee = (process.env.CODEXSWARM_REDMINE_ASSIGNEE_ID ?? "").trim();
661
- const envOwner = firstNonEmptyString(process.env.CODEXSWARM_REDMINE_OWNER, process.env.CODEXSWARM_REDMINE_OWNER_AGENT);
498
+ const envUrl = firstNonEmptyString(process.env.AGENTPLANE_REDMINE_URL);
499
+ const envApiKey = firstNonEmptyString(process.env.AGENTPLANE_REDMINE_API_KEY);
500
+ const envProjectId = firstNonEmptyString(process.env.AGENTPLANE_REDMINE_PROJECT_ID);
501
+ const envAssignee = (process.env.AGENTPLANE_REDMINE_ASSIGNEE_ID ?? "").trim();
502
+ const envOwner = firstNonEmptyString(process.env.AGENTPLANE_REDMINE_OWNER, process.env.AGENTPLANE_REDMINE_OWNER_AGENT);
662
503
  this.baseUrl = firstNonEmptyString(envUrl, settings.url).replaceAll(/\/+$/gu, "");
663
504
  this.apiKey = firstNonEmptyString(envApiKey, settings.api_key);
664
505
  this.projectId = firstNonEmptyString(envProjectId, settings.project_id);
@@ -696,7 +537,11 @@ export class RedmineBackend {
696
537
  const cached = await this.cache.listTasks();
697
538
  existingIds = new Set(cached.map((task) => toStringSafe(task.id)).filter(Boolean));
698
539
  }
699
- return generateTaskId(existingIds, length, attempts);
540
+ return await generateTaskId({
541
+ length,
542
+ attempts,
543
+ isAvailable: (taskId) => !existingIds.has(taskId),
544
+ });
700
545
  }
701
546
  async listTasks() {
702
547
  try {
@@ -1388,26 +1233,6 @@ export class RedmineBackend {
1388
1233
  throw lastError instanceof Error ? lastError : new RedmineUnavailable("Redmine unavailable");
1389
1234
  }
1390
1235
  }
1391
- function generateTaskId(existingIds, length, attempts) {
1392
- if (length < 4)
1393
- throw new Error(invalidLengthMessage(length, 4));
1394
- for (let i = 0; i < attempts; i++) {
1395
- const now = new Date();
1396
- const yyyy = String(now.getUTCFullYear()).padStart(4, "0");
1397
- const mm = String(now.getUTCMonth() + 1).padStart(2, "0");
1398
- const dd = String(now.getUTCDate()).padStart(2, "0");
1399
- const hh = String(now.getUTCHours()).padStart(2, "0");
1400
- const min = String(now.getUTCMinutes()).padStart(2, "0");
1401
- let suffix = "";
1402
- for (let j = 0; j < length; j++) {
1403
- suffix += ID_ALPHABET[randomInt(0, ID_ALPHABET.length)];
1404
- }
1405
- const candidate = `${yyyy}${mm}${dd}${hh}${min}-${suffix}`;
1406
- if (!existingIds.has(candidate))
1407
- return candidate;
1408
- }
1409
- throw new Error("Failed to generate a unique task id (exhausted attempts)");
1410
- }
1411
1236
  async function loadBackendConfig(configPath) {
1412
1237
  try {
1413
1238
  const raw = JSON.parse(await readFile(configPath, "utf8"));
@@ -1428,14 +1253,23 @@ function resolveMaybeRelative(root, input) {
1428
1253
  return null;
1429
1254
  return path.isAbsolute(raw) ? raw : path.join(root, raw);
1430
1255
  }
1256
+ function normalizeBackendConfig(raw) {
1257
+ if (!isRecord(raw)) {
1258
+ return { id: "local", version: 1, settings: {} };
1259
+ }
1260
+ const id = toStringSafe(raw.id).trim() || "local";
1261
+ const version = typeof raw.version === "number" ? raw.version : 1;
1262
+ const settings = isRecord(raw.settings) ? raw.settings : {};
1263
+ return { id, version, settings };
1264
+ }
1431
1265
  export async function loadTaskBackend(opts) {
1432
1266
  const resolved = await resolveProject({ cwd: opts.cwd, rootOverride: opts.rootOverride ?? null });
1433
1267
  const loaded = await loadConfig(resolved.agentplaneDir);
1434
1268
  const backendConfigPath = path.join(resolved.gitRoot, loaded.config.tasks_backend.config_path);
1435
1269
  const backendConfig = await loadBackendConfig(backendConfigPath);
1436
- const backendIdRaw = toStringSafe(backendConfig?.id).trim();
1437
- const backendId = backendIdRaw.length > 0 ? backendIdRaw : "local";
1438
- const settings = isRecord(backendConfig?.settings) ? backendConfig?.settings : {};
1270
+ const normalized = normalizeBackendConfig(backendConfig);
1271
+ const backendId = normalized.id;
1272
+ const settings = normalized.settings;
1439
1273
  if (backendId === "redmine") {
1440
1274
  await loadDotEnv(resolved.gitRoot);
1441
1275
  const cacheDirRaw = resolveMaybeRelative(resolved.gitRoot, settings.cache_dir);
@@ -0,0 +1,16 @@
1
+ import type { TaskData } from "./task-backend.js";
2
+ export declare const TASK_INDEX_SCHEMA_VERSION = 1;
3
+ export type TaskIndexEntry = {
4
+ task: TaskData;
5
+ readmePath: string;
6
+ mtimeMs: number;
7
+ };
8
+ export type TaskIndexFile = {
9
+ schema_version: 1;
10
+ tasks: TaskIndexEntry[];
11
+ };
12
+ export declare function resolveTaskIndexPath(tasksDir: string): string;
13
+ export declare function loadTaskIndex(indexPath: string): Promise<TaskIndexFile | null>;
14
+ export declare function saveTaskIndex(indexPath: string, index: TaskIndexFile): Promise<void>;
15
+ export declare function buildTaskIndexEntry(task: TaskData, readmePath: string, mtimeMs: number): TaskIndexEntry;
16
+ //# sourceMappingURL=task-index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"task-index.d.ts","sourceRoot":"","sources":["../../src/backends/task-index.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAElD,eAAO,MAAM,yBAAyB,IAAI,CAAC;AAG3C,MAAM,MAAM,cAAc,GAAG;IAC3B,IAAI,EAAE,QAAQ,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG;IAC1B,cAAc,EAAE,CAAC,CAAC;IAClB,KAAK,EAAE,cAAc,EAAE,CAAC;CACzB,CAAC;AAEF,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAU7D;AAuBD,wBAAsB,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CAkBpF;AAED,wBAAsB,aAAa,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAM1F;AAED,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,QAAQ,EACd,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,MAAM,GACd,cAAc,CAMhB"}
@@ -0,0 +1,84 @@
1
+ import { mkdir, readFile, rename, writeFile } from "node:fs/promises";
2
+ import path from "node:path";
3
+ export const TASK_INDEX_SCHEMA_VERSION = 1;
4
+ const TASK_INDEX_FILENAME = "tasks-index.v1.json";
5
+ export function resolveTaskIndexPath(tasksDir) {
6
+ const normalized = path.normalize(tasksDir);
7
+ const parts = normalized.split(path.sep);
8
+ const tail = parts.slice(-2).join(path.sep);
9
+ if (tail === path.join(".agentplane", "tasks")) {
10
+ const cacheDir = path.join(path.dirname(tasksDir), "cache");
11
+ return path.join(cacheDir, TASK_INDEX_FILENAME);
12
+ }
13
+ const cacheDir = path.join(tasksDir, ".cache");
14
+ return path.join(cacheDir, TASK_INDEX_FILENAME);
15
+ }
16
+ function isRecord(value) {
17
+ return !!value && typeof value === "object" && !Array.isArray(value);
18
+ }
19
+ function isTaskIndexEntry(value) {
20
+ if (!isRecord(value))
21
+ return false;
22
+ if (!isRecord(value.task))
23
+ return false;
24
+ if (typeof value.readmePath !== "string")
25
+ return false;
26
+ if (typeof value.mtimeMs !== "number")
27
+ return false;
28
+ const task = value.task;
29
+ if (typeof task.id !== "string")
30
+ return false;
31
+ if (typeof task.title !== "string")
32
+ return false;
33
+ if (typeof task.description !== "string")
34
+ return false;
35
+ if (typeof task.status !== "string")
36
+ return false;
37
+ if (typeof task.owner !== "string")
38
+ return false;
39
+ if (!Array.isArray(task.depends_on))
40
+ return false;
41
+ if (!Array.isArray(task.tags))
42
+ return false;
43
+ if (!Array.isArray(task.verify))
44
+ return false;
45
+ return true;
46
+ }
47
+ export async function loadTaskIndex(indexPath) {
48
+ let raw = "";
49
+ try {
50
+ raw = await readFile(indexPath, "utf8");
51
+ }
52
+ catch {
53
+ return null;
54
+ }
55
+ let parsed;
56
+ try {
57
+ parsed = JSON.parse(raw);
58
+ }
59
+ catch {
60
+ return null;
61
+ }
62
+ if (!isRecord(parsed))
63
+ return null;
64
+ if (parsed.schema_version !== TASK_INDEX_SCHEMA_VERSION)
65
+ return null;
66
+ if (!Array.isArray(parsed.tasks))
67
+ return null;
68
+ const tasks = parsed.tasks.filter((entry) => isTaskIndexEntry(entry));
69
+ return { schema_version: TASK_INDEX_SCHEMA_VERSION, tasks };
70
+ }
71
+ export async function saveTaskIndex(indexPath, index) {
72
+ const dir = path.dirname(indexPath);
73
+ await mkdir(dir, { recursive: true });
74
+ const tmpPath = `${indexPath}.tmp-${process.pid}`;
75
+ await writeFile(tmpPath, `${JSON.stringify(index, null, 2)}\n`, "utf8");
76
+ await rename(tmpPath, indexPath);
77
+ }
78
+ export function buildTaskIndexEntry(task, readmePath, mtimeMs) {
79
+ return {
80
+ task,
81
+ readmePath,
82
+ mtimeMs,
83
+ };
84
+ }
@@ -0,0 +1,16 @@
1
+ type ArchiveType = "tar" | "zip";
2
+ export type ArchiveEntryIssue = {
3
+ entry: string;
4
+ reason: string;
5
+ };
6
+ export declare function validateArchive(archivePath: string, type: ArchiveType): Promise<ArchiveEntryIssue[]>;
7
+ export declare function detectArchiveType(filePath: string): ArchiveType | null;
8
+ export declare function extractArchive(opts: {
9
+ archivePath: string;
10
+ destDir: string;
11
+ usage: string;
12
+ example?: string;
13
+ }): Promise<void>;
14
+ export declare function validateArchiveEntries(entries: string[], symlinks: string[]): ArchiveEntryIssue[];
15
+ export {};
16
+ //# sourceMappingURL=archive.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"archive.d.ts","sourceRoot":"","sources":["../../src/cli/archive.ts"],"names":[],"mappings":"AAYA,KAAK,WAAW,GAAG,KAAK,GAAG,KAAK,CAAC;AAOjC,MAAM,MAAM,iBAAiB,GAAG;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,wBAAsB,eAAe,CACnC,WAAW,EAAE,MAAM,EACnB,IAAI,EAAE,WAAW,GAChB,OAAO,CAAC,iBAAiB,EAAE,CAAC,CAU9B;AAED,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI,CAKtE;AAED,wBAAsB,cAAc,CAAC,IAAI,EAAE;IACzC,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,GAAG,OAAO,CAAC,IAAI,CAAC,CAwBhB;AAED,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,iBAAiB,EAAE,CA2BjG"}