@maxkle1nz/m1nd 0.9.0-beta.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/EXAMPLES.md +711 -0
- package/LICENSE +21 -0
- package/README.md +361 -0
- package/docs/AGENT-FIRST-DEMO.md +79 -0
- package/docs/AGENT-PACKS.md +107 -0
- package/docs/IDE-INTEGRATIONS.md +136 -0
- package/docs/MCP-HOST-REFRESH.md +123 -0
- package/docs/README.md +36 -0
- package/npm/bin/m1nd.js +9 -0
- package/npm/lib/cli.js +346 -0
- package/npm/test/cli.test.js +49 -0
- package/package.json +47 -0
- package/skills/README.md +26 -0
- package/skills/m1nd-first/SKILL.md +117 -0
- package/skills/m1nd-operator/SKILL.md +166 -0
- package/skills/m1nd-operator/references/l1ght-and-docs.md +164 -0
- package/skills/m1nd-operator/references/routing-playbooks.md +172 -0
- package/skills/m1nd-operator/references/runtime-and-refresh.md +135 -0
- package/skills/m1nd-operator/references/tool-families.md +168 -0
- package/skills/m1nd-operator/scripts/probe_m1nd.py +189 -0
- package/skills/m1nd-universal-agent-pack.md +81 -0
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
# IDE & Client Integration Matrix
|
|
2
|
+
|
|
3
|
+
This page tracks where major coding clients connect MCP servers, and how `m1nd`
|
|
4
|
+
fits into each one.
|
|
5
|
+
|
|
6
|
+
The high-level rule is simple:
|
|
7
|
+
|
|
8
|
+
- use `m1nd-mcp` for universal compatibility
|
|
9
|
+
- use the native hot daemon + thin proxy/client where the host supports a faster local lane
|
|
10
|
+
|
|
11
|
+
## Integration matrix
|
|
12
|
+
|
|
13
|
+
| Client | Integration surface | Config location / entrypoint | What to point at |
|
|
14
|
+
|---|---|---|---|
|
|
15
|
+
| Claude Code | MCP config file | `.claude/mcp.json` or `claude_mcp.json` | `m1nd-mcp` |
|
|
16
|
+
| Codex | MCP config in TOML | `~/.codex/config.toml` | `m1nd-mcp` |
|
|
17
|
+
| Cursor | MCP config file | `.cursor/mcp.json` (project) or `~/.cursor/mcp.json` | `m1nd-mcp` or a thin native proxy |
|
|
18
|
+
| Windsurf | MCP config file | Windsurf MCP settings / config JSON | `m1nd-mcp` or a thin native proxy |
|
|
19
|
+
| GitHub Copilot coding agent | Repository MCP config UI | repository settings UI for MCP servers | `m1nd-mcp` or an editor-facing native proxy command |
|
|
20
|
+
| Zed | Assistant MCP config | Zed assistant MCP configuration | `m1nd-mcp` |
|
|
21
|
+
| Continue | MCP / config layer | Continue MCP configuration | `m1nd-mcp` |
|
|
22
|
+
| Cline / Roo Code / OpenCode | MCP-compatible command config | client-specific MCP server config | `m1nd-mcp` |
|
|
23
|
+
| Antigravity | `mcp_config.json` | workspace-local `mcp_config.json` | native proxy recommended |
|
|
24
|
+
|
|
25
|
+
## Why some hosts should use a proxy
|
|
26
|
+
|
|
27
|
+
Most hosts are perfectly fine with:
|
|
28
|
+
|
|
29
|
+
```json
|
|
30
|
+
{
|
|
31
|
+
"mcpServers": {
|
|
32
|
+
"m1nd": {
|
|
33
|
+
"command": "/path/to/m1nd-mcp",
|
|
34
|
+
"env": {
|
|
35
|
+
"M1ND_GRAPH_SOURCE": "/tmp/m1nd-graph.json",
|
|
36
|
+
"M1ND_PLASTICITY_STATE": "/tmp/m1nd-plasticity.json"
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
On Windows the command should point at `m1nd-mcp.exe`, for example:
|
|
44
|
+
|
|
45
|
+
```json
|
|
46
|
+
{
|
|
47
|
+
"mcpServers": {
|
|
48
|
+
"m1nd": {
|
|
49
|
+
"command": "C:\\Users\\you\\.m1nd\\bin\\m1nd-mcp.exe",
|
|
50
|
+
"args": ["--stdio", "--no-gui"]
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Codex uses the same idea, but in TOML:
|
|
57
|
+
|
|
58
|
+
```toml
|
|
59
|
+
[mcp_servers.m1nd]
|
|
60
|
+
command = "/path/to/m1nd-mcp"
|
|
61
|
+
args = ["--stdio", "--no-gui"]
|
|
62
|
+
|
|
63
|
+
[mcp_servers.m1nd.env]
|
|
64
|
+
M1ND_GRAPH_SOURCE = "/tmp/m1nd-graph.json"
|
|
65
|
+
M1ND_PLASTICITY_STATE = "/tmp/m1nd-plasticity.json"
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
But some editor hosts still pay too much overhead if they cold-start a stdio MCP
|
|
69
|
+
server for every interaction.
|
|
70
|
+
|
|
71
|
+
For those, the better shape is:
|
|
72
|
+
|
|
73
|
+
```text
|
|
74
|
+
host -> thin proxy/client -> hot native daemon -> SessionState
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
That preserves host compatibility while avoiding cold process startup and graph reload
|
|
78
|
+
cost on every request.
|
|
79
|
+
|
|
80
|
+
## Native fast path strategy
|
|
81
|
+
|
|
82
|
+
### Universal lane
|
|
83
|
+
|
|
84
|
+
- `m1nd-mcp`
|
|
85
|
+
- stdio or HTTP/UI
|
|
86
|
+
- best for broad compatibility
|
|
87
|
+
|
|
88
|
+
### Native lane
|
|
89
|
+
|
|
90
|
+
- `m1nd-openclaw`
|
|
91
|
+
- persistent Unix socket bridge
|
|
92
|
+
- best for local runtimes that can benefit from a hot graph
|
|
93
|
+
- Unix/macOS/Linux lane today; use plain `m1nd-mcp` on Windows
|
|
94
|
+
|
|
95
|
+
### Thin adapters
|
|
96
|
+
|
|
97
|
+
Adapters should be very small:
|
|
98
|
+
|
|
99
|
+
- translate the host's command/stdio shape
|
|
100
|
+
- forward to the hot native daemon
|
|
101
|
+
- preserve host expectations
|
|
102
|
+
|
|
103
|
+
Examples:
|
|
104
|
+
|
|
105
|
+
- OpenClaw → native shell/bridge adapter
|
|
106
|
+
- Antigravity → `m1nd-antigravity-proxy.py`
|
|
107
|
+
- Cursor / Windsurf → project-local MCP config pointing at a native proxy if desired
|
|
108
|
+
|
|
109
|
+
## Practical recommendations
|
|
110
|
+
|
|
111
|
+
### Use plain MCP when:
|
|
112
|
+
|
|
113
|
+
- the host already keeps the MCP server alive
|
|
114
|
+
- the repo is small
|
|
115
|
+
- setup simplicity matters more than absolute latency
|
|
116
|
+
|
|
117
|
+
### Use the native daemon lane when:
|
|
118
|
+
|
|
119
|
+
- the host repeatedly cold-starts the server
|
|
120
|
+
- the graph is large
|
|
121
|
+
- you want graph-first navigation to feel immediate
|
|
122
|
+
|
|
123
|
+
## Current m1nd-native components
|
|
124
|
+
|
|
125
|
+
- `m1nd-openclaw` — hot daemon
|
|
126
|
+
- `m1nd-openclaw-client` — CLI client
|
|
127
|
+
- `scripts/macos/m1nd-openclaw-bridge.sh` — daemon wrapper
|
|
128
|
+
- `scripts/macos/m1nd-openclaw-call.sh` — call wrapper
|
|
129
|
+
- `scripts/macos/m1nd-antigravity-proxy.py` — editor-facing stdio proxy
|
|
130
|
+
|
|
131
|
+
## Notes
|
|
132
|
+
|
|
133
|
+
- Antigravity integration is grounded from the local product schema that recognizes
|
|
134
|
+
`mcp_config.json`.
|
|
135
|
+
- Codex, Cursor, Windsurf, Claude Code, GitHub Copilot coding agent, Zed, and Continue
|
|
136
|
+
all fit the same conceptual model: host config chooses the command, env, and working directory.
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
# MCP Host Refresh
|
|
2
|
+
|
|
3
|
+
Use this when the repo-local `m1nd-mcp` binary works, but a client such as
|
|
4
|
+
Codex, Claude, Cursor, Windsurf, Cline, or another MCP host still exposes an
|
|
5
|
+
older or partial tool surface.
|
|
6
|
+
|
|
7
|
+
The most common symptom is simple:
|
|
8
|
+
|
|
9
|
+
- local smoke sees `trust_selftest`, `ingest`, `seek`, `help`, `recovery_playbook`, and `doctor`
|
|
10
|
+
- the host session only sees some of those tools
|
|
11
|
+
- retrieval looks blocked or stale even after ingest
|
|
12
|
+
|
|
13
|
+
## 1. Prove The Local Binary First
|
|
14
|
+
|
|
15
|
+
From the repo root:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
cargo build -p m1nd-mcp
|
|
19
|
+
python3 scripts/m1nd_agent_demo.py --repo . --transport stdio --json
|
|
20
|
+
python3 scripts/m1nd_agent_demo.py --repo . --transport http --json
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Both outputs should report:
|
|
24
|
+
|
|
25
|
+
- `schema=m1nd-agent-first-demo-v0`
|
|
26
|
+
- `trust.verdict=full_trust`
|
|
27
|
+
- `checks.trust_selftest_full_trust=true`
|
|
28
|
+
- `checks.seek_scanned_ingested_graph=true`
|
|
29
|
+
- `checks.negative_trust_selftest_validated=true`
|
|
30
|
+
|
|
31
|
+
If these fail, fix the local binary or repo ingest path first. Do not blame the
|
|
32
|
+
host binding yet.
|
|
33
|
+
|
|
34
|
+
## 2. Compare The Host Tool Surface
|
|
35
|
+
|
|
36
|
+
In the client, inspect its `tools/list` result. A safe host surface should expose
|
|
37
|
+
at least:
|
|
38
|
+
|
|
39
|
+
```text
|
|
40
|
+
health
|
|
41
|
+
trust_selftest
|
|
42
|
+
session_handshake
|
|
43
|
+
recovery_playbook
|
|
44
|
+
doctor
|
|
45
|
+
ingest
|
|
46
|
+
seek
|
|
47
|
+
help
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
If `trust_selftest` is visible, call it first:
|
|
51
|
+
|
|
52
|
+
```json
|
|
53
|
+
{"agent_id":"dev"}
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
If it returns `full_trust`, continue with m1nd-first work. If it returns any
|
|
57
|
+
other verdict, follow the embedded `recovery_playbook` or call
|
|
58
|
+
`recovery_playbook` with the same evidence.
|
|
59
|
+
|
|
60
|
+
## 3. When Only `health` Is Visible
|
|
61
|
+
|
|
62
|
+
Some hosts can still expose `health` while hiding newly added tools. In that
|
|
63
|
+
case, call `health` and inspect:
|
|
64
|
+
|
|
65
|
+
- `tool_surface_contract.required_host_visible_tools`
|
|
66
|
+
- `host_binding_alignment`
|
|
67
|
+
- `binding_fingerprint`
|
|
68
|
+
|
|
69
|
+
If the contract requires tools the host does not list, treat the session as
|
|
70
|
+
`degraded_host_tool_surface`. Use the local demo output as runtime truth and
|
|
71
|
+
verify final answers against files until the client refreshes its MCP binding.
|
|
72
|
+
|
|
73
|
+
## 4. Refresh The Client Binding
|
|
74
|
+
|
|
75
|
+
Recommended order:
|
|
76
|
+
|
|
77
|
+
1. Rebuild `m1nd-mcp`.
|
|
78
|
+
2. Confirm the MCP config points at the rebuilt binary.
|
|
79
|
+
3. Restart the MCP server process if the host manages it separately.
|
|
80
|
+
4. Reload or restart the client window/session.
|
|
81
|
+
5. Re-run `tools/list`.
|
|
82
|
+
6. Call `trust_selftest`.
|
|
83
|
+
|
|
84
|
+
For hosts that cache tool schemas per conversation or workspace, start a new
|
|
85
|
+
conversation/session after rebuilding the binary. The old conversation may keep
|
|
86
|
+
the previous tool registry even though the local binary is correct.
|
|
87
|
+
|
|
88
|
+
## 5. Recovery Payload For Agents
|
|
89
|
+
|
|
90
|
+
When a host surface is suspicious, pass the host evidence into `trust_selftest`
|
|
91
|
+
or `recovery_playbook`:
|
|
92
|
+
|
|
93
|
+
```json
|
|
94
|
+
{
|
|
95
|
+
"agent_id": "dev",
|
|
96
|
+
"observed_tool": "tools/list",
|
|
97
|
+
"observed_proof_state": "blocked",
|
|
98
|
+
"observed_tool_count": 3,
|
|
99
|
+
"available_tools": ["health", "seek", "doctor"],
|
|
100
|
+
"missing_tools": ["trust_selftest", "ingest", "recovery_playbook"]
|
|
101
|
+
}
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
For blocked retrieval after a populated ingest:
|
|
105
|
+
|
|
106
|
+
```json
|
|
107
|
+
{
|
|
108
|
+
"agent_id": "dev",
|
|
109
|
+
"observed_tool": "seek",
|
|
110
|
+
"observed_proof_state": "blocked",
|
|
111
|
+
"observed_candidates": 0
|
|
112
|
+
}
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
The important rule is to compare binding fingerprints before falling back to
|
|
116
|
+
manual search. If the local stdio/HTTP demo and the host session disagree, the
|
|
117
|
+
problem is likely host binding freshness, not the graph model itself.
|
|
118
|
+
|
|
119
|
+
## Limits
|
|
120
|
+
|
|
121
|
+
This guide does not force any client to reload its MCP registry. It gives the
|
|
122
|
+
agent a deterministic way to classify the session, preserve evidence, and avoid
|
|
123
|
+
treating a stale host surface as a failed graph.
|
package/docs/README.md
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# Docs guide
|
|
2
|
+
|
|
3
|
+
This repo keeps the public docs surface intentionally small.
|
|
4
|
+
|
|
5
|
+
## Start here
|
|
6
|
+
- `../README.md` — primary project overview and quick start
|
|
7
|
+
- `AGENT-FIRST-DEMO.md` — shortest real agent-first demo transcript path
|
|
8
|
+
- `AGENT-PACKS.md` — installable m1nd-first skills and portable agent packs
|
|
9
|
+
- `MCP-HOST-REFRESH.md` — diagnose stale or partial MCP host tool surfaces
|
|
10
|
+
- `deployment.md` — persistent runtime and production setup
|
|
11
|
+
- `IDE-INTEGRATIONS.md` — client and integration notes
|
|
12
|
+
- `use-cases.md` — audience-oriented product workflows
|
|
13
|
+
- `benchmarks/README.md` — benchmark scenarios, runs, and methodology
|
|
14
|
+
- `wiki/` — canonical mdBook source for the public docs site
|
|
15
|
+
|
|
16
|
+
## Maintainer-facing docs
|
|
17
|
+
- `internal/` — design notes, visual reviews, hardening reports, and workflow docs
|
|
18
|
+
- `AGENT-TASKNOTES.md` — running capture of real agent friction worth turning into improvements
|
|
19
|
+
|
|
20
|
+
## Public docs policy
|
|
21
|
+
If a doc is primarily:
|
|
22
|
+
- user-facing
|
|
23
|
+
- setup-facing
|
|
24
|
+
- benchmark-facing
|
|
25
|
+
- or canonical product reference
|
|
26
|
+
|
|
27
|
+
it should stay in the public docs surface.
|
|
28
|
+
|
|
29
|
+
If it is primarily:
|
|
30
|
+
- exploratory
|
|
31
|
+
- historical
|
|
32
|
+
- design-internal
|
|
33
|
+
- release-process-specific
|
|
34
|
+
- or maintainer-only
|
|
35
|
+
|
|
36
|
+
it should live under `docs/internal/`.
|
package/npm/bin/m1nd.js
ADDED
package/npm/lib/cli.js
ADDED
|
@@ -0,0 +1,346 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const fs = require("fs");
|
|
4
|
+
const os = require("os");
|
|
5
|
+
const path = require("path");
|
|
6
|
+
const { spawnSync } = require("child_process");
|
|
7
|
+
|
|
8
|
+
const PACKAGE_ROOT = path.resolve(__dirname, "..", "..");
|
|
9
|
+
const SKILLS_ROOT = path.join(PACKAGE_ROOT, "skills");
|
|
10
|
+
const UNIVERSAL_PACK = path.join(SKILLS_ROOT, "m1nd-universal-agent-pack.md");
|
|
11
|
+
|
|
12
|
+
const HOSTS = new Set(["codex", "claude", "gemini", "antigravity", "generic", "all"]);
|
|
13
|
+
|
|
14
|
+
function usage() {
|
|
15
|
+
return `m1nd installer
|
|
16
|
+
|
|
17
|
+
Usage:
|
|
18
|
+
m1nd init [--host codex|claude|gemini|antigravity|generic|all] [--project <dir>]
|
|
19
|
+
m1nd install-skills <host> [--project <dir>]
|
|
20
|
+
m1nd mcp-config <host> [--binary <path>]
|
|
21
|
+
m1nd doctor [--json]
|
|
22
|
+
m1nd demo [--repo <dir>] [--transport stdio|http] [--json]
|
|
23
|
+
m1nd smoke [--repo <dir>] [--transport stdio|http] [--json]
|
|
24
|
+
m1nd pack-check [--json]
|
|
25
|
+
|
|
26
|
+
This npm package installs the universal agent doctrine and host adapters. The
|
|
27
|
+
native runtime is still m1nd-mcp; doctor tells you whether it is visible.`;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function parseArgs(args) {
|
|
31
|
+
const parsed = { _: [] };
|
|
32
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
33
|
+
const arg = args[index];
|
|
34
|
+
if (!arg.startsWith("--")) {
|
|
35
|
+
parsed._.push(arg);
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
const key = arg.slice(2);
|
|
39
|
+
if (["help", "json", "yes"].includes(key)) {
|
|
40
|
+
parsed[key] = true;
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
const value = args[index + 1];
|
|
44
|
+
if (!value || value.startsWith("--")) {
|
|
45
|
+
throw new Error(`missing value for --${key}`);
|
|
46
|
+
}
|
|
47
|
+
parsed[key] = value;
|
|
48
|
+
index += 1;
|
|
49
|
+
}
|
|
50
|
+
return parsed;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function ensureDir(dir) {
|
|
54
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function copyFile(source, target) {
|
|
58
|
+
ensureDir(path.dirname(target));
|
|
59
|
+
fs.copyFileSync(source, target);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function copyDir(source, target) {
|
|
63
|
+
ensureDir(target);
|
|
64
|
+
for (const entry of fs.readdirSync(source, { withFileTypes: true })) {
|
|
65
|
+
const sourcePath = path.join(source, entry.name);
|
|
66
|
+
const targetPath = path.join(target, entry.name);
|
|
67
|
+
if (entry.isDirectory()) {
|
|
68
|
+
copyDir(sourcePath, targetPath);
|
|
69
|
+
} else if (entry.isFile()) {
|
|
70
|
+
copyFile(sourcePath, targetPath);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function which(binary) {
|
|
76
|
+
const paths = (process.env.PATH || "").split(path.delimiter);
|
|
77
|
+
const extensions = process.platform === "win32" ? ["", ".exe", ".cmd", ".bat"] : [""];
|
|
78
|
+
for (const dir of paths) {
|
|
79
|
+
for (const extension of extensions) {
|
|
80
|
+
const candidate = path.join(dir, `${binary}${extension}`);
|
|
81
|
+
if (fs.existsSync(candidate)) {
|
|
82
|
+
return candidate;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function runtimeBinaryName(platform = process.platform) {
|
|
90
|
+
return platform === "win32" ? "m1nd-mcp.exe" : "m1nd-mcp";
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function defaultRuntimePath(platform = process.platform, homeDir = os.homedir()) {
|
|
94
|
+
const pathModule = platform === "win32" ? path.win32 : path;
|
|
95
|
+
return pathModule.join(homeDir, ".m1nd", "bin", runtimeBinaryName(platform));
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function findRuntimeBinary() {
|
|
99
|
+
return process.env.M1ND_MCP_BINARY || which("m1nd-mcp") || (fs.existsSync(defaultRuntimePath()) ? defaultRuntimePath() : null);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function readPackageVersion() {
|
|
103
|
+
const packageJson = JSON.parse(fs.readFileSync(path.join(PACKAGE_ROOT, "package.json"), "utf8"));
|
|
104
|
+
return packageJson.version;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function assertPackShape() {
|
|
108
|
+
const required = [
|
|
109
|
+
path.join(SKILLS_ROOT, "m1nd-first", "SKILL.md"),
|
|
110
|
+
path.join(SKILLS_ROOT, "m1nd-operator", "SKILL.md"),
|
|
111
|
+
path.join(SKILLS_ROOT, "m1nd-operator", "references", "routing-playbooks.md"),
|
|
112
|
+
path.join(SKILLS_ROOT, "m1nd-operator", "references", "tool-families.md"),
|
|
113
|
+
path.join(SKILLS_ROOT, "m1nd-operator", "references", "runtime-and-refresh.md"),
|
|
114
|
+
path.join(SKILLS_ROOT, "m1nd-operator", "references", "l1ght-and-docs.md"),
|
|
115
|
+
path.join(SKILLS_ROOT, "m1nd-operator", "scripts", "probe_m1nd.py"),
|
|
116
|
+
UNIVERSAL_PACK,
|
|
117
|
+
];
|
|
118
|
+
const missing = required.filter((file) => !fs.existsSync(file));
|
|
119
|
+
return { ok: missing.length === 0, missing, required };
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function installCodex() {
|
|
123
|
+
const targetRoot = path.join(os.homedir(), ".codex", "skills");
|
|
124
|
+
copyDir(path.join(SKILLS_ROOT, "m1nd-first"), path.join(targetRoot, "m1nd-first"));
|
|
125
|
+
copyDir(path.join(SKILLS_ROOT, "m1nd-operator"), path.join(targetRoot, "m1nd-operator"));
|
|
126
|
+
return {
|
|
127
|
+
host: "codex",
|
|
128
|
+
installed: [
|
|
129
|
+
path.join(targetRoot, "m1nd-first"),
|
|
130
|
+
path.join(targetRoot, "m1nd-operator"),
|
|
131
|
+
],
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function hostRuleFilename(host) {
|
|
136
|
+
switch (host) {
|
|
137
|
+
case "claude":
|
|
138
|
+
return "CLAUDE.md";
|
|
139
|
+
case "gemini":
|
|
140
|
+
return "GEMINI.md";
|
|
141
|
+
case "antigravity":
|
|
142
|
+
return "AGENTS.md";
|
|
143
|
+
default:
|
|
144
|
+
return "m1nd-agent-rules.md";
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function installPortable(host, projectDir) {
|
|
149
|
+
const targetRoot = path.join(projectDir, ".m1nd", "agent-pack");
|
|
150
|
+
copyDir(SKILLS_ROOT, path.join(targetRoot, "skills"));
|
|
151
|
+
const ruleFile = path.join(targetRoot, hostRuleFilename(host));
|
|
152
|
+
copyFile(UNIVERSAL_PACK, ruleFile);
|
|
153
|
+
return {
|
|
154
|
+
host,
|
|
155
|
+
installed: [targetRoot, ruleFile],
|
|
156
|
+
note: "Point your agent host at the rule file or paste it into the host custom-instructions surface.",
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function installSkills(host, projectDir) {
|
|
161
|
+
if (!HOSTS.has(host)) {
|
|
162
|
+
throw new Error(`unsupported host '${host}'. Supported hosts: ${Array.from(HOSTS).join(", ")}`);
|
|
163
|
+
}
|
|
164
|
+
if (host === "all") {
|
|
165
|
+
return [
|
|
166
|
+
installCodex(),
|
|
167
|
+
installPortable("claude", projectDir),
|
|
168
|
+
installPortable("gemini", projectDir),
|
|
169
|
+
installPortable("antigravity", projectDir),
|
|
170
|
+
installPortable("generic", projectDir),
|
|
171
|
+
];
|
|
172
|
+
}
|
|
173
|
+
if (host === "codex") {
|
|
174
|
+
return [installCodex()];
|
|
175
|
+
}
|
|
176
|
+
return [installPortable(host, projectDir)];
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function mcpConfig(host, binary) {
|
|
180
|
+
const command = binary || findRuntimeBinary() || defaultRuntimePath();
|
|
181
|
+
if (host === "codex") {
|
|
182
|
+
const escapedCommand = command.replace(/\\/g, "\\\\");
|
|
183
|
+
return `[mcp_servers.m1nd]
|
|
184
|
+
command = "${escapedCommand}"
|
|
185
|
+
args = ["--stdio", "--no-gui"]
|
|
186
|
+
`;
|
|
187
|
+
}
|
|
188
|
+
return JSON.stringify(
|
|
189
|
+
{
|
|
190
|
+
mcpServers: {
|
|
191
|
+
m1nd: {
|
|
192
|
+
command,
|
|
193
|
+
args: ["--stdio", "--no-gui"],
|
|
194
|
+
},
|
|
195
|
+
},
|
|
196
|
+
},
|
|
197
|
+
null,
|
|
198
|
+
2
|
|
199
|
+
);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
function doctor() {
|
|
203
|
+
const pack = assertPackShape();
|
|
204
|
+
const binary = findRuntimeBinary();
|
|
205
|
+
const codexSkillRoot = path.join(os.homedir(), ".codex", "skills");
|
|
206
|
+
const codexSkillsInstalled =
|
|
207
|
+
fs.existsSync(path.join(codexSkillRoot, "m1nd-first", "SKILL.md")) &&
|
|
208
|
+
fs.existsSync(path.join(codexSkillRoot, "m1nd-operator", "SKILL.md"));
|
|
209
|
+
|
|
210
|
+
const result = {
|
|
211
|
+
schema: "m1nd-npm-doctor-v0",
|
|
212
|
+
package_version: readPackageVersion(),
|
|
213
|
+
package_root: PACKAGE_ROOT,
|
|
214
|
+
pack_ok: pack.ok,
|
|
215
|
+
missing_pack_files: pack.missing,
|
|
216
|
+
runtime: {
|
|
217
|
+
platform: process.platform,
|
|
218
|
+
arch: process.arch,
|
|
219
|
+
binary: binary || null,
|
|
220
|
+
default_install_path: defaultRuntimePath(),
|
|
221
|
+
visible_on_path_or_env: Boolean(binary),
|
|
222
|
+
hint: binary
|
|
223
|
+
? "m1nd-mcp is visible. Use m1nd smoke from a repo checkout for a live MCP smoke."
|
|
224
|
+
: "m1nd-mcp is not visible. Build from source or install the native runtime before wiring MCP hosts.",
|
|
225
|
+
},
|
|
226
|
+
codex: {
|
|
227
|
+
skill_root: codexSkillRoot,
|
|
228
|
+
skills_installed: codexSkillsInstalled,
|
|
229
|
+
},
|
|
230
|
+
next_actions: [],
|
|
231
|
+
};
|
|
232
|
+
|
|
233
|
+
if (!pack.ok) {
|
|
234
|
+
result.next_actions.push("Reinstall or rebuild the npm package; required agent-pack files are missing.");
|
|
235
|
+
}
|
|
236
|
+
if (!binary) {
|
|
237
|
+
result.next_actions.push(`From a source checkout: cargo build --release -p m1nd-mcp, then copy ${runtimeBinaryName()} to ${defaultRuntimePath()}`);
|
|
238
|
+
}
|
|
239
|
+
if (!codexSkillsInstalled) {
|
|
240
|
+
result.next_actions.push("For Codex: m1nd install-skills codex");
|
|
241
|
+
}
|
|
242
|
+
if (result.next_actions.length === 0) {
|
|
243
|
+
result.next_actions.push("Run trust_selftest in your MCP host, then ingest your repo.");
|
|
244
|
+
}
|
|
245
|
+
return result;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
function runDemo(args) {
|
|
249
|
+
const repo = path.resolve(args.repo || process.cwd());
|
|
250
|
+
const script = path.join(repo, "scripts", "m1nd_agent_demo.py");
|
|
251
|
+
if (!fs.existsSync(script)) {
|
|
252
|
+
throw new Error(`demo script not found at ${script}; run this from a m1nd source checkout`);
|
|
253
|
+
}
|
|
254
|
+
const python = process.env.PYTHON || "python3";
|
|
255
|
+
const commandArgs = [script, "--repo", repo, "--transport", args.transport || "stdio"];
|
|
256
|
+
if (args.json) commandArgs.push("--json");
|
|
257
|
+
const result = spawnSync(python, commandArgs, { stdio: "inherit" });
|
|
258
|
+
if (result.error) {
|
|
259
|
+
throw result.error;
|
|
260
|
+
}
|
|
261
|
+
process.exitCode = result.status || 0;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
function print(value, asJson) {
|
|
265
|
+
if (asJson) {
|
|
266
|
+
console.log(JSON.stringify(value, null, 2));
|
|
267
|
+
return;
|
|
268
|
+
}
|
|
269
|
+
if (Array.isArray(value)) {
|
|
270
|
+
for (const item of value) {
|
|
271
|
+
console.log(`${item.host}:`);
|
|
272
|
+
for (const installed of item.installed) {
|
|
273
|
+
console.log(` installed ${installed}`);
|
|
274
|
+
}
|
|
275
|
+
if (item.note) console.log(` ${item.note}`);
|
|
276
|
+
}
|
|
277
|
+
return;
|
|
278
|
+
}
|
|
279
|
+
if (value.schema === "m1nd-npm-doctor-v0") {
|
|
280
|
+
console.log(`m1nd npm package ${value.package_version}`);
|
|
281
|
+
console.log(`pack: ${value.pack_ok ? "ok" : "missing files"}`);
|
|
282
|
+
console.log(`runtime: ${value.runtime.binary || "not found"}`);
|
|
283
|
+
console.log(`codex skills: ${value.codex.skills_installed ? "installed" : "not installed"}`);
|
|
284
|
+
console.log("next:");
|
|
285
|
+
for (const action of value.next_actions) console.log(` - ${action}`);
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
288
|
+
console.log(String(value));
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
async function main(rawArgs) {
|
|
292
|
+
const args = parseArgs(rawArgs);
|
|
293
|
+
const command = args._[0] || "help";
|
|
294
|
+
|
|
295
|
+
if (args.help || ["help", "-h", "--help"].includes(command)) {
|
|
296
|
+
console.log(usage());
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
if (["init", "install-skills"].includes(command)) {
|
|
301
|
+
const host = args._[1] || args.host || "generic";
|
|
302
|
+
const projectDir = path.resolve(args.project || process.cwd());
|
|
303
|
+
print(installSkills(host, projectDir), args.json);
|
|
304
|
+
return;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
if (command === "mcp-config") {
|
|
308
|
+
const host = args._[1] || args.host || "generic";
|
|
309
|
+
console.log(mcpConfig(host, args.binary));
|
|
310
|
+
return;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
if (command === "doctor") {
|
|
314
|
+
print(doctor(), args.json);
|
|
315
|
+
return;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
if (command === "pack-check") {
|
|
319
|
+
const result = assertPackShape();
|
|
320
|
+
if (args.json) {
|
|
321
|
+
console.log(JSON.stringify({ schema: "m1nd-agent-pack-check-v0", ...result }, null, 2));
|
|
322
|
+
} else {
|
|
323
|
+
console.log(result.ok ? "m1nd agent pack ok" : `m1nd agent pack missing: ${result.missing.join(", ")}`);
|
|
324
|
+
}
|
|
325
|
+
if (!result.ok) process.exitCode = 1;
|
|
326
|
+
return;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
if (["demo", "smoke"].includes(command)) {
|
|
330
|
+
runDemo(args);
|
|
331
|
+
return;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
throw new Error(`unknown command '${command}'\n\n${usage()}`);
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
module.exports = {
|
|
338
|
+
main,
|
|
339
|
+
assertPackShape,
|
|
340
|
+
defaultRuntimePath,
|
|
341
|
+
doctor,
|
|
342
|
+
findRuntimeBinary,
|
|
343
|
+
installSkills,
|
|
344
|
+
mcpConfig,
|
|
345
|
+
runtimeBinaryName,
|
|
346
|
+
};
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const assert = require("assert");
|
|
4
|
+
const path = require("path");
|
|
5
|
+
const { spawnSync } = require("child_process");
|
|
6
|
+
|
|
7
|
+
const {
|
|
8
|
+
defaultRuntimePath,
|
|
9
|
+
mcpConfig,
|
|
10
|
+
runtimeBinaryName,
|
|
11
|
+
} = require("../lib/cli");
|
|
12
|
+
|
|
13
|
+
const cli = path.resolve(__dirname, "../bin/m1nd.js");
|
|
14
|
+
|
|
15
|
+
assert.strictEqual(runtimeBinaryName("win32"), "m1nd-mcp.exe");
|
|
16
|
+
assert.strictEqual(runtimeBinaryName("darwin"), "m1nd-mcp");
|
|
17
|
+
assert.strictEqual(runtimeBinaryName("linux"), "m1nd-mcp");
|
|
18
|
+
|
|
19
|
+
assert.strictEqual(
|
|
20
|
+
defaultRuntimePath("win32", "C:\\Users\\you"),
|
|
21
|
+
"C:\\Users\\you\\.m1nd\\bin\\m1nd-mcp.exe"
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
const codexWindowsConfig = mcpConfig(
|
|
25
|
+
"codex",
|
|
26
|
+
"C:\\Users\\you\\.m1nd\\bin\\m1nd-mcp.exe"
|
|
27
|
+
);
|
|
28
|
+
assert(codexWindowsConfig.includes('command = "C:\\\\Users\\\\you\\\\.m1nd\\\\bin\\\\m1nd-mcp.exe"'));
|
|
29
|
+
assert(codexWindowsConfig.includes('args = ["--stdio", "--no-gui"]'));
|
|
30
|
+
|
|
31
|
+
const genericWindowsConfig = JSON.parse(
|
|
32
|
+
mcpConfig("generic", "C:\\Users\\you\\.m1nd\\bin\\m1nd-mcp.exe")
|
|
33
|
+
);
|
|
34
|
+
assert.strictEqual(
|
|
35
|
+
genericWindowsConfig.mcpServers.m1nd.command,
|
|
36
|
+
"C:\\Users\\you\\.m1nd\\bin\\m1nd-mcp.exe"
|
|
37
|
+
);
|
|
38
|
+
assert.deepStrictEqual(genericWindowsConfig.mcpServers.m1nd.args, ["--stdio", "--no-gui"]);
|
|
39
|
+
|
|
40
|
+
const help = spawnSync(process.execPath, [cli, "--help"], { encoding: "utf8" });
|
|
41
|
+
assert.strictEqual(help.status, 0, help.stderr);
|
|
42
|
+
assert(help.stdout.includes("m1nd installer"));
|
|
43
|
+
assert(help.stdout.includes("m1nd smoke"));
|
|
44
|
+
|
|
45
|
+
const packCheck = spawnSync(process.execPath, [cli, "pack-check", "--json"], { encoding: "utf8" });
|
|
46
|
+
assert.strictEqual(packCheck.status, 0, packCheck.stderr);
|
|
47
|
+
assert.strictEqual(JSON.parse(packCheck.stdout).schema, "m1nd-agent-pack-check-v0");
|
|
48
|
+
|
|
49
|
+
console.log("npm cli tests ok");
|