@yemi33/minions 0.1.2178 → 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 +39 -17
- package/dashboard/js/command-parser.js +1 -1
- package/dashboard/js/memory-panel.js +324 -0
- package/dashboard/js/qa.js +2 -2
- package/dashboard/js/refresh.js +19 -1
- package/dashboard/js/render-other.js +143 -2
- package/dashboard/js/render-prs.js +2 -1
- package/dashboard/js/render-schedules.js +1 -1
- package/dashboard/js/render-watches.js +1 -1
- package/dashboard/js/render-work-items.js +18 -1
- package/dashboard/js/settings.js +23 -0
- package/dashboard/pages/engine-memory-panel.html +56 -0
- package/dashboard/pages/engine.html +1 -0
- package/dashboard/pages/tools.html +8 -0
- package/dashboard/slim/js/link-pr.js +5 -5
- package/dashboard/slim/js/modals-tiles.js +44 -3
- package/dashboard/slim/js/projects.js +8 -6
- package/dashboard/slim/styles.css +20 -0
- package/dashboard-build.js +17 -2
- package/dashboard.js +693 -19
- package/docs/branch-derivation.md +13 -1
- 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/diagnostics-memory.js +190 -0
- package/engine/lifecycle.js +111 -1
- package/engine/preflight.js +265 -0
- package/engine/queries.js +331 -19
- 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 +390 -15
- package/engine/spawn-agent.js +178 -12
- package/engine/watchdog.js +6 -0
- package/engine.js +277 -4
- package/package.json +2 -2
package/engine/preflight.js
CHANGED
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
11
|
const fs = require('fs');
|
|
12
|
+
const os = require('os');
|
|
12
13
|
const path = require('path');
|
|
13
14
|
const { execSync, execFileSync } = require('child_process');
|
|
14
15
|
|
|
@@ -666,12 +667,272 @@ function doctor(minionsHome) {
|
|
|
666
667
|
});
|
|
667
668
|
}
|
|
668
669
|
|
|
670
|
+
/**
|
|
671
|
+
* Harness propagation diagnostic for `minions doctor --harness` (plan item #1
|
|
672
|
+
* of `seamless-user-repo-harness-invocation`, PRD `P-a3f9b2c1`). Iterates
|
|
673
|
+
* every registered runtime and prints every dir / file the engine would
|
|
674
|
+
* surface to a spawned agent today, grouped by scope label
|
|
675
|
+
* (`user` / `project:<name>` / `personal` / `minions`). Missing on-disk paths
|
|
676
|
+
* are flagged with `⚠` but do not fail the command — they're listed so the
|
|
677
|
+
* operator can decide whether to create / populate the dir or leave it as-is
|
|
678
|
+
* for this host.
|
|
679
|
+
*
|
|
680
|
+
* The diagnostic is intentionally read-only and side-effect-free: no mutation
|
|
681
|
+
* of config, cache files, or worktrees. Exits non-zero only on a true
|
|
682
|
+
* runtime/config error (e.g. registry refuses to resolve a registered
|
|
683
|
+
* runtime). See `docs/harness-propagation.md` for the full contract.
|
|
684
|
+
*
|
|
685
|
+
* Pure-ish: `homeDir` and `existsFn` are injectable for tests so a temp
|
|
686
|
+
* sandbox can assert the "missing path" branch without touching the real
|
|
687
|
+
* `~/.claude` / `~/.copilot` dirs.
|
|
688
|
+
*/
|
|
689
|
+
function _resolveProjectsForHarness(minionsHome) {
|
|
690
|
+
const configPath = path.join(minionsHome, 'config.json');
|
|
691
|
+
try {
|
|
692
|
+
const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
|
693
|
+
const projects = Array.isArray(config.projects) ? config.projects : [];
|
|
694
|
+
return {
|
|
695
|
+
config,
|
|
696
|
+
projects: projects.filter(p => p && p.name && p.localPath && !String(p.name).startsWith('YOUR_')),
|
|
697
|
+
};
|
|
698
|
+
} catch {
|
|
699
|
+
return { config: null, projects: [] };
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
function _padRight(s, len) {
|
|
704
|
+
s = String(s);
|
|
705
|
+
if (s.length >= len) return s;
|
|
706
|
+
return s + ' '.repeat(len - s.length);
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
function _formatHarnessLine(absPath, scopeLabel, exists) {
|
|
710
|
+
const marker = exists ? '✓' : '⚠';
|
|
711
|
+
const pathCol = _padRight(absPath, 56);
|
|
712
|
+
const scopeCol = `[${scopeLabel}]`;
|
|
713
|
+
const suffix = exists ? '' : ' (missing on disk)';
|
|
714
|
+
return ` ${marker} ${pathCol} ${scopeCol}${suffix}`;
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
function _runtimeHarnessRows(runtime, { homeDir, projects, existsFn }) {
|
|
718
|
+
const rows = {
|
|
719
|
+
userAssetDirs: [],
|
|
720
|
+
skillRootsUser: [],
|
|
721
|
+
skillRootsProject: [],
|
|
722
|
+
skillWriteTargets: [],
|
|
723
|
+
commandRootsUser: [],
|
|
724
|
+
commandRootsProject: [],
|
|
725
|
+
mcpConfigUser: [],
|
|
726
|
+
mcpConfigProject: [],
|
|
727
|
+
};
|
|
728
|
+
if (typeof runtime.getUserAssetDirs === 'function') {
|
|
729
|
+
try {
|
|
730
|
+
for (const d of runtime.getUserAssetDirs({ homeDir }) || []) {
|
|
731
|
+
if (!d) continue;
|
|
732
|
+
rows.userAssetDirs.push({ path: d, scope: 'user', exists: existsFn(d) });
|
|
733
|
+
}
|
|
734
|
+
} catch { /* adapter optional fields */ }
|
|
735
|
+
}
|
|
736
|
+
if (typeof runtime.getSkillRoots === 'function') {
|
|
737
|
+
try {
|
|
738
|
+
for (const root of runtime.getSkillRoots({ homeDir }) || []) {
|
|
739
|
+
if (!root || !root.dir) continue;
|
|
740
|
+
if (root.scope === 'project') continue; // user call shouldn't return project rows, but be defensive
|
|
741
|
+
rows.skillRootsUser.push({ path: root.dir, scope: root.scope || 'user', exists: existsFn(root.dir) });
|
|
742
|
+
}
|
|
743
|
+
} catch { /* */ }
|
|
744
|
+
for (const project of projects) {
|
|
745
|
+
try {
|
|
746
|
+
for (const root of runtime.getSkillRoots({ homeDir, project }) || []) {
|
|
747
|
+
if (!root || !root.dir || root.scope !== 'project') continue;
|
|
748
|
+
rows.skillRootsProject.push({
|
|
749
|
+
path: root.dir,
|
|
750
|
+
scope: `project:${root.projectName || project.name}`,
|
|
751
|
+
exists: existsFn(root.dir),
|
|
752
|
+
});
|
|
753
|
+
}
|
|
754
|
+
} catch { /* */ }
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
if (typeof runtime.getSkillWriteTargets === 'function') {
|
|
758
|
+
try {
|
|
759
|
+
const targets = runtime.getSkillWriteTargets({ homeDir }) || {};
|
|
760
|
+
if (targets.personal) {
|
|
761
|
+
rows.skillWriteTargets.push({ path: targets.personal, scope: 'personal', exists: existsFn(targets.personal) });
|
|
762
|
+
}
|
|
763
|
+
} catch { /* */ }
|
|
764
|
+
for (const project of projects) {
|
|
765
|
+
try {
|
|
766
|
+
const targets = runtime.getSkillWriteTargets({ homeDir, project }) || {};
|
|
767
|
+
if (targets.project) {
|
|
768
|
+
rows.skillWriteTargets.push({
|
|
769
|
+
path: targets.project,
|
|
770
|
+
scope: `project:${project.name}`,
|
|
771
|
+
exists: existsFn(targets.project),
|
|
772
|
+
});
|
|
773
|
+
}
|
|
774
|
+
} catch { /* */ }
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
if (typeof runtime.getCommandRoots === 'function') {
|
|
778
|
+
try {
|
|
779
|
+
for (const root of runtime.getCommandRoots({ homeDir }) || []) {
|
|
780
|
+
if (!root || !root.dir) continue;
|
|
781
|
+
if (root.scope === 'project') continue;
|
|
782
|
+
rows.commandRootsUser.push({ path: root.dir, scope: root.scope || 'user', exists: existsFn(root.dir) });
|
|
783
|
+
}
|
|
784
|
+
} catch { /* */ }
|
|
785
|
+
for (const project of projects) {
|
|
786
|
+
try {
|
|
787
|
+
for (const root of runtime.getCommandRoots({ homeDir, project }) || []) {
|
|
788
|
+
if (!root || !root.dir || root.scope !== 'project') continue;
|
|
789
|
+
rows.commandRootsProject.push({
|
|
790
|
+
path: root.dir,
|
|
791
|
+
scope: `project:${root.projectName || project.name}`,
|
|
792
|
+
exists: existsFn(root.dir),
|
|
793
|
+
});
|
|
794
|
+
}
|
|
795
|
+
} catch { /* */ }
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
if (typeof runtime.getMcpConfigPaths === 'function') {
|
|
799
|
+
try {
|
|
800
|
+
for (const entry of runtime.getMcpConfigPaths({ homeDir }) || []) {
|
|
801
|
+
if (!entry || !entry.file) continue;
|
|
802
|
+
if (entry.scope === 'project') continue;
|
|
803
|
+
rows.mcpConfigUser.push({ path: entry.file, scope: entry.scope || 'user', exists: existsFn(entry.file) });
|
|
804
|
+
}
|
|
805
|
+
} catch { /* */ }
|
|
806
|
+
for (const project of projects) {
|
|
807
|
+
try {
|
|
808
|
+
for (const entry of runtime.getMcpConfigPaths({ homeDir, project }) || []) {
|
|
809
|
+
if (!entry || !entry.file || entry.scope !== 'project') continue;
|
|
810
|
+
rows.mcpConfigProject.push({
|
|
811
|
+
path: entry.file,
|
|
812
|
+
scope: `project:${entry.projectName || project.name}`,
|
|
813
|
+
exists: existsFn(entry.file),
|
|
814
|
+
});
|
|
815
|
+
}
|
|
816
|
+
} catch { /* */ }
|
|
817
|
+
}
|
|
818
|
+
}
|
|
819
|
+
return rows;
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
function _computeAddDirSnapshot(runtime, { minionsHome, homeDir, existsFn }) {
|
|
823
|
+
let computeAddDirs;
|
|
824
|
+
try { ({ computeAddDirs } = require('./spawn-agent')); }
|
|
825
|
+
catch { return null; }
|
|
826
|
+
if (typeof computeAddDirs !== 'function') return null;
|
|
827
|
+
let dirs = [];
|
|
828
|
+
try { dirs = computeAddDirs({ runtime, minionsDir: minionsHome, homeDir, exists: existsFn }) || []; }
|
|
829
|
+
catch { return null; }
|
|
830
|
+
return dirs.map(d => {
|
|
831
|
+
let scope = 'user';
|
|
832
|
+
if (path.resolve(d) === path.resolve(minionsHome)) scope = 'minions';
|
|
833
|
+
return { path: d, scope, exists: existsFn(d) };
|
|
834
|
+
});
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
function runHarnessDoctor(minionsHome, opts = {}) {
|
|
838
|
+
const homeDir = opts.homeDir || os.homedir();
|
|
839
|
+
const existsFn = opts.existsFn || fs.existsSync;
|
|
840
|
+
const out = opts.out || (line => console.log(line));
|
|
841
|
+
|
|
842
|
+
let registry;
|
|
843
|
+
try { registry = require('./runtimes'); }
|
|
844
|
+
catch (e) {
|
|
845
|
+
out(`Minions Harness Propagation`);
|
|
846
|
+
out(` Could not load runtime registry: ${e.message}`);
|
|
847
|
+
return false;
|
|
848
|
+
}
|
|
849
|
+
const runtimeNames = registry.listRuntimes();
|
|
850
|
+
const { config, projects } = _resolveProjectsForHarness(minionsHome);
|
|
851
|
+
const defaultCli = (config && config.engine && config.engine.defaultCli) || 'copilot';
|
|
852
|
+
|
|
853
|
+
out('Minions Harness Propagation');
|
|
854
|
+
if (projects.length === 0) {
|
|
855
|
+
out(' (no real projects configured — project-scope rows will be empty)');
|
|
856
|
+
} else {
|
|
857
|
+
out(` Projects: ${projects.map(p => p.name).join(', ')}`);
|
|
858
|
+
}
|
|
859
|
+
|
|
860
|
+
let hardError = false;
|
|
861
|
+
for (const runtimeName of runtimeNames) {
|
|
862
|
+
out('');
|
|
863
|
+
out(` Runtime: ${runtimeName}`);
|
|
864
|
+
let runtime;
|
|
865
|
+
try { runtime = registry.resolveRuntime(runtimeName); }
|
|
866
|
+
catch (e) {
|
|
867
|
+
out(` Could not resolve adapter: ${e.message}`);
|
|
868
|
+
hardError = true;
|
|
869
|
+
continue;
|
|
870
|
+
}
|
|
871
|
+
const rows = _runtimeHarnessRows(runtime, { homeDir, projects, existsFn });
|
|
872
|
+
|
|
873
|
+
out(' User asset dirs (--add-dir to agents):');
|
|
874
|
+
if (rows.userAssetDirs.length === 0) out(' (adapter does not expose getUserAssetDirs)');
|
|
875
|
+
for (const r of rows.userAssetDirs) out(_formatHarnessLine(r.path, r.scope, r.exists));
|
|
876
|
+
|
|
877
|
+
out(' Skill roots (CLI native discovery):');
|
|
878
|
+
if (rows.skillRootsUser.length === 0 && rows.skillRootsProject.length === 0) {
|
|
879
|
+
out(' (adapter does not expose getSkillRoots)');
|
|
880
|
+
}
|
|
881
|
+
for (const r of rows.skillRootsUser) out(_formatHarnessLine(r.path, r.scope, r.exists));
|
|
882
|
+
for (const r of rows.skillRootsProject) out(_formatHarnessLine(r.path, r.scope, r.exists));
|
|
883
|
+
|
|
884
|
+
out(' Skill write targets (auto-extract destinations):');
|
|
885
|
+
if (rows.skillWriteTargets.length === 0) {
|
|
886
|
+
out(' (adapter does not expose getSkillWriteTargets)');
|
|
887
|
+
}
|
|
888
|
+
for (const r of rows.skillWriteTargets) out(_formatHarnessLine(r.path, r.scope, r.exists));
|
|
889
|
+
|
|
890
|
+
out(' Slash commands (CLI native discovery):');
|
|
891
|
+
if (rows.commandRootsUser.length === 0 && rows.commandRootsProject.length === 0) {
|
|
892
|
+
out(' (adapter does not expose getCommandRoots)');
|
|
893
|
+
}
|
|
894
|
+
for (const r of rows.commandRootsUser) out(_formatHarnessLine(r.path, r.scope, r.exists));
|
|
895
|
+
for (const r of rows.commandRootsProject) out(_formatHarnessLine(r.path, r.scope, r.exists));
|
|
896
|
+
|
|
897
|
+
out(' MCP config files:');
|
|
898
|
+
if (rows.mcpConfigUser.length === 0 && rows.mcpConfigProject.length === 0) {
|
|
899
|
+
out(' (adapter does not expose getMcpConfigPaths)');
|
|
900
|
+
}
|
|
901
|
+
for (const r of rows.mcpConfigUser) out(_formatHarnessLine(r.path, r.scope, r.exists));
|
|
902
|
+
for (const r of rows.mcpConfigProject) out(_formatHarnessLine(r.path, r.scope, r.exists));
|
|
903
|
+
}
|
|
904
|
+
|
|
905
|
+
out('');
|
|
906
|
+
out(` Worktree --add-dir snapshot (engine fleet default: ${defaultCli}):`);
|
|
907
|
+
let defaultRuntime = null;
|
|
908
|
+
try { defaultRuntime = registry.resolveRuntime(defaultCli); }
|
|
909
|
+
catch { /* preflight handles unknown-runtime errors; here we just skip */ }
|
|
910
|
+
if (!defaultRuntime) {
|
|
911
|
+
out(' (cannot resolve fleet-default runtime — see `minions doctor` for details)');
|
|
912
|
+
} else {
|
|
913
|
+
const snapshot = _computeAddDirSnapshot(defaultRuntime, { minionsHome, homeDir, existsFn });
|
|
914
|
+
if (!snapshot || snapshot.length === 0) {
|
|
915
|
+
out(' (no dirs attached — engine/spawn-agent.js unavailable or adapter has no asset dirs)');
|
|
916
|
+
} else {
|
|
917
|
+
for (const r of snapshot) out(_formatHarnessLine(r.path, r.scope, r.exists));
|
|
918
|
+
}
|
|
919
|
+
}
|
|
920
|
+
|
|
921
|
+
out('');
|
|
922
|
+
out(' All harness paths surveyed. Missing on-disk paths are warnings, not failures —');
|
|
923
|
+
out(' they\'re listed so you can decide whether to create the dir, populate it, or');
|
|
924
|
+
out(' ignore it for this host.');
|
|
925
|
+
out('');
|
|
926
|
+
return !hardError;
|
|
927
|
+
}
|
|
928
|
+
|
|
669
929
|
module.exports = {
|
|
670
930
|
findClaudeBinary,
|
|
671
931
|
runPreflight,
|
|
672
932
|
printPreflight,
|
|
673
933
|
checkOrExit,
|
|
674
934
|
doctor,
|
|
935
|
+
runHarnessDoctor,
|
|
675
936
|
// Exposed for unit tests (P-9e8a3f1d) — engine code MUST go through
|
|
676
937
|
// runPreflight/doctor, never these helpers directly.
|
|
677
938
|
_distinctRuntimes,
|
|
@@ -682,4 +943,8 @@ module.exports = {
|
|
|
682
943
|
_fetchCliHelpText,
|
|
683
944
|
_checkBypassFlagSupported,
|
|
684
945
|
_bypassFlagResults,
|
|
946
|
+
// Exposed for harness-propagation doctor unit tests (P-a3f9b2c1).
|
|
947
|
+
_runtimeHarnessRows,
|
|
948
|
+
_computeAddDirSnapshot,
|
|
949
|
+
_formatHarnessLine,
|
|
685
950
|
};
|