decision-topology
// Records the structure of conversations where ideas evolve, branch, get rejected, pivot, or combine. Saves each structural shift as a node in a local JSON tree the user can browse on demand. Zero network access, zero external dependencies. Covers proposals, rejections, pivots, and merges with cross-t
Decision Topology
Records how ideas branch and evolve during conversations, producing a browsable tree the user can review at any time. Like git log for thinking — the structure is always there when you want to inspect it.
Privacy note: This skill is installed and enabled by the user. All data stays on disk in the configured trees directory — nothing is sent externally. The user can view, delete, or relocate their trees at any time.
Security Properties
- Zero network access — no HTTP calls, no sockets, no DNS lookups. Works fully offline.
- Zero external dependencies — uses only Node.js built-in
fsandpathmodules. - No conversation content stored — the script enforces length limits on all persisted text fields (summary: 200 chars, reasoning: 300 chars, topic: 120 chars, concept: 50 chars). Text exceeding limits is truncated. This is a code-level guardrail, not just a policy.
- No process spawning — no
child_process, noexec, noeval, noFunction(). - Stdin-only input — all user-derived content is piped via stdin as JSON to prevent shell injection. See SECURITY.md for details.
- Path containment enforced — all file arguments are stripped to basename and resolved inside the canonical trees directory. Absolute paths and
..traversal are rejected at runtime. - User-controlled storage — trees are local JSON files the user can inspect, move, or delete at any time.
- ID generation — uses
Math.random()for 6-char hex node IDs. Cryptographic randomness is not needed — IDs only require tree-local uniqueness across 5-30 nodes.
Activation
Active by default (user can set always: false in metadata to require explicit invocation). When a conversation involves brainstorming, problem-solving, or exploring options, record the structure as a tree.
Skip pure Q&A ("what time is it"), greetings, and small talk.
Output Style
Do not insert status messages about tree operations into the conversation. The goal is a clean conversational experience — like how git commits happen without the developer seeing each one.
- Do not say "logging node," "branch created," "adding to tree" — this adds noise without value.
- Do not change your conversational behavior because of the skill. The user gets the same conversation they'd get without it.
- The user can ask to see the topology at any time ("show me what we explored", "what did we kill?").
- Think: git commits in the background. Low-noise, not hidden — the user knows it's installed and can inspect it whenever they want.
When to Record a Node
Create a node when:
- You propose a distinct idea, direction, solution, or option
- The user introduces a new angle or topic
- The user rejects, pushes back, or corrects — this is a branch kill, always record the reason
- The conversation pivots direction because of something said
- An insight combines elements from earlier dead branches (merge node)
- An analogy, metaphor, or reframe changes how the problem is understood
- A question redirects the exploration (pivot node)
Skip when:
- Same idea continues without meaningful evolution
- Minor refinement within the same direction
- Trivial "no" to something small that doesn't change direction
- Factual answers to factual questions
Depth calibration: A good tree has 5-30 nodes recording the shape of an exploration. Not 200 nodes transcribing every sentence. Only create nodes when the direction of thinking meaningfully shifts. Heuristic: would this rejection or pivot change what comes next? If yes, record it. If no, skip it.
Auto-Initialization
Do NOT create a tree when a conversation starts. Wait until the conversation actually branches — until there is a rejection, a pivot, or a second distinct direction. Only then initialize.
Most conversations won't need a tree. That's fine.
When initializing, auto-generate the filename from the date and a 2-4 word topic slug:
2026-02-24-business-model-exploration.json2026-02-24-vacation-planning.json
Auto-Association
When a conversation starts and ideas begin branching, check if this connects to an existing tree before creating a new one.
How to check: Run the associate command with the core topic:
echo '{"query": "short description of current topic"}' | node {baseDir}/scripts/topology.js associate
This scans existing trees and returns the best match with a relevance score.
- Score >= 0.4 — continue that tree (load it, add nodes to it)
- Score 0.25-0.4 — ambiguous. Ask the user naturally: "This feels related to [topic] we explored on [date]. Continuing that thread, or fresh start?"
- Score < 0.25 — new tree
Never ask the user to pick a tree by ID. If you need to disambiguate, ask naturally in conversation.
Setup
Script: {baseDir}/scripts/topology.js
Storage: Trees are stored in {baseDir}/trees/ by default. Override with the TOPOLOGY_TREES_DIR environment variable if you want trees stored elsewhere (e.g. in a memory directory for semantic search indexing).
Path containment: All file arguments are resolved to basenames inside the trees directory. Absolute paths and .. traversal are rejected — the script cannot read or write files outside the configured trees directory.
Invocation: Always pipe JSON args via stdin to prevent shell injection from user-derived content:
echo '<JSON args>' | node {baseDir}/scripts/topology.js <command>
Core Operations
Initialize a tree
echo '{"topic": "short topic description"}' | node {baseDir}/scripts/topology.js init
Returns the file path and root node ID. Remember both for the session.
Add a node
echo '{"file": "<path>", "parent_id": "<id>", "type": "proposal", "summary": "one-line description", "reasoning": "why", "concepts": ["keyword1", "keyword2"]}' | node {baseDir}/scripts/topology.js add-node
Types: proposal, pivot, merge. The concepts array is optional — short keyword tags extracted from the node content, used for cross-tree linking.
Kill a branch
echo '{"file": "<path>", "node_id": "<id>", "reason": "why it was rejected"}' | node {baseDir}/scripts/topology.js kill-branch
Then add the new direction as a child (pivot node linked to what was killed).
Merge branches
echo '{"file": "<path>", "source_ids": ["<id1>", "<id2>"], "summary": "merged insight", "reasoning": "combines X from A with Y from B"}' | node {baseDir}/scripts/topology.js merge
Fork from any node
echo '{"file": "<path>", "node_id": "<id>", "summary": "re-exploring from this point", "reasoning": "reason for revisiting"}' | node {baseDir}/scripts/topology.js fork
Node Types
| Type | When |
|---|---|
root | Core topic (created by init) |
proposal | You suggest a direction, idea, or option |
pivot | New direction that emerged from a rejection or redirection |
merge | Insight combining elements from multiple branches |
Status values: active (still exploring), dead (rejected, has killed_by), merged (combined into a merge node).
Viewing the Topology
The user does NOT need to learn slash commands. They ask naturally:
- "Show me what we explored"
- "What did we kill?"
- "What shape is this conversation?"
- "What paths did we reject and why?"
- "Go back to that idea about X"
You understand the intent and run the appropriate commands. Present results conversationally, not as raw script output.
/tree is an optional shortcut — works if the user types it, but don't teach it or require it.
Rendering
echo '{"file": "<path>"}' | node {baseDir}/scripts/topology.js render
After the tree, append a one-line summary: {N} branches explored, {M} killed, {K} active, depth {D}
List all trees
node {baseDir}/scripts/topology.js list
Statistics
echo '{"file": "<path>"}' | node {baseDir}/scripts/topology.js stats
Export as Mermaid
echo '{"file": "<path>"}' | node {baseDir}/scripts/topology.js export
Revisiting a dead branch
When the user asks about a killed path, find the node, present:
- What was proposed
- Why it was proposed
- Why it was killed
- What came after
Cross-tree analysis
node {baseDir}/scripts/topology.js analyze
Rebuilds the concept index, scans all trees, finds concepts appearing across multiple trees, reports which ideas keep surviving vs keep getting killed, identifies cross-root connections, and regenerates all companion .md files with updated cross-tree links and weights. Shows index health stats (total concepts, cross-tree count, orphans).
Query the concept index
echo '{"name": "trust"}' | node {baseDir}/scripts/topology.js concept
echo '{"list": true}' | node {baseDir}/scripts/topology.js concept
echo '{"orphans": true}' | node {baseDir}/scripts/topology.js concept
name— reverse-lookup: shows every node across every tree that references a conceptlist— all concepts sorted by cross-tree spread,*marks concepts spanning multiple treesorphans— concepts that exist in only one tree (candidates for future linking)
Rebuild concept index
node {baseDir}/scripts/topology.js rebuild-index
Full rebuild of concepts.json from all tree files. Also regenerates all companion .md files with cross-tree links and updated weights. Use as a recovery tool or after manual edits to tree JSON files.
Concept Index
A reverse-index at {trees_dir}/concepts.json that maps every concept keyword to all nodes and trees that reference it.
- Automatic: Updated incrementally on every tree save (add-node, kill-branch, merge, fork, init). No manual intervention needed.
- Cross-tree links: Companion .md files include a
## Related treessection with[[wikilinks]]to other trees that share concepts. Useful for semantic search indexing. - Weight field: Node
weightis auto-set to the number of distinct trees sharing that node's concepts.weight: 1= single-tree concept.weight: 2+= concept spans multiple trees. - Lazy discovery: Links form organically as nodes are added to any tree. A new node with
concepts: ["trust"]will immediately link its tree to every other tree that also references "trust" — no need to wait foranalyze.
Rules
- Clean output. Do not insert tree-operation status messages into the conversation. The user can inspect trees whenever they want.
- Judgment over completeness. Record the shape, not the transcript. 5-30 nodes per tree. Summaries only — never store verbatim conversation content.
- Causal links. Show WHY the conversation evolved, not just WHAT was said. Link rejections to pivots.
- Persist. Trees are JSON files that survive sessions. They can be searched if stored in an indexed directory.
- Continue, don't duplicate. If a conversation continues a previous topic, load and extend that tree.
- Graceful failures. Missing or corrupted tree — re-initialize. Missing node ID — say so clearly. Never crash.
- Natural interface. The user asks in plain language. You translate to the right operation. Slash commands are optional shortcuts, not the primary interface.
- Local only. All data stays on disk. No network calls, no external APIs, no telemetry. The user owns their data.