@astrosheep/keiyaku 4.0.1 → 4.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +137 -5
- 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 +3 -3
- package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-akuma/SKILL.md +91 -35
- package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-bind/SKILL.md +89 -58
- package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-workflow/SKILL.md +146 -53
- package/build/src/akuma/abort.d.ts +2 -0
- package/build/src/akuma/abort.js +50 -0
- package/build/src/akuma/akuma.d.ts +23 -14
- package/build/src/akuma/akuma.js +120 -104
- package/build/src/akuma/archetype.d.ts +7 -6
- package/build/src/akuma/archetype.js +68 -41
- package/build/src/akuma/body.d.ts +3 -5
- package/build/src/akuma/body.js +417 -214
- package/build/src/akuma/heart/facts.d.ts +25 -34
- package/build/src/akuma/heart/facts.js +18 -7
- package/build/src/akuma/heart/index.d.ts +39 -42
- package/build/src/akuma/heart/index.js +134 -93
- package/build/src/akuma/heart/rows.d.ts +9 -31
- package/build/src/akuma/heart/rows.js +26 -44
- package/build/src/akuma/heart/schema.d.ts +1 -1
- package/build/src/akuma/heart/schema.js +7 -15
- package/build/src/akuma/heart/soul.d.ts +8 -0
- package/build/src/akuma/heart/soul.js +136 -0
- package/build/src/akuma/heart/storage.d.ts +29 -14
- package/build/src/akuma/heart/storage.js +91 -26
- package/build/src/akuma/identity.d.ts +2 -2
- package/build/src/akuma/identity.js +9 -9
- package/build/src/akuma/index.d.ts +6 -1
- package/build/src/akuma/index.js +6 -0
- package/build/src/akuma/projection.d.ts +117 -38
- package/build/src/akuma/projection.js +208 -97
- package/build/src/akuma/provider-recipe.d.ts +24 -0
- package/build/src/akuma/provider-recipe.js +100 -0
- package/build/src/akuma/provider.d.ts +12 -4
- package/build/src/akuma/provider.js +117 -70
- package/build/src/akuma/providers/acp/core.d.ts +19 -0
- package/build/src/akuma/providers/acp/core.js +159 -0
- package/build/src/akuma/providers/acp/events.d.ts +20 -0
- package/build/src/akuma/providers/acp/events.js +73 -0
- package/build/src/akuma/providers/acp/index.d.ts +12 -0
- package/build/src/akuma/providers/acp/index.js +94 -0
- package/build/src/akuma/providers/claude/events.js +66 -13
- package/build/src/akuma/providers/claude/index.d.ts +2 -1
- package/build/src/akuma/providers/claude/index.js +35 -21
- package/build/src/akuma/providers/codex-app-server/events.js +4 -1
- package/build/src/akuma/providers/codex-app-server/index.d.ts +3 -3
- package/build/src/akuma/providers/codex-app-server/index.js +24 -31
- package/build/src/akuma/providers/grok-build/index.d.ts +4 -0
- package/build/src/akuma/providers/grok-build/index.js +70 -0
- package/build/src/akuma/providers/index.d.ts +5 -3
- package/build/src/akuma/providers/index.js +24 -59
- package/build/src/akuma/providers/opencode-sdk/events.d.ts +40 -34
- package/build/src/akuma/providers/opencode-sdk/events.js +220 -101
- package/build/src/akuma/providers/opencode-sdk/index.d.ts +3 -2
- package/build/src/akuma/providers/opencode-sdk/index.js +233 -128
- package/build/src/akuma/providers/opencode-sdk/session.d.ts +11 -26
- package/build/src/akuma/providers/opencode-sdk/session.js +10 -9
- package/build/src/akuma/providers/pi/events.js +49 -11
- package/build/src/akuma/providers/pi/index.d.ts +1 -1
- package/build/src/akuma/providers/pi/index.js +50 -21
- package/build/src/akuma/publication.d.ts +3 -3
- package/build/src/akuma/publication.js +61 -44
- package/build/src/akuma/requests.d.ts +9 -7
- package/build/src/akuma/requests.js +92 -65
- package/build/src/alias/index.d.ts +2 -2
- package/build/src/alias/index.js +10 -10
- package/build/src/body/decode.js +7 -1
- package/build/src/body/region.d.ts +2 -0
- package/build/src/body/region.js +77 -7
- package/build/src/cli/accepted.d.ts +17 -13
- package/build/src/cli/accepted.js +72 -34
- package/build/src/cli/actor.js +3 -3
- package/build/src/cli/commands/akuma-invoke.d.ts +4 -3
- package/build/src/cli/commands/akuma-invoke.js +14 -9
- package/build/src/cli/commands/akuma.d.ts +13 -3
- package/build/src/cli/commands/akuma.js +67 -34
- package/build/src/cli/commands/contract.d.ts +18 -5
- package/build/src/cli/commands/contract.js +9 -3
- package/build/src/cli/commands/install.js +2 -2
- package/build/src/cli/commands/task-invoke.d.ts +17 -4
- package/build/src/cli/commands/task-invoke.js +43 -20
- package/build/src/cli/commands/task-query.d.ts +5 -0
- package/build/src/cli/commands/task-query.js +247 -0
- package/build/src/cli/commands/task.d.ts +3 -1
- package/build/src/cli/commands/task.js +54 -16
- package/build/src/cli/coordinates.d.ts +18 -0
- package/build/src/cli/coordinates.js +120 -0
- package/build/src/cli/draft.d.ts +8 -0
- package/build/src/cli/draft.js +96 -0
- package/build/src/cli/invoke.d.ts +4 -3
- package/build/src/cli/invoke.js +160 -179
- package/build/src/cli/main.js +34 -6
- package/build/src/cli/parse.d.ts +9 -2
- package/build/src/cli/parse.js +31 -9
- package/build/src/cli/render/akuma-tool-command.d.ts +6 -0
- package/build/src/cli/render/akuma-tool-command.js +144 -0
- package/build/src/cli/render/akuma-tool.d.ts +2 -2
- package/build/src/cli/render/akuma-tool.js +30 -4
- package/build/src/cli/render/akuma.d.ts +1 -0
- package/build/src/cli/render/akuma.js +211 -116
- package/build/src/cli/render/audit.d.ts +3 -0
- package/build/src/cli/render/audit.js +104 -0
- package/build/src/cli/render/contract.d.ts +3 -3
- package/build/src/cli/render/contract.js +192 -79
- package/build/src/cli/render/kanshi.js +263 -168
- package/build/src/cli/render/receipt.d.ts +19 -0
- package/build/src/cli/render/receipt.js +106 -0
- package/build/src/cli/render/refusal.d.ts +16 -2
- package/build/src/cli/render/refusal.js +88 -20
- package/build/src/cli/render/region.d.ts +2 -0
- package/build/src/cli/render/region.js +17 -0
- package/build/src/cli/render/task.d.ts +2 -1
- package/build/src/cli/render/task.js +223 -60
- package/build/src/cli/render/terminal.d.ts +7 -0
- package/build/src/cli/render/terminal.js +46 -0
- package/build/src/cli/render/text.js +6 -3
- package/build/src/cli/result.d.ts +115 -20
- package/build/src/cli/usage.d.ts +3 -0
- package/build/src/cli/usage.js +7 -0
- package/build/src/contract-worktree.d.ts +6 -5
- package/build/src/contract-worktree.js +95 -57
- package/build/src/coordination/durable-file.d.ts +4 -3
- package/build/src/coordination/durable-file.js +29 -25
- package/build/src/coordination/sqlite-transaction-lock.d.ts +4 -0
- package/build/src/coordination/sqlite-transaction-lock.js +22 -5
- package/build/src/core/facts/fold.js +1 -6
- package/build/src/core/facts/gate.d.ts +1 -0
- package/build/src/core/facts/gate.js +1 -0
- package/build/src/core/verbs/amend.d.ts +1 -1
- package/build/src/core/verbs/amend.js +0 -11
- package/build/src/core/verbs/deliver.d.ts +1 -1
- package/build/src/core/verbs/deliver.js +1 -7
- package/build/src/core/verbs/placement.d.ts +1 -1
- package/build/src/core/verbs/placement.js +4 -1
- package/build/src/dispatch/index.d.ts +2 -2
- package/build/src/dispatch/index.js +17 -17
- package/build/src/git/admission.d.ts +1 -1
- package/build/src/git/admission.js +11 -11
- package/build/src/git/hooks.js +13 -13
- package/build/src/git/integration.d.ts +14 -15
- package/build/src/git/integration.js +80 -44
- package/build/src/git/observe.d.ts +5 -3
- package/build/src/git/observe.js +18 -12
- package/build/src/git/read-observation.js +10 -9
- package/build/src/git/reconcile.d.ts +2 -0
- package/build/src/git/reconcile.js +138 -95
- package/build/src/git/repository.d.ts +27 -19
- package/build/src/git/repository.js +155 -83
- package/build/src/git/scratch.d.ts +7 -3
- package/build/src/git/scratch.js +44 -37
- package/build/src/git/target-placement.d.ts +40 -4
- package/build/src/git/target-placement.js +221 -101
- package/build/src/git/tender.d.ts +6 -5
- package/build/src/git/tender.js +24 -43
- package/build/src/git/terminal-seal.d.ts +1 -1
- package/build/src/git/terminal-seal.js +7 -5
- package/build/src/git/workspace.d.ts +36 -4
- package/build/src/git/workspace.js +53 -18
- package/build/src/identity/selector.js +1 -1
- package/build/src/index.d.ts +3 -3
- package/build/src/index.js +1 -1
- package/build/src/kanshi/index.d.ts +2 -2
- package/build/src/kanshi/index.js +1 -1
- package/build/src/kanshi/read.d.ts +2 -1
- package/build/src/kanshi/read.js +118 -27
- package/build/src/kanshi/report.d.ts +50 -0
- package/build/src/kanshi/select.d.ts +5 -0
- package/build/src/kanshi/select.js +32 -0
- package/build/src/library/address.d.ts +12 -3
- package/build/src/library/address.js +77 -46
- package/build/src/library/akuma-creation.d.ts +6 -1
- package/build/src/library/akuma-creation.js +86 -13
- package/build/src/library/audit.d.ts +18 -0
- package/build/src/library/audit.js +44 -0
- package/build/src/library/bind.js +4 -4
- package/build/src/library/catalog.d.ts +1 -1
- package/build/src/library/catalog.js +5 -8
- package/build/src/library/contract.d.ts +5 -6
- package/build/src/library/contract.js +11 -35
- package/build/src/library/delivery.d.ts +2 -1
- package/build/src/library/fleet.d.ts +2 -2
- package/build/src/library/fleet.js +35 -71
- package/build/src/library/keiyaku.d.ts +3 -2
- package/build/src/library/keiyaku.js +1 -0
- package/build/src/library/mutation.d.ts +7 -6
- package/build/src/library/mutation.js +23 -25
- package/build/src/library/reconcile.d.ts +29 -0
- package/build/src/library/reconcile.js +179 -0
- package/build/src/library/region.d.ts +12 -0
- package/build/src/library/region.js +15 -1
- package/build/src/library/repo.d.ts +4 -16
- package/build/src/library/repo.js +13 -38
- package/build/src/protocol/attempt.js +6 -6
- package/build/src/protocol/bind.js +4 -4
- package/build/src/protocol/intent.d.ts +15 -2
- package/build/src/protocol/intent.js +21 -6
- package/build/src/protocol/operations.d.ts +73 -22
- package/build/src/protocol/operations.js +292 -88
- package/build/src/protocol/outcome.d.ts +9 -3
- package/build/src/protocol/outcome.js +3 -1
- package/build/src/protocol/placement.d.ts +15 -4
- package/build/src/protocol/placement.js +58 -25
- package/build/src/protocol/read/status.d.ts +8 -1
- package/build/src/protocol/read/status.js +61 -14
- package/build/src/runtime/proc/line-rpc.d.ts +3 -8
- package/build/src/runtime/proc/line-rpc.js +16 -43
- package/build/src/runtime/proc/run.d.ts +15 -29
- package/build/src/runtime/proc/run.js +113 -105
- package/build/src/runtime/proc/stdio.d.ts +18 -0
- package/build/src/runtime/proc/stdio.js +58 -0
- package/build/src/settings.d.ts +2 -2
- package/build/src/settings.js +8 -8
- package/build/src/settlement/fence.d.ts +0 -4
- package/build/src/settlement/fence.js +0 -9
- package/build/src/settlement/holder.d.ts +11 -1
- package/build/src/settlement/holder.js +25 -0
- package/build/src/settlement/settle.d.ts +1 -1
- package/build/src/settlement/settle.js +52 -89
- package/build/src/task/board.d.ts +3 -3
- package/build/src/task/board.js +6 -9
- package/build/src/task/compose.d.ts +1 -1
- package/build/src/task/compose.js +18 -10
- package/build/src/task/context.d.ts +3 -3
- package/build/src/task/context.js +14 -14
- package/build/src/task/document.d.ts +2 -1
- package/build/src/task/document.js +15 -5
- package/build/src/task/index.d.ts +19 -7
- package/build/src/task/index.js +42 -23
- package/build/src/task/operations.d.ts +15 -9
- package/build/src/task/operations.js +78 -48
- package/build/src/task/query.d.ts +67 -0
- package/build/src/task/query.js +279 -0
- package/build/src/task/store.d.ts +2 -2
- package/build/src/task/store.js +40 -25
- package/build/src/verification/execution.d.ts +4 -2
- package/build/src/verification/execution.js +9 -5
- package/build/src/workspace-place.d.ts +41 -0
- package/build/src/workspace-place.js +386 -0
- package/build/src/world.d.ts +13 -2
- package/build/src/world.js +81 -48
- package/package.json +3 -2
- package/build/src/protocol/read/audit.d.ts +0 -31
- package/build/src/protocol/read/audit.js +0 -55
package/build/src/task/index.js
CHANGED
|
@@ -2,8 +2,9 @@ import { buildTree, diagnoseBoard, projectDetailFacts } from "./board.js";
|
|
|
2
2
|
import { composeTasks } from "./compose.js";
|
|
3
3
|
import { TaskAuthorityCorruptionError } from "./document.js";
|
|
4
4
|
import { isTaskSegment, parseTaskId } from "./identity.js";
|
|
5
|
-
import { addTask, addTaskDocument, batchTasks, blockedTasks, currentNamespace, lifecycleTask, listTasks, readyTasks, setCurrentNamespace, taskView, updateTask, } from "./operations.js";
|
|
5
|
+
import { addTask, addTaskDocument, batchTasks, blockedTasks, currentNamespace, lifecycleTask, listTasks, queryTasks, readyTasks, setCurrentNamespace, taskView, updateTask, } from "./operations.js";
|
|
6
6
|
import { readBoard } from "./store.js";
|
|
7
|
+
import { isValidTaskLimit, normalizeTaskQuery, MAX_TASK_LIMIT, DEFAULT_TASK_LIMIT } from "./query.js";
|
|
7
8
|
export { TaskAuthorityCorruptionError };
|
|
8
9
|
function record(value, label) {
|
|
9
10
|
if (typeof value !== "object" || value === null || Array.isArray(value))
|
|
@@ -48,9 +49,26 @@ function namespace(value) {
|
|
|
48
49
|
function text(value, label) { if (value === undefined)
|
|
49
50
|
return undefined; if (typeof value !== "string")
|
|
50
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
|
+
}
|
|
51
59
|
function priority(value) { if (value === undefined)
|
|
52
60
|
return undefined; if (!Number.isInteger(value) || typeof value !== "number" || value < 0 || value > 3)
|
|
53
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
|
+
}
|
|
54
72
|
function state(value) {
|
|
55
73
|
if (value === undefined)
|
|
56
74
|
return undefined;
|
|
@@ -61,17 +79,18 @@ function state(value) {
|
|
|
61
79
|
function nullableId(value) { return value === undefined || value === null ? value : id(value); }
|
|
62
80
|
function addInput(input) {
|
|
63
81
|
const v = record(input, "add input");
|
|
64
|
-
closed(v, ["title", "namespace", "body", "note", "state", "priority", "needs", "parent", "supersedes", "relates", "signal"], "add input");
|
|
82
|
+
closed(v, ["title", "namespace", "body", "note", "state", "priority", "needs", "parent", "supersedes", "relates", "actor", "signal"], "add input");
|
|
65
83
|
const title = text(v.title, "title");
|
|
66
84
|
if (title === undefined || title.trim() === "")
|
|
67
85
|
throw new TypeError("title is required");
|
|
68
86
|
const ns = namespace(v.namespace), body = text(v.body, "body"), note = text(v.note, "note"), initialState = state(v.state), pri = priority(v.priority);
|
|
69
87
|
const needs = taskIds(v.needs, "needs"), parent = nullableId(v.parent), supersedes = taskIds(v.supersedes, "supersedes"), relates = taskIds(v.relates, "relates");
|
|
70
|
-
const abort = signal(v.signal);
|
|
88
|
+
const createdBy = actor(v.actor), abort = signal(v.signal);
|
|
71
89
|
return { title, ...(ns === undefined ? {} : { namespace: ns }), ...(body === undefined ? {} : { body }), ...(note === undefined ? {} : { note }),
|
|
72
90
|
...(initialState === undefined ? {} : { state: initialState }), ...(pri === undefined ? {} : { priority: pri }),
|
|
73
91
|
...(needs === undefined ? {} : { needs }), ...(parent === undefined ? {} : { parent }),
|
|
74
92
|
...(supersedes === undefined ? {} : { supersedes }), ...(relates === undefined ? {} : { relates }),
|
|
93
|
+
...(createdBy === undefined ? {} : { actor: createdBy }),
|
|
75
94
|
...(abort === undefined ? {} : { signal: abort }) };
|
|
76
95
|
}
|
|
77
96
|
function updateInput(input) {
|
|
@@ -120,15 +139,13 @@ class TaskHandle {
|
|
|
120
139
|
this.world = world;
|
|
121
140
|
}
|
|
122
141
|
async read() {
|
|
123
|
-
const facts = projectDetailFacts(readBoard(this.world).board, this.id);
|
|
142
|
+
const facts = projectDetailFacts((await readBoard(this.world)).board, this.id);
|
|
124
143
|
return facts === null ? null : { ...facts, task: taskView(facts.task) };
|
|
125
144
|
}
|
|
126
|
-
async tree(
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
throw new TypeError("full must be a boolean");
|
|
131
|
-
const node = buildTree(readBoard(this.world).board, this.id, value.full ?? false);
|
|
145
|
+
async tree() {
|
|
146
|
+
if (arguments.length > 0)
|
|
147
|
+
throw new TypeError("tree accepts no input");
|
|
148
|
+
const node = buildTree((await readBoard(this.world)).board, this.id);
|
|
132
149
|
return node === null ? { kind: "refused", refusal: { kind: "task-missing", taskId: this.id } } : { kind: "accepted", value: node };
|
|
133
150
|
}
|
|
134
151
|
update(input) { return updateTask(this.world, this.id, updateInput(input)); }
|
|
@@ -152,26 +169,28 @@ class TasksHandle {
|
|
|
152
169
|
this.world = world;
|
|
153
170
|
this.root = world;
|
|
154
171
|
}
|
|
155
|
-
async namespace() { const value = currentNamespace(this.world); return "kind" in value ? { kind: "refused", refusal: value } : { kind: "accepted", value }; }
|
|
172
|
+
async namespace() { const value = await currentNamespace(this.world); return "kind" in value ? { kind: "refused", refusal: value } : { kind: "accepted", value }; }
|
|
156
173
|
async setNamespace(input) { const v = record(input, "setNamespace input"); closed(v, ["namespace"], "setNamespace input"); const ns = namespace(v.namespace); if (ns === undefined)
|
|
157
|
-
throw new TypeError("namespace is required"); setCurrentNamespace(this.world, ns); }
|
|
174
|
+
throw new TypeError("namespace is required"); await setCurrentNamespace(this.world, ns); }
|
|
158
175
|
task(input) { const v = record(input, "task input"); closed(v, ["id"], "task input"); return new TaskHandle(id(v.id), this.world); }
|
|
159
176
|
add(input) { return addTask(this.world, addInput(input)); }
|
|
160
|
-
addDocument(input) { const v = record(input, "addDocument input"); closed(v, ["markdown", "namespace", "signal"], "addDocument input"); const markdown = text(v.markdown, "markdown"); if (markdown === undefined)
|
|
161
|
-
throw new TypeError("markdown is required"); const ns = namespace(v.namespace), abort = signal(v.signal); return addTaskDocument(this.world, { markdown, ...(ns === undefined ? {} : { namespace: ns }), ...(abort === undefined ? {} : { signal: abort }) }); }
|
|
162
|
-
async list(input = {}) { const v = record(input, "list input"); closed(v, ["selection", "scope"], "list input"); if (v.selection !== undefined && v.selection !== "active" && v.selection !== "closed" && v.selection !== "all")
|
|
177
|
+
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)
|
|
178
|
+
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 }) }); }
|
|
179
|
+
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")
|
|
163
180
|
throw new TypeError("selection must be active, closed, or all"); if (v.scope !== undefined && v.scope !== "namespace" && v.scope !== "world")
|
|
164
|
-
throw new TypeError("scope must be namespace or world"); return listTasks(this.world, v.selection ?? "active", v.scope); }
|
|
165
|
-
async ready(input = {}) { const v = record(input, "ready input"); closed(v, ["scope"], "ready input"); if (v.scope !== undefined && v.scope !== "namespace" && v.scope !== "world")
|
|
166
|
-
throw new TypeError("scope must be namespace or world"); return readyTasks(this.world, v.scope); }
|
|
167
|
-
async blocked(input = {}) { const v = record(input, "blocked input"); closed(v, ["scope"], "blocked input"); if (v.scope !== undefined && v.scope !== "namespace" && v.scope !== "world")
|
|
168
|
-
throw new TypeError("scope must be namespace or world"); return blockedTasks(this.world, v.scope); }
|
|
169
|
-
async
|
|
181
|
+
throw new TypeError("scope must be namespace or world"); return listTasks(this.world, v.selection ?? "active", v.scope, limit(v.limit) ?? DEFAULT_TASK_LIMIT); }
|
|
182
|
+
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")
|
|
183
|
+
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); }
|
|
184
|
+
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")
|
|
185
|
+
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); }
|
|
186
|
+
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")
|
|
187
|
+
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); }
|
|
188
|
+
async doctor() { return { issues: diagnoseBoard((await readBoard(this.world)).board) }; }
|
|
170
189
|
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")
|
|
171
190
|
throw new TypeError("batch verb is invalid"); const note = text(v.note, "note"); if (note !== undefined && verb !== "done" && verb !== "drop")
|
|
172
191
|
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); }
|
|
173
|
-
compose(input) { const v = record(input, "compose input"); closed(v, ["markdown", "signal"], "compose input"); const markdown = text(v.markdown, "markdown"); if (markdown === undefined)
|
|
174
|
-
throw new TypeError("markdown is required"); return composeTasks(this.world, markdown, signal(v.signal)); }
|
|
192
|
+
compose(input) { const v = record(input, "compose input"); closed(v, ["markdown", "actor", "signal"], "compose input"); const markdown = text(v.markdown, "markdown"); if (markdown === undefined)
|
|
193
|
+
throw new TypeError("markdown is required"); return composeTasks(this.world, markdown, signal(v.signal), actor(v.actor)); }
|
|
175
194
|
}
|
|
176
195
|
export const Tasks = Object.freeze({
|
|
177
196
|
of(root) {
|
|
@@ -2,6 +2,7 @@ import { type BlockedTaskRow, type TaskRow } from "./board.js";
|
|
|
2
2
|
import { type TaskDocument, type TaskPriority, type TaskState } from "./document.js";
|
|
3
3
|
import { type TaskId } from "./identity.js";
|
|
4
4
|
import type { WorldRoot } from "../world.js";
|
|
5
|
+
import { type TaskPage, type TaskQueryExpression, type TaskQueryRow, type TaskQuerySort } from "./query.js";
|
|
5
6
|
export type TaskView = Readonly<TaskDocument & {
|
|
6
7
|
namespace: readonly string[];
|
|
7
8
|
}>;
|
|
@@ -51,7 +52,7 @@ export type TaskBatchResult = Readonly<{
|
|
|
51
52
|
outcome: TaskMutationResult;
|
|
52
53
|
}>[];
|
|
53
54
|
}>;
|
|
54
|
-
export type SettledTaskAction = "done"
|
|
55
|
+
export type SettledTaskAction = "done";
|
|
55
56
|
export type SettledTaskResult = Readonly<{
|
|
56
57
|
kind: "changed";
|
|
57
58
|
task: TaskView;
|
|
@@ -76,11 +77,13 @@ export type AddTaskInput = Readonly<{
|
|
|
76
77
|
parent?: TaskId | null;
|
|
77
78
|
supersedes?: readonly TaskId[];
|
|
78
79
|
relates?: readonly TaskId[];
|
|
80
|
+
actor?: string;
|
|
79
81
|
signal?: AbortSignal;
|
|
80
82
|
}>;
|
|
81
83
|
export type AddTaskDocumentInput = Readonly<{
|
|
82
84
|
markdown: string;
|
|
83
85
|
namespace?: readonly string[];
|
|
86
|
+
actor?: string;
|
|
84
87
|
signal?: AbortSignal;
|
|
85
88
|
}>;
|
|
86
89
|
export type UpdateTaskInput = Readonly<{
|
|
@@ -107,12 +110,13 @@ export declare function addTaskDocument(world: WorldRoot, input: AddTaskDocument
|
|
|
107
110
|
export declare function updateTask(world: WorldRoot, id: TaskId, input: UpdateTaskInput): Promise<TaskUpdateResult>;
|
|
108
111
|
export declare function lifecycleTask(world: WorldRoot, id: TaskId, verb: TaskLifecycleVerb, signal?: AbortSignal, note?: string): Promise<TaskMutationResult>;
|
|
109
112
|
export declare function batchTasks(world: WorldRoot, verb: "done" | "drop" | "hold", ids: readonly TaskId[], signal?: AbortSignal, note?: string): Promise<TaskBatchResult>;
|
|
110
|
-
export declare function settleTask(world: WorldRoot, id: TaskId
|
|
111
|
-
export declare function listTasks(world: WorldRoot, selection: "active" | "closed" | "all", scope?: "namespace" | "world"): TaskOutcome<
|
|
112
|
-
export declare function readyTasks(world: WorldRoot, scope?: "namespace" | "world"): TaskOutcome<
|
|
113
|
-
export declare function blockedTasks(world: WorldRoot, scope?: "namespace" | "world"): TaskOutcome<
|
|
113
|
+
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(world: WorldRoot, expression: TaskQueryExpression, scope?: "namespace" | "world", sort?: TaskQuerySort, limit?: number): Promise<TaskOutcome<TaskPage<TaskQueryRow>>>;
|
|
114
118
|
/** Internal composite observation from one complete Task board read. */
|
|
115
|
-
export declare function observeTaskStatusRows(world: WorldRoot): (Readonly<{
|
|
119
|
+
export declare function observeTaskStatusRows(world: WorldRoot): Promise<(Readonly<{
|
|
116
120
|
id: TaskId;
|
|
117
121
|
title: string;
|
|
118
122
|
state: TaskState;
|
|
@@ -129,6 +133,8 @@ export declare function observeTaskStatusRows(world: WorldRoot): (Readonly<{
|
|
|
129
133
|
state: TaskState;
|
|
130
134
|
priority: TaskPriority;
|
|
131
135
|
disposition: import("./board.js").TaskDisposition;
|
|
132
|
-
})[]
|
|
133
|
-
|
|
134
|
-
export declare function
|
|
136
|
+
})[]>;
|
|
137
|
+
/** Internal identity catalog from one complete Task board read. */
|
|
138
|
+
export declare function observeTaskCatalogRows(world: WorldRoot): Promise<readonly TaskRow[]>;
|
|
139
|
+
export declare function setCurrentNamespace(world: WorldRoot, namespace: readonly string[]): Promise<void>;
|
|
140
|
+
export declare function currentNamespace(world: WorldRoot): Promise<readonly string[] | TaskRefusal>;
|
|
@@ -1,19 +1,21 @@
|
|
|
1
|
+
import { resolve } from "node:path";
|
|
1
2
|
import { documentDiff } from "../markdown/diff.js";
|
|
2
3
|
import { installNamespaceContext, readNamespaceContext } from "./context.js";
|
|
3
|
-
import { relationProblem, projectBlocked,
|
|
4
|
+
import { relationProblem, projectBlocked, projectRows, projectStatusRows } from "./board.js";
|
|
4
5
|
import { parseTaskCreationDocument, serializeTaskDocument } from "./document.js";
|
|
5
6
|
import { allocateLocalId, deriveLocalStem, formatTaskId, parseTaskId, sameNamespace } from "./identity.js";
|
|
6
7
|
import { authorityPath, readBoard, replaceAuthority, withTaskLocks, } from "./store.js";
|
|
8
|
+
import { DEFAULT_TASK_LIMIT, projectPage, projectQuery, queryUnderTargets, underExpression } from "./query.js";
|
|
7
9
|
export function taskView(document) {
|
|
8
10
|
return { ...document, namespace: parseTaskId(document.id).namespace };
|
|
9
11
|
}
|
|
10
12
|
function refused(refusal) { return { kind: "refused", refusal }; }
|
|
11
13
|
function retry(reason) { return { kind: "retry", reason }; }
|
|
12
|
-
function context(world, explicit) {
|
|
14
|
+
async function context(world, explicit) {
|
|
13
15
|
if (explicit !== undefined)
|
|
14
16
|
return explicit;
|
|
15
|
-
const current = readNamespaceContext(world);
|
|
16
|
-
return current === "malformed" ? { kind: "invalid-namespace-context", path:
|
|
17
|
+
const current = await readNamespaceContext(world);
|
|
18
|
+
return current === "malformed" ? { kind: "invalid-namespace-context", path: resolve(world, ".keiyaku", "namespace", "current") }
|
|
17
19
|
: current === "absent" ? [] : current;
|
|
18
20
|
}
|
|
19
21
|
function occupied(board, namespace) {
|
|
@@ -27,40 +29,44 @@ function advancedTimestamp(previous) {
|
|
|
27
29
|
const current = currentTimestamp();
|
|
28
30
|
return current > previous ? current : new Date(Date.parse(previous) + 1).toISOString();
|
|
29
31
|
}
|
|
30
|
-
function addDocument(base, namespace, board, at) {
|
|
32
|
+
function addDocument(base, namespace, board, at, actor) {
|
|
31
33
|
const localId = allocateLocalId(deriveLocalStem(base.title), occupied(board, namespace));
|
|
32
|
-
return {
|
|
34
|
+
return {
|
|
35
|
+
...base, id: formatTaskId({ namespace, localId }),
|
|
36
|
+
...(actor === undefined ? {} : { createdBy: actor }),
|
|
37
|
+
createdAt: at, updatedAt: at,
|
|
38
|
+
};
|
|
33
39
|
}
|
|
34
40
|
function boardWith(board, document) {
|
|
35
41
|
const tasks = new Map(board.tasks);
|
|
36
42
|
tasks.set(document.id, document);
|
|
37
43
|
return { tasks };
|
|
38
44
|
}
|
|
39
|
-
async function create(world, base, namespace, signal) {
|
|
45
|
+
async function create(world, base, namespace, signal, actor) {
|
|
40
46
|
const result = await withTaskLocks({ world, allocation: true, ids: [], ...(signal === undefined ? {} : { signal }) }, async () => {
|
|
41
|
-
const snapshot = readBoard(world), next = addDocument(base, namespace, snapshot.board, currentTimestamp());
|
|
47
|
+
const snapshot = await readBoard(world), next = addDocument(base, namespace, snapshot.board, currentTimestamp(), actor);
|
|
42
48
|
const problem = relationProblem(boardWith(snapshot.board, next), null, next);
|
|
43
49
|
if (problem !== null)
|
|
44
50
|
return refused({ kind: "invalid-graph", diagnostic: problem });
|
|
45
|
-
const replaced = replaceAuthority({ path: authorityPath(world, next.id), expected: null, next: serializeTaskDocument(next) });
|
|
51
|
+
const replaced = await replaceAuthority({ path: authorityPath(world, next.id), expected: null, next: serializeTaskDocument(next) });
|
|
46
52
|
return replaced === "replaced" ? { kind: "accepted", value: taskView(next) } : retry("concurrent-modification");
|
|
47
53
|
});
|
|
48
54
|
return result === "busy" ? retry("busy") : result;
|
|
49
55
|
}
|
|
50
56
|
export async function addTask(world, input) {
|
|
51
|
-
const namespace = context(world, input.namespace);
|
|
57
|
+
const namespace = await context(world, input.namespace);
|
|
52
58
|
if (!Array.isArray(namespace))
|
|
53
59
|
return refused(namespace);
|
|
54
60
|
return create(world, {
|
|
55
61
|
title: input.title, body: input.body ?? "", note: input.note ?? "", state: input.state ?? "open", priority: input.priority ?? 2, needs: input.needs ?? [], parent: input.parent ?? null,
|
|
56
62
|
supersedes: input.supersedes ?? [], relates: input.relates ?? [],
|
|
57
|
-
}, namespace, input.signal);
|
|
63
|
+
}, namespace, input.signal, input.actor);
|
|
58
64
|
}
|
|
59
65
|
export async function addTaskDocument(world, input) {
|
|
60
|
-
const namespace = context(world, input.namespace);
|
|
66
|
+
const namespace = await context(world, input.namespace);
|
|
61
67
|
if (!Array.isArray(namespace))
|
|
62
68
|
return refused(namespace);
|
|
63
|
-
return create(world, parseTaskCreationDocument(input.markdown), namespace, input.signal);
|
|
69
|
+
return create(world, parseTaskCreationDocument(input.markdown), namespace, input.signal, input.actor);
|
|
64
70
|
}
|
|
65
71
|
function listChange(current, replacement, additions, removals) {
|
|
66
72
|
const next = [...(replacement ?? current)];
|
|
@@ -93,7 +99,7 @@ function updateDocument(board, current, input) {
|
|
|
93
99
|
}
|
|
94
100
|
export async function updateTask(world, id, input) {
|
|
95
101
|
const result = await withTaskLocks({ world, allocation: false, ids: [id], ...(input.signal === undefined ? {} : { signal: input.signal }) }, async () => {
|
|
96
|
-
const snapshot = readBoard(world), current = snapshot.board.tasks.get(id);
|
|
102
|
+
const snapshot = await readBoard(world), current = snapshot.board.tasks.get(id);
|
|
97
103
|
if (current === undefined)
|
|
98
104
|
return { kind: "refused", refusal: { kind: "task-missing", taskId: id } };
|
|
99
105
|
const candidate = updateDocument(snapshot.board, current, input);
|
|
@@ -106,7 +112,7 @@ export async function updateTask(world, id, input) {
|
|
|
106
112
|
const changed = !Buffer.from(serializeTaskDocument(candidate)).equals(Buffer.from(predecessor));
|
|
107
113
|
const next = changed ? { ...candidate, updatedAt: advancedTimestamp(current.updatedAt) } : candidate;
|
|
108
114
|
const afterBytes = serializeTaskDocument(next), after = Buffer.from(afterBytes).toString("utf8");
|
|
109
|
-
if (before !== after && replaceAuthority({ path: authorityPath(world, id), expected: predecessor, next: afterBytes }) !== "replaced")
|
|
115
|
+
if (before !== after && await replaceAuthority({ path: authorityPath(world, id), expected: predecessor, next: afterBytes }) !== "replaced")
|
|
110
116
|
return { kind: "retry", reason: "concurrent-modification" };
|
|
111
117
|
const label = `${id}.md`;
|
|
112
118
|
return { kind: "accepted", value: { task: taskView(next), documentDiff: documentDiff(label, label, before, after) } };
|
|
@@ -120,7 +126,7 @@ const TRANSITIONS = {
|
|
|
120
126
|
};
|
|
121
127
|
export async function lifecycleTask(world, id, verb, signal, note) {
|
|
122
128
|
const result = await withTaskLocks({ world, allocation: false, ids: [id], ...(signal === undefined ? {} : { signal }) }, async () => {
|
|
123
|
-
const snapshot = readBoard(world), current = snapshot.board.tasks.get(id);
|
|
129
|
+
const snapshot = await readBoard(world), current = snapshot.board.tasks.get(id);
|
|
124
130
|
if (current === undefined)
|
|
125
131
|
return refused({ kind: "task-missing", taskId: id });
|
|
126
132
|
const state = TRANSITIONS[verb][current.state];
|
|
@@ -128,7 +134,7 @@ export async function lifecycleTask(world, id, verb, signal, note) {
|
|
|
128
134
|
return refused({ kind: "invalid-lifecycle-transition", taskId: id, state: current.state, verb });
|
|
129
135
|
const next = { ...current, state, ...(note === undefined ? {} : { note }), updatedAt: advancedTimestamp(current.updatedAt) };
|
|
130
136
|
const bytes = serializeTaskDocument(next);
|
|
131
|
-
return replaceAuthority({ path: authorityPath(world, id), expected: snapshot.bytes.get(id), next: bytes }) === "replaced"
|
|
137
|
+
return await replaceAuthority({ path: authorityPath(world, id), expected: snapshot.bytes.get(id), next: bytes }) === "replaced"
|
|
132
138
|
? { kind: "accepted", value: taskView(next) } : retry("concurrent-modification");
|
|
133
139
|
});
|
|
134
140
|
return result === "busy" ? retry("busy") : result;
|
|
@@ -141,58 +147,82 @@ export async function batchTasks(world, verb, ids, signal, note) {
|
|
|
141
147
|
}
|
|
142
148
|
return { items };
|
|
143
149
|
}
|
|
144
|
-
export async function settleTask(world, id
|
|
150
|
+
export async function settleTask(world, id) {
|
|
145
151
|
const result = await withTaskLocks({ world, allocation: false, ids: [id] }, async () => {
|
|
146
|
-
const snapshot = readBoard(world), current = snapshot.board.tasks.get(id);
|
|
152
|
+
const snapshot = await readBoard(world), current = snapshot.board.tasks.get(id);
|
|
147
153
|
if (current === undefined)
|
|
148
154
|
return { kind: "refused", refusal: { kind: "task-missing", taskId: id } };
|
|
149
|
-
if (
|
|
150
|
-
if (current.state === "done")
|
|
151
|
-
return { kind: "unchanged" };
|
|
152
|
-
if (current.state === "drop") {
|
|
153
|
-
return { kind: "refused", refusal: { kind: "invalid-lifecycle-transition", taskId: id, state: current.state, verb: "done" } };
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
else if (current.state !== "done")
|
|
155
|
+
if (current.state === "done")
|
|
157
156
|
return { kind: "unchanged" };
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
157
|
+
if (current.state === "drop") {
|
|
158
|
+
return { kind: "refused", refusal: { kind: "invalid-lifecycle-transition", taskId: id, state: current.state, verb: "done" } };
|
|
159
|
+
}
|
|
160
|
+
const next = { ...current, state: "done", updatedAt: advancedTimestamp(current.updatedAt) };
|
|
161
|
+
const replaced = await replaceAuthority({ path: authorityPath(world, id), expected: snapshot.bytes.get(id), next: serializeTaskDocument(next) });
|
|
161
162
|
return replaced === "replaced"
|
|
162
|
-
? { kind: "changed", task: taskView(next), action:
|
|
163
|
+
? { kind: "changed", task: taskView(next), action: "done" }
|
|
163
164
|
: { kind: "retry", reason: "concurrent-modification" };
|
|
164
165
|
});
|
|
165
166
|
return result === "busy" ? { kind: "retry", reason: "busy" } : result;
|
|
166
167
|
}
|
|
167
|
-
function readScope(world, scope) {
|
|
168
|
+
async function readScope(world, scope) {
|
|
168
169
|
if (scope === "world")
|
|
169
170
|
return null;
|
|
170
|
-
return context(world);
|
|
171
|
+
return await context(world);
|
|
171
172
|
}
|
|
172
|
-
export function listTasks(world, selection, scope) {
|
|
173
|
-
const selected = readScope(world, scope);
|
|
173
|
+
export async function listTasks(world, selection, scope, limit = DEFAULT_TASK_LIMIT) {
|
|
174
|
+
const selected = await readScope(world, scope);
|
|
174
175
|
if (selected !== null && !Array.isArray(selected))
|
|
175
176
|
return { kind: "refused", refusal: selected };
|
|
176
|
-
return { kind: "accepted", value: projectRows(readBoard(world).board, selected, selection) };
|
|
177
|
+
return { kind: "accepted", value: projectPage(projectRows((await readBoard(world)).board, selected, selection), limit) };
|
|
177
178
|
}
|
|
178
|
-
export function readyTasks(world, scope) {
|
|
179
|
-
const selected = readScope(world, scope);
|
|
179
|
+
export async function readyTasks(world, scope, parent, limit = DEFAULT_TASK_LIMIT) {
|
|
180
|
+
const selected = await readScope(world, scope);
|
|
180
181
|
if (selected !== null && !Array.isArray(selected))
|
|
181
182
|
return { kind: "refused", refusal: selected };
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
const
|
|
183
|
+
const board = (await readBoard(world)).board;
|
|
184
|
+
if (parent !== undefined && !board.tasks.has(parent))
|
|
185
|
+
return { kind: "refused", refusal: { kind: "task-missing", taskId: parent } };
|
|
186
|
+
const ready = { kind: "predicate", predicate: { field: "ready", operator: "=", value: true } };
|
|
187
|
+
const expression = parent === undefined ? ready : { kind: "and", terms: [ready, underExpression(parent)] };
|
|
188
|
+
const selectedRows = projectQuery(board, selected, expression, "priority", Math.max(1, board.tasks.size)).rows;
|
|
189
|
+
const rows = selectedRows.map(({ parent: _parent, needs: _needs, blocks: _blocks, createdAt: _createdAt, updatedAt: _updatedAt, ...row }) => row);
|
|
190
|
+
return { kind: "accepted", value: projectPage(rows, limit) };
|
|
191
|
+
}
|
|
192
|
+
export async function blockedTasks(world, scope, parent, limit = DEFAULT_TASK_LIMIT) {
|
|
193
|
+
const selected = await readScope(world, scope);
|
|
186
194
|
if (selected !== null && !Array.isArray(selected))
|
|
187
195
|
return { kind: "refused", refusal: selected };
|
|
188
|
-
|
|
196
|
+
const board = (await readBoard(world)).board;
|
|
197
|
+
if (parent !== undefined && !board.tasks.has(parent))
|
|
198
|
+
return { kind: "refused", refusal: { kind: "task-missing", taskId: parent } };
|
|
199
|
+
const blocked = { kind: "predicate", predicate: { field: "blocked", operator: "=", value: true } };
|
|
200
|
+
const expression = parent === undefined ? blocked : { kind: "and", terms: [blocked, underExpression(parent)] };
|
|
201
|
+
const selectedRows = projectQuery(board, selected, expression, "priority", Math.max(1, board.tasks.size)).rows;
|
|
202
|
+
const ids = new Set(selectedRows.map((row) => row.id));
|
|
203
|
+
return { kind: "accepted", value: projectPage(projectBlocked(board, selected).filter((row) => ids.has(row.id)), limit) };
|
|
204
|
+
}
|
|
205
|
+
export async function queryTasks(world, expression, scope, sort = "priority", limit = DEFAULT_TASK_LIMIT) {
|
|
206
|
+
const selected = await readScope(world, scope);
|
|
207
|
+
if (selected !== null && !Array.isArray(selected))
|
|
208
|
+
return { kind: "refused", refusal: selected };
|
|
209
|
+
const board = (await readBoard(world)).board;
|
|
210
|
+
for (const target of queryUnderTargets(expression))
|
|
211
|
+
if (!board.tasks.has(target)) {
|
|
212
|
+
return { kind: "refused", refusal: { kind: "task-missing", taskId: target } };
|
|
213
|
+
}
|
|
214
|
+
return { kind: "accepted", value: projectQuery(board, selected, expression, sort, limit) };
|
|
189
215
|
}
|
|
190
216
|
/** Internal composite observation from one complete Task board read. */
|
|
191
|
-
export function observeTaskStatusRows(world) {
|
|
192
|
-
return projectStatusRows(readBoard(world).board, null);
|
|
217
|
+
export async function observeTaskStatusRows(world) {
|
|
218
|
+
return projectStatusRows((await readBoard(world)).board, null);
|
|
219
|
+
}
|
|
220
|
+
/** Internal identity catalog from one complete Task board read. */
|
|
221
|
+
export async function observeTaskCatalogRows(world) {
|
|
222
|
+
return projectRows((await readBoard(world)).board, null, "all");
|
|
193
223
|
}
|
|
194
|
-
export function setCurrentNamespace(world, namespace) { installNamespaceContext(world, namespace); }
|
|
195
|
-
export function currentNamespace(world) {
|
|
196
|
-
const selected = context(world);
|
|
224
|
+
export async function setCurrentNamespace(world, namespace) { await installNamespaceContext(world, namespace); }
|
|
225
|
+
export async function currentNamespace(world) {
|
|
226
|
+
const selected = await context(world);
|
|
197
227
|
return selected;
|
|
198
228
|
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import type { TaskPriority, TaskState } from "./document.js";
|
|
2
|
+
import { type TaskId } from "./identity.js";
|
|
3
|
+
import type { BlockedTaskRow, TaskBoard, TaskRef, TaskRow } from "./board.js";
|
|
4
|
+
export declare const DEFAULT_TASK_LIMIT = 100;
|
|
5
|
+
export declare const MAX_TASK_LIMIT = 1000;
|
|
6
|
+
export type TaskQueryComparison = "=" | "!=" | "<" | "<=" | ">" | ">=" | "~";
|
|
7
|
+
export type TaskQueryPredicate = Readonly<{
|
|
8
|
+
field: "state";
|
|
9
|
+
operator: "=" | "!=";
|
|
10
|
+
value: TaskState;
|
|
11
|
+
}> | Readonly<{
|
|
12
|
+
field: "priority";
|
|
13
|
+
operator: "=" | "!=" | "<" | "<=" | ">" | ">=";
|
|
14
|
+
value: TaskPriority;
|
|
15
|
+
}> | Readonly<{
|
|
16
|
+
field: "title" | "id";
|
|
17
|
+
operator: "=" | "!=" | "~";
|
|
18
|
+
value: string;
|
|
19
|
+
}> | Readonly<{
|
|
20
|
+
field: "parent";
|
|
21
|
+
operator: "=" | "!=";
|
|
22
|
+
value: TaskId | null;
|
|
23
|
+
}> | Readonly<{
|
|
24
|
+
field: "under" | "needs" | "blocks";
|
|
25
|
+
operator: "=" | "!=";
|
|
26
|
+
value: TaskId;
|
|
27
|
+
}> | Readonly<{
|
|
28
|
+
field: "ready" | "blocked";
|
|
29
|
+
operator: "=" | "!=";
|
|
30
|
+
value: boolean;
|
|
31
|
+
}> | Readonly<{
|
|
32
|
+
field: "created" | "updated";
|
|
33
|
+
operator: "=" | "!=" | "<" | "<=" | ">" | ">=";
|
|
34
|
+
value: string;
|
|
35
|
+
}>;
|
|
36
|
+
export type TaskQueryExpression = Readonly<{
|
|
37
|
+
kind: "and" | "or";
|
|
38
|
+
terms: readonly TaskQueryExpression[];
|
|
39
|
+
}> | Readonly<{
|
|
40
|
+
kind: "not";
|
|
41
|
+
term: TaskQueryExpression;
|
|
42
|
+
}> | Readonly<{
|
|
43
|
+
kind: "predicate";
|
|
44
|
+
predicate: TaskQueryPredicate;
|
|
45
|
+
}>;
|
|
46
|
+
export type TaskQuerySort = "priority" | "created" | "updated" | "id";
|
|
47
|
+
export type TaskQueryRow = TaskRow & Readonly<{
|
|
48
|
+
parent: TaskId | null;
|
|
49
|
+
needs: readonly TaskRef[];
|
|
50
|
+
blocks: readonly TaskRef[];
|
|
51
|
+
createdAt: string;
|
|
52
|
+
updatedAt: string;
|
|
53
|
+
}>;
|
|
54
|
+
export type TaskPage<Row> = Readonly<{
|
|
55
|
+
rows: readonly Row[];
|
|
56
|
+
total: number;
|
|
57
|
+
returned: number;
|
|
58
|
+
truncated: boolean;
|
|
59
|
+
}>;
|
|
60
|
+
export declare function normalizeTaskQuery(value: unknown): TaskQueryExpression;
|
|
61
|
+
export declare function projectQuery(board: TaskBoard, scope: readonly string[] | null, expression: TaskQueryExpression, sort?: TaskQuerySort, limit?: number): TaskPage<TaskQueryRow>;
|
|
62
|
+
export declare function projectPage<Row>(rows: readonly Row[], limit?: number): TaskPage<Row>;
|
|
63
|
+
export declare function underExpression(parent: TaskId): TaskQueryExpression;
|
|
64
|
+
export declare function queryUnderTargets(expression: TaskQueryExpression): readonly TaskId[];
|
|
65
|
+
export declare function isValidTaskLimit(value: number): boolean;
|
|
66
|
+
export type TaskQueryPage = TaskPage<TaskQueryRow>;
|
|
67
|
+
export type TaskBlockedPage = TaskPage<BlockedTaskRow>;
|