@yemi33/minions 0.1.2078 → 0.1.2079
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/dashboard.js +9 -3
- package/engine/shared.js +110 -0
- package/package.json +1 -1
- package/prompts/cc-system.md +13 -0
package/dashboard.js
CHANGED
|
@@ -2810,9 +2810,15 @@ function _ccTurnEntryToActionType(kind) {
|
|
|
2810
2810
|
}
|
|
2811
2811
|
}
|
|
2812
2812
|
|
|
2813
|
-
// Hash the system prompt so we can detect changes and invalidate stale sessions
|
|
2814
|
-
|
|
2815
|
-
|
|
2813
|
+
// Hash the system prompt so we can detect changes and invalidate stale sessions.
|
|
2814
|
+
// Hashing the RENDERED prompt (not the raw template) means changes to the
|
|
2815
|
+
// substituted content — most importantly the dynamic {{available_skills}}
|
|
2816
|
+
// registry built from ~/.copilot/skills/ + installed-plugins — also invalidate
|
|
2817
|
+
// pooled sessions on dashboard restart. Without this, installing or removing
|
|
2818
|
+
// a skill would leave warm sessions glued to the old registry until they aged
|
|
2819
|
+
// out naturally.
|
|
2820
|
+
const _ccPromptHash = require('crypto').createHash('md5').update(CC_STATIC_SYSTEM_PROMPT).digest('hex').slice(0, 8);
|
|
2821
|
+
const _docChatPromptHash = require('crypto').createHash('md5').update(DOC_CHAT_SYSTEM_PROMPT).digest('hex').slice(0, 8);
|
|
2816
2822
|
|
|
2817
2823
|
function _sessionExpired(lastActiveAt, ttlMs) {
|
|
2818
2824
|
if (!lastActiveAt || !ttlMs) return false;
|
package/engine/shared.js
CHANGED
|
@@ -3455,6 +3455,106 @@ function describeCcProtectedPaths(liveRoot) {
|
|
|
3455
3455
|
return `READ ONLY in the live checkout at \`${norm}\` — never write/edit: ${basenames}, ${globs}, ${prefixes}. This rule is path-scoped, not basename-scoped. Files with the same basename inside an isolated agent worktree (e.g. \`{worktreeRoot}/W-<id>/dashboard.js\`) are NOT protected — agents working in their own worktrees may edit any repository source the work item requires.`;
|
|
3456
3456
|
}
|
|
3457
3457
|
|
|
3458
|
+
// Enumerate Copilot CLI skills from the conventional install layout so the CC
|
|
3459
|
+
// system prompt can mirror the vanilla runtime's <available_skills> registry.
|
|
3460
|
+
//
|
|
3461
|
+
// The CC system prompt fully replaces Copilot CLI's default — which means the
|
|
3462
|
+
// default's auto-injected <available_skills> block disappears, and the agent
|
|
3463
|
+
// has no idea what skills it has. It then falls back to grep/glob against
|
|
3464
|
+
// well-known paths (typically `.claude/skills/` because the underlying model
|
|
3465
|
+
// is Claude and that path dominates its training data). This helper rebuilds
|
|
3466
|
+
// the registry from the actual Copilot install dirs so the prompt can tell
|
|
3467
|
+
// the agent the truth.
|
|
3468
|
+
//
|
|
3469
|
+
// Locations scanned (matches Copilot CLI's discovery rules):
|
|
3470
|
+
// - ~/.copilot/skills/<name>/SKILL.md → location="user"
|
|
3471
|
+
// - ~/.copilot/installed-plugins/<mkt>/<plugin>/skills/<name>/SKILL.md
|
|
3472
|
+
// → location="plugin"
|
|
3473
|
+
//
|
|
3474
|
+
// Tolerates lowercase `skill.md` (Linux-style installs) and missing dirs
|
|
3475
|
+
// (returns an empty array rather than throwing). User-level entries shadow
|
|
3476
|
+
// plugin entries with the same name (mirrors Copilot CLI's user-wins rule).
|
|
3477
|
+
function listCopilotSkills(opts) {
|
|
3478
|
+
const homeDir = (opts && typeof opts.homeDir === 'string') ? opts.homeDir : os.homedir();
|
|
3479
|
+
const copilotRoot = path.join(homeDir, '.copilot');
|
|
3480
|
+
const collected = [];
|
|
3481
|
+
|
|
3482
|
+
const readSkillDir = (dir, fallbackName, location) => {
|
|
3483
|
+
let content;
|
|
3484
|
+
try {
|
|
3485
|
+
content = fs.readFileSync(path.join(dir, 'SKILL.md'), 'utf8');
|
|
3486
|
+
} catch {
|
|
3487
|
+
try { content = fs.readFileSync(path.join(dir, 'skill.md'), 'utf8'); }
|
|
3488
|
+
catch { return null; }
|
|
3489
|
+
}
|
|
3490
|
+
const fm = parseSkillFrontmatter(content, fallbackName + '.md');
|
|
3491
|
+
return {
|
|
3492
|
+
name: fm.name || fallbackName,
|
|
3493
|
+
description: fm.description || '',
|
|
3494
|
+
location,
|
|
3495
|
+
};
|
|
3496
|
+
};
|
|
3497
|
+
|
|
3498
|
+
const safeReadDirents = (dir) => {
|
|
3499
|
+
try { return fs.readdirSync(dir, { withFileTypes: true }); }
|
|
3500
|
+
catch { return []; }
|
|
3501
|
+
};
|
|
3502
|
+
|
|
3503
|
+
// User-level skills
|
|
3504
|
+
for (const entry of safeReadDirents(path.join(copilotRoot, 'skills'))) {
|
|
3505
|
+
if (!entry.isDirectory()) continue;
|
|
3506
|
+
const skill = readSkillDir(path.join(copilotRoot, 'skills', entry.name), entry.name, 'user');
|
|
3507
|
+
if (skill) collected.push(skill);
|
|
3508
|
+
}
|
|
3509
|
+
|
|
3510
|
+
// Plugin-provided skills (two levels deep: marketplace/plugin)
|
|
3511
|
+
const pluginsRoot = path.join(copilotRoot, 'installed-plugins');
|
|
3512
|
+
for (const mkt of safeReadDirents(pluginsRoot)) {
|
|
3513
|
+
if (!mkt.isDirectory()) continue;
|
|
3514
|
+
for (const plugin of safeReadDirents(path.join(pluginsRoot, mkt.name))) {
|
|
3515
|
+
if (!plugin.isDirectory()) continue;
|
|
3516
|
+
const pluginSkillsDir = path.join(pluginsRoot, mkt.name, plugin.name, 'skills');
|
|
3517
|
+
for (const entry of safeReadDirents(pluginSkillsDir)) {
|
|
3518
|
+
if (!entry.isDirectory()) continue;
|
|
3519
|
+
const skill = readSkillDir(path.join(pluginSkillsDir, entry.name), entry.name, 'plugin');
|
|
3520
|
+
if (skill) collected.push(skill);
|
|
3521
|
+
}
|
|
3522
|
+
}
|
|
3523
|
+
}
|
|
3524
|
+
|
|
3525
|
+
// De-dupe by name, user wins over plugin (matches Copilot CLI's precedence)
|
|
3526
|
+
const seen = new Map();
|
|
3527
|
+
for (const s of collected) {
|
|
3528
|
+
const existing = seen.get(s.name);
|
|
3529
|
+
if (!existing || (s.location === 'user' && existing.location !== 'user')) {
|
|
3530
|
+
seen.set(s.name, s);
|
|
3531
|
+
}
|
|
3532
|
+
}
|
|
3533
|
+
return Array.from(seen.values()).sort((a, b) => a.name.localeCompare(b.name));
|
|
3534
|
+
}
|
|
3535
|
+
|
|
3536
|
+
function _xmlEscapeForPrompt(s) {
|
|
3537
|
+
return String(s == null ? '' : s)
|
|
3538
|
+
.replace(/&/g, '&')
|
|
3539
|
+
.replace(/</g, '<')
|
|
3540
|
+
.replace(/>/g, '>');
|
|
3541
|
+
}
|
|
3542
|
+
|
|
3543
|
+
// Render a list of skills as an <available_skills> XML block matching the
|
|
3544
|
+
// shape Copilot CLI's default system prompt injects. When `skills` is empty
|
|
3545
|
+
// we still emit the block (with a sentinel comment) so the agent learns the
|
|
3546
|
+
// path exists rather than silently inferring nothing's there.
|
|
3547
|
+
function buildAvailableSkillsBlock(skills) {
|
|
3548
|
+
const list = Array.isArray(skills) ? skills : [];
|
|
3549
|
+
if (list.length === 0) {
|
|
3550
|
+
return '<available_skills>\n <!-- No skills found in ~/.copilot/skills/ or ~/.copilot/installed-plugins/<marketplace>/<plugin>/skills/ -->\n</available_skills>';
|
|
3551
|
+
}
|
|
3552
|
+
const items = list.map(s =>
|
|
3553
|
+
`<skill>\n <name>${_xmlEscapeForPrompt(s.name)}</name>\n <description>${_xmlEscapeForPrompt(s.description)}</description>\n <location>${_xmlEscapeForPrompt(s.location)}</location>\n</skill>`
|
|
3554
|
+
).join('\n');
|
|
3555
|
+
return `<available_skills>\n${items}\n</available_skills>`;
|
|
3556
|
+
}
|
|
3557
|
+
|
|
3458
3558
|
function renderCcSystemPrompt(raw, opts) {
|
|
3459
3559
|
const liveRoot = (opts && typeof opts.liveRoot === 'string') ? opts.liveRoot : MINIONS_DIR;
|
|
3460
3560
|
const turnId = (opts && typeof opts.turnId === 'string') ? opts.turnId : '';
|
|
@@ -3467,6 +3567,14 @@ function renderCcSystemPrompt(raw, opts) {
|
|
|
3467
3567
|
if (out.includes('{{cc_protected_paths}}')) {
|
|
3468
3568
|
out = out.replace(/\{\{cc_protected_paths\}\}/g, describeCcProtectedPaths(liveRoot));
|
|
3469
3569
|
}
|
|
3570
|
+
// Available-skills enumeration walks two directory trees and reads each
|
|
3571
|
+
// SKILL.md's frontmatter — only do it when the template asks for it.
|
|
3572
|
+
// Tests pass `opts.skillsHomeDir` to redirect the scan at a fixture dir.
|
|
3573
|
+
if (out.includes('{{available_skills}}')) {
|
|
3574
|
+
const skillsOpts = (opts && typeof opts.skillsHomeDir === 'string')
|
|
3575
|
+
? { homeDir: opts.skillsHomeDir } : undefined;
|
|
3576
|
+
out = out.replace(/\{\{available_skills\}\}/g, buildAvailableSkillsBlock(listCopilotSkills(skillsOpts)));
|
|
3577
|
+
}
|
|
3470
3578
|
return out
|
|
3471
3579
|
.replace(/\{\{cc_turn_id\}\}/g, turnId)
|
|
3472
3580
|
.replace(/\{\{dashboard_port\}\}/g, dashboardPort);
|
|
@@ -5418,6 +5526,8 @@ module.exports = {
|
|
|
5418
5526
|
isLiveCommandCenterPath,
|
|
5419
5527
|
describeCcProtectedPaths,
|
|
5420
5528
|
renderCcSystemPrompt,
|
|
5529
|
+
listCopilotSkills,
|
|
5530
|
+
buildAvailableSkillsBlock,
|
|
5421
5531
|
_CC_PROTECTED_BASENAMES, // exported for testing
|
|
5422
5532
|
_CC_PROTECTED_FILE_GLOBS, // exported for testing
|
|
5423
5533
|
_CC_PROTECTED_PREFIXES, // exported for testing
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yemi33/minions",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2079",
|
|
4
4
|
"description": "Multi-agent AI dev team that runs from ~/.minions/ — five autonomous agents share a single engine, dashboard, and knowledge base",
|
|
5
5
|
"bin": {
|
|
6
6
|
"minions": "bin/minions.js"
|
package/prompts/cc-system.md
CHANGED
|
@@ -39,6 +39,19 @@ CAN modify: notes, plans, knowledge, work items, pull-requests.json, routing.md,
|
|
|
39
39
|
## Filesystem
|
|
40
40
|
Minions state lives in `{{minions_dir}}/`. Key paths: `config.json` (config), `routing.md` (dispatch rules), `projects/{name}/work-items.json` & `pull-requests.json` (per-project), `agents/{id}/` (charters, output), `plans/` & `prd/` (plans), `knowledge/` (KB), `notes/inbox/` (inbox), `engine/dispatch.json` (queue), `playbooks/` (templates). Use tools to read specifics.
|
|
41
41
|
|
|
42
|
+
## Your skills
|
|
43
|
+
|
|
44
|
+
Your runtime is the **Copilot CLI**, not Claude Code. Your installed skills live in:
|
|
45
|
+
|
|
46
|
+
- `~/.copilot/skills/<name>/SKILL.md` — user-level skills (yours)
|
|
47
|
+
- `~/.copilot/installed-plugins/<marketplace>/<plugin>/skills/<name>/SKILL.md` — plugin-provided skills (also yours)
|
|
48
|
+
|
|
49
|
+
To invoke a skill, call your `skill` tool with the skill name (the directory name). The skill body then guides the next steps.
|
|
50
|
+
|
|
51
|
+
When asked "what skills do you have", answer from the registry below — do NOT grep/glob `.claude/skills/`. Those belong to a different runtime (Claude Code) and are not available to you, even if the directory exists in cwd or `$HOME`.
|
|
52
|
+
|
|
53
|
+
{{available_skills}}
|
|
54
|
+
|
|
42
55
|
## Role: Orchestrator
|
|
43
56
|
You are primarily a dispatcher. Agents have full Claude Code + worktrees + MCP tools and are better suited for real work — but you are not hard-stopped from handling small requests yourself.
|
|
44
57
|
|