runwork 0.20.0 → 0.21.1
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/bundled-types/core-workspace.d.ts +2 -2
- package/dist/index.js +678 -566
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -943,6 +943,58 @@ var init_store = __esm(() => {
|
|
|
943
943
|
CREDENTIALS_FILE = join2(RUNWORK_DIR, ".credentials");
|
|
944
944
|
});
|
|
945
945
|
|
|
946
|
+
// src/utils/workspace-state.ts
|
|
947
|
+
import { join as join3 } from "path";
|
|
948
|
+
import { homedir as homedir2 } from "os";
|
|
949
|
+
function storePath() {
|
|
950
|
+
return join3(homedir2(), ".runwork", "workspaces.json");
|
|
951
|
+
}
|
|
952
|
+
function readFile() {
|
|
953
|
+
const parsed = readJsonOrNull(storePath());
|
|
954
|
+
if (parsed && parsed.workspaces && typeof parsed.workspaces === "object")
|
|
955
|
+
return parsed;
|
|
956
|
+
return { workspaces: {} };
|
|
957
|
+
}
|
|
958
|
+
function loadWorkspaceRecord(workspaceId) {
|
|
959
|
+
return readFile().workspaces[workspaceId] ?? {};
|
|
960
|
+
}
|
|
961
|
+
function updateWorkspaceRecord(workspaceId, patch) {
|
|
962
|
+
if (!workspaceId)
|
|
963
|
+
return;
|
|
964
|
+
const file = readFile();
|
|
965
|
+
file.workspaces[workspaceId] = { ...file.workspaces[workspaceId], ...patch };
|
|
966
|
+
writeJsonAtomic(storePath(), file);
|
|
967
|
+
}
|
|
968
|
+
function clearParkedState(workspaceId) {
|
|
969
|
+
const file = readFile();
|
|
970
|
+
const record = file.workspaces[workspaceId];
|
|
971
|
+
if (!record?.parked)
|
|
972
|
+
return;
|
|
973
|
+
delete record.parked;
|
|
974
|
+
writeJsonAtomic(storePath(), file);
|
|
975
|
+
}
|
|
976
|
+
function forgetWorkspaceSelection() {
|
|
977
|
+
const statePath = join3(homedir2(), ".runwork", "setup.json");
|
|
978
|
+
const state = readJsonOrNull(statePath);
|
|
979
|
+
if (!state)
|
|
980
|
+
return false;
|
|
981
|
+
if (!state.workspaceId && !state.workspaceName && !state.workspaceSlug)
|
|
982
|
+
return false;
|
|
983
|
+
const next = { ...state };
|
|
984
|
+
delete next.workspaceId;
|
|
985
|
+
delete next.workspaceName;
|
|
986
|
+
delete next.workspaceSlug;
|
|
987
|
+
try {
|
|
988
|
+
writeJsonAtomic(statePath, next);
|
|
989
|
+
return true;
|
|
990
|
+
} catch {
|
|
991
|
+
return false;
|
|
992
|
+
}
|
|
993
|
+
}
|
|
994
|
+
var init_workspace_state = __esm(() => {
|
|
995
|
+
init_atomic_json();
|
|
996
|
+
});
|
|
997
|
+
|
|
946
998
|
// src/utils/subprocess.ts
|
|
947
999
|
import {
|
|
948
1000
|
execFileSync as cpExecFileSync,
|
|
@@ -1197,18 +1249,32 @@ function extractBaseUrl(loginUrl) {
|
|
|
1197
1249
|
return DEFAULT_BASE_URL2;
|
|
1198
1250
|
}
|
|
1199
1251
|
}
|
|
1252
|
+
function forgetWorkspaceIfAccountChanged(previousEmail, nextEmail) {
|
|
1253
|
+
if (!previousEmail || previousEmail === nextEmail)
|
|
1254
|
+
return;
|
|
1255
|
+
forgetWorkspaceSelection();
|
|
1256
|
+
}
|
|
1200
1257
|
async function pollAndSave(client, sessionId, baseUrl) {
|
|
1201
|
-
const
|
|
1202
|
-
const
|
|
1203
|
-
|
|
1204
|
-
|
|
1258
|
+
const deadline = Date.now() + 1800000;
|
|
1259
|
+
const EAGER_INTERVAL = 1000;
|
|
1260
|
+
const EAGER_WINDOW = 30000;
|
|
1261
|
+
const SETTLED_INTERVAL = 5000;
|
|
1262
|
+
const startedAt = Date.now();
|
|
1263
|
+
const previousEmail = getCredentials()?.email;
|
|
1264
|
+
for (;; ) {
|
|
1205
1265
|
const result = await client.pollLogin(sessionId);
|
|
1206
1266
|
if (result) {
|
|
1267
|
+
forgetWorkspaceIfAccountChanged(previousEmail, result.email);
|
|
1207
1268
|
saveCredentials(result);
|
|
1208
1269
|
await configureGitCredentials(result.baseUrl || baseUrl);
|
|
1209
1270
|
console.log(`Logged in as ${result.email}`);
|
|
1210
1271
|
return result;
|
|
1211
1272
|
}
|
|
1273
|
+
const remaining = deadline - Date.now();
|
|
1274
|
+
if (remaining <= 0)
|
|
1275
|
+
break;
|
|
1276
|
+
const interval = Date.now() - startedAt < EAGER_WINDOW ? EAGER_INTERVAL : SETTLED_INTERVAL;
|
|
1277
|
+
await new Promise((resolve) => setTimeout(resolve, Math.min(interval, remaining)));
|
|
1212
1278
|
}
|
|
1213
1279
|
console.error("Login timed out. Please try again.");
|
|
1214
1280
|
process.exit(1);
|
|
@@ -1217,6 +1283,7 @@ var DEFAULT_BASE_URL2 = "https://runwork.ai";
|
|
|
1217
1283
|
var init_login_flow = __esm(() => {
|
|
1218
1284
|
init_client();
|
|
1219
1285
|
init_store();
|
|
1286
|
+
init_workspace_state();
|
|
1220
1287
|
init_credentials();
|
|
1221
1288
|
});
|
|
1222
1289
|
|
|
@@ -1315,7 +1382,7 @@ var init_identity = __esm(() => {
|
|
|
1315
1382
|
// src/git/preflight.ts
|
|
1316
1383
|
import { existsSync as existsSync4 } from "fs";
|
|
1317
1384
|
import { win32 as winPath } from "path";
|
|
1318
|
-
import { homedir as
|
|
1385
|
+
import { homedir as homedir3 } from "os";
|
|
1319
1386
|
function tryRun(bin) {
|
|
1320
1387
|
try {
|
|
1321
1388
|
const out = execFileSync(bin, ["--version"], { stdio: ["ignore", "pipe", "pipe"] });
|
|
@@ -1377,7 +1444,7 @@ function canonicalGitCandidates() {
|
|
|
1377
1444
|
paths.push(winPath.join(...parts));
|
|
1378
1445
|
}
|
|
1379
1446
|
};
|
|
1380
|
-
const home =
|
|
1447
|
+
const home = homedir3();
|
|
1381
1448
|
push(process.env.ProgramFiles, "Git", "cmd", "git.exe");
|
|
1382
1449
|
push(process.env["ProgramFiles(x86)"], "Git", "cmd", "git.exe");
|
|
1383
1450
|
push(process.env.LOCALAPPDATA, "Programs", "Git", "cmd", "git.exe");
|
|
@@ -1570,7 +1637,7 @@ async function resolveApp(client, nameOrId, workspaceId) {
|
|
|
1570
1637
|
|
|
1571
1638
|
// src/utils/ignore-matcher.ts
|
|
1572
1639
|
import { existsSync as existsSync5, readFileSync as readFileSync4 } from "fs";
|
|
1573
|
-
import { basename, join as
|
|
1640
|
+
import { basename, join as join4 } from "path";
|
|
1574
1641
|
function defaultIgnoreSets() {
|
|
1575
1642
|
return {
|
|
1576
1643
|
dirs: new Set(DEFAULT_DIR_NAMES),
|
|
@@ -1610,7 +1677,7 @@ function parseGitignoreContent(content) {
|
|
|
1610
1677
|
return { dirs, files };
|
|
1611
1678
|
}
|
|
1612
1679
|
function loadGitignoreFromDir(dir) {
|
|
1613
|
-
const path =
|
|
1680
|
+
const path = join4(dir, ".gitignore");
|
|
1614
1681
|
if (!existsSync5(path))
|
|
1615
1682
|
return { dirs: new Set, files: new Set };
|
|
1616
1683
|
try {
|
|
@@ -1671,7 +1738,7 @@ var init_ignore_matcher = __esm(() => {
|
|
|
1671
1738
|
// src/template/manifest.ts
|
|
1672
1739
|
import { createHash } from "crypto";
|
|
1673
1740
|
import { readFileSync as readFileSync5, writeFileSync as writeFileSync2, existsSync as existsSync6, readdirSync, mkdirSync as mkdirSync2 } from "fs";
|
|
1674
|
-
import { join as
|
|
1741
|
+
import { join as join5, relative, sep } from "path";
|
|
1675
1742
|
function sha256(data) {
|
|
1676
1743
|
return "sha256:" + createHash("sha256").update(data).digest("hex");
|
|
1677
1744
|
}
|
|
@@ -1688,7 +1755,7 @@ function walkDir(dir, base, sets) {
|
|
|
1688
1755
|
if (dotIndex >= 0 && sets.extensions.has(entry.name.slice(dotIndex)))
|
|
1689
1756
|
continue;
|
|
1690
1757
|
}
|
|
1691
|
-
const fullPath =
|
|
1758
|
+
const fullPath = join5(dir, entry.name);
|
|
1692
1759
|
if (entry.isDirectory()) {
|
|
1693
1760
|
results.push(...walkDir(fullPath, base, sets));
|
|
1694
1761
|
} else if (entry.isFile()) {
|
|
@@ -1702,20 +1769,20 @@ async function generateManifest(dir) {
|
|
|
1702
1769
|
const sets = buildIgnoreSets(dir);
|
|
1703
1770
|
const allFiles = walkDir(dir, dir, sets);
|
|
1704
1771
|
for (const relPath of allFiles) {
|
|
1705
|
-
const content = readFileSync5(
|
|
1772
|
+
const content = readFileSync5(join5(dir, relPath));
|
|
1706
1773
|
files[relPath] = sha256(content);
|
|
1707
1774
|
}
|
|
1708
1775
|
return { version: 1, files };
|
|
1709
1776
|
}
|
|
1710
1777
|
async function saveManifest(dir, manifest) {
|
|
1711
|
-
const manifestDir =
|
|
1778
|
+
const manifestDir = join5(dir, ".runwork");
|
|
1712
1779
|
if (!existsSync6(manifestDir)) {
|
|
1713
1780
|
mkdirSync2(manifestDir, { recursive: true });
|
|
1714
1781
|
}
|
|
1715
|
-
writeFileSync2(
|
|
1782
|
+
writeFileSync2(join5(manifestDir, "template-manifest.json"), JSON.stringify(manifest, null, 2));
|
|
1716
1783
|
}
|
|
1717
1784
|
async function loadManifest(dir) {
|
|
1718
|
-
const manifestPath =
|
|
1785
|
+
const manifestPath = join5(dir, ".runwork", "template-manifest.json");
|
|
1719
1786
|
if (!existsSync6(manifestPath))
|
|
1720
1787
|
return null;
|
|
1721
1788
|
try {
|
|
@@ -1752,7 +1819,7 @@ async function detectUserEdits(dir, manifest) {
|
|
|
1752
1819
|
continue;
|
|
1753
1820
|
const expectedHash = manifest.files[relPath];
|
|
1754
1821
|
if (expectedHash) {
|
|
1755
|
-
const content = readFileSync5(
|
|
1822
|
+
const content = readFileSync5(join5(dir, relPath));
|
|
1756
1823
|
if (sha256(content) === expectedHash)
|
|
1757
1824
|
continue;
|
|
1758
1825
|
}
|
|
@@ -1774,7 +1841,7 @@ async function detectUserEdits(dir, manifest) {
|
|
|
1774
1841
|
continue;
|
|
1775
1842
|
if (trackedFiles.has(relPath))
|
|
1776
1843
|
continue;
|
|
1777
|
-
const filePath =
|
|
1844
|
+
const filePath = join5(dir, relPath);
|
|
1778
1845
|
try {
|
|
1779
1846
|
const content = readFileSync5(filePath);
|
|
1780
1847
|
if (sha256(content) !== expectedHash) {
|
|
@@ -1791,7 +1858,7 @@ var init_manifest = __esm(() => {
|
|
|
1791
1858
|
|
|
1792
1859
|
// src/utils/zip.ts
|
|
1793
1860
|
import { mkdirSync as mkdirSync3, readdirSync as readdirSync2, readFileSync as readFileSync6, statSync, writeFileSync as writeFileSync3 } from "fs";
|
|
1794
|
-
import { join as
|
|
1861
|
+
import { join as join6, dirname as dirname2, relative as relative2, sep as sep2 } from "path";
|
|
1795
1862
|
import { unzipSync, zipSync } from "fflate";
|
|
1796
1863
|
function ensureDirSync(dir) {
|
|
1797
1864
|
try {
|
|
@@ -1804,7 +1871,7 @@ function ensureDirSync(dir) {
|
|
|
1804
1871
|
function extractZip(zipData, targetDir) {
|
|
1805
1872
|
const files = unzipSync(new Uint8Array(zipData));
|
|
1806
1873
|
for (const [path, data] of Object.entries(files)) {
|
|
1807
|
-
const fullPath =
|
|
1874
|
+
const fullPath = join6(targetDir, path);
|
|
1808
1875
|
if (path.endsWith("/")) {
|
|
1809
1876
|
ensureDirSync(fullPath);
|
|
1810
1877
|
continue;
|
|
@@ -1817,7 +1884,7 @@ function createZipFromDir(sourceDir, outputPath) {
|
|
|
1817
1884
|
const files = {};
|
|
1818
1885
|
const walk = (dir) => {
|
|
1819
1886
|
for (const entry of readdirSync2(dir)) {
|
|
1820
|
-
const absPath =
|
|
1887
|
+
const absPath = join6(dir, entry);
|
|
1821
1888
|
const stats = statSync(absPath);
|
|
1822
1889
|
if (stats.isDirectory()) {
|
|
1823
1890
|
walk(absPath);
|
|
@@ -1836,14 +1903,14 @@ var init_zip = () => {};
|
|
|
1836
1903
|
|
|
1837
1904
|
// src/utils/fs.ts
|
|
1838
1905
|
import { readdirSync as readdirSync3, rmSync as rmSync2 } from "fs";
|
|
1839
|
-
import { join as
|
|
1906
|
+
import { join as join7, relative as relative3, sep as sep3 } from "path";
|
|
1840
1907
|
function removeNestedGitDirs(rootDir) {
|
|
1841
1908
|
const entries = readdirSync3(rootDir, { recursive: true, withFileTypes: true });
|
|
1842
1909
|
for (const entry of entries) {
|
|
1843
1910
|
if (entry.name !== ".git" || !entry.isDirectory())
|
|
1844
1911
|
continue;
|
|
1845
1912
|
const parentPath = "parentPath" in entry ? entry.parentPath : entry.path;
|
|
1846
|
-
const fullPath =
|
|
1913
|
+
const fullPath = join7(parentPath, entry.name);
|
|
1847
1914
|
const rel = relative3(rootDir, fullPath);
|
|
1848
1915
|
const depth = rel.split(sep3).length;
|
|
1849
1916
|
if (depth >= 2) {
|
|
@@ -1854,9 +1921,9 @@ function removeNestedGitDirs(rootDir) {
|
|
|
1854
1921
|
var init_fs = () => {};
|
|
1855
1922
|
|
|
1856
1923
|
// src/ui/banner.ts
|
|
1857
|
-
import { homedir as
|
|
1924
|
+
import { homedir as homedir4 } from "os";
|
|
1858
1925
|
function prettyPath(dir) {
|
|
1859
|
-
const home =
|
|
1926
|
+
const home = homedir4();
|
|
1860
1927
|
if (dir === home)
|
|
1861
1928
|
return "~";
|
|
1862
1929
|
if (dir.startsWith(home + "/"))
|
|
@@ -2126,8 +2193,8 @@ __export(exports_init, {
|
|
|
2126
2193
|
});
|
|
2127
2194
|
import { Command as Command2 } from "commander";
|
|
2128
2195
|
import { writeFileSync as writeFileSync4, existsSync as existsSync7, mkdirSync as mkdirSync4 } from "fs";
|
|
2129
|
-
import { join as
|
|
2130
|
-
import { homedir as
|
|
2196
|
+
import { join as join8, resolve } from "path";
|
|
2197
|
+
import { homedir as homedir5 } from "os";
|
|
2131
2198
|
async function execInit(client, appName, workspace, options = {}, creds) {
|
|
2132
2199
|
const app = await client.initApp(workspace.id, appName);
|
|
2133
2200
|
const slug = app.slug || appName.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
|
|
@@ -2135,7 +2202,7 @@ async function execInit(client, appName, workspace, options = {}, creds) {
|
|
|
2135
2202
|
if (!options.here && !existsSync7(parentDir)) {
|
|
2136
2203
|
mkdirSync4(parentDir, { recursive: true });
|
|
2137
2204
|
}
|
|
2138
|
-
const dir =
|
|
2205
|
+
const dir = join8(parentDir, slug);
|
|
2139
2206
|
if (existsSync7(dir)) {
|
|
2140
2207
|
console.error(`Directory "${dir}" already exists.`);
|
|
2141
2208
|
process.exit(1);
|
|
@@ -2162,8 +2229,8 @@ async function execInit(client, appName, workspace, options = {}, creds) {
|
|
|
2162
2229
|
appId: app.id,
|
|
2163
2230
|
appName: app.name
|
|
2164
2231
|
};
|
|
2165
|
-
writeFileSync4(
|
|
2166
|
-
if (!existsSync7(
|
|
2232
|
+
writeFileSync4(join8(dir, ".runwork.json"), JSON.stringify(config, null, 2));
|
|
2233
|
+
if (!existsSync7(join8(dir, ".git"))) {
|
|
2167
2234
|
try {
|
|
2168
2235
|
execFileSync("git", ["init"], { cwd: dir, stdio: "pipe" });
|
|
2169
2236
|
} catch (err) {
|
|
@@ -2181,8 +2248,8 @@ async function execInit(client, appName, workspace, options = {}, creds) {
|
|
|
2181
2248
|
} catch {
|
|
2182
2249
|
execFileSync("git", ["remote", "set-url", "runwork", remoteUrl], { cwd: dir, stdio: "pipe" });
|
|
2183
2250
|
}
|
|
2184
|
-
if (!existsSync7(
|
|
2185
|
-
writeFileSync4(
|
|
2251
|
+
if (!existsSync7(join8(dir, ".gitignore"))) {
|
|
2252
|
+
writeFileSync4(join8(dir, ".gitignore"), `node_modules/
|
|
2186
2253
|
.runwork/
|
|
2187
2254
|
.dev.vars
|
|
2188
2255
|
`);
|
|
@@ -2273,7 +2340,7 @@ var init_init = __esm(() => {
|
|
|
2273
2340
|
init_fs();
|
|
2274
2341
|
init_banner();
|
|
2275
2342
|
init_agent_guidance();
|
|
2276
|
-
DEFAULT_APPS_DIR =
|
|
2343
|
+
DEFAULT_APPS_DIR = join8(homedir5(), ".runwork", "apps");
|
|
2277
2344
|
initCommand = new Command2("init").alias("create").description("Create a new Runwork app").argument("[name]", "App name").option("--workspace <name-or-id>", "Workspace name or ID (skips interactive selection)").option("--here", `Scaffold in the current directory instead of ${DEFAULT_APPS_DIR}/<slug>`).action(async (name, options) => {
|
|
2278
2345
|
await runCreateFlow(name, options?.workspace, { here: options?.here });
|
|
2279
2346
|
});
|
|
@@ -2435,7 +2502,7 @@ __export(exports_clone, {
|
|
|
2435
2502
|
});
|
|
2436
2503
|
import { Command as Command3 } from "commander";
|
|
2437
2504
|
import { writeFileSync as writeFileSync5, mkdirSync as mkdirSync5, existsSync as existsSync8 } from "fs";
|
|
2438
|
-
import { join as
|
|
2505
|
+
import { join as join9, resolve as resolve2 } from "path";
|
|
2439
2506
|
async function execClone(client, app, directory, creds) {
|
|
2440
2507
|
const slug = app.slug || app.name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
|
|
2441
2508
|
const dir = directory || slug;
|
|
@@ -2452,7 +2519,7 @@ async function execClone(client, app, directory, creds) {
|
|
|
2452
2519
|
}
|
|
2453
2520
|
const manifest = await generateManifest(dir);
|
|
2454
2521
|
await saveManifest(dir, manifest);
|
|
2455
|
-
if (!existsSync8(
|
|
2522
|
+
if (!existsSync8(join9(dir, ".git"))) {
|
|
2456
2523
|
try {
|
|
2457
2524
|
execFileSync("git", ["init"], { cwd: dir, stdio: "pipe" });
|
|
2458
2525
|
} catch (err) {
|
|
@@ -2489,11 +2556,11 @@ async function execClone(client, app, directory, creds) {
|
|
|
2489
2556
|
appId: app.id,
|
|
2490
2557
|
appName: app.name
|
|
2491
2558
|
};
|
|
2492
|
-
writeFileSync5(
|
|
2559
|
+
writeFileSync5(join9(dir, ".runwork.json"), JSON.stringify(config, null, 2));
|
|
2493
2560
|
try {
|
|
2494
2561
|
const { skill } = await client.getAppSkill(app.id);
|
|
2495
2562
|
if (skill) {
|
|
2496
|
-
writeFileSync5(
|
|
2563
|
+
writeFileSync5(join9(dir, "SKILL.md"), skill, "utf-8");
|
|
2497
2564
|
}
|
|
2498
2565
|
} catch {}
|
|
2499
2566
|
return {
|
|
@@ -2627,7 +2694,7 @@ App "${cloneResult.appName}" cloned to ${cloneResult.directory}`);
|
|
|
2627
2694
|
// src/git/auto-commit.ts
|
|
2628
2695
|
import { readFileSync as readFileSync7 } from "fs";
|
|
2629
2696
|
import { watch } from "chokidar";
|
|
2630
|
-
import { join as
|
|
2697
|
+
import { join as join10, relative as relative4 } from "path";
|
|
2631
2698
|
async function watchAndAutoCommit(directory, client, appId, callbacks) {
|
|
2632
2699
|
activeCallbacks = callbacks;
|
|
2633
2700
|
const ignoreSets = buildIgnoreSets(directory);
|
|
@@ -2687,7 +2754,7 @@ async function executeFastSync(directory, client, appId) {
|
|
|
2687
2754
|
if (BINARY_EXTENSIONS.has(ext.toLowerCase()))
|
|
2688
2755
|
continue;
|
|
2689
2756
|
try {
|
|
2690
|
-
const contents = readFileSync7(
|
|
2757
|
+
const contents = readFileSync7(join10(directory, filePath), "utf-8");
|
|
2691
2758
|
files.push({ filePath, fileContents: contents });
|
|
2692
2759
|
} catch {}
|
|
2693
2760
|
}
|
|
@@ -2854,7 +2921,7 @@ var init_auto_commit = __esm(() => {
|
|
|
2854
2921
|
|
|
2855
2922
|
// src/git/sync.ts
|
|
2856
2923
|
import { unlinkSync as unlinkSync2 } from "fs";
|
|
2857
|
-
import { join as
|
|
2924
|
+
import { join as join11 } from "path";
|
|
2858
2925
|
function hasCommits(cwd) {
|
|
2859
2926
|
try {
|
|
2860
2927
|
execFileSync("git", ["rev-parse", "HEAD"], { cwd, stdio: "pipe" });
|
|
@@ -2888,7 +2955,7 @@ function removeConflictingUntrackedFiles(cwd) {
|
|
|
2888
2955
|
for (const file of remoteFiles) {
|
|
2889
2956
|
if (untracked.has(file)) {
|
|
2890
2957
|
try {
|
|
2891
|
-
unlinkSync2(
|
|
2958
|
+
unlinkSync2(join11(cwd, file));
|
|
2892
2959
|
} catch {}
|
|
2893
2960
|
}
|
|
2894
2961
|
}
|
|
@@ -2982,11 +3049,11 @@ var init_sync = __esm(() => {
|
|
|
2982
3049
|
|
|
2983
3050
|
// src/git/critical-files.ts
|
|
2984
3051
|
import { readFileSync as readFileSync8, writeFileSync as writeFileSync6, existsSync as existsSync9 } from "fs";
|
|
2985
|
-
import { join as
|
|
3052
|
+
import { join as join12 } from "path";
|
|
2986
3053
|
function snapshotCriticalFiles(cwd) {
|
|
2987
3054
|
const snapshots = [];
|
|
2988
3055
|
for (const rel of CRITICAL_FILES) {
|
|
2989
|
-
const abs =
|
|
3056
|
+
const abs = join12(cwd, rel);
|
|
2990
3057
|
if (!existsSync9(abs))
|
|
2991
3058
|
continue;
|
|
2992
3059
|
try {
|
|
@@ -2998,7 +3065,7 @@ function snapshotCriticalFiles(cwd) {
|
|
|
2998
3065
|
function restoreMissingCriticalFiles(cwd, snapshots) {
|
|
2999
3066
|
const restored = [];
|
|
3000
3067
|
for (const snap of snapshots) {
|
|
3001
|
-
const abs =
|
|
3068
|
+
const abs = join12(cwd, snap.path);
|
|
3002
3069
|
if (existsSync9(abs))
|
|
3003
3070
|
continue;
|
|
3004
3071
|
try {
|
|
@@ -3042,7 +3109,7 @@ function buildStartupSyncSummary(input) {
|
|
|
3042
3109
|
|
|
3043
3110
|
// src/logs/tailer.ts
|
|
3044
3111
|
import { appendFileSync, mkdirSync as mkdirSync6, writeFileSync as writeFileSync7 } from "fs";
|
|
3045
|
-
import { join as
|
|
3112
|
+
import { join as join13, dirname as dirname3 } from "path";
|
|
3046
3113
|
function formatTime() {
|
|
3047
3114
|
const now = new Date;
|
|
3048
3115
|
return [
|
|
@@ -3090,7 +3157,7 @@ function startLogTailer(options) {
|
|
|
3090
3157
|
toFile,
|
|
3091
3158
|
intervalMs = 5000
|
|
3092
3159
|
} = options;
|
|
3093
|
-
const logFilePath =
|
|
3160
|
+
const logFilePath = join13(projectDir, LOG_FILE);
|
|
3094
3161
|
if (toFile) {
|
|
3095
3162
|
mkdirSync6(dirname3(logFilePath), { recursive: true });
|
|
3096
3163
|
writeFileSync7(logFilePath, `# Runwork dev logs - started ${new Date().toISOString()}
|
|
@@ -4804,8 +4871,8 @@ export declare class WorkspaceContext {
|
|
|
4804
4871
|
/**
|
|
4805
4872
|
* Send a notification to a workspace user
|
|
4806
4873
|
* Can only send to verified workspace members
|
|
4807
|
-
* The platform wraps your content in a standard notification template
|
|
4808
|
-
*
|
|
4874
|
+
* The platform wraps your content in a standard notification template and
|
|
4875
|
+
* sends it from your app's name, using \`title\` as the subject line
|
|
4809
4876
|
*
|
|
4810
4877
|
* @example
|
|
4811
4878
|
* \`\`\`typescript
|
|
@@ -7503,11 +7570,11 @@ export {};
|
|
|
7503
7570
|
|
|
7504
7571
|
// src/types-manager.ts
|
|
7505
7572
|
import { existsSync as existsSync14, mkdirSync as mkdirSync9, readdirSync as readdirSync4, copyFileSync, writeFileSync as writeFileSync9 } from "fs";
|
|
7506
|
-
import { join as
|
|
7573
|
+
import { join as join15 } from "path";
|
|
7507
7574
|
async function populateTypes(projectDir) {
|
|
7508
|
-
const typesDir =
|
|
7575
|
+
const typesDir = join15(projectDir, TYPES_DIR);
|
|
7509
7576
|
mkdirSync9(typesDir, { recursive: true });
|
|
7510
|
-
const frameworkDist =
|
|
7577
|
+
const frameworkDist = join15(projectDir, "node_modules/@runworkai/framework/dist");
|
|
7511
7578
|
if (existsSync14(frameworkDist)) {
|
|
7512
7579
|
copyDtsFiles(frameworkDist, typesDir);
|
|
7513
7580
|
console.log("Types populated from node_modules/@runworkai/framework");
|
|
@@ -7516,7 +7583,7 @@ async function populateTypes(projectDir) {
|
|
|
7516
7583
|
const typeEntries = Object.entries(BUNDLED_TYPES);
|
|
7517
7584
|
if (typeEntries.length > 0) {
|
|
7518
7585
|
for (const [filename, content] of typeEntries) {
|
|
7519
|
-
writeFileSync9(
|
|
7586
|
+
writeFileSync9(join15(typesDir, filename), content);
|
|
7520
7587
|
}
|
|
7521
7588
|
console.log("Types populated from bundled types");
|
|
7522
7589
|
return;
|
|
@@ -7527,7 +7594,7 @@ function copyDtsFiles(srcDir, destDir) {
|
|
|
7527
7594
|
const files = readdirSync4(srcDir);
|
|
7528
7595
|
for (const file of files) {
|
|
7529
7596
|
if (file.endsWith(".d.ts")) {
|
|
7530
|
-
copyFileSync(
|
|
7597
|
+
copyFileSync(join15(srcDir, file), join15(destDir, file));
|
|
7531
7598
|
}
|
|
7532
7599
|
}
|
|
7533
7600
|
}
|
|
@@ -7645,7 +7712,7 @@ function createKeyboardListener() {
|
|
|
7645
7712
|
}
|
|
7646
7713
|
|
|
7647
7714
|
// src/generated/version.ts
|
|
7648
|
-
var VERSION = "0.
|
|
7715
|
+
var VERSION = "0.21.1";
|
|
7649
7716
|
|
|
7650
7717
|
// src/commands/dev.ts
|
|
7651
7718
|
var exports_dev = {};
|
|
@@ -7655,12 +7722,12 @@ __export(exports_dev, {
|
|
|
7655
7722
|
});
|
|
7656
7723
|
import { Command as Command4, Option } from "commander";
|
|
7657
7724
|
import { readFileSync as readFileSync12, writeFileSync as writeFileSync10, existsSync as existsSync15 } from "fs";
|
|
7658
|
-
import { join as
|
|
7725
|
+
import { join as join16 } from "path";
|
|
7659
7726
|
async function populateSkill(projectDir, client, appId) {
|
|
7660
7727
|
try {
|
|
7661
7728
|
const { skill } = await client.getAppSkill(appId);
|
|
7662
7729
|
if (skill) {
|
|
7663
|
-
writeFileSync10(
|
|
7730
|
+
writeFileSync10(join16(projectDir, "SKILL.md"), skill, "utf-8");
|
|
7664
7731
|
}
|
|
7665
7732
|
} catch {}
|
|
7666
7733
|
}
|
|
@@ -8682,9 +8749,9 @@ import { readFileSync as readFileSync15, existsSync as existsSync18 } from "fs";
|
|
|
8682
8749
|
// src/deploy/deploy-state.ts
|
|
8683
8750
|
init_subprocess();
|
|
8684
8751
|
import { existsSync as existsSync16, mkdirSync as mkdirSync10, readFileSync as readFileSync13, writeFileSync as writeFileSync11 } from "fs";
|
|
8685
|
-
import { dirname as dirname4, join as
|
|
8752
|
+
import { dirname as dirname4, join as join17 } from "path";
|
|
8686
8753
|
function deployStatePath(cwd) {
|
|
8687
|
-
return
|
|
8754
|
+
return join17(cwd, ".runwork", "last-deploy.json");
|
|
8688
8755
|
}
|
|
8689
8756
|
function getHeadSha(cwd) {
|
|
8690
8757
|
try {
|
|
@@ -8735,7 +8802,7 @@ function getDeploySummary(cwd) {
|
|
|
8735
8802
|
// src/deploy/deploy-status.ts
|
|
8736
8803
|
init_session();
|
|
8737
8804
|
import { existsSync as existsSync17, mkdirSync as mkdirSync11, readFileSync as readFileSync14, writeFileSync as writeFileSync12 } from "fs";
|
|
8738
|
-
import { dirname as dirname5, join as
|
|
8805
|
+
import { dirname as dirname5, join as join18 } from "path";
|
|
8739
8806
|
function evaluateDeployStatus(status, deps = {}) {
|
|
8740
8807
|
if (status.state !== "in-progress") {
|
|
8741
8808
|
return { status, effectiveState: status.state };
|
|
@@ -8751,10 +8818,10 @@ function evaluateDeployStatus(status, deps = {}) {
|
|
|
8751
8818
|
return { status, effectiveState: "in-progress" };
|
|
8752
8819
|
}
|
|
8753
8820
|
function statusPath(cwd) {
|
|
8754
|
-
return
|
|
8821
|
+
return join18(cwd, ".runwork", "deploy-status.json");
|
|
8755
8822
|
}
|
|
8756
8823
|
function deployLogPath(cwd) {
|
|
8757
|
-
return
|
|
8824
|
+
return join18(cwd, ".runwork", "deploy-stdout.log");
|
|
8758
8825
|
}
|
|
8759
8826
|
function writeDeployStatus(cwd, status) {
|
|
8760
8827
|
const path2 = statusPath(cwd);
|
|
@@ -8786,7 +8853,7 @@ init_session();
|
|
|
8786
8853
|
init_detach();
|
|
8787
8854
|
import * as fs5 from "fs";
|
|
8788
8855
|
import * as child_process2 from "child_process";
|
|
8789
|
-
import { join as
|
|
8856
|
+
import { join as join19 } from "path";
|
|
8790
8857
|
var DEPLOY_DETACHED_CHILD_FLAG = "--internal-detached-deploy-child";
|
|
8791
8858
|
function isInternalDeployChild(argv = process.argv) {
|
|
8792
8859
|
return argv.includes(DEPLOY_DETACHED_CHILD_FLAG);
|
|
@@ -8802,10 +8869,10 @@ function buildDeployChildArgs(parentArgv) {
|
|
|
8802
8869
|
return [...filtered, DEPLOY_DETACHED_CHILD_FLAG];
|
|
8803
8870
|
}
|
|
8804
8871
|
function spawnDetachedDeploy(childArgs, cwd) {
|
|
8805
|
-
const dir =
|
|
8872
|
+
const dir = join19(cwd, ".runwork");
|
|
8806
8873
|
fs5.mkdirSync(dir, { recursive: true });
|
|
8807
|
-
const stdoutFd = fs5.openSync(
|
|
8808
|
-
const stderrFd = fs5.openSync(
|
|
8874
|
+
const stdoutFd = fs5.openSync(join19(dir, "deploy-stdout.log"), "w");
|
|
8875
|
+
const stderrFd = fs5.openSync(join19(dir, "deploy-stderr.log"), "w");
|
|
8809
8876
|
try {
|
|
8810
8877
|
const proc = child_process2.spawn(process.execPath, childArgs, {
|
|
8811
8878
|
cwd,
|
|
@@ -9443,7 +9510,7 @@ import { Command as Command8 } from "commander";
|
|
|
9443
9510
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
9444
9511
|
import { platform as osPlatform, tmpdir as tmpdir2 } from "os";
|
|
9445
9512
|
import { mkdtempSync as mkdtempSync2, writeFileSync as writeFileSync13, rmSync as rmSync3 } from "fs";
|
|
9446
|
-
import { join as
|
|
9513
|
+
import { join as join20 } from "path";
|
|
9447
9514
|
var BASE_URL = "https://runwork.ai";
|
|
9448
9515
|
var LATEST_JSON_URL = `${BASE_URL}/cli/latest.json`;
|
|
9449
9516
|
var INSTALL_SH_URL = `${BASE_URL}/install.sh`;
|
|
@@ -9494,8 +9561,8 @@ Upgrading via PowerShell installer...
|
|
|
9494
9561
|
throw new Error(`Failed to download install.ps1: HTTP ${response2.status}`);
|
|
9495
9562
|
}
|
|
9496
9563
|
const script2 = await response2.text();
|
|
9497
|
-
const tmpDir = mkdtempSync2(
|
|
9498
|
-
const scriptPath =
|
|
9564
|
+
const tmpDir = mkdtempSync2(join20(tmpdir2(), "runwork-upgrade-"));
|
|
9565
|
+
const scriptPath = join20(tmpDir, "install.ps1");
|
|
9499
9566
|
try {
|
|
9500
9567
|
writeFileSync13(scriptPath, script2);
|
|
9501
9568
|
execFileSync2("powershell", ["-NoProfile", "-ExecutionPolicy", "Bypass", "-File", scriptPath], { stdio: "inherit" });
|
|
@@ -9651,6 +9718,7 @@ Upgrade failed: ${message}`);
|
|
|
9651
9718
|
// src/commands/logout.ts
|
|
9652
9719
|
init_store();
|
|
9653
9720
|
init_credentials();
|
|
9721
|
+
init_workspace_state();
|
|
9654
9722
|
import { Command as Command9 } from "commander";
|
|
9655
9723
|
var logoutCommand = new Command9("logout").description("Remove stored Runwork credentials").action(async () => {
|
|
9656
9724
|
const creds = getCredentials();
|
|
@@ -9658,6 +9726,7 @@ var logoutCommand = new Command9("logout").description("Remove stored Runwork cr
|
|
|
9658
9726
|
await removeGitCredentials(creds.baseUrl);
|
|
9659
9727
|
}
|
|
9660
9728
|
clearCredentials();
|
|
9729
|
+
forgetWorkspaceSelection();
|
|
9661
9730
|
console.log("Logged out.");
|
|
9662
9731
|
});
|
|
9663
9732
|
|
|
@@ -10193,7 +10262,7 @@ var openCommand = new Command11("open").description("Open app preview or dashboa
|
|
|
10193
10262
|
init_agent_guidance();
|
|
10194
10263
|
import { Command as Command12 } from "commander";
|
|
10195
10264
|
import { readFileSync as readFileSync21, existsSync as existsSync24 } from "fs";
|
|
10196
|
-
import { join as
|
|
10265
|
+
import { join as join21 } from "path";
|
|
10197
10266
|
|
|
10198
10267
|
// src/utils/app-info.ts
|
|
10199
10268
|
init_colors();
|
|
@@ -10393,7 +10462,7 @@ function tryReadConfig() {
|
|
|
10393
10462
|
}
|
|
10394
10463
|
}
|
|
10395
10464
|
function readBlueprint(cwd) {
|
|
10396
|
-
const blueprintPath =
|
|
10465
|
+
const blueprintPath = join21(cwd, "blueprint.json");
|
|
10397
10466
|
if (!existsSync24(blueprintPath))
|
|
10398
10467
|
return null;
|
|
10399
10468
|
try {
|
|
@@ -10742,6 +10811,110 @@ init_store();
|
|
|
10742
10811
|
init_client();
|
|
10743
10812
|
import { Command as Command13 } from "commander";
|
|
10744
10813
|
import { readFileSync as readFileSync22, existsSync as existsSync25 } from "fs";
|
|
10814
|
+
// ../../shared/skill/skill-canonical.ts
|
|
10815
|
+
function toSkillSlug(value) {
|
|
10816
|
+
return value.trim().replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
10817
|
+
}
|
|
10818
|
+
function quoteYamlValue(value) {
|
|
10819
|
+
if (value === "")
|
|
10820
|
+
return '""';
|
|
10821
|
+
if (/[:#[\]{}|>&*!,?'"]/.test(value) || value !== value.trim()) {
|
|
10822
|
+
const escaped = value.replace(/\\/g, "\\\\").replace(/"/g, "\\\"");
|
|
10823
|
+
return `"${escaped}"`;
|
|
10824
|
+
}
|
|
10825
|
+
return value;
|
|
10826
|
+
}
|
|
10827
|
+
function unquoteYamlValue(raw) {
|
|
10828
|
+
const value = raw.trim();
|
|
10829
|
+
if (value.length >= 2) {
|
|
10830
|
+
const first = value[0];
|
|
10831
|
+
const last = value[value.length - 1];
|
|
10832
|
+
if (first === '"' && last === '"') {
|
|
10833
|
+
return value.slice(1, -1).replace(/\\(["\\])/g, "$1");
|
|
10834
|
+
}
|
|
10835
|
+
if (first === "'" && last === "'") {
|
|
10836
|
+
return value.slice(1, -1);
|
|
10837
|
+
}
|
|
10838
|
+
}
|
|
10839
|
+
return value;
|
|
10840
|
+
}
|
|
10841
|
+
function parseSkillMd(content) {
|
|
10842
|
+
const lines = content.split(`
|
|
10843
|
+
`);
|
|
10844
|
+
if (lines[0]?.trim() !== "---") {
|
|
10845
|
+
const rawName = lines[0]?.replace(/^#\s*/, "").trim() || "Untitled Skill";
|
|
10846
|
+
return {
|
|
10847
|
+
frontmatter: {
|
|
10848
|
+
name: toSkillSlug(rawName) || "untitled-skill",
|
|
10849
|
+
description: ""
|
|
10850
|
+
},
|
|
10851
|
+
orderedKeys: [],
|
|
10852
|
+
body: content,
|
|
10853
|
+
hadFrontmatter: false
|
|
10854
|
+
};
|
|
10855
|
+
}
|
|
10856
|
+
let closingIndex = -1;
|
|
10857
|
+
for (let i = 1;i < lines.length; i++) {
|
|
10858
|
+
if (lines[i]?.trim() === "---") {
|
|
10859
|
+
closingIndex = i;
|
|
10860
|
+
break;
|
|
10861
|
+
}
|
|
10862
|
+
}
|
|
10863
|
+
if (closingIndex === -1) {
|
|
10864
|
+
return {
|
|
10865
|
+
frontmatter: { name: "untitled-skill", description: "" },
|
|
10866
|
+
orderedKeys: [],
|
|
10867
|
+
body: content,
|
|
10868
|
+
hadFrontmatter: false
|
|
10869
|
+
};
|
|
10870
|
+
}
|
|
10871
|
+
const frontmatterEntries = {};
|
|
10872
|
+
const orderedKeys = [];
|
|
10873
|
+
for (let i = 1;i < closingIndex; i++) {
|
|
10874
|
+
const line = lines[i];
|
|
10875
|
+
if (!line)
|
|
10876
|
+
continue;
|
|
10877
|
+
const colonIndex = line.indexOf(":");
|
|
10878
|
+
if (colonIndex > 0) {
|
|
10879
|
+
const key = line.slice(0, colonIndex).trim();
|
|
10880
|
+
const value = unquoteYamlValue(line.slice(colonIndex + 1));
|
|
10881
|
+
if (!(key in frontmatterEntries))
|
|
10882
|
+
orderedKeys.push(key);
|
|
10883
|
+
frontmatterEntries[key] = value;
|
|
10884
|
+
}
|
|
10885
|
+
}
|
|
10886
|
+
const body = lines.slice(closingIndex + 1).join(`
|
|
10887
|
+
`).trim();
|
|
10888
|
+
return {
|
|
10889
|
+
frontmatter: {
|
|
10890
|
+
...frontmatterEntries,
|
|
10891
|
+
name: toSkillSlug(frontmatterEntries.name || "") || "untitled-skill",
|
|
10892
|
+
description: frontmatterEntries.description || ""
|
|
10893
|
+
},
|
|
10894
|
+
orderedKeys,
|
|
10895
|
+
body,
|
|
10896
|
+
hadFrontmatter: true
|
|
10897
|
+
};
|
|
10898
|
+
}
|
|
10899
|
+
function buildSkillMd(parts) {
|
|
10900
|
+
const lines = [
|
|
10901
|
+
"---",
|
|
10902
|
+
`name: ${toSkillSlug(parts.name) || "untitled-skill"}`,
|
|
10903
|
+
`description: ${quoteYamlValue(parts.description ?? "")}`
|
|
10904
|
+
];
|
|
10905
|
+
if (parts.extra) {
|
|
10906
|
+
for (const [key, value] of Object.entries(parts.extra)) {
|
|
10907
|
+
if (key === "name" || key === "description")
|
|
10908
|
+
continue;
|
|
10909
|
+
lines.push(`${key}: ${quoteYamlValue(value)}`);
|
|
10910
|
+
}
|
|
10911
|
+
}
|
|
10912
|
+
lines.push("---", "", (parts.body ?? "").trim());
|
|
10913
|
+
return lines.join(`
|
|
10914
|
+
`);
|
|
10915
|
+
}
|
|
10916
|
+
|
|
10917
|
+
// src/commands/skills.ts
|
|
10745
10918
|
function truncate(text2, max) {
|
|
10746
10919
|
if (!text2)
|
|
10747
10920
|
return "";
|
|
@@ -10808,6 +10981,23 @@ async function readStdin2() {
|
|
|
10808
10981
|
}
|
|
10809
10982
|
return Buffer.concat(chunks).toString("utf-8");
|
|
10810
10983
|
}
|
|
10984
|
+
function buildSkillPushPayload(fileContent, nameArg) {
|
|
10985
|
+
const parsed = parseSkillMd(fileContent);
|
|
10986
|
+
const docName = parsed.hadFrontmatter && parsed.frontmatter.name !== "untitled-skill" ? parsed.frontmatter.name : "";
|
|
10987
|
+
const name = toSkillSlug(nameArg || "") || docName;
|
|
10988
|
+
if (!name)
|
|
10989
|
+
return null;
|
|
10990
|
+
const description = parsed.hadFrontmatter ? parsed.frontmatter.description : "";
|
|
10991
|
+
const extra = {};
|
|
10992
|
+
for (const key of parsed.orderedKeys) {
|
|
10993
|
+
if (key === "name" || key === "description")
|
|
10994
|
+
continue;
|
|
10995
|
+
const value = parsed.frontmatter[key];
|
|
10996
|
+
if (typeof value === "string")
|
|
10997
|
+
extra[key] = value;
|
|
10998
|
+
}
|
|
10999
|
+
return { name, description, content: buildSkillMd({ name, description, body: parsed.body, extra }) };
|
|
11000
|
+
}
|
|
10811
11001
|
var pushCommand = new Command13("push").description("Upload a local skill file to workspace (upsert by name). Accepts piped content via stdin.").argument("[first]", "Skill name (if piping content) or file path (name from frontmatter)").argument("[second]", "File path (when first arg is the skill name)").option("--workspace <name-or-id>", "Workspace name or ID").action(async (first, second, opts, command) => {
|
|
10812
11002
|
const useJson = shouldOutputJson(command.optsWithGlobals().json);
|
|
10813
11003
|
const credentials = requireAuth();
|
|
@@ -10849,35 +11039,22 @@ var pushCommand = new Command13("push").description("Upload a local skill file t
|
|
|
10849
11039
|
}
|
|
10850
11040
|
}
|
|
10851
11041
|
try {
|
|
10852
|
-
const
|
|
10853
|
-
|
|
10854
|
-
let description = "";
|
|
10855
|
-
let body = content;
|
|
10856
|
-
if (frontmatterMatch) {
|
|
10857
|
-
const fm = frontmatterMatch[1];
|
|
10858
|
-
body = frontmatterMatch[2];
|
|
10859
|
-
const descMatch = fm.match(/^description:\s*(.+)$/m);
|
|
10860
|
-
if (descMatch)
|
|
10861
|
-
description = descMatch[1].trim();
|
|
10862
|
-
if (!name) {
|
|
10863
|
-
const nameMatch = fm.match(/^name:\s*(.+)$/m);
|
|
10864
|
-
if (nameMatch)
|
|
10865
|
-
name = nameMatch[1].trim();
|
|
10866
|
-
}
|
|
10867
|
-
}
|
|
10868
|
-
if (!name) {
|
|
11042
|
+
const payload = buildSkillPushPayload(content, nameArg);
|
|
11043
|
+
if (!payload) {
|
|
10869
11044
|
console.error(`Skill name required. Provide as first argument or in YAML frontmatter:
|
|
10870
11045
|
runwork skills push "My Skill" ./skill.md
|
|
10871
11046
|
cat skill.md | runwork skills push "My Skill"`);
|
|
10872
11047
|
process.exit(1);
|
|
11048
|
+
return;
|
|
10873
11049
|
}
|
|
11050
|
+
const { name, description, content: document } = payload;
|
|
10874
11051
|
const existing = await client.listExternalSkills(workspaceId);
|
|
10875
11052
|
const match = existing.find((s) => s.name === name);
|
|
10876
11053
|
if (match) {
|
|
10877
11054
|
const updated = await client.updateExternalSkill(workspaceId, match.id, {
|
|
10878
11055
|
name,
|
|
10879
11056
|
description,
|
|
10880
|
-
content:
|
|
11057
|
+
content: document
|
|
10881
11058
|
});
|
|
10882
11059
|
if (useJson) {
|
|
10883
11060
|
jsonOut({ skill: updated, action: "updated" });
|
|
@@ -10888,7 +11065,7 @@ var pushCommand = new Command13("push").description("Upload a local skill file t
|
|
|
10888
11065
|
const created = await client.importExternalSkill(workspaceId, {
|
|
10889
11066
|
name,
|
|
10890
11067
|
description,
|
|
10891
|
-
content:
|
|
11068
|
+
content: document,
|
|
10892
11069
|
importedFrom: "cli"
|
|
10893
11070
|
});
|
|
10894
11071
|
if (useJson) {
|
|
@@ -11092,111 +11269,8 @@ init_client();
|
|
|
11092
11269
|
// src/agents/claude-code.ts
|
|
11093
11270
|
init_subprocess();
|
|
11094
11271
|
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
|
|
11096
|
-
import { homedir as
|
|
11097
|
-
|
|
11098
|
-
// ../../shared/skill/skill-canonical.ts
|
|
11099
|
-
function toSkillSlug(value) {
|
|
11100
|
-
return value.trim().replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
11101
|
-
}
|
|
11102
|
-
function quoteYamlValue(value) {
|
|
11103
|
-
if (value === "")
|
|
11104
|
-
return '""';
|
|
11105
|
-
if (/[:#[\]{}|>&*!,?'"]/.test(value) || value !== value.trim()) {
|
|
11106
|
-
const escaped = value.replace(/\\/g, "\\\\").replace(/"/g, "\\\"");
|
|
11107
|
-
return `"${escaped}"`;
|
|
11108
|
-
}
|
|
11109
|
-
return value;
|
|
11110
|
-
}
|
|
11111
|
-
function unquoteYamlValue(raw) {
|
|
11112
|
-
const value = raw.trim();
|
|
11113
|
-
if (value.length >= 2) {
|
|
11114
|
-
const first = value[0];
|
|
11115
|
-
const last = value[value.length - 1];
|
|
11116
|
-
if (first === '"' && last === '"') {
|
|
11117
|
-
return value.slice(1, -1).replace(/\\(["\\])/g, "$1");
|
|
11118
|
-
}
|
|
11119
|
-
if (first === "'" && last === "'") {
|
|
11120
|
-
return value.slice(1, -1);
|
|
11121
|
-
}
|
|
11122
|
-
}
|
|
11123
|
-
return value;
|
|
11124
|
-
}
|
|
11125
|
-
function parseSkillMd(content) {
|
|
11126
|
-
const lines = content.split(`
|
|
11127
|
-
`);
|
|
11128
|
-
if (lines[0]?.trim() !== "---") {
|
|
11129
|
-
const rawName = lines[0]?.replace(/^#\s*/, "").trim() || "Untitled Skill";
|
|
11130
|
-
return {
|
|
11131
|
-
frontmatter: {
|
|
11132
|
-
name: toSkillSlug(rawName) || "untitled-skill",
|
|
11133
|
-
description: ""
|
|
11134
|
-
},
|
|
11135
|
-
orderedKeys: [],
|
|
11136
|
-
body: content,
|
|
11137
|
-
hadFrontmatter: false
|
|
11138
|
-
};
|
|
11139
|
-
}
|
|
11140
|
-
let closingIndex = -1;
|
|
11141
|
-
for (let i = 1;i < lines.length; i++) {
|
|
11142
|
-
if (lines[i]?.trim() === "---") {
|
|
11143
|
-
closingIndex = i;
|
|
11144
|
-
break;
|
|
11145
|
-
}
|
|
11146
|
-
}
|
|
11147
|
-
if (closingIndex === -1) {
|
|
11148
|
-
return {
|
|
11149
|
-
frontmatter: { name: "untitled-skill", description: "" },
|
|
11150
|
-
orderedKeys: [],
|
|
11151
|
-
body: content,
|
|
11152
|
-
hadFrontmatter: false
|
|
11153
|
-
};
|
|
11154
|
-
}
|
|
11155
|
-
const frontmatterEntries = {};
|
|
11156
|
-
const orderedKeys = [];
|
|
11157
|
-
for (let i = 1;i < closingIndex; i++) {
|
|
11158
|
-
const line = lines[i];
|
|
11159
|
-
if (!line)
|
|
11160
|
-
continue;
|
|
11161
|
-
const colonIndex = line.indexOf(":");
|
|
11162
|
-
if (colonIndex > 0) {
|
|
11163
|
-
const key = line.slice(0, colonIndex).trim();
|
|
11164
|
-
const value = unquoteYamlValue(line.slice(colonIndex + 1));
|
|
11165
|
-
if (!(key in frontmatterEntries))
|
|
11166
|
-
orderedKeys.push(key);
|
|
11167
|
-
frontmatterEntries[key] = value;
|
|
11168
|
-
}
|
|
11169
|
-
}
|
|
11170
|
-
const body = lines.slice(closingIndex + 1).join(`
|
|
11171
|
-
`).trim();
|
|
11172
|
-
return {
|
|
11173
|
-
frontmatter: {
|
|
11174
|
-
...frontmatterEntries,
|
|
11175
|
-
name: toSkillSlug(frontmatterEntries.name || "") || "untitled-skill",
|
|
11176
|
-
description: frontmatterEntries.description || ""
|
|
11177
|
-
},
|
|
11178
|
-
orderedKeys,
|
|
11179
|
-
body,
|
|
11180
|
-
hadFrontmatter: true
|
|
11181
|
-
};
|
|
11182
|
-
}
|
|
11183
|
-
function buildSkillMd(parts) {
|
|
11184
|
-
const lines = [
|
|
11185
|
-
"---",
|
|
11186
|
-
`name: ${toSkillSlug(parts.name) || "untitled-skill"}`,
|
|
11187
|
-
`description: ${quoteYamlValue(parts.description ?? "")}`
|
|
11188
|
-
];
|
|
11189
|
-
if (parts.extra) {
|
|
11190
|
-
for (const [key, value] of Object.entries(parts.extra)) {
|
|
11191
|
-
if (key === "name" || key === "description")
|
|
11192
|
-
continue;
|
|
11193
|
-
lines.push(`${key}: ${quoteYamlValue(value)}`);
|
|
11194
|
-
}
|
|
11195
|
-
}
|
|
11196
|
-
lines.push("---", "", (parts.body ?? "").trim());
|
|
11197
|
-
return lines.join(`
|
|
11198
|
-
`);
|
|
11199
|
-
}
|
|
11272
|
+
import { join as join24 } from "path";
|
|
11273
|
+
import { homedir as homedir7, platform as platform2 } from "os";
|
|
11200
11274
|
|
|
11201
11275
|
// src/agents/types.ts
|
|
11202
11276
|
var RUNWORK_MCP_PREFIX = "Runwork: ";
|
|
@@ -11544,15 +11618,15 @@ function skillNameFromPath(path2) {
|
|
|
11544
11618
|
// src/utils/trash.ts
|
|
11545
11619
|
init_atomic_json();
|
|
11546
11620
|
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
|
|
11548
|
-
import { homedir as
|
|
11621
|
+
import { basename as basename2, dirname as dirname6, join as join22 } from "path";
|
|
11622
|
+
import { homedir as homedir6 } from "os";
|
|
11549
11623
|
var TRASH_RETENTION_DAYS = 30;
|
|
11550
11624
|
function trashRoot() {
|
|
11551
|
-
return
|
|
11625
|
+
return join22(homedir6(), ".runwork", "trash");
|
|
11552
11626
|
}
|
|
11553
11627
|
function batchDir(now) {
|
|
11554
11628
|
const stamp = now.toISOString().replace(/[:.]/g, "-");
|
|
11555
|
-
return
|
|
11629
|
+
return join22(trashRoot(), `${stamp}-${process.pid}`);
|
|
11556
11630
|
}
|
|
11557
11631
|
function pruneTrash(now = new Date) {
|
|
11558
11632
|
const root = trashRoot();
|
|
@@ -11560,7 +11634,7 @@ function pruneTrash(now = new Date) {
|
|
|
11560
11634
|
return;
|
|
11561
11635
|
const cutoff = now.getTime() - TRASH_RETENTION_DAYS * 24 * 60 * 60 * 1000;
|
|
11562
11636
|
for (const entry of readdirSync5(root)) {
|
|
11563
|
-
const dir =
|
|
11637
|
+
const dir = join22(root, entry);
|
|
11564
11638
|
try {
|
|
11565
11639
|
if (statSync3(dir).mtimeMs < cutoff)
|
|
11566
11640
|
rmSync4(dir, { recursive: true, force: true });
|
|
@@ -11577,8 +11651,8 @@ function moveToTrash(sourcePath, reason, now = new Date) {
|
|
|
11577
11651
|
}
|
|
11578
11652
|
const parent = basename2(dirname6(sourcePath));
|
|
11579
11653
|
const grandparent = basename2(dirname6(dirname6(sourcePath)));
|
|
11580
|
-
const destDir =
|
|
11581
|
-
const dest =
|
|
11654
|
+
const destDir = join22(activeBatch, `${grandparent}__${parent}`.replace(/[^a-zA-Z0-9._-]/g, "-"));
|
|
11655
|
+
const dest = join22(destDir, basename2(sourcePath));
|
|
11582
11656
|
try {
|
|
11583
11657
|
mkdirSync13(destDir, { recursive: true });
|
|
11584
11658
|
try {
|
|
@@ -11590,7 +11664,7 @@ function moveToTrash(sourcePath, reason, now = new Date) {
|
|
|
11590
11664
|
} catch {
|
|
11591
11665
|
return null;
|
|
11592
11666
|
}
|
|
11593
|
-
const manifestPath =
|
|
11667
|
+
const manifestPath = join22(activeBatch, "manifest.json");
|
|
11594
11668
|
const manifest = readJsonOrNull(manifestPath) ?? { entries: [] };
|
|
11595
11669
|
manifest.entries.push({ from: sourcePath, to: dest, reason, at: now.toISOString() });
|
|
11596
11670
|
try {
|
|
@@ -11691,14 +11765,14 @@ function mergeJsonMcpServers(filePath, servers, topKey) {
|
|
|
11691
11765
|
|
|
11692
11766
|
// src/agents/utils/skill-removal.ts
|
|
11693
11767
|
import { existsSync as existsSync28, readdirSync as readdirSync6 } from "fs";
|
|
11694
|
-
import { join as
|
|
11768
|
+
import { join as join23 } from "path";
|
|
11695
11769
|
function removeMatchingSkillDirs(dir, allowed, reason = "skill removed") {
|
|
11696
11770
|
if (!allowed.size || !existsSync28(dir))
|
|
11697
11771
|
return;
|
|
11698
11772
|
for (const entry of readdirSync6(dir)) {
|
|
11699
11773
|
if (!allowed.has(entry))
|
|
11700
11774
|
continue;
|
|
11701
|
-
moveToTrash(
|
|
11775
|
+
moveToTrash(join23(dir, entry), reason);
|
|
11702
11776
|
}
|
|
11703
11777
|
}
|
|
11704
11778
|
function removeMatchingSkillFiles(dir, allowed, suffix, reason = "skill removed") {
|
|
@@ -11708,7 +11782,7 @@ function removeMatchingSkillFiles(dir, allowed, suffix, reason = "skill removed"
|
|
|
11708
11782
|
for (const entry of readdirSync6(dir)) {
|
|
11709
11783
|
if (!names.has(entry))
|
|
11710
11784
|
continue;
|
|
11711
|
-
moveToTrash(
|
|
11785
|
+
moveToTrash(join23(dir, entry), reason);
|
|
11712
11786
|
}
|
|
11713
11787
|
}
|
|
11714
11788
|
|
|
@@ -12111,7 +12185,7 @@ class ClaudeCodeAdapter {
|
|
|
12111
12185
|
}
|
|
12112
12186
|
async writeMcpServers(servers, scope) {
|
|
12113
12187
|
if (scope === "project") {
|
|
12114
|
-
const filePath =
|
|
12188
|
+
const filePath = join24(process.cwd(), ".mcp.json");
|
|
12115
12189
|
const entries = {};
|
|
12116
12190
|
for (const s of servers) {
|
|
12117
12191
|
entries[s.name] = {
|
|
@@ -12123,7 +12197,7 @@ class ClaudeCodeAdapter {
|
|
|
12123
12197
|
}
|
|
12124
12198
|
mergeJsonMcpServers(filePath, entries, "mcpServers");
|
|
12125
12199
|
} else {
|
|
12126
|
-
const settingsPath =
|
|
12200
|
+
const settingsPath = join24(homedir7(), ".claude", "settings.json");
|
|
12127
12201
|
const entries = {};
|
|
12128
12202
|
for (const s of servers) {
|
|
12129
12203
|
entries[s.name] = {
|
|
@@ -12136,10 +12210,10 @@ class ClaudeCodeAdapter {
|
|
|
12136
12210
|
mergeJsonMcpServers(settingsPath, entries, "mcpServers");
|
|
12137
12211
|
const pluginDir = this.getPluginDir();
|
|
12138
12212
|
mkdirSync16(pluginDir, { recursive: true });
|
|
12139
|
-
const pluginMcpPath =
|
|
12213
|
+
const pluginMcpPath = join24(pluginDir, ".mcp.json");
|
|
12140
12214
|
const marketDir = this.getMarketplaceDir();
|
|
12141
12215
|
mkdirSync16(marketDir, { recursive: true });
|
|
12142
|
-
const marketplaceMcpPath =
|
|
12216
|
+
const marketplaceMcpPath = join24(marketDir, ".mcp.json");
|
|
12143
12217
|
const pluginEntries = {};
|
|
12144
12218
|
for (const s of servers) {
|
|
12145
12219
|
pluginEntries[s.name] = {
|
|
@@ -12154,44 +12228,44 @@ class ClaudeCodeAdapter {
|
|
|
12154
12228
|
}
|
|
12155
12229
|
}
|
|
12156
12230
|
installSessionStartHook(pluginDir, label) {
|
|
12157
|
-
const hooksDir =
|
|
12231
|
+
const hooksDir = join24(pluginDir, "hooks");
|
|
12158
12232
|
mkdirSync16(hooksDir, { recursive: true });
|
|
12159
|
-
const scriptPath =
|
|
12233
|
+
const scriptPath = join24(hooksDir, "on-session-start.sh");
|
|
12160
12234
|
writeFileSync16(scriptPath, SESSION_START_HOOK_SCRIPT);
|
|
12161
12235
|
try {
|
|
12162
12236
|
chmodSync(scriptPath, 493);
|
|
12163
12237
|
} catch {}
|
|
12164
|
-
writeFileSync16(
|
|
12238
|
+
writeFileSync16(join24(hooksDir, "hooks.json"), JSON.stringify(SESSION_START_HOOKS_MANIFEST, null, 2));
|
|
12165
12239
|
vlog(` [Claude Code] Installed SessionStart hook (${label}) -> ${scriptPath}`);
|
|
12166
12240
|
}
|
|
12167
12241
|
async writeSkills(skills, scope) {
|
|
12168
|
-
const baseDir = scope === "project" ?
|
|
12242
|
+
const baseDir = scope === "project" ? join24(process.cwd(), ".claude", "skills") : join24(homedir7(), ".claude", "skills");
|
|
12169
12243
|
for (const skill of skills) {
|
|
12170
|
-
const skillDir =
|
|
12244
|
+
const skillDir = join24(baseDir, skill.filename);
|
|
12171
12245
|
cleanupOldSkillDir(baseDir, skill);
|
|
12172
12246
|
mkdirSync16(skillDir, { recursive: true });
|
|
12173
|
-
writeFileSync16(
|
|
12247
|
+
writeFileSync16(join24(skillDir, "SKILL.md"), buildSkillMd2(skill));
|
|
12174
12248
|
}
|
|
12175
12249
|
if (scope === "user") {
|
|
12176
12250
|
const pluginDir = this.getPluginDir();
|
|
12177
|
-
const pluginJsonDir =
|
|
12251
|
+
const pluginJsonDir = join24(pluginDir, ".claude-plugin");
|
|
12178
12252
|
mkdirSync16(pluginJsonDir, { recursive: true });
|
|
12179
|
-
writeFileSync16(
|
|
12253
|
+
writeFileSync16(join24(pluginJsonDir, "plugin.json"), JSON.stringify(getPluginJson(), null, 2));
|
|
12180
12254
|
for (const skill of skills) {
|
|
12181
|
-
const skillDir =
|
|
12182
|
-
cleanupOldSkillDir(
|
|
12255
|
+
const skillDir = join24(pluginDir, "skills", skill.filename);
|
|
12256
|
+
cleanupOldSkillDir(join24(pluginDir, "skills"), skill);
|
|
12183
12257
|
mkdirSync16(skillDir, { recursive: true });
|
|
12184
|
-
writeFileSync16(
|
|
12258
|
+
writeFileSync16(join24(skillDir, "SKILL.md"), buildSkillMd2(skill));
|
|
12185
12259
|
}
|
|
12186
12260
|
const marketDir = this.getMarketplaceDir();
|
|
12187
|
-
const marketPluginJsonDir =
|
|
12261
|
+
const marketPluginJsonDir = join24(marketDir, ".claude-plugin");
|
|
12188
12262
|
mkdirSync16(marketPluginJsonDir, { recursive: true });
|
|
12189
|
-
writeFileSync16(
|
|
12263
|
+
writeFileSync16(join24(marketPluginJsonDir, "plugin.json"), JSON.stringify(getPluginJson(), null, 2));
|
|
12190
12264
|
for (const skill of skills) {
|
|
12191
|
-
const skillDir =
|
|
12192
|
-
cleanupOldSkillDir(
|
|
12265
|
+
const skillDir = join24(marketDir, "skills", skill.filename);
|
|
12266
|
+
cleanupOldSkillDir(join24(marketDir, "skills"), skill);
|
|
12193
12267
|
mkdirSync16(skillDir, { recursive: true });
|
|
12194
|
-
writeFileSync16(
|
|
12268
|
+
writeFileSync16(join24(skillDir, "SKILL.md"), buildSkillMd2(skill));
|
|
12195
12269
|
}
|
|
12196
12270
|
this.registerPlugin(pluginDir);
|
|
12197
12271
|
}
|
|
@@ -12210,11 +12284,11 @@ class ClaudeCodeAdapter {
|
|
|
12210
12284
|
}
|
|
12211
12285
|
}
|
|
12212
12286
|
async writeInstructionHint(hint, scope) {
|
|
12213
|
-
const filePath = scope === "project" ?
|
|
12287
|
+
const filePath = scope === "project" ? join24(process.cwd(), ".claude", "CLAUDE.md") : join24(homedir7(), ".claude", "CLAUDE.md");
|
|
12214
12288
|
writeHintToFile(filePath, hint);
|
|
12215
12289
|
}
|
|
12216
12290
|
async writeTeamInstructions(instructions, scope) {
|
|
12217
|
-
const filePath = scope === "project" ?
|
|
12291
|
+
const filePath = scope === "project" ? join24(process.cwd(), ".claude", "CLAUDE.md") : join24(homedir7(), ".claude", "CLAUDE.md");
|
|
12218
12292
|
writeTeamInstructionsToFile(filePath, instructions);
|
|
12219
12293
|
if (scope === "user") {
|
|
12220
12294
|
const skillContent = `---
|
|
@@ -12226,16 +12300,16 @@ ${instructions}`;
|
|
|
12226
12300
|
const pluginDir = this.getPluginDir();
|
|
12227
12301
|
const marketDir = this.getMarketplaceDir();
|
|
12228
12302
|
for (const dir of [
|
|
12229
|
-
|
|
12230
|
-
|
|
12303
|
+
join24(pluginDir, "skills", "runwork-team-instructions"),
|
|
12304
|
+
join24(marketDir, "skills", "runwork-team-instructions")
|
|
12231
12305
|
]) {
|
|
12232
12306
|
mkdirSync16(dir, { recursive: true });
|
|
12233
|
-
writeFileSync16(
|
|
12307
|
+
writeFileSync16(join24(dir, "SKILL.md"), skillContent);
|
|
12234
12308
|
}
|
|
12235
12309
|
}
|
|
12236
12310
|
}
|
|
12237
12311
|
async writeAgentConfig(config, scope, baseline) {
|
|
12238
|
-
const settingsPath = scope === "project" ?
|
|
12312
|
+
const settingsPath = scope === "project" ? join24(process.cwd(), ".claude", "settings.json") : join24(homedir7(), ".claude", "settings.json");
|
|
12239
12313
|
const hadFile = existsSync30(settingsPath);
|
|
12240
12314
|
let settings = {};
|
|
12241
12315
|
if (hadFile) {
|
|
@@ -12270,7 +12344,7 @@ ${instructions}`;
|
|
|
12270
12344
|
}
|
|
12271
12345
|
if (!hadFile && !config.modelPreference && !config.permissionRules)
|
|
12272
12346
|
return;
|
|
12273
|
-
mkdirSync16(
|
|
12347
|
+
mkdirSync16(join24(settingsPath, ".."), { recursive: true });
|
|
12274
12348
|
writeFileSync16(settingsPath, JSON.stringify(settings, null, 2));
|
|
12275
12349
|
}
|
|
12276
12350
|
async readManagedBlock(_scope) {
|
|
@@ -12280,26 +12354,26 @@ ${instructions}`;
|
|
|
12280
12354
|
if (!skillFilenames.length)
|
|
12281
12355
|
return;
|
|
12282
12356
|
const allowed = new Set(skillFilenames);
|
|
12283
|
-
const roots = scope === "project" ? [
|
|
12284
|
-
|
|
12285
|
-
|
|
12286
|
-
|
|
12357
|
+
const roots = scope === "project" ? [join24(process.cwd(), ".claude", "skills")] : [
|
|
12358
|
+
join24(homedir7(), ".claude", "skills"),
|
|
12359
|
+
join24(this.getPluginDir(), "skills"),
|
|
12360
|
+
join24(this.getMarketplaceDir(), "skills")
|
|
12287
12361
|
];
|
|
12288
12362
|
for (const dir of roots)
|
|
12289
12363
|
removeMatchingSkillDirs(dir, allowed);
|
|
12290
12364
|
}
|
|
12291
12365
|
async cleanup(scope, manifest) {
|
|
12292
12366
|
if (scope === "project") {
|
|
12293
|
-
removeRunworkMcpServers(
|
|
12367
|
+
removeRunworkMcpServers(join24(process.cwd(), ".mcp.json"), "mcpServers");
|
|
12294
12368
|
} else {
|
|
12295
|
-
removeRunworkMcpServers(
|
|
12369
|
+
removeRunworkMcpServers(join24(homedir7(), ".claude", "settings.json"), "mcpServers");
|
|
12296
12370
|
}
|
|
12297
12371
|
await this.removeSkills(manifest?.skillFilenames ?? [], scope);
|
|
12298
|
-
const instructionFile = scope === "project" ?
|
|
12372
|
+
const instructionFile = scope === "project" ? join24(process.cwd(), ".claude", "CLAUDE.md") : join24(homedir7(), ".claude", "CLAUDE.md");
|
|
12299
12373
|
removeHintFromFile(instructionFile);
|
|
12300
12374
|
removeTeamInstructionsFromFile(instructionFile);
|
|
12301
12375
|
if (scope === "user") {
|
|
12302
|
-
const settingsPath =
|
|
12376
|
+
const settingsPath = join24(homedir7(), ".claude", "settings.json");
|
|
12303
12377
|
if (existsSync30(settingsPath)) {
|
|
12304
12378
|
try {
|
|
12305
12379
|
const settings = JSON.parse(readFileSync25(settingsPath, "utf-8"));
|
|
@@ -12331,7 +12405,7 @@ ${instructions}`;
|
|
|
12331
12405
|
}
|
|
12332
12406
|
}
|
|
12333
12407
|
const pluginsBase = this.getPluginsBaseDir();
|
|
12334
|
-
const installedPath =
|
|
12408
|
+
const installedPath = join24(pluginsBase, "installed_plugins.json");
|
|
12335
12409
|
if (existsSync30(installedPath)) {
|
|
12336
12410
|
try {
|
|
12337
12411
|
const installed = readJsonConfig(installedPath);
|
|
@@ -12344,7 +12418,7 @@ ${instructions}`;
|
|
|
12344
12418
|
}
|
|
12345
12419
|
} catch {}
|
|
12346
12420
|
}
|
|
12347
|
-
const marketplacesPath =
|
|
12421
|
+
const marketplacesPath = join24(pluginsBase, "known_marketplaces.json");
|
|
12348
12422
|
if (existsSync30(marketplacesPath)) {
|
|
12349
12423
|
try {
|
|
12350
12424
|
const marketplaces = readJsonConfig(marketplacesPath);
|
|
@@ -12356,10 +12430,10 @@ ${instructions}`;
|
|
|
12356
12430
|
}
|
|
12357
12431
|
async readUsageStats(lastSyncAt) {
|
|
12358
12432
|
try {
|
|
12359
|
-
const claudeDir =
|
|
12433
|
+
const claudeDir = join24(homedir7(), ".claude");
|
|
12360
12434
|
if (!existsSync30(claudeDir))
|
|
12361
12435
|
return null;
|
|
12362
|
-
const projectsDir =
|
|
12436
|
+
const projectsDir = join24(claudeDir, "projects");
|
|
12363
12437
|
if (!existsSync30(projectsDir))
|
|
12364
12438
|
return null;
|
|
12365
12439
|
const sinceMs = lastSyncAt ? new Date(lastSyncAt).getTime() : 0;
|
|
@@ -12379,7 +12453,7 @@ ${instructions}`;
|
|
|
12379
12453
|
return null;
|
|
12380
12454
|
}
|
|
12381
12455
|
for (const cwd of cwdEntries) {
|
|
12382
|
-
const cwdPath =
|
|
12456
|
+
const cwdPath = join24(projectsDir, cwd);
|
|
12383
12457
|
let files;
|
|
12384
12458
|
try {
|
|
12385
12459
|
files = readdirSync7(cwdPath);
|
|
@@ -12389,7 +12463,7 @@ ${instructions}`;
|
|
|
12389
12463
|
for (const file of files) {
|
|
12390
12464
|
if (!file.endsWith(".jsonl"))
|
|
12391
12465
|
continue;
|
|
12392
|
-
const filePath =
|
|
12466
|
+
const filePath = join24(cwdPath, file);
|
|
12393
12467
|
let stat;
|
|
12394
12468
|
try {
|
|
12395
12469
|
stat = statSync4(filePath);
|
|
@@ -12486,7 +12560,7 @@ ${instructions}`;
|
|
|
12486
12560
|
}
|
|
12487
12561
|
async readSessionDigests(sinceISO) {
|
|
12488
12562
|
try {
|
|
12489
|
-
const projectsDir =
|
|
12563
|
+
const projectsDir = join24(homedir7(), ".claude", "projects");
|
|
12490
12564
|
if (!existsSync30(projectsDir))
|
|
12491
12565
|
return null;
|
|
12492
12566
|
const sinceMs = sinceISO ? new Date(sinceISO).getTime() : 0;
|
|
@@ -12498,7 +12572,7 @@ ${instructions}`;
|
|
|
12498
12572
|
}
|
|
12499
12573
|
const digests = [];
|
|
12500
12574
|
for (const cwd of cwdEntries) {
|
|
12501
|
-
const cwdPath =
|
|
12575
|
+
const cwdPath = join24(projectsDir, cwd);
|
|
12502
12576
|
let files;
|
|
12503
12577
|
try {
|
|
12504
12578
|
files = readdirSync7(cwdPath);
|
|
@@ -12508,7 +12582,7 @@ ${instructions}`;
|
|
|
12508
12582
|
for (const file of files) {
|
|
12509
12583
|
if (!file.endsWith(".jsonl"))
|
|
12510
12584
|
continue;
|
|
12511
|
-
const filePath =
|
|
12585
|
+
const filePath = join24(cwdPath, file);
|
|
12512
12586
|
let stat;
|
|
12513
12587
|
try {
|
|
12514
12588
|
stat = statSync4(filePath);
|
|
@@ -12535,7 +12609,7 @@ ${instructions}`;
|
|
|
12535
12609
|
}
|
|
12536
12610
|
async readSkillUsage(lastSyncAt) {
|
|
12537
12611
|
try {
|
|
12538
|
-
const projectsDir =
|
|
12612
|
+
const projectsDir = join24(homedir7(), ".claude", "projects");
|
|
12539
12613
|
if (!existsSync30(projectsDir))
|
|
12540
12614
|
return null;
|
|
12541
12615
|
const sinceMs = lastSyncAt ? new Date(lastSyncAt).getTime() : 0;
|
|
@@ -12557,7 +12631,7 @@ ${instructions}`;
|
|
|
12557
12631
|
return null;
|
|
12558
12632
|
}
|
|
12559
12633
|
for (const cwd of cwdEntries) {
|
|
12560
|
-
const cwdPath =
|
|
12634
|
+
const cwdPath = join24(projectsDir, cwd);
|
|
12561
12635
|
let files;
|
|
12562
12636
|
try {
|
|
12563
12637
|
files = readdirSync7(cwdPath);
|
|
@@ -12567,7 +12641,7 @@ ${instructions}`;
|
|
|
12567
12641
|
for (const file of files) {
|
|
12568
12642
|
if (!file.endsWith(".jsonl"))
|
|
12569
12643
|
continue;
|
|
12570
|
-
const filePath =
|
|
12644
|
+
const filePath = join24(cwdPath, file);
|
|
12571
12645
|
let fileStat;
|
|
12572
12646
|
try {
|
|
12573
12647
|
fileStat = statSync4(filePath);
|
|
@@ -12668,21 +12742,21 @@ ${instructions}`;
|
|
|
12668
12742
|
}
|
|
12669
12743
|
}
|
|
12670
12744
|
getPluginsBaseDir() {
|
|
12671
|
-
return
|
|
12745
|
+
return join24(homedir7(), ".claude", "plugins");
|
|
12672
12746
|
}
|
|
12673
12747
|
getPluginDir() {
|
|
12674
|
-
return
|
|
12748
|
+
return join24(this.getPluginsBaseDir(), "cache", "runwork", PLUGIN_NAME, PLUGIN_VERSION);
|
|
12675
12749
|
}
|
|
12676
12750
|
getMarketplaceRoot() {
|
|
12677
|
-
return
|
|
12751
|
+
return join24(this.getPluginsBaseDir(), "marketplaces", "runwork");
|
|
12678
12752
|
}
|
|
12679
12753
|
getMarketplaceDir() {
|
|
12680
|
-
return
|
|
12754
|
+
return join24(this.getMarketplaceRoot(), "plugins", PLUGIN_NAME);
|
|
12681
12755
|
}
|
|
12682
12756
|
registerPlugin(pluginDir) {
|
|
12683
12757
|
const pluginsBase = this.getPluginsBaseDir();
|
|
12684
12758
|
mkdirSync16(pluginsBase, { recursive: true });
|
|
12685
|
-
const installedPath =
|
|
12759
|
+
const installedPath = join24(pluginsBase, "installed_plugins.json");
|
|
12686
12760
|
const installed = readJsonConfig(installedPath);
|
|
12687
12761
|
if (!installed.version)
|
|
12688
12762
|
installed.version = 2;
|
|
@@ -12699,10 +12773,10 @@ ${instructions}`;
|
|
|
12699
12773
|
}];
|
|
12700
12774
|
writeJsonConfig(installedPath, installed);
|
|
12701
12775
|
const marketRoot = this.getMarketplaceRoot();
|
|
12702
|
-
const marketCatalogDir =
|
|
12776
|
+
const marketCatalogDir = join24(marketRoot, ".claude-plugin");
|
|
12703
12777
|
mkdirSync16(marketCatalogDir, { recursive: true });
|
|
12704
|
-
writeFileSync16(
|
|
12705
|
-
const marketplacesPath =
|
|
12778
|
+
writeFileSync16(join24(marketCatalogDir, "marketplace.json"), JSON.stringify(getMarketplaceJson(), null, 2));
|
|
12779
|
+
const marketplacesPath = join24(pluginsBase, "known_marketplaces.json");
|
|
12706
12780
|
const marketplaces = readJsonConfig(marketplacesPath);
|
|
12707
12781
|
marketplaces["runwork"] = {
|
|
12708
12782
|
source: { source: "directory", path: marketRoot },
|
|
@@ -12711,7 +12785,7 @@ ${instructions}`;
|
|
|
12711
12785
|
};
|
|
12712
12786
|
writeJsonConfig(marketplacesPath, marketplaces);
|
|
12713
12787
|
vlog(` [Claude Code] ${wasRegistered ? "Refreshed" : "Registered"} plugin ` + `${pluginKey} v${PLUGIN_VERSION} -> ${installedPath}`);
|
|
12714
|
-
const settingsPath =
|
|
12788
|
+
const settingsPath = join24(homedir7(), ".claude", "settings.json");
|
|
12715
12789
|
const settings = readJsonConfig(settingsPath);
|
|
12716
12790
|
if (!settings["enabledPlugins"]) {
|
|
12717
12791
|
settings["enabledPlugins"] = {};
|
|
@@ -12726,23 +12800,23 @@ ${instructions}`;
|
|
|
12726
12800
|
function cleanupOldSkillDir(baseDir, skill) {
|
|
12727
12801
|
if (skill.name === skill.filename)
|
|
12728
12802
|
return;
|
|
12729
|
-
const oldDir =
|
|
12803
|
+
const oldDir = join24(baseDir, skill.name);
|
|
12730
12804
|
moveToTrash(oldDir, `skill renamed to ${skill.filename}`);
|
|
12731
12805
|
}
|
|
12732
12806
|
|
|
12733
12807
|
// src/agents/claude-desktop.ts
|
|
12734
12808
|
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
|
|
12736
|
-
import { homedir as
|
|
12809
|
+
import { dirname as dirname9, join as join26 } from "path";
|
|
12810
|
+
import { homedir as homedir8, platform as platform3, tmpdir as tmpdir3 } from "os";
|
|
12737
12811
|
init_zip();
|
|
12738
12812
|
|
|
12739
12813
|
// src/agents/claude-desktop-plugin-tree.ts
|
|
12740
12814
|
import { chmodSync as chmodSync2, mkdirSync as mkdirSync17, rmSync as rmSync6, existsSync as existsSync31, writeFileSync as writeFileSync17 } from "fs";
|
|
12741
|
-
import { join as
|
|
12815
|
+
import { join as join25 } from "path";
|
|
12742
12816
|
function writePluginMetadata(destDir, metadata) {
|
|
12743
|
-
const pluginJsonDir =
|
|
12817
|
+
const pluginJsonDir = join25(destDir, ".claude-plugin");
|
|
12744
12818
|
mkdirSync17(pluginJsonDir, { recursive: true });
|
|
12745
|
-
writeFileSync17(
|
|
12819
|
+
writeFileSync17(join25(pluginJsonDir, "plugin.json"), JSON.stringify({
|
|
12746
12820
|
name: metadata.pluginName,
|
|
12747
12821
|
version: metadata.pluginVersion,
|
|
12748
12822
|
description: metadata.description,
|
|
@@ -12769,10 +12843,10 @@ function writePluginMcpConfig(destDir, mcpServers) {
|
|
|
12769
12843
|
};
|
|
12770
12844
|
}
|
|
12771
12845
|
}
|
|
12772
|
-
writeFileSync17(
|
|
12846
|
+
writeFileSync17(join25(destDir, ".mcp.json"), JSON.stringify({ mcpServers: mcpEntries }, null, 2));
|
|
12773
12847
|
}
|
|
12774
12848
|
function writePluginTeamInstructions(destDir, instructions) {
|
|
12775
|
-
const skillDir =
|
|
12849
|
+
const skillDir = join25(destDir, "skills", "runwork-team-instructions");
|
|
12776
12850
|
mkdirSync17(skillDir, { recursive: true });
|
|
12777
12851
|
const skillContent = `---
|
|
12778
12852
|
name: runwork-team-instructions
|
|
@@ -12780,34 +12854,34 @@ description: Team instructions from your Runwork workspace. Always follow these
|
|
|
12780
12854
|
---
|
|
12781
12855
|
|
|
12782
12856
|
${instructions}`;
|
|
12783
|
-
writeFileSync17(
|
|
12857
|
+
writeFileSync17(join25(skillDir, "SKILL.md"), skillContent);
|
|
12784
12858
|
}
|
|
12785
12859
|
function writePluginSkills(destDir, skills) {
|
|
12786
|
-
const skillsRoot =
|
|
12860
|
+
const skillsRoot = join25(destDir, "skills");
|
|
12787
12861
|
mkdirSync17(skillsRoot, { recursive: true });
|
|
12788
12862
|
for (const skill of skills) {
|
|
12789
12863
|
if (skill.name !== skill.filename) {
|
|
12790
|
-
const legacyDir =
|
|
12864
|
+
const legacyDir = join25(skillsRoot, skill.name);
|
|
12791
12865
|
if (existsSync31(legacyDir)) {
|
|
12792
12866
|
try {
|
|
12793
12867
|
rmSync6(legacyDir, { recursive: true, force: true });
|
|
12794
12868
|
} catch {}
|
|
12795
12869
|
}
|
|
12796
12870
|
}
|
|
12797
|
-
const skillDir =
|
|
12871
|
+
const skillDir = join25(skillsRoot, skill.filename);
|
|
12798
12872
|
mkdirSync17(skillDir, { recursive: true });
|
|
12799
|
-
writeFileSync17(
|
|
12873
|
+
writeFileSync17(join25(skillDir, "SKILL.md"), buildSkillMd2(skill));
|
|
12800
12874
|
}
|
|
12801
12875
|
}
|
|
12802
12876
|
function writePluginSessionStartHook(destDir) {
|
|
12803
|
-
const hooksDir =
|
|
12877
|
+
const hooksDir = join25(destDir, "hooks");
|
|
12804
12878
|
mkdirSync17(hooksDir, { recursive: true });
|
|
12805
|
-
const scriptPath =
|
|
12879
|
+
const scriptPath = join25(hooksDir, "on-session-start.sh");
|
|
12806
12880
|
writeFileSync17(scriptPath, SESSION_START_HOOK_SCRIPT);
|
|
12807
12881
|
try {
|
|
12808
12882
|
chmodSync2(scriptPath, 493);
|
|
12809
12883
|
} catch {}
|
|
12810
|
-
writeFileSync17(
|
|
12884
|
+
writeFileSync17(join25(hooksDir, "hooks.json"), JSON.stringify(SESSION_START_HOOKS_MANIFEST, null, 2));
|
|
12811
12885
|
console.log(` [Claude Desktop (Cowork)] Bundled SessionStart hook -> ${scriptPath}`);
|
|
12812
12886
|
}
|
|
12813
12887
|
function writePluginTree(destDir, input) {
|
|
@@ -12842,22 +12916,22 @@ function getPluginMetadata() {
|
|
|
12842
12916
|
function getMcpConfigPath() {
|
|
12843
12917
|
const os2 = platform3();
|
|
12844
12918
|
if (os2 === "darwin") {
|
|
12845
|
-
return
|
|
12919
|
+
return join26(homedir8(), "Library", "Application Support", "Claude", "claude_desktop_config.json");
|
|
12846
12920
|
}
|
|
12847
12921
|
if (os2 === "win32") {
|
|
12848
|
-
return
|
|
12922
|
+
return join26(process.env.APPDATA || join26(homedir8(), "AppData", "Roaming"), "Claude", "claude_desktop_config.json");
|
|
12849
12923
|
}
|
|
12850
|
-
return
|
|
12924
|
+
return join26(homedir8(), ".config", "Claude", "claude_desktop_config.json");
|
|
12851
12925
|
}
|
|
12852
12926
|
function getCoworkBaseDir() {
|
|
12853
12927
|
const os2 = platform3();
|
|
12854
12928
|
if (os2 === "darwin") {
|
|
12855
|
-
return
|
|
12929
|
+
return join26(homedir8(), "Library", "Application Support", "Claude", "local-agent-mode-sessions");
|
|
12856
12930
|
}
|
|
12857
12931
|
if (os2 === "win32") {
|
|
12858
|
-
return
|
|
12932
|
+
return join26(process.env.APPDATA || join26(homedir8(), "AppData", "Roaming"), "Claude", "local-agent-mode-sessions");
|
|
12859
12933
|
}
|
|
12860
|
-
return
|
|
12934
|
+
return join26(homedir8(), ".config", "Claude", "local-agent-mode-sessions");
|
|
12861
12935
|
}
|
|
12862
12936
|
function listOrgDirs() {
|
|
12863
12937
|
const baseDir = getCoworkBaseDir();
|
|
@@ -12867,10 +12941,10 @@ function listOrgDirs() {
|
|
|
12867
12941
|
try {
|
|
12868
12942
|
const sessionDirs = readdirSync8(baseDir).filter((d) => !d.startsWith(".") && d !== "skills-plugin");
|
|
12869
12943
|
for (const sessionId of sessionDirs) {
|
|
12870
|
-
const sessionPath =
|
|
12944
|
+
const sessionPath = join26(baseDir, sessionId);
|
|
12871
12945
|
try {
|
|
12872
12946
|
for (const orgId of readdirSync8(sessionPath).filter((d) => !d.startsWith("."))) {
|
|
12873
|
-
dirs.push(
|
|
12947
|
+
dirs.push(join26(sessionPath, orgId));
|
|
12874
12948
|
}
|
|
12875
12949
|
} catch {
|
|
12876
12950
|
continue;
|
|
@@ -12886,7 +12960,7 @@ function walkOrgDirs(visit) {
|
|
|
12886
12960
|
try {
|
|
12887
12961
|
const sessionDirs = readdirSync8(baseDir).filter((d) => !d.startsWith(".") && d !== "skills-plugin");
|
|
12888
12962
|
for (const sessionId of sessionDirs) {
|
|
12889
|
-
const sessionPath =
|
|
12963
|
+
const sessionPath = join26(baseDir, sessionId);
|
|
12890
12964
|
let orgDirs;
|
|
12891
12965
|
try {
|
|
12892
12966
|
orgDirs = readdirSync8(sessionPath).filter((d) => !d.startsWith("."));
|
|
@@ -12894,7 +12968,7 @@ function walkOrgDirs(visit) {
|
|
|
12894
12968
|
continue;
|
|
12895
12969
|
}
|
|
12896
12970
|
for (const orgId of orgDirs) {
|
|
12897
|
-
const orgDir =
|
|
12971
|
+
const orgDir = join26(sessionPath, orgId);
|
|
12898
12972
|
const result = visit(orgDir);
|
|
12899
12973
|
if (result !== null)
|
|
12900
12974
|
return result;
|
|
@@ -12911,7 +12985,7 @@ function getCoworkMemoryClaudeMdPaths() {
|
|
|
12911
12985
|
try {
|
|
12912
12986
|
const sessionDirs = readdirSync8(baseDir).filter((d) => !d.startsWith(".") && d !== "skills-plugin");
|
|
12913
12987
|
for (const sessionId of sessionDirs) {
|
|
12914
|
-
const sessionPath =
|
|
12988
|
+
const sessionPath = join26(baseDir, sessionId);
|
|
12915
12989
|
let orgDirs;
|
|
12916
12990
|
try {
|
|
12917
12991
|
orgDirs = readdirSync8(sessionPath).filter((d) => !d.startsWith("."));
|
|
@@ -12919,7 +12993,7 @@ function getCoworkMemoryClaudeMdPaths() {
|
|
|
12919
12993
|
continue;
|
|
12920
12994
|
}
|
|
12921
12995
|
for (const orgId of orgDirs) {
|
|
12922
|
-
paths.push(
|
|
12996
|
+
paths.push(join26(sessionPath, orgId, "memory", "CLAUDE.md"));
|
|
12923
12997
|
}
|
|
12924
12998
|
}
|
|
12925
12999
|
} catch {}
|
|
@@ -12927,13 +13001,13 @@ function getCoworkMemoryClaudeMdPaths() {
|
|
|
12927
13001
|
}
|
|
12928
13002
|
function findCoworkPluginsDir() {
|
|
12929
13003
|
return walkOrgDirs((orgDir) => {
|
|
12930
|
-
const pluginsDir =
|
|
12931
|
-
return existsSync32(
|
|
13004
|
+
const pluginsDir = join26(orgDir, "cowork_plugins");
|
|
13005
|
+
return existsSync32(join26(pluginsDir, "installed_plugins.json")) ? pluginsDir : null;
|
|
12932
13006
|
});
|
|
12933
13007
|
}
|
|
12934
13008
|
function findRpmPluginByName(pluginName) {
|
|
12935
13009
|
return walkOrgDirs((orgDir) => {
|
|
12936
|
-
const manifestPath =
|
|
13010
|
+
const manifestPath = join26(orgDir, "rpm", "manifest.json");
|
|
12937
13011
|
if (!existsSync32(manifestPath))
|
|
12938
13012
|
return null;
|
|
12939
13013
|
try {
|
|
@@ -12942,7 +13016,7 @@ function findRpmPluginByName(pluginName) {
|
|
|
12942
13016
|
if (!entry?.id)
|
|
12943
13017
|
return null;
|
|
12944
13018
|
return {
|
|
12945
|
-
pluginPath:
|
|
13019
|
+
pluginPath: join26(orgDir, "rpm", entry.id),
|
|
12946
13020
|
orgDir
|
|
12947
13021
|
};
|
|
12948
13022
|
} catch {
|
|
@@ -12964,7 +13038,7 @@ function getMarketplaceJson2() {
|
|
|
12964
13038
|
};
|
|
12965
13039
|
}
|
|
12966
13040
|
function getCoworkSettingsPath(pluginsDir) {
|
|
12967
|
-
return
|
|
13041
|
+
return join26(dirname9(pluginsDir), "cowork_settings.json");
|
|
12968
13042
|
}
|
|
12969
13043
|
function setCoworkPluginEnabled(pluginsDir, enabled) {
|
|
12970
13044
|
const settingsPath = getCoworkSettingsPath(pluginsDir);
|
|
@@ -13012,8 +13086,8 @@ class ClaudeDesktopAdapter {
|
|
|
13012
13086
|
}
|
|
13013
13087
|
const pluginsDir = findCoworkPluginsDir();
|
|
13014
13088
|
if (pluginsDir) {
|
|
13015
|
-
const cacheDir =
|
|
13016
|
-
const marketDir =
|
|
13089
|
+
const cacheDir = join26(pluginsDir, "cache", "runwork", PLUGIN_NAME2, PLUGIN_VERSION2);
|
|
13090
|
+
const marketDir = join26(pluginsDir, "marketplaces", "runwork", "plugins", PLUGIN_NAME2);
|
|
13017
13091
|
for (const dir of [cacheDir, marketDir]) {
|
|
13018
13092
|
writePluginMcpConfig(dir, servers);
|
|
13019
13093
|
}
|
|
@@ -13031,14 +13105,14 @@ class ClaudeDesktopAdapter {
|
|
|
13031
13105
|
console.warn(" [Claude Desktop] Cowork plugins directory not found. Open Claude Desktop at least once first.");
|
|
13032
13106
|
return 0;
|
|
13033
13107
|
}
|
|
13034
|
-
const cacheDir =
|
|
13035
|
-
const marketRoot =
|
|
13036
|
-
const marketDir =
|
|
13108
|
+
const cacheDir = join26(pluginsDir, "cache", "runwork", PLUGIN_NAME2, PLUGIN_VERSION2);
|
|
13109
|
+
const marketRoot = join26(pluginsDir, "marketplaces", "runwork");
|
|
13110
|
+
const marketDir = join26(marketRoot, "plugins", PLUGIN_NAME2);
|
|
13037
13111
|
for (const dir of [cacheDir, marketDir]) {
|
|
13038
13112
|
writePluginMetadata(dir, getPluginMetadata());
|
|
13039
13113
|
writePluginSkills(dir, skills);
|
|
13040
13114
|
}
|
|
13041
|
-
const installedPath =
|
|
13115
|
+
const installedPath = join26(pluginsDir, "installed_plugins.json");
|
|
13042
13116
|
const installed = readJsonConfig(installedPath);
|
|
13043
13117
|
if (!installed.version)
|
|
13044
13118
|
installed.version = 2;
|
|
@@ -13052,10 +13126,10 @@ class ClaudeDesktopAdapter {
|
|
|
13052
13126
|
lastUpdated: new Date().toISOString()
|
|
13053
13127
|
}];
|
|
13054
13128
|
writeJsonConfig(installedPath, installed);
|
|
13055
|
-
const marketCatalogDir =
|
|
13129
|
+
const marketCatalogDir = join26(marketRoot, ".claude-plugin");
|
|
13056
13130
|
mkdirSync18(marketCatalogDir, { recursive: true });
|
|
13057
|
-
writeFileSync18(
|
|
13058
|
-
const marketplacesPath =
|
|
13131
|
+
writeFileSync18(join26(marketCatalogDir, "marketplace.json"), JSON.stringify(getMarketplaceJson2(), null, 2));
|
|
13132
|
+
const marketplacesPath = join26(pluginsDir, "known_marketplaces.json");
|
|
13059
13133
|
const marketplaces = readJsonConfig(marketplacesPath);
|
|
13060
13134
|
marketplaces["runwork"] = {
|
|
13061
13135
|
source: { source: "directory", path: marketRoot },
|
|
@@ -13074,8 +13148,8 @@ class ClaudeDesktopAdapter {
|
|
|
13074
13148
|
}
|
|
13075
13149
|
}
|
|
13076
13150
|
async buildPluginZip(skills, mcpServers, teamInstructions, outputPath) {
|
|
13077
|
-
const stagingRoot = mkdtempSync3(
|
|
13078
|
-
const pluginStagingDir =
|
|
13151
|
+
const stagingRoot = mkdtempSync3(join26(tmpdir3(), "runwork-plugin-"));
|
|
13152
|
+
const pluginStagingDir = join26(stagingRoot, PLUGIN_NAME2);
|
|
13079
13153
|
try {
|
|
13080
13154
|
writePluginTree(pluginStagingDir, {
|
|
13081
13155
|
...getPluginMetadata(),
|
|
@@ -13106,8 +13180,8 @@ class ClaudeDesktopAdapter {
|
|
|
13106
13180
|
const pluginsDir = findCoworkPluginsDir();
|
|
13107
13181
|
if (!pluginsDir)
|
|
13108
13182
|
return;
|
|
13109
|
-
writePluginTeamInstructions(
|
|
13110
|
-
writePluginTeamInstructions(
|
|
13183
|
+
writePluginTeamInstructions(join26(pluginsDir, "cache", "runwork", PLUGIN_NAME2, PLUGIN_VERSION2), instructions);
|
|
13184
|
+
writePluginTeamInstructions(join26(pluginsDir, "marketplaces", "runwork", "plugins", PLUGIN_NAME2), instructions);
|
|
13111
13185
|
}
|
|
13112
13186
|
async writeAgentConfig(config, _scope) {
|
|
13113
13187
|
const configPath = getMcpConfigPath();
|
|
@@ -13149,7 +13223,7 @@ class ClaudeDesktopAdapter {
|
|
|
13149
13223
|
} catch {}
|
|
13150
13224
|
}
|
|
13151
13225
|
for (const orgDir of listOrgDirs()) {
|
|
13152
|
-
const manifestPath =
|
|
13226
|
+
const manifestPath = join26(orgDir, "rpm", "manifest.json");
|
|
13153
13227
|
if (!existsSync32(manifestPath))
|
|
13154
13228
|
continue;
|
|
13155
13229
|
try {
|
|
@@ -13162,7 +13236,7 @@ class ClaudeDesktopAdapter {
|
|
|
13162
13236
|
for (const entry of ours) {
|
|
13163
13237
|
if (!entry.id)
|
|
13164
13238
|
continue;
|
|
13165
|
-
moveToTrash(
|
|
13239
|
+
moveToTrash(join26(orgDir, "rpm", entry.id), `claude-desktop-plugin:${entry.name}`);
|
|
13166
13240
|
}
|
|
13167
13241
|
manifest.plugins = manifest.plugins.filter((p) => !isRunworkRpmPluginName(p.name));
|
|
13168
13242
|
manifest.lastUpdated = Date.now();
|
|
@@ -13170,11 +13244,11 @@ class ClaudeDesktopAdapter {
|
|
|
13170
13244
|
} catch {}
|
|
13171
13245
|
}
|
|
13172
13246
|
for (const orgDir of listOrgDirs()) {
|
|
13173
|
-
const pluginsDir =
|
|
13174
|
-
if (!existsSync32(
|
|
13247
|
+
const pluginsDir = join26(orgDir, "cowork_plugins");
|
|
13248
|
+
if (!existsSync32(join26(pluginsDir, "installed_plugins.json")))
|
|
13175
13249
|
continue;
|
|
13176
|
-
const cacheRunwork =
|
|
13177
|
-
const marketRunwork =
|
|
13250
|
+
const cacheRunwork = join26(pluginsDir, "cache", "runwork");
|
|
13251
|
+
const marketRunwork = join26(pluginsDir, "marketplaces", "runwork");
|
|
13178
13252
|
for (const dir of [cacheRunwork, marketRunwork]) {
|
|
13179
13253
|
if (existsSync32(dir)) {
|
|
13180
13254
|
try {
|
|
@@ -13182,7 +13256,7 @@ class ClaudeDesktopAdapter {
|
|
|
13182
13256
|
} catch {}
|
|
13183
13257
|
}
|
|
13184
13258
|
}
|
|
13185
|
-
const installedPath =
|
|
13259
|
+
const installedPath = join26(pluginsDir, "installed_plugins.json");
|
|
13186
13260
|
try {
|
|
13187
13261
|
const installed = readJsonConfig(installedPath);
|
|
13188
13262
|
if (installed.plugins) {
|
|
@@ -13193,7 +13267,7 @@ class ClaudeDesktopAdapter {
|
|
|
13193
13267
|
writeJsonConfig(installedPath, installed);
|
|
13194
13268
|
}
|
|
13195
13269
|
} catch {}
|
|
13196
|
-
const marketplacesPath =
|
|
13270
|
+
const marketplacesPath = join26(pluginsDir, "known_marketplaces.json");
|
|
13197
13271
|
if (existsSync32(marketplacesPath)) {
|
|
13198
13272
|
try {
|
|
13199
13273
|
const marketplaces = readJsonConfig(marketplacesPath);
|
|
@@ -13224,11 +13298,11 @@ class ClaudeDesktopAdapter {
|
|
|
13224
13298
|
const os2 = platform3();
|
|
13225
13299
|
let claudeAppDir;
|
|
13226
13300
|
if (os2 === "darwin") {
|
|
13227
|
-
claudeAppDir =
|
|
13301
|
+
claudeAppDir = join26(homedir8(), "Library", "Application Support", "Claude");
|
|
13228
13302
|
} else if (os2 === "win32") {
|
|
13229
|
-
claudeAppDir =
|
|
13303
|
+
claudeAppDir = join26(process.env.APPDATA || join26(homedir8(), "AppData", "Roaming"), "Claude");
|
|
13230
13304
|
} else {
|
|
13231
|
-
claudeAppDir =
|
|
13305
|
+
claudeAppDir = join26(homedir8(), ".config", "Claude");
|
|
13232
13306
|
}
|
|
13233
13307
|
if (!existsSync32(claudeAppDir))
|
|
13234
13308
|
return null;
|
|
@@ -13237,7 +13311,7 @@ class ClaudeDesktopAdapter {
|
|
|
13237
13311
|
let latestActivity = 0;
|
|
13238
13312
|
const modelsUsed = new Set;
|
|
13239
13313
|
let maxMcpTools = 0;
|
|
13240
|
-
const agentSessionsDir =
|
|
13314
|
+
const agentSessionsDir = join26(claudeAppDir, "local-agent-mode-sessions");
|
|
13241
13315
|
const activeDays = new Set;
|
|
13242
13316
|
if (existsSync32(agentSessionsDir)) {
|
|
13243
13317
|
this.walkSessionDirs(agentSessionsDir, sinceMs, (session) => {
|
|
@@ -13257,7 +13331,7 @@ class ClaudeDesktopAdapter {
|
|
|
13257
13331
|
}
|
|
13258
13332
|
});
|
|
13259
13333
|
}
|
|
13260
|
-
const codeSessionsDir =
|
|
13334
|
+
const codeSessionsDir = join26(claudeAppDir, "claude-code-sessions");
|
|
13261
13335
|
if (existsSync32(codeSessionsDir)) {
|
|
13262
13336
|
this.walkSessionDirs(codeSessionsDir, sinceMs, (session) => {
|
|
13263
13337
|
const sessionTime = session.lastActivityAt ?? session.createdAt ?? 0;
|
|
@@ -13272,7 +13346,7 @@ class ClaudeDesktopAdapter {
|
|
|
13272
13346
|
});
|
|
13273
13347
|
}
|
|
13274
13348
|
let scheduledTaskRuns = 0;
|
|
13275
|
-
const scheduledTasksPath =
|
|
13349
|
+
const scheduledTasksPath = join26(claudeAppDir, "scheduled-tasks.json");
|
|
13276
13350
|
if (existsSync32(scheduledTasksPath)) {
|
|
13277
13351
|
try {
|
|
13278
13352
|
const raw = readFileSync26(scheduledTasksPath, "utf-8");
|
|
@@ -13311,13 +13385,13 @@ class ClaudeDesktopAdapter {
|
|
|
13311
13385
|
const os2 = platform3();
|
|
13312
13386
|
let claudeAppDir;
|
|
13313
13387
|
if (os2 === "darwin") {
|
|
13314
|
-
claudeAppDir =
|
|
13388
|
+
claudeAppDir = join26(homedir8(), "Library", "Application Support", "Claude");
|
|
13315
13389
|
} else if (os2 === "win32") {
|
|
13316
|
-
claudeAppDir =
|
|
13390
|
+
claudeAppDir = join26(process.env.APPDATA || join26(homedir8(), "AppData", "Roaming"), "Claude");
|
|
13317
13391
|
} else {
|
|
13318
|
-
claudeAppDir =
|
|
13392
|
+
claudeAppDir = join26(homedir8(), ".config", "Claude");
|
|
13319
13393
|
}
|
|
13320
|
-
const versionPath =
|
|
13394
|
+
const versionPath = join26(claudeAppDir, "claude-code", "sdk-version");
|
|
13321
13395
|
if (existsSync32(versionPath)) {
|
|
13322
13396
|
return readFileSync26(versionPath, "utf-8").trim();
|
|
13323
13397
|
}
|
|
@@ -13331,7 +13405,7 @@ class ClaudeDesktopAdapter {
|
|
|
13331
13405
|
for (const orgDir of readdirSync8(baseDir)) {
|
|
13332
13406
|
if (orgDir.startsWith(".") || orgDir === "skills-plugin")
|
|
13333
13407
|
continue;
|
|
13334
|
-
const orgPath =
|
|
13408
|
+
const orgPath = join26(baseDir, orgDir);
|
|
13335
13409
|
try {
|
|
13336
13410
|
if (!statSync5(orgPath).isDirectory())
|
|
13337
13411
|
continue;
|
|
@@ -13341,7 +13415,7 @@ class ClaudeDesktopAdapter {
|
|
|
13341
13415
|
for (const userDir of readdirSync8(orgPath)) {
|
|
13342
13416
|
if (userDir.startsWith("."))
|
|
13343
13417
|
continue;
|
|
13344
|
-
const userPath =
|
|
13418
|
+
const userPath = join26(orgPath, userDir);
|
|
13345
13419
|
try {
|
|
13346
13420
|
if (!statSync5(userPath).isDirectory())
|
|
13347
13421
|
continue;
|
|
@@ -13352,7 +13426,7 @@ class ClaudeDesktopAdapter {
|
|
|
13352
13426
|
if (!file.endsWith(".json"))
|
|
13353
13427
|
continue;
|
|
13354
13428
|
try {
|
|
13355
|
-
const session = JSON.parse(readFileSync26(
|
|
13429
|
+
const session = JSON.parse(readFileSync26(join26(userPath, file), "utf-8"));
|
|
13356
13430
|
onSession(session);
|
|
13357
13431
|
} catch {
|
|
13358
13432
|
continue;
|
|
@@ -13367,8 +13441,8 @@ class ClaudeDesktopAdapter {
|
|
|
13367
13441
|
// src/agents/cursor.ts
|
|
13368
13442
|
init_subprocess();
|
|
13369
13443
|
import { existsSync as existsSync33, mkdirSync as mkdirSync19, unlinkSync as unlinkSync4, writeFileSync as writeFileSync19 } from "fs";
|
|
13370
|
-
import { join as
|
|
13371
|
-
import { homedir as
|
|
13444
|
+
import { join as join27 } from "path";
|
|
13445
|
+
import { homedir as homedir9, platform as platform4 } from "os";
|
|
13372
13446
|
|
|
13373
13447
|
// src/utils/sqlite-adapter.ts
|
|
13374
13448
|
var adapter = null;
|
|
@@ -13471,7 +13545,7 @@ class CursorAdapter {
|
|
|
13471
13545
|
return true;
|
|
13472
13546
|
}
|
|
13473
13547
|
async writeMcpServers(servers, scope) {
|
|
13474
|
-
const filePath = scope === "project" ?
|
|
13548
|
+
const filePath = scope === "project" ? join27(process.cwd(), ".cursor", "mcp.json") : join27(homedir9(), ".cursor", "mcp.json");
|
|
13475
13549
|
const entries = {};
|
|
13476
13550
|
for (const s of servers) {
|
|
13477
13551
|
entries[s.name] = {
|
|
@@ -13484,7 +13558,7 @@ class CursorAdapter {
|
|
|
13484
13558
|
async writeSkills(skills, scope) {
|
|
13485
13559
|
if (scope === "user")
|
|
13486
13560
|
return 0;
|
|
13487
|
-
const rulesDir =
|
|
13561
|
+
const rulesDir = join27(process.cwd(), ".cursor", "rules");
|
|
13488
13562
|
mkdirSync19(rulesDir, { recursive: true });
|
|
13489
13563
|
for (const skill of skills) {
|
|
13490
13564
|
const mdcContent = `---
|
|
@@ -13493,30 +13567,30 @@ alwaysApply: false
|
|
|
13493
13567
|
---
|
|
13494
13568
|
|
|
13495
13569
|
${skill.content}`;
|
|
13496
|
-
writeFileSync19(
|
|
13570
|
+
writeFileSync19(join27(rulesDir, `${skill.filename}.mdc`), mdcContent);
|
|
13497
13571
|
}
|
|
13498
13572
|
return skills.length;
|
|
13499
13573
|
}
|
|
13500
13574
|
async writeInstructionHint(hint, scope) {
|
|
13501
|
-
const filePath = scope === "project" ?
|
|
13575
|
+
const filePath = scope === "project" ? join27(process.cwd(), ".cursor", "rules", "runwork.mdc") : join27(homedir9(), ".cursor", "rules", "runwork.mdc");
|
|
13502
13576
|
const mdcContent = `---
|
|
13503
13577
|
description: "Runwork workspace connection"
|
|
13504
13578
|
alwaysApply: true
|
|
13505
13579
|
---
|
|
13506
13580
|
|
|
13507
13581
|
${hint}`;
|
|
13508
|
-
mkdirSync19(
|
|
13582
|
+
mkdirSync19(join27(filePath, ".."), { recursive: true });
|
|
13509
13583
|
writeFileSync19(filePath, mdcContent);
|
|
13510
13584
|
}
|
|
13511
13585
|
async writeTeamInstructions(instructions, scope) {
|
|
13512
|
-
const filePath = scope === "project" ?
|
|
13586
|
+
const filePath = scope === "project" ? join27(process.cwd(), ".cursor", "rules", "runwork-team.mdc") : join27(homedir9(), ".cursor", "rules", "runwork-team.mdc");
|
|
13513
13587
|
const mdcContent = `---
|
|
13514
13588
|
description: "Team instructions from Runwork workspace"
|
|
13515
13589
|
alwaysApply: true
|
|
13516
13590
|
---
|
|
13517
13591
|
|
|
13518
13592
|
${instructions}`;
|
|
13519
|
-
mkdirSync19(
|
|
13593
|
+
mkdirSync19(join27(filePath, ".."), { recursive: true });
|
|
13520
13594
|
writeFileSync19(filePath, mdcContent);
|
|
13521
13595
|
}
|
|
13522
13596
|
async writeAgentConfig(config, scope, baseline) {
|
|
@@ -13562,7 +13636,7 @@ ${instructions}`;
|
|
|
13562
13636
|
return;
|
|
13563
13637
|
}
|
|
13564
13638
|
mergeSandboxAllowlist(domains) {
|
|
13565
|
-
const configPath =
|
|
13639
|
+
const configPath = join27(homedir9(), ".cursor", "sandbox.json");
|
|
13566
13640
|
try {
|
|
13567
13641
|
const config = readJsonConfig(configPath);
|
|
13568
13642
|
if (!config.networkPolicy)
|
|
@@ -13580,15 +13654,15 @@ ${instructions}`;
|
|
|
13580
13654
|
async removeSkills(skillFilenames, scope) {
|
|
13581
13655
|
if (scope !== "project")
|
|
13582
13656
|
return;
|
|
13583
|
-
removeMatchingSkillFiles(
|
|
13657
|
+
removeMatchingSkillFiles(join27(process.cwd(), ".cursor", "rules"), new Set(skillFilenames), ".mdc");
|
|
13584
13658
|
}
|
|
13585
13659
|
async cleanup(scope, manifest) {
|
|
13586
|
-
const mcpPath = scope === "project" ?
|
|
13660
|
+
const mcpPath = scope === "project" ? join27(process.cwd(), ".cursor", "mcp.json") : join27(homedir9(), ".cursor", "mcp.json");
|
|
13587
13661
|
removeRunworkMcpServers(mcpPath, "mcpServers");
|
|
13588
|
-
const rulesDir = scope === "project" ?
|
|
13662
|
+
const rulesDir = scope === "project" ? join27(process.cwd(), ".cursor", "rules") : join27(homedir9(), ".cursor", "rules");
|
|
13589
13663
|
if (existsSync33(rulesDir)) {
|
|
13590
13664
|
for (const file of ["runwork.mdc", "runwork-team.mdc"]) {
|
|
13591
|
-
const filePath =
|
|
13665
|
+
const filePath = join27(rulesDir, file);
|
|
13592
13666
|
if (existsSync33(filePath)) {
|
|
13593
13667
|
try {
|
|
13594
13668
|
unlinkSync4(filePath);
|
|
@@ -13670,7 +13744,7 @@ ${instructions}`;
|
|
|
13670
13744
|
}
|
|
13671
13745
|
}
|
|
13672
13746
|
const sessionCount = newComposersWithoutId + activeComposerIds.size;
|
|
13673
|
-
const trackingDbPath =
|
|
13747
|
+
const trackingDbPath = join27(homedir9(), ".cursor", "ai-tracking", "ai-code-tracking.db");
|
|
13674
13748
|
let aiCommitCount = 0;
|
|
13675
13749
|
let avgAiPercent = 0;
|
|
13676
13750
|
if (existsSync33(trackingDbPath)) {
|
|
@@ -13716,12 +13790,12 @@ ${instructions}`;
|
|
|
13716
13790
|
globalStorageDbPath() {
|
|
13717
13791
|
const os2 = platform4();
|
|
13718
13792
|
if (os2 === "darwin") {
|
|
13719
|
-
return
|
|
13793
|
+
return join27(homedir9(), "Library", "Application Support", "Cursor", "User", "globalStorage", "state.vscdb");
|
|
13720
13794
|
}
|
|
13721
13795
|
if (os2 === "win32") {
|
|
13722
|
-
return
|
|
13796
|
+
return join27(process.env.APPDATA || join27(homedir9(), "AppData", "Roaming"), "Cursor", "User", "globalStorage", "state.vscdb");
|
|
13723
13797
|
}
|
|
13724
|
-
return
|
|
13798
|
+
return join27(homedir9(), ".config", "Cursor", "User", "globalStorage", "state.vscdb");
|
|
13725
13799
|
}
|
|
13726
13800
|
async readSessionDigests(sinceISO) {
|
|
13727
13801
|
try {
|
|
@@ -13741,16 +13815,16 @@ ${instructions}`;
|
|
|
13741
13815
|
|
|
13742
13816
|
// src/agents/windsurf.ts
|
|
13743
13817
|
import { existsSync as existsSync34, mkdirSync as mkdirSync20, unlinkSync as unlinkSync5, writeFileSync as writeFileSync20 } from "fs";
|
|
13744
|
-
import { join as
|
|
13745
|
-
import { homedir as
|
|
13818
|
+
import { join as join28 } from "path";
|
|
13819
|
+
import { homedir as homedir10, platform as platform5 } from "os";
|
|
13746
13820
|
function getWindsurfDataDir() {
|
|
13747
13821
|
if (platform5() === "win32") {
|
|
13748
|
-
return
|
|
13822
|
+
return join28(process.env.APPDATA || join28(homedir10(), "AppData", "Roaming"), "Codeium", "windsurf");
|
|
13749
13823
|
}
|
|
13750
|
-
return
|
|
13824
|
+
return join28(homedir10(), ".codeium", "windsurf");
|
|
13751
13825
|
}
|
|
13752
13826
|
function getConfigPath() {
|
|
13753
|
-
return
|
|
13827
|
+
return join28(getWindsurfDataDir(), "mcp_config.json");
|
|
13754
13828
|
}
|
|
13755
13829
|
|
|
13756
13830
|
class WindsurfAdapter {
|
|
@@ -13782,7 +13856,7 @@ class WindsurfAdapter {
|
|
|
13782
13856
|
async writeSkills(skills, scope) {
|
|
13783
13857
|
if (scope === "user")
|
|
13784
13858
|
return 0;
|
|
13785
|
-
const rulesDir =
|
|
13859
|
+
const rulesDir = join28(process.cwd(), ".windsurf", "rules");
|
|
13786
13860
|
mkdirSync20(rulesDir, { recursive: true });
|
|
13787
13861
|
for (const skill of skills) {
|
|
13788
13862
|
const content = `---
|
|
@@ -13790,44 +13864,44 @@ trigger: manual
|
|
|
13790
13864
|
---
|
|
13791
13865
|
|
|
13792
13866
|
${skill.content}`;
|
|
13793
|
-
writeFileSync20(
|
|
13867
|
+
writeFileSync20(join28(rulesDir, `${skill.filename}.md`), content);
|
|
13794
13868
|
}
|
|
13795
13869
|
return skills.length;
|
|
13796
13870
|
}
|
|
13797
13871
|
async writeTeamInstructions(instructions, scope) {
|
|
13798
|
-
const filePath = scope === "project" ?
|
|
13872
|
+
const filePath = scope === "project" ? join28(process.cwd(), ".windsurf", "rules", "runwork-team.md") : join28(getWindsurfDataDir(), "rules", "runwork-team.md");
|
|
13799
13873
|
const content = `---
|
|
13800
13874
|
trigger: always_on
|
|
13801
13875
|
description: "Team instructions from Runwork workspace"
|
|
13802
13876
|
---
|
|
13803
13877
|
|
|
13804
13878
|
${instructions}`;
|
|
13805
|
-
mkdirSync20(
|
|
13879
|
+
mkdirSync20(join28(filePath, ".."), { recursive: true });
|
|
13806
13880
|
writeFileSync20(filePath, content);
|
|
13807
13881
|
}
|
|
13808
13882
|
async writeInstructionHint(hint, scope) {
|
|
13809
|
-
const filePath = scope === "project" ?
|
|
13883
|
+
const filePath = scope === "project" ? join28(process.cwd(), ".windsurf", "rules", "runwork.md") : join28(getWindsurfDataDir(), "rules", "runwork.md");
|
|
13810
13884
|
const content = `---
|
|
13811
13885
|
trigger: always
|
|
13812
13886
|
---
|
|
13813
13887
|
|
|
13814
13888
|
${hint}`;
|
|
13815
|
-
mkdirSync20(
|
|
13889
|
+
mkdirSync20(join28(filePath, ".."), { recursive: true });
|
|
13816
13890
|
writeFileSync20(filePath, content);
|
|
13817
13891
|
}
|
|
13818
13892
|
async removeSkills(skillFilenames, scope) {
|
|
13819
13893
|
if (scope !== "project")
|
|
13820
13894
|
return;
|
|
13821
|
-
removeMatchingSkillFiles(
|
|
13895
|
+
removeMatchingSkillFiles(join28(process.cwd(), ".windsurf", "rules"), new Set(skillFilenames), ".md");
|
|
13822
13896
|
}
|
|
13823
13897
|
async cleanup(scope, manifest) {
|
|
13824
13898
|
if (scope === "user") {
|
|
13825
13899
|
removeRunworkMcpServers(getConfigPath(), "mcpServers");
|
|
13826
13900
|
}
|
|
13827
|
-
const rulesDir = scope === "project" ?
|
|
13901
|
+
const rulesDir = scope === "project" ? join28(process.cwd(), ".windsurf", "rules") : join28(getWindsurfDataDir(), "rules");
|
|
13828
13902
|
if (existsSync34(rulesDir)) {
|
|
13829
13903
|
for (const file of ["runwork.md", "runwork-team.md"]) {
|
|
13830
|
-
const filePath =
|
|
13904
|
+
const filePath = join28(rulesDir, file);
|
|
13831
13905
|
if (existsSync34(filePath)) {
|
|
13832
13906
|
try {
|
|
13833
13907
|
unlinkSync5(filePath);
|
|
@@ -13841,20 +13915,20 @@ ${hint}`;
|
|
|
13841
13915
|
|
|
13842
13916
|
// src/agents/codex.ts
|
|
13843
13917
|
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
|
|
13845
|
-
import { homedir as
|
|
13918
|
+
import { join as join31 } from "path";
|
|
13919
|
+
import { homedir as homedir13 } from "os";
|
|
13846
13920
|
import { parse, stringify } from "smol-toml";
|
|
13847
13921
|
|
|
13848
13922
|
// src/agents/detection.ts
|
|
13849
13923
|
import { execFile } from "child_process";
|
|
13850
13924
|
import { existsSync as existsSync36 } from "fs";
|
|
13851
|
-
import { homedir as
|
|
13852
|
-
import { isAbsolute as isAbsolute2, join as
|
|
13925
|
+
import { homedir as homedir12, platform as platform7 } from "os";
|
|
13926
|
+
import { isAbsolute as isAbsolute2, join as join30 } from "path";
|
|
13853
13927
|
import { promisify } from "util";
|
|
13854
13928
|
|
|
13855
13929
|
// src/agents/registry.ts
|
|
13856
|
-
import { platform as platform6, homedir as
|
|
13857
|
-
import { isAbsolute, join as
|
|
13930
|
+
import { platform as platform6, homedir as homedir11 } from "os";
|
|
13931
|
+
import { isAbsolute, join as join29 } from "path";
|
|
13858
13932
|
import { existsSync as existsSync35 } from "fs";
|
|
13859
13933
|
|
|
13860
13934
|
// src/agents/registry-data.ts
|
|
@@ -14452,7 +14526,7 @@ function resolveToAbsolute(ps, scope) {
|
|
|
14452
14526
|
const resolved = resolvePlatformString(ps);
|
|
14453
14527
|
if (!resolved)
|
|
14454
14528
|
return;
|
|
14455
|
-
return scope === "global" ?
|
|
14529
|
+
return scope === "global" ? join29(homedir11(), resolved) : join29(process.cwd(), resolved);
|
|
14456
14530
|
}
|
|
14457
14531
|
function resolveAgentCliCommand(slug) {
|
|
14458
14532
|
const agent = getAgent(slug);
|
|
@@ -14465,7 +14539,7 @@ function resolveAgentCliCommand(slug) {
|
|
|
14465
14539
|
const resolved = resolvePlatformString(candidate);
|
|
14466
14540
|
if (!resolved)
|
|
14467
14541
|
continue;
|
|
14468
|
-
const absolute = isAbsolute(resolved) ? resolved :
|
|
14542
|
+
const absolute = isAbsolute(resolved) ? resolved : join29(homedir11(), resolved);
|
|
14469
14543
|
if (existsSync35(absolute))
|
|
14470
14544
|
return absolute;
|
|
14471
14545
|
}
|
|
@@ -14509,7 +14583,7 @@ function resolveDetectionPath(target) {
|
|
|
14509
14583
|
const resolved = resolvePlatformString(target);
|
|
14510
14584
|
if (!resolved)
|
|
14511
14585
|
return null;
|
|
14512
|
-
return isAbsolute2(resolved) ? resolved :
|
|
14586
|
+
return isAbsolute2(resolved) ? resolved : join30(homedir12(), resolved);
|
|
14513
14587
|
}
|
|
14514
14588
|
function checkPath(target) {
|
|
14515
14589
|
const absolute = resolveDetectionPath(target);
|
|
@@ -14595,7 +14669,7 @@ class CodexAdapter {
|
|
|
14595
14669
|
return true;
|
|
14596
14670
|
}
|
|
14597
14671
|
async writeMcpServers(servers, _scope) {
|
|
14598
|
-
const configPath =
|
|
14672
|
+
const configPath = join31(homedir13(), ".codex", "config.toml");
|
|
14599
14673
|
let parsed = {};
|
|
14600
14674
|
if (existsSync37(configPath)) {
|
|
14601
14675
|
parsed = parse(readFileSync27(configPath, "utf-8"));
|
|
@@ -14621,36 +14695,36 @@ class CodexAdapter {
|
|
|
14621
14695
|
}
|
|
14622
14696
|
mcpServers[safeName] = entry;
|
|
14623
14697
|
}
|
|
14624
|
-
mkdirSync21(
|
|
14698
|
+
mkdirSync21(join31(configPath, ".."), { recursive: true });
|
|
14625
14699
|
writeFileSync21(configPath, stringify(parsed));
|
|
14626
14700
|
}
|
|
14627
14701
|
async writeSkills(skills, scope) {
|
|
14628
|
-
const root = scope === "project" ? process.cwd() :
|
|
14629
|
-
const baseDir =
|
|
14630
|
-
const legacyBaseDir =
|
|
14702
|
+
const root = scope === "project" ? process.cwd() : homedir13();
|
|
14703
|
+
const baseDir = join31(root, ".agents", "skills");
|
|
14704
|
+
const legacyBaseDir = join31(root, ".codex", "skills");
|
|
14631
14705
|
for (const skill of skills) {
|
|
14632
14706
|
if (skill.name !== skill.filename) {
|
|
14633
|
-
for (const dir of [
|
|
14707
|
+
for (const dir of [join31(baseDir, skill.name), join31(legacyBaseDir, skill.name)]) {
|
|
14634
14708
|
moveToTrash(dir, `skill renamed to ${skill.filename}`);
|
|
14635
14709
|
}
|
|
14636
14710
|
}
|
|
14637
|
-
moveToTrash(
|
|
14638
|
-
const skillDir =
|
|
14711
|
+
moveToTrash(join31(legacyBaseDir, skill.filename), "duplicate skill root consolidated");
|
|
14712
|
+
const skillDir = join31(baseDir, skill.filename);
|
|
14639
14713
|
mkdirSync21(skillDir, { recursive: true });
|
|
14640
|
-
writeFileSync21(
|
|
14714
|
+
writeFileSync21(join31(skillDir, "SKILL.md"), buildSkillMd2(skill));
|
|
14641
14715
|
}
|
|
14642
14716
|
return skills.length;
|
|
14643
14717
|
}
|
|
14644
14718
|
async writeInstructionHint(hint, scope) {
|
|
14645
|
-
const filePath = scope === "project" ?
|
|
14719
|
+
const filePath = scope === "project" ? join31(process.cwd(), "AGENTS.md") : join31(homedir13(), ".codex", "AGENTS.md");
|
|
14646
14720
|
writeHintToFile(filePath, hint);
|
|
14647
14721
|
}
|
|
14648
14722
|
async writeTeamInstructions(instructions, scope) {
|
|
14649
|
-
const filePath = scope === "project" ?
|
|
14723
|
+
const filePath = scope === "project" ? join31(process.cwd(), "AGENTS.md") : join31(homedir13(), ".codex", "AGENTS.md");
|
|
14650
14724
|
writeTeamInstructionsToFile(filePath, instructions);
|
|
14651
14725
|
}
|
|
14652
14726
|
async writeAgentConfig(config, scope) {
|
|
14653
|
-
const configPath = scope === "project" ?
|
|
14727
|
+
const configPath = scope === "project" ? join31(process.cwd(), ".codex", "config.toml") : join31(homedir13(), ".codex", "config.toml");
|
|
14654
14728
|
let parsed = {};
|
|
14655
14729
|
if (existsSync37(configPath)) {
|
|
14656
14730
|
parsed = parse(readFileSync27(configPath, "utf-8"));
|
|
@@ -14696,20 +14770,20 @@ class CodexAdapter {
|
|
|
14696
14770
|
sww.network_access = true;
|
|
14697
14771
|
}
|
|
14698
14772
|
}
|
|
14699
|
-
mkdirSync21(
|
|
14773
|
+
mkdirSync21(join31(configPath, ".."), { recursive: true });
|
|
14700
14774
|
writeFileSync21(configPath, stringify(parsed));
|
|
14701
14775
|
}
|
|
14702
14776
|
async removeSkills(skillFilenames, scope) {
|
|
14703
14777
|
if (!skillFilenames.length)
|
|
14704
14778
|
return;
|
|
14705
14779
|
const allowed = new Set(skillFilenames);
|
|
14706
|
-
const root = scope === "project" ? process.cwd() :
|
|
14707
|
-
for (const skillsDir of [
|
|
14780
|
+
const root = scope === "project" ? process.cwd() : homedir13();
|
|
14781
|
+
for (const skillsDir of [join31(root, ".agents", "skills"), join31(root, ".codex", "skills")]) {
|
|
14708
14782
|
removeMatchingSkillDirs(skillsDir, allowed);
|
|
14709
14783
|
}
|
|
14710
14784
|
}
|
|
14711
14785
|
async cleanup(scope, manifest) {
|
|
14712
|
-
const configPath =
|
|
14786
|
+
const configPath = join31(homedir13(), ".codex", "config.toml");
|
|
14713
14787
|
if (existsSync37(configPath)) {
|
|
14714
14788
|
try {
|
|
14715
14789
|
const parsed = parse(readFileSync27(configPath, "utf-8"));
|
|
@@ -14725,13 +14799,13 @@ class CodexAdapter {
|
|
|
14725
14799
|
} catch {}
|
|
14726
14800
|
}
|
|
14727
14801
|
await this.removeSkills(manifest?.skillFilenames ?? [], scope);
|
|
14728
|
-
const instructionFile = scope === "project" ?
|
|
14802
|
+
const instructionFile = scope === "project" ? join31(process.cwd(), "AGENTS.md") : join31(homedir13(), ".codex", "AGENTS.md");
|
|
14729
14803
|
removeHintFromFile(instructionFile);
|
|
14730
14804
|
removeTeamInstructionsFromFile(instructionFile);
|
|
14731
14805
|
}
|
|
14732
14806
|
async readUsageStats(lastSyncAt) {
|
|
14733
14807
|
try {
|
|
14734
|
-
const codexDir =
|
|
14808
|
+
const codexDir = join31(homedir13(), ".codex");
|
|
14735
14809
|
if (!existsSync37(codexDir))
|
|
14736
14810
|
return null;
|
|
14737
14811
|
const sinceMs = lastSyncAt ? new Date(lastSyncAt).getTime() : 0;
|
|
@@ -14741,7 +14815,7 @@ class CodexAdapter {
|
|
|
14741
14815
|
let tokensUsed = rollout.tokensUsed;
|
|
14742
14816
|
let latestMs = rollout.latestMs;
|
|
14743
14817
|
let versions = [];
|
|
14744
|
-
const dbPath =
|
|
14818
|
+
const dbPath = join31(codexDir, "state_5.sqlite");
|
|
14745
14819
|
if (existsSync37(dbPath)) {
|
|
14746
14820
|
const countResult = queryReadonlySqlite(dbPath, `SELECT count(*) FROM threads WHERE updated_at > ${sinceSec}`);
|
|
14747
14821
|
const dbSessionCount = parseInt(countResult) || 0;
|
|
@@ -14759,7 +14833,7 @@ class CodexAdapter {
|
|
|
14759
14833
|
}
|
|
14760
14834
|
let messageCount = rollout.messageCount;
|
|
14761
14835
|
if (messageCount === 0) {
|
|
14762
|
-
const historyPath =
|
|
14836
|
+
const historyPath = join31(codexDir, "history.jsonl");
|
|
14763
14837
|
if (existsSync37(historyPath)) {
|
|
14764
14838
|
const content = readFileSync27(historyPath, "utf-8").trim();
|
|
14765
14839
|
if (content) {
|
|
@@ -14804,7 +14878,7 @@ class CodexAdapter {
|
|
|
14804
14878
|
latestMs: 0,
|
|
14805
14879
|
activeDays: []
|
|
14806
14880
|
};
|
|
14807
|
-
const sessionsDir =
|
|
14881
|
+
const sessionsDir = join31(homedir13(), ".codex", "sessions");
|
|
14808
14882
|
if (!existsSync37(sessionsDir))
|
|
14809
14883
|
return result;
|
|
14810
14884
|
const files = [];
|
|
@@ -14816,7 +14890,7 @@ class CodexAdapter {
|
|
|
14816
14890
|
return;
|
|
14817
14891
|
}
|
|
14818
14892
|
for (const e of entries) {
|
|
14819
|
-
const full =
|
|
14893
|
+
const full = join31(dir, e.name);
|
|
14820
14894
|
if (e.isDirectory())
|
|
14821
14895
|
walk(full);
|
|
14822
14896
|
else if (e.name.startsWith("rollout-") && e.name.endsWith(".jsonl"))
|
|
@@ -14894,7 +14968,7 @@ class CodexAdapter {
|
|
|
14894
14968
|
}
|
|
14895
14969
|
async readSessionDigests(sinceISO) {
|
|
14896
14970
|
try {
|
|
14897
|
-
const sessionsDir =
|
|
14971
|
+
const sessionsDir = join31(homedir13(), ".codex", "sessions");
|
|
14898
14972
|
if (!existsSync37(sessionsDir))
|
|
14899
14973
|
return null;
|
|
14900
14974
|
const sinceMs = sinceISO ? new Date(sinceISO).getTime() : 0;
|
|
@@ -14907,7 +14981,7 @@ class CodexAdapter {
|
|
|
14907
14981
|
return;
|
|
14908
14982
|
}
|
|
14909
14983
|
for (const e of entries) {
|
|
14910
|
-
const full =
|
|
14984
|
+
const full = join31(dir, e.name);
|
|
14911
14985
|
if (e.isDirectory())
|
|
14912
14986
|
walk(full);
|
|
14913
14987
|
else if (e.name.startsWith("rollout-") && e.name.endsWith(".jsonl"))
|
|
@@ -14942,7 +15016,7 @@ class CodexAdapter {
|
|
|
14942
15016
|
}
|
|
14943
15017
|
async readVersion() {
|
|
14944
15018
|
try {
|
|
14945
|
-
const versionPath =
|
|
15019
|
+
const versionPath = join31(homedir13(), ".codex", "version.json");
|
|
14946
15020
|
if (existsSync37(versionPath)) {
|
|
14947
15021
|
const data = JSON.parse(readFileSync27(versionPath, "utf-8"));
|
|
14948
15022
|
return data.latest_version ?? null;
|
|
@@ -14952,7 +15026,7 @@ class CodexAdapter {
|
|
|
14952
15026
|
}
|
|
14953
15027
|
async readSkillUsage(lastSyncAt) {
|
|
14954
15028
|
try {
|
|
14955
|
-
const sessionsDir =
|
|
15029
|
+
const sessionsDir = join31(homedir13(), ".codex", "sessions");
|
|
14956
15030
|
if (!existsSync37(sessionsDir))
|
|
14957
15031
|
return null;
|
|
14958
15032
|
const sinceMs = lastSyncAt ? new Date(lastSyncAt).getTime() : 0;
|
|
@@ -14965,7 +15039,7 @@ class CodexAdapter {
|
|
|
14965
15039
|
return;
|
|
14966
15040
|
}
|
|
14967
15041
|
for (const entry of entries) {
|
|
14968
|
-
const fullPath =
|
|
15042
|
+
const fullPath = join31(dir, entry);
|
|
14969
15043
|
if (entry.endsWith(".jsonl")) {
|
|
14970
15044
|
let fileStat;
|
|
14971
15045
|
try {
|
|
@@ -15054,7 +15128,7 @@ class CodexAdapter {
|
|
|
15054
15128
|
}
|
|
15055
15129
|
}
|
|
15056
15130
|
registerDesktopWorkspace(workspacePath, label) {
|
|
15057
|
-
const statePath =
|
|
15131
|
+
const statePath = join31(homedir13(), ".codex", ".codex-global-state.json");
|
|
15058
15132
|
let state = {};
|
|
15059
15133
|
if (existsSync37(statePath)) {
|
|
15060
15134
|
try {
|
|
@@ -15085,7 +15159,7 @@ class CodexAdapter {
|
|
|
15085
15159
|
}
|
|
15086
15160
|
labels[workspacePath] = label;
|
|
15087
15161
|
state["electron-workspace-root-labels"] = labels;
|
|
15088
|
-
mkdirSync21(
|
|
15162
|
+
mkdirSync21(join31(statePath, ".."), { recursive: true });
|
|
15089
15163
|
writeFileSync21(statePath, JSON.stringify(state));
|
|
15090
15164
|
return "written";
|
|
15091
15165
|
}
|
|
@@ -15107,8 +15181,8 @@ class CodexDesktopAdapter extends CodexAdapter {
|
|
|
15107
15181
|
|
|
15108
15182
|
// src/agents/cline.ts
|
|
15109
15183
|
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
|
|
15111
|
-
import { homedir as
|
|
15184
|
+
import { join as join32 } from "path";
|
|
15185
|
+
import { homedir as homedir14 } from "os";
|
|
15112
15186
|
class ClineAdapter {
|
|
15113
15187
|
name = "Cline";
|
|
15114
15188
|
slug = "cline";
|
|
@@ -15123,7 +15197,7 @@ class ClineAdapter {
|
|
|
15123
15197
|
return true;
|
|
15124
15198
|
}
|
|
15125
15199
|
async writeMcpServers(servers, _scope) {
|
|
15126
|
-
const configPath =
|
|
15200
|
+
const configPath = join32(homedir14(), ".cline", "data", "settings", "cline_mcp_settings.json");
|
|
15127
15201
|
const entries = {};
|
|
15128
15202
|
for (const s of servers) {
|
|
15129
15203
|
entries[s.name] = {
|
|
@@ -15136,31 +15210,31 @@ class ClineAdapter {
|
|
|
15136
15210
|
async writeSkills(skills, scope) {
|
|
15137
15211
|
if (scope === "user")
|
|
15138
15212
|
return 0;
|
|
15139
|
-
const rulesDir =
|
|
15213
|
+
const rulesDir = join32(process.cwd(), ".clinerules");
|
|
15140
15214
|
mkdirSync22(rulesDir, { recursive: true });
|
|
15141
15215
|
for (const skill of skills) {
|
|
15142
|
-
writeFileSync22(
|
|
15216
|
+
writeFileSync22(join32(rulesDir, `${skill.filename}.md`), buildSkillMd2(skill));
|
|
15143
15217
|
}
|
|
15144
15218
|
return skills.length;
|
|
15145
15219
|
}
|
|
15146
15220
|
async writeInstructionHint(hint, scope) {
|
|
15147
15221
|
if (scope === "user")
|
|
15148
15222
|
return;
|
|
15149
|
-
const filePath =
|
|
15150
|
-
mkdirSync22(
|
|
15223
|
+
const filePath = join32(process.cwd(), ".clinerules", "runwork.md");
|
|
15224
|
+
mkdirSync22(join32(filePath, ".."), { recursive: true });
|
|
15151
15225
|
writeFileSync22(filePath, hint);
|
|
15152
15226
|
}
|
|
15153
15227
|
async writeTeamInstructions(instructions, scope) {
|
|
15154
15228
|
if (scope === "user")
|
|
15155
15229
|
return;
|
|
15156
|
-
const filePath =
|
|
15157
|
-
mkdirSync22(
|
|
15230
|
+
const filePath = join32(process.cwd(), ".clinerules", "runwork-team.md");
|
|
15231
|
+
mkdirSync22(join32(filePath, ".."), { recursive: true });
|
|
15158
15232
|
writeFileSync22(filePath, instructions);
|
|
15159
15233
|
}
|
|
15160
15234
|
async writeAgentConfig(config, scope) {
|
|
15161
15235
|
if (scope !== "user")
|
|
15162
15236
|
return;
|
|
15163
|
-
const globalStatePath =
|
|
15237
|
+
const globalStatePath = join32(homedir14(), ".cline", "data", "globalState.json");
|
|
15164
15238
|
let state = {};
|
|
15165
15239
|
if (existsSync38(globalStatePath)) {
|
|
15166
15240
|
try {
|
|
@@ -15185,24 +15259,24 @@ class ClineAdapter {
|
|
|
15185
15259
|
state.autoApprovalSettings.enabled = false;
|
|
15186
15260
|
}
|
|
15187
15261
|
}
|
|
15188
|
-
mkdirSync22(
|
|
15262
|
+
mkdirSync22(join32(globalStatePath, ".."), { recursive: true });
|
|
15189
15263
|
writeFileSync22(globalStatePath, JSON.stringify(state, null, 2));
|
|
15190
15264
|
}
|
|
15191
15265
|
async removeSkills(skillFilenames, scope) {
|
|
15192
15266
|
if (scope !== "project")
|
|
15193
15267
|
return;
|
|
15194
|
-
removeMatchingSkillFiles(
|
|
15268
|
+
removeMatchingSkillFiles(join32(process.cwd(), ".clinerules"), new Set(skillFilenames), ".md");
|
|
15195
15269
|
}
|
|
15196
15270
|
async cleanup(scope, manifest) {
|
|
15197
15271
|
if (scope === "user") {
|
|
15198
|
-
const configPath =
|
|
15272
|
+
const configPath = join32(homedir14(), ".cline", "data", "settings", "cline_mcp_settings.json");
|
|
15199
15273
|
removeRunworkMcpServers(configPath, "mcpServers");
|
|
15200
15274
|
}
|
|
15201
15275
|
if (scope === "project") {
|
|
15202
|
-
const rulesDir =
|
|
15276
|
+
const rulesDir = join32(process.cwd(), ".clinerules");
|
|
15203
15277
|
if (existsSync38(rulesDir)) {
|
|
15204
15278
|
for (const file of ["runwork.md", "runwork-team.md"]) {
|
|
15205
|
-
const filePath =
|
|
15279
|
+
const filePath = join32(rulesDir, file);
|
|
15206
15280
|
if (existsSync38(filePath)) {
|
|
15207
15281
|
try {
|
|
15208
15282
|
unlinkSync6(filePath);
|
|
@@ -15221,8 +15295,8 @@ class ClineAdapter {
|
|
|
15221
15295
|
|
|
15222
15296
|
// src/agents/gemini.ts
|
|
15223
15297
|
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
|
|
15225
|
-
import { homedir as
|
|
15298
|
+
import { join as join33 } from "path";
|
|
15299
|
+
import { homedir as homedir15 } from "os";
|
|
15226
15300
|
class GeminiAdapter {
|
|
15227
15301
|
name = "Gemini CLI";
|
|
15228
15302
|
slug = "gemini";
|
|
@@ -15237,7 +15311,7 @@ class GeminiAdapter {
|
|
|
15237
15311
|
return true;
|
|
15238
15312
|
}
|
|
15239
15313
|
async writeMcpServers(servers, _scope) {
|
|
15240
|
-
const configPath =
|
|
15314
|
+
const configPath = join33(homedir15(), ".gemini", "settings.json");
|
|
15241
15315
|
const entries = {};
|
|
15242
15316
|
for (const s of servers) {
|
|
15243
15317
|
entries[s.name] = {
|
|
@@ -15248,27 +15322,27 @@ class GeminiAdapter {
|
|
|
15248
15322
|
mergeJsonMcpServers(configPath, entries, "mcpServers");
|
|
15249
15323
|
}
|
|
15250
15324
|
async writeSkills(skills, scope) {
|
|
15251
|
-
const baseDir = scope === "project" ?
|
|
15325
|
+
const baseDir = scope === "project" ? join33(process.cwd(), ".gemini", "skills") : join33(homedir15(), ".gemini", "skills");
|
|
15252
15326
|
for (const skill of skills) {
|
|
15253
15327
|
if (skill.name !== skill.filename) {
|
|
15254
|
-
moveToTrash(
|
|
15328
|
+
moveToTrash(join33(baseDir, skill.name), `skill renamed to ${skill.filename}`);
|
|
15255
15329
|
}
|
|
15256
|
-
const skillDir =
|
|
15330
|
+
const skillDir = join33(baseDir, skill.filename);
|
|
15257
15331
|
mkdirSync23(skillDir, { recursive: true });
|
|
15258
|
-
writeFileSync23(
|
|
15332
|
+
writeFileSync23(join33(skillDir, "SKILL.md"), buildSkillMd2(skill));
|
|
15259
15333
|
}
|
|
15260
15334
|
return skills.length;
|
|
15261
15335
|
}
|
|
15262
15336
|
async writeInstructionHint(hint, scope) {
|
|
15263
|
-
const filePath = scope === "project" ?
|
|
15337
|
+
const filePath = scope === "project" ? join33(process.cwd(), "GEMINI.md") : join33(homedir15(), ".gemini", "GEMINI.md");
|
|
15264
15338
|
writeHintToFile(filePath, hint);
|
|
15265
15339
|
}
|
|
15266
15340
|
async writeTeamInstructions(instructions, scope) {
|
|
15267
|
-
const filePath = scope === "project" ?
|
|
15341
|
+
const filePath = scope === "project" ? join33(process.cwd(), "GEMINI.md") : join33(homedir15(), ".gemini", "GEMINI.md");
|
|
15268
15342
|
writeTeamInstructionsToFile(filePath, instructions);
|
|
15269
15343
|
}
|
|
15270
15344
|
async writeAgentConfig(config, scope) {
|
|
15271
|
-
const settingsPath = scope === "project" ?
|
|
15345
|
+
const settingsPath = scope === "project" ? join33(process.cwd(), ".gemini", "settings.json") : join33(homedir15(), ".gemini", "settings.json");
|
|
15272
15346
|
let settings = {};
|
|
15273
15347
|
if (existsSync39(settingsPath)) {
|
|
15274
15348
|
try {
|
|
@@ -15290,12 +15364,12 @@ class GeminiAdapter {
|
|
|
15290
15364
|
settings.tools = {};
|
|
15291
15365
|
settings.tools.exclude = config.permissionRules.deny;
|
|
15292
15366
|
}
|
|
15293
|
-
mkdirSync23(
|
|
15367
|
+
mkdirSync23(join33(settingsPath, ".."), { recursive: true });
|
|
15294
15368
|
writeFileSync23(settingsPath, JSON.stringify(settings, null, 2));
|
|
15295
15369
|
}
|
|
15296
15370
|
async readUsageStats(lastSyncAt) {
|
|
15297
15371
|
try {
|
|
15298
|
-
const tmpDir =
|
|
15372
|
+
const tmpDir = join33(homedir15(), ".gemini", "tmp");
|
|
15299
15373
|
if (!existsSync39(tmpDir))
|
|
15300
15374
|
return null;
|
|
15301
15375
|
const sinceMs = lastSyncAt ? new Date(lastSyncAt).getTime() : 0;
|
|
@@ -15312,7 +15386,7 @@ class GeminiAdapter {
|
|
|
15312
15386
|
for (const project of projects) {
|
|
15313
15387
|
if (!project.isDirectory())
|
|
15314
15388
|
continue;
|
|
15315
|
-
const chatsDir =
|
|
15389
|
+
const chatsDir = join33(tmpDir, project.name, "chats");
|
|
15316
15390
|
let files;
|
|
15317
15391
|
try {
|
|
15318
15392
|
files = readdirSync13(chatsDir, { withFileTypes: true });
|
|
@@ -15322,7 +15396,7 @@ class GeminiAdapter {
|
|
|
15322
15396
|
for (const file of files) {
|
|
15323
15397
|
if (!file.name.startsWith("session-") || !file.name.endsWith(".json"))
|
|
15324
15398
|
continue;
|
|
15325
|
-
const filePath =
|
|
15399
|
+
const filePath = join33(chatsDir, file.name);
|
|
15326
15400
|
let stat;
|
|
15327
15401
|
try {
|
|
15328
15402
|
stat = statSync7(filePath);
|
|
@@ -15374,15 +15448,15 @@ class GeminiAdapter {
|
|
|
15374
15448
|
}
|
|
15375
15449
|
}
|
|
15376
15450
|
async removeSkills(skillFilenames, scope) {
|
|
15377
|
-
const skillsDir = scope === "project" ?
|
|
15451
|
+
const skillsDir = scope === "project" ? join33(process.cwd(), ".gemini", "skills") : join33(homedir15(), ".gemini", "skills");
|
|
15378
15452
|
removeMatchingSkillDirs(skillsDir, new Set(skillFilenames));
|
|
15379
15453
|
}
|
|
15380
15454
|
async cleanup(scope, manifest) {
|
|
15381
15455
|
if (scope === "user") {
|
|
15382
|
-
removeRunworkMcpServers(
|
|
15456
|
+
removeRunworkMcpServers(join33(homedir15(), ".gemini", "settings.json"), "mcpServers");
|
|
15383
15457
|
}
|
|
15384
15458
|
await this.removeSkills(manifest?.skillFilenames ?? [], scope);
|
|
15385
|
-
const instructionFile = scope === "project" ?
|
|
15459
|
+
const instructionFile = scope === "project" ? join33(process.cwd(), "GEMINI.md") : join33(homedir15(), ".gemini", "GEMINI.md");
|
|
15386
15460
|
removeHintFromFile(instructionFile);
|
|
15387
15461
|
removeTeamInstructionsFromFile(instructionFile);
|
|
15388
15462
|
}
|
|
@@ -15390,8 +15464,8 @@ class GeminiAdapter {
|
|
|
15390
15464
|
|
|
15391
15465
|
// src/agents/generic-adapter.ts
|
|
15392
15466
|
import { existsSync as existsSync40, mkdirSync as mkdirSync24, rmSync as rmSync12, writeFileSync as writeFileSync24 } from "fs";
|
|
15393
|
-
import { join as
|
|
15394
|
-
import { homedir as
|
|
15467
|
+
import { join as join34 } from "path";
|
|
15468
|
+
import { homedir as homedir16 } from "os";
|
|
15395
15469
|
class GenericAgentAdapter {
|
|
15396
15470
|
name;
|
|
15397
15471
|
slug;
|
|
@@ -15416,7 +15490,7 @@ class GenericAgentAdapter {
|
|
|
15416
15490
|
async writeMcpServers(servers, _scope) {
|
|
15417
15491
|
if (!this.def.mcpConfigPath)
|
|
15418
15492
|
return;
|
|
15419
|
-
const filePath =
|
|
15493
|
+
const filePath = join34(homedir16(), resolvePlatformString(this.def.mcpConfigPath) || "");
|
|
15420
15494
|
if (!filePath)
|
|
15421
15495
|
return;
|
|
15422
15496
|
const entries = {};
|
|
@@ -15440,16 +15514,16 @@ class GenericAgentAdapter {
|
|
|
15440
15514
|
return 0;
|
|
15441
15515
|
for (const skill of skills) {
|
|
15442
15516
|
if (skill.name !== skill.filename) {
|
|
15443
|
-
const oldDir =
|
|
15517
|
+
const oldDir = join34(baseDir, skill.name);
|
|
15444
15518
|
if (existsSync40(oldDir)) {
|
|
15445
15519
|
try {
|
|
15446
15520
|
rmSync12(oldDir, { recursive: true, force: true });
|
|
15447
15521
|
} catch {}
|
|
15448
15522
|
}
|
|
15449
15523
|
}
|
|
15450
|
-
const skillDir =
|
|
15524
|
+
const skillDir = join34(baseDir, skill.filename);
|
|
15451
15525
|
mkdirSync24(skillDir, { recursive: true });
|
|
15452
|
-
writeFileSync24(
|
|
15526
|
+
writeFileSync24(join34(skillDir, "SKILL.md"), buildSkillMd2(skill));
|
|
15453
15527
|
}
|
|
15454
15528
|
return skills.length;
|
|
15455
15529
|
}
|
|
@@ -15489,7 +15563,7 @@ class GenericAgentAdapter {
|
|
|
15489
15563
|
if (this.def.mcpConfigPath && scope === "user") {
|
|
15490
15564
|
const resolved = resolvePlatformString(this.def.mcpConfigPath);
|
|
15491
15565
|
if (resolved) {
|
|
15492
|
-
const filePath =
|
|
15566
|
+
const filePath = join34(homedir16(), resolved);
|
|
15493
15567
|
removeRunworkMcpServers(filePath, this.def.mcpConfigKey || "mcpServers");
|
|
15494
15568
|
}
|
|
15495
15569
|
}
|
|
@@ -15571,13 +15645,13 @@ function insightLocalKey(teaches, slug) {
|
|
|
15571
15645
|
// src/reflect/insight-store.ts
|
|
15572
15646
|
init_atomic_json();
|
|
15573
15647
|
import { existsSync as existsSync41, readFileSync as readFileSync30 } from "fs";
|
|
15574
|
-
import { join as
|
|
15575
|
-
import { homedir as
|
|
15576
|
-
function
|
|
15577
|
-
return
|
|
15648
|
+
import { join as join35 } from "path";
|
|
15649
|
+
import { homedir as homedir17 } from "os";
|
|
15650
|
+
function storePath2() {
|
|
15651
|
+
return join35(homedir17(), ".runwork", "insights.json");
|
|
15578
15652
|
}
|
|
15579
15653
|
function readAll() {
|
|
15580
|
-
const path2 =
|
|
15654
|
+
const path2 = storePath2();
|
|
15581
15655
|
if (!existsSync41(path2))
|
|
15582
15656
|
return {};
|
|
15583
15657
|
try {
|
|
@@ -15588,7 +15662,7 @@ function readAll() {
|
|
|
15588
15662
|
}
|
|
15589
15663
|
}
|
|
15590
15664
|
function writeAll(store) {
|
|
15591
|
-
writeJsonAtomic(
|
|
15665
|
+
writeJsonAtomic(storePath2(), store);
|
|
15592
15666
|
}
|
|
15593
15667
|
function notExpired(i, now) {
|
|
15594
15668
|
const t = new Date(i.expiresAt).getTime();
|
|
@@ -15622,15 +15696,15 @@ function getInsight(id, opts = {}) {
|
|
|
15622
15696
|
// src/reflect/cadence.ts
|
|
15623
15697
|
init_atomic_json();
|
|
15624
15698
|
import { existsSync as existsSync42, readFileSync as readFileSync31 } from "fs";
|
|
15625
|
-
import { join as
|
|
15626
|
-
import { homedir as
|
|
15699
|
+
import { join as join36 } from "path";
|
|
15700
|
+
import { homedir as homedir18 } from "os";
|
|
15627
15701
|
var DEFAULT_STATE = { enabled: false, lastReflectedAt: null };
|
|
15628
15702
|
var COOLDOWN_HOURS = 20;
|
|
15629
15703
|
var ACTIVE_SESSION_THRESHOLD = 10;
|
|
15630
15704
|
var MAX_INTERVAL_HOURS = 7 * 24;
|
|
15631
15705
|
var MIN_SESSIONS_FIRST_RUN = 3;
|
|
15632
15706
|
function statePath() {
|
|
15633
|
-
return
|
|
15707
|
+
return join36(homedir18(), ".runwork", "reflect-state.json");
|
|
15634
15708
|
}
|
|
15635
15709
|
function loadCadenceState() {
|
|
15636
15710
|
try {
|
|
@@ -15666,39 +15740,8 @@ function isReflectionDue(state, newSessionCount, now = new Date) {
|
|
|
15666
15740
|
return hoursSince >= MAX_INTERVAL_HOURS;
|
|
15667
15741
|
}
|
|
15668
15742
|
|
|
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
|
-
|
|
15701
15743
|
// src/commands/reflect.ts
|
|
15744
|
+
init_workspace_state();
|
|
15702
15745
|
init_atomic_json();
|
|
15703
15746
|
init_colors();
|
|
15704
15747
|
var MAX_NAMES = 40;
|
|
@@ -18051,7 +18094,52 @@ function summarizeTelemetryForDryRun(result) {
|
|
|
18051
18094
|
return `Telemetry preview: ${plural(result.events.length, "event")} would be sent (${plural(activeCount, "adapter")} with activity). Use --verbose for details.`;
|
|
18052
18095
|
}
|
|
18053
18096
|
|
|
18054
|
-
//
|
|
18097
|
+
// ../../shared/agent-instructions/runwork-instructions.ts
|
|
18098
|
+
function formatList(items, max = 8) {
|
|
18099
|
+
if (items.length <= max)
|
|
18100
|
+
return items.join(", ");
|
|
18101
|
+
return `${items.slice(0, max).join(", ")} + ${items.length - max} more`;
|
|
18102
|
+
}
|
|
18103
|
+
function plural(n, one, many) {
|
|
18104
|
+
return n > 1 ? many : one;
|
|
18105
|
+
}
|
|
18106
|
+
function pushGroup(parts, group, one, many) {
|
|
18107
|
+
if (group.count <= 0)
|
|
18108
|
+
return;
|
|
18109
|
+
const label = `${group.count} ${plural(group.count, one, many)}`;
|
|
18110
|
+
parts.push(group.names.length > 0 ? `${label} (${formatList(group.names, 3)})` : label);
|
|
18111
|
+
}
|
|
18112
|
+
function buildInventoryLine(inv) {
|
|
18113
|
+
const parts = [];
|
|
18114
|
+
if (inv.appCount > 0)
|
|
18115
|
+
parts.push(`${inv.appCount} ${plural(inv.appCount, "app", "apps")}`);
|
|
18116
|
+
if (inv.entityNames.length > 0) {
|
|
18117
|
+
const n = inv.entityNames.length;
|
|
18118
|
+
parts.push(`${n} ${plural(n, "entity", "entities")} (${formatList(inv.entityNames, 5)})`);
|
|
18119
|
+
}
|
|
18120
|
+
if (inv.fileStorageCount > 0) {
|
|
18121
|
+
parts.push(`${inv.fileStorageCount} ${plural(inv.fileStorageCount, "file storage", "file storages")}`);
|
|
18122
|
+
}
|
|
18123
|
+
pushGroup(parts, inv.schedules, "schedule", "schedules");
|
|
18124
|
+
pushGroup(parts, inv.workflows, "workflow", "workflows");
|
|
18125
|
+
pushGroup(parts, inv.agents, "agent", "agents");
|
|
18126
|
+
if (inv.endpointCount > 0) {
|
|
18127
|
+
parts.push(`${inv.endpointCount} ${plural(inv.endpointCount, "endpoint", "endpoints")}`);
|
|
18128
|
+
}
|
|
18129
|
+
if (inv.componentCount > 0) {
|
|
18130
|
+
parts.push(`${inv.componentCount} ${plural(inv.componentCount, "component", "components")}`);
|
|
18131
|
+
}
|
|
18132
|
+
if (inv.integrationIds.length > 0) {
|
|
18133
|
+
const n = inv.integrationIds.length;
|
|
18134
|
+
parts.push(`${n} ${plural(n, "integration", "integrations")} (${formatList(inv.integrationIds, 5)})`);
|
|
18135
|
+
}
|
|
18136
|
+
if (inv.skillCount > 0)
|
|
18137
|
+
parts.push(`${inv.skillCount} ${plural(inv.skillCount, "skill", "skills")}`);
|
|
18138
|
+
if (inv.mcpServerCount > 0) {
|
|
18139
|
+
parts.push(`${inv.mcpServerCount} MCP ${plural(inv.mcpServerCount, "server", "servers")}`);
|
|
18140
|
+
}
|
|
18141
|
+
return parts.length > 0 ? parts.join(", ") : "no resources deployed yet";
|
|
18142
|
+
}
|
|
18055
18143
|
function buildPersonaBlock(persona) {
|
|
18056
18144
|
if (!persona || persona.level === 3)
|
|
18057
18145
|
return [];
|
|
@@ -18080,57 +18168,65 @@ function buildPersonaBlock(persona) {
|
|
|
18080
18168
|
"- Keep explanations concise and outcome-focused."
|
|
18081
18169
|
];
|
|
18082
18170
|
}
|
|
18083
|
-
|
|
18084
|
-
|
|
18085
|
-
|
|
18086
|
-
|
|
18171
|
+
var ROUTING_TABLE = [
|
|
18172
|
+
"| User asks for | Use Runwork | Don't |",
|
|
18173
|
+
"|---|---|---|",
|
|
18174
|
+
"| save / create / share / update a skill | `save_skill` MCP tool | local SKILL.md files, prompt snippets |",
|
|
18175
|
+
"| store, persist, or remember structured data | `entity_{Name}` MCP tools | local SQLite, JSON files, in-memory |",
|
|
18176
|
+
"| schedule a recurring or cron job | Runwork schedules (`schedule_{name}`, `runwork schedules`) | OS cron, `setInterval`, Celery |",
|
|
18177
|
+
"| multi-step process, pipeline, or durable workflow | Runwork workflows (`workflow_{name}`) | ad-hoc scripts, shell pipelines |",
|
|
18178
|
+
"| call Slack / HubSpot / Gmail / Google / any third-party API | `{id}_api` MCP tools (e.g. `hubspot_api`) | raw `fetch`, hand-built auth, SDK installs |",
|
|
18179
|
+
"| upload, save, or serve a file | Runwork file storage (`list_bucket_files`, `generate_user_presigned_url`) | local filesystem, `fs.writeFile` |",
|
|
18180
|
+
"| expose a public API / webhook endpoint | Runwork endpoints (`endpoint_*`) | standalone Express/Flask servers |",
|
|
18181
|
+
"| share AI prompts, instructions, or expertise with the team | `save_skill` MCP tool | local prompt files, copy-paste |",
|
|
18182
|
+
"| connect an AI tool or MCP server | Runwork MCP servers | manual `settings.json` edits per machine |",
|
|
18183
|
+
"| build a new internal tool, team app, or automation | `runwork init` -> `runwork dev` -> `runwork deploy` | one-off scripts, notebooks |"
|
|
18184
|
+
];
|
|
18185
|
+
var PRODUCT_DEV_EXCEPTION = "**Exception -- product development:** Editing your current codebase, running tests, git operations, and building features in the product you're working on are NOT Runwork territory. Runwork is for infrastructure and team tooling, not the product itself.";
|
|
18186
|
+
function buildRunworkInstructions(ctx) {
|
|
18187
|
+
const { workspaceName, workspaceId, workspaceSlug, inventory, surface = "machine" } = ctx;
|
|
18188
|
+
const dashboardUrl = workspaceSlug ? `https://${workspaceSlug}.runwork.ai` : "https://runwork.ai";
|
|
18189
|
+
const workspaceIdLine = `Workspace ID: \`${workspaceId}\`${workspaceSlug ? ` | Dashboard: ${dashboardUrl}` : ""}`;
|
|
18190
|
+
const connection = surface === "mcp" ? `You are connected to the **${workspaceName}** Runwork workspace` : `This machine is connected to the **${workspaceName}** Runwork workspace`;
|
|
18191
|
+
const integrationToolHint = inventory.integrationIds.length > 0 ? ` Connected integrations are callable via MCP tools (${inventory.integrationIds.map((id) => `\`${id}_api\``).join(", ")}) or CLI (\`runwork integrations call <id> <method> <path>\`).` : "";
|
|
18192
|
+
const lines = [
|
|
18193
|
+
"## Runwork",
|
|
18194
|
+
"",
|
|
18195
|
+
`${connection} -- your team's shared platform for building and running infrastructure: apps, data & file storage, scheduled jobs, workflows, integrations, AI agents, skills, MCP servers, public APIs, and reusable components.`,
|
|
18196
|
+
"",
|
|
18197
|
+
workspaceIdLine,
|
|
18198
|
+
""
|
|
18199
|
+
];
|
|
18200
|
+
lines.push("**Default to Runwork -- team sync beats local files.** When the user asks for any of the following, use the Runwork tool shown. These operations sync across every teammate's AI agents; local alternatives only work for the current user.", "", ...ROUTING_TABLE, "", PRODUCT_DEV_EXCEPTION, "", `**Available now:** ${buildInventoryLine(inventory)}`, "", `Runwork MCP tools are always connected -- use them for data access, integration API calls, skill management, and resource discovery.${integrationToolHint} Use \`runwork\` CLI to create (\`runwork init\`), develop (\`runwork dev\`), and deploy (\`runwork deploy\`) apps. Install CLI: \`curl -fsSL https://runwork.ai/install.sh | sh\`. Web dashboard: ${dashboardUrl}. Invoke the \`runwork\` skill for full capability reference.`, ...buildPersonaBlock(ctx.persona));
|
|
18201
|
+
return lines.join(`
|
|
18202
|
+
`);
|
|
18087
18203
|
}
|
|
18088
|
-
|
|
18089
|
-
|
|
18090
|
-
|
|
18091
|
-
|
|
18092
|
-
|
|
18093
|
-
|
|
18094
|
-
|
|
18095
|
-
|
|
18096
|
-
|
|
18097
|
-
|
|
18098
|
-
|
|
18099
|
-
|
|
18100
|
-
|
|
18101
|
-
|
|
18102
|
-
|
|
18103
|
-
|
|
18104
|
-
|
|
18105
|
-
|
|
18106
|
-
|
|
18107
|
-
|
|
18108
|
-
|
|
18109
|
-
|
|
18110
|
-
|
|
18111
|
-
|
|
18112
|
-
|
|
18113
|
-
|
|
18114
|
-
if (reg.agents.length > 0) {
|
|
18115
|
-
const names = reg.agents.map((a) => a.name).filter(Boolean);
|
|
18116
|
-
if (names.length > 0)
|
|
18117
|
-
parts.push(`${reg.agents.length} agent${reg.agents.length > 1 ? "s" : ""} (${formatList(names, 3)})`);
|
|
18118
|
-
else
|
|
18119
|
-
parts.push(`${reg.agents.length} agent${reg.agents.length > 1 ? "s" : ""}`);
|
|
18120
|
-
}
|
|
18121
|
-
if (reg.endpoints.length > 0)
|
|
18122
|
-
parts.push(`${reg.endpoints.length} endpoint${reg.endpoints.length > 1 ? "s" : ""}`);
|
|
18123
|
-
if (reg.components.length > 0)
|
|
18124
|
-
parts.push(`${reg.components.length} component${reg.components.length > 1 ? "s" : ""}`);
|
|
18125
|
-
}
|
|
18126
|
-
if (ctx.connectedIntegrations.length > 0) {
|
|
18127
|
-
parts.push(`${ctx.connectedIntegrations.length} integration${ctx.connectedIntegrations.length > 1 ? "s" : ""} (${formatList(ctx.connectedIntegrations, 5)})`);
|
|
18128
|
-
}
|
|
18129
|
-
if (ctx.skillCount > 0)
|
|
18130
|
-
parts.push(`${ctx.skillCount} skill${ctx.skillCount > 1 ? "s" : ""}`);
|
|
18131
|
-
if (ctx.mcpServerCount > 0)
|
|
18132
|
-
parts.push(`${ctx.mcpServerCount} MCP server${ctx.mcpServerCount > 1 ? "s" : ""}`);
|
|
18133
|
-
return parts.length > 0 ? parts.join(", ") : "no resources deployed yet";
|
|
18204
|
+
|
|
18205
|
+
// src/agents/intro-skill.ts
|
|
18206
|
+
function toRunworkInventory(ctx) {
|
|
18207
|
+
const reg = ctx.registries;
|
|
18208
|
+
return {
|
|
18209
|
+
appCount: ctx.appCount,
|
|
18210
|
+
entityNames: reg ? [...new Set(reg.entities.map((e) => e.entityName))] : [],
|
|
18211
|
+
fileStorageCount: reg?.fileStorages.length ?? 0,
|
|
18212
|
+
schedules: {
|
|
18213
|
+
count: reg?.schedules.length ?? 0,
|
|
18214
|
+
names: reg?.schedules.map((s) => s.name).filter(Boolean) ?? []
|
|
18215
|
+
},
|
|
18216
|
+
workflows: {
|
|
18217
|
+
count: reg?.workflows.length ?? 0,
|
|
18218
|
+
names: reg?.workflows.map((w) => w.name).filter(Boolean) ?? []
|
|
18219
|
+
},
|
|
18220
|
+
agents: {
|
|
18221
|
+
count: reg?.agents.length ?? 0,
|
|
18222
|
+
names: reg?.agents.map((a) => a.name).filter(Boolean) ?? []
|
|
18223
|
+
},
|
|
18224
|
+
endpointCount: reg?.endpoints.length ?? 0,
|
|
18225
|
+
componentCount: reg?.components.length ?? 0,
|
|
18226
|
+
integrationIds: ctx.connectedIntegrations,
|
|
18227
|
+
skillCount: ctx.skillCount,
|
|
18228
|
+
mcpServerCount: ctx.mcpServerCount
|
|
18229
|
+
};
|
|
18134
18230
|
}
|
|
18135
18231
|
function buildAppSkillDescription(appName, registries) {
|
|
18136
18232
|
if (!registries)
|
|
@@ -18423,39 +18519,17 @@ function generateIntroSkill(ctx) {
|
|
|
18423
18519
|
};
|
|
18424
18520
|
}
|
|
18425
18521
|
function generateInstructionHint(ctx) {
|
|
18426
|
-
const
|
|
18427
|
-
|
|
18428
|
-
|
|
18429
|
-
|
|
18522
|
+
const body = buildRunworkInstructions({
|
|
18523
|
+
workspaceName: ctx.workspaceName,
|
|
18524
|
+
workspaceId: ctx.workspaceId,
|
|
18525
|
+
workspaceSlug: ctx.workspaceSlug,
|
|
18526
|
+
inventory: toRunworkInventory(ctx),
|
|
18527
|
+
persona: ctx.persona,
|
|
18528
|
+
surface: "machine"
|
|
18529
|
+
});
|
|
18430
18530
|
return [
|
|
18431
18531
|
"<!-- runwork:start -->",
|
|
18432
|
-
|
|
18433
|
-
"",
|
|
18434
|
-
`This machine is connected to the **${ctx.workspaceName}** Runwork workspace -- your team's shared platform for building and running infrastructure: apps, data & file storage, scheduled jobs, workflows, integrations, AI agents, skills, MCP servers, public APIs, and reusable components.`,
|
|
18435
|
-
"",
|
|
18436
|
-
workspaceIdLine,
|
|
18437
|
-
"",
|
|
18438
|
-
"**Default to Runwork -- team sync beats local files.** When the user asks for any of the following, use the Runwork tool shown. These operations sync across every teammate's AI agents; local alternatives only work for the current user.",
|
|
18439
|
-
"",
|
|
18440
|
-
"| User asks for | Use Runwork | Don't |",
|
|
18441
|
-
"|---|---|---|",
|
|
18442
|
-
"| save / create / share / update a skill | `save_skill` MCP tool | local SKILL.md files, prompt snippets |",
|
|
18443
|
-
"| store, persist, or remember structured data | `entity_{Name}` MCP tools | local SQLite, JSON files, in-memory |",
|
|
18444
|
-
"| schedule a recurring or cron job | Runwork schedules (`schedule_{name}`, `runwork schedules`) | OS cron, `setInterval`, Celery |",
|
|
18445
|
-
"| multi-step process, pipeline, or durable workflow | Runwork workflows (`workflow_{name}`) | ad-hoc scripts, shell pipelines |",
|
|
18446
|
-
"| call Slack / HubSpot / Gmail / Google / any third-party API | `{id}_api` MCP tools (e.g. `hubspot_api`) | raw `fetch`, hand-built auth, SDK installs |",
|
|
18447
|
-
"| upload, save, or serve a file | Runwork file storage (`list_bucket_files`, `generate_user_presigned_url`) | local filesystem, `fs.writeFile` |",
|
|
18448
|
-
"| expose a public API / webhook endpoint | Runwork endpoints (`endpoint_*`) | standalone Express/Flask servers |",
|
|
18449
|
-
"| share AI prompts, instructions, or expertise with the team | `save_skill` MCP tool | local prompt files, copy-paste |",
|
|
18450
|
-
"| connect an AI tool or MCP server | Runwork MCP servers | manual `settings.json` edits per machine |",
|
|
18451
|
-
"| build a new internal tool, team app, or automation | `runwork init` -> `runwork dev` -> `runwork deploy` | one-off scripts, notebooks |",
|
|
18452
|
-
"",
|
|
18453
|
-
"**Exception -- product development:** Editing your current codebase, running tests, git operations, and building features in the product you're working on are NOT Runwork territory. Runwork is for infrastructure and team tooling, not the product itself.",
|
|
18454
|
-
"",
|
|
18455
|
-
`**Available now:** ${inventory}`,
|
|
18456
|
-
"",
|
|
18457
|
-
`Runwork MCP tools are always connected -- use them for data access, integration API calls, skill management, and resource discovery.${integrationToolHint} Use \`runwork\` CLI to create (\`runwork init\`), develop (\`runwork dev\`), and deploy (\`runwork deploy\`) apps. Install CLI: \`curl -fsSL https://runwork.ai/install.sh | sh\`. Web dashboard: ${dashboardUrl}. Invoke the \`runwork\` skill for full capability reference.`,
|
|
18458
|
-
...buildPersonaBlock(ctx.persona),
|
|
18532
|
+
body,
|
|
18459
18533
|
"<!-- runwork:end -->"
|
|
18460
18534
|
].join(`
|
|
18461
18535
|
`);
|
|
@@ -18801,6 +18875,7 @@ function computeSyncPlan(input) {
|
|
|
18801
18875
|
|
|
18802
18876
|
// src/commands/sync.ts
|
|
18803
18877
|
init_atomic_json();
|
|
18878
|
+
init_workspace_state();
|
|
18804
18879
|
|
|
18805
18880
|
// src/sync/conflict-ui.ts
|
|
18806
18881
|
init_prompt();
|
|
@@ -19160,9 +19235,31 @@ async function refreshConfiguredAgents(state) {
|
|
|
19160
19235
|
state.lastDetectedAt = new Date().toISOString();
|
|
19161
19236
|
return added;
|
|
19162
19237
|
}
|
|
19238
|
+
function isNotAMemberError(err) {
|
|
19239
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
19240
|
+
return message.includes("403") && message.includes("not a member of this workspace");
|
|
19241
|
+
}
|
|
19242
|
+
function ensureWorkspacePointer(state, statePath2, credentials) {
|
|
19243
|
+
if (state.workspaceId)
|
|
19244
|
+
return true;
|
|
19245
|
+
if (!credentials.defaultWorkspaceId) {
|
|
19246
|
+
console.error("No workspace selected for this account.");
|
|
19247
|
+
console.error("Run `runwork setup` (or `runwork info`) to choose one.");
|
|
19248
|
+
return false;
|
|
19249
|
+
}
|
|
19250
|
+
state.workspaceId = credentials.defaultWorkspaceId;
|
|
19251
|
+
state.workspaceName = credentials.defaultWorkspaceName || credentials.defaultWorkspaceId;
|
|
19252
|
+
try {
|
|
19253
|
+
writeJsonAtomic(statePath2, state);
|
|
19254
|
+
} catch {}
|
|
19255
|
+
return true;
|
|
19256
|
+
}
|
|
19163
19257
|
async function syncFromState(state, statePath2, credentials, opts) {
|
|
19164
19258
|
const client = new ApiClient(credentials);
|
|
19165
19259
|
setVerbose(!!opts.verbose);
|
|
19260
|
+
if (!ensureWorkspacePointer(state, statePath2, credentials)) {
|
|
19261
|
+
process.exit(1);
|
|
19262
|
+
}
|
|
19166
19263
|
if (!state.workspaceName || state.workspaceName === state.workspaceId || !state.workspaceSlug) {
|
|
19167
19264
|
try {
|
|
19168
19265
|
const workspaces = await client.listWorkspaces();
|
|
@@ -19183,14 +19280,28 @@ async function syncFromState(state, statePath2, credentials, opts) {
|
|
|
19183
19280
|
}
|
|
19184
19281
|
}
|
|
19185
19282
|
console.log(" Fetching workspace data...");
|
|
19186
|
-
|
|
19187
|
-
|
|
19188
|
-
|
|
19189
|
-
|
|
19190
|
-
|
|
19191
|
-
|
|
19192
|
-
|
|
19193
|
-
|
|
19283
|
+
let fetched;
|
|
19284
|
+
try {
|
|
19285
|
+
fetched = await Promise.all([
|
|
19286
|
+
client.listWorkspaceSkills(state.workspaceId, true),
|
|
19287
|
+
client.listMcpServers(state.workspaceId),
|
|
19288
|
+
client.listExternalSkills(state.workspaceId),
|
|
19289
|
+
client.getWorkspaceAll(state.workspaceId).catch(() => null),
|
|
19290
|
+
client.listConnectedIntegrations(state.workspaceId).then((list) => list.map((i) => i.canonicalId ?? i.integrationId)).catch(() => []),
|
|
19291
|
+
client.resolveMcpCredentials(state.workspaceId).catch(() => ({}))
|
|
19292
|
+
]);
|
|
19293
|
+
} catch (err) {
|
|
19294
|
+
if (isNotAMemberError(err)) {
|
|
19295
|
+
forgetWorkspaceSelection();
|
|
19296
|
+
console.error(`
|
|
19297
|
+
This account is not a member of "${state.workspaceName || state.workspaceId}".`);
|
|
19298
|
+
console.error("That workspace belonged to a previous sign in, so it has been forgotten.");
|
|
19299
|
+
console.error("Run `runwork setup` (or `runwork info`) to choose a workspace for this account.");
|
|
19300
|
+
process.exit(1);
|
|
19301
|
+
}
|
|
19302
|
+
throw err;
|
|
19303
|
+
}
|
|
19304
|
+
const [allSkills, mcpServers, externalSkills, registries, connectedIntegrations, resolvedMcpCredentials] = fetched;
|
|
19194
19305
|
const appCount = allSkills.filter((s) => s.type === "app").length;
|
|
19195
19306
|
const parts = [];
|
|
19196
19307
|
if (appCount > 0)
|
|
@@ -19730,6 +19841,7 @@ Sync complete.`);
|
|
|
19730
19841
|
});
|
|
19731
19842
|
|
|
19732
19843
|
// src/commands/setup.ts
|
|
19844
|
+
init_workspace_state();
|
|
19733
19845
|
var PERSONA_LABELS = {
|
|
19734
19846
|
1: "everyday",
|
|
19735
19847
|
2: "curious",
|