loom-spec 0.1.0 → 0.1.1

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/README.md CHANGED
@@ -45,6 +45,8 @@ Writes:
45
45
 
46
46
  Refuses to overwrite an existing `.loom/` unless `--force`.
47
47
 
48
+ Add `--mcp` to also register the MCP server in `.mcp.json` (idempotent merge — existing entries are preserved). Or run `npx loom-spec install-mcp` to register it after the fact.
49
+
48
50
  ### `loom-spec view [--root <dir>] [--port <n>]`
49
51
 
50
52
  Starts a local browser editor. Walks up from `--root` (default: cwd) to find the nearest `.loom/`. Opens on port 7777 by default.
@@ -69,12 +71,14 @@ Starts a **Model Context Protocol** server on stdio. Wire it into Claude Code (o
69
71
  "mcpServers": {
70
72
  "loom-spec": {
71
73
  "command": "npx",
72
- "args": ["loom-spec", "mcp"]
74
+ "args": ["-y", "loom-spec", "mcp"]
73
75
  }
74
76
  }
75
77
  }
76
78
  ```
77
79
 
80
+ If you'd rather not hand-edit, `npx loom-spec install-mcp` writes this entry into `.mcp.json` for you (merging with any existing servers, idempotent).
81
+
78
82
  The server exposes semantic tools that validate against the schema before writing, more token-efficient than re-reading and re-writing the JSON on every change:
79
83
 
80
84
  - `loom_list_diagrams`, `loom_read_diagram`, `loom_read_node_types`
package/dist/cli/index.js CHANGED
@@ -3,12 +3,18 @@ import { runInit } from "./init.js";
3
3
  import { runView } from "./view.js";
4
4
  import { runValidate } from "./validate.js";
5
5
  import { runMcp } from "./mcp.js";
6
+ import { runInstallMcp } from "./installMcp.js";
6
7
  const HELP = `loom-spec — node-based architecture spec for your repo
7
8
 
8
9
  Usage:
9
- loom-spec init [--path <dir>] [--force]
10
+ loom-spec init [--path <dir>] [--force] [--mcp]
10
11
  Scaffold .loom/ and .claude/skills/loom-spec/ in the target directory.
11
- Defaults to current working directory.
12
+ With --mcp, also register the MCP server in .mcp.json (merging
13
+ with any existing entries). Defaults to current working directory.
14
+
15
+ loom-spec install-mcp [--path <dir>]
16
+ Register the loom-spec MCP server in .mcp.json without touching
17
+ anything else. Idempotent — safe to run multiple times.
12
18
 
13
19
  loom-spec view [--root <dir>] [--port <n>] [--dev]
14
20
  Start the local browser editor. Walks up from --root (default: cwd)
@@ -61,6 +67,13 @@ async function main() {
61
67
  await runInit({
62
68
  path: flags.path ?? process.cwd(),
63
69
  force: Boolean(flags.force),
70
+ mcp: Boolean(flags.mcp),
71
+ });
72
+ return;
73
+ }
74
+ if (subcommand === "install-mcp") {
75
+ await runInstallMcp({
76
+ path: flags.path ?? process.cwd(),
64
77
  });
65
78
  return;
66
79
  }
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"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"}
@@ -1,5 +1,6 @@
1
1
  export interface InitArgs {
2
2
  path: string;
3
3
  force: boolean;
4
+ mcp: boolean;
4
5
  }
5
6
  export declare function runInit(args: InitArgs): Promise<void>;
package/dist/cli/init.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import { mkdir, readFile, writeFile, readdir, stat } from "node:fs/promises";
2
2
  import { resolve, dirname, join, relative } from "node:path";
3
3
  import { fileURLToPath } from "node:url";
4
+ import { installMcpEntry } from "./mcpConfig.js";
4
5
  const here = dirname(fileURLToPath(import.meta.url));
5
6
  // Templates ship alongside dist/ in the package: dist/cli/init.js → ../../templates/
6
7
  const templatesRoot = resolve(here, "../../templates");
@@ -60,10 +61,30 @@ export async function runInit(args) {
60
61
  for (const f of written) {
61
62
  console.log(` ${relative(target, f)}`);
62
63
  }
64
+ if (args.mcp) {
65
+ console.log();
66
+ try {
67
+ const report = await installMcpEntry(target);
68
+ const rel = relative(target, report.path);
69
+ if (report.action === "unchanged") {
70
+ console.log(`MCP entry already present in ${rel}.`);
71
+ }
72
+ else {
73
+ console.log(`${report.action === "created" ? "Created" : "Updated"} ${rel} with the loom-spec MCP server.`);
74
+ }
75
+ }
76
+ catch (e) {
77
+ console.error(`error registering MCP entry: ${e.message}`);
78
+ process.exitCode = 1;
79
+ }
80
+ }
63
81
  console.log();
64
82
  console.log("Next steps:");
65
83
  console.log(" 1. Run `npx loom-spec view` to open the editor.");
66
84
  console.log(" 2. Edit `.loom/node-types.json` to add project-specific node types.");
67
85
  console.log(" 3. Start populating `.loom/diagrams/overview.flow.json`.");
86
+ if (!args.mcp) {
87
+ console.log(" 4. To register the MCP server: re-run with `--mcp`, or `npx loom-spec install-mcp`.");
88
+ }
68
89
  }
69
90
  //# sourceMappingURL=init.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"init.js","sourceRoot":"","sources":["../../src/cli/init.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"init.js","sourceRoot":"","sources":["../../src/cli/init.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ export interface InstallMcpArgs {
2
+ path: string;
3
+ }
4
+ export declare function runInstallMcp(args: InstallMcpArgs): Promise<void>;
@@ -0,0 +1,23 @@
1
+ import { resolve, relative } from "node:path";
2
+ import { installMcpEntry } from "./mcpConfig.js";
3
+ export async function runInstallMcp(args) {
4
+ const target = resolve(args.path);
5
+ try {
6
+ const report = await installMcpEntry(target);
7
+ const rel = relative(process.cwd(), report.path);
8
+ if (report.action === "unchanged") {
9
+ console.log(`MCP entry already up to date in ${rel}.`);
10
+ }
11
+ else if (report.action === "created") {
12
+ console.log(`Created ${rel} with the loom-spec MCP server.`);
13
+ }
14
+ else {
15
+ console.log(`Updated ${rel} — added loom-spec MCP server entry.`);
16
+ }
17
+ }
18
+ catch (e) {
19
+ console.error(`error: ${e.message}`);
20
+ process.exit(1);
21
+ }
22
+ }
23
+ //# sourceMappingURL=installMcp.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"installMcp.js","sourceRoot":"","sources":["../../src/cli/installMcp.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAC9C,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAMjD,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,IAAoB;IACtD,MAAM,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAClC,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,eAAe,CAAC,MAAM,CAAC,CAAC;QAC7C,MAAM,GAAG,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC;QACjD,IAAI,MAAM,CAAC,MAAM,KAAK,WAAW,EAAE,CAAC;YAClC,OAAO,CAAC,GAAG,CAAC,mCAAmC,GAAG,GAAG,CAAC,CAAC;QACzD,CAAC;aAAM,IAAI,MAAM,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;YACvC,OAAO,CAAC,GAAG,CAAC,WAAW,GAAG,iCAAiC,CAAC,CAAC;QAC/D,CAAC;aAAM,CAAC;YACN,OAAO,CAAC,GAAG,CAAC,WAAW,GAAG,sCAAsC,CAAC,CAAC;QACpE,CAAC;IACH,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,OAAO,CAAC,KAAK,CAAC,UAAW,CAAW,CAAC,OAAO,EAAE,CAAC,CAAC;QAChD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC"}
@@ -0,0 +1,21 @@
1
+ export interface McpServerEntry {
2
+ command: string;
3
+ args?: string[];
4
+ env?: Record<string, string>;
5
+ cwd?: string;
6
+ }
7
+ /**
8
+ * Idempotently register the loom-spec MCP server in <projectPath>/.mcp.json.
9
+ *
10
+ * - If the file doesn't exist, creates it with just our entry.
11
+ * - If it exists and parses, merges our entry into mcpServers. Other servers
12
+ * and top-level keys are preserved.
13
+ * - If the existing entry is identical, reports "unchanged" and exits clean.
14
+ * - If the file exists but doesn't parse, errors out without writing.
15
+ *
16
+ * Returns a small report describing what happened.
17
+ */
18
+ export declare function installMcpEntry(projectPath: string): Promise<{
19
+ path: string;
20
+ action: "created" | "updated" | "unchanged";
21
+ }>;
@@ -0,0 +1,56 @@
1
+ import { readFile, writeFile, stat } from "node:fs/promises";
2
+ import { resolve, relative } from "node:path";
3
+ const SERVER_NAME = "loom-spec";
4
+ function loomSpecEntry() {
5
+ return {
6
+ command: "npx",
7
+ args: ["-y", "loom-spec", "mcp"],
8
+ };
9
+ }
10
+ /**
11
+ * Idempotently register the loom-spec MCP server in <projectPath>/.mcp.json.
12
+ *
13
+ * - If the file doesn't exist, creates it with just our entry.
14
+ * - If it exists and parses, merges our entry into mcpServers. Other servers
15
+ * and top-level keys are preserved.
16
+ * - If the existing entry is identical, reports "unchanged" and exits clean.
17
+ * - If the file exists but doesn't parse, errors out without writing.
18
+ *
19
+ * Returns a small report describing what happened.
20
+ */
21
+ export async function installMcpEntry(projectPath) {
22
+ const configPath = resolve(projectPath, ".mcp.json");
23
+ let existing = null;
24
+ try {
25
+ await stat(configPath);
26
+ const raw = await readFile(configPath, "utf8");
27
+ try {
28
+ existing = JSON.parse(raw);
29
+ }
30
+ catch (e) {
31
+ throw new Error(`${relative(process.cwd(), configPath)} exists but is not valid JSON: ${e.message}`);
32
+ }
33
+ }
34
+ catch (e) {
35
+ const code = e.code;
36
+ if (code && code !== "ENOENT")
37
+ throw e;
38
+ // fall through: existing remains null
39
+ }
40
+ const desired = loomSpecEntry();
41
+ const config = existing ?? {};
42
+ const servers = config.mcpServers ?? {};
43
+ const current = servers[SERVER_NAME];
44
+ // Compare normalized JSON to detect identical entries
45
+ if (current && JSON.stringify(current) === JSON.stringify(desired)) {
46
+ return { path: configPath, action: "unchanged" };
47
+ }
48
+ servers[SERVER_NAME] = desired;
49
+ config.mcpServers = servers;
50
+ await writeFile(configPath, JSON.stringify(config, null, 2) + "\n", "utf8");
51
+ return {
52
+ path: configPath,
53
+ action: existing ? "updated" : "created",
54
+ };
55
+ }
56
+ //# sourceMappingURL=mcpConfig.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcpConfig.js","sourceRoot":"","sources":["../../src/cli/mcpConfig.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAC7D,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAc9C,MAAM,WAAW,GAAG,WAAW,CAAC;AAEhC,SAAS,aAAa;IACpB,OAAO;QACL,OAAO,EAAE,KAAK;QACd,IAAI,EAAE,CAAC,IAAI,EAAE,WAAW,EAAE,KAAK,CAAC;KACjC,CAAC;AACJ,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,WAAmB;IAIvD,MAAM,UAAU,GAAG,OAAO,CAAC,WAAW,EAAE,WAAW,CAAC,CAAC;IAErD,IAAI,QAAQ,GAAqB,IAAI,CAAC;IACtC,IAAI,CAAC;QACH,MAAM,IAAI,CAAC,UAAU,CAAC,CAAC;QACvB,MAAM,GAAG,GAAG,MAAM,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;QAC/C,IAAI,CAAC;YACH,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAc,CAAC;QAC1C,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,MAAM,IAAI,KAAK,CACb,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,UAAU,CAAC,kCAAmC,CAAW,CAAC,OAAO,EAAE,CAC/F,CAAC;QACJ,CAAC;IACH,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,MAAM,IAAI,GAAI,CAA2B,CAAC,IAAI,CAAC;QAC/C,IAAI,IAAI,IAAI,IAAI,KAAK,QAAQ;YAAE,MAAM,CAAC,CAAC;QACvC,sCAAsC;IACxC,CAAC;IAED,MAAM,OAAO,GAAG,aAAa,EAAE,CAAC;IAChC,MAAM,MAAM,GAAc,QAAQ,IAAI,EAAE,CAAC;IACzC,MAAM,OAAO,GAAG,MAAM,CAAC,UAAU,IAAI,EAAE,CAAC;IACxC,MAAM,OAAO,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC;IAErC,sDAAsD;IACtD,IAAI,OAAO,IAAI,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,KAAK,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,EAAE,CAAC;QACnE,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,EAAE,WAAW,EAAE,CAAC;IACnD,CAAC;IAED,OAAO,CAAC,WAAW,CAAC,GAAG,OAAO,CAAC;IAC/B,MAAM,CAAC,UAAU,GAAG,OAAO,CAAC;IAE5B,MAAM,SAAS,CAAC,UAAU,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,EAAE,MAAM,CAAC,CAAC;IAC5E,OAAO;QACL,IAAI,EAAE,UAAU;QAChB,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS;KACzC,CAAC;AACJ,CAAC"}