@lkangd/cc-env 1.2.0 → 1.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,110 @@
1
+ <p align="center">
2
+ <img src="./statics/logo.png" alt="cc-env" width="320" />
3
+ </p>
4
+
5
+ <p align="center">
6
+ <a href="https://www.npmjs.com/package/@lkangd/cc-env"><img src="https://img.shields.io/npm/v/@lkangd/cc-env.svg" alt="npm version" /></a>
7
+ <a href="https://www.npmjs.com/package/@lkangd/cc-env"><img src="https://img.shields.io/npm/dm/@lkangd/cc-env.svg" alt="npm downloads" /></a>
8
+ <a href="https://github.com/lkangd/cc-env/blob/main/LICENSE"><img src="https://img.shields.io/npm/l/@lkangd/cc-env.svg" alt="license" /></a>
9
+ </p>
10
+
11
+ <p align="center">Manage runtime environment variables for <a href="https://claude.ai/code">Claude Code</a></p>
12
+
13
+ <p align="center">
14
+ <a href="./README.md">English</a> | <a href="./README.zh.md">简体中文</a>
15
+ </p>
16
+
17
+ ---
18
+
19
+ ## Overview
20
+
21
+ `cc-env` is a CLI tool that lets you define, switch, and restore environment variable configurations for Claude Code — per project or via reusable presets. No more manually editing `settings.json` or juggling `.env` files across workspaces.
22
+
23
+ ## Installation
24
+
25
+ ### via npm
26
+
27
+ ```bash
28
+ npm install -g @lkangd/cc-env
29
+ ```
30
+
31
+ Requires Node.js `>=20.19.2`.
32
+
33
+ ### via Homebrew
34
+
35
+ ```bash
36
+ brew tap lkangd/tap
37
+ brew install cc-env
38
+ ```
39
+
40
+ ## Quick Start
41
+
42
+ ```bash
43
+ # 1. Initialize cc-env in your project
44
+ cc-env init
45
+
46
+ # 2. Create a preset with your environment variables
47
+ cc-env create
48
+
49
+ # 3. Run Claude Code with the preset applied
50
+ cc-env run
51
+ ```
52
+
53
+ ## Commands
54
+
55
+ | Command | Description |
56
+ |---|---|
57
+ | `cc-env init` | Initialize cc-env for the current project |
58
+ | `cc-env run [args...]` | Run Claude Code with merged environment variables |
59
+ | `cc-env restore` | Restore environment variables from a previous snapshot |
60
+ | `cc-env show` | List and view all saved presets |
61
+ | `cc-env create` | Create a new environment preset |
62
+ | `cc-env edit <name>` | Edit an existing preset |
63
+ | `cc-env rename <from> <to>` | Rename a preset |
64
+ | `cc-env delete` | Delete a saved preset |
65
+ | `cc-env doctor` | Check system health and configuration |
66
+ | `cc-env completion` | Generate shell completion script |
67
+
68
+ ## Global Options
69
+
70
+ ```
71
+ --verbose Enable verbose output
72
+ --quiet Suppress non-essential output
73
+ --no-interactive Disable interactive prompts (equivalent to -y)
74
+ ```
75
+
76
+ ## Shell Completion
77
+
78
+ ```bash
79
+ # bash
80
+ cc-env completion bash >> ~/.bashrc
81
+
82
+ # zsh
83
+ cc-env completion zsh >> ~/.zshrc
84
+
85
+ # fish
86
+ cc-env completion fish >> ~/.config/fish/completions/cc-env.fish
87
+ ```
88
+
89
+ ## Development
90
+
91
+ ```bash
92
+ # Install dependencies
93
+ npm install
94
+
95
+ # Run in dev mode
96
+ npm run dev
97
+
98
+ # Build
99
+ npm run build
100
+
101
+ # Run tests
102
+ npm test
103
+
104
+ # Run tests with coverage
105
+ npm run test:coverage
106
+ ```
107
+
108
+ ## License
109
+
110
+ ISC © [lkangd](https://github.com/lkangd)
package/README.zh.md ADDED
@@ -0,0 +1,110 @@
1
+ <p align="center">
2
+ <img src="./statics/logo.png" alt="cc-env" width="320" />
3
+ </p>
4
+
5
+ <p align="center">
6
+ <a href="https://www.npmjs.com/package/@lkangd/cc-env"><img src="https://img.shields.io/npm/v/@lkangd/cc-env.svg" alt="npm version" /></a>
7
+ <a href="https://www.npmjs.com/package/@lkangd/cc-env"><img src="https://img.shields.io/npm/dm/@lkangd/cc-env.svg" alt="npm downloads" /></a>
8
+ <a href="https://github.com/lkangd/cc-env/blob/main/LICENSE"><img src="https://img.shields.io/npm/l/@lkangd/cc-env.svg" alt="license" /></a>
9
+ </p>
10
+
11
+ <p align="center">为 <a href="https://claude.ai/code">Claude Code</a> 管理运行时环境变量</p>
12
+
13
+ <p align="center">
14
+ <a href="./README.md">English</a> | <a href="./README.zh.md">简体中文</a>
15
+ </p>
16
+
17
+ ---
18
+
19
+ ## 概述
20
+
21
+ `cc-env` 是一个 CLI 工具,让你可以为 Claude Code 定义、切换和恢复环境变量配置——支持按项目配置或使用可复用的预设。不再需要手动编辑 `settings.json` 或在不同工作区之间切换 `.env` 文件。
22
+
23
+ ## 安装
24
+
25
+ ### 通过 npm
26
+
27
+ ```bash
28
+ npm install -g @lkangd/cc-env
29
+ ```
30
+
31
+ 需要 Node.js `>=20.19.2`。
32
+
33
+ ### 通过 Homebrew
34
+
35
+ ```bash
36
+ brew tap lkangd/tap
37
+ brew install cc-env
38
+ ```
39
+
40
+ ## 快速开始
41
+
42
+ ```bash
43
+ # 1. 在项目中初始化 cc-env
44
+ cc-env init
45
+
46
+ # 2. 创建一个包含环境变量的预设
47
+ cc-env create
48
+
49
+ # 3. 使用预设运行 Claude Code
50
+ cc-env run
51
+ ```
52
+
53
+ ## 命令
54
+
55
+ | 命令 | 说明 |
56
+ |---|---|
57
+ | `cc-env init` | 为当前项目初始化 cc-env |
58
+ | `cc-env run [args...]` | 使用合并后的环境变量运行 Claude Code |
59
+ | `cc-env restore` | 从之前的快照恢复环境变量 |
60
+ | `cc-env show` | 列出并查看所有保存的预设 |
61
+ | `cc-env create` | 创建新的环境变量预设 |
62
+ | `cc-env edit <name>` | 编辑现有预设 |
63
+ | `cc-env rename <from> <to>` | 重命名预设 |
64
+ | `cc-env delete` | 删除保存的预设 |
65
+ | `cc-env doctor` | 检查系统健康状况和配置 |
66
+ | `cc-env completion` | 生成 shell 补全脚本 |
67
+
68
+ ## 全局选项
69
+
70
+ ```
71
+ --verbose 启用详细输出
72
+ --quiet 抑制非必要输出
73
+ --no-interactive 禁用交互式提示(等同于 -y)
74
+ ```
75
+
76
+ ## Shell 补全
77
+
78
+ ```bash
79
+ # bash
80
+ cc-env completion bash >> ~/.bashrc
81
+
82
+ # zsh
83
+ cc-env completion zsh >> ~/.zshrc
84
+
85
+ # fish
86
+ cc-env completion fish >> ~/.config/fish/completions/cc-env.fish
87
+ ```
88
+
89
+ ## 开发
90
+
91
+ ```bash
92
+ # 安装依赖
93
+ npm install
94
+
95
+ # 开发模式运行
96
+ npm run dev
97
+
98
+ # 构建
99
+ npm run build
100
+
101
+ # 运行测试
102
+ npm test
103
+
104
+ # 运行测试并生成覆盖率报告
105
+ npm run test:coverage
106
+ ```
107
+
108
+ ## 许可证
109
+
110
+ ISC © [lkangd](https://github.com/lkangd)
package/dist/cli.js CHANGED
File without changes
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@lkangd/cc-env",
3
- "version": "1.2.0",
4
- "description": "",
3
+ "version": "1.2.1",
4
+ "description": "Manage runtime environment variables for Claude Code",
5
5
  "homepage": "https://github.com/lkangd/cc-env#readme",
6
6
  "bugs": {
7
7
  "url": "https://github.com/lkangd/cc-env/issues"
@@ -17,7 +17,7 @@
17
17
  },
18
18
  "type": "module",
19
19
  "engines": {
20
- "node": ">=20.19.2 <21"
20
+ "node": ">=20.19.2"
21
21
  },
22
22
  "bin": {
23
23
  "cc-env": "dist/cli.js"
@@ -54,6 +54,7 @@
54
54
  "@types/gradient-string": "^1.1.6",
55
55
  "@types/node": "^20.19.0",
56
56
  "@types/react": "^19.1.10",
57
+ "@vitest/coverage-istanbul": "^3.2.4",
57
58
  "@vitest/coverage-v8": "^3.2.4",
58
59
  "execa": "^9.6.0",
59
60
  "react-test-renderer": "^19.2.5",
@@ -1,17 +0,0 @@
1
- import { toProcessEnvMap } from '../core/process-env.js';
2
- import { renderEnvSummary } from '../ink/summary.js';
3
- export function createDebugCommand({ projectEnvService, processEnv }) {
4
- return async function debug() {
5
- const projectEnv = await projectEnvService.read();
6
- await renderEnvSummary({
7
- title: 'Process Environment',
8
- description: 'Current process environment variables',
9
- env: toProcessEnvMap(processEnv)
10
- });
11
- await renderEnvSummary({
12
- title: 'Project Environment',
13
- description: 'Environment variables from .cc-env/env.json',
14
- env: projectEnv
15
- });
16
- };
17
- }
@@ -1,16 +0,0 @@
1
- export function createListPresetsCommand({ presetService, projectEnvService, renderList, }) {
2
- return async function listPresets() {
3
- const names = await presetService.listNames();
4
- const globalPresets = await Promise.all(names.map((name) => presetService.read(name).then((p) => ({ name, env: p.env, source: 'global' }))));
5
- const { env: projectEnv, name: projectName } = await projectEnvService.readWithMeta();
6
- const projectPreset = Object.keys(projectEnv).length > 0
7
- ? [{ name: projectName ?? 'project', env: projectEnv, source: 'project' }]
8
- : [];
9
- const presets = [...projectPreset, ...globalPresets];
10
- if (presets.length === 0) {
11
- console.log('No presets found.');
12
- return;
13
- }
14
- await renderList(presets);
15
- };
16
- }
package/dist/core/lock.js DELETED
@@ -1,25 +0,0 @@
1
- import { open } from 'node:fs/promises';
2
- import lockfile from 'proper-lockfile';
3
- import { ensureParentDir } from './fs.js';
4
- export async function withFileLock(filePath, run) {
5
- await ensureParentDir(filePath);
6
- const handle = await open(filePath, 'a+');
7
- try {
8
- const release = await lockfile.lock(filePath, {
9
- realpath: false,
10
- retries: {
11
- retries: 3,
12
- factor: 1,
13
- },
14
- });
15
- try {
16
- return await run();
17
- }
18
- finally {
19
- await release();
20
- }
21
- }
22
- finally {
23
- await handle.close();
24
- }
25
- }
@@ -1,27 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { useMemo, useState } from 'react';
3
- import { Box, Text, useApp, useInput } from 'ink';
4
- import { EnvEntries } from './summary.js';
5
- export function PresetListApp({ presets, }) {
6
- const { exit } = useApp();
7
- const [cursor, setCursor] = useState(0);
8
- const activePreset = presets[cursor];
9
- const entries = useMemo(() => activePreset
10
- ? Object.entries(activePreset.env).sort(([a], [b]) => a.localeCompare(b))
11
- : [], [activePreset]);
12
- useInput((input, key) => {
13
- if (key.escape || input.toLowerCase() === 'q') {
14
- exit();
15
- return;
16
- }
17
- if (key.upArrow || input === 'k') {
18
- setCursor((c) => Math.max(0, c - 1));
19
- return;
20
- }
21
- if (key.downArrow || input === 'j') {
22
- setCursor((c) => Math.min(presets.length - 1, c + 1));
23
- return;
24
- }
25
- });
26
- return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { children: "Preset list" }), _jsx(Text, { dimColor: true, children: "\u2191/k \u2193/j navigate \u00B7 q exit" }), _jsxs(Box, { marginTop: 1, children: [_jsxs(Box, { flexDirection: "column", width: 28, marginRight: 2, children: [_jsx(Text, { bold: true, color: "cyan", children: "Presets" }), _jsx(Box, { flexDirection: "column", marginTop: 1, children: presets.map((preset, index) => (_jsxs(Box, { children: [_jsx(Text, { children: index === cursor ? '❯ ' : ' ' }), _jsx(Text, { ...(preset.source === 'project' ? { color: 'yellow' } : {}), children: preset.name }), _jsxs(Text, { dimColor: true, children: [" (", preset.source, ")"] })] }, `${preset.source}:${preset.name}`))) })] }), _jsxs(Box, { flexDirection: "column", flexGrow: 1, borderStyle: "round", borderColor: "green", paddingX: 1, children: [_jsx(Text, { bold: true, color: "green", children: activePreset?.name ?? 'Preview' }), _jsx(Text, { dimColor: true, children: activePreset?.source === 'project' ? 'Project preset' : 'Global preset' }), _jsx(Box, { flexDirection: "column", marginTop: 1, children: _jsx(EnvEntries, { entries: entries }) })] })] })] }));
27
- }
@@ -1,26 +0,0 @@
1
- import { readFile } from 'node:fs/promises';
2
- import { configSchema } from '../core/schema.js';
3
- import { atomicWriteFile } from '../core/fs.js';
4
- import { resolveConfigPath } from '../core/paths.js';
5
- export function createConfigService(globalRoot) {
6
- const filePath = resolveConfigPath(globalRoot);
7
- return {
8
- async read() {
9
- try {
10
- const content = await readFile(filePath, 'utf8');
11
- return configSchema.parse(JSON.parse(content));
12
- }
13
- catch (error) {
14
- if (error.code === 'ENOENT') {
15
- return configSchema.parse({});
16
- }
17
- throw error;
18
- }
19
- },
20
- async write(config) {
21
- const parsed = configSchema.parse(config);
22
- await atomicWriteFile(filePath, `${JSON.stringify(parsed, null, 2)}\n`);
23
- return parsed;
24
- },
25
- };
26
- }
@@ -1,13 +0,0 @@
1
- import { envMapSchema } from '../core/schema.js';
2
- export function createRuntimeEnvService() {
3
- return {
4
- merge({ processEnv, settingsEnv, projectEnv, presetEnv, }) {
5
- return envMapSchema.parse({
6
- ...processEnv,
7
- ...settingsEnv,
8
- ...projectEnv,
9
- ...presetEnv,
10
- });
11
- },
12
- };
13
- }