| name | exgraph |
| description | Build and maintain a single graph.yaml capturing the durable state of long-running, open-ended, multi-path work, then let the user render it with the exgraph CLI. Use for research, thesis, long-form fiction, world-building, product discovery, architecture migration, or any investigation with competing paths, hypotheses, and dead branches. Triggers when the user wants to track branches or dead ends, asks "which path am I on" or "what was I doing", or resumes work after days/weeks. Do not use for simple task lists, linear step-by-step execution, or one-shot conversations. |
ExGraph Skill
ExGraph is a structure plus a renderer, not an ontology. Your job is to maintain one graph.yaml that captures the durable state of a long-running exploration. The user runs the CLI to view it.
View = render(graph.yaml)
You edit YAML only. You never edit generated viewer files.
If the project clearly fits but no graph exists yet, offer: "Want me to set up an exploration graph to track the state of this?"
The model (this is the whole spec)
A graph has four parts:
nodes — durable things worth remembering. Each needs id and title.
edges — directed relations, from → to.
sets — named lists of node ids, surfaced as filters/highlights.
render — which views to offer.
The engine does NOT define what nodes and edges mean. kind on a node and kind on an edge are free-form strings you choose. There is no fixed taxonomy. Pick a vocabulary that fits the user's domain and stay consistent within one graph.
The only field with a closed value set is state:
seed · candidate · active · blocked · done · closed · archived
Use any other value and check reports an error. state is a project-management status, not a logical truth value — express logic (one thing supporting, conflicting with, or replacing another) through edges.
Top-level shape
version: 0.1
title: ...
description: ...
created: ...
updated: ...
nodes: [...]
edges: [...]
sets: {...}
render:
default_view: flow
views: [flow, cluster, frontier]
description, created, and updated are passed through untouched — the engine does not parse or validate them (any string is accepted). The engine imposes no format, but prefer ISO 8601 with timezone for the date fields, e.g. 2026-06-14T18:39:30+08:00 (date-only 2026-06-14 is also fine). Set created once and refresh updated when you edit the graph.
Node and edge fields
nodes:
- id: n1
title: ...
kind: ...
state: active
note: ...
state_note: ...
tags: [...]
extra: {...}
edges:
- from: n1
to: n2
kind: ...
label: ...
note: ...
tags: [...]
extra: {...}
Prefer note and state_note for nuance. Do not split nuance into structured fields unless the engine needs to parse it (it does not). Put domain-specific data in extra, never expand the core shape.
Defaults when omitted: a node with no state is treated as active; a node with no kind becomes node; an edge with no kind becomes relates_to. Because omitted-state defaults to active, and active nodes outside every set raise a warning, give real nodes an explicit state and put them in a set.
What the engine actually recognizes
Beyond the structural rules, check knows a few specific values. Writing these correctly lets you produce a graph with no warnings. Everything else is free.
Hard errors (block rendering):
- Missing
version, title, or nodes.
- A node missing
id or title; a duplicate id.
- An edge
from/to or a set entry referencing a node that does not exist.
- A
state value outside the closed list.
Soft semantics (warnings only — the engine looks at these literal values):
- Set names
mainline and frontier: if either is missing or empty, you get a warning. The viewer treats them as the believed path and the active focus. Other set names are free and become extra filters.
- A
closed node with no state_note → warning. Always explain why a branch was closed.
- A
blocked node with no state_note and no incoming edge of kind blocks or depends_on → warning. So either write state_note or connect the blocker with one of those two edge kinds.
- An edge with no
kind → warning (it is treated as a generic relates_to).
- Too many edges of kind
relates_to → warning. relates_to is the vague fallback; prefer a meaningful kind of your own.
- An
active node in no set at all → warning. Put active work in mainline, frontier, or any custom set.
So the only kind strings the engine reacts to are blocks, depends_on, and relates_to (for the lints above). Every other kind you invent is rendered generically and is perfectly valid.
Workflow
1. Understand the situation.
2. Confirm or create graph.yaml (exgraph init [file]).
3. Add/update nodes, edges, sets.
4. Run exgraph check <file>; fix errors, address warnings.
5. Point the user at exgraph serve <file>.
Commands assume the exgraph CLI is installed. If it is not, ask user run npm install -g @frostime/exgraph.
Add a node only when the information affects future judgment — a goal, a question, an attempt, a piece of evidence, a decision, a risk, an artifact. Skip transient conversation noise.
When status changes, update state. For blocked/closed, add state_note (reason, what is reusable, reopen condition).
Confirm before / safe to do
Ask the user first before:
- Substantially changing
sets.mainline.
- Closing an
active node, or deleting nodes.
- Promoting a tentative node into a firm decision.
- Merging or replacing major routes.
Usually safe to do directly:
- Adding tentative or evidence/artifact nodes.
- Adding edges that the conversation clearly implies.
- Updating
sets.frontier to match the current discussion.
- Adding
state_note to explain a blocked/closed state.
Reporting after edits
Report only the delta: nodes added/changed, edges added, sets changed, state transitions, and any diagnostics from exgraph check. Then give the view command. Do not dump the full YAML unless asked.
已更新:
- 新增节点 n3(候选路线 C)
- 新增边 n2 → n3
- frontier 更新为 [n1, n3]
查看:exgraph serve graph.yaml
Minimal example
kind values here (goal, option) are illustrative — choose your own vocabulary.
version: 0.1
title: 小说主线规划
nodes:
- id: g1
kind: goal
state: active
title: 完成长篇小说主线
tags: [mainline]
- id: o1
kind: option
state: closed
title: 女主早期背叛主角
state_note: >
关闭原因:破坏后续信任弧线。
可复用:背叛误会场景可改成第三方栽赃。
- id: o2
kind: option
state: active
title: 第三方栽赃导致信任危机
edges:
- from: o1
to: o2
kind: supersedes
label: 被替代
sets:
mainline: [g1, o2]
archive: [o1]
render:
default_view: flow
views: [flow, cluster, frontier]
Anti-patterns
- Turning every thought into a node (keep only durable information).
- Defaulting to
relates_to instead of a meaningful edge kind.
- Closing or blocking a branch without
state_note.
- Inventing structured fields for things that belong in
note or extra.
- Adding layout coordinates (the engine lays out the graph).
- Editing generated viewer files instead of
graph.yaml.