mirror-mind 0.34.0 → 0.35.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/.pi/skills/mm-build/SKILL.md +1 -1
- package/.pi/skills/mm-explore/SKILL.md +1 -1
- package/.pi/skills/mm-help/SKILL.md +2 -2
- package/.pi/skills/mm-soul/SKILL.md +1 -1
- package/AGENTS.md +7 -6
- package/README.md +5 -6
- package/docs/releases/index.md +1 -0
- package/docs/releases/v0.35.0.md +199 -0
- package/package.json +2 -4
- package/plugins/mirror-mind/.claude-plugin/plugin.json +1 -1
- package/plugins/mirror-mind/agy/pre-invocation.sh +92 -0
- package/plugins/mirror-mind/agy/session-start.sh +92 -0
- package/plugins/mirror-mind/agy/stop.sh +92 -0
- package/plugins/mirror-mind/hooks.json +26 -0
- package/plugins/mirror-mind/mcp_config.json +11 -0
- package/plugins/mirror-mind/plugin.json +4 -0
- package/plugins/mirror-mind/rules/AGENTS.md +189 -0
- package/plugins/mirror-mind/skills/mm-build/SKILL.md +1 -1
- package/ts/src/conversation/agyTranscript.ts +105 -0
- package/ts/src/conversation/commandTurn.ts +1 -1
- package/ts/src/conversation/deferredCloseTail.ts +2 -2
- package/ts/src/conversation/logger.ts +1 -1
- package/ts/src/conversation/runtimePolicy.ts +10 -1
- package/ts/src/docs/docsLint.ts +14 -1
- package/ts/src/frontDoor/stdinSource.ts +2 -2
- package/ts/src/guards/claudePlugin.ts +74 -0
- package/ts/src/guards/packContents.ts +3 -2
- package/ts/src/guards/piSourcedSkills.ts +1 -1
- package/ts/src/guards/retiredSurfaces.ts +31 -0
- package/ts/src/hooks/agy.ts +229 -0
- package/ts/src/hooks/main.ts +11 -15
- package/ts/src/hooks/payload.ts +8 -15
- package/ts/src/hooks/runtime.ts +11 -10
- package/ts/src/runtime/wiring.ts +18 -36
- package/.gemini/hooks/log-assistant.sh +0 -85
- package/.gemini/hooks/log-user.sh +0 -85
- package/.gemini/hooks/session-end.sh +0 -85
- package/.gemini/hooks/session-start.sh +0 -85
- package/ts/src/hooks/gemini.ts +0 -128
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: "mm-help"
|
|
3
|
-
description: Shows available Mirror Mind commands on Pi
|
|
3
|
+
description: Shows available Mirror Mind commands on Pi
|
|
4
4
|
user-invocable: true
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
# Help
|
|
8
8
|
|
|
9
|
-
Show the user the available commands
|
|
9
|
+
Show the user the available commands:
|
|
10
10
|
|
|
11
11
|
## Operating Modes
|
|
12
12
|
|
package/AGENTS.md
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
# Mirror Mind — Operating Instructions
|
|
2
2
|
|
|
3
3
|
<!-- This file ships in the mirror-mind package and is read by every runtime:
|
|
4
|
-
Pi and Codex read it from the project root natively, Claude Code
|
|
5
|
-
|
|
6
|
-
package's Pi session receives it from the extension
|
|
4
|
+
Pi and Codex read it from the project root natively, Claude Code through
|
|
5
|
+
the @AGENTS.md import in CLAUDE.md, Antigravity as the plugin's rules, and
|
|
6
|
+
an installed package's Pi session receives it from the extension
|
|
7
|
+
(CV22.DS10.US3, D13).
|
|
7
8
|
It holds nothing about this repository; that is CLAUDE.md's part. -->
|
|
8
9
|
|
|
9
10
|
---
|
|
@@ -17,9 +18,9 @@ Applies to all sessions, regardless of project.
|
|
|
17
18
|
The mirror operates in four modes, chosen automatically based on context:
|
|
18
19
|
Mirror, Builder, Explorer, and Soul.
|
|
19
20
|
|
|
20
|
-
Each skill below is named three ways: `/mm-<skill>` in Pi
|
|
21
|
-
|
|
22
|
-
|
|
21
|
+
Each skill below is named three ways: `/mm-<skill>` in Pi, `$mm-<skill>` in
|
|
22
|
+
Codex, `/mm:<skill>` in Claude Code. A **plugin** install prefixes the plugin's
|
|
23
|
+
name — `/mirror-mind:mm:<skill>` in Claude Code's menu and in Antigravity,
|
|
23
24
|
`$mirror-mind:mm:<skill>` in Codex, `mirror-mind:mm-<skill>` as the skill's own
|
|
24
25
|
name — and the forms below refer to the same skill.
|
|
25
26
|
|
package/README.md
CHANGED
|
@@ -60,8 +60,8 @@ This is not a chatbot with memory. This is a mirror — and yours.
|
|
|
60
60
|
|
|
61
61
|
## How It Works
|
|
62
62
|
|
|
63
|
-
Local-first. SQLite. Four supported runtimes: Pi,
|
|
64
|
-
|
|
63
|
+
Local-first. SQLite. Four supported runtimes: Pi, Claude Code, Codex,
|
|
64
|
+
Antigravity. One TypeScript core, run by Node.js. Jungian architecture: Self, Ego,
|
|
65
65
|
Personas, Shadow.
|
|
66
66
|
Context-activated modes — Mirror, Builder, Explorer, and Soul. The identity lives
|
|
67
67
|
in your machine, not a server.
|
|
@@ -79,9 +79,8 @@ in your machine, not a server.
|
|
|
79
79
|
- [OpenRouter](https://openrouter.ai) account with at least $5 in credits —
|
|
80
80
|
embeddings, memory extraction, and multi-LLM
|
|
81
81
|
- An AI runtime subscription:
|
|
82
|
-
[Codex Plus](https://openai.com/codex/) (recommended)
|
|
83
|
-
[Claude Code Pro](https://code.claude.com/docs)
|
|
84
|
-
[Gemini AI Pro](https://geminicli.com/)
|
|
82
|
+
[Codex Plus](https://openai.com/codex/) (recommended) or
|
|
83
|
+
[Claude Code Pro](https://code.claude.com/docs)
|
|
85
84
|
- [Pi](https://github.com/earendil-works/pi/tree/main/packages/coding-agent)
|
|
86
85
|
— recommended harness (multi-model, not locked to one provider)
|
|
87
86
|
|
|
@@ -111,7 +110,7 @@ this repository, turning a personal experiment into a reusable framework:
|
|
|
111
110
|
|
|
112
111
|
- **English** — full migration from Portuguese across code, CLI, schema, identity, and docs
|
|
113
112
|
- **Framework/user separation** — user identity and memory live privately under `~/.mirror/<user>/`, outside the repository
|
|
114
|
-
- **Multi-runtime** — from Claude Code only to
|
|
113
|
+
- **Multi-runtime** — from Claude Code only to Pi, Claude Code, Codex, and Antigravity
|
|
115
114
|
- **Multi-user** — independent mirrors for multiple users on the same machine
|
|
116
115
|
- **Multi-session** — concurrent sessions tracked safely in a database-backed session registry
|
|
117
116
|
- **Intelligence depth** — hybrid semantic and lexical search, honest reinforcement, two-pass extraction, memory consolidation, and shadow cultivation
|
package/docs/releases/index.md
CHANGED
|
@@ -12,6 +12,7 @@ Future release notes should use the structure defined in [Release Notes](../proc
|
|
|
12
12
|
|
|
13
13
|
## Releases
|
|
14
14
|
|
|
15
|
+
- [v0.35.0 — Four Runtimes, One Successor](v0.35.0.md) — minor release closing CV21.DS8: the Gemini CLI integration no longer ships and Antigravity stands in its place, the gate met by DS7's live L4 on agy 1.4.2. Four runtimes — Pi, Claude Code, Codex, Antigravity — named everywhere; the cutoff recorded in full, with the four hook entries a Gemini CLI user deletes and v0.34.0 as the last release that carries the integration; a retired-surface guard keeps it removed. No migration.
|
|
15
16
|
- [v0.34.0 — Codex Through the Canonical Plugin](v0.34.0.md) — minor release closing CV21.DS4: Codex runs Mirror through the canonical plugin, installed in two commands from a marketplace the package ships, measured live at L4 on Codex 0.163.0. The v0.32.1 wrapper wiring no longer ships; `runtime diagnose` and `runtime update` say when Codex's cached plugin copy is behind and name the re-add that brings it level. No migration.
|
|
16
17
|
- [v0.33.4 — A Name Is a Key](v0.33.4.md) — patch release of three first-run corrections: two from a contributor's PR #43 and CR141, which its review opened. `init --help` created a home named `--help`, `init ../escaped` created one outside `.mirror-minds`, and `init "My User"` put a space into `MIRROR_USER`; a user name is now a kebab-case key like a journey slug or a persona id, refused otherwise with a name that would work and nothing created. `identity edit` on Windows opens Notepad when no editor is named. Existing homes untouched. No migration.
|
|
17
18
|
- [v0.33.3 — Mirror Finds Itself Under Any Path](v0.33.3.md) — patch release of one Change Request, CR140, carried by a contributor's PR #42: Mirror read its own install path from `URL.pathname`, so a checkout or package under a path with a space or a non-ASCII character read as `Install: unknown`, the welcome card showed no update, and `runtime update` refused. The path is now a filesystem path, a guard keeps the idiom out, and an `unknown` install names the path it looked up from. On Windows, npm's global layout is recognized and npm runs with no shell, verified on Windows in CI for the two files they touch; there is still no Windows product. Affected installs update once by hand. No migration.
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
---
|
|
2
|
+
digest: >
|
|
3
|
+
v0.35.0 is a minor release closing CV21.DS8: the Gemini CLI integration no
|
|
4
|
+
longer ships, and Antigravity stands in its place. Mirror Mind supports four
|
|
5
|
+
runtimes — Pi, Claude Code, Codex, Antigravity — and every document says so.
|
|
6
|
+
The removal waited for its gate: DS7 measured Antigravity at L4 on agy 1.4.2,
|
|
7
|
+
all five bar items, so a Gemini CLI user who moves loses nothing and gains
|
|
8
|
+
MCP. The cutoff is recorded here in full — what no longer exists, why, what
|
|
9
|
+
to do instead, what still works, and what a user who does nothing will see —
|
|
10
|
+
with the four hook entries to delete named, and v0.34.0 named as the last
|
|
11
|
+
release that carries the integration. A retired-surface guard keeps the
|
|
12
|
+
removal removed. No migration: homes, memories, journeys, and the
|
|
13
|
+
conversations logged as gemini_cli are untouched.
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
# v0.35.0 — Four Runtimes, One Successor
|
|
17
|
+
|
|
18
|
+
**Date:** 2026-10-18
|
|
19
|
+
|
|
20
|
+
## Highlights
|
|
21
|
+
|
|
22
|
+
- **Gemini CLI is no longer a Mirror runtime; Antigravity is its successor**
|
|
23
|
+
([decision](../project/decisions.md#gemini-cli-is-retired-and-antigravity-is-its-successor)).
|
|
24
|
+
The four `.gemini/hooks` wrappers, the `gemini:*` hook family, the two
|
|
25
|
+
Gemini CLI steps `mirror init` printed, and the Gemini smoke no longer ship
|
|
26
|
+
(CV21.DS8.US1). The package's description and keywords, and every
|
|
27
|
+
runtime-facing document, name Pi, Claude Code, Codex, and Antigravity
|
|
28
|
+
(CV21.DS8.US2).
|
|
29
|
+
- **The gate was met first.** v0.33.0 gave notice that the integration would
|
|
30
|
+
be removed "only once Antigravity gives a Gemini CLI user what they have
|
|
31
|
+
now". CV21.DS7.US4 measured that on agy 1.4.2, live: all of Mirror's
|
|
32
|
+
skills, the Operating Instructions at session start, Mirror Mode context
|
|
33
|
+
injected once per user turn, every turn logged and the conversation closed,
|
|
34
|
+
setup in one step against Gemini CLI's two — and MCP reachable, which the
|
|
35
|
+
Gemini CLI wiring never had.
|
|
36
|
+
- **The cutoff is written where a user reads it before it happens to them**:
|
|
37
|
+
the section below, the Antigravity section of
|
|
38
|
+
[Getting Started](../getting-started.md#antigravity), and a
|
|
39
|
+
[troubleshooting entry](../process/troubleshooting.md#gemini-cli-runs-a-mirror-hook-that-no-longer-exists)
|
|
40
|
+
for the symptom a Gemini CLI user would meet.
|
|
41
|
+
- **A retired-surface guard keeps the removal removed.** `.gemini/` can never
|
|
42
|
+
ship again from this package: the row is enforced on every push and by the
|
|
43
|
+
release tripwire. That matters because `mirror init` once told users to
|
|
44
|
+
write absolute paths into that directory into their settings — a standing
|
|
45
|
+
instruction to execute whatever a later release might put there.
|
|
46
|
+
|
|
47
|
+
## Where We Started
|
|
48
|
+
|
|
49
|
+
v0.32.1 shipped Gemini CLI as one of four supported runtimes, at L4 parity,
|
|
50
|
+
wired by two documented steps. On 2026-09-25 the Navigator retired it —
|
|
51
|
+
Google had set a deprecation date for the Gemini Code Assist backend for
|
|
52
|
+
individuals, and Antigravity, which imports Claude-format plugins, was the
|
|
53
|
+
successor — but the call lived in one story's notes while the release kept
|
|
54
|
+
advertising the runtime. The decision was recorded on 2026-10-13, with a
|
|
55
|
+
condition: the integration goes only behind a documented cutoff, and only
|
|
56
|
+
once Antigravity meets the bar a Gemini CLI user already had. DS7 built the
|
|
57
|
+
agy-native layer and measured the bar. DS8 is the cutoff.
|
|
58
|
+
|
|
59
|
+
## What Changed
|
|
60
|
+
|
|
61
|
+
**The integration is gone from the package.** No `.gemini/hooks`, no `gemini`
|
|
62
|
+
entry in the wiring `mirror init` prints, no `gemini:*` hook names — an
|
|
63
|
+
unknown name is noted in `hooks.log` and the turn proceeds, as any unknown
|
|
64
|
+
name does. The Gemini smoke and the npm smoke's Gemini section retired with
|
|
65
|
+
it; the hook payload reader dropped the one field only Gemini CLI supplied.
|
|
66
|
+
|
|
67
|
+
**The docs say what ships.** Twenty files: README, Getting Started,
|
|
68
|
+
REFERENCE, the Operating Instructions and their plugin copy, the
|
|
69
|
+
runtime-interface spec, the welcome spec, the architecture, and the skills
|
|
70
|
+
that said "Pi and Gemini CLI". Where a runtime list, a wiring step, or a
|
|
71
|
+
parity table had Gemini CLI, it has Antigravity. Where a lesson says what
|
|
72
|
+
Gemini CLI taught — the payload-shape bug its smoke caught, the one-JSON-
|
|
73
|
+
object rule its hook entries kept — the lesson stays, in the past tense.
|
|
74
|
+
Getting Started's subscription table no longer carries a Gemini AI Pro row
|
|
75
|
+
and invents no Antigravity row: what Antigravity requires is Google's to
|
|
76
|
+
say, and Mirror needs only the `agy` command on your PATH.
|
|
77
|
+
|
|
78
|
+
**Antigravity's own documentation is unchanged** beyond taking Gemini CLI's
|
|
79
|
+
place: DS7 wrote it and measured it.
|
|
80
|
+
|
|
81
|
+
## The Cutoff: Gemini CLI
|
|
82
|
+
|
|
83
|
+
**What you keep.** Everything that is yours. Your `~/.mirror-minds/<user>`
|
|
84
|
+
home, your identity, your memories, your journeys, and every conversation
|
|
85
|
+
you logged — including the ones labelled `gemini_cli`, which stay readable
|
|
86
|
+
in `mm-conversations` and `mm-recall` and are never migrated or relabelled.
|
|
87
|
+
Only the runtime that reads them changes.
|
|
88
|
+
|
|
89
|
+
**Removed.** The four `.gemini/hooks` wrappers (`session-start.sh`,
|
|
90
|
+
`log-user.sh`, `log-assistant.sh`, `session-end.sh`) and the `gemini:*` hook
|
|
91
|
+
family behind them; the two Gemini CLI steps `mirror init` printed (the
|
|
92
|
+
skills link, the four hooks merged into `~/.gemini/settings.json`); the
|
|
93
|
+
Gemini smoke; the runtime from the package's description and keywords and
|
|
94
|
+
from every runtime-facing document.
|
|
95
|
+
|
|
96
|
+
**Why.** Google deprecated the Gemini Code Assist backend for individuals
|
|
97
|
+
(2026-06-18). Antigravity is its successor and imports Claude-format
|
|
98
|
+
plugins — the one plugin Mirror already ships. The removal waited for
|
|
99
|
+
DS7.US4's measurement on agy 1.4.2: all five items of the bar DS12.TS3 set
|
|
100
|
+
PASS, with per-turn injection — the cost the decision feared losing —
|
|
101
|
+
carried by agy's `PreInvocation` hook. The one difference that did
|
|
102
|
+
materialize: agy has no session-end signal, so a conversation closes when
|
|
103
|
+
your next session starts rather than at exit (as Codex's does); logging
|
|
104
|
+
parity holds, and memories extract after the close.
|
|
105
|
+
|
|
106
|
+
**What to do instead.** Install Antigravity, then the one step `mirror init`
|
|
107
|
+
prints when it finds `agy` on your PATH:
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
agy plugin install "<pkg>/plugins/mirror-mind" # <pkg> = $(npm root -g)/mirror-mind
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
Your home carries over unchanged — nothing to export, nothing to re-seed.
|
|
114
|
+
Same home, same memory, different door. The skills carry the plugin's name:
|
|
115
|
+
`/mirror-mind:mm:mirror`, `/mirror-mind:mm:journeys`, and so on.
|
|
116
|
+
|
|
117
|
+
**What still works.** Gemini *models*. Memory extraction runs on
|
|
118
|
+
`google/gemini-2.5-flash-lite` through OpenRouter, and `consult` reaches the
|
|
119
|
+
`gemini` family as before. Those are model choices, not the runtime.
|
|
120
|
+
|
|
121
|
+
**If you do nothing.** After the update, the four `mirror-*` hook commands in
|
|
122
|
+
your `~/.gemini/settings.json` point at files that no longer exist, while the
|
|
123
|
+
links in `~/.gemini/skills/` still resolve — `.pi/skills` keeps shipping. So
|
|
124
|
+
Gemini CLI keeps Mirror's skills and loses logging and Mirror Mode injection.
|
|
125
|
+
Said plainly: your Gemini CLI sessions stop being logged and Mirror Mode
|
|
126
|
+
stops being injected, and nothing on screen says so. Measured on Gemini CLI
|
|
127
|
+
0.61.0 in headless mode (`gemini -p`), 2026-10-18: a registered hook whose
|
|
128
|
+
command is missing is skipped silently; the session proceeds. The entries to
|
|
129
|
+
delete, in `~/.gemini/settings.json`: `mirror-session-start` (under
|
|
130
|
+
`SessionStart`), `mirror-log-user` (`BeforeAgent`), `mirror-log-assistant`
|
|
131
|
+
(`AfterAgent`), `mirror-session-end` (`SessionEnd`); and the `mm-*` links in
|
|
132
|
+
`~/.gemini/skills/`. Mirror never wrote those files and does not edit them.
|
|
133
|
+
Whether Antigravity also reads `~/.gemini/skills/` — which would show a
|
|
134
|
+
migrated user the stale links as duplicate skills — was not measured;
|
|
135
|
+
remove the links either way.
|
|
136
|
+
|
|
137
|
+
**To stay on Gemini CLI a while longer.** `npm install -g mirror-mind@0.34.0`
|
|
138
|
+
is the last release that carries the integration. The welcome card and
|
|
139
|
+
`runtime update` will keep offering the newer version every session; the pin
|
|
140
|
+
is a choice to make knowingly, not a quiet state.
|
|
141
|
+
|
|
142
|
+
## Upgrading
|
|
143
|
+
|
|
144
|
+
```bash
|
|
145
|
+
npm install -g mirror-mind@latest # or: mirror runtime update
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
**If you used Gemini CLI:** read the cutoff above; the steps are in Getting
|
|
149
|
+
Started's Antigravity section. **Every other runtime:** nothing to do. No
|
|
150
|
+
migration. Existing homes, sessions, and memories are untouched.
|
|
151
|
+
|
|
152
|
+
## Also In This Release
|
|
153
|
+
|
|
154
|
+
- **Typecheck is green on `main` again.** DS7.TS3 left one early return in
|
|
155
|
+
`hooks/agy.ts` without the field it had just added; `tsc --noEmit`, a CI
|
|
156
|
+
gate, refused since. Found by DS8.US1's validation, paid in its own commit.
|
|
157
|
+
- **The docs link check is green again.** The plugin's byte copy of the
|
|
158
|
+
Operating Instructions carries a root-relative link that cannot resolve
|
|
159
|
+
under `rules/`; the checker reported it on every `.md` push since DS7.US1.
|
|
160
|
+
The copy's links now resolve where the source lives — not skipped: a link
|
|
161
|
+
the source cannot resolve still fails through the copy.
|
|
162
|
+
- **The P1 redaction invariant outlives the runtime it was written against.**
|
|
163
|
+
"A failed hook never writes the prompt or the reply to `hooks.log`" was
|
|
164
|
+
tested through the Gemini CLI hooks; it now runs through the Claude-family
|
|
165
|
+
hooks every plugin runtime shares.
|
|
166
|
+
|
|
167
|
+
## Conscious Exclusions
|
|
168
|
+
|
|
169
|
+
- **No second cutoff document.** CV22 needed `pending-cutoffs.md` for eleven
|
|
170
|
+
cutoffs in one release; DS8 has one, and this note is its record.
|
|
171
|
+
- **Mirror does not edit `~/.gemini/settings.json` or `~/.gemini/skills/`.**
|
|
172
|
+
It never wrote them. The note and the guidance name what to remove.
|
|
173
|
+
- **The Claude plugin smoke's negative probe** still cannot run on a machine
|
|
174
|
+
with a globally installed `mirror-hook` (CR142, D-031): pre-existing,
|
|
175
|
+
local-only, carried.
|
|
176
|
+
- **DS5, DS10, DS11** — user extensions on every runtime, Grok through the
|
|
177
|
+
plugin, the contract and docs refresh — are CV21's remaining stories, not
|
|
178
|
+
this release's.
|
|
179
|
+
|
|
180
|
+
## What We Learned
|
|
181
|
+
|
|
182
|
+
A retirement that lives in a decision while the release advertises the
|
|
183
|
+
runtime has not happened; it happened here in three stories, in order:
|
|
184
|
+
remove, then make the tree say so, then tell the user — and the guard that
|
|
185
|
+
proves each step split by story, so nothing went red between them. The
|
|
186
|
+
sweep found what the inventory missed (a spec line, a history link), which
|
|
187
|
+
is what a guard is for. And the one fact this note most needed — what a
|
|
188
|
+
user who does nothing will see — turned out to be *nothing*: Gemini CLI
|
|
189
|
+
skips a missing hook in silence. That is the strongest reason the cutoff is
|
|
190
|
+
written where the user looks, not only here.
|
|
191
|
+
|
|
192
|
+
## Next Horizon
|
|
193
|
+
|
|
194
|
+
DS8 closes with this release, and with it the runtime set CV21 set out to
|
|
195
|
+
reach: the canonical plugin on Claude Code, Codex, and Antigravity, Pi
|
|
196
|
+
through its extension. What remains in CV21 is pulled deliberately from the
|
|
197
|
+
CV21 index: user extensions on every runtime (DS5), Grok through the plugin
|
|
198
|
+
(DS10), and the contract and docs refresh that folds all of it into one
|
|
199
|
+
account (DS11).
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mirror-mind",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Mirror Mind: a local-first memory and identity framework for agentic AI runtimes (Pi, Claude Code,
|
|
3
|
+
"version": "0.35.0",
|
|
4
|
+
"description": "Mirror Mind: a local-first memory and identity framework for agentic AI runtimes (Pi, Claude Code, Codex, Antigravity)",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
@@ -15,7 +15,6 @@
|
|
|
15
15
|
"agent",
|
|
16
16
|
"pi-package",
|
|
17
17
|
"claude-code",
|
|
18
|
-
"gemini-cli",
|
|
19
18
|
"codex",
|
|
20
19
|
"sqlite",
|
|
21
20
|
"local-first"
|
|
@@ -36,7 +35,6 @@
|
|
|
36
35
|
".pi/extensions",
|
|
37
36
|
".pi/skills",
|
|
38
37
|
".claude/hooks",
|
|
39
|
-
".gemini/hooks",
|
|
40
38
|
"plugins/mirror-mind",
|
|
41
39
|
".agents/plugins",
|
|
42
40
|
"AGENTS.md",
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Mirror Mind hook wrapper — GENERATED by scripts/generate_hook_wrappers.sh.
|
|
3
|
+
# Edit the generator, never this file: every agy wrapper shares these bytes
|
|
4
|
+
# and differs only in the hook name and its stdin gate (CV21.DS7.TS2).
|
|
5
|
+
set -uo pipefail
|
|
6
|
+
HOOK="agy:pre-invocation"
|
|
7
|
+
GATE='"invocationNum"[[:space:]]*:[[:space:]]*-?[0-9]*[1-9]'
|
|
8
|
+
|
|
9
|
+
# agy sends the payload on stdin and blocks its agent loop on this process.
|
|
10
|
+
# Read the payload once; when the gate says this invocation is not ours,
|
|
11
|
+
# answer the empty object without resolving Node at all.
|
|
12
|
+
PAYLOAD="$(cat 2>/dev/null || true)"
|
|
13
|
+
if [ -n "$GATE" ] && printf '%s' "$PAYLOAD" | grep -qE "$GATE"; then
|
|
14
|
+
printf '{}\n'
|
|
15
|
+
exit 0
|
|
16
|
+
fi
|
|
17
|
+
|
|
18
|
+
# This wrapper is the Claude plugin's, and a plugin is copied into Claude
|
|
19
|
+
# Code's own cache: no relative path from here reaches the Mirror tree. It
|
|
20
|
+
# runs the installed `mirror-hook` bin instead, and reads no tree .env.
|
|
21
|
+
REPO_ROOT=""
|
|
22
|
+
|
|
23
|
+
# A hook never fails the user's turn, and never fails in silence: when it
|
|
24
|
+
# cannot do its job it appends ONE line to <mirror home>/hooks.log and exits
|
|
25
|
+
# 0 — after answering agy's contract, so the agent loop never stalls.
|
|
26
|
+
env_file_value() {
|
|
27
|
+
[ -f "$2" ] || return 0
|
|
28
|
+
sed -n -E "s/^[[:space:]]*(export[[:space:]]+)?$1[[:space:]]*=[[:space:]]*//p" "$2" \
|
|
29
|
+
| tail -n 1 | tr -d "\"'\r"
|
|
30
|
+
}
|
|
31
|
+
env_value() {
|
|
32
|
+
local value=""
|
|
33
|
+
if [ -n "${!1+set}" ]; then printf '%s' "${!1}"; return; fi
|
|
34
|
+
[ -z "$REPO_ROOT" ] || value="$(env_file_value "$1" "$REPO_ROOT/.env")"
|
|
35
|
+
[ -n "$value" ] || value="$(env_file_value "$1" "${XDG_CONFIG_HOME:-$HOME/.config}/mirror/env")"
|
|
36
|
+
printf '%s' "$value"
|
|
37
|
+
}
|
|
38
|
+
note() {
|
|
39
|
+
local home user
|
|
40
|
+
home="$(env_value MIRROR_HOME)"
|
|
41
|
+
user="$(env_value MIRROR_USER)"
|
|
42
|
+
if [ -n "$home" ]; then
|
|
43
|
+
home="${home/#\~/$HOME}"
|
|
44
|
+
elif [ -n "$user" ]; then
|
|
45
|
+
home="$HOME/.mirror-minds/$user"
|
|
46
|
+
[ -d "$home" ] || [ ! -d "$HOME/.mirror/$user" ] || home="$HOME/.mirror/$user"
|
|
47
|
+
else
|
|
48
|
+
return 0
|
|
49
|
+
fi
|
|
50
|
+
mkdir -p "$home" 2>/dev/null \
|
|
51
|
+
&& printf '%s %s: %s\n' "$(date -u +%Y-%m-%dT%H:%M:%SZ)" "$HOOK" "$1" >> "$home/hooks.log" 2>/dev/null
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
# Find the bin. MIRROR_BIN names the directory holding `mirror` and
|
|
55
|
+
# `mirror-hook` (`$(npm prefix -g)/bin`), the same trust class as MIRROR_NODE;
|
|
56
|
+
# then the PATH; then the global bin directories a GUI launch leaves off it.
|
|
57
|
+
ENTRY=""
|
|
58
|
+
[ -n "${MIRROR_BIN:-}" ] && [ -x "$MIRROR_BIN/mirror-hook" ] && ENTRY="$MIRROR_BIN/mirror-hook"
|
|
59
|
+
[ -n "$ENTRY" ] || ENTRY="$(command -v mirror-hook 2>/dev/null || true)"
|
|
60
|
+
for candidate in "$HOME/.nvm/current/bin" /opt/homebrew/bin /usr/local/bin; do
|
|
61
|
+
[ -n "$ENTRY" ] && break
|
|
62
|
+
[ -x "$candidate/mirror-hook" ] && ENTRY="$candidate/mirror-hook"
|
|
63
|
+
done
|
|
64
|
+
|
|
65
|
+
if [ -z "$ENTRY" ]; then
|
|
66
|
+
note "mirror-hook not found on PATH; hook skipped. Install mirror-mind (npm install -g mirror-mind) or set MIRROR_BIN to its bin directory."
|
|
67
|
+
exit 0
|
|
68
|
+
fi
|
|
69
|
+
|
|
70
|
+
NODE="${MIRROR_NODE:-}"
|
|
71
|
+
if [ -z "$NODE" ] || [ ! -x "$NODE" ]; then
|
|
72
|
+
NODE="$(command -v node 2>/dev/null || true)"
|
|
73
|
+
fi
|
|
74
|
+
for candidate in "$(dirname "$ENTRY")/node" "$HOME/.nvm/current/bin/node" /opt/homebrew/bin/node /usr/local/bin/node; do
|
|
75
|
+
[ -n "$NODE" ] && break
|
|
76
|
+
[ -x "$candidate" ] && NODE="$candidate"
|
|
77
|
+
done
|
|
78
|
+
|
|
79
|
+
if [ -z "$NODE" ]; then
|
|
80
|
+
printf '{}\n'
|
|
81
|
+
note "node not found on PATH; hook skipped. Set MIRROR_NODE."
|
|
82
|
+
exit 0
|
|
83
|
+
fi
|
|
84
|
+
|
|
85
|
+
# The payload was consumed by the gate read; re-feed it to the entry.
|
|
86
|
+
printf '%s' "$PAYLOAD" | "$NODE" "$ENTRY" "$HOOK" "$@"
|
|
87
|
+
status=$?
|
|
88
|
+
if [ "$status" -ne 0 ]; then
|
|
89
|
+
printf '{}\n'
|
|
90
|
+
note "node exited $status before the hook finished ($NODE, $("$NODE" --version 2>/dev/null || echo 'version unknown')); Mirror needs Node 24 or later. Set MIRROR_NODE."
|
|
91
|
+
fi
|
|
92
|
+
exit 0
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Mirror Mind hook wrapper — GENERATED by scripts/generate_hook_wrappers.sh.
|
|
3
|
+
# Edit the generator, never this file: every agy wrapper shares these bytes
|
|
4
|
+
# and differs only in the hook name and its stdin gate (CV21.DS7.TS2).
|
|
5
|
+
set -uo pipefail
|
|
6
|
+
HOOK="agy:session-start"
|
|
7
|
+
GATE=''
|
|
8
|
+
|
|
9
|
+
# agy sends the payload on stdin and blocks its agent loop on this process.
|
|
10
|
+
# Read the payload once; when the gate says this invocation is not ours,
|
|
11
|
+
# answer the empty object without resolving Node at all.
|
|
12
|
+
PAYLOAD="$(cat 2>/dev/null || true)"
|
|
13
|
+
if [ -n "$GATE" ] && printf '%s' "$PAYLOAD" | grep -qE "$GATE"; then
|
|
14
|
+
printf '{}\n'
|
|
15
|
+
exit 0
|
|
16
|
+
fi
|
|
17
|
+
|
|
18
|
+
# This wrapper is the Claude plugin's, and a plugin is copied into Claude
|
|
19
|
+
# Code's own cache: no relative path from here reaches the Mirror tree. It
|
|
20
|
+
# runs the installed `mirror-hook` bin instead, and reads no tree .env.
|
|
21
|
+
REPO_ROOT=""
|
|
22
|
+
|
|
23
|
+
# A hook never fails the user's turn, and never fails in silence: when it
|
|
24
|
+
# cannot do its job it appends ONE line to <mirror home>/hooks.log and exits
|
|
25
|
+
# 0 — after answering agy's contract, so the agent loop never stalls.
|
|
26
|
+
env_file_value() {
|
|
27
|
+
[ -f "$2" ] || return 0
|
|
28
|
+
sed -n -E "s/^[[:space:]]*(export[[:space:]]+)?$1[[:space:]]*=[[:space:]]*//p" "$2" \
|
|
29
|
+
| tail -n 1 | tr -d "\"'\r"
|
|
30
|
+
}
|
|
31
|
+
env_value() {
|
|
32
|
+
local value=""
|
|
33
|
+
if [ -n "${!1+set}" ]; then printf '%s' "${!1}"; return; fi
|
|
34
|
+
[ -z "$REPO_ROOT" ] || value="$(env_file_value "$1" "$REPO_ROOT/.env")"
|
|
35
|
+
[ -n "$value" ] || value="$(env_file_value "$1" "${XDG_CONFIG_HOME:-$HOME/.config}/mirror/env")"
|
|
36
|
+
printf '%s' "$value"
|
|
37
|
+
}
|
|
38
|
+
note() {
|
|
39
|
+
local home user
|
|
40
|
+
home="$(env_value MIRROR_HOME)"
|
|
41
|
+
user="$(env_value MIRROR_USER)"
|
|
42
|
+
if [ -n "$home" ]; then
|
|
43
|
+
home="${home/#\~/$HOME}"
|
|
44
|
+
elif [ -n "$user" ]; then
|
|
45
|
+
home="$HOME/.mirror-minds/$user"
|
|
46
|
+
[ -d "$home" ] || [ ! -d "$HOME/.mirror/$user" ] || home="$HOME/.mirror/$user"
|
|
47
|
+
else
|
|
48
|
+
return 0
|
|
49
|
+
fi
|
|
50
|
+
mkdir -p "$home" 2>/dev/null \
|
|
51
|
+
&& printf '%s %s: %s\n' "$(date -u +%Y-%m-%dT%H:%M:%SZ)" "$HOOK" "$1" >> "$home/hooks.log" 2>/dev/null
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
# Find the bin. MIRROR_BIN names the directory holding `mirror` and
|
|
55
|
+
# `mirror-hook` (`$(npm prefix -g)/bin`), the same trust class as MIRROR_NODE;
|
|
56
|
+
# then the PATH; then the global bin directories a GUI launch leaves off it.
|
|
57
|
+
ENTRY=""
|
|
58
|
+
[ -n "${MIRROR_BIN:-}" ] && [ -x "$MIRROR_BIN/mirror-hook" ] && ENTRY="$MIRROR_BIN/mirror-hook"
|
|
59
|
+
[ -n "$ENTRY" ] || ENTRY="$(command -v mirror-hook 2>/dev/null || true)"
|
|
60
|
+
for candidate in "$HOME/.nvm/current/bin" /opt/homebrew/bin /usr/local/bin; do
|
|
61
|
+
[ -n "$ENTRY" ] && break
|
|
62
|
+
[ -x "$candidate/mirror-hook" ] && ENTRY="$candidate/mirror-hook"
|
|
63
|
+
done
|
|
64
|
+
|
|
65
|
+
if [ -z "$ENTRY" ]; then
|
|
66
|
+
note "mirror-hook not found on PATH; hook skipped. Install mirror-mind (npm install -g mirror-mind) or set MIRROR_BIN to its bin directory."
|
|
67
|
+
exit 0
|
|
68
|
+
fi
|
|
69
|
+
|
|
70
|
+
NODE="${MIRROR_NODE:-}"
|
|
71
|
+
if [ -z "$NODE" ] || [ ! -x "$NODE" ]; then
|
|
72
|
+
NODE="$(command -v node 2>/dev/null || true)"
|
|
73
|
+
fi
|
|
74
|
+
for candidate in "$(dirname "$ENTRY")/node" "$HOME/.nvm/current/bin/node" /opt/homebrew/bin/node /usr/local/bin/node; do
|
|
75
|
+
[ -n "$NODE" ] && break
|
|
76
|
+
[ -x "$candidate" ] && NODE="$candidate"
|
|
77
|
+
done
|
|
78
|
+
|
|
79
|
+
if [ -z "$NODE" ]; then
|
|
80
|
+
printf '{}\n'
|
|
81
|
+
note "node not found on PATH; hook skipped. Set MIRROR_NODE."
|
|
82
|
+
exit 0
|
|
83
|
+
fi
|
|
84
|
+
|
|
85
|
+
# The payload was consumed by the gate read; re-feed it to the entry.
|
|
86
|
+
printf '%s' "$PAYLOAD" | "$NODE" "$ENTRY" "$HOOK" "$@"
|
|
87
|
+
status=$?
|
|
88
|
+
if [ "$status" -ne 0 ]; then
|
|
89
|
+
printf '{}\n'
|
|
90
|
+
note "node exited $status before the hook finished ($NODE, $("$NODE" --version 2>/dev/null || echo 'version unknown')); Mirror needs Node 24 or later. Set MIRROR_NODE."
|
|
91
|
+
fi
|
|
92
|
+
exit 0
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Mirror Mind hook wrapper — GENERATED by scripts/generate_hook_wrappers.sh.
|
|
3
|
+
# Edit the generator, never this file: every agy wrapper shares these bytes
|
|
4
|
+
# and differs only in the hook name and its stdin gate (CV21.DS7.TS2).
|
|
5
|
+
set -uo pipefail
|
|
6
|
+
HOOK="agy:stop"
|
|
7
|
+
GATE=''
|
|
8
|
+
|
|
9
|
+
# agy sends the payload on stdin and blocks its agent loop on this process.
|
|
10
|
+
# Read the payload once; when the gate says this invocation is not ours,
|
|
11
|
+
# answer the empty object without resolving Node at all.
|
|
12
|
+
PAYLOAD="$(cat 2>/dev/null || true)"
|
|
13
|
+
if [ -n "$GATE" ] && printf '%s' "$PAYLOAD" | grep -qE "$GATE"; then
|
|
14
|
+
printf '{}\n'
|
|
15
|
+
exit 0
|
|
16
|
+
fi
|
|
17
|
+
|
|
18
|
+
# This wrapper is the Claude plugin's, and a plugin is copied into Claude
|
|
19
|
+
# Code's own cache: no relative path from here reaches the Mirror tree. It
|
|
20
|
+
# runs the installed `mirror-hook` bin instead, and reads no tree .env.
|
|
21
|
+
REPO_ROOT=""
|
|
22
|
+
|
|
23
|
+
# A hook never fails the user's turn, and never fails in silence: when it
|
|
24
|
+
# cannot do its job it appends ONE line to <mirror home>/hooks.log and exits
|
|
25
|
+
# 0 — after answering agy's contract, so the agent loop never stalls.
|
|
26
|
+
env_file_value() {
|
|
27
|
+
[ -f "$2" ] || return 0
|
|
28
|
+
sed -n -E "s/^[[:space:]]*(export[[:space:]]+)?$1[[:space:]]*=[[:space:]]*//p" "$2" \
|
|
29
|
+
| tail -n 1 | tr -d "\"'\r"
|
|
30
|
+
}
|
|
31
|
+
env_value() {
|
|
32
|
+
local value=""
|
|
33
|
+
if [ -n "${!1+set}" ]; then printf '%s' "${!1}"; return; fi
|
|
34
|
+
[ -z "$REPO_ROOT" ] || value="$(env_file_value "$1" "$REPO_ROOT/.env")"
|
|
35
|
+
[ -n "$value" ] || value="$(env_file_value "$1" "${XDG_CONFIG_HOME:-$HOME/.config}/mirror/env")"
|
|
36
|
+
printf '%s' "$value"
|
|
37
|
+
}
|
|
38
|
+
note() {
|
|
39
|
+
local home user
|
|
40
|
+
home="$(env_value MIRROR_HOME)"
|
|
41
|
+
user="$(env_value MIRROR_USER)"
|
|
42
|
+
if [ -n "$home" ]; then
|
|
43
|
+
home="${home/#\~/$HOME}"
|
|
44
|
+
elif [ -n "$user" ]; then
|
|
45
|
+
home="$HOME/.mirror-minds/$user"
|
|
46
|
+
[ -d "$home" ] || [ ! -d "$HOME/.mirror/$user" ] || home="$HOME/.mirror/$user"
|
|
47
|
+
else
|
|
48
|
+
return 0
|
|
49
|
+
fi
|
|
50
|
+
mkdir -p "$home" 2>/dev/null \
|
|
51
|
+
&& printf '%s %s: %s\n' "$(date -u +%Y-%m-%dT%H:%M:%SZ)" "$HOOK" "$1" >> "$home/hooks.log" 2>/dev/null
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
# Find the bin. MIRROR_BIN names the directory holding `mirror` and
|
|
55
|
+
# `mirror-hook` (`$(npm prefix -g)/bin`), the same trust class as MIRROR_NODE;
|
|
56
|
+
# then the PATH; then the global bin directories a GUI launch leaves off it.
|
|
57
|
+
ENTRY=""
|
|
58
|
+
[ -n "${MIRROR_BIN:-}" ] && [ -x "$MIRROR_BIN/mirror-hook" ] && ENTRY="$MIRROR_BIN/mirror-hook"
|
|
59
|
+
[ -n "$ENTRY" ] || ENTRY="$(command -v mirror-hook 2>/dev/null || true)"
|
|
60
|
+
for candidate in "$HOME/.nvm/current/bin" /opt/homebrew/bin /usr/local/bin; do
|
|
61
|
+
[ -n "$ENTRY" ] && break
|
|
62
|
+
[ -x "$candidate/mirror-hook" ] && ENTRY="$candidate/mirror-hook"
|
|
63
|
+
done
|
|
64
|
+
|
|
65
|
+
if [ -z "$ENTRY" ]; then
|
|
66
|
+
note "mirror-hook not found on PATH; hook skipped. Install mirror-mind (npm install -g mirror-mind) or set MIRROR_BIN to its bin directory."
|
|
67
|
+
exit 0
|
|
68
|
+
fi
|
|
69
|
+
|
|
70
|
+
NODE="${MIRROR_NODE:-}"
|
|
71
|
+
if [ -z "$NODE" ] || [ ! -x "$NODE" ]; then
|
|
72
|
+
NODE="$(command -v node 2>/dev/null || true)"
|
|
73
|
+
fi
|
|
74
|
+
for candidate in "$(dirname "$ENTRY")/node" "$HOME/.nvm/current/bin/node" /opt/homebrew/bin/node /usr/local/bin/node; do
|
|
75
|
+
[ -n "$NODE" ] && break
|
|
76
|
+
[ -x "$candidate" ] && NODE="$candidate"
|
|
77
|
+
done
|
|
78
|
+
|
|
79
|
+
if [ -z "$NODE" ]; then
|
|
80
|
+
printf '{}\n'
|
|
81
|
+
note "node not found on PATH; hook skipped. Set MIRROR_NODE."
|
|
82
|
+
exit 0
|
|
83
|
+
fi
|
|
84
|
+
|
|
85
|
+
# The payload was consumed by the gate read; re-feed it to the entry.
|
|
86
|
+
printf '%s' "$PAYLOAD" | "$NODE" "$ENTRY" "$HOOK" "$@"
|
|
87
|
+
status=$?
|
|
88
|
+
if [ "$status" -ne 0 ]; then
|
|
89
|
+
printf '{}\n'
|
|
90
|
+
note "node exited $status before the hook finished ($NODE, $("$NODE" --version 2>/dev/null || echo 'version unknown')); Mirror needs Node 24 or later. Set MIRROR_NODE."
|
|
91
|
+
fi
|
|
92
|
+
exit 0
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"mirror-mind": {
|
|
3
|
+
"enabled": true,
|
|
4
|
+
"SessionStart": [
|
|
5
|
+
{
|
|
6
|
+
"type": "command",
|
|
7
|
+
"command": "agy/session-start.sh",
|
|
8
|
+
"timeout": 60
|
|
9
|
+
}
|
|
10
|
+
],
|
|
11
|
+
"PreInvocation": [
|
|
12
|
+
{
|
|
13
|
+
"type": "command",
|
|
14
|
+
"command": "agy/pre-invocation.sh",
|
|
15
|
+
"timeout": 15
|
|
16
|
+
}
|
|
17
|
+
],
|
|
18
|
+
"Stop": [
|
|
19
|
+
{
|
|
20
|
+
"type": "command",
|
|
21
|
+
"command": "agy/stop.sh",
|
|
22
|
+
"timeout": 30
|
|
23
|
+
}
|
|
24
|
+
]
|
|
25
|
+
}
|
|
26
|
+
}
|