@heart-of-gold/toolkit 0.1.33 → 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.
@@ -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.3"
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.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.1"
30
+ "version": "0.2.3"
31
31
  },
32
32
  {
33
33
  "name": "quellis",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@heart-of-gold/toolkit",
3
- "version": "0.1.33",
3
+ "version": "0.1.35",
4
4
  "type": "module",
5
5
  "description": "Cross-platform installer for Heart of Gold skills — works with Codex, OpenCode, Pi, Claude Code, and more",
6
6
  "bin": {
@@ -11,6 +11,7 @@
11
11
  "check:security": "python3 scripts/check-security-regressions.py",
12
12
  "check:compat": "python3 scripts/check-harness-compatibility.py",
13
13
  "test:pi-guided": "node --test tests/pi-guided-workflows.test.mjs",
14
+ "test:visualize": "node --test tests/visualize-smart-render.test.mjs",
14
15
  "prepublishOnly": "npm run check:publish-safety && npm run check:compat"
15
16
  },
16
17
  "dependencies": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "babel-fish",
3
- "version": "0.2.1",
3
+ "version": "0.2.3",
4
4
  "description": "Universal Translator — media generation tools for audio, image, video, and visualization",
5
5
  "author": {
6
6
  "name": "ondrej-svec",
@@ -1,10 +1,10 @@
1
1
  ---
2
2
  name: visualize
3
3
  description: >
4
- Render mind maps and tree visualizations from markdown. Prefer shareable HTML first for brainstorms, plans,
5
- architecture docs, and other structured workflow artifacts when share-html infrastructure is configured;
6
- otherwise fall back to terminal output for quick local inspection. Works on markdown files or any structured
7
- content. Triggers: visualize, mindmap, mind map, show me the structure, draw a map.
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,31 +14,205 @@ allowed-tools:
14
14
 
15
15
  # Visualize — Babel Fish
16
16
 
17
- Translating structured text into spatial understanding. Because walls of text hide structure that pictures reveal.
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
- ## The Renderer
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
25
191
 
26
- A Node.js script that converts markdown headings into colored, spatial Unicode mind maps.
192
+ ## Renderers
27
193
 
28
- **Location:** `scripts/render-mindmap/index.js` (relative to this skill's directory)
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
196
+ - `scripts/render-mindmap/index.js` — specialized mind-map renderer for branchy content
197
+
198
+ **Locations:**
199
+ - `scripts/smart-render.js`
200
+ - `scripts/render-mindmap/index.js`
201
+
202
+ To find the smart renderer path:
29
203
 
30
- **To find the script path**, locate it by searching for `render-mindmap/index.js`:
31
204
  ```bash
32
205
  # Option 1: Use CLAUDE_PLUGIN_ROOT if available
33
- SCRIPT="${CLAUDE_PLUGIN_ROOT}/skills/visualize/scripts/render-mindmap/index.js"
206
+ SCRIPT="${CLAUDE_PLUGIN_ROOT}/skills/visualize/scripts/smart-render.js"
34
207
 
35
208
  # Option 2: Search for it
36
- SCRIPT=$(find ~/.claude/plugins -path "*/babel-fish/skills/visualize/scripts/render-mindmap/index.js" 2>/dev/null | head -1)
209
+ SCRIPT=$(find ~/.claude/plugins -path "*/babel-fish/skills/visualize/scripts/smart-render.js" 2>/dev/null | head -1)
37
210
  ```
38
211
 
39
- **First run:** If `node_modules/` doesn't exist in the renderer directory, run `npm install` there first:
212
+ First run for the mind-map renderer:
213
+
40
214
  ```bash
41
- RENDER_DIR=$(dirname "$SCRIPT")
215
+ RENDER_DIR=$(dirname "$SCRIPT")/render-mindmap
42
216
  if [ ! -d "$RENDER_DIR/node_modules" ]; then
43
217
  (cd "$RENDER_DIR" && npm install --silent)
44
218
  fi
@@ -47,21 +221,20 @@ fi
47
221
  ## Usage
48
222
 
49
223
  ```bash
50
- # Render a markdown file in the terminal
51
- node "$SCRIPT" path/to/file.md
52
-
53
- # With options
54
- node "$SCRIPT" --no-color path/to/file.md # plain Unicode, no ANSI
55
- node "$SCRIPT" --width 120 path/to/file.md # constrain to 120 columns
56
- node "$SCRIPT" --depth 2 path/to/file.md # limit tree depth
57
- node "$SCRIPT" --json path/to/data.json # JSON tree input
58
- node "$SCRIPT" --html /tmp/map.html path/to/file.md # generate HTML mind map
59
-
60
- # Pipe markdown
61
- echo "# Root\n## Branch A\n## Branch B" | node "$SCRIPT"
224
+ # Generate a safe default HTML visualization for a markdown file
225
+ node "$SCRIPT" path/to/file.md --out /tmp/view.html
226
+
227
+ # Usually the coding agent should choose the mode from context
228
+ node "$SCRIPT" path/to/file.md --mode roadmap --out /tmp/view.html
229
+ node "$SCRIPT" path/to/file.md --mode outline --out /tmp/view.html
230
+ node "$SCRIPT" path/to/file.md --mode architecture --out /tmp/view.html
231
+ node "$SCRIPT" path/to/file.md --mode mindmap --out /tmp/view.html
232
+
233
+ # Use the specialized mind-map renderer directly when needed
234
+ node "$(dirname "$SCRIPT")/render-mindmap/index.js" --html /tmp/map.html path/to/file.md
62
235
  ```
63
236
 
64
- ### Shareable HTML flow
237
+ ## HTML Share Flow
65
238
 
66
239
  Use the helper script when the user wants a browser URL and the share server is already configured:
67
240
 
@@ -70,152 +243,113 @@ bash scripts/render-and-share.sh path/to/file.md
70
243
  ```
71
244
 
72
245
  This script:
73
- 1. generates an HTML mind map via the existing renderer
74
- 2. locates `share-html/scripts/publish.sh`
75
- 3. publishes the artifact to the configured local share server
76
- 4. prints the publish JSON so you can return the URL
77
-
78
- ## Rendering Behavior
79
-
80
- - **Auto-depth:** If no `--depth` is specified, the renderer tries depths 3, 2, 1 and picks the deepest that fits the terminal width.
81
- - **Pruning:** Long labels are truncated with `…`. Nodes with many children show the first few plus `+N more`.
82
- - **Colors** (via ANSI, visible in Claude Code bash output):
83
- - Root: bold white on blue
84
- - Depth 1: bold cyan
85
- - Depth 2: green
86
- - Depth 3: yellow
87
- - Depth 4+: dim
88
-
89
- ## Phase 0 — Determine What to Visualize
90
-
91
- First decide whether this should be a browser/shareable HTML view or a quick terminal view.
92
-
93
- **Prefer browser/shareable HTML first when:**
94
- - the source is a brainstorm, plan, architecture doc, or other structured workflow artifact
95
- - the user asks to open it in a browser
96
- - the user wants to share the result with another person or device
97
- - the structure is large enough that browser navigation is more useful than terminal rendering
98
- - `share-html` is configured
99
-
100
- **Prefer terminal rendering when:**
101
- - share-html is not configured
102
- - the user explicitly wants a quick terminal-only look
103
- - the environment is SSH-heavy and the browser/share path is not requested
246
+ 1. generates one HTML artifact via the smart renderer
247
+ 2. uses the mode the coding agent chose (or the renderer's safe default)
248
+ 3. locates `share-html/scripts/publish.sh`
249
+ 4. publishes the artifact to the configured local share server
250
+ 5. prints the publish result so you can return the URL
104
251
 
105
- When invoked as `/visualize [path]`:
252
+ ### Recommended share flow
106
253
 
107
- **If a file path is provided:**
108
- 1. Read the file
109
- 2. If it looks like a brainstorm, plan, architecture doc, or the user asked for browser viewing, try the shareable HTML path first
110
- 3. Otherwise render it directly with the mind map renderer
111
-
112
- **If no path is provided:**
113
- 1. Check if there's a recent brainstorm or plan document in the conversation context
114
- 2. If yes: use that document's path
115
- 3. If no: summarize the current conversation topic into a markdown structure with headings, write it to a temp file, then render
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.
116
263
 
117
- **If the user says "visualize this" or "show me a mind map":**
118
- 1. Look at what was just discussed or created
119
- 2. Generate an appropriate markdown structure
120
- 3. Render it
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.
121
265
 
122
- ## Phase 1 — Render or Share
266
+ ## Terminal Rendering
123
267
 
124
- ### Path A — Terminal rendering
268
+ Use terminal rendering when:
269
+ - share-html is not configured
270
+ - the user explicitly wants terminal-only output
271
+ - a quick local structural check is more useful than a browser view
125
272
 
126
- **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.
127
274
 
128
275
  Many harness bash panels truncate long output and wrap wide content, breaking alignment. Instead:
129
276
 
130
- 1. Locate the renderer script (see above)
131
- 2. Ensure dependencies are installed
277
+ 1. Locate the renderer script.
278
+ 2. Ensure dependencies are installed.
132
279
  3. Run the renderer with `--no-color`, redirect to a temp file:
133
280
  ```bash
134
281
  node "$SCRIPT" --no-color [file] > /tmp/mindmap-result.txt 2>&1
135
282
  ```
136
- 4. Read `/tmp/mindmap-result.txt`
137
- 5. Output the contents inside a markdown fenced code block in your response text
138
- 6. Clean up: `rm -f /tmp/mindmap-result.txt`
139
-
140
- The default mode is **vertical layout** — boxes on main branches, compact leaves, ~40 chars wide.
141
-
142
- ### Path B — Shareable HTML
143
-
144
- For brainstorms, plans, architecture docs, and other structured workflow artifacts, prefer this path first when `share-html` is configured.
145
-
146
- 1. Verify or assume the input markdown is ready
147
- 2. Run:
283
+ 4. Read `/tmp/mindmap-result.txt`.
284
+ 5. Output the contents inside a fenced code block.
285
+ 6. Clean up:
148
286
  ```bash
149
- bash scripts/render-and-share.sh --url-only [file]
287
+ rm -f /tmp/mindmap-result.txt
150
288
  ```
151
- 3. Read the returned URL from stdout
152
- 4. Return that URL to the user as the primary result
153
- 5. Briefly explain what was published
154
-
155
- If you need more detail for debugging, you may run the helper without `--url-only` and inspect the returned JSON.
156
-
157
- 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.
158
289
 
159
- **For shell usage** (not through assistant panels): terminal rendering can use ANSI colors, or `--horizontal` for the wide spatial layout.
160
-
161
- ## Phase 2 — Offer Next Steps
162
-
163
- After rendering or sharing, briefly note:
164
- - for terminal mode: "Use `--depth N` to see more/less detail"
165
- - for terminal mode: "Use `--width N` to fit a different terminal size"
166
- - for shared HTML: return the final browser URL as the main result and say whether it is local-only or publicly reachable on the user's tailnet
167
- - if publishing failed due to missing share infrastructure: suggest `share-server-setup`
168
- - if the source was a brainstorm/plan/architecture doc, offer to continue the workflow (e.g., proceed to `/plan`, `/work`, or implementation)
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.
169
335
 
170
336
  ## Input Formats
171
337
 
172
338
  ### Markdown (primary)
173
- Standard markdown with `#` headings defining hierarchy:
174
- ```markdown
175
- # Root Topic
176
- ## Branch A
177
- - Detail 1
178
- - Detail 2
179
- ## Branch B
180
- ### Sub-branch
181
- - Detail 3
182
- ```
339
+ Standard markdown with `#` headings defining hierarchy.
183
340
 
184
341
  ### JSON
185
- Tree structure with `label` and `children`:
186
- ```json
187
- {
188
- "label": "Root",
189
- "children": [
190
- { "label": "Branch A", "children": [] },
191
- { "label": "Branch B", "children": [
192
- { "label": "Sub-branch", "children": [] }
193
- ]}
194
- ]
195
- }
196
- ```
197
-
198
- ## Generating Structure from Context
199
-
200
- When visualizing conversation context (no file path), create a markdown structure that captures:
201
-
202
- **For brainstorm content:**
203
- - Root = topic
204
- - Branches = key decisions or themes
205
- - Leaves = specific choices or details
342
+ Tree structure with `label` and `children`.
206
343
 
207
- **For plan content:**
208
- - Root = project name
209
- - Branches = phases
210
- - Leaves = tasks within each phase
344
+ ## Generating Structure From Context
211
345
 
212
- **For general discussion:**
213
- - Root = main topic
214
- - Branches = subtopics discussed
215
- - Leaves = key points or conclusions
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
216
350
 
217
351
  Write the generated markdown to `/tmp/mindmap-XXXXXX.md`, render it, then clean up.
218
352
 
219
353
  ## What Makes This Babel Fish
220
354
 
221
- The Babel Fish translates between languages. This skill translates between *modalities* — from linear text to spatial structure, and now from private working docs to shareable browser views. It makes the invisible visible: the hierarchy, the relationships, the gaps that only show up when you see the shape of the thinking.
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.
@@ -3,11 +3,12 @@ set -euo pipefail
3
3
 
4
4
  SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
5
5
  RENDER_DIR="$SCRIPT_DIR/render-mindmap"
6
- RENDER_SCRIPT="$RENDER_DIR/index.js"
6
+ RENDER_SCRIPT="$SCRIPT_DIR/smart-render.js"
7
7
  INPUT_PATH=""
8
8
  SLUG=""
9
9
  TITLE=""
10
10
  ALIAS=""
11
+ MODE="auto"
11
12
  KEEP_HTML=0
12
13
  URL_ONLY=0
13
14
  HTML_OUT=""
@@ -15,15 +16,17 @@ TEMP_DIR=""
15
16
 
16
17
  usage() {
17
18
  cat <<'EOF'
18
- Usage: render-and-share.sh <markdown-file> [--slug STEM] [--title TITLE] [--alias ALIAS] [--html-out PATH] [--keep-html] [--url-only]
19
+ Usage: render-and-share.sh <markdown-file> [--mode MODE] [--slug STEM] [--title TITLE] [--alias ALIAS] [--html-out PATH] [--keep-html] [--url-only]
19
20
 
20
- Generate an HTML mind map from a markdown file, publish it via share-html, and print the publish JSON.
21
+ Generate a polished HTML visualization from a markdown file, publish it via share-html, and print the publish JSON.
22
+ Use --mode to force a renderer (mindmap, outline, roadmap, architecture, mockup, explainer).
21
23
  Use --url-only to print only the final browser URL.
22
24
  EOF
23
25
  }
24
26
 
25
27
  while [[ $# -gt 0 ]]; do
26
28
  case "$1" in
29
+ --mode) MODE="$2"; shift 2 ;;
27
30
  --slug) SLUG="$2"; shift 2 ;;
28
31
  --title) TITLE="$2"; shift 2 ;;
29
32
  --alias) ALIAS="$2"; shift 2 ;;
@@ -75,7 +78,7 @@ cleanup() {
75
78
  }
76
79
  trap cleanup EXIT
77
80
 
78
- node "$RENDER_SCRIPT" --html "$HTML_OUT" "$INPUT_PATH" >/dev/null
81
+ node "$RENDER_SCRIPT" "$INPUT_PATH" --mode "$MODE" --out "$HTML_OUT" >/dev/null
79
82
 
80
83
  find_share_publish_script() {
81
84
  local candidates=(
@@ -0,0 +1,563 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { readFileSync, writeFileSync } from 'node:fs';
4
+ import { basename, dirname, join } from 'node:path';
5
+ import { execFileSync } from 'node:child_process';
6
+
7
+ function parseArgs(args) {
8
+ const opts = { file: null, out: null, mode: 'auto' };
9
+ for (let i = 0; i < args.length; i++) {
10
+ const arg = args[i];
11
+ if (arg === '--out' && i + 1 < args.length) opts.out = args[++i];
12
+ else if (arg === '--mode' && i + 1 < args.length) opts.mode = args[++i];
13
+ else if (!arg.startsWith('-')) opts.file = arg;
14
+ }
15
+ return opts;
16
+ }
17
+
18
+ function parseDocument(markdown) {
19
+ const lines = markdown.split(/\r?\n/);
20
+ let i = 0;
21
+ const frontmatter = {};
22
+ if (lines[0]?.trim() === '---') {
23
+ i = 1;
24
+ for (; i < lines.length; i++) {
25
+ const line = lines[i].trim();
26
+ if (line === '---') { i++; break; }
27
+ const match = line.match(/^([A-Za-z0-9_-]+):\s*(.*)$/);
28
+ if (match) frontmatter[match[1]] = match[2].replace(/^"|"$/g, '');
29
+ }
30
+ }
31
+
32
+ const sections = [];
33
+ let current = null;
34
+ for (; i < lines.length; i++) {
35
+ const line = lines[i];
36
+ const heading = line.match(/^(#{1,6})\s+(.+)$/);
37
+ if (heading) {
38
+ current = { level: heading[1].length, title: heading[2].trim(), lines: [] };
39
+ sections.push(current);
40
+ } else if (current) {
41
+ current.lines.push(line);
42
+ }
43
+ }
44
+ return { frontmatter, sections, raw: markdown };
45
+ }
46
+
47
+ function textFromLines(lines) {
48
+ return lines.join('\n').trim();
49
+ }
50
+
51
+ function normalizeWhitespace(text) {
52
+ return text.replace(/\s+/g, ' ').trim();
53
+ }
54
+
55
+ function firstParagraph(lines) {
56
+ const blocks = textFromLines(lines).split(/\n\s*\n/).map((b) => b.trim()).filter(Boolean);
57
+ return blocks[0] || '';
58
+ }
59
+
60
+ function splitBlocks(lines) {
61
+ return textFromLines(lines).split(/\n\s*\n/).map((b) => b.trim()).filter(Boolean);
62
+ }
63
+
64
+ function classifyMode(_filePath, _doc, preferredMode) {
65
+ if (preferredMode && preferredMode !== 'auto') return preferredMode;
66
+ return 'outline';
67
+ }
68
+
69
+ function escapeHtml(value) {
70
+ return String(value)
71
+ .replace(/&/g, '&amp;')
72
+ .replace(/</g, '&lt;')
73
+ .replace(/>/g, '&gt;')
74
+ .replace(/"/g, '&quot;')
75
+ .replace(/'/g, '&#39;');
76
+ }
77
+
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 = '' }) {
83
+ return `<!doctype html>
84
+ <html lang="en">
85
+ <head>
86
+ <meta charset="utf-8" />
87
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
88
+ <title>${escapeHtml(title)}</title>
89
+ <style>
90
+ :root {
91
+ --bg: #09111f;
92
+ --bg-2: #0f1a31;
93
+ --panel: rgba(255,255,255,0.06);
94
+ --panel-strong: rgba(255,255,255,0.1);
95
+ --panel-soft: rgba(255,255,255,0.04);
96
+ --text: #eef2ff;
97
+ --muted: #b8c0d9;
98
+ --muted-2: #93a0bf;
99
+ --border: rgba(255,255,255,0.11);
100
+ --accent: #7c9cff;
101
+ --accent-2: #80e0d0;
102
+ --success: #86efac;
103
+ --warn: #fbbf24;
104
+ --danger: #fca5a5;
105
+ --shadow: 0 24px 80px rgba(0,0,0,0.34);
106
+ --radius-xl: 28px;
107
+ --radius-lg: 22px;
108
+ --radius-md: 16px;
109
+ }
110
+ * { box-sizing: border-box; }
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; }
135
+ .hero p { margin: 0; max-width: 78ch; color: var(--muted); font-size: 17px; }
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; }
178
+ .section p, .section li, .section td, .section th { color: var(--muted); font-size: 15px; }
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
+ }
254
+ </style>
255
+ </head>
256
+ <body>
257
+ <main>
258
+ <section class="hero">
259
+ <div class="eyebrow">${escapeHtml(eyebrow)}</div>
260
+ <h1>${escapeHtml(title)}</h1>
261
+ <p>${escapeHtml(summary || 'Shareable HTML artifact generated by Heart of Gold Visualize.')}</p>
262
+ <div class="hero-row">
263
+ ${badge ? `<div class="badge">${escapeHtml(badge)}</div>` : ''}
264
+ </div>
265
+ </section>
266
+ <div class="page">
267
+ <section>${content}</section>
268
+ <aside class="rail">${nav}</aside>
269
+ </div>
270
+ </main>
271
+ </body>
272
+ </html>`;
273
+ }
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
+
281
+ function renderOutline(doc, title, eyebrow, badge) {
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>`;
285
+ const content = doc.sections.map((section, i) => {
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>`;
288
+ }).join('');
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>`;
396
+ }
397
+
398
+ function renderRoadmap(doc, title, badge) {
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'],
429
+ ];
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>`;
444
+ }).join('');
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 });
519
+ }
520
+
521
+ function renderArchitecture(doc, title, badge) {
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 });
529
+ }
530
+
531
+ function renderMindmap(filePath, outFile) {
532
+ const script = join(dirname(new URL(import.meta.url).pathname), 'render-mindmap', 'index.js');
533
+ execFileSync('node', [script, '--html', outFile, filePath], { stdio: 'ignore' });
534
+ }
535
+
536
+ function main() {
537
+ const opts = parseArgs(process.argv.slice(2));
538
+ if (!opts.file || !opts.out) {
539
+ console.error('Usage: smart-render.js <file.md> --out <file.html> [--mode auto|mindmap|outline|roadmap|architecture|mockup|explainer]');
540
+ process.exit(1);
541
+ }
542
+ const markdown = readFileSync(opts.file, 'utf8');
543
+ const doc = parseDocument(markdown);
544
+ const mode = classifyMode(opts.file, doc, opts.mode);
545
+ const title = doc.frontmatter.title || doc.sections[0]?.title || basename(opts.file);
546
+ const badge = opts.mode && opts.mode !== 'auto'
547
+ ? `Mode: ${mode}`
548
+ : 'Mode: outline (safe default — agent may override)';
549
+
550
+ if (mode === 'mindmap') {
551
+ renderMindmap(opts.file, opts.out);
552
+ return;
553
+ }
554
+
555
+ const html = mode === 'roadmap'
556
+ ? renderRoadmap(doc, title, badge)
557
+ : mode === 'architecture'
558
+ ? renderArchitecture(doc, title, badge)
559
+ : renderOutline(doc, title, 'Structured View', badge);
560
+ writeFileSync(opts.out, html, 'utf8');
561
+ }
562
+
563
+ main();
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "deep-thought",
3
- "version": "0.2.3",
3
+ "version": "0.2.5",
4
4
  "description": "The Answer Computer — reasoning tools for brainstorming, planning, architecture design, and deep thinking",
5
5
  "author": {
6
6
  "name": "ondrej-svec",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "marvin",
3
- "version": "0.3.3",
3
+ "version": "0.3.5",
4
4
  "description": "The Paranoid Android — quality tools for code review, knowledge compounding, and work execution",
5
5
  "author": {
6
6
  "name": "ondrej-svec",