autodocs-engine 0.9.8 → 0.10.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 (76) hide show
  1. package/README.md +123 -117
  2. package/dist/anti-pattern-detector.js +22 -0
  3. package/dist/anti-pattern-detector.js.map +1 -1
  4. package/dist/architecture-detector.js +33 -29
  5. package/dist/architecture-detector.js.map +1 -1
  6. package/dist/ast-parser.js +31 -3
  7. package/dist/ast-parser.js.map +1 -1
  8. package/dist/benchmark/scorer.js +3 -1
  9. package/dist/benchmark/scorer.js.map +1 -1
  10. package/dist/bin/autodocs-engine.js +7 -0
  11. package/dist/bin/autodocs-engine.js.map +1 -1
  12. package/dist/bin/serve.d.ts +1 -0
  13. package/dist/bin/serve.js +5 -1
  14. package/dist/bin/serve.js.map +1 -1
  15. package/dist/bin/setup-hooks.d.ts +1 -0
  16. package/dist/bin/setup-hooks.js +59 -0
  17. package/dist/bin/setup-hooks.js.map +1 -0
  18. package/dist/config.d.ts +1 -0
  19. package/dist/config.js +4 -0
  20. package/dist/config.js.map +1 -1
  21. package/dist/convention-extractor.js +10 -0
  22. package/dist/convention-extractor.js.map +1 -1
  23. package/dist/detectors/api-patterns.d.ts +2 -0
  24. package/dist/detectors/api-patterns.js +101 -0
  25. package/dist/detectors/api-patterns.js.map +1 -0
  26. package/dist/detectors/async-patterns.d.ts +2 -0
  27. package/dist/detectors/async-patterns.js +79 -0
  28. package/dist/detectors/async-patterns.js.map +1 -0
  29. package/dist/detectors/error-handling.js +82 -30
  30. package/dist/detectors/error-handling.js.map +1 -1
  31. package/dist/detectors/state-management.d.ts +2 -0
  32. package/dist/detectors/state-management.js +75 -0
  33. package/dist/detectors/state-management.js.map +1 -0
  34. package/dist/execution-flow.d.ts +23 -0
  35. package/dist/execution-flow.js +286 -0
  36. package/dist/execution-flow.js.map +1 -0
  37. package/dist/git-history.js +18 -7
  38. package/dist/git-history.js.map +1 -1
  39. package/dist/implicit-coupling.d.ts +7 -0
  40. package/dist/implicit-coupling.js +40 -0
  41. package/dist/implicit-coupling.js.map +1 -0
  42. package/dist/import-chain.js +12 -3
  43. package/dist/import-chain.js.map +1 -1
  44. package/dist/index.js +20 -1
  45. package/dist/index.js.map +1 -1
  46. package/dist/llm/serializer.js +8 -6
  47. package/dist/llm/serializer.js.map +1 -1
  48. package/dist/mcp/cache.d.ts +11 -1
  49. package/dist/mcp/cache.js +49 -7
  50. package/dist/mcp/cache.js.map +1 -1
  51. package/dist/mcp/queries.d.ts +19 -3
  52. package/dist/mcp/queries.js +342 -62
  53. package/dist/mcp/queries.js.map +1 -1
  54. package/dist/mcp/server.d.ts +1 -0
  55. package/dist/mcp/server.js +42 -1
  56. package/dist/mcp/server.js.map +1 -1
  57. package/dist/mcp/tools.d.ts +1 -0
  58. package/dist/mcp/tools.js +162 -20
  59. package/dist/mcp/tools.js.map +1 -1
  60. package/dist/output-validator.js +73 -0
  61. package/dist/output-validator.js.map +1 -1
  62. package/dist/pipeline.js +37 -0
  63. package/dist/pipeline.js.map +1 -1
  64. package/dist/symbol-graph.js +4 -0
  65. package/dist/symbol-graph.js.map +1 -1
  66. package/dist/type-enricher.d.ts +17 -0
  67. package/dist/type-enricher.js +127 -0
  68. package/dist/type-enricher.js.map +1 -0
  69. package/dist/type-resolver.d.ts +13 -0
  70. package/dist/type-resolver.js +75 -0
  71. package/dist/type-resolver.js.map +1 -0
  72. package/dist/types.d.ts +37 -4
  73. package/dist/types.js +7 -2
  74. package/dist/types.js.map +1 -1
  75. package/hooks/autodocs-hook.cjs +227 -0
  76. package/package.json +3 -2
@@ -0,0 +1,227 @@
1
+ // hooks/autodocs-hook.cjs — PreToolUse/PostToolUse handler for Claude Code
2
+ // Reads cached analysis from ~/.autodocs/cache/ and augments search results
3
+ // with callers, co-change partners, and execution flows.
4
+ //
5
+ // Design principles:
6
+ // - Silent on any error (never break the original tool)
7
+ // - <100ms response time (reads pre-computed JSON snapshot)
8
+ // - No analysis in hook process (MCP server writes the snapshot)
9
+ // - CommonJS for broad Node.js compatibility
10
+
11
+ const { execFileSync } = require("child_process");
12
+ const fs = require("fs");
13
+ const path = require("path");
14
+ const os = require("os");
15
+ const crypto = require("crypto");
16
+
17
+ // ─── Dispatch ──────────────────────────────────────────────────────────────
18
+
19
+ const handlers = {
20
+ PreToolUse: handlePreToolUse,
21
+ PostToolUse: handlePostToolUse,
22
+ };
23
+
24
+ function main() {
25
+ try {
26
+ const input = JSON.parse(fs.readFileSync("/dev/stdin", "utf-8"));
27
+ const handler = handlers[input.hook_event_name];
28
+ if (handler) handler(input);
29
+ } catch {
30
+ // Silent — never break the original tool
31
+ if (process.env.AUTODOCS_DEBUG) {
32
+ process.stderr.write("[autodocs-hook] Error in main dispatch\n");
33
+ }
34
+ }
35
+ }
36
+
37
+ // ─── PreToolUse: Augment searches with graph context ───────────────────────
38
+
39
+ function handlePreToolUse(input) {
40
+ const pattern = extractPattern(input.tool_name, input.tool_input);
41
+ if (!pattern || pattern.length < 3) return;
42
+
43
+ const cwd = input.cwd;
44
+ if (!cwd || !path.isAbsolute(cwd)) return;
45
+
46
+ const snapshot = loadSnapshot(cwd);
47
+ if (!snapshot) return;
48
+
49
+ const context = augment(snapshot, pattern);
50
+ if (!context) return;
51
+
52
+ emit("PreToolUse", context);
53
+ }
54
+
55
+ // ─── PostToolUse: Detect index staleness after git mutations ───────────────
56
+
57
+ const GIT_MUTATION_RE = /\bgit\s+(commit|merge|rebase|cherry-pick|pull)(\s|$)/;
58
+
59
+ function handlePostToolUse(input) {
60
+ if (input.tool_name !== "Bash") return;
61
+
62
+ const cmd = input.tool_input?.command || "";
63
+ if (!GIT_MUTATION_RE.test(cmd)) return;
64
+ if (input.tool_output?.exit_code !== 0) return;
65
+
66
+ const cwd = input.cwd;
67
+ if (!cwd || !path.isAbsolute(cwd)) return;
68
+
69
+ const snapshot = loadSnapshot(cwd);
70
+ if (!snapshot) return;
71
+
72
+ try {
73
+ const head = execFileSync("git", ["rev-parse", "HEAD"], {
74
+ cwd,
75
+ encoding: "utf-8",
76
+ timeout: 3000,
77
+ stdio: ["pipe", "pipe", "pipe"],
78
+ }).trim();
79
+
80
+ // Cache key format: "HEAD_HASH:STATUS_HASH"
81
+ const snapshotHead = (snapshot.cacheKey || "").split(":")[0];
82
+ if (snapshotHead === head) return; // Still fresh
83
+
84
+ emit(
85
+ "PostToolUse",
86
+ "[autodocs] Analysis cache is stale after git change. The MCP server will re-analyze automatically on the next tool call.",
87
+ );
88
+ } catch {
89
+ // Silent
90
+ }
91
+ }
92
+
93
+ // ─── Pattern Extraction ────────────────────────────────────────────────────
94
+
95
+ function extractPattern(toolName, toolInput) {
96
+ if (!toolInput) return null;
97
+
98
+ // Grep: pattern is directly available
99
+ if (toolName === "Grep") {
100
+ return toolInput.pattern || null;
101
+ }
102
+
103
+ // Glob: extract meaningful name from glob syntax
104
+ if (toolName === "Glob") {
105
+ const raw = toolInput.pattern || "";
106
+ // "**/*.ts" → skip (too generic). "auth*.ts" → "auth". "**/authenticate/**" → "authenticate"
107
+ const match = raw.match(/[*/]([a-zA-Z][a-zA-Z0-9_-]{2,})/);
108
+ return match ? match[1] : null;
109
+ }
110
+
111
+ // Bash: parse grep/rg commands for the search pattern
112
+ if (toolName === "Bash") {
113
+ const cmd = toolInput.command || "";
114
+ if (!/\brg\b|\bgrep\b/.test(cmd)) return null;
115
+
116
+ // Simple token extraction: skip the command name and flags
117
+ const tokens = cmd.split(/\s+/);
118
+ const skipNextValue = new Set(["-e", "-f", "-A", "-B", "-C", "--glob", "--type", "-g", "-t"]);
119
+ let skipNext = false;
120
+
121
+ for (let i = 1; i < tokens.length; i++) {
122
+ if (skipNext) {
123
+ skipNext = false;
124
+ continue;
125
+ }
126
+ const t = tokens[i].replace(/^['"]|['"]$/g, "");
127
+ if (t.startsWith("-")) {
128
+ if (skipNextValue.has(t)) skipNext = true;
129
+ continue;
130
+ }
131
+ if (t.length >= 3) return t;
132
+ }
133
+ return null;
134
+ }
135
+
136
+ return null;
137
+ }
138
+
139
+ // ─── Snapshot Loading ──────────────────────────────────────────────────────
140
+
141
+ function loadSnapshot(cwd) {
142
+ try {
143
+ const hash = crypto.createHash("sha256").update(cwd).digest("hex").slice(0, 12);
144
+ const cacheFile = path.join(os.homedir(), ".autodocs", "cache", `${hash}.json`);
145
+ return JSON.parse(fs.readFileSync(cacheFile, "utf-8"));
146
+ } catch {
147
+ return null;
148
+ }
149
+ }
150
+
151
+ // ─── Augmentation ──────────────────────────────────────────────────────────
152
+
153
+ function augment(snapshot, pattern) {
154
+ // Find the primary package (largest)
155
+ const pkgs = snapshot.packages || [];
156
+ if (pkgs.length === 0) return null;
157
+ const pkg = pkgs.reduce((a, b) => ((a.publicAPI || []).length > (b.publicAPI || []).length ? a : b));
158
+
159
+ const patternLower = pattern.toLowerCase();
160
+
161
+ // Match against public API symbols
162
+ const matches = (pkg.publicAPI || []).filter((e) => e.name.toLowerCase().includes(patternLower));
163
+ if (matches.length === 0) return null;
164
+
165
+ const results = [];
166
+ for (const exp of matches.slice(0, 3)) {
167
+ const lines = [`**${exp.name}** (${exp.kind}) — \`${exp.sourceFile}\``];
168
+
169
+ // Callers from call graph
170
+ const callers = (pkg.callGraph || [])
171
+ .filter((e) => e.to === exp.name)
172
+ .map((e) => e.from)
173
+ .slice(0, 3);
174
+ if (callers.length > 0) lines.push(` Called by: ${callers.join(", ")}`);
175
+
176
+ // Callees from call graph
177
+ const callees = (pkg.callGraph || [])
178
+ .filter((e) => e.from === exp.name)
179
+ .map((e) => e.to)
180
+ .slice(0, 3);
181
+ if (callees.length > 0) lines.push(` Calls: ${callees.join(", ")}`);
182
+
183
+ // Co-change partners
184
+ const coChanges = (pkg.gitHistory?.coChangeEdges || [])
185
+ .filter((e) => e.file1 === exp.sourceFile || e.file2 === exp.sourceFile)
186
+ .sort((a, b) => b.jaccard - a.jaccard)
187
+ .slice(0, 2)
188
+ .map((e) => {
189
+ const partner = e.file1 === exp.sourceFile ? e.file2 : e.file1;
190
+ return `${partner} (${Math.round(e.jaccard * 100)}%)`;
191
+ });
192
+ if (coChanges.length > 0) lines.push(` Co-changes with: ${coChanges.join(", ")}`);
193
+
194
+ // Execution flows
195
+ const flows = (pkg.executionFlows || [])
196
+ .filter((f) => f.steps.includes(exp.name))
197
+ .slice(0, 2)
198
+ .map((f) => {
199
+ const idx = f.steps.indexOf(exp.name);
200
+ return `${f.label} (step ${idx + 1}/${f.length})`;
201
+ });
202
+ if (flows.length > 0) lines.push(` Flows: ${flows.join("; ")}`);
203
+
204
+ // Import count
205
+ if (exp.importCount > 0) lines.push(` Imported by: ${exp.importCount} files`);
206
+
207
+ results.push(lines.join("\n"));
208
+ }
209
+
210
+ if (results.length === 0) return null;
211
+ return `[autodocs] ${results.length} related symbol${results.length > 1 ? "s" : ""} found:\n\n${results.join("\n\n")}`;
212
+ }
213
+
214
+ // ─── Output ────────────────────────────────────────────────────────────────
215
+
216
+ function emit(hookEventName, additionalContext) {
217
+ const response = {
218
+ hookSpecificOutput: {
219
+ hookEventName,
220
+ additionalContext,
221
+ },
222
+ };
223
+ process.stdout.write(JSON.stringify(response));
224
+ }
225
+
226
+ // ─── Entry Point ───────────────────────────────────────────────────────────
227
+ main();
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "autodocs-engine",
3
- "version": "0.9.8",
4
- "description": "Codebase intelligence engine for generating AI context files",
3
+ "version": "0.10.0",
4
+ "description": "Codebase intelligence for AI coding agents — MCP server with git co-change analysis, convention detection, execution flows, and type-aware analysis",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
7
7
  "types": "dist/index.d.ts",
@@ -10,6 +10,7 @@
10
10
  },
11
11
  "files": [
12
12
  "dist/",
13
+ "hooks/",
13
14
  "README.md",
14
15
  "LICENSE"
15
16
  ],