@duytransipher/gitnexus 1.4.6-sipher.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 +73 -0
- package/README.md +261 -0
- package/dist/cli/ai-context.d.ts +23 -0
- package/dist/cli/ai-context.js +265 -0
- package/dist/cli/analyze.d.ts +12 -0
- package/dist/cli/analyze.js +345 -0
- package/dist/cli/augment.d.ts +13 -0
- package/dist/cli/augment.js +33 -0
- package/dist/cli/clean.d.ts +10 -0
- package/dist/cli/clean.js +60 -0
- package/dist/cli/eval-server.d.ts +37 -0
- package/dist/cli/eval-server.js +389 -0
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.js +137 -0
- package/dist/cli/lazy-action.d.ts +6 -0
- package/dist/cli/lazy-action.js +18 -0
- package/dist/cli/list.d.ts +6 -0
- package/dist/cli/list.js +30 -0
- package/dist/cli/mcp.d.ts +8 -0
- package/dist/cli/mcp.js +36 -0
- package/dist/cli/serve.d.ts +4 -0
- package/dist/cli/serve.js +6 -0
- package/dist/cli/setup.d.ts +8 -0
- package/dist/cli/setup.js +367 -0
- package/dist/cli/sipher-patched.d.ts +2 -0
- package/dist/cli/sipher-patched.js +77 -0
- package/dist/cli/skill-gen.d.ts +26 -0
- package/dist/cli/skill-gen.js +549 -0
- package/dist/cli/status.d.ts +6 -0
- package/dist/cli/status.js +36 -0
- package/dist/cli/tool.d.ts +60 -0
- package/dist/cli/tool.js +180 -0
- package/dist/cli/wiki.d.ts +15 -0
- package/dist/cli/wiki.js +365 -0
- package/dist/config/ignore-service.d.ts +26 -0
- package/dist/config/ignore-service.js +284 -0
- package/dist/config/supported-languages.d.ts +15 -0
- package/dist/config/supported-languages.js +16 -0
- package/dist/core/augmentation/engine.d.ts +26 -0
- package/dist/core/augmentation/engine.js +240 -0
- package/dist/core/embeddings/embedder.d.ts +60 -0
- package/dist/core/embeddings/embedder.js +251 -0
- package/dist/core/embeddings/embedding-pipeline.d.ts +51 -0
- package/dist/core/embeddings/embedding-pipeline.js +356 -0
- package/dist/core/embeddings/index.d.ts +9 -0
- package/dist/core/embeddings/index.js +9 -0
- package/dist/core/embeddings/text-generator.d.ts +24 -0
- package/dist/core/embeddings/text-generator.js +182 -0
- package/dist/core/embeddings/types.d.ts +87 -0
- package/dist/core/embeddings/types.js +32 -0
- package/dist/core/graph/graph.d.ts +2 -0
- package/dist/core/graph/graph.js +66 -0
- package/dist/core/graph/types.d.ts +66 -0
- package/dist/core/graph/types.js +1 -0
- package/dist/core/ingestion/ast-cache.d.ts +11 -0
- package/dist/core/ingestion/ast-cache.js +35 -0
- package/dist/core/ingestion/call-processor.d.ts +23 -0
- package/dist/core/ingestion/call-processor.js +793 -0
- package/dist/core/ingestion/call-routing.d.ts +68 -0
- package/dist/core/ingestion/call-routing.js +129 -0
- package/dist/core/ingestion/cluster-enricher.d.ts +38 -0
- package/dist/core/ingestion/cluster-enricher.js +170 -0
- package/dist/core/ingestion/community-processor.d.ts +39 -0
- package/dist/core/ingestion/community-processor.js +312 -0
- package/dist/core/ingestion/constants.d.ts +16 -0
- package/dist/core/ingestion/constants.js +16 -0
- package/dist/core/ingestion/entry-point-scoring.d.ts +40 -0
- package/dist/core/ingestion/entry-point-scoring.js +353 -0
- package/dist/core/ingestion/export-detection.d.ts +18 -0
- package/dist/core/ingestion/export-detection.js +231 -0
- package/dist/core/ingestion/filesystem-walker.d.ts +28 -0
- package/dist/core/ingestion/filesystem-walker.js +81 -0
- package/dist/core/ingestion/framework-detection.d.ts +54 -0
- package/dist/core/ingestion/framework-detection.js +411 -0
- package/dist/core/ingestion/heritage-processor.d.ts +28 -0
- package/dist/core/ingestion/heritage-processor.js +251 -0
- package/dist/core/ingestion/import-processor.d.ts +34 -0
- package/dist/core/ingestion/import-processor.js +398 -0
- package/dist/core/ingestion/language-config.d.ts +46 -0
- package/dist/core/ingestion/language-config.js +167 -0
- package/dist/core/ingestion/mro-processor.d.ts +45 -0
- package/dist/core/ingestion/mro-processor.js +369 -0
- package/dist/core/ingestion/named-binding-extraction.d.ts +61 -0
- package/dist/core/ingestion/named-binding-extraction.js +363 -0
- package/dist/core/ingestion/parsing-processor.d.ts +19 -0
- package/dist/core/ingestion/parsing-processor.js +315 -0
- package/dist/core/ingestion/pipeline.d.ts +6 -0
- package/dist/core/ingestion/pipeline.js +401 -0
- package/dist/core/ingestion/process-processor.d.ts +51 -0
- package/dist/core/ingestion/process-processor.js +315 -0
- package/dist/core/ingestion/resolution-context.d.ts +53 -0
- package/dist/core/ingestion/resolution-context.js +132 -0
- package/dist/core/ingestion/resolvers/csharp.d.ts +22 -0
- package/dist/core/ingestion/resolvers/csharp.js +109 -0
- package/dist/core/ingestion/resolvers/go.d.ts +19 -0
- package/dist/core/ingestion/resolvers/go.js +42 -0
- package/dist/core/ingestion/resolvers/index.d.ts +18 -0
- package/dist/core/ingestion/resolvers/index.js +13 -0
- package/dist/core/ingestion/resolvers/jvm.d.ts +23 -0
- package/dist/core/ingestion/resolvers/jvm.js +87 -0
- package/dist/core/ingestion/resolvers/php.d.ts +15 -0
- package/dist/core/ingestion/resolvers/php.js +35 -0
- package/dist/core/ingestion/resolvers/python.d.ts +19 -0
- package/dist/core/ingestion/resolvers/python.js +52 -0
- package/dist/core/ingestion/resolvers/ruby.d.ts +12 -0
- package/dist/core/ingestion/resolvers/ruby.js +15 -0
- package/dist/core/ingestion/resolvers/rust.d.ts +15 -0
- package/dist/core/ingestion/resolvers/rust.js +73 -0
- package/dist/core/ingestion/resolvers/standard.d.ts +28 -0
- package/dist/core/ingestion/resolvers/standard.js +123 -0
- package/dist/core/ingestion/resolvers/utils.d.ts +33 -0
- package/dist/core/ingestion/resolvers/utils.js +122 -0
- package/dist/core/ingestion/structure-processor.d.ts +2 -0
- package/dist/core/ingestion/structure-processor.js +36 -0
- package/dist/core/ingestion/symbol-table.d.ts +63 -0
- package/dist/core/ingestion/symbol-table.js +85 -0
- package/dist/core/ingestion/tree-sitter-queries.d.ts +15 -0
- package/dist/core/ingestion/tree-sitter-queries.js +888 -0
- package/dist/core/ingestion/type-env.d.ts +49 -0
- package/dist/core/ingestion/type-env.js +613 -0
- package/dist/core/ingestion/type-extractors/c-cpp.d.ts +2 -0
- package/dist/core/ingestion/type-extractors/c-cpp.js +385 -0
- package/dist/core/ingestion/type-extractors/csharp.d.ts +2 -0
- package/dist/core/ingestion/type-extractors/csharp.js +383 -0
- package/dist/core/ingestion/type-extractors/go.d.ts +2 -0
- package/dist/core/ingestion/type-extractors/go.js +467 -0
- package/dist/core/ingestion/type-extractors/index.d.ts +22 -0
- package/dist/core/ingestion/type-extractors/index.js +31 -0
- package/dist/core/ingestion/type-extractors/jvm.d.ts +3 -0
- package/dist/core/ingestion/type-extractors/jvm.js +681 -0
- package/dist/core/ingestion/type-extractors/php.d.ts +2 -0
- package/dist/core/ingestion/type-extractors/php.js +549 -0
- package/dist/core/ingestion/type-extractors/python.d.ts +2 -0
- package/dist/core/ingestion/type-extractors/python.js +455 -0
- package/dist/core/ingestion/type-extractors/ruby.d.ts +2 -0
- package/dist/core/ingestion/type-extractors/ruby.js +389 -0
- package/dist/core/ingestion/type-extractors/rust.d.ts +2 -0
- package/dist/core/ingestion/type-extractors/rust.js +456 -0
- package/dist/core/ingestion/type-extractors/shared.d.ts +145 -0
- package/dist/core/ingestion/type-extractors/shared.js +810 -0
- package/dist/core/ingestion/type-extractors/swift.d.ts +2 -0
- package/dist/core/ingestion/type-extractors/swift.js +137 -0
- package/dist/core/ingestion/type-extractors/types.d.ts +127 -0
- package/dist/core/ingestion/type-extractors/types.js +1 -0
- package/dist/core/ingestion/type-extractors/typescript.d.ts +2 -0
- package/dist/core/ingestion/type-extractors/typescript.js +494 -0
- package/dist/core/ingestion/utils.d.ts +138 -0
- package/dist/core/ingestion/utils.js +1290 -0
- package/dist/core/ingestion/workers/parse-worker.d.ts +122 -0
- package/dist/core/ingestion/workers/parse-worker.js +1126 -0
- package/dist/core/ingestion/workers/worker-pool.d.ts +16 -0
- package/dist/core/ingestion/workers/worker-pool.js +128 -0
- package/dist/core/lbug/csv-generator.d.ts +33 -0
- package/dist/core/lbug/csv-generator.js +366 -0
- package/dist/core/lbug/lbug-adapter.d.ts +103 -0
- package/dist/core/lbug/lbug-adapter.js +769 -0
- package/dist/core/lbug/schema.d.ts +53 -0
- package/dist/core/lbug/schema.js +430 -0
- package/dist/core/search/bm25-index.d.ts +23 -0
- package/dist/core/search/bm25-index.js +96 -0
- package/dist/core/search/hybrid-search.d.ts +49 -0
- package/dist/core/search/hybrid-search.js +118 -0
- package/dist/core/tree-sitter/parser-loader.d.ts +5 -0
- package/dist/core/tree-sitter/parser-loader.js +63 -0
- package/dist/core/wiki/generator.d.ts +120 -0
- package/dist/core/wiki/generator.js +939 -0
- package/dist/core/wiki/graph-queries.d.ts +80 -0
- package/dist/core/wiki/graph-queries.js +238 -0
- package/dist/core/wiki/html-viewer.d.ts +10 -0
- package/dist/core/wiki/html-viewer.js +297 -0
- package/dist/core/wiki/llm-client.d.ts +43 -0
- package/dist/core/wiki/llm-client.js +186 -0
- package/dist/core/wiki/prompts.d.ts +53 -0
- package/dist/core/wiki/prompts.js +174 -0
- package/dist/lib/utils.d.ts +1 -0
- package/dist/lib/utils.js +3 -0
- package/dist/mcp/compatible-stdio-transport.d.ts +25 -0
- package/dist/mcp/compatible-stdio-transport.js +200 -0
- package/dist/mcp/core/embedder.d.ts +27 -0
- package/dist/mcp/core/embedder.js +108 -0
- package/dist/mcp/core/lbug-adapter.d.ts +57 -0
- package/dist/mcp/core/lbug-adapter.js +455 -0
- package/dist/mcp/local/local-backend.d.ts +181 -0
- package/dist/mcp/local/local-backend.js +1722 -0
- package/dist/mcp/resources.d.ts +31 -0
- package/dist/mcp/resources.js +411 -0
- package/dist/mcp/server.d.ts +23 -0
- package/dist/mcp/server.js +296 -0
- package/dist/mcp/staleness.d.ts +15 -0
- package/dist/mcp/staleness.js +29 -0
- package/dist/mcp/tools.d.ts +24 -0
- package/dist/mcp/tools.js +292 -0
- package/dist/server/api.d.ts +10 -0
- package/dist/server/api.js +344 -0
- package/dist/server/mcp-http.d.ts +13 -0
- package/dist/server/mcp-http.js +100 -0
- package/dist/storage/git.d.ts +6 -0
- package/dist/storage/git.js +35 -0
- package/dist/storage/repo-manager.d.ts +138 -0
- package/dist/storage/repo-manager.js +299 -0
- package/dist/types/pipeline.d.ts +32 -0
- package/dist/types/pipeline.js +18 -0
- package/dist/unreal/bridge.d.ts +4 -0
- package/dist/unreal/bridge.js +113 -0
- package/dist/unreal/config.d.ts +6 -0
- package/dist/unreal/config.js +55 -0
- package/dist/unreal/types.d.ts +105 -0
- package/dist/unreal/types.js +1 -0
- package/hooks/claude/gitnexus-hook.cjs +238 -0
- package/hooks/claude/pre-tool-use.sh +79 -0
- package/hooks/claude/session-start.sh +42 -0
- package/package.json +100 -0
- package/scripts/ensure-cli-executable.cjs +21 -0
- package/scripts/patch-tree-sitter-swift.cjs +74 -0
- package/scripts/setup-unreal-gitnexus.ps1 +191 -0
- package/skills/gitnexus-cli.md +82 -0
- package/skills/gitnexus-debugging.md +89 -0
- package/skills/gitnexus-exploring.md +78 -0
- package/skills/gitnexus-guide.md +64 -0
- package/skills/gitnexus-impact-analysis.md +97 -0
- package/skills/gitnexus-pr-review.md +163 -0
- package/skills/gitnexus-refactoring.md +121 -0
- package/vendor/leiden/index.cjs +355 -0
- package/vendor/leiden/utils.cjs +392 -0
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Repository Manager
|
|
3
|
+
*
|
|
4
|
+
* Manages GitNexus index storage in .gitnexus/ at repo root.
|
|
5
|
+
* Also maintains a global registry at ~/.gitnexus/registry.json
|
|
6
|
+
* so the MCP server can discover indexed repos from any cwd.
|
|
7
|
+
*/
|
|
8
|
+
import fs from 'fs/promises';
|
|
9
|
+
import path from 'path';
|
|
10
|
+
import os from 'os';
|
|
11
|
+
const GITNEXUS_DIR = '.gitnexus';
|
|
12
|
+
// ─── Local Storage Helpers ─────────────────────────────────────────────
|
|
13
|
+
/**
|
|
14
|
+
* Get the .gitnexus storage path for a repository
|
|
15
|
+
*/
|
|
16
|
+
export const getStoragePath = (repoPath) => {
|
|
17
|
+
return path.join(path.resolve(repoPath), GITNEXUS_DIR);
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* Get paths to key storage files
|
|
21
|
+
*/
|
|
22
|
+
export const getStoragePaths = (repoPath) => {
|
|
23
|
+
const storagePath = getStoragePath(repoPath);
|
|
24
|
+
return {
|
|
25
|
+
storagePath,
|
|
26
|
+
lbugPath: path.join(storagePath, 'lbug'),
|
|
27
|
+
metaPath: path.join(storagePath, 'meta.json'),
|
|
28
|
+
};
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* Check whether a KuzuDB index exists in the given storage path.
|
|
32
|
+
* Non-destructive — safe to call from status commands.
|
|
33
|
+
*/
|
|
34
|
+
export const hasKuzuIndex = async (storagePath) => {
|
|
35
|
+
try {
|
|
36
|
+
await fs.stat(path.join(storagePath, 'kuzu'));
|
|
37
|
+
return true;
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
/**
|
|
44
|
+
* Clean up stale KuzuDB files after migration to LadybugDB.
|
|
45
|
+
*
|
|
46
|
+
* Returns:
|
|
47
|
+
* found — true if .gitnexus/kuzu existed and was deleted
|
|
48
|
+
* needsReindex — true if kuzu existed but lbug does not (re-analyze required)
|
|
49
|
+
*
|
|
50
|
+
* Callers own the user-facing messaging; this function only deletes files.
|
|
51
|
+
*/
|
|
52
|
+
export const cleanupOldKuzuFiles = async (storagePath) => {
|
|
53
|
+
const oldPath = path.join(storagePath, 'kuzu');
|
|
54
|
+
const newPath = path.join(storagePath, 'lbug');
|
|
55
|
+
try {
|
|
56
|
+
await fs.stat(oldPath);
|
|
57
|
+
// Old kuzu file/dir exists — determine if lbug is already present
|
|
58
|
+
let needsReindex = false;
|
|
59
|
+
try {
|
|
60
|
+
await fs.stat(newPath);
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
needsReindex = true;
|
|
64
|
+
}
|
|
65
|
+
// Delete kuzu database file and its sidecars (.wal, .lock)
|
|
66
|
+
for (const suffix of ['', '.wal', '.lock']) {
|
|
67
|
+
try {
|
|
68
|
+
await fs.unlink(oldPath + suffix);
|
|
69
|
+
}
|
|
70
|
+
catch { }
|
|
71
|
+
}
|
|
72
|
+
// Also handle the case where kuzu was stored as a directory
|
|
73
|
+
try {
|
|
74
|
+
await fs.rm(oldPath, { recursive: true, force: true });
|
|
75
|
+
}
|
|
76
|
+
catch { }
|
|
77
|
+
return { found: true, needsReindex };
|
|
78
|
+
}
|
|
79
|
+
catch {
|
|
80
|
+
// Old path doesn't exist — nothing to do
|
|
81
|
+
return { found: false, needsReindex: false };
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
/**
|
|
85
|
+
* Load metadata from an indexed repo
|
|
86
|
+
*/
|
|
87
|
+
export const loadMeta = async (storagePath) => {
|
|
88
|
+
try {
|
|
89
|
+
const metaPath = path.join(storagePath, 'meta.json');
|
|
90
|
+
const raw = await fs.readFile(metaPath, 'utf-8');
|
|
91
|
+
return JSON.parse(raw);
|
|
92
|
+
}
|
|
93
|
+
catch {
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
/**
|
|
98
|
+
* Save metadata to storage
|
|
99
|
+
*/
|
|
100
|
+
export const saveMeta = async (storagePath, meta) => {
|
|
101
|
+
await fs.mkdir(storagePath, { recursive: true });
|
|
102
|
+
const metaPath = path.join(storagePath, 'meta.json');
|
|
103
|
+
await fs.writeFile(metaPath, JSON.stringify(meta, null, 2), 'utf-8');
|
|
104
|
+
};
|
|
105
|
+
/**
|
|
106
|
+
* Check if a path has a GitNexus index
|
|
107
|
+
*/
|
|
108
|
+
export const hasIndex = async (repoPath) => {
|
|
109
|
+
const { metaPath } = getStoragePaths(repoPath);
|
|
110
|
+
try {
|
|
111
|
+
await fs.access(metaPath);
|
|
112
|
+
return true;
|
|
113
|
+
}
|
|
114
|
+
catch {
|
|
115
|
+
return false;
|
|
116
|
+
}
|
|
117
|
+
};
|
|
118
|
+
/**
|
|
119
|
+
* Load an indexed repo from a path
|
|
120
|
+
*/
|
|
121
|
+
export const loadRepo = async (repoPath) => {
|
|
122
|
+
const paths = getStoragePaths(repoPath);
|
|
123
|
+
const meta = await loadMeta(paths.storagePath);
|
|
124
|
+
if (!meta)
|
|
125
|
+
return null;
|
|
126
|
+
return {
|
|
127
|
+
repoPath: path.resolve(repoPath),
|
|
128
|
+
...paths,
|
|
129
|
+
meta,
|
|
130
|
+
};
|
|
131
|
+
};
|
|
132
|
+
/**
|
|
133
|
+
* Find .gitnexus by walking up from a starting path
|
|
134
|
+
*/
|
|
135
|
+
export const findRepo = async (startPath) => {
|
|
136
|
+
let current = path.resolve(startPath);
|
|
137
|
+
const root = path.parse(current).root;
|
|
138
|
+
while (current !== root) {
|
|
139
|
+
const repo = await loadRepo(current);
|
|
140
|
+
if (repo)
|
|
141
|
+
return repo;
|
|
142
|
+
current = path.dirname(current);
|
|
143
|
+
}
|
|
144
|
+
return null;
|
|
145
|
+
};
|
|
146
|
+
/**
|
|
147
|
+
* Add .gitnexus to .gitignore if not already present
|
|
148
|
+
*/
|
|
149
|
+
export const addToGitignore = async (repoPath) => {
|
|
150
|
+
const gitignorePath = path.join(repoPath, '.gitignore');
|
|
151
|
+
try {
|
|
152
|
+
const content = await fs.readFile(gitignorePath, 'utf-8');
|
|
153
|
+
if (content.includes(GITNEXUS_DIR))
|
|
154
|
+
return;
|
|
155
|
+
const newContent = content.endsWith('\n')
|
|
156
|
+
? `${content}${GITNEXUS_DIR}\n`
|
|
157
|
+
: `${content}\n${GITNEXUS_DIR}\n`;
|
|
158
|
+
await fs.writeFile(gitignorePath, newContent, 'utf-8');
|
|
159
|
+
}
|
|
160
|
+
catch {
|
|
161
|
+
// .gitignore doesn't exist, create it
|
|
162
|
+
await fs.writeFile(gitignorePath, `${GITNEXUS_DIR}\n`, 'utf-8');
|
|
163
|
+
}
|
|
164
|
+
};
|
|
165
|
+
// ─── Global Registry (~/.gitnexus/registry.json) ───────────────────────
|
|
166
|
+
/**
|
|
167
|
+
* Get the path to the global GitNexus directory
|
|
168
|
+
*/
|
|
169
|
+
export const getGlobalDir = () => {
|
|
170
|
+
return path.join(os.homedir(), '.gitnexus');
|
|
171
|
+
};
|
|
172
|
+
/**
|
|
173
|
+
* Get the path to the global registry file
|
|
174
|
+
*/
|
|
175
|
+
export const getGlobalRegistryPath = () => {
|
|
176
|
+
return path.join(getGlobalDir(), 'registry.json');
|
|
177
|
+
};
|
|
178
|
+
/**
|
|
179
|
+
* Read the global registry. Returns empty array if not found.
|
|
180
|
+
*/
|
|
181
|
+
export const readRegistry = async () => {
|
|
182
|
+
try {
|
|
183
|
+
const raw = await fs.readFile(getGlobalRegistryPath(), 'utf-8');
|
|
184
|
+
const data = JSON.parse(raw);
|
|
185
|
+
return Array.isArray(data) ? data : [];
|
|
186
|
+
}
|
|
187
|
+
catch {
|
|
188
|
+
return [];
|
|
189
|
+
}
|
|
190
|
+
};
|
|
191
|
+
/**
|
|
192
|
+
* Write the global registry to disk
|
|
193
|
+
*/
|
|
194
|
+
const writeRegistry = async (entries) => {
|
|
195
|
+
const dir = getGlobalDir();
|
|
196
|
+
await fs.mkdir(dir, { recursive: true });
|
|
197
|
+
await fs.writeFile(getGlobalRegistryPath(), JSON.stringify(entries, null, 2), 'utf-8');
|
|
198
|
+
};
|
|
199
|
+
/**
|
|
200
|
+
* Register (add or update) a repo in the global registry.
|
|
201
|
+
* Called after `gitnexus analyze` completes.
|
|
202
|
+
*/
|
|
203
|
+
export const registerRepo = async (repoPath, meta) => {
|
|
204
|
+
const resolved = path.resolve(repoPath);
|
|
205
|
+
const name = path.basename(resolved);
|
|
206
|
+
const { storagePath } = getStoragePaths(resolved);
|
|
207
|
+
const entries = await readRegistry();
|
|
208
|
+
const existing = entries.findIndex((e) => {
|
|
209
|
+
const a = path.resolve(e.path);
|
|
210
|
+
const b = resolved;
|
|
211
|
+
return process.platform === 'win32'
|
|
212
|
+
? a.toLowerCase() === b.toLowerCase()
|
|
213
|
+
: a === b;
|
|
214
|
+
});
|
|
215
|
+
const entry = {
|
|
216
|
+
name,
|
|
217
|
+
path: resolved,
|
|
218
|
+
storagePath,
|
|
219
|
+
indexedAt: meta.indexedAt,
|
|
220
|
+
lastCommit: meta.lastCommit,
|
|
221
|
+
stats: meta.stats,
|
|
222
|
+
};
|
|
223
|
+
if (existing >= 0) {
|
|
224
|
+
entries[existing] = entry;
|
|
225
|
+
}
|
|
226
|
+
else {
|
|
227
|
+
entries.push(entry);
|
|
228
|
+
}
|
|
229
|
+
await writeRegistry(entries);
|
|
230
|
+
};
|
|
231
|
+
/**
|
|
232
|
+
* Remove a repo from the global registry.
|
|
233
|
+
* Called after `gitnexus clean`.
|
|
234
|
+
*/
|
|
235
|
+
export const unregisterRepo = async (repoPath) => {
|
|
236
|
+
const resolved = path.resolve(repoPath);
|
|
237
|
+
const entries = await readRegistry();
|
|
238
|
+
const filtered = entries.filter((e) => path.resolve(e.path) !== resolved);
|
|
239
|
+
await writeRegistry(filtered);
|
|
240
|
+
};
|
|
241
|
+
/**
|
|
242
|
+
* List all registered repos from the global registry.
|
|
243
|
+
* Optionally validates that each entry's .gitnexus/ still exists.
|
|
244
|
+
*/
|
|
245
|
+
export const listRegisteredRepos = async (opts) => {
|
|
246
|
+
const entries = await readRegistry();
|
|
247
|
+
if (!opts?.validate)
|
|
248
|
+
return entries;
|
|
249
|
+
// Validate each entry still has a .gitnexus/ directory
|
|
250
|
+
const valid = [];
|
|
251
|
+
for (const entry of entries) {
|
|
252
|
+
try {
|
|
253
|
+
await fs.access(path.join(entry.storagePath, 'meta.json'));
|
|
254
|
+
valid.push(entry);
|
|
255
|
+
}
|
|
256
|
+
catch {
|
|
257
|
+
// Index no longer exists — skip
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
// If we pruned any entries, save the cleaned registry
|
|
261
|
+
if (valid.length !== entries.length) {
|
|
262
|
+
await writeRegistry(valid);
|
|
263
|
+
}
|
|
264
|
+
return valid;
|
|
265
|
+
};
|
|
266
|
+
/**
|
|
267
|
+
* Get the path to the global CLI config file
|
|
268
|
+
*/
|
|
269
|
+
export const getGlobalConfigPath = () => {
|
|
270
|
+
return path.join(getGlobalDir(), 'config.json');
|
|
271
|
+
};
|
|
272
|
+
/**
|
|
273
|
+
* Load CLI config from ~/.gitnexus/config.json
|
|
274
|
+
*/
|
|
275
|
+
export const loadCLIConfig = async () => {
|
|
276
|
+
try {
|
|
277
|
+
const raw = await fs.readFile(getGlobalConfigPath(), 'utf-8');
|
|
278
|
+
return JSON.parse(raw);
|
|
279
|
+
}
|
|
280
|
+
catch {
|
|
281
|
+
return {};
|
|
282
|
+
}
|
|
283
|
+
};
|
|
284
|
+
/**
|
|
285
|
+
* Save CLI config to ~/.gitnexus/config.json
|
|
286
|
+
*/
|
|
287
|
+
export const saveCLIConfig = async (config) => {
|
|
288
|
+
const dir = getGlobalDir();
|
|
289
|
+
await fs.mkdir(dir, { recursive: true });
|
|
290
|
+
const configPath = getGlobalConfigPath();
|
|
291
|
+
await fs.writeFile(configPath, JSON.stringify(config, null, 2), 'utf-8');
|
|
292
|
+
// Restrict file permissions on Unix (config may contain API keys)
|
|
293
|
+
if (process.platform !== 'win32') {
|
|
294
|
+
try {
|
|
295
|
+
await fs.chmod(configPath, 0o600);
|
|
296
|
+
}
|
|
297
|
+
catch { /* best-effort */ }
|
|
298
|
+
}
|
|
299
|
+
};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { GraphNode, GraphRelationship, KnowledgeGraph } from '../core/graph/types.js';
|
|
2
|
+
import { CommunityDetectionResult } from '../core/ingestion/community-processor.js';
|
|
3
|
+
import { ProcessDetectionResult } from '../core/ingestion/process-processor.js';
|
|
4
|
+
export type PipelinePhase = 'idle' | 'extracting' | 'structure' | 'parsing' | 'imports' | 'calls' | 'heritage' | 'communities' | 'processes' | 'enriching' | 'complete' | 'error';
|
|
5
|
+
export interface PipelineProgress {
|
|
6
|
+
phase: PipelinePhase;
|
|
7
|
+
percent: number;
|
|
8
|
+
message: string;
|
|
9
|
+
detail?: string;
|
|
10
|
+
stats?: {
|
|
11
|
+
filesProcessed: number;
|
|
12
|
+
totalFiles: number;
|
|
13
|
+
nodesCreated: number;
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
export interface PipelineResult {
|
|
17
|
+
graph: KnowledgeGraph;
|
|
18
|
+
/** Absolute path to the repo root — used for lazy file reads during LadybugDB loading */
|
|
19
|
+
repoPath: string;
|
|
20
|
+
/** Total files scanned (for stats) */
|
|
21
|
+
totalFileCount: number;
|
|
22
|
+
communityResult?: CommunityDetectionResult;
|
|
23
|
+
processResult?: ProcessDetectionResult;
|
|
24
|
+
}
|
|
25
|
+
export interface SerializablePipelineResult {
|
|
26
|
+
nodes: GraphNode[];
|
|
27
|
+
relationships: GraphRelationship[];
|
|
28
|
+
repoPath: string;
|
|
29
|
+
totalFileCount: number;
|
|
30
|
+
}
|
|
31
|
+
export declare const serializePipelineResult: (result: PipelineResult) => SerializablePipelineResult;
|
|
32
|
+
export declare const deserializePipelineResult: (serialized: SerializablePipelineResult, createGraph: () => KnowledgeGraph) => PipelineResult;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
// Helper to convert PipelineResult to serializable format
|
|
2
|
+
export const serializePipelineResult = (result) => ({
|
|
3
|
+
nodes: [...result.graph.iterNodes()],
|
|
4
|
+
relationships: [...result.graph.iterRelationships()],
|
|
5
|
+
repoPath: result.repoPath,
|
|
6
|
+
totalFileCount: result.totalFileCount,
|
|
7
|
+
});
|
|
8
|
+
// Helper to reconstruct from serializable format (used in main thread)
|
|
9
|
+
export const deserializePipelineResult = (serialized, createGraph) => {
|
|
10
|
+
const graph = createGraph();
|
|
11
|
+
serialized.nodes.forEach(node => graph.addNode(node));
|
|
12
|
+
serialized.relationships.forEach(rel => graph.addRelationship(rel));
|
|
13
|
+
return {
|
|
14
|
+
graph,
|
|
15
|
+
repoPath: serialized.repoPath,
|
|
16
|
+
totalFileCount: serialized.totalFileCount,
|
|
17
|
+
};
|
|
18
|
+
};
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { ExpandBlueprintChainResult, FindNativeBlueprintReferencesResult, NativeFunctionTarget, SyncUnrealAssetManifestResult, UnrealBlueprintCandidate, UnrealConfig } from './types.js';
|
|
2
|
+
export declare function syncUnrealAssetManifest(storagePath: string, config: UnrealConfig): Promise<SyncUnrealAssetManifestResult>;
|
|
3
|
+
export declare function findNativeBlueprintReferences(storagePath: string, config: UnrealConfig, target: NativeFunctionTarget, candidateAssets: UnrealBlueprintCandidate[], manifestPath?: string): Promise<FindNativeBlueprintReferencesResult>;
|
|
4
|
+
export declare function expandBlueprintChain(storagePath: string, config: UnrealConfig, assetPath: string, chainAnchorId: string, direction: 'upstream' | 'downstream', maxDepth: number): Promise<ExpandBlueprintChainResult>;
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import fs from 'fs/promises';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { execFile } from 'node:child_process';
|
|
4
|
+
import { promisify } from 'node:util';
|
|
5
|
+
import { randomUUID } from 'node:crypto';
|
|
6
|
+
import { ensureUnrealStorage, saveUnrealAssetManifest } from './config.js';
|
|
7
|
+
const execFileAsync = promisify(execFile);
|
|
8
|
+
function buildBaseArgs(config, operation, outputPath) {
|
|
9
|
+
return [
|
|
10
|
+
config.project_path,
|
|
11
|
+
`-run=${config.commandlet || 'GitNexusBlueprintAnalyzer'}`,
|
|
12
|
+
`-Operation=${operation}`,
|
|
13
|
+
`-OutputJson=${outputPath}`,
|
|
14
|
+
'-unattended',
|
|
15
|
+
'-nop4',
|
|
16
|
+
'-nosplash',
|
|
17
|
+
'-nullrhi',
|
|
18
|
+
...(config.extra_args || []),
|
|
19
|
+
];
|
|
20
|
+
}
|
|
21
|
+
function requestPaths(paths) {
|
|
22
|
+
const requestId = randomUUID();
|
|
23
|
+
return {
|
|
24
|
+
requestPath: path.join(paths.requests_dir, `${requestId}.json`),
|
|
25
|
+
outputPath: path.join(paths.outputs_dir, `${requestId}.json`),
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
async function runCommand(config, operation, args) {
|
|
29
|
+
return execFileAsync(config.editor_cmd, args, {
|
|
30
|
+
cwd: config.working_directory,
|
|
31
|
+
timeout: config.timeout_ms || 300000,
|
|
32
|
+
windowsHide: true,
|
|
33
|
+
maxBuffer: 10 * 1024 * 1024,
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
async function readOutputJson(outputPath, stdout) {
|
|
37
|
+
try {
|
|
38
|
+
const raw = await fs.readFile(outputPath, 'utf-8');
|
|
39
|
+
return JSON.parse(raw);
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
return JSON.parse(stdout);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
export async function syncUnrealAssetManifest(storagePath, config) {
|
|
46
|
+
const unrealPaths = await ensureUnrealStorage(storagePath);
|
|
47
|
+
const { outputPath } = requestPaths(unrealPaths);
|
|
48
|
+
const args = buildBaseArgs(config, 'SyncAssets', outputPath);
|
|
49
|
+
try {
|
|
50
|
+
const { stdout } = await runCommand(config, 'SyncAssets', args);
|
|
51
|
+
const response = await readOutputJson(outputPath, stdout);
|
|
52
|
+
const manifestPath = await saveUnrealAssetManifest(storagePath, response.manifest);
|
|
53
|
+
return {
|
|
54
|
+
status: 'success',
|
|
55
|
+
manifest_path: manifestPath,
|
|
56
|
+
asset_count: response.manifest.assets.length,
|
|
57
|
+
generated_at: response.manifest.generated_at,
|
|
58
|
+
warnings: response.warnings || [],
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
catch (error) {
|
|
62
|
+
return {
|
|
63
|
+
status: 'error',
|
|
64
|
+
error: error instanceof Error ? error.message : String(error),
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
export async function findNativeBlueprintReferences(storagePath, config, target, candidateAssets, manifestPath) {
|
|
69
|
+
const unrealPaths = await ensureUnrealStorage(storagePath);
|
|
70
|
+
const { requestPath, outputPath } = requestPaths(unrealPaths);
|
|
71
|
+
await fs.writeFile(requestPath, JSON.stringify({ candidate_assets: candidateAssets }, null, 2), 'utf-8');
|
|
72
|
+
const args = [
|
|
73
|
+
...buildBaseArgs(config, 'FindNativeBlueprintReferences', outputPath),
|
|
74
|
+
`-TargetSymbolKey=${target.symbol_key}`,
|
|
75
|
+
`-TargetFunction=${target.symbol_name}`,
|
|
76
|
+
...(target.class_name ? [`-TargetClass=${target.class_name}`] : []),
|
|
77
|
+
`-CandidatesJson=${requestPath}`,
|
|
78
|
+
];
|
|
79
|
+
const { stdout } = await runCommand(config, 'FindNativeBlueprintReferences', args);
|
|
80
|
+
const response = await readOutputJson(outputPath, stdout);
|
|
81
|
+
return {
|
|
82
|
+
target_function: {
|
|
83
|
+
...target,
|
|
84
|
+
...(response.target_function || {}),
|
|
85
|
+
},
|
|
86
|
+
candidates_scanned: response.candidates_scanned ?? candidateAssets.length,
|
|
87
|
+
candidate_assets: candidateAssets,
|
|
88
|
+
confirmed_references: response.confirmed_references || [],
|
|
89
|
+
manifest_path: manifestPath,
|
|
90
|
+
warnings: response.warnings || [],
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
export async function expandBlueprintChain(storagePath, config, assetPath, chainAnchorId, direction, maxDepth) {
|
|
94
|
+
const unrealPaths = await ensureUnrealStorage(storagePath);
|
|
95
|
+
const { outputPath } = requestPaths(unrealPaths);
|
|
96
|
+
const args = [
|
|
97
|
+
...buildBaseArgs(config, 'ExpandBlueprintChain', outputPath),
|
|
98
|
+
`-AssetPath=${assetPath}`,
|
|
99
|
+
`-ChainAnchorId=${chainAnchorId}`,
|
|
100
|
+
`-Direction=${direction}`,
|
|
101
|
+
`-MaxDepth=${maxDepth}`,
|
|
102
|
+
];
|
|
103
|
+
const { stdout } = await runCommand(config, 'ExpandBlueprintChain', args);
|
|
104
|
+
const response = await readOutputJson(outputPath, stdout);
|
|
105
|
+
return {
|
|
106
|
+
asset_path: assetPath,
|
|
107
|
+
chain_anchor_id: chainAnchorId,
|
|
108
|
+
direction,
|
|
109
|
+
max_depth: maxDepth,
|
|
110
|
+
nodes: response.nodes || [],
|
|
111
|
+
warnings: response.warnings || [],
|
|
112
|
+
};
|
|
113
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { UnrealAssetManifest, UnrealConfig, UnrealStoragePaths } from './types.js';
|
|
2
|
+
export declare function getUnrealStoragePaths(storagePath: string): UnrealStoragePaths;
|
|
3
|
+
export declare function ensureUnrealStorage(storagePath: string): Promise<UnrealStoragePaths>;
|
|
4
|
+
export declare function loadUnrealConfig(storagePath: string): Promise<UnrealConfig | null>;
|
|
5
|
+
export declare function loadUnrealAssetManifest(storagePath: string): Promise<UnrealAssetManifest | null>;
|
|
6
|
+
export declare function saveUnrealAssetManifest(storagePath: string, manifest: UnrealAssetManifest): Promise<string>;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import fs from 'fs/promises';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
export function getUnrealStoragePaths(storagePath) {
|
|
4
|
+
const rootDir = path.join(storagePath, 'unreal');
|
|
5
|
+
return {
|
|
6
|
+
root_dir: rootDir,
|
|
7
|
+
config_path: path.join(rootDir, 'config.json'),
|
|
8
|
+
manifest_path: path.join(rootDir, 'asset-manifest.json'),
|
|
9
|
+
requests_dir: path.join(rootDir, 'requests'),
|
|
10
|
+
outputs_dir: path.join(rootDir, 'outputs'),
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
export async function ensureUnrealStorage(storagePath) {
|
|
14
|
+
const paths = getUnrealStoragePaths(storagePath);
|
|
15
|
+
await fs.mkdir(paths.requests_dir, { recursive: true });
|
|
16
|
+
await fs.mkdir(paths.outputs_dir, { recursive: true });
|
|
17
|
+
return paths;
|
|
18
|
+
}
|
|
19
|
+
export async function loadUnrealConfig(storagePath) {
|
|
20
|
+
try {
|
|
21
|
+
const paths = getUnrealStoragePaths(storagePath);
|
|
22
|
+
const raw = await fs.readFile(paths.config_path, 'utf-8');
|
|
23
|
+
const parsed = JSON.parse(raw);
|
|
24
|
+
if (!parsed.editor_cmd || !parsed.project_path) {
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
return {
|
|
28
|
+
commandlet: 'GitNexusBlueprintAnalyzer',
|
|
29
|
+
timeout_ms: 300000,
|
|
30
|
+
...parsed,
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
export async function loadUnrealAssetManifest(storagePath) {
|
|
38
|
+
try {
|
|
39
|
+
const paths = getUnrealStoragePaths(storagePath);
|
|
40
|
+
const raw = await fs.readFile(paths.manifest_path, 'utf-8');
|
|
41
|
+
const parsed = JSON.parse(raw);
|
|
42
|
+
if (!Array.isArray(parsed.assets)) {
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
return parsed;
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
export async function saveUnrealAssetManifest(storagePath, manifest) {
|
|
52
|
+
const paths = await ensureUnrealStorage(storagePath);
|
|
53
|
+
await fs.writeFile(paths.manifest_path, JSON.stringify(manifest, null, 2), 'utf-8');
|
|
54
|
+
return paths.manifest_path;
|
|
55
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
export interface UnrealAssetManifestAsset {
|
|
2
|
+
asset_path: string;
|
|
3
|
+
generated_class?: string;
|
|
4
|
+
parent_class?: string;
|
|
5
|
+
native_parents?: string[];
|
|
6
|
+
native_function_refs?: string[];
|
|
7
|
+
dependencies?: string[];
|
|
8
|
+
}
|
|
9
|
+
export interface UnrealAssetManifest {
|
|
10
|
+
version: number;
|
|
11
|
+
generated_at: string;
|
|
12
|
+
project_path?: string;
|
|
13
|
+
assets: UnrealAssetManifestAsset[];
|
|
14
|
+
}
|
|
15
|
+
export interface UnrealConfig {
|
|
16
|
+
editor_cmd: string;
|
|
17
|
+
project_path: string;
|
|
18
|
+
commandlet?: string;
|
|
19
|
+
timeout_ms?: number;
|
|
20
|
+
working_directory?: string;
|
|
21
|
+
extra_args?: string[];
|
|
22
|
+
}
|
|
23
|
+
export interface UnrealStoragePaths {
|
|
24
|
+
root_dir: string;
|
|
25
|
+
config_path: string;
|
|
26
|
+
manifest_path: string;
|
|
27
|
+
requests_dir: string;
|
|
28
|
+
outputs_dir: string;
|
|
29
|
+
}
|
|
30
|
+
export interface NativeFunctionTarget {
|
|
31
|
+
symbol_id: string;
|
|
32
|
+
symbol_name: string;
|
|
33
|
+
symbol_type: string;
|
|
34
|
+
symbol_key: string;
|
|
35
|
+
qualified_name: string;
|
|
36
|
+
class_name?: string;
|
|
37
|
+
file_path?: string;
|
|
38
|
+
start_line?: number;
|
|
39
|
+
}
|
|
40
|
+
export interface UnrealBlueprintCandidate {
|
|
41
|
+
asset_path: string;
|
|
42
|
+
generated_class?: string;
|
|
43
|
+
parent_class?: string;
|
|
44
|
+
reason: 'native_parent' | 'native_function_ref' | 'dependency' | 'manifest';
|
|
45
|
+
}
|
|
46
|
+
export interface UnrealConfirmedReference {
|
|
47
|
+
asset_path: string;
|
|
48
|
+
graph_name?: string;
|
|
49
|
+
node_kind: string;
|
|
50
|
+
node_title?: string;
|
|
51
|
+
blueprint_owner_function?: string;
|
|
52
|
+
chain_anchor_id: string;
|
|
53
|
+
source: 'editor_confirmed';
|
|
54
|
+
}
|
|
55
|
+
export interface UnrealChainNode {
|
|
56
|
+
node_id: string;
|
|
57
|
+
graph_name?: string;
|
|
58
|
+
node_kind: string;
|
|
59
|
+
node_title?: string;
|
|
60
|
+
depth: number;
|
|
61
|
+
}
|
|
62
|
+
export interface SyncUnrealAssetManifestResult {
|
|
63
|
+
status: 'success' | 'error';
|
|
64
|
+
manifest_path?: string;
|
|
65
|
+
asset_count?: number;
|
|
66
|
+
generated_at?: string;
|
|
67
|
+
warnings?: string[];
|
|
68
|
+
error?: string;
|
|
69
|
+
}
|
|
70
|
+
export interface FindNativeBlueprintReferencesResult {
|
|
71
|
+
target_function: NativeFunctionTarget;
|
|
72
|
+
candidates_scanned: number;
|
|
73
|
+
candidate_assets: UnrealBlueprintCandidate[];
|
|
74
|
+
confirmed_references: UnrealConfirmedReference[];
|
|
75
|
+
manifest_path?: string;
|
|
76
|
+
manifest_refreshed?: boolean;
|
|
77
|
+
warnings?: string[];
|
|
78
|
+
}
|
|
79
|
+
export interface ExpandBlueprintChainResult {
|
|
80
|
+
asset_path: string;
|
|
81
|
+
chain_anchor_id: string;
|
|
82
|
+
direction: 'upstream' | 'downstream';
|
|
83
|
+
max_depth: number;
|
|
84
|
+
nodes: UnrealChainNode[];
|
|
85
|
+
warnings?: string[];
|
|
86
|
+
}
|
|
87
|
+
export interface DerivedBlueprintResult {
|
|
88
|
+
class_name: string;
|
|
89
|
+
manifest_path: string;
|
|
90
|
+
blueprints: UnrealBlueprintCandidate[];
|
|
91
|
+
}
|
|
92
|
+
export interface UnrealAnalyzerSyncResponse {
|
|
93
|
+
manifest: UnrealAssetManifest;
|
|
94
|
+
warnings?: string[];
|
|
95
|
+
}
|
|
96
|
+
export interface UnrealAnalyzerFindRefsResponse {
|
|
97
|
+
target_function?: Partial<NativeFunctionTarget>;
|
|
98
|
+
candidates_scanned?: number;
|
|
99
|
+
confirmed_references?: UnrealConfirmedReference[];
|
|
100
|
+
warnings?: string[];
|
|
101
|
+
}
|
|
102
|
+
export interface UnrealAnalyzerExpandChainResponse {
|
|
103
|
+
nodes?: UnrealChainNode[];
|
|
104
|
+
warnings?: string[];
|
|
105
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|