agent-bober 0.7.0 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +64 -2
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/init.js +176 -1
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/discovery/config-generator.d.ts +28 -0
- package/dist/discovery/config-generator.d.ts.map +1 -0
- package/dist/discovery/config-generator.js +225 -0
- package/dist/discovery/config-generator.js.map +1 -0
- package/dist/discovery/index.d.ts +20 -0
- package/dist/discovery/index.d.ts.map +1 -0
- package/dist/discovery/index.js +19 -0
- package/dist/discovery/index.js.map +1 -0
- package/dist/discovery/scanner.d.ts +17 -0
- package/dist/discovery/scanner.d.ts.map +1 -0
- package/dist/discovery/scanner.js +120 -0
- package/dist/discovery/scanner.js.map +1 -0
- package/dist/discovery/scanners/ci-checks.d.ts +10 -0
- package/dist/discovery/scanners/ci-checks.d.ts.map +1 -0
- package/dist/discovery/scanners/ci-checks.js +169 -0
- package/dist/discovery/scanners/ci-checks.js.map +1 -0
- package/dist/discovery/scanners/code-conventions.d.ts +12 -0
- package/dist/discovery/scanners/code-conventions.d.ts.map +1 -0
- package/dist/discovery/scanners/code-conventions.js +216 -0
- package/dist/discovery/scanners/code-conventions.js.map +1 -0
- package/dist/discovery/scanners/documentation.d.ts +17 -0
- package/dist/discovery/scanners/documentation.d.ts.map +1 -0
- package/dist/discovery/scanners/documentation.js +92 -0
- package/dist/discovery/scanners/documentation.js.map +1 -0
- package/dist/discovery/scanners/git-conventions.d.ts +11 -0
- package/dist/discovery/scanners/git-conventions.d.ts.map +1 -0
- package/dist/discovery/scanners/git-conventions.js +128 -0
- package/dist/discovery/scanners/git-conventions.js.map +1 -0
- package/dist/discovery/scanners/package-scripts.d.ts +9 -0
- package/dist/discovery/scanners/package-scripts.d.ts.map +1 -0
- package/dist/discovery/scanners/package-scripts.js +112 -0
- package/dist/discovery/scanners/package-scripts.js.map +1 -0
- package/dist/discovery/scanners/test-conventions.d.ts +9 -0
- package/dist/discovery/scanners/test-conventions.d.ts.map +1 -0
- package/dist/discovery/scanners/test-conventions.js +231 -0
- package/dist/discovery/scanners/test-conventions.js.map +1 -0
- package/dist/discovery/synthesizer.d.ts +30 -0
- package/dist/discovery/synthesizer.d.ts.map +1 -0
- package/dist/discovery/synthesizer.js +348 -0
- package/dist/discovery/synthesizer.js.map +1 -0
- package/dist/discovery/types.d.ts +160 -0
- package/dist/discovery/types.d.ts.map +1 -0
- package/dist/discovery/types.js +9 -0
- package/dist/discovery/types.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/mcp/tools/init.d.ts.map +1 -1
- package/dist/mcp/tools/init.js +102 -1
- package/dist/mcp/tools/init.js.map +1 -1
- package/package.json +2 -2
- package/skills/bober.principles/SKILL.md +36 -3
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Scanner: Test Conventions
|
|
3
|
+
*
|
|
4
|
+
* Detects test framework, file naming patterns, directory structure,
|
|
5
|
+
* mocking library, and coverage configuration.
|
|
6
|
+
*/
|
|
7
|
+
import { readFile } from "node:fs/promises";
|
|
8
|
+
import { join, dirname, relative } from "node:path";
|
|
9
|
+
import { glob } from "glob";
|
|
10
|
+
import { fileExists } from "../../utils/fs.js";
|
|
11
|
+
// ── Constants ─────────────────────────────────────────────────────
|
|
12
|
+
const IGNORE_DIRS = [
|
|
13
|
+
"node_modules",
|
|
14
|
+
"dist",
|
|
15
|
+
".git",
|
|
16
|
+
".bober",
|
|
17
|
+
"build",
|
|
18
|
+
"coverage",
|
|
19
|
+
".next",
|
|
20
|
+
"__pycache__",
|
|
21
|
+
".turbo",
|
|
22
|
+
".cache",
|
|
23
|
+
"out",
|
|
24
|
+
".vercel",
|
|
25
|
+
];
|
|
26
|
+
async function readPackageJson(projectRoot) {
|
|
27
|
+
const pkgPath = join(projectRoot, "package.json");
|
|
28
|
+
if (!(await fileExists(pkgPath)))
|
|
29
|
+
return null;
|
|
30
|
+
try {
|
|
31
|
+
const raw = await readFile(pkgPath, "utf-8");
|
|
32
|
+
return JSON.parse(raw);
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
function detectFramework(pkg) {
|
|
39
|
+
if (!pkg)
|
|
40
|
+
return "unknown";
|
|
41
|
+
const allDeps = {
|
|
42
|
+
...(pkg.dependencies ?? {}),
|
|
43
|
+
...(pkg.devDependencies ?? {}),
|
|
44
|
+
};
|
|
45
|
+
if ("vitest" in allDeps)
|
|
46
|
+
return "vitest";
|
|
47
|
+
if ("jest" in allDeps)
|
|
48
|
+
return "jest";
|
|
49
|
+
if ("mocha" in allDeps)
|
|
50
|
+
return "mocha";
|
|
51
|
+
if ("jasmine" in allDeps)
|
|
52
|
+
return "jasmine";
|
|
53
|
+
if ("pytest" in allDeps)
|
|
54
|
+
return "pytest";
|
|
55
|
+
// Check scripts for clues
|
|
56
|
+
const scripts = pkg.scripts ?? {};
|
|
57
|
+
const testScript = Object.values(scripts).join(" ").toLowerCase();
|
|
58
|
+
if (testScript.includes("vitest"))
|
|
59
|
+
return "vitest";
|
|
60
|
+
if (testScript.includes("jest"))
|
|
61
|
+
return "jest";
|
|
62
|
+
if (testScript.includes("mocha"))
|
|
63
|
+
return "mocha";
|
|
64
|
+
if (testScript.includes("pytest"))
|
|
65
|
+
return "pytest";
|
|
66
|
+
return "unknown";
|
|
67
|
+
}
|
|
68
|
+
function detectMockingLibrary(pkg, framework) {
|
|
69
|
+
if (!pkg)
|
|
70
|
+
return "unknown";
|
|
71
|
+
const allDeps = {
|
|
72
|
+
...(pkg.dependencies ?? {}),
|
|
73
|
+
...(pkg.devDependencies ?? {}),
|
|
74
|
+
};
|
|
75
|
+
// vitest and jest have built-in mocking
|
|
76
|
+
if (framework === "vitest")
|
|
77
|
+
return "vitest";
|
|
78
|
+
if (framework === "jest")
|
|
79
|
+
return "jest";
|
|
80
|
+
if ("sinon" in allDeps)
|
|
81
|
+
return "sinon";
|
|
82
|
+
if ("testdouble" in allDeps)
|
|
83
|
+
return "testdouble";
|
|
84
|
+
return "none";
|
|
85
|
+
}
|
|
86
|
+
function detectFilePattern(testFiles) {
|
|
87
|
+
let testTs = 0;
|
|
88
|
+
let specTs = 0;
|
|
89
|
+
let testJs = 0;
|
|
90
|
+
let specJs = 0;
|
|
91
|
+
for (const f of testFiles) {
|
|
92
|
+
if (f.endsWith(".test.ts") || f.endsWith(".test.tsx"))
|
|
93
|
+
testTs++;
|
|
94
|
+
else if (f.endsWith(".spec.ts") || f.endsWith(".spec.tsx"))
|
|
95
|
+
specTs++;
|
|
96
|
+
else if (f.endsWith(".test.js") || f.endsWith(".test.jsx"))
|
|
97
|
+
testJs++;
|
|
98
|
+
else if (f.endsWith(".spec.js") || f.endsWith(".spec.jsx"))
|
|
99
|
+
specJs++;
|
|
100
|
+
}
|
|
101
|
+
const total = testTs + specTs + testJs + specJs;
|
|
102
|
+
if (total === 0)
|
|
103
|
+
return "unknown";
|
|
104
|
+
const counts = [
|
|
105
|
+
["*.test.ts", testTs],
|
|
106
|
+
["*.spec.ts", specTs],
|
|
107
|
+
["*.test.js", testJs],
|
|
108
|
+
["*.spec.js", specJs],
|
|
109
|
+
];
|
|
110
|
+
const [dominant] = counts.sort((a, b) => b[1] - a[1]);
|
|
111
|
+
if (!dominant)
|
|
112
|
+
return "unknown";
|
|
113
|
+
// If dominant pattern accounts for >= 80% of test files, use it
|
|
114
|
+
if (dominant[1] / total >= 0.8)
|
|
115
|
+
return dominant[0];
|
|
116
|
+
return "mixed";
|
|
117
|
+
}
|
|
118
|
+
// ── Directory structure detection ─────────────────────────────────
|
|
119
|
+
function detectColocated(testFiles, projectRoot) {
|
|
120
|
+
if (testFiles.length === 0)
|
|
121
|
+
return false;
|
|
122
|
+
let colocatedCount = 0;
|
|
123
|
+
let separateCount = 0;
|
|
124
|
+
for (const filePath of testFiles) {
|
|
125
|
+
const rel = relative(projectRoot, filePath);
|
|
126
|
+
const dir = dirname(rel);
|
|
127
|
+
if (dir.includes("__tests__") ||
|
|
128
|
+
dir.startsWith("test/") ||
|
|
129
|
+
dir.startsWith("tests/") ||
|
|
130
|
+
dir === "test" ||
|
|
131
|
+
dir === "tests") {
|
|
132
|
+
separateCount++;
|
|
133
|
+
}
|
|
134
|
+
else {
|
|
135
|
+
colocatedCount++;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
return colocatedCount >= separateCount;
|
|
139
|
+
}
|
|
140
|
+
function extractTestDirs(testFiles, projectRoot) {
|
|
141
|
+
const dirs = new Set();
|
|
142
|
+
for (const filePath of testFiles) {
|
|
143
|
+
const rel = relative(projectRoot, filePath);
|
|
144
|
+
const dir = dirname(rel);
|
|
145
|
+
if (dir.includes("__tests__") ||
|
|
146
|
+
dir.startsWith("test") ||
|
|
147
|
+
dir.startsWith("tests")) {
|
|
148
|
+
// Get the top-level test directory name
|
|
149
|
+
const parts = dir.split("/");
|
|
150
|
+
const testDirPart = parts.find((p) => p === "__tests__" || p === "test" || p === "tests");
|
|
151
|
+
if (testDirPart)
|
|
152
|
+
dirs.add(testDirPart);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
return Array.from(dirs);
|
|
156
|
+
}
|
|
157
|
+
// ── Coverage config detection ─────────────────────────────────────
|
|
158
|
+
async function detectCoverageConfig(projectRoot) {
|
|
159
|
+
// Check vitest.config.ts / vitest.config.js
|
|
160
|
+
const vitestConfigs = ["vitest.config.ts", "vitest.config.js", "vitest.config.mjs"];
|
|
161
|
+
for (const cfg of vitestConfigs) {
|
|
162
|
+
const cfgPath = join(projectRoot, cfg);
|
|
163
|
+
if (await fileExists(cfgPath)) {
|
|
164
|
+
try {
|
|
165
|
+
const content = await readFile(cfgPath, "utf-8");
|
|
166
|
+
if (content.includes("coverage"))
|
|
167
|
+
return true;
|
|
168
|
+
}
|
|
169
|
+
catch {
|
|
170
|
+
// Skip
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
// Check jest.config.ts / jest.config.js
|
|
175
|
+
const jestConfigs = [
|
|
176
|
+
"jest.config.ts",
|
|
177
|
+
"jest.config.js",
|
|
178
|
+
"jest.config.mjs",
|
|
179
|
+
"jest.config.json",
|
|
180
|
+
];
|
|
181
|
+
for (const cfg of jestConfigs) {
|
|
182
|
+
if (await fileExists(join(projectRoot, cfg)))
|
|
183
|
+
return true;
|
|
184
|
+
}
|
|
185
|
+
// Check package.json for coverage config
|
|
186
|
+
const pkgPath = join(projectRoot, "package.json");
|
|
187
|
+
if (await fileExists(pkgPath)) {
|
|
188
|
+
try {
|
|
189
|
+
const raw = await readFile(pkgPath, "utf-8");
|
|
190
|
+
const pkg = JSON.parse(raw);
|
|
191
|
+
if ("jest" in pkg) {
|
|
192
|
+
const jestConfig = pkg["jest"];
|
|
193
|
+
if ("collectCoverage" in jestConfig || "coverageDirectory" in jestConfig) {
|
|
194
|
+
return true;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
catch {
|
|
199
|
+
// Skip
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
return false;
|
|
203
|
+
}
|
|
204
|
+
// ── Main scanner ──────────────────────────────────────────────────
|
|
205
|
+
export async function scanTestConventions(projectRoot) {
|
|
206
|
+
const ignore = IGNORE_DIRS.map((d) => `**/${d}/**`);
|
|
207
|
+
let testFiles;
|
|
208
|
+
try {
|
|
209
|
+
testFiles = await glob("**/*.{test,spec}.{ts,tsx,js,jsx}", { cwd: projectRoot, ignore, absolute: true });
|
|
210
|
+
}
|
|
211
|
+
catch {
|
|
212
|
+
return null;
|
|
213
|
+
}
|
|
214
|
+
const pkg = await readPackageJson(projectRoot);
|
|
215
|
+
const framework = detectFramework(pkg);
|
|
216
|
+
const mockingLibrary = detectMockingLibrary(pkg, framework);
|
|
217
|
+
const filePattern = detectFilePattern(testFiles);
|
|
218
|
+
const colocated = detectColocated(testFiles, projectRoot);
|
|
219
|
+
const testDirs = extractTestDirs(testFiles, projectRoot);
|
|
220
|
+
const hasCoverageConfig = await detectCoverageConfig(projectRoot);
|
|
221
|
+
return {
|
|
222
|
+
framework,
|
|
223
|
+
filePattern,
|
|
224
|
+
colocated,
|
|
225
|
+
testDirs,
|
|
226
|
+
mockingLibrary,
|
|
227
|
+
hasCoverageConfig,
|
|
228
|
+
testFileCount: testFiles.length,
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
//# sourceMappingURL=test-conventions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"test-conventions.js","sourceRoot":"","sources":["../../../src/discovery/scanners/test-conventions.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LLM-powered principles synthesizer.
|
|
3
|
+
*
|
|
4
|
+
* synthesizePrinciples() takes a DiscoveryReport produced by scanProject()
|
|
5
|
+
* and makes a single LLM call to produce a comprehensive principles.md
|
|
6
|
+
* document tailored to the scanned project.
|
|
7
|
+
*
|
|
8
|
+
* The returned markdown is ready to write to .bober/principles.md.
|
|
9
|
+
*/
|
|
10
|
+
import type { BoberConfig } from "../config/schema.js";
|
|
11
|
+
import type { DiscoveryReport } from "./types.js";
|
|
12
|
+
/**
|
|
13
|
+
* Validate the synthesized markdown contains required structure.
|
|
14
|
+
* Returns true if valid; false otherwise. Never throws.
|
|
15
|
+
*/
|
|
16
|
+
export declare function validatePrinciplesMarkdown(markdown: string): boolean;
|
|
17
|
+
/**
|
|
18
|
+
* Make a single LLM call to synthesize a DiscoveryReport into a
|
|
19
|
+
* comprehensive principles.md markdown document.
|
|
20
|
+
*
|
|
21
|
+
* If the LLM response fails validation (missing headings), the raw
|
|
22
|
+
* response is returned anyway — never throws on validation failure.
|
|
23
|
+
*
|
|
24
|
+
* @param report DiscoveryReport produced by scanProject().
|
|
25
|
+
* @param projectRoot Absolute path to the project root (used in prompt).
|
|
26
|
+
* @param config Full BoberConfig — uses planner provider/model/endpoint.
|
|
27
|
+
* @returns Markdown string starting with "# Project Principles".
|
|
28
|
+
*/
|
|
29
|
+
export declare function synthesizePrinciples(report: DiscoveryReport, projectRoot: string, config: BoberConfig): Promise<string>;
|
|
30
|
+
//# sourceMappingURL=synthesizer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"synthesizer.d.ts","sourceRoot":"","sources":["../../src/discovery/synthesizer.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAGvD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AA+TlD;;;GAGG;AACH,wBAAgB,0BAA0B,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAYpE;AAID;;;;;;;;;;;GAWG;AACH,wBAAsB,oBAAoB,CACxC,MAAM,EAAE,eAAe,EACvB,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,WAAW,GAClB,OAAO,CAAC,MAAM,CAAC,CAmCjB"}
|
|
@@ -0,0 +1,348 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LLM-powered principles synthesizer.
|
|
3
|
+
*
|
|
4
|
+
* synthesizePrinciples() takes a DiscoveryReport produced by scanProject()
|
|
5
|
+
* and makes a single LLM call to produce a comprehensive principles.md
|
|
6
|
+
* document tailored to the scanned project.
|
|
7
|
+
*
|
|
8
|
+
* The returned markdown is ready to write to .bober/principles.md.
|
|
9
|
+
*/
|
|
10
|
+
import { createClient } from "../providers/factory.js";
|
|
11
|
+
import { resolveModel } from "../orchestrator/model-resolver.js";
|
|
12
|
+
// ── Required headings ─────────────────────────────────────────────
|
|
13
|
+
const REQUIRED_HEADINGS = [
|
|
14
|
+
"## Code Style",
|
|
15
|
+
"## TypeScript Conventions",
|
|
16
|
+
"## Testing Standards",
|
|
17
|
+
"## Git Workflow",
|
|
18
|
+
"## Error Handling",
|
|
19
|
+
"## File Organization",
|
|
20
|
+
"## Dependencies and Imports",
|
|
21
|
+
];
|
|
22
|
+
// ── Prompt builders ───────────────────────────────────────────────
|
|
23
|
+
function buildSystemPrompt() {
|
|
24
|
+
return `You are analyzing a codebase to produce project principles for an AI development harness.
|
|
25
|
+
|
|
26
|
+
Your output will be read by AI code generators (like Claude Code) as binding rules they must follow.
|
|
27
|
+
Every rule you write must be:
|
|
28
|
+
- Specific and actionable — a code generator can follow it without ambiguity
|
|
29
|
+
- Grounded in the actual codebase evidence provided — do not invent conventions
|
|
30
|
+
- Concrete, never vague — NEVER write phrases like "follow best practices", "maintain code quality", "write clean code", or any similar platitude
|
|
31
|
+
|
|
32
|
+
You are writing binding rules, not recommendations. Write in imperative mood ("Use X", "Never do Y").`;
|
|
33
|
+
}
|
|
34
|
+
function formatPackageScripts(report) {
|
|
35
|
+
const ps = report.packageScripts;
|
|
36
|
+
if (!ps)
|
|
37
|
+
return " (no package.json detected)";
|
|
38
|
+
const lines = [];
|
|
39
|
+
lines.push(` Package manager: ${ps.packageManager ?? "unknown"}`);
|
|
40
|
+
lines.push(` All scripts: ${Object.keys(ps.allScripts).join(", ")}`);
|
|
41
|
+
const cats = Object.entries(ps.categorized);
|
|
42
|
+
if (cats.length > 0) {
|
|
43
|
+
lines.push(" Detected command mappings:");
|
|
44
|
+
for (const [category, mapping] of cats) {
|
|
45
|
+
if (mapping) {
|
|
46
|
+
lines.push(` ${category}: ${mapping.runCommand} (script: "${mapping.scriptName}", raw: "${mapping.command}")`);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return lines.join("\n");
|
|
51
|
+
}
|
|
52
|
+
function formatCIChecks(report) {
|
|
53
|
+
const ci = report.ciChecks;
|
|
54
|
+
if (!ci || ci.workflows.length === 0)
|
|
55
|
+
return " (no CI workflows detected)";
|
|
56
|
+
const lines = [];
|
|
57
|
+
for (const workflow of ci.workflows) {
|
|
58
|
+
lines.push(` Workflow: ${workflow.file}`);
|
|
59
|
+
for (const step of workflow.steps) {
|
|
60
|
+
const name = step.name ? `"${step.name}"` : "(unnamed)";
|
|
61
|
+
lines.push(` - [${step.category}] ${name}: ${step.runCommand}`);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
if (ci.allRunCommands.length > 0) {
|
|
65
|
+
lines.push(` All CI run commands: ${ci.allRunCommands.join(", ")}`);
|
|
66
|
+
}
|
|
67
|
+
return lines.join("\n");
|
|
68
|
+
}
|
|
69
|
+
function formatGitConventions(report) {
|
|
70
|
+
const git = report.gitConventions;
|
|
71
|
+
if (!git)
|
|
72
|
+
return " (no git history detected)";
|
|
73
|
+
const lines = [];
|
|
74
|
+
lines.push(` Uses conventional commits: ${git.usesConventionalCommits}`);
|
|
75
|
+
lines.push(` Most common commit prefix: ${git.mostCommonPrefix ?? "(none detected)"}`);
|
|
76
|
+
lines.push(` Has linear history (no merge commits): ${git.hasLinearHistory}`);
|
|
77
|
+
lines.push(` Merge commit ratio: ${(git.mergeCommitRatio * 100).toFixed(0)}%`);
|
|
78
|
+
if (git.branchPatterns.length > 0) {
|
|
79
|
+
lines.push(` Branch naming patterns: ${git.branchPatterns.join(", ")}`);
|
|
80
|
+
}
|
|
81
|
+
if (git.recentMessages.length > 0) {
|
|
82
|
+
const sample = git.recentMessages.slice(0, 10);
|
|
83
|
+
lines.push(" Recent commit messages (sample):");
|
|
84
|
+
for (const msg of sample) {
|
|
85
|
+
lines.push(` - ${msg}`);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
return lines.join("\n");
|
|
89
|
+
}
|
|
90
|
+
function formatCodeConventions(report) {
|
|
91
|
+
const cc = report.codeConventions;
|
|
92
|
+
if (!cc)
|
|
93
|
+
return " (no source files scanned)";
|
|
94
|
+
const lines = [];
|
|
95
|
+
lines.push(` Files sampled: ${cc.filesSampled}`);
|
|
96
|
+
// File naming
|
|
97
|
+
lines.push(` File naming: dominant style is "${cc.fileNaming.dominant}"`);
|
|
98
|
+
const namingCounts = Object.entries(cc.fileNaming.counts)
|
|
99
|
+
.filter(([, count]) => count > 0)
|
|
100
|
+
.map(([style, count]) => `${style}=${count}`)
|
|
101
|
+
.join(", ");
|
|
102
|
+
if (namingCounts) {
|
|
103
|
+
lines.push(` Counts by style: ${namingCounts}`);
|
|
104
|
+
}
|
|
105
|
+
// Imports
|
|
106
|
+
lines.push(` Import style: ${cc.importStyle.relativeCount} relative, ${cc.importStyle.absoluteCount} absolute/alias`);
|
|
107
|
+
lines.push(` Uses .js extensions in imports: ${cc.importStyle.usesJsExtensions}`);
|
|
108
|
+
if (cc.importStyle.examples.length > 0) {
|
|
109
|
+
lines.push(" Import examples:");
|
|
110
|
+
for (const ex of cc.importStyle.examples) {
|
|
111
|
+
lines.push(` ${ex.trim()}`);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
// Exports
|
|
115
|
+
lines.push(` Export style: dominant is "${cc.exportStyle.dominant}" (named=${cc.exportStyle.namedExportCount}, default=${cc.exportStyle.defaultExportCount})`);
|
|
116
|
+
// TypeScript
|
|
117
|
+
if (cc.typescriptPatterns) {
|
|
118
|
+
const ts = cc.typescriptPatterns;
|
|
119
|
+
lines.push(` TypeScript usage:`);
|
|
120
|
+
lines.push(` interface declarations: ${ts.interfaceCount}`);
|
|
121
|
+
lines.push(` type alias declarations: ${ts.typeAliasCount}`);
|
|
122
|
+
lines.push(` enum declarations: ${ts.enumCount}`);
|
|
123
|
+
lines.push(` "any" usages: ${ts.anyCount}`);
|
|
124
|
+
lines.push(` @ts-ignore/@ts-expect-error usages: ${ts.tsIgnoreCount}`);
|
|
125
|
+
}
|
|
126
|
+
return lines.join("\n");
|
|
127
|
+
}
|
|
128
|
+
function formatTestConventions(report) {
|
|
129
|
+
const tc = report.testConventions;
|
|
130
|
+
if (!tc)
|
|
131
|
+
return " (no test files detected)";
|
|
132
|
+
const lines = [];
|
|
133
|
+
lines.push(` Framework: ${tc.framework}`);
|
|
134
|
+
lines.push(` Mocking library: ${tc.mockingLibrary}`);
|
|
135
|
+
lines.push(` File naming pattern: ${tc.filePattern}`);
|
|
136
|
+
lines.push(` Tests colocated with source: ${tc.colocated}`);
|
|
137
|
+
lines.push(` Test file count: ${tc.testFileCount}`);
|
|
138
|
+
lines.push(` Has coverage config: ${tc.hasCoverageConfig}`);
|
|
139
|
+
if (tc.testDirs.length > 0) {
|
|
140
|
+
lines.push(` Test directories: ${tc.testDirs.join(", ")}`);
|
|
141
|
+
}
|
|
142
|
+
return lines.join("\n");
|
|
143
|
+
}
|
|
144
|
+
function formatDocumentation(report) {
|
|
145
|
+
const docs = report.documentation;
|
|
146
|
+
if (!docs || docs.files.length === 0)
|
|
147
|
+
return " (no documentation files found)";
|
|
148
|
+
const lines = [];
|
|
149
|
+
for (const file of docs.files) {
|
|
150
|
+
const truncated = file.truncated ? " (truncated)" : "";
|
|
151
|
+
lines.push(` File: ${file.path}${truncated}`);
|
|
152
|
+
// Include a short excerpt (first 300 chars) to give the LLM flavour
|
|
153
|
+
const excerpt = file.content.slice(0, 300).replace(/\n/g, "\n ");
|
|
154
|
+
lines.push(` ${excerpt}`);
|
|
155
|
+
if (file.truncated) {
|
|
156
|
+
lines.push(" [... content truncated ...]");
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
return lines.join("\n");
|
|
160
|
+
}
|
|
161
|
+
function formatDetectedStack(report) {
|
|
162
|
+
const ds = report.detectedStack;
|
|
163
|
+
if (!ds)
|
|
164
|
+
return " (stack detection failed)";
|
|
165
|
+
const active = [];
|
|
166
|
+
if (ds.hasTypescript)
|
|
167
|
+
active.push("TypeScript");
|
|
168
|
+
if (ds.hasReact)
|
|
169
|
+
active.push("React");
|
|
170
|
+
if (ds.hasNext)
|
|
171
|
+
active.push("Next.js");
|
|
172
|
+
if (ds.hasVite)
|
|
173
|
+
active.push("Vite");
|
|
174
|
+
if (ds.hasVitest)
|
|
175
|
+
active.push("Vitest");
|
|
176
|
+
if (ds.hasJest)
|
|
177
|
+
active.push("Jest");
|
|
178
|
+
if (ds.hasPlaywright)
|
|
179
|
+
active.push("Playwright");
|
|
180
|
+
if (ds.hasEslint)
|
|
181
|
+
active.push("ESLint");
|
|
182
|
+
if (ds.hasPython)
|
|
183
|
+
active.push("Python");
|
|
184
|
+
if (ds.hasRust)
|
|
185
|
+
active.push("Rust");
|
|
186
|
+
if (ds.hasNestjs)
|
|
187
|
+
active.push("NestJS");
|
|
188
|
+
if (ds.hasFastify)
|
|
189
|
+
active.push("Fastify");
|
|
190
|
+
if (ds.hasExpress)
|
|
191
|
+
active.push("Express");
|
|
192
|
+
const lines = [];
|
|
193
|
+
lines.push(` Primary language: ${ds.primaryLanguage}`);
|
|
194
|
+
lines.push(` Detected technologies: ${active.length > 0 ? active.join(", ") : "(none)"}`);
|
|
195
|
+
return lines.join("\n");
|
|
196
|
+
}
|
|
197
|
+
function buildUserMessage(report, projectRoot) {
|
|
198
|
+
const date = new Date().toISOString().slice(0, 10);
|
|
199
|
+
const sections = [];
|
|
200
|
+
sections.push(`# Codebase Analysis Report
|
|
201
|
+
Project root: ${projectRoot}
|
|
202
|
+
Scanned at: ${report.scannedAt}
|
|
203
|
+
Package manager: ${report.packageManager ?? "unknown"}`);
|
|
204
|
+
sections.push(`## Package Scripts and Detected Commands
|
|
205
|
+
${formatPackageScripts(report)}`);
|
|
206
|
+
sections.push(`## CI/CD Workflows
|
|
207
|
+
${formatCIChecks(report)}`);
|
|
208
|
+
sections.push(`## Git Conventions
|
|
209
|
+
${formatGitConventions(report)}`);
|
|
210
|
+
sections.push(`## Code Conventions
|
|
211
|
+
${formatCodeConventions(report)}`);
|
|
212
|
+
sections.push(`## Test Conventions
|
|
213
|
+
${formatTestConventions(report)}`);
|
|
214
|
+
sections.push(`## Documentation Excerpts
|
|
215
|
+
${formatDocumentation(report)}`);
|
|
216
|
+
sections.push(`## Detected Stack
|
|
217
|
+
${formatDetectedStack(report)}`);
|
|
218
|
+
sections.push(`---
|
|
219
|
+
|
|
220
|
+
# Instructions
|
|
221
|
+
|
|
222
|
+
Using the codebase analysis above, produce a comprehensive \`principles.md\` document.
|
|
223
|
+
|
|
224
|
+
**Content requirements — you MUST follow all of these:**
|
|
225
|
+
|
|
226
|
+
1. Include file path examples for each convention discovered.
|
|
227
|
+
Example: "Components use PascalCase naming — see src/components/UserProfile.tsx"
|
|
228
|
+
Example: "Imports use relative paths with .js extensions — see src/utils/fs.ts"
|
|
229
|
+
|
|
230
|
+
2. Note any inconsistencies with the majority pattern.
|
|
231
|
+
Example: "Most files use camelCase but src/utils/parse-config.ts uses kebab-case"
|
|
232
|
+
Example: "Named exports dominate but some utility files use default exports"
|
|
233
|
+
|
|
234
|
+
3. NEVER use vague phrases like "follow best practices", "maintain code quality",
|
|
235
|
+
"write clean code", "be consistent", "use idiomatic code", or similar platitudes.
|
|
236
|
+
Every rule must be specific and actionable.
|
|
237
|
+
|
|
238
|
+
4. Produce specific actionable rules that a code generator can follow without ambiguity.
|
|
239
|
+
BAD: "Handle errors properly"
|
|
240
|
+
GOOD: "All async functions must catch errors and either rethrow with context or return null — never swallow errors silently"
|
|
241
|
+
|
|
242
|
+
5. Base every rule on the actual evidence in the report above. Do not invent conventions
|
|
243
|
+
that are not supported by the scanned data.
|
|
244
|
+
|
|
245
|
+
**Output format — produce EXACTLY this structure:**
|
|
246
|
+
|
|
247
|
+
\`\`\`markdown
|
|
248
|
+
# Project Principles
|
|
249
|
+
> Auto-discovered by agent-bober on ${date}
|
|
250
|
+
|
|
251
|
+
## Code Style
|
|
252
|
+
[rules]
|
|
253
|
+
|
|
254
|
+
## TypeScript Conventions
|
|
255
|
+
[rules]
|
|
256
|
+
|
|
257
|
+
## Testing Standards
|
|
258
|
+
[rules]
|
|
259
|
+
|
|
260
|
+
## Git Workflow
|
|
261
|
+
[rules]
|
|
262
|
+
|
|
263
|
+
## Error Handling
|
|
264
|
+
[rules]
|
|
265
|
+
|
|
266
|
+
## File Organization
|
|
267
|
+
[rules]
|
|
268
|
+
|
|
269
|
+
## Dependencies and Imports
|
|
270
|
+
[rules]
|
|
271
|
+
\`\`\`
|
|
272
|
+
|
|
273
|
+
Output ONLY the markdown document — no preamble, no explanation, no text outside the code fence.`);
|
|
274
|
+
return sections.join("\n\n");
|
|
275
|
+
}
|
|
276
|
+
// ── Response parsing ──────────────────────────────────────────────
|
|
277
|
+
/**
|
|
278
|
+
* Strip markdown code fences from a response if present.
|
|
279
|
+
*
|
|
280
|
+
* Handles:
|
|
281
|
+
* - ```markdown ... ```
|
|
282
|
+
* - ```md ... ```
|
|
283
|
+
* - ``` ... ```
|
|
284
|
+
* - No fences (returned as-is)
|
|
285
|
+
*/
|
|
286
|
+
function stripCodeFences(text) {
|
|
287
|
+
const trimmed = text.trim();
|
|
288
|
+
// Match opening fence with optional language tag, capture content, closing fence
|
|
289
|
+
const fenceMatch = /^```(?:markdown|md)?\s*\n([\s\S]*?)\n?```\s*$/.exec(trimmed);
|
|
290
|
+
if (fenceMatch) {
|
|
291
|
+
return fenceMatch[1].trim();
|
|
292
|
+
}
|
|
293
|
+
return trimmed;
|
|
294
|
+
}
|
|
295
|
+
/**
|
|
296
|
+
* Validate the synthesized markdown contains required structure.
|
|
297
|
+
* Returns true if valid; false otherwise. Never throws.
|
|
298
|
+
*/
|
|
299
|
+
export function validatePrinciplesMarkdown(markdown) {
|
|
300
|
+
if (!markdown.includes("# Project Principles")) {
|
|
301
|
+
return false;
|
|
302
|
+
}
|
|
303
|
+
for (const heading of REQUIRED_HEADINGS) {
|
|
304
|
+
if (!markdown.includes(heading)) {
|
|
305
|
+
return false;
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
return true;
|
|
309
|
+
}
|
|
310
|
+
// ── Public API ────────────────────────────────────────────────────
|
|
311
|
+
/**
|
|
312
|
+
* Make a single LLM call to synthesize a DiscoveryReport into a
|
|
313
|
+
* comprehensive principles.md markdown document.
|
|
314
|
+
*
|
|
315
|
+
* If the LLM response fails validation (missing headings), the raw
|
|
316
|
+
* response is returned anyway — never throws on validation failure.
|
|
317
|
+
*
|
|
318
|
+
* @param report DiscoveryReport produced by scanProject().
|
|
319
|
+
* @param projectRoot Absolute path to the project root (used in prompt).
|
|
320
|
+
* @param config Full BoberConfig — uses planner provider/model/endpoint.
|
|
321
|
+
* @returns Markdown string starting with "# Project Principles".
|
|
322
|
+
*/
|
|
323
|
+
export async function synthesizePrinciples(report, projectRoot, config) {
|
|
324
|
+
const client = createClient(config.planner.provider ?? null, config.planner.endpoint ?? null, config.planner.providerConfig, config.planner.model);
|
|
325
|
+
const model = resolveModel(config.planner.model);
|
|
326
|
+
const systemPrompt = buildSystemPrompt();
|
|
327
|
+
const userMessage = buildUserMessage(report, projectRoot);
|
|
328
|
+
const response = await client.chat({
|
|
329
|
+
model,
|
|
330
|
+
system: systemPrompt,
|
|
331
|
+
messages: [
|
|
332
|
+
{ role: "user", content: userMessage },
|
|
333
|
+
],
|
|
334
|
+
// No tools — single chat call
|
|
335
|
+
tools: [],
|
|
336
|
+
maxTokens: 16384,
|
|
337
|
+
});
|
|
338
|
+
const raw = response.text;
|
|
339
|
+
const stripped = stripCodeFences(raw);
|
|
340
|
+
// Validation — if it fails, return stripped anyway (never throw)
|
|
341
|
+
// This ensures callers always get something useful even if the model
|
|
342
|
+
// deviated from the requested format.
|
|
343
|
+
if (!validatePrinciplesMarkdown(stripped)) {
|
|
344
|
+
return stripped || raw;
|
|
345
|
+
}
|
|
346
|
+
return stripped;
|
|
347
|
+
}
|
|
348
|
+
//# sourceMappingURL=synthesizer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"synthesizer.js","sourceRoot":"","sources":["../../src/discovery/synthesizer.ts"],"names":[],"mappings":"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"}
|