@astrosheep/keiyaku 4.6.2 → 4.6.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.
- package/README.md +93 -45
- package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-akuma/SKILL.md +4 -3
- package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-akuma/references/automation.md +2 -2
- package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-babysit/SKILL.md +1 -1
- package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-bind/SKILL.md +4 -3
- package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-workflow/SKILL.md +34 -22
- package/build/src/akuma/providers/pi/events.js +3 -2
- package/build/src/body/render.d.ts +9 -1
- package/build/src/body/render.js +11 -0
- package/build/src/cli/commands/akuma-grammar.js +12 -3
- package/build/src/cli/commands/akuma.js +6 -0
- package/build/src/cli/commands/contract-grammar.js +7 -0
- package/build/src/cli/commands/contract-help.d.ts +1 -1
- package/build/src/cli/commands/contract-help.js +21 -9
- package/build/src/cli/commands/contract.js +3 -1
- package/build/src/cli/commands/task-grammar.js +9 -7
- package/build/src/cli/parse.js +5 -0
- package/build/src/cli/render/akuma-activity.d.ts +26 -6
- package/build/src/cli/render/akuma-activity.js +128 -118
- package/build/src/cli/render/akuma-tool.js +1 -1
- package/build/src/cli/render/audit.js +4 -6
- package/build/src/cli/render/catalog.js +7 -4
- package/build/src/cli/render/contract-history.js +8 -18
- package/build/src/cli/render/contract.js +5 -10
- package/build/src/cli/render/kanshi-akuma.js +1 -1
- package/build/src/cli/render/kanshi.js +24 -30
- package/build/src/cli/render/receipt.d.ts +1 -2
- package/build/src/cli/render/receipt.js +1 -6
- package/build/src/cli/render/refusal.js +2 -1
- package/build/src/cli/render/task.js +2 -1
- package/build/src/core/facts/codec.js +0 -6
- package/build/src/core/facts/fold.js +0 -7
- package/build/src/core/facts/types.d.ts +1 -4
- package/build/src/core/verbs/deliver.js +2 -14
- package/build/src/library/bind.d.ts +20 -20
- package/build/src/library/bind.js +2 -1
- package/build/src/library/contract-composition.d.ts +5 -2
- package/build/src/library/contract-composition.js +5 -3
- package/build/src/library/contract-creation.d.ts +7 -1
- package/build/src/library/contract-creation.js +19 -9
- package/build/src/library/contract-execution.d.ts +1 -0
- package/build/src/library/contract-execution.js +12 -4
- package/build/src/library/contract-settings.d.ts +2 -2
- package/build/src/library/contract-settings.js +21 -12
- package/build/src/library/outcome.d.ts +476 -652
- package/build/src/library/refusal.d.ts +2 -2
- package/build/src/protocol/bind.d.ts +1 -1
- package/build/src/protocol/deliver.js +1 -1
- package/build/src/protocol/execution-observation.d.ts +0 -8
- package/build/src/protocol/intent.js +2 -2
- package/build/src/protocol/operations.d.ts +1 -1
- package/build/src/protocol/read/status.d.ts +1 -1
- package/build/src/protocol/read/status.js +0 -2
- package/build/src/runtime/proc/windows-launch.exe +0 -0
- package/build/src/world.d.ts +1 -0
- package/build/src/world.js +14 -0
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -52,7 +52,7 @@ The CLI parses a predicate; the evaluator never sees a raw shell string.
|
|
|
52
52
|
Query reads only Task facts. No Contract. No Akuma.
|
|
53
53
|
|
|
54
54
|
## Verification
|
|
55
|
-
```bash
|
|
55
|
+
```bash timeout=2m
|
|
56
56
|
npm test
|
|
57
57
|
```
|
|
58
58
|
```
|
|
@@ -60,60 +60,60 @@ npm test
|
|
|
60
60
|
## What a deal looks like
|
|
61
61
|
|
|
62
62
|
```bash
|
|
63
|
-
keiyaku bind - <
|
|
64
|
-
keiyaku call worker -
|
|
65
|
-
keiyaku deliver
|
|
66
|
-
keiyaku review --satisfied
|
|
63
|
+
keiyaku bind - < contract.md # terms written; an isolated worktree appears
|
|
64
|
+
keiyaku call worker - # a worker goes in
|
|
65
|
+
keiyaku deliver <contract> # delivered; Verification runs; gates judge
|
|
66
|
+
keiyaku review <contract> --satisfied --summary - # attested; main moves with a commit receipt
|
|
67
67
|
```
|
|
68
68
|
|
|
69
69
|
## What the journal records
|
|
70
70
|
|
|
71
|
-
One real deal,
|
|
71
|
+
One real deal, read back with `keiyaku history`:
|
|
72
72
|
|
|
73
73
|
```text
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
74
|
+
history kei/ship-typed-task-query-b186 · accepted · 9 entries
|
|
75
|
+
|
|
76
|
+
2026-10-04
|
|
77
|
+
|
|
78
|
+
16:53 bound to targetless @ 3c5d8c0 · gate review
|
|
79
|
+
16:53 delivered 210eeed · ✓ verification · 10 lines
|
|
80
|
+
16:53 × review · 6 lines
|
|
81
|
+
16:53 delivered aebef87 · ✓ verification · 10 lines
|
|
82
|
+
16:53 ✓ review · 6 lines
|
|
83
|
+
16:53 accepted
|
|
84
84
|
```
|
|
85
85
|
|
|
86
|
-
The review said no
|
|
87
|
-
|
|
86
|
+
The review said no, and the deal did not land. The verdict and its reason
|
|
87
|
+
stay on the journal as bytes — `keiyaku history --json` reads them:
|
|
88
|
+
|
|
89
|
+
```json
|
|
90
|
+
{
|
|
91
|
+
"kind": "attestation",
|
|
92
|
+
"contract": "kei/ship-typed-task-query-b186",
|
|
93
|
+
"data": {
|
|
94
|
+
"gate": "reviewed",
|
|
95
|
+
"verdict": "unsatisfied",
|
|
96
|
+
"summary": "Query evaluator still reaches for Contract facts; reads are not Task-owned."
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
```
|
|
88
100
|
|
|
89
101
|
## The board
|
|
90
102
|
|
|
91
|
-
`keiyaku status` is the one screen.
|
|
103
|
+
`keiyaku status` is the one screen. Two Contracts and two Tasks in flight:
|
|
92
104
|
|
|
93
105
|
```text
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
● aku/expert-akuma/a7aafc9e running
|
|
106
|
-
alias @process-custody-lead
|
|
107
|
-
keiyaku kei/make-process-custody-capability-honest (active)
|
|
108
|
-
○ aku/design-akuma/cc53ef08 asleep
|
|
109
|
-
alias @timeline-design
|
|
110
|
-
! aku/grok/95d90b7d stranded
|
|
111
|
-
alias @acp-provider-impl
|
|
112
|
-
|
|
113
|
-
task 8 · 5 ready · 2 held
|
|
114
|
-
● task/align-task-cli-truth-promises-for-ready-compose in_progress
|
|
115
|
-
Align Task CLI truth promises for ready compose and world absence
|
|
116
|
-
P0 · keiyaku kei/align-task-cli-truth-promises (active)
|
|
106
|
+
CONTRACTS // recent
|
|
107
|
+
|
|
108
|
+
⧗ kei/ship-typed-task-query-c898 · 1s · Ship typed Task query
|
|
109
|
+
[✓] delivery [ ] review
|
|
110
|
+
⧗ kei/tighten-receipt-vocabulary-4a70 · 18s · Tighten receipt vocabulary
|
|
111
|
+
[ ] delivery [ ] review
|
|
112
|
+
|
|
113
|
+
TASKS // recent
|
|
114
|
+
|
|
115
|
+
○ task/regenerate-readme-from-real-010b · ready · P1 · Regenerate README from real output
|
|
116
|
+
○ task/align-task-cli-truth-promises-3af5 · ready · P0 · Align Task CLI truth promises
|
|
117
117
|
```
|
|
118
118
|
|
|
119
119
|
Marks accelerate scanning; the words carry the state.
|
|
@@ -122,9 +122,8 @@ Marks accelerate scanning; the words carry the state.
|
|
|
122
122
|
|
|
123
123
|
```markdown
|
|
124
124
|
---
|
|
125
|
-
provider:
|
|
126
|
-
model:
|
|
127
|
-
access: write
|
|
125
|
+
provider: pi
|
|
126
|
+
model: kimi-coding/k3-256k
|
|
128
127
|
description: Repository implementation agent
|
|
129
128
|
---
|
|
130
129
|
Make scoped changes and run relevant tests.
|
|
@@ -132,6 +131,55 @@ Make scoped changes and run relevant tests.
|
|
|
132
131
|
|
|
133
132
|
One Markdown file, one worker. `keiyaku call worker` summons it.
|
|
134
133
|
|
|
134
|
+
## Configuration
|
|
135
|
+
|
|
136
|
+
Settings are JSON at two addresses: `~/.keiyaku/settings.json` for the user,
|
|
137
|
+
`.keiyaku/settings.json` in the project. A project record
|
|
138
|
+
wholly shadows the same-name user record. There is no write command; edit the
|
|
139
|
+
files directly and inspect the merged, provenance-annotated view with
|
|
140
|
+
`keiyaku settings`.
|
|
141
|
+
|
|
142
|
+
Named gate groups provide reusable gate sets. Bind and amend select them with
|
|
143
|
+
`--gates`; the `default` group applies when binding with `--gates` omitted.
|
|
144
|
+
Amending without `--gates` keeps the existing gates:
|
|
145
|
+
|
|
146
|
+
```json
|
|
147
|
+
{
|
|
148
|
+
"gates": {
|
|
149
|
+
"default": { "kind": "bundle", "gates": ["reviewed"] },
|
|
150
|
+
"strict": { "kind": "bundle", "gates": ["reviewed", "verified"] }
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
Selections accept the built-in names `reviewed` and `verified`, or configured
|
|
156
|
+
group names. Unknown names are rejected with the known names listed. Custom gates
|
|
157
|
+
must be declared inside a configured group; they stay unsatisfied until a producer
|
|
158
|
+
attests them.
|
|
159
|
+
|
|
160
|
+
Worktree hooks run commands when a Contract's managed worktree is created or
|
|
161
|
+
destroyed — dependency installs are the usual suspect:
|
|
162
|
+
|
|
163
|
+
```json
|
|
164
|
+
{
|
|
165
|
+
"worktree": {
|
|
166
|
+
"create": [
|
|
167
|
+
{ "name": "install", "argv": ["npm", "ci", "--ignore-scripts", "--prefer-offline"], "timeoutMs": 300000 }
|
|
168
|
+
],
|
|
169
|
+
"destroy": [
|
|
170
|
+
{ "name": "teardown", "argv": ["docker", "compose", "down", "-v"], "timeoutMs": 60000 }
|
|
171
|
+
]
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
Hooks run as one ordered phase inside the worktree and must be replay-safe: a
|
|
177
|
+
retry reruns the phase from its beginning. Create hooks prepare the worktree;
|
|
178
|
+
destroy hooks release external resources the worktree deletion alone cannot.
|
|
179
|
+
A failing hook retains the worktree
|
|
180
|
+
and reports lag; it never abandons the Contract. `keiyaku settings --help`
|
|
181
|
+
lists every recognized namespace.
|
|
182
|
+
|
|
135
183
|
## Install
|
|
136
184
|
|
|
137
185
|
```bash
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: keiyaku-akuma
|
|
3
3
|
description: >-
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
Use when the work involves Akuma or Aku, or when you want other hands on
|
|
5
|
+
it: delegating a job, or fanning out research, checks, or other parallel
|
|
6
|
+
work.
|
|
6
7
|
---
|
|
7
8
|
|
|
8
9
|
# Keiyaku Akuma
|
|
@@ -22,7 +23,7 @@ waiting for the first answer. Use explicit `--wait <duration>` when this
|
|
|
22
23
|
invocation should block while observing that initial work. `--wait` only bounds
|
|
23
24
|
this command; it never stops the Akuma. Read `call --help` for flag syntax,
|
|
24
25
|
defaults, and action vocabulary.
|
|
25
|
-
|
|
26
|
+
To hand a Contract role to an Akuma, use the grant table in `keiyaku-workflow`.
|
|
26
27
|
|
|
27
28
|
The selected Akuma name is a reusable worker configuration, not an individual.
|
|
28
29
|
Calling it again creates an independent Akuma. Use different names for
|
package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-akuma/references/automation.md
CHANGED
|
@@ -46,8 +46,8 @@ skeptics to break the finalists, and generate replacements only for the
|
|
|
46
46
|
identified weaknesses. Bound rounds and cost; "keep improving" is not a useful
|
|
47
47
|
stop condition.
|
|
48
48
|
|
|
49
|
-
|
|
50
|
-
|
|
49
|
+
Blind comparisons and adversarial checks need separate contexts. Create
|
|
50
|
+
separate Akuma for them. Reuse an identity for follow-up
|
|
51
51
|
investigation, not as a supposedly fresh judge of its own earlier answer.
|
|
52
52
|
|
|
53
53
|
## Public Entry And A Single Structured Turn
|
|
@@ -52,7 +52,7 @@ does not move without you.
|
|
|
52
52
|
| Observed shape | First response |
|
|
53
53
|
| --- | --- |
|
|
54
54
|
| Silent wedge: initial answer empty and worktree clean | Inspect status and history, then send one steering `tell`. If the worker remains unresponsive, `kill` and call a new Akuma; an empty answer alone is not proof the Contract failed. |
|
|
55
|
-
| Verdict record refused with `not-allowed` | Read the worker's analysis in history
|
|
55
|
+
| Verdict record refused with `not-allowed` | Read the worker's analysis in history. Record the verdict yourself if you did not deliver the candidate, or call a new Reviewer with the grant from `keiyaku-workflow` and have it inspect current work and record a fresh verdict. The refused request wrote no Contract testimony. |
|
|
56
56
|
| Review stale after changed candidate tender | Normal content-currentness result. Give the same Reviewer the changed diff and current terms with `tell`; have it re-examine and record current testimony. A target move alone does not stale review. |
|
|
57
57
|
| Target moved under the appointed worktree | Have the Deliverer inspect target movement and merge the target branch (main when selected) in that worktree when required; resolve conflicts there, then audit again. Do not resolve against a stale integration preview. |
|
|
58
58
|
| Typed refusal names its rule | Fix the named input or physical condition, then retry from a fresh observation. Do not repeat the same request blindly. |
|
|
@@ -183,9 +183,10 @@ keiyaku -C <repo> bind --gates "" - < CONTRACT.md
|
|
|
183
183
|
keiyaku -C <repo> bind --gates reviewed - < CONTRACT.md
|
|
184
184
|
```
|
|
185
185
|
|
|
186
|
-
Omitting `--gates` uses `gates.default`, or `reviewed` when no default
|
|
186
|
+
Omitting `--gates` uses `gates.default`, or `reviewed` when no default group
|
|
187
187
|
exists. `--gates ""` selects no gates. Gates are named acceptance obligations,
|
|
188
|
-
not work assignments.
|
|
188
|
+
not work assignments. Named gate groups and worktree hooks live in Settings;
|
|
189
|
+
see the README Configuration section or `keiyaku settings --help`.
|
|
189
190
|
|
|
190
191
|
## 3. After Binding
|
|
191
192
|
|
|
@@ -204,7 +205,7 @@ waiting, the stated prerequisites remain; do not bind a duplicate Contract.
|
|
|
204
205
|
|
|
205
206
|
The holder now owns the lifecycle:
|
|
206
207
|
|
|
207
|
-
1. Start the work directly or delegate Tasks, Arc chapters, Akumas, or
|
|
208
|
+
1. Start the work directly or delegate Tasks, Arc chapters, Akumas, or Contract roles.
|
|
208
209
|
2. Run independent work in parallel and let it converge on the one acceptance
|
|
209
210
|
boundary.
|
|
210
211
|
3. Prepare and deliver the candidate.
|
|
@@ -38,13 +38,26 @@ to create parallel work.
|
|
|
38
38
|
Use `after` only when one `kei` requires another to be accepted first. Ordinary
|
|
39
39
|
overlap is not a dependency.
|
|
40
40
|
|
|
41
|
-
## 3.
|
|
41
|
+
## 3. Roles
|
|
42
42
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
43
|
+
A `kei` has three roles. They are responsibilities, not people, and one agent
|
|
44
|
+
may fill more than one of them, with one exception below.
|
|
45
|
+
|
|
46
|
+
- **Holder:** exactly one per `kei`. Owns the fulfillment loop and acceptance
|
|
47
|
+
until the Contract is accepted or abandoned. Handing work to others never
|
|
48
|
+
transfers this responsibility.
|
|
49
|
+
- **Deliverer:** produces the candidate in the appointed worktree and tenders
|
|
50
|
+
it with `deliver`.
|
|
51
|
+
- **Reviewer:** judges the complete Contract against its acceptance conditions
|
|
52
|
+
and records the verdict with `review`.
|
|
53
|
+
|
|
54
|
+
Review is independent when the Reviewer did not deliver the candidate under
|
|
55
|
+
review. That is the whole requirement. The holder may review its own `kei`; a
|
|
56
|
+
fresh Akuma is not what makes review independent. Keiyaku does not check who
|
|
57
|
+
reviews, so keeping the Reviewer apart from the Deliverer is the holder's duty.
|
|
58
|
+
|
|
59
|
+
Any role may be filled directly or handed to an Akuma. Hand one over when other
|
|
60
|
+
hands help the work, not to make review count.
|
|
48
61
|
|
|
49
62
|
## 4. Lifecycle
|
|
50
63
|
|
|
@@ -78,31 +91,30 @@ Placement judges declared gates, not the producer's identity. Review does not
|
|
|
78
91
|
replace Verification; audit never requests placement. Read the final receipt
|
|
79
92
|
for stops and reconciliation lag after an admitted act.
|
|
80
93
|
|
|
81
|
-
## 5.
|
|
94
|
+
## 5. Handing A Role To An Akuma
|
|
82
95
|
|
|
83
96
|
This is the one commissioning map. `call --help` owns the exact action
|
|
84
|
-
vocabulary and default set.
|
|
97
|
+
vocabulary and default set. The flagship acting directly needs no grant.
|
|
98
|
+
Birth freezes an Akuma's actions; `--contract` records association, not
|
|
99
|
+
authority.
|
|
85
100
|
|
|
86
|
-
|
|
|
101
|
+
| Role | Grant at birth | Return |
|
|
87
102
|
| --- | --- | --- |
|
|
88
|
-
| Holder |
|
|
89
|
-
| Deliverer | The ordinary baseline
|
|
90
|
-
| Reviewer |
|
|
103
|
+
| Holder | The actions the delegated loop will perform; include `contract.review` when this holder will review. Commission with the `kei`, current Arc when one exists, appointed worktree, and holder duties. | Acceptance or abandonment, with receipts. |
|
|
104
|
+
| Deliverer | The ordinary baseline: every Task mutation, Contract audit and delivery, and Akuma management. `keiyaku -C <repo> call worker --alias @deliverer --contract <kei/...> --workdir <appointed-path> -` | The delivery receipt. |
|
|
105
|
+
| Reviewer | `--allowed contract.review`: `keiyaku -C <repo> call review-akuma --alias @reviewer --contract <kei/...> --workdir <appointed-path> --allowed contract.review -` | The verdict receipt. Without the grant, `not-allowed` refuses and no verdict is recorded. |
|
|
91
106
|
|
|
92
|
-
An Archetype's explicit allowed set replaces the ordinary baseline
|
|
107
|
+
An Archetype's explicit allowed set replaces the ordinary baseline; check
|
|
93
108
|
`status` for effective actions. `tell` and `fork` cannot add a grant to a live
|
|
94
|
-
Akuma
|
|
95
|
-
parent's actions.
|
|
96
|
-
|
|
97
|
-
## 6. Delegate the Work
|
|
109
|
+
Akuma, so call a fresh one with the grant. Nested calls cannot exceed their
|
|
110
|
+
direct parent's actions.
|
|
98
111
|
|
|
99
112
|
Commission with a clear objective, current Arc when one exists, appointed
|
|
100
|
-
worktree, and expected result.
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
command help for exact syntax.
|
|
113
|
+
worktree, and expected result. Never ask the Akuma that delivered a candidate
|
|
114
|
+
to review it. Use `keiyaku-akuma` for invocation and observation, and command
|
|
115
|
+
help for exact syntax.
|
|
104
116
|
|
|
105
|
-
##
|
|
117
|
+
## 6. Decide What Happens Next
|
|
106
118
|
|
|
107
119
|
The holder checks candidate, verification, review, prerequisites, and gates.
|
|
108
120
|
|
|
@@ -164,10 +164,11 @@ function translateMessage(event, state) {
|
|
|
164
164
|
state.assistantSeen = true;
|
|
165
165
|
state.answer = text;
|
|
166
166
|
}
|
|
167
|
-
//
|
|
167
|
+
// Some providers emit a blank text block alongside every tool-use message:
|
|
168
|
+
// empty from Gemini, whitespace from openai-responses-style proxies. It is
|
|
168
169
|
// transport scaffolding, not narration; an ordinary terminal empty answer
|
|
169
170
|
// remains public evidence.
|
|
170
|
-
if (hasTextContent(message.content) && !(message.stopReason === "toolUse" && text.length === 0)) {
|
|
171
|
+
if (hasTextContent(message.content) && !(message.stopReason === "toolUse" && text.trim().length === 0)) {
|
|
171
172
|
translated.push({ type: "assistant", text });
|
|
172
173
|
}
|
|
173
174
|
return translated;
|
|
@@ -1,4 +1,12 @@
|
|
|
1
1
|
import type { ArcData } from "../core/facts/types.js";
|
|
2
|
-
import type { ContractBody } from "./types.js";
|
|
2
|
+
import type { ContractBody, DecodedContractDocument } from "./types.js";
|
|
3
3
|
export declare function renderContractBody(body: ContractBody, currentArc?: ArcData): string;
|
|
4
|
+
/**
|
|
5
|
+
* Binding persists the canonical rendering of decoded terms, never the caller's
|
|
6
|
+
* literal byte stream: layout-only authorship is normalized at admission so it
|
|
7
|
+
* never reappears as phantom changes in later amendment diffs.
|
|
8
|
+
*/
|
|
9
|
+
export declare function decodeCanonicalContractDocument(source: string, options?: Readonly<{
|
|
10
|
+
requireTimeout?: boolean;
|
|
11
|
+
}>): DecodedContractDocument;
|
|
4
12
|
export declare function renderAmendedContractBody(currentSource: string, body: ContractBody, changedSections: ReadonlySet<string>): string;
|
package/build/src/body/render.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { formatDuration } from "../duration.js";
|
|
2
|
+
import { decodeContractDocument } from "./decode.js";
|
|
2
3
|
import { parseToAST } from "../markdown/parse.js";
|
|
3
4
|
import { indexDocument, indexedHeadings, normalizeTitle, rawSlice } from "../markdown/query.js";
|
|
4
5
|
import { CONTRACT_SECTIONS } from "./shape.js";
|
|
@@ -54,6 +55,16 @@ export function renderContractBody(body, currentArc) {
|
|
|
54
55
|
.join("\n\n")
|
|
55
56
|
.concat("\n");
|
|
56
57
|
}
|
|
58
|
+
/**
|
|
59
|
+
* Binding persists the canonical rendering of decoded terms, never the caller's
|
|
60
|
+
* literal byte stream: layout-only authorship is normalized at admission so it
|
|
61
|
+
* never reappears as phantom changes in later amendment diffs.
|
|
62
|
+
*/
|
|
63
|
+
export function decodeCanonicalContractDocument(source, options = {}) {
|
|
64
|
+
const decoded = decodeContractDocument(source, options);
|
|
65
|
+
const rendered = renderContractBody(decoded);
|
|
66
|
+
return rendered === decoded.document.bytes ? decoded : decodeContractDocument(rendered, options);
|
|
67
|
+
}
|
|
57
68
|
function sections(document) {
|
|
58
69
|
return indexedHeadings(indexDocument(document), { level: 2 }).filter((node) => node.type === "section");
|
|
59
70
|
}
|
|
@@ -304,8 +304,10 @@ function parseAddressed(action, rawSelectors, flags, output, fail) {
|
|
|
304
304
|
return { command: action, akuma, at, output };
|
|
305
305
|
}
|
|
306
306
|
function parsePrompted(action, positionals, stdin, fail) {
|
|
307
|
-
if (positionals.length
|
|
308
|
-
fail(`${action}
|
|
307
|
+
if (positionals.length === 0)
|
|
308
|
+
fail(action === "call" ? "call requires an Akuma name" : `${action} requires an Akuma selector`);
|
|
309
|
+
if (positionals.length > 2)
|
|
310
|
+
fail(`${action} accepts at most ${action === "call" ? "a name" : "a selector"} and one prompt`);
|
|
309
311
|
const argument = positionals[1];
|
|
310
312
|
if (stdin && argument !== undefined)
|
|
311
313
|
fail(`${action} accepts either a prompt argument or stdin, not both`);
|
|
@@ -406,7 +408,14 @@ export function parseAkumaCommand(argv) {
|
|
|
406
408
|
: parseAsk(flags, parsed.subject, parsed.prompt, output, fail);
|
|
407
409
|
}
|
|
408
410
|
if (spec.arity === "one-or-more" ? positionals.length === 0 : positionals.length !== spec.arity) {
|
|
409
|
-
|
|
411
|
+
if (positionals.length === 0)
|
|
412
|
+
fail(spec.arity === "one-or-more"
|
|
413
|
+
? `${action} requires at least one Akuma selector`
|
|
414
|
+
: `${action} requires an Akuma selector`);
|
|
415
|
+
else
|
|
416
|
+
fail(spec.arity === 1
|
|
417
|
+
? `${action} accepts one Akuma selector`
|
|
418
|
+
: `${action} accepts exactly ${spec.arity} Akuma selectors`);
|
|
410
419
|
}
|
|
411
420
|
if (stdin)
|
|
412
421
|
fail(`${action} reads no stdin`);
|
|
@@ -9,6 +9,7 @@ import { executionChannel } from "../../akuma/requests.js";
|
|
|
9
9
|
import { settings } from "../../settings.js";
|
|
10
10
|
import { AkumaWorldScopeError } from "../../index.js";
|
|
11
11
|
import { akumasWithExecution } from "../../library/akumas.js";
|
|
12
|
+
import { World } from "../../world.js";
|
|
12
13
|
import { contractFromInput } from "../selectors.js";
|
|
13
14
|
import { ActivityDriver } from "../activity.js";
|
|
14
15
|
import { displayContext, resultContext, writeJson, writeStderr, writeStdout } from "../streams.js";
|
|
@@ -67,6 +68,11 @@ async function inputInitiator(input) {
|
|
|
67
68
|
catch { }
|
|
68
69
|
if (initiator === undefined)
|
|
69
70
|
return {};
|
|
71
|
+
// Plugins coordinate within an established World; a `call` may run where the
|
|
72
|
+
// marker does not exist yet, and activating there would only fail noisily.
|
|
73
|
+
// The initiator still travels with the request either way.
|
|
74
|
+
if (!(await World.established(input.path)))
|
|
75
|
+
return { initiator };
|
|
70
76
|
try {
|
|
71
77
|
await emitInitiatingPluginSignal({
|
|
72
78
|
world: input.path,
|
|
@@ -22,6 +22,9 @@ function optionalFlag(flags, name) {
|
|
|
22
22
|
function refuse(command, message) {
|
|
23
23
|
throw new CliUsageError(message, commandGuide(command, CONTRACT_COMMAND_SPECS[command].usage));
|
|
24
24
|
}
|
|
25
|
+
// The CLI restates the literal gate-word grammar its help prints; the corpus
|
|
26
|
+
// pin in cli-parse.test.ts keeps this shape honest against the owner's gateWord.
|
|
27
|
+
const GATE_NAME_PATTERN = /^[a-z][a-z0-9-]{0,63}$/u;
|
|
25
28
|
function parseGateNames(parts, command) {
|
|
26
29
|
const value = optionalFlag(parts.flags, "gates");
|
|
27
30
|
if (value === undefined)
|
|
@@ -32,6 +35,10 @@ function parseGateNames(parts, command) {
|
|
|
32
35
|
if (names.some((name) => name.length === 0)) {
|
|
33
36
|
refuse(command, '--gates requires comma-separated names or "" to clear gates');
|
|
34
37
|
}
|
|
38
|
+
const invalid = names.find((name) => !GATE_NAME_PATTERN.test(name));
|
|
39
|
+
if (invalid !== undefined) {
|
|
40
|
+
refuse(command, `--gates name must match ^[a-z][a-z0-9-]{0,63}$: ${invalid}`);
|
|
41
|
+
}
|
|
35
42
|
return names;
|
|
36
43
|
}
|
|
37
44
|
function parseBind(parts) {
|
|
@@ -106,7 +106,7 @@ export declare const CONTRACT_COMMAND_SPECS: {
|
|
|
106
106
|
readonly json: "boolean";
|
|
107
107
|
};
|
|
108
108
|
readonly usage: "ls task[/] [--limit <count>]\nls kei[/] [--limit <count>]\nls aku[/] [--limit <count>]\nls aku/<name>[/] [--limit <count>]\nls \"aku/<name>/*\" [--limit <count>]\nls \"aku/*/*\" [--limit <count>]";
|
|
109
|
-
readonly purpose: "List
|
|
109
|
+
readonly purpose: "List tasks (ls task), contracts (ls kei), or akumas (ls aku).";
|
|
110
110
|
readonly details: string;
|
|
111
111
|
};
|
|
112
112
|
readonly audit: {
|
|
@@ -15,11 +15,15 @@ export const CONTRACT_COMMAND_SPECS = {
|
|
|
15
15
|
usage: "bind [--task <task/...>] [--target <ref>] [--after <kei/...> ...] [--gates <name,...>] [--actor <actor>] - | bind --fork-of <kei/...> [--target <ref>] [--actor <actor>]",
|
|
16
16
|
purpose: "Create a Contract from stdin Markdown or copy an existing Contract as a starting point.",
|
|
17
17
|
details: [
|
|
18
|
-
"
|
|
19
|
-
"
|
|
20
|
-
"
|
|
18
|
+
"Gates are acceptance obligations checked at placement: reviewed awaits a",
|
|
19
|
+
"satisfied review, verified awaits a passing Verification run.",
|
|
20
|
+
"",
|
|
21
|
+
"--gates <name,...> selects reviewed, verified, or named gate groups from Settings",
|
|
22
|
+
"(keiyaku settings). A group expands into its gates; unknown names are rejected",
|
|
23
|
+
"with the known names listed. Custom gates must be declared in a configured group.",
|
|
24
|
+
"Duplicates are removed in first-seen order. Names match ^[a-z][a-z0-9-]{0,63}$.",
|
|
21
25
|
"Omitting --gates selects gates.default, or reviewed when no default exists.",
|
|
22
|
-
'--gates "" explicitly binds without gates; --gates reviewed needs no
|
|
26
|
+
'--gates "" explicitly binds without gates; --gates reviewed needs no configuration.',
|
|
23
27
|
"",
|
|
24
28
|
"Binding appoints one managed worktree for the Contract and the receipt",
|
|
25
29
|
"reports its path; the holder and delegates work there. When appointment",
|
|
@@ -55,9 +59,14 @@ export const CONTRACT_COMMAND_SPECS = {
|
|
|
55
59
|
usage: "amend [<contract>|@<contract>] [--after <kei/...> ... | --clear-after] [--gates <name,...>] [--actor <actor>] [-]",
|
|
56
60
|
purpose: "Change a Contract's document, prerequisites, or acceptance gates.",
|
|
57
61
|
details: [
|
|
58
|
-
"
|
|
59
|
-
"
|
|
60
|
-
"
|
|
62
|
+
"Gates are acceptance obligations checked at placement: reviewed awaits a",
|
|
63
|
+
"satisfied review, verified awaits a passing Verification run.",
|
|
64
|
+
"",
|
|
65
|
+
"--gates <name,...> replaces gates using reviewed, verified, or named gate groups",
|
|
66
|
+
"from Settings (keiyaku settings). A group expands into its gates; unknown names",
|
|
67
|
+
"are rejected with the known names listed. Custom gates must be declared in a",
|
|
68
|
+
"configured group. Duplicates are removed in first-seen order.",
|
|
69
|
+
"Names match ^[a-z][a-z0-9-]{0,63}$.",
|
|
61
70
|
'Omitting --gates leaves gates unchanged; --gates "" clears them.',
|
|
62
71
|
"",
|
|
63
72
|
" ## Context|Objective|Design|Region|Criteria|Verification|<extension> (replace, or add new extension)",
|
|
@@ -88,6 +97,7 @@ export const CONTRACT_COMMAND_SPECS = {
|
|
|
88
97
|
"Delivery requests integration. If prerequisites and gates pass, it integrates now and accepts the Contract; otherwise the candidate remains waiting.",
|
|
89
98
|
"Only changed candidate work invalidates earlier review. If Verification did not finish, repeating delivery resumes that candidate; --overwrite replaces it.",
|
|
90
99
|
"Delivery never counts as an independent review verdict.",
|
|
100
|
+
"Content identity names the change's content hash, never a commit; it survives rebase and Git cannot look it up.",
|
|
91
101
|
"",
|
|
92
102
|
" --materialize-conflict Write the observed integration conflict into the worktree as an uncommitted merge.",
|
|
93
103
|
" With --include-dirty, current non-ignored changes are preserved first.",
|
|
@@ -147,8 +157,9 @@ export const CONTRACT_COMMAND_SPECS = {
|
|
|
147
157
|
stdin: "none",
|
|
148
158
|
flags: { limit: "value", json: "boolean" },
|
|
149
159
|
usage: 'ls task[/] [--limit <count>]\nls kei[/] [--limit <count>]\nls aku[/] [--limit <count>]\nls aku/<name>[/] [--limit <count>]\nls "aku/<name>/*" [--limit <count>]\nls "aku/*/*" [--limit <count>]',
|
|
150
|
-
purpose: "List
|
|
160
|
+
purpose: "List tasks (ls task), contracts (ls kei), or akumas (ls aku).",
|
|
151
161
|
details: [
|
|
162
|
+
"ls kei lists active Contracts; accepted and abandoned ones leave the list, and their record stays in history and Git.",
|
|
152
163
|
"ls aku/ lists the callable Akuma-name catalog; ls aku/<name> lists the living Akumas under one name.",
|
|
153
164
|
'ls "aku/<name>/*" is the same living set in glob form; ls "aku/*/*" lists every living Akuma.',
|
|
154
165
|
].join("\n"),
|
|
@@ -162,6 +173,7 @@ export const CONTRACT_COMMAND_SPECS = {
|
|
|
162
173
|
details: [
|
|
163
174
|
"--include-dirty checks all non-ignored worktree changes, staged or not.",
|
|
164
175
|
"--show-diff includes the proposed candidate diff when one can be prepared.",
|
|
176
|
+
"Content identity names the change's content hash, never a commit; it survives rebase and Git cannot look it up.",
|
|
165
177
|
"Progress appears on stderr; stdout contains one final result.",
|
|
166
178
|
].join("\n"),
|
|
167
179
|
},
|
|
@@ -210,7 +222,7 @@ export function renderContractHelp(command) {
|
|
|
210
222
|
"command; edit the files directly.",
|
|
211
223
|
"",
|
|
212
224
|
"Recognized settings:",
|
|
213
|
-
" gates gate
|
|
225
|
+
" gates named gate groups selected by bind and amend",
|
|
214
226
|
" worktree commands run when worktrees are created or destroyed",
|
|
215
227
|
" git.requireBranchesToBeUpToDate whether deliver and audit require up-to-date target branches",
|
|
216
228
|
" providers the available Akuma and how each is run",
|
|
@@ -148,7 +148,9 @@ async function contractLibrary(command, execution, configuration, runtime) {
|
|
|
148
148
|
return composeContractLibrary(execution, createKeiyakuHandle, {
|
|
149
149
|
...(configuration === undefined ? {} : { settings: configuration }),
|
|
150
150
|
...(actor === undefined ? {} : { actor }),
|
|
151
|
-
}
|
|
151
|
+
},
|
|
152
|
+
// The CLI owns this default: an omitted Markdown --target names the invocation branch.
|
|
153
|
+
{ kind: "current-branch" });
|
|
152
154
|
}
|
|
153
155
|
async function runBind(command, input, library) {
|
|
154
156
|
const { coordinates, runtime } = input;
|
|
@@ -204,14 +204,12 @@ export function renderTaskHelp(action) {
|
|
|
204
204
|
const spec = TASK_COMMAND_SPECS[action];
|
|
205
205
|
return `${spec.purpose}\n\n${renderTaskUsage(action)}${spec.details === undefined ? "" : `\n\n${spec.details}`}`;
|
|
206
206
|
}
|
|
207
|
+
const nameWidth = Math.max(...Object.keys(TASK_COMMAND_SPECS).map((name) => name.length));
|
|
207
208
|
return [
|
|
208
209
|
"usage keiyaku task <command> ...",
|
|
210
|
+
" keiyaku task <command> --help shows that command's complete usage",
|
|
209
211
|
"",
|
|
210
|
-
|
|
211
|
-
...Object.values(TASK_COMMAND_SPECS).flatMap((spec) => spec.usage
|
|
212
|
-
.split("\n")
|
|
213
|
-
.map((line) => ` ${line}`)
|
|
214
|
-
.concat(` ${spec.purpose}`)),
|
|
212
|
+
...Object.entries(TASK_COMMAND_SPECS).map(([name, spec]) => ` ${name.padEnd(nameWidth)} ${spec.purpose}`),
|
|
215
213
|
].join("\n");
|
|
216
214
|
}
|
|
217
215
|
function renderTaskUsage(action) {
|
|
@@ -387,8 +385,12 @@ function validateListSelector(selector, fail) {
|
|
|
387
385
|
}
|
|
388
386
|
function validateTaskScan(action, scanned, fail) {
|
|
389
387
|
const spec = TASK_COMMAND_SPECS[action], { positionals, flags, stdin } = scanned;
|
|
390
|
-
if (positionals.length < spec.arity[0]
|
|
391
|
-
fail(`task ${action}
|
|
388
|
+
if (positionals.length < spec.arity[0])
|
|
389
|
+
fail(spec.arity[1] === 1 ? `task ${action} requires a TaskId` : `task ${action} requires at least one TaskId`);
|
|
390
|
+
if (positionals.length > spec.arity[1])
|
|
391
|
+
fail(spec.arity[1] === 0
|
|
392
|
+
? `task ${action} accepts no positional arguments`
|
|
393
|
+
: `task ${action} accepts at most ${spec.arity[1]} positional argument${spec.arity[1] === 1 ? "" : "s"}`);
|
|
392
394
|
if (action === "add" && (stdin === "document") === (positionals.length === 1))
|
|
393
395
|
fail("task add requires either TITLE or '-' input");
|
|
394
396
|
if (action === "add" &&
|
package/build/src/cli/parse.js
CHANGED
|
@@ -294,6 +294,11 @@ export function parseArgv(argv) {
|
|
|
294
294
|
const help = helpCoordinate(invocation.commandArgv);
|
|
295
295
|
if (help !== null)
|
|
296
296
|
return { help };
|
|
297
|
+
// A bare invocation asks for orientation: the root help index, not a refusal.
|
|
298
|
+
if (invocation.commandArgv.length === 0)
|
|
299
|
+
return { help: { kind: "root" } };
|
|
300
|
+
if (invocation.commandArgv.length === 1 && invocation.commandArgv[0] === "task")
|
|
301
|
+
return { help: { kind: "task" } };
|
|
297
302
|
if (invocation.commandArgv.length === 1 && invocation.commandArgv[0] === "--version")
|
|
298
303
|
return { version: true };
|
|
299
304
|
const task = invocation.commandArgv[0] === "task" ? parseTaskCommand(invocation.commandArgv.slice(1)) : undefined;
|