@zuvia-software-solutions/code-mapper 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +215 -0
- package/dist/cli/ai-context.d.ts +19 -0
- package/dist/cli/ai-context.js +168 -0
- package/dist/cli/analyze.d.ts +7 -0
- package/dist/cli/analyze.js +325 -0
- package/dist/cli/augment.d.ts +7 -0
- package/dist/cli/augment.js +27 -0
- package/dist/cli/clean.d.ts +5 -0
- package/dist/cli/clean.js +56 -0
- package/dist/cli/eval-server.d.ts +25 -0
- package/dist/cli/eval-server.js +365 -0
- package/dist/cli/index.d.ts +6 -0
- package/dist/cli/index.js +102 -0
- package/dist/cli/lazy-action.d.ts +6 -0
- package/dist/cli/lazy-action.js +19 -0
- package/dist/cli/list.d.ts +2 -0
- package/dist/cli/list.js +27 -0
- package/dist/cli/mcp.d.ts +8 -0
- package/dist/cli/mcp.js +35 -0
- package/dist/cli/refresh.d.ts +12 -0
- package/dist/cli/refresh.js +165 -0
- package/dist/cli/serve.d.ts +5 -0
- package/dist/cli/serve.js +8 -0
- package/dist/cli/setup.d.ts +6 -0
- package/dist/cli/setup.js +218 -0
- package/dist/cli/status.d.ts +2 -0
- package/dist/cli/status.js +33 -0
- package/dist/cli/tool.d.ts +28 -0
- package/dist/cli/tool.js +87 -0
- package/dist/config/ignore-service.d.ts +32 -0
- package/dist/config/ignore-service.js +282 -0
- package/dist/config/supported-languages.d.ts +23 -0
- package/dist/config/supported-languages.js +52 -0
- package/dist/core/augmentation/engine.d.ts +22 -0
- package/dist/core/augmentation/engine.js +232 -0
- package/dist/core/embeddings/embedder.d.ts +35 -0
- package/dist/core/embeddings/embedder.js +171 -0
- package/dist/core/embeddings/embedding-pipeline.d.ts +41 -0
- package/dist/core/embeddings/embedding-pipeline.js +402 -0
- package/dist/core/embeddings/index.d.ts +5 -0
- package/dist/core/embeddings/index.js +6 -0
- package/dist/core/embeddings/text-generator.d.ts +20 -0
- package/dist/core/embeddings/text-generator.js +159 -0
- package/dist/core/embeddings/types.d.ts +60 -0
- package/dist/core/embeddings/types.js +23 -0
- package/dist/core/graph/graph.d.ts +4 -0
- package/dist/core/graph/graph.js +65 -0
- package/dist/core/graph/types.d.ts +69 -0
- package/dist/core/graph/types.js +3 -0
- package/dist/core/incremental/child-process.d.ts +8 -0
- package/dist/core/incremental/child-process.js +649 -0
- package/dist/core/incremental/refresh-coordinator.d.ts +32 -0
- package/dist/core/incremental/refresh-coordinator.js +147 -0
- package/dist/core/incremental/types.d.ts +78 -0
- package/dist/core/incremental/types.js +153 -0
- package/dist/core/incremental/watcher.d.ts +63 -0
- package/dist/core/incremental/watcher.js +338 -0
- package/dist/core/ingestion/ast-cache.d.ts +12 -0
- package/dist/core/ingestion/ast-cache.js +34 -0
- package/dist/core/ingestion/call-processor.d.ts +34 -0
- package/dist/core/ingestion/call-processor.js +937 -0
- package/dist/core/ingestion/call-routing.d.ts +40 -0
- package/dist/core/ingestion/call-routing.js +97 -0
- package/dist/core/ingestion/cluster-enricher.d.ts +30 -0
- package/dist/core/ingestion/cluster-enricher.js +151 -0
- package/dist/core/ingestion/community-processor.d.ts +26 -0
- package/dist/core/ingestion/community-processor.js +272 -0
- package/dist/core/ingestion/constants.d.ts +5 -0
- package/dist/core/ingestion/constants.js +8 -0
- package/dist/core/ingestion/entry-point-scoring.d.ts +23 -0
- package/dist/core/ingestion/entry-point-scoring.js +317 -0
- package/dist/core/ingestion/export-detection.d.ts +11 -0
- package/dist/core/ingestion/export-detection.js +203 -0
- package/dist/core/ingestion/filesystem-walker.d.ts +18 -0
- package/dist/core/ingestion/filesystem-walker.js +64 -0
- package/dist/core/ingestion/framework-detection.d.ts +42 -0
- package/dist/core/ingestion/framework-detection.js +405 -0
- package/dist/core/ingestion/heritage-processor.d.ts +15 -0
- package/dist/core/ingestion/heritage-processor.js +237 -0
- package/dist/core/ingestion/import-processor.d.ts +31 -0
- package/dist/core/ingestion/import-processor.js +416 -0
- package/dist/core/ingestion/language-config.d.ts +32 -0
- package/dist/core/ingestion/language-config.js +161 -0
- package/dist/core/ingestion/mro-processor.d.ts +32 -0
- package/dist/core/ingestion/mro-processor.js +343 -0
- package/dist/core/ingestion/named-binding-extraction.d.ts +51 -0
- package/dist/core/ingestion/named-binding-extraction.js +343 -0
- package/dist/core/ingestion/parsing-processor.d.ts +20 -0
- package/dist/core/ingestion/parsing-processor.js +282 -0
- package/dist/core/ingestion/pipeline.d.ts +3 -0
- package/dist/core/ingestion/pipeline.js +416 -0
- package/dist/core/ingestion/process-processor.d.ts +42 -0
- package/dist/core/ingestion/process-processor.js +357 -0
- package/dist/core/ingestion/resolution-context.d.ts +40 -0
- package/dist/core/ingestion/resolution-context.js +171 -0
- package/dist/core/ingestion/resolvers/csharp.d.ts +10 -0
- package/dist/core/ingestion/resolvers/csharp.js +101 -0
- package/dist/core/ingestion/resolvers/go.d.ts +8 -0
- package/dist/core/ingestion/resolvers/go.js +33 -0
- package/dist/core/ingestion/resolvers/index.d.ts +14 -0
- package/dist/core/ingestion/resolvers/index.js +10 -0
- package/dist/core/ingestion/resolvers/jvm.d.ts +9 -0
- package/dist/core/ingestion/resolvers/jvm.js +74 -0
- package/dist/core/ingestion/resolvers/php.d.ts +7 -0
- package/dist/core/ingestion/resolvers/php.js +30 -0
- package/dist/core/ingestion/resolvers/ruby.d.ts +9 -0
- package/dist/core/ingestion/resolvers/ruby.js +13 -0
- package/dist/core/ingestion/resolvers/rust.d.ts +5 -0
- package/dist/core/ingestion/resolvers/rust.js +62 -0
- package/dist/core/ingestion/resolvers/standard.d.ts +16 -0
- package/dist/core/ingestion/resolvers/standard.js +144 -0
- package/dist/core/ingestion/resolvers/utils.d.ts +18 -0
- package/dist/core/ingestion/resolvers/utils.js +113 -0
- package/dist/core/ingestion/structure-processor.d.ts +4 -0
- package/dist/core/ingestion/structure-processor.js +39 -0
- package/dist/core/ingestion/symbol-table.d.ts +34 -0
- package/dist/core/ingestion/symbol-table.js +48 -0
- package/dist/core/ingestion/tree-sitter-queries.d.ts +20 -0
- package/dist/core/ingestion/tree-sitter-queries.js +691 -0
- package/dist/core/ingestion/type-env.d.ts +52 -0
- package/dist/core/ingestion/type-env.js +349 -0
- package/dist/core/ingestion/type-extractors/c-cpp.d.ts +4 -0
- package/dist/core/ingestion/type-extractors/c-cpp.js +214 -0
- package/dist/core/ingestion/type-extractors/csharp.d.ts +4 -0
- package/dist/core/ingestion/type-extractors/csharp.js +224 -0
- package/dist/core/ingestion/type-extractors/go.d.ts +4 -0
- package/dist/core/ingestion/type-extractors/go.js +261 -0
- package/dist/core/ingestion/type-extractors/index.d.ts +20 -0
- package/dist/core/ingestion/type-extractors/index.js +30 -0
- package/dist/core/ingestion/type-extractors/jvm.d.ts +5 -0
- package/dist/core/ingestion/type-extractors/jvm.js +386 -0
- package/dist/core/ingestion/type-extractors/php.d.ts +4 -0
- package/dist/core/ingestion/type-extractors/php.js +280 -0
- package/dist/core/ingestion/type-extractors/python.d.ts +4 -0
- package/dist/core/ingestion/type-extractors/python.js +175 -0
- package/dist/core/ingestion/type-extractors/ruby.d.ts +12 -0
- package/dist/core/ingestion/type-extractors/ruby.js +218 -0
- package/dist/core/ingestion/type-extractors/rust.d.ts +4 -0
- package/dist/core/ingestion/type-extractors/rust.js +290 -0
- package/dist/core/ingestion/type-extractors/shared.d.ts +81 -0
- package/dist/core/ingestion/type-extractors/shared.js +322 -0
- package/dist/core/ingestion/type-extractors/swift.d.ts +4 -0
- package/dist/core/ingestion/type-extractors/swift.js +140 -0
- package/dist/core/ingestion/type-extractors/types.d.ts +111 -0
- package/dist/core/ingestion/type-extractors/types.js +4 -0
- package/dist/core/ingestion/type-extractors/typescript.d.ts +4 -0
- package/dist/core/ingestion/type-extractors/typescript.js +227 -0
- package/dist/core/ingestion/utils.d.ts +73 -0
- package/dist/core/ingestion/utils.js +992 -0
- package/dist/core/ingestion/workers/parse-worker.d.ts +99 -0
- package/dist/core/ingestion/workers/parse-worker.js +1055 -0
- package/dist/core/ingestion/workers/worker-pool.d.ts +15 -0
- package/dist/core/ingestion/workers/worker-pool.js +123 -0
- package/dist/core/lbug/csv-generator.d.ts +28 -0
- package/dist/core/lbug/csv-generator.js +355 -0
- package/dist/core/lbug/lbug-adapter.d.ts +96 -0
- package/dist/core/lbug/lbug-adapter.js +753 -0
- package/dist/core/lbug/schema.d.ts +46 -0
- package/dist/core/lbug/schema.js +402 -0
- package/dist/core/search/bm25-index.d.ts +20 -0
- package/dist/core/search/bm25-index.js +123 -0
- package/dist/core/search/hybrid-search.d.ts +32 -0
- package/dist/core/search/hybrid-search.js +131 -0
- package/dist/core/search/query-cache.d.ts +18 -0
- package/dist/core/search/query-cache.js +47 -0
- package/dist/core/search/query-expansion.d.ts +19 -0
- package/dist/core/search/query-expansion.js +75 -0
- package/dist/core/search/reranker.d.ts +29 -0
- package/dist/core/search/reranker.js +122 -0
- package/dist/core/search/types.d.ts +154 -0
- package/dist/core/search/types.js +51 -0
- package/dist/core/semantic/tsgo-service.d.ts +67 -0
- package/dist/core/semantic/tsgo-service.js +355 -0
- package/dist/core/tree-sitter/parser-loader.d.ts +12 -0
- package/dist/core/tree-sitter/parser-loader.js +71 -0
- package/dist/lib/memory-guard.d.ts +35 -0
- package/dist/lib/memory-guard.js +70 -0
- package/dist/lib/utils.d.ts +3 -0
- package/dist/lib/utils.js +6 -0
- package/dist/mcp/compatible-stdio-transport.d.ts +32 -0
- package/dist/mcp/compatible-stdio-transport.js +209 -0
- package/dist/mcp/core/embedder.d.ts +24 -0
- package/dist/mcp/core/embedder.js +168 -0
- package/dist/mcp/core/lbug-adapter.d.ts +29 -0
- package/dist/mcp/core/lbug-adapter.js +330 -0
- package/dist/mcp/local/local-backend.d.ts +188 -0
- package/dist/mcp/local/local-backend.js +2759 -0
- package/dist/mcp/resources.d.ts +22 -0
- package/dist/mcp/resources.js +379 -0
- package/dist/mcp/server.d.ts +10 -0
- package/dist/mcp/server.js +217 -0
- package/dist/mcp/staleness.d.ts +10 -0
- package/dist/mcp/staleness.js +25 -0
- package/dist/mcp/tools.d.ts +21 -0
- package/dist/mcp/tools.js +202 -0
- package/dist/server/api.d.ts +5 -0
- package/dist/server/api.js +340 -0
- package/dist/server/mcp-http.d.ts +7 -0
- package/dist/server/mcp-http.js +95 -0
- package/dist/storage/git.d.ts +6 -0
- package/dist/storage/git.js +35 -0
- package/dist/storage/repo-manager.d.ts +87 -0
- package/dist/storage/repo-manager.js +249 -0
- package/dist/types/pipeline.d.ts +35 -0
- package/dist/types/pipeline.js +20 -0
- package/hooks/claude/code-mapper-hook.cjs +238 -0
- package/hooks/claude/pre-tool-use.sh +79 -0
- package/hooks/claude/session-start.sh +42 -0
- package/models/mlx-embedder.py +185 -0
- package/package.json +100 -0
- package/scripts/patch-tree-sitter-swift.cjs +74 -0
- package/vendor/leiden/index.cjs +355 -0
- package/vendor/leiden/utils.cjs +392 -0
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/** @file git.ts
|
|
2
|
+
* @description Git utilities for repository detection, commit tracking, and root discovery */
|
|
3
|
+
export declare const isGitRepo: (repoPath: string) => boolean;
|
|
4
|
+
export declare const getCurrentCommit: (repoPath: string) => string;
|
|
5
|
+
/** Find the git repository root from any path inside the repo */
|
|
6
|
+
export declare const getGitRoot: (fromPath: string) => string | null;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
// code-mapper/src/storage/git.ts
|
|
2
|
+
/** @file git.ts
|
|
3
|
+
* @description Git utilities for repository detection, commit tracking, and root discovery */
|
|
4
|
+
import { execSync } from 'child_process';
|
|
5
|
+
import path from 'path';
|
|
6
|
+
export const isGitRepo = (repoPath) => {
|
|
7
|
+
try {
|
|
8
|
+
execSync('git rev-parse --is-inside-work-tree', { cwd: repoPath, stdio: 'ignore' });
|
|
9
|
+
return true;
|
|
10
|
+
}
|
|
11
|
+
catch {
|
|
12
|
+
return false;
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
export const getCurrentCommit = (repoPath) => {
|
|
16
|
+
try {
|
|
17
|
+
return execSync('git rev-parse HEAD', { cwd: repoPath }).toString().trim();
|
|
18
|
+
}
|
|
19
|
+
catch {
|
|
20
|
+
return '';
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
/** Find the git repository root from any path inside the repo */
|
|
24
|
+
export const getGitRoot = (fromPath) => {
|
|
25
|
+
try {
|
|
26
|
+
const raw = execSync('git rev-parse --show-toplevel', { cwd: fromPath })
|
|
27
|
+
.toString()
|
|
28
|
+
.trim();
|
|
29
|
+
// On Windows, git returns /d/Projects/Foo — path.resolve normalizes to D:\Projects\Foo
|
|
30
|
+
return path.resolve(raw);
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
};
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/** @file repo-manager.ts
|
|
2
|
+
* @description Manages Code Mapper index storage in .code-mapper/ at repo root
|
|
3
|
+
* Maintains a global registry at ~/.code-mapper/registry.json for MCP repo discovery */
|
|
4
|
+
export interface RepoMeta {
|
|
5
|
+
repoPath: string;
|
|
6
|
+
lastCommit: string;
|
|
7
|
+
indexedAt: string;
|
|
8
|
+
stats?: {
|
|
9
|
+
files?: number;
|
|
10
|
+
nodes?: number;
|
|
11
|
+
edges?: number;
|
|
12
|
+
communities?: number;
|
|
13
|
+
processes?: number;
|
|
14
|
+
embeddings?: number;
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
export interface IndexedRepo {
|
|
18
|
+
repoPath: string;
|
|
19
|
+
storagePath: string;
|
|
20
|
+
lbugPath: string;
|
|
21
|
+
metaPath: string;
|
|
22
|
+
meta: RepoMeta;
|
|
23
|
+
}
|
|
24
|
+
/** Shape of an entry in the global registry (~/.code-mapper/registry.json) */
|
|
25
|
+
export interface RegistryEntry {
|
|
26
|
+
name: string;
|
|
27
|
+
path: string;
|
|
28
|
+
storagePath: string;
|
|
29
|
+
indexedAt: string;
|
|
30
|
+
lastCommit: string;
|
|
31
|
+
stats?: RepoMeta['stats'];
|
|
32
|
+
}
|
|
33
|
+
/** Get the .code-mapper storage path for a repository */
|
|
34
|
+
export declare const getStoragePath: (repoPath: string) => string;
|
|
35
|
+
/** Get paths to key storage files */
|
|
36
|
+
export declare const getStoragePaths: (repoPath: string) => {
|
|
37
|
+
storagePath: string;
|
|
38
|
+
lbugPath: string;
|
|
39
|
+
metaPath: string;
|
|
40
|
+
};
|
|
41
|
+
/** Check whether a KuzuDB index exists (non-destructive, safe for status commands) */
|
|
42
|
+
export declare const hasKuzuIndex: (storagePath: string) => Promise<boolean>;
|
|
43
|
+
/**
|
|
44
|
+
* Clean up stale KuzuDB files after migration to LadybugDB
|
|
45
|
+
* @returns found (kuzu existed and was deleted), needsReindex (kuzu existed but lbug does not)
|
|
46
|
+
*/
|
|
47
|
+
export declare const cleanupOldKuzuFiles: (storagePath: string) => Promise<{
|
|
48
|
+
found: boolean;
|
|
49
|
+
needsReindex: boolean;
|
|
50
|
+
}>;
|
|
51
|
+
/** Load metadata from an indexed repo */
|
|
52
|
+
export declare const loadMeta: (storagePath: string) => Promise<RepoMeta | null>;
|
|
53
|
+
/** Save metadata to storage */
|
|
54
|
+
export declare const saveMeta: (storagePath: string, meta: RepoMeta) => Promise<void>;
|
|
55
|
+
/** Check if a path has a Code Mapper index */
|
|
56
|
+
export declare const hasIndex: (repoPath: string) => Promise<boolean>;
|
|
57
|
+
/** Load an indexed repo from a path */
|
|
58
|
+
export declare const loadRepo: (repoPath: string) => Promise<IndexedRepo | null>;
|
|
59
|
+
/** Find .code-mapper by walking up from a starting path */
|
|
60
|
+
export declare const findRepo: (startPath: string) => Promise<IndexedRepo | null>;
|
|
61
|
+
/** Add .code-mapper to .gitignore if not already present */
|
|
62
|
+
export declare const addToGitignore: (repoPath: string) => Promise<void>;
|
|
63
|
+
/** Get the path to the global Code Mapper directory */
|
|
64
|
+
export declare const getGlobalDir: () => string;
|
|
65
|
+
/** Get the path to the global registry file */
|
|
66
|
+
export declare const getGlobalRegistryPath: () => string;
|
|
67
|
+
/** Read the global registry, returns empty array if not found */
|
|
68
|
+
export declare const readRegistry: () => Promise<RegistryEntry[]>;
|
|
69
|
+
/** Register (add or update) a repo in the global registry after analyze */
|
|
70
|
+
export declare const registerRepo: (repoPath: string, meta: RepoMeta) => Promise<void>;
|
|
71
|
+
/** Remove a repo from the global registry after clean */
|
|
72
|
+
export declare const unregisterRepo: (repoPath: string) => Promise<void>;
|
|
73
|
+
/** List all registered repos, optionally validating each .code-mapper/ still exists */
|
|
74
|
+
export declare const listRegisteredRepos: (opts?: {
|
|
75
|
+
validate?: boolean;
|
|
76
|
+
}) => Promise<RegistryEntry[]>;
|
|
77
|
+
export interface CLIConfig {
|
|
78
|
+
apiKey?: string;
|
|
79
|
+
model?: string;
|
|
80
|
+
baseUrl?: string;
|
|
81
|
+
}
|
|
82
|
+
/** Get the path to the global CLI config file */
|
|
83
|
+
export declare const getGlobalConfigPath: () => string;
|
|
84
|
+
/** Load CLI config from ~/.code-mapper/config.json */
|
|
85
|
+
export declare const loadCLIConfig: () => Promise<CLIConfig>;
|
|
86
|
+
/** Save CLI config to ~/.code-mapper/config.json */
|
|
87
|
+
export declare const saveCLIConfig: (config: CLIConfig) => Promise<void>;
|
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
// code-mapper/src/storage/repo-manager.ts
|
|
2
|
+
/** @file repo-manager.ts
|
|
3
|
+
* @description Manages Code Mapper index storage in .code-mapper/ at repo root
|
|
4
|
+
* Maintains a global registry at ~/.code-mapper/registry.json for MCP repo discovery */
|
|
5
|
+
import fs from 'fs/promises';
|
|
6
|
+
import path from 'path';
|
|
7
|
+
import os from 'os';
|
|
8
|
+
const CODE_MAPPER_DIR = '.code-mapper';
|
|
9
|
+
// Local Storage Helpers
|
|
10
|
+
/** Get the .code-mapper storage path for a repository */
|
|
11
|
+
export const getStoragePath = (repoPath) => {
|
|
12
|
+
return path.join(path.resolve(repoPath), CODE_MAPPER_DIR);
|
|
13
|
+
};
|
|
14
|
+
/** Get paths to key storage files */
|
|
15
|
+
export const getStoragePaths = (repoPath) => {
|
|
16
|
+
const storagePath = getStoragePath(repoPath);
|
|
17
|
+
return {
|
|
18
|
+
storagePath,
|
|
19
|
+
lbugPath: path.join(storagePath, 'lbug'),
|
|
20
|
+
metaPath: path.join(storagePath, 'meta.json'),
|
|
21
|
+
};
|
|
22
|
+
};
|
|
23
|
+
/** Check whether a KuzuDB index exists (non-destructive, safe for status commands) */
|
|
24
|
+
export const hasKuzuIndex = async (storagePath) => {
|
|
25
|
+
try {
|
|
26
|
+
await fs.stat(path.join(storagePath, 'kuzu'));
|
|
27
|
+
return true;
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
/**
|
|
34
|
+
* Clean up stale KuzuDB files after migration to LadybugDB
|
|
35
|
+
* @returns found (kuzu existed and was deleted), needsReindex (kuzu existed but lbug does not)
|
|
36
|
+
*/
|
|
37
|
+
export const cleanupOldKuzuFiles = async (storagePath) => {
|
|
38
|
+
const oldPath = path.join(storagePath, 'kuzu');
|
|
39
|
+
const newPath = path.join(storagePath, 'lbug');
|
|
40
|
+
try {
|
|
41
|
+
await fs.stat(oldPath);
|
|
42
|
+
// Old kuzu file/dir exists — determine if lbug is already present
|
|
43
|
+
let needsReindex = false;
|
|
44
|
+
try {
|
|
45
|
+
await fs.stat(newPath);
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
needsReindex = true;
|
|
49
|
+
}
|
|
50
|
+
// Delete kuzu database file and its sidecars (.wal, .lock)
|
|
51
|
+
for (const suffix of ['', '.wal', '.lock']) {
|
|
52
|
+
try {
|
|
53
|
+
await fs.unlink(oldPath + suffix);
|
|
54
|
+
}
|
|
55
|
+
catch { }
|
|
56
|
+
}
|
|
57
|
+
// Also handle the case where kuzu was stored as a directory
|
|
58
|
+
try {
|
|
59
|
+
await fs.rm(oldPath, { recursive: true, force: true });
|
|
60
|
+
}
|
|
61
|
+
catch { }
|
|
62
|
+
return { found: true, needsReindex };
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
// Old path doesn't exist — nothing to do
|
|
66
|
+
return { found: false, needsReindex: false };
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
/** Load metadata from an indexed repo */
|
|
70
|
+
export const loadMeta = async (storagePath) => {
|
|
71
|
+
try {
|
|
72
|
+
const metaPath = path.join(storagePath, 'meta.json');
|
|
73
|
+
const raw = await fs.readFile(metaPath, 'utf-8');
|
|
74
|
+
return JSON.parse(raw);
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
/** Save metadata to storage */
|
|
81
|
+
export const saveMeta = async (storagePath, meta) => {
|
|
82
|
+
await fs.mkdir(storagePath, { recursive: true });
|
|
83
|
+
const metaPath = path.join(storagePath, 'meta.json');
|
|
84
|
+
await fs.writeFile(metaPath, JSON.stringify(meta, null, 2), 'utf-8');
|
|
85
|
+
};
|
|
86
|
+
/** Check if a path has a Code Mapper index */
|
|
87
|
+
export const hasIndex = async (repoPath) => {
|
|
88
|
+
const { metaPath } = getStoragePaths(repoPath);
|
|
89
|
+
try {
|
|
90
|
+
await fs.access(metaPath);
|
|
91
|
+
return true;
|
|
92
|
+
}
|
|
93
|
+
catch {
|
|
94
|
+
return false;
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
/** Load an indexed repo from a path */
|
|
98
|
+
export const loadRepo = async (repoPath) => {
|
|
99
|
+
const paths = getStoragePaths(repoPath);
|
|
100
|
+
const meta = await loadMeta(paths.storagePath);
|
|
101
|
+
if (!meta)
|
|
102
|
+
return null;
|
|
103
|
+
return {
|
|
104
|
+
repoPath: path.resolve(repoPath),
|
|
105
|
+
...paths,
|
|
106
|
+
meta,
|
|
107
|
+
};
|
|
108
|
+
};
|
|
109
|
+
/** Find .code-mapper by walking up from a starting path */
|
|
110
|
+
export const findRepo = async (startPath) => {
|
|
111
|
+
let current = path.resolve(startPath);
|
|
112
|
+
const root = path.parse(current).root;
|
|
113
|
+
while (current !== root) {
|
|
114
|
+
const repo = await loadRepo(current);
|
|
115
|
+
if (repo)
|
|
116
|
+
return repo;
|
|
117
|
+
current = path.dirname(current);
|
|
118
|
+
}
|
|
119
|
+
return null;
|
|
120
|
+
};
|
|
121
|
+
/** Add .code-mapper to .gitignore if not already present */
|
|
122
|
+
export const addToGitignore = async (repoPath) => {
|
|
123
|
+
const gitignorePath = path.join(repoPath, '.gitignore');
|
|
124
|
+
try {
|
|
125
|
+
const content = await fs.readFile(gitignorePath, 'utf-8');
|
|
126
|
+
if (content.includes(CODE_MAPPER_DIR))
|
|
127
|
+
return;
|
|
128
|
+
const newContent = content.endsWith('\n')
|
|
129
|
+
? `${content}${CODE_MAPPER_DIR}\n`
|
|
130
|
+
: `${content}\n${CODE_MAPPER_DIR}\n`;
|
|
131
|
+
await fs.writeFile(gitignorePath, newContent, 'utf-8');
|
|
132
|
+
}
|
|
133
|
+
catch {
|
|
134
|
+
// .gitignore doesn't exist, create it
|
|
135
|
+
await fs.writeFile(gitignorePath, `${CODE_MAPPER_DIR}\n`, 'utf-8');
|
|
136
|
+
}
|
|
137
|
+
};
|
|
138
|
+
// Global Registry (~/.code-mapper/registry.json)
|
|
139
|
+
/** Get the path to the global Code Mapper directory */
|
|
140
|
+
export const getGlobalDir = () => {
|
|
141
|
+
return path.join(os.homedir(), '.code-mapper');
|
|
142
|
+
};
|
|
143
|
+
/** Get the path to the global registry file */
|
|
144
|
+
export const getGlobalRegistryPath = () => {
|
|
145
|
+
return path.join(getGlobalDir(), 'registry.json');
|
|
146
|
+
};
|
|
147
|
+
/** Read the global registry, returns empty array if not found */
|
|
148
|
+
export const readRegistry = async () => {
|
|
149
|
+
try {
|
|
150
|
+
const raw = await fs.readFile(getGlobalRegistryPath(), 'utf-8');
|
|
151
|
+
const data = JSON.parse(raw);
|
|
152
|
+
return Array.isArray(data) ? data : [];
|
|
153
|
+
}
|
|
154
|
+
catch {
|
|
155
|
+
return [];
|
|
156
|
+
}
|
|
157
|
+
};
|
|
158
|
+
/** Write the global registry to disk */
|
|
159
|
+
const writeRegistry = async (entries) => {
|
|
160
|
+
const dir = getGlobalDir();
|
|
161
|
+
await fs.mkdir(dir, { recursive: true });
|
|
162
|
+
await fs.writeFile(getGlobalRegistryPath(), JSON.stringify(entries, null, 2), 'utf-8');
|
|
163
|
+
};
|
|
164
|
+
/** Register (add or update) a repo in the global registry after analyze */
|
|
165
|
+
export const registerRepo = async (repoPath, meta) => {
|
|
166
|
+
const resolved = path.resolve(repoPath);
|
|
167
|
+
const name = path.basename(resolved);
|
|
168
|
+
const { storagePath } = getStoragePaths(resolved);
|
|
169
|
+
const entries = await readRegistry();
|
|
170
|
+
const existing = entries.findIndex((e) => {
|
|
171
|
+
const a = path.resolve(e.path);
|
|
172
|
+
const b = resolved;
|
|
173
|
+
return process.platform === 'win32'
|
|
174
|
+
? a.toLowerCase() === b.toLowerCase()
|
|
175
|
+
: a === b;
|
|
176
|
+
});
|
|
177
|
+
const entry = {
|
|
178
|
+
name,
|
|
179
|
+
path: resolved,
|
|
180
|
+
storagePath,
|
|
181
|
+
indexedAt: meta.indexedAt,
|
|
182
|
+
lastCommit: meta.lastCommit,
|
|
183
|
+
stats: meta.stats,
|
|
184
|
+
};
|
|
185
|
+
if (existing >= 0) {
|
|
186
|
+
entries[existing] = entry;
|
|
187
|
+
}
|
|
188
|
+
else {
|
|
189
|
+
entries.push(entry);
|
|
190
|
+
}
|
|
191
|
+
await writeRegistry(entries);
|
|
192
|
+
};
|
|
193
|
+
/** Remove a repo from the global registry after clean */
|
|
194
|
+
export const unregisterRepo = async (repoPath) => {
|
|
195
|
+
const resolved = path.resolve(repoPath);
|
|
196
|
+
const entries = await readRegistry();
|
|
197
|
+
const filtered = entries.filter((e) => path.resolve(e.path) !== resolved);
|
|
198
|
+
await writeRegistry(filtered);
|
|
199
|
+
};
|
|
200
|
+
/** List all registered repos, optionally validating each .code-mapper/ still exists */
|
|
201
|
+
export const listRegisteredRepos = async (opts) => {
|
|
202
|
+
const entries = await readRegistry();
|
|
203
|
+
if (!opts?.validate)
|
|
204
|
+
return entries;
|
|
205
|
+
// Validate each entry still has a .code-mapper/ directory
|
|
206
|
+
const valid = [];
|
|
207
|
+
for (const entry of entries) {
|
|
208
|
+
try {
|
|
209
|
+
await fs.access(path.join(entry.storagePath, 'meta.json'));
|
|
210
|
+
valid.push(entry);
|
|
211
|
+
}
|
|
212
|
+
catch {
|
|
213
|
+
// Index no longer exists — skip
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
// If we pruned any entries, save the cleaned registry
|
|
217
|
+
if (valid.length !== entries.length) {
|
|
218
|
+
await writeRegistry(valid);
|
|
219
|
+
}
|
|
220
|
+
return valid;
|
|
221
|
+
};
|
|
222
|
+
/** Get the path to the global CLI config file */
|
|
223
|
+
export const getGlobalConfigPath = () => {
|
|
224
|
+
return path.join(getGlobalDir(), 'config.json');
|
|
225
|
+
};
|
|
226
|
+
/** Load CLI config from ~/.code-mapper/config.json */
|
|
227
|
+
export const loadCLIConfig = async () => {
|
|
228
|
+
try {
|
|
229
|
+
const raw = await fs.readFile(getGlobalConfigPath(), 'utf-8');
|
|
230
|
+
return JSON.parse(raw);
|
|
231
|
+
}
|
|
232
|
+
catch {
|
|
233
|
+
return {};
|
|
234
|
+
}
|
|
235
|
+
};
|
|
236
|
+
/** Save CLI config to ~/.code-mapper/config.json */
|
|
237
|
+
export const saveCLIConfig = async (config) => {
|
|
238
|
+
const dir = getGlobalDir();
|
|
239
|
+
await fs.mkdir(dir, { recursive: true });
|
|
240
|
+
const configPath = getGlobalConfigPath();
|
|
241
|
+
await fs.writeFile(configPath, JSON.stringify(config, null, 2), 'utf-8');
|
|
242
|
+
// Restrict file permissions on Unix (config may contain API keys)
|
|
243
|
+
if (process.platform !== 'win32') {
|
|
244
|
+
try {
|
|
245
|
+
await fs.chmod(configPath, 0o600);
|
|
246
|
+
}
|
|
247
|
+
catch { /* best-effort */ }
|
|
248
|
+
}
|
|
249
|
+
};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/** @file pipeline.ts @description Types for the ingestion pipeline: phases, progress, results, and serialization helpers */
|
|
2
|
+
import { GraphNode, GraphRelationship, KnowledgeGraph } from '../core/graph/types.js';
|
|
3
|
+
import { CommunityDetectionResult } from '../core/ingestion/community-processor.js';
|
|
4
|
+
import { ProcessDetectionResult } from '../core/ingestion/process-processor.js';
|
|
5
|
+
export type PipelinePhase = 'idle' | 'extracting' | 'structure' | 'parsing' | 'imports' | 'calls' | 'heritage' | 'communities' | 'processes' | 'enriching' | 'complete' | 'error';
|
|
6
|
+
export interface PipelineProgress {
|
|
7
|
+
phase: PipelinePhase;
|
|
8
|
+
percent: number;
|
|
9
|
+
message: string;
|
|
10
|
+
detail?: string;
|
|
11
|
+
stats?: {
|
|
12
|
+
filesProcessed: number;
|
|
13
|
+
totalFiles: number;
|
|
14
|
+
nodesCreated: number;
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
export interface PipelineResult {
|
|
18
|
+
graph: KnowledgeGraph;
|
|
19
|
+
/** Absolute path to the repo root, used for lazy file reads during LadybugDB loading */
|
|
20
|
+
repoPath: string;
|
|
21
|
+
/** Total files scanned */
|
|
22
|
+
totalFileCount: number;
|
|
23
|
+
communityResult?: CommunityDetectionResult;
|
|
24
|
+
processResult?: ProcessDetectionResult;
|
|
25
|
+
}
|
|
26
|
+
export interface SerializablePipelineResult {
|
|
27
|
+
nodes: GraphNode[];
|
|
28
|
+
relationships: GraphRelationship[];
|
|
29
|
+
repoPath: string;
|
|
30
|
+
totalFileCount: number;
|
|
31
|
+
}
|
|
32
|
+
/** Convert PipelineResult to a serializable format for postMessage */
|
|
33
|
+
export declare const serializePipelineResult: (result: PipelineResult) => SerializablePipelineResult;
|
|
34
|
+
/** Reconstruct a PipelineResult from serialized format (used in main thread) */
|
|
35
|
+
export declare const deserializePipelineResult: (serialized: SerializablePipelineResult, createGraph: () => KnowledgeGraph) => PipelineResult;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// code-mapper/src/types/pipeline.ts
|
|
2
|
+
/** @file pipeline.ts @description Types for the ingestion pipeline: phases, progress, results, and serialization helpers */
|
|
3
|
+
/** Convert PipelineResult to a serializable format for postMessage */
|
|
4
|
+
export const serializePipelineResult = (result) => ({
|
|
5
|
+
nodes: [...result.graph.iterNodes()],
|
|
6
|
+
relationships: [...result.graph.iterRelationships()],
|
|
7
|
+
repoPath: result.repoPath,
|
|
8
|
+
totalFileCount: result.totalFileCount,
|
|
9
|
+
});
|
|
10
|
+
/** Reconstruct a PipelineResult from serialized format (used in main thread) */
|
|
11
|
+
export const deserializePipelineResult = (serialized, createGraph) => {
|
|
12
|
+
const graph = createGraph();
|
|
13
|
+
serialized.nodes.forEach(node => graph.addNode(node));
|
|
14
|
+
serialized.relationships.forEach(rel => graph.addRelationship(rel));
|
|
15
|
+
return {
|
|
16
|
+
graph,
|
|
17
|
+
repoPath: serialized.repoPath,
|
|
18
|
+
totalFileCount: serialized.totalFileCount,
|
|
19
|
+
};
|
|
20
|
+
};
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Code Mapper Claude Code Hook
|
|
4
|
+
*
|
|
5
|
+
* PreToolUse — intercepts Grep/Glob/Bash searches and augments
|
|
6
|
+
* with graph context from the Code Mapper index.
|
|
7
|
+
* PostToolUse — detects stale index after git mutations and notifies
|
|
8
|
+
* the agent to reindex.
|
|
9
|
+
*
|
|
10
|
+
* NOTE: SessionStart hooks are broken on Windows (Claude Code bug).
|
|
11
|
+
* Session context is injected via CLAUDE.md / skills instead.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
const fs = require('fs');
|
|
15
|
+
const path = require('path');
|
|
16
|
+
const { spawnSync } = require('child_process');
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Read JSON input from stdin synchronously.
|
|
20
|
+
*/
|
|
21
|
+
function readInput() {
|
|
22
|
+
try {
|
|
23
|
+
const data = fs.readFileSync(0, 'utf-8');
|
|
24
|
+
return JSON.parse(data);
|
|
25
|
+
} catch {
|
|
26
|
+
return {};
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Find the .code-mapper directory by walking up from startDir.
|
|
32
|
+
* Returns the path to .code-mapper/ or null if not found.
|
|
33
|
+
*/
|
|
34
|
+
function findCodeMapperDir(startDir) {
|
|
35
|
+
let dir = startDir || process.cwd();
|
|
36
|
+
for (let i = 0; i < 5; i++) {
|
|
37
|
+
const candidate = path.join(dir, '.code-mapper');
|
|
38
|
+
if (fs.existsSync(candidate)) return candidate;
|
|
39
|
+
const parent = path.dirname(dir);
|
|
40
|
+
if (parent === dir) break;
|
|
41
|
+
dir = parent;
|
|
42
|
+
}
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Extract search pattern from tool input.
|
|
48
|
+
*/
|
|
49
|
+
function extractPattern(toolName, toolInput) {
|
|
50
|
+
if (toolName === 'Grep') {
|
|
51
|
+
return toolInput.pattern || null;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (toolName === 'Glob') {
|
|
55
|
+
const raw = toolInput.pattern || '';
|
|
56
|
+
const match = raw.match(/[*\/]([a-zA-Z][a-zA-Z0-9_-]{2,})/);
|
|
57
|
+
return match ? match[1] : null;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (toolName === 'Bash') {
|
|
61
|
+
const cmd = toolInput.command || '';
|
|
62
|
+
if (!/\brg\b|\bgrep\b/.test(cmd)) return null;
|
|
63
|
+
|
|
64
|
+
const tokens = cmd.split(/\s+/);
|
|
65
|
+
let foundCmd = false;
|
|
66
|
+
let skipNext = false;
|
|
67
|
+
const flagsWithValues = new Set(['-e', '-f', '-m', '-A', '-B', '-C', '-g', '--glob', '-t', '--type', '--include', '--exclude']);
|
|
68
|
+
|
|
69
|
+
for (const token of tokens) {
|
|
70
|
+
if (skipNext) { skipNext = false; continue; }
|
|
71
|
+
if (!foundCmd) {
|
|
72
|
+
if (/\brg$|\bgrep$/.test(token)) foundCmd = true;
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
if (token.startsWith('-')) {
|
|
76
|
+
if (flagsWithValues.has(token)) skipNext = true;
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
const cleaned = token.replace(/['"]/g, '');
|
|
80
|
+
return cleaned.length >= 3 ? cleaned : null;
|
|
81
|
+
}
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Resolve the code-mapper CLI path.
|
|
90
|
+
* 1. Relative path (works when script is inside npm package)
|
|
91
|
+
* 2. require.resolve (works when code-mapper is globally installed)
|
|
92
|
+
* 3. Fall back to npx (returns empty string)
|
|
93
|
+
*/
|
|
94
|
+
function resolveCliPath() {
|
|
95
|
+
let cliPath = path.resolve(__dirname, '..', '..', 'dist', 'cli', 'index.js');
|
|
96
|
+
if (!fs.existsSync(cliPath)) {
|
|
97
|
+
try {
|
|
98
|
+
cliPath = require.resolve('code-mapper/dist/cli/index.js');
|
|
99
|
+
} catch {
|
|
100
|
+
cliPath = '';
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return cliPath;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Spawn a code-mapper CLI command synchronously.
|
|
108
|
+
* Returns the stderr output (KuzuDB captures stdout at OS level).
|
|
109
|
+
*/
|
|
110
|
+
function runCodeMapperCli(cliPath, args, cwd, timeout) {
|
|
111
|
+
const isWin = process.platform === 'win32';
|
|
112
|
+
if (cliPath) {
|
|
113
|
+
return spawnSync(
|
|
114
|
+
process.execPath,
|
|
115
|
+
[cliPath, ...args],
|
|
116
|
+
{ encoding: 'utf-8', timeout, cwd, stdio: ['pipe', 'pipe', 'pipe'] }
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
// On Windows, invoke npx.cmd directly (no shell needed)
|
|
120
|
+
return spawnSync(
|
|
121
|
+
isWin ? 'npx.cmd' : 'npx',
|
|
122
|
+
['-y', 'code-mapper', ...args],
|
|
123
|
+
{ encoding: 'utf-8', timeout: timeout + 5000, cwd, stdio: ['pipe', 'pipe', 'pipe'] }
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* PreToolUse handler — augment searches with graph context.
|
|
129
|
+
*/
|
|
130
|
+
function handlePreToolUse(input) {
|
|
131
|
+
const cwd = input.cwd || process.cwd();
|
|
132
|
+
if (!path.isAbsolute(cwd)) return;
|
|
133
|
+
if (!findCodeMapperDir(cwd)) return;
|
|
134
|
+
|
|
135
|
+
const toolName = input.tool_name || '';
|
|
136
|
+
const toolInput = input.tool_input || {};
|
|
137
|
+
|
|
138
|
+
if (toolName !== 'Grep' && toolName !== 'Glob' && toolName !== 'Bash') return;
|
|
139
|
+
|
|
140
|
+
const pattern = extractPattern(toolName, toolInput);
|
|
141
|
+
if (!pattern || pattern.length < 3) return;
|
|
142
|
+
|
|
143
|
+
const cliPath = resolveCliPath();
|
|
144
|
+
let result = '';
|
|
145
|
+
try {
|
|
146
|
+
const child = runCodeMapperCli(cliPath, ['augment', '--', pattern], cwd, 7000);
|
|
147
|
+
if (!child.error && child.status === 0) {
|
|
148
|
+
result = child.stderr || '';
|
|
149
|
+
}
|
|
150
|
+
} catch { /* graceful failure */ }
|
|
151
|
+
|
|
152
|
+
if (result && result.trim()) {
|
|
153
|
+
sendHookResponse('PreToolUse', result.trim());
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Emit a PostToolUse hook response with additional context for the agent.
|
|
159
|
+
*/
|
|
160
|
+
function sendHookResponse(hookEventName, message) {
|
|
161
|
+
console.log(JSON.stringify({
|
|
162
|
+
hookSpecificOutput: { hookEventName, additionalContext: message }
|
|
163
|
+
}));
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* PostToolUse handler — detect index staleness after git mutations.
|
|
168
|
+
*
|
|
169
|
+
* Instead of spawning a full `code-mapper analyze` synchronously (which blocks
|
|
170
|
+
* the agent for up to 120s and risks KuzuDB corruption on timeout), we do a
|
|
171
|
+
* lightweight staleness check: compare `git rev-parse HEAD` against the
|
|
172
|
+
* lastCommit stored in `.code-mapper/meta.json`. If they differ, notify the
|
|
173
|
+
* agent so it can decide when to reindex.
|
|
174
|
+
*/
|
|
175
|
+
function handlePostToolUse(input) {
|
|
176
|
+
const toolName = input.tool_name || '';
|
|
177
|
+
if (toolName !== 'Bash') return;
|
|
178
|
+
|
|
179
|
+
const command = (input.tool_input || {}).command || '';
|
|
180
|
+
if (!/\bgit\s+(commit|merge|rebase|cherry-pick|pull)(\s|$)/.test(command)) return;
|
|
181
|
+
|
|
182
|
+
// Only proceed if the command succeeded
|
|
183
|
+
const toolOutput = input.tool_output || {};
|
|
184
|
+
if (toolOutput.exit_code !== undefined && toolOutput.exit_code !== 0) return;
|
|
185
|
+
|
|
186
|
+
const cwd = input.cwd || process.cwd();
|
|
187
|
+
if (!path.isAbsolute(cwd)) return;
|
|
188
|
+
const codeMapperDir = findCodeMapperDir(cwd);
|
|
189
|
+
if (!codeMapperDir) return;
|
|
190
|
+
|
|
191
|
+
// Compare HEAD against last indexed commit — skip if unchanged
|
|
192
|
+
let currentHead = '';
|
|
193
|
+
try {
|
|
194
|
+
const headResult = spawnSync('git', ['rev-parse', 'HEAD'], {
|
|
195
|
+
encoding: 'utf-8', timeout: 3000, cwd, stdio: ['pipe', 'pipe', 'pipe'],
|
|
196
|
+
});
|
|
197
|
+
currentHead = (headResult.stdout || '').trim();
|
|
198
|
+
} catch { return; }
|
|
199
|
+
|
|
200
|
+
if (!currentHead) return;
|
|
201
|
+
|
|
202
|
+
let lastCommit = '';
|
|
203
|
+
let hadEmbeddings = false;
|
|
204
|
+
try {
|
|
205
|
+
const meta = JSON.parse(fs.readFileSync(path.join(codeMapperDir, 'meta.json'), 'utf-8'));
|
|
206
|
+
lastCommit = meta.lastCommit || '';
|
|
207
|
+
hadEmbeddings = (meta.stats && meta.stats.embeddings > 0);
|
|
208
|
+
} catch { /* no meta — treat as stale */ }
|
|
209
|
+
|
|
210
|
+
// If HEAD matches last indexed commit, no reindex needed
|
|
211
|
+
if (currentHead && currentHead === lastCommit) return;
|
|
212
|
+
|
|
213
|
+
const analyzeCmd = `npx code-mapper analyze${hadEmbeddings ? ' --embeddings' : ''}`;
|
|
214
|
+
sendHookResponse('PostToolUse',
|
|
215
|
+
`Code Mapper index is stale (last indexed: ${lastCommit ? lastCommit.slice(0, 7) : 'never'}). ` +
|
|
216
|
+
`Run \`${analyzeCmd}\` to update the knowledge graph.`
|
|
217
|
+
);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// Dispatch map for hook events
|
|
221
|
+
const handlers = {
|
|
222
|
+
PreToolUse: handlePreToolUse,
|
|
223
|
+
PostToolUse: handlePostToolUse,
|
|
224
|
+
};
|
|
225
|
+
|
|
226
|
+
function main() {
|
|
227
|
+
try {
|
|
228
|
+
const input = readInput();
|
|
229
|
+
const handler = handlers[input.hook_event_name || ''];
|
|
230
|
+
if (handler) handler(input);
|
|
231
|
+
} catch (err) {
|
|
232
|
+
if (process.env.CODE_MAPPER_DEBUG) {
|
|
233
|
+
console.error('Code Mapper hook error:', (err.message || '').slice(0, 200));
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
main();
|