@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.
@@ -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/`.
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+
4
+ const { main } = require("../lib/cli");
5
+
6
+ main(process.argv.slice(2)).catch((error) => {
7
+ console.error(`m1nd: ${error.message}`);
8
+ process.exitCode = 1;
9
+ });
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");