understand

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

/understand

/understand

Analyze the current codebase and produce a
knowledge-graph.json
file in
.understand-anything/
. This file powers the interactive dashboard for exploring the project's architecture.
分析当前代码库并在
.understand-anything/
目录下生成
knowledge-graph.json
文件。该文件为探索项目架构的交互式仪表盘提供数据支持。

Options

选项

  • $ARGUMENTS
    may contain:
    • --full
      — Force a full rebuild, ignoring any existing graph
    • --auto-update
      — Enable automatic graph updates on commit (writes
      autoUpdate: true
      to
      .understand-anything/config.json
      )
    • --no-auto-update
      — Disable automatic graph updates (writes
      autoUpdate: false
      to
      .understand-anything/config.json
      )
    • --review
      — Run full LLM graph-reviewer instead of inline deterministic validation
    • --language <lang>
      — Generate all textual content (summaries, descriptions, tags, titles, languageNotes, languageLesson) in the specified language. Accepts ISO 639-1 codes (
      zh
      ,
      ja
      ,
      ko
      ,
      en
      ,
      es
      ,
      fr
      ,
      de
      , etc.) or friendly names (
      chinese
      ,
      japanese
      ,
      korean
      ,
      english
      ,
      spanish
      , etc.). Locale variants supported:
      zh-TW
      ,
      zh-HK
      , etc. Defaults to
      en
      (English). Stores preference in
      .understand-anything/config.json
      for consistency across incremental updates.
    • A directory path (e.g.
      /path/to/repo
      or
      ../other-project
      ) — Analyze the given directory instead of the current working directory

  • $ARGUMENTS
    可包含以下内容:
    • --full
      — 强制完整重建,忽略现有图谱
    • --auto-update
      — 启用提交时自动更新图谱(将
      autoUpdate: true
      写入
      .understand-anything/config.json
    • --no-auto-update
      — 禁用自动更新图谱(将
      autoUpdate: false
      写入
      .understand-anything/config.json
    • --review
      — 运行完整的LLM图谱评审器,而非内联确定性验证
    • --language <lang>
      — 指定语言生成所有文本内容(摘要、描述、标签、标题、languageNotes、languageLesson)。支持ISO 639-1代码(
      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:
    Analyzing batch X/N (files: foo.ts, bar.ts, ...)
    (list up to 3 filenames, then
    ...
    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阶段,报告每个批次的索引和总数:
    Analyzing batch X/N (files: foo.ts, bar.ts, ...)
    (最多列出3个文件名,若更多则显示
    ...
  • 阶段完成:阶段结束时,简要确认:
    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.
  1. Resolve
    PROJECT_ROOT
    :
    • Parse
      $ARGUMENTS
      for a non-flag token (any argument that does not start with
      --
      ). 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
        test -d <path>
        ). If it does not exist or is not a directory, report an error to the user and STOP.
      • Set
        PROJECT_ROOT
        to the resolved absolute path.
    • If no directory path argument is found, set
      PROJECT_ROOT
      to the current working directory.
    • Worktree redirect. If
      PROJECT_ROOT
      is inside a git worktree (not the main checkout), redirect output to the main repository root. Worktrees managed by Claude Code are ephemeral —
      .understand-anything/
      written there is destroyed when the session ends, taking the knowledge graph with it (issue #133). Detect a worktree by comparing
      git rev-parse --git-dir
      against
      git rev-parse --git-common-dir
      ; in a normal checkout or submodule they resolve to the same path, in a worktree they differ and the parent of
      --git-common-dir
      is the main repo root.
      bash
      COMMON_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
      Set
      UNDERSTAND_NO_WORKTREE_REDIRECT=1
      if 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 import
      @understand-anything/core
      . On a fresh install
      packages/core/dist/
      does not exist yet — build once.
    Important: do not assume the plugin root is simply two directories above the skill path string. In many installations
    ~/.agents/skills/understand
    is 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.
    Resolve the plugin root like this:
    bash
    SKILL_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
    If
    pnpm
    is missing, report to the user: "Install Node.js ≥ 22 and pnpm ≥ 10, then re-run
    /understand
    ."
  2. Get the current git commit hash:
    bash
    git rev-parse HEAD
  3. 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
mv
s scratch dirs into
.trash-<timestamp>/
rather than
rm -rf
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:
bash
find $PROJECT_ROOT/.understand-anything/ -maxdepth 1 -type d -name '.trash-*' -mtime +7 -exec rm -rf {} + 2>/dev/null || true
3.5. Auto-update configuration: - If
--auto-update
is in
$ARGUMENTS
: write
{"autoUpdate": true}
to
$PROJECT_ROOT/.understand-anything/config.json
- If
--no-auto-update
is in
$ARGUMENTS
: write
{"autoUpdate": false}
to
$PROJECT_ROOT/.understand-anything/config.json
- These flags only set the config — analysis proceeds normally regardless.
3.6. Language configuration: - Parse
$ARGUMENTS
for
--language <lang>
flag. If found, extract the language code. - Language code normalization: Map friendly names to ISO codes: -
chinese
zh
,
japanese
ja
,
korean
ko
,
english
en
,
spanish
es
,
french
fr
,
german
de
,
portuguese
pt
,
russian
ru
,
arabic
ar
, etc. - Locale variants:
zh-TW
,
zh-HK
,
zh-CN
,
pt-BR
, etc. are preserved as-is. - If
--language
is NOT specified: - Stored preference wins. If
$PROJECT_ROOT/.understand-anything/config.json
has an
outputLanguage
field, set
$OUTPUT_LANGUAGE
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 (
$DETECTED_LANG
). If it is
en
or cannot be confidently determined, set
$OUTPUT_LANGUAGE=en
and proceed silently — no prompt (English users see no change). - If
$DETECTED_LANG
en
, confirm once before analyzing:
tell the user you detected
<language>
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
$DETECTED_LANG
, and print a one-line notice instead of blocking. - Persist the resolved
$OUTPUT_LANGUAGE
(including
en
) into
config.json
so it never re-prompts for this project. - If
--language
IS specified: - Update
$PROJECT_ROOT/.understand-anything/config.json
with the new language: merge
{"outputLanguage": "<lang>"}
into existing config. - Store as
$OUTPUT_LANGUAGE
for use throughout all phases. - Language directive template: Store as
$LANGUAGE_DIRECTIVE
:
markdown       > **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").       
  1. Check for subdomain knowledge graphs to merge: List all
    *knowledge-graph*.json
    files in
    $PROJECT_ROOT/.understand-anything/
    excluding
    knowledge-graph.json
    itself (e.g.
    frontend-knowledge-graph.json
    ,
    backend-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):
bash
python <SKILL_DIR>/merge-subdomain-graphs.py $PROJECT_ROOT
The script discovers subdomain graphs, loads the existing
knowledge-graph.json
as a base (if present), and merges everything into
knowledge-graph.json
(deduplicating nodes and edges). Report the merge summary to the user, then continue with the merged graph.
  1. Check if
    $PROJECT_ROOT/.understand-anything/knowledge-graph.json
    exists. If it does, read it.
  2. Check if
    $PROJECT_ROOT/.understand-anything/meta.json
    exists. If it does, read it to get
    gitCommitHash
    .
  3. Decision logic:
    ConditionAction
    --full
    flag in
    $ARGUMENTS
    Full analysis (all phases)
    No existing graph or metaFull analysis (all phases)
    --review
    flag + existing graph + unchanged commit hash
    Skip to Phase 6 (review-only — reuse existing assembled graph)
    Existing graph + unchanged commit hashAsk the user: "The graph is up to date at this commit. Would you like to: (a) run a full rebuild (
    --full
    ), (b) run the LLM graph reviewer (
    --review
    ), or (c) do nothing?" Then follow their choice. If they pick (c), STOP.
    Existing graph + changed filesIncremental update (re-analyze changed files only)
    Review-only path: Copy the existing
    knowledge-graph.json
    to
    $PROJECT_ROOT/.understand-anything/intermediate/assembled-graph.json
    , then jump directly to Phase 6 step 3.
    For incremental updates, get the changed file list:
    bash
    git diff <lastCommitHash>..HEAD --name-only
    If this returns no files, report "Graph is up to date" and STOP.
  4. Collect project context for subagent injection:
    • Read
      README.md
      (or
      README.rst
      ,
      readme.md
      ) from
      $PROJECT_ROOT
      if it exists. Store as
      $README_CONTENT
      (first 3000 characters).
    • Read the primary package manifest (
      package.json
      ,
      pyproject.toml
      ,
      Cargo.toml
      ,
      go.mod
      ,
      pom.xml
      ) if it exists. Store as
      $MANIFEST_CONTENT
      .
    • Capture the top-level directory tree:
      bash
      find $PROJECT_ROOT -maxdepth 2 -type f -not -path '*/node_modules/*' -not -path '*/.git/*' -not -path '*/dist/*' | head -100
      Store as
      $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
      ,
      index.php
      . Store first match as
      $ENTRY_POINT
      .

确定是运行完整分析还是增量更新。
  1. 解析
    PROJECT_ROOT
    • $ARGUMENTS
      中解析非标志型参数(任何不以
      --
      开头的参数)。若找到,将其视为目标目录路径。
      • 若路径为相对路径,基于当前工作目录解析为绝对路径。
      • 验证解析后的路径存在且为目录(执行
        test -d <path>
        )。若不存在或不是目录,向用户报告错误并终止
      • PROJECT_ROOT
        设置为解析后的绝对路径。
    • 若未找到目录路径参数,将
      PROJECT_ROOT
      设置为当前工作目录。
    • 工作树重定向:若
      PROJECT_ROOT
      位于git工作树中(而非主检出目录),将输出重定向到主仓库根目录。Claude Code管理的工作树是临时的 — 写入其中的
      .understand-anything/
      会在会话结束时销毁,导致知识图谱丢失(问题#133)。通过比较
      git rev-parse --git-dir
      git rev-parse --git-common-dir
      来检测工作树;在正常检出或子模块中,两者路径相同;在工作树中,两者路径不同,且
      --git-common-dir
      的父目录为主仓库根目录。
      bash
      COMMON_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
      若有意为每个工作树生成独立图谱(少见 — 大多数用户希望重定向),请设置
      UNDERSTAND_NO_WORKTREE_REDIRECT=1
      。 1.5. 确保插件已构建:后续阶段会调用导入
      @understand-anything/core
      的Node脚本。全新安装时
      packages/core/dist/
      不存在 — 需构建一次。
    重要提示:不要假设插件根目录就是技能路径字符串的上两级目录。在许多安装中,
    ~/.agents/skills/understand
    是指向真实插件检出目录的符号链接。优先使用运行时提供的插件根目录(针对Claude),然后回退到通用符号链接、技能符号链接解析和常见克隆安装路径。
    按以下方式解析插件根目录:
    bash
    SKILL_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
    若缺少
    pnpm
    ,向用户报告:"Install Node.js ≥ 22 and pnpm ≥ 10, then re-run
    /understand
    ."
  2. 获取当前git提交哈希:
    bash
    git rev-parse HEAD
  3. 创建中间和临时输出目录:
    bash
    mkdir -p $PROJECT_ROOT/.understand-anything/intermediate
    mkdir -p $PROJECT_ROOT/.understand-anything/tmp
3.1. 清理过期垃圾目录:第7阶段清理时会将临时目录移动到
.trash-<timestamp>/
而非直接
rm -rf
(见问题#301),这样可避免在加固主机上触发破坏性操作限制(例如 freshness-window 检查),这些限制会标记刚创建的目录。在此处回收超过7天的垃圾目录空间 — 此时任何新鲜度窗口检查都不再关心这些目录:
bash
find $PROJECT_ROOT/.understand-anything/ -maxdepth 1 -type d -name '.trash-*' -mtime +7 -exec rm -rf {} + 2>/dev/null || true
3.5. 自动更新配置: - 若
$ARGUMENTS
中包含
--auto-update
:将
{"autoUpdate": true}
写入
$PROJECT_ROOT/.understand-anything/config.json
- 若
$ARGUMENTS
中包含
--no-auto-update
:将
{"autoUpdate": false}
写入
$PROJECT_ROOT/.understand-anything/config.json
- 这些标志仅设置配置 — 分析会正常进行,不受影响。
3.6. 语言配置: - 从
$ARGUMENTS
中解析
--language <lang>
标志。若找到,提取语言代码。 - 语言代码标准化:将友好名称映射为ISO代码: -
chinese
zh
,
japanese
ja
,
korean
ko
,
english
en
,
spanish
es
,
french
fr
,
german
de
,
portuguese
pt
,
russian
ru
,
arabic
ar
等。 - 区域变体:
zh-TW
,
zh-HK
,
zh-CN
,
pt-BR
等保持原样。 - 若未指定
--language
: - 存储的偏好优先:若
$PROJECT_ROOT/.understand-anything/config.json
包含
outputLanguage
字段,将
$OUTPUT_LANGUAGE
设置为该值并跳过后续步骤。 - 否则进行检测(仅首次运行):推断用户对话的主要语言为ISO 639-1代码(
$DETECTED_LANG
)。若为
en
或无法确定,设置
$OUTPUT_LANGUAGE=en
并静默继续 — 无提示(英语用户无变化)。 -
$DETECTED_LANG
en
,分析前确认一次
:告知用户检测到
<language>
,询问是否以此语言生成所有内容;用户按Enter/输入"yes"接受,或输入其他语言代码/名称覆盖(通过上述友好名称映射标准化)。若为非交互式运行(无法回复),跳过等待,使用
$DETECTED_LANG
,并打印单行通知而非阻塞。 - 持久化:将解析后的
$OUTPUT_LANGUAGE
(包括
en
)存入
config.json
,以便该项目不再重复提示。 - 若指定了
--language
: - 更新
$PROJECT_ROOT/.understand-anything/config.json
中的语言:将
{"outputLanguage": "<lang>"}
合并到现有配置中。 - 将其存储为
$OUTPUT_LANGUAGE
,供所有阶段使用。 - 语言指令模板:存储为
$LANGUAGE_DIRECTIVE
markdown       > **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").       
  1. 检查待合并的子域知识图谱: 列出
    $PROJECT_ROOT/.understand-anything/
    中所有
    *knowledge-graph*.json
    文件,排除
    knowledge-graph.json
    本身(例如
    frontend-knowledge-graph.json
    ,
    backend-knowledge-graph.json
    )。若存在子域图谱,运行本技能附带的合并脚本(位于本SKILL.md文件旁 — 使用技能目录路径,而非项目根目录):
bash
python <SKILL_DIR>/merge-subdomain-graphs.py $PROJECT_ROOT
该脚本会发现子域图谱,加载现有
knowledge-graph.json
作为基础(若存在),并将所有内容合并到
knowledge-graph.json
中(去重节点和边)。向用户报告合并摘要,然后继续处理合并后的图谱。
  1. 检查
    $PROJECT_ROOT/.understand-anything/knowledge-graph.json
    是否存在。若存在,读取该文件。
  2. 检查
    $PROJECT_ROOT/.understand-anything/meta.json
    是否存在。若存在,读取其中的
    gitCommitHash
  3. 决策逻辑
    条件操作
    $ARGUMENTS
    中包含
    --full
    标志
    完整分析(所有阶段)
    无现有图谱或元数据完整分析(所有阶段)
    包含
    --review
    标志 + 存在现有图谱 + 提交哈希未变
    跳至阶段6(仅评审 — 复用现有已组装图谱)
    存在现有图谱 + 提交哈希未变询问用户:"The graph is up to date at this commit. Would you like to: (a) run a full rebuild (
    --full
    ), (b) run the LLM graph reviewer (
    --review
    ), or (c) do nothing?" 然后遵循用户选择。若用户选择(c),终止。
    存在现有图谱 + 文件已变更增量更新(仅重新分析变更文件)
    仅评审路径:将现有
    knowledge-graph.json
    复制到
    $PROJECT_ROOT/.understand-anything/intermediate/assembled-graph.json
    ,然后直接跳至阶段6第3步。
    对于增量更新,获取变更文件列表:
    bash
    git diff <lastCommitHash>..HEAD --name-only
    若返回无文件,报告"Graph is up to date"并终止。
  4. 收集子代理注入的项目上下文
    • $PROJECT_ROOT
      下存在
      README.md
      (或
      README.rst
      ,
      readme.md
      ),读取该文件。存储为
      $README_CONTENT
      (前3000字符)。
    • 若存在主包清单(
      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
.understandignore
file before scanning.
  1. Check if
    $PROJECT_ROOT/.understand-anything/.understandignore
    exists.
  2. If it does NOT exist, generate a starter file by invoking the bundled script (delegates to
    generateStarterIgnoreFile
    in
    @understand-anything/core
    , which reads
    .gitignore
    , deduplicates against built-in defaults, and emits language-grouped test-file suggestions). Pass
    $PLUGIN_ROOT
    via the env so the script doesn't have to re-derive it from its own path (which breaks for copied skill installs):
    bash
    PLUGIN_ROOT="$PLUGIN_ROOT" node <SKILL_DIR>/generate-ignore.mjs $PROJECT_ROOT
    • Report to the user:
      Generated
      .understand-anything/.understandignore
      with 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.
    • Wait for user confirmation before proceeding.
  3. If it already exists, report:
    Found
    .understand-anything/.understandignore
    . Review it if needed, then confirm to continue.
    • Wait for user confirmation before proceeding.
  4. After confirmation, proceed to Phase 1.

在扫描前设置并验证
.understandignore
文件。
  1. 检查
    $PROJECT_ROOT/.understand-anything/.understandignore
    是否存在。
  2. 若不存在,调用附带脚本生成初始文件(委托给
    @understand-anything/core
    中的
    generateStarterIgnoreFile
    ,该函数读取
    .gitignore
    ,与内置默认值去重,并按语言分组输出测试文件建议)。通过环境变量传递
    $PLUGIN_ROOT
    ,以便脚本无需从自身路径重新推导(这会在复制技能安装时失效):
    bash
    PLUGIN_ROOT="$PLUGIN_ROOT" node <SKILL_DIR>/generate-ignore.mjs $PROJECT_ROOT
    • 向用户报告:
      Generated
      .understand-anything/.understandignore
      with 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.
    • 等待用户确认后再继续
  3. 若已存在,报告:
    Found
    .understand-anything/.understandignore
    . Review it if needed, then confirm to continue.
    • 等待用户确认后再继续
  4. 确认后,进入阶段1。

Phase 1 — SCAN (Full analysis only)

阶段1 — 扫描(仅完整分析)

Report to the user:
[Phase 1/7] Scanning project files...
Dispatch a subagent using the
project-scanner
agent definition (at
agents/project-scanner.md
). Append the following additional context:
Additional context from main session:
Project README (first 3000 chars):
$README_CONTENT
Package manifest:
$MANIFEST_CONTENT
Use 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:
$PROJECT_ROOT
Write output to:
$PROJECT_ROOT/.understand-anything/intermediate/scan-result.json
After the subagent completes, read
$PROJECT_ROOT/.understand-anything/intermediate/scan-result.json
to get:
  • Project name, description
  • Languages, frameworks
  • File list with line counts and
    fileCategory
    per file (
    code
    ,
    config
    ,
    docs
    ,
    infra
    ,
    data
    ,
    script
    ,
    markup
    )
  • Complexity estimate
  • Import map (
    importMap
    ): pre-resolved project-internal imports per file (non-code files have empty arrays)
Store
importMap
in memory as
$IMPORT_MAP
for use in Phase 2 batch construction. Store the file list as
$FILE_LIST
with
fileCategory
metadata for use in Phase 2 batch construction.
Gate 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
filteredByIgnore > 0
, report:
Excluded {filteredByIgnore} files via
.understandignore
.

向用户报告:
[Phase 1/7] Scanning project files...
使用
project-scanner
代理定义(位于
agents/project-scanner.md
)调度子代理。附加以下额外上下文:
Additional context from main session:
Project README (first 3000 chars):
$README_CONTENT
Package manifest:
$MANIFEST_CONTENT
Use 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:
$PROJECT_ROOT
Write output to:
$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
    ):每个文件预解析的项目内部导入(非代码文件为空数组)
importMap
存储在内存中作为
$IMPORT_MAP
,供阶段2批处理构建使用。 将文件列表存储为带
fileCategory
元数据的
$FILE_LIST
,供阶段2批处理构建使用。
关卡检查:若文件数>100,告知用户并建议使用子目录参数限定范围。仅在用户确认或添加说明(可能耗时较长)后继续。
若扫描结果包含
filteredByIgnore > 0
,报告:
Excluded {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_ROOT
Reads
.understand-anything/intermediate/scan-result.json
, writes
.understand-anything/intermediate/batches.json
.
Capture stderr. Append any line starting with
Warning:
to
$PHASE_WARNINGS
for the final report.
If 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
.understand-anything/intermediate/batches.json
(produced by Phase 1.5). Iterate the
batches[]
array.
Report:
[Phase 2/7] Analyzing files — <totalFiles> files in <totalBatches> batches (up to 5 concurrent)...
For each batch, dispatch a subagent using the
file-analyzer
agent definition (at
agents/file-analyzer.md
). Run up to 5 subagents concurrently. Append the following additional context:
Additional context from main session:
Project:
<projectName>
<projectDescription>
Languages:
<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_ROOT
Project:
<projectName>
Languages:
<languages>
Batch:
<batchIndex>/<totalBatches>
Skill directory (for bundled scripts):
<SKILL_DIR>
Output: write to
$PROJECT_ROOT/.understand-anything/intermediate/batch-<batchIndex>.json
(single-file mode) OR
batch-<batchIndex>-part-<k>.json
(split mode, per Step B of your output protocol).
Pre-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 to
batchFiles
with all four fields —
path
,
language
,
sizeLines
,
fileCategory
):
  1. <path>
    (<sizeLines> lines, language:
    <language>
    , fileCategory:
    <fileCategory>
    )
  2. <path>
    (<sizeLines> lines, language:
    <language>
    , fileCategory:
    <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
batchIndex
using
batch-<batchIndex>.json
or
batch-<batchIndex>-part-<k>.json
. The merge script's regex (
batch-(\d+)(?:-part-(\d+))?\.json
) silently drops any other naming (e.g.,
batch-fused-8-13.json
,
batch-8-13.json
), losing every node and edge in that file. After each dispatch returns, verify each
batchIndex
in the dispatched input has a corresponding
batch-<batchIndex>.json
(or
batch-<batchIndex>-part-*.json
) on disk before proceeding to the next dispatch.
After 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_ROOT
This script reads all
batch-*.json
files (including
batch-<i>-part-<k>.json
produced by file-analyzers that split their output) from
$PROJECT_ROOT/.understand-anything/intermediate/
, then in one pass:
  • 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
    complex
    , etc.)
  • 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
tested_by
linker that canonicalizes test-coverage edges in two passes. Pass 1 walks LLM-emitted
tested_by
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
tested_by
edge get a
"tested"
tag. All resulting edges run
production → test
.
Output:
$PROJECT_ROOT/.understand-anything/intermediate/assembled-graph.json
Include the script's warnings in
$PHASE_WARNINGS
for the reviewer.
加载
.understand-anything/intermediate/batches.json
(阶段1.5生成)。遍历
batches[]
数组。
报告:
[Phase 2/7] Analyzing files — <totalFiles> files in <totalBatches> batches (up to 5 concurrent)...
对于每个批次,使用
file-analyzer
代理定义(位于
agents/file-analyzer.md
)调度子代理。最多5个子代理并发运行。附加以下额外上下文:
Additional context from main session:
Project:
<projectName>
<projectDescription>
Languages:
<languages from Phase 1>
$LANGUAGE_DIRECTIVE
调度提示模板(填充
batches.json[i]
中的批次特定值):
Analyze these files and produce GraphNode and GraphEdge objects. Project root:
$PROJECT_ROOT
Project:
<projectName>
Languages:
<languages>
Batch:
<batchIndex>/<totalBatches>
Skill directory (for bundled scripts):
<SKILL_DIR>
Output: write to
$PROJECT_ROOT/.understand-anything/intermediate/batch-<batchIndex>.json
(single-file mode) OR
batch-<batchIndex>-part-<k>.json
(split mode, per Step B of your output protocol).
Pre-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 to
batchFiles
with all four fields —
path
,
language
,
sizeLines
,
fileCategory
):
  1. <path>
    (<sizeLines> lines, language:
    <language>
    , fileCategory:
    <fileCategory>
    )
  2. <path>
    (<sizeLines> lines, language:
    <language>
    , fileCategory:
    <fileCategory>
    ) ...
输出命名按batchIndex — 不合并。若为提高token效率将多个小批次合并为单个file-analyzer调度,被调度的代理仍必须为每个原始
batchIndex
写入一个输出文件,使用
batch-<batchIndex>.json
batch-<batchIndex>-part-<k>.json
。合并脚本的正则表达式(
batch-(\d+)(?:-part-(\d+))?\.json
)会静默丢弃其他命名(例如
batch-fused-8-13.json
,
batch-8-13.json
),导致该文件中的所有节点和边丢失。每个调度返回后,在继续下一个调度前,验证调度输入中的每个
batchIndex
在磁盘上都有对应的
batch-<batchIndex>.json
(或
batch-<batchIndex>-part-*.json
)。
所有批次完成后,向用户报告:
Phase 2 complete. All <totalBatches> batches analyzed.
运行本技能附带的合并与标准化脚本(位于本SKILL.md文件旁 — 使用技能目录路径,而非项目根目录):
bash
python <SKILL_DIR>/merge-batch-graphs.py $PROJECT_ROOT
该脚本读取
$PROJECT_ROOT/.understand-anything/intermediate/
中的所有
batch-*.json
文件(包括file-analyzer拆分输出的
batch-<i>-part-<k>.json
),然后一次性执行以下操作:
  • 合并所有批次的节点和边
  • 标准化节点ID(去除双重前缀、项目名称前缀,添加缺失前缀)
  • 标准化复杂度值(
    low
    simple
    ,
    medium
    moderate
    ,
    high
    complex
    等)
  • 重写边引用以匹配修正后的节点ID
  • 按ID去重节点(保留最后出现的),按
    (source, target, type)
    去重边
  • 删除引用缺失节点的悬空边
  • 将所有修正和丢弃项记录到stderr
合并脚本还会运行
tested_by
链接器,分两批规范化测试覆盖边。第1批遍历LLM生成的
tested_by
边,就地翻转反向边;语义错误的边(测试↔测试、生产↔生产、孤立端点)会被丢弃。第2批补充路径约定配对。最终有
tested_by
边的生产节点会获得
"tested"
标签。所有生成的边均为
production → test
方向。
输出:
$PROJECT_ROOT/.understand-anything/intermediate/assembled-graph.json
将脚本的警告包含到
$PHASE_WARNINGS
中,供评审器使用。

Incremental 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.txt
Run compute-batches with
--changed-files
:
bash
node <SKILL_DIR>/compute-batches.mjs $PROJECT_ROOT \
  --changed-files=$PROJECT_ROOT/.understand-anything/tmp/changed-files.txt
This produces a
batches.json
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.
Then dispatch file-analyzer subagents per the same template as the full path.
After batches complete:
  1. Remove old nodes whose
    filePath
    matches any changed file from the existing graph
  2. Remove old edges whose
    source
    or
    target
    references a removed node
  3. Write the pruned existing nodes/edges as
    batch-existing.json
    in the intermediate directory
  4. Run the same merge script — it will combine
    batch-existing.json
    with the fresh
    batch-*.json
    files:
    bash
    python <SKILL_DIR>/merge-batch-graphs.py $PROJECT_ROOT

将变更文件列表(每行一个路径)写入临时文件:
bash
git diff <lastCommitHash>..HEAD --name-only > $PROJECT_ROOT/.understand-anything/tmp/changed-files.txt
使用
--changed-files
运行compute-batches:
bash
node <SKILL_DIR>/compute-batches.mjs $PROJECT_ROOT \
  --changed-files=$PROJECT_ROOT/.understand-anything/tmp/changed-files.txt
这会生成仅包含变更文件批次的
batches.json
,但neighborMap条目仍引用未变更文件(带全图谱batchIndex),因此仍可生成跨批次边。
然后按照完整路径的相同模板调度file-analyzer子代理。
批次完成后:
  1. 从现有图谱中删除
    filePath
    匹配任何变更文件的旧节点
  2. 删除引用已删除节点的旧边
  3. 将修剪后的现有节点/边写入中间目录的
    batch-existing.json
  4. 运行相同的合并脚本 — 它会将
    batch-existing.json
    与新的
    batch-*.json
    文件合并:
    bash
    python <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
assemble-reviewer
agent definition (at
agents/assemble-reviewer.md
).
Pass these parameters in the dispatch prompt:
Review the assembled graph at
$PROJECT_ROOT/.understand-anything/intermediate/assembled-graph.json
. Project root:
$PROJECT_ROOT
Batch files are at:
$PROJECT_ROOT/.understand-anything/intermediate/batch-*.json
Write review output to:
$PROJECT_ROOT/.understand-anything/intermediate/assemble-review.json
Merge 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
$PROJECT_ROOT/.understand-anything/intermediate/assemble-review.json
and add any notes to
$PHASE_WARNINGS
.

向用户报告:
[Phase 3/7] Reviewing assembled graph...
使用
assemble-reviewer
代理定义(位于
agents/assemble-reviewer.md
)调度子代理。
在调度提示中传递以下参数:
Review the assembled graph at
$PROJECT_ROOT/.understand-anything/intermediate/assembled-graph.json
. Project root:
$PROJECT_ROOT
Batch files are at:
$PROJECT_ROOT/.understand-anything/intermediate/batch-*.json
Write review output to:
$PROJECT_ROOT/.understand-anything/intermediate/assemble-review.json
Merge 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_WARNINGS

Phase 4 — ARCHITECTURE

阶段4 — 架构分析

Report to the user:
[Phase 4/7] Identifying architectural layers...
Build the combined prompt template:
  1. Use the
    architecture-analyzer
    agent definition (at
    agents/architecture-analyzer.md
    ).
  2. Language context injection: For each language detected in Phase 1 (e.g.,
    python
    ,
    markdown
    ,
    dockerfile
    ,
    yaml
    ,
    sql
    ,
    terraform
    ,
    graphql
    ,
    protobuf
    ,
    shell
    ,
    html
    ,
    css
    ), read the file at
    ./languages/<language-id>.md
    (e.g.,
    ./languages/python.md
    ,
    ./languages/dockerfile.md
    ) and append its content after the base template under a
    ## Language Context
    header. If the file does not exist for a detected language, skip it silently and continue. These files are in the
    languages/
    subdirectory next to this SKILL.md file. Include non-code language snippets — they provide edge patterns and summary styles for non-code files.
  3. Framework addendum injection: For each framework detected in Phase 1 (e.g.,
    Django
    ), read the file at
    ./frameworks/<framework-id-lowercase>.md
    (e.g.,
    ./frameworks/django.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/
    subdirectory next to this SKILL.md file.
  4. Output locale injection: If
    $OUTPUT_LANGUAGE
    is NOT
    en
    (English), read the locale guidance file at
    ./locales/<language-code>.md
    (e.g.,
    ./locales/zh.md
    ,
    ./locales/ja.md
    ,
    ./locales/ko.md
    ) and append its content after the framework addendums under a
    ## Output Language Guidelines
    header. 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
    $LANGUAGE_DIRECTIVE
    still applies. These files are in the
    locales/
    subdirectory next to this SKILL.md file.
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_TREE
Use 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:
$PROJECT_ROOT
Write output to:
$PROJECT_ROOT/.understand-anything/intermediate/layers.json
Project:
<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
$PROJECT_ROOT/.understand-anything/intermediate/layers.json
and normalize it into a final
layers
array. Apply these steps in order:
  1. Unwrap envelope: If the file contains
    { "layers": [...] }
    instead of a plain array, extract the inner array. (The prompt requests a plain array, but LLMs may still produce an envelope.)
  2. Rename legacy fields: If any layer object has a
    nodes
    field instead of
    nodeIds
    , rename
    nodes
    nodeIds
    . If
    nodes
    entries are objects with an
    id
    field rather than plain strings, extract just the
    id
    values into
    nodeIds
    .
  3. Synthesize missing IDs: If any layer is missing an
    id
    , generate one as
    layer:<kebab-case-name>
    .
  4. Convert file paths: If
    nodeIds
    entries are raw file paths without a known prefix (
    file:
    ,
    config:
    ,
    document:
    ,
    service:
    ,
    pipeline:
    ,
    table:
    ,
    schema:
    ,
    resource:
    ,
    endpoint:
    ), convert them to
    file:<relative-path>
    .
  5. Drop dangling refs: Remove any
    nodeIds
    entries that do not exist in the merged node set.
Each element of the final
layers
array MUST have this shape:
json
[
  {
    "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 (
id
,
name
,
description
,
nodeIds
) are required.
For 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...
构建组合提示模板
  1. 使用
    architecture-analyzer
    代理定义(位于
    agents/architecture-analyzer.md
    )。
  2. 语言上下文注入:对于阶段1检测到的每种语言(例如
    python
    ,
    markdown
    ,
    dockerfile
    ,
    yaml
    ,
    sql
    ,
    terraform
    ,
    graphql
    ,
    protobuf
    ,
    shell
    ,
    html
    ,
    css
    ),读取
    ./languages/<language-id>.md
    文件(例如
    ./languages/python.md
    ,
    ./languages/dockerfile.md
    ),并将其内容追加到基础模板的
    ## Language Context
    标题下。若检测到的语言对应的文件不存在,静默跳过并继续。这些文件位于本SKILL.md文件旁的
    languages/
    子目录中。包含非代码语言片段 — 它们为非代码文件提供边模式和摘要样式。
  3. 框架附录注入:对于阶段1检测到的每个框架(例如
    Django
    ),读取
    ./frameworks/<framework-id-lowercase>.md
    文件(例如
    ./frameworks/django.md
    ),并将其完整内容追加到语言上下文之后。若框架对应的文件不存在,静默跳过并继续。这些文件位于本SKILL.md文件旁的
    frameworks/
    子目录中。
  4. 输出区域注入:若
    $OUTPUT_LANGUAGE
    不是
    en
    (英语),读取
    ./locales/<language-code>.md
    区域指导文件(例如
    ./locales/zh.md
    ,
    ./locales/ja.md
    ,
    ./locales/ko.md
    ),并将其内容追加到框架附录之后的
    ## Output Language Guidelines
    标题下。这提供了特定语言的标签命名约定、摘要样式和层名称翻译指导。若指定语言的区域文件不存在,静默跳过 —
    $LANGUAGE_DIRECTIVE
    仍适用。这些文件位于本SKILL.md文件旁的
    locales/
    子目录中。
将语言/框架上下文和以下额外上下文追加到代理的提示中:
Additional context from main session:
Frameworks detected:
<frameworks from Phase 1>
Directory tree (top 2 levels):
$DIR_TREE
Use 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:
$PROJECT_ROOT
Write output to:
$PROJECT_ROOT/.understand-anything/intermediate/layers.json
Project:
<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.json
并将其标准化为最终的
layers
数组。按顺序应用以下步骤:
  1. 解包信封:若文件包含
    { "layers": [...] }
    而非纯数组,提取内部数组。(提示要求纯数组,但LLM仍可能生成信封结构。)
  2. 重命名遗留字段:若任何层对象有
    nodes
    字段而非
    nodeIds
    ,将
    nodes
    重命名为
    nodeIds
    。若
    nodes
    条目是带
    id
    字段的对象而非纯字符串,仅提取
    id
    值到
    nodeIds
  3. 合成缺失ID:若任何层缺少
    id
    ,生成
    layer:<kebab-case-name>
    格式的ID。
  4. 转换文件路径:若
    nodeIds
    条目是无前缀的原始文件路径(
    file:
    ,
    config:
    ,
    document:
    ,
    service:
    ,
    pipeline:
    ,
    table:
    ,
    schema:
    ,
    resource:
    ,
    endpoint:
    ),转换为
    file:<relative-path>
    格式。
  5. 删除悬空引用:移除任何不存在于合并节点集中的
    nodeIds
    条目。
最终
layers
数组的每个元素必须具有以下结构:
json
[
  {
    "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"]
  }
]
四个字段(
id
,
name
,
description
,
nodeIds
)均为必填项。
对于增量更新:始终在完整合并节点集上重新运行架构分析,因为文件变更可能导致层分配变化。
增量更新上下文:重新运行架构分析时,还需注入之前的层定义:
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
tour-builder
agent definition (at
agents/tour-builder.md
). Append the following additional context:
Additional context from main session:
Project README (first 3000 chars):
$README_CONTENT
Project entry point:
$ENTRY_POINT
Use 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:
$PROJECT_ROOT
Write output to:
$PROJECT_ROOT/.understand-anything/intermediate/tour.json
Project:
<projectName>
<projectDescription>
Languages:
<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
$PROJECT_ROOT/.understand-anything/intermediate/tour.json
and normalize it into a final
tour
array. Apply these steps in order:
  1. Unwrap envelope: If the file contains
    { "steps": [...] }
    instead of a plain array, extract the inner array. (The prompt requests a plain array, but LLMs may still produce an envelope.)
  2. Rename legacy fields: If any step has
    nodesToInspect
    instead of
    nodeIds
    , rename it →
    nodeIds
    . If any step has
    whyItMatters
    instead of
    description
    , rename it →
    description
    .
  3. Convert file paths: If
    nodeIds
    entries are raw file paths without a known prefix (
    file:
    ,
    config:
    ,
    document:
    ,
    service:
    ,
    pipeline:
    ,
    table:
    ,
    schema:
    ,
    resource:
    ,
    endpoint:
    ), convert them to
    file:<relative-path>
    .
  4. Drop dangling refs: Remove any
    nodeIds
    entries that do not exist in the merged node set.
  5. Sort by
    order
    before saving.
Each element of the final
tour
array MUST have this shape:
json
[
  {
    "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:
order
,
title
,
description
,
nodeIds
. Preserve optional
languageLesson
when present.

向用户报告:
[Phase 5/7] Building guided tour...
使用
tour-builder
代理定义(位于
agents/tour-builder.md
)调度子代理。附加以下额外上下文:
Additional context from main session:
Project README (first 3000 chars):
$README_CONTENT
Project entry point:
$ENTRY_POINT
Use 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:
$PROJECT_ROOT
Write output to:
$PROJECT_ROOT/.understand-anything/intermediate/tour.json
Project:
<projectName>
<projectDescription>
Languages:
<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.json
并将其标准化为最终的
tour
数组。按顺序应用以下步骤:
  1. 解包信封:若文件包含
    { "steps": [...] }
    而非纯数组,提取内部数组。(提示要求纯数组,但LLM仍可能生成信封结构。)
  2. 重命名遗留字段:若任何步骤有
    nodesToInspect
    而非
    nodeIds
    ,将其重命名为
    nodeIds
    。若任何步骤有
    whyItMatters
    而非
    description
    ,将其重命名为
    description
  3. 转换文件路径:若
    nodeIds
    条目是无前缀的原始文件路径(
    file:
    ,
    config:
    ,
    document:
    ,
    service:
    ,
    pipeline:
    ,
    table:
    ,
    schema:
    ,
    resource:
    ,
    endpoint:
    ),转换为
    file:<relative-path>
    格式。
  4. 删除悬空引用:移除任何不存在于合并节点集中的
    nodeIds
    条目。
  5. 排序:保存前按
    order
    排序。
最终
tour
数组的每个元素必须具有以下结构:
json
[
  {
    "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"]
  }
]
必填字段:
order
,
title
,
description
,
nodeIds
。若存在可选字段
languageLesson
,请保留。

Phase 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>]
}
  1. Before writing the assembled graph, validate that:
    • layers
      is an array of objects with these required fields:
      id
      ,
      name
      ,
      description
      ,
      nodeIds
    • tour
      is an array of objects with these required fields:
      order
      ,
      title
      ,
      description
      ,
      nodeIds
    • tour[*].languageLesson
      is allowed as an optional string field
    • Every
      layers[*].nodeIds
      entry exists in the merged node set
    • Every
      tour[*].nodeIds
      entry exists in the merged node set
    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.
  2. Write the assembled graph to
    $PROJECT_ROOT/.understand-anything/intermediate/assembled-graph.json
    .
  3. Check
    $ARGUMENTS
    for
    --review
    flag.
    Then run the appropriate validation path:

向用户报告:
[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>]
}
  1. 写入组装图谱前,验证:
    • layers
      是包含以下必填字段的对象数组:
      id
      ,
      name
      ,
      description
      ,
      nodeIds
    • tour
      是包含以下必填字段的对象数组:
      order
      ,
      title
      ,
      description
      ,
      nodeIds
    • tour[*].languageLesson
      是允许的可选字符串字段
    • 每个
      layers[*].nodeIds
      条目都存在于合并节点集中
    • 每个
      tour[*].nodeIds
      条目都存在于合并节点集中
    若验证失败,自动将图谱标准化并重写为上述结构后保存。若标准化后仍未通过最终验证,保存图谱并附带警告,但标记跳过仪表盘自动启动。
  2. 将组装图谱写入
    $PROJECT_ROOT/.understand-anything/intermediate/assembled-graph.json
  3. 检查
    $ARGUMENTS
    中是否有
    --review
    标志
    。然后运行相应的验证路径:

Default path (no
--review
): inline deterministic validation

默认路径(无
--review
):内联确定性验证

Write the following Node.js script to
$PROJECT_ROOT/.understand-anything/tmp/ua-inline-validate.cjs
:
javascript
#!/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.cjs
javascript
#!/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
路径:完整LLM评审

If
--review
IS in
$ARGUMENTS
, dispatch the LLM graph-reviewer subagent as follows:
Dispatch a subagent using the
graph-reviewer
agent definition (at
agents/graph-reviewer.md
). Append the following additional context:
Additional 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:
,
endpoint:
). Flag any missing files. Also flag any graph nodes whose
filePath
doesn't appear in the scan inventory.
Pass these parameters in the dispatch prompt:
Validate the knowledge graph at
$PROJECT_ROOT/.understand-anything/intermediate/assembled-graph.json
. Project root:
$PROJECT_ROOT
Read the file and validate it for completeness and correctness. Write output to:
$PROJECT_ROOT/.understand-anything/intermediate/review.json

  1. Read
    $PROJECT_ROOT/.understand-anything/intermediate/review.json
    .
  2. If
    issues
    array is non-empty:
    • Review the
      issues
      list
    • Apply automated fixes where possible:
      • Remove edges with dangling references
      • Fill missing required fields with sensible defaults (e.g., empty
        tags
        ->
        ["untagged"]
        , empty
        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
  3. If
    issues
    array is empty:
    Proceed to Phase 7.

$ARGUMENTS
中包含
--review
,按以下方式调度LLM图谱评审子代理:
使用
graph-reviewer
代理定义(位于
agents/graph-reviewer.md
)调度子代理。附加以下额外上下文:
Additional 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:
,
endpoint:
). Flag any missing files. Also flag any graph nodes whose
filePath
doesn't appear in the scan inventory.
在调度提示中传递以下参数:
Validate the knowledge graph at
$PROJECT_ROOT/.understand-anything/intermediate/assembled-graph.json
. Project root:
$PROJECT_ROOT
Read the file and validate it for completeness and correctness. Write output to:
$PROJECT_ROOT/.understand-anything/intermediate/review.json

  1. 读取
    $PROJECT_ROOT/.understand-anything/intermediate/review.json
  2. issues
    数组非空
    • 查看
      issues
      列表
    • 尽可能应用自动修复:
      • 删除带有悬空引用的边
      • 用合理默认值填充缺失的必填字段(例如空
        tags
        ->
        ["untagged"]
        ,空
        summary
        ->
        "No summary available"
      • 删除类型无效的节点
    • 自动修复后重新运行最终图谱验证
    • 若一次修复后仍存在关键问题,仍保存图谱,但在最终报告中包含警告,并标记跳过仪表盘自动启动
  3. issues
    数组为空
    :进入阶段7。

Phase 7 — SAVE

阶段7 — 保存

Report to the user:
[Phase 7/7] Saving knowledge graph...
  1. Write the final knowledge graph to
    $PROJECT_ROOT/.understand-anything/knowledge-graph.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>",
      "gitCommitHash": "<commit hash>",
      "version": "1.0.0",
      "analyzedFiles": <number of files analyzed>
    }
  4. Clean up intermediate files, preserving
    scan-result.json
    so future incremental runs can skip Phase 1 SCAN (see issue #293). We
    mv
    scratch dirs into a timestamped
    .trash-*
    instead of
    rm -rf
    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.
    bash
    # 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
  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)
    • 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
  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.

向用户报告:
[Phase 7/7] Saving knowledge graph...
  1. 将最终知识图谱写入
    $PROJECT_ROOT/.understand-anything/knowledge-graph.json
  2. 生成结构指纹基线。这为未来自动增量更新创建基础,且必须在写入
    meta.json
    前成功
    — 否则自动更新会看到新的提交哈希但无指纹可比较,将所有文件归类为STRUCTURAL,并在后续每次提交时升级为FULL_UPDATE(问题#152)。
    写入输入文件:
    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
    然后调用附带脚本(位于本SKILL.md文件旁):
    bash
    node <SKILL_DIR>/build-fingerprints.mjs \
      $PROJECT_ROOT/.understand-anything/intermediate/fingerprint-input.json
    该脚本与
    extract-structure.mjs
    完全一样使用
    TreeSitterPlugin + PluginRegistry
    ,因此基线与自动更新中使用的比较逻辑一致。
    若脚本非零退出或stdout不包含
    Fingerprints baseline:
    ,终止阶段7并报告错误。不要继续执行步骤3(写入
    meta.json
    )。
  3. 将元数据写入
    $PROJECT_ROOT/.understand-anything/meta.json
    (仅在步骤2成功后):
    json
    {
      "lastAnalyzedAt": "<ISO 8601 timestamp>",
      "gitCommitHash": "<commit hash>",
      "version": "1.0.0",
      "analyzedFiles": <number of files analyzed>
    }
  4. 清理中间文件,保留
    scan-result.json
    ,以便未来增量运行可跳过阶段1扫描(见问题#293)。我们将临时目录移动到带时间戳的
    .trash-*
    而非直接
    rm -rf
    — 这可避免在加固主机上触发破坏性操作限制(例如freshness-window检查),这些限制会标记刚创建的目录(见问题#301)。阶段0中的延迟清理步骤会在垃圾目录超过7天后回收空间。
    bash
    # 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
  5. 向用户报告摘要,包含:
    • 项目名称和描述
    • 已分析文件数 / 总文件数(按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
  6. 仅在标准化/评审修复后通过最终图谱验证时,才通过调用
    /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
    $PHASE_WARNINGS
    list. When using
    --review
    , pass this list to the graph-reviewer in Phase 6. On the default path, include accumulated warnings in the Phase 7 final report.
  • 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
    列表中跟踪每个阶段的所有警告和错误。使用
    --review
    时,将此列表传递给阶段6的图谱评审器。在默认路径中,将累积的警告包含到阶段7的最终报告中。
  • 若第二次仍失败,跳过该阶段并继续处理部分结果。
  • 始终保存部分结果 — 部分图谱总比没有好。
  • 在最终摘要中报告任何跳过的阶段或错误,以便用户了解情况。
  • 永远不要静默丢弃错误。每个失败都必须在最终报告中可见。

Reference: KnowledgeGraph Schema

参考:KnowledgeGraph Schema

Node Types (13 total)

节点类型(共13种)

TypeDescriptionID Convention
file
Source code file
file:<relative-path>
function
Function or method
function:<relative-path>:<name>
class
Class, interface, or type
class:<relative-path>:<name>
module
Logical module or package
module:<name>
concept
Abstract concept or pattern
concept:<name>
config
Configuration file (YAML, JSON, TOML, env)
config:<relative-path>
document
Documentation file (Markdown, RST, TXT)
document:<relative-path>
service
Deployable service definition (Dockerfile, K8s)
service:<relative-path>
table
Database table or migration
table:<relative-path>:<table-name>
endpoint
API endpoint or route definition
endpoint:<relative-path>:<endpoint-name>
pipeline
CI/CD pipeline configuration
pipeline:<relative-path>
schema
Schema definition (GraphQL, Protobuf, Prisma)
schema:<relative-path>
resource
Infrastructure resource (Terraform, CloudFormation)
resource:<relative-path>
类型描述ID约定
file
源代码文件
file:<relative-path>
function
函数或方法
function:<relative-path>:<name>
class
类、接口或类型
class:<relative-path>:<name>
module
逻辑模块或包
module:<name>
concept
抽象概念或模式
concept:<name>
config
配置文件(YAML, JSON, TOML, env)
config:<relative-path>
document
文档文件(Markdown, RST, TXT)
document:<relative-path>
service
可部署服务定义(Dockerfile, K8s)
service:<relative-path>
table
数据库表或迁移
table:<relative-path>:<table-name>
endpoint
API端点或路由定义
endpoint:<relative-path>:<endpoint-name>
pipeline
CI/CD流水线配置
pipeline:<relative-path>
schema
Schema定义(GraphQL, Protobuf, Prisma)
schema:<relative-path>
resource
基础设施资源(Terraform, CloudFormation)
resource:<relative-path>

Edge Types (26 total)

边类型(共26种)

CategoryTypes
Structural
imports
,
exports
,
contains
,
inherits
,
implements
Behavioral
calls
,
subscribes
,
publishes
,
middleware
Data flow
reads_from
,
writes_to
,
transforms
,
validates
Dependencies
depends_on
,
tested_by
,
configures
Semantic
related
,
similar_to
Infrastructure
deploys
,
serves
,
provisions
,
triggers
Schema/Data
migrates
,
documents
,
routes
,
defines_schema
类别类型
结构型
imports
,
exports
,
contains
,
inherits
,
implements
行为型
calls
,
subscribes
,
publishes
,
middleware
数据流
reads_from
,
writes_to
,
transforms
,
validates
依赖型
depends_on
,
tested_by
,
configures
语义型
related
,
similar_to
基础设施型
deploys
,
serves
,
provisions
,
triggers
Schema/数据型
migrates
,
documents
,
routes
,
defines_schema

Edge Weight Conventions

边权重约定

Edge TypeWeight
contains
1.0
inherits
,
implements
0.9
calls
,
exports
,
defines_schema
0.8
imports
,
deploys
,
migrates
0.7
depends_on
,
configures
,
triggers
0.6
tested_by
,
documents
,
provisions
,
serves
,
routes
0.5
All others0.5 (default)
边类型权重
contains
1.0
inherits
,
implements
0.9
calls
,
exports
,
defines_schema
0.8
imports
,
deploys
,
migrates
0.7
depends_on
,
configures
,
triggers
0.6
tested_by
,
documents
,
provisions
,
serves
,
routes
0.5
其他所有类型0.5(默认)