refine-mcp 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 (39) hide show
  1. package/dist/detection/compressor.d.ts +26 -0
  2. package/dist/detection/compressor.js +446 -0
  3. package/dist/detection/concern-map.d.ts +16 -0
  4. package/dist/detection/concern-map.js +236 -0
  5. package/dist/detection/context-extractor.d.ts +39 -0
  6. package/dist/detection/context-extractor.js +390 -0
  7. package/dist/detection/file-tree.d.ts +33 -0
  8. package/dist/detection/file-tree.js +174 -0
  9. package/dist/index.d.ts +10 -0
  10. package/dist/index.js +48 -0
  11. package/dist/prompts/index.d.ts +9 -0
  12. package/dist/prompts/index.js +125 -0
  13. package/dist/resources/identity.d.ts +6 -0
  14. package/dist/resources/identity.js +30 -0
  15. package/dist/resources/index.d.ts +6 -0
  16. package/dist/resources/index.js +14 -0
  17. package/dist/resources/prd.d.ts +6 -0
  18. package/dist/resources/prd.js +30 -0
  19. package/dist/resources/roadmap.d.ts +6 -0
  20. package/dist/resources/roadmap.js +30 -0
  21. package/dist/resources/tokens.d.ts +6 -0
  22. package/dist/resources/tokens.js +30 -0
  23. package/dist/setup.d.ts +11 -0
  24. package/dist/setup.js +429 -0
  25. package/dist/tools/checkin.d.ts +6 -0
  26. package/dist/tools/checkin.js +72 -0
  27. package/dist/tools/complete.d.ts +6 -0
  28. package/dist/tools/complete.js +56 -0
  29. package/dist/tools/get-prompt.d.ts +7 -0
  30. package/dist/tools/get-prompt.js +68 -0
  31. package/dist/tools/get-skill.d.ts +6 -0
  32. package/dist/tools/get-skill.js +57 -0
  33. package/dist/tools/refine-run.d.ts +22 -0
  34. package/dist/tools/refine-run.js +150 -0
  35. package/dist/utils/api.d.ts +12 -0
  36. package/dist/utils/api.js +69 -0
  37. package/dist/utils/config.d.ts +9 -0
  38. package/dist/utils/config.js +24 -0
  39. package/package.json +33 -0
@@ -0,0 +1,236 @@
1
+ /**
2
+ * Concern Map — regras determinísticas de seleção de arquivos por tipo de refine.
3
+ * Zero tokens de LLM. Cada tipo de refine tem patterns genéricos + stack-specific.
4
+ */
5
+ import { glob } from "glob";
6
+ import { IGNORE_DIRS } from "./file-tree.js";
7
+ // ---------------------------------------------------------------------------
8
+ // Ignore dirs (extends file-tree IGNORE_DIRS)
9
+ // ---------------------------------------------------------------------------
10
+ const IGNORE_DIRS_EXTENDED = [
11
+ ...IGNORE_DIRS,
12
+ ".vercel",
13
+ "coverage",
14
+ "test-results",
15
+ "playwright-report",
16
+ ];
17
+ const IGNORE_PATTERN = IGNORE_DIRS_EXTENDED.map((d) => `**/${d}/**`);
18
+ // ---------------------------------------------------------------------------
19
+ // Concern Maps
20
+ // ---------------------------------------------------------------------------
21
+ const CONCERN_MAPS = {
22
+ security: {
23
+ generic: [
24
+ "**/.env*",
25
+ "**/middleware.*",
26
+ "**/auth/**",
27
+ "**/*secret*",
28
+ "**/*token*",
29
+ "**/*password*",
30
+ "**/*api-key*",
31
+ ],
32
+ stackSpecific: {
33
+ convex: ["convex/schema.ts", "convex/auth.ts", "convex/http.ts", "convex/*.ts"],
34
+ supabase: ["**/supabase/**", "**/policies.sql"],
35
+ prisma: ["prisma/schema.prisma"],
36
+ firebase: ["**/firestore.rules", "**/storage.rules"],
37
+ },
38
+ },
39
+ schema: {
40
+ generic: ["**/*schema*", "**/*model*", "**/migration*"],
41
+ stackSpecific: {
42
+ convex: ["convex/schema.ts"],
43
+ prisma: ["prisma/schema.prisma"],
44
+ drizzle: ["**/drizzle/**"],
45
+ supabase: ["**/supabase/migrations/**"],
46
+ },
47
+ },
48
+ "design-system": {
49
+ generic: [
50
+ "**/tailwind.config*",
51
+ "**/globals.css",
52
+ "**/index.css",
53
+ "**/theme*",
54
+ "**/ui/**",
55
+ "**/styleguide*",
56
+ "**/design-system*",
57
+ ],
58
+ stackSpecific: {},
59
+ },
60
+ responsive: {
61
+ generic: [
62
+ "**/tailwind.config*",
63
+ "**/globals.css",
64
+ "**/index.css",
65
+ "**/components/**/*.{tsx,jsx}",
66
+ "**/pages/**/*.{tsx,jsx}",
67
+ ],
68
+ stackSpecific: {},
69
+ },
70
+ "pre-deploy": {
71
+ generic: [
72
+ "package.json",
73
+ "**/.env*",
74
+ "**/config*",
75
+ "tsconfig*",
76
+ "**/vite.config*",
77
+ "**/next.config*",
78
+ "**/vercel.json",
79
+ "**/.vercelignore",
80
+ ],
81
+ stackSpecific: {
82
+ convex: ["convex/*.ts"],
83
+ },
84
+ },
85
+ performance: {
86
+ generic: [
87
+ "package.json",
88
+ "**/vite.config*",
89
+ "**/next.config*",
90
+ "**/pages/**/*.{tsx,jsx}",
91
+ "**/app/**/*.{tsx,jsx}",
92
+ "**/components/**/*.{tsx,jsx}",
93
+ "**/tailwind.config*",
94
+ "**/index.css",
95
+ "**/globals.css",
96
+ ],
97
+ stackSpecific: {
98
+ convex: ["convex/*.ts"],
99
+ },
100
+ },
101
+ "ux-laws": {
102
+ generic: [
103
+ "**/pages/**/*.{tsx,jsx}",
104
+ "**/app/**/*.{tsx,jsx}",
105
+ "**/components/**/*.{tsx,jsx}",
106
+ "**/index.css",
107
+ "**/globals.css",
108
+ ],
109
+ stackSpecific: {},
110
+ },
111
+ consistency: {
112
+ generic: [
113
+ "**/components/**/*.{tsx,jsx}",
114
+ "**/pages/**/*.{tsx,jsx}",
115
+ "**/app/**/*.{tsx,jsx}",
116
+ "**/layout*",
117
+ "**/index.css",
118
+ "**/globals.css",
119
+ ],
120
+ stackSpecific: {},
121
+ },
122
+ "motion-audit": {
123
+ generic: [
124
+ "**/components/**/*.{tsx,jsx}",
125
+ "**/pages/**/*.{tsx,jsx}",
126
+ "**/lib/motion*",
127
+ "**/animations*",
128
+ "**/index.css",
129
+ "**/globals.css",
130
+ ],
131
+ stackSpecific: {},
132
+ },
133
+ "auth-check": {
134
+ generic: ["**/.env*", "**/auth*", "**/login*", "**/signup*", "**/middleware*", "**/session*"],
135
+ stackSpecific: {
136
+ convex: ["convex/auth.ts", "convex/http.ts", "convex/schema.ts", "convex/*.ts"],
137
+ supabase: ["**/supabase/**"],
138
+ firebase: ["**/firestore.rules"],
139
+ },
140
+ },
141
+ "env-check": {
142
+ generic: ["**/.env*", "**/config*", "package.json", "**/vite.config*", "**/next.config*"],
143
+ stackSpecific: {
144
+ convex: ["convex/http.ts"],
145
+ },
146
+ },
147
+ "taste-review": {
148
+ generic: [
149
+ "**/pages/**/*.{tsx,jsx}",
150
+ "**/app/**/*.{tsx,jsx}",
151
+ "**/components/**/*.{tsx,jsx}",
152
+ "**/index.css",
153
+ "**/globals.css",
154
+ ],
155
+ stackSpecific: {},
156
+ },
157
+ "product-check": {
158
+ generic: ["**/pages/**/*.{tsx,jsx}", "**/app/**/*.{tsx,jsx}", "**/components/**/*.{tsx,jsx}"],
159
+ stackSpecific: {},
160
+ },
161
+ "refactor-audit": {
162
+ generic: ["**/*.{ts,tsx,js,jsx}", "package.json", "tsconfig*"],
163
+ stackSpecific: {
164
+ convex: ["convex/*.ts"],
165
+ },
166
+ },
167
+ "pivot-audit": {
168
+ generic: ["**/docs/**", "**/*.md", "**/schema*", "**/prompts/**", "**/lib/**", "package.json"],
169
+ stackSpecific: {
170
+ convex: ["convex/schema.ts", "convex/lib/**"],
171
+ },
172
+ },
173
+ debug: {
174
+ generic: [
175
+ "**/pages/**/*.{tsx,jsx}",
176
+ "**/components/**/*.{tsx,jsx}",
177
+ "**/lib/**/*.{ts,tsx}",
178
+ "**/hooks/**/*.{ts,tsx}",
179
+ "**/*.{test,spec}.{ts,tsx}",
180
+ ],
181
+ stackSpecific: {
182
+ convex: ["convex/*.ts"],
183
+ },
184
+ },
185
+ product: {
186
+ generic: [
187
+ "**/pages/**/*.{tsx,jsx}",
188
+ "**/app/**/*.{tsx,jsx}",
189
+ "**/components/**/*.{tsx,jsx}",
190
+ "**/docs/**",
191
+ "**/*.md",
192
+ ],
193
+ stackSpecific: {},
194
+ },
195
+ placeholders: {
196
+ generic: [
197
+ "**/pages/**/*.{tsx,jsx}",
198
+ "**/components/**/*.{tsx,jsx}",
199
+ "**/lib/**/*.{ts,tsx}",
200
+ "**/hooks/**/*.{ts,tsx}",
201
+ "**/*.{test,spec}.{ts,tsx}",
202
+ ],
203
+ stackSpecific: {
204
+ convex: ["convex/*.ts"],
205
+ },
206
+ },
207
+ };
208
+ // ---------------------------------------------------------------------------
209
+ // Main
210
+ // ---------------------------------------------------------------------------
211
+ /**
212
+ * Seleciona arquivos relevantes para um tipo de refine, baseado em patterns
213
+ * determinísticos (genéricos + stack-specific).
214
+ */
215
+ export async function selectFiles(refineType, stack, cwd) {
216
+ const map = CONCERN_MAPS[refineType];
217
+ // Fallback: for refine types without a specific concern map, scan all source files
218
+ const fallbackPatterns = ["**/*.{ts,tsx,js,jsx}", "**/*.css", "**/package.json"];
219
+ const patterns = map ? [...map.generic, ...(map.stackSpecific[stack] ?? [])] : fallbackPatterns;
220
+ const allFiles = new Set();
221
+ const results = await Promise.all(patterns.map((pattern) => glob(pattern, {
222
+ cwd,
223
+ ignore: IGNORE_PATTERN,
224
+ nodir: true,
225
+ })));
226
+ for (const files of results) {
227
+ for (const file of files) {
228
+ allFiles.add(file);
229
+ }
230
+ }
231
+ return {
232
+ files: [...allFiles].sort(),
233
+ patterns,
234
+ stack,
235
+ };
236
+ }
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Context Extractor — Full Project Context
3
+ * Detecta stack, design tokens, schema, componentes, rotas e outputs anteriores.
4
+ * 100% deterministico, zero tokens de LLM.
5
+ */
6
+ export interface StackInfo {
7
+ framework: "nextjs" | "remix" | "vite" | "astro" | "nuxt" | "unknown";
8
+ database: "convex" | "supabase" | "prisma" | "drizzle" | "firebase" | "django" | "unknown";
9
+ ui: "tailwind" | "css-modules" | "styled-components" | "emotion" | "unknown";
10
+ auth: "convex-auth" | "clerk" | "next-auth" | "supabase-auth" | "firebase-auth" | "unknown";
11
+ }
12
+ export interface DesignTokens {
13
+ colors: Record<string, string>;
14
+ radius: Record<string, string>;
15
+ fonts: Record<string, string>;
16
+ spacing: Record<string, string>;
17
+ other: Record<string, string>;
18
+ }
19
+ export interface SchemaTable {
20
+ name: string;
21
+ fields: Array<{
22
+ name: string;
23
+ type: string;
24
+ }>;
25
+ }
26
+ export interface ComponentInfo {
27
+ name: string;
28
+ path: string;
29
+ }
30
+ export interface ProjectContext {
31
+ stack: StackInfo;
32
+ designTokens: DesignTokens | null;
33
+ schema: SchemaTable[] | null;
34
+ components: ComponentInfo[] | null;
35
+ routes: string[] | null;
36
+ brand: null;
37
+ previousOutputs: Record<string, unknown> | null;
38
+ }
39
+ export declare function extractProjectContext(cwd: string): Promise<ProjectContext>;
@@ -0,0 +1,390 @@
1
+ /**
2
+ * Context Extractor — Full Project Context
3
+ * Detecta stack, design tokens, schema, componentes, rotas e outputs anteriores.
4
+ * 100% deterministico, zero tokens de LLM.
5
+ */
6
+ import { existsSync } from "node:fs";
7
+ import { readdir, readFile } from "node:fs/promises";
8
+ import { basename, extname, join } from "node:path";
9
+ import { glob } from "glob";
10
+ import { IGNORE_DIRS } from "./file-tree.js";
11
+ // ---------------------------------------------------------------------------
12
+ // Helpers
13
+ // ---------------------------------------------------------------------------
14
+ const IGNORE_PATTERN = IGNORE_DIRS.map((d) => `**/${d}/**`);
15
+ async function readFileOrNull(path) {
16
+ if (!existsSync(path))
17
+ return null;
18
+ try {
19
+ return await readFile(path, "utf-8");
20
+ }
21
+ catch {
22
+ return null;
23
+ }
24
+ }
25
+ async function readPackageJson(cwd) {
26
+ const raw = await readFileOrNull(join(cwd, "package.json"));
27
+ if (!raw)
28
+ return { dependencies: {}, devDependencies: {} };
29
+ try {
30
+ const pkg = JSON.parse(raw);
31
+ return {
32
+ dependencies: pkg.dependencies ?? {},
33
+ devDependencies: pkg.devDependencies ?? {},
34
+ };
35
+ }
36
+ catch {
37
+ return { dependencies: {}, devDependencies: {} };
38
+ }
39
+ }
40
+ // ---------------------------------------------------------------------------
41
+ // Stack Detection (TASK-130 — existing)
42
+ // ---------------------------------------------------------------------------
43
+ function detectFramework(deps, devDeps) {
44
+ if ("next" in deps)
45
+ return "nextjs";
46
+ if ("@remix-run/react" in deps)
47
+ return "remix";
48
+ if ("vite" in devDeps)
49
+ return "vite";
50
+ if ("astro" in deps)
51
+ return "astro";
52
+ if ("nuxt" in deps)
53
+ return "nuxt";
54
+ return "unknown";
55
+ }
56
+ function detectDatabase(deps, cwd) {
57
+ if ("convex" in deps)
58
+ return "convex";
59
+ if ("@supabase/ssr" in deps || "@supabase/supabase-js" in deps)
60
+ return "supabase";
61
+ if ("@prisma/client" in deps)
62
+ return "prisma";
63
+ if ("drizzle-orm" in deps)
64
+ return "drizzle";
65
+ if ("firebase" in deps)
66
+ return "firebase";
67
+ if (existsSync(join(cwd, "manage.py")))
68
+ return "django";
69
+ return "unknown";
70
+ }
71
+ async function detectUI(deps, devDeps, cwd) {
72
+ const allDeps = { ...deps, ...devDeps };
73
+ if ("tailwindcss" in allDeps || "@tailwindcss/vite" in allDeps)
74
+ return "tailwind";
75
+ const cssModules = await glob("src/**/*.module.css", {
76
+ cwd,
77
+ ignore: IGNORE_PATTERN,
78
+ maxDepth: 4,
79
+ });
80
+ if (cssModules.length > 0)
81
+ return "css-modules";
82
+ if ("styled-components" in deps)
83
+ return "styled-components";
84
+ if ("@emotion/styled" in deps)
85
+ return "emotion";
86
+ return "unknown";
87
+ }
88
+ function detectAuth(deps, database) {
89
+ if ("@convex-dev/auth" in deps)
90
+ return "convex-auth";
91
+ if ("@clerk/nextjs" in deps || "@clerk/clerk-react" in deps)
92
+ return "clerk";
93
+ if ("next-auth" in deps)
94
+ return "next-auth";
95
+ if ("@supabase/ssr" in deps && database === "supabase")
96
+ return "supabase-auth";
97
+ if ("firebase" in deps && database === "firebase")
98
+ return "firebase-auth";
99
+ return "unknown";
100
+ }
101
+ // ---------------------------------------------------------------------------
102
+ // TASK-131 — Design Tokens Extraction
103
+ // ---------------------------------------------------------------------------
104
+ /**
105
+ * Extrai design tokens de globals.css (CSS custom properties).
106
+ * Categoriza por prefixo: --color-*, --radius-*, --font-*, --space-*.
107
+ */
108
+ async function extractDesignTokens(cwd) {
109
+ const candidates = [
110
+ "src/index.css",
111
+ "src/globals.css",
112
+ "globals.css",
113
+ "app/globals.css",
114
+ "src/app/globals.css",
115
+ ];
116
+ let cssContent = null;
117
+ for (const candidate of candidates) {
118
+ cssContent = await readFileOrNull(join(cwd, candidate));
119
+ if (cssContent)
120
+ break;
121
+ }
122
+ // Also check tailwind.config
123
+ const twConfigPaths = ["tailwind.config.ts", "tailwind.config.js", "tailwind.config.mjs"];
124
+ let twContent = null;
125
+ for (const p of twConfigPaths) {
126
+ twContent = await readFileOrNull(join(cwd, p));
127
+ if (twContent)
128
+ break;
129
+ }
130
+ if (!cssContent && !twContent)
131
+ return null;
132
+ const tokens = {
133
+ colors: {},
134
+ radius: {},
135
+ fonts: {},
136
+ spacing: {},
137
+ other: {},
138
+ };
139
+ // Parse CSS custom properties
140
+ if (cssContent) {
141
+ const varRegex = /^\s*(--[\w-]+)\s*:\s*(.+?)\s*;/gm;
142
+ for (const match of cssContent.matchAll(varRegex)) {
143
+ const [, name, value] = match;
144
+ if (name.startsWith("--color-") ||
145
+ name.startsWith("--bg-") ||
146
+ name.startsWith("--text-") ||
147
+ name.startsWith("--border-")) {
148
+ tokens.colors[name] = value;
149
+ }
150
+ else if (name.startsWith("--radius-") || name.startsWith("--rounded-")) {
151
+ tokens.radius[name] = value;
152
+ }
153
+ else if (name.startsWith("--font-") ||
154
+ name.startsWith("--text-") ||
155
+ name.startsWith("--leading-")) {
156
+ // --text- pode ser cor ou tipografia; se ja adicionou como cor, pular
157
+ if (!tokens.colors[name]) {
158
+ tokens.fonts[name] = value;
159
+ }
160
+ }
161
+ else if (name.startsWith("--space-") ||
162
+ name.startsWith("--gap-") ||
163
+ name.startsWith("--padding-")) {
164
+ tokens.spacing[name] = value;
165
+ }
166
+ else {
167
+ tokens.other[name] = value;
168
+ }
169
+ }
170
+ }
171
+ const hasTokens = Object.keys(tokens.colors).length > 0 ||
172
+ Object.keys(tokens.radius).length > 0 ||
173
+ Object.keys(tokens.fonts).length > 0 ||
174
+ Object.keys(tokens.spacing).length > 0;
175
+ return hasTokens ? tokens : null;
176
+ }
177
+ // ---------------------------------------------------------------------------
178
+ // TASK-132 — Schema Extraction
179
+ // ---------------------------------------------------------------------------
180
+ /**
181
+ * Extrai schema do ORM/DB detectado.
182
+ * Convex: parseia convex/schema.ts (defineTable calls).
183
+ * Prisma: parseia prisma/schema.prisma (model blocks).
184
+ * Supabase/Drizzle: parseia arquivos de schema.
185
+ */
186
+ async function extractSchema(cwd, database) {
187
+ switch (database) {
188
+ case "convex":
189
+ return extractConvexSchema(cwd);
190
+ case "prisma":
191
+ return extractPrismaSchema(cwd);
192
+ case "drizzle":
193
+ return extractDrizzleSchema(cwd);
194
+ default:
195
+ return null;
196
+ }
197
+ }
198
+ async function extractConvexSchema(cwd) {
199
+ const content = await readFileOrNull(join(cwd, "convex/schema.ts"));
200
+ if (!content)
201
+ return null;
202
+ const tables = [];
203
+ // Match: tableName: defineTable({ ... })
204
+ // We capture the table name and the field block
205
+ const tableRegex = /(\w+):\s*defineTable\(\{([^}]*(?:\{[^}]*\}[^}]*)*)\}\)/g;
206
+ for (const tableMatch of content.matchAll(tableRegex)) {
207
+ const [, tableName, fieldsBlock] = tableMatch;
208
+ const fields = [];
209
+ // Match field: v.type() or v.optional(v.type())
210
+ const fieldRegex = /(\w+):\s*(v\.[^,\n]+)/g;
211
+ for (const fieldMatch of fieldsBlock.matchAll(fieldRegex)) {
212
+ const [, fieldName, fieldType] = fieldMatch;
213
+ // Simplify type: v.string() -> string, v.optional(v.string()) -> optional(string)
214
+ const simplified = fieldType
215
+ .replace(/v\./g, "")
216
+ .replace(/\(\)/g, "")
217
+ .replace(/\(/g, "(")
218
+ .replace(/\)/g, ")");
219
+ fields.push({ name: fieldName, type: simplified });
220
+ }
221
+ tables.push({ name: tableName, fields });
222
+ }
223
+ return tables.length > 0 ? tables : null;
224
+ }
225
+ async function extractPrismaSchema(cwd) {
226
+ const content = await readFileOrNull(join(cwd, "prisma/schema.prisma"));
227
+ if (!content)
228
+ return null;
229
+ const tables = [];
230
+ const modelRegex = /model\s+(\w+)\s*\{([^}]+)\}/g;
231
+ for (const modelMatch of content.matchAll(modelRegex)) {
232
+ const [, modelName, body] = modelMatch;
233
+ const fields = [];
234
+ for (const line of body.split("\n")) {
235
+ const trimmed = line.trim();
236
+ if (!trimmed || trimmed.startsWith("//") || trimmed.startsWith("@@"))
237
+ continue;
238
+ const parts = trimmed.split(/\s+/);
239
+ if (parts.length >= 2 && !parts[0].startsWith("@")) {
240
+ fields.push({ name: parts[0], type: parts[1] });
241
+ }
242
+ }
243
+ tables.push({ name: modelName, fields });
244
+ }
245
+ return tables.length > 0 ? tables : null;
246
+ }
247
+ async function extractDrizzleSchema(cwd) {
248
+ const candidates = await glob("**/schema.ts", {
249
+ cwd,
250
+ ignore: [...IGNORE_PATTERN, "convex/**"],
251
+ maxDepth: 3,
252
+ });
253
+ if (candidates.length === 0)
254
+ return null;
255
+ const tables = [];
256
+ for (const candidate of candidates) {
257
+ const content = await readFileOrNull(join(cwd, candidate));
258
+ if (!content)
259
+ continue;
260
+ // Match: export const tableName = pgTable("name", { ... }) or sqliteTable etc.
261
+ const tableRegex = /(?:pgTable|sqliteTable|mysqlTable)\(\s*["'](\w+)["']\s*,\s*\{([^}]+)\}/g;
262
+ for (const match of content.matchAll(tableRegex)) {
263
+ const [, tableName, fieldsBlock] = match;
264
+ const fields = [];
265
+ const fieldRegex = /(\w+):\s*(\w+)\(/g;
266
+ for (const fieldMatch of fieldsBlock.matchAll(fieldRegex)) {
267
+ fields.push({ name: fieldMatch[1], type: fieldMatch[2] });
268
+ }
269
+ tables.push({ name: tableName, fields });
270
+ }
271
+ }
272
+ return tables.length > 0 ? tables : null;
273
+ }
274
+ // ---------------------------------------------------------------------------
275
+ // TASK-133 — Component Listing + Route Listing
276
+ // ---------------------------------------------------------------------------
277
+ /**
278
+ * Lista componentes em src/components/ (name, path).
279
+ */
280
+ async function extractComponents(cwd) {
281
+ const componentFiles = await glob("src/components/**/*.{tsx,jsx}", {
282
+ cwd,
283
+ ignore: [...IGNORE_PATTERN, "**/*.test.*", "**/*.spec.*", "**/*.stories.*"],
284
+ maxDepth: 5,
285
+ });
286
+ if (componentFiles.length === 0)
287
+ return null;
288
+ return componentFiles.map((filePath) => ({
289
+ name: basename(filePath, extname(filePath)),
290
+ path: filePath,
291
+ }));
292
+ }
293
+ /**
294
+ * Lista rotas do projeto baseado no framework detectado.
295
+ * Vite: src/pages, Next.js: app/page ou pages/, Remix: app/routes, Astro: src/pages.
296
+ */
297
+ async function extractRoutes(cwd, framework) {
298
+ let patterns;
299
+ switch (framework) {
300
+ case "nextjs":
301
+ patterns = ["app/**/page.{tsx,jsx,ts,js}", "pages/**/*.{tsx,jsx,ts,js}"];
302
+ break;
303
+ case "remix":
304
+ patterns = ["app/routes/**/*.{tsx,jsx,ts,js}"];
305
+ break;
306
+ case "astro":
307
+ patterns = ["src/pages/**/*.{astro,tsx,jsx,md,mdx}"];
308
+ break;
309
+ default:
310
+ patterns = [
311
+ "src/pages/**/*.{tsx,jsx,ts,js}",
312
+ "src/routes/**/*.{tsx,jsx,ts,js}",
313
+ "src/app/**/*.{tsx,jsx,ts,js}",
314
+ ];
315
+ break;
316
+ }
317
+ const allRoutes = [];
318
+ for (const pattern of patterns) {
319
+ const files = await glob(pattern, {
320
+ cwd,
321
+ ignore: [...IGNORE_PATTERN, "**/_*", "**/components/**"],
322
+ maxDepth: 5,
323
+ });
324
+ allRoutes.push(...files);
325
+ }
326
+ return allRoutes.length > 0 ? allRoutes : null;
327
+ }
328
+ // ---------------------------------------------------------------------------
329
+ // TASK-138 — Pipeline .refine/ (previous outputs)
330
+ // ---------------------------------------------------------------------------
331
+ /**
332
+ * Le outputs de skills anteriores em .refine/ (identity.json, design-system.json, etc).
333
+ * Retorna mapa de skillName -> output JSON.
334
+ */
335
+ async function extractPreviousOutputs(cwd) {
336
+ const refineDir = join(cwd, ".refine");
337
+ if (!existsSync(refineDir))
338
+ return null;
339
+ let entries;
340
+ try {
341
+ const dirEntries = await readdir(refineDir);
342
+ entries = dirEntries.filter((e) => e.endsWith(".json"));
343
+ }
344
+ catch {
345
+ return null;
346
+ }
347
+ if (entries.length === 0)
348
+ return null;
349
+ const outputs = {};
350
+ for (const entry of entries) {
351
+ const raw = await readFileOrNull(join(refineDir, entry));
352
+ if (!raw)
353
+ continue;
354
+ try {
355
+ const skillName = basename(entry, ".json");
356
+ outputs[skillName] = JSON.parse(raw);
357
+ }
358
+ catch {
359
+ // Skip malformed JSON
360
+ }
361
+ }
362
+ return Object.keys(outputs).length > 0 ? outputs : null;
363
+ }
364
+ // ---------------------------------------------------------------------------
365
+ // Main
366
+ // ---------------------------------------------------------------------------
367
+ export async function extractProjectContext(cwd) {
368
+ const { dependencies, devDependencies } = await readPackageJson(cwd);
369
+ const framework = detectFramework(dependencies, devDependencies);
370
+ const database = detectDatabase(dependencies, cwd);
371
+ const ui = await detectUI(dependencies, devDependencies, cwd);
372
+ const auth = detectAuth(dependencies, database);
373
+ // Run all extractions in parallel
374
+ const [designTokens, schema, components, routes, previousOutputs] = await Promise.all([
375
+ extractDesignTokens(cwd),
376
+ extractSchema(cwd, database),
377
+ extractComponents(cwd),
378
+ extractRoutes(cwd, framework),
379
+ extractPreviousOutputs(cwd),
380
+ ]);
381
+ return {
382
+ stack: { framework, database, ui, auth },
383
+ designTokens,
384
+ schema,
385
+ components,
386
+ routes,
387
+ brand: null,
388
+ previousOutputs,
389
+ };
390
+ }
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Scan do file tree do projeto do builder + deteccao de skill por sinais.
3
+ * Modulo puro, sem dependencias externas alem de glob e fs.
4
+ */
5
+ export interface RoadmapPhaseProgress {
6
+ total: number;
7
+ done: number;
8
+ }
9
+ export interface RoadmapTasks {
10
+ total: number;
11
+ done: number;
12
+ tasksDone: string[];
13
+ byPhase: Record<string, RoadmapPhaseProgress>;
14
+ }
15
+ export interface FileTreeSummary {
16
+ pages: string[];
17
+ tests: string[];
18
+ mutations: string[];
19
+ cssVarCount: number;
20
+ colorVarCount: number;
21
+ hasPackageJson: boolean;
22
+ hasConvex: boolean;
23
+ hasTailwind: boolean;
24
+ hasVercelConfig: boolean;
25
+ roadmapTasks: RoadmapTasks | null;
26
+ }
27
+ export declare const IGNORE_DIRS: string[];
28
+ export declare function scanFileTree(cwd: string): Promise<FileTreeSummary>;
29
+ /**
30
+ * Detecta a skill inferida como "mais avancada concluida" baseado nos sinais do file tree.
31
+ * Percorre da prioridade mais alta pra mais baixa.
32
+ */
33
+ export declare function detectSkill(summary: FileTreeSummary): string;