runwork 0.16.0 → 0.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +423 -108
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -7381,7 +7381,7 @@ function createKeyboardListener() {
|
|
|
7381
7381
|
}
|
|
7382
7382
|
|
|
7383
7383
|
// src/generated/version.ts
|
|
7384
|
-
var VERSION = "0.
|
|
7384
|
+
var VERSION = "0.17.0";
|
|
7385
7385
|
|
|
7386
7386
|
// src/commands/dev.ts
|
|
7387
7387
|
var exports_dev = {};
|
|
@@ -10989,6 +10989,92 @@ function extractCodexRolloutSession(raw, agentSlug) {
|
|
|
10989
10989
|
digest.project = "codex";
|
|
10990
10990
|
return digest;
|
|
10991
10991
|
}
|
|
10992
|
+
function extractCursorSessions(composerRows, bubbleRows, sinceISO) {
|
|
10993
|
+
const composerCreatedAt = new Map;
|
|
10994
|
+
for (const line of composerRows.split(`
|
|
10995
|
+
`)) {
|
|
10996
|
+
if (!line.trim())
|
|
10997
|
+
continue;
|
|
10998
|
+
try {
|
|
10999
|
+
const o = JSON.parse(line);
|
|
11000
|
+
if (o.id)
|
|
11001
|
+
composerCreatedAt.set(o.id, typeof o.createdAt === "number" ? o.createdAt : 0);
|
|
11002
|
+
} catch {}
|
|
11003
|
+
}
|
|
11004
|
+
const byComposer = new Map;
|
|
11005
|
+
for (const line of bubbleRows.split(`
|
|
11006
|
+
`)) {
|
|
11007
|
+
if (!line.trim())
|
|
11008
|
+
continue;
|
|
11009
|
+
let o;
|
|
11010
|
+
try {
|
|
11011
|
+
o = JSON.parse(line);
|
|
11012
|
+
} catch {
|
|
11013
|
+
continue;
|
|
11014
|
+
}
|
|
11015
|
+
const parts = (o.k ?? "").split(":");
|
|
11016
|
+
if (parts.length < 3)
|
|
11017
|
+
continue;
|
|
11018
|
+
const cid = parts[1];
|
|
11019
|
+
const list = byComposer.get(cid) ?? [];
|
|
11020
|
+
list.push({ type: o.type ?? null, text: o.text ?? null, ts: o.ts ?? null, tool: o.tool ?? null, status: o.status ?? null });
|
|
11021
|
+
byComposer.set(cid, list);
|
|
11022
|
+
}
|
|
11023
|
+
const digests = [];
|
|
11024
|
+
for (const [cid, bubbles] of byComposer) {
|
|
11025
|
+
bubbles.sort((a, b) => (a.ts ?? "").localeCompare(b.ts ?? ""));
|
|
11026
|
+
const digest = {
|
|
11027
|
+
agentSlug: "cursor",
|
|
11028
|
+
project: "cursor",
|
|
11029
|
+
start: null,
|
|
11030
|
+
end: null,
|
|
11031
|
+
userMessages: [],
|
|
11032
|
+
droppedUserMessages: 0,
|
|
11033
|
+
toolCounts: {},
|
|
11034
|
+
skillInvocations: [],
|
|
11035
|
+
errorCount: 0,
|
|
11036
|
+
assistantTurns: 0
|
|
11037
|
+
};
|
|
11038
|
+
for (const b of bubbles) {
|
|
11039
|
+
if (b.ts) {
|
|
11040
|
+
if (!digest.start)
|
|
11041
|
+
digest.start = b.ts;
|
|
11042
|
+
digest.end = b.ts;
|
|
11043
|
+
}
|
|
11044
|
+
if (b.tool) {
|
|
11045
|
+
digest.toolCounts[b.tool] = (digest.toolCounts[b.tool] ?? 0) + 1;
|
|
11046
|
+
if (b.status === "error")
|
|
11047
|
+
digest.errorCount++;
|
|
11048
|
+
} else if (b.type === 2) {
|
|
11049
|
+
digest.assistantTurns++;
|
|
11050
|
+
}
|
|
11051
|
+
if (b.type === 1 && typeof b.text === "string") {
|
|
11052
|
+
const trimmed = b.text.trim();
|
|
11053
|
+
if (!trimmed || SKIP_PREFIXES.some((p) => trimmed.startsWith(p)))
|
|
11054
|
+
continue;
|
|
11055
|
+
if (digest.userMessages.length >= MAX_MSGS_PER_SESSION) {
|
|
11056
|
+
digest.droppedUserMessages++;
|
|
11057
|
+
continue;
|
|
11058
|
+
}
|
|
11059
|
+
digest.userMessages.push(trimmed.length > MAX_MSG_CHARS ? trimmed.slice(0, MAX_MSG_CHARS) + " [...]" : trimmed);
|
|
11060
|
+
}
|
|
11061
|
+
}
|
|
11062
|
+
if (!digest.start) {
|
|
11063
|
+
const ms = composerCreatedAt.get(cid);
|
|
11064
|
+
if (ms) {
|
|
11065
|
+
const iso = new Date(ms).toISOString();
|
|
11066
|
+
digest.start = iso;
|
|
11067
|
+
digest.end = iso;
|
|
11068
|
+
}
|
|
11069
|
+
}
|
|
11070
|
+
if (digest.userMessages.length === 0)
|
|
11071
|
+
continue;
|
|
11072
|
+
if (sinceISO && digest.end && digest.end < sinceISO)
|
|
11073
|
+
continue;
|
|
11074
|
+
digests.push(digest);
|
|
11075
|
+
}
|
|
11076
|
+
return digests;
|
|
11077
|
+
}
|
|
10992
11078
|
function formatCombinedDigest(sessions, opts = { days: 7 }) {
|
|
10993
11079
|
const sorted = [...sessions].sort((a, b) => (a.start ?? "").localeCompare(b.start ?? ""));
|
|
10994
11080
|
const lines = [];
|
|
@@ -11032,6 +11118,33 @@ function formatCombinedDigest(sessions, opts = { days: 7 }) {
|
|
|
11032
11118
|
// src/agents/utils/json-config.ts
|
|
11033
11119
|
import { readFileSync as readFileSync22, writeFileSync as writeFileSync14, mkdirSync as mkdirSync13, existsSync as existsSync25 } from "fs";
|
|
11034
11120
|
import { dirname as dirname5 } from "path";
|
|
11121
|
+
|
|
11122
|
+
// src/sync/hash.ts
|
|
11123
|
+
import { createHash as createHash2 } from "crypto";
|
|
11124
|
+
function contentHash(content) {
|
|
11125
|
+
return "sha256:" + createHash2("sha256").update(content).digest("hex");
|
|
11126
|
+
}
|
|
11127
|
+
function stableStringify(value) {
|
|
11128
|
+
return JSON.stringify(sortValue(value));
|
|
11129
|
+
}
|
|
11130
|
+
function sortValue(value) {
|
|
11131
|
+
if (Array.isArray(value))
|
|
11132
|
+
return value.map(sortValue);
|
|
11133
|
+
if (value !== null && typeof value === "object") {
|
|
11134
|
+
const obj = value;
|
|
11135
|
+
const sorted = {};
|
|
11136
|
+
for (const key of Object.keys(obj).sort()) {
|
|
11137
|
+
sorted[key] = sortValue(obj[key]);
|
|
11138
|
+
}
|
|
11139
|
+
return sorted;
|
|
11140
|
+
}
|
|
11141
|
+
return value;
|
|
11142
|
+
}
|
|
11143
|
+
function configHash(value) {
|
|
11144
|
+
return contentHash(stableStringify(value));
|
|
11145
|
+
}
|
|
11146
|
+
|
|
11147
|
+
// src/agents/utils/json-config.ts
|
|
11035
11148
|
function isRunworkManagedKey(key) {
|
|
11036
11149
|
return key === RUNWORK_WORKSPACE_MCP_NAME || key.startsWith(RUNWORK_MCP_PREFIX) || key.startsWith(RUNWORK_MCP_PREFIX_LEGACY);
|
|
11037
11150
|
}
|
|
@@ -11070,6 +11183,14 @@ function removeRunworkMcpServers(filePath, topKey) {
|
|
|
11070
11183
|
function mergeJsonMcpServers(filePath, servers, topKey) {
|
|
11071
11184
|
const config = readJsonConfig(filePath);
|
|
11072
11185
|
const existing = config[topKey] || {};
|
|
11186
|
+
const managedBefore = {};
|
|
11187
|
+
for (const key of Object.keys(existing)) {
|
|
11188
|
+
if (isRunworkManagedKey(key))
|
|
11189
|
+
managedBefore[key] = existing[key];
|
|
11190
|
+
}
|
|
11191
|
+
const changed = configHash(managedBefore) !== configHash(servers);
|
|
11192
|
+
if (!changed)
|
|
11193
|
+
return false;
|
|
11073
11194
|
for (const key of Object.keys(existing)) {
|
|
11074
11195
|
if (isRunworkManagedKey(key)) {
|
|
11075
11196
|
delete existing[key];
|
|
@@ -11078,6 +11199,7 @@ function mergeJsonMcpServers(filePath, servers, topKey) {
|
|
|
11078
11199
|
Object.assign(existing, servers);
|
|
11079
11200
|
config[topKey] = existing;
|
|
11080
11201
|
writeJsonConfig(filePath, config);
|
|
11202
|
+
return true;
|
|
11081
11203
|
}
|
|
11082
11204
|
|
|
11083
11205
|
// src/agents/utils/instruction-hint.ts
|
|
@@ -11401,6 +11523,19 @@ function resolveAgentDefaults(input) {
|
|
|
11401
11523
|
};
|
|
11402
11524
|
}
|
|
11403
11525
|
|
|
11526
|
+
// src/ui/verbosity.ts
|
|
11527
|
+
var verbose = false;
|
|
11528
|
+
function setVerbose(value) {
|
|
11529
|
+
verbose = value;
|
|
11530
|
+
}
|
|
11531
|
+
function isVerbose() {
|
|
11532
|
+
return verbose;
|
|
11533
|
+
}
|
|
11534
|
+
function vlog(...args) {
|
|
11535
|
+
if (verbose)
|
|
11536
|
+
console.log(...args);
|
|
11537
|
+
}
|
|
11538
|
+
|
|
11404
11539
|
// src/agents/claude-code.ts
|
|
11405
11540
|
var PLUGIN_NAME = "runwork";
|
|
11406
11541
|
var PLUGIN_VERSION = "1.0.0";
|
|
@@ -11503,7 +11638,7 @@ class ClaudeCodeAdapter {
|
|
|
11503
11638
|
]
|
|
11504
11639
|
};
|
|
11505
11640
|
writeFileSync16(join21(hooksDir, "hooks.json"), JSON.stringify(hooksManifest, null, 2));
|
|
11506
|
-
|
|
11641
|
+
vlog(` [Claude Code] Installed SessionStart hook (${label}) -> ${scriptPath}`);
|
|
11507
11642
|
}
|
|
11508
11643
|
async writeSkills(skills, scope) {
|
|
11509
11644
|
const baseDir = scope === "project" ? join21(process.cwd(), ".claude", "skills") : join21(homedir5(), ".claude", "skills");
|
|
@@ -11536,6 +11671,7 @@ class ClaudeCodeAdapter {
|
|
|
11536
11671
|
}
|
|
11537
11672
|
this.registerPlugin(pluginDir);
|
|
11538
11673
|
}
|
|
11674
|
+
return skills.length;
|
|
11539
11675
|
}
|
|
11540
11676
|
async writeBuiltInHooks(scope) {
|
|
11541
11677
|
if (scope !== "user")
|
|
@@ -12030,7 +12166,7 @@ ${instructions}`;
|
|
|
12030
12166
|
lastUpdated: new Date().toISOString()
|
|
12031
12167
|
};
|
|
12032
12168
|
writeJsonConfig(marketplacesPath, marketplaces);
|
|
12033
|
-
|
|
12169
|
+
vlog(` [Claude Code] ${wasRegistered ? "Refreshed" : "Registered"} plugin ` + `${pluginKey} v${PLUGIN_VERSION} -> ${installedPath}`);
|
|
12034
12170
|
const settingsPath = join21(homedir5(), ".claude", "settings.json");
|
|
12035
12171
|
const settings = readJsonConfig(settingsPath);
|
|
12036
12172
|
if (!settings["enabledPlugins"]) {
|
|
@@ -12330,12 +12466,12 @@ class ClaudeDesktopAdapter {
|
|
|
12330
12466
|
if (rpm) {
|
|
12331
12467
|
writePluginMetadata(rpm.pluginPath, getPluginMetadata());
|
|
12332
12468
|
writePluginSkills(rpm.pluginPath, skills);
|
|
12333
|
-
return;
|
|
12469
|
+
return skills.length;
|
|
12334
12470
|
}
|
|
12335
12471
|
const pluginsDir = findCoworkPluginsDir();
|
|
12336
12472
|
if (!pluginsDir) {
|
|
12337
12473
|
console.warn(" [Claude Desktop] Cowork plugins directory not found. Open Claude Desktop at least once first.");
|
|
12338
|
-
return;
|
|
12474
|
+
return 0;
|
|
12339
12475
|
}
|
|
12340
12476
|
const cacheDir = join23(pluginsDir, "cache", "runwork", PLUGIN_NAME2, PLUGIN_VERSION2);
|
|
12341
12477
|
const marketRoot = join23(pluginsDir, "marketplaces", "runwork");
|
|
@@ -12370,6 +12506,7 @@ class ClaudeDesktopAdapter {
|
|
|
12370
12506
|
};
|
|
12371
12507
|
writeJsonConfig(marketplacesPath, marketplaces);
|
|
12372
12508
|
setCoworkPluginEnabled(pluginsDir, true);
|
|
12509
|
+
return skills.length;
|
|
12373
12510
|
}
|
|
12374
12511
|
async writeInstructionHint(hint, _scope) {
|
|
12375
12512
|
for (const path2 of getCoworkMemoryClaudeMdPaths()) {
|
|
@@ -12780,6 +12917,9 @@ async function loadAdapter() {
|
|
|
12780
12917
|
return null;
|
|
12781
12918
|
}
|
|
12782
12919
|
adapter = await loadAdapter();
|
|
12920
|
+
function sqliteAvailable() {
|
|
12921
|
+
return adapter !== null;
|
|
12922
|
+
}
|
|
12783
12923
|
var BUSY_TIMEOUT_MS = 5000;
|
|
12784
12924
|
function setBusyTimeout(db) {
|
|
12785
12925
|
try {
|
|
@@ -12857,7 +12997,7 @@ class CursorAdapter {
|
|
|
12857
12997
|
}
|
|
12858
12998
|
async writeSkills(skills, scope) {
|
|
12859
12999
|
if (scope === "user")
|
|
12860
|
-
return;
|
|
13000
|
+
return 0;
|
|
12861
13001
|
const rulesDir = join24(process.cwd(), ".cursor", "rules");
|
|
12862
13002
|
mkdirSync18(rulesDir, { recursive: true });
|
|
12863
13003
|
for (const skill of skills) {
|
|
@@ -12869,6 +13009,7 @@ alwaysApply: false
|
|
|
12869
13009
|
${skill.content}`;
|
|
12870
13010
|
writeFileSync19(join24(rulesDir, `${skill.filename}.mdc`), mdcContent);
|
|
12871
13011
|
}
|
|
13012
|
+
return skills.length;
|
|
12872
13013
|
}
|
|
12873
13014
|
async writeInstructionHint(hint, scope) {
|
|
12874
13015
|
const filePath = scope === "project" ? join24(process.cwd(), ".cursor", "rules", "runwork.mdc") : join24(homedir7(), ".cursor", "rules", "runwork.mdc");
|
|
@@ -12898,15 +13039,7 @@ ${instructions}`;
|
|
|
12898
13039
|
if (config.networkAllowlist?.length) {
|
|
12899
13040
|
this.mergeSandboxAllowlist(config.networkAllowlist);
|
|
12900
13041
|
}
|
|
12901
|
-
const
|
|
12902
|
-
let globalDbPath;
|
|
12903
|
-
if (os2 === "darwin") {
|
|
12904
|
-
globalDbPath = join24(homedir7(), "Library", "Application Support", "Cursor", "User", "globalStorage", "state.vscdb");
|
|
12905
|
-
} else if (os2 === "win32") {
|
|
12906
|
-
globalDbPath = join24(process.env.APPDATA || join24(homedir7(), "AppData", "Roaming"), "Cursor", "User", "globalStorage", "state.vscdb");
|
|
12907
|
-
} else {
|
|
12908
|
-
globalDbPath = join24(homedir7(), ".config", "Cursor", "User", "globalStorage", "state.vscdb");
|
|
12909
|
-
}
|
|
13042
|
+
const globalDbPath = this.globalStorageDbPath();
|
|
12910
13043
|
if (!existsSync30(globalDbPath))
|
|
12911
13044
|
return;
|
|
12912
13045
|
const db = openWritableSqlite(globalDbPath);
|
|
@@ -12986,15 +13119,7 @@ ${instructions}`;
|
|
|
12986
13119
|
async readUsageStats(lastSyncAt) {
|
|
12987
13120
|
try {
|
|
12988
13121
|
const sinceMs = lastSyncAt ? new Date(lastSyncAt).getTime() : 0;
|
|
12989
|
-
const
|
|
12990
|
-
let globalDbPath;
|
|
12991
|
-
if (os2 === "darwin") {
|
|
12992
|
-
globalDbPath = join24(homedir7(), "Library", "Application Support", "Cursor", "User", "globalStorage", "state.vscdb");
|
|
12993
|
-
} else if (os2 === "win32") {
|
|
12994
|
-
globalDbPath = join24(process.env.APPDATA || join24(homedir7(), "AppData", "Roaming"), "Cursor", "User", "globalStorage", "state.vscdb");
|
|
12995
|
-
} else {
|
|
12996
|
-
globalDbPath = join24(homedir7(), ".config", "Cursor", "User", "globalStorage", "state.vscdb");
|
|
12997
|
-
}
|
|
13122
|
+
const globalDbPath = this.globalStorageDbPath();
|
|
12998
13123
|
let sessionCount = 0;
|
|
12999
13124
|
let totalLinesAdded = 0;
|
|
13000
13125
|
let totalLinesRemoved = 0;
|
|
@@ -13069,6 +13194,30 @@ ${instructions}`;
|
|
|
13069
13194
|
return null;
|
|
13070
13195
|
}
|
|
13071
13196
|
}
|
|
13197
|
+
globalStorageDbPath() {
|
|
13198
|
+
const os2 = platform4();
|
|
13199
|
+
if (os2 === "darwin") {
|
|
13200
|
+
return join24(homedir7(), "Library", "Application Support", "Cursor", "User", "globalStorage", "state.vscdb");
|
|
13201
|
+
}
|
|
13202
|
+
if (os2 === "win32") {
|
|
13203
|
+
return join24(process.env.APPDATA || join24(homedir7(), "AppData", "Roaming"), "Cursor", "User", "globalStorage", "state.vscdb");
|
|
13204
|
+
}
|
|
13205
|
+
return join24(homedir7(), ".config", "Cursor", "User", "globalStorage", "state.vscdb");
|
|
13206
|
+
}
|
|
13207
|
+
async readSessionDigests(sinceISO) {
|
|
13208
|
+
try {
|
|
13209
|
+
const dbPath = this.globalStorageDbPath();
|
|
13210
|
+
if (!existsSync30(dbPath) || !sqliteAvailable())
|
|
13211
|
+
return null;
|
|
13212
|
+
const composerRows = queryReadonlySqlite(dbPath, `SELECT json_object('id', json_extract(value,'$.composerId'), 'createdAt', json_extract(value,'$.createdAt')) FROM cursorDiskKV WHERE key LIKE 'composerData:%'`);
|
|
13213
|
+
const bubbleRows = queryReadonlySqlite(dbPath, `SELECT json_object('k', key, 'type', json_extract(value,'$.type'), 'text', substr(json_extract(value,'$.text'),1,2000), 'ts', json_extract(value,'$.createdAt'), 'tool', json_extract(value,'$.toolFormerData.name'), 'status', json_extract(value,'$.toolFormerData.status')) FROM cursorDiskKV WHERE key LIKE 'bubbleId:%'`);
|
|
13214
|
+
if (!bubbleRows.trim())
|
|
13215
|
+
return [];
|
|
13216
|
+
return extractCursorSessions(composerRows, bubbleRows, sinceISO);
|
|
13217
|
+
} catch {
|
|
13218
|
+
return null;
|
|
13219
|
+
}
|
|
13220
|
+
}
|
|
13072
13221
|
}
|
|
13073
13222
|
|
|
13074
13223
|
// src/agents/windsurf.ts
|
|
@@ -13113,7 +13262,7 @@ class WindsurfAdapter {
|
|
|
13113
13262
|
}
|
|
13114
13263
|
async writeSkills(skills, scope) {
|
|
13115
13264
|
if (scope === "user")
|
|
13116
|
-
return;
|
|
13265
|
+
return 0;
|
|
13117
13266
|
const rulesDir = join25(process.cwd(), ".windsurf", "rules");
|
|
13118
13267
|
mkdirSync19(rulesDir, { recursive: true });
|
|
13119
13268
|
for (const skill of skills) {
|
|
@@ -13124,6 +13273,7 @@ trigger: manual
|
|
|
13124
13273
|
${skill.content}`;
|
|
13125
13274
|
writeFileSync20(join25(rulesDir, `${skill.filename}.md`), content);
|
|
13126
13275
|
}
|
|
13276
|
+
return skills.length;
|
|
13127
13277
|
}
|
|
13128
13278
|
async writeTeamInstructions(instructions, scope) {
|
|
13129
13279
|
const filePath = scope === "project" ? join25(process.cwd(), ".windsurf", "rules", "runwork-team.md") : join25(getWindsurfDataDir(), "rules", "runwork-team.md");
|
|
@@ -13371,7 +13521,7 @@ var AGENT_REGISTRY = [
|
|
|
13371
13521
|
]
|
|
13372
13522
|
},
|
|
13373
13523
|
launch: { app: { macos: "Codex", windows: "Codex" } },
|
|
13374
|
-
logo: "
|
|
13524
|
+
logo: "codex",
|
|
13375
13525
|
downloadUrl: "https://openai.com/codex/",
|
|
13376
13526
|
skillsPaths: { global: ".codex/skills", project: ".agents/skills" },
|
|
13377
13527
|
instructionFile: { global: ".codex/instructions.md", project: "AGENTS.md" },
|
|
@@ -13396,7 +13546,7 @@ var AGENT_REGISTRY = [
|
|
|
13396
13546
|
category: "cli",
|
|
13397
13547
|
detection: { method: "binary", target: "codex" },
|
|
13398
13548
|
launch: { cli: "codex", cliAcceptsPrompt: true },
|
|
13399
|
-
logo: "
|
|
13549
|
+
logo: "codex",
|
|
13400
13550
|
downloadUrl: "https://github.com/openai/codex",
|
|
13401
13551
|
skillsPaths: { global: ".codex/skills", project: ".agents/skills" },
|
|
13402
13552
|
instructionFile: { global: ".codex/instructions.md", project: "AGENTS.md" },
|
|
@@ -13919,6 +14069,7 @@ class CodexAdapter {
|
|
|
13919
14069
|
mkdirSync20(skillDir, { recursive: true });
|
|
13920
14070
|
writeFileSync21(join28(skillDir, "SKILL.md"), buildSkillMd2(skill));
|
|
13921
14071
|
}
|
|
14072
|
+
return skills.length;
|
|
13922
14073
|
}
|
|
13923
14074
|
async writeInstructionHint(hint, scope) {
|
|
13924
14075
|
const filePath = scope === "project" ? join28(process.cwd(), "AGENTS.md") : join28(homedir11(), ".codex", "AGENTS.md");
|
|
@@ -14280,12 +14431,13 @@ class ClineAdapter {
|
|
|
14280
14431
|
}
|
|
14281
14432
|
async writeSkills(skills, scope) {
|
|
14282
14433
|
if (scope === "user")
|
|
14283
|
-
return;
|
|
14434
|
+
return 0;
|
|
14284
14435
|
const rulesDir = join29(process.cwd(), ".clinerules");
|
|
14285
14436
|
mkdirSync21(rulesDir, { recursive: true });
|
|
14286
14437
|
for (const skill of skills) {
|
|
14287
14438
|
writeFileSync22(join29(rulesDir, `${skill.filename}.md`), buildSkillMd2(skill));
|
|
14288
14439
|
}
|
|
14440
|
+
return skills.length;
|
|
14289
14441
|
}
|
|
14290
14442
|
async writeInstructionHint(hint, scope) {
|
|
14291
14443
|
if (scope === "user")
|
|
@@ -14410,6 +14562,7 @@ class GeminiAdapter {
|
|
|
14410
14562
|
mkdirSync22(skillDir, { recursive: true });
|
|
14411
14563
|
writeFileSync23(join30(skillDir, "SKILL.md"), buildSkillMd2(skill));
|
|
14412
14564
|
}
|
|
14565
|
+
return skills.length;
|
|
14413
14566
|
}
|
|
14414
14567
|
async writeInstructionHint(hint, scope) {
|
|
14415
14568
|
const filePath = scope === "project" ? join30(process.cwd(), "GEMINI.md") : join30(homedir13(), ".gemini", "GEMINI.md");
|
|
@@ -14509,13 +14662,13 @@ class GenericAgentAdapter {
|
|
|
14509
14662
|
}
|
|
14510
14663
|
async writeSkills(skills, scope) {
|
|
14511
14664
|
if (!this.def.skillsPaths)
|
|
14512
|
-
return;
|
|
14665
|
+
return 0;
|
|
14513
14666
|
const pathTemplate = scope === "project" ? this.def.skillsPaths.project : this.def.skillsPaths.global;
|
|
14514
14667
|
if (!pathTemplate)
|
|
14515
|
-
return;
|
|
14668
|
+
return 0;
|
|
14516
14669
|
const baseDir = resolveToAbsolute(pathTemplate, scope === "project" ? "project" : "global");
|
|
14517
14670
|
if (!baseDir)
|
|
14518
|
-
return;
|
|
14671
|
+
return 0;
|
|
14519
14672
|
for (const skill of skills) {
|
|
14520
14673
|
if (skill.name !== skill.filename) {
|
|
14521
14674
|
const oldDir = join31(baseDir, skill.name);
|
|
@@ -14529,6 +14682,7 @@ class GenericAgentAdapter {
|
|
|
14529
14682
|
mkdirSync23(skillDir, { recursive: true });
|
|
14530
14683
|
writeFileSync24(join31(skillDir, "SKILL.md"), buildSkillMd2(skill));
|
|
14531
14684
|
}
|
|
14685
|
+
return skills.length;
|
|
14532
14686
|
}
|
|
14533
14687
|
async writeInstructionHint(hint, scope) {
|
|
14534
14688
|
if (!this.def.instructionFile)
|
|
@@ -14643,9 +14797,9 @@ function printNoAgentsMessage() {
|
|
|
14643
14797
|
}
|
|
14644
14798
|
|
|
14645
14799
|
// src/utils/insight-id.ts
|
|
14646
|
-
import { createHash as
|
|
14800
|
+
import { createHash as createHash3 } from "node:crypto";
|
|
14647
14801
|
function computeInsightId(userSeed, localKey) {
|
|
14648
|
-
return
|
|
14802
|
+
return createHash3("sha256").update(`${userSeed}:${localKey}`).digest("hex").slice(0, 22);
|
|
14649
14803
|
}
|
|
14650
14804
|
function insightLocalKey(teaches, slug) {
|
|
14651
14805
|
return `${teaches}:${slug}`.toLowerCase().replace(/[^a-z0-9:_-]+/g, "-").replace(/-+/g, "-");
|
|
@@ -16567,19 +16721,6 @@ async function collectTelemetryEvents(params) {
|
|
|
16567
16721
|
const now = params.now ?? new Date().toISOString();
|
|
16568
16722
|
const events = [];
|
|
16569
16723
|
const adapterResults = [];
|
|
16570
|
-
const previouslyReported = new Set(params.state.reportedAgentSlugs ?? []);
|
|
16571
|
-
const newlyReportedAgents = [];
|
|
16572
|
-
for (const slug of params.state.configuredAgents) {
|
|
16573
|
-
if (!previouslyReported.has(slug)) {
|
|
16574
|
-
events.push({
|
|
16575
|
-
eventType: "local_agent.detected",
|
|
16576
|
-
agentSlug: slug,
|
|
16577
|
-
metadata: { installed: true },
|
|
16578
|
-
timestamp: now
|
|
16579
|
-
});
|
|
16580
|
-
newlyReportedAgents.push(slug);
|
|
16581
|
-
}
|
|
16582
|
-
}
|
|
16583
16724
|
for (const adapter2 of params.adapters) {
|
|
16584
16725
|
let stats = "unsupported";
|
|
16585
16726
|
let skills = "unsupported";
|
|
@@ -16671,11 +16812,12 @@ async function collectTelemetryEvents(params) {
|
|
|
16671
16812
|
syncedMcpServers: params.syncedMcpServersCount,
|
|
16672
16813
|
teamInstructionsApplied: params.teamInstructionsApplied,
|
|
16673
16814
|
agentConfigsApplied: params.agentConfigsApplied,
|
|
16815
|
+
agents: [...params.state.configuredAgents],
|
|
16674
16816
|
...params.state.persona?.label ? { persona: params.state.persona.label } : {}
|
|
16675
16817
|
},
|
|
16676
16818
|
timestamp: now
|
|
16677
16819
|
});
|
|
16678
|
-
return { events, adapterResults,
|
|
16820
|
+
return { events, adapterResults, healthReported };
|
|
16679
16821
|
}
|
|
16680
16822
|
function formatStatsLine(stats) {
|
|
16681
16823
|
const parts = [];
|
|
@@ -16721,10 +16863,6 @@ function printTelemetryVerbose(result) {
|
|
|
16721
16863
|
const errSuffix = r.error ? ` (error: ${r.error})` : "";
|
|
16722
16864
|
console.log(` [${r.name}] stats: ${statsLabel}; skills: ${skillsLabel}${errSuffix}`);
|
|
16723
16865
|
}
|
|
16724
|
-
if (result.newlyReportedAgents.length > 0) {
|
|
16725
|
-
console.log(`
|
|
16726
|
-
First-time detections: ${result.newlyReportedAgents.join(", ")}`);
|
|
16727
|
-
}
|
|
16728
16866
|
if (result.healthReported) {
|
|
16729
16867
|
console.log(" Health report: due (daily)");
|
|
16730
16868
|
}
|
|
@@ -17354,12 +17492,6 @@ function buildSaveConversationSkill() {
|
|
|
17354
17492
|
};
|
|
17355
17493
|
}
|
|
17356
17494
|
|
|
17357
|
-
// src/sync/hash.ts
|
|
17358
|
-
import { createHash as createHash3 } from "crypto";
|
|
17359
|
-
function contentHash(content) {
|
|
17360
|
-
return "sha256:" + createHash3("sha256").update(content).digest("hex");
|
|
17361
|
-
}
|
|
17362
|
-
|
|
17363
17495
|
// src/sync/change-detect.ts
|
|
17364
17496
|
function computeSyncPlan(input) {
|
|
17365
17497
|
const { storedHashes, localSkills, remoteSkills } = input;
|
|
@@ -17391,6 +17523,16 @@ function computeSyncPlan(input) {
|
|
|
17391
17523
|
const localChanged = localHash !== null && localHash !== stored.localHash;
|
|
17392
17524
|
const remoteChanged = remoteHash !== stored.remoteHash;
|
|
17393
17525
|
if (!local) {
|
|
17526
|
+
if (remote.source === "app" && !remoteChanged) {
|
|
17527
|
+
plan.skips.push({
|
|
17528
|
+
name: remote.name,
|
|
17529
|
+
action: "skip",
|
|
17530
|
+
source: remote.source,
|
|
17531
|
+
remoteId: remote.remoteId,
|
|
17532
|
+
appId: remote.appId
|
|
17533
|
+
});
|
|
17534
|
+
continue;
|
|
17535
|
+
}
|
|
17394
17536
|
plan.pulls.push({
|
|
17395
17537
|
name: remote.name,
|
|
17396
17538
|
action: "pull",
|
|
@@ -17570,6 +17712,47 @@ function resolveConflictNonInteractive(prefer) {
|
|
|
17570
17712
|
return prefer;
|
|
17571
17713
|
}
|
|
17572
17714
|
|
|
17715
|
+
// src/sync/summary-format.ts
|
|
17716
|
+
function summarizeSkillPlan(plan) {
|
|
17717
|
+
return {
|
|
17718
|
+
added: plan.pulls.filter((p) => p.action === "new-remote").map((p) => p.name),
|
|
17719
|
+
updated: plan.pulls.filter((p) => p.action === "pull").map((p) => p.name),
|
|
17720
|
+
removed: plan.deletions.map((p) => p.name),
|
|
17721
|
+
pushed: plan.pushes.map((p) => p.name),
|
|
17722
|
+
unchanged: plan.skips.length,
|
|
17723
|
+
conflicts: plan.conflicts.length
|
|
17724
|
+
};
|
|
17725
|
+
}
|
|
17726
|
+
function formatNameList(names, cap2 = 6) {
|
|
17727
|
+
if (names.length <= cap2)
|
|
17728
|
+
return names.join(", ");
|
|
17729
|
+
return `${names.slice(0, cap2).join(", ")}, +${names.length - cap2} more`;
|
|
17730
|
+
}
|
|
17731
|
+
function formatSkillSummaryLine(c) {
|
|
17732
|
+
const parts = [];
|
|
17733
|
+
if (c.added.length)
|
|
17734
|
+
parts.push(`${c.added.length} added (${formatNameList(c.added)})`);
|
|
17735
|
+
if (c.updated.length)
|
|
17736
|
+
parts.push(`${c.updated.length} updated (${formatNameList(c.updated)})`);
|
|
17737
|
+
if (c.removed.length)
|
|
17738
|
+
parts.push(`${c.removed.length} removed (${formatNameList(c.removed)})`);
|
|
17739
|
+
if (c.pushed.length)
|
|
17740
|
+
parts.push(`${c.pushed.length} pushed (${formatNameList(c.pushed)})`);
|
|
17741
|
+
if (c.conflicts)
|
|
17742
|
+
parts.push(`${c.conflicts} conflict${c.conflicts === 1 ? "" : "s"}`);
|
|
17743
|
+
parts.push(`${c.unchanged} up to date`);
|
|
17744
|
+
return parts.join(", ");
|
|
17745
|
+
}
|
|
17746
|
+
var REASON_LABEL = {
|
|
17747
|
+
mcp: "MCP",
|
|
17748
|
+
settings: "settings"
|
|
17749
|
+
};
|
|
17750
|
+
function formatRestartChanges(changes) {
|
|
17751
|
+
if (changes.length === 0)
|
|
17752
|
+
return "none";
|
|
17753
|
+
return changes.map((c) => `${c.name} (${c.reasons.map((r) => REASON_LABEL[r]).join(", ")})`).join("; ");
|
|
17754
|
+
}
|
|
17755
|
+
|
|
17573
17756
|
// src/sync/executor.ts
|
|
17574
17757
|
async function executeSyncPlan(plan, resolvedConflicts, ctx) {
|
|
17575
17758
|
const newHashes = {};
|
|
@@ -17585,7 +17768,7 @@ async function executeSyncPlan(plan, resolvedConflicts, ctx) {
|
|
|
17585
17768
|
remoteId: action.remoteId,
|
|
17586
17769
|
appId: action.appId
|
|
17587
17770
|
};
|
|
17588
|
-
|
|
17771
|
+
vlog(` Pulled: ${action.name}`);
|
|
17589
17772
|
}
|
|
17590
17773
|
for (const action of plan.pushes) {
|
|
17591
17774
|
if (!action.localContent || !action.remoteId)
|
|
@@ -17602,7 +17785,7 @@ async function executeSyncPlan(plan, resolvedConflicts, ctx) {
|
|
|
17602
17785
|
source: "external",
|
|
17603
17786
|
remoteId: action.remoteId
|
|
17604
17787
|
};
|
|
17605
|
-
|
|
17788
|
+
vlog(` Pushed: ${action.name}`);
|
|
17606
17789
|
}
|
|
17607
17790
|
for (const action of plan.conflicts) {
|
|
17608
17791
|
const resolution = resolvedConflicts.get(action.name);
|
|
@@ -17616,7 +17799,7 @@ async function executeSyncPlan(plan, resolvedConflicts, ctx) {
|
|
|
17616
17799
|
});
|
|
17617
17800
|
const hash = contentHash(action.localContent);
|
|
17618
17801
|
newHashes[action.name] = { localHash: hash, remoteHash: hash, source: "external", remoteId: action.remoteId };
|
|
17619
|
-
|
|
17802
|
+
vlog(` Pushed (conflict resolved): ${action.name}`);
|
|
17620
17803
|
} else if (resolution === "remote" && action.remoteContent) {
|
|
17621
17804
|
const skillFile = makeSkillFile(action.name, action.remoteContent);
|
|
17622
17805
|
await writeSkillToAgents(skillFile, action.source, ctx);
|
|
@@ -17626,12 +17809,12 @@ async function executeSyncPlan(plan, resolvedConflicts, ctx) {
|
|
|
17626
17809
|
source: "external",
|
|
17627
17810
|
remoteId: action.remoteId
|
|
17628
17811
|
};
|
|
17629
|
-
|
|
17812
|
+
vlog(` Pulled (conflict resolved): ${action.name}`);
|
|
17630
17813
|
}
|
|
17631
17814
|
}
|
|
17632
17815
|
for (const _action of plan.skips) {}
|
|
17633
17816
|
for (const action of plan.deletions) {
|
|
17634
|
-
|
|
17817
|
+
vlog(` Deleted locally: ${action.name}`);
|
|
17635
17818
|
}
|
|
17636
17819
|
return newHashes;
|
|
17637
17820
|
}
|
|
@@ -17661,6 +17844,53 @@ function extractDescription(content) {
|
|
|
17661
17844
|
return match ? match[1].trim() : "";
|
|
17662
17845
|
}
|
|
17663
17846
|
|
|
17847
|
+
// src/sync/restart-stamp.ts
|
|
17848
|
+
function computeRestartReasons(input) {
|
|
17849
|
+
const settingsSet = new Set(input.settingsChangedSlugs);
|
|
17850
|
+
const out = {};
|
|
17851
|
+
for (const agent of input.agents) {
|
|
17852
|
+
if (agent.connectOnly)
|
|
17853
|
+
continue;
|
|
17854
|
+
const reasons = [];
|
|
17855
|
+
if ((input.mcpChanged || agent.isNew) && agent.receivesMcp)
|
|
17856
|
+
reasons.push("mcp");
|
|
17857
|
+
if (settingsSet.has(agent.slug))
|
|
17858
|
+
reasons.push("settings");
|
|
17859
|
+
if (reasons.length > 0)
|
|
17860
|
+
out[agent.slug] = reasons;
|
|
17861
|
+
}
|
|
17862
|
+
return out;
|
|
17863
|
+
}
|
|
17864
|
+
function summarizeRealSkillChanges(newLocalHashes, priorLocalHashes, materializedDeletions) {
|
|
17865
|
+
const added = [];
|
|
17866
|
+
const updated = [];
|
|
17867
|
+
for (const [name, hash] of Object.entries(newLocalHashes)) {
|
|
17868
|
+
if (!(name in priorLocalHashes))
|
|
17869
|
+
added.push(name);
|
|
17870
|
+
else if (priorLocalHashes[name] !== hash)
|
|
17871
|
+
updated.push(name);
|
|
17872
|
+
}
|
|
17873
|
+
return {
|
|
17874
|
+
added: added.sort(),
|
|
17875
|
+
updated: updated.sort(),
|
|
17876
|
+
removed: [...materializedDeletions].sort()
|
|
17877
|
+
};
|
|
17878
|
+
}
|
|
17879
|
+
function hasRealSkillChange(c) {
|
|
17880
|
+
return c.added.length > 0 || c.updated.length > 0 || c.removed.length > 0;
|
|
17881
|
+
}
|
|
17882
|
+
function sameStringSet(a, b) {
|
|
17883
|
+
const setA = new Set(a ?? []);
|
|
17884
|
+
const setB = new Set(b ?? []);
|
|
17885
|
+
if (setA.size !== setB.size)
|
|
17886
|
+
return false;
|
|
17887
|
+
for (const item of setA) {
|
|
17888
|
+
if (!setB.has(item))
|
|
17889
|
+
return false;
|
|
17890
|
+
}
|
|
17891
|
+
return true;
|
|
17892
|
+
}
|
|
17893
|
+
|
|
17664
17894
|
// src/commands/sync.ts
|
|
17665
17895
|
async function printAdoptionHint(credentials, workspaceId) {
|
|
17666
17896
|
if (!workspaceId)
|
|
@@ -17745,6 +17975,7 @@ async function refreshConfiguredAgents(state) {
|
|
|
17745
17975
|
}
|
|
17746
17976
|
async function syncFromState(state, statePath2, credentials, opts) {
|
|
17747
17977
|
const client = new ApiClient(credentials);
|
|
17978
|
+
setVerbose(!!opts.verbose);
|
|
17748
17979
|
if (!state.workspaceName || state.workspaceName === state.workspaceId || !state.workspaceSlug) {
|
|
17749
17980
|
try {
|
|
17750
17981
|
const workspaces = await client.listWorkspaces();
|
|
@@ -17756,11 +17987,12 @@ async function syncFromState(state, statePath2, credentials, opts) {
|
|
|
17756
17987
|
} catch {}
|
|
17757
17988
|
}
|
|
17758
17989
|
console.log(`Syncing workspace: ${state.workspaceName || state.workspaceId}`);
|
|
17990
|
+
let addedThisSync = [];
|
|
17759
17991
|
if (shouldRedetect(state, opts)) {
|
|
17760
17992
|
console.log(" Detecting installed agents...");
|
|
17761
|
-
|
|
17762
|
-
if (
|
|
17763
|
-
console.log(` Detected new agent${
|
|
17993
|
+
addedThisSync = await refreshConfiguredAgents(state);
|
|
17994
|
+
if (addedThisSync.length > 0) {
|
|
17995
|
+
console.log(` Detected new agent${addedThisSync.length > 1 ? "s" : ""}: ${addedThisSync.join(", ")}`);
|
|
17764
17996
|
}
|
|
17765
17997
|
}
|
|
17766
17998
|
console.log(" Fetching workspace data...");
|
|
@@ -17802,6 +18034,8 @@ async function syncFromState(state, statePath2, credentials, opts) {
|
|
|
17802
18034
|
headers: { Authorization: `Bearer ${credentials.apiKey}` },
|
|
17803
18035
|
description: "Runwork workspace tools: query entities, trigger workflows, manage integrations, run agents, and access shared data across your team's apps."
|
|
17804
18036
|
});
|
|
18037
|
+
const mcpEntriesHash = configHash([...mcpEntries].sort((a, b) => a.name.localeCompare(b.name)));
|
|
18038
|
+
const mcpChanged = state.lastMcpEntriesHash !== undefined && state.lastMcpEntriesHash !== mcpEntriesHash;
|
|
17805
18039
|
const remoteSkills = [];
|
|
17806
18040
|
for (const s of externalSkills) {
|
|
17807
18041
|
remoteSkills.push({ name: s.name, content: s.content, source: "external", remoteId: s.id });
|
|
@@ -17831,12 +18065,9 @@ async function syncFromState(state, statePath2, credentials, opts) {
|
|
|
17831
18065
|
plan.skips.push(...plan.conflicts.map((c) => ({ ...c, action: "skip" })));
|
|
17832
18066
|
plan.conflicts = [];
|
|
17833
18067
|
}
|
|
17834
|
-
|
|
17835
|
-
if (
|
|
18068
|
+
console.log(` Skills: ${formatSkillSummaryLine(summarizeSkillPlan(plan))}`);
|
|
18069
|
+
if (isVerbose())
|
|
17836
18070
|
printSyncSummary(plan);
|
|
17837
|
-
} else {
|
|
17838
|
-
console.log(" All skills up to date.");
|
|
17839
|
-
}
|
|
17840
18071
|
const adapters = [];
|
|
17841
18072
|
for (const slug of state.configuredAgents) {
|
|
17842
18073
|
const adapter2 = getAdapterBySlug(slug);
|
|
@@ -17876,6 +18107,7 @@ async function syncFromState(state, statePath2, credentials, opts) {
|
|
|
17876
18107
|
}
|
|
17877
18108
|
}
|
|
17878
18109
|
}
|
|
18110
|
+
const settingsChangedSlugs = [];
|
|
17879
18111
|
const scopes = state.scope === "both" ? ["project", "user"] : [state.scope];
|
|
17880
18112
|
if (adapters.length > 0) {
|
|
17881
18113
|
console.log(` Syncing to: ${adapters.map((a) => a.name).join(", ")}`);
|
|
@@ -17924,6 +18156,8 @@ async function syncFromState(state, statePath2, credentials, opts) {
|
|
|
17924
18156
|
buildSaveConversationSkill()
|
|
17925
18157
|
];
|
|
17926
18158
|
const builtInNames = builtInSkills.map((s) => s.name);
|
|
18159
|
+
const builtInSkillsHash = configHash(builtInSkills.map((s) => s.content));
|
|
18160
|
+
const builtInSkillsChanged = state.builtInSkillsHash !== undefined && state.builtInSkillsHash !== builtInSkillsHash;
|
|
17927
18161
|
const summary = {
|
|
17928
18162
|
adaptersProcessed: 0,
|
|
17929
18163
|
adaptersFailed: 0,
|
|
@@ -17933,16 +18167,18 @@ async function syncFromState(state, statePath2, credentials, opts) {
|
|
|
17933
18167
|
instructionHintWrites: 0,
|
|
17934
18168
|
hookInstallCalls: 0
|
|
17935
18169
|
};
|
|
18170
|
+
const mcpFailedAdapters = new Set;
|
|
18171
|
+
const skillFailedAdapters = new Set;
|
|
17936
18172
|
for (const adapter2 of adapters) {
|
|
17937
18173
|
if (isConnectOnlyAgent(getRegistryAgent(adapter2.slug))) {
|
|
17938
|
-
|
|
18174
|
+
vlog(` [${adapter2.name}] Connect-only agent: no local files to sync`);
|
|
17939
18175
|
summary.adaptersProcessed++;
|
|
17940
18176
|
continue;
|
|
17941
18177
|
}
|
|
17942
18178
|
let adapterFailedAnyScope = false;
|
|
17943
18179
|
for (const scope of scopes) {
|
|
17944
|
-
|
|
17945
|
-
|
|
18180
|
+
if (adapter2.supportsSkills()) {
|
|
18181
|
+
try {
|
|
17946
18182
|
const skipAppSkills = adapter2.mcpProvidesSkills && mcpEntries.length > 0;
|
|
17947
18183
|
const scopeSkills = remoteSkills.filter((s) => {
|
|
17948
18184
|
if (builtInNameSet.has(s.name))
|
|
@@ -17962,25 +18198,41 @@ async function syncFromState(state, statePath2, credentials, opts) {
|
|
|
17962
18198
|
description: s.source === "app" ? buildAppSkillDescription(s.name, registries) || `${s.name} - Runwork workspace application` : `${s.name} - Runwork workspace skill`
|
|
17963
18199
|
}));
|
|
17964
18200
|
const allSkillFiles = [...builtInSkills, ...workspaceSkillFiles];
|
|
17965
|
-
await adapter2.writeSkills(allSkillFiles, scope);
|
|
17966
|
-
|
|
17967
|
-
|
|
17968
|
-
|
|
17969
|
-
|
|
17970
|
-
|
|
18201
|
+
const written = await adapter2.writeSkills(allSkillFiles, scope);
|
|
18202
|
+
if (written > 0) {
|
|
18203
|
+
summary.skillWrites++;
|
|
18204
|
+
summary.skillFilesWritten += written;
|
|
18205
|
+
vlog(` [${adapter2.name}] Wrote ${written} skills (${scope}): ` + `${builtInSkills.length} built-in [${builtInNames.join(", ")}], ` + `${workspaceSkillFiles.length} workspace`);
|
|
18206
|
+
} else {
|
|
18207
|
+
vlog(` [${adapter2.name}] Skipped skills (${scope}): adapter writes none at this scope`);
|
|
18208
|
+
}
|
|
18209
|
+
} catch (err) {
|
|
18210
|
+
adapterFailedAnyScope = true;
|
|
18211
|
+
skillFailedAdapters.add(adapter2.slug);
|
|
18212
|
+
console.warn(` [${adapter2.name}] Failed skills (${scope}): ${err instanceof Error ? err.message : err}`);
|
|
17971
18213
|
}
|
|
17972
|
-
|
|
18214
|
+
} else {
|
|
18215
|
+
vlog(` [${adapter2.name}] Skipped skills (${scope}): adapter does not support skills`);
|
|
18216
|
+
}
|
|
18217
|
+
if (adapter2.supportsMcpScope(scope) && mcpEntries.length > 0) {
|
|
18218
|
+
try {
|
|
17973
18219
|
await adapter2.writeMcpServers(mcpEntries, scope);
|
|
17974
18220
|
summary.mcpServerWrites++;
|
|
17975
|
-
|
|
17976
|
-
}
|
|
17977
|
-
|
|
17978
|
-
|
|
17979
|
-
console.
|
|
18221
|
+
vlog(` [${adapter2.name}] Updated ${mcpEntries.length} MCP server${mcpEntries.length > 1 ? "s" : ""} (${scope})`);
|
|
18222
|
+
} catch (err) {
|
|
18223
|
+
adapterFailedAnyScope = true;
|
|
18224
|
+
mcpFailedAdapters.add(adapter2.slug);
|
|
18225
|
+
console.warn(` [${adapter2.name}] Failed MCP servers (${scope}): ${err instanceof Error ? err.message : err}`);
|
|
17980
18226
|
}
|
|
18227
|
+
} else if (mcpEntries.length === 0) {
|
|
18228
|
+
vlog(` [${adapter2.name}] Skipped MCP servers (${scope}): no workspace MCP servers configured`);
|
|
18229
|
+
} else if (!adapter2.supportsMcpScope(scope)) {
|
|
18230
|
+
vlog(` [${adapter2.name}] Skipped MCP servers (${scope}): adapter does not support MCP at this scope`);
|
|
18231
|
+
}
|
|
18232
|
+
try {
|
|
17981
18233
|
await adapter2.writeInstructionHint(instructionHint, scope);
|
|
17982
18234
|
summary.instructionHintWrites++;
|
|
17983
|
-
|
|
18235
|
+
vlog(` [${adapter2.name}] Updated instruction hints (${scope})`);
|
|
17984
18236
|
if (adapter2.writeBuiltInHooks) {
|
|
17985
18237
|
await adapter2.writeBuiltInHooks(scope);
|
|
17986
18238
|
summary.hookInstallCalls++;
|
|
@@ -18019,7 +18271,7 @@ async function syncFromState(state, statePath2, credentials, opts) {
|
|
|
18019
18271
|
try {
|
|
18020
18272
|
await adapter2.writeTeamInstructions(fullInstructions, scope);
|
|
18021
18273
|
teamInstructionsApplied = true;
|
|
18022
|
-
|
|
18274
|
+
vlog(` [${adapter2.name}] Updated team instructions (${scope})`);
|
|
18023
18275
|
} catch {}
|
|
18024
18276
|
}
|
|
18025
18277
|
}
|
|
@@ -18040,7 +18292,7 @@ async function syncFromState(state, statePath2, credentials, opts) {
|
|
|
18040
18292
|
await adapter2.writeAgentConfig(configWithoutInstructions, scope);
|
|
18041
18293
|
agentConfigsApplied++;
|
|
18042
18294
|
const configKeys = Object.keys(configWithoutInstructions).join(", ");
|
|
18043
|
-
|
|
18295
|
+
vlog(` [${adapter2.name}] Updated agent config: ${configKeys} (${scope})`);
|
|
18044
18296
|
} catch {}
|
|
18045
18297
|
}
|
|
18046
18298
|
}
|
|
@@ -18095,6 +18347,12 @@ async function syncFromState(state, statePath2, credentials, opts) {
|
|
|
18095
18347
|
try {
|
|
18096
18348
|
const baseline = agentState?.lastInjected;
|
|
18097
18349
|
await adapter2.writeAgentConfig(mergedConfig, "user", baseline);
|
|
18350
|
+
if (baked && !resolved.bootstrapped) {
|
|
18351
|
+
const prev = agentState?.lastInjected;
|
|
18352
|
+
if (!sameStringSet(prev?.allow, resolved.applicableAllow) || !sameStringSet(prev?.deny, resolved.applicableDeny)) {
|
|
18353
|
+
settingsChangedSlugs.push(slug);
|
|
18354
|
+
}
|
|
18355
|
+
}
|
|
18098
18356
|
if (baked) {
|
|
18099
18357
|
const nextState = {
|
|
18100
18358
|
lastInjected: {
|
|
@@ -18108,11 +18366,11 @@ async function syncFromState(state, statePath2, credentials, opts) {
|
|
|
18108
18366
|
};
|
|
18109
18367
|
state.agentDefaults[slug] = nextState;
|
|
18110
18368
|
if (resolved.bootstrapped) {
|
|
18111
|
-
|
|
18369
|
+
vlog(` [${adapter2.name}] Bootstrapped default-rule tracking (defaults apply on next sync)`);
|
|
18112
18370
|
} else if (resolved.applicableAllow.length || resolved.applicableDeny.length || resolved.removalsThisSync.allow || resolved.removalsThisSync.deny) {
|
|
18113
18371
|
const totalRemovals = resolved.removalsThisSync.allow + resolved.removalsThisSync.deny;
|
|
18114
18372
|
const removalNote = totalRemovals > 0 ? ` (${totalRemovals} new opt-out${totalRemovals > 1 ? "s" : ""} honored)` : "";
|
|
18115
|
-
|
|
18373
|
+
vlog(` [${adapter2.name}] Applied defaults: ${resolved.applicableAllow.length} allow, ${resolved.applicableDeny.length} deny${removalNote}`);
|
|
18116
18374
|
}
|
|
18117
18375
|
}
|
|
18118
18376
|
if (team)
|
|
@@ -18126,11 +18384,12 @@ async function syncFromState(state, statePath2, credentials, opts) {
|
|
|
18126
18384
|
const runworkDir = join35(homedir17(), ".runwork");
|
|
18127
18385
|
const result = adapter2.registerDesktopWorkspace(runworkDir, "Runwork");
|
|
18128
18386
|
if (result === "written") {
|
|
18129
|
-
|
|
18387
|
+
vlog(` [${adapter2.name}] Registered workspace in Codex desktop app`);
|
|
18130
18388
|
}
|
|
18131
18389
|
break;
|
|
18132
18390
|
}
|
|
18133
18391
|
}
|
|
18392
|
+
const prevMcpNames = state.mcpServers ?? [];
|
|
18134
18393
|
state.lastSyncAt = new Date().toISOString();
|
|
18135
18394
|
state.mcpServers = mcpEntries.map((e) => e.name);
|
|
18136
18395
|
state.skills = remoteSkills.map((s) => s.name);
|
|
@@ -18138,6 +18397,57 @@ async function syncFromState(state, statePath2, credentials, opts) {
|
|
|
18138
18397
|
"runwork",
|
|
18139
18398
|
...remoteSkills.map((s) => s.name.toLowerCase().replace(/[^a-z0-9]+/g, "-"))
|
|
18140
18399
|
];
|
|
18400
|
+
const priorSkillHashes = state.skillHashes || {};
|
|
18401
|
+
const newLocalHashes = {};
|
|
18402
|
+
for (const [name, h] of Object.entries(newHashes))
|
|
18403
|
+
newLocalHashes[name] = h.localHash;
|
|
18404
|
+
const priorLocalHashes = {};
|
|
18405
|
+
for (const [name, h] of Object.entries(priorSkillHashes))
|
|
18406
|
+
priorLocalHashes[name] = h.localHash;
|
|
18407
|
+
const materializedDeletions = plan.deletions.filter((d) => d.localContent !== undefined).map((d) => d.name);
|
|
18408
|
+
const skillChanges = summarizeRealSkillChanges(newLocalHashes, priorLocalHashes, materializedDeletions);
|
|
18409
|
+
const newMcpNames = mcpEntries.map((e) => e.name);
|
|
18410
|
+
const prevMcpSet = new Set(prevMcpNames);
|
|
18411
|
+
const newMcpSet = new Set(newMcpNames);
|
|
18412
|
+
const mcpAdded = mcpChanged ? newMcpNames.filter((n) => !prevMcpSet.has(n)) : [];
|
|
18413
|
+
const mcpRemoved = mcpChanged ? prevMcpNames.filter((n) => !newMcpSet.has(n)) : [];
|
|
18414
|
+
const addedSet = new Set(addedThisSync);
|
|
18415
|
+
const restartReasons = computeRestartReasons({
|
|
18416
|
+
agents: adapters.map((a) => ({
|
|
18417
|
+
slug: a.slug,
|
|
18418
|
+
receivesMcp: mcpEntries.length > 0 && scopes.some((sc) => a.supportsMcpScope(sc)) && !mcpFailedAdapters.has(a.slug),
|
|
18419
|
+
connectOnly: isConnectOnlyAgent(getRegistryAgent(a.slug)),
|
|
18420
|
+
isNew: !!opts.initialSetup || addedSet.has(a.slug)
|
|
18421
|
+
})),
|
|
18422
|
+
mcpChanged,
|
|
18423
|
+
settingsChangedSlugs
|
|
18424
|
+
});
|
|
18425
|
+
if (Object.keys(restartReasons).length > 0) {
|
|
18426
|
+
if (!state.agentConfigChangedAt)
|
|
18427
|
+
state.agentConfigChangedAt = {};
|
|
18428
|
+
for (const [slug, reasons] of Object.entries(restartReasons)) {
|
|
18429
|
+
state.agentConfigChangedAt[slug] = { changedAt: state.lastSyncAt, reasons };
|
|
18430
|
+
}
|
|
18431
|
+
}
|
|
18432
|
+
if (hasRealSkillChange(skillChanges) || builtInSkillsChanged || mcpChanged || settingsChangedSlugs.length > 0) {
|
|
18433
|
+
state.lastSyncChange = {
|
|
18434
|
+
at: state.lastSyncAt,
|
|
18435
|
+
skills: skillChanges,
|
|
18436
|
+
builtInSkillsChanged,
|
|
18437
|
+
mcp: { changed: mcpChanged, added: mcpAdded, removed: mcpRemoved },
|
|
18438
|
+
settingsSlugs: settingsChangedSlugs
|
|
18439
|
+
};
|
|
18440
|
+
}
|
|
18441
|
+
if (mcpFailedAdapters.size === 0)
|
|
18442
|
+
state.lastMcpEntriesHash = mcpEntriesHash;
|
|
18443
|
+
if (skillFailedAdapters.size === 0)
|
|
18444
|
+
state.builtInSkillsHash = builtInSkillsHash;
|
|
18445
|
+
const nameBySlug = new Map(adapters.map((a) => [a.slug, a.name]));
|
|
18446
|
+
const restartChanges = Object.entries(restartReasons).map(([slug, reasons]) => ({
|
|
18447
|
+
name: nameBySlug.get(slug) ?? slug,
|
|
18448
|
+
reasons
|
|
18449
|
+
}));
|
|
18450
|
+
console.log(` Restart-worthy changes: ${formatRestartChanges(restartChanges)}`);
|
|
18141
18451
|
const mergedHashes = { ...state.skillHashes || {} };
|
|
18142
18452
|
for (const [name, hash] of Object.entries(newHashes)) {
|
|
18143
18453
|
mergedHashes[name] = hash;
|
|
@@ -18159,25 +18469,29 @@ async function syncFromState(state, statePath2, credentials, opts) {
|
|
|
18159
18469
|
if (opts.verbose)
|
|
18160
18470
|
printTelemetryVerbose(telemetry);
|
|
18161
18471
|
await client.reportTelemetry(state.workspaceId, telemetry.events);
|
|
18162
|
-
if (telemetry.healthReported)
|
|
18472
|
+
if (telemetry.healthReported) {
|
|
18163
18473
|
state.lastHealthReportAt = new Date().toISOString();
|
|
18164
|
-
|
|
18165
|
-
|
|
18166
|
-
];
|
|
18167
|
-
writeFileSync29(statePath2, JSON.stringify(state, null, 2));
|
|
18474
|
+
writeFileSync29(statePath2, JSON.stringify(state, null, 2));
|
|
18475
|
+
}
|
|
18168
18476
|
} catch {}
|
|
18169
|
-
const
|
|
18170
|
-
|
|
18171
|
-
|
|
18172
|
-
summaryParts.push(`${summary.
|
|
18173
|
-
|
|
18174
|
-
|
|
18175
|
-
summaryParts.push(`${summary.
|
|
18176
|
-
|
|
18177
|
-
|
|
18178
|
-
|
|
18179
|
-
|
|
18477
|
+
const failedNote = summary.adaptersFailed > 0 ? ` (${summary.adaptersFailed} failed)` : "";
|
|
18478
|
+
if (isVerbose()) {
|
|
18479
|
+
const summaryParts = [];
|
|
18480
|
+
summaryParts.push(`${summary.adaptersProcessed} adapter${summary.adaptersProcessed === 1 ? "" : "s"}`);
|
|
18481
|
+
if (summary.adaptersFailed > 0)
|
|
18482
|
+
summaryParts.push(`${summary.adaptersFailed} failed`);
|
|
18483
|
+
summaryParts.push(`${summary.skillWrites} skill write${summary.skillWrites === 1 ? "" : "s"} (${summary.skillFilesWritten} files)`);
|
|
18484
|
+
if (summary.mcpServerWrites > 0)
|
|
18485
|
+
summaryParts.push(`${summary.mcpServerWrites} MCP config write${summary.mcpServerWrites === 1 ? "" : "s"}`);
|
|
18486
|
+
if (summary.hookInstallCalls > 0)
|
|
18487
|
+
summaryParts.push(`${summary.hookInstallCalls} hook install${summary.hookInstallCalls === 1 ? "" : "s"}`);
|
|
18488
|
+
summaryParts.push(`${summary.instructionHintWrites} instruction hint write${summary.instructionHintWrites === 1 ? "" : "s"}`);
|
|
18489
|
+
console.log(`
|
|
18180
18490
|
Summary: ${summaryParts.join(", ")}.`);
|
|
18491
|
+
} else {
|
|
18492
|
+
console.log(`
|
|
18493
|
+
${summary.adaptersProcessed} agent${summary.adaptersProcessed === 1 ? "" : "s"} synced${failedNote}.`);
|
|
18494
|
+
}
|
|
18181
18495
|
try {
|
|
18182
18496
|
const cadence = loadCadenceState();
|
|
18183
18497
|
const lastMs = cadence.lastReflectedAt ? new Date(cadence.lastReflectedAt).getTime() : 0;
|
|
@@ -18370,7 +18684,8 @@ Syncing workspace data...
|
|
|
18370
18684
|
dryRun: false,
|
|
18371
18685
|
pullOnly: true,
|
|
18372
18686
|
yes: true,
|
|
18373
|
-
prefer: "remote"
|
|
18687
|
+
prefer: "remote",
|
|
18688
|
+
initialSetup: true
|
|
18374
18689
|
});
|
|
18375
18690
|
}
|
|
18376
18691
|
console.log("\nSetup complete. Run `runwork sync` anytime to refresh.");
|
package/package.json
CHANGED