@yemi33/minions 0.1.1686 → 0.1.1688
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/CHANGELOG.md +10 -0
- package/dashboard/js/render-other.js +4 -2
- package/dashboard/js/render-skills.js +6 -4
- package/dashboard.js +1 -32
- package/engine/cleanup.js +11 -8
- package/engine/cli.js +9 -12
- package/engine/copilot-models.json +1 -1
- package/engine/lifecycle.js +7 -6
- package/engine/pipeline.js +11 -8
- package/engine/runtimes/claude.js +28 -6
- package/engine/runtimes/copilot.js +6 -0
- package/engine/shared.js +11 -5
- package/engine/spawn-agent.js +32 -10
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -335,6 +335,7 @@ async function openScanProjectsModal() {
|
|
|
335
335
|
'<button onclick="_runProjectScan()" style="padding:6px 16px;background:var(--blue);color:#fff;border:none;border-radius:var(--radius-sm);cursor:pointer;white-space:nowrap">Scan</button>' +
|
|
336
336
|
'</div>' +
|
|
337
337
|
'<div id="scan-results" style="color:var(--muted);font-size:12px">Click Scan to find git repos in the directory.</div>' +
|
|
338
|
+
'<div class="cmd-toast" id="scan-toast" style="margin-top:0"></div>' +
|
|
338
339
|
'</div>';
|
|
339
340
|
document.getElementById('modal-body').style.whiteSpace = 'normal';
|
|
340
341
|
document.getElementById('modal-body').style.fontFamily = "'Segoe UI', system-ui, sans-serif";
|
|
@@ -410,19 +411,20 @@ async function _addSelectedProjects() {
|
|
|
410
411
|
var data = await res.json().catch(function() { return {}; });
|
|
411
412
|
if (res.ok) {
|
|
412
413
|
added++;
|
|
414
|
+
var addedName = data.name || repo.name;
|
|
413
415
|
optimisticallyAddProject({
|
|
414
|
-
name:
|
|
416
|
+
name: addedName,
|
|
415
417
|
description: (data.detected && data.detected.description) || repo.description || '',
|
|
416
418
|
path: data.path || repo.path,
|
|
417
419
|
localPath: data.path || repo.path,
|
|
418
420
|
});
|
|
419
421
|
cb.disabled = true;
|
|
420
422
|
cb.closest('label').style.opacity = '0.5';
|
|
423
|
+
showToast('scan-toast', added + ' project(s) added', true);
|
|
421
424
|
}
|
|
422
425
|
} catch { /* continue with next */ }
|
|
423
426
|
}
|
|
424
427
|
if (added > 0) {
|
|
425
|
-
showToast('cmd-toast', added + ' project(s) added', true);
|
|
426
428
|
refresh();
|
|
427
429
|
}
|
|
428
430
|
}
|
|
@@ -69,12 +69,14 @@ function renderSkills(skills) {
|
|
|
69
69
|
}
|
|
70
70
|
html += '</div>';
|
|
71
71
|
|
|
72
|
-
// Note clarifying skill visibility — agents read these on demand
|
|
73
|
-
//
|
|
74
|
-
//
|
|
72
|
+
// Note clarifying skill visibility — agents read these on demand. Runtime-
|
|
73
|
+
// native locations (~/.claude/skills, ~/.copilot/skills, plugin skills) are
|
|
74
|
+
// only visible to that runtime. The "agent" tab (~/.agents/skills) is the
|
|
75
|
+
// cross-runtime portable bucket and IS visible to every runtime.
|
|
75
76
|
html += '<div style="font-size:9px;color:var(--muted);margin-bottom:8px;line-height:1.4">' +
|
|
76
77
|
'Skills are reference docs agents read on demand — they are not injected wholesale into prompts. ' +
|
|
77
|
-
'Each tab reflects what the matching runtime would see;
|
|
78
|
+
'Each tab reflects what the matching runtime would see; runtime-native skills are NOT cross-visible. ' +
|
|
79
|
+
'The agent tab (~/.agents/skills) is the cross-runtime portable bucket — visible to every runtime.' +
|
|
78
80
|
'</div>';
|
|
79
81
|
|
|
80
82
|
// Filter by tab
|
package/dashboard.js
CHANGED
|
@@ -4502,39 +4502,8 @@ What would you like to discuss or change? When you're happy, say "approve" and I
|
|
|
4502
4502
|
const source = params.get('source') || '';
|
|
4503
4503
|
if (!_isValidSkillFileName(file)) { res.statusCode = 400; res.end('Invalid file'); return; }
|
|
4504
4504
|
|
|
4505
|
-
let content = '';
|
|
4506
4505
|
const skillPath = _resolveSkillReadPath({ file, dir, source, config: CONFIG });
|
|
4507
|
-
|
|
4508
|
-
// Fallback when caller didn't supply `dir`: try the source's known native
|
|
4509
|
-
// locations. `_resolveSkillReadPath` only matches entries returned by
|
|
4510
|
-
// `collectSkillFiles`, so a skill that already has `dir` will resolve there.
|
|
4511
|
-
if (!content && !dir) {
|
|
4512
|
-
const home = os.homedir();
|
|
4513
|
-
const skillStem = file.replace(/\.md$/, '').replace(/^SKILL$/, '');
|
|
4514
|
-
const candidates = [];
|
|
4515
|
-
if (source === 'claude-code' || !source) {
|
|
4516
|
-
candidates.push(path.join(home, '.claude', 'skills', skillStem, 'SKILL.md'));
|
|
4517
|
-
}
|
|
4518
|
-
if (source === 'copilot') {
|
|
4519
|
-
candidates.push(path.join(home, '.copilot', 'skills', skillStem, 'SKILL.md'));
|
|
4520
|
-
}
|
|
4521
|
-
if (source === 'agent-skill') {
|
|
4522
|
-
candidates.push(path.join(home, '.agents', 'skills', skillStem, 'SKILL.md'));
|
|
4523
|
-
}
|
|
4524
|
-
if (source.startsWith('project:')) {
|
|
4525
|
-
const proj = PROJECTS.find(p => p.name === source.slice('project:'.length));
|
|
4526
|
-
if (proj?.localPath) {
|
|
4527
|
-
for (const sub of ['.claude', '.github', '.agents']) {
|
|
4528
|
-
candidates.push(path.join(proj.localPath, sub, 'skills', file));
|
|
4529
|
-
candidates.push(path.join(proj.localPath, sub, 'skills', skillStem, 'SKILL.md'));
|
|
4530
|
-
}
|
|
4531
|
-
}
|
|
4532
|
-
}
|
|
4533
|
-
for (const c of candidates) {
|
|
4534
|
-
content = safeRead(c) || '';
|
|
4535
|
-
if (content) break;
|
|
4536
|
-
}
|
|
4537
|
-
}
|
|
4506
|
+
const content = skillPath ? (safeRead(skillPath) || '') : '';
|
|
4538
4507
|
res.setHeader('Content-Type', 'text/plain; charset=utf-8');
|
|
4539
4508
|
res.setHeader('Access-Control-Allow-Origin', '*');
|
|
4540
4509
|
res.end(content || 'Skill not found.');
|
package/engine/cleanup.js
CHANGED
|
@@ -621,17 +621,20 @@ function runCleanup(config, verbose = false) {
|
|
|
621
621
|
catch { orphanPrdEntries = []; }
|
|
622
622
|
for (const pf of orphanPrdEntries) {
|
|
623
623
|
const prdPath = path.join(PRD_DIR, pf);
|
|
624
|
-
const
|
|
625
|
-
if (!
|
|
624
|
+
const peek = safeJson(prdPath);
|
|
625
|
+
if (!peek?.missing_features) continue;
|
|
626
626
|
let reset = 0;
|
|
627
|
-
|
|
628
|
-
if (
|
|
629
|
-
|
|
630
|
-
|
|
627
|
+
mutateJsonFileLocked(prdPath, (prd) => {
|
|
628
|
+
if (!prd?.missing_features) return prd;
|
|
629
|
+
for (const feat of prd.missing_features) {
|
|
630
|
+
if ((feat.status === shared.WI_STATUS.DISPATCHED || feat.status === shared.WI_STATUS.FAILED) && !wiIds.has(feat.id)) {
|
|
631
|
+
feat.status = shared.WI_STATUS.PENDING;
|
|
632
|
+
reset++;
|
|
633
|
+
}
|
|
631
634
|
}
|
|
632
|
-
|
|
635
|
+
return prd;
|
|
636
|
+
}, { skipWriteIfUnchanged: true });
|
|
633
637
|
if (reset > 0) {
|
|
634
|
-
safeWrite(prdPath, prd);
|
|
635
638
|
log('info', `Reset ${reset} orphaned PRD item status(es) → pending in ${pf}`);
|
|
636
639
|
cleaned.orphanedPrdStatuses += reset;
|
|
637
640
|
}
|
package/engine/cli.js
CHANGED
|
@@ -146,21 +146,18 @@ function _parseRuntimeFlags(args) {
|
|
|
146
146
|
* Heuristic flag for "this model is obviously wrong for this runtime". Used
|
|
147
147
|
* to surface the "pass --model '' to clear" hint when a user switches CLIs
|
|
148
148
|
* but leaves a stale model behind. Errs on the side of false-negatives —
|
|
149
|
-
* unknown runtime → no opinion,
|
|
149
|
+
* unknown runtime → no opinion, runtime adapter without `modelLooksFamiliar`
|
|
150
|
+
* → no opinion. Runtime-specific knowledge lives in the adapter (see
|
|
151
|
+
* claude.js#modelLooksFamiliar) so adding a new runtime never requires
|
|
152
|
+
* editing cli.js.
|
|
150
153
|
*/
|
|
151
154
|
function _modelLooksIncompatible(runtime, model) {
|
|
152
155
|
if (!model) return false;
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
}
|
|
159
|
-
if (runtime === 'copilot') {
|
|
160
|
-
// Copilot adapter maps Minions' family aliases before spawning.
|
|
161
|
-
return false;
|
|
162
|
-
}
|
|
163
|
-
return false;
|
|
156
|
+
let adapter;
|
|
157
|
+
try { adapter = require('./runtimes').resolveRuntime(runtime); }
|
|
158
|
+
catch { return false; } // unknown runtime → no opinion
|
|
159
|
+
if (typeof adapter.modelLooksFamiliar !== 'function') return false; // adapter doesn't claim a model namespace → no opinion
|
|
160
|
+
return !adapter.modelLooksFamiliar(model);
|
|
164
161
|
}
|
|
165
162
|
|
|
166
163
|
/**
|
package/engine/lifecycle.js
CHANGED
|
@@ -1037,7 +1037,8 @@ async function findOpenPrForBranch(meta, config) {
|
|
|
1037
1037
|
if (host === 'github') {
|
|
1038
1038
|
const ghSlug = projectObj.prUrlBase?.match(/github\.com\/([^/]+\/[^/]+)\/pull/)?.[1];
|
|
1039
1039
|
if (!ghSlug) return null;
|
|
1040
|
-
|
|
1040
|
+
const maxAttempts = ENGINE_DEFAULTS.prAutoLinkRetries;
|
|
1041
|
+
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
1041
1042
|
if (attempt > 0) await new Promise(r => setTimeout(r, 3000));
|
|
1042
1043
|
let raw = '';
|
|
1043
1044
|
try {
|
|
@@ -1047,13 +1048,13 @@ async function findOpenPrForBranch(meta, config) {
|
|
|
1047
1048
|
if (hits.length > 0 && hits[0].state === 'OPEN') {
|
|
1048
1049
|
return { project: projectObj, prNumber: hits[0].number, url: hits[0].url };
|
|
1049
1050
|
}
|
|
1050
|
-
if (attempt ===
|
|
1051
|
-
log('warn', `Auto-link fallback: no open PR found on branch ${meta.branch} after
|
|
1051
|
+
if (attempt === maxAttempts - 1) {
|
|
1052
|
+
log('warn', `Auto-link fallback: no open PR found on branch ${meta.branch} after ${maxAttempts} attempts (raw: ${(raw || '').slice(0, 200)})`);
|
|
1052
1053
|
}
|
|
1053
1054
|
} catch (err) {
|
|
1054
|
-
if (attempt ===
|
|
1055
|
+
if (attempt === maxAttempts - 1) {
|
|
1055
1056
|
const rawSuffix = raw ? ` (raw: ${raw.slice(0, 200)})` : '';
|
|
1056
|
-
log('warn', `Auto-link fallback: gh pr list lookup failed on branch ${meta.branch} after
|
|
1057
|
+
log('warn', `Auto-link fallback: gh pr list lookup failed on branch ${meta.branch} after ${maxAttempts} attempts: ${err.message}${rawSuffix}`);
|
|
1057
1058
|
}
|
|
1058
1059
|
}
|
|
1059
1060
|
}
|
|
@@ -1475,7 +1476,7 @@ async function processPendingRebases(config) {
|
|
|
1475
1476
|
const result = await rebaseBranchOntoMain(pr, project, config);
|
|
1476
1477
|
if (!result.success) {
|
|
1477
1478
|
entry.attempts = (entry.attempts || 0) + 1;
|
|
1478
|
-
if (entry.attempts <
|
|
1479
|
+
if (entry.attempts < ENGINE_DEFAULTS.rebaseQueueRetries) {
|
|
1479
1480
|
remaining.push(entry);
|
|
1480
1481
|
} else {
|
|
1481
1482
|
log('warn', `Rebase failed after retries for ${pr.id} on ${pr.branch}: ${result.error}`);
|
package/engine/pipeline.js
CHANGED
|
@@ -730,14 +730,17 @@ function isStageComplete(stage, stageState, run, config) {
|
|
|
730
730
|
if (stage.autoApprove && artifacts.prds?.length > 0) {
|
|
731
731
|
for (const prdFile of artifacts.prds) {
|
|
732
732
|
const prdPath = path.join(prdDir, prdFile);
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
prd.status
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
733
|
+
let approved = false;
|
|
734
|
+
mutateJsonFileLocked(prdPath, (prd) => {
|
|
735
|
+
if (prd && prd.status === PLAN_STATUS.AWAITING_APPROVAL) {
|
|
736
|
+
prd.status = PLAN_STATUS.APPROVED;
|
|
737
|
+
prd.approvedAt = ts();
|
|
738
|
+
prd.approvedBy = 'pipeline:' + run.pipelineId;
|
|
739
|
+
approved = true;
|
|
740
|
+
}
|
|
741
|
+
return prd;
|
|
742
|
+
}, { skipWriteIfUnchanged: true });
|
|
743
|
+
if (approved) log('info', `Pipeline ${run.pipelineId}: auto-approved PRD ${prdFile}`);
|
|
741
744
|
}
|
|
742
745
|
}
|
|
743
746
|
|
|
@@ -640,20 +640,28 @@ const capabilities = {
|
|
|
640
640
|
// (fatal error message). Multi-line so all platforms see actionable guidance.
|
|
641
641
|
const INSTALL_HINT = 'install from https://claude.ai/download or: npm install -g @anthropic-ai/claude-code';
|
|
642
642
|
|
|
643
|
+
// Asset roots passed to spawn as `--add-dir` so worktrees can read globally
|
|
644
|
+
// installed skills. `~/.agents/skills` is the cross-runtime portable location;
|
|
645
|
+
// every runtime adapter exposes it so a skill placed there is genuinely visible
|
|
646
|
+
// to every runtime (matches the directory name's promise).
|
|
643
647
|
function getUserAssetDirs({ homeDir = os.homedir() } = {}) {
|
|
644
|
-
return [
|
|
648
|
+
return [
|
|
649
|
+
path.join(homeDir, '.claude'),
|
|
650
|
+
path.join(homeDir, '.agents'),
|
|
651
|
+
];
|
|
645
652
|
}
|
|
646
653
|
|
|
647
654
|
function getSkillRoots({ homeDir = os.homedir(), project = null } = {}) {
|
|
648
655
|
const roots = [
|
|
649
656
|
{ dir: path.join(homeDir, '.claude', 'skills'), scope: 'claude-code' },
|
|
657
|
+
{ dir: path.join(homeDir, '.agents', 'skills'), scope: 'agent-skill' },
|
|
650
658
|
];
|
|
651
659
|
if (project?.localPath) {
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
scope: 'project',
|
|
655
|
-
|
|
656
|
-
|
|
660
|
+
const projectName = project.name || path.basename(project.localPath);
|
|
661
|
+
roots.push(
|
|
662
|
+
{ dir: path.join(project.localPath, '.claude', 'skills'), scope: 'project', projectName },
|
|
663
|
+
{ dir: path.join(project.localPath, '.agents', 'skills'), scope: 'project', projectName },
|
|
664
|
+
);
|
|
657
665
|
}
|
|
658
666
|
return roots;
|
|
659
667
|
}
|
|
@@ -666,6 +674,19 @@ function getSkillWriteTargets({ homeDir = os.homedir(), project = null } = {}) {
|
|
|
666
674
|
return targets;
|
|
667
675
|
}
|
|
668
676
|
|
|
677
|
+
// Heuristic: does `model` look like a Claude model identifier? Powers the
|
|
678
|
+
// preflight "stale model after CLI switch" warning in cli.js. Returning false
|
|
679
|
+
// means "this looks wrong for Claude" — gpt-5.4 / o3-* / codex etc. Keep this
|
|
680
|
+
// here (not in cli.js) so the runtime owns its own model namespace and adding
|
|
681
|
+
// a future runtime never requires editing cli.js.
|
|
682
|
+
function modelLooksFamiliar(model) {
|
|
683
|
+
if (!model) return true;
|
|
684
|
+
const m = String(model).toLowerCase();
|
|
685
|
+
if (m.startsWith('claude-')) return true;
|
|
686
|
+
if (m === 'sonnet' || m === 'opus' || m === 'haiku') return true;
|
|
687
|
+
return false;
|
|
688
|
+
}
|
|
689
|
+
|
|
669
690
|
module.exports = {
|
|
670
691
|
name: 'claude',
|
|
671
692
|
capabilities,
|
|
@@ -688,6 +709,7 @@ module.exports = {
|
|
|
688
709
|
usesSystemPromptFile,
|
|
689
710
|
classifyFailure,
|
|
690
711
|
resolveModel,
|
|
712
|
+
modelLooksFamiliar,
|
|
691
713
|
parseOutput,
|
|
692
714
|
parseStreamChunk,
|
|
693
715
|
parseError,
|
|
@@ -798,6 +798,11 @@ function getUserAssetDirs({ homeDir = os.homedir() } = {}) {
|
|
|
798
798
|
];
|
|
799
799
|
}
|
|
800
800
|
|
|
801
|
+
// Copilot CLI reads project skills from .github/skills, .claude/skills, AND
|
|
802
|
+
// .agents/skills per the official docs (see "Adding agent skills for GitHub
|
|
803
|
+
// Copilot CLI"). Listing all three keeps the dashboard accurate and ensures
|
|
804
|
+
// spawned Copilot agents receive `--add-dir` for every dir Copilot would
|
|
805
|
+
// natively read from.
|
|
801
806
|
function getSkillRoots({ homeDir = os.homedir(), project = null } = {}) {
|
|
802
807
|
const roots = [
|
|
803
808
|
{ dir: path.join(homeDir, '.copilot', 'skills'), scope: 'copilot' },
|
|
@@ -807,6 +812,7 @@ function getSkillRoots({ homeDir = os.homedir(), project = null } = {}) {
|
|
|
807
812
|
const projectName = project.name || path.basename(project.localPath);
|
|
808
813
|
roots.push(
|
|
809
814
|
{ dir: path.join(project.localPath, '.github', 'skills'), scope: 'project', projectName },
|
|
815
|
+
{ dir: path.join(project.localPath, '.claude', 'skills'), scope: 'project', projectName },
|
|
810
816
|
{ dir: path.join(project.localPath, '.agents', 'skills'), scope: 'project', projectName },
|
|
811
817
|
);
|
|
812
818
|
}
|
package/engine/shared.js
CHANGED
|
@@ -793,6 +793,8 @@ const ENGINE_DEFAULTS = {
|
|
|
793
793
|
minRetryGapMs: 120000, // 2min — minimum gap between retry dispatches for the same work item; prevents tight retry loops when an idempotent agent (e.g. review bailing out on a duplicate) cannot produce the expected output (#1770)
|
|
794
794
|
pipelineApiRetries: 2, // max attempts for pipeline API calls
|
|
795
795
|
pipelineApiRetryDelay: 2000, // ms delay between pipeline API retries
|
|
796
|
+
prAutoLinkRetries: 3, // max attempts for gh pr list lookup when auto-linking PR after merge (3s backoff between attempts)
|
|
797
|
+
rebaseQueueRetries: 3, // max rebase attempts per queued PR before giving up
|
|
796
798
|
versionCheckInterval: 3600000, // 1 hour — how often to check npm for updates (ms)
|
|
797
799
|
logFlushInterval: 5000, // 5s — how often to flush buffered log entries to disk
|
|
798
800
|
logBufferSize: 50, // flush immediately when buffer exceeds this many entries
|
|
@@ -1096,15 +1098,19 @@ function runtimeConfigWarnings(config, registeredRuntimes) {
|
|
|
1096
1098
|
}
|
|
1097
1099
|
}
|
|
1098
1100
|
|
|
1099
|
-
// 3. Bare-mode misconfig: claudeBareMode +
|
|
1100
|
-
// CC system prompt. `--bare` suppresses CLAUDE.md
|
|
1101
|
-
// lose project context unless the user wires an
|
|
1101
|
+
// 3. Bare-mode misconfig: claudeBareMode + a CC runtime that honours
|
|
1102
|
+
// `--bare` + no explicit CC system prompt. `--bare` suppresses CLAUDE.md
|
|
1103
|
+
// auto-discovery; CC will lose project context unless the user wires an
|
|
1104
|
+
// explicit prompt. Gated on `capabilities.bareMode` rather than runtime
|
|
1105
|
+
// name so any future runtime that adopts the same flag is covered.
|
|
1102
1106
|
if (engine.claudeBareMode === true) {
|
|
1103
1107
|
const ccCli = resolveCcCli(engine);
|
|
1104
|
-
|
|
1108
|
+
let ccRuntime = null;
|
|
1109
|
+
try { ccRuntime = require('./runtimes').resolveRuntime(ccCli); } catch { /* unknown runtime — skip */ }
|
|
1110
|
+
if (ccRuntime?.capabilities?.bareMode === true && !_isMeaningful(engine.ccSystemPrompt)) {
|
|
1105
1111
|
warnings.push({
|
|
1106
1112
|
id: 'bare-mode-misconfig',
|
|
1107
|
-
message:
|
|
1113
|
+
message: `engine.claudeBareMode is true but CC runs on ${ccCli} (which honours --bare) with no engine.ccSystemPrompt — CLAUDE.md auto-discovery is suppressed and CC will lose project context.`,
|
|
1108
1114
|
});
|
|
1109
1115
|
}
|
|
1110
1116
|
}
|
package/engine/spawn-agent.js
CHANGED
|
@@ -201,6 +201,36 @@ async function writeProcessExitSentinel({
|
|
|
201
201
|
return { sentinel, stdoutFlushed, outputPathWritten };
|
|
202
202
|
}
|
|
203
203
|
|
|
204
|
+
/**
|
|
205
|
+
* Build the `--add-dir` list passed to the runtime CLI. Pure: takes
|
|
206
|
+
* `{ runtime, minionsDir, homeDir, exists }` and returns an ordered, deduped
|
|
207
|
+
* array of dirs the agent should be able to read from outside its worktree.
|
|
208
|
+
*
|
|
209
|
+
* Order: minionsDir first (so playbooks/system-prompt are always reachable),
|
|
210
|
+
* followed by every existing dir from `runtime.getUserAssetDirs({ homeDir })`.
|
|
211
|
+
* Non-existent asset dirs are dropped — Claude CLI rejects unknown `--add-dir`
|
|
212
|
+
* entries. The dedup compares resolved paths so we never emit minionsDir twice
|
|
213
|
+
* (e.g. when runtime asset dir IS the minions repo in unusual setups).
|
|
214
|
+
*
|
|
215
|
+
* `exists` is injectable for tests; defaults to `fs.existsSync`.
|
|
216
|
+
*/
|
|
217
|
+
function computeAddDirs({ runtime, minionsDir, homeDir, exists = fs.existsSync } = {}) {
|
|
218
|
+
const out = [minionsDir];
|
|
219
|
+
const seen = new Set([path.resolve(minionsDir)]);
|
|
220
|
+
const assetDirs = typeof runtime?.getUserAssetDirs === 'function'
|
|
221
|
+
? runtime.getUserAssetDirs({ homeDir })
|
|
222
|
+
: [];
|
|
223
|
+
for (const d of assetDirs) {
|
|
224
|
+
if (!d) continue;
|
|
225
|
+
const resolved = path.resolve(d);
|
|
226
|
+
if (seen.has(resolved)) continue;
|
|
227
|
+
if (!exists(d)) continue;
|
|
228
|
+
out.push(d);
|
|
229
|
+
seen.add(resolved);
|
|
230
|
+
}
|
|
231
|
+
return out;
|
|
232
|
+
}
|
|
233
|
+
|
|
204
234
|
// ─── Main script execution ──────────────────────────────────────────────────
|
|
205
235
|
|
|
206
236
|
function _installHint(name, runtime) {
|
|
@@ -252,15 +282,7 @@ function main() {
|
|
|
252
282
|
// worktree, so runtime-native global assets would otherwise be invisible.
|
|
253
283
|
// The adapter owns both where those assets live and how to surface them.
|
|
254
284
|
const minionsDir = path.resolve(__dirname, '..');
|
|
255
|
-
const addDirs =
|
|
256
|
-
const runtimeAssetDirs = typeof runtime.getUserAssetDirs === 'function'
|
|
257
|
-
? runtime.getUserAssetDirs({ homeDir: os.homedir() })
|
|
258
|
-
: [];
|
|
259
|
-
for (const dir of runtimeAssetDirs) {
|
|
260
|
-
if (dir && fs.existsSync(dir) && path.resolve(dir) !== path.resolve(minionsDir)) {
|
|
261
|
-
addDirs.push(dir);
|
|
262
|
-
}
|
|
263
|
-
}
|
|
285
|
+
const addDirs = computeAddDirs({ runtime, minionsDir, homeDir: os.homedir() });
|
|
264
286
|
|
|
265
287
|
let resolved;
|
|
266
288
|
try { resolved = runtime.resolveBinary({ env }); }
|
|
@@ -378,6 +400,6 @@ function main() {
|
|
|
378
400
|
});
|
|
379
401
|
}
|
|
380
402
|
|
|
381
|
-
module.exports = { parseSpawnArgs, buildSpawnInvocation, normalizeRuntimeExit, injectAdoTokenEnv, writeProcessExitSentinel };
|
|
403
|
+
module.exports = { parseSpawnArgs, buildSpawnInvocation, normalizeRuntimeExit, injectAdoTokenEnv, writeProcessExitSentinel, computeAddDirs };
|
|
382
404
|
|
|
383
405
|
if (require.main === module) main();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yemi33/minions",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1688",
|
|
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"
|