@praeviso/code-env-switch 0.1.1 → 0.1.2

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 (75) hide show
  1. package/.github/workflows/npm-publish.yml +25 -0
  2. package/AGENTS.md +32 -0
  3. package/PLAN.md +33 -0
  4. package/README.md +24 -0
  5. package/README_zh.md +24 -0
  6. package/bin/cli/args.js +303 -0
  7. package/bin/cli/help.js +77 -0
  8. package/bin/cli/index.js +13 -0
  9. package/bin/commands/add.js +81 -0
  10. package/bin/commands/index.js +21 -0
  11. package/bin/commands/launch.js +330 -0
  12. package/bin/commands/list.js +57 -0
  13. package/bin/commands/show.js +10 -0
  14. package/bin/commands/statusline.js +12 -0
  15. package/bin/commands/unset.js +20 -0
  16. package/bin/commands/use.js +92 -0
  17. package/bin/config/defaults.js +85 -0
  18. package/bin/config/index.js +20 -0
  19. package/bin/config/io.js +72 -0
  20. package/bin/constants.js +27 -0
  21. package/bin/index.js +279 -0
  22. package/bin/profile/display.js +78 -0
  23. package/bin/profile/index.js +26 -0
  24. package/bin/profile/match.js +40 -0
  25. package/bin/profile/resolve.js +79 -0
  26. package/bin/profile/type.js +90 -0
  27. package/bin/shell/detect.js +40 -0
  28. package/bin/shell/index.js +18 -0
  29. package/bin/shell/snippet.js +92 -0
  30. package/bin/shell/utils.js +35 -0
  31. package/bin/statusline/claude.js +153 -0
  32. package/bin/statusline/codex.js +356 -0
  33. package/bin/statusline/index.js +469 -0
  34. package/bin/types.js +5 -0
  35. package/bin/ui/index.js +16 -0
  36. package/bin/ui/interactive.js +189 -0
  37. package/bin/ui/readline.js +76 -0
  38. package/bin/usage/index.js +709 -0
  39. package/code-env.example.json +11 -0
  40. package/package.json +2 -2
  41. package/src/cli/args.ts +318 -0
  42. package/src/cli/help.ts +75 -0
  43. package/src/cli/index.ts +5 -0
  44. package/src/commands/add.ts +91 -0
  45. package/src/commands/index.ts +10 -0
  46. package/src/commands/launch.ts +395 -0
  47. package/src/commands/list.ts +91 -0
  48. package/src/commands/show.ts +12 -0
  49. package/src/commands/statusline.ts +18 -0
  50. package/src/commands/unset.ts +19 -0
  51. package/src/commands/use.ts +121 -0
  52. package/src/config/defaults.ts +88 -0
  53. package/src/config/index.ts +19 -0
  54. package/src/config/io.ts +69 -0
  55. package/src/constants.ts +28 -0
  56. package/src/index.ts +359 -0
  57. package/src/profile/display.ts +77 -0
  58. package/src/profile/index.ts +12 -0
  59. package/src/profile/match.ts +41 -0
  60. package/src/profile/resolve.ts +84 -0
  61. package/src/profile/type.ts +83 -0
  62. package/src/shell/detect.ts +30 -0
  63. package/src/shell/index.ts +6 -0
  64. package/src/shell/snippet.ts +92 -0
  65. package/src/shell/utils.ts +30 -0
  66. package/src/statusline/claude.ts +172 -0
  67. package/src/statusline/codex.ts +393 -0
  68. package/src/statusline/index.ts +626 -0
  69. package/src/types.ts +95 -0
  70. package/src/ui/index.ts +5 -0
  71. package/src/ui/interactive.ts +220 -0
  72. package/src/ui/readline.ts +85 -0
  73. package/src/usage/index.ts +833 -0
  74. package/bin/codenv.js +0 -1316
  75. package/src/codenv.ts +0 -1478
@@ -0,0 +1,25 @@
1
+ name: Publish to npm
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - "v*"
7
+ workflow_dispatch:
8
+
9
+ jobs:
10
+ publish:
11
+ runs-on: ubuntu-latest
12
+ permissions:
13
+ contents: read
14
+ steps:
15
+ - uses: actions/checkout@v4
16
+ - uses: actions/setup-node@v4
17
+ with:
18
+ node-version: "20"
19
+ registry-url: "https://registry.npmjs.org"
20
+ cache: "npm"
21
+ - run: npm ci
22
+ - run: npm run build
23
+ - run: npm publish --access public
24
+ env:
25
+ NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
package/AGENTS.md ADDED
@@ -0,0 +1,32 @@
1
+ # Repository Guidelines
2
+
3
+ ## Project Structure & Module Organization
4
+ - `src/` holds TypeScript sources. Key areas: `src/cli/` argument parsing, `src/commands/` CLI actions, `src/config/` config IO, `src/profile/` profile resolution, `src/shell/` shell integration, `src/ui/` prompts, `src/usage/` logging.
5
+ - `bin/` contains compiled JavaScript from `tsc`; treat it as generated output.
6
+ - `code-env.example.json` is the public config template; `README.md` and `README_zh.md` are user docs.
7
+
8
+ ## Build, Test, and Development Commands
9
+ - `npm install` installs dependencies.
10
+ - `npm run build` compiles `src/` to `bin/` using `tsconfig.json`.
11
+ - `npm run lint` runs ESLint; `npm run lint:fix` auto-fixes.
12
+ - `npm link` (or `npm install -g .`) installs the CLI locally for manual testing.
13
+ - No automated test script is configured yet.
14
+
15
+ ## Coding Style & Naming Conventions
16
+ - Follow existing formatting: 4-space indentation, double quotes, and semicolons.
17
+ - Use `camelCase` for variables/functions and `PascalCase` for types/interfaces.
18
+ - CLI commands are single verbs (`add`, `use`, `unset`); flags are `--kebab-case` (e.g., `--config`, `--shell`).
19
+ - Keep changes ESLint-clean.
20
+
21
+ ## Testing Guidelines
22
+ - There is no test framework configured. If you add tests, also add a `npm test` script and document the framework in `README.md`.
23
+ - Prefer a top-level `tests/` folder or `src/**/__tests__` for discoverable structure.
24
+
25
+ ## Commit & Pull Request Guidelines
26
+ - Git history shows Conventional Commit-style prefixes (e.g., `feat:`) plus simple descriptive messages; release commits use version numbers like `0.1.1`.
27
+ - Use short, imperative subjects and include a brief body for non-trivial changes.
28
+ - PRs should include: summary, rationale, manual test commands run (e.g., `npm run build`, `codenv use`), and any config or shell-rc impacts; link related issues.
29
+
30
+ ## Configuration & Security Notes
31
+ - Config is loaded via `--config`, `CODE_ENV_CONFIG`, or `~/.config/code-env/config.json`; use `code-env.example.json` for sanitized examples.
32
+ - Never commit real API keys or user-specific config.
package/PLAN.md ADDED
@@ -0,0 +1,33 @@
1
+ # Statusline Plan (Simplified)
2
+
3
+ ## Goals
4
+ - Provide a host-agnostic statusline outputter for Codex CLI and Claude Code.
5
+ - Show Git status, model label, and usage in a single line.
6
+ - Auto-enable a bottom statusline when launching via `codenv`.
7
+
8
+ ## Done (implemented)
9
+ - `codenv statusline` outputs text/JSON with Git + model + usage.
10
+ - ANSI bottom-bar renderer added with forced redraw support (for Codex UI repaint).
11
+ - `codenv launch codex` auto-starts the renderer.
12
+ - `codenv launch claude` ensures `.claude/settings.json` uses a `statusLine` command object.
13
+ - Env knobs added for enable/disable, interval, offset/reserve, and force redraw.
14
+
15
+ ## Next plan (overall)
16
+ ### Phase 1 — Stabilize behavior
17
+ - Verify Codex overlay behavior across terminals; tune default interval/offset as needed.
18
+ - Add a simple “compatibility fallback” note for terminals that don’t support scroll regions.
19
+ - Confirm Claude statusLine object format across versions (no string form).
20
+
21
+ ### Phase 2 — Data quality
22
+ - Optional: resolve model from session logs if not provided by env/stdin.
23
+ - Clarify usage sync strategy (per-session vs aggregate) and align with `src/usage/`.
24
+
25
+ ### Phase 3 — Integrations & ergonomics
26
+ - Provide generic wrapper example for other CLIs (stdin JSON contract).
27
+ - Optional tmux statusline snippet as alternative for Codex.
28
+ - Add minimal config knobs to `code-env.example.json` if needed.
29
+
30
+ ### Phase 4 — QA & polish
31
+ - Manual test checklist (bash/zsh/fish, macOS/Linux).
32
+ - Performance check target (<50ms typical render).
33
+ - Harden error handling and safe fallbacks.
package/README.md CHANGED
@@ -65,6 +65,8 @@ npm link
65
65
 
66
66
  > By default, `codenv use` only outputs shell commands. After running
67
67
  > `codenv init`, the shell wrapper applies them automatically.
68
+ > The snippet also wraps `codex`/`claude` to bind sessions to profiles; use
69
+ > `command codex` / `command claude` to bypass.
68
70
 
69
71
  ### Common commands
70
72
 
@@ -204,6 +206,17 @@ If nothing is found, `codenv add` writes to `~/.config/code-env/config.json`.
204
206
  "codex": "primary",
205
207
  "claude": "default"
206
208
  },
209
+ "codexStatusline": {
210
+ "command": ["codenv", "statusline", "--type", "codex", "--sync-usage"],
211
+ "showHints": false,
212
+ "updateIntervalMs": 300,
213
+ "timeoutMs": 1000
214
+ },
215
+ "claudeStatusline": {
216
+ "command": "codenv statusline --type claude --sync-usage",
217
+ "type": "command",
218
+ "padding": 0
219
+ },
207
220
  "profiles": {
208
221
  "p_a1b2c3": {
209
222
  "name": "primary",
@@ -223,6 +236,17 @@ If nothing is found, `codenv add` writes to `~/.config/code-env/config.json`.
223
236
  Notes:
224
237
  - `unset`: global keys to clear. Type-specific defaults are applied only for the active type and won't clear the other type.
225
238
  - `defaultProfiles`: optional; map of `codex`/`claude` to profile name or key used by `codenv auto`.
239
+ - `codexStatusline`: optional; config to inject Codex TUI status line settings when launching `codex`.
240
+ - `command`: string or string[]; command passed to Codex `tui.status_line.command`.
241
+ - `showHints`: boolean; whether Codex footer hints are appended when the status line is active.
242
+ - `updateIntervalMs`: number; update interval in ms for the status line command.
243
+ - `timeoutMs`: number; timeout in ms for the status line command.
244
+ - `configPath`: optional; override `~/.codex/config.toml` (also supports `CODE_ENV_CODEX_CONFIG_PATH`).
245
+ - `claudeStatusline`: optional; config to inject Claude Code statusLine settings when launching `claude`.
246
+ - `command`: string (or string[]; arrays are joined into a single command string).
247
+ - `type`: string; statusLine type (default: `command`).
248
+ - `padding`: number; statusLine padding (default: 0).
249
+ - `settingsPath`: optional; override `~/.claude/settings.json` (also supports `CODE_ENV_CLAUDE_SETTINGS_PATH`).
226
250
  - `name`: human-facing profile name shown in `codenv list` and used by `codenv use <name>`.
227
251
  - `type`: optional; `codex` or `claude` (alias `cc`) for `codenv use <type> <name>` matching.
228
252
  - `note`: shown in `codenv list`.
package/README_zh.md CHANGED
@@ -65,6 +65,8 @@ npm link
65
65
 
66
66
  > 默认情况下,`codenv use` 仅输出 shell 命令;执行 `codenv init` 后,
67
67
  > shell 包装函数会自动在当前终端生效。
68
+ > 该片段还会包装 `codex`/`claude` 以绑定会话到 profile;如需绕过,
69
+ > 可使用 `command codex` / `command claude`。
68
70
 
69
71
  ### 常用命令
70
72
 
@@ -203,6 +205,17 @@ codenv use codex primary | source
203
205
  "codex": "primary",
204
206
  "claude": "default"
205
207
  },
208
+ "codexStatusline": {
209
+ "command": ["codenv", "statusline", "--type", "codex", "--sync-usage"],
210
+ "showHints": false,
211
+ "updateIntervalMs": 300,
212
+ "timeoutMs": 1000
213
+ },
214
+ "claudeStatusline": {
215
+ "command": "codenv statusline --type claude --sync-usage",
216
+ "type": "command",
217
+ "padding": 0
218
+ },
206
219
  "profiles": {
207
220
  "p_a1b2c3": {
208
221
  "name": "primary",
@@ -222,6 +235,17 @@ codenv use codex primary | source
222
235
  说明:
223
236
  - `unset`:全局需要清理的环境变量。按 type 的默认清理键只会对当前 type 生效,不会影响其他 type。
224
237
  - `defaultProfiles`:可选;`codex`/`claude` 对应的默认 profile 名称或 key,供 `codenv auto` 使用。
238
+ - `codexStatusline`:可选;在启动 `codex` 时写入 Codex TUI status line 配置。
239
+ - `command`:字符串或字符串数组;写入 Codex 的 `tui.status_line.command`。
240
+ - `showHints`:布尔值;状态栏激活时是否拼接 footer 提示。
241
+ - `updateIntervalMs`:数字;状态栏命令的刷新间隔(毫秒)。
242
+ - `timeoutMs`:数字;状态栏命令超时(毫秒)。
243
+ - `configPath`:可选;覆盖 `~/.codex/config.toml`(也可用 `CODE_ENV_CODEX_CONFIG_PATH`)。
244
+ - `claudeStatusline`:可选;在启动 `claude` 时写入 Claude Code statusLine 配置。
245
+ - `command`:字符串(或字符串数组;数组会被拼接成单个命令字符串)。
246
+ - `type`:字符串;statusLine 类型(默认 `command`)。
247
+ - `padding`:数字;statusLine padding(默认 0)。
248
+ - `settingsPath`:可选;覆盖 `~/.claude/settings.json`(也可用 `CODE_ENV_CLAUDE_SETTINGS_PATH`)。
225
249
  - `name`:用于展示的 profile 名称,`codenv list` 与 `codenv use <name>` 会使用它。
226
250
  - `type`:可选,`codex` 或 `claude`(别名 `cc`),便于用 `codenv use <type> <name>` 匹配。
227
251
  - `note`:显示在 `codenv list` 输出中。
@@ -0,0 +1,303 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.parseArgs = parseArgs;
4
+ exports.parseInitArgs = parseInitArgs;
5
+ exports.parseAddArgs = parseAddArgs;
6
+ exports.parseStatuslineArgs = parseStatuslineArgs;
7
+ const type_1 = require("../profile/type");
8
+ function parseArgs(argv) {
9
+ let configPath = null;
10
+ const args = [];
11
+ for (let i = 0; i < argv.length; i++) {
12
+ const arg = argv[i];
13
+ if (arg === "-h" || arg === "--help") {
14
+ return { args: [], configPath: null, help: true };
15
+ }
16
+ if (arg === "-c" || arg === "--config") {
17
+ configPath = argv[i + 1];
18
+ i++;
19
+ continue;
20
+ }
21
+ if (arg.startsWith("--config=")) {
22
+ configPath = arg.slice("--config=".length);
23
+ continue;
24
+ }
25
+ args.push(arg);
26
+ }
27
+ return { args, configPath, help: false };
28
+ }
29
+ function parseInitArgs(args) {
30
+ const result = { apply: true, print: false, shell: null };
31
+ for (let i = 0; i < args.length; i++) {
32
+ const arg = args[i];
33
+ if (arg === "--apply") {
34
+ result.apply = true;
35
+ result.print = false;
36
+ continue;
37
+ }
38
+ if (arg === "--print") {
39
+ result.print = true;
40
+ result.apply = false;
41
+ continue;
42
+ }
43
+ if (arg === "--shell") {
44
+ const val = args[i + 1];
45
+ if (!val)
46
+ throw new Error("Missing value for --shell.");
47
+ result.shell = val;
48
+ i++;
49
+ continue;
50
+ }
51
+ if (arg.startsWith("--shell=")) {
52
+ result.shell = arg.slice("--shell=".length);
53
+ continue;
54
+ }
55
+ throw new Error(`Unknown init argument: ${arg}`);
56
+ }
57
+ return result;
58
+ }
59
+ function parseAddArgs(args) {
60
+ const result = {
61
+ profile: null,
62
+ pairs: [],
63
+ note: null,
64
+ removeFiles: [],
65
+ commands: [],
66
+ unset: [],
67
+ type: null,
68
+ };
69
+ for (let i = 0; i < args.length; i++) {
70
+ const arg = args[i];
71
+ if (!result.profile && !arg.startsWith("-")) {
72
+ result.profile = arg;
73
+ continue;
74
+ }
75
+ if (arg === "-n" || arg === "--note") {
76
+ const val = args[i + 1];
77
+ if (!val)
78
+ throw new Error("Missing value for --note.");
79
+ result.note = val;
80
+ i++;
81
+ continue;
82
+ }
83
+ if (arg.startsWith("--note=")) {
84
+ result.note = arg.slice("--note=".length);
85
+ continue;
86
+ }
87
+ if (arg === "-t" || arg === "--type") {
88
+ const val = args[i + 1];
89
+ if (!val)
90
+ throw new Error("Missing value for --type.");
91
+ result.type = val;
92
+ i++;
93
+ continue;
94
+ }
95
+ if (arg.startsWith("--type=")) {
96
+ result.type = arg.slice("--type=".length);
97
+ continue;
98
+ }
99
+ if (arg === "-r" || arg === "--remove-file") {
100
+ const val = args[i + 1];
101
+ if (!val)
102
+ throw new Error("Missing value for --remove-file.");
103
+ result.removeFiles.push(val);
104
+ i++;
105
+ continue;
106
+ }
107
+ if (arg.startsWith("--remove-file=")) {
108
+ result.removeFiles.push(arg.slice("--remove-file=".length));
109
+ continue;
110
+ }
111
+ if (arg === "-x" || arg === "--command") {
112
+ const val = args[i + 1];
113
+ if (!val)
114
+ throw new Error("Missing value for --command.");
115
+ result.commands.push(val);
116
+ i++;
117
+ continue;
118
+ }
119
+ if (arg.startsWith("--command=")) {
120
+ result.commands.push(arg.slice("--command=".length));
121
+ continue;
122
+ }
123
+ if (arg === "-u" || arg === "--unset") {
124
+ const val = args[i + 1];
125
+ if (!val)
126
+ throw new Error("Missing value for --unset.");
127
+ result.unset.push(val);
128
+ i++;
129
+ continue;
130
+ }
131
+ if (arg.startsWith("--unset=")) {
132
+ result.unset.push(arg.slice("--unset=".length));
133
+ continue;
134
+ }
135
+ if (arg.includes("=")) {
136
+ result.pairs.push(arg);
137
+ continue;
138
+ }
139
+ throw new Error(`Unknown add argument: ${arg}`);
140
+ }
141
+ if (!result.profile) {
142
+ throw new Error("Missing profile name.");
143
+ }
144
+ if (result.type) {
145
+ const normalized = (0, type_1.normalizeType)(result.type);
146
+ if (!normalized) {
147
+ throw new Error(`Unknown type: ${result.type}`);
148
+ }
149
+ result.type = normalized;
150
+ }
151
+ return result;
152
+ }
153
+ function parseNumberFlag(value, flag) {
154
+ if (value === null || value === undefined || value === "") {
155
+ throw new Error(`Missing value for ${flag}.`);
156
+ }
157
+ const num = Number(value);
158
+ if (!Number.isFinite(num)) {
159
+ throw new Error(`Invalid number for ${flag}: ${value}`);
160
+ }
161
+ return num;
162
+ }
163
+ function parseStatuslineArgs(args) {
164
+ const result = {
165
+ format: "text",
166
+ cwd: null,
167
+ type: null,
168
+ profileKey: null,
169
+ profileName: null,
170
+ model: null,
171
+ usageToday: null,
172
+ usageTotal: null,
173
+ usageInput: null,
174
+ usageOutput: null,
175
+ syncUsage: false,
176
+ };
177
+ for (let i = 0; i < args.length; i++) {
178
+ const arg = args[i];
179
+ if (arg === "--format") {
180
+ const val = args[i + 1];
181
+ if (!val)
182
+ throw new Error("Missing value for --format.");
183
+ const normalized = val.toLowerCase();
184
+ if (normalized !== "text" && normalized !== "json") {
185
+ throw new Error(`Unknown format: ${val}`);
186
+ }
187
+ result.format = normalized;
188
+ i++;
189
+ continue;
190
+ }
191
+ if (arg.startsWith("--format=")) {
192
+ const val = arg.slice("--format=".length);
193
+ const normalized = val.toLowerCase();
194
+ if (normalized !== "text" && normalized !== "json") {
195
+ throw new Error(`Unknown format: ${val}`);
196
+ }
197
+ result.format = normalized;
198
+ continue;
199
+ }
200
+ if (arg === "--cwd") {
201
+ const val = args[i + 1];
202
+ if (!val)
203
+ throw new Error("Missing value for --cwd.");
204
+ result.cwd = val;
205
+ i++;
206
+ continue;
207
+ }
208
+ if (arg.startsWith("--cwd=")) {
209
+ result.cwd = arg.slice("--cwd=".length);
210
+ continue;
211
+ }
212
+ if (arg === "--type") {
213
+ const val = args[i + 1];
214
+ if (!val)
215
+ throw new Error("Missing value for --type.");
216
+ result.type = val;
217
+ i++;
218
+ continue;
219
+ }
220
+ if (arg.startsWith("--type=")) {
221
+ result.type = arg.slice("--type=".length);
222
+ continue;
223
+ }
224
+ if (arg === "--profile-key") {
225
+ const val = args[i + 1];
226
+ if (!val)
227
+ throw new Error("Missing value for --profile-key.");
228
+ result.profileKey = val;
229
+ i++;
230
+ continue;
231
+ }
232
+ if (arg.startsWith("--profile-key=")) {
233
+ result.profileKey = arg.slice("--profile-key=".length);
234
+ continue;
235
+ }
236
+ if (arg === "--profile-name") {
237
+ const val = args[i + 1];
238
+ if (!val)
239
+ throw new Error("Missing value for --profile-name.");
240
+ result.profileName = val;
241
+ i++;
242
+ continue;
243
+ }
244
+ if (arg.startsWith("--profile-name=")) {
245
+ result.profileName = arg.slice("--profile-name=".length);
246
+ continue;
247
+ }
248
+ if (arg === "--model") {
249
+ const val = args[i + 1];
250
+ if (!val)
251
+ throw new Error("Missing value for --model.");
252
+ result.model = val;
253
+ i++;
254
+ continue;
255
+ }
256
+ if (arg.startsWith("--model=")) {
257
+ result.model = arg.slice("--model=".length);
258
+ continue;
259
+ }
260
+ if (arg === "--usage-today") {
261
+ result.usageToday = parseNumberFlag(args[i + 1], "--usage-today");
262
+ i++;
263
+ continue;
264
+ }
265
+ if (arg.startsWith("--usage-today=")) {
266
+ result.usageToday = parseNumberFlag(arg.slice("--usage-today=".length), "--usage-today");
267
+ continue;
268
+ }
269
+ if (arg === "--usage-total") {
270
+ result.usageTotal = parseNumberFlag(args[i + 1], "--usage-total");
271
+ i++;
272
+ continue;
273
+ }
274
+ if (arg.startsWith("--usage-total=")) {
275
+ result.usageTotal = parseNumberFlag(arg.slice("--usage-total=".length), "--usage-total");
276
+ continue;
277
+ }
278
+ if (arg === "--usage-input") {
279
+ result.usageInput = parseNumberFlag(args[i + 1], "--usage-input");
280
+ i++;
281
+ continue;
282
+ }
283
+ if (arg.startsWith("--usage-input=")) {
284
+ result.usageInput = parseNumberFlag(arg.slice("--usage-input=".length), "--usage-input");
285
+ continue;
286
+ }
287
+ if (arg === "--usage-output") {
288
+ result.usageOutput = parseNumberFlag(args[i + 1], "--usage-output");
289
+ i++;
290
+ continue;
291
+ }
292
+ if (arg.startsWith("--usage-output=")) {
293
+ result.usageOutput = parseNumberFlag(arg.slice("--usage-output=".length), "--usage-output");
294
+ continue;
295
+ }
296
+ if (arg === "--sync-usage") {
297
+ result.syncUsage = true;
298
+ continue;
299
+ }
300
+ throw new Error(`Unknown statusline argument: ${arg}`);
301
+ }
302
+ return result;
303
+ }
@@ -0,0 +1,77 @@
1
+ "use strict";
2
+ /**
3
+ * Help message for codenv CLI
4
+ */
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.printHelp = printHelp;
7
+ function printHelp() {
8
+ const msg = `codenv - switch Claude/Codex env vars
9
+
10
+ Usage:
11
+ codenv list
12
+ codenv ls
13
+ codenv config
14
+ codenv auto
15
+ codenv use
16
+ codenv use <profile>
17
+ codenv use <type> <name>
18
+ codenv show <profile>
19
+ codenv show <type> <name>
20
+ codenv default <profile>
21
+ codenv default <type> <name>
22
+ codenv default --clear
23
+ codenv remove <profile> [<profile> ...]
24
+ codenv remove <type> <name> [<type> <name> ...]
25
+ codenv remove --all
26
+ codenv unset
27
+ codenv add <profile> KEY=VALUE [KEY=VALUE ...]
28
+ codenv add
29
+ codenv launch <codex|claude> [--] [args...]
30
+ codenv init
31
+ codenv statusline [options]
32
+
33
+ Options:
34
+ -c, --config <path> Path to config JSON
35
+ -h, --help Show help
36
+
37
+ Init options:
38
+ --apply Append shell helper to your shell rc (default)
39
+ --print Print helper snippet to stdout
40
+ --shell <bash|zsh|fish> Explicitly set the target shell
41
+
42
+ Add options:
43
+ -t, --type <codex|claude> Set profile type (alias: cc)
44
+ -n, --note <text> Set profile note
45
+ -r, --remove-file <path> Add a removeFiles entry (repeat)
46
+ -x, --command <cmd> Add a commands entry (repeat)
47
+ -u, --unset <KEY> Add a global unset key (repeat)
48
+
49
+ Statusline options:
50
+ --format <text|json> Output format (default: text)
51
+ --cwd <path> Override working directory
52
+ --type <type> Set profile type
53
+ --profile-key <key> Set profile key
54
+ --profile-name <name> Set profile name
55
+ --model <model> Set model label
56
+ --usage-today <n> Set today's token usage
57
+ --usage-total <n> Set total token usage
58
+ --usage-input <n> Set input token usage
59
+ --usage-output <n> Set output token usage
60
+ --sync-usage Sync usage from sessions before reading
61
+
62
+ Examples:
63
+ codenv init
64
+ codenv use codex primary
65
+ codenv list
66
+ codenv default codex primary
67
+ codenv remove codex primary
68
+ codenv remove codex primary claude default
69
+ codenv remove --all
70
+ codenv launch codex -- --help
71
+ codenv statusline --format json
72
+ CODE_ENV_CONFIG=~/.config/code-env/config.json codenv use claude default
73
+ codenv add --type codex primary OPENAI_BASE_URL=https://api.example.com/v1 OPENAI_API_KEY=YOUR_API_KEY
74
+ codenv add
75
+ `;
76
+ console.log(msg);
77
+ }
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.printHelp = exports.parseStatuslineArgs = exports.parseAddArgs = exports.parseInitArgs = exports.parseArgs = void 0;
4
+ /**
5
+ * CLI module exports
6
+ */
7
+ var args_1 = require("./args");
8
+ Object.defineProperty(exports, "parseArgs", { enumerable: true, get: function () { return args_1.parseArgs; } });
9
+ Object.defineProperty(exports, "parseInitArgs", { enumerable: true, get: function () { return args_1.parseInitArgs; } });
10
+ Object.defineProperty(exports, "parseAddArgs", { enumerable: true, get: function () { return args_1.parseAddArgs; } });
11
+ Object.defineProperty(exports, "parseStatuslineArgs", { enumerable: true, get: function () { return args_1.parseStatuslineArgs; } });
12
+ var help_1 = require("./help");
13
+ Object.defineProperty(exports, "printHelp", { enumerable: true, get: function () { return help_1.printHelp; } });
@@ -0,0 +1,81 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.addConfig = addConfig;
4
+ const match_1 = require("../profile/match");
5
+ const resolve_1 = require("../profile/resolve");
6
+ function addConfig(config, addArgs) {
7
+ if (!config.profiles || typeof config.profiles !== "object") {
8
+ config.profiles = {};
9
+ }
10
+ let targetKey = null;
11
+ let matchedByName = false;
12
+ if (Object.prototype.hasOwnProperty.call(config.profiles, addArgs.profile)) {
13
+ targetKey = addArgs.profile;
14
+ }
15
+ else {
16
+ const matches = (0, match_1.findProfileKeysByName)(config, addArgs.profile, addArgs.type);
17
+ if (matches.length === 1) {
18
+ targetKey = matches[0];
19
+ matchedByName = true;
20
+ }
21
+ else if (matches.length > 1) {
22
+ const hint = addArgs.type
23
+ ? `Use profile key: ${matches.join(", ")}`
24
+ : `Use: codenv add --type <type> ${addArgs.profile} ... (or profile key: ${matches.join(", ")})`;
25
+ throw new Error(`Multiple profiles named "${addArgs.profile}". ${hint}`);
26
+ }
27
+ }
28
+ if (!targetKey) {
29
+ targetKey = (0, resolve_1.generateProfileKey)(config);
30
+ matchedByName = true;
31
+ }
32
+ if (!config.profiles[targetKey]) {
33
+ config.profiles[targetKey] = {};
34
+ }
35
+ const profile = config.profiles[targetKey];
36
+ if (!profile.env || typeof profile.env !== "object") {
37
+ profile.env = {};
38
+ }
39
+ if (matchedByName) {
40
+ profile.name = addArgs.profile;
41
+ }
42
+ if (addArgs.type) {
43
+ profile.type = addArgs.type;
44
+ }
45
+ for (const pair of addArgs.pairs) {
46
+ const idx = pair.indexOf("=");
47
+ if (idx <= 0)
48
+ throw new Error(`Invalid KEY=VALUE: ${pair}`);
49
+ const key = pair.slice(0, idx);
50
+ const value = pair.slice(idx + 1);
51
+ profile.env[key] = value;
52
+ }
53
+ if (addArgs.note !== null && addArgs.note !== undefined) {
54
+ profile.note = addArgs.note;
55
+ }
56
+ if (addArgs.removeFiles.length > 0) {
57
+ if (!Array.isArray(profile.removeFiles))
58
+ profile.removeFiles = [];
59
+ for (const p of addArgs.removeFiles) {
60
+ if (!profile.removeFiles.includes(p))
61
+ profile.removeFiles.push(p);
62
+ }
63
+ }
64
+ if (addArgs.commands.length > 0) {
65
+ if (!Array.isArray(profile.commands))
66
+ profile.commands = [];
67
+ for (const cmd of addArgs.commands) {
68
+ if (!profile.commands.includes(cmd))
69
+ profile.commands.push(cmd);
70
+ }
71
+ }
72
+ if (addArgs.unset.length > 0) {
73
+ if (!Array.isArray(config.unset))
74
+ config.unset = [];
75
+ for (const key of addArgs.unset) {
76
+ if (!config.unset.includes(key))
77
+ config.unset.push(key);
78
+ }
79
+ }
80
+ return config;
81
+ }
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.printStatusline = exports.runLaunch = exports.printUnset = exports.printShow = exports.addConfig = exports.printList = exports.printUse = exports.buildUseLines = void 0;
4
+ /**
5
+ * Commands module exports
6
+ */
7
+ var use_1 = require("./use");
8
+ Object.defineProperty(exports, "buildUseLines", { enumerable: true, get: function () { return use_1.buildUseLines; } });
9
+ Object.defineProperty(exports, "printUse", { enumerable: true, get: function () { return use_1.printUse; } });
10
+ var list_1 = require("./list");
11
+ Object.defineProperty(exports, "printList", { enumerable: true, get: function () { return list_1.printList; } });
12
+ var add_1 = require("./add");
13
+ Object.defineProperty(exports, "addConfig", { enumerable: true, get: function () { return add_1.addConfig; } });
14
+ var show_1 = require("./show");
15
+ Object.defineProperty(exports, "printShow", { enumerable: true, get: function () { return show_1.printShow; } });
16
+ var unset_1 = require("./unset");
17
+ Object.defineProperty(exports, "printUnset", { enumerable: true, get: function () { return unset_1.printUnset; } });
18
+ var launch_1 = require("./launch");
19
+ Object.defineProperty(exports, "runLaunch", { enumerable: true, get: function () { return launch_1.runLaunch; } });
20
+ var statusline_1 = require("./statusline");
21
+ Object.defineProperty(exports, "printStatusline", { enumerable: true, get: function () { return statusline_1.printStatusline; } });