@astrosheep/keiyaku 4.0.2 → 4.0.4

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 (145) hide show
  1. package/build/integrations/marketplace/plugins/keiyaku/.claude-plugin/plugin.json +1 -1
  2. package/build/integrations/marketplace/plugins/keiyaku/.codex-plugin/plugin.json +1 -1
  3. package/build/integrations/marketplace/plugins/keiyaku/package.json +1 -1
  4. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku/SKILL.md +1 -1
  5. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-akuma/SKILL.md +1 -1
  6. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-bind/SKILL.md +5 -0
  7. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-task/SKILL.md +3 -1
  8. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-workflow/SKILL.md +9 -8
  9. package/build/src/akuma/akuma.d.ts +34 -11
  10. package/build/src/akuma/akuma.js +121 -61
  11. package/build/src/akuma/allowed.d.ts +7 -0
  12. package/build/src/akuma/allowed.js +40 -0
  13. package/build/src/akuma/archetype.d.ts +7 -4
  14. package/build/src/akuma/archetype.js +15 -16
  15. package/build/src/akuma/body.d.ts +3 -1
  16. package/build/src/akuma/body.js +19 -8
  17. package/build/src/akuma/heart/facts.d.ts +49 -7
  18. package/build/src/akuma/heart/index.d.ts +4 -8
  19. package/build/src/akuma/heart/index.js +76 -15
  20. package/build/src/akuma/heart/rows.d.ts +7 -5
  21. package/build/src/akuma/heart/rows.js +98 -11
  22. package/build/src/akuma/heart/schema.d.ts +1 -1
  23. package/build/src/akuma/heart/schema.js +15 -10
  24. package/build/src/akuma/heart/soul.js +3 -1
  25. package/build/src/akuma/heart/storage.d.ts +2 -0
  26. package/build/src/akuma/heart/storage.js +20 -0
  27. package/build/src/akuma/heart/timeline.d.ts +1 -5
  28. package/build/src/akuma/heart/timeline.js +8 -17
  29. package/build/src/akuma/index.d.ts +4 -1
  30. package/build/src/akuma/index.js +1 -0
  31. package/build/src/akuma/projection.d.ts +17 -3
  32. package/build/src/akuma/projection.js +40 -3
  33. package/build/src/akuma/provider.d.ts +1 -1
  34. package/build/src/akuma/provider.js +2 -1
  35. package/build/src/akuma/providers/acp/core.d.ts +3 -1
  36. package/build/src/akuma/providers/acp/core.js +4 -4
  37. package/build/src/akuma/providers/acp/events.d.ts +11 -3
  38. package/build/src/akuma/providers/acp/events.js +78 -4
  39. package/build/src/akuma/providers/claude/events.js +62 -3
  40. package/build/src/akuma/providers/codex-app-server/events.js +1 -13
  41. package/build/src/akuma/providers/grok-build/index.d.ts +2 -1
  42. package/build/src/akuma/providers/grok-build/index.js +72 -2
  43. package/build/src/akuma/providers/opencode-sdk/events.js +71 -6
  44. package/build/src/akuma/providers/pi/events.js +17 -3
  45. package/build/src/akuma/providers/unified-patch.d.ts +4 -0
  46. package/build/src/akuma/providers/unified-patch.js +14 -0
  47. package/build/src/akuma/publication.d.ts +1 -1
  48. package/build/src/akuma/publication.js +36 -7
  49. package/build/src/akuma/requests.d.ts +70 -4
  50. package/build/src/akuma/requests.js +288 -77
  51. package/build/src/akuma-body.d.ts +1 -0
  52. package/build/src/akuma-body.js +31 -0
  53. package/build/src/cli/accepted.d.ts +4 -1
  54. package/build/src/cli/accepted.js +26 -4
  55. package/build/src/cli/commands/akuma-invoke.d.ts +8 -7
  56. package/build/src/cli/commands/akuma-invoke.js +18 -10
  57. package/build/src/cli/commands/akuma.d.ts +11 -2
  58. package/build/src/cli/commands/akuma.js +67 -28
  59. package/build/src/cli/commands/amend.d.ts +1 -1
  60. package/build/src/cli/commands/amend.js +1 -1
  61. package/build/src/cli/commands/contract.d.ts +3 -3
  62. package/build/src/cli/commands/contract.js +1 -1
  63. package/build/src/cli/commands/install.js +4 -4
  64. package/build/src/cli/commands/task-query.d.ts +1 -1
  65. package/build/src/cli/commands/task-query.js +11 -5
  66. package/build/src/cli/commands/task.js +4 -4
  67. package/build/src/cli/coordinates.d.ts +1 -0
  68. package/build/src/cli/coordinates.js +5 -1
  69. package/build/src/cli/invoke.d.ts +1 -0
  70. package/build/src/cli/invoke.js +90 -73
  71. package/build/src/cli/main.d.ts +2 -0
  72. package/build/src/cli/main.js +29 -4
  73. package/build/src/cli/parse.d.ts +1 -0
  74. package/build/src/cli/parse.js +8 -4
  75. package/build/src/cli/render/akuma.d.ts +2 -2
  76. package/build/src/cli/render/akuma.js +84 -13
  77. package/build/src/cli/render/audit.js +16 -2
  78. package/build/src/cli/render/contract.d.ts +2 -0
  79. package/build/src/cli/render/contract.js +203 -36
  80. package/build/src/cli/render/kanshi.js +64 -29
  81. package/build/src/cli/render/receipt.d.ts +2 -1
  82. package/build/src/cli/render/receipt.js +9 -1
  83. package/build/src/cli/render/text.js +3 -1
  84. package/build/src/cli/result.d.ts +38 -3
  85. package/build/src/cli/selectors.d.ts +1 -0
  86. package/build/src/cli/selectors.js +9 -6
  87. package/build/src/cli/usage.d.ts +2 -0
  88. package/build/src/cli/usage.js +4 -0
  89. package/build/src/contract-worktree.js +3 -0
  90. package/build/src/dispatch/index.js +50 -39
  91. package/build/src/git/hooks.d.ts +0 -4
  92. package/build/src/git/hooks.js +0 -36
  93. package/build/src/git/integration.d.ts +2 -6
  94. package/build/src/git/integration.js +8 -8
  95. package/build/src/git/read-observation.js +1 -0
  96. package/build/src/git/reconcile.d.ts +6 -1
  97. package/build/src/git/reconcile.js +55 -9
  98. package/build/src/git/tender.d.ts +19 -4
  99. package/build/src/git/tender.js +44 -13
  100. package/build/src/git/terminal-seal.d.ts +14 -0
  101. package/build/src/git/terminal-seal.js +15 -2
  102. package/build/src/git/workspace.d.ts +1 -0
  103. package/build/src/git/workspace.js +12 -4
  104. package/build/src/index.d.ts +1 -1
  105. package/build/src/kanshi/index.d.ts +1 -1
  106. package/build/src/kanshi/index.js +1 -1
  107. package/build/src/kanshi/read.d.ts +9 -1
  108. package/build/src/kanshi/read.js +91 -37
  109. package/build/src/kanshi/report.d.ts +1 -0
  110. package/build/src/library/address.d.ts +5 -14
  111. package/build/src/library/address.js +20 -22
  112. package/build/src/library/akuma-creation.d.ts +4 -2
  113. package/build/src/library/akuma-creation.js +43 -26
  114. package/build/src/library/bind.js +3 -3
  115. package/build/src/library/catalog.d.ts +1 -2
  116. package/build/src/library/catalog.js +11 -6
  117. package/build/src/library/configuration.d.ts +4 -0
  118. package/build/src/library/configuration.js +19 -0
  119. package/build/src/library/contract.d.ts +15 -1
  120. package/build/src/library/contract.js +51 -4
  121. package/build/src/library/fleet.d.ts +47 -9
  122. package/build/src/library/fleet.js +184 -44
  123. package/build/src/library/input.js +1 -0
  124. package/build/src/library/keiyaku.d.ts +4 -4
  125. package/build/src/library/keiyaku.js +1 -2
  126. package/build/src/protocol/bind.d.ts +1 -2
  127. package/build/src/protocol/bind.js +2 -16
  128. package/build/src/protocol/operations.d.ts +4 -1
  129. package/build/src/protocol/operations.js +33 -27
  130. package/build/src/protocol/read/status.d.ts +3 -1
  131. package/build/src/protocol/read/status.js +24 -14
  132. package/build/src/runtime/proc/run.d.ts +1 -0
  133. package/build/src/runtime/proc/run.js +9 -2
  134. package/build/src/settlement/settle.d.ts +1 -0
  135. package/build/src/settlement/settle.js +4 -1
  136. package/build/src/task/board.d.ts +18 -3
  137. package/build/src/task/board.js +73 -26
  138. package/build/src/task/index.d.ts +3 -3
  139. package/build/src/task/index.js +7 -5
  140. package/build/src/task/operations.d.ts +2 -19
  141. package/build/src/task/operations.js +29 -7
  142. package/build/src/task/query.d.ts +9 -2
  143. package/build/src/task/query.js +32 -64
  144. package/build/src/verification/execution.js +2 -1
  145. package/package.json +3 -1
@@ -13,7 +13,6 @@ async function invokeWait(command, input) {
13
13
  result: await Keiyaku.wait({
14
14
  path: input.path,
15
15
  akuma: command.akuma,
16
- settings: input.settings,
17
16
  ...(input.repo === undefined ? {} : { repo: input.repo }),
18
17
  ...(command.completion === undefined ? {} : { completion: command.completion }),
19
18
  ...(command.timeoutMs === undefined ? {} : { timeoutMs: command.timeoutMs }),
@@ -24,11 +23,21 @@ async function invokeWait(command, input) {
24
23
  async function invokeTell(command, input) {
25
24
  const body = await promptBody(command, input);
26
25
  if (command.interrupt) {
27
- const result = await Keiyaku.interrupt({ path: input.path, akuma: command.akuma, settings: input.settings, body, ...(input.repo === undefined ? {} : { repo: input.repo }) });
26
+ const result = await Keiyaku.interrupt({
27
+ path: input.path,
28
+ akuma: command.akuma,
29
+ body,
30
+ ...(input.repo === undefined ? {} : { repo: input.repo }),
31
+ });
28
32
  const alias = inputAlias(command.akuma);
29
33
  return { kind: "akuma", action: "tell", mode: "interrupt", result, body, ...(alias === undefined ? {} : { alias }) };
30
34
  }
31
- const result = await Keiyaku.tell({ path: input.path, akuma: command.akuma, settings: input.settings, body, ...(input.repo === undefined ? {} : { repo: input.repo }) });
35
+ const result = await Keiyaku.tell({
36
+ path: input.path,
37
+ akuma: command.akuma,
38
+ body,
39
+ ...(input.repo === undefined ? {} : { repo: input.repo }),
40
+ });
32
41
  const alias = inputAlias(command.akuma);
33
42
  return { kind: "akuma", action: "tell", mode: "ordinary", result, body, ...(alias === undefined ? {} : { alias }) };
34
43
  }
@@ -36,7 +45,6 @@ async function invokeHistory(command, input) {
36
45
  const result = await Keiyaku.history({
37
46
  path: input.path,
38
47
  akuma: command.akuma,
39
- settings: input.settings,
40
48
  ...(input.repo === undefined ? {} : { repo: input.repo }),
41
49
  ...(command.before === undefined ? {} : { before: command.before }),
42
50
  ...(command.since === undefined ? {} : { since: command.since }),
@@ -64,7 +72,6 @@ async function invokeFork(command, input) {
64
72
  path: input.path,
65
73
  akuma: command.akuma,
66
74
  at: command.at,
67
- settings: input.settings,
68
75
  ...(input.repo === undefined ? {} : { repo: input.repo }),
69
76
  }),
70
77
  };
@@ -77,7 +84,6 @@ async function invokeKill(command, input) {
77
84
  result: await Keiyaku.kill({
78
85
  path: input.path,
79
86
  akuma: command.akuma,
80
- settings: input.settings,
81
87
  ...(input.repo === undefined ? {} : { repo: input.repo }),
82
88
  }),
83
89
  ...(alias === undefined ? {} : { alias }),
@@ -91,12 +97,14 @@ export async function invokeAkuma(command, input) {
91
97
  path: input.path,
92
98
  archetype: command.archetype,
93
99
  body,
94
- settings: input.settings,
95
- ...(input.executionCwd === undefined ? {} : { cwd: input.executionCwd }),
100
+ ...(input.home === undefined ? {} : { home: input.home }),
101
+ ...(input.settings === undefined ? {} : { settings: input.settings }),
102
+ ...(input.statedCwd === undefined ? {} : { cwd: input.statedCwd }),
96
103
  mode: command.mode,
97
104
  ...(command.timeoutMs === undefined ? {} : { timeoutMs: command.timeoutMs }),
98
105
  ...(input.contract === undefined ? {} : { contract: input.contract }),
99
106
  ...(command.alias === undefined ? {} : { alias: command.alias }),
107
+ ...(command.allowed === undefined ? {} : { allowed: command.allowed }),
100
108
  });
101
109
  return { kind: "akuma", action: "call", result, world: input.path };
102
110
  }
@@ -107,11 +115,11 @@ export async function invokeAkuma(command, input) {
107
115
  case "kill": return await invokeKill(command, input);
108
116
  }
109
117
  }
110
- export async function invokeAkumaStatus(path, akuma, settings, alias, repo) {
118
+ export async function invokeAkumaStatus(path, akuma, alias, repo) {
111
119
  return {
112
120
  kind: "akuma",
113
121
  action: "status",
114
- status: await Keiyaku.status({ path, akuma, settings, ...(repo === undefined ? {} : { repo }) }),
122
+ status: await Keiyaku.status({ path, akuma, ...(repo === undefined ? {} : { repo }) }),
115
123
  ...(alias === undefined ? {} : { alias }),
116
124
  };
117
125
  }
@@ -1,4 +1,5 @@
1
1
  import { type AkumaAlias } from "../../identity/selector.js";
2
+ import { type AllowedActions } from "../../akuma/allowed.js";
2
3
  type Output = Readonly<{
3
4
  output: "text" | "json";
4
5
  }>;
@@ -21,6 +22,7 @@ export type ParsedAkumaCommand = Output & ((Readonly<{
21
22
  alias?: AkumaAlias;
22
23
  mode: "wait" | "detach";
23
24
  timeoutMs?: number;
25
+ allowed?: AllowedActions;
24
26
  }> & Prompted) | Readonly<{
25
27
  command: "kill";
26
28
  akuma: readonly string[];
@@ -38,15 +40,22 @@ export type ParsedAkumaCommand = Output & ((Readonly<{
38
40
  before?: number;
39
41
  since?: number;
40
42
  limit?: number;
41
- }> & Addressed) | (Readonly<{
43
+ }> & Addressed) | Readonly<{
44
+ command: "history";
45
+ contract: string;
46
+ }> | (Readonly<{
42
47
  command: "fork";
43
48
  at: string;
44
49
  }> & Addressed));
50
+ export type InvokedAkumaCommand = Exclude<ParsedAkumaCommand, {
51
+ command: "history";
52
+ contract: string;
53
+ }>;
45
54
  export type AkumaAction = ParsedAkumaCommand["command"];
46
55
  export declare function isAkumaAction(value: string | undefined): value is AkumaAction;
47
56
  export declare function isParsedAkumaCommand(command: Readonly<{
48
57
  command: string;
49
- }>): command is ParsedAkumaCommand;
58
+ }>): command is InvokedAkumaCommand;
50
59
  export declare function renderAkumaRootRows(): readonly string[];
51
60
  export declare function renderAkumaHelp(action: AkumaAction): string;
52
61
  export declare function renderAkumaUsage(action: AkumaAction): string;
@@ -1,7 +1,8 @@
1
- import { CliUsageError, isBlankInput, usageLine } from "../usage.js";
1
+ import { CliUsageError, isBlankInput, usageLine, withJsonAutomationHelp } from "../usage.js";
2
2
  import { archetypeName, parseAkuId } from "../../akuma/identity.js";
3
3
  import { parseDuration as decodeDuration } from "../../duration.js";
4
4
  import { parseAkumaAlias, parseAkumaGlob } from "../../identity/selector.js";
5
+ import { decodeAllowedActions } from "../../akuma/allowed.js";
5
6
  const AKUMA_COMMAND_SPECS = {
6
7
  call: {
7
8
  arity: 1,
@@ -11,15 +12,17 @@ const AKUMA_COMMAND_SPECS = {
11
12
  alias: "value",
12
13
  wait: "value",
13
14
  detach: "boolean",
15
+ allowed: "repeatable",
14
16
  json: "boolean",
15
17
  },
16
- usage: "call <akuma-name> [--contract <kei/...>] [--alias @name] [--wait <duration> | -d | --detach] [--json] (<prompt> | -)",
18
+ usage: "call <akuma-name> [--contract <kei/...>] [--alias @name] [--allowed <product.action>]... [--wait <duration> | -d | --detach] [--json] (<prompt> | -)",
17
19
  purpose: "Birth an Akuma from <akuma-name> with one prompt.",
18
20
  details: [
19
21
  "Give <prompt> as one argument, or use final - to read stdin.",
20
22
  "Default: --wait 5m. An explicit --wait replaces that duration; -d and --detach return after birth.",
21
23
  "--contract dispatches the born Akuma to that Contract.",
22
24
  "--alias assigns the world-local @name selector to the born Akuma.",
25
+ "Repeated --allowed replaces the Archetype list; --allowed none selects an empty list.",
23
26
  "With --contract, Dispatch succeeds first. If @name exists, the alias then moves.",
24
27
  ].join("\n"),
25
28
  },
@@ -45,8 +48,8 @@ const AKUMA_COMMAND_SPECS = {
45
48
  arity: 1,
46
49
  stdin: false,
47
50
  flags: { before: "value", since: "value", limit: "value", last: "boolean", json: "boolean" },
48
- usage: "history <aku/...> [--before <index> | --since <index>] [--limit <count>] [--last] [--json]",
49
- purpose: "Read the persistent execution history or the last answer.",
51
+ usage: "history <aku/...|@alias|kei/...> [--before <index> | --since <index>] [--limit <count>] [--last] [--json]",
52
+ purpose: "Read Akuma execution history or one complete Contract journal and Dispatch timeline.",
50
53
  },
51
54
  fork: {
52
55
  arity: 1,
@@ -67,14 +70,14 @@ export function isAkumaAction(value) {
67
70
  return value !== undefined && Object.hasOwn(AKUMA_COMMAND_SPECS, value);
68
71
  }
69
72
  export function isParsedAkumaCommand(command) {
70
- return isAkumaAction(command.command);
73
+ return isAkumaAction(command.command) && !("contract" in command && command.command === "history");
71
74
  }
72
75
  export function renderAkumaRootRows() {
73
76
  return Object.values(AKUMA_COMMAND_SPECS).flatMap((spec) => [` ${spec.usage}`, ` ${spec.purpose}`]);
74
77
  }
75
78
  export function renderAkumaHelp(action) {
76
79
  const spec = AKUMA_COMMAND_SPECS[action];
77
- return `${spec.purpose}\n\n${usageLine(spec.usage)}${spec.details === undefined ? "" : `\n\n${spec.details}`}`;
80
+ return withJsonAutomationHelp(`${spec.purpose}\n\n${usageLine(spec.usage)}${spec.details === undefined ? "" : `\n\n${spec.details}`}`);
78
81
  }
79
82
  export function renderAkumaUsage(action) {
80
83
  return usageLine(AKUMA_COMMAND_SPECS[action].usage);
@@ -104,6 +107,29 @@ function parseDuration(raw, option, fail) {
104
107
  fail(`${option} duration exceeds the safe millisecond range`);
105
108
  return duration.milliseconds;
106
109
  }
110
+ function scanNamedOption(input, flags, fail) {
111
+ const token = input.argv[input.index];
112
+ const name = token.slice(2);
113
+ const kind = input.spec.flags[name];
114
+ if (kind === undefined)
115
+ fail(`option ${token} is not valid for ${input.action}`);
116
+ if (kind !== "repeatable" && flags[name] !== undefined)
117
+ fail(`duplicate option: ${token}`);
118
+ if (kind === "boolean") {
119
+ flags[name] = true;
120
+ return input.index;
121
+ }
122
+ const value = input.argv[input.index + 1];
123
+ if (value === undefined || value === "-" || value === "-d" || value.startsWith("--")) {
124
+ fail(`${token} requires a value`);
125
+ }
126
+ if (isBlankInput(value))
127
+ fail(`${token} requires a nonblank value`);
128
+ flags[name] = kind === "repeatable"
129
+ ? [...(Array.isArray(flags[name]) ? flags[name] : []), value]
130
+ : value;
131
+ return input.index + 1;
132
+ }
107
133
  function scanAkuma(action, argv, fail) {
108
134
  const spec = AKUMA_COMMAND_SPECS[action];
109
135
  const flags = {};
@@ -129,24 +155,7 @@ function scanAkuma(action, argv, fail) {
129
155
  positionals.push(token);
130
156
  }
131
157
  else {
132
- const name = token.slice(2);
133
- const kind = spec.flags[name];
134
- if (kind === undefined)
135
- fail(`option ${token} is not valid for ${action}`);
136
- if (flags[name] !== undefined)
137
- fail(`duplicate option: ${token}`);
138
- if (kind === "boolean")
139
- flags[name] = true;
140
- else {
141
- const value = argv[index + 1];
142
- if (value === undefined || value === "-" || value === "-d" || value.startsWith("--")) {
143
- fail(`${token} requires a value`);
144
- }
145
- if (isBlankInput(value))
146
- fail(`${token} requires a nonblank value`);
147
- flags[name] = value;
148
- index += 1;
149
- }
158
+ index = scanNamedOption({ action, spec, argv, index }, flags, fail);
150
159
  }
151
160
  }
152
161
  return { flags, positionals, stdin };
@@ -200,7 +209,13 @@ function parseWait(rawSelectors, flags, output, fail) {
200
209
  output,
201
210
  };
202
211
  }
203
- function parseHistory(akuma, flags, output, fail) {
212
+ function parseHistory(selector, flags, output, fail) {
213
+ if (selector.startsWith("kei/")) {
214
+ if (flags.before !== undefined || flags.since !== undefined || flags.limit !== undefined || flags.last === true) {
215
+ fail("history kei/... does not accept --before, --since, --limit, or --last");
216
+ }
217
+ return { command: "history", contract: selector, output };
218
+ }
204
219
  if (flags.before !== undefined && flags.since !== undefined)
205
220
  fail("history --before and --since are mutually exclusive");
206
221
  if (flags.last === true && (flags.before !== undefined || flags.since !== undefined || flags.limit !== undefined)) {
@@ -211,7 +226,7 @@ function parseHistory(akuma, flags, output, fail) {
211
226
  fail("--limit must be no greater than 5000");
212
227
  return {
213
228
  command: "history",
214
- akuma,
229
+ akuma: validateDirect(selector, fail),
215
230
  last: flags.last === true,
216
231
  ...(flags.before === undefined ? {} : { before: positiveIndex(flags.before, "--before", fail) }),
217
232
  ...(flags.since === undefined ? {} : { since: positiveIndex(flags.since, "--since", fail) }),
@@ -224,9 +239,13 @@ function parseAddressed(action, rawSelectors, flags, output, fail) {
224
239
  return { command: action, akuma: rawSelectors.map((value) => validateSet(value, fail)), output };
225
240
  if (action === "wait")
226
241
  return parseWait(rawSelectors, flags, output, fail);
242
+ if (action === "history") {
243
+ const selector = rawSelectors[0];
244
+ if (selector.includes("*"))
245
+ fail("history accepts one complete aku/..., @alias, or kei/... selector");
246
+ return parseHistory(selector, flags, output, fail);
247
+ }
227
248
  const akuma = validateDirect(rawSelectors[0], fail);
228
- if (action === "history")
229
- return parseHistory(akuma, flags, output, fail);
230
249
  const at = stringFlag(flags.at, "fork requires --at <historyId>", fail);
231
250
  return { command: action, akuma, at, output };
232
251
  }
@@ -246,6 +265,24 @@ function parsePrompted(action, positionals, stdin, fail) {
246
265
  function parseTell(flags, subject, prompt, output, fail) {
247
266
  return { command: "tell", akuma: validateDirect(subject, fail), interrupt: flags.interrupt === true, prompt, output };
248
267
  }
268
+ function parseAllowedFlag(raw, fail) {
269
+ if (raw === undefined)
270
+ return undefined;
271
+ if (!Array.isArray(raw))
272
+ fail("--allowed requires a value");
273
+ const selected = raw;
274
+ if (selected.includes("none")) {
275
+ if (selected.length !== 1)
276
+ fail("--allowed none cannot be combined with another value");
277
+ return [];
278
+ }
279
+ try {
280
+ return decodeAllowedActions(selected, "--allowed");
281
+ }
282
+ catch (error) {
283
+ fail(error instanceof Error ? error.message : "invalid --allowed action");
284
+ }
285
+ }
249
286
  function parseCall(flags, archetype, prompt, output, fail) {
250
287
  let alias;
251
288
  if (flags.alias !== undefined) {
@@ -260,6 +297,7 @@ function parseCall(flags, archetype, prompt, output, fail) {
260
297
  fail("call --wait and --detach are mutually exclusive");
261
298
  const mode = flags.detach === true ? "detach" : "wait";
262
299
  const timeoutMs = flags.wait === undefined ? undefined : parseDuration(flags.wait, "--wait", fail);
300
+ const allowed = parseAllowedFlag(flags.allowed, fail);
263
301
  return {
264
302
  command: "call",
265
303
  archetype,
@@ -267,6 +305,7 @@ function parseCall(flags, archetype, prompt, output, fail) {
267
305
  ...(alias === undefined ? {} : { alias }),
268
306
  mode,
269
307
  ...(timeoutMs === undefined ? {} : { timeoutMs }),
308
+ ...(allowed === undefined ? {} : { allowed }),
270
309
  prompt,
271
310
  output,
272
311
  };
@@ -4,7 +4,7 @@ type AmendCommandInput = Readonly<{
4
4
  command: ParsedAmend;
5
5
  repo: Repo;
6
6
  contract: Keiyaku;
7
- markdown: string;
7
+ markdown?: string;
8
8
  gates: readonly Gate[] | undefined;
9
9
  actor?: ActorId;
10
10
  hooks?: WorktreeHooks;
@@ -4,7 +4,7 @@ export function amendFromCommand({ command, repo, contract, markdown, gates, act
4
4
  ? []
5
5
  : command.after?.map((id) => contractFromInput(repo, id).id);
6
6
  return contract.amend({
7
- markdown,
7
+ ...(markdown === undefined ? {} : { markdown }),
8
8
  ...(actor === undefined ? {} : { actor }),
9
9
  ...(hooks === undefined ? {} : { hooks }),
10
10
  ...(after === undefined ? {} : { after }),
@@ -25,7 +25,7 @@ export declare const CONTRACT_COMMAND_SPECS: {
25
25
  };
26
26
  readonly amend: {
27
27
  readonly positional: "optional";
28
- readonly stdin: "required";
28
+ readonly stdin: "optional";
29
29
  readonly flags: {
30
30
  readonly actor: "value";
31
31
  readonly after: "repeat-value";
@@ -33,8 +33,8 @@ export declare const CONTRACT_COMMAND_SPECS: {
33
33
  readonly gates: "value";
34
34
  readonly json: "boolean";
35
35
  };
36
- readonly usage: "amend [<contract>|@<contract>] [--after <kei/...> ... | --clear-after] [--gates <name>] [--actor <actor>] [--json] -";
37
- readonly purpose: "Apply stdin amendment operations to one Contract.";
36
+ readonly usage: "amend [<contract>|@<contract>] [--after <kei/...> ... | --clear-after] [--gates <name>] [--actor <actor>] [--json] [-]";
37
+ readonly purpose: "Amend one Contract's document operations or structured terms.";
38
38
  };
39
39
  readonly deliver: {
40
40
  readonly positional: "optional";
@@ -5,7 +5,7 @@ export const AMEND_MINIMAL_STDIN_HELP = [
5
5
  ].join("\n");
6
6
  export const CONTRACT_COMMAND_SPECS = {
7
7
  bind: { positional: "none", stdin: "required", flags: { actor: "value", task: "value", target: "value", here: "boolean", after: "repeat-value", gates: "value", json: "boolean" }, usage: "bind [--task <task/...>] [--target <ref>] [--here] [--after <kei/...> ...] [--gates <name>] [--actor <actor>] [--json] -", purpose: "Create one Contract from stdin Markdown." },
8
- amend: { positional: "optional", stdin: "required", flags: { actor: "value", after: "repeat-value", "clear-after": "boolean", gates: "value", json: "boolean" }, usage: "amend [<contract>|@<contract>] [--after <kei/...> ... | --clear-after] [--gates <name>] [--actor <actor>] [--json] -", purpose: "Apply stdin amendment operations to one Contract." },
8
+ amend: { positional: "optional", stdin: "optional", flags: { actor: "value", after: "repeat-value", "clear-after": "boolean", gates: "value", json: "boolean" }, usage: "amend [<contract>|@<contract>] [--after <kei/...> ... | --clear-after] [--gates <name>] [--actor <actor>] [--json] [-]", purpose: "Amend one Contract's document operations or structured terms." },
9
9
  deliver: { positional: "optional", stdin: "none", flags: { actor: "value", message: "value", "include-dirty": "boolean", json: "boolean" }, usage: "deliver [<contract>|@<contract>] [--message <text>] [--include-dirty] [--actor <actor>] [--json]", purpose: "Deliver one Contract candidate." },
10
10
  review: { positional: "optional", stdin: "optional", flags: { actor: "value", satisfied: "boolean", unsatisfied: "boolean", summary: "value", json: "boolean" }, usage: "review [<contract>|@<contract>] (--satisfied | --unsatisfied) (--summary <text> | -) [--actor <actor>] [--json]", purpose: "Record one review verdict." },
11
11
  arc: { positional: "optional", stdin: "required", flags: { actor: "value", json: "boolean" }, usage: "arc [<contract>|@<contract>] [--actor <actor>] [--json] -", purpose: "Record stdin arc Markdown for one Contract." },
@@ -1,7 +1,7 @@
1
1
  import { dirname, resolve } from "node:path";
2
2
  import { fileURLToPath } from "node:url";
3
- import { runProcess } from "../../runtime/proc/run.js";
4
- import { CliUsageError, usageLine } from "../usage.js";
3
+ import { runCrossPlatformProcess } from "../../runtime/proc/run.js";
4
+ import { CliUsageError, usageLine, withJsonAutomationHelp } from "../usage.js";
5
5
  export const HARNESS_NAMES = ["codex", "claude", "opencode", "pi"];
6
6
  export const INSTALL_USAGE = "install <codex|claude|opencode|pi> [--json]\n install --all [--json]";
7
7
  function isHarness(value) {
@@ -43,7 +43,7 @@ export function parseInstallCommand(argv) {
43
43
  };
44
44
  }
45
45
  export function renderInstallHelp() {
46
- return `Install the Keiyaku skills into one or more agent harnesses.\n\n${usageLine(INSTALL_USAGE)}`;
46
+ return withJsonAutomationHelp(`Install the Keiyaku skills into one or more agent harnesses.\n\n${usageLine(INSTALL_USAGE)}`);
47
47
  }
48
48
  const INSTALL_TIMEOUT_MS = 5 * 60 * 1000;
49
49
  const ASSET_ROOT = resolve(dirname(fileURLToPath(import.meta.url)), "../../../integrations/marketplace");
@@ -80,7 +80,7 @@ function diagnostic(argv, outcome) {
80
80
  export function installAssetsRoot() {
81
81
  return ASSET_ROOT;
82
82
  }
83
- export async function installHarnesses(harnesses, environment, runner = runProcess) {
83
+ export async function installHarnesses(harnesses, environment, runner = runCrossPlatformProcess) {
84
84
  const results = [];
85
85
  for (const harness of harnesses) {
86
86
  let failure;
@@ -1,4 +1,4 @@
1
- import type { TaskQueryExpression } from "../../task/index.js";
1
+ import { type TaskQueryExpression } from "../../task/index.js";
2
2
  export type { TaskQueryExpression } from "../../task/index.js";
3
3
  export declare function parseTaskQueryExpression(source: string): TaskQueryExpression;
4
4
  export declare function validateTaskLimit(source: string): void;
@@ -1,3 +1,4 @@
1
+ import { TASK_RELATION_PREDICATE_FIELDS, } from "../../task/index.js";
1
2
  import { MAX_TASK_LIMIT, normalizeTaskQuery } from "../../task/query.js";
2
3
  function syntax(message, offset) { throw new Error(`${message} at column ${offset + 1}`); }
3
4
  function scanString(source, offset) {
@@ -133,7 +134,15 @@ function parseTextPredicate(field, operator, value) {
133
134
  return { field, operator: selected, value: value.value };
134
135
  }
135
136
  function parseRelationPredicate(field, operator, value) {
136
- return { field, operator: equalityOperator(operator, field), value: canonicalTaskId(value, field) };
137
+ const relationField = TASK_RELATION_PREDICATE_FIELDS.find((candidate) => candidate === field.value);
138
+ if (relationField === undefined) {
139
+ syntax(`unknown query field ${JSON.stringify(field.value)}`, field.offset);
140
+ }
141
+ return {
142
+ field: relationField,
143
+ operator: equalityOperator(operator, relationField),
144
+ value: canonicalTaskId(value, relationField),
145
+ };
137
146
  }
138
147
  function parseBooleanPredicate(field, operator, value) {
139
148
  return { field, operator: equalityOperator(operator, field), value: boolean(value) };
@@ -216,14 +225,11 @@ class Parser {
216
225
  operator: equalityOperator(operator, "parent"),
217
226
  value: value.value === "none" ? null : canonicalTaskId(value, "parent"),
218
227
  };
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
228
  case "ready": return parseBooleanPredicate("ready", operator, value);
223
229
  case "blocked": return parseBooleanPredicate("blocked", operator, value);
224
230
  case "created": return parseTimestampPredicate("created", operator, value);
225
231
  case "updated": return parseTimestampPredicate("updated", operator, value);
226
- default: return syntax(`unknown query field ${JSON.stringify(field.value)}`, field.offset);
232
+ default: return parseRelationPredicate(field, operator, value);
227
233
  }
228
234
  }
229
235
  }
@@ -1,4 +1,4 @@
1
- import { CliUsageError, isBlankInput, usageLine } from "../usage.js";
1
+ import { CliUsageError, isBlankInput, usageLine, withJsonAutomationHelp } from "../usage.js";
2
2
  import { parseTaskQueryExpression, validateTaskLimit, validateTaskParent, } from "./task-query.js";
3
3
  const COMMON = { json: "boolean" };
4
4
  const TASK_COMMAND_SPECS = {
@@ -46,14 +46,14 @@ export function isTaskAction(value) {
46
46
  export function renderTaskHelp(action) {
47
47
  if (action !== undefined) {
48
48
  const spec = TASK_COMMAND_SPECS[action];
49
- return `${spec.purpose}\n\n${usageLine(spec.usage)}`;
49
+ return withJsonAutomationHelp(`${spec.purpose}\n\n${usageLine(spec.usage)}`);
50
50
  }
51
- return [
51
+ return withJsonAutomationHelp([
52
52
  "usage: keiyaku task <command> ...",
53
53
  "",
54
54
  "commands:",
55
55
  ...Object.values(TASK_COMMAND_SPECS).flatMap((spec) => spec.usage.split("\n").map((line) => ` ${line}`).concat(` ${spec.purpose}`)),
56
- ].join("\n");
56
+ ].join("\n"));
57
57
  }
58
58
  export function renderTaskUsage(action) {
59
59
  return usageLine(TASK_COMMAND_SPECS[action].usage);
@@ -3,6 +3,7 @@ import { type WorldRoot } from "../world.js";
3
3
  import { type ParsedCommand } from "./parse.js";
4
4
  export type CliCoordinates = Readonly<{
5
5
  cwd: string;
6
+ cwdSource: "input" | "process";
6
7
  repo?: Repo;
7
8
  world: WorldRoot | null;
8
9
  candidateWorld: WorldRoot | null;
@@ -38,8 +38,11 @@ function repoPolicy(command) {
38
38
  return { use: command.query.kind === "contracts" ? "required" : "none", acceptsExplicit: false };
39
39
  case "status":
40
40
  case "tell":
41
- case "history":
42
41
  return { use: "optional", acceptsExplicit: false };
42
+ case "history":
43
+ return "contract" in command
44
+ ? { use: "required", acceptsExplicit: true }
45
+ : { use: "optional", acceptsExplicit: false };
43
46
  case "fork":
44
47
  return { use: "optional", acceptsExplicit: true };
45
48
  case "wait":
@@ -112,6 +115,7 @@ export async function resolveCliCoordinates(input) {
112
115
  const world = await resolveWorld(cwd, worldRepo);
113
116
  return {
114
117
  cwd,
118
+ cwdSource: input.cwd === undefined ? "process" : "input",
115
119
  ...(repo === undefined ? {} : { repo }),
116
120
  world: world.root,
117
121
  candidateWorld: world.candidate,
@@ -10,6 +10,7 @@ type InvokeRuntime = Readonly<{
10
10
  environment?: NodeJS.ProcessEnv;
11
11
  readStdin?: () => Promise<string>;
12
12
  actor?: ActorId;
13
+ onOperationStart?: () => void;
13
14
  }>;
14
15
  export type SettingsInvocationResult = Readonly<{
15
16
  kind: "settings";