@domphy/mcp 0.16.0 → 0.17.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/README.md
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
# @domphy/mcp
|
|
1
|
+
# @domphy/mcp
|
|
2
|
+
|
|
3
|
+
**[domphy.com](https://domphy.com)** · [Docs](https://domphy.com/docs/mcp/) · [npm](https://www.npmjs.com/package/@domphy/mcp)
|
|
2
4
|
|
|
3
5
|
A [Model Context Protocol](https://modelcontextprotocol.io) server that gives MCP-capable AI agents (Claude Desktop, Cursor, …) first-class access to Domphy — so they can look up the real API and validate their own output instead of guessing.
|
|
4
6
|
|
|
@@ -10,7 +12,7 @@ A [Model Context Protocol](https://modelcontextprotocol.io) server that gives MC
|
|
|
10
12
|
| `domphy_get_patch` | one patch's full contract (host tag, signature, doc, source) |
|
|
11
13
|
| `domphy_list_packages` | all `@domphy/*` packages with versions + descriptions |
|
|
12
14
|
| `domphy_rules` | the Domphy code-generation rules (`llms.txt`) |
|
|
13
|
-
| `domphy_diagnose` | run [`@domphy/doctor`](https://
|
|
15
|
+
| `domphy_diagnose` | run [`@domphy/doctor`](https://domphy.com/docs/doctor/) on a JSON element tree and return issues to fix |
|
|
14
16
|
|
|
15
17
|
Patch/package data is fetched live from `domphy.com` (always current with the latest release); `domphy_diagnose` runs locally.
|
|
16
18
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { readFile } from "fs/promises";
|
|
3
3
|
import { dirname, isAbsolute, resolve } from "path";
|
|
4
4
|
import { diagnose, fix, format, validate } from "@domphy/doctor";
|
|
5
|
-
var ORIGIN = process.env.DOMPHY_ORIGIN ?? "https://
|
|
5
|
+
var ORIGIN = process.env.DOMPHY_ORIGIN ?? "https://domphy.com";
|
|
6
6
|
function appManifestSetting() {
|
|
7
7
|
return process.env.DOMPHY_APP_MANIFEST ?? "./app-manifest.json";
|
|
8
8
|
}
|
|
@@ -156,4 +156,4 @@ export {
|
|
|
156
156
|
listAppBlocks,
|
|
157
157
|
getAppBlock
|
|
158
158
|
};
|
|
159
|
-
//# sourceMappingURL=chunk-
|
|
159
|
+
//# sourceMappingURL=chunk-TYNEHXOP.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/tools.ts"],"sourcesContent":["import { readFile } from \"node:fs/promises\";\nimport { dirname, isAbsolute, resolve } from \"node:path\";\nimport { diagnose, fix, format, validate } from \"@domphy/doctor\";\n\n/**\n * Pure tool implementations for the Domphy MCP server. Kept transport-free so\n * they are unit-testable. The server (index.ts) wires these to MCP requests.\n */\n\nconst ORIGIN = process.env.DOMPHY_ORIGIN ?? \"https://domphy.com\";\n\n// Path to the app-block registry produced by `apps/web/scripts/app-manifest.mjs`.\n// Read lazily (per call) so the env var can be set after this module loads, and\n// overridable so the same MCP server can serve any app's blocks.\nfunction appManifestSetting(): string {\n return process.env.DOMPHY_APP_MANIFEST ?? \"./app-manifest.json\";\n}\n\ninterface Manifest {\n version: string;\n packages: Array<{\n name: string;\n version: string;\n description: string;\n subpaths: string[];\n peerDependencies: string[];\n }>;\n patches: Array<{\n name: string;\n hostTag: string | null;\n signature: string;\n props: Array<{\n name: string;\n type: string;\n optional: boolean;\n doc: string;\n }>;\n doc: string;\n example: string;\n source: string;\n }>;\n}\n\nlet cache: Manifest | null = null;\n\nexport async function loadManifest(): Promise<Manifest> {\n if (cache) return cache;\n const res = await fetch(`${ORIGIN}/manifest.json`);\n if (!res.ok) throw new Error(`Failed to fetch manifest: ${res.status}`);\n cache = (await res.json()) as Manifest;\n return cache;\n}\n\nexport async function listPatches(): Promise<string> {\n const m = await loadManifest();\n return m.patches\n .map(\n (p) => `${p.name}${p.hostTag ? ` <${p.hostTag}>` : \"\"} — ${p.signature}`,\n )\n .join(\"\\n\");\n}\n\nexport async function getPatch(name: string): Promise<string> {\n const m = await loadManifest();\n const patch = m.patches.find((p) => p.name === name);\n if (!patch) {\n const near = m.patches\n .filter((p) => p.name.includes(name) || name.includes(p.name))\n .map((p) => p.name);\n return `No patch named \"${name}\".${near.length ? ` Did you mean: ${near.join(\", \")}?` : \"\"}`;\n }\n return JSON.stringify(patch, null, 2);\n}\n\nexport async function listPackages(): Promise<string> {\n const m = await loadManifest();\n return m.packages\n .map((p) => `${p.name}@${p.version} — ${p.description}`)\n .join(\"\\n\");\n}\n\nexport async function getRules(): Promise<string> {\n const res = await fetch(`${ORIGIN}/llms.txt`);\n if (!res.ok) throw new Error(`Failed to fetch rules: ${res.status}`);\n return res.text();\n}\n\n/** Valid tone names + theme color names (tones.json) for themeColor()/dataTone. */\nexport async function getTones(): Promise<string> {\n const res = await fetch(`${ORIGIN}/tones.json`);\n if (!res.ok) throw new Error(`Failed to fetch tones: ${res.status}`);\n return res.text();\n}\n\n/** Runs @domphy/doctor on a JSON element tree (static parts only). */\nexport function diagnoseTree(elementJson: string): string {\n let tree: unknown;\n try {\n tree = JSON.parse(elementJson);\n } catch (error) {\n return `Invalid JSON: ${(error as Error).message}`;\n }\n return format(diagnose(tree));\n}\n\n/**\n * Runs @domphy/doctor's aggregate `validate()` on a JSON element tree and\n * returns the structured report (ok flag, issues, severity counts) as JSON.\n */\nexport function validateTree(elementJson: string): string {\n let tree: unknown;\n try {\n tree = JSON.parse(elementJson);\n } catch (error) {\n return `Invalid JSON: ${(error as Error).message}`;\n }\n return JSON.stringify(validate(tree), null, 2);\n}\n\n/**\n * Applies @domphy/doctor's lossless autofix to a JSON element tree and returns\n * the fixed tree, the fixes applied, and a validation report of what remains\n * (issues needing intent are not auto-fixed).\n */\nexport function fixTree(elementJson: string): string {\n let tree: unknown;\n try {\n tree = JSON.parse(elementJson);\n } catch (error) {\n return `Invalid JSON: ${(error as Error).message}`;\n }\n return JSON.stringify(fix(tree), null, 2);\n}\n\n// --- app-block registry (an app's OWN reusable Domphy blocks) ---\n\ninterface AppBlock {\n name: string;\n kind: \"block\" | \"patch\";\n /** Repo-relative path of the file the block is declared in. */\n file: string;\n signature: string;\n jsdoc: string;\n exportKind: \"default\" | \"named\";\n}\n\n/** Resolves the app-manifest path against the manifest dir / cwd as needed. */\nfunction appManifestPath(): string {\n const setting = appManifestSetting();\n return isAbsolute(setting) ? setting : resolve(process.cwd(), setting);\n}\n\nasync function loadAppBlocks(): Promise<AppBlock[]> {\n const text = await readFile(appManifestPath(), \"utf8\");\n return JSON.parse(text) as AppBlock[];\n}\n\nfunction missingManifestHint(): string {\n return (\n `No app-manifest found at \"${appManifestPath()}\". ` +\n \"Generate it with `node apps/web/scripts/app-manifest.mjs <srcDir> <outFile>` \" +\n \"and point DOMPHY_APP_MANIFEST at the output (default ./app-manifest.json).\"\n );\n}\n\n/** Lists the app's own blocks (name + signature + file) from the app-manifest. */\nexport async function listAppBlocks(): Promise<string> {\n let blocks: AppBlock[];\n try {\n blocks = await loadAppBlocks();\n } catch {\n return missingManifestHint();\n }\n if (blocks.length === 0) {\n return \"The app-manifest is empty — no exported Domphy blocks were found.\";\n }\n return blocks\n .map((b) => `${b.name} [${b.kind}] — ${b.signature} (${b.file})`)\n .join(\"\\n\");\n}\n\n/**\n * Returns one app block's full source (the file at the manifest's `file`),\n * along with its signature and jsdoc.\n */\nexport async function getAppBlock(name: string): Promise<string> {\n let blocks: AppBlock[];\n try {\n blocks = await loadAppBlocks();\n } catch {\n return missingManifestHint();\n }\n const block = blocks.find((b) => b.name === name);\n if (!block) {\n const near = blocks\n .filter((b) => b.name.includes(name) || name.includes(b.name))\n .map((b) => b.name);\n return `No app block named \"${name}\".${near.length ? ` Did you mean: ${near.join(\", \")}?` : \"\"}`;\n }\n // The manifest stores repo-relative paths; resolve them against the repo root,\n // which is the manifest's directory walked up out of apps/web/public, falling\n // back to cwd-relative resolution when that layout does not apply.\n let source: string;\n try {\n source = await readBlockSource(block.file);\n } catch (error) {\n source = `// Could not read source: ${(error as Error).message}`;\n }\n return JSON.stringify(\n {\n name: block.name,\n kind: block.kind,\n file: block.file,\n signature: block.signature,\n jsdoc: block.jsdoc,\n exportKind: block.exportKind,\n source,\n },\n null,\n 2,\n );\n}\n\n/** Reads a block's source file, resolving its repo-relative `file` path. */\nasync function readBlockSource(repoRelativeFile: string): Promise<string> {\n // The app-manifest lives at <repo>/apps/web/public/app-manifest.json by\n // default, so the repo root is three levels up from the manifest directory.\n const manifestDir = dirname(appManifestPath());\n const candidates = [\n resolve(manifestDir, \"../../..\", repoRelativeFile),\n resolve(process.cwd(), repoRelativeFile),\n resolve(manifestDir, repoRelativeFile),\n ];\n let lastError: unknown;\n for (const candidate of candidates) {\n try {\n return await readFile(candidate, \"utf8\");\n } catch (error) {\n lastError = error;\n }\n }\n throw lastError instanceof Error\n ? lastError\n : new Error(`file not found: ${repoRelativeFile}`);\n}\n"],"mappings":"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","names":[]}
|
package/dist/index.js
CHANGED
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
listPackages,
|
|
11
11
|
listPatches,
|
|
12
12
|
validateTree
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-TYNEHXOP.js";
|
|
14
14
|
|
|
15
15
|
// src/index.ts
|
|
16
16
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
@@ -20,7 +20,7 @@ import {
|
|
|
20
20
|
ListToolsRequestSchema
|
|
21
21
|
} from "@modelcontextprotocol/sdk/types.js";
|
|
22
22
|
var server = new Server(
|
|
23
|
-
{ name: "domphy", version: "0.
|
|
23
|
+
{ name: "domphy", version: "0.16.0" },
|
|
24
24
|
{ capabilities: { tools: {} } }
|
|
25
25
|
);
|
|
26
26
|
var tools = [
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { Server } from \"@modelcontextprotocol/sdk/server/index.js\";\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\nimport {\n CallToolRequestSchema,\n ListToolsRequestSchema,\n} from \"@modelcontextprotocol/sdk/types.js\";\nimport {\n diagnoseTree,\n fixTree,\n getAppBlock,\n getPatch,\n getRules,\n getTones,\n listAppBlocks,\n listPackages,\n listPatches,\n validateTree,\n} from \"./tools.js\";\n\nconst server = new Server(\n { name: \"domphy\", version: \"0.
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { Server } from \"@modelcontextprotocol/sdk/server/index.js\";\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\nimport {\n CallToolRequestSchema,\n ListToolsRequestSchema,\n} from \"@modelcontextprotocol/sdk/types.js\";\nimport {\n diagnoseTree,\n fixTree,\n getAppBlock,\n getPatch,\n getRules,\n getTones,\n listAppBlocks,\n listPackages,\n listPatches,\n validateTree,\n} from \"./tools.js\";\n\nconst server = new Server(\n { name: \"domphy\", version: \"0.16.0\" },\n { capabilities: { tools: {} } },\n);\n\nconst tools = [\n {\n name: \"domphy_list_patches\",\n description: \"List every @domphy/ui patch with its host tag and signature.\",\n inputSchema: { type: \"object\", properties: {} },\n },\n {\n name: \"domphy_get_patch\",\n description:\n \"Get one patch's full contract: host tag, signature, props (name/type/optional/doc), example, doc, and source.\",\n inputSchema: {\n type: \"object\",\n properties: {\n name: { type: \"string\", description: \"patch name, e.g. button\" },\n },\n required: [\"name\"],\n },\n },\n {\n name: \"domphy_list_packages\",\n description: \"List all @domphy/* packages with versions and descriptions.\",\n inputSchema: { type: \"object\", properties: {} },\n },\n {\n name: \"domphy_rules\",\n description: \"Get the Domphy code-generation rules (llms.txt) to follow.\",\n inputSchema: { type: \"object\", properties: {} },\n },\n {\n name: \"domphy_tones\",\n description:\n 'Get the valid tone names and theme color names for themeColor()/dataTone (e.g. themeColor(l, \"shift-9\", \"primary\")). Use this to avoid invented tones like \"surface\"/\"text\".',\n inputSchema: { type: \"object\", properties: {} },\n },\n {\n name: \"domphy_diagnose\",\n description:\n \"Run @domphy/doctor on a JSON Domphy element tree and return issues to fix (inline-typography, void-content, unknown-tag, missing/duplicate/unstable _key, …).\",\n inputSchema: {\n type: \"object\",\n properties: {\n element: {\n type: \"string\",\n description: \"JSON of the Domphy element tree\",\n },\n },\n required: [\"element\"],\n },\n },\n {\n name: \"domphy_validate\",\n description:\n \"Run @domphy/doctor's aggregate validate() on a JSON Domphy element tree. Returns a structured report { ok, issues, summary } with severity counts.\",\n inputSchema: {\n type: \"object\",\n properties: {\n element: {\n type: \"string\",\n description: \"JSON of the Domphy element tree\",\n },\n },\n required: [\"element\"],\n },\n },\n {\n name: \"domphy_fix\",\n description:\n \"Apply @domphy/doctor's lossless autofix to a JSON Domphy element tree. Returns { tree, applied, report }; only provably-safe fixes (e.g. void-content) are applied, remaining issues are in report.\",\n inputSchema: {\n type: \"object\",\n properties: {\n element: {\n type: \"string\",\n description: \"JSON of the Domphy element tree\",\n },\n },\n required: [\"element\"],\n },\n },\n {\n name: \"domphy_list_app_blocks\",\n description:\n \"List the current app's OWN reusable Domphy blocks (name, kind, signature, file) from its app-manifest.json. Run `app-manifest.mjs` first if absent.\",\n inputSchema: { type: \"object\", properties: {} },\n },\n {\n name: \"domphy_get_app_block\",\n description:\n \"Get one app block's full source plus signature and jsdoc, by name, from the app-manifest.\",\n inputSchema: {\n type: \"object\",\n properties: {\n name: { type: \"string\", description: \"app block name, e.g. App\" },\n },\n required: [\"name\"],\n },\n },\n];\n\nserver.setRequestHandler(ListToolsRequestSchema, async () => ({ tools }));\n\nserver.setRequestHandler(CallToolRequestSchema, async (request) => {\n const { name } = request.params;\n const args = (request.params.arguments ?? {}) as Record<string, unknown>;\n let text: string;\n try {\n switch (name) {\n case \"domphy_list_patches\":\n text = await listPatches();\n break;\n case \"domphy_get_patch\":\n text = await getPatch(String(args.name));\n break;\n case \"domphy_list_packages\":\n text = await listPackages();\n break;\n case \"domphy_rules\":\n text = await getRules();\n break;\n case \"domphy_tones\":\n text = await getTones();\n break;\n case \"domphy_diagnose\":\n text = diagnoseTree(String(args.element));\n break;\n case \"domphy_validate\":\n text = validateTree(String(args.element));\n break;\n case \"domphy_fix\":\n text = fixTree(String(args.element));\n break;\n case \"domphy_list_app_blocks\":\n text = await listAppBlocks();\n break;\n case \"domphy_get_app_block\":\n text = await getAppBlock(String(args.name));\n break;\n default:\n text = `Unknown tool: ${name}`;\n }\n } catch (error) {\n text = `Error: ${(error as Error).message}`;\n }\n return { content: [{ type: \"text\", text }] };\n});\n\nawait server.connect(new StdioServerTransport());\n"],"mappings":"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","names":[]}
|
package/dist/tools.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@domphy/mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.17.0",
|
|
4
4
|
"description": "Domphy MCP server - exposes patches, packages, rules, and the doctor to MCP-capable AI agents",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -37,17 +37,17 @@
|
|
|
37
37
|
},
|
|
38
38
|
"dependencies": {
|
|
39
39
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
40
|
-
"@domphy/doctor": "^0.
|
|
40
|
+
"@domphy/doctor": "^0.17.0"
|
|
41
41
|
},
|
|
42
42
|
"peerDependencies": {
|
|
43
|
-
"@domphy/core": "^0.
|
|
43
|
+
"@domphy/core": "^0.17.0"
|
|
44
44
|
},
|
|
45
45
|
"devDependencies": {
|
|
46
46
|
"@types/node": "^25.9.2",
|
|
47
47
|
"tsup": "^8.5.0",
|
|
48
48
|
"typescript": "^5.8.3",
|
|
49
49
|
"vitest": "^4.0.18",
|
|
50
|
-
"@domphy/core": "0.
|
|
50
|
+
"@domphy/core": "0.17.0"
|
|
51
51
|
},
|
|
52
52
|
"files": [
|
|
53
53
|
"dist",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/tools.ts"],"sourcesContent":["import { readFile } from \"node:fs/promises\";\nimport { dirname, isAbsolute, resolve } from \"node:path\";\nimport { diagnose, fix, format, validate } from \"@domphy/doctor\";\n\n/**\n * Pure tool implementations for the Domphy MCP server. Kept transport-free so\n * they are unit-testable. The server (index.ts) wires these to MCP requests.\n */\n\nconst ORIGIN = process.env.DOMPHY_ORIGIN ?? \"https://www.domphy.com\";\n\n// Path to the app-block registry produced by `apps/web/scripts/app-manifest.mjs`.\n// Read lazily (per call) so the env var can be set after this module loads, and\n// overridable so the same MCP server can serve any app's blocks.\nfunction appManifestSetting(): string {\n return process.env.DOMPHY_APP_MANIFEST ?? \"./app-manifest.json\";\n}\n\ninterface Manifest {\n version: string;\n packages: Array<{\n name: string;\n version: string;\n description: string;\n subpaths: string[];\n peerDependencies: string[];\n }>;\n patches: Array<{\n name: string;\n hostTag: string | null;\n signature: string;\n props: Array<{\n name: string;\n type: string;\n optional: boolean;\n doc: string;\n }>;\n doc: string;\n example: string;\n source: string;\n }>;\n}\n\nlet cache: Manifest | null = null;\n\nexport async function loadManifest(): Promise<Manifest> {\n if (cache) return cache;\n const res = await fetch(`${ORIGIN}/manifest.json`);\n if (!res.ok) throw new Error(`Failed to fetch manifest: ${res.status}`);\n cache = (await res.json()) as Manifest;\n return cache;\n}\n\nexport async function listPatches(): Promise<string> {\n const m = await loadManifest();\n return m.patches\n .map(\n (p) => `${p.name}${p.hostTag ? ` <${p.hostTag}>` : \"\"} — ${p.signature}`,\n )\n .join(\"\\n\");\n}\n\nexport async function getPatch(name: string): Promise<string> {\n const m = await loadManifest();\n const patch = m.patches.find((p) => p.name === name);\n if (!patch) {\n const near = m.patches\n .filter((p) => p.name.includes(name) || name.includes(p.name))\n .map((p) => p.name);\n return `No patch named \"${name}\".${near.length ? ` Did you mean: ${near.join(\", \")}?` : \"\"}`;\n }\n return JSON.stringify(patch, null, 2);\n}\n\nexport async function listPackages(): Promise<string> {\n const m = await loadManifest();\n return m.packages\n .map((p) => `${p.name}@${p.version} — ${p.description}`)\n .join(\"\\n\");\n}\n\nexport async function getRules(): Promise<string> {\n const res = await fetch(`${ORIGIN}/llms.txt`);\n if (!res.ok) throw new Error(`Failed to fetch rules: ${res.status}`);\n return res.text();\n}\n\n/** Valid tone names + theme color names (tones.json) for themeColor()/dataTone. */\nexport async function getTones(): Promise<string> {\n const res = await fetch(`${ORIGIN}/tones.json`);\n if (!res.ok) throw new Error(`Failed to fetch tones: ${res.status}`);\n return res.text();\n}\n\n/** Runs @domphy/doctor on a JSON element tree (static parts only). */\nexport function diagnoseTree(elementJson: string): string {\n let tree: unknown;\n try {\n tree = JSON.parse(elementJson);\n } catch (error) {\n return `Invalid JSON: ${(error as Error).message}`;\n }\n return format(diagnose(tree));\n}\n\n/**\n * Runs @domphy/doctor's aggregate `validate()` on a JSON element tree and\n * returns the structured report (ok flag, issues, severity counts) as JSON.\n */\nexport function validateTree(elementJson: string): string {\n let tree: unknown;\n try {\n tree = JSON.parse(elementJson);\n } catch (error) {\n return `Invalid JSON: ${(error as Error).message}`;\n }\n return JSON.stringify(validate(tree), null, 2);\n}\n\n/**\n * Applies @domphy/doctor's lossless autofix to a JSON element tree and returns\n * the fixed tree, the fixes applied, and a validation report of what remains\n * (issues needing intent are not auto-fixed).\n */\nexport function fixTree(elementJson: string): string {\n let tree: unknown;\n try {\n tree = JSON.parse(elementJson);\n } catch (error) {\n return `Invalid JSON: ${(error as Error).message}`;\n }\n return JSON.stringify(fix(tree), null, 2);\n}\n\n// --- app-block registry (an app's OWN reusable Domphy blocks) ---\n\ninterface AppBlock {\n name: string;\n kind: \"block\" | \"patch\";\n /** Repo-relative path of the file the block is declared in. */\n file: string;\n signature: string;\n jsdoc: string;\n exportKind: \"default\" | \"named\";\n}\n\n/** Resolves the app-manifest path against the manifest dir / cwd as needed. */\nfunction appManifestPath(): string {\n const setting = appManifestSetting();\n return isAbsolute(setting) ? setting : resolve(process.cwd(), setting);\n}\n\nasync function loadAppBlocks(): Promise<AppBlock[]> {\n const text = await readFile(appManifestPath(), \"utf8\");\n return JSON.parse(text) as AppBlock[];\n}\n\nfunction missingManifestHint(): string {\n return (\n `No app-manifest found at \"${appManifestPath()}\". ` +\n \"Generate it with `node apps/web/scripts/app-manifest.mjs <srcDir> <outFile>` \" +\n \"and point DOMPHY_APP_MANIFEST at the output (default ./app-manifest.json).\"\n );\n}\n\n/** Lists the app's own blocks (name + signature + file) from the app-manifest. */\nexport async function listAppBlocks(): Promise<string> {\n let blocks: AppBlock[];\n try {\n blocks = await loadAppBlocks();\n } catch {\n return missingManifestHint();\n }\n if (blocks.length === 0) {\n return \"The app-manifest is empty — no exported Domphy blocks were found.\";\n }\n return blocks\n .map((b) => `${b.name} [${b.kind}] — ${b.signature} (${b.file})`)\n .join(\"\\n\");\n}\n\n/**\n * Returns one app block's full source (the file at the manifest's `file`),\n * along with its signature and jsdoc.\n */\nexport async function getAppBlock(name: string): Promise<string> {\n let blocks: AppBlock[];\n try {\n blocks = await loadAppBlocks();\n } catch {\n return missingManifestHint();\n }\n const block = blocks.find((b) => b.name === name);\n if (!block) {\n const near = blocks\n .filter((b) => b.name.includes(name) || name.includes(b.name))\n .map((b) => b.name);\n return `No app block named \"${name}\".${near.length ? ` Did you mean: ${near.join(\", \")}?` : \"\"}`;\n }\n // The manifest stores repo-relative paths; resolve them against the repo root,\n // which is the manifest's directory walked up out of apps/web/public, falling\n // back to cwd-relative resolution when that layout does not apply.\n let source: string;\n try {\n source = await readBlockSource(block.file);\n } catch (error) {\n source = `// Could not read source: ${(error as Error).message}`;\n }\n return JSON.stringify(\n {\n name: block.name,\n kind: block.kind,\n file: block.file,\n signature: block.signature,\n jsdoc: block.jsdoc,\n exportKind: block.exportKind,\n source,\n },\n null,\n 2,\n );\n}\n\n/** Reads a block's source file, resolving its repo-relative `file` path. */\nasync function readBlockSource(repoRelativeFile: string): Promise<string> {\n // The app-manifest lives at <repo>/apps/web/public/app-manifest.json by\n // default, so the repo root is three levels up from the manifest directory.\n const manifestDir = dirname(appManifestPath());\n const candidates = [\n resolve(manifestDir, \"../../..\", repoRelativeFile),\n resolve(process.cwd(), repoRelativeFile),\n resolve(manifestDir, repoRelativeFile),\n ];\n let lastError: unknown;\n for (const candidate of candidates) {\n try {\n return await readFile(candidate, \"utf8\");\n } catch (error) {\n lastError = error;\n }\n }\n throw lastError instanceof Error\n ? lastError\n : new Error(`file not found: ${repoRelativeFile}`);\n}\n"],"mappings":"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","names":[]}
|