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
fs and path modules. - 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, no exec, no eval, no Function(). - 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:
- - INLINECODE10
- INLINECODE11
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:
CODEBLOCK0
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: INLINECODE12
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:
CODEBLOCK1
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
CODEBLOCK5
Fork from any node
CODEBLOCK6
Node Types
| Type | When |
|---|
| INLINECODE21 | Core topic (created by init) |
| INLINECODE23 |
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: INLINECODE31
List all trees
CODEBLOCK8
Statistics
CODEBLOCK9
Export as Mermaid
CODEBLOCK10
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 concept - INLINECODE33 — all concepts sorted by cross-tree spread,
* marks concepts spanning multiple trees - INLINECODE35 — 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 trees section with [[wikilinks]] to other trees that share concepts. Useful for semantic search indexing. - Weight field: Node
weight is 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 for analyze.
Rules
- 1. 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.
决策拓扑
记录对话中想法如何分支和演化,生成用户随时可浏览的树形结构。如同思维的git log——当你想要审视时,结构始终存在。
隐私说明: 此技能由用户安装并启用。所有数据仅存储在配置的树目录磁盘中——不会对外发送任何内容。用户可随时查看、删除或迁移其树结构。
安全属性
- - 零网络访问——无HTTP调用、无套接字、无DNS查询。完全离线运行。
- 零外部依赖——仅使用Node.js内置的fs和path模块。
- 不存储对话内容——脚本对所有持久化文本字段强制执行长度限制(摘要:200字符,推理:300字符,主题:120字符,概念:50字符)。超出限制的文本将被截断。这是代码级别的防护措施,而不仅仅是策略。
- 不生成子进程——无childprocess、无exec、无eval、无Function()。
- 仅标准输入——所有用户衍生内容通过stdin以JSON格式管道传输,防止shell注入。详见SECURITY.md。
- 强制路径隔离——所有文件参数被剥离为基本名称,并在规范树目录内解析。运行时拒绝绝对路径和..遍历。
- 用户控制存储——树结构是用户可随时检查、移动或删除的本地JSON文件。
- ID生成——使用Math.random()生成6字符十六进制节点ID。无需加密随机性——ID仅需在5-30个节点范围内保持树内唯一性。
激活
默认激活(用户可在元数据中设置always: false以要求显式调用)。当对话涉及头脑风暴、问题解决或选项探索时,将结构记录为树。
跳过纯问答(现在几点了)、问候和闲聊。
输出风格
不要在对话中插入关于树操作的状态消息。目标是干净的对话体验——就像git提交发生时开发者不会看到每个提交一样。
- - 不要说记录节点、分支已创建、添加到树——这会产生无价值的噪音。
- 不要因为此技能而改变你的对话行为。用户得到的对话与没有此技能时相同。
- 用户可以随时要求查看拓扑(展示我们探索了什么、我们否定了什么?)。
- 思考:后台的git提交。低噪音,不隐藏——用户知道它已安装,并可在需要时随时检查。
何时记录节点
创建节点时:
- - 你提出一个独特的想法、方向、解决方案或选项
- 用户引入新的角度或主题
- 用户拒绝、反驳或纠正——这是分支终止,始终记录原因
- 对话因某句话而转向
- 某个洞察结合了早期已废弃分支的元素(合并节点)
- 类比、隐喻或重构改变了问题的理解方式
- 某个问题重定向了探索方向(转向节点)
跳过时:
- - 同一想法继续发展,没有有意义的演变
- 同一方向内的细微改进
- 对不会改变方向的小事说不
- 对事实性问题的回答
深度校准: 一个好的树有5-30个节点,记录探索的形态。不是200个节点转录每个句子。仅在思维方向发生有意义转变时创建节点。启发式方法:这个拒绝或转向会改变接下来发生的事情吗?如果是,记录它。如果否,跳过它。
自动初始化
不要在对话开始时创建树。等待对话实际分支——直到出现拒绝、转向或第二个不同方向。只有那时才初始化。
大多数对话不需要树。这没问题。
初始化时,根据日期和2-4个词的主题摘要自动生成文件名:
- - 2026-02-24-商业模式探索.json
- 2026-02-24-假期规划.json
自动关联
当对话开始且想法开始分支时,在创建新树之前检查是否与现有树相关。
如何检查: 使用核心主题运行关联命令:
bash
echo {query: 当前主题的简短描述} | node {baseDir}/scripts/topology.js associate
这会扫描现有树并返回最佳匹配及其相关性分数。
- - 分数 >= 0.4 —— 继续该树(加载它,向其添加节点)
- 分数 0.25-0.4 —— 不明确。自然地询问用户:这感觉与我们在[日期]探索的[主题]相关。继续那个线程,还是重新开始?
- 分数 < 0.25 —— 新树
永远不要要求用户通过ID选择树。如果需要消除歧义,在对话中自然地询问。
设置
脚本: {baseDir}/scripts/topology.js
存储: 默认情况下,树存储在{baseDir}/trees/中。如果希望将树存储在其他位置(例如用于语义搜索索引的内存目录),可使用TOPOLOGYTREESDIR环境变量覆盖。
路径隔离: 所有file参数在树目录内解析为基本名称。拒绝绝对路径和..遍历——脚本无法在配置的树目录之外读取或写入文件。
调用: 始终通过stdin管道传输JSON参数,以防止用户衍生内容的shell注入:
bash
echo | node {baseDir}/scripts/topology.js <命令>
核心操作
初始化树
bash
echo {topic: 简短主题描述} | node {baseDir}/scripts/topology.js init
返回文件路径和根节点ID。在会话中记住两者。
添加节点
bash
echo {file: <路径>, parent_id:
, type: proposal, summary: 一行描述, reasoning: 原因, concepts: [关键词1, 关键词2]} | node {baseDir}/scripts/topology.js add-node
类型:proposal(提议)、pivot(转向)、merge(合并)。concepts数组可选——从节点内容中提取的简短关键词标签,用于跨树链接。
终止分支
bash
echo {file: <路径>, node_id: , reason: 被拒绝的原因} | node {baseDir}/scripts/topology.js kill-branch
然后将新方向添加为子节点(转向节点链接到被终止的内容)。
合并分支
bash
echo {file: <路径>, source_ids: [, ], summary: 合并的洞察, reasoning: 将A中的X与B中的Y结合} | node {baseDir}/scripts/topology.js merge
从任意节点分叉
bash
echo {file: <路径>, node_id: , summary: 从此点重新探索, reasoning: 重新审视的原因} | node {baseDir}/scripts/topology.js fork
节点类型
| 类型 | 使用时机 |
|---|
| root(根) | 核心主题(由init创建) |
| proposal(提议) |
你建议一个方向、想法或选项 |
| pivot(转向) | 从拒绝或重定向中产生的新方向 |
| merge(合并) | 结合多个分支元素的洞察 |
状态值:active(活跃,仍在探索)、dead(已废弃,被拒绝,有killed_by)、merged(已合并,合并到合并节点中)。
查看拓扑
用户不需要学习斜杠命令。他们自然地询问:
- - 展示我们探索了什么
- 我们否定了什么?
- 这个对话是什么形状?
- 我们拒绝了哪些路径,为什么?
- 回到关于X的那个想法
你理解意图并运行适当的命令。以对话方式呈现结果,而不是原始脚本输出。
/tree是可选的快捷方式——如果用户输入它则有效,但不要教授或要求它。
渲染
bash
echo {file: <路径>} | node {baseDir}/scripts/topology.js render
在树之后,附加一行摘要:{N}个分支已探索,{M}个已废弃,{K}个活跃,深度{D}
列出所有树
bash
node {baseDir}/scripts/topology.js list
统计信息
bash
echo {file: <路径>} | node {baseDir}/scripts/topology.js stats
导出为Mermaid
bash
echo {file: <路径>} | node {baseDir}/scripts/topology.js export
重新审视已废弃分支
当用户询问关于已废弃路径时,找到该节点,呈现:
- - 提出了什么
- 为什么提出
- 为什么被废弃
- 之后发生了什么
跨树分析
bash
node {baseDir}/scripts/topology.js analyze
重建概念索引,扫描所有树,查找跨多棵树出现的概念,报告哪些想法持续存活与被废弃,识别跨根连接,并重新