@astrosheep/keiyaku 4.0.1 → 4.0.2

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 (245) hide show
  1. package/README.md +137 -5
  2. package/build/integrations/marketplace/plugins/keiyaku/.claude-plugin/plugin.json +1 -1
  3. package/build/integrations/marketplace/plugins/keiyaku/.codex-plugin/plugin.json +1 -1
  4. package/build/integrations/marketplace/plugins/keiyaku/package.json +1 -1
  5. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku/SKILL.md +3 -3
  6. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-akuma/SKILL.md +91 -35
  7. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-bind/SKILL.md +98 -58
  8. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-workflow/SKILL.md +152 -53
  9. package/build/src/akuma/abort.d.ts +2 -0
  10. package/build/src/akuma/abort.js +50 -0
  11. package/build/src/akuma/akuma.d.ts +23 -14
  12. package/build/src/akuma/akuma.js +120 -104
  13. package/build/src/akuma/archetype.d.ts +7 -6
  14. package/build/src/akuma/archetype.js +68 -41
  15. package/build/src/akuma/body.d.ts +3 -5
  16. package/build/src/akuma/body.js +408 -213
  17. package/build/src/akuma/heart/facts.d.ts +25 -34
  18. package/build/src/akuma/heart/facts.js +18 -7
  19. package/build/src/akuma/heart/index.d.ts +39 -42
  20. package/build/src/akuma/heart/index.js +134 -93
  21. package/build/src/akuma/heart/rows.d.ts +9 -31
  22. package/build/src/akuma/heart/rows.js +26 -44
  23. package/build/src/akuma/heart/schema.d.ts +1 -1
  24. package/build/src/akuma/heart/schema.js +7 -15
  25. package/build/src/akuma/heart/soul.d.ts +8 -0
  26. package/build/src/akuma/heart/soul.js +136 -0
  27. package/build/src/akuma/heart/storage.d.ts +28 -14
  28. package/build/src/akuma/heart/storage.js +86 -26
  29. package/build/src/akuma/identity.d.ts +2 -2
  30. package/build/src/akuma/identity.js +9 -9
  31. package/build/src/akuma/index.d.ts +6 -1
  32. package/build/src/akuma/index.js +6 -0
  33. package/build/src/akuma/projection.d.ts +117 -38
  34. package/build/src/akuma/projection.js +208 -97
  35. package/build/src/akuma/provider-recipe.d.ts +24 -0
  36. package/build/src/akuma/provider-recipe.js +100 -0
  37. package/build/src/akuma/provider.d.ts +12 -4
  38. package/build/src/akuma/provider.js +117 -70
  39. package/build/src/akuma/providers/acp/core.d.ts +19 -0
  40. package/build/src/akuma/providers/acp/core.js +159 -0
  41. package/build/src/akuma/providers/acp/events.d.ts +20 -0
  42. package/build/src/akuma/providers/acp/events.js +73 -0
  43. package/build/src/akuma/providers/acp/index.d.ts +12 -0
  44. package/build/src/akuma/providers/acp/index.js +94 -0
  45. package/build/src/akuma/providers/claude/events.js +66 -13
  46. package/build/src/akuma/providers/claude/index.d.ts +2 -1
  47. package/build/src/akuma/providers/claude/index.js +35 -21
  48. package/build/src/akuma/providers/codex-app-server/events.js +4 -1
  49. package/build/src/akuma/providers/codex-app-server/index.d.ts +3 -3
  50. package/build/src/akuma/providers/codex-app-server/index.js +24 -31
  51. package/build/src/akuma/providers/grok-build/index.d.ts +4 -0
  52. package/build/src/akuma/providers/grok-build/index.js +70 -0
  53. package/build/src/akuma/providers/index.d.ts +5 -3
  54. package/build/src/akuma/providers/index.js +24 -59
  55. package/build/src/akuma/providers/opencode-sdk/events.d.ts +40 -34
  56. package/build/src/akuma/providers/opencode-sdk/events.js +220 -101
  57. package/build/src/akuma/providers/opencode-sdk/index.d.ts +3 -2
  58. package/build/src/akuma/providers/opencode-sdk/index.js +233 -128
  59. package/build/src/akuma/providers/opencode-sdk/session.d.ts +11 -26
  60. package/build/src/akuma/providers/opencode-sdk/session.js +10 -9
  61. package/build/src/akuma/providers/pi/events.js +49 -11
  62. package/build/src/akuma/providers/pi/index.d.ts +1 -1
  63. package/build/src/akuma/providers/pi/index.js +50 -21
  64. package/build/src/akuma/publication.d.ts +3 -3
  65. package/build/src/akuma/publication.js +28 -40
  66. package/build/src/akuma/requests.d.ts +9 -7
  67. package/build/src/akuma/requests.js +92 -65
  68. package/build/src/alias/index.d.ts +2 -2
  69. package/build/src/alias/index.js +10 -10
  70. package/build/src/body/decode.js +7 -1
  71. package/build/src/body/region.d.ts +2 -0
  72. package/build/src/body/region.js +77 -7
  73. package/build/src/cli/accepted.d.ts +17 -13
  74. package/build/src/cli/accepted.js +72 -34
  75. package/build/src/cli/actor.js +3 -3
  76. package/build/src/cli/commands/akuma-invoke.d.ts +4 -3
  77. package/build/src/cli/commands/akuma-invoke.js +14 -9
  78. package/build/src/cli/commands/akuma.d.ts +13 -3
  79. package/build/src/cli/commands/akuma.js +67 -34
  80. package/build/src/cli/commands/contract.d.ts +18 -5
  81. package/build/src/cli/commands/contract.js +9 -3
  82. package/build/src/cli/commands/task-invoke.d.ts +17 -4
  83. package/build/src/cli/commands/task-invoke.js +43 -20
  84. package/build/src/cli/commands/task-query.d.ts +5 -0
  85. package/build/src/cli/commands/task-query.js +247 -0
  86. package/build/src/cli/commands/task.d.ts +3 -1
  87. package/build/src/cli/commands/task.js +51 -13
  88. package/build/src/cli/coordinates.d.ts +18 -0
  89. package/build/src/cli/coordinates.js +120 -0
  90. package/build/src/cli/draft.d.ts +8 -0
  91. package/build/src/cli/draft.js +96 -0
  92. package/build/src/cli/invoke.d.ts +4 -3
  93. package/build/src/cli/invoke.js +160 -179
  94. package/build/src/cli/main.js +34 -6
  95. package/build/src/cli/parse.d.ts +9 -2
  96. package/build/src/cli/parse.js +29 -7
  97. package/build/src/cli/render/akuma-tool-command.d.ts +6 -0
  98. package/build/src/cli/render/akuma-tool-command.js +144 -0
  99. package/build/src/cli/render/akuma-tool.d.ts +2 -2
  100. package/build/src/cli/render/akuma-tool.js +30 -4
  101. package/build/src/cli/render/akuma.d.ts +1 -0
  102. package/build/src/cli/render/akuma.js +211 -116
  103. package/build/src/cli/render/audit.d.ts +3 -0
  104. package/build/src/cli/render/audit.js +104 -0
  105. package/build/src/cli/render/contract.d.ts +3 -3
  106. package/build/src/cli/render/contract.js +192 -79
  107. package/build/src/cli/render/kanshi.js +263 -168
  108. package/build/src/cli/render/receipt.d.ts +19 -0
  109. package/build/src/cli/render/receipt.js +106 -0
  110. package/build/src/cli/render/refusal.d.ts +16 -2
  111. package/build/src/cli/render/refusal.js +88 -20
  112. package/build/src/cli/render/region.d.ts +2 -0
  113. package/build/src/cli/render/region.js +17 -0
  114. package/build/src/cli/render/task.d.ts +2 -1
  115. package/build/src/cli/render/task.js +223 -60
  116. package/build/src/cli/render/terminal.d.ts +7 -0
  117. package/build/src/cli/render/terminal.js +46 -0
  118. package/build/src/cli/render/text.js +6 -3
  119. package/build/src/cli/result.d.ts +115 -20
  120. package/build/src/cli/usage.d.ts +1 -0
  121. package/build/src/cli/usage.js +3 -0
  122. package/build/src/contract-worktree.d.ts +6 -5
  123. package/build/src/contract-worktree.js +95 -57
  124. package/build/src/coordination/durable-file.d.ts +4 -3
  125. package/build/src/coordination/durable-file.js +29 -25
  126. package/build/src/coordination/sqlite-transaction-lock.d.ts +4 -0
  127. package/build/src/coordination/sqlite-transaction-lock.js +22 -5
  128. package/build/src/core/facts/fold.js +1 -6
  129. package/build/src/core/facts/gate.d.ts +1 -0
  130. package/build/src/core/facts/gate.js +1 -0
  131. package/build/src/core/verbs/amend.d.ts +1 -1
  132. package/build/src/core/verbs/amend.js +0 -11
  133. package/build/src/core/verbs/deliver.d.ts +1 -1
  134. package/build/src/core/verbs/deliver.js +1 -7
  135. package/build/src/core/verbs/placement.d.ts +1 -1
  136. package/build/src/core/verbs/placement.js +4 -1
  137. package/build/src/dispatch/index.d.ts +2 -2
  138. package/build/src/dispatch/index.js +17 -17
  139. package/build/src/git/admission.d.ts +1 -1
  140. package/build/src/git/admission.js +11 -11
  141. package/build/src/git/hooks.js +13 -13
  142. package/build/src/git/integration.d.ts +14 -15
  143. package/build/src/git/integration.js +80 -44
  144. package/build/src/git/observe.d.ts +5 -3
  145. package/build/src/git/observe.js +18 -12
  146. package/build/src/git/read-observation.js +9 -9
  147. package/build/src/git/reconcile.d.ts +2 -0
  148. package/build/src/git/reconcile.js +138 -95
  149. package/build/src/git/repository.d.ts +27 -19
  150. package/build/src/git/repository.js +155 -83
  151. package/build/src/git/scratch.d.ts +7 -3
  152. package/build/src/git/scratch.js +44 -37
  153. package/build/src/git/target-placement.d.ts +40 -4
  154. package/build/src/git/target-placement.js +221 -101
  155. package/build/src/git/tender.d.ts +6 -5
  156. package/build/src/git/tender.js +24 -43
  157. package/build/src/git/terminal-seal.d.ts +1 -1
  158. package/build/src/git/terminal-seal.js +7 -5
  159. package/build/src/git/workspace.d.ts +36 -4
  160. package/build/src/git/workspace.js +53 -18
  161. package/build/src/identity/selector.js +1 -1
  162. package/build/src/index.d.ts +3 -3
  163. package/build/src/index.js +1 -1
  164. package/build/src/kanshi/index.d.ts +2 -2
  165. package/build/src/kanshi/index.js +1 -1
  166. package/build/src/kanshi/read.d.ts +2 -1
  167. package/build/src/kanshi/read.js +118 -27
  168. package/build/src/kanshi/report.d.ts +50 -0
  169. package/build/src/kanshi/select.d.ts +5 -0
  170. package/build/src/kanshi/select.js +32 -0
  171. package/build/src/library/address.d.ts +12 -3
  172. package/build/src/library/address.js +77 -46
  173. package/build/src/library/akuma-creation.d.ts +6 -1
  174. package/build/src/library/akuma-creation.js +86 -13
  175. package/build/src/library/audit.d.ts +18 -0
  176. package/build/src/library/audit.js +44 -0
  177. package/build/src/library/bind.js +4 -4
  178. package/build/src/library/catalog.d.ts +1 -1
  179. package/build/src/library/catalog.js +5 -8
  180. package/build/src/library/contract.d.ts +5 -6
  181. package/build/src/library/contract.js +11 -35
  182. package/build/src/library/delivery.d.ts +2 -1
  183. package/build/src/library/fleet.d.ts +2 -2
  184. package/build/src/library/fleet.js +35 -71
  185. package/build/src/library/keiyaku.d.ts +3 -2
  186. package/build/src/library/keiyaku.js +1 -0
  187. package/build/src/library/mutation.d.ts +7 -6
  188. package/build/src/library/mutation.js +23 -25
  189. package/build/src/library/reconcile.d.ts +29 -0
  190. package/build/src/library/reconcile.js +179 -0
  191. package/build/src/library/region.d.ts +12 -0
  192. package/build/src/library/region.js +15 -1
  193. package/build/src/library/repo.d.ts +4 -16
  194. package/build/src/library/repo.js +13 -38
  195. package/build/src/protocol/attempt.js +6 -6
  196. package/build/src/protocol/bind.js +4 -4
  197. package/build/src/protocol/intent.d.ts +15 -2
  198. package/build/src/protocol/intent.js +21 -6
  199. package/build/src/protocol/operations.d.ts +73 -22
  200. package/build/src/protocol/operations.js +292 -88
  201. package/build/src/protocol/outcome.d.ts +9 -3
  202. package/build/src/protocol/outcome.js +3 -1
  203. package/build/src/protocol/placement.d.ts +15 -4
  204. package/build/src/protocol/placement.js +58 -25
  205. package/build/src/protocol/read/status.d.ts +8 -1
  206. package/build/src/protocol/read/status.js +61 -14
  207. package/build/src/runtime/proc/line-rpc.d.ts +3 -8
  208. package/build/src/runtime/proc/line-rpc.js +16 -43
  209. package/build/src/runtime/proc/run.d.ts +15 -29
  210. package/build/src/runtime/proc/run.js +113 -105
  211. package/build/src/runtime/proc/stdio.d.ts +18 -0
  212. package/build/src/runtime/proc/stdio.js +58 -0
  213. package/build/src/settings.d.ts +2 -2
  214. package/build/src/settings.js +8 -8
  215. package/build/src/settlement/fence.d.ts +0 -4
  216. package/build/src/settlement/fence.js +0 -9
  217. package/build/src/settlement/holder.d.ts +11 -1
  218. package/build/src/settlement/holder.js +25 -0
  219. package/build/src/settlement/settle.d.ts +1 -1
  220. package/build/src/settlement/settle.js +52 -89
  221. package/build/src/task/board.d.ts +3 -3
  222. package/build/src/task/board.js +6 -9
  223. package/build/src/task/compose.d.ts +1 -1
  224. package/build/src/task/compose.js +18 -10
  225. package/build/src/task/context.d.ts +3 -3
  226. package/build/src/task/context.js +14 -14
  227. package/build/src/task/document.d.ts +2 -1
  228. package/build/src/task/document.js +15 -5
  229. package/build/src/task/index.d.ts +19 -7
  230. package/build/src/task/index.js +42 -23
  231. package/build/src/task/operations.d.ts +15 -9
  232. package/build/src/task/operations.js +78 -48
  233. package/build/src/task/query.d.ts +67 -0
  234. package/build/src/task/query.js +279 -0
  235. package/build/src/task/store.d.ts +2 -2
  236. package/build/src/task/store.js +40 -25
  237. package/build/src/verification/execution.d.ts +4 -2
  238. package/build/src/verification/execution.js +9 -5
  239. package/build/src/workspace-place.d.ts +41 -0
  240. package/build/src/workspace-place.js +386 -0
  241. package/build/src/world.d.ts +13 -2
  242. package/build/src/world.js +81 -48
  243. package/package.json +3 -2
  244. package/build/src/protocol/read/audit.d.ts +0 -31
  245. package/build/src/protocol/read/audit.js +0 -55
@@ -7,7 +7,7 @@ function values(command, name) {
7
7
  const item = command.flags[name];
8
8
  return Array.isArray(item) ? item : undefined;
9
9
  }
10
- function namespace(raw) { return raw === undefined ? undefined : raw === "" ? [] : raw.split("/"); }
10
+ function namespace(raw) { return raw === undefined ? undefined : raw === "/" ? [] : raw.split("/"); }
11
11
  function priority(raw) {
12
12
  if (raw === undefined)
13
13
  return undefined;
@@ -16,10 +16,17 @@ function priority(raw) {
16
16
  }
17
17
  function state(raw) { return raw; }
18
18
  function ids(items) { return items; }
19
- function invokeAdd(tasks, command, readStdin) {
19
+ function limit(command) { const raw = value(command, "limit"); return raw === undefined ? undefined : Number(raw); }
20
+ async function invokeAdd(tasks, command, input) {
20
21
  const selectedNamespace = namespace(value(command, "namespace"));
21
- if (command.stdin === "document")
22
- return tasks.addDocument({ markdown: readStdin(), ...(selectedNamespace === undefined ? {} : { namespace: selectedNamespace }) });
22
+ const actor = input.actor;
23
+ if (command.stdin === "document") {
24
+ return tasks.addDocument({
25
+ markdown: await input.readStdin(),
26
+ ...(selectedNamespace === undefined ? {} : { namespace: selectedNamespace }),
27
+ ...(actor === undefined ? {} : { actor }),
28
+ });
29
+ }
23
30
  const body = value(command, "body"), note = value(command, "note"), initialState = state(value(command, "state")), selectedPriority = priority(value(command, "priority"));
24
31
  const needs = ids(values(command, "needs")), parent = value(command, "parent");
25
32
  const supersedes = ids(values(command, "supersedes")), relates = ids(values(command, "relates"));
@@ -28,11 +35,12 @@ function invokeAdd(tasks, command, readStdin) {
28
35
  ...(body === undefined ? {} : { body }), ...(note === undefined ? {} : { note }), ...(initialState === undefined ? {} : { state: initialState }), ...(selectedPriority === undefined ? {} : { priority: selectedPriority }),
29
36
  ...(needs === undefined ? {} : { needs }), ...(parent === undefined ? {} : { parent: parent }),
30
37
  ...(supersedes === undefined ? {} : { supersedes }), ...(relates === undefined ? {} : { relates }),
38
+ ...(actor === undefined ? {} : { actor }),
31
39
  });
32
40
  }
33
- function invokeUpdate(tasks, command, readStdin) {
34
- const body = command.stdin === "body" ? readStdin() : value(command, "body"), appendBody = command.stdin === "append" ? readStdin() : value(command, "append");
35
- const note = command.stdin === "note" ? readStdin() : value(command, "note");
41
+ async function invokeUpdate(tasks, command, readStdin) {
42
+ const body = command.stdin === "body" ? await readStdin() : value(command, "body"), appendBody = command.stdin === "append" ? await readStdin() : value(command, "append");
43
+ const note = command.stdin === "note" ? await readStdin() : value(command, "note");
36
44
  const title = value(command, "title"), selectedPriority = priority(value(command, "priority"));
37
45
  const addNeeds = ids(values(command, "needs")), dropNeeds = ids(values(command, "drop-needs"));
38
46
  const parent = value(command, "parent"), addSupersedes = ids(values(command, "supersedes"));
@@ -53,24 +61,37 @@ async function invokeRead(tasks, command) {
53
61
  const detail = await tasks.task({ id }).read();
54
62
  return detail ?? { kind: "refused", refusal: { kind: "task-missing", taskId: id } };
55
63
  }
56
- case "ls": return tasks.list({ selection: command.flags.all === true ? "all" : command.flags.closed === true ? "closed" : "active", ...(command.flags.world === true ? { scope: "world" } : {}) });
57
- case "ready": return tasks.ready(command.flags.world === true ? { scope: "world" } : {});
58
- case "blocked": return tasks.blocked(command.flags.world === true ? { scope: "world" } : {});
59
- case "tree": return tasks.task({ id }).tree(command.flags.full === true ? { full: true } : {});
64
+ case "ls": return tasks.list({ selection: command.flags.all === true ? "all" : command.flags.closed === true ? "closed" : "active", ...(command.flags.world === true ? { scope: "world" } : {}), ...(limit(command) === undefined ? {} : { limit: limit(command) }) });
65
+ case "ready": return tasks.ready({ ...(command.flags.world === true ? { scope: "world" } : {}), ...(value(command, "parent") === undefined ? {} : { parent: value(command, "parent") }), ...(limit(command) === undefined ? {} : { limit: limit(command) }) });
66
+ case "blocked": return tasks.blocked({ ...(command.flags.world === true ? { scope: "world" } : {}), ...(value(command, "parent") === undefined ? {} : { parent: value(command, "parent") }), ...(limit(command) === undefined ? {} : { limit: limit(command) }) });
67
+ case "query": return tasks.query({ ...(command.where === undefined ? {} : { where: command.where }), ...(command.flags.world === true ? { scope: "world" } : {}), ...(value(command, "sort") === undefined ? {} : { sort: value(command, "sort") }), ...(limit(command) === undefined ? {} : { limit: limit(command) }) });
68
+ case "tree": return tasks.task({ id }).tree();
60
69
  case "doctor": return tasks.doctor();
61
70
  default: throw new Error(`task action is not a read: ${command.action}`);
62
71
  }
63
72
  }
73
+ function isWorldObservation(command) {
74
+ return command.action === "ls" || command.action === "ready" || command.action === "blocked"
75
+ || command.action === "query" || command.action === "doctor";
76
+ }
77
+ function diagnostic(error) { return error instanceof Error ? error.message : String(error); }
78
+ async function observeWorldRead(tasks, command) {
79
+ try {
80
+ return { kind: "present", value: await invokeRead(tasks, command) };
81
+ }
82
+ catch (error) {
83
+ return { kind: "failed", failure: { message: diagnostic(error) } };
84
+ }
85
+ }
64
86
  function missingWorld(command) {
65
87
  const missing = (id) => ({
66
88
  kind: "refused",
67
89
  refusal: { kind: "task-missing", taskId: id },
68
90
  });
69
- if (command.action === "doctor")
70
- return { issues: [] };
71
- if (command.action === "ls" || command.action === "ready" || command.action === "blocked" || command.action === "namespace") {
91
+ if (isWorldObservation(command))
92
+ return { kind: "absent" };
93
+ if (command.action === "namespace")
72
94
  return { kind: "accepted", value: [] };
73
- }
74
95
  if (command.action === "hold" || command.action === "done" || command.action === "drop") {
75
96
  return { items: command.positionals.map((id) => ({ id: id, outcome: missing(id) })) };
76
97
  }
@@ -80,17 +101,19 @@ export async function invokeTask(command, input) {
80
101
  let world = input.world;
81
102
  if (world === null && (command.action === "add" || command.action === "compose"
82
103
  || (command.action === "namespace" && command.positionals.length > 0))) {
83
- world = input.establish();
104
+ world = await input.establish();
84
105
  }
85
106
  if (world === null)
86
107
  return missingWorld(command);
87
108
  const tasks = Tasks.of(world);
88
- if (["show", "ls", "ready", "blocked", "tree", "doctor"].includes(command.action))
109
+ if (isWorldObservation(command))
110
+ return observeWorldRead(tasks, command);
111
+ if (command.action === "show" || command.action === "tree")
89
112
  return invokeRead(tasks, command);
90
113
  if (command.action === "add")
91
- return invokeAdd(tasks, command, input.readStdin);
114
+ return await invokeAdd(tasks, command, input);
92
115
  if (command.action === "update")
93
- return invokeUpdate(tasks, command, input.readStdin);
116
+ return await invokeUpdate(tasks, command, input.readStdin);
94
117
  const id = command.positionals[0];
95
118
  switch (command.action) {
96
119
  case "start": return tasks.task({ id }).start();
@@ -111,7 +134,7 @@ export async function invokeTask(command, input) {
111
134
  await tasks.setNamespace({ namespace: selected });
112
135
  return tasks.namespace();
113
136
  }
114
- case "compose": return tasks.compose({ markdown: input.readStdin() });
137
+ case "compose": return tasks.compose({ markdown: await input.readStdin(), ...(input.actor === undefined ? {} : { actor: input.actor }) });
115
138
  default: throw new Error(`task action has no invocation: ${command.action}`);
116
139
  }
117
140
  }
@@ -0,0 +1,5 @@
1
+ import type { TaskQueryExpression } from "../../task/index.js";
2
+ export type { TaskQueryExpression } from "../../task/index.js";
3
+ export declare function parseTaskQueryExpression(source: string): TaskQueryExpression;
4
+ export declare function validateTaskLimit(source: string): void;
5
+ export declare function validateTaskParent(source: string): void;
@@ -0,0 +1,247 @@
1
+ import { MAX_TASK_LIMIT, normalizeTaskQuery } from "../../task/query.js";
2
+ function syntax(message, offset) { throw new Error(`${message} at column ${offset + 1}`); }
3
+ function scanString(source, offset) {
4
+ let index = offset + 1;
5
+ let value = "";
6
+ while (index < source.length && source[index] !== "\"") {
7
+ const character = source[index];
8
+ if (character !== "\\") {
9
+ value += character;
10
+ index += 1;
11
+ continue;
12
+ }
13
+ const escaped = source[index + 1];
14
+ if (escaped === undefined)
15
+ syntax("unterminated string", offset);
16
+ if (escaped !== "\\" && escaped !== "\"")
17
+ syntax("only \\\\ and \\\" escapes are supported", index);
18
+ value += escaped;
19
+ index += 2;
20
+ }
21
+ if (source[index] !== "\"")
22
+ syntax("unterminated string", offset);
23
+ return { token: { kind: "string", value, offset }, next: index + 1 };
24
+ }
25
+ function scanOperator(source, offset) {
26
+ const pair = source.slice(offset, offset + 2);
27
+ if (pair === "!=" || pair === "<=" || pair === ">=") {
28
+ return { token: { kind: "operator", value: pair, offset }, next: offset + 2 };
29
+ }
30
+ const character = source[offset];
31
+ if (character !== "=" && character !== "<" && character !== ">" && character !== "~")
32
+ return null;
33
+ return { token: { kind: "operator", value: character, offset }, next: offset + 1 };
34
+ }
35
+ function scanWord(source, offset) {
36
+ let index = offset;
37
+ while (index < source.length && !/[\s()=!<>~"]/u.test(source[index]))
38
+ index += 1;
39
+ if (index === offset)
40
+ syntax(`unexpected ${JSON.stringify(source[offset])}`, offset);
41
+ return { token: { kind: "word", value: source.slice(offset, index), offset }, next: index };
42
+ }
43
+ function lex(source) {
44
+ const tokens = [];
45
+ let index = 0;
46
+ while (index < source.length) {
47
+ if (/\s/u.test(source[index])) {
48
+ index += 1;
49
+ continue;
50
+ }
51
+ const offset = index, character = source[index];
52
+ if (character === "(") {
53
+ tokens.push({ kind: "left", value: character, offset });
54
+ index += 1;
55
+ continue;
56
+ }
57
+ if (character === ")") {
58
+ tokens.push({ kind: "right", value: character, offset });
59
+ index += 1;
60
+ continue;
61
+ }
62
+ if (character === "\"") {
63
+ const scanned = scanString(source, offset);
64
+ tokens.push(scanned.token);
65
+ index = scanned.next;
66
+ continue;
67
+ }
68
+ const operator = scanOperator(source, offset);
69
+ if (operator !== null) {
70
+ tokens.push(operator.token);
71
+ index = operator.next;
72
+ continue;
73
+ }
74
+ const word = scanWord(source, offset);
75
+ tokens.push(word.token);
76
+ index = word.next;
77
+ }
78
+ tokens.push({ kind: "end", value: "", offset: source.length });
79
+ return tokens;
80
+ }
81
+ function canonicalTaskId(token, field) {
82
+ if (!token.value.startsWith("task/"))
83
+ return syntax(`${field} requires a canonical TaskId`, token.offset);
84
+ return token.value;
85
+ }
86
+ function state(token) {
87
+ if (token.value !== "open" && token.value !== "in_progress" && token.value !== "on_hold" && token.value !== "done" && token.value !== "drop") {
88
+ return syntax(`invalid state ${JSON.stringify(token.value)}`, token.offset);
89
+ }
90
+ return token.value;
91
+ }
92
+ function priority(token) {
93
+ const value = Number(token.value);
94
+ if (!Number.isInteger(value) || value < 0 || value > 3)
95
+ return syntax("priority requires 0..3", token.offset);
96
+ return value;
97
+ }
98
+ function boolean(token) {
99
+ if (token.value === "true")
100
+ return true;
101
+ if (token.value === "false")
102
+ return false;
103
+ return syntax("boolean predicate requires true or false", token.offset);
104
+ }
105
+ function timestamp(token, field) {
106
+ const milliseconds = Date.parse(token.value);
107
+ if (!Number.isFinite(milliseconds) || new Date(milliseconds).toISOString() !== token.value)
108
+ return syntax(`${field} requires a canonical UTC ISO timestamp`, token.offset);
109
+ return token.value;
110
+ }
111
+ function equalityOperator(token, field) {
112
+ if (token.value !== "=" && token.value !== "!=")
113
+ syntax(`${field} supports only = and !=`, token.offset);
114
+ return token.value;
115
+ }
116
+ function orderedOperator(token, field) {
117
+ if (token.value === "~")
118
+ syntax(`${field} operator is invalid`, token.offset);
119
+ return token.value;
120
+ }
121
+ function textOperator(token, field) {
122
+ if (token.value !== "=" && token.value !== "!=" && token.value !== "~") {
123
+ syntax(`${field} supports only =, !=, and ~`, token.offset);
124
+ }
125
+ return token.value;
126
+ }
127
+ function parseTextPredicate(field, operator, value) {
128
+ const selected = textOperator(operator, field);
129
+ if (field === "title" && value.kind !== "string")
130
+ syntax("title values must use double quotes", value.offset);
131
+ if (field === "id" && selected !== "~")
132
+ canonicalTaskId(value, "id");
133
+ return { field, operator: selected, value: value.value };
134
+ }
135
+ function parseRelationPredicate(field, operator, value) {
136
+ return { field, operator: equalityOperator(operator, field), value: canonicalTaskId(value, field) };
137
+ }
138
+ function parseBooleanPredicate(field, operator, value) {
139
+ return { field, operator: equalityOperator(operator, field), value: boolean(value) };
140
+ }
141
+ function parseTimestampPredicate(field, operator, value) {
142
+ return { field, operator: orderedOperator(operator, field), value: timestamp(value, field) };
143
+ }
144
+ class Parser {
145
+ tokens;
146
+ index = 0;
147
+ constructor(tokens) {
148
+ this.tokens = tokens;
149
+ }
150
+ current() { return this.tokens[this.index]; }
151
+ take() { const token = this.current(); this.index += 1; return token; }
152
+ word(value) { return this.current().kind === "word" && this.current().value === value; }
153
+ parse() {
154
+ const value = this.or();
155
+ if (this.current().kind !== "end")
156
+ syntax(`unexpected ${JSON.stringify(this.current().value)}`, this.current().offset);
157
+ return value;
158
+ }
159
+ or() {
160
+ const terms = [this.and()];
161
+ while (this.word("or")) {
162
+ this.take();
163
+ terms.push(this.and());
164
+ }
165
+ return terms.length === 1 ? terms[0] : { kind: "or", terms };
166
+ }
167
+ and() {
168
+ const terms = [this.not()];
169
+ while (this.word("and")) {
170
+ this.take();
171
+ terms.push(this.not());
172
+ }
173
+ return terms.length === 1 ? terms[0] : { kind: "and", terms };
174
+ }
175
+ not() {
176
+ if (this.word("not")) {
177
+ this.take();
178
+ return { kind: "not", term: this.not() };
179
+ }
180
+ return this.primary();
181
+ }
182
+ primary() {
183
+ if (this.current().kind === "left") {
184
+ const opening = this.take();
185
+ const value = this.or();
186
+ if (this.current().kind !== "right")
187
+ syntax("missing closing parenthesis", opening.offset);
188
+ this.take();
189
+ return value;
190
+ }
191
+ return { kind: "predicate", predicate: this.predicate() };
192
+ }
193
+ predicate() {
194
+ const field = this.take();
195
+ if (field.kind !== "word")
196
+ syntax("expected query field", field.offset);
197
+ if ((field.value === "ready" || field.value === "blocked") && this.current().kind !== "operator") {
198
+ return { field: field.value, operator: "=", value: true };
199
+ }
200
+ const operator = this.take();
201
+ if (operator.kind !== "operator")
202
+ syntax(`expected operator after ${field.value}`, operator.offset);
203
+ const value = this.take();
204
+ if (value.kind !== "word" && value.kind !== "string")
205
+ syntax(`expected value for ${field.value}`, value.offset);
206
+ return this.typedPredicate(field, operator, value);
207
+ }
208
+ typedPredicate(field, operator, value) {
209
+ switch (field.value) {
210
+ case "state": return { field: "state", operator: equalityOperator(operator, "state"), value: state(value) };
211
+ case "priority": return { field: "priority", operator: orderedOperator(operator, "priority"), value: priority(value) };
212
+ case "title": return parseTextPredicate("title", operator, value);
213
+ case "id": return parseTextPredicate("id", operator, value);
214
+ case "parent": return {
215
+ field: "parent",
216
+ operator: equalityOperator(operator, "parent"),
217
+ value: value.value === "none" ? null : canonicalTaskId(value, "parent"),
218
+ };
219
+ case "under": return parseRelationPredicate("under", operator, value);
220
+ case "needs": return parseRelationPredicate("needs", operator, value);
221
+ case "blocks": return parseRelationPredicate("blocks", operator, value);
222
+ case "ready": return parseBooleanPredicate("ready", operator, value);
223
+ case "blocked": return parseBooleanPredicate("blocked", operator, value);
224
+ case "created": return parseTimestampPredicate("created", operator, value);
225
+ case "updated": return parseTimestampPredicate("updated", operator, value);
226
+ default: return syntax(`unknown query field ${JSON.stringify(field.value)}`, field.offset);
227
+ }
228
+ }
229
+ }
230
+ export function parseTaskQueryExpression(source) {
231
+ if (source.trim().length === 0)
232
+ return syntax("query expression must be nonblank", 0);
233
+ return normalizeTaskQuery(new Parser(lex(source)).parse());
234
+ }
235
+ export function validateTaskLimit(source) {
236
+ if (!/^[1-9][0-9]*$/u.test(source) || Number(source) > MAX_TASK_LIMIT) {
237
+ throw new Error(`--limit must be an integer from 1 to ${MAX_TASK_LIMIT}`);
238
+ }
239
+ }
240
+ export function validateTaskParent(source) {
241
+ try {
242
+ normalizeTaskQuery({ kind: "predicate", predicate: { field: "under", operator: "=", value: source } });
243
+ }
244
+ catch {
245
+ throw new Error("--parent requires a canonical TaskId");
246
+ }
247
+ }
@@ -1,4 +1,5 @@
1
- export type TaskAction = "add" | "show" | "ls" | "ready" | "blocked" | "tree" | "doctor" | "update" | "start" | "stop" | "hold" | "resume" | "done" | "drop" | "namespace" | "compose";
1
+ import { type TaskQueryExpression } from "./task-query.js";
2
+ export type TaskAction = "add" | "show" | "ls" | "ready" | "blocked" | "tree" | "doctor" | "update" | "query" | "start" | "stop" | "hold" | "resume" | "done" | "drop" | "namespace" | "compose";
2
3
  type TaskFlagValue = string | true | readonly string[];
3
4
  type TaskStdin = "document" | "body" | "append" | "note" | "compose";
4
5
  export type ParsedTaskCommand = Readonly<{
@@ -8,6 +9,7 @@ export type ParsedTaskCommand = Readonly<{
8
9
  positionals: readonly string[];
9
10
  flags: Readonly<Record<string, TaskFlagValue>>;
10
11
  stdin?: TaskStdin;
12
+ where?: TaskQueryExpression;
11
13
  }>;
12
14
  export declare function isTaskAction(value: string | undefined): value is TaskAction;
13
15
  export declare function renderTaskHelp(action?: TaskAction): string;
@@ -1,22 +1,25 @@
1
- import { CliUsageError, usageLine } from "../usage.js";
1
+ import { CliUsageError, isBlankInput, usageLine } from "../usage.js";
2
+ import { parseTaskQueryExpression, validateTaskLimit, validateTaskParent, } from "./task-query.js";
2
3
  const COMMON = { json: "boolean" };
3
4
  const TASK_COMMAND_SPECS = {
4
5
  add: {
5
- arity: [0, 1], stdin: "document", flags: { ...COMMON, namespace: "value", state: "value", priority: "value", needs: "repeat", parent: "value", supersedes: "repeat", relates: "repeat", body: "value", note: "value" },
6
+ arity: [0, 1], stdin: "document", flags: { ...COMMON, namespace: "value", state: "value", priority: "value", needs: "repeat", parent: "value", supersedes: "repeat", relates: "repeat", body: "value", note: "value", actor: "value" },
6
7
  usage: `task add <TITLE> [--namespace <ns>] [--priority 0..3]
7
8
  [--state open|in_progress|on_hold|done|drop]
8
- [--note <text>]
9
+ [--note <text>] [--actor <actor>]
9
10
  [--needs <TaskId>]... [--parent <TaskId>]
10
11
  [--supersedes <TaskId>]... [--relates <TaskId>]...
11
12
  [--body <text>] [--json]
12
- task add [--namespace <ns>] [--json] -`,
13
+ task add [--namespace <ns>] [--actor <actor>] [--json] -`,
13
14
  purpose: "Create one Task from flags or a canonical stdin document.",
14
15
  },
15
16
  show: { arity: [1, 1], flags: COMMON, usage: "task show <TaskId> [--json]", purpose: "Read one Task and its relationships." },
16
- ls: { arity: [0, 0], flags: { ...COMMON, closed: "boolean", all: "boolean", world: "boolean" }, usage: "task ls [--closed | --all] [--world] [--json]", purpose: "List Tasks in the selected scope." },
17
- ready: { arity: [0, 0], flags: { ...COMMON, world: "boolean" }, usage: "task ready [--world] [--json]", purpose: "List Tasks whose dependencies are satisfied." },
18
- blocked: { arity: [0, 0], flags: { ...COMMON, world: "boolean" }, usage: "task blocked [--world] [--json]", purpose: "List Tasks blocked by dependencies." },
19
- tree: { arity: [1, 1], flags: { ...COMMON, full: "boolean" }, usage: "task tree <TaskId> [--full] [--json]", purpose: "Read one Task dependency tree." },
17
+ ls: { arity: [0, 0], flags: { ...COMMON, closed: "boolean", all: "boolean", world: "boolean", limit: "value" }, usage: "task ls [--closed | --all] [--world] [--limit <n>] [--json]", purpose: "List Tasks in the selected scope." },
18
+ ready: { arity: [0, 0], flags: { ...COMMON, world: "boolean", parent: "value", limit: "value" }, usage: "task ready [--world] [--parent <TaskId>] [--limit <n>] [--json]", purpose: "List open Tasks whose every need is terminal." },
19
+ blocked: { arity: [0, 0], flags: { ...COMMON, world: "boolean", parent: "value", limit: "value" }, usage: "task blocked [--world] [--parent <TaskId>] [--limit <n>] [--json]", purpose: "List Tasks blocked by dependencies." },
20
+ query: { arity: [0, 0], flags: { ...COMMON, where: "value", world: "boolean", sort: "value", limit: "value" }, usage: `task query [--where <expression>] [--world]
21
+ [--sort priority|created|updated|id] [--limit <n>] [--json]`, purpose: "Query Task facts with a typed boolean expression." },
22
+ tree: { arity: [1, 1], flags: COMMON, usage: "task tree <TaskId> [--json]", purpose: "Read one Task parent decomposition tree." },
20
23
  doctor: { arity: [0, 0], flags: COMMON, usage: "task doctor [--json]", purpose: "Inspect Task authority without repairing it." },
21
24
  update: {
22
25
  arity: [1, 1], flags: { ...COMMON, title: "value", body: "value", append: "value", note: "value", priority: "value", needs: "repeat", "drop-needs": "repeat", parent: "value", "no-parent": "boolean", supersedes: "repeat", "drop-supersedes": "repeat", relates: "repeat", "drop-relates": "repeat" },
@@ -35,7 +38,7 @@ task add [--namespace <ns>] [--json] -`,
35
38
  done: { arity: [1, Number.POSITIVE_INFINITY], flags: { ...COMMON, note: "value" }, usage: "task done <TaskId>... [--note <text>] [--json]", purpose: "Mark one or more Tasks done." },
36
39
  drop: { arity: [1, Number.POSITIVE_INFINITY], flags: { ...COMMON, note: "value" }, usage: "task drop <TaskId>... [--note <text>] [--json]", purpose: "Drop one or more Tasks." },
37
40
  namespace: { arity: [0, 1], flags: COMMON, usage: "task namespace [<namespace>] [--json]", purpose: "Read or replace the current Task namespace." },
38
- compose: { arity: [0, 0], stdin: "compose", flags: COMMON, usage: "task compose [--json] -", purpose: "Admit independent Task document changes from stdin; partial admission is reported." },
41
+ compose: { arity: [0, 0], stdin: "compose", flags: { ...COMMON, actor: "value" }, usage: "task compose [--actor <actor>] [--json] -", purpose: "Admit Task documents independently; partial admission has no cross-file atomicity or rollback." },
39
42
  };
40
43
  export function isTaskAction(value) {
41
44
  return value !== undefined && Object.hasOwn(TASK_COMMAND_SPECS, value);
@@ -64,6 +67,8 @@ function setFlag(flags, name, kind, value, fail) {
64
67
  }
65
68
  if (value === undefined)
66
69
  fail(`--${name} requires a value`);
70
+ if (isBlankInput(value))
71
+ fail(`--${name} requires a nonblank value`);
67
72
  if (kind === "repeat") {
68
73
  const current = flags[name];
69
74
  flags[name] = [...(Array.isArray(current) ? current : []), value];
@@ -99,6 +104,8 @@ function scanTaskArgv(action, argv, fail) {
99
104
  continue;
100
105
  }
101
106
  if (!token.startsWith("--")) {
107
+ if (isBlankInput(token))
108
+ fail(`task ${action} requires a nonblank value`);
102
109
  positionals.push(token);
103
110
  continue;
104
111
  }
@@ -117,18 +124,40 @@ function validateUpdate(scanned, fail) {
117
124
  if (Object.keys(flags).every((name) => name === "json"))
118
125
  fail("task update requires at least one patch option");
119
126
  }
127
+ function validateTaskReadFlags(action, flags, fail) {
128
+ if (action === "ls" && flags.closed === true && flags.all === true)
129
+ fail("--closed and --all are mutually exclusive");
130
+ if (typeof flags.limit === "string") {
131
+ try {
132
+ validateTaskLimit(flags.limit);
133
+ }
134
+ catch (error) {
135
+ fail(error instanceof Error ? error.message : String(error));
136
+ }
137
+ }
138
+ if (typeof flags.sort === "string" && flags.sort !== "priority" && flags.sort !== "created" && flags.sort !== "updated" && flags.sort !== "id") {
139
+ fail("--sort must be priority, created, updated, or id");
140
+ }
141
+ if ((action === "ready" || action === "blocked") && typeof flags.parent === "string") {
142
+ try {
143
+ validateTaskParent(flags.parent);
144
+ }
145
+ catch (error) {
146
+ fail(error instanceof Error ? error.message : String(error));
147
+ }
148
+ }
149
+ }
120
150
  function validateTaskScan(action, scanned, fail) {
121
151
  const spec = TASK_COMMAND_SPECS[action], { positionals, flags, stdin } = scanned;
122
152
  if (positionals.length < spec.arity[0] || positionals.length > spec.arity[1])
123
153
  fail(`task ${action} has invalid positional arguments`);
124
154
  if (action === "add" && (stdin === "document") === (positionals.length === 1))
125
155
  fail("task add requires either TITLE or final '-' input");
126
- if (action === "add" && stdin === "document" && Object.keys(flags).some((name) => name !== "json" && name !== "namespace"))
156
+ if (action === "add" && stdin === "document" && Object.keys(flags).some((name) => name !== "json" && name !== "namespace" && name !== "actor"))
127
157
  fail("task add document input owns its creation fields");
128
158
  if (action === "compose" && stdin !== "compose")
129
159
  fail("task compose requires final '-' input");
130
- if (action === "ls" && flags.closed === true && flags.all === true)
131
- fail("--closed and --all are mutually exclusive");
160
+ validateTaskReadFlags(action, flags, fail);
132
161
  if (action === "update")
133
162
  validateUpdate(scanned, fail);
134
163
  }
@@ -140,5 +169,14 @@ export function parseTaskCommand(argv) {
140
169
  const fail = (message) => { throw new CliUsageError(message, renderTaskUsage(action)); };
141
170
  const scanned = scanTaskArgv(action, argv, fail);
142
171
  validateTaskScan(action, scanned, fail);
143
- return { command: "task", action, output: scanned.flags.json === true ? "json" : "text", ...scanned };
172
+ let where;
173
+ if (action === "query" && typeof scanned.flags.where === "string") {
174
+ try {
175
+ where = parseTaskQueryExpression(scanned.flags.where);
176
+ }
177
+ catch (error) {
178
+ fail(error instanceof Error ? error.message : String(error));
179
+ }
180
+ }
181
+ return { command: "task", action, output: scanned.flags.json === true ? "json" : "text", ...scanned, ...(where === undefined ? {} : { where }) };
144
182
  }
@@ -0,0 +1,18 @@
1
+ import { Repo } from "../index.js";
2
+ import { type WorldRoot } from "../world.js";
3
+ import { type ParsedCommand } from "./parse.js";
4
+ export type CliCoordinates = Readonly<{
5
+ cwd: string;
6
+ repo?: Repo;
7
+ world: WorldRoot | null;
8
+ candidateWorld: WorldRoot | null;
9
+ establishWorld: () => Promise<WorldRoot>;
10
+ }>;
11
+ export type CliCoordinateInput = Readonly<{
12
+ processCwd?: string;
13
+ cwd?: string;
14
+ repo?: string;
15
+ command: ParsedCommand;
16
+ }>;
17
+ export declare function assertExplicitRepoUse(command: ParsedCommand, repo: string | undefined): void;
18
+ export declare function resolveCliCoordinates(input: CliCoordinateInput): Promise<CliCoordinates>;
@@ -0,0 +1,120 @@
1
+ import { realpath } from "node:fs/promises";
2
+ import { resolve } from "node:path";
3
+ import { NoGitWorldError, Repo } from "../index.js";
4
+ import { World, WorldError } from "../world.js";
5
+ import { CliUsageError } from "./parse.js";
6
+ async function canonicalInvocationCwd(input) {
7
+ try {
8
+ return await realpath(input);
9
+ }
10
+ catch {
11
+ throw new CliUsageError(`invocation cwd is not an existing directory: ${input}`);
12
+ }
13
+ }
14
+ async function discoverRepoAt(coordinate) {
15
+ try {
16
+ return { kind: "present", repo: await Repo.at({ path: coordinate }) };
17
+ }
18
+ catch (error) {
19
+ if (error instanceof NoGitWorldError)
20
+ return { kind: "absent", error };
21
+ throw error;
22
+ }
23
+ }
24
+ function repoPolicy(command) {
25
+ switch (command.command) {
26
+ case "bind":
27
+ case "amend":
28
+ case "deliver":
29
+ case "review":
30
+ case "arc":
31
+ case "abandon":
32
+ case "audit":
33
+ case "reconcile":
34
+ case "show":
35
+ case "region":
36
+ return { use: "required", acceptsExplicit: true };
37
+ case "ls":
38
+ return { use: command.query.kind === "contracts" ? "required" : "none", acceptsExplicit: false };
39
+ case "status":
40
+ case "tell":
41
+ case "history":
42
+ return { use: "optional", acceptsExplicit: false };
43
+ case "fork":
44
+ return { use: "optional", acceptsExplicit: true };
45
+ case "wait":
46
+ case "kill": {
47
+ const contractSelector = command.akuma.some((selector) => selector.startsWith("kei/"));
48
+ return { use: contractSelector ? "required" : "optional", acceptsExplicit: contractSelector };
49
+ }
50
+ case "call":
51
+ return {
52
+ use: command.contract === undefined ? "none" : "required",
53
+ acceptsExplicit: command.contract !== undefined,
54
+ };
55
+ case "settings":
56
+ case "task":
57
+ case "install":
58
+ return { use: "none", acceptsExplicit: false };
59
+ }
60
+ }
61
+ function refuseUnusedRepo(command) {
62
+ if (command.command === "call")
63
+ throw new CliUsageError("--repo has no consumer without --contract");
64
+ throw new CliUsageError(`--repo has no consumer for ${command.command}`);
65
+ }
66
+ export function assertExplicitRepoUse(command, repo) {
67
+ if (repo !== undefined && !repoPolicy(command).acceptsExplicit)
68
+ refuseUnusedRepo(command);
69
+ }
70
+ function repoFor(use, discovery) {
71
+ if (use === "none")
72
+ return undefined;
73
+ if (discovery.kind === "present")
74
+ return discovery.repo;
75
+ if (use === "required")
76
+ throw discovery.error;
77
+ return undefined;
78
+ }
79
+ async function resolveWorld(cwd, repo) {
80
+ try {
81
+ const resolution = await World.resolve({ cwd, ...(repo === undefined ? {} : { repositoryRoot: repo.root }) });
82
+ return {
83
+ root: resolution.root,
84
+ candidate: resolution.candidate,
85
+ async establish() {
86
+ try {
87
+ return await resolution.establish();
88
+ }
89
+ catch (error) {
90
+ if (error instanceof WorldError)
91
+ throw new CliUsageError(error.message);
92
+ throw error;
93
+ }
94
+ },
95
+ };
96
+ }
97
+ catch (error) {
98
+ if (error instanceof WorldError)
99
+ throw new CliUsageError(error.message);
100
+ throw error;
101
+ }
102
+ }
103
+ export async function resolveCliCoordinates(input) {
104
+ const processCwd = resolve(input.processCwd ?? ".");
105
+ const cwd = await canonicalInvocationCwd(resolve(processCwd, input.cwd ?? "."));
106
+ const invocationRepo = await discoverRepoAt(cwd);
107
+ const selectedRepo = input.repo === undefined
108
+ ? invocationRepo
109
+ : await discoverRepoAt(resolve(cwd, input.repo));
110
+ const worldRepo = invocationRepo.kind === "present" ? invocationRepo.repo : undefined;
111
+ const repo = repoFor(repoPolicy(input.command).use, selectedRepo);
112
+ const world = await resolveWorld(cwd, worldRepo);
113
+ return {
114
+ cwd,
115
+ ...(repo === undefined ? {} : { repo }),
116
+ world: world.root,
117
+ candidateWorld: world.candidate,
118
+ establishWorld: world.establish,
119
+ };
120
+ }