@mindrian_os/install 1.13.0-beta.11 → 1.13.0-beta.12

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.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "mos",
3
3
  "description": "MindrianOS -- Your AI innovation co-founder. Larry thinks with you through PWS methodology, builds your Data Room as you explore, and chains frameworks intelligently. Install and go.",
4
- "version": "1.13.0-beta.11",
4
+ "version": "1.13.0-beta.12",
5
5
  "author": {
6
6
  "name": "Jonathan Sagir",
7
7
  "url": "https://mindrian.ai"
package/CHANGELOG.md CHANGED
@@ -9,17 +9,40 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
9
9
  <!-- When onboarding: true, the onboard_steps list is shown to returning users in the What's New flow -->
10
10
  <!-- This allows new releases to automatically surface relevant guidance without code changes -->
11
11
 
12
- ## [Unreleased] -- v1.13.0-beta.11 (in progress)
12
+ ## [1.13.0-beta.12] - 2026-05-12
13
13
 
14
- beta.11 is the v1.13.0 CAPSTONE -- its headline content is the **Workflow Layer** (framework <-> command registry + reliable invocation; spec at `.planning/WORKFLOW-LAYER-SPEC.md`), not yet built. The items below are what has landed on `main` toward the capstone so far; the entry will be finalized (dated, full feature list) when the Workflow Layer ships. The capstone was re-numbered from beta.10 to beta.11 on 2026-05-12: `1.13.0-beta.10` had already been published to npm that day as a token-validation build (dist-tag `@next`, no git tag, not on the marketplace), so the tagged + marketplace'd capstone takes the next number.
14
+ The v1.13.0 CAPSTONE release -- headline content is the **Workflow Layer** (Phase 122: framework <-> command registry + reliable invocation; spec at `.planning/WORKFLOW-LAYER-SPEC.md`, doc at `docs/WORKFLOWS.md`) plus the npm-installer overhaul (`npx @mindrian_os/install` is now a real one-command installer), the `@mindrian_os/cli` -> `@mindrian_os/install` rename, and the install-machinery fixes a Windows live test surfaced (doctor/update path resolution, the statusline pre-release blind spot, and the single plugin-root resolver that retires that whole bug family). Version trail to here: `1.13.0-beta.10` (a token-validation npm publish on 2026-05-12 -- now deprecated), `1.13.0-beta.11` (the real npm installer + the package rename, npm-only -- now deprecated, doctor path bug), `1.13.0-beta.12` (this release: the capstone, tagged `v1.13.0-beta.12`, marketplace `source.ref` pinned, `@mindrian_os/install` published with the `@next` dist-tag).
15
15
 
16
16
  ### Added
17
17
 
18
+ - **The Workflow Layer (Phase 122) -- the framework-to-command registry + reliable invocation.** Larry can now turn "the methodology suggests framework X" into "run `/mos:x`" as a CI-enforced guarantee, not model recall:
19
+ - **`data/command-registry.json`** -- the generated, committed framework-to-command registry (`{ ontology_ref, commands[], framework_index, curated_chains[] }`), built from each `commands/*.md` frontmatter; never hand-edited. Plus `data/framework-names.json` -- the FEEDS_INTO-linked Brain `:Framework` name slice (+ a small curated whitelist), the only Brain-derived artifact in this loop.
20
+ - **`scripts/build-command-registry.cjs`** -- the generator + the `--check` drift tripwire (fails on a stale registry or an unresolvable framework name) + `--refresh-names` (a read-only build-time Brain query that snapshots the allowlist). The `--check` is wired into the pre-commit hook (when any `commands/*.md` / `data/command-registry.json` / `data/framework-names.json` is staged) and the Feynman test runner.
21
+ - **`lib/workflow/command-resolver.cjs`** -- the SOLE deterministic framework-to-command door (`commandsForFramework`, `frameworksForCommand`, `composeWorkflow`, `validateChainAutonomy`); reads only `data/command-registry.json`; zero Brain calls; degrades to empty results / `{ command: null, optional: true }` on a missing registry or a command-less framework (degrade, do not fabricate).
22
+ - **`lib/brain/chain-recommender.cjs`** -- `recommendFrameworkChain({problemType?, currentFramework?, roomState?}) -> [frameworkName]` via the Brain's `FEEDS_INTO` traversal (framework names + problem-type enums only, never a command string, never user content); degrades to `[seed]`.
23
+ - **The five new `/mos:` command frontmatter keys** -- `kind` (`methodology | utility | meta`), `frameworks[]` (the exact Brain `:Framework` name(s)), `produces`, `inputs`, `autonomous_safe` -- retrofitted across 44 commands (the algorithmic cohort first). Contract: `docs/COMMAND-FRONTMATTER.md`.
24
+ - **`/mos:pipeline --from-problem-type <x>` / `--from-framework <x>`** -- Brain-derive the chain, compose commands, print the `/mos:` run order. **`/mos:act --chain`** -- runs the composed workflow but `validateChainAutonomy` first and STOPS at the first non-`autonomous_safe` (or command-less) step with a "needs you here" gate (the Canon Part 3 "human confirms" clause made literal). **`/mos:suggest-next`** -- now returns a step-numbered command sequence, not just a framework list.
25
+ - **The pre-commit registry-drift tripwire** -- `build-command-registry.cjs --check` runs in `.git/hooks/pre-commit`; the Feynman runner runs it too.
26
+ - **`docs/WORKFLOWS.md`** -- the Brain <-> registry <-> Larry join, the five reliability rules, the Canon Part 8 boundary (commands never enter the Brain -- no `Command` node, ever), and the resolver/recommender surface. `docs/THE-BRAIN.md` and `docs/CANON-PHASE-MAP.md` point at it.
27
+ - **`lib/memory/workflow-layer-e2e.test.cjs`** -- walks frontmatter -> `build-command-registry --check` -> `resolver.composeWorkflow(the spec's acceptance example)` -> the command-less degrade case -> the `validateChainAutonomy` stop-point, then runs the Canon Part 8 zero-Brain-mutation grep sweep. Registered in the Feynman runner + `tests/run-all-122.sh`.
18
28
  - **`npx @mindrian_os/install` is a real one-command installer, not a printout.** Previously `npx @mindrian_os/cli install` only echoed the marketplace commands for the user to paste into Claude Code by hand ("no side effects, just guidance"). It now drives Claude Code's own plugin CLI: it checks that `claude` is on PATH (and prints how to install Claude Code if not), runs `claude plugin marketplace add jsagir/mindrian-marketplace`, then `claude plugin install mos@mindrian-marketplace`. Running it with no subcommand -- or with only flags, e.g. `npx @mindrian_os/install --version 1.13.0-beta.9` -- does the install; flags pass through to `claude plugin install`. `doctor` and `update` are still explicit subcommands. The Brain key stays a printed hint -- writing it to the environment is the one side effect left to the user. This unblocks un-gating the npm-quick-install card on the install site (`mindrianos-install-site.vercel.app`). (`bin/cli.js`.)
29
+ - Post-beta.11 follow-up (2026-05-12, after a Windows live test): `mindrian-os doctor` / `update` were resolving the plugin at the legacy `~/.claude/plugins/mindrian-os/` path, which does not exist for a `claude plugin install` -- the plugin is named `mos` and lives at `~/.claude/plugins/cache/<marketplace>/mos/<version>/`. So `npx @mindrian_os/install doctor` was throwing a raw node `MODULE_NOT_FOUND` stack. `bin/cli.js` now resolves the plugin root in order (MINDRIAN_OS_ROOT -> newest marketplace-cache `mos/<version>/` with a `scripts/doctor.cjs` -> legacy clone -> not-found), prints a plain "not installed -- run `npx @mindrian_os/install`" message when truly absent, and `update` uses `claude plugin marketplace update` + `claude plugin update mos@mindrian-marketplace` for a marketplace install (the `git pull` + `install.sh` path is kept only for a dev clone / MINDRIAN_OS_ROOT). The `install` flow also now runs `claude plugin marketplace update` then `claude plugin install` + `claude plugin update` (so an already-installed plugin gets moved to the current ref rather than just reported "already installed", which was the misleading message in the Windows test where the version actually moved 1.12.0 -> 1.13.0-beta.9). Lands in the next `@mindrian_os/install` npm publish.
30
+ - Same Windows test surfaced a pre-release blind spot in `scripts/statusline-mos` (the self-healing statusline resolver): it picked the "latest" cache version with `grep -E '^[0-9]+\.[0-9]+\.[0-9]+$'`, which rejects `-beta.N` suffixes. A box with `1.12.0` + `1.13.0-beta.9` in the marketplace cache picked `1.12.0`, rendered the stale statusline from there, and exported `MINDRIAN_OS_ROOT` pointing at it -- so the version banner / room-context lookup / focus glyph were all computed from the wrong version. Widened the anchor to `^[0-9]+(\.[0-9]+)+(-[A-Za-z0-9.]+)?$` (`sort -V` already orders pre-releases correctly: `1.12.0 < 1.13.0-beta.9 < 1.13.0`). Ships in the next plugin release; the deployed `~/.claude/statusline-mos` (or the `register_statusline` block in `~/.claude/settings.json` that points at `<install-dir>/scripts/statusline-mos`) picks it up when the plugin re-stamps on next install/update. Immediate workaround on an affected box: delete the stale lower-version cache dir under `~/.claude/plugins/cache/mindrian-marketplace/mos/`.
31
+ - Root-cause fix (the three above were band-aids on three independent guessers): **`lib/core/active-plugin-root.cjs`** -- the ONE plugin-root resolver. Precedence: `MINDRIAN_OS_ROOT` env -> `~/.claude/plugins/installed_plugins.json` (Claude Code's own registry of the *active* `mos@mindrian-marketplace` install; temporal truth -- right even when "highest semver" isn't the active version) -> newest pre-release-tolerant `~/.claude/plugins/cache/<marketplace>/mos/<version>/` -> legacy `~/.claude/plugins/mindrian-os/` -> not-found. Usable as a module (`resolveActivePluginRoot()`) and as a CLI (`node active-plugin-root.cjs` prints the path; `--json` for `{root, source}`). `bin/cli.js` (doctor/update) now delegates to it; `scripts/statusline-mos` shells out to its CLI form (with the cache-scan as a fallback for older deployed copies of the wrapper). Reads LOCAL files only (Canon Part 8). Still TODO (separate, lower-risk pass): have `scripts/session-start` re-stamp the `register_statusline` block in `~/.claude/settings.json` from this resolver on every run, so the deployed wrapper / settings pointer can never drift.
19
32
 
20
33
  ### Changed
21
34
 
22
- - **npm package renamed (twice): `@mindrian/os` -> `@mindrian_os/cli` -> `@mindrian_os/install`.** First rename (2026-05-11): the `@mindrian` npm scope never existed (`{"error":"Scope not found"}`), so `@mindrian/os` could never be published; the maintainer created the `@mindrian_os` org and the package moved to `@mindrian_os/cli`; first npm publish was `@mindrian_os/cli@1.13.0-beta.10` on 2026-05-12 (a token-validation build, dist-tag `@next`, no git tag, not on the marketplace) -- now deprecated. Second rename (2026-05-12): `@mindrian_os/cli` implied "a CLI tool" / a guidance printer, which is exactly what it had been; once `install` became a real installer the package name should be the verb, so it moved to `@mindrian_os/install` -- `npx @mindrian_os/install` reads as "install MindrianOS". The `bin` entry stays `mindrian-os` (the post-install command for `doctor`/`update`). `package.json` + `.claude-plugin/plugin.json` are at `1.13.0-beta.11`; once `@mindrian_os/install@1.13.0-beta.11` is published, the v1.13.0 capstone (Phase 122 / Workflow Layer) takes the next number, `1.13.0-beta.12`. Forward-looking references that named `@mindrian_os/cli` (`scripts/release.sh`, `docs/install/PACKAGING-PATHS.md`, `tests/manual/95.6-windows-cold-install-acceptance.md`, `tests/test-release-npm-gate.sh`, the install site's npm-quick-install card) need updating to `@mindrian_os/install`. (The `[1.13.0-beta.9]` entry below is left intact as the historical record of the pre-rename release -- which shipped to GitHub and the marketplace as `v1.13.0-beta.9` but was never published to npm.)
35
+ - **`/mos:suggest-next` returns a command sequence**, not just a framework list; **`framework-chain-composer.proposeNextFramework` routes through `lib/workflow/command-resolver.cjs`** (the only door -- `command:null` degrade for a command-less next framework); **the `pws-methodology` and `brain-connector` skills point at the resolver** (framework routing goes through `command-resolver.commandsForFramework` / `composeWorkflow`, never a `/mos:` named from memory). **The three remaining hand-maintained framework-to-command maps were pruned:** `framework-chain-composer.FRAMEWORK_TO_COMMAND_SLUG` is now an empty back-compat export, `lib/hmi/jtbd-taxonomy.json:methodology_hooks` is marked informational-only (the resolver is authoritative), and `references/methodology/index.md` is now just a pointer to `docs/COMMAND-FRONTMATTER.md` / `data/command-registry.json` / `docs/WORKFLOWS.md` -- it no longer hand-maintains a routing table.
36
+ - **npm package renamed (twice): `@mindrian/os` -> `@mindrian_os/cli` -> `@mindrian_os/install`.** First rename (2026-05-11): the `@mindrian` npm scope never existed (`{"error":"Scope not found"}`), so `@mindrian/os` could never be published; the maintainer created the `@mindrian_os` org and the package moved to `@mindrian_os/cli`; first npm publish was `@mindrian_os/cli@1.13.0-beta.10` on 2026-05-12 (a token-validation build, dist-tag `@next`, no git tag, not on the marketplace) -- now deprecated. Second rename (2026-05-12): `@mindrian_os/cli` implied "a CLI tool" / a guidance printer, which is exactly what it had been; once `install` became a real installer the package name should be the verb, so it moved to `@mindrian_os/install` -- `npx @mindrian_os/install` reads as "install MindrianOS". The `bin` entry stays `mindrian-os` (the post-install command for `doctor`/`update`). `package.json` + `.claude-plugin/plugin.json` ship as `1.13.0-beta.12` (this release); the npm-only intermediate publishes `@mindrian_os/cli@1.13.0-beta.10` and `@mindrian_os/install@1.13.0-beta.11` are deprecated. The install site's npm-quick-install card already names `@mindrian_os/install`; `scripts/release.sh` still says `@mindrian_os/cli` (Step 9.5) and only handles clean `X.Y.Z` bumps (it choked on the pre-release version, so this release was hand-rolled per the CLAUDE.md release process) -- both worth a follow-up. `docs/install/PACKAGING-PATHS.md`, `tests/manual/95.6-windows-cold-install-acceptance.md`, `tests/test-release-npm-gate.sh` still name `@mindrian_os/cli` and need updating to `@mindrian_os/install`. (The `[1.13.0-beta.9]` entry below is left intact as the historical record of the pre-rename release -- which shipped to GitHub and the marketplace as `v1.13.0-beta.9` but was never published to npm.)
37
+
38
+ ### Fixed
39
+
40
+ - **The hallucinated-command failure mode.** Larry could name a non-existent or semantically wrong command -- e.g. `/mos:jtbd` for the JTBD *methodology* when `/mos:analyze-needs` is the framework command (`/mos:jtbd` is the active-JTBD management command, not a methodology runner). With the Workflow Layer, every command Larry surfaces comes back from `lib/workflow/command-resolver.cjs` reading the generated registry -- `composeWorkflow(["Jobs to Be Done (JTBD)"])` returns `/mos:analyze-needs`. A hallucinated command cannot be emitted.
41
+ - **A latent Canon Part 8 breach in prose.** `skills/brain-connector/SKILL.md` carried dead "Brain has Command nodes linked to Frameworks ... `brain_proactive_command` ... `FOLLOWS_FRAMEWORK -> Command`" prose, and `references/brain/command-triggers-schema.md` was a whole dead "commands are first-class Neo4j nodes" schema doc -- both asserted that plugin commands live in the Brain, which the live Brain never implemented (no `Command` label) and which Canon Part 8 forbids. Both were deleted; the `command-triggers-schema.md` path now carries a `REMOVED` tombstone pointing at the Workflow Layer. The `lib/memory/workflow-layer-e2e.test.cjs` grep sweep now fails the build if a `Command`-node assertion ever returns anywhere in `skills/`, `agents/`, or `references/`.
42
+
43
+ ### Maintainer Notes
44
+
45
+ - **Release steps (maintainer-gated -- NOT performed in this phase):** cut the `v1.13.0-beta.11` tag, pin `~/mindrian-marketplace/.claude-plugin/marketplace.json` `source.ref` to the tag, and `npm publish @mindrian_os/install` with the `@next` dist-tag -- per the CLAUDE.md release process and the `feedback_release_lockstep_npm` rule (every plugin release publishes the npm package in lockstep). Phase 122 only finalized this CHANGELOG block and shipped the Workflow Layer code/docs/tests; it did not bump any version, did not `git tag`, did not `npm publish`, and did not edit `marketplace.json`.
23
46
 
24
47
  ### Notes
25
48
 
package/bin/cli.js CHANGED
@@ -10,53 +10,83 @@
10
10
  * flags, e.g. `npx @mindrian_os/install --version 1.13.0-beta.9`) does the
11
11
  * install. `doctor` and `update` are still explicit subcommands.
12
12
  *
13
- * install Path B. Actually install MindrianOS by driving Claude Code's
14
- * own plugin CLI: registers the Mindrian marketplace, then runs
15
- * `claude plugin install mos@mindrian-marketplace`. Requires the
16
- * `claude` CLI on PATH (prints how to get it if missing). Any
17
- * flags after `install` pass through to `claude plugin install`
18
- * (e.g. `mindrian-os install --version 1.13.0-beta.9`). The Brain
19
- * key stays a printed hint -- writing it to the environment is the
20
- * one side effect we leave to the user.
21
- * doctor Path C. Run /mos:doctor's diagnostic logic from OUTSIDE Claude
22
- * Code so users catch install/drift problems before a session.
23
- * Spawns `node <pluginRoot>/scripts/doctor.cjs` with any extra
24
- * args passed through (e.g. `mindrian-os doctor --all --fix`).
25
- * Exits with doctor.cjs's exit code.
26
- * update Mirror /mos:update. `git -C <pluginRoot> pull --ff-only`, then
27
- * re-run `bash <pluginRoot>/install.sh` to re-register agents,
28
- * hooks, settings.json, and the statusLine block.
13
+ * install Install (or bring current) MindrianOS by driving Claude Code's
14
+ * own plugin CLI: registers the Mindrian marketplace, refreshes the
15
+ * catalog, then `claude plugin install` + `claude plugin update` on
16
+ * mos@mindrian-marketplace. Requires the `claude` CLI on PATH (prints
17
+ * how to get it if missing). Flags pass through to `claude plugin
18
+ * install` (e.g. `--version 1.13.0-beta.9`); when a version is pinned
19
+ * the update step is skipped. The Brain key stays a printed hint --
20
+ * writing it to the environment is the one side effect left to you.
21
+ * doctor Run /mos:doctor's diagnostic from OUTSIDE Claude Code so you catch
22
+ * install/drift problems before a session. Resolves the installed
23
+ * plugin (marketplace cache, dev clone, or MINDRIAN_OS_ROOT), then
24
+ * `node <pluginRoot>/scripts/doctor.cjs <args...>`. If the plugin is
25
+ * not installed, says so plainly instead of throwing a node stack.
26
+ * update Bring MindrianOS current. For a marketplace install: `claude plugin
27
+ * marketplace update` + `claude plugin update mos@mindrian-marketplace`.
28
+ * For a dev clone (MINDRIAN_OS_ROOT set): `git -C <root> pull --ff-only`
29
+ * + `bash <root>/install.sh`.
29
30
  *
30
31
  * GSD pattern: pure CJS, node built-ins only, zero npm deps. No CLI framework
31
32
  * (no commander/yargs/meow). process.argv switch-case routing, mirroring
32
33
  * bin/mindrian-tools.cjs and ~/.claude/get-shit-done/bin/gsd-tools.cjs.
33
34
  *
34
- * PLUGIN_ROOT resolution: MINDRIAN_OS_ROOT env var if set (tests, dev boxes),
35
- * else the canonical install cache at ~/.claude/plugins/mindrian-os.
35
+ * Plugin-root resolution is delegated to lib/core/active-plugin-root.cjs (the
36
+ * single source of truth: MINDRIAN_OS_ROOT -> installed_plugins.json -> newest
37
+ * marketplace-cache mos/<version>/ -> legacy clone -> not-found). doctor and
38
+ * update both read from it; scripts/statusline-mos shells out to its CLI form.
36
39
  */
37
40
 
38
41
  const { spawnSync } = require('node:child_process');
39
42
  const path = require('node:path');
40
43
  const os = require('node:os');
44
+ const fs = require('node:fs');
45
+ // The ONE plugin-root resolver: installed_plugins.json (temporal truth) first,
46
+ // then the pre-release-tolerant marketplace-cache scan, then a legacy clone.
47
+ const { resolveActivePluginRoot } = require('../lib/core/active-plugin-root.cjs');
41
48
 
42
- const PLUGIN_ROOT = process.env.MINDRIAN_OS_ROOT
43
- || path.join(os.homedir(), '.claude', 'plugins', 'mindrian-os');
49
+ const MARKETPLACE = 'jsagir/mindrian-marketplace';
50
+ const PLUGIN_SPEC = 'mos@mindrian-marketplace';
44
51
 
45
52
  function run(cmd, args, opts) {
46
53
  return spawnSync(cmd, args, { stdio: 'inherit', ...opts });
47
54
  }
48
55
 
56
+ function ok(result) {
57
+ return result && typeof result.status === 'number' && result.status === 0;
58
+ }
59
+
49
60
  function exitFrom(result) {
50
61
  // spawnSync sets status=null when the process was killed by a signal or
51
62
  // failed to launch; treat that as a generic failure.
52
63
  process.exit(result && typeof result.status === 'number' ? result.status : 1);
53
64
  }
54
65
 
66
+ function hasDoctor(dir) {
67
+ try {
68
+ return !!dir && fs.existsSync(path.join(dir, 'scripts', 'doctor.cjs'));
69
+ } catch {
70
+ return false;
71
+ }
72
+ }
73
+
55
74
  function printUsage() {
56
75
  console.log('mindrian-os <install|doctor|update> (no subcommand = install)');
57
- console.log(' install install MindrianOS via Claude Code (adds the marketplace, installs the mos plugin)');
58
- console.log(' doctor run the MindrianOS install/drift diagnostic (Path C; passes flags through to /mos:doctor)');
59
- console.log(' update pull the latest plugin and re-run install registration');
76
+ console.log(' install install / bring current via Claude Code (marketplace add + plugin install/update)');
77
+ console.log(' doctor run the MindrianOS install/drift diagnostic (passes flags through to /mos:doctor)');
78
+ console.log(' update bring MindrianOS current (marketplace update + plugin update; or git pull for a dev clone)');
79
+ }
80
+
81
+ function requireClaudeCli() {
82
+ const check = spawnSync('claude', ['--version'], { stdio: 'ignore' });
83
+ if (ok(check)) return true;
84
+ console.error('Claude Code is not installed (no `claude` command on your PATH).');
85
+ console.error('Install it first:');
86
+ console.error(' npm install -g @anthropic-ai/claude-code');
87
+ console.error('Then re-run:');
88
+ console.error(' npx @mindrian_os/install');
89
+ return false;
60
90
  }
61
91
 
62
92
  // No subcommand, or flags only (e.g. `npx @mindrian_os/install --version 1.13.0-beta.9`),
@@ -70,68 +100,95 @@ if (!sub || sub.startsWith('-')) {
70
100
 
71
101
  switch (sub) {
72
102
  case 'doctor': {
73
- // Path C: run /mos:doctor's logic from outside Claude Code.
74
- const doctorPath = path.join(PLUGIN_ROOT, 'scripts', 'doctor.cjs');
75
- const r = run(process.execPath, [doctorPath, ...process.argv.slice(3)]);
103
+ const { root } = resolveActivePluginRoot();
104
+ if (!root || !hasDoctor(root)) {
105
+ console.error('MindrianOS does not appear to be installed (could not find scripts/doctor.cjs).');
106
+ console.error('Looked under: ' + path.join(os.homedir(), '.claude', 'plugins') + (process.env.MINDRIAN_OS_ROOT ? ' and $MINDRIAN_OS_ROOT' : ''));
107
+ console.error('Install it first:');
108
+ console.error(' npx @mindrian_os/install');
109
+ process.exit(1);
110
+ }
111
+ const r = run(process.execPath, [path.join(root, 'scripts', 'doctor.cjs'), ...process.argv.slice(argOffset)]);
76
112
  exitFrom(r);
77
113
  break;
78
114
  }
79
115
 
80
116
  case 'update': {
81
- // Mirror /mos:update: fast-forward the plugin clone, then re-run install.sh
82
- // so agents, hooks, settings.json, and the statusLine block get re-stamped.
83
- run('git', ['-C', PLUGIN_ROOT, 'pull', '--ff-only']);
84
- const r = run('bash', [path.join(PLUGIN_ROOT, 'install.sh')]);
85
- exitFrom(r);
117
+ // Dev clone (MINDRIAN_OS_ROOT or a legacy hand-clone): git pull + re-run install.sh.
118
+ const { root } = resolveActivePluginRoot();
119
+ const isDevClone = !!process.env.MINDRIAN_OS_ROOT
120
+ || (root && fs.existsSync(path.join(root, '.git')) && fs.existsSync(path.join(root, 'install.sh')));
121
+ if (isDevClone && root) {
122
+ run('git', ['-C', root, 'pull', '--ff-only']);
123
+ const r = run('bash', [path.join(root, 'install.sh')]);
124
+ exitFrom(r);
125
+ break;
126
+ }
127
+ // Marketplace install: use Claude Code's own update path.
128
+ if (!requireClaudeCli()) process.exit(1);
129
+ console.log('Refreshing the marketplace catalog...');
130
+ run('claude', ['plugin', 'marketplace', 'update']);
131
+ console.log('Updating the MindrianOS plugin...');
132
+ const r = run('claude', ['plugin', 'update', PLUGIN_SPEC]);
133
+ if (!ok(r)) {
134
+ console.error('');
135
+ console.error('`claude plugin update ' + PLUGIN_SPEC + '` did not complete.');
136
+ console.error('Try inside Claude Code: /plugin marketplace update then /plugin update ' + PLUGIN_SPEC);
137
+ exitFrom(r);
138
+ }
139
+ console.log('');
140
+ console.log('MindrianOS is up to date. Run `claude plugin list` to confirm the version.');
141
+ console.log('Verify: mindrian-os doctor (or /mos:doctor inside Claude Code)');
142
+ process.exit(0);
86
143
  break;
87
144
  }
88
145
 
89
146
  case 'install': {
90
- // Path B: actually install MindrianOS by driving Claude Code's plugin CLI.
91
- // Registers the Mindrian marketplace, then `claude plugin install mos@...`.
92
147
  // Flags after `install` (or leading flags when `install` is implied) pass
93
- // through to `claude plugin install` (e.g. `... install --version 1.13.0-beta.9`).
148
+ // through to `claude plugin install` (e.g. `... --version 1.13.0-beta.9`).
94
149
  const passthrough = process.argv.slice(argOffset);
150
+ const pinned = passthrough.some((a) => a === '--version' || a.startsWith('--version='));
95
151
 
96
- // 1. Claude Code must be on PATH -- it does the actual plugin install.
97
- const claudeCheck = spawnSync('claude', ['--version'], { stdio: 'ignore' });
98
- if (!claudeCheck || claudeCheck.status !== 0) {
99
- console.error('Claude Code is not installed (no `claude` command on your PATH).');
100
- console.error('Install it first:');
101
- console.error(' npm install -g @anthropic-ai/claude-code');
102
- console.error('Then re-run:');
103
- console.error(' npx @mindrian_os/install');
104
- process.exit(1);
105
- }
152
+ if (!requireClaudeCli()) process.exit(1);
106
153
 
107
- // 2. Register the Mindrian marketplace. Best-effort: if it is already
108
- // registered Claude Code may exit non-zero with "already added" -- that
109
- // is fine, the install step below still works.
154
+ // Register the Mindrian marketplace (idempotent) and refresh its catalog so
155
+ // we resolve the current ref. Both are best-effort -- "already added" /
156
+ // "already up to date" exit non-zero on some Claude Code versions; fine.
110
157
  console.log('Adding the Mindrian marketplace...');
111
- run('claude', ['plugin', 'marketplace', 'add', 'jsagir/mindrian-marketplace']);
158
+ run('claude', ['plugin', 'marketplace', 'add', MARKETPLACE]);
159
+ console.log('Refreshing the marketplace catalog...');
160
+ run('claude', ['plugin', 'marketplace', 'update']);
112
161
 
113
- // 3. Install (or update) the plugin. This one's exit code matters.
162
+ // Install. On an already-installed plugin Claude Code reports "already
163
+ // installed" and still exits 0 -- the update step below then moves it
164
+ // forward to the current ref (unless the caller pinned a version).
114
165
  console.log('Installing the MindrianOS plugin...');
115
- const inst = run('claude', ['plugin', 'install', 'mos@mindrian-marketplace', ...passthrough]);
116
- if (!inst || inst.status !== 0) {
166
+ const inst = run('claude', ['plugin', 'install', PLUGIN_SPEC, ...passthrough]);
167
+ let updated = false;
168
+ if (!pinned) {
169
+ console.log('Bringing the plugin to the current build...');
170
+ const upd = run('claude', ['plugin', 'update', PLUGIN_SPEC]);
171
+ updated = ok(upd);
172
+ }
173
+
174
+ if (!ok(inst) && !updated) {
117
175
  console.error('');
118
- console.error('`claude plugin install mos@mindrian-marketplace` did not complete.');
176
+ console.error('Installing ' + PLUGIN_SPEC + ' did not complete.');
119
177
  console.error('Finish it by hand inside Claude Code:');
120
- console.error(' /plugin marketplace add jsagir/mindrian-marketplace');
121
- console.error(' /plugin install mos@mindrian-marketplace');
178
+ console.error(' /plugin marketplace add ' + MARKETPLACE);
179
+ console.error(' /plugin install ' + PLUGIN_SPEC);
122
180
  exitFrom(inst);
123
181
  }
124
182
 
125
- // 4. Done. Point at the Brain key + first run.
126
183
  console.log('');
127
- console.log('MindrianOS installed.');
184
+ console.log('MindrianOS is installed and current. Run `claude plugin list` to see the version.');
128
185
  console.log('');
129
186
  console.log('Optional -- connect the Brain for enriched intelligence:');
130
187
  console.log(' inside Claude Code: /mos:setup (choose "Configure Brain", paste your key)');
131
188
  console.log(' or set it directly: export MINDRIAN_BRAIN_KEY="<your-key>" (or add it to ~/.claude/.env)');
132
189
  console.log('');
133
190
  console.log('Verify: mindrian-os doctor (or /mos:doctor inside Claude Code)');
134
- console.log('Start: run `claude`, then /mos:onboard');
191
+ console.log('Start: open a fresh Claude Code session, then /mos:onboard');
135
192
  process.exit(0);
136
193
  break;
137
194
  }
package/commands/act.md CHANGED
@@ -211,9 +211,23 @@ Display the thinking trace (Step 4) and the execution plan following the dry-run
211
211
  Run /mos:act to execute this plan.
212
212
  ```
213
213
 
214
- ### Chain Mode (`/mos:act --chain`)
214
+ ### Chain Mode (`/mos:act --chain`) -- the autonomy gate
215
215
 
216
- 1. Select 3-5 frameworks using the chain selection logic:
216
+ **Before anything else in `--chain` mode, plan + autonomy-gate the chain through the resolver:**
217
+
218
+ ```bash
219
+ node "${CLAUDE_PLUGIN_ROOT}/scripts/act-command.cjs" --chain --room ./room
220
+ ```
221
+
222
+ The helper picks the framework chain for the room state via `lib/brain/chain-recommender.cjs` `recommendFrameworkChain` (a FEEDS_INTO traversal -- framework names + problem-type enums only; Canon Part 8: never a command string, never user content), composes it into `/mos:` commands via `lib/workflow/command-resolver.cjs` `composeWorkflow` (the SOLE framework -> command path, reading only the generated `data/command-registry.json`), calls `validateChainAutonomy(workflow)` FIRST, then walks the steps in order. At the FIRST step whose command is not `autonomous_safe: true` (or whose framework has no `/mos:` command at all), it STOPS and renders a "needs you here" gate (a Shape F.0 / E action report: "[GATE] Chain reached step N: /mos:x for <framework>. This step is not autonomous_safe -- it needs your eyes. [continue] [stop]"). You then:
223
+ - run the `autonomous_safe` prefix steps unattended (dispatch `agents/framework-runner.md` per step, with the checkpoint pause between steps as below),
224
+ - at the gate step, do NOT run it autonomously -- surface the gate to the user and wait. `[continue]` = the user runs that step themselves (or approves running it), then resume the chain from the next step. `[stop]` = halt; what ran above is filed.
225
+
226
+ You NEVER name a `/mos:` command in `--chain` mode from memory and you NEVER decide a step is safe to run unattended from memory -- the command came back from the resolver and the autonomy decision came back from `validateChainAutonomy` / `data/command-registry.json`'s `autonomous_safe` field. `--chain --from-framework <x>` / `--chain --problem-type <x>` seed the chain explicitly.
227
+
228
+ Then, for the steps the helper greenlit:
229
+
230
+ 1. Select 3-5 frameworks using the chain selection logic (the helper already did this via the recommender; this list is the same chain):
217
231
  - First framework: targets weakest section or most pressing gap
218
232
  - Subsequent frameworks: build on previous, guided by Brain `FEEDS_INTO` relationships or natural progression (Exploration -> Analysis -> Synthesis -> Validation)
219
233
  - Never select redundant frameworks
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: pipeline
3
3
  description: Chain a multi-step methodology pipeline
4
- argument-hint: [pipeline-name]
4
+ argument-hint: '[pipeline-name] [--from-problem-type <x>] [--from-framework <x>]'
5
5
  serves_jtbd: ["plan-execution"]
6
6
  # --- Phase 122 workflow-layer frontmatter ---
7
7
  kind: meta
@@ -20,6 +20,18 @@ allowed-tools:
20
20
 
21
21
  You are Larry. This command orchestrates multi-step methodology chains -- connected sequences where each framework's output feeds the next as structured input.
22
22
 
23
+ ## Brain-Derived Chains -- `--from-problem-type <x>` / `--from-framework <x>`
24
+
25
+ `/mos:pipeline --from-problem-type ill-defined` (or `--from-framework "Beautiful Question Framework"`) does NOT run a static named pipeline -- it Brain-derives the framework chain and runs the resolver-composed `/mos:` command sequence end to end:
26
+
27
+ ```bash
28
+ node "${CLAUDE_PLUGIN_ROOT}/scripts/pipeline-command.cjs" --from-problem-type ill-defined --room ./room
29
+ ```
30
+
31
+ The helper calls `lib/brain/chain-recommender.cjs` `recommendFrameworkChain` (a FEEDS_INTO traversal -- framework names + problem-type enums only; Canon Part 8: never a command string, never user content), composes that chain into `/mos:` commands via `lib/workflow/command-resolver.cjs` `composeWorkflow` (the SOLE framework -> command path, reading only the generated `data/command-registry.json`), and prints the run order. Then run the printed `/mos:` commands in sequence using the Stage Execution Loop machinery below -- one resolved command per step. For a step whose framework has no `/mos:` command, the helper prints "no /mos: for <framework> -- run it manually; continuing"; skip that step (or run the framework manually) and continue. Every command the helper prints exists in the registry -- the resolver only ever returns registered commands, so you never invoke a `/mos:` that does not exist.
32
+
33
+ `--from-problem-type` accepts the canonical `UDP` / `IDP` / `WDP` tokens and the `undefined` / `ill-defined` / `well-defined` aliases. With neither flag and no named pipeline, the helper falls back to the room's `ProblemType` from `room/STATE.md`.
34
+
23
35
  ## Brain Enhancement (Optional)
24
36
 
25
37
  Try calling Brain: first `mcp__mindrian-brain__brain_schema`, then `mcp__mindrian-brain__get_neo4j_schema` as fallback. If it succeeds, Brain mode is active. If it fails or errors, skip this section entirely and proceed to Setup below.
@@ -45,6 +57,9 @@ Proceed to Setup below with this additional context. Static chains remain the de
45
57
 
46
58
  ### Chain Selection
47
59
 
60
+ **If user passes `--from-problem-type <x>` or `--from-framework <x>`:**
61
+ Run the helper above (`scripts/pipeline-command.cjs`) to Brain-derive the chain and get the resolver-composed `/mos:` run order, then execute those commands in sequence via the Stage Execution Loop. This is the dynamic, graph-derived path -- no `CHAIN.md` file involved.
62
+
48
63
  **If user specifies a pipeline name** (e.g., `/mos:pipeline discovery`):
49
64
  Load `pipelines/{name}/CHAIN.md` and proceed to Stage 1.
50
65
 
@@ -17,9 +17,23 @@ allowed-tools:
17
17
 
18
18
  # /mos:suggest-next
19
19
 
20
- You are Larry. This command uses the Brain graph to recommend what the user should work on next based on their current room state and framework chains.
20
+ You are Larry. This command recommends what the user should work on next as a COMMAND SEQUENCE, not just a list of frameworks: it reads the room's ProblemType (and active JTBD), Brain-derives the framework chain, and composes that chain into the exact `/mos:` commands to run, in order.
21
21
 
22
- **Requires Brain MCP.** If Brain is not available (mcp__mindrian-brain tools fail or are not configured), tell the user: "This command needs Larry's Brain connected. Run `/mos:setup brain` to set it up." Then stop.
22
+ ## The resolver is the only door
23
+
24
+ Run the helper to get the resolver-composed command sequence:
25
+
26
+ ```bash
27
+ node "${CLAUDE_PLUGIN_ROOT}/scripts/suggest-next-command.cjs" --room ./room
28
+ ```
29
+
30
+ It reads `room/STATE.md` for the ProblemType / active JTBD (or pass `--problem-type <x>` / `--from-framework <x>` explicitly), calls `lib/brain/chain-recommender.cjs` `recommendFrameworkChain` (a FEEDS_INTO traversal -- framework names + problem-type enums only; Canon Part 8: never a command string, never user content), composes the chain into `/mos:` commands via `lib/workflow/command-resolver.cjs` `composeWorkflow` (the SOLE framework -> command path, reading only the generated `data/command-registry.json`), and prints BOTH the framework chain AND the step-numbered command sequence. A framework with no `/mos:` yet renders as "(no /mos: for this -- run it manually)" -- degrade, do not fabricate.
31
+
32
+ **Larry NEVER names a `/mos:` command from memory.** Every command you surface came back from the resolver via this helper. If you find yourself about to type a `/mos:` you have not seen the resolver return, stop -- run the helper first. Render in Shape B (Semantic Tree) per `skills/ui-system/SKILL.md`; do not invent a format.
33
+
34
+ When Brain is connected you may additionally weave the co-occurrence narrative below; when it is not, the helper still produces a true command sequence from the registry (framework-only advice degrades gracefully -- still through the resolver).
35
+
36
+ **Note on Brain MCP:** the deeper "similar venture patterns" enrichment below benefits from Brain. If Brain is not available, skip those queries -- the resolver-composed sequence above still stands.
23
37
 
24
38
  ## Setup
25
39
 
@@ -61,7 +75,7 @@ Combine both query results. Present 2-3 next steps ranked by:
61
75
  - Co-occurrence patterns from similar ventures
62
76
 
63
77
  For each recommendation:
64
- - **What to do:** Name the framework and the specific `/mos:` command to run
78
+ - **What to do:** Name the framework AND the specific `/mos:` command -- but take the command from the helper's resolver-composed sequence above (or `lib/workflow/command-resolver.cjs` `commandsForFramework(<framework>)`), never from memory. If a framework has no command, say "run <framework> manually -- there is no /mos: for it" rather than inventing one.
65
79
  - **Why this sequence:** Cite the relationship type from the graph (e.g., "Explore Domains FEEDS_INTO Analyze Needs with 0.85 confidence -- mapping the landscape first sharpens your customer discovery")
66
80
  - **What similar ventures did:** Reference co-occurrence data ("Projects that used Beautiful Question most commonly followed with Explore Domains or Map Unknowns")
67
81
 
@@ -0,0 +1,142 @@
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
+ function resolveActivePluginRoot() {
120
+ const envRoot = process.env.MINDRIAN_OS_ROOT;
121
+ if (envRoot) return { root: envRoot, source: 'MINDRIAN_OS_ROOT' };
122
+
123
+ const home = os.homedir();
124
+ let r;
125
+ if ((r = fromInstalledPlugins(home))) return { root: r, source: 'installed_plugins.json' };
126
+ if ((r = fromMarketplaceCache(home))) return { root: r, source: 'marketplace-cache' };
127
+ if ((r = fromLegacyClone(home))) return { root: r, source: 'legacy-clone' };
128
+ return { root: null, source: 'not-found' };
129
+ }
130
+
131
+ module.exports = { resolveActivePluginRoot };
132
+
133
+ // CLI entry point.
134
+ if (require.main === module) {
135
+ const out = resolveActivePluginRoot();
136
+ if (process.argv.includes('--json')) {
137
+ process.stdout.write(JSON.stringify(out) + '\n');
138
+ } else if (out.root) {
139
+ process.stdout.write(out.root + '\n');
140
+ }
141
+ process.exit(out.root ? 0 : 1);
142
+ }