agent-bober 0.7.0 → 0.8.1

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 (60) hide show
  1. package/README.md +64 -2
  2. package/dist/cli/commands/init.d.ts.map +1 -1
  3. package/dist/cli/commands/init.js +176 -1
  4. package/dist/cli/commands/init.js.map +1 -1
  5. package/dist/discovery/config-generator.d.ts +28 -0
  6. package/dist/discovery/config-generator.d.ts.map +1 -0
  7. package/dist/discovery/config-generator.js +225 -0
  8. package/dist/discovery/config-generator.js.map +1 -0
  9. package/dist/discovery/index.d.ts +20 -0
  10. package/dist/discovery/index.d.ts.map +1 -0
  11. package/dist/discovery/index.js +19 -0
  12. package/dist/discovery/index.js.map +1 -0
  13. package/dist/discovery/scanner.d.ts +17 -0
  14. package/dist/discovery/scanner.d.ts.map +1 -0
  15. package/dist/discovery/scanner.js +120 -0
  16. package/dist/discovery/scanner.js.map +1 -0
  17. package/dist/discovery/scanners/ci-checks.d.ts +10 -0
  18. package/dist/discovery/scanners/ci-checks.d.ts.map +1 -0
  19. package/dist/discovery/scanners/ci-checks.js +169 -0
  20. package/dist/discovery/scanners/ci-checks.js.map +1 -0
  21. package/dist/discovery/scanners/code-conventions.d.ts +12 -0
  22. package/dist/discovery/scanners/code-conventions.d.ts.map +1 -0
  23. package/dist/discovery/scanners/code-conventions.js +216 -0
  24. package/dist/discovery/scanners/code-conventions.js.map +1 -0
  25. package/dist/discovery/scanners/documentation.d.ts +17 -0
  26. package/dist/discovery/scanners/documentation.d.ts.map +1 -0
  27. package/dist/discovery/scanners/documentation.js +92 -0
  28. package/dist/discovery/scanners/documentation.js.map +1 -0
  29. package/dist/discovery/scanners/git-conventions.d.ts +11 -0
  30. package/dist/discovery/scanners/git-conventions.d.ts.map +1 -0
  31. package/dist/discovery/scanners/git-conventions.js +128 -0
  32. package/dist/discovery/scanners/git-conventions.js.map +1 -0
  33. package/dist/discovery/scanners/package-scripts.d.ts +9 -0
  34. package/dist/discovery/scanners/package-scripts.d.ts.map +1 -0
  35. package/dist/discovery/scanners/package-scripts.js +112 -0
  36. package/dist/discovery/scanners/package-scripts.js.map +1 -0
  37. package/dist/discovery/scanners/test-conventions.d.ts +9 -0
  38. package/dist/discovery/scanners/test-conventions.d.ts.map +1 -0
  39. package/dist/discovery/scanners/test-conventions.js +231 -0
  40. package/dist/discovery/scanners/test-conventions.js.map +1 -0
  41. package/dist/discovery/synthesizer.d.ts +30 -0
  42. package/dist/discovery/synthesizer.d.ts.map +1 -0
  43. package/dist/discovery/synthesizer.js +348 -0
  44. package/dist/discovery/synthesizer.js.map +1 -0
  45. package/dist/discovery/types.d.ts +160 -0
  46. package/dist/discovery/types.d.ts.map +1 -0
  47. package/dist/discovery/types.js +9 -0
  48. package/dist/discovery/types.js.map +1 -0
  49. package/dist/index.d.ts +4 -0
  50. package/dist/index.d.ts.map +1 -1
  51. package/dist/index.js +4 -0
  52. package/dist/index.js.map +1 -1
  53. package/dist/mcp/tools/init.d.ts.map +1 -1
  54. package/dist/mcp/tools/init.js +102 -1
  55. package/dist/mcp/tools/init.js.map +1 -1
  56. package/package.json +2 -2
  57. package/skills/bober.eval/SKILL.md +4 -0
  58. package/skills/bober.principles/SKILL.md +36 -3
  59. package/skills/bober.run/SKILL.md +12 -0
  60. package/skills/bober.sprint/SKILL.md +8 -0
@@ -0,0 +1,225 @@
1
+ /**
2
+ * Config Generator
3
+ *
4
+ * Generates evaluator strategies and commands from a DiscoveryReport,
5
+ * enabling auto-configuration of bober.config.json for a scanned project.
6
+ *
7
+ * generateEvalConfig() is the main entry point. It reads the detected
8
+ * package scripts, CI checks, and stack information from the report and
9
+ * produces a CommandsSection and an array of EvalStrategy objects ready
10
+ * to be written into the evaluator config.
11
+ */
12
+ // ── Package manager install commands ─────────────────────────────
13
+ function buildInstallCommand(pm) {
14
+ switch (pm) {
15
+ case "yarn":
16
+ return "yarn";
17
+ case "pnpm":
18
+ return "pnpm install";
19
+ case "bun":
20
+ return "bun install";
21
+ case "npm":
22
+ default:
23
+ return "npm install";
24
+ }
25
+ }
26
+ // ── Commands section generation ───────────────────────────────────
27
+ /**
28
+ * Produce a CommandsSection from the package scripts portion of a
29
+ * DiscoveryReport. Each bober command category maps 1-to-1 to the
30
+ * CommandsSection fields, using the pre-built runCommand strings that
31
+ * the package-scripts scanner already computed.
32
+ */
33
+ function generateCommands(report) {
34
+ const categorized = report.packageScripts?.categorized ?? {};
35
+ return {
36
+ install: buildInstallCommand(report.packageManager),
37
+ build: categorized.build?.runCommand,
38
+ test: categorized.test?.runCommand,
39
+ lint: categorized.lint?.runCommand,
40
+ typecheck: categorized.typecheck?.runCommand,
41
+ dev: categorized.dev?.runCommand,
42
+ };
43
+ }
44
+ // ── Core strategy generation ──────────────────────────────────────
45
+ /**
46
+ * Map the detected package script categories to evaluator strategies.
47
+ * The ordering reflects typical CI pipeline order (typecheck -> lint -> build -> test).
48
+ */
49
+ function generateCoreStrategies(report) {
50
+ const categorized = report.packageScripts?.categorized ?? {};
51
+ const strategies = [];
52
+ if (categorized.typecheck) {
53
+ strategies.push({ type: "typecheck", required: true, command: categorized.typecheck.runCommand });
54
+ }
55
+ if (categorized.lint) {
56
+ strategies.push({ type: "lint", required: true, command: categorized.lint.runCommand });
57
+ }
58
+ if (categorized.build) {
59
+ strategies.push({ type: "build", required: true, command: categorized.build.runCommand });
60
+ }
61
+ if (categorized.test) {
62
+ strategies.push({ type: "unit-test", required: true, command: categorized.test.runCommand });
63
+ }
64
+ return strategies;
65
+ }
66
+ // ── Playwright strategy generation ───────────────────────────────
67
+ /**
68
+ * Playwright requires both the @playwright/test dependency AND a
69
+ * playwright config file to be considered fully configured.
70
+ *
71
+ * - Both present: required: true
72
+ * - Only one of the two present: required: false (partial setup)
73
+ * - Neither present: no strategy added
74
+ */
75
+ function generatePlaywrightStrategy(report) {
76
+ const stack = report.detectedStack;
77
+ if (!stack)
78
+ return null;
79
+ const hasDep = stack.hasPlaywright;
80
+ // Check for playwright config file presence by scanning allScripts or relying
81
+ // on the stack report. The stack scanner checks @playwright/test in deps.
82
+ // For config file detection we look at the CI commands or documentation heuristic.
83
+ // Since DiscoveryReport doesn't have a dedicated playwright config field, we
84
+ // determine "has config" by checking if any documented playwright config
85
+ // patterns appear in the scanned project files. We use a conservative
86
+ // approach: treat it as "config present" if the dep is detected (the most
87
+ // common signal) and additionally check CI commands for playwright references.
88
+ const hasPlaywrightInCI = report.ciChecks.allRunCommands.some((cmd) => cmd.toLowerCase().includes("playwright"));
89
+ // A playwright config file is inferred from its dep being present AND
90
+ // either a CI reference or a test script that mentions playwright.
91
+ const testScript = report.packageScripts?.categorized.test?.command ?? "";
92
+ const hasPlaywrightInTestScript = testScript.toLowerCase().includes("playwright");
93
+ const hasConfig = hasDep && (hasPlaywrightInCI || hasPlaywrightInTestScript);
94
+ if (!hasDep && !hasConfig)
95
+ return null;
96
+ // required: true only when both dep and config evidence are present
97
+ return {
98
+ type: "playwright",
99
+ required: hasDep && hasConfig,
100
+ };
101
+ }
102
+ // ── API framework strategy generation ────────────────────────────
103
+ /**
104
+ * When an API framework is detected in the stack, add an api-check strategy.
105
+ * Always required: false since not all projects expose a live server during CI.
106
+ */
107
+ function generateApiStrategy(report) {
108
+ const stack = report.detectedStack;
109
+ if (!stack)
110
+ return null;
111
+ const hasApiFramework = stack.hasNestjs || stack.hasFastify || stack.hasExpress;
112
+ if (!hasApiFramework)
113
+ return null;
114
+ return { type: "api-check", required: false };
115
+ }
116
+ // ── CI-derived strategy generation ───────────────────────────────
117
+ /**
118
+ * Convert a CI run command into a safe strategy type name.
119
+ *
120
+ * Examples:
121
+ * "cargo clippy" -> "cargo-clippy"
122
+ * "npx prettier --check ." -> "prettier"
123
+ * "python -m pytest" -> "pytest"
124
+ */
125
+ function ciCommandToStrategyType(cmd) {
126
+ // Strip common prefixes that don't add type identity
127
+ const stripped = cmd
128
+ .replace(/^(npx|python\s+-m|\.\/node_modules\/.bin\/)\s*/i, "")
129
+ .trim();
130
+ // Take the first two tokens for compound commands (e.g. "cargo clippy")
131
+ const tokens = stripped.split(/\s+/).filter(Boolean);
132
+ const base = tokens.slice(0, 2).join("-");
133
+ // Sanitize to produce a valid type name: lowercase alphanumeric and hyphens
134
+ return base
135
+ .toLowerCase()
136
+ .replace(/[^a-z0-9-]/g, "-")
137
+ .replace(/-+/g, "-")
138
+ .replace(/^-|-$/g, "");
139
+ }
140
+ /**
141
+ * Build a human-readable label from the strategy type (derived from CI command).
142
+ * E.g., "cargo-clippy" -> "Cargo-Clippy (from CI)"
143
+ */
144
+ function ciStrategyLabel(type) {
145
+ const formatted = type
146
+ .split("-")
147
+ .map((part) => part.charAt(0).toUpperCase() + part.slice(1))
148
+ .join("-");
149
+ return `${formatted} (from CI)`;
150
+ }
151
+ /**
152
+ * Determine which CI step run commands don't already have coverage from
153
+ * the core strategies and generate inline-command strategies for them.
154
+ *
155
+ * A CI command is "already covered" if its inferred category matches one
156
+ * of the core strategy types we already emitted.
157
+ */
158
+ function generateCIStrategies(report, coveredTypes) {
159
+ const strategies = [];
160
+ const seenTypes = new Set();
161
+ // Gather all unique CI steps across all workflows
162
+ const allSteps = report.ciChecks.workflows.flatMap((w) => w.steps);
163
+ for (const step of allSteps) {
164
+ const cmd = step.runCommand.trim();
165
+ if (!cmd)
166
+ continue;
167
+ // Map CI category to bober strategy type
168
+ const ciCategoryToStrategyType = {
169
+ test: "unit-test",
170
+ lint: "lint",
171
+ build: "build",
172
+ deploy: "deploy",
173
+ other: "",
174
+ };
175
+ const mappedType = ciCategoryToStrategyType[step.category] ?? "";
176
+ // Skip if this category is already covered by a core strategy
177
+ if (mappedType && coveredTypes.has(mappedType))
178
+ continue;
179
+ // Derive a unique type name from the command itself
180
+ const type = ciCommandToStrategyType(cmd);
181
+ if (!type || seenTypes.has(type))
182
+ continue;
183
+ seenTypes.add(type);
184
+ strategies.push({
185
+ type,
186
+ command: cmd,
187
+ required: false,
188
+ label: ciStrategyLabel(type),
189
+ });
190
+ }
191
+ return strategies;
192
+ }
193
+ /**
194
+ * Generate evaluator strategies and commands from a DiscoveryReport.
195
+ *
196
+ * The returned object is ready to be merged into a bober.config.json:
197
+ * config.evaluator.strategies = result.strategies
198
+ * config.commands = result.commands
199
+ *
200
+ * @param report The DiscoveryReport produced by scanProject().
201
+ */
202
+ export function generateEvalConfig(report) {
203
+ const commands = generateCommands(report);
204
+ const coreStrategies = generateCoreStrategies(report);
205
+ // Track which strategy types are already covered to avoid duplicating
206
+ // them when processing CI-derived commands
207
+ const coveredTypes = new Set(coreStrategies.map((s) => s.type));
208
+ const playwrightStrategy = generatePlaywrightStrategy(report);
209
+ if (playwrightStrategy) {
210
+ coveredTypes.add(playwrightStrategy.type);
211
+ }
212
+ const apiStrategy = generateApiStrategy(report);
213
+ if (apiStrategy) {
214
+ coveredTypes.add(apiStrategy.type);
215
+ }
216
+ const ciStrategies = generateCIStrategies(report, coveredTypes);
217
+ const strategies = [
218
+ ...coreStrategies,
219
+ ...(playwrightStrategy ? [playwrightStrategy] : []),
220
+ ...(apiStrategy ? [apiStrategy] : []),
221
+ ...ciStrategies,
222
+ ];
223
+ return { strategies, commands };
224
+ }
225
+ //# sourceMappingURL=config-generator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config-generator.js","sourceRoot":"","sources":["../../src/discovery/config-generator.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Barrel exports for src/discovery.
3
+ *
4
+ * Public surface:
5
+ * - scanProject() -- the main orchestration function
6
+ * - DiscoveryReport and all sub-types
7
+ * - Individual sub-scanner functions (for selective use or testing)
8
+ */
9
+ export { scanProject } from "./scanner.js";
10
+ export { synthesizePrinciples, validatePrinciplesMarkdown } from "./synthesizer.js";
11
+ export { generateEvalConfig } from "./config-generator.js";
12
+ export type { EvalConfig } from "./config-generator.js";
13
+ export type { DiscoveryReport, PackageScriptsReport, BoberCommandCategory, CommandMapping, CIChecksReport, CIWorkflow, CIStep, CICategory, GitConventionsReport, CodeConventionsReport, FileNamingStyle, FileNamingReport, ImportStyleReport, ExportStyleReport, TypeScriptPatternsReport, TestConventionsReport, TestFramework, MockingLibrary, DocumentationReport, DocFile, DetectedStackReport, } from "./types.js";
14
+ export { scanPackageScripts } from "./scanners/package-scripts.js";
15
+ export { scanCIChecks } from "./scanners/ci-checks.js";
16
+ export { scanGitConventions } from "./scanners/git-conventions.js";
17
+ export { scanCodeConventions } from "./scanners/code-conventions.js";
18
+ export { scanTestConventions } from "./scanners/test-conventions.js";
19
+ export { scanDocumentation } from "./scanners/documentation.js";
20
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/discovery/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,EAAE,oBAAoB,EAAE,0BAA0B,EAAE,MAAM,kBAAkB,CAAC;AACpF,OAAO,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAC3D,YAAY,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAExD,YAAY,EACV,eAAe,EACf,oBAAoB,EACpB,oBAAoB,EACpB,cAAc,EACd,cAAc,EACd,UAAU,EACV,MAAM,EACN,UAAU,EACV,oBAAoB,EACpB,qBAAqB,EACrB,eAAe,EACf,gBAAgB,EAChB,iBAAiB,EACjB,iBAAiB,EACjB,wBAAwB,EACxB,qBAAqB,EACrB,aAAa,EACb,cAAc,EACd,mBAAmB,EACnB,OAAO,EACP,mBAAmB,GACpB,MAAM,YAAY,CAAC;AAGpB,OAAO,EAAE,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AACnE,OAAO,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AACvD,OAAO,EAAE,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AACnE,OAAO,EAAE,mBAAmB,EAAE,MAAM,gCAAgC,CAAC;AACrE,OAAO,EAAE,mBAAmB,EAAE,MAAM,gCAAgC,CAAC;AACrE,OAAO,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAC"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Barrel exports for src/discovery.
3
+ *
4
+ * Public surface:
5
+ * - scanProject() -- the main orchestration function
6
+ * - DiscoveryReport and all sub-types
7
+ * - Individual sub-scanner functions (for selective use or testing)
8
+ */
9
+ export { scanProject } from "./scanner.js";
10
+ export { synthesizePrinciples, validatePrinciplesMarkdown } from "./synthesizer.js";
11
+ export { generateEvalConfig } from "./config-generator.js";
12
+ // Sub-scanners exported individually for selective use
13
+ export { scanPackageScripts } from "./scanners/package-scripts.js";
14
+ export { scanCIChecks } from "./scanners/ci-checks.js";
15
+ export { scanGitConventions } from "./scanners/git-conventions.js";
16
+ export { scanCodeConventions } from "./scanners/code-conventions.js";
17
+ export { scanTestConventions } from "./scanners/test-conventions.js";
18
+ export { scanDocumentation } from "./scanners/documentation.js";
19
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/discovery/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,EAAE,oBAAoB,EAAE,0BAA0B,EAAE,MAAM,kBAAkB,CAAC;AACpF,OAAO,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AA2B3D,uDAAuD;AACvD,OAAO,EAAE,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AACnE,OAAO,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AACvD,OAAO,EAAE,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AACnE,OAAO,EAAE,mBAAmB,EAAE,MAAM,gCAAgC,CAAC;AACrE,OAAO,EAAE,mBAAmB,EAAE,MAAM,gCAAgC,CAAC;AACrE,OAAO,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAC"}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Main codebase scanner.
3
+ *
4
+ * scanProject() orchestrates all sub-scanners and returns a fully-populated
5
+ * DiscoveryReport. Each sub-scanner handles its own failures gracefully,
6
+ * so a failure in one section does not break the rest.
7
+ */
8
+ import type { DiscoveryReport } from "./types.js";
9
+ /**
10
+ * Scan a project root and return a structured DiscoveryReport.
11
+ *
12
+ * Never throws -- individual scanner failures result in null sections.
13
+ *
14
+ * @param projectRoot Absolute path to the project root directory.
15
+ */
16
+ export declare function scanProject(projectRoot: string): Promise<DiscoveryReport>;
17
+ //# sourceMappingURL=scanner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scanner.d.ts","sourceRoot":"","sources":["../../src/discovery/scanner.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAWH,OAAO,KAAK,EAAE,eAAe,EAAuB,MAAM,YAAY,CAAC;AA6FvE;;;;;;GAMG;AACH,wBAAsB,WAAW,CAC/B,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,eAAe,CAAC,CA+B1B"}
@@ -0,0 +1,120 @@
1
+ /**
2
+ * Main codebase scanner.
3
+ *
4
+ * scanProject() orchestrates all sub-scanners and returns a fully-populated
5
+ * DiscoveryReport. Each sub-scanner handles its own failures gracefully,
6
+ * so a failure in one section does not break the rest.
7
+ */
8
+ import { readFile } from "node:fs/promises";
9
+ import { join } from "node:path";
10
+ import { fileExists } from "../utils/fs.js";
11
+ import { scanPackageScripts } from "./scanners/package-scripts.js";
12
+ import { scanCIChecks } from "./scanners/ci-checks.js";
13
+ import { scanGitConventions } from "./scanners/git-conventions.js";
14
+ import { scanCodeConventions } from "./scanners/code-conventions.js";
15
+ import { scanTestConventions } from "./scanners/test-conventions.js";
16
+ import { scanDocumentation } from "./scanners/documentation.js";
17
+ async function detectStack(projectRoot) {
18
+ const pkgPath = join(projectRoot, "package.json");
19
+ let allDeps = {};
20
+ if (await fileExists(pkgPath)) {
21
+ try {
22
+ const raw = await readFile(pkgPath, "utf-8");
23
+ const pkg = JSON.parse(raw);
24
+ allDeps = {
25
+ ...(pkg.dependencies ?? {}),
26
+ ...(pkg.devDependencies ?? {}),
27
+ };
28
+ }
29
+ catch {
30
+ // Continue with empty deps
31
+ }
32
+ }
33
+ const hasTypescript = (await fileExists(join(projectRoot, "tsconfig.json"))) ||
34
+ "typescript" in allDeps;
35
+ const hasReact = "react" in allDeps;
36
+ const nextConfigs = ["next.config.js", "next.config.ts", "next.config.mjs"];
37
+ let hasNext = "next" in allDeps;
38
+ if (!hasNext) {
39
+ for (const cfg of nextConfigs) {
40
+ if (await fileExists(join(projectRoot, cfg))) {
41
+ hasNext = true;
42
+ break;
43
+ }
44
+ }
45
+ }
46
+ const hasVite = "vite" in allDeps;
47
+ const hasPlaywright = "@playwright/test" in allDeps;
48
+ const hasEslint = "eslint" in allDeps;
49
+ const hasVitest = "vitest" in allDeps;
50
+ const hasJest = "jest" in allDeps;
51
+ const hasPython = (await fileExists(join(projectRoot, "pyproject.toml"))) ||
52
+ (await fileExists(join(projectRoot, "requirements.txt"))) ||
53
+ (await fileExists(join(projectRoot, "Pipfile")));
54
+ const hasRust = await fileExists(join(projectRoot, "Cargo.toml"));
55
+ const hasNestjs = "@nestjs/core" in allDeps;
56
+ const hasFastify = "fastify" in allDeps;
57
+ const hasExpress = "express" in allDeps;
58
+ let primaryLanguage = "unknown";
59
+ if (hasTypescript) {
60
+ primaryLanguage = "typescript";
61
+ }
62
+ else if ((await fileExists(join(projectRoot, "package.json"))) &&
63
+ !hasTypescript) {
64
+ primaryLanguage = "javascript";
65
+ }
66
+ else if (hasPython) {
67
+ primaryLanguage = "python";
68
+ }
69
+ else if (hasRust) {
70
+ primaryLanguage = "rust";
71
+ }
72
+ return {
73
+ hasTypescript,
74
+ hasReact,
75
+ hasNext,
76
+ hasVite,
77
+ hasPlaywright,
78
+ hasEslint,
79
+ hasVitest,
80
+ hasJest,
81
+ hasPython,
82
+ hasRust,
83
+ hasNestjs,
84
+ hasFastify,
85
+ hasExpress,
86
+ primaryLanguage,
87
+ };
88
+ }
89
+ // ── Main entry point ──────────────────────────────────────────────
90
+ /**
91
+ * Scan a project root and return a structured DiscoveryReport.
92
+ *
93
+ * Never throws -- individual scanner failures result in null sections.
94
+ *
95
+ * @param projectRoot Absolute path to the project root directory.
96
+ */
97
+ export async function scanProject(projectRoot) {
98
+ const [packageScripts, ciChecks, gitConventions, codeConventions, testConventions, documentation, detectedStack,] = await Promise.all([
99
+ scanPackageScripts(projectRoot).catch(() => null),
100
+ scanCIChecks(projectRoot).catch(() => ({ workflows: [], allRunCommands: [] })),
101
+ scanGitConventions(projectRoot).catch(() => null),
102
+ scanCodeConventions(projectRoot).catch(() => null),
103
+ scanTestConventions(projectRoot).catch(() => null),
104
+ scanDocumentation(projectRoot).catch(() => ({ files: [] })),
105
+ detectStack(projectRoot).catch(() => null),
106
+ ]);
107
+ return {
108
+ projectRoot,
109
+ scannedAt: new Date().toISOString(),
110
+ packageScripts,
111
+ packageManager: packageScripts?.packageManager ?? null,
112
+ ciChecks,
113
+ gitConventions,
114
+ codeConventions,
115
+ testConventions,
116
+ documentation,
117
+ detectedStack,
118
+ };
119
+ }
120
+ //# sourceMappingURL=scanner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scanner.js","sourceRoot":"","sources":["../../src/discovery/scanner.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Scanner: CI Checks
3
+ *
4
+ * Reads .github/workflows/*.yml and .gitlab-ci.yml.
5
+ * Extracts "run:" commands from steps using a simple line-by-line parser.
6
+ * No yaml dependency -- pure string parsing.
7
+ */
8
+ import type { CIChecksReport } from "../types.js";
9
+ export declare function scanCIChecks(projectRoot: string): Promise<CIChecksReport>;
10
+ //# sourceMappingURL=ci-checks.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ci-checks.d.ts","sourceRoot":"","sources":["../../../src/discovery/scanners/ci-checks.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAKH,OAAO,KAAK,EAAE,cAAc,EAAkC,MAAM,aAAa,CAAC;AAiIlF,wBAAsB,YAAY,CAChC,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,cAAc,CAAC,CA6CzB"}
@@ -0,0 +1,169 @@
1
+ /**
2
+ * Scanner: CI Checks
3
+ *
4
+ * Reads .github/workflows/*.yml and .gitlab-ci.yml.
5
+ * Extracts "run:" commands from steps using a simple line-by-line parser.
6
+ * No yaml dependency -- pure string parsing.
7
+ */
8
+ import { readFile, readdir } from "node:fs/promises";
9
+ import { join } from "node:path";
10
+ import { fileExists } from "../../utils/fs.js";
11
+ // ── Category inference ────────────────────────────────────────────
12
+ const CATEGORY_KEYWORDS = [
13
+ { category: "test", keywords: ["test", "vitest", "jest", "pytest", "mocha", "spec"] },
14
+ { category: "lint", keywords: ["lint", "eslint", "tslint", "prettier", "stylelint"] },
15
+ { category: "build", keywords: ["build", "compile", "tsc", "webpack", "vite", "rollup"] },
16
+ { category: "deploy", keywords: ["deploy", "release", "publish", "docker", "k8s", "heroku", "vercel", "netlify"] },
17
+ ];
18
+ function inferCategory(runCommand) {
19
+ const lower = runCommand.toLowerCase();
20
+ for (const { category, keywords } of CATEGORY_KEYWORDS) {
21
+ if (keywords.some((kw) => lower.includes(kw))) {
22
+ return category;
23
+ }
24
+ }
25
+ return "other";
26
+ }
27
+ // ── YAML line-by-line parser ──────────────────────────────────────
28
+ /**
29
+ * Extracts run commands from a YAML workflow file.
30
+ *
31
+ * Strategy:
32
+ * 1. Look for lines that start with (optional whitespace) "run:" (inline)
33
+ * e.g. ` run: npm test`
34
+ * 2. Look for multiline run blocks:
35
+ * e.g. ` run: |`
36
+ * ` npm test`
37
+ * ` npm run build`
38
+ *
39
+ * We also try to capture the nearest preceding "name:" to label the step.
40
+ */
41
+ function parseWorkflowFile(content, relPath) {
42
+ const lines = content.split("\n");
43
+ const steps = [];
44
+ let currentName = null;
45
+ let inRunBlock = false;
46
+ let runBlockIndent = 0;
47
+ let currentRunLines = [];
48
+ const flushRunBlock = () => {
49
+ if (currentRunLines.length > 0) {
50
+ const runCommand = currentRunLines.join("\n").trim();
51
+ if (runCommand) {
52
+ steps.push({
53
+ name: currentName,
54
+ runCommand,
55
+ category: inferCategory(runCommand),
56
+ });
57
+ }
58
+ }
59
+ currentRunLines = [];
60
+ inRunBlock = false;
61
+ runBlockIndent = 0;
62
+ };
63
+ for (const line of lines) {
64
+ const trimmed = line.trimStart();
65
+ const indent = line.length - trimmed.length;
66
+ // If we're in a multiline run block
67
+ if (inRunBlock) {
68
+ // If indentation decreased to or below the run: line's indent, the block ended
69
+ if (indent <= runBlockIndent && trimmed.length > 0) {
70
+ flushRunBlock();
71
+ // Fall through to process this line normally
72
+ }
73
+ else {
74
+ // Still in the block
75
+ if (trimmed.length > 0) {
76
+ currentRunLines.push(trimmed);
77
+ }
78
+ continue;
79
+ }
80
+ }
81
+ // Detect "name:" lines to capture step names
82
+ const nameMatch = /^\s*-?\s*name:\s*(.+)$/.exec(line);
83
+ if (nameMatch) {
84
+ currentName = nameMatch[1]?.trim() ?? null;
85
+ continue;
86
+ }
87
+ // Detect "run:" line
88
+ const runInlineMatch = /^\s*run:\s*(.+)$/.exec(line);
89
+ if (runInlineMatch) {
90
+ const value = runInlineMatch[1]?.trim() ?? "";
91
+ // Check if it's a multiline block indicator (| or >)
92
+ if (value === "|" || value === ">") {
93
+ inRunBlock = true;
94
+ runBlockIndent = indent;
95
+ currentRunLines = [];
96
+ }
97
+ else {
98
+ // Inline single-line run
99
+ steps.push({
100
+ name: currentName,
101
+ runCommand: value,
102
+ category: inferCategory(value),
103
+ });
104
+ currentName = null;
105
+ }
106
+ continue;
107
+ }
108
+ // Detect "run: |" or "run: >" on its own line pattern (no inline value)
109
+ const runBlockMatch = /^\s*run:\s*[|>]?\s*$/.exec(line);
110
+ if (runBlockMatch && !runInlineMatch) {
111
+ inRunBlock = true;
112
+ runBlockIndent = indent;
113
+ currentRunLines = [];
114
+ continue;
115
+ }
116
+ }
117
+ // Flush any remaining run block
118
+ if (inRunBlock && currentRunLines.length > 0) {
119
+ flushRunBlock();
120
+ }
121
+ return { file: relPath, steps };
122
+ }
123
+ // ── Main scanner ──────────────────────────────────────────────────
124
+ export async function scanCIChecks(projectRoot) {
125
+ const workflows = [];
126
+ // Scan .github/workflows/
127
+ const githubWorkflowsDir = join(projectRoot, ".github", "workflows");
128
+ if (await fileExists(githubWorkflowsDir)) {
129
+ try {
130
+ const files = await readdir(githubWorkflowsDir);
131
+ for (const file of files.filter((f) => f.endsWith(".yml") || f.endsWith(".yaml"))) {
132
+ const filePath = join(githubWorkflowsDir, file);
133
+ try {
134
+ const content = await readFile(filePath, "utf-8");
135
+ const relPath = `.github/workflows/${file}`;
136
+ const workflow = parseWorkflowFile(content, relPath);
137
+ if (workflow.steps.length > 0) {
138
+ workflows.push(workflow);
139
+ }
140
+ }
141
+ catch {
142
+ // Skip unreadable files
143
+ }
144
+ }
145
+ }
146
+ catch {
147
+ // Directory not readable
148
+ }
149
+ }
150
+ // Scan .gitlab-ci.yml
151
+ const gitlabCiPath = join(projectRoot, ".gitlab-ci.yml");
152
+ if (await fileExists(gitlabCiPath)) {
153
+ try {
154
+ const content = await readFile(gitlabCiPath, "utf-8");
155
+ const workflow = parseWorkflowFile(content, ".gitlab-ci.yml");
156
+ if (workflow.steps.length > 0) {
157
+ workflows.push(workflow);
158
+ }
159
+ }
160
+ catch {
161
+ // Skip
162
+ }
163
+ }
164
+ const allRunCommands = [
165
+ ...new Set(workflows.flatMap((w) => w.steps.map((s) => s.runCommand))),
166
+ ];
167
+ return { workflows, allRunCommands };
168
+ }
169
+ //# sourceMappingURL=ci-checks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ci-checks.js","sourceRoot":"","sources":["../../../src/discovery/scanners/ci-checks.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Scanner: Code Conventions
3
+ *
4
+ * Samples up to 20 source files per file category and detects:
5
+ * - File naming patterns (camelCase / kebab-case / PascalCase / snake_case)
6
+ * - Import style (relative vs absolute/alias, .js extensions)
7
+ * - Export patterns (named vs default)
8
+ * - TypeScript patterns (any, ts-ignore, enum, interface, type)
9
+ */
10
+ import type { CodeConventionsReport } from "../types.js";
11
+ export declare function scanCodeConventions(projectRoot: string): Promise<CodeConventionsReport | null>;
12
+ //# sourceMappingURL=code-conventions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"code-conventions.d.ts","sourceRoot":"","sources":["../../../src/discovery/scanners/code-conventions.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAKH,OAAO,KAAK,EACV,qBAAqB,EAMtB,MAAM,aAAa,CAAC;AAqLrB,wBAAsB,mBAAmB,CACvC,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,qBAAqB,GAAG,IAAI,CAAC,CAqDvC"}