@getrouter/getrouter-cli 0.1.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.
- package/.github/workflows/ci.yml +19 -0
- package/AGENTS.md +78 -0
- package/README.ja.md +116 -0
- package/README.md +116 -0
- package/README.zh-cn.md +116 -0
- package/biome.json +10 -0
- package/bun.lock +397 -0
- package/dist/bin.mjs +1422 -0
- package/docs/plans/2026-01-01-getrouter-cli-config-command-plan.md +231 -0
- package/docs/plans/2026-01-01-getrouter-cli-config-core-plan.md +307 -0
- package/docs/plans/2026-01-01-getrouter-cli-design.md +106 -0
- package/docs/plans/2026-01-01-getrouter-cli-scaffold-plan.md +327 -0
- package/docs/plans/2026-01-02-getrouter-cli-auth-design.md +68 -0
- package/docs/plans/2026-01-02-getrouter-cli-auth-device-design.md +73 -0
- package/docs/plans/2026-01-02-getrouter-cli-auth-device-plan.md +411 -0
- package/docs/plans/2026-01-02-getrouter-cli-auth-plan.md +435 -0
- package/docs/plans/2026-01-02-getrouter-cli-http-client-plan.md +235 -0
- package/docs/plans/2026-01-02-getrouter-cli-keys-create-update-output-design.md +24 -0
- package/docs/plans/2026-01-02-getrouter-cli-keys-create-update-output-plan.md +141 -0
- package/docs/plans/2026-01-02-getrouter-cli-keys-delete-output-design.md +22 -0
- package/docs/plans/2026-01-02-getrouter-cli-keys-delete-output-plan.md +122 -0
- package/docs/plans/2026-01-02-getrouter-cli-keys-get-output-design.md +23 -0
- package/docs/plans/2026-01-02-getrouter-cli-keys-get-output-plan.md +141 -0
- package/docs/plans/2026-01-02-getrouter-cli-keys-interactive-design.md +28 -0
- package/docs/plans/2026-01-02-getrouter-cli-keys-interactive-plan.md +247 -0
- package/docs/plans/2026-01-02-getrouter-cli-keys-output-design.md +31 -0
- package/docs/plans/2026-01-02-getrouter-cli-keys-output-plan.md +187 -0
- package/docs/plans/2026-01-02-getrouter-cli-keys-subscription-design.md +52 -0
- package/docs/plans/2026-01-02-getrouter-cli-keys-subscription-plan.md +306 -0
- package/docs/plans/2026-01-02-getrouter-cli-setup-env-design.md +67 -0
- package/docs/plans/2026-01-02-getrouter-cli-setup-env-plan.md +441 -0
- package/docs/plans/2026-01-02-getrouter-cli-subscription-output-design.md +34 -0
- package/docs/plans/2026-01-02-getrouter-cli-subscription-output-plan.md +157 -0
- package/docs/plans/2026-01-03-bun-migration-plan.md +103 -0
- package/docs/plans/2026-01-03-cli-emoji-output.md +45 -0
- package/docs/plans/2026-01-03-cli-english-output.md +123 -0
- package/docs/plans/2026-01-03-cli-simplify-design.md +62 -0
- package/docs/plans/2026-01-03-cli-simplify-implementation.md +468 -0
- package/docs/plans/2026-01-03-readme-command-descriptions.md +116 -0
- package/docs/plans/2026-01-03-tsdown-migration-plan.md +75 -0
- package/docs/plans/2026-01-04-cli-docs-cleanup-design.md +49 -0
- package/docs/plans/2026-01-04-cli-docs-cleanup-plan.md +126 -0
- package/docs/plans/2026-01-04-codex-multistep-design.md +76 -0
- package/docs/plans/2026-01-04-codex-multistep-plan.md +240 -0
- package/docs/plans/2026-01-04-env-hook-design.md +48 -0
- package/docs/plans/2026-01-04-env-hook-plan.md +173 -0
- package/docs/plans/2026-01-04-models-keys-fuzzy-design.md +75 -0
- package/docs/plans/2026-01-04-models-keys-fuzzy-implementation.md +704 -0
- package/package.json +37 -0
- package/src/.gitkeep +0 -0
- package/src/bin.ts +4 -0
- package/src/cli.ts +12 -0
- package/src/cmd/auth.ts +44 -0
- package/src/cmd/claude.ts +10 -0
- package/src/cmd/codex.ts +119 -0
- package/src/cmd/config-helpers.ts +16 -0
- package/src/cmd/config.ts +31 -0
- package/src/cmd/env.ts +103 -0
- package/src/cmd/index.ts +20 -0
- package/src/cmd/keys.ts +207 -0
- package/src/cmd/models.ts +48 -0
- package/src/cmd/status.ts +106 -0
- package/src/cmd/usages.ts +29 -0
- package/src/core/api/client.ts +79 -0
- package/src/core/auth/device.ts +105 -0
- package/src/core/auth/index.ts +37 -0
- package/src/core/config/fs.ts +13 -0
- package/src/core/config/index.ts +37 -0
- package/src/core/config/paths.ts +5 -0
- package/src/core/config/redact.ts +18 -0
- package/src/core/config/types.ts +23 -0
- package/src/core/http/errors.ts +32 -0
- package/src/core/http/request.ts +41 -0
- package/src/core/http/url.ts +12 -0
- package/src/core/interactive/clipboard.ts +61 -0
- package/src/core/interactive/codex.ts +75 -0
- package/src/core/interactive/fuzzy.ts +64 -0
- package/src/core/interactive/keys.ts +164 -0
- package/src/core/output/table.ts +34 -0
- package/src/core/output/usages.ts +75 -0
- package/src/core/paths.ts +4 -0
- package/src/core/setup/codex.ts +129 -0
- package/src/core/setup/env.ts +220 -0
- package/src/core/usages/aggregate.ts +69 -0
- package/src/generated/router/dashboard/v1/index.ts +1104 -0
- package/src/index.ts +1 -0
- package/tests/.gitkeep +0 -0
- package/tests/auth/device.test.ts +75 -0
- package/tests/auth/status.test.ts +64 -0
- package/tests/cli.test.ts +31 -0
- package/tests/cmd/auth.test.ts +90 -0
- package/tests/cmd/claude.test.ts +132 -0
- package/tests/cmd/codex.test.ts +147 -0
- package/tests/cmd/config-helpers.test.ts +18 -0
- package/tests/cmd/config.test.ts +56 -0
- package/tests/cmd/keys.test.ts +163 -0
- package/tests/cmd/models.test.ts +63 -0
- package/tests/cmd/status.test.ts +82 -0
- package/tests/cmd/usages.test.ts +42 -0
- package/tests/config/fs.test.ts +14 -0
- package/tests/config/index.test.ts +63 -0
- package/tests/config/paths.test.ts +10 -0
- package/tests/config/redact.test.ts +17 -0
- package/tests/config/types.test.ts +10 -0
- package/tests/core/api/client.test.ts +92 -0
- package/tests/core/interactive/clipboard.test.ts +44 -0
- package/tests/core/interactive/codex.test.ts +17 -0
- package/tests/core/interactive/fuzzy.test.ts +30 -0
- package/tests/core/setup/codex.test.ts +38 -0
- package/tests/core/setup/env.test.ts +84 -0
- package/tests/core/usages/aggregate.test.ts +55 -0
- package/tests/http/errors.test.ts +15 -0
- package/tests/http/request.test.ts +82 -0
- package/tests/http/url.test.ts +17 -0
- package/tests/output/table.test.ts +29 -0
- package/tests/output/usages.test.ts +71 -0
- package/tests/paths.test.ts +9 -0
- package/tsconfig.json +13 -0
- package/tsdown.config.ts +5 -0
- package/vitest.config.ts +7 -0
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
# CLI Command Cleanup + README Localization Implementation Plan
|
|
2
|
+
|
|
3
|
+
> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
|
|
4
|
+
|
|
5
|
+
**Goal:** Remove unregistered command entrypoints and split README into English/Chinese/Japanese files.
|
|
6
|
+
|
|
7
|
+
**Architecture:** Keep the registered command list unchanged while deleting unused command modules. Split README content into language-specific files with a small language switch header.
|
|
8
|
+
|
|
9
|
+
**Tech Stack:** TypeScript, Vitest, Commander, Markdown.
|
|
10
|
+
|
|
11
|
+
### Task 1: Add a failing test that enforces the command entrypoint set
|
|
12
|
+
|
|
13
|
+
**Files:**
|
|
14
|
+
- Modify: `tests/cli.test.ts`
|
|
15
|
+
|
|
16
|
+
**Step 1: Write the failing test**
|
|
17
|
+
|
|
18
|
+
```ts
|
|
19
|
+
import { readdirSync } from "node:fs";
|
|
20
|
+
import path from "node:path";
|
|
21
|
+
|
|
22
|
+
it("only ships registered command entrypoints", () => {
|
|
23
|
+
const cmdDir = path.join(process.cwd(), "src", "cmd");
|
|
24
|
+
const files = readdirSync(cmdDir).filter((file) => file.endsWith(".ts"));
|
|
25
|
+
const expected = [
|
|
26
|
+
"auth.ts",
|
|
27
|
+
"claude.ts",
|
|
28
|
+
"codex.ts",
|
|
29
|
+
"config-helpers.ts",
|
|
30
|
+
"config.ts",
|
|
31
|
+
"env.ts",
|
|
32
|
+
"index.ts",
|
|
33
|
+
"keys.ts",
|
|
34
|
+
"models.ts",
|
|
35
|
+
"status.ts",
|
|
36
|
+
"usages.ts",
|
|
37
|
+
];
|
|
38
|
+
expect(files.sort()).toEqual(expected.sort());
|
|
39
|
+
});
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
**Step 2: Run test to verify it fails**
|
|
43
|
+
|
|
44
|
+
Run: `bun run test -- tests/cli.test.ts`
|
|
45
|
+
Expected: FAIL (extra files exist in `src/cmd`).
|
|
46
|
+
|
|
47
|
+
**Step 3: Commit**
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
git add tests/cli.test.ts
|
|
51
|
+
git commit -m "test: guard cmd entrypoints"
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### Task 2: Remove unused command entrypoints
|
|
55
|
+
|
|
56
|
+
**Files:**
|
|
57
|
+
- Delete: `src/cmd/plans.ts`
|
|
58
|
+
- Delete: `src/cmd/providers.ts`
|
|
59
|
+
- Delete: `src/cmd/subscription.ts`
|
|
60
|
+
- Delete: `src/cmd/user.ts`
|
|
61
|
+
|
|
62
|
+
**Step 1: Remove files**
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
rm src/cmd/plans.ts src/cmd/providers.ts src/cmd/subscription.ts src/cmd/user.ts
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
**Step 2: Run test to verify it passes**
|
|
69
|
+
|
|
70
|
+
Run: `bun run test -- tests/cli.test.ts`
|
|
71
|
+
Expected: PASS.
|
|
72
|
+
|
|
73
|
+
**Step 3: Commit**
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
git add src/cmd/plans.ts src/cmd/providers.ts src/cmd/subscription.ts src/cmd/user.ts
|
|
77
|
+
git commit -m "chore: remove unused cmd entrypoints"
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### Task 3: Split README into language-specific files
|
|
81
|
+
|
|
82
|
+
**Files:**
|
|
83
|
+
- Modify: `README.md`
|
|
84
|
+
- Create: `README.zh-cn.md`
|
|
85
|
+
- Create: `README.ja.md`
|
|
86
|
+
|
|
87
|
+
**Step 1: Update `README.md` to English only and add language links**
|
|
88
|
+
|
|
89
|
+
Add a language switcher at the top, then keep only the English content.
|
|
90
|
+
|
|
91
|
+
**Step 2: Create `README.zh-cn.md`**
|
|
92
|
+
|
|
93
|
+
Move the current Chinese section into this file and add the same language switcher.
|
|
94
|
+
|
|
95
|
+
**Step 3: Create `README.ja.md`**
|
|
96
|
+
|
|
97
|
+
Translate the English content into Japanese, keeping the same structure and headings.
|
|
98
|
+
|
|
99
|
+
**Step 4: Commit**
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
git add README.md README.zh-cn.md README.ja.md
|
|
103
|
+
git commit -m "docs: split readmes by language"
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### Task 4: Full verification
|
|
107
|
+
|
|
108
|
+
**Step 1: Run tests**
|
|
109
|
+
|
|
110
|
+
Run: `bun run test`
|
|
111
|
+
Expected: PASS.
|
|
112
|
+
|
|
113
|
+
**Step 2: Run typecheck**
|
|
114
|
+
|
|
115
|
+
Run: `bun run typecheck`
|
|
116
|
+
Expected: PASS.
|
|
117
|
+
|
|
118
|
+
**Step 3: Run lint**
|
|
119
|
+
|
|
120
|
+
Run: `bun run lint`
|
|
121
|
+
Expected: PASS.
|
|
122
|
+
|
|
123
|
+
**Step 4: Run format**
|
|
124
|
+
|
|
125
|
+
Run: `bun run format`
|
|
126
|
+
Expected: PASS (no changes).
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# Codex Multi-step Config Design
|
|
2
|
+
|
|
3
|
+
## Goals
|
|
4
|
+
- Replace `getrouter codex` env output with a multi-step interactive flow.
|
|
5
|
+
- Support fuzzy search at each step (model, reasoning, key).
|
|
6
|
+
- Write `~/.codex/config.toml` with selected model, reasoning effort, and provider config.
|
|
7
|
+
- Write `~/.codex/auth.json` with `OPENAI_API_KEY` from selected key.
|
|
8
|
+
- Preserve other keys in existing config/auth files (merge, don’t clobber).
|
|
9
|
+
|
|
10
|
+
## Non-Goals
|
|
11
|
+
- Do not change `getrouter claude` behavior.
|
|
12
|
+
- No new dependencies (manual TOML merge).
|
|
13
|
+
- No non-interactive mode for codex (TTY required).
|
|
14
|
+
|
|
15
|
+
## Command Flow (TTY only)
|
|
16
|
+
1. **Model**: select from fixed list with descriptions and fuzzy search.
|
|
17
|
+
- gpt-5.2-codex — Latest frontier agentic coding model.
|
|
18
|
+
- gpt-5.1-codex-max — Codex-optimized flagship for deep and fast reasoning.
|
|
19
|
+
- gpt-5.1-codex-mini — Optimized for codex. Cheaper, faster, but less capable.
|
|
20
|
+
- gpt-5.2 — Latest frontier model with improvements across knowledge, reasoning and coding.
|
|
21
|
+
- Prompt message includes: “Access legacy models by running codex -m <model_name> or in your config.toml”.
|
|
22
|
+
2. **Reasoning**: fuzzy select with display name + description.
|
|
23
|
+
- Low → `low`
|
|
24
|
+
- Medium (default) → `medium`
|
|
25
|
+
- High → `high`
|
|
26
|
+
- Extra high → `xhigh` (display name differs from stored value)
|
|
27
|
+
3. **Key**: fuzzy select existing API keys (reuse consumer list flow).
|
|
28
|
+
4. **Confirm**: show summary (model, reasoning, provider, key id/name) and ask for final confirm.
|
|
29
|
+
|
|
30
|
+
If any step is canceled, exit quietly with no writes.
|
|
31
|
+
|
|
32
|
+
## Output Files
|
|
33
|
+
### `~/.codex/config.toml`
|
|
34
|
+
Set/merge:
|
|
35
|
+
```
|
|
36
|
+
model = "<model>"
|
|
37
|
+
model_reasoning_effort = "<low|medium|high|xhigh>"
|
|
38
|
+
model_provider = "getrouter"
|
|
39
|
+
|
|
40
|
+
[model_providers.getrouter]
|
|
41
|
+
name = "getrouter"
|
|
42
|
+
base_url = "https://api.getrouter.dev/codex"
|
|
43
|
+
wire_api = "responses"
|
|
44
|
+
requires_openai_auth = true
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### `~/.codex/auth.json`
|
|
48
|
+
Merge JSON and set:
|
|
49
|
+
```
|
|
50
|
+
{ "OPENAI_API_KEY": "<key>" }
|
|
51
|
+
```
|
|
52
|
+
Ensure permissions 0600 on non-Windows systems.
|
|
53
|
+
|
|
54
|
+
## Merge Strategy
|
|
55
|
+
- TOML: line-based update at root for `model`, `model_reasoning_effort`, `model_provider`.
|
|
56
|
+
- `[model_providers.getrouter]`:
|
|
57
|
+
- If present, update/insert keys within that table.
|
|
58
|
+
- If absent, append the table block.
|
|
59
|
+
- JSON: read existing, update only `OPENAI_API_KEY`.
|
|
60
|
+
|
|
61
|
+
## Error Handling
|
|
62
|
+
- Non-TTY: error “Interactive mode required…”
|
|
63
|
+
- Missing keys: error “No available API keys”.
|
|
64
|
+
- Write/parse failures: throw and exit with status 1.
|
|
65
|
+
|
|
66
|
+
## Testing Plan
|
|
67
|
+
- Update `tests/cmd/codex.test.ts` for multi-step flow and file writes.
|
|
68
|
+
- Add tests for TOML merge and auth.json merge.
|
|
69
|
+
- Validate reasoning value mapping (Extra high → xhigh).
|
|
70
|
+
|
|
71
|
+
## Files To Touch
|
|
72
|
+
- `src/cmd/codex.ts`
|
|
73
|
+
- `src/core/setup/codex.ts` (new)
|
|
74
|
+
- `src/core/interactive/codex.ts` (new)
|
|
75
|
+
- `tests/cmd/codex.test.ts`
|
|
76
|
+
- `README.*` (doc updates for codex behavior)
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
# Codex Multi-step Config Implementation Plan
|
|
2
|
+
|
|
3
|
+
> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
|
|
4
|
+
|
|
5
|
+
**Goal:** Replace `getrouter codex` with a multi-step interactive flow that writes `~/.codex/config.toml` and `~/.codex/auth.json`, preserving existing keys.
|
|
6
|
+
|
|
7
|
+
**Architecture:** Add codex-specific interactive helpers and TOML/JSON merge utilities. Update codex command to use the new flow and remove env/hook writing for codex only.
|
|
8
|
+
|
|
9
|
+
**Tech Stack:** TypeScript, prompts, Vitest, Node fs.
|
|
10
|
+
|
|
11
|
+
### Task 1: Add failing tests for codex multi-step flow + file writes
|
|
12
|
+
|
|
13
|
+
**Files:**
|
|
14
|
+
- Modify: `tests/cmd/codex.test.ts`
|
|
15
|
+
|
|
16
|
+
**Step 1: Write the failing tests**
|
|
17
|
+
|
|
18
|
+
```ts
|
|
19
|
+
import fs from "node:fs";
|
|
20
|
+
import path from "node:path";
|
|
21
|
+
import prompts from "prompts";
|
|
22
|
+
import { createProgram } from "../../src/cli";
|
|
23
|
+
|
|
24
|
+
const mockModels = [
|
|
25
|
+
"gpt-5.2-codex",
|
|
26
|
+
"gpt-5.1-codex-max",
|
|
27
|
+
"gpt-5.1-codex-mini",
|
|
28
|
+
"gpt-5.2",
|
|
29
|
+
];
|
|
30
|
+
|
|
31
|
+
it("writes codex config and auth after interactive flow", async () => {
|
|
32
|
+
// arrange env + mock consumer
|
|
33
|
+
// inject: model, reasoning, key, confirm
|
|
34
|
+
// expect config.toml + auth.json content
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it("merges existing codex config and auth", async () => {
|
|
38
|
+
// write preexisting config.toml with extra keys and table
|
|
39
|
+
// write preexisting auth.json with extra fields
|
|
40
|
+
// run codex flow and assert extra keys are preserved
|
|
41
|
+
});
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
**Step 2: Run tests to verify they fail**
|
|
45
|
+
|
|
46
|
+
Run: `bun run test -- tests/cmd/codex.test.ts`
|
|
47
|
+
Expected: FAIL (codex still writes env/hook).
|
|
48
|
+
|
|
49
|
+
**Step 3: Commit**
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
git add tests/cmd/codex.test.ts
|
|
53
|
+
git commit -m "test: codex multistep flow"
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### Task 2: Add codex config merge helpers
|
|
57
|
+
|
|
58
|
+
**Files:**
|
|
59
|
+
- Create: `src/core/setup/codex.ts`
|
|
60
|
+
- Test: `tests/core/setup/codex.test.ts`
|
|
61
|
+
|
|
62
|
+
**Step 1: Write the failing tests**
|
|
63
|
+
|
|
64
|
+
```ts
|
|
65
|
+
import fs from "node:fs";
|
|
66
|
+
import os from "node:os";
|
|
67
|
+
import path from "node:path";
|
|
68
|
+
import { mergeCodexToml, mergeAuthJson } from "../../../src/core/setup/codex";
|
|
69
|
+
|
|
70
|
+
it("merges codex toml at root and provider table", () => {
|
|
71
|
+
const input = "other = \"keep\"\n[model_providers.other]\nname = \"x\"\n";
|
|
72
|
+
const output = mergeCodexToml(input, {
|
|
73
|
+
model: "gpt-5.2-codex",
|
|
74
|
+
reasoning: "xhigh",
|
|
75
|
+
});
|
|
76
|
+
expect(output).toContain("model = \"gpt-5.2-codex\"");
|
|
77
|
+
expect(output).toContain("model_reasoning_effort = \"xhigh\"");
|
|
78
|
+
expect(output).toContain("model_provider = \"getrouter\"");
|
|
79
|
+
expect(output).toContain("[model_providers.getrouter]");
|
|
80
|
+
expect(output).toContain("other = \"keep\"");
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
it("merges auth json", () => {
|
|
84
|
+
const output = mergeAuthJson({ existing: "keep" }, "key-123");
|
|
85
|
+
expect(output.OPENAI_API_KEY).toBe("key-123");
|
|
86
|
+
expect(output.existing).toBe("keep");
|
|
87
|
+
});
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
**Step 2: Run test to verify it fails**
|
|
91
|
+
|
|
92
|
+
Run: `bun run test -- tests/core/setup/codex.test.ts`
|
|
93
|
+
Expected: FAIL (helpers missing).
|
|
94
|
+
|
|
95
|
+
**Step 3: Implement minimal helpers**
|
|
96
|
+
|
|
97
|
+
```ts
|
|
98
|
+
export const mergeCodexToml = (content: string, input: { model: string; reasoning: string }) => {
|
|
99
|
+
// line-based update for top-level keys
|
|
100
|
+
// update or append [model_providers.getrouter]
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
export const mergeAuthJson = (data: Record<string, unknown>, apiKey: string) => ({
|
|
104
|
+
...data,
|
|
105
|
+
OPENAI_API_KEY: apiKey,
|
|
106
|
+
});
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
**Step 4: Run test to verify it passes**
|
|
110
|
+
|
|
111
|
+
Run: `bun run test -- tests/core/setup/codex.test.ts`
|
|
112
|
+
Expected: PASS
|
|
113
|
+
|
|
114
|
+
**Step 5: Commit**
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
git add src/core/setup/codex.ts tests/core/setup/codex.test.ts
|
|
118
|
+
git commit -m "feat: codex config merge helpers"
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
### Task 3: Add codex interactive helpers (model + reasoning)
|
|
122
|
+
|
|
123
|
+
**Files:**
|
|
124
|
+
- Create: `src/core/interactive/codex.ts`
|
|
125
|
+
- Test: `tests/core/interactive/codex.test.ts`
|
|
126
|
+
|
|
127
|
+
**Step 1: Write the failing tests**
|
|
128
|
+
|
|
129
|
+
```ts
|
|
130
|
+
import { describe, it, expect } from "vitest";
|
|
131
|
+
import { MODEL_CHOICES, REASONING_CHOICES, mapReasoningValue } from "../../../src/core/interactive/codex";
|
|
132
|
+
|
|
133
|
+
it("maps extra high to xhigh", () => {
|
|
134
|
+
expect(mapReasoningValue("extra_high")).toBe("xhigh");
|
|
135
|
+
});
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
**Step 2: Run test to verify it fails**
|
|
139
|
+
|
|
140
|
+
Run: `bun run test -- tests/core/interactive/codex.test.ts`
|
|
141
|
+
Expected: FAIL (module missing).
|
|
142
|
+
|
|
143
|
+
**Step 3: Implement minimal helpers**
|
|
144
|
+
|
|
145
|
+
```ts
|
|
146
|
+
export const MODEL_CHOICES = [
|
|
147
|
+
{ id: "gpt-5.2-codex", title: "gpt-5.2-codex", description: "Latest frontier agentic coding model." },
|
|
148
|
+
// ...
|
|
149
|
+
];
|
|
150
|
+
|
|
151
|
+
export const REASONING_CHOICES = [
|
|
152
|
+
{ id: "low", label: "Low", value: "low", description: "Fast responses with lighter reasoning" },
|
|
153
|
+
{ id: "medium", label: "Medium (default)", value: "medium", description: "Balances speed and reasoning depth for everyday tasks" },
|
|
154
|
+
{ id: "high", label: "High", value: "high", description: "Greater reasoning depth for complex problems" },
|
|
155
|
+
{ id: "extra_high", label: "Extra high", value: "xhigh", description: "Extra high reasoning depth for complex problems" },
|
|
156
|
+
];
|
|
157
|
+
|
|
158
|
+
export const mapReasoningValue = (id: string) =>
|
|
159
|
+
REASONING_CHOICES.find((item) => item.id === id)?.value ?? "medium";
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
**Step 4: Run test to verify it passes**
|
|
163
|
+
|
|
164
|
+
Run: `bun run test -- tests/core/interactive/codex.test.ts`
|
|
165
|
+
Expected: PASS
|
|
166
|
+
|
|
167
|
+
**Step 5: Commit**
|
|
168
|
+
|
|
169
|
+
```bash
|
|
170
|
+
git add src/core/interactive/codex.ts tests/core/interactive/codex.test.ts
|
|
171
|
+
git commit -m "feat: codex interactive choices"
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
### Task 4: Implement codex command flow
|
|
175
|
+
|
|
176
|
+
**Files:**
|
|
177
|
+
- Modify: `src/cmd/codex.ts`
|
|
178
|
+
- Modify: `src/cmd/index.ts` (if needed)
|
|
179
|
+
- Modify: `tests/cmd/codex.test.ts`
|
|
180
|
+
|
|
181
|
+
**Step 1: Implement flow**
|
|
182
|
+
|
|
183
|
+
- Replace `registerEnvCommand` usage with custom `codex` command.
|
|
184
|
+
- Use fuzzy selects for model + reasoning.
|
|
185
|
+
- Use `selectConsumer` for key selection and fetch API key via `GetConsumer`.
|
|
186
|
+
- Confirm summary before writing.
|
|
187
|
+
- Write config/auth using helpers and set file permissions (0600).
|
|
188
|
+
|
|
189
|
+
**Step 2: Run test to verify it passes**
|
|
190
|
+
|
|
191
|
+
Run: `bun run test -- tests/cmd/codex.test.ts`
|
|
192
|
+
Expected: PASS
|
|
193
|
+
|
|
194
|
+
**Step 3: Commit**
|
|
195
|
+
|
|
196
|
+
```bash
|
|
197
|
+
git add src/cmd/codex.ts tests/cmd/codex.test.ts
|
|
198
|
+
git commit -m "feat: codex multistep config"
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
### Task 5: Update docs
|
|
202
|
+
|
|
203
|
+
**Files:**
|
|
204
|
+
- Modify: `README.md`
|
|
205
|
+
- Modify: `README.zh-cn.md`
|
|
206
|
+
- Modify: `README.ja.md`
|
|
207
|
+
|
|
208
|
+
**Step 1: Update codex description**
|
|
209
|
+
|
|
210
|
+
- Note that `getrouter codex` now configures `~/.codex/config.toml` + `~/.codex/auth.json`.
|
|
211
|
+
- Remove or soften env/hook wording for codex only (claude still env-based).
|
|
212
|
+
|
|
213
|
+
**Step 2: Commit**
|
|
214
|
+
|
|
215
|
+
```bash
|
|
216
|
+
git add README.md README.zh-cn.md README.ja.md
|
|
217
|
+
git commit -m "docs: update codex config flow"
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
### Task 6: Full verification
|
|
221
|
+
|
|
222
|
+
**Step 1: Run tests**
|
|
223
|
+
|
|
224
|
+
Run: `bun run test`
|
|
225
|
+
Expected: PASS
|
|
226
|
+
|
|
227
|
+
**Step 2: Run typecheck**
|
|
228
|
+
|
|
229
|
+
Run: `bun run typecheck`
|
|
230
|
+
Expected: PASS
|
|
231
|
+
|
|
232
|
+
**Step 3: Run lint**
|
|
233
|
+
|
|
234
|
+
Run: `bun run lint`
|
|
235
|
+
Expected: PASS
|
|
236
|
+
|
|
237
|
+
**Step 4: Run format**
|
|
238
|
+
|
|
239
|
+
Run: `bun run format`
|
|
240
|
+
Expected: PASS
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# Env Hook Auto-Source Design
|
|
2
|
+
|
|
3
|
+
## Goals
|
|
4
|
+
- Ensure `getrouter codex` / `getrouter claude` automatically `source` the generated env file in the **current shell** after successful execution.
|
|
5
|
+
- Install a shell hook on `--install` that wraps `getrouter` and performs the auto-source step.
|
|
6
|
+
- Keep existing behavior (env file generation, rc insertion) intact and best-effort.
|
|
7
|
+
|
|
8
|
+
## Non-Goals
|
|
9
|
+
- No new dependencies.
|
|
10
|
+
- No attempt to force the current parent shell to reload rc automatically (shell limitation).
|
|
11
|
+
- No new CLI flags in this iteration (use existing `--install`).
|
|
12
|
+
|
|
13
|
+
## Behavior
|
|
14
|
+
- `getrouter codex --install` / `getrouter claude --install` will:
|
|
15
|
+
- Write the env file (`env.sh` or `env.ps1`) as today.
|
|
16
|
+
- Write a shell hook file under `~/.getrouter/` (or `GETROUTER_CONFIG_DIR`).
|
|
17
|
+
- Append a `source`/dot-source line for the hook into the user’s rc file (idempotent).
|
|
18
|
+
- Once the hook is loaded (by reloading rc or opening a new shell), any subsequent `getrouter codex` or `getrouter claude` call will:
|
|
19
|
+
- Run the real CLI command.
|
|
20
|
+
- If exit code is 0, `source` the env file to update variables in the current shell.
|
|
21
|
+
|
|
22
|
+
## Hook Format
|
|
23
|
+
- **bash/zsh**: `hook.sh` with a `getrouter()` wrapper that calls `command getrouter` to avoid recursion.
|
|
24
|
+
- **fish**: `hook.fish` defining a `getrouter` function with `command getrouter`.
|
|
25
|
+
- **pwsh**: `hook.ps1` defining `function getrouter { & getrouter @args; ...; . $envPath }`.
|
|
26
|
+
- Hook resolves config dir dynamically at runtime:
|
|
27
|
+
- `GETROUTER_CONFIG_DIR` if set, else `~/.getrouter`.
|
|
28
|
+
- Env file path: `env.sh` for sh/fish, `env.ps1` for pwsh.
|
|
29
|
+
|
|
30
|
+
## Error Handling
|
|
31
|
+
- Hook is best-effort and silent on failures.
|
|
32
|
+
- Auto-source only runs when the CLI exits successfully.
|
|
33
|
+
|
|
34
|
+
## Testing Plan
|
|
35
|
+
- Update `tests/core/setup/env.test.ts` to cover hook path + hook rendering output.
|
|
36
|
+
- Update `tests/cmd/codex.test.ts` and `tests/cmd/claude.test.ts` to assert:
|
|
37
|
+
- Hook file is written.
|
|
38
|
+
- Rc file includes hook source line.
|
|
39
|
+
|
|
40
|
+
## Files To Touch
|
|
41
|
+
- `src/core/setup/env.ts`
|
|
42
|
+
- `src/cmd/env.ts`
|
|
43
|
+
- `tests/core/setup/env.test.ts`
|
|
44
|
+
- `tests/cmd/codex.test.ts`
|
|
45
|
+
- `tests/cmd/claude.test.ts`
|
|
46
|
+
- `README.md`
|
|
47
|
+
- `README.zh-cn.md`
|
|
48
|
+
- `README.ja.md`
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
# Env Hook Auto-Source Implementation Plan
|
|
2
|
+
|
|
3
|
+
> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
|
|
4
|
+
|
|
5
|
+
**Goal:** Install a shell hook on `--install` so `getrouter codex/claude` auto-sources env vars in the current shell.
|
|
6
|
+
|
|
7
|
+
**Architecture:** Generate a shell-specific hook file and append a source line to the user’s rc file. The hook wraps `getrouter` and sources the env file after successful `codex/claude` runs.
|
|
8
|
+
|
|
9
|
+
**Tech Stack:** TypeScript, Commander, Vitest, Node fs.
|
|
10
|
+
|
|
11
|
+
### Task 1: Add failing tests for hook generation + installation
|
|
12
|
+
|
|
13
|
+
**Files:**
|
|
14
|
+
- Modify: `tests/core/setup/env.test.ts`
|
|
15
|
+
- Modify: `tests/cmd/codex.test.ts`
|
|
16
|
+
- Modify: `tests/cmd/claude.test.ts`
|
|
17
|
+
|
|
18
|
+
**Step 1: Write the failing tests**
|
|
19
|
+
|
|
20
|
+
```ts
|
|
21
|
+
// tests/core/setup/env.test.ts
|
|
22
|
+
import { getHookFilePath, renderHook } from "../../../src/core/setup/env";
|
|
23
|
+
|
|
24
|
+
it("renders sh hook", () => {
|
|
25
|
+
const output = renderHook("bash");
|
|
26
|
+
expect(output).toContain("getrouter() {");
|
|
27
|
+
expect(output).toContain("command getrouter");
|
|
28
|
+
expect(output).toContain("source");
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it("renders pwsh hook", () => {
|
|
32
|
+
const output = renderHook("pwsh");
|
|
33
|
+
expect(output).toContain("function getrouter");
|
|
34
|
+
expect(output).toContain("$LASTEXITCODE");
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it("resolves hook file paths", () => {
|
|
38
|
+
expect(getHookFilePath("bash", "/tmp")).toBe("/tmp/hook.sh");
|
|
39
|
+
expect(getHookFilePath("zsh", "/tmp")).toBe("/tmp/hook.sh");
|
|
40
|
+
expect(getHookFilePath("fish", "/tmp")).toBe("/tmp/hook.fish");
|
|
41
|
+
expect(getHookFilePath("pwsh", "/tmp")).toBe("/tmp/hook.ps1");
|
|
42
|
+
});
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
```ts
|
|
46
|
+
// tests/cmd/codex.test.ts
|
|
47
|
+
import { getHookFilePath } from "../../src/core/setup/env";
|
|
48
|
+
|
|
49
|
+
it("installs hook into rc", async () => {
|
|
50
|
+
// ...existing setup...
|
|
51
|
+
await program.parseAsync(["node", "getrouter", "codex", "--install"]);
|
|
52
|
+
const hookPath = getHookFilePath("bash", dir);
|
|
53
|
+
expect(fs.existsSync(hookPath)).toBe(true);
|
|
54
|
+
const rcContent = fs.readFileSync(rcPath ?? "", "utf8");
|
|
55
|
+
expect(rcContent).toContain(`source ${hookPath}`);
|
|
56
|
+
});
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
```ts
|
|
60
|
+
// tests/cmd/claude.test.ts
|
|
61
|
+
import { getHookFilePath } from "../../src/core/setup/env";
|
|
62
|
+
|
|
63
|
+
it("installs hook into rc", async () => {
|
|
64
|
+
// ...existing setup...
|
|
65
|
+
await program.parseAsync(["node", "getrouter", "claude", "--install"]);
|
|
66
|
+
const hookPath = getHookFilePath("bash", dir);
|
|
67
|
+
expect(fs.existsSync(hookPath)).toBe(true);
|
|
68
|
+
const rcContent = fs.readFileSync(rcPath ?? "", "utf8");
|
|
69
|
+
expect(rcContent).toContain(`source ${hookPath}`);
|
|
70
|
+
});
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
**Step 2: Run tests to verify they fail**
|
|
74
|
+
|
|
75
|
+
Run: `bun run test -- tests/core/setup/env.test.ts`
|
|
76
|
+
Expected: FAIL (missing exports)
|
|
77
|
+
|
|
78
|
+
Run: `bun run test -- tests/cmd/codex.test.ts`
|
|
79
|
+
Expected: FAIL (hook not created)
|
|
80
|
+
|
|
81
|
+
**Step 3: Commit**
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
git add tests/core/setup/env.test.ts tests/cmd/codex.test.ts tests/cmd/claude.test.ts
|
|
85
|
+
git commit -m "test: cover env hook installation"
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### Task 2: Implement hook generation + installation
|
|
89
|
+
|
|
90
|
+
**Files:**
|
|
91
|
+
- Modify: `src/core/setup/env.ts`
|
|
92
|
+
- Modify: `src/cmd/env.ts`
|
|
93
|
+
|
|
94
|
+
**Step 1: Implement hook helpers**
|
|
95
|
+
|
|
96
|
+
```ts
|
|
97
|
+
export const getHookFilePath = (shell: RcShell, configDir: string) => {
|
|
98
|
+
if (shell === "pwsh") return path.join(configDir, "hook.ps1");
|
|
99
|
+
if (shell === "fish") return path.join(configDir, "hook.fish");
|
|
100
|
+
return path.join(configDir, "hook.sh");
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
export const renderHook = (shell: RcShell) => {
|
|
104
|
+
// return shell-specific wrapper that auto-sources env file
|
|
105
|
+
};
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
**Step 2: Wire hook into `--install`**
|
|
109
|
+
|
|
110
|
+
```ts
|
|
111
|
+
const hookPath = getHookFilePath(shell, configDir);
|
|
112
|
+
writeEnvFile(hookPath, renderHook(shell));
|
|
113
|
+
appendRcIfMissing(rcPath, formatSourceLine(envShell, hookPath));
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
**Step 3: Run tests to verify they pass**
|
|
117
|
+
|
|
118
|
+
Run: `bun run test -- tests/core/setup/env.test.ts`
|
|
119
|
+
Expected: PASS
|
|
120
|
+
|
|
121
|
+
Run: `bun run test -- tests/cmd/codex.test.ts`
|
|
122
|
+
Expected: PASS
|
|
123
|
+
|
|
124
|
+
Run: `bun run test -- tests/cmd/claude.test.ts`
|
|
125
|
+
Expected: PASS
|
|
126
|
+
|
|
127
|
+
**Step 4: Commit**
|
|
128
|
+
|
|
129
|
+
```bash
|
|
130
|
+
git add src/core/setup/env.ts src/cmd/env.ts
|
|
131
|
+
git commit -m "feat: install env auto-source hook"
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### Task 3: Update docs
|
|
135
|
+
|
|
136
|
+
**Files:**
|
|
137
|
+
- Modify: `README.md`
|
|
138
|
+
- Modify: `README.zh-cn.md`
|
|
139
|
+
- Modify: `README.ja.md`
|
|
140
|
+
|
|
141
|
+
**Step 1: Add install hook note**
|
|
142
|
+
|
|
143
|
+
- Mention `--install` adds a shell hook that auto-sources env after `codex/claude`.
|
|
144
|
+
- Add a short note: reload your shell or `source ~/.zshrc` once to activate.
|
|
145
|
+
|
|
146
|
+
**Step 2: Commit**
|
|
147
|
+
|
|
148
|
+
```bash
|
|
149
|
+
git add README.md README.zh-cn.md README.ja.md
|
|
150
|
+
git commit -m "docs: document env auto-source hook"
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
### Task 4: Full verification
|
|
154
|
+
|
|
155
|
+
**Step 1: Run tests**
|
|
156
|
+
|
|
157
|
+
Run: `bun run test`
|
|
158
|
+
Expected: PASS
|
|
159
|
+
|
|
160
|
+
**Step 2: Run typecheck**
|
|
161
|
+
|
|
162
|
+
Run: `bun run typecheck`
|
|
163
|
+
Expected: PASS
|
|
164
|
+
|
|
165
|
+
**Step 3: Run lint**
|
|
166
|
+
|
|
167
|
+
Run: `bun run lint`
|
|
168
|
+
Expected: PASS
|
|
169
|
+
|
|
170
|
+
**Step 4: Run format**
|
|
171
|
+
|
|
172
|
+
Run: `bun run format`
|
|
173
|
+
Expected: PASS
|