ccjk 10.2.0 → 11.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/README.md +28 -0
  2. package/dist/chunks/auto-init.mjs +7585 -0
  3. package/dist/chunks/ccr.mjs +2 -1
  4. package/dist/chunks/check-updates.mjs +1 -0
  5. package/dist/chunks/claude-code-config-manager.mjs +1 -0
  6. package/dist/chunks/claude-code-incremental-manager.mjs +2 -1
  7. package/dist/chunks/codex-config-switch.mjs +1 -0
  8. package/dist/chunks/codex-provider-manager.mjs +1 -0
  9. package/dist/chunks/config-switch.mjs +1 -0
  10. package/dist/chunks/config.mjs +2 -94
  11. package/dist/chunks/config2.mjs +1 -0
  12. package/dist/chunks/config3.mjs +1 -0
  13. package/dist/chunks/evolution.mjs +383 -0
  14. package/dist/chunks/features.mjs +2 -1
  15. package/dist/chunks/init.mjs +1 -1
  16. package/dist/chunks/mcp.mjs +1 -0
  17. package/dist/chunks/menu.mjs +7 -1
  18. package/dist/chunks/package.mjs +1 -1
  19. package/dist/chunks/quick-provider.mjs +3 -0
  20. package/dist/chunks/quick-setup.mjs +2 -1
  21. package/dist/chunks/remote.mjs +166 -0
  22. package/dist/chunks/simple-config.mjs +1 -6
  23. package/dist/chunks/update.mjs +2 -1
  24. package/dist/chunks/zero-config.mjs +359 -0
  25. package/dist/cli.mjs +107 -0
  26. package/dist/i18n/locales/en/configuration.json +33 -0
  27. package/dist/i18n/locales/en/evolution.json +54 -0
  28. package/dist/i18n/locales/en/remote.json +39 -0
  29. package/dist/i18n/locales/zh-CN/configuration.json +33 -0
  30. package/dist/i18n/locales/zh-CN/evolution.json +54 -0
  31. package/dist/i18n/locales/zh-CN/remote.json +39 -0
  32. package/dist/index.mjs +3 -73
  33. package/dist/shared/ccjk.BiCrMV5O.mjs +94 -0
  34. package/dist/shared/ccjk.Cu_R2MbQ.mjs +75 -0
  35. package/dist/shared/{ccjk.IbImMVWM.mjs → ccjk.DVBW2wxp.mjs} +2 -1
  36. package/package.json +7 -1
@@ -566,6 +566,8 @@ const KNOWN_COMMANDS = /* @__PURE__ */ new Set([
566
566
  "vim",
567
567
  "permissions",
568
568
  "perm",
569
+ "zero-config",
570
+ "zc",
569
571
  "skills",
570
572
  "sk",
571
573
  "skill",
@@ -600,6 +602,7 @@ const KNOWN_COMMANDS = /* @__PURE__ */ new Set([
600
602
  "status",
601
603
  "st",
602
604
  "boost",
605
+ "evolution",
603
606
  // CCJK v8 commands
604
607
  "ccjk:mcp",
605
608
  "ccjk-mcp",
@@ -28,11 +28,12 @@ import 'ora';
28
28
  import 'semver';
29
29
  import './config.mjs';
30
30
  import './claude-config.mjs';
31
+ import '../shared/ccjk.BiCrMV5O.mjs';
31
32
  import '../shared/ccjk.BFQ7yr5S.mjs';
32
33
  import './prompts.mjs';
33
34
  import '../shared/ccjk.DHbrGcgg.mjs';
34
35
  import 'inquirer-toggle';
35
- import '../shared/ccjk.IbImMVWM.mjs';
36
+ import '../shared/ccjk.DVBW2wxp.mjs';
36
37
  import './banner.mjs';
37
38
  import './config2.mjs';
38
39
  import 'node:util';
@@ -0,0 +1,166 @@
1
+ import { l as logger } from '../shared/ccjk.Cu_R2MbQ.mjs';
2
+ import { i18n } from './index2.mjs';
3
+ import inquirer from 'inquirer';
4
+ import { spawn } from 'child_process';
5
+ import { homedir } from 'os';
6
+ import { join } from 'path';
7
+ import { existsSync, readFileSync, writeFileSync } from 'fs';
8
+ import ora from 'ora';
9
+ import 'ansis';
10
+ import 'node:fs';
11
+ import 'node:process';
12
+ import 'node:url';
13
+ import 'i18next';
14
+ import 'i18next-fs-backend';
15
+ import 'pathe';
16
+
17
+ const DAEMON_CONFIG_PATH = join(homedir(), ".ccjk", "daemon.json");
18
+ async function enableRemote() {
19
+ console.log(i18n.t("remote:enable.title"));
20
+ const config = loadDaemonConfig();
21
+ if (config.enabled) {
22
+ console.log(i18n.t("remote:enable.already_enabled"));
23
+ return;
24
+ }
25
+ const { serverUrl } = await inquirer.prompt([
26
+ {
27
+ type: "input",
28
+ name: "serverUrl",
29
+ message: i18n.t("remote:enable.server_url"),
30
+ default: "https://ccjk-server.example.com"
31
+ }
32
+ ]);
33
+ const machineId = `machine-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;
34
+ const newConfig = {
35
+ enabled: true,
36
+ serverUrl,
37
+ machineId
38
+ };
39
+ saveDaemonConfig(newConfig);
40
+ console.log(i18n.t("remote:enable.success"));
41
+ console.log(i18n.t("remote:enable.next_steps"));
42
+ }
43
+ async function disableRemote() {
44
+ console.log(i18n.t("remote:disable.title"));
45
+ const config = loadDaemonConfig();
46
+ if (!config.enabled) {
47
+ console.log(i18n.t("remote:disable.already_disabled"));
48
+ return;
49
+ }
50
+ await stopDaemon();
51
+ config.enabled = false;
52
+ saveDaemonConfig(config);
53
+ console.log(i18n.t("remote:disable.success"));
54
+ }
55
+ async function remoteStatus() {
56
+ const config = loadDaemonConfig();
57
+ console.log(i18n.t("remote:status.title"));
58
+ console.log(` ${i18n.t("remote:status.enabled")}: ${config.enabled ? "\u2705" : "\u274C"}`);
59
+ if (config.enabled) {
60
+ console.log(` ${i18n.t("remote:status.server")}: ${config.serverUrl}`);
61
+ console.log(` ${i18n.t("remote:status.machine_id")}: ${config.machineId}`);
62
+ const daemonRunning = await isDaemonRunning();
63
+ console.log(` ${i18n.t("remote:status.daemon")}: ${daemonRunning ? "\u{1F7E2} Running" : "\u{1F534} Stopped"}`);
64
+ }
65
+ }
66
+ async function showQRCode() {
67
+ const config = loadDaemonConfig();
68
+ if (!config.enabled) {
69
+ console.log(i18n.t("remote:qr.not_enabled"));
70
+ return;
71
+ }
72
+ const pairingData = {
73
+ serverUrl: config.serverUrl,
74
+ machineId: config.machineId,
75
+ timestamp: Date.now()
76
+ };
77
+ const pairingUrl = `ccjk://pair?data=${encodeURIComponent(JSON.stringify(pairingData))}`;
78
+ console.log(i18n.t("remote:qr.title"));
79
+ console.log(i18n.t("remote:qr.instructions"));
80
+ console.log();
81
+ console.log(` ${pairingUrl}`);
82
+ console.log();
83
+ console.log(i18n.t("remote:qr.manual_entry"));
84
+ console.log(` Server: ${config.serverUrl}`);
85
+ console.log(` Machine ID: ${config.machineId}`);
86
+ }
87
+ async function startDaemon() {
88
+ const spinner = ora(i18n.t("remote:daemon.starting")).start();
89
+ try {
90
+ if (await isDaemonRunning()) {
91
+ spinner.warn(i18n.t("remote:daemon.already_running"));
92
+ return;
93
+ }
94
+ const daemon = spawn("ccjk-daemon", ["start"], {
95
+ detached: true,
96
+ stdio: "ignore"
97
+ });
98
+ daemon.unref();
99
+ await new Promise((resolve) => setTimeout(resolve, 2e3));
100
+ if (await isDaemonRunning()) {
101
+ spinner.succeed(i18n.t("remote:daemon.started"));
102
+ } else {
103
+ spinner.fail(i18n.t("remote:daemon.start_failed"));
104
+ }
105
+ } catch (error) {
106
+ spinner.fail(i18n.t("remote:daemon.start_error"));
107
+ logger.error("Failed to start daemon:", error);
108
+ }
109
+ }
110
+ async function stopDaemon() {
111
+ const spinner = ora(i18n.t("remote:daemon.stopping")).start();
112
+ try {
113
+ if (!await isDaemonRunning()) {
114
+ spinner.warn(i18n.t("remote:daemon.not_running"));
115
+ return;
116
+ }
117
+ const response = await fetch("http://127.0.0.1:37821/stop", {
118
+ method: "POST"
119
+ });
120
+ if (response.ok) {
121
+ spinner.succeed(i18n.t("remote:daemon.stopped"));
122
+ } else {
123
+ spinner.fail(i18n.t("remote:daemon.stop_failed"));
124
+ }
125
+ } catch (error) {
126
+ spinner.fail(i18n.t("remote:daemon.stop_error"));
127
+ logger.error("Failed to stop daemon:", error);
128
+ }
129
+ }
130
+ async function isDaemonRunning() {
131
+ try {
132
+ const response = await fetch("http://127.0.0.1:37821/health", {
133
+ signal: AbortSignal.timeout(1e3)
134
+ });
135
+ return response.ok;
136
+ } catch {
137
+ return false;
138
+ }
139
+ }
140
+ function loadDaemonConfig() {
141
+ try {
142
+ if (existsSync(DAEMON_CONFIG_PATH)) {
143
+ return JSON.parse(readFileSync(DAEMON_CONFIG_PATH, "utf-8"));
144
+ }
145
+ } catch (error) {
146
+ logger.error("Failed to load daemon config:", error);
147
+ }
148
+ return {
149
+ enabled: false,
150
+ serverUrl: ""
151
+ };
152
+ }
153
+ function saveDaemonConfig(config) {
154
+ try {
155
+ const dir = join(homedir(), ".ccjk");
156
+ if (!existsSync(dir)) {
157
+ require("fs").mkdirSync(dir, { recursive: true });
158
+ }
159
+ writeFileSync(DAEMON_CONFIG_PATH, JSON.stringify(config, null, 2));
160
+ } catch (error) {
161
+ logger.error("Failed to save daemon config:", error);
162
+ throw error;
163
+ }
164
+ }
165
+
166
+ export { disableRemote, enableRemote, remoteStatus, showQRCode, startDaemon, stopDaemon };
@@ -4,7 +4,7 @@ import { join, dirname } from 'pathe';
4
4
  import { exec } from 'tinyexec';
5
5
  import { SETTINGS_FILE, CLAUDE_DIR } from './constants.mjs';
6
6
  import { ensureDir, writeFileAtomic } from './fs-operations.mjs';
7
- import { m as mergeAndCleanPermissions } from './config.mjs';
7
+ import { m as mergeAndCleanPermissions } from '../shared/ccjk.BiCrMV5O.mjs';
8
8
  import { g as getPlatform } from './platform.mjs';
9
9
  import 'node:os';
10
10
  import './index2.mjs';
@@ -13,11 +13,6 @@ import 'i18next';
13
13
  import 'i18next-fs-backend';
14
14
  import 'node:crypto';
15
15
  import 'node:fs/promises';
16
- import 'ansis';
17
- import 'dayjs';
18
- import 'inquirer';
19
- import './claude-config.mjs';
20
- import './json-config.mjs';
21
16
 
22
17
  const __dirname = dirname(fileURLToPath(import.meta.url));
23
18
  function getTemplateSettings() {
@@ -9,7 +9,7 @@ import { displayBanner } from './banner.mjs';
9
9
  import { readZcfConfig, updateZcfConfig } from './ccjk-config.mjs';
10
10
  import { r as readMcpConfig } from './claude-config.mjs';
11
11
  import { c as copyConfigFiles } from './config.mjs';
12
- import { n as needsMigration, m as migrateSettingsForTokenRetrieval, d as displayMigrationResult, p as promptMigration, u as updatePromptOnly, s as selectAndInstallWorkflows } from '../shared/ccjk.IbImMVWM.mjs';
12
+ import { n as needsMigration, m as migrateSettingsForTokenRetrieval, d as displayMigrationResult, p as promptMigration, u as updatePromptOnly, s as selectAndInstallWorkflows } from '../shared/ccjk.DVBW2wxp.mjs';
13
13
  import { h as handleExitPromptError, a as handleGeneralError } from '../shared/ccjk.DrMygfCF.mjs';
14
14
  import { i as installMcpServices } from '../shared/ccjk.DB2UYcq0.mjs';
15
15
  import { resolveAiOutputLanguage } from './prompts.mjs';
@@ -34,6 +34,7 @@ import 'inquirer-toggle';
34
34
  import 'node:child_process';
35
35
  import 'i18next';
36
36
  import 'i18next-fs-backend';
37
+ import '../shared/ccjk.BiCrMV5O.mjs';
37
38
  import 'node:path';
38
39
  import 'node:util';
39
40
 
@@ -0,0 +1,359 @@
1
+ import { existsSync, readFileSync } from 'node:fs';
2
+ import ansis from 'ansis';
3
+ import inquirer from 'inquirer';
4
+ import { SETTINGS_FILE, CLAUDE_DIR } from './constants.mjs';
5
+ import { i18n } from './index2.mjs';
6
+ import { ensureDir, writeFileAtomic } from './fs-operations.mjs';
7
+ import { m as mergeAndCleanPermissions } from '../shared/ccjk.BiCrMV5O.mjs';
8
+ import { a as addNumbersToChoices } from '../shared/ccjk.BFQ7yr5S.mjs';
9
+ import 'node:os';
10
+ import 'pathe';
11
+ import 'node:process';
12
+ import 'node:url';
13
+ import 'i18next';
14
+ import 'i18next-fs-backend';
15
+ import 'node:crypto';
16
+ import 'node:fs/promises';
17
+
18
+ const MAX_PRESET = {
19
+ id: "max",
20
+ name: "Maximum Permissions",
21
+ description: "All common commands, file operations, and MCP servers",
22
+ permissions: [
23
+ // Package managers
24
+ "Bash(pnpm *)",
25
+ "Bash(npm *)",
26
+ "Bash(npx *)",
27
+ "Bash(NODE_ENV=* pnpm *)",
28
+ "Bash(yarn *)",
29
+ "Bash(bun *)",
30
+ "Bash(deno *)",
31
+ // Version control
32
+ "Bash(git *)",
33
+ // Build tools
34
+ "Bash(make *)",
35
+ "Bash(cmake *)",
36
+ "Bash(cargo *)",
37
+ "Bash(go *)",
38
+ "Bash(rustc *)",
39
+ // Container tools
40
+ "Bash(docker *)",
41
+ "Bash(docker-compose *)",
42
+ "Bash(podman *)",
43
+ // Programming languages
44
+ "Bash(python *)",
45
+ "Bash(python3 *)",
46
+ "Bash(node *)",
47
+ "Bash(ruby *)",
48
+ "Bash(php *)",
49
+ "Bash(java *)",
50
+ "Bash(javac *)",
51
+ // Shell utilities
52
+ "Bash(which *)",
53
+ "Bash(cat *)",
54
+ "Bash(ls *)",
55
+ "Bash(echo *)",
56
+ "Bash(grep *)",
57
+ "Bash(find *)",
58
+ "Bash(head *)",
59
+ "Bash(tail *)",
60
+ "Bash(wc *)",
61
+ "Bash(sort *)",
62
+ "Bash(uniq *)",
63
+ "Bash(cut *)",
64
+ "Bash(sed *)",
65
+ "Bash(awk *)",
66
+ "Bash(tr *)",
67
+ "Bash(xargs *)",
68
+ // File operations
69
+ "Bash(mkdir *)",
70
+ "Bash(touch *)",
71
+ "Bash(cp *)",
72
+ "Bash(mv *)",
73
+ "Bash(chmod *)",
74
+ "Bash(chown *)",
75
+ "Bash(ln *)",
76
+ // Network tools
77
+ "Bash(curl *)",
78
+ "Bash(wget *)",
79
+ "Bash(ping *)",
80
+ "Bash(netstat *)",
81
+ "Bash(ss *)",
82
+ // System info
83
+ "Bash(ps *)",
84
+ "Bash(top *)",
85
+ "Bash(htop *)",
86
+ "Bash(df *)",
87
+ "Bash(du *)",
88
+ "Bash(free *)",
89
+ "Bash(uname *)",
90
+ // Text editors
91
+ "Bash(vim *)",
92
+ "Bash(nano *)",
93
+ "Bash(emacs *)",
94
+ "Bash(code *)",
95
+ // Compression
96
+ "Bash(tar *)",
97
+ "Bash(gzip *)",
98
+ "Bash(gunzip *)",
99
+ "Bash(zip *)",
100
+ "Bash(unzip *)",
101
+ // Package managers (system)
102
+ "Bash(brew *)",
103
+ "Bash(apt *)",
104
+ "Bash(apt-get *)",
105
+ "Bash(yum *)",
106
+ "Bash(dnf *)",
107
+ "Bash(pacman *)",
108
+ // File operations (Claude Code tools)
109
+ "Read(*)",
110
+ "Edit(*)",
111
+ "Write(*)",
112
+ "NotebookEdit(*)",
113
+ // Web access
114
+ "WebFetch(*)",
115
+ // MCP servers (wildcard for all)
116
+ "MCP(*)"
117
+ ],
118
+ env: {
119
+ ANTHROPIC_MODEL: "",
120
+ ANTHROPIC_DEFAULT_HAIKU_MODEL: "",
121
+ ANTHROPIC_DEFAULT_SONNET_MODEL: "",
122
+ ANTHROPIC_DEFAULT_OPUS_MODEL: ""
123
+ }
124
+ };
125
+ const DEV_PRESET = {
126
+ id: "dev",
127
+ name: "Developer Preset",
128
+ description: "Build tools, git, package managers, and file operations",
129
+ permissions: [
130
+ // Package managers
131
+ "Bash(pnpm *)",
132
+ "Bash(npm *)",
133
+ "Bash(npx *)",
134
+ "Bash(NODE_ENV=* pnpm *)",
135
+ "Bash(yarn *)",
136
+ "Bash(bun *)",
137
+ // Version control
138
+ "Bash(git *)",
139
+ // Build tools
140
+ "Bash(make *)",
141
+ "Bash(cargo *)",
142
+ "Bash(go *)",
143
+ // Programming languages
144
+ "Bash(python *)",
145
+ "Bash(python3 *)",
146
+ "Bash(node *)",
147
+ // Shell utilities
148
+ "Bash(which *)",
149
+ "Bash(cat *)",
150
+ "Bash(ls *)",
151
+ "Bash(echo *)",
152
+ "Bash(grep *)",
153
+ "Bash(find *)",
154
+ "Bash(head *)",
155
+ "Bash(tail *)",
156
+ "Bash(wc *)",
157
+ "Bash(sort *)",
158
+ // File operations
159
+ "Bash(mkdir *)",
160
+ "Bash(touch *)",
161
+ "Bash(cp *)",
162
+ "Bash(mv *)",
163
+ "Bash(chmod *)",
164
+ // File operations (Claude Code tools)
165
+ "Read(*)",
166
+ "Edit(*)",
167
+ "Write(*)",
168
+ "NotebookEdit(*)",
169
+ // Web access for docs
170
+ "WebFetch(*)"
171
+ ],
172
+ env: {
173
+ ANTHROPIC_MODEL: ""
174
+ }
175
+ };
176
+ const SAFE_PRESET = {
177
+ id: "safe",
178
+ name: "Safe Preset",
179
+ description: "Read-only commands, no file modifications",
180
+ permissions: [
181
+ // Read-only shell utilities
182
+ "Bash(which *)",
183
+ "Bash(cat *)",
184
+ "Bash(ls *)",
185
+ "Bash(echo *)",
186
+ "Bash(grep *)",
187
+ "Bash(find *)",
188
+ "Bash(head *)",
189
+ "Bash(tail *)",
190
+ "Bash(wc *)",
191
+ "Bash(sort *)",
192
+ "Bash(uniq *)",
193
+ "Bash(cut *)",
194
+ // System info (read-only)
195
+ "Bash(ps *)",
196
+ "Bash(df *)",
197
+ "Bash(du *)",
198
+ "Bash(uname *)",
199
+ // Git read operations
200
+ "Bash(git status *)",
201
+ "Bash(git log *)",
202
+ "Bash(git diff *)",
203
+ "Bash(git show *)",
204
+ "Bash(git branch *)",
205
+ // File operations (read-only)
206
+ "Read(*)",
207
+ // Web access
208
+ "WebFetch(*)"
209
+ ]
210
+ };
211
+ const PRESETS = [MAX_PRESET, DEV_PRESET, SAFE_PRESET];
212
+ function loadCurrentSettings() {
213
+ if (!existsSync(SETTINGS_FILE)) {
214
+ return {};
215
+ }
216
+ try {
217
+ const content = readFileSync(SETTINGS_FILE, "utf-8");
218
+ return JSON.parse(content);
219
+ } catch {
220
+ return {};
221
+ }
222
+ }
223
+ function saveSettings(settings) {
224
+ ensureDir(CLAUDE_DIR);
225
+ writeFileAtomic(SETTINGS_FILE, JSON.stringify(settings, null, 2));
226
+ }
227
+ function backupSettings() {
228
+ if (!existsSync(SETTINGS_FILE)) {
229
+ return null;
230
+ }
231
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").slice(0, -5);
232
+ const backupDir = `${CLAUDE_DIR}/backup`;
233
+ ensureDir(backupDir);
234
+ const backupPath = `${backupDir}/settings-${timestamp}.json`;
235
+ const content = readFileSync(SETTINGS_FILE, "utf-8");
236
+ writeFileAtomic(backupPath, content);
237
+ return backupPath;
238
+ }
239
+ function applyPreset(preset, currentSettings) {
240
+ const newSettings = { ...currentSettings };
241
+ if (!newSettings.permissions) {
242
+ newSettings.permissions = { allow: [] };
243
+ }
244
+ newSettings.permissions.allow = mergeAndCleanPermissions(
245
+ preset.permissions,
246
+ newSettings.permissions.allow || []
247
+ );
248
+ if (preset.env) {
249
+ newSettings.env = {
250
+ ...newSettings.env,
251
+ ...preset.env
252
+ };
253
+ }
254
+ return newSettings;
255
+ }
256
+ function showPresetDiff(preset, currentSettings) {
257
+ const isZh = i18n.language === "zh-CN";
258
+ const currentPermissions = new Set(currentSettings.permissions?.allow || []);
259
+ const newPermissions = preset.permissions.filter((p) => !currentPermissions.has(p));
260
+ console.log("");
261
+ console.log(ansis.bold.cyan(isZh ? "\u{1F4CB} \u9884\u8BBE\u8BE6\u60C5" : "\u{1F4CB} Preset Details"));
262
+ console.log(ansis.dim("\u2500".repeat(60)));
263
+ console.log(`${ansis.green("Name:")} ${preset.name}`);
264
+ console.log(`${ansis.green("Description:")} ${preset.description}`);
265
+ console.log("");
266
+ if (newPermissions.length > 0) {
267
+ console.log(ansis.bold.yellow(isZh ? "\u2728 \u5C06\u6DFB\u52A0\u7684\u6743\u9650:" : "\u2728 Permissions to be added:"));
268
+ console.log(ansis.dim(` ${isZh ? "\u603B\u8BA1" : "Total"}: ${newPermissions.length} ${isZh ? "\u9879" : "items"}`));
269
+ const bashPerms = newPermissions.filter((p) => p.startsWith("Bash("));
270
+ const filePerms = newPermissions.filter((p) => ["Read", "Edit", "Write", "NotebookEdit"].some((t) => p.startsWith(t)));
271
+ const otherPerms = newPermissions.filter((p) => !bashPerms.includes(p) && !filePerms.includes(p));
272
+ if (bashPerms.length > 0) {
273
+ console.log(` ${ansis.cyan("Bash:")} ${bashPerms.length} ${isZh ? "\u4E2A\u547D\u4EE4" : "commands"}`);
274
+ }
275
+ if (filePerms.length > 0) {
276
+ console.log(` ${ansis.cyan("File:")} ${filePerms.length} ${isZh ? "\u4E2A\u64CD\u4F5C" : "operations"}`);
277
+ }
278
+ if (otherPerms.length > 0) {
279
+ console.log(` ${ansis.cyan("Other:")} ${otherPerms.length} ${isZh ? "\u9879" : "items"}`);
280
+ }
281
+ } else {
282
+ console.log(ansis.yellow(isZh ? "\u2713 \u6240\u6709\u6743\u9650\u5DF2\u5B58\u5728" : "\u2713 All permissions already exist"));
283
+ }
284
+ console.log(ansis.dim("\u2500".repeat(60)));
285
+ console.log("");
286
+ }
287
+ async function zeroConfig(options = {}) {
288
+ const isZh = i18n.language === "zh-CN";
289
+ if (options.list) {
290
+ console.log("");
291
+ console.log(ansis.bold.cyan(isZh ? "\u{1F4E6} \u53EF\u7528\u7684\u6743\u9650\u9884\u8BBE" : "\u{1F4E6} Available Permission Presets"));
292
+ console.log(ansis.dim("\u2500".repeat(60)));
293
+ for (const preset of PRESETS) {
294
+ console.log(` ${ansis.green(preset.id.padEnd(8))} - ${preset.name}`);
295
+ console.log(` ${ansis.dim(" ".repeat(10))}${preset.description}`);
296
+ console.log(` ${ansis.dim(" ".repeat(10))}${preset.permissions.length} ${isZh ? "\u9879\u6743\u9650" : "permissions"}`);
297
+ console.log("");
298
+ }
299
+ console.log(ansis.dim("\u2500".repeat(60)));
300
+ console.log(ansis.gray(isZh ? "\u4F7F\u7528: npx ccjk zc --preset=<id>" : "Usage: npx ccjk zc --preset=<id>"));
301
+ console.log("");
302
+ return;
303
+ }
304
+ let selectedPreset;
305
+ if (options.preset) {
306
+ selectedPreset = PRESETS.find((p) => p.id === options.preset);
307
+ if (!selectedPreset) {
308
+ console.error(ansis.red(isZh ? `\u9519\u8BEF: \u672A\u627E\u5230\u9884\u8BBE "${options.preset}"` : `Error: Preset "${options.preset}" not found`));
309
+ console.log(ansis.gray(isZh ? "\u4F7F\u7528 --list \u67E5\u770B\u53EF\u7528\u9884\u8BBE" : "Use --list to see available presets"));
310
+ return;
311
+ }
312
+ } else {
313
+ const { presetId } = await inquirer.prompt({
314
+ type: "list",
315
+ name: "presetId",
316
+ message: isZh ? "\u9009\u62E9\u6743\u9650\u9884\u8BBE:" : "Select permission preset:",
317
+ choices: addNumbersToChoices(
318
+ PRESETS.map((p) => ({
319
+ name: `${p.name} - ${ansis.gray(p.description)}`,
320
+ value: p.id,
321
+ short: p.name
322
+ }))
323
+ )
324
+ });
325
+ selectedPreset = PRESETS.find((p) => p.id === presetId);
326
+ if (!selectedPreset) {
327
+ console.log(ansis.yellow(i18n.t("common:cancelled")));
328
+ return;
329
+ }
330
+ }
331
+ const currentSettings = loadCurrentSettings();
332
+ showPresetDiff(selectedPreset, currentSettings);
333
+ if (!options.preset) {
334
+ const { confirm } = await inquirer.prompt({
335
+ type: "confirm",
336
+ name: "confirm",
337
+ message: isZh ? "\u786E\u8BA4\u5E94\u7528\u6B64\u9884\u8BBE?" : "Confirm applying this preset?",
338
+ default: true
339
+ });
340
+ if (!confirm) {
341
+ console.log(ansis.yellow(i18n.t("common:cancelled")));
342
+ return;
343
+ }
344
+ }
345
+ if (!options.skipBackup) {
346
+ const backupPath = backupSettings();
347
+ if (backupPath) {
348
+ console.log(ansis.gray(`\u2714 ${isZh ? "\u5DF2\u5907\u4EFD\u5230" : "Backed up to"}: ${backupPath}`));
349
+ }
350
+ }
351
+ const newSettings = applyPreset(selectedPreset, currentSettings);
352
+ saveSettings(newSettings);
353
+ console.log("");
354
+ console.log(ansis.green(`\u2705 ${isZh ? "\u6743\u9650\u9884\u8BBE\u5DF2\u5E94\u7528" : "Permission preset applied"}: ${selectedPreset.name}`));
355
+ console.log(ansis.gray(` ${isZh ? "\u914D\u7F6E\u6587\u4EF6" : "Config file"}: ${SETTINGS_FILE}`));
356
+ console.log("");
357
+ }
358
+
359
+ export { zeroConfig };