@oneie/claude 0.6.0 → 0.7.0

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 (111) hide show
  1. package/agents/w1-recon.md +9 -4
  2. package/agents/w2-decide.md +51 -28
  3. package/agents/w3-edit.md +70 -78
  4. package/agents/w4-verify.md +164 -69
  5. package/commands/browser.md +98 -35
  6. package/commands/cc-connect.md +6 -0
  7. package/commands/chat.md +13 -1
  8. package/commands/close.md +45 -19
  9. package/commands/create.md +20 -10
  10. package/commands/db-sync.md +5 -1
  11. package/commands/deploy.md +174 -87
  12. package/commands/do-autonomous.md +31 -20
  13. package/commands/do-improve.md +4 -4
  14. package/commands/do-show.md +4 -3
  15. package/commands/do.md +53 -13
  16. package/commands/go.md +75 -0
  17. package/commands/improve.md +4 -2
  18. package/commands/kill.md +6 -8
  19. package/commands/notify.md +15 -2
  20. package/commands/oo-push.md +24 -9
  21. package/commands/rag.md +6 -0
  22. package/commands/release.md +46 -25
  23. package/commands/restart.md +5 -4
  24. package/commands/see.md +45 -35
  25. package/commands/skill-create.md +38 -5
  26. package/commands/sync.md +21 -11
  27. package/hooks/scripts/auto-continue.sh +70 -0
  28. package/hooks/scripts/branch-pin.sh +85 -0
  29. package/hooks/scripts/git-add-guard.sh +16 -5
  30. package/hooks/scripts/post-edit-check.sh +16 -0
  31. package/hooks/scripts/session-start.sh +13 -8
  32. package/hooks/scripts/sync-priority-todo.sh +5 -0
  33. package/package.json +1 -1
  34. package/rules/api.md +9 -0
  35. package/rules/design.md +13 -3
  36. package/rules/documentation.md +30 -71
  37. package/rules/engine.md +8 -1
  38. package/rules/ui.md +5 -1
  39. package/scripts/__pycache__/do-rank.cpython-313.pyc +0 -0
  40. package/scripts/browser-check.mjs +44 -103
  41. package/scripts/cc-events-proof.sh +13 -12
  42. package/scripts/chrome.mjs +381 -0
  43. package/scripts/do-analyze.sh +109 -4
  44. package/scripts/do-auto.sh +350 -43
  45. package/scripts/do-consumer-sweep.sh +72 -0
  46. package/scripts/do-derives-check.sh +88 -0
  47. package/scripts/do-fleet.sh +50 -1
  48. package/scripts/do-killswitch-audit.py +33 -14
  49. package/scripts/do-promise-lint.sh +150 -4
  50. package/scripts/do-promise-settle.sh +131 -9
  51. package/scripts/do-prove.sh +197 -17
  52. package/scripts/do-rank.py +606 -20
  53. package/scripts/do-rubric.py +21 -2
  54. package/scripts/do-signal.sh +157 -17
  55. package/scripts/do-smoke.sh +9 -0
  56. package/scripts/do-substrate-check.sh +49 -0
  57. package/scripts/do-tasks-wire-check.sh +81 -0
  58. package/scripts/do-tier.sh +90 -5
  59. package/scripts/do-ui-gate.sh +332 -0
  60. package/scripts/do-untracked-gate.sh +231 -0
  61. package/scripts/do-walk.sh +344 -0
  62. package/scripts/factory-check.sh +747 -0
  63. package/scripts/factory-repo.sh +1361 -0
  64. package/scripts/factory-tasks-check.sh +85 -0
  65. package/scripts/outcome-pull.ts +279 -0
  66. package/scripts/promise-manifest.mjs +167 -0
  67. package/scripts/reconcile-allow.txt +5 -0
  68. package/scripts/rubric-weights.json +64 -0
  69. package/scripts/spine-canary.sh +20 -0
  70. package/scripts/typedb-cluster-status.sh +128 -0
  71. package/scripts/typedb-env.sh +182 -0
  72. package/scripts/typedb-flap-recorder.sh +85 -0
  73. package/scripts/typedb-probes/containment-probe.py +88 -0
  74. package/scripts/typedb-probes/panic-probe-version.py +92 -0
  75. package/scripts/typedb-probes/panic-probe.py +77 -0
  76. package/scripts/typedb-scratch.sh +73 -0
  77. package/scripts/urls-lint.sh +58 -0
  78. package/scripts/w4-rubric.ts +96 -12
  79. package/scripts/wf-check.mjs +75 -0
  80. package/skills/ai-ui/SKILL.md +1 -1
  81. package/skills/astro/SKILL.md +177 -132
  82. package/skills/astro/add-content-collection.md +29 -13
  83. package/skills/astro/check-build.md +19 -13
  84. package/skills/astro/create-component.md +36 -9
  85. package/skills/astro/create-page.md +39 -10
  86. package/skills/astro/optimize-performance.md +36 -8
  87. package/skills/cli/SKILL.md +254 -220
  88. package/skills/dev/SKILL.md +1 -1
  89. package/skills/directory-autofill/SKILL.md +68 -0
  90. package/skills/docs/SKILL.md +4 -4
  91. package/skills/mcp/SKILL.md +186 -156
  92. package/skills/promise-make/SKILL.md +2 -2
  93. package/skills/puck/SKILL.md +465 -250
  94. package/skills/react19/SKILL.md +196 -296
  95. package/skills/reactflow/SKILL.md +387 -412
  96. package/skills/sdk/SKILL.md +167 -106
  97. package/skills/shadcn/SKILL.md +245 -201
  98. package/skills/signal/SKILL.md +5 -0
  99. package/skills/sui/SKILL.md +6 -1
  100. package/skills/tutorial/SKILL.md +15 -0
  101. package/skills/typedb/SKILL.md +427 -217
  102. package/skills/typedb/examples/python-patterns.py +11 -4
  103. package/skills/typedb/examples/query-patterns.tql +8 -3
  104. package/skills/typedb/examples/schema-patterns.tql +4 -2
  105. package/skills/typedb/reference/migration-2x-3x.md +19 -1
  106. package/skills/typedb/reference/python-driver.md +17 -1
  107. package/skills/voice/SKILL.md +117 -0
  108. package/skills/writer/SKILL.md +29 -12
  109. package/templates/template-feature.md +26 -1
  110. package/templates/template-tests.md +4 -0
  111. package/templates/template-todo.md +65 -10
@@ -1,318 +1,352 @@
1
+ ---
2
+ name: cli
3
+ description: Add or change a command in @oneie/cli — the operator CLI shipped as the `oneie` and `one` bins. Use when adding a CLI verb or command group, editing packages/cli/src/*.ts, wiring a command to a receiver through ask(), closing the loop with out(cmd, data), handling --json output, or resolving the API base URL and key. Triggers — "add a CLI command", "new `one` verb", "oneie <group> <verb>", "make the command output JSON", "which host does --api point at", "one fn / one agent / one push is doing X".
4
+ ---
5
+
1
6
  # CLI verbs — operator commands via @oneie/cli
2
7
 
3
- CLI verbs let operators interact with ONE from the terminal. They're discoverable, typed, and chain with Unix pipes.
8
+ CLI verbs let operators interact with ONE from the terminal. Every substrate
9
+ command is a thin wrapper over a receiver — no new HTTP routes.
4
10
 
5
11
  ## Structure
6
12
 
7
13
  ```
8
14
  packages/cli/
9
15
  ├── src/
10
- │ ├── cli.ts # CLI entrypoint (parsing, routing)
11
- │ ├── commands/ # verb implementations
12
- │ │ ├── index.ts # command registry
13
- │ │ ├── memory/
14
- │ │ │ ├── index.ts
15
- │ │ │ ├── read.ts
16
- │ │ ├── write.ts
17
- │ │ └── delete.ts
18
- │ │ └── {domain}/
19
- │ │ ├── index.ts
20
- └── {verb}.ts
21
- └── utils/ # parsing, formatting, logging
16
+ │ ├── index.ts # builds `program`, addCommand/group
17
+ │ ├── <group>.ts # one file per group → <group>Cmd()
18
+ │ │ # agent · skill · auth · dev · group
19
+ │ │ # social · connect · broadcast · chat
20
+ │ │ # links · staff · wallet · push · fn
21
+ │ │ # deploy · ship · workflow · plugin …
22
+ │ ├── substrate.ts # 6 verbs + dim reads
23
+ ├── lib/
24
+ │ │ ├── client.ts # resolveBase/Key, out, jsonMode
25
+ │ │ └── api-path.ts # askPath() — gateway vs web
26
+ │ └── templates/
27
+ └── tests/ # vitest registration + behaviour
22
28
  ```
23
29
 
30
+ There is no `src/cli.ts` and no `src/commands/` directory — a command group is
31
+ one flat file at `src/<group>.ts`. Bins: `oneie` and `one`, both
32
+ `dist/src/index.js`. Runtime deps are exactly two: `commander` + `@oneie/sdk`.
33
+
24
34
  ## Pattern: Adding a CLI verb
25
35
 
26
36
  ### 1. Define the command shape
27
37
 
28
38
  ```typescript
29
- // packages/cli/src/commands/memory/read.ts
30
- import { Command } from 'commander';
31
- import { SubstrateClient } from '@oneie/sdk';
32
-
33
- export function createReadMemoryCommand(): Command {
34
- return new Command('read')
35
- .description('Read group memory')
36
- .argument('<group-id>', 'Group ID')
37
- .option('-f, --format <fmt>', 'Output format: json, text, yaml', 'text')
38
- .action(async (groupId, options) => {
39
- try {
40
- const client = new SubstrateClient();
41
- const memory = await client.memory(groupId);
42
-
43
- if (!memory) {
44
- console.log('No memory found for this group.');
45
- return;
46
- }
47
-
48
- switch (options.format) {
49
- case 'json':
50
- console.log(JSON.stringify(memory, null, 2));
51
- break;
52
- case 'yaml':
53
- console.log(YAML.stringify(memory));
54
- break;
55
- default:
56
- console.log(`Memory for ${groupId}:\n${memory.content}`);
57
- }
58
- } catch (error) {
59
- console.error(`Error: ${error.message}`);
60
- process.exit(1);
61
- }
62
- });
39
+ // packages/cli/src/links.ts
40
+ import { Command } from 'commander'
41
+ import { authHeaders, resolveBase } from './lib/client.js'
42
+ import { askPath } from './lib/api-path.js'
43
+
44
+ const DEFAULT_API = resolveBase({}, 'https://api.one.ie')
45
+
46
+ async function ask(api: string, receiver: string, data: unknown): Promise<unknown> {
47
+ const res = await fetch(askPath(api.replace(/\/$/, ''), receiver), {
48
+ method: 'POST',
49
+ headers: authHeaders(),
50
+ body: JSON.stringify({ data }),
51
+ })
52
+ if (!res.ok) {
53
+ process.stderr.write(`POST /api/ask/${receiver} → ${res.status}\n`)
54
+ process.exit(1)
55
+ }
56
+ return res.json()
57
+ }
58
+
59
+ export function linksCmd(): Command {
60
+ const cmd = new Command('links').description('Create actor-bound tracked links')
61
+
62
+ cmd
63
+ .command('create <actorId>')
64
+ .description('Create a tracked link bound to a contact')
65
+ .option('--to <path>', "Destination path, e.g. '/pricing'", '/')
66
+ .option('--expires <days>', 'Link TTL in days (omit = never)')
67
+ .option('--api <url>', 'API base URL', DEFAULT_API)
68
+ .action(async (actorId: string, opts: { to: string; expires?: string; api: string }) => {
69
+ out(await ask(opts.api, 'links:create', {
70
+ actorId,
71
+ destination: opts.to,
72
+ expiresInDays: opts.expires ? Number(opts.expires) : undefined,
73
+ }))
74
+ })
75
+
76
+ return cmd
63
77
  }
64
78
  ```
65
79
 
66
- ### 2. Register the command
80
+ **Rules:**
81
+ - Build the path with `askPath(base, receiver)`. The gateway (`api.one.ie`)
82
+ serves `/ask/<receiver>`; `one.ie/web` serves `/api/ask/<receiver>`. Hardcoding
83
+ either shape 404s against the other host
84
+ - `--api` default is per-module, not global. Verified by
85
+ `grep -n "api.one.ie\|https://one.ie" packages/cli/src/*.ts`: gateway-facing
86
+ (`https://api.one.ie`) are `broadcast`, `links`, `substrate`, `wallet`,
87
+ `catalog`, `trade`, `fn`, `coordinate`, `client`, `push`, `setup`; web-facing
88
+ (`https://one.ie`) are `chat`, `connect`, `group`, `social`, `trail`, `staff`,
89
+ `auth`, `init`, `onboard`, `status`, `skill`, `plugin`. Read the module's own
90
+ `DEFAULT_API` before assuming — some call `resolveBase({}, fallback)` and some
91
+ read `ONEIE_BASE_URL`/`ONE_API_URL` inline
92
+ - The option name determines the property: `--to` reads as `opts.to`,
93
+ `--batch-size` as `opts.batchSize`. Map to the receiver's field names explicitly
94
+ - Target an existing receiver or `/api/*` route — never add a new endpoint
95
+
96
+ ### 2. Close the loop with `out()`
97
+
98
+ `out(cmd, data)` from `lib/client.ts` is the canonical closer: human-readable
99
+ `ok k=v k=v` by default, one line of JSON under `--json`, `ok: false` routed to
100
+ stderr with exit code 1.
67
101
 
68
102
  ```typescript
69
- // packages/cli/src/commands/memory/index.ts
70
- import { Command } from 'commander';
71
- import { createReadMemoryCommand } from './read';
72
- import { createWriteMemoryCommand } from './write';
103
+ import { out, resolveBase, resolveKey } from './lib/client.js'
73
104
 
74
- export function createMemoryCommand(): Command {
75
- const memory = new Command('memory')
76
- .description('Manage group memory');
77
-
78
- memory.addCommand(createReadMemoryCommand());
79
- memory.addCommand(createWriteMemoryCommand());
80
-
81
- return memory;
82
- }
83
-
84
- // packages/cli/src/commands/index.ts
85
- export { createMemoryCommand } from './memory';
86
- export { createTaskCommand } from './task';
87
- // other commands
105
+ .action(async (opts: { csv: string; api: string }) => {
106
+ const key = resolveKey()
107
+ if (!key) {
108
+ out(cmd, { ok: false, error: 'no API key — set ONE_API_KEY or run: oneie auth login' })
109
+ return
110
+ }
111
+ out(cmd, { ok: true, created: 12, skipped: 3 })
112
+ })
88
113
  ```
89
114
 
90
- ### 3. Wire into main CLI
115
+ `jsonMode(cmd)` walks `cmd.parent` to the root, so `--json` works wherever the
116
+ operator puts it. Some older groups (`links`, `broadcast`) still use a local
117
+ `out(data)` that always pretty-prints JSON — new commands use `out(cmd, data)`.
91
118
 
92
- ```typescript
93
- // packages/cli/src/cli.ts
94
- import { Command } from 'commander';
95
- import { createMemoryCommand } from './commands';
119
+ ### 3. Register the command
96
120
 
97
- export function createCli(): Command {
98
- const program = new Command()
99
- .name('one')
100
- .description('ONE operator CLI')
101
- .version('1.0.0');
121
+ ```typescript
122
+ // packages/cli/src/index.ts
123
+ import { linksCmd } from './links.js'
102
124
 
103
- program.addCommand(createMemoryCommand());
104
- program.addCommand(createTaskCommand());
105
- // other commands
125
+ const program = new Command()
126
+ .name('oneie')
127
+ .description('ONE agent CLI')
128
+ .version(pkg.version)
129
+ .option('--json', 'Output structured JSON')
106
130
 
107
- return program;
108
- }
109
-
110
- // bin/one (executable)
111
- #!/usr/bin/env node
112
- const cli = createCli();
113
- cli.parse(process.argv);
131
+ program.addCommand(linksCmd())
114
132
  ```
115
133
 
134
+ Groups that register several top-level commands export a `<group>Commands(program)`
135
+ function instead (`substrateCommands`, `catalogCommands`, `tradeCommands`,
136
+ `coordinateCommands`). Relative imports carry the `.js` extension — ESM.
137
+
116
138
  ### 4. Test the command
117
139
 
140
+ Registration tests are cheap and catch the common regression (a subcommand
141
+ silently dropped):
142
+
118
143
  ```typescript
119
- // packages/cli/test/memory.read.test.ts
120
- import { execSync } from 'child_process';
121
-
122
- describe('one memory read', () => {
123
- it('returns memory for a group', () => {
124
- const result = execSync('one memory read group-123 --format json').toString();
125
- const memory = JSON.parse(result);
126
- expect(memory).toHaveProperty('content');
127
- });
128
-
129
- it('shows error when group not found', () => {
130
- expect(() => {
131
- execSync('one memory read nonexistent');
132
- }).toThrow();
133
- });
134
-
135
- it('supports multiple output formats', () => {
136
- const json = execSync('one memory read group-123 --format json').toString();
137
- const yaml = execSync('one memory read group-123 --format yaml').toString();
138
- expect(json).toContain('{');
139
- expect(yaml).not.toContain('{');
140
- });
141
- });
144
+ // packages/cli/tests/workflow.test.ts
145
+ import { describe, it, expect } from 'vitest'
146
+ import { workflowCmd } from '../src/workflow.js'
147
+
148
+ describe('workflow CLI registration', () => {
149
+ it('registers list/pull/push/run/validate/resolve/logs subcommands', () => {
150
+ const names = workflowCmd().commands.map((c) => c.name()).sort()
151
+ expect(names).toEqual(['list', 'logs', 'pull', 'push', 'resolve', 'run', 'validate'])
152
+ })
153
+ })
142
154
  ```
143
155
 
156
+ **Rules:**
157
+ - `bun run test` in `packages/cli` (vitest)
158
+ - Import the `<group>Cmd()` factory and assert on the commander tree — don't
159
+ `execSync` the bin; the built `dist/` may be stale and the call hits the network
160
+ - For behaviour, call the action's collaborators directly (see
161
+ `tests/doctor.test.ts`, `tests/setup-keyless.test.ts`)
162
+
144
163
  ## Naming conventions
145
164
 
146
- **Command structure:** `one {domain} {verb} [args]`
147
- - `one memory read <group-id>`
148
- - `one task create --title "..."`
149
- - `one actor invite <email>`
150
- - `one -m read-group-memory` (unclear)
151
- - `one memory --read <group-id>` (verb is option, not subcommand)
165
+ **Command structure:** `one {group} {verb} [args]`
166
+ - Correct: `one links create <actorId>`
167
+ - Correct: `one broadcast send <id>`
168
+ - Correct: `one workflow pull <id> --slug acme`
169
+ - Wrong: `one -l create-link` (unclear)
170
+ - Wrong: `one links --create <actorId>` (verb is an option, not a subcommand)
171
+
172
+ Single-purpose groups stay flat as top-level commands — `one whoami`, `one doctor`,
173
+ `one status`, `one trail <tags>`, `one signal <receiver>`.
152
174
 
153
175
  **Verb names:** imperative, no "get"
154
- - `read`, `create`, `update`, `delete`, `list`, `send`, `approve`
155
- - `get-memory`, `CREATE_MEMORY`, `mem_list` (inconsistent)
176
+ - Correct: `create`, `list`, `send`, `pull`, `push`, `run`, `validate`, `approve`
177
+ - Wrong: `get-broadcast`, `CREATE_LINK`, `bc_list`
156
178
 
157
179
  **Arguments and options:**
158
- - Arguments: positional, required, no dashes
159
- - `one memory read <group-id>`
160
- - Options: named, optional, with dashes
161
- - `--format json`, `-f json`
180
+ - Arguments: positional, required, no dashes — `one broadcast send <id>`
181
+ - Options: named, optional, with dashes — `--to /pricing`, `--limit 20`
182
+ - `--api <url>` and `--key <k>` are the two conventional overrides; both fall
183
+ through to `resolveBase()` / `resolveKey()` when omitted
162
184
 
163
185
  ## Command categories
164
186
 
165
187
  | Category | Verbs | Purpose |
166
188
  |----------|-------|---------|
167
- | **Read** | `read`, `list`, `show` | Display data without side effects |
168
- | **Create** | `create`, `add`, `init` | Create new entity |
169
- | **Update** | `update`, `edit`, `set` | Modify existing entity |
170
- | **Delete** | `delete`, `remove`, `rm` | Remove entity (requires confirmation) |
171
- | **Execute** | `send`, `approve`, `run` | Action with side effects |
189
+ | **Read** | `list`, `get`, `status`, `whoami`, `doctor` | Display data without side effects |
190
+ | **Create** | `create`, `new`, `init`, `add`, `scaffold` | Create new entity |
191
+ | **Update** | `push`, `set`, `rename`, `refresh` | Modify existing entity |
192
+ | **Delete** | `unpublish`, `unimport`, `logout` | Remove entity |
193
+ | **Execute** | `send`, `run`, `publish`, `deploy`, `eval` | Action with side effects |
172
194
 
173
195
  ## Output format
174
196
 
175
- **Default (text):** human-readable, one result per line
197
+ **Default (human):** one `ok` line of key=value pairs, from `out(cmd, data)`
176
198
 
177
199
  ```
178
- $ one task list
179
- ID Title Status
180
- task-1 Review PR open
181
- task-2 Deploy v1.2 in-progress
182
- task-3 Write docs done
200
+ $ one group bulk-create --csv clients.csv
201
+ batch 1/1 created=12 skipped=3
202
+ ok total_rows=15 created=12 duplicates=3 batches=1 plan="studio" segment="ICP-1" dry_run=false
183
203
  ```
184
204
 
185
- **JSON:** machine-readable, suitable for piping
205
+ Values are `JSON.stringify`d, so strings keep their quotes and `ok` itself is
206
+ dropped from the human line.
186
207
 
187
- ```
188
- $ one task list --format json
189
- [
190
- {"id": "task-1", "title": "Review PR", "status": "open"},
191
- {"id": "task-2", "title": "Deploy v1.2", "status": "in-progress"},
192
- {"id": "task-3", "title": "Write docs", "status": "done"}
193
- ]
194
- ```
195
-
196
- **CSV:** for spreadsheet import
208
+ **`--json`:** one line of structured JSON, suitable for piping
197
209
 
198
210
  ```
199
- $ one task list --format csv
200
- id,title,status
201
- task-1,Review PR,open
202
- task-2,Deploy v1.2,in-progress
203
- task-3,Write docs,done
211
+ $ one group bulk-create --csv clients.csv --json
212
+ {"ok":true,"total_rows":15,"created":12,"duplicates":3,"batches":1,"plan":"studio","segment":"ICP-1","dry_run":false}
204
213
  ```
205
214
 
215
+ `--json` is a global option declared on `program`, and `jsonMode()` finds it at
216
+ any depth. There is no `--format` flag and no YAML or CSV output — `--json` plus
217
+ `jq` is the whole contract. An uncaught error also honours it: `index.ts` prints
218
+ `{"ok":false,"error":"…"}` to stdout under `--json`, the message to stderr
219
+ otherwise, and exits 1.
220
+
206
221
  ## Anti-patterns
207
222
 
208
- **Verb as an option**
223
+ **Verb as an option**
209
224
  ```bash
210
225
  # WRONG
211
- one memory --read group-123
212
- one memory --write group-123 "content"
226
+ one links --create u-123
213
227
  ```
214
228
  **Fix:** Verbs are subcommands, not options.
215
229
  ```bash
216
- one memory read group-123
217
- one memory write group-123 --content "content"
230
+ one links create u-123 --to /pricing
218
231
  ```
219
232
 
220
- **No error handling**
233
+ **Hardcoding the ask path**
221
234
  ```typescript
222
- // WRONG
223
- program.action(async (groupId) => {
224
- const memory = await client.memory(groupId);
225
- console.log(memory); // crashes if error
226
- });
235
+ // WRONG — 404s whenever --api points at the gateway
236
+ await fetch(`${api}/api/ask/links:create`, { … })
227
237
  ```
228
- **Fix:** Wrap in try-catch, exit with code 1.
238
+ **Fix:** `askPath(base, 'links:create')` picks the form by hostname.
239
+
240
+ **Hardcoding the base URL or reading the key inline**
229
241
  ```typescript
230
- program.action(async (groupId) => {
231
- try {
232
- const memory = await client.memory(groupId);
233
- console.log(memory);
234
- } catch (error) {
235
- console.error(`Error: ${error.message}`);
236
- process.exit(1);
237
- }
238
- });
242
+ // WRONG
243
+ const api = 'https://one.ie'
244
+ const key = process.env.ONE_API_KEY
239
245
  ```
246
+ **Fix:** `resolveBase(opts, fallback)` and `resolveKey(opts)` — they encode the
247
+ `--flag > canonical env > legacy env > ~/.config/oneie/key` ladder.
240
248
 
241
- **Unformatted output**
242
- ```bash
243
- $ one task list
244
- Task { id: 'task-1', title: 'Review PR', status: TaskStatus { ... } }
249
+ **Silent return**
250
+ ```typescript
251
+ // WRONG
252
+ program.action(async (id) => {
253
+ const r = await ask(api, 'broadcast:send', { broadcastId: id })
254
+ console.log(r) // no ok/error shape, no exit code, breaks --json
255
+ })
245
256
  ```
246
- **Fix:** Format output for human readability or JSON.
257
+ **Fix:** Close the loop with `out(cmd, { ok, ...data })` — the locked closed-loop rule.
247
258
 
248
- **No help text**
259
+ **No help text**
249
260
  ```typescript
250
261
  // WRONG
251
- new Command('read')
252
- .argument('<group-id>')
253
- .action(async (groupId) => { ... });
262
+ new Command('create')
263
+ .argument('<actorId>')
264
+ .action(async (actorId) => { /* … */ })
254
265
  ```
255
- **Fix:** Add description and option help.
266
+ **Fix:** Add a description to the command and to every argument and option.
256
267
  ```typescript
257
- new Command('read')
258
- .description('Read group memory')
259
- .argument('<group-id>', 'ID of the group')
260
- .option('-f, --format <fmt>', 'Output format (json, text, yaml)', 'text')
261
- .action(async (groupId, options) => { ... });
268
+ new Command('create')
269
+ .description('Create a tracked link bound to a contact')
270
+ .argument('<actorId>', 'Contact the link is bound to')
271
+ .option('--to <path>', "Destination path, e.g. '/pricing'", '/')
262
272
  ```
263
273
 
274
+ **Adding a dependency**
275
+ ```typescript
276
+ // WRONG — breaks `npm i -g @oneie/cli` / `npx oneie`
277
+ import YAML from 'yaml'
278
+ import { z } from 'zod'
279
+ ```
280
+ **Fix:** Runtime deps are `commander` + `@oneie/sdk`, full stop. Hand-rolled
281
+ YAML/Markdown parsers live in the SDK; `@oneie/evals` is a devDependency on purpose.
282
+
264
283
  ## Composability rules
265
284
 
266
- **New verb composes SDK methods:**
285
+ **New verb wraps a receiver:**
267
286
  ```typescript
268
- // GOOD — CLI wraps SDK
269
- export function createMemoryReadCommand(): Command {
270
- return new Command('read')
271
- .argument('<group-id>')
272
- .action(async (groupId) => {
273
- const client = new SubstrateClient();
274
- const memory = await client.memory(groupId); // SDK handles all logic
275
- console.log(formatMemory(memory)); // only formatting here
276
- });
277
- }
287
+ // GOOD — the receiver owns authority, validation, and persistence
288
+ cmd
289
+ .command('list')
290
+ .description('List broadcasts for the workspace')
291
+ .option('--limit <n>', 'Max results', '20')
292
+ .action(async (opts: { limit: string; api: string }) => {
293
+ out(await ask(opts.api, 'broadcast:list', { limit: Number(opts.limit) }))
294
+ })
278
295
  ```
279
296
 
280
- **New verb creates novel capability:**
297
+ **New verb composes two receivers:**
281
298
  ```typescript
282
- // GOOD — operator-specific logic
283
- export function createMemoryAnalyzeCommand(): Command {
284
- return new Command('analyze')
285
- .argument('<group-id>')
286
- .action(async (groupId) => {
287
- const client = new SubstrateClient();
288
- const memory = await client.memory(groupId);
289
- // Analysis is operator-specific, not SDK
290
- const sentiment = analyzeSentiment(memory.content);
291
- console.log(`Memory sentiment: ${sentiment}`);
292
- });
299
+ // GOOD — segment preview needs the definition, not the id, so the CLI
300
+ // fetches it first and fails loudly if it is missing
301
+ const got = await ask(opts.api, 'segment:get', { id }) as { segment?: { definition?: unknown } }
302
+ if (!got.segment) {
303
+ process.stderr.write(`segment:get returned no segment for id=${id}\n`)
304
+ process.exit(1)
293
305
  }
306
+ out(await ask(opts.api, 'segment:preview', { definition: got.segment.definition }))
307
+ ```
308
+
309
+ **New verb is generated from a catalog:**
310
+ ```typescript
311
+ // GOOD — `one fn list` / `one fn run <name>` enumerate FN_ALLOWLIST from
312
+ // @oneie/sdk/fn-allowlist (20 entries today) and route through ask('fn:run').
313
+ // Allowlisting a fun there reaches web, MCP, CLI, and channels in one edit.
294
314
  ```
295
315
 
296
316
  ## Piping and composability
297
317
 
298
- CLI verbs should output JSON (with `--format json`) so they can chain:
318
+ Commands that emit JSON chain through `jq`. Receiver wrappers print the `/api/ask`
319
+ envelope verbatim — `{ outcome, result, signalId, receiver }` — so the receiver's
320
+ own response sits one level down, under `.result`:
299
321
 
300
322
  ```bash
301
- # List all groups, extract IDs, read memory for each
302
- one group list --format json | jq -r '.[] | .id' | xargs -I {} one memory read {}
303
-
304
- # Find tasks, filter by status, format as CSV
305
- one task list --format json | jq '.[] | select(.status == "open")' | jq -s . | one format-csv
323
+ # List broadcasts, keep the drafts, send each one
324
+ one ask broadcast:list --data '{"workspace":"acme","limit":50}' \
325
+ | jq -r '.result.broadcasts[] | select(.status=="draft") | .id' \
326
+ | xargs -I {} one broadcast send {}
306
327
  ```
307
328
 
329
+ `validateReceiver` strict-parses the payload against the receiver's zod `request`
330
+ before dispatch, so every required field must be in `--data` — omitting
331
+ `workspace` here is a 400, not a default.
332
+
333
+ Check the group's own `out()` before writing a pipeline: `out(cmd, data)` gives
334
+ one line under `--json` and `ok k=v` otherwise, the local `out(data)` in `links`
335
+ and `broadcast` pretty-prints JSON either way, and a few commands (`whoami`,
336
+ `doctor`) print fixed human lines with no JSON mode at all.
337
+
308
338
  **Rules:**
309
- - Default output is human-readable (text)
310
- - `--format json` outputs valid JSON (parseable by `jq`)
311
- - One result per line in JSON array
312
- - Errors go to stderr, exit code 1
339
+ - Default output is human-readable (`ok k=v`); `--json` is opt-in
340
+ - `--json` output is valid JSON parseable by `jq`
341
+ - Errors go to stderr with exit code 1; under `--json` the error object goes to
342
+ stdout so a pipeline can read it
343
+ - Never print a secret. `one wallet keygen` prints once and never writes a file
344
+ without `--yes-i-accept-plaintext-file`; `one connect` never echoes the token
313
345
 
314
346
  ## See also
315
347
 
316
- - `packages/cli/CLAUDE.md` — CLI development guide
317
- - `packages/sdk/src/receivers.ts` — receiver registry (CLI calls receivers via SDK)
318
- - `text/cli-reference.md` — current CLI commands and roadmap
348
+ - `packages/cli/CLAUDE.md` — the full command map, conventions, and distribution
349
+ - `packages/cli/src/lib/client.ts` — `resolveBase` / `resolveKey` / `out` / `jsonMode`
350
+ - `packages/sdk/src/receivers.ts` — receiver registry (every substrate verb wraps one)
351
+ - `text/cli.md` — the promise: who this CLI is for
352
+ - `text/cli-reference.md` — the command reference written for external readers
@@ -42,7 +42,7 @@ lsof -i :4321
42
42
  ### Kill existing server
43
43
 
44
44
  ```bash
45
- pkill -f "astro dev"
45
+ lsof -ti:4321 | xargs kill # port-scoped — a bare `pkill -f "astro dev"` kills every Astro on the machine, including other projects and sessions
46
46
  ```
47
47
 
48
48
  ### Run on different port