glm-coding-router 1.1.0 → 1.1.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/README.md CHANGED
@@ -1,419 +1,419 @@
1
- # GLM Coding Router
2
-
3
- GLM Coding Plan workers for Claude Code and Codex — on Windows, Linux, and (experimentally) macOS.
4
-
5
- Claude Code and Codex stay your orchestrators — they keep responsibility for requirements,
6
- architecture, review, and integration. `glm-coding-router` delegates well-scoped
7
- implementation work (exploration, CRUD, boilerplate, tests, mechanical refactoring) to
8
- GLM workers via Z.ai's Anthropic-compatible endpoint.
9
-
10
- One global npm install replaces the manual `.cmd` shim setup:
11
-
12
- ```text
13
- Claude / Codex → shell → glm-worker → claude.exe harness → Z.ai endpoint → GLM Coding Plan
14
- ```
15
-
16
- ## Architecture
17
-
18
- ```text
19
- Developer
20
-
21
- ┌───────────────┴───────────────┐
22
- ▼ ▼
23
- Claude Code Codex
24
- │ │
25
- └───────────────┬───────────────┘
26
- shell command
27
-
28
- ┌───────────────┼───────────────┐
29
- ▼ ▼ ▼
30
- glm-chat glm-fast glm-worker glm-review
31
- │ │ │ │
32
- └─────────┴──────────┴──────────┘
33
- claude.exe
34
- (injected environment only)
35
-
36
-
37
- https://api.z.ai/api/anthropic
38
-
39
-
40
- GLM Coding Plan
41
- GLM-5.3 / GLM-5.3-Flash
42
- ```
43
-
44
- ## Requirements
45
-
46
- - Windows 10/11 or Linux (both verified); macOS is experimental — the suite has not been
47
- run on a Mac
48
- - Node.js >= 20
49
- - Claude Code (`claude.exe`) — the GLM commands run on the Claude Code harness
50
- - Codex (optional — Claude-only setups are fully supported)
51
- - A Z.ai Coding Plan API key
52
-
53
- No Anthropic pay-as-you-go, no OpenAI API, no LiteLLM, no proxy.
54
-
55
- ## Installation
56
-
57
- ```powershell
58
- npm install -g glm-coding-router
59
- glm-router init
60
- ```
61
-
62
- `npx glm-coding-router init` also works for a one-off check, but the global install is
63
- what puts `glm-worker` on your PATH long-term.
64
-
65
- ## Quick start
66
-
67
- After `glm-router init`:
68
-
69
- ```powershell
70
- glm-chat
71
- glm-fast
72
- glm-worker "Implement validation and add tests"
73
- glm-review "Analyze the auth module"
74
- ```
75
-
76
- ## glm-chat
77
-
78
- Interactive GLM-backed Claude Code session. Resolves the Z.ai key, locates `claude.exe`,
79
- injects the Z.ai environment **into the child process only**, and spawns it with
80
- pass-through arguments:
81
-
82
- ```powershell
83
- glm-chat
84
- glm-chat --version
85
- glm-chat --any-claude-flag
86
- ```
87
-
88
- Your normal `claude` command and its authentication are untouched.
89
-
90
- ## glm-fast
91
-
92
- Interactive GLM-backed session pinned to the **fast model** (`models.fast`,
93
- `glm-5.3-flash` by default) — every model slot in the child environment maps to
94
- it, so whichever tier Claude Code picks, it gets the fast model. Same pass-through
95
- arguments as `glm-chat`:
96
-
97
- ```powershell
98
- glm-fast
99
- glm-fast --profile air
100
- ```
101
-
102
- ## glm-worker
103
-
104
- Headless implementation worker:
105
-
106
- ```powershell
107
- glm-worker "Implement validation and add tests"
108
- ```
109
-
110
- Or via stdin (a structured task packet):
111
-
112
- ```powershell
113
- @"
114
- TASK:
115
- Implement refresh token validation.
116
-
117
- SCOPE:
118
- internal/auth/
119
-
120
- VALIDATION:
121
- go test ./internal/auth/...
122
- "@ | glm-worker
123
- ```
124
-
125
- Input priority: **stdin → arguments → error**. The worker runs with
126
- `--max-turns 20 --permission-mode acceptEdits --tools Read,Glob,Grep,Edit,Write,Bash`.
127
- It never uses `--dangerously-skip-permissions`.
128
-
129
- ## glm-review
130
-
131
- Read-only worker for repository exploration, call-graph discovery, duplicate detection,
132
- dependency inspection, and preliminary review:
133
-
134
- ```powershell
135
- glm-review "Inspect this repository"
136
- ```
137
-
138
- Runs with `--tools Read,Glob,Grep` — it cannot edit files or run commands.
139
-
140
- ## Profiles
141
-
142
- All four task binaries (`glm-chat`, `glm-fast`, `glm-worker`, `glm-review`)
143
- accept `--profile <name>` to overlay saved model/maxTurns settings. Profiles
144
- live in `config.json`:
145
-
146
- ```json
147
- {
148
- "profiles": {
149
- "test": { "workerMaxTurns": 10, "fast": "glm-5.3-flash" },
150
- "frontend": { "main": "glm-5.3", "reviewMaxTurns": 30 }
151
- }
152
- }
153
- ```
154
-
155
- ```powershell
156
- glm-worker --profile test "Add failing test then fix it"
157
- glm-review --profile frontend "Review the component tree"
158
- ```
159
-
160
- Fields (all optional): `main`, `fast`, `workerMaxTurns`, `reviewMaxTurns`.
161
- Unknown profile names fail with `ERROR [11]` listing the available ones.
162
- Note: `--profile` belongs to these wrappers — it shadows Claude Code's own
163
- `--profile` flag inside them.
164
-
165
- ## delegate
166
-
167
- Run a GLM worker in an **isolated git worktree** so parallel tasks never trample
168
- each other's working tree (`glm-router delegate backend|frontend|tests`):
169
-
170
- ```powershell
171
- glm-router delegate backend "Implement refresh token validation in internal/auth"
172
- Get-Content task.md | glm-router delegate auth-refresh
173
- ```
174
-
175
- Each run creates a worktree at `<repo>.glm-worktrees\<name>` (outside the repo,
176
- so your checkout's status stays clean) on a new branch `glm/delegate/<name>`
177
- cut from `HEAD`, and runs the standard `glm-worker` inside it. The worktree and
178
- branch are **kept** after the run — the tool never commits, merges, or deletes
179
- your work; the footer prints the path and the merge command:
180
-
181
- ```text
182
- [glm-router] worktree kept at D:\code\my-repo.glm-worktrees\backend
183
- [glm-router] next: inspect it, then merge glm/delegate/backend (or discard with git worktree remove)
184
- ```
185
-
186
- - Prompt priority is stdin → arguments, same as `glm-worker`.
187
- - Profiles: `--profile test` explicitly, or — when omitted — a profile literally
188
- named after the delegate (`delegate test` → the `test` profile) if one exists.
189
- - `--remove` deletes the worktree **after a successful run only**; plain
190
- `git worktree remove` is used, so git refuses (and the worktree is kept) when
191
- the worker left uncommitted changes. The branch is always kept.
192
- - Pre-flight checks fail fast (`ERROR [31]`) when the branch or directory
193
- already exists, or the repo has no commits yet; outside a git repo →
194
- `ERROR [30]`. Uncommitted changes in your main checkout are **not** visible
195
- to the worker — it starts from the last commit.
196
- - `--dry-run` prints the plan; `--json` prints pre-flight and result objects.
197
- - Run several delegates concurrently — distinct names cannot collide:
198
-
199
- ```powershell
200
- glm-router delegate backend "Task A" # terminal 1
201
- glm-router delegate tests "Task B" # terminal 2
202
- ```
203
-
204
- ## benchmark
205
-
206
- Measure the Claude Code + GLM stack on built-in coding tasks (spec §54 v0.4).
207
- Each task runs in a throwaway temp directory: the router writes the task files,
208
- spawns the standard GLM worker (same env injection, plus `--output-format json`
209
- to capture the result document), then runs the task's validation command:
210
-
211
- ```powershell
212
- glm-router benchmark --yes # both built-in tasks, 1 run each
213
- glm-router benchmark --yes --task fn-reverse --repeat 3
214
- glm-router benchmark --yes --max-turns 15
215
- ```
216
-
217
- Report (per task × run): **duration**, **GLM calls** (assistant turns),
218
- **retries** (`-` — not exposed by Claude Code yet), **tokens in/out**,
219
- **tests** (PASS/FAIL of `node test.js`), **success**, **intervention**
220
- (`needed` when the run did not self-complete). The full JSON report is always
221
- saved to `%USERPROFILE%\.glm-coding-router\benchmarks\benchmark-<timestamp>.json`
222
- and `--json` also prints it.
223
-
224
- Built-in tasks: `fn-reverse` (implement `reverseWords` until the test passes),
225
- `fix-bug` (repair an even-length `median` bug).
226
-
227
- Notes:
228
- - Benchmarking makes **real GLM API calls** — interactive runs ask for
229
- confirmation; non-interactive runs require `--yes`.
230
- - Failed tasks are measurements, not errors: the command exits 0 once the
231
- suite ran. Missing key/claude or a broken spawn still fail with the usual
232
- `ERROR [10]/[20]/[40]`.
233
- - `--stack codex` is recognized but not supported yet (headless Codex
234
- orchestration isn't drivable today); the harness is stack-shaped so it can
235
- be added later.
236
-
237
- ## usage
238
-
239
- Provider usage snapshots (spec §54 v0.5) — what is reliably retrievable:
240
-
241
- ```powershell
242
- glm-router usage
243
- ```
244
-
245
- - **Z.ai Coding Plan quota** (network): queries the Z.ai monitor endpoint
246
- (`/api/monitor/usage/quota/limit`) with your key and shows each credit
247
- window — consumed/total, percentage, reset time — plus the plan level.
248
- Unreachable endpoint or a rejected request renders `✗ <reason>` and exits 1.
249
- - **Local totals** (offline): aggregates saved benchmark reports — run count
250
- and summed input/output tokens (`glm-router benchmark` writes them).
251
- - **Claude quota / Codex usage**: always shown as "not available" — neither
252
- exposes a headless usage API today (and claude.ai quota is irrelevant while
253
- traffic is routed to GLM).
254
-
255
- `--json` emits the same data machine-readably. No key configured → `ERROR [10]`.
256
-
257
- ## Agent skills (Claude Code + Codex)
258
-
259
- `glm-router skill install` writes the `glm-delegation` SKILL.md into **both**
260
- agent homes — `~/.claude/skills/` and `~/.codex/skills/` — so either
261
- orchestrator natively knows how to delegate to GLM workers. Missing homes are
262
- skipped with a note (optional enhancement, never fatal); `skill remove`
263
- cleans both. `status` shows one skill row per agent.
264
-
265
- ## MCP server (optional)
266
-
267
- `glm-mcp` (installed with the package) exposes the router as MCP tools over
268
- stdio — any MCP client can delegate without shell syntax:
269
-
270
- | Tool | What it does |
271
- |---|---|
272
- | `glm_worker(prompt, profile?)` | implementation worker, returns output |
273
- | `glm_review(prompt, profile?)` | read-only review/exploration |
274
- | `glm_delegate(name, prompt)` | worker in an isolated git worktree |
275
- | `glm_usage()` | Z.ai quota windows + local benchmark totals |
276
-
277
- Register it with Claude Code (we never edit `~/.claude.json` ourselves — it
278
- goes through Claude's own CLI):
279
-
280
- ```powershell
281
- glm-router mcp # prints the snippet + the exact command
282
- glm-router mcp install # claude mcp add -s user glm-coding-router -- node .../glm-mcp.js
283
- glm-router mcp remove # claude mcp remove -s user glm-coding-router
284
- ```
285
-
286
- Tool-level failures return `isError` results (missing key, no claude, outside
287
- a git repo, unreachable endpoint); the server never prints anything to stdout
288
- except JSON-RPC frames.
289
-
290
- ## CLI reference
291
-
292
- ```text
293
- glm-router init guided setup
294
- glm-router doctor [--network] full runtime diagnosis
295
- glm-router status quick offline overview
296
- glm-router key set store ZAI_API_KEY in this platform's per-user store
297
- glm-router key check key configured? from which source?
298
- glm-router config show
299
- glm-router config set models.main glm-5.3
300
- glm-router delegate <name> run a GLM worker in an isolated git worktree
301
- glm-router benchmark measure the Claude+GLM stack on built-in tasks
302
- glm-router usage Z.ai quota snapshot + local benchmark totals
303
- glm-router mcp optional MCP server registration (glm-mcp)
304
- glm-router project init CLAUDE.md / AGENTS.md managed blocks (--dry-run supported)
305
- glm-router project remove
306
- glm-router skill install optional Codex delegation skill
307
- glm-router skill remove
308
- glm-router uninstall guided removal (keeps ZAI_API_KEY by default)
309
- ```
310
-
311
- Global flags: `--json --quiet --verbose --dry-run --force --yes`
312
-
313
- ## Claude integration
314
-
315
- `glm-router project init` adds a **managed block** to `CLAUDE.md` at the project root
316
- (`git rev-parse --show-toplevel`, falling back to cwd):
317
-
318
- ```text
319
- <!-- glm-coding-router:start -->
320
- ... delegation policy ...
321
- <!-- glm-coding-router:end -->
322
- ```
323
-
324
- - Everything outside the markers is preserved; existing blocks are replaced in place;
325
- runs are idempotent and never duplicate.
326
- - Files are updated atomically (tmp file → fsync → rename).
327
- - On a malformed marker pair the file is left untouched with an actionable error.
328
- - CRLF/LF and UTF-8 are preserved.
329
- - `glm-router project remove` deletes only the managed block. A file the router
330
- created entirely is deleted only when it would otherwise be empty.
331
-
332
- ## Codex integration
333
-
334
- The same command updates `AGENTS.md` (Codex's repository instruction file) with an
335
- equivalent managed block. Additionally, `glm-router skill install` installs the optional
336
- `glm-delegation` skill to `~/.codex/skills/glm-delegation/SKILL.md`. If Codex is not
337
- detected, the skill step warns and skips — AGENTS.md integration and the core tool are
338
- unaffected.
339
-
340
- ## Orca behavior (stale environments)
341
-
342
- Terminals embedded in Orca snapshot the Windows environment at startup. A key added
343
- after Orca starts is invisible to those terminals. Every GLM command therefore resolves
344
- the key in this order:
345
-
346
- 1. `process.env.ZAI_API_KEY`
347
- 2. This platform's per-user store — Windows User Environment (PowerShell), macOS login
348
- keychain (`security`), or libsecret (`secret-tool`, when installed)
349
- 3. fail with an actionable error
350
-
351
- The key is never cached to disk.
352
-
353
- ## Security model
354
-
355
- - The key lives only in that per-user store; it is never written to
356
- `config.json`, the repo, logs, or stack traces. Debug output redacts
357
- `ZAI_API_KEY`, `ANTHROPIC_AUTH_TOKEN`, and Authorization headers.
358
- - Z.ai routing environment variables (`ANTHROPIC_AUTH_TOKEN`,
359
- `ANTHROPIC_BASE_URL`, model overrides) are injected **only** into the spawned
360
- `claude.exe` child process. `ANTHROPIC_API_KEY` is blanked in the child so your
361
- normal Claude auth is never in play. `ANTHROPIC_BASE_URL` is never persisted globally.
362
- - Claude Code and Codex global authentication are never modified.
363
- - Child processes are spawned with argument arrays (`shell: false`) — prompts with
364
- quotes, pipes, ampersands, or newlines are passed verbatim, never through a shell.
365
- - No telemetry, no automatic git commits.
366
-
367
- ## Troubleshooting
368
-
369
- | Symptom | Fix |
370
- | --- | --- |
371
- | `ERROR [ZAI_KEY_MISSING]` | `glm-router key set`, then open a **new** terminal |
372
- | `ERROR [CLAUDE_NOT_FOUND]` | Install Claude Code, or `glm-router config set claudePath C:\path\to\claude.exe` |
373
- | Key works in a new terminal but not inside Orca | Expected — workers re-read the per-user store automatically; run `glm-router doctor` to confirm |
374
- | `glm-router key set` prints an `export` line instead of saving | This platform has no secret store (e.g. Linux without `secret-tool`). Add the line to your shell profile; `glm-router key check` verifies it |
375
- | The worker creates files but never runs the tests | Its Bash allowlist is empty. `glm-router config show` → `worker.allowedBash`; the default list covers common test commands |
376
- | `glm-*` not on PATH after install | Reopen the terminal; check `npm config get prefix` is on PATH |
377
- | `ERROR [MANAGED_BLOCK_CORRUPT]` | Fix the marker pair in the named file manually, then re-run |
378
-
379
- Run `glm-router doctor` (add `--network` to probe the Z.ai endpoint) for a full diagnosis.
380
-
381
- ## Uninstall
382
-
383
- ```powershell
384
- glm-router uninstall
385
- ```
386
-
387
- The wizard removes the config, the Codex skill, and optionally the current project
388
- integration. `ZAI_API_KEY` is **kept** by default — removing credentials requires
389
- explicit consent. Finish with `npm uninstall -g glm-coding-router`.
390
-
391
- ## Development
392
-
393
- ```powershell
394
- npm install
395
- npm run build # tsc → dist/
396
- npm test # vitest run
397
- npm run lint # eslint src tests
398
- npm run dev # tsx src/cli.ts <args>
399
- ```
400
-
401
- Integration tests spawn `tests/fixtures/fake-agent.mjs` (via `node.exe`) to verify
402
- argument passing, environment injection, and exit-code propagation without spending
403
- API quota. See the `docs/GLM Coding Router — Technical Specification v0.1.md` for the full
404
- v0.1 contract (exit codes, managed-block test matrix, acceptance criteria).
405
-
406
- ## Publishing
407
-
408
- ```powershell
409
- npm run build
410
- npm test
411
- npm publish
412
- ```
413
-
414
- `prepublishOnly` runs build + tests. The package ships only `dist/`; the four binaries
415
- (`glm-router`, `glm-chat`, `glm-fast`, `glm-worker`, `glm-review`) are declared in `bin`.
416
-
417
- ## License
418
-
419
- MIT
1
+ # GLM Coding Router
2
+
3
+ GLM Coding Plan workers for Claude Code and Codex — on Windows, Linux, and (experimentally) macOS.
4
+
5
+ Claude Code and Codex stay your orchestrators — they keep responsibility for requirements,
6
+ architecture, review, and integration. `glm-coding-router` delegates well-scoped
7
+ implementation work (exploration, CRUD, boilerplate, tests, mechanical refactoring) to
8
+ GLM workers via Z.ai's Anthropic-compatible endpoint.
9
+
10
+ One global npm install replaces the manual `.cmd` shim setup:
11
+
12
+ ```text
13
+ Claude / Codex → shell → glm-worker → claude.exe harness → Z.ai endpoint → GLM Coding Plan
14
+ ```
15
+
16
+ ## Architecture
17
+
18
+ ```text
19
+ Developer
20
+
21
+ ┌───────────────┴───────────────┐
22
+ ▼ ▼
23
+ Claude Code Codex
24
+ │ │
25
+ └───────────────┬───────────────┘
26
+ shell command
27
+
28
+ ┌───────────────┼───────────────┐
29
+ ▼ ▼ ▼
30
+ glm-chat glm-fast glm-worker glm-review
31
+ │ │ │ │
32
+ └─────────┴──────────┴──────────┘
33
+ claude.exe
34
+ (injected environment only)
35
+
36
+
37
+ https://api.z.ai/api/anthropic
38
+
39
+
40
+ GLM Coding Plan
41
+ GLM-5.3 / GLM-5.3-Flash
42
+ ```
43
+
44
+ ## Requirements
45
+
46
+ - Windows 10/11 or Linux (both verified); macOS is experimental — the suite has not been
47
+ run on a Mac
48
+ - Node.js >= 20
49
+ - Claude Code (`claude.exe`) — the GLM commands run on the Claude Code harness
50
+ - Codex (optional — Claude-only setups are fully supported)
51
+ - A Z.ai Coding Plan API key
52
+
53
+ No Anthropic pay-as-you-go, no OpenAI API, no LiteLLM, no proxy.
54
+
55
+ ## Installation
56
+
57
+ ```powershell
58
+ npm install -g glm-coding-router
59
+ glm-router init
60
+ ```
61
+
62
+ `npx glm-coding-router init` also works for a one-off check, but the global install is
63
+ what puts `glm-worker` on your PATH long-term.
64
+
65
+ ## Quick start
66
+
67
+ After `glm-router init`:
68
+
69
+ ```powershell
70
+ glm-chat
71
+ glm-fast
72
+ glm-worker "Implement validation and add tests"
73
+ glm-review "Analyze the auth module"
74
+ ```
75
+
76
+ ## glm-chat
77
+
78
+ Interactive GLM-backed Claude Code session. Resolves the Z.ai key, locates `claude.exe`,
79
+ injects the Z.ai environment **into the child process only**, and spawns it with
80
+ pass-through arguments:
81
+
82
+ ```powershell
83
+ glm-chat
84
+ glm-chat --version
85
+ glm-chat --any-claude-flag
86
+ ```
87
+
88
+ Your normal `claude` command and its authentication are untouched.
89
+
90
+ ## glm-fast
91
+
92
+ Interactive GLM-backed session pinned to the **fast model** (`models.fast`,
93
+ `glm-5.3-flash` by default) — every model slot in the child environment maps to
94
+ it, so whichever tier Claude Code picks, it gets the fast model. Same pass-through
95
+ arguments as `glm-chat`:
96
+
97
+ ```powershell
98
+ glm-fast
99
+ glm-fast --profile air
100
+ ```
101
+
102
+ ## glm-worker
103
+
104
+ Headless implementation worker:
105
+
106
+ ```powershell
107
+ glm-worker "Implement validation and add tests"
108
+ ```
109
+
110
+ Or via stdin (a structured task packet):
111
+
112
+ ```powershell
113
+ @"
114
+ TASK:
115
+ Implement refresh token validation.
116
+
117
+ SCOPE:
118
+ internal/auth/
119
+
120
+ VALIDATION:
121
+ go test ./internal/auth/...
122
+ "@ | glm-worker
123
+ ```
124
+
125
+ Input priority: **stdin → arguments → error**. The worker runs with
126
+ `--max-turns 20 --permission-mode acceptEdits --tools Read,Glob,Grep,Edit,Write,Bash`.
127
+ It never uses `--dangerously-skip-permissions`.
128
+
129
+ ## glm-review
130
+
131
+ Read-only worker for repository exploration, call-graph discovery, duplicate detection,
132
+ dependency inspection, and preliminary review:
133
+
134
+ ```powershell
135
+ glm-review "Inspect this repository"
136
+ ```
137
+
138
+ Runs with `--tools Read,Glob,Grep` — it cannot edit files or run commands.
139
+
140
+ ## Profiles
141
+
142
+ All four task binaries (`glm-chat`, `glm-fast`, `glm-worker`, `glm-review`)
143
+ accept `--profile <name>` to overlay saved model/maxTurns settings. Profiles
144
+ live in `config.json`:
145
+
146
+ ```json
147
+ {
148
+ "profiles": {
149
+ "test": { "workerMaxTurns": 10, "fast": "glm-5.3-flash" },
150
+ "frontend": { "main": "glm-5.3", "reviewMaxTurns": 30 }
151
+ }
152
+ }
153
+ ```
154
+
155
+ ```powershell
156
+ glm-worker --profile test "Add failing test then fix it"
157
+ glm-review --profile frontend "Review the component tree"
158
+ ```
159
+
160
+ Fields (all optional): `main`, `fast`, `workerMaxTurns`, `reviewMaxTurns`.
161
+ Unknown profile names fail with `ERROR [11]` listing the available ones.
162
+ Note: `--profile` belongs to these wrappers — it shadows Claude Code's own
163
+ `--profile` flag inside them.
164
+
165
+ ## delegate
166
+
167
+ Run a GLM worker in an **isolated git worktree** so parallel tasks never trample
168
+ each other's working tree (`glm-router delegate backend|frontend|tests`):
169
+
170
+ ```powershell
171
+ glm-router delegate backend "Implement refresh token validation in internal/auth"
172
+ Get-Content task.md | glm-router delegate auth-refresh
173
+ ```
174
+
175
+ Each run creates a worktree at `<repo>.glm-worktrees\<name>` (outside the repo,
176
+ so your checkout's status stays clean) on a new branch `glm/delegate/<name>`
177
+ cut from `HEAD`, and runs the standard `glm-worker` inside it. The worktree and
178
+ branch are **kept** after the run — the tool never commits, merges, or deletes
179
+ your work; the footer prints the path and the merge command:
180
+
181
+ ```text
182
+ [glm-router] worktree kept at D:\code\my-repo.glm-worktrees\backend
183
+ [glm-router] next: inspect it, then merge glm/delegate/backend (or discard with git worktree remove)
184
+ ```
185
+
186
+ - Prompt priority is stdin → arguments, same as `glm-worker`.
187
+ - Profiles: `--profile test` explicitly, or — when omitted — a profile literally
188
+ named after the delegate (`delegate test` → the `test` profile) if one exists.
189
+ - `--remove` deletes the worktree **after a successful run only**; plain
190
+ `git worktree remove` is used, so git refuses (and the worktree is kept) when
191
+ the worker left uncommitted changes. The branch is always kept.
192
+ - Pre-flight checks fail fast (`ERROR [31]`) when the branch or directory
193
+ already exists, or the repo has no commits yet; outside a git repo →
194
+ `ERROR [30]`. Uncommitted changes in your main checkout are **not** visible
195
+ to the worker — it starts from the last commit.
196
+ - `--dry-run` prints the plan; `--json` prints pre-flight and result objects.
197
+ - Run several delegates concurrently — distinct names cannot collide:
198
+
199
+ ```powershell
200
+ glm-router delegate backend "Task A" # terminal 1
201
+ glm-router delegate tests "Task B" # terminal 2
202
+ ```
203
+
204
+ ## benchmark
205
+
206
+ Measure the Claude Code + GLM stack on built-in coding tasks (spec §54 v0.4).
207
+ Each task runs in a throwaway temp directory: the router writes the task files,
208
+ spawns the standard GLM worker (same env injection, plus `--output-format json`
209
+ to capture the result document), then runs the task's validation command:
210
+
211
+ ```powershell
212
+ glm-router benchmark --yes # both built-in tasks, 1 run each
213
+ glm-router benchmark --yes --task fn-reverse --repeat 3
214
+ glm-router benchmark --yes --max-turns 15
215
+ ```
216
+
217
+ Report (per task × run): **duration**, **GLM calls** (assistant turns),
218
+ **retries** (`-` — not exposed by Claude Code yet), **tokens in/out**,
219
+ **tests** (PASS/FAIL of `node test.js`), **success**, **intervention**
220
+ (`needed` when the run did not self-complete). The full JSON report is always
221
+ saved to `%USERPROFILE%\.glm-coding-router\benchmarks\benchmark-<timestamp>.json`
222
+ and `--json` also prints it.
223
+
224
+ Built-in tasks: `fn-reverse` (implement `reverseWords` until the test passes),
225
+ `fix-bug` (repair an even-length `median` bug).
226
+
227
+ Notes:
228
+ - Benchmarking makes **real GLM API calls** — interactive runs ask for
229
+ confirmation; non-interactive runs require `--yes`.
230
+ - Failed tasks are measurements, not errors: the command exits 0 once the
231
+ suite ran. Missing key/claude or a broken spawn still fail with the usual
232
+ `ERROR [10]/[20]/[40]`.
233
+ - `--stack codex` is recognized but not supported yet (headless Codex
234
+ orchestration isn't drivable today); the harness is stack-shaped so it can
235
+ be added later.
236
+
237
+ ## usage
238
+
239
+ Provider usage snapshots (spec §54 v0.5) — what is reliably retrievable:
240
+
241
+ ```powershell
242
+ glm-router usage
243
+ ```
244
+
245
+ - **Z.ai Coding Plan quota** (network): queries the Z.ai monitor endpoint
246
+ (`/api/monitor/usage/quota/limit`) with your key and shows each credit
247
+ window — consumed/total, percentage, reset time — plus the plan level.
248
+ Unreachable endpoint or a rejected request renders `✗ <reason>` and exits 1.
249
+ - **Local totals** (offline): aggregates saved benchmark reports — run count
250
+ and summed input/output tokens (`glm-router benchmark` writes them).
251
+ - **Claude quota / Codex usage**: always shown as "not available" — neither
252
+ exposes a headless usage API today (and claude.ai quota is irrelevant while
253
+ traffic is routed to GLM).
254
+
255
+ `--json` emits the same data machine-readably. No key configured → `ERROR [10]`.
256
+
257
+ ## Agent skills (Claude Code + Codex)
258
+
259
+ `glm-router skill install` writes the `glm-delegation` SKILL.md into **both**
260
+ agent homes — `~/.claude/skills/` and `~/.codex/skills/` — so either
261
+ orchestrator natively knows how to delegate to GLM workers. Missing homes are
262
+ skipped with a note (optional enhancement, never fatal); `skill remove`
263
+ cleans both. `status` shows one skill row per agent.
264
+
265
+ ## MCP server (optional)
266
+
267
+ `glm-mcp` (installed with the package) exposes the router as MCP tools over
268
+ stdio — any MCP client can delegate without shell syntax:
269
+
270
+ | Tool | What it does |
271
+ |---|---|
272
+ | `glm_worker(prompt, profile?)` | implementation worker, returns output |
273
+ | `glm_review(prompt, profile?)` | read-only review/exploration |
274
+ | `glm_delegate(name, prompt)` | worker in an isolated git worktree |
275
+ | `glm_usage()` | Z.ai quota windows + local benchmark totals |
276
+
277
+ Register it with Claude Code (we never edit `~/.claude.json` ourselves — it
278
+ goes through Claude's own CLI):
279
+
280
+ ```powershell
281
+ glm-router mcp # prints the snippet + the exact command
282
+ glm-router mcp install # claude mcp add -s user glm-coding-router -- node .../glm-mcp.js
283
+ glm-router mcp remove # claude mcp remove -s user glm-coding-router
284
+ ```
285
+
286
+ Tool-level failures return `isError` results (missing key, no claude, outside
287
+ a git repo, unreachable endpoint); the server never prints anything to stdout
288
+ except JSON-RPC frames.
289
+
290
+ ## CLI reference
291
+
292
+ ```text
293
+ glm-router init guided setup
294
+ glm-router doctor [--network] full runtime diagnosis
295
+ glm-router status quick offline overview
296
+ glm-router key set store ZAI_API_KEY in this platform's per-user store
297
+ glm-router key check key configured? from which source?
298
+ glm-router config show
299
+ glm-router config set models.main glm-5.3
300
+ glm-router delegate <name> run a GLM worker in an isolated git worktree
301
+ glm-router benchmark measure the Claude+GLM stack on built-in tasks
302
+ glm-router usage Z.ai quota snapshot + local benchmark totals
303
+ glm-router mcp optional MCP server registration (glm-mcp)
304
+ glm-router project init CLAUDE.md / AGENTS.md managed blocks (--dry-run supported)
305
+ glm-router project remove
306
+ glm-router skill install optional Codex delegation skill
307
+ glm-router skill remove
308
+ glm-router uninstall guided removal (keeps ZAI_API_KEY by default)
309
+ ```
310
+
311
+ Global flags: `--json --quiet --verbose --dry-run --force --yes`
312
+
313
+ ## Claude integration
314
+
315
+ `glm-router project init` adds a **managed block** to `CLAUDE.md` at the project root
316
+ (`git rev-parse --show-toplevel`, falling back to cwd):
317
+
318
+ ```text
319
+ <!-- glm-coding-router:start -->
320
+ ... delegation policy ...
321
+ <!-- glm-coding-router:end -->
322
+ ```
323
+
324
+ - Everything outside the markers is preserved; existing blocks are replaced in place;
325
+ runs are idempotent and never duplicate.
326
+ - Files are updated atomically (tmp file → fsync → rename).
327
+ - On a malformed marker pair the file is left untouched with an actionable error.
328
+ - CRLF/LF and UTF-8 are preserved.
329
+ - `glm-router project remove` deletes only the managed block. A file the router
330
+ created entirely is deleted only when it would otherwise be empty.
331
+
332
+ ## Codex integration
333
+
334
+ The same command updates `AGENTS.md` (Codex's repository instruction file) with an
335
+ equivalent managed block. Additionally, `glm-router skill install` installs the optional
336
+ `glm-delegation` skill to `~/.codex/skills/glm-delegation/SKILL.md`. If Codex is not
337
+ detected, the skill step warns and skips — AGENTS.md integration and the core tool are
338
+ unaffected.
339
+
340
+ ## Orca behavior (stale environments)
341
+
342
+ Terminals embedded in Orca snapshot the Windows environment at startup. A key added
343
+ after Orca starts is invisible to those terminals. Every GLM command therefore resolves
344
+ the key in this order:
345
+
346
+ 1. `process.env.ZAI_API_KEY`
347
+ 2. This platform's per-user store — Windows User Environment (PowerShell), macOS login
348
+ keychain (`security`), or libsecret (`secret-tool`, when installed)
349
+ 3. fail with an actionable error
350
+
351
+ The key is never cached to disk.
352
+
353
+ ## Security model
354
+
355
+ - The key lives only in that per-user store; it is never written to
356
+ `config.json`, the repo, logs, or stack traces. Debug output redacts
357
+ `ZAI_API_KEY`, `ANTHROPIC_AUTH_TOKEN`, and Authorization headers.
358
+ - Z.ai routing environment variables (`ANTHROPIC_AUTH_TOKEN`,
359
+ `ANTHROPIC_BASE_URL`, model overrides) are injected **only** into the spawned
360
+ `claude.exe` child process. `ANTHROPIC_API_KEY` is blanked in the child so your
361
+ normal Claude auth is never in play. `ANTHROPIC_BASE_URL` is never persisted globally.
362
+ - Claude Code and Codex global authentication are never modified.
363
+ - Child processes are spawned with argument arrays (`shell: false`) — prompts with
364
+ quotes, pipes, ampersands, or newlines are passed verbatim, never through a shell.
365
+ - No telemetry, no automatic git commits.
366
+
367
+ ## Troubleshooting
368
+
369
+ | Symptom | Fix |
370
+ | --- | --- |
371
+ | `ERROR [ZAI_KEY_MISSING]` | `glm-router key set`, then open a **new** terminal |
372
+ | `ERROR [CLAUDE_NOT_FOUND]` | Install Claude Code, or `glm-router config set claudePath C:\path\to\claude.exe` |
373
+ | Key works in a new terminal but not inside Orca | Expected — workers re-read the per-user store automatically; run `glm-router doctor` to confirm |
374
+ | `glm-router key set` prints an `export` line instead of saving | This platform has no secret store (e.g. Linux without `secret-tool`). Add the line to your shell profile; `glm-router key check` verifies it |
375
+ | The worker creates files but never runs the tests | Its Bash allowlist is empty. `glm-router config show` → `worker.allowedBash`; the default list covers common test commands |
376
+ | `glm-*` not on PATH after install | Reopen the terminal; check `npm config get prefix` is on PATH |
377
+ | `ERROR [MANAGED_BLOCK_CORRUPT]` | Fix the marker pair in the named file manually, then re-run |
378
+
379
+ Run `glm-router doctor` (add `--network` to probe the Z.ai endpoint) for a full diagnosis.
380
+
381
+ ## Uninstall
382
+
383
+ ```powershell
384
+ glm-router uninstall
385
+ ```
386
+
387
+ The wizard removes the config, the Codex skill, and optionally the current project
388
+ integration. `ZAI_API_KEY` is **kept** by default — removing credentials requires
389
+ explicit consent. Finish with `npm uninstall -g glm-coding-router`.
390
+
391
+ ## Development
392
+
393
+ ```powershell
394
+ npm install
395
+ npm run build # tsc → dist/
396
+ npm test # vitest run
397
+ npm run lint # eslint src tests
398
+ npm run dev # tsx src/cli.ts <args>
399
+ ```
400
+
401
+ Integration tests spawn `tests/fixtures/fake-agent.mjs` (via `node.exe`) to verify
402
+ argument passing, environment injection, and exit-code propagation without spending
403
+ API quota. See the `docs/GLM Coding Router — Technical Specification v0.1.md` for the full
404
+ v0.1 contract (exit codes, managed-block test matrix, acceptance criteria).
405
+
406
+ ## Publishing
407
+
408
+ ```powershell
409
+ npm run build
410
+ npm test
411
+ npm publish
412
+ ```
413
+
414
+ `prepublishOnly` runs build + tests. The package ships only `dist/`; the four binaries
415
+ (`glm-router`, `glm-chat`, `glm-fast`, `glm-worker`, `glm-review`) are declared in `bin`.
416
+
417
+ ## License
418
+
419
+ MIT
@@ -51,6 +51,8 @@ export function statusCommand(options, deps = {}) {
51
51
  });
52
52
  return 0;
53
53
  }
54
+ /** Every status row pads its label to this column (spec §41). */
55
+ const LABEL_WIDTH = 16;
54
56
  const lines = [
55
57
  `GLM Coding Router v${version}`,
56
58
  "",
@@ -63,7 +65,7 @@ export function statusCommand(options, deps = {}) {
63
65
  ];
64
66
  for (const row of skillState) {
65
67
  const enabled = row.homeDetected && row.installed;
66
- lines.push(`${row.agent} skill ${enabled ? "enabled" : "disabled"}`);
68
+ lines.push(`${`${row.agent} skill`.padEnd(LABEL_WIDTH)}${enabled ? "enabled" : "disabled"}`);
67
69
  }
68
70
  lines.push("", `Main model ${config.models.main}`, `Fast model ${config.models.fast}`);
69
71
  process.stdout.write(lines.join("\n") + "\n");
@@ -9,7 +9,7 @@ import { emitJson } from "./context.js";
9
9
  /** Z.ai monitor API used by their own dashboard (specs/usage.md). */
10
10
  const ZAI_QUOTA_URL = "https://api.z.ai/api/monitor/usage/quota/limit";
11
11
  /** Window labels for the observed enum values (specs/usage.md); unknown values stay generic. */
12
- function describeWindow(limit) {
12
+ export function describeWindow(limit) {
13
13
  if (limit.unit === 3 && typeof limit.number === "number") {
14
14
  return `${limit.number}-hour window`;
15
15
  }
@@ -168,14 +168,16 @@ export function describeShellExport(name, deps = {}) {
168
168
  const env = deps.env ?? process.env;
169
169
  const home = deps.home ?? env.HOME ?? "~";
170
170
  const exists = deps.exists ?? ((file) => fs.existsSync(file));
171
- const shell = path.basename(env.SHELL ?? "bash");
171
+ const shell = path.posix.basename(env.SHELL ?? "bash");
172
172
  const candidates = shell === "zsh"
173
173
  ? [".zshrc", ".zprofile", ".profile"]
174
174
  : shell === "fish"
175
175
  ? [".config/fish/config.fish"]
176
176
  : [".bashrc", ".bash_profile", ".profile"];
177
- const found = candidates.find((rel) => exists(path.join(home, rel)));
178
- const profile = path.join(home, found ?? candidates[0]);
177
+ // POSIX paths by definition: this guidance only ever names a shell rc file,
178
+ // so it must not pick up Windows separators when the process runs on win32.
179
+ const found = candidates.find((rel) => exists(path.posix.join(home, rel)));
180
+ const profile = path.posix.join(home, found ?? candidates[0]);
179
181
  const line = shell === "fish" ? `set -gx ${name} <your-key>` : `export ${name}="<your-key>"`;
180
182
  return { line, profile };
181
183
  }
@@ -11,7 +11,7 @@ import { applyProfile } from "../core/profile.js";
11
11
  import { version } from "../core/version.js";
12
12
  import { createDelegateWorktree, delegateBranch, removeDelegateWorktree, rollbackDelegateBranch, validateDelegateName, } from "../core/worktree.js";
13
13
  import { resolveZaiApiKey } from "../core/zai-key.js";
14
- import { aggregateLocalUsage, fetchZaiQuota } from "../commands/usage.js";
14
+ import { aggregateLocalUsage, describeWindow, fetchZaiQuota } from "../commands/usage.js";
15
15
  const PROMPT_PROPERTY = { type: "string", description: "The task prompt for the GLM agent." };
16
16
  export const MCP_TOOLS = [
17
17
  {
@@ -138,7 +138,7 @@ async function usage(deps) {
138
138
  lines.push(`Z.ai Coding Plan${quota.level ? ` (level: ${quota.level})` : ""}`);
139
139
  for (const limit of quota.limits ?? []) {
140
140
  const resets = typeof limit.nextResetTime === "number" ? ` — resets ${new Date(limit.nextResetTime).toISOString()}` : "";
141
- lines.push(` ${String(limit.number ?? "?")}x unit ${String(limit.unit ?? "?")}: ${String(limit.currentValue ?? "?")} / ${String(limit.usage ?? "?")} credits (${String(limit.percentage ?? "?")}%)${resets}`);
141
+ lines.push(` ${describeWindow(limit).padEnd(15)} ${String(limit.currentValue ?? "?")} / ${String(limit.usage ?? "?")} credits (${String(limit.percentage ?? "?")}%)${resets}`);
142
142
  }
143
143
  }
144
144
  catch (error) {
package/package.json CHANGED
@@ -1,48 +1,48 @@
1
- {
2
- "name": "glm-coding-router",
3
- "version": "1.1.0",
4
- "description": "GLM Coding Plan workers for Claude Code and Codex",
5
- "type": "module",
6
- "license": "MIT",
7
- "author": "hieu9721",
8
- "repository": {
9
- "type": "git",
10
- "url": "git+https://github.com/hieu9721/GLM-coding-router.git"
11
- },
12
- "bin": {
13
- "glm-router": "./dist/cli.js",
14
- "glm-chat": "./dist/bin/glm-chat.js",
15
- "glm-worker": "./dist/bin/glm-worker.js",
16
- "glm-review": "./dist/bin/glm-review.js",
17
- "glm-fast": "./dist/bin/glm-fast.js",
18
- "glm-mcp": "./dist/bin/glm-mcp.js"
19
- },
20
- "files": [
21
- "dist"
22
- ],
23
- "scripts": {
24
- "dev": "tsx src/cli.ts",
25
- "build": "tsc",
26
- "test": "vitest run",
27
- "test:watch": "vitest",
28
- "lint": "eslint src tests",
29
- "prepublishOnly": "npm run build && npm test"
30
- },
31
- "engines": {
32
- "node": ">=20"
33
- },
34
- "dependencies": {
35
- "commander": "^15.0.0",
36
- "prompts": "^2.4.2",
37
- "zod": "^4.6.5"
38
- },
39
- "devDependencies": {
40
- "@types/node": "^22.20.3",
41
- "@types/prompts": "^2.4.9",
42
- "eslint": "^9.39.5",
43
- "tsx": "^4.23.13",
44
- "typescript": "^5.9.3",
45
- "typescript-eslint": "^8.70.0",
46
- "vitest": "^5.0.1"
47
- }
48
- }
1
+ {
2
+ "name": "glm-coding-router",
3
+ "version": "1.1.1",
4
+ "description": "GLM Coding Plan workers for Claude Code and Codex",
5
+ "type": "module",
6
+ "license": "MIT",
7
+ "author": "hieu9721",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/hieu9721/GLM-coding-router.git"
11
+ },
12
+ "bin": {
13
+ "glm-router": "./dist/cli.js",
14
+ "glm-chat": "./dist/bin/glm-chat.js",
15
+ "glm-worker": "./dist/bin/glm-worker.js",
16
+ "glm-review": "./dist/bin/glm-review.js",
17
+ "glm-fast": "./dist/bin/glm-fast.js",
18
+ "glm-mcp": "./dist/bin/glm-mcp.js"
19
+ },
20
+ "files": [
21
+ "dist"
22
+ ],
23
+ "scripts": {
24
+ "dev": "tsx src/cli.ts",
25
+ "build": "tsc",
26
+ "test": "vitest run",
27
+ "test:watch": "vitest",
28
+ "lint": "eslint src tests",
29
+ "prepublishOnly": "npm run build && npm test"
30
+ },
31
+ "engines": {
32
+ "node": ">=20"
33
+ },
34
+ "dependencies": {
35
+ "commander": "^15.0.0",
36
+ "prompts": "^2.4.2",
37
+ "zod": "^4.6.5"
38
+ },
39
+ "devDependencies": {
40
+ "@types/node": "^22.20.3",
41
+ "@types/prompts": "^2.4.9",
42
+ "eslint": "^9.39.5",
43
+ "tsx": "^4.23.13",
44
+ "typescript": "^5.9.3",
45
+ "typescript-eslint": "^8.70.0",
46
+ "vitest": "^5.0.1"
47
+ }
48
+ }