@yemi33/minions 0.1.2179 → 0.1.2180
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/README.md +7 -5
- package/bin/minions.js +15 -6
- package/dashboard/js/memory-panel.js +62 -0
- package/dashboard/js/refresh.js +18 -0
- package/dashboard/js/render-other.js +142 -1
- package/dashboard/js/render-work-items.js +18 -1
- package/dashboard/js/settings.js +23 -0
- package/dashboard/pages/engine-memory-panel.html +7 -0
- package/dashboard/pages/tools.html +8 -0
- package/dashboard.js +466 -3
- package/docs/diagnostics-memory.md +446 -0
- package/docs/harness-propagation.md +273 -0
- package/docs/human-vs-automated.md +1 -1
- package/docs/runtime-adapters.md +5 -0
- package/engine/cli.js +24 -5
- package/engine/preflight.js +265 -0
- package/engine/queries.js +192 -15
- package/engine/runtimes/claude.js +36 -0
- package/engine/runtimes/codex.js +19 -0
- package/engine/runtimes/copilot.js +27 -36
- package/engine/shared.js +277 -13
- package/engine/spawn-agent.js +178 -12
- package/engine.js +232 -3
- package/package.json +1 -1
package/engine/queries.js
CHANGED
|
@@ -1041,6 +1041,121 @@ function _skillsCacheKeyFor(config, homeDir) {
|
|
|
1041
1041
|
return JSON.stringify({ homeDir, projects });
|
|
1042
1042
|
}
|
|
1043
1043
|
|
|
1044
|
+
// ── Harness helpers (P-f5a91c30) ────────────────────────────────────────────
|
|
1045
|
+
//
|
|
1046
|
+
// `getUserHarnesses(homeDir)` and `getProjectHarnesses(project)` are the
|
|
1047
|
+
// shared aggregation layer over the per-runtime adapter methods
|
|
1048
|
+
// `getSkillRoots`, `getCommandRoots`, and `getMcpConfigPaths`. They iterate
|
|
1049
|
+
// every registered runtime, union the contributions, and dedup by absolute
|
|
1050
|
+
// path so the same dir (e.g. `~/.agents/skills` — exposed by Codex + Copilot)
|
|
1051
|
+
// only surfaces once. Each surviving entry tracks which runtimes contributed
|
|
1052
|
+
// it via `runtimes: [name, ...]` so diagnostics can show provenance.
|
|
1053
|
+
//
|
|
1054
|
+
// Consumers: dashboard `/api/status` skills/MCP slices (via collectSkillFiles
|
|
1055
|
+
// / collectCommandFiles refactor), the planned `/api/harness/diagnostics`
|
|
1056
|
+
// endpoint, `engine.js#spawnAgent` for `computeAddDirs` project-local asset
|
|
1057
|
+
// propagation, and `minions doctor --harness` (preflight.js still calls the
|
|
1058
|
+
// per-adapter methods directly there to label by adapter — that's intentional).
|
|
1059
|
+
//
|
|
1060
|
+
// Return shape:
|
|
1061
|
+
// { skills: HarnessEntry[], commands: HarnessEntry[], mcps: HarnessEntry[] }
|
|
1062
|
+
// where HarnessEntry =
|
|
1063
|
+
// { dir|file: string, scope: string, projectName?: string, runtimes: string[] }
|
|
1064
|
+
// `skills`/`commands` use `dir`; `mcps` uses `file`. Both keys are present on
|
|
1065
|
+
// the same shape for ergonomic destructure.
|
|
1066
|
+
|
|
1067
|
+
function _addHarnessEntry(out, byPath, runtimeName, pathValue, payload) {
|
|
1068
|
+
if (!pathValue) return;
|
|
1069
|
+
const abs = path.resolve(pathValue);
|
|
1070
|
+
const existing = byPath.get(abs);
|
|
1071
|
+
if (existing) {
|
|
1072
|
+
if (!existing.runtimes.includes(runtimeName)) existing.runtimes.push(runtimeName);
|
|
1073
|
+
return;
|
|
1074
|
+
}
|
|
1075
|
+
const entry = { ...payload, runtimes: [runtimeName] };
|
|
1076
|
+
byPath.set(abs, entry);
|
|
1077
|
+
out.push(entry);
|
|
1078
|
+
}
|
|
1079
|
+
|
|
1080
|
+
function _collectHarnessesScope(scopeFilter, runtimeOpts) {
|
|
1081
|
+
const skills = [];
|
|
1082
|
+
const commands = [];
|
|
1083
|
+
const mcps = [];
|
|
1084
|
+
const skillsByPath = new Map();
|
|
1085
|
+
const commandsByPath = new Map();
|
|
1086
|
+
const mcpsByPath = new Map();
|
|
1087
|
+
let runtimeNames = [];
|
|
1088
|
+
try {
|
|
1089
|
+
const { listRuntimes, resolveRuntime } = require('./runtimes');
|
|
1090
|
+
runtimeNames = listRuntimes();
|
|
1091
|
+
for (const runtimeName of runtimeNames) {
|
|
1092
|
+
let runtime;
|
|
1093
|
+
try { runtime = resolveRuntime(runtimeName); } catch { continue; }
|
|
1094
|
+
if (!runtime) continue;
|
|
1095
|
+
|
|
1096
|
+
if (typeof runtime.getSkillRoots === 'function') {
|
|
1097
|
+
let roots = [];
|
|
1098
|
+
try { roots = runtime.getSkillRoots(runtimeOpts) || []; } catch { roots = []; }
|
|
1099
|
+
for (const root of roots) {
|
|
1100
|
+
if (!root || !root.dir) continue;
|
|
1101
|
+
if (!scopeFilter(root)) continue;
|
|
1102
|
+
_addHarnessEntry(skills, skillsByPath, runtimeName, root.dir, {
|
|
1103
|
+
dir: path.resolve(root.dir),
|
|
1104
|
+
scope: root.scope,
|
|
1105
|
+
projectName: root.projectName,
|
|
1106
|
+
});
|
|
1107
|
+
}
|
|
1108
|
+
}
|
|
1109
|
+
|
|
1110
|
+
if (typeof runtime.getCommandRoots === 'function') {
|
|
1111
|
+
let roots = [];
|
|
1112
|
+
try { roots = runtime.getCommandRoots(runtimeOpts) || []; } catch { roots = []; }
|
|
1113
|
+
for (const root of roots) {
|
|
1114
|
+
if (!root || !root.dir) continue;
|
|
1115
|
+
if (!scopeFilter(root)) continue;
|
|
1116
|
+
_addHarnessEntry(commands, commandsByPath, runtimeName, root.dir, {
|
|
1117
|
+
dir: path.resolve(root.dir),
|
|
1118
|
+
scope: root.scope,
|
|
1119
|
+
projectName: root.projectName,
|
|
1120
|
+
});
|
|
1121
|
+
}
|
|
1122
|
+
}
|
|
1123
|
+
|
|
1124
|
+
if (typeof runtime.getMcpConfigPaths === 'function') {
|
|
1125
|
+
let entries = [];
|
|
1126
|
+
try { entries = runtime.getMcpConfigPaths(runtimeOpts) || []; } catch { entries = []; }
|
|
1127
|
+
for (const entry of entries) {
|
|
1128
|
+
if (!entry || !entry.file) continue;
|
|
1129
|
+
if (!scopeFilter(entry)) continue;
|
|
1130
|
+
_addHarnessEntry(mcps, mcpsByPath, runtimeName, entry.file, {
|
|
1131
|
+
file: path.resolve(entry.file),
|
|
1132
|
+
scope: entry.scope,
|
|
1133
|
+
projectName: entry.projectName,
|
|
1134
|
+
});
|
|
1135
|
+
}
|
|
1136
|
+
}
|
|
1137
|
+
}
|
|
1138
|
+
} catch { /* runtime registry optional in partial installs */ }
|
|
1139
|
+
return { skills, commands, mcps };
|
|
1140
|
+
}
|
|
1141
|
+
|
|
1142
|
+
function getUserHarnesses(homeDir = os.homedir()) {
|
|
1143
|
+
return _collectHarnessesScope(
|
|
1144
|
+
(entry) => entry.scope !== 'project',
|
|
1145
|
+
{ homeDir },
|
|
1146
|
+
);
|
|
1147
|
+
}
|
|
1148
|
+
|
|
1149
|
+
function getProjectHarnesses(project) {
|
|
1150
|
+
if (!project || !project.localPath) {
|
|
1151
|
+
return { skills: [], commands: [], mcps: [] };
|
|
1152
|
+
}
|
|
1153
|
+
return _collectHarnessesScope(
|
|
1154
|
+
(entry) => entry.scope === 'project',
|
|
1155
|
+
{ homeDir: os.homedir(), project },
|
|
1156
|
+
);
|
|
1157
|
+
}
|
|
1158
|
+
|
|
1044
1159
|
function collectSkillFiles(config) {
|
|
1045
1160
|
const now = Date.now();
|
|
1046
1161
|
config = config || getConfig();
|
|
@@ -1069,18 +1184,22 @@ function collectSkillFiles(config) {
|
|
|
1069
1184
|
projectName: root.projectName,
|
|
1070
1185
|
});
|
|
1071
1186
|
}
|
|
1072
|
-
for (const project of projects) {
|
|
1073
|
-
if (!project.localPath) continue;
|
|
1074
|
-
for (const root of runtime.getSkillRoots({ homeDir, project })) {
|
|
1075
|
-
if (root.scope !== 'project') continue;
|
|
1076
|
-
_collectNativeSkillsDir(root.dir, root.scope, seenFor(root.scope, root.projectName), skillFiles, {
|
|
1077
|
-
projectName: root.projectName,
|
|
1078
|
-
});
|
|
1079
|
-
}
|
|
1080
|
-
}
|
|
1081
1187
|
}
|
|
1082
1188
|
} catch { /* runtime registry optional in partial installs */ }
|
|
1083
1189
|
|
|
1190
|
+
// Project-scope skill roots: aggregate across runtimes via the shared
|
|
1191
|
+
// getProjectHarnesses helper so dedup-by-absolute-path happens once
|
|
1192
|
+
// (e.g. `<repo>/.agents/skills` is exposed by both Codex and Copilot).
|
|
1193
|
+
for (const project of projects) {
|
|
1194
|
+
if (!project.localPath) continue;
|
|
1195
|
+
for (const root of getProjectHarnesses(project).skills) {
|
|
1196
|
+
if (root.scope !== 'project') continue;
|
|
1197
|
+
_collectNativeSkillsDir(root.dir, root.scope, seenFor(root.scope, root.projectName), skillFiles, {
|
|
1198
|
+
projectName: root.projectName,
|
|
1199
|
+
});
|
|
1200
|
+
}
|
|
1201
|
+
}
|
|
1202
|
+
|
|
1084
1203
|
// 1b. Installed plugin skills: ~/.claude/plugins/installed_plugins.json
|
|
1085
1204
|
// Plugins use commands/*.md and/or skills/<name>/SKILL.md and/or skills/SKILL.md
|
|
1086
1205
|
try {
|
|
@@ -1247,7 +1366,10 @@ function collectCommandFiles(config) {
|
|
|
1247
1366
|
function addCommandDir(rootDir, scope, extra = {}) {
|
|
1248
1367
|
const root = path.resolve(rootDir);
|
|
1249
1368
|
for (const cmd of _collectMarkdownFilesRecursive(root)) {
|
|
1250
|
-
|
|
1369
|
+
// Dedup is by absolute path + relative-file so runtimes that share a
|
|
1370
|
+
// dir (none today for commands, but the shape mirrors collectSkillFiles)
|
|
1371
|
+
// don't double-list.
|
|
1372
|
+
const key = `${root}:${cmd.rel}`;
|
|
1251
1373
|
if (seen.has(key)) continue;
|
|
1252
1374
|
seen.add(key);
|
|
1253
1375
|
const commandName = cmd.rel.replace(/\.md$/, '').replace(/\\/g, '/');
|
|
@@ -1255,7 +1377,20 @@ function collectCommandFiles(config) {
|
|
|
1255
1377
|
}
|
|
1256
1378
|
}
|
|
1257
1379
|
|
|
1258
|
-
|
|
1380
|
+
// User-scope command roots from every runtime adapter (was hard-coded to
|
|
1381
|
+
// `~/.claude/commands`; Copilot exposes `~/.copilot/commands` and Codex
|
|
1382
|
+
// currently returns nothing — adapters fall in/out as their CLIs evolve).
|
|
1383
|
+
try {
|
|
1384
|
+
const { listRuntimes, resolveRuntime } = require('./runtimes');
|
|
1385
|
+
for (const runtimeName of listRuntimes()) {
|
|
1386
|
+
const runtime = resolveRuntime(runtimeName);
|
|
1387
|
+
if (typeof runtime.getCommandRoots !== 'function') continue;
|
|
1388
|
+
for (const root of runtime.getCommandRoots({ homeDir })) {
|
|
1389
|
+
if (!root || !root.dir || root.scope === 'project') continue;
|
|
1390
|
+
addCommandDir(root.dir, root.scope);
|
|
1391
|
+
}
|
|
1392
|
+
}
|
|
1393
|
+
} catch { /* runtime registry optional in partial installs */ }
|
|
1259
1394
|
|
|
1260
1395
|
try {
|
|
1261
1396
|
const pluginsFile = path.join(homeDir, '.claude', 'plugins', 'installed_plugins.json');
|
|
@@ -1269,9 +1404,15 @@ function collectCommandFiles(config) {
|
|
|
1269
1404
|
}
|
|
1270
1405
|
} catch { /* optional */ }
|
|
1271
1406
|
|
|
1407
|
+
// Project-scope command roots aggregated across runtimes via the shared
|
|
1408
|
+
// getProjectHarnesses helper. Dedup-by-absolute-path is handled inside the
|
|
1409
|
+
// helper so adapters that name the same dir don't double-list here.
|
|
1272
1410
|
for (const project of getProjects(config)) {
|
|
1273
1411
|
if (!project.localPath) continue;
|
|
1274
|
-
|
|
1412
|
+
for (const root of getProjectHarnesses(project).commands) {
|
|
1413
|
+
if (!root || !root.dir || root.scope !== 'project') continue;
|
|
1414
|
+
addCommandDir(root.dir, root.scope, { projectName: root.projectName || project.name });
|
|
1415
|
+
}
|
|
1275
1416
|
}
|
|
1276
1417
|
|
|
1277
1418
|
return commandFiles;
|
|
@@ -1285,10 +1426,42 @@ function _commandTitle(content, fallback) {
|
|
|
1285
1426
|
return fallback;
|
|
1286
1427
|
}
|
|
1287
1428
|
|
|
1429
|
+
// Dashboard-facing wrapper over `collectCommandFiles`. Returns a normalized
|
|
1430
|
+
// per-entry shape the tools.html "Slash commands" panel + `/api/status`
|
|
1431
|
+
// slow-state slice render directly. Includes the resolved title (from YAML
|
|
1432
|
+
// `description:` frontmatter or first `# heading`) and a `dir`-with-forward-
|
|
1433
|
+
// slashes copy so the renderer can build display strings without re-walking
|
|
1434
|
+
// the file on the client. Sort matches getCommandIndex (project → plugin →
|
|
1435
|
+
// per-runtime user) so the panel and the agent-facing index stay in lockstep.
|
|
1436
|
+
function getCommands(config) {
|
|
1437
|
+
try {
|
|
1438
|
+
const commandFiles = collectCommandFiles(config).sort((a, b) => {
|
|
1439
|
+
const priority = { project: 0, plugin: 1, 'claude-code': 2, copilot: 3, codex: 4 };
|
|
1440
|
+
return (priority[a.scope] ?? 9) - (priority[b.scope] ?? 9)
|
|
1441
|
+
|| String(a.projectName || '').localeCompare(String(b.projectName || ''))
|
|
1442
|
+
|| String(a.commandName || '').localeCompare(String(b.commandName || ''));
|
|
1443
|
+
});
|
|
1444
|
+
return commandFiles.map(({ file, dir, rel, scope, commandName, projectName, pluginName }) => {
|
|
1445
|
+
const content = safeRead(path.join(dir, file)) || '';
|
|
1446
|
+
return {
|
|
1447
|
+
commandName,
|
|
1448
|
+
title: _commandTitle(content, commandName),
|
|
1449
|
+
file,
|
|
1450
|
+
rel: rel || file,
|
|
1451
|
+
dir: dir.replace(/\\/g, '/'),
|
|
1452
|
+
scope,
|
|
1453
|
+
projectName: projectName || null,
|
|
1454
|
+
pluginName: pluginName || null,
|
|
1455
|
+
};
|
|
1456
|
+
});
|
|
1457
|
+
} catch { return []; }
|
|
1458
|
+
}
|
|
1459
|
+
|
|
1288
1460
|
function getCommandIndex(config) {
|
|
1289
1461
|
try {
|
|
1290
1462
|
const commandFiles = collectCommandFiles(config).sort((a, b) => {
|
|
1291
|
-
|
|
1463
|
+
// Lower numbers sort first. Unknown scopes (future runtimes) fall to 9.
|
|
1464
|
+
const priority = { project: 0, plugin: 1, 'claude-code': 2, copilot: 3 };
|
|
1292
1465
|
return (priority[a.scope] ?? 9) - (priority[b.scope] ?? 9)
|
|
1293
1466
|
|| String(a.projectName || '').localeCompare(String(b.projectName || ''))
|
|
1294
1467
|
|| String(a.commandName || '').localeCompare(String(b.commandName || ''));
|
|
@@ -1305,7 +1478,7 @@ function getCommandIndex(config) {
|
|
|
1305
1478
|
? `project:${projectName || 'unknown'}`
|
|
1306
1479
|
: scope === 'plugin'
|
|
1307
1480
|
? `plugin:${pluginName || 'unknown'}`
|
|
1308
|
-
: '
|
|
1481
|
+
: scope || 'unknown';
|
|
1309
1482
|
index += `- \`/${commandName}\` (${label}) — ${title}\n`;
|
|
1310
1483
|
index += ` File: \`${dir.replace(/\\/g, '/')}/${f}\`\n`;
|
|
1311
1484
|
}
|
|
@@ -2737,7 +2910,11 @@ module.exports = {
|
|
|
2737
2910
|
collectSkillFiles, getSkills, getSkillIndex, invalidateSkillsCache,
|
|
2738
2911
|
|
|
2739
2912
|
// Commands
|
|
2740
|
-
collectCommandFiles, getCommandIndex,
|
|
2913
|
+
collectCommandFiles, getCommandIndex, getCommands,
|
|
2914
|
+
|
|
2915
|
+
// Harness aggregation (P-f5a91c30) — shared union over per-runtime
|
|
2916
|
+
// getSkillRoots / getCommandRoots / getMcpConfigPaths.
|
|
2917
|
+
getUserHarnesses, getProjectHarnesses,
|
|
2741
2918
|
|
|
2742
2919
|
// Knowledge base
|
|
2743
2920
|
getKnowledgeBaseEntries, getKnowledgeBaseEntriesSnapshot, getKnowledgeBaseIndex,
|
|
@@ -800,6 +800,40 @@ function getSkillWriteTargets({ homeDir = os.homedir(), project = null } = {}) {
|
|
|
800
800
|
return targets;
|
|
801
801
|
}
|
|
802
802
|
|
|
803
|
+
// Slash-command roots. Mirrors getSkillRoots' shape so consumers can iterate
|
|
804
|
+
// runtimes generically. Plugin-installed command dirs are NOT enumerated here
|
|
805
|
+
// — those are discovered dynamically from `~/.claude/plugins/installed_plugins.json`
|
|
806
|
+
// in engine/queries.js#collectCommandFiles and stay there because they require
|
|
807
|
+
// a registry read, not a fixed dir.
|
|
808
|
+
function getCommandRoots({ homeDir = os.homedir(), project = null } = {}) {
|
|
809
|
+
const roots = [
|
|
810
|
+
{ dir: path.join(homeDir, '.claude', 'commands'), scope: 'claude-code' },
|
|
811
|
+
];
|
|
812
|
+
if (project?.localPath) {
|
|
813
|
+
const projectName = project.name || path.basename(project.localPath);
|
|
814
|
+
roots.push(
|
|
815
|
+
{ dir: path.join(project.localPath, '.claude', 'commands'), scope: 'project', projectName },
|
|
816
|
+
);
|
|
817
|
+
}
|
|
818
|
+
return roots;
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
// MCP config files this runtime reads. The user-scope file is `~/.claude.json`
|
|
822
|
+
// (which holds more than mcpServers but the engine only reads the mcpServers
|
|
823
|
+
// slice). Project-scope `<root>/.mcp.json` is a cross-runtime convention —
|
|
824
|
+
// Claude and Copilot both read it — so it appears in both adapters; consumers
|
|
825
|
+
// that aggregate across runtimes should dedupe by absolute path.
|
|
826
|
+
function getMcpConfigPaths({ homeDir = os.homedir(), project = null } = {}) {
|
|
827
|
+
const paths = [
|
|
828
|
+
{ file: path.join(homeDir, '.claude.json'), scope: 'claude-code' },
|
|
829
|
+
];
|
|
830
|
+
if (project?.localPath) {
|
|
831
|
+
const projectName = project.name || path.basename(project.localPath);
|
|
832
|
+
paths.push({ file: path.join(project.localPath, '.mcp.json'), scope: 'project', projectName });
|
|
833
|
+
}
|
|
834
|
+
return paths;
|
|
835
|
+
}
|
|
836
|
+
|
|
803
837
|
// Heuristic: does `model` look like a Claude model identifier? Powers the
|
|
804
838
|
// preflight "stale model after CLI switch" warning in cli.js. Returning false
|
|
805
839
|
// means "this looks wrong for Claude" — gpt-5.4 / o3-* / codex etc. Keep this
|
|
@@ -828,6 +862,8 @@ module.exports = {
|
|
|
828
862
|
getUserAssetDirs,
|
|
829
863
|
getSkillRoots,
|
|
830
864
|
getSkillWriteTargets,
|
|
865
|
+
getCommandRoots,
|
|
866
|
+
getMcpConfigPaths,
|
|
831
867
|
getResumeSessionId,
|
|
832
868
|
saveSession,
|
|
833
869
|
detectPermissionGate,
|
package/engine/runtimes/codex.js
CHANGED
|
@@ -395,6 +395,23 @@ function getSkillWriteTargets({ homeDir = os.homedir(), project = null } = {}) {
|
|
|
395
395
|
return targets;
|
|
396
396
|
}
|
|
397
397
|
|
|
398
|
+
// Slash-command roots. Codex CLI does not yet surface user-defined commands
|
|
399
|
+
// (per docs/harness-propagation.md adapter table). Return empty to satisfy the
|
|
400
|
+
// contract — consumers iterate runtimes and handle empty arrays gracefully.
|
|
401
|
+
// Replace with real paths once the CLI ships an official command discovery
|
|
402
|
+
// surface so the engine automatically picks them up.
|
|
403
|
+
function getCommandRoots({ homeDir = os.homedir(), project = null } = {}) {
|
|
404
|
+
return [];
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
// MCP config files. Codex MCP wiring is not yet a stable contract (see
|
|
408
|
+
// harness-propagation table). Return empty until the CLI ships an official
|
|
409
|
+
// config path; consumers that aggregate across runtimes will simply skip
|
|
410
|
+
// codex's contribution.
|
|
411
|
+
function getMcpConfigPaths({ homeDir = os.homedir(), project = null } = {}) {
|
|
412
|
+
return [];
|
|
413
|
+
}
|
|
414
|
+
|
|
398
415
|
function getResumeSessionId({ agentId, branchName, agentsDir, maxAgeMs = 2 * 60 * 60 * 1000, logger = console } = {}) {
|
|
399
416
|
if (!agentId || agentId.startsWith('temp-') || !agentsDir) return null;
|
|
400
417
|
const sessionPath = path.join(agentsDir, agentId, 'session.json');
|
|
@@ -835,6 +852,8 @@ module.exports = {
|
|
|
835
852
|
getUserAssetDirs,
|
|
836
853
|
getSkillRoots,
|
|
837
854
|
getSkillWriteTargets,
|
|
855
|
+
getCommandRoots,
|
|
856
|
+
getMcpConfigPaths,
|
|
838
857
|
getResumeSessionId,
|
|
839
858
|
saveSession,
|
|
840
859
|
detectPermissionGate,
|
|
@@ -526,42 +526,6 @@ function buildSpawnFlags(opts = {}) {
|
|
|
526
526
|
return flags;
|
|
527
527
|
}
|
|
528
528
|
|
|
529
|
-
function getUserAssetDirs({ homeDir = os.homedir() } = {}) {
|
|
530
|
-
return [
|
|
531
|
-
path.join(homeDir, '.copilot'),
|
|
532
|
-
path.join(homeDir, '.agents'),
|
|
533
|
-
];
|
|
534
|
-
}
|
|
535
|
-
|
|
536
|
-
function getSkillRoots({ homeDir = os.homedir(), project = null } = {}) {
|
|
537
|
-
const roots = [
|
|
538
|
-
{ scope: 'copilot', dir: path.join(homeDir, '.copilot', 'skills') },
|
|
539
|
-
{ scope: 'agent-skill', dir: path.join(homeDir, '.agents', 'skills') },
|
|
540
|
-
];
|
|
541
|
-
if (project?.localPath) {
|
|
542
|
-
roots.push(
|
|
543
|
-
{
|
|
544
|
-
scope: 'project',
|
|
545
|
-
projectName: project.name,
|
|
546
|
-
dir: path.join(project.localPath, '.github', 'skills'),
|
|
547
|
-
},
|
|
548
|
-
{
|
|
549
|
-
scope: 'project',
|
|
550
|
-
projectName: project.name,
|
|
551
|
-
dir: path.join(project.localPath, '.agents', 'skills'),
|
|
552
|
-
},
|
|
553
|
-
);
|
|
554
|
-
}
|
|
555
|
-
return roots;
|
|
556
|
-
}
|
|
557
|
-
|
|
558
|
-
function getSkillWriteTargets({ homeDir = os.homedir(), project = null } = {}) {
|
|
559
|
-
return {
|
|
560
|
-
personal: path.join(homeDir, '.copilot', 'skills'),
|
|
561
|
-
project: project?.localPath ? path.join(project.localPath, '.github', 'skills') : null,
|
|
562
|
-
};
|
|
563
|
-
}
|
|
564
|
-
|
|
565
529
|
// Stamped into every session.json this adapter writes so the pre-spawn resume
|
|
566
530
|
// path can detect "session was produced by a different runtime" — Copilot
|
|
567
531
|
// rejects Claude session IDs (and vice versa) with "No conversation found",
|
|
@@ -1297,6 +1261,31 @@ function getSkillWriteTargets({ homeDir = os.homedir(), project = null } = {}) {
|
|
|
1297
1261
|
return targets;
|
|
1298
1262
|
}
|
|
1299
1263
|
|
|
1264
|
+
// Slash-command roots. Copilot CLI's project-scope command discovery is not
|
|
1265
|
+
// yet a confirmed contract (the harness-propagation doc annotates the user
|
|
1266
|
+
// path as "probe"); leaving project rows empty matches that. Add them here
|
|
1267
|
+
// once the CLI ships an official path so consumers automatically pick them up.
|
|
1268
|
+
function getCommandRoots({ homeDir = os.homedir(), project = null } = {}) {
|
|
1269
|
+
return [
|
|
1270
|
+
{ dir: path.join(homeDir, '.copilot', 'commands'), scope: 'copilot' },
|
|
1271
|
+
];
|
|
1272
|
+
}
|
|
1273
|
+
|
|
1274
|
+
// MCP config files. The user-scope file is `~/.copilot/mcp-config.json`.
|
|
1275
|
+
// Project-scope `<root>/.mcp.json` is the cross-runtime convention Copilot
|
|
1276
|
+
// also honors; it also appears in claude.getMcpConfigPaths, so consumers that
|
|
1277
|
+
// aggregate across runtimes should dedupe by absolute path.
|
|
1278
|
+
function getMcpConfigPaths({ homeDir = os.homedir(), project = null } = {}) {
|
|
1279
|
+
const paths = [
|
|
1280
|
+
{ file: path.join(homeDir, '.copilot', 'mcp-config.json'), scope: 'copilot' },
|
|
1281
|
+
];
|
|
1282
|
+
if (project?.localPath) {
|
|
1283
|
+
const projectName = project.name || path.basename(project.localPath);
|
|
1284
|
+
paths.push({ file: path.join(project.localPath, '.mcp.json'), scope: 'project', projectName });
|
|
1285
|
+
}
|
|
1286
|
+
return paths;
|
|
1287
|
+
}
|
|
1288
|
+
|
|
1300
1289
|
module.exports = {
|
|
1301
1290
|
name: 'copilot',
|
|
1302
1291
|
capabilities,
|
|
@@ -1313,6 +1302,8 @@ module.exports = {
|
|
|
1313
1302
|
getUserAssetDirs,
|
|
1314
1303
|
getSkillRoots,
|
|
1315
1304
|
getSkillWriteTargets,
|
|
1305
|
+
getCommandRoots,
|
|
1306
|
+
getMcpConfigPaths,
|
|
1316
1307
|
getResumeSessionId,
|
|
1317
1308
|
saveSession,
|
|
1318
1309
|
detectPermissionGate,
|