@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.
Files changed (120) hide show
  1. package/.github/workflows/ci.yml +19 -0
  2. package/AGENTS.md +78 -0
  3. package/README.ja.md +116 -0
  4. package/README.md +116 -0
  5. package/README.zh-cn.md +116 -0
  6. package/biome.json +10 -0
  7. package/bun.lock +397 -0
  8. package/dist/bin.mjs +1422 -0
  9. package/docs/plans/2026-01-01-getrouter-cli-config-command-plan.md +231 -0
  10. package/docs/plans/2026-01-01-getrouter-cli-config-core-plan.md +307 -0
  11. package/docs/plans/2026-01-01-getrouter-cli-design.md +106 -0
  12. package/docs/plans/2026-01-01-getrouter-cli-scaffold-plan.md +327 -0
  13. package/docs/plans/2026-01-02-getrouter-cli-auth-design.md +68 -0
  14. package/docs/plans/2026-01-02-getrouter-cli-auth-device-design.md +73 -0
  15. package/docs/plans/2026-01-02-getrouter-cli-auth-device-plan.md +411 -0
  16. package/docs/plans/2026-01-02-getrouter-cli-auth-plan.md +435 -0
  17. package/docs/plans/2026-01-02-getrouter-cli-http-client-plan.md +235 -0
  18. package/docs/plans/2026-01-02-getrouter-cli-keys-create-update-output-design.md +24 -0
  19. package/docs/plans/2026-01-02-getrouter-cli-keys-create-update-output-plan.md +141 -0
  20. package/docs/plans/2026-01-02-getrouter-cli-keys-delete-output-design.md +22 -0
  21. package/docs/plans/2026-01-02-getrouter-cli-keys-delete-output-plan.md +122 -0
  22. package/docs/plans/2026-01-02-getrouter-cli-keys-get-output-design.md +23 -0
  23. package/docs/plans/2026-01-02-getrouter-cli-keys-get-output-plan.md +141 -0
  24. package/docs/plans/2026-01-02-getrouter-cli-keys-interactive-design.md +28 -0
  25. package/docs/plans/2026-01-02-getrouter-cli-keys-interactive-plan.md +247 -0
  26. package/docs/plans/2026-01-02-getrouter-cli-keys-output-design.md +31 -0
  27. package/docs/plans/2026-01-02-getrouter-cli-keys-output-plan.md +187 -0
  28. package/docs/plans/2026-01-02-getrouter-cli-keys-subscription-design.md +52 -0
  29. package/docs/plans/2026-01-02-getrouter-cli-keys-subscription-plan.md +306 -0
  30. package/docs/plans/2026-01-02-getrouter-cli-setup-env-design.md +67 -0
  31. package/docs/plans/2026-01-02-getrouter-cli-setup-env-plan.md +441 -0
  32. package/docs/plans/2026-01-02-getrouter-cli-subscription-output-design.md +34 -0
  33. package/docs/plans/2026-01-02-getrouter-cli-subscription-output-plan.md +157 -0
  34. package/docs/plans/2026-01-03-bun-migration-plan.md +103 -0
  35. package/docs/plans/2026-01-03-cli-emoji-output.md +45 -0
  36. package/docs/plans/2026-01-03-cli-english-output.md +123 -0
  37. package/docs/plans/2026-01-03-cli-simplify-design.md +62 -0
  38. package/docs/plans/2026-01-03-cli-simplify-implementation.md +468 -0
  39. package/docs/plans/2026-01-03-readme-command-descriptions.md +116 -0
  40. package/docs/plans/2026-01-03-tsdown-migration-plan.md +75 -0
  41. package/docs/plans/2026-01-04-cli-docs-cleanup-design.md +49 -0
  42. package/docs/plans/2026-01-04-cli-docs-cleanup-plan.md +126 -0
  43. package/docs/plans/2026-01-04-codex-multistep-design.md +76 -0
  44. package/docs/plans/2026-01-04-codex-multistep-plan.md +240 -0
  45. package/docs/plans/2026-01-04-env-hook-design.md +48 -0
  46. package/docs/plans/2026-01-04-env-hook-plan.md +173 -0
  47. package/docs/plans/2026-01-04-models-keys-fuzzy-design.md +75 -0
  48. package/docs/plans/2026-01-04-models-keys-fuzzy-implementation.md +704 -0
  49. package/package.json +37 -0
  50. package/src/.gitkeep +0 -0
  51. package/src/bin.ts +4 -0
  52. package/src/cli.ts +12 -0
  53. package/src/cmd/auth.ts +44 -0
  54. package/src/cmd/claude.ts +10 -0
  55. package/src/cmd/codex.ts +119 -0
  56. package/src/cmd/config-helpers.ts +16 -0
  57. package/src/cmd/config.ts +31 -0
  58. package/src/cmd/env.ts +103 -0
  59. package/src/cmd/index.ts +20 -0
  60. package/src/cmd/keys.ts +207 -0
  61. package/src/cmd/models.ts +48 -0
  62. package/src/cmd/status.ts +106 -0
  63. package/src/cmd/usages.ts +29 -0
  64. package/src/core/api/client.ts +79 -0
  65. package/src/core/auth/device.ts +105 -0
  66. package/src/core/auth/index.ts +37 -0
  67. package/src/core/config/fs.ts +13 -0
  68. package/src/core/config/index.ts +37 -0
  69. package/src/core/config/paths.ts +5 -0
  70. package/src/core/config/redact.ts +18 -0
  71. package/src/core/config/types.ts +23 -0
  72. package/src/core/http/errors.ts +32 -0
  73. package/src/core/http/request.ts +41 -0
  74. package/src/core/http/url.ts +12 -0
  75. package/src/core/interactive/clipboard.ts +61 -0
  76. package/src/core/interactive/codex.ts +75 -0
  77. package/src/core/interactive/fuzzy.ts +64 -0
  78. package/src/core/interactive/keys.ts +164 -0
  79. package/src/core/output/table.ts +34 -0
  80. package/src/core/output/usages.ts +75 -0
  81. package/src/core/paths.ts +4 -0
  82. package/src/core/setup/codex.ts +129 -0
  83. package/src/core/setup/env.ts +220 -0
  84. package/src/core/usages/aggregate.ts +69 -0
  85. package/src/generated/router/dashboard/v1/index.ts +1104 -0
  86. package/src/index.ts +1 -0
  87. package/tests/.gitkeep +0 -0
  88. package/tests/auth/device.test.ts +75 -0
  89. package/tests/auth/status.test.ts +64 -0
  90. package/tests/cli.test.ts +31 -0
  91. package/tests/cmd/auth.test.ts +90 -0
  92. package/tests/cmd/claude.test.ts +132 -0
  93. package/tests/cmd/codex.test.ts +147 -0
  94. package/tests/cmd/config-helpers.test.ts +18 -0
  95. package/tests/cmd/config.test.ts +56 -0
  96. package/tests/cmd/keys.test.ts +163 -0
  97. package/tests/cmd/models.test.ts +63 -0
  98. package/tests/cmd/status.test.ts +82 -0
  99. package/tests/cmd/usages.test.ts +42 -0
  100. package/tests/config/fs.test.ts +14 -0
  101. package/tests/config/index.test.ts +63 -0
  102. package/tests/config/paths.test.ts +10 -0
  103. package/tests/config/redact.test.ts +17 -0
  104. package/tests/config/types.test.ts +10 -0
  105. package/tests/core/api/client.test.ts +92 -0
  106. package/tests/core/interactive/clipboard.test.ts +44 -0
  107. package/tests/core/interactive/codex.test.ts +17 -0
  108. package/tests/core/interactive/fuzzy.test.ts +30 -0
  109. package/tests/core/setup/codex.test.ts +38 -0
  110. package/tests/core/setup/env.test.ts +84 -0
  111. package/tests/core/usages/aggregate.test.ts +55 -0
  112. package/tests/http/errors.test.ts +15 -0
  113. package/tests/http/request.test.ts +82 -0
  114. package/tests/http/url.test.ts +17 -0
  115. package/tests/output/table.test.ts +29 -0
  116. package/tests/output/usages.test.ts +71 -0
  117. package/tests/paths.test.ts +9 -0
  118. package/tsconfig.json +13 -0
  119. package/tsdown.config.ts +5 -0
  120. 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