@astrosheep/keiyaku 4.0.0 → 4.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (241) hide show
  1. package/README.md +2 -9
  2. package/build/integrations/marketplace/plugins/keiyaku/.claude-plugin/plugin.json +2 -2
  3. package/build/integrations/marketplace/plugins/keiyaku/.codex-plugin/plugin.json +4 -4
  4. package/build/integrations/marketplace/plugins/keiyaku/opencode.js +2 -2
  5. package/build/integrations/marketplace/plugins/keiyaku/package.json +2 -2
  6. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku/SKILL.md +11 -3
  7. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-akuma/SKILL.md +9 -6
  8. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-bind/SKILL.md +94 -0
  9. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-bind/agents/openai.yaml +4 -0
  10. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-task/SKILL.md +41 -6
  11. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-workflow/SKILL.md +162 -45
  12. package/build/src/akuma/akuma.d.ts +33 -25
  13. package/build/src/akuma/akuma.js +93 -61
  14. package/build/src/akuma/archetype.d.ts +8 -0
  15. package/build/src/akuma/archetype.js +28 -9
  16. package/build/src/akuma/body.js +196 -84
  17. package/build/src/akuma/coordinate.d.ts +9 -0
  18. package/build/src/akuma/coordinate.js +22 -0
  19. package/build/src/akuma/heart/facts.d.ts +67 -15
  20. package/build/src/akuma/heart/facts.js +3 -3
  21. package/build/src/akuma/heart/index.d.ts +41 -29
  22. package/build/src/akuma/heart/index.js +72 -193
  23. package/build/src/akuma/heart/rows.d.ts +30 -53
  24. package/build/src/akuma/heart/rows.js +91 -111
  25. package/build/src/akuma/heart/schema.d.ts +1 -1
  26. package/build/src/akuma/heart/schema.js +55 -8
  27. package/build/src/akuma/heart/storage.d.ts +33 -0
  28. package/build/src/akuma/heart/storage.js +164 -0
  29. package/build/src/akuma/heart/tells.d.ts +12 -0
  30. package/build/src/akuma/heart/tells.js +79 -0
  31. package/build/src/akuma/heart/timeline.d.ts +15 -0
  32. package/build/src/akuma/heart/timeline.js +86 -0
  33. package/build/src/akuma/index.d.ts +4 -2
  34. package/build/src/akuma/index.js +1 -1
  35. package/build/src/akuma/projection.d.ts +97 -0
  36. package/build/src/akuma/projection.js +145 -0
  37. package/build/src/akuma/provider.d.ts +57 -11
  38. package/build/src/akuma/provider.js +57 -37
  39. package/build/src/akuma/providers/{claude.d.ts → claude/events.d.ts} +9 -41
  40. package/build/src/akuma/providers/claude/events.js +185 -0
  41. package/build/src/akuma/providers/claude/index.d.ts +46 -0
  42. package/build/src/akuma/providers/claude/index.js +265 -0
  43. package/build/src/akuma/providers/claude/input.d.ts +13 -0
  44. package/build/src/akuma/providers/claude/input.js +109 -0
  45. package/build/src/akuma/providers/codex-app-server/events.d.ts +1 -1
  46. package/build/src/akuma/providers/codex-app-server/events.js +12 -12
  47. package/build/src/akuma/providers/codex-app-server/index.d.ts +13 -11
  48. package/build/src/akuma/providers/codex-app-server/index.js +73 -22
  49. package/build/src/akuma/providers/index.js +22 -8
  50. package/build/src/akuma/providers/opencode-sdk/events.d.ts +48 -0
  51. package/build/src/akuma/providers/opencode-sdk/events.js +116 -0
  52. package/build/src/akuma/providers/opencode-sdk/index.d.ts +32 -0
  53. package/build/src/akuma/providers/opencode-sdk/index.js +203 -0
  54. package/build/src/akuma/providers/opencode-sdk/session.d.ts +41 -0
  55. package/build/src/akuma/providers/opencode-sdk/session.js +62 -0
  56. package/build/src/akuma/providers/pi/events.d.ts +33 -0
  57. package/build/src/akuma/providers/pi/events.js +128 -0
  58. package/build/src/akuma/providers/pi/index.d.ts +11 -0
  59. package/build/src/akuma/providers/pi/index.js +162 -0
  60. package/build/src/akuma/requests.d.ts +1 -0
  61. package/build/src/akuma/requests.js +5 -2
  62. package/build/src/alias/index.d.ts +4 -3
  63. package/build/src/alias/index.js +2 -3
  64. package/build/src/body/render.js +2 -1
  65. package/build/src/body/shape.js +7 -1
  66. package/build/src/body/verification.js +19 -4
  67. package/build/src/cli/accepted.d.ts +2 -0
  68. package/build/src/cli/accepted.js +2 -1
  69. package/build/src/cli/commands/akuma-invoke.d.ts +42 -18
  70. package/build/src/cli/commands/akuma-invoke.js +94 -50
  71. package/build/src/cli/commands/akuma.d.ts +15 -7
  72. package/build/src/cli/commands/akuma.js +94 -56
  73. package/build/src/cli/commands/contract.d.ts +142 -0
  74. package/build/src/cli/commands/contract.js +14 -0
  75. package/build/src/cli/commands/install.js +2 -0
  76. package/build/src/cli/commands/task-invoke.d.ts +3 -1
  77. package/build/src/cli/commands/task-invoke.js +27 -2
  78. package/build/src/cli/commands/task.js +2 -2
  79. package/build/src/cli/invoke.d.ts +6 -1
  80. package/build/src/cli/invoke.js +241 -43
  81. package/build/src/cli/main.js +20 -1
  82. package/build/src/cli/parse.d.ts +13 -116
  83. package/build/src/cli/parse.js +51 -78
  84. package/build/src/cli/render/akuma-tool.d.ts +2 -0
  85. package/build/src/cli/render/akuma-tool.js +6 -2
  86. package/build/src/cli/render/akuma.d.ts +2 -1
  87. package/build/src/cli/render/akuma.js +157 -217
  88. package/build/src/cli/render/catalog.d.ts +2 -0
  89. package/build/src/cli/render/catalog.js +16 -0
  90. package/build/src/cli/render/contract.d.ts +1 -1
  91. package/build/src/cli/render/contract.js +38 -5
  92. package/build/src/cli/render/kanshi.js +53 -5
  93. package/build/src/cli/render/refusal.js +17 -0
  94. package/build/src/cli/render/terminal.d.ts +13 -0
  95. package/build/src/cli/render/terminal.js +75 -0
  96. package/build/src/cli/render/text.js +5 -0
  97. package/build/src/cli/result.d.ts +15 -3
  98. package/build/src/contract-worktree.d.ts +43 -0
  99. package/build/src/contract-worktree.js +213 -0
  100. package/build/src/coordination/durable-file.d.ts +3 -0
  101. package/build/src/coordination/durable-file.js +44 -1
  102. package/build/src/core/facts/codec.js +23 -4
  103. package/build/src/core/facts/eligibility.d.ts +1 -10
  104. package/build/src/core/facts/eligibility.js +0 -49
  105. package/build/src/core/facts/observation.d.ts +3 -0
  106. package/build/src/core/facts/observation.js +12 -0
  107. package/build/src/core/facts/types.d.ts +10 -3
  108. package/build/src/core/subject.js +2 -2
  109. package/build/src/core/verbs/amend.js +12 -16
  110. package/build/src/core/verbs/bind.js +2 -16
  111. package/build/src/core/verbs/deliver.js +20 -4
  112. package/build/src/core/verbs/placement.js +3 -8
  113. package/build/src/dispatch/index.d.ts +3 -1
  114. package/build/src/dispatch/index.js +14 -5
  115. package/build/src/duration.d.ts +10 -0
  116. package/build/src/duration.js +22 -0
  117. package/build/src/git/admission.d.ts +2 -2
  118. package/build/src/git/admission.js +26 -16
  119. package/build/src/git/hooks.d.ts +15 -0
  120. package/build/src/git/hooks.js +57 -5
  121. package/build/src/git/identity.d.ts +5 -1
  122. package/build/src/git/identity.js +23 -4
  123. package/build/src/git/integration.d.ts +49 -0
  124. package/build/src/git/integration.js +214 -0
  125. package/build/src/git/observe.d.ts +25 -13
  126. package/build/src/git/observe.js +175 -72
  127. package/build/src/git/read-observation.d.ts +41 -0
  128. package/build/src/git/read-observation.js +361 -0
  129. package/build/src/git/reconcile.d.ts +15 -5
  130. package/build/src/git/reconcile.js +238 -85
  131. package/build/src/git/repository.d.ts +19 -4
  132. package/build/src/git/repository.js +77 -43
  133. package/build/src/git/scratch.d.ts +13 -0
  134. package/build/src/git/scratch.js +66 -0
  135. package/build/src/git/target-placement.d.ts +56 -0
  136. package/build/src/git/target-placement.js +255 -0
  137. package/build/src/git/tender.d.ts +48 -0
  138. package/build/src/git/tender.js +82 -0
  139. package/build/src/git/terminal-seal.d.ts +24 -0
  140. package/build/src/git/terminal-seal.js +87 -0
  141. package/build/src/git/tree.d.ts +2 -2
  142. package/build/src/git/tree.js +4 -0
  143. package/build/src/git/workspace.d.ts +21 -0
  144. package/build/src/git/workspace.js +104 -0
  145. package/build/src/identity/selector.d.ts +6 -0
  146. package/build/src/identity/selector.js +19 -0
  147. package/build/src/index.d.ts +4 -2
  148. package/build/src/index.js +2 -1
  149. package/build/src/kanshi/index.d.ts +1 -1
  150. package/build/src/kanshi/read.d.ts +5 -2
  151. package/build/src/kanshi/read.js +154 -45
  152. package/build/src/kanshi/report.d.ts +31 -6
  153. package/build/src/kanshi/select.js +1 -1
  154. package/build/src/library/address.d.ts +48 -0
  155. package/build/src/library/address.js +134 -0
  156. package/build/src/library/akuma-creation.d.ts +10 -6
  157. package/build/src/library/akuma-creation.js +12 -11
  158. package/build/src/library/bind.d.ts +21 -0
  159. package/build/src/library/bind.js +56 -0
  160. package/build/src/library/catalog.d.ts +58 -0
  161. package/build/src/library/catalog.js +72 -0
  162. package/build/src/library/configuration.d.ts +6 -15
  163. package/build/src/library/configuration.js +14 -17
  164. package/build/src/library/contract.d.ts +21 -46
  165. package/build/src/library/contract.js +193 -183
  166. package/build/src/library/delivery.d.ts +23 -0
  167. package/build/src/library/delivery.js +21 -0
  168. package/build/src/library/fleet.d.ts +65 -0
  169. package/build/src/library/fleet.js +189 -0
  170. package/build/src/library/input.d.ts +3 -0
  171. package/build/src/library/input.js +19 -0
  172. package/build/src/library/keiyaku.d.ts +16 -2
  173. package/build/src/library/keiyaku.js +11 -1
  174. package/build/src/library/mutation.d.ts +38 -0
  175. package/build/src/library/mutation.js +39 -0
  176. package/build/src/library/refusal.d.ts +21 -0
  177. package/build/src/library/refusal.js +29 -0
  178. package/build/src/library/region.d.ts +2 -1
  179. package/build/src/library/region.js +3 -3
  180. package/build/src/library/repo.d.ts +4 -1
  181. package/build/src/library/repo.js +31 -16
  182. package/build/src/protocol/attempt.d.ts +16 -3
  183. package/build/src/protocol/attempt.js +39 -8
  184. package/build/src/protocol/bind.d.ts +9 -2
  185. package/build/src/protocol/bind.js +47 -39
  186. package/build/src/protocol/intent.d.ts +42 -18
  187. package/build/src/protocol/intent.js +36 -75
  188. package/build/src/protocol/operations.d.ts +93 -35
  189. package/build/src/protocol/operations.js +235 -123
  190. package/build/src/protocol/outcome.d.ts +7 -2
  191. package/build/src/protocol/outcome.js +9 -3
  192. package/build/src/protocol/placement.d.ts +30 -0
  193. package/build/src/protocol/placement.js +114 -0
  194. package/build/src/protocol/read/audit.d.ts +9 -3
  195. package/build/src/protocol/read/audit.js +25 -9
  196. package/build/src/protocol/read/documents.d.ts +2 -2
  197. package/build/src/protocol/read/documents.js +4 -4
  198. package/build/src/protocol/read/status.d.ts +11 -5
  199. package/build/src/protocol/read/status.js +28 -15
  200. package/build/src/protocol/run.d.ts +49 -7
  201. package/build/src/protocol/run.js +53 -25
  202. package/build/src/runtime/proc/line-rpc.d.ts +1 -0
  203. package/build/src/runtime/proc/line-rpc.js +21 -0
  204. package/build/src/runtime/proc/run.d.ts +18 -2
  205. package/build/src/runtime/proc/run.js +34 -8
  206. package/build/src/settings.d.ts +6 -0
  207. package/build/src/settings.js +29 -14
  208. package/build/src/settlement/fence.d.ts +9 -0
  209. package/build/src/settlement/fence.js +23 -0
  210. package/build/src/settlement/holder.d.ts +25 -4
  211. package/build/src/settlement/holder.js +102 -28
  212. package/build/src/settlement/settle.d.ts +8 -0
  213. package/build/src/settlement/settle.js +102 -28
  214. package/build/src/task/board.d.ts +20 -2
  215. package/build/src/task/board.js +11 -1
  216. package/build/src/task/compose.d.ts +2 -2
  217. package/build/src/task/compose.js +2 -2
  218. package/build/src/task/context.js +2 -7
  219. package/build/src/task/index.d.ts +9 -9
  220. package/build/src/task/index.js +15 -12
  221. package/build/src/task/operations.d.ts +31 -12
  222. package/build/src/task/operations.js +8 -4
  223. package/build/src/task/store.d.ts +4 -6
  224. package/build/src/task/store.js +3 -3
  225. package/build/src/verification/declaration.d.ts +1 -0
  226. package/build/src/verification/execution.d.ts +49 -0
  227. package/build/src/verification/execution.js +104 -0
  228. package/build/src/world.d.ts +13 -0
  229. package/build/src/world.js +103 -0
  230. package/package.json +7 -4
  231. package/build/src/akuma/activity.d.ts +0 -66
  232. package/build/src/akuma/activity.js +0 -139
  233. package/build/src/akuma/providers/claude.js +0 -305
  234. package/build/src/context-root.d.ts +0 -4
  235. package/build/src/context-root.js +0 -15
  236. package/build/src/git/delivery.d.ts +0 -20
  237. package/build/src/git/delivery.js +0 -109
  238. package/build/src/git/verification.d.ts +0 -14
  239. package/build/src/git/verification.js +0 -56
  240. package/build/src/verification/producer.d.ts +0 -23
  241. package/build/src/verification/producer.js +0 -54
@@ -1,5 +1,5 @@
1
1
  import { type TaskId } from "./identity.js";
2
- import { type TaskWorld } from "./store.js";
2
+ import type { WorldRoot } from "../world.js";
3
3
  import type { TaskRefusal, TaskRetry } from "./operations.js";
4
4
  type TaskDocumentChange = Readonly<{
5
5
  taskId: TaskId;
@@ -21,5 +21,5 @@ export type TaskCompositionResult = Readonly<{
21
21
  }>;
22
22
  draft: string;
23
23
  }>;
24
- export declare function composeTasks(world: TaskWorld, markdown: string, signal?: AbortSignal): Promise<TaskCompositionResult>;
24
+ export declare function composeTasks(world: WorldRoot, markdown: string, signal?: AbortSignal): Promise<TaskCompositionResult>;
25
25
  export {};
@@ -195,9 +195,9 @@ export async function composeTasks(world, markdown, signal) {
195
195
  if ("kind" in sketch)
196
196
  return { kind: "refused", refusal: sketch };
197
197
  const at = currentTimestamp();
198
- const context = sketch.namespace ?? readNamespaceContext(world.root);
198
+ const context = sketch.namespace ?? readNamespaceContext(world);
199
199
  if (context === "malformed")
200
- return { kind: "refused", refusal: { kind: "invalid-namespace-context", path: `${world.root}/.keiyaku/namespace/current` } };
200
+ return { kind: "refused", refusal: { kind: "invalid-namespace-context", path: `${world}/.keiyaku/namespace/current` } };
201
201
  const namespace = context === "absent" ? [] : context;
202
202
  const initial = readBoard(world);
203
203
  const planned = plan(sketch, initial.board, namespace, at);
@@ -1,6 +1,6 @@
1
1
  import { lstatSync, mkdirSync, readFileSync, } from "node:fs";
2
2
  import { join } from "node:path";
3
- import { replaceFileDurably } from "../coordination/durable-file.js";
3
+ import { repairDerivedFile, replaceFileDurably } from "../coordination/durable-file.js";
4
4
  import { normalizeIdentityStem } from "../identity/normalize.js";
5
5
  function directory(root) { return join(root, ".keiyaku", "namespace"); }
6
6
  function currentPath(root) { return join(directory(root), "current"); }
@@ -38,12 +38,7 @@ export function installNamespaceContext(root, segments) {
38
38
  replaceFileDurably(currentPath(root), `${segments.join("/")}\n`);
39
39
  }
40
40
  function installIgnore(root) {
41
- const ignore = ignorePath(root);
42
- const stat = lstatSync(ignore, { throwIfNoEntry: false });
43
- if (stat !== undefined && (!stat.isFile() || stat.isSymbolicLink()))
44
- throw new Error(`namespace ignore is not a regular file: ${ignore}`);
45
- if (stat === undefined || readFileSync(ignore, "utf8") !== "*\n")
46
- replaceFileDurably(ignore, "*\n");
41
+ repairDerivedFile(ignorePath(root), "*\n");
47
42
  }
48
43
  export function repairNamespaceContext(root, segments) {
49
44
  const current = readNamespaceContext(root);
@@ -1,9 +1,10 @@
1
- import { type BlockedTaskRow, type TaskDetailFacts, type TaskDoctorIssue, type TaskRow, type TaskTreeNode } from "./board.js";
1
+ import type { WorldRoot } from "../world.js";
2
+ export type { WorldRoot } from "../world.js";
3
+ import { type BlockedTaskRow, type TaskDetailFacts, type TaskDoctorIssue, type TaskRef, type TaskRow, type TaskTreeNode } from "./board.js";
2
4
  import { type TaskCompositionResult } from "./compose.js";
3
5
  import { TaskAuthorityCorruptionError, type TaskPriority, type TaskState } from "./document.js";
4
6
  import { type TaskId } from "./identity.js";
5
7
  import { type AddTaskDocumentInput, type AddTaskInput, type TaskBatchResult, type TaskMutationResult, type TaskOutcome, type TaskRefusal, type TaskRetry, type TaskUpdateResult, type TaskView, type UpdateTaskInput } from "./operations.js";
6
- import { type TaskWorld } from "./store.js";
7
8
  export type TaskDetail = Omit<TaskDetailFacts, "task"> & Readonly<{
8
9
  task: TaskView;
9
10
  }>;
@@ -14,12 +15,12 @@ export type TaskDoctorReport = Readonly<{
14
15
  issues: readonly TaskDoctorIssue[];
15
16
  }>;
16
17
  export type TaskDependencyTree = TaskOutcome<TaskTreeNode>;
17
- export type { AddTaskDocumentInput, AddTaskInput, BlockedTaskRow, TaskBatchResult, TaskCompositionResult, TaskDoctorIssue, TaskId, TaskMutationResult, TaskOutcome, TaskPriority, TaskRefusal, TaskRetry, TaskRow, TaskState, TaskUpdateResult, TaskView, UpdateTaskInput };
18
+ export type { AddTaskDocumentInput, AddTaskInput, BlockedTaskRow, TaskBatchResult, TaskCompositionResult, TaskDoctorIssue, TaskId, TaskMutationResult, TaskOutcome, TaskPriority, TaskRef, TaskRefusal, TaskRetry, TaskRow, TaskState, TaskUpdateResult, TaskView, UpdateTaskInput };
18
19
  export { TaskAuthorityCorruptionError };
19
20
  declare class TaskHandle {
20
21
  readonly id: TaskId;
21
22
  private readonly world;
22
- constructor(id: TaskId, world: TaskWorld);
23
+ constructor(id: TaskId, world: WorldRoot);
23
24
  read(): Promise<TaskDetail | null>;
24
25
  tree(input?: Readonly<{
25
26
  full?: boolean;
@@ -39,6 +40,7 @@ declare class TaskHandle {
39
40
  signal?: AbortSignal;
40
41
  }): Promise<TaskMutationResult>;
41
42
  done(input?: {
43
+ note?: string;
42
44
  signal?: AbortSignal;
43
45
  }): Promise<TaskMutationResult>;
44
46
  drop(input?: {
@@ -48,9 +50,9 @@ declare class TaskHandle {
48
50
  }
49
51
  export type Task = TaskHandle;
50
52
  declare class TasksHandle {
51
- readonly root: string;
52
53
  private readonly world;
53
- constructor(path?: string);
54
+ readonly root: WorldRoot;
55
+ constructor(world: WorldRoot);
54
56
  namespace(): Promise<TaskNamespaceResult>;
55
57
  setNamespace(input: Readonly<{
56
58
  namespace: readonly string[];
@@ -84,7 +86,5 @@ declare class TasksHandle {
84
86
  }
85
87
  export type Tasks = TasksHandle;
86
88
  export declare const Tasks: Readonly<{
87
- at(input?: Readonly<{
88
- path?: string;
89
- }>): Tasks;
89
+ of(root: WorldRoot): Tasks;
90
90
  }>;
@@ -1,4 +1,3 @@
1
- import { resolveContextRoot } from "../context-root.js";
2
1
  import { buildTree, diagnoseBoard, projectDetailFacts } from "./board.js";
3
2
  import { composeTasks } from "./compose.js";
4
3
  import { TaskAuthorityCorruptionError } from "./document.js";
@@ -135,8 +134,8 @@ class TaskHandle {
135
134
  update(input) { return updateTask(this.world, this.id, updateInput(input)); }
136
135
  lifecycle(verb, input) {
137
136
  const value = record(input ?? {}, `${verb} input`);
138
- closed(value, verb === "drop" ? ["note", "signal"] : ["signal"], `${verb} input`);
139
- const note = verb === "drop" ? text(value.note, "note") : undefined;
137
+ closed(value, verb === "drop" || verb === "done" ? ["note", "signal"] : ["signal"], `${verb} input`);
138
+ const note = verb === "drop" || verb === "done" ? text(value.note, "note") : undefined;
140
139
  return lifecycleTask(this.world, this.id, verb, signal(value.signal), note);
141
140
  }
142
141
  start(input) { return this.lifecycle("start", input); }
@@ -147,12 +146,11 @@ class TaskHandle {
147
146
  drop(input) { return this.lifecycle("drop", input); }
148
147
  }
149
148
  class TasksHandle {
150
- root;
151
149
  world;
152
- constructor(path) {
153
- const root = resolveContextRoot({ from: path ?? process.cwd(), marker: ".keiyaku" });
154
- this.world = { root };
155
- this.root = root;
150
+ root;
151
+ constructor(world) {
152
+ this.world = world;
153
+ this.root = world;
156
154
  }
157
155
  async namespace() { const value = currentNamespace(this.world); return "kind" in value ? { kind: "refused", refusal: value } : { kind: "accepted", value }; }
158
156
  async setNamespace(input) { const v = record(input, "setNamespace input"); closed(v, ["namespace"], "setNamespace input"); const ns = namespace(v.namespace); if (ns === undefined)
@@ -170,10 +168,15 @@ class TasksHandle {
170
168
  throw new TypeError("scope must be namespace or world"); return blockedTasks(this.world, v.scope); }
171
169
  async doctor() { return { issues: diagnoseBoard(readBoard(this.world).board) }; }
172
170
  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")
173
- throw new TypeError("batch verb is invalid"); const note = text(v.note, "note"); if (note !== undefined && verb !== "drop")
174
- throw new TypeError("batch note is valid only for drop"); return batchTasks(this.world, verb, taskIds(v.ids, "ids") ?? [], signal(v.signal), note); }
171
+ throw new TypeError("batch verb is invalid"); const note = text(v.note, "note"); if (note !== undefined && verb !== "done" && verb !== "drop")
172
+ 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); }
175
173
  compose(input) { const v = record(input, "compose input"); closed(v, ["markdown", "signal"], "compose input"); const markdown = text(v.markdown, "markdown"); if (markdown === undefined)
176
174
  throw new TypeError("markdown is required"); return composeTasks(this.world, markdown, signal(v.signal)); }
177
175
  }
178
- export const Tasks = Object.freeze({ at(input) { if (input === undefined)
179
- return new TasksHandle(); const value = record(input, "Tasks.at input"); closed(value, ["path"], "Tasks.at input"); return new TasksHandle(text(value.path, "path")); } });
176
+ export const Tasks = Object.freeze({
177
+ of(root) {
178
+ if (typeof root !== "string")
179
+ throw new TypeError("Tasks.of root must be a WorldRoot");
180
+ return new TasksHandle(root);
181
+ },
182
+ });
@@ -1,7 +1,7 @@
1
1
  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
- import { type TaskWorld } from "./store.js";
4
+ import type { WorldRoot } from "../world.js";
5
5
  export type TaskView = Readonly<TaskDocument & {
6
6
  namespace: readonly string[];
7
7
  }>;
@@ -102,14 +102,33 @@ export type UpdateTaskInput = Readonly<{
102
102
  signal?: AbortSignal;
103
103
  }>;
104
104
  export declare function taskView(document: TaskDocument): TaskView;
105
- export declare function addTask(world: TaskWorld, input: AddTaskInput): Promise<TaskMutationResult>;
106
- export declare function addTaskDocument(world: TaskWorld, input: AddTaskDocumentInput): Promise<TaskMutationResult>;
107
- export declare function updateTask(world: TaskWorld, id: TaskId, input: UpdateTaskInput): Promise<TaskUpdateResult>;
108
- export declare function lifecycleTask(world: TaskWorld, id: TaskId, verb: TaskLifecycleVerb, signal?: AbortSignal, note?: string): Promise<TaskMutationResult>;
109
- export declare function batchTasks(world: TaskWorld, verb: "done" | "drop" | "hold", ids: readonly TaskId[], signal?: AbortSignal, note?: string): Promise<TaskBatchResult>;
110
- export declare function settleTask(world: TaskWorld, id: TaskId, desired: "done" | "open-from-done"): Promise<SettledTaskResult>;
111
- export declare function listTasks(world: TaskWorld, selection: "active" | "closed" | "all", scope?: "namespace" | "world"): TaskOutcome<readonly TaskRow[]>;
112
- export declare function readyTasks(world: TaskWorld, scope?: "namespace" | "world"): TaskOutcome<readonly TaskRow[]>;
113
- export declare function blockedTasks(world: TaskWorld, scope?: "namespace" | "world"): TaskOutcome<readonly BlockedTaskRow[]>;
114
- export declare function setCurrentNamespace(world: TaskWorld, namespace: readonly string[]): void;
115
- export declare function currentNamespace(world: TaskWorld): readonly string[] | TaskRefusal;
105
+ export declare function addTask(world: WorldRoot, input: AddTaskInput): Promise<TaskMutationResult>;
106
+ export declare function addTaskDocument(world: WorldRoot, input: AddTaskDocumentInput): Promise<TaskMutationResult>;
107
+ export declare function updateTask(world: WorldRoot, id: TaskId, input: UpdateTaskInput): Promise<TaskUpdateResult>;
108
+ export declare function lifecycleTask(world: WorldRoot, id: TaskId, verb: TaskLifecycleVerb, signal?: AbortSignal, note?: string): Promise<TaskMutationResult>;
109
+ 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, desired: "done" | "open-from-done"): Promise<SettledTaskResult>;
111
+ export declare function listTasks(world: WorldRoot, selection: "active" | "closed" | "all", scope?: "namespace" | "world"): TaskOutcome<readonly TaskRow[]>;
112
+ export declare function readyTasks(world: WorldRoot, scope?: "namespace" | "world"): TaskOutcome<readonly TaskRow[]>;
113
+ export declare function blockedTasks(world: WorldRoot, scope?: "namespace" | "world"): TaskOutcome<readonly BlockedTaskRow[]>;
114
+ /** Internal composite observation from one complete Task board read. */
115
+ export declare function observeTaskStatusRows(world: WorldRoot): (Readonly<{
116
+ id: TaskId;
117
+ title: string;
118
+ state: TaskState;
119
+ priority: TaskPriority;
120
+ disposition: import("./board.js").TaskDisposition;
121
+ }> | {
122
+ blockers: readonly Readonly<{
123
+ id: TaskId;
124
+ title: string | null;
125
+ state: TaskState | "missing";
126
+ }>[];
127
+ id: TaskId;
128
+ title: string;
129
+ state: TaskState;
130
+ priority: TaskPriority;
131
+ disposition: import("./board.js").TaskDisposition;
132
+ })[];
133
+ export declare function setCurrentNamespace(world: WorldRoot, namespace: readonly string[]): void;
134
+ export declare function currentNamespace(world: WorldRoot): readonly string[] | TaskRefusal;
@@ -1,6 +1,6 @@
1
1
  import { documentDiff } from "../markdown/diff.js";
2
2
  import { installNamespaceContext, readNamespaceContext } from "./context.js";
3
- import { relationProblem, projectBlocked, projectReady, projectRows } from "./board.js";
3
+ import { relationProblem, projectBlocked, projectReady, projectRows, projectStatusRows } from "./board.js";
4
4
  import { parseTaskCreationDocument, serializeTaskDocument } from "./document.js";
5
5
  import { allocateLocalId, deriveLocalStem, formatTaskId, parseTaskId, sameNamespace } from "./identity.js";
6
6
  import { authorityPath, readBoard, replaceAuthority, withTaskLocks, } from "./store.js";
@@ -12,8 +12,8 @@ function retry(reason) { return { kind: "retry", reason }; }
12
12
  function context(world, explicit) {
13
13
  if (explicit !== undefined)
14
14
  return explicit;
15
- const current = readNamespaceContext(world.root);
16
- return current === "malformed" ? { kind: "invalid-namespace-context", path: `${world.root}/.keiyaku/namespace/current` }
15
+ const current = readNamespaceContext(world);
16
+ return current === "malformed" ? { kind: "invalid-namespace-context", path: `${world}/.keiyaku/namespace/current` }
17
17
  : current === "absent" ? [] : current;
18
18
  }
19
19
  function occupied(board, namespace) {
@@ -187,7 +187,11 @@ export function blockedTasks(world, scope) {
187
187
  return { kind: "refused", refusal: selected };
188
188
  return { kind: "accepted", value: projectBlocked(readBoard(world).board, selected) };
189
189
  }
190
- export function setCurrentNamespace(world, namespace) { installNamespaceContext(world.root, namespace); }
190
+ /** Internal composite observation from one complete Task board read. */
191
+ export function observeTaskStatusRows(world) {
192
+ return projectStatusRows(readBoard(world).board, null);
193
+ }
194
+ export function setCurrentNamespace(world, namespace) { installNamespaceContext(world, namespace); }
191
195
  export function currentNamespace(world) {
192
196
  const selected = context(world);
193
197
  return selected;
@@ -1,22 +1,20 @@
1
+ import type { WorldRoot } from "../world.js";
1
2
  import { type TaskId } from "./identity.js";
2
3
  import type { TaskBoard } from "./board.js";
3
- export type TaskWorld = Readonly<{
4
- root: string;
5
- }>;
6
4
  export type BoardSnapshot = Readonly<{
7
5
  board: TaskBoard;
8
6
  bytes: ReadonlyMap<TaskId, Uint8Array>;
9
7
  }>;
10
- export declare function readBoard(world: TaskWorld): BoardSnapshot;
8
+ export declare function readBoard(world: WorldRoot): BoardSnapshot;
11
9
  export declare function replaceAuthority(input: Readonly<{
12
10
  path: string;
13
11
  expected: Uint8Array | null;
14
12
  next: Uint8Array;
15
13
  }>): "replaced" | "concurrent-modification";
16
14
  export declare function withTaskLocks<T>(input: Readonly<{
17
- world: TaskWorld;
15
+ world: WorldRoot;
18
16
  allocation: boolean;
19
17
  ids: readonly TaskId[];
20
18
  signal?: AbortSignal;
21
19
  }>, action: () => Promise<T>): Promise<T | "busy">;
22
- export declare function authorityPath(world: TaskWorld, id: TaskId): string;
20
+ export declare function authorityPath(world: WorldRoot, id: TaskId): string;
@@ -4,7 +4,7 @@ import { dirname, relative, resolve, sep } from "node:path";
4
4
  import { acquireSqliteTransactionLock, SqliteTransactionLockError } from "../coordination/sqlite-transaction-lock.js";
5
5
  import { parseTaskDocument } from "./document.js";
6
6
  import { formatTaskId, parseTaskId, taskAuthorityPath } from "./identity.js";
7
- function tasksDirectory(world) { return resolve(world.root, ".keiyaku", "tasks"); }
7
+ function tasksDirectory(world) { return resolve(world, ".keiyaku", "tasks"); }
8
8
  function authorityFiles(directory) {
9
9
  if (!existsSync(directory))
10
10
  return [];
@@ -88,11 +88,11 @@ export function replaceAuthority(input) {
88
88
  }
89
89
  function lockPath(world, id) {
90
90
  const coordinate = parseTaskId(id);
91
- return resolve(world.root, ".keiyaku", "locks", "task", ...coordinate.namespace, `${coordinate.localId}.sqlite`);
91
+ return resolve(world, ".keiyaku", "locks", "task", ...coordinate.namespace, `${coordinate.localId}.sqlite`);
92
92
  }
93
93
  export async function withTaskLocks(input, action) {
94
94
  const paths = [
95
- ...(input.allocation ? [resolve(input.world.root, ".keiyaku", "locks", "task-allocation.sqlite")] : []),
95
+ ...(input.allocation ? [resolve(input.world, ".keiyaku", "locks", "task-allocation.sqlite")] : []),
96
96
  ...[...new Set(input.ids)].sort((a, b) => Buffer.compare(Buffer.from(a), Buffer.from(b))).map((id) => lockPath(input.world, id)),
97
97
  ];
98
98
  const held = [];
@@ -4,6 +4,7 @@ export type VerificationExecutor = "bash" | "zsh" | "pwsh";
4
4
  export type VerificationDeclaration = Readonly<{
5
5
  readonly executor: VerificationExecutor;
6
6
  readonly script: string;
7
+ readonly timeoutMs?: number;
7
8
  }>;
8
9
  export type VerificationDefinition = Readonly<{
9
10
  readonly segment: DocumentSegmentKey;
@@ -0,0 +1,49 @@
1
+ import type { SnapshotId } from "../core/facts/types.js";
2
+ import { type HookFailure } from "../git/hooks.js";
3
+ import type { MaterializedScratchCandidate, WorktreeLeak } from "../git/scratch.js";
4
+ import type { GitRepository } from "../git/repository.js";
5
+ import type { Settings } from "../settings.js";
6
+ import type { VerificationDeclaration } from "./declaration.js";
7
+ type VerificationVerdict = "satisfied" | "unsatisfied";
8
+ export type VerificationTerminalOutcome = Readonly<{
9
+ kind: "terminal";
10
+ verdict: VerificationVerdict;
11
+ summary?: string;
12
+ }>;
13
+ export type VerificationNonterminalOutcome = Readonly<{
14
+ kind: "unknown-exit";
15
+ }> | Readonly<{
16
+ kind: "cancelled";
17
+ }> | Readonly<{
18
+ kind: "spawn-error";
19
+ diagnostic: string;
20
+ }>;
21
+ export type VerificationExecutionStop = VerificationNonterminalOutcome | Readonly<{
22
+ kind: "candidate-unavailable" | "environment-failure";
23
+ diagnostic: string;
24
+ }> | Readonly<{
25
+ kind: "environment-failure";
26
+ command: number;
27
+ detail: HookFailure;
28
+ }>;
29
+ export type VerificationExecution = Readonly<{
30
+ outcome: VerificationTerminalOutcome | VerificationExecutionStop;
31
+ cleanup?: Readonly<{
32
+ phase: "destroy";
33
+ command: number;
34
+ detail: HookFailure;
35
+ }>;
36
+ leak?: WorktreeLeak;
37
+ }>;
38
+ export type ExecuteVerificationInput = Readonly<{
39
+ repository: GitRepository;
40
+ candidate: SnapshotId;
41
+ declarations: readonly VerificationDeclaration[];
42
+ environment: NodeJS.ProcessEnv;
43
+ materializeScratchCandidate: (repository: GitRepository, candidate: SnapshotId) => MaterializedScratchCandidate;
44
+ projectSettings: (root: string) => Settings;
45
+ signal?: AbortSignal;
46
+ }>;
47
+ /** Execute one disposable Verification attempt over the exact integration snapshot. */
48
+ export declare function executeVerification(input: ExecuteVerificationInput): Promise<VerificationExecution>;
49
+ export {};
@@ -0,0 +1,104 @@
1
+ import { runHookCommands, worktreeHooksFrom } from "../git/hooks.js";
2
+ import { runProcess } from "../runtime/proc/run.js";
3
+ const SUMMARY_BYTES = 32 * 1024;
4
+ function argvFor(declaration) {
5
+ const args = declaration.executor === "pwsh" ? ["-Command", declaration.script] : ["-c", declaration.script];
6
+ return [declaration.executor, ...args];
7
+ }
8
+ function processDiagnostic(declaration, index, outcome) {
9
+ if (outcome.code === 0 && outcome.stdout.length === 0 && outcome.stderr.length === 0 && !outcome.truncated) {
10
+ return null;
11
+ }
12
+ return [
13
+ `[${index + 1} ${declaration.executor} exit ${outcome.code}${outcome.truncated ? " output-truncated" : ""}]`,
14
+ ...(outcome.stdout.length === 0 ? [] : [`stdout:\n${outcome.stdout}`]),
15
+ ...(outcome.stderr.length === 0 ? [] : [`stderr:\n${outcome.stderr}`]),
16
+ ].join("\n");
17
+ }
18
+ function appendSummary(current, diagnostic) {
19
+ if (diagnostic === null)
20
+ return current;
21
+ const combined = Buffer.from(current === undefined ? diagnostic : `${current}\n\n${diagnostic}`);
22
+ if (combined.length <= SUMMARY_BYTES)
23
+ return combined.toString("utf8");
24
+ const marker = Buffer.from("[earlier output truncated]\n");
25
+ let start = combined.length - (SUMMARY_BYTES - marker.length);
26
+ while (start < combined.length && (combined[start] & 0xc0) === 0x80)
27
+ start += 1;
28
+ return Buffer.concat([marker, combined.subarray(start)]).toString("utf8");
29
+ }
30
+ async function executeDeclarations(input) {
31
+ let verdict = "satisfied";
32
+ let summary;
33
+ for (const [index, declaration] of input.declarations.entries()) {
34
+ const outcome = await runProcess({
35
+ argv: argvFor(declaration),
36
+ cwd: input.cwd,
37
+ env: input.environment,
38
+ ...(input.signal === undefined ? {} : { signal: input.signal }),
39
+ ...(declaration.timeoutMs === undefined ? {} : { timeoutMs: declaration.timeoutMs }),
40
+ });
41
+ if (outcome.kind === "timeout") {
42
+ verdict = "unsatisfied";
43
+ summary = appendSummary(summary, `[${index + 1} ${declaration.executor} timeout after ${declaration.timeoutMs}ms]`);
44
+ continue;
45
+ }
46
+ if (outcome.kind === "spawn-error" || outcome.kind === "unknown-exit" || outcome.kind === "cancelled")
47
+ return outcome;
48
+ if (outcome.code !== 0)
49
+ verdict = "unsatisfied";
50
+ summary = appendSummary(summary, processDiagnostic(declaration, index, outcome));
51
+ }
52
+ return { kind: "terminal", verdict, ...(summary === undefined ? {} : { summary }) };
53
+ }
54
+ /** Execute one disposable Verification attempt over the exact integration snapshot. */
55
+ export async function executeVerification(input) {
56
+ let scratch;
57
+ try {
58
+ scratch = input.materializeScratchCandidate(input.repository, input.candidate);
59
+ }
60
+ catch (error) {
61
+ return { outcome: { kind: "candidate-unavailable", diagnostic: error instanceof Error ? error.message : String(error) } };
62
+ }
63
+ let outcome;
64
+ let cleanup;
65
+ let leak = null;
66
+ let destroy;
67
+ try {
68
+ try {
69
+ const hooks = worktreeHooksFrom({ settings: input.projectSettings(scratch.cwd) });
70
+ destroy = hooks.destroy;
71
+ const readiness = await runHookCommands(scratch.cwd, hooks.create, input.signal);
72
+ outcome = readiness.kind === "cancelled"
73
+ ? { kind: "cancelled" }
74
+ : readiness.kind === "failed"
75
+ ? { kind: "environment-failure", command: readiness.command, detail: readiness.failure }
76
+ : await executeDeclarations({
77
+ declarations: input.declarations,
78
+ cwd: scratch.cwd,
79
+ environment: input.environment,
80
+ ...(input.signal === undefined ? {} : { signal: input.signal }),
81
+ });
82
+ }
83
+ catch (error) {
84
+ outcome = { kind: "environment-failure", diagnostic: error instanceof Error ? error.message : String(error) };
85
+ }
86
+ }
87
+ finally {
88
+ if (destroy !== undefined) {
89
+ const result = await runHookCommands(scratch.cwd, destroy);
90
+ if (result.kind === "cancelled")
91
+ throw new Error("scratch destroy cancelled without a signal");
92
+ if (result.kind === "failed")
93
+ cleanup = { phase: "destroy", command: result.command, detail: result.failure };
94
+ }
95
+ leak = scratch.dispose();
96
+ }
97
+ if (outcome === undefined)
98
+ throw new Error("Verification ended without an outcome");
99
+ return {
100
+ outcome,
101
+ ...(cleanup === undefined ? {} : { cleanup }),
102
+ ...(leak === null ? {} : { leak }),
103
+ };
104
+ }
@@ -0,0 +1,13 @@
1
+ declare const WORLD_BRAND: unique symbol;
2
+ export type WorldRoot = string & {
3
+ readonly [WORLD_BRAND]: true;
4
+ };
5
+ export declare class WorldError extends Error {
6
+ readonly kind: "invalid-world" | "home-world" | "root-world";
7
+ constructor(kind: "invalid-world" | "home-world" | "root-world", message: string);
8
+ }
9
+ export declare const World: Readonly<{
10
+ locate(input: string): WorldRoot | null;
11
+ at(input: string): WorldRoot;
12
+ }>;
13
+ export {};
@@ -0,0 +1,103 @@
1
+ import { existsSync, mkdirSync, realpathSync, statSync } from "node:fs";
2
+ import { homedir } from "node:os";
3
+ import { dirname, join, parse, resolve } from "node:path";
4
+ const WORLD_BRAND = Symbol("keiyaku.world");
5
+ export class WorldError extends Error {
6
+ kind;
7
+ constructor(kind, message) {
8
+ super(message);
9
+ this.name = "WorldError";
10
+ this.kind = kind;
11
+ }
12
+ }
13
+ function homeRoot() {
14
+ try {
15
+ return realpathSync(resolve(homedir()));
16
+ }
17
+ catch {
18
+ return resolve(homedir());
19
+ }
20
+ }
21
+ function directory(input, label) {
22
+ if (typeof input !== "string" || input.trim().length === 0) {
23
+ throw new TypeError(`${label} must be a nonblank path`);
24
+ }
25
+ const path = resolve(input);
26
+ let real;
27
+ try {
28
+ real = realpathSync(path);
29
+ }
30
+ catch (error) {
31
+ throw new WorldError("invalid-world", `world path is not an existing directory: ${path}`);
32
+ }
33
+ try {
34
+ if (!statSync(real).isDirectory())
35
+ throw new Error("not a directory");
36
+ }
37
+ catch {
38
+ throw new WorldError("invalid-world", `world path is not a directory: ${real}`);
39
+ }
40
+ return real;
41
+ }
42
+ function brand(path) { return path; }
43
+ function marker(root) { return join(root, ".keiyaku"); }
44
+ function rejectReservedRoot(root) {
45
+ if (root === homeRoot()) {
46
+ throw new WorldError("home-world", "the user home directory cannot be a Keiyaku world");
47
+ }
48
+ if (root === parse(root).root) {
49
+ throw new WorldError("root-world", "the filesystem root cannot be a Keiyaku world");
50
+ }
51
+ }
52
+ function ensureMarker(root) {
53
+ const path = marker(root);
54
+ if (existsSync(path)) {
55
+ try {
56
+ if (!statSync(path).isDirectory())
57
+ throw new Error("not a directory");
58
+ }
59
+ catch {
60
+ throw new WorldError("invalid-world", `world marker is not a directory: ${path}`);
61
+ }
62
+ return;
63
+ }
64
+ mkdirSync(path, { recursive: true });
65
+ }
66
+ function exact(input) {
67
+ const root = directory(input, "world");
68
+ rejectReservedRoot(root);
69
+ ensureMarker(root);
70
+ return brand(root);
71
+ }
72
+ function locateInput(input) {
73
+ return directory(input, "world location");
74
+ }
75
+ export const World = Object.freeze({
76
+ locate(input) {
77
+ let candidate = locateInput(input);
78
+ const home = homeRoot();
79
+ for (;;) {
80
+ const filesystemRoot = parse(candidate).root;
81
+ if (candidate !== home && candidate !== filesystemRoot && existsSync(marker(candidate))) {
82
+ try {
83
+ if (!statSync(marker(candidate)).isDirectory()) {
84
+ throw new WorldError("invalid-world", `world marker is not a directory: ${marker(candidate)}`);
85
+ }
86
+ }
87
+ catch (error) {
88
+ if (error instanceof WorldError)
89
+ throw error;
90
+ throw new WorldError("invalid-world", `world marker is not a directory: ${marker(candidate)}`);
91
+ }
92
+ return brand(candidate);
93
+ }
94
+ const parent = dirname(candidate);
95
+ if (parent === candidate || candidate === filesystemRoot)
96
+ return null;
97
+ candidate = parent;
98
+ }
99
+ },
100
+ at(input) {
101
+ return exact(input);
102
+ },
103
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@astrosheep/keiyaku",
3
- "version": "4.0.0",
3
+ "version": "4.0.1",
4
4
  "files": [
5
5
  "build"
6
6
  ],
@@ -34,10 +34,11 @@
34
34
  "scripts": {
35
35
  "build": "node --input-type=module -e \"import { rmSync } from 'node:fs'; rmSync('build', { recursive: true, force: true })\" && tsc -p tsconfig.json && node scripts/copy-integrations.js && node --input-type=module -e \"import { chmodSync } from 'node:fs'; chmodSync('build/src/cli/index.js', 0o755)\"",
36
36
  "review:model-impact": "node --import tsx scripts/model-change-impact.ts",
37
- "test": "npm run test:architecture && npm run test:maintainability && npm run test:reachability && node --import tsx --test tests/**/*.test.ts tests/maintainability.test.js && npm run test:dogfood",
37
+ "test": "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:dogfood": "npm run build && node --import tsx --test tests/day1-shell-dogfood.dogfood.ts",
39
+ "test:e2e": "npm run build && node --import tsx --test tests/packaged-cli.e2e.ts",
40
40
  "test:maintainability": "node scripts/check-maintainability.js",
41
+ "test:focused": "node --import tsx --test --test-reporter=dot",
41
42
  "test:reachability": "knip --include files,dependencies,unlisted,unresolved,duplicates",
42
43
  "test:typecheck": "tsc -p tsconfig.json --noEmit"
43
44
  },
@@ -51,10 +52,12 @@
51
52
  },
52
53
  "dependencies": {
53
54
  "@anthropic-ai/claude-agent-sdk": "^0.3.226",
55
+ "@earendil-works/pi-coding-agent": "0.80.10",
56
+ "@opencode-ai/sdk": "1.18.3",
54
57
  "diff": "^8.0.4",
55
58
  "yaml": "^2.9.0"
56
59
  },
57
- "description": "Keiyaku v4 is a clean implementation of an agent-facing contract workflow.",
60
+ "description": "Keiyaku is an agent-facing contract workflow.",
58
61
  "directories": {
59
62
  "doc": "docs",
60
63
  "test": "tests"