Merge pull request #163 from Lum1104/fix/extract-structure-symlink-isCli

fix(skills/understand): extract-structure isCli silently no-ops via symlinked SKILL_DIR
This commit is contained in:
Yuxiang Lin
2026-05-21 19:40:01 +08:00
committed by GitHub
Unverified
7 changed files with 29 additions and 9 deletions
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "understand-anything",
"description": "AI-powered codebase understanding — analyze, visualize, and explain any project",
"version": "2.7.3",
"version": "2.7.4",
"author": {
"name": "Lum1104"
},
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "understand-anything",
"description": "AI-powered codebase understanding — analyze, visualize, and explain any project",
"version": "2.7.3",
"version": "2.7.4",
"author": {
"name": "Lum1104"
},
+1 -1
View File
@@ -2,7 +2,7 @@
"name": "understand-anything",
"displayName": "Understand Anything",
"description": "AI-powered codebase understanding — analyze, visualize, and explain any project",
"version": "2.7.3",
"version": "2.7.4",
"author": {
"name": "Lum1104"
},
@@ -1,7 +1,7 @@
{
"name": "understand-anything",
"description": "AI-powered codebase understanding — analyze, visualize, and explain any project",
"version": "2.7.3",
"version": "2.7.4",
"author": {
"name": "Lum1104"
},
@@ -64,6 +64,8 @@ node <SKILL_DIR>/extract-structure.mjs \
If the script exits non-zero, read stderr and report the error. Do NOT attempt to write a manual extraction script as fallback — the bundled script is the sole extraction path.
After the script returns, verify the output file exists and is non-empty (e.g. `test -s $PROJECT_ROOT/.understand-anything/tmp/ua-file-extract-results-<batchIndex>.json`). Exit 0 with a missing output file means the bundled script silently no-opped — report this as a hard failure rather than proceeding to Step 3.
### Step 3 — Read the extraction results
Read `$PROJECT_ROOT/.understand-anything/tmp/ua-file-extract-results-<batchIndex>.json`. The output format is:
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@understand-anything/skill",
"version": "2.7.3",
"version": "2.7.4",
"type": "module",
"main": "dist/index.js",
"types": "dist/index.d.ts",
@@ -19,7 +19,7 @@
import { createRequire } from 'node:module';
import { dirname, resolve, join } from 'node:path';
import { fileURLToPath, pathToFileURL } from 'node:url';
import { readFileSync, writeFileSync } from 'node:fs';
import { existsSync, readFileSync, realpathSync, writeFileSync } from 'node:fs';
const __dirname = dirname(fileURLToPath(import.meta.url));
// skills/understand/ -> plugin root is two dirs up
@@ -133,6 +133,10 @@ async function main() {
};
writeFileSync(outputPath, JSON.stringify(output, null, 2), 'utf-8');
if (!existsSync(outputPath)) {
throw new Error(`output file missing after write: ${outputPath}`);
}
}
// ---------------------------------------------------------------------------
@@ -302,11 +306,25 @@ export function buildResult(file, totalLines, nonEmptyLines, analysis, callGraph
// ---------------------------------------------------------------------------
// Run only when executed directly as a CLI; importing the module (e.g. from
// tests) must not trigger main().
//
// Canonicalize both sides through realpathSync. Node ESM resolves
// import.meta.url through symlinks but pathToFileURL(process.argv[1]) preserves
// them, so a raw equality check silently no-ops when the script is invoked via
// a symlinked plugin install path (the default in Claude Code / Copilot CLI
// caches). See GitHub issue #162.
// ---------------------------------------------------------------------------
const isCli =
process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href;
function isCliEntry() {
if (!process.argv[1]) return false;
try {
const modulePath = realpathSync(fileURLToPath(import.meta.url));
const argvPath = realpathSync(process.argv[1]);
return modulePath === argvPath;
} catch {
return false;
}
}
if (isCli) {
if (isCliEntry()) {
try {
await main();
} catch (err) {