xling 0.0.2 → 0.2.4
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 +229 -1
- package/dist/base-BlaKDtDu.d.ts +43 -0
- package/dist/base-BlaKDtDu.d.ts.map +1 -0
- package/dist/base-Cm9xh6sW.js +69 -0
- package/dist/base-Cm9xh6sW.js.map +1 -0
- package/dist/claude-l3zomoHg.js +209 -0
- package/dist/claude-l3zomoHg.js.map +1 -0
- package/dist/claudeDefault-Fk1-kHLE.js +19 -0
- package/dist/claudeDefault-Fk1-kHLE.js.map +1 -0
- package/dist/codex-CR8xqNrZ.js +85 -0
- package/dist/codex-CR8xqNrZ.js.map +1 -0
- package/dist/commands/settings/get.d.ts +24 -0
- package/dist/commands/settings/get.d.ts.map +1 -0
- package/dist/commands/settings/get.js +118 -0
- package/dist/commands/settings/get.js.map +1 -0
- package/dist/commands/settings/inspect.d.ts +19 -0
- package/dist/commands/settings/inspect.d.ts.map +1 -0
- package/dist/commands/settings/inspect.js +88 -0
- package/dist/commands/settings/inspect.js.map +1 -0
- package/dist/commands/settings/list.d.ts +25 -0
- package/dist/commands/settings/list.d.ts.map +1 -0
- package/dist/commands/settings/list.js +147 -0
- package/dist/commands/settings/list.js.map +1 -0
- package/dist/commands/settings/set.d.ts +22 -0
- package/dist/commands/settings/set.d.ts.map +1 -0
- package/dist/commands/settings/set.js +83 -0
- package/dist/commands/settings/set.js.map +1 -0
- package/dist/commands/settings/switch.d.ts +27 -0
- package/dist/commands/settings/switch.d.ts.map +1 -0
- package/dist/commands/settings/switch.js +164 -0
- package/dist/commands/settings/switch.js.map +1 -0
- package/dist/dispatcher-JXm3OqRQ.js +76 -0
- package/dist/dispatcher-JXm3OqRQ.js.map +1 -0
- package/dist/domain/interfaces.d.ts +3 -0
- package/dist/domain/interfaces.js +1 -0
- package/dist/domain/types.d.ts +2 -0
- package/dist/domain/types.js +1 -0
- package/dist/domain/validators.d.ts +82 -0
- package/dist/domain/validators.d.ts.map +1 -0
- package/dist/domain/validators.js +58 -0
- package/dist/domain/validators.js.map +1 -0
- package/dist/editor-D4qoje1V.js +32 -0
- package/dist/editor-D4qoje1V.js.map +1 -0
- package/dist/errors-CAZ5k5YT.js +89 -0
- package/dist/errors-CAZ5k5YT.js.map +1 -0
- package/dist/format-Cqecj3RS.js +229 -0
- package/dist/format-Cqecj3RS.js.map +1 -0
- package/dist/fsStore-i-3731TM.js +122 -0
- package/dist/fsStore-i-3731TM.js.map +1 -0
- package/dist/gemini-aNNm-QGE.js +52 -0
- package/dist/gemini-aNNm-QGE.js.map +1 -0
- package/dist/interfaces-CriQW6hF.d.ts +41 -0
- package/dist/interfaces-CriQW6hF.d.ts.map +1 -0
- package/dist/run.d.ts +1 -0
- package/dist/run.js +15 -0
- package/dist/run.js.map +1 -0
- package/dist/services/settings/adapters/base.d.ts +4 -0
- package/dist/services/settings/adapters/base.js +5 -0
- package/dist/services/settings/adapters/claude.d.ts +43 -0
- package/dist/services/settings/adapters/claude.d.ts.map +1 -0
- package/dist/services/settings/adapters/claude.js +9 -0
- package/dist/services/settings/adapters/codex.d.ts +45 -0
- package/dist/services/settings/adapters/codex.d.ts.map +1 -0
- package/dist/services/settings/adapters/codex.js +6 -0
- package/dist/services/settings/adapters/gemini.d.ts +32 -0
- package/dist/services/settings/adapters/gemini.d.ts.map +1 -0
- package/dist/services/settings/adapters/gemini.js +6 -0
- package/dist/services/settings/dispatcher.d.ts +36 -0
- package/dist/services/settings/dispatcher.d.ts.map +1 -0
- package/dist/services/settings/dispatcher.js +12 -0
- package/dist/services/settings/fsStore.d.ts +46 -0
- package/dist/services/settings/fsStore.d.ts.map +1 -0
- package/dist/services/settings/fsStore.js +4 -0
- package/dist/services/settings/templates/claudeDefault.d.ts +20 -0
- package/dist/services/settings/templates/claudeDefault.d.ts.map +1 -0
- package/dist/services/settings/templates/claudeDefault.js +3 -0
- package/dist/types-BfNSo2rs.d.ts +71 -0
- package/dist/types-BfNSo2rs.d.ts.map +1 -0
- package/dist/utils/editor.d.ts +6 -0
- package/dist/utils/editor.d.ts.map +1 -0
- package/dist/utils/editor.js +4 -0
- package/dist/utils/errors.d.ts +61 -0
- package/dist/utils/errors.d.ts.map +1 -0
- package/dist/utils/errors.js +3 -0
- package/dist/utils/format.d.ts +20 -0
- package/dist/utils/format.d.ts.map +1 -0
- package/dist/utils/format.js +3 -0
- package/dist/utils/logger.d.ts +32 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +46 -0
- package/dist/utils/logger.js.map +1 -0
- package/package.json +66 -6
package/README.md
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
# xling
|
|
2
|
+
|
|
3
|
+
Unified CLI for managing AI tool settings across Claude Code, Codex, and Gemini CLI.
|
|
4
|
+
|
|
1
5
|
## What's in a Name? The Story of `xling`
|
|
2
6
|
|
|
3
7
|
The name `xling` was carefully chosen to represent the project's philosophy, blending modern tech symbolism with a rich, dual-meaning core.
|
|
@@ -16,4 +20,228 @@ At the heart of the name is `ling`, a concept with two harmonious faces rooted i
|
|
|
16
20
|
1. **令 (lìng)**: This means "command" or "order." It represents the tool's heritage as a command-line interface—unwavering, precise, and powerful.
|
|
17
21
|
2. **灵 (líng)**: This translates to "intelligence," "spirit," or "agility." It embodies the AI engine that gives `xling` its smarts, allowing it to understand natural language, anticipate intent, and perform complex tasks with a touch of magic.
|
|
18
22
|
|
|
19
|
-
Together, `xling` is more than just a tool; it's an intelligent partner that amplifies your ability to command your digital world.
|
|
23
|
+
Together, `xling` is more than just a tool; it's an intelligent partner that amplifies your ability to command your digital world.
|
|
24
|
+
|
|
25
|
+
## Features
|
|
26
|
+
|
|
27
|
+
- **Unified Interface**: Manage settings for multiple AI CLI tools with a single command
|
|
28
|
+
- **Multiple Scopes**: Support for user, project, local, and system-level configurations
|
|
29
|
+
- **Profile Switching**: Switch between different configuration profiles (Codex)
|
|
30
|
+
- **Dry Run Mode**: Preview changes before applying them
|
|
31
|
+
- **JSON Output**: Machine-readable output for scripting
|
|
32
|
+
- **Type Safe**: Built with TypeScript for reliability
|
|
33
|
+
|
|
34
|
+
## Installation
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
# Install dependencies
|
|
38
|
+
bun install
|
|
39
|
+
|
|
40
|
+
# Build the project
|
|
41
|
+
bun run build
|
|
42
|
+
|
|
43
|
+
# Link globally (optional)
|
|
44
|
+
npm link
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Usage
|
|
48
|
+
|
|
49
|
+
### List Settings
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
# List all settings for Claude Code (user scope)
|
|
53
|
+
xling settings:list --tool claude --scope user
|
|
54
|
+
|
|
55
|
+
# List Codex settings in table format
|
|
56
|
+
xling settings:list --tool codex --table
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
> Claude-specific: `settings:list --tool claude` now enumerates every
|
|
60
|
+
> `settings*.json` file (for example `settings.hxi.json`) in the selected scope
|
|
61
|
+
> so you can quickly discover switchable variants.
|
|
62
|
+
>
|
|
63
|
+
> Codex-specific: `settings:list --tool codex` surfaces only the `model_providers`
|
|
64
|
+
> block from `~/.codex/config.toml`, helping you audit provider names, base URLs,
|
|
65
|
+
> and env key bindings at a glance.
|
|
66
|
+
|
|
67
|
+
`settings:list` 默认输出 YAML 风格的简洁概览;如需表格/JSON,请加 `--table` 或
|
|
68
|
+
`--json`。其余命令仍以 JSON 为默认输出,可通过 `--no-json` 获取文本格式。
|
|
69
|
+
|
|
70
|
+
### Get Settings File
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
# Show Claude user settings (plain text default)
|
|
74
|
+
xling settings:get --tool claude --scope user
|
|
75
|
+
|
|
76
|
+
# Inspect a Claude variant (settings.hxi.json)
|
|
77
|
+
xling settings:get hxi --tool claude --scope user
|
|
78
|
+
|
|
79
|
+
# Show Codex config (plain text)
|
|
80
|
+
xling settings:get --tool codex
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### Edit Settings (Claude)
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
# Create/edit settings.hxi.json in VS Code (default)
|
|
87
|
+
xling settings:set --tool claude --scope user --name hxi
|
|
88
|
+
|
|
89
|
+
# Open default settings in Cursor
|
|
90
|
+
xling settings:set --tool claude --scope project --name default --ide cursor --no-json
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
`settings:set` 现专注于整文件编辑:传 `--name`(默认 `default`)即可创建/打开
|
|
94
|
+
`settings.<name>.json`,并使用 `--ide` 指定编辑器(默认 VS Code 的 `code`)。
|
|
95
|
+
|
|
96
|
+
> Note: 所有 `settings:*` 命令仅依赖 `--tool`、`--scope`、`--name` 等 flag;不再提供
|
|
97
|
+
> `developerShortcuts.runCommand` 这类键级参数。
|
|
98
|
+
|
|
99
|
+
### Switch Profiles or Claude Variants
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
# Switch to a different profile
|
|
103
|
+
xling settings:switch oss --tool codex
|
|
104
|
+
|
|
105
|
+
# Activate settings.hxi.json for Claude user scope
|
|
106
|
+
xling settings:switch hxi --tool claude --scope user
|
|
107
|
+
|
|
108
|
+
# Apply without prompt (Claude)
|
|
109
|
+
xling settings:switch hxi --tool claude --scope user --force
|
|
110
|
+
|
|
111
|
+
# Force and keep a .bak backup
|
|
112
|
+
xling settings:switch hxi --tool claude --scope user --force --backup
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
Claude switches现在默认进行交互式 diff 预览:命令会先打印彩色统一 diff,
|
|
116
|
+
然后提示 `overwrite / backup / cancel`。若要非交互执行,使用 `--force`,并可
|
|
117
|
+
通过 `--backup` 强制保留 `.bak`。Codex 保持原行为,直接切换 profile。
|
|
118
|
+
|
|
119
|
+
### Inspect Configuration
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
# View configuration file information
|
|
123
|
+
xling settings:inspect --tool claude --scope user
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
## Supported Tools
|
|
127
|
+
|
|
128
|
+
### Claude Code
|
|
129
|
+
|
|
130
|
+
- **Scopes**: user, project, local
|
|
131
|
+
- **Config Files**:
|
|
132
|
+
- User: `~/.claude/settings.json`
|
|
133
|
+
- Project: `.claude/settings.json`
|
|
134
|
+
- Local: `.claude/settings.local.json`
|
|
135
|
+
|
|
136
|
+
### Codex
|
|
137
|
+
|
|
138
|
+
- **Scopes**: user
|
|
139
|
+
- **Config Files**:
|
|
140
|
+
- User: `~/.codex/config.toml`
|
|
141
|
+
- **Features**: Profile switching
|
|
142
|
+
|
|
143
|
+
### Gemini CLI
|
|
144
|
+
|
|
145
|
+
- **Scopes**: user, project, system
|
|
146
|
+
- **Config Files**:
|
|
147
|
+
- User: `~/.gemini/settings.json`
|
|
148
|
+
- Project: `.gemini/settings.json`
|
|
149
|
+
- System: Platform-dependent
|
|
150
|
+
|
|
151
|
+
## Architecture
|
|
152
|
+
|
|
153
|
+
The project follows SOLID principles:
|
|
154
|
+
|
|
155
|
+
- **Single Responsibility**: Each adapter handles one tool
|
|
156
|
+
- **Open/Closed**: Easy to add new tools without modifying existing code
|
|
157
|
+
- **Liskov Substitution**: All adapters implement the same interface
|
|
158
|
+
- **Interface Segregation**: Clean, focused interfaces
|
|
159
|
+
- **Dependency Inversion**: Commands depend on abstractions, not implementations
|
|
160
|
+
|
|
161
|
+
### Directory Structure
|
|
162
|
+
|
|
163
|
+
```
|
|
164
|
+
xling/
|
|
165
|
+
├── bin/
|
|
166
|
+
│ └── run.js # CLI entry point
|
|
167
|
+
├── src/
|
|
168
|
+
│ ├── commands/ # oclif commands
|
|
169
|
+
│ │ └── settings/
|
|
170
|
+
│ ├── domain/ # Types and interfaces
|
|
171
|
+
│ ├── services/ # Business logic
|
|
172
|
+
│ │ └── settings/
|
|
173
|
+
│ │ ├── adapters/ # Tool adapters
|
|
174
|
+
│ │ ├── fsStore.ts # File system operations
|
|
175
|
+
│ │ └── dispatcher.ts
|
|
176
|
+
│ └── utils/ # Utilities
|
|
177
|
+
└── test/ # Tests and fixtures
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
## Development
|
|
181
|
+
|
|
182
|
+
```bash
|
|
183
|
+
# Install dependencies
|
|
184
|
+
bun install
|
|
185
|
+
|
|
186
|
+
# Build (using tsdown)
|
|
187
|
+
bun run build
|
|
188
|
+
|
|
189
|
+
# Watch mode (tsdown --watch)
|
|
190
|
+
bun run dev
|
|
191
|
+
|
|
192
|
+
# Code quality
|
|
193
|
+
bun run lint # Lint with oxlint
|
|
194
|
+
bun run lint:fix # Auto-fix lint issues
|
|
195
|
+
bun run format # Format with oxfmt
|
|
196
|
+
bun run format:check # Check formatting
|
|
197
|
+
bun run typecheck # Type check with tsc
|
|
198
|
+
|
|
199
|
+
# Run tests
|
|
200
|
+
bun test
|
|
201
|
+
bun test:watch
|
|
202
|
+
bun test:coverage
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
### Toolchain
|
|
206
|
+
|
|
207
|
+
**Build System**
|
|
208
|
+
|
|
209
|
+
This project uses [tsdown](https://tsdown.vercel.app/) for fast TypeScript compilation and bundling:
|
|
210
|
+
|
|
211
|
+
- **Fast builds**: Powered by rolldown (Rust-based bundler)
|
|
212
|
+
- **ESM output**: Generates `.js` files for modern Node.js
|
|
213
|
+
- **Type definitions**: Automatically generates `.d.ts` files
|
|
214
|
+
- **Source maps**: Includes source maps for debugging
|
|
215
|
+
- **Tree shaking**: Optimized bundle size
|
|
216
|
+
|
|
217
|
+
**Code Quality**
|
|
218
|
+
|
|
219
|
+
- **Linting**: [oxlint](https://oxc.rs/) - Rust-based linter, 50-100x faster than ESLint
|
|
220
|
+
- **Formatting**: [oxfmt](https://oxc.rs/) - Fast formatter compatible with Prettier config
|
|
221
|
+
- **Type Checking**: TypeScript compiler for strict type safety
|
|
222
|
+
|
|
223
|
+
## Testing
|
|
224
|
+
|
|
225
|
+
```bash
|
|
226
|
+
# Run all tests
|
|
227
|
+
bun test
|
|
228
|
+
|
|
229
|
+
# Run with coverage
|
|
230
|
+
bun test:coverage
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
## Contributing
|
|
234
|
+
|
|
235
|
+
1. Fork the repository
|
|
236
|
+
2. Create your feature branch (`git checkout -b feature/amazing-feature`)
|
|
237
|
+
3. Commit your changes (`git commit -m 'Add some amazing feature'`)
|
|
238
|
+
4. Push to the branch (`git push origin feature/amazing-feature`)
|
|
239
|
+
5. Open a Pull Request
|
|
240
|
+
|
|
241
|
+
## License
|
|
242
|
+
|
|
243
|
+
Apache-2.0
|
|
244
|
+
|
|
245
|
+
## Author
|
|
246
|
+
|
|
247
|
+
Kingsword <kingsword09@gmail.com>
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { d as ToolId, i as Scope, l as SettingsResult, n as InspectResult, s as SettingsListData, t as EditOptions, u as SwitchOptions } from "./types-BfNSo2rs.js";
|
|
2
|
+
import { t as SettingsAdapter } from "./interfaces-CriQW6hF.js";
|
|
3
|
+
|
|
4
|
+
//#region src/services/settings/adapters/base.d.ts
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* 抽象基类
|
|
8
|
+
*/
|
|
9
|
+
declare abstract class BaseAdapter implements SettingsAdapter {
|
|
10
|
+
abstract readonly toolId: ToolId;
|
|
11
|
+
/**
|
|
12
|
+
* 子类必须实现的方法
|
|
13
|
+
*/
|
|
14
|
+
abstract resolvePath(scope: Scope): string;
|
|
15
|
+
abstract validateScope(scope: Scope): boolean;
|
|
16
|
+
/**
|
|
17
|
+
* 列出所有配置
|
|
18
|
+
*/
|
|
19
|
+
list(scope: Scope): Promise<SettingsListData>;
|
|
20
|
+
/**
|
|
21
|
+
* 默认 switchProfile 会抛出,子类可覆盖
|
|
22
|
+
*/
|
|
23
|
+
switchProfile(_scope: Scope, _profile: string, _options?: SwitchOptions): Promise<SettingsResult>;
|
|
24
|
+
/**
|
|
25
|
+
* 检查配置文件
|
|
26
|
+
*/
|
|
27
|
+
inspect(scope: Scope): Promise<InspectResult>;
|
|
28
|
+
/**
|
|
29
|
+
* 默认 edit 会抛出,子类可覆盖
|
|
30
|
+
*/
|
|
31
|
+
edit(scope: Scope, _options: EditOptions): Promise<SettingsResult>;
|
|
32
|
+
/**
|
|
33
|
+
* 读取配置文件(子类可覆盖)
|
|
34
|
+
*/
|
|
35
|
+
protected readConfig(path: string): Record<string, unknown>;
|
|
36
|
+
/**
|
|
37
|
+
* 写入配置文件(子类可覆盖)
|
|
38
|
+
*/
|
|
39
|
+
protected writeConfig(path: string, data: Record<string, unknown>, backup?: boolean): void;
|
|
40
|
+
}
|
|
41
|
+
//#endregion
|
|
42
|
+
export { BaseAdapter as t };
|
|
43
|
+
//# sourceMappingURL=base-BlaKDtDu.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"base-BlaKDtDu.d.ts","names":[],"sources":["../src/services/settings/adapters/base.ts"],"sourcesContent":[],"mappings":";;;;;;;;AAiCoC,uBAZd,WAAA,YAAuB,eAYT,CAAA;EAAR,kBAAA,MAAA,EAXA,MAWA;EAmBhB;;;EAGP,SAAA,WAAA,CAAA,KAAA,EA5ByB,KA4BzB,CAAA,EAAA,MAAA;EAOkB,SAAA,aAAA,CAAA,KAAA,EAlCS,KAkCT,CAAA,EAAA,OAAA;EAAgB;;;EA8BF,IAAA,CAAA,KAAA,EA3DjB,KA2DiB,CAAA,EA3DT,OA2DS,CA3DD,gBA2DC,CAAA;EAAsB;;;EAgBjD,aAAA,CAAA,MAAA,EAxDE,KAwDF,EAAA,QAAA,EAAA,MAAA,EAAA,QAAA,CAAA,EAtDK,aAsDL,CAAA,EArDL,OAqDK,CArDG,cAqDH,CAAA;EAvFmC;;;iBAyCtB,QAAQ,QAAQ;;;;cA8BnB,iBAAiB,cAAc,QAAQ;;;;sCAOrB;;;;4CAS5B"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { a as InvalidScopeError } from "./errors-CAZ5k5YT.js";
|
|
2
|
+
import { a as readJSON, c as writeJSON, i as getFileInfo, r as fileExists } from "./fsStore-i-3731TM.js";
|
|
3
|
+
|
|
4
|
+
//#region src/services/settings/adapters/base.ts
|
|
5
|
+
/**
|
|
6
|
+
* 抽象基类
|
|
7
|
+
*/
|
|
8
|
+
var BaseAdapter = class {
|
|
9
|
+
/**
|
|
10
|
+
* 列出所有配置
|
|
11
|
+
*/
|
|
12
|
+
async list(scope) {
|
|
13
|
+
if (!this.validateScope(scope)) throw new InvalidScopeError(scope);
|
|
14
|
+
const path = this.resolvePath(scope);
|
|
15
|
+
return {
|
|
16
|
+
type: "entries",
|
|
17
|
+
entries: this.readConfig(path),
|
|
18
|
+
filePath: path
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* 默认 switchProfile 会抛出,子类可覆盖
|
|
23
|
+
*/
|
|
24
|
+
async switchProfile(_scope, _profile, _options) {
|
|
25
|
+
throw new Error(`Tool ${this.toolId} does not support profile switching`);
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* 检查配置文件
|
|
29
|
+
*/
|
|
30
|
+
async inspect(scope) {
|
|
31
|
+
if (!this.validateScope(scope)) throw new InvalidScopeError(scope);
|
|
32
|
+
const path = this.resolvePath(scope);
|
|
33
|
+
if (!fileExists(path)) return {
|
|
34
|
+
path,
|
|
35
|
+
exists: false
|
|
36
|
+
};
|
|
37
|
+
const fileInfo = getFileInfo(path);
|
|
38
|
+
const config = this.readConfig(path);
|
|
39
|
+
return {
|
|
40
|
+
path,
|
|
41
|
+
exists: true,
|
|
42
|
+
content: JSON.stringify(config, null, 2),
|
|
43
|
+
size: fileInfo?.size,
|
|
44
|
+
lastModified: fileInfo?.lastModified
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* 默认 edit 会抛出,子类可覆盖
|
|
49
|
+
*/
|
|
50
|
+
async edit(scope, _options) {
|
|
51
|
+
throw new Error(`Tool ${this.toolId} does not support edit for ${scope}`);
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* 读取配置文件(子类可覆盖)
|
|
55
|
+
*/
|
|
56
|
+
readConfig(path) {
|
|
57
|
+
return readJSON(path);
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* 写入配置文件(子类可覆盖)
|
|
61
|
+
*/
|
|
62
|
+
writeConfig(path, data, backup = true) {
|
|
63
|
+
writeJSON(path, data, backup);
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
//#endregion
|
|
68
|
+
export { BaseAdapter as t };
|
|
69
|
+
//# sourceMappingURL=base-Cm9xh6sW.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"base-Cm9xh6sW.js","names":["fsStore.fileExists","fsStore.getFileInfo","fsStore.readJSON"],"sources":["../src/services/settings/adapters/base.ts"],"sourcesContent":["/**\n * 适配器抽象基类\n * 实现通用逻辑,减少重复代码(DRY 原则)\n */\n\nimport type { SettingsAdapter } from \"@/domain/interfaces.ts\";\nimport type {\n ToolId,\n Scope,\n InspectResult,\n SettingsListData,\n SettingsResult,\n EditOptions,\n SwitchOptions,\n} from \"@/domain/types.ts\";\nimport { InvalidScopeError } from \"@/utils/errors.ts\";\nimport * as fsStore from \"@/services/settings/fsStore.ts\";\n\n/**\n * 抽象基类\n */\nexport abstract class BaseAdapter implements SettingsAdapter {\n abstract readonly toolId: ToolId;\n\n /**\n * 子类必须实现的方法\n */\n abstract resolvePath(scope: Scope): string;\n abstract validateScope(scope: Scope): boolean;\n\n /**\n * 列出所有配置\n */\n async list(scope: Scope): Promise<SettingsListData> {\n if (!this.validateScope(scope)) {\n throw new InvalidScopeError(scope);\n }\n\n const path = this.resolvePath(scope);\n const config = this.readConfig(path);\n\n return {\n type: \"entries\",\n entries: config,\n filePath: path,\n };\n }\n\n /**\n * 默认 switchProfile 会抛出,子类可覆盖\n */\n async switchProfile(\n _scope: Scope,\n _profile: string,\n _options?: SwitchOptions,\n ): Promise<SettingsResult> {\n throw new Error(`Tool ${this.toolId} does not support profile switching`);\n }\n\n /**\n * 检查配置文件\n */\n async inspect(scope: Scope): Promise<InspectResult> {\n if (!this.validateScope(scope)) {\n throw new InvalidScopeError(scope);\n }\n\n const path = this.resolvePath(scope);\n const exists = fsStore.fileExists(path);\n\n if (!exists) {\n return {\n path,\n exists: false,\n };\n }\n\n const fileInfo = fsStore.getFileInfo(path);\n const config = this.readConfig(path);\n\n return {\n path,\n exists: true,\n content: JSON.stringify(config, null, 2),\n size: fileInfo?.size,\n lastModified: fileInfo?.lastModified,\n };\n }\n\n /**\n * 默认 edit 会抛出,子类可覆盖\n */\n async edit(scope: Scope, _options: EditOptions): Promise<SettingsResult> {\n throw new Error(`Tool ${this.toolId} does not support edit for ${scope}`);\n }\n\n /**\n * 读取配置文件(子类可覆盖)\n */\n protected readConfig(path: string): Record<string, unknown> {\n return fsStore.readJSON(path);\n }\n\n /**\n * 写入配置文件(子类可覆盖)\n */\n protected writeConfig(\n path: string,\n data: Record<string, unknown>,\n backup = true,\n ): void {\n fsStore.writeJSON(path, data, backup);\n }\n}\n"],"mappings":";;;;;;;AAqBA,IAAsB,cAAtB,MAA6D;;;;CAY3D,MAAM,KAAK,OAAyC;AAClD,MAAI,CAAC,KAAK,cAAc,MAAM,CAC5B,OAAM,IAAI,kBAAkB,MAAM;EAGpC,MAAM,OAAO,KAAK,YAAY,MAAM;AAGpC,SAAO;GACL,MAAM;GACN,SAJa,KAAK,WAAW,KAAK;GAKlC,UAAU;GACX;;;;;CAMH,MAAM,cACJ,QACA,UACA,UACyB;AACzB,QAAM,IAAI,MAAM,QAAQ,KAAK,OAAO,qCAAqC;;;;;CAM3E,MAAM,QAAQ,OAAsC;AAClD,MAAI,CAAC,KAAK,cAAc,MAAM,CAC5B,OAAM,IAAI,kBAAkB,MAAM;EAGpC,MAAM,OAAO,KAAK,YAAY,MAAM;AAGpC,MAAI,CAFWA,WAAmB,KAAK,CAGrC,QAAO;GACL;GACA,QAAQ;GACT;EAGH,MAAM,WAAWC,YAAoB,KAAK;EAC1C,MAAM,SAAS,KAAK,WAAW,KAAK;AAEpC,SAAO;GACL;GACA,QAAQ;GACR,SAAS,KAAK,UAAU,QAAQ,MAAM,EAAE;GACxC,MAAM,UAAU;GAChB,cAAc,UAAU;GACzB;;;;;CAMH,MAAM,KAAK,OAAc,UAAgD;AACvE,QAAM,IAAI,MAAM,QAAQ,KAAK,OAAO,6BAA6B,QAAQ;;;;;CAM3E,AAAU,WAAW,MAAuC;AAC1D,SAAOC,SAAiB,KAAK;;;;;CAM/B,AAAU,YACR,MACA,MACA,SAAS,MACH;AACN,YAAkB,MAAM,MAAM,OAAO"}
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
import { a as InvalidScopeError, s as SettingsVariantNotFoundError } from "./errors-CAZ5k5YT.js";
|
|
2
|
+
import { n as resolveEditorCommand, t as openInEditor } from "./editor-D4qoje1V.js";
|
|
3
|
+
import { t as formatDiff } from "./format-Cqecj3RS.js";
|
|
4
|
+
import { c as writeJSON, i as getFileInfo, n as ensureDir, s as resolveHome } from "./fsStore-i-3731TM.js";
|
|
5
|
+
import { t as BaseAdapter } from "./base-Cm9xh6sW.js";
|
|
6
|
+
import { t as CLAUDE_SETTINGS_TEMPLATE } from "./claudeDefault-Fk1-kHLE.js";
|
|
7
|
+
import * as fs from "fs";
|
|
8
|
+
import * as path from "path";
|
|
9
|
+
|
|
10
|
+
//#region src/services/settings/adapters/claude.ts
|
|
11
|
+
/**
|
|
12
|
+
* Claude Code 适配器
|
|
13
|
+
*/
|
|
14
|
+
/**
|
|
15
|
+
* Claude Code 配置适配器
|
|
16
|
+
*
|
|
17
|
+
* 配置文件路径:
|
|
18
|
+
* - user: ~/.claude/settings.json
|
|
19
|
+
* - project: <cwd>/.claude/settings.json
|
|
20
|
+
* - local: <cwd>/.claude/settings.local.json
|
|
21
|
+
*/
|
|
22
|
+
var ClaudeAdapter = class extends BaseAdapter {
|
|
23
|
+
toolId = "claude";
|
|
24
|
+
/**
|
|
25
|
+
* 列出所有 settings.*.json 文件
|
|
26
|
+
*/
|
|
27
|
+
async list(scope) {
|
|
28
|
+
if (!this.validateScope(scope)) throw new InvalidScopeError(scope);
|
|
29
|
+
const activePath = resolveHome(this.resolvePath(scope));
|
|
30
|
+
const directory = path.dirname(activePath);
|
|
31
|
+
const activeFilename = path.basename(activePath);
|
|
32
|
+
const files = [this.buildEntry(activePath, scope, true)];
|
|
33
|
+
if (fs.existsSync(directory)) {
|
|
34
|
+
const entries = fs.readdirSync(directory, { withFileTypes: true });
|
|
35
|
+
for (const entry of entries) {
|
|
36
|
+
if (!entry.isFile()) continue;
|
|
37
|
+
if (entry.name === activeFilename) continue;
|
|
38
|
+
if (!this.isSettingsFile(entry.name)) continue;
|
|
39
|
+
const entryPath = path.join(directory, entry.name);
|
|
40
|
+
files.push(this.buildEntry(entryPath, scope, false));
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return {
|
|
44
|
+
type: "files",
|
|
45
|
+
files: this.sortFiles(files)
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* 解析配置文件路径
|
|
50
|
+
*/
|
|
51
|
+
resolvePath(scope) {
|
|
52
|
+
switch (scope) {
|
|
53
|
+
case "user": return "~/.claude/settings.json";
|
|
54
|
+
case "project": return ".claude/settings.json";
|
|
55
|
+
case "local": return ".claude/settings.local.json";
|
|
56
|
+
default: throw new Error(`Unsupported scope for Claude: ${scope}`);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* 验证 scope 是否有效
|
|
61
|
+
*/
|
|
62
|
+
validateScope(scope) {
|
|
63
|
+
return [
|
|
64
|
+
"user",
|
|
65
|
+
"project",
|
|
66
|
+
"local"
|
|
67
|
+
].includes(scope);
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* 切换 settings.<variant>.json 到活动文件
|
|
71
|
+
*/
|
|
72
|
+
async switchProfile(scope, profile, options) {
|
|
73
|
+
if (!this.validateScope(scope)) throw new InvalidScopeError(scope);
|
|
74
|
+
const variant = profile.trim();
|
|
75
|
+
if (!variant) throw new Error("Variant name cannot be empty");
|
|
76
|
+
const targetPath = resolveHome(this.resolvePath(scope));
|
|
77
|
+
const directory = path.dirname(targetPath);
|
|
78
|
+
const sourcePath = this.findVariantPath(directory, variant, targetPath);
|
|
79
|
+
if (!sourcePath) throw new SettingsVariantNotFoundError(variant);
|
|
80
|
+
const nextConfig = this.readConfig(sourcePath);
|
|
81
|
+
let currentConfig = {};
|
|
82
|
+
try {
|
|
83
|
+
currentConfig = this.readConfig(targetPath);
|
|
84
|
+
} catch {
|
|
85
|
+
currentConfig = {};
|
|
86
|
+
}
|
|
87
|
+
const diff = formatDiff(currentConfig, nextConfig);
|
|
88
|
+
if (options?.preview) return {
|
|
89
|
+
success: true,
|
|
90
|
+
preview: true,
|
|
91
|
+
filePath: targetPath,
|
|
92
|
+
diff: diff ?? void 0,
|
|
93
|
+
data: {
|
|
94
|
+
from: sourcePath,
|
|
95
|
+
to: targetPath,
|
|
96
|
+
hasChanges: Boolean(diff)
|
|
97
|
+
},
|
|
98
|
+
message: diff ? "Preview generated. Review diff before applying." : "Current settings already match the selected variant."
|
|
99
|
+
};
|
|
100
|
+
if (!diff) return {
|
|
101
|
+
success: true,
|
|
102
|
+
message: "Current settings already match the selected variant.",
|
|
103
|
+
filePath: targetPath
|
|
104
|
+
};
|
|
105
|
+
const shouldBackup = options?.backup ?? false;
|
|
106
|
+
this.writeConfig(targetPath, nextConfig, shouldBackup);
|
|
107
|
+
return {
|
|
108
|
+
success: true,
|
|
109
|
+
message: `Switched to ${path.basename(sourcePath)}`,
|
|
110
|
+
filePath: targetPath,
|
|
111
|
+
data: {
|
|
112
|
+
from: sourcePath,
|
|
113
|
+
to: targetPath
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
async edit(scope, options) {
|
|
118
|
+
if (!this.validateScope(scope)) throw new InvalidScopeError(scope);
|
|
119
|
+
const basePath = resolveHome(this.resolvePath(scope));
|
|
120
|
+
const directory = path.dirname(basePath);
|
|
121
|
+
const variantName = options.name?.trim();
|
|
122
|
+
const resolvedEditor = resolveEditorCommand(options.ide);
|
|
123
|
+
ensureDir(directory);
|
|
124
|
+
let targetPath = basePath;
|
|
125
|
+
let label = "default";
|
|
126
|
+
if (variantName && variantName !== "" && variantName !== "default") {
|
|
127
|
+
const existingPath = this.findVariantPath(directory, variantName, basePath);
|
|
128
|
+
if (existingPath) targetPath = existingPath;
|
|
129
|
+
else {
|
|
130
|
+
targetPath = path.join(directory, `settings.${variantName}.json`);
|
|
131
|
+
if (!fs.existsSync(targetPath)) {
|
|
132
|
+
const seed = this.buildSeedConfig(basePath);
|
|
133
|
+
writeJSON(targetPath, seed, false);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
label = variantName;
|
|
137
|
+
} else if (!fs.existsSync(targetPath)) {
|
|
138
|
+
const seed = this.buildSeedConfig(basePath);
|
|
139
|
+
writeJSON(targetPath, seed, false);
|
|
140
|
+
}
|
|
141
|
+
await openInEditor(resolvedEditor, targetPath);
|
|
142
|
+
return {
|
|
143
|
+
success: true,
|
|
144
|
+
message: `Opened ${label} settings in ${resolvedEditor}`,
|
|
145
|
+
filePath: targetPath,
|
|
146
|
+
data: {
|
|
147
|
+
variant: label,
|
|
148
|
+
ide: resolvedEditor
|
|
149
|
+
}
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
buildEntry(filePath, scope, active) {
|
|
153
|
+
const info = getFileInfo(filePath);
|
|
154
|
+
return {
|
|
155
|
+
filename: path.basename(filePath),
|
|
156
|
+
variant: this.extractVariant(filePath),
|
|
157
|
+
path: filePath,
|
|
158
|
+
scope,
|
|
159
|
+
active,
|
|
160
|
+
exists: Boolean(info),
|
|
161
|
+
size: info?.size,
|
|
162
|
+
lastModified: info?.lastModified
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
extractVariant(filePath) {
|
|
166
|
+
const filename = path.basename(filePath);
|
|
167
|
+
const match = filename.match(/^settings(?:[._-](.+))?\.json$/);
|
|
168
|
+
if (!match) return filename.replace(/\.json$/, "");
|
|
169
|
+
return match[1] ?? "default";
|
|
170
|
+
}
|
|
171
|
+
buildSeedConfig(basePath) {
|
|
172
|
+
try {
|
|
173
|
+
if (fs.existsSync(basePath)) {
|
|
174
|
+
const content = fs.readFileSync(basePath, "utf-8");
|
|
175
|
+
return JSON.parse(content);
|
|
176
|
+
}
|
|
177
|
+
} catch {}
|
|
178
|
+
return { ...CLAUDE_SETTINGS_TEMPLATE };
|
|
179
|
+
}
|
|
180
|
+
isSettingsFile(filename) {
|
|
181
|
+
return /^settings[._-].+\.json$/.test(filename);
|
|
182
|
+
}
|
|
183
|
+
sortFiles(files) {
|
|
184
|
+
return files.sort((a, b) => {
|
|
185
|
+
if (a.active && !b.active) return -1;
|
|
186
|
+
if (!a.active && b.active) return 1;
|
|
187
|
+
return a.variant.localeCompare(b.variant);
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
findVariantPath(directory, profile, defaultPath) {
|
|
191
|
+
const candidates = [];
|
|
192
|
+
const pushCandidate = (value) => {
|
|
193
|
+
if (!candidates.includes(value)) candidates.push(value);
|
|
194
|
+
};
|
|
195
|
+
if (profile === "default") pushCandidate(defaultPath);
|
|
196
|
+
if (profile.endsWith(".json")) pushCandidate(path.isAbsolute(profile) ? profile : path.join(directory, profile));
|
|
197
|
+
else {
|
|
198
|
+
pushCandidate(path.join(directory, `settings.${profile}.json`));
|
|
199
|
+
pushCandidate(path.join(directory, `settings-${profile}.json`));
|
|
200
|
+
pushCandidate(path.join(directory, `settings_${profile}.json`));
|
|
201
|
+
}
|
|
202
|
+
for (const candidate of candidates) if (fs.existsSync(candidate)) return candidate;
|
|
203
|
+
return null;
|
|
204
|
+
}
|
|
205
|
+
};
|
|
206
|
+
|
|
207
|
+
//#endregion
|
|
208
|
+
export { ClaudeAdapter as t };
|
|
209
|
+
//# sourceMappingURL=claude-l3zomoHg.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claude-l3zomoHg.js","names":["fsStore.resolveHome","files: SettingsFileEntry[]","currentConfig: Record<string, unknown>","fsStore.getFileInfo","candidates: string[]"],"sources":["../src/services/settings/adapters/claude.ts"],"sourcesContent":["/**\n * Claude Code 适配器\n */\n\nimport * as fs from \"fs\";\nimport * as path from \"path\";\nimport type {\n Scope,\n SettingsListData,\n SettingsFileEntry,\n SettingsResult,\n EditOptions,\n SwitchOptions,\n} from \"@/domain/types.ts\";\nimport { BaseAdapter } from \"./base.ts\";\nimport * as fsStore from \"@/services/settings/fsStore.ts\";\nimport {\n InvalidScopeError,\n SettingsVariantNotFoundError,\n} from \"@/utils/errors.ts\";\nimport { CLAUDE_SETTINGS_TEMPLATE } from \"@/services/settings/templates/claudeDefault.ts\";\nimport { openInEditor, resolveEditorCommand } from \"@/utils/editor.ts\";\nimport { formatDiff } from \"@/utils/format.ts\";\n\n/**\n * Claude Code 配置适配器\n *\n * 配置文件路径:\n * - user: ~/.claude/settings.json\n * - project: <cwd>/.claude/settings.json\n * - local: <cwd>/.claude/settings.local.json\n */\nexport class ClaudeAdapter extends BaseAdapter {\n readonly toolId = \"claude\" as const;\n\n /**\n * 列出所有 settings.*.json 文件\n */\n override async list(scope: Scope): Promise<SettingsListData> {\n if (!this.validateScope(scope)) {\n throw new InvalidScopeError(scope);\n }\n\n const activePath = fsStore.resolveHome(this.resolvePath(scope));\n const directory = path.dirname(activePath);\n const activeFilename = path.basename(activePath);\n\n const files: SettingsFileEntry[] = [\n this.buildEntry(activePath, scope, true),\n ];\n\n if (fs.existsSync(directory)) {\n const entries = fs.readdirSync(directory, { withFileTypes: true });\n for (const entry of entries) {\n if (!entry.isFile()) continue;\n if (entry.name === activeFilename) continue;\n if (!this.isSettingsFile(entry.name)) continue;\n\n const entryPath = path.join(directory, entry.name);\n files.push(this.buildEntry(entryPath, scope, false));\n }\n }\n\n return {\n type: \"files\",\n files: this.sortFiles(files),\n };\n }\n\n /**\n * 解析配置文件路径\n */\n resolvePath(scope: Scope): string {\n switch (scope) {\n case \"user\":\n return \"~/.claude/settings.json\";\n case \"project\":\n return \".claude/settings.json\";\n case \"local\":\n return \".claude/settings.local.json\";\n default:\n throw new Error(`Unsupported scope for Claude: ${scope}`);\n }\n }\n\n /**\n * 验证 scope 是否有效\n */\n validateScope(scope: Scope): boolean {\n return [\"user\", \"project\", \"local\"].includes(scope);\n }\n\n /**\n * 切换 settings.<variant>.json 到活动文件\n */\n override async switchProfile(\n scope: Scope,\n profile: string,\n options?: SwitchOptions,\n ): Promise<SettingsResult> {\n if (!this.validateScope(scope)) {\n throw new InvalidScopeError(scope);\n }\n\n const variant = profile.trim();\n if (!variant) {\n throw new Error(\"Variant name cannot be empty\");\n }\n\n const targetPath = fsStore.resolveHome(this.resolvePath(scope));\n const directory = path.dirname(targetPath);\n const sourcePath = this.findVariantPath(directory, variant, targetPath);\n\n if (!sourcePath) {\n throw new SettingsVariantNotFoundError(variant);\n }\n\n const nextConfig = this.readConfig(sourcePath);\n\n let currentConfig: Record<string, unknown> = {};\n try {\n currentConfig = this.readConfig(targetPath);\n } catch {\n currentConfig = {};\n }\n\n const diff = formatDiff(currentConfig, nextConfig);\n\n if (options?.preview) {\n return {\n success: true,\n preview: true,\n filePath: targetPath,\n diff: diff ?? undefined,\n data: {\n from: sourcePath,\n to: targetPath,\n hasChanges: Boolean(diff),\n },\n message: diff\n ? \"Preview generated. Review diff before applying.\"\n : \"Current settings already match the selected variant.\",\n };\n }\n\n if (!diff) {\n return {\n success: true,\n message: \"Current settings already match the selected variant.\",\n filePath: targetPath,\n };\n }\n\n const shouldBackup = options?.backup ?? false;\n this.writeConfig(targetPath, nextConfig, shouldBackup);\n\n return {\n success: true,\n message: `Switched to ${path.basename(sourcePath)}`,\n filePath: targetPath,\n data: {\n from: sourcePath,\n to: targetPath,\n },\n };\n }\n\n override async edit(\n scope: Scope,\n options: EditOptions,\n ): Promise<SettingsResult> {\n if (!this.validateScope(scope)) {\n throw new InvalidScopeError(scope);\n }\n\n const basePath = fsStore.resolveHome(this.resolvePath(scope));\n const directory = path.dirname(basePath);\n const variantName = options.name?.trim();\n const resolvedEditor = resolveEditorCommand(options.ide);\n\n fsStore.ensureDir(directory);\n\n let targetPath = basePath;\n let label = \"default\";\n\n if (variantName && variantName !== \"\" && variantName !== \"default\") {\n const existingPath = this.findVariantPath(\n directory,\n variantName,\n basePath,\n );\n if (existingPath) {\n targetPath = existingPath;\n } else {\n targetPath = path.join(directory, `settings.${variantName}.json`);\n if (!fs.existsSync(targetPath)) {\n const seed = this.buildSeedConfig(basePath);\n fsStore.writeJSON(targetPath, seed, false);\n }\n }\n label = variantName;\n } else if (!fs.existsSync(targetPath)) {\n const seed = this.buildSeedConfig(basePath);\n fsStore.writeJSON(targetPath, seed, false);\n }\n\n await openInEditor(resolvedEditor, targetPath);\n\n return {\n success: true,\n message: `Opened ${label} settings in ${resolvedEditor}`,\n filePath: targetPath,\n data: {\n variant: label,\n ide: resolvedEditor,\n },\n };\n }\n\n private buildEntry(\n filePath: string,\n scope: Scope,\n active: boolean,\n ): SettingsFileEntry {\n const info = fsStore.getFileInfo(filePath);\n\n return {\n filename: path.basename(filePath),\n variant: this.extractVariant(filePath),\n path: filePath,\n scope,\n active,\n exists: Boolean(info),\n size: info?.size,\n lastModified: info?.lastModified,\n };\n }\n\n private extractVariant(filePath: string): string {\n const filename = path.basename(filePath);\n const match = filename.match(/^settings(?:[._-](.+))?\\.json$/);\n if (!match) {\n return filename.replace(/\\.json$/, \"\");\n }\n return match[1] ?? \"default\";\n }\n\n private buildSeedConfig(basePath: string): Record<string, unknown> {\n try {\n if (fs.existsSync(basePath)) {\n const content = fs.readFileSync(basePath, \"utf-8\");\n return JSON.parse(content);\n }\n } catch {\n // ignore, fallback to template\n }\n return { ...CLAUDE_SETTINGS_TEMPLATE };\n }\n\n private isSettingsFile(filename: string): boolean {\n return /^settings[._-].+\\.json$/.test(filename);\n }\n\n private sortFiles(files: SettingsFileEntry[]): SettingsFileEntry[] {\n return files.sort((a, b) => {\n if (a.active && !b.active) return -1;\n if (!a.active && b.active) return 1;\n return a.variant.localeCompare(b.variant);\n });\n }\n\n private findVariantPath(\n directory: string,\n profile: string,\n defaultPath: string,\n ): string | null {\n const candidates: string[] = [];\n const pushCandidate = (value: string) => {\n if (!candidates.includes(value)) {\n candidates.push(value);\n }\n };\n\n if (profile === \"default\") {\n pushCandidate(defaultPath);\n }\n\n if (profile.endsWith(\".json\")) {\n pushCandidate(\n path.isAbsolute(profile) ? profile : path.join(directory, profile),\n );\n } else {\n pushCandidate(path.join(directory, `settings.${profile}.json`));\n pushCandidate(path.join(directory, `settings-${profile}.json`));\n pushCandidate(path.join(directory, `settings_${profile}.json`));\n }\n\n for (const candidate of candidates) {\n if (fs.existsSync(candidate)) {\n return candidate;\n }\n }\n\n return null;\n }\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
//#region src/services/settings/templates/claudeDefault.ts
|
|
2
|
+
const CLAUDE_SETTINGS_TEMPLATE = {
|
|
3
|
+
env: {
|
|
4
|
+
ANTHROPIC_AUTH_TOKEN: "",
|
|
5
|
+
ANTHROPIC_BASE_URL: "https://api.anthropic.com",
|
|
6
|
+
CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC: "1",
|
|
7
|
+
ANTHROPIC_MODEL: "claude-sonnet-4-5-20250929",
|
|
8
|
+
ANTHROPIC_SMALL_FAST_MODEL: "claude-haiku-4-5-20251001"
|
|
9
|
+
},
|
|
10
|
+
permissions: {
|
|
11
|
+
allow: [],
|
|
12
|
+
deny: []
|
|
13
|
+
},
|
|
14
|
+
enabledPlugins: { "example-skills@anthropic-agent-skills": false }
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
//#endregion
|
|
18
|
+
export { CLAUDE_SETTINGS_TEMPLATE as t };
|
|
19
|
+
//# sourceMappingURL=claudeDefault-Fk1-kHLE.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claudeDefault-Fk1-kHLE.js","names":[],"sources":["../src/services/settings/templates/claudeDefault.ts"],"sourcesContent":["export const CLAUDE_SETTINGS_TEMPLATE = {\n env: {\n ANTHROPIC_AUTH_TOKEN: \"\",\n ANTHROPIC_BASE_URL: \"https://api.anthropic.com\",\n CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC: \"1\",\n ANTHROPIC_MODEL: \"claude-sonnet-4-5-20250929\",\n ANTHROPIC_SMALL_FAST_MODEL: \"claude-haiku-4-5-20251001\",\n },\n permissions: {\n allow: [],\n deny: [],\n },\n enabledPlugins: {\n \"example-skills@anthropic-agent-skills\": false,\n },\n} satisfies Record<string, unknown>;\n"],"mappings":";AAAA,MAAa,2BAA2B;CACtC,KAAK;EACH,sBAAsB;EACtB,oBAAoB;EACpB,0CAA0C;EAC1C,iBAAiB;EACjB,4BAA4B;EAC7B;CACD,aAAa;EACX,OAAO,EAAE;EACT,MAAM,EAAE;EACT;CACD,gBAAgB,EACd,yCAAyC,OAC1C;CACF"}
|