ccem 2.0.0-beta.8 → 2.0.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.
package/dist/index.js CHANGED
@@ -7,11 +7,11 @@ import inquirer from "inquirer";
7
7
  import chalk7 from "chalk";
8
8
  import Table3 from "cli-table3";
9
9
  import { spawn as spawn3 } from "child_process";
10
- import * as fs8 from "fs";
11
- import * as path6 from "path";
10
+ import * as fs9 from "fs";
11
+ import * as path7 from "path";
12
12
  import { fileURLToPath as fileURLToPath2 } from "url";
13
13
 
14
- // ../../packages/core/dist/chunk-6STWB3RD.js
14
+ // ../../packages/core/dist/chunk-25EB45SM.js
15
15
  var TIER_MODEL_ALIASES = /* @__PURE__ */ new Set(["opus", "sonnet", "haiku"]);
16
16
  function normalizeEnvConfig(envConfig, defaultRuntimeModel = "opus") {
17
17
  const hasTierDefaults = Boolean(envConfig.ANTHROPIC_DEFAULT_OPUS_MODEL) || Boolean(envConfig.ANTHROPIC_DEFAULT_SONNET_MODEL) || Boolean(envConfig.ANTHROPIC_DEFAULT_HAIKU_MODEL);
@@ -68,8 +68,8 @@ function recoverEnvConfigFromLegacy(currentEnvConfig, legacyEnvConfig) {
68
68
  var ENV_PRESETS = {
69
69
  "GLM": {
70
70
  ANTHROPIC_BASE_URL: "https://open.bigmodel.cn/api/anthropic",
71
- ANTHROPIC_DEFAULT_OPUS_MODEL: "glm-5",
72
- ANTHROPIC_DEFAULT_SONNET_MODEL: "glm-5",
71
+ ANTHROPIC_DEFAULT_OPUS_MODEL: "glm-5.1",
72
+ ANTHROPIC_DEFAULT_SONNET_MODEL: "glm-5.1",
73
73
  ANTHROPIC_DEFAULT_HAIKU_MODEL: "glm-4.5-air",
74
74
  ANTHROPIC_MODEL: "opus"
75
75
  },
@@ -96,30 +96,51 @@ var ENV_PRESETS = {
96
96
  },
97
97
  "DeepSeek": {
98
98
  ANTHROPIC_BASE_URL: "https://api.deepseek.com/anthropic",
99
- ANTHROPIC_DEFAULT_OPUS_MODEL: "deepseek-chat",
100
- ANTHROPIC_DEFAULT_SONNET_MODEL: "deepseek-chat",
101
- ANTHROPIC_DEFAULT_HAIKU_MODEL: "deepseek-chat",
99
+ ANTHROPIC_DEFAULT_OPUS_MODEL: "deepseek-v4-pro[1m]",
100
+ ANTHROPIC_DEFAULT_SONNET_MODEL: "deepseek-v4-pro[1m]",
101
+ ANTHROPIC_DEFAULT_HAIKU_MODEL: "deepseek-v4-flash",
102
102
  ANTHROPIC_MODEL: "opus"
103
103
  },
104
104
  "Bailian": {
105
105
  ANTHROPIC_BASE_URL: "https://dashscope.aliyuncs.com/api/v2/apps/claude-code-proxy",
106
- ANTHROPIC_DEFAULT_OPUS_MODEL: "qwen3-coder-plus",
106
+ ANTHROPIC_DEFAULT_OPUS_MODEL: "qwen3-coder-next",
107
107
  ANTHROPIC_DEFAULT_SONNET_MODEL: "qwen3-coder-plus",
108
108
  ANTHROPIC_DEFAULT_HAIKU_MODEL: "qwen3-coder-flash",
109
109
  ANTHROPIC_MODEL: "opus"
110
110
  },
111
111
  "BailianCodePlan": {
112
112
  ANTHROPIC_BASE_URL: "https://coding.dashscope.aliyuncs.com/api/v2/apps/claude-code-proxy",
113
- ANTHROPIC_DEFAULT_OPUS_MODEL: "qwen3-coder-plus",
113
+ ANTHROPIC_DEFAULT_OPUS_MODEL: "qwen3-coder-next",
114
114
  ANTHROPIC_DEFAULT_SONNET_MODEL: "qwen3-coder-plus",
115
- ANTHROPIC_DEFAULT_HAIKU_MODEL: "qwen3-coder-plus",
115
+ ANTHROPIC_DEFAULT_HAIKU_MODEL: "qwen3-coder-flash",
116
116
  ANTHROPIC_MODEL: "opus"
117
117
  },
118
118
  "OpenRouter": {
119
119
  ANTHROPIC_BASE_URL: "https://openrouter.ai/api/v1",
120
- ANTHROPIC_DEFAULT_OPUS_MODEL: "anthropic/claude-opus-4-1",
121
- ANTHROPIC_DEFAULT_SONNET_MODEL: "anthropic/claude-opus-4-1",
122
- ANTHROPIC_DEFAULT_HAIKU_MODEL: "anthropic/claude-3.5-haiku",
120
+ ANTHROPIC_DEFAULT_OPUS_MODEL: "anthropic/claude-opus-4.6",
121
+ ANTHROPIC_DEFAULT_SONNET_MODEL: "anthropic/claude-sonnet-4.6",
122
+ ANTHROPIC_DEFAULT_HAIKU_MODEL: "anthropic/claude-haiku-4.5",
123
+ ANTHROPIC_MODEL: "opus"
124
+ },
125
+ "Ollama": {
126
+ ANTHROPIC_BASE_URL: "http://localhost:11434",
127
+ ANTHROPIC_DEFAULT_OPUS_MODEL: "gemma4:31b",
128
+ ANTHROPIC_DEFAULT_SONNET_MODEL: "gemma4:26b",
129
+ ANTHROPIC_DEFAULT_HAIKU_MODEL: "gemma4:e4b",
130
+ ANTHROPIC_MODEL: "opus"
131
+ },
132
+ "MiMo": {
133
+ ANTHROPIC_BASE_URL: "https://api.xiaomimimo.com/anthropic",
134
+ ANTHROPIC_DEFAULT_OPUS_MODEL: "mimo-v2.5-pro",
135
+ ANTHROPIC_DEFAULT_SONNET_MODEL: "mimo-v2.5-pro",
136
+ ANTHROPIC_DEFAULT_HAIKU_MODEL: "mimo-v2.5",
137
+ ANTHROPIC_MODEL: "opus"
138
+ },
139
+ "MiMoTokenPlan": {
140
+ ANTHROPIC_BASE_URL: "https://token-plan-cn.xiaomimimo.com/anthropic",
141
+ ANTHROPIC_DEFAULT_OPUS_MODEL: "mimo-v2.5-pro",
142
+ ANTHROPIC_DEFAULT_SONNET_MODEL: "mimo-v2.5-pro",
143
+ ANTHROPIC_DEFAULT_HAIKU_MODEL: "mimo-v2.5",
123
144
  ANTHROPIC_MODEL: "opus"
124
145
  }
125
146
  };
@@ -914,10 +935,10 @@ var renderLogoWithEnvPanel = (envName, env, defaultMode) => {
914
935
  const parsed = new URL(url);
915
936
  const protocol = parsed.protocol + "//";
916
937
  const host = parsed.host;
917
- const path7 = parsed.pathname + parsed.search;
938
+ const path8 = parsed.pathname + parsed.search;
918
939
  const hostStart = host.slice(0, 8);
919
940
  const hostEnd = host.slice(-4);
920
- const pathPart = path7.length > 10 ? path7.slice(0, 7) + "..." : path7;
941
+ const pathPart = path8.length > 10 ? path8.slice(0, 7) + "..." : path8;
921
942
  return `${protocol}${hostStart}...${hostEnd}${pathPart}`;
922
943
  } catch {
923
944
  return truncate(url, max);
@@ -1355,7 +1376,7 @@ var selectEnvWithKeys = (registries, current) => {
1355
1376
  };
1356
1377
 
1357
1378
  // src/permissions.ts
1358
- import fs5 from "fs";
1379
+ import fs6 from "fs";
1359
1380
  import chalk3 from "chalk";
1360
1381
  import Table2 from "cli-table3";
1361
1382
 
@@ -1408,9 +1429,347 @@ var ensureGlobalClaudeDir = () => {
1408
1429
 
1409
1430
  // src/launcher.ts
1410
1431
  import { spawn } from "child_process";
1411
- import * as fs4 from "fs";
1412
- import * as path4 from "path";
1432
+ import * as fs5 from "fs";
1433
+ import * as path5 from "path";
1413
1434
  import chalk2 from "chalk";
1435
+
1436
+ // src/sessionProvenance.ts
1437
+ import { execFileSync } from "child_process";
1438
+ import fs4 from "fs";
1439
+ import { createRequire } from "module";
1440
+ import os2 from "os";
1441
+ import path4 from "path";
1442
+ var DEFAULT_CONFIG_SOURCE = "ccem";
1443
+ var STATE_DB_FILE_NAME = "state.sqlite";
1444
+ var BIND_POLL_INTERVAL_MS = 500;
1445
+ var BIND_POLL_TIMEOUT_MS = 2e4;
1446
+ var SQLITE_EXPERIMENTAL_WARNING = "SQLite is an experimental feature";
1447
+ var require2 = createRequire(import.meta.url);
1448
+ var databaseSyncCtor = null;
1449
+ var databaseSyncResolved = false;
1450
+ var sqlite3CliAvailable = null;
1451
+ function startCliClaudeProvenanceTracking(options) {
1452
+ const envName = normalizeText(options.envName) ?? "unknown";
1453
+ const workingDir = normalizeText(options.workingDir);
1454
+ if (!workingDir) {
1455
+ return null;
1456
+ }
1457
+ const ccemSessionId = `cli-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
1458
+ try {
1459
+ registerLaunch({
1460
+ ccemSessionId,
1461
+ client: "claude",
1462
+ envName,
1463
+ configSource: DEFAULT_CONFIG_SOURCE,
1464
+ workingDir,
1465
+ permMode: normalizeText(options.permMode),
1466
+ launchMode: "cli_external",
1467
+ startedVia: "cli",
1468
+ sourceSessionId: normalizeText(options.resumeSessionId)
1469
+ });
1470
+ } catch (error) {
1471
+ reportTrackingError("register launch", error);
1472
+ return null;
1473
+ }
1474
+ let timer = null;
1475
+ const stop = () => {
1476
+ if (timer) {
1477
+ clearInterval(timer);
1478
+ timer = null;
1479
+ }
1480
+ };
1481
+ const resumeSessionId = normalizeText(options.resumeSessionId);
1482
+ if (resumeSessionId) {
1483
+ return { ccemSessionId, stop };
1484
+ }
1485
+ const startedAtMs = Date.now();
1486
+ timer = setInterval(() => {
1487
+ if (Date.now() - startedAtMs > BIND_POLL_TIMEOUT_MS) {
1488
+ stop();
1489
+ return;
1490
+ }
1491
+ try {
1492
+ const jsonlPath = discoverClaudeJsonlPath(workingDir, startedAtMs);
1493
+ if (!jsonlPath) {
1494
+ return;
1495
+ }
1496
+ const sourceSessionId = readClaudeSessionId(jsonlPath) ?? normalizeText(path4.parse(jsonlPath).name);
1497
+ if (!sourceSessionId) {
1498
+ return;
1499
+ }
1500
+ bindSourceSessionId("claude", ccemSessionId, sourceSessionId);
1501
+ stop();
1502
+ } catch (error) {
1503
+ reportTrackingError("bind source session id", error);
1504
+ stop();
1505
+ }
1506
+ }, BIND_POLL_INTERVAL_MS);
1507
+ return { ccemSessionId, stop };
1508
+ }
1509
+ function registerLaunch(options) {
1510
+ const now = (/* @__PURE__ */ new Date()).toISOString();
1511
+ const DatabaseSync = getDatabaseSyncCtor();
1512
+ if (DatabaseSync) {
1513
+ const db = openNodeSqliteDb(DatabaseSync);
1514
+ try {
1515
+ const statement = db.prepare(`
1516
+ INSERT INTO session_provenance (
1517
+ ccem_session_id,
1518
+ client,
1519
+ env_name,
1520
+ config_source,
1521
+ working_dir,
1522
+ perm_mode,
1523
+ launch_mode,
1524
+ started_via,
1525
+ source_session_id,
1526
+ created_at,
1527
+ updated_at
1528
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
1529
+ ON CONFLICT(ccem_session_id) DO UPDATE SET
1530
+ client = excluded.client,
1531
+ env_name = excluded.env_name,
1532
+ config_source = COALESCE(excluded.config_source, session_provenance.config_source),
1533
+ working_dir = excluded.working_dir,
1534
+ perm_mode = COALESCE(excluded.perm_mode, session_provenance.perm_mode),
1535
+ launch_mode = excluded.launch_mode,
1536
+ started_via = excluded.started_via,
1537
+ source_session_id = COALESCE(excluded.source_session_id, session_provenance.source_session_id),
1538
+ updated_at = excluded.updated_at
1539
+ `);
1540
+ statement.run(
1541
+ options.ccemSessionId,
1542
+ options.client,
1543
+ options.envName,
1544
+ normalizeText(options.configSource),
1545
+ options.workingDir,
1546
+ normalizeText(options.permMode),
1547
+ options.launchMode,
1548
+ options.startedVia,
1549
+ normalizeText(options.sourceSessionId),
1550
+ now,
1551
+ now
1552
+ );
1553
+ return;
1554
+ } finally {
1555
+ db.close();
1556
+ }
1557
+ }
1558
+ execSqlite3Cli(`
1559
+ INSERT INTO session_provenance (
1560
+ ccem_session_id,
1561
+ client,
1562
+ env_name,
1563
+ config_source,
1564
+ working_dir,
1565
+ perm_mode,
1566
+ launch_mode,
1567
+ started_via,
1568
+ source_session_id,
1569
+ created_at,
1570
+ updated_at
1571
+ ) VALUES (
1572
+ ${sqliteLiteral(options.ccemSessionId)},
1573
+ ${sqliteLiteral(options.client)},
1574
+ ${sqliteLiteral(options.envName)},
1575
+ ${sqliteLiteral(normalizeText(options.configSource))},
1576
+ ${sqliteLiteral(options.workingDir)},
1577
+ ${sqliteLiteral(normalizeText(options.permMode))},
1578
+ ${sqliteLiteral(options.launchMode)},
1579
+ ${sqliteLiteral(options.startedVia)},
1580
+ ${sqliteLiteral(normalizeText(options.sourceSessionId))},
1581
+ ${sqliteLiteral(now)},
1582
+ ${sqliteLiteral(now)}
1583
+ )
1584
+ ON CONFLICT(ccem_session_id) DO UPDATE SET
1585
+ client = excluded.client,
1586
+ env_name = excluded.env_name,
1587
+ config_source = COALESCE(excluded.config_source, session_provenance.config_source),
1588
+ working_dir = excluded.working_dir,
1589
+ perm_mode = COALESCE(excluded.perm_mode, session_provenance.perm_mode),
1590
+ launch_mode = excluded.launch_mode,
1591
+ started_via = excluded.started_via,
1592
+ source_session_id = COALESCE(excluded.source_session_id, session_provenance.source_session_id),
1593
+ updated_at = excluded.updated_at;
1594
+ `);
1595
+ }
1596
+ function bindSourceSessionId(client, ccemSessionId, sourceSessionId) {
1597
+ const updatedAt = (/* @__PURE__ */ new Date()).toISOString();
1598
+ const DatabaseSync = getDatabaseSyncCtor();
1599
+ if (DatabaseSync) {
1600
+ const db = openNodeSqliteDb(DatabaseSync);
1601
+ try {
1602
+ const statement = db.prepare(`
1603
+ UPDATE session_provenance
1604
+ SET source_session_id = ?, updated_at = ?
1605
+ WHERE ccem_session_id = ? AND client = ?
1606
+ `);
1607
+ statement.run(sourceSessionId, updatedAt, ccemSessionId, client);
1608
+ return;
1609
+ } finally {
1610
+ db.close();
1611
+ }
1612
+ }
1613
+ execSqlite3Cli(`
1614
+ UPDATE session_provenance
1615
+ SET source_session_id = ${sqliteLiteral(sourceSessionId)},
1616
+ updated_at = ${sqliteLiteral(updatedAt)}
1617
+ WHERE ccem_session_id = ${sqliteLiteral(ccemSessionId)}
1618
+ AND client = ${sqliteLiteral(client)};
1619
+ `);
1620
+ }
1621
+ function openNodeSqliteDb(DatabaseSync) {
1622
+ const dbPath = getStateDbPath();
1623
+ fs4.mkdirSync(path4.dirname(dbPath), { recursive: true });
1624
+ const db = new DatabaseSync(dbPath);
1625
+ db.exec(schemaSql());
1626
+ return db;
1627
+ }
1628
+ function getDatabaseSyncCtor() {
1629
+ if (databaseSyncResolved) {
1630
+ return databaseSyncCtor;
1631
+ }
1632
+ const originalEmitWarning = process.emitWarning;
1633
+ process.emitWarning = ((warning, ...args) => {
1634
+ const message = typeof warning === "string" ? warning : warning instanceof Error ? warning.message : String(warning);
1635
+ if (message.includes(SQLITE_EXPERIMENTAL_WARNING)) {
1636
+ return;
1637
+ }
1638
+ return originalEmitWarning(warning, ...args);
1639
+ });
1640
+ try {
1641
+ const loaded = require2("node:sqlite");
1642
+ databaseSyncCtor = loaded.DatabaseSync;
1643
+ } catch {
1644
+ databaseSyncCtor = null;
1645
+ } finally {
1646
+ process.emitWarning = originalEmitWarning;
1647
+ databaseSyncResolved = true;
1648
+ }
1649
+ return databaseSyncCtor;
1650
+ }
1651
+ function execSqlite3Cli(statementSql) {
1652
+ if (sqlite3CliAvailable == null) {
1653
+ try {
1654
+ execFileSync("sqlite3", ["-version"], { stdio: "ignore" });
1655
+ sqlite3CliAvailable = true;
1656
+ } catch {
1657
+ sqlite3CliAvailable = false;
1658
+ }
1659
+ }
1660
+ if (!sqlite3CliAvailable) {
1661
+ throw new Error("sqlite3 CLI is unavailable and node:sqlite is not supported by this Node runtime");
1662
+ }
1663
+ const dbPath = getStateDbPath();
1664
+ fs4.mkdirSync(path4.dirname(dbPath), { recursive: true });
1665
+ execFileSync("sqlite3", [dbPath], {
1666
+ input: `${schemaSql()}
1667
+ ${statementSql}
1668
+ `,
1669
+ stdio: ["pipe", "ignore", "pipe"]
1670
+ });
1671
+ }
1672
+ function getStateDbPath() {
1673
+ const override = normalizeText(process.env.CCEM_STATE_DB_PATH);
1674
+ if (override) {
1675
+ return override;
1676
+ }
1677
+ return path4.join(os2.homedir(), ".ccem", STATE_DB_FILE_NAME);
1678
+ }
1679
+ function schemaSql() {
1680
+ return `
1681
+ PRAGMA journal_mode = WAL;
1682
+ PRAGMA synchronous = NORMAL;
1683
+ CREATE TABLE IF NOT EXISTS session_provenance (
1684
+ ccem_session_id TEXT PRIMARY KEY,
1685
+ client TEXT NOT NULL,
1686
+ env_name TEXT NOT NULL,
1687
+ config_source TEXT,
1688
+ working_dir TEXT NOT NULL,
1689
+ perm_mode TEXT,
1690
+ launch_mode TEXT NOT NULL,
1691
+ started_via TEXT NOT NULL,
1692
+ source_session_id TEXT,
1693
+ created_at TEXT NOT NULL,
1694
+ updated_at TEXT NOT NULL
1695
+ );
1696
+ CREATE INDEX IF NOT EXISTS idx_session_provenance_client_source
1697
+ ON session_provenance (client, source_session_id);
1698
+ CREATE INDEX IF NOT EXISTS idx_session_provenance_client_updated
1699
+ ON session_provenance (client, updated_at DESC);
1700
+ `;
1701
+ }
1702
+ function discoverClaudeJsonlPath(projectDir, startedAtMs) {
1703
+ const projectsDir = path4.join(os2.homedir(), ".claude", "projects");
1704
+ const earliestModifiedAt = startedAtMs - 15e3;
1705
+ for (const key of projectDirKeys(projectDir)) {
1706
+ const baseDir = path4.join(projectsDir, key);
1707
+ if (!fs4.existsSync(baseDir)) {
1708
+ continue;
1709
+ }
1710
+ const candidates = fs4.readdirSync(baseDir, { withFileTypes: true }).filter((entry) => entry.isFile() && entry.name.endsWith(".jsonl")).map((entry) => {
1711
+ const fullPath = path4.join(baseDir, entry.name);
1712
+ return { path: fullPath, modifiedAt: fs4.statSync(fullPath).mtimeMs };
1713
+ }).filter((candidate) => candidate.modifiedAt >= earliestModifiedAt).sort((left, right) => right.modifiedAt - left.modifiedAt);
1714
+ if (candidates.length > 0) {
1715
+ return candidates[0].path;
1716
+ }
1717
+ }
1718
+ return null;
1719
+ }
1720
+ function projectDirKeys(projectDir) {
1721
+ const candidates = [projectDir];
1722
+ try {
1723
+ candidates.push(fs4.realpathSync(projectDir));
1724
+ } catch {
1725
+ }
1726
+ if (process.platform === "darwin") {
1727
+ if (projectDir.startsWith("/private/")) {
1728
+ candidates.push(projectDir.slice("/private".length));
1729
+ } else if (projectDir.startsWith("/tmp")) {
1730
+ candidates.push(path4.posix.join("/private", projectDir));
1731
+ } else if (projectDir.startsWith("/var/")) {
1732
+ candidates.push(path4.posix.join("/private", projectDir));
1733
+ }
1734
+ }
1735
+ return [...new Set(candidates.map(projectDirKey))];
1736
+ }
1737
+ function projectDirKey(projectDir) {
1738
+ return projectDir.replace(/[\/\\: ]/g, "-");
1739
+ }
1740
+ function readClaudeSessionId(jsonlPath) {
1741
+ const lines = fs4.readFileSync(jsonlPath, "utf8").split("\n");
1742
+ for (const line of lines) {
1743
+ if (!line.trim()) {
1744
+ continue;
1745
+ }
1746
+ try {
1747
+ const value = JSON.parse(line);
1748
+ const sessionId = normalizeText(value.sessionId);
1749
+ if (sessionId) {
1750
+ return sessionId;
1751
+ }
1752
+ } catch {
1753
+ }
1754
+ }
1755
+ return null;
1756
+ }
1757
+ function normalizeText(value) {
1758
+ const trimmed = value?.trim();
1759
+ return trimmed ? trimmed : void 0;
1760
+ }
1761
+ function sqliteLiteral(value) {
1762
+ if (!value) {
1763
+ return "NULL";
1764
+ }
1765
+ return `'${value.replace(/'/g, "''")}'`;
1766
+ }
1767
+ function reportTrackingError(stage, error) {
1768
+ const message = error instanceof Error ? error.message : String(error);
1769
+ console.error(`[ccem] Failed to ${stage}: ${message}`);
1770
+ }
1771
+
1772
+ // src/launcher.ts
1414
1773
  var MANAGED_CLAUDE_ENV_KEYS = [
1415
1774
  "ANTHROPIC_BASE_URL",
1416
1775
  "ANTHROPIC_AUTH_TOKEN",
@@ -1448,14 +1807,14 @@ function buildPermArgs(modeName) {
1448
1807
  return args;
1449
1808
  }
1450
1809
  function ensureSessionsDir() {
1451
- const dir = path4.join(ensureCcemDir(), "sessions");
1452
- if (!fs4.existsSync(dir)) {
1453
- fs4.mkdirSync(dir, { recursive: true });
1810
+ const dir = path5.join(ensureCcemDir(), "sessions");
1811
+ if (!fs5.existsSync(dir)) {
1812
+ fs5.mkdirSync(dir, { recursive: true });
1454
1813
  }
1455
1814
  return dir;
1456
1815
  }
1457
1816
  async function launchClaude(options) {
1458
- const { envConfig, permMode, workingDir, sessionId, resumeSessionId, silent } = options;
1817
+ const { envName, envConfig, permMode, workingDir, sessionId, resumeSessionId, silent } = options;
1459
1818
  const env = { ...process.env };
1460
1819
  for (const key of MANAGED_CLAUDE_ENV_KEYS) {
1461
1820
  delete env[key];
@@ -1482,22 +1841,36 @@ async function launchClaude(options) {
1482
1841
  if (workingDir) {
1483
1842
  process.chdir(workingDir);
1484
1843
  }
1844
+ const effectiveWorkingDir = process.cwd();
1485
1845
  if (!silent && !permMode) {
1486
1846
  console.log(renderStarting());
1487
1847
  }
1488
1848
  const sessionsDir = ensureSessionsDir();
1489
1849
  return new Promise((resolve2) => {
1850
+ let provenanceTracking = null;
1490
1851
  const child = spawn("claude", args, {
1491
1852
  stdio: "inherit",
1492
1853
  shell: false,
1493
1854
  // 直接执行二进制,避免 shell 注入风险
1494
1855
  env
1495
1856
  });
1857
+ child.once("spawn", () => {
1858
+ if (sessionId) {
1859
+ return;
1860
+ }
1861
+ provenanceTracking = startCliClaudeProvenanceTracking({
1862
+ envName: envName ?? "unknown",
1863
+ workingDir: effectiveWorkingDir,
1864
+ permMode,
1865
+ resumeSessionId
1866
+ });
1867
+ });
1496
1868
  child.on("exit", (code) => {
1869
+ provenanceTracking?.stop();
1497
1870
  if (sessionId) {
1498
1871
  try {
1499
- fs4.writeFileSync(
1500
- path4.join(sessionsDir, `${sessionId}.exit`),
1872
+ fs5.writeFileSync(
1873
+ path5.join(sessionsDir, `${sessionId}.exit`),
1501
1874
  String(code ?? 0)
1502
1875
  );
1503
1876
  } catch {
@@ -1506,7 +1879,24 @@ async function launchClaude(options) {
1506
1879
  process.exit(code ?? 0);
1507
1880
  });
1508
1881
  child.on("error", (err) => {
1509
- console.error(chalk2.red(`\u542F\u52A8 Claude Code \u5931\u8D25: ${err.message}`));
1882
+ provenanceTracking?.stop();
1883
+ if (err.code === "ENOENT") {
1884
+ console.error("");
1885
+ console.error(chalk2.red.bold("\u2718 \u672A\u627E\u5230 Claude Code"));
1886
+ console.error("");
1887
+ console.error(chalk2.white(" CCEM \u9700\u8981 Claude Code CLI \u624D\u80FD\u542F\u52A8\u4F1A\u8BDD\uFF0C\u4F46\u5728\u7CFB\u7EDF\u4E2D\u672A\u68C0\u6D4B\u5230 ") + chalk2.cyan("claude") + chalk2.white(" \u547D\u4EE4\u3002"));
1888
+ console.error("");
1889
+ console.error(chalk2.white(" \u8BF7\u5148\u5B89\u88C5 Claude Code:"));
1890
+ console.error(chalk2.cyan(" npm install -g @anthropic-ai/claude-code"));
1891
+ console.error("");
1892
+ console.error(chalk2.gray(" \u5982\u679C\u5DF2\u5B89\u88C5\u4F46\u4ECD\u62A5\u9519\uFF0C\u8BF7\u68C0\u67E5:"));
1893
+ console.error(chalk2.gray(" 1. \u8FD0\u884C claude --version \u786E\u8BA4\u5B89\u88C5\u6210\u529F"));
1894
+ console.error(chalk2.gray(" 2. \u786E\u4FDD npm \u5168\u5C40\u76EE\u5F55\u5728\u7CFB\u7EDF PATH \u4E2D\uFF08npm config get prefix\uFF09"));
1895
+ console.error(chalk2.gray(" 3. \u5B89\u88C5\u540E\u8BF7\u91CD\u542F\u7EC8\u7AEF"));
1896
+ console.error("");
1897
+ } else {
1898
+ console.error(chalk2.red(`\u542F\u52A8 Claude Code \u5931\u8D25: ${err.message}`));
1899
+ }
1510
1900
  process.exit(1);
1511
1901
  });
1512
1902
  });
@@ -1514,14 +1904,14 @@ async function launchClaude(options) {
1514
1904
 
1515
1905
  // src/permissions.ts
1516
1906
  var readSettings = (settingsPath) => {
1517
- if (fs5.existsSync(settingsPath)) {
1907
+ if (fs6.existsSync(settingsPath)) {
1518
1908
  try {
1519
- const content = fs5.readFileSync(settingsPath, "utf-8");
1909
+ const content = fs6.readFileSync(settingsPath, "utf-8");
1520
1910
  return JSON.parse(content);
1521
1911
  } catch {
1522
1912
  console.warn(chalk3.yellow(`\u8B66\u544A: \u65E0\u6CD5\u89E3\u6790 ${settingsPath}\uFF0C\u5C06\u521B\u5EFA\u5907\u4EFD`));
1523
1913
  const backupPath = settingsPath + ".error." + Date.now();
1524
- fs5.copyFileSync(settingsPath, backupPath);
1914
+ fs6.copyFileSync(settingsPath, backupPath);
1525
1915
  console.log(chalk3.gray(`\u5907\u4EFD\u5DF2\u4FDD\u5B58\u5230: ${backupPath}`));
1526
1916
  return {};
1527
1917
  }
@@ -1530,7 +1920,7 @@ var readSettings = (settingsPath) => {
1530
1920
  };
1531
1921
  var writeSettings = (settingsPath, config3) => {
1532
1922
  ensureClaudeDir();
1533
- fs5.writeFileSync(settingsPath, JSON.stringify(config3, null, 2) + "\n");
1923
+ fs6.writeFileSync(settingsPath, JSON.stringify(config3, null, 2) + "\n");
1534
1924
  };
1535
1925
  var mergePermissions = (existing, preset) => {
1536
1926
  const existingAllow = existing.permissions?.allow || [];
@@ -1562,14 +1952,14 @@ var applyPermissionMode = (modeName) => {
1562
1952
  };
1563
1953
  var resetPermissions = () => {
1564
1954
  const settingsPath = getSettingsPath(true);
1565
- if (!fs5.existsSync(settingsPath)) {
1955
+ if (!fs6.existsSync(settingsPath)) {
1566
1956
  console.log(chalk3.yellow("\u6CA1\u6709\u81EA\u5B9A\u4E49\u6743\u9650\u914D\u7F6E\u9700\u8981\u91CD\u7F6E"));
1567
1957
  return;
1568
1958
  }
1569
1959
  const config3 = readSettings(settingsPath);
1570
1960
  delete config3.permissions;
1571
1961
  if (Object.keys(config3).length === 0) {
1572
- fs5.unlinkSync(settingsPath);
1962
+ fs6.unlinkSync(settingsPath);
1573
1963
  console.log(chalk3.green("\u5DF2\u5220\u9664\u914D\u7F6E\u6587\u4EF6\uFF08\u6587\u4EF6\u4E3A\u7A7A\uFF09"));
1574
1964
  } else {
1575
1965
  writeSettings(settingsPath, config3);
@@ -1579,7 +1969,7 @@ var resetPermissions = () => {
1579
1969
  };
1580
1970
  var showCurrentMode = () => {
1581
1971
  const settingsPath = getSettingsPath(true);
1582
- if (!fs5.existsSync(settingsPath)) {
1972
+ if (!fs6.existsSync(settingsPath)) {
1583
1973
  console.log(chalk3.yellow("\u672A\u914D\u7F6E\u81EA\u5B9A\u4E49\u6743\u9650"));
1584
1974
  console.log(chalk3.gray(`\u6587\u4EF6\u4E0D\u5B58\u5728: ${settingsPath}`));
1585
1975
  return;
@@ -1629,24 +2019,24 @@ var listAvailableModes = () => {
1629
2019
  console.log(chalk3.gray("\n\u4E34\u65F6\u6A21\u5F0F: ccem <mode>"));
1630
2020
  console.log(chalk3.gray("\u6C38\u4E45\u6A21\u5F0F: ccem setup perms --<mode>"));
1631
2021
  };
1632
- var runWithTempPermissions = async (modeName, envConfig) => {
2022
+ var runWithTempPermissions = async (modeName, envConfig, envName) => {
1633
2023
  const preset = PERMISSION_PRESETS[modeName];
1634
2024
  if (!preset) {
1635
2025
  console.error(chalk3.red(`\u672A\u77E5\u7684\u6743\u9650\u6A21\u5F0F: ${modeName}`));
1636
2026
  console.log(chalk3.yellow("\u53EF\u7528\u6A21\u5F0F: " + getPermissionModeNames().join(", ")));
1637
2027
  process.exit(1);
1638
2028
  }
1639
- await launchClaude({ envConfig, permMode: modeName });
2029
+ await launchClaude({ envConfig, envName, permMode: modeName });
1640
2030
  };
1641
2031
 
1642
2032
  // src/setup.ts
1643
- import fs6 from "fs";
2033
+ import fs7 from "fs";
1644
2034
  import chalk4 from "chalk";
1645
2035
  import { spawn as spawn2 } from "child_process";
1646
2036
  var readJsonFile = (filePath) => {
1647
- if (fs6.existsSync(filePath)) {
2037
+ if (fs7.existsSync(filePath)) {
1648
2038
  try {
1649
- const content = fs6.readFileSync(filePath, "utf-8");
2039
+ const content = fs7.readFileSync(filePath, "utf-8");
1650
2040
  return JSON.parse(content);
1651
2041
  } catch {
1652
2042
  console.warn(chalk4.yellow(`\u8B66\u544A: \u65E0\u6CD5\u89E3\u6790 ${filePath}`));
@@ -1656,7 +2046,7 @@ var readJsonFile = (filePath) => {
1656
2046
  return {};
1657
2047
  };
1658
2048
  var writeJsonFile = (filePath, data) => {
1659
- fs6.writeFileSync(filePath, JSON.stringify(data, null, 2) + "\n");
2049
+ fs7.writeFileSync(filePath, JSON.stringify(data, null, 2) + "\n");
1660
2050
  };
1661
2051
  var setupOnboarding = () => {
1662
2052
  const configPath = getGlobalClaudeConfigPath();
@@ -1783,8 +2173,8 @@ var runSetupInit = async (options = {}) => {
1783
2173
 
1784
2174
  // src/skills.ts
1785
2175
  import { execSync } from "child_process";
1786
- import * as fs7 from "fs";
1787
- import * as path5 from "path";
2176
+ import * as fs8 from "fs";
2177
+ import * as path6 from "path";
1788
2178
  import chalk5 from "chalk";
1789
2179
  var SKILL_GROUPS = {
1790
2180
  official: { label: "\u5B98\u65B9", icon: "\u{1F3E2}" },
@@ -1953,12 +2343,12 @@ function parseGitHubUrl(url) {
1953
2343
  };
1954
2344
  }
1955
2345
  function getSkillsDir() {
1956
- return path5.join(process.cwd(), ".claude", "skills");
2346
+ return path6.join(process.cwd(), ".claude", "skills");
1957
2347
  }
1958
2348
  function ensureSkillsDir() {
1959
2349
  const skillsDir = getSkillsDir();
1960
- if (!fs7.existsSync(skillsDir)) {
1961
- fs7.mkdirSync(skillsDir, { recursive: true });
2350
+ if (!fs8.existsSync(skillsDir)) {
2351
+ fs8.mkdirSync(skillsDir, { recursive: true });
1962
2352
  } else {
1963
2353
  cleanupTempDirs(skillsDir);
1964
2354
  }
@@ -1966,11 +2356,11 @@ function ensureSkillsDir() {
1966
2356
  }
1967
2357
  function cleanupTempDirs(skillsDir) {
1968
2358
  try {
1969
- const entries = fs7.readdirSync(skillsDir, { withFileTypes: true });
2359
+ const entries = fs8.readdirSync(skillsDir, { withFileTypes: true });
1970
2360
  for (const entry of entries) {
1971
2361
  if (entry.isDirectory() && entry.name.startsWith(".tmp-")) {
1972
- const tmpPath = path5.join(skillsDir, entry.name);
1973
- fs7.rmSync(tmpPath, { recursive: true });
2362
+ const tmpPath = path6.join(skillsDir, entry.name);
2363
+ fs8.rmSync(tmpPath, { recursive: true });
1974
2364
  }
1975
2365
  }
1976
2366
  } catch {
@@ -1978,24 +2368,24 @@ function cleanupTempDirs(skillsDir) {
1978
2368
  }
1979
2369
  function downloadSkillWithGit(owner, repo, branch, repoPath, targetName) {
1980
2370
  const skillsDir = ensureSkillsDir();
1981
- const targetDir = path5.join(skillsDir, targetName);
1982
- if (fs7.existsSync(targetDir)) {
2371
+ const targetDir = path6.join(skillsDir, targetName);
2372
+ if (fs8.existsSync(targetDir)) {
1983
2373
  console.log(chalk5.yellow(`Skill "${targetName}" already exists. Updating...`));
1984
- fs7.rmSync(targetDir, { recursive: true });
2374
+ fs8.rmSync(targetDir, { recursive: true });
1985
2375
  }
1986
2376
  const repoUrl = `https://github.com/${owner}/${repo}.git`;
1987
- const tempDir = path5.join(skillsDir, `.tmp-${Date.now()}`);
2377
+ const tempDir = path6.join(skillsDir, `.tmp-${Date.now()}`);
1988
2378
  try {
1989
- fs7.mkdirSync(tempDir, { recursive: true });
2379
+ fs8.mkdirSync(tempDir, { recursive: true });
1990
2380
  execSync(`git init`, { cwd: tempDir, stdio: "pipe" });
1991
2381
  execSync(`git remote add origin ${repoUrl}`, { cwd: tempDir, stdio: "pipe" });
1992
2382
  execSync(`git config core.sparseCheckout true`, { cwd: tempDir, stdio: "pipe" });
1993
- const sparseFile = path5.join(tempDir, ".git", "info", "sparse-checkout");
1994
- fs7.writeFileSync(sparseFile, repoPath ? `${repoPath}/
2383
+ const sparseFile = path6.join(tempDir, ".git", "info", "sparse-checkout");
2384
+ fs8.writeFileSync(sparseFile, repoPath ? `${repoPath}/
1995
2385
  ` : "*\n");
1996
2386
  execSync(`git pull --depth=1 origin ${branch}`, { cwd: tempDir, stdio: "pipe" });
1997
- const sourceDir = repoPath ? path5.join(tempDir, repoPath) : tempDir;
1998
- if (!fs7.existsSync(sourceDir)) {
2387
+ const sourceDir = repoPath ? path6.join(tempDir, repoPath) : tempDir;
2388
+ if (!fs8.existsSync(sourceDir)) {
1999
2389
  throw new Error(`Path "${repoPath}" not found in repository`);
2000
2390
  }
2001
2391
  copyDir(sourceDir, targetDir);
@@ -2006,22 +2396,22 @@ function downloadSkillWithGit(owner, repo, branch, repoPath, targetName) {
2006
2396
  console.error(chalk5.red(`Failed to download skill: ${errMsg}`));
2007
2397
  return false;
2008
2398
  } finally {
2009
- if (fs7.existsSync(tempDir)) {
2010
- fs7.rmSync(tempDir, { recursive: true });
2399
+ if (fs8.existsSync(tempDir)) {
2400
+ fs8.rmSync(tempDir, { recursive: true });
2011
2401
  }
2012
2402
  }
2013
2403
  }
2014
2404
  function copyDir(src, dest) {
2015
- fs7.mkdirSync(dest, { recursive: true });
2016
- const entries = fs7.readdirSync(src, { withFileTypes: true });
2405
+ fs8.mkdirSync(dest, { recursive: true });
2406
+ const entries = fs8.readdirSync(src, { withFileTypes: true });
2017
2407
  for (const entry of entries) {
2018
2408
  if (entry.name === ".git") continue;
2019
- const srcPath = path5.join(src, entry.name);
2020
- const destPath = path5.join(dest, entry.name);
2409
+ const srcPath = path6.join(src, entry.name);
2410
+ const destPath = path6.join(dest, entry.name);
2021
2411
  if (entry.isDirectory()) {
2022
2412
  copyDir(srcPath, destPath);
2023
2413
  } else {
2024
- fs7.copyFileSync(srcPath, destPath);
2414
+ fs8.copyFileSync(srcPath, destPath);
2025
2415
  }
2026
2416
  }
2027
2417
  }
@@ -2067,7 +2457,7 @@ function addSkillFromGitHub(urlOrPreset) {
2067
2457
  }
2068
2458
  let skillName;
2069
2459
  if (parsed.path) {
2070
- skillName = path5.basename(parsed.path);
2460
+ skillName = path6.basename(parsed.path);
2071
2461
  } else {
2072
2462
  skillName = parsed.repo;
2073
2463
  }
@@ -2081,23 +2471,23 @@ function addSkillFromGitHub(urlOrPreset) {
2081
2471
  }
2082
2472
  function listInstalledSkills() {
2083
2473
  const skillsDir = getSkillsDir();
2084
- if (!fs7.existsSync(skillsDir)) {
2474
+ if (!fs8.existsSync(skillsDir)) {
2085
2475
  return [];
2086
2476
  }
2087
- const entries = fs7.readdirSync(skillsDir, { withFileTypes: true });
2477
+ const entries = fs8.readdirSync(skillsDir, { withFileTypes: true });
2088
2478
  return entries.filter((entry) => entry.isDirectory() && !entry.name.startsWith(".")).map((entry) => ({
2089
2479
  name: entry.name,
2090
- path: path5.join(skillsDir, entry.name)
2480
+ path: path6.join(skillsDir, entry.name)
2091
2481
  }));
2092
2482
  }
2093
2483
  function removeSkill(name) {
2094
2484
  const skillsDir = getSkillsDir();
2095
- const targetDir = path5.join(skillsDir, name);
2096
- if (!fs7.existsSync(targetDir)) {
2485
+ const targetDir = path6.join(skillsDir, name);
2486
+ if (!fs8.existsSync(targetDir)) {
2097
2487
  console.error(chalk5.red(`Skill "${name}" not found`));
2098
2488
  return false;
2099
2489
  }
2100
- fs7.rmSync(targetDir, { recursive: true });
2490
+ fs8.rmSync(targetDir, { recursive: true });
2101
2491
  console.log(chalk5.green(`Removed skill "${name}"`));
2102
2492
  return true;
2103
2493
  }
@@ -2511,9 +2901,9 @@ Replace \\\`TARGET_ID\\\` or \\\`TARGET_NAME\\\` with the user's selection.
2511
2901
 
2512
2902
  // src/index.ts
2513
2903
  var __filename2 = fileURLToPath2(import.meta.url);
2514
- var __dirname2 = path6.dirname(__filename2);
2515
- var pkgPath = path6.resolve(__dirname2, "..", "package.json");
2516
- var pkg = JSON.parse(fs8.readFileSync(pkgPath, "utf-8"));
2904
+ var __dirname2 = path7.dirname(__filename2);
2905
+ var pkgPath = path7.resolve(__dirname2, "..", "package.json");
2906
+ var pkg = JSON.parse(fs9.readFileSync(pkgPath, "utf-8"));
2517
2907
  var program = new Command();
2518
2908
  var DEFAULT_OFFICIAL_ENV = {
2519
2909
  ANTHROPIC_BASE_URL: "https://api.anthropic.com",
@@ -2577,11 +2967,11 @@ var recoverRegistriesFromLegacy = (registries) => {
2577
2967
  return registries;
2578
2968
  }
2579
2969
  const legacyConfigPath = getLegacyConfigPath();
2580
- if (!fs8.existsSync(legacyConfigPath)) {
2970
+ if (!fs9.existsSync(legacyConfigPath)) {
2581
2971
  return registries;
2582
2972
  }
2583
2973
  try {
2584
- const legacyRaw = JSON.parse(fs8.readFileSync(legacyConfigPath, "utf-8"));
2974
+ const legacyRaw = JSON.parse(fs9.readFileSync(legacyConfigPath, "utf-8"));
2585
2975
  const legacyRegistries = legacyRaw.registries ?? {};
2586
2976
  let changed = false;
2587
2977
  const recovered = { ...registries };
@@ -2727,7 +3117,7 @@ PERMISSION_MODES.forEach((mode) => {
2727
3117
  const registries = getRegistries();
2728
3118
  const current = config2.get("current");
2729
3119
  const envConfig = registries[current];
2730
- await runWithTempPermissions(mode, envConfig);
3120
+ await runWithTempPermissions(mode, envConfig, current);
2731
3121
  });
2732
3122
  });
2733
3123
  var showCurrentEnv = (usageStats2, usageLoading2) => {
@@ -2775,14 +3165,14 @@ var switchEnvironment = async (name) => {
2775
3165
  exportCmds.forEach((cmd) => console.log(cmd));
2776
3166
  }
2777
3167
  };
2778
- var getSessionsFilePath = () => path6.join(getCcemConfigDir(), "sessions.json");
2779
- var getRuntimeStateFilePath = () => path6.join(getCcemConfigDir(), "runtime-state.json");
3168
+ var getSessionsFilePath = () => path7.join(getCcemConfigDir(), "sessions.json");
3169
+ var getRuntimeStateFilePath = () => path7.join(getCcemConfigDir(), "runtime-state.json");
2780
3170
  var parseJsonFile = (filePath) => {
2781
- if (!fs8.existsSync(filePath)) {
3171
+ if (!fs9.existsSync(filePath)) {
2782
3172
  return null;
2783
3173
  }
2784
3174
  try {
2785
- return JSON.parse(fs8.readFileSync(filePath, "utf-8"));
3175
+ return JSON.parse(fs9.readFileSync(filePath, "utf-8"));
2786
3176
  } catch {
2787
3177
  return null;
2788
3178
  }
@@ -3104,12 +3494,12 @@ setupCmd.command("migrate").description("\u8FC1\u79FB\u65E7\u7248\u914D\u7F6E\u5
3104
3494
  const newConfigPath = getCcemConfigPath();
3105
3495
  const legacyConfigPath = getLegacyConfigPath();
3106
3496
  console.log(chalk7.cyan("\n\u{1F504} \u914D\u7F6E\u8FC1\u79FB\n"));
3107
- if (!fs8.existsSync(legacyConfigPath)) {
3497
+ if (!fs9.existsSync(legacyConfigPath)) {
3108
3498
  console.log(chalk7.yellow("\u672A\u627E\u5230\u65E7\u7248\u914D\u7F6E\u6587\u4EF6"));
3109
3499
  console.log(chalk7.gray(` \u65E7\u8DEF\u5F84: ${legacyConfigPath}`));
3110
3500
  return;
3111
3501
  }
3112
- if (fs8.existsSync(newConfigPath) && !options.force) {
3502
+ if (fs9.existsSync(newConfigPath) && !options.force) {
3113
3503
  console.log(chalk7.green("\u2713 \u914D\u7F6E\u5DF2\u5728\u65B0\u8DEF\u5F84"));
3114
3504
  console.log(chalk7.gray(` \u8DEF\u5F84: ${newConfigPath}`));
3115
3505
  console.log(chalk7.gray("\n\u4F7F\u7528 --force \u5F3A\u5236\u91CD\u65B0\u8FC1\u79FB"));
@@ -3117,15 +3507,15 @@ setupCmd.command("migrate").description("\u8FC1\u79FB\u65E7\u7248\u914D\u7F6E\u5
3117
3507
  }
3118
3508
  try {
3119
3509
  ensureCcemDir();
3120
- fs8.copyFileSync(legacyConfigPath, newConfigPath);
3510
+ fs9.copyFileSync(legacyConfigPath, newConfigPath);
3121
3511
  console.log(chalk7.green("\u2713 \u914D\u7F6E\u5DF2\u8FC1\u79FB"));
3122
3512
  console.log(chalk7.gray(` \u4ECE: ${legacyConfigPath}`));
3123
3513
  console.log(chalk7.gray(` \u5230: ${newConfigPath}`));
3124
3514
  if (options.clean) {
3125
- fs8.unlinkSync(legacyConfigPath);
3126
- const legacyDir = path6.dirname(legacyConfigPath);
3515
+ fs9.unlinkSync(legacyConfigPath);
3516
+ const legacyDir = path7.dirname(legacyConfigPath);
3127
3517
  try {
3128
- fs8.rmdirSync(legacyDir);
3518
+ fs9.rmdirSync(legacyDir);
3129
3519
  } catch {
3130
3520
  }
3131
3521
  console.log(chalk7.green("\u2713 \u5DF2\u5220\u9664\u65E7\u914D\u7F6E\u6587\u4EF6"));
@@ -3136,13 +3526,13 @@ setupCmd.command("migrate").description("\u8FC1\u79FB\u65E7\u7248\u914D\u7F6E\u5
3136
3526
  });
3137
3527
  setupCmd.command("cron").description("\u5B89\u88C5 ccem-cron skill \u5230 Claude Code\uFF08~/.claude/skills/\uFF09").option("--force", "\u5F3A\u5236\u8986\u76D6\u5DF2\u6709\u6587\u4EF6").action(async function() {
3138
3528
  const options = this.opts();
3139
- const skillDir = path6.join(process.env.HOME || "~", ".claude", "skills");
3140
- const targetPath = path6.join(skillDir, "ccem-cron.md");
3141
- if (!fs8.existsSync(skillDir)) {
3142
- fs8.mkdirSync(skillDir, { recursive: true });
3529
+ const skillDir = path7.join(process.env.HOME || "~", ".claude", "skills");
3530
+ const targetPath = path7.join(skillDir, "ccem-cron.md");
3531
+ if (!fs9.existsSync(skillDir)) {
3532
+ fs9.mkdirSync(skillDir, { recursive: true });
3143
3533
  console.log(chalk7.gray(`\u521B\u5EFA\u76EE\u5F55: ${skillDir}`));
3144
3534
  }
3145
- if (fs8.existsSync(targetPath) && !options.force) {
3535
+ if (fs9.existsSync(targetPath) && !options.force) {
3146
3536
  const { overwrite } = await inquirer.prompt([
3147
3537
  {
3148
3538
  type: "confirm",
@@ -3156,7 +3546,7 @@ setupCmd.command("cron").description("\u5B89\u88C5 ccem-cron skill \u5230 Claude
3156
3546
  return;
3157
3547
  }
3158
3548
  }
3159
- fs8.writeFileSync(targetPath, CCEM_CRON_SKILL_CONTENT, "utf-8");
3549
+ fs9.writeFileSync(targetPath, CCEM_CRON_SKILL_CONTENT, "utf-8");
3160
3550
  console.log(chalk7.green(`\u2713 \u5DF2\u5B89\u88C5 ccem-cron skill`));
3161
3551
  console.log(chalk7.gray(` \u8DEF\u5F84: ${targetPath}`));
3162
3552
  console.log(chalk7.cyan(`
@@ -3238,6 +3628,7 @@ program.command("launch").description(false).option("--env <name>", "\u73AF\u588
3238
3628
  const proxyBaseUrl = opts.proxyBaseUrl || opts.anthropicBaseUrl;
3239
3629
  const launchEnvConfig = proxyBaseUrl ? { ...envConfig, ANTHROPIC_BASE_URL: proxyBaseUrl } : envConfig;
3240
3630
  await launchClaude({
3631
+ envName,
3241
3632
  envConfig: launchEnvConfig,
3242
3633
  permMode: opts.perm,
3243
3634
  workingDir: opts.workingDir,
@@ -3296,7 +3687,11 @@ program.action(async (options) => {
3296
3687
  msg.error("No environment configuration found.");
3297
3688
  process.exit(1);
3298
3689
  }
3299
- await launchClaude({ envConfig, permMode: defaultMode || void 0 });
3690
+ await launchClaude({
3691
+ envName: current,
3692
+ envConfig,
3693
+ permMode: defaultMode || void 0
3694
+ });
3300
3695
  return;
3301
3696
  } else if (action === "usage") {
3302
3697
  console.clear();
@@ -3421,7 +3816,7 @@ Editing environment '${result.name}'`));
3421
3816
  }
3422
3817
  ]);
3423
3818
  if (permMode !== "back") {
3424
- await runWithTempPermissions(permMode, envConfig);
3819
+ await runWithTempPermissions(permMode, envConfig, current);
3425
3820
  return;
3426
3821
  }
3427
3822
  } else if (action === "setDefault") {
package/model-prices.json CHANGED
@@ -2159,6 +2159,18 @@
2159
2159
  "input_cost_per_token": 2.8e-7,
2160
2160
  "output_cost_per_token": 4e-7
2161
2161
  },
2162
+ "deepseek/deepseek-v4-pro": {
2163
+ "input_cost_per_token": 1.66e-6,
2164
+ "output_cost_per_token": 3.31e-6,
2165
+ "cache_read_input_token_cost": 3.3e-7,
2166
+ "cache_creation_input_token_cost": 0
2167
+ },
2168
+ "deepseek/deepseek-v4-flash": {
2169
+ "input_cost_per_token": 1.38e-7,
2170
+ "output_cost_per_token": 2.76e-7,
2171
+ "cache_read_input_token_cost": 2.8e-8,
2172
+ "cache_creation_input_token_cost": 0
2173
+ },
2162
2174
  "deepseek.v3-v1:0": {
2163
2175
  "input_cost_per_token": 5.8e-7,
2164
2176
  "output_cost_per_token": 0.00000168
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ccem",
3
- "version": "2.0.0-beta.8",
3
+ "version": "2.0.0",
4
4
  "type": "module",
5
5
  "description": "Claude Code Environment Manager",
6
6
  "author": {
@@ -36,7 +36,7 @@
36
36
  "tsup": "^8.0.2",
37
37
  "typescript": "^5.3.3",
38
38
  "vitest": "^4.0.18",
39
- "@ccem/core": "2.0.0-beta.3"
39
+ "@ccem/core": "2.0.0"
40
40
  },
41
41
  "scripts": {
42
42
  "generate-logo": "bash scripts/generate-logo.sh",