diff --git a/plugins/antianqi/mcode-island/.claude-plugin/plugin.json b/plugins/antianqi/mcode-island/.claude-plugin/plugin.json new file mode 100644 index 00000000..3323015b --- /dev/null +++ b/plugins/antianqi/mcode-island/.claude-plugin/plugin.json @@ -0,0 +1,285 @@ +{ + "name": "mcode-island", + "version": "1.0.0", + "displayName": "mcode Dynamic Island", + "description": "Windows Dynamic Island status pill for MiniMax Code agents. Each session event (SessionStart, PreToolUse, PostToolUse, Stop, etc.) is forwarded to a top-of-screen WPF widget so the user can see the agent working/thinking/done without keeping mcode in focus. No credentials, no network, no telemetry, no third-party services. Pure local PowerShell + WPF on Windows.", + "author": { + "name": "antianqi", + "url": "https://github.com/antianqi" + }, + "homepage": "https://github.com/MiniMax-AI/MiniMax-Code-Plugins/tree/main/plugins/antianqi/mcode-island", + "repository": "https://github.com/MiniMax-AI/MiniMax-Code-Plugins/tree/main/plugins/antianqi/mcode-island", + "license": "Apache-2.0", + "keywords": [ + "mcode", + "windows", + "wpf", + "powershell", + "status", + "ui", + "dynamic-island", + "hooks", + "session-events" + ], + "skills": ["./skills"], + "_compatibility": { + "mcodeMin": "0.4.0", + "hookSchema": "CLAUDE", + "events": [ + "SessionStart", + "SessionEnd", + "UserPromptSubmit", + "PreToolUse", + "PostToolUse", + "Stop", + "PreCompact", + "Notification", + "SubagentStart", + "SubagentStop", + "PermissionRequest", + "PermissionDenied" + ] + }, + "hooks": { + "SessionStart": [ + { + "matcher": "*", + "hooks": [ + { + "type": "command", + "command": "powershell", + "args": [ + "-NoProfile", + "-ExecutionPolicy", + "Bypass", + "-File", + "${PLUGIN_ROOT}/io.minimax.mcode/hooks/scripts/session-start.ps1" + ], + "shell": "powershell", + "timeout": 5 + } + ] + } + ], + "SessionEnd": [ + { + "matcher": "*", + "hooks": [ + { + "type": "command", + "command": "powershell", + "args": [ + "-NoProfile", + "-ExecutionPolicy", + "Bypass", + "-File", + "${PLUGIN_ROOT}/io.minimax.mcode/hooks/scripts/session-end.ps1" + ], + "shell": "powershell", + "timeout": 5 + } + ] + } + ], + "UserPromptSubmit": [ + { + "matcher": "*", + "hooks": [ + { + "type": "command", + "command": "powershell", + "args": [ + "-NoProfile", + "-ExecutionPolicy", + "Bypass", + "-File", + "${PLUGIN_ROOT}/io.minimax.mcode/hooks/scripts/user-prompt-submit.ps1" + ], + "shell": "powershell", + "timeout": 5 + } + ] + } + ], + "PreToolUse": [ + { + "matcher": "*", + "hooks": [ + { + "type": "command", + "command": "powershell", + "args": [ + "-NoProfile", + "-ExecutionPolicy", + "Bypass", + "-File", + "${PLUGIN_ROOT}/io.minimax.mcode/hooks/scripts/pre-tool-use.ps1" + ], + "shell": "powershell", + "timeout": 5 + } + ] + } + ], + "PostToolUse": [ + { + "matcher": "*", + "hooks": [ + { + "type": "command", + "command": "powershell", + "args": [ + "-NoProfile", + "-ExecutionPolicy", + "Bypass", + "-File", + "${PLUGIN_ROOT}/io.minimax.mcode/hooks/scripts/post-tool-use.ps1" + ], + "shell": "powershell", + "timeout": 5 + } + ] + } + ], + "Stop": [ + { + "matcher": "*", + "hooks": [ + { + "type": "command", + "command": "powershell", + "args": [ + "-NoProfile", + "-ExecutionPolicy", + "Bypass", + "-File", + "${PLUGIN_ROOT}/io.minimax.mcode/hooks/scripts/stop.ps1" + ], + "shell": "powershell", + "timeout": 5 + } + ] + } + ], + "PreCompact": [ + { + "matcher": "*", + "hooks": [ + { + "type": "command", + "command": "powershell", + "args": [ + "-NoProfile", + "-ExecutionPolicy", + "Bypass", + "-File", + "${PLUGIN_ROOT}/io.minimax.mcode/hooks/scripts/pre-compact.ps1" + ], + "shell": "powershell", + "timeout": 5 + } + ] + } + ], + "Notification": [ + { + "matcher": "*", + "hooks": [ + { + "type": "command", + "command": "powershell", + "args": [ + "-NoProfile", + "-ExecutionPolicy", + "Bypass", + "-File", + "${PLUGIN_ROOT}/io.minimax.mcode/hooks/scripts/notification.ps1" + ], + "shell": "powershell", + "timeout": 5 + } + ] + } + ], + "SubagentStart": [ + { + "matcher": "*", + "hooks": [ + { + "type": "command", + "command": "powershell", + "args": [ + "-NoProfile", + "-ExecutionPolicy", + "Bypass", + "-File", + "${PLUGIN_ROOT}/io.minimax.mcode/hooks/scripts/subagent-start.ps1" + ], + "shell": "powershell", + "timeout": 5 + } + ] + } + ], + "SubagentStop": [ + { + "matcher": "*", + "hooks": [ + { + "type": "command", + "command": "powershell", + "args": [ + "-NoProfile", + "-ExecutionPolicy", + "Bypass", + "-File", + "${PLUGIN_ROOT}/io.minimax.mcode/hooks/scripts/subagent-stop.ps1" + ], + "shell": "powershell", + "timeout": 5 + } + ] + } + ], + "PermissionRequest": [ + { + "matcher": "*", + "hooks": [ + { + "type": "command", + "command": "powershell", + "args": [ + "-NoProfile", + "-ExecutionPolicy", + "Bypass", + "-File", + "${PLUGIN_ROOT}/io.minimax.mcode/hooks/scripts/permission-request.ps1" + ], + "shell": "powershell", + "timeout": 5 + } + ] + } + ], + "PermissionDenied": [ + { + "matcher": "*", + "hooks": [ + { + "type": "command", + "command": "powershell", + "args": [ + "-NoProfile", + "-ExecutionPolicy", + "Bypass", + "-File", + "${PLUGIN_ROOT}/io.minimax.mcode/hooks/scripts/permission-denied.ps1" + ], + "shell": "powershell", + "timeout": 5 + } + ] + } + ] + } +} diff --git a/plugins/antianqi/mcode-island/skills/SKILL.md b/plugins/antianqi/mcode-island/skills/SKILL.md new file mode 100644 index 00000000..6ab1c982 --- /dev/null +++ b/plugins/antianqi/mcode-island/skills/SKILL.md @@ -0,0 +1,241 @@ +--- +name: mcode-island +description: Push the user's terminal out of focus to a Windows desktop Dynamic Island pill so the user can watch your work without switching back to mcode. On mcode 0.2.4+ with the `io.minimax.mcode` Hooks extension enabled (forward-compatible with MiniMax-Code-Plugins PR #20), every tool lifecycle event fires a script under `io.minimax.mcode/hooks/scripts/` automatically — the agent does not need to push states manually. On older mcode or when the extension is not yet active, fall back to calling `notify-island.ps1` before and after each tool call, or use `wrap-tool.ps1` for the bash path. +license: Apache-2.0 +compatibility: Requires Windows 10/11 with PowerShell 5.1+ and the mcode-island widget running (started via `mcode-island start` or `autostart.ps1 -Enable`). Hook-driven mode additionally requires mcode 0.2.4+ with the `io.minimax.mcode` extension namespace accepted by the registry validator. +metadata: + author: antianqi + version: "0.3.0" +--- + +# mcode-island — 桌面灵动岛状态通知 + +让用户在不切回 mcode 窗口的情况下,从桌面顶部悬浮 pill 上看到你(agent)的实时工作状态。 + +## What it looks like + +A 320×60 pill anchored to the top center of the primary display, always-on-top, dark +theme. Six states with distinct color and motion: + +| state | color | icon | meaning | +| ---------- | -------------- | ---- | -------------------------------- | +| `idle` | gray | — | waiting for user input | +| `thinking` | yellow pulse | — | reasoning, no tool call yet | +| `working` | blue pulse | ⚙ | actively running a tool | +| `waiting` | orange | ? | tool needs user approval / input | +| `done` | green | ✓ | step finished, more to do | +| `error` | red | ✕ | tool or step failed | + +Click the pill to switch focus back to the originating terminal tab. Run +`mcode-island pin` from inside a terminal to fix the focus target explicitly +(useful when the auto-detected HWND is wrong, e.g. Windows Terminal multi-tab). + +## Two ways to drive the pill + +### Mode A — Hook-driven (mcode 0.2.4+ with `io.minimax.mcode`) + +When mcode accepts the `io.minimax.mcode` client extension, the runtime spawns +the script under `io.minimax.mcode/hooks/scripts/.ps1` for every matching +lifecycle event. The agent does **not** need to push state manually. + +| event | script | pill state | +| ----------------- | --------------------------------- | ----------- | +| `SessionStart` | `session-start.ps1` | `idle` | +| `SessionEnd` | `session-end.ps1` | `idle` | +| `UserPromptSubmit`| `user-prompt-submit.ps1` | `thinking` | +| `PreToolUse` | `pre-tool-use.ps1` | `working` | +| `PostToolUse` | `post-tool-use.ps1` | `done`/`error` | +| `Stop` | `stop.ps1` | `done` | +| `PreCompact` | `pre-compact.ps1` | `thinking` | +| `Notification` | `notification.ps1` | `idle` | +| `SubagentStart` | `subagent-start.ps1` (CODEX only) | `working` | +| `SubagentStop` | `subagent-stop.ps1` (CODEX only) | `done` | +| `PermissionRequest`| `permission-request.ps1` (returns `{"decision":"allow"}` so the runtime's fail-closed default does not deny) | `waiting` | +| `PermissionDenied`| `permission-denied.ps1` | `error` | + +The hooks conform to the portable spec proposed in +`MiniMax-Code-Plugins` PR #20. Each script reads the JSON event payload from +stdin, calls `notify-island.ps1` with the appropriate state, and exits 0 +(decision-bearing events also write a JSON decision to stdout). Self-push +filtering prevents the pill from churning when the agent calls +`notify-island.ps1` directly through Bash. + +If you are running on mcode 0.2.4+ and the pill is updating itself before you +push anything, Mode A is active. Otherwise fall through to Mode B. + +### Mode B — Agent-pushed (legacy, always works) + +For older mcode, or when the `io.minimax.mcode` extension is not yet active +(registry validator has not accepted the namespace), the agent pushes state +through `notify-island.ps1` directly. The `mcode-status-detect.ps1` detector +also infers state from the runtime's `ledger.jsonl` / `messages.jsonl`, so +the pill will still move — your manual pushes just sharpen the message and +cover edge cases (notably `ask_user`). + +| moment | state | example message | +| ----------------------------------------------------- | --------- | ------------------------------ | +| receive user task, start reasoning | `thinking`| (none) | +| about to invoke any tool | `working` | `"bash: npm test"` | +| tool returned 0, before reporting back | `done` | `"3 files modified"` | +| tool needs approval (e.g. permission prompt) | `waiting` | `"bash: needs approval"` | +| about to call `ask_user` (user must pick) | `waiting` | `"ask_user: 2 options"` | +| user answered `ask_user`, resuming work | `done` | `"ask_user answered"` | +| tool failed / threw / non-zero exit | `error` | `"compile failed: missing import"` | +| conversation idle, waiting for user | `idle` | (none) | + +**`ask_user` is a special tool** — the detector cannot infer it is a "wait for +user" moment (it looks like any other tool call to the session log). When in +Mode B, the agent MUST push `waiting` immediately before invoking `ask_user`, +and `done` immediately after the user answers; otherwise the pill will sit in +`working` (yellow/blue) while the user is actually being asked to decide. In +Mode A, the same coverage comes for free because `ask_user` is a tool call +that fires `PreToolUse`/`PostToolUse`. + +**Never push the same state twice in a row** — the widget de-duplicates by +state+message. Push only on transitions, or include a fresh message each time. + +## Copyable example (agent side, Mode B) + +The plugin ships a thin wrapper `wrap-tool.ps1` that **publishes state only** +(it does NOT execute the command). Run the command via mcode's own bash tool, +then call `wrap-tool.ps1` to publish the outcome: + +```powershell +# Step 1: announce "working" before invoking mcode's bash tool +& "\wrap-tool.ps1" -Tool bash -Command "npm test" -Description "run tests" + +# Step 2: after mcode's bash tool returns, publish the outcome +& "\wrap-tool.ps1" -Tool bash -Command "npm test" -ExitCode $LASTEXITCODE +``` + +`$LASTEXITCODE` is interpreted as: `0` → `done`, codes in `-WaitingExitCodes` +(default `[1]`) → `waiting`, anything else → `error`. The wrapper returns the +exit code unchanged so the calling shell still sees it. + +The wrapper accepts `-Tool bash|read|write|edit|glob|grep|web|task|notebook` and +emits a tool-specific `done` message (e.g. `read C:\path`, `edited file.cs`, +`npm test 完成`) so the pill text is informative. For read/write/edit/glob/grep +the wrapper itself does not execute the command — mcode's own tool does; this +script only publishes the state. + +For other tools (read/write/edit) — and for any state push that is not a single +command — call `notify-island.ps1` directly: + +```powershell +$plugin = "" # directory that contains notify-island.ps1 +& "$plugin\notify-island.ps1" -State thinking +& "$plugin\notify-island.ps1" -State working -Message "read source tree" +& "$plugin\notify-island.ps1" -State done -Message "indexed 142 files" +& "$plugin\notify-island.ps1" -State error -Message "compile failed: missing import" +& "$plugin\notify-island.ps1" -State waiting -Message "permission prompt" +``` + +`` is the directory that contains `notify-island.ps1`. +Substitute the absolute path your user installed the plugin at. The Skill body +deliberately avoids hard-coded paths so any user / any install location works. + +## Expected result + +After each push (or after each hook fires), the widget on the user's primary +display updates within ~400 ms (one polling cycle). On click, the originating +terminal tab regains focus. The widget is intentionally hard to kill: Alt+F4 +hides it, not closes it, and `mcode-island show` re-raises the hidden window +in under 1 second. + +## User-side management + +```cmd +mcode-island REM start the widget (idempotent) +mcode-island stop REM stop the widget +mcode-island status REM show PID + recent log +mcode-island show REM re-raise hidden widget +mcode-island pin REM lock focus target to current foreground window +mcode-island unpin REM clear focus target +mcode-island autostart-on REM register for Windows logon +mcode-island autostart-off REM unregister +``` + +To enable login auto-start, the user runs once: + +```powershell +& "\autostart.ps1" -Enable +``` + +This writes to `HKCU\Software\Microsoft\Windows\CurrentVersion\Run` — no admin +rights required. + +## Runtime data + +All widget state lives under `%APPDATA%\mcode-island\`: + +| file | purpose | +| ---------------- | ----------------------------------------------------- | +| `status.json` | current state (widget polls this every 400 ms) | +| `caller.json` | originating terminal HWND / PID (for click-to-focus) | +| `config.json` | pill position, size, opacity (saved on drag) | +| `widget.pid` | widget process PID (used by start/stop/status) | +| `island.log` | append-only state transition history | +| `widget.log` | widget internal debug log | +| `show.signal` | transient file written by `mcode-island show` | + +No data leaves the local machine *unless* an opt-in 5-hour usage token is +configured. See the **Network access** + **Accounts** sections in +`README.md` for the exact host (`api.minimax.io/v1/coding_plan/remains`), +the rate limit (one GET per 60 s), and the storage locations +(`config.json:planApiToken` or env `MINIMAX_OAUTH_TOKEN` / `MINIMAX_API_KEY`). +When no token is configured the plugin makes no network requests at all. + +## What is in this package + +``` +mcode-island/ +├── plugin.json # plugin manifest +├── README.md # full user-facing docs +├── LICENSE # Apache-2.0 +├── mcode-island.ps1 # WPF widget main loop +├── mcode-island.cmd # CLI shim (start/stop/status/...) +├── start-island.ps1 # launch the widget in STA +├── stop-island.ps1 # stop the widget +├── status-island.ps1 # print widget state +├── show-island.ps1 # re-raise hidden widget +├── pin-island.ps1 # lock focus target to foreground +├── autostart.ps1 # register/unregister Windows logon +├── notify-island.ps1 # state-push helper (agents call this) +├── wrap-tool.ps1 # all-in-one bash wrapper +├── mcode-status-detect.ps1 # runtime-state detector +├── io.minimax.mcode/ # client extension (PR #20 spec) +│ └── hooks/ +│ ├── hooks.json # 12-event declaration +│ └── scripts/ +│ ├── _lib.ps1 # shared helper +│ ├── session-start.ps1 +│ ├── session-end.ps1 +│ ├── user-prompt-submit.ps1 +│ ├── pre-tool-use.ps1 +│ ├── post-tool-use.ps1 +│ ├── stop.ps1 +│ ├── pre-compact.ps1 +│ ├── notification.ps1 +│ ├── subagent-start.ps1 +│ ├── subagent-stop.ps1 +│ ├── permission-request.ps1 +│ └── permission-denied.ps1 +├── skills/mcode-island/SKILL.md # this file +└── assets/ # screenshots used in the README +``` + +## Limitations and known constraints + +- Windows 10/11 only (uses WPF, `user32`, and `kernel32` P/Invoke). +- Single widget per user session. +- Hook-driven mode requires mcode 0.2.4+ Runtime. The portable spec + (`io.minimax.mcode` client extension) is still pending merge in + `MiniMax-Code-Plugins` PR #20; until the registry validator accepts the + namespace, the hooks subdirectory is dormant and the plugin falls back to + Mode B (agent-pushed + detector). +- No hover-expand, no media-control integration yet — see the `v0.2` roadmap in + the upstream issue tracker. +- `wrap-tool.ps1` is a **status publisher only** — it never executes the + command itself (mcode's tool does). The agent still runs every read / write / + edit through mcode and then calls `wrap-tool.ps1` to publish the outcome. + This avoids shell-injection ambiguity from a prior `Invoke-Expression` design.