mirror of
https://github.com/Egonex-AI/Understand-Anything.git
synced 2026-06-22 10:58:03 +08:00
fix(skills/understand): bundle build-fingerprints.mjs and reorder Phase 7
The Phase 7 step 2.5 code example in SKILL.md called buildFingerprintStore() with 2 arguments, but the real signature requires 4 (projectDir, filePaths, registry: PluginRegistry, gitCommitHash: string). It also omitted the required `await TreeSitterPlugin.init()`. Any LLM following the example threw TypeError on `registry.analyzeFile()` and never produced a baseline — which is why fingerprints.json never existed in a usable form after a fresh /understand, and is the root cause behind issue #152's "every auto-update escalates to FULL_UPDATE" cascade. Replace the LLM-written script with a bundled `build-fingerprints.mjs` that mirrors `extract-structure.mjs`: resolves @understand-anything/core via createRequire, initializes TreeSitterPlugin + PluginRegistry correctly, calls buildFingerprintStore with all four arguments, and persists via saveFingerprints. Smoke-tested on this repo (3 files, correct functions/classes/imports extracted). Reorder Phase 7 so fingerprints are written BEFORE meta.json. If fingerprint generation fails, the new step explicitly says to abort Phase 7 — meta.json must not advance without a valid baseline, or the next auto-update sees a fresh commit hash with no fingerprints and classifies every file as STRUCTURAL. Affects every install since 2.7.0 (when the broken example was introduced). Users running /understand --full on 2.7.3+ will get a usable fingerprints.json on the first try.
This commit is contained in:
@@ -682,7 +682,30 @@ Pass these parameters in the dispatch prompt:
|
||||
|
||||
1. Write the final knowledge graph to `$PROJECT_ROOT/.understand-anything/knowledge-graph.json`.
|
||||
|
||||
2. Write metadata to `$PROJECT_ROOT/.understand-anything/meta.json`:
|
||||
2. **Generate structural fingerprints baseline.** This creates the basis for future automatic incremental updates and **must succeed before `meta.json` is written** — otherwise auto-update sees a fresh commit hash with no fingerprints to compare against, classifies every file as STRUCTURAL, and escalates to `FULL_UPDATE` on every subsequent commit (issue #152).
|
||||
|
||||
Write the input file:
|
||||
```bash
|
||||
cat > $PROJECT_ROOT/.understand-anything/intermediate/fingerprint-input.json <<EOF
|
||||
{
|
||||
"projectRoot": "$PROJECT_ROOT",
|
||||
"sourceFilePaths": [<all source file paths from Phase 1, as JSON array>],
|
||||
"gitCommitHash": "<current commit hash>"
|
||||
}
|
||||
EOF
|
||||
```
|
||||
|
||||
Then invoke the bundled script (located next to this SKILL.md):
|
||||
```bash
|
||||
node <SKILL_DIR>/build-fingerprints.mjs \
|
||||
$PROJECT_ROOT/.understand-anything/intermediate/fingerprint-input.json
|
||||
```
|
||||
|
||||
The script uses `TreeSitterPlugin + PluginRegistry` exactly like `extract-structure.mjs`, so the baseline matches the comparison logic used during auto-updates.
|
||||
|
||||
**If the script exits non-zero or stdout does not include `Fingerprints baseline:`, abort Phase 7 and report the error. Do NOT proceed to step 3 (writing `meta.json`).**
|
||||
|
||||
3. Write metadata to `$PROJECT_ROOT/.understand-anything/meta.json` (only after step 2 succeeded):
|
||||
```json
|
||||
{
|
||||
"lastAnalyzedAt": "<ISO 8601 timestamp>",
|
||||
@@ -692,25 +715,13 @@ Pass these parameters in the dispatch prompt:
|
||||
}
|
||||
```
|
||||
|
||||
2.5. **Generate structural fingerprints** for all analyzed files and save to `$PROJECT_ROOT/.understand-anything/fingerprints.json`. This creates the baseline for future automatic incremental updates.
|
||||
|
||||
Write and execute a Node.js script that uses the core fingerprint module (tree-sitter-based, not regex):
|
||||
```javascript
|
||||
import { buildFingerprintStore } from '@understand-anything/core';
|
||||
import { saveFingerprints } from '@understand-anything/core';
|
||||
|
||||
const store = await buildFingerprintStore('<PROJECT_ROOT>', sourceFilePaths);
|
||||
saveFingerprints('<PROJECT_ROOT>', store);
|
||||
```
|
||||
Where `sourceFilePaths` is the list of all analyzed source file paths from Phase 1. This uses the same tree-sitter analysis pipeline as the main fingerprint engine, ensuring the baseline matches the comparison logic used during auto-updates.
|
||||
|
||||
3. Clean up intermediate files:
|
||||
4. Clean up intermediate files:
|
||||
```bash
|
||||
rm -rf $PROJECT_ROOT/.understand-anything/intermediate
|
||||
rm -rf $PROJECT_ROOT/.understand-anything/tmp
|
||||
```
|
||||
|
||||
4. Report a summary to the user containing:
|
||||
5. Report a summary to the user containing:
|
||||
- Project name and description
|
||||
- Files analyzed / total files (with breakdown by fileCategory: code, config, docs, infra, data, script, markup)
|
||||
- Nodes created (broken down by type: file, function, class, config, document, service, table, endpoint, pipeline, schema, resource)
|
||||
@@ -720,7 +731,7 @@ Pass these parameters in the dispatch prompt:
|
||||
- Any warnings from the reviewer
|
||||
- Path to the output file: `$PROJECT_ROOT/.understand-anything/knowledge-graph.json`
|
||||
|
||||
5. Only automatically launch the dashboard by invoking the `/understand-dashboard` skill if final graph validation passed after normalization/review fixes.
|
||||
6. Only automatically launch the dashboard by invoking the `/understand-dashboard` skill if final graph validation passed after normalization/review fixes.
|
||||
If final validation did not pass, report that the graph was saved with warnings and dashboard launch was skipped.
|
||||
|
||||
---
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* build-fingerprints.mjs
|
||||
*
|
||||
* Builds the structural-fingerprint baseline used by auto-update's
|
||||
* incremental change detection. Runs once per /understand full rebuild
|
||||
* (Phase 7 step 2.5), generating .understand-anything/fingerprints.json.
|
||||
*
|
||||
* Replaces the LLM-written fingerprint script that previously sat in
|
||||
* SKILL.md as a code example — that example had the wrong signature
|
||||
* for buildFingerprintStore() and never successfully produced a baseline,
|
||||
* which silently broke auto-update for every install (see issue #152).
|
||||
*
|
||||
* Usage:
|
||||
* node build-fingerprints.mjs <input.json>
|
||||
*
|
||||
* Input JSON:
|
||||
* { projectRoot: string, sourceFilePaths: string[], gitCommitHash: string }
|
||||
*
|
||||
* Writes: <projectRoot>/.understand-anything/fingerprints.json
|
||||
* Exit code: 0 on success (including 0 files analyzed); non-zero on error.
|
||||
*/
|
||||
|
||||
import { createRequire } from 'node:module';
|
||||
import { dirname, resolve } from 'node:path';
|
||||
import { fileURLToPath, pathToFileURL } from 'node:url';
|
||||
import { readFileSync } from 'node:fs';
|
||||
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
// skills/understand/ -> plugin root is two dirs up
|
||||
const pluginRoot = resolve(__dirname, '../..');
|
||||
const require = createRequire(resolve(pluginRoot, 'package.json'));
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Resolve @understand-anything/core (matches extract-structure.mjs).
|
||||
// pathToFileURL() is required for Windows: dynamic import() of a raw
|
||||
// "C:\..." path throws ERR_UNSUPPORTED_ESM_URL_SCHEME.
|
||||
// ---------------------------------------------------------------------------
|
||||
let core;
|
||||
try {
|
||||
core = await import(pathToFileURL(require.resolve('@understand-anything/core')).href);
|
||||
} catch {
|
||||
core = await import(pathToFileURL(resolve(pluginRoot, 'packages/core/dist/index.js')).href);
|
||||
}
|
||||
|
||||
const {
|
||||
TreeSitterPlugin,
|
||||
PluginRegistry,
|
||||
builtinLanguageConfigs,
|
||||
registerAllParsers,
|
||||
buildFingerprintStore,
|
||||
saveFingerprints,
|
||||
} = core;
|
||||
|
||||
async function main() {
|
||||
const [, , inputPath] = process.argv;
|
||||
if (!inputPath) {
|
||||
process.stderr.write('Usage: node build-fingerprints.mjs <input.json>\n');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const { projectRoot, sourceFilePaths, gitCommitHash } = JSON.parse(
|
||||
readFileSync(inputPath, 'utf-8'),
|
||||
);
|
||||
|
||||
if (!projectRoot || !Array.isArray(sourceFilePaths) || typeof gitCommitHash !== 'string') {
|
||||
throw new Error(
|
||||
'Invalid input: requires { projectRoot: string, sourceFilePaths: string[], gitCommitHash: string }',
|
||||
);
|
||||
}
|
||||
|
||||
// Create tree-sitter plugin with all configs that have WASM grammars,
|
||||
// mirroring extract-structure.mjs so the baseline matches the comparison
|
||||
// logic used during auto-updates.
|
||||
const tsConfigs = builtinLanguageConfigs.filter((c) => c.treeSitter);
|
||||
const tsPlugin = new TreeSitterPlugin(tsConfigs);
|
||||
await tsPlugin.init();
|
||||
|
||||
const registry = new PluginRegistry();
|
||||
registry.register(tsPlugin);
|
||||
registerAllParsers(registry);
|
||||
|
||||
const store = buildFingerprintStore(projectRoot, sourceFilePaths, registry, gitCommitHash);
|
||||
saveFingerprints(projectRoot, store);
|
||||
|
||||
const fileCount = Object.keys(store.files).length;
|
||||
process.stdout.write(`Fingerprints baseline: ${fileCount} files\n`);
|
||||
}
|
||||
|
||||
await main();
|
||||
Reference in New Issue
Block a user