@heart-of-gold/toolkit 0.1.34 → 0.1.35
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +3 -3
- package/package.json +1 -1
- package/plugins/babel-fish/.claude-plugin/plugin.json +1 -1
- package/plugins/babel-fish/skills/visualize/SKILL.md +257 -181
- package/plugins/babel-fish/skills/visualize/scripts/smart-render.js +411 -83
- package/plugins/deep-thought/.claude-plugin/plugin.json +1 -1
- package/plugins/marvin/.claude-plugin/plugin.json +1 -1
|
@@ -15,19 +15,19 @@
|
|
|
15
15
|
"name": "deep-thought",
|
|
16
16
|
"source": "./plugins/deep-thought",
|
|
17
17
|
"description": "The Answer Computer — reasoning tools for brainstorming, planning, and deep thinking",
|
|
18
|
-
"version": "0.2.
|
|
18
|
+
"version": "0.2.5"
|
|
19
19
|
},
|
|
20
20
|
{
|
|
21
21
|
"name": "marvin",
|
|
22
22
|
"source": "./plugins/marvin",
|
|
23
23
|
"description": "The Paranoid Android — quality tools for code review, knowledge compounding, and work execution",
|
|
24
|
-
"version": "0.3.
|
|
24
|
+
"version": "0.3.5"
|
|
25
25
|
},
|
|
26
26
|
{
|
|
27
27
|
"name": "babel-fish",
|
|
28
28
|
"source": "./plugins/babel-fish",
|
|
29
29
|
"description": "Universal Translator — media generation tools for audio, image, and video content",
|
|
30
|
-
"version": "0.2.
|
|
30
|
+
"version": "0.2.3"
|
|
31
31
|
},
|
|
32
32
|
{
|
|
33
33
|
"name": "quellis",
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: visualize
|
|
3
3
|
description: >
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
4
|
+
Create shareable HTML visual artifacts from markdown, plans, architecture docs, brainstorms, and other structured
|
|
5
|
+
content. Prefer browser-viewable HTML first when it will materially improve clarity or sharing; otherwise fall back
|
|
6
|
+
to terminal rendering. Triggers: visualize, mindmap, mind map, show me the structure, draw a map, make this clear,
|
|
7
|
+
make this visual.
|
|
8
8
|
allowed-tools:
|
|
9
9
|
- Read
|
|
10
10
|
- Bash
|
|
@@ -14,24 +14,193 @@ allowed-tools:
|
|
|
14
14
|
|
|
15
15
|
# Visualize — Babel Fish
|
|
16
16
|
|
|
17
|
-
Translating structured text into spatial understanding.
|
|
17
|
+
Translating structured text into spatial understanding. The job is not to "turn markdown into HTML." The job is to create a visual artifact that helps a human understand the material faster.
|
|
18
|
+
|
|
19
|
+
## Mission
|
|
20
|
+
|
|
21
|
+
Create one clear, polished, shareable visual artifact that:
|
|
22
|
+
- matches the user's actual need
|
|
23
|
+
- feels intentionally designed, not markdown restyled in boxes
|
|
24
|
+
- summarizes before detailing
|
|
25
|
+
- uses browser HTML as the primary medium for substantial artifacts
|
|
26
|
+
- falls back to terminal rendering only when that is the better fit
|
|
18
27
|
|
|
19
28
|
## Boundaries
|
|
20
29
|
|
|
21
30
|
- MAY: read files, generate terminal mind maps, generate temporary HTML artifacts, run renderer/share scripts via bash
|
|
22
31
|
- MAY NOT: modify project files, create persistent files outside temp/output artifacts, install unrelated packages
|
|
23
32
|
|
|
24
|
-
##
|
|
33
|
+
## Core Principle
|
|
34
|
+
|
|
35
|
+
Do **not** mirror the source document structure one-to-one unless the user explicitly wants a document view.
|
|
36
|
+
|
|
37
|
+
Instead:
|
|
38
|
+
1. understand the source
|
|
39
|
+
2. decide what the artifact is trying to communicate
|
|
40
|
+
3. choose the visual form that best serves that goal
|
|
41
|
+
4. compress and reshape the content into a stronger visual story
|
|
42
|
+
5. keep raw/source detail secondary or collapsible when possible
|
|
43
|
+
|
|
44
|
+
HTML should feel like:
|
|
45
|
+
- a dashboard
|
|
46
|
+
- an explainer
|
|
47
|
+
- a roadmap
|
|
48
|
+
- an architecture brief
|
|
49
|
+
- a deliberate mind map
|
|
50
|
+
|
|
51
|
+
Not like:
|
|
52
|
+
- a markdown page with nicer CSS
|
|
53
|
+
|
|
54
|
+
## Artifact Families
|
|
55
|
+
|
|
56
|
+
These are guidance categories for the coding agent. They are not rigid parser outputs.
|
|
57
|
+
|
|
58
|
+
### `outline`
|
|
59
|
+
Use when the user wants:
|
|
60
|
+
- document structure
|
|
61
|
+
- a reading aid
|
|
62
|
+
- a faithful but polished source-oriented view
|
|
63
|
+
- a safe fallback when a richer artifact is not justified
|
|
64
|
+
|
|
65
|
+
### `roadmap`
|
|
66
|
+
Use when the source is best understood as:
|
|
67
|
+
- phases
|
|
68
|
+
- priorities
|
|
69
|
+
- sequencing
|
|
70
|
+
- workstreams
|
|
71
|
+
- execution flow
|
|
72
|
+
|
|
73
|
+
### `architecture`
|
|
74
|
+
Use when the source is best understood as:
|
|
75
|
+
- components
|
|
76
|
+
- boundaries
|
|
77
|
+
- integrations
|
|
78
|
+
- decisions
|
|
79
|
+
- responsibilities
|
|
80
|
+
|
|
81
|
+
### `mindmap`
|
|
82
|
+
Use only when the content is genuinely:
|
|
83
|
+
- concise
|
|
84
|
+
- branchy
|
|
85
|
+
- idea-oriented
|
|
86
|
+
- better understood spatially than sequentially
|
|
87
|
+
|
|
88
|
+
### `explainer`
|
|
89
|
+
Use when the artifact should help another human quickly understand:
|
|
90
|
+
- the recommendation
|
|
91
|
+
- tradeoffs
|
|
92
|
+
- key decisions
|
|
93
|
+
- what matters and why
|
|
94
|
+
|
|
95
|
+
### `mockup`
|
|
96
|
+
Use when the user really wants:
|
|
97
|
+
- product/UI concept visualization
|
|
98
|
+
- believable interface framing
|
|
99
|
+
- layout and interaction-oriented representation
|
|
100
|
+
|
|
101
|
+
## Style Foundations
|
|
102
|
+
|
|
103
|
+
Apply these defaults unless the user asks for something else:
|
|
104
|
+
|
|
105
|
+
- calm, high-contrast visual language
|
|
106
|
+
- restrained accent usage
|
|
107
|
+
- strong hierarchy and generous spacing
|
|
108
|
+
- summary-first information architecture
|
|
109
|
+
- progressive disclosure for dense detail
|
|
110
|
+
- cards, panels, lanes, chips, and callouts over markdown-heavy paragraphs
|
|
111
|
+
- readable max-widths for prose
|
|
112
|
+
- sticky navigation only when it helps, never as the dominant element
|
|
113
|
+
- polished but restrained effects; no gimmicky AI-demo chrome
|
|
114
|
+
|
|
115
|
+
See also:
|
|
116
|
+
- `docs/architecture/visualize-design-rules.md`
|
|
117
|
+
|
|
118
|
+
## Rules: Do
|
|
119
|
+
|
|
120
|
+
- Decide the communication goal before choosing the renderer.
|
|
121
|
+
- Prefer shareable HTML for substantial workflow artifacts when `share-html` is configured.
|
|
122
|
+
- Transform the source into a view model in your head before rendering: summary, priorities, risks, dependencies, decisions, outcomes.
|
|
123
|
+
- Lead with a strong first screen: title, one-line mission, key takeaways, and obvious next scan targets.
|
|
124
|
+
- Convert dense content into visual units where appropriate: cards, grouped sections, lanes, side panels, chips, callouts, expandable details.
|
|
125
|
+
- Use `roadmap` or richer execution-oriented views for plans when that improves understanding.
|
|
126
|
+
- Use `architecture` views for system/design-heavy documents.
|
|
127
|
+
- Keep raw source detail available, but secondary.
|
|
128
|
+
- Briefly explain why you chose the visualization mode when sharing the result.
|
|
129
|
+
|
|
130
|
+
## Rules: Don't
|
|
131
|
+
|
|
132
|
+
- Do not treat HTML generation as a markdown restyling task.
|
|
133
|
+
- Do not dump long raw paragraphs into large cards as the main UI.
|
|
134
|
+
- Do not let the table of contents dominate the page.
|
|
135
|
+
- Do not force a mind map onto content that is not naturally branch-shaped.
|
|
136
|
+
- Do not use flashy gradients, glass, shadows, or color noise unless they clearly improve hierarchy.
|
|
137
|
+
- Do not silently guess when the visualization choice is materially ambiguous.
|
|
138
|
+
- Do not create multiple competing artifacts unless the user explicitly asks for comparison.
|
|
139
|
+
|
|
140
|
+
## Expected Behavior
|
|
141
|
+
|
|
142
|
+
When invoked, behave like a visual editor, not a format converter.
|
|
143
|
+
|
|
144
|
+
1. Read the source or infer the source from context.
|
|
145
|
+
2. Decide whether the user needs:
|
|
146
|
+
- structure comprehension
|
|
147
|
+
- execution clarity
|
|
148
|
+
- system understanding
|
|
149
|
+
- stakeholder explanation
|
|
150
|
+
- UI/product visualization
|
|
151
|
+
3. Choose the best artifact family.
|
|
152
|
+
4. If uncertain, ask one concise question.
|
|
153
|
+
5. Generate one HTML artifact first when browser rendering will help.
|
|
154
|
+
6. Fall back to terminal rendering when browser/share is unavailable or explicitly not wanted.
|
|
155
|
+
|
|
156
|
+
If the user says "you decide," choose the clearest non-gimmicky artifact, not the fanciest one.
|
|
157
|
+
|
|
158
|
+
## Uncertainty Protocol
|
|
159
|
+
|
|
160
|
+
When the best visualization is not clear, do **not** silently guess if the choice would materially affect usefulness.
|
|
161
|
+
|
|
162
|
+
Ask **one concise question at a time**:
|
|
163
|
+
- state the decision in plain language
|
|
164
|
+
- offer 2-4 explicit options
|
|
165
|
+
- include a recommended option when you have one
|
|
166
|
+
- keep option labels outcome-focused, not renderer-jargon-first
|
|
167
|
+
|
|
168
|
+
Good pattern:
|
|
169
|
+
- "Which would help most here?"
|
|
170
|
+
- `Roadmap` — show phases, sequencing, and implementation progress
|
|
171
|
+
- `Outline` — show the document structure clearly
|
|
172
|
+
- `Mind map` — show branching ideas and relationships
|
|
173
|
+
- `Architecture view` — show components, boundaries, and decisions
|
|
174
|
+
|
|
175
|
+
If the harness supports structured choices, use them.
|
|
176
|
+
If not, use a short plain-text question such as:
|
|
177
|
+
|
|
178
|
+
```text
|
|
179
|
+
I can visualize this a few different ways. Which would be most useful?
|
|
180
|
+
1. Roadmap — phases and tasks
|
|
181
|
+
2. Outline — document structure
|
|
182
|
+
3. Mind map — branching ideas
|
|
183
|
+
4. Architecture view — components and boundaries
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
If the user does not care or says "you decide," choose the safest useful mode:
|
|
187
|
+
- default to `outline`
|
|
188
|
+
- use `roadmap` for clearly execution-heavy plans
|
|
189
|
+
- use `architecture` for clearly system-design-heavy docs
|
|
190
|
+
- use `mindmap` only when the artifact is genuinely concise and branchy
|
|
191
|
+
|
|
192
|
+
## Renderers
|
|
25
193
|
|
|
26
|
-
Visualization
|
|
27
|
-
- `scripts/smart-render.js` — renders one HTML artifact using the mode the coding agent chose
|
|
194
|
+
Visualization has two implementation layers:
|
|
195
|
+
- `scripts/smart-render.js` — renders one HTML artifact using the mode the coding agent chose, with a safe fallback
|
|
28
196
|
- `scripts/render-mindmap/index.js` — specialized mind-map renderer for branchy content
|
|
29
197
|
|
|
30
198
|
**Locations:**
|
|
31
199
|
- `scripts/smart-render.js`
|
|
32
200
|
- `scripts/render-mindmap/index.js`
|
|
33
201
|
|
|
34
|
-
|
|
202
|
+
To find the smart renderer path:
|
|
203
|
+
|
|
35
204
|
```bash
|
|
36
205
|
# Option 1: Use CLAUDE_PLUGIN_ROOT if available
|
|
37
206
|
SCRIPT="${CLAUDE_PLUGIN_ROOT}/skills/visualize/scripts/smart-render.js"
|
|
@@ -40,7 +209,8 @@ SCRIPT="${CLAUDE_PLUGIN_ROOT}/skills/visualize/scripts/smart-render.js"
|
|
|
40
209
|
SCRIPT=$(find ~/.claude/plugins -path "*/babel-fish/skills/visualize/scripts/smart-render.js" 2>/dev/null | head -1)
|
|
41
210
|
```
|
|
42
211
|
|
|
43
|
-
|
|
212
|
+
First run for the mind-map renderer:
|
|
213
|
+
|
|
44
214
|
```bash
|
|
45
215
|
RENDER_DIR=$(dirname "$SCRIPT")/render-mindmap
|
|
46
216
|
if [ ! -d "$RENDER_DIR/node_modules" ]; then
|
|
@@ -64,9 +234,9 @@ node "$SCRIPT" path/to/file.md --mode mindmap --out /tmp/view.html
|
|
|
64
234
|
node "$(dirname "$SCRIPT")/render-mindmap/index.js" --html /tmp/map.html path/to/file.md
|
|
65
235
|
```
|
|
66
236
|
|
|
67
|
-
|
|
237
|
+
## HTML Share Flow
|
|
68
238
|
|
|
69
|
-
Use the helper script when the user wants a browser URL and the share server is already configured
|
|
239
|
+
Use the helper script when the user wants a browser URL and the share server is already configured:
|
|
70
240
|
|
|
71
241
|
```bash
|
|
72
242
|
bash scripts/render-and-share.sh path/to/file.md
|
|
@@ -79,201 +249,107 @@ This script:
|
|
|
79
249
|
4. publishes the artifact to the configured local share server
|
|
80
250
|
5. prints the publish result so you can return the URL
|
|
81
251
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
- **Auto-depth:** If no `--depth` is specified, the renderer tries depths 3, 2, 1 and picks the deepest that fits the terminal width.
|
|
85
|
-
- **Pruning:** Long labels are truncated with `…`. Nodes with many children show the first few plus `+N more`.
|
|
86
|
-
- **Colors** (via ANSI, visible in Claude Code bash output):
|
|
87
|
-
- Root: bold white on blue
|
|
88
|
-
- Depth 1: bold cyan
|
|
89
|
-
- Depth 2: green
|
|
90
|
-
- Depth 3: yellow
|
|
91
|
-
- Depth 4+: dim
|
|
252
|
+
### Recommended share flow
|
|
92
253
|
|
|
93
|
-
|
|
254
|
+
1. Verify or assume the input markdown is ready.
|
|
255
|
+
2. Choose the mode from context.
|
|
256
|
+
3. Run:
|
|
257
|
+
```bash
|
|
258
|
+
bash scripts/render-and-share.sh --mode <chosen-mode> --url-only [file]
|
|
259
|
+
```
|
|
260
|
+
4. Read the returned URL from stdout.
|
|
261
|
+
5. Return that URL to the user as the primary result.
|
|
262
|
+
6. Briefly explain what was published and why this mode was chosen.
|
|
94
263
|
|
|
95
|
-
|
|
264
|
+
If publishing fails because the share server is not configured, say so clearly and fall back to terminal rendering unless the user wants to stop and run `share-server-setup` first.
|
|
96
265
|
|
|
97
|
-
|
|
98
|
-
- the source is a brainstorm, plan, architecture doc, or other structured workflow artifact
|
|
99
|
-
- the user asks to open it in a browser
|
|
100
|
-
- the user wants to share the result with another person or device
|
|
101
|
-
- the structure is large enough that browser navigation is more useful than terminal rendering
|
|
102
|
-
- `share-html` is configured
|
|
266
|
+
## Terminal Rendering
|
|
103
267
|
|
|
104
|
-
|
|
268
|
+
Use terminal rendering when:
|
|
105
269
|
- share-html is not configured
|
|
106
|
-
- the user explicitly wants
|
|
107
|
-
-
|
|
108
|
-
|
|
109
|
-
When invoked as `/visualize [path]`:
|
|
110
|
-
|
|
111
|
-
**If a file path is provided:**
|
|
112
|
-
1. Read the file
|
|
113
|
-
2. Decide what kind of visual artifact would help most from context
|
|
114
|
-
3. If confidence is high enough, choose the mode and generate/share HTML first
|
|
115
|
-
4. If confidence is not high enough, ask the user which direction would help most
|
|
116
|
-
5. If sharing is unavailable or the user explicitly wants terminal output, fall back appropriately
|
|
270
|
+
- the user explicitly wants terminal-only output
|
|
271
|
+
- a quick local structural check is more useful than a browser view
|
|
117
272
|
|
|
118
|
-
**
|
|
119
|
-
1. Check if there's a recent brainstorm or plan document in the conversation context
|
|
120
|
-
2. If yes: use that document's path
|
|
121
|
-
3. If no: summarize the current conversation topic into a markdown structure with headings, write it to a temp file, then render
|
|
122
|
-
|
|
123
|
-
**If the user says "visualize this" or "show me a mind map":**
|
|
124
|
-
1. Look at what was just discussed or created
|
|
125
|
-
2. Generate an appropriate markdown structure
|
|
126
|
-
3. Render it
|
|
127
|
-
|
|
128
|
-
## Phase 1 — Render or Share
|
|
129
|
-
|
|
130
|
-
### Path A — Terminal rendering
|
|
131
|
-
|
|
132
|
-
**IMPORTANT: Output the mind map in the assistant response text, NOT as raw bash tool output.**
|
|
273
|
+
**IMPORTANT:** Output the mind map in the assistant response text, not as raw bash tool output.
|
|
133
274
|
|
|
134
275
|
Many harness bash panels truncate long output and wrap wide content, breaking alignment. Instead:
|
|
135
276
|
|
|
136
|
-
1. Locate the renderer script
|
|
137
|
-
2. Ensure dependencies are installed
|
|
277
|
+
1. Locate the renderer script.
|
|
278
|
+
2. Ensure dependencies are installed.
|
|
138
279
|
3. Run the renderer with `--no-color`, redirect to a temp file:
|
|
139
280
|
```bash
|
|
140
281
|
node "$SCRIPT" --no-color [file] > /tmp/mindmap-result.txt 2>&1
|
|
141
282
|
```
|
|
142
|
-
4. Read `/tmp/mindmap-result.txt
|
|
143
|
-
5. Output the contents inside a
|
|
144
|
-
6. Clean up:
|
|
145
|
-
|
|
146
|
-
The default mode is **vertical layout** — boxes on main branches, compact leaves, ~40 chars wide.
|
|
147
|
-
|
|
148
|
-
### Path B — Shareable HTML
|
|
149
|
-
|
|
150
|
-
For substantial artifacts, prefer this path first when `share-html` is configured.
|
|
151
|
-
|
|
152
|
-
The coding agent should choose the mode from context. Toolkit guidance:
|
|
153
|
-
- plans often fit `roadmap` or `outline`
|
|
154
|
-
- architecture docs often fit `architecture` or `outline`
|
|
155
|
-
- concise branchy brainstorms may fit `mindmap`
|
|
156
|
-
- product/UI concepts may fit `mockup`
|
|
157
|
-
- stakeholder-friendly summaries may fit `explainer`
|
|
158
|
-
|
|
159
|
-
If uncertain, ask the user using the harness's structured choice UI when available; otherwise present concise plain-text options.
|
|
160
|
-
|
|
161
|
-
### Uncertainty protocol
|
|
162
|
-
|
|
163
|
-
When the best visualization is not clear, do **not** silently guess if the choice would materially affect usefulness.
|
|
164
|
-
|
|
165
|
-
Ask **one concise question at a time**:
|
|
166
|
-
- state the decision in plain language
|
|
167
|
-
- offer 2-4 explicit options
|
|
168
|
-
- include a recommended option when you have one
|
|
169
|
-
- keep option labels outcome-focused, not renderer-jargon-first
|
|
170
|
-
|
|
171
|
-
Good pattern:
|
|
172
|
-
- "Which would help most here?"
|
|
173
|
-
- `Roadmap` — show phases, sequencing, and implementation progress
|
|
174
|
-
- `Outline` — show the document structure clearly
|
|
175
|
-
- `Mind map` — show branching ideas and relationships
|
|
176
|
-
- `Architecture view` — show components, boundaries, and decisions
|
|
177
|
-
|
|
178
|
-
If the harness supports structured choices, use them.
|
|
179
|
-
If not, use a short plain-text question such as:
|
|
180
|
-
|
|
181
|
-
```text
|
|
182
|
-
I can visualize this a few different ways. Which would be most useful?
|
|
183
|
-
1. Roadmap — phases and tasks
|
|
184
|
-
2. Outline — document structure
|
|
185
|
-
3. Mind map — branching ideas
|
|
186
|
-
4. Architecture view — components and boundaries
|
|
187
|
-
```
|
|
188
|
-
|
|
189
|
-
If the user does not care or says "you decide," choose the safest useful mode:
|
|
190
|
-
- default to `outline`
|
|
191
|
-
- use `roadmap` for clearly execution-heavy plans
|
|
192
|
-
- use `architecture` for clearly system-design-heavy docs
|
|
193
|
-
- use `mindmap` only when the artifact is genuinely concise and branchy
|
|
194
|
-
|
|
195
|
-
1. Verify or assume the input markdown is ready
|
|
196
|
-
2. Run:
|
|
283
|
+
4. Read `/tmp/mindmap-result.txt`.
|
|
284
|
+
5. Output the contents inside a fenced code block.
|
|
285
|
+
6. Clean up:
|
|
197
286
|
```bash
|
|
198
|
-
|
|
287
|
+
rm -f /tmp/mindmap-result.txt
|
|
199
288
|
```
|
|
200
|
-
3. Read the returned URL from stdout
|
|
201
|
-
4. Return that URL to the user as the primary result
|
|
202
|
-
5. Briefly explain what was published and why this mode was chosen
|
|
203
289
|
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
-
|
|
213
|
-
-
|
|
214
|
-
|
|
215
|
-
-
|
|
216
|
-
-
|
|
217
|
-
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
-
|
|
223
|
-
-
|
|
224
|
-
-
|
|
225
|
-
-
|
|
226
|
-
-
|
|
290
|
+
The default mode is vertical layout — boxes on main branches, compact leaves, about 40 chars wide.
|
|
291
|
+
|
|
292
|
+
## Required Output Structure
|
|
293
|
+
|
|
294
|
+
For substantial HTML artifacts, prefer this structure:
|
|
295
|
+
- strong title + one-line framing
|
|
296
|
+
- summary layer first
|
|
297
|
+
- main visual body second
|
|
298
|
+
- dense details compressed or collapsible
|
|
299
|
+
- source-faithful appendix only if needed
|
|
300
|
+
|
|
301
|
+
### Plan-oriented artifact target shape
|
|
302
|
+
- title + mission
|
|
303
|
+
- key stats or scope summary
|
|
304
|
+
- priorities / phases / workstreams
|
|
305
|
+
- dependencies / risks / acceptance gates
|
|
306
|
+
- expandable detail or appendix
|
|
307
|
+
|
|
308
|
+
### Architecture-oriented artifact target shape
|
|
309
|
+
- title + system framing
|
|
310
|
+
- major components / boundaries / integrations
|
|
311
|
+
- key decisions and tradeoffs
|
|
312
|
+
- risks / assumptions
|
|
313
|
+
- supporting detail below
|
|
314
|
+
|
|
315
|
+
### Explainer target shape
|
|
316
|
+
- title + recommendation
|
|
317
|
+
- why this matters
|
|
318
|
+
- options / comparison / decision
|
|
319
|
+
- what happens next
|
|
320
|
+
- supporting source detail below
|
|
321
|
+
|
|
322
|
+
## Quality Gates
|
|
323
|
+
|
|
324
|
+
Before returning a shared HTML result, check mentally:
|
|
325
|
+
- Does the first screen explain the artifact in under 10 seconds?
|
|
326
|
+
- Does this feel designed, not like markdown with nicer CSS?
|
|
327
|
+
- Is hierarchy obvious?
|
|
328
|
+
- Is summary ahead of detail?
|
|
329
|
+
- Are dense sections compressed into meaningful visual units?
|
|
330
|
+
- Is the chosen mode actually appropriate for the content?
|
|
331
|
+
- If this is a plan, does it foreground execution rather than document order?
|
|
332
|
+
- If this is a brainstorm, is it actually branch-shaped enough for a mind map?
|
|
333
|
+
|
|
334
|
+
If the answer to several of these is no, reconsider the mode or ask the user.
|
|
227
335
|
|
|
228
336
|
## Input Formats
|
|
229
337
|
|
|
230
338
|
### Markdown (primary)
|
|
231
|
-
Standard markdown with `#` headings defining hierarchy
|
|
232
|
-
```markdown
|
|
233
|
-
# Root Topic
|
|
234
|
-
## Branch A
|
|
235
|
-
- Detail 1
|
|
236
|
-
- Detail 2
|
|
237
|
-
## Branch B
|
|
238
|
-
### Sub-branch
|
|
239
|
-
- Detail 3
|
|
240
|
-
```
|
|
339
|
+
Standard markdown with `#` headings defining hierarchy.
|
|
241
340
|
|
|
242
341
|
### JSON
|
|
243
|
-
Tree structure with `label` and `children
|
|
244
|
-
```json
|
|
245
|
-
{
|
|
246
|
-
"label": "Root",
|
|
247
|
-
"children": [
|
|
248
|
-
{ "label": "Branch A", "children": [] },
|
|
249
|
-
{ "label": "Branch B", "children": [
|
|
250
|
-
{ "label": "Sub-branch", "children": [] }
|
|
251
|
-
]}
|
|
252
|
-
]
|
|
253
|
-
}
|
|
254
|
-
```
|
|
255
|
-
|
|
256
|
-
## Generating Structure from Context
|
|
257
|
-
|
|
258
|
-
When visualizing conversation context (no file path), create a markdown structure that captures:
|
|
259
|
-
|
|
260
|
-
**For brainstorm content:**
|
|
261
|
-
- Root = topic
|
|
262
|
-
- Branches = key decisions or themes
|
|
263
|
-
- Leaves = specific choices or details
|
|
342
|
+
Tree structure with `label` and `children`.
|
|
264
343
|
|
|
265
|
-
|
|
266
|
-
- Root = project name
|
|
267
|
-
- Branches = phases
|
|
268
|
-
- Leaves = tasks within each phase
|
|
344
|
+
## Generating Structure From Context
|
|
269
345
|
|
|
270
|
-
|
|
271
|
-
-
|
|
272
|
-
-
|
|
273
|
-
-
|
|
346
|
+
When visualizing conversation context with no file path:
|
|
347
|
+
- brainstorms: root = topic, branches = key themes, leaves = concrete ideas
|
|
348
|
+
- plans: root = project, branches = priorities/phases, leaves = tasks
|
|
349
|
+
- general discussion: root = main topic, branches = subtopics, leaves = key takeaways
|
|
274
350
|
|
|
275
351
|
Write the generated markdown to `/tmp/mindmap-XXXXXX.md`, render it, then clean up.
|
|
276
352
|
|
|
277
353
|
## What Makes This Babel Fish
|
|
278
354
|
|
|
279
|
-
The Babel Fish translates between languages. This skill translates between
|
|
355
|
+
The Babel Fish translates between languages. This skill translates between modalities — from linear text to spatial understanding, and from private working notes to clear shareable browser artifacts.
|
|
@@ -44,22 +44,23 @@ function parseDocument(markdown) {
|
|
|
44
44
|
return { frontmatter, sections, raw: markdown };
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
-
function collectChecklists(lines) {
|
|
48
|
-
return lines
|
|
49
|
-
.map((line) => line.match(/^\s*- \[( |x)\]\s+(.+)$/i))
|
|
50
|
-
.filter(Boolean)
|
|
51
|
-
.map((m) => ({ done: m[1].toLowerCase() === 'x', text: m[2].trim() }));
|
|
52
|
-
}
|
|
53
|
-
|
|
54
47
|
function textFromLines(lines) {
|
|
55
48
|
return lines.join('\n').trim();
|
|
56
49
|
}
|
|
57
50
|
|
|
51
|
+
function normalizeWhitespace(text) {
|
|
52
|
+
return text.replace(/\s+/g, ' ').trim();
|
|
53
|
+
}
|
|
54
|
+
|
|
58
55
|
function firstParagraph(lines) {
|
|
59
56
|
const blocks = textFromLines(lines).split(/\n\s*\n/).map((b) => b.trim()).filter(Boolean);
|
|
60
57
|
return blocks[0] || '';
|
|
61
58
|
}
|
|
62
59
|
|
|
60
|
+
function splitBlocks(lines) {
|
|
61
|
+
return textFromLines(lines).split(/\n\s*\n/).map((b) => b.trim()).filter(Boolean);
|
|
62
|
+
}
|
|
63
|
+
|
|
63
64
|
function classifyMode(_filePath, _doc, preferredMode) {
|
|
64
65
|
if (preferredMode && preferredMode !== 'auto') return preferredMode;
|
|
65
66
|
return 'outline';
|
|
@@ -74,7 +75,11 @@ function escapeHtml(value) {
|
|
|
74
75
|
.replace(/'/g, ''');
|
|
75
76
|
}
|
|
76
77
|
|
|
77
|
-
function
|
|
78
|
+
function slugify(value) {
|
|
79
|
+
return String(value).toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '');
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function renderShell({ title, eyebrow, summary, nav, content, badge = '' }) {
|
|
78
83
|
return `<!doctype html>
|
|
79
84
|
<html lang="en">
|
|
80
85
|
<head>
|
|
@@ -83,56 +88,169 @@ function renderShell({ title, eyebrow, summary, toc, content, badge = '' }) {
|
|
|
83
88
|
<title>${escapeHtml(title)}</title>
|
|
84
89
|
<style>
|
|
85
90
|
:root {
|
|
86
|
-
--bg: #
|
|
91
|
+
--bg: #09111f;
|
|
92
|
+
--bg-2: #0f1a31;
|
|
87
93
|
--panel: rgba(255,255,255,0.06);
|
|
88
94
|
--panel-strong: rgba(255,255,255,0.1);
|
|
95
|
+
--panel-soft: rgba(255,255,255,0.04);
|
|
89
96
|
--text: #eef2ff;
|
|
90
97
|
--muted: #b8c0d9;
|
|
91
|
-
--
|
|
98
|
+
--muted-2: #93a0bf;
|
|
99
|
+
--border: rgba(255,255,255,0.11);
|
|
92
100
|
--accent: #7c9cff;
|
|
93
101
|
--accent-2: #80e0d0;
|
|
94
102
|
--success: #86efac;
|
|
95
103
|
--warn: #fbbf24;
|
|
96
104
|
--danger: #fca5a5;
|
|
97
|
-
--shadow: 0
|
|
105
|
+
--shadow: 0 24px 80px rgba(0,0,0,0.34);
|
|
106
|
+
--radius-xl: 28px;
|
|
107
|
+
--radius-lg: 22px;
|
|
108
|
+
--radius-md: 16px;
|
|
98
109
|
}
|
|
99
110
|
* { box-sizing: border-box; }
|
|
100
|
-
html, body { margin: 0; padding: 0;
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
111
|
+
html, body { margin: 0; padding: 0; }
|
|
112
|
+
html { color-scheme: dark; }
|
|
113
|
+
body {
|
|
114
|
+
background:
|
|
115
|
+
radial-gradient(circle at top left, rgba(124,156,255,0.18), transparent 32%),
|
|
116
|
+
radial-gradient(circle at top right, rgba(128,224,208,0.12), transparent 24%),
|
|
117
|
+
linear-gradient(180deg, var(--bg-2) 0%, var(--bg) 100%);
|
|
118
|
+
color: var(--text);
|
|
119
|
+
font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
|
120
|
+
line-height: 1.55;
|
|
121
|
+
}
|
|
122
|
+
main { max-width: 1480px; margin: 0 auto; padding: 36px 22px 84px; }
|
|
123
|
+
.hero {
|
|
124
|
+
display: grid;
|
|
125
|
+
gap: 18px;
|
|
126
|
+
padding: 28px;
|
|
127
|
+
border-radius: 32px;
|
|
128
|
+
background: linear-gradient(180deg, rgba(255,255,255,0.08), rgba(255,255,255,0.04));
|
|
129
|
+
border: 1px solid rgba(255,255,255,0.14);
|
|
130
|
+
box-shadow: var(--shadow);
|
|
131
|
+
margin-bottom: 24px;
|
|
132
|
+
}
|
|
133
|
+
.eyebrow { color: var(--accent-2); text-transform: uppercase; letter-spacing: 0.16em; font-size: 12px; font-weight: 700; }
|
|
134
|
+
.hero h1 { margin: 0; font-size: clamp(34px, 4vw, 62px); line-height: 1.02; text-wrap: balance; }
|
|
106
135
|
.hero p { margin: 0; max-width: 78ch; color: var(--muted); font-size: 17px; }
|
|
107
|
-
.
|
|
108
|
-
.
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
.
|
|
114
|
-
|
|
115
|
-
|
|
136
|
+
.hero-row { display: flex; flex-wrap: wrap; gap: 12px; align-items: center; }
|
|
137
|
+
.badge, .pill {
|
|
138
|
+
display: inline-flex; align-items: center; gap: 8px; width: fit-content;
|
|
139
|
+
padding: 8px 12px; border-radius: 999px; border: 1px solid rgba(255,255,255,0.14);
|
|
140
|
+
background: rgba(255,255,255,0.06); color: #dce5ff; font-size: 13px;
|
|
141
|
+
}
|
|
142
|
+
.page {
|
|
143
|
+
display: grid;
|
|
144
|
+
grid-template-columns: minmax(0, 1fr) minmax(260px, 320px);
|
|
145
|
+
gap: 24px;
|
|
146
|
+
align-items: start;
|
|
147
|
+
}
|
|
148
|
+
main > nav, .rail {
|
|
149
|
+
position: sticky;
|
|
150
|
+
top: 16px;
|
|
151
|
+
align-self: start;
|
|
152
|
+
display: grid;
|
|
153
|
+
gap: 14px;
|
|
154
|
+
}
|
|
155
|
+
.panel {
|
|
156
|
+
padding: 18px;
|
|
157
|
+
border-radius: var(--radius-lg);
|
|
158
|
+
background: var(--panel);
|
|
159
|
+
border: 1px solid var(--border);
|
|
160
|
+
box-shadow: var(--shadow);
|
|
161
|
+
backdrop-filter: blur(14px);
|
|
162
|
+
}
|
|
163
|
+
.panel h2, .panel h3 { margin: 0 0 10px; }
|
|
164
|
+
.panel h2 { font-size: 15px; color: var(--muted); text-transform: uppercase; letter-spacing: 0.1em; }
|
|
165
|
+
.nav-links { display: grid; gap: 8px; }
|
|
166
|
+
.nav-links a {
|
|
167
|
+
display: block; padding: 10px 12px; color: var(--text); text-decoration: none;
|
|
168
|
+
border-radius: 12px; background: rgba(255,255,255,0.02);
|
|
169
|
+
}
|
|
170
|
+
.nav-links a:hover { background: rgba(255,255,255,0.07); }
|
|
171
|
+
.section {
|
|
172
|
+
margin-bottom: 20px; padding: 24px; border-radius: var(--radius-xl);
|
|
173
|
+
background: var(--panel); border: 1px solid var(--border); box-shadow: var(--shadow);
|
|
174
|
+
backdrop-filter: blur(16px);
|
|
175
|
+
}
|
|
176
|
+
.section h2 { margin: 0 0 14px; font-size: clamp(22px, 2vw, 32px); text-wrap: balance; }
|
|
177
|
+
.section h3 { margin: 0 0 10px; font-size: 18px; }
|
|
116
178
|
.section p, .section li, .section td, .section th { color: var(--muted); font-size: 15px; }
|
|
117
|
-
.section ul { padding-left: 18px; }
|
|
118
|
-
.grid { display: grid; gap: 16px; }
|
|
119
|
-
.grid.
|
|
120
|
-
.card
|
|
121
|
-
.card
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
.
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
.
|
|
128
|
-
.
|
|
129
|
-
.
|
|
130
|
-
.
|
|
131
|
-
.
|
|
132
|
-
.
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
179
|
+
.section ul { margin: 0; padding-left: 18px; }
|
|
180
|
+
.summary-grid, .stats-grid, .aside-grid, .card-grid, .lane-grid, .dependency-grid { display: grid; gap: 16px; }
|
|
181
|
+
.summary-grid, .stats-grid { grid-template-columns: repeat(4, minmax(0, 1fr)); }
|
|
182
|
+
.card-grid.cols-2, .aside-grid, .dependency-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
|
|
183
|
+
.card, .stat, .task-card, .lane, .detail-card, .mini-card {
|
|
184
|
+
padding: 18px; border-radius: var(--radius-md); background: var(--panel-soft); border: 1px solid var(--border);
|
|
185
|
+
}
|
|
186
|
+
.stat .label, .mini-card .label, .task-meta, .kicker {
|
|
187
|
+
font-size: 12px; text-transform: uppercase; letter-spacing: 0.11em; color: var(--accent-2);
|
|
188
|
+
}
|
|
189
|
+
.stat .value { margin-top: 8px; font-size: 28px; font-weight: 700; color: var(--text); }
|
|
190
|
+
.stat .hint, .mini-card p { margin: 8px 0 0; color: var(--muted-2); font-size: 13px; }
|
|
191
|
+
.card p, .task-card p, .lane p { margin: 0; }
|
|
192
|
+
.kicker { margin-bottom: 8px; }
|
|
193
|
+
.lane-grid { grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); align-items: start; }
|
|
194
|
+
.lane {
|
|
195
|
+
padding: 20px;
|
|
196
|
+
background: linear-gradient(180deg, rgba(255,255,255,0.06), rgba(255,255,255,0.035));
|
|
197
|
+
}
|
|
198
|
+
.lane h3 { margin: 0 0 4px; font-size: 22px; }
|
|
199
|
+
.lane .lane-copy { color: var(--muted-2); margin-bottom: 14px; }
|
|
200
|
+
.stack { display: grid; gap: 12px; }
|
|
201
|
+
.task-card {
|
|
202
|
+
background: rgba(7, 14, 27, 0.44);
|
|
203
|
+
}
|
|
204
|
+
.task-head { display: flex; gap: 10px; align-items: flex-start; justify-content: space-between; margin-bottom: 10px; }
|
|
205
|
+
.task-id {
|
|
206
|
+
display: inline-flex; align-items: center; justify-content: center; min-width: 42px;
|
|
207
|
+
padding: 6px 10px; border-radius: 999px; background: rgba(124,156,255,0.18);
|
|
208
|
+
color: #dbe5ff; font-weight: 700; font-size: 13px;
|
|
209
|
+
}
|
|
210
|
+
.task-title { margin: 0; font-size: 16px; line-height: 1.35; }
|
|
211
|
+
.task-body { color: var(--muted); font-size: 14px; }
|
|
212
|
+
.chip-row { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 12px; }
|
|
213
|
+
.dependency-row { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 12px; }
|
|
214
|
+
.dependency-pill {
|
|
215
|
+
display: inline-flex; align-items: center; gap: 10px; padding: 10px 12px; border-radius: 14px;
|
|
216
|
+
background: rgba(255,255,255,0.05); border: 1px solid rgba(255,255,255,0.08); color: var(--text); font-size: 13px;
|
|
217
|
+
}
|
|
218
|
+
.dependency-pill .arrow { color: var(--accent-2); font-weight: 700; }
|
|
219
|
+
.chip {
|
|
220
|
+
display: inline-flex; align-items: center; gap: 6px; padding: 6px 10px; border-radius: 999px;
|
|
221
|
+
background: rgba(255,255,255,0.06); border: 1px solid rgba(255,255,255,0.08); color: var(--muted-2); font-size: 12px;
|
|
222
|
+
}
|
|
223
|
+
.chip.warn { color: #ffd88d; border-color: rgba(251,191,36,0.22); background: rgba(251,191,36,0.08); }
|
|
224
|
+
.chip.good { color: #c7ffd7; border-color: rgba(134,239,172,0.24); background: rgba(134,239,172,0.08); }
|
|
225
|
+
.chip.danger { color: #ffd1d1; border-color: rgba(252,165,165,0.24); background: rgba(252,165,165,0.09); }
|
|
226
|
+
.callout {
|
|
227
|
+
padding: 18px; border-radius: var(--radius-md); background: rgba(124,156,255,0.09);
|
|
228
|
+
border: 1px solid rgba(124,156,255,0.18);
|
|
229
|
+
}
|
|
230
|
+
.callout strong { display: block; margin-bottom: 8px; }
|
|
231
|
+
.details { margin-top: 14px; }
|
|
232
|
+
.details summary {
|
|
233
|
+
cursor: pointer; list-style: none; color: var(--text); font-weight: 600;
|
|
234
|
+
}
|
|
235
|
+
.details summary::-webkit-details-marker { display: none; }
|
|
236
|
+
.details .detail-body { margin-top: 12px; color: var(--muted); font-size: 14px; }
|
|
237
|
+
.raw {
|
|
238
|
+
white-space: pre-wrap; background: rgba(0,0,0,0.24); padding: 16px; border-radius: 14px; border: 1px solid var(--border);
|
|
239
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 13px; color: #d8e1fb;
|
|
240
|
+
}
|
|
241
|
+
.metric-list { display: grid; gap: 12px; }
|
|
242
|
+
.metric-list .mini-card { padding: 14px 16px; }
|
|
243
|
+
.metric-list .value { margin-top: 6px; font-size: 15px; color: var(--text); font-weight: 600; }
|
|
244
|
+
@media (max-width: 1080px) {
|
|
245
|
+
.page { grid-template-columns: 1fr; }
|
|
246
|
+
main > nav, .rail { position: static; }
|
|
247
|
+
.summary-grid, .stats-grid, .card-grid.cols-2, .aside-grid { grid-template-columns: 1fr 1fr; }
|
|
248
|
+
}
|
|
249
|
+
@media (max-width: 720px) {
|
|
250
|
+
main { padding: 20px 14px 60px; }
|
|
251
|
+
.hero, .section { padding: 20px; }
|
|
252
|
+
.summary-grid, .stats-grid, .card-grid.cols-2, .aside-grid { grid-template-columns: 1fr; }
|
|
253
|
+
}
|
|
136
254
|
</style>
|
|
137
255
|
</head>
|
|
138
256
|
<body>
|
|
@@ -141,63 +259,273 @@ pre { white-space: pre-wrap; background: rgba(0,0,0,0.24); padding: 16px; border
|
|
|
141
259
|
<div class="eyebrow">${escapeHtml(eyebrow)}</div>
|
|
142
260
|
<h1>${escapeHtml(title)}</h1>
|
|
143
261
|
<p>${escapeHtml(summary || 'Shareable HTML artifact generated by Heart of Gold Visualize.')}</p>
|
|
144
|
-
|
|
262
|
+
<div class="hero-row">
|
|
263
|
+
${badge ? `<div class="badge">${escapeHtml(badge)}</div>` : ''}
|
|
264
|
+
</div>
|
|
145
265
|
</section>
|
|
146
|
-
<div class="
|
|
147
|
-
<nav>
|
|
148
|
-
<h2>Contents</h2>
|
|
149
|
-
${toc}
|
|
150
|
-
</nav>
|
|
266
|
+
<div class="page">
|
|
151
267
|
<section>${content}</section>
|
|
268
|
+
<aside class="rail">${nav}</aside>
|
|
152
269
|
</div>
|
|
153
270
|
</main>
|
|
154
271
|
</body>
|
|
155
272
|
</html>`;
|
|
156
273
|
}
|
|
157
274
|
|
|
275
|
+
function renderListBlock(block) {
|
|
276
|
+
const items = block.split(/\n/).map((line) => line.match(/^\s*[-*]\s+(.+)$/)?.[1]).filter(Boolean);
|
|
277
|
+
if (!items.length) return null;
|
|
278
|
+
return `<ul>${items.map((item) => `<li>${escapeHtml(item)}</li>`).join('')}</ul>`;
|
|
279
|
+
}
|
|
280
|
+
|
|
158
281
|
function renderOutline(doc, title, eyebrow, badge) {
|
|
159
|
-
const
|
|
282
|
+
const nav = `
|
|
283
|
+
<div class="panel"><h2>View</h2><div class="metric-list"><div class="mini-card"><div class="label">Mode</div><div class="value">Structured outline</div><p>Safe default when the agent does not force a richer artifact family.</p></div></div></div>
|
|
284
|
+
<div class="panel"><h2>Sections</h2><div class="nav-links">${doc.sections.map((s, i) => `<a href="#section-${i}">${escapeHtml(s.title)}</a>`).join('')}</div></div>`;
|
|
160
285
|
const content = doc.sections.map((section, i) => {
|
|
161
|
-
const
|
|
162
|
-
|
|
163
|
-
if (b.includes('\n- ') || b.match(/^-/m)) {
|
|
164
|
-
const items = b.split(/\n/).map((line) => line.match(/^\s*[-*]\s+(.+)$/)?.[1]).filter(Boolean);
|
|
165
|
-
if (items.length) return `<ul>${items.map((item) => `<li>${escapeHtml(item)}</li>`).join('')}</ul>`;
|
|
166
|
-
}
|
|
167
|
-
return `<p>${escapeHtml(b)}</p>`;
|
|
168
|
-
}).join('');
|
|
169
|
-
return `<article id="section-${i}" class="section"><h2>${escapeHtml(section.title)}</h2>${paragraphs || '<p>No details captured.</p>'}</article>`;
|
|
286
|
+
const blocks = splitBlocks(section.lines).map((block) => renderListBlock(block) || `<p>${escapeHtml(block)}</p>`).join('');
|
|
287
|
+
return `<article id="section-${i}" class="section"><div class="kicker">Structured View</div><h2>${escapeHtml(section.title)}</h2>${blocks || '<p>No details captured.</p>'}</article>`;
|
|
170
288
|
}).join('');
|
|
171
|
-
return renderShell({ title, eyebrow, summary: firstParagraph(doc.sections[0]?.lines || []),
|
|
289
|
+
return renderShell({ title, eyebrow, summary: firstParagraph(doc.sections[0]?.lines || []), nav, content, badge });
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
function parseTaskSection(section) {
|
|
293
|
+
const source = textFromLines(section.lines);
|
|
294
|
+
if (!source) return [];
|
|
295
|
+
const regex = /- \[( |x)\]\s+\*\*([^*]+)\*\*\s*([\s\S]*?)(?=(?:\s+- \[(?: |x)\]\s+\*\*)|$)/g;
|
|
296
|
+
const tasks = [];
|
|
297
|
+
let match;
|
|
298
|
+
while ((match = regex.exec(source))) {
|
|
299
|
+
const done = match[1].toLowerCase() === 'x';
|
|
300
|
+
const bold = normalizeWhitespace(match[2]);
|
|
301
|
+
const detail = normalizeWhitespace(match[3] || '');
|
|
302
|
+
const idMatch = bold.match(/^([A-Z][A-Z0-9-]*)\.\s*(.+)$/);
|
|
303
|
+
const id = idMatch ? idMatch[1] : null;
|
|
304
|
+
const title = idMatch ? idMatch[2] : bold;
|
|
305
|
+
const depMatch = detail.match(/\*\*\[depends on ([^\]]+)\]\*\*/i);
|
|
306
|
+
const parallel = /\*\*\[parallel(?: with [^\]]+)?\]\*\*/i.test(detail);
|
|
307
|
+
const notInAcceptance = /not in this plan'?s acceptance/i.test(detail);
|
|
308
|
+
tasks.push({
|
|
309
|
+
done,
|
|
310
|
+
id,
|
|
311
|
+
title,
|
|
312
|
+
detail,
|
|
313
|
+
dependency: depMatch ? depMatch[1] : null,
|
|
314
|
+
parallel,
|
|
315
|
+
deferred: notInAcceptance,
|
|
316
|
+
});
|
|
317
|
+
}
|
|
318
|
+
return tasks;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
function compactDetail(detail) {
|
|
322
|
+
return detail
|
|
323
|
+
.replace(/\*\*\[parallel(?: with [^\]]+)?\]\*\*/ig, '')
|
|
324
|
+
.replace(/\*\*\[depends on ([^\]]+)\]\*\*/ig, 'Depends on $1.')
|
|
325
|
+
.replace(/\*\*/g, '')
|
|
326
|
+
.replace(/\s+/g, ' ')
|
|
327
|
+
.trim();
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
function inferTaskIdFromDependency(value) {
|
|
331
|
+
const match = String(value).match(/([A-Z][A-Z0-9-]*)/);
|
|
332
|
+
return match ? match[1] : value;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
function extractPlanModel(doc) {
|
|
336
|
+
const byTitle = new Map(doc.sections.map((s) => [s.title.toLowerCase(), s]));
|
|
337
|
+
const find = (name) => byTitle.get(name.toLowerCase());
|
|
338
|
+
const prioritySections = doc.sections.filter((s) => /^(P\d+|Deferred)/.test(s.title));
|
|
339
|
+
const workstreams = prioritySections.map((section) => ({
|
|
340
|
+
title: section.title,
|
|
341
|
+
summary: firstParagraph(section.lines),
|
|
342
|
+
tasks: parseTaskSection(section),
|
|
343
|
+
}));
|
|
344
|
+
|
|
345
|
+
const tasks = workstreams.flatMap((lane) => lane.tasks.map((task) => ({ ...task, lane: lane.title })));
|
|
346
|
+
const dependencies = tasks.filter((t) => t.dependency).map((t) => ({
|
|
347
|
+
from: inferTaskIdFromDependency(t.dependency),
|
|
348
|
+
to: t.id || t.title,
|
|
349
|
+
lane: t.lane,
|
|
350
|
+
}));
|
|
351
|
+
const risksText = firstParagraph(find('Risk Analysis')?.lines || []);
|
|
352
|
+
const acceptanceText = firstParagraph(find('Acceptance Criteria')?.lines || []);
|
|
353
|
+
const constraintsText = firstParagraph(find('Constraints and Boundaries')?.lines || []);
|
|
354
|
+
const proposedSolution = firstParagraph(find('Proposed Solution')?.lines || []);
|
|
355
|
+
const scope = firstParagraph(find('Scope and Non-Goals')?.lines || []);
|
|
356
|
+
const rationale = firstParagraph(find('Decision Rationale')?.lines || []);
|
|
357
|
+
|
|
358
|
+
return {
|
|
359
|
+
mission: firstParagraph(doc.sections[0]?.lines || []),
|
|
360
|
+
problem: firstParagraph(find('Problem Statement')?.lines || []),
|
|
361
|
+
targetEndState: firstParagraph(find('Target End State')?.lines || []),
|
|
362
|
+
proposedSolution,
|
|
363
|
+
scope,
|
|
364
|
+
rationale,
|
|
365
|
+
risksText,
|
|
366
|
+
acceptanceText,
|
|
367
|
+
constraintsText,
|
|
368
|
+
workstreams,
|
|
369
|
+
tasks,
|
|
370
|
+
dependencies,
|
|
371
|
+
dependencyCount: dependencies.length,
|
|
372
|
+
parallelCount: tasks.filter((t) => t.parallel).length,
|
|
373
|
+
deferredCount: tasks.filter((t) => t.deferred).length,
|
|
374
|
+
};
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
function renderTaskCard(task) {
|
|
378
|
+
const chips = [];
|
|
379
|
+
chips.push(`<span class="chip ${task.done ? 'good' : 'warn'}">${task.done ? 'Done' : 'Planned'}</span>`);
|
|
380
|
+
if (task.dependency) chips.push(`<span class="chip">Depends on ${escapeHtml(task.dependency)}</span>`);
|
|
381
|
+
if (task.parallel) chips.push('<span class="chip good">Parallel</span>');
|
|
382
|
+
if (task.deferred) chips.push('<span class="chip danger">Deferred</span>');
|
|
383
|
+
const detail = compactDetail(task.detail);
|
|
384
|
+
const short = detail.slice(0, 220) + (detail.length > 220 ? '…' : '');
|
|
385
|
+
return `<article class="task-card">
|
|
386
|
+
<div class="task-head">
|
|
387
|
+
<div>
|
|
388
|
+
<div class="task-meta">${escapeHtml(task.lane || 'Task')}</div>
|
|
389
|
+
<h4 class="task-title">${task.id ? `<span class="task-id">${escapeHtml(task.id)}</span> ` : ''}${escapeHtml(task.title)}</h4>
|
|
390
|
+
</div>
|
|
391
|
+
</div>
|
|
392
|
+
${short ? `<p class="task-body">${escapeHtml(short)}</p>` : ''}
|
|
393
|
+
<div class="chip-row">${chips.join('')}</div>
|
|
394
|
+
${detail.length > 220 ? `<details class="details"><summary>Show Full Task Detail</summary><div class="detail-body">${escapeHtml(detail)}</div></details>` : ''}
|
|
395
|
+
</article>`;
|
|
172
396
|
}
|
|
173
397
|
|
|
174
398
|
function renderRoadmap(doc, title, badge) {
|
|
175
|
-
const
|
|
176
|
-
const
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
399
|
+
const model = extractPlanModel(doc);
|
|
400
|
+
const nav = `
|
|
401
|
+
<div class="panel">
|
|
402
|
+
<h2>Execution Snapshot</h2>
|
|
403
|
+
<div class="metric-list">
|
|
404
|
+
<div class="mini-card"><div class="label">Mode</div><div class="value">Plan dashboard</div><p>Optimized for execution clarity rather than markdown fidelity.</p></div>
|
|
405
|
+
<div class="mini-card"><div class="label">Workstreams</div><div class="value">${model.workstreams.length}</div></div>
|
|
406
|
+
<div class="mini-card"><div class="label">Tasks</div><div class="value">${model.tasks.length}</div></div>
|
|
407
|
+
<div class="mini-card"><div class="label">Dependencies</div><div class="value">${model.dependencyCount}</div></div>
|
|
408
|
+
</div>
|
|
409
|
+
</div>
|
|
410
|
+
<div class="panel">
|
|
411
|
+
<h2>Jump</h2>
|
|
412
|
+
<div class="nav-links">
|
|
413
|
+
<a href="#summary">Summary</a>
|
|
414
|
+
<a href="#workstreams">Priority Lanes</a>
|
|
415
|
+
<a href="#acceptance">Acceptance & Risks</a>
|
|
416
|
+
<a href="#appendix">Source Appendix</a>
|
|
417
|
+
</div>
|
|
418
|
+
</div>
|
|
419
|
+
<div class="panel">
|
|
420
|
+
<h2>Decision</h2>
|
|
421
|
+
<p>${escapeHtml(model.rationale || 'Execution-first plan view selected to foreground phases, tasks, and dependencies.')}</p>
|
|
422
|
+
</div>`;
|
|
423
|
+
|
|
424
|
+
const heroStats = [
|
|
425
|
+
['Workstreams', model.workstreams.length, 'Priority lanes in this plan'],
|
|
426
|
+
['Tasks', model.tasks.length, 'Tracked implementation actions'],
|
|
427
|
+
['Parallel', model.parallelCount, 'Tasks marked as parallelizable'],
|
|
428
|
+
['Deferred', model.deferredCount, 'Explicitly out of this pass'],
|
|
183
429
|
];
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
const
|
|
191
|
-
|
|
430
|
+
|
|
431
|
+
const topTasks = model.tasks.slice(0, 4).map(renderTaskCard).join('');
|
|
432
|
+
const dependencyHtml = model.dependencies.length
|
|
433
|
+
? model.dependencies.slice(0, 10).map((dep) => `<div class="dependency-pill"><span>${escapeHtml(dep.from)}</span><span class="arrow">→</span><span>${escapeHtml(dep.to)}</span><span class="chip">${escapeHtml(dep.lane)}</span></div>`).join('')
|
|
434
|
+
: '<div class="card"><p>No explicit dependencies extracted.</p></div>';
|
|
435
|
+
const laneHtml = model.workstreams.map((lane) => {
|
|
436
|
+
const laneId = slugify(lane.title);
|
|
437
|
+
const intro = lane.summary || `${lane.tasks.length} task${lane.tasks.length === 1 ? '' : 's'} in this lane.`;
|
|
438
|
+
return `<section class="lane" id="lane-${laneId}">
|
|
439
|
+
<div class="kicker">Priority Lane</div>
|
|
440
|
+
<h3>${escapeHtml(lane.title)}</h3>
|
|
441
|
+
<p class="lane-copy">${escapeHtml(intro)}</p>
|
|
442
|
+
<div class="stack">${lane.tasks.length ? lane.tasks.map(renderTaskCard).join('') : '<div class="task-card"><p class="task-body">No parsed tasks found. See appendix for source detail.</p></div>'}</div>
|
|
443
|
+
</section>`;
|
|
192
444
|
}).join('');
|
|
193
|
-
|
|
445
|
+
|
|
446
|
+
const appendixSections = doc.sections.map((section, i) => {
|
|
447
|
+
const raw = textFromLines(section.lines);
|
|
448
|
+
const snippet = raw.length > 1400 ? `${raw.slice(0, 1400)}\n…` : raw;
|
|
449
|
+
return `<article class="detail-card">
|
|
450
|
+
<div class="kicker">Source Section</div>
|
|
451
|
+
<h3>${escapeHtml(section.title)}</h3>
|
|
452
|
+
<p>${escapeHtml(firstParagraph(section.lines) || 'No summary captured.')}</p>
|
|
453
|
+
<details class="details"><summary>Show Source Detail</summary><div class="detail-body"><div class="raw">${escapeHtml(snippet || 'No detail captured.')}</div></div></details>
|
|
454
|
+
</article>`;
|
|
455
|
+
}).join('');
|
|
456
|
+
|
|
457
|
+
const content = `
|
|
458
|
+
<article class="section" id="summary">
|
|
459
|
+
<div class="kicker">Plan Dashboard</div>
|
|
460
|
+
<h2>Execution Summary</h2>
|
|
461
|
+
<div class="callout"><strong>Mission</strong>${escapeHtml(model.mission || 'No summary captured.')}</div>
|
|
462
|
+
<div class="summary-grid" style="margin-top:16px;">
|
|
463
|
+
${heroStats.map(([label, value, hint]) => `<div class="stat"><div class="label">${escapeHtml(label)}</div><div class="value">${escapeHtml(value)}</div><div class="hint">${escapeHtml(hint)}</div></div>`).join('')}
|
|
464
|
+
</div>
|
|
465
|
+
<div class="aside-grid" style="margin-top:16px;">
|
|
466
|
+
<div class="card"><div class="kicker">Problem</div><h3>Why This Exists</h3><p>${escapeHtml(model.problem || 'See source document for full problem framing.')}</p></div>
|
|
467
|
+
<div class="card"><div class="kicker">Target</div><h3>What Good Looks Like</h3><p>${escapeHtml(model.targetEndState || 'See source document for target end state.')}</p></div>
|
|
468
|
+
</div>
|
|
469
|
+
</article>
|
|
470
|
+
|
|
471
|
+
<article class="section">
|
|
472
|
+
<div class="kicker">Plan Strategy</div>
|
|
473
|
+
<h2>Scope, Approach & Key Moves</h2>
|
|
474
|
+
<div class="card-grid cols-2">
|
|
475
|
+
<div class="card"><div class="kicker">Scope</div><p>${escapeHtml(model.scope || 'See source document for scope detail.')}</p></div>
|
|
476
|
+
<div class="card"><div class="kicker">Approach</div><p>${escapeHtml(model.proposedSolution || 'See source document for solution framing.')}</p></div>
|
|
477
|
+
</div>
|
|
478
|
+
</article>
|
|
479
|
+
|
|
480
|
+
<article class="section" id="workstreams">
|
|
481
|
+
<div class="kicker">Priority Lanes</div>
|
|
482
|
+
<h2>Workstreams & Tasks</h2>
|
|
483
|
+
<p>The plan is shown as priority lanes so execution order, task grouping, and dependency shape are visible without reading the full markdown in sequence.</p>
|
|
484
|
+
<div class="lane-grid" style="margin-top:16px;">${laneHtml}</div>
|
|
485
|
+
</article>
|
|
486
|
+
|
|
487
|
+
<article class="section">
|
|
488
|
+
<div class="kicker">Immediate Scan</div>
|
|
489
|
+
<h2>Representative Tasks</h2>
|
|
490
|
+
<div class="card-grid cols-2">${topTasks || '<div class="card"><p>No tasks captured.</p></div>'}</div>
|
|
491
|
+
</article>
|
|
492
|
+
|
|
493
|
+
<article class="section">
|
|
494
|
+
<div class="kicker">Dependency Shape</div>
|
|
495
|
+
<h2>Key Sequencing</h2>
|
|
496
|
+
<p>Dependencies are surfaced explicitly here so the execution order is visible without reading task prose.</p>
|
|
497
|
+
<div class="dependency-row">${dependencyHtml}</div>
|
|
498
|
+
</article>
|
|
499
|
+
|
|
500
|
+
<article class="section" id="acceptance">
|
|
501
|
+
<div class="kicker">Execution Guardrails</div>
|
|
502
|
+
<h2>Acceptance, Risks & Constraints</h2>
|
|
503
|
+
<div class="card-grid cols-2">
|
|
504
|
+
<div class="card"><div class="kicker">Acceptance Gates</div><p>${escapeHtml(model.acceptanceText || 'See source markdown for explicit acceptance criteria.')}</p></div>
|
|
505
|
+
<div class="card"><div class="kicker">Primary Risk</div><p>${escapeHtml(model.risksText || 'No risk summary extracted.')}</p></div>
|
|
506
|
+
<div class="card"><div class="kicker">Constraint</div><p>${escapeHtml(model.constraintsText || 'No constraint summary extracted.')}</p></div>
|
|
507
|
+
<div class="card"><div class="kicker">Decision Rationale</div><p>${escapeHtml(model.rationale || 'No rationale summary extracted.')}</p></div>
|
|
508
|
+
</div>
|
|
509
|
+
</article>
|
|
510
|
+
|
|
511
|
+
<article class="section" id="appendix">
|
|
512
|
+
<div class="kicker">Source Appendix</div>
|
|
513
|
+
<h2>Source Detail</h2>
|
|
514
|
+
<p>The raw plan remains canonical. This appendix keeps source detail available without letting markdown dominate the primary reading path.</p>
|
|
515
|
+
<div class="stack" style="margin-top:16px;">${appendixSections}</div>
|
|
516
|
+
</article>`;
|
|
517
|
+
|
|
518
|
+
return renderShell({ title, eyebrow: 'Plan Dashboard', summary: model.mission, nav, content, badge });
|
|
194
519
|
}
|
|
195
520
|
|
|
196
521
|
function renderArchitecture(doc, title, badge) {
|
|
197
|
-
const
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
522
|
+
const nav = `
|
|
523
|
+
<div class="panel"><h2>View</h2><div class="metric-list"><div class="mini-card"><div class="label">Mode</div><div class="value">Architecture brief</div><p>Focused on boundaries, sections, and key decisions.</p></div></div></div>
|
|
524
|
+
<div class="panel"><h2>Sections</h2><div class="nav-links">${doc.sections.map((s, i) => `<a href="#section-${i}">${escapeHtml(s.title)}</a>`).join('')}</div></div>`;
|
|
525
|
+
const cards = doc.sections.slice(0, 6).map((section) => `<div class="card"><div class="kicker">${escapeHtml(section.title)}</div><p>${escapeHtml(firstParagraph(section.lines) || 'See source markdown for detail.')}</p></div>`).join('');
|
|
526
|
+
const detail = doc.sections.map((section, i) => `<article id="section-${i}" class="section"><div class="kicker">Architecture Section</div><h2>${escapeHtml(section.title)}</h2><p>${escapeHtml(firstParagraph(section.lines) || 'See source markdown for detail.')}</p></article>`).join('');
|
|
527
|
+
const content = `<article class="section"><div class="kicker">Architecture View</div><h2>System Overview</h2><div class="card-grid cols-2">${cards}</div></article>${detail}`;
|
|
528
|
+
return renderShell({ title, eyebrow: 'Architecture View', summary: firstParagraph(doc.sections[0]?.lines || []), nav, content, badge });
|
|
201
529
|
}
|
|
202
530
|
|
|
203
531
|
function renderMindmap(filePath, outFile) {
|