@astrosheep/keiyaku 4.0.4 → 4.0.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.
- package/build/integrations/marketplace/plugins/keiyaku/.claude-plugin/plugin.json +1 -1
- package/build/integrations/marketplace/plugins/keiyaku/.codex-plugin/plugin.json +1 -1
- package/build/integrations/marketplace/plugins/keiyaku/package.json +1 -1
- package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku/SKILL.md +4 -4
- package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-akuma/SKILL.md +15 -7
- package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-bind/SKILL.md +5 -3
- package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-workflow/SKILL.md +24 -4
- package/build/src/akuma/akuma.d.ts +2 -1
- package/build/src/akuma/akuma.js +27 -9
- package/build/src/akuma/allowed.d.ts +1 -1
- package/build/src/akuma/allowed.js +3 -10
- package/build/src/akuma/archetype.d.ts +3 -1
- package/build/src/akuma/archetype.js +11 -6
- package/build/src/akuma/body-turn.d.ts +64 -0
- package/build/src/akuma/body-turn.js +449 -0
- package/build/src/akuma/body.d.ts +4 -5
- package/build/src/akuma/body.js +9 -414
- package/build/src/akuma/heart/facts.d.ts +37 -9
- package/build/src/akuma/heart/facts.js +3 -1
- package/build/src/akuma/heart/index.d.ts +4 -13
- package/build/src/akuma/heart/index.js +14 -135
- package/build/src/akuma/heart/request-authority.d.ts +12 -0
- package/build/src/akuma/heart/request-authority.js +119 -0
- package/build/src/akuma/heart/request-rows.d.ts +16 -0
- package/build/src/akuma/heart/request-rows.js +88 -0
- package/build/src/akuma/heart/rows.d.ts +1 -28
- package/build/src/akuma/heart/rows.js +4 -138
- package/build/src/akuma/heart/schema.d.ts +1 -1
- package/build/src/akuma/heart/schema.js +11 -3
- package/build/src/akuma/heart/soul.js +0 -36
- package/build/src/akuma/heart/timeline.d.ts +1 -0
- package/build/src/akuma/heart/timeline.js +19 -0
- package/build/src/akuma/index.d.ts +1 -1
- package/build/src/akuma/nuke.d.ts +2 -0
- package/build/src/akuma/nuke.js +152 -0
- package/build/src/akuma/provider-recipe.js +1 -11
- package/build/src/akuma/provider.d.ts +4 -7
- package/build/src/akuma/providers/acp/config.d.ts +8 -0
- package/build/src/akuma/providers/acp/config.js +37 -0
- package/build/src/akuma/providers/acp/core.js +12 -2
- package/build/src/akuma/providers/acp/index.d.ts +1 -8
- package/build/src/akuma/providers/acp/index.js +2 -38
- package/build/src/akuma/providers/claude/index.d.ts +0 -4
- package/build/src/akuma/providers/claude/index.js +12 -3
- package/build/src/akuma/providers/codex-app-server/index.d.ts +2 -27
- package/build/src/akuma/providers/codex-app-server/index.js +5 -2
- package/build/src/akuma/providers/grok-build/index.js +1 -2
- package/build/src/akuma/providers/index.d.ts +3 -2
- package/build/src/akuma/providers/index.js +36 -36
- package/build/src/akuma/providers/opencode-sdk/index.d.ts +2 -28
- package/build/src/akuma/providers/opencode-sdk/index.js +14 -4
- package/build/src/akuma/providers/opencode-sdk/session.d.ts +2 -2
- package/build/src/akuma/providers/opencode-sdk/session.js +2 -2
- package/build/src/akuma/providers/pi/index.d.ts +1 -1
- package/build/src/akuma/providers/pi/index.js +29 -6
- package/build/src/akuma/request-serve.d.ts +88 -0
- package/build/src/akuma/request-serve.js +484 -0
- package/build/src/akuma/request-wire.d.ts +125 -0
- package/build/src/akuma/request-wire.js +383 -0
- package/build/src/akuma/requests.d.ts +28 -94
- package/build/src/akuma/requests.js +52 -562
- package/build/src/akuma-body.d.ts +7 -0
- package/build/src/akuma-body.js +69 -2
- package/build/src/alias/index.d.ts +1 -0
- package/build/src/alias/index.js +34 -1
- package/build/src/body/arc.js +4 -29
- package/build/src/body/decode.js +5 -39
- package/build/src/body/envelope.d.ts +7 -0
- package/build/src/body/envelope.js +37 -0
- package/build/src/cli/accepted.js +21 -9
- package/build/src/cli/commands/akuma-invoke.js +1 -0
- package/build/src/cli/commands/akuma.d.ts +1 -0
- package/build/src/cli/commands/akuma.js +13 -10
- package/build/src/cli/commands/contract.d.ts +28 -17
- package/build/src/cli/commands/contract.js +23 -17
- package/build/src/cli/commands/install.d.ts +2 -2
- package/build/src/cli/commands/install.js +6 -6
- package/build/src/cli/commands/nuke.d.ts +10 -0
- package/build/src/cli/commands/nuke.js +20 -0
- package/build/src/cli/commands/task-invoke.d.ts +15 -0
- package/build/src/cli/commands/task-invoke.js +144 -24
- package/build/src/cli/commands/task-query.d.ts +2 -2
- package/build/src/cli/commands/task-query.js +1 -2
- package/build/src/cli/commands/task.js +22 -22
- package/build/src/cli/coordinates.d.ts +6 -2
- package/build/src/cli/coordinates.js +12 -56
- package/build/src/cli/invoke.d.ts +5 -4
- package/build/src/cli/invoke.js +112 -58
- package/build/src/cli/main.d.ts +0 -2
- package/build/src/cli/main.js +4 -140
- package/build/src/cli/parse.d.ts +17 -4
- package/build/src/cli/parse.js +93 -15
- package/build/src/cli/render/akuma.js +111 -53
- package/build/src/cli/render/audit.js +2 -6
- package/build/src/cli/render/contract.js +61 -20
- package/build/src/cli/render/kanshi.js +15 -5
- package/build/src/cli/render/nuke.d.ts +3 -0
- package/build/src/cli/render/nuke.js +8 -0
- package/build/src/cli/render/receipt.js +28 -1
- package/build/src/cli/render/refusal.d.ts +2 -1
- package/build/src/cli/render/refusal.js +31 -0
- package/build/src/cli/render/terminal.d.ts +0 -2
- package/build/src/cli/render/terminal.js +0 -29
- package/build/src/cli/render/text.js +6 -1
- package/build/src/cli/result.d.ts +36 -9
- package/build/src/cli/runtime.d.ts +2 -0
- package/build/src/cli/runtime.js +140 -0
- package/build/src/cli/usage.d.ts +0 -2
- package/build/src/cli/usage.js +0 -4
- package/build/src/contract-worktree.d.ts +29 -14
- package/build/src/contract-worktree.js +131 -70
- package/build/src/core/facts/codec.js +9 -0
- package/build/src/core/facts/fold.js +13 -1
- package/build/src/core/facts/types.d.ts +8 -1
- package/build/src/core/subject.js +4 -3
- package/build/src/core/verbs/placement.d.ts +9 -1
- package/build/src/core/verbs/placement.js +24 -6
- package/build/src/core/verbs/reintegrate.d.ts +13 -0
- package/build/src/core/verbs/reintegrate.js +24 -0
- package/build/src/dispatch/index.d.ts +1 -1
- package/build/src/git/admission.d.ts +2 -1
- package/build/src/git/hooks.d.ts +2 -0
- package/build/src/git/hooks.js +34 -1
- package/build/src/git/integration.d.ts +9 -2
- package/build/src/git/integration.js +92 -1
- package/build/src/git/nuke.d.ts +2 -0
- package/build/src/git/nuke.js +97 -0
- package/build/src/git/observe.d.ts +2 -1
- package/build/src/git/observe.js +6 -5
- package/build/src/git/process.d.ts +28 -0
- package/build/src/git/process.js +94 -0
- package/build/src/git/read-observation.d.ts +2 -1
- package/build/src/git/read-observation.js +3 -2
- package/build/src/git/reconcile.d.ts +4 -1
- package/build/src/git/reconcile.js +42 -8
- package/build/src/git/repository.d.ts +2 -29
- package/build/src/git/repository.js +7 -118
- package/build/src/git/scratch.d.ts +1 -1
- package/build/src/git/scratch.js +1 -1
- package/build/src/git/target-placement.d.ts +3 -2
- package/build/src/git/target-placement.js +15 -14
- package/build/src/git/tender.d.ts +2 -1
- package/build/src/git/tender.js +4 -3
- package/build/src/git/terminal-seal.d.ts +2 -2
- package/build/src/git/terminal-seal.js +3 -6
- package/build/src/git/workspace.d.ts +4 -1
- package/build/src/git/workspace.js +1 -1
- package/build/src/index.d.ts +1 -2
- package/build/src/index.js +1 -1
- package/build/src/kanshi/read.js +5 -1
- package/build/src/library/address.js +7 -1
- package/build/src/library/akuma-creation.d.ts +1 -0
- package/build/src/library/akuma-creation.js +8 -1
- package/build/src/library/audit.d.ts +3 -1
- package/build/src/library/audit.js +2 -1
- package/build/src/library/bind.d.ts +2 -1
- package/build/src/library/bind.js +1 -1
- package/build/src/library/configuration.d.ts +1 -1
- package/build/src/library/configuration.js +52 -18
- package/build/src/library/contract.d.ts +59 -4
- package/build/src/library/contract.js +195 -34
- package/build/src/library/delivery.d.ts +4 -2
- package/build/src/library/fleet.d.ts +27 -7
- package/build/src/library/fleet.js +79 -39
- package/build/src/library/input.d.ts +0 -1
- package/build/src/library/input.js +0 -5
- package/build/src/library/keiyaku.d.ts +6 -2
- package/build/src/library/keiyaku.js +2 -0
- package/build/src/library/mutation.d.ts +3 -1
- package/build/src/library/mutation.js +3 -0
- package/build/src/library/nuke.d.ts +14 -0
- package/build/src/library/nuke.js +54 -0
- package/build/src/library/reconcile.d.ts +2 -1
- package/build/src/library/reconcile.js +8 -3
- package/build/src/library/refusal.d.ts +10 -1
- package/build/src/library/region.d.ts +1 -1
- package/build/src/library/repo.d.ts +4 -1
- package/build/src/library/repo.js +10 -4
- package/build/src/protocol/abandon.d.ts +9 -0
- package/build/src/protocol/abandon.js +15 -0
- package/build/src/protocol/amend.d.ts +16 -0
- package/build/src/protocol/amend.js +77 -0
- package/build/src/protocol/arc.d.ts +6 -0
- package/build/src/protocol/arc.js +12 -0
- package/build/src/protocol/attempt.d.ts +2 -1
- package/build/src/protocol/audit.d.ts +53 -0
- package/build/src/protocol/audit.js +147 -0
- package/build/src/protocol/bind.d.ts +1 -1
- package/build/src/protocol/completion.d.ts +42 -0
- package/build/src/protocol/completion.js +183 -0
- package/build/src/protocol/deliver.d.ts +49 -0
- package/build/src/protocol/deliver.js +261 -0
- package/build/src/protocol/intent.d.ts +2 -1
- package/build/src/protocol/intent.js +8 -2
- package/build/src/protocol/operations.d.ts +93 -202
- package/build/src/protocol/operations.js +23 -642
- package/build/src/protocol/placement.d.ts +12 -13
- package/build/src/protocol/placement.js +8 -19
- package/build/src/protocol/read/status.d.ts +17 -5
- package/build/src/protocol/read/status.js +57 -13
- package/build/src/protocol/reconcile.d.ts +35 -0
- package/build/src/protocol/reconcile.js +41 -0
- package/build/src/protocol/reintegrate.d.ts +31 -0
- package/build/src/protocol/reintegrate.js +74 -0
- package/build/src/protocol/review.d.ts +34 -0
- package/build/src/protocol/review.js +156 -0
- package/build/src/protocol/run.d.ts +1 -1
- package/build/src/runtime/proc/run.js +9 -5
- package/build/src/settlement/fence.d.ts +3 -1
- package/build/src/settlement/fence.js +24 -0
- package/build/src/settlement/holder.d.ts +1 -1
- package/build/src/settlement/settle.d.ts +1 -1
- package/build/src/settlement/settle.js +1 -2
- package/build/src/task/board.d.ts +0 -1
- package/build/src/task/board.js +0 -3
- package/build/src/task/compose.d.ts +2 -2
- package/build/src/task/compose.js +13 -11
- package/build/src/task/context.d.ts +14 -2
- package/build/src/task/context.js +30 -7
- package/build/src/task/index.d.ts +7 -6
- package/build/src/task/index.js +17 -145
- package/build/src/task/input.d.ts +18 -0
- package/build/src/task/input.js +189 -0
- package/build/src/task/mutation.d.ts +44 -0
- package/build/src/task/mutation.js +93 -0
- package/build/src/task/operations.d.ts +14 -6
- package/build/src/task/operations.js +57 -57
- package/build/src/task/store.d.ts +1 -0
- package/build/src/task/store.js +91 -3
- package/build/src/verification/execution.d.ts +1 -1
- package/build/src/workspace-place.d.ts +3 -4
- package/build/src/workspace-place.js +26 -15
- package/package.json +3 -3
package/build/src/task/index.js
CHANGED
|
@@ -1,136 +1,11 @@
|
|
|
1
1
|
import { buildTree, createTaskRelations, diagnoseBoard, projectDetailFacts, } from "./board.js";
|
|
2
2
|
import { composeTasks } from "./compose.js";
|
|
3
3
|
import { TaskAuthorityCorruptionError } from "./document.js";
|
|
4
|
-
import {
|
|
5
|
-
import { addTask, addTaskDocument, batchTasks, blockedTasks, currentNamespace, lifecycleTask, listTasks, queryTasks, readyTasks, setCurrentNamespace, taskView, updateTask, } from "./operations.js";
|
|
4
|
+
import { addTask, addTaskDocument, batchTasks, blockedTasks, lifecycleTask, listTasks, queryTasks, readyTasks, taskView, updateTask, } from "./operations.js";
|
|
6
5
|
import { readBoard } from "./store.js";
|
|
7
|
-
import {
|
|
6
|
+
import { actor, addInput, closed, limit, namespace, record, signal, sort, taskId as id, taskIds, text, updateInput, } from "./input.js";
|
|
7
|
+
import { normalizeTaskQuery, TASK_RELATION_PREDICATE_FIELDS, DEFAULT_TASK_LIMIT, } from "./query.js";
|
|
8
8
|
export { TaskAuthorityCorruptionError, TASK_RELATION_PREDICATE_FIELDS };
|
|
9
|
-
function record(value, label) {
|
|
10
|
-
if (typeof value !== "object" || value === null || Array.isArray(value))
|
|
11
|
-
throw new TypeError(`${label} must be an object`);
|
|
12
|
-
return value;
|
|
13
|
-
}
|
|
14
|
-
function closed(value, allowed, label) {
|
|
15
|
-
for (const key of Object.keys(value))
|
|
16
|
-
if (!allowed.includes(key))
|
|
17
|
-
throw new TypeError(`${label} has unknown field: ${key}`);
|
|
18
|
-
}
|
|
19
|
-
function id(value) { if (typeof value !== "string")
|
|
20
|
-
throw new TypeError("task ID must be a string"); parseTaskId(value); return value; }
|
|
21
|
-
function signal(value) {
|
|
22
|
-
if (value === undefined)
|
|
23
|
-
return undefined;
|
|
24
|
-
if (!(value instanceof AbortSignal))
|
|
25
|
-
throw new TypeError("signal must be an AbortSignal");
|
|
26
|
-
return value;
|
|
27
|
-
}
|
|
28
|
-
function strings(value, label) {
|
|
29
|
-
if (value === undefined)
|
|
30
|
-
return undefined;
|
|
31
|
-
if (!Array.isArray(value) || !value.every((item) => typeof item === "string"))
|
|
32
|
-
throw new TypeError(`${label} must be an array of strings`);
|
|
33
|
-
return value;
|
|
34
|
-
}
|
|
35
|
-
function taskIds(value, label) {
|
|
36
|
-
const values = strings(value, label)?.map(id);
|
|
37
|
-
if (values !== undefined && new Set(values).size !== values.length)
|
|
38
|
-
throw new TypeError(`${label} must not contain duplicates`);
|
|
39
|
-
return values;
|
|
40
|
-
}
|
|
41
|
-
function namespace(value) {
|
|
42
|
-
const values = strings(value, "namespace");
|
|
43
|
-
if (values === undefined)
|
|
44
|
-
return undefined;
|
|
45
|
-
if (!values.every(isTaskSegment))
|
|
46
|
-
throw new TypeError("namespace must contain canonical segments");
|
|
47
|
-
return values;
|
|
48
|
-
}
|
|
49
|
-
function text(value, label) { if (value === undefined)
|
|
50
|
-
return undefined; if (typeof value !== "string")
|
|
51
|
-
throw new TypeError(`${label} must be a string`); return value; }
|
|
52
|
-
function actor(value) {
|
|
53
|
-
if (value === undefined)
|
|
54
|
-
return undefined;
|
|
55
|
-
if (typeof value !== "string" || value.trim().length === 0)
|
|
56
|
-
throw new TypeError("actor must be a nonblank string");
|
|
57
|
-
return value;
|
|
58
|
-
}
|
|
59
|
-
function priority(value) { if (value === undefined)
|
|
60
|
-
return undefined; if (!Number.isInteger(value) || typeof value !== "number" || value < 0 || value > 3)
|
|
61
|
-
throw new TypeError("priority must be 0..3"); return value; }
|
|
62
|
-
function limit(value) { if (value === undefined)
|
|
63
|
-
return undefined; if (typeof value !== "number" || !isValidTaskLimit(value))
|
|
64
|
-
throw new TypeError(`limit must be an integer from 1 to ${MAX_TASK_LIMIT}`); return value; }
|
|
65
|
-
function sort(value) {
|
|
66
|
-
if (value === undefined)
|
|
67
|
-
return undefined;
|
|
68
|
-
if (value !== "priority" && value !== "created" && value !== "updated" && value !== "id")
|
|
69
|
-
throw new TypeError("sort is invalid");
|
|
70
|
-
return value;
|
|
71
|
-
}
|
|
72
|
-
function state(value) {
|
|
73
|
-
if (value === undefined)
|
|
74
|
-
return undefined;
|
|
75
|
-
if (value !== "open" && value !== "in_progress" && value !== "on_hold" && value !== "done" && value !== "drop")
|
|
76
|
-
throw new TypeError("state is invalid");
|
|
77
|
-
return value;
|
|
78
|
-
}
|
|
79
|
-
function nullableId(value) { return value === undefined || value === null ? value : id(value); }
|
|
80
|
-
function addInput(input) {
|
|
81
|
-
const v = record(input, "add input");
|
|
82
|
-
closed(v, ["title", "namespace", "body", "note", "state", "priority", "needs", "parent", "supersedes", "relates", "actor", "signal"], "add input");
|
|
83
|
-
const title = text(v.title, "title");
|
|
84
|
-
if (title === undefined || title.trim() === "")
|
|
85
|
-
throw new TypeError("title is required");
|
|
86
|
-
const ns = namespace(v.namespace), body = text(v.body, "body"), note = text(v.note, "note"), initialState = state(v.state), pri = priority(v.priority);
|
|
87
|
-
const needs = taskIds(v.needs, "needs"), parent = nullableId(v.parent), supersedes = taskIds(v.supersedes, "supersedes"), relates = taskIds(v.relates, "relates");
|
|
88
|
-
const createdBy = actor(v.actor), abort = signal(v.signal);
|
|
89
|
-
return { title, ...(ns === undefined ? {} : { namespace: ns }), ...(body === undefined ? {} : { body }), ...(note === undefined ? {} : { note }),
|
|
90
|
-
...(initialState === undefined ? {} : { state: initialState }), ...(pri === undefined ? {} : { priority: pri }),
|
|
91
|
-
...(needs === undefined ? {} : { needs }), ...(parent === undefined ? {} : { parent }),
|
|
92
|
-
...(supersedes === undefined ? {} : { supersedes }), ...(relates === undefined ? {} : { relates }),
|
|
93
|
-
...(createdBy === undefined ? {} : { actor: createdBy }),
|
|
94
|
-
...(abort === undefined ? {} : { signal: abort }) };
|
|
95
|
-
}
|
|
96
|
-
function updateInput(input) {
|
|
97
|
-
const v = record(input, "update input");
|
|
98
|
-
closed(v, ["title", "body", "appendBody", "note", "priority", "needs", "addNeeds", "dropNeeds", "parent", "supersedes", "addSupersedes", "dropSupersedes", "relates", "addRelates", "dropRelates", "signal"], "update input");
|
|
99
|
-
if (v.body !== undefined && v.appendBody !== undefined)
|
|
100
|
-
throw new TypeError("body and appendBody are mutually exclusive");
|
|
101
|
-
const result = {};
|
|
102
|
-
const title = text(v.title, "title");
|
|
103
|
-
if (title !== undefined) {
|
|
104
|
-
if (title.trim().length === 0)
|
|
105
|
-
throw new TypeError("title must be nonblank");
|
|
106
|
-
result.title = title;
|
|
107
|
-
}
|
|
108
|
-
const body = text(v.body, "body");
|
|
109
|
-
if (body !== undefined)
|
|
110
|
-
result.body = body;
|
|
111
|
-
const appendBody = text(v.appendBody, "appendBody");
|
|
112
|
-
if (appendBody !== undefined)
|
|
113
|
-
result.appendBody = appendBody;
|
|
114
|
-
const note = text(v.note, "note");
|
|
115
|
-
if (note !== undefined)
|
|
116
|
-
result.note = note;
|
|
117
|
-
const pri = priority(v.priority);
|
|
118
|
-
if (pri !== undefined)
|
|
119
|
-
result.priority = pri;
|
|
120
|
-
for (const [key, value] of [["needs", taskIds(v.needs, "needs")], ["addNeeds", taskIds(v.addNeeds, "addNeeds")], ["dropNeeds", taskIds(v.dropNeeds, "dropNeeds")], ["supersedes", taskIds(v.supersedes, "supersedes")], ["addSupersedes", taskIds(v.addSupersedes, "addSupersedes")], ["dropSupersedes", taskIds(v.dropSupersedes, "dropSupersedes")], ["relates", taskIds(v.relates, "relates")], ["addRelates", taskIds(v.addRelates, "addRelates")], ["dropRelates", taskIds(v.dropRelates, "dropRelates")]])
|
|
121
|
-
if (value !== undefined)
|
|
122
|
-
result[key] = value;
|
|
123
|
-
const parent = nullableId(v.parent);
|
|
124
|
-
if (parent !== undefined)
|
|
125
|
-
result.parent = parent;
|
|
126
|
-
const abort = signal(v.signal);
|
|
127
|
-
if (abort !== undefined)
|
|
128
|
-
result.signal = abort;
|
|
129
|
-
const update = result;
|
|
130
|
-
if (Object.keys(result).filter((key) => key !== "signal").length === 0)
|
|
131
|
-
throw new TypeError("update requires at least one field change");
|
|
132
|
-
return update;
|
|
133
|
-
}
|
|
134
9
|
class TaskHandle {
|
|
135
10
|
id;
|
|
136
11
|
world;
|
|
@@ -171,33 +46,30 @@ class TasksHandle {
|
|
|
171
46
|
this.world = world;
|
|
172
47
|
this.root = world;
|
|
173
48
|
}
|
|
174
|
-
async namespace() { const value = await currentNamespace(this.world); return "kind" in value ? { kind: "refused", refusal: value } : { kind: "accepted", value }; }
|
|
175
|
-
async setNamespace(input) { const v = record(input, "setNamespace input"); closed(v, ["namespace"], "setNamespace input"); const ns = namespace(v.namespace); if (ns === undefined)
|
|
176
|
-
throw new TypeError("namespace is required"); await setCurrentNamespace(this.world, ns); }
|
|
177
49
|
task(input) { const v = record(input, "task input"); closed(v, ["id"], "task input"); return new TaskHandle(id(v.id), this.world); }
|
|
178
50
|
add(input) { return addTask(this.world, addInput(input)); }
|
|
179
51
|
addDocument(input) { const v = record(input, "addDocument input"); closed(v, ["markdown", "namespace", "actor", "signal"], "addDocument input"); const markdown = text(v.markdown, "markdown"); if (markdown === undefined)
|
|
180
52
|
throw new TypeError("markdown is required"); const ns = namespace(v.namespace), createdBy = actor(v.actor), abort = signal(v.signal); return addTaskDocument(this.world, { markdown, ...(ns === undefined ? {} : { namespace: ns }), ...(createdBy === undefined ? {} : { actor: createdBy }), ...(abort === undefined ? {} : { signal: abort }) }); }
|
|
181
|
-
async list(input = {}) { const v = record(input, "list input"); closed(v, ["selection", "scope", "limit"], "list input"); if (v.selection !== undefined && v.selection !== "active" && v.selection !== "closed" && v.selection !== "all")
|
|
53
|
+
async list(input = {}) { const v = record(input, "list input"); closed(v, ["selection", "scope", "namespace", "limit"], "list input"); if (v.selection !== undefined && v.selection !== "active" && v.selection !== "closed" && v.selection !== "all")
|
|
182
54
|
throw new TypeError("selection must be active, closed, or all"); if (v.scope !== undefined && v.scope !== "namespace" && v.scope !== "world")
|
|
183
|
-
throw new TypeError("scope must be namespace or world"); return listTasks(this.world, v.selection ?? "active", v.scope, limit(v.limit) ?? DEFAULT_TASK_LIMIT); }
|
|
184
|
-
async ready(input = {}) { const v = record(input, "ready input"); closed(v, ["scope", "parent", "limit"], "ready input"); if (v.scope !== undefined && v.scope !== "namespace" && v.scope !== "world")
|
|
185
|
-
throw new TypeError("scope must be namespace or world"); const parent = v.parent === undefined ? undefined : id(v.parent); return readyTasks(this.world, v.scope, parent, limit(v.limit) ?? DEFAULT_TASK_LIMIT); }
|
|
186
|
-
async blocked(input = {}) { const v = record(input, "blocked input"); closed(v, ["scope", "parent", "limit"], "blocked input"); if (v.scope !== undefined && v.scope !== "namespace" && v.scope !== "world")
|
|
187
|
-
throw new TypeError("scope must be namespace or world"); const parent = v.parent === undefined ? undefined : id(v.parent); return blockedTasks(this.world, v.scope, parent, limit(v.limit) ?? DEFAULT_TASK_LIMIT); }
|
|
188
|
-
async query(input = {}) { const v = record(input, "query input"); closed(v, ["where", "scope", "sort", "limit"], "query input"); if (v.scope !== undefined && v.scope !== "namespace" && v.scope !== "world")
|
|
189
|
-
throw new TypeError("scope must be namespace or world"); const expression = v.where === undefined ? { kind: "and", terms: [{ kind: "predicate", predicate: { field: "state", operator: "!=", value: "done" } }, { kind: "predicate", predicate: { field: "state", operator: "!=", value: "drop" } }] } : normalizeTaskQuery(v.where); return queryTasks(this.world, expression, v.scope, sort(v.sort) ?? "priority", limit(v.limit) ?? DEFAULT_TASK_LIMIT); }
|
|
55
|
+
throw new TypeError("scope must be namespace or world"); return listTasks(this.world, namespace(v.namespace), v.selection ?? "active", v.scope, limit(v.limit) ?? DEFAULT_TASK_LIMIT); }
|
|
56
|
+
async ready(input = {}) { const v = record(input, "ready input"); closed(v, ["scope", "namespace", "parent", "limit"], "ready input"); if (v.scope !== undefined && v.scope !== "namespace" && v.scope !== "world")
|
|
57
|
+
throw new TypeError("scope must be namespace or world"); const parent = v.parent === undefined ? undefined : id(v.parent); return readyTasks(this.world, namespace(v.namespace), v.scope, parent, limit(v.limit) ?? DEFAULT_TASK_LIMIT); }
|
|
58
|
+
async blocked(input = {}) { const v = record(input, "blocked input"); closed(v, ["scope", "namespace", "parent", "limit"], "blocked input"); if (v.scope !== undefined && v.scope !== "namespace" && v.scope !== "world")
|
|
59
|
+
throw new TypeError("scope must be namespace or world"); const parent = v.parent === undefined ? undefined : id(v.parent); return blockedTasks(this.world, namespace(v.namespace), v.scope, parent, limit(v.limit) ?? DEFAULT_TASK_LIMIT); }
|
|
60
|
+
async query(input = {}) { const v = record(input, "query input"); closed(v, ["where", "scope", "namespace", "sort", "limit"], "query input"); if (v.scope !== undefined && v.scope !== "namespace" && v.scope !== "world")
|
|
61
|
+
throw new TypeError("scope must be namespace or world"); const expression = v.where === undefined ? { kind: "and", terms: [{ kind: "predicate", predicate: { field: "state", operator: "!=", value: "done" } }, { kind: "predicate", predicate: { field: "state", operator: "!=", value: "drop" } }] } : normalizeTaskQuery(v.where); const selected = namespace(v.namespace); return queryTasks({ world: this.world, ...(selected === undefined ? {} : { namespace: selected }), expression, ...(v.scope === undefined ? {} : { scope: v.scope }), sort: sort(v.sort) ?? "priority", limit: limit(v.limit) ?? DEFAULT_TASK_LIMIT }); }
|
|
190
62
|
async doctor() { return { issues: diagnoseBoard((await readBoard(this.world)).board) }; }
|
|
191
63
|
batch(input) { const v = record(input, "batch input"); closed(v, ["verb", "ids", "note", "signal"], "batch input"); const verb = v.verb; if (verb !== "done" && verb !== "drop" && verb !== "hold")
|
|
192
64
|
throw new TypeError("batch verb is invalid"); const note = text(v.note, "note"); if (note !== undefined && verb !== "done" && verb !== "drop")
|
|
193
65
|
throw new TypeError("batch note is valid only for done or drop"); return batchTasks(this.world, verb, taskIds(v.ids, "ids") ?? [], signal(v.signal), note); }
|
|
194
|
-
compose(input) { const v = record(input, "compose input"); closed(v, ["markdown", "actor", "signal"], "compose input"); const markdown = text(v.markdown, "markdown"); if (markdown === undefined)
|
|
195
|
-
throw new TypeError("markdown is required"); return composeTasks(this.world, markdown, signal(v.signal), actor(v.actor)); }
|
|
66
|
+
compose(input) { const v = record(input, "compose input"); closed(v, ["markdown", "namespace", "actor", "signal"], "compose input"); const markdown = text(v.markdown, "markdown"); if (markdown === undefined)
|
|
67
|
+
throw new TypeError("markdown is required"); const selected = namespace(v.namespace); return composeTasks(this.world, markdown, signal(v.signal), actor(v.actor), selected); }
|
|
196
68
|
}
|
|
197
69
|
export const Tasks = Object.freeze({
|
|
198
|
-
of(
|
|
199
|
-
if (typeof
|
|
200
|
-
throw new TypeError("Tasks.of
|
|
201
|
-
return new TasksHandle(
|
|
70
|
+
of(world) {
|
|
71
|
+
if (typeof world !== "string")
|
|
72
|
+
throw new TypeError("Tasks.of world must be a WorldRoot");
|
|
73
|
+
return new TasksHandle(world);
|
|
202
74
|
},
|
|
203
75
|
});
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { type TaskPriority, type TaskState } from "./document.js";
|
|
2
|
+
import { type TaskId } from "./identity.js";
|
|
3
|
+
import type { AddTaskInput, UpdateTaskInput } from "./operations.js";
|
|
4
|
+
import { type TaskQuerySort } from "./query.js";
|
|
5
|
+
export declare function record(value: unknown, label: string): Record<string, unknown>;
|
|
6
|
+
export declare function closed(value: Record<string, unknown>, allowed: readonly string[], label: string): void;
|
|
7
|
+
export declare function taskId(value: unknown): TaskId;
|
|
8
|
+
export declare function signal(value: unknown): AbortSignal | undefined;
|
|
9
|
+
export declare function taskIds(value: unknown, label: string): readonly TaskId[] | undefined;
|
|
10
|
+
export declare function namespace(value: unknown): readonly string[] | undefined;
|
|
11
|
+
export declare function text(value: unknown, label: string): string | undefined;
|
|
12
|
+
export declare function actor(value: unknown): string | undefined;
|
|
13
|
+
export declare function priority(value: unknown): TaskPriority | undefined;
|
|
14
|
+
export declare function limit(value: unknown): number | undefined;
|
|
15
|
+
export declare function sort(value: unknown): TaskQuerySort | undefined;
|
|
16
|
+
export declare function state(value: unknown): TaskState | undefined;
|
|
17
|
+
export declare function addInput(input: unknown): AddTaskInput;
|
|
18
|
+
export declare function updateInput(input: unknown): UpdateTaskInput;
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
import { isTaskSegment, parseTaskId } from "./identity.js";
|
|
2
|
+
import { isValidTaskLimit, MAX_TASK_LIMIT } from "./query.js";
|
|
3
|
+
export function record(value, label) {
|
|
4
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
5
|
+
throw new TypeError(`${label} must be an object`);
|
|
6
|
+
}
|
|
7
|
+
return value;
|
|
8
|
+
}
|
|
9
|
+
export function closed(value, allowed, label) {
|
|
10
|
+
for (const key of Object.keys(value)) {
|
|
11
|
+
if (!allowed.includes(key))
|
|
12
|
+
throw new TypeError(`${label} has unknown field: ${key}`);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
export function taskId(value) {
|
|
16
|
+
if (typeof value !== "string")
|
|
17
|
+
throw new TypeError("task ID must be a string");
|
|
18
|
+
parseTaskId(value);
|
|
19
|
+
return value;
|
|
20
|
+
}
|
|
21
|
+
export function signal(value) {
|
|
22
|
+
if (value === undefined)
|
|
23
|
+
return undefined;
|
|
24
|
+
if (!(value instanceof AbortSignal))
|
|
25
|
+
throw new TypeError("signal must be an AbortSignal");
|
|
26
|
+
return value;
|
|
27
|
+
}
|
|
28
|
+
function strings(value, label) {
|
|
29
|
+
if (value === undefined)
|
|
30
|
+
return undefined;
|
|
31
|
+
if (!Array.isArray(value) || !value.every((item) => typeof item === "string")) {
|
|
32
|
+
throw new TypeError(`${label} must be an array of strings`);
|
|
33
|
+
}
|
|
34
|
+
return value;
|
|
35
|
+
}
|
|
36
|
+
export function taskIds(value, label) {
|
|
37
|
+
const values = strings(value, label)?.map(taskId);
|
|
38
|
+
if (values !== undefined && new Set(values).size !== values.length) {
|
|
39
|
+
throw new TypeError(`${label} must not contain duplicates`);
|
|
40
|
+
}
|
|
41
|
+
return values;
|
|
42
|
+
}
|
|
43
|
+
export function namespace(value) {
|
|
44
|
+
const values = strings(value, "namespace");
|
|
45
|
+
if (values === undefined)
|
|
46
|
+
return undefined;
|
|
47
|
+
if (!values.every(isTaskSegment))
|
|
48
|
+
throw new TypeError("namespace must contain canonical segments");
|
|
49
|
+
return values;
|
|
50
|
+
}
|
|
51
|
+
export function text(value, label) {
|
|
52
|
+
if (value === undefined)
|
|
53
|
+
return undefined;
|
|
54
|
+
if (typeof value !== "string")
|
|
55
|
+
throw new TypeError(`${label} must be a string`);
|
|
56
|
+
return value;
|
|
57
|
+
}
|
|
58
|
+
export function actor(value) {
|
|
59
|
+
if (value === undefined)
|
|
60
|
+
return undefined;
|
|
61
|
+
if (typeof value !== "string" || value.trim().length === 0) {
|
|
62
|
+
throw new TypeError("actor must be a nonblank string");
|
|
63
|
+
}
|
|
64
|
+
return value;
|
|
65
|
+
}
|
|
66
|
+
export function priority(value) {
|
|
67
|
+
if (value === undefined)
|
|
68
|
+
return undefined;
|
|
69
|
+
if (!Number.isInteger(value) || typeof value !== "number" || value < 0 || value > 3) {
|
|
70
|
+
throw new TypeError("priority must be 0..3");
|
|
71
|
+
}
|
|
72
|
+
return value;
|
|
73
|
+
}
|
|
74
|
+
export function limit(value) {
|
|
75
|
+
if (value === undefined)
|
|
76
|
+
return undefined;
|
|
77
|
+
if (typeof value !== "number" || !isValidTaskLimit(value)) {
|
|
78
|
+
throw new TypeError(`limit must be an integer from 1 to ${MAX_TASK_LIMIT}`);
|
|
79
|
+
}
|
|
80
|
+
return value;
|
|
81
|
+
}
|
|
82
|
+
export function sort(value) {
|
|
83
|
+
if (value === undefined)
|
|
84
|
+
return undefined;
|
|
85
|
+
if (value !== "priority" && value !== "created" && value !== "updated" && value !== "id") {
|
|
86
|
+
throw new TypeError("sort is invalid");
|
|
87
|
+
}
|
|
88
|
+
return value;
|
|
89
|
+
}
|
|
90
|
+
export function state(value) {
|
|
91
|
+
if (value === undefined)
|
|
92
|
+
return undefined;
|
|
93
|
+
if (value !== "open" && value !== "in_progress" && value !== "on_hold"
|
|
94
|
+
&& value !== "done" && value !== "drop") {
|
|
95
|
+
throw new TypeError("state is invalid");
|
|
96
|
+
}
|
|
97
|
+
return value;
|
|
98
|
+
}
|
|
99
|
+
function nullableId(value) {
|
|
100
|
+
return value === undefined || value === null ? value : taskId(value);
|
|
101
|
+
}
|
|
102
|
+
export function addInput(input) {
|
|
103
|
+
const value = record(input, "add input");
|
|
104
|
+
closed(value, [
|
|
105
|
+
"title", "namespace", "body", "note", "state", "priority", "needs", "parent",
|
|
106
|
+
"supersedes", "relates", "actor", "signal",
|
|
107
|
+
], "add input");
|
|
108
|
+
const title = text(value.title, "title");
|
|
109
|
+
if (title === undefined || title.trim() === "")
|
|
110
|
+
throw new TypeError("title is required");
|
|
111
|
+
const selectedNamespace = namespace(value.namespace);
|
|
112
|
+
const body = text(value.body, "body");
|
|
113
|
+
const note = text(value.note, "note");
|
|
114
|
+
const initialState = state(value.state);
|
|
115
|
+
const selectedPriority = priority(value.priority);
|
|
116
|
+
const needs = taskIds(value.needs, "needs");
|
|
117
|
+
const parent = nullableId(value.parent);
|
|
118
|
+
const supersedes = taskIds(value.supersedes, "supersedes");
|
|
119
|
+
const relates = taskIds(value.relates, "relates");
|
|
120
|
+
const createdBy = actor(value.actor);
|
|
121
|
+
const abort = signal(value.signal);
|
|
122
|
+
return {
|
|
123
|
+
title,
|
|
124
|
+
...(selectedNamespace === undefined ? {} : { namespace: selectedNamespace }),
|
|
125
|
+
...(body === undefined ? {} : { body }),
|
|
126
|
+
...(note === undefined ? {} : { note }),
|
|
127
|
+
...(initialState === undefined ? {} : { state: initialState }),
|
|
128
|
+
...(selectedPriority === undefined ? {} : { priority: selectedPriority }),
|
|
129
|
+
...(needs === undefined ? {} : { needs }),
|
|
130
|
+
...(parent === undefined ? {} : { parent }),
|
|
131
|
+
...(supersedes === undefined ? {} : { supersedes }),
|
|
132
|
+
...(relates === undefined ? {} : { relates }),
|
|
133
|
+
...(createdBy === undefined ? {} : { actor: createdBy }),
|
|
134
|
+
...(abort === undefined ? {} : { signal: abort }),
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
export function updateInput(input) {
|
|
138
|
+
const value = record(input, "update input");
|
|
139
|
+
closed(value, [
|
|
140
|
+
"title", "body", "appendBody", "note", "priority", "needs", "addNeeds", "dropNeeds",
|
|
141
|
+
"parent", "supersedes", "addSupersedes", "dropSupersedes", "relates", "addRelates",
|
|
142
|
+
"dropRelates", "signal",
|
|
143
|
+
], "update input");
|
|
144
|
+
if (value.body !== undefined && value.appendBody !== undefined) {
|
|
145
|
+
throw new TypeError("body and appendBody are mutually exclusive");
|
|
146
|
+
}
|
|
147
|
+
const result = {};
|
|
148
|
+
const title = text(value.title, "title");
|
|
149
|
+
if (title !== undefined) {
|
|
150
|
+
if (title.trim().length === 0)
|
|
151
|
+
throw new TypeError("title must be nonblank");
|
|
152
|
+
result.title = title;
|
|
153
|
+
}
|
|
154
|
+
const body = text(value.body, "body");
|
|
155
|
+
if (body !== undefined)
|
|
156
|
+
result.body = body;
|
|
157
|
+
const appendBody = text(value.appendBody, "appendBody");
|
|
158
|
+
if (appendBody !== undefined)
|
|
159
|
+
result.appendBody = appendBody;
|
|
160
|
+
const note = text(value.note, "note");
|
|
161
|
+
if (note !== undefined)
|
|
162
|
+
result.note = note;
|
|
163
|
+
const selectedPriority = priority(value.priority);
|
|
164
|
+
if (selectedPriority !== undefined)
|
|
165
|
+
result.priority = selectedPriority;
|
|
166
|
+
for (const [key, ids] of [
|
|
167
|
+
["needs", taskIds(value.needs, "needs")],
|
|
168
|
+
["addNeeds", taskIds(value.addNeeds, "addNeeds")],
|
|
169
|
+
["dropNeeds", taskIds(value.dropNeeds, "dropNeeds")],
|
|
170
|
+
["supersedes", taskIds(value.supersedes, "supersedes")],
|
|
171
|
+
["addSupersedes", taskIds(value.addSupersedes, "addSupersedes")],
|
|
172
|
+
["dropSupersedes", taskIds(value.dropSupersedes, "dropSupersedes")],
|
|
173
|
+
["relates", taskIds(value.relates, "relates")],
|
|
174
|
+
["addRelates", taskIds(value.addRelates, "addRelates")],
|
|
175
|
+
["dropRelates", taskIds(value.dropRelates, "dropRelates")],
|
|
176
|
+
])
|
|
177
|
+
if (ids !== undefined)
|
|
178
|
+
result[key] = ids;
|
|
179
|
+
const parent = nullableId(value.parent);
|
|
180
|
+
if (parent !== undefined)
|
|
181
|
+
result.parent = parent;
|
|
182
|
+
const abort = signal(value.signal);
|
|
183
|
+
if (abort !== undefined)
|
|
184
|
+
result.signal = abort;
|
|
185
|
+
if (Object.keys(result).filter((key) => key !== "signal").length === 0) {
|
|
186
|
+
throw new TypeError("update requires at least one field change");
|
|
187
|
+
}
|
|
188
|
+
return result;
|
|
189
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { WorldRoot } from "../world.js";
|
|
2
|
+
import type { TaskCompositionResult } from "./compose.js";
|
|
3
|
+
import type { AddTaskDocumentInput, AddTaskInput, TaskBatchResult, TaskMutationResult, TaskUpdateResult, UpdateTaskInput } from "./operations.js";
|
|
4
|
+
import type { TaskId } from "./identity.js";
|
|
5
|
+
export declare const TASK_MUTATION_ACTIONS: readonly ["task.add", "task.addDocument", "task.compose", "task.done", "task.drop", "task.hold", "task.resume", "task.start", "task.stop", "task.update"];
|
|
6
|
+
export type TaskMutationAction = (typeof TASK_MUTATION_ACTIONS)[number];
|
|
7
|
+
export declare function isTaskMutationAction(value: unknown): value is TaskMutationAction;
|
|
8
|
+
export type TaskMutationRequest = Readonly<{
|
|
9
|
+
action: "task.add";
|
|
10
|
+
input: Omit<AddTaskInput, "actor" | "signal" | "namespace"> & Readonly<{
|
|
11
|
+
namespace: readonly string[];
|
|
12
|
+
}>;
|
|
13
|
+
}> | Readonly<{
|
|
14
|
+
action: "task.addDocument";
|
|
15
|
+
input: Omit<AddTaskDocumentInput, "actor" | "signal" | "namespace"> & Readonly<{
|
|
16
|
+
namespace: readonly string[];
|
|
17
|
+
}>;
|
|
18
|
+
}> | Readonly<{
|
|
19
|
+
action: "task.compose";
|
|
20
|
+
markdown: string;
|
|
21
|
+
namespace: readonly string[];
|
|
22
|
+
}> | Readonly<{
|
|
23
|
+
action: "task.update";
|
|
24
|
+
id: TaskId;
|
|
25
|
+
input: Omit<UpdateTaskInput, "signal">;
|
|
26
|
+
}> | Readonly<{
|
|
27
|
+
action: "task.start" | "task.stop" | "task.resume";
|
|
28
|
+
id: TaskId;
|
|
29
|
+
}> | Readonly<{
|
|
30
|
+
action: "task.hold";
|
|
31
|
+
ids: readonly TaskId[];
|
|
32
|
+
}> | Readonly<{
|
|
33
|
+
action: "task.done" | "task.drop";
|
|
34
|
+
ids: readonly TaskId[];
|
|
35
|
+
note?: string;
|
|
36
|
+
}>;
|
|
37
|
+
export type TaskMutationExecutionResult = TaskMutationResult | TaskUpdateResult | TaskBatchResult | TaskCompositionResult;
|
|
38
|
+
export declare function decodeTaskMutationRequest(action: TaskMutationAction, value: unknown): TaskMutationRequest;
|
|
39
|
+
export declare function executeTaskMutation(input: Readonly<{
|
|
40
|
+
world: WorldRoot;
|
|
41
|
+
request: TaskMutationRequest;
|
|
42
|
+
requester: string;
|
|
43
|
+
signal?: AbortSignal;
|
|
44
|
+
}>): Promise<TaskMutationExecutionResult>;
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { addInput, closed, namespace, record, taskId, taskIds, text, updateInput } from "./input.js";
|
|
2
|
+
export const TASK_MUTATION_ACTIONS = Object.freeze([
|
|
3
|
+
"task.add", "task.addDocument", "task.compose", "task.done", "task.drop",
|
|
4
|
+
"task.hold", "task.resume", "task.start", "task.stop", "task.update",
|
|
5
|
+
]);
|
|
6
|
+
const TASK_MUTATION_ACTION_SET = new Set(TASK_MUTATION_ACTIONS);
|
|
7
|
+
export function isTaskMutationAction(value) {
|
|
8
|
+
return typeof value === "string" && TASK_MUTATION_ACTION_SET.has(value);
|
|
9
|
+
}
|
|
10
|
+
function mutationObject(value, keys, label) {
|
|
11
|
+
const decoded = record(value, label);
|
|
12
|
+
closed(decoded, keys, label);
|
|
13
|
+
return decoded;
|
|
14
|
+
}
|
|
15
|
+
export function decodeTaskMutationRequest(action, value) {
|
|
16
|
+
switch (action) {
|
|
17
|
+
case "task.add": {
|
|
18
|
+
const input = mutationObject(value, ["input"], "task.add request");
|
|
19
|
+
const raw = mutationObject(input.input, [
|
|
20
|
+
"title", "namespace", "body", "note", "state", "priority", "needs", "parent", "supersedes", "relates",
|
|
21
|
+
], "task.add input");
|
|
22
|
+
const decoded = addInput(raw);
|
|
23
|
+
if (decoded.namespace === undefined)
|
|
24
|
+
throw new TypeError("task.add request namespace is required");
|
|
25
|
+
return { action, input: { ...decoded, namespace: decoded.namespace } };
|
|
26
|
+
}
|
|
27
|
+
case "task.addDocument": {
|
|
28
|
+
const input = mutationObject(value, ["input"], "task.addDocument request");
|
|
29
|
+
const raw = mutationObject(input.input, ["markdown", "namespace"], "task.addDocument input");
|
|
30
|
+
const markdown = text(raw.markdown, "markdown");
|
|
31
|
+
if (markdown === undefined)
|
|
32
|
+
throw new TypeError("markdown is required");
|
|
33
|
+
const selected = namespace(raw.namespace);
|
|
34
|
+
if (selected === undefined)
|
|
35
|
+
throw new TypeError("task.addDocument request namespace is required");
|
|
36
|
+
return { action, input: { markdown, namespace: selected } };
|
|
37
|
+
}
|
|
38
|
+
case "task.compose": {
|
|
39
|
+
const input = mutationObject(value, ["markdown", "namespace"], "task.compose request");
|
|
40
|
+
const markdown = text(input.markdown, "markdown");
|
|
41
|
+
if (markdown === undefined)
|
|
42
|
+
throw new TypeError("markdown is required");
|
|
43
|
+
const selected = namespace(input.namespace);
|
|
44
|
+
if (selected === undefined)
|
|
45
|
+
throw new TypeError("task.compose request namespace is required");
|
|
46
|
+
return { action, markdown, namespace: selected };
|
|
47
|
+
}
|
|
48
|
+
case "task.update": {
|
|
49
|
+
const request = mutationObject(value, ["id", "input"], "task.update request");
|
|
50
|
+
const raw = mutationObject(request.input, [
|
|
51
|
+
"title", "body", "appendBody", "note", "priority", "needs", "addNeeds", "dropNeeds", "parent",
|
|
52
|
+
"supersedes", "addSupersedes", "dropSupersedes", "relates", "addRelates", "dropRelates",
|
|
53
|
+
], "task.update input");
|
|
54
|
+
return { action, id: taskId(request.id), input: updateInput(raw) };
|
|
55
|
+
}
|
|
56
|
+
case "task.start":
|
|
57
|
+
case "task.stop":
|
|
58
|
+
case "task.resume": {
|
|
59
|
+
const request = mutationObject(value, ["id"], `${action} request`);
|
|
60
|
+
return { action, id: taskId(request.id) };
|
|
61
|
+
}
|
|
62
|
+
case "task.hold": {
|
|
63
|
+
const request = mutationObject(value, ["ids"], "task.hold request");
|
|
64
|
+
return { action, ids: taskIds(request.ids, "ids") ?? [] };
|
|
65
|
+
}
|
|
66
|
+
case "task.done":
|
|
67
|
+
case "task.drop": {
|
|
68
|
+
const request = mutationObject(value, ["ids", "note"], `${action} request`);
|
|
69
|
+
const note = text(request.note, "note");
|
|
70
|
+
return { action, ids: taskIds(request.ids, "ids") ?? [], ...(note === undefined ? {} : { note }) };
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
export async function executeTaskMutation(input) {
|
|
75
|
+
const { Tasks } = await import("./index.js");
|
|
76
|
+
const tasks = Tasks.of(input.world);
|
|
77
|
+
const { request, signal } = input;
|
|
78
|
+
const withSignal = signal === undefined ? {} : { signal };
|
|
79
|
+
switch (request.action) {
|
|
80
|
+
case "task.add": return await tasks.add({ ...request.input, actor: input.requester, ...withSignal });
|
|
81
|
+
case "task.addDocument": return await tasks.addDocument({ ...request.input, actor: input.requester, ...withSignal });
|
|
82
|
+
case "task.compose": return await tasks.compose({ markdown: request.markdown, namespace: request.namespace, actor: input.requester, ...withSignal });
|
|
83
|
+
case "task.update": return await tasks.task({ id: request.id }).update({ ...request.input, ...withSignal });
|
|
84
|
+
case "task.start": return await tasks.task({ id: request.id }).start(withSignal);
|
|
85
|
+
case "task.stop": return await tasks.task({ id: request.id }).stop(withSignal);
|
|
86
|
+
case "task.resume": return await tasks.task({ id: request.id }).resume(withSignal);
|
|
87
|
+
case "task.hold": return await tasks.batch({ verb: "hold", ids: request.ids, ...withSignal });
|
|
88
|
+
case "task.done": return await tasks.batch({ verb: "done", ids: request.ids, ...withSignal,
|
|
89
|
+
...(request.note === undefined ? {} : { note: request.note }) });
|
|
90
|
+
case "task.drop": return await tasks.batch({ verb: "drop", ids: request.ids, ...withSignal,
|
|
91
|
+
...(request.note === undefined ? {} : { note: request.note }) });
|
|
92
|
+
}
|
|
93
|
+
}
|
|
@@ -6,6 +6,7 @@ import { type TaskPage, type TaskQueryExpression, type TaskQueryRow, type TaskQu
|
|
|
6
6
|
export type TaskView = Readonly<TaskDocument & {
|
|
7
7
|
namespace: readonly string[];
|
|
8
8
|
}>;
|
|
9
|
+
export declare function nukeTask(world: WorldRoot): Promise<void>;
|
|
9
10
|
export type TaskRefusal = Readonly<{
|
|
10
11
|
kind: "task-missing";
|
|
11
12
|
taskId: TaskId;
|
|
@@ -105,19 +106,26 @@ export type UpdateTaskInput = Readonly<{
|
|
|
105
106
|
signal?: AbortSignal;
|
|
106
107
|
}>;
|
|
107
108
|
export declare function taskView(document: TaskDocument): TaskView;
|
|
109
|
+
/** Internal Task timestamp rule shared by ordinary mutations and compose. */
|
|
110
|
+
export declare function advanceTaskTimestamp(previous: string, candidate: string): string;
|
|
108
111
|
export declare function addTask(world: WorldRoot, input: AddTaskInput): Promise<TaskMutationResult>;
|
|
109
112
|
export declare function addTaskDocument(world: WorldRoot, input: AddTaskDocumentInput): Promise<TaskMutationResult>;
|
|
110
113
|
export declare function updateTask(world: WorldRoot, id: TaskId, input: UpdateTaskInput): Promise<TaskUpdateResult>;
|
|
111
114
|
export declare function lifecycleTask(world: WorldRoot, id: TaskId, verb: TaskLifecycleVerb, signal?: AbortSignal, note?: string): Promise<TaskMutationResult>;
|
|
112
115
|
export declare function batchTasks(world: WorldRoot, verb: "done" | "drop" | "hold", ids: readonly TaskId[], signal?: AbortSignal, note?: string): Promise<TaskBatchResult>;
|
|
113
116
|
export declare function settleTask(world: WorldRoot, id: TaskId): Promise<SettledTaskResult>;
|
|
114
|
-
export declare function listTasks(world: WorldRoot, selection: "active" | "closed" | "all", scope?: "namespace" | "world", limit?: number): Promise<TaskOutcome<TaskPage<TaskRow>>>;
|
|
115
|
-
export declare function readyTasks(world: WorldRoot, scope?: "namespace" | "world", parent?: TaskId, limit?: number): Promise<TaskOutcome<TaskPage<TaskRow>>>;
|
|
116
|
-
export declare function blockedTasks(world: WorldRoot, scope?: "namespace" | "world", parent?: TaskId, limit?: number): Promise<TaskOutcome<TaskPage<BlockedTaskRow>>>;
|
|
117
|
-
export declare function queryTasks(
|
|
117
|
+
export declare function listTasks(world: WorldRoot, namespace: readonly string[] | undefined, selection: "active" | "closed" | "all", scope?: "namespace" | "world", limit?: number): Promise<TaskOutcome<TaskPage<TaskRow>>>;
|
|
118
|
+
export declare function readyTasks(world: WorldRoot, namespace: readonly string[] | undefined, scope?: "namespace" | "world", parent?: TaskId, limit?: number): Promise<TaskOutcome<TaskPage<TaskRow>>>;
|
|
119
|
+
export declare function blockedTasks(world: WorldRoot, namespace: readonly string[] | undefined, scope?: "namespace" | "world", parent?: TaskId, limit?: number): Promise<TaskOutcome<TaskPage<BlockedTaskRow>>>;
|
|
120
|
+
export declare function queryTasks(input: Readonly<{
|
|
121
|
+
world: WorldRoot;
|
|
122
|
+
namespace?: readonly string[];
|
|
123
|
+
expression: TaskQueryExpression;
|
|
124
|
+
scope?: "namespace" | "world";
|
|
125
|
+
sort?: TaskQuerySort;
|
|
126
|
+
limit?: number;
|
|
127
|
+
}>): Promise<TaskOutcome<TaskPage<TaskQueryRow>>>;
|
|
118
128
|
/** Internal composite observation from one complete Task board read. */
|
|
119
129
|
export declare function observeTaskBoard(world: WorldRoot): Promise<TaskBoardObservation>;
|
|
120
130
|
/** Internal identity catalog from one complete Task board read. */
|
|
121
131
|
export declare function observeTaskCatalogRows(world: WorldRoot): Promise<readonly TaskRow[]>;
|
|
122
|
-
export declare function setCurrentNamespace(world: WorldRoot, namespace: readonly string[]): Promise<void>;
|
|
123
|
-
export declare function currentNamespace(world: WorldRoot): Promise<readonly string[] | TaskRefusal>;
|