From 94f009c6e1c5abe69fc6c240d9c676c170b9b512 Mon Sep 17 00:00:00 2001 From: miaoyuanli Date: Tue, 30 Jun 2026 10:19:06 +0800 Subject: [PATCH 1/5] feat: add codebase-wiki plugin MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A Claude Code plugin that turns source code repositories into a living, cross-referenced knowledge base. Implements the LLM Wiki pattern: Entity → Problem Space → Concept pipeline with wikilink network. 6 skills: /ingest, /query, /compare, /lint, /evolve-apply, /completion-gate --- .claude-plugin/marketplace.json | 12 + .../codebase-wiki/.claude-plugin/plugin.json | 42 + plugins/codebase-wiki/.gitignore | 11 + plugins/codebase-wiki/README.md | 49 ++ plugins/codebase-wiki/commands/compare.md | 95 ++ .../codebase-wiki/commands/completion-gate.md | 79 ++ .../codebase-wiki/commands/evolve-apply.md | 424 +++++++++ plugins/codebase-wiki/commands/ingest.md | 825 ++++++++++++++++++ plugins/codebase-wiki/commands/lint.md | 87 ++ plugins/codebase-wiki/commands/query.md | 85 ++ plugins/codebase-wiki/evolve-signals/.gitkeep | 0 plugins/codebase-wiki/hooks/hooks.json | 11 + plugins/codebase-wiki/hooks/session-start.sh | 51 ++ plugins/codebase-wiki/requirements.txt | 2 + plugins/codebase-wiki/schema/CLAUDE.md | 161 ++++ .../codebase-wiki/schema/concept-criteria.md | 145 +++ plugins/codebase-wiki/scripts/lint.py | 463 ++++++++++ plugins/codebase-wiki/seeds/.gitkeep | 0 plugins/codebase-wiki/tests/test_lint.py | 155 ++++ plugins/codebase-wiki/wiki/.obsidian/app.json | 9 + .../wiki/.obsidian/appearance.json | 1 + .../wiki/.obsidian/core-plugins.json | 33 + .../wiki/.obsidian/workspace.json | 201 +++++ plugins/codebase-wiki/wiki/concepts/.gitkeep | 0 plugins/codebase-wiki/wiki/hot.md | 6 + plugins/codebase-wiki/wiki/index.md | 17 + plugins/codebase-wiki/wiki/log.md | 3 + plugins/codebase-wiki/wiki/repos/.gitkeep | 0 plugins/codebase-wiki/wiki/views/.gitkeep | 0 29 files changed, 2967 insertions(+) create mode 100644 plugins/codebase-wiki/.claude-plugin/plugin.json create mode 100644 plugins/codebase-wiki/.gitignore create mode 100644 plugins/codebase-wiki/README.md create mode 100644 plugins/codebase-wiki/commands/compare.md create mode 100644 plugins/codebase-wiki/commands/completion-gate.md create mode 100644 plugins/codebase-wiki/commands/evolve-apply.md create mode 100644 plugins/codebase-wiki/commands/ingest.md create mode 100644 plugins/codebase-wiki/commands/lint.md create mode 100644 plugins/codebase-wiki/commands/query.md create mode 100644 plugins/codebase-wiki/evolve-signals/.gitkeep create mode 100644 plugins/codebase-wiki/hooks/hooks.json create mode 100755 plugins/codebase-wiki/hooks/session-start.sh create mode 100644 plugins/codebase-wiki/requirements.txt create mode 100644 plugins/codebase-wiki/schema/CLAUDE.md create mode 100644 plugins/codebase-wiki/schema/concept-criteria.md create mode 100644 plugins/codebase-wiki/scripts/lint.py create mode 100644 plugins/codebase-wiki/seeds/.gitkeep create mode 100644 plugins/codebase-wiki/tests/test_lint.py create mode 100644 plugins/codebase-wiki/wiki/.obsidian/app.json create mode 100644 plugins/codebase-wiki/wiki/.obsidian/appearance.json create mode 100644 plugins/codebase-wiki/wiki/.obsidian/core-plugins.json create mode 100644 plugins/codebase-wiki/wiki/.obsidian/workspace.json create mode 100644 plugins/codebase-wiki/wiki/concepts/.gitkeep create mode 100644 plugins/codebase-wiki/wiki/hot.md create mode 100644 plugins/codebase-wiki/wiki/index.md create mode 100644 plugins/codebase-wiki/wiki/log.md create mode 100644 plugins/codebase-wiki/wiki/repos/.gitkeep create mode 100644 plugins/codebase-wiki/wiki/views/.gitkeep diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index 9c6b6ca..ae4c15a 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -8,6 +8,18 @@ "version": "1.0.0" }, "plugins": [ + { + "name": "codebase-wiki", + "description": "Codebase knowledge accumulation system — analyze repos, build a structured wiki, compare architectures, and query design decisions. Built on the LLM Wiki pattern.", + "version": "0.2.0", + "author": { + "name": "myl17" + }, + "source": "./plugins/codebase-wiki", + "category": "developer-tools", + "tags": ["codebase", "wiki", "knowledge-management", "code-analysis"], + "keywords": ["codebase", "wiki", "knowledge", "ingest", "architecture", "comparison", "lint"] + }, { "name": "switch-provider", "description": "Switch Claude Code between AI providers (Anthropic, Z.AI, Kimi, MiniMax) with a single command", diff --git a/plugins/codebase-wiki/.claude-plugin/plugin.json b/plugins/codebase-wiki/.claude-plugin/plugin.json new file mode 100644 index 0000000..9feb492 --- /dev/null +++ b/plugins/codebase-wiki/.claude-plugin/plugin.json @@ -0,0 +1,42 @@ +{ + "name": "codebase-wiki", + "description": "Codebase knowledge management plugin — analyze repos, build a structured wiki, compare architectures, query design decisions, and track staleness across your organization's repositories", + "version": "0.1.0", + "author": { + "name": "myl17" + }, + "homepage": "https://github.com/myl17/codebase-wiki", + "repository": "https://github.com/myl17/codebase-wiki", + "license": "MIT", + "keywords": [ + "codebase", + "wiki", + "knowledge-management", + "code-analysis", + "architecture", + "comparison", + "lint" + ], + "commands": { + "ingest": { + "description": "Extract structural entities from a code repository, map to problem spaces, and evolve concept pages", + "file": "commands/ingest.md" + }, + "query": { + "description": "Answer questions using the wiki knowledge base", + "file": "commands/query.md" + }, + "compare": { + "description": "Generate a comparison matrix across repos in a category", + "file": "commands/compare.md" + }, + "lint": { + "description": "Run programmatic health checks on the wiki", + "file": "commands/lint.md" + }, + "evolve-apply": { + "description": "Wikipedia-style concept page evolution: merge, split, redirect", + "file": "commands/evolve-apply.md" + } + } +} diff --git a/plugins/codebase-wiki/.gitignore b/plugins/codebase-wiki/.gitignore new file mode 100644 index 0000000..3ec1428 --- /dev/null +++ b/plugins/codebase-wiki/.gitignore @@ -0,0 +1,11 @@ +.manifest.json +wiki/eval-history.jsonl +raw/ +__pycache__/ +*.pyc +.pytest_cache/ +wiki/repos/*/.hashes.json +wiki/.obsidian/graph.json +experiments/ +.claude/ +skills/ diff --git a/plugins/codebase-wiki/README.md b/plugins/codebase-wiki/README.md new file mode 100644 index 0000000..c40cb22 --- /dev/null +++ b/plugins/codebase-wiki/README.md @@ -0,0 +1,49 @@ +# codebase-wiki + +> A growing markdown directory maintained by LLM — not a graph database. + +A [Claude Code](https://claude.ai/code) plugin that turns source code repositories into a **living, cross-referenced knowledge base**. It implements the [LLM Wiki](https://gist.github.com/karpathy/442a6bf555914893e9891c11519de94f) pattern by Andrej Karpathy. + +## Install + +``` +/plugin marketplace add kossakovsky/cc-plugins +/plugin install codebase-wiki@cc-plugins +``` + +Or install standalone: + +``` +/plugin marketplace add myl17/codebase-wiki +/plugin install codebase-wiki@codebase-wiki +``` + +## What it does + +Each time you ingest a repo, the LLM reads the source, extracts structural modules (**Entities**), maps them to cross-repo design questions (**Problem Spaces**), and writes or updates **Concept** pages with comparison tables, wikilinks, and source provenance. The `[[wikilink]]` network **is** the graph — open it in Obsidian to see the knowledge structure directly. + +## Skills + +| Skill | Description | +|-------|-------------| +| `/ingest` | Read source → extract entities → map problem spaces → write/update concept pages | +| `/query` | Answer questions via wikilink traversal + 3-level retrieval escalation | +| `/compare` | Cross-repo comparison: Concept → Entity → Source code | +| `/lint` | Full wiki health check: wikilinks, frontmatter, orphans, provenance | +| `/evolve-apply` | Wikipedia-style concept evolution: merge, split, redirect | + +## Quick Start + +``` +/ingest /path/to/your/repo my-repo-name +``` + +After ingest, open `wiki/` in Obsidian with Graph View to see the wikilink network. Ingest a second repo in the same domain and it will update existing Concept pages with comparisons. + +## Documentation + +See the [GitHub repo](https://github.com/myl17/codebase-wiki) for full documentation. + +## License + +MIT diff --git a/plugins/codebase-wiki/commands/compare.md b/plugins/codebase-wiki/commands/compare.md new file mode 100644 index 0000000..0ad9d47 --- /dev/null +++ b/plugins/codebase-wiki/commands/compare.md @@ -0,0 +1,95 @@ +# /compare — Cross-Repo Comparison + +Given repo names or design problem keywords, find the cheapest available information source along the escalation chain and produce a comparison. + +**Announce at start:** "Running /compare ..." + +## Trigger + +``` +/compare [ ...] [--concept ] [--auto] +/compare --concept +/compare ... --concept +``` + +Examples: +``` +/compare hermes-agent nanobot openclaw +/compare --concept memory +/compare hermes-agent openclaw --concept agent +``` + +## --auto mode + +`--auto` skips user interaction but does NOT skip correctness checks. See each section's `[--auto]` annotation for impact. + +``` + Normal mode --auto mode + ─────────── ─────────── +Self-check MUST RUN MUST RUN (unchanged) +Accuracy MUST RUN → ask user MUST RUN → write evolve-signals/ → continue +Structure MUST RUN → ask user MUST RUN → write evolve-signals/ → continue +Archive ask user skip (don't archive) +``` + +## 3-Level Escalation Chain + +### Level 1 — Concept Comparison (cheapest, pre-computed) +Read `wiki/index.md` → find the Concepts table. For each concept covering the target repos, read the **Comparison** section of the concept page: +```bash +grep -A 50 "^## 对比" wiki/concepts/.md +``` +Extract the comparison table — this is a pre-computed summary across repos. + +### Level 2 — Entity Page (slightly more expensive) +When Level 1 doesn't cover a particular problem dimension, go to entity pages for the target repos: +```bash +ls wiki/repos//entities/ +``` +Read entity frontmatter (`problem:` field) → identify entities that address the same problem across repos → read their **Key Mechanisms** sections. + +### Level 3 — Source Code (most expensive, last resort) +When Levels 1-2 still leave a specific comparison point unaddressed, read the actual source files (via the `source_files:` field in entity frontmatter). Read only the file and function ranges noted in provenance references. + +## STOP 1 — Content Accuracy Self-Check + +After producing the comparison output at any level, before presenting to the user: + +1. Compare the generated comparison against concept page descriptions. Any inconsistency between what Level 3 source reading shows and what concept pages claim? + +2. Check: do concept pages used in the comparison have provenance references? Are descriptions complete? + +3. If discrepancies found → **STOP 1a: Content Repair Flow**: + - (a) Present diff (old description → new description), inform user of discrepancy + - (b) After user confirmation, write correction to wiki page. `[--auto]`: write signal to `evolve-signals/` + - (c) Regenerate affected parts of the comparison output + - (d) Log append: `[源码验证:
修正] [触发: /compare]` + +## STOP 2 — Concept Structure Self-Check + +After STOP 1 passes, check concept structure: + +1. Do any comparison results suggest a concept needs to be split? (Two very different approaches to the same concern, different enough to be independent design spaces) +2. Do any comparison results suggest concepts need to be merged? (Two concepts where repos choose essentially the same trade-off axis) +3. Do any comparison results suggest a new concept? (A recurring pattern across repos not yet captured) + +If yes → present candidate operations, trigger evolve-apply logic. `[--auto]`: write signal to `evolve-signals/` → continue. + +## Archive Decision + +After comparison output is finalized: + +``` +> Archive this comparison? +> - A: Don't archive (ad-hoc view) +> - B: Save to wiki/views/ +> - C: Initiate /ingest to fold comparison findings into Concept evolution +``` + +### Option B — Save as view +- Write `wiki/views/-compare-.md` +- Append to `wiki/log.md` with `[compare]` tag +- Update `wiki/index.md` `## Views` section + +### After B — completion-gate +Invoke `/completion-gate` as a REQUIRED SUB-SKILL before claiming completion. diff --git a/plugins/codebase-wiki/commands/completion-gate.md b/plugins/codebase-wiki/commands/completion-gate.md new file mode 100644 index 0000000..4885e17 --- /dev/null +++ b/plugins/codebase-wiki/commands/completion-gate.md @@ -0,0 +1,79 @@ +# /completion-gate — Completion Quality Gate + +A shared quality gate that must be passed before any wiki write operation can be declared complete. Not a user-invoked skill — referenced by other skills as `REQUIRED SUB-SKILL` at completion. + +## Trigger + +``` +(not user-invoked — referenced by other skills as REQUIRED SUB-SKILL at completion) +``` + +**Announce at start:** Execute silently. On a clean pass, output nothing to the user. + +## Iron Law + +``` +NO COMPLETION CLAIMS BEFORE THE GATE IS CLEARED +``` + +Before claiming "operation complete," every item in this gate must be passed. Skipping any item = not complete. + +## The Gate Function + +``` +BEFORE claiming any wiki operation is complete: + +1. IDENTIFY: Which wiki files does this operation touch? +2. CHECK: Is each file in the correct state? (operation scope, not full wiki coverage) +3. VERIFY: Do logs and index reflect this operation? +4. GATE: All pass → declare complete. Any fails → fix and re-check. +``` + +## Non-Negotiable Checklist + +### 1. Write Correctness + +- [ ] If Entity pages were written → frontmatter has all 6 required fields (`type`, `repo`, `slug`, `problem`, `source_files`, `generated`) +- [ ] If Concept pages were written → + - ① frontmatter `repos:` covers **all** repos discussed in the body? (body has `### repo-name` sections or wikilinks but frontmatter doesn't list it = coverage invisible → BLOCK) + - ② For each frontmatter-listed repo, is there at least one `[[repos//entities/...]]` wikilink in the body? (repos declared in metadata with no wikilink evidence → WARN, log but don't block) +- [ ] If maintenance files were written (`index.md`, `log.md`, `hot.md`) → format matches spec +- [ ] Every fact claim in new/changed pages has at least one provenance reference `^[path:line]` + +### 2. Incremental Wikilink Validation + +- [ ] Every wikilink in files written by this operation resolves to an existing file +- [ ] For each new concept page: do source entity pages have backlinks added? (`**Associated Concepts**: [[concepts/]]`) +- [ ] For each updated concept page: if `repos:` gained a new repo, do that repo's entity pages have backlinks added? + +### 3. Maintenance File Sync + +- [ ] `wiki/log.md`: new entry appended at bottom, with correct format (`[timestamp] operation detail`) +- [ ] `wiki/hot.md`: overwritten with current status (last operation, active repos, concept count, pending signal count) +- [ ] `wiki/index.md`: if repos were added → Repos section updated. If concepts were created/merged → Concepts table updated. If views/insights were archived → Views or Insights section updated. + +### 4. Cleanup + +- [ ] No leftover `[源码验证:]` markers in commit messages or page bodies (these are transient during-edit markers, should be resolved) +- [ ] No "I'll do this later" / "TODO" / unfinished write promises in written content +- [ ] If the operation produced `evolve-signals/` files → remind user: "N evolve signals are pending in evolve-signals/" + +## Red Flags + +Do NOT claim completion if any of: +- ① A written file has broken wikilinks (target doesn't exist) +- ② A written file is missing required frontmatter fields +- ③ A written concept page has `repos:` in frontmatter that don't match the repos actually discussed in body +- ④ `wiki/log.md` or `wiki/hot.md` don't reflect this operation +- ⑤ Source entity pages lack backlinks to newly written concept pages +- ⑥ The operation produced artifacts outside the wiki directory without updating `.gitignore` + +## When To Apply + +| Operation | Gate invoked by | +|-----------|----------------| +| `/ingest` (write entities + concepts) | `/ingest` itself at completion | +| `/evolve-apply` (merge/split/redirect) | `/evolve-apply` itself at completion | +| `/compare` (archive as view → B or C) | `/compare` itself at completion | +| `/query` (archive as insight → B or C) | `/query` itself at completion | +| Any manual edit to wiki pages | Editor's responsibility to self-check | diff --git a/plugins/codebase-wiki/commands/evolve-apply.md b/plugins/codebase-wiki/commands/evolve-apply.md new file mode 100644 index 0000000..4eeed9c --- /dev/null +++ b/plugins/codebase-wiki/commands/evolve-apply.md @@ -0,0 +1,424 @@ +# /evolve-apply — Concept Evolution + +Wikipedia-style Concept page evolution: merge, split, redirect. + +## Trigger + +``` +/evolve-apply # Process all signals in the specified file +/evolve-apply # List all available signal files +/evolve-apply --skip-check # Skip precondition checks (dangerous, only when user explicitly requests) +/evolve-apply --auto # Skip operation confirmation prompts, execute directly after preconditions pass +/evolve-apply --skip-check --auto # Fully automatic, no questions +``` + +`` can be a filename (e.g., `2026-06-24-hermes-agent.md`) or a full path. + +Triggered when the user expresses intent to merge, split, or redirect Concept pages — whether from `/ingest` Type D signals, structural discoveries during `/compare`, or direct instructions. + +## Execution Protocol + +Every evolution operation appends to `wiki/log.md` with trigger source noted: + +``` +[] evolve merge [trigger: /compare session] +[] evolve split [trigger: /ingest pause point 2] +[] evolve redirect [trigger: user direct instruction] +``` + +### Step 0 — List signal files + +```bash +ls -1 evolve-signals/ +``` + +Display: + +``` +Available evolve signal files: + +| File | Date | Source ingest | +|------|------|--------------| +| 2026-06-24-hermes-agent.md | 2026-06-24 | hermes-agent | +| 2026-06-20-openclaw.md | 2026-06-20 | openclaw | + +Run `/evolve-apply ` to process. Example: + /evolve-apply 2026-06-24-hermes-agent.md +``` + +Stop. + +### Step 1 — Read signal file + +Read `evolve-signals/`, parse each signal: + +- Problem: +- Related Concept: +- Signal type: granularity mismatch / merge candidate / split candidate +- Reason: + +Present grouped summary to user: + +``` +Signal file: +Total evolve signals: + + Merge candidates (): + - "" should merge into "": + ... + + Split candidates (): + - Split out "" from "": + ... + + Redirect candidates (): + - "" → "": + ... + +Process each? Confirm to proceed. +``` + +After user confirmation, process each in order: merge → split → redirect. + +### Step 2 — Per-signal precondition check + +For each signal, run precondition checks first. Only proceed to execution if checks pass. Skip and log reason if not. + +**If user passed `--skip-check`:** Skip precondition checks, proceed directly to execution. But must show warning before executing: + +``` +⚠️ --skip-check: skipping precondition checks, executing directly. +The following operations will proceed without condition verification and may cause irreversible wiki structural changes. +Confirm to continue? +``` + +User must confirm again before execution. + +**If user passed `--auto`:** Skip per-operation "confirm execution?" prompts. Operations that pass precondition checks execute directly; those that don't pass are still skipped (unaffected by `--auto`). After executing all operations, output a summary. `--skip-check --auto` combined = fully automatic, process all signals without asking. + +**This skill only defines precondition criteria and execution steps for the three operations.** Specific operation prompts are in their respective subsections — these prompts are for the independent agent dispatched to execute each operation. + +**Key: after each operation completes, continue to the next — don't stop midway to ask "continue?".** The precondition pass/fail already gives the user the choice. The execution phase is batch-driven. + +--- + +## User Direct Instruction Path + +When the user directly expresses merge/split/redirect intent (e.g., "merge A and B", "split memory-management into two", "add alias Y for X"), don't use the signal file flow — use this path. + +**Key difference from signal-driven path:** +- Signal-driven: rigid threshold, precondition failure → skip directly +- User direct instruction: **soft analysis**, give recommendation but allow user override + +### Step A — Feasibility analysis (must run first) + +Read the relevant Concept pages, analyze against the corresponding operation's precondition criteria. + +**Merge analysis:** +1. Is A's problem a sub-dimension of B's problem? +2. Can A's content be fully expressed within B's page after merging? (no loss of independent discussion value) +3. Does A have any independent concerns or comparison dimensions not covered by B? + +**Split analysis:** +1. Does the sub-topic already have ≥2 repos with different solutions? (count from the page) +2. Is there a real trade-off between solutions? (not one solution strictly dominates on all dimensions) +3. After splitting into an independent page, can the sub-topic still pass Concept admission criteria ①②③? (single-source definition in `schema/concept-criteria.md`) + +**Redirect analysis:** +1. Does the target page exist? +2. Do both names genuinely point to the same problem space? + +### Step B — Give judgment + recommendation + +**If analysis passes all checks:** +``` +Analysis: this operation is valid. +- +- +Recommend proceeding. Continue? +``` +User confirms → execute per the corresponding operation (Merge/Split/Redirect) execution steps. Log: `[分析通过]`. + +**If analysis shows issues:** +``` +Analysis: this operation has risks. +- +- +Recommend against proceeding. + +If you still wish to proceed, I will continue but will mark "user overrode precondition" in the evolution log. +Proceed anyway? +``` +User insists → execute (evolution log marked `[⚠️ user overrode precondition]` + risk description). +User declines → abort. + +**If analysis is clearly unreasonable:** +``` +Analysis: this operation is not recommended. +- A discusses "", B discusses "" + These are two entirely different problem spaces; merging would confuse the Concept page definition. +Strongly recommend against proceeding. +``` +User still insists → execute (evolution log marked `[⚠️ user overrode precondition] [severe]`). + +### Log format + +``` +[] evolve merge [trigger: user direct instruction] [analysis passed] +[] evolve split [trigger: user direct instruction] [⚠️ user overrode precondition] risk: +[] evolve redirect [trigger: user direct instruction] [analysis passed] +``` + +**Contrast: signal-driven path log format unchanged:** +``` +[] evolve merge [trigger: /ingest pause point 2] +``` + +--- + +## Operation A: Merge + +### Preconditions (must check each before execution) + +Read the source page `wiki/concepts/.md` and target page `wiki/concepts/.md`, check: + +1. Does ``'s problem represent a sub-dimension of ``'s problem? +2. Can ``'s content be fully expressed within ``'s page after merging? (no loss of independent discussion value) +3. Does `` have any independent concerns or comparison dimensions not covered by ``? + +**All three pass → proceed, show change preview, execute after user confirmation.** +**Any one fails → don't proceed, explain reason to user, skip this signal.** + +### Change preview + +``` +Merge operation preview: + + Merge all content of [[]] into [[]] + will become a redirect page + + Scope of impact: + - Will append repo solutions to + - Will update 's comparison table (new comparison dimension) + - will be rewritten as a redirect page + - seeds/master.md corresponding entries marked merged_into + + Confirm execution? +``` + +### Execution (independent agent) + +Dispatch an agent with the following prompt: + +``` +Your task is to merge two Concept pages into one. + +## Input + +- Source page: wiki/concepts/.md +- Target page: wiki/concepts/.md +- Merge reason: + +## Validation check (must verify before executing) + +Merge validity requires: +- 's problem is a sub-dimension of 's problem +- 's content can be fully expressed within 's page +- has no independent concerns or comparison dimensions absent from + +If any condition fails, stop and explain why. + +## Execution steps + +1. Merge each repo's solutions from into the corresponding sections of +2. Update 's comparison table to include dimensions introduced by +3. Update 's evolution log with merge source and date +4. Rewrite .md as a redirect page: + + --- + redirect_to: + reason: + date: + --- + # + > This page has been merged into [[]]. Reason: + +5. Update wiki/index.md Concepts table: remove row, update row +6. Update seeds/master.md, mark related entries merged_into: +7. Overwrite wiki/hot.md (update pending evolve signals count) +8. Append to wiki/log.md: [] evolve merge + +## Do not modify + +Existing repo content in — only append, never overwrite. +``` + +--- + +## Operation B: Split + +### Preconditions (must check each before execution) + +Read the source page `wiki/concepts/.md`, check: + +1. Does the sub-topic already have ≥2 repos with different solutions? (count from within the page) +2. Is there a real trade-off between solutions? (not one solution strictly dominates on all dimensions) +3. After splitting into an independent page, can the sub-topic still pass Concept admission criteria ①②③? (single-source definition in `schema/concept-criteria.md`) + +**All three pass → proceed, show change preview, execute after user confirmation.** +**Any one fails → don't proceed, explain reason to user, skip this signal.** + +### Change preview + +``` +Split operation preview: + + Split out new sub-Concept page [[]] from [[]] + + Scope of impact: + - Will create wiki/concepts/.md ( repo solutions) + - Will update (remove migrated content, retain summary and link) + - seeds/master.md related entries marked split_to + + Confirm execution? +``` + +### Execution (independent agent) + +Dispatch an agent with the following prompt: + +``` +Your task is to split a new sub-Concept page out of an existing Concept page. + +## Input + +- Source page: wiki/concepts/.md +- Split sub-topic: +- Split reason: + +## Validation check (must verify before executing) + +Split validity requires: +- The sub-topic has ≥2 repos with different solutions, with real trade-offs between them +- After splitting into an independent page, the sub-topic can still pass Concept admission criteria ①②③ (single-source definition in `schema/concept-criteria.md`): + ① Multiple solutions: at least two different repos solve the same problem in clearly different ways. + Note: if one solution dominates another on all trade-off dimensions after analysis, it doesn't qualify. + ② Independent design space: this problem cannot be fully covered by an existing problem space — + merging it in would cause its discussion dimensions to disappear and decision value to be lost. + ③ Persistent trade-off: no silver bullet across different solutions — + satisfying concern A increases the cost of satisfying concern B, and vice versa. +- The split sub-topic is not just a standalone description of one solution from the source page + +If any condition fails, stop and explain why. + +## Execution steps + +1. Create wiki/concepts/.md + Include repo solutions, concerns, and comparison table extracted from the source page + Evolution log: "Split from [[]] on " + +2. Update source page: + - Remove migrated detailed content + - Keep summary (one sentence) with wikilink to the new page + - Evolution log: "Split out [[]] on " + +3. Update wiki/index.md Concepts table: add row, update row +4. Update seeds/master.md, mark related entries split_to: +5. Overwrite wiki/hot.md (update pending evolve signals count) +6. Append to wiki/log.md: [] evolve split +``` + +--- + +## Operation C: Redirect + +### Preconditions + +Check that the target page `wiki/concepts/.md` exists. + +**Exists → pass.** +**Doesn't exist → fail, stop.** + +### Change preview + +``` +Redirect operation preview: + + Will create redirect page [[]] → [[]] + + Scope of impact: + - Will create wiki/concepts/.md (frontmatter + one line only) + - No changes to + + Confirm execution? +``` + +### Execution (independent agent) + +Dispatch an agent with the following prompt: + +``` +Your task is to create a redirect alias for a Concept page. + +## Input + +- Target page: wiki/concepts/.md +- Alias name: +- Reason: + +## Execution steps + +1. Create wiki/concepts/.md: + + --- + redirect_to: + reason: + date: + --- + # + > This name redirects to [[]]. Reason: + +2. Update wiki/index.md Concepts table: add row (marked as redirect) +3. Overwrite wiki/hot.md (update pending evolve signals count) +4. Append to wiki/log.md: [] evolve redirect + +## Do not modify any content in the target page +``` + +--- + +## Step 3 — Summary + +After all signals are processed, output a summary. + +## After Step 3 + +**REQUIRED SUB-SKILL:** If at least one evolution operation (merge/split/redirect) was executed, invoke `completion-gate` before claiming "complete". If all signals were skipped (no actual operations), skip completion-gate. + +``` +Evolve signal processing complete: + + ✅ Merge: succeeded, skipped + - ✅ + - ⏭️ Reason: failed precondition #3 + ✅ Split: succeeded, skipped + ✅ Redirect: succeeded, skipped + +Skip reason details: + - : 's independent concern "" has no corresponding dimension in ; merging would lose discussion value. +``` + +## Edge Cases + +- If source page is already a redirect page → skip, note " is already a redirect page, nothing to merge" +- If merge target page doesn't exist → stop, note " doesn't exist, verify slug correctness first" +- If split target already exists → prompt user, confirm whether to append or rename +- If signal file contains duplicate signals (multiple signals for the same slug) → process only the first + +## Irreversibility Warning + +Merge and split operations modify wiki page content. While git can roll back, confirm git state is clean before proceeding: + +```bash +git status --short +``` diff --git a/plugins/codebase-wiki/commands/ingest.md b/plugins/codebase-wiki/commands/ingest.md new file mode 100644 index 0000000..30d3686 --- /dev/null +++ b/plugins/codebase-wiki/commands/ingest.md @@ -0,0 +1,825 @@ +# /ingest — Code Repository Ingest + +Extract structural knowledge from source repositories and progressively evolve the wiki. + +## Trigger + +``` +/ingest [] [--verify] [--full] [--auto] +``` + +- ``: Source directory (read-only) +- ``: Identifier in the wiki; defaults to the last segment of `` +- `--verify`: Enable Step 5 independent verification (disabled by default) +- `--full`: Force full re-extraction (skip delta detection) +- `--auto`: Skip all pause points, execute fully automatically (summary still written to log) + +When the user asks to analyze, ingest, or add a code repository to the wiki. + +### Multi-Repo Parallelism + +When the user specifies multiple repos at once: + +``` +Steps 1+2 in parallel (each repo gets its own agent, concurrent) + agent-1: repo-A → Entity extraction → Problem Space mapping + agent-2: repo-B → Entity extraction → Problem Space mapping + agent-3: repo-C → Entity extraction → Problem Space mapping + +After all complete, converge: + +Step 3 Problem Space matching (needs cross-repo info, unified processing) + → All repos' problem-maps + existing seed bank + existing Concept pages + → Produce candidate list (cross-repo comparison data now available → A/B/D types possible) + +Steps 4-6 continue +``` + +**The parallel vs. serial dividing line: whether cross-repo information is needed.** +- Entity extraction (Step 1): only looks at single repo source → parallelizable +- Problem Space mapping (Step 2): only looks at single repo entity pages → parallelizable +- Problem Space matching (Step 3): needs comparison against other repos → must wait for all to complete + +### Re-ingest (Incremental Update) + +Running ingest again on a previously-ingested repo. Delta detection is not an optimization — without it, every re-ingest would require re-reading 500 repos' source code from scratch, an unacceptable token cost. + +#### `.ingest-state.json` format + +Stored inside the wiki project: `wiki/repos//.ingest-state.json` (sibling to `entities/` and `overview.md` — does not pollute the source repo). + +```json +{ + "repo": "", + "source_path": "", + "last_ingest": "", + "files": { + "": "", + ... + }, + "entity_map": { + "": ["", ...] + } +} +``` + +- `source_path`: Path to the source directory. If the user moved the repo, a changed path also counts as "needs re-detection." +- `files`: SHA-256 hashes of all source files actually read during ingest. Not every file in the repo — only those Step 1 actually read. +- `entity_map`: Which source files each entity depends on. Used during change detection for reverse mapping: file changed → which entities are affected. + +#### Step 0 — Delta Detection + +``` +Your task is to perform delta detection for , determining what needs re-extraction. + +## Input + +- Source directory: +- Last ingest snapshot: wiki/repos//.ingest-state.json + +## Detection logic + +1. If wiki/repos//.ingest-state.json does not exist: + → Report "first ingest", trigger full pipeline (Steps 1-6) + +2. Read .ingest-state.json, compare source_path: + - If source_path differs from current source directory → path moved, trigger full re-ingest + - Same → continue + +3. Extract the files dictionary; for each path, compute SHA-256 of current file content, compare to snapshot +4. Record changed file list + +5. If no changes: + → Report "no changes", end. Do not execute subsequent steps. + +6. If new files exist that weren't in the last snapshot (repo added files): + → Read these new files, determine whether they contain new independent modules (entities) + → If yes, mark them as affected entities + +7. Reverse-map changed files + entity_map: + → Does each entity's dependent files appear in the change list? + → Yes: mark that entity as "affected", needs re-extraction + +8. Output: + - Changed files: + - Affected entities: + - New entity candidates: (if any) +``` + +#### Step 1 (incremental mode) + +Only re-extract affected entities + new entity candidates. Unaffected entity pages remain untouched. + +overview.md is always refreshed — the entity list may have changed (additions/removals), and the tags description may be stale. + +#### Snapshot update + +In Step 6 wrap-up, overwrite `.ingest-state.json` with the files and SHA-256 hashes actually read during this ingest. + +--- + +## Pipeline Overview + +``` +Step 1 Entity Extraction + Input: source directory + Output: wiki/repos//entities/.md (one file per Entity) + wiki/repos//overview.md + +Step 2 Entity Problem Space Mapping + Input: Entity pages + optionally read source + Output: seeds/-problem-map.md + +★ Pause Point 1 (user confirms problem space list completeness) + +Step 3 Problem Space Matching + Input: problem-map + seed bank + existing Concept pages + Output: seeds/-candidates.md + evolve-signals/-.md (Type D signals) + +★ Pause Point 2 (user confirms candidate list + capability coverage table) + +Step 4 Concept Writing (per-Concept independent agent) + Input: Type A/B entries from candidates.md + Output: wiki/concepts/.md (new or appended) + +Step 5 [optional, --verify] Independent Verification + Repair + Input: Step 4 output + Output: verification report → repaired Concept pages + +Step 6 Seed Bank Update + Evolution Report Finalization + Input: problem-map + candidates.md + Output: seeds/master.md updated + wiki/log.md + wiki/hot.md updated + +★ Pause Point 3 (user reviews ingest summary, decides whether to trigger /evolve-apply) +``` + +Outside of pause points, the LLM executes autonomously. + +--- + +## Step 1: Entity Extraction + +Run the following prompt: + +``` +Your task is to extract all Structural Entities from source code. + +## Input source + +Source directory: +This is the sole source of information — do not use prior knowledge from training data. + +## Approach + +Decide for yourself which files to read. Explore broadly until you comprehensively understand the repo structure. +Criterion: does a module have an independent responsibility boundary, external interface, and can it be understood and replaced in isolation? + +## Output each Entity as a separate file + +Path: wiki/repos//entities/.md + +Format: + +--- +type: entity +repo: +slug: +problem: +generated: +source_files: + - +--- + +# + +**Code location**: +**What problem this module solves**: +- Implementation layer: +- Problem layer: +**What it exposes**: +**What it interacts with**: +- Depends on [[entities/]] () +- Called by [[entities/]] () +(same-repo entities use wikilinks; external libraries use plain text) +**Why it is separable**: + +**Key Mechanisms** (visible in source): +- : ^[file-path:line] +- : ... + +**Source evidence**: +- Entry file: +- Core type/interface definitions: + +## Additional output: Repo Overview + +Path: wiki/repos//overview.md + +Content: +- What this repo is (one paragraph) +- Core subsystem list, **each item with wikilink**: `- [[repos//entities/]]` +- What it explicitly does NOT do + +## Core constraints + +1. Every factual claim must have ^[file-path:line] provenance +2. Each Entity gets its own file — do not merge +3. Explore broadly — do not miss independent directories or independent packages +``` + +--- + +## Step 2: Entity Problem Space Mapping + +Run the following prompt: + +``` +Your task is to translate all Entity pages for into problem space entries. + +## Who you are translating for + +Framework Builder — someone studying the design space of a framework class. +They need to know: is this a question that everyone building a similar framework must answer? +What choice did this repo make on this question? + +## Input + +- Entity pages: wiki/repos//entities/*.md +- Source directory: (read on demand when Entity page "problem layer" descriptions are unclear) +- Do not read the existing seed bank or other repos' results; work independently + +## Phase 1: Per-Entity Mapping + +For each Entity: + +1. Determine whether its "problem layer" question is worth entering as a candidate: + Would someone building a similar framework have to make a design choice on this question? + → Yes: generate one problem space entry (only one), focused on the fundamental reason this Entity exists + → No (implementation detail / unique to this repo): skip + +2. When generating an entry, supplement with this repo's solution and concerns + +## Phase 2: Cross-Entity Coverage Check + +After Phase 1 completes, perform the following purely mechanical operation: + +1. Collect every item listed in the **Key Mechanisms** section of each mapped Entity's body. + For each item, ask yourself: if you strip away the domain-specific terminology belonging to that Entity's problem domain, + does the remaining structure still describe an independent design choice? + If yes → add to candidate pool, annotate with source Entity + If no → ignore (it's just that Entity's implementation tactic within its problem domain) + +2. After the candidate pool is collected, check whether the same design choice (or highly similar) + is mentioned by ≥2 different Entities. + For each that satisfies this: + - If the current problem-map already has an entry covering this design dimension → skip + - If not → generate a supplementary problem space entry, same format as Phase 1, + **Source Entity** listing all Entity slugs involved in this design + +## Output format + +Path: seeds/-problem-map.md + +Each problem space entry: + +--- +## ("how to..." form) + +**Problem Statement**: +**Core Concerns**: +- Concern 1: +- Concern 2: ... +**'s Solution**: +**Source Evidence**: +**Source Entity**: +**Level**: Architectural Decision / Technology Choice + +End-of-file notes: + +### Skipped Entities +- : + +### Cross-Entity Coverage Check +| Candidate Design Element | Involved Entities | Added Entry | Rationale | +|-------------------------|-------------------|-------------|-----------| +| ... | ... | Yes/No | ... | +``` + +--- + +## ★ Pause Point 1: Problem Space Completeness Confirmation + +After Step 2 completes, present the following summary and wait for user confirmation: + +``` +Extracted problem spaces from Entities: + +| Problem Space | Source Entity | Level | +|-------------------------------|------------------|--------------------| +| How to... | | Architectural | +| ... | ... | ... | + +Skipped Entities (, implementation details): +- : + +Any missing capability domains? Confirm to proceed to Step 3. +``` + +User can: point out omissions → LLM supplements extraction, updates problem-map → continues. +No response from user → auto-continue. + +**If --auto: skip user confirmation, proceed directly to Step 3. Still output problem space list and skipped entity summary to log.** + +--- + +## Step 3: Problem Space Matching + +Run the following prompt: + +``` +Your task is to compare 's problem space mapping results against existing Concept pages and produce a candidate list. + +## Input + +- New repo problem space mapping: seeds/-problem-map.md +- Existing seed bank: **Do NOT read seeds/master.md in full.** Grep seeds/master.md with keywords from the problem-map, only read matching lines: `grep -i "keyword1\|keyword2\|..." seeds/master.md` +- Existing Concept pages: + **Scale detection**: first run `ls wiki/concepts/*.md 2>/dev/null | wc -l` to get total Concept count + + **If ≤ 50 Concepts (Strategy A)**: + 1. `for f in wiki/concepts/*.md; do head -10 "$f"; done` scan all Concept frontmatter in one pass + 2. For Concepts the LLM judges to have semantic relevance, deep-read the full "Core Problem" and "Concerns" sections + + **If 50–500 Concepts (Strategy B)**: + 1. From each problem-map entry's "Problem Name," extract 2–4 core technical keywords + - Choose substantive technical terms; skip connectors like "how to," "the," "a." If common English equivalents exist, include English variants. + - Example: "How to let main Agent delegate background sub-agent to execute complex tasks" → subagent delegate isolation execution + - Counter-example: short abbreviations like "MCP," "RAG," "LLM" (2–3 characters) are equally high-value keywords — do NOT skip them due to short length. They are concentrated signals in technical documentation and core signals for precise matching. + 2. 【Per-entry independent grep — strictly forbid cross-entry merging】For each entry in the problem-map, perform one grep using only that entry's own extracted keywords. Do NOT merge keywords from different entries into a single giant grep: + - Entry 1: `grep -l "entry1-kw1\|entry1-kw2\|..." wiki/concepts/*.md` → record as result list A + - Entry 2: `grep -l "entry2-kw1\|entry2-kw2\|..." wiki/concepts/*.md` → record as result list B + - ...and so on. After each entry's grep, record its hit file list separately. + - **Strictly forbidden**: synthesizing all entries' keywords into one grep — merging breaks the correspondence between hits and entries, and some entries may get zero hits because their keywords are diluted in a long OR chain. + 3. 【Self-check: per-entry grep execution coverage audit】Before merging and deduplicating, audit the grep execution records entry by entry: + - For every entry in the problem-map, confirm it has a corresponding grep execution record (i.e., result list A, B, ...) + - If any entry is found without a grep execution (missing its result list), immediately go back to step 2 and run grep for it + - Only after the self-check passes (all entries have been grep'd and results recorded) may you proceed to step 4 + 4. Merge all entries' hit file lists and deduplicate + 5. Only run `head -10` on deduplicated files to confirm frontmatter match + 6. Deep-read the full text of confirmed-matching Concepts + + **If > 500 Concepts (Strategy C)**: + 1. First execute Strategy B + 2. For unmatched entries, extract expanded terms from matched Concepts' `concerns` fields, run a second round of grep + 3. Entries still unmatched → mark as "manual review needed" (not a search failure — genuinely no matching Concept), explain why + +## Search behavior constraints + +Before starting matching, must execute scale detection and explicitly declare: +"Detected N Concepts → selected Strategy [A/B/C]" + +Then execute retrieval per the chosen strategy. + +## Classification criteria + +For each problem space entry, classify into one of four cases (see below). +When creating new or appending, must pass the following criteria check (single-source definition in `schema/concept-criteria.md`): + +Hard thresholds (all three must pass): + +① Multiple Solutions + At least two different repos solve the same problem in clearly different ways. + Note: if after analysis one solution dominates another on all trade-off dimensions, + this is not a genuine design trade-off — does not qualify. + +② Independent Design Space + This problem cannot be fully covered by an existing problem space — + merging it in would cause its own discussion dimensions to disappear, + and the Framework Builder would lose decision value on this problem. + +③ Persistent Trade-off + No silver bullet across different solutions — + satisfying Concern A increases the cost of satisfying Concern B, and vice versa. + +Auxiliary criterion (not meeting it does not veto; affects priority): + +④ Sustainable Extensibility + New repos are likely to contribute new solutions to this problem in the future. + If no new repos join long-term, trigger "downgrade" evolution recommendation. + +## Few-shot examples + +### Example Domain 1: AI Agent Frameworks + +Input Entities (cross-repo): +- OpenClaw: Agent (YAML config), Workflow (explicit orchestration), Memory (external context injection), + ToolTimeout (per-tool YAML timeout config) +- HermesAgent: Agent (@agent decorator), EventBus (event-driven coordination), + Memory (internal state sync), ToolTimeout (per-toolset timeout) + +Positive example — "Agent Definition Style": +① At least two repos, different approaches: config-driven vs. decorator-driven. ✅ +② Independent design space: evaluation dimensions are declarative convenience vs. programming flexibility; + does not share evaluation dimensions with "multi-Agent coordination." ✅ +③ Persistent trade-off: config is simple but inflexible vs. programming freedom with higher barrier; no silver bullet. ✅ +④ Sustainable extensibility: new frameworks will still make different choices on this question. ✅ +Decision: ✅ Create new Concept page agent-definition-style + +Counter-example 1 — fails ①: single repo only +Candidate group "HermesAgent SafeWriter pipeline protection": +① Multiple solutions: ❌ Only HermesAgent has this; other repos have no corresponding Entity. +Decision: ❌ Enter seed bank for observation; does not qualify as a Concept + +Counter-example 2 — fails ②: not an independent design space +Candidate group "Tool Execution Timeout Configuration": +① Multiple solutions: OpenClaw uses per-tool YAML timeout; HermesAgent uses per-toolset timeout. ✅ +② Independent design space: ❌ "Timeout configuration" is a sub-dimension of the existing problem space + "Tool Execution Safety & Control." Merging it in would not lose discussion dimensions. +Decision: ❌ Does not qualify; handle as sub-dimension of "Tool Execution Safety" Concept + +Counter-example 3 — fails ③: no genuine trade-off +Candidate group "Structured Log Format": +① Multiple solutions: OpenClaw uses plain text; HermesAgent uses structured JSON + auto-redaction. ✅ +② Independent design space: log format has its own evaluation dimensions. ✅ +③ Persistent trade-off: ❌ Structured JSON dominates plain text on all concerns; + this is not a mutually-constraining trade-off — one solution simply hasn't evolved yet. +Decision: ❌ Does not qualify; record plain-text format as historical note + +Counter-example 4 — fails ④ (auxiliary) +Candidate group "Agent Process Startup Order": +① ✅ ② ✅ ③ ✅ +④ Sustainable extensibility: ⚠️ As async runtimes proliferate, this problem space may converge quickly. +Decision: ⚠️ Create page tentatively; note "low extensibility expectation" in evolve signal file + +### Example Domain 2: Embedded Databases + +Input Entities (cross-repo): +- SQLite: B-Tree (storage structure), WAL (write-ahead log) +- LevelDB: LSM-Tree (storage structure), MemTable+SSTable (write pipeline) +- RocksDB: ColumnFamily, Compaction (compaction strategy), BloomFilter + +Positive example — "Storage Engine Core Data Structure": +① B-Tree vs LSM-Tree, fundamentally different design philosophies. ✅ +② Independent evaluation dimensions from persistence strategy. ✅ +③ Read-optimized vs. write-optimized; classic no-silver-bullet trade-off. ✅ +Decision: ✅ Create new Concept page storage-engine-data-structure + +Counter-example — fails ①: +Candidate group "Bloom Filter Strategy": +① ❌ Only RocksDB has BloomFilter. +Decision: ❌ Enter seed bank for observation + +## Four case types + +Case A — Hits existing problem space + Core problem is the same as an existing Concept page + Action: mark "append to " + +Case B — New problem space, passes all three hard thresholds + Action: mark "create new Concept page," with one-sentence justification for each criterion + +Case C — Pending observation + Currently only one repo faces this problem + Action: enter seed bank; do not promote + +Case D — Evolve signal + Partially overlaps with existing Concept but does not fully match + Action: record as evolve signal; do not enter this round's writing + +## Output + +File 1: seeds/-candidates.md + Each entry: case type / problem name / target slug (Case A) or new name (Case B) / justification + + End-of-file capability coverage table (for human review): + | Capability Domain | | | | + |-------------------|---------|---------|------------| + | | ✅/— | ✅/— | ✅/— | + +File 2: evolve-signals/-.md + Only Type D signals, each: + - Problem: + - Related Concept: + - Signal type: granularity mismatch / merge candidate / split candidate + - Reason: +``` + +--- + +## ★ Pause Point 2: Candidate List Confirmation + +After Step 3 completes, present: + +``` +Concept candidate list: + + Type A (append to existing page): + - → [[]] + ... + + Type B (create new Concept page): + - (new ) + Justification: ① + ... + + Type C (pending observation): + Type D (evolve signals): , written to evolve-signals/ + +Capability coverage table: +| Capability Domain | Repo A | Repo B | New Repo | +|-------------------|--------|--------|----------| +| ... | ... | ... | ... | + +Adjust anything? Confirm to proceed to Step 4. +``` + +User can: veto a Type B creation / manually promote a Type C / adjust slug naming. +No response from user → auto-continue. + +**If --auto: skip user confirmation, proceed to Step 4 directly with model-determined A/B/C/D classification. Still output candidate list summary and capability coverage table to log.** + +--- + +## Step 4: Concept Writing + +For each Type A/B entry in candidates.md, launch an independent agent: + +``` +You are responsible for writing or updating a Concept page. Process one Concept at a time. + +## Who is the reader + +Framework Builder — someone studying the design space of this framework class. +They don't need to know which is "best"; they need a complete design space map: +on this design problem, what choices did different frameworks make, and what is the cost of each choice. + +## Input + +- A single entry from the candidate list (Case A or B) +- Entry source: seeds/-candidates.md +- If Case A: existing Concept page wiki/concepts/.md +- Source directory (must read source to verify; don't rely solely on mapping results) + +## Mandatory rules + +1. Every claim must have source evidence ^[file-path:line] +2. Case A (append): only add new repo content; do NOT modify existing repo content +3. Concept naming format: - (lowercase kebab-case) +4. Comparison table focuses on tensions between concerns, not a feature list +5. **All repo solutions must read the corresponding entity page to obtain source citations; reproducing from memory is not permitted.** New repo: read `wiki/repos//entities/.md`; existing repos: read `wiki/repos//entities/.md`. Format: one section per repo, section header `### `, first line of body `Source: [[repos//entities/]]`, each key mechanism annotated with `^[file-path:line]`. +6. **After writing this page, check that the `repos:` frontmatter lists every `### ` section in the body.** If the body discusses N repos, repos must contain N entries. + +## Output + +Path: wiki/concepts/.md + +--- +type: concept +concept: +problem: +concerns: [, ] +repos: [] +generated: +--- + +# + +## Core Problem + + + + +## Concerns + + + +## Solutions by Framework + +### + +Source: [[repos//entities/]] +**Solution**: +**Implementation**: ^[file-path:line] +**Trade-offs**: which concerns are satisfied, at what cost + +[one section per repo] + +## Comparison + +| Framework | Concern A | Concern B | Concern C | +|-----------|-----------|-----------|-----------| + +## Evolution Log + +- : Initial creation, covers +- : Added + +## Post-write action + +After writing this page, go to **every repo mentioned** in the body's entity pages (`wiki/repos//entities/.md`) and append a backlink at the end — not just the new repo; existing repos referenced in the body also need backlinks. +If the entity already has backlinks from other concepts, append to the existing list; if none, create a new list: + +``` +**Associated Concepts**: +- [[concepts/]] +``` + +Note: an entity may be associated with multiple concepts — e.g., MemoryManager involves both memory-backend-replaceability and state-synchronization. When appending, do not overwrite existing entries. +``` + +--- + +## Step 5: Verification + Repair (when `--verify` is enabled) + +``` +## Verification agent + +Input: wiki/concepts/.md + corresponding repo source + +For each repo's solution, verify one by one: +1. Source evidence exists (path:line can be found) +2. Description matches source (no exaggeration, no omission of key constraints) +3. Comparison table judgments have source support + +Output verification report: + ✅ Accurate + ⚠️ : partially inaccurate, repairable + ❌ : critically wrong, needs rewrite + +--- + +## Repair agent + +Input: verification report + wiki/concepts/.md + source + +Only repair ⚠️ or ❌ items. +Do not modify content that passed verification. +Every modification must include a repair reason. +``` + +--- + +## Step 6: Seed Bank Update + Snapshot Save + +``` +Complete the wrap-up work for this ingest. + +## Input + +- Problem space mapping: seeds/-problem-map.md +- Candidate list: seeds/-candidates.md +- Existing seed bank: seeds/master.md (skip if doesn't exist) +- Source directory: + +## Six operations + +1. Merge into seed bank + Append all problem space entries for (Type A/B/C all go in) to seeds/master.md + Annotate with source repo and case type + +2. Confirm evolve signal file + Verify evolve-signals/-.md exists and is complete + (generated in Step 3; this step only confirms integrity) + +3. Update wiki/index.md + - Repos section: add or update row (format per maintenance file spec) + - Concepts section: refresh Concept table per format (sync added/updated Concept rows) + +4. Overwrite wiki/hot.md: + # Hot Context + **Last operation:** ingest entities, concepts + **Active repos:** + **Concept pages:** + **Pending evolve signals:** (evolve-signals/) + +5. Append to wiki/log.md: + [] ingest entities, concepts updated/created + +6. Overwrite wiki/repos//.ingest-state.json: + Overwrite the snapshot with the files and SHA-256 hashes actually read during this ingest. + Write the current source directory path to source_path. + Populate entity_map: collect mapping from each entity page's frontmatter source_files field. + Format: { "": ["", ...], ... } + entity_map is used in Step 0 delta detection for reverse mapping — when a file changes, you know which entities are affected. + Not populating it forces scanning all entity page frontmatter on every re-ingest, unacceptable at 500-repo scale. +``` + +--- + +## Before Step 6 Wrap-up + +**REQUIRED SUB-SKILL:** Before claiming "ingest complete," must invoke `completion-gate` to verify maintenance file consistency. + +--- + +## ★ Pause Point 3: Ingest Completion Summary + +After Step 6 completes, present: + +``` +ingest complete: + - Entities extracted + - Concept pages updated/created + - evolve signals written to evolve-signals/-.md + +Suggested next steps: + 1. Trigger /evolve-apply to process evolve signals + 2. Continue ingesting next repo: + 3. Deep-dive current Concept: /query +``` + +**If --auto: don't wait for user decision; default to NOT triggering /evolve-apply. Still output completion summary and suggested next steps to log.** + +--- + +## File Structure Specification + +``` +wiki/ ← Persistent knowledge (committed to git) + repos/ + / + .ingest-state.json ← Step 6 maintained (delta snapshot) + overview.md ← Step 1 output (new or overwritten) + entities/ ← Step 1 output + .md + concepts/ ← Step 4 output + .md + +seeds/ ← Pipeline artifacts (project root, sibling to wiki/) + -problem-map.md ← Step 2 output + -candidates.md ← Step 3 output + master.md ← Step 6 maintained + +evolve-signals/ ← Signal inbox (project root, sibling to wiki/) + -.md ← Step 3 output (Type D signals) +``` + +### Path Discipline (Mandatory) + +Pipeline artifact (`seeds/` and `evolve-signals/`) path rules: +- **Must write to `seeds/` and `evolve-signals/` at project root, siblings to `wiki/`** +- **Never write under `wiki/repos//`** — that pollutes the wiki knowledge base, causing false lint positives and maintenance chaos +- `seeds/` and `evolve-signals/` are not wiki pages, not inside the Obsidian vault, do not need wikilinks +- Correct example: `seeds/hermes-agent-problem-map.md` ← ✅ +- Incorrect example: `wiki/repos/hermes-agent/seeds/hermes-agent-problem-map.md` ← ❌ + +## Maintenance File Specs + +The following three files are automatically maintained by the LLM after ingest / query / compare / evolve operations. +Format must be strictly followed — `/query` Level 1 index scan and `/lint` checks both depend on these formats. + +### wiki/index.md + +**Role**: Wiki directory page. The first read target in `/query`'s Level 1 index scan — the LLM uses it to determine "are there relevant pages worth digging into." + +**Format**: + +```markdown +# Codebase Wiki + +## Repos + +- [[/overview]] — — topics: — last ingest: + +## Concepts + +| Problem | Page | Covered Repos | +|---------|------|---------------| +| | [[concepts/]] | , | +| ... | ... | ... | + +## Views + +- [[views/]] — + +## Insights + +- [[insights/]] — — <YYYY-MM-DD> +``` + +### wiki/log.md + +**Role**: Operation log. `/lint` checks last operation time etc. + +**Format** (append only, never modify existing lines): + +``` +[YYYY-MM-DD HH:MM] <operation> <detail> +``` + +### wiki/hot.md + +**Role**: Hot context. Overwritten on every operation. `/lint` reads rapidly when checking staleness. + +**Format**: + +```markdown +# Hot Context + +**Last operation:** <most recent operation and result> +**Active repos:** <all currently ingested repos, comma-separated> +**Concept pages:** <N> +**Pending evolve signals:** <K> +``` diff --git a/plugins/codebase-wiki/commands/lint.md b/plugins/codebase-wiki/commands/lint.md new file mode 100644 index 0000000..52235a0 --- /dev/null +++ b/plugins/codebase-wiki/commands/lint.md @@ -0,0 +1,87 @@ +# /lint — Wiki Health Check + +Full-structural health check of the entire wiki. Manually invoked by the user. + +## Role + +`lint.py` is the comprehensive health-check tool for wiki structural integrity. It scans all wiki pages, checking wikilink validity, frontmatter compliance, repo consistency, orphan pages, provenance coverage, and view freshness. + +**Relationship to completion-gate:** The gate is a scoped self-check ("Did I do this operation correctly?"), lint is a full audit ("Is the entire wiki healthy?"). The gate does not auto-run lint. Users manually run `/lint` periodically for full health checks, or when the gate suggests it. + +## Trigger + +``` +/lint +/lint --fix # attempt auto-fix for INFO-level issues +``` + +## Execution Protocol + +### Step 1 — Run lint.py + +```bash +python scripts/lint.py --wiki wiki/ +``` + +### Step 2 — Frontmatter compliance check + +Scan all entity pages and concept pages, verify required fields. + +**Entity pages** (`wiki/repos/*/entities/*.md`) must each have: +- `type: entity` +- `repo:` — owning repo name +- `slug:` — entity identifier +- `problem:` — problem-level description, "how to..." form +- `source_files:` — source file list (at least one) +- `generated:` — generation date + +Missing any → `[ERROR] entity_missing_frontmatter` + +**Concept pages** (`wiki/concepts/*.md`) must each have: +- `type: concept` +- `concept:` — concept identifier +- `problem:` — core problem, one sentence +- `concerns:` — concern list (can be empty array `[]`) +- `repos:` — repo list +- `generated:` — generation date + +Missing any → `[ERROR] concept_missing_frontmatter` + +**Concept page structure check:** +Must contain `## Evolution Log` section. Missing → `[WARN] concept_missing_evolution_log` + +**Entity → Concept backlink check:** +For each concept page, take the repos in its `repos:` list, check that the corresponding source entity pages have `**Associated Concepts**: [[concepts/<slug>]]` at the end of the file. +Missing → `[WARN] entity_missing_concept_backlink` + +### Step 3 — Report findings + +Present a summary organized by severity: + +**Errors (must fix):** +- List each [ERROR] finding with file and detail + +**Warnings (should fix):** +- List each [WARN] finding + +**Info (optional):** +- List each [INFO] finding + +**Health score:** +- Wikilink integrity: X% (broken links / total links) +- Entity frontmatter compliance: X% +- Concept frontmatter compliance: X% +- Concept evolution log coverage: X% + +### Step 4 — Remediation guidance + +ERROR level: +- `check_broken_wikilinks` → check whether the target file exists. If pointing to old `nodes/` or `dimensions/` paths, these are legacy links — delete them directly +- `entity_missing_frontmatter` → fill in missing frontmatter fields for the entity page +- `concept_missing_frontmatter` → fill in missing frontmatter fields for the concept page + +WARN level: +- `concept_missing_evolution_log` → append `## Evolution Log` section to end of concept page (at minimum one initial-creation entry) +- `entity_missing_concept_backlink` → append `**Associated Concepts**: [[concepts/<slug>]]` to end of entity page + +Never auto-fix errors without user confirmation. Auto-fix only INFO-level issues with `--fix`. diff --git a/plugins/codebase-wiki/commands/query.md b/plugins/codebase-wiki/commands/query.md new file mode 100644 index 0000000..ff0f735 --- /dev/null +++ b/plugins/codebase-wiki/commands/query.md @@ -0,0 +1,85 @@ +# /query — Knowledge Query + +Answer questions using the wiki knowledge base. Follow the retrieval escalation chain — start cheap, escalate only as needed. + +## Trigger + +``` +/query <question> +/query --repo react,vue <question> +``` + +## Wikilink Traversal (structural questions first) + +If the question matches the following patterns, do wikilink traversal first rather than the Retrieval Escalation Chain: + +- "What does X affect?" / "What would changing X impact?" / "X's blast radius" +- "Why does X exist?" / "Why is X designed this way?" +- "Which other repos also have X?" / "How do different repos approach X?" + +### Why wikilink traversal suffices for these questions + +Because entity pages are connected through concept pages — entity → concept → other entities. +Following this network shows you "what different choices other repos made on the same problem," which is exactly what structural questions are asking. + +### Traversal steps + +1. Identify keywords from the question, scan `wiki/repos/*/entities/` and `wiki/concepts/`: + ```bash + # Find entity pages by keyword (read frontmatter problem: field) + grep -rl "关键词" wiki/repos/*/entities/ --include="*.md" | head -5 + ``` + +2. Read matching entity frontmatter (first 10 lines) — find the `problem:` field to filter by relevance + +3. From relevant entities, follow wikilinks: + - `[[concepts/<slug>]]` → read concept page → find other repos' entities via `[[repos/<name>/entities/<slug>]]` + - This yields: same problem, different repos, different solutions → structural answer + +4. If the traversal path found enough information → answer with provenance `^[file:line]`. If not enough → fall through to Retrieval Escalation Chain. + +## Retrieval Escalation Chain + +### Level 1 — Index Scan (cheapest) +Read `wiki/index.md`. If the question has obvious keyword matches in the Concepts table → go directly to the corresponding concept page. + +### Level 2 — Section Grepping (moderate cost) +```bash +grep -n "^## \|^### " wiki/concepts/<slug>.md +``` +Read only the most relevant sections of candidate concept pages, not the whole file. + +### Level 3 — Full Page Read (most expensive, last resort) +Read the entire entity page or concept page — only when Levels 1-2 still leave unanswered questions. + +## Post-Answer Content Accuracy Check + +After producing the answer, if Level 3 source-code reading reveals information that is more accurate or more complete than what the Concept page (or Entity page) currently states, this triggers the **wiki knowledge freshness closed-loop protocol**: + +- (a) Actively present the diff (old description → new description), inform the user of the discrepancy +- (b) After user confirmation, write the correction to the wiki page on the spot +- (c) After correction, regenerate the affected output +- (d) Log append: `[源码验证: <page> <section> 修正]` + +## Archive Decision + +After answering: + +``` +> Is this analysis worth archiving in the wiki? +> - A: Don't archive (answer already in existing pages, just a summary) +> - B: Supplement existing pages (found additions or corrections to existing Entity or Concept pages) +> - C: Create new Insight page (synthesis has standalone value) +``` + +### Option B — Supplement existing pages +Apply the content accuracy fix protocol above, then update `wiki/log.md`. + +### Option C — Create new Insight page +- Write `wiki/insights/<YYYY-MM-DD>-<slug>.md` with frontmatter: `type: insight`, original question, generation date, source array, provenance_repos array +- Add wikilinks to relevant entities and concepts +- Append to `wiki/log.md` with `[query]` tag +- Update `wiki/index.md` `## Insights` section + +### After B or C — completion-gate +Invoke `/completion-gate` as a REQUIRED SUB-SKILL before claiming completion. diff --git a/plugins/codebase-wiki/evolve-signals/.gitkeep b/plugins/codebase-wiki/evolve-signals/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/plugins/codebase-wiki/hooks/hooks.json b/plugins/codebase-wiki/hooks/hooks.json new file mode 100644 index 0000000..d5087e5 --- /dev/null +++ b/plugins/codebase-wiki/hooks/hooks.json @@ -0,0 +1,11 @@ +{ + "hooks": { + "SessionStart": [ + { + "type": "command", + "command": "${CLAUDE_PLUGIN_ROOT}/hooks/session-start.sh", + "async": false + } + ] + } +} \ No newline at end of file diff --git a/plugins/codebase-wiki/hooks/session-start.sh b/plugins/codebase-wiki/hooks/session-start.sh new file mode 100755 index 0000000..0e7fbb2 --- /dev/null +++ b/plugins/codebase-wiki/hooks/session-start.sh @@ -0,0 +1,51 @@ +#!/usr/bin/env bash +# Inject wiki current state into Claude Code SessionStart context. +# Output follows Claude Code hookSpecificOutput.additionalContext format. + +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +WIKI_ROOT="$(dirname "$SCRIPT_DIR")" +HOT_FILE="$WIKI_ROOT/wiki/hot.md" +LOG_FILE="$WIKI_ROOT/wiki/log.md" +EVOLVE_DIR="$WIKI_ROOT/evolve-signals" + +hot_content="" +if [ -f "$HOT_FILE" ]; then + hot_content="$(cat "$HOT_FILE")" +fi + +last_log_lines="" +if [ -f "$LOG_FILE" ]; then + last_log_lines="$(tail -3 "$LOG_FILE" 2>/dev/null || true)" +fi + +# Count pending evolve signals +evolve_count=0 +if [ -d "$EVOLVE_DIR" ]; then + evolve_count=$(ls "$EVOLVE_DIR"/*.md 2>/dev/null | wc -l | tr -d ' ') +fi + +# Build additionalContext safely: use python to produce valid JSON, +# avoiding shell interpolation of quotes/backslashes in content strings. +python3 - <<PYEOF +import json + +context = """## Codebase Wiki Status + +{hot} + +### Recent Operations + +{log} + +### Health + +Pending evolve signals: {evolve}""".format( + hot="""$hot_content""", + log="""$last_log_lines""", + evolve="""$evolve_count""", +) + +print(json.dumps({"additionalContext": context})) +PYEOF diff --git a/plugins/codebase-wiki/requirements.txt b/plugins/codebase-wiki/requirements.txt new file mode 100644 index 0000000..90b56d7 --- /dev/null +++ b/plugins/codebase-wiki/requirements.txt @@ -0,0 +1,2 @@ +# No external Python dependencies required. +# lint.py uses only stdlib modules. diff --git a/plugins/codebase-wiki/schema/CLAUDE.md b/plugins/codebase-wiki/schema/CLAUDE.md new file mode 100644 index 0000000..0e164b1 --- /dev/null +++ b/plugins/codebase-wiki/schema/CLAUDE.md @@ -0,0 +1,161 @@ +# Codebase-Wiki + +一个基于 Karpathy LLM Wiki 模式构建的代码知识累积系统,以 Claude Code 插件形式分发。 + +## 这是什么 + +不是图谱数据库,不是一个有类型系统的知识工程。**是一个会生长的 markdown 目录。** 每次 ingest 一个代码仓库,LLM 不只是索引源码等以后检索——它读源码、提取关键信息、整合进已有的 wiki 页面、更新关联页面的交叉引用、标记新旧矛盾、强化或挑战已有的分析。知识编译一次然后持续保鲜,不是每次查询都重新推导。 + +核心机制是 **wikilink 网络**——entity 页链到 concept 页、concept 页链回 entity 页、overview 链到 entity。这个 wikilink 网络本身就是唯一的"图谱":Obsidian Graph View 里看到的就是它,LLM 在回答查询时沿着它遍历的就是它。不需要额外一层结构化图谱数据。 + +## 架构 + +``` +Raw Sources → 代码仓库(不可变,LLM 只读) +The Wiki → LLM 拥有并维护的所有 markdown(wiki/ 目录) +The Schema → 告诉 LLM 怎么结构化、什么约定、什么流程(schema/ + CLAUDE.md) +The Skills → 每个 Claude Code skill(commands/*.md)是 LLM 的操作手册 +``` + +## 管线 + +``` +Source → Entity(源码中可定位的模块,有独立职责边界) + → Problem Space Mapping(将单仓库 entity 翻译成跨仓库的"如何..."问题) + → Concept(跨仓库的问题空间,含各框架解法 + 对比表,四条准则筛选) + → Insights(按需:/query 存档 / /compare 存档) + +演化层: + → /evolve-apply(Wikipedia 风格 merge / split / redirect,Concept 页结构调整) +``` + +## Skills + +| Skill | 职责 | 读/写 | +|-------|------|------| +| `/ingest` | 从源码提取 entity → 映射问题空间 → 匹配 concept → 写 concept 页。ingest 完成后 wiki 是完整更新的。 | 写 | +| `/query` | 回答问题:wikilink 遍历 + 检索升级链。有价值的结果存档为 insight。 | 读为主 | +| `/compare` | 多仓库在同一问题上的对比:Concept → Entity → 源码 三级升级链。 | 读为主 | +| `/lint` | 全量 wiki 结构性健康检查:wikilink 完整性 + frontmatter 合规 + repos 一致性 + 孤立页检测 + provenance 覆盖。用户手动触发。 | 读 | +| `/evolve-apply` | Wikipedia 风格 Concept 页演化:merge / split / redirect。独立工具,信号驱动或手动指定。 | 写 | +| `/completion-gate` | 共享质量门——所有写操作完成前必须通过。操作范围检查:本次写入文件正确性 + 维护文件同步 + frontmatter 合规 + 增量 wikilink 验证。不是用户直接调用,由其他 write skill 以 `REQUIRED SUB-SKILL` 引用。 | 质量门 | + +**各 skill 职责独立,不互相合并。** 不要用"当前规模小"论证合并的合理性——工具职责由功能决定,不由当前数据量决定。 + +## 人机分工 + +| | 人 | LLM | +|------|-----|-----| +| 角色 | 策展来源、引导方向、提出好问题、思考意义 | 写一切:提取、分类、交叉引用、标记冲突、演化操作 | +| 动作 | 「这部分再展开」「比较 A 和 B」「这个方向值得深挖」 | 自主完成一轮工作,在暂停点汇报,告诉人做了什么、发现了什么、建议下一步 | +| ingest 中 | 暂停点介入:确认问题空间覆盖、候选清单、后续方向 | 暂停点之外自主执行 | + +LLM 写、人读、人给方向、LLM 继续。不是人批准 LLM 的每一步产出。 + +## 关键设计原则 + +### 规模假设 +**这个项目面向数百个仓库、数百个 concept、数百个 views/insights 的规模设计。** 永远不要用"当前只有 N 个仓库所以简单方案够用"来为设计决策辩护。每个决策必须回答"如果 500 个仓库,这个方案还能工作吗?" + +### 知识组织 +1. **知识累积性优先。** 每次 ingest 的产出归档为 wiki 页面,每次有价值的查询结果也归档。不能消失在聊天历史里。 +2. **wikilink 网络就是图。** 不维护与 wikilink 重复的结构化图谱数据。 +3. **Concept 页是主战场。** 跨仓库知识的真正累积发生在这里——新 repo 进来,更新已有 Concept 的实例列表、对比表。Concept 页的 depth 增长就是知识网的成长。 +4. **index.md 只做路由。** 不列全部 concept——500 个 concept 时 index.md 仍然 < 600 行。LLM 读 index.md 后沿 wikilink 做语义路由。搜索靠 frontmatter + grep,不靠全量列表。 + +### Wikipedia 演化 +5. **演化是内置机制。** 不是一次性提取完美的 concept,是靠有规则的持续演化(merge/split/redirect)。D 类信号在 ingest 暂停点当场处理,不推迟到另一个命令。 +6. **/evolve-apply 是独立工具。** 既可以处理信号文件(信号驱动),也可以直接指定操作(用户手动),共享同一套前置判断+执行逻辑。 + +### 规模与性能 +7. **增量更新是基础设施,不是优化。** 没有 delta tracking,每次 ingest 全量重读 500 个仓库源码的 token 成本不可接受。per-repo `.ingest-state.json` + SHA-256 content hash。 +8. **搜索不靠全量扫描。** grep 能做精确匹配但做不了语义搜索。正确路径:index.md 路由 → wikilink 遍历 → frontmatter 定位 → LLM 判断相关性。不是把所有 concept 的 problem 字段都 grep 出来让 LLM 筛选。 +9. **批量 ingest 可并行。** 用户一次扔多个仓库时,Step 1(Entity 提取)和 Step 2(问题空间映射)仅依赖单仓库源码,多仓库可并行执行。Step 3(匹配)需要等待所有仓库的 Step 2 完成后再统一处理——因为需要跨仓库对比。并行 vs 串行的分界线是"是否需要跨仓库信息"。 + +### Skill 调用模型 +10. **Skill 是 LLM 的能力,不只是用户要记的命令。** 用户不需要显式输入 `/compare` 或 `/evolve-apply` 的完整语法。用户说"对比一下 A 和 B"、"把 memory 的两个 concept 合并",LLM 应自动识别意图并调用对应 skill。Skill 文件中 Trigger 节的语法是给 LLM 认路用的,不是给用户背的。 +11. **Skill 之间可以嵌套。** 用户在 `/compare` 过程中说"这两个 concept 合并",就是在 compare 流程中插入了 evolve 操作。这是正常的——LLM 在执行一个 skill 时收到了另一个意图,应该暂停当前 skill、执行嵌套操作、然后回到原 skill 继续。不需要等第一个 skill 完整结束。log.md 记录操作来源(如 `[触发: /compare 对话中]`)。 + +### 仓库分类 +12. **不设单一 category。** 仓库用 GitHub topics 做多标签(`ai-agent` `python` `event-driven`),tags 只帮发现,不限制对比范围。对比范围由 Concept 的 `repos:` 字段动态定义。 + +### 文件格式 +13. **Frontmatter 为模型设计。** entity 页和 concept 页的 YAML frontmatter 包含 `type`、`problem`、`repos` 等字段,模型读前 10 行即可判断文件类型和内容,不必读全文。 +14. **Wikilink 有语义。** 每个 wikilink 从"这里在提到什么,那个东西有独立页面"出发,不是为凑图而加。entity → concept、concept → entity、overview → entity 三类链接各有含义。 + +### 内容保鲜 +15. **任何交互中发现不准确就必须修正,且修正是原子闭环。** `/compare`、`/query`、或日常对话中,当 LLM 从源码拿到比 Concept 页(或 Entity 页)更准确/更完整的信息时,必须: + - (a) 主动展示 diff(旧描述 → 新描述),告知用户差异 + - (b) 用户确认后当场写入 wiki 页 + - (c) **如果是对比/查询过程中发现 → 修正后重新生成受影响的输出(如 `/compare` 重新计算对比、`/query` 刷新答案),不是修完就停** + - (d) 日志标记触发来源:在父 command 的日志行尾追加 `[源码验证: <页名> <节名>修正]` + + 这一条的全称是"wiki 知识保鲜闭环",是所有读-写 skill(`/compare`、`/query`)的共同行为规范。各 skill 文件中必须覆盖此行为。 + +### 插件通用性 +16. **这是个通用插件。** SKILL.md 中的所有路径使用相对于 wiki 根目录的相对路径(`wiki/concepts/`),绝不写死本地绝对路径如 `/Users/xxx/Work/codebase-wiki/`。 + +## 文件格式规范 + +### Entity 页 frontmatter +```yaml +--- +type: entity +repo: <name> +slug: <slug> +problem: <问题层一句话,"如何..."形式> +generated: <YYYY-MM-DD> +source_files: + - <repo-relative-path> +--- +``` + +### Concept 页 frontmatter +```yaml +--- +type: concept +concept: <slug> +problem: <核心问题,一句话> +concerns: [<关切1>, <关切2>] +repos: [<仓库列表>] +generated: <YYYY-MM-DD> +--- +``` + +### 重定向页 frontmatter +```yaml +--- +redirect_to: <slug-target> +reason: <一句话> +date: <YYYY-MM-DD> +--- +``` + +### 维护文件 +- `wiki/index.md`:路由页,Repos 表 + 搜索入口指引。不列全部 concept/view/insight +- `wiki/log.md`:只追加,每行 `[<ts>] <操作> <详情>` +- `wiki/hot.md`:覆盖写入,Last operation + Active repos + Concept count + Pending signals count + +## 工具调用规范 + +每轮工具调用必须分批执行,每批不超过 3-4 个并行调用。发出一批、等结果回来、再发下一批。 + +读大文件时使用 `offset`/`limit` 参数只读需要的部分;Bash 命令输出加 `| head -N` 限制长度,避免大量 token 一次性追加到上下文。 + +## 测试与验证规范 + +**所有测试、验证、场景模拟必须在独立 subagent 中执行,绝不在主上下文中直接执行。** + +原因:主上下文的 token 预算应留给决策、分析和编排。测试产生的噪音(大量的 lint 输出、文件读取、验证逻辑)会污染上下文,降低后续决策质量。subagent 有独立上下文窗口,测试在其内部完成,只把结论和摘要回报主 agent。 + +适用的操作类型: +- `/ingest` 的执行(Entity 提取、问题空间映射、Concept 写作)→ subagent +- `/lint` 的运行及结果分析 → subagent +- 场景模拟(如"模拟用户否决 Concept B 类")→ subagent +- 对比验证(如"与实验基线比较 entity 数量")→ subagent +- `pytest` 测试运行 → subagent +- 任何读取超过 10 个文件的操作 → subagent + +不适用:单文件查询、用户直接提问、摘要性任务(如"这个 concept 讲了什么")。 + +反例:`python scripts/lint.py 2>&1 | head -80` 直接在主上下文跑 → **禁止**。正确做法:启动 subagent 跑 lint,只返回摘要。 diff --git a/plugins/codebase-wiki/schema/concept-criteria.md b/plugins/codebase-wiki/schema/concept-criteria.md new file mode 100644 index 0000000..724950b --- /dev/null +++ b/plugins/codebase-wiki/schema/concept-criteria.md @@ -0,0 +1,145 @@ +# Concept Admission Criteria + +**Version:** v1.0 +**Role:** Determines whether a problem space entry should be promoted to an independent Concept page. Referenced by `/ingest` Step 3, `/evolve-apply` Split, and `/compare` Concept structure review — this is the single-source definition. + +--- + +## Hard Thresholds (all three must pass) + +### 1. Multiple Solutions + +At least two different repos solve the same problem in clearly different ways. + +Note: if after analysis one solution dominates another on all trade-off dimensions, this is not a genuine design trade-off — does not qualify. + +### 2. Independent Design Space + +This problem cannot be fully covered by an existing problem space — merging it in would cause its own discussion dimensions to disappear, and the Framework Builder would lose decision value on this problem. + +### 3. Persistent Trade-off + +No silver bullet across different solutions — satisfying Concern A increases the cost of satisfying Concern B, and vice versa. + +--- + +## Auxiliary Criterion (does not veto if unmet; affects priority) + +### 4. Sustainable Extensibility + +New repos are likely to contribute new solutions to this problem in the future. If no new repos join long-term, trigger "downgrade" evolution recommendation. + +--- + +## Four Case Types + +| Case | Condition | Action | +|------|-----------|--------| +| **A** | Hits existing Concept (core problem is the same) | Append to existing Concept page | +| **B** | New problem space, passes ①②③ | Create new Concept page | +| **C** | Currently only one repo faces this | Enter seed bank (`seeds/master.md`); do not promote | +| **D** | Partially overlaps with existing Concept but doesn't fully match | Record as evolve signal in `evolve-signals/` | + +--- + +## Few-shot Examples + +### Example Domain 1: AI Agent Frameworks + +Input Entities (cross-repo): + +- **OpenClaw**: Agent (YAML config), Workflow (explicit orchestration), Memory (external context injection), ToolTimeout (per-tool YAML timeout config) +- **HermesAgent**: Agent (@agent decorator), EventBus (event-driven coordination), Memory (internal state sync), ToolTimeout (per-toolset timeout) + +--- + +**Positive — "Agent Definition Style":** + +1. At least two repos, different approaches: config-driven vs. decorator-driven. ✅ +2. Independent design space: evaluation dimensions are declarative convenience vs. programming flexibility; does not share evaluation dimensions with "multi-Agent coordination." ✅ +3. Persistent trade-off: config is simple but inflexible vs. programming freedom with higher barrier; no silver bullet. ✅ +4. Sustainable extensibility: new frameworks will still make different choices on this question. ✅ + +Decision: ✅ Create new Concept page `agent-definition-style` + +--- + +**Counter-example 1 — fails 1: single repo only** + +Candidate group "HermesAgent SafeWriter pipeline protection": + +1. Multiple solutions: ❌ Only HermesAgent has this; other repos have no corresponding Entity. + +Decision: ❌ Enter seed bank for observation; does not qualify as a Concept + +--- + +**Counter-example 2 — fails 2: not an independent design space** + +Candidate group "Tool Execution Timeout Configuration": + +1. Multiple solutions: OpenClaw uses per-tool YAML timeout; HermesAgent uses per-toolset timeout. ✅ +2. Independent design space: ❌ "Timeout configuration" is a sub-dimension of the existing problem space "Tool Execution Safety & Control." Merging it in would not lose discussion dimensions. + +Decision: ❌ Does not qualify; handle as sub-dimension of "Tool Execution Safety" Concept + +--- + +**Counter-example 3 — fails 3: no genuine trade-off** + +Candidate group "Structured Log Format": + +1. Multiple solutions: OpenClaw uses plain text; HermesAgent uses structured JSON + auto-redaction. ✅ +2. Independent design space: log format has its own evaluation dimensions. ✅ +3. Persistent trade-off: ❌ Structured JSON dominates plain text on all concerns; this is not a mutually-constraining trade-off — one solution simply hasn't evolved yet. + +Decision: ❌ Does not qualify; record plain-text format as historical note + +--- + +**Counter-example 4 — fails 4 (auxiliary; does not veto but affects priority)** + +Candidate group "Agent Process Startup Order": + +1. ✅ 2. ✅ 3. ✅ +4. Sustainable extensibility: ⚠️ As async runtimes proliferate, this problem space may converge quickly. + +Decision: ⚠️ Create page tentatively; note "low extensibility expectation" in evolve signal file + +--- + +### Example Domain 2: Embedded Databases + +Input Entities (cross-repo): + +- **SQLite**: B-Tree (storage structure), WAL (write-ahead log) +- **LevelDB**: LSM-Tree (storage structure), MemTable+SSTable (write pipeline) +- **RocksDB**: ColumnFamily, Compaction (compaction strategy), BloomFilter + +--- + +**Positive — "Storage Engine Core Data Structure":** + +1. B-Tree vs LSM-Tree, fundamentally different design philosophies. ✅ +2. Independent evaluation dimensions from persistence strategy. ✅ +3. Read-optimized vs. write-optimized; classic no-silver-bullet trade-off. ✅ + +Decision: ✅ Create new Concept page `storage-engine-data-structure` + +--- + +**Counter-example — fails 1: single repo only** + +Candidate group "Bloom Filter Strategy": + +1. Multiple solutions: ❌ Only RocksDB has BloomFilter. + +Decision: ❌ Enter seed bank for observation + +--- + +## Criteria Use in Evolution + +`/evolve-apply` Split reuses ①②③ for precondition checks — the split-out sub-topic must independently pass all three hard thresholds to be allowed. + +`/compare` after completing a comparison uses these criteria to review the structural quality of involved Concepts: are there pages covering broadly divergent sub-topics (suggest split)? Are there pages that are essentially the same (suggest merge)? diff --git a/plugins/codebase-wiki/scripts/lint.py b/plugins/codebase-wiki/scripts/lint.py new file mode 100644 index 0000000..66713e7 --- /dev/null +++ b/plugins/codebase-wiki/scripts/lint.py @@ -0,0 +1,463 @@ +#!/usr/bin/env python3 +""" +lint.py — wiki health-check rules for the Entity→Concept architecture. + +Usage: + python scripts/lint.py [--wiki wiki/] + +Exit code: 0 if no errors, 1 if any [ERROR] found. + +Output format per finding: + [LEVEL] rule_name detail +""" +import argparse +import re +import sys +from pathlib import Path + +WIKILINK_RE = re.compile(r"\[\[([^\]]+)\]\]") +PROVENANCE_RE = re.compile(r"\^\[.+?\]") + + +def _read_wiki_pages(wiki_root: Path): + """Yield all .md files under wiki_root.""" + return list(wiki_root.rglob("*.md")) + + +def _strip_frontmatter(text: str) -> tuple: + """Return (body, frontmatter_dict). frontmatter_dict may be empty.""" + if not text.startswith("---"): + return text, {} + end = text.find("\n---", 3) + if end == -1: + return text, {} + fm_text = text[3:end] + body = text[end + 4:] + fm = {} + for line in fm_text.splitlines(): + if ":" in line: + k, _, v = line.partition(":") + fm[k.strip()] = v.strip() + return body, fm + + +def check_broken_wikilinks(wiki_root: Path) -> list: + """[ERROR] Any [[page]] whose file doesn't exist. + + Wikilink resolution (new architecture): + - [[repos/<name>/entities/<slug>]] → wiki/repos/<name>/entities/<slug>.md + - [[repos/<name>/overview]] → wiki/repos/<name>/overview.md + - [[concepts/<slug>]] → wiki/concepts/<slug>.md + - [[views/<filename>]] → wiki/views/<filename>.md + - [[insights/<filename>]] → wiki/insights/<filename>.md + """ + errors = [] + pages = _read_wiki_pages(wiki_root) + + # Build resolvable targets relative to wiki/ + wiki_strs = { + str(p.relative_to(wiki_root).with_suffix("")).lower().replace("\\", "/") + for p in pages + } + + def _resolves(target: str, page_rel: str) -> bool: + """Check if a wikilink target resolves to any known wiki page. + + Tries resolution strategies in order: + 1. Exact match against all relative paths + 2. Relative to the page's own directory + 3. Prepend 'repos/' (for shorthand like [[nanobot/overview]]) + 4. Prepend 'concepts/' (for shorthand like [[some-concept]]) + 5. Suffix match: for targets like [[entities/xxx]], find + any page whose path ends with /entities/xxx + """ + t = target.lower().replace("\\", "/") + + # Strategy 1: exact match + if t in wiki_strs: + return True + + # Strategy 2: relative to page's directory + page_dir = "/".join(page_rel.split("/")[:-1]) if "/" in page_rel else "" + if page_dir: + candidate = f"{page_dir}/{t}" + if candidate in wiki_strs: + return True + + # Strategy 3: prepend repos/ (e.g. [[nanobot/overview]] -> repos/nanobot/overview) + candidate = f"repos/{t}" + if candidate in wiki_strs: + return True + + # Strategy 4: prepend concepts/ + candidate = f"concepts/{t}" + if candidate in wiki_strs: + return True + + # Strategy 5: suffix match — for [[entities/xxx]] or [[repos/r/xxx]] + # Build a suffix index lazily + if not hasattr(_resolves, "suffix_index"): + suffix_index = {} + for w in wiki_strs: + parts = w.split("/") + # Index by last 2, 3, 4 segments + for n in (2, 3): + if len(parts) >= n: + key = "/".join(parts[-n:]) + suffix_index.setdefault(key, set()).add(w) + _resolves.suffix_index = suffix_index + + if t in _resolves.suffix_index: + return True + + return False + + for page in pages: + page_rel = str(page.relative_to(wiki_root)) + content = page.read_text(errors="replace") + for m in WIKILINK_RE.finditer(content): + target = m.group(1).strip() + # Skip pipeline intermediate paths (seeds/, evolve-signals/) + # — these are project-level artifacts, not wiki pages + if target.lower().split("/")[0] in {"seeds", "evolve-signals"}: + continue + if not _resolves(target, page_rel): + errors.append({ + "level": "ERROR", + "rule": "check_broken_wikilinks", + "file": page_rel, + "detail": f"[[{target}]] — target file not found", + }) + return errors + + +def check_orphan_pages(wiki_root: Path) -> list: + """[WARN] Pages not referenced by any other wiki page (excluding maintenance files).""" + maintenance_files = {"hot.md", "log.md", "index.md"} + pages = [p for p in _read_wiki_pages(wiki_root) + if p.name not in maintenance_files] + + # Build set of all link targets mentioned anywhere with resolution + linked = set() + for page in pages: + content = page.read_text(errors="replace") + for m in WIKILINK_RE.finditer(content): + target = m.group(1).strip().lower().replace("\\", "/") + linked.add(target) + # Also try common resolutions (same as check_broken_wikilinks) + page_rel = str(page.relative_to(wiki_root)) + page_dir = "/".join(page_rel.split("/")[:-1]) if "/" in page_rel else "" + if page_dir: + linked.add(f"{page_dir}/{target}") + linked.add(f"repos/{target}") + linked.add(f"concepts/{target}") + + # Build suffix index for cross-repo entity resolution (for wikilinks like [[entities/x]]) + suffix_index = {} + for p in pages: + rel = str(p.relative_to(wiki_root).with_suffix("")).replace("\\", "/") + parts = rel.split("/") + for n in (2, 3): + if len(parts) >= n: + key = "/".join(parts[-n:]) + suffix_index.setdefault(key, set()).add(rel) + + # Build aliases for each page (how it can be referenced) + def _page_aliases(rel: str) -> set: + parts = rel.split("/") + aliases = {rel} + if len(parts) >= 3 and parts[0] == "repos": + aliases.add("/".join(parts[1:])) # r/overview + if len(parts) >= 4 and parts[2] == "entities": + aliases.add("/".join(parts[2:])) # entities/x + return {a.lower() for a in aliases} + + def _is_linked(rel: str) -> bool: + """Check if any alias of this page appears in linked set.""" + for alias in _page_aliases(rel): + if alias in linked: + return True + # Check suffix-index entries for this alias + parts = alias.split("/") + for n in (2, 3): + if len(parts) >= n: + key = "/".join(parts[-n:]) + if key in suffix_index: + if any(suffix_alias in linked for suffix_alias in suffix_index[key]): + return True + return False + + # Also scan index.md wikilinks into the linked set (even though index.md + # itself is excluded from the pages list) + index = wiki_root / "index.md" + if index.exists(): + for m in WIKILINK_RE.finditer(index.read_text(errors="replace")): + target = m.group(1).strip().lower().replace("\\", "/") + linked.add(target) + linked.add(f"repos/{target}") + linked.add(f"concepts/{target}") + + warnings = [] + index_content = index.read_text(errors="replace") if index.exists() else "" + exclude_dirs = {"seeds", "evolve-signals"} + + for page in pages: + if page == index: + continue + # Skip pipeline intermediates + if any(d in page.parts for d in exclude_dirs): + continue + rel = str(page.relative_to(wiki_root).with_suffix("")).replace("\\", "/") + if not _is_linked(rel) and rel.lower() not in index_content.lower(): + warnings.append({ + "level": "WARN", + "rule": "check_orphan_pages", + "file": str(page.relative_to(wiki_root)), + "detail": rel, + }) + return warnings + + +def check_missing_provenance(wiki_root: Path) -> list: + """[WARN] Entity/Concept pages with no ^[...] provenance references. + + Checks entity pages (wiki/repos/*/entities/*.md) and + concept pages (wiki/concepts/*.md). Skips redirect pages. + """ + warnings = [] + for page in _read_wiki_pages(wiki_root): + body, fm = _strip_frontmatter(page.read_text(errors="replace")) + page_type = fm.get("type", "") + + # Only check entity and concept pages + if page_type not in ("entity", "concept"): + continue + + # Skip redirect pages + if fm.get("redirect_to"): + continue + + if not PROVENANCE_RE.search(body): + warnings.append({ + "level": "WARN", + "rule": "check_missing_provenance", + "file": str(page.relative_to(wiki_root)), + "detail": "no ^[file:line] provenance references found", + }) + return warnings + + +def check_concept_repos_consistency(wiki_root: Path) -> list: + """[ERROR/WARN] Concept pages: frontmatter repos: must match body content. + + Three checks: + 1. [ERROR] Body repo section (### <name>) not in frontmatter repos: → + Concept page discusses a repo but frontmatter doesn't list it. + Causes Obsidian Graph isolation — the repo's entities can't link back. + 2. [WARN] Frontmatter repo not in body wikilinks ([[repos/X/entities/Y]]): → + Frontmatter claims coverage but body has no source evidence from that repo. + 3. [WARN] Body wikilink repo not in frontmatter: → + Body links to an entity whose repo isn't declared in frontmatter. + """ + errors = [] + warnings = [] + concepts_root = wiki_root / "concepts" + if not concepts_root.exists(): + return [] + + # Build known repo names from wiki/repos/ directories + repos_root = wiki_root / "repos" + known_repos = set() + if repos_root.exists(): + for d in repos_root.iterdir(): + if d.is_dir(): + known_repos.add(d.name) + + for page in concepts_root.rglob("*.md"): + body, fm = _strip_frontmatter(page.read_text(errors="replace")) + if fm.get("type") != "concept" or fm.get("redirect_to"): + continue + + page_rel = str(page.relative_to(wiki_root)) + + # Parse frontmatter repos: field (supports both [a, b] and [a,b]) + repos_raw = fm.get("repos", "") + front_repos = set() + if repos_raw: + # Strip brackets and split + cleaned = repos_raw.strip().lstrip("[").rstrip("]") + for r in cleaned.split(","): + r = r.strip() + if r: + front_repos.add(r) + + # Extract wikilink repos: [[repos/<repo>/entities/<slug>]] + wiki_repos = set() + for m in WIKILINK_RE.finditer(body): + target = m.group(1).strip() + parts = target.split("/") + if len(parts) >= 3 and parts[0] == "repos" and parts[2] == "entities": + wiki_repos.add(parts[1]) + + # Extract section-header repos: ### <name> where name is a known repo + section_repos = set() + for line in body.splitlines(): + stripped = line.strip() + if stripped.startswith("### ") or stripped.startswith("#### "): + header_text = stripped.lstrip("#").strip() + # Check if this header names a known repo + if header_text in known_repos: + section_repos.add(header_text) + + # Check 1: [ERROR] section repo not in frontmatter + missing_from_front = section_repos - front_repos + if missing_from_front: + errors.append({ + "level": "ERROR", + "rule": "check_concept_repos_consistency", + "file": page_rel, + "detail": f"body has ### section(s) for {sorted(missing_from_front)} " + f"but frontmatter repos: lists {sorted(front_repos)}. " + f"Add missing repos to frontmatter or add wikilinks to entity pages.", + }) + + # Check 2: [WARN] frontmatter repo has no wikilink in body + missing_wikilinks = front_repos - wiki_repos + if missing_wikilinks: + warnings.append({ + "level": "WARN", + "rule": "check_concept_repos_consistency", + "file": page_rel, + "detail": f"frontmatter repos: {sorted(front_repos)} but no " + f"[[repos/X/entities/Y]] wikilink found for {sorted(missing_wikilinks)}. " + f"Body should link to entity pages for each listed repo.", + }) + + # Check 3: [WARN] wikilink repo not in frontmatter + missing_from_wiki = wiki_repos - front_repos + if missing_from_wiki: + warnings.append({ + "level": "WARN", + "rule": "check_concept_repos_consistency", + "file": page_rel, + "detail": f"body has wikilinks to {sorted(missing_from_wiki)} entities " + f"but frontmatter repos: lists {sorted(front_repos)}. " + f"Add repos to frontmatter for complete coverage.", + }) + + return errors + warnings + + +def check_views_freshness(wiki_root: Path) -> list: + """[INFO] views/ pages older than any of their source pages. + + sources frontmatter must be a single-line JSON array, e.g.: + sources: ["wiki/concepts/memory-backend.md","wiki/repos/nanobot/entities/memory.md"] + """ + import json as _json + infos = [] + views_root = wiki_root / "views" + if not views_root.exists(): + return [] + for view_page in views_root.rglob("*.md"): + _, fm = _strip_frontmatter(view_page.read_text(errors="replace")) + generated = fm.get("generated") + if not generated: + continue + sources_raw = fm.get("sources", "[]").strip() + try: + source_paths = _json.loads(sources_raw) + except (_json.JSONDecodeError, TypeError): + continue + for src_rel in source_paths: + src = wiki_root.parent / src_rel + if src.exists(): + _, src_fm = _strip_frontmatter(src.read_text(errors="replace")) + src_gen = src_fm.get("generated", "") + if src_gen > generated: + infos.append({ + "level": "INFO", + "rule": "check_views_freshness", + "file": str(view_page.relative_to(wiki_root)), + "detail": f"source {src.name} (generated {src_gen}) is newer than this view ({generated})", + }) + return infos + + +def check_pipeline_file_placement(wiki_root: Path) -> list: + """[ERROR] Pipeline intermediate files (seeds/, evolve-signals/) misplaced under wiki/. + + Pipeline intermediates belong at project root (alongside wiki/), not under wiki/repos/<name>/. + If found under wiki/, it means an ingest subagent wrote to the wrong path. + + Checks both directories and individual files. + """ + errors = [] + project_root = wiki_root.parent + + # Check for misplaced directories under wiki/repos/<name>/ + for repo_dir in (wiki_root / "repos").iterdir() if (wiki_root / "repos").exists() else []: + if not repo_dir.is_dir(): + continue + for bad_dir in ("seeds", "evolve-signals"): + misplaced = repo_dir / bad_dir + if misplaced.exists(): + files_inside = list(misplaced.rglob("*.md")) + errors.append({ + "level": "ERROR", + "rule": "check_pipeline_file_placement", + "file": str(misplaced.relative_to(wiki_root)), + "detail": f"pipeline directory '{bad_dir}/' misplaced under wiki/repos/. " + f"Should be at project root: {bad_dir}/. " + f"({len(files_inside)} files inside: {', '.join(f.name for f in files_inside)})", + }) + + # Also check for individual pipeline files directly under wiki/repos/ (without a subdirectory) + for bad_dir in ("seeds", "evolve-signals"): + misplaced_in_wiki = wiki_root / bad_dir + if misplaced_in_wiki.exists(): + errors.append({ + "level": "ERROR", + "rule": "check_pipeline_file_placement", + "file": str(misplaced_in_wiki.relative_to(project_root)), + "detail": f"pipeline directory '{bad_dir}/' misplaced under wiki/. " + f"Should be at project root: {bad_dir}/.", + }) + + return errors + + +def run_all(wiki_root: Path) -> list: + findings = [] + findings += check_broken_wikilinks(wiki_root) + findings += check_pipeline_file_placement(wiki_root) + findings += check_orphan_pages(wiki_root) + findings += check_concept_repos_consistency(wiki_root) + findings += check_missing_provenance(wiki_root) + findings += check_views_freshness(wiki_root) + return findings + + +def main(): + parser = argparse.ArgumentParser(description="Lint codebase wiki health.") + parser.add_argument("--wiki", default="wiki", help="Path to wiki/ root") + args = parser.parse_args() + + wiki_root = Path(args.wiki) + + findings = run_all(wiki_root) + + has_error = False + for f in findings: + print(f"[{f['level']}] {f['rule']} {f['file']} — {f['detail']}") + if f["level"] == "ERROR": + has_error = True + + if not findings: + print("✓ No issues found.") + + sys.exit(1 if has_error else 0) + + +if __name__ == "__main__": + main() diff --git a/plugins/codebase-wiki/seeds/.gitkeep b/plugins/codebase-wiki/seeds/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/plugins/codebase-wiki/tests/test_lint.py b/plugins/codebase-wiki/tests/test_lint.py new file mode 100644 index 0000000..3c63cbd --- /dev/null +++ b/plugins/codebase-wiki/tests/test_lint.py @@ -0,0 +1,155 @@ +# tests/test_lint.py +import sys +from pathlib import Path +sys.path.insert(0, str(Path(__file__).parent.parent / "scripts")) + +from lint import ( + check_broken_wikilinks, + check_orphan_pages, + check_missing_provenance, + check_views_freshness, +) + + +def write(path: Path, content: str): + path.parent.mkdir(parents=True, exist_ok=True) + path.write_text(content) + + +# ---- check_broken_wikilinks ---- + +def test_check_broken_wikilinks_entity_missing(tmp_path): + """[[repos/hermes/entities/memory]] resolves to wiki/repos/hermes/entities/memory.md.""" + write(tmp_path / "wiki/repos/openclaw/entities/agent.md", + "See also [[repos/hermes/entities/memory]]") + errors = check_broken_wikilinks(tmp_path / "wiki") + assert len(errors) == 1 + assert "repos/hermes/entities/memory" in errors[0]["detail"] + + +def test_check_broken_wikilinks_entity_present(tmp_path): + write(tmp_path / "wiki/repos/openclaw/entities/agent.md", + "See also [[repos/hermes/entities/memory]]") + write(tmp_path / "wiki/repos/hermes/entities/memory.md", "# Memory") + errors = check_broken_wikilinks(tmp_path / "wiki") + assert errors == [] + + +def test_check_broken_wikilinks_concept_resolves(tmp_path): + """[[concepts/memory-backend]] resolves to wiki/concepts/memory-backend.md.""" + write(tmp_path / "wiki/repos/openclaw/entities/memory.md", + "**关联 Concept**:[[concepts/memory-backend]]") + write(tmp_path / "wiki/concepts/memory-backend.md", "# Memory Backend") + errors = check_broken_wikilinks(tmp_path / "wiki") + assert errors == [] + + +def test_check_broken_wikilinks_concept_missing(tmp_path): + write(tmp_path / "wiki/repos/openclaw/entities/memory.md", + "**关联 Concept**:[[concepts/nonexistent]]") + errors = check_broken_wikilinks(tmp_path / "wiki") + assert len(errors) == 1 + assert "concepts/nonexistent" in errors[0]["detail"] + + +def test_check_broken_wikilinks_overview_resolves(tmp_path): + write(tmp_path / "wiki/concepts/agent-style.md", + "来源:[[repos/openclaw/overview]]") + write(tmp_path / "wiki/repos/openclaw/overview.md", "# OpenClaw") + errors = check_broken_wikilinks(tmp_path / "wiki") + assert errors == [] + + +# ---- check_orphan_pages ---- + +def test_check_orphan_pages(tmp_path): + write(tmp_path / "wiki/index.md", "# Index\n- [[repos/openclaw/overview]]") + write(tmp_path / "wiki/repos/openclaw/overview.md", "# OpenClaw") + write(tmp_path / "wiki/repos/openclaw/entities/isolated.md", "# Nobody links me") + warnings = check_orphan_pages(tmp_path / "wiki") + orphan_paths = [w["detail"] for w in warnings] + assert any("isolated" in p for p in orphan_paths) + + +def test_check_orphan_pages_skip_maintenance(tmp_path): + """hot.md, log.md, index.md should never be flagged as orphan.""" + write(tmp_path / "wiki/hot.md", "# Hot") + write(tmp_path / "wiki/log.md", "# Log") + warnings = check_orphan_pages(tmp_path / "wiki") + orphan_names = [Path(w["file"]).name for w in warnings] + assert "hot.md" not in orphan_names + assert "log.md" not in orphan_names + + +# ---- check_missing_provenance ---- + +def test_check_missing_provenance_entity(tmp_path): + write(tmp_path / "wiki/repos/openclaw/entities/agent.md", + "---\ntype: entity\nrepo: openclaw\nslug: agent\nproblem: 如何定义Agent\n" + "generated: 2026-06-25\nsource_files:\n - src/agent.ts\n---\n" + "# Agent\n\nOpenClaw uses YAML config to define agents.") + warnings = check_missing_provenance(tmp_path / "wiki") + assert len(warnings) == 1 + assert "agent.md" in warnings[0]["file"] + + +def test_check_missing_provenance_entity_passes(tmp_path): + write(tmp_path / "wiki/repos/openclaw/entities/agent.md", + "---\ntype: entity\nrepo: openclaw\nslug: agent\nproblem: 如何定义Agent\n" + "generated: 2026-06-25\nsource_files:\n - src/agent.ts\n---\n" + "# Agent\n\nOpenClaw uses YAML. ^[src/agent.ts:42-58]") + warnings = check_missing_provenance(tmp_path / "wiki") + assert warnings == [] + + +def test_check_missing_provenance_concept(tmp_path): + write(tmp_path / "wiki/concepts/agent-style.md", + "---\ntype: concept\nconcept: agent-style\nproblem: 如何定义Agent\n" + "concerns: [声明式便捷性, 编程灵活性]\nrepos: [openclaw]\ngenerated: 2026-06-25\n---\n" + "# Agent Style\n\n配置驱动提供简单性但灵活性受限。") + warnings = check_missing_provenance(tmp_path / "wiki") + assert len(warnings) == 1 + assert "agent-style.md" in warnings[0]["file"] + + +def test_check_missing_provenance_skips_redirect(tmp_path): + """Redirect pages should not be checked for provenance.""" + write(tmp_path / "wiki/concepts/old-name.md", + "---\nredirect_to: new-name\nreason: renamed\ndate: 2026-06-25\n---\n" + "# Old Name\n> 此页面已合并至 [[new-name]]。") + warnings = check_missing_provenance(tmp_path / "wiki") + assert warnings == [] + + +def test_check_missing_provenance_skips_non_entity_concept(tmp_path): + """Pages without type: entity or type: concept should be skipped.""" + write(tmp_path / "wiki/repos/openclaw/overview.md", + "---\ntype: overview\nrepo: openclaw\ngenerated: 2026-06-25\n---\n" + "# OpenClaw\nNo provenance here.") + warnings = check_missing_provenance(tmp_path / "wiki") + assert warnings == [] + + +# ---- check_views_freshness ---- + +def test_check_views_freshness_newer_source(tmp_path): + import json + write(tmp_path / "wiki/concepts/agent-style.md", + "---\ntype: concept\ngenerated: 2026-06-20\n---\n# Agent Style") + write(tmp_path / "wiki/views/categories/2026-06-15-compare.md", + f"---\ntype: view\nrepos: [openclaw]\ngenerated: 2026-06-15\n" + f'sources: {json.dumps(["wiki/concepts/agent-style.md"])}\n---\n# Compare') + infos = check_views_freshness(tmp_path / "wiki") + assert len(infos) == 1 + assert "newer" in infos[0]["detail"] + + +def test_check_views_freshness_fresh(tmp_path): + import json + write(tmp_path / "wiki/concepts/agent-style.md", + "---\ntype: concept\ngenerated: 2026-06-10\n---\n# Agent Style") + write(tmp_path / "wiki/views/categories/2026-06-15-compare.md", + f"---\ntype: view\nrepos: [openclaw]\ngenerated: 2026-06-15\n" + f'sources: {json.dumps(["wiki/concepts/agent-style.md"])}\n---\n# Compare') + infos = check_views_freshness(tmp_path / "wiki") + assert infos == [] diff --git a/plugins/codebase-wiki/wiki/.obsidian/app.json b/plugins/codebase-wiki/wiki/.obsidian/app.json new file mode 100644 index 0000000..a7a2e1a --- /dev/null +++ b/plugins/codebase-wiki/wiki/.obsidian/app.json @@ -0,0 +1,9 @@ +{ + "userIgnoreFilters": [ + "hot.md", + "log.md", + "index.md", + ".hashes.json", + "graph.json" + ] +} \ No newline at end of file diff --git a/plugins/codebase-wiki/wiki/.obsidian/appearance.json b/plugins/codebase-wiki/wiki/.obsidian/appearance.json new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/plugins/codebase-wiki/wiki/.obsidian/appearance.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/plugins/codebase-wiki/wiki/.obsidian/core-plugins.json b/plugins/codebase-wiki/wiki/.obsidian/core-plugins.json new file mode 100644 index 0000000..30d4a91 --- /dev/null +++ b/plugins/codebase-wiki/wiki/.obsidian/core-plugins.json @@ -0,0 +1,33 @@ +{ + "file-explorer": true, + "global-search": true, + "switcher": true, + "graph": true, + "backlink": true, + "canvas": true, + "outgoing-link": true, + "tag-pane": true, + "footnotes": true, + "properties": true, + "page-preview": true, + "daily-notes": true, + "templates": true, + "note-composer": true, + "command-palette": true, + "slash-command": false, + "editor-status": true, + "bookmarks": true, + "markdown-importer": false, + "zk-prefixer": false, + "random-note": false, + "outline": true, + "word-count": true, + "slides": false, + "audio-recorder": false, + "workspaces": false, + "file-recovery": true, + "publish": false, + "sync": true, + "bases": true, + "webviewer": false +} \ No newline at end of file diff --git a/plugins/codebase-wiki/wiki/.obsidian/workspace.json b/plugins/codebase-wiki/wiki/.obsidian/workspace.json new file mode 100644 index 0000000..d8aacca --- /dev/null +++ b/plugins/codebase-wiki/wiki/.obsidian/workspace.json @@ -0,0 +1,201 @@ +{ + "main": { + "id": "fa0fd9619b012c4e", + "type": "split", + "children": [ + { + "id": "6efb66632424ef28", + "type": "tabs", + "children": [ + { + "id": "7fc803dfd2f17d13", + "type": "leaf", + "state": { + "type": "graph", + "state": {}, + "icon": "lucide-git-fork", + "title": "Graph view" + } + } + ] + } + ], + "direction": "vertical" + }, + "left": { + "id": "1bfaa82c12d406bd", + "type": "split", + "children": [ + { + "id": "f1a3100e895e55d7", + "type": "tabs", + "children": [ + { + "id": "6707340e6c42b3d5", + "type": "leaf", + "state": { + "type": "file-explorer", + "state": { + "sortOrder": "alphabetical", + "autoReveal": false + }, + "icon": "lucide-folder-closed", + "title": "Files" + } + }, + { + "id": "f1397355f7a98186", + "type": "leaf", + "state": { + "type": "search", + "state": { + "query": "", + "matchingCase": false, + "explainSearch": false, + "collapseAll": false, + "extraContext": false, + "sortOrder": "alphabetical" + }, + "icon": "lucide-search", + "title": "Search" + } + }, + { + "id": "1f09718ef60690ba", + "type": "leaf", + "state": { + "type": "bookmarks", + "state": {}, + "icon": "lucide-bookmark", + "title": "Bookmarks" + } + } + ] + } + ], + "direction": "horizontal", + "width": 282.5 + }, + "right": { + "id": "6d5e763cd2290a11", + "type": "split", + "children": [ + { + "id": "d79c9950d1b23736", + "type": "tabs", + "children": [ + { + "id": "66fce75d1803ed9d", + "type": "leaf", + "state": { + "type": "backlink", + "state": { + "file": "", + "collapseAll": false, + "extraContext": false, + "sortOrder": "alphabetical", + "showSearch": false, + "searchQuery": "", + "backlinkCollapsed": false, + "unlinkedCollapsed": true + }, + "icon": "links-coming-in", + "title": "Backlinks" + } + }, + { + "id": "1315f1887c385bae", + "type": "leaf", + "state": { + "type": "outgoing-link", + "state": { + "file": "", + "linksCollapsed": false, + "unlinkedCollapsed": true + }, + "icon": "links-going-out", + "title": "Outgoing links" + } + }, + { + "id": "ee568528acfb8c63", + "type": "leaf", + "state": { + "type": "tag", + "state": { + "sortOrder": "frequency", + "useHierarchy": true, + "showSearch": false, + "searchQuery": "" + }, + "icon": "lucide-tags", + "title": "Tags" + } + }, + { + "id": "7998f9db8f7db741", + "type": "leaf", + "state": { + "type": "all-properties", + "state": { + "sortOrder": "frequency", + "showSearch": false, + "searchQuery": "" + }, + "icon": "lucide-archive", + "title": "Properties" + } + }, + { + "id": "5ddc63a64f1c465c", + "type": "leaf", + "state": { + "type": "outline", + "state": { + "file": "", + "followCursor": false, + "showSearch": false, + "searchQuery": "" + }, + "icon": "lucide-list", + "title": "Outline" + } + }, + { + "id": "8331ccdccf548b8e", + "type": "leaf", + "state": { + "type": "footnotes", + "state": { + "file": "" + }, + "icon": "lucide-file-signature", + "title": "Footnotes" + } + } + ] + } + ], + "direction": "horizontal", + "width": 300, + "collapsed": true + }, + "left-ribbon": { + "hiddenItems": { + "switcher:Open quick switcher": false, + "graph:Open graph view": false, + "canvas:Create new canvas": false, + "daily-notes:Open today's daily note": false, + "templates:Insert template": false, + "command-palette:Open command palette": false, + "bases:Create new base": false + } + }, + "active": "6707340e6c42b3d5", + "lastOpenFiles": [ + "wiki/concepts", + "wiki/repos", + "wiki/views", + "wiki/index.md" + ] +} \ No newline at end of file diff --git a/plugins/codebase-wiki/wiki/concepts/.gitkeep b/plugins/codebase-wiki/wiki/concepts/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/plugins/codebase-wiki/wiki/hot.md b/plugins/codebase-wiki/wiki/hot.md new file mode 100644 index 0000000..8483f1d --- /dev/null +++ b/plugins/codebase-wiki/wiki/hot.md @@ -0,0 +1,6 @@ +# Hot Context + +**Last operation:** *(none)* +**Active repos:** *(none)* +**Concept pages:** 0 +**Pending evolve signals:** 0 diff --git a/plugins/codebase-wiki/wiki/index.md b/plugins/codebase-wiki/wiki/index.md new file mode 100644 index 0000000..2ea7dbc --- /dev/null +++ b/plugins/codebase-wiki/wiki/index.md @@ -0,0 +1,17 @@ +# Codebase Wiki + +## Repos + +*(no repos ingested yet — run `/ingest /path/to/your/repo <name>`)* + +## Concepts + +*(no concepts yet — concepts are created automatically during ingest)* + +## Views + +*(no views yet)* + +## Insights + +*(no insights yet)* diff --git a/plugins/codebase-wiki/wiki/log.md b/plugins/codebase-wiki/wiki/log.md new file mode 100644 index 0000000..a5526bf --- /dev/null +++ b/plugins/codebase-wiki/wiki/log.md @@ -0,0 +1,3 @@ +# Operation Log + +*(no operations yet)* diff --git a/plugins/codebase-wiki/wiki/repos/.gitkeep b/plugins/codebase-wiki/wiki/repos/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/plugins/codebase-wiki/wiki/views/.gitkeep b/plugins/codebase-wiki/wiki/views/.gitkeep new file mode 100644 index 0000000..e69de29 From 51e1a1ae92f5aa219e11db8912f44925c99dc6c3 Mon Sep 17 00:00:00 2001 From: miaoyuanli <miaoyuanli@gf.com.cn> Date: Tue, 30 Jun 2026 10:22:54 +0800 Subject: [PATCH 2/5] fix: sync marketplace description and keywords with plugin.json --- .claude-plugin/marketplace.json | 63 ++----------------- .../codebase-wiki/.claude-plugin/plugin.json | 2 +- 2 files changed, 7 insertions(+), 58 deletions(-) diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index ae4c15a..c8ad077 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -1,72 +1,21 @@ { - "name": "cc-plugins", + "name": "codebase-wiki", + "description": "Codebase knowledge accumulation system — analyze repos, build a structured wiki, compare architectures, and query design decisions", "owner": { - "name": "kossakovsky" - }, - "metadata": { - "description": "Community marketplace of plugins for Claude Code", - "version": "1.0.0" + "name": "myl17" }, "plugins": [ { "name": "codebase-wiki", - "description": "Codebase knowledge accumulation system — analyze repos, build a structured wiki, compare architectures, and query design decisions. Built on the LLM Wiki pattern.", + "description": "Codebase knowledge management plugin — analyze repos, build a structured wiki, compare architectures, query design decisions, and track staleness across your organization's repositories", "version": "0.2.0", + "source": "./", "author": { "name": "myl17" }, - "source": "./plugins/codebase-wiki", "category": "developer-tools", "tags": ["codebase", "wiki", "knowledge-management", "code-analysis"], - "keywords": ["codebase", "wiki", "knowledge", "ingest", "architecture", "comparison", "lint"] - }, - { - "name": "switch-provider", - "description": "Switch Claude Code between AI providers (Anthropic, Z.AI, Kimi, MiniMax) with a single command", - "version": "1.0.0", - "author": { - "name": "kossakovsky" - }, - "source": "./plugins/switch-provider", - "category": "ai-tools", - "tags": ["provider", "api", "configuration"], - "keywords": ["provider", "switch", "api", "zai", "kimi", "minimax"] - }, - { - "name": "plugin-development", - "description": "A comprehensive toolkit for creating, validating, and distributing Claude Code plugins", - "version": "1.3.0", - "author": { - "name": "kossakovsky" - }, - "source": "./plugins/plugin-development", - "category": "developer-tools", - "tags": ["development", "plugins", "scaffold", "validation", "tools"], - "keywords": ["claude-code", "plugins", "developer-tools", "plugin-development", "plugin-authoring", "scaffold", "scaffolding", "validation"] - }, - { - "name": "commit", - "description": "Smart git commits with conventional commit message generation", - "version": "1.0.0", - "author": { - "name": "kossakovsky" - }, - "source": "./plugins/commit", - "category": "developer-tools", - "tags": ["git", "commits", "conventional-commits"], - "keywords": ["git", "commit", "conventional-commits", "changelog"] - }, - { - "name": "writing-skills", - "description": "Create, test, and bulletproof Claude Code skills using TDD methodology. Based on obra/superpowers writing-skills", - "version": "1.0.0", - "author": { - "name": "obra" - }, - "source": "./plugins/writing-skills", - "category": "developer-tools", - "tags": ["skills", "tdd", "testing", "development"], - "keywords": ["skills", "tdd", "testing", "development"] + "keywords": ["codebase", "wiki", "knowledge-management", "code-analysis", "architecture", "comparison", "lint"] } ] } diff --git a/plugins/codebase-wiki/.claude-plugin/plugin.json b/plugins/codebase-wiki/.claude-plugin/plugin.json index 9feb492..96ab99c 100644 --- a/plugins/codebase-wiki/.claude-plugin/plugin.json +++ b/plugins/codebase-wiki/.claude-plugin/plugin.json @@ -1,7 +1,7 @@ { "name": "codebase-wiki", "description": "Codebase knowledge management plugin — analyze repos, build a structured wiki, compare architectures, query design decisions, and track staleness across your organization's repositories", - "version": "0.1.0", + "version": "0.2.0", "author": { "name": "myl17" }, From 6e117d1de1c62b868cb853b83bae780e1fdbf286 Mon Sep 17 00:00:00 2001 From: miaoyuanli <miaoyuanli@gf.com.cn> Date: Tue, 30 Jun 2026 10:23:26 +0800 Subject: [PATCH 3/5] Revert "fix: sync marketplace description and keywords with plugin.json" This reverts commit 51e1a1ae92f5aa219e11db8912f44925c99dc6c3. --- .claude-plugin/marketplace.json | 63 +++++++++++++++++-- .../codebase-wiki/.claude-plugin/plugin.json | 2 +- 2 files changed, 58 insertions(+), 7 deletions(-) diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index c8ad077..ae4c15a 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -1,21 +1,72 @@ { - "name": "codebase-wiki", - "description": "Codebase knowledge accumulation system — analyze repos, build a structured wiki, compare architectures, and query design decisions", + "name": "cc-plugins", "owner": { - "name": "myl17" + "name": "kossakovsky" + }, + "metadata": { + "description": "Community marketplace of plugins for Claude Code", + "version": "1.0.0" }, "plugins": [ { "name": "codebase-wiki", - "description": "Codebase knowledge management plugin — analyze repos, build a structured wiki, compare architectures, query design decisions, and track staleness across your organization's repositories", + "description": "Codebase knowledge accumulation system — analyze repos, build a structured wiki, compare architectures, and query design decisions. Built on the LLM Wiki pattern.", "version": "0.2.0", - "source": "./", "author": { "name": "myl17" }, + "source": "./plugins/codebase-wiki", "category": "developer-tools", "tags": ["codebase", "wiki", "knowledge-management", "code-analysis"], - "keywords": ["codebase", "wiki", "knowledge-management", "code-analysis", "architecture", "comparison", "lint"] + "keywords": ["codebase", "wiki", "knowledge", "ingest", "architecture", "comparison", "lint"] + }, + { + "name": "switch-provider", + "description": "Switch Claude Code between AI providers (Anthropic, Z.AI, Kimi, MiniMax) with a single command", + "version": "1.0.0", + "author": { + "name": "kossakovsky" + }, + "source": "./plugins/switch-provider", + "category": "ai-tools", + "tags": ["provider", "api", "configuration"], + "keywords": ["provider", "switch", "api", "zai", "kimi", "minimax"] + }, + { + "name": "plugin-development", + "description": "A comprehensive toolkit for creating, validating, and distributing Claude Code plugins", + "version": "1.3.0", + "author": { + "name": "kossakovsky" + }, + "source": "./plugins/plugin-development", + "category": "developer-tools", + "tags": ["development", "plugins", "scaffold", "validation", "tools"], + "keywords": ["claude-code", "plugins", "developer-tools", "plugin-development", "plugin-authoring", "scaffold", "scaffolding", "validation"] + }, + { + "name": "commit", + "description": "Smart git commits with conventional commit message generation", + "version": "1.0.0", + "author": { + "name": "kossakovsky" + }, + "source": "./plugins/commit", + "category": "developer-tools", + "tags": ["git", "commits", "conventional-commits"], + "keywords": ["git", "commit", "conventional-commits", "changelog"] + }, + { + "name": "writing-skills", + "description": "Create, test, and bulletproof Claude Code skills using TDD methodology. Based on obra/superpowers writing-skills", + "version": "1.0.0", + "author": { + "name": "obra" + }, + "source": "./plugins/writing-skills", + "category": "developer-tools", + "tags": ["skills", "tdd", "testing", "development"], + "keywords": ["skills", "tdd", "testing", "development"] } ] } diff --git a/plugins/codebase-wiki/.claude-plugin/plugin.json b/plugins/codebase-wiki/.claude-plugin/plugin.json index 96ab99c..9feb492 100644 --- a/plugins/codebase-wiki/.claude-plugin/plugin.json +++ b/plugins/codebase-wiki/.claude-plugin/plugin.json @@ -1,7 +1,7 @@ { "name": "codebase-wiki", "description": "Codebase knowledge management plugin — analyze repos, build a structured wiki, compare architectures, query design decisions, and track staleness across your organization's repositories", - "version": "0.2.0", + "version": "0.1.0", "author": { "name": "myl17" }, From 4a46c6ae9247bc1763e38ac3e718b55eb5056e8e Mon Sep 17 00:00:00 2001 From: miaoyuanli <miaoyuanli@gf.com.cn> Date: Tue, 30 Jun 2026 10:23:48 +0800 Subject: [PATCH 4/5] fix: sync marketplace description and keywords with plugin.json for CI cross-validation --- .claude-plugin/marketplace.json | 4 ++-- plugins/codebase-wiki/.claude-plugin/plugin.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index ae4c15a..32eb66c 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -10,7 +10,7 @@ "plugins": [ { "name": "codebase-wiki", - "description": "Codebase knowledge accumulation system — analyze repos, build a structured wiki, compare architectures, and query design decisions. Built on the LLM Wiki pattern.", + "description": "Codebase knowledge management plugin — analyze repos, build a structured wiki, compare architectures, query design decisions, and track staleness across your organization's repositories", "version": "0.2.0", "author": { "name": "myl17" @@ -18,7 +18,7 @@ "source": "./plugins/codebase-wiki", "category": "developer-tools", "tags": ["codebase", "wiki", "knowledge-management", "code-analysis"], - "keywords": ["codebase", "wiki", "knowledge", "ingest", "architecture", "comparison", "lint"] + "keywords": ["codebase", "wiki", "knowledge-management", "code-analysis", "architecture", "comparison", "lint"] }, { "name": "switch-provider", diff --git a/plugins/codebase-wiki/.claude-plugin/plugin.json b/plugins/codebase-wiki/.claude-plugin/plugin.json index 9feb492..96ab99c 100644 --- a/plugins/codebase-wiki/.claude-plugin/plugin.json +++ b/plugins/codebase-wiki/.claude-plugin/plugin.json @@ -1,7 +1,7 @@ { "name": "codebase-wiki", "description": "Codebase knowledge management plugin — analyze repos, build a structured wiki, compare architectures, query design decisions, and track staleness across your organization's repositories", - "version": "0.1.0", + "version": "0.2.0", "author": { "name": "myl17" }, From a9b30e025164d5dfdcb87d4bf57b5f59084852e5 Mon Sep 17 00:00:00 2001 From: miaoyuanli <miaoyuanli@gf.com.cn> Date: Tue, 30 Jun 2026 10:24:35 +0800 Subject: [PATCH 5/5] fix: add pytest to requirements.txt --- plugins/codebase-wiki/requirements.txt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/plugins/codebase-wiki/requirements.txt b/plugins/codebase-wiki/requirements.txt index 90b56d7..039d26e 100644 --- a/plugins/codebase-wiki/requirements.txt +++ b/plugins/codebase-wiki/requirements.txt @@ -1,2 +1 @@ -# No external Python dependencies required. -# lint.py uses only stdlib modules. +pytest>=8.0