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.
- package/dist/index.js +1217 -867
- 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
|
|
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 (!
|
|
905
|
+
if (!existsSync2(CREDENTIALS_FILE))
|
|
884
906
|
return null;
|
|
885
907
|
try {
|
|
886
|
-
const raw =
|
|
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
|
-
|
|
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 (
|
|
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
|
|
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:
|
|
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
|
|
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 &&
|
|
1338
|
+
if (exe && existsSync4(exe))
|
|
1317
1339
|
return exe;
|
|
1318
|
-
const fallback = lines.find((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 (
|
|
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 (!
|
|
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
|
|
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 (!
|
|
1614
|
+
if (!existsSync5(path))
|
|
1593
1615
|
return { dirs: new Set, files: new Set };
|
|
1594
1616
|
try {
|
|
1595
|
-
return parseGitignoreContent(
|
|
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
|
|
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 =
|
|
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 (!
|
|
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 (!
|
|
1719
|
+
if (!existsSync6(manifestPath))
|
|
1698
1720
|
return null;
|
|
1699
1721
|
try {
|
|
1700
|
-
const manifest = JSON.parse(
|
|
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 =
|
|
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 =
|
|
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
|
|
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(
|
|
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] =
|
|
1826
|
+
files[relPath] = readFileSync6(absPath);
|
|
1805
1827
|
}
|
|
1806
1828
|
}
|
|
1807
1829
|
};
|
|
1808
1830
|
walk(sourceDir);
|
|
1809
1831
|
const zipped = zipSync(files);
|
|
1810
|
-
ensureDirSync(
|
|
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
|
|
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 && !
|
|
2135
|
+
if (!options.here && !existsSync7(parentDir)) {
|
|
2114
2136
|
mkdirSync4(parentDir, { recursive: true });
|
|
2115
2137
|
}
|
|
2116
2138
|
const dir = join7(parentDir, slug);
|
|
2117
|
-
if (
|
|
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 (!
|
|
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 (!
|
|
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
|
|
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 (!
|
|
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
|
|
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 =
|
|
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
|
|
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 (!
|
|
2990
|
+
if (!existsSync9(abs))
|
|
2969
2991
|
continue;
|
|
2970
2992
|
try {
|
|
2971
|
-
snapshots.push({ path: rel, contents:
|
|
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 (
|
|
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
|
|
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(
|
|
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
|
|
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 (
|
|
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.
|
|
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
|
|
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 (!
|
|
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(
|
|
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
|
|
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 =
|
|
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
|
|
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
|
|
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
|
|
8663
|
-
import { dirname as
|
|
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 (!
|
|
8678
|
-
mkdirSync10(
|
|
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 (!
|
|
8706
|
+
if (!existsSync16(path2))
|
|
8685
8707
|
return null;
|
|
8686
8708
|
try {
|
|
8687
|
-
const parsed = JSON.parse(
|
|
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
|
|
8716
|
-
import { dirname as
|
|
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 (!
|
|
8741
|
-
mkdirSync11(
|
|
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 (!
|
|
8769
|
+
if (!existsSync17(path2))
|
|
8748
8770
|
return null;
|
|
8749
8771
|
try {
|
|
8750
|
-
const parsed = JSON.parse(
|
|
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 (!
|
|
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(
|
|
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
|
|
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 (!
|
|
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(
|
|
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
|
|
9241
|
+
import { readFileSync as readFileSync17, existsSync as existsSync20 } from "fs";
|
|
9220
9242
|
function readConfig2() {
|
|
9221
|
-
if (!
|
|
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(
|
|
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
|
|
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
|
|
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 (
|
|
9686
|
+
if (existsSync21(".runwork.json")) {
|
|
9665
9687
|
try {
|
|
9666
|
-
const config = JSON.parse(
|
|
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
|
|
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 (
|
|
9745
|
+
if (existsSync21(".runwork.json")) {
|
|
9724
9746
|
try {
|
|
9725
|
-
const config = JSON.parse(
|
|
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 (!
|
|
10080
|
+
if (!existsSync22(filePath)) {
|
|
10059
10081
|
console.error(`File not found: ${filePath}`);
|
|
10060
10082
|
process.exit(1);
|
|
10061
10083
|
}
|
|
10062
|
-
curlStr =
|
|
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
|
|
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 (!
|
|
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(
|
|
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
|
|
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 (!
|
|
10387
|
+
if (!existsSync24(".runwork.json"))
|
|
10366
10388
|
return null;
|
|
10367
10389
|
try {
|
|
10368
|
-
return JSON.parse(
|
|
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 (!
|
|
10397
|
+
if (!existsSync24(blueprintPath))
|
|
10376
10398
|
return null;
|
|
10377
10399
|
try {
|
|
10378
|
-
return JSON.parse(
|
|
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
|
|
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 (
|
|
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 (!
|
|
10839
|
+
if (!existsSync25(filePath)) {
|
|
10818
10840
|
console.error(`File not found: ${filePath}`);
|
|
10819
10841
|
process.exit(1);
|
|
10820
10842
|
}
|
|
10821
|
-
content =
|
|
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
|
|
11034
|
-
import { join as
|
|
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
|
|
11073
|
-
import { join as
|
|
11074
|
-
import { homedir as
|
|
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
|
|
11520
|
-
import { dirname as
|
|
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 (!
|
|
11639
|
+
if (!existsSync27(filePath))
|
|
11553
11640
|
return {};
|
|
11554
11641
|
try {
|
|
11555
|
-
return JSON.parse(
|
|
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
|
-
|
|
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 (!
|
|
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
|
|
11607
|
-
import { dirname as
|
|
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
|
-
|
|
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 (
|
|
11616
|
-
content =
|
|
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 (!
|
|
11753
|
+
if (!existsSync29(filePath))
|
|
11639
11754
|
return false;
|
|
11640
|
-
let content =
|
|
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 (!
|
|
11773
|
+
if (!existsSync29(filePath))
|
|
11659
11774
|
return false;
|
|
11660
|
-
let content =
|
|
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
|
-
|
|
11793
|
+
mkdirSync15(dirname8(filePath), { recursive: true });
|
|
11679
11794
|
let content = "";
|
|
11680
|
-
if (
|
|
11681
|
-
content =
|
|
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
|
|
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 =
|
|
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 =
|
|
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
|
-
|
|
12024
|
-
const pluginMcpPath =
|
|
12138
|
+
mkdirSync16(pluginDir, { recursive: true });
|
|
12139
|
+
const pluginMcpPath = join23(pluginDir, ".mcp.json");
|
|
12025
12140
|
const marketDir = this.getMarketplaceDir();
|
|
12026
|
-
|
|
12027
|
-
const marketplaceMcpPath =
|
|
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 =
|
|
12043
|
-
|
|
12044
|
-
const scriptPath =
|
|
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(
|
|
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" ?
|
|
12168
|
+
const baseDir = scope === "project" ? join23(process.cwd(), ".claude", "skills") : join23(homedir6(), ".claude", "skills");
|
|
12054
12169
|
for (const skill of skills) {
|
|
12055
|
-
const skillDir =
|
|
12170
|
+
const skillDir = join23(baseDir, skill.filename);
|
|
12056
12171
|
cleanupOldSkillDir(baseDir, skill);
|
|
12057
|
-
|
|
12058
|
-
writeFileSync16(
|
|
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 =
|
|
12063
|
-
|
|
12064
|
-
writeFileSync16(
|
|
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 =
|
|
12067
|
-
cleanupOldSkillDir(
|
|
12068
|
-
|
|
12069
|
-
writeFileSync16(
|
|
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 =
|
|
12073
|
-
|
|
12074
|
-
writeFileSync16(
|
|
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 =
|
|
12077
|
-
cleanupOldSkillDir(
|
|
12078
|
-
|
|
12079
|
-
writeFileSync16(
|
|
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 (
|
|
12205
|
+
if (existsSync30(pluginDir)) {
|
|
12091
12206
|
this.installSessionStartHook(pluginDir, "plugin cache");
|
|
12092
12207
|
}
|
|
12093
|
-
if (
|
|
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" ?
|
|
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" ?
|
|
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
|
-
|
|
12115
|
-
|
|
12229
|
+
join23(pluginDir, "skills", "runwork-team-instructions"),
|
|
12230
|
+
join23(marketDir, "skills", "runwork-team-instructions")
|
|
12116
12231
|
]) {
|
|
12117
|
-
|
|
12118
|
-
writeFileSync16(
|
|
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" ?
|
|
12124
|
-
const hadFile =
|
|
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(
|
|
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
|
-
|
|
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(
|
|
12293
|
+
removeRunworkMcpServers(join23(process.cwd(), ".mcp.json"), "mcpServers");
|
|
12167
12294
|
} else {
|
|
12168
|
-
removeRunworkMcpServers(
|
|
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
|
-
|
|
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 =
|
|
12186
|
-
if (
|
|
12302
|
+
const settingsPath = join23(homedir6(), ".claude", "settings.json");
|
|
12303
|
+
if (existsSync30(settingsPath)) {
|
|
12187
12304
|
try {
|
|
12188
|
-
const settings = JSON.parse(
|
|
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
|
-
|
|
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 (
|
|
12327
|
+
if (existsSync30(dir)) {
|
|
12208
12328
|
try {
|
|
12209
|
-
|
|
12329
|
+
rmSync5(dir, { recursive: true, force: true });
|
|
12210
12330
|
} catch {}
|
|
12211
12331
|
}
|
|
12212
12332
|
}
|
|
12213
12333
|
const pluginsBase = this.getPluginsBaseDir();
|
|
12214
|
-
const installedPath =
|
|
12215
|
-
if (
|
|
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
|
-
|
|
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 =
|
|
12225
|
-
if (
|
|
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 =
|
|
12237
|
-
if (!
|
|
12359
|
+
const claudeDir = join23(homedir6(), ".claude");
|
|
12360
|
+
if (!existsSync30(claudeDir))
|
|
12238
12361
|
return null;
|
|
12239
|
-
const projectsDir =
|
|
12240
|
-
if (!
|
|
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 =
|
|
12377
|
+
cwdEntries = readdirSync7(projectsDir);
|
|
12255
12378
|
} catch {
|
|
12256
12379
|
return null;
|
|
12257
12380
|
}
|
|
12258
12381
|
for (const cwd of cwdEntries) {
|
|
12259
|
-
const cwdPath =
|
|
12382
|
+
const cwdPath = join23(projectsDir, cwd);
|
|
12260
12383
|
let files;
|
|
12261
12384
|
try {
|
|
12262
|
-
files =
|
|
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 =
|
|
12392
|
+
const filePath = join23(cwdPath, file);
|
|
12270
12393
|
let stat;
|
|
12271
12394
|
try {
|
|
12272
|
-
stat =
|
|
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 =
|
|
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 =
|
|
12367
|
-
if (!
|
|
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 =
|
|
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 =
|
|
12501
|
+
const cwdPath = join23(projectsDir, cwd);
|
|
12379
12502
|
let files;
|
|
12380
12503
|
try {
|
|
12381
|
-
files =
|
|
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 =
|
|
12511
|
+
const filePath = join23(cwdPath, file);
|
|
12389
12512
|
let stat;
|
|
12390
12513
|
try {
|
|
12391
|
-
stat =
|
|
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 =
|
|
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 =
|
|
12416
|
-
if (!
|
|
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 =
|
|
12555
|
+
cwdEntries = readdirSync7(projectsDir);
|
|
12433
12556
|
} catch {
|
|
12434
12557
|
return null;
|
|
12435
12558
|
}
|
|
12436
12559
|
for (const cwd of cwdEntries) {
|
|
12437
|
-
const cwdPath =
|
|
12560
|
+
const cwdPath = join23(projectsDir, cwd);
|
|
12438
12561
|
let files;
|
|
12439
12562
|
try {
|
|
12440
|
-
files =
|
|
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 =
|
|
12570
|
+
const filePath = join23(cwdPath, file);
|
|
12448
12571
|
let fileStat;
|
|
12449
12572
|
try {
|
|
12450
|
-
fileStat =
|
|
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 =
|
|
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
|
|
12671
|
+
return join23(homedir6(), ".claude", "plugins");
|
|
12549
12672
|
}
|
|
12550
12673
|
getPluginDir() {
|
|
12551
|
-
return
|
|
12674
|
+
return join23(this.getPluginsBaseDir(), "cache", "runwork", PLUGIN_NAME, PLUGIN_VERSION);
|
|
12552
12675
|
}
|
|
12553
12676
|
getMarketplaceRoot() {
|
|
12554
|
-
return
|
|
12677
|
+
return join23(this.getPluginsBaseDir(), "marketplaces", "runwork");
|
|
12555
12678
|
}
|
|
12556
12679
|
getMarketplaceDir() {
|
|
12557
|
-
return
|
|
12680
|
+
return join23(this.getMarketplaceRoot(), "plugins", PLUGIN_NAME);
|
|
12558
12681
|
}
|
|
12559
12682
|
registerPlugin(pluginDir) {
|
|
12560
12683
|
const pluginsBase = this.getPluginsBaseDir();
|
|
12561
|
-
|
|
12562
|
-
const installedPath =
|
|
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 =
|
|
12580
|
-
|
|
12581
|
-
writeFileSync16(
|
|
12582
|
-
const marketplacesPath =
|
|
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 =
|
|
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 =
|
|
12607
|
-
|
|
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
|
|
12616
|
-
import { dirname as
|
|
12617
|
-
import { homedir as
|
|
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
|
|
12622
|
-
import { join as
|
|
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 =
|
|
12625
|
-
|
|
12626
|
-
writeFileSync17(
|
|
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
|
-
|
|
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(
|
|
12772
|
+
writeFileSync17(join24(destDir, ".mcp.json"), JSON.stringify({ mcpServers: mcpEntries }, null, 2));
|
|
12654
12773
|
}
|
|
12655
12774
|
function writePluginTeamInstructions(destDir, instructions) {
|
|
12656
|
-
const skillDir =
|
|
12657
|
-
|
|
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(
|
|
12783
|
+
writeFileSync17(join24(skillDir, "SKILL.md"), skillContent);
|
|
12665
12784
|
}
|
|
12666
12785
|
function writePluginSkills(destDir, skills) {
|
|
12667
|
-
const skillsRoot =
|
|
12668
|
-
|
|
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 =
|
|
12672
|
-
if (
|
|
12790
|
+
const legacyDir = join24(skillsRoot, skill.name);
|
|
12791
|
+
if (existsSync31(legacyDir)) {
|
|
12673
12792
|
try {
|
|
12674
|
-
|
|
12793
|
+
rmSync6(legacyDir, { recursive: true, force: true });
|
|
12675
12794
|
} catch {}
|
|
12676
12795
|
}
|
|
12677
12796
|
}
|
|
12678
|
-
const skillDir =
|
|
12679
|
-
|
|
12680
|
-
writeFileSync17(
|
|
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 =
|
|
12685
|
-
|
|
12686
|
-
const scriptPath =
|
|
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(
|
|
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
|
-
|
|
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
|
|
12845
|
+
return join25(homedir7(), "Library", "Application Support", "Claude", "claude_desktop_config.json");
|
|
12724
12846
|
}
|
|
12725
12847
|
if (os2 === "win32") {
|
|
12726
|
-
return
|
|
12848
|
+
return join25(process.env.APPDATA || join25(homedir7(), "AppData", "Roaming"), "Claude", "claude_desktop_config.json");
|
|
12727
12849
|
}
|
|
12728
|
-
return
|
|
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
|
|
12855
|
+
return join25(homedir7(), "Library", "Application Support", "Claude", "local-agent-mode-sessions");
|
|
12734
12856
|
}
|
|
12735
12857
|
if (os2 === "win32") {
|
|
12736
|
-
return
|
|
12858
|
+
return join25(process.env.APPDATA || join25(homedir7(), "AppData", "Roaming"), "Claude", "local-agent-mode-sessions");
|
|
12737
12859
|
}
|
|
12738
|
-
return
|
|
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 (!
|
|
12884
|
+
if (!existsSync32(baseDir))
|
|
12743
12885
|
return null;
|
|
12744
12886
|
try {
|
|
12745
|
-
const sessionDirs =
|
|
12887
|
+
const sessionDirs = readdirSync8(baseDir).filter((d) => !d.startsWith(".") && d !== "skills-plugin");
|
|
12746
12888
|
for (const sessionId of sessionDirs) {
|
|
12747
|
-
const sessionPath =
|
|
12889
|
+
const sessionPath = join25(baseDir, sessionId);
|
|
12748
12890
|
let orgDirs;
|
|
12749
12891
|
try {
|
|
12750
|
-
orgDirs =
|
|
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 =
|
|
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 (!
|
|
12909
|
+
if (!existsSync32(baseDir))
|
|
12768
12910
|
return paths;
|
|
12769
12911
|
try {
|
|
12770
|
-
const sessionDirs =
|
|
12912
|
+
const sessionDirs = readdirSync8(baseDir).filter((d) => !d.startsWith(".") && d !== "skills-plugin");
|
|
12771
12913
|
for (const sessionId of sessionDirs) {
|
|
12772
|
-
const sessionPath =
|
|
12914
|
+
const sessionPath = join25(baseDir, sessionId);
|
|
12773
12915
|
let orgDirs;
|
|
12774
12916
|
try {
|
|
12775
|
-
orgDirs =
|
|
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(
|
|
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 =
|
|
12789
|
-
return
|
|
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 =
|
|
12795
|
-
if (!
|
|
12936
|
+
const manifestPath = join25(orgDir, "rpm", "manifest.json");
|
|
12937
|
+
if (!existsSync32(manifestPath))
|
|
12796
12938
|
return null;
|
|
12797
12939
|
try {
|
|
12798
|
-
const manifest = JSON.parse(
|
|
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:
|
|
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
|
|
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
|
|
12843
|
-
return
|
|
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 =
|
|
12874
|
-
const marketDir =
|
|
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 =
|
|
12893
|
-
const marketRoot =
|
|
12894
|
-
const marketDir =
|
|
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 =
|
|
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 =
|
|
12914
|
-
|
|
12915
|
-
writeFileSync18(
|
|
12916
|
-
const marketplacesPath =
|
|
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(
|
|
12936
|
-
const pluginStagingDir =
|
|
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
|
-
|
|
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(
|
|
12968
|
-
writePluginTeamInstructions(
|
|
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 (
|
|
13115
|
+
if (existsSync32(configPath)) {
|
|
12974
13116
|
try {
|
|
12975
|
-
desktopConfig = JSON.parse(
|
|
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
|
|
13010
|
-
|
|
13011
|
-
if (
|
|
13012
|
-
|
|
13013
|
-
|
|
13014
|
-
|
|
13015
|
-
|
|
13016
|
-
|
|
13017
|
-
|
|
13018
|
-
|
|
13019
|
-
|
|
13020
|
-
|
|
13021
|
-
|
|
13022
|
-
|
|
13023
|
-
|
|
13024
|
-
|
|
13025
|
-
|
|
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
|
|
13029
|
-
|
|
13030
|
-
|
|
13031
|
-
|
|
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 (
|
|
13179
|
+
if (existsSync32(dir)) {
|
|
13034
13180
|
try {
|
|
13035
|
-
|
|
13181
|
+
rmSync7(dir, { recursive: true, force: true });
|
|
13036
13182
|
} catch {}
|
|
13037
13183
|
}
|
|
13038
13184
|
}
|
|
13039
|
-
const installedPath =
|
|
13040
|
-
|
|
13041
|
-
|
|
13042
|
-
|
|
13043
|
-
|
|
13044
|
-
|
|
13045
|
-
|
|
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
|
-
|
|
13048
|
-
|
|
13049
|
-
|
|
13050
|
-
|
|
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[
|
|
13200
|
+
delete marketplaces[RUNWORK_PLUGIN_MARKETPLACE];
|
|
13054
13201
|
writeJsonConfig(marketplacesPath, marketplaces);
|
|
13055
13202
|
} catch {}
|
|
13056
13203
|
}
|
|
13057
13204
|
const coworkSettingsPath = getCoworkSettingsPath(pluginsDir);
|
|
13058
|
-
if (
|
|
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
|
-
|
|
13064
|
-
|
|
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 =
|
|
13227
|
+
claudeAppDir = join25(homedir7(), "Library", "Application Support", "Claude");
|
|
13077
13228
|
} else if (os2 === "win32") {
|
|
13078
|
-
claudeAppDir =
|
|
13229
|
+
claudeAppDir = join25(process.env.APPDATA || join25(homedir7(), "AppData", "Roaming"), "Claude");
|
|
13079
13230
|
} else {
|
|
13080
|
-
claudeAppDir =
|
|
13231
|
+
claudeAppDir = join25(homedir7(), ".config", "Claude");
|
|
13081
13232
|
}
|
|
13082
|
-
if (!
|
|
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 =
|
|
13240
|
+
const agentSessionsDir = join25(claudeAppDir, "local-agent-mode-sessions");
|
|
13090
13241
|
const activeDays = new Set;
|
|
13091
|
-
if (
|
|
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 =
|
|
13110
|
-
if (
|
|
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 =
|
|
13125
|
-
if (
|
|
13275
|
+
const scheduledTasksPath = join25(claudeAppDir, "scheduled-tasks.json");
|
|
13276
|
+
if (existsSync32(scheduledTasksPath)) {
|
|
13126
13277
|
try {
|
|
13127
|
-
const raw =
|
|
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 =
|
|
13314
|
+
claudeAppDir = join25(homedir7(), "Library", "Application Support", "Claude");
|
|
13164
13315
|
} else if (os2 === "win32") {
|
|
13165
|
-
claudeAppDir =
|
|
13316
|
+
claudeAppDir = join25(process.env.APPDATA || join25(homedir7(), "AppData", "Roaming"), "Claude");
|
|
13166
13317
|
} else {
|
|
13167
|
-
claudeAppDir =
|
|
13318
|
+
claudeAppDir = join25(homedir7(), ".config", "Claude");
|
|
13168
13319
|
}
|
|
13169
|
-
const versionPath =
|
|
13170
|
-
if (
|
|
13171
|
-
return
|
|
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
|
|
13331
|
+
for (const orgDir of readdirSync8(baseDir)) {
|
|
13181
13332
|
if (orgDir.startsWith(".") || orgDir === "skills-plugin")
|
|
13182
13333
|
continue;
|
|
13183
|
-
const orgPath =
|
|
13334
|
+
const orgPath = join25(baseDir, orgDir);
|
|
13184
13335
|
try {
|
|
13185
|
-
if (!
|
|
13336
|
+
if (!statSync5(orgPath).isDirectory())
|
|
13186
13337
|
continue;
|
|
13187
13338
|
} catch {
|
|
13188
13339
|
continue;
|
|
13189
13340
|
}
|
|
13190
|
-
for (const userDir of
|
|
13341
|
+
for (const userDir of readdirSync8(orgPath)) {
|
|
13191
13342
|
if (userDir.startsWith("."))
|
|
13192
13343
|
continue;
|
|
13193
|
-
const userPath =
|
|
13344
|
+
const userPath = join25(orgPath, userDir);
|
|
13194
13345
|
try {
|
|
13195
|
-
if (!
|
|
13346
|
+
if (!statSync5(userPath).isDirectory())
|
|
13196
13347
|
continue;
|
|
13197
13348
|
} catch {
|
|
13198
13349
|
continue;
|
|
13199
13350
|
}
|
|
13200
|
-
for (const file of
|
|
13351
|
+
for (const file of readdirSync8(userPath)) {
|
|
13201
13352
|
if (!file.endsWith(".json"))
|
|
13202
13353
|
continue;
|
|
13203
13354
|
try {
|
|
13204
|
-
const session = JSON.parse(
|
|
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
|
|
13219
|
-
import { join as
|
|
13220
|
-
import { homedir as
|
|
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" &&
|
|
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" ?
|
|
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 =
|
|
13337
|
-
|
|
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(
|
|
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" ?
|
|
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
|
-
|
|
13508
|
+
mkdirSync19(join26(filePath, ".."), { recursive: true });
|
|
13358
13509
|
writeFileSync19(filePath, mdcContent);
|
|
13359
13510
|
}
|
|
13360
13511
|
async writeTeamInstructions(instructions, scope) {
|
|
13361
|
-
const filePath = scope === "project" ?
|
|
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
|
-
|
|
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 (!
|
|
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 =
|
|
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" ?
|
|
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" ?
|
|
13433
|
-
if (
|
|
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 =
|
|
13436
|
-
if (
|
|
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
|
-
|
|
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 (
|
|
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 =
|
|
13673
|
+
const trackingDbPath = join26(homedir8(), ".cursor", "ai-tracking", "ai-code-tracking.db");
|
|
13527
13674
|
let aiCommitCount = 0;
|
|
13528
13675
|
let avgAiPercent = 0;
|
|
13529
|
-
if (
|
|
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
|
|
13719
|
+
return join26(homedir8(), "Library", "Application Support", "Cursor", "User", "globalStorage", "state.vscdb");
|
|
13573
13720
|
}
|
|
13574
13721
|
if (os2 === "win32") {
|
|
13575
|
-
return
|
|
13722
|
+
return join26(process.env.APPDATA || join26(homedir8(), "AppData", "Roaming"), "Cursor", "User", "globalStorage", "state.vscdb");
|
|
13576
13723
|
}
|
|
13577
|
-
return
|
|
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 (!
|
|
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
|
|
13597
|
-
import { join as
|
|
13598
|
-
import { homedir as
|
|
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
|
|
13748
|
+
return join27(process.env.APPDATA || join27(homedir9(), "AppData", "Roaming"), "Codeium", "windsurf");
|
|
13602
13749
|
}
|
|
13603
|
-
return
|
|
13750
|
+
return join27(homedir9(), ".codeium", "windsurf");
|
|
13604
13751
|
}
|
|
13605
13752
|
function getConfigPath() {
|
|
13606
|
-
return
|
|
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" &&
|
|
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 =
|
|
13639
|
-
|
|
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(
|
|
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" ?
|
|
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
|
-
|
|
13805
|
+
mkdirSync20(join27(filePath, ".."), { recursive: true });
|
|
13659
13806
|
writeFileSync20(filePath, content);
|
|
13660
13807
|
}
|
|
13661
13808
|
async writeInstructionHint(hint, scope) {
|
|
13662
|
-
const filePath = scope === "project" ?
|
|
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
|
-
|
|
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" ?
|
|
13676
|
-
if (
|
|
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 =
|
|
13679
|
-
if (
|
|
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
|
-
|
|
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
|
|
13701
|
-
import { join as
|
|
13702
|
-
import { homedir as
|
|
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
|
|
13708
|
-
import { homedir as
|
|
13709
|
-
import { isAbsolute as isAbsolute2, join as
|
|
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
|
|
13714
|
-
import { isAbsolute, join as
|
|
13715
|
-
import { existsSync as
|
|
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" ?
|
|
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 :
|
|
14326
|
-
if (
|
|
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 :
|
|
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
|
|
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 =
|
|
14598
|
+
const configPath = join30(homedir12(), ".codex", "config.toml");
|
|
14456
14599
|
let parsed = {};
|
|
14457
|
-
if (
|
|
14458
|
-
parsed = parse(
|
|
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
|
-
|
|
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() :
|
|
14486
|
-
const baseDir =
|
|
14487
|
-
const legacyBaseDir =
|
|
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 [
|
|
14491
|
-
|
|
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
|
-
|
|
14499
|
-
|
|
14500
|
-
|
|
14501
|
-
|
|
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" ?
|
|
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" ?
|
|
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" ?
|
|
14653
|
+
const configPath = scope === "project" ? join30(process.cwd(), ".codex", "config.toml") : join30(homedir12(), ".codex", "config.toml");
|
|
14520
14654
|
let parsed = {};
|
|
14521
|
-
if (
|
|
14522
|
-
parsed = parse(
|
|
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
|
-
|
|
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 =
|
|
14570
|
-
if (
|
|
14712
|
+
const configPath = join30(homedir12(), ".codex", "config.toml");
|
|
14713
|
+
if (existsSync37(configPath)) {
|
|
14571
14714
|
try {
|
|
14572
|
-
const parsed = parse(
|
|
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
|
-
|
|
14585
|
-
|
|
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 =
|
|
14604
|
-
if (!
|
|
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 =
|
|
14614
|
-
if (
|
|
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 =
|
|
14632
|
-
if (
|
|
14633
|
-
const content =
|
|
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 =
|
|
14677
|
-
if (!
|
|
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 =
|
|
14814
|
+
entries = readdirSync11(dir, { withFileTypes: true });
|
|
14684
14815
|
} catch {
|
|
14685
14816
|
return;
|
|
14686
14817
|
}
|
|
14687
14818
|
for (const e of entries) {
|
|
14688
|
-
const full =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
14767
|
-
if (!
|
|
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 =
|
|
14905
|
+
entries = readdirSync11(dir, { withFileTypes: true });
|
|
14775
14906
|
} catch {
|
|
14776
14907
|
return;
|
|
14777
14908
|
}
|
|
14778
14909
|
for (const e of entries) {
|
|
14779
|
-
const full =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
14815
|
-
if (
|
|
14816
|
-
const data = JSON.parse(
|
|
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 =
|
|
14825
|
-
if (!
|
|
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 =
|
|
14963
|
+
entries = readdirSync11(dir);
|
|
14833
14964
|
} catch {
|
|
14834
14965
|
return;
|
|
14835
14966
|
}
|
|
14836
14967
|
for (const entry of entries) {
|
|
14837
|
-
const fullPath =
|
|
14968
|
+
const fullPath = join30(dir, entry);
|
|
14838
14969
|
if (entry.endsWith(".jsonl")) {
|
|
14839
14970
|
let fileStat;
|
|
14840
14971
|
try {
|
|
14841
|
-
fileStat =
|
|
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 (
|
|
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 =
|
|
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 =
|
|
15057
|
+
const statePath = join30(homedir12(), ".codex", ".codex-global-state.json");
|
|
14927
15058
|
let state = {};
|
|
14928
|
-
if (
|
|
15059
|
+
if (existsSync37(statePath)) {
|
|
14929
15060
|
try {
|
|
14930
|
-
state = JSON.parse(
|
|
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
|
-
|
|
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
|
|
14979
|
-
import { join as
|
|
14980
|
-
import { homedir as
|
|
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 =
|
|
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 =
|
|
15009
|
-
|
|
15139
|
+
const rulesDir = join31(process.cwd(), ".clinerules");
|
|
15140
|
+
mkdirSync22(rulesDir, { recursive: true });
|
|
15010
15141
|
for (const skill of skills) {
|
|
15011
|
-
writeFileSync22(
|
|
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 =
|
|
15019
|
-
|
|
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 =
|
|
15026
|
-
|
|
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 =
|
|
15163
|
+
const globalStatePath = join31(homedir13(), ".cline", "data", "globalState.json");
|
|
15033
15164
|
let state = {};
|
|
15034
|
-
if (
|
|
15165
|
+
if (existsSync38(globalStatePath)) {
|
|
15035
15166
|
try {
|
|
15036
|
-
state = JSON.parse(
|
|
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
|
-
|
|
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 =
|
|
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 =
|
|
15067
|
-
if (
|
|
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 =
|
|
15070
|
-
if (
|
|
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
|
-
|
|
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 (
|
|
15088
|
-
|
|
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
|
|
15097
|
-
import { join as
|
|
15098
|
-
import { homedir as
|
|
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 =
|
|
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" ?
|
|
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
|
-
|
|
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 =
|
|
15135
|
-
|
|
15136
|
-
writeFileSync23(
|
|
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" ?
|
|
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" ?
|
|
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" ?
|
|
15271
|
+
const settingsPath = scope === "project" ? join32(process.cwd(), ".gemini", "settings.json") : join32(homedir14(), ".gemini", "settings.json");
|
|
15150
15272
|
let settings = {};
|
|
15151
|
-
if (
|
|
15273
|
+
if (existsSync39(settingsPath)) {
|
|
15152
15274
|
try {
|
|
15153
|
-
settings = JSON.parse(
|
|
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
|
-
|
|
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 =
|
|
15177
|
-
if (!
|
|
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 =
|
|
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 =
|
|
15315
|
+
const chatsDir = join32(tmpDir, project.name, "chats");
|
|
15194
15316
|
let files;
|
|
15195
15317
|
try {
|
|
15196
|
-
files =
|
|
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 =
|
|
15325
|
+
const filePath = join32(chatsDir, file.name);
|
|
15204
15326
|
let stat;
|
|
15205
15327
|
try {
|
|
15206
|
-
stat =
|
|
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(
|
|
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(
|
|
15382
|
+
removeRunworkMcpServers(join32(homedir14(), ".gemini", "settings.json"), "mcpServers");
|
|
15257
15383
|
}
|
|
15258
|
-
|
|
15259
|
-
|
|
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
|
|
15277
|
-
import { join as
|
|
15278
|
-
import { homedir as
|
|
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 =
|
|
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 =
|
|
15328
|
-
if (
|
|
15443
|
+
const oldDir = join33(baseDir, skill.name);
|
|
15444
|
+
if (existsSync40(oldDir)) {
|
|
15329
15445
|
try {
|
|
15330
|
-
|
|
15446
|
+
rmSync12(oldDir, { recursive: true, force: true });
|
|
15331
15447
|
} catch {}
|
|
15332
15448
|
}
|
|
15333
15449
|
}
|
|
15334
|
-
const skillDir =
|
|
15335
|
-
|
|
15336
|
-
writeFileSync24(
|
|
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 =
|
|
15492
|
+
const filePath = join33(homedir15(), resolved);
|
|
15367
15493
|
removeRunworkMcpServers(filePath, this.def.mcpConfigKey || "mcpServers");
|
|
15368
15494
|
}
|
|
15369
15495
|
}
|
|
15370
|
-
|
|
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
|
-
|
|
15463
|
-
import {
|
|
15464
|
-
import {
|
|
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
|
|
15577
|
+
return join34(homedir16(), ".runwork", "insights.json");
|
|
15467
15578
|
}
|
|
15468
15579
|
function readAll() {
|
|
15469
15580
|
const path2 = storePath();
|
|
15470
|
-
if (!
|
|
15581
|
+
if (!existsSync41(path2))
|
|
15471
15582
|
return {};
|
|
15472
15583
|
try {
|
|
15473
|
-
const parsed = JSON.parse(
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
15509
|
-
import {
|
|
15510
|
-
import {
|
|
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
|
|
15633
|
+
return join35(homedir17(), ".runwork", "reflect-state.json");
|
|
15518
15634
|
}
|
|
15519
15635
|
function loadCadenceState() {
|
|
15520
15636
|
try {
|
|
15521
15637
|
const p = statePath();
|
|
15522
|
-
if (!
|
|
15638
|
+
if (!existsSync42(p))
|
|
15523
15639
|
return { ...DEFAULT_STATE };
|
|
15524
|
-
const parsed = JSON.parse(
|
|
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
|
-
|
|
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
|
-
|
|
15764
|
-
|
|
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
|
-
|
|
15981
|
+
const perWorkspace = workspaceId ? loadWorkspaceRecord(workspaceId).reflectionEnabled : undefined;
|
|
15982
|
+
let allowed = perWorkspace ?? loadCadenceState().workspaceReflectionEnabled ?? true;
|
|
15824
15983
|
try {
|
|
15825
|
-
|
|
15826
|
-
const { workspaceId } = await resolveWorkspace2(client, {});
|
|
15827
|
-
if (workspaceId) {
|
|
15984
|
+
if (client && workspaceId) {
|
|
15828
15985
|
allowed = (await client.getReflectionSetting(workspaceId)).reflectionEnabled;
|
|
15829
|
-
|
|
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
|
|
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 (!
|
|
16183
|
+
if (!existsSync43(filePath)) {
|
|
16036
16184
|
console.error(`File not found: ${filePath}`);
|
|
16037
16185
|
process.exit(1);
|
|
16038
16186
|
}
|
|
16039
|
-
const content =
|
|
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
|
|
17055
|
-
import { basename as
|
|
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 ||
|
|
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
|
-
|
|
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 ||
|
|
17312
|
+
const objectKey = key || basename3(localPath);
|
|
17165
17313
|
try {
|
|
17166
|
-
const fileBuffer =
|
|
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 {
|
|
17666
|
-
import {
|
|
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
|
|
17675
|
-
import { join as
|
|
17676
|
-
import { homedir as
|
|
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
|
-
|
|
18841
|
-
|
|
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 (!
|
|
19097
|
+
if (!existsSync45(filePath))
|
|
18935
19098
|
return null;
|
|
18936
19099
|
try {
|
|
18937
|
-
return JSON.parse(
|
|
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 =
|
|
18959
|
-
if (
|
|
18960
|
-
results.push({ name: skillName, content:
|
|
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 =
|
|
18965
|
-
if (
|
|
18966
|
-
results.push({ name: skillName, content:
|
|
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 (
|
|
19333
|
+
if (existsSync45(".runwork.json")) {
|
|
19171
19334
|
try {
|
|
19172
|
-
const config = JSON.parse(
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
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 =
|
|
19543
|
-
const userStatePath =
|
|
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
|
|
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
|
-
|
|
19610
|
-
|
|
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
|
-
|
|
19614
|
-
|
|
19615
|
-
|
|
19616
|
-
|
|
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
|
|
19829
|
+
return parkedAt ?? watermark ?? now.toISOString();
|
|
19620
19830
|
}
|
|
19621
|
-
|
|
19831
|
+
async function runSetup(opts) {
|
|
19622
19832
|
const credentials = requireAuth();
|
|
19623
19833
|
const client = new ApiClient(credentials);
|
|
19624
|
-
const { workspaceId, workspaceName, workspaceSlug } = await
|
|
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(
|
|
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
|
-
|
|
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,
|
|
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]
|
|
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" ?
|
|
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
|
|
19728
|
-
import { existsSync as
|
|
19729
|
-
import { resolve as resolve3, join as
|
|
19730
|
-
import { homedir as
|
|
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 (!
|
|
20047
|
+
if (!existsSync47(filePath))
|
|
19733
20048
|
return null;
|
|
19734
20049
|
try {
|
|
19735
|
-
return JSON.parse(
|
|
20050
|
+
return JSON.parse(readFileSync36(filePath, "utf-8"));
|
|
19736
20051
|
} catch {
|
|
19737
20052
|
return null;
|
|
19738
20053
|
}
|
|
19739
20054
|
}
|
|
19740
|
-
var buildPluginCommand = new
|
|
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 =
|
|
19752
|
-
const userStatePath =
|
|
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
|
|
19842
|
-
import { existsSync as
|
|
19843
|
-
import { join as
|
|
19844
|
-
import { homedir as
|
|
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 (!
|
|
20162
|
+
if (!existsSync48(filePath))
|
|
19848
20163
|
return null;
|
|
19849
20164
|
try {
|
|
19850
|
-
return JSON.parse(
|
|
20165
|
+
return JSON.parse(readFileSync37(filePath, "utf-8"));
|
|
19851
20166
|
} catch {
|
|
19852
20167
|
return null;
|
|
19853
20168
|
}
|
|
19854
20169
|
}
|
|
19855
|
-
var uninstallCommand = new
|
|
19856
|
-
const projectStatePath =
|
|
19857
|
-
const userStatePath =
|
|
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" ?
|
|
20252
|
+
const stateDir = label === "project" ? join44(process.cwd(), ".runwork") : join44(homedir23(), ".runwork");
|
|
19938
20253
|
if (opts.keepAuth && label === "user") {
|
|
19939
|
-
const setupFile =
|
|
19940
|
-
if (
|
|
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 (
|
|
20264
|
+
} else if (existsSync48(stateDir)) {
|
|
19950
20265
|
try {
|
|
19951
|
-
|
|
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
|
|
20286
|
+
import { Command as Command29 } from "commander";
|
|
19972
20287
|
init_init();
|
|
19973
|
-
var listCommand12 = new
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
20081
|
-
import { readFileSync as
|
|
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
|
|
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 =
|
|
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 =
|
|
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
|
|
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
|
|
20184
|
-
import { join as
|
|
20185
|
-
import { homedir as
|
|
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 =
|
|
20217
|
-
if (
|
|
20531
|
+
const configPath = join45(process.cwd(), ".runwork.json");
|
|
20532
|
+
if (existsSync49(configPath)) {
|
|
20218
20533
|
try {
|
|
20219
|
-
config = JSON.parse(
|
|
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 =
|
|
20321
|
-
const canonicalDir =
|
|
20322
|
-
const canonicalBinary = isWindows2 ?
|
|
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 (
|
|
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 =
|
|
20455
|
-
if (!
|
|
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 (!
|
|
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 =
|
|
20572
|
-
const userPath =
|
|
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 (
|
|
20889
|
+
if (existsSync49(p)) {
|
|
20575
20890
|
try {
|
|
20576
|
-
return JSON.parse(
|
|
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 =
|
|
20592
|
-
if (!
|
|
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(
|
|
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 =
|
|
20651
|
-
if (!
|
|
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(
|
|
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 =
|
|
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 &&
|
|
21030
|
+
if (mcpConfigPath && existsSync49(mcpConfigPath)) {
|
|
20716
21031
|
try {
|
|
20717
|
-
const content =
|
|
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 =
|
|
20741
|
-
return !
|
|
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 =
|
|
21081
|
+
const home = homedir24();
|
|
20767
21082
|
switch (slug) {
|
|
20768
21083
|
case "claude-code":
|
|
20769
|
-
return scope === "project" ?
|
|
21084
|
+
return scope === "project" ? join45(process.cwd(), ".mcp.json") : join45(home, ".claude", "settings.json");
|
|
20770
21085
|
case "cursor":
|
|
20771
|
-
return scope === "project" ?
|
|
21086
|
+
return scope === "project" ? join45(process.cwd(), ".cursor", "mcp.json") : join45(home, ".cursor", "mcp.json");
|
|
20772
21087
|
case "windsurf":
|
|
20773
|
-
return scope === "project" ?
|
|
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" ?
|
|
21091
|
+
return scope === "user" ? join45(home, ".codex", "config.toml") : null;
|
|
20777
21092
|
case "gemini":
|
|
20778
|
-
return scope === "user" ?
|
|
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 =
|
|
21132
|
+
const home = homedir24();
|
|
20785
21133
|
switch (slug) {
|
|
20786
21134
|
case "claude-code":
|
|
20787
|
-
return scope === "project" ?
|
|
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" ?
|
|
21138
|
+
return scope === "project" ? join45(process.cwd(), ".agents", "skills") : join45(home, ".agents", "skills");
|
|
20791
21139
|
case "gemini":
|
|
20792
|
-
return scope === "project" ?
|
|
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
|
|
20845
|
-
import { join as
|
|
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 (!
|
|
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
|
|
20892
|
-
import { homedir as
|
|
20893
|
-
import { join as
|
|
20894
|
-
var RUNWORK_SESSIONS_DIR =
|
|
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 =
|
|
20958
|
-
if (!
|
|
21306
|
+
const path4 = join47(RUNWORK_SESSIONS_DIR, `${sessionId}.json`);
|
|
21307
|
+
if (!existsSync51(path4))
|
|
20959
21308
|
return null;
|
|
20960
21309
|
try {
|
|
20961
|
-
const raw =
|
|
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 =
|
|
20970
|
-
if (!
|
|
21318
|
+
const root = join47(homedir25(), ".claude", "projects");
|
|
21319
|
+
if (!existsSync51(root))
|
|
20971
21320
|
return null;
|
|
20972
21321
|
let projectDirs;
|
|
20973
21322
|
try {
|
|
20974
|
-
projectDirs =
|
|
21323
|
+
projectDirs = readdirSync15(root);
|
|
20975
21324
|
} catch {
|
|
20976
21325
|
return null;
|
|
20977
21326
|
}
|
|
20978
21327
|
for (const dir of projectDirs) {
|
|
20979
|
-
const candidate =
|
|
20980
|
-
if (
|
|
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 =
|
|
20987
|
-
if (!
|
|
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 =
|
|
21343
|
+
entries = readdirSync15(dir);
|
|
20995
21344
|
} catch {
|
|
20996
21345
|
continue;
|
|
20997
21346
|
}
|
|
20998
21347
|
for (const entry of entries) {
|
|
20999
|
-
const full =
|
|
21348
|
+
const full = join47(dir, entry);
|
|
21000
21349
|
let s;
|
|
21001
21350
|
try {
|
|
21002
|
-
s =
|
|
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 =
|
|
21017
|
-
if (!
|
|
21365
|
+
const root = join47(homedir25(), ".claude", "projects");
|
|
21366
|
+
if (!existsSync51(root))
|
|
21018
21367
|
return null;
|
|
21019
21368
|
let projectDirs;
|
|
21020
21369
|
try {
|
|
21021
|
-
projectDirs =
|
|
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 =
|
|
21376
|
+
const projectPath = join47(root, dir);
|
|
21028
21377
|
let files;
|
|
21029
21378
|
try {
|
|
21030
|
-
files =
|
|
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 =
|
|
21386
|
+
const full = join47(projectPath, file);
|
|
21038
21387
|
try {
|
|
21039
|
-
const s =
|
|
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 =
|
|
21056
|
-
if (!
|
|
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 =
|
|
21413
|
+
entries = readdirSync15(dir);
|
|
21065
21414
|
} catch {
|
|
21066
21415
|
continue;
|
|
21067
21416
|
}
|
|
21068
21417
|
for (const entry of entries) {
|
|
21069
|
-
const full =
|
|
21418
|
+
const full = join47(dir, entry);
|
|
21070
21419
|
let s;
|
|
21071
21420
|
try {
|
|
21072
|
-
s =
|
|
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
|
|
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
|
|
21300
|
-
import { readFileSync as
|
|
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 (!
|
|
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 =
|
|
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 (!
|
|
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 &&
|
|
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 =
|
|
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(
|
|
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
|
|
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
|
|
21442
|
-
var saveConvoCommand = new
|
|
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
|
|
21448
|
-
var inboxCommand = new
|
|
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
|
|
21489
|
-
import { writeFileSync as
|
|
21490
|
-
import { homedir as
|
|
21491
|
-
import { join as
|
|
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 =
|
|
21529
|
-
|
|
21530
|
-
const placedAt =
|
|
21531
|
-
|
|
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 =
|
|
21540
|
-
|
|
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 =
|
|
21543
|
-
|
|
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
|
|
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
|
|
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);
|