@neurodock/cli 0.1.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.
Files changed (68) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/LICENSE +11 -0
  3. package/README.md +101 -0
  4. package/dist/clients/claude-code.d.ts +3 -0
  5. package/dist/clients/claude-code.d.ts.map +1 -0
  6. package/dist/clients/claude-code.js +22 -0
  7. package/dist/clients/claude-code.js.map +1 -0
  8. package/dist/clients/claude-desktop.d.ts +3 -0
  9. package/dist/clients/claude-desktop.d.ts.map +1 -0
  10. package/dist/clients/claude-desktop.js +18 -0
  11. package/dist/clients/claude-desktop.js.map +1 -0
  12. package/dist/clients/cursor.d.ts +3 -0
  13. package/dist/clients/cursor.d.ts.map +1 -0
  14. package/dist/clients/cursor.js +22 -0
  15. package/dist/clients/cursor.js.map +1 -0
  16. package/dist/clients/index.d.ts +17 -0
  17. package/dist/clients/index.d.ts.map +1 -0
  18. package/dist/clients/index.js +16 -0
  19. package/dist/clients/index.js.map +1 -0
  20. package/dist/commands/doctor.d.ts +7 -0
  21. package/dist/commands/doctor.d.ts.map +1 -0
  22. package/dist/commands/doctor.js +107 -0
  23. package/dist/commands/doctor.js.map +1 -0
  24. package/dist/commands/init.d.ts +13 -0
  25. package/dist/commands/init.d.ts.map +1 -0
  26. package/dist/commands/init.js +189 -0
  27. package/dist/commands/init.js.map +1 -0
  28. package/dist/commands/profile.d.ts +14 -0
  29. package/dist/commands/profile.d.ts.map +1 -0
  30. package/dist/commands/profile.js +29 -0
  31. package/dist/commands/profile.js.map +1 -0
  32. package/dist/index.d.ts +5 -0
  33. package/dist/index.d.ts.map +1 -0
  34. package/dist/index.js +117 -0
  35. package/dist/index.js.map +1 -0
  36. package/dist/lib/env.d.ts +11 -0
  37. package/dist/lib/env.d.ts.map +1 -0
  38. package/dist/lib/env.js +37 -0
  39. package/dist/lib/env.js.map +1 -0
  40. package/dist/lib/json-patch.d.ts +22 -0
  41. package/dist/lib/json-patch.d.ts.map +1 -0
  42. package/dist/lib/json-patch.js +76 -0
  43. package/dist/lib/json-patch.js.map +1 -0
  44. package/dist/lib/mcp-entries.d.ts +9 -0
  45. package/dist/lib/mcp-entries.d.ts.map +1 -0
  46. package/dist/lib/mcp-entries.js +56 -0
  47. package/dist/lib/mcp-entries.js.map +1 -0
  48. package/dist/lib/paths.d.ts +23 -0
  49. package/dist/lib/paths.d.ts.map +1 -0
  50. package/dist/lib/paths.js +91 -0
  51. package/dist/lib/paths.js.map +1 -0
  52. package/dist/profile/defaults.d.ts +29 -0
  53. package/dist/profile/defaults.d.ts.map +1 -0
  54. package/dist/profile/defaults.js +50 -0
  55. package/dist/profile/defaults.js.map +1 -0
  56. package/dist/profile/loader.d.ts +23 -0
  57. package/dist/profile/loader.d.ts.map +1 -0
  58. package/dist/profile/loader.js +52 -0
  59. package/dist/profile/loader.js.map +1 -0
  60. package/dist/profile/validator.d.ts +12 -0
  61. package/dist/profile/validator.d.ts.map +1 -0
  62. package/dist/profile/validator.js +62 -0
  63. package/dist/profile/validator.js.map +1 -0
  64. package/dist/types.d.ts +51 -0
  65. package/dist/types.d.ts.map +1 -0
  66. package/dist/types.js +3 -0
  67. package/dist/types.js.map +1 -0
  68. package/package.json +51 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,13 @@
1
+ # @neurodock/cli changelog
2
+
3
+ ## 0.1.0 (unreleased)
4
+
5
+ First implementation. Phase 1 deliverable per .
6
+
7
+ - `neurodock init` — detect Claude Desktop, Claude Code, and Cursor on macOS, Linux, and Windows; install profile from `profile.example.yaml` (default) or `profile.minimal.yaml`; wire three Python MCP servers into the detected client configs.
8
+ - `neurodock doctor` — checklist diagnostic: Node >= 22, `uv` available, Python available, profile presence + schema validity, client config JSON syntax, NeuroDock server wiring.
9
+ - `neurodock profile validate` — Ajv-driven schema validation with field-path violation output.
10
+ - `neurodock profile show` — print the resolved profile with loader defaults applied (per ADR 0004 §15).
11
+ - `--dry-run` and `--yes` flags on `init`.
12
+ - Preserves existing `mcpServers` keys, comments in profile YAML, and unknown top-level keys (forward-compat per ADR 0004).
13
+ - 24+ unit tests across paths, validator, defaults, client adapters, init, and doctor.
package/LICENSE ADDED
@@ -0,0 +1,11 @@
1
+ NeuroDock is licensed under the GNU Affero General Public License v3.0 or later.
2
+
3
+ SPDX-License-Identifier: AGPL-3.0-or-later
4
+
5
+ The canonical license text is available at:
6
+ https://www.gnu.org/licenses/agpl-3.0.txt
7
+
8
+ A local copy can be added by running:
9
+ curl -o LICENSE-AGPL-3.0 https://www.gnu.org/licenses/agpl-3.0.txt
10
+
11
+ License intent confirmed by the founding council at first meeting (see GOVERNANCE.md).
package/README.md ADDED
@@ -0,0 +1,101 @@
1
+ # @neurodock/cli
2
+
3
+ The `neurodock` installer and diagnostic CLI.
4
+
5
+ Status: Phase 1 (v0.1 developer preview).
6
+
7
+ ## Quickstart
8
+
9
+ ```bash
10
+ # From a published package once it lands on npm:
11
+ npx neurodock init
12
+
13
+ # From a fresh clone of the monorepo:
14
+ pnpm install
15
+ pnpm --filter @neurodock/cli build
16
+ node packages/cli/dist/index.js init
17
+ ```
18
+
19
+ `neurodock init` wires three Python MCP servers (chronometric, cognitive-graph,
20
+ task-fractionator) into every MCP-aware client it can detect.
21
+
22
+ **Prerequisite:** the CLI does not install Python deps for you. From a clone
23
+ run `uv sync` (or `pip install -e packages/mcp-chronometric` etc.) so the
24
+ `neurodock-mcp-*` console entry points are on `$PATH` for `uv run`. From a
25
+ published install this matures into `uv tool install neurodock-mcp-*`.
26
+
27
+ ## Commands
28
+
29
+ | Command | What it does |
30
+ |---|---|
31
+ | `neurodock init` | Install MCP servers into Claude Desktop / Claude Code / Cursor. |
32
+ | `neurodock doctor` | Diagnose your install — profile validity, client wiring, tool availability. |
33
+ | `neurodock profile validate` | Validate `~/.neurodock/profile.yaml` against the v0.1 schema. |
34
+ | `neurodock profile show` | Print the resolved profile with loader defaults applied. |
35
+
36
+ ### `neurodock init`
37
+
38
+ ```
39
+ neurodock init [--client=claude-desktop|claude-code|cursor|all] \
40
+ [--profile=minimal|example] \
41
+ [--dry-run] [--yes]
42
+ ```
43
+
44
+ - `--client` defaults to `all`. With `all` we only act on already-existing client configs.
45
+ - `--profile` defaults to `example` (the worked "T" profile). Use `minimal` for the shortest valid file.
46
+ - `--dry-run` prints the diff and exits 0.
47
+ - `--yes` overwrites colliding `mcpServers` keys without prompting.
48
+
49
+ What `init` does, in order:
50
+
51
+ 1. Detects supported clients per platform (see "Detection locations" below).
52
+ 2. Copies `profile.example.yaml` or `profile.minimal.yaml` to `~/.neurodock/profile.yaml` if it does not already exist. Sets `identity.display_name` from `$USER` / `%USERNAME%`.
53
+ 3. Adds three `mcpServers` entries (`neurodock-chronometric`, `neurodock-cognitive-graph`, `neurodock-task-fractionator`) to each detected client's config.
54
+ 4. Preserves all unrelated keys, comments, and unknown server entries already in those configs.
55
+
56
+ Idempotent. Re-running with no changes is a no-op. Collisions on a previous
57
+ key are skipped unless `--yes` is supplied.
58
+
59
+ ### Detection locations
60
+
61
+ | Client | Platform | Path |
62
+ |---|---|---|
63
+ | Claude Desktop | macOS | `~/Library/Application Support/Claude/claude_desktop_config.json` |
64
+ | Claude Desktop | Windows | `%APPDATA%\Claude\claude_desktop_config.json` |
65
+ | Claude Desktop | Linux | `~/.config/Claude/claude_desktop_config.json` |
66
+ | Claude Code | All | `./.claude/settings.json` (project, preferred) then `~/.claude/settings.json` (user) |
67
+ | Cursor | All | `./.cursor/mcp.json` (project, preferred) then `~/.cursor/mcp.json` (user) |
68
+
69
+ ### Profile precedence
70
+
71
+ Loader precedence (highest first):
72
+
73
+ 1. `$NEURODOCK_PROFILE_PATH`
74
+ 2. `$XDG_CONFIG_HOME/neurodock/profile.yaml`
75
+ 3. `~/.neurodock/profile.yaml`
76
+
77
+ ## Build and test
78
+
79
+ ```bash
80
+ cd packages/cli
81
+ pnpm install
82
+ pnpm run typecheck
83
+ pnpm run test
84
+ pnpm run build
85
+ node dist/index.js --version
86
+ node dist/index.js --help
87
+ ```
88
+
89
+ ## Design notes
90
+
91
+ - Strict TypeScript, NodeNext modules, ES2022 target.
92
+ - Profile YAML round-trip preserves comments (the `yaml` package's
93
+ `parseDocument` API), per ADR 0004 §14.
94
+ - All loader defaults live in `src/profile/defaults.ts` and mirror
95
+ `profile.schema.json` — JSON Schema validation does not apply defaults
96
+ itself, so the loader does.
97
+ - No telemetry, no remote calls, no auto-install of Python packages.
98
+
99
+ ## License
100
+
101
+ AGPL-3.0-or-later.
@@ -0,0 +1,3 @@
1
+ import type { ClientAdapter } from "./index.js";
2
+ export declare const claudeCodeAdapter: ClientAdapter;
3
+ //# sourceMappingURL=claude-code.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"claude-code.d.ts","sourceRoot":"","sources":["../../src/clients/claude-code.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAIhD,eAAO,MAAM,iBAAiB,EAAE,aAgB/B,CAAC"}
@@ -0,0 +1,22 @@
1
+ import { claudeCodeConfigPaths } from "../lib/paths.js";
2
+ export const claudeCodeAdapter = {
3
+ id: "claude-code",
4
+ displayName: "Claude Code",
5
+ configPaths(env) {
6
+ const paths = claudeCodeConfigPaths(env);
7
+ return [
8
+ { path: paths.project, scope: "project" },
9
+ { path: paths.user, scope: "user" },
10
+ ];
11
+ },
12
+ shapeConfig(existing, mcpServers) {
13
+ const base = isObject(existing) ? { ...existing } : {};
14
+ const prev = isObject(base["mcpServers"]) ? base["mcpServers"] : {};
15
+ base["mcpServers"] = { ...prev, ...mcpServers };
16
+ return base;
17
+ },
18
+ };
19
+ function isObject(x) {
20
+ return typeof x === "object" && x !== null && !Array.isArray(x);
21
+ }
22
+ //# sourceMappingURL=claude-code.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"claude-code.js","sourceRoot":"","sources":["../../src/clients/claude-code.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AAExD,MAAM,CAAC,MAAM,iBAAiB,GAAkB;IAC9C,EAAE,EAAE,aAAa;IACjB,WAAW,EAAE,aAAa;IAC1B,WAAW,CAAC,GAAG;QACb,MAAM,KAAK,GAAG,qBAAqB,CAAC,GAAG,CAAC,CAAC;QACzC,OAAO;YACL,EAAE,IAAI,EAAE,KAAK,CAAC,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE;YACzC,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE;SACpC,CAAC;IACJ,CAAC;IACD,WAAW,CAAC,QAAQ,EAAE,UAAU;QAC9B,MAAM,IAAI,GAAG,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,EAAE,GAAG,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACvD,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,CAAE,IAAI,CAAC,YAAY,CAAoC,CAAC,CAAC,CAAC,EAAE,CAAC;QACxG,IAAI,CAAC,YAAY,CAAC,GAAG,EAAE,GAAG,IAAI,EAAE,GAAG,UAAU,EAAE,CAAC;QAChD,OAAO,IAAI,CAAC;IACd,CAAC;CACF,CAAC;AAEF,SAAS,QAAQ,CAAC,CAAU;IAC1B,OAAO,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;AAClE,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { ClientAdapter } from "./index.js";
2
+ export declare const claudeDesktopAdapter: ClientAdapter;
3
+ //# sourceMappingURL=claude-desktop.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"claude-desktop.d.ts","sourceRoot":"","sources":["../../src/clients/claude-desktop.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAIhD,eAAO,MAAM,oBAAoB,EAAE,aAYlC,CAAC"}
@@ -0,0 +1,18 @@
1
+ import { claudeDesktopConfigPath } from "../lib/paths.js";
2
+ export const claudeDesktopAdapter = {
3
+ id: "claude-desktop",
4
+ displayName: "Claude Desktop",
5
+ configPaths(env) {
6
+ return [{ path: claudeDesktopConfigPath(env), scope: "user" }];
7
+ },
8
+ shapeConfig(existing, mcpServers) {
9
+ const base = isObject(existing) ? { ...existing } : {};
10
+ const prev = isObject(base["mcpServers"]) ? base["mcpServers"] : {};
11
+ base["mcpServers"] = { ...prev, ...mcpServers };
12
+ return base;
13
+ },
14
+ };
15
+ function isObject(x) {
16
+ return typeof x === "object" && x !== null && !Array.isArray(x);
17
+ }
18
+ //# sourceMappingURL=claude-desktop.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"claude-desktop.js","sourceRoot":"","sources":["../../src/clients/claude-desktop.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,uBAAuB,EAAE,MAAM,iBAAiB,CAAC;AAE1D,MAAM,CAAC,MAAM,oBAAoB,GAAkB;IACjD,EAAE,EAAE,gBAAgB;IACpB,WAAW,EAAE,gBAAgB;IAC7B,WAAW,CAAC,GAAG;QACb,OAAO,CAAC,EAAE,IAAI,EAAE,uBAAuB,CAAC,GAAG,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC;IACjE,CAAC;IACD,WAAW,CAAC,QAAQ,EAAE,UAAU;QAC9B,MAAM,IAAI,GAAG,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,EAAE,GAAG,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACvD,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,CAAE,IAAI,CAAC,YAAY,CAAoC,CAAC,CAAC,CAAC,EAAE,CAAC;QACxG,IAAI,CAAC,YAAY,CAAC,GAAG,EAAE,GAAG,IAAI,EAAE,GAAG,UAAU,EAAE,CAAC;QAChD,OAAO,IAAI,CAAC;IACd,CAAC;CACF,CAAC;AAEF,SAAS,QAAQ,CAAC,CAAU;IAC1B,OAAO,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;AAClE,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { ClientAdapter } from "./index.js";
2
+ export declare const cursorAdapter: ClientAdapter;
3
+ //# sourceMappingURL=cursor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cursor.d.ts","sourceRoot":"","sources":["../../src/clients/cursor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAIhD,eAAO,MAAM,aAAa,EAAE,aAgB3B,CAAC"}
@@ -0,0 +1,22 @@
1
+ import { cursorConfigPaths } from "../lib/paths.js";
2
+ export const cursorAdapter = {
3
+ id: "cursor",
4
+ displayName: "Cursor",
5
+ configPaths(env) {
6
+ const paths = cursorConfigPaths(env);
7
+ return [
8
+ { path: paths.project, scope: "project" },
9
+ { path: paths.user, scope: "user" },
10
+ ];
11
+ },
12
+ shapeConfig(existing, mcpServers) {
13
+ const base = isObject(existing) ? { ...existing } : {};
14
+ const prev = isObject(base["mcpServers"]) ? base["mcpServers"] : {};
15
+ base["mcpServers"] = { ...prev, ...mcpServers };
16
+ return base;
17
+ },
18
+ };
19
+ function isObject(x) {
20
+ return typeof x === "object" && x !== null && !Array.isArray(x);
21
+ }
22
+ //# sourceMappingURL=cursor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cursor.js","sourceRoot":"","sources":["../../src/clients/cursor.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAEpD,MAAM,CAAC,MAAM,aAAa,GAAkB;IAC1C,EAAE,EAAE,QAAQ;IACZ,WAAW,EAAE,QAAQ;IACrB,WAAW,CAAC,GAAG;QACb,MAAM,KAAK,GAAG,iBAAiB,CAAC,GAAG,CAAC,CAAC;QACrC,OAAO;YACL,EAAE,IAAI,EAAE,KAAK,CAAC,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE;YACzC,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE;SACpC,CAAC;IACJ,CAAC;IACD,WAAW,CAAC,QAAQ,EAAE,UAAU;QAC9B,MAAM,IAAI,GAAG,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,EAAE,GAAG,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACvD,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,CAAE,IAAI,CAAC,YAAY,CAAoC,CAAC,CAAC,CAAC,EAAE,CAAC;QACxG,IAAI,CAAC,YAAY,CAAC,GAAG,EAAE,GAAG,IAAI,EAAE,GAAG,UAAU,EAAE,CAAC;QAChD,OAAO,IAAI,CAAC;IACd,CAAC;CACF,CAAC;AAEF,SAAS,QAAQ,CAAC,CAAU;IAC1B,OAAO,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;AAClE,CAAC"}
@@ -0,0 +1,17 @@
1
+ import type { ClientId, McpServerEntry } from "../types.js";
2
+ import type { EnvSnapshot } from "../lib/env.js";
3
+ export interface ClientAdapter {
4
+ readonly id: ClientId;
5
+ readonly displayName: string;
6
+ /** Possible config file locations, highest precedence first. */
7
+ configPaths(env: EnvSnapshot): ReadonlyArray<ConfigCandidate>;
8
+ /** Wrap an mcpServers map into the shape this client expects. */
9
+ shapeConfig(existing: unknown, mcpServers: Record<string, McpServerEntry>): unknown;
10
+ }
11
+ export interface ConfigCandidate {
12
+ readonly path: string;
13
+ readonly scope: "user" | "project";
14
+ }
15
+ export declare const allAdapters: ReadonlyArray<ClientAdapter>;
16
+ export declare function adapterFor(id: ClientId): ClientAdapter;
17
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/clients/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC5D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAKjD,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,EAAE,EAAE,QAAQ,CAAC;IACtB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,gEAAgE;IAChE,WAAW,CAAC,GAAG,EAAE,WAAW,GAAG,aAAa,CAAC,eAAe,CAAC,CAAC;IAC9D,iEAAiE;IACjE,WAAW,CAAC,QAAQ,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,GAAG,OAAO,CAAC;CACrF;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,CAAC;CACpC;AAED,eAAO,MAAM,WAAW,EAAE,aAAa,CAAC,aAAa,CAIpD,CAAC;AAEF,wBAAgB,UAAU,CAAC,EAAE,EAAE,QAAQ,GAAG,aAAa,CAMtD"}
@@ -0,0 +1,16 @@
1
+ import { claudeDesktopAdapter } from "./claude-desktop.js";
2
+ import { claudeCodeAdapter } from "./claude-code.js";
3
+ import { cursorAdapter } from "./cursor.js";
4
+ export const allAdapters = [
5
+ claudeDesktopAdapter,
6
+ claudeCodeAdapter,
7
+ cursorAdapter,
8
+ ];
9
+ export function adapterFor(id) {
10
+ const found = allAdapters.find((a) => a.id === id);
11
+ if (!found) {
12
+ throw new Error(`Unknown client id: ${id}`);
13
+ }
14
+ return found;
15
+ }
16
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/clients/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAgB5C,MAAM,CAAC,MAAM,WAAW,GAAiC;IACvD,oBAAoB;IACpB,iBAAiB;IACjB,aAAa;CACd,CAAC;AAEF,MAAM,UAAU,UAAU,CAAC,EAAY;IACrC,MAAM,KAAK,GAAG,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC;IACnD,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,IAAI,KAAK,CAAC,sBAAsB,EAAE,EAAE,CAAC,CAAC;IAC9C,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC"}
@@ -0,0 +1,7 @@
1
+ import type { CheckResult } from "../types.js";
2
+ export interface DoctorResult {
3
+ readonly checks: ReadonlyArray<CheckResult>;
4
+ readonly ok: boolean;
5
+ }
6
+ export declare function runDoctor(): Promise<DoctorResult>;
7
+ //# sourceMappingURL=doctor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../../src/commands/doctor.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAO/C,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC,WAAW,CAAC,CAAC;IAC5C,QAAQ,CAAC,EAAE,EAAE,OAAO,CAAC;CACtB;AAED,wBAAsB,SAAS,IAAI,OAAO,CAAC,YAAY,CAAC,CA8EvD"}
@@ -0,0 +1,107 @@
1
+ import { execFileSync } from "node:child_process";
2
+ import { readFileSync, existsSync } from "node:fs";
3
+ import { readEnv } from "../lib/env.js";
4
+ import { profilePath, detectClients } from "../lib/paths.js";
5
+ import { loadProfileFromFile } from "../profile/loader.js";
6
+ import { validateProfile } from "../profile/validator.js";
7
+ import { parseJsonSafely } from "../lib/json-patch.js";
8
+ export async function runDoctor() {
9
+ const env = readEnv();
10
+ const checks = [];
11
+ checks.push(checkNodeVersion());
12
+ checks.push(checkCommandAvailable("uv", ["--version"]));
13
+ checks.push(checkCommandAvailable("python3", ["--version"], "python3 or python >= 3.11"));
14
+ // Profile presence + validity.
15
+ const pPath = profilePath(env);
16
+ if (!existsSync(pPath)) {
17
+ checks.push({
18
+ name: "Profile exists",
19
+ status: "FAIL",
20
+ detail: `No profile at ${pPath}. Run 'neurodock init' to create one.`,
21
+ });
22
+ }
23
+ else {
24
+ checks.push({ name: "Profile exists", status: "PASS", detail: pPath });
25
+ try {
26
+ const loaded = loadProfileFromFile(pPath);
27
+ const result = validateProfile(loaded.raw);
28
+ if (result.valid) {
29
+ checks.push({ name: "Profile schema valid", status: "PASS" });
30
+ }
31
+ else {
32
+ checks.push({
33
+ name: "Profile schema valid",
34
+ status: "FAIL",
35
+ detail: result.violations.map((v) => `${v.path}: ${v.message}`).join("; "),
36
+ });
37
+ }
38
+ }
39
+ catch (err) {
40
+ checks.push({
41
+ name: "Profile schema valid",
42
+ status: "FAIL",
43
+ detail: err instanceof Error ? err.message : String(err),
44
+ });
45
+ }
46
+ }
47
+ // Client configs syntax check.
48
+ const detections = detectClients(env);
49
+ const existing = detections.filter((d) => d.exists);
50
+ if (existing.length === 0) {
51
+ checks.push({
52
+ name: "Client configs",
53
+ status: "SKIP",
54
+ detail: "No supported client config found. Skipping wiring check.",
55
+ });
56
+ }
57
+ else {
58
+ for (const d of existing) {
59
+ const raw = readFileSync(d.path, "utf8");
60
+ const parsed = parseJsonSafely(raw);
61
+ if (!parsed.ok) {
62
+ checks.push({
63
+ name: `Client config syntax: ${d.id} (${d.scope})`,
64
+ status: "FAIL",
65
+ detail: `${d.path}: ${parsed.error}`,
66
+ });
67
+ continue;
68
+ }
69
+ checks.push({
70
+ name: `Client config syntax: ${d.id} (${d.scope})`,
71
+ status: "PASS",
72
+ detail: d.path,
73
+ });
74
+ const cfg = (parsed.value ?? {});
75
+ const servers = cfg.mcpServers ?? {};
76
+ const wired = Object.keys(servers).filter((k) => k.startsWith("neurodock-"));
77
+ checks.push({
78
+ name: `NeuroDock servers wired: ${d.id} (${d.scope})`,
79
+ status: wired.length > 0 ? "PASS" : "FAIL",
80
+ detail: wired.length > 0 ? wired.join(", ") : "no neurodock-* entries found",
81
+ });
82
+ }
83
+ }
84
+ const ok = checks.every((c) => c.status !== "FAIL");
85
+ return { checks, ok };
86
+ }
87
+ function checkNodeVersion() {
88
+ const v = process.versions.node;
89
+ const major = Number.parseInt(v.split(".")[0] ?? "0", 10);
90
+ if (Number.isFinite(major) && major >= 22) {
91
+ return { name: "Node >= 22", status: "PASS", detail: `node ${v}` };
92
+ }
93
+ return { name: "Node >= 22", status: "FAIL", detail: `node ${v}` };
94
+ }
95
+ function checkCommandAvailable(cmd, args, label) {
96
+ const name = label ?? `${cmd} available`;
97
+ try {
98
+ const out = execFileSync(cmd, args, { stdio: ["ignore", "pipe", "pipe"], timeout: 4000 });
99
+ const text = out.toString().trim().split("\n")[0] ?? "";
100
+ return { name, status: "PASS", detail: text };
101
+ }
102
+ catch (err) {
103
+ const detail = err instanceof Error ? err.message.split("\n")[0] ?? "unknown error" : String(err);
104
+ return { name, status: "FAIL", detail };
105
+ }
106
+ }
107
+ //# sourceMappingURL=doctor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"doctor.js","sourceRoot":"","sources":["../../src/commands/doctor.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ import type { InitDiff, InitOptions } from "../types.js";
2
+ import { readEnv } from "../lib/env.js";
3
+ export interface InitRunResult {
4
+ readonly diff: InitDiff;
5
+ readonly applied: boolean;
6
+ readonly messages: ReadonlyArray<string>;
7
+ }
8
+ export interface InitDependencies {
9
+ /** Override the environment snapshot (for tests). */
10
+ readonly envOverrides?: Parameters<typeof readEnv>[0];
11
+ }
12
+ export declare function runInit(options: InitOptions, deps?: InitDependencies): Promise<InitRunResult>;
13
+ //# sourceMappingURL=init.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAGV,QAAQ,EACR,WAAW,EAEZ,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAOxC,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;IACxB,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,QAAQ,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;CAC1C;AAED,MAAM,WAAW,gBAAgB;IAC/B,qDAAqD;IACrD,QAAQ,CAAC,YAAY,CAAC,EAAE,UAAU,CAAC,OAAO,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;CACvD;AAED,wBAAsB,OAAO,CAAC,OAAO,EAAE,WAAW,EAAE,IAAI,GAAE,gBAAqB,GAAG,OAAO,CAAC,aAAa,CAAC,CAgGvG"}
@@ -0,0 +1,189 @@
1
+ import { readFileSync, writeFileSync, mkdirSync, existsSync } from "node:fs";
2
+ import { dirname, join, resolve } from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+ import { readEnv } from "../lib/env.js";
5
+ import { profilePath, detectClients } from "../lib/paths.js";
6
+ import { adapterFor } from "../clients/index.js";
7
+ import { mergeMcpServers, parseJsonSafely } from "../lib/json-patch.js";
8
+ import { writeProfileFromTemplate } from "../profile/loader.js";
9
+ import { buildMcpServers, detectRepoRoot } from "../lib/mcp-entries.js";
10
+ export async function runInit(options, deps = {}) {
11
+ const env = readEnv(deps.envOverrides ?? {});
12
+ const messages = [];
13
+ // 1. Detect clients.
14
+ const detections = detectClients(env);
15
+ const targets = pickTargets(detections, options.client);
16
+ const detectedTargets = targets.filter((t) => t.exists);
17
+ if (options.client !== "all" && targets.length === 0) {
18
+ messages.push(`No supported client locations exist for '${options.client}'.`);
19
+ }
20
+ if (options.client === "all" && detectedTargets.length === 0) {
21
+ messages.push("No supported MCP client detected.");
22
+ messages.push("Looked for:");
23
+ for (const t of targets) {
24
+ messages.push(` - ${t.id} (${t.scope}): ${t.path}`);
25
+ }
26
+ messages.push("See: https://docs.neurodock.org/install#supported-clients");
27
+ return {
28
+ diff: { profileAction: "skipped", profilePath: profilePath(env), clients: [] },
29
+ applied: false,
30
+ messages,
31
+ };
32
+ }
33
+ // 2. Build the desired mcpServers entries once.
34
+ const repoRoot = detectRepoRoot(env);
35
+ const desiredServers = buildMcpServers({
36
+ ...(repoRoot !== undefined ? { repoRoot } : {}),
37
+ });
38
+ // 3. Profile.
39
+ const pPath = profilePath(env);
40
+ const profileAction = existsSync(pPath) ? "exists" : "create";
41
+ // 4. Per-client diff.
42
+ const clientDiffs = [];
43
+ // For "all" mode, only act on already-existing locations; for explicit
44
+ // client choice, act on the highest-precedence path even if absent.
45
+ const actOn = options.client === "all" ? detectedTargets : targets.slice(0, 1);
46
+ for (const target of actOn) {
47
+ const diff = await diffClient(target, desiredServers, options);
48
+ clientDiffs.push(diff);
49
+ }
50
+ const diff = {
51
+ profileAction,
52
+ profilePath: pPath,
53
+ clients: clientDiffs,
54
+ };
55
+ if (options.dryRun) {
56
+ return {
57
+ diff,
58
+ applied: false,
59
+ messages: ["Dry run. No changes written.", ...messages, ...formatDiff(diff)],
60
+ };
61
+ }
62
+ // 5. Apply.
63
+ // 5a. Profile.
64
+ if (profileAction === "create") {
65
+ const templatePath = resolveTemplatePath(options.profile);
66
+ writeProfileFromTemplate({
67
+ templatePath,
68
+ targetPath: pPath,
69
+ displayName: env.user,
70
+ });
71
+ messages.push(`Profile created at ${pPath}.`);
72
+ }
73
+ else {
74
+ messages.push(`Profile already exists at ${pPath}. Left untouched.`);
75
+ }
76
+ // 5b. Client configs.
77
+ for (const cd of clientDiffs) {
78
+ if (cd.action === "create" || cd.action === "update") {
79
+ applyClientDiff(cd, desiredServers, options.yes);
80
+ messages.push(`Wired ${cd.client} at ${cd.path}.`);
81
+ }
82
+ else if (cd.action === "no-change") {
83
+ messages.push(`${cd.client} already wired at ${cd.path}.`);
84
+ }
85
+ else if (cd.action === "skip") {
86
+ messages.push(`Skipped ${cd.client} at ${cd.path}: ${cd.reason ?? "collision (re-run with --yes to overwrite)"}.`);
87
+ }
88
+ }
89
+ // 5c. What-next.
90
+ messages.push("");
91
+ messages.push("What next:");
92
+ messages.push(" 1. Restart your MCP client so it picks up the new servers.");
93
+ messages.push(` 2. Edit ${pPath} to set your neurotypes and preferences.`);
94
+ return { diff, applied: true, messages };
95
+ }
96
+ function pickTargets(detections, client) {
97
+ if (client === "all")
98
+ return detections;
99
+ return detections.filter((d) => d.id === client);
100
+ }
101
+ async function diffClient(target, desired, options) {
102
+ let existing = null;
103
+ if (existsSync(target.path)) {
104
+ const raw = readFileSync(target.path, "utf8");
105
+ const parsed = parseJsonSafely(raw);
106
+ if (!parsed.ok) {
107
+ return {
108
+ client: target.id,
109
+ path: target.path,
110
+ action: "skip",
111
+ added: [],
112
+ collisions: [],
113
+ reason: `existing config is not valid JSON: ${parsed.error}`,
114
+ };
115
+ }
116
+ existing = parsed.value ?? {};
117
+ }
118
+ const merge = mergeMcpServers((existing ?? {}), desired, options.yes);
119
+ if (merge.collisions.length > 0 && !options.yes) {
120
+ return {
121
+ client: target.id,
122
+ path: target.path,
123
+ action: "skip",
124
+ added: merge.added,
125
+ collisions: merge.collisions,
126
+ reason: "existing keys would be overwritten; re-run with --yes to confirm",
127
+ };
128
+ }
129
+ if (merge.unchanged && existing !== null) {
130
+ return {
131
+ client: target.id,
132
+ path: target.path,
133
+ action: "no-change",
134
+ added: [],
135
+ collisions: [],
136
+ };
137
+ }
138
+ return {
139
+ client: target.id,
140
+ path: target.path,
141
+ action: existing === null ? "create" : "update",
142
+ added: merge.added,
143
+ collisions: merge.collisions,
144
+ };
145
+ }
146
+ function applyClientDiff(cd, desired, overwrite) {
147
+ const adapter = adapterFor(cd.client);
148
+ let existing = null;
149
+ if (existsSync(cd.path)) {
150
+ const raw = readFileSync(cd.path, "utf8");
151
+ const parsed = parseJsonSafely(raw);
152
+ if (parsed.ok)
153
+ existing = parsed.value;
154
+ }
155
+ const merged = mergeMcpServers(existing ?? {}, desired, overwrite);
156
+ const shaped = adapter.shapeConfig(existing, merged.merged.mcpServers);
157
+ mkdirSync(dirname(cd.path), { recursive: true });
158
+ writeFileSync(cd.path, `${JSON.stringify(shaped, null, 2)}\n`, "utf8");
159
+ }
160
+ function resolveTemplatePath(profile) {
161
+ const filename = profile === "minimal" ? "profile.minimal.yaml" : "profile.example.yaml";
162
+ const here = dirname(fileURLToPath(import.meta.url));
163
+ const candidates = [
164
+ resolve(here, "..", "..", "..", "core", "schemas", filename),
165
+ resolve(here, "..", "..", "..", "..", "core", "schemas", filename),
166
+ resolve(here, "..", "..", "core", "schemas", filename),
167
+ join(process.cwd(), "packages", "core", "schemas", filename),
168
+ ];
169
+ for (const c of candidates) {
170
+ if (existsSync(c))
171
+ return c;
172
+ }
173
+ throw new Error(`Could not locate template: ${filename}`);
174
+ }
175
+ function formatDiff(diff) {
176
+ const out = [];
177
+ out.push(`Profile: ${diff.profileAction} -> ${diff.profilePath}`);
178
+ for (const cd of diff.clients) {
179
+ out.push(`Client ${cd.client} (${cd.action}): ${cd.path}`);
180
+ for (const a of cd.added)
181
+ out.push(` + mcpServers.${a}`);
182
+ for (const c of cd.collisions)
183
+ out.push(` ! mcpServers.${c} (collision)`);
184
+ if (cd.reason)
185
+ out.push(` reason: ${cd.reason}`);
186
+ }
187
+ return out;
188
+ }
189
+ //# sourceMappingURL=init.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"init.js","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ import { type ValidationResult } from "../profile/validator.js";
2
+ export interface ValidateResult {
3
+ readonly path: string;
4
+ readonly result: ValidationResult;
5
+ readonly missing: boolean;
6
+ }
7
+ export declare function runProfileValidate(): Promise<ValidateResult>;
8
+ export interface ShowResult {
9
+ readonly path: string;
10
+ readonly yaml: string;
11
+ readonly missing: boolean;
12
+ }
13
+ export declare function runProfileShow(): Promise<ShowResult>;
14
+ //# sourceMappingURL=profile.d.ts.map