viepilot 2.4.0 → 2.15.0

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.
@@ -0,0 +1,807 @@
1
+ # Workflow: vp-proposal — Proposal Package Generation
2
+
3
+ **Skill:** `/vp-proposal`
4
+ **Version:** 0.1.0
5
+ **Output:** `docs/proposals/{slug}-{date}.pptx` + `.docx` + `.md` (+ optional `-slides.txt`)
6
+
7
+ <purpose>
8
+ Convert a brainstorm session (or direct brief) into a professional proposal package suitable
9
+ for client, partner, or investor presentations. Generates three synchronized files:
10
+ - .pptx — presentation (ViePilot branded, dark navy/charcoal, for direct delivery)
11
+ - .docx — detailed supporting document (full narrative, specs, appendix)
12
+ - .md — Markdown source of truth (version-controlled, diff-friendly)
13
+ Supports 4 proposal types with configurable slide counts; optional Google Slides upload.
14
+ </purpose>
15
+
16
+ ## Implementation routing guard (ENH-021)
17
+
18
+ This workflow generates **proposal output artifacts** (`docs/proposals/`) — not ViePilot framework
19
+ shipping code. Use `/vp-request` → `/vp-evolve` → `/vp-auto` for framework feature work.
20
+
21
+ <process>
22
+
23
+ <step name="initialize">
24
+ ## Step 1: Initialize
25
+
26
+ ```bash
27
+ Read lib/proposal-generator.cjs → PROPOSAL_TYPES, resolveTemplate, detectBrainstormSession, buildLangInstruction
28
+ Read lib/viepilot-config.cjs → getProposalLang, recordProposalLang
29
+ Read package.json → verify pptxgenjs + docx dependencies present
30
+ ```
31
+
32
+ Read `~/.viepilot/config.json` → `proposal.recentLangs` via `getProposalLang()`.
33
+ If `--lang` flag provided: validate ISO 639-1 code; set `lang = flag value`.
34
+
35
+ Display startup banner:
36
+ ```
37
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
38
+ VIEPILOT ► PROPOSAL
39
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
40
+ ```
41
+
42
+ ---
43
+
44
+ </step>
45
+
46
+ <step name="context_detection">
47
+ ## Step 2: Context Detection
48
+
49
+ Priority order:
50
+ 1. `--from <file>` flag → load specified session file
51
+ 2. Auto-detect: scan `docs/brainstorm/` for `session-*.md` → sort descending → load latest
52
+ 3. No session found → standalone brief mode (go to Step 2B)
53
+
54
+ ### Step 2A: Load brainstorm session
55
+ ```
56
+ Session loaded: docs/brainstorm/session-{date}.md
57
+ Topic: {topic from session}
58
+ Key decisions: {D1, D2, D3...}
59
+ ```
60
+ Extract from session:
61
+ - Project name / topic
62
+ - Problem statement
63
+ - Key decisions and features
64
+ - Target audience (if stated)
65
+
66
+ ### Step 2B: Standalone brief (no session)
67
+ Prompt user:
68
+ ```
69
+ No brainstorm session found. Please provide a brief:
70
+
71
+ 1. Project name?
72
+ 2. One-line description?
73
+ 3. Target audience? (client / partner / investor / internal)
74
+ 4. 3–5 key points to cover?
75
+ 5. Any specific requirements or constraints?
76
+ ```
77
+
78
+ ---
79
+
80
+ </step>
81
+
82
+ <step name="type_selection">
83
+ ## Step 3: Proposal Type Selection
84
+
85
+ If `--type <id>` provided: validate against `PROPOSAL_TYPES`; confirm with user.
86
+
87
+ If not provided, present menu:
88
+ ```
89
+ Select proposal type:
90
+
91
+ 1. Project Proposal (10 slides)
92
+ Scope, timeline, budget — for client delivery
93
+
94
+ 2. Technical Architecture (12 slides)
95
+ System design, components, tech stack — for technical partners
96
+
97
+ 3. Product Pitch Deck (12 slides)
98
+ Problem/solution, market, traction — for investors/partners
99
+
100
+ 4. General Proposal ( 8 slides)
101
+ Flexible structure for any audience
102
+ ```
103
+
104
+ Output: `typeId` (string), `slideCount` (number), `label` (string)
105
+
106
+ ---
107
+
108
+ </step>
109
+
110
+ <step name="language_selection">
111
+ ## Step 3b: Language Selection
112
+
113
+ If `--lang` provided: skip prompt; use flag value directly.
114
+
115
+ If `--lang` not provided:
116
+ ```
117
+ Language for this proposal?
118
+
119
+ 1. {recentLangs[0]} (most recently used) ← only shown if recentLangs non-empty
120
+ 2. English (en)
121
+ 3. Vietnamese (vi)
122
+ 4. Other — enter ISO 639-1 code (e.g. ja, fr, zh, ko)
123
+ ```
124
+ - `recent = getProposalLang()` from config
125
+ - Present numbered menu; save selected `lang`
126
+
127
+ Output: `lang` (ISO 639-1 string), `langContentOnly` (boolean from `--lang-content-only` flag)
128
+
129
+ ---
130
+
131
+ </step>
132
+
133
+ <step name="quality_brief">
134
+ ## Step 2C: Quality Brief
135
+
136
+ Ask these 4 questions regardless of whether a session was loaded:
137
+
138
+ ```
139
+ To sharpen the proposal, please answer briefly:
140
+
141
+ 1. Decision / CTA: What should this proposal make the audience do?
142
+ (e.g. "sign the contract", "approve budget", "schedule a demo")
143
+
144
+ 2. Budget range: Approximate investment level?
145
+ (e.g. "$50K–$100K", "internal only", or skip with Enter)
146
+
147
+ 3. Timeline: Key deadline or constraint?
148
+ (e.g. "present end of month", "no fixed deadline")
149
+
150
+ 4. Decision-maker: Who is the primary audience?
151
+ (e.g. "non-technical CEO", "CTO + engineering team", "procurement committee")
152
+ ```
153
+
154
+ Store answers in manifest `meta` object:
155
+ ```json
156
+ {
157
+ "meta": {
158
+ "cta": "sign the contract",
159
+ "budget": "$50K–$100K",
160
+ "timeline": "present end of month",
161
+ "decisionMaker": "non-technical CEO"
162
+ }
163
+ }
164
+ ```
165
+
166
+ These drive sharper content in Step 4 (manifest_generation): tone, word choice, CTA on closing slide.
167
+
168
+ ---
169
+
170
+ </step>
171
+
172
+ <step name="manifest_generation">
173
+ ## Step 4: AI Slide Manifest Generation
174
+
175
+ Build language instruction and prepend to prompt:
176
+ ```js
177
+ const langInstruction = buildLangInstruction(lang, langContentOnly);
178
+ // Prepend langInstruction to the AI content generation prompt before the slide structure spec.
179
+ ```
180
+
181
+ Generate a structured JSON manifest from the loaded context.
182
+
183
+ **Manifest schema:**
184
+ ```json
185
+ {
186
+ "title": "Project Name",
187
+ "subtitle": "One compelling sentence — not just a date",
188
+ "type": "project-proposal",
189
+ "audience": "client|partner|investor|internal",
190
+ "meta": {
191
+ "cta": "sign the contract",
192
+ "budget": "$50K–$100K",
193
+ "timeline": "present end of month",
194
+ "decisionMaker": "non-technical CEO"
195
+ },
196
+ "designConfig": {
197
+ "colorPalette": "navy-electric",
198
+ "layoutStyle": "modern-tech",
199
+ "fontPair": "Calibri / Calibri Light"
200
+ },
201
+ "slides": [
202
+ {
203
+ "index": 1,
204
+ "layout": "cover",
205
+ "heading": "Title",
206
+ "body": "Subtitle / prepared by / date",
207
+ "speakerNotes": "Opening remarks"
208
+ },
209
+ {
210
+ "index": 2,
211
+ "layout": "section",
212
+ "heading": "Section Title",
213
+ "bullets": ["Point 1", "Point 2", "Point 3"],
214
+ "speakerNotes": "Talking points for this slide"
215
+ }
216
+ ]
217
+ }
218
+ ```
219
+
220
+ ## AI Prompt Contract
221
+
222
+ Inject this system context before asking AI to generate the manifest:
223
+
224
+ ```
225
+ You are generating a professional proposal slide manifest.
226
+
227
+ AUDIENCE: {meta.decisionMaker || "business decision-maker"}
228
+ CTA: {meta.cta || "approve the proposal"}
229
+ BUDGET CONTEXT: {meta.budget || "not specified"}
230
+ TIMELINE: {meta.timeline || "not specified"}
231
+ TONE: {if decisionMaker contains "technical" → "precise and data-driven"; else → "clear and persuasive"}
232
+ {langInstruction}
233
+
234
+ CONTENT RULES — apply to every slide:
235
+ - Cover subtitle: one compelling sentence that states the value proposition (NOT just a date)
236
+ - Every bullet point: 8–15 words, outcome-oriented, starts with an action verb
237
+ - Speaker notes: 3–5 sentences — (1) key talking point, (2) supporting evidence or example,
238
+ (3) anticipated objection + response, (4) transition to next slide
239
+ - Closing slide: concrete CTA matching meta.cta — NOT "Thank you" alone
240
+ - Data/metrics slide: include at least one quantified metric (estimate is fine, label as such)
241
+ - AVOID filler phrases: "In conclusion", "As you can see", "We believe", "It is important to note"
242
+ - AVOID vague bullets: "Improved performance", "Better results", "Enhanced UX"
243
+ ```
244
+
245
+ **Slide layout types:**
246
+ - `cover` — full-bleed title slide (index 1 always)
247
+ - `section` — heading + bullet list (main content slides)
248
+ - `two-column` — heading + two content columns (for comparisons)
249
+ - `data` — heading + key metric callouts
250
+ - `closing` — CTA block matching meta.cta (last slide always)
251
+
252
+ **Slide count rules (ENH-045 — dynamic):**
253
+ - Base skeleton per type defines the minimum set of slides (see structure below).
254
+ - AI may add extra slides when content warrants — there is **no hard maximum**.
255
+ - Content-aware split triggers:
256
+ - `technicalNarrative` has > 4 paragraphs → split into 2 Technical Approach slides
257
+ - `team` has > 4 members → split into 2 Team slides
258
+ - `phases` / timeline has > 4 items → split into 2 Timeline slides
259
+ - `riskRegister` has > 3 items → add a dedicated Risk slide
260
+ - Every generated manifest MUST include a `designConfig` field (see **Design Selection** below).
261
+
262
+ **Design Selection (ENH-045):**
263
+
264
+ The AI MUST choose a `designConfig` based on project context (sector, audience, tone) and include it in the manifest:
265
+
266
+ ```json
267
+ "designConfig": {
268
+ "colorPalette": "navy-electric | navy-gold | dark-vibrant",
269
+ "layoutStyle": "modern-tech | enterprise | creative",
270
+ "fontPair": "Calibri / Calibri Light | Georgia / Calibri | Calibri / Calibri"
271
+ }
272
+ ```
273
+
274
+ Selection heuristic:
275
+ - **enterprise** → finance, banking, legal, compliance, government, corporate audience
276
+ - **creative** → startup, design agency, gaming, media, art, consumer product
277
+ - **modern-tech** → default; SaaS, developer tools, technical architecture, general tech
278
+
279
+ Add this block to the AI prompt immediately after the TONE line:
280
+
281
+ ```
282
+ DESIGN SELECTION:
283
+ Choose one layoutStyle based on project context:
284
+ enterprise — if sector/audience is: finance, banking, legal, compliance, government, corporate
285
+ creative — if sector/audience is: startup, design, gaming, media, agency, consumer
286
+ modern-tech — default for SaaS, dev tools, tech architecture, general
287
+ Include "designConfig": { "colorPalette": ..., "layoutStyle": ..., "fontPair": ... } in the manifest root.
288
+ ```
289
+
290
+ **Slide structure by type:**
291
+
292
+ ### project-proposal (10 slides)
293
+ 1. Cover — project name, client name, date
294
+ 2. Agenda — what we'll cover
295
+ 3. Problem / Opportunity
296
+ 4. Proposed Solution
297
+ 5. Key Features & Deliverables
298
+ 6. Technical Approach (high level)
299
+ 7. Project Timeline
300
+ 8. Team & Expertise
301
+ 9. Investment / Budget Estimate
302
+ 10. Next Steps & CTA
303
+
304
+ ### tech-architecture (12 slides)
305
+ 1. Cover
306
+ 2. Executive Summary
307
+ 3. Problem Statement
308
+ 4. Architecture Overview (diagram description)
309
+ 5. Component Breakdown
310
+ 6. Data Flow
311
+ 7. Tech Stack
312
+ 8. Security & Compliance
313
+ 9. Scalability & Performance
314
+ 10. Integration Points
315
+ 11. Implementation Roadmap
316
+ 12. Next Steps & CTA
317
+
318
+ ### product-pitch (12 slides)
319
+ 1. Cover
320
+ 2. The Problem
321
+ 3. Our Solution
322
+ 4. Market Opportunity
323
+ 5. Product Demo / Screenshots
324
+ 6. Business Model
325
+ 7. Traction & Validation
326
+ 8. Competitive Landscape
327
+ 9. Team
328
+ 10. Roadmap
329
+ 11. Ask / Investment
330
+ 12. Thank You & Contact
331
+
332
+ ### general (8 slides)
333
+ 1. Cover
334
+ 2. Overview
335
+ 3. Problem / Context
336
+ 4. Solution / Proposal
337
+ 5. Key Benefits
338
+ 6. Approach & Timeline
339
+ 7. About Us / Credentials
340
+ 8. Next Steps & CTA
341
+
342
+ If `--dry-run`: print manifest as JSON and stop here.
343
+
344
+ ---
345
+
346
+ </step>
347
+
348
+ <step name="generate_docx_content">
349
+ ## Step 4b: AI Docx Content Generation (independent pass)
350
+
351
+ This is a **separate AI call** from the slide manifest generation.
352
+ - Input: brainstorm session + quality brief meta + proposal type + slide manifest (for context only)
353
+ - Output: `docxContent` JSON — per-section rich content + diagram specs
354
+ - This content is deeper and longer than slides; it is NOT derived from slide bullets
355
+
356
+ **Docx content AI prompt contract:**
357
+
358
+ ```
359
+ You are generating the DETAILED DOCUMENT for a professional proposal.
360
+ This document is the deep technical and narrative companion to the slides.
361
+
362
+ CONTEXT:
363
+ - Proposal type: {typeId} — {label}
364
+ - Audience: {meta.decisionMaker}
365
+ - CTA: {meta.cta}
366
+ - Budget: {meta.budget}
367
+ - Timeline: {meta.timeline}
368
+ - Brainstorm session summary: {sessionSummary or "none"}
369
+ - Slide headings (for context only): {slides.map(s => s.heading).join(', ')}
370
+ {langInstruction}
371
+
372
+ DIAGRAM TYPES to generate: {getDiagramTypes(typeId)}
373
+ project-proposal → flowchart, gantt
374
+ tech-architecture → flowchart, sequenceDiagram, classDiagram
375
+ product-pitch → flowchart, sequenceDiagram
376
+ general → flowchart
377
+
378
+ GENERATE a JSON object with this exact schema:
379
+ {
380
+ "executiveSummary": ["paragraph1 (3–6 sentences)", "paragraph2", "paragraph3"],
381
+ "problemStatement": ["paragraph1", "paragraph2"],
382
+ "solutionNarrative": ["paragraph1", "paragraph2"],
383
+ "technicalNarrative": ["paragraph1"],
384
+ "riskRegister": [
385
+ { "risk": "...", "probability": "High|Med|Low", "impact": "High|Med|Low", "mitigation": "..." }
386
+ ],
387
+ "glossary": [
388
+ { "term": "...", "definition": "plain-language definition" }
389
+ ],
390
+ "diagrams": [
391
+ {
392
+ "type": "flowchart|sequenceDiagram|classDiagram|erDiagram|gantt",
393
+ "title": "diagram title",
394
+ "mermaidSource": "valid Mermaid 10+ source code"
395
+ }
396
+ ]
397
+ }
398
+
399
+ CONTENT RULES:
400
+ - Each paragraph: 3–6 sentences, outcome-oriented, uses data from brainstorm session
401
+ - riskRegister: 3–5 rows covering the most likely project risks
402
+ - glossary: 5–10 key terms the decision-maker may not know
403
+ - diagrams: generate one diagram per type in getDiagramTypes(typeId)
404
+ - Mermaid source MUST be syntactically valid Mermaid 10+ syntax
405
+ - flowchart uses `flowchart TD` (not `graph TD`)
406
+ - gantt must include `dateFormat YYYY-MM-DD` and at least 3 sections
407
+ - AVOID placeholder text — generate real content from the brainstorm session context
408
+ ```
409
+
410
+ Store result as `docxContent` variable for use in Steps 7 and 8.
411
+
412
+ ---
413
+
414
+ </step>
415
+
416
+
417
+ <step name="detect_visual_artifacts">
418
+ ## Step 4c: Detect Visual Artifacts (MANDATORY visual pass when artifacts exist)
419
+
420
+ This step runs **after Step 4b** and **before template resolution**.
421
+
422
+ **ENH-044 enforcement rule:** When `detectVisualArtifacts()` returns non-empty results, `visualSlides[]` MUST be populated — silent skip is not allowed. If puppeteer is absent, use `addPlaceholderVisual()` and emit a WARNING.
423
+
424
+ 1. Call `detectVisualArtifacts()` from `lib/proposal-generator.cjs`:
425
+ ```js
426
+ const { detectVisualArtifacts } = require('./lib/proposal-generator.cjs');
427
+ const artifacts = detectVisualArtifacts(); // auto-detects latest .viepilot/ui-direction/ session
428
+ ```
429
+
430
+ 2. **If no artifacts found** (`artifacts.uiPages.length === 0 && artifacts.architectPages.length === 0`):
431
+ - Set `visualSlides = []`
432
+ - Log: `[visuals] No HTML artifacts found — skipping screenshot pass`
433
+ - Continue to Step 5
434
+
435
+ 3. **If artifacts are found** — `visualSlides[]` MUST be populated (never left empty):
436
+
437
+ **3a. When puppeteer is available** (`isPuppeteerAvailable()` returns true):
438
+ - Use `screenshotArtifact(htmlPath)` → `addImage()` for each mapped artifact
439
+ - `visualSlides[]` entries use real screenshots
440
+
441
+ **3b. When puppeteer is absent** (MANDATORY fallback):
442
+ ```js
443
+ const { warnMissingTool, isPuppeteerAvailable } = require('./lib/screenshot-artifact.cjs');
444
+ if (!isPuppeteerAvailable()) {
445
+ warnMissingTool('puppeteer', 'npm install puppeteer');
446
+ // Use addPlaceholderVisual(slide, label) for each mapped artifact — do NOT skip
447
+ }
448
+ ```
449
+ - Call `addPlaceholderVisual(slide, label)` for each entry that would have had a screenshot
450
+ - `visualSlides[]` is still populated — placeholder entries count as valid visual slides
451
+ - Do NOT leave `visualSlides[]` empty when artifacts exist
452
+
453
+ **Produce `visualSlides[]`** by mapping slide topics to artifact files using AI judgment:
454
+
455
+ **Artifact → slide mapping rules:**
456
+ | Slide topic keywords | Artifact to use | artifactType |
457
+ |----------------------|-----------------|--------------|
458
+ | UI, interface, mockup, design, screens | `artifacts.uiPages[0]` (index.html) | `ui-overview` |
459
+ | Specific UI page (login, dashboard…) | matching `artifacts.uiPages[N]` | `ui-page` |
460
+ | Architecture, system design, components | `architecture.html` in architectPages | `architect-arch` |
461
+ | Database, entities, ERD, data model | `erd.html` in architectPages | `architect-erd` |
462
+ | Flow, sequence, interactions, API calls | `sequence-diagram.html` in architectPages | `architect-seq` |
463
+ | Features, feature map | `feature-map.html` in architectPages | `architect-features` |
464
+ | Users, roles, use cases, actors | `user-use-cases.html` in architectPages | `architect-usecases` |
465
+
466
+ 4. `visualSlides[]` schema — one entry per slide that should receive a visual:
467
+ ```json
468
+ [
469
+ {
470
+ "slideIndex": 3,
471
+ "artifactType": "ui-overview",
472
+ "htmlPath": "/absolute/path/to/index.html",
473
+ "label": "UI Prototype Overview"
474
+ },
475
+ {
476
+ "slideIndex": 7,
477
+ "artifactType": "architect-arch",
478
+ "htmlPath": "/absolute/path/to/architecture.html",
479
+ "label": "System Architecture"
480
+ }
481
+ ]
482
+ ```
483
+ - `slideIndex`: 0-based index in the slides array
484
+ - Maximum **1 visual per slide**
485
+ - **Only assign visuals to content slides** — do NOT assign to cover (slide 0), section dividers, or closing/CTA slides
486
+ - Maximum 3–4 visual slides per proposal to avoid visual overload
487
+
488
+ 5. Store as `visualSlides` variable for use in Step 6 (`generate_pptx`).
489
+
490
+ Progress: `[visuals] Found {N} artifact(s) → {M} slides will have screenshots`
491
+
492
+ ---
493
+
494
+ </step>
495
+
496
+ <step name="template_resolution">
497
+ ## Step 5: Template Resolution
498
+
499
+ ```js
500
+ const pptxTemplate = resolveTemplate(typeId, 'pptx', projectRoot);
501
+ const docxTemplate = resolveTemplate('project-detail', 'docx', projectRoot);
502
+ ```
503
+
504
+ - If project override found: `[override] Using .viepilot/proposal-templates/{type}.pptx`
505
+ - If stock fallback: `[stock] Using ViePilot template for {type}`
506
+ - If stock file missing (not yet generated): warn user and continue with programmatic fallback
507
+
508
+ Ensure output directory:
509
+ ```bash
510
+ mkdir -p docs/proposals/
511
+ ```
512
+
513
+ ---
514
+
515
+ </step>
516
+
517
+ <step name="generate_pptx">
518
+ ## Step 6: Generate .pptx
519
+
520
+ Using `pptxgenjs`:
521
+ 1. Create new `pptxgen` instance
522
+ 2. Define Slide Master from template (dark navy `#1a1f36` background, accent `#4f6ef7`)
523
+ 3. For each slide in manifest:
524
+ - Apply layout-appropriate template slide
525
+ - Inject `heading`, `bullets` / `body`, `speakerNotes`
526
+ 4. Write to `docs/proposals/{slug}-{date}.pptx`
527
+
528
+ Progress: `[pptx] Generating {N} slides...`
529
+
530
+ ---
531
+
532
+ </step>
533
+
534
+ <step name="generate_docx">
535
+ ## Step 7: Generate .docx
536
+
537
+ Using `docx` package, consuming **`docxContent`** (from Step 4b) for all narrative paragraphs:
538
+
539
+ 1. Build `Document` with structured sections
540
+ 2. **Cover page**: title (H1, large), subtitle, prepared-for, date
541
+ 3. **Body sections** — sourced from `docxContent`:
542
+
543
+ | Section | Source | Content |
544
+ |---------|--------|---------|
545
+ | Executive Summary | `docxContent.executiveSummary[]` | Paragraphs rendered as Word body text |
546
+ | Problem & Opportunity | `docxContent.problemStatement[]` | Paragraphs rendered as Word body text |
547
+ | Proposed Solution | `docxContent.solutionNarrative[]` | Paragraphs rendered as Word body text |
548
+ | Technical Approach | `docxContent.technicalNarrative[]` | Paragraph(s); omit section if array is empty |
549
+ | **Project Timeline** | slide manifest `meta` | **Gantt-style table: Phase \| Milestone \| Duration \| Dependencies** |
550
+ | **Investment Estimate** | slide manifest `meta` | **Budget table: Line Item \| Estimate \| Notes** (use meta.budget as header context) |
551
+ | Team & Expertise | slide manifest | Role \| Experience \| Responsibility table |
552
+ | **Risk Register** | `docxContent.riskRegister[]` | **Risk \| Probability \| Impact \| Mitigation table** |
553
+ | **Diagram Reference** | `docxContent.diagrams[]` | **Per diagram: render `mermaidSource` → PNG via `renderMermaidToPng()` → `ImageRun`; fallback: preformatted Mermaid source text when mmdc absent** |
554
+ | Why Choose Us | slide manifest | 2–3 differentiator bullets |
555
+ | Next Steps | slide manifest `meta.cta` | Numbered action list |
556
+ | **Glossary** | `docxContent.glossary[]` | **Term \| Definition table** |
557
+ | Appendix | session file | Full brainstorm session notes (if session loaded) |
558
+
559
+ 4. Timeline table structure:
560
+ ```
561
+ | Phase | Milestone | Duration | Dependencies |
562
+ |-------|-----------|----------|--------------|
563
+ | 1 | Discovery | 2 weeks | — |
564
+ | 2 | Design | 3 weeks | Phase 1 |
565
+ ```
566
+
567
+ 5. Budget table structure:
568
+ ```
569
+ | Line Item | Estimate | Notes |
570
+ |------------------|------------|--------------------|
571
+ | Discovery & UX | $X,000 | Fixed fee |
572
+ | Development | $XX,000 | Time & materials |
573
+ | Testing & QA | $X,000 | Included |
574
+ | Total | $XX,000 | {meta.budget} |
575
+ ```
576
+
577
+ 6. Risk Register table structure (from `docxContent.riskRegister[]`):
578
+ ```
579
+ | Risk | Probability | Impact | Mitigation |
580
+ |------|-------------|--------|------------|
581
+ | {r.risk} | {r.probability} | {r.impact} | {r.mitigation} |
582
+ ```
583
+
584
+ 7. Diagram Reference structure (one block per diagram in `docxContent.diagrams[]`):
585
+
586
+ **MANDATORY (ENH-044):** This section MUST be added when `docxContent.diagrams.length > 0` — do not skip.
587
+
588
+ - Heading: `{diagram.title} ({diagram.type})`
589
+ - **If `isMmdcAvailable()`**: render `mermaidSource` → PNG via `renderMermaidToPng()` → embed as `ImageRun` in a Paragraph
590
+ - **Fallback** (mmdc absent): emit WARNING + embed preformatted text:
591
+ ```js
592
+ const { warnMissingTool, isMmdcAvailable } = require('./lib/screenshot-artifact.cjs');
593
+ if (!isMmdcAvailable()) {
594
+ warnMissingTool('mmdc', 'npm install -g @mermaid-js/mermaid-cli');
595
+ // Add monospace paragraph with preformatted mermaidSource text (NOT skip)
596
+ }
597
+ ```
598
+ - Cleanup temp PNG immediately after embedding (`cleanupScreenshot()`)
599
+
600
+ 8. Glossary table structure (from `docxContent.glossary[]`):
601
+ ```
602
+ | Term | Definition |
603
+ |------|------------|
604
+ | {g.term} | {g.definition} |
605
+ ```
606
+
607
+ 9. **Visual embedding** (ENH-043/ENH-044 — MANDATORY when artifacts exist):
608
+
609
+ **MANDATORY (ENH-044):** When `detectVisualArtifacts()` returns non-empty, visual sections MUST be added to the document — do not skip silently. If puppeteer is absent, emit WARNING and add a placeholder text paragraph instead.
610
+
611
+ **Mermaid diagram images** — for each `docxContent.diagrams[]` entry:
612
+ ```js
613
+ const { renderMermaidToPng, isMmdcAvailable, warnMissingTool, cleanupScreenshot } = require('./lib/screenshot-artifact.cjs');
614
+ const tmpPng = path.join(os.tmpdir(), `vp-mmdc-${Date.now()}.png`);
615
+ const rendered = renderMermaidToPng(diagram.mermaidSource, tmpPng);
616
+ if (rendered) {
617
+ const imgRun = imageRunFromPng(rendered);
618
+ if (imgRun) children.push(new Paragraph({ children: [imgRun] }));
619
+ cleanupScreenshot(rendered);
620
+ }
621
+ // Fallback: preformatted mermaid source text already added (step 7, always present)
622
+ ```
623
+
624
+ **UI prototype screenshot** — MANDATORY when `artifacts.uiPages.length > 0`:
625
+ ```js
626
+ const { screenshotArtifact, isPuppeteerAvailable, warnMissingTool, cleanupScreenshot } = require('./lib/screenshot-artifact.cjs');
627
+ const artifacts = detectVisualArtifacts();
628
+ if (artifacts.uiPages.length > 0) {
629
+ if (isPuppeteerAvailable()) {
630
+ const tmpPng = await screenshotArtifact(artifacts.uiPages[0]);
631
+ if (tmpPng) {
632
+ const imgRun = imageRunFromPng(tmpPng);
633
+ if (imgRun) executiveSummaryChildren.unshift(
634
+ new Paragraph({ children: [imgRun], spacing: { before: 120, after: 200 } })
635
+ );
636
+ cleanupScreenshot(tmpPng);
637
+ }
638
+ } else {
639
+ warnMissingTool('puppeteer', 'npm install puppeteer');
640
+ // Add placeholder paragraph — do NOT skip the section
641
+ executiveSummaryChildren.unshift(
642
+ new Paragraph({ text: '[UI Prototype screenshot — install puppeteer to embed]', style: 'Caption' })
643
+ );
644
+ }
645
+ }
646
+ ```
647
+
648
+ **Architecture screenshot** — MANDATORY when `architecture.html` in `artifacts.architectPages`:
649
+ ```js
650
+ const archHtml = artifacts.architectPages.find(p => p.endsWith('architecture.html'));
651
+ if (archHtml) {
652
+ if (isPuppeteerAvailable()) {
653
+ const tmpPng = await screenshotArtifact(archHtml);
654
+ if (tmpPng) {
655
+ const imgRun = imageRunFromPng(tmpPng);
656
+ if (imgRun) techSectionChildren.push(
657
+ new Paragraph({ children: [imgRun], spacing: { before: 200, after: 120 } })
658
+ );
659
+ cleanupScreenshot(tmpPng);
660
+ }
661
+ } else {
662
+ // warnMissingTool already called above (once per run — not repeated per artifact)
663
+ techSectionChildren.push(
664
+ new Paragraph({ text: '[Architecture diagram screenshot — install puppeteer to embed]', style: 'Caption' })
665
+ );
666
+ }
667
+ }
668
+ ```
669
+
670
+ Rule: `warnMissingTool()` is called **once per missing tool per generation run** — not per-diagram or per-artifact.
671
+
672
+ 10. Write to `docs/proposals/{slug}-{date}.docx`
673
+
674
+ Progress: `[docx] Building detailed document...`
675
+
676
+ ---
677
+
678
+ </step>
679
+
680
+ <step name="generate_md">
681
+ ## Step 8: Generate .md Summary
682
+
683
+ Write a Markdown document combining the slide manifest and `docxContent` diagrams:
684
+
685
+ ```markdown
686
+ # {title}
687
+
688
+ **Type:** {label}
689
+ **Date:** {date}
690
+ **Audience:** {audience}
691
+
692
+ ---
693
+
694
+ ## Slides
695
+
696
+ ### Slide {N}: {heading}
697
+
698
+ {bullets as - list}
699
+
700
+ > Speaker notes: {speakerNotes}
701
+
702
+ ---
703
+
704
+ ## Diagrams
705
+
706
+ For each entry in `docxContent.diagrams[]`, emit a fenced Mermaid code block:
707
+
708
+ ### {diagram.title}
709
+
710
+ ```{diagram.type}
711
+ {diagram.mermaidSource}
712
+ ```
713
+
714
+ _(repeat for each diagram)_
715
+ ```
716
+
717
+ - Mermaid fences use the diagram type as the language identifier (e.g. ` ```flowchart `, ` ```sequenceDiagram `)
718
+ - Diagrams section is omitted if `docxContent.diagrams` is empty or unavailable
719
+
720
+ Write to `docs/proposals/{slug}-{date}.md`
721
+
722
+ ---
723
+
724
+ </step>
725
+
726
+ <step name="google_slides_upload">
727
+ ## Step 9: Optional Google Slides Upload (`--slides`)
728
+
729
+ ```js
730
+ const { uploadToSlides } = require('../lib/google-slides-exporter.cjs');
731
+ try {
732
+ const url = await uploadToSlides(pptxPath, title);
733
+ fs.writeFileSync(slidesUrlPath, url);
734
+ console.log(`[slides] ${url}`);
735
+ } catch (err) {
736
+ console.warn(`[slides] Upload failed: ${err.message}`);
737
+ // Files already written — non-fatal
738
+ }
739
+ ```
740
+
741
+ Prerequisites (shown if auth fails):
742
+ - `npm install @googleapis/slides`
743
+ - Create service account → download JSON key
744
+ - `export GOOGLE_APPLICATION_CREDENTIALS=/path/to/key.json`
745
+
746
+ ---
747
+
748
+ </step>
749
+
750
+ <step name="confirm_output">
751
+ ## Step 10: Confirm Output
752
+
753
+ ```
754
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
755
+ VIEPILOT ► PROPOSAL GENERATED ✓
756
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
757
+ Type: {label} ({N} slides)
758
+ Context: {session file or "direct brief"}
759
+
760
+ Files:
761
+ docs/proposals/{slug}-{date}.md ({size})
762
+ docs/proposals/{slug}-{date}.pptx ({size})
763
+ docs/proposals/{slug}-{date}.docx ({size})
764
+ [docs/proposals/{slug}-{date}-slides.txt]
765
+
766
+ Next:
767
+ Open .pptx in PowerPoint / Keynote / LibreOffice Impress
768
+ Share .docx as supporting document
769
+ /vp-proposal --slides to upload to Google Slides
770
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
771
+ ```
772
+
773
+ Update MRU config:
774
+ ```js
775
+ recordProposalLang(lang) // → update ~/.viepilot/config.json proposal.recentLangs
776
+ ```
777
+
778
+ ---
779
+
780
+ ## Error Handling
781
+
782
+ | Situation | Action |
783
+ |-----------|--------|
784
+ | Unknown `--type` | Show valid types list; ask user to select |
785
+ | `--from` file not found | Show path; ask to continue with auto-detect |
786
+ | Stock template missing (not yet generated) | Warn; use pptxgenjs programmatic fallback |
787
+ | `@googleapis/slides` not installed | Clear install instructions; continue without upload |
788
+ | `GOOGLE_APPLICATION_CREDENTIALS` not set | Clear setup guide; continue without upload |
789
+ | `docs/proposals/` write error | Show path and permission error; stop |
790
+
791
+ ---
792
+
793
+ </step>
794
+
795
+ </process>
796
+
797
+ <success_criteria>
798
+ - [ ] Context loaded (brainstorm session or direct brief)
799
+ - [ ] Type validated; slide count ≥ base skeleton for type (dynamic — no hard maximum)
800
+ - [ ] Manifest generated with correct structure; `designConfig` field present
801
+ - [ ] `.pptx` written to `docs/proposals/`
802
+ - [ ] `.docx` written to `docs/proposals/`
803
+ - [ ] `.md` written to `docs/proposals/`
804
+ - [ ] Template resolution: project override takes precedence over stock
805
+ - [ ] `--slides`: Google Slides URL in `-slides.txt` or clear non-fatal error shown
806
+ - [ ] `--dry-run`: manifest printed to console; no files written
807
+ </success_criteria>