understand
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
Chinese/understand
/understand
Analyze the current codebase and produce a file in . This file powers the interactive dashboard for exploring the project's architecture.
knowledge-graph.json.understand-anything/分析当前代码库并在目录下生成文件。该文件为探索项目架构的交互式仪表盘提供数据支持。
.understand-anything/knowledge-graph.jsonOptions
选项
- may contain:
$ARGUMENTS- — Force a full rebuild, ignoring any existing graph
--full - — Enable automatic graph updates on commit (writes
--auto-updatetoautoUpdate: true).understand-anything/config.json - — Disable automatic graph updates (writes
--no-auto-updatetoautoUpdate: false).understand-anything/config.json - — Run full LLM graph-reviewer instead of inline deterministic validation
--review - — Generate all textual content (summaries, descriptions, tags, titles, languageNotes, languageLesson) in the specified language. Accepts ISO 639-1 codes (
--language <lang>,zh,ja,ko,en,es,fr, etc.) or friendly names (de,chinese,japanese,korean,english, etc.). Locale variants supported:spanish,zh-TW, etc. Defaults tozh-HK(English). Stores preference inenfor consistency across incremental updates..understand-anything/config.json - A directory path (e.g. or
/path/to/repo) — Analyze the given directory instead of the current working directory../other-project
- 可包含以下内容:
$ARGUMENTS- — 强制完整重建,忽略现有图谱
--full - — 启用提交时自动更新图谱(将
--auto-update写入autoUpdate: true).understand-anything/config.json - — 禁用自动更新图谱(将
--no-auto-update写入autoUpdate: false).understand-anything/config.json - — 运行完整的LLM图谱评审器,而非内联确定性验证
--review - — 指定语言生成所有文本内容(摘要、描述、标签、标题、languageNotes、languageLesson)。支持ISO 639-1代码(
--language <lang>,zh,ja,ko,en,es,fr等)或友好名称(de,chinese,japanese,korean,english等)。支持区域变体:spanish,zh-TW等。默认为zh-HK(英语)。偏好设置会存储在en中,以确保增量更新的一致性。.understand-anything/config.json - 目录路径(例如或
/path/to/repo) — 分析指定目录而非当前工作目录../other-project
Progress Reporting
进度报告
Throughout execution, report progress to the user at each phase transition and during batch processing. This keeps users informed on large codebases where analysis can take a long time.
-
Phase transitions: At the start of each phase, print a status line:
[Phase N/7] <phase name>...Example:[Phase 2/7] Analyzing files (12 batches)... -
Batch progress: During Phase 2, report each batch with its index and total:(list up to 3 filenames, then
Analyzing batch X/N (files: foo.ts, bar.ts, ...)if more)... -
Phase completion: When a phase finishes, briefly confirm:
Phase N complete. <one-line summary of result>Example:Phase 1 complete. Found 247 files across 3 languages.
在执行过程中,需在每个阶段转换及批处理期间向用户报告进度。这能让用户了解大型代码库的分析进度,避免长时间等待。
-
阶段转换:每个阶段开始时,打印状态行:
[Phase N/7] <phase name>...示例:[Phase 2/7] Analyzing files (12 batches)... -
批处理进度:在第2阶段,报告每个批次的索引和总数:(最多列出3个文件名,若更多则显示
Analyzing batch X/N (files: foo.ts, bar.ts, ...))... -
阶段完成:阶段结束时,简要确认:
Phase N complete. <one-line summary of result>示例:Phase 1 complete. Found 247 files across 3 languages.
Phase 0 — Pre-flight
阶段0 — 预检
Determine whether to run a full analysis or incremental update.
-
Resolve:
PROJECT_ROOT-
Parsefor a non-flag token (any argument that does not start with
$ARGUMENTS). If found, treat it as the target directory path.--- If the path is relative, resolve it against the current working directory.
- Verify the resolved path exists and is a directory (run ). If it does not exist or is not a directory, report an error to the user and STOP.
test -d <path> - Set to the resolved absolute path.
PROJECT_ROOT
-
If no directory path argument is found, setto the current working directory.
PROJECT_ROOT -
Worktree redirect. Ifis inside a git worktree (not the main checkout), redirect output to the main repository root. Worktrees managed by Claude Code are ephemeral —
PROJECT_ROOTwritten there is destroyed when the session ends, taking the knowledge graph with it (issue #133). Detect a worktree by comparing.understand-anything/againstgit rev-parse --git-dir; in a normal checkout or submodule they resolve to the same path, in a worktree they differ and the parent ofgit rev-parse --git-common-diris the main repo root.--git-common-dirbashCOMMON_DIR=$(git -C "$PROJECT_ROOT" rev-parse --git-common-dir 2>/dev/null) GIT_DIR=$(git -C "$PROJECT_ROOT" rev-parse --git-dir 2>/dev/null) if [ -n "$COMMON_DIR" ] && [ -n "$GIT_DIR" ]; then COMMON_ABS=$(cd "$PROJECT_ROOT" && cd "$COMMON_DIR" 2>/dev/null && pwd -P) GIT_ABS=$(cd "$PROJECT_ROOT" && cd "$GIT_DIR" 2>/dev/null && pwd -P) if [ -n "$COMMON_ABS" ] && [ "$COMMON_ABS" != "$GIT_ABS" ]; then MAIN_ROOT=$(dirname "$COMMON_ABS") if [ -d "$MAIN_ROOT" ] && [ "${UNDERSTAND_NO_WORKTREE_REDIRECT:-0}" != "1" ]; then echo "[understand] Detected git worktree at $PROJECT_ROOT" echo "[understand] Redirecting output to main repo root: $MAIN_ROOT" echo "[understand] (Set UNDERSTAND_NO_WORKTREE_REDIRECT=1 to keep PROJECT_ROOT as the worktree.)" PROJECT_ROOT="$MAIN_ROOT" fi fi fiSetif you intentionally want a per-worktree graph (rare — most users want the redirect). 1.5. Ensure the plugin is built. Later phases invoke Node scripts that importUNDERSTAND_NO_WORKTREE_REDIRECT=1. On a fresh install@understand-anything/coredoes not exist yet — build once.packages/core/dist/
Important: do not assume the plugin root is simply two directories above the skill path string. In many installationsis a symlink into the real plugin checkout. Prefer runtime-provided plugin roots first (for Claude), then fall back to universal symlinks, skill symlink resolution, and common clone-based install paths.~/.agents/skills/understandResolve the plugin root like this:bashSKILL_REAL=$(realpath ~/.agents/skills/understand 2>/dev/null || readlink -f ~/.agents/skills/understand 2>/dev/null || echo "") SELF_RELATIVE=$([ -n "$SKILL_REAL" ] && cd "$SKILL_REAL/../.." 2>/dev/null && pwd || echo "") COPILOT_SKILL_REAL=$(realpath ~/.copilot/skills/understand 2>/dev/null || readlink -f ~/.copilot/skills/understand 2>/dev/null || echo "") COPILOT_SELF_RELATIVE=$([ -n "$COPILOT_SKILL_REAL" ] && cd "$COPILOT_SKILL_REAL/../.." 2>/dev/null && pwd || echo "") PLUGIN_ROOT="" for candidate in \ "${CLAUDE_PLUGIN_ROOT}" \ "$HOME/.understand-anything-plugin" \ "$SELF_RELATIVE" \ "$COPILOT_SELF_RELATIVE" \ "$HOME/.codex/understand-anything/understand-anything-plugin" \ "$HOME/.opencode/understand-anything/understand-anything-plugin" \ "$HOME/.pi/understand-anything/understand-anything-plugin" \ "$HOME/understand-anything/understand-anything-plugin"; do if [ -n "$candidate" ] && [ -f "$candidate/package.json" ] && [ -f "$candidate/pnpm-workspace.yaml" ]; then PLUGIN_ROOT="$candidate" break fi done if [ -z "$PLUGIN_ROOT" ]; then echo "Error: Cannot find the understand-anything plugin root." echo "Checked:" echo " - ${CLAUDE_PLUGIN_ROOT:-<unset CLAUDE_PLUGIN_ROOT>}" echo " - $HOME/.understand-anything-plugin" echo " - ${SELF_RELATIVE:-<unresolved path derived from ~/.agents/skills/understand>}" echo " - ${COPILOT_SELF_RELATIVE:-<unresolved path derived from ~/.copilot/skills/understand>}" echo " - $HOME/.codex/understand-anything/understand-anything-plugin" echo " - $HOME/.opencode/understand-anything/understand-anything-plugin" echo " - $HOME/.pi/understand-anything/understand-anything-plugin" echo " - $HOME/understand-anything/understand-anything-plugin" echo "Make sure the plugin is installed correctly." exit 1 fi if [ ! -f "$PLUGIN_ROOT/packages/core/dist/index.js" ]; then cd "$PLUGIN_ROOT" && (pnpm install --frozen-lockfile 2>/dev/null || pnpm install) && pnpm --filter @understand-anything/core build fiIfis missing, report to the user: "Install Node.js ≥ 22 and pnpm ≥ 10, then re-runpnpm."/understand -
-
Get the current git commit hash:bash
git rev-parse HEAD -
Create the intermediate and temp output directories:bash
mkdir -p $PROJECT_ROOT/.understand-anything/intermediate mkdir -p $PROJECT_ROOT/.understand-anything/tmp
3.1. Purge stale trash dirs. Phase 7 cleanup s scratch dirs into rather than ing them directly (see issue #301), so that destructive-action gates on hardened hosts don't trip on just-created paths. Reclaim the space here once the trash is older than 7 days — by this point any freshness-window check has long since stopped caring about those dirs:
mv.trash-<timestamp>/rm -rfbash
find $PROJECT_ROOT/.understand-anything/ -maxdepth 1 -type d -name '.trash-*' -mtime +7 -exec rm -rf {} + 2>/dev/null || true3.5. Auto-update configuration:
- If is in : write to
- If is in : write to
- These flags only set the config — analysis proceeds normally regardless.
--auto-update$ARGUMENTS{"autoUpdate": true}$PROJECT_ROOT/.understand-anything/config.json--no-auto-update$ARGUMENTS{"autoUpdate": false}$PROJECT_ROOT/.understand-anything/config.json3.6. Language configuration:
- Parse for flag. If found, extract the language code.
- Language code normalization: Map friendly names to ISO codes:
- → , → , → , → , → , → , → , → , → , → , etc.
- Locale variants: , , , , etc. are preserved as-is.
- If is NOT specified:
- Stored preference wins. If has an field, set to it and skip the rest.
- Otherwise detect (first run only). Infer the predominant language of the user's conversation as an ISO 639-1 code (). If it is or cannot be confidently determined, set and proceed silently — no prompt (English users see no change).
- If ≠ , confirm once before analyzing: tell the user you detected and ask whether to generate all content in it; they press Enter/"yes" to accept, or type another language code/name to override (normalize via the friendly-name map above). If running non-interactively (no reply possible), skip the wait, use , and print a one-line notice instead of blocking.
- Persist the resolved (including ) into so it never re-prompts for this project.
- If IS specified:
- Update with the new language: merge into existing config.
- Store as for use throughout all phases.
- Language directive template: Store as :
$ARGUMENTS--language <lang>chinesezhjapanesejakoreankoenglishenspanishesfrenchfrgermandeportugueseptrussianruarabicarzh-TWzh-HKzh-CNpt-BR--language$PROJECT_ROOT/.understand-anything/config.jsonoutputLanguage$OUTPUT_LANGUAGE$DETECTED_LANGen$OUTPUT_LANGUAGE=en$DETECTED_LANGen<language>$DETECTED_LANG$OUTPUT_LANGUAGEenconfig.json--language$PROJECT_ROOT/.understand-anything/config.json{"outputLanguage": "<lang>"}$OUTPUT_LANGUAGE$LANGUAGE_DIRECTIVEmarkdown > **Language directive**: Generate all textual content (summaries, descriptions, tags, titles, languageNotes, languageLesson) in **{language}**. Maintain technical accuracy while using natural, native-level phrasing in the target language. Keep technical terms in English when no standard translation exists (e.g., "middleware", "hook", "barrel"). - Check for subdomain knowledge graphs to merge:
List all files in
*knowledge-graph*.jsonexcluding$PROJECT_ROOT/.understand-anything/itself (e.g.knowledge-graph.json,frontend-knowledge-graph.json). If any subdomain graphs exist, run the merge script bundled with this skill (located next to this SKILL.md file — use the skill directory path, not the project root):backend-knowledge-graph.json
bash
python <SKILL_DIR>/merge-subdomain-graphs.py $PROJECT_ROOTThe script discovers subdomain graphs, loads the existing as a base (if present), and merges everything into (deduplicating nodes and edges). Report the merge summary to the user, then continue with the merged graph.
knowledge-graph.jsonknowledge-graph.json-
Check ifexists. If it does, read it.
$PROJECT_ROOT/.understand-anything/knowledge-graph.json -
Check ifexists. If it does, read it to get
$PROJECT_ROOT/.understand-anything/meta.json.gitCommitHash -
Decision logic:
Condition Action flag in--full$ARGUMENTSFull analysis (all phases) No existing graph or meta Full analysis (all phases) flag + existing graph + unchanged commit hash--reviewSkip to Phase 6 (review-only — reuse existing assembled graph) Existing graph + unchanged commit hash Ask the user: "The graph is up to date at this commit. Would you like to: (a) run a full rebuild ( ), (b) run the LLM graph reviewer (--full), or (c) do nothing?" Then follow their choice. If they pick (c), STOP.--reviewExisting graph + changed files Incremental update (re-analyze changed files only) Review-only path: Copy the existingtoknowledge-graph.json, then jump directly to Phase 6 step 3.$PROJECT_ROOT/.understand-anything/intermediate/assembled-graph.jsonFor incremental updates, get the changed file list:bashgit diff <lastCommitHash>..HEAD --name-onlyIf this returns no files, report "Graph is up to date" and STOP. -
Collect project context for subagent injection:
- Read (or
README.md,README.rst) fromreadme.mdif it exists. Store as$PROJECT_ROOT(first 3000 characters).$README_CONTENT - Read the primary package manifest (,
package.json,pyproject.toml,Cargo.toml,go.mod) if it exists. Store aspom.xml.$MANIFEST_CONTENT - Capture the top-level directory tree:
Store asbash
find $PROJECT_ROOT -maxdepth 2 -type f -not -path '*/node_modules/*' -not -path '*/.git/*' -not -path '*/dist/*' | head -100.$DIR_TREE - Detect the project entry point by checking for common patterns (in order): ,
src/index.ts,src/main.ts,src/App.tsx,index.js,main.py,manage.py,app.py,wsgi.py,asgi.py,run.py,__main__.py,main.go,cmd/*/main.go,src/main.rs,src/lib.rs,src/main/java/**/Application.java,Program.cs,config.ru. Store first match asindex.php.$ENTRY_POINT
- Read
确定是运行完整分析还是增量更新。
-
解析:
PROJECT_ROOT-
从中解析非标志型参数(任何不以
$ARGUMENTS开头的参数)。若找到,将其视为目标目录路径。--- 若路径为相对路径,基于当前工作目录解析为绝对路径。
- 验证解析后的路径存在且为目录(执行)。若不存在或不是目录,向用户报告错误并终止。
test -d <path> - 将设置为解析后的绝对路径。
PROJECT_ROOT
-
若未找到目录路径参数,将设置为当前工作目录。
PROJECT_ROOT -
工作树重定向:若位于git工作树中(而非主检出目录),将输出重定向到主仓库根目录。Claude Code管理的工作树是临时的 — 写入其中的
PROJECT_ROOT会在会话结束时销毁,导致知识图谱丢失(问题#133)。通过比较.understand-anything/与git rev-parse --git-dir来检测工作树;在正常检出或子模块中,两者路径相同;在工作树中,两者路径不同,且git rev-parse --git-common-dir的父目录为主仓库根目录。--git-common-dirbashCOMMON_DIR=$(git -C "$PROJECT_ROOT" rev-parse --git-common-dir 2>/dev/null) GIT_DIR=$(git -C "$PROJECT_ROOT" rev-parse --git-dir 2>/dev/null) if [ -n "$COMMON_DIR" ] && [ -n "$GIT_DIR" ]; then COMMON_ABS=$(cd "$PROJECT_ROOT" && cd "$COMMON_DIR" 2>/dev/null && pwd -P) GIT_ABS=$(cd "$PROJECT_ROOT" && cd "$GIT_DIR" 2>/dev/null && pwd -P) if [ -n "$COMMON_ABS" ] && [ "$COMMON_ABS" != "$GIT_ABS" ]; then MAIN_ROOT=$(dirname "$COMMON_ABS") if [ -d "$MAIN_ROOT" ] && [ "${UNDERSTAND_NO_WORKTREE_REDIRECT:-0}" != "1" ]; then echo "[understand] Detected git worktree at $PROJECT_ROOT" echo "[understand] Redirecting output to main repo root: $MAIN_ROOT" echo "[understand] (Set UNDERSTAND_NO_WORKTREE_REDIRECT=1 to keep PROJECT_ROOT as the worktree.)" PROJECT_ROOT="$MAIN_ROOT" fi fi fi若有意为每个工作树生成独立图谱(少见 — 大多数用户希望重定向),请设置。 1.5. 确保插件已构建:后续阶段会调用导入UNDERSTAND_NO_WORKTREE_REDIRECT=1的Node脚本。全新安装时@understand-anything/core不存在 — 需构建一次。packages/core/dist/
重要提示:不要假设插件根目录就是技能路径字符串的上两级目录。在许多安装中,是指向真实插件检出目录的符号链接。优先使用运行时提供的插件根目录(针对Claude),然后回退到通用符号链接、技能符号链接解析和常见克隆安装路径。~/.agents/skills/understand按以下方式解析插件根目录:bashSKILL_REAL=$(realpath ~/.agents/skills/understand 2>/dev/null || readlink -f ~/.agents/skills/understand 2>/dev/null || echo "") SELF_RELATIVE=$([ -n "$SKILL_REAL" ] && cd "$SKILL_REAL/../.." 2>/dev/null && pwd || echo "") COPILOT_SKILL_REAL=$(realpath ~/.copilot/skills/understand 2>/dev/null || readlink -f ~/.copilot/skills/understand 2>/dev/null || echo "") COPILOT_SELF_RELATIVE=$([ -n "$COPILOT_SKILL_REAL" ] && cd "$COPILOT_SKILL_REAL/../.." 2>/dev/null && pwd || echo "") PLUGIN_ROOT="" for candidate in \ "${CLAUDE_PLUGIN_ROOT}" \ "$HOME/.understand-anything-plugin" \ "$SELF_RELATIVE" \ "$COPILOT_SELF_RELATIVE" \ "$HOME/.codex/understand-anything/understand-anything-plugin" \ "$HOME/.opencode/understand-anything/understand-anything-plugin" \ "$HOME/.pi/understand-anything/understand-anything-plugin" \ "$HOME/understand-anything/understand-anything-plugin"; do if [ -n "$candidate" ] && [ -f "$candidate/package.json" ] && [ -f "$candidate/pnpm-workspace.yaml" ]; then PLUGIN_ROOT="$candidate" break fi done if [ -z "$PLUGIN_ROOT" ]; then echo "Error: Cannot find the understand-anything plugin root." echo "Checked:" echo " - ${CLAUDE_PLUGIN_ROOT:-<unset CLAUDE_PLUGIN_ROOT>}" echo " - $HOME/.understand-anything-plugin" echo " - ${SELF_RELATIVE:-<unresolved path derived from ~/.agents/skills/understand>}" echo " - ${COPILOT_SELF_RELATIVE:-<unresolved path derived from ~/.copilot/skills/understand>}" echo " - $HOME/.codex/understand-anything/understand-anything-plugin" echo " - $HOME/.opencode/understand-anything/understand-anything-plugin" echo " - $HOME/.pi/understand-anything/understand-anything-plugin" echo " - $HOME/understand-anything/understand-anything-plugin" echo "Make sure the plugin is installed correctly." exit 1 fi if [ ! -f "$PLUGIN_ROOT/packages/core/dist/index.js" ]; then cd "$PLUGIN_ROOT" && (pnpm install --frozen-lockfile 2>/dev/null || pnpm install) && pnpm --filter @understand-anything/core build fi若缺少,向用户报告:"Install Node.js ≥ 22 and pnpm ≥ 10, then re-runpnpm."/understand -
-
获取当前git提交哈希:bash
git rev-parse HEAD -
创建中间和临时输出目录:bash
mkdir -p $PROJECT_ROOT/.understand-anything/intermediate mkdir -p $PROJECT_ROOT/.understand-anything/tmp
3.1. 清理过期垃圾目录:第7阶段清理时会将临时目录移动到而非直接(见问题#301),这样可避免在加固主机上触发破坏性操作限制(例如 freshness-window 检查),这些限制会标记刚创建的目录。在此处回收超过7天的垃圾目录空间 — 此时任何新鲜度窗口检查都不再关心这些目录:
.trash-<timestamp>/rm -rfbash
find $PROJECT_ROOT/.understand-anything/ -maxdepth 1 -type d -name '.trash-*' -mtime +7 -exec rm -rf {} + 2>/dev/null || true3.5. 自动更新配置:
- 若中包含:将写入
- 若中包含:将写入
- 这些标志仅设置配置 — 分析会正常进行,不受影响。
$ARGUMENTS--auto-update{"autoUpdate": true}$PROJECT_ROOT/.understand-anything/config.json$ARGUMENTS--no-auto-update{"autoUpdate": false}$PROJECT_ROOT/.understand-anything/config.json3.6. 语言配置:
- 从中解析标志。若找到,提取语言代码。
- 语言代码标准化:将友好名称映射为ISO代码:
- → , → , → , → , → , → , → , → , → , → 等。
- 区域变体:, , , 等保持原样。
- 若未指定:
- 存储的偏好优先:若包含字段,将设置为该值并跳过后续步骤。
- 否则进行检测(仅首次运行):推断用户对话的主要语言为ISO 639-1代码()。若为或无法确定,设置并静默继续 — 无提示(英语用户无变化)。
- 若 ≠ ,分析前确认一次:告知用户检测到,询问是否以此语言生成所有内容;用户按Enter/输入"yes"接受,或输入其他语言代码/名称覆盖(通过上述友好名称映射标准化)。若为非交互式运行(无法回复),跳过等待,使用,并打印单行通知而非阻塞。
- 持久化:将解析后的(包括)存入,以便该项目不再重复提示。
- 若指定了:
- 更新中的语言:将合并到现有配置中。
- 将其存储为,供所有阶段使用。
- 语言指令模板:存储为:
$ARGUMENTS--language <lang>chinesezhjapanesejakoreankoenglishenspanishesfrenchfrgermandeportugueseptrussianruarabicarzh-TWzh-HKzh-CNpt-BR--language$PROJECT_ROOT/.understand-anything/config.jsonoutputLanguage$OUTPUT_LANGUAGE$DETECTED_LANGen$OUTPUT_LANGUAGE=en$DETECTED_LANGen<language>$DETECTED_LANG$OUTPUT_LANGUAGEenconfig.json--language$PROJECT_ROOT/.understand-anything/config.json{"outputLanguage": "<lang>"}$OUTPUT_LANGUAGE$LANGUAGE_DIRECTIVEmarkdown > **Language directive**: Generate all textual content (summaries, descriptions, tags, titles, languageNotes, languageLesson) in **{language}**. Maintain technical accuracy while using natural, native-level phrasing in the target language. Keep technical terms in English when no standard translation exists (e.g., "middleware", "hook", "barrel"). - 检查待合并的子域知识图谱:
列出中所有
$PROJECT_ROOT/.understand-anything/文件,排除*knowledge-graph*.json本身(例如knowledge-graph.json,frontend-knowledge-graph.json)。若存在子域图谱,运行本技能附带的合并脚本(位于本SKILL.md文件旁 — 使用技能目录路径,而非项目根目录):backend-knowledge-graph.json
bash
python <SKILL_DIR>/merge-subdomain-graphs.py $PROJECT_ROOT该脚本会发现子域图谱,加载现有作为基础(若存在),并将所有内容合并到中(去重节点和边)。向用户报告合并摘要,然后继续处理合并后的图谱。
knowledge-graph.jsonknowledge-graph.json-
检查是否存在。若存在,读取该文件。
$PROJECT_ROOT/.understand-anything/knowledge-graph.json -
检查是否存在。若存在,读取其中的
$PROJECT_ROOT/.understand-anything/meta.json。gitCommitHash -
决策逻辑:
条件 操作 中包含$ARGUMENTS标志--full完整分析(所有阶段) 无现有图谱或元数据 完整分析(所有阶段) 包含 标志 + 存在现有图谱 + 提交哈希未变--review跳至阶段6(仅评审 — 复用现有已组装图谱) 存在现有图谱 + 提交哈希未变 询问用户:"The graph is up to date at this commit. Would you like to: (a) run a full rebuild ( ), (b) run the LLM graph reviewer (--full), or (c) do nothing?" 然后遵循用户选择。若用户选择(c),终止。--review存在现有图谱 + 文件已变更 增量更新(仅重新分析变更文件) 仅评审路径:将现有复制到knowledge-graph.json,然后直接跳至阶段6第3步。$PROJECT_ROOT/.understand-anything/intermediate/assembled-graph.json对于增量更新,获取变更文件列表:bashgit diff <lastCommitHash>..HEAD --name-only若返回无文件,报告"Graph is up to date"并终止。 -
收集子代理注入的项目上下文:
- 若下存在
$PROJECT_ROOT(或README.md,README.rst),读取该文件。存储为readme.md(前3000字符)。$README_CONTENT - 若存在主包清单(,
package.json,pyproject.toml,Cargo.toml,go.mod),读取该文件。存储为pom.xml。$MANIFEST_CONTENT - 捕获顶级目录树:
存储为bash
find $PROJECT_ROOT -maxdepth 2 -type f -not -path '*/node_modules/*' -not -path '*/.git/*' -not -path '*/dist/*' | head -100。$DIR_TREE - 通过检查常见模式(按顺序)检测项目入口点:,
src/index.ts,src/main.ts,src/App.tsx,index.js,main.py,manage.py,app.py,wsgi.py,asgi.py,run.py,__main__.py,main.go,cmd/*/main.go,src/main.rs,src/lib.rs,src/main/java/**/Application.java,Program.cs,config.ru。将第一个匹配项存储为index.php。$ENTRY_POINT
- 若
Phase 0.5 — Ignore Configuration
阶段0.5 — 忽略配置
Set up and verify the file before scanning.
.understandignore- Check if exists.
$PROJECT_ROOT/.understand-anything/.understandignore - If it does NOT exist, generate a starter file by invoking the bundled script (delegates to in
generateStarterIgnoreFile, which reads@understand-anything/core, deduplicates against built-in defaults, and emits language-grouped test-file suggestions). Pass.gitignorevia the env so the script doesn't have to re-derive it from its own path (which breaks for copied skill installs):$PLUGIN_ROOTbashPLUGIN_ROOT="$PLUGIN_ROOT" node <SKILL_DIR>/generate-ignore.mjs $PROJECT_ROOT- Report to the user:
Generatedwith suggested exclusions based on your project structure. Please review it and uncomment any patterns you'd like to exclude from analysis. When ready, confirm to continue.
.understand-anything/.understandignore - Wait for user confirmation before proceeding.
- Report to the user:
- If it already exists, report:
Found. Review it if needed, then confirm to continue.
.understand-anything/.understandignore- Wait for user confirmation before proceeding.
- After confirmation, proceed to Phase 1.
在扫描前设置并验证文件。
.understandignore- 检查是否存在。
$PROJECT_ROOT/.understand-anything/.understandignore - 若不存在,调用附带脚本生成初始文件(委托给中的
@understand-anything/core,该函数读取generateStarterIgnoreFile,与内置默认值去重,并按语言分组输出测试文件建议)。通过环境变量传递.gitignore,以便脚本无需从自身路径重新推导(这会在复制技能安装时失效):$PLUGIN_ROOTbashPLUGIN_ROOT="$PLUGIN_ROOT" node <SKILL_DIR>/generate-ignore.mjs $PROJECT_ROOT- 向用户报告:
Generatedwith suggested exclusions based on your project structure. Please review it and uncomment any patterns you'd like to exclude from analysis. When ready, confirm to continue.
.understand-anything/.understandignore - 等待用户确认后再继续。
- 向用户报告:
- 若已存在,报告:
Found. Review it if needed, then confirm to continue.
.understand-anything/.understandignore- 等待用户确认后再继续。
- 确认后,进入阶段1。
Phase 1 — SCAN (Full analysis only)
阶段1 — 扫描(仅完整分析)
Report to the user:
[Phase 1/7] Scanning project files...Dispatch a subagent using the agent definition (at ). Append the following additional context:
project-scanneragents/project-scanner.mdAdditional context from main session:Project README (first 3000 chars):$README_CONTENTPackage manifest:$MANIFEST_CONTENTUse this context to produce more accurate project name, description, and framework detection. The README and manifest are authoritative — prefer their information over heuristics.$LANGUAGE_DIRECTIVE
Pass these parameters in the dispatch prompt:
Scan this project directory to discover all project files (including non-code files like configs, docs, infrastructure), detect languages and frameworks. Project root:Write output to:$PROJECT_ROOT$PROJECT_ROOT/.understand-anything/intermediate/scan-result.json
After the subagent completes, read to get:
$PROJECT_ROOT/.understand-anything/intermediate/scan-result.json- Project name, description
- Languages, frameworks
- File list with line counts and per file (
fileCategory,code,config,docs,infra,data,script)markup - Complexity estimate
- Import map (): pre-resolved project-internal imports per file (non-code files have empty arrays)
importMap
Store in memory as for use in Phase 2 batch construction.
Store the file list as with metadata for use in Phase 2 batch construction.
importMap$IMPORT_MAP$FILE_LISTfileCategoryGate check: If >100 files, inform the user and suggest scoping with a subdirectory argument. Proceed only if user confirms or add guidance that this may take a while.
If the scan result includes , report:
filteredByIgnore > 0Excluded {filteredByIgnore} files via..understandignore
向用户报告:
[Phase 1/7] Scanning project files...使用代理定义(位于)调度子代理。附加以下额外上下文:
project-scanneragents/project-scanner.mdAdditional context from main session:Project README (first 3000 chars):$README_CONTENTPackage manifest:$MANIFEST_CONTENTUse this context to produce more accurate project name, description, and framework detection. The README and manifest are authoritative — prefer their information over heuristics.$LANGUAGE_DIRECTIVE
在调度提示中传递以下参数:
Scan this project directory to discover all project files (including non-code files like configs, docs, infrastructure), detect languages and frameworks. Project root:Write output to:$PROJECT_ROOT$PROJECT_ROOT/.understand-anything/intermediate/scan-result.json
子代理完成后,读取以获取:
$PROJECT_ROOT/.understand-anything/intermediate/scan-result.json- 项目名称、描述
- 语言、框架
- 文件列表,包含行数和每个文件的(
fileCategory,code,config,docs,infra,data,script)markup - 复杂度估算
- 导入映射():每个文件预解析的项目内部导入(非代码文件为空数组)
importMap
将存储在内存中作为,供阶段2批处理构建使用。
将文件列表存储为带元数据的,供阶段2批处理构建使用。
importMap$IMPORT_MAPfileCategory$FILE_LIST关卡检查:若文件数>100,告知用户并建议使用子目录参数限定范围。仅在用户确认或添加说明(可能耗时较长)后继续。
若扫描结果包含,报告:
filteredByIgnore > 0Excluded {filteredByIgnore} files via..understandignore
Phase 1.5 — BATCH
阶段1.5 — 批处理
Report:
[Phase 1.5/7] Computing semantic batches...Run the bundled batching script:
bash
node <SKILL_DIR>/compute-batches.mjs $PROJECT_ROOTReads , writes .
.understand-anything/intermediate/scan-result.json.understand-anything/intermediate/batches.jsonCapture stderr. Append any line starting with to for the final report.
Warning:$PHASE_WARNINGSIf the script exits non-zero, the failure is hard — relay the full stderr to the user as a Phase 1.5 failure. Do not attempt to recover; the script's internal fallback (count-based) already handles recoverable issues. A non-zero exit means a fundamental problem (missing input file, malformed JSON, etc.).
报告:
[Phase 1.5/7] Computing semantic batches...运行附带的批处理脚本:
bash
node <SKILL_DIR>/compute-batches.mjs $PROJECT_ROOT读取,写入。
.understand-anything/intermediate/scan-result.json.understand-anything/intermediate/batches.json捕获stderr。将任何以开头的行追加到,用于最终报告。
Warning:$PHASE_WARNINGS若脚本非零退出,说明是严重失败 — 将完整stderr转发给用户作为阶段1.5失败信息。不要尝试恢复;脚本的内部回退(基于计数)已处理可恢复问题。非零退出意味着存在根本性问题(缺少输入文件、JSON格式错误等)。
Phase 2 — ANALYZE
阶段2 — 分析
Full analysis path
完整分析路径
Load (produced by Phase 1.5). Iterate the array.
.understand-anything/intermediate/batches.jsonbatches[]Report:
[Phase 2/7] Analyzing files — <totalFiles> files in <totalBatches> batches (up to 5 concurrent)...For each batch, dispatch a subagent using the agent definition (at ). Run up to 5 subagents concurrently. Append the following additional context:
file-analyzeragents/file-analyzer.mdAdditional context from main session:Project:—<projectName>Languages:<projectDescription><languages from Phase 1>$LANGUAGE_DIRECTIVE
Dispatch prompt template (fill in batch-specific values from ):
batches.json[i]Analyze these files and produce GraphNode and GraphEdge objects. Project root:Project:$PROJECT_ROOTLanguages:<projectName>Batch:<languages>Skill directory (for bundled scripts):<batchIndex>/<totalBatches>Output: write to<SKILL_DIR>(single-file mode) OR$PROJECT_ROOT/.understand-anything/intermediate/batch-<batchIndex>.json(split mode, per Step B of your output protocol).batch-<batchIndex>-part-<k>.jsonPre-resolved import data for this batch (use directly — do NOT re-resolve imports from source):json<batchImportData JSON from batches.json[i].batchImportData>Cross-batch neighbors with their exported symbols (confidence boost for cross-batch edges):json<neighborMap JSON from batches.json[i].neighborMap>Files to analyze in this batch (every entry MUST be passed through towith all four fields —batchFiles,path,language,sizeLines):fileCategory
(<sizeLines> lines, language:<path>, fileCategory:<language>)<fileCategory> (<sizeLines> lines, language:<path>, fileCategory:<language>) ...<fileCategory>
Output naming is per-batchIndex — no fusion. If you fuse multiple small batches into a single file-analyzer dispatch for token efficiency, the dispatched agent must STILL write one output file per original using or . The merge script's regex () silently drops any other naming (e.g., , ), losing every node and edge in that file. After each dispatch returns, verify each in the dispatched input has a corresponding (or ) on disk before proceeding to the next dispatch.
batchIndexbatch-<batchIndex>.jsonbatch-<batchIndex>-part-<k>.jsonbatch-(\d+)(?:-part-(\d+))?\.jsonbatch-fused-8-13.jsonbatch-8-13.jsonbatchIndexbatch-<batchIndex>.jsonbatch-<batchIndex>-part-*.jsonAfter ALL batches complete, report to the user:
Phase 2 complete. All <totalBatches> batches analyzed.Run the merge-and-normalize script bundled with this skill (located next to this SKILL.md file — use the skill directory path, not the project root):
bash
python <SKILL_DIR>/merge-batch-graphs.py $PROJECT_ROOTThis script reads all files (including produced by file-analyzers that split their output) from , then in one pass:
batch-*.jsonbatch-<i>-part-<k>.json$PROJECT_ROOT/.understand-anything/intermediate/- Combines all nodes and edges across batches
- Normalizes node IDs (strips double prefixes, project-name prefixes, adds missing prefixes)
- Normalizes complexity values (→
low,simple→medium,moderate→high, etc.)complex - Rewrites edge references to match corrected node IDs
- Deduplicates nodes by ID (keeps last occurrence) and edges by
(source, target, type) - Drops dangling edges referencing missing nodes
- Logs all corrections and dropped items to stderr
The merge script also runs a linker that canonicalizes test-coverage edges in two passes. Pass 1 walks LLM-emitted edges and flips inverted ones in place; semantically broken edges (test↔test, prod↔prod, orphan endpoints) are dropped. Pass 2 supplements with path-convention pairings. Production nodes that end up sourcing any edge get a tag. All resulting edges run .
tested_bytested_bytested_by"tested"production → testOutput:
$PROJECT_ROOT/.understand-anything/intermediate/assembled-graph.jsonInclude the script's warnings in for the reviewer.
$PHASE_WARNINGS加载(阶段1.5生成)。遍历数组。
.understand-anything/intermediate/batches.jsonbatches[]报告:
[Phase 2/7] Analyzing files — <totalFiles> files in <totalBatches> batches (up to 5 concurrent)...对于每个批次,使用代理定义(位于)调度子代理。最多5个子代理并发运行。附加以下额外上下文:
file-analyzeragents/file-analyzer.mdAdditional context from main session:Project:—<projectName>Languages:<projectDescription><languages from Phase 1>$LANGUAGE_DIRECTIVE
调度提示模板(填充中的批次特定值):
batches.json[i]Analyze these files and produce GraphNode and GraphEdge objects. Project root:Project:$PROJECT_ROOTLanguages:<projectName>Batch:<languages>Skill directory (for bundled scripts):<batchIndex>/<totalBatches>Output: write to<SKILL_DIR>(single-file mode) OR$PROJECT_ROOT/.understand-anything/intermediate/batch-<batchIndex>.json(split mode, per Step B of your output protocol).batch-<batchIndex>-part-<k>.jsonPre-resolved import data for this batch (use directly — do NOT re-resolve imports from source):json<batchImportData JSON from batches.json[i].batchImportData>Cross-batch neighbors with their exported symbols (confidence boost for cross-batch edges):json<neighborMap JSON from batches.json[i].neighborMap>Files to analyze in this batch (every entry MUST be passed through towith all four fields —batchFiles,path,language,sizeLines):fileCategory
(<sizeLines> lines, language:<path>, fileCategory:<language>)<fileCategory> (<sizeLines> lines, language:<path>, fileCategory:<language>) ...<fileCategory>
输出命名按batchIndex — 不合并。若为提高token效率将多个小批次合并为单个file-analyzer调度,被调度的代理仍必须为每个原始写入一个输出文件,使用或。合并脚本的正则表达式()会静默丢弃其他命名(例如, ),导致该文件中的所有节点和边丢失。每个调度返回后,在继续下一个调度前,验证调度输入中的每个在磁盘上都有对应的(或)。
batchIndexbatch-<batchIndex>.jsonbatch-<batchIndex>-part-<k>.jsonbatch-(\d+)(?:-part-(\d+))?\.jsonbatch-fused-8-13.jsonbatch-8-13.jsonbatchIndexbatch-<batchIndex>.jsonbatch-<batchIndex>-part-*.json所有批次完成后,向用户报告:
Phase 2 complete. All <totalBatches> batches analyzed.运行本技能附带的合并与标准化脚本(位于本SKILL.md文件旁 — 使用技能目录路径,而非项目根目录):
bash
python <SKILL_DIR>/merge-batch-graphs.py $PROJECT_ROOT该脚本读取中的所有文件(包括file-analyzer拆分输出的),然后一次性执行以下操作:
$PROJECT_ROOT/.understand-anything/intermediate/batch-*.jsonbatch-<i>-part-<k>.json- 合并所有批次的节点和边
- 标准化节点ID(去除双重前缀、项目名称前缀,添加缺失前缀)
- 标准化复杂度值(→
low,simple→medium,moderate→high等)complex - 重写边引用以匹配修正后的节点ID
- 按ID去重节点(保留最后出现的),按去重边
(source, target, type) - 删除引用缺失节点的悬空边
- 将所有修正和丢弃项记录到stderr
合并脚本还会运行链接器,分两批规范化测试覆盖边。第1批遍历LLM生成的边,就地翻转反向边;语义错误的边(测试↔测试、生产↔生产、孤立端点)会被丢弃。第2批补充路径约定配对。最终有边的生产节点会获得标签。所有生成的边均为方向。
tested_bytested_bytested_by"tested"production → test输出:
$PROJECT_ROOT/.understand-anything/intermediate/assembled-graph.json将脚本的警告包含到中,供评审器使用。
$PHASE_WARNINGSIncremental update path
增量更新路径
Write the changed-files list (one path per line) to a temp file:
bash
git diff <lastCommitHash>..HEAD --name-only > $PROJECT_ROOT/.understand-anything/tmp/changed-files.txtRun compute-batches with :
--changed-filesbash
node <SKILL_DIR>/compute-batches.mjs $PROJECT_ROOT \
--changed-files=$PROJECT_ROOT/.understand-anything/tmp/changed-files.txtThis produces a that contains only batches with changed files, but neighborMap entries still reference unchanged files (with their full-graph batchIndex) so cross-batch edges remain emittable.
batches.jsonThen dispatch file-analyzer subagents per the same template as the full path.
After batches complete:
- Remove old nodes whose matches any changed file from the existing graph
filePath - Remove old edges whose or
sourcereferences a removed nodetarget - Write the pruned existing nodes/edges as in the intermediate directory
batch-existing.json - Run the same merge script — it will combine with the fresh
batch-existing.jsonfiles:batch-*.jsonbashpython <SKILL_DIR>/merge-batch-graphs.py $PROJECT_ROOT
将变更文件列表(每行一个路径)写入临时文件:
bash
git diff <lastCommitHash>..HEAD --name-only > $PROJECT_ROOT/.understand-anything/tmp/changed-files.txt使用运行compute-batches:
--changed-filesbash
node <SKILL_DIR>/compute-batches.mjs $PROJECT_ROOT \
--changed-files=$PROJECT_ROOT/.understand-anything/tmp/changed-files.txt这会生成仅包含变更文件批次的,但neighborMap条目仍引用未变更文件(带全图谱batchIndex),因此仍可生成跨批次边。
batches.json然后按照完整路径的相同模板调度file-analyzer子代理。
批次完成后:
- 从现有图谱中删除匹配任何变更文件的旧节点
filePath - 删除引用已删除节点的旧边
- 将修剪后的现有节点/边写入中间目录的
batch-existing.json - 运行相同的合并脚本 — 它会将与新的
batch-existing.json文件合并:batch-*.jsonbashpython <SKILL_DIR>/merge-batch-graphs.py $PROJECT_ROOT
Phase 3 — ASSEMBLE REVIEW
阶段3 — 组装评审
Report to the user:
[Phase 3/7] Reviewing assembled graph...Dispatch a subagent using the agent definition (at ).
assemble-revieweragents/assemble-reviewer.mdPass these parameters in the dispatch prompt:
Review the assembled graph at. Project root:$PROJECT_ROOT/.understand-anything/intermediate/assembled-graph.jsonBatch files are at:$PROJECT_ROOTWrite review output to:$PROJECT_ROOT/.understand-anything/intermediate/batch-*.json$PROJECT_ROOT/.understand-anything/intermediate/assemble-review.jsonMerge script report:<paste the full stderr output from merge-batch-graphs.py>Import map for cross-batch edge verification:json$IMPORT_MAP
After the subagent completes, read and add any notes to .
$PROJECT_ROOT/.understand-anything/intermediate/assemble-review.json$PHASE_WARNINGS向用户报告:
[Phase 3/7] Reviewing assembled graph...使用代理定义(位于)调度子代理。
assemble-revieweragents/assemble-reviewer.md在调度提示中传递以下参数:
Review the assembled graph at. Project root:$PROJECT_ROOT/.understand-anything/intermediate/assembled-graph.jsonBatch files are at:$PROJECT_ROOTWrite review output to:$PROJECT_ROOT/.understand-anything/intermediate/batch-*.json$PROJECT_ROOT/.understand-anything/intermediate/assemble-review.jsonMerge script report:<paste the full stderr output from merge-batch-graphs.py>Import map for cross-batch edge verification:json$IMPORT_MAP
子代理完成后,读取并将任何注释添加到。
$PROJECT_ROOT/.understand-anything/intermediate/assemble-review.json$PHASE_WARNINGSPhase 4 — ARCHITECTURE
阶段4 — 架构分析
Report to the user:
[Phase 4/7] Identifying architectural layers...Build the combined prompt template:
- Use the agent definition (at
architecture-analyzer).agents/architecture-analyzer.md - Language context injection: For each language detected in Phase 1 (e.g., ,
python,markdown,dockerfile,yaml,sql,terraform,graphql,protobuf,shell,html), read the file atcss(e.g.,./languages/<language-id>.md,./languages/python.md) and append its content after the base template under a./languages/dockerfile.mdheader. If the file does not exist for a detected language, skip it silently and continue. These files are in the## Language Contextsubdirectory next to this SKILL.md file. Include non-code language snippets — they provide edge patterns and summary styles for non-code files.languages/ - Framework addendum injection: For each framework detected in Phase 1 (e.g., ), read the file at
Django(e.g.,./frameworks/<framework-id-lowercase>.md) and append its full content after the language context. If the file does not exist for a detected framework, skip it silently and continue. These files are in the./frameworks/django.mdsubdirectory next to this SKILL.md file.frameworks/ - Output locale injection: If is NOT
$OUTPUT_LANGUAGE(English), read the locale guidance file aten(e.g.,./locales/<language-code>.md,./locales/zh.md,./locales/ja.md) and append its content after the framework addendums under a./locales/ko.mdheader. This provides language-specific guidance for tag naming conventions, summary style, and layer name translations. If the locale file does not exist for the specified language, skip silently — the## Output Language Guidelinesstill applies. These files are in the$LANGUAGE_DIRECTIVEsubdirectory next to this SKILL.md file.locales/
Append the language/framework context and the following additional context to the agent's prompt:
Additional context from main session:Frameworks detected:<frameworks from Phase 1>Directory tree (top 2 levels):$DIR_TREEUse the directory tree, language context, and framework addendums (appended above) to inform layer assignments. Directory structure is strong evidence for layer boundaries. Non-code files (config, docs, infrastructure, data) should be assigned to appropriate layers — see the prompt template for guidance.$LANGUAGE_DIRECTIVE
Pass these parameters in the dispatch prompt:
Analyze this codebase's structure to identify architectural layers. Project root:Write output to:$PROJECT_ROOTProject:$PROJECT_ROOT/.understand-anything/intermediate/layers.json—<projectName><projectDescription>File nodes (all node types — includes code files, config, document, service, pipeline, table, schema, resource, endpoint):json[list of {id, type, name, filePath, summary, tags} for ALL file-level nodes — omit complexity, languageNotes]Import edges:json[list of edges with type "imports"]All edges (for cross-category analysis — includes configures, documents, deploys, triggers, etc.):json[list of ALL edges — include all edge types]
After the subagent completes, read and normalize it into a final array. Apply these steps in order:
$PROJECT_ROOT/.understand-anything/intermediate/layers.jsonlayers- Unwrap envelope: If the file contains instead of a plain array, extract the inner array. (The prompt requests a plain array, but LLMs may still produce an envelope.)
{ "layers": [...] } - Rename legacy fields: If any layer object has a field instead of
nodes, renamenodeIds→nodes. IfnodeIdsentries are objects with annodesfield rather than plain strings, extract just theidvalues intoid.nodeIds - Synthesize missing IDs: If any layer is missing an , generate one as
id.layer:<kebab-case-name> - Convert file paths: If entries are raw file paths without a known prefix (
nodeIds,file:,config:,document:,service:,pipeline:,table:,schema:,resource:), convert them toendpoint:.file:<relative-path> - Drop dangling refs: Remove any entries that do not exist in the merged node set.
nodeIds
Each element of the final array MUST have this shape:
layersjson
[
{
"id": "layer:<kebab-case-name>",
"name": "<layer name>",
"description": "<what belongs in this layer>",
"nodeIds": ["file:src/App.tsx", "config:tsconfig.json", "document:README.md"]
}
]All four fields (, , , ) are required.
idnamedescriptionnodeIdsFor incremental updates: Always re-run architecture analysis on the full merged node set, since layer assignments may shift when files change.
Context for incremental updates: When re-running architecture analysis, also inject the previous layer definitions:
Previous layer definitions (for naming consistency):json[previous layers from existing graph]Maintain the same layer names and IDs where possible. Only add/remove layers if the file structure has materially changed.
向用户报告:
[Phase 4/7] Identifying architectural layers...构建组合提示模板:
- 使用代理定义(位于
architecture-analyzer)。agents/architecture-analyzer.md - 语言上下文注入:对于阶段1检测到的每种语言(例如,
python,markdown,dockerfile,yaml,sql,terraform,graphql,protobuf,shell,html),读取css文件(例如./languages/<language-id>.md,./languages/python.md),并将其内容追加到基础模板的./languages/dockerfile.md标题下。若检测到的语言对应的文件不存在,静默跳过并继续。这些文件位于本SKILL.md文件旁的## Language Context子目录中。包含非代码语言片段 — 它们为非代码文件提供边模式和摘要样式。languages/ - 框架附录注入:对于阶段1检测到的每个框架(例如),读取
Django文件(例如./frameworks/<framework-id-lowercase>.md),并将其完整内容追加到语言上下文之后。若框架对应的文件不存在,静默跳过并继续。这些文件位于本SKILL.md文件旁的./frameworks/django.md子目录中。frameworks/ - 输出区域注入:若不是
$OUTPUT_LANGUAGE(英语),读取en区域指导文件(例如./locales/<language-code>.md,./locales/zh.md,./locales/ja.md),并将其内容追加到框架附录之后的./locales/ko.md标题下。这提供了特定语言的标签命名约定、摘要样式和层名称翻译指导。若指定语言的区域文件不存在,静默跳过 —## Output Language Guidelines仍适用。这些文件位于本SKILL.md文件旁的$LANGUAGE_DIRECTIVE子目录中。locales/
将语言/框架上下文和以下额外上下文追加到代理的提示中:
Additional context from main session:Frameworks detected:<frameworks from Phase 1>Directory tree (top 2 levels):$DIR_TREEUse the directory tree, language context, and framework addendums (appended above) to inform layer assignments. Directory structure is strong evidence for layer boundaries. Non-code files (config, docs, infrastructure, data) should be assigned to appropriate layers — see the prompt template for guidance.$LANGUAGE_DIRECTIVE
在调度提示中传递以下参数:
Analyze this codebase's structure to identify architectural layers. Project root:Write output to:$PROJECT_ROOTProject:$PROJECT_ROOT/.understand-anything/intermediate/layers.json—<projectName><projectDescription>File nodes (all node types — includes code files, config, document, service, pipeline, table, schema, resource, endpoint):json[list of {id, type, name, filePath, summary, tags} for ALL file-level nodes — omit complexity, languageNotes]Import edges:json[list of edges with type "imports"]All edges (for cross-category analysis — includes configures, documents, deploys, triggers, etc.):json[list of ALL edges — include all edge types]
子代理完成后,读取并将其标准化为最终的数组。按顺序应用以下步骤:
$PROJECT_ROOT/.understand-anything/intermediate/layers.jsonlayers- 解包信封:若文件包含而非纯数组,提取内部数组。(提示要求纯数组,但LLM仍可能生成信封结构。)
{ "layers": [...] } - 重命名遗留字段:若任何层对象有字段而非
nodes,将nodeIds重命名为nodes。若nodeIds条目是带nodes字段的对象而非纯字符串,仅提取id值到id。nodeIds - 合成缺失ID:若任何层缺少,生成
id格式的ID。layer:<kebab-case-name> - 转换文件路径:若条目是无前缀的原始文件路径(
nodeIds,file:,config:,document:,service:,pipeline:,table:,schema:,resource:),转换为endpoint:格式。file:<relative-path> - 删除悬空引用:移除任何不存在于合并节点集中的条目。
nodeIds
最终数组的每个元素必须具有以下结构:
layersjson
[
{
"id": "layer:<kebab-case-name>",
"name": "<layer name>",
"description": "<what belongs in this layer>",
"nodeIds": ["file:src/App.tsx", "config:tsconfig.json", "document:README.md"]
}
]四个字段(, , , )均为必填项。
idnamedescriptionnodeIds对于增量更新:始终在完整合并节点集上重新运行架构分析,因为文件变更可能导致层分配变化。
增量更新上下文:重新运行架构分析时,还需注入之前的层定义:
Previous layer definitions (for naming consistency):json[previous layers from existing graph]Maintain the same layer names and IDs where possible. Only add/remove layers if the file structure has materially changed.
Phase 5 — TOUR
阶段5 — 导览构建
Report to the user:
[Phase 5/7] Building guided tour...Dispatch a subagent using the agent definition (at ). Append the following additional context:
tour-builderagents/tour-builder.mdAdditional context from main session:Project README (first 3000 chars):$README_CONTENTProject entry point:$ENTRY_POINTUse the README to align the tour narrative with the project's own documentation. Start the tour from the entry point if one was detected. The tour should tell the same story the README tells, but through the lens of actual code structure.$LANGUAGE_DIRECTIVE
Pass these parameters in the dispatch prompt:
Create a guided learning tour for this codebase. Project root:Write output to:$PROJECT_ROOTProject:$PROJECT_ROOT/.understand-anything/intermediate/tour.json—<projectName>Languages:<projectDescription><languages>Nodes (all file-level nodes — includes code files, config, document, service, pipeline, table, schema, resource, endpoint):json[list of {id, name, filePath, summary, type} for ALL file-level nodes — do NOT include function or class nodes]Layers:json[list of {id, name, description} for each layer — omit nodeIds]Edges (all types — includes imports, calls, configures, documents, deploys, triggers, etc.):json[list of ALL edges — include all edge types for complete graph topology analysis]
After the subagent completes, read and normalize it into a final array. Apply these steps in order:
$PROJECT_ROOT/.understand-anything/intermediate/tour.jsontour- Unwrap envelope: If the file contains instead of a plain array, extract the inner array. (The prompt requests a plain array, but LLMs may still produce an envelope.)
{ "steps": [...] } - Rename legacy fields: If any step has instead of
nodesToInspect, rename it →nodeIds. If any step hasnodeIdsinstead ofwhyItMatters, rename it →description.description - Convert file paths: If entries are raw file paths without a known prefix (
nodeIds,file:,config:,document:,service:,pipeline:,table:,schema:,resource:), convert them toendpoint:.file:<relative-path> - Drop dangling refs: Remove any entries that do not exist in the merged node set.
nodeIds - Sort by before saving.
order
Each element of the final array MUST have this shape:
tourjson
[
{
"order": 1,
"title": "Project Overview",
"description": "Start with the README to understand the project's purpose and architecture.",
"nodeIds": ["document:README.md"]
},
{
"order": 2,
"title": "Application Entry Point",
"description": "This step explains how the frontend boots and mounts.",
"nodeIds": ["file:src/main.tsx", "file:src/App.tsx"]
}
]Required fields: , , , . Preserve optional when present.
ordertitledescriptionnodeIdslanguageLesson向用户报告:
[Phase 5/7] Building guided tour...使用代理定义(位于)调度子代理。附加以下额外上下文:
tour-builderagents/tour-builder.mdAdditional context from main session:Project README (first 3000 chars):$README_CONTENTProject entry point:$ENTRY_POINTUse the README to align the tour narrative with the project's own documentation. Start the tour from the entry point if one was detected. The tour should tell the same story the README tells, but through the lens of actual code structure.$LANGUAGE_DIRECTIVE
在调度提示中传递以下参数:
Create a guided learning tour for this codebase. Project root:Write output to:$PROJECT_ROOTProject:$PROJECT_ROOT/.understand-anything/intermediate/tour.json—<projectName>Languages:<projectDescription><languages>Nodes (all file-level nodes — includes code files, config, document, service, pipeline, table, schema, resource, endpoint):json[list of {id, name, filePath, summary, type} for ALL file-level nodes — do NOT include function or class nodes]Layers:json[list of {id, name, description} for each layer — omit nodeIds]Edges (all types — includes imports, calls, configures, documents, deploys, triggers, etc.):json[list of ALL edges — include all edge types for complete graph topology analysis]
子代理完成后,读取并将其标准化为最终的数组。按顺序应用以下步骤:
$PROJECT_ROOT/.understand-anything/intermediate/tour.jsontour- 解包信封:若文件包含而非纯数组,提取内部数组。(提示要求纯数组,但LLM仍可能生成信封结构。)
{ "steps": [...] } - 重命名遗留字段:若任何步骤有而非
nodesToInspect,将其重命名为nodeIds。若任何步骤有nodeIds而非whyItMatters,将其重命名为description。description - 转换文件路径:若条目是无前缀的原始文件路径(
nodeIds,file:,config:,document:,service:,pipeline:,table:,schema:,resource:),转换为endpoint:格式。file:<relative-path> - 删除悬空引用:移除任何不存在于合并节点集中的条目。
nodeIds - 排序:保存前按排序。
order
最终数组的每个元素必须具有以下结构:
tourjson
[
{
"order": 1,
"title": "Project Overview",
"description": "Start with the README to understand the project's purpose and architecture.",
"nodeIds": ["document:README.md"]
},
{
"order": 2,
"title": "Application Entry Point",
"description": "This step explains how the frontend boots and mounts.",
"nodeIds": ["file:src/main.tsx", "file:src/App.tsx"]
}
]必填字段:, , , 。若存在可选字段,请保留。
ordertitledescriptionnodeIdslanguageLessonPhase 6 — REVIEW
阶段6 — 评审
Report to the user:
[Phase 6/7] Validating knowledge graph...Assemble the full KnowledgeGraph JSON object:
json
{
"version": "1.0.0",
"project": {
"name": "<projectName>",
"languages": ["<languages>"],
"frameworks": ["<frameworks>"],
"description": "<projectDescription>",
"analyzedAt": "<ISO 8601 timestamp>",
"gitCommitHash": "<commit hash from Phase 0>"
},
"nodes": [<all nodes from assembled-graph.json after Phase 3 review>],
"edges": [<all edges from assembled-graph.json after Phase 3 review>],
"layers": [<layers from Phase 4>],
"tour": [<steps from Phase 5>]
}-
Before writing the assembled graph, validate that:
- is an array of objects with these required fields:
layers,id,name,descriptionnodeIds - is an array of objects with these required fields:
tour,order,title,descriptionnodeIds - is allowed as an optional string field
tour[*].languageLesson - Every entry exists in the merged node set
layers[*].nodeIds - Every entry exists in the merged node set
tour[*].nodeIds
If validation fails, automatically normalize and rewrite the graph into this shape before saving. If the graph still fails final validation after the normalization pass, save it with warnings but mark dashboard auto-launch as skipped. -
Write the assembled graph to.
$PROJECT_ROOT/.understand-anything/intermediate/assembled-graph.json -
Checkfor
$ARGUMENTSflag. Then run the appropriate validation path:--review
向用户报告:
[Phase 6/7] Validating knowledge graph...组装完整的KnowledgeGraph JSON对象:
json
{
"version": "1.0.0",
"project": {
"name": "<projectName>",
"languages": ["<languages>"],
"frameworks": ["<frameworks>"],
"description": "<projectDescription>",
"analyzedAt": "<ISO 8601 timestamp>",
"gitCommitHash": "<commit hash from Phase 0>"
},
"nodes": [<all nodes from assembled-graph.json after Phase 3 review>],
"edges": [<all edges from assembled-graph.json after Phase 3 review>],
"layers": [<layers from Phase 4>],
"tour": [<steps from Phase 5>]
}-
写入组装图谱前,验证:
- 是包含以下必填字段的对象数组:
layers,id,name,descriptionnodeIds - 是包含以下必填字段的对象数组:
tour,order,title,descriptionnodeIds - 是允许的可选字符串字段
tour[*].languageLesson - 每个条目都存在于合并节点集中
layers[*].nodeIds - 每个条目都存在于合并节点集中
tour[*].nodeIds
若验证失败,自动将图谱标准化并重写为上述结构后保存。若标准化后仍未通过最终验证,保存图谱并附带警告,但标记跳过仪表盘自动启动。 -
将组装图谱写入。
$PROJECT_ROOT/.understand-anything/intermediate/assembled-graph.json -
检查中是否有
$ARGUMENTS标志。然后运行相应的验证路径:--review
Default path (no --review
): inline deterministic validation
--review默认路径(无--review
):内联确定性验证
--reviewWrite the following Node.js script to :
$PROJECT_ROOT/.understand-anything/tmp/ua-inline-validate.cjsjavascript
#!/usr/bin/env node
const fs = require('fs');
const graphPath = process.argv[2];
const outputPath = process.argv[3];
try {
const graph = JSON.parse(fs.readFileSync(graphPath, 'utf8'));
const issues = [], warnings = [];
if (!Array.isArray(graph.nodes)) { issues.push('graph.nodes is missing or not an array'); graph.nodes = []; }
if (!Array.isArray(graph.edges)) { issues.push('graph.edges is missing or not an array'); graph.edges = []; }
const nodeIds = new Set();
const seen = new Map();
graph.nodes.forEach((n, i) => {
if (!n.id) { issues.push(`Node[${i}] missing id`); return; }
if (!n.type) issues.push(`Node[${i}] '${n.id}' missing type`);
if (!n.name) issues.push(`Node[${i}] '${n.id}' missing name`);
if (!n.summary) issues.push(`Node[${i}] '${n.id}' missing summary`);
if (!n.tags || !n.tags.length) issues.push(`Node[${i}] '${n.id}' missing tags`);
if (seen.has(n.id)) issues.push(`Duplicate node ID '${n.id}' at indices ${seen.get(n.id)} and ${i}`);
else seen.set(n.id, i);
nodeIds.add(n.id);
});
graph.edges.forEach((e, i) => {
if (!nodeIds.has(e.source)) issues.push(`Edge[${i}] source '${e.source}' not found`);
if (!nodeIds.has(e.target)) issues.push(`Edge[${i}] target '${e.target}' not found`);
});
const fileLevelTypes = new Set(['file', 'config', 'document', 'service', 'pipeline', 'table', 'schema', 'resource', 'endpoint']);
const fileNodes = graph.nodes.filter(n => fileLevelTypes.has(n.type)).map(n => n.id);
const assigned = new Map();
if (!Array.isArray(graph.layers)) { if (graph.layers) warnings.push('graph.layers is not an array'); graph.layers = []; }
if (!Array.isArray(graph.tour)) { if (graph.tour) warnings.push('graph.tour is not an array'); graph.tour = []; }
graph.layers.forEach(layer => {
(layer.nodeIds || []).forEach(id => {
if (!nodeIds.has(id)) issues.push(`Layer '${layer.id}' refs missing node '${id}'`);
if (assigned.has(id)) issues.push(`Node '${id}' appears in multiple layers`);
assigned.set(id, layer.id);
});
});
fileNodes.forEach(id => {
if (!assigned.has(id)) issues.push(`File node '${id}' not in any layer`);
});
graph.tour.forEach((step, i) => {
(step.nodeIds || []).forEach(id => {
if (!nodeIds.has(id)) issues.push(`Tour step[${i}] refs missing node '${id}'`);
});
});
const withEdges = new Set([
...graph.edges.map(e => e.source),
...graph.edges.map(e => e.target)
]);
graph.nodes.forEach(n => {
if (!withEdges.has(n.id)) warnings.push(`Node '${n.id}' has no edges (orphan)`);
});
const stats = {
totalNodes: graph.nodes.length,
totalEdges: graph.edges.length,
totalLayers: graph.layers.length,
tourSteps: graph.tour.length,
nodeTypes: graph.nodes.reduce((a, n) => { a[n.type] = (a[n.type]||0)+1; return a; }, {}),
edgeTypes: graph.edges.reduce((a, e) => { a[e.type] = (a[e.type]||0)+1; return a; }, {})
};
fs.writeFileSync(outputPath, JSON.stringify({ issues, warnings, stats }, null, 2));
process.exit(0);
} catch (err) { process.stderr.write(err.message + '\n'); process.exit(1); }Execute it:
bash
node $PROJECT_ROOT/.understand-anything/tmp/ua-inline-validate.cjs \
"$PROJECT_ROOT/.understand-anything/intermediate/assembled-graph.json" \
"$PROJECT_ROOT/.understand-anything/intermediate/review.json"If the script exits non-zero, read stderr, fix the script, and retry once.
将以下Node.js脚本写入:
$PROJECT_ROOT/.understand-anything/tmp/ua-inline-validate.cjsjavascript
#!/usr/bin/env node
const fs = require('fs');
const graphPath = process.argv[2];
const outputPath = process.argv[3];
try {
const graph = JSON.parse(fs.readFileSync(graphPath, 'utf8'));
const issues = [], warnings = [];
if (!Array.isArray(graph.nodes)) { issues.push('graph.nodes is missing or not an array'); graph.nodes = []; }
if (!Array.isArray(graph.edges)) { issues.push('graph.edges is missing or not an array'); graph.edges = []; }
const nodeIds = new Set();
const seen = new Map();
graph.nodes.forEach((n, i) => {
if (!n.id) { issues.push(`Node[${i}] missing id`); return; }
if (!n.type) issues.push(`Node[${i}] '${n.id}' missing type`);
if (!n.name) issues.push(`Node[${i}] '${n.id}' missing name`);
if (!n.summary) issues.push(`Node[${i}] '${n.id}' missing summary`);
if (!n.tags || !n.tags.length) issues.push(`Node[${i}] '${n.id}' missing tags`);
if (seen.has(n.id)) issues.push(`Duplicate node ID '${n.id}' at indices ${seen.get(n.id)} and ${i}`);
else seen.set(n.id, i);
nodeIds.add(n.id);
});
graph.edges.forEach((e, i) => {
if (!nodeIds.has(e.source)) issues.push(`Edge[${i}] source '${e.source}' not found`);
if (!nodeIds.has(e.target)) issues.push(`Edge[${i}] target '${e.target}' not found`);
});
const fileLevelTypes = new Set(['file', 'config', 'document', 'service', 'pipeline', 'table', 'schema', 'resource', 'endpoint']);
const fileNodes = graph.nodes.filter(n => fileLevelTypes.has(n.type)).map(n => n.id);
const assigned = new Map();
if (!Array.isArray(graph.layers)) { if (graph.layers) warnings.push('graph.layers is not an array'); graph.layers = []; }
if (!Array.isArray(graph.tour)) { if (graph.tour) warnings.push('graph.tour is not an array'); graph.tour = []; }
graph.layers.forEach(layer => {
(layer.nodeIds || []).forEach(id => {
if (!nodeIds.has(id)) issues.push(`Layer '${layer.id}' refs missing node '${id}'`);
if (assigned.has(id)) issues.push(`Node '${id}' appears in multiple layers`);
assigned.set(id, layer.id);
});
});
fileNodes.forEach(id => {
if (!assigned.has(id)) issues.push(`File node '${id}' not in any layer`);
});
graph.tour.forEach((step, i) => {
(step.nodeIds || []).forEach(id => {
if (!nodeIds.has(id)) issues.push(`Tour step[${i}] refs missing node '${id}'`);
});
});
const withEdges = new Set([
...graph.edges.map(e => e.source),
...graph.edges.map(e => e.target)
]);
graph.nodes.forEach(n => {
if (!withEdges.has(n.id)) warnings.push(`Node '${n.id}' has no edges (orphan)`);
});
const stats = {
totalNodes: graph.nodes.length,
totalEdges: graph.edges.length,
totalLayers: graph.layers.length,
tourSteps: graph.tour.length,
nodeTypes: graph.nodes.reduce((a, n) => { a[n.type] = (a[n.type]||0)+1; return a; }, {}),
edgeTypes: graph.edges.reduce((a, e) => { a[e.type] = (a[e.type]||0)+1; return a; }, {})
};
fs.writeFileSync(outputPath, JSON.stringify({ issues, warnings, stats }, null, 2));
process.exit(0);
} catch (err) { process.stderr.write(err.message + '\n'); process.exit(1); }执行该脚本:
bash
node $PROJECT_ROOT/.understand-anything/tmp/ua-inline-validate.cjs \
"$PROJECT_ROOT/.understand-anything/intermediate/assembled-graph.json" \
"$PROJECT_ROOT/.understand-anything/intermediate/review.json"若脚本非零退出,读取stderr,修复脚本并重试一次。
--review
path: full LLM reviewer
--review--review
路径:完整LLM评审
--reviewIf IS in , dispatch the LLM graph-reviewer subagent as follows:
--review$ARGUMENTSDispatch a subagent using the agent definition (at ). Append the following additional context:
graph-revieweragents/graph-reviewer.mdAdditional context from main session:Phase 1 scan results (file inventory):json[list of {path, sizeLines} from scan-result.json]Phase warnings/errors accumulated during analysis:
- [list any batch failures, skipped files, or warnings from Phases 2-5]
Cross-validate: every file in the scan inventory should have a corresponding node in the graph (node types may vary:,file:,config:,document:,service:,pipeline:,table:,schema:,resource:). Flag any missing files. Also flag any graph nodes whoseendpoint:doesn't appear in the scan inventory.filePath
Pass these parameters in the dispatch prompt:
Validate the knowledge graph at. Project root:$PROJECT_ROOT/.understand-anything/intermediate/assembled-graph.jsonRead the file and validate it for completeness and correctness. Write output to:$PROJECT_ROOT$PROJECT_ROOT/.understand-anything/intermediate/review.json
-
Read.
$PROJECT_ROOT/.understand-anything/intermediate/review.json -
Ifarray is non-empty:
issues- Review the list
issues - Apply automated fixes where possible:
- Remove edges with dangling references
- Fill missing required fields with sensible defaults (e.g., empty ->
tags, empty["untagged"]->summary)"No summary available" - Remove nodes with invalid types
- Re-run the final graph validation after automated fixes
- If critical issues remain after one fix attempt, save the graph anyway but include the warnings in the final report and mark dashboard auto-launch as skipped
- Review the
-
Ifarray is empty: Proceed to Phase 7.
issues
若中包含,按以下方式调度LLM图谱评审子代理:
$ARGUMENTS--review使用代理定义(位于)调度子代理。附加以下额外上下文:
graph-revieweragents/graph-reviewer.mdAdditional context from main session:Phase 1 scan results (file inventory):json[list of {path, sizeLines} from scan-result.json]Phase warnings/errors accumulated during analysis:
- [list any batch failures, skipped files, or warnings from Phases 2-5]
Cross-validate: every file in the scan inventory should have a corresponding node in the graph (node types may vary:,file:,config:,document:,service:,pipeline:,table:,schema:,resource:). Flag any missing files. Also flag any graph nodes whoseendpoint:doesn't appear in the scan inventory.filePath
在调度提示中传递以下参数:
Validate the knowledge graph at. Project root:$PROJECT_ROOT/.understand-anything/intermediate/assembled-graph.jsonRead the file and validate it for completeness and correctness. Write output to:$PROJECT_ROOT$PROJECT_ROOT/.understand-anything/intermediate/review.json
-
读取。
$PROJECT_ROOT/.understand-anything/intermediate/review.json -
若数组非空:
issues- 查看列表
issues - 尽可能应用自动修复:
- 删除带有悬空引用的边
- 用合理默认值填充缺失的必填字段(例如空->
tags,空["untagged"]->summary)"No summary available" - 删除类型无效的节点
- 自动修复后重新运行最终图谱验证
- 若一次修复后仍存在关键问题,仍保存图谱,但在最终报告中包含警告,并标记跳过仪表盘自动启动
- 查看
-
若数组为空:进入阶段7。
issues
Phase 7 — SAVE
阶段7 — 保存
Report to the user:
[Phase 7/7] Saving knowledge graph...-
Write the final knowledge graph to.
$PROJECT_ROOT/.understand-anything/knowledge-graph.json -
Generate structural fingerprints baseline. This creates the basis for future automatic incremental updates and must succeed beforeis written — otherwise auto-update sees a fresh commit hash with no fingerprints to compare against, classifies every file as STRUCTURAL, and escalates to
meta.jsonon every subsequent commit (issue #152).FULL_UPDATEWrite the input file:bashcat > $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>" } EOFThen invoke the bundled script (located next to this SKILL.md):bashnode <SKILL_DIR>/build-fingerprints.mjs \ $PROJECT_ROOT/.understand-anything/intermediate/fingerprint-input.jsonThe script usesexactly likeTreeSitterPlugin + PluginRegistry, so the baseline matches the comparison logic used during auto-updates.extract-structure.mjsIf the script exits non-zero or stdout does not include, abort Phase 7 and report the error. Do NOT proceed to step 3 (writingFingerprints baseline:).meta.json -
Write metadata to(only after step 2 succeeded):
$PROJECT_ROOT/.understand-anything/meta.jsonjson{ "lastAnalyzedAt": "<ISO 8601 timestamp>", "gitCommitHash": "<commit hash>", "version": "1.0.0", "analyzedFiles": <number of files analyzed> } -
Clean up intermediate files, preservingso future incremental runs can skip Phase 1 SCAN (see issue #293). We
scan-result.jsonscratch dirs into a timestampedmvinstead of.trash-*ing them directly — this avoids tripping destructive-action gates on hardened hosts (e.g. freshness-window checks) that flag deleting directories created moments earlier (see issue #301). The delayed-purge step in Phase 0 reclaims the space once the trash is older than 7 days.rm -rfbash# Preserve scan-result.json — Phase 1's deterministic file inventory. # Future incremental runs (Phase 2 compute-batches.mjs --changed-files=…) # need this inventory; without it, Phase 1 must re-dispatch and pay ~157k # tokens / ~158s per incremental run. TRASH="$PROJECT_ROOT/.understand-anything/.trash-$(date +%s)" mkdir -p "$TRASH" INTER="$PROJECT_ROOT/.understand-anything/intermediate" if [ -d "$INTER" ]; then # Move every entry except scan-result.json into the trash dir. find "$INTER" -mindepth 1 -maxdepth 1 -not -name 'scan-result.json' -exec mv {} "$TRASH/" \; 2>/dev/null || true fi mv "$PROJECT_ROOT/.understand-anything/tmp" "$TRASH/" 2>/dev/null || true -
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)
- Edges created (broken down by type)
- Layers identified (with names)
- Tour steps generated (count)
- Any warnings from the reviewer
- Path to the output file:
$PROJECT_ROOT/.understand-anything/knowledge-graph.json
-
Only automatically launch the dashboard by invoking theskill 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.
/understand-dashboard
向用户报告:
[Phase 7/7] Saving knowledge graph...-
将最终知识图谱写入。
$PROJECT_ROOT/.understand-anything/knowledge-graph.json -
生成结构指纹基线。这为未来自动增量更新创建基础,且必须在写入前成功 — 否则自动更新会看到新的提交哈希但无指纹可比较,将所有文件归类为STRUCTURAL,并在后续每次提交时升级为FULL_UPDATE(问题#152)。
meta.json写入输入文件:bashcat > $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然后调用附带脚本(位于本SKILL.md文件旁):bashnode <SKILL_DIR>/build-fingerprints.mjs \ $PROJECT_ROOT/.understand-anything/intermediate/fingerprint-input.json该脚本与完全一样使用extract-structure.mjs,因此基线与自动更新中使用的比较逻辑一致。TreeSitterPlugin + PluginRegistry若脚本非零退出或stdout不包含,终止阶段7并报告错误。不要继续执行步骤3(写入Fingerprints baseline:)。meta.json -
将元数据写入(仅在步骤2成功后):
$PROJECT_ROOT/.understand-anything/meta.jsonjson{ "lastAnalyzedAt": "<ISO 8601 timestamp>", "gitCommitHash": "<commit hash>", "version": "1.0.0", "analyzedFiles": <number of files analyzed> } -
清理中间文件,保留,以便未来增量运行可跳过阶段1扫描(见问题#293)。我们将临时目录移动到带时间戳的
scan-result.json而非直接.trash-*— 这可避免在加固主机上触发破坏性操作限制(例如freshness-window检查),这些限制会标记刚创建的目录(见问题#301)。阶段0中的延迟清理步骤会在垃圾目录超过7天后回收空间。rm -rfbash# Preserve scan-result.json — Phase 1's deterministic file inventory. # Future incremental runs (Phase 2 compute-batches.mjs --changed-files=…) # need this inventory; without it, Phase 1 must re-dispatch and pay ~157k # tokens / ~158s per incremental run. TRASH="$PROJECT_ROOT/.understand-anything/.trash-$(date +%s)" mkdir -p "$TRASH" INTER="$PROJECT_ROOT/.understand-anything/intermediate" if [ -d "$INTER" ]; then # Move every entry except scan-result.json into the trash dir. find "$INTER" -mindepth 1 -maxdepth 1 -not -name 'scan-result.json' -exec mv {} "$TRASH/" \; 2>/dev/null || true fi mv "$PROJECT_ROOT/.understand-anything/tmp" "$TRASH/" 2>/dev/null || true -
向用户报告摘要,包含:
- 项目名称和描述
- 已分析文件数 / 总文件数(按fileCategory细分:code, config, docs, infra, data, script, markup)
- 创建的节点数(按类型细分:file, function, class, config, document, service, table, endpoint, pipeline, schema, resource)
- 创建的边数(按类型细分)
- 识别的层(带名称)
- 生成的导览步骤数(计数)
- 评审器的任何警告
- 输出文件路径:
$PROJECT_ROOT/.understand-anything/knowledge-graph.json
-
仅在标准化/评审修复后通过最终图谱验证时,才通过调用技能自动启动仪表盘。 若未通过最终验证,报告图谱已保存但带有警告,且已跳过仪表盘启动。
/understand-dashboard
Error Handling
错误处理
- If any subagent dispatch fails, retry once with the same prompt plus additional context about the failure.
- Track all warnings and errors from each phase in a list. When using
$PHASE_WARNINGS, pass this list to the graph-reviewer in Phase 6. On the default path, include accumulated warnings in the Phase 7 final report.--review - If it fails a second time, skip that phase and continue with partial results.
- ALWAYS save partial results — a partial graph is better than no graph.
- Report any skipped phases or errors in the final summary so the user knows what happened.
- NEVER silently drop errors. Every failure must be visible in the final report.
- 若任何子代理调度失败,重试一次,使用相同提示并附加失败相关的额外上下文。
- 在列表中跟踪每个阶段的所有警告和错误。使用
$PHASE_WARNINGS时,将此列表传递给阶段6的图谱评审器。在默认路径中,将累积的警告包含到阶段7的最终报告中。--review - 若第二次仍失败,跳过该阶段并继续处理部分结果。
- 始终保存部分结果 — 部分图谱总比没有好。
- 在最终摘要中报告任何跳过的阶段或错误,以便用户了解情况。
- 永远不要静默丢弃错误。每个失败都必须在最终报告中可见。
Reference: KnowledgeGraph Schema
参考:KnowledgeGraph Schema
Node Types (13 total)
节点类型(共13种)
| Type | Description | ID Convention |
|---|---|---|
| Source code file | |
| Function or method | |
| Class, interface, or type | |
| Logical module or package | |
| Abstract concept or pattern | |
| Configuration file (YAML, JSON, TOML, env) | |
| Documentation file (Markdown, RST, TXT) | |
| Deployable service definition (Dockerfile, K8s) | |
| Database table or migration | |
| API endpoint or route definition | |
| CI/CD pipeline configuration | |
| Schema definition (GraphQL, Protobuf, Prisma) | |
| Infrastructure resource (Terraform, CloudFormation) | |
| 类型 | 描述 | ID约定 |
|---|---|---|
| 源代码文件 | |
| 函数或方法 | |
| 类、接口或类型 | |
| 逻辑模块或包 | |
| 抽象概念或模式 | |
| 配置文件(YAML, JSON, TOML, env) | |
| 文档文件(Markdown, RST, TXT) | |
| 可部署服务定义(Dockerfile, K8s) | |
| 数据库表或迁移 | |
| API端点或路由定义 | |
| CI/CD流水线配置 | |
| Schema定义(GraphQL, Protobuf, Prisma) | |
| 基础设施资源(Terraform, CloudFormation) | |
Edge Types (26 total)
边类型(共26种)
| Category | Types |
|---|---|
| Structural | |
| Behavioral | |
| Data flow | |
| Dependencies | |
| Semantic | |
| Infrastructure | |
| Schema/Data | |
| 类别 | 类型 |
|---|---|
| 结构型 | |
| 行为型 | |
| 数据流 | |
| 依赖型 | |
| 语义型 | |
| 基础设施型 | |
| Schema/数据型 | |
Edge Weight Conventions
边权重约定
| Edge Type | Weight |
|---|---|
| 1.0 |
| 0.9 |
| 0.8 |
| 0.7 |
| 0.6 |
| 0.5 |
| All others | 0.5 (default) |
| 边类型 | 权重 |
|---|---|
| 1.0 |
| 0.9 |
| 0.8 |
| 0.7 |
| 0.6 |
| 0.5 |
| 其他所有类型 | 0.5(默认) |