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.
- package/dist/cli.js +274 -160
- package/dist/renderer.d.ts +8 -2
- package/dist/renderer.js +71 -12
- package/node_modules/@poe-code/design-system/dist/components/help-formatter-plain.d.ts +4 -0
- package/node_modules/@poe-code/design-system/dist/components/help-formatter-plain.js +132 -0
- package/node_modules/@poe-code/design-system/dist/components/help-formatter.d.ts +13 -0
- package/node_modules/@poe-code/design-system/dist/components/help-formatter.js +116 -7
- package/node_modules/@poe-code/design-system/dist/components/index.d.ts +2 -2
- package/node_modules/@poe-code/design-system/dist/components/index.js +1 -1
- package/node_modules/@poe-code/design-system/dist/components/text.d.ts +1 -0
- package/node_modules/@poe-code/design-system/dist/components/text.js +8 -0
- package/node_modules/@poe-code/design-system/dist/index.d.ts +3 -2
- package/node_modules/@poe-code/design-system/dist/index.js +2 -1
- package/node_modules/@poe-code/process-runner/README.md +41 -0
- package/node_modules/@poe-code/process-runner/dist/docker/args.d.ts +2 -0
- package/node_modules/@poe-code/process-runner/dist/docker/args.js +40 -0
- package/node_modules/@poe-code/process-runner/dist/docker/context.d.ts +3 -0
- package/node_modules/@poe-code/process-runner/dist/docker/context.js +30 -0
- package/node_modules/@poe-code/process-runner/dist/docker/docker-execution-env.d.ts +28 -0
- package/node_modules/@poe-code/process-runner/dist/docker/docker-execution-env.js +428 -0
- package/node_modules/@poe-code/process-runner/dist/docker/docker-runner.d.ts +2 -0
- package/node_modules/@poe-code/process-runner/dist/docker/docker-runner.js +131 -0
- package/node_modules/@poe-code/process-runner/dist/docker/engine.d.ts +3 -0
- package/node_modules/@poe-code/process-runner/dist/docker/engine.js +24 -0
- package/node_modules/@poe-code/process-runner/dist/host/host-execution-env.d.ts +2 -0
- package/node_modules/@poe-code/process-runner/dist/host/host-execution-env.js +48 -0
- package/node_modules/@poe-code/process-runner/dist/host/host-runner.d.ts +3 -0
- package/node_modules/@poe-code/process-runner/dist/host/host-runner.js +74 -0
- package/node_modules/@poe-code/process-runner/dist/index.d.ts +8 -0
- package/node_modules/@poe-code/process-runner/dist/index.js +7 -0
- package/node_modules/@poe-code/process-runner/dist/testing/index.d.ts +2 -0
- package/node_modules/@poe-code/process-runner/dist/testing/index.js +1 -0
- package/node_modules/@poe-code/process-runner/dist/testing/mock-runner.d.ts +3 -0
- package/node_modules/@poe-code/process-runner/dist/testing/mock-runner.js +115 -0
- package/node_modules/@poe-code/process-runner/dist/testing/verify.d.ts +1 -0
- package/node_modules/@poe-code/process-runner/dist/testing/verify.js +359 -0
- package/node_modules/@poe-code/process-runner/dist/types.d.ts +180 -0
- package/node_modules/@poe-code/process-runner/dist/types.js +1 -0
- package/node_modules/@poe-code/process-runner/package.json +27 -0
- package/node_modules/@poe-code/task-list/README.md +49 -5
- package/node_modules/@poe-code/task-list/dist/backends/gh-issues-client.d.ts +19 -0
- package/node_modules/@poe-code/task-list/dist/backends/gh-issues-client.js +62 -0
- package/node_modules/@poe-code/task-list/dist/backends/gh-issues.d.ts +13 -0
- package/node_modules/@poe-code/task-list/dist/backends/gh-issues.js +627 -0
- package/node_modules/@poe-code/task-list/dist/backends/markdown-dir.js +253 -41
- package/node_modules/@poe-code/task-list/dist/backends/utils.d.ts +7 -1
- package/node_modules/@poe-code/task-list/dist/backends/utils.js +21 -0
- package/node_modules/@poe-code/task-list/dist/backends/yaml-file.js +171 -16
- package/node_modules/@poe-code/task-list/dist/index.d.ts +3 -1
- package/node_modules/@poe-code/task-list/dist/index.js +1 -1
- package/node_modules/@poe-code/task-list/dist/open.d.ts +4 -2
- package/node_modules/@poe-code/task-list/dist/open.js +27 -3
- package/node_modules/@poe-code/task-list/dist/types.d.ts +51 -3
- package/node_modules/@poe-code/task-list/dist/types.js +25 -0
- package/node_modules/@poe-code/task-list/package.json +1 -0
- 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,
|
|
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([
|
|
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
|
|
291
|
-
|
|
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
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
17
|
-
|
|
18
|
-
|
|
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
|
|
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
|
|
77
|
-
|
|
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
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "toolcraft",
|
|
3
|
-
"version": "0.0.
|
|
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.
|
|
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": "*",
|