briefed 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (117) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +118 -0
  3. package/dist/bench/metrics.d.ts +31 -0
  4. package/dist/bench/metrics.js +122 -0
  5. package/dist/bench/metrics.js.map +1 -0
  6. package/dist/bench/runner.d.ts +27 -0
  7. package/dist/bench/runner.js +184 -0
  8. package/dist/bench/runner.js.map +1 -0
  9. package/dist/cli.d.ts +2 -0
  10. package/dist/cli.js +42 -0
  11. package/dist/cli.js.map +1 -0
  12. package/dist/commands/bench.d.ts +11 -0
  13. package/dist/commands/bench.js +23 -0
  14. package/dist/commands/bench.js.map +1 -0
  15. package/dist/commands/doctor.d.ts +5 -0
  16. package/dist/commands/doctor.js +246 -0
  17. package/dist/commands/doctor.js.map +1 -0
  18. package/dist/commands/init.d.ts +8 -0
  19. package/dist/commands/init.js +319 -0
  20. package/dist/commands/init.js.map +1 -0
  21. package/dist/commands/stats.d.ts +5 -0
  22. package/dist/commands/stats.js +87 -0
  23. package/dist/commands/stats.js.map +1 -0
  24. package/dist/deliver/ci.d.ts +4 -0
  25. package/dist/deliver/ci.js +62 -0
  26. package/dist/deliver/ci.js.map +1 -0
  27. package/dist/deliver/claudemd.d.ts +9 -0
  28. package/dist/deliver/claudemd.js +51 -0
  29. package/dist/deliver/claudemd.js.map +1 -0
  30. package/dist/deliver/cross-tool.d.ts +10 -0
  31. package/dist/deliver/cross-tool.js +49 -0
  32. package/dist/deliver/cross-tool.js.map +1 -0
  33. package/dist/deliver/git-hook.d.ts +10 -0
  34. package/dist/deliver/git-hook.js +104 -0
  35. package/dist/deliver/git-hook.js.map +1 -0
  36. package/dist/deliver/hooks.d.ts +9 -0
  37. package/dist/deliver/hooks.js +315 -0
  38. package/dist/deliver/hooks.js.map +1 -0
  39. package/dist/extract/complexity.d.ts +16 -0
  40. package/dist/extract/complexity.js +46 -0
  41. package/dist/extract/complexity.js.map +1 -0
  42. package/dist/extract/conventions.d.ts +18 -0
  43. package/dist/extract/conventions.js +143 -0
  44. package/dist/extract/conventions.js.map +1 -0
  45. package/dist/extract/depgraph.d.ts +12 -0
  46. package/dist/extract/depgraph.js +70 -0
  47. package/dist/extract/depgraph.js.map +1 -0
  48. package/dist/extract/env.d.ts +17 -0
  49. package/dist/extract/env.js +142 -0
  50. package/dist/extract/env.js.map +1 -0
  51. package/dist/extract/error-patterns.d.ts +15 -0
  52. package/dist/extract/error-patterns.js +107 -0
  53. package/dist/extract/error-patterns.js.map +1 -0
  54. package/dist/extract/frontend.d.ts +30 -0
  55. package/dist/extract/frontend.js +244 -0
  56. package/dist/extract/frontend.js.map +1 -0
  57. package/dist/extract/gotchas.d.ts +12 -0
  58. package/dist/extract/gotchas.js +145 -0
  59. package/dist/extract/gotchas.js.map +1 -0
  60. package/dist/extract/history.d.ts +29 -0
  61. package/dist/extract/history.js +91 -0
  62. package/dist/extract/history.js.map +1 -0
  63. package/dist/extract/infra.d.ts +27 -0
  64. package/dist/extract/infra.js +226 -0
  65. package/dist/extract/infra.js.map +1 -0
  66. package/dist/extract/monorepo.d.ts +16 -0
  67. package/dist/extract/monorepo.js +135 -0
  68. package/dist/extract/monorepo.js.map +1 -0
  69. package/dist/extract/routes.d.ts +16 -0
  70. package/dist/extract/routes.js +156 -0
  71. package/dist/extract/routes.js.map +1 -0
  72. package/dist/extract/scanner.d.ts +18 -0
  73. package/dist/extract/scanner.js +109 -0
  74. package/dist/extract/scanner.js.map +1 -0
  75. package/dist/extract/schema.d.ts +28 -0
  76. package/dist/extract/schema.js +192 -0
  77. package/dist/extract/schema.js.map +1 -0
  78. package/dist/extract/scripts.d.ts +18 -0
  79. package/dist/extract/scripts.js +104 -0
  80. package/dist/extract/scripts.js.map +1 -0
  81. package/dist/extract/security.d.ts +20 -0
  82. package/dist/extract/security.js +95 -0
  83. package/dist/extract/security.js.map +1 -0
  84. package/dist/extract/signatures.d.ts +33 -0
  85. package/dist/extract/signatures.js +608 -0
  86. package/dist/extract/signatures.js.map +1 -0
  87. package/dist/extract/staleness.d.ts +16 -0
  88. package/dist/extract/staleness.js +108 -0
  89. package/dist/extract/staleness.js.map +1 -0
  90. package/dist/extract/tests.d.ts +16 -0
  91. package/dist/extract/tests.js +175 -0
  92. package/dist/extract/tests.js.map +1 -0
  93. package/dist/extract/usage-examples.d.ts +17 -0
  94. package/dist/extract/usage-examples.js +115 -0
  95. package/dist/extract/usage-examples.js.map +1 -0
  96. package/dist/generate/index-file.d.ts +29 -0
  97. package/dist/generate/index-file.js +168 -0
  98. package/dist/generate/index-file.js.map +1 -0
  99. package/dist/generate/rules.d.ts +6 -0
  100. package/dist/generate/rules.js +94 -0
  101. package/dist/generate/rules.js.map +1 -0
  102. package/dist/generate/skeleton.d.ts +14 -0
  103. package/dist/generate/skeleton.js +145 -0
  104. package/dist/generate/skeleton.js.map +1 -0
  105. package/dist/learn/tracker.d.ts +54 -0
  106. package/dist/learn/tracker.js +129 -0
  107. package/dist/learn/tracker.js.map +1 -0
  108. package/dist/utils/detect.d.ts +16 -0
  109. package/dist/utils/detect.js +188 -0
  110. package/dist/utils/detect.js.map +1 -0
  111. package/dist/utils/pagerank.d.ts +19 -0
  112. package/dist/utils/pagerank.js +52 -0
  113. package/dist/utils/pagerank.js.map +1 -0
  114. package/dist/utils/tokens.d.ts +11 -0
  115. package/dist/utils/tokens.js +27 -0
  116. package/dist/utils/tokens.js.map +1 -0
  117. package/package.json +43 -0
@@ -0,0 +1,108 @@
1
+ import { existsSync, readFileSync, statSync } from "fs";
2
+ import { join } from "path";
3
+ import { execSync } from "child_process";
4
+ /**
5
+ * Check if the briefed context is stale (source files changed since last index).
6
+ */
7
+ export function checkStaleness(root) {
8
+ const report = {
9
+ isStale: false,
10
+ lastIndexed: null,
11
+ changedFiles: 0,
12
+ totalFiles: 0,
13
+ stalePct: 0,
14
+ details: [],
15
+ };
16
+ const indexPath = join(root, ".briefed", "index.json");
17
+ if (!existsSync(indexPath)) {
18
+ report.isStale = true;
19
+ report.details.push("No index found — run briefed init");
20
+ return report;
21
+ }
22
+ // Get last indexed time
23
+ const indexStat = statSync(indexPath);
24
+ report.lastIndexed = indexStat.mtime;
25
+ // Use git to find files changed since last index
26
+ try {
27
+ const sinceDate = indexStat.mtime.toISOString();
28
+ const changedOutput = execSync(`git diff --name-only --diff-filter=ACMR HEAD -- "*.ts" "*.tsx" "*.js" "*.jsx" "*.py" "*.go" "*.rs" "*.java"`, { cwd: root, encoding: "utf-8", timeout: 5000, stdio: ["pipe", "pipe", "pipe"] }).trim();
29
+ // Also check for uncommitted changes
30
+ const uncommitted = execSync(`git status --porcelain -- "*.ts" "*.tsx" "*.js" "*.jsx" "*.py" "*.go" "*.rs" "*.java"`, { cwd: root, encoding: "utf-8", timeout: 5000, stdio: ["pipe", "pipe", "pipe"] }).trim();
31
+ const changed = new Set();
32
+ if (changedOutput) {
33
+ for (const f of changedOutput.split("\n"))
34
+ changed.add(f.trim());
35
+ }
36
+ if (uncommitted) {
37
+ for (const line of uncommitted.split("\n")) {
38
+ const file = line.trim().slice(3); // strip status chars
39
+ if (file)
40
+ changed.add(file);
41
+ }
42
+ }
43
+ // Check which changed files are newer than the index
44
+ let staleCount = 0;
45
+ for (const file of changed) {
46
+ const fullPath = join(root, file);
47
+ if (existsSync(fullPath)) {
48
+ const fileStat = statSync(fullPath);
49
+ if (fileStat.mtime > indexStat.mtime) {
50
+ staleCount++;
51
+ if (staleCount <= 5) {
52
+ report.details.push(` changed: ${file}`);
53
+ }
54
+ }
55
+ }
56
+ }
57
+ if (staleCount > 5) {
58
+ report.details.push(` ... and ${staleCount - 5} more`);
59
+ }
60
+ report.changedFiles = staleCount;
61
+ // Load index to get total file count
62
+ try {
63
+ const index = JSON.parse(readFileSync(indexPath, "utf-8"));
64
+ report.totalFiles = index.modules?.reduce((sum, m) => sum + (m.files?.length || 0), 0) || 0;
65
+ }
66
+ catch { /* skip */ }
67
+ report.stalePct = report.totalFiles > 0
68
+ ? Math.round((staleCount / report.totalFiles) * 100)
69
+ : 0;
70
+ // Consider stale if >10% of files changed or >5 files changed
71
+ report.isStale = staleCount > 5 || report.stalePct > 10;
72
+ }
73
+ catch {
74
+ // Not a git repo or git not available — check file timestamps
75
+ report.details.push("No git available — using timestamp comparison");
76
+ report.isStale = true;
77
+ }
78
+ return report;
79
+ }
80
+ /**
81
+ * Format staleness report for display.
82
+ */
83
+ export function formatStaleness(report) {
84
+ if (!report.isStale) {
85
+ const age = report.lastIndexed
86
+ ? timeSince(report.lastIndexed)
87
+ : "unknown";
88
+ return ` Context is fresh (indexed ${age} ago)`;
89
+ }
90
+ const lines = [];
91
+ lines.push(` Context is STALE — ${report.changedFiles} files changed since last index`);
92
+ for (const d of report.details) {
93
+ lines.push(d);
94
+ }
95
+ lines.push(` Run: npx briefed init`);
96
+ return lines.join("\n");
97
+ }
98
+ function timeSince(date) {
99
+ const secs = Math.floor((Date.now() - date.getTime()) / 1000);
100
+ if (secs < 60)
101
+ return `${secs}s`;
102
+ if (secs < 3600)
103
+ return `${Math.floor(secs / 60)}m`;
104
+ if (secs < 86400)
105
+ return `${Math.floor(secs / 3600)}h`;
106
+ return `${Math.floor(secs / 86400)}d`;
107
+ }
108
+ //# sourceMappingURL=staleness.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"staleness.js","sourceRoot":"","sources":["../../src/extract/staleness.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ export interface TestMapping {
2
+ sourceFile: string;
3
+ testFile: string;
4
+ testNames: string[];
5
+ testCount: number;
6
+ }
7
+ /**
8
+ * Find test files that correspond to source files.
9
+ * Uses naming conventions: foo.test.ts, foo.spec.ts, test_foo.py, etc.
10
+ * This is the #1 research-backed improvement: +45.97% pass@1 (TiCoder, IEEE TSE).
11
+ */
12
+ export declare function findTestMappings(sourceFiles: string[], root: string): TestMapping[];
13
+ /**
14
+ * Format test mappings for inclusion in the skeleton or contracts.
15
+ */
16
+ export declare function formatTestContext(mapping: TestMapping): string;
@@ -0,0 +1,175 @@
1
+ import { readFileSync } from "fs";
2
+ import { join, dirname, basename, extname } from "path";
3
+ import { glob } from "glob";
4
+ /**
5
+ * Find test files that correspond to source files.
6
+ * Uses naming conventions: foo.test.ts, foo.spec.ts, test_foo.py, etc.
7
+ * This is the #1 research-backed improvement: +45.97% pass@1 (TiCoder, IEEE TSE).
8
+ */
9
+ export function findTestMappings(sourceFiles, root) {
10
+ const mappings = [];
11
+ // Build a map of all test files for fast lookup
12
+ const testFileSet = new Set();
13
+ const allFiles = sourceFiles;
14
+ for (const f of allFiles) {
15
+ if (isTestFile(f))
16
+ testFileSet.add(f);
17
+ }
18
+ // Also scan for test files not in the source list (test/ directories)
19
+ try {
20
+ const testGlobs = [
21
+ "test/**/*.{ts,tsx,js,jsx,py,go,rs}",
22
+ "tests/**/*.{ts,tsx,js,jsx,py,go,rs}",
23
+ "**/*.test.{ts,tsx,js,jsx}",
24
+ "**/*.spec.{ts,tsx,js,jsx}",
25
+ "**/test_*.py",
26
+ "**/*_test.go",
27
+ "**/*_test.rs",
28
+ ];
29
+ for (const pattern of testGlobs) {
30
+ const found = glob.sync(pattern, {
31
+ cwd: root,
32
+ ignore: ["node_modules/**", ".git/**", "dist/**", "build/**"],
33
+ });
34
+ for (const f of found)
35
+ testFileSet.add(f.replace(/\\/g, "/"));
36
+ }
37
+ }
38
+ catch {
39
+ // glob errors are non-fatal
40
+ }
41
+ // Match source files to test files
42
+ for (const sourceFile of sourceFiles) {
43
+ if (isTestFile(sourceFile))
44
+ continue;
45
+ const matched = findMatchingTest(sourceFile, testFileSet);
46
+ if (matched) {
47
+ const testPath = join(root, matched);
48
+ let testNames = [];
49
+ let testCount = 0;
50
+ try {
51
+ const content = readFileSync(testPath, "utf-8");
52
+ const extracted = extractTestNames(content, extname(matched));
53
+ testNames = extracted.names;
54
+ testCount = extracted.count;
55
+ }
56
+ catch {
57
+ // Can't read test file — still map it
58
+ }
59
+ mappings.push({
60
+ sourceFile,
61
+ testFile: matched,
62
+ testNames,
63
+ testCount,
64
+ });
65
+ }
66
+ }
67
+ return mappings;
68
+ }
69
+ /**
70
+ * Check if a file path looks like a test file.
71
+ */
72
+ function isTestFile(filePath) {
73
+ const name = basename(filePath);
74
+ return (name.includes(".test.") ||
75
+ name.includes(".spec.") ||
76
+ name.includes("_test.") ||
77
+ name.startsWith("test_") ||
78
+ filePath.includes("/test/") ||
79
+ filePath.includes("/tests/") ||
80
+ filePath.includes("/__tests__/"));
81
+ }
82
+ /**
83
+ * Find the test file that matches a given source file.
84
+ */
85
+ function findMatchingTest(sourceFile, testFiles) {
86
+ const dir = dirname(sourceFile);
87
+ const ext = extname(sourceFile);
88
+ const name = basename(sourceFile, ext);
89
+ // Try common test file naming patterns
90
+ const candidates = [
91
+ // Colocated: foo.test.ts next to foo.ts
92
+ `${dir}/${name}.test${ext}`,
93
+ `${dir}/${name}.spec${ext}`,
94
+ // __tests__ directory
95
+ `${dir}/__tests__/${name}.test${ext}`,
96
+ `${dir}/__tests__/${name}${ext}`,
97
+ // test/ directory at same level
98
+ `${dir.replace(/\/src\//, "/test/")}/${name}.test${ext}`,
99
+ `${dir.replace(/\/src\//, "/test/")}/${name}${ext}`,
100
+ `${dir.replace(/\/src\//, "/tests/")}/${name}.test${ext}`,
101
+ // test/ at root
102
+ `test/${name}.test${ext}`,
103
+ `test/${name}${ext}`,
104
+ `tests/${name}.test${ext}`,
105
+ // Python conventions
106
+ `${dir}/test_${name}${ext}`,
107
+ `test/test_${name}${ext}`,
108
+ `tests/test_${name}${ext}`,
109
+ // Go conventions
110
+ `${dir}/${name}_test${ext}`,
111
+ // Rust conventions
112
+ `tests/${name}${ext}`,
113
+ ];
114
+ for (const candidate of candidates) {
115
+ const normalized = candidate.replace(/\\/g, "/");
116
+ if (testFiles.has(normalized))
117
+ return normalized;
118
+ }
119
+ // Fuzzy match: look for any test file containing the source name
120
+ for (const testFile of testFiles) {
121
+ const testName = basename(testFile, extname(testFile))
122
+ .replace(/\.test$|\.spec$|^test_|_test$/, "");
123
+ if (testName === name)
124
+ return testFile;
125
+ }
126
+ return null;
127
+ }
128
+ /**
129
+ * Extract test names (describe/it/test blocks) from a test file.
130
+ */
131
+ function extractTestNames(content, ext) {
132
+ const names = [];
133
+ if ([".ts", ".tsx", ".js", ".jsx", ".mjs"].includes(ext)) {
134
+ // JS/TS: describe("name"), it("name"), test("name")
135
+ const regex = /(?:describe|it|test)\s*\(\s*['"`]([^'"`]+)['"`]/g;
136
+ for (const match of content.matchAll(regex)) {
137
+ names.push(match[1]);
138
+ }
139
+ }
140
+ else if (ext === ".py") {
141
+ // Python: def test_name, class TestName
142
+ const fnRegex = /def\s+(test_\w+)/g;
143
+ const classRegex = /class\s+(Test\w+)/g;
144
+ for (const match of content.matchAll(fnRegex))
145
+ names.push(match[1]);
146
+ for (const match of content.matchAll(classRegex))
147
+ names.push(match[1]);
148
+ }
149
+ else if (ext === ".go") {
150
+ // Go: func TestName(t *testing.T)
151
+ const regex = /func\s+(Test\w+)\s*\(/g;
152
+ for (const match of content.matchAll(regex))
153
+ names.push(match[1]);
154
+ }
155
+ else if (ext === ".rs") {
156
+ // Rust: #[test] fn test_name()
157
+ const regex = /fn\s+(test_\w+)\s*\(/g;
158
+ for (const match of content.matchAll(regex))
159
+ names.push(match[1]);
160
+ }
161
+ return { names, count: names.length };
162
+ }
163
+ /**
164
+ * Format test mappings for inclusion in the skeleton or contracts.
165
+ */
166
+ export function formatTestContext(mapping) {
167
+ const lines = [];
168
+ lines.push(`tests: ${mapping.testFile} (${mapping.testCount} tests)`);
169
+ if (mapping.testNames.length > 0) {
170
+ const preview = mapping.testNames.slice(0, 8);
171
+ lines.push(` covers: ${preview.join(", ")}${mapping.testNames.length > 8 ? ` +${mapping.testNames.length - 8} more` : ""}`);
172
+ }
173
+ return lines.join("\n");
174
+ }
175
+ //# sourceMappingURL=tests.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tests.js","sourceRoot":"","sources":["../../src/extract/tests.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ import type { FileExtraction } from "./signatures.js";
2
+ export interface UsageExample {
3
+ symbol: string;
4
+ file: string;
5
+ line: number;
6
+ snippet: string;
7
+ }
8
+ /**
9
+ * Find how functions/classes are actually USED in the codebase.
10
+ * Research shows API usage examples improve LLM output by 3x vs descriptions alone.
11
+ * Instead of telling Claude what a function does, show it how the project calls it.
12
+ */
13
+ export declare function findUsageExamples(extractions: FileExtraction[], maxExamplesPerSymbol?: number): Map<string, UsageExample[]>;
14
+ /**
15
+ * Format usage examples for context injection.
16
+ */
17
+ export declare function formatUsageExamples(examples: Map<string, UsageExample[]>, maxTotal?: number): string;
@@ -0,0 +1,115 @@
1
+ import { readFileSync } from "fs";
2
+ import { basename } from "path";
3
+ /**
4
+ * Find how functions/classes are actually USED in the codebase.
5
+ * Research shows API usage examples improve LLM output by 3x vs descriptions alone.
6
+ * Instead of telling Claude what a function does, show it how the project calls it.
7
+ */
8
+ export function findUsageExamples(extractions, maxExamplesPerSymbol = 3) {
9
+ const examples = new Map();
10
+ // Build a set of all exported symbol names worth tracking
11
+ const exportedSymbols = new Map(); // name → defining file
12
+ for (const ext of extractions) {
13
+ for (const sym of ext.symbols) {
14
+ if (sym.exported && (sym.kind === "function" || sym.kind === "class" || sym.kind === "method")) {
15
+ const shortName = sym.name.split(".").pop();
16
+ if (shortName.length > 2 && !isCommonName(shortName)) {
17
+ exportedSymbols.set(shortName, ext.path);
18
+ }
19
+ }
20
+ }
21
+ }
22
+ if (exportedSymbols.size === 0)
23
+ return examples;
24
+ // Scan files for usages of these symbols
25
+ for (const ext of extractions) {
26
+ // Only look in files that import something
27
+ if (ext.imports.length === 0)
28
+ continue;
29
+ let content;
30
+ try {
31
+ content = readFileSync(ext.path, "utf-8");
32
+ }
33
+ catch {
34
+ continue;
35
+ }
36
+ const lines = content.split("\n");
37
+ // What does this file import?
38
+ const importedNames = new Set();
39
+ for (const imp of ext.imports) {
40
+ for (const name of imp.names) {
41
+ importedNames.add(name);
42
+ }
43
+ }
44
+ // Find lines where imported symbols are called/used
45
+ for (let i = 0; i < lines.length; i++) {
46
+ const line = lines[i].trim();
47
+ // Skip imports, comments, empty lines
48
+ if (!line || line.startsWith("import ") || line.startsWith("//") ||
49
+ line.startsWith("/*") || line.startsWith("*") || line.startsWith("from ") ||
50
+ line.startsWith("require("))
51
+ continue;
52
+ for (const symName of importedNames) {
53
+ if (!exportedSymbols.has(symName))
54
+ continue;
55
+ // Check if this line calls/uses the symbol
56
+ // Match: symName( or new SymName( or symName. or await symName(
57
+ const callPattern = new RegExp(`\\b${escapeRegex(symName)}\\s*[.(]`);
58
+ if (callPattern.test(line)) {
59
+ if (!examples.has(symName))
60
+ examples.set(symName, []);
61
+ const existing = examples.get(symName);
62
+ if (existing.length < maxExamplesPerSymbol) {
63
+ // Don't duplicate from same file
64
+ if (!existing.some((e) => e.file === ext.path)) {
65
+ existing.push({
66
+ symbol: symName,
67
+ file: ext.path,
68
+ line: i + 1,
69
+ snippet: line.slice(0, 120), // truncate long lines
70
+ });
71
+ }
72
+ }
73
+ }
74
+ }
75
+ }
76
+ }
77
+ return examples;
78
+ }
79
+ /**
80
+ * Format usage examples for context injection.
81
+ */
82
+ export function formatUsageExamples(examples, maxTotal = 20) {
83
+ if (examples.size === 0)
84
+ return "";
85
+ const lines = [];
86
+ let count = 0;
87
+ // Sort by number of usages (most-used first)
88
+ const sorted = [...examples.entries()]
89
+ .sort((a, b) => b[1].length - a[1].length);
90
+ for (const [symbol, usages] of sorted) {
91
+ if (count >= maxTotal)
92
+ break;
93
+ for (const usage of usages) {
94
+ if (count >= maxTotal)
95
+ break;
96
+ lines.push(` ${symbol}: ${usage.snippet} (${basename(usage.file)}:${usage.line})`);
97
+ count++;
98
+ }
99
+ }
100
+ if (lines.length === 0)
101
+ return "";
102
+ return "Usage examples:\n" + lines.join("\n");
103
+ }
104
+ function isCommonName(name) {
105
+ const common = new Set([
106
+ "get", "set", "map", "filter", "reduce", "find", "push", "pop",
107
+ "log", "error", "warn", "info", "debug", "toString", "valueOf",
108
+ "then", "catch", "finally", "resolve", "reject", "use", "run",
109
+ ]);
110
+ return common.has(name);
111
+ }
112
+ function escapeRegex(str) {
113
+ return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
114
+ }
115
+ //# sourceMappingURL=usage-examples.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"usage-examples.js","sourceRoot":"","sources":["../../src/extract/usage-examples.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,29 @@
1
+ import type { FileExtraction } from "../extract/signatures.js";
2
+ import type { DepGraph } from "../extract/depgraph.js";
3
+ import type { ComplexityScore } from "../extract/complexity.js";
4
+ export interface ModuleEntry {
5
+ name: string;
6
+ dir: string;
7
+ files: string[];
8
+ keywords: string[];
9
+ complexity: number;
10
+ file: string;
11
+ }
12
+ export interface ModuleIndex {
13
+ modules: ModuleEntry[];
14
+ generated: string;
15
+ }
16
+ /**
17
+ * Group files into logical modules and generate an index for prompt matching.
18
+ * Modules are primarily directory-based with keyword extraction.
19
+ */
20
+ export declare function generateModuleIndex(extractions: FileExtraction[], depGraph: DepGraph, complexity: ComplexityScore[], root: string): ModuleIndex;
21
+ /**
22
+ * Write the module index to .briefed/index.json
23
+ */
24
+ export declare function writeModuleIndex(root: string, index: ModuleIndex): void;
25
+ /**
26
+ * Generate simple contract files for each module (non-LLM version).
27
+ * Extracts structural info: purpose, exports, deps, complexity.
28
+ */
29
+ export declare function generateSimpleContracts(index: ModuleIndex, extractions: FileExtraction[], depGraph: DepGraph, root: string): void;
@@ -0,0 +1,168 @@
1
+ import { dirname, basename } from "path";
2
+ import { writeFileSync, mkdirSync, existsSync } from "fs";
3
+ import { join } from "path";
4
+ import yaml from "js-yaml";
5
+ /**
6
+ * Group files into logical modules and generate an index for prompt matching.
7
+ * Modules are primarily directory-based with keyword extraction.
8
+ */
9
+ export function generateModuleIndex(extractions, depGraph, complexity, root) {
10
+ // Group files by directory
11
+ const dirGroups = new Map();
12
+ for (const ext of extractions) {
13
+ const dir = dirname(ext.path);
14
+ if (!dirGroups.has(dir))
15
+ dirGroups.set(dir, []);
16
+ dirGroups.get(dir).push(ext);
17
+ }
18
+ const modules = [];
19
+ for (const [dir, files] of dirGroups) {
20
+ if (files.length === 0)
21
+ continue;
22
+ // Extract keywords from:
23
+ // 1. Directory name parts
24
+ // 2. File names
25
+ // 3. Exported symbol names
26
+ const keywords = new Set();
27
+ // Directory parts
28
+ for (const part of dir.split("/")) {
29
+ if (part && part.length > 2) {
30
+ keywords.add(part.toLowerCase());
31
+ // Split camelCase/PascalCase
32
+ for (const word of splitIdentifier(part)) {
33
+ if (word.length > 2)
34
+ keywords.add(word.toLowerCase());
35
+ }
36
+ }
37
+ }
38
+ // File names (without extension)
39
+ for (const f of files) {
40
+ const name = basename(f.path).replace(/\.[^.]+$/, "");
41
+ keywords.add(name.toLowerCase());
42
+ for (const word of splitIdentifier(name)) {
43
+ if (word.length > 2)
44
+ keywords.add(word.toLowerCase());
45
+ }
46
+ }
47
+ // Exported symbol names
48
+ for (const f of files) {
49
+ for (const sym of f.symbols.filter((s) => s.exported)) {
50
+ const name = sym.name.split(".").pop();
51
+ keywords.add(name.toLowerCase());
52
+ for (const word of splitIdentifier(name)) {
53
+ if (word.length > 2)
54
+ keywords.add(word.toLowerCase());
55
+ }
56
+ }
57
+ }
58
+ // Remove very common/generic keywords
59
+ const genericWords = new Set([
60
+ "src", "lib", "app", "index", "utils", "helpers", "types",
61
+ "const", "config", "common", "shared", "core", "main",
62
+ ]);
63
+ for (const gw of genericWords)
64
+ keywords.delete(gw);
65
+ // Compute average complexity
66
+ const fileComplexities = files
67
+ .map((f) => complexity.find((c) => c.file === f.path)?.score || 0);
68
+ const avgComplexity = fileComplexities.length > 0
69
+ ? fileComplexities.reduce((s, v) => s + v, 0) / fileComplexities.length
70
+ : 0;
71
+ const safeName = dir.replace(/[\/\\]/g, "-").replace(/^-/, "") || "root";
72
+ modules.push({
73
+ name: safeName,
74
+ dir,
75
+ files: files.map((f) => f.path),
76
+ keywords: [...keywords],
77
+ complexity: Math.round(avgComplexity * 10) / 10,
78
+ file: `${safeName}.yaml`,
79
+ });
80
+ }
81
+ // Sort by complexity (most complex first — they need context most)
82
+ modules.sort((a, b) => b.complexity - a.complexity);
83
+ return {
84
+ modules,
85
+ generated: new Date().toISOString(),
86
+ };
87
+ }
88
+ /**
89
+ * Write the module index to .briefed/index.json
90
+ */
91
+ export function writeModuleIndex(root, index) {
92
+ const briefedDir = join(root, ".briefed");
93
+ if (!existsSync(briefedDir))
94
+ mkdirSync(briefedDir, { recursive: true });
95
+ writeFileSync(join(briefedDir, "index.json"), JSON.stringify(index, null, 2));
96
+ }
97
+ /**
98
+ * Generate simple contract files for each module (non-LLM version).
99
+ * Extracts structural info: purpose, exports, deps, complexity.
100
+ */
101
+ export function generateSimpleContracts(index, extractions, depGraph, root) {
102
+ const contractsDir = join(root, ".briefed", "contracts");
103
+ if (!existsSync(contractsDir))
104
+ mkdirSync(contractsDir, { recursive: true });
105
+ for (const mod of index.modules) {
106
+ const modExtractions = extractions.filter((e) => mod.files.includes(e.path));
107
+ const exports = modExtractions.flatMap((e) => e.symbols.filter((s) => s.exported));
108
+ // Get dependencies (other modules this one imports from)
109
+ const deps = new Set();
110
+ for (const ext of modExtractions) {
111
+ for (const imp of ext.imports) {
112
+ if (imp.isRelative) {
113
+ // Find which module this import belongs to
114
+ for (const otherMod of index.modules) {
115
+ if (otherMod.name === mod.name)
116
+ continue;
117
+ if (otherMod.files.some((f) => imp.source.includes(basename(f).replace(/\.[^.]+$/, "")))) {
118
+ deps.add(otherMod.name);
119
+ }
120
+ }
121
+ }
122
+ }
123
+ }
124
+ // Get dependents (other modules that import from this one)
125
+ const dependents = new Set();
126
+ for (const otherMod of index.modules) {
127
+ if (otherMod.name === mod.name)
128
+ continue;
129
+ const otherExtractions = extractions.filter((e) => otherMod.files.includes(e.path));
130
+ for (const ext of otherExtractions) {
131
+ for (const imp of ext.imports) {
132
+ if (imp.isRelative && mod.files.some((f) => imp.source.includes(basename(f).replace(/\.[^.]+$/, "")))) {
133
+ dependents.add(otherMod.name);
134
+ }
135
+ }
136
+ }
137
+ }
138
+ const contract = {
139
+ module: mod.dir,
140
+ files: mod.files.length,
141
+ complexity: mod.complexity,
142
+ };
143
+ if (exports.length > 0) {
144
+ contract.exports = exports.slice(0, 15).map((s) => s.signature);
145
+ }
146
+ if (deps.size > 0) {
147
+ contract.dependencies = [...deps];
148
+ }
149
+ if (dependents.size > 0) {
150
+ contract.dependents = [...dependents];
151
+ }
152
+ const yamlContent = yaml.dump(contract, {
153
+ indent: 2,
154
+ lineWidth: 120,
155
+ noRefs: true,
156
+ });
157
+ writeFileSync(join(contractsDir, mod.file), yamlContent);
158
+ }
159
+ }
160
+ /** Split camelCase/PascalCase/snake_case identifiers into words */
161
+ function splitIdentifier(name) {
162
+ return name
163
+ .replace(/([a-z])([A-Z])/g, "$1 $2")
164
+ .replace(/[_-]/g, " ")
165
+ .split(/\s+/)
166
+ .filter((w) => w.length > 0);
167
+ }
168
+ //# sourceMappingURL=index-file.js.map