runwork 0.19.1 → 0.20.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 +1217 -867
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -853,6 +853,28 @@ var init_client = __esm(() => {
853
853
  init_http();
854
854
  });
855
855
 
856
+ // src/utils/atomic-json.ts
857
+ import { existsSync, mkdirSync, readFileSync as readFileSync2, renameSync, writeFileSync } from "fs";
858
+ import { dirname } from "path";
859
+ function writeJsonAtomic(path, value, opts = {}) {
860
+ mkdirSync(dirname(path), { recursive: true });
861
+ const json = JSON.stringify(value, null, 2);
862
+ const tmpPath = `${path}.${process.pid}.tmp`;
863
+ writeFileSync(tmpPath, json, opts.mode !== undefined ? { mode: opts.mode } : {});
864
+ renameSync(tmpPath, path);
865
+ }
866
+ function readJsonOrNull(path) {
867
+ if (!existsSync(path))
868
+ return null;
869
+ try {
870
+ const parsed = JSON.parse(readFileSync2(path, "utf-8"));
871
+ return parsed && typeof parsed === "object" ? parsed : null;
872
+ } catch {
873
+ return null;
874
+ }
875
+ }
876
+ var init_atomic_json = () => {};
877
+
856
878
  // src/auth/store.ts
857
879
  var exports_store = {};
858
880
  __export(exports_store, {
@@ -861,7 +883,7 @@ __export(exports_store, {
861
883
  getCredentials: () => getCredentials,
862
884
  clearCredentials: () => clearCredentials
863
885
  });
864
- import { readFileSync as readFileSync2, writeFileSync, mkdirSync, existsSync, unlinkSync } from "fs";
886
+ import { readFileSync as readFileSync3, existsSync as existsSync2, unlinkSync } from "fs";
865
887
  import { join as join2 } from "path";
866
888
  import { homedir } from "os";
867
889
  function getCredentials() {
@@ -880,21 +902,20 @@ function getCredentials() {
880
902
  defaultWorkspaceName: undefined
881
903
  };
882
904
  }
883
- if (!existsSync(CREDENTIALS_FILE))
905
+ if (!existsSync2(CREDENTIALS_FILE))
884
906
  return null;
885
907
  try {
886
- const raw = readFileSync2(CREDENTIALS_FILE, "utf-8");
908
+ const raw = readFileSync3(CREDENTIALS_FILE, "utf-8");
887
909
  return JSON.parse(raw);
888
910
  } catch {
889
911
  return null;
890
912
  }
891
913
  }
892
914
  function saveCredentials(credentials) {
893
- mkdirSync(RUNWORK_DIR, { recursive: true });
894
- writeFileSync(CREDENTIALS_FILE, JSON.stringify(credentials, null, 2), { mode: 384 });
915
+ writeJsonAtomic(CREDENTIALS_FILE, credentials, { mode: 384 });
895
916
  }
896
917
  function clearCredentials() {
897
- if (existsSync(CREDENTIALS_FILE)) {
918
+ if (existsSync2(CREDENTIALS_FILE)) {
898
919
  unlinkSync(CREDENTIALS_FILE);
899
920
  }
900
921
  }
@@ -917,6 +938,7 @@ function requireAuth() {
917
938
  }
918
939
  var RUNWORK_DIR, CREDENTIALS_FILE;
919
940
  var init_store = __esm(() => {
941
+ init_atomic_json();
920
942
  RUNWORK_DIR = join2(homedir(), ".runwork");
921
943
  CREDENTIALS_FILE = join2(RUNWORK_DIR, ".credentials");
922
944
  });
@@ -934,7 +956,7 @@ var execFileSync = (file, args, options) => {
934
956
  var init_subprocess = () => {};
935
957
 
936
958
  // src/git/credentials.ts
937
- import { existsSync as existsSync2 } from "fs";
959
+ import { existsSync as existsSync3 } from "fs";
938
960
  function buildHelperValue(execPath, scriptPath) {
939
961
  const normalised = execPath.replace(/\\/g, "/");
940
962
  const runtimeName = normalised.split("/").pop()?.toLowerCase() ?? "";
@@ -1015,7 +1037,7 @@ function helperBinaryStatus(value) {
1015
1037
  const helperPath = pathMatch?.[1] ?? pathMatch?.[2] ?? "";
1016
1038
  if (!helperPath)
1017
1039
  return { ok: true, path: null };
1018
- return { ok: existsSync2(helperPath), path: helperPath };
1040
+ return { ok: existsSync3(helperPath), path: helperPath };
1019
1041
  }
1020
1042
  async function ensureGitCredentialHelper(baseUrl) {
1021
1043
  let origin;
@@ -1291,7 +1313,7 @@ var init_identity = __esm(() => {
1291
1313
  });
1292
1314
 
1293
1315
  // src/git/preflight.ts
1294
- import { existsSync as existsSync3 } from "fs";
1316
+ import { existsSync as existsSync4 } from "fs";
1295
1317
  import { win32 as winPath } from "path";
1296
1318
  import { homedir as homedir2 } from "os";
1297
1319
  function tryRun(bin) {
@@ -1313,9 +1335,9 @@ function whereGit() {
1313
1335
  const out = buf.toString("utf-8");
1314
1336
  const lines = out.split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
1315
1337
  const exe = lines.find((line) => /\.exe$/i.test(line));
1316
- if (exe && existsSync3(exe))
1338
+ if (exe && existsSync4(exe))
1317
1339
  return exe;
1318
- const fallback = lines.find((line) => existsSync3(line));
1340
+ const fallback = lines.find((line) => existsSync4(line));
1319
1341
  return fallback ?? null;
1320
1342
  } catch {
1321
1343
  return null;
@@ -1340,7 +1362,7 @@ function registryGit() {
1340
1362
  continue;
1341
1363
  const installRoot = match[1].trim();
1342
1364
  const gitExe = winPath.join(installRoot, "cmd", "git.exe");
1343
- if (existsSync3(gitExe))
1365
+ if (existsSync4(gitExe))
1344
1366
  return gitExe;
1345
1367
  } catch {}
1346
1368
  }
@@ -1408,7 +1430,7 @@ function probeGit() {
1408
1430
  }
1409
1431
  }
1410
1432
  for (const candidate of canonicalGitCandidates()) {
1411
- if (!existsSync3(candidate))
1433
+ if (!existsSync4(candidate))
1412
1434
  continue;
1413
1435
  const verify = tryRun(candidate);
1414
1436
  if (verify.ok) {
@@ -1547,7 +1569,7 @@ async function resolveApp(client, nameOrId, workspaceId) {
1547
1569
  }
1548
1570
 
1549
1571
  // src/utils/ignore-matcher.ts
1550
- import { existsSync as existsSync4, readFileSync as readFileSync3 } from "fs";
1572
+ import { existsSync as existsSync5, readFileSync as readFileSync4 } from "fs";
1551
1573
  import { basename, join as join3 } from "path";
1552
1574
  function defaultIgnoreSets() {
1553
1575
  return {
@@ -1589,10 +1611,10 @@ function parseGitignoreContent(content) {
1589
1611
  }
1590
1612
  function loadGitignoreFromDir(dir) {
1591
1613
  const path = join3(dir, ".gitignore");
1592
- if (!existsSync4(path))
1614
+ if (!existsSync5(path))
1593
1615
  return { dirs: new Set, files: new Set };
1594
1616
  try {
1595
- return parseGitignoreContent(readFileSync3(path, "utf-8"));
1617
+ return parseGitignoreContent(readFileSync4(path, "utf-8"));
1596
1618
  } catch {
1597
1619
  return { dirs: new Set, files: new Set };
1598
1620
  }
@@ -1648,7 +1670,7 @@ var init_ignore_matcher = __esm(() => {
1648
1670
 
1649
1671
  // src/template/manifest.ts
1650
1672
  import { createHash } from "crypto";
1651
- import { readFileSync as readFileSync4, writeFileSync as writeFileSync2, existsSync as existsSync5, readdirSync, mkdirSync as mkdirSync2 } from "fs";
1673
+ import { readFileSync as readFileSync5, writeFileSync as writeFileSync2, existsSync as existsSync6, readdirSync, mkdirSync as mkdirSync2 } from "fs";
1652
1674
  import { join as join4, relative, sep } from "path";
1653
1675
  function sha256(data) {
1654
1676
  return "sha256:" + createHash("sha256").update(data).digest("hex");
@@ -1680,24 +1702,24 @@ async function generateManifest(dir) {
1680
1702
  const sets = buildIgnoreSets(dir);
1681
1703
  const allFiles = walkDir(dir, dir, sets);
1682
1704
  for (const relPath of allFiles) {
1683
- const content = readFileSync4(join4(dir, relPath));
1705
+ const content = readFileSync5(join4(dir, relPath));
1684
1706
  files[relPath] = sha256(content);
1685
1707
  }
1686
1708
  return { version: 1, files };
1687
1709
  }
1688
1710
  async function saveManifest(dir, manifest) {
1689
1711
  const manifestDir = join4(dir, ".runwork");
1690
- if (!existsSync5(manifestDir)) {
1712
+ if (!existsSync6(manifestDir)) {
1691
1713
  mkdirSync2(manifestDir, { recursive: true });
1692
1714
  }
1693
1715
  writeFileSync2(join4(manifestDir, "template-manifest.json"), JSON.stringify(manifest, null, 2));
1694
1716
  }
1695
1717
  async function loadManifest(dir) {
1696
1718
  const manifestPath = join4(dir, ".runwork", "template-manifest.json");
1697
- if (!existsSync5(manifestPath))
1719
+ if (!existsSync6(manifestPath))
1698
1720
  return null;
1699
1721
  try {
1700
- const manifest = JSON.parse(readFileSync4(manifestPath, "utf-8"));
1722
+ const manifest = JSON.parse(readFileSync5(manifestPath, "utf-8"));
1701
1723
  const files = {};
1702
1724
  for (const [relPath, hash] of Object.entries(manifest.files)) {
1703
1725
  files[relPath.split("\\").join("/")] = hash;
@@ -1730,7 +1752,7 @@ async function detectUserEdits(dir, manifest) {
1730
1752
  continue;
1731
1753
  const expectedHash = manifest.files[relPath];
1732
1754
  if (expectedHash) {
1733
- const content = readFileSync4(join4(dir, relPath));
1755
+ const content = readFileSync5(join4(dir, relPath));
1734
1756
  if (sha256(content) === expectedHash)
1735
1757
  continue;
1736
1758
  }
@@ -1754,7 +1776,7 @@ async function detectUserEdits(dir, manifest) {
1754
1776
  continue;
1755
1777
  const filePath = join4(dir, relPath);
1756
1778
  try {
1757
- const content = readFileSync4(filePath);
1779
+ const content = readFileSync5(filePath);
1758
1780
  if (sha256(content) !== expectedHash) {
1759
1781
  edits.push(relPath);
1760
1782
  }
@@ -1768,8 +1790,8 @@ var init_manifest = __esm(() => {
1768
1790
  });
1769
1791
 
1770
1792
  // src/utils/zip.ts
1771
- import { mkdirSync as mkdirSync3, readdirSync as readdirSync2, readFileSync as readFileSync5, statSync, writeFileSync as writeFileSync3 } from "fs";
1772
- import { join as join5, dirname, relative as relative2, sep as sep2 } from "path";
1793
+ import { mkdirSync as mkdirSync3, readdirSync as readdirSync2, readFileSync as readFileSync6, statSync, writeFileSync as writeFileSync3 } from "fs";
1794
+ import { join as join5, dirname as dirname2, relative as relative2, sep as sep2 } from "path";
1773
1795
  import { unzipSync, zipSync } from "fflate";
1774
1796
  function ensureDirSync(dir) {
1775
1797
  try {
@@ -1787,7 +1809,7 @@ function extractZip(zipData, targetDir) {
1787
1809
  ensureDirSync(fullPath);
1788
1810
  continue;
1789
1811
  }
1790
- ensureDirSync(dirname(fullPath));
1812
+ ensureDirSync(dirname2(fullPath));
1791
1813
  writeFileSync3(fullPath, data);
1792
1814
  }
1793
1815
  }
@@ -1801,13 +1823,13 @@ function createZipFromDir(sourceDir, outputPath) {
1801
1823
  walk(absPath);
1802
1824
  } else if (stats.isFile()) {
1803
1825
  const relPath = relative2(sourceDir, absPath).split(sep2).join("/");
1804
- files[relPath] = readFileSync5(absPath);
1826
+ files[relPath] = readFileSync6(absPath);
1805
1827
  }
1806
1828
  }
1807
1829
  };
1808
1830
  walk(sourceDir);
1809
1831
  const zipped = zipSync(files);
1810
- ensureDirSync(dirname(outputPath));
1832
+ ensureDirSync(dirname2(outputPath));
1811
1833
  writeFileSync3(outputPath, zipped);
1812
1834
  }
1813
1835
  var init_zip = () => {};
@@ -2103,18 +2125,18 @@ __export(exports_init, {
2103
2125
  DEFAULT_APPS_DIR: () => DEFAULT_APPS_DIR
2104
2126
  });
2105
2127
  import { Command as Command2 } from "commander";
2106
- import { writeFileSync as writeFileSync4, existsSync as existsSync6, mkdirSync as mkdirSync4 } from "fs";
2128
+ import { writeFileSync as writeFileSync4, existsSync as existsSync7, mkdirSync as mkdirSync4 } from "fs";
2107
2129
  import { join as join7, resolve } from "path";
2108
2130
  import { homedir as homedir4 } from "os";
2109
2131
  async function execInit(client, appName, workspace, options = {}, creds) {
2110
2132
  const app = await client.initApp(workspace.id, appName);
2111
2133
  const slug = app.slug || appName.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
2112
2134
  const parentDir = options.here ? process.cwd() : DEFAULT_APPS_DIR;
2113
- if (!options.here && !existsSync6(parentDir)) {
2135
+ if (!options.here && !existsSync7(parentDir)) {
2114
2136
  mkdirSync4(parentDir, { recursive: true });
2115
2137
  }
2116
2138
  const dir = join7(parentDir, slug);
2117
- if (existsSync6(dir)) {
2139
+ if (existsSync7(dir)) {
2118
2140
  console.error(`Directory "${dir}" already exists.`);
2119
2141
  process.exit(1);
2120
2142
  }
@@ -2141,7 +2163,7 @@ async function execInit(client, appName, workspace, options = {}, creds) {
2141
2163
  appName: app.name
2142
2164
  };
2143
2165
  writeFileSync4(join7(dir, ".runwork.json"), JSON.stringify(config, null, 2));
2144
- if (!existsSync6(join7(dir, ".git"))) {
2166
+ if (!existsSync7(join7(dir, ".git"))) {
2145
2167
  try {
2146
2168
  execFileSync("git", ["init"], { cwd: dir, stdio: "pipe" });
2147
2169
  } catch (err) {
@@ -2159,7 +2181,7 @@ async function execInit(client, appName, workspace, options = {}, creds) {
2159
2181
  } catch {
2160
2182
  execFileSync("git", ["remote", "set-url", "runwork", remoteUrl], { cwd: dir, stdio: "pipe" });
2161
2183
  }
2162
- if (!existsSync6(join7(dir, ".gitignore"))) {
2184
+ if (!existsSync7(join7(dir, ".gitignore"))) {
2163
2185
  writeFileSync4(join7(dir, ".gitignore"), `node_modules/
2164
2186
  .runwork/
2165
2187
  .dev.vars
@@ -2412,7 +2434,7 @@ __export(exports_clone, {
2412
2434
  RESTRICTED_FS_HELP: () => RESTRICTED_FS_HELP
2413
2435
  });
2414
2436
  import { Command as Command3 } from "commander";
2415
- import { writeFileSync as writeFileSync5, mkdirSync as mkdirSync5, existsSync as existsSync7 } from "fs";
2437
+ import { writeFileSync as writeFileSync5, mkdirSync as mkdirSync5, existsSync as existsSync8 } from "fs";
2416
2438
  import { join as join8, resolve as resolve2 } from "path";
2417
2439
  async function execClone(client, app, directory, creds) {
2418
2440
  const slug = app.slug || app.name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
@@ -2430,7 +2452,7 @@ async function execClone(client, app, directory, creds) {
2430
2452
  }
2431
2453
  const manifest = await generateManifest(dir);
2432
2454
  await saveManifest(dir, manifest);
2433
- if (!existsSync7(join8(dir, ".git"))) {
2455
+ if (!existsSync8(join8(dir, ".git"))) {
2434
2456
  try {
2435
2457
  execFileSync("git", ["init"], { cwd: dir, stdio: "pipe" });
2436
2458
  } catch (err) {
@@ -2603,7 +2625,7 @@ App "${cloneResult.appName}" cloned to ${cloneResult.directory}`);
2603
2625
  });
2604
2626
 
2605
2627
  // src/git/auto-commit.ts
2606
- import { readFileSync as readFileSync6 } from "fs";
2628
+ import { readFileSync as readFileSync7 } from "fs";
2607
2629
  import { watch } from "chokidar";
2608
2630
  import { join as join9, relative as relative4 } from "path";
2609
2631
  async function watchAndAutoCommit(directory, client, appId, callbacks) {
@@ -2665,7 +2687,7 @@ async function executeFastSync(directory, client, appId) {
2665
2687
  if (BINARY_EXTENSIONS.has(ext.toLowerCase()))
2666
2688
  continue;
2667
2689
  try {
2668
- const contents = readFileSync6(join9(directory, filePath), "utf-8");
2690
+ const contents = readFileSync7(join9(directory, filePath), "utf-8");
2669
2691
  files.push({ filePath, fileContents: contents });
2670
2692
  } catch {}
2671
2693
  }
@@ -2959,16 +2981,16 @@ var init_sync = __esm(() => {
2959
2981
  });
2960
2982
 
2961
2983
  // src/git/critical-files.ts
2962
- import { readFileSync as readFileSync7, writeFileSync as writeFileSync6, existsSync as existsSync8 } from "fs";
2984
+ import { readFileSync as readFileSync8, writeFileSync as writeFileSync6, existsSync as existsSync9 } from "fs";
2963
2985
  import { join as join11 } from "path";
2964
2986
  function snapshotCriticalFiles(cwd) {
2965
2987
  const snapshots = [];
2966
2988
  for (const rel of CRITICAL_FILES) {
2967
2989
  const abs = join11(cwd, rel);
2968
- if (!existsSync8(abs))
2990
+ if (!existsSync9(abs))
2969
2991
  continue;
2970
2992
  try {
2971
- snapshots.push({ path: rel, contents: readFileSync7(abs, "utf-8") });
2993
+ snapshots.push({ path: rel, contents: readFileSync8(abs, "utf-8") });
2972
2994
  } catch {}
2973
2995
  }
2974
2996
  return snapshots;
@@ -2977,7 +2999,7 @@ function restoreMissingCriticalFiles(cwd, snapshots) {
2977
2999
  const restored = [];
2978
3000
  for (const snap of snapshots) {
2979
3001
  const abs = join11(cwd, snap.path);
2980
- if (existsSync8(abs))
3002
+ if (existsSync9(abs))
2981
3003
  continue;
2982
3004
  try {
2983
3005
  writeFileSync6(abs, snap.contents, "utf-8");
@@ -3020,7 +3042,7 @@ function buildStartupSyncSummary(input) {
3020
3042
 
3021
3043
  // src/logs/tailer.ts
3022
3044
  import { appendFileSync, mkdirSync as mkdirSync6, writeFileSync as writeFileSync7 } from "fs";
3023
- import { join as join12, dirname as dirname2 } from "path";
3045
+ import { join as join12, dirname as dirname3 } from "path";
3024
3046
  function formatTime() {
3025
3047
  const now = new Date;
3026
3048
  return [
@@ -3070,7 +3092,7 @@ function startLogTailer(options) {
3070
3092
  } = options;
3071
3093
  const logFilePath = join12(projectDir, LOG_FILE);
3072
3094
  if (toFile) {
3073
- mkdirSync6(dirname2(logFilePath), { recursive: true });
3095
+ mkdirSync6(dirname3(logFilePath), { recursive: true });
3074
3096
  writeFileSync7(logFilePath, `# Runwork dev logs - started ${new Date().toISOString()}
3075
3097
 
3076
3098
  `, "utf-8");
@@ -7480,13 +7502,13 @@ export {};
7480
7502
  });
7481
7503
 
7482
7504
  // src/types-manager.ts
7483
- import { existsSync as existsSync13, mkdirSync as mkdirSync9, readdirSync as readdirSync4, copyFileSync, writeFileSync as writeFileSync9 } from "fs";
7505
+ import { existsSync as existsSync14, mkdirSync as mkdirSync9, readdirSync as readdirSync4, copyFileSync, writeFileSync as writeFileSync9 } from "fs";
7484
7506
  import { join as join14 } from "path";
7485
7507
  async function populateTypes(projectDir) {
7486
7508
  const typesDir = join14(projectDir, TYPES_DIR);
7487
7509
  mkdirSync9(typesDir, { recursive: true });
7488
7510
  const frameworkDist = join14(projectDir, "node_modules/@runworkai/framework/dist");
7489
- if (existsSync13(frameworkDist)) {
7511
+ if (existsSync14(frameworkDist)) {
7490
7512
  copyDtsFiles(frameworkDist, typesDir);
7491
7513
  console.log("Types populated from node_modules/@runworkai/framework");
7492
7514
  return;
@@ -7623,7 +7645,7 @@ function createKeyboardListener() {
7623
7645
  }
7624
7646
 
7625
7647
  // src/generated/version.ts
7626
- var VERSION = "0.19.1";
7648
+ var VERSION = "0.20.0";
7627
7649
 
7628
7650
  // src/commands/dev.ts
7629
7651
  var exports_dev = {};
@@ -7632,7 +7654,7 @@ __export(exports_dev, {
7632
7654
  devCommand: () => devCommand
7633
7655
  });
7634
7656
  import { Command as Command4, Option } from "commander";
7635
- import { readFileSync as readFileSync11, writeFileSync as writeFileSync10, existsSync as existsSync14 } from "fs";
7657
+ import { readFileSync as readFileSync12, writeFileSync as writeFileSync10, existsSync as existsSync15 } from "fs";
7636
7658
  import { join as join15 } from "path";
7637
7659
  async function populateSkill(projectDir, client, appId) {
7638
7660
  try {
@@ -7643,11 +7665,11 @@ async function populateSkill(projectDir, client, appId) {
7643
7665
  } catch {}
7644
7666
  }
7645
7667
  function readConfig() {
7646
- if (!existsSync14(".runwork.json")) {
7668
+ if (!existsSync15(".runwork.json")) {
7647
7669
  console.error("No .runwork.json found. Run `runwork init` first.");
7648
7670
  process.exit(1);
7649
7671
  }
7650
- return JSON.parse(readFileSync11(".runwork.json", "utf-8"));
7672
+ return JSON.parse(readFileSync12(".runwork.json", "utf-8"));
7651
7673
  }
7652
7674
  async function execDev(options) {
7653
7675
  const useJson = options?.json ?? false;
@@ -8527,7 +8549,7 @@ var exports_welcome = {};
8527
8549
  __export(exports_welcome, {
8528
8550
  runWelcomeWizard: () => runWelcomeWizard
8529
8551
  });
8530
- import { basename as basename3 } from "path";
8552
+ import { basename as basename4 } from "path";
8531
8553
  async function runWelcomeWizard() {
8532
8554
  console.log(getWelcomeBanner());
8533
8555
  let creds = getCredentials();
@@ -8593,7 +8615,7 @@ async function runWelcomeWizard() {
8593
8615
  const { execDev: execDev2 } = await Promise.resolve().then(() => (init_dev(), exports_dev));
8594
8616
  await execDev2();
8595
8617
  } else {
8596
- const slug = basename3(appDir) || appDir;
8618
+ const slug = basename4(appDir) || appDir;
8597
8619
  console.log("");
8598
8620
  console.log(`Next: ${cyan(`cd ${slug} && runwork dev`)}`);
8599
8621
  }
@@ -8608,7 +8630,7 @@ var init_welcome = __esm(() => {
8608
8630
  });
8609
8631
 
8610
8632
  // src/index.ts
8611
- import { Command as Command36 } from "commander";
8633
+ import { Command as Command37 } from "commander";
8612
8634
 
8613
8635
  // src/commands/login.ts
8614
8636
  init_login_flow();
@@ -8655,12 +8677,12 @@ init_store();
8655
8677
  init_client();
8656
8678
  init_colors();
8657
8679
  import { Command as Command5, Option as Option2 } from "commander";
8658
- import { readFileSync as readFileSync14, existsSync as existsSync17 } from "fs";
8680
+ import { readFileSync as readFileSync15, existsSync as existsSync18 } from "fs";
8659
8681
 
8660
8682
  // src/deploy/deploy-state.ts
8661
8683
  init_subprocess();
8662
- import { existsSync as existsSync15, mkdirSync as mkdirSync10, readFileSync as readFileSync12, writeFileSync as writeFileSync11 } from "fs";
8663
- import { dirname as dirname3, join as join16 } from "path";
8684
+ import { existsSync as existsSync16, mkdirSync as mkdirSync10, readFileSync as readFileSync13, writeFileSync as writeFileSync11 } from "fs";
8685
+ import { dirname as dirname4, join as join16 } from "path";
8664
8686
  function deployStatePath(cwd) {
8665
8687
  return join16(cwd, ".runwork", "last-deploy.json");
8666
8688
  }
@@ -8674,17 +8696,17 @@ function getHeadSha(cwd) {
8674
8696
  function writeDeployState(cwd, state) {
8675
8697
  const path2 = deployStatePath(cwd);
8676
8698
  try {
8677
- if (!existsSync15(dirname3(path2)))
8678
- mkdirSync10(dirname3(path2), { recursive: true });
8699
+ if (!existsSync16(dirname4(path2)))
8700
+ mkdirSync10(dirname4(path2), { recursive: true });
8679
8701
  writeFileSync11(path2, JSON.stringify(state, null, 2));
8680
8702
  } catch {}
8681
8703
  }
8682
8704
  function readDeployState(cwd) {
8683
8705
  const path2 = deployStatePath(cwd);
8684
- if (!existsSync15(path2))
8706
+ if (!existsSync16(path2))
8685
8707
  return null;
8686
8708
  try {
8687
- const parsed = JSON.parse(readFileSync12(path2, "utf-8"));
8709
+ const parsed = JSON.parse(readFileSync13(path2, "utf-8"));
8688
8710
  if (typeof parsed.sha === "string" && typeof parsed.deployedAt === "string") {
8689
8711
  return { sha: parsed.sha, deployedAt: parsed.deployedAt, url: parsed.url ?? "" };
8690
8712
  }
@@ -8712,8 +8734,8 @@ function getDeploySummary(cwd) {
8712
8734
 
8713
8735
  // src/deploy/deploy-status.ts
8714
8736
  init_session();
8715
- import { existsSync as existsSync16, mkdirSync as mkdirSync11, readFileSync as readFileSync13, writeFileSync as writeFileSync12 } from "fs";
8716
- import { dirname as dirname4, join as join17 } from "path";
8737
+ import { existsSync as existsSync17, mkdirSync as mkdirSync11, readFileSync as readFileSync14, writeFileSync as writeFileSync12 } from "fs";
8738
+ import { dirname as dirname5, join as join17 } from "path";
8717
8739
  function evaluateDeployStatus(status, deps = {}) {
8718
8740
  if (status.state !== "in-progress") {
8719
8741
  return { status, effectiveState: status.state };
@@ -8737,17 +8759,17 @@ function deployLogPath(cwd) {
8737
8759
  function writeDeployStatus(cwd, status) {
8738
8760
  const path2 = statusPath(cwd);
8739
8761
  try {
8740
- if (!existsSync16(dirname4(path2)))
8741
- mkdirSync11(dirname4(path2), { recursive: true });
8762
+ if (!existsSync17(dirname5(path2)))
8763
+ mkdirSync11(dirname5(path2), { recursive: true });
8742
8764
  writeFileSync12(path2, JSON.stringify(status, null, 2));
8743
8765
  } catch {}
8744
8766
  }
8745
8767
  function readDeployStatus(cwd) {
8746
8768
  const path2 = statusPath(cwd);
8747
- if (!existsSync16(path2))
8769
+ if (!existsSync17(path2))
8748
8770
  return null;
8749
8771
  try {
8750
- const parsed = JSON.parse(readFileSync13(path2, "utf-8"));
8772
+ const parsed = JSON.parse(readFileSync14(path2, "utf-8"));
8751
8773
  if ((parsed.state === "in-progress" || parsed.state === "succeeded" || parsed.state === "failed") && typeof parsed.startedAt === "string") {
8752
8774
  return parsed;
8753
8775
  }
@@ -8853,7 +8875,7 @@ init_prompt();
8853
8875
  var deployCommand = new Command5("deploy").description("Deploy the current app to production").option("-y, --yes", "Skip the confirmation prompt when the working tree differs from the preview").option("--detach", "Run the deploy in the background and return immediately. Logs go to .runwork/deploy-{stdout,stderr}.log; poll `runwork deploy --status`.").option("--status", "Show the status (in-progress/succeeded/failed) of the last deploy started from this machine, plus the deployed-vs-local commit.").addOption(new Option2(DEPLOY_DETACHED_CHILD_FLAG).hideHelp()).action(async (opts, command) => {
8854
8876
  const useJson = shouldOutputJson(command.optsWithGlobals().json);
8855
8877
  const cwd = process.cwd();
8856
- if (!existsSync17(".runwork.json")) {
8878
+ if (!existsSync18(".runwork.json")) {
8857
8879
  if (useJson) {
8858
8880
  jsonOut(buildErrorResponse("deploy", "No .runwork.json found", "This directory is not a Runwork app.", ["Run runwork init to create an app first", "Or cd into an existing app directory"]));
8859
8881
  process.exit(1);
@@ -8866,7 +8888,7 @@ var deployCommand = new Command5("deploy").description("Deploy the current app t
8866
8888
  return;
8867
8889
  }
8868
8890
  requireGit("deploy");
8869
- const config = JSON.parse(readFileSync14(".runwork.json", "utf-8"));
8891
+ const config = JSON.parse(readFileSync15(".runwork.json", "utf-8"));
8870
8892
  const isChild = isInternalDeployChild(process.argv);
8871
8893
  if (opts.detach && !isChild) {
8872
8894
  const startedAt = new Date().toISOString();
@@ -9079,7 +9101,7 @@ init_client();
9079
9101
  init_colors();
9080
9102
  init_preflight();
9081
9103
  import { Command as Command6 } from "commander";
9082
- import { readFileSync as readFileSync15, existsSync as existsSync18 } from "fs";
9104
+ import { readFileSync as readFileSync16, existsSync as existsSync19 } from "fs";
9083
9105
 
9084
9106
  // src/validate/freshness.ts
9085
9107
  init_subprocess();
@@ -9151,7 +9173,7 @@ var BUILD_FOLLOWUP = "In-sandbox typecheck/lint requires a worker exec endpoint
9151
9173
  var validateCommand = new Command6("validate").description("Validate the app before deploy: git sync state + preview liveness (no local deps needed)").action(async (_opts, command) => {
9152
9174
  const useJson = shouldOutputJson(command.optsWithGlobals().json);
9153
9175
  const cwd = process.cwd();
9154
- if (!existsSync18(".runwork.json")) {
9176
+ if (!existsSync19(".runwork.json")) {
9155
9177
  if (useJson) {
9156
9178
  jsonOut({ success: false, command: "validate", error: "No .runwork.json found. Run inside a Runwork app." });
9157
9179
  process.exit(1);
@@ -9160,7 +9182,7 @@ var validateCommand = new Command6("validate").description("Validate the app bef
9160
9182
  process.exit(1);
9161
9183
  }
9162
9184
  requireGit("validate");
9163
- const config = JSON.parse(readFileSync15(".runwork.json", "utf-8"));
9185
+ const config = JSON.parse(readFileSync16(".runwork.json", "utf-8"));
9164
9186
  const creds = requireAuth();
9165
9187
  const client = new ApiClient(creds);
9166
9188
  const sync = getGitFreshness(cwd);
@@ -9216,13 +9238,13 @@ var validateCommand = new Command6("validate").description("Validate the app bef
9216
9238
  init_store();
9217
9239
  init_client();
9218
9240
  import { Command as Command7 } from "commander";
9219
- import { readFileSync as readFileSync16, existsSync as existsSync19 } from "fs";
9241
+ import { readFileSync as readFileSync17, existsSync as existsSync20 } from "fs";
9220
9242
  function readConfig2() {
9221
- if (!existsSync19(".runwork.json")) {
9243
+ if (!existsSync20(".runwork.json")) {
9222
9244
  console.error("No .runwork.json found. Run `runwork init` first.");
9223
9245
  process.exit(1);
9224
9246
  }
9225
- return JSON.parse(readFileSync16(".runwork.json", "utf-8"));
9247
+ return JSON.parse(readFileSync17(".runwork.json", "utf-8"));
9226
9248
  }
9227
9249
  function formatEvent(event) {
9228
9250
  const time = new Date(event.timestamp).toLocaleTimeString();
@@ -9643,12 +9665,12 @@ var logoutCommand = new Command9("logout").description("Remove stored Runwork cr
9643
9665
  init_store();
9644
9666
  init_client();
9645
9667
  import { Command as Command10 } from "commander";
9646
- import { readFileSync as readFileSync18, existsSync as existsSync21 } from "fs";
9668
+ import { readFileSync as readFileSync19, existsSync as existsSync22 } from "fs";
9647
9669
 
9648
9670
  // src/workspace/resolve.ts
9649
9671
  init_store();
9650
9672
  init_prompt();
9651
- import { existsSync as existsSync20, readFileSync as readFileSync17 } from "fs";
9673
+ import { existsSync as existsSync21, readFileSync as readFileSync18 } from "fs";
9652
9674
  var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
9653
9675
  async function resolveWorkspace2(client, options = {}) {
9654
9676
  if (options.workspace) {
@@ -9661,9 +9683,9 @@ async function resolveWorkspace2(client, options = {}) {
9661
9683
  if (process.env.WORKSPACE_ID) {
9662
9684
  return { workspaceId: process.env.WORKSPACE_ID, workspaceName: "", source: "flag" };
9663
9685
  }
9664
- if (existsSync20(".runwork.json")) {
9686
+ if (existsSync21(".runwork.json")) {
9665
9687
  try {
9666
- const config = JSON.parse(readFileSync17(".runwork.json", "utf-8"));
9688
+ const config = JSON.parse(readFileSync18(".runwork.json", "utf-8"));
9667
9689
  if (config.workspaceId) {
9668
9690
  return {
9669
9691
  workspaceId: config.workspaceId,
@@ -9708,7 +9730,7 @@ function saveDefaultWorkspace(workspaceId, workspaceName) {
9708
9730
  }
9709
9731
  }
9710
9732
  function hasProjectConfig() {
9711
- return existsSync20(".runwork.json");
9733
+ return existsSync21(".runwork.json");
9712
9734
  }
9713
9735
  async function resolveApp2(client, workspaceId, options = {}) {
9714
9736
  if (options.app) {
@@ -9720,9 +9742,9 @@ async function resolveApp2(client, workspaceId, options = {}) {
9720
9742
  }
9721
9743
  return { appId: match.id, appName: match.name, source: "flag" };
9722
9744
  }
9723
- if (existsSync20(".runwork.json")) {
9745
+ if (existsSync21(".runwork.json")) {
9724
9746
  try {
9725
- const config = JSON.parse(readFileSync17(".runwork.json", "utf-8"));
9747
+ const config = JSON.parse(readFileSync18(".runwork.json", "utf-8"));
9726
9748
  if (config.appId) {
9727
9749
  return { appId: config.appId, appName: config.appName || "", source: "project" };
9728
9750
  }
@@ -10055,11 +10077,11 @@ var callCommand = new Command10("call").description("Make a proxy call to a conn
10055
10077
  let curlStr = opts.curl;
10056
10078
  if (opts.curlFile) {
10057
10079
  const filePath = opts.curlFile;
10058
- if (!existsSync21(filePath)) {
10080
+ if (!existsSync22(filePath)) {
10059
10081
  console.error(`File not found: ${filePath}`);
10060
10082
  process.exit(1);
10061
10083
  }
10062
- curlStr = readFileSync18(filePath, "utf-8");
10084
+ curlStr = readFileSync19(filePath, "utf-8");
10063
10085
  }
10064
10086
  try {
10065
10087
  const result = await parseCurlToRequest(curlStr);
@@ -10122,14 +10144,14 @@ init_store();
10122
10144
  init_client();
10123
10145
  init_colors();
10124
10146
  import { Command as Command11 } from "commander";
10125
- import { readFileSync as readFileSync19, existsSync as existsSync22 } from "fs";
10147
+ import { readFileSync as readFileSync20, existsSync as existsSync23 } from "fs";
10126
10148
  var openCommand = new Command11("open").description("Open app preview or dashboard in browser").argument("[target]", "What to open: preview (default), dashboard", "preview").action(async (target, _opts, command) => {
10127
10149
  const useJson = shouldOutputJson(command.optsWithGlobals().json);
10128
- if (!existsSync22(".runwork.json")) {
10150
+ if (!existsSync23(".runwork.json")) {
10129
10151
  console.error("No .runwork.json found. Run `runwork init` first.");
10130
10152
  process.exit(1);
10131
10153
  }
10132
- const config = JSON.parse(readFileSync19(".runwork.json", "utf-8"));
10154
+ const config = JSON.parse(readFileSync20(".runwork.json", "utf-8"));
10133
10155
  const creds = requireAuth();
10134
10156
  const client = new ApiClient(creds);
10135
10157
  const open = await import("open");
@@ -10170,7 +10192,7 @@ var openCommand = new Command11("open").description("Open app preview or dashboa
10170
10192
  // src/commands/info.ts
10171
10193
  init_agent_guidance();
10172
10194
  import { Command as Command12 } from "commander";
10173
- import { readFileSync as readFileSync20, existsSync as existsSync23 } from "fs";
10195
+ import { readFileSync as readFileSync21, existsSync as existsSync24 } from "fs";
10174
10196
  import { join as join20 } from "path";
10175
10197
 
10176
10198
  // src/utils/app-info.ts
@@ -10362,20 +10384,20 @@ function readLocalDevSession(appDir, appId) {
10362
10384
  };
10363
10385
  }
10364
10386
  function tryReadConfig() {
10365
- if (!existsSync23(".runwork.json"))
10387
+ if (!existsSync24(".runwork.json"))
10366
10388
  return null;
10367
10389
  try {
10368
- return JSON.parse(readFileSync20(".runwork.json", "utf-8"));
10390
+ return JSON.parse(readFileSync21(".runwork.json", "utf-8"));
10369
10391
  } catch {
10370
10392
  return null;
10371
10393
  }
10372
10394
  }
10373
10395
  function readBlueprint(cwd) {
10374
10396
  const blueprintPath = join20(cwd, "blueprint.json");
10375
- if (!existsSync23(blueprintPath))
10397
+ if (!existsSync24(blueprintPath))
10376
10398
  return null;
10377
10399
  try {
10378
- return JSON.parse(readFileSync20(blueprintPath, "utf-8"));
10400
+ return JSON.parse(readFileSync21(blueprintPath, "utf-8"));
10379
10401
  } catch {
10380
10402
  return null;
10381
10403
  }
@@ -10719,7 +10741,7 @@ var infoCommand = new Command12("info").description("Show app context, registrie
10719
10741
  init_store();
10720
10742
  init_client();
10721
10743
  import { Command as Command13 } from "commander";
10722
- import { readFileSync as readFileSync21, existsSync as existsSync24 } from "fs";
10744
+ import { readFileSync as readFileSync22, existsSync as existsSync25 } from "fs";
10723
10745
  function truncate(text2, max) {
10724
10746
  if (!text2)
10725
10747
  return "";
@@ -10797,7 +10819,7 @@ var pushCommand = new Command13("push").description("Upload a local skill file t
10797
10819
  nameArg = first;
10798
10820
  filePath = second;
10799
10821
  } else if (first) {
10800
- if (existsSync24(first)) {
10822
+ if (existsSync25(first)) {
10801
10823
  filePath = first;
10802
10824
  } else if (!process.stdin.isTTY) {
10803
10825
  nameArg = first;
@@ -10814,11 +10836,11 @@ var pushCommand = new Command13("push").description("Upload a local skill file t
10814
10836
  }
10815
10837
  let content;
10816
10838
  if (filePath) {
10817
- if (!existsSync24(filePath)) {
10839
+ if (!existsSync25(filePath)) {
10818
10840
  console.error(`File not found: ${filePath}`);
10819
10841
  process.exit(1);
10820
10842
  }
10821
- content = readFileSync21(filePath, "utf-8");
10843
+ content = readFileSync22(filePath, "utf-8");
10822
10844
  } else {
10823
10845
  content = await readStdin2();
10824
10846
  if (!content.trim()) {
@@ -11030,8 +11052,8 @@ var skillsCommand = new Command13("skills").description("Manage workspace skills
11030
11052
  // src/commands/reflect.ts
11031
11053
  init_subprocess();
11032
11054
  import { Command as Command14 } from "commander";
11033
- import { writeFileSync as writeFileSync27, mkdirSync as mkdirSync26 } from "fs";
11034
- import { join as join34 } from "path";
11055
+ import { writeFileSync as writeFileSync25, mkdirSync as mkdirSync25 } from "fs";
11056
+ import { join as join37 } from "path";
11035
11057
 
11036
11058
  // src/utils/which.ts
11037
11059
  init_subprocess();
@@ -11069,9 +11091,9 @@ init_client();
11069
11091
 
11070
11092
  // src/agents/claude-code.ts
11071
11093
  init_subprocess();
11072
- import { chmodSync, existsSync as existsSync27, mkdirSync as mkdirSync15, readFileSync as readFileSync24, readdirSync as readdirSync5, rmSync as rmSync4, statSync as statSync3, writeFileSync as writeFileSync16 } from "fs";
11073
- import { join as join21 } from "path";
11074
- import { homedir as homedir5, platform as platform2 } from "os";
11094
+ import { chmodSync, existsSync as existsSync30, mkdirSync as mkdirSync16, readFileSync as readFileSync25, readdirSync as readdirSync7, rmSync as rmSync5, statSync as statSync4, writeFileSync as writeFileSync16 } from "fs";
11095
+ import { join as join23 } from "path";
11096
+ import { homedir as homedir6, platform as platform2 } from "os";
11075
11097
 
11076
11098
  // ../../shared/skill/skill-canonical.ts
11077
11099
  function toSkillSlug(value) {
@@ -11180,6 +11202,10 @@ function buildSkillMd(parts) {
11180
11202
  var RUNWORK_MCP_PREFIX = "Runwork: ";
11181
11203
  var RUNWORK_MCP_PREFIX_LEGACY = "runwork-";
11182
11204
  var RUNWORK_WORKSPACE_MCP_NAME = "Runwork";
11205
+ var RUNWORK_PLUGIN_MARKETPLACE = "runwork";
11206
+ function isRunworkPluginKey(key) {
11207
+ return key.endsWith(`@${RUNWORK_PLUGIN_MARKETPLACE}`);
11208
+ }
11183
11209
  function appendTokenToUrl(url, token) {
11184
11210
  const parsed = new URL(url);
11185
11211
  parsed.searchParams.set("token", token);
@@ -11515,9 +11541,70 @@ function skillNameFromPath(path2) {
11515
11541
  return canonical || null;
11516
11542
  }
11517
11543
 
11544
+ // src/utils/trash.ts
11545
+ init_atomic_json();
11546
+ import { cpSync, existsSync as existsSync26, mkdirSync as mkdirSync13, readdirSync as readdirSync5, renameSync as renameSync3, rmSync as rmSync4, statSync as statSync3 } from "fs";
11547
+ import { basename as basename2, dirname as dirname6, join as join21 } from "path";
11548
+ import { homedir as homedir5 } from "os";
11549
+ var TRASH_RETENTION_DAYS = 30;
11550
+ function trashRoot() {
11551
+ return join21(homedir5(), ".runwork", "trash");
11552
+ }
11553
+ function batchDir(now) {
11554
+ const stamp = now.toISOString().replace(/[:.]/g, "-");
11555
+ return join21(trashRoot(), `${stamp}-${process.pid}`);
11556
+ }
11557
+ function pruneTrash(now = new Date) {
11558
+ const root = trashRoot();
11559
+ if (!existsSync26(root))
11560
+ return;
11561
+ const cutoff = now.getTime() - TRASH_RETENTION_DAYS * 24 * 60 * 60 * 1000;
11562
+ for (const entry of readdirSync5(root)) {
11563
+ const dir = join21(root, entry);
11564
+ try {
11565
+ if (statSync3(dir).mtimeMs < cutoff)
11566
+ rmSync4(dir, { recursive: true, force: true });
11567
+ } catch {}
11568
+ }
11569
+ }
11570
+ var activeBatch = null;
11571
+ function moveToTrash(sourcePath, reason, now = new Date) {
11572
+ if (!existsSync26(sourcePath))
11573
+ return null;
11574
+ if (!activeBatch || !activeBatch.startsWith(trashRoot())) {
11575
+ activeBatch = batchDir(now);
11576
+ pruneTrash(now);
11577
+ }
11578
+ const parent = basename2(dirname6(sourcePath));
11579
+ const grandparent = basename2(dirname6(dirname6(sourcePath)));
11580
+ const destDir = join21(activeBatch, `${grandparent}__${parent}`.replace(/[^a-zA-Z0-9._-]/g, "-"));
11581
+ const dest = join21(destDir, basename2(sourcePath));
11582
+ try {
11583
+ mkdirSync13(destDir, { recursive: true });
11584
+ try {
11585
+ renameSync3(sourcePath, dest);
11586
+ } catch {
11587
+ cpSync(sourcePath, dest, { recursive: true });
11588
+ rmSync4(sourcePath, { recursive: true, force: true });
11589
+ }
11590
+ } catch {
11591
+ return null;
11592
+ }
11593
+ const manifestPath = join21(activeBatch, "manifest.json");
11594
+ const manifest = readJsonOrNull(manifestPath) ?? { entries: [] };
11595
+ manifest.entries.push({ from: sourcePath, to: dest, reason, at: now.toISOString() });
11596
+ try {
11597
+ writeJsonAtomic(manifestPath, manifest);
11598
+ } catch {}
11599
+ return dest;
11600
+ }
11601
+ function currentTrashBatch() {
11602
+ return activeBatch;
11603
+ }
11604
+
11518
11605
  // src/agents/utils/json-config.ts
11519
- import { readFileSync as readFileSync22, writeFileSync as writeFileSync14, mkdirSync as mkdirSync13, existsSync as existsSync25 } from "fs";
11520
- import { dirname as dirname5 } from "path";
11606
+ import { readFileSync as readFileSync23, writeFileSync as writeFileSync14, mkdirSync as mkdirSync14, existsSync as existsSync27 } from "fs";
11607
+ import { dirname as dirname7 } from "path";
11521
11608
 
11522
11609
  // src/sync/hash.ts
11523
11610
  import { createHash as createHash2 } from "crypto";
@@ -11549,21 +11636,21 @@ function isRunworkManagedKey(key) {
11549
11636
  return key === RUNWORK_WORKSPACE_MCP_NAME || key.startsWith(RUNWORK_MCP_PREFIX) || key.startsWith(RUNWORK_MCP_PREFIX_LEGACY);
11550
11637
  }
11551
11638
  function readJsonConfig(filePath) {
11552
- if (!existsSync25(filePath))
11639
+ if (!existsSync27(filePath))
11553
11640
  return {};
11554
11641
  try {
11555
- return JSON.parse(readFileSync22(filePath, "utf-8"));
11642
+ return JSON.parse(readFileSync23(filePath, "utf-8"));
11556
11643
  } catch {
11557
11644
  return {};
11558
11645
  }
11559
11646
  }
11560
11647
  function writeJsonConfig(filePath, config) {
11561
- mkdirSync13(dirname5(filePath), { recursive: true });
11648
+ mkdirSync14(dirname7(filePath), { recursive: true });
11562
11649
  writeFileSync14(filePath, JSON.stringify(config, null, 2) + `
11563
11650
  `);
11564
11651
  }
11565
11652
  function removeRunworkMcpServers(filePath, topKey) {
11566
- if (!existsSync25(filePath))
11653
+ if (!existsSync27(filePath))
11567
11654
  return false;
11568
11655
  const config = readJsonConfig(filePath);
11569
11656
  const existing = config[topKey] || {};
@@ -11602,18 +11689,46 @@ function mergeJsonMcpServers(filePath, servers, topKey) {
11602
11689
  return true;
11603
11690
  }
11604
11691
 
11692
+ // src/agents/utils/skill-removal.ts
11693
+ import { existsSync as existsSync28, readdirSync as readdirSync6 } from "fs";
11694
+ import { join as join22 } from "path";
11695
+ function removeMatchingSkillDirs(dir, allowed, reason = "skill removed") {
11696
+ if (!allowed.size || !existsSync28(dir))
11697
+ return;
11698
+ for (const entry of readdirSync6(dir)) {
11699
+ if (!allowed.has(entry))
11700
+ continue;
11701
+ moveToTrash(join22(dir, entry), reason);
11702
+ }
11703
+ }
11704
+ function removeMatchingSkillFiles(dir, allowed, suffix, reason = "skill removed") {
11705
+ if (!allowed.size || !existsSync28(dir))
11706
+ return;
11707
+ const names = new Set([...allowed].map((slug) => `${slug}${suffix}`));
11708
+ for (const entry of readdirSync6(dir)) {
11709
+ if (!names.has(entry))
11710
+ continue;
11711
+ moveToTrash(join22(dir, entry), reason);
11712
+ }
11713
+ }
11714
+
11605
11715
  // src/agents/utils/instruction-hint.ts
11606
- import { existsSync as existsSync26, readFileSync as readFileSync23, writeFileSync as writeFileSync15, mkdirSync as mkdirSync14 } from "fs";
11607
- import { dirname as dirname6 } from "path";
11716
+ import { existsSync as existsSync29, readFileSync as readFileSync24, writeFileSync as writeFileSync15, mkdirSync as mkdirSync15 } from "fs";
11717
+ import { dirname as dirname8 } from "path";
11608
11718
  var START_MARKER = "<!-- runwork:start -->";
11609
11719
  var END_MARKER = "<!-- runwork:end -->";
11610
11720
  var TEAM_START_MARKER = "<!-- runwork-team:start -->";
11611
11721
  var TEAM_END_MARKER = "<!-- runwork-team:end -->";
11612
11722
  function writeHintToFile(filePath, hint) {
11613
- mkdirSync14(dirname6(filePath), { recursive: true });
11723
+ mkdirSync15(dirname8(filePath), { recursive: true });
11724
+ if (!hint.includes(START_MARKER) || !hint.includes(END_MARKER)) {
11725
+ hint = `${START_MARKER}
11726
+ ${hint}
11727
+ ${END_MARKER}`;
11728
+ }
11614
11729
  let content = "";
11615
- if (existsSync26(filePath)) {
11616
- content = readFileSync23(filePath, "utf-8");
11730
+ if (existsSync29(filePath)) {
11731
+ content = readFileSync24(filePath, "utf-8");
11617
11732
  }
11618
11733
  const startIdx = content.indexOf(START_MARKER);
11619
11734
  const endIdx = content.indexOf(END_MARKER);
@@ -11635,9 +11750,9 @@ function writeHintToFile(filePath, hint) {
11635
11750
  writeFileSync15(filePath, content);
11636
11751
  }
11637
11752
  function removeHintFromFile(filePath) {
11638
- if (!existsSync26(filePath))
11753
+ if (!existsSync29(filePath))
11639
11754
  return false;
11640
- let content = readFileSync23(filePath, "utf-8");
11755
+ let content = readFileSync24(filePath, "utf-8");
11641
11756
  const startIdx = content.indexOf(START_MARKER);
11642
11757
  const endIdx = content.indexOf(END_MARKER);
11643
11758
  if (startIdx < 0 || endIdx < 0)
@@ -11655,9 +11770,9 @@ function removeHintFromFile(filePath) {
11655
11770
  return true;
11656
11771
  }
11657
11772
  function removeTeamInstructionsFromFile(filePath) {
11658
- if (!existsSync26(filePath))
11773
+ if (!existsSync29(filePath))
11659
11774
  return false;
11660
- let content = readFileSync23(filePath, "utf-8");
11775
+ let content = readFileSync24(filePath, "utf-8");
11661
11776
  const startIdx = content.indexOf(TEAM_START_MARKER);
11662
11777
  const endIdx = content.indexOf(TEAM_END_MARKER);
11663
11778
  if (startIdx < 0 || endIdx < 0)
@@ -11675,10 +11790,10 @@ function removeTeamInstructionsFromFile(filePath) {
11675
11790
  return true;
11676
11791
  }
11677
11792
  function writeTeamInstructionsToFile(filePath, instructions) {
11678
- mkdirSync14(dirname6(filePath), { recursive: true });
11793
+ mkdirSync15(dirname8(filePath), { recursive: true });
11679
11794
  let content = "";
11680
- if (existsSync26(filePath)) {
11681
- content = readFileSync23(filePath, "utf-8");
11795
+ if (existsSync29(filePath)) {
11796
+ content = readFileSync24(filePath, "utf-8");
11682
11797
  }
11683
11798
  const block = `${TEAM_START_MARKER}
11684
11799
  ${instructions}
@@ -11985,7 +12100,7 @@ class ClaudeCodeAdapter {
11985
12100
  if (whichBinary("claude"))
11986
12101
  return true;
11987
12102
  if (platform2() === "darwin")
11988
- return existsSync27("/Applications/Claude.app");
12103
+ return existsSync30("/Applications/Claude.app");
11989
12104
  return false;
11990
12105
  }
11991
12106
  supportsMcpScope(_scope) {
@@ -11996,7 +12111,7 @@ class ClaudeCodeAdapter {
11996
12111
  }
11997
12112
  async writeMcpServers(servers, scope) {
11998
12113
  if (scope === "project") {
11999
- const filePath = join21(process.cwd(), ".mcp.json");
12114
+ const filePath = join23(process.cwd(), ".mcp.json");
12000
12115
  const entries = {};
12001
12116
  for (const s of servers) {
12002
12117
  entries[s.name] = {
@@ -12008,7 +12123,7 @@ class ClaudeCodeAdapter {
12008
12123
  }
12009
12124
  mergeJsonMcpServers(filePath, entries, "mcpServers");
12010
12125
  } else {
12011
- const settingsPath = join21(homedir5(), ".claude", "settings.json");
12126
+ const settingsPath = join23(homedir6(), ".claude", "settings.json");
12012
12127
  const entries = {};
12013
12128
  for (const s of servers) {
12014
12129
  entries[s.name] = {
@@ -12020,11 +12135,11 @@ class ClaudeCodeAdapter {
12020
12135
  }
12021
12136
  mergeJsonMcpServers(settingsPath, entries, "mcpServers");
12022
12137
  const pluginDir = this.getPluginDir();
12023
- mkdirSync15(pluginDir, { recursive: true });
12024
- const pluginMcpPath = join21(pluginDir, ".mcp.json");
12138
+ mkdirSync16(pluginDir, { recursive: true });
12139
+ const pluginMcpPath = join23(pluginDir, ".mcp.json");
12025
12140
  const marketDir = this.getMarketplaceDir();
12026
- mkdirSync15(marketDir, { recursive: true });
12027
- const marketplaceMcpPath = join21(marketDir, ".mcp.json");
12141
+ mkdirSync16(marketDir, { recursive: true });
12142
+ const marketplaceMcpPath = join23(marketDir, ".mcp.json");
12028
12143
  const pluginEntries = {};
12029
12144
  for (const s of servers) {
12030
12145
  pluginEntries[s.name] = {
@@ -12039,44 +12154,44 @@ class ClaudeCodeAdapter {
12039
12154
  }
12040
12155
  }
12041
12156
  installSessionStartHook(pluginDir, label) {
12042
- const hooksDir = join21(pluginDir, "hooks");
12043
- mkdirSync15(hooksDir, { recursive: true });
12044
- const scriptPath = join21(hooksDir, "on-session-start.sh");
12157
+ const hooksDir = join23(pluginDir, "hooks");
12158
+ mkdirSync16(hooksDir, { recursive: true });
12159
+ const scriptPath = join23(hooksDir, "on-session-start.sh");
12045
12160
  writeFileSync16(scriptPath, SESSION_START_HOOK_SCRIPT);
12046
12161
  try {
12047
12162
  chmodSync(scriptPath, 493);
12048
12163
  } catch {}
12049
- writeFileSync16(join21(hooksDir, "hooks.json"), JSON.stringify(SESSION_START_HOOKS_MANIFEST, null, 2));
12164
+ writeFileSync16(join23(hooksDir, "hooks.json"), JSON.stringify(SESSION_START_HOOKS_MANIFEST, null, 2));
12050
12165
  vlog(` [Claude Code] Installed SessionStart hook (${label}) -> ${scriptPath}`);
12051
12166
  }
12052
12167
  async writeSkills(skills, scope) {
12053
- const baseDir = scope === "project" ? join21(process.cwd(), ".claude", "skills") : join21(homedir5(), ".claude", "skills");
12168
+ const baseDir = scope === "project" ? join23(process.cwd(), ".claude", "skills") : join23(homedir6(), ".claude", "skills");
12054
12169
  for (const skill of skills) {
12055
- const skillDir = join21(baseDir, skill.filename);
12170
+ const skillDir = join23(baseDir, skill.filename);
12056
12171
  cleanupOldSkillDir(baseDir, skill);
12057
- mkdirSync15(skillDir, { recursive: true });
12058
- writeFileSync16(join21(skillDir, "SKILL.md"), buildSkillMd2(skill));
12172
+ mkdirSync16(skillDir, { recursive: true });
12173
+ writeFileSync16(join23(skillDir, "SKILL.md"), buildSkillMd2(skill));
12059
12174
  }
12060
12175
  if (scope === "user") {
12061
12176
  const pluginDir = this.getPluginDir();
12062
- const pluginJsonDir = join21(pluginDir, ".claude-plugin");
12063
- mkdirSync15(pluginJsonDir, { recursive: true });
12064
- writeFileSync16(join21(pluginJsonDir, "plugin.json"), JSON.stringify(getPluginJson(), null, 2));
12177
+ const pluginJsonDir = join23(pluginDir, ".claude-plugin");
12178
+ mkdirSync16(pluginJsonDir, { recursive: true });
12179
+ writeFileSync16(join23(pluginJsonDir, "plugin.json"), JSON.stringify(getPluginJson(), null, 2));
12065
12180
  for (const skill of skills) {
12066
- const skillDir = join21(pluginDir, "skills", skill.filename);
12067
- cleanupOldSkillDir(join21(pluginDir, "skills"), skill);
12068
- mkdirSync15(skillDir, { recursive: true });
12069
- writeFileSync16(join21(skillDir, "SKILL.md"), buildSkillMd2(skill));
12181
+ const skillDir = join23(pluginDir, "skills", skill.filename);
12182
+ cleanupOldSkillDir(join23(pluginDir, "skills"), skill);
12183
+ mkdirSync16(skillDir, { recursive: true });
12184
+ writeFileSync16(join23(skillDir, "SKILL.md"), buildSkillMd2(skill));
12070
12185
  }
12071
12186
  const marketDir = this.getMarketplaceDir();
12072
- const marketPluginJsonDir = join21(marketDir, ".claude-plugin");
12073
- mkdirSync15(marketPluginJsonDir, { recursive: true });
12074
- writeFileSync16(join21(marketPluginJsonDir, "plugin.json"), JSON.stringify(getPluginJson(), null, 2));
12187
+ const marketPluginJsonDir = join23(marketDir, ".claude-plugin");
12188
+ mkdirSync16(marketPluginJsonDir, { recursive: true });
12189
+ writeFileSync16(join23(marketPluginJsonDir, "plugin.json"), JSON.stringify(getPluginJson(), null, 2));
12075
12190
  for (const skill of skills) {
12076
- const skillDir = join21(marketDir, "skills", skill.filename);
12077
- cleanupOldSkillDir(join21(marketDir, "skills"), skill);
12078
- mkdirSync15(skillDir, { recursive: true });
12079
- writeFileSync16(join21(skillDir, "SKILL.md"), buildSkillMd2(skill));
12191
+ const skillDir = join23(marketDir, "skills", skill.filename);
12192
+ cleanupOldSkillDir(join23(marketDir, "skills"), skill);
12193
+ mkdirSync16(skillDir, { recursive: true });
12194
+ writeFileSync16(join23(skillDir, "SKILL.md"), buildSkillMd2(skill));
12080
12195
  }
12081
12196
  this.registerPlugin(pluginDir);
12082
12197
  }
@@ -12087,19 +12202,19 @@ class ClaudeCodeAdapter {
12087
12202
  return;
12088
12203
  const pluginDir = this.getPluginDir();
12089
12204
  const marketDir = this.getMarketplaceDir();
12090
- if (existsSync27(pluginDir)) {
12205
+ if (existsSync30(pluginDir)) {
12091
12206
  this.installSessionStartHook(pluginDir, "plugin cache");
12092
12207
  }
12093
- if (existsSync27(marketDir)) {
12208
+ if (existsSync30(marketDir)) {
12094
12209
  this.installSessionStartHook(marketDir, "marketplace");
12095
12210
  }
12096
12211
  }
12097
12212
  async writeInstructionHint(hint, scope) {
12098
- const filePath = scope === "project" ? join21(process.cwd(), ".claude", "CLAUDE.md") : join21(homedir5(), ".claude", "CLAUDE.md");
12213
+ const filePath = scope === "project" ? join23(process.cwd(), ".claude", "CLAUDE.md") : join23(homedir6(), ".claude", "CLAUDE.md");
12099
12214
  writeHintToFile(filePath, hint);
12100
12215
  }
12101
12216
  async writeTeamInstructions(instructions, scope) {
12102
- const filePath = scope === "project" ? join21(process.cwd(), ".claude", "CLAUDE.md") : join21(homedir5(), ".claude", "CLAUDE.md");
12217
+ const filePath = scope === "project" ? join23(process.cwd(), ".claude", "CLAUDE.md") : join23(homedir6(), ".claude", "CLAUDE.md");
12103
12218
  writeTeamInstructionsToFile(filePath, instructions);
12104
12219
  if (scope === "user") {
12105
12220
  const skillContent = `---
@@ -12111,21 +12226,21 @@ ${instructions}`;
12111
12226
  const pluginDir = this.getPluginDir();
12112
12227
  const marketDir = this.getMarketplaceDir();
12113
12228
  for (const dir of [
12114
- join21(pluginDir, "skills", "runwork-team-instructions"),
12115
- join21(marketDir, "skills", "runwork-team-instructions")
12229
+ join23(pluginDir, "skills", "runwork-team-instructions"),
12230
+ join23(marketDir, "skills", "runwork-team-instructions")
12116
12231
  ]) {
12117
- mkdirSync15(dir, { recursive: true });
12118
- writeFileSync16(join21(dir, "SKILL.md"), skillContent);
12232
+ mkdirSync16(dir, { recursive: true });
12233
+ writeFileSync16(join23(dir, "SKILL.md"), skillContent);
12119
12234
  }
12120
12235
  }
12121
12236
  }
12122
12237
  async writeAgentConfig(config, scope, baseline) {
12123
- const settingsPath = scope === "project" ? join21(process.cwd(), ".claude", "settings.json") : join21(homedir5(), ".claude", "settings.json");
12124
- const hadFile = existsSync27(settingsPath);
12238
+ const settingsPath = scope === "project" ? join23(process.cwd(), ".claude", "settings.json") : join23(homedir6(), ".claude", "settings.json");
12239
+ const hadFile = existsSync30(settingsPath);
12125
12240
  let settings = {};
12126
12241
  if (hadFile) {
12127
12242
  try {
12128
- settings = JSON.parse(readFileSync24(settingsPath, "utf-8"));
12243
+ settings = JSON.parse(readFileSync25(settingsPath, "utf-8"));
12129
12244
  } catch {}
12130
12245
  }
12131
12246
  if (settings.permissions && typeof settings.permissions === "object") {
@@ -12155,37 +12270,39 @@ ${instructions}`;
12155
12270
  }
12156
12271
  if (!hadFile && !config.modelPreference && !config.permissionRules)
12157
12272
  return;
12158
- mkdirSync15(join21(settingsPath, ".."), { recursive: true });
12273
+ mkdirSync16(join23(settingsPath, ".."), { recursive: true });
12159
12274
  writeFileSync16(settingsPath, JSON.stringify(settings, null, 2));
12160
12275
  }
12161
12276
  async readManagedBlock(_scope) {
12162
12277
  return;
12163
12278
  }
12279
+ async removeSkills(skillFilenames, scope) {
12280
+ if (!skillFilenames.length)
12281
+ return;
12282
+ const allowed = new Set(skillFilenames);
12283
+ const roots = scope === "project" ? [join23(process.cwd(), ".claude", "skills")] : [
12284
+ join23(homedir6(), ".claude", "skills"),
12285
+ join23(this.getPluginDir(), "skills"),
12286
+ join23(this.getMarketplaceDir(), "skills")
12287
+ ];
12288
+ for (const dir of roots)
12289
+ removeMatchingSkillDirs(dir, allowed);
12290
+ }
12164
12291
  async cleanup(scope, manifest) {
12165
12292
  if (scope === "project") {
12166
- removeRunworkMcpServers(join21(process.cwd(), ".mcp.json"), "mcpServers");
12293
+ removeRunworkMcpServers(join23(process.cwd(), ".mcp.json"), "mcpServers");
12167
12294
  } else {
12168
- removeRunworkMcpServers(join21(homedir5(), ".claude", "settings.json"), "mcpServers");
12169
- }
12170
- const skillsDir = scope === "project" ? join21(process.cwd(), ".claude", "skills") : join21(homedir5(), ".claude", "skills");
12171
- if (existsSync27(skillsDir) && manifest?.skillFilenames.length) {
12172
- const allowed = new Set(manifest.skillFilenames);
12173
- for (const entry of readdirSync5(skillsDir)) {
12174
- if (!allowed.has(entry))
12175
- continue;
12176
- try {
12177
- rmSync4(join21(skillsDir, entry), { recursive: true, force: true });
12178
- } catch {}
12179
- }
12295
+ removeRunworkMcpServers(join23(homedir6(), ".claude", "settings.json"), "mcpServers");
12180
12296
  }
12181
- const instructionFile = scope === "project" ? join21(process.cwd(), ".claude", "CLAUDE.md") : join21(homedir5(), ".claude", "CLAUDE.md");
12297
+ await this.removeSkills(manifest?.skillFilenames ?? [], scope);
12298
+ const instructionFile = scope === "project" ? join23(process.cwd(), ".claude", "CLAUDE.md") : join23(homedir6(), ".claude", "CLAUDE.md");
12182
12299
  removeHintFromFile(instructionFile);
12183
12300
  removeTeamInstructionsFromFile(instructionFile);
12184
12301
  if (scope === "user") {
12185
- const settingsPath = join21(homedir5(), ".claude", "settings.json");
12186
- if (existsSync27(settingsPath)) {
12302
+ const settingsPath = join23(homedir6(), ".claude", "settings.json");
12303
+ if (existsSync30(settingsPath)) {
12187
12304
  try {
12188
- const settings = JSON.parse(readFileSync24(settingsPath, "utf-8"));
12305
+ const settings = JSON.parse(readFileSync25(settingsPath, "utf-8"));
12189
12306
  if (settings.permissions) {
12190
12307
  for (const key of ["allow", "deny"]) {
12191
12308
  const arr = settings.permissions[key];
@@ -12196,7 +12313,10 @@ ${instructions}`;
12196
12313
  }
12197
12314
  }
12198
12315
  if (settings.enabledPlugins) {
12199
- delete settings.enabledPlugins[`${PLUGIN_NAME}@runwork`];
12316
+ for (const key of Object.keys(settings.enabledPlugins)) {
12317
+ if (isRunworkPluginKey(key))
12318
+ delete settings.enabledPlugins[key];
12319
+ }
12200
12320
  }
12201
12321
  writeFileSync16(settingsPath, JSON.stringify(settings, null, 2));
12202
12322
  } catch {}
@@ -12204,25 +12324,28 @@ ${instructions}`;
12204
12324
  const pluginDir = this.getPluginDir();
12205
12325
  const marketRoot = this.getMarketplaceRoot();
12206
12326
  for (const dir of [pluginDir, marketRoot]) {
12207
- if (existsSync27(dir)) {
12327
+ if (existsSync30(dir)) {
12208
12328
  try {
12209
- rmSync4(dir, { recursive: true, force: true });
12329
+ rmSync5(dir, { recursive: true, force: true });
12210
12330
  } catch {}
12211
12331
  }
12212
12332
  }
12213
12333
  const pluginsBase = this.getPluginsBaseDir();
12214
- const installedPath = join21(pluginsBase, "installed_plugins.json");
12215
- if (existsSync27(installedPath)) {
12334
+ const installedPath = join23(pluginsBase, "installed_plugins.json");
12335
+ if (existsSync30(installedPath)) {
12216
12336
  try {
12217
12337
  const installed = readJsonConfig(installedPath);
12218
12338
  if (installed.plugins) {
12219
- delete installed.plugins[`${PLUGIN_NAME}@runwork`];
12339
+ for (const key of Object.keys(installed.plugins)) {
12340
+ if (isRunworkPluginKey(key))
12341
+ delete installed.plugins[key];
12342
+ }
12220
12343
  writeJsonConfig(installedPath, installed);
12221
12344
  }
12222
12345
  } catch {}
12223
12346
  }
12224
- const marketplacesPath = join21(pluginsBase, "known_marketplaces.json");
12225
- if (existsSync27(marketplacesPath)) {
12347
+ const marketplacesPath = join23(pluginsBase, "known_marketplaces.json");
12348
+ if (existsSync30(marketplacesPath)) {
12226
12349
  try {
12227
12350
  const marketplaces = readJsonConfig(marketplacesPath);
12228
12351
  delete marketplaces["runwork"];
@@ -12233,11 +12356,11 @@ ${instructions}`;
12233
12356
  }
12234
12357
  async readUsageStats(lastSyncAt) {
12235
12358
  try {
12236
- const claudeDir = join21(homedir5(), ".claude");
12237
- if (!existsSync27(claudeDir))
12359
+ const claudeDir = join23(homedir6(), ".claude");
12360
+ if (!existsSync30(claudeDir))
12238
12361
  return null;
12239
- const projectsDir = join21(claudeDir, "projects");
12240
- if (!existsSync27(projectsDir))
12362
+ const projectsDir = join23(claudeDir, "projects");
12363
+ if (!existsSync30(projectsDir))
12241
12364
  return null;
12242
12365
  const sinceMs = lastSyncAt ? new Date(lastSyncAt).getTime() : 0;
12243
12366
  let sessionCount = 0;
@@ -12251,25 +12374,25 @@ ${instructions}`;
12251
12374
  const seenEntries = new Set;
12252
12375
  let cwdEntries;
12253
12376
  try {
12254
- cwdEntries = readdirSync5(projectsDir);
12377
+ cwdEntries = readdirSync7(projectsDir);
12255
12378
  } catch {
12256
12379
  return null;
12257
12380
  }
12258
12381
  for (const cwd of cwdEntries) {
12259
- const cwdPath = join21(projectsDir, cwd);
12382
+ const cwdPath = join23(projectsDir, cwd);
12260
12383
  let files;
12261
12384
  try {
12262
- files = readdirSync5(cwdPath);
12385
+ files = readdirSync7(cwdPath);
12263
12386
  } catch {
12264
12387
  continue;
12265
12388
  }
12266
12389
  for (const file of files) {
12267
12390
  if (!file.endsWith(".jsonl"))
12268
12391
  continue;
12269
- const filePath = join21(cwdPath, file);
12392
+ const filePath = join23(cwdPath, file);
12270
12393
  let stat;
12271
12394
  try {
12272
- stat = statSync3(filePath);
12395
+ stat = statSync4(filePath);
12273
12396
  } catch {
12274
12397
  continue;
12275
12398
  }
@@ -12277,7 +12400,7 @@ ${instructions}`;
12277
12400
  continue;
12278
12401
  let content;
12279
12402
  try {
12280
- content = readFileSync24(filePath, "utf-8");
12403
+ content = readFileSync25(filePath, "utf-8");
12281
12404
  } catch {
12282
12405
  continue;
12283
12406
  }
@@ -12363,32 +12486,32 @@ ${instructions}`;
12363
12486
  }
12364
12487
  async readSessionDigests(sinceISO) {
12365
12488
  try {
12366
- const projectsDir = join21(homedir5(), ".claude", "projects");
12367
- if (!existsSync27(projectsDir))
12489
+ const projectsDir = join23(homedir6(), ".claude", "projects");
12490
+ if (!existsSync30(projectsDir))
12368
12491
  return null;
12369
12492
  const sinceMs = sinceISO ? new Date(sinceISO).getTime() : 0;
12370
12493
  let cwdEntries;
12371
12494
  try {
12372
- cwdEntries = readdirSync5(projectsDir);
12495
+ cwdEntries = readdirSync7(projectsDir);
12373
12496
  } catch {
12374
12497
  return null;
12375
12498
  }
12376
12499
  const digests = [];
12377
12500
  for (const cwd of cwdEntries) {
12378
- const cwdPath = join21(projectsDir, cwd);
12501
+ const cwdPath = join23(projectsDir, cwd);
12379
12502
  let files;
12380
12503
  try {
12381
- files = readdirSync5(cwdPath);
12504
+ files = readdirSync7(cwdPath);
12382
12505
  } catch {
12383
12506
  continue;
12384
12507
  }
12385
12508
  for (const file of files) {
12386
12509
  if (!file.endsWith(".jsonl"))
12387
12510
  continue;
12388
- const filePath = join21(cwdPath, file);
12511
+ const filePath = join23(cwdPath, file);
12389
12512
  let stat;
12390
12513
  try {
12391
- stat = statSync3(filePath);
12514
+ stat = statSync4(filePath);
12392
12515
  } catch {
12393
12516
  continue;
12394
12517
  }
@@ -12396,7 +12519,7 @@ ${instructions}`;
12396
12519
  continue;
12397
12520
  let content;
12398
12521
  try {
12399
- content = readFileSync24(filePath, "utf-8");
12522
+ content = readFileSync25(filePath, "utf-8");
12400
12523
  } catch {
12401
12524
  continue;
12402
12525
  }
@@ -12412,8 +12535,8 @@ ${instructions}`;
12412
12535
  }
12413
12536
  async readSkillUsage(lastSyncAt) {
12414
12537
  try {
12415
- const projectsDir = join21(homedir5(), ".claude", "projects");
12416
- if (!existsSync27(projectsDir))
12538
+ const projectsDir = join23(homedir6(), ".claude", "projects");
12539
+ if (!existsSync30(projectsDir))
12417
12540
  return null;
12418
12541
  const sinceMs = lastSyncAt ? new Date(lastSyncAt).getTime() : 0;
12419
12542
  const skillCounts = new Map;
@@ -12429,25 +12552,25 @@ ${instructions}`;
12429
12552
  };
12430
12553
  let cwdEntries;
12431
12554
  try {
12432
- cwdEntries = readdirSync5(projectsDir);
12555
+ cwdEntries = readdirSync7(projectsDir);
12433
12556
  } catch {
12434
12557
  return null;
12435
12558
  }
12436
12559
  for (const cwd of cwdEntries) {
12437
- const cwdPath = join21(projectsDir, cwd);
12560
+ const cwdPath = join23(projectsDir, cwd);
12438
12561
  let files;
12439
12562
  try {
12440
- files = readdirSync5(cwdPath);
12563
+ files = readdirSync7(cwdPath);
12441
12564
  } catch {
12442
12565
  continue;
12443
12566
  }
12444
12567
  for (const file of files) {
12445
12568
  if (!file.endsWith(".jsonl"))
12446
12569
  continue;
12447
- const filePath = join21(cwdPath, file);
12570
+ const filePath = join23(cwdPath, file);
12448
12571
  let fileStat;
12449
12572
  try {
12450
- fileStat = statSync3(filePath);
12573
+ fileStat = statSync4(filePath);
12451
12574
  } catch {
12452
12575
  continue;
12453
12576
  }
@@ -12455,7 +12578,7 @@ ${instructions}`;
12455
12578
  continue;
12456
12579
  let content;
12457
12580
  try {
12458
- content = readFileSync24(filePath, "utf-8");
12581
+ content = readFileSync25(filePath, "utf-8");
12459
12582
  } catch {
12460
12583
  continue;
12461
12584
  }
@@ -12545,21 +12668,21 @@ ${instructions}`;
12545
12668
  }
12546
12669
  }
12547
12670
  getPluginsBaseDir() {
12548
- return join21(homedir5(), ".claude", "plugins");
12671
+ return join23(homedir6(), ".claude", "plugins");
12549
12672
  }
12550
12673
  getPluginDir() {
12551
- return join21(this.getPluginsBaseDir(), "cache", "runwork", PLUGIN_NAME, PLUGIN_VERSION);
12674
+ return join23(this.getPluginsBaseDir(), "cache", "runwork", PLUGIN_NAME, PLUGIN_VERSION);
12552
12675
  }
12553
12676
  getMarketplaceRoot() {
12554
- return join21(this.getPluginsBaseDir(), "marketplaces", "runwork");
12677
+ return join23(this.getPluginsBaseDir(), "marketplaces", "runwork");
12555
12678
  }
12556
12679
  getMarketplaceDir() {
12557
- return join21(this.getMarketplaceRoot(), "plugins", PLUGIN_NAME);
12680
+ return join23(this.getMarketplaceRoot(), "plugins", PLUGIN_NAME);
12558
12681
  }
12559
12682
  registerPlugin(pluginDir) {
12560
12683
  const pluginsBase = this.getPluginsBaseDir();
12561
- mkdirSync15(pluginsBase, { recursive: true });
12562
- const installedPath = join21(pluginsBase, "installed_plugins.json");
12684
+ mkdirSync16(pluginsBase, { recursive: true });
12685
+ const installedPath = join23(pluginsBase, "installed_plugins.json");
12563
12686
  const installed = readJsonConfig(installedPath);
12564
12687
  if (!installed.version)
12565
12688
  installed.version = 2;
@@ -12576,10 +12699,10 @@ ${instructions}`;
12576
12699
  }];
12577
12700
  writeJsonConfig(installedPath, installed);
12578
12701
  const marketRoot = this.getMarketplaceRoot();
12579
- const marketCatalogDir = join21(marketRoot, ".claude-plugin");
12580
- mkdirSync15(marketCatalogDir, { recursive: true });
12581
- writeFileSync16(join21(marketCatalogDir, "marketplace.json"), JSON.stringify(getMarketplaceJson(), null, 2));
12582
- const marketplacesPath = join21(pluginsBase, "known_marketplaces.json");
12702
+ const marketCatalogDir = join23(marketRoot, ".claude-plugin");
12703
+ mkdirSync16(marketCatalogDir, { recursive: true });
12704
+ writeFileSync16(join23(marketCatalogDir, "marketplace.json"), JSON.stringify(getMarketplaceJson(), null, 2));
12705
+ const marketplacesPath = join23(pluginsBase, "known_marketplaces.json");
12583
12706
  const marketplaces = readJsonConfig(marketplacesPath);
12584
12707
  marketplaces["runwork"] = {
12585
12708
  source: { source: "directory", path: marketRoot },
@@ -12588,7 +12711,7 @@ ${instructions}`;
12588
12711
  };
12589
12712
  writeJsonConfig(marketplacesPath, marketplaces);
12590
12713
  vlog(` [Claude Code] ${wasRegistered ? "Refreshed" : "Registered"} plugin ` + `${pluginKey} v${PLUGIN_VERSION} -> ${installedPath}`);
12591
- const settingsPath = join21(homedir5(), ".claude", "settings.json");
12714
+ const settingsPath = join23(homedir6(), ".claude", "settings.json");
12592
12715
  const settings = readJsonConfig(settingsPath);
12593
12716
  if (!settings["enabledPlugins"]) {
12594
12717
  settings["enabledPlugins"] = {};
@@ -12603,27 +12726,23 @@ ${instructions}`;
12603
12726
  function cleanupOldSkillDir(baseDir, skill) {
12604
12727
  if (skill.name === skill.filename)
12605
12728
  return;
12606
- const oldDir = join21(baseDir, skill.name);
12607
- if (existsSync27(oldDir)) {
12608
- try {
12609
- rmSync4(oldDir, { recursive: true, force: true });
12610
- } catch {}
12611
- }
12729
+ const oldDir = join23(baseDir, skill.name);
12730
+ moveToTrash(oldDir, `skill renamed to ${skill.filename}`);
12612
12731
  }
12613
12732
 
12614
12733
  // src/agents/claude-desktop.ts
12615
- import { existsSync as existsSync29, mkdtempSync as mkdtempSync3, readdirSync as readdirSync6, readFileSync as readFileSync25, rmSync as rmSync6, statSync as statSync4, writeFileSync as writeFileSync18, mkdirSync as mkdirSync17 } from "fs";
12616
- import { dirname as dirname7, join as join23 } from "path";
12617
- import { homedir as homedir6, platform as platform3, tmpdir as tmpdir3 } from "os";
12734
+ import { existsSync as existsSync32, mkdtempSync as mkdtempSync3, readdirSync as readdirSync8, readFileSync as readFileSync26, rmSync as rmSync7, statSync as statSync5, writeFileSync as writeFileSync18, mkdirSync as mkdirSync18 } from "fs";
12735
+ import { dirname as dirname9, join as join25 } from "path";
12736
+ import { homedir as homedir7, platform as platform3, tmpdir as tmpdir3 } from "os";
12618
12737
  init_zip();
12619
12738
 
12620
12739
  // src/agents/claude-desktop-plugin-tree.ts
12621
- import { chmodSync as chmodSync2, mkdirSync as mkdirSync16, rmSync as rmSync5, existsSync as existsSync28, writeFileSync as writeFileSync17 } from "fs";
12622
- import { join as join22 } from "path";
12740
+ import { chmodSync as chmodSync2, mkdirSync as mkdirSync17, rmSync as rmSync6, existsSync as existsSync31, writeFileSync as writeFileSync17 } from "fs";
12741
+ import { join as join24 } from "path";
12623
12742
  function writePluginMetadata(destDir, metadata) {
12624
- const pluginJsonDir = join22(destDir, ".claude-plugin");
12625
- mkdirSync16(pluginJsonDir, { recursive: true });
12626
- writeFileSync17(join22(pluginJsonDir, "plugin.json"), JSON.stringify({
12743
+ const pluginJsonDir = join24(destDir, ".claude-plugin");
12744
+ mkdirSync17(pluginJsonDir, { recursive: true });
12745
+ writeFileSync17(join24(pluginJsonDir, "plugin.json"), JSON.stringify({
12627
12746
  name: metadata.pluginName,
12628
12747
  version: metadata.pluginVersion,
12629
12748
  description: metadata.description,
@@ -12631,7 +12750,7 @@ function writePluginMetadata(destDir, metadata) {
12631
12750
  }, null, 2));
12632
12751
  }
12633
12752
  function writePluginMcpConfig(destDir, mcpServers) {
12634
- mkdirSync16(destDir, { recursive: true });
12753
+ mkdirSync17(destDir, { recursive: true });
12635
12754
  const mcpEntries = {};
12636
12755
  for (const server of mcpServers) {
12637
12756
  if (server.name === RUNWORK_WORKSPACE_MCP_NAME) {
@@ -12650,49 +12769,49 @@ function writePluginMcpConfig(destDir, mcpServers) {
12650
12769
  };
12651
12770
  }
12652
12771
  }
12653
- writeFileSync17(join22(destDir, ".mcp.json"), JSON.stringify({ mcpServers: mcpEntries }, null, 2));
12772
+ writeFileSync17(join24(destDir, ".mcp.json"), JSON.stringify({ mcpServers: mcpEntries }, null, 2));
12654
12773
  }
12655
12774
  function writePluginTeamInstructions(destDir, instructions) {
12656
- const skillDir = join22(destDir, "skills", "runwork-team-instructions");
12657
- mkdirSync16(skillDir, { recursive: true });
12775
+ const skillDir = join24(destDir, "skills", "runwork-team-instructions");
12776
+ mkdirSync17(skillDir, { recursive: true });
12658
12777
  const skillContent = `---
12659
12778
  name: runwork-team-instructions
12660
12779
  description: Team instructions from your Runwork workspace. Always follow these guidelines.
12661
12780
  ---
12662
12781
 
12663
12782
  ${instructions}`;
12664
- writeFileSync17(join22(skillDir, "SKILL.md"), skillContent);
12783
+ writeFileSync17(join24(skillDir, "SKILL.md"), skillContent);
12665
12784
  }
12666
12785
  function writePluginSkills(destDir, skills) {
12667
- const skillsRoot = join22(destDir, "skills");
12668
- mkdirSync16(skillsRoot, { recursive: true });
12786
+ const skillsRoot = join24(destDir, "skills");
12787
+ mkdirSync17(skillsRoot, { recursive: true });
12669
12788
  for (const skill of skills) {
12670
12789
  if (skill.name !== skill.filename) {
12671
- const legacyDir = join22(skillsRoot, skill.name);
12672
- if (existsSync28(legacyDir)) {
12790
+ const legacyDir = join24(skillsRoot, skill.name);
12791
+ if (existsSync31(legacyDir)) {
12673
12792
  try {
12674
- rmSync5(legacyDir, { recursive: true, force: true });
12793
+ rmSync6(legacyDir, { recursive: true, force: true });
12675
12794
  } catch {}
12676
12795
  }
12677
12796
  }
12678
- const skillDir = join22(skillsRoot, skill.filename);
12679
- mkdirSync16(skillDir, { recursive: true });
12680
- writeFileSync17(join22(skillDir, "SKILL.md"), buildSkillMd2(skill));
12797
+ const skillDir = join24(skillsRoot, skill.filename);
12798
+ mkdirSync17(skillDir, { recursive: true });
12799
+ writeFileSync17(join24(skillDir, "SKILL.md"), buildSkillMd2(skill));
12681
12800
  }
12682
12801
  }
12683
12802
  function writePluginSessionStartHook(destDir) {
12684
- const hooksDir = join22(destDir, "hooks");
12685
- mkdirSync16(hooksDir, { recursive: true });
12686
- const scriptPath = join22(hooksDir, "on-session-start.sh");
12803
+ const hooksDir = join24(destDir, "hooks");
12804
+ mkdirSync17(hooksDir, { recursive: true });
12805
+ const scriptPath = join24(hooksDir, "on-session-start.sh");
12687
12806
  writeFileSync17(scriptPath, SESSION_START_HOOK_SCRIPT);
12688
12807
  try {
12689
12808
  chmodSync2(scriptPath, 493);
12690
12809
  } catch {}
12691
- writeFileSync17(join22(hooksDir, "hooks.json"), JSON.stringify(SESSION_START_HOOKS_MANIFEST, null, 2));
12810
+ writeFileSync17(join24(hooksDir, "hooks.json"), JSON.stringify(SESSION_START_HOOKS_MANIFEST, null, 2));
12692
12811
  console.log(` [Claude Desktop (Cowork)] Bundled SessionStart hook -> ${scriptPath}`);
12693
12812
  }
12694
12813
  function writePluginTree(destDir, input) {
12695
- mkdirSync16(destDir, { recursive: true });
12814
+ mkdirSync17(destDir, { recursive: true });
12696
12815
  writePluginMetadata(destDir, {
12697
12816
  pluginName: input.pluginName,
12698
12817
  pluginVersion: input.pluginVersion,
@@ -12709,6 +12828,9 @@ var PLUGIN_NAME2 = "runwork";
12709
12828
  var PLUGIN_VERSION2 = "1.0.0";
12710
12829
  var PLUGIN_DESCRIPTION = "Skills and tools from your Runwork workspace";
12711
12830
  var PLUGIN_AUTHOR_NAME = "Runwork";
12831
+ function isRunworkRpmPluginName(name) {
12832
+ return !!name && (name === PLUGIN_NAME2 || name.startsWith(`${PLUGIN_NAME2}-`));
12833
+ }
12712
12834
  function getPluginMetadata() {
12713
12835
  return {
12714
12836
  pluginName: PLUGIN_NAME2,
@@ -12720,39 +12842,59 @@ function getPluginMetadata() {
12720
12842
  function getMcpConfigPath() {
12721
12843
  const os2 = platform3();
12722
12844
  if (os2 === "darwin") {
12723
- return join23(homedir6(), "Library", "Application Support", "Claude", "claude_desktop_config.json");
12845
+ return join25(homedir7(), "Library", "Application Support", "Claude", "claude_desktop_config.json");
12724
12846
  }
12725
12847
  if (os2 === "win32") {
12726
- return join23(process.env.APPDATA || join23(homedir6(), "AppData", "Roaming"), "Claude", "claude_desktop_config.json");
12848
+ return join25(process.env.APPDATA || join25(homedir7(), "AppData", "Roaming"), "Claude", "claude_desktop_config.json");
12727
12849
  }
12728
- return join23(homedir6(), ".config", "Claude", "claude_desktop_config.json");
12850
+ return join25(homedir7(), ".config", "Claude", "claude_desktop_config.json");
12729
12851
  }
12730
12852
  function getCoworkBaseDir() {
12731
12853
  const os2 = platform3();
12732
12854
  if (os2 === "darwin") {
12733
- return join23(homedir6(), "Library", "Application Support", "Claude", "local-agent-mode-sessions");
12855
+ return join25(homedir7(), "Library", "Application Support", "Claude", "local-agent-mode-sessions");
12734
12856
  }
12735
12857
  if (os2 === "win32") {
12736
- return join23(process.env.APPDATA || join23(homedir6(), "AppData", "Roaming"), "Claude", "local-agent-mode-sessions");
12858
+ return join25(process.env.APPDATA || join25(homedir7(), "AppData", "Roaming"), "Claude", "local-agent-mode-sessions");
12737
12859
  }
12738
- return join23(homedir6(), ".config", "Claude", "local-agent-mode-sessions");
12860
+ return join25(homedir7(), ".config", "Claude", "local-agent-mode-sessions");
12861
+ }
12862
+ function listOrgDirs() {
12863
+ const baseDir = getCoworkBaseDir();
12864
+ if (!existsSync32(baseDir))
12865
+ return [];
12866
+ const dirs = [];
12867
+ try {
12868
+ const sessionDirs = readdirSync8(baseDir).filter((d) => !d.startsWith(".") && d !== "skills-plugin");
12869
+ for (const sessionId of sessionDirs) {
12870
+ const sessionPath = join25(baseDir, sessionId);
12871
+ try {
12872
+ for (const orgId of readdirSync8(sessionPath).filter((d) => !d.startsWith("."))) {
12873
+ dirs.push(join25(sessionPath, orgId));
12874
+ }
12875
+ } catch {
12876
+ continue;
12877
+ }
12878
+ }
12879
+ } catch {}
12880
+ return dirs;
12739
12881
  }
12740
12882
  function walkOrgDirs(visit) {
12741
12883
  const baseDir = getCoworkBaseDir();
12742
- if (!existsSync29(baseDir))
12884
+ if (!existsSync32(baseDir))
12743
12885
  return null;
12744
12886
  try {
12745
- const sessionDirs = readdirSync6(baseDir).filter((d) => !d.startsWith(".") && d !== "skills-plugin");
12887
+ const sessionDirs = readdirSync8(baseDir).filter((d) => !d.startsWith(".") && d !== "skills-plugin");
12746
12888
  for (const sessionId of sessionDirs) {
12747
- const sessionPath = join23(baseDir, sessionId);
12889
+ const sessionPath = join25(baseDir, sessionId);
12748
12890
  let orgDirs;
12749
12891
  try {
12750
- orgDirs = readdirSync6(sessionPath).filter((d) => !d.startsWith("."));
12892
+ orgDirs = readdirSync8(sessionPath).filter((d) => !d.startsWith("."));
12751
12893
  } catch {
12752
12894
  continue;
12753
12895
  }
12754
12896
  for (const orgId of orgDirs) {
12755
- const orgDir = join23(sessionPath, orgId);
12897
+ const orgDir = join25(sessionPath, orgId);
12756
12898
  const result = visit(orgDir);
12757
12899
  if (result !== null)
12758
12900
  return result;
@@ -12764,20 +12906,20 @@ function walkOrgDirs(visit) {
12764
12906
  function getCoworkMemoryClaudeMdPaths() {
12765
12907
  const paths = [];
12766
12908
  const baseDir = getCoworkBaseDir();
12767
- if (!existsSync29(baseDir))
12909
+ if (!existsSync32(baseDir))
12768
12910
  return paths;
12769
12911
  try {
12770
- const sessionDirs = readdirSync6(baseDir).filter((d) => !d.startsWith(".") && d !== "skills-plugin");
12912
+ const sessionDirs = readdirSync8(baseDir).filter((d) => !d.startsWith(".") && d !== "skills-plugin");
12771
12913
  for (const sessionId of sessionDirs) {
12772
- const sessionPath = join23(baseDir, sessionId);
12914
+ const sessionPath = join25(baseDir, sessionId);
12773
12915
  let orgDirs;
12774
12916
  try {
12775
- orgDirs = readdirSync6(sessionPath).filter((d) => !d.startsWith("."));
12917
+ orgDirs = readdirSync8(sessionPath).filter((d) => !d.startsWith("."));
12776
12918
  } catch {
12777
12919
  continue;
12778
12920
  }
12779
12921
  for (const orgId of orgDirs) {
12780
- paths.push(join23(sessionPath, orgId, "memory", "CLAUDE.md"));
12922
+ paths.push(join25(sessionPath, orgId, "memory", "CLAUDE.md"));
12781
12923
  }
12782
12924
  }
12783
12925
  } catch {}
@@ -12785,22 +12927,22 @@ function getCoworkMemoryClaudeMdPaths() {
12785
12927
  }
12786
12928
  function findCoworkPluginsDir() {
12787
12929
  return walkOrgDirs((orgDir) => {
12788
- const pluginsDir = join23(orgDir, "cowork_plugins");
12789
- return existsSync29(join23(pluginsDir, "installed_plugins.json")) ? pluginsDir : null;
12930
+ const pluginsDir = join25(orgDir, "cowork_plugins");
12931
+ return existsSync32(join25(pluginsDir, "installed_plugins.json")) ? pluginsDir : null;
12790
12932
  });
12791
12933
  }
12792
12934
  function findRpmPluginByName(pluginName) {
12793
12935
  return walkOrgDirs((orgDir) => {
12794
- const manifestPath = join23(orgDir, "rpm", "manifest.json");
12795
- if (!existsSync29(manifestPath))
12936
+ const manifestPath = join25(orgDir, "rpm", "manifest.json");
12937
+ if (!existsSync32(manifestPath))
12796
12938
  return null;
12797
12939
  try {
12798
- const manifest = JSON.parse(readFileSync25(manifestPath, "utf-8"));
12940
+ const manifest = JSON.parse(readFileSync26(manifestPath, "utf-8"));
12799
12941
  const entry = manifest.plugins?.find((p) => p.name === pluginName);
12800
12942
  if (!entry?.id)
12801
12943
  return null;
12802
12944
  return {
12803
- pluginPath: join23(orgDir, "rpm", entry.id),
12945
+ pluginPath: join25(orgDir, "rpm", entry.id),
12804
12946
  orgDir
12805
12947
  };
12806
12948
  } catch {
@@ -12822,7 +12964,7 @@ function getMarketplaceJson2() {
12822
12964
  };
12823
12965
  }
12824
12966
  function getCoworkSettingsPath(pluginsDir) {
12825
- return join23(dirname7(pluginsDir), "cowork_settings.json");
12967
+ return join25(dirname9(pluginsDir), "cowork_settings.json");
12826
12968
  }
12827
12969
  function setCoworkPluginEnabled(pluginsDir, enabled) {
12828
12970
  const settingsPath = getCoworkSettingsPath(pluginsDir);
@@ -12839,8 +12981,8 @@ class ClaudeDesktopAdapter {
12839
12981
  async detect() {
12840
12982
  const os2 = platform3();
12841
12983
  if (os2 === "darwin")
12842
- return existsSync29("/Applications/Claude.app");
12843
- return existsSync29(getMcpConfigPath());
12984
+ return existsSync32("/Applications/Claude.app");
12985
+ return existsSync32(getMcpConfigPath());
12844
12986
  }
12845
12987
  supportsMcpScope(scope) {
12846
12988
  return scope === "user";
@@ -12870,8 +13012,8 @@ class ClaudeDesktopAdapter {
12870
13012
  }
12871
13013
  const pluginsDir = findCoworkPluginsDir();
12872
13014
  if (pluginsDir) {
12873
- const cacheDir = join23(pluginsDir, "cache", "runwork", PLUGIN_NAME2, PLUGIN_VERSION2);
12874
- const marketDir = join23(pluginsDir, "marketplaces", "runwork", "plugins", PLUGIN_NAME2);
13015
+ const cacheDir = join25(pluginsDir, "cache", "runwork", PLUGIN_NAME2, PLUGIN_VERSION2);
13016
+ const marketDir = join25(pluginsDir, "marketplaces", "runwork", "plugins", PLUGIN_NAME2);
12875
13017
  for (const dir of [cacheDir, marketDir]) {
12876
13018
  writePluginMcpConfig(dir, servers);
12877
13019
  }
@@ -12889,14 +13031,14 @@ class ClaudeDesktopAdapter {
12889
13031
  console.warn(" [Claude Desktop] Cowork plugins directory not found. Open Claude Desktop at least once first.");
12890
13032
  return 0;
12891
13033
  }
12892
- const cacheDir = join23(pluginsDir, "cache", "runwork", PLUGIN_NAME2, PLUGIN_VERSION2);
12893
- const marketRoot = join23(pluginsDir, "marketplaces", "runwork");
12894
- const marketDir = join23(marketRoot, "plugins", PLUGIN_NAME2);
13034
+ const cacheDir = join25(pluginsDir, "cache", "runwork", PLUGIN_NAME2, PLUGIN_VERSION2);
13035
+ const marketRoot = join25(pluginsDir, "marketplaces", "runwork");
13036
+ const marketDir = join25(marketRoot, "plugins", PLUGIN_NAME2);
12895
13037
  for (const dir of [cacheDir, marketDir]) {
12896
13038
  writePluginMetadata(dir, getPluginMetadata());
12897
13039
  writePluginSkills(dir, skills);
12898
13040
  }
12899
- const installedPath = join23(pluginsDir, "installed_plugins.json");
13041
+ const installedPath = join25(pluginsDir, "installed_plugins.json");
12900
13042
  const installed = readJsonConfig(installedPath);
12901
13043
  if (!installed.version)
12902
13044
  installed.version = 2;
@@ -12910,10 +13052,10 @@ class ClaudeDesktopAdapter {
12910
13052
  lastUpdated: new Date().toISOString()
12911
13053
  }];
12912
13054
  writeJsonConfig(installedPath, installed);
12913
- const marketCatalogDir = join23(marketRoot, ".claude-plugin");
12914
- mkdirSync17(marketCatalogDir, { recursive: true });
12915
- writeFileSync18(join23(marketCatalogDir, "marketplace.json"), JSON.stringify(getMarketplaceJson2(), null, 2));
12916
- const marketplacesPath = join23(pluginsDir, "known_marketplaces.json");
13055
+ const marketCatalogDir = join25(marketRoot, ".claude-plugin");
13056
+ mkdirSync18(marketCatalogDir, { recursive: true });
13057
+ writeFileSync18(join25(marketCatalogDir, "marketplace.json"), JSON.stringify(getMarketplaceJson2(), null, 2));
13058
+ const marketplacesPath = join25(pluginsDir, "known_marketplaces.json");
12917
13059
  const marketplaces = readJsonConfig(marketplacesPath);
12918
13060
  marketplaces["runwork"] = {
12919
13061
  source: { source: "directory", path: marketRoot },
@@ -12932,8 +13074,8 @@ class ClaudeDesktopAdapter {
12932
13074
  }
12933
13075
  }
12934
13076
  async buildPluginZip(skills, mcpServers, teamInstructions, outputPath) {
12935
- const stagingRoot = mkdtempSync3(join23(tmpdir3(), "runwork-plugin-"));
12936
- const pluginStagingDir = join23(stagingRoot, PLUGIN_NAME2);
13077
+ const stagingRoot = mkdtempSync3(join25(tmpdir3(), "runwork-plugin-"));
13078
+ const pluginStagingDir = join25(stagingRoot, PLUGIN_NAME2);
12937
13079
  try {
12938
13080
  writePluginTree(pluginStagingDir, {
12939
13081
  ...getPluginMetadata(),
@@ -12946,7 +13088,7 @@ class ClaudeDesktopAdapter {
12946
13088
  createZipFromDir(pluginStagingDir, outputPath);
12947
13089
  } finally {
12948
13090
  try {
12949
- rmSync6(stagingRoot, { recursive: true, force: true });
13091
+ rmSync7(stagingRoot, { recursive: true, force: true });
12950
13092
  } catch {}
12951
13093
  }
12952
13094
  }
@@ -12964,15 +13106,15 @@ class ClaudeDesktopAdapter {
12964
13106
  const pluginsDir = findCoworkPluginsDir();
12965
13107
  if (!pluginsDir)
12966
13108
  return;
12967
- writePluginTeamInstructions(join23(pluginsDir, "cache", "runwork", PLUGIN_NAME2, PLUGIN_VERSION2), instructions);
12968
- writePluginTeamInstructions(join23(pluginsDir, "marketplaces", "runwork", "plugins", PLUGIN_NAME2), instructions);
13109
+ writePluginTeamInstructions(join25(pluginsDir, "cache", "runwork", PLUGIN_NAME2, PLUGIN_VERSION2), instructions);
13110
+ writePluginTeamInstructions(join25(pluginsDir, "marketplaces", "runwork", "plugins", PLUGIN_NAME2), instructions);
12969
13111
  }
12970
13112
  async writeAgentConfig(config, _scope) {
12971
13113
  const configPath = getMcpConfigPath();
12972
13114
  let desktopConfig = {};
12973
- if (existsSync29(configPath)) {
13115
+ if (existsSync32(configPath)) {
12974
13116
  try {
12975
- desktopConfig = JSON.parse(readFileSync25(configPath, "utf-8"));
13117
+ desktopConfig = JSON.parse(readFileSync26(configPath, "utf-8"));
12976
13118
  } catch {}
12977
13119
  }
12978
13120
  if (!desktopConfig.preferences)
@@ -13006,62 +13148,71 @@ class ClaudeDesktopAdapter {
13006
13148
  removeTeamInstructionsFromFile(path2);
13007
13149
  } catch {}
13008
13150
  }
13009
- const rpm = findRpmPluginByName(PLUGIN_NAME2);
13010
- if (rpm) {
13011
- if (existsSync29(rpm.pluginPath)) {
13012
- try {
13013
- rmSync6(rpm.pluginPath, { recursive: true, force: true });
13014
- } catch {}
13015
- }
13016
- const manifestPath = join23(rpm.orgDir, "rpm", "manifest.json");
13017
- if (existsSync29(manifestPath)) {
13018
- try {
13019
- const manifest = JSON.parse(readFileSync25(manifestPath, "utf-8"));
13020
- if (Array.isArray(manifest.plugins)) {
13021
- manifest.plugins = manifest.plugins.filter((p) => p.name !== PLUGIN_NAME2);
13022
- manifest.lastUpdated = Date.now();
13023
- writeFileSync18(manifestPath, JSON.stringify(manifest, null, 2));
13024
- }
13025
- } catch {}
13026
- }
13151
+ for (const orgDir of listOrgDirs()) {
13152
+ const manifestPath = join25(orgDir, "rpm", "manifest.json");
13153
+ if (!existsSync32(manifestPath))
13154
+ continue;
13155
+ try {
13156
+ const manifest = JSON.parse(readFileSync26(manifestPath, "utf-8"));
13157
+ if (!Array.isArray(manifest.plugins))
13158
+ continue;
13159
+ const ours = manifest.plugins.filter((p) => isRunworkRpmPluginName(p.name));
13160
+ if (!ours.length)
13161
+ continue;
13162
+ for (const entry of ours) {
13163
+ if (!entry.id)
13164
+ continue;
13165
+ moveToTrash(join25(orgDir, "rpm", entry.id), `claude-desktop-plugin:${entry.name}`);
13166
+ }
13167
+ manifest.plugins = manifest.plugins.filter((p) => !isRunworkRpmPluginName(p.name));
13168
+ manifest.lastUpdated = Date.now();
13169
+ writeFileSync18(manifestPath, JSON.stringify(manifest, null, 2));
13170
+ } catch {}
13027
13171
  }
13028
- const pluginsDir = findCoworkPluginsDir();
13029
- if (pluginsDir) {
13030
- const cacheRunwork = join23(pluginsDir, "cache", "runwork");
13031
- const marketRunwork = join23(pluginsDir, "marketplaces", "runwork");
13172
+ for (const orgDir of listOrgDirs()) {
13173
+ const pluginsDir = join25(orgDir, "cowork_plugins");
13174
+ if (!existsSync32(join25(pluginsDir, "installed_plugins.json")))
13175
+ continue;
13176
+ const cacheRunwork = join25(pluginsDir, "cache", "runwork");
13177
+ const marketRunwork = join25(pluginsDir, "marketplaces", "runwork");
13032
13178
  for (const dir of [cacheRunwork, marketRunwork]) {
13033
- if (existsSync29(dir)) {
13179
+ if (existsSync32(dir)) {
13034
13180
  try {
13035
- rmSync6(dir, { recursive: true, force: true });
13181
+ rmSync7(dir, { recursive: true, force: true });
13036
13182
  } catch {}
13037
13183
  }
13038
13184
  }
13039
- const installedPath = join23(pluginsDir, "installed_plugins.json");
13040
- if (existsSync29(installedPath)) {
13041
- try {
13042
- const installed = readJsonConfig(installedPath);
13043
- if (installed.plugins) {
13044
- delete installed.plugins[`${PLUGIN_NAME2}@runwork`];
13045
- writeJsonConfig(installedPath, installed);
13185
+ const installedPath = join25(pluginsDir, "installed_plugins.json");
13186
+ try {
13187
+ const installed = readJsonConfig(installedPath);
13188
+ if (installed.plugins) {
13189
+ for (const key of Object.keys(installed.plugins)) {
13190
+ if (isRunworkPluginKey(key))
13191
+ delete installed.plugins[key];
13046
13192
  }
13047
- } catch {}
13048
- }
13049
- const marketplacesPath = join23(pluginsDir, "known_marketplaces.json");
13050
- if (existsSync29(marketplacesPath)) {
13193
+ writeJsonConfig(installedPath, installed);
13194
+ }
13195
+ } catch {}
13196
+ const marketplacesPath = join25(pluginsDir, "known_marketplaces.json");
13197
+ if (existsSync32(marketplacesPath)) {
13051
13198
  try {
13052
13199
  const marketplaces = readJsonConfig(marketplacesPath);
13053
- delete marketplaces["runwork"];
13200
+ delete marketplaces[RUNWORK_PLUGIN_MARKETPLACE];
13054
13201
  writeJsonConfig(marketplacesPath, marketplaces);
13055
13202
  } catch {}
13056
13203
  }
13057
13204
  const coworkSettingsPath = getCoworkSettingsPath(pluginsDir);
13058
- if (existsSync29(coworkSettingsPath)) {
13205
+ if (existsSync32(coworkSettingsPath)) {
13059
13206
  try {
13060
13207
  const settings = readJsonConfig(coworkSettingsPath);
13061
13208
  const enabledPlugins = settings.enabledPlugins;
13062
13209
  if (enabledPlugins && typeof enabledPlugins === "object" && !Array.isArray(enabledPlugins)) {
13063
- delete enabledPlugins[`${PLUGIN_NAME2}@runwork`];
13064
- settings.enabledPlugins = enabledPlugins;
13210
+ const flags = enabledPlugins;
13211
+ for (const key of Object.keys(flags)) {
13212
+ if (isRunworkPluginKey(key))
13213
+ delete flags[key];
13214
+ }
13215
+ settings.enabledPlugins = flags;
13065
13216
  writeJsonConfig(coworkSettingsPath, settings);
13066
13217
  }
13067
13218
  } catch {}
@@ -13073,22 +13224,22 @@ class ClaudeDesktopAdapter {
13073
13224
  const os2 = platform3();
13074
13225
  let claudeAppDir;
13075
13226
  if (os2 === "darwin") {
13076
- claudeAppDir = join23(homedir6(), "Library", "Application Support", "Claude");
13227
+ claudeAppDir = join25(homedir7(), "Library", "Application Support", "Claude");
13077
13228
  } else if (os2 === "win32") {
13078
- claudeAppDir = join23(process.env.APPDATA || join23(homedir6(), "AppData", "Roaming"), "Claude");
13229
+ claudeAppDir = join25(process.env.APPDATA || join25(homedir7(), "AppData", "Roaming"), "Claude");
13079
13230
  } else {
13080
- claudeAppDir = join23(homedir6(), ".config", "Claude");
13231
+ claudeAppDir = join25(homedir7(), ".config", "Claude");
13081
13232
  }
13082
- if (!existsSync29(claudeAppDir))
13233
+ if (!existsSync32(claudeAppDir))
13083
13234
  return null;
13084
13235
  const sinceMs = lastSyncAt ? new Date(lastSyncAt).getTime() : 0;
13085
13236
  let sessionCount = 0;
13086
13237
  let latestActivity = 0;
13087
13238
  const modelsUsed = new Set;
13088
13239
  let maxMcpTools = 0;
13089
- const agentSessionsDir = join23(claudeAppDir, "local-agent-mode-sessions");
13240
+ const agentSessionsDir = join25(claudeAppDir, "local-agent-mode-sessions");
13090
13241
  const activeDays = new Set;
13091
- if (existsSync29(agentSessionsDir)) {
13242
+ if (existsSync32(agentSessionsDir)) {
13092
13243
  this.walkSessionDirs(agentSessionsDir, sinceMs, (session) => {
13093
13244
  const sessionTime = session.lastActivityAt ?? session.createdAt ?? 0;
13094
13245
  if (sessionTime > sinceMs) {
@@ -13106,8 +13257,8 @@ class ClaudeDesktopAdapter {
13106
13257
  }
13107
13258
  });
13108
13259
  }
13109
- const codeSessionsDir = join23(claudeAppDir, "claude-code-sessions");
13110
- if (existsSync29(codeSessionsDir)) {
13260
+ const codeSessionsDir = join25(claudeAppDir, "claude-code-sessions");
13261
+ if (existsSync32(codeSessionsDir)) {
13111
13262
  this.walkSessionDirs(codeSessionsDir, sinceMs, (session) => {
13112
13263
  const sessionTime = session.lastActivityAt ?? session.createdAt ?? 0;
13113
13264
  if (sessionTime > sinceMs) {
@@ -13121,10 +13272,10 @@ class ClaudeDesktopAdapter {
13121
13272
  });
13122
13273
  }
13123
13274
  let scheduledTaskRuns = 0;
13124
- const scheduledTasksPath = join23(claudeAppDir, "scheduled-tasks.json");
13125
- if (existsSync29(scheduledTasksPath)) {
13275
+ const scheduledTasksPath = join25(claudeAppDir, "scheduled-tasks.json");
13276
+ if (existsSync32(scheduledTasksPath)) {
13126
13277
  try {
13127
- const raw = readFileSync25(scheduledTasksPath, "utf-8");
13278
+ const raw = readFileSync26(scheduledTasksPath, "utf-8");
13128
13279
  const parsed = JSON.parse(raw);
13129
13280
  const tasks = Array.isArray(parsed) ? parsed : Object.values(parsed);
13130
13281
  for (const task of tasks) {
@@ -13160,15 +13311,15 @@ class ClaudeDesktopAdapter {
13160
13311
  const os2 = platform3();
13161
13312
  let claudeAppDir;
13162
13313
  if (os2 === "darwin") {
13163
- claudeAppDir = join23(homedir6(), "Library", "Application Support", "Claude");
13314
+ claudeAppDir = join25(homedir7(), "Library", "Application Support", "Claude");
13164
13315
  } else if (os2 === "win32") {
13165
- claudeAppDir = join23(process.env.APPDATA || join23(homedir6(), "AppData", "Roaming"), "Claude");
13316
+ claudeAppDir = join25(process.env.APPDATA || join25(homedir7(), "AppData", "Roaming"), "Claude");
13166
13317
  } else {
13167
- claudeAppDir = join23(homedir6(), ".config", "Claude");
13318
+ claudeAppDir = join25(homedir7(), ".config", "Claude");
13168
13319
  }
13169
- const versionPath = join23(claudeAppDir, "claude-code", "sdk-version");
13170
- if (existsSync29(versionPath)) {
13171
- return readFileSync25(versionPath, "utf-8").trim();
13320
+ const versionPath = join25(claudeAppDir, "claude-code", "sdk-version");
13321
+ if (existsSync32(versionPath)) {
13322
+ return readFileSync26(versionPath, "utf-8").trim();
13172
13323
  }
13173
13324
  return null;
13174
13325
  } catch {
@@ -13177,31 +13328,31 @@ class ClaudeDesktopAdapter {
13177
13328
  }
13178
13329
  walkSessionDirs(baseDir, _sinceMs, onSession) {
13179
13330
  try {
13180
- for (const orgDir of readdirSync6(baseDir)) {
13331
+ for (const orgDir of readdirSync8(baseDir)) {
13181
13332
  if (orgDir.startsWith(".") || orgDir === "skills-plugin")
13182
13333
  continue;
13183
- const orgPath = join23(baseDir, orgDir);
13334
+ const orgPath = join25(baseDir, orgDir);
13184
13335
  try {
13185
- if (!statSync4(orgPath).isDirectory())
13336
+ if (!statSync5(orgPath).isDirectory())
13186
13337
  continue;
13187
13338
  } catch {
13188
13339
  continue;
13189
13340
  }
13190
- for (const userDir of readdirSync6(orgPath)) {
13341
+ for (const userDir of readdirSync8(orgPath)) {
13191
13342
  if (userDir.startsWith("."))
13192
13343
  continue;
13193
- const userPath = join23(orgPath, userDir);
13344
+ const userPath = join25(orgPath, userDir);
13194
13345
  try {
13195
- if (!statSync4(userPath).isDirectory())
13346
+ if (!statSync5(userPath).isDirectory())
13196
13347
  continue;
13197
13348
  } catch {
13198
13349
  continue;
13199
13350
  }
13200
- for (const file of readdirSync6(userPath)) {
13351
+ for (const file of readdirSync8(userPath)) {
13201
13352
  if (!file.endsWith(".json"))
13202
13353
  continue;
13203
13354
  try {
13204
- const session = JSON.parse(readFileSync25(join23(userPath, file), "utf-8"));
13355
+ const session = JSON.parse(readFileSync26(join25(userPath, file), "utf-8"));
13205
13356
  onSession(session);
13206
13357
  } catch {
13207
13358
  continue;
@@ -13215,9 +13366,9 @@ class ClaudeDesktopAdapter {
13215
13366
 
13216
13367
  // src/agents/cursor.ts
13217
13368
  init_subprocess();
13218
- import { existsSync as existsSync30, mkdirSync as mkdirSync18, readdirSync as readdirSync7, unlinkSync as unlinkSync4, writeFileSync as writeFileSync19 } from "fs";
13219
- import { join as join24 } from "path";
13220
- import { homedir as homedir7, platform as platform4 } from "os";
13369
+ import { existsSync as existsSync33, mkdirSync as mkdirSync19, unlinkSync as unlinkSync4, writeFileSync as writeFileSync19 } from "fs";
13370
+ import { join as join26 } from "path";
13371
+ import { homedir as homedir8, platform as platform4 } from "os";
13221
13372
 
13222
13373
  // src/utils/sqlite-adapter.ts
13223
13374
  var adapter = null;
@@ -13309,7 +13460,7 @@ class CursorAdapter {
13309
13460
  mcpProvidesSkills = true;
13310
13461
  async detect() {
13311
13462
  const os2 = platform4();
13312
- if (os2 === "darwin" && existsSync30("/Applications/Cursor.app"))
13463
+ if (os2 === "darwin" && existsSync33("/Applications/Cursor.app"))
13313
13464
  return true;
13314
13465
  return !!whichBinary("cursor");
13315
13466
  }
@@ -13320,7 +13471,7 @@ class CursorAdapter {
13320
13471
  return true;
13321
13472
  }
13322
13473
  async writeMcpServers(servers, scope) {
13323
- const filePath = scope === "project" ? join24(process.cwd(), ".cursor", "mcp.json") : join24(homedir7(), ".cursor", "mcp.json");
13474
+ const filePath = scope === "project" ? join26(process.cwd(), ".cursor", "mcp.json") : join26(homedir8(), ".cursor", "mcp.json");
13324
13475
  const entries = {};
13325
13476
  for (const s of servers) {
13326
13477
  entries[s.name] = {
@@ -13333,8 +13484,8 @@ class CursorAdapter {
13333
13484
  async writeSkills(skills, scope) {
13334
13485
  if (scope === "user")
13335
13486
  return 0;
13336
- const rulesDir = join24(process.cwd(), ".cursor", "rules");
13337
- mkdirSync18(rulesDir, { recursive: true });
13487
+ const rulesDir = join26(process.cwd(), ".cursor", "rules");
13488
+ mkdirSync19(rulesDir, { recursive: true });
13338
13489
  for (const skill of skills) {
13339
13490
  const mdcContent = `---
13340
13491
  description: "${skill.description}"
@@ -13342,30 +13493,30 @@ alwaysApply: false
13342
13493
  ---
13343
13494
 
13344
13495
  ${skill.content}`;
13345
- writeFileSync19(join24(rulesDir, `${skill.filename}.mdc`), mdcContent);
13496
+ writeFileSync19(join26(rulesDir, `${skill.filename}.mdc`), mdcContent);
13346
13497
  }
13347
13498
  return skills.length;
13348
13499
  }
13349
13500
  async writeInstructionHint(hint, scope) {
13350
- const filePath = scope === "project" ? join24(process.cwd(), ".cursor", "rules", "runwork.mdc") : join24(homedir7(), ".cursor", "rules", "runwork.mdc");
13501
+ const filePath = scope === "project" ? join26(process.cwd(), ".cursor", "rules", "runwork.mdc") : join26(homedir8(), ".cursor", "rules", "runwork.mdc");
13351
13502
  const mdcContent = `---
13352
13503
  description: "Runwork workspace connection"
13353
13504
  alwaysApply: true
13354
13505
  ---
13355
13506
 
13356
13507
  ${hint}`;
13357
- mkdirSync18(join24(filePath, ".."), { recursive: true });
13508
+ mkdirSync19(join26(filePath, ".."), { recursive: true });
13358
13509
  writeFileSync19(filePath, mdcContent);
13359
13510
  }
13360
13511
  async writeTeamInstructions(instructions, scope) {
13361
- const filePath = scope === "project" ? join24(process.cwd(), ".cursor", "rules", "runwork-team.mdc") : join24(homedir7(), ".cursor", "rules", "runwork-team.mdc");
13512
+ const filePath = scope === "project" ? join26(process.cwd(), ".cursor", "rules", "runwork-team.mdc") : join26(homedir8(), ".cursor", "rules", "runwork-team.mdc");
13362
13513
  const mdcContent = `---
13363
13514
  description: "Team instructions from Runwork workspace"
13364
13515
  alwaysApply: true
13365
13516
  ---
13366
13517
 
13367
13518
  ${instructions}`;
13368
- mkdirSync18(join24(filePath, ".."), { recursive: true });
13519
+ mkdirSync19(join26(filePath, ".."), { recursive: true });
13369
13520
  writeFileSync19(filePath, mdcContent);
13370
13521
  }
13371
13522
  async writeAgentConfig(config, scope, baseline) {
@@ -13375,7 +13526,7 @@ ${instructions}`;
13375
13526
  this.mergeSandboxAllowlist(config.networkAllowlist);
13376
13527
  }
13377
13528
  const globalDbPath = this.globalStorageDbPath();
13378
- if (!existsSync30(globalDbPath))
13529
+ if (!existsSync33(globalDbPath))
13379
13530
  return;
13380
13531
  const db = openWritableSqlite(globalDbPath);
13381
13532
  if (!db) {
@@ -13411,7 +13562,7 @@ ${instructions}`;
13411
13562
  return;
13412
13563
  }
13413
13564
  mergeSandboxAllowlist(domains) {
13414
- const configPath = join24(homedir7(), ".cursor", "sandbox.json");
13565
+ const configPath = join26(homedir8(), ".cursor", "sandbox.json");
13415
13566
  try {
13416
13567
  const config = readJsonConfig(configPath);
13417
13568
  if (!config.networkPolicy)
@@ -13426,29 +13577,25 @@ ${instructions}`;
13426
13577
  writeJsonConfig(configPath, config);
13427
13578
  } catch {}
13428
13579
  }
13580
+ async removeSkills(skillFilenames, scope) {
13581
+ if (scope !== "project")
13582
+ return;
13583
+ removeMatchingSkillFiles(join26(process.cwd(), ".cursor", "rules"), new Set(skillFilenames), ".mdc");
13584
+ }
13429
13585
  async cleanup(scope, manifest) {
13430
- const mcpPath = scope === "project" ? join24(process.cwd(), ".cursor", "mcp.json") : join24(homedir7(), ".cursor", "mcp.json");
13586
+ const mcpPath = scope === "project" ? join26(process.cwd(), ".cursor", "mcp.json") : join26(homedir8(), ".cursor", "mcp.json");
13431
13587
  removeRunworkMcpServers(mcpPath, "mcpServers");
13432
- const rulesDir = scope === "project" ? join24(process.cwd(), ".cursor", "rules") : join24(homedir7(), ".cursor", "rules");
13433
- if (existsSync30(rulesDir)) {
13588
+ const rulesDir = scope === "project" ? join26(process.cwd(), ".cursor", "rules") : join26(homedir8(), ".cursor", "rules");
13589
+ if (existsSync33(rulesDir)) {
13434
13590
  for (const file of ["runwork.mdc", "runwork-team.mdc"]) {
13435
- const filePath = join24(rulesDir, file);
13436
- if (existsSync30(filePath)) {
13591
+ const filePath = join26(rulesDir, file);
13592
+ if (existsSync33(filePath)) {
13437
13593
  try {
13438
13594
  unlinkSync4(filePath);
13439
13595
  } catch {}
13440
13596
  }
13441
13597
  }
13442
- if (manifest?.skillFilenames.length) {
13443
- const allowed = new Set(manifest.skillFilenames.map((f) => `${f}.mdc`));
13444
- for (const entry of readdirSync7(rulesDir)) {
13445
- if (allowed.has(entry)) {
13446
- try {
13447
- unlinkSync4(join24(rulesDir, entry));
13448
- } catch {}
13449
- }
13450
- }
13451
- }
13598
+ await this.removeSkills(manifest?.skillFilenames ?? [], scope);
13452
13599
  }
13453
13600
  }
13454
13601
  async readUsageStats(lastSyncAt) {
@@ -13465,7 +13612,7 @@ ${instructions}`;
13465
13612
  let latestActivity = 0;
13466
13613
  let agenticSessions = 0;
13467
13614
  let chatSessions = 0;
13468
- if (existsSync30(globalDbPath)) {
13615
+ if (existsSync33(globalDbPath)) {
13469
13616
  const composerResult = queryReadonlySqlite(globalDbPath, `SELECT value FROM cursorDiskKV WHERE key LIKE 'composerData:%'`);
13470
13617
  if (composerResult) {
13471
13618
  for (const line of composerResult.split(`
@@ -13523,10 +13670,10 @@ ${instructions}`;
13523
13670
  }
13524
13671
  }
13525
13672
  const sessionCount = newComposersWithoutId + activeComposerIds.size;
13526
- const trackingDbPath = join24(homedir7(), ".cursor", "ai-tracking", "ai-code-tracking.db");
13673
+ const trackingDbPath = join26(homedir8(), ".cursor", "ai-tracking", "ai-code-tracking.db");
13527
13674
  let aiCommitCount = 0;
13528
13675
  let avgAiPercent = 0;
13529
- if (existsSync30(trackingDbPath)) {
13676
+ if (existsSync33(trackingDbPath)) {
13530
13677
  const sinceSec = Math.floor(sinceMs / 1000);
13531
13678
  const commitResult = queryReadonlySqlite(trackingDbPath, `SELECT count(*), avg(CAST(v2AiPercentage AS REAL)) FROM scored_commits WHERE scoredAt > ${sinceSec}`);
13532
13679
  if (commitResult) {
@@ -13569,17 +13716,17 @@ ${instructions}`;
13569
13716
  globalStorageDbPath() {
13570
13717
  const os2 = platform4();
13571
13718
  if (os2 === "darwin") {
13572
- return join24(homedir7(), "Library", "Application Support", "Cursor", "User", "globalStorage", "state.vscdb");
13719
+ return join26(homedir8(), "Library", "Application Support", "Cursor", "User", "globalStorage", "state.vscdb");
13573
13720
  }
13574
13721
  if (os2 === "win32") {
13575
- return join24(process.env.APPDATA || join24(homedir7(), "AppData", "Roaming"), "Cursor", "User", "globalStorage", "state.vscdb");
13722
+ return join26(process.env.APPDATA || join26(homedir8(), "AppData", "Roaming"), "Cursor", "User", "globalStorage", "state.vscdb");
13576
13723
  }
13577
- return join24(homedir7(), ".config", "Cursor", "User", "globalStorage", "state.vscdb");
13724
+ return join26(homedir8(), ".config", "Cursor", "User", "globalStorage", "state.vscdb");
13578
13725
  }
13579
13726
  async readSessionDigests(sinceISO) {
13580
13727
  try {
13581
13728
  const dbPath = this.globalStorageDbPath();
13582
- if (!existsSync30(dbPath) || !sqliteAvailable())
13729
+ if (!existsSync33(dbPath) || !sqliteAvailable())
13583
13730
  return null;
13584
13731
  const composerRows = queryReadonlySqlite(dbPath, `SELECT json_object('id', json_extract(value,'$.composerId'), 'createdAt', json_extract(value,'$.createdAt')) FROM cursorDiskKV WHERE key LIKE 'composerData:%'`);
13585
13732
  const bubbleRows = queryReadonlySqlite(dbPath, `SELECT json_object('k', key, 'type', json_extract(value,'$.type'), 'text', substr(json_extract(value,'$.text'),1,2000), 'ts', json_extract(value,'$.createdAt'), 'tool', json_extract(value,'$.toolFormerData.name'), 'status', json_extract(value,'$.toolFormerData.status')) FROM cursorDiskKV WHERE key LIKE 'bubbleId:%'`);
@@ -13593,17 +13740,17 @@ ${instructions}`;
13593
13740
  }
13594
13741
 
13595
13742
  // src/agents/windsurf.ts
13596
- import { existsSync as existsSync31, mkdirSync as mkdirSync19, readdirSync as readdirSync8, unlinkSync as unlinkSync5, writeFileSync as writeFileSync20 } from "fs";
13597
- import { join as join25 } from "path";
13598
- import { homedir as homedir8, platform as platform5 } from "os";
13743
+ import { existsSync as existsSync34, mkdirSync as mkdirSync20, unlinkSync as unlinkSync5, writeFileSync as writeFileSync20 } from "fs";
13744
+ import { join as join27 } from "path";
13745
+ import { homedir as homedir9, platform as platform5 } from "os";
13599
13746
  function getWindsurfDataDir() {
13600
13747
  if (platform5() === "win32") {
13601
- return join25(process.env.APPDATA || join25(homedir8(), "AppData", "Roaming"), "Codeium", "windsurf");
13748
+ return join27(process.env.APPDATA || join27(homedir9(), "AppData", "Roaming"), "Codeium", "windsurf");
13602
13749
  }
13603
- return join25(homedir8(), ".codeium", "windsurf");
13750
+ return join27(homedir9(), ".codeium", "windsurf");
13604
13751
  }
13605
13752
  function getConfigPath() {
13606
- return join25(getWindsurfDataDir(), "mcp_config.json");
13753
+ return join27(getWindsurfDataDir(), "mcp_config.json");
13607
13754
  }
13608
13755
 
13609
13756
  class WindsurfAdapter {
@@ -13612,7 +13759,7 @@ class WindsurfAdapter {
13612
13759
  mcpProvidesSkills = true;
13613
13760
  async detect() {
13614
13761
  const os2 = platform5();
13615
- if (os2 === "darwin" && existsSync31("/Applications/Windsurf.app"))
13762
+ if (os2 === "darwin" && existsSync34("/Applications/Windsurf.app"))
13616
13763
  return true;
13617
13764
  return !!whichBinary("windsurf");
13618
13765
  }
@@ -13635,84 +13782,80 @@ class WindsurfAdapter {
13635
13782
  async writeSkills(skills, scope) {
13636
13783
  if (scope === "user")
13637
13784
  return 0;
13638
- const rulesDir = join25(process.cwd(), ".windsurf", "rules");
13639
- mkdirSync19(rulesDir, { recursive: true });
13785
+ const rulesDir = join27(process.cwd(), ".windsurf", "rules");
13786
+ mkdirSync20(rulesDir, { recursive: true });
13640
13787
  for (const skill of skills) {
13641
13788
  const content = `---
13642
13789
  trigger: manual
13643
13790
  ---
13644
13791
 
13645
13792
  ${skill.content}`;
13646
- writeFileSync20(join25(rulesDir, `${skill.filename}.md`), content);
13793
+ writeFileSync20(join27(rulesDir, `${skill.filename}.md`), content);
13647
13794
  }
13648
13795
  return skills.length;
13649
13796
  }
13650
13797
  async writeTeamInstructions(instructions, scope) {
13651
- const filePath = scope === "project" ? join25(process.cwd(), ".windsurf", "rules", "runwork-team.md") : join25(getWindsurfDataDir(), "rules", "runwork-team.md");
13798
+ const filePath = scope === "project" ? join27(process.cwd(), ".windsurf", "rules", "runwork-team.md") : join27(getWindsurfDataDir(), "rules", "runwork-team.md");
13652
13799
  const content = `---
13653
13800
  trigger: always_on
13654
13801
  description: "Team instructions from Runwork workspace"
13655
13802
  ---
13656
13803
 
13657
13804
  ${instructions}`;
13658
- mkdirSync19(join25(filePath, ".."), { recursive: true });
13805
+ mkdirSync20(join27(filePath, ".."), { recursive: true });
13659
13806
  writeFileSync20(filePath, content);
13660
13807
  }
13661
13808
  async writeInstructionHint(hint, scope) {
13662
- const filePath = scope === "project" ? join25(process.cwd(), ".windsurf", "rules", "runwork.md") : join25(getWindsurfDataDir(), "rules", "runwork.md");
13809
+ const filePath = scope === "project" ? join27(process.cwd(), ".windsurf", "rules", "runwork.md") : join27(getWindsurfDataDir(), "rules", "runwork.md");
13663
13810
  const content = `---
13664
13811
  trigger: always
13665
13812
  ---
13666
13813
 
13667
13814
  ${hint}`;
13668
- mkdirSync19(join25(filePath, ".."), { recursive: true });
13815
+ mkdirSync20(join27(filePath, ".."), { recursive: true });
13669
13816
  writeFileSync20(filePath, content);
13670
13817
  }
13818
+ async removeSkills(skillFilenames, scope) {
13819
+ if (scope !== "project")
13820
+ return;
13821
+ removeMatchingSkillFiles(join27(process.cwd(), ".windsurf", "rules"), new Set(skillFilenames), ".md");
13822
+ }
13671
13823
  async cleanup(scope, manifest) {
13672
13824
  if (scope === "user") {
13673
13825
  removeRunworkMcpServers(getConfigPath(), "mcpServers");
13674
13826
  }
13675
- const rulesDir = scope === "project" ? join25(process.cwd(), ".windsurf", "rules") : join25(getWindsurfDataDir(), "rules");
13676
- if (existsSync31(rulesDir)) {
13827
+ const rulesDir = scope === "project" ? join27(process.cwd(), ".windsurf", "rules") : join27(getWindsurfDataDir(), "rules");
13828
+ if (existsSync34(rulesDir)) {
13677
13829
  for (const file of ["runwork.md", "runwork-team.md"]) {
13678
- const filePath = join25(rulesDir, file);
13679
- if (existsSync31(filePath)) {
13830
+ const filePath = join27(rulesDir, file);
13831
+ if (existsSync34(filePath)) {
13680
13832
  try {
13681
13833
  unlinkSync5(filePath);
13682
13834
  } catch {}
13683
13835
  }
13684
13836
  }
13685
- if (manifest?.skillFilenames.length) {
13686
- const allowed = new Set(manifest.skillFilenames.map((f) => `${f}.md`));
13687
- for (const entry of readdirSync8(rulesDir)) {
13688
- if (allowed.has(entry)) {
13689
- try {
13690
- unlinkSync5(join25(rulesDir, entry));
13691
- } catch {}
13692
- }
13693
- }
13694
- }
13837
+ await this.removeSkills(manifest?.skillFilenames ?? [], scope);
13695
13838
  }
13696
13839
  }
13697
13840
  }
13698
13841
 
13699
13842
  // src/agents/codex.ts
13700
- import { existsSync as existsSync34, mkdirSync as mkdirSync20, readdirSync as readdirSync9, readFileSync as readFileSync26, rmSync as rmSync8, statSync as statSync5, writeFileSync as writeFileSync21 } from "fs";
13701
- import { join as join28 } from "path";
13702
- import { homedir as homedir11 } from "os";
13843
+ import { existsSync as existsSync37, mkdirSync as mkdirSync21, readdirSync as readdirSync11, readFileSync as readFileSync27, statSync as statSync6, writeFileSync as writeFileSync21 } from "fs";
13844
+ import { join as join30 } from "path";
13845
+ import { homedir as homedir12 } from "os";
13703
13846
  import { parse, stringify } from "smol-toml";
13704
13847
 
13705
13848
  // src/agents/detection.ts
13706
13849
  import { execFile } from "child_process";
13707
- import { existsSync as existsSync33 } from "fs";
13708
- import { homedir as homedir10, platform as platform7 } from "os";
13709
- import { isAbsolute as isAbsolute2, join as join27 } from "path";
13850
+ import { existsSync as existsSync36 } from "fs";
13851
+ import { homedir as homedir11, platform as platform7 } from "os";
13852
+ import { isAbsolute as isAbsolute2, join as join29 } from "path";
13710
13853
  import { promisify } from "util";
13711
13854
 
13712
13855
  // src/agents/registry.ts
13713
- import { platform as platform6, homedir as homedir9 } from "os";
13714
- import { isAbsolute, join as join26 } from "path";
13715
- import { existsSync as existsSync32 } from "fs";
13856
+ import { platform as platform6, homedir as homedir10 } from "os";
13857
+ import { isAbsolute, join as join28 } from "path";
13858
+ import { existsSync as existsSync35 } from "fs";
13716
13859
 
13717
13860
  // src/agents/registry-data.ts
13718
13861
  var CLAUDE_CODE_NATIVE_INSTALL_PATHS = [
@@ -14309,7 +14452,7 @@ function resolveToAbsolute(ps, scope) {
14309
14452
  const resolved = resolvePlatformString(ps);
14310
14453
  if (!resolved)
14311
14454
  return;
14312
- return scope === "global" ? join26(homedir9(), resolved) : join26(process.cwd(), resolved);
14455
+ return scope === "global" ? join28(homedir10(), resolved) : join28(process.cwd(), resolved);
14313
14456
  }
14314
14457
  function resolveAgentCliCommand(slug) {
14315
14458
  const agent = getAgent(slug);
@@ -14322,8 +14465,8 @@ function resolveAgentCliCommand(slug) {
14322
14465
  const resolved = resolvePlatformString(candidate);
14323
14466
  if (!resolved)
14324
14467
  continue;
14325
- const absolute = isAbsolute(resolved) ? resolved : join26(homedir9(), resolved);
14326
- if (existsSync32(absolute))
14468
+ const absolute = isAbsolute(resolved) ? resolved : join28(homedir10(), resolved);
14469
+ if (existsSync35(absolute))
14327
14470
  return absolute;
14328
14471
  }
14329
14472
  return null;
@@ -14366,13 +14509,13 @@ function resolveDetectionPath(target) {
14366
14509
  const resolved = resolvePlatformString(target);
14367
14510
  if (!resolved)
14368
14511
  return null;
14369
- return isAbsolute2(resolved) ? resolved : join27(homedir10(), resolved);
14512
+ return isAbsolute2(resolved) ? resolved : join29(homedir11(), resolved);
14370
14513
  }
14371
14514
  function checkPath(target) {
14372
14515
  const absolute = resolveDetectionPath(target);
14373
14516
  if (!absolute)
14374
14517
  return null;
14375
- return existsSync33(absolute) ? absolute : null;
14518
+ return existsSync36(absolute) ? absolute : null;
14376
14519
  }
14377
14520
  async function checkMacosBundleId(target) {
14378
14521
  if (platform7() !== "darwin")
@@ -14452,10 +14595,10 @@ class CodexAdapter {
14452
14595
  return true;
14453
14596
  }
14454
14597
  async writeMcpServers(servers, _scope) {
14455
- const configPath = join28(homedir11(), ".codex", "config.toml");
14598
+ const configPath = join30(homedir12(), ".codex", "config.toml");
14456
14599
  let parsed = {};
14457
- if (existsSync34(configPath)) {
14458
- parsed = parse(readFileSync26(configPath, "utf-8"));
14600
+ if (existsSync37(configPath)) {
14601
+ parsed = parse(readFileSync27(configPath, "utf-8"));
14459
14602
  }
14460
14603
  if (!parsed.mcp_servers || typeof parsed.mcp_servers !== "object") {
14461
14604
  parsed.mcp_servers = {};
@@ -14478,48 +14621,39 @@ class CodexAdapter {
14478
14621
  }
14479
14622
  mcpServers[safeName] = entry;
14480
14623
  }
14481
- mkdirSync20(join28(configPath, ".."), { recursive: true });
14624
+ mkdirSync21(join30(configPath, ".."), { recursive: true });
14482
14625
  writeFileSync21(configPath, stringify(parsed));
14483
14626
  }
14484
14627
  async writeSkills(skills, scope) {
14485
- const root = scope === "project" ? process.cwd() : homedir11();
14486
- const baseDir = join28(root, ".agents", "skills");
14487
- const legacyBaseDir = join28(root, ".codex", "skills");
14628
+ const root = scope === "project" ? process.cwd() : homedir12();
14629
+ const baseDir = join30(root, ".agents", "skills");
14630
+ const legacyBaseDir = join30(root, ".codex", "skills");
14488
14631
  for (const skill of skills) {
14489
14632
  if (skill.name !== skill.filename) {
14490
- for (const dir of [join28(baseDir, skill.name), join28(legacyBaseDir, skill.name)]) {
14491
- if (existsSync34(dir)) {
14492
- try {
14493
- rmSync8(dir, { recursive: true, force: true });
14494
- } catch {}
14495
- }
14633
+ for (const dir of [join30(baseDir, skill.name), join30(legacyBaseDir, skill.name)]) {
14634
+ moveToTrash(dir, `skill renamed to ${skill.filename}`);
14496
14635
  }
14497
14636
  }
14498
- const legacyDir = join28(legacyBaseDir, skill.filename);
14499
- if (existsSync34(legacyDir)) {
14500
- try {
14501
- rmSync8(legacyDir, { recursive: true, force: true });
14502
- } catch {}
14503
- }
14504
- const skillDir = join28(baseDir, skill.filename);
14505
- mkdirSync20(skillDir, { recursive: true });
14506
- writeFileSync21(join28(skillDir, "SKILL.md"), buildSkillMd2(skill));
14637
+ moveToTrash(join30(legacyBaseDir, skill.filename), "duplicate skill root consolidated");
14638
+ const skillDir = join30(baseDir, skill.filename);
14639
+ mkdirSync21(skillDir, { recursive: true });
14640
+ writeFileSync21(join30(skillDir, "SKILL.md"), buildSkillMd2(skill));
14507
14641
  }
14508
14642
  return skills.length;
14509
14643
  }
14510
14644
  async writeInstructionHint(hint, scope) {
14511
- const filePath = scope === "project" ? join28(process.cwd(), "AGENTS.md") : join28(homedir11(), ".codex", "AGENTS.md");
14645
+ const filePath = scope === "project" ? join30(process.cwd(), "AGENTS.md") : join30(homedir12(), ".codex", "AGENTS.md");
14512
14646
  writeHintToFile(filePath, hint);
14513
14647
  }
14514
14648
  async writeTeamInstructions(instructions, scope) {
14515
- const filePath = scope === "project" ? join28(process.cwd(), "AGENTS.md") : join28(homedir11(), ".codex", "AGENTS.md");
14649
+ const filePath = scope === "project" ? join30(process.cwd(), "AGENTS.md") : join30(homedir12(), ".codex", "AGENTS.md");
14516
14650
  writeTeamInstructionsToFile(filePath, instructions);
14517
14651
  }
14518
14652
  async writeAgentConfig(config, scope) {
14519
- const configPath = scope === "project" ? join28(process.cwd(), ".codex", "config.toml") : join28(homedir11(), ".codex", "config.toml");
14653
+ const configPath = scope === "project" ? join30(process.cwd(), ".codex", "config.toml") : join30(homedir12(), ".codex", "config.toml");
14520
14654
  let parsed = {};
14521
- if (existsSync34(configPath)) {
14522
- parsed = parse(readFileSync26(configPath, "utf-8"));
14655
+ if (existsSync37(configPath)) {
14656
+ parsed = parse(readFileSync27(configPath, "utf-8"));
14523
14657
  }
14524
14658
  if (config.modelPreference) {
14525
14659
  parsed.model = config.modelPreference;
@@ -14562,14 +14696,23 @@ class CodexAdapter {
14562
14696
  sww.network_access = true;
14563
14697
  }
14564
14698
  }
14565
- mkdirSync20(join28(configPath, ".."), { recursive: true });
14699
+ mkdirSync21(join30(configPath, ".."), { recursive: true });
14566
14700
  writeFileSync21(configPath, stringify(parsed));
14567
14701
  }
14702
+ async removeSkills(skillFilenames, scope) {
14703
+ if (!skillFilenames.length)
14704
+ return;
14705
+ const allowed = new Set(skillFilenames);
14706
+ const root = scope === "project" ? process.cwd() : homedir12();
14707
+ for (const skillsDir of [join30(root, ".agents", "skills"), join30(root, ".codex", "skills")]) {
14708
+ removeMatchingSkillDirs(skillsDir, allowed);
14709
+ }
14710
+ }
14568
14711
  async cleanup(scope, manifest) {
14569
- const configPath = join28(homedir11(), ".codex", "config.toml");
14570
- if (existsSync34(configPath)) {
14712
+ const configPath = join30(homedir12(), ".codex", "config.toml");
14713
+ if (existsSync37(configPath)) {
14571
14714
  try {
14572
- const parsed = parse(readFileSync26(configPath, "utf-8"));
14715
+ const parsed = parse(readFileSync27(configPath, "utf-8"));
14573
14716
  if (parsed.mcp_servers && typeof parsed.mcp_servers === "object") {
14574
14717
  const mcpServers = parsed.mcp_servers;
14575
14718
  for (const key of Object.keys(mcpServers)) {
@@ -14581,27 +14724,15 @@ class CodexAdapter {
14581
14724
  writeFileSync21(configPath, stringify(parsed));
14582
14725
  } catch {}
14583
14726
  }
14584
- const cleanupRoot = scope === "project" ? process.cwd() : homedir11();
14585
- for (const skillsDir of [join28(cleanupRoot, ".agents", "skills"), join28(cleanupRoot, ".codex", "skills")]) {
14586
- if (!existsSync34(skillsDir) || !manifest?.skillFilenames.length)
14587
- continue;
14588
- const allowed = new Set(manifest.skillFilenames);
14589
- for (const entry of readdirSync9(skillsDir)) {
14590
- if (!allowed.has(entry))
14591
- continue;
14592
- try {
14593
- rmSync8(join28(skillsDir, entry), { recursive: true, force: true });
14594
- } catch {}
14595
- }
14596
- }
14597
- const instructionFile = scope === "project" ? join28(process.cwd(), "AGENTS.md") : join28(homedir11(), ".codex", "AGENTS.md");
14727
+ await this.removeSkills(manifest?.skillFilenames ?? [], scope);
14728
+ const instructionFile = scope === "project" ? join30(process.cwd(), "AGENTS.md") : join30(homedir12(), ".codex", "AGENTS.md");
14598
14729
  removeHintFromFile(instructionFile);
14599
14730
  removeTeamInstructionsFromFile(instructionFile);
14600
14731
  }
14601
14732
  async readUsageStats(lastSyncAt) {
14602
14733
  try {
14603
- const codexDir = join28(homedir11(), ".codex");
14604
- if (!existsSync34(codexDir))
14734
+ const codexDir = join30(homedir12(), ".codex");
14735
+ if (!existsSync37(codexDir))
14605
14736
  return null;
14606
14737
  const sinceMs = lastSyncAt ? new Date(lastSyncAt).getTime() : 0;
14607
14738
  const sinceSec = Math.floor(sinceMs / 1000);
@@ -14610,8 +14741,8 @@ class CodexAdapter {
14610
14741
  let tokensUsed = rollout.tokensUsed;
14611
14742
  let latestMs = rollout.latestMs;
14612
14743
  let versions = [];
14613
- const dbPath = join28(codexDir, "state_5.sqlite");
14614
- if (existsSync34(dbPath)) {
14744
+ const dbPath = join30(codexDir, "state_5.sqlite");
14745
+ if (existsSync37(dbPath)) {
14615
14746
  const countResult = queryReadonlySqlite(dbPath, `SELECT count(*) FROM threads WHERE updated_at > ${sinceSec}`);
14616
14747
  const dbSessionCount = parseInt(countResult) || 0;
14617
14748
  const tokensResult = queryReadonlySqlite(dbPath, `SELECT coalesce(sum(tokens_used), 0) FROM threads WHERE updated_at > ${sinceSec}`);
@@ -14628,9 +14759,9 @@ class CodexAdapter {
14628
14759
  }
14629
14760
  let messageCount = rollout.messageCount;
14630
14761
  if (messageCount === 0) {
14631
- const historyPath = join28(codexDir, "history.jsonl");
14632
- if (existsSync34(historyPath)) {
14633
- const content = readFileSync26(historyPath, "utf-8").trim();
14762
+ const historyPath = join30(codexDir, "history.jsonl");
14763
+ if (existsSync37(historyPath)) {
14764
+ const content = readFileSync27(historyPath, "utf-8").trim();
14634
14765
  if (content) {
14635
14766
  for (const line of content.split(/[\r\n]+/)) {
14636
14767
  try {
@@ -14673,19 +14804,19 @@ class CodexAdapter {
14673
14804
  latestMs: 0,
14674
14805
  activeDays: []
14675
14806
  };
14676
- const sessionsDir = join28(homedir11(), ".codex", "sessions");
14677
- if (!existsSync34(sessionsDir))
14807
+ const sessionsDir = join30(homedir12(), ".codex", "sessions");
14808
+ if (!existsSync37(sessionsDir))
14678
14809
  return result;
14679
14810
  const files = [];
14680
14811
  const walk = (dir) => {
14681
14812
  let entries;
14682
14813
  try {
14683
- entries = readdirSync9(dir, { withFileTypes: true });
14814
+ entries = readdirSync11(dir, { withFileTypes: true });
14684
14815
  } catch {
14685
14816
  return;
14686
14817
  }
14687
14818
  for (const e of entries) {
14688
- const full = join28(dir, e.name);
14819
+ const full = join30(dir, e.name);
14689
14820
  if (e.isDirectory())
14690
14821
  walk(full);
14691
14822
  else if (e.name.startsWith("rollout-") && e.name.endsWith(".jsonl"))
@@ -14696,7 +14827,7 @@ class CodexAdapter {
14696
14827
  for (const file of files) {
14697
14828
  let stat;
14698
14829
  try {
14699
- stat = statSync5(file);
14830
+ stat = statSync6(file);
14700
14831
  } catch {
14701
14832
  continue;
14702
14833
  }
@@ -14704,7 +14835,7 @@ class CodexAdapter {
14704
14835
  continue;
14705
14836
  let content;
14706
14837
  try {
14707
- content = readFileSync26(file, "utf-8");
14838
+ content = readFileSync27(file, "utf-8");
14708
14839
  } catch {
14709
14840
  continue;
14710
14841
  }
@@ -14763,20 +14894,20 @@ class CodexAdapter {
14763
14894
  }
14764
14895
  async readSessionDigests(sinceISO) {
14765
14896
  try {
14766
- const sessionsDir = join28(homedir11(), ".codex", "sessions");
14767
- if (!existsSync34(sessionsDir))
14897
+ const sessionsDir = join30(homedir12(), ".codex", "sessions");
14898
+ if (!existsSync37(sessionsDir))
14768
14899
  return null;
14769
14900
  const sinceMs = sinceISO ? new Date(sinceISO).getTime() : 0;
14770
14901
  const files = [];
14771
14902
  const walk = (dir) => {
14772
14903
  let entries;
14773
14904
  try {
14774
- entries = readdirSync9(dir, { withFileTypes: true });
14905
+ entries = readdirSync11(dir, { withFileTypes: true });
14775
14906
  } catch {
14776
14907
  return;
14777
14908
  }
14778
14909
  for (const e of entries) {
14779
- const full = join28(dir, e.name);
14910
+ const full = join30(dir, e.name);
14780
14911
  if (e.isDirectory())
14781
14912
  walk(full);
14782
14913
  else if (e.name.startsWith("rollout-") && e.name.endsWith(".jsonl"))
@@ -14788,7 +14919,7 @@ class CodexAdapter {
14788
14919
  for (const file of files) {
14789
14920
  let stat;
14790
14921
  try {
14791
- stat = statSync5(file);
14922
+ stat = statSync6(file);
14792
14923
  } catch {
14793
14924
  continue;
14794
14925
  }
@@ -14796,7 +14927,7 @@ class CodexAdapter {
14796
14927
  continue;
14797
14928
  let content;
14798
14929
  try {
14799
- content = readFileSync26(file, "utf-8");
14930
+ content = readFileSync27(file, "utf-8");
14800
14931
  } catch {
14801
14932
  continue;
14802
14933
  }
@@ -14811,9 +14942,9 @@ class CodexAdapter {
14811
14942
  }
14812
14943
  async readVersion() {
14813
14944
  try {
14814
- const versionPath = join28(homedir11(), ".codex", "version.json");
14815
- if (existsSync34(versionPath)) {
14816
- const data = JSON.parse(readFileSync26(versionPath, "utf-8"));
14945
+ const versionPath = join30(homedir12(), ".codex", "version.json");
14946
+ if (existsSync37(versionPath)) {
14947
+ const data = JSON.parse(readFileSync27(versionPath, "utf-8"));
14817
14948
  return data.latest_version ?? null;
14818
14949
  }
14819
14950
  } catch {}
@@ -14821,24 +14952,24 @@ class CodexAdapter {
14821
14952
  }
14822
14953
  async readSkillUsage(lastSyncAt) {
14823
14954
  try {
14824
- const sessionsDir = join28(homedir11(), ".codex", "sessions");
14825
- if (!existsSync34(sessionsDir))
14955
+ const sessionsDir = join30(homedir12(), ".codex", "sessions");
14956
+ if (!existsSync37(sessionsDir))
14826
14957
  return null;
14827
14958
  const sinceMs = lastSyncAt ? new Date(lastSyncAt).getTime() : 0;
14828
14959
  const skillCounts = new Map;
14829
14960
  const walkDir2 = (dir) => {
14830
14961
  let entries;
14831
14962
  try {
14832
- entries = readdirSync9(dir);
14963
+ entries = readdirSync11(dir);
14833
14964
  } catch {
14834
14965
  return;
14835
14966
  }
14836
14967
  for (const entry of entries) {
14837
- const fullPath = join28(dir, entry);
14968
+ const fullPath = join30(dir, entry);
14838
14969
  if (entry.endsWith(".jsonl")) {
14839
14970
  let fileStat;
14840
14971
  try {
14841
- fileStat = statSync5(fullPath);
14972
+ fileStat = statSync6(fullPath);
14842
14973
  } catch {
14843
14974
  continue;
14844
14975
  }
@@ -14847,7 +14978,7 @@ class CodexAdapter {
14847
14978
  this.parseRolloutForSkills(fullPath, sinceMs, skillCounts);
14848
14979
  } else {
14849
14980
  try {
14850
- if (statSync5(fullPath).isDirectory())
14981
+ if (statSync6(fullPath).isDirectory())
14851
14982
  walkDir2(fullPath);
14852
14983
  } catch {
14853
14984
  continue;
@@ -14874,7 +15005,7 @@ class CodexAdapter {
14874
15005
  parseRolloutForSkills(filePath, sinceMs, skillCounts) {
14875
15006
  let content;
14876
15007
  try {
14877
- content = readFileSync26(filePath, "utf-8");
15008
+ content = readFileSync27(filePath, "utf-8");
14878
15009
  } catch {
14879
15010
  return;
14880
15011
  }
@@ -14923,11 +15054,11 @@ class CodexAdapter {
14923
15054
  }
14924
15055
  }
14925
15056
  registerDesktopWorkspace(workspacePath, label) {
14926
- const statePath = join28(homedir11(), ".codex", ".codex-global-state.json");
15057
+ const statePath = join30(homedir12(), ".codex", ".codex-global-state.json");
14927
15058
  let state = {};
14928
- if (existsSync34(statePath)) {
15059
+ if (existsSync37(statePath)) {
14929
15060
  try {
14930
- state = JSON.parse(readFileSync26(statePath, "utf-8"));
15061
+ state = JSON.parse(readFileSync27(statePath, "utf-8"));
14931
15062
  } catch {
14932
15063
  return "app_running";
14933
15064
  }
@@ -14954,7 +15085,7 @@ class CodexAdapter {
14954
15085
  }
14955
15086
  labels[workspacePath] = label;
14956
15087
  state["electron-workspace-root-labels"] = labels;
14957
- mkdirSync20(join28(statePath, ".."), { recursive: true });
15088
+ mkdirSync21(join30(statePath, ".."), { recursive: true });
14958
15089
  writeFileSync21(statePath, JSON.stringify(state));
14959
15090
  return "written";
14960
15091
  }
@@ -14975,9 +15106,9 @@ class CodexDesktopAdapter extends CodexAdapter {
14975
15106
  }
14976
15107
 
14977
15108
  // src/agents/cline.ts
14978
- import { existsSync as existsSync35, mkdirSync as mkdirSync21, readFileSync as readFileSync27, readdirSync as readdirSync10, rmSync as rmSync9, unlinkSync as unlinkSync6, writeFileSync as writeFileSync22 } from "fs";
14979
- import { join as join29 } from "path";
14980
- import { homedir as homedir12 } from "os";
15109
+ import { existsSync as existsSync38, mkdirSync as mkdirSync22, readFileSync as readFileSync28, readdirSync as readdirSync12, rmSync as rmSync10, unlinkSync as unlinkSync6, writeFileSync as writeFileSync22 } from "fs";
15110
+ import { join as join31 } from "path";
15111
+ import { homedir as homedir13 } from "os";
14981
15112
  class ClineAdapter {
14982
15113
  name = "Cline";
14983
15114
  slug = "cline";
@@ -14992,7 +15123,7 @@ class ClineAdapter {
14992
15123
  return true;
14993
15124
  }
14994
15125
  async writeMcpServers(servers, _scope) {
14995
- const configPath = join29(homedir12(), ".cline", "data", "settings", "cline_mcp_settings.json");
15126
+ const configPath = join31(homedir13(), ".cline", "data", "settings", "cline_mcp_settings.json");
14996
15127
  const entries = {};
14997
15128
  for (const s of servers) {
14998
15129
  entries[s.name] = {
@@ -15005,35 +15136,35 @@ class ClineAdapter {
15005
15136
  async writeSkills(skills, scope) {
15006
15137
  if (scope === "user")
15007
15138
  return 0;
15008
- const rulesDir = join29(process.cwd(), ".clinerules");
15009
- mkdirSync21(rulesDir, { recursive: true });
15139
+ const rulesDir = join31(process.cwd(), ".clinerules");
15140
+ mkdirSync22(rulesDir, { recursive: true });
15010
15141
  for (const skill of skills) {
15011
- writeFileSync22(join29(rulesDir, `${skill.filename}.md`), buildSkillMd2(skill));
15142
+ writeFileSync22(join31(rulesDir, `${skill.filename}.md`), buildSkillMd2(skill));
15012
15143
  }
15013
15144
  return skills.length;
15014
15145
  }
15015
15146
  async writeInstructionHint(hint, scope) {
15016
15147
  if (scope === "user")
15017
15148
  return;
15018
- const filePath = join29(process.cwd(), ".clinerules", "runwork.md");
15019
- mkdirSync21(join29(filePath, ".."), { recursive: true });
15149
+ const filePath = join31(process.cwd(), ".clinerules", "runwork.md");
15150
+ mkdirSync22(join31(filePath, ".."), { recursive: true });
15020
15151
  writeFileSync22(filePath, hint);
15021
15152
  }
15022
15153
  async writeTeamInstructions(instructions, scope) {
15023
15154
  if (scope === "user")
15024
15155
  return;
15025
- const filePath = join29(process.cwd(), ".clinerules", "runwork-team.md");
15026
- mkdirSync21(join29(filePath, ".."), { recursive: true });
15156
+ const filePath = join31(process.cwd(), ".clinerules", "runwork-team.md");
15157
+ mkdirSync22(join31(filePath, ".."), { recursive: true });
15027
15158
  writeFileSync22(filePath, instructions);
15028
15159
  }
15029
15160
  async writeAgentConfig(config, scope) {
15030
15161
  if (scope !== "user")
15031
15162
  return;
15032
- const globalStatePath = join29(homedir12(), ".cline", "data", "globalState.json");
15163
+ const globalStatePath = join31(homedir13(), ".cline", "data", "globalState.json");
15033
15164
  let state = {};
15034
- if (existsSync35(globalStatePath)) {
15165
+ if (existsSync38(globalStatePath)) {
15035
15166
  try {
15036
- state = JSON.parse(readFileSync27(globalStatePath, "utf-8"));
15167
+ state = JSON.parse(readFileSync28(globalStatePath, "utf-8"));
15037
15168
  } catch {}
15038
15169
  }
15039
15170
  if (config.modelPreference) {
@@ -15054,38 +15185,34 @@ class ClineAdapter {
15054
15185
  state.autoApprovalSettings.enabled = false;
15055
15186
  }
15056
15187
  }
15057
- mkdirSync21(join29(globalStatePath, ".."), { recursive: true });
15188
+ mkdirSync22(join31(globalStatePath, ".."), { recursive: true });
15058
15189
  writeFileSync22(globalStatePath, JSON.stringify(state, null, 2));
15059
15190
  }
15191
+ async removeSkills(skillFilenames, scope) {
15192
+ if (scope !== "project")
15193
+ return;
15194
+ removeMatchingSkillFiles(join31(process.cwd(), ".clinerules"), new Set(skillFilenames), ".md");
15195
+ }
15060
15196
  async cleanup(scope, manifest) {
15061
15197
  if (scope === "user") {
15062
- const configPath = join29(homedir12(), ".cline", "data", "settings", "cline_mcp_settings.json");
15198
+ const configPath = join31(homedir13(), ".cline", "data", "settings", "cline_mcp_settings.json");
15063
15199
  removeRunworkMcpServers(configPath, "mcpServers");
15064
15200
  }
15065
15201
  if (scope === "project") {
15066
- const rulesDir = join29(process.cwd(), ".clinerules");
15067
- if (existsSync35(rulesDir)) {
15202
+ const rulesDir = join31(process.cwd(), ".clinerules");
15203
+ if (existsSync38(rulesDir)) {
15068
15204
  for (const file of ["runwork.md", "runwork-team.md"]) {
15069
- const filePath = join29(rulesDir, file);
15070
- if (existsSync35(filePath)) {
15205
+ const filePath = join31(rulesDir, file);
15206
+ if (existsSync38(filePath)) {
15071
15207
  try {
15072
15208
  unlinkSync6(filePath);
15073
15209
  } catch {}
15074
15210
  }
15075
15211
  }
15076
- if (manifest?.skillFilenames.length) {
15077
- const allowed = new Set(manifest.skillFilenames.map((f) => `${f}.md`));
15078
- for (const entry of readdirSync10(rulesDir)) {
15079
- if (allowed.has(entry)) {
15080
- try {
15081
- unlinkSync6(join29(rulesDir, entry));
15082
- } catch {}
15083
- }
15084
- }
15085
- }
15212
+ await this.removeSkills(manifest?.skillFilenames ?? [], scope);
15086
15213
  try {
15087
- if (readdirSync10(rulesDir).length === 0)
15088
- rmSync9(rulesDir, { recursive: true });
15214
+ if (readdirSync12(rulesDir).length === 0)
15215
+ rmSync10(rulesDir, { recursive: true });
15089
15216
  } catch {}
15090
15217
  }
15091
15218
  }
@@ -15093,9 +15220,9 @@ class ClineAdapter {
15093
15220
  }
15094
15221
 
15095
15222
  // src/agents/gemini.ts
15096
- import { existsSync as existsSync36, mkdirSync as mkdirSync22, readdirSync as readdirSync11, readFileSync as readFileSync28, rmSync as rmSync10, statSync as statSync6, writeFileSync as writeFileSync23 } from "fs";
15097
- import { join as join30 } from "path";
15098
- import { homedir as homedir13 } from "os";
15223
+ import { existsSync as existsSync39, mkdirSync as mkdirSync23, readdirSync as readdirSync13, readFileSync as readFileSync29, statSync as statSync7, writeFileSync as writeFileSync23 } from "fs";
15224
+ import { join as join32 } from "path";
15225
+ import { homedir as homedir14 } from "os";
15099
15226
  class GeminiAdapter {
15100
15227
  name = "Gemini CLI";
15101
15228
  slug = "gemini";
@@ -15110,7 +15237,7 @@ class GeminiAdapter {
15110
15237
  return true;
15111
15238
  }
15112
15239
  async writeMcpServers(servers, _scope) {
15113
- const configPath = join30(homedir13(), ".gemini", "settings.json");
15240
+ const configPath = join32(homedir14(), ".gemini", "settings.json");
15114
15241
  const entries = {};
15115
15242
  for (const s of servers) {
15116
15243
  entries[s.name] = {
@@ -15121,36 +15248,31 @@ class GeminiAdapter {
15121
15248
  mergeJsonMcpServers(configPath, entries, "mcpServers");
15122
15249
  }
15123
15250
  async writeSkills(skills, scope) {
15124
- const baseDir = scope === "project" ? join30(process.cwd(), ".gemini", "skills") : join30(homedir13(), ".gemini", "skills");
15251
+ const baseDir = scope === "project" ? join32(process.cwd(), ".gemini", "skills") : join32(homedir14(), ".gemini", "skills");
15125
15252
  for (const skill of skills) {
15126
15253
  if (skill.name !== skill.filename) {
15127
- const oldDir = join30(baseDir, skill.name);
15128
- if (existsSync36(oldDir)) {
15129
- try {
15130
- rmSync10(oldDir, { recursive: true, force: true });
15131
- } catch {}
15132
- }
15254
+ moveToTrash(join32(baseDir, skill.name), `skill renamed to ${skill.filename}`);
15133
15255
  }
15134
- const skillDir = join30(baseDir, skill.filename);
15135
- mkdirSync22(skillDir, { recursive: true });
15136
- writeFileSync23(join30(skillDir, "SKILL.md"), buildSkillMd2(skill));
15256
+ const skillDir = join32(baseDir, skill.filename);
15257
+ mkdirSync23(skillDir, { recursive: true });
15258
+ writeFileSync23(join32(skillDir, "SKILL.md"), buildSkillMd2(skill));
15137
15259
  }
15138
15260
  return skills.length;
15139
15261
  }
15140
15262
  async writeInstructionHint(hint, scope) {
15141
- const filePath = scope === "project" ? join30(process.cwd(), "GEMINI.md") : join30(homedir13(), ".gemini", "GEMINI.md");
15263
+ const filePath = scope === "project" ? join32(process.cwd(), "GEMINI.md") : join32(homedir14(), ".gemini", "GEMINI.md");
15142
15264
  writeHintToFile(filePath, hint);
15143
15265
  }
15144
15266
  async writeTeamInstructions(instructions, scope) {
15145
- const filePath = scope === "project" ? join30(process.cwd(), "GEMINI.md") : join30(homedir13(), ".gemini", "GEMINI.md");
15267
+ const filePath = scope === "project" ? join32(process.cwd(), "GEMINI.md") : join32(homedir14(), ".gemini", "GEMINI.md");
15146
15268
  writeTeamInstructionsToFile(filePath, instructions);
15147
15269
  }
15148
15270
  async writeAgentConfig(config, scope) {
15149
- const settingsPath = scope === "project" ? join30(process.cwd(), ".gemini", "settings.json") : join30(homedir13(), ".gemini", "settings.json");
15271
+ const settingsPath = scope === "project" ? join32(process.cwd(), ".gemini", "settings.json") : join32(homedir14(), ".gemini", "settings.json");
15150
15272
  let settings = {};
15151
- if (existsSync36(settingsPath)) {
15273
+ if (existsSync39(settingsPath)) {
15152
15274
  try {
15153
- settings = JSON.parse(readFileSync28(settingsPath, "utf-8"));
15275
+ settings = JSON.parse(readFileSync29(settingsPath, "utf-8"));
15154
15276
  } catch {}
15155
15277
  }
15156
15278
  if (config.modelPreference) {
@@ -15168,13 +15290,13 @@ class GeminiAdapter {
15168
15290
  settings.tools = {};
15169
15291
  settings.tools.exclude = config.permissionRules.deny;
15170
15292
  }
15171
- mkdirSync22(join30(settingsPath, ".."), { recursive: true });
15293
+ mkdirSync23(join32(settingsPath, ".."), { recursive: true });
15172
15294
  writeFileSync23(settingsPath, JSON.stringify(settings, null, 2));
15173
15295
  }
15174
15296
  async readUsageStats(lastSyncAt) {
15175
15297
  try {
15176
- const tmpDir = join30(homedir13(), ".gemini", "tmp");
15177
- if (!existsSync36(tmpDir))
15298
+ const tmpDir = join32(homedir14(), ".gemini", "tmp");
15299
+ if (!existsSync39(tmpDir))
15178
15300
  return null;
15179
15301
  const sinceMs = lastSyncAt ? new Date(lastSyncAt).getTime() : 0;
15180
15302
  let sessionCount = 0;
@@ -15183,27 +15305,27 @@ class GeminiAdapter {
15183
15305
  const activeDays = new Set;
15184
15306
  let projects;
15185
15307
  try {
15186
- projects = readdirSync11(tmpDir, { withFileTypes: true });
15308
+ projects = readdirSync13(tmpDir, { withFileTypes: true });
15187
15309
  } catch {
15188
15310
  return null;
15189
15311
  }
15190
15312
  for (const project of projects) {
15191
15313
  if (!project.isDirectory())
15192
15314
  continue;
15193
- const chatsDir = join30(tmpDir, project.name, "chats");
15315
+ const chatsDir = join32(tmpDir, project.name, "chats");
15194
15316
  let files;
15195
15317
  try {
15196
- files = readdirSync11(chatsDir, { withFileTypes: true });
15318
+ files = readdirSync13(chatsDir, { withFileTypes: true });
15197
15319
  } catch {
15198
15320
  continue;
15199
15321
  }
15200
15322
  for (const file of files) {
15201
15323
  if (!file.name.startsWith("session-") || !file.name.endsWith(".json"))
15202
15324
  continue;
15203
- const filePath = join30(chatsDir, file.name);
15325
+ const filePath = join32(chatsDir, file.name);
15204
15326
  let stat;
15205
15327
  try {
15206
- stat = statSync6(filePath);
15328
+ stat = statSync7(filePath);
15207
15329
  } catch {
15208
15330
  continue;
15209
15331
  }
@@ -15211,7 +15333,7 @@ class GeminiAdapter {
15211
15333
  continue;
15212
15334
  let session;
15213
15335
  try {
15214
- session = JSON.parse(readFileSync28(filePath, "utf-8"));
15336
+ session = JSON.parse(readFileSync29(filePath, "utf-8"));
15215
15337
  } catch {
15216
15338
  continue;
15217
15339
  }
@@ -15251,31 +15373,25 @@ class GeminiAdapter {
15251
15373
  return null;
15252
15374
  }
15253
15375
  }
15376
+ async removeSkills(skillFilenames, scope) {
15377
+ const skillsDir = scope === "project" ? join32(process.cwd(), ".gemini", "skills") : join32(homedir14(), ".gemini", "skills");
15378
+ removeMatchingSkillDirs(skillsDir, new Set(skillFilenames));
15379
+ }
15254
15380
  async cleanup(scope, manifest) {
15255
15381
  if (scope === "user") {
15256
- removeRunworkMcpServers(join30(homedir13(), ".gemini", "settings.json"), "mcpServers");
15382
+ removeRunworkMcpServers(join32(homedir14(), ".gemini", "settings.json"), "mcpServers");
15257
15383
  }
15258
- const skillsDir = scope === "project" ? join30(process.cwd(), ".gemini", "skills") : join30(homedir13(), ".gemini", "skills");
15259
- if (existsSync36(skillsDir) && manifest?.skillFilenames.length) {
15260
- const allowed = new Set(manifest.skillFilenames);
15261
- for (const entry of readdirSync11(skillsDir)) {
15262
- if (!allowed.has(entry))
15263
- continue;
15264
- try {
15265
- rmSync10(join30(skillsDir, entry), { recursive: true, force: true });
15266
- } catch {}
15267
- }
15268
- }
15269
- const instructionFile = scope === "project" ? join30(process.cwd(), "GEMINI.md") : join30(homedir13(), ".gemini", "GEMINI.md");
15384
+ await this.removeSkills(manifest?.skillFilenames ?? [], scope);
15385
+ const instructionFile = scope === "project" ? join32(process.cwd(), "GEMINI.md") : join32(homedir14(), ".gemini", "GEMINI.md");
15270
15386
  removeHintFromFile(instructionFile);
15271
15387
  removeTeamInstructionsFromFile(instructionFile);
15272
15388
  }
15273
15389
  }
15274
15390
 
15275
15391
  // src/agents/generic-adapter.ts
15276
- import { existsSync as existsSync37, mkdirSync as mkdirSync23, readdirSync as readdirSync12, rmSync as rmSync11, writeFileSync as writeFileSync24 } from "fs";
15277
- import { join as join31 } from "path";
15278
- import { homedir as homedir14 } from "os";
15392
+ import { existsSync as existsSync40, mkdirSync as mkdirSync24, rmSync as rmSync12, writeFileSync as writeFileSync24 } from "fs";
15393
+ import { join as join33 } from "path";
15394
+ import { homedir as homedir15 } from "os";
15279
15395
  class GenericAgentAdapter {
15280
15396
  name;
15281
15397
  slug;
@@ -15300,7 +15416,7 @@ class GenericAgentAdapter {
15300
15416
  async writeMcpServers(servers, _scope) {
15301
15417
  if (!this.def.mcpConfigPath)
15302
15418
  return;
15303
- const filePath = join31(homedir14(), resolvePlatformString(this.def.mcpConfigPath) || "");
15419
+ const filePath = join33(homedir15(), resolvePlatformString(this.def.mcpConfigPath) || "");
15304
15420
  if (!filePath)
15305
15421
  return;
15306
15422
  const entries = {};
@@ -15324,16 +15440,16 @@ class GenericAgentAdapter {
15324
15440
  return 0;
15325
15441
  for (const skill of skills) {
15326
15442
  if (skill.name !== skill.filename) {
15327
- const oldDir = join31(baseDir, skill.name);
15328
- if (existsSync37(oldDir)) {
15443
+ const oldDir = join33(baseDir, skill.name);
15444
+ if (existsSync40(oldDir)) {
15329
15445
  try {
15330
- rmSync11(oldDir, { recursive: true, force: true });
15446
+ rmSync12(oldDir, { recursive: true, force: true });
15331
15447
  } catch {}
15332
15448
  }
15333
15449
  }
15334
- const skillDir = join31(baseDir, skill.filename);
15335
- mkdirSync23(skillDir, { recursive: true });
15336
- writeFileSync24(join31(skillDir, "SKILL.md"), buildSkillMd2(skill));
15450
+ const skillDir = join33(baseDir, skill.filename);
15451
+ mkdirSync24(skillDir, { recursive: true });
15452
+ writeFileSync24(join33(skillDir, "SKILL.md"), buildSkillMd2(skill));
15337
15453
  }
15338
15454
  return skills.length;
15339
15455
  }
@@ -15359,31 +15475,25 @@ class GenericAgentAdapter {
15359
15475
  return;
15360
15476
  writeTeamInstructionsToFile(filePath, instructions);
15361
15477
  }
15478
+ async removeSkills(skillFilenames, scope) {
15479
+ if (!this.def.skillsPaths || !skillFilenames.length)
15480
+ return;
15481
+ const pathTemplate = scope === "project" ? this.def.skillsPaths.project : this.def.skillsPaths.global;
15482
+ if (!pathTemplate)
15483
+ return;
15484
+ const baseDir = resolveToAbsolute(pathTemplate, scope === "project" ? "project" : "global");
15485
+ if (baseDir)
15486
+ removeMatchingSkillDirs(baseDir, new Set(skillFilenames));
15487
+ }
15362
15488
  async cleanup(scope, manifest) {
15363
15489
  if (this.def.mcpConfigPath && scope === "user") {
15364
15490
  const resolved = resolvePlatformString(this.def.mcpConfigPath);
15365
15491
  if (resolved) {
15366
- const filePath = join31(homedir14(), resolved);
15492
+ const filePath = join33(homedir15(), resolved);
15367
15493
  removeRunworkMcpServers(filePath, this.def.mcpConfigKey || "mcpServers");
15368
15494
  }
15369
15495
  }
15370
- if (this.def.skillsPaths && manifest?.skillFilenames.length) {
15371
- const pathTemplate = scope === "project" ? this.def.skillsPaths.project : this.def.skillsPaths.global;
15372
- if (pathTemplate) {
15373
- const baseDir = resolveToAbsolute(pathTemplate, scope === "project" ? "project" : "global");
15374
- if (baseDir && existsSync37(baseDir)) {
15375
- const allowed = new Set(manifest.skillFilenames);
15376
- for (const entry of readdirSync12(baseDir)) {
15377
- if (!allowed.has(entry))
15378
- continue;
15379
- const entryPath = join31(baseDir, entry);
15380
- try {
15381
- rmSync11(entryPath, { recursive: true, force: true });
15382
- } catch {}
15383
- }
15384
- }
15385
- }
15386
- }
15496
+ await this.removeSkills(manifest?.skillFilenames ?? [], scope);
15387
15497
  if (this.def.instructionFile) {
15388
15498
  const pathTemplate = scope === "project" ? this.def.instructionFile.project : this.def.instructionFile.global;
15389
15499
  if (pathTemplate) {
@@ -15459,27 +15569,26 @@ function insightLocalKey(teaches, slug) {
15459
15569
  }
15460
15570
 
15461
15571
  // src/reflect/insight-store.ts
15462
- import { existsSync as existsSync38, mkdirSync as mkdirSync24, readFileSync as readFileSync29, writeFileSync as writeFileSync25 } from "fs";
15463
- import { join as join32 } from "path";
15464
- import { homedir as homedir15 } from "os";
15572
+ init_atomic_json();
15573
+ import { existsSync as existsSync41, readFileSync as readFileSync30 } from "fs";
15574
+ import { join as join34 } from "path";
15575
+ import { homedir as homedir16 } from "os";
15465
15576
  function storePath() {
15466
- return join32(homedir15(), ".runwork", "insights.json");
15577
+ return join34(homedir16(), ".runwork", "insights.json");
15467
15578
  }
15468
15579
  function readAll() {
15469
15580
  const path2 = storePath();
15470
- if (!existsSync38(path2))
15581
+ if (!existsSync41(path2))
15471
15582
  return {};
15472
15583
  try {
15473
- const parsed = JSON.parse(readFileSync29(path2, "utf-8"));
15584
+ const parsed = JSON.parse(readFileSync30(path2, "utf-8"));
15474
15585
  return parsed && typeof parsed === "object" ? parsed : {};
15475
15586
  } catch {
15476
15587
  return {};
15477
15588
  }
15478
15589
  }
15479
15590
  function writeAll(store) {
15480
- const path2 = storePath();
15481
- mkdirSync24(join32(homedir15(), ".runwork"), { recursive: true });
15482
- writeFileSync25(path2, JSON.stringify(store, null, 2));
15591
+ writeJsonAtomic(storePath(), store);
15483
15592
  }
15484
15593
  function notExpired(i, now) {
15485
15594
  const t = new Date(i.expiresAt).getTime();
@@ -15499,37 +15608,43 @@ function loadInsights(opts = {}) {
15499
15608
  const now = Date.now();
15500
15609
  return Object.values(readAll()).filter((i) => notExpired(i, now) && (!opts.workspaceId || i.workspaceId === opts.workspaceId)).sort((a, b) => (b.createdAt ?? "").localeCompare(a.createdAt ?? ""));
15501
15610
  }
15502
- function getInsight(id) {
15611
+ function getInsight(id, opts = {}) {
15503
15612
  const i = readAll()[id];
15504
- return i && notExpired(i, Date.now()) ? i : null;
15613
+ if (!i || !notExpired(i, Date.now()))
15614
+ return null;
15615
+ const allowed = (opts.workspaceIds ?? []).filter((w) => !!w);
15616
+ if (allowed.length && i.workspaceId && i.workspaceId !== "local" && !allowed.includes(i.workspaceId)) {
15617
+ return null;
15618
+ }
15619
+ return i;
15505
15620
  }
15506
15621
 
15507
15622
  // src/reflect/cadence.ts
15508
- import { existsSync as existsSync39, mkdirSync as mkdirSync25, readFileSync as readFileSync30, writeFileSync as writeFileSync26 } from "fs";
15509
- import { join as join33 } from "path";
15510
- import { homedir as homedir16 } from "os";
15623
+ init_atomic_json();
15624
+ import { existsSync as existsSync42, readFileSync as readFileSync31 } from "fs";
15625
+ import { join as join35 } from "path";
15626
+ import { homedir as homedir17 } from "os";
15511
15627
  var DEFAULT_STATE = { enabled: false, lastReflectedAt: null };
15512
15628
  var COOLDOWN_HOURS = 20;
15513
15629
  var ACTIVE_SESSION_THRESHOLD = 10;
15514
15630
  var MAX_INTERVAL_HOURS = 7 * 24;
15515
15631
  var MIN_SESSIONS_FIRST_RUN = 3;
15516
15632
  function statePath() {
15517
- return join33(homedir16(), ".runwork", "reflect-state.json");
15633
+ return join35(homedir17(), ".runwork", "reflect-state.json");
15518
15634
  }
15519
15635
  function loadCadenceState() {
15520
15636
  try {
15521
15637
  const p = statePath();
15522
- if (!existsSync39(p))
15638
+ if (!existsSync42(p))
15523
15639
  return { ...DEFAULT_STATE };
15524
- const parsed = JSON.parse(readFileSync30(p, "utf-8"));
15640
+ const parsed = JSON.parse(readFileSync31(p, "utf-8"));
15525
15641
  return { ...DEFAULT_STATE, ...parsed && typeof parsed === "object" ? parsed : {} };
15526
15642
  } catch {
15527
15643
  return { ...DEFAULT_STATE };
15528
15644
  }
15529
15645
  }
15530
15646
  function saveCadenceState(state) {
15531
- mkdirSync25(join33(homedir16(), ".runwork"), { recursive: true });
15532
- writeFileSync26(statePath(), JSON.stringify(state, null, 2));
15647
+ writeJsonAtomic(statePath(), state);
15533
15648
  }
15534
15649
  function recordReflection(now = new Date) {
15535
15650
  saveCadenceState({ ...loadCadenceState(), lastReflectedAt: now.toISOString() });
@@ -15551,7 +15666,40 @@ function isReflectionDue(state, newSessionCount, now = new Date) {
15551
15666
  return hoursSince >= MAX_INTERVAL_HOURS;
15552
15667
  }
15553
15668
 
15669
+ // src/utils/workspace-state.ts
15670
+ init_atomic_json();
15671
+ import { join as join36 } from "path";
15672
+ import { homedir as homedir18 } from "os";
15673
+ function storePath2() {
15674
+ return join36(homedir18(), ".runwork", "workspaces.json");
15675
+ }
15676
+ function readFile() {
15677
+ const parsed = readJsonOrNull(storePath2());
15678
+ if (parsed && parsed.workspaces && typeof parsed.workspaces === "object")
15679
+ return parsed;
15680
+ return { workspaces: {} };
15681
+ }
15682
+ function loadWorkspaceRecord(workspaceId) {
15683
+ return readFile().workspaces[workspaceId] ?? {};
15684
+ }
15685
+ function updateWorkspaceRecord(workspaceId, patch) {
15686
+ if (!workspaceId)
15687
+ return;
15688
+ const file = readFile();
15689
+ file.workspaces[workspaceId] = { ...file.workspaces[workspaceId], ...patch };
15690
+ writeJsonAtomic(storePath2(), file);
15691
+ }
15692
+ function clearParkedState(workspaceId) {
15693
+ const file = readFile();
15694
+ const record = file.workspaces[workspaceId];
15695
+ if (!record?.parked)
15696
+ return;
15697
+ delete record.parked;
15698
+ writeJsonAtomic(storePath2(), file);
15699
+ }
15700
+
15554
15701
  // src/commands/reflect.ts
15702
+ init_atomic_json();
15555
15703
  init_colors();
15556
15704
  var MAX_NAMES = 40;
15557
15705
  function cap(names) {
@@ -15667,7 +15815,7 @@ function runAnalyst(binary, args, prompt, timeoutMs = ANALYST_TIMEOUT_MS) {
15667
15815
  }
15668
15816
  });
15669
15817
  }
15670
- function enrichInsights(evidence, userSeed) {
15818
+ function enrichInsights(evidence, userSeed, workspaceScope) {
15671
15819
  const insights = evidence?.insights;
15672
15820
  if (!Array.isArray(insights))
15673
15821
  return [];
@@ -15681,7 +15829,7 @@ function enrichInsights(evidence, userSeed) {
15681
15829
  if (!teaches || !slug)
15682
15830
  continue;
15683
15831
  items.push({
15684
- id: computeInsightId(userSeed, insightLocalKey(teaches, slug)),
15832
+ id: computeInsightId(userSeed, `${workspaceScope}:${insightLocalKey(teaches, slug)}`),
15685
15833
  source: "reflection",
15686
15834
  teaches,
15687
15835
  dimension: typeof r.dimension === "string" ? r.dimension : undefined,
@@ -15760,8 +15908,8 @@ var reflectCommand = new Command14("reflect").description("Weekly reflection: an
15760
15908
  const save = (file, content) => {
15761
15909
  if (!outDir)
15762
15910
  return;
15763
- mkdirSync26(outDir, { recursive: true });
15764
- writeFileSync27(join34(outDir, file), content);
15911
+ mkdirSync25(outDir, { recursive: true });
15912
+ writeFileSync25(join37(outDir, file), content);
15765
15913
  };
15766
15914
  const adapters = await detectAgents();
15767
15915
  const digests = [];
@@ -15819,14 +15967,23 @@ Skipping: ${cadence.enabled ? "not enough new work since the last reflection" :
15819
15967
  return;
15820
15968
  }
15821
15969
  }
15970
+ let client;
15971
+ let workspaceId;
15972
+ try {
15973
+ client = new ApiClient(requireAuth());
15974
+ workspaceId = (await resolveWorkspace2(client, {})).workspaceId;
15975
+ } catch {
15976
+ try {
15977
+ workspaceId = requireAuth().defaultWorkspaceId;
15978
+ } catch {}
15979
+ }
15822
15980
  {
15823
- let allowed = loadCadenceState().workspaceReflectionEnabled ?? true;
15981
+ const perWorkspace = workspaceId ? loadWorkspaceRecord(workspaceId).reflectionEnabled : undefined;
15982
+ let allowed = perWorkspace ?? loadCadenceState().workspaceReflectionEnabled ?? true;
15824
15983
  try {
15825
- const client = new ApiClient(requireAuth());
15826
- const { workspaceId } = await resolveWorkspace2(client, {});
15827
- if (workspaceId) {
15984
+ if (client && workspaceId) {
15828
15985
  allowed = (await client.getReflectionSetting(workspaceId)).reflectionEnabled;
15829
- saveCadenceState({ ...loadCadenceState(), workspaceReflectionEnabled: allowed });
15986
+ updateWorkspaceRecord(workspaceId, { reflectionEnabled: allowed });
15830
15987
  }
15831
15988
  } catch {}
15832
15989
  if (!allowed) {
@@ -15901,21 +16058,11 @@ Analyst failed: ${e instanceof Error ? e.message : String(e)}`));
15901
16058
  return "local";
15902
16059
  }
15903
16060
  })();
15904
- const items = evidence ? enrichInsights(evidence, userSeed) : [];
16061
+ const items = evidence ? enrichInsights(evidence, userSeed, workspaceId ?? "local") : [];
15905
16062
  if (evidence)
15906
16063
  save("evidence.json", JSON.stringify(evidence, null, 2));
15907
16064
  save("insights.json", JSON.stringify(items, null, 2));
15908
16065
  if (items.length > 0) {
15909
- let client;
15910
- let workspaceId;
15911
- try {
15912
- client = new ApiClient(requireAuth());
15913
- workspaceId = (await resolveWorkspace2(client, {})).workspaceId;
15914
- } catch {
15915
- try {
15916
- workspaceId = requireAuth().defaultWorkspaceId;
15917
- } catch {}
15918
- }
15919
16066
  const nowIso = new Date().toISOString();
15920
16067
  const expiresIso = new Date(Date.now() + 30 * 24 * 60 * 60 * 1000).toISOString();
15921
16068
  saveInsights(items.map((it) => ({
@@ -15985,8 +16132,9 @@ reflectCommand.command("show [id]").description("Show reflection insights stored
15985
16132
  try {
15986
16133
  workspaceId = requireAuth().defaultWorkspaceId;
15987
16134
  } catch {}
16135
+ const projectWorkspaceId = readJsonOrNull(join37(process.cwd(), ".runwork.json"))?.workspaceId;
15988
16136
  if (id) {
15989
- const insight = getInsight(id);
16137
+ const insight = getInsight(id, { workspaceIds: [projectWorkspaceId, workspaceId] });
15990
16138
  if (!insight) {
15991
16139
  if (wantJson) {
15992
16140
  jsonOut({ error: "not-found", id });
@@ -16027,16 +16175,16 @@ import * as path3 from "node:path";
16027
16175
  init_prompt();
16028
16176
 
16029
16177
  // src/utils/data-input.ts
16030
- import { readFileSync as readFileSync31, existsSync as existsSync40 } from "fs";
16178
+ import { readFileSync as readFileSync32, existsSync as existsSync43 } from "fs";
16031
16179
  async function parseDataInput(dataFlag) {
16032
16180
  if (dataFlag) {
16033
16181
  if (dataFlag.startsWith("@")) {
16034
16182
  const filePath = dataFlag.slice(1);
16035
- if (!existsSync40(filePath)) {
16183
+ if (!existsSync43(filePath)) {
16036
16184
  console.error(`File not found: ${filePath}`);
16037
16185
  process.exit(1);
16038
16186
  }
16039
- const content = readFileSync31(filePath, "utf-8");
16187
+ const content = readFileSync32(filePath, "utf-8");
16040
16188
  return parseJson(content, filePath);
16041
16189
  }
16042
16190
  return parseJson(dataFlag, "--data");
@@ -17051,8 +17199,8 @@ var endpointsCommand = new Command18("endpoints").alias("routes").description("M
17051
17199
  init_store();
17052
17200
  init_client();
17053
17201
  import { Command as Command19 } from "commander";
17054
- import { writeFileSync as writeFileSync29, readFileSync as readFileSync32 } from "fs";
17055
- import { basename as basename2 } from "path";
17202
+ import { writeFileSync as writeFileSync27, readFileSync as readFileSync33 } from "fs";
17203
+ import { basename as basename3 } from "path";
17056
17204
  init_prompt();
17057
17205
  init_http();
17058
17206
  function formatSize(bytes) {
@@ -17138,14 +17286,14 @@ var downloadCommand = new Command19("download").description("Download a file fro
17138
17286
  const credentials = requireAuth();
17139
17287
  const client = new ApiClient(credentials);
17140
17288
  const { workspaceId } = await resolveWorkspace2(client, opts);
17141
- const outputPath = output || basename2(key);
17289
+ const outputPath = output || basename3(key);
17142
17290
  try {
17143
17291
  const { url } = await client.getPresignedUrl(workspaceId, bucket, { action: "read", key });
17144
17292
  const response = await httpFetch(url);
17145
17293
  if (!response.ok) {
17146
17294
  throw new Error(`Download failed: ${response.status} ${response.statusText}`);
17147
17295
  }
17148
- writeFileSync29(outputPath, Buffer.from(await response.arrayBuffer()));
17296
+ writeFileSync27(outputPath, Buffer.from(await response.arrayBuffer()));
17149
17297
  if (useJson) {
17150
17298
  jsonOut({ success: true, bucket, key, outputPath });
17151
17299
  return;
@@ -17161,9 +17309,9 @@ var uploadCommand = new Command19("upload").description("Upload a local file to
17161
17309
  const credentials = requireAuth();
17162
17310
  const client = new ApiClient(credentials);
17163
17311
  const { workspaceId } = await resolveWorkspace2(client, opts);
17164
- const objectKey = key || basename2(localPath);
17312
+ const objectKey = key || basename3(localPath);
17165
17313
  try {
17166
- const fileBuffer = readFileSync32(localPath);
17314
+ const fileBuffer = readFileSync33(localPath);
17167
17315
  const { url } = await client.getPresignedUrl(workspaceId, bucket, { action: "write", key: objectKey });
17168
17316
  const response = await httpFetch(url, { method: "PUT", body: fileBuffer });
17169
17317
  if (!response.ok) {
@@ -17659,21 +17807,21 @@ function truncateUrl(url, max) {
17659
17807
  var mcpCommand = new Command23("mcp").description("Manage workspace MCP servers").addCommand(listCommand11).addCommand(addCommand).addCommand(removeCommand).addCommand(searchCommand3).addCommand(installCommand2);
17660
17808
 
17661
17809
  // src/commands/setup.ts
17810
+ init_atomic_json();
17662
17811
  init_store();
17663
17812
  init_client();
17664
17813
  import { Command as Command25 } from "commander";
17665
- import { writeFileSync as writeFileSync31, mkdirSync as mkdirSync28 } from "fs";
17666
- import { join as join39 } from "path";
17667
- import { homedir as homedir19 } from "os";
17814
+ import { join as join41 } from "path";
17815
+ import { homedir as homedir20 } from "os";
17668
17816
  init_prompt();
17669
17817
 
17670
17818
  // src/commands/sync.ts
17671
17819
  init_store();
17672
17820
  init_client();
17673
17821
  import { Command as Command24 } from "commander";
17674
- import { readFileSync as readFileSync33, writeFileSync as writeFileSync30, existsSync as existsSync42 } from "fs";
17675
- import { join as join37 } from "path";
17676
- import { homedir as homedir17 } from "os";
17822
+ import { readFileSync as readFileSync34, existsSync as existsSync45 } from "fs";
17823
+ import { join as join40 } from "path";
17824
+ import { homedir as homedir19 } from "os";
17677
17825
 
17678
17826
  // src/commands/mcp-entries.ts
17679
17827
  function buildExternalMcpEntries(mcpServers, resolvedCredentials) {
@@ -18651,6 +18799,9 @@ function computeSyncPlan(input) {
18651
18799
  return plan;
18652
18800
  }
18653
18801
 
18802
+ // src/commands/sync.ts
18803
+ init_atomic_json();
18804
+
18654
18805
  // src/sync/conflict-ui.ts
18655
18806
  init_prompt();
18656
18807
  init_colors();
@@ -18837,8 +18988,20 @@ async function executeSyncPlan(plan, resolvedConflicts, ctx) {
18837
18988
  }
18838
18989
  }
18839
18990
  for (const _action of plan.skips) {}
18840
- for (const action of plan.deletions) {
18841
- vlog(` Deleted locally: ${action.name}`);
18991
+ const deletionSlugs = plan.deletions.map((action) => toSlug(action.name));
18992
+ if (deletionSlugs.length) {
18993
+ for (const adapter2 of ctx.adapters) {
18994
+ if (!adapter2.supportsSkills() || !adapter2.removeSkills)
18995
+ continue;
18996
+ for (const scope of ctx.scopes) {
18997
+ try {
18998
+ await adapter2.removeSkills(deletionSlugs, scope);
18999
+ } catch {}
19000
+ }
19001
+ }
19002
+ for (const action of plan.deletions) {
19003
+ vlog(` Deleted locally: ${action.name}`);
19004
+ }
18842
19005
  }
18843
19006
  return newHashes;
18844
19007
  }
@@ -18931,10 +19094,10 @@ Tip: ${hint.title}`);
18931
19094
  } catch {}
18932
19095
  }
18933
19096
  function loadSetupState(filePath) {
18934
- if (!existsSync42(filePath))
19097
+ if (!existsSync45(filePath))
18935
19098
  return null;
18936
19099
  try {
18937
- return JSON.parse(readFileSync33(filePath, "utf-8"));
19100
+ return JSON.parse(readFileSync34(filePath, "utf-8"));
18938
19101
  } catch {
18939
19102
  return null;
18940
19103
  }
@@ -18955,15 +19118,15 @@ function readLocalSkills(state) {
18955
19118
  if (!baseDir)
18956
19119
  continue;
18957
19120
  for (const skillName of state.skills) {
18958
- const skillMdPath = join37(baseDir, skillName, "SKILL.md");
18959
- if (existsSync42(skillMdPath)) {
18960
- results.push({ name: skillName, content: readFileSync33(skillMdPath, "utf-8") });
19121
+ const skillMdPath = join40(baseDir, skillName, "SKILL.md");
19122
+ if (existsSync45(skillMdPath)) {
19123
+ results.push({ name: skillName, content: readFileSync34(skillMdPath, "utf-8") });
18961
19124
  continue;
18962
19125
  }
18963
19126
  const filename = skillName.toLowerCase().replace(/[^a-z0-9]+/g, "-");
18964
- const flatPath = join37(baseDir, `${filename}.md`);
18965
- if (existsSync42(flatPath)) {
18966
- results.push({ name: skillName, content: readFileSync33(flatPath, "utf-8") });
19127
+ const flatPath = join40(baseDir, `${filename}.md`);
19128
+ if (existsSync45(flatPath)) {
19129
+ results.push({ name: skillName, content: readFileSync34(flatPath, "utf-8") });
18967
19130
  }
18968
19131
  }
18969
19132
  if (results.length > 0)
@@ -19167,9 +19330,9 @@ async function syncFromState(state, statePath2, credentials, opts) {
19167
19330
  persona: state.persona
19168
19331
  });
19169
19332
  let projectAppSkillFilter = null;
19170
- if (existsSync42(".runwork.json")) {
19333
+ if (existsSync45(".runwork.json")) {
19171
19334
  try {
19172
- const config = JSON.parse(readFileSync33(".runwork.json", "utf-8"));
19335
+ const config = JSON.parse(readFileSync34(".runwork.json", "utf-8"));
19173
19336
  if (config.appName) {
19174
19337
  projectAppSkillFilter = config.appName.toLowerCase().replace(/[^a-z0-9]+/g, "-");
19175
19338
  }
@@ -19406,7 +19569,7 @@ async function syncFromState(state, statePath2, credentials, opts) {
19406
19569
  }
19407
19570
  for (const adapter2 of adapters) {
19408
19571
  if (adapter2 instanceof CodexAdapter) {
19409
- const runworkDir = join37(homedir17(), ".runwork");
19572
+ const runworkDir = join40(homedir19(), ".runwork");
19410
19573
  const result = adapter2.registerDesktopWorkspace(runworkDir, "Runwork");
19411
19574
  if (result === "written") {
19412
19575
  vlog(` [${adapter2.name}] Registered workspace in Codex desktop app`);
@@ -19481,7 +19644,7 @@ async function syncFromState(state, statePath2, credentials, opts) {
19481
19644
  delete mergedHashes[del.name];
19482
19645
  }
19483
19646
  state.skillHashes = mergedHashes;
19484
- writeFileSync30(statePath2, JSON.stringify(state, null, 2));
19647
+ writeJsonAtomic(statePath2, state);
19485
19648
  try {
19486
19649
  const telemetryNow = new Date().toISOString();
19487
19650
  const telemetry = await collectTelemetryEvents({
@@ -19501,7 +19664,13 @@ async function syncFromState(state, statePath2, credentials, opts) {
19501
19664
  if (telemetry.healthReported) {
19502
19665
  state.lastHealthReportAt = new Date().toISOString();
19503
19666
  }
19504
- writeFileSync30(statePath2, JSON.stringify(state, null, 2));
19667
+ writeJsonAtomic(statePath2, state);
19668
+ updateWorkspaceRecord(state.workspaceId, {
19669
+ workspaceName: state.workspaceName || undefined,
19670
+ workspaceSlug: state.workspaceSlug,
19671
+ lastTelemetryAt: state.lastTelemetryAt,
19672
+ lastHealthReportAt: state.lastHealthReportAt
19673
+ });
19505
19674
  } catch {}
19506
19675
  const failedNote = summary.adaptersFailed > 0 ? ` (${summary.adaptersFailed} failed)` : "";
19507
19676
  if (isVerbose()) {
@@ -19539,8 +19708,8 @@ var syncCommand = new Command24("sync").description("Sync skills bidirectionally
19539
19708
  verbose: !!opts.verbose,
19540
19709
  redetect: !!opts.redetect
19541
19710
  };
19542
- const projectStatePath = join37(process.cwd(), ".runwork", "setup.json");
19543
- const userStatePath = join37(homedir17(), ".runwork", "setup.json");
19711
+ const projectStatePath = join40(process.cwd(), ".runwork", "setup.json");
19712
+ const userStatePath = join40(homedir19(), ".runwork", "setup.json");
19544
19713
  const projectState = loadSetupState(projectStatePath);
19545
19714
  const userState = loadSetupState(userStatePath);
19546
19715
  if (!projectState && !userState) {
@@ -19560,25 +19729,6 @@ Sync complete.`);
19560
19729
  }
19561
19730
  });
19562
19731
 
19563
- // src/utils/setup-state.ts
19564
- import { existsSync as existsSync43, readFileSync as readFileSync34 } from "fs";
19565
- import { join as join38 } from "path";
19566
- import { homedir as homedir18 } from "os";
19567
- function loadSetupState2() {
19568
- const projectPath = join38(process.cwd(), ".runwork", "setup.json");
19569
- const userPath = join38(homedir18(), ".runwork", "setup.json");
19570
- for (const p of [projectPath, userPath]) {
19571
- if (existsSync43(p)) {
19572
- try {
19573
- return JSON.parse(readFileSync34(p, "utf-8"));
19574
- } catch {
19575
- continue;
19576
- }
19577
- }
19578
- }
19579
- return null;
19580
- }
19581
-
19582
19732
  // src/commands/setup.ts
19583
19733
  var PERSONA_LABELS = {
19584
19734
  1: "everyday",
@@ -19594,7 +19744,7 @@ function resolvePersona(flag, existing) {
19594
19744
  }
19595
19745
  return existing;
19596
19746
  }
19597
- async function resolveAndPersistWorkspace(client, opts) {
19747
+ async function resolveSetupWorkspace(client, opts) {
19598
19748
  const resolved = await resolveWorkspace2(client, opts);
19599
19749
  const { workspaceId } = resolved;
19600
19750
  let allWorkspaces = [];
@@ -19606,29 +19756,89 @@ async function resolveAndPersistWorkspace(client, opts) {
19606
19756
  console.error(`Workspace "${opts.workspace}" not found or not accessible.`);
19607
19757
  process.exit(1);
19608
19758
  }
19609
- const workspaceName = resolved.workspaceName || workspaceMeta?.name || "";
19610
- const workspaceSlug = workspaceMeta?.slug;
19759
+ return {
19760
+ workspaceId,
19761
+ workspaceName: resolved.workspaceName || workspaceMeta?.name || "",
19762
+ workspaceSlug: workspaceMeta?.slug
19763
+ };
19764
+ }
19765
+ function persistDefaultWorkspace(workspaceId, workspaceName) {
19611
19766
  const freshCreds = getCredentials();
19612
- if (freshCreds) {
19613
- saveCredentials({
19614
- ...freshCreds,
19615
- defaultWorkspaceId: workspaceId,
19616
- defaultWorkspaceName: workspaceName
19617
- });
19767
+ if (!freshCreds)
19768
+ return;
19769
+ saveCredentials({
19770
+ ...freshCreds,
19771
+ defaultWorkspaceId: workspaceId,
19772
+ defaultWorkspaceName: workspaceName
19773
+ });
19774
+ }
19775
+ function toSkillFilename(name) {
19776
+ return name.toLowerCase().replace(/[^a-z0-9]+/g, "-");
19777
+ }
19778
+ function loadSetupStateForScope(scope) {
19779
+ const path4 = scope === "project" ? join41(process.cwd(), ".runwork", "setup.json") : join41(homedir20(), ".runwork", "setup.json");
19780
+ return readJsonOrNull(path4);
19781
+ }
19782
+ async function parkAndTeardownWorkspace(previous, scopes) {
19783
+ const cleanupScopes = scopes ?? (previous.scope === "both" ? ["project", "user"] : [previous.scope ?? "user"]);
19784
+ const skillFilenames = previous.skillFilenames ?? (previous.skills ?? []).map(toSkillFilename);
19785
+ const patch = {
19786
+ parked: {
19787
+ skillFilenames,
19788
+ mcpServers: previous.mcpServers ?? [],
19789
+ agentDefaults: previous.agentDefaults,
19790
+ agentDefaultsVersion: previous.agentDefaultsVersion,
19791
+ parkedAt: new Date().toISOString()
19792
+ }
19793
+ };
19794
+ if (previous.workspaceName)
19795
+ patch.workspaceName = previous.workspaceName;
19796
+ if (previous.workspaceSlug)
19797
+ patch.workspaceSlug = previous.workspaceSlug;
19798
+ if (previous.lastTelemetryAt)
19799
+ patch.lastTelemetryAt = previous.lastTelemetryAt;
19800
+ if (previous.lastHealthReportAt)
19801
+ patch.lastHealthReportAt = previous.lastHealthReportAt;
19802
+ updateWorkspaceRecord(previous.workspaceId, patch);
19803
+ const manifest = {
19804
+ skillFilenames,
19805
+ mcpServerNames: previous.mcpServers ?? []
19806
+ };
19807
+ for (const slug of previous.configuredAgents ?? []) {
19808
+ const adapter2 = getAdapterBySlug(slug);
19809
+ if (!adapter2?.cleanup)
19810
+ continue;
19811
+ for (const scope of cleanupScopes) {
19812
+ try {
19813
+ await adapter2.cleanup(scope, manifest);
19814
+ console.log(` [${adapter2.name}] Previous workspace removed (${scope})`);
19815
+ } catch (err) {
19816
+ console.warn(` [${adapter2.name}] Cleanup failed (${scope}): ${err instanceof Error ? err.message : err}`);
19817
+ }
19818
+ }
19819
+ }
19820
+ }
19821
+ function resolveTelemetryCursor(carry, targetRecord, now = new Date) {
19822
+ if (carry?.lastTelemetryAt)
19823
+ return carry.lastTelemetryAt;
19824
+ const watermark = targetRecord.lastTelemetryAt;
19825
+ const parkedAt = targetRecord.parked?.parkedAt;
19826
+ if (watermark && parkedAt) {
19827
+ return Date.parse(parkedAt) > Date.parse(watermark) ? parkedAt : watermark;
19618
19828
  }
19619
- return { workspaceId, workspaceName, workspaceSlug };
19829
+ return parkedAt ?? watermark ?? now.toISOString();
19620
19830
  }
19621
- var setupCommand = new Command25("setup").description("Configure local AI agents with workspace skills and MCP servers").option("--workspace <name-or-id>", "Workspace name or ID").option("--agent <slug>", "Only configure a specific agent (e.g. claude-code, cursor)").option("--dry-run", "Show what would be configured without writing files").option("-y, --yes", "Skip all prompts, configure all detected agents with user scope").option("--persona <level>", "Technical-level persona for agent instructions (1=everyday, 2=curious, 3=engineer)").action(async (opts) => {
19831
+ async function runSetup(opts) {
19622
19832
  const credentials = requireAuth();
19623
19833
  const client = new ApiClient(credentials);
19624
- const { workspaceId, workspaceName, workspaceSlug } = await resolveAndPersistWorkspace(client, opts);
19834
+ const { workspaceId, workspaceName, workspaceSlug } = await resolveSetupWorkspace(client, opts);
19625
19835
  console.log(`
19626
19836
  Workspace: ${workspaceName || workspaceId}
19627
19837
  `);
19628
19838
  let agents = await detectAgents();
19629
19839
  if (agents.length === 0) {
19630
19840
  printNoAgentsMessage();
19631
- process.exit(0);
19841
+ process.exit(1);
19632
19842
  }
19633
19843
  if (opts.agent) {
19634
19844
  agents = agents.filter((a) => a.slug === opts.agent);
@@ -19660,8 +19870,8 @@ Configuring all agents (--yes).
19660
19870
  }
19661
19871
  agents = kept;
19662
19872
  if (agents.length === 0) {
19663
- console.log("No agents selected.");
19664
- return;
19873
+ console.log("No agents selected. Nothing was changed.");
19874
+ process.exit(1);
19665
19875
  }
19666
19876
  }
19667
19877
  }
@@ -19675,6 +19885,37 @@ Configuring all agents (--yes).
19675
19885
  const chosen = await promptSelect("Configure for:", scopeChoices);
19676
19886
  scope = chosen.value;
19677
19887
  }
19888
+ const scopes = scope === "both" ? ["project", "user"] : [scope];
19889
+ const outgoing = new Map;
19890
+ for (const s of scopes) {
19891
+ const prior = loadSetupStateForScope(s);
19892
+ if (prior?.workspaceId && prior.workspaceId !== workspaceId) {
19893
+ outgoing.set(s, prior);
19894
+ }
19895
+ }
19896
+ const previous = loadSetupStateForScope(scopes.includes("user") ? "user" : "project");
19897
+ const isSwitch = outgoing.size > 0;
19898
+ if (isSwitch && !opts.dryRun) {
19899
+ const names = [...new Set([...outgoing.values()].map((p) => p.workspaceName || p.workspaceId))];
19900
+ console.log(`Switching workspace: ${names.join(", ")} -> ${workspaceName || workspaceId}`);
19901
+ console.log(`Removing the previous workspace's skills and connections from your AI tools...
19902
+ `);
19903
+ for (const [s, prior] of outgoing) {
19904
+ await parkAndTeardownWorkspace(prior, [s]);
19905
+ }
19906
+ const trashed = currentTrashBatch();
19907
+ if (trashed) {
19908
+ console.log(` A copy of everything removed is kept for ${TRASH_RETENTION_DAYS} days in:`);
19909
+ console.log(` ${trashed}`);
19910
+ }
19911
+ console.log("");
19912
+ } else if (isSwitch && opts.dryRun) {
19913
+ const names = [...new Set([...outgoing.values()].map((p) => p.workspaceName || p.workspaceId))];
19914
+ console.log(`[Dry run] Would switch workspace: ${names.join(", ")} -> ${workspaceName || workspaceId} (teardown + re-setup).`);
19915
+ }
19916
+ const carry = !isSwitch && previous?.workspaceId === workspaceId ? previous : undefined;
19917
+ const targetRecord = loadWorkspaceRecord(workspaceId);
19918
+ const restored = carry ? undefined : targetRecord.parked;
19678
19919
  const state = {
19679
19920
  workspaceId,
19680
19921
  workspaceName: workspaceName || "",
@@ -19684,19 +19925,17 @@ Configuring all agents (--yes).
19684
19925
  lastSyncAt: "",
19685
19926
  mcpServers: [],
19686
19927
  skills: [],
19687
- skillHashes: {},
19928
+ skillHashes: carry?.skillHashes ?? {},
19929
+ agentDefaults: carry?.agentDefaults ?? restored?.agentDefaults,
19930
+ agentDefaultsVersion: carry?.agentDefaultsVersion ?? restored?.agentDefaultsVersion,
19931
+ lastTelemetryAt: resolveTelemetryCursor(carry, targetRecord),
19932
+ lastHealthReportAt: carry?.lastHealthReportAt ?? targetRecord.lastHealthReportAt,
19688
19933
  lastDetectedAt: new Date().toISOString(),
19689
- persona: resolvePersona(opts.persona, loadSetupState2()?.persona)
19934
+ persona: resolvePersona(opts.persona, previous?.persona)
19690
19935
  };
19691
- const scopes = scope === "both" ? ["project", "user"] : [scope];
19692
- for (const s of scopes) {
19693
- const dir = s === "project" ? ".runwork" : join39(homedir19(), ".runwork");
19694
- mkdirSync28(dir, { recursive: true });
19695
- writeFileSync31(join39(dir, "setup.json"), JSON.stringify(state, null, 2));
19696
- }
19697
19936
  if (opts.dryRun) {
19698
19937
  console.log(`
19699
- [Dry run] Saved setup state. Would sync:
19938
+ [Dry run] Nothing written. Would save setup state and sync:
19700
19939
  `);
19701
19940
  console.log(` Agents: ${agents.map((a) => a.name).join(", ")}`);
19702
19941
  console.log(` Scope: ${scope}`);
@@ -19704,11 +19943,18 @@ Configuring all agents (--yes).
19704
19943
  Re-run without --dry-run to sync workspace data.`);
19705
19944
  return;
19706
19945
  }
19946
+ persistDefaultWorkspace(workspaceId, workspaceName);
19947
+ for (const s of scopes) {
19948
+ const dir = s === "project" ? ".runwork" : join41(homedir20(), ".runwork");
19949
+ writeJsonAtomic(join41(dir, "setup.json"), state);
19950
+ }
19951
+ if (restored)
19952
+ clearParkedState(workspaceId);
19707
19953
  console.log(`
19708
19954
  Syncing workspace data...
19709
19955
  `);
19710
19956
  for (const s of scopes) {
19711
- const statePath2 = s === "project" ? join39(process.cwd(), ".runwork", "setup.json") : join39(homedir19(), ".runwork", "setup.json");
19957
+ const statePath2 = s === "project" ? join41(process.cwd(), ".runwork", "setup.json") : join41(homedir20(), ".runwork", "setup.json");
19712
19958
  await syncFromState(state, statePath2, credentials, {
19713
19959
  dryRun: false,
19714
19960
  pullOnly: true,
@@ -19719,25 +19965,94 @@ Syncing workspace data...
19719
19965
  }
19720
19966
  console.log("\nSetup complete. Run `runwork sync` anytime to refresh.");
19721
19967
  await printAdoptionHint(credentials, workspaceId);
19968
+ }
19969
+ var setupCommand = new Command25("setup").description("Configure local AI agents with workspace skills and MCP servers").option("--workspace <name-or-id>", "Workspace name or ID").option("--agent <slug>", "Only configure a specific agent (e.g. claude-code, cursor)").option("--dry-run", "Show what would be configured without writing files").option("-y, --yes", "Skip all prompts, configure all detected agents with user scope").option("--persona <level>", "Technical-level persona for agent instructions (1=everyday, 2=curious, 3=engineer)").action(async (opts) => runSetup(opts));
19970
+
19971
+ // src/commands/workspace.ts
19972
+ init_store();
19973
+ init_client();
19974
+ import { Command as Command26 } from "commander";
19975
+
19976
+ // src/utils/setup-state.ts
19977
+ import { existsSync as existsSync46, readFileSync as readFileSync35 } from "fs";
19978
+ import { join as join42 } from "path";
19979
+ import { homedir as homedir21 } from "os";
19980
+ function loadSetupState2() {
19981
+ const projectPath = join42(process.cwd(), ".runwork", "setup.json");
19982
+ const userPath = join42(homedir21(), ".runwork", "setup.json");
19983
+ for (const p of [projectPath, userPath]) {
19984
+ if (existsSync46(p)) {
19985
+ try {
19986
+ return JSON.parse(readFileSync35(p, "utf-8"));
19987
+ } catch {
19988
+ continue;
19989
+ }
19990
+ }
19991
+ }
19992
+ return null;
19993
+ }
19994
+
19995
+ // src/commands/workspace.ts
19996
+ var workspaceCommand = new Command26("workspace").description("Inspect or switch the workspace this machine is set up for");
19997
+ workspaceCommand.command("list").description("List workspaces you belong to (the active one is marked)").action(async (_opts, command) => {
19998
+ const json = command.optsWithGlobals().json === true;
19999
+ const credentials = requireAuth();
20000
+ const client = new ApiClient(credentials);
20001
+ const workspaces = await client.listWorkspaces();
20002
+ const activeId = loadSetupState2()?.workspaceId ?? credentials.defaultWorkspaceId;
20003
+ if (json) {
20004
+ jsonOut({
20005
+ workspaces: workspaces.map((w) => ({ id: w.id, name: w.name, slug: w.slug, active: w.id === activeId }))
20006
+ });
20007
+ return;
20008
+ }
20009
+ if (workspaces.length === 0) {
20010
+ console.log("No workspaces found for this account.");
20011
+ return;
20012
+ }
20013
+ for (const w of workspaces) {
20014
+ const marker = w.id === activeId ? "*" : " ";
20015
+ console.log(` ${marker} ${w.name} (${w.id})`);
20016
+ }
20017
+ console.log("\n* = active on this machine. Switch with `runwork workspace switch <name-or-id>`.");
20018
+ });
20019
+ workspaceCommand.command("current").description("Show the workspace this machine is currently set up for").action(async (_opts, command) => {
20020
+ const json = command.optsWithGlobals().json === true;
20021
+ const state = loadSetupState2();
20022
+ const credentials = requireAuth();
20023
+ const workspaceId = state?.workspaceId ?? credentials.defaultWorkspaceId ?? null;
20024
+ const workspaceName = state?.workspaceName || credentials.defaultWorkspaceName || null;
20025
+ if (json) {
20026
+ jsonOut({ workspaceId, workspaceName, source: state?.workspaceId ? "setup" : "credentials" });
20027
+ return;
20028
+ }
20029
+ if (!workspaceId) {
20030
+ console.log("This machine is not set up for a workspace yet. Run `runwork setup`.");
20031
+ return;
20032
+ }
20033
+ console.log(`${workspaceName || workspaceId} (${workspaceId})`);
20034
+ });
20035
+ workspaceCommand.command("switch <name-or-id>").description("Re-setup this machine for another workspace (removes the current workspace's skills and connections from your AI tools, then sets up the new one)").option("--persona <level>", "Technical-level persona for agent instructions (1=everyday, 2=curious, 3=engineer)").action(async (target, opts) => {
20036
+ await runSetup({ workspace: target, yes: true, persona: opts.persona });
19722
20037
  });
19723
20038
 
19724
20039
  // src/commands/build-plugin.ts
19725
20040
  init_store();
19726
20041
  init_client();
19727
- import { Command as Command26 } from "commander";
19728
- import { existsSync as existsSync44, readFileSync as readFileSync35 } from "fs";
19729
- import { resolve as resolve3, join as join40 } from "path";
19730
- import { homedir as homedir20 } from "os";
20042
+ import { Command as Command27 } from "commander";
20043
+ import { existsSync as existsSync47, readFileSync as readFileSync36 } from "fs";
20044
+ import { resolve as resolve3, join as join43 } from "path";
20045
+ import { homedir as homedir22 } from "os";
19731
20046
  function loadSetupState3(filePath) {
19732
- if (!existsSync44(filePath))
20047
+ if (!existsSync47(filePath))
19733
20048
  return null;
19734
20049
  try {
19735
- return JSON.parse(readFileSync35(filePath, "utf-8"));
20050
+ return JSON.parse(readFileSync36(filePath, "utf-8"));
19736
20051
  } catch {
19737
20052
  return null;
19738
20053
  }
19739
20054
  }
19740
- var buildPluginCommand = new Command26("build-plugin").description("Build an installable plugin archive for an agent (e.g. Claude Desktop)").requiredOption("--agent <slug>", "Target agent slug (e.g. claude-desktop)").option("--output <path>", "Output zip path", "runwork-plugin.zip").action(async (opts) => {
20055
+ var buildPluginCommand = new Command27("build-plugin").description("Build an installable plugin archive for an agent (e.g. Claude Desktop)").requiredOption("--agent <slug>", "Target agent slug (e.g. claude-desktop)").option("--output <path>", "Output zip path", "runwork-plugin.zip").action(async (opts) => {
19741
20056
  const adapter2 = getAdapterBySlug(opts.agent);
19742
20057
  if (!adapter2) {
19743
20058
  console.error(`Unknown agent: ${opts.agent}`);
@@ -19748,8 +20063,8 @@ var buildPluginCommand = new Command26("build-plugin").description("Build an ins
19748
20063
  process.exit(1);
19749
20064
  }
19750
20065
  const credentials = requireAuth();
19751
- const projectStatePath = join40(process.cwd(), ".runwork", "setup.json");
19752
- const userStatePath = join40(homedir20(), ".runwork", "setup.json");
20066
+ const projectStatePath = join43(process.cwd(), ".runwork", "setup.json");
20067
+ const userStatePath = join43(homedir22(), ".runwork", "setup.json");
19753
20068
  const state = loadSetupState3(projectStatePath) ?? loadSetupState3(userStatePath);
19754
20069
  if (!state) {
19755
20070
  console.error("No setup state found. Run `runwork setup` first.");
@@ -19838,23 +20153,23 @@ var buildPluginCommand = new Command26("build-plugin").description("Build an ins
19838
20153
  });
19839
20154
 
19840
20155
  // src/commands/uninstall.ts
19841
- import { Command as Command27 } from "commander";
19842
- import { existsSync as existsSync45, readFileSync as readFileSync36, rmSync as rmSync12, unlinkSync as unlinkSync7 } from "fs";
19843
- import { join as join41 } from "path";
19844
- import { homedir as homedir21 } from "os";
20156
+ import { Command as Command28 } from "commander";
20157
+ import { existsSync as existsSync48, readFileSync as readFileSync37, rmSync as rmSync13, unlinkSync as unlinkSync7 } from "fs";
20158
+ import { join as join44 } from "path";
20159
+ import { homedir as homedir23 } from "os";
19845
20160
  init_prompt();
19846
20161
  function loadSetupState4(filePath) {
19847
- if (!existsSync45(filePath))
20162
+ if (!existsSync48(filePath))
19848
20163
  return null;
19849
20164
  try {
19850
- return JSON.parse(readFileSync36(filePath, "utf-8"));
20165
+ return JSON.parse(readFileSync37(filePath, "utf-8"));
19851
20166
  } catch {
19852
20167
  return null;
19853
20168
  }
19854
20169
  }
19855
- var uninstallCommand = new Command27("uninstall").description("Remove all Runwork configuration from local agents (MCP servers, skills, instructions)").option("-y, --yes", "Skip confirmation prompt").option("--keep-auth", "Keep authentication credentials (only remove agent configs)").action(async (opts) => {
19856
- const projectStatePath = join41(process.cwd(), ".runwork", "setup.json");
19857
- const userStatePath = join41(homedir21(), ".runwork", "setup.json");
20170
+ var uninstallCommand = new Command28("uninstall").description("Remove all Runwork configuration from local agents (MCP servers, skills, instructions)").option("-y, --yes", "Skip confirmation prompt").option("--keep-auth", "Keep authentication credentials (only remove agent configs)").action(async (opts) => {
20171
+ const projectStatePath = join44(process.cwd(), ".runwork", "setup.json");
20172
+ const userStatePath = join44(homedir23(), ".runwork", "setup.json");
19858
20173
  const projectState = loadSetupState4(projectStatePath);
19859
20174
  const userState = loadSetupState4(userStatePath);
19860
20175
  if (!projectState && !userState) {
@@ -19934,10 +20249,10 @@ This will remove all Runwork configuration from your local agents:
19934
20249
  }
19935
20250
  }
19936
20251
  }
19937
- const stateDir = label === "project" ? join41(process.cwd(), ".runwork") : join41(homedir21(), ".runwork");
20252
+ const stateDir = label === "project" ? join44(process.cwd(), ".runwork") : join44(homedir23(), ".runwork");
19938
20253
  if (opts.keepAuth && label === "user") {
19939
- const setupFile = join41(stateDir, "setup.json");
19940
- if (existsSync45(setupFile)) {
20254
+ const setupFile = join44(stateDir, "setup.json");
20255
+ if (existsSync48(setupFile)) {
19941
20256
  try {
19942
20257
  unlinkSync7(setupFile);
19943
20258
  console.log(` Removed ${setupFile} (kept credentials)`);
@@ -19946,9 +20261,9 @@ This will remove all Runwork configuration from your local agents:
19946
20261
  errors++;
19947
20262
  }
19948
20263
  }
19949
- } else if (existsSync45(stateDir)) {
20264
+ } else if (existsSync48(stateDir)) {
19950
20265
  try {
19951
- rmSync12(stateDir, { recursive: true, force: true });
20266
+ rmSync13(stateDir, { recursive: true, force: true });
19952
20267
  console.log(` Removed ${stateDir}`);
19953
20268
  } catch (err) {
19954
20269
  console.warn(` Failed to remove ${stateDir}: ${err instanceof Error ? err.message : err}`);
@@ -19968,9 +20283,9 @@ This will remove all Runwork configuration from your local agents:
19968
20283
  // src/commands/apps.ts
19969
20284
  init_store();
19970
20285
  init_client();
19971
- import { Command as Command28 } from "commander";
20286
+ import { Command as Command29 } from "commander";
19972
20287
  init_init();
19973
- var listCommand12 = new Command28("list").description("List apps in workspace").option("--workspace <name-or-id>", "Workspace name or ID").action(async (opts, command) => {
20288
+ var listCommand12 = new Command29("list").description("List apps in workspace").option("--workspace <name-or-id>", "Workspace name or ID").action(async (opts, command) => {
19974
20289
  const useJson = shouldOutputJson(command.optsWithGlobals().json);
19975
20290
  const credentials = requireAuth();
19976
20291
  const client = new ApiClient(credentials);
@@ -20000,10 +20315,10 @@ Workspace: ${workspaceName || workspaceId}
20000
20315
  process.exit(1);
20001
20316
  }
20002
20317
  });
20003
- var createCommand3 = new Command28("create").description("Create a new Runwork app").argument("[name]", "App name").action(async (name) => {
20318
+ var createCommand3 = new Command29("create").description("Create a new Runwork app").argument("[name]", "App name").action(async (name) => {
20004
20319
  await runCreateFlow(name);
20005
20320
  });
20006
- var infoCommand2 = new Command28("info").description("Show detailed app info, preview status, and registries").argument("[app]", "App ID, name, or slug").option("--workspace <name-or-id>", "Workspace name or ID").action(async (appArg, opts, command) => {
20321
+ var infoCommand2 = new Command29("info").description("Show detailed app info, preview status, and registries").argument("[app]", "App ID, name, or slug").option("--workspace <name-or-id>", "Workspace name or ID").action(async (appArg, opts, command) => {
20007
20322
  const useJson = shouldOutputJson(command.optsWithGlobals().json);
20008
20323
  const credentials = requireAuth();
20009
20324
  const client = new ApiClient(credentials);
@@ -20028,12 +20343,12 @@ var infoCommand2 = new Command28("info").description("Show detailed app info, pr
20028
20343
  }
20029
20344
  printAppInfo(data);
20030
20345
  });
20031
- var appsCommand = new Command28("apps").description("Manage workspace apps").addCommand(listCommand12).addCommand(createCommand3).addCommand(infoCommand2);
20346
+ var appsCommand = new Command29("apps").description("Manage workspace apps").addCommand(listCommand12).addCommand(createCommand3).addCommand(infoCommand2);
20032
20347
 
20033
20348
  // src/commands/members.ts
20034
20349
  init_store();
20035
20350
  init_client();
20036
- import { Command as Command29 } from "commander";
20351
+ import { Command as Command30 } from "commander";
20037
20352
  function formatMemberRows(members) {
20038
20353
  return members.map((m) => ({
20039
20354
  name: m.user.displayName || m.user.email,
@@ -20043,7 +20358,7 @@ function formatMemberRows(members) {
20043
20358
  userId: m.userId
20044
20359
  }));
20045
20360
  }
20046
- var listCommand13 = new Command29("list").description("List members of a workspace (name, email, role)").option("--workspace <name-or-id>", "Workspace name or ID").action(async (opts, command) => {
20361
+ var listCommand13 = new Command30("list").description("List members of a workspace (name, email, role)").option("--workspace <name-or-id>", "Workspace name or ID").action(async (opts, command) => {
20047
20362
  const useJson = shouldOutputJson(command.optsWithGlobals().json);
20048
20363
  const credentials = requireAuth();
20049
20364
  const client = new ApiClient(credentials);
@@ -20072,13 +20387,13 @@ Workspace: ${workspaceName || workspaceId}
20072
20387
  process.exit(1);
20073
20388
  }
20074
20389
  });
20075
- var membersCommand = new Command29("members").description("List workspace members").addCommand(listCommand13);
20390
+ var membersCommand = new Command30("members").description("List workspace members").addCommand(listCommand13);
20076
20391
 
20077
20392
  // src/commands/api.ts
20078
20393
  init_store();
20079
20394
  init_client();
20080
- import { Command as Command30 } from "commander";
20081
- import { readFileSync as readFileSync37 } from "fs";
20395
+ import { Command as Command31 } from "commander";
20396
+ import { readFileSync as readFileSync38 } from "fs";
20082
20397
  function normalizeApiPath(rawPath, baseUrl) {
20083
20398
  if (/^https?:\/\//i.test(rawPath)) {
20084
20399
  const target = new URL(rawPath);
@@ -20090,7 +20405,7 @@ function normalizeApiPath(rawPath, baseUrl) {
20090
20405
  }
20091
20406
  return rawPath.startsWith("/") ? rawPath : `/${rawPath}`;
20092
20407
  }
20093
- var apiCommand = new Command30("api").description("Make an authenticated request to the Runwork platform API (escape hatch for endpoints the CLI does not cover)").argument("<method>", "HTTP method (GET, POST, PUT, DELETE, etc.)").argument("[path]", "API path (e.g. /api/workspaces); optional with --curl").option("--body <json>", "Request body JSON (or @file.json)").option("--header <header>", 'Request header (format: "Key: Value", repeatable)', (val, prev) => [...prev, val], []).option("--query <query>", "Query string (e.g. limit=10&offset=0)").option("--curl <command>", "Parse a curl command (method, path, headers, body)").option("--curl-file <file>", "Read curl command from a file").addHelpText("after", `
20408
+ var apiCommand = new Command31("api").description("Make an authenticated request to the Runwork platform API (escape hatch for endpoints the CLI does not cover)").argument("<method>", "HTTP method (GET, POST, PUT, DELETE, etc.)").argument("[path]", "API path (e.g. /api/workspaces); optional with --curl").option("--body <json>", "Request body JSON (or @file.json)").option("--header <header>", 'Request header (format: "Key: Value", repeatable)', (val, prev) => [...prev, val], []).option("--query <query>", "Query string (e.g. limit=10&offset=0)").option("--curl <command>", "Parse a curl command (method, path, headers, body)").option("--curl-file <file>", "Read curl command from a file").addHelpText("after", `
20094
20409
  Examples:
20095
20410
  runwork api GET /api/workspaces
20096
20411
  runwork api GET /api/workspaces/<id>/members
@@ -20111,7 +20426,7 @@ to be read or pasted manually. Prefer a dedicated command when one exists
20111
20426
  let curlStr = opts.curl;
20112
20427
  if (opts.curlFile) {
20113
20428
  try {
20114
- curlStr = readFileSync37(opts.curlFile, "utf-8");
20429
+ curlStr = readFileSync38(opts.curlFile, "utf-8");
20115
20430
  } catch (err) {
20116
20431
  console.error(`Could not read --curl-file: ${err instanceof Error ? err.message : err}`);
20117
20432
  process.exit(1);
@@ -20131,7 +20446,7 @@ to be read or pasted manually. Prefer a dedicated command when one exists
20131
20446
  let raw = opts.body;
20132
20447
  if (raw.startsWith("@")) {
20133
20448
  try {
20134
- raw = readFileSync37(raw.slice(1), "utf-8");
20449
+ raw = readFileSync38(raw.slice(1), "utf-8");
20135
20450
  } catch (err) {
20136
20451
  console.error(`Could not read body file: ${err instanceof Error ? err.message : err}`);
20137
20452
  process.exit(1);
@@ -20173,16 +20488,16 @@ to be read or pasted manually. Prefer a dedicated command when one exists
20173
20488
 
20174
20489
  // src/commands/doctor.ts
20175
20490
  init_colors();
20176
- import { Command as Command31 } from "commander";
20491
+ import { Command as Command32 } from "commander";
20177
20492
 
20178
20493
  // src/health/checks.ts
20179
20494
  init_subprocess();
20180
20495
  init_store();
20181
20496
  init_client();
20182
20497
  import { parse as parse2 } from "smol-toml";
20183
- import { existsSync as existsSync46, readFileSync as readFileSync38 } from "fs";
20184
- import { join as join42, sep as sep4 } from "path";
20185
- import { homedir as homedir22, platform as osPlatform2, arch as osArch } from "os";
20498
+ import { existsSync as existsSync49, readFileSync as readFileSync39 } from "fs";
20499
+ import { join as join45, sep as sep4 } from "path";
20500
+ import { homedir as homedir24, platform as osPlatform2, arch as osArch } from "os";
20186
20501
  init_http();
20187
20502
  init_preflight();
20188
20503
  init_credentials();
@@ -20213,10 +20528,10 @@ function buildContext() {
20213
20528
  const credentials = getCredentials();
20214
20529
  const client = credentials ? new ApiClient(credentials) : null;
20215
20530
  let config = null;
20216
- const configPath = join42(process.cwd(), ".runwork.json");
20217
- if (existsSync46(configPath)) {
20531
+ const configPath = join45(process.cwd(), ".runwork.json");
20532
+ if (existsSync49(configPath)) {
20218
20533
  try {
20219
- config = JSON.parse(readFileSync38(configPath, "utf-8"));
20534
+ config = JSON.parse(readFileSync39(configPath, "utf-8"));
20220
20535
  } catch {}
20221
20536
  }
20222
20537
  return { credentials, client, config, cwd: process.cwd() };
@@ -20317,9 +20632,9 @@ async function checkCliArtifactReachable() {
20317
20632
  }
20318
20633
  async function checkCliInstallLocation() {
20319
20634
  const isWindows2 = osPlatform2() === "win32";
20320
- const home = homedir22();
20321
- const canonicalDir = join42(home, ".runwork", "bin");
20322
- const canonicalBinary = isWindows2 ? join42(canonicalDir, "runwork.exe") : join42(canonicalDir, "runwork");
20635
+ const home = homedir24();
20636
+ const canonicalDir = join45(home, ".runwork", "bin");
20637
+ const canonicalBinary = isWindows2 ? join45(canonicalDir, "runwork.exe") : join45(canonicalDir, "runwork");
20323
20638
  const candidates = [process.execPath, process.argv[1] || ""].filter(Boolean);
20324
20639
  const runsFromCanonical = candidates.some((p) => normalizePath(p) === normalizePath(canonicalBinary));
20325
20640
  if (runsFromCanonical) {
@@ -20329,7 +20644,7 @@ async function checkCliInstallLocation() {
20329
20644
  message: `canonical (${canonicalBinary})`
20330
20645
  };
20331
20646
  }
20332
- if (existsSync46(canonicalBinary)) {
20647
+ if (existsSync49(canonicalBinary)) {
20333
20648
  return {
20334
20649
  name: "cli-install-location",
20335
20650
  status: "warn",
@@ -20451,8 +20766,8 @@ async function checkGitCredentialHelper(ctx) {
20451
20766
  };
20452
20767
  }
20453
20768
  async function checkProjectConfig(ctx) {
20454
- const configPath = join42(ctx.cwd, ".runwork.json");
20455
- if (!existsSync46(configPath)) {
20769
+ const configPath = join45(ctx.cwd, ".runwork.json");
20770
+ if (!existsSync49(configPath)) {
20456
20771
  if (!ctx.credentials) {
20457
20772
  return { name: "project-config", status: "skip", message: "no project (not logged in)" };
20458
20773
  }
@@ -20514,7 +20829,7 @@ async function checkGitRemote(ctx) {
20514
20829
  if (!ctx.config) {
20515
20830
  return { name: "git-remote", status: "skip", message: "skipped (no project)" };
20516
20831
  }
20517
- if (!existsSync46(join42(ctx.cwd, ".git"))) {
20832
+ if (!existsSync49(join45(ctx.cwd, ".git"))) {
20518
20833
  return {
20519
20834
  name: "git-remote",
20520
20835
  status: "fail",
@@ -20568,12 +20883,12 @@ async function checkDeployFreshness(ctx) {
20568
20883
  return { name: "deploy-freshness", status: "skip", message: "local HEAD unknown" };
20569
20884
  }
20570
20885
  function loadSetupState5() {
20571
- const projectPath = join42(process.cwd(), ".runwork", "setup.json");
20572
- const userPath = join42(homedir22(), ".runwork", "setup.json");
20886
+ const projectPath = join45(process.cwd(), ".runwork", "setup.json");
20887
+ const userPath = join45(homedir24(), ".runwork", "setup.json");
20573
20888
  for (const p of [projectPath, userPath]) {
20574
- if (existsSync46(p)) {
20889
+ if (existsSync49(p)) {
20575
20890
  try {
20576
- return JSON.parse(readFileSync38(p, "utf-8"));
20891
+ return JSON.parse(readFileSync39(p, "utf-8"));
20577
20892
  } catch {
20578
20893
  continue;
20579
20894
  }
@@ -20588,13 +20903,13 @@ async function checkCodexNetwork() {
20588
20903
  if (!state || !state.configuredAgents.includes("codex")) {
20589
20904
  return { name, status: "skip", message: "Codex not configured for Runwork" };
20590
20905
  }
20591
- const configPath = join42(homedir22(), ".codex", "config.toml");
20592
- if (!existsSync46(configPath)) {
20906
+ const configPath = join45(homedir24(), ".codex", "config.toml");
20907
+ if (!existsSync49(configPath)) {
20593
20908
  return { name, status: "skip", message: "no Codex config found" };
20594
20909
  }
20595
20910
  let parsed;
20596
20911
  try {
20597
- parsed = parse2(readFileSync38(configPath, "utf-8"));
20912
+ parsed = parse2(readFileSync39(configPath, "utf-8"));
20598
20913
  } catch {
20599
20914
  return { name, status: "warn", message: "could not parse ~/.codex/config.toml" };
20600
20915
  }
@@ -20647,19 +20962,19 @@ async function checkCodexDesktopProject() {
20647
20962
  if (!usesCodex) {
20648
20963
  return { name, status: "skip", message: "Codex not configured for Runwork" };
20649
20964
  }
20650
- const statePath2 = join42(homedir22(), ".codex", ".codex-global-state.json");
20651
- if (!existsSync46(statePath2)) {
20965
+ const statePath2 = join45(homedir24(), ".codex", ".codex-global-state.json");
20966
+ if (!existsSync49(statePath2)) {
20652
20967
  return { name, status: "skip", message: "Codex desktop app not detected" };
20653
20968
  }
20654
20969
  let savedRoots = [];
20655
20970
  try {
20656
- const parsed = JSON.parse(readFileSync38(statePath2, "utf-8"));
20971
+ const parsed = JSON.parse(readFileSync39(statePath2, "utf-8"));
20657
20972
  const roots = parsed["electron-saved-workspace-roots"];
20658
20973
  savedRoots = Array.isArray(roots) ? roots.filter((r) => typeof r === "string") : [];
20659
20974
  } catch {
20660
20975
  return { name, status: "warn", message: "could not read Codex desktop state" };
20661
20976
  }
20662
- const runworkDir = join42(homedir22(), ".runwork");
20977
+ const runworkDir = join45(homedir24(), ".runwork");
20663
20978
  if (savedRoots.includes(runworkDir)) {
20664
20979
  return { name, status: "pass", message: "Runwork project added to Codex desktop sidebar" };
20665
20980
  }
@@ -20712,9 +21027,9 @@ async function checkAgentSetup() {
20712
21027
  if (!adapter2 || !adapter2.supportsMcpScope("user"))
20713
21028
  continue;
20714
21029
  const mcpConfigPath = getMcpConfigPath2(slug, "user");
20715
- if (mcpConfigPath && existsSync46(mcpConfigPath)) {
21030
+ if (mcpConfigPath && existsSync49(mcpConfigPath)) {
20716
21031
  try {
20717
- const content = readFileSync38(mcpConfigPath, "utf-8");
21032
+ const content = readFileSync39(mcpConfigPath, "utf-8");
20718
21033
  const missingMcp = state.mcpServers.filter((name) => !content.includes(name));
20719
21034
  if (missingMcp.length > 0) {
20720
21035
  details.push(`${missingMcp.length} MCP server(s) missing from ${slug} config`);
@@ -20737,8 +21052,8 @@ async function checkAgentSetup() {
20737
21052
  if (!skillsDir)
20738
21053
  continue;
20739
21054
  const missingSkills = state.skills.filter((name) => {
20740
- const skillPath = join42(skillsDir, name, "SKILL.md");
20741
- return !existsSync46(skillPath);
21055
+ const skillPath = join45(skillsDir, name, "SKILL.md");
21056
+ return !existsSync49(skillPath);
20742
21057
  });
20743
21058
  if (missingSkills.length > 0) {
20744
21059
  details.push(`${missingSkills.length} skill(s) missing from ${slug}`);
@@ -20763,33 +21078,66 @@ async function checkAgentSetup() {
20763
21078
  };
20764
21079
  }
20765
21080
  function getMcpConfigPath2(slug, scope) {
20766
- const home = homedir22();
21081
+ const home = homedir24();
20767
21082
  switch (slug) {
20768
21083
  case "claude-code":
20769
- return scope === "project" ? join42(process.cwd(), ".mcp.json") : join42(home, ".claude", "settings.json");
21084
+ return scope === "project" ? join45(process.cwd(), ".mcp.json") : join45(home, ".claude", "settings.json");
20770
21085
  case "cursor":
20771
- return scope === "project" ? join42(process.cwd(), ".cursor", "mcp.json") : join42(home, ".cursor", "mcp.json");
21086
+ return scope === "project" ? join45(process.cwd(), ".cursor", "mcp.json") : join45(home, ".cursor", "mcp.json");
20772
21087
  case "windsurf":
20773
- return scope === "project" ? join42(process.cwd(), ".windsurf", "mcp.json") : join42(home, ".windsurf", "mcp.json");
21088
+ return scope === "project" ? join45(process.cwd(), ".windsurf", "mcp.json") : join45(home, ".windsurf", "mcp.json");
20774
21089
  case "codex":
20775
21090
  case "codex-app":
20776
- return scope === "user" ? join42(home, ".codex", "config.toml") : null;
21091
+ return scope === "user" ? join45(home, ".codex", "config.toml") : null;
20777
21092
  case "gemini":
20778
- return scope === "user" ? join42(home, ".gemini", "settings.json") : null;
21093
+ return scope === "user" ? join45(home, ".gemini", "settings.json") : null;
20779
21094
  default:
20780
21095
  return null;
20781
21096
  }
20782
21097
  }
21098
+ async function checkWorkspacePointers() {
21099
+ const userStatePath = join45(homedir24(), ".runwork", "setup.json");
21100
+ const state = existsSync49(userStatePath) ? (() => {
21101
+ try {
21102
+ return JSON.parse(readFileSync39(userStatePath, "utf-8"));
21103
+ } catch {
21104
+ return null;
21105
+ }
21106
+ })() : null;
21107
+ const credentials = getCredentials();
21108
+ if (!state) {
21109
+ return { name: "workspace-pointers", status: "skip", message: "setup has not run on this machine" };
21110
+ }
21111
+ if (!credentials?.defaultWorkspaceId) {
21112
+ return { name: "workspace-pointers", status: "skip", message: "no default workspace in credentials" };
21113
+ }
21114
+ if (credentials.defaultWorkspaceId === state.workspaceId) {
21115
+ const label = state.workspaceName || state.workspaceId;
21116
+ return { name: "workspace-pointers", status: "pass", message: `${label} everywhere` };
21117
+ }
21118
+ const configured = state.workspaceName || state.workspaceId;
21119
+ const selected = credentials.defaultWorkspaceName || credentials.defaultWorkspaceId;
21120
+ return {
21121
+ name: "workspace-pointers",
21122
+ status: "fail",
21123
+ message: `your AI tools are set up for ${configured}, but ${selected} is selected`,
21124
+ details: [
21125
+ `setup.json (drives sync): ${state.workspaceId}`,
21126
+ `credentials (selected): ${credentials.defaultWorkspaceId}`
21127
+ ],
21128
+ fix: `runwork workspace switch ${credentials.defaultWorkspaceId}`
21129
+ };
21130
+ }
20783
21131
  function getSkillsDir(slug, scope) {
20784
- const home = homedir22();
21132
+ const home = homedir24();
20785
21133
  switch (slug) {
20786
21134
  case "claude-code":
20787
- return scope === "project" ? join42(process.cwd(), ".claude", "skills") : join42(home, ".claude", "skills");
21135
+ return scope === "project" ? join45(process.cwd(), ".claude", "skills") : join45(home, ".claude", "skills");
20788
21136
  case "codex":
20789
21137
  case "codex-app":
20790
- return scope === "project" ? join42(process.cwd(), ".agents", "skills") : join42(home, ".agents", "skills");
21138
+ return scope === "project" ? join45(process.cwd(), ".agents", "skills") : join45(home, ".agents", "skills");
20791
21139
  case "gemini":
20792
- return scope === "project" ? join42(process.cwd(), ".gemini", "skills") : join42(home, ".gemini", "skills");
21140
+ return scope === "project" ? join45(process.cwd(), ".gemini", "skills") : join45(home, ".gemini", "skills");
20793
21141
  default:
20794
21142
  return null;
20795
21143
  }
@@ -20815,7 +21163,8 @@ var CHECK_RUNNERS = [
20815
21163
  { names: ["deploy-freshness"], run: async (ctx) => [await checkDeployFreshness(ctx)] },
20816
21164
  { names: ["agent-setup"], run: async () => [await checkAgentSetup()] },
20817
21165
  { names: ["codex-network"], run: async () => [await checkCodexNetwork()] },
20818
- { names: ["codex-desktop-project"], run: async () => [await checkCodexDesktopProject()] }
21166
+ { names: ["codex-desktop-project"], run: async () => [await checkCodexDesktopProject()] },
21167
+ { names: ["workspace-pointers"], run: async () => [await checkWorkspacePointers()] }
20819
21168
  ];
20820
21169
  var ALL_CHECK_NAMES = CHECK_RUNNERS.flatMap((r) => r.names);
20821
21170
  async function runAllChecks(options) {
@@ -20841,8 +21190,8 @@ async function runAllChecks(options) {
20841
21190
  // src/health/fix.ts
20842
21191
  init_credentials();
20843
21192
  init_remote();
20844
- import { existsSync as existsSync47 } from "fs";
20845
- import { join as join43 } from "path";
21193
+ import { existsSync as existsSync50 } from "fs";
21194
+ import { join as join46 } from "path";
20846
21195
  async function applyDoctorFixes(ctx, failingNames) {
20847
21196
  const failing = new Set(failingNames);
20848
21197
  const outcomes = [];
@@ -20869,7 +21218,7 @@ async function applyDoctorFixes(ctx, failingNames) {
20869
21218
  applied: false,
20870
21219
  message: "no project config -- run inside an app directory"
20871
21220
  });
20872
- } else if (!existsSync47(join43(ctx.cwd, ".git"))) {
21221
+ } else if (!existsSync50(join46(ctx.cwd, ".git"))) {
20873
21222
  outcomes.push({
20874
21223
  name: "git-remote",
20875
21224
  applied: false,
@@ -20888,10 +21237,10 @@ async function applyDoctorFixes(ctx, failingNames) {
20888
21237
  }
20889
21238
 
20890
21239
  // src/agents/runtime-detection.ts
20891
- import { existsSync as existsSync48, readFileSync as readFileSync39, statSync as statSync8, readdirSync as readdirSync13 } from "fs";
20892
- import { homedir as homedir23 } from "os";
20893
- import { join as join44 } from "path";
20894
- var RUNWORK_SESSIONS_DIR = join44(homedir23(), ".runwork", "sessions");
21240
+ import { existsSync as existsSync51, readFileSync as readFileSync40, statSync as statSync9, readdirSync as readdirSync15 } from "fs";
21241
+ import { homedir as homedir25 } from "os";
21242
+ import { join as join47 } from "path";
21243
+ var RUNWORK_SESSIONS_DIR = join47(homedir25(), ".runwork", "sessions");
20895
21244
  function detectCurrentAgent() {
20896
21245
  const claudeCodeSessionId = process.env.CLAUDE_CODE_SESSION_ID;
20897
21246
  if (claudeCodeSessionId) {
@@ -20954,11 +21303,11 @@ function detectCurrentAgent() {
20954
21303
  return null;
20955
21304
  }
20956
21305
  function readHookSessionInfo(sessionId) {
20957
- const path4 = join44(RUNWORK_SESSIONS_DIR, `${sessionId}.json`);
20958
- if (!existsSync48(path4))
21306
+ const path4 = join47(RUNWORK_SESSIONS_DIR, `${sessionId}.json`);
21307
+ if (!existsSync51(path4))
20959
21308
  return null;
20960
21309
  try {
20961
- const raw = readFileSync39(path4, "utf8");
21310
+ const raw = readFileSync40(path4, "utf8");
20962
21311
  const parsed = JSON.parse(raw);
20963
21312
  return parsed;
20964
21313
  } catch {
@@ -20966,40 +21315,40 @@ function readHookSessionInfo(sessionId) {
20966
21315
  }
20967
21316
  }
20968
21317
  function findClaudeCodeSessionFile(sessionId) {
20969
- const root = join44(homedir23(), ".claude", "projects");
20970
- if (!existsSync48(root))
21318
+ const root = join47(homedir25(), ".claude", "projects");
21319
+ if (!existsSync51(root))
20971
21320
  return null;
20972
21321
  let projectDirs;
20973
21322
  try {
20974
- projectDirs = readdirSync13(root);
21323
+ projectDirs = readdirSync15(root);
20975
21324
  } catch {
20976
21325
  return null;
20977
21326
  }
20978
21327
  for (const dir of projectDirs) {
20979
- const candidate = join44(root, dir, `${sessionId}.jsonl`);
20980
- if (existsSync48(candidate))
21328
+ const candidate = join47(root, dir, `${sessionId}.jsonl`);
21329
+ if (existsSync51(candidate))
20981
21330
  return candidate;
20982
21331
  }
20983
21332
  return null;
20984
21333
  }
20985
21334
  function findCodexRolloutFile(threadId) {
20986
- const root = join44(homedir23(), ".codex", "sessions");
20987
- if (!existsSync48(root))
21335
+ const root = join47(homedir25(), ".codex", "sessions");
21336
+ if (!existsSync51(root))
20988
21337
  return null;
20989
21338
  const stack = [root];
20990
21339
  while (stack.length > 0) {
20991
21340
  const dir = stack.pop();
20992
21341
  let entries;
20993
21342
  try {
20994
- entries = readdirSync13(dir);
21343
+ entries = readdirSync15(dir);
20995
21344
  } catch {
20996
21345
  continue;
20997
21346
  }
20998
21347
  for (const entry of entries) {
20999
- const full = join44(dir, entry);
21348
+ const full = join47(dir, entry);
21000
21349
  let s;
21001
21350
  try {
21002
- s = statSync8(full);
21351
+ s = statSync9(full);
21003
21352
  } catch {
21004
21353
  continue;
21005
21354
  }
@@ -21013,30 +21362,30 @@ function findCodexRolloutFile(threadId) {
21013
21362
  return null;
21014
21363
  }
21015
21364
  function findNewestClaudeCodeSession() {
21016
- const root = join44(homedir23(), ".claude", "projects");
21017
- if (!existsSync48(root))
21365
+ const root = join47(homedir25(), ".claude", "projects");
21366
+ if (!existsSync51(root))
21018
21367
  return null;
21019
21368
  let projectDirs;
21020
21369
  try {
21021
- projectDirs = readdirSync13(root);
21370
+ projectDirs = readdirSync15(root);
21022
21371
  } catch {
21023
21372
  return null;
21024
21373
  }
21025
21374
  let best = null;
21026
21375
  for (const dir of projectDirs) {
21027
- const projectPath = join44(root, dir);
21376
+ const projectPath = join47(root, dir);
21028
21377
  let files;
21029
21378
  try {
21030
- files = readdirSync13(projectPath);
21379
+ files = readdirSync15(projectPath);
21031
21380
  } catch {
21032
21381
  continue;
21033
21382
  }
21034
21383
  for (const file of files) {
21035
21384
  if (!file.endsWith(".jsonl"))
21036
21385
  continue;
21037
- const full = join44(projectPath, file);
21386
+ const full = join47(projectPath, file);
21038
21387
  try {
21039
- const s = statSync8(full);
21388
+ const s = statSync9(full);
21040
21389
  if (!best || s.mtimeMs > best.mtime) {
21041
21390
  best = {
21042
21391
  sessionId: file.replace(/\.jsonl$/, ""),
@@ -21052,8 +21401,8 @@ function findNewestClaudeCodeSession() {
21052
21401
  return best ? { sessionId: best.sessionId, path: best.path } : null;
21053
21402
  }
21054
21403
  function findNewestCodexRollout() {
21055
- const root = join44(homedir23(), ".codex", "sessions");
21056
- if (!existsSync48(root))
21404
+ const root = join47(homedir25(), ".codex", "sessions");
21405
+ if (!existsSync51(root))
21057
21406
  return null;
21058
21407
  const stack = [root];
21059
21408
  let best = null;
@@ -21061,15 +21410,15 @@ function findNewestCodexRollout() {
21061
21410
  const dir = stack.pop();
21062
21411
  let entries;
21063
21412
  try {
21064
- entries = readdirSync13(dir);
21413
+ entries = readdirSync15(dir);
21065
21414
  } catch {
21066
21415
  continue;
21067
21416
  }
21068
21417
  for (const entry of entries) {
21069
- const full = join44(dir, entry);
21418
+ const full = join47(dir, entry);
21070
21419
  let s;
21071
21420
  try {
21072
- s = statSync8(full);
21421
+ s = statSync9(full);
21073
21422
  } catch {
21074
21423
  continue;
21075
21424
  }
@@ -21245,7 +21594,7 @@ function parseCheckNames(raw) {
21245
21594
  const unknown = requested.filter((n) => !known.has(n));
21246
21595
  return { only, unknown };
21247
21596
  }
21248
- var doctorCommand = new Command31("doctor").description("Check system health: auth, network, project config, agent setup").option("-v, --verbose", "Include host-agent detection results, runtime info, and allowlisted env vars (useful for AI agents debugging their own environment)").option("--check <names>", `Run only the named checks (comma-separated). Available: ${ALL_CHECK_NAMES.join(", ")}`).option("--fix", "Auto-remediate fixable failures (git credential helper, runwork remote), then re-check").action(async (opts, command) => {
21597
+ var doctorCommand = new Command32("doctor").description("Check system health: auth, network, project config, agent setup").option("-v, --verbose", "Include host-agent detection results, runtime info, and allowlisted env vars (useful for AI agents debugging their own environment)").option("--check <names>", `Run only the named checks (comma-separated). Available: ${ALL_CHECK_NAMES.join(", ")}`).option("--fix", "Auto-remediate fixable failures (git credential helper, runwork remote), then re-check").action(async (opts, command) => {
21249
21598
  const asJson = shouldOutputJson(command.optsWithGlobals().json);
21250
21599
  let only;
21251
21600
  if (opts.check) {
@@ -21296,8 +21645,8 @@ var doctorCommand = new Command31("doctor").description("Check system health: au
21296
21645
  // src/commands/share-convo.ts
21297
21646
  init_store();
21298
21647
  init_client();
21299
- import { Command as Command32 } from "commander";
21300
- import { readFileSync as readFileSync40, existsSync as existsSync49 } from "fs";
21648
+ import { Command as Command33 } from "commander";
21649
+ import { readFileSync as readFileSync41, existsSync as existsSync52 } from "fs";
21301
21650
  import { createHash as createHash4 } from "crypto";
21302
21651
  function nativeBundleFormatForAgent(slug) {
21303
21652
  if (slug === "claude-code" || slug === "claude-desktop")
@@ -21318,7 +21667,7 @@ async function runShareConvo(opts, command, isPersonalAlias = false) {
21318
21667
  console.error("Error: --transcript-file is required. Pass the path to the LLM-emitted markdown transcript.");
21319
21668
  process.exit(1);
21320
21669
  }
21321
- if (!existsSync49(opts.transcriptFile)) {
21670
+ if (!existsSync52(opts.transcriptFile)) {
21322
21671
  console.error(`Error: transcript file does not exist: ${opts.transcriptFile}`);
21323
21672
  process.exit(1);
21324
21673
  }
@@ -21339,7 +21688,7 @@ async function runShareConvo(opts, command, isPersonalAlias = false) {
21339
21688
  const credentials = requireAuth();
21340
21689
  const client = new ApiClient(credentials);
21341
21690
  const { workspaceId } = await resolveWorkspace2(client, { workspace: opts.workspace });
21342
- const transcriptContent = readFileSync40(opts.transcriptFile, "utf8");
21691
+ const transcriptContent = readFileSync41(opts.transcriptFile, "utf8");
21343
21692
  const bundles = [
21344
21693
  {
21345
21694
  format: "transcript",
@@ -21352,19 +21701,19 @@ async function runShareConvo(opts, command, isPersonalAlias = false) {
21352
21701
  const sourceAgent = opts.sourceAgent ?? detected?.slug ?? "generic";
21353
21702
  let nativeFilePath = null;
21354
21703
  if (opts.nativeFile) {
21355
- if (!existsSync49(opts.nativeFile)) {
21704
+ if (!existsSync52(opts.nativeFile)) {
21356
21705
  console.error(`Error: --native-file path does not exist: ${opts.nativeFile}`);
21357
21706
  process.exit(1);
21358
21707
  }
21359
21708
  nativeFilePath = opts.nativeFile;
21360
- } else if (detected?.sessionFilePath && existsSync49(detected.sessionFilePath)) {
21709
+ } else if (detected?.sessionFilePath && existsSync52(detected.sessionFilePath)) {
21361
21710
  nativeFilePath = detected.sessionFilePath;
21362
21711
  }
21363
21712
  if (nativeFilePath) {
21364
21713
  const nativeFormat = nativeBundleFormatForAgent(sourceAgent);
21365
21714
  if (nativeFormat) {
21366
21715
  try {
21367
- const content = readFileSync40(nativeFilePath, "utf8");
21716
+ const content = readFileSync41(nativeFilePath, "utf8");
21368
21717
  bundles.push({
21369
21718
  format: nativeFormat,
21370
21719
  content,
@@ -21380,7 +21729,7 @@ async function runShareConvo(opts, command, isPersonalAlias = false) {
21380
21729
  let metadata = {};
21381
21730
  if (opts.metadataFile) {
21382
21731
  try {
21383
- metadata = JSON.parse(readFileSync40(opts.metadataFile, "utf8"));
21732
+ metadata = JSON.parse(readFileSync41(opts.metadataFile, "utf8"));
21384
21733
  } catch (err) {
21385
21734
  console.error(`Error: --metadata-file is not valid JSON: ${err instanceof Error ? err.message : err}`);
21386
21735
  process.exit(1);
@@ -21435,17 +21784,17 @@ Skipped: ${result.skipped.map((s) => `${s.identifier} (${s.reason})`).join(", ")
21435
21784
  process.exit(1);
21436
21785
  }
21437
21786
  }
21438
- var shareConvoCommand = new Command32("share-convo").description("Share the current AI conversation with a teammate").option("--to <email>", "Recipient email (repeatable)", (value, prev = []) => [...prev, value], []).option("--transcript-file <path>", "Path to the LLM-emitted markdown transcript (required)").option("--native-file <path>", "Path to the native session file (optional; auto-detected from env vars otherwise)").option("--source-agent <slug>", "Override host-agent detection (e.g. claude-code, codex, claude-desktop)").option("--title <string>", "Short title for the conversation (required)").option("--note <string>", "Optional personal note to recipients").option("--ttl-days <n>", "Days until expiration (1-30, default 7)").option("--metadata-json <json>", "Inline JSON object with workMode, openQuestions, suggestedNextStep, etc.").option("--metadata-file <path>", "Path to a JSON file with the same metadata fields").option("--workspace <name-or-id>", "Workspace name or ID").action((opts, command) => runShareConvo(opts, command, false));
21787
+ var shareConvoCommand = new Command33("share-convo").description("Share the current AI conversation with a teammate").option("--to <email>", "Recipient email (repeatable)", (value, prev = []) => [...prev, value], []).option("--transcript-file <path>", "Path to the LLM-emitted markdown transcript (required)").option("--native-file <path>", "Path to the native session file (optional; auto-detected from env vars otherwise)").option("--source-agent <slug>", "Override host-agent detection (e.g. claude-code, codex, claude-desktop)").option("--title <string>", "Short title for the conversation (required)").option("--note <string>", "Optional personal note to recipients").option("--ttl-days <n>", "Days until expiration (1-30, default 7)").option("--metadata-json <json>", "Inline JSON object with workMode, openQuestions, suggestedNextStep, etc.").option("--metadata-file <path>", "Path to a JSON file with the same metadata fields").option("--workspace <name-or-id>", "Workspace name or ID").action((opts, command) => runShareConvo(opts, command, false));
21439
21788
 
21440
21789
  // src/commands/save-convo.ts
21441
- import { Command as Command33 } from "commander";
21442
- var saveConvoCommand = new Command33("save-convo").description("Save the current AI conversation as a personal checkpoint").option("--transcript-file <path>", "Path to the LLM-emitted markdown transcript (required)").option("--native-file <path>", "Path to the native session file (optional; auto-detected from env vars)").option("--source-agent <slug>", "Override host-agent detection").option("--title <string>", "Short title for the conversation (required)").option("--note <string>", "Optional note to your future self").option("--ttl-days <n>", "Days until expiration (1-30, default 7)").option("--metadata-json <json>", "Inline JSON object with workMode, openQuestions, etc.").option("--metadata-file <path>", "Path to a JSON file with metadata fields").option("--workspace <name-or-id>", "Workspace name or ID").action((opts, command) => runShareConvo({ ...opts, personal: true }, command, true));
21790
+ import { Command as Command34 } from "commander";
21791
+ var saveConvoCommand = new Command34("save-convo").description("Save the current AI conversation as a personal checkpoint").option("--transcript-file <path>", "Path to the LLM-emitted markdown transcript (required)").option("--native-file <path>", "Path to the native session file (optional; auto-detected from env vars)").option("--source-agent <slug>", "Override host-agent detection").option("--title <string>", "Short title for the conversation (required)").option("--note <string>", "Optional note to your future self").option("--ttl-days <n>", "Days until expiration (1-30, default 7)").option("--metadata-json <json>", "Inline JSON object with workMode, openQuestions, etc.").option("--metadata-file <path>", "Path to a JSON file with metadata fields").option("--workspace <name-or-id>", "Workspace name or ID").action((opts, command) => runShareConvo({ ...opts, personal: true }, command, true));
21443
21792
 
21444
21793
  // src/commands/inbox.ts
21445
21794
  init_store();
21446
21795
  init_client();
21447
- import { Command as Command34 } from "commander";
21448
- var inboxCommand = new Command34("inbox").description("List shared conversations visible to you").option("--filter <scope>", "Filter: all | received | sent | saved", "all").option("--limit <n>", "Max rows to return", "50").option("--workspace <name-or-id>", "Workspace name or ID").action(async (opts, command) => {
21796
+ import { Command as Command35 } from "commander";
21797
+ var inboxCommand = new Command35("inbox").description("List shared conversations visible to you").option("--filter <scope>", "Filter: all | received | sent | saved", "all").option("--limit <n>", "Max rows to return", "50").option("--workspace <name-or-id>", "Workspace name or ID").action(async (opts, command) => {
21449
21798
  const useJson = shouldOutputJson(command.optsWithGlobals().json);
21450
21799
  const scope = opts.filter === "received" || opts.filter === "sent" || opts.filter === "saved" ? opts.filter : "all";
21451
21800
  const limit = opts.limit ? parseInt(opts.limit, 10) : 50;
@@ -21485,10 +21834,10 @@ Shared conversations (${scope}, ${total}):
21485
21834
  // src/commands/resume.ts
21486
21835
  init_store();
21487
21836
  init_client();
21488
- import { Command as Command35 } from "commander";
21489
- import { writeFileSync as writeFileSync32, mkdirSync as mkdirSync29, realpathSync } from "fs";
21490
- import { homedir as homedir24 } from "os";
21491
- import { join as join45 } from "path";
21837
+ import { Command as Command36 } from "commander";
21838
+ import { writeFileSync as writeFileSync29, mkdirSync as mkdirSync27, realpathSync } from "fs";
21839
+ import { homedir as homedir26 } from "os";
21840
+ import { join as join48 } from "path";
21492
21841
  import { spawn as spawn5 } from "child_process";
21493
21842
  function encodeClaudeCodeCwd(cwd) {
21494
21843
  let canonical;
@@ -21525,10 +21874,10 @@ function extractCodexUuid(rolloutContent) {
21525
21874
  }
21526
21875
  function placeClaudeJsonl(uuid, content, recipientCwd) {
21527
21876
  const encoded = encodeClaudeCodeCwd(recipientCwd);
21528
- const projectDir = join45(homedir24(), ".claude", "projects", encoded);
21529
- mkdirSync29(projectDir, { recursive: true });
21530
- const placedAt = join45(projectDir, `${uuid}.jsonl`);
21531
- writeFileSync32(placedAt, content);
21877
+ const projectDir = join48(homedir26(), ".claude", "projects", encoded);
21878
+ mkdirSync27(projectDir, { recursive: true });
21879
+ const placedAt = join48(projectDir, `${uuid}.jsonl`);
21880
+ writeFileSync29(placedAt, content);
21532
21881
  return { placedAt, runFromCwd: recipientCwd };
21533
21882
  }
21534
21883
  function placeCodexRollout(uuid, content) {
@@ -21536,11 +21885,11 @@ function placeCodexRollout(uuid, content) {
21536
21885
  const yyyy = String(now.getUTCFullYear());
21537
21886
  const mm = String(now.getUTCMonth() + 1).padStart(2, "0");
21538
21887
  const dd = String(now.getUTCDate()).padStart(2, "0");
21539
- const dir = join45(homedir24(), ".codex", "sessions", yyyy, mm, dd);
21540
- mkdirSync29(dir, { recursive: true });
21888
+ const dir = join48(homedir26(), ".codex", "sessions", yyyy, mm, dd);
21889
+ mkdirSync27(dir, { recursive: true });
21541
21890
  const ts = now.toISOString().replace(/\.\d+Z$/, "").replace(/:/g, "-");
21542
- const placedAt = join45(dir, `rollout-${ts}-${uuid}.jsonl`);
21543
- writeFileSync32(placedAt, content);
21891
+ const placedAt = join48(dir, `rollout-${ts}-${uuid}.jsonl`);
21892
+ writeFileSync29(placedAt, content);
21544
21893
  return { placedAt };
21545
21894
  }
21546
21895
  function pickTargetAgent(opts, sourceAgent) {
@@ -21559,7 +21908,7 @@ function isAgentInstalled(agent) {
21559
21908
  }
21560
21909
  return false;
21561
21910
  }
21562
- var resumeCommand2 = new Command35("resume").description("Resume a shared conversation locally in your agent of choice").argument("<share-id>", "The share ID (sc_*) returned by share-convo or save-convo").option("--agent <slug>", "Override target agent (e.g. claude-code, codex)").option("--into <path>", "Override target cwd (defaults to current $PWD)").option("--dry-run", "Print the resume command instead of executing it").option("--pick", "Show interactive picker (requires TTY) - not yet implemented").option("--workspace <name-or-id>", "Workspace name or ID").action(async (shareId, opts, command) => {
21911
+ var resumeCommand2 = new Command36("resume").description("Resume a shared conversation locally in your agent of choice").argument("<share-id>", "The share ID (sc_*) returned by share-convo or save-convo").option("--agent <slug>", "Override target agent (e.g. claude-code, codex)").option("--into <path>", "Override target cwd (defaults to current $PWD)").option("--dry-run", "Print the resume command instead of executing it").option("--pick", "Show interactive picker (requires TTY) - not yet implemented").option("--workspace <name-or-id>", "Workspace name or ID").action(async (shareId, opts, command) => {
21563
21912
  const useJson = shouldOutputJson(command.optsWithGlobals().json);
21564
21913
  const credentials = requireAuth();
21565
21914
  const client = new ApiClient(credentials);
@@ -21789,7 +22138,7 @@ process.on("uncaughtException", (err) => {
21789
22138
  console.error(`Uncaught exception: ${formatError(err)}`);
21790
22139
  process.exit(1);
21791
22140
  });
21792
- var program = new Command36;
22141
+ var program = new Command37;
21793
22142
  program.name("runwork").description("Runwork CLI - local development for Runwork apps").version(VERSION).option("--json", "Output as JSON (auto-enabled when stdout is not a TTY)");
21794
22143
  program.addCommand(infoCommand);
21795
22144
  program.addCommand(loginCommand);
@@ -21814,6 +22163,7 @@ program.addCommand(apiKeysCommand);
21814
22163
  program.addCommand(agentsCommand);
21815
22164
  program.addCommand(mcpCommand);
21816
22165
  program.addCommand(setupCommand);
22166
+ program.addCommand(workspaceCommand);
21817
22167
  program.addCommand(syncCommand);
21818
22168
  program.addCommand(buildPluginCommand);
21819
22169
  program.addCommand(uninstallCommand);