关于 orca-onboard
用于将代码库整理为可浏览文档的工作流技能,通过代码图谱、领域图谱、Wiki 和 HTML 文档进行引导式分析。分阶段执行并设有用户确认关卡,支持更新已有分析。
主要功能
- 01扫描代码并结合 Git 历史
- 02生成知识图谱与引导式导览
- 03构建领域图谱并检查覆盖率
- 04生成 Wiki 与 HTML 文档
- 05支持全量重建和增量更新
适用场景
- 01为新项目建立代码库概览
- 02梳理领域流程及其代码位置
- 03更新已有代码库分析与文档
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ rheinmir/ setup --skill orca-onboard安装后在 Claude Code 中输入/orca-onboard确认已加载。
附带文件assets/docs-site-skeleton.html
详细介绍
orca-onboard
🧭 调度后端 — 选择成本较低的 Agent/模型、并行运行多个 worker、opencode/orchestration 语法 → 请参阅 orca-dispatch-reference(唯一真实来源,请勿在此重复语法)。
通过提炼后的 understand-anything 流程接入代码库(图谱 + Git 历史,不使用插件),然后进行领域增强 + wiki + HTML。
WHAT
目的和背景
- 目的: 将任意代码库接入为可用知识:知识图谱(扫描 + Git 历史 + 批量分析 + 分层/导览,不使用插件)、领域图谱(领域 → 流程 → 步骤)、wiki
llmwiki/wiki/和 HTML 文档页面 — 推理由 Claude 主线程负责,机械性工作则调度给 opencode + DeepSeek Flash v4。 - 触发条件(何时使用):“onboard codebase”、“analyze codebase”、“knowledge graph”、“guided tour”、“/orca-onboard”。
- 非目标: 不安装 Understand-Anything 插件;不将推理(领域、架构、分层/导览)调度到 opencode/agy;不自行编写原始 HTML(始终通过 skeleton v2);不捏造文件路径或 file:line;不重复调度语法(真实来源为
orca-dispatch-reference)。
心智模型
Phase 0 pre-flight/bootstrap → Phase 0.5 GATE (draft + dispatch board → 用户审批) → Phase 1 graph (scan → git history → analyze → merge → layers+tour → validate → [code-graph index]) → Phase 2 domain graph → Phase 3 wiki → Phase 4 HTML (JSON → skeleton v2 → wiki-graph vector) → output report。Draft llmwiki/wiki/draft/orca/<DATE>-onboard-<slug>.md 是状态日志:每个 phase 状态为 pending → in-progress → done,恢复时从中读取。
输入和输出约定
| Field | Required? | 含义 | |
|---|---|---|---|
| In | <path> | 否(默认为 .) | 目标项目目录;@<draft> = 从现有 draft 恢复 |
| In | --full | 否 | 删除 .overstack/graph/ 后重建 |
| In | --update | 否 | 仅重新分析上次运行后有变化的文件(需要已有 graph + meta.json) |
| In | --language <lang> | 否 | 输出语言(vi、en、zh 等) |
| In | --skip-wiki | 否 | 跳过 wiki 生成 |
| In | 用户在 Phase 0.5 的审批 | 是 | dispatch board + propose 内容已输出到终端后 |
| In | opencode、skill docs-site-macos | 是 | 缺少时 → Phase 0 以状态码 1 退出,并附带安装命令 |
| Out | .overstack/graph/knowledge-graph.json、ONBOARDING.md、meta.json | 是 | Phase 1(Phase 1 最终 gate 检查前两个文件) |
| Out | .overstack/onboard/intermediate/domain-graph.json | 是 | Phase 2 |
| Out | .overstack/onboard/intermediate/surface.json + coverage.json | 是 | Phase 2 — 实际表面 @commit + 覆盖日志;输出报告会打印 已覆盖 N/M 个表面项(skill surface-coverage) |
| Out | llmwiki/wiki/(index、concepts、entities) | 是,除非使用 --skip-wiki | Phase 3;每个页面都有 ## Origin + index 行 |
| Out | llmwiki/html/onboarding-<slug>.html + llmwiki/html/wiki-graph.html | 是(vector 失败时放行) | Phase 4 |
| Out | draft 状态 + index/log 行 | 是 | 每个 phase 结束后更新 |
“完成” = draft 中 4 个 phase 均为 done,HTML 文件存在,并已记录输出报告。
规则与能力
- RULE-01 (MUST):Phase 0 完成前,DO NOT run Phase 0.5 or Phases 1–4。 顺序不可颠倒:Phase 0(验证
llmwiki/存在)→ Phase 0.5(先创建草稿,再询问用户)→ Phases 1–4。 - RULE-02 (MUST):缺少
llmwiki/→ 在 Phase 0 引导初始化,NEVER defer;引导初始化失败 → 完全 STOP,并报告错误。 - RULE-03 (MUST):每个 phase 结束后 → 立即更新 status draft,不要留到最后。
- RULE-04 (MUST):Dispatch board + 提议内容必须通过 bash 打印(在终端中可见),然后才能提出 gate 问题;草稿中的 Agent Task Assignment 行必须与 board 一致。
- RULE-05 (MUST):推理任务 MUST 留在 Claude 主线程中。NO reasoning to opencode/agy。 opencode + DeepSeek:仅用于填充模板、渲染 wiki 和 HTML。
- RULE-06 (MUST):先读再操作——每个 phase 都要在采取任何操作前读取
READ FIRST块中的所有输入。不要向用户询问文件中已有的信息。 - RULE-07 (MUST):Phase 1 之后,NO full
knowledge-graph.jsonreads——改用ONBOARDING.md。 - RULE-08 (MUST):仅使用真实文件路径——绝不捏造;domain graph 只引用真实的 file:line,并对照代码进行验证。
- RULE-12 (MUST):domain graph 的覆盖度必须通过与真实表面进行 diff 报告(
surface-coverage.py check),格式为N/M 项;绝不要报告 flow/文章数量,仿佛它们已经解释了整个应用。 - RULE-09 (MUST):Wiki:wikilink 格式为
[[page-name]];tour:5-15 个步骤,从项目概览开始。 - RULE-10 (MUST):Phase 4 绝不自行编写原始 HTML——始终通过 skeleton v2。
- RULE-11 (MUST):仅当
dep-health.py --json报告status == "ok"时,才索引 code-graph——在 config 中声明 ≠ 服务正常运行。 - Capabilities:读取目标仓库 + VCS 历史记录;运行本地脚本(bash/python);分派低成本 LLM 处理机械性工作;写入
.overstack/、llmwiki/;联网以引导初始化/安装依赖。
失败边界
- 缺少依赖项(opencode、docs-site-macos)或 project root 不存在 → blocked(exit 1 + 安装命令)。
--update但尚无 graph/meta.json→ blocked,提示先不带--update运行。- 引导初始化失败 → failed,完全 STOP。
- 用户未在 gate 批准 → cancelled,只保留草稿。
- Batch analyze 失败 2 次 → 放弃 batch,记录 PHASE_WARNINGS → partial graph(partial graph 优于 no graph)。
- Phase 1 gate 缺少
knowledge-graph.json/ONBOARDING.md→ Phase 1 failed。 - 没有 HTTP/CLI/event 入口点 → domains 为空(狭义 succeeded)。
- 绘制
wiki-graph.html失败 → fail-open,不阻止 onboard。
HOW
主要工作流
| 步骤 | 类型 | 输入 | 操作 | 输出/退出 | 失败/后续 |
|---|---|---|---|---|---|
| W01 | 确定性 | args, máy | 阶段 0 — 检查依赖、resume/root、更新模式,初始化 llmwiki+harness,创建目录、统计文件、探测 Agent | 环境变量 + files.txt | 缺少依赖/root → 退出码 1 |
| W02 | 效果 | W01 的结果 | 阶段 0.5 Gate — 先创建草稿,再建立索引+日志,打印调度板 + 提案,询问用户 | 草稿 pending,等待批准 | 未批准 → 已取消 |
| W03 | 确定性 | repo | 阶段 1 1.1 SCAN + 1.2 GIT HISTORY(bash)+ 1.3 ANALYZE(静态解析;opencode 仅用于增强)+ 1.4 MERGE(python) | assembled-graph.json | 批次失败 → 重试 1 次 → 放弃,记录到 PHASE_WARNINGS |
| W04 | 判断 | assembled graph、目录树、README | 1.5 LAYERS + TOUR(Claude 主线程) | layers + 5-15 步导览 | — |
| W05 | 确定性 | graph | 1.6 VALIDATE + SAVE + 阶段 1 最终关卡 | knowledge-graph.json、meta.json、ONBOARDING.md | 缺少文件 → 退出码 1 |
| W06 | 判断 | ONBOARDING.md、layers、surface.json | 阶段 2 实际覆盖面(surface-coverage.py scan)→ 领域增强(Claude)→ check 关卡 | domain-graph.json + coverage.json、N/M 项一行 | 没有入口点 → domains 为空;3 轮后 check 仍显示红色 → 部分完成,并附上尚未覆盖的列表 |
| W07 | 效果 | ONBOARDING + domain graph | 阶段 3 wiki(opencode,填充模板) | llmwiki/wiki/ | opencode 出错 → Claude 备用方案 |
| W08 | 效果 | ONBOARDING + domain graph + skeleton | 阶段 4 STEP A JSON → STEP B 填充骨架(python)→ STEP C vector | HTML + wiki-graph.html | skeleton 缺失 → 退出码 1;vector 出错 → 失败时继续开放 |
| W09 | 效果 | 结果 | 输出报告 + 更新状态 + 同步推送 | 最终草稿、索引/日志 | — |
各步骤详情(W01–W09 的事实来源):
触发条件
- “onboard codebase”、“analyze codebase”、“knowledge graph”、“guided tour”
选项
--full— 删除.overstack/graph/并重新构建--update— 增量更新:仅重新分析上次运行后发生变化的文件--language <lang>— 输出语言(vi、en、zh 等)--skip-wiki— 跳过 wiki 生成<path>— 目标目录(默认:.)
进度
[Phase 1/4] Graph generation (scan + git history + batch analyze + layers/tour)...
[Phase 2/4] Domain enrichment (Claude)...
[Phase 3/4] Wiki generation (opencode + DeepSeek Flash v4)...
[Phase 4/4] HTML docs (opencode + DeepSeek Flash v4)...
⚠️ 硬性规则
⛔ 在阶段 0 完成前,禁止运行阶段 0.5 或阶段 1–4。
严格按顺序执行,不得颠倒:
- 阶段 0 → 完成,确认
llmwiki/存在 ✅ - 阶段 0.5(Gate) → 先创建草稿文件,再询问用户
- 阶段 1–4 → 执行,每个阶段结束后立即更新状态
- 若缺少
llmwiki/→ 在阶段 0 初始化,绝不延后 - 初始化失败 → 立即完全停止并报告错误
- 每个阶段结束后 → 立即更新草稿状态,绝不留到最后
调度规则
TASK TYPE → AGENT → MODEL
──────────────────────────────────────────────────────
Scan + git history → bash/python direct → (no LLM)
Batch file analyze → python static parse → (no LLM — PRIMARY; opencode chỉ enrich)
Merge + validate → python inline → (no LLM)
Layers / tour → Claude main thread → Sonnet (never dispatch out)
Domain reasoning → Claude main thread → Sonnet (never dispatch out)
Wiki / HTML render → opencode → opencode/deepseek-v4-flash-free
推理任务必须留在 Claude 主线程中。不得将推理任务交给 opencode/agy。 opencode + DeepSeek:仅用于填充模板、渲染 wiki 和生成 HTML。
Agent 二进制文件
| Agent | 二进制文件 | 默认模型 | 检查命令 |
|---|---|---|---|
| Antigravity | agy | Claude(Sonnet/Opus) | agy --version |
| OpenCode | opencode | 可配置 | opencode --version |
| Kiro | kiro-cli | — | kiro-cli --version |
无需安装 Understand-Anything 插件。 阶段 1 直接在此技能中实现从 Understand-Anything 提炼出的方法(scan → batch analyze → merge → layers → tour → validate)——使用 bash/python + 纯 opencode prompt + Claude 主线程运行。无需在 agy/opencode 中安装任何额外组件。
OpenCode 搭配 DeepSeek Flash v4:
opencode run "$SPEC" --model opencode/deepseek-v4-flash-free < /dev/null \
# Fallback if flag unsupported:
echo "$SPEC" | opencode --model opencode/deepseek-v4-flash-free
# Last resort: Claude main thread
状态更新辅助工具
update_phase_status() {
local keyword="$1"
local status="$2"
[ -z "$DRAFT_FILE" ] && echo "[WARN] DRAFT_FILE unset" && return 0
local f=$(echo "$DRAFT_FILE" | tr '\\' '/')
[ ! -f "$f" ] && echo "[WARN] DRAFT_FILE not found: $f" && return 0
perl -i -pe "s/(\Q$keyword\E.*?)\| (?:pending|in-progress|done)/\$1| $status/g if /\Q$keyword\E/" "$f" \
|| sed -i "s/\(.*$(echo "$keyword" | sed 's/[^^]/[&]/g').*\)| [a-z-]*/\1| $status/" "$f" \
|| echo "[WARN] status update failed for: $keyword"
}
阶段 0 — 预检与设置
# --- 1. Dependency check (agy optional — Phase 1 dùng distilled pipeline, không cần agy) ---
MISSING=()
opencode --version >/dev/null 2>&1 || MISSING+=("opencode")
ls ~/.agents/skills/docs-site-macos/SKILL.md >/dev/null 2>&1 || MISSING+=("docs-site-macos skill")
if [ ${#MISSING[@]} -gt 0 ]; then
echo "❌ Missing dependencies:"
for dep in "${MISSING[@]}"; do echo " - $dep"; done
echo ""
echo "docs-site-macos: npx skills add rheinmir/setup@orca --skill docs-site-macos --global -y"
exit 1
fi
# --- 2. Resume Mode check & Project Root resolution ---
if [[ "$1" == @* ]]; then
DRAFT_FILE="${1#@}"
DRAFT_FILE=$(echo "$DRAFT_FILE" | tr '\\' '/')
RESUME_MODE=true
PROJECT_ROOT=$(grep "Project root:" "$DRAFT_FILE" | grep -oP '(?<=`)[^`]+(?=`)' | head -1)
PROJECT_ROOT=$(echo "$PROJECT_ROOT" | tr '\\' '/')
else
RESUME_MODE=false
PROJECT_ROOT=${1:-.}
PROJECT_ROOT=$(echo "$PROJECT_ROOT" | tr '\\' '/')
fi
test -d "$PROJECT_ROOT" || { echo "❌ Project root directory not found: $PROJECT_ROOT"; exit 1; }
# --- 3. Update Mode check ---
UPDATE_MODE=false
for arg in "$@"; do
[ "$arg" = "--update" ] && UPDATE_MODE=true
done
if [ "$UPDATE_MODE" = "true" ]; then
[ ! -f "$PROJECT_ROOT/.overstack/graph/knowledge-graph.json" ] \
&& echo "❌ --update requires existing graph. Run without --update first." && exit 1
[ ! -f "$PROJECT_ROOT/.overstack/graph/meta.json" ] \
&& echo "❌ --update requires meta.json. Run without --update first." && exit 1
echo "[update] Incremental mode — reading changed files from meta.json"
CHANGED_FILES=$(python3 -c "
import json, sys
meta = json.load(open('$PROJECT_ROOT/.overstack/graph/meta.json'))
changed = meta.get('changedFiles', meta.get('changed', []))
print('\n'.join(changed))
" 2>/dev/null || echo "")
if [ -z "$CHANGED_FILES" ]; then
echo "[update] no changedFiles in meta.json — falling back to full rebuild"
UPDATE_MODE=false
else
echo "[update] Changed files:"; echo "$CHANGED_FILES"
fi
fi
# --- 4. Bootstrap llmwiki + harness if missing ---
# Harness có 2 mode: GLOBAL (~/.claude/harness + hooks trong ~/.claude/settings.json,
# bảo vệ mọi project llmwiki trên máy này) và PER-PROJECT (harness/ commit vào repo,
# bảo vệ teammate). Global đủ cho máy dev cá nhân — chỉ WARN per-project khi cả 2 thiếu.
GLOBAL_HARNESS=false
[ -f "$HOME/.claude/harness/hooks/pre_tool_use.py" ] \
&& grep -q 'harness/hooks/pre_tool_use.py' "$HOME/.claude/settings.json" 2>/dev/null \
&& GLOBAL_HARNESS=true
echo "[harness] global=$GLOBAL_HARNESS project=$([ -d "$PROJECT_ROOT/harness" ] && echo yes || echo no)"
if [ -f "$PROJECT_ROOT/.llmwiki/.harness-stamp" ]; then
# Dự án layout DOT (cài bằng bootstrap v4): wiki ở .llmwiki/, engine ở GLOBAL — KHÔNG chép llmwiki/ trần, KHÔNG gọi đường per-project
# (install-harness.sh sẽ tự dừng rc 5). Thiếu/cũ thì cập nhật bằng: curl -fsSL …/harness/poc-vendor-neutral/bootstrap.sh | bash
echo "[orca-onboard] dự án dot-layout đã có overstack (.llmwiki/.harness-stamp) — bỏ qua bootstrap"
elif [ ! -d "$PROJECT_ROOT/llmwiki" ] || { [ "$GLOBAL_HARNESS" = "false" ] && [ ! -d "$PROJECT_ROOT/harness" ]; }; then
echo "[orca-onboard] bootstrapping llmwiki + harness..."
git clone https://github.com/rheinmir/setup.git /tmp/orca-llmwiki-bootstrap --depth 1 -b orca -q
[ ! -d "$PROJECT_ROOT/llmwiki" ] && cp -r /tmp/orca-llmwiki-bootstrap/llmwiki "$PROJECT_ROOT/llmwiki"
if [ "$GLOBAL_HARNESS" = "false" ] && [ ! -d "$PROJECT_ROOT/harness" ]; then
bash /tmp/orca-llmwiki-bootstrap/harness/scripts/install-harness.sh "$PROJECT_ROOT" \
&& echo "[orca-onboard] harness installed OK" \
|| echo "[WARN] harness install failed/denied — đường khuyên dùng: curl -fsSL https://raw.githubusercontent.com/Rheinmir/setup/orca/harness/poc-vendor-neutral/bootstrap.sh | bash (cài global + kéo orca-graph); hoặc 'install-harness.sh --global'"
fi
rm -rf /tmp/orca-llmwiki-bootstrap
echo "[orca-onboard] bootstrap done"
fi
# --- 5. Create dirs ---
mkdir -p $PROJECT_ROOT/.overstack/onboard/{intermediate,tmp}
mkdir -p $PROJECT_ROOT/llmwiki/wiki/draft/{cave,uiux,orca}
# --- 6. File count & availability check ---
git rev-parse HEAD 2>/dev/null > $PROJECT_ROOT/.overstack/onboard/tmp/commit.txt
git ls-files > $PROJECT_ROOT/.overstack/onboard/tmp/files.txt 2>/dev/null \
|| find $PROJECT_ROOT -type f \
! -path "*/.overstack/onboard/*" ! -path "*/llmwiki/*" ! -path "*/.git/*" \
> $PROJECT_ROOT/.overstack/onboard/tmp/files.txt
FILE_COUNT=$(wc -l < $PROJECT_ROOT/.overstack/onboard/tmp/files.txt | tr -d ' ')
# Probe agent availability
AGY_OK=$(agy --version 2>/dev/null && echo "✅ usable" || echo "❌ not found")
OC_OK=$(opencode --version 2>/dev/null && echo "✅ usable" || echo "❌ not found")
KIRO_OK=$(kiro-cli --version 2>/dev/null && echo "✅ usable" || echo "❌ not found")
echo "[pre-flight] $FILE_COUNT files | agy=$AGY_OK | opencode=$OC_OK"
# Parse statuses if in resume mode
parse_status() {
local keyword="$1"
awk '/## Agent Task Assignment/{p=1} p && /^## [^A]/{p=0} p' "$DRAFT_FILE" \
| grep "$keyword" | grep -oE 'pending|in-progress|done' | head -1
}
if [ "$RESUME_MODE" = true ]; then
PHASE1_STATUS=$(parse_status "Phase 1 —")
PHASE2_STATUS=$(parse_status "Phase 2 —")
PHASE3_STATUS=$(parse_status "Phase 3 —")
PHASE4_STATUS=$(parse_status "Phase 4 —")
echo "[Resume] P1=$PHASE1_STATUS P2=$PHASE2_STATUS P3=$PHASE3_STATUS P4=$PHASE4_STATUS"
fi
阶段 0.5 — 确认关卡
必需:先创建草稿文件,再询问用户。绝不能颠倒顺序。
DATE=$(date +%d%m%y)
PROJECT_SLUG=$(basename "$PROJECT_ROOT" | tr '[:upper:]' '[:lower:]' | tr ' ' '-')
DRAFT_FILE="$PROJECT_ROOT/llmwiki/wiki/draft/orca/${DATE}-onboard-${PROJECT_SLUG}.md"
# Graph = distilled pipeline (no plugin): scan/history bash → batch opencode → layers/tour Claude
OC_AVAIL=$(opencode --version 2>/dev/null && echo "yes" || echo "no")
if [ "$OC_AVAIL" = "yes" ]; then
AGENT_GRAPH="distilled pipeline: bash + opencode (batch) + Claude (layers/tour)"
else
AGENT_GRAPH="distilled pipeline: bash + Claude main thread (opencode unavailable)"
fi
cat > "$DRAFT_FILE" << EOF
---
type: draft
title: ${DATE}-onboard-${PROJECT_SLUG}
status: proposed
tags: [orca-onboard, output-report]
timestamp: $(date +%Y-%m-%d)
---
# ${DATE}-onboard-${PROJECT_SLUG}
**Status:** proposed
## Agent CLI Availability
| Agent | Binary | Status |
|-------|--------|--------|
| Antigravity | \`agy\` | $AGY_OK |
| OpenCode | \`opencode\` | $OC_OK |
| Kiro | \`kiro-cli\` | $KIRO_OK |
## Agent Task Assignment
| Task | Agent | Model | Status |
|------|-------|-------|--------|
| Phase 1 — Graph generation ($FILE_COUNT files) | $AGENT_GRAPH | DeepSeek Flash v4 + Sonnet | pending |
| Phase 2 — Domain enrichment | Claude main thread | Sonnet | pending |
| Phase 3 — Wiki generation | opencode | DeepSeek Flash v4 | pending |
| Phase 4 — HTML docs | opencode | DeepSeek Flash v4 | pending |
## What
Onboard \`$PROJECT_ROOT\` — understand-anything graph, domain enrichment, wiki, HTML.
## Output
- \`.overstack/graph/knowledge-graph.json\` (tree-sitter + Louvain)
- \`.overstack/graph/ONBOARDING.md\` (~20k tokens distilled)
- \`.overstack/onboard/intermediate/domain-graph.json\`
- \`llmwiki/wiki/\` (index, concepts, entities, architecture, tours)
- \`llmwiki/html/onboarding-${PROJECT_SLUG}.html\`
- \`llmwiki/html/wiki-graph.html\` (vector quan hệ concept↔code — STEP C)
## Files
| File | Action |
|------|--------|
| \`.overstack/graph/knowledge-graph.json\` | created by Phase 1 pipeline |
| \`.overstack/graph/ONBOARDING.md\` | created by Phase 1 pipeline |
| \`.overstack/onboard/intermediate/domain-graph.json\` | created by Claude |
| \`llmwiki/wiki/index.md\` | created/modified |
| \`llmwiki/wiki/concepts/architecture.md\` | created |
| \`llmwiki/wiki/concepts/*.md\` | created |
| \`llmwiki/wiki/entities/*.md\` | created |
| \`llmwiki/wiki/concepts/onboarding-tour.md\` | created |
| \`llmwiki/html/onboarding-${PROJECT_SLUG}.html\` | created |
| \`llmwiki/html/wiki-graph.html\` | created by STEP C (vector) |
## Notes
- Invoked via: \`/orca-onboard\` skill
- Project root: \`$PROJECT_ROOT\`
- Files tracked: $FILE_COUNT
- Reasoning phases in Claude main thread — NOT dispatched to cheap models
- Mechanical phases: opencode + DeepSeek Flash v4
## Cost Estimate
| Phase | Agent | Est. tokens | Est. cost |
|-------|-------|-------------|-----------|
| Phase 1 (graph) | bash + opencode batches + Claude layers/tour | ~1.5M (DeepSeek) + ~50k (Sonnet) | ~\$0.50 |
| Phase 2 (domain) | Claude Sonnet | ~50k | ~\$0.50 |
| Phase 3 (wiki) | DeepSeek Flash | ~100k | ~\$0.02 |
| Phase 4 (HTML) | DeepSeek Flash | ~50k | ~\$0.01 |
## Origin
- **Draft:** \`wiki/draft/orca/${DATE}-onboard-${PROJECT_SLUG}.md\`
- **Commit:** _(filled by verify-before-commit)_
- **Date promoted:** _(filled by verify-before-commit)_
EOF
# Update wiki index + log
echo "| [${DATE}-onboard-${PROJECT_SLUG}](draft/orca/${DATE}-onboard-${PROJECT_SLUG}.md) | draft | $(date +%Y-%m-%d) |" >> "$PROJECT_ROOT/llmwiki/wiki/index.md"
echo "## $(date +%Y-%m-%d) — orca-onboard — onboard-${PROJECT_SLUG}" >> "$PROJECT_ROOT/llmwiki/wiki/log.md"
echo "DRAFT_FILE=$DRAFT_FILE"
# --- Terminal dispatch board (REQUIRED — must print in terminal BEFORE the gate question) ---
P1_NOTE="(distilled pipeline: 1.1-1.2 bash, 1.3 opencode batches, 1.4/1.6 python, 1.5 Claude)"
echo ""
echo "==================== ORCA-ONBOARD DISPATCH BOARD ===================="
echo " Project : $PROJECT_SLUG"
echo " Files : $FILE_COUNT"
echo " Draft : $DRAFT_FILE"
echo "----------------------------------------------------------------------"
printf " %-7s %-22s %-24s %-20s %s\n" "PHASE" "TASK" "AGENT" "MODEL" "EST.COST"
printf " %-7s %-22s %-24s %-20s %s\n" "1" "Graph generation" "bash+opencode+Claude" "DeepSeek+Sonnet" "~\$0.50"
printf " %-7s %-22s %-24s %-20s %s\n" "2" "Domain enrichment" "Claude main thread" "Sonnet" "~\$0.50"
printf " %-7s %-22s %-24s %-20s %s\n" "3" "Wiki generation" "opencode" "DeepSeek Flash v4" "~\$0.02"
printf " %-7s %-22s %-24s %-20s %s\n" "4" "HTML docs" "opencode" "DeepSeek Flash v4" "~\$0.01"
[ -n "$P1_NOTE" ] && echo " Phase 1 $P1_NOTE"
echo "======================================================================"
# --- Print propose file path + content (REQUIRED — user reviews this before approving) ---
echo ""
echo "PROPOSE FILE: $DRAFT_FILE"
echo "----------------------------------------------------------------------"
cat "$DRAFT_FILE"
⛔ 必须通过 bash 打印调度板和上方的提案文件内容(在终端中可见)——仅在聊天中或仅在草稿文件中重述都不够。用户会根据此调度板和提案来决定是否编排。草稿中的 Agent 任务分配行必须与调度板一致(例如,如果因文件数 >100 将阶段 1 改派给 opencode,草稿必须写 opencode,而不是 agy)。
继续之前,请先征求用户确认。
阶段 1 — 图谱生成(精简版 understand-anything — KHÔNG cần plugin)
**流程:**扫描(bash)→ git 历史(bash)→ 批量分析(opencode + DeepSeek)→ 合并(python)→ 层级 + 导览(Claude 主线程)→ 验证 + 保存(python + Claude)。方法从 Understand-Anything 精简而来;所有提示词/模式都在下方——不调用 /understand,不安装插件。
必需输出:
.overstack/graph/knowledge-graph.json(nodes、edges、layers、tour).overstack/graph/ONBOARDING.md(约 20k tokens 的精简内容).overstack/graph/meta.json
跳过检查:RESUME_MODE=true + PHASE1_STATUS=done → 跳到阶段 2。
决策表:
| 图谱是否存在 | 标记 | 操作 |
|---|---|---|
| 否 | (无) | 完整流程 |
| 是 | (无) | 跳过阶段 1,使用现有图谱 |
| 是 | --update | 仅重新分析 git diff <meta.gitCommitHash>..HEAD --name-only 中的文件,并合并到现有图谱 |
| 是 | --full | 删除 .overstack/graph/,执行完整流程 |
1.1 扫描(bash — 无 LLM)
mkdir -p "$PROJECT_ROOT/.overstack/graph" "$PROJECT_ROOT/.overstack/onboard/tmp"
git -C "$PROJECT_ROOT" ls-files > "$PROJECT_ROOT/.overstack/onboard/tmp/files.txt"
# Loại file nhị phân/lock/vendor; phân loại theo extension: code/config/docs/infra/data/script
# Đọc context: README (3000 chars đầu), manifest (package.json/pyproject.toml/Cargo.toml/go.mod), dir tree 2 cấp
# Detect entry point: src/index.ts, main.py, app.py, main.go, src/main.rs, cmd/*/main.go, manage.py, __main__.py...
1.2 GIT 历史(bash — 无 LLM)
Git 历史信号可丰富图谱——这是静态分析无法提供的:
# Churn — file nóng (hay sửa nhất, 365 ngày)
git -C "$PROJECT_ROOT" log --since="365 days ago" --name-only --pretty=format: \
| grep -v '^$' | sort | uniq -c | sort -rn | head -40 > "$PROJECT_ROOT/.overstack/onboard/tmp/churn.txt"
# Recent — file vừa đổi (30 ngày)
git -C "$PROJECT_ROOT" log --since="30 days ago" --name-only --pretty=format: | grep -v '^$' | sort -u \
> "$PROJECT_ROOT/.overstack/onboard/tmp/recent.txt"
# Commit subjects gần nhất — chủ đề đang phát triển
git -C "$PROJECT_ROOT" log --since="90 days ago" --pretty=format:'%s' | head -50 \
> "$PROJECT_ROOT/.overstack/onboard/tmp/recent-subjects.txt"
# Co-change — python đọc `git log --name-only` theo commit, đếm cặp file đổi cùng nhau, ngưỡng ≥3
应用方式:将 churn 排名前列的节点标记为 hot;将 recent.txt 中的节点标记为 recent;将共同变更 ≥3 次的文件对添加为 related 边(weight 0.5);导览 + ONBOARDING 优先介绍 hot 文件;recent-subjects 用于「正在处理的内容」部分。
1.3 分析 — 首选:静态解析(0 token)· 仅用 opencode 补充信息
默认使用 python 静态解析(run 120626-zca-bridge 的经验——既省成本又确定):
使用正则表达式解析 import/export ... from + require() → 解析相对路径 → 添加 imports 边;
test/**.test.ts → tested_by;summary = 文件开头的注释 / exports 列表 / md 标题;
根据扩展名确定 type(file/config/document)。Imports 属于机械性工作——LLM 并不比正则表达式更准确。
仅当复杂文件需要更丰富的语义 summary 时才调用 opencode(可选增强):
将文件列表分成每批约 25 个文件(把相关文件放在同一批:Dockerfile+compose、migrations、CI configs、docs)。每批调用:
opencode run "$BATCH_SPEC" --model opencode/deepseek-v4-flash-free < /dev/null
# ⚠ KHÔNG thêm --dangerously-skip-permissions: Claude Code auto-mode classifier sẽ DENY lệnh dispatch.
# opencode treo chờ permission / bị chặn → dùng STATIC PARSE fallback bên dưới (ưu tiên mặc định).
BATCH_SPEC(注入完整上下文——DeepSeek 不会自行读取分配列表以外的任何内容):
分析项目 (,语言:)中的这些文件。为每个文件生成 GraphNode
{id, type, name, filePath, summary(1-2 句), tags[]},并根据文件内容中可见的 imports/calls/configures/documents 关系生成 GraphEdges{source, target, type, weight}。读取列出的每个文件。只将有效 JSON{"nodes":[...], "edges":[...]}写入.overstack/onboard/tmp/batch-<i>.json。ID 约定:file:<relpath>、config:<relpath>、document:<relpath>。文件:<包含行数的列表>
最多并行处理 5 个批次。批次失败 → 重试 1 次 → 再次失败则跳过,并记录到 PHASE_WARNINGS(部分图谱胜过没有图谱)。如果 opencode 不可用 → 由 Claude 主线程自行分析批次。
1.4 合并 + 规范化(内联 python — 无 LLM)
Claude 编写脚本 .overstack/onboard/tmp/merge.py(逻辑精简自 Understand-Anything 的 merge-batch-graphs.py):读取所有 batch-*.json → 合并 nodes/edges → 规范化 ID 前缀(移除重复前缀,补上缺失前缀)→ 按 id 去重 node(保留最后一个),按 (source,target,type) 去重 edge → 丢弃悬空 edge(写入 stderr 日志)→ 根据共同变更(1.2)添加 related 边 → 写入 assembled-graph.json。
1.5 层 + 导览(Claude 主线程——推理,不派发)
根据 assembled-graph、目录树和 README:
- 层:
[{id: "layer:<kebab>", name, description, nodeIds[]}]——每个文件级节点恰好属于一个层;目录结构是最有力的依据。 - 导览: 5-15 个步骤
[{order, title, description, nodeIds[]}]——第 1 步始终是项目概览(README),然后遵循入口点,优先介绍hot文件;讲述 README 所讲的同一个故事,但通过真实代码呈现。
1.6 验证 + 保存(python + Claude)
进行确定性验证(脚本):不重复节点 ID、不存在悬空边、每个文件节点恰好属于一个层、导览中的 nodeIds 均存在,并统计孤立节点。能自动修复的就自动修复(删除悬空边、填充默认值),其余情况记录警告——仍然保存部分图谱。
写入:
knowledge-graph.json:{version, project: {name, languages, frameworks, description, analyzedAt, gitCommitHash}, nodes, edges, layers, tour}meta.json:{lastAnalyzedAt, gitCommitHash, analyzedFiles}ONBOARDING.md(由 Claude 提炼,约 20k 个 token):概览、各层 + 描述、关键流程、重要文件 + 原因(变更频率)、“正在处理的内容”(recent-subjects)、导览叙述、统计信息。
最后:删除 batch-*.json,报告摘要(已分析的文件数、按类型统计的节点/边、层数、导览步骤数、警告)。
Schema(从 Understand-Anything 提炼)
- 节点类型:
file, function, class, module, concept, config, document, service, table, endpoint, pipeline, schema, resource——ID:<type>:<relpath>[:<name>] - 主要边类型: imports、exports、contains、inherits、implements、calls、reads_from、writes_to、depends_on、tested_by、configures、related、documents、deploys、triggers、routes、defines_schema
- 权重: contains 1.0 · inherits/implements 0.9 · calls/exports 0.8 · imports 0.7 · depends_on/configures 0.6 · 其余 0.5
# Gate cuối Phase 1
[ ! -f "$PROJECT_ROOT/.overstack/graph/knowledge-graph.json" ] \
&& echo "❌ Phase 1 FAIL: knowledge-graph.json missing" && exit 1
[ ! -f "$PROJECT_ROOT/.overstack/graph/ONBOARDING.md" ] \
&& echo "❌ Phase 1 FAIL: ONBOARDING.md missing" && exit 1
echo "✅ Phase 1 done"
# update_phase_status "Phase 1 —" "done"
1.7 CODE-GRAPH MCP 索引(可选——仅当 mcpServers 中存在 code-graph 服务时)
用于按精确行号查询代码关系的层,补充上述提炼出的图谱:search_symbols、get_callers/callees 可即时检查影响。code-graph 服务(~/.claude.json → mcpServers)会在每个仓库的 <repo>/.graph-agent/index.db 中写入一个 SQLite 数据库。
**何时运行:**项目包含真实代码子模块/仓库(Go/TS/Py……),并且 code-graph 确实可用——通过 python3 harness/scripts/dep-health.py --json 检查,并要求 status == "ok";不要只看它是否在 ~/.claude.json 中声明。配置中有声明 ≠ 服务正在运行:0 字节数据库、缺少 symbols 表的数据库,或已停止的服务,仍会显示为“已声明”(2026-07-20 的教训——code-graph 连续数周不可用,但每个会话仍被引导去使用它)。状态为 degraded/absent 时跳过此步骤。
# 1. Index từng repo code (auto-detect root từ .git/go.mod/package.json).
# Gọi MCP tool reindex_repo(<abs path repo>, force=true) cho MỖI repo code.
# Lưu ý: code có thể nằm ở SUBDIR của submodule (vd <sub>/<svc>/go.mod chứ không phải <sub>/).
# → trỏ vào thư mục chứa go.mod/package.json, không phải submodule root rỗng.
# 2. reindex_repo tự thêm repo vào registry ~/.graph-agent/repos.txt
# → server --watch re-watch sau restart (durability).
# 3. Verify: get_stats() phải thấy symbols thật (>0), không phải stub.
echo "[1.7] code-graph: gọi reindex_repo cho mỗi repo code có manifest"
持久性保障: SessionStart hook code_graph_keeper.py(随 install-harness.sh 一起安装)会在仓库从注册表中掉出时自动重新注册,并提醒尚未建立索引的仓库。故障时放行,若项目未使用 code-graph,则为空操作——判断条件是存在可用的 .graph-agent/index.db(可打开且包含 symbols/files 表),而不仅仅是存在该文件:空数据库曾导致 keeper 错误写入注册表,并彻底压掉“尚未建立索引”的警告。不要每次编辑都重新索引(watcher 已负责处理,防抖时间为 2 秒)。之后编写一个 wiki 概念页面 concepts/code-graph-nav.md(介绍工具用法 + caller trace 示例),让团队了解如何使用。
后续会自动保持最新:编辑文件 → watcher 约 2 秒后重新索引。除非是新克隆仓库,或服务器关闭期间发生了大量更改,否则无需手动重新索引。
阶段 2 — 领域扩充
Agent: Claude 主线程(不分发) | 模型: Sonnet
先阅读(按顺序,在编写任何内容之前):
.overstack/graph/ONBOARDING.md— 完整阅读(约 20k 个 token).overstack/graph/knowledge-graph.json— 仅阅读layers数组和带有entry-point标签的节点(⛔ 不要读取整个文件,以免上下文溢出)- 如果
UPDATE_MODE=true:读取现有的.overstack/onboard/intermediate/domain-graph.json(合并,不要覆盖)
执行:
0. 先覆盖真实表面,再选择流程(技能 surface-coverage,GH#191 — 曾有 18 个流程被报告为覆盖了一个包含 62 个页面和 37 个模块的应用):
python3 "$FDK_TOOLS/surface-coverage.py" scan "$PROJECT_ROOT" [--modules '<thư mục module API>/*'] --out .overstack/onboard/intermediate/surface.json
其中 FDK_TOOLS=$([ -f fdk/tools/surface-coverage.py ] && echo fdk/tools || echo "$HOME/.claude/harness/fdk/tools")(框架仓库 → 使用仓库内版本;客户机器 → 使用全局引擎)。对于非基于文件的框架,编写脚本列出路由,然后合并到同一文件。
- 从入口点识别 HTTP 端点 / CLI 命令 / 事件 / cron 作业 —
surface.json中的每个条目都必须由某个流程覆盖,或有理由地排除 - 逆向分析:入口点 → 流程(过程)→ 步骤(操作 @ 文件:行号)
- 构建
domain → flow → step层级结构 - 写入
.overstack/onboard/intermediate/domain-graph.json+ 覆盖记录.overstack/onboard/intermediate/coverage.json({"commit", "items": {"page:/x": {"by": ["flow:…"]} | {"exclude": "理由"}}}) - 门禁:
python3 "$FDK_TOOLS/surface-coverage.py" check surface.json coverage.json --flows domain-graph.json --root "$PROJECT_ROOT"— 返回码为 1(存在未覆盖条目、流程 ID 错误、file:line错误)→ 下一轮编码时补齐,最多 3 轮;如果仍未通过,则阶段 2 = 部分完成。最终报告(W09)必须逐字输出N/M 个表面条目已覆盖,K 个已排除+ 未覆盖条目列表 — 绝不能只报告流程数量,仿佛已经覆盖完整。
领域图谱架构:
{
"domains": [
{
"id": "domain:order-management",
"name": "Order Management",
"flows": [
{
"id": "flow:create-order",
"name": "Create Order",
"steps": [
{ "order": 0.1, "id": "step:validate-input", "file": "src/orders/validator.ts", "line": 42 },
{ "order": 0.2, "id": "step:check-inventory", "file": "src/inventory/checker.ts", "line": 15 }
]
}
]
}
]
}
输出: .overstack/onboard/intermediate/domain-graph.json
跳过检查: RESUME_MODE=true + PHASE2_STATUS=done → 跳至阶段 3。
if [ "$RESUME_MODE" != "true" ] || [ "$PHASE2_STATUS" != "done" ]; then
update_phase_status "Phase 2 —" "in-progress"
# Claude main thread — no dispatch
if [ "$UPDATE_MODE" = "true" ] && [ -n "$CHANGED_FILES" ]; then
echo "[Phase 2] Update mode: re-enrich domain steps for changed files only"
# Read existing domain-graph.json
# Filter steps with filePath in CHANGED_FILES
# Re-analyze those steps from ONBOARDING.md
# Merge back into domain-graph.json (keep unchanged steps)
else
echo "[Phase 2] Full domain enrichment from ONBOARDING.md"
# Read ONBOARDING.md + layers from knowledge-graph.json, write new domain-graph.json
fi
update_phase_status "Phase 2 —" "done"
fi
阶段 3 — Wiki 生成
Agent: opencode | 模型: opencode/deepseek-v4-flash-free
先阅读(在分发前注入 SPEC — DeepSeek 除非注入内容,否则不会读取文件):
.overstack/graph/ONBOARDING.md— 完整阅读.overstack/onboard/intermediate/domain-graph.json— 完整阅读- 如果
UPDATE_MODE=true:在llmwiki/wiki/中 grep 对CHANGED_FILES的引用 → 找出过时页面
- ⛔ 不要读取
.overstack/graph/knowledge-graph.json— 文件太大,会导致上下文溢出
执行: 根据提炼后的内容填写 Wiki 模板。不进行推理 — 机械地填写模板。
输出结构:
llmwiki/wiki/
├── index.md
├── concepts/ ← architecture.md, message-sync/flows, onboarding-tour.md (R5: KHÔNG tạo folder architecture/ hay tours/ — validator folder_structure chặn)
└── entities/ ← domain entities + project-structure.md
# Mọi trang PHẢI có '## Origin' (R2) và row trong index.md (R3)
跳过检查: RESUME_MODE=true + PHASE3_STATUS=done → 跳至阶段 4。
if [ "$RESUME_MODE" != "true" ] || [ "$PHASE3_STATUS" != "done" ]; then
update_phase_status "Phase 3 —" "in-progress"
if [ "$UPDATE_MODE" = "true" ] && [ -n "$CHANGED_FILES" ]; then
STALE_PAGES=$(grep -rl "$CHANGED_FILES" "$PROJECT_ROOT/llmwiki/wiki/" 2>/dev/null | tr '\n' ' ')
if [ -n "$STALE_PAGES" ]; then
echo "[Phase 3] Stale wiki pages: $STALE_PAGES"
SPEC="Update these wiki pages based on changes in ONBOARDING.md: $STALE_PAGES
Changed source files: $CHANGED_FILES
Do NOT regenerate unchanged pages. Preserve existing content in unchanged pages."
else
echo "[Phase 3] No stale wiki pages — skipping"
update_phase_status "Phase 3 —" "done"
fi
else
SPEC="Generate wiki pages from .overstack/graph/ONBOARDING.md and .overstack/onboard/intermediate/domain-graph.json.
Create: llmwiki/wiki/index.md, llmwiki/wiki/concepts/*.md (architecture.md + flow pages + onboarding-tour.md),
llmwiki/wiki/entities/*.md (domain entities + project-structure.md).
R5: chỉ được ghi vào concepts/ hoặc entities/. R2: mọi trang có '## Origin'. R3: cập nhật index.md.
Use wikilink format [[page-name]]. Do NOT read knowledge-graph.json directly."
fi
opencode run "$SPEC" --model opencode/deepseek-v4-flash-free < /dev/null \
|| echo "[WARN] opencode unavailable — Claude main thread fallback for wiki"
update_phase_status "Phase 3 —" "done"
fi
阶段 4 — HTML 文档(skeleton v2 + JSON island)
Agent: 组装 JSON = opencode(机械操作)/ Claude 备用 · 填充 skeleton = python(不使用 LLM)
工作原理(与旧版不同 — 防止内容简陋 + UX 中断): 不要让模型生成原始 HTML
(DeepSeek 会自行编造 CSS/JS → “镜像”丢失、点击无响应,输出行数在 173–816 之间且不稳定)。
改为:模型只生成一个 JSON 对象,并遵循 schema;UI 由冻结的 skeleton v2
(assets/docs-site-skeleton.html,已应用 /docs-site-macos 设计系统)渲染。
Skeleton = 固定导航栏(Overview/What & How/Architecture/Guided Tour/Modules/Docker;如果 JSON 中 what 和 how 均缺失,则隐藏 What & How)+ 固定侧边栏 + 折叠 + 滚动侦测 + 旅游主从视图 + 可拖动图表;Mono 项目会自动隐藏 Modules/Docker。子内容(layer/tour/module/docker)由 JSON 数据驱动。
先阅读(丰富来源 — 注入 SPEC;DeepSeek 不会自行读取文件):
.overstack/graph/ONBOARDING.md— 全文(overview、hot files、flows、tour narrative).overstack/onboard/intermediate/domain-graph.json— domain→flow→step(tour + lifecycle)knowledge-graph.json中仅包含layers+tour+ node entry-point 的部分(⛔ 不要读取全文 — 会超出上下文容量) ⛔ 不要使用单薄的 wiki md 作为主要来源(这正是旧版内容简陋的原因)。
跳过检查: RESUME_MODE=true + PHASE4_STATUS=done → 跳过。
if [ "$RESUME_MODE" != "true" ] || [ "$PHASE4_STATUS" != "done" ]; then
update_phase_status "Phase 4 —" "in-progress"
# --- locate skeleton v2 ---
SKELETON=$(ls ~/.agents/skills/orca-onboard/assets/docs-site-skeleton.html \
~/.claude/skills/orca-onboard/assets/docs-site-skeleton.html \
"$PROJECT_ROOT/skills/orca-onboard/assets/docs-site-skeleton.html" 2>/dev/null | head -1)
[ -z "$SKELETON" ] && echo "❌ Phase 4: skeleton v2 not found" && exit 1
# --- detect Docker (multi-service → tab Docker; mono → docker:null, Modules:[]) ---
DKFILE=$(ls "$PROJECT_ROOT"/docker-compose.y*ml "$PROJECT_ROOT"/compose.y*ml 2>/dev/null | head -1)
N_SVC=0; [ -n "$DKFILE" ] && N_SVC=$(grep -cE '^ [a-zA-Z0-9_-]+:' "$DKFILE" 2>/dev/null || echo 0)
echo "[Phase 4] skeleton=$SKELETON | compose=$DKFILE | services=$N_SVC"
# --- STEP A: assemble ONBOARD_JSON (model emit JSON ONLY → file) ---
JSON_OUT="$PROJECT_ROOT/.overstack/onboard/tmp/onboard.json"
SPEC="Đọc .overstack/graph/ONBOARDING.md + .overstack/onboard/intermediate/domain-graph.json.
Phát ra MỘT object JSON (KHÔNG markdown, KHÔNG giải thích) theo ĐÚNG schema trong header của
$SKELETON, ghi vào $JSON_OUT. Yêu cầu chất lượng:
- project: name/subtitle/about thật; stack[] + versions[] từ manifest; stats[] (files, services, layers, tour steps).
- project.what = 2–4 câu LỜI THƯỜNG: dự án làm gì, cho ai, giải quyết vấn đề gì (không thuật ngữ nội bộ); project.how[] = 3–7 bước {t,d} kể luồng chạy đầu→cuối bằng lời người — hiện thành mục What & How NGAY SAU Overview.
- architecture.layers[] = layers thật; architecture.diagram.nodes[] (toạ độ trong ~760×rộng, rect≥70×30) + edges[] theo luồng.
- tour[] 5–15 bước, MỖI bước GIÀU: role(1-2 câu), file+line THẬT, in[]/out[], hot(churn), narr — KHÔNG 1–2 dòng.
- modules[] = mỗi container/image (đọc $DKFILE): name,img,meta(port/env/volume),life[[read|proc|write,desc]],arch. Repo mono ($N_SVC≤1) → modules:[].
- docker = {strategy, cmd[[text,c|p|]], table[[svc,img,cmd/port,deps]], order[]} nếu có compose; mono → docker:null."
opencode run "$SPEC" --model opencode/deepseek-v4-flash-free < /dev/null 2>/dev/null \
|| echo "[Phase 4] opencode unavailable — Claude main thread tự assemble $JSON_OUT theo schema skeleton"
# Nếu opencode treo/trống → Claude main thread đọc 2 nguồn trên, tự viết $JSON_OUT (reasoning OK ở đây).
# --- STEP B: fill skeleton (python, no LLM) — thay {{TITLE}} + {{ONBOARD_JSON}} ---
OUT="$PROJECT_ROOT/llmwiki/html/onboarding-${PROJECT_SLUG}.html"
mkdir -p "$PROJECT_ROOT/llmwiki/html"
python3 - "$SKELETON" "$JSON_OUT" "$OUT" <<'PY'
import json,sys,html
sk=open(sys.argv[1]).read(); data=json.load(open(sys.argv[2]))
name=(data.get("project") or {}).get("name","Project")
out=sk.replace("{{TITLE}}",html.escape(name)).replace("{{ONBOARD_JSON}}",json.dumps(data,ensure_ascii=False))
assert "{{ONBOARD_JSON}}" not in out and "{{TITLE}}" not in out, "token chưa thay hết"
open(sys.argv[3],"w").write(out); print("✅ filled →",sys.argv[3],len(out),"bytes")
PY
ls "$OUT" && echo "✅ Phase 4 done" || echo "❌ Phase 4 FAIL: HTML not found"
update_phase_status "Phase 4 —" "done"
fi
备用方案: opencode 崩溃/挂起 → Claude 主线程自行读取
ONBOARDING.md+domain-graph.json,按 schema 编写$JSON_OUT(这是组装数据,可以由 Claude 完成), 然后运行 STEP B。绝不要自行编写原始 HTML — 始终使用 skeleton v2。
STEP C — 绘制矢量图 wiki-graph.html(Phase 3 刚生成的 wiki → 现在已有概念可供绘制):
Onboard 会生成 wiki,但不会自行绘制关系矢量图 — 在此处立即绘制,这样仅代码项目完成
onboard 后就会同时拥有矢量图(正好解决关键瓶颈:build-wiki-graph.py 会读取 llmwiki/wiki/*.md + 代码,不会直接读取代码来生成概念图)。采用 fail-open,不阻止 onboard。
# engine: repo-local → global (cùng thứ tự hook Stop resolve)
WG=$(ls "$PROJECT_ROOT/fdk/tools/build-wiki-graph.py" \
"$HOME/.claude/harness/fdk/tools/build-wiki-graph.py" 2>/dev/null | head -1)
if [ -n "$WG" ] && [ -d "$PROJECT_ROOT/llmwiki/wiki" ]; then
ALSO=""; [ -d "$PROJECT_ROOT/fdk/wiki" ] && ALSO="--also fdk/wiki"
( cd "$PROJECT_ROOT" && python3 "$WG" llmwiki/wiki $ALSO --code-root . ) \
&& echo "✅ vector: llmwiki/html/wiki-graph.html (concept↔code)" \
|| echo "⚠️ wiki-graph vẽ lỗi — không chặn onboard (Stop hook sẽ vẽ khi wiki/code đổi)"
else
echo "⚠️ bỏ vẽ vector — thiếu engine build-wiki-graph.py (cài harness global) hoặc chưa có llmwiki/wiki"
fi
完成后:
lsof -ti :8765 >/dev/null 2>&1 || nohup npx serve -p 8765 > /tmp/serve.log 2>&1 &
echo "→ http://localhost:8765/llmwiki/html/onboarding-${PROJECT_SLUG}.html"
规则
- 先读取,再操作 — 每个阶段在采取任何操作前,都要读取
READ FIRST块中的所有输入。不要询问用户文件中已有的信息。 - Phase 1 之后禁止读取完整的
knowledge-graph.json— 改为使用ONBOARDING.md - 只能使用真实文件路径 — 绝不捏造
- Domain graph:只引用真实的 file:line,并对照代码验证
- Wiki:wikilink 格式
[[page-name]] - Tour:5–15 个步骤,从项目概览开始
- 推理任务(domain、architecture):只能由 Claude 主线程执行
错误处理
- Phase 1 批次失败(opencode)→ 重试 1 次,再次失败 → Claude 主线程自行分析该批次;写入 PHASE_WARNINGS
- Phase 1 失败(没有 graph)→ 检查 batch-*.json 是否存在;如果 merge 脚本出错 → 阅读 stderr,修复后重新运行
- Phase 2 domain 为空 → 未找到 HTTP/CLI/event 入口点;写入空的 domains 数组
- Phase 3 失败 → 检查 opencode 模型配置;备用方案为 Claude 主线程
- Phase 4 失败 → 备用方案:在 Claude 中直接调用 docs-site-macos skill
输出报告
所有阶段完成后,将提案草稿写入 wiki。
1. 文件名: DDMMYY-<ten>.md — 今天的日期 + 2–4 个 kebab-case 摘要词
2. 写入 llmwiki/wiki/draft/orca/DDMMYY-<ten>.md:
---
type: draft
title: DDMMYY-<ten>
status: proposed
tags: [orca-onboard, output-report]
timestamp: YYYY-MM-DD
---
# DDMMYY-<ten>
**Status:** proposed
## Agent Task Assignment
| Task | Agent | Model | Status |
|------|-------|-------|--------|
| Phase 1 — Graph generation | agy /understand | Claude (agy) | done |
| Phase 2 — Domain enrichment | Claude main | Sonnet | done |
| Phase 3 — Wiki generation | opencode | DeepSeek Flash v4 | done |
| Phase 4 — HTML docs | opencode | DeepSeek Flash v4 | done |
## What
<One sentence>
## Output
<Key artefacts>
## Files
| File | Action |
|------|--------|
| `.overstack/graph/knowledge-graph.json` | created |
| `llmwiki/wiki/index.md` | created/modified |
| `llmwiki/html/onboarding-<slug>.html` | created |
## Notes
- Invoked via: `/orca-onboard` skill
## Origin
- **Draft:** `wiki/draft/orca/DDMMYY-<ten>.md`
- **Commit:** _(filled by verify-before-commit)_
- **Date promoted:** _(filled by verify-before-commit)_
3. 更新 wiki 索引和日志:
llmwiki/wiki/index.md— 追加:| [DDMMYY-<ten>](draft/orca/DDMMYY-<ten>.md) | draft | YYYY-MM-DD |llmwiki/wiki/log.md— 追加:## YYYY-MM-DD — orca-onboard — <ten>
4. 更新状态并同步推送 — 必须执行:
- 更新草稿文件中的 Status 列,使其反映实际运行状态
- 克隆
rheinmir/setup的orca分支,复制更新后的 SKILL.md 并推送:git clone [email protected]:rheinmir/setup.git /tmp/rheinmir-setup-sync -b orca --depth 1 cp ~/.agents/skills/orca-onboard/SKILL.md /tmp/rheinmir-setup-sync/skills/orca-onboard/SKILL.md cd /tmp/rheinmir-setup-sync git add . git commit -m "skill: orca-onboard — wrap understand-anything, DeepSeek mechanical dispatch" git push origin orca rm -rf /tmp/rheinmir-setup-sync
只有当 skill 未生成任何产物且未做出任何决策时,才跳过输出报告。
输出报告
所有阶段完成后,将提案草稿写入 wiki。
1. 文件名: DDMMYY-<ten>.md — 当天日期 + 2-4 个 kebab-case 摘要词
2. 写入 llmwiki/wiki/draft/orca/DDMMYY-<ten>.md:
---
type: draft
status: proposed
tags: [orca-onboard, output-report]
proposed: YYYY-MM-DD
---
# DDMMYY-<ten>
## Agent Task Assignment
| Task | Agent | Model | Status |
|------|-------|-------|--------|
| Phase 1 — Graph generation | agy /understand | Claude (agy) | done |
| Phase 2 — Domain enrichment | Claude main | Sonnet | done |
| Phase 3 — Wiki generation | opencode | DeepSeek Flash v4 | done |
| Phase 4 — HTML docs | opencode | DeepSeek Flash v4 | done |
## What
<One sentence>
## Output
<Key artefacts>
## Files
| File | Action |
|------|--------|
| `.overstack/graph/knowledge-graph.json` | created |
| `llmwiki/wiki/index.md` | created/modified |
| `llmwiki/html/onboarding-<slug>.html` | created |
## Notes
- Invoked via: `/orca-onboard` skill
## Origin
- **Draft:** `wiki/draft/orca/DDMMYY-<ten>.md`
- **Commit:** _(filled by verify-before-commit)_
- **Date promoted:** _(filled by verify-before-commit)_
3. 更新 wiki 索引和日志:
llmwiki/wiki/index.md— 追加:| [DDMMYY-<ten>](draft/orca/DDMMYY-<ten>.md) | draft | YYYY-MM-DD |llmwiki/wiki/log.md— 追加:## YYYY-MM-DD — orca-onboard — <ten>
4. 更新状态并同步推送 — 必须执行:
- 更新草稿文件中的 Status 列,反映实际运行情况
- 克隆
rheinmir/setup的orca分支,复制更新后的 SKILL.md 并推送:git clone [email protected]:rheinmir/setup.git /tmp/rheinmir-setup-sync -b orca --depth 1 cp ~/.agents/skills/orca-onboard/SKILL.md /tmp/rheinmir-setup-sync/skills/orca-onboard/SKILL.md cd /tmp/rheinmir-setup-sync git add . git commit -m "skill: orca-onboard — wrap understand-anything, DeepSeek mechanical dispatch" git push origin orca rm -rf /tmp/rheinmir-setup-sync
仅当技能未生成任何产物且未作出任何决策时,才跳过输出报告。
分支
| ID | 类型 | 条件 | 行为 | 跳过/失败 | 返回 |
|---|---|---|---|---|---|
| B01 | user_optional | @<draft> (RESUME_MODE) | 从草稿读取各阶段的状态;done 阶段则跳过 | 草稿缺少 Project root: → root 为空 → exit 1 | 第一个尚未 done 的阶段 |
| B02 | user_optional | --update + 已有 graph | 仅重新分析变更的文件,并合并到现有 graph/domain/wiki | 没有 graph/meta.json → exit 1;changedFiles 为空 → 完整重建 | W03 |
| B03 | user_optional | --full | 删除 .overstack/graph/,运行完整流程 | — | W03 |
| B04 | conditional_required | 已有 graph,且未指定标志 | 跳过 Phase 1,使用旧 graph | — | W06 |
| B05 | capability_optional | code-graph dep-health.py --json 返回 status == "ok" | 1.7 CODE-GRAPH MCP INDEX:对每个代码仓库运行 reindex_repo,验证 get_stats() >0,并写入 concepts/code-graph-nav.md | degraded/absent → 跳过 | W06 |
| B06 | recovery | opencode 在 1.3、Phase 3、Phase 4 STEP A 中不可用/卡住 | Claude 主线程自行处理(分析批次/wiki/组装 JSON)— Phase 4 仍通过 skeleton | Phase 4 完全失败 → 直接调用 docs-site-macos | 该阶段的下一步 |
| B07 | user_optional | --skip-wiki | 跳过 Phase 3 | — | W08 |
| B08 | conditional_required | 单体仓库(N_SVC ≤ 1) | modules:[]、docker:null → skeleton 自动隐藏 Modules/Docker 标签页 | 多服务 → 显示 Docker 标签页 | W08 |
验证与停止
使用脚本进行确定性检查:Phase 1 末尾的 gate(必须有 2 个文件)、验证 graph 1.6(无重复 ID、无悬空边、每个文件节点恰好属于 1 个 layer、tour nodeIds 均存在)、断言 STEP B 中的 {{TITLE}}/{{ONBOARD_JSON}} 均已替换、Phase 4 末尾运行 ls "$OUT"。需要人工审查:layers/tour、domain graph(对照实际 file:line)、wiki 质量。当草稿中 4 个阶段均为 done 时停止;批次最多重试 1 次。
示例
- 正向: 在包含 180 个文件且有 opencode 的仓库上运行
/orca-onboard ~/code/zca-bridge→ 在终端输出草稿 + dispatch board → 用户回复“ok” → 生成 180 个节点、7 个 layer 和 9 个步骤的 tour →domain-graph.json包含 3 个 domain → wiki concepts/entities →llmwiki/html/onboarding-zca-bridge.html+wiki-graph.html;草稿中的 4 个阶段均为done。 - 边界/失败: 在从未执行过 onboard 的仓库上运行
/orca-onboard --update→ Phase 0 输出“❌ --update requires existing graph. Run without --update first.”并 exit 1,不创建草稿。 - 边界: opencode 在 Phase 4 STEP A 卡住 → Claude 按照 skeleton schema 自行编写
onboard.json,然后运行 STEP B;不要直接编写 HTML。