mirror-mind 0.40.0 → 0.41.0
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/docs/releases/index.md +2 -0
- package/docs/releases/v0.40.1.md +120 -0
- package/docs/releases/v0.41.0.md +116 -0
- package/package.json +1 -1
- package/plugins/mirror-mind/.claude-plugin/plugin.json +2 -2
- package/plugins/mirror-mind/agy/pre-invocation.sh +24 -3
- package/plugins/mirror-mind/agy/session-start.sh +24 -3
- package/plugins/mirror-mind/agy/stop.sh +24 -3
- package/plugins/mirror-mind/hooks/log-assistant-reply.sh +24 -3
- package/plugins/mirror-mind/hooks/log-session-end.sh +24 -3
- package/plugins/mirror-mind/hooks/log-user-prompt.sh +24 -3
- package/plugins/mirror-mind/hooks/mirror-inject.sh +24 -3
- package/plugins/mirror-mind/hooks/session-start.sh +24 -3
- package/plugins/mirror-mind/mcp_config.json +1 -1
- package/plugins/mirror-mind/plugin.json +2 -2
- package/ts/src/frontDoor/mirrorModeRoute.ts +24 -2
- package/ts/src/guards/claudePlugin.ts +9 -2
- package/ts/src/mirror/defaultResolution.ts +21 -16
- package/ts/src/mirror/deliverContext.ts +35 -11
- package/ts/src/mirror/doorman.ts +86 -43
- package/ts/src/mirror/orchestration.ts +10 -1
- package/ts/src/mirror/personaRouter.ts +389 -0
- package/ts/src/providers/config.ts +25 -0
package/docs/releases/index.md
CHANGED
|
@@ -12,6 +12,8 @@ Future release notes should use the structure defined in [Release Notes](../proc
|
|
|
12
12
|
|
|
13
13
|
## Releases
|
|
14
14
|
|
|
15
|
+
- [v0.41.0 — A Persona When the Subject Asks for One](v0.41.0.md) — minor release of CR203 and CR204: a Mirror Mode turn's persona is routed by its subject, by a Decisions model through OpenRouter taken only above a threshold, else the Journey's `default_persona`, else none; a turn never inherits the session's persona, and a turn with none tells the model not to sign; every routing failure is none; one content-free `llm_calls` row per call; `mirror-mind/desktop` returns `personaName` beside the persona's id. No migration.
|
|
16
|
+
- [v0.40.1 — The Bins You Named, or Why Not](v0.40.1.md) — patch release of CR197: the plugin's hook wrappers and MCP entry use `MIRROR_BIN` only as an absolute path to a directory holding the bin they need, the rule `MIRROR_NODE` follows since v0.39.5; a relative value is never used, so a project's own `bin/mirror-hook` cannot run, and a refused value is named with its reason, once a day in `hooks.log` and on the MCP entry's stderr; the forward-slash `C:/…` form Windows machines set counts as absolute, in CI under Git Bash. Also carries CV9's re-baseline (documents only). No migration.
|
|
15
17
|
- [v0.40.0 — A Reply Stays in Its Own Conversation, and a Journey Can Go](v0.40.0.md) — minor release closing the first release train: CR151, a session's transcript fills only the conversation that session is bound to, each reply stamped with its transcript time, where it used to fill every open reply-less conversation in the transcript's window, another session's included; a transcript no session owns backfills nothing; and CR200, previewed as v0.40.0-next.1, `mirror-mind/desktop` deletes a Journey with every record that belongs to it, by digest, behind a verified backup. Update every install that has a home. No migration.
|
|
16
18
|
- [v0.40.0-next.1 — A Journey Can Go, Showing What Goes With It](v0.40.0-next.1.md) — preview under npm's `next`, read by no updater, for Nautilus for Mirror (the release train): CR200, `mirror-mind/desktop` gains `previewJourneyDeletion` and `deleteJourneyWithRecords`, deleting a Journey with every record that belongs to it after a preview that counts it, by digest, behind a verified backup; child Journeys refuse. Terminal users receive it with v0.40.0. No migration.
|
|
17
19
|
- [v0.39.5 — The Node You Named, or Why Not](v0.39.5.md) — patch release of CR192, from PR #47's thread (@rodrigoimmaginario): the hook wrappers, the plugin's MCP entry, and `runtime diagnose` use `MIRROR_NODE` only as an absolute path to an executable file, as the Pi extension does; a relative value is never run, and a refused one is named with its reason, once a day in `hooks.log` and on the MCP entry's stderr; the Windows installer's drive-letter path counts as absolute, in CI under Git Bash and on the installer. No migration.
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
---
|
|
2
|
+
digest: >
|
|
3
|
+
v0.40.1 is a patch release of one Change Request, CR197. The plugin's hook wrappers and
|
|
4
|
+
MCP entry now use `MIRROR_BIN` only when it is an absolute path to a directory holding the
|
|
5
|
+
bin they need, the rule `MIRROR_NODE` follows since v0.39.5. A relative value could make
|
|
6
|
+
a plugin hook run a JavaScript file from the project the runtime was opened in, and a
|
|
7
|
+
value that no longer held `mirror-hook` was dropped without a word. Now neither is used,
|
|
8
|
+
and the refused value is named with its reason. The forward-slash drive form Windows
|
|
9
|
+
machines set for the Claude Code forwarders counts as absolute, proven in CI under Git
|
|
10
|
+
Bash. No schema change, no migration, and no model or prompt change.
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
# v0.40.1 — The Bins You Named, or Why Not
|
|
14
|
+
|
|
15
|
+
**Date:** 2026-11-07
|
|
16
|
+
|
|
17
|
+
## Highlights
|
|
18
|
+
|
|
19
|
+
- **A relative `MIRROR_BIN` is never used**
|
|
20
|
+
([CR197](../project/refinement/rs010-cv22-oracle-and-port-hygiene/cr197-the-hook-wrappers-and-the-mcp-locator-accept-a-relative-mirror-bin.md)).
|
|
21
|
+
`MIRROR_BIN` names the directory holding `mirror` and `mirror-hook`, for the plugin's
|
|
22
|
+
hook wrappers and MCP entry, which a runtime copies into its own cache. A runtime starts
|
|
23
|
+
them from the project it was opened in, and both readers took any value that passed
|
|
24
|
+
`[ -x ]` there: with `MIRROR_BIN=bin`, Node ran the project's `bin/mirror-hook` as
|
|
25
|
+
JavaScript on every plugin hook, and its `bin/mirror` at every MCP server start. Both
|
|
26
|
+
readers now use the value only as an absolute path to a directory holding the bin they
|
|
27
|
+
need as an executable file.
|
|
28
|
+
- **A value that is not used is named, with its reason.** A value that no longer held the
|
|
29
|
+
bin, such as a prefix left behind by a Node switch, used to be dropped without a word.
|
|
30
|
+
Now the hook wrappers write
|
|
31
|
+
`MIRROR_BIN=bin is relative; searching the PATH and the usual places instead` to
|
|
32
|
+
`<mirror home>/hooks.log`, once a UTC day for the same value, and the MCP entry writes
|
|
33
|
+
the same message to its stderr. The reasons are `relative`, `missing`,
|
|
34
|
+
`not a directory`, and `a directory without an executable mirror-hook` (or `mirror`).
|
|
35
|
+
The search then goes on through the PATH and the usual places, as before.
|
|
36
|
+
- **The Windows form counts as absolute.** The rule uses `MIRROR_NODE`'s pattern, so
|
|
37
|
+
`C:/Users/<you>/.mirror/bin`, the forward-slash form Windows machines set for the Claude
|
|
38
|
+
Code forwarders, is absolute, and a drive-relative `C:bin` is refused as `relative`. The
|
|
39
|
+
Windows CI job runs a plugin wrapper with that form under Git Bash.
|
|
40
|
+
- No schema change, no migration, and no model or prompt change.
|
|
41
|
+
|
|
42
|
+
## Where We Started
|
|
43
|
+
|
|
44
|
+
v0.39.5 gave `MIRROR_NODE` its rule
|
|
45
|
+
([CR192](../project/refinement/rs010-cv22-oracle-and-port-hygiene/cr192-the-hook-wrappers-and-the-mcp-locator-accept-a-relative-mirror-node.md)),
|
|
46
|
+
and its review found the same exposure one variable over: with `MIRROR_BIN=bin`, the
|
|
47
|
+
plugin's prompt hook made Node run the project's `bin/mirror-hook`, probed on 2026-11-04.
|
|
48
|
+
It was captured as CR197 and left out of v0.39.5 on purpose. On 2026-11-07 the CV9
|
|
49
|
+
re-baseline decided to ship it as a patch ahead of the 1.0 gate.
|
|
50
|
+
|
|
51
|
+
## What Changed
|
|
52
|
+
|
|
53
|
+
**One rule, two variables.** The plugin's bin and agy wrappers, generated by
|
|
54
|
+
`scripts/generate_hook_wrappers.sh`, and the MCP locator in
|
|
55
|
+
`ts/src/guards/claudePlugin.ts` test `MIRROR_BIN` with `ABSOLUTE_PATH_CASE`, the pattern
|
|
56
|
+
`MIRROR_NODE` uses, and a test fails if the copies drift. The bin search now carries the
|
|
57
|
+
pattern's backslashes, so the generator splices it by the text around its placeholder, as
|
|
58
|
+
it does the other shared fragments. The eight plugin wrappers are regenerated; the five
|
|
59
|
+
tree-form wrappers under `.claude/hooks/` read no `MIRROR_BIN` and are unchanged.
|
|
60
|
+
|
|
61
|
+
**A refusal that cannot forge a line.** Line breaks are removed from the value before it is
|
|
62
|
+
logged, as for `MIRROR_NODE`. The locator reads `$MIRROR_BIN` once, where it read it three
|
|
63
|
+
times, because Grok pastes a set variable's value into the MCP arguments it loads.
|
|
64
|
+
|
|
65
|
+
**Tests and CI.** The new tests ran the project's planted `bin/mirror-hook` and
|
|
66
|
+
`bin/mirror` before the fix. They cover each reason, the daily rule, a forged line break,
|
|
67
|
+
both variables refused in one run, the agy form, and a valid value that outranks the PATH.
|
|
68
|
+
The Windows job's Git Bash step runs a plugin wrapper over a prefix shaped like npm's on
|
|
69
|
+
Windows, with `MIRROR_BIN` in the forward-slash drive form.
|
|
70
|
+
|
|
71
|
+
**Docs.** The rule is stated once, in full, in the
|
|
72
|
+
[configuration reference](../reference/configuration.md#hook-bin-resolution), and
|
|
73
|
+
`REFERENCE.md` points there. Troubleshooting shows the refusal line and the fix, and no
|
|
74
|
+
longer says `MIRROR_BIN` is honored as given. Getting Started says the directory is given
|
|
75
|
+
as an absolute path.
|
|
76
|
+
|
|
77
|
+
Validated by the Navigator in a scratch home: two runs of the plugin's prompt hook with
|
|
78
|
+
`MIRROR_BIN=bin` wrote one line,
|
|
79
|
+
|
|
80
|
+
```text
|
|
81
|
+
2026-11-07T19:11:56Z claude:user-prompt: MIRROR_BIN=bin is relative; searching the PATH and the usual places instead
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
the MCP entry printed the same refusal and answered `initialize`, the project's planted
|
|
85
|
+
bins never ran, the prompt was logged, and `/opt/homebrew/bin`, the value his own Claude
|
|
86
|
+
settings set, was used with no refusal.
|
|
87
|
+
|
|
88
|
+
**Also in this release.** CV9's re-baseline
|
|
89
|
+
([CV9.DS10](../project/roadmap/cv9-mirror-1-0/cv9-ds10-the-1-0-done-condition-describes-the-system-that-exists/index.md)),
|
|
90
|
+
which changes documents only: the 1.0 done condition is now a table of claims, each with
|
|
91
|
+
the story that owns it.
|
|
92
|
+
|
|
93
|
+
## Conscious Exclusions
|
|
94
|
+
|
|
95
|
+
- `runtime diagnose` does not name a refused `MIRROR_BIN`. It counts the hooks' line as
|
|
96
|
+
`hook_failures_recorded`, and the value and its reason are in `hooks.log`. Deferred to
|
|
97
|
+
[CR198](../project/refinement/rs010-cv22-oracle-and-port-hygiene/cr198-runtime-diagnose-does-not-name-a-refused-mirror-node.md),
|
|
98
|
+
which now covers both variables.
|
|
99
|
+
- A relative entry in your own `PATH`, such as `.`: `command -v` finds a project's file
|
|
100
|
+
through it for every command you run, not only Mirror's.
|
|
101
|
+
- An absolute value is trusted wherever it points: whoever sets it chooses what runs.
|
|
102
|
+
- Rodrigo Immaginario's installer run against `main` was asked for and does not gate this
|
|
103
|
+
tag; the Windows CI job is this release's Windows evidence.
|
|
104
|
+
|
|
105
|
+
## Upgrading
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
mirror runtime update
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
If you set `MIRROR_BIN` by hand, make it the absolute path of the directory holding
|
|
112
|
+
`mirror-hook`, `$(npm prefix -g)/bin`: a relative one is now refused, with one line a day
|
|
113
|
+
in `hooks.log`. A plugin copied into a runtime's cache keeps the old wrappers until the
|
|
114
|
+
runtime takes the update; `runtime diagnose` reports a copy that is behind.
|
|
115
|
+
|
|
116
|
+
## Next Horizon
|
|
117
|
+
|
|
118
|
+
CR198, so that `runtime diagnose` names a refused `MIRROR_NODE` or `MIRROR_BIN`; CV9.DS11,
|
|
119
|
+
journeys that can be reshaped without losing a record; and the rest of the way to 1.0 in
|
|
120
|
+
the [CV9 index](../project/roadmap/cv9-mirror-1-0/index.md).
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
---
|
|
2
|
+
digest: >
|
|
3
|
+
v0.41.0 routes each Mirror Mode turn's persona by what the turn is about, and lets a turn
|
|
4
|
+
have none. A Decisions model chooses among your personas and says whether the subject
|
|
5
|
+
belongs to any of them; its choice is taken only above a threshold, else the persona the
|
|
6
|
+
turn's Journey names as its default, else none. A turn no longer inherits the session's
|
|
7
|
+
persona, and a turn with none tells the model not to sign. `mirror-mind/desktop` also hands
|
|
8
|
+
its host the persona's own name beside its id. One new model call per turn, made beside
|
|
9
|
+
the embedding the turn already makes. No schema change, no migration.
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# v0.41.0 — A Persona When the Subject Asks for One
|
|
13
|
+
|
|
14
|
+
**Date:** 2026-11-08
|
|
15
|
+
|
|
16
|
+
## Highlights
|
|
17
|
+
|
|
18
|
+
- **A turn's persona follows its subject, or there is none**
|
|
19
|
+
([CR203](../project/refinement/rs010-cv22-oracle-and-port-hygiene/cr203-route-a-turns-persona-by-its-subject-and-let-it-have-none.md)).
|
|
20
|
+
In Mirror Mode, on the delta of an ordinary turn and on the full load alike, the persona
|
|
21
|
+
is, in order: the one you named (`mirror load --persona`); the router's answer, a
|
|
22
|
+
Decisions model through OpenRouter's Decisions API asked which persona the subject
|
|
23
|
+
belongs to and whether it belongs to any, taken only at or above a threshold; the persona
|
|
24
|
+
the turn's Journey names as `default_persona` in its metadata; or none. It is never the
|
|
25
|
+
session's by inheritance.
|
|
26
|
+
- **None is said, not left silent.** A turn with no persona delivers
|
|
27
|
+
`persona: none` and the clause
|
|
28
|
+
`answer as the mirror; no persona this turn: do not sign, and do not answer as an earlier persona`,
|
|
29
|
+
so the model does not keep signing as the last persona it answered as.
|
|
30
|
+
- **Every routing failure is none, and the turn goes on.** No key, a timeout, a refusal, a
|
|
31
|
+
malformed answer or a persona that does not exist each give a turn with no persona. One
|
|
32
|
+
attempt, bounded like reception (`MEMORY_LLM_TIMEOUT_RECEPTION`, 10 s by default).
|
|
33
|
+
- **Two settings.** `MEMORY_PERSONA_ROUTER_MODEL` (`liquid/d1-ultra`) and
|
|
34
|
+
`MEMORY_PERSONA_ROUTER_THRESHOLD` (`0.6`), in [`REFERENCE.md`](../../REFERENCE.md); an
|
|
35
|
+
invalid value reads as unset.
|
|
36
|
+
- **The host gets the persona's name**
|
|
37
|
+
([CR204](../project/refinement/rs010-cv22-oracle-and-port-hygiene/cr204-hand-the-host-the-personas-name-with-its-id.md)).
|
|
38
|
+
`deliverMirrorContext` in `mirror-mind/desktop` returns `personaName`, the persona's own
|
|
39
|
+
`name` from its metadata, or `null`; `persona` stays the id. Additive.
|
|
40
|
+
- No schema change, no migration.
|
|
41
|
+
|
|
42
|
+
## Where We Started
|
|
43
|
+
|
|
44
|
+
A keyword count chose every turn's persona, and when no keyword scored, the session's
|
|
45
|
+
persona kept answering. Measured on the maintainer's own data over five days (550
|
|
46
|
+
deliveries), every turn had a persona, the persona changed on one turn in three, and
|
|
47
|
+
specialists from unrelated domains answered: health, customer support and QA personas on a
|
|
48
|
+
book-writing Journey. The maintainer's criterion: a wrong persona is worse than none,
|
|
49
|
+
because it puts a false specialist into the answer, while no persona only loses one
|
|
50
|
+
domain's lens and keeps identity, voice and memories.
|
|
51
|
+
|
|
52
|
+
## What Changed
|
|
53
|
+
|
|
54
|
+
**One router for both paths.** `ts/src/mirror/personaRouter.ts` owns the Decisions route,
|
|
55
|
+
which OpenRouter marks `alpha`: its request and answer shapes live in that module and a
|
|
56
|
+
contract test pins them, so a change in the route fails a test, not a turn. The doorman's
|
|
57
|
+
delta and the full load call the same rule, so the two cannot disagree. The delta asks the
|
|
58
|
+
router and embeds the prompt at the same time, so the call adds no wait of its own to the
|
|
59
|
+
delivery. Each persona is offered with its own `description`, else its generated
|
|
60
|
+
descriptor, else the start of its content.
|
|
61
|
+
|
|
62
|
+
**The session records the turn's answer.** A session's persona is now the last turn's,
|
|
63
|
+
none included. Which persona sections the conversation already holds is kept apart
|
|
64
|
+
(`doorman_personas` in the session's metadata), so a section is delivered the first time
|
|
65
|
+
its persona answers and not again; a full load starts that list over, and a compaction
|
|
66
|
+
clears it.
|
|
67
|
+
|
|
68
|
+
**Reception still runs** on a full load, for the Journey and the turn's shaping. It no
|
|
69
|
+
longer chooses the persona. Its prompt is unchanged.
|
|
70
|
+
|
|
71
|
+
**The ledger.** Each routing call writes one `llm_calls` row, `role = persona_routing`,
|
|
72
|
+
with the model, latency and cost, and an empty prompt and response in every logging mode:
|
|
73
|
+
the subject of your turn is not the ledger's to keep.
|
|
74
|
+
|
|
75
|
+
**Measured before the release.** The router was chosen by an eval of 100 labelled turns
|
|
76
|
+
from the maintainer's production data
|
|
77
|
+
([report](../project/refinement/rs010-cv22-oracle-and-port-hygiene/cr203-persona-routing-eval-2026-11-07.md)).
|
|
78
|
+
The same 100, rerun through this release's code on a copy of a home with 28 personas: 93
|
|
79
|
+
right, 3 wrong, 4 missed (the keyword router: 35 wrong), at 0.61 s median and 1.32 s at
|
|
80
|
+
p95, about US$ 0.00008 per turn. The private turns stay out of this repository; a
|
|
81
|
+
synthetic set runs in CI.
|
|
82
|
+
|
|
83
|
+
**Model-behavior gate.** This release changes no extraction model, embedding model or
|
|
84
|
+
`prompts.ts` prompt, so the gate's letter does not apply; it does add a model call and a
|
|
85
|
+
line the model reads, and the rerun above is its evidence in place of `eval --all`, which
|
|
86
|
+
does not probe routing.
|
|
87
|
+
|
|
88
|
+
## Conscious Exclusions
|
|
89
|
+
|
|
90
|
+
- **No command sets a Journey's `default_persona` yet.** The Core reads it; setting it is a
|
|
91
|
+
follow-up. Two of the rerun's three wrong picks are a Journey's own writing persona on
|
|
92
|
+
turns that asked for none, the case a default resolves.
|
|
93
|
+
- **The no-persona clause is a first wording**, one constant (`NO_PERSONA_CLAUSE`), to be
|
|
94
|
+
settled in prompt review.
|
|
95
|
+
- **`detect-persona` stays** as a command, an MCP tool and a package export. It no longer
|
|
96
|
+
decides a turn.
|
|
97
|
+
- **Reception's prompt still lists the personas**: its bytes are digest-pinned, and
|
|
98
|
+
changing it is its own decision.
|
|
99
|
+
- **A full load's worst case grows**: reception, then routing, each bounded at 10 s. A
|
|
100
|
+
delta's is unchanged.
|
|
101
|
+
- **Two personas covering one domain** remain content for their owner to merge.
|
|
102
|
+
|
|
103
|
+
## Upgrading
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
mirror runtime update
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
Nothing to configure: the router uses the OpenRouter key Mirror already reads. The routing
|
|
110
|
+
calls are the `persona_routing` rows of `mirror inspect llm-calls`. Turns without a persona
|
|
111
|
+
are expected: they answer as the mirror, unsigned.
|
|
112
|
+
|
|
113
|
+
## Next Horizon
|
|
114
|
+
|
|
115
|
+
A command and a host call to set a Journey's `default_persona`; prompt review of the
|
|
116
|
+
no-persona clause; Nautilus for Mirror showing the persona's name from `personaName`.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mirror-mind",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.41.0",
|
|
4
4
|
"description": "Mirror Mind — local-first memory and identity for agentic runtimes.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Mirror Mind"
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
"command": "/bin/sh",
|
|
11
11
|
"args": [
|
|
12
12
|
"-c",
|
|
13
|
-
"mmbin=\"\"; [ -n \"$
|
|
13
|
+
"mmbin=\"\"; mmbindir=\"$MIRROR_BIN\"; mmwhy=\"\"; [ -n \"$mmbindir\" ] && case \"$mmbindir\" in /*|\\\\*|[A-Za-z]:[\\\\/]*) if [ ! -e \"$mmbindir\" ]; then mmwhy=\"missing\"; elif [ ! -d \"$mmbindir\" ]; then mmwhy=\"not a directory\"; elif [ ! -f \"$mmbindir/mirror\" ] || [ ! -x \"$mmbindir/mirror\" ]; then mmwhy=\"a directory without an executable mirror\"; else mmbin=\"$mmbindir/mirror\"; fi ;; *) mmwhy=\"relative\" ;; esac; [ -n \"$mmwhy\" ] && printf 'mirror-mind: MIRROR_BIN=%s is %s; searching the PATH and the usual places instead\\n' \"$(printf '%s' \"$mmbindir\" | tr -d '\\r\\n')\" \"$mmwhy\" >&2; [ -n \"$mmbin\" ] || mmbin=\"$(command -v mirror 2>/dev/null)\"; for mmdir in \"$HOME/.nvm/current/bin\" /opt/homebrew/bin /usr/local/bin; do [ -n \"$mmbin\" ] && break; [ -x \"$mmdir/mirror\" ] && mmbin=\"$mmdir/mirror\"; done; [ -n \"$mmbin\" ] || { echo \"mirror-mind: mirror not found on PATH; the MCP server cannot start. Install mirror-mind (npm install -g mirror-mind) or set MIRROR_BIN to its bin directory.\" >&2; exit 1; }; mmjs=\"$(dirname \"$mmbin\")/node_modules/mirror-mind/bin/mirror.js\"; [ -f \"$mmjs\" ] && mmbin=\"$mmjs\"; mmnode=\"$MIRROR_NODE\"; mmwhy=\"\"; [ -n \"$mmnode\" ] && case \"$mmnode\" in /*|\\\\*|[A-Za-z]:[\\\\/]*) if [ -d \"$mmnode\" ]; then mmwhy=\"a directory\"; elif [ ! -e \"$mmnode\" ]; then mmwhy=\"missing\"; elif [ ! -f \"$mmnode\" ] || [ ! -x \"$mmnode\" ]; then mmwhy=\"not executable\"; fi ;; *) mmwhy=\"relative\" ;; esac; [ -n \"$mmwhy\" ] && { printf 'mirror-mind: MIRROR_NODE=%s is %s; searching the PATH and the usual places instead\\n' \"$(printf '%s' \"$mmnode\" | tr -d '\\r\\n')\" \"$mmwhy\" >&2; mmnode=\"\"; }; [ -n \"$mmnode\" ] || mmnode=\"$(command -v node 2>/dev/null)\"; for mmdir in \"$(dirname \"$mmbin\")\" \"$HOME/.nvm/current/bin\" /opt/homebrew/bin /usr/local/bin; do [ -n \"$mmnode\" ] && break; [ -x \"$mmdir/node\" ] && mmnode=\"$mmdir/node\"; done; [ -n \"$mmnode\" ] || { echo \"mirror-mind: node not found on PATH; the MCP server cannot start. Set MIRROR_NODE.\" >&2; exit 1; }; exec \"$mmnode\" \"$mmbin\" mcp"
|
|
14
14
|
]
|
|
15
15
|
}
|
|
16
16
|
}
|
|
@@ -62,10 +62,31 @@ note() {
|
|
|
62
62
|
}
|
|
63
63
|
|
|
64
64
|
# Find the bin. MIRROR_BIN names the directory holding `mirror` and
|
|
65
|
-
# `mirror-hook` (`$(npm prefix -g)/bin`)
|
|
66
|
-
#
|
|
65
|
+
# `mirror-hook` (`$(npm prefix -g)/bin`); then the PATH; then the global bin
|
|
66
|
+
# directories a GUI launch leaves off it.
|
|
67
|
+
#
|
|
68
|
+
# MIRROR_BIN is used only as an absolute path to a directory holding an
|
|
69
|
+
# executable `mirror-hook`, by MIRROR_NODE's rule and pattern (CR197): a
|
|
70
|
+
# relative one would resolve against the project the runtime spawned this hook
|
|
71
|
+
# from, and Node would run that project's file. Anything else is noted with its
|
|
72
|
+
# reason, in MIRROR_NODE's words, once a day, and the search goes on without it.
|
|
67
73
|
ENTRY=""
|
|
68
|
-
[ -n "${MIRROR_BIN:-}" ]
|
|
74
|
+
if [ -n "${MIRROR_BIN:-}" ]; then
|
|
75
|
+
refused=""
|
|
76
|
+
case "$MIRROR_BIN" in
|
|
77
|
+
/*|\\*|[A-Za-z]:[\\/]*)
|
|
78
|
+
if [ ! -e "$MIRROR_BIN" ]; then refused="missing"
|
|
79
|
+
elif [ ! -d "$MIRROR_BIN" ]; then refused="not a directory"
|
|
80
|
+
elif [ ! -f "$MIRROR_BIN/mirror-hook" ] || [ ! -x "$MIRROR_BIN/mirror-hook" ]; then refused="a directory without an executable mirror-hook"
|
|
81
|
+
else ENTRY="$MIRROR_BIN/mirror-hook"
|
|
82
|
+
fi
|
|
83
|
+
;;
|
|
84
|
+
*) refused="relative" ;;
|
|
85
|
+
esac
|
|
86
|
+
if [ -n "$refused" ]; then
|
|
87
|
+
note "MIRROR_BIN=${MIRROR_BIN//[$'\r\n']/} is $refused; searching the PATH and the usual places instead" daily
|
|
88
|
+
fi
|
|
89
|
+
fi
|
|
69
90
|
[ -n "$ENTRY" ] || ENTRY="$(command -v mirror-hook 2>/dev/null || true)"
|
|
70
91
|
for candidate in "$HOME/.nvm/current/bin" /opt/homebrew/bin /usr/local/bin; do
|
|
71
92
|
[ -n "$ENTRY" ] && break
|
|
@@ -62,10 +62,31 @@ note() {
|
|
|
62
62
|
}
|
|
63
63
|
|
|
64
64
|
# Find the bin. MIRROR_BIN names the directory holding `mirror` and
|
|
65
|
-
# `mirror-hook` (`$(npm prefix -g)/bin`)
|
|
66
|
-
#
|
|
65
|
+
# `mirror-hook` (`$(npm prefix -g)/bin`); then the PATH; then the global bin
|
|
66
|
+
# directories a GUI launch leaves off it.
|
|
67
|
+
#
|
|
68
|
+
# MIRROR_BIN is used only as an absolute path to a directory holding an
|
|
69
|
+
# executable `mirror-hook`, by MIRROR_NODE's rule and pattern (CR197): a
|
|
70
|
+
# relative one would resolve against the project the runtime spawned this hook
|
|
71
|
+
# from, and Node would run that project's file. Anything else is noted with its
|
|
72
|
+
# reason, in MIRROR_NODE's words, once a day, and the search goes on without it.
|
|
67
73
|
ENTRY=""
|
|
68
|
-
[ -n "${MIRROR_BIN:-}" ]
|
|
74
|
+
if [ -n "${MIRROR_BIN:-}" ]; then
|
|
75
|
+
refused=""
|
|
76
|
+
case "$MIRROR_BIN" in
|
|
77
|
+
/*|\\*|[A-Za-z]:[\\/]*)
|
|
78
|
+
if [ ! -e "$MIRROR_BIN" ]; then refused="missing"
|
|
79
|
+
elif [ ! -d "$MIRROR_BIN" ]; then refused="not a directory"
|
|
80
|
+
elif [ ! -f "$MIRROR_BIN/mirror-hook" ] || [ ! -x "$MIRROR_BIN/mirror-hook" ]; then refused="a directory without an executable mirror-hook"
|
|
81
|
+
else ENTRY="$MIRROR_BIN/mirror-hook"
|
|
82
|
+
fi
|
|
83
|
+
;;
|
|
84
|
+
*) refused="relative" ;;
|
|
85
|
+
esac
|
|
86
|
+
if [ -n "$refused" ]; then
|
|
87
|
+
note "MIRROR_BIN=${MIRROR_BIN//[$'\r\n']/} is $refused; searching the PATH and the usual places instead" daily
|
|
88
|
+
fi
|
|
89
|
+
fi
|
|
69
90
|
[ -n "$ENTRY" ] || ENTRY="$(command -v mirror-hook 2>/dev/null || true)"
|
|
70
91
|
for candidate in "$HOME/.nvm/current/bin" /opt/homebrew/bin /usr/local/bin; do
|
|
71
92
|
[ -n "$ENTRY" ] && break
|
|
@@ -62,10 +62,31 @@ note() {
|
|
|
62
62
|
}
|
|
63
63
|
|
|
64
64
|
# Find the bin. MIRROR_BIN names the directory holding `mirror` and
|
|
65
|
-
# `mirror-hook` (`$(npm prefix -g)/bin`)
|
|
66
|
-
#
|
|
65
|
+
# `mirror-hook` (`$(npm prefix -g)/bin`); then the PATH; then the global bin
|
|
66
|
+
# directories a GUI launch leaves off it.
|
|
67
|
+
#
|
|
68
|
+
# MIRROR_BIN is used only as an absolute path to a directory holding an
|
|
69
|
+
# executable `mirror-hook`, by MIRROR_NODE's rule and pattern (CR197): a
|
|
70
|
+
# relative one would resolve against the project the runtime spawned this hook
|
|
71
|
+
# from, and Node would run that project's file. Anything else is noted with its
|
|
72
|
+
# reason, in MIRROR_NODE's words, once a day, and the search goes on without it.
|
|
67
73
|
ENTRY=""
|
|
68
|
-
[ -n "${MIRROR_BIN:-}" ]
|
|
74
|
+
if [ -n "${MIRROR_BIN:-}" ]; then
|
|
75
|
+
refused=""
|
|
76
|
+
case "$MIRROR_BIN" in
|
|
77
|
+
/*|\\*|[A-Za-z]:[\\/]*)
|
|
78
|
+
if [ ! -e "$MIRROR_BIN" ]; then refused="missing"
|
|
79
|
+
elif [ ! -d "$MIRROR_BIN" ]; then refused="not a directory"
|
|
80
|
+
elif [ ! -f "$MIRROR_BIN/mirror-hook" ] || [ ! -x "$MIRROR_BIN/mirror-hook" ]; then refused="a directory without an executable mirror-hook"
|
|
81
|
+
else ENTRY="$MIRROR_BIN/mirror-hook"
|
|
82
|
+
fi
|
|
83
|
+
;;
|
|
84
|
+
*) refused="relative" ;;
|
|
85
|
+
esac
|
|
86
|
+
if [ -n "$refused" ]; then
|
|
87
|
+
note "MIRROR_BIN=${MIRROR_BIN//[$'\r\n']/} is $refused; searching the PATH and the usual places instead" daily
|
|
88
|
+
fi
|
|
89
|
+
fi
|
|
69
90
|
[ -n "$ENTRY" ] || ENTRY="$(command -v mirror-hook 2>/dev/null || true)"
|
|
70
91
|
for candidate in "$HOME/.nvm/current/bin" /opt/homebrew/bin /usr/local/bin; do
|
|
71
92
|
[ -n "$ENTRY" ] && break
|
|
@@ -57,10 +57,31 @@ note() {
|
|
|
57
57
|
}
|
|
58
58
|
|
|
59
59
|
# Find the bin. MIRROR_BIN names the directory holding `mirror` and
|
|
60
|
-
# `mirror-hook` (`$(npm prefix -g)/bin`)
|
|
61
|
-
#
|
|
60
|
+
# `mirror-hook` (`$(npm prefix -g)/bin`); then the PATH; then the global bin
|
|
61
|
+
# directories a GUI launch leaves off it.
|
|
62
|
+
#
|
|
63
|
+
# MIRROR_BIN is used only as an absolute path to a directory holding an
|
|
64
|
+
# executable `mirror-hook`, by MIRROR_NODE's rule and pattern (CR197): a
|
|
65
|
+
# relative one would resolve against the project the runtime spawned this hook
|
|
66
|
+
# from, and Node would run that project's file. Anything else is noted with its
|
|
67
|
+
# reason, in MIRROR_NODE's words, once a day, and the search goes on without it.
|
|
62
68
|
ENTRY=""
|
|
63
|
-
[ -n "${MIRROR_BIN:-}" ]
|
|
69
|
+
if [ -n "${MIRROR_BIN:-}" ]; then
|
|
70
|
+
refused=""
|
|
71
|
+
case "$MIRROR_BIN" in
|
|
72
|
+
/*|\\*|[A-Za-z]:[\\/]*)
|
|
73
|
+
if [ ! -e "$MIRROR_BIN" ]; then refused="missing"
|
|
74
|
+
elif [ ! -d "$MIRROR_BIN" ]; then refused="not a directory"
|
|
75
|
+
elif [ ! -f "$MIRROR_BIN/mirror-hook" ] || [ ! -x "$MIRROR_BIN/mirror-hook" ]; then refused="a directory without an executable mirror-hook"
|
|
76
|
+
else ENTRY="$MIRROR_BIN/mirror-hook"
|
|
77
|
+
fi
|
|
78
|
+
;;
|
|
79
|
+
*) refused="relative" ;;
|
|
80
|
+
esac
|
|
81
|
+
if [ -n "$refused" ]; then
|
|
82
|
+
note "MIRROR_BIN=${MIRROR_BIN//[$'\r\n']/} is $refused; searching the PATH and the usual places instead" daily
|
|
83
|
+
fi
|
|
84
|
+
fi
|
|
64
85
|
[ -n "$ENTRY" ] || ENTRY="$(command -v mirror-hook 2>/dev/null || true)"
|
|
65
86
|
for candidate in "$HOME/.nvm/current/bin" /opt/homebrew/bin /usr/local/bin; do
|
|
66
87
|
[ -n "$ENTRY" ] && break
|
|
@@ -57,10 +57,31 @@ note() {
|
|
|
57
57
|
}
|
|
58
58
|
|
|
59
59
|
# Find the bin. MIRROR_BIN names the directory holding `mirror` and
|
|
60
|
-
# `mirror-hook` (`$(npm prefix -g)/bin`)
|
|
61
|
-
#
|
|
60
|
+
# `mirror-hook` (`$(npm prefix -g)/bin`); then the PATH; then the global bin
|
|
61
|
+
# directories a GUI launch leaves off it.
|
|
62
|
+
#
|
|
63
|
+
# MIRROR_BIN is used only as an absolute path to a directory holding an
|
|
64
|
+
# executable `mirror-hook`, by MIRROR_NODE's rule and pattern (CR197): a
|
|
65
|
+
# relative one would resolve against the project the runtime spawned this hook
|
|
66
|
+
# from, and Node would run that project's file. Anything else is noted with its
|
|
67
|
+
# reason, in MIRROR_NODE's words, once a day, and the search goes on without it.
|
|
62
68
|
ENTRY=""
|
|
63
|
-
[ -n "${MIRROR_BIN:-}" ]
|
|
69
|
+
if [ -n "${MIRROR_BIN:-}" ]; then
|
|
70
|
+
refused=""
|
|
71
|
+
case "$MIRROR_BIN" in
|
|
72
|
+
/*|\\*|[A-Za-z]:[\\/]*)
|
|
73
|
+
if [ ! -e "$MIRROR_BIN" ]; then refused="missing"
|
|
74
|
+
elif [ ! -d "$MIRROR_BIN" ]; then refused="not a directory"
|
|
75
|
+
elif [ ! -f "$MIRROR_BIN/mirror-hook" ] || [ ! -x "$MIRROR_BIN/mirror-hook" ]; then refused="a directory without an executable mirror-hook"
|
|
76
|
+
else ENTRY="$MIRROR_BIN/mirror-hook"
|
|
77
|
+
fi
|
|
78
|
+
;;
|
|
79
|
+
*) refused="relative" ;;
|
|
80
|
+
esac
|
|
81
|
+
if [ -n "$refused" ]; then
|
|
82
|
+
note "MIRROR_BIN=${MIRROR_BIN//[$'\r\n']/} is $refused; searching the PATH and the usual places instead" daily
|
|
83
|
+
fi
|
|
84
|
+
fi
|
|
64
85
|
[ -n "$ENTRY" ] || ENTRY="$(command -v mirror-hook 2>/dev/null || true)"
|
|
65
86
|
for candidate in "$HOME/.nvm/current/bin" /opt/homebrew/bin /usr/local/bin; do
|
|
66
87
|
[ -n "$ENTRY" ] && break
|
|
@@ -57,10 +57,31 @@ note() {
|
|
|
57
57
|
}
|
|
58
58
|
|
|
59
59
|
# Find the bin. MIRROR_BIN names the directory holding `mirror` and
|
|
60
|
-
# `mirror-hook` (`$(npm prefix -g)/bin`)
|
|
61
|
-
#
|
|
60
|
+
# `mirror-hook` (`$(npm prefix -g)/bin`); then the PATH; then the global bin
|
|
61
|
+
# directories a GUI launch leaves off it.
|
|
62
|
+
#
|
|
63
|
+
# MIRROR_BIN is used only as an absolute path to a directory holding an
|
|
64
|
+
# executable `mirror-hook`, by MIRROR_NODE's rule and pattern (CR197): a
|
|
65
|
+
# relative one would resolve against the project the runtime spawned this hook
|
|
66
|
+
# from, and Node would run that project's file. Anything else is noted with its
|
|
67
|
+
# reason, in MIRROR_NODE's words, once a day, and the search goes on without it.
|
|
62
68
|
ENTRY=""
|
|
63
|
-
[ -n "${MIRROR_BIN:-}" ]
|
|
69
|
+
if [ -n "${MIRROR_BIN:-}" ]; then
|
|
70
|
+
refused=""
|
|
71
|
+
case "$MIRROR_BIN" in
|
|
72
|
+
/*|\\*|[A-Za-z]:[\\/]*)
|
|
73
|
+
if [ ! -e "$MIRROR_BIN" ]; then refused="missing"
|
|
74
|
+
elif [ ! -d "$MIRROR_BIN" ]; then refused="not a directory"
|
|
75
|
+
elif [ ! -f "$MIRROR_BIN/mirror-hook" ] || [ ! -x "$MIRROR_BIN/mirror-hook" ]; then refused="a directory without an executable mirror-hook"
|
|
76
|
+
else ENTRY="$MIRROR_BIN/mirror-hook"
|
|
77
|
+
fi
|
|
78
|
+
;;
|
|
79
|
+
*) refused="relative" ;;
|
|
80
|
+
esac
|
|
81
|
+
if [ -n "$refused" ]; then
|
|
82
|
+
note "MIRROR_BIN=${MIRROR_BIN//[$'\r\n']/} is $refused; searching the PATH and the usual places instead" daily
|
|
83
|
+
fi
|
|
84
|
+
fi
|
|
64
85
|
[ -n "$ENTRY" ] || ENTRY="$(command -v mirror-hook 2>/dev/null || true)"
|
|
65
86
|
for candidate in "$HOME/.nvm/current/bin" /opt/homebrew/bin /usr/local/bin; do
|
|
66
87
|
[ -n "$ENTRY" ] && break
|
|
@@ -57,10 +57,31 @@ note() {
|
|
|
57
57
|
}
|
|
58
58
|
|
|
59
59
|
# Find the bin. MIRROR_BIN names the directory holding `mirror` and
|
|
60
|
-
# `mirror-hook` (`$(npm prefix -g)/bin`)
|
|
61
|
-
#
|
|
60
|
+
# `mirror-hook` (`$(npm prefix -g)/bin`); then the PATH; then the global bin
|
|
61
|
+
# directories a GUI launch leaves off it.
|
|
62
|
+
#
|
|
63
|
+
# MIRROR_BIN is used only as an absolute path to a directory holding an
|
|
64
|
+
# executable `mirror-hook`, by MIRROR_NODE's rule and pattern (CR197): a
|
|
65
|
+
# relative one would resolve against the project the runtime spawned this hook
|
|
66
|
+
# from, and Node would run that project's file. Anything else is noted with its
|
|
67
|
+
# reason, in MIRROR_NODE's words, once a day, and the search goes on without it.
|
|
62
68
|
ENTRY=""
|
|
63
|
-
[ -n "${MIRROR_BIN:-}" ]
|
|
69
|
+
if [ -n "${MIRROR_BIN:-}" ]; then
|
|
70
|
+
refused=""
|
|
71
|
+
case "$MIRROR_BIN" in
|
|
72
|
+
/*|\\*|[A-Za-z]:[\\/]*)
|
|
73
|
+
if [ ! -e "$MIRROR_BIN" ]; then refused="missing"
|
|
74
|
+
elif [ ! -d "$MIRROR_BIN" ]; then refused="not a directory"
|
|
75
|
+
elif [ ! -f "$MIRROR_BIN/mirror-hook" ] || [ ! -x "$MIRROR_BIN/mirror-hook" ]; then refused="a directory without an executable mirror-hook"
|
|
76
|
+
else ENTRY="$MIRROR_BIN/mirror-hook"
|
|
77
|
+
fi
|
|
78
|
+
;;
|
|
79
|
+
*) refused="relative" ;;
|
|
80
|
+
esac
|
|
81
|
+
if [ -n "$refused" ]; then
|
|
82
|
+
note "MIRROR_BIN=${MIRROR_BIN//[$'\r\n']/} is $refused; searching the PATH and the usual places instead" daily
|
|
83
|
+
fi
|
|
84
|
+
fi
|
|
64
85
|
[ -n "$ENTRY" ] || ENTRY="$(command -v mirror-hook 2>/dev/null || true)"
|
|
65
86
|
for candidate in "$HOME/.nvm/current/bin" /opt/homebrew/bin /usr/local/bin; do
|
|
66
87
|
[ -n "$ENTRY" ] && break
|
|
@@ -57,10 +57,31 @@ note() {
|
|
|
57
57
|
}
|
|
58
58
|
|
|
59
59
|
# Find the bin. MIRROR_BIN names the directory holding `mirror` and
|
|
60
|
-
# `mirror-hook` (`$(npm prefix -g)/bin`)
|
|
61
|
-
#
|
|
60
|
+
# `mirror-hook` (`$(npm prefix -g)/bin`); then the PATH; then the global bin
|
|
61
|
+
# directories a GUI launch leaves off it.
|
|
62
|
+
#
|
|
63
|
+
# MIRROR_BIN is used only as an absolute path to a directory holding an
|
|
64
|
+
# executable `mirror-hook`, by MIRROR_NODE's rule and pattern (CR197): a
|
|
65
|
+
# relative one would resolve against the project the runtime spawned this hook
|
|
66
|
+
# from, and Node would run that project's file. Anything else is noted with its
|
|
67
|
+
# reason, in MIRROR_NODE's words, once a day, and the search goes on without it.
|
|
62
68
|
ENTRY=""
|
|
63
|
-
[ -n "${MIRROR_BIN:-}" ]
|
|
69
|
+
if [ -n "${MIRROR_BIN:-}" ]; then
|
|
70
|
+
refused=""
|
|
71
|
+
case "$MIRROR_BIN" in
|
|
72
|
+
/*|\\*|[A-Za-z]:[\\/]*)
|
|
73
|
+
if [ ! -e "$MIRROR_BIN" ]; then refused="missing"
|
|
74
|
+
elif [ ! -d "$MIRROR_BIN" ]; then refused="not a directory"
|
|
75
|
+
elif [ ! -f "$MIRROR_BIN/mirror-hook" ] || [ ! -x "$MIRROR_BIN/mirror-hook" ]; then refused="a directory without an executable mirror-hook"
|
|
76
|
+
else ENTRY="$MIRROR_BIN/mirror-hook"
|
|
77
|
+
fi
|
|
78
|
+
;;
|
|
79
|
+
*) refused="relative" ;;
|
|
80
|
+
esac
|
|
81
|
+
if [ -n "$refused" ]; then
|
|
82
|
+
note "MIRROR_BIN=${MIRROR_BIN//[$'\r\n']/} is $refused; searching the PATH and the usual places instead" daily
|
|
83
|
+
fi
|
|
84
|
+
fi
|
|
64
85
|
[ -n "$ENTRY" ] || ENTRY="$(command -v mirror-hook 2>/dev/null || true)"
|
|
65
86
|
for candidate in "$HOME/.nvm/current/bin" /opt/homebrew/bin /usr/local/bin; do
|
|
66
87
|
[ -n "$ENTRY" ] && break
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"command": "/bin/sh",
|
|
5
5
|
"args": [
|
|
6
6
|
"-c",
|
|
7
|
-
"mmbin=\"\"; [ -n \"$
|
|
7
|
+
"mmbin=\"\"; mmbindir=\"$MIRROR_BIN\"; mmwhy=\"\"; [ -n \"$mmbindir\" ] && case \"$mmbindir\" in /*|\\\\*|[A-Za-z]:[\\\\/]*) if [ ! -e \"$mmbindir\" ]; then mmwhy=\"missing\"; elif [ ! -d \"$mmbindir\" ]; then mmwhy=\"not a directory\"; elif [ ! -f \"$mmbindir/mirror\" ] || [ ! -x \"$mmbindir/mirror\" ]; then mmwhy=\"a directory without an executable mirror\"; else mmbin=\"$mmbindir/mirror\"; fi ;; *) mmwhy=\"relative\" ;; esac; [ -n \"$mmwhy\" ] && printf 'mirror-mind: MIRROR_BIN=%s is %s; searching the PATH and the usual places instead\\n' \"$(printf '%s' \"$mmbindir\" | tr -d '\\r\\n')\" \"$mmwhy\" >&2; [ -n \"$mmbin\" ] || mmbin=\"$(command -v mirror 2>/dev/null)\"; for mmdir in \"$HOME/.nvm/current/bin\" /opt/homebrew/bin /usr/local/bin; do [ -n \"$mmbin\" ] && break; [ -x \"$mmdir/mirror\" ] && mmbin=\"$mmdir/mirror\"; done; [ -n \"$mmbin\" ] || { echo \"mirror-mind: mirror not found on PATH; the MCP server cannot start. Install mirror-mind (npm install -g mirror-mind) or set MIRROR_BIN to its bin directory.\" >&2; exit 1; }; mmjs=\"$(dirname \"$mmbin\")/node_modules/mirror-mind/bin/mirror.js\"; [ -f \"$mmjs\" ] && mmbin=\"$mmjs\"; mmnode=\"$MIRROR_NODE\"; mmwhy=\"\"; [ -n \"$mmnode\" ] && case \"$mmnode\" in /*|\\\\*|[A-Za-z]:[\\\\/]*) if [ -d \"$mmnode\" ]; then mmwhy=\"a directory\"; elif [ ! -e \"$mmnode\" ]; then mmwhy=\"missing\"; elif [ ! -f \"$mmnode\" ] || [ ! -x \"$mmnode\" ]; then mmwhy=\"not executable\"; fi ;; *) mmwhy=\"relative\" ;; esac; [ -n \"$mmwhy\" ] && { printf 'mirror-mind: MIRROR_NODE=%s is %s; searching the PATH and the usual places instead\\n' \"$(printf '%s' \"$mmnode\" | tr -d '\\r\\n')\" \"$mmwhy\" >&2; mmnode=\"\"; }; [ -n \"$mmnode\" ] || mmnode=\"$(command -v node 2>/dev/null)\"; for mmdir in \"$(dirname \"$mmbin\")\" \"$HOME/.nvm/current/bin\" /opt/homebrew/bin /usr/local/bin; do [ -n \"$mmnode\" ] && break; [ -x \"$mmdir/node\" ] && mmnode=\"$mmdir/node\"; done; [ -n \"$mmnode\" ] || { echo \"mirror-mind: node not found on PATH; the MCP server cannot start. Set MIRROR_NODE.\" >&2; exit 1; }; exec \"$mmnode\" \"$mmbin\" mcp"
|
|
8
8
|
]
|
|
9
9
|
}
|
|
10
10
|
}
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mirror-mind",
|
|
3
3
|
"description": "Mirror Mind — local-first memory and identity for agentic runtimes.",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.41.0",
|
|
5
5
|
"mcpServers": {
|
|
6
6
|
"mirror-mind": {
|
|
7
7
|
"command": "/bin/sh",
|
|
8
8
|
"args": [
|
|
9
9
|
"-c",
|
|
10
|
-
"mmbin=\"\"; [ -n \"$
|
|
10
|
+
"mmbin=\"\"; mmbindir=\"$MIRROR_BIN\"; mmwhy=\"\"; [ -n \"$mmbindir\" ] && case \"$mmbindir\" in /*|\\\\*|[A-Za-z]:[\\\\/]*) if [ ! -e \"$mmbindir\" ]; then mmwhy=\"missing\"; elif [ ! -d \"$mmbindir\" ]; then mmwhy=\"not a directory\"; elif [ ! -f \"$mmbindir/mirror\" ] || [ ! -x \"$mmbindir/mirror\" ]; then mmwhy=\"a directory without an executable mirror\"; else mmbin=\"$mmbindir/mirror\"; fi ;; *) mmwhy=\"relative\" ;; esac; [ -n \"$mmwhy\" ] && printf 'mirror-mind: MIRROR_BIN=%s is %s; searching the PATH and the usual places instead\\n' \"$(printf '%s' \"$mmbindir\" | tr -d '\\r\\n')\" \"$mmwhy\" >&2; [ -n \"$mmbin\" ] || mmbin=\"$(command -v mirror 2>/dev/null)\"; for mmdir in \"$HOME/.nvm/current/bin\" /opt/homebrew/bin /usr/local/bin; do [ -n \"$mmbin\" ] && break; [ -x \"$mmdir/mirror\" ] && mmbin=\"$mmdir/mirror\"; done; [ -n \"$mmbin\" ] || { echo \"mirror-mind: mirror not found on PATH; the MCP server cannot start. Install mirror-mind (npm install -g mirror-mind) or set MIRROR_BIN to its bin directory.\" >&2; exit 1; }; mmjs=\"$(dirname \"$mmbin\")/node_modules/mirror-mind/bin/mirror.js\"; [ -f \"$mmjs\" ] && mmbin=\"$mmjs\"; mmnode=\"$MIRROR_NODE\"; mmwhy=\"\"; [ -n \"$mmnode\" ] && case \"$mmnode\" in /*|\\\\*|[A-Za-z]:[\\\\/]*) if [ -d \"$mmnode\" ]; then mmwhy=\"a directory\"; elif [ ! -e \"$mmnode\" ]; then mmwhy=\"missing\"; elif [ ! -f \"$mmnode\" ] || [ ! -x \"$mmnode\" ]; then mmwhy=\"not executable\"; fi ;; *) mmwhy=\"relative\" ;; esac; [ -n \"$mmwhy\" ] && { printf 'mirror-mind: MIRROR_NODE=%s is %s; searching the PATH and the usual places instead\\n' \"$(printf '%s' \"$mmnode\" | tr -d '\\r\\n')\" \"$mmwhy\" >&2; mmnode=\"\"; }; [ -n \"$mmnode\" ] || mmnode=\"$(command -v node 2>/dev/null)\"; for mmdir in \"$(dirname \"$mmbin\")\" \"$HOME/.nvm/current/bin\" /opt/homebrew/bin /usr/local/bin; do [ -n \"$mmnode\" ] && break; [ -x \"$mmdir/node\" ] && mmnode=\"$mmdir/node\"; done; [ -n \"$mmnode\" ] || { echo \"mirror-mind: node not found on PATH; the MCP server cannot start. Set MIRROR_NODE.\" >&2; exit 1; }; exec \"$mmnode\" \"$mmbin\" mcp"
|
|
11
11
|
]
|
|
12
12
|
}
|
|
13
13
|
}
|