@mindrian_os/install 1.13.0-beta.11 → 1.13.0-beta.13
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/.claude-plugin/plugin.json +1 -1
- package/CHANGELOG.md +68 -3
- package/bin/cli.js +114 -57
- package/commands/act.md +16 -2
- package/commands/auto-explore.md +1 -0
- package/commands/doctor.md +1 -1
- package/commands/operator.md +1 -1
- package/commands/pipeline.md +16 -1
- package/commands/setup.md +7 -3
- package/commands/suggest-next.md +17 -3
- package/lib/core/active-plugin-root.cjs +207 -0
- package/lib/core/brain-client.cjs +451 -36
- package/lib/core/cache-prune.cjs +208 -0
- package/lib/core/framework-chain-composer.cjs +156 -43
- package/lib/core/migrations/phase-109-nodes-provenance.cjs +47 -0
- package/lib/core/navigation/memory-events.cjs +17 -1
- package/lib/core/navigation/neighborhood.cjs +5 -4
- package/lib/core/navigation/packet.cjs +87 -1
- package/lib/core/navigation.cjs +6 -0
- package/lib/core/resolve-brain-key.cjs +201 -0
- package/lib/hmi/jtbd-taxonomy.json +2 -1
- package/lib/memory/framework-chain-composer.test.cjs +54 -20
- package/lib/memory/navigation-hook-resolver.test.cjs +177 -0
- package/lib/memory/run-feynman-tests.cjs +102 -0
- package/lib/memory/security-trifecta.test.cjs +23 -6
- package/lib/memory/suggest-next-workflow.test.cjs +176 -0
- package/lib/memory/workflow-layer-e2e.test.cjs +262 -0
- package/lib/workflow/ROOM.md +1 -1
- package/package.json +4 -1
- package/references/brain/command-triggers-schema.md +10 -221
- package/references/methodology/index.md +11 -74
- package/skills/brain-connector/SKILL.md +12 -8
- package/skills/pws-methodology/SKILL.md +7 -5
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
/*
|
|
3
|
+
* lib/core/active-plugin-root.cjs -- the ONE resolver for "where is the active
|
|
4
|
+
* MindrianOS plugin install on this machine?"
|
|
5
|
+
*
|
|
6
|
+
* Background: before this module, three different places guessed independently
|
|
7
|
+
* (bin/cli.js hardcoded a legacy path; scripts/statusline-mos did `ls cache |
|
|
8
|
+
* sort -V` with a pure-semver regex that rejected `-beta.N`; context-monitor
|
|
9
|
+
* inherited whatever those picked). Three guessers, three failure modes -- a box
|
|
10
|
+
* with 1.12.0 + 1.13.0-beta.9 in the cache ended up rendering the statusline,
|
|
11
|
+
* the version banner, and MINDRIAN_OS_ROOT from the wrong (older) version. This
|
|
12
|
+
* collapses them into one source of truth.
|
|
13
|
+
*
|
|
14
|
+
* Precedence (first hit wins):
|
|
15
|
+
* 1. MINDRIAN_OS_ROOT env var -- tests, dev boxes, hand clones.
|
|
16
|
+
* 2. ~/.claude/plugins/installed_plugins.json
|
|
17
|
+
* -- Claude Code's own registry of the
|
|
18
|
+
* ACTIVE install of mos@mindrian-marketplace.
|
|
19
|
+
* Temporal truth: it knows which one is
|
|
20
|
+
* current even when "highest semver" is not
|
|
21
|
+
* the active one (e.g. a pinned older version).
|
|
22
|
+
* 3. ~/.claude/plugins/cache/<marketplace>/mos/<version>/
|
|
23
|
+
* -- newest valid version dir; pre-release
|
|
24
|
+
* tolerant (`sort -V` ordering). Fallback
|
|
25
|
+
* for when installed_plugins.json is missing
|
|
26
|
+
* or unparseable.
|
|
27
|
+
* 4. ~/.claude/plugins/mindrian-os/ -- legacy hand-clone layout.
|
|
28
|
+
* 5. null -- not installed.
|
|
29
|
+
*
|
|
30
|
+
* Use as a module:
|
|
31
|
+
* const { resolveActivePluginRoot } = require('<...>/lib/core/active-plugin-root.cjs');
|
|
32
|
+
* const { root, source } = resolveActivePluginRoot(); // root may be null
|
|
33
|
+
*
|
|
34
|
+
* Use as a CLI (so a bash wrapper -- e.g. scripts/statusline-mos -- can shell out):
|
|
35
|
+
* node active-plugin-root.cjs -> prints the resolved path, or nothing; exit 0 if found, 1 if not
|
|
36
|
+
* node active-plugin-root.cjs --json -> prints {"root": "<path|null>", "source": "<...>"}
|
|
37
|
+
*
|
|
38
|
+
* Canon Part 8: this reads LOCAL files only (~/.claude/plugins/). Zero network.
|
|
39
|
+
*/
|
|
40
|
+
|
|
41
|
+
const fs = require('node:fs');
|
|
42
|
+
const path = require('node:path');
|
|
43
|
+
const os = require('node:os');
|
|
44
|
+
|
|
45
|
+
const PLUGIN_KEYS = ['mos', 'mindrian-os']; // accept either marketplace plugin name
|
|
46
|
+
|
|
47
|
+
// A valid plugin install dir has a .claude-plugin/plugin.json.
|
|
48
|
+
function isPluginDir(dir) {
|
|
49
|
+
try {
|
|
50
|
+
return !!dir && fs.statSync(dir).isDirectory() && fs.existsSync(path.join(dir, '.claude-plugin', 'plugin.json'));
|
|
51
|
+
} catch {
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function fromInstalledPlugins(home) {
|
|
57
|
+
// Shape (Claude Code 2.x):
|
|
58
|
+
// { "version": N, "plugins": { "mos@mindrian-marketplace": [ { "installPath": "...", ... } ], ... } }
|
|
59
|
+
// Defensive: the key might be just "mos"; the value might be an object not a
|
|
60
|
+
// 1-element array; the path field might be installPath / path / dir.
|
|
61
|
+
const file = path.join(home, '.claude', 'plugins', 'installed_plugins.json');
|
|
62
|
+
let data;
|
|
63
|
+
try {
|
|
64
|
+
data = JSON.parse(fs.readFileSync(file, 'utf8'));
|
|
65
|
+
} catch {
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
const plugins = (data && (data.plugins || data)) || {};
|
|
69
|
+
for (const key of Object.keys(plugins)) {
|
|
70
|
+
const name = String(key).split('@')[0];
|
|
71
|
+
if (!PLUGIN_KEYS.includes(name)) continue;
|
|
72
|
+
let entry = plugins[key];
|
|
73
|
+
if (Array.isArray(entry)) entry = entry[0];
|
|
74
|
+
if (!entry || typeof entry !== 'object') continue;
|
|
75
|
+
const p = entry.installPath || entry.path || entry.dir;
|
|
76
|
+
if (p && isPluginDir(p)) return p;
|
|
77
|
+
}
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function fromMarketplaceCache(home) {
|
|
82
|
+
const cacheBase = path.join(home, '.claude', 'plugins', 'cache');
|
|
83
|
+
let marketplaces;
|
|
84
|
+
try {
|
|
85
|
+
marketplaces = fs.readdirSync(cacheBase, { withFileTypes: true });
|
|
86
|
+
} catch {
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
const found = [];
|
|
90
|
+
for (const mk of marketplaces) {
|
|
91
|
+
if (!mk.isDirectory()) continue;
|
|
92
|
+
for (const pluginName of PLUGIN_KEYS) {
|
|
93
|
+
const pluginDir = path.join(cacheBase, mk.name, pluginName);
|
|
94
|
+
let versions;
|
|
95
|
+
try {
|
|
96
|
+
versions = fs.readdirSync(pluginDir, { withFileTypes: true });
|
|
97
|
+
} catch {
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
for (const v of versions) {
|
|
101
|
+
if (!v.isDirectory()) continue;
|
|
102
|
+
const candidate = path.join(pluginDir, v.name);
|
|
103
|
+
if (isPluginDir(candidate)) found.push({ dir: candidate, ver: v.name });
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
if (!found.length) return null;
|
|
108
|
+
// `localeCompare` numeric ordering matches `sort -V` for the cases we care
|
|
109
|
+
// about: 1.12.0 < 1.12.5.1 < 1.13.0-beta.9 < 1.13.0-beta.11 < 1.13.0.
|
|
110
|
+
found.sort((a, b) => String(a.ver).localeCompare(String(b.ver), undefined, { numeric: true }));
|
|
111
|
+
return found[found.length - 1].dir;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function fromLegacyClone(home) {
|
|
115
|
+
const legacy = path.join(home, '.claude', 'plugins', 'mindrian-os');
|
|
116
|
+
return isPluginDir(legacy) ? legacy : null;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Phase 123 Plan-02 (HARNESS-123-05): classify the resolved plugin root into one
|
|
120
|
+
// of the 4 known topologies. RESEARCH Pitfall 4: a legacy-clone dir can carry an
|
|
121
|
+
// unrelated origin remote (the dogfood box's ~/.claude/plugins/mindrian-os/ has
|
|
122
|
+
// its origin pointed at mindrian-agno-backend.git) so a naive "has origin ->
|
|
123
|
+
// dev-clone" heuristic mis-classifies it. The order below is precedence-locked:
|
|
124
|
+
// env override and explicit legacy path win before the structural git probe.
|
|
125
|
+
//
|
|
126
|
+
// Returns one of: 'marketplace-cache' | 'dev-clone' | 'legacy' | 'not-found'.
|
|
127
|
+
function classifyTopology(root, source) {
|
|
128
|
+
if (!root) return 'not-found';
|
|
129
|
+
// env override -> always treated as a dev clone (tests, dev boxes, hand clones).
|
|
130
|
+
if (source === 'MINDRIAN_OS_ROOT') return 'dev-clone';
|
|
131
|
+
|
|
132
|
+
const home = os.homedir();
|
|
133
|
+
// Explicit legacy path wins before the structural check -- some legacy clones
|
|
134
|
+
// carry an unrelated origin remote (Pitfall 4).
|
|
135
|
+
try {
|
|
136
|
+
if (path.resolve(root) === path.resolve(home, '.claude', 'plugins', 'mindrian-os')) {
|
|
137
|
+
return 'legacy';
|
|
138
|
+
}
|
|
139
|
+
} catch { /* ignore */ }
|
|
140
|
+
|
|
141
|
+
// Marketplace cache: under ~/.claude/plugins/cache/<mp>/{mos|mindrian-os}/<version>/.
|
|
142
|
+
try {
|
|
143
|
+
const cacheBase = path.resolve(home, '.claude', 'plugins', 'cache');
|
|
144
|
+
const resolved = path.resolve(root);
|
|
145
|
+
if (resolved.startsWith(cacheBase + path.sep)) {
|
|
146
|
+
return 'marketplace-cache';
|
|
147
|
+
}
|
|
148
|
+
} catch { /* ignore */ }
|
|
149
|
+
|
|
150
|
+
// Dev clone structural probe: a .git + install.sh + an origin remote URL that
|
|
151
|
+
// mentions the plugin repo name. execSync is wrapped in try/catch so a missing
|
|
152
|
+
// git binary or a non-git dir falls through cleanly.
|
|
153
|
+
try {
|
|
154
|
+
if (fs.existsSync(path.join(root, '.git')) && fs.existsSync(path.join(root, 'install.sh'))) {
|
|
155
|
+
const cp = require('node:child_process');
|
|
156
|
+
let originUrl = '';
|
|
157
|
+
try {
|
|
158
|
+
originUrl = cp.execSync('git -C ' + JSON.stringify(root) + ' remote get-url origin', {
|
|
159
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
160
|
+
encoding: 'utf8',
|
|
161
|
+
timeout: 1500,
|
|
162
|
+
}).trim();
|
|
163
|
+
} catch { /* no origin remote, swallow */ }
|
|
164
|
+
if (/mindrian-os-plugin/i.test(originUrl)) return 'dev-clone';
|
|
165
|
+
}
|
|
166
|
+
} catch { /* ignore */ }
|
|
167
|
+
|
|
168
|
+
// Defensive defaults: respect the resolver's source classification.
|
|
169
|
+
if (source === 'legacy-clone') return 'legacy';
|
|
170
|
+
// A resolved plugin dir that is not under a recognized cache path and not the
|
|
171
|
+
// legacy fixed path and does not pass the dev-clone probe -- treat as
|
|
172
|
+
// marketplace-cache (the bin/cli.js + installed_plugins.json path).
|
|
173
|
+
return 'marketplace-cache';
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
function resolveActivePluginRoot() {
|
|
177
|
+
const envRoot = process.env.MINDRIAN_OS_ROOT;
|
|
178
|
+
if (envRoot) {
|
|
179
|
+
return { root: envRoot, source: 'MINDRIAN_OS_ROOT', topology: classifyTopology(envRoot, 'MINDRIAN_OS_ROOT') };
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
const home = os.homedir();
|
|
183
|
+
let r;
|
|
184
|
+
if ((r = fromInstalledPlugins(home))) {
|
|
185
|
+
return { root: r, source: 'installed_plugins.json', topology: classifyTopology(r, 'installed_plugins.json') };
|
|
186
|
+
}
|
|
187
|
+
if ((r = fromMarketplaceCache(home))) {
|
|
188
|
+
return { root: r, source: 'marketplace-cache', topology: classifyTopology(r, 'marketplace-cache') };
|
|
189
|
+
}
|
|
190
|
+
if ((r = fromLegacyClone(home))) {
|
|
191
|
+
return { root: r, source: 'legacy-clone', topology: classifyTopology(r, 'legacy-clone') };
|
|
192
|
+
}
|
|
193
|
+
return { root: null, source: 'not-found', topology: 'not-found' };
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
module.exports = { resolveActivePluginRoot, classifyTopology };
|
|
197
|
+
|
|
198
|
+
// CLI entry point.
|
|
199
|
+
if (require.main === module) {
|
|
200
|
+
const out = resolveActivePluginRoot();
|
|
201
|
+
if (process.argv.includes('--json')) {
|
|
202
|
+
process.stdout.write(JSON.stringify(out) + '\n');
|
|
203
|
+
} else if (out.root) {
|
|
204
|
+
process.stdout.write(out.root + '\n');
|
|
205
|
+
}
|
|
206
|
+
process.exit(out.root ? 0 : 1);
|
|
207
|
+
}
|