graphwiki 0.2.0__tar.gz → 0.3.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (29) hide show
  1. {graphwiki-0.2.0 → graphwiki-0.3.0}/PKG-INFO +18 -12
  2. {graphwiki-0.2.0 → graphwiki-0.3.0}/README.md +17 -11
  3. {graphwiki-0.2.0 → graphwiki-0.3.0}/pyproject.toml +1 -1
  4. {graphwiki-0.2.0 → graphwiki-0.3.0}/src/graphwiki/cli.py +69 -10
  5. {graphwiki-0.2.0 → graphwiki-0.3.0}/src/graphwiki/logging_util.py +1 -5
  6. {graphwiki-0.2.0 → graphwiki-0.3.0}/src/graphwiki/render.py +10 -2
  7. {graphwiki-0.2.0 → graphwiki-0.3.0}/src/graphwiki/skill_install.py +1 -3
  8. graphwiki-0.3.0/src/graphwiki/skills/graphwiki/SKILL.md +220 -0
  9. {graphwiki-0.2.0 → graphwiki-0.3.0}/src/graphwiki/skills/graphwiki/references/invariants.md +11 -10
  10. graphwiki-0.3.0/src/graphwiki/skills/graphwiki/references/page-format.md +123 -0
  11. {graphwiki-0.2.0 → graphwiki-0.3.0}/src/graphwiki/writer.py +68 -0
  12. graphwiki-0.2.0/src/graphwiki/skills/graphwiki/SKILL.md +0 -202
  13. graphwiki-0.2.0/src/graphwiki/skills/graphwiki/references/page-format.md +0 -127
  14. {graphwiki-0.2.0 → graphwiki-0.3.0}/src/graphwiki/__init__.py +0 -0
  15. {graphwiki-0.2.0 → graphwiki-0.3.0}/src/graphwiki/__main__.py +0 -0
  16. {graphwiki-0.2.0 → graphwiki-0.3.0}/src/graphwiki/assets/README.md +0 -0
  17. {graphwiki-0.2.0 → graphwiki-0.3.0}/src/graphwiki/assets/mermaid.min.js +0 -0
  18. {graphwiki-0.2.0 → graphwiki-0.3.0}/src/graphwiki/bootstrap.py +0 -0
  19. {graphwiki-0.2.0 → graphwiki-0.3.0}/src/graphwiki/context.py +0 -0
  20. {graphwiki-0.2.0 → graphwiki-0.3.0}/src/graphwiki/emit.py +0 -0
  21. {graphwiki-0.2.0 → graphwiki-0.3.0}/src/graphwiki/gates.py +0 -0
  22. {graphwiki-0.2.0 → graphwiki-0.3.0}/src/graphwiki/graph_client.py +0 -0
  23. {graphwiki-0.2.0 → graphwiki-0.3.0}/src/graphwiki/mcp_server.py +0 -0
  24. {graphwiki-0.2.0 → graphwiki-0.3.0}/src/graphwiki/plan.py +0 -0
  25. {graphwiki-0.2.0 → graphwiki-0.3.0}/src/graphwiki/py.typed +0 -0
  26. {graphwiki-0.2.0 → graphwiki-0.3.0}/src/graphwiki/state.py +0 -0
  27. {graphwiki-0.2.0 → graphwiki-0.3.0}/src/graphwiki/types.py +0 -0
  28. {graphwiki-0.2.0 → graphwiki-0.3.0}/src/graphwiki/watch.py +0 -0
  29. {graphwiki-0.2.0 → graphwiki-0.3.0}/src/graphwiki/web.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: graphwiki
3
- Version: 0.2.0
3
+ Version: 0.3.0
4
4
  Summary: Graph-grounded code wiki generator: deterministic traversal, LLM writes prose only.
5
5
  Keywords: documentation,wiki,code-graph,llm,developer-tools
6
6
  Author: Mayur Pise
@@ -134,6 +134,7 @@ works.
134
134
  | `ollama` (default) | `http://localhost:11434/v1` | `gemma3` | No |
135
135
  | `nim` | `http://localhost:8000/v1` | `gemma` | No |
136
136
  | `offline` | none | none | No (no LLM at all) |
137
+ | `agent` | none — a coding assistant writes the prose | No (no LLM/egress); see [Coding-assisted mode](#coding-assisted-mode-no-llm-api) |
137
138
  | `grok` | `https://api.x.ai/v1` | `grok-4` | **Yes** — external SaaS |
138
139
  | `claude` | `https://api.anthropic.com/v1` | `claude-opus-4-8` | **Yes** — external SaaS |
139
140
  | `openai` | `https://api.openai.com/v1` | `gpt-4o` | **Yes** — external SaaS |
@@ -155,19 +156,22 @@ page is sent unless you pass `--allow-cloud-egress` or set
155
156
  egress is never silent). A raw non-local `--base-url` (an internal NIM/Triton
156
157
  box) is only logged, never blocked. Full policy: [DATA_GOVERNANCE.md](docs/DATA_GOVERNANCE.md).
157
158
 
158
- > **Note:** these are model **APIs**. Coding-agent CLIs (Codex CLI, Claude Code)
159
- > are deliberately not supported as backends: they are interactive, tool-wielding
160
- > agents, whereas the writer needs a stateless completion that is fed ONLY the
161
- > pre-assembled graph context and forbidden from traversing the invariant that
162
- > keeps citations verifiable. To use Claude, point `--base-url` at the Anthropic
163
- > API, not the CLI.
159
+ > **Note:** the cloud/local rows above are model **APIs** reached over HTTP
160
+ > don't point `--base-url` at an interactive coding-agent CLI. When you want a
161
+ > coding assistant to write the prose, use `--backend agent` (below): it feeds
162
+ > the assistant the SAME stateless, pre-assembled context the API writer gets
163
+ > via prompt files, with no traversal tools so citations stay verifiable.
164
164
 
165
165
  ### Coding-assisted mode (no LLM API)
166
166
 
167
- When no model endpoint is available, use the **graphwiki skill** with a coding
168
- assistant as the prose writer. Python still plans coverage and assembles context
169
- (`--backend offline`); the agent only rewrites narrative under the four `##`
170
- sections, then `graphwiki validate` gates the result.
167
+ When no model endpoint is available, use `--backend agent` (or the **graphwiki
168
+ skill**) to make a coding assistant the prose writer. Python still plans
169
+ coverage and assembles context; the tool writes the exact `(system prompt,
170
+ context)` pair the API writer sends into `wiki/.agent/prompts/<id>.md`, the
171
+ assistant writes the four `§SECTION:` blocks back into
172
+ `wiki/.agent/responses/<id>.md`, and a re-run feeds them through the same
173
+ `merge → gate → emit` path — the assistant never edits a page. `graphwiki
174
+ validate` gates the result.
171
175
 
172
176
  **Installers place the skill automatically** into every detected coding-assistant
173
177
  skill root (Claude Code, Cursor, GitHub Copilot, Grok, OpenAI Codex,
@@ -182,8 +186,10 @@ skill root (Claude Code, Cursor, GitHub Copilot, Grok, OpenAI Codex,
182
186
  graphwiki skill install # detected assistants
183
187
  graphwiki skill install --all # every known root
184
188
  graphwiki skill status
185
- graphwiki build . --backend offline --no-web
189
+ graphwiki build . --backend agent --no-web
186
190
  # In the assistant: /graphwiki or "coding-assisted graphwiki"
191
+ # → narrate wiki/.agent/prompts/* into wiki/.agent/responses/*, then:
192
+ graphwiki build . --backend agent --no-web # ingests the responses
187
193
  graphwiki validate .
188
194
  ```
189
195
 
@@ -106,6 +106,7 @@ works.
106
106
  | `ollama` (default) | `http://localhost:11434/v1` | `gemma3` | No |
107
107
  | `nim` | `http://localhost:8000/v1` | `gemma` | No |
108
108
  | `offline` | none | none | No (no LLM at all) |
109
+ | `agent` | none — a coding assistant writes the prose | No (no LLM/egress); see [Coding-assisted mode](#coding-assisted-mode-no-llm-api) |
109
110
  | `grok` | `https://api.x.ai/v1` | `grok-4` | **Yes** — external SaaS |
110
111
  | `claude` | `https://api.anthropic.com/v1` | `claude-opus-4-8` | **Yes** — external SaaS |
111
112
  | `openai` | `https://api.openai.com/v1` | `gpt-4o` | **Yes** — external SaaS |
@@ -127,19 +128,22 @@ page is sent unless you pass `--allow-cloud-egress` or set
127
128
  egress is never silent). A raw non-local `--base-url` (an internal NIM/Triton
128
129
  box) is only logged, never blocked. Full policy: [DATA_GOVERNANCE.md](docs/DATA_GOVERNANCE.md).
129
130
 
130
- > **Note:** these are model **APIs**. Coding-agent CLIs (Codex CLI, Claude Code)
131
- > are deliberately not supported as backends: they are interactive, tool-wielding
132
- > agents, whereas the writer needs a stateless completion that is fed ONLY the
133
- > pre-assembled graph context and forbidden from traversing the invariant that
134
- > keeps citations verifiable. To use Claude, point `--base-url` at the Anthropic
135
- > API, not the CLI.
131
+ > **Note:** the cloud/local rows above are model **APIs** reached over HTTP
132
+ > don't point `--base-url` at an interactive coding-agent CLI. When you want a
133
+ > coding assistant to write the prose, use `--backend agent` (below): it feeds
134
+ > the assistant the SAME stateless, pre-assembled context the API writer gets
135
+ > via prompt files, with no traversal tools so citations stay verifiable.
136
136
 
137
137
  ### Coding-assisted mode (no LLM API)
138
138
 
139
- When no model endpoint is available, use the **graphwiki skill** with a coding
140
- assistant as the prose writer. Python still plans coverage and assembles context
141
- (`--backend offline`); the agent only rewrites narrative under the four `##`
142
- sections, then `graphwiki validate` gates the result.
139
+ When no model endpoint is available, use `--backend agent` (or the **graphwiki
140
+ skill**) to make a coding assistant the prose writer. Python still plans
141
+ coverage and assembles context; the tool writes the exact `(system prompt,
142
+ context)` pair the API writer sends into `wiki/.agent/prompts/<id>.md`, the
143
+ assistant writes the four `§SECTION:` blocks back into
144
+ `wiki/.agent/responses/<id>.md`, and a re-run feeds them through the same
145
+ `merge → gate → emit` path — the assistant never edits a page. `graphwiki
146
+ validate` gates the result.
143
147
 
144
148
  **Installers place the skill automatically** into every detected coding-assistant
145
149
  skill root (Claude Code, Cursor, GitHub Copilot, Grok, OpenAI Codex,
@@ -154,8 +158,10 @@ skill root (Claude Code, Cursor, GitHub Copilot, Grok, OpenAI Codex,
154
158
  graphwiki skill install # detected assistants
155
159
  graphwiki skill install --all # every known root
156
160
  graphwiki skill status
157
- graphwiki build . --backend offline --no-web
161
+ graphwiki build . --backend agent --no-web
158
162
  # In the assistant: /graphwiki or "coding-assisted graphwiki"
163
+ # → narrate wiki/.agent/prompts/* into wiki/.agent/responses/*, then:
164
+ graphwiki build . --backend agent --no-web # ingests the responses
159
165
  graphwiki validate .
160
166
  ```
161
167
 
@@ -2,7 +2,7 @@
2
2
  # Name verified available on PyPI (2026-07-01). npm has an unrelated `graphwiki`
3
3
  # but this is a Python-only tool, so there is no conflict.
4
4
  name = "graphwiki"
5
- version = "0.2.0"
5
+ version = "0.3.0"
6
6
  description = "Graph-grounded code wiki generator: deterministic traversal, LLM writes prose only."
7
7
  readme = "README.md"
8
8
  requires-python = ">=3.11"
@@ -27,7 +27,14 @@ from .graph_client import EngineError, GraphClient
27
27
  from .logging_util import echo as _echo
28
28
  from .state import State
29
29
  from .types import HOME_DIR, ConceptContext, ConceptType, Page
30
- from .writer import BACKENDS, CLOUD_BACKENDS, Writer, WriterConfig
30
+ from .writer import (
31
+ BACKENDS,
32
+ CLOUD_BACKENDS,
33
+ AgentBridge,
34
+ Writer,
35
+ WriterConfig,
36
+ _serialize_context,
37
+ )
31
38
 
32
39
  app = typer.Typer(add_completion=False, help="Graph-grounded code wiki generator.")
33
40
  skill_app = typer.Typer(
@@ -143,7 +150,9 @@ def _resolve_backend(
143
150
  )
144
151
  raise typer.Exit(code=2)
145
152
  preset_url, preset_model = BACKENDS[backend]
146
- if backend == "offline":
153
+ if backend in ("offline", "agent"):
154
+ # agent has no endpoint either; an un-narrated page falls back to the
155
+ # offline template, so allow_offline must be set (else fail-loud).
147
156
  offline = True
148
157
  # Explicit (non-None) flags override the preset; else preset; else Ollama.
149
158
  resolved_url = base_url if base_url is not None else (preset_url or ollama_url)
@@ -196,7 +205,11 @@ def _resolve_backend(
196
205
  raise typer.Exit(code=2)
197
206
 
198
207
  return WriterConfig(
199
- base_url=resolved_url, model=resolved_model, allow_offline=offline, api_key=key
208
+ base_url=resolved_url,
209
+ model=resolved_model,
210
+ allow_offline=offline,
211
+ api_key=key,
212
+ agent=backend == "agent",
200
213
  )
201
214
 
202
215
 
@@ -346,7 +359,27 @@ def _writer_fingerprint(cfg: WriterConfig) -> str:
346
359
  """Writer identity for staleness. A change here (backend/model/offline) must
347
360
  invalidate all pages even when the graph context is unchanged — otherwise
348
361
  ``update`` after switching backends is a silent no-op."""
349
- return f"{cfg.model}|{cfg.base_url}|offline={cfg.allow_offline}"
362
+ return f"{cfg.model}|{cfg.base_url}|offline={cfg.allow_offline}|agent={cfg.agent}"
363
+
364
+
365
+ def _report_agent_handoff(bridge: AgentBridge) -> None:
366
+ """Tell the coding assistant what prose is still owed after an agent-mode run.
367
+
368
+ Pass 1 (no responses yet) reports every page as pending and emits offline
369
+ fallback bodies (gate-valid). The assistant narrates the prompts, then a
370
+ re-run ingests the responses. Idempotent: re-runs re-report the true state.
371
+ """
372
+ narrated, pending = len(bridge.narrated), len(bridge.pending)
373
+ total = narrated + pending
374
+ _echo(f"[agent] {narrated}/{total} page(s) narrated, {pending} awaiting prose")
375
+ if pending:
376
+ _echo(
377
+ f"[agent] next: for each prompt in {bridge.prompts}/ write §SECTION: "
378
+ f"prose to {bridge.responses}/<same-name>, then re-run "
379
+ "`graphwiki build --backend agent`."
380
+ )
381
+ else:
382
+ _echo("[agent] all pages narrated — run `graphwiki validate` to gate.")
350
383
 
351
384
 
352
385
  def _fmt_eta(secs: float) -> str:
@@ -443,7 +476,16 @@ def _run(
443
476
 
444
477
  state = State(out)
445
478
  writer_fp = _writer_fingerprint(cfg)
446
- targets = state.stale(contexts, writer_fp) if incremental else contexts
479
+ # Agent mode always regenerates every page: the two-phase (emit prompts →
480
+ # assistant writes responses → re-run to ingest) flow needs the second
481
+ # pass to re-render each page, which staleness would skip on an unchanged
482
+ # graph. Regeneration is offline-cheap, so this costs nothing but LLM-free
483
+ # passes over the plan.
484
+ targets = (
485
+ state.stale(contexts, writer_fp)
486
+ if (incremental and not cfg.agent)
487
+ else contexts
488
+ )
447
489
  _echo(
448
490
  f"[generate] {len(targets)}/{len(contexts)} pages "
449
491
  f"({'incremental' if incremental else 'full'})"
@@ -457,7 +499,15 @@ def _run(
457
499
  )
458
500
 
459
501
  _t = time.monotonic()
460
- writer = Writer(cfg)
502
+ bridge: AgentBridge | None = None
503
+ if cfg.agent:
504
+ key_by_context = {
505
+ _serialize_context(c): c.concept.concept_id for c in targets
506
+ }
507
+ bridge = AgentBridge(out / ".agent", key_by_context)
508
+ writer = Writer(cfg, complete=bridge)
509
+ else:
510
+ writer = Writer(cfg)
461
511
  target_pages = _generate_pages(writer, targets)
462
512
  _log.debug(
463
513
  "generated %d page(s) in %.2fs", len(target_pages), time.monotonic() - _t
@@ -531,6 +581,8 @@ def _run(
531
581
  f"{len(target_pages)} page(s) emitted, gates {report.layers}",
532
582
  )
533
583
  _echo(f"[emit] {out} ({len(target_pages)} page(s) written)")
584
+ if bridge is not None:
585
+ _report_agent_handoff(bridge)
534
586
  finally:
535
587
  graph.close()
536
588
  if writer is not None:
@@ -609,7 +661,7 @@ def build(
609
661
  backend: str = typer.Option(
610
662
  None,
611
663
  "--backend",
612
- help="Preset: ollama | nim | offline | grok | claude | openai. Sets base-url+model.",
664
+ help="Preset: ollama | nim | offline | agent | grok | claude | openai. Sets base-url+model.",
613
665
  ),
614
666
  base_url: str = typer.Option(
615
667
  None, "--base-url", help="OpenAI-compatible base URL (default: local Ollama)."
@@ -690,7 +742,7 @@ def update(
690
742
  backend: str = typer.Option(
691
743
  None,
692
744
  "--backend",
693
- help="Preset: ollama | nim | offline | grok | claude | openai. Sets base-url+model.",
745
+ help="Preset: ollama | nim | offline | agent | grok | claude | openai. Sets base-url+model.",
694
746
  ),
695
747
  base_url: str = typer.Option(
696
748
  None, "--base-url", help="OpenAI-compatible base URL (default: local Ollama)."
@@ -858,6 +910,11 @@ def _doctor_backend(
858
910
  if backend == "offline":
859
911
  typer.echo("[doctor] backend: offline (deterministic bodies, no LLM/egress)")
860
912
  return
913
+ if backend == "agent":
914
+ typer.echo(
915
+ "[doctor] backend: agent (coding assistant writes prose; no LLM/egress)"
916
+ )
917
+ return
861
918
  if backend in CLOUD_BACKENDS:
862
919
  kind = (
863
920
  "CLOUD (needs --allow-cloud-egress + API key; sends code-derived context)"
@@ -998,7 +1055,9 @@ def _print_skill_results(results: list[skill_install.InstallResult]) -> None:
998
1055
  "error": "x",
999
1056
  }.get(r.status, "?")
1000
1057
  dest = str(r.dest) if r.dest else ""
1001
- detail = f" ({r.detail})" if r.detail and r.status in ("error", "skipped") else ""
1058
+ detail = (
1059
+ f" ({r.detail})" if r.detail and r.status in ("error", "skipped") else ""
1060
+ )
1002
1061
  loc = f" → {dest}" if dest and r.status not in ("skipped",) else ""
1003
1062
  typer.echo(f" [{mark}] {r.label}: {r.status}{loc}{detail}")
1004
1063
 
@@ -1081,7 +1140,7 @@ def watch(
1081
1140
  backend: str = typer.Option(
1082
1141
  None,
1083
1142
  "--backend",
1084
- help="Preset: ollama | nim | offline | grok | claude | openai. Sets base-url+model.",
1143
+ help="Preset: ollama | nim | offline | agent | grok | claude | openai. Sets base-url+model.",
1085
1144
  ),
1086
1145
  base_url: str = typer.Option(
1087
1146
  None, "--base-url", help="OpenAI-compatible base URL (default: local Ollama)."
@@ -81,11 +81,7 @@ def configure(*, verbosity: int = 0, quiet: bool = False, json: bool = False) ->
81
81
  if json:
82
82
  handler.setFormatter(_JsonFormatter())
83
83
  else:
84
- fmt = (
85
- "%(levelname)s %(name)s: %(message)s"
86
- if verbosity >= 2
87
- else "%(message)s"
88
- )
84
+ fmt = "%(levelname)s %(name)s: %(message)s" if verbosity >= 2 else "%(message)s"
89
85
  handler.setFormatter(logging.Formatter(fmt))
90
86
 
91
87
  _state.update(verbosity=verbosity, quiet=quiet, json=json)
@@ -109,8 +109,16 @@ def _safe_label(text: str) -> str:
109
109
  inject markup into the viewer. Map the dangerous chars to readable-but-inert
110
110
  equivalents rather than trusting the names to be clean."""
111
111
  for bad, good in (
112
- ("&", "+"), ("<", "("), (">", ")"), ('"', "'"),
113
- ("[", "("), ("]", ")"), ("{", "("), ("}", ")"), ("|", "/"), ("`", "'"),
112
+ ("&", "+"),
113
+ ("<", "("),
114
+ (">", ")"),
115
+ ('"', "'"),
116
+ ("[", "("),
117
+ ("]", ")"),
118
+ ("{", "("),
119
+ ("}", ")"),
120
+ ("|", "/"),
121
+ ("`", "'"),
114
122
  ):
115
123
  text = text.replace(bad, good)
116
124
  return text
@@ -218,9 +218,7 @@ def status_skills(*, home: Path | None = None) -> list[InstallResult]:
218
218
  InstallResult(tool, label, dest, "skipped", "skill not installed")
219
219
  )
220
220
  elif _trees_equal(src, dest):
221
- results.append(
222
- InstallResult(tool, label, dest, "unchanged", "up to date")
223
- )
221
+ results.append(InstallResult(tool, label, dest, "unchanged", "up to date"))
224
222
  else:
225
223
  results.append(
226
224
  InstallResult(tool, label, dest, "updated", "installed but stale")
@@ -0,0 +1,220 @@
1
+ ---
2
+ name: graphwiki
3
+ description: >
4
+ Coding-assisted graphwiki — generate or refresh a graph-grounded code wiki
5
+ when no LLM API/backend is available. The coding assistant acts as the
6
+ prose writer only; Python still decides coverage and context. Use when the
7
+ user asks to "build graphwiki offline", "coding-assisted graphwiki",
8
+ "graphwiki without ollama/nim/api", "narrate the wiki", "rewrite offline
9
+ stubs", "agent-assisted wiki", or runs /graphwiki. Also use when graphwiki
10
+ build fails for lack of a model endpoint and the user wants quality prose
11
+ instead of deterministic offline templates.
12
+ ---
13
+
14
+ # Coding-Assisted GraphWiki
15
+
16
+ Run **graphwiki** with a coding assistant (Claude Code, Grok, Cursor, Codex, …)
17
+ as the writer when no model API is reachable. This is **not** free-form repo
18
+ documentation (OpenWiki/deepagents style). Coverage, context, citations, and
19
+ gates stay owned by the Python tool.
20
+
21
+ **How it works — `--backend agent` makes you a drop-in for the LLM.** The tool
22
+ builds the graph, plans coverage, and assembles each page's context, then writes
23
+ the SAME `(system prompt, context)` pair the online writer sends to an API into
24
+ `wiki/.agent/prompts/<id>.md`. You write the four `§SECTION:` blocks back to
25
+ `wiki/.agent/responses/<id>.md`. A re-run feeds your prose through the exact same
26
+ `merge → assemble → gate → emit` path the API-key writer uses. You never open or
27
+ edit a page under `wiki/` — Python assembles the frontmatter, fact lists, and
28
+ Mermaid **around** your prose, so you cannot corrupt the deterministic parts.
29
+
30
+ ## Frozen invariants (do not break)
31
+
32
+ | Layer | Owner | Agent may… |
33
+ |---|---|---|
34
+ | What to document (coverage law) | `graphwiki plan` | Never decide, never drop a required page |
35
+ | What facts each page gets | `graphwiki` context assembly | Never invent members/callers/paths |
36
+ | Narrative prose | **this skill / the agent (as the writer)** | Write the four `§SECTION:` blocks into the response file only |
37
+ | Frontmatter, Mermaid, fact lists, page files | Python | **Never write or edit** — assembled around your prose |
38
+ | Validation | `graphwiki validate` | Must pass before claiming done |
39
+
40
+ **Anti-pattern:** grepping the repo and writing new path citations. That
41
+ reintroduces hallucination and fails the citation gate. Ground every claim in
42
+ the context the prompt file hands you (its `# Context` block) — nothing else.
43
+
44
+ Full invariant detail: `references/invariants.md`.
45
+
46
+ ## Prerequisites
47
+
48
+ ```bash
49
+ # Prefer uv on host
50
+ uv tool install graphwiki # or: uvx graphwiki …
51
+ # Engine auto-fetches on first build (or pin --engine-bin)
52
+ graphwiki doctor . --backend agent # engine + index healthy; no LLM needed
53
+ ```
54
+
55
+ If `doctor` fails on the engine, fix that first — the agent cannot replace the
56
+ graph engine.
57
+
58
+ ## Modes
59
+
60
+ ### A. Assisted build (no LLM API) — default
61
+
62
+ Use when Ollama/NIM/cloud are unavailable or the user wants agent-written prose.
63
+
64
+ ```
65
+ 1. Build graph + emit per-page prompts (deterministic, no network):
66
+ graphwiki build . --backend agent --no-web
67
+ # Indexes the repo, plans coverage, assembles context, writes one prompt to
68
+ # wiki/.agent/prompts/<id>.md per page, and emits offline-fallback pages so
69
+ # the wiki is gate-valid immediately. The final [agent] line reports how many
70
+ # pages still await prose. (optional: --out PATH, --allow-defer GLOB, --engine-bin PATH)
71
+
72
+ 2. Narrate each prompt (see "Narration recipe"): for every file in
73
+ wiki/.agent/prompts/, write §SECTION prose to wiki/.agent/responses/<same-name>.
74
+ Prioritize high x-hotspot-score pages first if the tree is large.
75
+
76
+ 3. Ingest + gate (re-run is idempotent):
77
+ graphwiki build . --backend agent --no-web
78
+ graphwiki validate .
79
+ # The re-run feeds your responses through the same writer path an LLM uses.
80
+ # Repeat step 2 → step 3 until [agent] reports "0 awaiting prose".
81
+
82
+ 4. Optional viewer:
83
+ graphwiki web --no-open # or open the URL
84
+ ```
85
+
86
+ Your responses persist in `wiki/.agent/responses/` — that directory is the
87
+ durable narration store. Every `build --backend agent` re-derives the pages from
88
+ `(graph + responses)`, so the flow is fully reproducible.
89
+
90
+ ### B. Assisted refresh (after code changes)
91
+
92
+ ```
93
+ 1. graphwiki build . --backend agent --no-web
94
+ # Re-indexes; each page's prompt now reflects current context; existing
95
+ # responses are re-applied. Pages whose context moved get an updated prompt.
96
+ 2. Re-narrate the prompts whose # Context changed (or that the user named);
97
+ leave the rest — their prior response still applies.
98
+ 3. graphwiki build . --backend agent --no-web && graphwiki validate .
99
+ ```
100
+
101
+ ### C. Editorial polish (prose only)
102
+
103
+ Existing wiki already gate-valid; user wants better narrative.
104
+
105
+ ```
106
+ 1. Edit the §SECTION prose in wiki/.agent/responses/<id>.md (NOT the page under
107
+ wiki/ — that is derived and will be overwritten on the next run).
108
+ 2. graphwiki build . --backend agent --no-web # re-derives the page
109
+ 3. graphwiki validate .
110
+ ```
111
+
112
+ ### D. Consume wiki while coding (read path)
113
+
114
+ Not generation. Point the agent at `wiki/.ai-context.md` and relevant pages,
115
+ or run `graphwiki serve` (MCP: `read_wiki_structure` / `read_wiki_contents` /
116
+ `ask_question`). Do not invent pages in this mode.
117
+
118
+ ## Pending pages (what still needs prose)
119
+
120
+ Two signals, in order of authority:
121
+
122
+ ```bash
123
+ # Authoritative: the run's own report
124
+ # [agent] 3/17 page(s) narrated, 14 awaiting prose
125
+ # A prompt with no matching response file is pending:
126
+ comm -23 <(cd wiki/.agent/prompts && ls) <(cd wiki/.agent/responses && ls 2>/dev/null)
127
+
128
+ # Secondary: a derived page still carrying the offline template signature
129
+ rg -n "documented from the code knowledge graph" wiki -g '*.md'
130
+ ```
131
+
132
+ After you narrate and re-run, the pending count drops and the offline signature
133
+ disappears from that page.
134
+
135
+ ## Narration recipe (one page)
136
+
137
+ Load `references/page-format.md` once; apply for every page.
138
+
139
+ 1. **Read** the prompt file `wiki/.agent/prompts/<id>.md`. It contains:
140
+ - `# System instructions` — the writer contract (identical to the LLM's
141
+ system prompt). It defines the four sections and the `§SECTION:` markers.
142
+ - `# Context — the ONLY source of truth` — concept type, title, resource,
143
+ public surface, members, child modules, callers, blast radius, tests.
144
+ 2. **Do not** open source files or the page under `wiki/` to "learn more" for new
145
+ citations. The `# Context` block is the complete, only source of truth.
146
+ Optional peek at `resource` only to phrase responsibility accurately; still
147
+ cite only names/paths already in the context.
148
+ 3. **Write only** the four `§SECTION:` blocks to
149
+ `wiki/.agent/responses/<id>.md`, each introduced by a line that is EXACTLY:
150
+ ```
151
+ §SECTION: purpose
152
+ §SECTION: architecture
153
+ §SECTION: key_types
154
+ §SECTION: fit
155
+ ```
156
+ Nothing else — no frontmatter, no `##` headings, no code fences, no fact
157
+ lists. Python assembles all of that around your prose.
158
+ 4. **Prose rules** (they are also stated in the prompt's system block — follow it):
159
+ - Concrete, not boilerplate; explain the graph facts, do not restate raw lists
160
+ - Reference ONLY symbols/paths present in the `# Context` block
161
+ - A short bulleted list is allowed ONLY in `key_types`
162
+ - If a list (callers/tests/public surface) is empty, say so plainly (leaf / no
163
+ public surface / untested) — do not invent
164
+ - Clear depth floors (the tool's anti-stub gate; offline fallback already
165
+ clears them, so match or exceed): Subsystem ≥200, Module/Feature/Entrypoint
166
+ ≥120, API/DataModel ≥80 chars of prose
167
+
168
+ ## Batch strategy
169
+
170
+ For large wikis:
171
+
172
+ 1. Run `build --backend agent` once to emit all prompts.
173
+ 2. Sort prompts by their page's `x-hotspot-score` (highest first — hotspot lives
174
+ in the emitted page frontmatter, or narrate the prompts flagged pending).
175
+ 3. Narrate responses in batches of 5–15, then run
176
+ `graphwiki build --backend agent && graphwiki validate .` so failures stay local.
177
+ 4. Skip deferred / non-required concepts (no prompt is emitted for them).
178
+
179
+ Do not hand-create prompt or page files — re-run `graphwiki build --backend
180
+ agent` to (re)materialize the plan.
181
+
182
+ ## Failure handling
183
+
184
+ | Symptom | Action |
185
+ |---|---|
186
+ | `doctor` engine missing | Fix engine (`--engine-bin` or auto-fetch); do not free-write a wiki |
187
+ | `validate` structure/quality fail | Narrate the named page's response deeper (below the anti-stub floor) |
188
+ | `validate` citations fail | Remove the invented path token from that response; re-run + re-validate |
189
+ | `validate` coverage fail | Re-run `build --backend agent` — do not invent missing pages |
190
+ | A response is ignored on re-run | Confirm the filename matches its prompt exactly (`<id>.md`) and has `§SECTION:` markers |
191
+ | User wants cloud/local API instead | Prefer `graphwiki build . --backend ollama\|nim\|…` over this skill |
192
+
193
+ ## Done criteria
194
+
195
+ - [ ] `graphwiki validate .` exits 0
196
+ - [ ] The run reports `0 awaiting prose` (every required page has a response)
197
+ - [ ] No page still contains `documented from the code knowledge graph`
198
+ - [ ] You wrote only response files — never a page under `wiki/`
199
+ - [ ] Summary for the user: pages narrated, validate OK, how to serve
200
+ (`graphwiki web` or `graphwiki serve`)
201
+
202
+ ## CLI cheat sheet
203
+
204
+ ```bash
205
+ graphwiki doctor . --backend agent
206
+ graphwiki build . --backend agent --no-web # emit prompts + ingest responses
207
+ graphwiki validate .
208
+ graphwiki web
209
+ graphwiki serve --out wiki # needs mcp extra
210
+ ```
211
+
212
+ Backend note: `agent` is a first-class backend — it substitutes YOU for the LLM
213
+ in the writer's one completion call. The offline scaffold still exists as the
214
+ per-page fallback when a response is missing (`--backend offline` for a pure
215
+ deterministic wiki with no agent). See product README.
216
+
217
+ ## Additional resources
218
+
219
+ - `references/invariants.md` — why agents must not traverse for generation
220
+ - `references/page-format.md` — the `§SECTION:` response contract + what Python owns
@@ -23,8 +23,10 @@ plugged in as free-form backends.
23
23
  and citation ground truth.
24
24
 
25
25
  OpenWiki-style deepagents (shell + FS tools freely traversing the repo to write
26
- docs) is explicitly **WON'T-DO** on the generation path. Bounded prose-only
27
- work on already-scaffolded pages is the only agent role that fits.
26
+ docs) is explicitly **WON'T-DO** on the generation path. Writing bounded
27
+ prose-only `§SECTION:` responses to the tool's per-page prompts (`--backend
28
+ agent`) is the only agent role that fits — you substitute for the LLM's one
29
+ completion call, nothing more.
28
30
 
29
31
  ## Citation rule (anti-hallucination)
30
32
 
@@ -32,12 +34,11 @@ Any path-shaped inline-code token (e.g. `` `src/foo/bar.py` ``, `` `writer.py` `
32
34
  in a page must resolve to a path the graph indexed. The citation gate enforces
33
35
  this on validate when the engine is available.
34
36
 
35
- **Safe citation set for agents:**
37
+ **Safe citation set for agents** (all drawn from the prompt's `# Context` block):
36
38
 
37
- - `resource` from frontmatter
38
- - basenames / paths already in **Public surface**, **Used by**, **Blast radius**,
39
- **Tests**, **Child modules**, or mermaid node labels
40
- - symbols already listed on the page
39
+ - `RESOURCE` from the context
40
+ - names/paths already in `PUBLIC SURFACE`, `USED BY`, `BLAST RADIUS`, `TESTS`,
41
+ `CHILD MODULES`, or `MEMBERS`
41
42
 
42
43
  **Unsafe:** any new path discovered by reading the filesystem during narration.
43
44
 
@@ -46,11 +47,11 @@ this on validate when the engine is available.
46
47
  | Mode | Prose source | Network | When |
47
48
  |---|---|---|---|
48
49
  | `--backend offline` | Deterministic templates from graph facts | None | CI, scaffold, demos |
49
- | **This skill** | Coding assistant, facts from scaffold page | None for generation | No API; want quality prose |
50
+ | **`--backend agent` (this skill)** | Coding assistant, facts from the prompt's `# Context` | None for generation | No API; want quality prose |
50
51
  | `--backend ollama\|nim\|…` | Model API completion | Local or cloud (gated) | Normal automated runs |
51
52
 
52
- Assisted mode should start from an offline scaffold so frontmatter, lists, and
53
- diagrams stay tool-authored.
53
+ Agent mode reuses the offline template as the per-page fallback, so frontmatter,
54
+ lists, and diagrams stay tool-authored no matter which pages you narrate.
54
55
 
55
56
  ## Incremental honesty
56
57
 
@@ -0,0 +1,123 @@
1
+ # Response format (`§SECTION:` writer contract)
2
+
3
+ Under `--backend agent` you write **response files**, not pages. Python derives
4
+ each page under `wiki/` from `(graph context + your response)`. You never open a
5
+ page file.
6
+
7
+ Default output tree: `<repo>/wiki/` (`--out` overrides).
8
+
9
+ ## Tree
10
+
11
+ ```
12
+ wiki/
13
+ .ai-context.md # index — tool-owned
14
+ architecture.md # system Mermaid — tool-owned
15
+ log.md # run log — tool-owned
16
+ .state/ # refresh hashes — tool-owned
17
+ .agent/
18
+ prompts/<id>.md # tool-emitted: system prompt + context. READ ONLY.
19
+ responses/<id>.md # YOU write this: the four §SECTION blocks.
20
+ systems/*.md # DERIVED page (Subsystem + Module) — do not edit
21
+ features/*.md # DERIVED page (Feature) — do not edit
22
+ entrypoints/*.md # DERIVED page (Entrypoint) — do not edit
23
+ reference/*.md # DERIVED page (API, DataModel, …) — do not edit
24
+ ```
25
+
26
+ ## The prompt file you read (`wiki/.agent/prompts/<id>.md`)
27
+
28
+ ```markdown
29
+ <!-- graphwiki agent writer prompt: <id> — DO NOT EDIT. ... -->
30
+
31
+ # System instructions
32
+ <the writer contract: the four sections, the §SECTION: markers, the prose rules>
33
+
34
+ # Context — the ONLY source of truth
35
+ CONCEPT TYPE: <Module|Subsystem|Feature|Entrypoint|…>
36
+ TITLE: <title>
37
+ RESOURCE: <canonical path or symbol>
38
+ PUBLIC SURFACE: <symbols…> # present only if non-empty
39
+ MEMBERS: <symbols…>
40
+ CHILD MODULES: <names…>
41
+ USED BY (callers): <names…>
42
+ BLAST RADIUS (dependents): <names…>
43
+ TESTS: <paths…>
44
+ ```
45
+
46
+ The context is a fixed extract from the code knowledge graph. It is the **only**
47
+ source of truth. Never introduce a name, path, caller, or test not present in it.
48
+
49
+ ## The response file you write (`wiki/.agent/responses/<id>.md`)
50
+
51
+ Exactly four blocks, each opened by a line that is EXACTLY the marker (no `##`
52
+ heading, no numbering, no frontmatter, no code fences):
53
+
54
+ ```text
55
+ §SECTION: purpose
56
+ 1–2 short paragraphs: what this concept is and the responsibility it owns.
57
+ Concrete, not boilerplate.
58
+
59
+ §SECTION: architecture
60
+ 1 short paragraph: how it is structured and how it relates to what it depends on
61
+ or is used by. If the context lists child modules or a diagram, narrate what it
62
+ shows — Python renders the actual Mermaid; you do not.
63
+
64
+ §SECTION: key_types
65
+ A few sentences on the most important types/functions in the public surface and
66
+ what each is for. Reference ONLY names in the context. A short bulleted list is
67
+ allowed here (and only here). If the surface is empty, say so in one sentence.
68
+
69
+ §SECTION: fit
70
+ 1 short paragraph: who calls it, what breaks if it changes (its blast radius),
71
+ how it is tested. Ground every claim in the callers/blast-radius/tests given.
72
+ ```
73
+
74
+ Parsing is on the markers: any preamble before the first `§SECTION:` and any
75
+ unknown marker is dropped. A section you omit is filled from the offline
76
+ template, so a partial response still yields a complete page — but an empty or
77
+ marker-less response leaves the page on the offline stub.
78
+
79
+ ## What Python owns (you never write it)
80
+
81
+ - The YAML frontmatter (`type`, `title`, `description` routing key, `resource`,
82
+ `x-callers`, `x-grounded-paths`, `x-hotspot-score`)
83
+ - The `## Purpose & Capabilities` / `## Architecture` / `## Key Types` /
84
+ `## How It Fits` headings
85
+ - Every fact panel: `**Child modules:**`, `**Public surface:**`, `**Used by:**`,
86
+ `**Blast radius** …`, `**Tests:**` and their list items
87
+ - The `` ```mermaid `` diagram fences
88
+
89
+ `type` is a frozen vocabulary and `description` is a routing decision ("should I
90
+ open this?"), not a summary — both are tool-owned; do not attempt to influence
91
+ them from a response.
92
+
93
+ ## Example (shape only)
94
+
95
+ Prompt `# Context` says: `TITLE: writer`, `RESOURCE: src/graphwiki/writer.py`,
96
+ `PUBLIC SURFACE: Writer, WriterConfig, WriterError`, `MEMBERS: 12 symbols`.
97
+
98
+ Response `wiki/.agent/responses/writer.md`:
99
+
100
+ ```text
101
+ §SECTION: purpose
102
+ `writer` owns page narration for a single pre-assembled concept. It turns graph
103
+ context into structured body sections, using either an OpenAI-compatible
104
+ completion or deterministic offline templates. It is fed one concept at a time;
105
+ it does not plan coverage or query the graph.
106
+
107
+ §SECTION: architecture
108
+ It is a thin renderer around one completion call: the config selects the backend,
109
+ the writer serializes the context, and the parsed sections are merged with
110
+ deterministic fact panels assembled by the caller.
111
+
112
+ §SECTION: key_types
113
+ `Writer` runs the per-page render; `WriterConfig` carries backend/model/offline
114
+ identity; `WriterError` fails a page loud when an online completion is empty.
115
+
116
+ §SECTION: fit
117
+ It sits below planning and context assembly and above emit. Changing its section
118
+ contract ripples to every page, so its blast radius is the whole wiki; the writer
119
+ test suite exercises the offline, partial, and fail-loud paths.
120
+ ```
121
+
122
+ Python renders the frontmatter, the four headings, and the public-surface / used-by
123
+ / blast-radius / tests panels around this prose.
@@ -23,6 +23,7 @@ from __future__ import annotations
23
23
 
24
24
  from collections.abc import Callable
25
25
  from dataclasses import dataclass
26
+ from pathlib import Path
26
27
  from typing import Any
27
28
 
28
29
  from .types import HOME_DIR, ConceptContext, Frontmatter, Page
@@ -87,6 +88,7 @@ BACKENDS: dict[str, tuple[str, str]] = {
87
88
  "ollama": ("http://localhost:11434/v1", "gemma3"),
88
89
  "nim": ("http://localhost:8000/v1", "gemma"),
89
90
  "offline": ("", ""),
91
+ "agent": ("", ""), # coding assistant writes prose; no endpoint (see AgentBridge)
90
92
  "grok": ("https://api.x.ai/v1", "grok-4"),
91
93
  "claude": ("https://api.anthropic.com/v1", "claude-opus-4-8"),
92
94
  "openai": ("https://api.openai.com/v1", "gpt-4o"),
@@ -104,6 +106,7 @@ class WriterConfig:
104
106
  api_key: str = "not-needed" # local endpoints ignore it
105
107
  temperature: float = 0.0 # determinism-friendly
106
108
  allow_offline: bool = False # deterministic body if no LLM
109
+ agent: bool = False # coding assistant is the writer (see AgentBridge)
107
110
 
108
111
 
109
112
  class WriterError(Exception):
@@ -458,3 +461,68 @@ def _assemble_body(sections: dict[str, str], ctx: ConceptContext) -> str:
458
461
  parts.append("")
459
462
 
460
463
  return "\n".join(parts).rstrip() + "\n"
464
+
465
+
466
+ # ── agent backend: coding assistant as a drop-in for the LLM ─────────────────
467
+ _AGENT_PROMPT_MARK = "graphwiki agent writer prompt"
468
+
469
+
470
+ class AgentBridge:
471
+ """File-backed ``Complete`` — the coding assistant IS the model.
472
+
473
+ Realizes ``--backend agent``, the true-parity path for coding assistants:
474
+ instead of an LLM call, each page's ``(SYSTEM_PROMPT, serialized-context)``
475
+ — the EXACT pair the online writer sends — is written to
476
+ ``<out>/.agent/prompts/<concept_id>.md``. When the assistant has already
477
+ written the matching ``<out>/.agent/responses/<concept_id>.md`` (raw
478
+ ``§SECTION:`` prose), its text is returned so the SAME
479
+ ``_merge_sections`` → ``_assemble_body`` → gate path renders the page — no
480
+ different code path from the API-key writer. A page with no response yet
481
+ returns "" so the offline template fills it (build stays green); it is
482
+ reported as ``pending`` for the handoff.
483
+
484
+ The assistant never sees frontmatter, fact lists, or Mermaid — Python
485
+ assembles those AFTER this returns, so agent prose cannot corrupt the
486
+ deterministic sections (the footgun of the old scaffold-then-edit skill).
487
+
488
+ ``key_by_context`` maps each page's serialized context back to its
489
+ concept_id so prompt/response files are human-addressable. Two concepts with
490
+ identical assembled context legitimately share a file — identical context
491
+ means identical prose, which is the deterministic-correct outcome.
492
+ """
493
+
494
+ def __init__(self, agent_dir: Path, key_by_context: dict[str, str]) -> None:
495
+ self.prompts = agent_dir / "prompts"
496
+ self.responses = agent_dir / "responses"
497
+ self.prompts.mkdir(parents=True, exist_ok=True)
498
+ self.responses.mkdir(parents=True, exist_ok=True)
499
+ self._key = key_by_context
500
+ self.pending: list[str] = []
501
+ self.narrated: list[str] = []
502
+
503
+ def _concept_id(self, user: str) -> str:
504
+ cid = self._key.get(user)
505
+ if cid:
506
+ return cid
507
+ import hashlib
508
+
509
+ return "ctx-" + hashlib.sha1(user.encode("utf-8")).hexdigest()[:12]
510
+
511
+ def __call__(self, system: str, user: str) -> str:
512
+ cid = self._concept_id(user)
513
+ (self.prompts / f"{cid}.md").write_text(
514
+ f"<!-- {_AGENT_PROMPT_MARK}: {cid} — DO NOT EDIT.\n"
515
+ f" Write your prose (the four §SECTION: blocks described below)\n"
516
+ f" to ../responses/{cid}.md, then re-run"
517
+ f" `graphwiki build --backend agent`. -->\n\n"
518
+ f"# System instructions\n\n{system}\n\n"
519
+ f"# Context — the ONLY source of truth\n\n{user}\n",
520
+ encoding="utf-8",
521
+ )
522
+ resp = self.responses / f"{cid}.md"
523
+ text = resp.read_text(encoding="utf-8").strip() if resp.is_file() else ""
524
+ if text:
525
+ self.narrated.append(cid)
526
+ return text
527
+ self.pending.append(cid)
528
+ return ""
@@ -1,202 +0,0 @@
1
- ---
2
- name: graphwiki
3
- description: >
4
- Coding-assisted graphwiki — generate or refresh a graph-grounded code wiki
5
- when no LLM API/backend is available. The coding assistant acts as the
6
- prose writer only; Python still decides coverage and context. Use when the
7
- user asks to "build graphwiki offline", "coding-assisted graphwiki",
8
- "graphwiki without ollama/nim/api", "narrate the wiki", "rewrite offline
9
- stubs", "agent-assisted wiki", or runs /graphwiki. Also use when graphwiki
10
- build fails for lack of a model endpoint and the user wants quality prose
11
- instead of deterministic offline templates.
12
- ---
13
-
14
- # Coding-Assisted GraphWiki
15
-
16
- Run **graphwiki** with a coding assistant (Claude Code, Grok, Cursor, Codex, …)
17
- as the writer when no model API is reachable. This is **not** free-form repo
18
- documentation (OpenWiki/deepagents style). Coverage, context, citations, and
19
- gates stay owned by the Python tool.
20
-
21
- ## Frozen invariants (do not break)
22
-
23
- | Layer | Owner | Agent may… |
24
- |---|---|---|
25
- | What to document (coverage law) | `graphwiki plan` | Never decide, never drop a required page |
26
- | What facts each page gets | `graphwiki` context assembly | Never invent members/callers/paths |
27
- | Narrative prose | **this skill / the agent** | Rewrite only the prose under the four `##` sections |
28
- | Frontmatter, Mermaid, fact lists | Python / offline scaffold | **Never edit** |
29
- | Validation | `graphwiki validate` | Must pass before claiming done |
30
-
31
- **Anti-pattern:** grepping the repo and writing new path citations. That
32
- reintroduces hallucination and fails the citation gate. Ground every claim in
33
- facts already present on the page (frontmatter + deterministic sections).
34
-
35
- Full invariant detail: `references/invariants.md`.
36
-
37
- ## Prerequisites
38
-
39
- ```bash
40
- # Prefer uv on host
41
- uv tool install graphwiki # or: uvx graphwiki …
42
- # Engine auto-fetches on first build (or pin --engine-bin)
43
- graphwiki doctor . # engine + index healthy; no LLM needed
44
- ```
45
-
46
- If `doctor` fails on the engine, fix that first — the agent cannot replace the
47
- graph engine.
48
-
49
- ## Modes
50
-
51
- ### A. Assisted build (no LLM API) — default
52
-
53
- Use when Ollama/NIM/cloud are unavailable or the user wants agent-written prose.
54
-
55
- ```
56
- 1. Scaffold (deterministic, no network):
57
- graphwiki build . --backend offline --no-web
58
- # optional: --out PATH, --allow-defer GLOB, --engine-bin PATH
59
-
60
- 2. Narrate every required page under wiki/ (see "Narration recipe").
61
- Prioritize high x-hotspot-score pages first if the tree is large.
62
-
63
- 3. Gate:
64
- graphwiki validate .
65
-
66
- 4. Optional viewer:
67
- graphwiki web --no-open # or open the URL
68
- ```
69
-
70
- ### B. Assisted update (incremental)
71
-
72
- ```
73
- 1. graphwiki update . --backend offline --no-web
74
- 2. Narrate only pages whose body still matches the offline stub signature
75
- (see "Stub detection") or that the user named.
76
- 3. graphwiki validate .
77
- ```
78
-
79
- ### C. Editorial polish (prose only)
80
-
81
- Existing wiki already gate-valid; user wants better narrative without a rebuild.
82
-
83
- ```
84
- 1. Do NOT re-run build (preserves state hashes).
85
- 2. Edit prose only under the four ## sections.
86
- 3. graphwiki validate .
87
- ```
88
-
89
- ### D. Consume wiki while coding (read path)
90
-
91
- Not generation. Point the agent at `wiki/.ai-context.md` and relevant pages,
92
- or run `graphwiki serve` (MCP: `read_wiki_structure` / `read_wiki_contents` /
93
- `ask_question`). Do not invent pages in this mode.
94
-
95
- ## Stub detection
96
-
97
- Offline scaffold prose is graph-grounded but thin. Identify stubs by content:
98
-
99
- ```bash
100
- # Offline template signature (primary)
101
- rg -n "documented from the code knowledge graph" wiki -g '*.md'
102
-
103
- # Optional: short Architecture-only stubs
104
- rg -n "The graph records no internal sub-structure" wiki -g '*.md'
105
- ```
106
-
107
- A page that still contains either phrase needs narration. After rewrite, those
108
- phrases must be gone.
109
-
110
- ## Narration recipe (one page)
111
-
112
- Load `references/page-format.md` once; apply for every page.
113
-
114
- 1. **Read** the page fully (frontmatter + body). The page *is* the context —
115
- it already carries resource, callers, blast radius, public surface, tests,
116
- child modules, Mermaid.
117
- 2. **Do not** open source files to "learn more" for new citations. Optional
118
- peek at `resource` only to phrase responsibility accurately; still cite only
119
- names/paths already on the page.
120
- 3. **Rewrite only** the narrative paragraphs under:
121
- - `## Purpose & Capabilities`
122
- - `## Architecture`
123
- - `## Key Types`
124
- - `## How It Fits`
125
- 4. **Leave untouched:**
126
- - Entire YAML frontmatter
127
- - `**Child modules:**` lists
128
- - Fenced ` ```mermaid ` blocks
129
- - `**Public surface:**` / `**Used by:**` / `**Blast radius**` / `**Tests:**` lists
130
- 5. **Prose rules** (same intent as the online writer system prompt):
131
- - Concrete, not boilerplate; explain the graph facts, do not restate raw lists
132
- - Reference ONLY symbols/paths already present on the page
133
- - Inline-code path citations (`foo/bar.py`) must resolve to something the
134
- graph indexed — safest: only cite `resource`, list basenames, or symbols
135
- already shown
136
- - No new frontmatter, no code fences in narrative, no inventing callers/tests
137
- - Clear anti-stub floors (chars of body including deterministic sections —
138
- offline scaffold already clears them; keep depth, do not shrink):
139
- Subsystem ≥200, Module/Feature/Entrypoint ≥120, API/DataModel ≥80
140
- 6. **Write** the file in place under `wiki/` (default `--out`).
141
-
142
- ### Quality bar per section
143
-
144
- | Section | Write |
145
- |---|---|
146
- | Purpose & Capabilities | 1–2 short paragraphs: what it owns; why it exists |
147
- | Architecture | How it is structured; narrate Mermaid/children if present — do not redraw |
148
- | Key Types | What the important public symbols are *for* (names must already be listed) |
149
- | How It Fits | Callers, blast radius, tests — ground every claim in the page's lists |
150
-
151
- If a list is empty, say so plainly (leaf / no public surface / untested) — do not invent.
152
-
153
- ## Batch strategy
154
-
155
- For large wikis:
156
-
157
- 1. Run offline scaffold once.
158
- 2. Sort targets by `x-hotspot-score` descending (frontmatter).
159
- 3. Narrate in batches of 5–15 pages; after each batch run
160
- `graphwiki validate .` so failures stay local.
161
- 4. Skip deferred / non-required concepts (they may be absent from disk by design).
162
-
163
- Do not hand-create pages for concepts missing from the plan — re-run
164
- `graphwiki build/update --backend offline` to materialize them.
165
-
166
- ## Failure handling
167
-
168
- | Symptom | Action |
169
- |---|---|
170
- | `doctor` engine missing | Fix engine (`--engine-bin` or auto-fetch); do not free-write a wiki |
171
- | `validate` structure/quality fail | Fix the named page; usually leftover TODO or stub-thin prose |
172
- | `validate` citations fail | Remove or replace the invented path token; re-validate |
173
- | `validate` coverage fail | Re-run offline build/update — do not invent missing pages |
174
- | User wants cloud/local API instead | Prefer `graphwiki build . --backend ollama\|nim\|…` over this skill |
175
-
176
- ## Done criteria
177
-
178
- - [ ] `graphwiki validate .` exits 0
179
- - [ ] No page still contains `documented from the code knowledge graph`
180
- - [ ] Frontmatter and deterministic fact lists unchanged from scaffold
181
- - [ ] Summary for the user: page count narrated, validate OK, how to serve
182
- (`graphwiki web` or `graphwiki serve`)
183
-
184
- ## CLI cheat sheet
185
-
186
- ```bash
187
- graphwiki doctor .
188
- graphwiki build . --backend offline --no-web
189
- graphwiki update . --backend offline --no-web
190
- graphwiki validate .
191
- graphwiki web
192
- graphwiki serve --out wiki # needs mcp extra
193
- ```
194
-
195
- Backend note: coding-agent CLIs are intentionally **not** `--backend` values.
196
- This skill is the supported agent path: scaffold offline → agent narrates →
197
- gates. See product README.
198
-
199
- ## Additional resources
200
-
201
- - `references/invariants.md` — why agents must not traverse for generation
202
- - `references/page-format.md` — frontmatter + section contract + edit boundaries
@@ -1,127 +0,0 @@
1
- # Wiki page format (edit contract)
2
-
3
- Default output tree: `<repo>/wiki/` (`--out` overrides).
4
-
5
- ## Tree
6
-
7
- ```
8
- wiki/
9
- .ai-context.md # index — regenerate via tool, do not hand-edit
10
- architecture.md # system Mermaid — tool-owned
11
- log.md # run log — tool-owned
12
- .state/ # refresh hashes — tool-owned, do not edit
13
- systems/*.md # Subsystem + Module
14
- features/*.md # Feature
15
- entrypoints/*.md # Entrypoint
16
- reference/*.md # API, DataModel, Dependency, ADR, Runbook (when present)
17
- ```
18
-
19
- ## Frontmatter (never rewrite)
20
-
21
- ```yaml
22
- ---
23
- description: <routing key — "open when…"; not a summary>
24
- resource: <canonical path or symbol>
25
- title: <concept title>
26
- type: Module | Subsystem | Feature | Entrypoint | …
27
- x-callers: […]
28
- x-grounded-paths: […]
29
- x-hotspot-score: <float or null>
30
- ---
31
- ```
32
-
33
- - `type` is a frozen vocabulary — do not invent types.
34
- - `description` is a **routing decision** for agents ("should I open this?"),
35
- not a prose summary. Leave the offline/tool value unless the user explicitly
36
- asks to improve routing keys (then keep the same shape).
37
-
38
- ## Body structure (edit boundaries)
39
-
40
- ```markdown
41
- ## Purpose & Capabilities
42
-
43
- <NARRATIVE — agent may rewrite>
44
-
45
- ## Architecture
46
-
47
- <NARRATIVE — agent may rewrite>
48
-
49
- **Child modules:** # KEEP if present
50
- - `…`
51
-
52
- ```mermaid # KEEP entire fence if present
53
-
54
- ```
55
-
56
- ## Key Types
57
-
58
- <NARRATIVE — agent may rewrite>
59
-
60
- **Public surface:** # KEEP if present
61
- - `…`
62
-
63
- ## How It Fits
64
-
65
- <NARRATIVE — agent may rewrite>
66
-
67
- **Used by:** # KEEP if present
68
- - `…`
69
-
70
- **Blast radius** — …: # KEEP if present
71
- - `…`
72
-
73
- **Tests:** # KEEP if present
74
- - `…`
75
- ```
76
-
77
- ### Editable
78
-
79
- - Paragraphs (and optional short bullets **only** under Key Types) that sit
80
- directly under the four `##` headings, **above** the bold fact panels.
81
-
82
- ### Forbidden to change
83
-
84
- - YAML frontmatter block
85
- - Heading titles (`## Purpose & Capabilities`, etc.)
86
- - Bold labels and every list item under them
87
- - Mermaid code fences
88
- - Adding new top-level `##` sections
89
- - Adding code-sample fences in narrative (prose only)
90
-
91
- ## Offline stub signature
92
-
93
- Scaffold purpose lines look like:
94
-
95
- ```text
96
- `{title}` is a {module|subsystem|…} documented from the code knowledge graph; its
97
- canonical resource is `{resource}`. …
98
- ```
99
-
100
- Replace that entire purpose paragraph with real narrative. Same for architecture
101
- lines that only say "The graph records no internal sub-structure…" when richer
102
- context (children, mermaid, callers) is available to explain.
103
-
104
- ## Example good edit (shape only)
105
-
106
- **Before (offline):**
107
-
108
- ```markdown
109
- ## Purpose & Capabilities
110
-
111
- `writer` is a module documented from the code knowledge graph; its canonical
112
- resource is `src/graphwiki/writer.py`. The graph associates 12 symbol(s) with it,
113
- of which 4 form its public surface.
114
- ```
115
-
116
- **After (assisted):**
117
-
118
- ```markdown
119
- ## Purpose & Capabilities
120
-
121
- `writer` owns page narration for a single pre-assembled concept. It turns graph
122
- context into frontmatter plus structured body sections, using either an
123
- OpenAI-compatible completion or deterministic offline templates. Callers feed it
124
- one concept at a time; it does not plan coverage or query the graph.
125
- ```
126
-
127
- Facts (resource, public surface list below) stay exactly as scaffolded.