@duytransipher/gitnexus 1.2.1 → 1.3.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/dist/cli/analyze.js +1 -1
- package/dist/cli/index.js +1 -0
- package/dist/cli/tool.d.ts +1 -0
- package/dist/cli/tool.js +31 -9
- package/dist/cli/unreal-progress.d.ts +12 -0
- package/dist/cli/unreal-progress.js +66 -0
- package/dist/mcp/local/local-backend.js +11 -5
- package/dist/mcp/tools.js +108 -103
- package/dist/unreal/blueprint-ingestion.d.ts +8 -1
- package/dist/unreal/blueprint-ingestion.js +35 -3
- package/dist/unreal/bridge.d.ts +1 -1
- package/dist/unreal/bridge.js +94 -2
- package/dist/unreal/types.d.ts +4 -0
- package/package.json +1 -1
- package/vendor/GitNexusUnreal/Source/GitNexusUnreal/Private/GitNexusBlueprintAnalyzerCommandlet.cpp +482 -17
- package/vendor/GitNexusUnreal/Source/GitNexusUnreal/Public/GitNexusBlueprintAnalyzerCommandlet.h +25 -3
package/dist/unreal/bridge.js
CHANGED
|
@@ -4,6 +4,7 @@ import { execFile } from 'node:child_process';
|
|
|
4
4
|
import { promisify } from 'node:util';
|
|
5
5
|
import { randomUUID } from 'node:crypto';
|
|
6
6
|
import { ensureUnrealStorage, saveUnrealAssetManifest } from './config.js';
|
|
7
|
+
import { loadIgnoreRules } from '../config/ignore-service.js';
|
|
7
8
|
const execFileAsync = promisify(execFile);
|
|
8
9
|
function buildBaseArgs(config, operation, outputPath) {
|
|
9
10
|
return [
|
|
@@ -40,7 +41,17 @@ async function readUELogErrors(config) {
|
|
|
40
41
|
const logPath = path.join(projectDir, 'Saved', 'Logs', `${projectName}.log`);
|
|
41
42
|
const content = await fs.readFile(logPath, 'utf-8');
|
|
42
43
|
const lines = content.split(/\r?\n/);
|
|
43
|
-
const
|
|
44
|
+
const stripped = (l) => l.replace(/^\[.*?\]\[\s*\d+\]/, '');
|
|
45
|
+
// Skip callstack lines, driver errors, and empty error lines
|
|
46
|
+
const isNoise = (l) => {
|
|
47
|
+
const s = stripped(l);
|
|
48
|
+
return /^LogWindows.*Failed to get driver/i.test(s)
|
|
49
|
+
|| /\[Callstack\]/i.test(s)
|
|
50
|
+
|| /^LogWindows: Error:\s*$/i.test(s)
|
|
51
|
+
|| /^LogWindows: Error: ===/.test(s)
|
|
52
|
+
|| /^LogWindows: Error: Fatal error!/i.test(s);
|
|
53
|
+
};
|
|
54
|
+
const errorLines = lines.filter(l => /\bError\b/i.test(l) && !isNoise(l));
|
|
44
55
|
if (errorLines.length === 0)
|
|
45
56
|
return '';
|
|
46
57
|
return 'UE Log errors:\n' + errorLines.slice(-10).join('\n');
|
|
@@ -58,10 +69,74 @@ async function readOutputJson(outputPath, stdout) {
|
|
|
58
69
|
return JSON.parse(stdout);
|
|
59
70
|
}
|
|
60
71
|
}
|
|
61
|
-
|
|
72
|
+
/**
|
|
73
|
+
* Build include/exclude prefix filters for the Unreal commandlet.
|
|
74
|
+
* Merges config `include_paths`/`exclude_paths` with .gitnexusignore patterns,
|
|
75
|
+
* mapping filesystem patterns to Unreal asset path prefixes.
|
|
76
|
+
*/
|
|
77
|
+
async function buildFilterPrefixes(repoPath, config) {
|
|
78
|
+
const include_prefixes = [];
|
|
79
|
+
const exclude_prefixes = [];
|
|
80
|
+
// Add explicit include_paths (whitelist) from unreal config
|
|
81
|
+
if (config.include_paths && Array.isArray(config.include_paths)) {
|
|
82
|
+
for (const p of config.include_paths) {
|
|
83
|
+
include_prefixes.push(p);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
// Add explicit exclude_paths from unreal config
|
|
87
|
+
if (config.exclude_paths && Array.isArray(config.exclude_paths)) {
|
|
88
|
+
for (const p of config.exclude_paths) {
|
|
89
|
+
exclude_prefixes.push(p);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
// Derive exclude prefixes from .gitnexusignore patterns
|
|
93
|
+
if (repoPath) {
|
|
94
|
+
const ig = await loadIgnoreRules(repoPath);
|
|
95
|
+
if (ig) {
|
|
96
|
+
const projectDir = path.dirname(config.project_path);
|
|
97
|
+
try {
|
|
98
|
+
const contentDir = path.join(projectDir, 'Content');
|
|
99
|
+
const entries = await fs.readdir(contentDir, { withFileTypes: true });
|
|
100
|
+
for (const entry of entries) {
|
|
101
|
+
if (entry.isDirectory()) {
|
|
102
|
+
const relPath = `Content/${entry.name}`;
|
|
103
|
+
if (ig.ignores(relPath) || ig.ignores(relPath + '/')) {
|
|
104
|
+
exclude_prefixes.push(`/Game/${entry.name}`);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
catch { /* Content dir might not exist */ }
|
|
110
|
+
try {
|
|
111
|
+
const pluginsDir = path.join(projectDir, 'Plugins');
|
|
112
|
+
const entries = await fs.readdir(pluginsDir, { withFileTypes: true });
|
|
113
|
+
for (const entry of entries) {
|
|
114
|
+
if (entry.isDirectory()) {
|
|
115
|
+
const relPath = `Plugins/${entry.name}`;
|
|
116
|
+
if (ig.ignores(relPath) || ig.ignores(relPath + '/')) {
|
|
117
|
+
exclude_prefixes.push(`/${entry.name}`);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
catch { /* Plugins dir might not exist */ }
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
return { include_prefixes, exclude_prefixes };
|
|
126
|
+
}
|
|
127
|
+
export async function syncUnrealAssetManifest(storagePath, config, repoPath, deep) {
|
|
62
128
|
const unrealPaths = await ensureUnrealStorage(storagePath);
|
|
63
129
|
const { outputPath } = requestPaths(unrealPaths);
|
|
64
130
|
const args = buildBaseArgs(config, 'SyncAssets', outputPath);
|
|
131
|
+
// Pass scan mode: metadata (default, zero loading) or deep (full Blueprint loading)
|
|
132
|
+
args.push(`-Mode=${deep ? 'deep' : 'metadata'}`);
|
|
133
|
+
// Build and pass filter prefixes (include + exclude)
|
|
134
|
+
const filters = await buildFilterPrefixes(repoPath, config);
|
|
135
|
+
if (filters.include_prefixes.length > 0 || filters.exclude_prefixes.length > 0) {
|
|
136
|
+
const filterJsonPath = path.join(unrealPaths.requests_dir, `filter-${randomUUID()}.json`);
|
|
137
|
+
await fs.writeFile(filterJsonPath, JSON.stringify(filters), 'utf-8');
|
|
138
|
+
args.push(`-FilterJson=${filterJsonPath}`);
|
|
139
|
+
}
|
|
65
140
|
try {
|
|
66
141
|
const { stdout } = await runCommand(config, 'SyncAssets', args);
|
|
67
142
|
const manifest = await readOutputJson(outputPath, stdout);
|
|
@@ -75,6 +150,23 @@ export async function syncUnrealAssetManifest(storagePath, config) {
|
|
|
75
150
|
};
|
|
76
151
|
}
|
|
77
152
|
catch (error) {
|
|
153
|
+
// UE may exit non-zero due to Blueprint compilation warnings even though
|
|
154
|
+
// the commandlet completed and wrote valid output. Try reading the file first.
|
|
155
|
+
try {
|
|
156
|
+
const stdout = error?.stdout ? String(error.stdout).trim() : '';
|
|
157
|
+
const manifest = await readOutputJson(outputPath, stdout);
|
|
158
|
+
if (manifest && Array.isArray(manifest.assets) && manifest.assets.length > 0) {
|
|
159
|
+
const manifestPath = await saveUnrealAssetManifest(storagePath, manifest);
|
|
160
|
+
return {
|
|
161
|
+
status: 'success',
|
|
162
|
+
manifest_path: manifestPath,
|
|
163
|
+
asset_count: manifest.assets.length,
|
|
164
|
+
generated_at: manifest.generated_at,
|
|
165
|
+
warnings: ['UE exited with non-zero code (likely Blueprint compilation warnings)'],
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
catch { /* output file not readable, fall through to error */ }
|
|
78
170
|
const stderr = error?.stderr ? String(error.stderr).trim() : '';
|
|
79
171
|
const stdout = error?.stdout ? String(error.stdout).trim() : '';
|
|
80
172
|
const msg = error instanceof Error ? error.message : String(error);
|
package/dist/unreal/types.d.ts
CHANGED
|
@@ -19,6 +19,10 @@ export interface UnrealConfig {
|
|
|
19
19
|
timeout_ms?: number;
|
|
20
20
|
working_directory?: string;
|
|
21
21
|
extra_args?: string[];
|
|
22
|
+
/** Unreal package path prefixes to exclude from sync (e.g., "/Game/ThirdParty", "/SomePlugin") */
|
|
23
|
+
exclude_paths?: string[];
|
|
24
|
+
/** Unreal package path prefixes to include (whitelist). If set, ONLY assets under these prefixes are scanned. */
|
|
25
|
+
include_paths?: string[];
|
|
22
26
|
}
|
|
23
27
|
export interface UnrealStoragePaths {
|
|
24
28
|
root_dir: string;
|
package/package.json
CHANGED