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,142 @@
1
+ 'use strict';
2
+ /**
3
+ * screenshot-artifact.cjs
4
+ * ENH-042 + ENH-043: Optional screenshot/render utility for vp-proposal visual embedding.
5
+ *
6
+ * - puppeteer (optional): screenshots HTML artifact files → PNG
7
+ * - mmdc CLI (optional): renders Mermaid source → PNG
8
+ * Both return null gracefully when the tool is absent — no crash, no hard dep.
9
+ *
10
+ * Usage:
11
+ * const { screenshotArtifact, isPuppeteerAvailable,
12
+ * isMmdcAvailable, renderMermaidToPng, cleanupScreenshot }
13
+ * = require('./lib/screenshot-artifact.cjs');
14
+ */
15
+
16
+ const os = require('os');
17
+ const path = require('path');
18
+ const fs = require('fs');
19
+ const cp = require('child_process');
20
+
21
+ /**
22
+ * Check if puppeteer is available without throwing.
23
+ * @returns {boolean}
24
+ */
25
+ function isPuppeteerAvailable() {
26
+ try { require.resolve('puppeteer'); return true; } catch { return false; }
27
+ }
28
+
29
+ /**
30
+ * Screenshot an HTML file to a temporary PNG using puppeteer (headless Chrome).
31
+ * Returns null silently when puppeteer is not installed.
32
+ *
33
+ * @param {string} htmlPath - Absolute path to the HTML file to screenshot
34
+ * @param {object} [opts]
35
+ * @param {number} [opts.width=1280] - Viewport width in px
36
+ * @param {number} [opts.height=720] - Viewport height in px (matches 16:9 slide ratio)
37
+ * @returns {Promise<string|null>} Absolute path to a temp PNG file, or null if unavailable
38
+ */
39
+ async function screenshotArtifact(htmlPath, opts = {}) {
40
+ // Graceful: return null if puppeteer not installed
41
+ let puppeteer;
42
+ try { puppeteer = require('puppeteer'); } catch { return null; }
43
+
44
+ if (!htmlPath || !fs.existsSync(htmlPath)) return null;
45
+
46
+ const { width = 1280, height = 720 } = opts;
47
+ const tmpFile = path.join(os.tmpdir(), `vp-artifact-${Date.now()}.png`);
48
+
49
+ const browser = await puppeteer.launch({
50
+ headless: 'new', // puppeteer ≥ 20 compat
51
+ args: ['--no-sandbox', '--disable-setuid-sandbox'], // CI-safe
52
+ });
53
+ try {
54
+ const page = await browser.newPage();
55
+ await page.setViewport({ width, height });
56
+ await page.goto(`file://${htmlPath}`, { waitUntil: 'networkidle0', timeout: 15000 });
57
+ await page.screenshot({ path: tmpFile, fullPage: false });
58
+ return tmpFile;
59
+ } finally {
60
+ await browser.close();
61
+ }
62
+ }
63
+
64
+ /**
65
+ * Clean up a temp screenshot file after embedding.
66
+ * @param {string|null} tmpPath
67
+ */
68
+ function cleanupScreenshot(tmpPath) {
69
+ if (tmpPath && fs.existsSync(tmpPath)) {
70
+ try { fs.unlinkSync(tmpPath); } catch { /* ignore cleanup errors */ }
71
+ }
72
+ }
73
+
74
+ // ── Mermaid rendering (ENH-043) ───────────────────────────────────────────────
75
+
76
+ /**
77
+ * Check if @mermaid-js/mermaid-cli (mmdc) is available on PATH.
78
+ * @returns {boolean}
79
+ */
80
+ function isMmdcAvailable() {
81
+ try {
82
+ const r = cp.spawnSync('mmdc', ['--version'], { encoding: 'utf8', timeout: 5000 });
83
+ return r.status === 0;
84
+ } catch {
85
+ return false;
86
+ }
87
+ }
88
+
89
+ /**
90
+ * Render a Mermaid diagram source string to a PNG file using the mmdc CLI.
91
+ * Returns null silently when mmdc is not available or rendering fails.
92
+ *
93
+ * @param {string} mermaidSource - Valid Mermaid 10+ source code
94
+ * @param {string} outputPath - Absolute path for the output .png file
95
+ * @returns {string|null} outputPath on success, null if mmdc absent or error
96
+ */
97
+ function renderMermaidToPng(mermaidSource, outputPath) {
98
+ if (!isMmdcAvailable()) return null;
99
+ if (!mermaidSource || !mermaidSource.trim()) return null;
100
+ if (!outputPath) return null;
101
+
102
+ const tmpInput = outputPath.replace(/\.png$/i, '.mmd');
103
+ try {
104
+ fs.writeFileSync(tmpInput, mermaidSource, 'utf8');
105
+ const r = cp.spawnSync(
106
+ 'mmdc',
107
+ ['-i', tmpInput, '-o', outputPath, '-b', 'white'],
108
+ { encoding: 'utf8', timeout: 30000 }
109
+ );
110
+ return (r.status === 0 && fs.existsSync(outputPath)) ? outputPath : null;
111
+ } catch {
112
+ return null;
113
+ } finally {
114
+ try { if (fs.existsSync(tmpInput)) fs.unlinkSync(tmpInput); } catch { /* ignore */ }
115
+ }
116
+ }
117
+
118
+ // ── Missing-tool warning (ENH-044) ───────────────────────────────────────────
119
+
120
+ /**
121
+ * Emit a standardized warning to stderr when a visual rendering tool is absent
122
+ * but visual artifacts exist and embedding is mandatory.
123
+ *
124
+ * @param {string} tool - Tool name (e.g. 'puppeteer', 'mmdc')
125
+ * @param {string} installCmd - Install hint (e.g. 'npm install puppeteer')
126
+ */
127
+ function warnMissingTool(tool, installCmd) {
128
+ process.stderr.write(
129
+ `\n[vp-proposal] ⚠ Visual artifacts found but '${tool}' is not installed.\n` +
130
+ ` Install to enable screenshots: ${installCmd}\n` +
131
+ ` Using placeholder/text fallback instead.\n\n`
132
+ );
133
+ }
134
+
135
+ module.exports = {
136
+ screenshotArtifact,
137
+ isPuppeteerAvailable,
138
+ cleanupScreenshot,
139
+ isMmdcAvailable,
140
+ renderMermaidToPng,
141
+ warnMissingTool,
142
+ };
@@ -13,12 +13,16 @@ const fs = require('fs');
13
13
  const path = require('path');
14
14
  const os = require('os');
15
15
 
16
- /** @type {{ language: { communication: string, document: string } }} */
16
+ /** @type {{ language: { communication: string, document: string }, proposal: { recentLangs: string[], defaultLang: string } }} */
17
17
  const DEFAULTS = {
18
18
  language: {
19
19
  communication: 'en',
20
20
  document: 'en',
21
21
  },
22
+ proposal: {
23
+ recentLangs: [], // MRU list, most recent first, max 5
24
+ defaultLang: 'en', // kept in sync with recentLangs[0]
25
+ },
22
26
  };
23
27
 
24
28
  /**
@@ -94,10 +98,37 @@ function resetConfig(overrideHomedir) {
94
98
  fs.writeFileSync(configPath, JSON.stringify(DEFAULTS, null, 2) + '\n', 'utf8');
95
99
  }
96
100
 
101
+ /**
102
+ * Get the suggested default language for proposals.
103
+ * Returns recentLangs[0] if present, else 'en'.
104
+ * @param {string | undefined} overrideHomedir
105
+ * @returns {string}
106
+ */
107
+ function getProposalLang(overrideHomedir) {
108
+ const cfg = readConfig(overrideHomedir);
109
+ const recent = cfg.proposal && cfg.proposal.recentLangs;
110
+ return (Array.isArray(recent) && recent.length > 0) ? recent[0] : 'en';
111
+ }
112
+
113
+ /**
114
+ * Record a used language: prepend to recentLangs, dedup, cap at 5, sync defaultLang.
115
+ * @param {string} lang - ISO 639-1 code
116
+ * @param {string | undefined} overrideHomedir
117
+ */
118
+ function recordProposalLang(lang, overrideHomedir) {
119
+ const cfg = readConfig(overrideHomedir);
120
+ const existing = (cfg.proposal && Array.isArray(cfg.proposal.recentLangs))
121
+ ? cfg.proposal.recentLangs : [];
122
+ const updated = [lang, ...existing.filter(l => l !== lang)].slice(0, 5);
123
+ writeConfig({ proposal: { recentLangs: updated, defaultLang: updated[0] } }, overrideHomedir);
124
+ }
125
+
97
126
  module.exports = {
98
127
  DEFAULTS,
99
128
  getConfigPath,
100
129
  readConfig,
101
130
  writeConfig,
102
131
  resetConfig,
132
+ getProposalLang,
133
+ recordProposalLang,
103
134
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "viepilot",
3
- "version": "2.4.0",
3
+ "version": "2.15.0",
4
4
  "description": "**Autonomous Vibe Coding Framework / Bộ khung phát triển tự động có kiểm soát**",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -72,6 +72,13 @@
72
72
  }
73
73
  }
74
74
  },
75
+ "dependencies": {
76
+ "docx": "^9.0.0",
77
+ "pptxgenjs": "^3.12.0"
78
+ },
79
+ "optionalDependencies": {
80
+ "@googleapis/slides": "^1.0.0"
81
+ },
75
82
  "devDependencies": {
76
83
  "jest": "^30.3.0"
77
84
  }
@@ -34,6 +34,17 @@ Audit ViePilot project state and documentation to detect drift.
34
34
  Works on **any project** using ViePilot (Java, Node, Python, etc.).
35
35
  Auto-detects if running inside the viepilot framework repo to enable framework-specific checks.
36
36
 
37
+ **Brownfield Import Compatibility (FEAT-018):**
38
+
39
+ When auditing a project bootstrapped via `vp-crystallize --brownfield`:
40
+
41
+ - If `docs/brainstorm/` exists and contains **only** `session-brownfield-import.md` (no `session-*.md` greenfield files):
42
+ - **Valid brownfield import** — do NOT flag as missing brainstorm session.
43
+ - Verify `session-brownfield-import.md` contains a `## Scan Report` section with a YAML block.
44
+ - If YAML block absent → flag LOW severity: "Brownfield stub missing Scan Report content."
45
+ - If `.viepilot/TRACKER.md` contains `## Brownfield Import` section → brownfield metadata confirmed; no further brainstorm check needed.
46
+ - If `docs/brainstorm/` is completely absent → flag MEDIUM severity: "No brainstorm session or brownfield stub found — run `/vp-crystallize` or `/vp-crystallize --brownfield`."
47
+
37
48
  **Tier 1 — ViePilot State Consistency (all projects):**
38
49
  - `.viepilot/TRACKER.md` current state vs `.viepilot/phases/*/PHASE-STATE.md`
39
50
  - `.viepilot/ROADMAP.md` phase status vs PHASE-STATE.md
@@ -107,3 +107,24 @@ Key steps:
107
107
  - [ ] **FEAT-009**: intake completed, binding already present, **or** waiver with reason before Completed; session records **`## Project meta intake (FEAT-009)`**
108
108
  - [ ] Next steps suggested
109
109
  </success_criteria>
110
+
111
+ ## Adapter Compatibility
112
+
113
+ ### AskUserQuestion Tool (ENH-048)
114
+ This skill uses adapter-aware interactive prompts. Behavior depends on your adapter:
115
+
116
+ | Adapter | Interactive Prompts | Notes |
117
+ |---------|---------------------|-------|
118
+ | Claude Code (terminal) | ✅ `AskUserQuestion` tool | Click-to-select UI, multi-select, preview panels |
119
+ | Claude Code (VS Code ext) | ⚠️ Partial | Terminal yes; VS Code UI pending [anthropics/claude-code#12609](https://github.com/anthropics/claude-code/issues/12609) |
120
+ | Cursor (Plan Mode) | ⚠️ Partial | `AskQuestion` in Plan Mode only — not in Agent/Skills Mode |
121
+ | Cursor (Agent/Skills) | ❌ Text fallback | AskQuestion not available in Agent Mode |
122
+ | Codex CLI | ❌ Text fallback | Native tool N/A; community MCP available |
123
+ | Antigravity (native agent) | ❌ Text fallback | Artifact model, no raw tool calls |
124
+
125
+ When `AskUserQuestion` is not available, the skill automatically falls back to
126
+ plain-text numbered list prompts — no configuration required.
127
+
128
+ **Prompts using AskUserQuestion in this skill:**
129
+ - Session intent (continue / review / new — Step 2)
130
+ - Landing page layout selection (Step 4 — Layout A/B/C/D)
@@ -77,6 +77,46 @@ Convert brainstorm sessions into structured artifacts for autonomous AI executio
77
77
  - `DOCUMENT_LANG` controls content language for all generated files (ROADMAP, TRACKER, ARCHITECTURE, etc.).
78
78
  - `COMMUNICATION_LANG` controls prompt/confirmation language for this session.
79
79
  - Configure via: `vp-tools config set language.document vi`
80
+
81
+ **Brownfield Mode (`--brownfield`) — FEAT-018:**
82
+
83
+ Use when adopting ViePilot on an **existing project** (no brainstorm session required).
84
+
85
+ Flags:
86
+ - `--brownfield` : Explicit brownfield mode
87
+ - *(auto-detected)* : Triggers when `docs/brainstorm/` is absent/empty AND `.viepilot/` does not exist
88
+
89
+ Scanner runs 12 signal categories across the existing codebase:
90
+ 1. **Build manifests** — `package.json`, `pom.xml`, `pyproject.toml`, `Cargo.toml`, `go.mod`, etc. (11 platforms) → infers project_name, version, language, deps
91
+ 2. **Framework detection** — 40+ dependency patterns → backend/frontend/ORM/auth/broker/test frameworks
92
+ 3. **Architecture layers** — 18 directory patterns → controller/service/repository/frontend/infra/etc.
93
+ 4. **Database schema signals** — Flyway/Liquibase/Prisma/Rails migrations + docker-compose services
94
+ 5. **API contracts** — OpenAPI, gRPC `.proto`, GraphQL schemas
95
+ 6. **Infrastructure** — Dockerfile, docker-compose, k8s, Terraform, Vercel, Fly.io, etc. (16 patterns)
96
+ 7. **Environment config** — `.env.example` key names (never reads `.env`)
97
+ 8. **Test coverage** — Jest/pytest/JUnit/Cypress config + coverage report dirs
98
+ 9. **Code quality tools** — ESLint/Prettier/SonarQube/pre-commit/golangci-lint/etc. (14 patterns)
99
+ 10. **Documentation** — README, CHANGELOG, ADRs, docs/ (priority-ordered)
100
+ 11. **Git history** — commit convention, version pattern, contributors, repo URL
101
+ 12. **Language survey** — file extension glob → language distribution
102
+
103
+ **Multi-repo / monorepo support (ENH-047):**
104
+
105
+ - **Git submodule detection** — reads `.gitmodules`; scans each initialized submodule path (Signal Cat 1+2+4); records uninitialized paths as `primary_language: MISSING`. Never runs `git submodule update` — read-only.
106
+ - **Polyrepo hints** — detects docker-compose `../` build contexts, `file:../` deps, CI cross-repo clones, README external links, Makefile `cd ../` targets; outputs `polyrepo_hints[]`; prompts user to supply `related_repos[]` (optional).
107
+ - **Per-module gap detection** — every `modules[]` entry carries `gap_tier` (DETECTED/ASSUMED/MISSING), `must_detect_status{}` (evidence per field: value + source + tier), and `open_questions[]`. A module with `gap_tier: MISSING` blocks artifact generation with a targeted per-field prompt.
108
+
109
+ **Scan Report contains:**
110
+ - Root `gap_tier` (= worst tier across all modules: MISSING > ASSUMED > DETECTED)
111
+ - `modules[]` — one entry per workspace/submodule/root with `gap_tier`, `must_detect_status{}`, `open_questions[]`
112
+ - `polyrepo_hints[]` — polyrepo signals (omitted when empty, no empty arrays)
113
+ - `related_repos[]` — user-supplied sibling repos (omitted when empty)
114
+ - Root `open_questions[]` — includes rollup from all modules
115
+
116
+ Produces **Scan Report** (YAML) with DETECTED / ASSUMED / MISSING classification.
117
+ MUST-DETECT gaps (root: project_name, primary_language, ≥1 framework, current_version; per-module: primary_language, framework, module_purpose, entry_point) block artifact generation until user fills interactively.
118
+ Generates `docs/brainstorm/session-brownfield-import.md` stub for `vp-audit` compatibility.
119
+ Safety: never reads `.env`; skips `node_modules/`, `.git/`, `target/`, `build/`, `dist/`.
80
120
  </objective>
81
121
 
82
122
  <execution_context>
@@ -223,3 +263,27 @@ Ask user for (confirm proposals from profile if present):
223
263
  - [ ] **ENH-022:** Every generated Mermaid diagram has a `.viepilot/architecture/<canonical-name>.mermaid` file in sync with `ARCHITECTURE.md` (no extra files created for N/A)
224
264
  - [ ] **FEAT-009:** When profile is bound — ARCHITECTURE + PROJECT-CONTEXT record the profile source; if not — state none / not configured explicitly
225
265
  </success_criteria>
266
+
267
+ ## Adapter Compatibility
268
+
269
+ ### AskUserQuestion Tool (ENH-048)
270
+ This skill uses adapter-aware interactive prompts. Behavior depends on your adapter:
271
+
272
+ | Adapter | Interactive Prompts | Notes |
273
+ |---------|---------------------|-------|
274
+ | Claude Code (terminal) | ✅ `AskUserQuestion` tool | Click-to-select UI, multi-select, preview panels |
275
+ | Claude Code (VS Code ext) | ⚠️ Partial | Terminal yes; VS Code UI pending [anthropics/claude-code#12609](https://github.com/anthropics/claude-code/issues/12609) |
276
+ | Cursor (Plan Mode) | ⚠️ Partial | `AskQuestion` in Plan Mode only — not in Agent/Skills Mode |
277
+ | Cursor (Agent/Skills) | ❌ Text fallback | AskQuestion not available in Agent Mode |
278
+ | Codex CLI | ❌ Text fallback | Native tool N/A; community MCP available |
279
+ | Antigravity (native agent) | ❌ Text fallback | Artifact model, no raw tool calls |
280
+
281
+ When `AskUserQuestion` is not available, the skill automatically falls back to
282
+ plain-text numbered list prompts — no configuration required.
283
+
284
+ **Prompts using AskUserQuestion in this skill:**
285
+ - License selection (Step 0 metadata)
286
+ - Brownfield overwrite confirmation (Step 0-B)
287
+ - Polyrepo related-repos prompt (Step 0-B)
288
+ - UI direction gate choice (Step 1A)
289
+ - Architect mode suggestion (Step 1D)
@@ -80,19 +80,29 @@ Optional flags:
80
80
 
81
81
  ### Step 0: Resolve Project Context (ALWAYS first)
82
82
  ```bash
83
- # Read actual GitHub URL — never use hardcoded placeholder
83
+ # Forge-agnostic remote URL parser supports GitHub, GitLab, Bitbucket, Azure DevOps, Gitea, self-hosted
84
84
  REMOTE_URL=$(git remote get-url origin 2>/dev/null || echo "")
85
- GITHUB_SLUG=$(echo "$REMOTE_URL" | sed 's|.*github\.com[:/]||; s|\.git$||')
86
- GITHUB_OWNER=$(echo "$GITHUB_SLUG" | cut -d'/' -f1)
87
- GITHUB_REPO=$(echo "$GITHUB_SLUG" | cut -d'/' -f2)
85
+ if echo "$REMOTE_URL" | grep -q 'dev\.azure\.com'; then
86
+ GIT_HOST="dev.azure.com"
87
+ GIT_OWNER=$(echo "$REMOTE_URL" | sed 's|.*dev\.azure\.com/||; s|/.*||')
88
+ GIT_REPO=$(echo "$REMOTE_URL" | sed 's|.*/_git/||; s|\.git$||; s|/$||')
89
+ elif echo "$REMOTE_URL" | grep -q '^git@'; then
90
+ GIT_HOST=$(echo "$REMOTE_URL" | sed 's|^git@||; s|:.*||')
91
+ GIT_OWNER=$(echo "$REMOTE_URL" | sed 's|^git@[^:]*:||; s|/.*||')
92
+ GIT_REPO=$(echo "$REMOTE_URL" | sed 's|^git@[^:]*:[^/]*/||; s|\.git$||')
93
+ else
94
+ GIT_HOST=$(echo "$REMOTE_URL" | sed 's|^https\?://||; s|/.*||')
95
+ GIT_OWNER=$(echo "$REMOTE_URL" | sed 's|^https\?://[^/]*/||; s|/.*||')
96
+ GIT_REPO=$(echo "$REMOTE_URL" | sed 's|^https\?://[^/]*/[^/]*/||; s|\.git$||; s|/$||')
97
+ fi
88
98
  # Fallback: use searchable placeholder, not 'your-org'
89
- [ -z "$GITHUB_OWNER" ] && GITHUB_OWNER="{GITHUB_OWNER}" && GITHUB_REPO="{GITHUB_REPO}"
99
+ [ -z "$GIT_OWNER" ] && GIT_HOST="{GIT_HOST}" && GIT_OWNER="{GIT_OWNER}" && GIT_REPO="{GIT_REPO}"
90
100
 
91
101
  ACTUAL_SKILLS=$(ls skills/*/SKILL.md 2>/dev/null | wc -l | tr -d ' ')
92
102
  ACTUAL_WORKFLOWS=$(ls workflows/*.md 2>/dev/null | wc -l | tr -d ' ')
93
103
  ```
94
104
 
95
- > Use `$GITHUB_OWNER/$GITHUB_REPO` in all generated files.
105
+ > Use `$GIT_HOST`, `$GIT_OWNER`, `$GIT_REPO` in all generated files.
96
106
  > Use `$ACTUAL_SKILLS` and `$ACTUAL_WORKFLOWS` for counts.
97
107
  > **Never write** `your-org`, `YOUR_USERNAME`, `YOUR_ORG` into generated files.
98
108
 
@@ -0,0 +1,175 @@
1
+ ---
2
+ name: vp-proposal
3
+ description: "Generate professional proposal packages (.pptx + .docx + .md) from brainstorm sessions or direct briefs"
4
+ version: 0.1.0
5
+ ---
6
+
7
+ <cursor_skill_adapter>
8
+ ## A. Skill Invocation
9
+ - Skill được gọi khi user mention `vp-proposal`, `/vp-proposal`, "proposal", "pitch deck", "presentation", "tài liệu đề xuất"
10
+ - Treat all user text after the skill mention as `{{VP_ARGS}}`
11
+
12
+ ## B. User Prompting
13
+ Prompt user conversationally with numbered list options.
14
+
15
+ ## C. Tool Usage
16
+ Use Cursor tools: `Shell`, `ReadFile`, `Glob`, `rg`, `ApplyPatch`, `WebSearch`, `WebFetch`, `Subagent`
17
+ </cursor_skill_adapter>
18
+ <scope_policy>
19
+ ## ViePilot Namespace Guard (BUG-004)
20
+ - Default mode: only use and reference `vp-*` skills in ViePilot workflows.
21
+ - External skills (`non vp-*`) are out of framework scope unless user explicitly opts in.
22
+ </scope_policy>
23
+ <implementation_routing_guard>
24
+ ## Implementation routing guard (ENH-021)
25
+
26
+ - **`/vp-proposal`** generates **output artifacts** (`docs/proposals/*.pptx`, `*.docx`, `*.md`) — it does **not** implement shipping code for `lib/`, `tests/`, `bin/`, or framework `workflows/`/`skills/`.
27
+ - This skill is the **delivery lane** after `/vp-brainstorm` captures the ideas. Use `/vp-request` → `/vp-evolve` → `/vp-auto` for ViePilot framework feature work.
28
+ - **Exception:** User **explicit** bypass — state clearly in chat.
29
+ </implementation_routing_guard>
30
+
31
+ <objective>
32
+ Generate a professional proposal package from a brainstorm session or direct brief.
33
+
34
+ **Output files** (written to `docs/proposals/`):
35
+ - `{slug}-{date}.md` — structured proposal Markdown (source of truth)
36
+ - `{slug}-{date}.pptx` — presentation file (ViePilot branded, dark navy/charcoal)
37
+ - `{slug}-{date}.docx` — detailed project document
38
+ - `{slug}-{date}-slides.txt` — Google Slides URL (only with `--slides` flag)
39
+
40
+ **Proposal types:**
41
+ | Type | Slides | Use case |
42
+ |------|--------|----------|
43
+ | `project-proposal` | 10 | Scope, timeline, budget for clients |
44
+ | `tech-architecture` | 12 | Technical design for partners |
45
+ | `product-pitch` | 12 | Investor / partner pitch deck |
46
+ | `general` | 8 | Generic, fallback |
47
+
48
+ **Template resolution (2-tier):**
49
+ 1. `.viepilot/proposal-templates/{type}.pptx` — project-level override
50
+ 2. `{viepilot-install}/templates/proposal/pptx/{type}.pptx` — ViePilot stock
51
+
52
+ **Context detection:**
53
+ - Auto-loads latest `docs/brainstorm/session-*.md` when present
54
+ - Standalone mode if no session found (user provides brief)
55
+ - `--from session-YYYY-MM-DD.md` for explicit session selection
56
+ </objective>
57
+
58
+ <execution_context>
59
+ workflows/proposal.md
60
+ </execution_context>
61
+
62
+ <context>
63
+ Optional flags:
64
+ - `--type <id>` : Proposal type — `project-proposal` | `tech-architecture` | `product-pitch` | `general`
65
+ If omitted: guided selection menu is shown
66
+ - `--from <file>` : Explicit brainstorm session file to use as context
67
+ Default: auto-detect latest `docs/brainstorm/session-*.md`
68
+ - `--lang <code>` : Language for generated content — ISO 639-1 (e.g. vi, en, ja, fr, zh).
69
+ If omitted: prompted with MRU suggestions from config.
70
+ Saved to ~/.viepilot/config.json → proposal.recentLangs after generation.
71
+ - `--lang-content-only` : Translate content (bullets, notes, paragraphs) only.
72
+ Keep structural labels / section names in English.
73
+ - `--slides` : After .pptx is generated, upload to Google Slides via service account auth
74
+ Requires: `@googleapis/slides` installed + `GOOGLE_APPLICATION_CREDENTIALS` env var
75
+ - `--dry-run` : Show slide manifest (JSON) without writing any files
76
+ </context>
77
+
78
+ <process>
79
+ Execute workflow from `workflows/proposal.md`
80
+
81
+ ### Step 1: Context Detection
82
+ - Scan `docs/brainstorm/` for `session-*.md` → sort descending → load latest
83
+ - If `--from` specified: load that file directly
84
+ - If no session found: prompt user for brief:
85
+ - Project name
86
+ - One-line description
87
+ - Target audience (client / partner / investor)
88
+ - 3–5 key points to cover
89
+
90
+ ### Step 2: Proposal Type Selection
91
+ - If `--type` provided: validate; show type + slide count to confirm
92
+ - Else: present menu:
93
+ ```
94
+ 1. Project Proposal (10 slides) — scope, timeline, budget
95
+ 2. Tech Architecture (12 slides) — system design for partners
96
+ 3. Product Pitch Deck (12 slides) — investor / partner pitch
97
+ 4. General Proposal ( 8 slides) — generic fallback
98
+ ```
99
+
100
+ ### Step 3: AI Slide Manifest Generation
101
+ - Structure context into JSON manifest:
102
+ ```json
103
+ {
104
+ "title": "...",
105
+ "subtitle": "...",
106
+ "slides": [
107
+ { "index": 1, "layout": "cover", "heading": "...", "body": "...", "speakerNotes": "..." },
108
+ { "index": 2, "layout": "section", "heading": "...", "bullets": ["..."], "speakerNotes": "..." }
109
+ ]
110
+ }
111
+ ```
112
+ - Slide count MUST match `PROPOSAL_TYPES[typeId].slides`
113
+ - If `--dry-run`: print manifest and stop
114
+
115
+ ### Step 4: Template Resolution
116
+ - Call `resolveTemplate(typeId, 'pptx', projectRoot)` → pptx template path
117
+ - Call `resolveTemplate('project-detail', 'docx', projectRoot)` → docx template path
118
+ - Warn (not error) if stock fallback is used
119
+
120
+ ### Step 5: Generate .pptx
121
+ - Load template via pptxgenjs
122
+ - Apply slide manifest: inject titles, bullets, speaker notes per slide
123
+ - Ensure `docs/proposals/` directory exists
124
+ - Write `{slug}-{date}.pptx`
125
+
126
+ ### Step 6: Generate .docx
127
+ - Build detailed document from same manifest + extended content
128
+ - Sections: Executive Summary, Problem & Solution, Features & Specifications,
129
+ Technical Architecture (if relevant), Timeline, Team, Appendix
130
+ - Write `{slug}-{date}.docx`
131
+
132
+ ### Step 7: Generate .md summary
133
+ - Write Markdown mirror of the manifest
134
+ - Save `{slug}-{date}.md`
135
+
136
+ ### Step 8: Optional Google Slides upload (`--slides`)
137
+ - Call `lib/google-slides-exporter.cjs` → `uploadToSlides(pptxPath, title)`
138
+ - Write URL to `{slug}-{date}-slides.txt`
139
+ - On failure: surface error but do NOT fail the whole command (files already written)
140
+
141
+ ### Step 9: Confirm output
142
+ ```
143
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
144
+ VIEPILOT ► PROPOSAL GENERATED ✓
145
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
146
+ Type: {type label} ({N} slides)
147
+ Context: {session file or "direct brief"}
148
+
149
+ Files:
150
+ docs/proposals/{slug}-{date}.md
151
+ docs/proposals/{slug}-{date}.pptx
152
+ docs/proposals/{slug}-{date}.docx
153
+ [docs/proposals/{slug}-{date}-slides.txt] ← if --slides
154
+
155
+ Next:
156
+ Open .pptx in PowerPoint / Keynote / LibreOffice
157
+ Share .docx as supporting document
158
+ Run /vp-proposal --slides to upload to Google Slides
159
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
160
+ ```
161
+ </process>
162
+
163
+ <success_criteria>
164
+ - [ ] Context detected (brainstorm session or user brief)
165
+ - [ ] Proposal type selected and validated
166
+ - [ ] Slide manifest generated with correct slide count
167
+ - [ ] .pptx written to docs/proposals/
168
+ - [ ] .docx written to docs/proposals/
169
+ - [ ] .md summary written to docs/proposals/
170
+ - [ ] Template resolution: project override takes precedence over stock
171
+ - [ ] --lang: content generated in specified language; MRU saved to config
172
+ - [ ] --lang-content-only: bullets/notes translated; structural labels stay English
173
+ - [ ] --slides: Google Slides URL written to -slides.txt (or clear error shown)
174
+ - [ ] --dry-run: manifest printed, no files written
175
+ </success_criteria>
@@ -265,3 +265,25 @@ Update `.viepilot/TRACKER.md`:
265
265
  - [ ] TRACKER.md updated
266
266
  - [ ] Appropriate routing suggested
267
267
  </success_criteria>
268
+
269
+ ## Adapter Compatibility
270
+
271
+ ### AskUserQuestion Tool (ENH-048)
272
+ This skill uses adapter-aware interactive prompts. Behavior depends on your adapter:
273
+
274
+ | Adapter | Interactive Prompts | Notes |
275
+ |---------|---------------------|-------|
276
+ | Claude Code (terminal) | ✅ `AskUserQuestion` tool | Click-to-select UI, multi-select, preview panels |
277
+ | Claude Code (VS Code ext) | ⚠️ Partial | Terminal yes; VS Code UI pending [anthropics/claude-code#12609](https://github.com/anthropics/claude-code/issues/12609) |
278
+ | Cursor (Plan Mode) | ⚠️ Partial | `AskQuestion` in Plan Mode only — not in Agent/Skills Mode |
279
+ | Cursor (Agent/Skills) | ❌ Text fallback | AskQuestion not available in Agent Mode |
280
+ | Codex CLI | ❌ Text fallback | Native tool N/A; community MCP available |
281
+ | Antigravity (native agent) | ❌ Text fallback | Artifact model, no raw tool calls |
282
+
283
+ When `AskUserQuestion` is not available, the skill automatically falls back to
284
+ plain-text numbered list prompts — no configuration required.
285
+
286
+ **Prompts using AskUserQuestion in this skill:**
287
+ - Request type detection (Bug / Feature / Enhancement / Tech Debt — Step 2)
288
+ - Bug severity selection (Critical / High / Medium / Low — Step 4A)
289
+ - Feature priority selection (Must-have / Should-have / Nice-to-have — Step 4B)
@@ -3,6 +3,14 @@ Interactive brainstorm session to gather ideas, requirements, and decisions for
3
3
  Allows research inline within the same brainstorm session when needed.
4
4
  </purpose>
5
5
 
6
+ ## Adapter Compatibility
7
+
8
+ | Feature | Claude Code (terminal) | Cursor (Agent/Skills) | Codex CLI | Antigravity (native) |
9
+ |---------|----------------------|-----------------------|-----------|----------------------|
10
+ | Interactive prompts | ✅ `AskUserQuestion` tool | ❌ text fallback | ❌ text fallback | ❌ text fallback |
11
+
12
+ When `AskUserQuestion` is not available, each prompt block falls back to the plain-text numbered list shown below it — no configuration needed.
13
+
6
14
  ## ViePilot Skill Scope Policy (BUG-004)
7
15
 
8
16
  - Default behavior: only use and suggest skills under `vp-*`.
@@ -37,6 +45,15 @@ Parse results to get list of existing sessions.
37
45
  ## 2. Ask User Intent
38
46
 
39
47
  **If previous sessions exist:**
48
+
49
+ > **Adapter-aware prompt:**
50
+ > - **Claude Code (terminal):** use `AskUserQuestion` tool — spec:
51
+ > - question: "Previous brainstorm sessions found. What would you like to do?"
52
+ > - header: "Session"
53
+ > - options: [{ label: "Continue recent session", description: "Resume the most recent session from where it stopped" }, { label: "Review specific session", description: "Choose a particular session to review or continue" }, { label: "New brainstorm session", description: "Start fresh — previous sessions are preserved" }]
54
+ > - multiSelect: false
55
+ > - **Cursor / Codex / Antigravity / other:** use text menu below
56
+
40
57
  ```
41
58
  I found previous brainstorm sessions:
42
59
  {list sessions with dates}
@@ -118,6 +135,15 @@ If the user is brainstorming a landing page / homepage / marketing page:
118
135
  - Visual tone? (minimal, modern, bold, enterprise, playful)
119
136
  - Primary CTA and secondary CTA?
120
137
  2. Present a layout menu for the user to choose from:
138
+
139
+ > **Adapter-aware prompt:**
140
+ > - **Claude Code (terminal):** use `AskUserQuestion` tool — spec:
141
+ > - question: "Which landing page layout fits your goals and audience?"
142
+ > - header: "Layout"
143
+ > - options: [{ label: "Layout A — Hero centric", description: "Hero + trust logos + features + CTA — best for brand awareness and conversions" }, { label: "Layout B — Problem/Solution", description: "Problem/Solution + social proof + pricing + FAQ — best for SaaS sign-ups" }, { label: "Layout C — Product storytelling", description: "Screenshots + testimonials + final CTA — best for product demos" }, { label: "Layout D — SaaS conversion", description: "Integrations + comparison + onboarding steps — best for tool adoption" }]
144
+ > - multiSelect: false
145
+ > - **Cursor / Codex / Antigravity / other:** use list below
146
+
121
147
  - Layout A: Hero centric + trust logos + features + CTA
122
148
  - Layout B: Problem/Solution + social proof + pricing + FAQ
123
149
  - Layout C: Product storytelling + screenshots + testimonials + final CTA