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.
- package/LICENSE +21 -0
- package/README.md +118 -0
- package/dist/bench/metrics.d.ts +31 -0
- package/dist/bench/metrics.js +122 -0
- package/dist/bench/metrics.js.map +1 -0
- package/dist/bench/runner.d.ts +27 -0
- package/dist/bench/runner.js +184 -0
- package/dist/bench/runner.js.map +1 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +42 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/bench.d.ts +11 -0
- package/dist/commands/bench.js +23 -0
- package/dist/commands/bench.js.map +1 -0
- package/dist/commands/doctor.d.ts +5 -0
- package/dist/commands/doctor.js +246 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/init.d.ts +8 -0
- package/dist/commands/init.js +319 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/stats.d.ts +5 -0
- package/dist/commands/stats.js +87 -0
- package/dist/commands/stats.js.map +1 -0
- package/dist/deliver/ci.d.ts +4 -0
- package/dist/deliver/ci.js +62 -0
- package/dist/deliver/ci.js.map +1 -0
- package/dist/deliver/claudemd.d.ts +9 -0
- package/dist/deliver/claudemd.js +51 -0
- package/dist/deliver/claudemd.js.map +1 -0
- package/dist/deliver/cross-tool.d.ts +10 -0
- package/dist/deliver/cross-tool.js +49 -0
- package/dist/deliver/cross-tool.js.map +1 -0
- package/dist/deliver/git-hook.d.ts +10 -0
- package/dist/deliver/git-hook.js +104 -0
- package/dist/deliver/git-hook.js.map +1 -0
- package/dist/deliver/hooks.d.ts +9 -0
- package/dist/deliver/hooks.js +315 -0
- package/dist/deliver/hooks.js.map +1 -0
- package/dist/extract/complexity.d.ts +16 -0
- package/dist/extract/complexity.js +46 -0
- package/dist/extract/complexity.js.map +1 -0
- package/dist/extract/conventions.d.ts +18 -0
- package/dist/extract/conventions.js +143 -0
- package/dist/extract/conventions.js.map +1 -0
- package/dist/extract/depgraph.d.ts +12 -0
- package/dist/extract/depgraph.js +70 -0
- package/dist/extract/depgraph.js.map +1 -0
- package/dist/extract/env.d.ts +17 -0
- package/dist/extract/env.js +142 -0
- package/dist/extract/env.js.map +1 -0
- package/dist/extract/error-patterns.d.ts +15 -0
- package/dist/extract/error-patterns.js +107 -0
- package/dist/extract/error-patterns.js.map +1 -0
- package/dist/extract/frontend.d.ts +30 -0
- package/dist/extract/frontend.js +244 -0
- package/dist/extract/frontend.js.map +1 -0
- package/dist/extract/gotchas.d.ts +12 -0
- package/dist/extract/gotchas.js +145 -0
- package/dist/extract/gotchas.js.map +1 -0
- package/dist/extract/history.d.ts +29 -0
- package/dist/extract/history.js +91 -0
- package/dist/extract/history.js.map +1 -0
- package/dist/extract/infra.d.ts +27 -0
- package/dist/extract/infra.js +226 -0
- package/dist/extract/infra.js.map +1 -0
- package/dist/extract/monorepo.d.ts +16 -0
- package/dist/extract/monorepo.js +135 -0
- package/dist/extract/monorepo.js.map +1 -0
- package/dist/extract/routes.d.ts +16 -0
- package/dist/extract/routes.js +156 -0
- package/dist/extract/routes.js.map +1 -0
- package/dist/extract/scanner.d.ts +18 -0
- package/dist/extract/scanner.js +109 -0
- package/dist/extract/scanner.js.map +1 -0
- package/dist/extract/schema.d.ts +28 -0
- package/dist/extract/schema.js +192 -0
- package/dist/extract/schema.js.map +1 -0
- package/dist/extract/scripts.d.ts +18 -0
- package/dist/extract/scripts.js +104 -0
- package/dist/extract/scripts.js.map +1 -0
- package/dist/extract/security.d.ts +20 -0
- package/dist/extract/security.js +95 -0
- package/dist/extract/security.js.map +1 -0
- package/dist/extract/signatures.d.ts +33 -0
- package/dist/extract/signatures.js +608 -0
- package/dist/extract/signatures.js.map +1 -0
- package/dist/extract/staleness.d.ts +16 -0
- package/dist/extract/staleness.js +108 -0
- package/dist/extract/staleness.js.map +1 -0
- package/dist/extract/tests.d.ts +16 -0
- package/dist/extract/tests.js +175 -0
- package/dist/extract/tests.js.map +1 -0
- package/dist/extract/usage-examples.d.ts +17 -0
- package/dist/extract/usage-examples.js +115 -0
- package/dist/extract/usage-examples.js.map +1 -0
- package/dist/generate/index-file.d.ts +29 -0
- package/dist/generate/index-file.js +168 -0
- package/dist/generate/index-file.js.map +1 -0
- package/dist/generate/rules.d.ts +6 -0
- package/dist/generate/rules.js +94 -0
- package/dist/generate/rules.js.map +1 -0
- package/dist/generate/skeleton.d.ts +14 -0
- package/dist/generate/skeleton.js +145 -0
- package/dist/generate/skeleton.js.map +1 -0
- package/dist/learn/tracker.d.ts +54 -0
- package/dist/learn/tracker.js +129 -0
- package/dist/learn/tracker.js.map +1 -0
- package/dist/utils/detect.d.ts +16 -0
- package/dist/utils/detect.js +188 -0
- package/dist/utils/detect.js.map +1 -0
- package/dist/utils/pagerank.d.ts +19 -0
- package/dist/utils/pagerank.js +52 -0
- package/dist/utils/pagerank.js.map +1 -0
- package/dist/utils/tokens.d.ts +11 -0
- package/dist/utils/tokens.js +27 -0
- package/dist/utils/tokens.js.map +1 -0
- package/package.json +43 -0
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync } from "fs";
|
|
2
|
+
import { join } from "path";
|
|
3
|
+
/**
|
|
4
|
+
* Install briefed hooks into .claude/settings.json.
|
|
5
|
+
* Adds SessionStart (compact) and UserPromptSubmit hooks.
|
|
6
|
+
*/
|
|
7
|
+
export function installHooks(root) {
|
|
8
|
+
const claudeDir = join(root, ".claude");
|
|
9
|
+
const settingsPath = join(claudeDir, "settings.json");
|
|
10
|
+
// Ensure .claude directory exists
|
|
11
|
+
if (!existsSync(claudeDir)) {
|
|
12
|
+
mkdirSync(claudeDir, { recursive: true });
|
|
13
|
+
}
|
|
14
|
+
// Load existing settings or create new
|
|
15
|
+
// Security: backup existing settings before modifying
|
|
16
|
+
let settings = {};
|
|
17
|
+
if (existsSync(settingsPath)) {
|
|
18
|
+
try {
|
|
19
|
+
const raw = readFileSync(settingsPath, "utf-8");
|
|
20
|
+
settings = JSON.parse(raw);
|
|
21
|
+
// Backup before modifying
|
|
22
|
+
writeFileSync(settingsPath + ".briefed-backup", raw);
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
settings = {};
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
// Initialize hooks object
|
|
29
|
+
if (!settings.hooks)
|
|
30
|
+
settings.hooks = {};
|
|
31
|
+
// Remove any existing briefed hooks (for idempotency)
|
|
32
|
+
for (const eventName of Object.keys(settings.hooks)) {
|
|
33
|
+
settings.hooks[eventName] = settings.hooks[eventName].filter((entry) => !entry.hooks.some((h) => h.command.includes("briefed")));
|
|
34
|
+
if (settings.hooks[eventName].length === 0) {
|
|
35
|
+
delete settings.hooks[eventName];
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
// Add SessionStart hook (compact matcher) — re-inject skeleton after compaction
|
|
39
|
+
if (!settings.hooks.SessionStart)
|
|
40
|
+
settings.hooks.SessionStart = [];
|
|
41
|
+
settings.hooks.SessionStart.push({
|
|
42
|
+
matcher: "compact",
|
|
43
|
+
hooks: [
|
|
44
|
+
{
|
|
45
|
+
type: "command",
|
|
46
|
+
command: `node "${join(root, ".briefed", "hooks", "session-start.js")}"`,
|
|
47
|
+
timeout: 5,
|
|
48
|
+
},
|
|
49
|
+
],
|
|
50
|
+
});
|
|
51
|
+
// Add UserPromptSubmit hook — dynamic per-prompt context injection
|
|
52
|
+
if (!settings.hooks.UserPromptSubmit)
|
|
53
|
+
settings.hooks.UserPromptSubmit = [];
|
|
54
|
+
settings.hooks.UserPromptSubmit.push({
|
|
55
|
+
hooks: [
|
|
56
|
+
{
|
|
57
|
+
type: "command",
|
|
58
|
+
command: `node "${join(root, ".briefed", "hooks", "prompt-submit.js")}"`,
|
|
59
|
+
timeout: 5,
|
|
60
|
+
},
|
|
61
|
+
],
|
|
62
|
+
});
|
|
63
|
+
// Add PostToolUse hook — learning loop (tracks file reads)
|
|
64
|
+
if (!settings.hooks.PostToolUse)
|
|
65
|
+
settings.hooks.PostToolUse = [];
|
|
66
|
+
settings.hooks.PostToolUse.push({
|
|
67
|
+
matcher: "Read",
|
|
68
|
+
hooks: [
|
|
69
|
+
{
|
|
70
|
+
type: "command",
|
|
71
|
+
command: `node "${join(root, ".briefed", "hooks", "post-read.js")}"`,
|
|
72
|
+
timeout: 2,
|
|
73
|
+
},
|
|
74
|
+
],
|
|
75
|
+
});
|
|
76
|
+
writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n");
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Generate the hook scripts that get executed by Claude Code.
|
|
80
|
+
*/
|
|
81
|
+
export function generateHookScripts(root) {
|
|
82
|
+
const hooksDir = join(root, ".briefed", "hooks");
|
|
83
|
+
mkdirSync(hooksDir, { recursive: true });
|
|
84
|
+
// SessionStart hook — re-inject skeleton after compaction
|
|
85
|
+
const sessionStartScript = `#!/usr/bin/env node
|
|
86
|
+
// briefed: SessionStart hook — re-inject skeleton after compaction
|
|
87
|
+
// Security: only reads from .briefed/ directory, never writes, never persists input
|
|
88
|
+
const { readFileSync, realpathSync } = require("fs");
|
|
89
|
+
const { join, resolve } = require("path");
|
|
90
|
+
|
|
91
|
+
const briefedDir = resolve(join(__dirname, ".."));
|
|
92
|
+
const skeletonPath = join(briefedDir, "skeleton.md");
|
|
93
|
+
|
|
94
|
+
// Verify the skeleton file is inside .briefed/ (prevent path traversal)
|
|
95
|
+
try {
|
|
96
|
+
const realPath = realpathSync(skeletonPath);
|
|
97
|
+
if (!realPath.startsWith(realpathSync(briefedDir))) process.exit(0);
|
|
98
|
+
process.stdout.write(readFileSync(realPath, "utf-8"));
|
|
99
|
+
} catch {}
|
|
100
|
+
`.trim();
|
|
101
|
+
writeFileSync(join(hooksDir, "session-start.js"), sessionStartScript);
|
|
102
|
+
// UserPromptSubmit hook — analyze prompt, inject relevant contracts + dependencies
|
|
103
|
+
const promptSubmitScript = `
|
|
104
|
+
#!/usr/bin/env node
|
|
105
|
+
// briefed: UserPromptSubmit hook — adaptive context injection
|
|
106
|
+
// Security: read-only, never persists prompt data, all file reads scoped to .briefed/
|
|
107
|
+
const { readFileSync, existsSync, realpathSync } = require("fs");
|
|
108
|
+
const { join, resolve } = require("path");
|
|
109
|
+
|
|
110
|
+
const briefedDir = resolve(join(__dirname, ".."));
|
|
111
|
+
const contractsDir = join(briefedDir, "contracts");
|
|
112
|
+
const indexPath = join(briefedDir, "index.json");
|
|
113
|
+
const testMapPath = join(briefedDir, "test-map.json");
|
|
114
|
+
const historyPath = join(briefedDir, "history.json");
|
|
115
|
+
|
|
116
|
+
// Security: verify a path is inside .briefed/ before reading
|
|
117
|
+
function safeRead(filePath) {
|
|
118
|
+
try {
|
|
119
|
+
const real = realpathSync(filePath);
|
|
120
|
+
if (!real.startsWith(realpathSync(briefedDir))) return null;
|
|
121
|
+
return readFileSync(real, "utf-8");
|
|
122
|
+
} catch { return null; }
|
|
123
|
+
}
|
|
124
|
+
function safeExists(filePath) {
|
|
125
|
+
try {
|
|
126
|
+
if (!existsSync(filePath)) return false;
|
|
127
|
+
return realpathSync(filePath).startsWith(realpathSync(briefedDir));
|
|
128
|
+
} catch { return false; }
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Complexity scoring — determines how much context to inject
|
|
132
|
+
function scorePrompt(prompt) {
|
|
133
|
+
let score = 3; // baseline: moderate
|
|
134
|
+
|
|
135
|
+
// High complexity signals (+2 each)
|
|
136
|
+
const highSignals = [/refactor/i, /restructur/i, /redesign/i, /migrat/i, /debug/i, /investigate/i, /\\bwhy\\b/i, /architect/i, /\\bsystem\\b/i, /security/i, /vulnerab/i, /performance/i, /optimiz/i];
|
|
137
|
+
for (const s of highSignals) { if (s.test(prompt)) score += 2; }
|
|
138
|
+
|
|
139
|
+
// Medium signals (+1 each)
|
|
140
|
+
const medSignals = [/\\badd\\b/i, /implement/i, /create/i, /build/i, /integrat/i, /\\btest/i, /endpoint/i, /feature/i, /module/i, /service/i];
|
|
141
|
+
for (const s of medSignals) { if (s.test(prompt)) score += 1; }
|
|
142
|
+
|
|
143
|
+
// Low complexity signals (-2 each)
|
|
144
|
+
const lowSignals = [/typo/i, /rename/i, /comment/i, /\\blog\\b/i, /print/i, /format/i, /lint/i, /spell/i];
|
|
145
|
+
for (const s of lowSignals) { if (s.test(prompt)) score -= 2; }
|
|
146
|
+
|
|
147
|
+
// Long prompts = more complex
|
|
148
|
+
if (prompt.length > 200) score += 1;
|
|
149
|
+
if (prompt.length > 500) score += 1;
|
|
150
|
+
if (prompt.length < 30) score -= 1;
|
|
151
|
+
|
|
152
|
+
// Multiple file/module mentions = complex
|
|
153
|
+
const fileRefs = (prompt.match(/\\w+\\.\\w{2,4}/g) || []).length;
|
|
154
|
+
if (fileRefs >= 2) score += 2;
|
|
155
|
+
|
|
156
|
+
return Math.max(1, Math.min(10, score));
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
let input = "";
|
|
160
|
+
process.stdin.setEncoding("utf-8");
|
|
161
|
+
process.stdin.on("data", (chunk) => { input += chunk; });
|
|
162
|
+
process.stdin.on("end", () => {
|
|
163
|
+
try {
|
|
164
|
+
const data = JSON.parse(input);
|
|
165
|
+
const prompt = (data.prompt || data.message || "").toLowerCase();
|
|
166
|
+
|
|
167
|
+
if (!prompt || !safeExists(indexPath)) {
|
|
168
|
+
process.exit(0);
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// Security: truncate prompt analysis to prevent ReDoS on long inputs
|
|
173
|
+
const safePrompt = prompt.slice(0, 2000);
|
|
174
|
+
const index = JSON.parse(safeRead(indexPath) || "{}");
|
|
175
|
+
if (!index.modules) { process.exit(0); return; }
|
|
176
|
+
const complexity = scorePrompt(safePrompt);
|
|
177
|
+
|
|
178
|
+
// Adaptive budget based on prompt complexity
|
|
179
|
+
// Simple (1-3): 1500 chars (~400 tokens) — contracts only
|
|
180
|
+
// Moderate (4-6): 5000 chars (~1350 tokens) — contracts + deps
|
|
181
|
+
// Complex (7-10): 9000 chars (~2400 tokens) — contracts + deps + tests + history
|
|
182
|
+
const budget = complexity <= 3 ? 1500 : complexity <= 6 ? 5000 : 9000;
|
|
183
|
+
const includeDeps = complexity >= 4;
|
|
184
|
+
const includeTests = complexity >= 7;
|
|
185
|
+
const includeHistory = complexity >= 7;
|
|
186
|
+
|
|
187
|
+
let used = 0;
|
|
188
|
+
const loaded = new Set();
|
|
189
|
+
const output = [];
|
|
190
|
+
|
|
191
|
+
// Score each module by keyword hits
|
|
192
|
+
const scored = index.modules.map((mod) => {
|
|
193
|
+
const keywords = mod.keywords || [];
|
|
194
|
+
const hits = keywords.filter((k) => safePrompt.includes(k.toLowerCase()));
|
|
195
|
+
return { mod, hits: hits.length, complexity: mod.complexity || 0 };
|
|
196
|
+
})
|
|
197
|
+
.filter((s) => s.hits > 0)
|
|
198
|
+
.sort((a, b) => b.hits - a.hits || b.complexity - a.complexity);
|
|
199
|
+
|
|
200
|
+
// Load matching modules + their dependencies
|
|
201
|
+
for (const { mod } of scored) {
|
|
202
|
+
if (used >= budget) break;
|
|
203
|
+
|
|
204
|
+
// Load the module's contract
|
|
205
|
+
if (!loaded.has(mod.file)) {
|
|
206
|
+
const contractPath = join(contractsDir, mod.file);
|
|
207
|
+
const contract = safeRead(contractPath);
|
|
208
|
+
if (contract) {
|
|
209
|
+
if (used + contract.length <= budget) {
|
|
210
|
+
output.push("# Module: " + mod.dir + "\\n" + contract);
|
|
211
|
+
used += contract.length;
|
|
212
|
+
loaded.add(mod.file);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// Load dependency modules (moderate+ complexity)
|
|
218
|
+
if (includeDeps) {
|
|
219
|
+
const contractPath2 = join(contractsDir, mod.file);
|
|
220
|
+
const contractText = safeRead(contractPath2);
|
|
221
|
+
if (contractText) {
|
|
222
|
+
const depMatch = contractText.match(/dependencies:\\n([\\s\\S]*?)(?:\\n\\w|$)/);
|
|
223
|
+
if (depMatch) {
|
|
224
|
+
const deps = depMatch[1].match(/- (.+)/g) || [];
|
|
225
|
+
for (const dep of deps) {
|
|
226
|
+
const depName = dep.replace("- ", "").trim();
|
|
227
|
+
const depMod = index.modules.find((m) => m.name === depName);
|
|
228
|
+
if (depMod && !loaded.has(depMod.file) && used < budget) {
|
|
229
|
+
const depPath = join(contractsDir, depMod.file);
|
|
230
|
+
const depContract = safeRead(depPath);
|
|
231
|
+
if (depContract) {
|
|
232
|
+
if (used + depContract.length <= budget) {
|
|
233
|
+
output.push("# Dependency: " + depMod.dir + "\\n" + depContract);
|
|
234
|
+
used += depContract.length;
|
|
235
|
+
loaded.add(depMod.file);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// Inject test info for matched modules (complex tasks only)
|
|
245
|
+
if (includeTests && safeExists(testMapPath) && used < budget) {
|
|
246
|
+
try {
|
|
247
|
+
const testMap = JSON.parse(safeRead(testMapPath) || "{}");
|
|
248
|
+
for (const file of mod.files || []) {
|
|
249
|
+
const testInfo = testMap[file];
|
|
250
|
+
if (testInfo && used < budget) {
|
|
251
|
+
const testLine = "# Tests for " + file + ": " + testInfo.test + " (" + testInfo.count + " tests)\\n" +
|
|
252
|
+
(testInfo.names || []).slice(0, 5).map((n) => " - " + n).join("\\n");
|
|
253
|
+
if (used + testLine.length <= budget) {
|
|
254
|
+
output.push(testLine);
|
|
255
|
+
used += testLine.length;
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
} catch {}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// Inject history for matched modules (complex tasks only)
|
|
263
|
+
if (includeHistory && safeExists(historyPath) && used < budget) {
|
|
264
|
+
try {
|
|
265
|
+
const history = JSON.parse(safeRead(historyPath) || "{}");
|
|
266
|
+
for (const file of mod.files || []) {
|
|
267
|
+
const hist = history[file];
|
|
268
|
+
if (hist && hist.recent && hist.recent.length > 0 && used < budget) {
|
|
269
|
+
const histLine = "# History for " + file + " (" + hist.frequency + " recent commits):\\n" +
|
|
270
|
+
hist.recent.slice(0, 3).map((m) => " - " + m).join("\\n");
|
|
271
|
+
if (used + histLine.length <= budget) {
|
|
272
|
+
output.push(histLine);
|
|
273
|
+
used += histLine.length;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
} catch {}
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
if (output.length > 0) {
|
|
282
|
+
process.stdout.write(output.join("\\n---\\n"));
|
|
283
|
+
}
|
|
284
|
+
} catch {
|
|
285
|
+
// Fail silently
|
|
286
|
+
}
|
|
287
|
+
process.exit(0);
|
|
288
|
+
});
|
|
289
|
+
`.trim();
|
|
290
|
+
writeFileSync(join(hooksDir, "prompt-submit.js"), promptSubmitScript);
|
|
291
|
+
// PostToolUse hook — learning loop (tracks file reads for future improvement)
|
|
292
|
+
writeFileSync(join(hooksDir, "post-read.js"), `#!/usr/bin/env node
|
|
293
|
+
// briefed: PostToolUse hook — tracks which files Claude reads for the learning loop
|
|
294
|
+
// Security: only appends to .briefed/session-reads.log, never reads prompt content
|
|
295
|
+
const { appendFileSync, mkdirSync, existsSync } = require("fs");
|
|
296
|
+
const { join } = require("path");
|
|
297
|
+
|
|
298
|
+
let input = "";
|
|
299
|
+
process.stdin.setEncoding("utf-8");
|
|
300
|
+
process.stdin.on("data", (chunk) => { input += chunk; });
|
|
301
|
+
process.stdin.on("end", () => {
|
|
302
|
+
try {
|
|
303
|
+
const data = JSON.parse(input);
|
|
304
|
+
if (data.tool_name !== "Read") { process.exit(0); return; }
|
|
305
|
+
const filePath = data.tool_input && data.tool_input.file_path;
|
|
306
|
+
if (!filePath) { process.exit(0); return; }
|
|
307
|
+
const briefedDir = join(process.cwd(), ".briefed");
|
|
308
|
+
if (!existsSync(briefedDir)) mkdirSync(briefedDir, { recursive: true });
|
|
309
|
+
appendFileSync(join(briefedDir, "session-reads.log"), filePath + "\\n");
|
|
310
|
+
} catch {}
|
|
311
|
+
process.exit(0);
|
|
312
|
+
});
|
|
313
|
+
`);
|
|
314
|
+
}
|
|
315
|
+
//# sourceMappingURL=hooks.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hooks.js","sourceRoot":"","sources":["../../src/deliver/hooks.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { FileExtraction } from "./signatures.js";
|
|
2
|
+
import type { DepGraph } from "./depgraph.js";
|
|
3
|
+
export interface ComplexityScore {
|
|
4
|
+
file: string;
|
|
5
|
+
score: number;
|
|
6
|
+
fanOut: number;
|
|
7
|
+
fanIn: number;
|
|
8
|
+
branchCount: number;
|
|
9
|
+
lineCount: number;
|
|
10
|
+
symbolCount: number;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Compute complexity score for a file.
|
|
14
|
+
* Higher score = more context needed when working with this file.
|
|
15
|
+
*/
|
|
16
|
+
export declare function computeComplexity(extraction: FileExtraction, depGraph: DepGraph): ComplexityScore;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { readFileSync } from "fs";
|
|
2
|
+
/**
|
|
3
|
+
* Compute complexity score for a file.
|
|
4
|
+
* Higher score = more context needed when working with this file.
|
|
5
|
+
*/
|
|
6
|
+
export function computeComplexity(extraction, depGraph) {
|
|
7
|
+
const content = readFileSync(extraction.path, "utf-8");
|
|
8
|
+
const fanOut = extraction.imports.filter((i) => i.isRelative).length;
|
|
9
|
+
const node = depGraph.nodes.get(extraction.path.replace(/\\/g, "/"));
|
|
10
|
+
const fanIn = node?.inEdges.length || 0;
|
|
11
|
+
// Count branching constructs
|
|
12
|
+
const branchPatterns = [
|
|
13
|
+
/\bif\s*\(/g,
|
|
14
|
+
/\belse\s+if\s*\(/g,
|
|
15
|
+
/\bswitch\s*\(/g,
|
|
16
|
+
/\bcase\s+/g,
|
|
17
|
+
/\bcatch\s*\(/g,
|
|
18
|
+
/\?\s*[^:]/g, // ternary
|
|
19
|
+
];
|
|
20
|
+
let branchCount = 0;
|
|
21
|
+
for (const pattern of branchPatterns) {
|
|
22
|
+
const matches = content.match(pattern);
|
|
23
|
+
if (matches)
|
|
24
|
+
branchCount += matches.length;
|
|
25
|
+
}
|
|
26
|
+
// Compute weighted score (0-10)
|
|
27
|
+
const score = Math.min(10, (clamp(fanOut / 3, 0, 3) + // 0-3 points for imports
|
|
28
|
+
clamp(fanIn / 3, 0, 3) + // 0-3 points for dependents
|
|
29
|
+
clamp(branchCount / 15, 0, 2) + // 0-2 points for branching
|
|
30
|
+
clamp(extraction.lineCount / 200, 0, 1) + // 0-1 points for size
|
|
31
|
+
clamp(extraction.symbols.length / 10, 0, 1) // 0-1 points for symbol density
|
|
32
|
+
));
|
|
33
|
+
return {
|
|
34
|
+
file: extraction.path,
|
|
35
|
+
score: Math.round(score * 10) / 10,
|
|
36
|
+
fanOut,
|
|
37
|
+
fanIn,
|
|
38
|
+
branchCount,
|
|
39
|
+
lineCount: extraction.lineCount,
|
|
40
|
+
symbolCount: extraction.symbols.length,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
function clamp(val, min, max) {
|
|
44
|
+
return Math.max(min, Math.min(max, val));
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=complexity.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"complexity.js","sourceRoot":"","sources":["../../src/extract/complexity.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,IAAI,CAAC;AAclC;;;GAGG;AACH,MAAM,UAAU,iBAAiB,CAC/B,UAA0B,EAC1B,QAAkB;IAElB,MAAM,OAAO,GAAG,YAAY,CAAC,UAAU,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IAEvD,MAAM,MAAM,GAAG,UAAU,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,MAAM,CAAC;IACrE,MAAM,IAAI,GAAG,QAAQ,CAAC,KAAK,CAAC,GAAG,CAC7B,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CACpC,CAAC;IACF,MAAM,KAAK,GAAG,IAAI,EAAE,OAAO,CAAC,MAAM,IAAI,CAAC,CAAC;IAExC,6BAA6B;IAC7B,MAAM,cAAc,GAAG;QACrB,YAAY;QACZ,mBAAmB;QACnB,gBAAgB;QAChB,YAAY;QACZ,eAAe;QACf,YAAY,EAAG,UAAU;KAC1B,CAAC;IACF,IAAI,WAAW,GAAG,CAAC,CAAC;IACpB,KAAK,MAAM,OAAO,IAAI,cAAc,EAAE,CAAC;QACrC,MAAM,OAAO,GAAG,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QACvC,IAAI,OAAO;YAAE,WAAW,IAAI,OAAO,CAAC,MAAM,CAAC;IAC7C,CAAC;IAED,gCAAgC;IAChC,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CACzB,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,GAAS,yBAAyB;QACzD,KAAK,CAAC,KAAK,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,GAAU,4BAA4B;QAC5D,KAAK,CAAC,WAAW,GAAG,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC,GAAG,2BAA2B;QAC3D,KAAK,CAAC,UAAU,CAAC,SAAS,GAAG,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC,GAAG,sBAAsB;QAChE,KAAK,CAAC,UAAU,CAAC,OAAO,CAAC,MAAM,GAAG,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,gCAAgC;KAC7E,CAAC,CAAC;IAEH,OAAO;QACL,IAAI,EAAE,UAAU,CAAC,IAAI;QACrB,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,EAAE,CAAC,GAAG,EAAE;QAClC,MAAM;QACN,KAAK;QACL,WAAW;QACX,SAAS,EAAE,UAAU,CAAC,SAAS;QAC/B,WAAW,EAAE,UAAU,CAAC,OAAO,CAAC,MAAM;KACvC,CAAC;AACJ,CAAC;AAED,SAAS,KAAK,CAAC,GAAW,EAAE,GAAW,EAAE,GAAW;IAClD,OAAO,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC;AAC3C,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { FileExtraction } from "./signatures.js";
|
|
2
|
+
export interface ProjectConventions {
|
|
3
|
+
naming: string[];
|
|
4
|
+
errorHandling: string[];
|
|
5
|
+
patterns: string[];
|
|
6
|
+
testing: string[];
|
|
7
|
+
imports: string[];
|
|
8
|
+
other: string[];
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Auto-detect project conventions from code patterns.
|
|
12
|
+
* Prevents the "inconsistent code style" problem that compounds across sessions.
|
|
13
|
+
*/
|
|
14
|
+
export declare function detectConventions(extractions: FileExtraction[], root: string): ProjectConventions;
|
|
15
|
+
/**
|
|
16
|
+
* Format conventions for inclusion in CLAUDE.md or rules.
|
|
17
|
+
*/
|
|
18
|
+
export declare function formatConventions(conv: ProjectConventions): string;
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { readFileSync } from "fs";
|
|
2
|
+
/**
|
|
3
|
+
* Auto-detect project conventions from code patterns.
|
|
4
|
+
* Prevents the "inconsistent code style" problem that compounds across sessions.
|
|
5
|
+
*/
|
|
6
|
+
export function detectConventions(extractions, root) {
|
|
7
|
+
const conventions = {
|
|
8
|
+
naming: [],
|
|
9
|
+
errorHandling: [],
|
|
10
|
+
patterns: [],
|
|
11
|
+
testing: [],
|
|
12
|
+
imports: [],
|
|
13
|
+
other: [],
|
|
14
|
+
};
|
|
15
|
+
// Sample files for analysis (up to 20 of the most important)
|
|
16
|
+
const sample = extractions
|
|
17
|
+
.filter((e) => e.symbols.length > 0)
|
|
18
|
+
.slice(0, 20);
|
|
19
|
+
if (sample.length === 0)
|
|
20
|
+
return conventions;
|
|
21
|
+
// Analyze naming conventions
|
|
22
|
+
const allSymbols = sample.flatMap((e) => e.symbols);
|
|
23
|
+
const exportedFunctions = allSymbols.filter((s) => s.exported && (s.kind === "function" || s.kind === "method"));
|
|
24
|
+
const exportedTypes = allSymbols.filter((s) => s.exported && (s.kind === "class" || s.kind === "interface" || s.kind === "type"));
|
|
25
|
+
if (exportedFunctions.length > 3) {
|
|
26
|
+
const camelCount = exportedFunctions.filter((s) => /^[a-z][a-zA-Z]+$/.test(s.name.split(".").pop())).length;
|
|
27
|
+
const snakeCount = exportedFunctions.filter((s) => /^[a-z][a-z_]+$/.test(s.name.split(".").pop())).length;
|
|
28
|
+
if (camelCount > snakeCount * 2) {
|
|
29
|
+
conventions.naming.push("camelCase for functions and methods");
|
|
30
|
+
}
|
|
31
|
+
else if (snakeCount > camelCount * 2) {
|
|
32
|
+
conventions.naming.push("snake_case for functions and methods");
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
if (exportedTypes.length > 2) {
|
|
36
|
+
const pascalCount = exportedTypes.filter((s) => /^[A-Z][a-zA-Z]+$/.test(s.name)).length;
|
|
37
|
+
if (pascalCount > exportedTypes.length * 0.7) {
|
|
38
|
+
conventions.naming.push("PascalCase for types, classes, and interfaces");
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
// Analyze error handling patterns
|
|
42
|
+
const fileContents = sample.map((e) => {
|
|
43
|
+
try {
|
|
44
|
+
return readFileSync(e.path, "utf-8");
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
return "";
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
const tryCatchCount = fileContents.filter((c) => c.includes("try {") || c.includes("try:")).length;
|
|
51
|
+
const resultTypeCount = fileContents.filter((c) => c.includes("Result<") || c.includes("Either<") || c.includes("Result[")).length;
|
|
52
|
+
const throwCount = fileContents.filter((c) => c.includes("throw new") || c.includes("raise ")).length;
|
|
53
|
+
if (tryCatchCount > sample.length * 0.3) {
|
|
54
|
+
conventions.errorHandling.push("uses try/catch for error handling");
|
|
55
|
+
}
|
|
56
|
+
if (resultTypeCount > 2) {
|
|
57
|
+
conventions.errorHandling.push("uses Result/Either types for error propagation");
|
|
58
|
+
}
|
|
59
|
+
if (throwCount > sample.length * 0.3) {
|
|
60
|
+
// Check if custom error classes are used
|
|
61
|
+
const customErrors = fileContents.filter((c) => c.match(/class \w+Error extends/)).length;
|
|
62
|
+
if (customErrors > 0) {
|
|
63
|
+
conventions.errorHandling.push("throws custom error classes (not generic Error)");
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
// Analyze import patterns
|
|
67
|
+
const allImports = sample.flatMap((e) => e.imports);
|
|
68
|
+
const aliasImports = allImports.filter((i) => i.source.startsWith("@/") || i.source.startsWith("~/") || i.source.startsWith("#"));
|
|
69
|
+
if (aliasImports.length > allImports.length * 0.1) {
|
|
70
|
+
const prefix = aliasImports[0]?.source.split("/")[0];
|
|
71
|
+
conventions.imports.push(`uses ${prefix} path alias for absolute imports`);
|
|
72
|
+
}
|
|
73
|
+
const relativeImports = allImports.filter((i) => i.isRelative);
|
|
74
|
+
const absoluteImports = allImports.filter((i) => !i.isRelative);
|
|
75
|
+
if (relativeImports.length > absoluteImports.length * 2) {
|
|
76
|
+
conventions.imports.push("prefers relative imports");
|
|
77
|
+
}
|
|
78
|
+
// Detect testing patterns
|
|
79
|
+
const testFiles = extractions.filter((e) => e.path.includes(".test.") || e.path.includes(".spec.") ||
|
|
80
|
+
e.path.includes("_test.") || e.path.startsWith("test_"));
|
|
81
|
+
if (testFiles.length > 0) {
|
|
82
|
+
const colocated = testFiles.filter((t) => {
|
|
83
|
+
const dir = t.path.split("/").slice(0, -1).join("/");
|
|
84
|
+
return sample.some((s) => s.path.split("/").slice(0, -1).join("/") === dir);
|
|
85
|
+
});
|
|
86
|
+
if (colocated.length > testFiles.length * 0.5) {
|
|
87
|
+
conventions.testing.push("test files are colocated with source files");
|
|
88
|
+
}
|
|
89
|
+
else {
|
|
90
|
+
conventions.testing.push("test files are in separate test/ directory");
|
|
91
|
+
}
|
|
92
|
+
if (testFiles.some((t) => t.path.includes(".test."))) {
|
|
93
|
+
conventions.testing.push("uses .test.{ext} naming convention");
|
|
94
|
+
}
|
|
95
|
+
else if (testFiles.some((t) => t.path.includes(".spec."))) {
|
|
96
|
+
conventions.testing.push("uses .spec.{ext} naming convention");
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
// Detect common architectural patterns
|
|
100
|
+
const hasServices = extractions.some((e) => e.path.includes("/services/") || e.path.includes("/service/"));
|
|
101
|
+
const hasControllers = extractions.some((e) => e.path.includes("/controllers/") || e.path.includes("/routes/") ||
|
|
102
|
+
e.path.includes("/api/"));
|
|
103
|
+
if (hasServices && hasControllers) {
|
|
104
|
+
conventions.patterns.push("layered architecture: routes/controllers → services");
|
|
105
|
+
}
|
|
106
|
+
// Detect async patterns
|
|
107
|
+
const asyncCount = fileContents.filter((c) => c.includes("async ") || c.includes("await ")).length;
|
|
108
|
+
if (asyncCount > sample.length * 0.5) {
|
|
109
|
+
conventions.patterns.push("predominantly async/await (not callbacks)");
|
|
110
|
+
}
|
|
111
|
+
// Detect export style
|
|
112
|
+
const defaultExports = fileContents.filter((c) => c.includes("export default") || c.includes("module.exports =")).length;
|
|
113
|
+
const namedExports = fileContents.filter((c) => c.includes("export function") || c.includes("export const") || c.includes("export class")).length;
|
|
114
|
+
if (namedExports > defaultExports * 3) {
|
|
115
|
+
conventions.patterns.push("prefers named exports over default exports");
|
|
116
|
+
}
|
|
117
|
+
else if (defaultExports > namedExports * 2) {
|
|
118
|
+
conventions.patterns.push("uses default exports");
|
|
119
|
+
}
|
|
120
|
+
return conventions;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Format conventions for inclusion in CLAUDE.md or rules.
|
|
124
|
+
*/
|
|
125
|
+
export function formatConventions(conv) {
|
|
126
|
+
const lines = [];
|
|
127
|
+
const all = [
|
|
128
|
+
...conv.naming,
|
|
129
|
+
...conv.errorHandling,
|
|
130
|
+
...conv.patterns,
|
|
131
|
+
...conv.testing,
|
|
132
|
+
...conv.imports,
|
|
133
|
+
...conv.other,
|
|
134
|
+
];
|
|
135
|
+
if (all.length === 0)
|
|
136
|
+
return "";
|
|
137
|
+
lines.push("Conventions:");
|
|
138
|
+
for (const c of all) {
|
|
139
|
+
lines.push(` - ${c}`);
|
|
140
|
+
}
|
|
141
|
+
return lines.join("\n");
|
|
142
|
+
}
|
|
143
|
+
//# sourceMappingURL=conventions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"conventions.js","sourceRoot":"","sources":["../../src/extract/conventions.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { FileExtraction } from "./signatures.js";
|
|
2
|
+
import { GraphNode } from "../utils/pagerank.js";
|
|
3
|
+
export interface DepGraph {
|
|
4
|
+
nodes: Map<string, GraphNode>;
|
|
5
|
+
pageRank: Map<string, number>;
|
|
6
|
+
refCounts: Map<string, number>;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Build a dependency graph from file extractions.
|
|
10
|
+
* Resolves relative imports to actual file paths.
|
|
11
|
+
*/
|
|
12
|
+
export declare function buildDepGraph(extractions: FileExtraction[], root: string): DepGraph;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { dirname, join, relative } from "path";
|
|
2
|
+
import { computePageRank, computeRefCounts } from "../utils/pagerank.js";
|
|
3
|
+
/**
|
|
4
|
+
* Build a dependency graph from file extractions.
|
|
5
|
+
* Resolves relative imports to actual file paths.
|
|
6
|
+
*/
|
|
7
|
+
export function buildDepGraph(extractions, root) {
|
|
8
|
+
const fileSet = new Set(extractions.map((e) => e.path));
|
|
9
|
+
const nodes = new Map();
|
|
10
|
+
// Initialize all nodes
|
|
11
|
+
for (const ext of extractions) {
|
|
12
|
+
nodes.set(ext.path, {
|
|
13
|
+
id: ext.path,
|
|
14
|
+
outEdges: [],
|
|
15
|
+
inEdges: [],
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
// Resolve imports and build edges
|
|
19
|
+
for (const ext of extractions) {
|
|
20
|
+
const node = nodes.get(ext.path);
|
|
21
|
+
for (const imp of ext.imports) {
|
|
22
|
+
if (!imp.isRelative)
|
|
23
|
+
continue; // skip external packages
|
|
24
|
+
const resolved = resolveImport(ext.path, imp.source, root, fileSet);
|
|
25
|
+
if (resolved && nodes.has(resolved)) {
|
|
26
|
+
if (!node.outEdges.includes(resolved)) {
|
|
27
|
+
node.outEdges.push(resolved);
|
|
28
|
+
}
|
|
29
|
+
const targetNode = nodes.get(resolved);
|
|
30
|
+
if (!targetNode.inEdges.includes(ext.path)) {
|
|
31
|
+
targetNode.inEdges.push(ext.path);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
// Compute rankings
|
|
37
|
+
const pageRank = computePageRank(nodes);
|
|
38
|
+
const refCounts = computeRefCounts(nodes);
|
|
39
|
+
return { nodes, pageRank, refCounts };
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Resolve a relative import to a file path.
|
|
43
|
+
* Handles TypeScript/JS resolution with extension inference.
|
|
44
|
+
*/
|
|
45
|
+
function resolveImport(fromFile, importPath, root, fileSet) {
|
|
46
|
+
const fromDir = dirname(join(root, fromFile));
|
|
47
|
+
const basePath = relative(root, join(fromDir, importPath)).replace(/\\/g, "/");
|
|
48
|
+
// Security: prevent path traversal outside project root
|
|
49
|
+
if (basePath.startsWith("..") || basePath.startsWith("/"))
|
|
50
|
+
return null;
|
|
51
|
+
// Try exact match first
|
|
52
|
+
if (fileSet.has(basePath))
|
|
53
|
+
return basePath;
|
|
54
|
+
// Try with extensions
|
|
55
|
+
const extensions = [".ts", ".tsx", ".js", ".jsx", ".mjs", ".py", ".go", ".rs"];
|
|
56
|
+
for (const ext of extensions) {
|
|
57
|
+
const withExt = basePath + ext;
|
|
58
|
+
if (fileSet.has(withExt))
|
|
59
|
+
return withExt;
|
|
60
|
+
}
|
|
61
|
+
// Try index files
|
|
62
|
+
const indexFiles = ["index.ts", "index.tsx", "index.js", "index.jsx", "mod.rs", "__init__.py"];
|
|
63
|
+
for (const idx of indexFiles) {
|
|
64
|
+
const indexPath = basePath + "/" + idx;
|
|
65
|
+
if (fileSet.has(indexPath))
|
|
66
|
+
return indexPath;
|
|
67
|
+
}
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=depgraph.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"depgraph.js","sourceRoot":"","sources":["../../src/extract/depgraph.ts"],"names":[],"mappings":"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"}
|