codebrief 1.1.9 → 1.1.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/package.json +1 -1
- package/src/ai.js +216 -69
package/package.json
CHANGED
package/src/ai.js
CHANGED
|
@@ -11,31 +11,57 @@ const IMPORTANT_PATTERNS = [
|
|
|
11
11
|
"package.json",
|
|
12
12
|
"tsconfig.json",
|
|
13
13
|
"next.config.*",
|
|
14
|
+
"nuxt.config.*",
|
|
14
15
|
"vite.config.*",
|
|
16
|
+
"webpack.config.*",
|
|
15
17
|
"tailwind.config.*",
|
|
18
|
+
"postcss.config.*",
|
|
16
19
|
"prisma/schema.prisma",
|
|
17
20
|
"drizzle.config.*",
|
|
18
|
-
"
|
|
21
|
+
".env.example",
|
|
22
|
+
"docker-compose.*",
|
|
23
|
+
"Dockerfile",
|
|
19
24
|
"src/index.*",
|
|
20
25
|
"src/main.*",
|
|
21
26
|
"src/app.*",
|
|
22
27
|
"src/server.*",
|
|
28
|
+
"src/config.*",
|
|
29
|
+
"src/routes.*",
|
|
30
|
+
"src/middleware.*",
|
|
23
31
|
"lib/db.*",
|
|
24
32
|
"lib/auth.*",
|
|
33
|
+
"lib/utils.*",
|
|
25
34
|
"app/layout.*",
|
|
26
35
|
"app/page.*",
|
|
36
|
+
"app/api/**/route.*",
|
|
37
|
+
"pages/_app.*",
|
|
38
|
+
"pages/index.*",
|
|
39
|
+
"server/index.*",
|
|
40
|
+
"server/api/**",
|
|
41
|
+
"controllers/*",
|
|
42
|
+
"models/*",
|
|
43
|
+
"services/*",
|
|
27
44
|
];
|
|
28
45
|
|
|
29
|
-
function sampleSourceFiles(rootDir, fileTree, charBudget =
|
|
46
|
+
function sampleSourceFiles(rootDir, fileTree, charBudget = 32000) {
|
|
30
47
|
const samples = [];
|
|
31
48
|
let budget = charBudget;
|
|
32
49
|
|
|
33
|
-
//
|
|
50
|
+
// Smart read: for large files, take head + tail to capture imports AND exports/env vars
|
|
51
|
+
function smartRead(filePath, maxChars) {
|
|
52
|
+
const raw = fs.readFileSync(filePath, "utf-8");
|
|
53
|
+
if (raw.length <= maxChars) return raw;
|
|
54
|
+
const head = Math.floor(maxChars * 0.6);
|
|
55
|
+
const tail = maxChars - head - 20; // 20 for separator
|
|
56
|
+
return raw.slice(0, head) + "\n// ... (truncated) ...\n" + raw.slice(-tail);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// First pass: prioritised files (generous budget — these are the most important)
|
|
34
60
|
for (const pattern of IMPORTANT_PATTERNS) {
|
|
35
61
|
const full = path.join(rootDir, pattern);
|
|
36
62
|
if (fs.existsSync(full)) {
|
|
37
63
|
try {
|
|
38
|
-
const content =
|
|
64
|
+
const content = smartRead(full, 6000);
|
|
39
65
|
samples.push({ file: pattern, content });
|
|
40
66
|
budget -= content.length;
|
|
41
67
|
if (budget <= 0) break;
|
|
@@ -68,18 +94,17 @@ function sampleSourceFiles(rootDir, fileTree, charBudget = 18000) {
|
|
|
68
94
|
const ext = path.extname(entry.name).toLowerCase();
|
|
69
95
|
if (!sourceExts.has(ext)) continue;
|
|
70
96
|
|
|
71
|
-
const full = path.join(rootDir, entry.
|
|
97
|
+
const full = path.join(rootDir, entry.path || entry.name);
|
|
72
98
|
// Skip already-read files
|
|
73
99
|
if (samples.some((s) => full.endsWith(s.file))) continue;
|
|
74
100
|
|
|
75
101
|
try {
|
|
76
|
-
const
|
|
77
|
-
const snippet = raw.slice(0, 1500);
|
|
102
|
+
const content = smartRead(full, 4000);
|
|
78
103
|
samples.push({
|
|
79
|
-
file: entry.
|
|
80
|
-
content
|
|
104
|
+
file: entry.path || entry.name,
|
|
105
|
+
content,
|
|
81
106
|
});
|
|
82
|
-
budget -=
|
|
107
|
+
budget -= content.length;
|
|
83
108
|
} catch {
|
|
84
109
|
/* skip */
|
|
85
110
|
}
|
|
@@ -88,14 +113,80 @@ function sampleSourceFiles(rootDir, fileTree, charBudget = 18000) {
|
|
|
88
113
|
return samples;
|
|
89
114
|
}
|
|
90
115
|
|
|
116
|
+
// ── Env var extractor ────────────────────────────────────────
|
|
117
|
+
// Scans all source files for process.env.XXX references
|
|
118
|
+
function extractEnvVars(rootDir, fileTree) {
|
|
119
|
+
const envVars = new Map(); // name → [files]
|
|
120
|
+
const sourceExts = new Set([".js", ".ts", ".jsx", ".tsx", ".vue", ".svelte", ".py", ".go", ".rs", ".rb", ".php"]);
|
|
121
|
+
const envRegex = /process\.env\.([A-Z_][A-Z0-9_]*)/g;
|
|
122
|
+
const dotenvRegex = /^([A-Z_][A-Z0-9_]*)=/gm;
|
|
123
|
+
|
|
124
|
+
for (const entry of fileTree) {
|
|
125
|
+
if (entry.type !== "file") continue;
|
|
126
|
+
const ext = path.extname(entry.name).toLowerCase();
|
|
127
|
+
const name = entry.name.toLowerCase();
|
|
128
|
+
if (!sourceExts.has(ext) && !name.startsWith(".env")) continue;
|
|
129
|
+
|
|
130
|
+
const full = path.join(rootDir, entry.path || entry.name);
|
|
131
|
+
try {
|
|
132
|
+
const raw = fs.readFileSync(full, "utf-8");
|
|
133
|
+
const regex = name.startsWith(".env") ? dotenvRegex : envRegex;
|
|
134
|
+
let match;
|
|
135
|
+
while ((match = regex.exec(raw)) !== null) {
|
|
136
|
+
const varName = match[1];
|
|
137
|
+
// Skip generic placeholder names
|
|
138
|
+
if (varName === "XXX" || varName.length < 3) continue;
|
|
139
|
+
if (!envVars.has(varName)) envVars.set(varName, []);
|
|
140
|
+
const file = entry.path || entry.name;
|
|
141
|
+
if (!envVars.get(varName).includes(file)) envVars.get(varName).push(file);
|
|
142
|
+
}
|
|
143
|
+
} catch { /* skip */ }
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return envVars;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// ── Export extractor ─────────────────────────────────────────
|
|
150
|
+
// Scans all source files for key function/class exports
|
|
151
|
+
function extractExports(rootDir, fileTree) {
|
|
152
|
+
const exports = [];
|
|
153
|
+
const sourceExts = new Set([".js", ".ts", ".jsx", ".tsx"]);
|
|
154
|
+
const patterns = [
|
|
155
|
+
/(?:module\.exports\s*=\s*\{([^}]+)\})/g,
|
|
156
|
+
/(?:exports\.(\w+)\s*=)/g,
|
|
157
|
+
/(?:export\s+(?:default\s+)?(?:function|class|const|let|var)\s+(\w+))/g,
|
|
158
|
+
];
|
|
159
|
+
|
|
160
|
+
for (const entry of fileTree) {
|
|
161
|
+
if (entry.type !== "file") continue;
|
|
162
|
+
const ext = path.extname(entry.name).toLowerCase();
|
|
163
|
+
if (!sourceExts.has(ext)) continue;
|
|
164
|
+
|
|
165
|
+
const full = path.join(rootDir, entry.path || entry.name);
|
|
166
|
+
try {
|
|
167
|
+
const raw = fs.readFileSync(full, "utf-8");
|
|
168
|
+
const file = entry.path || entry.name;
|
|
169
|
+
for (const regex of patterns) {
|
|
170
|
+
let match;
|
|
171
|
+
regex.lastIndex = 0;
|
|
172
|
+
while ((match = regex.exec(raw)) !== null) {
|
|
173
|
+
exports.push({ file, exports: match[1] || match[0] });
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
} catch { /* skip */ }
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
return exports;
|
|
180
|
+
}
|
|
181
|
+
|
|
91
182
|
// ── Prompt builder ───────────────────────────────────────────
|
|
92
|
-
function buildPrompt(analysis, fileTree, fileSamples) {
|
|
183
|
+
function buildPrompt(analysis, fileTree, fileSamples, envVars, fileExports) {
|
|
93
184
|
const fileList = fileTree
|
|
94
|
-
.slice(0,
|
|
185
|
+
.slice(0, 120)
|
|
95
186
|
.map((e) =>
|
|
96
187
|
e.type === "dir"
|
|
97
|
-
? ` ${e.
|
|
98
|
-
: ` ${e.
|
|
188
|
+
? ` ${e.path || e.name}/`
|
|
189
|
+
: ` ${e.path || e.name}`,
|
|
99
190
|
)
|
|
100
191
|
.join("\n");
|
|
101
192
|
|
|
@@ -108,41 +199,71 @@ function buildPrompt(analysis, fileTree, fileSamples) {
|
|
|
108
199
|
.map(([k, v]) => ` ${k}: ${v}`)
|
|
109
200
|
.join("\n") || " (none)";
|
|
110
201
|
|
|
111
|
-
|
|
202
|
+
const envVarsText = envVars.size > 0
|
|
203
|
+
? Array.from(envVars.entries())
|
|
204
|
+
.map(([name, files]) => `- \`${name}\` — used in ${files.map((f) => "`" + f + "`").join(", ")}`)
|
|
205
|
+
.join("\n")
|
|
206
|
+
: "(none found)";
|
|
207
|
+
|
|
208
|
+
const exportsText = fileExports.length > 0
|
|
209
|
+
? fileExports.slice(0, 30).map((e) => `- \`${e.file}\`: ${e.exports}`).join("\n")
|
|
210
|
+
: "(none found)";
|
|
211
|
+
|
|
212
|
+
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.`;
|
|
112
213
|
|
|
113
|
-
|
|
114
|
-
- Every claim must be grounded in the actual files and code samples provided. No generic filler.
|
|
115
|
-
- Reference specific file paths (e.g. \`src/lib/auth.ts\`, \`app/api/users/route.ts\`) wherever possible.
|
|
116
|
-
- If you cannot infer something from the provided code, omit that bullet entirely — do NOT guess or write placeholders.
|
|
117
|
-
- Architecture Notes must name real functions, classes, modules, or patterns you actually observed, not vague descriptions.
|
|
118
|
-
- Rules for AI must reflect patterns actually present in the source — not generic best practices.
|
|
214
|
+
const userMessage = `I ran \`codebrief\` and need you to write a CONTEXT.md that lets an AI code assistant (Cursor, Copilot) understand this project so well it can write production code immediately.
|
|
119
215
|
|
|
216
|
+
---
|
|
217
|
+
## HARD RULES (violating these = failure)
|
|
218
|
+
|
|
219
|
+
1. **File-path grounding**: Every bullet in Architecture Notes, Rules for AI, and Never Do MUST reference at least one real file path or function/export name from the code samples. No exceptions.
|
|
220
|
+
2. **No negatives**: NEVER write "X is not used", "the project does not have Y", "no database detected". If something doesn't exist, simply don't mention it.
|
|
221
|
+
3. **No generic advice**: NEVER write vague statements like "follow best practices", "maintain code quality", "adhere to coding standards", "ensure security". These are worthless.
|
|
222
|
+
4. **Omit, don't guess**: If you can't infer something from the actual code samples, omit that section/bullet entirely. Empty sections should be removed.
|
|
223
|
+
5. **Specific > exhaustive**: 5 deeply specific bullets beat 15 vague ones.
|
|
224
|
+
|
|
225
|
+
## BAD (never write like this)
|
|
226
|
+
- "Authentication and session logic are not explicitly handled within the project"
|
|
227
|
+
- "Adhere to the project's coding standards and best practices"
|
|
228
|
+
- "Regular security audits are essential"
|
|
229
|
+
- "Error handling mechanisms are crucial for a robust application"
|
|
230
|
+
- "The project follows a modular structure, enhancing maintainability"
|
|
231
|
+
|
|
232
|
+
## GOOD (write like this)
|
|
233
|
+
- "CLI entry point is \`src/index.js:main()\` — parses flags via \`hasFlag()\`/\`getFlagValue()\`, calls \`scanDirectory()\` → \`analyzeProject()\` → \`generateContextFile()\` in sequence"
|
|
234
|
+
- "AI enhancement in \`src/ai.js:enhanceWithAI()\` samples up to 32k chars of source via \`sampleSourceFiles()\`, builds a structured prompt, dispatches to the selected provider (Groq/OpenAI/Anthropic/Gemini/Grok/Ollama)"
|
|
235
|
+
- "Never add npm dependencies — this project uses zero deps (native \`https\`, \`fs\`, \`path\` only). See \`package.json\` dependencies field is empty."
|
|
236
|
+
- "All color output uses the \`c\` object from \`src/index.js\` (ANSI escape codes) — never use chalk or other color libraries"
|
|
237
|
+
|
|
238
|
+
---
|
|
120
239
|
## Project metadata
|
|
121
240
|
- Name: ${analysis.name}
|
|
122
241
|
- Framework / Type: ${analysis.type}
|
|
123
242
|
- Language: ${analysis.language}
|
|
124
243
|
- Package manager: ${analysis.packageManager}
|
|
125
244
|
- Stack: ${analysis.stack.join(", ") || "unknown"}
|
|
126
|
-
- CSS
|
|
127
|
-
-
|
|
128
|
-
- State management: ${analysis.stateManagement || "none detected"}
|
|
129
|
-
- Database / ORM: ${analysis.database || "none detected"}
|
|
130
|
-
- Test framework: ${analysis.testFramework || "none detected"}
|
|
131
|
-
- Deployment target: ${analysis.deployment || "unknown"}
|
|
245
|
+
- CSS: ${analysis.cssFramework || "none"} · UI: ${analysis.uiLibrary || "none"} · State: ${analysis.stateManagement || "none"}
|
|
246
|
+
- DB: ${analysis.database || "none"} · Tests: ${analysis.testFramework || "none"} · Deploy: ${analysis.deployment || "unknown"}
|
|
132
247
|
- Monorepo: ${analysis.isMonorepo}
|
|
133
248
|
|
|
134
249
|
## Scripts
|
|
135
250
|
${scripts}
|
|
136
251
|
|
|
137
|
-
## File tree
|
|
252
|
+
## File tree
|
|
138
253
|
${fileList}
|
|
139
254
|
|
|
140
|
-
## Source
|
|
255
|
+
## Source code samples (READ CAREFULLY — this is your evidence)
|
|
141
256
|
${samplesText}
|
|
142
257
|
|
|
258
|
+
## Environment variables found in code
|
|
259
|
+
${envVarsText}
|
|
260
|
+
|
|
261
|
+
## Module exports detected
|
|
262
|
+
${exportsText}
|
|
263
|
+
|
|
143
264
|
---
|
|
144
265
|
|
|
145
|
-
|
|
266
|
+
Now produce the CONTEXT.md in EXACTLY this structure. Remove any section where you have nothing concrete to say. Keep the emoji in every section header EXACTLY as shown.
|
|
146
267
|
|
|
147
268
|
# Project Context: ${analysis.name}
|
|
148
269
|
> AI-enhanced by **codebrief** · ${new Date().toISOString().split("T")[0]}
|
|
@@ -150,44 +271,40 @@ Produce the CONTEXT.md in exactly this structure:
|
|
|
150
271
|
---
|
|
151
272
|
|
|
152
273
|
## 🧱 Tech Stack
|
|
153
|
-
|
|
274
|
+
Bullet list. Each bullet: technology name + its specific role citing where it's used.
|
|
275
|
+
Example: "Node.js — runtime; entry point at \`src/index.js\`, all code is CommonJS with \`require()\`"
|
|
154
276
|
|
|
155
277
|
## 🚀 Key Files
|
|
156
|
-
|
|
278
|
+
The 5–8 most important files to read first. Exact paths. One sentence each explaining what the file does and its key exports/functions.
|
|
157
279
|
|
|
158
280
|
## 📁 Folder Structure
|
|
159
|
-
|
|
281
|
+
One bullet per top-level directory, explaining its responsibility based on the actual files inside.
|
|
160
282
|
|
|
161
283
|
## 🔧 Scripts
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
## 🗂️ Project Tree
|
|
165
|
-
\`\`\`
|
|
166
|
-
${fileList}
|
|
167
|
-
\`\`\`
|
|
284
|
+
One bullet per script. Say what it actually does, not just its command.
|
|
168
285
|
|
|
169
286
|
## 🏗️ Architecture Notes
|
|
170
|
-
|
|
171
|
-
- Name
|
|
172
|
-
- Describe a concrete data flow,
|
|
173
|
-
-
|
|
287
|
+
8–15 bullets. Each MUST:
|
|
288
|
+
- Name specific file(s), function(s), or export(s)
|
|
289
|
+
- Describe a concrete data flow, dependency, or design decision
|
|
290
|
+
- Be something useful for an AI about to write code in this project
|
|
174
291
|
|
|
175
292
|
## 🤖 Rules for AI
|
|
176
|
-
|
|
177
|
-
- "Always
|
|
178
|
-
- "API routes live in \`app/api/\` and must use the response helper from \`src/lib/response.ts\`"
|
|
293
|
+
8–12 rules extracted from the actual code patterns. Format:
|
|
294
|
+
- "Always/Never [specific action] — [file or pattern reference]"
|
|
179
295
|
|
|
180
296
|
## 🚫 Never Do
|
|
181
|
-
|
|
297
|
+
6–10 prohibitions grounded in the codebase. Each must cite WHY (a file, pattern, or convention).
|
|
182
298
|
|
|
183
|
-
## 🔐 Environment &
|
|
184
|
-
|
|
185
|
-
- Note any auth patterns, token handling, or secrets management observed.
|
|
299
|
+
## 🔐 Environment & Secrets
|
|
300
|
+
List actual env var names found in the code (e.g. \`GROQ_API_KEY\`, \`OPENAI_API_KEY\`). Describe how they're loaded and used. If none found, omit this section.
|
|
186
301
|
|
|
187
302
|
---
|
|
188
303
|
*Re-run \`codebrief --ai\` after major refactors to keep this file current.*
|
|
189
304
|
|
|
190
|
-
Respond with ONLY the Markdown. No preamble, no code fences
|
|
305
|
+
Respond with ONLY the Markdown. No preamble, no wrapping code fences.`;
|
|
306
|
+
|
|
307
|
+
return { systemMessage, userMessage };
|
|
191
308
|
}
|
|
192
309
|
|
|
193
310
|
// ── HTTP helper (native, no deps) ────────────────────────────
|
|
@@ -233,6 +350,10 @@ async function callGroq(prompt, model) {
|
|
|
233
350
|
" Get a free key in ~30s at https://console.groq.com",
|
|
234
351
|
);
|
|
235
352
|
|
|
353
|
+
const messages = typeof prompt === "string"
|
|
354
|
+
? [{ role: "user", content: prompt }]
|
|
355
|
+
: [{ role: "system", content: prompt.systemMessage }, { role: "user", content: prompt.userMessage }];
|
|
356
|
+
|
|
236
357
|
const res = await httpsPost(
|
|
237
358
|
"api.groq.com",
|
|
238
359
|
"/openai/v1/chat/completions",
|
|
@@ -242,9 +363,9 @@ async function callGroq(prompt, model) {
|
|
|
242
363
|
},
|
|
243
364
|
{
|
|
244
365
|
model,
|
|
245
|
-
messages
|
|
246
|
-
temperature: 0.
|
|
247
|
-
max_tokens:
|
|
366
|
+
messages,
|
|
367
|
+
temperature: 0.2,
|
|
368
|
+
max_tokens: 8192,
|
|
248
369
|
},
|
|
249
370
|
);
|
|
250
371
|
return res.choices?.[0]?.message?.content || "";
|
|
@@ -256,6 +377,10 @@ async function callOpenAI(prompt, model) {
|
|
|
256
377
|
if (!apiKey)
|
|
257
378
|
throw new Error("OPENAI_API_KEY environment variable is not set.");
|
|
258
379
|
|
|
380
|
+
const messages = typeof prompt === "string"
|
|
381
|
+
? [{ role: "user", content: prompt }]
|
|
382
|
+
: [{ role: "system", content: prompt.systemMessage }, { role: "user", content: prompt.userMessage }];
|
|
383
|
+
|
|
259
384
|
const res = await httpsPost(
|
|
260
385
|
"api.openai.com",
|
|
261
386
|
"/v1/chat/completions",
|
|
@@ -265,9 +390,9 @@ async function callOpenAI(prompt, model) {
|
|
|
265
390
|
},
|
|
266
391
|
{
|
|
267
392
|
model,
|
|
268
|
-
messages
|
|
269
|
-
temperature: 0.
|
|
270
|
-
max_tokens:
|
|
393
|
+
messages,
|
|
394
|
+
temperature: 0.2,
|
|
395
|
+
max_tokens: 8192,
|
|
271
396
|
},
|
|
272
397
|
);
|
|
273
398
|
return res.choices?.[0]?.message?.content || "";
|
|
@@ -279,6 +404,19 @@ async function callAnthropic(prompt, model) {
|
|
|
279
404
|
if (!apiKey)
|
|
280
405
|
throw new Error("ANTHROPIC_API_KEY environment variable is not set.");
|
|
281
406
|
|
|
407
|
+
const messages = typeof prompt === "string"
|
|
408
|
+
? [{ role: "user", content: prompt }]
|
|
409
|
+
: [{ role: "user", content: prompt.userMessage }];
|
|
410
|
+
|
|
411
|
+
const system = typeof prompt === "string" ? undefined : prompt.systemMessage;
|
|
412
|
+
|
|
413
|
+
const body = {
|
|
414
|
+
model,
|
|
415
|
+
max_tokens: 8192,
|
|
416
|
+
messages,
|
|
417
|
+
};
|
|
418
|
+
if (system) body.system = system;
|
|
419
|
+
|
|
282
420
|
const res = await httpsPost(
|
|
283
421
|
"api.anthropic.com",
|
|
284
422
|
"/v1/messages",
|
|
@@ -287,11 +425,7 @@ async function callAnthropic(prompt, model) {
|
|
|
287
425
|
"x-api-key": apiKey,
|
|
288
426
|
"anthropic-version": "2023-06-01",
|
|
289
427
|
},
|
|
290
|
-
|
|
291
|
-
model,
|
|
292
|
-
max_tokens: 4096,
|
|
293
|
-
messages: [{ role: "user", content: prompt }],
|
|
294
|
-
},
|
|
428
|
+
body,
|
|
295
429
|
);
|
|
296
430
|
return res.content?.[0]?.text || "";
|
|
297
431
|
}
|
|
@@ -305,13 +439,17 @@ async function callGemini(prompt, model) {
|
|
|
305
439
|
" Get a free key at https://aistudio.google.com/app/apikey",
|
|
306
440
|
);
|
|
307
441
|
|
|
442
|
+
const fullText = typeof prompt === "string"
|
|
443
|
+
? prompt
|
|
444
|
+
: `${prompt.systemMessage}\n\n${prompt.userMessage}`;
|
|
445
|
+
|
|
308
446
|
const res = await httpsPost(
|
|
309
447
|
"generativelanguage.googleapis.com",
|
|
310
448
|
`/v1beta/models/${model}:generateContent?key=${apiKey}`,
|
|
311
449
|
{ "Content-Type": "application/json" },
|
|
312
450
|
{
|
|
313
|
-
contents: [{ parts: [{ text:
|
|
314
|
-
generationConfig: { temperature: 0.
|
|
451
|
+
contents: [{ parts: [{ text: fullText }] }],
|
|
452
|
+
generationConfig: { temperature: 0.2, maxOutputTokens: 8192 },
|
|
315
453
|
},
|
|
316
454
|
);
|
|
317
455
|
return res.candidates?.[0]?.content?.parts?.[0]?.text || "";
|
|
@@ -326,6 +464,10 @@ async function callGrok(prompt, model) {
|
|
|
326
464
|
" Get a key at https://console.x.ai",
|
|
327
465
|
);
|
|
328
466
|
|
|
467
|
+
const messages = typeof prompt === "string"
|
|
468
|
+
? [{ role: "user", content: prompt }]
|
|
469
|
+
: [{ role: "system", content: prompt.systemMessage }, { role: "user", content: prompt.userMessage }];
|
|
470
|
+
|
|
329
471
|
const res = await httpsPost(
|
|
330
472
|
"api.x.ai",
|
|
331
473
|
"/v1/chat/completions",
|
|
@@ -335,9 +477,9 @@ async function callGrok(prompt, model) {
|
|
|
335
477
|
},
|
|
336
478
|
{
|
|
337
479
|
model,
|
|
338
|
-
messages
|
|
339
|
-
temperature: 0.
|
|
340
|
-
max_tokens:
|
|
480
|
+
messages,
|
|
481
|
+
temperature: 0.2,
|
|
482
|
+
max_tokens: 8192,
|
|
341
483
|
},
|
|
342
484
|
);
|
|
343
485
|
return res.choices?.[0]?.message?.content || "";
|
|
@@ -347,7 +489,10 @@ async function callOllama(prompt, model) {
|
|
|
347
489
|
model = model || getDefaultModel("ollama");
|
|
348
490
|
// Ollama runs locally on port 11434 — use http
|
|
349
491
|
const http = require("http");
|
|
350
|
-
const
|
|
492
|
+
const fullText = typeof prompt === "string"
|
|
493
|
+
? prompt
|
|
494
|
+
: `${prompt.systemMessage}\n\n${prompt.userMessage}`;
|
|
495
|
+
const body = JSON.stringify({ model, prompt: fullText, stream: false });
|
|
351
496
|
|
|
352
497
|
return new Promise((resolve, reject) => {
|
|
353
498
|
const req = http.request(
|
|
@@ -396,7 +541,9 @@ async function enhanceWithAI(analysis, fileTree, rootDir, options = {}) {
|
|
|
396
541
|
const { provider = "openai", model } = options;
|
|
397
542
|
|
|
398
543
|
const fileSamples = sampleSourceFiles(rootDir, fileTree);
|
|
399
|
-
const
|
|
544
|
+
const envVars = extractEnvVars(rootDir, fileTree);
|
|
545
|
+
const fileExports = extractExports(rootDir, fileTree);
|
|
546
|
+
const prompt = buildPrompt(analysis, fileTree, fileSamples, envVars, fileExports);
|
|
400
547
|
|
|
401
548
|
switch (provider.toLowerCase()) {
|
|
402
549
|
case "groq":
|