capy-mcp 1.0.8 → 1.0.10

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/dist/brief.js CHANGED
@@ -13,6 +13,7 @@ const SECTION_ORDER = [
13
13
  "Data Display",
14
14
  "Feedback",
15
15
  "Overlays",
16
+ "Components",
16
17
  "Feature or Page Sections",
17
18
  ];
18
19
  export async function buildPreviewBrief(projectRoot, input) {
@@ -143,6 +144,7 @@ function buildWarnings(framework, changedFiles, discoveryGaps = []) {
143
144
  warnings.push("No changedFiles were provided. The agent should inspect git diff or recent edits when performing update_preview.");
144
145
  }
145
146
  warnings.push(...discoveryGaps);
147
+ warnings.push("IMPORTANT: Add .capy/ and the created preview route/page files (e.g. app/preview/, pages/preview.tsx) to .gitignore so they are not committed to the repository.");
146
148
  return warnings;
147
149
  }
148
150
  function buildInstructions(projectFacts, input, discoveryInstruction) {
@@ -150,5 +152,5 @@ function buildInstructions(projectFacts, input, discoveryInstruction) {
150
152
  ? "Update the existing /preview route incrementally."
151
153
  : "Create the /preview route from scratch.";
152
154
  const userGoal = input.userGoal ? ` User goal: ${input.userGoal}.` : "";
153
- return `${lead}${userGoal} Read the app shell first, then global styles, then traverse all discovered component directories. After that, implement ${projectFacts.previewEntryFile} as a clean preview surface that supports both vertical and horizontal scanning when useful, includes a dedicated icon section when icons can be discovered, and renders colors as consistent swatches with 6-character hex labels plus click-to-copy behavior using a pointer cursor. ${discoveryInstruction}`;
155
+ return `${lead}${userGoal} Read the app shell first, then global styles, then traverse all discovered component directories. After that, implement ${projectFacts.previewEntryFile} as a clean preview surface that supports both vertical and horizontal scanning when useful, includes a dedicated icon section when icons can be discovered, and renders colors as consistent swatches with 6-character hex labels plus click-to-copy behavior using a pointer cursor. ${discoveryInstruction} IMPORTANT: After creating files, add the .capy/ folder and the created preview page files (e.g. ${projectFacts.previewEntryFile}) to .gitignore so they are not committed to the repository.`;
154
156
  }
@@ -87,9 +87,13 @@ function buildDiscoveryFacts(components) {
87
87
  for (const comp of components) {
88
88
  const slashIndex = comp.path.lastIndexOf("/");
89
89
  const dir = slashIndex === -1 ? "." : comp.path.slice(0, slashIndex);
90
- if (!byDir.has(dir))
91
- byDir.set(dir, []);
92
- byDir.get(dir).push(comp.exports[0] ?? comp.basename);
90
+ const existing = byDir.get(dir);
91
+ if (existing) {
92
+ existing.push(comp.exports[0] ?? comp.basename);
93
+ }
94
+ else {
95
+ byDir.set(dir, [comp.exports[0] ?? comp.basename]);
96
+ }
93
97
  }
94
98
  const facts = [];
95
99
  for (const [dir, names] of byDir) {
@@ -1,6 +1,5 @@
1
1
  import { glob } from "glob";
2
2
  import { basename, join } from "path";
3
- import { buildPreviewBrief } from "./brief.js";
4
3
  import { buildComponentDiscoveryPlan } from "./component-discovery.js";
5
4
  import { readText, toPosixPath, writeText } from "./files.js";
6
5
  import { detectFramework } from "./framework.js";
@@ -10,28 +9,10 @@ export async function buildDesignSystemArtifact(projectRoot, input = {}) {
10
9
  const framework = await detectFramework(projectRoot);
11
10
  const projectFacts = await buildProjectFacts(projectRoot, framework);
12
11
  const discoveryPlan = await buildComponentDiscoveryPlan(projectRoot, projectFacts);
13
- const previewBrief = await buildPreviewBrief(projectRoot, {
14
- task: input.mode === "update" ? "update_preview" : "build_preview",
15
- changedFiles: input.changedFiles,
16
- userGoal: input.userGoal,
17
- });
18
12
  const cssVariables = await collectCssVariables(projectRoot, projectFacts.likelyStyleFiles);
19
13
  const components = await collectComponents(projectRoot, projectFacts);
20
- const readFirst = buildReadFirst(previewBrief, components, discoveryPlan.prioritizedFiles);
21
- const bridges = await buildUsageBridges(projectRoot, projectFacts.likelyStyleFiles, components);
22
- const gaps = buildGapNotes(projectFacts, components, cssVariables, discoveryPlan.missingFamilyGaps);
23
14
  const artifactPath = input.artifactPath ?? DEFAULT_ARTIFACT_PATH;
24
15
  return {
25
- forAgent: {
26
- intent: input.mode === "update"
27
- ? "Update /preview incrementally. Read the files below first, then touch only the sections affected by the changed files unless shared foundations changed."
28
- : "Build or refine /preview. Read the files below first, then mirror the repo's existing UI language instead of inventing a new one.",
29
- readFirst,
30
- facts: buildAgentFacts(projectFacts, components, cssVariables, discoveryPlan.discoveredFamilyFacts),
31
- bridges,
32
- gaps,
33
- updateHints: previewBrief.updateStrategy,
34
- },
35
16
  artifact: {
36
17
  generatedAt: new Date().toISOString(),
37
18
  mode: input.mode ?? "build",
@@ -50,6 +31,7 @@ export async function buildDesignSystemArtifact(projectRoot, input = {}) {
50
31
  pageDirs: projectFacts.likelyPageDirs,
51
32
  styleFiles: projectFacts.likelyStyleFiles,
52
33
  uiDirs: projectFacts.likelyUiDirs,
34
+ discoveredFamilies: discoveryPlan.discoveredFamilyFacts,
53
35
  },
54
36
  tokens: {
55
37
  cssVariables,
@@ -59,12 +41,6 @@ export async function buildDesignSystemArtifact(projectRoot, input = {}) {
59
41
  count: components.length,
60
42
  items: components,
61
43
  },
62
- preview: {
63
- route: projectFacts.previewRoute,
64
- entryFile: projectFacts.previewEntryFile,
65
- sections: previewBrief.deliverableSpec.sections,
66
- updateStrategy: previewBrief.updateStrategy,
67
- },
68
44
  };
69
45
  }
70
46
  export async function writeDesignSystemArtifact(projectRoot, input = {}) {
@@ -170,131 +146,3 @@ function classifyCssVariable(name) {
170
146
  function lineNumberAt(contents, index) {
171
147
  return contents.slice(0, index).split("\n").length;
172
148
  }
173
- function buildReadFirst(previewBrief, components, prioritizedFiles) {
174
- const seen = new Set();
175
- const readFirst = [];
176
- const firstComponentByDir = new Map();
177
- for (const component of components) {
178
- const slashIndex = component.path.lastIndexOf("/");
179
- if (slashIndex === -1)
180
- continue;
181
- const directory = component.path.slice(0, slashIndex);
182
- if (!firstComponentByDir.has(directory)) {
183
- firstComponentByDir.set(directory, component.path);
184
- }
185
- }
186
- for (const step of previewBrief.inspectionPlan.slice(0, 2)) {
187
- for (const target of step.targets) {
188
- if (!looksLikeProjectPath(target))
189
- continue;
190
- const normalizedTarget = firstComponentByDir.get(target) ?? target;
191
- if (seen.has(normalizedTarget))
192
- continue;
193
- seen.add(normalizedTarget);
194
- readFirst.push({
195
- path: normalizedTarget,
196
- reason: step.reason,
197
- });
198
- if (readFirst.length >= 8) {
199
- return readFirst;
200
- }
201
- }
202
- }
203
- for (const prioritizedFile of prioritizedFiles) {
204
- if (seen.has(prioritizedFile))
205
- continue;
206
- seen.add(prioritizedFile);
207
- readFirst.push({
208
- path: prioritizedFile,
209
- reason: "Likely reusable primitive or usage example. Read this before inventing preview-only specimens.",
210
- });
211
- if (readFirst.length >= 8) {
212
- return readFirst;
213
- }
214
- }
215
- for (const step of previewBrief.inspectionPlan.slice(2)) {
216
- for (const target of step.targets) {
217
- if (!looksLikeProjectPath(target))
218
- continue;
219
- const normalizedTarget = firstComponentByDir.get(target) ?? target;
220
- if (seen.has(normalizedTarget))
221
- continue;
222
- seen.add(normalizedTarget);
223
- readFirst.push({
224
- path: normalizedTarget,
225
- reason: step.reason,
226
- });
227
- if (readFirst.length >= 8) {
228
- return readFirst;
229
- }
230
- }
231
- }
232
- return readFirst;
233
- }
234
- function buildAgentFacts(projectFacts, components, cssVariables, discoveredFamilyFacts) {
235
- return [
236
- `Use ${projectFacts.framework} conventions.`,
237
- `Implement /preview at ${projectFacts.previewEntryFile}.`,
238
- "Use repo-relative paths only.",
239
- `Found ${components.length} component candidates and ${cssVariables.length} CSS variables.`,
240
- ...discoveredFamilyFacts,
241
- ];
242
- }
243
- function buildGapNotes(projectFacts, components, cssVariables, discoveryGaps) {
244
- const gaps = [...discoveryGaps];
245
- if (components.length === 0) {
246
- gaps.push("No components were detected. Grep src/components, src/ui, src/features, components, ui, or features before assuming the app has no reusable UI.");
247
- }
248
- if (cssVariables.length === 0 && projectFacts.likelyStyleFiles.length > 0) {
249
- gaps.push(`No CSS variables were detected. Read ${projectFacts.likelyStyleFiles[0]} directly and look for literal classes, theme config, or inline color tokens.`);
250
- }
251
- if (projectFacts.likelyStyleFiles.length === 0) {
252
- gaps.push("No global style files were detected. Inspect app shell files and component code directly for layout and color usage.");
253
- }
254
- return gaps;
255
- }
256
- async function buildUsageBridges(projectRoot, styleFiles, components) {
257
- const bridges = [];
258
- for (const relativePath of styleFiles) {
259
- const fileContents = await readText(join(projectRoot, relativePath));
260
- if (!fileContents)
261
- continue;
262
- if (fileContents.includes("hsl(var(--")) {
263
- bridges.push(`Theme tokens are bridged through hsl(var(--...)) in ${relativePath}.`);
264
- break;
265
- }
266
- }
267
- const hexByFile = await collectHexLiterals(projectRoot, components);
268
- for (const [path, hexValues] of hexByFile.slice(0, 3)) {
269
- bridges.push(`Literal accents also appear in ${path}: ${hexValues.join(", ")}.`);
270
- }
271
- return bridges;
272
- }
273
- async function collectHexLiterals(projectRoot, components) {
274
- const matches = [];
275
- for (const component of components) {
276
- const contents = await readText(join(projectRoot, component.path));
277
- if (!contents)
278
- continue;
279
- const hexMatches = Array.from(contents.matchAll(/#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{6})\b/g))
280
- .map((match) => normalizeHex(match[0]))
281
- .filter((value, index, values) => values.indexOf(value) === index);
282
- if (hexMatches.length > 0) {
283
- matches.push([component.path, hexMatches.slice(0, 4)]);
284
- }
285
- }
286
- return matches;
287
- }
288
- function normalizeHex(hex) {
289
- const normalized = hex.replace("#", "").toUpperCase();
290
- if (normalized.length === 3) {
291
- return `#${normalized
292
- .split("")
293
- .map((char) => `${char}${char}`)
294
- .join("")}`;
295
- }
296
- return `#${normalized}`;
297
- }
298
- function looksLikeProjectPath(target) {
299
- return target.includes("/") || /^[A-Za-z0-9._-]+\.(?:tsx|ts|jsx|js|css|scss|sass|less)$/.test(target);
300
- }
package/dist/server.js CHANGED
@@ -224,9 +224,9 @@ export function createServer(projectRoot = process.cwd()) {
224
224
  preview_entry_file: z.string(),
225
225
  component_count: z.number(),
226
226
  css_variable_count: z.number(),
227
- read_first: z.array(z.string()),
228
- warnings: z.array(z.string()),
229
- summary: z.string(),
227
+ component_dirs: z.array(z.string()),
228
+ style_files: z.array(z.string()),
229
+ discovered_families: z.array(z.string()),
230
230
  },
231
231
  }, async ({ artifactPath = ".capy/design-system.json", mode = "build", changedFiles, userGoal }) => {
232
232
  const artifact = await writeDesignSystemArtifact(projectRoot, {
@@ -243,9 +243,9 @@ export function createServer(projectRoot = process.cwd()) {
243
243
  preview_entry_file: artifact.repo.previewEntryFile,
244
244
  component_count: artifact.components.count,
245
245
  css_variable_count: artifact.tokens.cssVariables.length,
246
- read_first: artifact.forAgent.readFirst.map((item) => item.path),
247
- warnings: artifact.forAgent.gaps,
248
- summary: artifact.forAgent.intent,
246
+ component_dirs: artifact.scan.componentDirs,
247
+ style_files: artifact.scan.styleFiles,
248
+ discovered_families: artifact.scan.discoveredFamilies,
249
249
  };
250
250
  return {
251
251
  content: [
@@ -263,7 +263,8 @@ export function createServer(projectRoot = process.cwd()) {
263
263
  return server;
264
264
  }
265
265
  export async function main() {
266
- const server = createServer();
266
+ const projectRoot = process.argv[2] || process.cwd();
267
+ const server = createServer(projectRoot);
267
268
  const transport = new StdioServerTransport();
268
269
  await server.connect(transport);
269
270
  }
package/dist/types.d.ts CHANGED
@@ -66,17 +66,6 @@ export interface ComponentRecord {
66
66
  kind: "primitive" | "component" | "feature" | "unknown";
67
67
  }
68
68
  export interface DesignSystemArtifact {
69
- forAgent: {
70
- intent: string;
71
- readFirst: Array<{
72
- path: string;
73
- reason: string;
74
- }>;
75
- facts: string[];
76
- bridges: string[];
77
- gaps: string[];
78
- updateHints: string[];
79
- };
80
69
  artifact: {
81
70
  generatedAt: string;
82
71
  mode: "build" | "update";
@@ -95,6 +84,7 @@ export interface DesignSystemArtifact {
95
84
  pageDirs: string[];
96
85
  styleFiles: string[];
97
86
  uiDirs: string[];
87
+ discoveredFamilies: string[];
98
88
  };
99
89
  tokens: {
100
90
  cssVariables: CssVariableRecord[];
@@ -104,12 +94,6 @@ export interface DesignSystemArtifact {
104
94
  count: number;
105
95
  items: ComponentRecord[];
106
96
  };
107
- preview: {
108
- route: string;
109
- entryFile: string;
110
- sections: string[];
111
- updateStrategy: string[];
112
- };
113
97
  }
114
98
  export interface PreviewStateSnapshot {
115
99
  generatedAt: string;
package/dist/update.js CHANGED
@@ -29,7 +29,7 @@ export async function runPreviewUpdate(projectRoot, input = {}) {
29
29
  userGoal: input.userGoal,
30
30
  });
31
31
  await writePreviewStateSnapshot(projectRoot, snapshotPath, diffResult.snapshot);
32
- const warnings = [...previewBrief.warnings];
32
+ const warnings = [];
33
33
  if (diffResult.baselineCreated) {
34
34
  warnings.unshift("No prior preview-state snapshot was found. Capy created a baseline snapshot, so this run cannot infer incremental changes automatically.");
35
35
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "capy-mcp",
3
- "version": "1.0.8",
3
+ "version": "1.0.10",
4
4
  "description": "MCP server that inspects a repo, returns a structured /preview brief, and writes a design-system JSON artifact for AI coding agents",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",