cortex-sync 0.4.0 → 0.4.2

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 (3) hide show
  1. package/README.md +43 -0
  2. package/dist/cli.js +223 -48
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -65,6 +65,49 @@ Open any project on Machine B — Claude Code shows your full session history.
65
65
 
66
66
  ---
67
67
 
68
+ ## Team context
69
+
70
+ Share skills, CLAUDE.md, plugins, and chat sessions with your team via a shared GitHub repo.
71
+
72
+ ```bash
73
+ # Tech Lead — one-time setup
74
+ cortex team init --repo https://github.com/your-org/claude-config
75
+
76
+ # Push your local .claude/ context + sessions (if opted in)
77
+ cortex team push
78
+
79
+ # Dev — pull team context + sessions
80
+ cortex team pull
81
+
82
+ # New dev — first-time install
83
+ cortex install --repo https://github.com/your-org/claude-config
84
+ ```
85
+
86
+ ### What gets shared
87
+
88
+ | What | Source | Destination |
89
+ |---|---|---|
90
+ | Skills | `.claude/skills/*.md` | `skills/` in team repo |
91
+ | CLAUDE.md | `.claude/CLAUDE.md` | `CLAUDE.md` in team repo |
92
+ | Plugins | Installed Claude plugins | `cortex.json → plugins[]` |
93
+ | Sessions (opt-in) | `~/.claude/projects/<project>/` | `sessions/<email>/<project-id>/` |
94
+
95
+ ### Session sharing
96
+
97
+ During `cortex team init` you are asked once whether to share your Claude Code chat sessions with the team. If you accept:
98
+
99
+ - Sessions are uploaded on every `cortex team push`
100
+ - Teammates get your sessions (paths remapped to their machine) on `cortex team pull`
101
+ - Claude Code shows all team sessions natively — no extra steps
102
+
103
+ You can choose to encrypt sessions with a shared team passphrase (AES-256-GCM). Share the passphrase with your team via a password manager — it is never stored by cortex.
104
+
105
+ **Two machines, same GitHub user:** Each machine generates unique session IDs, so pushing from both machines never creates duplicates.
106
+
107
+ > **Privacy:** Sessions may contain source code, API calls, and sensitive context. Only opt in if your team has a shared understanding that sessions are visible to all members.
108
+
109
+ ---
110
+
68
111
  ## Claude Code MCP integration
69
112
 
70
113
  Use `sync`, `pull`, `status`, `convert`, and `init` directly from the Claude Code chat — one command does everything:
package/dist/cli.js CHANGED
@@ -1341,9 +1341,9 @@ Make sure Claude Code CLI is installed and "claude" is in your PATH.`
1341
1341
  }
1342
1342
 
1343
1343
  // src/commands/team/init.ts
1344
- import { input as input3 } from "@inquirer/prompts";
1345
- import { readFile as readFile12, writeFile as writeFile10, mkdir as mkdir10 } from "fs/promises";
1346
- import { join as join15 } from "path";
1344
+ import { input as input3, confirm as confirm3, select as select2, password as password5 } from "@inquirer/prompts";
1345
+ import { writeFile as writeFile12, mkdir as mkdir11 } from "fs/promises";
1346
+ import { join as join17 } from "path";
1347
1347
 
1348
1348
  // src/lib/team-repo.ts
1349
1349
  import { access as access3, mkdir as mkdir8, rm } from "fs/promises";
@@ -1410,10 +1410,9 @@ function commitAndPush(repoUrl, token, message, dir = TEAM_DIR) {
1410
1410
 
1411
1411
  // src/lib/claude-skills.ts
1412
1412
  import { readdir as readdir3, readFile as readFile10, mkdir as mkdir9, writeFile as writeFile9 } from "fs/promises";
1413
- import { homedir as homedir4 } from "os";
1414
1413
  import { join as join13, dirname as dirname7 } from "path";
1415
- var LOCAL_SKILLS_DIR = join13(homedir4(), ".claude", "skills");
1416
- var LOCAL_CLAUDE_MD = join13(homedir4(), ".claude", "CLAUDE.md");
1414
+ var LOCAL_SKILLS_DIR = join13(process.cwd(), ".claude", "skills");
1415
+ var LOCAL_CLAUDE_MD = join13(process.cwd(), ".claude", "CLAUDE.md");
1417
1416
  async function readSkillsFromDir(dir) {
1418
1417
  const map = /* @__PURE__ */ new Map();
1419
1418
  let entries;
@@ -1448,10 +1447,10 @@ async function writeFileToPath(filePath, content) {
1448
1447
 
1449
1448
  // src/lib/claude-plugins.ts
1450
1449
  import { readFile as readFile11 } from "fs/promises";
1451
- import { homedir as homedir5 } from "os";
1450
+ import { homedir as homedir4 } from "os";
1452
1451
  import { join as join14 } from "path";
1453
1452
  import { spawnSync as spawnSync3 } from "child_process";
1454
- var INSTALLED_PLUGINS_PATH = join14(homedir5(), ".claude", "plugins", "installed_plugins.json");
1453
+ var INSTALLED_PLUGINS_PATH = join14(homedir4(), ".claude", "plugins", "installed_plugins.json");
1455
1454
  function parseInstalledPlugins(data) {
1456
1455
  return Object.keys(data.plugins);
1457
1456
  }
@@ -1470,6 +1469,124 @@ function installPlugin(pluginId) {
1470
1469
  if (result.status !== 0) throw new Error(`Plugin install failed for ${pluginId}`);
1471
1470
  }
1472
1471
 
1472
+ // src/lib/project-config.ts
1473
+ import { readFile as readFile12, writeFile as writeFile10 } from "fs/promises";
1474
+ import { join as join15 } from "path";
1475
+ async function readProjectConfig(cwd = process.cwd()) {
1476
+ try {
1477
+ const content = await readFile12(join15(cwd, "cortex.json"), "utf-8");
1478
+ return JSON.parse(content);
1479
+ } catch {
1480
+ return {};
1481
+ }
1482
+ }
1483
+ async function writeProjectConfig(updates, cwd = process.cwd()) {
1484
+ const existing = await readProjectConfig(cwd);
1485
+ const merged = { ...existing, ...updates };
1486
+ await writeFile10(join15(cwd, "cortex.json"), JSON.stringify(merged, null, 2), "utf-8");
1487
+ }
1488
+
1489
+ // src/lib/team-sessions.ts
1490
+ import { readdir as readdir4, readFile as readFile13, writeFile as writeFile11, mkdir as mkdir10 } from "fs/promises";
1491
+ import { homedir as homedir5 } from "os";
1492
+ import { join as join16 } from "path";
1493
+ function localSessionsDir(cwd) {
1494
+ return join16(homedir5(), ".claude", "projects", encodeProjectPath(cwd));
1495
+ }
1496
+ function teamSessionsDir(base, email, projectId) {
1497
+ return join16(base, "sessions", email, projectId);
1498
+ }
1499
+ async function readSessionFiles(dir) {
1500
+ const result = /* @__PURE__ */ new Map();
1501
+ try {
1502
+ const entries = await readdir4(dir);
1503
+ for (const entry of entries) {
1504
+ if (entry.endsWith(".jsonl")) {
1505
+ result.set(entry, await readFile13(join16(dir, entry)));
1506
+ }
1507
+ }
1508
+ } catch {
1509
+ }
1510
+ return result;
1511
+ }
1512
+ async function copySessionsToTeamDir(srcDir, destDir, derived) {
1513
+ const sessions = await readSessionFiles(srcDir);
1514
+ if (sessions.size === 0) return 0;
1515
+ await mkdir10(destDir, { recursive: true });
1516
+ for (const [filename, content] of sessions) {
1517
+ if (derived) {
1518
+ await writeFile11(join16(destDir, `${filename}.enc`), encrypt(content, derived));
1519
+ } else {
1520
+ await writeFile11(join16(destDir, filename), content);
1521
+ }
1522
+ }
1523
+ return sessions.size;
1524
+ }
1525
+ async function copySessionsFromRepo(sessionsRoot, projectId, destDir, localCwd, derived) {
1526
+ let devFolders;
1527
+ try {
1528
+ devFolders = await readdir4(sessionsRoot);
1529
+ } catch {
1530
+ return 0;
1531
+ }
1532
+ await mkdir10(destDir, { recursive: true });
1533
+ let count = 0;
1534
+ for (const email of devFolders) {
1535
+ const projDir = join16(sessionsRoot, email, projectId);
1536
+ let files;
1537
+ try {
1538
+ files = await readdir4(projDir);
1539
+ } catch {
1540
+ continue;
1541
+ }
1542
+ for (const file of files) {
1543
+ try {
1544
+ const isEnc = file.endsWith(".jsonl.enc");
1545
+ const isPlain = file.endsWith(".jsonl") && !isEnc;
1546
+ if (!isEnc && !isPlain) continue;
1547
+ if (isEnc && !derived) continue;
1548
+ let data = await readFile13(join16(projDir, file));
1549
+ if (isEnc && derived) {
1550
+ data = decrypt(data, derived);
1551
+ }
1552
+ const remoteCwd = extractCwdFromJsonl(data);
1553
+ if (remoteCwd && remoteCwd !== localCwd) {
1554
+ data = remapJsonlBuffer(data, remoteCwd, localCwd);
1555
+ }
1556
+ const destFilename = isEnc ? file.slice(0, -".enc".length) : file;
1557
+ const destPath = join16(destDir, destFilename);
1558
+ let finalPath = destPath;
1559
+ try {
1560
+ await readFile13(destPath);
1561
+ const initials = email.split("@")[0].slice(0, 3);
1562
+ const base = destFilename.slice(0, -".jsonl".length);
1563
+ finalPath = join16(destDir, `${base}.${initials}.jsonl`);
1564
+ } catch {
1565
+ }
1566
+ await writeFile11(finalPath, data);
1567
+ count++;
1568
+ } catch {
1569
+ continue;
1570
+ }
1571
+ }
1572
+ }
1573
+ return count;
1574
+ }
1575
+ async function pushSessions(email, cwd, derived) {
1576
+ const projectInfo = identifyProject(cwd);
1577
+ if (!projectInfo) return 0;
1578
+ const srcDir = localSessionsDir(cwd);
1579
+ const destDir = teamSessionsDir(TEAM_DIR, email, projectInfo.projectId);
1580
+ return copySessionsToTeamDir(srcDir, destDir, derived);
1581
+ }
1582
+ async function pullSessions(cwd, derived) {
1583
+ const projectInfo = identifyProject(cwd);
1584
+ if (!projectInfo) return 0;
1585
+ const sessionsRoot = join16(TEAM_DIR, "sessions");
1586
+ const destDir = localSessionsDir(cwd);
1587
+ return copySessionsFromRepo(sessionsRoot, projectInfo.projectId, destDir, cwd, derived);
1588
+ }
1589
+
1473
1590
  // src/commands/team/init.ts
1474
1591
  async function teamInitCommand(opts) {
1475
1592
  const config = await loadConfig();
@@ -1484,35 +1601,67 @@ Cloning ${repoUrl} \u2192 ~/.cortex/team/`);
1484
1601
  await cloneTeamRepo(repoUrl, token);
1485
1602
  const skills = await readSkillsFromDir(LOCAL_SKILLS_DIR);
1486
1603
  if (skills.size > 0) {
1487
- await mkdir10(join15(TEAM_DIR, "skills"), { recursive: true });
1604
+ await mkdir11(join17(TEAM_DIR, "skills"), { recursive: true });
1488
1605
  for (const [filename, content] of skills) {
1489
- await writeFile10(join15(TEAM_DIR, "skills", filename), content, "utf-8");
1606
+ await writeFile12(join17(TEAM_DIR, "skills", filename), content, "utf-8");
1490
1607
  }
1491
- console.log(` Copied ${skills.size} skills`);
1608
+ console.log(` Copied ${skills.size} skills from .claude/skills/`);
1492
1609
  }
1493
1610
  const claudeMd = await readFileFromPath(LOCAL_CLAUDE_MD);
1494
1611
  if (claudeMd) {
1495
- await writeFile10(join15(TEAM_DIR, "CLAUDE.md"), claudeMd, "utf-8");
1496
- console.log(" Copied CLAUDE.md");
1612
+ await writeFile12(join17(TEAM_DIR, "CLAUDE.md"), claudeMd, "utf-8");
1613
+ console.log(" Copied .claude/CLAUDE.md");
1497
1614
  }
1498
1615
  const plugins = await getInstalledPluginIds();
1499
1616
  const cortexJson = { version: "1", plugins };
1500
- await writeFile10(join15(TEAM_DIR, "cortex.json"), JSON.stringify(cortexJson, null, 2), "utf-8");
1617
+ await writeFile12(join17(TEAM_DIR, "cortex.json"), JSON.stringify(cortexJson, null, 2), "utf-8");
1501
1618
  console.log(` Generated cortex.json (${plugins.length} plugins)`);
1502
1619
  commitAndPush(repoUrl, token, "feat: initial team Claude Code context");
1503
- const raw = JSON.parse(await readFile12(CONFIG_PATH, "utf-8"));
1504
- raw.teamRepo = repoUrl;
1505
- await writeFile10(CONFIG_PATH, JSON.stringify(raw, null, 2), "utf-8");
1620
+ console.log("\n\u26A0 Compartir sesiones de chat");
1621
+ console.log(" Tus sesiones de Claude Code para este proyecto se subir\xEDan");
1622
+ console.log(" al repo de equipo y ser\xEDan visibles por todos los miembros.");
1623
+ console.log(" Las sesiones pueden contener c\xF3digo privado o informaci\xF3n sensible.");
1624
+ const shareSession = await confirm3({
1625
+ message: "\xBFCompartir sesiones con el equipo?",
1626
+ default: false
1627
+ });
1628
+ let encryptSessions = false;
1629
+ if (shareSession) {
1630
+ const encChoice = await select2({
1631
+ message: "\xBFEncriptar las sesiones?",
1632
+ choices: [
1633
+ { name: "S\xED \u2014 encriptadas con passphrase del equipo (recomendado)", value: true },
1634
+ { name: "No \u2014 JSONL plano (legible directo en GitHub)", value: false }
1635
+ ]
1636
+ });
1637
+ encryptSessions = encChoice;
1638
+ let derived;
1639
+ if (encryptSessions) {
1640
+ const teamPassphrase = await password5({
1641
+ message: "Team passphrase (shared with all devs, min 12 chars):",
1642
+ mask: "*",
1643
+ validate: (v) => v.length >= 12 || "Minimum 12 characters"
1644
+ });
1645
+ derived = deriveKey(teamPassphrase, repoUrl);
1646
+ }
1647
+ const count = await pushSessions(config.email, process.cwd(), derived);
1648
+ if (count > 0) {
1649
+ commitAndPush(repoUrl, token, `feat: share ${count} team sessions`);
1650
+ console.log(` Uploaded ${count} sessions`);
1651
+ }
1652
+ }
1653
+ await writeProjectConfig({ repo: repoUrl, shareSession, encryptSessions });
1506
1654
  console.log("\n\u2713 Team repo initialized and pushed.");
1507
1655
  console.log(` Devs can now run: cortex install --repo ${repoUrl}`);
1508
1656
  }
1509
1657
 
1510
1658
  // src/commands/team/push.ts
1511
- import { writeFile as writeFile11, mkdir as mkdir11 } from "fs/promises";
1512
- import { join as join16 } from "path";
1659
+ import { writeFile as writeFile13, mkdir as mkdir12 } from "fs/promises";
1660
+ import { join as join18 } from "path";
1661
+ import { password as password6 } from "@inquirer/prompts";
1513
1662
  async function teamPushCommand() {
1514
1663
  const config = await loadConfig();
1515
- const repoUrl = config.teamRepo;
1664
+ const { repo: repoUrl, shareSession, encryptSessions } = await readProjectConfig();
1516
1665
  const token = config.githubToken;
1517
1666
  if (!repoUrl) throw new Error('No team repo configured. Run "cortex team init --repo <url>" first.');
1518
1667
  if (!token) throw new Error('No GitHub token found. Run "cortex init" first.');
@@ -1521,31 +1670,45 @@ async function teamPushCommand() {
1521
1670
  pullTeamRepo();
1522
1671
  const skills = await readSkillsFromDir(LOCAL_SKILLS_DIR);
1523
1672
  if (skills.size > 0) {
1524
- await mkdir11(join16(TEAM_DIR, "skills"), { recursive: true });
1673
+ await mkdir12(join18(TEAM_DIR, "skills"), { recursive: true });
1525
1674
  for (const [filename, content] of skills) {
1526
- await writeFile11(join16(TEAM_DIR, "skills", filename), content, "utf-8");
1675
+ await writeFile13(join18(TEAM_DIR, "skills", filename), content, "utf-8");
1527
1676
  }
1528
1677
  }
1529
1678
  const claudeMd = await readFileFromPath(LOCAL_CLAUDE_MD);
1530
1679
  if (claudeMd) {
1531
- await writeFile11(join16(TEAM_DIR, "CLAUDE.md"), claudeMd, "utf-8");
1680
+ await writeFile13(join18(TEAM_DIR, "CLAUDE.md"), claudeMd, "utf-8");
1532
1681
  }
1533
1682
  const plugins = await getInstalledPluginIds();
1534
- await writeFile11(
1535
- join16(TEAM_DIR, "cortex.json"),
1683
+ await writeFile13(
1684
+ join18(TEAM_DIR, "cortex.json"),
1536
1685
  JSON.stringify({ version: "1", plugins }, null, 2),
1537
1686
  "utf-8"
1538
1687
  );
1688
+ if (shareSession) {
1689
+ let derived;
1690
+ if (encryptSessions) {
1691
+ const teamPassphrase = await password6({
1692
+ message: "Team passphrase:",
1693
+ mask: "*",
1694
+ validate: (v) => v.length >= 12 || "Minimum 12 characters"
1695
+ });
1696
+ derived = deriveKey(teamPassphrase, repoUrl);
1697
+ }
1698
+ const count = await pushSessions(config.email, process.cwd(), derived);
1699
+ if (count > 0) console.log(` Uploaded ${count} sessions`);
1700
+ }
1539
1701
  commitAndPush(repoUrl, token, "chore: update team Claude Code context");
1540
1702
  console.log("\n\u2713 Pushed to team repo.");
1541
1703
  }
1542
1704
 
1543
1705
  // src/commands/team/pull.ts
1544
- import { readFile as readFile13 } from "fs/promises";
1545
- import { join as join17 } from "path";
1706
+ import { readFile as readFile14 } from "fs/promises";
1707
+ import { join as join19 } from "path";
1708
+ import { password as password7 } from "@inquirer/prompts";
1546
1709
 
1547
1710
  // src/lib/conflict.ts
1548
- import { select as select2 } from "@inquirer/prompts";
1711
+ import { select as select3 } from "@inquirer/prompts";
1549
1712
  function hasConflict(local, remote) {
1550
1713
  return local.trim() !== remote.trim();
1551
1714
  }
@@ -1579,7 +1742,7 @@ function displayDiff(filename, local, remote) {
1579
1742
  }
1580
1743
  async function promptConflict(filename, local, remote) {
1581
1744
  displayDiff(filename, local, remote);
1582
- return select2({
1745
+ return select3({
1583
1746
  message: "How to resolve?",
1584
1747
  choices: [
1585
1748
  { name: "[M] Merge \u2014 keep both versions", value: "merge" },
@@ -1591,13 +1754,13 @@ async function promptConflict(filename, local, remote) {
1591
1754
 
1592
1755
  // src/commands/team/pull.ts
1593
1756
  async function teamPullCommand() {
1594
- const config = await loadConfig();
1595
- const repoUrl = config.teamRepo;
1757
+ await loadConfig();
1758
+ const { repo: repoUrl, encryptSessions } = await readProjectConfig();
1596
1759
  if (!repoUrl) throw new Error('No team repo configured. Run "cortex team init --repo <url>" first.');
1597
1760
  if (!await hasLocalClone()) throw new Error('No local team clone found. Run "cortex install" first.');
1598
1761
  console.log("Pulling from team repo\u2026");
1599
1762
  pullTeamRepo();
1600
- const remoteSkills = await readSkillsFromDir(join17(TEAM_DIR, "skills"));
1763
+ const remoteSkills = await readSkillsFromDir(join19(TEAM_DIR, "skills"));
1601
1764
  const localSkills = await readSkillsFromDir(LOCAL_SKILLS_DIR);
1602
1765
  for (const [filename, remoteContent] of remoteSkills) {
1603
1766
  const localContent = localSkills.get(filename) ?? null;
@@ -1618,7 +1781,7 @@ async function teamPullCommand() {
1618
1781
  console.log(` ~ ${filename} skipped`);
1619
1782
  }
1620
1783
  }
1621
- const remoteMd = await readFileFromPath(join17(TEAM_DIR, "CLAUDE.md"));
1784
+ const remoteMd = await readFileFromPath(join19(TEAM_DIR, "CLAUDE.md"));
1622
1785
  if (remoteMd) {
1623
1786
  const localMd = await readFileFromPath(LOCAL_CLAUDE_MD);
1624
1787
  if (!localMd) {
@@ -1637,24 +1800,37 @@ async function teamPullCommand() {
1637
1800
  }
1638
1801
  }
1639
1802
  }
1640
- let cortexJson = {};
1803
+ let cortexManifest = {};
1641
1804
  try {
1642
- cortexJson = JSON.parse(await readFile13(join17(TEAM_DIR, "cortex.json"), "utf-8"));
1805
+ cortexManifest = JSON.parse(await readFile14(join19(TEAM_DIR, "cortex.json"), "utf-8"));
1643
1806
  } catch {
1644
1807
  }
1645
- for (const pluginId of cortexJson.plugins ?? []) {
1808
+ for (const pluginId of cortexManifest.plugins ?? []) {
1646
1809
  try {
1647
1810
  installPlugin(pluginId);
1648
1811
  } catch (e) {
1649
1812
  console.warn(` \u26A0 Could not install ${pluginId}: ${e.message}`);
1650
1813
  }
1651
1814
  }
1815
+ let derived;
1816
+ if (encryptSessions) {
1817
+ const teamPassphrase = await password7({
1818
+ message: "Team passphrase (to decrypt sessions):",
1819
+ mask: "*",
1820
+ validate: (v) => v.length >= 12 || "Minimum 12 characters"
1821
+ });
1822
+ derived = deriveKey(teamPassphrase, repoUrl);
1823
+ }
1824
+ const sessionCount = await pullSessions(process.cwd(), derived);
1825
+ if (sessionCount > 0) {
1826
+ console.log(` + ${sessionCount} sessions installed (restart Claude Code to see them)`);
1827
+ }
1652
1828
  console.log("\n\u2713 Team context applied.");
1653
1829
  }
1654
1830
 
1655
1831
  // src/commands/install.ts
1656
- import { readFile as readFile14, writeFile as writeFile13 } from "fs/promises";
1657
- import { join as join18 } from "path";
1832
+ import { readFile as readFile15 } from "fs/promises";
1833
+ import { join as join20 } from "path";
1658
1834
  async function installCommand(opts) {
1659
1835
  let config;
1660
1836
  try {
@@ -1662,27 +1838,28 @@ async function installCommand(opts) {
1662
1838
  } catch {
1663
1839
  throw new Error('Run "cortex init" first to configure your GitHub token.');
1664
1840
  }
1665
- const repoUrl = opts.repo ?? config.teamRepo;
1841
+ const projectConfig = await readProjectConfig();
1842
+ const repoUrl = opts.repo ?? projectConfig.repo;
1666
1843
  if (!repoUrl) {
1667
1844
  throw new Error('No team repo URL. Pass --repo <url> or run "cortex team init" first.');
1668
1845
  }
1669
1846
  const token = config.githubToken;
1670
1847
  if (!token) throw new Error('No GitHub token found. Run "cortex init" first.');
1671
- console.log(`Installing from ${repoUrl}\u2026`);
1848
+ console.log(`Installing from ${repoUrl} into ${process.cwd()}/.claude/`);
1672
1849
  await cloneTeamRepo(repoUrl, token);
1673
- const skills = await readSkillsFromDir(join18(TEAM_DIR, "skills"));
1850
+ const skills = await readSkillsFromDir(join20(TEAM_DIR, "skills"));
1674
1851
  for (const [filename, content] of skills) {
1675
1852
  await writeSkillToDir(LOCAL_SKILLS_DIR, filename, content);
1676
- console.log(` + ${filename}`);
1853
+ console.log(` + .claude/skills/${filename}`);
1677
1854
  }
1678
- const claudeMd = await readFileFromPath(join18(TEAM_DIR, "CLAUDE.md"));
1855
+ const claudeMd = await readFileFromPath(join20(TEAM_DIR, "CLAUDE.md"));
1679
1856
  if (claudeMd) {
1680
1857
  await writeFileToPath(LOCAL_CLAUDE_MD, claudeMd);
1681
- console.log(" + CLAUDE.md");
1858
+ console.log(" + .claude/CLAUDE.md");
1682
1859
  }
1683
1860
  let cortexJson = {};
1684
1861
  try {
1685
- cortexJson = JSON.parse(await readFile14(join18(TEAM_DIR, "cortex.json"), "utf-8"));
1862
+ cortexJson = JSON.parse(await readFile15(join20(TEAM_DIR, "cortex.json"), "utf-8"));
1686
1863
  } catch {
1687
1864
  }
1688
1865
  for (const pluginId of cortexJson.plugins ?? []) {
@@ -1692,9 +1869,7 @@ async function installCommand(opts) {
1692
1869
  console.warn(` \u26A0 Could not install ${pluginId}: ${e.message}`);
1693
1870
  }
1694
1871
  }
1695
- const raw = JSON.parse(await readFile14(CONFIG_PATH, "utf-8"));
1696
- raw.teamRepo = repoUrl;
1697
- await writeFile13(CONFIG_PATH, JSON.stringify(raw, null, 2), "utf-8");
1872
+ await writeProjectConfig({ repo: repoUrl });
1698
1873
  console.log("\n\u2713 Team context installed. Restart Claude Code to activate new skills and plugins.");
1699
1874
  }
1700
1875
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cortex-sync",
3
- "version": "0.4.0",
3
+ "version": "0.4.2",
4
4
  "description": "Sync Claude Code sessions between machines with automatic path remapping and skill conversion",
5
5
  "license": "AGPL-3.0",
6
6
  "type": "module",