toolcraft 0.0.11 → 0.0.13

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 (56) hide show
  1. package/dist/cli.js +274 -160
  2. package/dist/renderer.d.ts +8 -2
  3. package/dist/renderer.js +71 -12
  4. package/node_modules/@poe-code/design-system/dist/components/help-formatter-plain.d.ts +4 -0
  5. package/node_modules/@poe-code/design-system/dist/components/help-formatter-plain.js +132 -0
  6. package/node_modules/@poe-code/design-system/dist/components/help-formatter.d.ts +13 -0
  7. package/node_modules/@poe-code/design-system/dist/components/help-formatter.js +116 -7
  8. package/node_modules/@poe-code/design-system/dist/components/index.d.ts +2 -2
  9. package/node_modules/@poe-code/design-system/dist/components/index.js +1 -1
  10. package/node_modules/@poe-code/design-system/dist/components/text.d.ts +1 -0
  11. package/node_modules/@poe-code/design-system/dist/components/text.js +8 -0
  12. package/node_modules/@poe-code/design-system/dist/index.d.ts +3 -2
  13. package/node_modules/@poe-code/design-system/dist/index.js +2 -1
  14. package/node_modules/@poe-code/process-runner/README.md +41 -0
  15. package/node_modules/@poe-code/process-runner/dist/docker/args.d.ts +2 -0
  16. package/node_modules/@poe-code/process-runner/dist/docker/args.js +40 -0
  17. package/node_modules/@poe-code/process-runner/dist/docker/context.d.ts +3 -0
  18. package/node_modules/@poe-code/process-runner/dist/docker/context.js +30 -0
  19. package/node_modules/@poe-code/process-runner/dist/docker/docker-execution-env.d.ts +28 -0
  20. package/node_modules/@poe-code/process-runner/dist/docker/docker-execution-env.js +428 -0
  21. package/node_modules/@poe-code/process-runner/dist/docker/docker-runner.d.ts +2 -0
  22. package/node_modules/@poe-code/process-runner/dist/docker/docker-runner.js +131 -0
  23. package/node_modules/@poe-code/process-runner/dist/docker/engine.d.ts +3 -0
  24. package/node_modules/@poe-code/process-runner/dist/docker/engine.js +24 -0
  25. package/node_modules/@poe-code/process-runner/dist/host/host-execution-env.d.ts +2 -0
  26. package/node_modules/@poe-code/process-runner/dist/host/host-execution-env.js +48 -0
  27. package/node_modules/@poe-code/process-runner/dist/host/host-runner.d.ts +3 -0
  28. package/node_modules/@poe-code/process-runner/dist/host/host-runner.js +74 -0
  29. package/node_modules/@poe-code/process-runner/dist/index.d.ts +8 -0
  30. package/node_modules/@poe-code/process-runner/dist/index.js +7 -0
  31. package/node_modules/@poe-code/process-runner/dist/testing/index.d.ts +2 -0
  32. package/node_modules/@poe-code/process-runner/dist/testing/index.js +1 -0
  33. package/node_modules/@poe-code/process-runner/dist/testing/mock-runner.d.ts +3 -0
  34. package/node_modules/@poe-code/process-runner/dist/testing/mock-runner.js +115 -0
  35. package/node_modules/@poe-code/process-runner/dist/testing/verify.d.ts +1 -0
  36. package/node_modules/@poe-code/process-runner/dist/testing/verify.js +359 -0
  37. package/node_modules/@poe-code/process-runner/dist/types.d.ts +180 -0
  38. package/node_modules/@poe-code/process-runner/dist/types.js +1 -0
  39. package/node_modules/@poe-code/process-runner/package.json +27 -0
  40. package/node_modules/@poe-code/task-list/README.md +49 -5
  41. package/node_modules/@poe-code/task-list/dist/backends/gh-issues-client.d.ts +19 -0
  42. package/node_modules/@poe-code/task-list/dist/backends/gh-issues-client.js +62 -0
  43. package/node_modules/@poe-code/task-list/dist/backends/gh-issues.d.ts +13 -0
  44. package/node_modules/@poe-code/task-list/dist/backends/gh-issues.js +627 -0
  45. package/node_modules/@poe-code/task-list/dist/backends/markdown-dir.js +253 -41
  46. package/node_modules/@poe-code/task-list/dist/backends/utils.d.ts +7 -1
  47. package/node_modules/@poe-code/task-list/dist/backends/utils.js +21 -0
  48. package/node_modules/@poe-code/task-list/dist/backends/yaml-file.js +171 -16
  49. package/node_modules/@poe-code/task-list/dist/index.d.ts +3 -1
  50. package/node_modules/@poe-code/task-list/dist/index.js +1 -1
  51. package/node_modules/@poe-code/task-list/dist/open.d.ts +4 -2
  52. package/node_modules/@poe-code/task-list/dist/open.js +27 -3
  53. package/node_modules/@poe-code/task-list/dist/types.d.ts +51 -3
  54. package/node_modules/@poe-code/task-list/dist/types.js +25 -0
  55. package/node_modules/@poe-code/task-list/package.json +1 -0
  56. package/package.json +11 -4
@@ -1,18 +1,26 @@
1
1
  import { acquireFileLock } from "@poe-code/file-lock";
2
- import { parseDocument } from "yaml";
2
+ import { isMap, parseDocument } from "yaml";
3
3
  import storeSchema from "../schema/store.schema.json" with { type: "json" };
4
4
  import taskSchema from "../schema/task.schema.json" with { type: "json" };
5
5
  import { eventsFromState, findEvent } from "../state-machine.js";
6
6
  import { resolveStateMachine } from "../state.js";
7
- import { InvalidTransitionError, MalformedTaskError, TaskAlreadyExistsError, TaskNotFoundError } from "../types.js";
8
- import { isRecord, sortStrings, sortTasks, statIfExists, validateTaskId, writeAtomically } from "./utils.js";
7
+ import { AnchorNotFoundError, InvalidTransitionError, MalformedTaskError, OrderMismatchError, TaskAlreadyExistsError, TaskNotFoundError } from "../types.js";
8
+ import { applyOrder, isRecord, sortStrings, statIfExists, validateTaskId, writeAtomically } from "./utils.js";
9
9
  const STORE_KIND = "task-store";
10
10
  const STORE_SCHEMA_ID = storeSchema.$id;
11
11
  const STORE_VERSION = 1;
12
12
  const TASK_KIND = "task";
13
13
  const TASK_SCHEMA_ID = taskSchema.$id;
14
14
  const TASK_VERSION = 1;
15
- const RESERVED_TASK_KEYS = new Set(["$schema", "description", "kind", "name", "state", "version"]);
15
+ const RESERVED_TASK_KEYS = new Set([
16
+ "$schema",
17
+ "created",
18
+ "description",
19
+ "kind",
20
+ "name",
21
+ "state",
22
+ "version"
23
+ ]);
16
24
  function malformedStore(filePath, field) {
17
25
  return new MalformedTaskError(`Malformed task store "${filePath}": invalid "${field}".`);
18
26
  }
@@ -89,6 +97,7 @@ function createTaskRecord(defaults, input, initialState) {
89
97
  taskRecord[key] = value;
90
98
  }
91
99
  }
100
+ taskRecord.created = new Date().toISOString();
92
101
  return taskRecord;
93
102
  }
94
103
  function assertCreateDoesNotSetState(input) {
@@ -96,6 +105,11 @@ function assertCreateDoesNotSetState(input) {
96
105
  throw new Error('Tasks.create() does not accept "state"; new tasks always start at stateMachine.initial.');
97
106
  }
98
107
  }
108
+ function assertCreateHasId(input) {
109
+ if (input.id === undefined) {
110
+ throw new Error("id is required for yaml-file backend");
111
+ }
112
+ }
99
113
  function assertUpdateDoesNotSetState(patch) {
100
114
  if (Object.prototype.hasOwnProperty.call(patch, "state")) {
101
115
  throw new Error('Tasks.update() does not accept "state"; use fire() to change task state.');
@@ -248,6 +262,50 @@ function getTaskOrThrow(store, list, id) {
248
262
  }
249
263
  return taskRecord;
250
264
  }
265
+ function getListNode(document, list) {
266
+ const lists = document.get("lists");
267
+ if (!isMap(lists)) {
268
+ return undefined;
269
+ }
270
+ const listNode = lists.get(list);
271
+ if (!isMap(listNode)) {
272
+ return undefined;
273
+ }
274
+ return listNode;
275
+ }
276
+ function pairKey(pair) {
277
+ const key = pair.key;
278
+ if (typeof key === "string") {
279
+ return key;
280
+ }
281
+ if (key && typeof key === "object" && "value" in key && typeof key.value === "string") {
282
+ return key.value;
283
+ }
284
+ return undefined;
285
+ }
286
+ function findItemIndex(listNode, id) {
287
+ return listNode.items.findIndex((pair) => pairKey(pair) === id);
288
+ }
289
+ function activeItemIds(listNode, validStates) {
290
+ const ids = [];
291
+ for (const pair of listNode.items) {
292
+ const id = pairKey(pair);
293
+ if (id === undefined)
294
+ continue;
295
+ const value = pair.value;
296
+ let state;
297
+ if (value && typeof value === "object" && "get" in value && typeof value.get === "function") {
298
+ state = value.get("state");
299
+ }
300
+ else if (isRecord(value)) {
301
+ state = value.state;
302
+ }
303
+ if (typeof state === "string" && validStates.has(state) && state !== "archived") {
304
+ ids.push(id);
305
+ }
306
+ }
307
+ return ids;
308
+ }
251
309
  async function ensureStorePath(deps) {
252
310
  if (!deps.create) {
253
311
  await deps.fs.stat(deps.path);
@@ -287,8 +345,13 @@ function createTasksView(deps, list) {
287
345
  if (!listRecord) {
288
346
  return [];
289
347
  }
290
- const tasks = Object.entries(listRecord).map(([id, taskRecord]) => createTask(list, id, taskRecord));
291
- return sortTasks(tasks.filter((task) => matchesFilter(task, filter)));
348
+ const entries = Object.entries(listRecord)
349
+ .map(([id, taskRecord]) => ({
350
+ task: createTask(list, id, taskRecord),
351
+ raw: taskRecord
352
+ }))
353
+ .filter(({ task }) => matchesFilter(task, filter));
354
+ return applyOrder(entries, filter?.order);
292
355
  }
293
356
  function assertFireableTaskEvent(task, eventName) {
294
357
  const event = findEvent(stateMachine, task.state, eventName);
@@ -315,6 +378,7 @@ function createTasksView(deps, list) {
315
378
  },
316
379
  async create(input) {
317
380
  assertCreateDoesNotSetState(input);
381
+ assertCreateHasId(input);
318
382
  validateTaskId(input.id);
319
383
  return withStoreLock(deps, async () => {
320
384
  const { document, store } = await readStore(deps.fs, deps.path, validStates);
@@ -403,6 +467,73 @@ function createTasksView(deps, list) {
403
467
  document.deleteIn(["lists", list, id]);
404
468
  await writeAtomically(deps.fs, deps.path, serializeDocument(document));
405
469
  });
470
+ },
471
+ async move(id, anchor) {
472
+ validateTaskId(id);
473
+ return withStoreLock(deps, async () => {
474
+ const { document, store } = await readStore(deps.fs, deps.path, validStates);
475
+ const taskRecord = getTaskOrThrow(store, list, id);
476
+ const listNode = getListNode(document, list);
477
+ if (!listNode) {
478
+ throw new TaskNotFoundError(`Task "${list}/${id}" not found.`);
479
+ }
480
+ const fromIndex = findItemIndex(listNode, id);
481
+ if (fromIndex < 0) {
482
+ throw new TaskNotFoundError(`Task "${list}/${id}" not found.`);
483
+ }
484
+ const [movedPair] = listNode.items.splice(fromIndex, 1);
485
+ let insertIndex;
486
+ if ("position" in anchor) {
487
+ insertIndex = anchor.position === "top" ? 0 : listNode.items.length;
488
+ }
489
+ else {
490
+ const anchorId = "before" in anchor ? anchor.before : anchor.after;
491
+ const anchorIndex = findItemIndex(listNode, anchorId);
492
+ if (anchorIndex < 0) {
493
+ listNode.items.splice(fromIndex, 0, movedPair);
494
+ throw new AnchorNotFoundError(anchorId);
495
+ }
496
+ insertIndex = "before" in anchor ? anchorIndex : anchorIndex + 1;
497
+ }
498
+ listNode.items.splice(insertIndex, 0, movedPair);
499
+ await writeAtomically(deps.fs, deps.path, serializeDocument(document));
500
+ return createTask(list, id, taskRecord);
501
+ });
502
+ },
503
+ async reorder(ids) {
504
+ for (const id of ids) {
505
+ validateTaskId(id);
506
+ }
507
+ return withStoreLock(deps, async () => {
508
+ const { document, store } = await readStore(deps.fs, deps.path, validStates);
509
+ const listNode = getListNode(document, list);
510
+ if (!listNode) {
511
+ throw new OrderMismatchError({ missing: [...ids], extra: [] });
512
+ }
513
+ const currentActive = activeItemIds(listNode, validStates);
514
+ const currentSet = new Set(currentActive);
515
+ const inputSet = new Set(ids);
516
+ const missing = currentActive.filter((id) => !inputSet.has(id));
517
+ const extra = ids.filter((id) => !currentSet.has(id));
518
+ if (missing.length > 0 || extra.length > 0) {
519
+ throw new OrderMismatchError({ missing, extra });
520
+ }
521
+ const archivedPairs = listNode.items.filter((pair) => {
522
+ const id = pairKey(pair);
523
+ return id !== undefined && !currentSet.has(id);
524
+ });
525
+ const orderedActive = ids.map((id) => {
526
+ const pair = listNode.items.find((p) => pairKey(p) === id);
527
+ if (!pair) {
528
+ throw new OrderMismatchError({ missing: [id], extra: [] });
529
+ }
530
+ return pair;
531
+ });
532
+ listNode.items.splice(0, listNode.items.length, ...orderedActive, ...archivedPairs);
533
+ await writeAtomically(deps.fs, deps.path, serializeDocument(document));
534
+ const tasks = ids.map((id) => createTask(list, id, getTaskOrThrow(store, list, id)));
535
+ return tasks;
536
+ });
406
537
  }
407
538
  };
408
539
  }
@@ -420,25 +551,49 @@ export async function yamlFileBackend(deps) {
420
551
  };
421
552
  const allTasks = async (filter) => {
422
553
  const { store } = await readStore(deps.fs, deps.path, validStates);
423
- const tasks = [];
424
- for (const [listName, listRecord] of Object.entries(getListsRecord(store))) {
425
- for (const [id, taskRecord] of Object.entries(listRecord)) {
426
- const task = createTask(listName, id, taskRecord);
427
- if (matchesFilter(task, filter)) {
428
- tasks.push(task);
429
- }
430
- }
554
+ const result = [];
555
+ const listNames = sortStrings(Object.keys(getListsRecord(store)));
556
+ for (const listName of listNames) {
557
+ const listRecord = getListsRecord(store)[listName];
558
+ if (!isRecord(listRecord))
559
+ continue;
560
+ const entries = Object.entries(listRecord)
561
+ .map(([id, taskRecord]) => ({
562
+ task: createTask(listName, id, taskRecord),
563
+ raw: taskRecord
564
+ }))
565
+ .filter(({ task }) => matchesFilter(task, filter));
566
+ result.push(...applyOrder(entries, filter?.order));
431
567
  }
432
- return sortTasks(tasks);
568
+ return result;
433
569
  };
434
570
  const get = async (qualifiedId) => {
435
571
  const { list: listName, id } = parseQualifiedId(qualifiedId);
436
572
  return list(listName).get(id);
437
573
  };
574
+ const moveBetweenLists = async (qualifiedId, targetList) => {
575
+ const { list: sourceListName, id } = parseQualifiedId(qualifiedId);
576
+ const targetListName = validateListName(targetList);
577
+ return withStoreLock(deps, async () => {
578
+ const { document, store } = await readStore(deps.fs, deps.path, validStates);
579
+ const taskRecord = getTaskOrThrow(store, sourceListName, id);
580
+ if (sourceListName === targetListName) {
581
+ return createTask(targetListName, id, taskRecord);
582
+ }
583
+ if (getTaskRecord(store, targetListName, id)) {
584
+ throw new TaskAlreadyExistsError(`Task "${targetListName}/${id}" already exists.`);
585
+ }
586
+ document.deleteIn(["lists", sourceListName, id]);
587
+ document.setIn(["lists", targetListName, id], taskRecord);
588
+ await writeAtomically(deps.fs, deps.path, serializeDocument(document));
589
+ return createTask(targetListName, id, taskRecord);
590
+ });
591
+ };
438
592
  return {
439
593
  list,
440
594
  lists,
441
595
  allTasks,
442
- get
596
+ get,
597
+ moveBetweenLists
443
598
  };
444
599
  }
@@ -1,4 +1,6 @@
1
1
  export { openTaskList } from "./open.js";
2
2
  export { assertEvent, assertTransition, defaultStateMachine, type TaskEvent } from "./state.js";
3
3
  export { eventsFromState, findEvent, validateMachine, type EventDef, type StateMachineDef } from "./state-machine.js";
4
- export { InvalidTransitionError, MalformedTaskError, TaskAlreadyExistsError, TaskNotFoundError, type ListFilter, type OpenTaskListOptions, type Task, type TaskCreate, type TaskDefaults, type TaskFireOptions, type TaskList, type TaskListFs, type TaskState, type Tasks, type TaskUpdate } from "./types.js";
4
+ export { AnchorNotFoundError, InvalidTransitionError, MalformedTaskError, OrderMismatchError, TaskAlreadyExistsError, TaskNotFoundError, type ListFilter, type MoveAnchor, type OpenGhIssuesOptions, type OpenMarkdownDirOptions, type OpenTaskListOptions, type OpenYamlFileOptions, type Task, type TaskCreate, type TaskDefaults, type TaskFireOptions, type TaskList, type TaskListFs, type TaskOrder, type TaskState, type Tasks, type TaskUpdate } from "./types.js";
5
+ export type { GhClient, GhClientOptions, ResolveAuthOptions, ResolveEndpointOptions } from "./backends/gh-issues-client.js";
6
+ export type { GhIssuesBackendDeps } from "./backends/gh-issues.js";
@@ -1,4 +1,4 @@
1
1
  export { openTaskList } from "./open.js";
2
2
  export { assertEvent, assertTransition, defaultStateMachine } from "./state.js";
3
3
  export { eventsFromState, findEvent, validateMachine } from "./state-machine.js";
4
- export { InvalidTransitionError, MalformedTaskError, TaskAlreadyExistsError, TaskNotFoundError } from "./types.js";
4
+ export { AnchorNotFoundError, InvalidTransitionError, MalformedTaskError, OrderMismatchError, TaskAlreadyExistsError, TaskNotFoundError } from "./types.js";
@@ -1,3 +1,5 @@
1
- import type { BackendFactory, OpenTaskListOptions, TaskList } from "./types.js";
2
- export declare const backendFactories: Record<OpenTaskListOptions["type"], BackendFactory>;
1
+ import type { BackendFactory, OpenMarkdownDirOptions, OpenTaskListOptions, OpenYamlFileOptions, TaskList } from "./types.js";
2
+ type FileBackendOptions = OpenMarkdownDirOptions | OpenYamlFileOptions;
3
+ export declare const backendFactories: Record<FileBackendOptions["type"], BackendFactory>;
3
4
  export declare function openTaskList(options: OpenTaskListOptions): Promise<TaskList>;
5
+ export {};
@@ -1,4 +1,6 @@
1
1
  import * as fsPromises from "node:fs/promises";
2
+ import { ghIssuesBackend } from "./backends/gh-issues.js";
3
+ import { resolveAuth, resolveEndpoint } from "./backends/gh-issues-client.js";
2
4
  import { markdownDirBackend } from "./backends/markdown-dir.js";
3
5
  import { yamlFileBackend } from "./backends/yaml-file.js";
4
6
  import { validateMachine } from "./state-machine.js";
@@ -13,10 +15,18 @@ function createDefaultFs() {
13
15
  return fsPromises;
14
16
  }
15
17
  export async function openTaskList(options) {
16
- const factory = backendFactories[options.type];
17
- if (factory === undefined) {
18
- throw new Error(`Unknown task list backend type "${options.type}".`);
18
+ switch (options.type) {
19
+ case "markdown-dir":
20
+ case "yaml-file":
21
+ return openFileBackend(options);
22
+ case "gh-issues":
23
+ return openGhIssuesBackend(options);
24
+ default:
25
+ throw new Error(`Unknown task list backend type "${options.type}".`);
19
26
  }
27
+ }
28
+ async function openFileBackend(options) {
29
+ const factory = backendFactories[options.type];
20
30
  const stateMachine = resolveStateMachine(options.stateMachine);
21
31
  validateMachine(stateMachine);
22
32
  const deps = {
@@ -32,3 +42,17 @@ export async function openTaskList(options) {
32
42
  };
33
43
  return factory(deps);
34
44
  }
45
+ async function openGhIssuesBackend(options) {
46
+ const token = await resolveAuth({ explicitToken: options.auth?.token });
47
+ const endpoint = resolveEndpoint();
48
+ return ghIssuesBackend({
49
+ repo: options.repo,
50
+ project: options.project,
51
+ defaults: {
52
+ metadata: { ...(options.defaults?.metadata ?? {}) }
53
+ },
54
+ token,
55
+ endpoint,
56
+ fetch: options.fetch
57
+ });
58
+ }
@@ -10,7 +10,7 @@ export interface Task {
10
10
  metadata: Record<string, unknown>;
11
11
  }
12
12
  export interface TaskCreate {
13
- id: string;
13
+ id?: string;
14
14
  name: string;
15
15
  description?: string;
16
16
  metadata?: Record<string, unknown>;
@@ -24,10 +24,19 @@ export interface TaskUpdate {
24
24
  export interface TaskFireOptions {
25
25
  metadataPatch?: Record<string, unknown>;
26
26
  }
27
+ export type TaskOrder = "priority" | "alphabetical" | "created";
27
28
  export interface ListFilter {
28
29
  state?: string;
29
30
  includeArchived?: boolean;
31
+ order?: TaskOrder;
30
32
  }
33
+ export type MoveAnchor = {
34
+ before: string;
35
+ } | {
36
+ after: string;
37
+ } | {
38
+ position: "top" | "bottom";
39
+ };
31
40
  export interface Tasks {
32
41
  readonly name: string;
33
42
  readonly stateMachine: StateMachineDef;
@@ -39,12 +48,15 @@ export interface Tasks {
39
48
  canFire(id: string, event: string): Promise<boolean>;
40
49
  events(id: string): Promise<readonly string[]>;
41
50
  delete(id: string): Promise<void>;
51
+ move(id: string, anchor: MoveAnchor): Promise<Task>;
52
+ reorder(ids: readonly string[]): Promise<readonly Task[]>;
42
53
  }
43
54
  export interface TaskList {
44
55
  list(name: string): Tasks;
45
56
  lists(): Promise<string[]>;
46
57
  allTasks(filter?: ListFilter): Promise<Task[]>;
47
58
  get(qualifiedId: string): Promise<Task>;
59
+ moveBetweenLists(qualifiedId: string, targetList: string): Promise<Task>;
48
60
  }
49
61
  export interface TaskDefaults {
50
62
  metadata?: Record<string, unknown>;
@@ -73,8 +85,19 @@ export interface TaskListFs {
73
85
  flag?: string;
74
86
  }): Promise<void>;
75
87
  }
76
- export interface OpenTaskListOptions {
77
- type: "markdown-dir" | "yaml-file";
88
+ export type OpenTaskListOptions = OpenMarkdownDirOptions | OpenYamlFileOptions | OpenGhIssuesOptions;
89
+ export interface OpenMarkdownDirOptions {
90
+ type: "markdown-dir";
91
+ path: string;
92
+ defaults?: TaskDefaults;
93
+ create?: boolean;
94
+ lockStaleMs?: number;
95
+ lockRetries?: number;
96
+ fs?: TaskListFs;
97
+ stateMachine?: StateMachineDef;
98
+ }
99
+ export interface OpenYamlFileOptions {
100
+ type: "yaml-file";
78
101
  path: string;
79
102
  defaults?: TaskDefaults;
80
103
  create?: boolean;
@@ -83,6 +106,19 @@ export interface OpenTaskListOptions {
83
106
  fs?: TaskListFs;
84
107
  stateMachine?: StateMachineDef;
85
108
  }
109
+ export interface OpenGhIssuesOptions {
110
+ type: "gh-issues";
111
+ repo: string;
112
+ project: {
113
+ owner: string;
114
+ number: number;
115
+ };
116
+ defaults?: TaskDefaults;
117
+ auth?: {
118
+ token: string;
119
+ };
120
+ fetch?: typeof fetch;
121
+ }
86
122
  export interface BackendDeps {
87
123
  path: string;
88
124
  defaults: Required<TaskDefaults>;
@@ -114,3 +150,15 @@ export declare class InvalidTransitionError extends Error {
114
150
  export declare class MalformedTaskError extends Error {
115
151
  constructor(message?: string);
116
152
  }
153
+ export declare class OrderMismatchError extends Error {
154
+ readonly missing: readonly string[];
155
+ readonly extra: readonly string[];
156
+ constructor(options: {
157
+ missing: readonly string[];
158
+ extra: readonly string[];
159
+ });
160
+ }
161
+ export declare class AnchorNotFoundError extends Error {
162
+ readonly anchor: string;
163
+ constructor(anchor: string);
164
+ }
@@ -35,3 +35,28 @@ export class MalformedTaskError extends Error {
35
35
  this.name = "MalformedTaskError";
36
36
  }
37
37
  }
38
+ export class OrderMismatchError extends Error {
39
+ missing;
40
+ extra;
41
+ constructor(options) {
42
+ const parts = [];
43
+ if (options.missing.length > 0) {
44
+ parts.push(`missing ${options.missing.map((id) => `"${id}"`).join(", ")}`);
45
+ }
46
+ if (options.extra.length > 0) {
47
+ parts.push(`extra ${options.extra.map((id) => `"${id}"`).join(", ")}`);
48
+ }
49
+ super(`reorder requires the exact set of active task ids: ${parts.join("; ")}.`);
50
+ this.name = "OrderMismatchError";
51
+ this.missing = options.missing;
52
+ this.extra = options.extra;
53
+ }
54
+ }
55
+ export class AnchorNotFoundError extends Error {
56
+ anchor;
57
+ constructor(anchor) {
58
+ super(`Anchor task "${anchor}" not found.`);
59
+ this.name = "AnchorNotFoundError";
60
+ this.anchor = anchor;
61
+ }
62
+ }
@@ -21,6 +21,7 @@
21
21
  ],
22
22
  "dependencies": {
23
23
  "@poe-code/file-lock": "*",
24
+ "@poe-code/process-runner": "*",
24
25
  "yaml": "*"
25
26
  }
26
27
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "toolcraft",
3
- "version": "0.0.11",
3
+ "version": "0.0.13",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -35,8 +35,8 @@
35
35
  "test": "cd ../.. && vitest run packages/toolcraft/src",
36
36
  "test:unit": "cd ../.. && vitest run packages/toolcraft/src",
37
37
  "lint": "cd ../.. && eslint packages/toolcraft/src --ext ts && tsc -p packages/toolcraft/tsconfig.json --noEmit",
38
- "prepack": "node ../../scripts/manage-bundled-workspace-deps.mjs prepare . @poe-code/design-system @poe-code/agent-mcp-config @poe-code/agent-human-in-loop @poe-code/task-list @poe-code/file-lock @poe-code/agent-defs @poe-code/config-mutations tiny-mcp-client mcp-oauth auth-store",
39
- "postpack": "node ../../scripts/manage-bundled-workspace-deps.mjs cleanup . @poe-code/design-system @poe-code/agent-mcp-config @poe-code/agent-human-in-loop @poe-code/task-list @poe-code/file-lock @poe-code/agent-defs @poe-code/config-mutations tiny-mcp-client mcp-oauth auth-store"
38
+ "prepack": "node ../../scripts/manage-bundled-workspace-deps.mjs prepare . @poe-code/design-system @poe-code/agent-mcp-config @poe-code/agent-human-in-loop @poe-code/task-list @poe-code/file-lock @poe-code/agent-defs @poe-code/config-mutations @poe-code/process-runner tiny-mcp-client mcp-oauth auth-store",
39
+ "postpack": "node ../../scripts/manage-bundled-workspace-deps.mjs cleanup . @poe-code/design-system @poe-code/agent-mcp-config @poe-code/agent-human-in-loop @poe-code/task-list @poe-code/file-lock @poe-code/agent-defs @poe-code/config-mutations @poe-code/process-runner tiny-mcp-client mcp-oauth auth-store"
40
40
  },
41
41
  "dependencies": {
42
42
  "@clack/core": "^1.0.0",
@@ -44,8 +44,13 @@
44
44
  "chalk": "^5.6.2",
45
45
  "commander": "^14.0.3",
46
46
  "console-table-printer": "^2.15.0",
47
+ "jose": "^6.1.2",
48
+ "jsonc-parser": "^3.3.1",
49
+ "mustache": "^4.2.0",
50
+ "smol-toml": "^1.3.0",
47
51
  "tiny-stdio-mcp-server": "^0.1.0",
48
- "toolcraft-schema": "^0.0.11"
52
+ "toolcraft-schema": "^0.0.13",
53
+ "yaml": "^2.8.2"
49
54
  },
50
55
  "files": [
51
56
  "dist"
@@ -66,6 +71,7 @@
66
71
  "@poe-code/file-lock",
67
72
  "@poe-code/agent-defs",
68
73
  "@poe-code/config-mutations",
74
+ "@poe-code/process-runner",
69
75
  "tiny-mcp-client",
70
76
  "mcp-oauth",
71
77
  "auth-store"
@@ -77,6 +83,7 @@
77
83
  "@poe-code/config-mutations": "*",
78
84
  "@poe-code/design-system": "^0.0.1",
79
85
  "@poe-code/file-lock": "*",
86
+ "@poe-code/process-runner": "*",
80
87
  "@poe-code/task-list": "*",
81
88
  "auth-store": "*",
82
89
  "mcp-oauth": "*",