ccem 2.30.0 → 2.32.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 (2) hide show
  1. package/dist/index.js +169 -61
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -711,7 +711,7 @@ async function parseJSONLFileAsync(filePath, prices, signal) {
711
711
  throw new Error("Aborted");
712
712
  }
713
713
  if (lineCount % 1e3 === 0) {
714
- await new Promise((resolve3) => setTimeout(resolve3, 0));
714
+ await new Promise((resolve4) => setTimeout(resolve4, 0));
715
715
  }
716
716
  }
717
717
  if (!line.trim()) continue;
@@ -800,7 +800,7 @@ async function getUsageStats(signal) {
800
800
  fileStats = cachedFile.stats;
801
801
  } else {
802
802
  fileStats = await parseJSONLFileAsync(file, prices, signal);
803
- await new Promise((resolve3) => setTimeout(resolve3, 0));
803
+ await new Promise((resolve4) => setTimeout(resolve4, 0));
804
804
  }
805
805
  return {
806
806
  file,
@@ -1312,7 +1312,7 @@ var renderFooterHints = (hints) => {
1312
1312
  return minimal;
1313
1313
  };
1314
1314
  var selectEnvWithKeys = (registries, current) => {
1315
- return new Promise((resolve3) => {
1315
+ return new Promise((resolve4) => {
1316
1316
  const envNames = Object.keys(registries);
1317
1317
  let selectedIndex = envNames.indexOf(current);
1318
1318
  if (selectedIndex === -1) selectedIndex = 0;
@@ -1374,7 +1374,7 @@ var selectEnvWithKeys = (registries, current) => {
1374
1374
  }
1375
1375
  if (char === "\x1B" && key.length === 1) {
1376
1376
  cleanup();
1377
- resolve3({ action: "cancel" });
1377
+ resolve4({ action: "cancel" });
1378
1378
  return;
1379
1379
  }
1380
1380
  if (char === "\x1B[A" || char === "k") {
@@ -1391,27 +1391,27 @@ var selectEnvWithKeys = (registries, current) => {
1391
1391
  }
1392
1392
  if (char === "\r" || char === "\n") {
1393
1393
  cleanup();
1394
- resolve3({ action: "select", name: envNames[selectedIndex] });
1394
+ resolve4({ action: "select", name: envNames[selectedIndex] });
1395
1395
  return;
1396
1396
  }
1397
1397
  if (char === "e" || char === "E") {
1398
1398
  cleanup();
1399
- resolve3({ action: "edit", name: envNames[selectedIndex] });
1399
+ resolve4({ action: "edit", name: envNames[selectedIndex] });
1400
1400
  return;
1401
1401
  }
1402
1402
  if (char === "r" || char === "R") {
1403
1403
  cleanup();
1404
- resolve3({ action: "rename", name: envNames[selectedIndex] });
1404
+ resolve4({ action: "rename", name: envNames[selectedIndex] });
1405
1405
  return;
1406
1406
  }
1407
1407
  if (char === "c" || char === "C") {
1408
1408
  cleanup();
1409
- resolve3({ action: "copy", name: envNames[selectedIndex] });
1409
+ resolve4({ action: "copy", name: envNames[selectedIndex] });
1410
1410
  return;
1411
1411
  }
1412
1412
  if (char === "d" || char === "D") {
1413
1413
  cleanup();
1414
- resolve3({ action: "delete", name: envNames[selectedIndex] });
1414
+ resolve4({ action: "delete", name: envNames[selectedIndex] });
1415
1415
  return;
1416
1416
  }
1417
1417
  };
@@ -1893,7 +1893,7 @@ async function launchClaude(options) {
1893
1893
  console.log(renderStarting());
1894
1894
  }
1895
1895
  const sessionsDir = ensureSessionsDir();
1896
- return new Promise((resolve3) => {
1896
+ return new Promise((resolve4) => {
1897
1897
  let provenanceTracking = null;
1898
1898
  const child = spawn("claude", args, {
1899
1899
  stdio: "inherit",
@@ -2151,7 +2151,7 @@ var setupEnvSettings = () => {
2151
2151
  }
2152
2152
  };
2153
2153
  var setupMcpTool = () => {
2154
- return new Promise((resolve3) => {
2154
+ return new Promise((resolve4) => {
2155
2155
  console.log(chalk4.cyan(" \u2192 \u6B63\u5728\u6DFB\u52A0 chrome-devtools MCP \u5DE5\u5177..."));
2156
2156
  const child = spawn2("claude", [
2157
2157
  "mcp",
@@ -2176,22 +2176,22 @@ var setupMcpTool = () => {
2176
2176
  child.on("exit", (code) => {
2177
2177
  if (code === 0) {
2178
2178
  console.log(chalk4.green(" \u2713 \u5DF2\u6DFB\u52A0 chrome-devtools MCP \u5DE5\u5177"));
2179
- resolve3(true);
2179
+ resolve4(true);
2180
2180
  } else {
2181
2181
  if (stderr.includes("already exists") || stdout.includes("already exists")) {
2182
2182
  console.log(chalk4.gray(" \u2713 chrome-devtools MCP \u5DE5\u5177\u5DF2\u5B58\u5728"));
2183
- resolve3(true);
2183
+ resolve4(true);
2184
2184
  } else {
2185
2185
  console.error(chalk4.red(` \u2717 \u6DFB\u52A0 MCP \u5DE5\u5177\u5931\u8D25 (code: ${code})`));
2186
2186
  if (stderr) console.error(chalk4.gray(` ${stderr.trim()}`));
2187
- resolve3(false);
2187
+ resolve4(false);
2188
2188
  }
2189
2189
  }
2190
2190
  });
2191
2191
  child.on("error", (err) => {
2192
2192
  console.error(chalk4.red(` \u2717 \u6267\u884C claude \u547D\u4EE4\u5931\u8D25: ${err.message}`));
2193
2193
  console.log(chalk4.yellow(" \u8BF7\u786E\u4FDD\u5DF2\u5B89\u88C5 Claude Code CLI"));
2194
- resolve3(false);
2194
+ resolve4(false);
2195
2195
  });
2196
2196
  });
2197
2197
  };
@@ -2221,7 +2221,7 @@ var runSetupInit = async (options = {}) => {
2221
2221
  };
2222
2222
 
2223
2223
  // src/skills.ts
2224
- import { execSync } from "child_process";
2224
+ import { execFileSync as execFileSync2 } from "child_process";
2225
2225
  import * as fs8 from "fs";
2226
2226
  import * as path6 from "path";
2227
2227
  import chalk5 from "chalk";
@@ -2230,6 +2230,58 @@ var SKILL_GROUPS = {
2230
2230
  featured: { label: "\u7CBE\u9009", icon: "\u2B50" },
2231
2231
  others: { label: "\u5176\u4ED6", icon: "\u{1F4E6}" }
2232
2232
  };
2233
+ var SAFE_GITHUB_PART_RE = /^[A-Za-z0-9][A-Za-z0-9._-]*$/;
2234
+ var SAFE_PATH_PART_RE = /^[A-Za-z0-9._-]+$/;
2235
+ var SAFE_GIT_REF_RE = /^[A-Za-z0-9._/-]+$/;
2236
+ var SAFE_PLUGIN_PACKAGE_RE = /^[A-Za-z0-9._@-]+$/;
2237
+ function isSafeGitHubPart(value) {
2238
+ return SAFE_GITHUB_PART_RE.test(value) && value !== "." && value !== "..";
2239
+ }
2240
+ function isSafePathPart(value) {
2241
+ return SAFE_PATH_PART_RE.test(value) && value !== "." && value !== "..";
2242
+ }
2243
+ function isSafeRepoPath(value) {
2244
+ if (!value) return true;
2245
+ if (value.startsWith("/") || value.includes("\\")) return false;
2246
+ const parts = value.split("/");
2247
+ if (parts.some((part) => !part || !isSafePathPart(part))) return false;
2248
+ return true;
2249
+ }
2250
+ function isSafeGitRef(value) {
2251
+ if (!value || !SAFE_GIT_REF_RE.test(value)) return false;
2252
+ if (value.startsWith("/") || value.endsWith("/") || value.includes("//")) return false;
2253
+ if (value.includes("..") || value.includes("@{")) return false;
2254
+ if (value.split("/").some((part) => part === "." || part === ".." || part.endsWith(".lock"))) {
2255
+ return false;
2256
+ }
2257
+ return true;
2258
+ }
2259
+ function isSafeTargetName(value) {
2260
+ return isSafePathPart(value) && !value.startsWith(".");
2261
+ }
2262
+ function isSafeMarketplace(value) {
2263
+ return isSafeRepoPath(value);
2264
+ }
2265
+ function isSafePluginPackage(value) {
2266
+ return SAFE_PLUGIN_PACKAGE_RE.test(value) && !value.includes("..");
2267
+ }
2268
+ function validateGitHubInstallParts(owner, repo, branch, repoPath, targetName) {
2269
+ if (!isSafeGitHubPart(owner)) {
2270
+ throw new Error(`Invalid GitHub owner: ${owner}`);
2271
+ }
2272
+ if (!isSafeGitHubPart(repo)) {
2273
+ throw new Error(`Invalid GitHub repo: ${repo}`);
2274
+ }
2275
+ if (!isSafeGitRef(branch)) {
2276
+ throw new Error(`Invalid Git branch: ${branch}`);
2277
+ }
2278
+ if (!isSafeRepoPath(repoPath)) {
2279
+ throw new Error(`Invalid repository path: ${repoPath}`);
2280
+ }
2281
+ if (!isSafeTargetName(targetName)) {
2282
+ throw new Error(`Invalid skill name: ${targetName}`);
2283
+ }
2284
+ }
2233
2285
  var SKILL_PRESETS = [
2234
2286
  // ===== 官方 (Official) =====
2235
2287
  {
@@ -2375,21 +2427,44 @@ function getGroupOrder() {
2375
2427
  return ["official", "featured", "others"];
2376
2428
  }
2377
2429
  function parseGitHubUrl(url) {
2378
- if (/^[\w-]+\/[\w-]+$/.test(url)) {
2379
- const [owner2, repo2] = url.split("/");
2380
- return { owner: owner2, repo: repo2, branch: "main", path: "" };
2430
+ if (url !== url.trim()) return null;
2431
+ if (/(?:^|\/)\.{1,2}(?:\/|$)/.test(url) || /%2e|%2f/i.test(url)) return null;
2432
+ if (/^[A-Za-z0-9._-]+\/[A-Za-z0-9._-]+$/.test(url)) {
2433
+ const [owner, repo] = url.split("/");
2434
+ if (!isSafeGitHubPart(owner) || !isSafeGitHubPart(repo)) return null;
2435
+ return { owner, repo, branch: "main", path: "" };
2436
+ }
2437
+ try {
2438
+ const parsed = new URL(url);
2439
+ if (parsed.protocol !== "https:" || parsed.hostname !== "github.com") {
2440
+ return null;
2441
+ }
2442
+ if (parsed.search || parsed.hash || parsed.username || parsed.password) {
2443
+ return null;
2444
+ }
2445
+ const parts = parsed.pathname.split("/");
2446
+ if (parts.shift() !== "") return null;
2447
+ const [owner, rawRepo, ...rest] = parts;
2448
+ if (!owner || !rawRepo) return null;
2449
+ const repo = rawRepo.replace(/\.git$/, "");
2450
+ let branch = "main";
2451
+ let repoPath = "";
2452
+ if (rest.length > 0) {
2453
+ if (rest[0] !== "tree" || !rest[1]) return null;
2454
+ branch = rest[1];
2455
+ repoPath = rest.slice(2).join("/");
2456
+ }
2457
+ validateGitHubInstallParts(
2458
+ owner,
2459
+ repo,
2460
+ branch,
2461
+ repoPath,
2462
+ repoPath ? path6.basename(repoPath) : repo
2463
+ );
2464
+ return { owner, repo, branch, path: repoPath };
2465
+ } catch {
2466
+ return null;
2381
2467
  }
2382
- const match = url.match(
2383
- /github\.com\/([^/]+)\/([^/]+)(?:\/tree\/([^/]+)(?:\/(.*))?)?/
2384
- );
2385
- if (!match) return null;
2386
- const [, owner, repo, branch = "main", repoPath = ""] = match;
2387
- return {
2388
- owner,
2389
- repo: repo.replace(/\.git$/, ""),
2390
- branch,
2391
- path: repoPath
2392
- };
2393
2468
  }
2394
2469
  function getSkillsDir() {
2395
2470
  return path6.join(process.cwd(), ".claude", "skills");
@@ -2416,23 +2491,29 @@ function cleanupTempDirs(skillsDir) {
2416
2491
  }
2417
2492
  }
2418
2493
  function downloadSkillWithGit(owner, repo, branch, repoPath, targetName) {
2419
- const skillsDir = ensureSkillsDir();
2420
- const targetDir = path6.join(skillsDir, targetName);
2421
- if (fs8.existsSync(targetDir)) {
2422
- console.log(chalk5.yellow(`Skill "${targetName}" already exists. Updating...`));
2423
- fs8.rmSync(targetDir, { recursive: true });
2424
- }
2425
- const repoUrl = `https://github.com/${owner}/${repo}.git`;
2426
- const tempDir = path6.join(skillsDir, `.tmp-${Date.now()}`);
2494
+ let tempDir = null;
2427
2495
  try {
2496
+ validateGitHubInstallParts(owner, repo, branch, repoPath, targetName);
2497
+ const skillsDir = ensureSkillsDir();
2498
+ const targetDir = path6.join(skillsDir, targetName);
2499
+ if (fs8.existsSync(targetDir)) {
2500
+ console.log(chalk5.yellow(`Skill "${targetName}" already exists. Updating...`));
2501
+ fs8.rmSync(targetDir, { recursive: true });
2502
+ }
2503
+ const repoUrl = `https://github.com/${owner}/${repo}.git`;
2504
+ tempDir = path6.join(skillsDir, `.tmp-${Date.now()}`);
2428
2505
  fs8.mkdirSync(tempDir, { recursive: true });
2429
- execSync(`git init`, { cwd: tempDir, stdio: "pipe" });
2430
- execSync(`git remote add origin ${repoUrl}`, { cwd: tempDir, stdio: "pipe" });
2431
- execSync(`git config core.sparseCheckout true`, { cwd: tempDir, stdio: "pipe" });
2506
+ execFileSync2("git", ["init"], { cwd: tempDir, stdio: "pipe" });
2507
+ execFileSync2("git", ["remote", "add", "origin", repoUrl], { cwd: tempDir, stdio: "pipe" });
2508
+ execFileSync2("git", ["config", "core.sparseCheckout", "true"], { cwd: tempDir, stdio: "pipe" });
2432
2509
  const sparseFile = path6.join(tempDir, ".git", "info", "sparse-checkout");
2510
+ fs8.mkdirSync(path6.dirname(sparseFile), { recursive: true });
2433
2511
  fs8.writeFileSync(sparseFile, repoPath ? `${repoPath}/
2434
2512
  ` : "*\n");
2435
- execSync(`git pull --depth=1 origin ${branch}`, { cwd: tempDir, stdio: "pipe" });
2513
+ execFileSync2("git", ["pull", "--depth=1", "origin", branch], {
2514
+ cwd: tempDir,
2515
+ stdio: "pipe"
2516
+ });
2436
2517
  const sourceDir = repoPath ? path6.join(tempDir, repoPath) : tempDir;
2437
2518
  if (!fs8.existsSync(sourceDir)) {
2438
2519
  throw new Error(`Path "${repoPath}" not found in repository`);
@@ -2445,7 +2526,7 @@ function downloadSkillWithGit(owner, repo, branch, repoPath, targetName) {
2445
2526
  console.error(chalk5.red(`Failed to download skill: ${errMsg}`));
2446
2527
  return false;
2447
2528
  } finally {
2448
- if (fs8.existsSync(tempDir)) {
2529
+ if (tempDir && fs8.existsSync(tempDir)) {
2449
2530
  fs8.rmSync(tempDir, { recursive: true });
2450
2531
  }
2451
2532
  }
@@ -2464,6 +2545,13 @@ function copyDir(src, dest) {
2464
2545
  }
2465
2546
  }
2466
2547
  }
2548
+ function isSafeSkillName(name) {
2549
+ return name.trim().length > 0 && name !== "." && name !== ".." && !name.startsWith(".") && !path6.isAbsolute(name) && !name.includes("/") && !name.includes("\\") && !name.includes("\0");
2550
+ }
2551
+ function isPathInside(childPath, parentPath) {
2552
+ const relativePath = path6.relative(parentPath, childPath);
2553
+ return relativePath === "" || relativePath.length > 0 && !relativePath.startsWith("..") && !path6.isAbsolute(relativePath);
2554
+ }
2467
2555
  function addSkillFromGitHub(urlOrPreset) {
2468
2556
  const preset = SKILL_PRESETS.find((p) => p.name === urlOrPreset);
2469
2557
  if (preset) {
@@ -2530,22 +2618,42 @@ function listInstalledSkills() {
2530
2618
  }));
2531
2619
  }
2532
2620
  function removeSkill(name) {
2621
+ if (!isSafeSkillName(name)) {
2622
+ console.error(chalk5.red(`Invalid skill name "${name}"`));
2623
+ return false;
2624
+ }
2533
2625
  const skillsDir = getSkillsDir();
2534
- const targetDir = path6.join(skillsDir, name);
2626
+ const targetDir = path6.resolve(skillsDir, name);
2535
2627
  if (!fs8.existsSync(targetDir)) {
2536
2628
  console.error(chalk5.red(`Skill "${name}" not found`));
2537
2629
  return false;
2538
2630
  }
2631
+ const canonicalSkillsDir = fs8.realpathSync(skillsDir);
2632
+ const canonicalTargetDir = fs8.realpathSync(targetDir);
2633
+ if (!isPathInside(canonicalTargetDir, canonicalSkillsDir)) {
2634
+ console.error(chalk5.red(`Refusing to remove skill outside skills directory: "${name}"`));
2635
+ return false;
2636
+ }
2539
2637
  fs8.rmSync(targetDir, { recursive: true });
2540
2638
  console.log(chalk5.green(`Removed skill "${name}"`));
2541
2639
  return true;
2542
2640
  }
2543
2641
  function installFromPluginMarketplace(marketplace, packageName) {
2544
2642
  try {
2643
+ if (!isSafeMarketplace(marketplace)) {
2644
+ throw new Error(`Invalid marketplace: ${marketplace}`);
2645
+ }
2646
+ if (!isSafePluginPackage(packageName)) {
2647
+ throw new Error(`Invalid package name: ${packageName}`);
2648
+ }
2545
2649
  console.log(chalk5.cyan(`Adding marketplace: ${marketplace}...`));
2546
- execSync(`claude plugin marketplace add ${marketplace}`, { stdio: "inherit" });
2650
+ execFileSync2("claude", ["plugin", "marketplace", "add", marketplace], {
2651
+ stdio: "inherit"
2652
+ });
2547
2653
  console.log(chalk5.cyan(`Installing package: ${packageName}...`));
2548
- execSync(`claude plugin install ${packageName}`, { stdio: "inherit" });
2654
+ execFileSync2("claude", ["plugin", "install", packageName], {
2655
+ stdio: "inherit"
2656
+ });
2549
2657
  console.log(chalk5.green(`Successfully installed ${packageName}`));
2550
2658
  return true;
2551
2659
  } catch (error) {
@@ -2660,7 +2768,7 @@ function SkillSelector({ onSelect, onCancel }) {
2660
2768
 
2661
2769
  // src/components/index.tsx
2662
2770
  async function runSkillSelector() {
2663
- return new Promise((resolve3) => {
2771
+ return new Promise((resolve4) => {
2664
2772
  let resolved = false;
2665
2773
  const { unmount, waitUntilExit } = render(
2666
2774
  /* @__PURE__ */ React2.createElement(
@@ -2671,23 +2779,23 @@ async function runSkillSelector() {
2671
2779
  resolved = true;
2672
2780
  unmount();
2673
2781
  if (result === "custom") {
2674
- resolve3({ type: "custom" });
2782
+ resolve4({ type: "custom" });
2675
2783
  } else {
2676
- resolve3({ type: "skill", skill: result });
2784
+ resolve4({ type: "skill", skill: result });
2677
2785
  }
2678
2786
  },
2679
2787
  onCancel: () => {
2680
2788
  if (resolved) return;
2681
2789
  resolved = true;
2682
2790
  unmount();
2683
- resolve3({ type: "cancelled" });
2791
+ resolve4({ type: "cancelled" });
2684
2792
  }
2685
2793
  }
2686
2794
  )
2687
2795
  );
2688
2796
  waitUntilExit().then(() => {
2689
2797
  if (!resolved) {
2690
- resolve3({ type: "cancelled" });
2798
+ resolve4({ type: "cancelled" });
2691
2799
  }
2692
2800
  });
2693
2801
  });
@@ -3838,12 +3946,12 @@ var findAttachSession = (id) => {
3838
3946
  };
3839
3947
  var attachTmuxTarget = (target) => {
3840
3948
  const args = process.env.TMUX ? ["switch-client", "-t", target] : ["attach-session", "-t", target];
3841
- return new Promise((resolve3, reject) => {
3949
+ return new Promise((resolve4, reject) => {
3842
3950
  const child = spawn3("tmux", args, { stdio: "inherit" });
3843
3951
  child.on("error", reject);
3844
3952
  child.on("exit", (code) => {
3845
3953
  if (code === 0 || code === null) {
3846
- resolve3();
3954
+ resolve4();
3847
3955
  } else {
3848
3956
  reject(new Error(`tmux exited with code ${code}`));
3849
3957
  }
@@ -4562,11 +4670,11 @@ Editing environment '${result.name}'`));
4562
4670
  registries[result.name] = applyPromptAnswers(envToEdit, answers, true);
4563
4671
  setRegistries(registries);
4564
4672
  msg.success(`Environment '${result.name}' updated.`);
4565
- await new Promise((resolve3) => setTimeout(resolve3, 800));
4673
+ await new Promise((resolve4) => setTimeout(resolve4, 800));
4566
4674
  } else if (result.action === "rename") {
4567
4675
  if (result.name === "official") {
4568
4676
  msg.error("Cannot rename default 'official' environment.");
4569
- await new Promise((resolve3) => setTimeout(resolve3, 800));
4677
+ await new Promise((resolve4) => setTimeout(resolve4, 800));
4570
4678
  } else {
4571
4679
  const { newName } = await inquirer.prompt([
4572
4680
  {
@@ -4587,7 +4695,7 @@ Editing environment '${result.name}'`));
4587
4695
  config2.set("current", newName);
4588
4696
  }
4589
4697
  msg.success(`Environment '${result.name}' renamed to '${newName}'.`);
4590
- await new Promise((resolve3) => setTimeout(resolve3, 800));
4698
+ await new Promise((resolve4) => setTimeout(resolve4, 800));
4591
4699
  }
4592
4700
  } else if (result.action === "copy") {
4593
4701
  const { targetName } = await inquirer.prompt([
@@ -4620,11 +4728,11 @@ Editing environment '${result.name}'`));
4620
4728
  setRegistries(registries);
4621
4729
  msg.success(`Environment '${targetName}' updated.`);
4622
4730
  }
4623
- await new Promise((resolve3) => setTimeout(resolve3, 800));
4731
+ await new Promise((resolve4) => setTimeout(resolve4, 800));
4624
4732
  } else if (result.action === "delete") {
4625
4733
  if (result.name === "official") {
4626
4734
  msg.error("Cannot delete default 'official' environment.");
4627
- await new Promise((resolve3) => setTimeout(resolve3, 800));
4735
+ await new Promise((resolve4) => setTimeout(resolve4, 800));
4628
4736
  } else {
4629
4737
  const { confirm } = await inquirer.prompt([
4630
4738
  {
@@ -4644,7 +4752,7 @@ Editing environment '${result.name}'`));
4644
4752
  msg.success(`Environment '${result.name}' deleted.`);
4645
4753
  }
4646
4754
  }
4647
- await new Promise((resolve3) => setTimeout(resolve3, 800));
4755
+ await new Promise((resolve4) => setTimeout(resolve4, 800));
4648
4756
  }
4649
4757
  }
4650
4758
  } else if (action === "perm") {
@@ -4675,11 +4783,11 @@ Editing environment '${result.name}'`));
4675
4783
  if (selectedMode === "clear") {
4676
4784
  config2.set("defaultMode", null);
4677
4785
  msg.success("Default mode cleared");
4678
- await new Promise((resolve3) => setTimeout(resolve3, 800));
4786
+ await new Promise((resolve4) => setTimeout(resolve4, 800));
4679
4787
  } else if (selectedMode !== "back") {
4680
4788
  config2.set("defaultMode", selectedMode);
4681
4789
  msg.success(`Default mode set: ${PERMISSION_PRESETS[selectedMode].name}`);
4682
- await new Promise((resolve3) => setTimeout(resolve3, 800));
4790
+ await new Promise((resolve4) => setTimeout(resolve4, 800));
4683
4791
  }
4684
4792
  } else {
4685
4793
  process.exit(0);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ccem",
3
- "version": "2.30.0",
3
+ "version": "2.32.0",
4
4
  "type": "module",
5
5
  "description": "Claude Code Environment Manager",
6
6
  "repository": {