@astrosheep/keiyaku 4.0.3 → 4.0.5
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 +1 -1
- package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-bind/SKILL.md +15 -1
- package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-task/SKILL.md +3 -1
- package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-workflow/SKILL.md +34 -13
- package/build/src/akuma/akuma.d.ts +34 -12
- package/build/src/akuma/akuma.js +118 -63
- package/build/src/akuma/allowed.d.ts +7 -0
- package/build/src/akuma/allowed.js +33 -0
- package/build/src/akuma/archetype.d.ts +7 -4
- package/build/src/akuma/archetype.js +15 -16
- package/build/src/akuma/body.d.ts +6 -1
- package/build/src/akuma/body.js +36 -27
- package/build/src/akuma/heart/facts.d.ts +82 -14
- package/build/src/akuma/heart/index.d.ts +5 -18
- package/build/src/akuma/heart/index.js +14 -84
- 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 -26
- package/build/src/akuma/heart/rows.js +0 -50
- package/build/src/akuma/heart/schema.d.ts +1 -1
- package/build/src/akuma/heart/schema.js +22 -10
- package/build/src/akuma/heart/soul.js +2 -36
- package/build/src/akuma/heart/storage.d.ts +1 -0
- package/build/src/akuma/heart/storage.js +15 -0
- package/build/src/akuma/heart/timeline.d.ts +1 -5
- package/build/src/akuma/heart/timeline.js +8 -17
- package/build/src/akuma/index.d.ts +5 -2
- package/build/src/akuma/index.js +1 -0
- package/build/src/akuma/projection.d.ts +17 -3
- package/build/src/akuma/projection.js +40 -3
- package/build/src/akuma/provider-recipe.js +1 -11
- package/build/src/akuma/provider.d.ts +2 -7
- package/build/src/akuma/provider.js +2 -1
- package/build/src/akuma/providers/acp/core.d.ts +3 -1
- package/build/src/akuma/providers/acp/core.js +10 -6
- package/build/src/akuma/providers/acp/events.d.ts +11 -3
- package/build/src/akuma/providers/acp/events.js +78 -4
- package/build/src/akuma/providers/acp/index.js +0 -1
- package/build/src/akuma/providers/claude/events.js +62 -3
- package/build/src/akuma/providers/claude/index.d.ts +0 -4
- package/build/src/akuma/providers/claude/index.js +7 -3
- package/build/src/akuma/providers/codex-app-server/events.js +1 -13
- package/build/src/akuma/providers/codex-app-server/index.d.ts +2 -27
- package/build/src/akuma/providers/codex-app-server/index.js +0 -2
- package/build/src/akuma/providers/grok-build/index.d.ts +2 -1
- package/build/src/akuma/providers/grok-build/index.js +72 -3
- package/build/src/akuma/providers/opencode-sdk/events.js +71 -6
- package/build/src/akuma/providers/opencode-sdk/index.d.ts +2 -28
- package/build/src/akuma/providers/opencode-sdk/index.js +8 -4
- package/build/src/akuma/providers/opencode-sdk/session.d.ts +1 -1
- package/build/src/akuma/providers/opencode-sdk/session.js +1 -1
- package/build/src/akuma/providers/pi/events.js +17 -3
- package/build/src/akuma/providers/pi/index.js +11 -4
- package/build/src/akuma/providers/unified-patch.d.ts +4 -0
- package/build/src/akuma/providers/unified-patch.js +14 -0
- package/build/src/akuma/publication.d.ts +1 -1
- package/build/src/akuma/publication.js +1 -1
- package/build/src/akuma/request-serve.d.ts +87 -0
- package/build/src/akuma/request-serve.js +484 -0
- package/build/src/akuma/request-wire.d.ts +124 -0
- package/build/src/akuma/request-wire.js +380 -0
- package/build/src/akuma/requests.d.ts +41 -41
- package/build/src/akuma/requests.js +75 -374
- package/build/src/akuma-body.d.ts +8 -0
- package/build/src/akuma-body.js +84 -0
- 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.d.ts +4 -1
- package/build/src/cli/accepted.js +39 -9
- package/build/src/cli/commands/akuma-invoke.d.ts +8 -7
- package/build/src/cli/commands/akuma-invoke.js +18 -10
- package/build/src/cli/commands/akuma.d.ts +11 -2
- package/build/src/cli/commands/akuma.js +67 -28
- package/build/src/cli/commands/amend.d.ts +1 -1
- package/build/src/cli/commands/amend.js +1 -1
- package/build/src/cli/commands/contract.d.ts +8 -8
- package/build/src/cli/commands/contract.js +3 -3
- package/build/src/cli/commands/install.js +2 -2
- package/build/src/cli/commands/task-invoke.js +98 -9
- package/build/src/cli/commands/task-query.d.ts +1 -1
- package/build/src/cli/commands/task-query.js +11 -5
- package/build/src/cli/coordinates.d.ts +2 -0
- package/build/src/cli/coordinates.js +12 -5
- package/build/src/cli/invoke.d.ts +1 -0
- package/build/src/cli/invoke.js +116 -75
- package/build/src/cli/main.d.ts +2 -0
- package/build/src/cli/main.js +29 -4
- package/build/src/cli/parse.d.ts +3 -2
- package/build/src/cli/parse.js +25 -8
- package/build/src/cli/render/akuma.d.ts +2 -2
- package/build/src/cli/render/akuma.js +138 -24
- package/build/src/cli/render/audit.js +18 -8
- package/build/src/cli/render/contract.d.ts +2 -0
- package/build/src/cli/render/contract.js +243 -36
- package/build/src/cli/render/kanshi.js +64 -29
- package/build/src/cli/render/receipt.d.ts +2 -1
- package/build/src/cli/render/receipt.js +37 -2
- 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 +3 -1
- package/build/src/cli/result.d.ts +52 -9
- package/build/src/cli/selectors.d.ts +1 -0
- package/build/src/cli/selectors.js +9 -6
- package/build/src/contract-worktree.d.ts +1 -2
- package/build/src/contract-worktree.js +7 -9
- 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/dispatch/index.js +50 -39
- package/build/src/git/admission.d.ts +2 -1
- package/build/src/git/hooks.d.ts +0 -4
- package/build/src/git/hooks.js +0 -36
- package/build/src/git/integration.d.ts +7 -8
- package/build/src/git/integration.js +62 -9
- package/build/src/git/observe.d.ts +2 -1
- package/build/src/git/observe.js +6 -5
- package/build/src/git/process.d.ts +26 -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 +8 -2
- package/build/src/git/reconcile.js +63 -14
- package/build/src/git/repository.d.ts +2 -29
- package/build/src/git/repository.js +4 -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 +1 -1
- package/build/src/git/target-placement.js +9 -7
- package/build/src/git/tender.d.ts +20 -5
- package/build/src/git/tender.js +45 -13
- package/build/src/git/terminal-seal.d.ts +16 -2
- package/build/src/git/terminal-seal.js +15 -5
- package/build/src/git/workspace.d.ts +2 -1
- package/build/src/git/workspace.js +13 -5
- package/build/src/index.d.ts +1 -2
- package/build/src/index.js +1 -1
- package/build/src/kanshi/index.d.ts +1 -1
- package/build/src/kanshi/index.js +1 -1
- package/build/src/kanshi/read.d.ts +9 -1
- package/build/src/kanshi/read.js +91 -37
- package/build/src/kanshi/report.d.ts +1 -0
- package/build/src/library/address.d.ts +5 -14
- package/build/src/library/address.js +27 -23
- package/build/src/library/akuma-creation.d.ts +4 -2
- package/build/src/library/akuma-creation.js +43 -26
- package/build/src/library/audit.d.ts +2 -1
- package/build/src/library/audit.js +1 -1
- package/build/src/library/bind.d.ts +2 -1
- package/build/src/library/bind.js +4 -4
- package/build/src/library/catalog.d.ts +1 -2
- package/build/src/library/catalog.js +11 -6
- package/build/src/library/configuration.d.ts +5 -1
- package/build/src/library/configuration.js +71 -18
- package/build/src/library/contract.d.ts +70 -3
- package/build/src/library/contract.js +213 -27
- package/build/src/library/delivery.d.ts +1 -1
- package/build/src/library/fleet.d.ts +70 -12
- package/build/src/library/fleet.js +236 -56
- package/build/src/library/input.d.ts +0 -1
- package/build/src/library/input.js +1 -5
- package/build/src/library/keiyaku.d.ts +4 -4
- package/build/src/library/keiyaku.js +1 -2
- package/build/src/library/mutation.d.ts +2 -1
- package/build/src/library/reconcile.d.ts +2 -1
- package/build/src/library/reconcile.js +3 -1
- package/build/src/library/region.d.ts +1 -1
- package/build/src/library/repo.d.ts +3 -1
- package/build/src/library/repo.js +8 -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 +140 -0
- package/build/src/protocol/bind.d.ts +2 -3
- package/build/src/protocol/bind.js +2 -16
- package/build/src/protocol/completion.d.ts +32 -0
- package/build/src/protocol/completion.js +130 -0
- package/build/src/protocol/deliver.d.ts +37 -0
- package/build/src/protocol/deliver.js +160 -0
- package/build/src/protocol/intent.d.ts +1 -1
- package/build/src/protocol/intent.js +1 -1
- package/build/src/protocol/operations.d.ts +65 -198
- package/build/src/protocol/operations.js +21 -634
- package/build/src/protocol/placement.d.ts +4 -13
- package/build/src/protocol/placement.js +3 -15
- package/build/src/protocol/read/status.d.ts +4 -2
- package/build/src/protocol/read/status.js +27 -15
- 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 +33 -0
- package/build/src/protocol/review.js +148 -0
- package/build/src/protocol/run.d.ts +1 -1
- package/build/src/runtime/proc/run.d.ts +1 -0
- package/build/src/runtime/proc/run.js +9 -2
- package/build/src/settlement/fence.d.ts +1 -1
- package/build/src/settlement/holder.d.ts +1 -1
- package/build/src/settlement/settle.d.ts +2 -1
- package/build/src/settlement/settle.js +4 -1
- package/build/src/task/board.d.ts +18 -4
- package/build/src/task/board.js +73 -29
- package/build/src/task/compose.d.ts +1 -1
- package/build/src/task/compose.js +11 -4
- package/build/src/task/index.d.ts +4 -4
- package/build/src/task/index.js +8 -131
- package/build/src/task/input.d.ts +18 -0
- package/build/src/task/input.js +189 -0
- package/build/src/task/mutation.d.ts +39 -0
- package/build/src/task/mutation.js +85 -0
- package/build/src/task/operations.d.ts +4 -19
- package/build/src/task/operations.js +64 -18
- package/build/src/task/query.d.ts +9 -2
- package/build/src/task/query.js +32 -64
- package/build/src/task/store.js +0 -2
- package/build/src/verification/execution.d.ts +1 -1
- package/build/src/verification/execution.js +2 -1
- package/build/src/workspace-place.d.ts +1 -4
- package/build/src/workspace-place.js +0 -15
- package/package.json +5 -3
|
@@ -0,0 +1,39 @@
|
|
|
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">;
|
|
11
|
+
}> | Readonly<{
|
|
12
|
+
action: "task.addDocument";
|
|
13
|
+
input: Omit<AddTaskDocumentInput, "actor" | "signal">;
|
|
14
|
+
}> | Readonly<{
|
|
15
|
+
action: "task.compose";
|
|
16
|
+
markdown: string;
|
|
17
|
+
}> | Readonly<{
|
|
18
|
+
action: "task.update";
|
|
19
|
+
id: TaskId;
|
|
20
|
+
input: Omit<UpdateTaskInput, "signal">;
|
|
21
|
+
}> | Readonly<{
|
|
22
|
+
action: "task.start" | "task.stop" | "task.resume";
|
|
23
|
+
id: TaskId;
|
|
24
|
+
}> | Readonly<{
|
|
25
|
+
action: "task.hold";
|
|
26
|
+
ids: readonly TaskId[];
|
|
27
|
+
}> | Readonly<{
|
|
28
|
+
action: "task.done" | "task.drop";
|
|
29
|
+
ids: readonly TaskId[];
|
|
30
|
+
note?: string;
|
|
31
|
+
}>;
|
|
32
|
+
export type TaskMutationExecutionResult = TaskMutationResult | TaskUpdateResult | TaskBatchResult | TaskCompositionResult;
|
|
33
|
+
export declare function decodeTaskMutationRequest(action: TaskMutationAction, value: unknown): TaskMutationRequest;
|
|
34
|
+
export declare function executeTaskMutation(input: Readonly<{
|
|
35
|
+
world: WorldRoot;
|
|
36
|
+
request: TaskMutationRequest;
|
|
37
|
+
requester: string;
|
|
38
|
+
signal?: AbortSignal;
|
|
39
|
+
}>): Promise<TaskMutationExecutionResult>;
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { addInput, closed, namespace, record, taskId, taskIds, text, updateInput } from "./input.js";
|
|
2
|
+
import { Tasks } from "./index.js";
|
|
3
|
+
export const TASK_MUTATION_ACTIONS = Object.freeze([
|
|
4
|
+
"task.add", "task.addDocument", "task.compose", "task.done", "task.drop",
|
|
5
|
+
"task.hold", "task.resume", "task.start", "task.stop", "task.update",
|
|
6
|
+
]);
|
|
7
|
+
const TASK_MUTATION_ACTION_SET = new Set(TASK_MUTATION_ACTIONS);
|
|
8
|
+
export function isTaskMutationAction(value) {
|
|
9
|
+
return typeof value === "string" && TASK_MUTATION_ACTION_SET.has(value);
|
|
10
|
+
}
|
|
11
|
+
function mutationObject(value, keys, label) {
|
|
12
|
+
const decoded = record(value, label);
|
|
13
|
+
closed(decoded, keys, label);
|
|
14
|
+
return decoded;
|
|
15
|
+
}
|
|
16
|
+
export function decodeTaskMutationRequest(action, value) {
|
|
17
|
+
switch (action) {
|
|
18
|
+
case "task.add": {
|
|
19
|
+
const input = mutationObject(value, ["input"], "task.add request");
|
|
20
|
+
const raw = mutationObject(input.input, [
|
|
21
|
+
"title", "namespace", "body", "note", "state", "priority", "needs", "parent", "supersedes", "relates",
|
|
22
|
+
], "task.add input");
|
|
23
|
+
return { action, input: addInput(raw) };
|
|
24
|
+
}
|
|
25
|
+
case "task.addDocument": {
|
|
26
|
+
const input = mutationObject(value, ["input"], "task.addDocument request");
|
|
27
|
+
const raw = mutationObject(input.input, ["markdown", "namespace"], "task.addDocument input");
|
|
28
|
+
const markdown = text(raw.markdown, "markdown");
|
|
29
|
+
if (markdown === undefined)
|
|
30
|
+
throw new TypeError("markdown is required");
|
|
31
|
+
const selected = namespace(raw.namespace);
|
|
32
|
+
return { action, input: { markdown, ...(selected === undefined ? {} : { namespace: selected }) } };
|
|
33
|
+
}
|
|
34
|
+
case "task.compose": {
|
|
35
|
+
const input = mutationObject(value, ["markdown"], "task.compose request");
|
|
36
|
+
const markdown = text(input.markdown, "markdown");
|
|
37
|
+
if (markdown === undefined)
|
|
38
|
+
throw new TypeError("markdown is required");
|
|
39
|
+
return { action, markdown };
|
|
40
|
+
}
|
|
41
|
+
case "task.update": {
|
|
42
|
+
const request = mutationObject(value, ["id", "input"], "task.update request");
|
|
43
|
+
const raw = mutationObject(request.input, [
|
|
44
|
+
"title", "body", "appendBody", "note", "priority", "needs", "addNeeds", "dropNeeds", "parent",
|
|
45
|
+
"supersedes", "addSupersedes", "dropSupersedes", "relates", "addRelates", "dropRelates",
|
|
46
|
+
], "task.update input");
|
|
47
|
+
return { action, id: taskId(request.id), input: updateInput(raw) };
|
|
48
|
+
}
|
|
49
|
+
case "task.start":
|
|
50
|
+
case "task.stop":
|
|
51
|
+
case "task.resume": {
|
|
52
|
+
const request = mutationObject(value, ["id"], `${action} request`);
|
|
53
|
+
return { action, id: taskId(request.id) };
|
|
54
|
+
}
|
|
55
|
+
case "task.hold": {
|
|
56
|
+
const request = mutationObject(value, ["ids"], "task.hold request");
|
|
57
|
+
return { action, ids: taskIds(request.ids, "ids") ?? [] };
|
|
58
|
+
}
|
|
59
|
+
case "task.done":
|
|
60
|
+
case "task.drop": {
|
|
61
|
+
const request = mutationObject(value, ["ids", "note"], `${action} request`);
|
|
62
|
+
const note = text(request.note, "note");
|
|
63
|
+
return { action, ids: taskIds(request.ids, "ids") ?? [], ...(note === undefined ? {} : { note }) };
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
export async function executeTaskMutation(input) {
|
|
68
|
+
const tasks = Tasks.of(input.world);
|
|
69
|
+
const { request, signal } = input;
|
|
70
|
+
const withSignal = signal === undefined ? {} : { signal };
|
|
71
|
+
switch (request.action) {
|
|
72
|
+
case "task.add": return await tasks.add({ ...request.input, actor: input.requester, ...withSignal });
|
|
73
|
+
case "task.addDocument": return await tasks.addDocument({ ...request.input, actor: input.requester, ...withSignal });
|
|
74
|
+
case "task.compose": return await tasks.compose({ markdown: request.markdown, actor: input.requester, ...withSignal });
|
|
75
|
+
case "task.update": return await tasks.task({ id: request.id }).update({ ...request.input, ...withSignal });
|
|
76
|
+
case "task.start": return await tasks.task({ id: request.id }).start(withSignal);
|
|
77
|
+
case "task.stop": return await tasks.task({ id: request.id }).stop(withSignal);
|
|
78
|
+
case "task.resume": return await tasks.task({ id: request.id }).resume(withSignal);
|
|
79
|
+
case "task.hold": return await tasks.batch({ verb: "hold", ids: request.ids, ...withSignal });
|
|
80
|
+
case "task.done": return await tasks.batch({ verb: "done", ids: request.ids, ...withSignal,
|
|
81
|
+
...(request.note === undefined ? {} : { note: request.note }) });
|
|
82
|
+
case "task.drop": return await tasks.batch({ verb: "drop", ids: request.ids, ...withSignal,
|
|
83
|
+
...(request.note === undefined ? {} : { note: request.note }) });
|
|
84
|
+
}
|
|
85
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type BlockedTaskRow, type TaskRow } from "./board.js";
|
|
1
|
+
import { type BlockedTaskRow, type TaskBoardObservation, 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";
|
|
@@ -105,6 +105,8 @@ export type UpdateTaskInput = Readonly<{
|
|
|
105
105
|
signal?: AbortSignal;
|
|
106
106
|
}>;
|
|
107
107
|
export declare function taskView(document: TaskDocument): TaskView;
|
|
108
|
+
/** Internal Task timestamp rule shared by ordinary mutations and compose. */
|
|
109
|
+
export declare function advanceTaskTimestamp(previous: string, candidate: string): string;
|
|
108
110
|
export declare function addTask(world: WorldRoot, input: AddTaskInput): Promise<TaskMutationResult>;
|
|
109
111
|
export declare function addTaskDocument(world: WorldRoot, input: AddTaskDocumentInput): Promise<TaskMutationResult>;
|
|
110
112
|
export declare function updateTask(world: WorldRoot, id: TaskId, input: UpdateTaskInput): Promise<TaskUpdateResult>;
|
|
@@ -116,24 +118,7 @@ export declare function readyTasks(world: WorldRoot, scope?: "namespace" | "worl
|
|
|
116
118
|
export declare function blockedTasks(world: WorldRoot, scope?: "namespace" | "world", parent?: TaskId, limit?: number): Promise<TaskOutcome<TaskPage<BlockedTaskRow>>>;
|
|
117
119
|
export declare function queryTasks(world: WorldRoot, expression: TaskQueryExpression, scope?: "namespace" | "world", sort?: TaskQuerySort, limit?: number): Promise<TaskOutcome<TaskPage<TaskQueryRow>>>;
|
|
118
120
|
/** Internal composite observation from one complete Task board read. */
|
|
119
|
-
export declare function
|
|
120
|
-
id: TaskId;
|
|
121
|
-
title: string;
|
|
122
|
-
state: TaskState;
|
|
123
|
-
priority: TaskPriority;
|
|
124
|
-
disposition: import("./board.js").TaskDisposition;
|
|
125
|
-
}> | {
|
|
126
|
-
blockers: readonly Readonly<{
|
|
127
|
-
id: TaskId;
|
|
128
|
-
title: string | null;
|
|
129
|
-
state: TaskState | "missing";
|
|
130
|
-
}>[];
|
|
131
|
-
id: TaskId;
|
|
132
|
-
title: string;
|
|
133
|
-
state: TaskState;
|
|
134
|
-
priority: TaskPriority;
|
|
135
|
-
disposition: import("./board.js").TaskDisposition;
|
|
136
|
-
})[]>;
|
|
121
|
+
export declare function observeTaskBoard(world: WorldRoot): Promise<TaskBoardObservation>;
|
|
137
122
|
/** Internal identity catalog from one complete Task board read. */
|
|
138
123
|
export declare function observeTaskCatalogRows(world: WorldRoot): Promise<readonly TaskRow[]>;
|
|
139
124
|
export declare function setCurrentNamespace(world: WorldRoot, namespace: readonly string[]): Promise<void>;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { resolve } from "node:path";
|
|
2
2
|
import { documentDiff } from "../markdown/diff.js";
|
|
3
3
|
import { installNamespaceContext, readNamespaceContext } from "./context.js";
|
|
4
|
-
import { relationProblem, projectBlocked, projectRows,
|
|
4
|
+
import { createTaskRelations, projectTaskBoardObservation, relationProblem, projectBlocked, projectRows, } from "./board.js";
|
|
5
5
|
import { parseTaskCreationDocument, serializeTaskDocument } from "./document.js";
|
|
6
6
|
import { allocateLocalId, deriveLocalStem, formatTaskId, parseTaskId, sameNamespace } from "./identity.js";
|
|
7
7
|
import { authorityPath, readBoard, replaceAuthority, withTaskLocks, } from "./store.js";
|
|
@@ -24,10 +24,14 @@ function occupied(board, namespace) {
|
|
|
24
24
|
return sameNamespace(coordinate.namespace, namespace) ? [coordinate.localId] : [];
|
|
25
25
|
}));
|
|
26
26
|
}
|
|
27
|
-
function currentTimestamp() {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
27
|
+
function currentTimestamp() {
|
|
28
|
+
return new Date().toISOString();
|
|
29
|
+
}
|
|
30
|
+
/** Internal Task timestamp rule shared by ordinary mutations and compose. */
|
|
31
|
+
export function advanceTaskTimestamp(previous, candidate) {
|
|
32
|
+
return candidate > previous
|
|
33
|
+
? candidate
|
|
34
|
+
: new Date(Date.parse(previous) + 1).toISOString();
|
|
31
35
|
}
|
|
32
36
|
function addDocument(base, namespace, board, at, actor) {
|
|
33
37
|
const localId = allocateLocalId(deriveLocalStem(base.title), occupied(board, namespace));
|
|
@@ -108,12 +112,20 @@ export async function updateTask(world, id, input) {
|
|
|
108
112
|
const problem = relationProblem(boardWith(snapshot.board, candidate), current, candidate);
|
|
109
113
|
if (problem !== null)
|
|
110
114
|
return { kind: "refused", refusal: { kind: "invalid-graph", diagnostic: problem } };
|
|
111
|
-
const predecessor = snapshot.bytes.get(id)
|
|
115
|
+
const predecessor = snapshot.bytes.get(id);
|
|
116
|
+
const before = Buffer.from(predecessor).toString("utf8");
|
|
112
117
|
const changed = !Buffer.from(serializeTaskDocument(candidate)).equals(Buffer.from(predecessor));
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
118
|
+
let next = candidate;
|
|
119
|
+
if (changed) {
|
|
120
|
+
const at = currentTimestamp();
|
|
121
|
+
next = { ...candidate, updatedAt: advanceTaskTimestamp(current.updatedAt, at) };
|
|
122
|
+
}
|
|
123
|
+
const afterBytes = serializeTaskDocument(next);
|
|
124
|
+
const after = Buffer.from(afterBytes).toString("utf8");
|
|
125
|
+
if (before !== after
|
|
126
|
+
&& await replaceAuthority({ path: authorityPath(world, id), expected: predecessor, next: afterBytes }) !== "replaced") {
|
|
116
127
|
return { kind: "retry", reason: "concurrent-modification" };
|
|
128
|
+
}
|
|
117
129
|
const label = `${id}.md`;
|
|
118
130
|
return { kind: "accepted", value: { task: taskView(next), documentDiff: documentDiff(label, label, before, after) } };
|
|
119
131
|
});
|
|
@@ -130,9 +142,16 @@ export async function lifecycleTask(world, id, verb, signal, note) {
|
|
|
130
142
|
if (current === undefined)
|
|
131
143
|
return refused({ kind: "task-missing", taskId: id });
|
|
132
144
|
const state = TRANSITIONS[verb][current.state];
|
|
133
|
-
if (state === undefined)
|
|
145
|
+
if (state === undefined) {
|
|
134
146
|
return refused({ kind: "invalid-lifecycle-transition", taskId: id, state: current.state, verb });
|
|
135
|
-
|
|
147
|
+
}
|
|
148
|
+
const at = currentTimestamp();
|
|
149
|
+
const next = {
|
|
150
|
+
...current,
|
|
151
|
+
state,
|
|
152
|
+
...(note === undefined ? {} : { note }),
|
|
153
|
+
updatedAt: advanceTaskTimestamp(current.updatedAt, at),
|
|
154
|
+
};
|
|
136
155
|
const bytes = serializeTaskDocument(next);
|
|
137
156
|
return await replaceAuthority({ path: authorityPath(world, id), expected: snapshot.bytes.get(id), next: bytes }) === "replaced"
|
|
138
157
|
? { kind: "accepted", value: taskView(next) } : retry("concurrent-modification");
|
|
@@ -157,7 +176,12 @@ export async function settleTask(world, id) {
|
|
|
157
176
|
if (current.state === "drop") {
|
|
158
177
|
return { kind: "refused", refusal: { kind: "invalid-lifecycle-transition", taskId: id, state: current.state, verb: "done" } };
|
|
159
178
|
}
|
|
160
|
-
const
|
|
179
|
+
const at = currentTimestamp();
|
|
180
|
+
const next = {
|
|
181
|
+
...current,
|
|
182
|
+
state: "done",
|
|
183
|
+
updatedAt: advanceTaskTimestamp(current.updatedAt, at),
|
|
184
|
+
};
|
|
161
185
|
const replaced = await replaceAuthority({ path: authorityPath(world, id), expected: snapshot.bytes.get(id), next: serializeTaskDocument(next) });
|
|
162
186
|
return replaced === "replaced"
|
|
163
187
|
? { kind: "changed", task: taskView(next), action: "done" }
|
|
@@ -183,9 +207,14 @@ export async function readyTasks(world, scope, parent, limit = DEFAULT_TASK_LIMI
|
|
|
183
207
|
const board = (await readBoard(world)).board;
|
|
184
208
|
if (parent !== undefined && !board.tasks.has(parent))
|
|
185
209
|
return { kind: "refused", refusal: { kind: "task-missing", taskId: parent } };
|
|
210
|
+
const relations = createTaskRelations(board);
|
|
186
211
|
const ready = { kind: "predicate", predicate: { field: "ready", operator: "=", value: true } };
|
|
187
212
|
const expression = parent === undefined ? ready : { kind: "and", terms: [ready, underExpression(parent)] };
|
|
188
|
-
const selectedRows = projectQuery(board,
|
|
213
|
+
const selectedRows = projectQuery(board, relations, {
|
|
214
|
+
scope: selected,
|
|
215
|
+
expression,
|
|
216
|
+
limit: Math.max(1, board.tasks.size),
|
|
217
|
+
}).rows;
|
|
189
218
|
const rows = selectedRows.map(({ parent: _parent, needs: _needs, blocks: _blocks, createdAt: _createdAt, updatedAt: _updatedAt, ...row }) => row);
|
|
190
219
|
return { kind: "accepted", value: projectPage(rows, limit) };
|
|
191
220
|
}
|
|
@@ -196,26 +225,43 @@ export async function blockedTasks(world, scope, parent, limit = DEFAULT_TASK_LI
|
|
|
196
225
|
const board = (await readBoard(world)).board;
|
|
197
226
|
if (parent !== undefined && !board.tasks.has(parent))
|
|
198
227
|
return { kind: "refused", refusal: { kind: "task-missing", taskId: parent } };
|
|
228
|
+
const relations = createTaskRelations(board);
|
|
199
229
|
const blocked = { kind: "predicate", predicate: { field: "blocked", operator: "=", value: true } };
|
|
200
230
|
const expression = parent === undefined ? blocked : { kind: "and", terms: [blocked, underExpression(parent)] };
|
|
201
|
-
const selectedRows = projectQuery(board,
|
|
231
|
+
const selectedRows = projectQuery(board, relations, {
|
|
232
|
+
scope: selected,
|
|
233
|
+
expression,
|
|
234
|
+
limit: Math.max(1, board.tasks.size),
|
|
235
|
+
}).rows;
|
|
202
236
|
const ids = new Set(selectedRows.map((row) => row.id));
|
|
203
|
-
return {
|
|
237
|
+
return {
|
|
238
|
+
kind: "accepted",
|
|
239
|
+
value: projectPage(projectBlocked(board, selected, relations).filter((row) => ids.has(row.id)), limit),
|
|
240
|
+
};
|
|
204
241
|
}
|
|
205
242
|
export async function queryTasks(world, expression, scope, sort = "priority", limit = DEFAULT_TASK_LIMIT) {
|
|
206
243
|
const selected = await readScope(world, scope);
|
|
207
244
|
if (selected !== null && !Array.isArray(selected))
|
|
208
245
|
return { kind: "refused", refusal: selected };
|
|
209
246
|
const board = (await readBoard(world)).board;
|
|
247
|
+
const relations = createTaskRelations(board);
|
|
210
248
|
for (const target of queryUnderTargets(expression))
|
|
211
249
|
if (!board.tasks.has(target)) {
|
|
212
250
|
return { kind: "refused", refusal: { kind: "task-missing", taskId: target } };
|
|
213
251
|
}
|
|
214
|
-
return {
|
|
252
|
+
return {
|
|
253
|
+
kind: "accepted",
|
|
254
|
+
value: projectQuery(board, relations, {
|
|
255
|
+
scope: selected,
|
|
256
|
+
expression,
|
|
257
|
+
sort,
|
|
258
|
+
limit,
|
|
259
|
+
}),
|
|
260
|
+
};
|
|
215
261
|
}
|
|
216
262
|
/** Internal composite observation from one complete Task board read. */
|
|
217
|
-
export async function
|
|
218
|
-
return
|
|
263
|
+
export async function observeTaskBoard(world) {
|
|
264
|
+
return projectTaskBoardObservation((await readBoard(world)).board);
|
|
219
265
|
}
|
|
220
266
|
/** Internal identity catalog from one complete Task board read. */
|
|
221
267
|
export async function observeTaskCatalogRows(world) {
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import type { TaskPriority, TaskState } from "./document.js";
|
|
2
2
|
import { type TaskId } from "./identity.js";
|
|
3
|
-
import type { BlockedTaskRow, TaskBoard, TaskRef, TaskRow } from "./board.js";
|
|
3
|
+
import type { BlockedTaskRow, TaskBoard, TaskRef, TaskRelationProjection, TaskRow } from "./board.js";
|
|
4
4
|
export declare const DEFAULT_TASK_LIMIT = 100;
|
|
5
5
|
export declare const MAX_TASK_LIMIT = 1000;
|
|
6
|
+
export declare const TASK_RELATION_PREDICATE_FIELDS: readonly ["under", "needs", "blocks"];
|
|
7
|
+
export type TaskRelationPredicateField = (typeof TASK_RELATION_PREDICATE_FIELDS)[number];
|
|
6
8
|
export type TaskQueryComparison = "=" | "!=" | "<" | "<=" | ">" | ">=" | "~";
|
|
7
9
|
export type TaskQueryPredicate = Readonly<{
|
|
8
10
|
field: "state";
|
|
@@ -58,7 +60,12 @@ export type TaskPage<Row> = Readonly<{
|
|
|
58
60
|
truncated: boolean;
|
|
59
61
|
}>;
|
|
60
62
|
export declare function normalizeTaskQuery(value: unknown): TaskQueryExpression;
|
|
61
|
-
export declare function projectQuery(board: TaskBoard,
|
|
63
|
+
export declare function projectQuery(board: TaskBoard, relations: TaskRelationProjection, input: Readonly<{
|
|
64
|
+
scope: readonly string[] | null;
|
|
65
|
+
expression: TaskQueryExpression;
|
|
66
|
+
sort?: TaskQuerySort;
|
|
67
|
+
limit?: number;
|
|
68
|
+
}>): TaskPage<TaskQueryRow>;
|
|
62
69
|
export declare function projectPage<Row>(rows: readonly Row[], limit?: number): TaskPage<Row>;
|
|
63
70
|
export declare function underExpression(parent: TaskId): TaskQueryExpression;
|
|
64
71
|
export declare function queryUnderTargets(expression: TaskQueryExpression): readonly TaskId[];
|
package/build/src/task/query.js
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
import { formatTaskId, parseTaskId, sameNamespace } from "./identity.js";
|
|
2
|
-
import { taskDisposition } from "./board.js";
|
|
2
|
+
import { taskBlocked, taskDisposition, taskRef } from "./board.js";
|
|
3
3
|
export const DEFAULT_TASK_LIMIT = 100;
|
|
4
4
|
export const MAX_TASK_LIMIT = 1_000;
|
|
5
|
+
export const TASK_RELATION_PREDICATE_FIELDS = ["under", "needs", "blocks"];
|
|
6
|
+
function isTaskRelationPredicateField(value) {
|
|
7
|
+
return TASK_RELATION_PREDICATE_FIELDS.includes(value);
|
|
8
|
+
}
|
|
5
9
|
const states = ["open", "in_progress", "on_hold", "done", "drop"];
|
|
6
10
|
const comparisons = ["=", "!=", "<", "<=", ">", ">=", "~"];
|
|
7
11
|
function fail(message) { throw new TypeError(message); }
|
|
@@ -97,14 +101,14 @@ function predicate(value) {
|
|
|
97
101
|
case "title": return normalizeTextPredicate(input, "title", operator);
|
|
98
102
|
case "id": return normalizeTextPredicate(input, "id", operator);
|
|
99
103
|
case "parent": return normalizeParentPredicate(input, operator);
|
|
100
|
-
case "under": return normalizeRelationPredicate(input, "under", operator);
|
|
101
|
-
case "needs": return normalizeRelationPredicate(input, "needs", operator);
|
|
102
|
-
case "blocks": return normalizeRelationPredicate(input, "blocks", operator);
|
|
103
104
|
case "ready": return normalizeBooleanPredicate(input, "ready", operator);
|
|
104
105
|
case "blocked": return normalizeBooleanPredicate(input, "blocked", operator);
|
|
105
106
|
case "created": return normalizeTimestampPredicate(input, "created", operator);
|
|
106
107
|
case "updated": return normalizeTimestampPredicate(input, "updated", operator);
|
|
107
|
-
default:
|
|
108
|
+
default:
|
|
109
|
+
if (isTaskRelationPredicateField(field))
|
|
110
|
+
return normalizeRelationPredicate(input, field, operator);
|
|
111
|
+
return fail(`unknown query field: ${field}`);
|
|
108
112
|
}
|
|
109
113
|
}
|
|
110
114
|
export function normalizeTaskQuery(value) {
|
|
@@ -139,46 +143,24 @@ function compare(left, operator, right) {
|
|
|
139
143
|
return left >= right;
|
|
140
144
|
}
|
|
141
145
|
function membership(left, operator, right) { return operator === "=" ? left === right : left !== right; }
|
|
142
|
-
function
|
|
143
|
-
const children = new Map();
|
|
144
|
-
for (const task of board.tasks.values()) {
|
|
145
|
-
if (task.parent === null)
|
|
146
|
-
continue;
|
|
147
|
-
const siblings = children.get(task.parent) ?? [];
|
|
148
|
-
siblings.push(task.id);
|
|
149
|
-
children.set(task.parent, siblings);
|
|
150
|
-
}
|
|
151
|
-
return children;
|
|
152
|
-
}
|
|
153
|
-
function indexReverseNeeds(board) {
|
|
154
|
-
const reverse = new Map();
|
|
155
|
-
for (const task of board.tasks.values()) {
|
|
156
|
-
for (const need of task.needs) {
|
|
157
|
-
const blockers = reverse.get(need) ?? new Set();
|
|
158
|
-
blockers.add(task.id);
|
|
159
|
-
reverse.set(need, blockers);
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
return reverse;
|
|
163
|
-
}
|
|
164
|
-
function descendants(children, parent) {
|
|
146
|
+
function descendants(relations, parent) {
|
|
165
147
|
const result = new Set(), pending = [parent];
|
|
166
148
|
while (pending.length > 0) {
|
|
167
149
|
const current = pending.shift();
|
|
168
|
-
for (const child of children
|
|
169
|
-
if (result.has(child))
|
|
150
|
+
for (const child of relations.children(current)) {
|
|
151
|
+
if (result.has(child.id))
|
|
170
152
|
continue;
|
|
171
|
-
result.add(child);
|
|
172
|
-
pending.push(child);
|
|
153
|
+
result.add(child.id);
|
|
154
|
+
pending.push(child.id);
|
|
173
155
|
}
|
|
174
156
|
}
|
|
175
157
|
return result;
|
|
176
158
|
}
|
|
177
|
-
function relation(board,
|
|
159
|
+
function relation(board, relations, id, field, target) {
|
|
178
160
|
const task = board.tasks.get(id);
|
|
179
161
|
if (task === undefined)
|
|
180
162
|
return false;
|
|
181
|
-
return field === "needs" ? task.needs.includes(target) :
|
|
163
|
+
return field === "needs" ? task.needs.includes(target) : relations.blocks(id).some((ref) => ref.id === target);
|
|
182
164
|
}
|
|
183
165
|
function matchText(left, operator, right) {
|
|
184
166
|
return operator === "~" ? left.includes(right) : compare(left, operator, right);
|
|
@@ -186,18 +168,7 @@ function matchText(left, operator, right) {
|
|
|
186
168
|
function matchRelation(selected, operator) {
|
|
187
169
|
return operator === "=" ? selected : !selected;
|
|
188
170
|
}
|
|
189
|
-
function
|
|
190
|
-
const active = task.state === "open" || task.state === "in_progress";
|
|
191
|
-
return active && task.needs.some((need) => {
|
|
192
|
-
const target = board.tasks.get(need);
|
|
193
|
-
return target === undefined || (target.state !== "done" && target.state !== "drop");
|
|
194
|
-
});
|
|
195
|
-
}
|
|
196
|
-
function taskRef(board, id) {
|
|
197
|
-
const task = board.tasks.get(id);
|
|
198
|
-
return task === undefined ? { id, title: null, state: "missing" } : { id, title: task.title, state: task.state };
|
|
199
|
-
}
|
|
200
|
-
function matchesPredicate(board, reverseNeeds, task, value, under) {
|
|
171
|
+
function matchesPredicate(board, relations, task, value, under) {
|
|
201
172
|
switch (value.field) {
|
|
202
173
|
case "state": return compare(task.state, value.operator, value.value);
|
|
203
174
|
case "priority": return compare(task.priority, value.operator, value.value);
|
|
@@ -205,32 +176,30 @@ function matchesPredicate(board, reverseNeeds, task, value, under) {
|
|
|
205
176
|
case "id": return matchText(task.id, value.operator, value.value);
|
|
206
177
|
case "parent": return compare(task.parent ?? "", value.operator, value.value ?? "");
|
|
207
178
|
case "under": return matchRelation(under.get(value.value)?.has(task.id) === true, value.operator);
|
|
208
|
-
case "needs": return matchRelation(relation(board,
|
|
209
|
-
case "blocks": return matchRelation(relation(board,
|
|
179
|
+
case "needs": return matchRelation(relation(board, relations, task.id, "needs", value.value), value.operator);
|
|
180
|
+
case "blocks": return matchRelation(relation(board, relations, task.id, "blocks", value.value), value.operator);
|
|
210
181
|
case "ready": return membership(taskDisposition(board, task) === "ready", value.operator, value.value);
|
|
211
|
-
case "blocked": return membership(
|
|
182
|
+
case "blocked": return membership(taskBlocked(board, task), value.operator, value.value);
|
|
212
183
|
case "created": return compare(task.createdAt, value.operator, value.value);
|
|
213
184
|
case "updated": return compare(task.updatedAt, value.operator, value.value);
|
|
214
185
|
}
|
|
215
186
|
}
|
|
216
|
-
function matches(board,
|
|
187
|
+
function matches(board, relations, task, expression, under) {
|
|
217
188
|
if (expression.kind === "not")
|
|
218
|
-
return !matches(board,
|
|
189
|
+
return !matches(board, relations, task, expression.term, under);
|
|
219
190
|
if (expression.kind !== "predicate") {
|
|
220
191
|
return expression.kind === "and"
|
|
221
|
-
? expression.terms.every((term) => matches(board,
|
|
222
|
-
: expression.terms.some((term) => matches(board,
|
|
192
|
+
? expression.terms.every((term) => matches(board, relations, task, term, under))
|
|
193
|
+
: expression.terms.some((term) => matches(board, relations, task, term, under));
|
|
223
194
|
}
|
|
224
|
-
return matchesPredicate(board,
|
|
195
|
+
return matchesPredicate(board, relations, task, expression.predicate, under);
|
|
225
196
|
}
|
|
226
|
-
function row(board,
|
|
197
|
+
function row(board, relations, task) {
|
|
227
198
|
return {
|
|
228
199
|
id: task.id, title: task.title, state: task.state, priority: task.priority, disposition: taskDisposition(board, task),
|
|
229
200
|
parent: task.parent,
|
|
230
201
|
needs: task.needs.map((need) => taskRef(board, need)),
|
|
231
|
-
blocks:
|
|
232
|
-
.sort((left, right) => Buffer.compare(Buffer.from(left), Buffer.from(right)))
|
|
233
|
-
.map((id) => taskRef(board, id)),
|
|
202
|
+
blocks: relations.blocks(task.id),
|
|
234
203
|
createdAt: task.createdAt, updatedAt: task.updatedAt,
|
|
235
204
|
};
|
|
236
205
|
}
|
|
@@ -245,13 +214,12 @@ function sortTasks(tasks, sort) {
|
|
|
245
214
|
return (sort === "updated" ? -compared : compared) || tie;
|
|
246
215
|
});
|
|
247
216
|
}
|
|
248
|
-
export function projectQuery(board,
|
|
249
|
-
const
|
|
250
|
-
const
|
|
251
|
-
const under = new Map(queryUnderTargets(expression).map((target) => [target, descendants(children, target)]));
|
|
217
|
+
export function projectQuery(board, relations, input) {
|
|
218
|
+
const { scope, expression, sort = "priority", limit = DEFAULT_TASK_LIMIT } = input;
|
|
219
|
+
const under = new Map(queryUnderTargets(expression).map((target) => [target, descendants(relations, target)]));
|
|
252
220
|
const selected = sortTasks([...board.tasks.values()].filter((task) => scope === null || sameNamespace(parseTaskId(task.id).namespace, scope))
|
|
253
|
-
.filter((task) => matches(board,
|
|
254
|
-
const rows = selected.slice(0, limit).map((task) => row(board,
|
|
221
|
+
.filter((task) => matches(board, relations, task, expression, under)), sort);
|
|
222
|
+
const rows = selected.slice(0, limit).map((task) => row(board, relations, task));
|
|
255
223
|
return { rows, total: selected.length, returned: rows.length, truncated: rows.length < selected.length };
|
|
256
224
|
}
|
|
257
225
|
export function projectPage(rows, limit = DEFAULT_TASK_LIMIT) {
|
package/build/src/task/store.js
CHANGED
|
@@ -34,8 +34,6 @@ async function authorityFiles(directory) {
|
|
|
34
34
|
files.push(...await authorityFiles(path));
|
|
35
35
|
else if (entry.isFile() && entry.name.endsWith(".md"))
|
|
36
36
|
files.push(path);
|
|
37
|
-
else
|
|
38
|
-
throw new Error(`unexpected Task authority entry: ${path}`);
|
|
39
37
|
}
|
|
40
38
|
return files.sort((a, b) => Buffer.compare(Buffer.from(a), Buffer.from(b)));
|
|
41
39
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { SnapshotId } from "../core/facts/types.js";
|
|
2
2
|
import { type HookFailure } from "../git/hooks.js";
|
|
3
3
|
import type { MaterializedScratchCandidate, WorktreeLeak } from "../git/scratch.js";
|
|
4
|
-
import type { GitRepository } from "../git/
|
|
4
|
+
import type { GitRepository } from "../git/process.js";
|
|
5
5
|
import type { Settings } from "../settings.js";
|
|
6
6
|
import type { VerificationDeclaration } from "./declaration.js";
|
|
7
7
|
type VerificationVerdict = "satisfied" | "unsatisfied";
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { runHookCommands
|
|
1
|
+
import { runHookCommands } from "../git/hooks.js";
|
|
2
|
+
import { worktreeHooksFrom } from "../library/configuration.js";
|
|
2
3
|
import { runProcess } from "../runtime/proc/run.js";
|
|
3
4
|
const SUMMARY_BYTES = 32 * 1024;
|
|
4
5
|
function argvFor(declaration) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type ContractId } from "./core/facts/types.js";
|
|
2
|
-
import type { GitRepository } from "./git/
|
|
2
|
+
import type { GitRepository } from "./git/process.js";
|
|
3
3
|
export declare const CONTRACT_PLACES: readonly string[];
|
|
4
4
|
export type Place = string & {
|
|
5
5
|
readonly [placeBrand]: "Place";
|
|
@@ -27,15 +27,12 @@ export type ManagedWorktreeAppointment = Readonly<{
|
|
|
27
27
|
export declare function placeRegisterPath(repository: GitRepository): string;
|
|
28
28
|
export declare function place(value: string): Place;
|
|
29
29
|
export declare function nextPlace(current?: Place): Place;
|
|
30
|
-
export declare function firstFreePlace(register: PlaceRegister): Place;
|
|
31
30
|
export declare function canonicalPlaceRegister(register: PlaceRegister): string;
|
|
32
31
|
export declare function decodePlaceRegister(path: string, bytes: string): PlaceRegister;
|
|
33
32
|
export declare function emptyPlaceRegister(): PlaceRegister;
|
|
34
33
|
export declare function readPlaceRegister(repository: GitRepository): Promise<PlaceRegister>;
|
|
35
34
|
export declare function appointmentFor(register: PlaceRegister, contract: ContractId): PlaceAppointment | undefined;
|
|
36
35
|
export declare function appointManagedWorktrees(repository: GitRepository, contracts: readonly ContractId[]): Promise<PlaceRegister>;
|
|
37
|
-
export declare function appointManagedWorktree(repository: GitRepository, contract: ContractId): Promise<PlaceAppointment>;
|
|
38
36
|
export declare function releaseManagedWorktrees(repository: GitRepository, contracts: readonly ContractId[]): Promise<void>;
|
|
39
|
-
export declare function releaseManagedWorktree(repository: GitRepository, contract: ContractId): Promise<void>;
|
|
40
37
|
export declare function readManagedWorktreeAppointment(repository: GitRepository, contract: ContractId, register?: PlaceRegister): Promise<ManagedWorktreeAppointment>;
|
|
41
38
|
export {};
|
|
@@ -230,12 +230,6 @@ export function nextPlace(current) {
|
|
|
230
230
|
? composePlace(CONTRACT_PLACES[index + 1], parsed.generation)
|
|
231
231
|
: composePlace(CONTRACT_PLACES[0], parsed.generation + 1n);
|
|
232
232
|
}
|
|
233
|
-
export function firstFreePlace(register) {
|
|
234
|
-
for (let candidate = nextPlace();; candidate = nextPlace(candidate)) {
|
|
235
|
-
if (!register.byPlace.has(candidate))
|
|
236
|
-
return candidate;
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
233
|
export function canonicalPlaceRegister(register) {
|
|
240
234
|
const appointments = Object.fromEntries(register.appointments
|
|
241
235
|
.slice()
|
|
@@ -354,12 +348,6 @@ function withAppointments(register, contracts) {
|
|
|
354
348
|
export async function appointManagedWorktrees(repository, contracts) {
|
|
355
349
|
return await mutatePlaceRegister(repository, (current) => withAppointments(current, contracts));
|
|
356
350
|
}
|
|
357
|
-
export async function appointManagedWorktree(repository, contract) {
|
|
358
|
-
const appointed = appointmentFor(await appointManagedWorktrees(repository, [contract]), contract);
|
|
359
|
-
if (appointed === undefined)
|
|
360
|
-
throw new Error(`Place appointment was not recorded: ${contract}`);
|
|
361
|
-
return appointed;
|
|
362
|
-
}
|
|
363
351
|
export async function releaseManagedWorktrees(repository, contracts) {
|
|
364
352
|
if (contracts.length === 0)
|
|
365
353
|
return;
|
|
@@ -369,9 +357,6 @@ export async function releaseManagedWorktrees(repository, contracts) {
|
|
|
369
357
|
return kept.length === current.appointments.length ? current : indexedRegister(kept);
|
|
370
358
|
});
|
|
371
359
|
}
|
|
372
|
-
export async function releaseManagedWorktree(repository, contract) {
|
|
373
|
-
await releaseManagedWorktrees(repository, [contract]);
|
|
374
|
-
}
|
|
375
360
|
export async function readManagedWorktreeAppointment(repository, contract, register) {
|
|
376
361
|
try {
|
|
377
362
|
const snapshot = register ?? await readPlaceRegister(repository);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@astrosheep/keiyaku",
|
|
3
|
-
"version": "4.0.
|
|
3
|
+
"version": "4.0.5",
|
|
4
4
|
"files": [
|
|
5
5
|
"build"
|
|
6
6
|
],
|
|
@@ -36,13 +36,14 @@
|
|
|
36
36
|
"review:model-impact": "node --import tsx scripts/model-change-impact.ts",
|
|
37
37
|
"test": "npm run build && npm run test:architecture && npm run test:maintainability && npm run test:reachability && node --import tsx scripts/run-tests.mjs",
|
|
38
38
|
"test:architecture": "node --import tsx scripts/check-architecture.ts",
|
|
39
|
-
"test:e2e": "npm run build && node
|
|
39
|
+
"test:e2e": "npm run build && node scripts/run-tests.mjs tests/packaged-cli.e2e.ts",
|
|
40
40
|
"test:maintainability": "node scripts/check-maintainability.js",
|
|
41
|
-
"test:focused": "node
|
|
41
|
+
"test:focused": "node scripts/run-tests.mjs",
|
|
42
42
|
"test:reachability": "knip --include files,dependencies,unlisted,unresolved,duplicates",
|
|
43
43
|
"test:typecheck": "tsc -p tsconfig.json --noEmit"
|
|
44
44
|
},
|
|
45
45
|
"devDependencies": {
|
|
46
|
+
"@types/cross-spawn": "^6.0.6",
|
|
46
47
|
"@types/node": "^25.0.0",
|
|
47
48
|
"@typescript-eslint/parser": "^8.66.0",
|
|
48
49
|
"eslint": "^9.39.5",
|
|
@@ -55,6 +56,7 @@
|
|
|
55
56
|
"@anthropic-ai/claude-agent-sdk": "^0.3.226",
|
|
56
57
|
"@earendil-works/pi-coding-agent": "0.80.10",
|
|
57
58
|
"@opencode-ai/sdk": "1.18.3",
|
|
59
|
+
"cross-spawn": "^7.0.6",
|
|
58
60
|
"diff": "^8.0.4",
|
|
59
61
|
"yaml": "^2.9.0"
|
|
60
62
|
},
|