@ia-ccun/code-agent-cli 0.0.52 → 0.0.53-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/cli.js +6 -2
- package/config/agent/bin/README.md +102 -0
- package/config/agent/extensions/aicode-advisor.ts +13 -0
- package/config/agent/extensions/aicode-btw.ts +66 -14
- package/config/agent/extensions/aicode-cd.ts +834 -0
- package/config/agent/extensions/aicode-grillMe.ts +198 -0
- package/config/agent/extensions/aicode-pets.ts +362 -0
- package/config/agent/extensions/aicode-safety-guard.ts +552 -0
- package/config/agent/extensions/aicode-tips.ts +70 -16
- package/config/agent/extensions/aicode-todo-progress.ts +547 -0
- package/config/agent/extensions/custom.ts +93 -95
- package/config/agent/extensions/error-monitor.ts +1 -18
- package/config/agent/extensions/ponytail.ts +441 -0
- package/config/agent/extensions/tps.ts +87 -0
- package/config/agent/extensions/working-msg.ts +165 -50
- package/config/agent/help.md +0 -7
- package/config/agent/models.json +7 -7
- package/config/agent/settings.json +2 -1
- package/config/agent/skills/chroma/LICENSE +21 -0
- package/config/agent/skills/chroma/chroma-local/SKILL.md +165 -0
- package/config/agent/skills/chroma/chroma-local/cli.md +48 -0
- package/config/agent/skills/chroma/chroma-local/data-model.md +103 -0
- package/config/agent/skills/chroma/chroma-local/error-handling/python.md +166 -0
- package/config/agent/skills/chroma/chroma-local/error-handling/typescript.md +231 -0
- package/config/agent/skills/chroma/chroma-local/local-chroma/python.md +95 -0
- package/config/agent/skills/chroma/chroma-local/local-chroma/typescript.md +101 -0
- package/config/agent/skills/chroma/chroma-local/metadata/python.md +154 -0
- package/config/agent/skills/chroma/chroma-local/metadata/typescript.md +153 -0
- package/config/agent/skills/chroma/chroma-local/querying/python.md +147 -0
- package/config/agent/skills/chroma/chroma-local/querying/typescript.md +157 -0
- package/config/agent/skills/chroma/chroma-local/regex/python.md +69 -0
- package/config/agent/skills/chroma/chroma-local/regex/typescript.md +71 -0
- package/config/agent/skills/chroma/chroma-local/understanding-a-codebase.md +80 -0
- package/config/agent/skills/chroma/chroma-local/updating-deleting/python.md +159 -0
- package/config/agent/skills/chroma/chroma-local/updating-deleting/typescript.md +228 -0
- package/config/agent/skills/milvus-skill/LICENSE +202 -0
- package/config/agent/skills/milvus-skill/README.md +37 -0
- package/config/agent/skills/milvus-skill/SKILL.md +259 -0
- package/config/agent/skills/milvus-skill/references/collection.md +86 -0
- package/config/agent/skills/milvus-skill/references/database.md +24 -0
- package/config/agent/skills/milvus-skill/references/index.md +69 -0
- package/config/agent/skills/milvus-skill/references/partition.md +29 -0
- package/config/agent/skills/milvus-skill/references/patterns.md +150 -0
- package/config/agent/skills/milvus-skill/references/user-role.md +90 -0
- package/config/agent/skills/milvus-skill/references/vector.md +247 -0
- package/config/agent/skills/ponytail/SKILL.md +100 -0
- package/config/agent/skills/ponytail-audit/SKILL.md +40 -0
- package/config/agent/skills/ponytail-debt/SKILL.md +44 -0
- package/config/agent/skills/ponytail-help/SKILL.md +68 -0
- package/config/agent/skills/ponytail-review/SKILL.md +56 -0
- package/config/agent/skills/zread/README.md +115 -0
- package/config/agent/skills/zread/SKILL.md +103 -0
- package/config/agent/skills/zread/references/stdio-protocol.md +312 -0
- package/package.json +7 -6
- package/scripts/postinstall.js +58 -1
- package/scripts/regression/README.md +224 -0
- package/scripts/regression/cases/01-version-and-deps.sh +94 -0
- package/scripts/regression/cases/02-extension-loading.sh +130 -0
- package/scripts/regression/cases/03-rpc-protocol.sh +259 -0
- package/scripts/regression/cases/04-prompt-stream.sh +130 -0
- package/scripts/regression/cases/05-slash-commands-and-skills.sh +119 -0
- package/scripts/regression/cases/06-tool-execution.sh +115 -0
- package/scripts/regression/cases/07-pi-new-features.sh +141 -0
- package/scripts/regression/lib/common.sh +287 -0
- package/scripts/regression/results/.last_pid +1 -0
- package/scripts/regression/results/.last_workdir +1 -0
- package/scripts/regression/results/02-extension-loading.log +29 -0
- package/scripts/regression/results/03-prompt-stream.log +25 -0
- package/scripts/regression/results/03-rpc-protocol.log +38 -0
- package/scripts/regression/results/04-slash-commands-and-skills.log +10 -0
- package/scripts/regression/results/05-tool-execution.log +6 -0
- package/scripts/regression/results/06-pi-new-features.log +33 -0
- package/scripts/regression/run-all.sh +187 -0
- package/config/agent/dangerous-patterns.json +0 -106
- package/config/agent/extensions/bash-check.ts +0 -227
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
# zread `--stdio` protocol
|
|
2
|
+
|
|
3
|
+
Every zread command accepts `--stdio`, switching the process from an interactive
|
|
4
|
+
TUI into a JSON-line machine protocol on stdin/stdout. This is the contract used
|
|
5
|
+
by GUI / programmatic clients to drive zread.
|
|
6
|
+
|
|
7
|
+
## Architecture
|
|
8
|
+
|
|
9
|
+
zread internally uses a Driver / Client split:
|
|
10
|
+
|
|
11
|
+
- **Driver** holds all business logic (loading config, calling LLMs, running the
|
|
12
|
+
doc server, managing drafts). It is framework-agnostic and only talks to a
|
|
13
|
+
`Transport`.
|
|
14
|
+
- **Client** is either a Bubbletea TUI or `StdioClient`. With `--stdio`, the
|
|
15
|
+
cobra command instantiates `transport.NewLocalTransport()` (a channel pair),
|
|
16
|
+
spawns the driver in a goroutine, then runs `StdioClient.Run(ctx)`.
|
|
17
|
+
- `StdioClient` reads JSON lines from `os.Stdin` and forwards them as commands
|
|
18
|
+
to the driver, while encoding `Event`s from the driver to `os.Stdout` (one
|
|
19
|
+
JSON object per line). It returns when an event with `"done": true` is
|
|
20
|
+
received.
|
|
21
|
+
|
|
22
|
+
Result: stdin = command stream, stdout = event stream, both newline-delimited
|
|
23
|
+
JSON. stderr is reserved for logs/errors and should not be parsed.
|
|
24
|
+
|
|
25
|
+
## Event format (driver → client, stdout)
|
|
26
|
+
|
|
27
|
+
```json
|
|
28
|
+
{
|
|
29
|
+
"vm": { ... },
|
|
30
|
+
"waiting_for": ["..."],
|
|
31
|
+
"done": false,
|
|
32
|
+
"error": ""
|
|
33
|
+
}
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
- `vm`: full ViewModel snapshot for the current state (shape varies per command — see below).
|
|
37
|
+
- `waiting_for`: command `type` values the client may legally send right now. `quit` is always accepted regardless.
|
|
38
|
+
- `done`: `true` on the final event; the process will exit on its own.
|
|
39
|
+
- `error`: non-empty when the previous command was rejected; `vm`/`waiting_for` remain unchanged.
|
|
40
|
+
|
|
41
|
+
Rules:
|
|
42
|
+
- An event is emitted after every state transition. Treat `vm` as the full current state (no diffs).
|
|
43
|
+
- `waiting_for: []` with `done: false` means the driver is awaiting an internal async result — do not send any command; just keep reading.
|
|
44
|
+
- The very last event always has `done: true` and an empty `waiting_for`.
|
|
45
|
+
|
|
46
|
+
## Command format (client → driver, stdin)
|
|
47
|
+
|
|
48
|
+
One JSON object per line:
|
|
49
|
+
|
|
50
|
+
```json
|
|
51
|
+
{"type": "<command_type>", "params": { ... }}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
- `type` is required. `params` may be omitted (treated as `{}`).
|
|
55
|
+
- Universal command: `{"type":"quit","params":{}}` — every driver handles it and shuts down cleanly.
|
|
56
|
+
- Command types prefixed with `_` are driver-internal. They never appear in `waiting_for`; clients must not send them.
|
|
57
|
+
- Sending a command not in `waiting_for` returns an event with `error` set while `vm`/`waiting_for` remain unchanged.
|
|
58
|
+
- Error message format: `"command \"xxx\" not allowed in state yyy"` / `"invalid params: ..."` / `"invalid JSON: ..."`.
|
|
59
|
+
|
|
60
|
+
## Driving a session
|
|
61
|
+
|
|
62
|
+
1. Spawn `zread <command> --stdio` with piped stdin/stdout.
|
|
63
|
+
2. Read the first event — it carries the initial ViewModel and `waiting_for`.
|
|
64
|
+
3. Render or inspect `vm`; pick a legal command from `waiting_for` and write it as a single JSON line followed by `\n`.
|
|
65
|
+
4. Loop: read next event, react, write next command.
|
|
66
|
+
5. Stop when `done: true` arrives. The process will exit on its own. Sending `quit` at any time triggers the same shutdown.
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
## `zread browse --stdio`
|
|
71
|
+
|
|
72
|
+
### ViewModel fields
|
|
73
|
+
|
|
74
|
+
| Field | Type | Notes |
|
|
75
|
+
|---|---|---|
|
|
76
|
+
| `state` | string | `"loading"` `"serving"` `"select_version"` `"no_wiki"` `"error"` |
|
|
77
|
+
| `url` | string | Local server URL; present when `state="serving"` |
|
|
78
|
+
| `browser_opened` | bool | Whether the browser was auto-launched |
|
|
79
|
+
| `has_current` | bool | Whether a "current" wiki version exists |
|
|
80
|
+
| `versions` | array | Present when `state="select_version"` |
|
|
81
|
+
| `versions[].id` | string | Wiki version ID |
|
|
82
|
+
| `versions[].timestamp` | string | Version timestamp |
|
|
83
|
+
| `versions[].is_current` | bool | Whether this is the current version |
|
|
84
|
+
| `error` | string | Present when `state="error"` |
|
|
85
|
+
|
|
86
|
+
### Commands
|
|
87
|
+
|
|
88
|
+
| State | `waiting_for` | Command | Params |
|
|
89
|
+
|---|---|---|---|
|
|
90
|
+
| `select_version` | `["select_version","quit"]` | `select_version` | `{"wiki_id":"<id>"}` or `{"current":true}` |
|
|
91
|
+
| `serving` | `["quit"]` | — | — |
|
|
92
|
+
| `no_wiki` | `["quit"]` | — | — |
|
|
93
|
+
| `loading` | `[]` | *(wait for internal event)* | — |
|
|
94
|
+
| `error` | `[]` | — | — |
|
|
95
|
+
|
|
96
|
+
### State machine
|
|
97
|
+
|
|
98
|
+
```
|
|
99
|
+
loading ──(server ok)──────────────────► serving
|
|
100
|
+
loading ──(error)──────────────────────► error (terminal)
|
|
101
|
+
select_version ──(select_version cmd)──► loading ──► serving
|
|
102
|
+
no_wiki ───────────────────────────────► (terminal, done=true)
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
---
|
|
106
|
+
|
|
107
|
+
## `zread generate --stdio`
|
|
108
|
+
|
|
109
|
+
### ViewModel fields
|
|
110
|
+
|
|
111
|
+
| Field | Type | Notes |
|
|
112
|
+
|---|---|---|
|
|
113
|
+
| `state` | string | `"select_action"` `"running"` `"done"` `"error"` |
|
|
114
|
+
| `max_retries` | int | Configured max retries per page |
|
|
115
|
+
| `select_action.scenario` | string | `"has_wiki"` `"has_draft"` `"empty"` |
|
|
116
|
+
| `select_action.wiki_date` | string | Date of existing wiki (when `scenario="has_wiki"`) |
|
|
117
|
+
| `select_action.draft_done` | int | Pages already done in draft |
|
|
118
|
+
| `select_action.draft_total` | int | Total pages in draft |
|
|
119
|
+
| `select_action.next_action` | string | Pre-suggested action: `"generate"` `"browse"` `""` |
|
|
120
|
+
| `catalog.status` | string | `"idle"` `"running"` `"done"` `"resumed"` `"error"` |
|
|
121
|
+
| `catalog.tool_name` | string | Currently-running LLM tool (if any) |
|
|
122
|
+
| `catalog.error` | string | Catalog error message |
|
|
123
|
+
| `catalog.auto_retry` | int | Auto-retry count so far |
|
|
124
|
+
| `pages.tasks` | array | Per-page task list |
|
|
125
|
+
| `pages.tasks[].id` | int | Task ID (used for `retry` command) |
|
|
126
|
+
| `pages.tasks[].title` | string | Page title |
|
|
127
|
+
| `pages.tasks[].slug` | string | Page slug |
|
|
128
|
+
| `pages.tasks[].state` | string | `"pending"` `"running"` `"retry_pending"` `"done"` `"failed"` `"resumed"` |
|
|
129
|
+
| `pages.tasks[].retry_count` | int | Retries attempted so far |
|
|
130
|
+
| `pages.tasks[].max_retries` | int | Retry limit for this task |
|
|
131
|
+
| `pages.tasks[].error` | string | Error message if failed |
|
|
132
|
+
| `pages.done` | int | Completed page count |
|
|
133
|
+
| `pages.total` | int | Total page count |
|
|
134
|
+
| `pages.waiting_retry` | bool | `true` when driver is paused waiting for retry/skip decision |
|
|
135
|
+
| `done_total` | int | Total pages done (in `state="done"`) |
|
|
136
|
+
| `error` | string | Fatal error message |
|
|
137
|
+
|
|
138
|
+
### Commands
|
|
139
|
+
|
|
140
|
+
| State / condition | `waiting_for` | Command | Params |
|
|
141
|
+
|---|---|---|---|
|
|
142
|
+
| `select_action` | `["select_action","cancel","quit"]` | `select_action` | `{"action":"generate"}` or `"browse"` or `"resume"` or `"clear"` or `"cancel"` |
|
|
143
|
+
| `running` (normal) | `["quit"]` | — | — |
|
|
144
|
+
| `running` (catalog error) | `["quit","retry_catalog"]` | `retry_catalog` | `{}` |
|
|
145
|
+
| `running` (pages waiting retry) | `["quit","retry","skip_all"]` | `retry` | `{"task_id":<int>}` |
|
|
146
|
+
| `running` (pages waiting retry) | `["quit","retry","skip_all"]` | `skip_all` | `{}` |
|
|
147
|
+
| `running` | `["quit","cancel"]` | `cancel` | `{}` |
|
|
148
|
+
|
|
149
|
+
### CLI flags that affect stdio flow
|
|
150
|
+
|
|
151
|
+
- `--yes` / `-y`: skips the `select_action` gate; generation starts immediately.
|
|
152
|
+
- `--draft-action <resume|clear|cancel>`: pre-answers the draft prompt.
|
|
153
|
+
- `--skip-failed`: equivalent to auto-sending `skip_all` when pages fail.
|
|
154
|
+
|
|
155
|
+
### State machine
|
|
156
|
+
|
|
157
|
+
```
|
|
158
|
+
select_action ──(generate/resume)──► running ──(all done, no failures)──► done
|
|
159
|
+
select_action ──(cancel/browse)────► done (immediate)
|
|
160
|
+
running ──(pages failed)───────────► waiting_retry=true, accepts retry/skip_all
|
|
161
|
+
running ──(cancel)─────────────────► done
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
---
|
|
165
|
+
|
|
166
|
+
## `zread config --stdio`
|
|
167
|
+
|
|
168
|
+
### ViewModel fields
|
|
169
|
+
|
|
170
|
+
| Field | Type | Notes |
|
|
171
|
+
|---|---|---|
|
|
172
|
+
| `fields` | array (9 items) | Ordered config fields |
|
|
173
|
+
| `fields[].title_key` | string | Display label key |
|
|
174
|
+
| `fields[].json_key` | string | Key to use in `update_fields`; empty = read-only |
|
|
175
|
+
| `fields[].initial` | string | Value at load time |
|
|
176
|
+
| `fields[].value` | string | Current (possibly edited) value |
|
|
177
|
+
| `dirty` | bool | `true` if any field differs from `initial` |
|
|
178
|
+
| `saved` | bool | `true` after successful save |
|
|
179
|
+
| `error` | string | Validation error |
|
|
180
|
+
|
|
181
|
+
Field index → `json_key` mapping:
|
|
182
|
+
|
|
183
|
+
| Index | `json_key` | Notes |
|
|
184
|
+
|---|---|---|
|
|
185
|
+
| 0 | `language` | generation language |
|
|
186
|
+
| 1 | `doc_language` | doc language |
|
|
187
|
+
| 2 | *(empty)* | read-only LLM provider display |
|
|
188
|
+
| 3 | `max_concurrent` | int |
|
|
189
|
+
| 4 | `max_retries` | int |
|
|
190
|
+
| 5 | `llm_provider` | editable in stdio mode |
|
|
191
|
+
| 6 | `llm_base_url` | editable in stdio mode |
|
|
192
|
+
| 7 | `llm_model` | editable in stdio mode |
|
|
193
|
+
| 8 | `llm_api_key` | editable in stdio mode |
|
|
194
|
+
|
|
195
|
+
### Commands
|
|
196
|
+
|
|
197
|
+
No state machine. Fixed `waiting_for`: `["update_fields","save","reload_llm","quit"]`.
|
|
198
|
+
|
|
199
|
+
| Command | Params | Effect |
|
|
200
|
+
|---|---|---|
|
|
201
|
+
| `update_fields` | `{"fields":{"<json_key>": "<value>", ...}}` | Partial update; only listed keys change |
|
|
202
|
+
| `save` | `{}` | Persists config and terminates (`done=true`) |
|
|
203
|
+
| `reload_llm` | `{}` | Re-validates LLM provider settings |
|
|
204
|
+
| `quit` | `{}` | Exits without saving |
|
|
205
|
+
|
|
206
|
+
---
|
|
207
|
+
|
|
208
|
+
## `zread login --stdio`
|
|
209
|
+
|
|
210
|
+
### ViewModel fields
|
|
211
|
+
|
|
212
|
+
| Field | Type | Notes |
|
|
213
|
+
|---|---|---|
|
|
214
|
+
| `state` | string | See states below |
|
|
215
|
+
| `authorize_url` | string | OAuth URL to open; present in `open_browser` state |
|
|
216
|
+
| `auth_timeout` | int | Nanoseconds until auth expires; present in `waiting` state |
|
|
217
|
+
| `browser_warn` | string | Warning if browser launch failed |
|
|
218
|
+
| `username` | string | Logged-in username; present in `done` state |
|
|
219
|
+
| `avail_models` | array of string | Models to choose from; present in `select_model` state |
|
|
220
|
+
| `selected_model` | string | Currently selected model |
|
|
221
|
+
| `error` | string | Error message in `error` state |
|
|
222
|
+
| `wants_llm_provider_editor` | bool | `true` (with `done=true`) when `--custom` flag was used |
|
|
223
|
+
|
|
224
|
+
States: `"select_region"` → `"init_flow"` → `"open_browser"` → `"waiting"` → `"select_model"` → `"saving"` → `"done"` / `"error"`
|
|
225
|
+
|
|
226
|
+
### Commands
|
|
227
|
+
|
|
228
|
+
| State | `waiting_for` | Command | Params |
|
|
229
|
+
|---|---|---|---|
|
|
230
|
+
| `select_region` | `["select_region","quit"]` | `select_region` | `{"region":"<provider_key>"}` or `{"region":"__use_own_key__"}` |
|
|
231
|
+
| `select_model` | `["select_model","quit"]` | `select_model` | `{"model":"<model_id>"}` |
|
|
232
|
+
| all others | `[]` | *(wait for internal auth events)* | — |
|
|
233
|
+
|
|
234
|
+
### Special cases
|
|
235
|
+
|
|
236
|
+
- `--custom` flag: driver immediately emits `done: true` with `wants_llm_provider_editor: true`. Client should then launch `zread config --stdio` to configure a custom LLM provider.
|
|
237
|
+
|
|
238
|
+
### State machine
|
|
239
|
+
|
|
240
|
+
```
|
|
241
|
+
select_region ──(cmd)──► init_flow ──► open_browser ──► waiting ──► select_model
|
|
242
|
+
select_model ──(cmd)───► saving ──► done
|
|
243
|
+
any ────────────────────────────────────────────────────────────────► error
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
---
|
|
247
|
+
|
|
248
|
+
## `zread update --stdio`
|
|
249
|
+
|
|
250
|
+
### ViewModel fields
|
|
251
|
+
|
|
252
|
+
| Field | Type | Notes |
|
|
253
|
+
|---|---|---|
|
|
254
|
+
| `state` | string | See states below |
|
|
255
|
+
| `current_version` | string | Installed version |
|
|
256
|
+
| `latest_version` | string | Available version; present in `has_update`+ |
|
|
257
|
+
| `download_url` | string | Asset URL for the update binary |
|
|
258
|
+
| `package_manager` | string | e.g. `"brew"`; present in `package_manager` state |
|
|
259
|
+
| `downloaded_bytes` | int | Bytes downloaded so far |
|
|
260
|
+
| `total_bytes` | int | Total download size |
|
|
261
|
+
| `download_percent` | float | 0–100 |
|
|
262
|
+
| `error` | string | Error message in `error` state |
|
|
263
|
+
|
|
264
|
+
States: `"checking"` → `"up_to_date"` / `"package_manager"` / `"has_update"` → `"downloading"` → `"confirm_restart"` / `"error"`
|
|
265
|
+
|
|
266
|
+
Terminal states (emit `done=true`): `up_to_date`, `package_manager`, `error`, and after restart/skip.
|
|
267
|
+
|
|
268
|
+
### Commands
|
|
269
|
+
|
|
270
|
+
| State | `waiting_for` | Command | Params |
|
|
271
|
+
|---|---|---|---|
|
|
272
|
+
| `has_update` | `["download","quit"]` | `download` | `{}` |
|
|
273
|
+
| `confirm_restart` | `["restart","skip_restart","quit"]` | `restart` | `{}` |
|
|
274
|
+
| `confirm_restart` | `["restart","skip_restart","quit"]` | `skip_restart` | `{}` |
|
|
275
|
+
| all others | `[]` | *(wait for internal events)* | — |
|
|
276
|
+
|
|
277
|
+
### State machine
|
|
278
|
+
|
|
279
|
+
```
|
|
280
|
+
checking ──► up_to_date (terminal)
|
|
281
|
+
──► package_manager (terminal)
|
|
282
|
+
──► has_update ──(download)──► downloading ──► confirm_restart
|
|
283
|
+
──(restart/skip)──► done
|
|
284
|
+
checking / downloading ──(error)──► error (terminal)
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
---
|
|
288
|
+
|
|
289
|
+
## `zread version --stdio`
|
|
290
|
+
|
|
291
|
+
Emits a single event with `done: true` immediately. No commands are accepted.
|
|
292
|
+
|
|
293
|
+
### ViewModel fields
|
|
294
|
+
|
|
295
|
+
| Field | Type | Notes |
|
|
296
|
+
|---|---|---|
|
|
297
|
+
| `version` | string | e.g. `"1.2.3"` |
|
|
298
|
+
| `channel` | string | Release channel (if set) |
|
|
299
|
+
| `go_version` | string | Go runtime version |
|
|
300
|
+
| `os` | string | OS name |
|
|
301
|
+
| `arch` | string | CPU architecture |
|
|
302
|
+
|
|
303
|
+
---
|
|
304
|
+
|
|
305
|
+
## Practical tips
|
|
306
|
+
|
|
307
|
+
- Always flush stdin after writing a command (the driver reads line-by-line).
|
|
308
|
+
- Treat any line on stdout that fails to parse as JSON as a bug — log it but do not crash; zread should not print non-JSON to stdout in `--stdio` mode.
|
|
309
|
+
- When `waiting_for` is `[]` and `done` is `false`, keep reading — the driver is awaiting an internal async result and will emit the next event on its own.
|
|
310
|
+
- For long-running commands (`generate`), keep reading events continuously; there is no heartbeat, but progress events arrive whenever state changes.
|
|
311
|
+
- To cancel cleanly, send `{"type":"quit","params":{}}` and then drain events until `done` rather than killing the process.
|
|
312
|
+
- `done: true` events are always terminal. Do not send further commands.
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ia-ccun/code-agent-cli",
|
|
3
|
-
"version": "0.0.
|
|
4
|
-
"description": "AI Coding Agent CLI - 基于OpenClaw🦞底层Agent
|
|
3
|
+
"version": "0.0.53-beta.1",
|
|
4
|
+
"description": "AI Coding Agent CLI - 基于OpenClaw🦞底层Agent原理封装的编码智能体(供学习使用)。",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
7
7
|
"types": "./dist/index.d.ts",
|
|
@@ -35,10 +35,10 @@
|
|
|
35
35
|
"prepare": "husky"
|
|
36
36
|
},
|
|
37
37
|
"dependencies": {
|
|
38
|
-
"@earendil-works/pi-agent-core": "^0.
|
|
39
|
-
"@earendil-works/pi-ai": "^0.
|
|
40
|
-
"@earendil-works/pi-coding-agent": "^0.
|
|
41
|
-
"@earendil-works/pi-tui": "^0.
|
|
38
|
+
"@earendil-works/pi-agent-core": "^0.85.0",
|
|
39
|
+
"@earendil-works/pi-ai": "^0.85.0",
|
|
40
|
+
"@earendil-works/pi-coding-agent": "^0.85.0",
|
|
41
|
+
"@earendil-works/pi-tui": "^0.85.0"
|
|
42
42
|
},
|
|
43
43
|
"overrides": {
|
|
44
44
|
"glob": "^11.0.0",
|
|
@@ -60,6 +60,7 @@
|
|
|
60
60
|
"@typescript-eslint/parser": "^8.57.0",
|
|
61
61
|
"eslint": "^10.0.3",
|
|
62
62
|
"husky": "^9.1.7",
|
|
63
|
+
"pi-lean-ctx": "^3.8.11",
|
|
63
64
|
"playwright": "^1.60.0",
|
|
64
65
|
"shx": "^0.4.0",
|
|
65
66
|
"typescript": "^5.7.2",
|
package/scripts/postinstall.js
CHANGED
|
@@ -6,6 +6,7 @@ import { symlink as fsSymlink } from 'fs/promises';
|
|
|
6
6
|
import { join, dirname, relative } from 'path';
|
|
7
7
|
import { homedir } from 'os';
|
|
8
8
|
import { fileURLToPath } from 'url';
|
|
9
|
+
import { spawn } from 'child_process';
|
|
9
10
|
|
|
10
11
|
// Get __dirname equivalent for ES modules
|
|
11
12
|
const __filename = fileURLToPath(import.meta.url);
|
|
@@ -240,7 +241,6 @@ const FORCE_OVERWRITE_LIST = [
|
|
|
240
241
|
'APPEND_SYSTEM.md',
|
|
241
242
|
'SYSTEM.md',
|
|
242
243
|
'dangerous-patterns.json',
|
|
243
|
-
'settings.json',
|
|
244
244
|
'help.md',
|
|
245
245
|
'extensions/*.ts',
|
|
246
246
|
'prompts/*.md',
|
|
@@ -463,6 +463,63 @@ logSecurity('install_complete', {
|
|
|
463
463
|
piDir,
|
|
464
464
|
});
|
|
465
465
|
|
|
466
|
+
// ============================================================================
|
|
467
|
+
// Auto-install codegraph (optional dependency)
|
|
468
|
+
// ============================================================================
|
|
469
|
+
|
|
470
|
+
const CODEGRAPH_PKG = '@colbymchenry/codegraph@0.9.7';
|
|
471
|
+
|
|
472
|
+
async function installCodegraph() {
|
|
473
|
+
return new Promise((resolve) => {
|
|
474
|
+
const child = spawn('npm', ['i', '-g', CODEGRAPH_PKG], {
|
|
475
|
+
stdio: 'pipe',
|
|
476
|
+
shell: process.platform === 'win32',
|
|
477
|
+
});
|
|
478
|
+
|
|
479
|
+
let stderr = '';
|
|
480
|
+
|
|
481
|
+
child.stderr.on('data', (data) => {
|
|
482
|
+
stderr += data.toString();
|
|
483
|
+
});
|
|
484
|
+
|
|
485
|
+
child.on('close', (code) => {
|
|
486
|
+
if (code === 0) {
|
|
487
|
+
console.log('✓ Installed codegraph globally,See UniDoc《2026.5 aicode+codegraph 安装与使用》');
|
|
488
|
+
logSecurity('codegraph_install', {
|
|
489
|
+
package: CODEGRAPH_PKG,
|
|
490
|
+
success: true
|
|
491
|
+
});
|
|
492
|
+
} else {
|
|
493
|
+
// 静默失败,不中断主流程
|
|
494
|
+
if (process.env.DEBUG) {
|
|
495
|
+
console.log(`⚠ Could not install codegraph: ${stderr.slice(0, 100)}`);
|
|
496
|
+
}
|
|
497
|
+
logSecurity('codegraph_install', {
|
|
498
|
+
package: CODEGRAPH_PKG,
|
|
499
|
+
success: false,
|
|
500
|
+
error: stderr.slice(0, 200)
|
|
501
|
+
}, 'warn');
|
|
502
|
+
}
|
|
503
|
+
resolve();
|
|
504
|
+
});
|
|
505
|
+
|
|
506
|
+
child.on('error', (err) => {
|
|
507
|
+
if (process.env.DEBUG) {
|
|
508
|
+
console.log(`⚠ Could not install codegraph: ${err.message}`);
|
|
509
|
+
}
|
|
510
|
+
logSecurity('codegraph_install', {
|
|
511
|
+
package: CODEGRAPH_PKG,
|
|
512
|
+
success: false,
|
|
513
|
+
error: err.message
|
|
514
|
+
}, 'warn');
|
|
515
|
+
resolve();
|
|
516
|
+
});
|
|
517
|
+
});
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
// Execute codegraph installation
|
|
521
|
+
await installCodegraph();
|
|
522
|
+
|
|
466
523
|
console.log('\n✨ Setup complete!');
|
|
467
524
|
console.log('📝 Configuration files are in ~/.aicode-cli/');
|
|
468
525
|
console.log('🔗 ~/.pi is linked to ~/.aicode-cli (ensuring model configs work)');
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
# aicode-cli 回归测试套件
|
|
2
|
+
|
|
3
|
+
> 用于验证每次升级 pi 底层依赖后,aicode-cli 的功能完整性不受影响。
|
|
4
|
+
> 也可用于新 aicode 版本发布前的本地预检。
|
|
5
|
+
|
|
6
|
+
## 设计目标
|
|
7
|
+
|
|
8
|
+
| 维度 | 验证内容 |
|
|
9
|
+
|---|---|
|
|
10
|
+
| **版本对齐** | aicode 二进制版本 + 4 个 pi 依赖包主版本一致 |
|
|
11
|
+
| **aicode 特色功能** | 20 个自定义 TS 扩展全部加载,无冲突 / 无语法错误 |
|
|
12
|
+
| **RPC 协议兼容** ⭐ | AicodeRpcClient.kt 实际用到的 17 个 RPC 命令 + 7 个事件类型 + 字段 schema |
|
|
13
|
+
| **预置资产** | 5 个 Prompt + 21+ 个 Skill 全部通过 `get_commands` 注册 |
|
|
14
|
+
| **核心能力** | prompt → message_update 完整事件流、tool_execution 配对 |
|
|
15
|
+
| **pi 0.85 新功能** | `clear_queue` RPC、GPT-6 Astra、thinking 持久化、`usage.cacheWrite1h` |
|
|
16
|
+
|
|
17
|
+
> ⭐ **RPC 协议兼容** 是本套件的核心重点 — 任何 RPC 命令的字段格式变化都会在 case 03 失败。
|
|
18
|
+
|
|
19
|
+
## 快速开始
|
|
20
|
+
|
|
21
|
+
### 前置依赖
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
brew install jq
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
### 运行全部测试
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
# 默认(使用 PATH 中的 aicode,配置目录 ~/.aicode-cli)
|
|
31
|
+
./run-all.sh
|
|
32
|
+
|
|
33
|
+
# 指定 aicode 二进制(测试本地包)
|
|
34
|
+
AICODE_BIN=$(pwd)/bin/cli.js ./run-all.sh
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### 只跑部分 case
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
# 只跑 RPC 兼容性(升级后必跑)
|
|
41
|
+
./run-all.sh --only 03
|
|
42
|
+
|
|
43
|
+
# 跑 RPC + 新功能验证(最小升级预检)
|
|
44
|
+
./run-all.sh --only 03,07
|
|
45
|
+
|
|
46
|
+
# 跑核心 4 个
|
|
47
|
+
./run-all.sh --only 01,02,03,07
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### 单个 case 直接跑
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
./cases/01-version-and-deps.sh
|
|
54
|
+
./cases/02-extension-loading.sh
|
|
55
|
+
./cases/03-rpc-protocol.sh
|
|
56
|
+
./cases/04-prompt-stream.sh
|
|
57
|
+
./cases/05-slash-commands-and-skills.sh
|
|
58
|
+
./cases/06-tool-execution.sh
|
|
59
|
+
./cases/07-pi-new-features.sh
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Case 清单
|
|
63
|
+
|
|
64
|
+
| Case | 文件 | 主题 | 关键断言 |
|
|
65
|
+
|---|---|---|---|
|
|
66
|
+
| **01** | `01-version-and-deps.sh` | 版本与依赖 | `aicode -v` 格式合规 + 4 个 pi 包主版本匹配 |
|
|
67
|
+
| **02** | `02-extension-loading.sh` | aicode 扩展加载 | 启动无 `Failed to load`/`conflict`/`SyntaxError`;aicode-* 命令注册 |
|
|
68
|
+
| **03** ⭐ | `03-rpc-protocol.sh` | **RPC 协议兼容性矩阵** | 17 个 RPC 命令 + 7 个事件类型 + 字段 schema + stopReason 枚举值 |
|
|
69
|
+
| **04** | `04-prompt-stream.sh` | prompt 事件流 | `agent_start`/`turn_start`/`message_update`(thinking_delta/text_delta)/`turn_end`/`agent_end` 序列 |
|
|
70
|
+
| **05** | `05-slash-commands-and-skills.sh` | Prompts + Skills | `/git-commit` `/workflow` 等 5 个 prompt + `/skill:code-review` 等核心 skill |
|
|
71
|
+
| **06** | `06-tool-execution.sh` | 工具调用 | `tool_execution_start` 必带 `toolCallId`+`toolName`(0.84.3 修复点) |
|
|
72
|
+
| **07** | `07-pi-new-features.sh` | pi 0.85 新功能 | `clear_queue` RPC、GPT-6、thinking 持久化、`cacheWrite1h` |
|
|
73
|
+
|
|
74
|
+
## RPC 协议覆盖矩阵(case 03)
|
|
75
|
+
|
|
76
|
+
### RPC 命令(17 个)
|
|
77
|
+
|
|
78
|
+
| 命令 | 来源 | 用途 |
|
|
79
|
+
|---|---|---|
|
|
80
|
+
| `prompt` / `steer` / `follow_up` / `abort` | AicodeRpcClient.streamPrompt/Steer/FollowUp/abort | 消息流 |
|
|
81
|
+
| `compact` | AicodeRpcClient.compact | 上下文压缩 |
|
|
82
|
+
| `extension_ui_response` | AicodeRpcClient.respondExtensionUi | 扩展 UI 回调 |
|
|
83
|
+
| `get_state` / `get_session_stats` / `get_commands` | fetchState/fetchSessionStats/getCommands | 状态查询 |
|
|
84
|
+
| `get_available_models` / `get_available_thinking_levels` | AicodeRpcClient | 模型/thinking 选择 |
|
|
85
|
+
| `get_messages` / `get_entries` / `get_tree` | AicodeRpcClient | 会话历史 |
|
|
86
|
+
| `set_thinking_level` / `cycle_thinking_level` / `set_model_provider` | AicodeRpcClient | 模型控制 |
|
|
87
|
+
| `set_tools` | AicodeRpcClient.setTools | 工具开关 |
|
|
88
|
+
| `new_session` / `clear_queue` | AicodeRpcClient.newSession/clearQueue | 会话管理 + 队列 |
|
|
89
|
+
|
|
90
|
+
### 事件类型(11 个)
|
|
91
|
+
|
|
92
|
+
| 事件 | AicodeStreamItem 映射 |
|
|
93
|
+
|---|---|
|
|
94
|
+
| `agent_start` / `turn_start` | lifecycle |
|
|
95
|
+
| `message_start` / `message_end` | lifecycle |
|
|
96
|
+
| `message_update` | Text / Thinking |
|
|
97
|
+
| `turn_end` / `agent_end` | TurnEnd / lifecycle |
|
|
98
|
+
| `tool_execution_start` / `_update` / `_end` | ToolStart / ToolUpdate / ToolEnd |
|
|
99
|
+
| `queue_update` | QueueUpdate |
|
|
100
|
+
| `auto_retry_end` | Error |
|
|
101
|
+
|
|
102
|
+
### 字段 schema(关键)
|
|
103
|
+
|
|
104
|
+
- `message_start.message.role` ∈ {user, assistant, toolResult}
|
|
105
|
+
- `message_update.assistantMessageEvent.{type, delta, contentIndex}`
|
|
106
|
+
- `turn_end.message.usage.{input, output, cost, ...}`
|
|
107
|
+
- `tool_execution_start.{toolCallId, toolName, args}` (0.84.3 修复点)
|
|
108
|
+
- `tool_execution_end.{isError: "true"|"false"}`
|
|
109
|
+
- `message_end.message.stopReason` ∈ {end_turn, tool_use, error, max_tokens, aborted}
|
|
110
|
+
- `agent_end.willRetry` ∈ {"true", "false"}
|
|
111
|
+
|
|
112
|
+
## 升级后必跑
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
# 1. 改完 package.json,npm install
|
|
116
|
+
# 2. 编译
|
|
117
|
+
npm run build
|
|
118
|
+
|
|
119
|
+
# 3. RPC 兼容性是首要重点
|
|
120
|
+
./scripts/regression/run-all.sh --only 03
|
|
121
|
+
|
|
122
|
+
# 4. 全量回归
|
|
123
|
+
./scripts/regression/run-all.sh
|
|
124
|
+
|
|
125
|
+
# 5. 看 results/*.log
|
|
126
|
+
ls scripts/regression/results/
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
## 验收标准
|
|
130
|
+
|
|
131
|
+
升级可发布的最低条件:
|
|
132
|
+
|
|
133
|
+
| Case | 最低要求 |
|
|
134
|
+
|---|---|
|
|
135
|
+
| 01 version-and-deps | 全部 pass |
|
|
136
|
+
| 02 extension-loading | fail_count = 0 |
|
|
137
|
+
| **03 rpc-protocol** | **全部 pass 或 skip(已知不支持)** |
|
|
138
|
+
| 04 prompt-stream | 全部 pass |
|
|
139
|
+
| 05 slash-commands-and-skills | 注册率 ≥ 80% |
|
|
140
|
+
| 06 tool-execution | 至少 1 个 tool 事件配对成功 |
|
|
141
|
+
| 07 pi-new-features | skip 都标 "pi < 0.85.0 不支持" |
|
|
142
|
+
|
|
143
|
+
## 输出位置
|
|
144
|
+
|
|
145
|
+
| 文件 | 内容 |
|
|
146
|
+
|---|---|
|
|
147
|
+
| `results/<case-name>.log` | 每个 case 的 stdout(pass/fail 行 + RPC 事件 log) |
|
|
148
|
+
| `results/.last_pid` | 最近一次 spawn 的 RPC 进程 PID(调试用) |
|
|
149
|
+
|
|
150
|
+
## 隔离机制
|
|
151
|
+
|
|
152
|
+
每个 case 独立 spawn 一个 `aicode --mode rpc --no-session` 子进程,使用临时目录(`mktemp -d`)作为 configDir,**不污染**用户 `~/.aicode-cli/`。结束后自动清理。
|
|
153
|
+
|
|
154
|
+
但 `02-extension-loading.sh` 需要真实加载扩展(不能用 `-ne`),所以它会用临时目录作为 configDir 跑全量加载,验证 20 个扩展都能正常启动。
|
|
155
|
+
|
|
156
|
+
## 依赖扩展的 case
|
|
157
|
+
|
|
158
|
+
- **02、05** 都需要正常启动扩展 → 不传 `-ne`
|
|
159
|
+
- **03、04、06、07** 主要测 RPC 协议 → 用 `-ne` 跳过扩展避免冲突
|
|
160
|
+
|
|
161
|
+
## 调试
|
|
162
|
+
|
|
163
|
+
### 单独看 RPC 事件流
|
|
164
|
+
|
|
165
|
+
```bash
|
|
166
|
+
# 看 03 的 RPC 命令响应
|
|
167
|
+
jq -c 'select(.type=="response")' scripts/regression/results/03-rpc-protocol.log | head -20
|
|
168
|
+
|
|
169
|
+
# 看 message_update delta 流
|
|
170
|
+
grep message_update scripts/regression/results/04-prompt-stream.log | \
|
|
171
|
+
jq -c '{event: .assistantMessageEvent.type, delta: .assistantMessageEvent.delta}'
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
### 验证 RPC schema 漂移
|
|
175
|
+
|
|
176
|
+
```bash
|
|
177
|
+
# 列出所有 turn_end 的 usage keys
|
|
178
|
+
jq -c 'select(.type=="turn_end") | .message.usage | keys | sort' \
|
|
179
|
+
scripts/regression/results/03-rpc-protocol.log | sort -u
|
|
180
|
+
|
|
181
|
+
# 对比不同 pi 版本
|
|
182
|
+
diff <(jq -c '...' results-v0.80.log) <(jq -c '...' results-v0.85.log)
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
## 与 aicode-plugin-swing 的关系
|
|
186
|
+
|
|
187
|
+
| 层 | 测试位置 |
|
|
188
|
+
|---|---|
|
|
189
|
+
| aicode CLI 功能(命令/扩展/skills) | 本套件(scripts/regression/) |
|
|
190
|
+
| RPC 协议兼容性 | 本套件 case 03 ⭐ |
|
|
191
|
+
| Kotlin 客户端逻辑 | `~/icode/aicode-idea-plugin-swing/src/test/kotlin/com/nu/aicode/rpc/` |
|
|
192
|
+
| 升级兼容性评估 | `~/icode/aicode-idea-plugin-swing/docs/spec/upgrade-code-agent-cli-to-pi-085.md` |
|
|
193
|
+
| 端到端 IDE 集成 | aicode-plugin-swing 实际 IDE 测试 |
|
|
194
|
+
|
|
195
|
+
**本套件(CLI 侧)+ Kotlin 单元测试 + 实际 IDE 集成 = 完整回归链路。**
|
|
196
|
+
|
|
197
|
+
## 添加新 case
|
|
198
|
+
|
|
199
|
+
1. 复制 `cases/06-tool-execution.sh` → `cases/08-my-feature.sh`
|
|
200
|
+
2. 修改 `CURRENT_CASE`
|
|
201
|
+
3. 调用 `source "$SCRIPT_DIR/../lib/common.sh"`
|
|
202
|
+
4. 用 `pass` / `fail` / `skip` 报告结果
|
|
203
|
+
5. 最后 `summary; exit $?`
|
|
204
|
+
|
|
205
|
+
## 添加新的 RPC 命令到 case 03
|
|
206
|
+
|
|
207
|
+
编辑 `03-rpc-protocol.sh` 的 `RPC_COMMANDS` 数组:
|
|
208
|
+
|
|
209
|
+
```bash
|
|
210
|
+
RPC_COMMANDS=(
|
|
211
|
+
...
|
|
212
|
+
"my_new_command|{\"foo\":\"bar\"}"
|
|
213
|
+
)
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
格式:`type|payload`,payload 会拼到 `{...,"id":"rpcN"}` 中。
|
|
217
|
+
|
|
218
|
+
## 修订记录
|
|
219
|
+
|
|
220
|
+
| 日期 | 修订 |
|
|
221
|
+
|---|---|
|
|
222
|
+
| 2026-09-12 | 初版:6 个 case |
|
|
223
|
+
| 2026-09-12 v2 | 新增 case 03 RPC 协议兼容性矩阵(17 命令 + 11 事件 + schema)|
|
|
224
|
+
| 2026-09-12 v2 | 重新编号:03 RPC / 04 prompt / 05 slash / 06 tool / 07 pi-new |
|