# 🗺️ dsh-context-map

[中文](README.md) · **English**

<p align="center">
  <b>Turn a conversation's context into an information-architecture map you can ship.</b><br>
  <sub>Goals · background · decisions · actions · artifacts · risks · open questions — one layered diagram, exported as SVG, interactive HTML and Mermaid.</sub>
</p>

<p align="center">
  <a href="CHANGELOG.md"><img src="https://img.shields.io/badge/version-0.3.0-0891b2?style=flat-square" alt="Version 0.3.0"></a>
  <a href="https://www.npmjs.com/package/dsh-context-map"><img src="https://img.shields.io/npm/v/dsh-context-map?style=flat-square&color=CB3837" alt="npm"></a>
  <a href="https://www.dsh.so/artifact/dsh-context-map-2/"><img src="https://cdn.dsh.so/badge/install/dsh-context-map-2@0.2.1-rc.5.svg" alt="dsh.so install: runtime verified (L5) on dsh 0.2.1-rc.5"></a>
  <a href="https://www.dsh.so/artifact/dsh-context-map-2/"><img src="https://cdn.dsh.so/badge/dsh-context-map-2.svg" alt="dsh.so risk: low"></a>
  <a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-16a34a?style=flat-square" alt="License"></a>
  <a href="package.json"><img src="https://img.shields.io/badge/node-%3E%3D20-339933?style=flat-square" alt="Node"></a>
  <a href="package.json"><img src="https://img.shields.io/badge/runtime%20deps-0-blue?style=flat-square" alt="Zero dependencies"></a>
  <a href="package.json"><img src="https://img.shields.io/badge/dsh-%3E%3D0.2.0--rc.1-4D6BFE?style=flat-square" alt="DSH"></a>
  <a href="https://github.com/gavinff1990/dsh-context-map/stargazers"><img src="https://img.shields.io/github/stars/gavinff1990/dsh-context-map?style=flat-square&color=4D6BFE" alt="Stars"></a>
</p>

<p align="center">
  <a href="#when-to-use-it">Scenarios</a> ·
  <a href="#quick-start">Quick start</a> ·
  <a href="#examples">Examples</a> ·
  <a href="#capabilities">Capabilities</a> ·
  <a href="#parameters">Parameters</a> ·
  <a href="#installation--troubleshooting">Troubleshooting</a> ·
  <a href="#how-it-differs">Comparison</a> ·
  <a href="#faq">FAQ</a>
</p>

<p align="center">
  <img src="docs/context-map-example.svg" alt="An example map generated by dsh-context-map: the goal on the left, four branches for research, design, delivery and risks on the right" width="900">
</p>

<p align="center">
  <sub>Generated by the plugin itself from <code>examples/spec.json</code> → <code>docs/context-map-example.svg</code> (23.9 KB). No design tool involved.</sub>
</p>

---

**The missing step between a long conversation and a document.**

At the end of a long session the valuable part is buried: why the direction changed, which file is the deliverable, what is still undecided. Scrolling back does not recover it, and screenshots carry no structure.

`dsh-context-map` gives the agent one tool, `context_map`: the agent distills the conversation into nodes, the plugin **validates → lays out → renders**. What you get is a left-to-right layered structure, not a radial mind map — the parent/child relationship reads in one pass.

- **Zero runtime dependencies**, no API key, no network, no session-log access
- **Deterministic layout**: the same input always produces the same map; a same-day rebuild is byte-identical, so the artifact can be committed
- **Four artifacts**: vector SVG, zoomable searchable HTML, reviewable Mermaid Markdown, and a JSON that re-renders and diffs

> **Rather not install it by hand?** Send this to your agent:
>
> ```text
> Install the dsh-context-map plugin: dsh plugin --profile web add dsh-context-map
> Then run dsh --profile web --dump-config and confirm the context-map row is there.
> ```

## When to use it

| Situation | What you say to the agent | What ends up on the map |
| :--- | :--- | :--- |
| 🧭 **Project retro** | "Map the context of this discussion" | Goal, dead ends, final calls, next steps |
| 🧩 **Tech choice** | "Map the options we compared and why we picked B" | Candidates, rejected reasons, decision, risks |
| 📝 **PR write-up** | "Map what this change is for, put it under `docs/`" | Motivation, touch points, blast radius, leftovers |
| 📋 **Requirement breakdown** | "Turn this brief into an information-architecture map" | Goal, constraints, actions, open items |
| 🔥 **Incident review** | "Map the context of this failure" | Symptom, root cause, response, prevention |

It works in the other direction too: **the map is first for you**. Compressing a fuzzy discussion into 8–20 nodes exposes the parts you have not actually decided — they can only come out as `question`.

## Quick start

### 1. Install

Requirements: DeepSeek Harness (with the `dsh plugin` command) + Node.js ≥ 20 + **pnpm on the DSH process's PATH**.

```sh
# DSH installs plugins through pnpm
npm install -g pnpm

dsh plugin --profile web add dsh-context-map
```

Other routes:

| Route | Command |
| --- | --- |
| GitHub tag | `dsh plugin --profile web add github:gavinff1990/dsh-context-map#v0.1.0` |
| git repository | `dsh plugin --profile web add git+https://github.com/gavinff1990/dsh-context-map.git` |
| Local checkout | `dsh plugin --profile web add /absolute/path/to/dsh-context-map` |

Verify the row made it into the composed config:

```sh
dsh --profile web --dump-config | grep -A1 context-map
# - id: context-map
#   name: dsh-context-map
```

### 2. Ask for it

> Map the context of this discussion.
> Draw an architecture map of this project, with risks and open questions.

The agent calls `context_map`, turns the conversation into nodes, and renders them. Ask for a location ("put it under `docs/`") if you care where the files land.

### 3. Open the artifacts

```
.dsh-context-map/context-map.svg     # paste into a document
.dsh-context-map/context-map.html    # open in a browser: zoom, search, filter
.dsh-context-map/context-map.md      # paste into a PR or a wiki
.dsh-context-map/context-map.json    # the data layer; pass it to update next time
```

**Prefer an absolute `outDir`**: the plugin process working directory is not necessarily the session workspace.

## Examples

Three real artifacts, all generated from `examples/spec.json`:

| File | Size | What is inside |
| --- | --- | --- |
| [`context-map-example.svg`](docs/context-map-example.svg) | 23.9 KB | One vector file; both themes inline, `prefers-color-scheme` applies |
| [`context-map-example.html`](docs/context-map-example.html) | 39.4 KB | The same map plus toolbar and outline rail; offline, no CDN |
| [`context-map-example.md`](docs/context-map-example.md) | 5.6 KB | Mermaid `flowchart` + nested outline + kind counts + node inventory |
| `context-map-example.json` | ~4 KB | The map as data: nodes, links, revision number and history; the file `update` reads |

## Capabilities

| Surface | Best for | What you get |
| :--- | :--- | :--- |
| 🖼️ **SVG** | Documents, README, slides | Vector, adapts to light/dark |
| 🖱️ **HTML** | Reading it yourself or sharing it | Zoom, fit, search, filter by kind, outline jumps, A3 print |
| 🧾 **Markdown** | Pull requests and wikis | Mermaid source + outline + inventory, reviewable diff |
| 🗃️ **JSON** | Revising the same map later | A re-renderable sidecar: revision, history, original nodes |
| ⌨️ **CLI** | CI, batch rebuilds, hand-checking a spec | Same renderer; `--json` dumps the geometry |
| 🔎 **session_facts** | Citing what actually happened | Files written/read, commands run, failures, for this session |

### 🧭 A layered layout, not a radar chart

Depth is a column: column width from the widest card, parents centered on their children's subtree band, siblings stacked without overlap. Cross-branch links (a risk that blocks an action) are dashed and routed around the right edge instead of crossing cards. The algorithm and its invariants live in [`docs/ARCHITECTURE.md`](docs/ARCHITECTURE.md).

### 🎨 Semantic colour, stable across maps

Eight kinds, one accent and one tint each: goal blue, context slate, decision violet, action green, artifact amber, risk rose, question cyan, note grey. Status (`done` / `active` / `planned` / `blocked`) is a dot on the left edge.

### 🖱️ What the HTML gives you

- `/` focuses search, `+` / `-` zoom, `0` resets, `f` fits, `Esc` clears
- Click a legend entry to hide or show that kind
- Click any line in the outline rail to jump to and flash that node
- `◐` cycles auto / light / dark; `⎙` prints

### 🔎 Session facts, so nodes cite evidence

A model writing nodes from memory will happily turn "I think I changed that file" into "I changed that file". The plugin also listens to this session's `tools/result` events and records what actually happened — files written and read, commands run, calls that failed — in memory only. No session-log parsing, nothing on disk.

Call `session_facts` first when the map should cite real paths, then use its `path` values in `artifact` and `action` nodes:

```text
session_facts()                       → files and commands for this session
session_facts({ scope: "files" })     → files only
session_facts({ clear: true })        → read then reset, to start a new phase
```

### 🔁 Revisions, so a map stays maintainable

The first render writes a `.json` beside the artifacts. To revise, hand that path back as `update` instead of re-describing the whole map:

```text
context_map({ title: "...", update: "docs/context-map/context-map.json", nodes: [...] })
```

The plugin diffs the new outline against the previous revision:

- **Added** nodes get a dashed outline and a `+` badge
- **Changed** nodes get a heavier outline and a `~` badge, and the Markdown lists which fields moved
- **Removed** nodes stay off the map but appear in the Markdown change list and in the JSON
- The revision number increments, and the last five revisions' counts are kept in `history`

The HTML export gains a `±` button that dims everything except this revision's changes. A map can now follow a project instead of being redrawn each time.

### 🔒 No network, no dependencies, no configuration

No `fetch`, no socket, no telemetry, no subprocess, no `eval`. It writes exactly three files under `outDir` and refuses the filesystem root, `~/.dsh`, `~/.ssh`, `~/.aws` and similar locations.

## Parameters

| Parameter | Required | Meaning |
| --- | --- | --- |
| `title` | ✅ | Map title, one line |
| `nodes` | ✅ | Node array, capped at 300 |
| `subtitle` | | The question this map answers |
| `links` | | Cross-branch dependencies the tree cannot express, capped at 150 |
| `outDir` | | Output directory, default `.dsh-context-map` |
| `slug` | | File base name, derived from the title as `[a-z0-9-]` |
| `formats` | | Subset of `svg` / `html` / `md` / `json` |
| `update` | | Path to the previous `<slug>.json`. With it the call is a **revision**: added and changed nodes are marked, removed ones are listed, the revision number increments |
| `theme` | | `light` / `dark` / `auto` |
| `locale` | | `zh` / `en`, default `zh` |

Node fields: `label` (required), `kind`, `detail`, `parent`, `id`, `status`.

### Kinds

| kind | Meaning |
| --- | --- |
| `goal` | What the user actually wants |
| `context` | Constraints, established facts, external rules |
| `decision` | A choice already made |
| `action` | Work in progress or planned |
| `artifact` | A file, repository, document or diagram |
| `risk` | Something that can break the plan |
| `question` | Still open |
| `note` | Worth keeping, none of the above |

## CLI

```sh
node bin/cli.js examples/spec.json --out docs        # write the three files
node bin/cli.js examples/spec.json --json            # geometry only (debugging)
node bin/cli.js examples/spec.json --stdout > m.svg  # SVG on stdout
echo '{"title":"t","nodes":[{"label":"root"}]}' | node bin/cli.js -   # spec from stdin
```

## Installation & troubleshooting

**Desktop.** The desktop profile is managed by the Electron application; `dsh plugin --profile desktop add` is refused. Install from the in-app plugin market. Before it is listed there, add this row to `~/.dsh/profiles/desktop/cordis.patch.yml` and make sure the package is present in that profile's `node_modules`:

```yaml
- id: context-map
  name: dsh-context-map
```

**`pnpm: command not found`.** The installer shells out to pnpm and needs it on the DSH process's PATH, not just in one terminal. `npm install -g pnpm`, verify with `pnpm --version`, then restart DSH.

**Installed but `context_map` is missing.** Check `dsh --profile web --dump-config | grep -A1 context-map`; if the row exists, confirm `@deepseek-ai/dsh-tools` loaded (the plugin `inject`s `tools`), then start a new session — the tool list is fixed at session start.

**Update / remove.**

```sh
dsh plugin --profile web add dsh-context-map@latest
dsh plugin --profile web remove dsh-context-map
```

## Privacy

| Item | Status |
| --- | --- |
| Network | **None** — no `fetch`, socket or telemetry |
| Session logs | **Not read** — only the nodes passed into this call |
| Session facts | Process memory only, dropped when the plugin unloads; never written to disk or sent anywhere |
| Writes | Three files under `outDir`: `<slug>.svg`, `<slug>.html`, `<slug>.md` |
| Process / code execution | No spawn, no `eval`, no remote code, no install hooks |
| Secrets | None required |

## Compatibility

- DSH `>=0.2.0-rc.1 <0.3.0-0`; verified on **0.2.1-rc.2** (a scratch profile composes the `context-map` row via `--dump-config`).
- Node `>=20`.
- macOS, Linux, Windows.

## How it differs

Every diagramming tool takes a different input:

| Tool type | Input | Output | Good at |
| :--- | :--- | :--- | :--- |
| Mind map (XMind, Workflowy) | An outline you type | Radial tree | Brainstorming; weak at hierarchy and cross-branch dependencies |
| Repo-to-architecture tools | A code repository | System / module structure | Understanding a codebase; they do not record *why* |
| Diagram DSLs (Mermaid, draw.io) | A DSL or manual placement | A diagram | Precise control; you own layout and colour |
| **dsh-context-map** | **A conversation** | **Layered IA map, three formats** | **Retros, handovers, PR write-ups; no code reading, no model call** |

It does not replace those tools. It fills the part they cannot see: **decisions, risks and open questions exist only in the conversation.**

## Updates

### 0.1.0 — 2026-11-02

First release: the `context_map` tool, deterministic tree layout, SVG / interactive HTML / Mermaid exports, a CLI, and 55 tests. See [CHANGELOG](CHANGELOG.md).

## Limits

- One map is one tree; several roots get a synthetic parent.
- 300 nodes / 150 cross links per map. Bigger maps get tall — group first (3–5 branches, 2–6 children each), which is what the tool description tells the model.
- It does not decide content, only renders it faithfully.
- Not a design tool: no dragging, no manual placement, no PNG/PDF export (SVG plus browser print covers it).

## FAQ

**Why doesn't the plugin read the chat log itself?**
DSH session files are `session.v4.jsonl.zstd` — multi-frame zstd, an internal format that breaks across versions. The model already holds the conversation, so distilling there is both simpler and more accurate. Keeping the plugin to rendering is what buys the "no network, no secrets, reproducible" guarantees.

**The map is too tall.**
Group the nodes. A dozen siblings under one parent produce a long vertical column; 3–5 branches with 2–6 children each read best. Once generated, the HTML export can zoom, search and filter.

**Can I change the palette?**
Edit `KIND_STYLES` in [`lib/theme.js`](lib/theme.js) — one accent and one tint per kind, applied to both themes.

**Will the map differ every time?**
Coordinates are fully deterministic and the timestamp is day-precise, so a same-day rebuild is byte-identical.

**Can it diagram a codebase?**
No, and it is not intended to — that belongs to the repo-to-architecture tools. This plugin maps what the conversation contains that the code does not.

## Development

```sh
npm test          # 55 cases across 6 suites
npm run demo      # render examples/spec.json into examples/out
npm run check     # node --check on the entry points
```

`scripts/test.mjs` imports every suite into one process: `node --test` forks a child per file, which confined runners refuse with `EPERM`.

[`test/package.test.js`](test/package.test.js) turns the directory requirements into assertions: slug-safe name, a real `dsh.bundle.patch`, SPDX licence, install command in the README, zero dependencies.

## Links

[Issues](https://github.com/gavinff1990/dsh-context-map/issues) · [Releases](https://github.com/gavinff1990/dsh-context-map/releases) · [CHANGELOG](CHANGELOG.md) · [CONTRIBUTING](CONTRIBUTING.md) · [SECURITY](SECURITY.md)

## License

[MIT](LICENSE)
