codebrief 1.1.10 → 1.1.11
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 +26 -15
- package/package.json +18 -3
- package/src/ai.js +79 -35
- package/src/analyzer.js +316 -8
- package/src/generator.js +62 -21
package/README.md
CHANGED
|
@@ -325,19 +325,30 @@ Now help me with: ...
|
|
|
325
325
|
|
|
326
326
|
## Detected Stacks
|
|
327
327
|
|
|
328
|
-
| Category | Detected
|
|
329
|
-
| ------------------- |
|
|
330
|
-
| **Frameworks** | Next.js, Remix, SvelteKit, Astro, Nuxt.js, React, Vue.js, Svelte, Angular, NestJS, Express
|
|
331
|
-
| **
|
|
332
|
-
| **CSS** | Tailwind CSS, styled-components, Emotion, SASS
|
|
333
|
-
| **UI Libraries** | shadcn/ui, Material UI, Ant Design, Chakra UI, Mantine
|
|
334
|
-
| **State** | TanStack Query, Zustand, Jotai, Redux Toolkit, MobX
|
|
335
|
-
| **Database/ORM** | Prisma, Drizzle, Mongoose, Supabase, Firebase
|
|
336
|
-
| **
|
|
337
|
-
| **
|
|
338
|
-
| **
|
|
339
|
-
| **
|
|
340
|
-
| **
|
|
328
|
+
| Category | Detected |
|
|
329
|
+
| ------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
330
|
+
| **Frameworks** | Next.js, Remix, SvelteKit, Astro, Nuxt.js, Gatsby, SolidJS, Qwik, Eleventy, React, Vue.js, Svelte, Angular, NestJS, Express, Fastify, Hono, Koa, tRPC, Electron, React Native, Expo |
|
|
331
|
+
| **Languages** | TypeScript, JavaScript, Python, Go, Rust, Java, Kotlin, Ruby, PHP, C# |
|
|
332
|
+
| **CSS** | Tailwind CSS, styled-components, Emotion, SASS/SCSS, Vanilla Extract, UnoCSS |
|
|
333
|
+
| **UI Libraries** | shadcn/ui, Material UI, Ant Design, Chakra UI, Mantine, Headless UI, NextUI, DaisyUI |
|
|
334
|
+
| **State** | TanStack Query, Zustand, Jotai, Redux Toolkit, MobX, Pinia, Vuex, Recoil, Valtio |
|
|
335
|
+
| **Database/ORM** | Prisma, Drizzle, TypeORM, Sequelize, Knex, Mongoose, PostgreSQL, MySQL, SQLite, Supabase, Firebase, Redis, Elasticsearch, DynamoDB |
|
|
336
|
+
| **Auth** | NextAuth.js, Clerk, Supabase Auth, Passport.js, Lucia Auth, JWT |
|
|
337
|
+
| **API / Data** | tRPC, Axios, GraphQL (Apollo), SWR |
|
|
338
|
+
| **Validation** | Zod, Yup, Joi, TypeBox |
|
|
339
|
+
| **Testing** | Vitest, Jest, Playwright, Cypress, React Testing Library, Mocha, AVA |
|
|
340
|
+
| **Bundlers** | Vite, Webpack, esbuild, Rollup, Turbopack, tsup, SWC |
|
|
341
|
+
| **Linting** | ESLint, Biome, Prettier |
|
|
342
|
+
| **Deployment** | Vercel, Netlify, Railway, Fly.io, Render, Google Cloud, Serverless (AWS), Docker, Docker Compose |
|
|
343
|
+
| **Package Manager** | npm, pnpm, yarn, bun, cargo, go modules, pip/poetry, Maven, Gradle, bundler, composer, dotnet/NuGet |
|
|
344
|
+
| **Python** | Django, FastAPI, Flask, Streamlit |
|
|
345
|
+
| **Go** | Gin, Fiber, Echo, Chi |
|
|
346
|
+
| **Rust** | Actix Web, Axum, Rocket, Tauri |
|
|
347
|
+
| **Java/Kotlin** | Spring Boot, Quarkus, Ktor, Android |
|
|
348
|
+
| **Ruby** | Ruby on Rails, Sinatra |
|
|
349
|
+
| **PHP** | Laravel, Symfony, Slim |
|
|
350
|
+
| **C# / .NET** | ASP.NET Core, Blazor |
|
|
351
|
+
| **Monorepo** | pnpm workspaces, Turborepo, Lerna, npm workspaces |
|
|
341
352
|
|
|
342
353
|
---
|
|
343
354
|
|
|
@@ -359,9 +370,9 @@ codebrief/
|
|
|
359
370
|
src/
|
|
360
371
|
index.js ← CLI entry point, argument parsing, orchestration
|
|
361
372
|
scanner.js ← Directory walker (respects .gitignore + .codebriefignore)
|
|
362
|
-
analyzer.js ← Stack detection
|
|
373
|
+
analyzer.js ← Stack detection: 10 languages, 50+ frameworks/tools
|
|
363
374
|
generator.js ← Markdown/MDC generators + section parser for --update
|
|
364
|
-
ai.js ← AI enhancement: file sampling, prompt builder,
|
|
375
|
+
ai.js ← AI enhancement: file sampling, prompt builder, 6 providers
|
|
365
376
|
models.js ← All provider model lists — edit here to update/add models
|
|
366
377
|
package.json
|
|
367
378
|
README.md
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codebrief",
|
|
3
|
-
"version": "1.1.
|
|
4
|
-
"description": "Generate AI context files for your project in seconds",
|
|
3
|
+
"version": "1.1.11",
|
|
4
|
+
"description": "Generate AI context files for your project in seconds — supports 10 languages, 50+ frameworks, Cursor rules & Copilot instructions",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"bin": {
|
|
7
7
|
"codebrief": "src/index.js"
|
|
@@ -13,11 +13,26 @@
|
|
|
13
13
|
"keywords": [
|
|
14
14
|
"ai",
|
|
15
15
|
"cursor",
|
|
16
|
+
"copilot",
|
|
16
17
|
"context",
|
|
17
18
|
"developer-tools",
|
|
18
|
-
"cli"
|
|
19
|
+
"cli",
|
|
20
|
+
"code-analysis",
|
|
21
|
+
"project-context",
|
|
22
|
+
"llm",
|
|
23
|
+
"openai",
|
|
24
|
+
"anthropic",
|
|
25
|
+
"groq",
|
|
26
|
+
"gemini",
|
|
27
|
+
"ollama"
|
|
19
28
|
],
|
|
29
|
+
"author": "Murat Hüdavendigâr Öncü",
|
|
20
30
|
"license": "MIT",
|
|
31
|
+
"repository": {
|
|
32
|
+
"type": "git",
|
|
33
|
+
"url": "https://github.com/murathudavendigar/codebrief"
|
|
34
|
+
},
|
|
35
|
+
"homepage": "https://www.npmjs.com/package/codebrief",
|
|
21
36
|
"files": [
|
|
22
37
|
"src/"
|
|
23
38
|
]
|
package/src/ai.js
CHANGED
|
@@ -117,7 +117,19 @@ function sampleSourceFiles(rootDir, fileTree, charBudget = 32000) {
|
|
|
117
117
|
// Scans all source files for process.env.XXX references
|
|
118
118
|
function extractEnvVars(rootDir, fileTree) {
|
|
119
119
|
const envVars = new Map(); // name → [files]
|
|
120
|
-
const sourceExts = new Set([
|
|
120
|
+
const sourceExts = new Set([
|
|
121
|
+
".js",
|
|
122
|
+
".ts",
|
|
123
|
+
".jsx",
|
|
124
|
+
".tsx",
|
|
125
|
+
".vue",
|
|
126
|
+
".svelte",
|
|
127
|
+
".py",
|
|
128
|
+
".go",
|
|
129
|
+
".rs",
|
|
130
|
+
".rb",
|
|
131
|
+
".php",
|
|
132
|
+
]);
|
|
121
133
|
const envRegex = /process\.env\.([A-Z_][A-Z0-9_]*)/g;
|
|
122
134
|
const dotenvRegex = /^([A-Z_][A-Z0-9_]*)=/gm;
|
|
123
135
|
|
|
@@ -138,9 +150,12 @@ function extractEnvVars(rootDir, fileTree) {
|
|
|
138
150
|
if (varName === "XXX" || varName.length < 3) continue;
|
|
139
151
|
if (!envVars.has(varName)) envVars.set(varName, []);
|
|
140
152
|
const file = entry.path || entry.name;
|
|
141
|
-
if (!envVars.get(varName).includes(file))
|
|
153
|
+
if (!envVars.get(varName).includes(file))
|
|
154
|
+
envVars.get(varName).push(file);
|
|
142
155
|
}
|
|
143
|
-
} catch {
|
|
156
|
+
} catch {
|
|
157
|
+
/* skip */
|
|
158
|
+
}
|
|
144
159
|
}
|
|
145
160
|
|
|
146
161
|
return envVars;
|
|
@@ -173,7 +188,9 @@ function extractExports(rootDir, fileTree) {
|
|
|
173
188
|
exports.push({ file, exports: match[1] || match[0] });
|
|
174
189
|
}
|
|
175
190
|
}
|
|
176
|
-
} catch {
|
|
191
|
+
} catch {
|
|
192
|
+
/* skip */
|
|
193
|
+
}
|
|
177
194
|
}
|
|
178
195
|
|
|
179
196
|
return exports;
|
|
@@ -184,9 +201,7 @@ function buildPrompt(analysis, fileTree, fileSamples, envVars, fileExports) {
|
|
|
184
201
|
const fileList = fileTree
|
|
185
202
|
.slice(0, 120)
|
|
186
203
|
.map((e) =>
|
|
187
|
-
e.type === "dir"
|
|
188
|
-
? ` ${e.path || e.name}/`
|
|
189
|
-
: ` ${e.path || e.name}`,
|
|
204
|
+
e.type === "dir" ? ` ${e.path || e.name}/` : ` ${e.path || e.name}`,
|
|
190
205
|
)
|
|
191
206
|
.join("\n");
|
|
192
207
|
|
|
@@ -199,15 +214,23 @@ function buildPrompt(analysis, fileTree, fileSamples, envVars, fileExports) {
|
|
|
199
214
|
.map(([k, v]) => ` ${k}: ${v}`)
|
|
200
215
|
.join("\n") || " (none)";
|
|
201
216
|
|
|
202
|
-
const envVarsText =
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
217
|
+
const envVarsText =
|
|
218
|
+
envVars.size > 0
|
|
219
|
+
? Array.from(envVars.entries())
|
|
220
|
+
.map(
|
|
221
|
+
([name, files]) =>
|
|
222
|
+
`- \`${name}\` — used in ${files.map((f) => "`" + f + "`").join(", ")}`,
|
|
223
|
+
)
|
|
224
|
+
.join("\n")
|
|
225
|
+
: "(none found)";
|
|
226
|
+
|
|
227
|
+
const exportsText =
|
|
228
|
+
fileExports.length > 0
|
|
229
|
+
? fileExports
|
|
230
|
+
.slice(0, 30)
|
|
231
|
+
.map((e) => `- \`${e.file}\`: ${e.exports}`)
|
|
232
|
+
.join("\n")
|
|
233
|
+
: "(none found)";
|
|
211
234
|
|
|
212
235
|
const systemMessage = `You are a world-class software architect. You read source code and produce extremely precise, file-grounded documentation. You NEVER write generic advice. Every sentence you write must cite a real file path, function name, or pattern visible in the code you are given. If you cannot ground a claim in the actual source, you omit it entirely.`;
|
|
213
236
|
|
|
@@ -350,9 +373,13 @@ async function callGroq(prompt, model) {
|
|
|
350
373
|
" Get a free key in ~30s at https://console.groq.com",
|
|
351
374
|
);
|
|
352
375
|
|
|
353
|
-
const messages =
|
|
354
|
-
|
|
355
|
-
|
|
376
|
+
const messages =
|
|
377
|
+
typeof prompt === "string"
|
|
378
|
+
? [{ role: "user", content: prompt }]
|
|
379
|
+
: [
|
|
380
|
+
{ role: "system", content: prompt.systemMessage },
|
|
381
|
+
{ role: "user", content: prompt.userMessage },
|
|
382
|
+
];
|
|
356
383
|
|
|
357
384
|
const res = await httpsPost(
|
|
358
385
|
"api.groq.com",
|
|
@@ -377,9 +404,13 @@ async function callOpenAI(prompt, model) {
|
|
|
377
404
|
if (!apiKey)
|
|
378
405
|
throw new Error("OPENAI_API_KEY environment variable is not set.");
|
|
379
406
|
|
|
380
|
-
const messages =
|
|
381
|
-
|
|
382
|
-
|
|
407
|
+
const messages =
|
|
408
|
+
typeof prompt === "string"
|
|
409
|
+
? [{ role: "user", content: prompt }]
|
|
410
|
+
: [
|
|
411
|
+
{ role: "system", content: prompt.systemMessage },
|
|
412
|
+
{ role: "user", content: prompt.userMessage },
|
|
413
|
+
];
|
|
383
414
|
|
|
384
415
|
const res = await httpsPost(
|
|
385
416
|
"api.openai.com",
|
|
@@ -404,9 +435,10 @@ async function callAnthropic(prompt, model) {
|
|
|
404
435
|
if (!apiKey)
|
|
405
436
|
throw new Error("ANTHROPIC_API_KEY environment variable is not set.");
|
|
406
437
|
|
|
407
|
-
const messages =
|
|
408
|
-
|
|
409
|
-
|
|
438
|
+
const messages =
|
|
439
|
+
typeof prompt === "string"
|
|
440
|
+
? [{ role: "user", content: prompt }]
|
|
441
|
+
: [{ role: "user", content: prompt.userMessage }];
|
|
410
442
|
|
|
411
443
|
const system = typeof prompt === "string" ? undefined : prompt.systemMessage;
|
|
412
444
|
|
|
@@ -439,9 +471,10 @@ async function callGemini(prompt, model) {
|
|
|
439
471
|
" Get a free key at https://aistudio.google.com/app/apikey",
|
|
440
472
|
);
|
|
441
473
|
|
|
442
|
-
const fullText =
|
|
443
|
-
|
|
444
|
-
|
|
474
|
+
const fullText =
|
|
475
|
+
typeof prompt === "string"
|
|
476
|
+
? prompt
|
|
477
|
+
: `${prompt.systemMessage}\n\n${prompt.userMessage}`;
|
|
445
478
|
|
|
446
479
|
const res = await httpsPost(
|
|
447
480
|
"generativelanguage.googleapis.com",
|
|
@@ -464,9 +497,13 @@ async function callGrok(prompt, model) {
|
|
|
464
497
|
" Get a key at https://console.x.ai",
|
|
465
498
|
);
|
|
466
499
|
|
|
467
|
-
const messages =
|
|
468
|
-
|
|
469
|
-
|
|
500
|
+
const messages =
|
|
501
|
+
typeof prompt === "string"
|
|
502
|
+
? [{ role: "user", content: prompt }]
|
|
503
|
+
: [
|
|
504
|
+
{ role: "system", content: prompt.systemMessage },
|
|
505
|
+
{ role: "user", content: prompt.userMessage },
|
|
506
|
+
];
|
|
470
507
|
|
|
471
508
|
const res = await httpsPost(
|
|
472
509
|
"api.x.ai",
|
|
@@ -489,9 +526,10 @@ async function callOllama(prompt, model) {
|
|
|
489
526
|
model = model || getDefaultModel("ollama");
|
|
490
527
|
// Ollama runs locally on port 11434 — use http
|
|
491
528
|
const http = require("http");
|
|
492
|
-
const fullText =
|
|
493
|
-
|
|
494
|
-
|
|
529
|
+
const fullText =
|
|
530
|
+
typeof prompt === "string"
|
|
531
|
+
? prompt
|
|
532
|
+
: `${prompt.systemMessage}\n\n${prompt.userMessage}`;
|
|
495
533
|
const body = JSON.stringify({ model, prompt: fullText, stream: false });
|
|
496
534
|
|
|
497
535
|
return new Promise((resolve, reject) => {
|
|
@@ -543,7 +581,13 @@ async function enhanceWithAI(analysis, fileTree, rootDir, options = {}) {
|
|
|
543
581
|
const fileSamples = sampleSourceFiles(rootDir, fileTree);
|
|
544
582
|
const envVars = extractEnvVars(rootDir, fileTree);
|
|
545
583
|
const fileExports = extractExports(rootDir, fileTree);
|
|
546
|
-
const prompt = buildPrompt(
|
|
584
|
+
const prompt = buildPrompt(
|
|
585
|
+
analysis,
|
|
586
|
+
fileTree,
|
|
587
|
+
fileSamples,
|
|
588
|
+
envVars,
|
|
589
|
+
fileExports,
|
|
590
|
+
);
|
|
547
591
|
|
|
548
592
|
switch (provider.toLowerCase()) {
|
|
549
593
|
case "groq":
|
package/src/analyzer.js
CHANGED
|
@@ -29,6 +29,12 @@ function analyzeProject(rootDir) {
|
|
|
29
29
|
stateManagement: null,
|
|
30
30
|
database: null,
|
|
31
31
|
deployment: null,
|
|
32
|
+
auth: null,
|
|
33
|
+
api: null,
|
|
34
|
+
bundler: null,
|
|
35
|
+
linter: null,
|
|
36
|
+
formatter: null,
|
|
37
|
+
validation: null,
|
|
32
38
|
extraRules: [],
|
|
33
39
|
isMonorepo: false,
|
|
34
40
|
packages: [],
|
|
@@ -47,12 +53,17 @@ function analyzeProject(rootDir) {
|
|
|
47
53
|
...pkg.devDependencies,
|
|
48
54
|
};
|
|
49
55
|
|
|
50
|
-
// Framework detection
|
|
56
|
+
// Framework detection (order: full-stack first, then SPA, then backend)
|
|
51
57
|
if (deps["next"]) result.type = "Next.js";
|
|
52
58
|
else if (deps["@remix-run/react"]) result.type = "Remix";
|
|
53
59
|
else if (deps["@sveltejs/kit"]) result.type = "SvelteKit";
|
|
54
60
|
else if (deps["astro"]) result.type = "Astro";
|
|
55
61
|
else if (deps["nuxt"]) result.type = "Nuxt.js";
|
|
62
|
+
else if (deps["gatsby"]) result.type = "Gatsby";
|
|
63
|
+
else if (deps["solid-js"]) result.type = "SolidJS";
|
|
64
|
+
else if (deps["@builder.io/qwik"]) result.type = "Qwik";
|
|
65
|
+
else if (deps["@eleventy/eleventy"] || deps["@11ty/eleventy"])
|
|
66
|
+
result.type = "Eleventy";
|
|
56
67
|
else if (deps["react"]) result.type = "React";
|
|
57
68
|
else if (deps["vue"]) result.type = "Vue.js";
|
|
58
69
|
else if (deps["svelte"]) result.type = "Svelte";
|
|
@@ -61,6 +72,11 @@ function analyzeProject(rootDir) {
|
|
|
61
72
|
else if (deps["express"]) result.type = "Express.js API";
|
|
62
73
|
else if (deps["fastify"]) result.type = "Fastify API";
|
|
63
74
|
else if (deps["hono"]) result.type = "Hono API";
|
|
75
|
+
else if (deps["koa"]) result.type = "Koa API";
|
|
76
|
+
else if (deps["@trpc/server"]) result.type = "tRPC API";
|
|
77
|
+
else if (deps["electron"]) result.type = "Electron";
|
|
78
|
+
else if (deps["react-native"]) result.type = "React Native";
|
|
79
|
+
else if (deps["expo"]) result.type = "Expo (React Native)";
|
|
64
80
|
|
|
65
81
|
// Language
|
|
66
82
|
if (deps["typescript"] || fileExists(rootDir, "tsconfig.json")) {
|
|
@@ -80,6 +96,10 @@ function analyzeProject(rootDir) {
|
|
|
80
96
|
result.cssFramework = "Emotion CSS-in-JS";
|
|
81
97
|
} else if (deps["sass"] || deps["node-sass"]) {
|
|
82
98
|
result.cssFramework = "SASS/SCSS";
|
|
99
|
+
} else if (deps["@vanilla-extract/css"]) {
|
|
100
|
+
result.cssFramework = "Vanilla Extract";
|
|
101
|
+
} else if (deps["unocss"]) {
|
|
102
|
+
result.cssFramework = "UnoCSS";
|
|
83
103
|
}
|
|
84
104
|
|
|
85
105
|
// UI Library
|
|
@@ -99,6 +119,12 @@ function analyzeProject(rootDir) {
|
|
|
99
119
|
result.uiLibrary = "Chakra UI";
|
|
100
120
|
} else if (deps["@mantine/core"]) {
|
|
101
121
|
result.uiLibrary = "Mantine";
|
|
122
|
+
} else if (deps["@headlessui/react"]) {
|
|
123
|
+
result.uiLibrary = "Headless UI";
|
|
124
|
+
} else if (deps["@nextui-org/react"]) {
|
|
125
|
+
result.uiLibrary = "NextUI";
|
|
126
|
+
} else if (deps["daisyui"]) {
|
|
127
|
+
result.uiLibrary = "DaisyUI";
|
|
102
128
|
}
|
|
103
129
|
|
|
104
130
|
// State management
|
|
@@ -115,6 +141,14 @@ function analyzeProject(rootDir) {
|
|
|
115
141
|
result.stateManagement = "Redux Toolkit";
|
|
116
142
|
} else if (deps["mobx"]) {
|
|
117
143
|
result.stateManagement = "MobX";
|
|
144
|
+
} else if (deps["pinia"]) {
|
|
145
|
+
result.stateManagement = "Pinia (Vue)";
|
|
146
|
+
} else if (deps["vuex"]) {
|
|
147
|
+
result.stateManagement = "Vuex (Vue)";
|
|
148
|
+
} else if (deps["recoil"]) {
|
|
149
|
+
result.stateManagement = "Recoil";
|
|
150
|
+
} else if (deps["valtio"]) {
|
|
151
|
+
result.stateManagement = "Valtio";
|
|
118
152
|
}
|
|
119
153
|
|
|
120
154
|
// Test framework
|
|
@@ -122,6 +156,10 @@ function analyzeProject(rootDir) {
|
|
|
122
156
|
else if (deps["jest"]) result.testFramework = "Jest";
|
|
123
157
|
else if (deps["@playwright/test"]) result.testFramework = "Playwright";
|
|
124
158
|
else if (deps["cypress"]) result.testFramework = "Cypress";
|
|
159
|
+
else if (deps["@testing-library/react"])
|
|
160
|
+
result.testFramework = "React Testing Library";
|
|
161
|
+
else if (deps["mocha"]) result.testFramework = "Mocha";
|
|
162
|
+
else if (deps["ava"]) result.testFramework = "AVA";
|
|
125
163
|
|
|
126
164
|
// Database / ORM
|
|
127
165
|
if (deps["prisma"] || deps["@prisma/client"]) {
|
|
@@ -137,7 +175,87 @@ function analyzeProject(rootDir) {
|
|
|
137
175
|
result.database = "Supabase";
|
|
138
176
|
} else if (deps["firebase"] || deps["firebase-admin"]) {
|
|
139
177
|
result.database = "Firebase";
|
|
178
|
+
} else if (deps["typeorm"]) {
|
|
179
|
+
result.database = "TypeORM";
|
|
180
|
+
} else if (deps["sequelize"]) {
|
|
181
|
+
result.database = "Sequelize";
|
|
182
|
+
} else if (deps["knex"]) {
|
|
183
|
+
result.database = "Knex.js";
|
|
184
|
+
} else if (deps["pg"] || deps["mysql2"] || deps["better-sqlite3"]) {
|
|
185
|
+
result.database = deps["pg"]
|
|
186
|
+
? "PostgreSQL (pg)"
|
|
187
|
+
: deps["mysql2"]
|
|
188
|
+
? "MySQL"
|
|
189
|
+
: "SQLite";
|
|
190
|
+
} else if (deps["redis"] || deps["ioredis"]) {
|
|
191
|
+
result.database = "Redis";
|
|
192
|
+
} else if (deps["@elastic/elasticsearch"]) {
|
|
193
|
+
result.database = "Elasticsearch";
|
|
194
|
+
} else if (deps["dynamoose"] || deps["@aws-sdk/client-dynamodb"]) {
|
|
195
|
+
result.database = "DynamoDB";
|
|
140
196
|
}
|
|
197
|
+
|
|
198
|
+
// Auth
|
|
199
|
+
if (deps["next-auth"] || deps["@auth/core"]) {
|
|
200
|
+
result.auth = "NextAuth.js (Auth.js)";
|
|
201
|
+
result.extraRules.push(
|
|
202
|
+
"Auth is managed by NextAuth.js. Use getServerSession() on the server.",
|
|
203
|
+
);
|
|
204
|
+
} else if (deps["@clerk/nextjs"] || deps["@clerk/clerk-react"]) {
|
|
205
|
+
result.auth = "Clerk";
|
|
206
|
+
} else if (deps["@supabase/auth-helpers-nextjs"]) {
|
|
207
|
+
result.auth = "Supabase Auth";
|
|
208
|
+
} else if (deps["passport"]) {
|
|
209
|
+
result.auth = "Passport.js";
|
|
210
|
+
} else if (deps["@lucia-auth/lucia"] || deps["lucia"]) {
|
|
211
|
+
result.auth = "Lucia Auth";
|
|
212
|
+
} else if (deps["jsonwebtoken"]) {
|
|
213
|
+
result.auth = "JWT (jsonwebtoken)";
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// API / Data fetching
|
|
217
|
+
if (deps["@trpc/client"] || deps["@trpc/server"]) {
|
|
218
|
+
result.api = "tRPC";
|
|
219
|
+
result.extraRules.push(
|
|
220
|
+
"Use tRPC for all API communication. Never use raw fetch for internal APIs.",
|
|
221
|
+
);
|
|
222
|
+
} else if (deps["axios"]) {
|
|
223
|
+
result.api = "Axios";
|
|
224
|
+
} else if (deps["graphql"] || deps["@apollo/client"]) {
|
|
225
|
+
result.api = "GraphQL" + (deps["@apollo/client"] ? " (Apollo)" : "");
|
|
226
|
+
} else if (deps["swr"]) {
|
|
227
|
+
result.api = "SWR";
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// Validation
|
|
231
|
+
if (deps["zod"]) {
|
|
232
|
+
result.validation = "Zod";
|
|
233
|
+
result.extraRules.push(
|
|
234
|
+
"Use Zod for all input validation and type inference.",
|
|
235
|
+
);
|
|
236
|
+
} else if (deps["yup"]) {
|
|
237
|
+
result.validation = "Yup";
|
|
238
|
+
} else if (deps["joi"]) {
|
|
239
|
+
result.validation = "Joi";
|
|
240
|
+
} else if (deps["@sinclair/typebox"]) {
|
|
241
|
+
result.validation = "TypeBox";
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// Bundler / Build tool
|
|
245
|
+
if (deps["vite"]) result.bundler = "Vite";
|
|
246
|
+
else if (deps["webpack"]) result.bundler = "Webpack";
|
|
247
|
+
else if (deps["esbuild"]) result.bundler = "esbuild";
|
|
248
|
+
else if (deps["rollup"]) result.bundler = "Rollup";
|
|
249
|
+
else if (deps["turbopack"]) result.bundler = "Turbopack";
|
|
250
|
+
else if (deps["tsup"]) result.bundler = "tsup";
|
|
251
|
+
else if (deps["swc"]) result.bundler = "SWC";
|
|
252
|
+
|
|
253
|
+
// Linter / Formatter
|
|
254
|
+
if (deps["eslint"]) result.linter = "ESLint";
|
|
255
|
+
if (deps["@biomejs/biome"]) result.linter = "Biome";
|
|
256
|
+
if (deps["prettier"]) result.formatter = "Prettier";
|
|
257
|
+
if (deps["@biomejs/biome"] && !deps["prettier"])
|
|
258
|
+
result.formatter = "Biome";
|
|
141
259
|
}
|
|
142
260
|
}
|
|
143
261
|
|
|
@@ -157,28 +275,186 @@ function analyzeProject(rootDir) {
|
|
|
157
275
|
fileExists(rootDir, "railway.toml")
|
|
158
276
|
) {
|
|
159
277
|
result.deployment = "Railway";
|
|
278
|
+
} else if (fileExists(rootDir, "fly.toml")) {
|
|
279
|
+
result.deployment = "Fly.io";
|
|
280
|
+
} else if (fileExists(rootDir, "render.yaml")) {
|
|
281
|
+
result.deployment = "Render";
|
|
282
|
+
} else if (
|
|
283
|
+
fileExists(rootDir, "app.yaml") ||
|
|
284
|
+
fileExists(rootDir, "app.yml")
|
|
285
|
+
) {
|
|
286
|
+
result.deployment = "Google Cloud (App Engine)";
|
|
287
|
+
} else if (
|
|
288
|
+
fileExists(rootDir, "serverless.yml") ||
|
|
289
|
+
fileExists(rootDir, "serverless.yaml")
|
|
290
|
+
) {
|
|
291
|
+
result.deployment = "Serverless Framework (AWS)";
|
|
160
292
|
} else if (fileExists(rootDir, "Dockerfile")) {
|
|
161
293
|
result.deployment = "Docker";
|
|
294
|
+
} else if (
|
|
295
|
+
fileExists(rootDir, "docker-compose.yml") ||
|
|
296
|
+
fileExists(rootDir, "docker-compose.yaml")
|
|
297
|
+
) {
|
|
298
|
+
result.deployment = "Docker Compose";
|
|
162
299
|
}
|
|
163
300
|
|
|
164
301
|
// ── Python project ────────────────────────────────────────────
|
|
165
302
|
if (
|
|
166
303
|
fileExists(rootDir, "requirements.txt") ||
|
|
167
|
-
fileExists(rootDir, "pyproject.toml")
|
|
304
|
+
fileExists(rootDir, "pyproject.toml") ||
|
|
305
|
+
fileExists(rootDir, "setup.py") ||
|
|
306
|
+
fileExists(rootDir, "Pipfile")
|
|
168
307
|
) {
|
|
169
308
|
result.language = "Python";
|
|
170
309
|
if (fileExists(rootDir, "manage.py")) result.type = "Django";
|
|
171
|
-
else if (fileExists(rootDir, "main.py")) {
|
|
172
|
-
// try to detect fastapi
|
|
310
|
+
else if (fileExists(rootDir, "main.py") || fileExists(rootDir, "app.py")) {
|
|
173
311
|
try {
|
|
174
|
-
const
|
|
175
|
-
path.join(rootDir, "requirements.txt")
|
|
312
|
+
const reqPath = fileExists(rootDir, "requirements.txt")
|
|
313
|
+
? path.join(rootDir, "requirements.txt")
|
|
314
|
+
: null;
|
|
315
|
+
const pyprojectPath = fileExists(rootDir, "pyproject.toml")
|
|
316
|
+
? path.join(rootDir, "pyproject.toml")
|
|
317
|
+
: null;
|
|
318
|
+
const content = reqPath
|
|
319
|
+
? fs.readFileSync(reqPath, "utf-8")
|
|
320
|
+
: pyprojectPath
|
|
321
|
+
? fs.readFileSync(pyprojectPath, "utf-8")
|
|
322
|
+
: "";
|
|
323
|
+
if (content.includes("fastapi")) result.type = "FastAPI";
|
|
324
|
+
else if (content.includes("flask")) result.type = "Flask";
|
|
325
|
+
else if (content.includes("streamlit")) result.type = "Streamlit";
|
|
326
|
+
} catch {}
|
|
327
|
+
}
|
|
328
|
+
if (fileExists(rootDir, "pyproject.toml"))
|
|
329
|
+
result.packageManager = "pip/poetry";
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
// ── Go project ──────────────────────────────────────────────
|
|
333
|
+
if (fileExists(rootDir, "go.mod")) {
|
|
334
|
+
result.language = "Go";
|
|
335
|
+
result.packageManager = "go modules";
|
|
336
|
+
try {
|
|
337
|
+
const goMod = fs.readFileSync(path.join(rootDir, "go.mod"), "utf-8");
|
|
338
|
+
if (goMod.includes("gin-gonic/gin")) result.type = "Gin (Go)";
|
|
339
|
+
else if (goMod.includes("gofiber/fiber")) result.type = "Fiber (Go)";
|
|
340
|
+
else if (goMod.includes("labstack/echo")) result.type = "Echo (Go)";
|
|
341
|
+
else if (goMod.includes("go-chi/chi")) result.type = "Chi (Go)";
|
|
342
|
+
else result.type = "Go application";
|
|
343
|
+
} catch {
|
|
344
|
+
result.type = "Go application";
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
// ── Rust project ────────────────────────────────────────────
|
|
349
|
+
if (fileExists(rootDir, "Cargo.toml")) {
|
|
350
|
+
result.language = "Rust";
|
|
351
|
+
result.packageManager = "cargo";
|
|
352
|
+
try {
|
|
353
|
+
const cargo = fs.readFileSync(path.join(rootDir, "Cargo.toml"), "utf-8");
|
|
354
|
+
if (cargo.includes("actix-web")) result.type = "Actix Web (Rust)";
|
|
355
|
+
else if (cargo.includes("axum")) result.type = "Axum (Rust)";
|
|
356
|
+
else if (cargo.includes("rocket")) result.type = "Rocket (Rust)";
|
|
357
|
+
else if (cargo.includes("tauri")) result.type = "Tauri (Rust)";
|
|
358
|
+
else result.type = "Rust application";
|
|
359
|
+
} catch {
|
|
360
|
+
result.type = "Rust application";
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
// ── Java / Kotlin project ───────────────────────────────────
|
|
365
|
+
if (
|
|
366
|
+
fileExists(rootDir, "pom.xml") ||
|
|
367
|
+
fileExists(rootDir, "build.gradle") ||
|
|
368
|
+
fileExists(rootDir, "build.gradle.kts")
|
|
369
|
+
) {
|
|
370
|
+
result.language = fileExists(rootDir, "build.gradle.kts")
|
|
371
|
+
? "Kotlin"
|
|
372
|
+
: "Java";
|
|
373
|
+
result.packageManager = fileExists(rootDir, "pom.xml") ? "Maven" : "Gradle";
|
|
374
|
+
if (fileExists(rootDir, "pom.xml")) {
|
|
375
|
+
try {
|
|
376
|
+
const pom = fs.readFileSync(path.join(rootDir, "pom.xml"), "utf-8");
|
|
377
|
+
if (pom.includes("spring-boot")) result.type = "Spring Boot";
|
|
378
|
+
else if (pom.includes("quarkus")) result.type = "Quarkus";
|
|
379
|
+
} catch {}
|
|
380
|
+
} else {
|
|
381
|
+
try {
|
|
382
|
+
const gradle = fs.readFileSync(
|
|
383
|
+
path.join(
|
|
384
|
+
rootDir,
|
|
385
|
+
fileExists(rootDir, "build.gradle.kts")
|
|
386
|
+
? "build.gradle.kts"
|
|
387
|
+
: "build.gradle",
|
|
388
|
+
),
|
|
176
389
|
"utf-8",
|
|
177
390
|
);
|
|
178
|
-
if (
|
|
179
|
-
else if (
|
|
391
|
+
if (gradle.includes("spring-boot")) result.type = "Spring Boot";
|
|
392
|
+
else if (gradle.includes("ktor")) result.type = "Ktor (Kotlin)";
|
|
393
|
+
else if (gradle.includes("android")) result.type = "Android";
|
|
180
394
|
} catch {}
|
|
181
395
|
}
|
|
396
|
+
if (result.type === "unknown")
|
|
397
|
+
result.type = `${result.language} application`;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
// ── Ruby project ────────────────────────────────────────────
|
|
401
|
+
if (fileExists(rootDir, "Gemfile")) {
|
|
402
|
+
result.language = "Ruby";
|
|
403
|
+
result.packageManager = "bundler";
|
|
404
|
+
try {
|
|
405
|
+
const gemfile = fs.readFileSync(path.join(rootDir, "Gemfile"), "utf-8");
|
|
406
|
+
if (gemfile.includes("rails")) result.type = "Ruby on Rails";
|
|
407
|
+
else if (gemfile.includes("sinatra")) result.type = "Sinatra";
|
|
408
|
+
else result.type = "Ruby application";
|
|
409
|
+
} catch {
|
|
410
|
+
result.type = "Ruby application";
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
// ── PHP project ─────────────────────────────────────────────
|
|
415
|
+
if (fileExists(rootDir, "composer.json")) {
|
|
416
|
+
result.language = "PHP";
|
|
417
|
+
result.packageManager = "composer";
|
|
418
|
+
try {
|
|
419
|
+
const composer = readJson(path.join(rootDir, "composer.json"));
|
|
420
|
+
const req = {
|
|
421
|
+
...(composer?.require || {}),
|
|
422
|
+
...(composer?.["require-dev"] || {}),
|
|
423
|
+
};
|
|
424
|
+
if (req["laravel/framework"]) result.type = "Laravel";
|
|
425
|
+
else if (req["symfony/framework-bundle"]) result.type = "Symfony";
|
|
426
|
+
else if (req["slim/slim"]) result.type = "Slim (PHP)";
|
|
427
|
+
else result.type = "PHP application";
|
|
428
|
+
} catch {
|
|
429
|
+
result.type = "PHP application";
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
// ── C# / .NET project ──────────────────────────────────────
|
|
434
|
+
const csprojFiles = (() => {
|
|
435
|
+
try {
|
|
436
|
+
return fs
|
|
437
|
+
.readdirSync(rootDir)
|
|
438
|
+
.filter((f) => f.endsWith(".csproj") || f.endsWith(".sln"));
|
|
439
|
+
} catch {
|
|
440
|
+
return [];
|
|
441
|
+
}
|
|
442
|
+
})();
|
|
443
|
+
if (csprojFiles.length > 0) {
|
|
444
|
+
result.language = "C#";
|
|
445
|
+
result.packageManager = "dotnet / NuGet";
|
|
446
|
+
if (fileExists(rootDir, "Program.cs")) {
|
|
447
|
+
try {
|
|
448
|
+
const prog = fs.readFileSync(path.join(rootDir, "Program.cs"), "utf-8");
|
|
449
|
+
if (prog.includes("WebApplication")) result.type = "ASP.NET Core";
|
|
450
|
+
else if (prog.includes("Blazor")) result.type = "Blazor";
|
|
451
|
+
else result.type = ".NET application";
|
|
452
|
+
} catch {
|
|
453
|
+
result.type = ".NET application";
|
|
454
|
+
}
|
|
455
|
+
} else {
|
|
456
|
+
result.type = ".NET application";
|
|
457
|
+
}
|
|
182
458
|
}
|
|
183
459
|
|
|
184
460
|
// ── Folder conventions ────────────────────────────────────────
|
|
@@ -212,6 +488,32 @@ function analyzeProject(rootDir) {
|
|
|
212
488
|
result.conventions.push("Middleware → /middleware");
|
|
213
489
|
if (topDirs.includes("prisma"))
|
|
214
490
|
result.conventions.push("Database schema → /prisma");
|
|
491
|
+
if (topDirs.includes("config"))
|
|
492
|
+
result.conventions.push("Configuration files → /config");
|
|
493
|
+
if (topDirs.includes("constants"))
|
|
494
|
+
result.conventions.push("Constants & enums → /constants");
|
|
495
|
+
if (topDirs.includes("layouts"))
|
|
496
|
+
result.conventions.push("Page layouts → /layouts");
|
|
497
|
+
if (topDirs.includes("features"))
|
|
498
|
+
result.conventions.push("Feature-based modules → /features");
|
|
499
|
+
if (topDirs.includes("modules"))
|
|
500
|
+
result.conventions.push("Domain modules → /modules");
|
|
501
|
+
if (topDirs.includes("pages"))
|
|
502
|
+
result.conventions.push("Page components → /pages");
|
|
503
|
+
if (topDirs.includes("routes"))
|
|
504
|
+
result.conventions.push("Route definitions → /routes");
|
|
505
|
+
if (topDirs.includes("controllers"))
|
|
506
|
+
result.conventions.push("Controllers → /controllers");
|
|
507
|
+
if (topDirs.includes("models"))
|
|
508
|
+
result.conventions.push("Data models → /models");
|
|
509
|
+
if (topDirs.includes("helpers"))
|
|
510
|
+
result.conventions.push("Helper functions → /helpers");
|
|
511
|
+
if (topDirs.includes("tests") || topDirs.includes("__tests__"))
|
|
512
|
+
result.conventions.push("Tests → /tests");
|
|
513
|
+
if (topDirs.includes("public") || topDirs.includes("static"))
|
|
514
|
+
result.conventions.push("Static assets → /public");
|
|
515
|
+
if (topDirs.includes("assets"))
|
|
516
|
+
result.conventions.push("Assets (images, fonts) → /assets");
|
|
215
517
|
|
|
216
518
|
// ── Monorepo detection ──────────────────────────────────────
|
|
217
519
|
const pkgJsonMonorepo = (() => {
|
|
@@ -256,8 +558,14 @@ function analyzeProject(rootDir) {
|
|
|
256
558
|
result.cssFramework,
|
|
257
559
|
result.uiLibrary,
|
|
258
560
|
result.stateManagement,
|
|
561
|
+
result.auth,
|
|
562
|
+
result.api,
|
|
563
|
+
result.validation,
|
|
259
564
|
result.testFramework,
|
|
260
565
|
result.database,
|
|
566
|
+
result.bundler,
|
|
567
|
+
result.linter ? `Linter: ${result.linter}` : null,
|
|
568
|
+
result.formatter ? `Formatter: ${result.formatter}` : null,
|
|
261
569
|
result.deployment ? `Deployed on ${result.deployment}` : null,
|
|
262
570
|
].filter(Boolean);
|
|
263
571
|
|
package/src/generator.js
CHANGED
|
@@ -57,7 +57,9 @@ function generateContextMd(analysis, fileTree, preserved = null) {
|
|
|
57
57
|
const scriptLines =
|
|
58
58
|
Object.entries(analysis.scripts).length > 0
|
|
59
59
|
? Object.entries(analysis.scripts)
|
|
60
|
-
.map(
|
|
60
|
+
.map(
|
|
61
|
+
([k, v]) => `- \`${analysis.packageManager} run ${k}\` → \`${v}\``,
|
|
62
|
+
)
|
|
61
63
|
.join("\n")
|
|
62
64
|
: "- No scripts found";
|
|
63
65
|
|
|
@@ -66,23 +68,46 @@ function generateContextMd(analysis, fileTree, preserved = null) {
|
|
|
66
68
|
? analysis.extraRules.map((r) => `- ${r}`).join("\n")
|
|
67
69
|
: "- No auto-detected rules";
|
|
68
70
|
|
|
71
|
+
// Key info section (only show detected items)
|
|
72
|
+
const keyInfo = [
|
|
73
|
+
analysis.auth ? `- **Auth**: ${analysis.auth}` : null,
|
|
74
|
+
analysis.api ? `- **API**: ${analysis.api}` : null,
|
|
75
|
+
analysis.validation ? `- **Validation**: ${analysis.validation}` : null,
|
|
76
|
+
analysis.bundler ? `- **Bundler**: ${analysis.bundler}` : null,
|
|
77
|
+
analysis.linter ? `- **Linter**: ${analysis.linter}` : null,
|
|
78
|
+
analysis.formatter ? `- **Formatter**: ${analysis.formatter}` : null,
|
|
79
|
+
]
|
|
80
|
+
.filter(Boolean)
|
|
81
|
+
.join("\n");
|
|
82
|
+
|
|
83
|
+
const keyInfoSection = keyInfo
|
|
84
|
+
? `\n## 🔧 Tooling & Libraries\n${keyInfo}\n`
|
|
85
|
+
: "";
|
|
86
|
+
|
|
69
87
|
const archSection = preserved?.architectureNotes
|
|
70
88
|
? preserved.architectureNotes
|
|
71
|
-
: `<!-- FILL THIS IN — this is the
|
|
72
|
-
<!--
|
|
73
|
-
-
|
|
74
|
-
-
|
|
75
|
-
-
|
|
76
|
-
-
|
|
89
|
+
: `<!-- FILL THIS IN — this is the MOST VALUABLE part of this file -->
|
|
90
|
+
<!-- Describe your app in 5–15 bullet points. Think about:
|
|
91
|
+
- How does data flow? (request → controller → service → DB → response)
|
|
92
|
+
- Where does auth live? How are sessions/tokens handled?
|
|
93
|
+
- What patterns are used? (repository pattern, factory, etc.)
|
|
94
|
+
- What are the main entry points?
|
|
95
|
+
- What would surprise a new developer?
|
|
96
|
+
Example:
|
|
97
|
+
- "Auth is handled by NextAuth.js. Session available via getServerSession()."
|
|
98
|
+
- "All API calls route through /lib/api.ts — never use fetch() directly."
|
|
99
|
+
- "We use the repository pattern. Data access only through /services."
|
|
77
100
|
-->`;
|
|
78
101
|
|
|
79
102
|
const neverSection = preserved?.neverDo
|
|
80
103
|
? preserved.neverDo
|
|
81
|
-
: `<!-- Add
|
|
82
|
-
<!--
|
|
83
|
-
|
|
84
|
-
- "Never
|
|
85
|
-
- "Never
|
|
104
|
+
: `<!-- Add 5–10 rules the AI must NEVER break in this project -->
|
|
105
|
+
<!-- Be specific to YOUR codebase, not generic advice.
|
|
106
|
+
Example:
|
|
107
|
+
- "Never use class components — we only use function components with hooks"
|
|
108
|
+
- "Never commit .env files — all secrets are in Vercel env settings"
|
|
109
|
+
- "Never use useState for server data — use TanStack Query"
|
|
110
|
+
- "Never import from @/utils/old — that directory is deprecated"
|
|
86
111
|
-->`;
|
|
87
112
|
|
|
88
113
|
const monorepoSection =
|
|
@@ -92,13 +117,13 @@ function generateContextMd(analysis, fileTree, preserved = null) {
|
|
|
92
117
|
|
|
93
118
|
return `# Project Context: ${analysis.name}
|
|
94
119
|
> Auto-generated by **codebrief** on ${now}
|
|
95
|
-
> Edit this file freely — especially the Architecture Notes
|
|
120
|
+
> Edit this file freely — especially the Architecture Notes and Never Do sections.
|
|
96
121
|
|
|
97
122
|
---
|
|
98
123
|
|
|
99
124
|
## 🧱 Tech Stack
|
|
100
125
|
${stackLines}
|
|
101
|
-
${monorepoSection}
|
|
126
|
+
${monorepoSection}${keyInfoSection}
|
|
102
127
|
## 📁 Folder Conventions
|
|
103
128
|
${conventionLines}
|
|
104
129
|
|
|
@@ -120,7 +145,7 @@ ${rulesLines}
|
|
|
120
145
|
${neverSection}
|
|
121
146
|
|
|
122
147
|
---
|
|
123
|
-
*Re-run \`codebrief\` after major refactors
|
|
148
|
+
*Auto-generated by [codebrief](https://www.npmjs.com/package/codebrief). Re-run \`codebrief --update\` after major refactors.*
|
|
124
149
|
`;
|
|
125
150
|
}
|
|
126
151
|
|
|
@@ -128,19 +153,27 @@ function generateCursorRule(analysis) {
|
|
|
128
153
|
const stackLines = analysis.stack.map((s) => `- ${s}`).join("\n");
|
|
129
154
|
const rulesLines = analysis.extraRules.map((r) => `- ${r}`).join("\n");
|
|
130
155
|
|
|
156
|
+
const authLine = analysis.auth
|
|
157
|
+
? `\n## Auth\n- Authentication: ${analysis.auth}\n`
|
|
158
|
+
: "";
|
|
159
|
+
const dbLine = analysis.database
|
|
160
|
+
? `\n## Database\n- ${analysis.database}\n`
|
|
161
|
+
: "";
|
|
162
|
+
|
|
131
163
|
return `---
|
|
132
164
|
description: Auto-generated project rules for ${analysis.name}
|
|
133
165
|
alwaysApply: true
|
|
134
166
|
---
|
|
135
167
|
|
|
136
168
|
# ${analysis.name} — Cursor Project Rules
|
|
169
|
+
> Auto-generated by [codebrief](https://www.npmjs.com/package/codebrief)
|
|
137
170
|
|
|
138
171
|
## Tech Stack
|
|
139
172
|
${stackLines}
|
|
140
173
|
|
|
141
174
|
## Package Manager
|
|
142
175
|
Always use \`${analysis.packageManager}\`. Never suggest a different package manager.
|
|
143
|
-
|
|
176
|
+
${authLine}${dbLine}
|
|
144
177
|
## Code Conventions
|
|
145
178
|
${rulesLines || "- Follow existing patterns in the codebase"}
|
|
146
179
|
|
|
@@ -151,27 +184,35 @@ ${analysis.conventions.map((c) => `- ${c}`).join("\n") || "- See CONTEXT.md for
|
|
|
151
184
|
- Read CONTEXT.md in the project root for full architecture context
|
|
152
185
|
- Never modify files in node_modules, dist, or build directories
|
|
153
186
|
- Ask before adding new dependencies
|
|
187
|
+
- Match the existing code style and patterns
|
|
154
188
|
`;
|
|
155
189
|
}
|
|
156
190
|
|
|
157
191
|
function generateVSCodeInstructions(analysis) {
|
|
158
|
-
// For VS Code / GitHub Copilot users (.github/copilot-instructions.md)
|
|
159
192
|
const stackLines = analysis.stack.join(", ");
|
|
193
|
+
const authLine = analysis.auth ? `\n## Auth\n- ${analysis.auth}\n` : "";
|
|
194
|
+
const dbLine = analysis.database
|
|
195
|
+
? `\n## Database\n- ${analysis.database}\n`
|
|
196
|
+
: "";
|
|
197
|
+
|
|
160
198
|
return `# Copilot Instructions: ${analysis.name}
|
|
199
|
+
> Auto-generated by [codebrief](https://www.npmjs.com/package/codebrief)
|
|
161
200
|
|
|
162
201
|
## Stack
|
|
163
202
|
${stackLines}
|
|
164
203
|
|
|
165
204
|
## Package Manager
|
|
166
205
|
Use \`${analysis.packageManager}\` for all commands.
|
|
167
|
-
|
|
206
|
+
${authLine}${dbLine}
|
|
168
207
|
## Conventions
|
|
169
|
-
${analysis.conventions.map((c) => `- ${c}`).join("\n")}
|
|
208
|
+
${analysis.conventions.map((c) => `- ${c}`).join("\n") || "- No conventions detected"}
|
|
170
209
|
|
|
171
210
|
## Rules
|
|
172
|
-
${analysis.extraRules.map((r) => `- ${r}`).join("\n")}
|
|
211
|
+
${analysis.extraRules.map((r) => `- ${r}`).join("\n") || "- Follow existing patterns"}
|
|
173
212
|
|
|
174
|
-
|
|
213
|
+
## Important
|
|
214
|
+
- Read CONTEXT.md in the project root for full architecture context
|
|
215
|
+
- Match the existing code style and patterns
|
|
175
216
|
`;
|
|
176
217
|
}
|
|
177
218
|
|