DR McCullochHOMESHOPABOUT

March 2026 · TypeScript

Concept Weaver

I designed Concept Weaver as a fork of VS Code and built the first version as a VS Code extension, working with Claude Code over two days in March 2026. The thing you edit in it is a graph of ideas, each idea a node and each link between two of them an edge with a direction, a weight and a type, so that a document is whatever you get by tracing a path from node to node.

To the graph

Nodes and edges

A node is what you get when you start typing: free-form prose in markdown, which is the default, and which you can promote at any time to a concept with a title, tags and a body. Nodes are joined by edges, and every edge is directed, so that “A grounds B” is a different edge from “B grounds A”, weighted with a strength from 0.0 to 1.0, typed from a controlled vocabulary of five relations that you can add to and annotated with an optional note about the relationship.

The graph is stored as one YAML file of nodes and edges with a markdown file for each node, so git and grep work on it as they would on any text and every change to the graph shows up as a clean diff.

groundsweight 0.9Node ANode B
groundsextendstensionsappliesforeshadows

The five edge types

grounds

A supports, justifies or underlies B.

“Positionality requirement grounds the scope statement”0.7

extends

B continues or elaborates A, the way one step of an argument leads to the next.

“Constraint transparency reveals binary as instance of its own argument”0.9

tensions

A and B contradict each other and the text leaves the contradiction open.

“Fire as discrete vs digital as ongoing process”0.9

applies

A is a method, view or operation that puts the concept B into practice.

document view → path traversal0.85

foreshadows

A anticipates B, which a later part of the text makes explicit.

“Ch9 → Ch11: space between figures → third sibling named Goronwy”1.0

Four of these examples are notes I wrote on edges in the GORONWY argument map and the applies example is an edge from the design graph, which has no note. Every edge is directed, so reversing one would make a different statement.

The graph in three layouts

There are two graphs here: the ideas in Concept Weaver’s own design document and a slice of the argument map of my book GORONWY, covering the prologue and Chapter 1 and the arguments that its foreshadowing thread passes through on the way to Chapter 11. The three buttons lay out the same nodes in different ways: Flow puts each chapter in its own column, Sequence lists every node from top to bottom in reading order and Force lets the nodes repel each other while the edges pull linked ones together until the layout settles. Hovering over a node lights up everything it links to, and clicking nodes one after another builds a document in the reading panel, a saved path through the graph that reads from start to finish.

nodeweight 0.3 and 1.0

Click a type to show only its edges and click it again to show them all.

drawing the graph…

Calling the AI

The model writes nothing until you call it, and when you do it works on a subgraph, the nodes you selected plus their neighbours to a depth you set, with a summary of the whole graph always included for orientation. No model runs on this page, but pressing shows what a suggestion looks like: I held a handful of edges back from the design graph, and the button draws them as white dashed lines with a panel where you can accept or reject each one.

The six AI operations in the design document
Quoted from the design document
OperationInputOutput
Suggest connectionsSelected node(s)Proposed edges with type, weight and reasoning
Draft bridge proseTwo or more nodesA new text node connecting them, placed in the graph
Analyse structureWhole graph or subgraphReport: orphans, weak links, dense clusters, gaps
Propose pathStart + end node + constraintsAn ordered traversal forming a coherent document
Weight auditEdge type filterIdentifies edges whose weights don’t match node content
Promote/decomposeText or concept nodeSplit long text into concept nodes with edges, or merge several into one

Five views and two modes

The design gives the same data five views that can sit side by side, the graph on the left and the editor on the right for example, so that an edit in one shows up in all the others.

01

Files

The whole project workspace with its folders and git history, including the images, PDFs and data files that never become nodes in the graph.

02

Graph

The canvas above, where you drag, zoom and cluster nodes and where the edges the AI suggests appear as dashed lines for you to accept or reject.

03

Document

A path through the graph set out as text you read and edit from start to finish, in which reordering the nodes reorders the document and the transitions between nodes are prose you can edit.

04

Tapestry

The adjacency matrix of the whole graph as an editable spreadsheet, with a row for each source node, a column for each target and the edge weight in each cell, which you set by clicking the cell.

05

Editor

The Monaco editor from VS Code, always open, with syntax highlighting, multiple cursors, the diff view and everything else VS Code has.

Guided

The graph and document views are the most prominent, the command palette is simplified and the AI appears as a sidebar you talk to, asking it to “help me connect these ideas”.

Power

The full VS Code with the graph and tapestry views, graph query commands in the palette, keyboard shortcuts for every node and edge operation, scripting through the API and custom AI providers.

The code and the data

The graph engine is written in TypeScript and handles adjacency matrices, neighbour expansion and path-finding, and the reader on this page runs a copy of its breadth-first findPath, printed below, to fill the gaps between nodes you choose that have no edge between them. I tested it on the argument map of GORONWY, which divides the book from the prologue to the coda into 209 arguments of about a paragraph each and joins them with 242 typed, weighted edges.

findPath(from: string, to: string): string[] | null {
  const visited = new Set<string>();
  const queue: string[][] = [[from]];
  while (queue.length > 0) {
    const path = queue.shift()!;
    const current = path[path.length - 1];
    if (current === to) return path;
    if (visited.has(current)) continue;
    visited.add(current);
    for (const edge of this.edgesFrom(current)) {
      if (!visited.has(edge.to)) queue.push([...path, edge.to]);
    }
  }
  return null;
}

src/core/graph.ts

Scope

Files stay first-class, and the graph is stored inside an ordinary workspace next to everything that never becomes a node. The AI can propose edges and draft prose, but nothing it suggests enters the graph until you accept it. The typed, weighted edges are what set it apart from a note-taking app, and everything is kept as plain files on disk that git can track and that copy from one machine to another.

Edges in the full GORONWY argument map, by type
  • extends183
  • grounds19
  • applies17
  • tensions14
  • foreshadows9

Of the 242 edges, 183 are extends, one argument continuing the one before it. The 9 foreshadows edges are the rarest, and every one of them leads towards Chapter 11, where the third sibling left out of the Prometheus myth is finally named Goronwy.

All 209 arguments and 242 edges are drawn in full on the GORONWY argument map.

← back home