@lkangd/cc-env 1.1.1 → 1.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 (71) hide show
  1. package/LICENSE +15 -0
  2. package/package.json +8 -1
  3. package/.claude/settings.json +0 -6
  4. package/.claude/settings.local.json +0 -8
  5. package/.nvmrc +0 -1
  6. package/CHANGELOG.md +0 -71
  7. package/docs/product-specs/index.draft.md +0 -106
  8. package/docs/product-specs/index.md +0 -911
  9. package/docs/product-specs/optional.md +0 -42
  10. package/docs/references/claude-code-env.md +0 -224
  11. package/docs/superpowers/plans/2026-04-24-cc-env-init-shell-migration.md +0 -1331
  12. package/docs/superpowers/plans/2026-04-24-cc-env.md +0 -1666
  13. package/docs/superpowers/plans/2026-04-26-preset-create-interactive-refactor.md +0 -1432
  14. package/docs/superpowers/specs/2026-04-24-cc-env-design.md +0 -438
  15. package/docs/superpowers/specs/2026-04-24-cc-env-init-shell-migration-design.md +0 -181
  16. package/docs/superpowers/specs/2026-04-26-preset-create-interactive-refactor-design.md +0 -78
  17. package/src/cli.ts +0 -340
  18. package/src/commands/init.ts +0 -139
  19. package/src/commands/preset/create.ts +0 -96
  20. package/src/commands/preset/delete.ts +0 -62
  21. package/src/commands/preset/show.ts +0 -51
  22. package/src/commands/restore.ts +0 -150
  23. package/src/commands/run.ts +0 -158
  24. package/src/core/errors.ts +0 -13
  25. package/src/core/find-claude.ts +0 -70
  26. package/src/core/format.ts +0 -29
  27. package/src/core/fs.ts +0 -18
  28. package/src/core/gitignore.ts +0 -26
  29. package/src/core/logger.ts +0 -11
  30. package/src/core/mask.ts +0 -17
  31. package/src/core/paths.ts +0 -41
  32. package/src/core/process-env.ts +0 -11
  33. package/src/core/schema.ts +0 -55
  34. package/src/core/spawn.ts +0 -36
  35. package/src/flows/init-flow.ts +0 -61
  36. package/src/flows/preset-create-flow.ts +0 -129
  37. package/src/flows/restore-flow.ts +0 -144
  38. package/src/ink/init-app.tsx +0 -110
  39. package/src/ink/preset-create-app.tsx +0 -451
  40. package/src/ink/preset-delete-app.tsx +0 -114
  41. package/src/ink/preset-show-app.tsx +0 -76
  42. package/src/ink/restore-app.tsx +0 -230
  43. package/src/ink/run-preset-select-app.tsx +0 -83
  44. package/src/ink/summary.tsx +0 -91
  45. package/src/services/claude-settings-env-service.ts +0 -72
  46. package/src/services/history-service.ts +0 -48
  47. package/src/services/preset-service.ts +0 -72
  48. package/src/services/project-env-service.ts +0 -128
  49. package/src/services/project-state-service.ts +0 -31
  50. package/src/services/settings-env-service.ts +0 -40
  51. package/src/services/shell-env-service.ts +0 -112
  52. package/src/types.d.ts +0 -19
  53. package/tests/cli/help.test.ts +0 -133
  54. package/tests/cli/init.test.ts +0 -76
  55. package/tests/cli/restore.test.ts +0 -172
  56. package/tests/commands/create.test.ts +0 -263
  57. package/tests/commands/output.test.ts +0 -119
  58. package/tests/commands/run.test.ts +0 -218
  59. package/tests/core/gitignore.test.ts +0 -98
  60. package/tests/core/paths.test.ts +0 -24
  61. package/tests/core/schema-mask.test.ts +0 -182
  62. package/tests/core/spawn.test.ts +0 -47
  63. package/tests/flows/init-flow.test.ts +0 -40
  64. package/tests/flows/preset-create-flow.test.ts +0 -225
  65. package/tests/flows/restore-flow.test.ts +0 -157
  66. package/tests/integration/init-restore.test.ts +0 -406
  67. package/tests/services/claude-shell.test.ts +0 -183
  68. package/tests/services/storage.test.ts +0 -143
  69. package/tsconfig.build.json +0 -9
  70. package/tsconfig.json +0 -22
  71. package/vitest.config.ts +0 -8
@@ -1,438 +0,0 @@
1
- # cc-env Design Spec
2
-
3
- Date: 2026-04-24
4
- Status: Approved for implementation
5
- Scope: Full v1
6
-
7
- ## 1. Objective
8
-
9
- Build a Node.js CLI named `cc-env` that injects a deterministic set of environment variables into a child process at runtime, primarily for Claude Code CLI usage, without relying on persistent shell mutation.
10
-
11
- The tool must support:
12
- - global presets for provider switching
13
- - project-level env overrides
14
- - migration from `~/.claude/settings.json`
15
- - history and restore flows
16
- - masked output for sensitive values
17
- - deterministic merge behavior
18
-
19
- ## 2. Product Decisions Confirmed
20
-
21
- The following decisions were confirmed during design:
22
-
23
- - Full v1 scope is in scope, not MVP-only.
24
- - Destructive or state-changing confirmation flows default to interactive confirmation, with `--yes` available for non-interactive automation.
25
- - `debug` supports explicit `--preset <name>` and also falls back to a default preset in `~/.cc-env/config.json`.
26
- - `preset create` interactive variable enumeration defaults to `process.env` and `~/.claude/settings.json.env`, and may additionally import current project env when present.
27
- - `init` migrates selected keys from `~/.claude/settings.json.env` into a global preset chosen interactively by the user, and also writes a history record.
28
- - Migrated variables belong to the global configuration domain, not the project override layer.
29
- - `restore` supports choosing the destination interactively: restore to `settings.json.env` or to a global preset.
30
- - Preset secrets are allowed to be stored in plaintext on disk in v1; outputs and logs must still mask sensitive values.
31
- - Project env is part of full v1. It is managed indirectly through `preset create` flows: users can import current project env into a create flow and can choose to save create results back to the current project env file.
32
- - Action commands use subcommands rather than long flags. Behavior modifiers still use flags.
33
- - If both `./.cc-env/env.json` and `./.cc-env/env.yaml` exist, the command fails with a human-readable error.
34
- - The design approach is a layered command-oriented architecture rather than a monolithic CLI or heavyweight domain abstraction.
35
-
36
- ## 3. Command Model
37
-
38
- Action-style operations are represented as subcommands:
39
-
40
- ```bash
41
- cc-env run [--preset <name>] [--dry-run] <command> [args...]
42
- cc-env init [--yes]
43
- cc-env restore [--yes]
44
- cc-env preset create [--file <path>] [KEY=VALUE ...]
45
- cc-env preset list
46
- cc-env preset show <name>
47
- cc-env preset delete <name> [--yes]
48
- cc-env preset edit <name>
49
- cc-env debug [--preset <name>]
50
- ```
51
-
52
- ### 3.1 Command semantics
53
-
54
- #### `cc-env run`
55
- - Loads env inputs from all supported sources.
56
- - Resolves the effective preset from `--preset` or the default preset in `~/.cc-env/config.json`.
57
- - If neither is available, fails with a human-readable preset selection error.
58
- - Merges environment variables in the deterministic order defined below.
59
- - Spawns the target child process with `stdio: 'inherit'`.
60
- - Uses `cross-spawn`, not `exec()`.
61
- - In `--dry-run`, prints what would run and the masked env values that would be injected, but does not spawn and does not mutate state.
62
-
63
- #### `cc-env init`
64
- - Reads `~/.claude/settings.json`.
65
- - If no `env` field exists, prints `No env field found` and exits successfully without writing state.
66
- - Uses Ink UI to let the user select which keys to migrate.
67
- - Uses Ink UI to let the user name or select the destination global preset.
68
- - Shows a preview of which keys will be written to the preset and removed from `settings.json.env`.
69
- - Confirms before applying unless `--yes` is provided.
70
- - On apply:
71
- - writes selected keys to the destination global preset
72
- - writes a history record
73
- - removes the selected keys from `~/.claude/settings.json.env`
74
-
75
- #### `cc-env restore`
76
- - Lists available history records in Ink.
77
- - Lets the user select a history record.
78
- - Lets the user choose the restore target:
79
- - `~/.claude/settings.json.env`
80
- - a global preset
81
- - Detects key collisions and asks for overwrite confirmation unless `--yes` is provided.
82
- - Applies the restore and records the result in logs.
83
-
84
- #### `cc-env preset create`
85
- Supports three input modes:
86
- - interactive selection
87
- - file import with `--file`
88
- - inline `KEY=VALUE` arguments
89
-
90
- Interactive mode:
91
- - lets the user choose variables from:
92
- - `process.env`
93
- - `~/.claude/settings.json.env`
94
- - may additionally import current project env when present
95
- - then lets the user choose the save target:
96
- - a global preset
97
- - the current project env file
98
-
99
- Project env save behavior:
100
- - if a project env file already exists, preserve its existing format
101
- - if neither project env file exists, default to writing `./.cc-env/env.json`
102
-
103
- #### `cc-env preset list`
104
- - Prints a compact tabular list of presets with name, updated date, and variable count.
105
- - No Ink UI.
106
-
107
- #### `cc-env preset show <name>`
108
- - Prints the preset content with sensitive values masked.
109
- - No Ink UI.
110
-
111
- #### `cc-env preset delete <name>`
112
- - Requires confirmation unless `--yes` is provided.
113
- - Deletes the preset file under file lock.
114
-
115
- #### `cc-env preset edit <name>`
116
- - Opens the preset in `$EDITOR`.
117
- - Validation is re-run after save.
118
- - Invalid edited content is rejected with a human-readable error.
119
-
120
- #### `cc-env debug`
121
- - Resolves preset from `--preset` or default config.
122
- - Loads all env sources.
123
- - Computes the final merged env.
124
- - Prints masked effective env and source participation details.
125
- - No Ink UI.
126
-
127
- ## 4. Storage Layout
128
-
129
- Global state lives under:
130
-
131
- ```text
132
- ~/.cc-env/
133
- config.json
134
- presets/
135
- <name>.json
136
- history/
137
- <timestamp>.json
138
- logs/
139
- cc-env.log
140
- ```
141
-
142
- Project state lives under the working directory:
143
-
144
- ```text
145
- ./.cc-env/
146
- env.json
147
- env.yaml
148
- ```
149
-
150
- Exactly zero or one project env file may exist. If both exist, the command fails.
151
-
152
- ## 5. Data Model
153
-
154
- ## 5.1 Preset schema
155
-
156
- Each preset file is JSON and validates with zod:
157
-
158
- ```json
159
- {
160
- "name": "openai",
161
- "createdAt": "2026-04-24T10:00:00Z",
162
- "updatedAt": "2026-04-24T10:00:00Z",
163
- "env": {
164
- "ANTHROPIC_BASE_URL": "https://api.openai.com",
165
- "ANTHROPIC_AUTH_TOKEN": "sk-xxx"
166
- }
167
- }
168
- ```
169
-
170
- Rules:
171
- - `env` must be a flat object
172
- - all values must be strings
173
- - all keys must match `^[A-Z0-9_]+$`
174
-
175
- ## 5.2 History schema
176
-
177
- Each history record is JSON and validates with zod:
178
-
179
- ```json
180
- {
181
- "timestamp": "2026-04-24T10:00:00Z",
182
- "action": "init",
183
- "movedKeys": ["ANTHROPIC_BASE_URL"],
184
- "backup": {
185
- "ANTHROPIC_BASE_URL": "https://api.anthropic.com"
186
- },
187
- "targetType": "preset",
188
- "targetName": "openai"
189
- }
190
- ```
191
-
192
- Required semantics:
193
- - `timestamp` identifies when the state-changing operation happened
194
- - `action` identifies the originating action, such as `init` or `restore`
195
- - `movedKeys` records which keys were affected
196
- - `backup` stores the source values needed for restore
197
- - `targetType` is either `settings` or `preset`
198
- - `targetName` is present when `targetType` is `preset`
199
-
200
- ## 5.3 Config schema
201
-
202
- `~/.cc-env/config.json` stores stable global tool settings. For v1, it only needs:
203
-
204
- ```json
205
- {
206
- "defaultPreset": "openai"
207
- }
208
- ```
209
-
210
- ## 6. Deterministic Merge Rules
211
-
212
- The effective env must always be computed in the same precedence order:
213
-
214
- 1. `~/.claude/settings.json.env`
215
- 2. `process.env`
216
- 3. selected preset
217
- 4. project env
218
-
219
- Later sources override earlier sources.
220
-
221
- This order is implemented once in a dedicated runtime env service and reused by both `run` and `debug`.
222
-
223
- ## 7. Architecture
224
-
225
- The implementation uses a layered structure:
226
-
227
- - **CLI layer** — Commander setup and top-level process exit handling
228
- - **Command layer** — one entry per command or subcommand
229
- - **Service layer** — business logic for presets, history, settings migration, project env, and runtime merge
230
- - **Core layer** — schemas, masking, paths, locking, errors, logger, and process spawning helpers
231
-
232
- Suggested source layout:
233
-
234
- ```text
235
- src/
236
- cli.ts
237
- commands/
238
- run.ts
239
- init.ts
240
- restore.ts
241
- debug.ts
242
- preset/
243
- create.ts
244
- list.ts
245
- show.ts
246
- delete.ts
247
- edit.ts
248
- services/
249
- preset-service.ts
250
- project-env-service.ts
251
- settings-env-service.ts
252
- history-service.ts
253
- runtime-env-service.ts
254
- core/
255
- schema.ts
256
- mask.ts
257
- paths.ts
258
- lock.ts
259
- errors.ts
260
- logger.ts
261
- spawn.ts
262
- ```
263
-
264
- ### 7.1 Architectural rule
265
-
266
- All computation of the final merged env must happen in `runtime-env-service`. No command may reimplement merge precedence independently.
267
-
268
- ## 8. Interaction Design
269
-
270
- Ink is used only for complex interactive flows:
271
- - `init`
272
- - `restore`
273
- - interactive `preset create`
274
-
275
- Ink is not used for:
276
- - `preset list`
277
- - `preset show`
278
- - `debug`
279
-
280
- ### 8.1 `init` interaction flow
281
- 1. Load `settings.json.env`
282
- 2. Show selectable keys and masked previews
283
- 3. Ask for destination global preset name
284
- 4. Show operation preview
285
- 5. Confirm or auto-apply with `--yes`
286
- 6. Apply write operations under lock
287
-
288
- ### 8.2 `restore` interaction flow
289
- 1. List history records
290
- 2. Select one record
291
- 3. Select restore target
292
- 4. Detect collisions
293
- 5. Ask overwrite confirmation or auto-overwrite with `--yes`
294
- 6. Apply write operations under lock
295
-
296
- ### 8.3 `preset create` interaction flow
297
- 1. Determine input mode
298
- 2. For interactive mode, select variable source(s)
299
- 3. Select keys and values
300
- 4. Select destination target
301
- 5. Preview result
302
- 6. Apply write operations under lock
303
-
304
- ## 9. Security and Privacy Rules
305
-
306
- Sensitive values may be stored in preset files in plaintext in v1, but must never be emitted raw in user-facing output or logs when the key name indicates a secret.
307
-
308
- Masking applies to keys matching at least:
309
- - `*_TOKEN`
310
- - `*_KEY`
311
- - `*_SECRET`
312
- - `*_PASSWORD`
313
-
314
- Masked output should preserve enough prefix to identify the value, for example:
315
-
316
- ```text
317
- sk-123456********
318
- ```
319
-
320
- The logger must never write full secret values.
321
-
322
- ## 10. Concurrency and File Safety
323
-
324
- Every state-changing write uses file locking via `proper-lockfile`.
325
-
326
- This includes:
327
- - writing presets
328
- - deleting presets
329
- - editing presets after validation
330
- - mutating `~/.claude/settings.json`
331
- - writing history records
332
- - writing project env files
333
- - updating `config.json`
334
-
335
- The system must prefer reversible writes:
336
- - read current state
337
- - validate intended new state
338
- - write atomically where possible
339
- - record history for destructive migrations and restore operations
340
-
341
- ## 11. Error Handling
342
-
343
- All user-facing errors are short and human-readable.
344
-
345
- Rules:
346
- - no stack traces in normal CLI output
347
- - non-zero exit codes for failure
348
- - distinguish argument errors from business-logic failures
349
-
350
- Suggested exit code policy:
351
- - `1` — runtime or business error
352
- - `2` — invalid CLI usage or argument validation
353
-
354
- Examples:
355
- - `Preset not found: openai`
356
- - `Project env conflict: env.json and env.yaml both exist`
357
- - `No env field found`
358
-
359
- ## 12. Logging
360
-
361
- Log file path:
362
-
363
- ```text
364
- ~/.cc-env/logs/cc-env.log
365
- ```
366
-
367
- Log entries include:
368
- - timestamp
369
- - command
370
- - result
371
- - error summary when present
372
-
373
- Logs must not include raw secret values.
374
-
375
- ## 13. Testing Strategy
376
-
377
- ### 13.1 Unit tests
378
- Cover:
379
- - zod schema validation
380
- - env merge precedence
381
- - secret masking
382
- - default preset fallback
383
- - project env conflict detection
384
- - project env format preservation
385
-
386
- ### 13.2 Integration tests
387
- Cover:
388
- - `run --dry-run`
389
- - `preset create --file`
390
- - inline `preset create`
391
- - `init` migration side effects
392
- - `restore` side effects
393
- - `debug` output shape
394
-
395
- ### 13.3 Interaction tests
396
- For Ink flows, test:
397
- - key selection
398
- - target selection
399
- - overwrite confirmation
400
- - `--yes` bypass behavior
401
-
402
- Tests should verify decisions and side effects, not terminal styling.
403
-
404
- ## 14. Implementation Boundaries
405
-
406
- This v1 does not include:
407
- - shell mutation
408
- - token lifecycle management
409
- - secret encryption or OS keychain integration
410
- - sandboxing
411
- - Claude CLI hooking
412
- - project env dedicated management commands outside the `preset create` flow
413
-
414
- ## 15. Recommended Implementation Sequence
415
-
416
- 1. Initialize TypeScript project structure and build/test tooling
417
- 2. Implement core schema, path, mask, error, and lock utilities
418
- 3. Implement preset storage service and config service
419
- 4. Implement project env and settings env readers/writers
420
- 5. Implement runtime env merge service
421
- 6. Implement `preset list/show/create/delete/edit`
422
- 7. Implement `debug`
423
- 8. Implement `run` with `--dry-run`
424
- 9. Implement `init`
425
- 10. Implement `restore`
426
- 11. Add interaction tests and integration coverage
427
-
428
- ## 16. Success Criteria
429
-
430
- The design is successful when:
431
- - the same inputs always produce the same merged env
432
- - the tool can switch provider settings without shell mutation
433
- - global presets and project env can be combined predictably
434
- - migration from `~/.claude/settings.json.env` is reversible
435
- - restore can target either settings or preset destination
436
- - all secret-like output is masked
437
- - all state-changing writes are lock-protected
438
- - non-interactive automation is possible via `--yes` and `--dry-run`
@@ -1,181 +0,0 @@
1
- # cc-env init shell migration redesign
2
-
3
- ## Goal
4
-
5
- Redefine `cc-env init` so it migrates selected env keys out of Claude Code's home-directory settings files and into shell-level global environment configuration. The command must stop creating presets. Its purpose is to remove startup-time env overrides from `~/.claude/settings.json` and `~/.claude/settings.local.json`, then make those values available to new terminal sessions through managed shell config blocks.
6
-
7
- ## Scope
8
-
9
- This redesign changes:
10
-
11
- - `init` input sources and migration target
12
- - history shape for init migrations
13
- - `restore` behavior for init history
14
- - supporting services for Claude settings files and shell config files
15
-
16
- This redesign does not change:
17
-
18
- - preset CRUD behavior
19
- - project env file behavior
20
- - runtime merge precedence beyond the already-approved `process < settings < project < preset`
21
-
22
- ## Required init behavior
23
-
24
- ### Input sources
25
-
26
- `init` reads only these two files in the user's home Claude directory:
27
-
28
- - `~/.claude/settings.json`
29
- - `~/.claude/settings.local.json`
30
-
31
- For each file, `init` reads only its `env` field. If both files are missing, `init` exits with an error.
32
-
33
- If the same env key exists in both files, `settings.local.json` wins for the effective migration value.
34
-
35
- ### Selection behavior
36
-
37
- `init` builds a candidate view from the union of both `env` maps.
38
-
39
- These six keys are always selected by default and cannot be deselected:
40
-
41
- - `ANTHROPIC_AUTH_TOKEN`
42
- - `ANTHROPIC_BASE_URL`
43
- - `ANTHROPIC_DEFAULT_HAIKU_MODEL`
44
- - `ANTHROPIC_DEFAULT_OPUS_MODEL`
45
- - `ANTHROPIC_DEFAULT_SONNET_MODEL`
46
- - `ANTHROPIC_REASONING_MODEL`
47
-
48
- The user may add other discovered keys to the migration set.
49
-
50
- If no selected key resolves to an effective value after applying `settings.local.json` precedence, `init` exits with an error instead of writing empty shell config.
51
-
52
- ### Migration behavior
53
-
54
- After confirmation, `init` performs three operations:
55
-
56
- 1. Remove the selected keys from `~/.claude/settings.json` `env`
57
- 2. Remove the selected keys from `~/.claude/settings.local.json` `env`
58
- 3. Write the effective migrated values to managed shell config blocks for:
59
- - `~/.zshrc`
60
- - `~/.bashrc`
61
- - `~/.config/fish/config.fish`
62
-
63
- Shell writes guarantee the values are available to newly opened terminals. Already-running terminals are not updated in place.
64
-
65
- `init` no longer creates or updates any preset.
66
-
67
- ## Managed shell block design
68
-
69
- Each supported shell file gets a `cc-env` managed block. `cc-env` may only create, replace, or remove content inside its own block.
70
-
71
- ### zsh/bash block
72
-
73
- ```sh
74
- # >>> cc-env >>>
75
- export KEY="value"
76
- # <<< cc-env <<<
77
- ```
78
-
79
- ### fish block
80
-
81
- ```fish
82
- # >>> cc-env >>>
83
- set -gx KEY "value"
84
- # <<< cc-env <<<
85
- ```
86
-
87
- Behavior rules:
88
-
89
- - If the target shell file does not exist, create it and write the managed block
90
- - If the managed block exists, replace the entire block
91
- - If the managed block does not exist, append a new block
92
- - Do not inspect or modify unrelated user content outside the managed block
93
-
94
- ## Service boundaries
95
-
96
- ### Claude settings env service
97
-
98
- Introduce a dedicated service for Claude home settings files. It is responsible for:
99
-
100
- - reading `env` from `~/.claude/settings.json`
101
- - reading `env` from `~/.claude/settings.local.json`
102
- - writing updated `env` maps back to each file independently
103
- - treating missing files as missing sources rather than project-local defaults
104
-
105
- This service should expose per-file data so history and restore can preserve source boundaries.
106
-
107
- ### Shell env service
108
-
109
- Introduce a dedicated service for shell config files. It is responsible for:
110
-
111
- - rendering shell-specific export syntax
112
- - inserting/replacing/removing the `cc-env` managed block
113
- - reading current managed keys when needed for restore
114
- - handling zsh, bash, and fish paths independently
115
-
116
- This service should not parse or rewrite arbitrary user shell config beyond its own block.
117
-
118
- ## History redesign
119
-
120
- The current init history model is too narrow because it stores a single backup map and assumes restore targets are either `settings` or `preset`.
121
-
122
- Init history must be expanded so restore can reverse the migration without guessing. An init record must contain at least:
123
-
124
- - `timestamp`
125
- - `action: 'init'`
126
- - `migratedKeys`
127
- - `settingsBackup` — keys removed from `~/.claude/settings.json`
128
- - `settingsLocalBackup` — keys removed from `~/.claude/settings.local.json`
129
- - `shellWrites` — which shell files were written and which effective key/value pairs were placed there
130
-
131
- Restore logic must rely on this persisted data rather than recomputing source ownership later.
132
-
133
- ## Restore behavior for init history
134
-
135
- For an init-created history entry, `restore` performs a two-way reversal:
136
-
137
- 1. Remove the migrated keys from the `cc-env` managed blocks in zsh, bash, and fish
138
- 2. Restore the backed-up keys to their original Claude settings source files:
139
- - `settingsBackup` back to `~/.claude/settings.json`
140
- - `settingsLocalBackup` back to `~/.claude/settings.local.json`
141
-
142
- If a shell file was not present when the init record was created, restore should only touch files listed in that record's `shellWrites`.
143
-
144
- Restore for non-init history should continue using its existing target-driven flow unless deliberately refactored as part of the implementation.
145
-
146
- ## Error handling
147
-
148
- `init` should fail with a `CliError` when:
149
-
150
- - both Claude settings files are missing
151
- - no selected key has an effective value to migrate
152
- - a required settings file cannot be parsed
153
- - a shell config file cannot be updated
154
-
155
- `restore` should fail with a `CliError` when:
156
-
157
- - the requested history entry does not exist
158
- - a recorded shell target cannot be updated
159
- - a recorded settings target cannot be restored
160
-
161
- ## Testing
162
-
163
- Add or update tests for:
164
-
165
- - reading env from both Claude settings files
166
- - `settings.local.json` overriding `settings.json` for effective migrated values
167
- - the six required keys being preselected and non-removable in init flow state
168
- - writing zsh/bash/fish managed blocks
169
- - replacing an existing managed block without touching surrounding content
170
- - removing managed keys during restore
171
- - recording per-file Claude settings backups in history
172
- - restoring those backups to the correct source file
173
- - erroring when both settings files are absent
174
- - erroring when no selected key resolves to a value
175
-
176
- ## Implementation notes
177
-
178
- - Keep the existing codebase pattern of small services plus thin command handlers
179
- - Prefer extending existing restore flow carefully over broad refactors
180
- - Do not add preset compatibility shims to init
181
- - Do not treat current working directory settings files as init inputs anymore
@@ -1,78 +0,0 @@
1
- # Preset Create 交互重构设计
2
-
3
- ## 目标
4
-
5
- 将 `preset create` 命令从半交互式(支持 CLI 参数直接执行)改为全交互式,去掉所有 CLI 参数(`-n`、`-f`、`--project`、`[pairs...]`),完全通过 ink UI 引导用户完成 preset 创建。
6
-
7
- ## 交互流程
8
-
9
- ```
10
- source → filePath? → keys → manualInput? → name → destination → confirm → done
11
- ```
12
-
13
- ### 步骤说明
14
-
15
- 1. **source** — 选择数据来源:「文件导入(默认)」或「手动输入」,回车确认
16
- 2. **filePath**(仅文件导入)— 文本输入框输入文件路径,回车确认。支持 `.yaml`/`.yml`/`.json` 格式。读取失败时显示红色错误提示,允许重新输入
17
- 3. **keys**(仅文件导入)— 复用 init 的 checkbox 模式(j/k 上下、空格勾选、回车确认),展示从文件解析出的 key 供用户勾选
18
- 4. **manualInput**(仅手动输入)— 文本输入框输入 `KEY=VALUE` 回车添加,已添加的 key-value 实时显示,输入 `q` 结束
19
- 5. **name** — 文本输入框输入 preset 名称,回车确认
20
- 6. **destination** — 选择「全局 preset」或「项目 preset」,回车确认
21
- 7. **confirm** — 复用 `EnvSummary` 组件展示来源文件(如有)、保存路径、key-value 对,回车确认保存,`q` 取消
22
-
23
- 全局按键:`q`/Escape 在任意步骤退出。
24
-
25
- ## 架构变更
26
-
27
- ### Flow 状态机 (`src/flows/preset-create-flow.ts`)
28
-
29
- 从当前 5 步扩展为 7 步条件分支流程。State 新增字段:
30
-
31
- - `source: 'file' | 'manual'`
32
- - `filePath: string`
33
- - `presetName: string`
34
- - `destination: 'global' | 'project'`
35
- - `env: EnvMap`(解析后的键值对)
36
-
37
- 路径分支:
38
- - `source='file'` → `filePath` → `keys` → `name` → `destination` → `confirm` → `done`
39
- - `source='manual'` → `manualInput` → `name` → `destination` → `confirm` → `done`
40
-
41
- ### Ink 组件 (`src/ink/preset-create-app.tsx`)
42
-
43
- 扩展渲染逻辑覆盖所有 7 个步骤。每步根据 flow state 的当前 step 渲染对应 UI。keys 步骤复用 init-app 的 checkbox 模式(j/k/空格/回车)。
44
-
45
- ### 命令层 (`src/commands/preset/create.ts`)
46
-
47
- - 移除所有参数(`name`、`file`、`pairs`、`project`),命令函数无参数
48
- - 不再有"有参数直接执行 vs 无参数交互"的分支
49
- - 只调用 `renderFlow()` 拿结果,写入对应位置
50
- - `buildPlaceholderEnv` 删除
51
- - `readEnvFile` 和 `parseInlinePairs` 保留,由 ink 组件在交互中通过 renderFlow 回调调用
52
-
53
- ### CLI 注册 (`src/cli.ts`)
54
-
55
- - 移除 `-n`、`-f`、`--project` flag 和 `[pairs...]` 参数
56
- - 只保留裸命令 `preset create`
57
-
58
- ### 服务接口
59
-
60
- `presetService.write()` 和 `projectEnvService.write()` 保持现有签名不变。
61
-
62
- ## 文件解析逻辑
63
-
64
- 在 `readEnvFile` 中增加 JSON env 字段处理:如果解析结果是对象且存在 `env` 字段(且 `env` 是对象),则使用 `env` 内的字段;否则使用第一层字段。
65
-
66
- ## 错误处理
67
-
68
- - 文件不存在 / 无法读取 → 红色提示,停留在 filePath 步骤
69
- - 文件格式不支持(非 .yaml/.yml/.json)→ 红色提示,重新输入
70
- - 文件内容解析失败 → 红色提示,重新输入
71
- - preset 名称为空 → 提示必须输入,停留在 name 步骤
72
- - preset 名称已存在 → 提示已存在,可选择覆盖或重新输入
73
-
74
- ## 测试
75
-
76
- - `readEnvFile` 的 JSON env 字段提取逻辑补单测
77
- - flow 状态机的条件分支路径补单测(file 路径 vs manual 路径)
78
- - 命令函数验证 renderFlow 结果正确路由到对应 service