viepilot 2.50.1 → 3.7.3

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.
Files changed (49) hide show
  1. package/CHANGELOG.md +214 -0
  2. package/README.md +17 -17
  3. package/bin/viepilot.cjs +1 -0
  4. package/bin/vp-tools.cjs +123 -1
  5. package/docs/brainstorm/session-2026-05-22.md +472 -0
  6. package/docs/dev/agents.md +51 -41
  7. package/docs/dev/architecture.md +26 -0
  8. package/docs/skills-reference.md +96 -0
  9. package/lib/adapter-context.cjs +294 -0
  10. package/lib/adapters/antigravity.cjs +8 -2
  11. package/lib/adapters/claude-code.cjs +4 -0
  12. package/lib/audit/browser-runner.cjs +102 -0
  13. package/lib/intake/adapters/browser.cjs +58 -0
  14. package/lib/intake/adapters/excel-m365.cjs +54 -6
  15. package/lib/intake/auto-intake.cjs +194 -0
  16. package/lib/intake/classifier.cjs +22 -4
  17. package/lib/intake/manifest.cjs +81 -0
  18. package/lib/intake/triage-ux.cjs +10 -2
  19. package/lib/intake/validator.cjs +97 -0
  20. package/lib/intake/writeback.cjs +169 -3
  21. package/lib/request/url-enricher.cjs +69 -0
  22. package/lib/viepilot-install.cjs +15 -0
  23. package/package.json +1 -1
  24. package/skills/vp-audit/SKILL.md +99 -3
  25. package/skills/vp-auto/SKILL.md +54 -4
  26. package/skills/vp-brainstorm/SKILL.md +69 -3
  27. package/skills/vp-crystallize/SKILL.md +52 -3
  28. package/skills/vp-debug/SKILL.md +52 -3
  29. package/skills/vp-design/SKILL.md +52 -3
  30. package/skills/vp-docs/SKILL.md +52 -3
  31. package/skills/vp-evolve/SKILL.md +52 -3
  32. package/skills/vp-info/SKILL.md +52 -3
  33. package/skills/vp-intake/SKILL.md +306 -7
  34. package/skills/vp-pause/SKILL.md +52 -3
  35. package/skills/vp-persona/SKILL.md +52 -3
  36. package/skills/vp-proposal/SKILL.md +52 -3
  37. package/skills/vp-request/SKILL.md +72 -3
  38. package/skills/vp-resume/SKILL.md +52 -3
  39. package/skills/vp-rollback/SKILL.md +52 -3
  40. package/skills/vp-skills/SKILL.md +52 -3
  41. package/skills/vp-status/SKILL.md +52 -3
  42. package/skills/vp-task/SKILL.md +52 -3
  43. package/skills/vp-ui-components/SKILL.md +52 -3
  44. package/skills/vp-update/SKILL.md +52 -3
  45. package/workflows/autonomous.md +268 -18
  46. package/workflows/brainstorm.md +222 -7
  47. package/workflows/crystallize.md +124 -6
  48. package/workflows/design.md +62 -1
  49. package/workflows/request.md +54 -8
@@ -104,7 +104,7 @@ async function writebackCsv(channel, triageResult, projectRoot) {
104
104
  function httpsRequest(method, url, body, headers) {
105
105
  return new Promise((resolve, reject) => {
106
106
  const urlObj = new URL(url);
107
- const payload = body ? JSON.stringify(body) : '';
107
+ const payload = body ? (typeof body === 'string' ? body : JSON.stringify(body)) : '';
108
108
  const req = https.request({
109
109
  hostname: urlObj.hostname,
110
110
  path: urlObj.pathname + urlObj.search,
@@ -201,6 +201,94 @@ async function writebackGoogleSheets(channel, triageResult, projectRoot) {
201
201
  }
202
202
  }
203
203
 
204
+ // ---------------------------------------------------------------------------
205
+ // Excel M365 write-back (Graph API PATCH)
206
+ // ---------------------------------------------------------------------------
207
+
208
+ async function writebackExcelM365(channel, triageResult, projectRoot) {
209
+ if (channel.sharing_url) {
210
+ return {
211
+ success: false,
212
+ error: `Channel "${channel.id || channel.name}" uses a sharing_url — write-back is read-only for sharing links.\n` +
213
+ `To enable write-back: configure workbook_id + .viepilot/.credentials/m365-credentials.json`,
214
+ };
215
+ }
216
+
217
+ const root = projectRoot || process.cwd();
218
+ const credPath = path.join(root, '.viepilot', '.credentials', 'm365-credentials.json');
219
+ if (!fs.existsSync(credPath)) {
220
+ return {
221
+ success: false,
222
+ error: `m365-credentials.json not found at ${credPath}.\n` +
223
+ `Create it with: { "tenant_id": "...", "client_id": "...", "client_secret": "..." }`,
224
+ };
225
+ }
226
+
227
+ let creds;
228
+ try {
229
+ creds = JSON.parse(fs.readFileSync(credPath, 'utf8'));
230
+ } catch (e) {
231
+ return { success: false, error: `Failed to parse m365-credentials.json: ${e.message}` };
232
+ }
233
+
234
+ try {
235
+ const tokenUrl = `https://login.microsoftonline.com/${creds.tenant_id}/oauth2/v2.0/token`;
236
+ const tokenBody = [
237
+ `grant_type=client_credentials`,
238
+ `client_id=${encodeURIComponent(creds.client_id)}`,
239
+ `client_secret=${encodeURIComponent(creds.client_secret)}`,
240
+ `scope=https%3A%2F%2Fgraph.microsoft.com%2F.default`,
241
+ ].join('&');
242
+
243
+ const tokenRes = await httpsRequest('POST', tokenUrl, tokenBody, {
244
+ 'Content-Type': 'application/x-www-form-urlencoded',
245
+ 'Content-Length': Buffer.byteLength(tokenBody),
246
+ });
247
+
248
+ if (!tokenRes.access_token) {
249
+ return { success: false, error: `M365 authentication failed: ${JSON.stringify(tokenRes)}` };
250
+ }
251
+
252
+ const token = tokenRes.access_token;
253
+ const workbookId = channel.workbook_id;
254
+ const sheetName = encodeURIComponent(channel.sheet_name || 'Sheet1');
255
+ const colMap = channel.column_map || {};
256
+ const lastColIdx = Object.values(colMap).length > 0
257
+ ? Math.max(...Object.values(colMap).map(colLetterToIndex))
258
+ : 5;
259
+ const vpStatusCol = indexToColLetter(lastColIdx + 1);
260
+ const vpCommentCol = indexToColLetter(lastColIdx + 2);
261
+ const vpReqCol = indexToColLetter(lastColIdx + 3);
262
+
263
+ const decisions = buildTicketDecisions(triageResult);
264
+ const allTickets = [
265
+ ...triageResult.accepted.map((a) => a.ticket),
266
+ ...triageResult.declined.map((d) => d.ticket),
267
+ ...(triageResult.unclear || []).map((u) => u.ticket),
268
+ ];
269
+
270
+ let updatedCount = 0;
271
+ for (const ticket of allTickets) {
272
+ const d = decisions[ticket._source_row];
273
+ if (!d) continue;
274
+ const rowNum = ticket._source_row + 1;
275
+ const rangeAddress = `${vpStatusCol}${rowNum}:${vpReqCol}${rowNum}`;
276
+ const url = `https://graph.microsoft.com/v1.0/me/drive/items/${workbookId}/workbook/worksheets/${sheetName}/range(address='${rangeAddress}')`;
277
+ const res = await httpsRequest('PATCH', url, { values: [[d.vp_status, d.vp_comment, d.vp_request_id]] }, {
278
+ Authorization: `Bearer ${token}`,
279
+ });
280
+ if (res.error) {
281
+ return { success: false, error: `Graph API PATCH error: ${res.error.message || JSON.stringify(res.error)}` };
282
+ }
283
+ updatedCount++;
284
+ }
285
+
286
+ return { success: true, updated: updatedCount };
287
+ } catch (e) {
288
+ return { success: false, error: e.message };
289
+ }
290
+ }
291
+
204
292
  // ---------------------------------------------------------------------------
205
293
  // Main writeback dispatcher
206
294
  // ---------------------------------------------------------------------------
@@ -214,7 +302,7 @@ async function writeback(channel, triageResult, projectRoot) {
214
302
  } else if (channel.type === 'google_sheets') {
215
303
  result = await writebackGoogleSheets(channel, triageResult, root);
216
304
  } else if (channel.type === 'excel_m365') {
217
- result = { success: false, error: 'Excel/M365 write-back requires Graph API — see task 123.4' };
305
+ result = await writebackExcelM365(channel, triageResult, root);
218
306
  } else {
219
307
  result = { success: false, error: `Unknown channel type: ${channel.type}` };
220
308
  }
@@ -225,4 +313,82 @@ async function writeback(channel, triageResult, projectRoot) {
225
313
  return result;
226
314
  }
227
315
 
228
- module.exports = { writeback };
316
+ // ---------------------------------------------------------------------------
317
+ // Post-task intake write-back (ENH-095)
318
+ // ---------------------------------------------------------------------------
319
+
320
+ /**
321
+ * Write task completion status back to the original source row.
322
+ * Called by vp-auto post-PASS hook when a task has an ## Intake Source block.
323
+ *
324
+ * @param {object} channel - Original intake channel config (type, sharing_url or workbook_id)
325
+ * @param {number} sourceRow - 0-based row index (ticket._source_row)
326
+ * @param {object} response - { status, phaseTask, version, date }
327
+ * @param {string} projectRoot
328
+ * @param {string} [sheetName] - Sheet name from manifest
329
+ * @param {string} [responseCol] - Column letter from manifest write_back.response_col
330
+ * @returns {Promise<{success: boolean, cell?: string, text?: string, error?: string}>}
331
+ */
332
+ async function writebackIntakeResponse(channel, sourceRow, response, projectRoot, sheetName, responseCol) {
333
+ const text = [
334
+ response.status || 'Done',
335
+ response.phaseTask || '',
336
+ response.version || '',
337
+ response.date || new Date().toISOString().slice(0, 10),
338
+ ].filter(Boolean).join(' | ');
339
+
340
+ if (!channel || !channel.type) {
341
+ process.stderr.write(`[vp-intake] Write-back skipped: no channel info\n`);
342
+ return { success: false, error: 'no channel info' };
343
+ }
344
+
345
+ if (channel.type === 'excel_m365' && channel.sharing_url) {
346
+ process.stderr.write(`[vp-intake] Write-back skipped for sharing_url channel (read-only). Text: ${text}\n`);
347
+ return { success: false, error: 'sharing_url is read-only — use workbook_id for write-back' };
348
+ }
349
+
350
+ if (channel.type === 'excel_m365' && channel.workbook_id) {
351
+ const root = projectRoot || process.cwd();
352
+ const credPath = path.join(root, '.viepilot', '.credentials', 'm365-credentials.json');
353
+ if (!fs.existsSync(credPath)) {
354
+ process.stderr.write(`[vp-intake] Write-back skipped: m365-credentials.json not found\n`);
355
+ return { success: false, error: 'm365-credentials.json not found' };
356
+ }
357
+ try {
358
+ const creds = JSON.parse(fs.readFileSync(credPath, 'utf8'));
359
+ const tokenUrl = `https://login.microsoftonline.com/${creds.tenant_id}/oauth2/v2.0/token`;
360
+ const tokenBody = [
361
+ 'grant_type=client_credentials',
362
+ `client_id=${encodeURIComponent(creds.client_id)}`,
363
+ `client_secret=${encodeURIComponent(creds.client_secret)}`,
364
+ 'scope=https%3A%2F%2Fgraph.microsoft.com%2F.default',
365
+ ].join('&');
366
+ const tokenRes = await httpsRequest('POST', tokenUrl, tokenBody, {
367
+ 'Content-Type': 'application/x-www-form-urlencoded',
368
+ 'Content-Length': Buffer.byteLength(tokenBody),
369
+ });
370
+ if (!tokenRes.access_token) {
371
+ return { success: false, error: `M365 auth failed: ${JSON.stringify(tokenRes)}` };
372
+ }
373
+ const col = responseCol || 'G';
374
+ const sheet = encodeURIComponent(sheetName || channel.sheet_name || 'Sheet1');
375
+ const cell = `${col}${sourceRow + 2}`;
376
+ const url = `https://graph.microsoft.com/v1.0/me/drive/items/${channel.workbook_id}/workbook/worksheets/${sheet}/range(address='${cell}')`;
377
+ const res = await httpsRequest('PATCH', url, { values: [[text]] }, {
378
+ Authorization: `Bearer ${tokenRes.access_token}`,
379
+ });
380
+ if (res.error) {
381
+ return { success: false, error: `Graph API error: ${res.error.message || JSON.stringify(res.error)}` };
382
+ }
383
+ return { success: true, cell, text };
384
+ } catch (e) {
385
+ return { success: false, error: e.message };
386
+ }
387
+ }
388
+
389
+ // Google Sheets, CSV, browser, unknown: skip silently
390
+ process.stderr.write(`[vp-intake] Write-back skipped for channel type "${channel.type}" (not writable or not yet implemented)\n`);
391
+ return { success: false, error: `channel type "${channel.type}" does not support post-task write-back` };
392
+ }
393
+
394
+ module.exports = { writeback, writebackIntakeResponse };
@@ -0,0 +1,69 @@
1
+ 'use strict';
2
+
3
+ const URL_ROUTES = {
4
+ github: /github\.com\/[^/]+\/[^/]+\/issues\/\d+/,
5
+ linear: /linear\.app\/[^/]+\/issue\/[A-Z]+-\d+/,
6
+ jira: /atlassian\.net\/browse\/[A-Z]+-\d+/,
7
+ trello: /trello\.com\/c\/[a-zA-Z0-9]+/,
8
+ notion: /notion\.so\/[a-f0-9-]{8,}/,
9
+ };
10
+
11
+ /**
12
+ * Detect whether input string contains a known issue-tracker URL.
13
+ * @param {string} input - raw user input (may contain URL anywhere)
14
+ * @returns {{ matched: boolean, source?: string, url?: string }}
15
+ */
16
+ function detectIssueUrl(input) {
17
+ if (!input || typeof input !== 'string') return { matched: false };
18
+ // Extract first https?:// URL from input
19
+ const urlMatch = input.match(/https?:\/\/[^\s]+/);
20
+ if (!urlMatch) return { matched: false };
21
+ const url = urlMatch[0].replace(/[.,!?)]+$/, ''); // strip trailing punctuation
22
+ for (const [source, pattern] of Object.entries(URL_ROUTES)) {
23
+ if (pattern.test(url)) return { matched: true, source, url };
24
+ }
25
+ return { matched: false };
26
+ }
27
+
28
+ /**
29
+ * Enrich a request with details extracted from an issue URL via browser-intake-agent.
30
+ * Returns null (never throws) when extraction fails or agent is unavailable.
31
+ * CC adapter only — dispatched by vp-request SKILL.md / workflows/request.md.
32
+ * @param {string} url
33
+ * @param {string} source - detected source type
34
+ * @param {string} projectRoot
35
+ * @returns {Promise<{title,type,description,priority,labels}|null>}
36
+ */
37
+ async function enrichFromUrl(url, source, projectRoot) {
38
+ try {
39
+ // CC adapter: Agent dispatch is handled by request.md (orchestrator layer).
40
+ // Direct callers (non-CC) receive null — enrichment silently skipped.
41
+ return null;
42
+ } catch {
43
+ return null;
44
+ }
45
+ }
46
+
47
+ /**
48
+ * Format pre-fill display for user confirmation.
49
+ * @param {object} enriched - { title, type, description, priority, labels }
50
+ * @param {string} url
51
+ * @returns {string} formatted display string
52
+ */
53
+ function formatPreFillDisplay(enriched, url) {
54
+ if (!enriched) return '';
55
+ const lines = [
56
+ `Context extracted from: ${url}`,
57
+ ``,
58
+ ` Title: ${enriched.title || '(not detected)'}`,
59
+ ` Type: ${enriched.type || '(not detected)'}`,
60
+ ` Description: ${String(enriched.description || '').slice(0, 120)}${(enriched.description || '').length > 120 ? '...' : ''}`,
61
+ ` Priority: ${enriched.priority || '(not detected)'}`,
62
+ ];
63
+ if (enriched.labels && enriched.labels.length > 0) {
64
+ lines.push(` Labels: ${enriched.labels.join(', ')}`);
65
+ }
66
+ return lines.join('\n');
67
+ }
68
+
69
+ module.exports = { detectIssueUrl, enrichFromUrl, formatPreFillDisplay, URL_ROUTES };
@@ -203,6 +203,21 @@ function buildInstallPlan(packageRoot, envSource = process.env, opts = {}) {
203
203
  steps.push(ent.isDirectory() ? { kind: 'copy_dir', from: src, to: dest } : { kind: 'copy_file', from: src, to: dest });
204
204
  }
205
205
  }
206
+ // BUG-027: copy adapter-native agent definitions to adapter's native agents dir
207
+ // e.g. agents/claude-code/ → ~/.claude/agents/ (not ~/.claude/viepilot/agents/claude-code/)
208
+ if (adapter.claudeAgentsSrc && adapter.claudeAgentsDir) {
209
+ const agentsSrcDir = path.join(root, adapter.claudeAgentsSrc);
210
+ const agentsDestDir = adapter.claudeAgentsDir(home);
211
+ if (fs.existsSync(agentsSrcDir)) {
212
+ steps.push({ kind: 'mkdir', path: agentsDestDir });
213
+ for (const ent of fs.readdirSync(agentsSrcDir, { withFileTypes: true })) {
214
+ const src = path.join(agentsSrcDir, ent.name);
215
+ const dest = path.join(agentsDestDir, ent.name);
216
+ steps.push(ent.isDirectory() ? { kind: 'copy_dir', from: src, to: dest } : { kind: 'copy_file', from: src, to: dest });
217
+ }
218
+ }
219
+ }
220
+
206
221
  // BUG-007: copy package.json so resolveViepilotPackageRoot() finds the root
207
222
  steps.push({ kind: 'copy_file', from: path.join(root, 'package.json'), to: path.join(vpDir, 'package.json') });
208
223
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "viepilot",
3
- "version": "2.50.1",
3
+ "version": "3.7.3",
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": {
@@ -53,7 +53,7 @@ Silent if command unavailable or errors.
53
53
  </persona_context>
54
54
 
55
55
 
56
- <cursor_skill_adapter>
56
+ <adapter id="claude-code">
57
57
  ## A. Skill Invocation
58
58
  - Skill được gọi khi user mention `vp-audit`, `/vp-audit`, "audit", "kiểm tra", "check docs"
59
59
  - Treat all user text after the skill mention as `{{VP_ARGS}}`
@@ -62,8 +62,57 @@ Silent if command unavailable or errors.
62
62
  Display audit results clearly with actionable suggestions grouped by tier.
63
63
 
64
64
  ## C. Tool Usage
65
- Use Cursor tools: `Shell`, `ReadFile`, `Glob`, `rg`, `ApplyPatch`, `WebSearch`, `WebFetch`, `Subagent`
66
- </cursor_skill_adapter>
65
+ Use Claude Code tools: `Bash` (shell), `Read` (file), `Edit` + `Write` (file write/patch),
66
+ `Grep` (search), `Glob` (file patterns), `LS`, `WebSearch`, `WebFetch`,
67
+ `Agent` (spawn subagent — multi-level nesting supported)
68
+ Interactive: `AskUserQuestion` (deferred — preload via ToolSearch before first call)
69
+ </adapter>
70
+
71
+ <adapter id="cursor-agent">
72
+ ## A. Skill Invocation
73
+ Same trigger keywords as claude-code adapter.
74
+
75
+ ## C. Tool Usage
76
+ Use Cursor tools: `run_terminal_cmd` (shell), `read_file` (read), `edit_file` (write/edit),
77
+ `grep_search` (search), `web_search`, `codebase_search`, `list_dir`, `file_search`
78
+ Interactive: text list fallback (AskQuestion available in Plan Mode only; Agent Mode = text)
79
+ Subagent: `/multitask` (user command, single-level only — not a callable tool)
80
+ MCP limit: 40 tools
81
+ </adapter>
82
+
83
+ <adapter id="antigravity">
84
+ ## A. Skill Invocation
85
+ Same trigger keywords as claude-code adapter.
86
+ Skill discovery: LLM-driven (automatic, no slash command needed).
87
+
88
+ ## C. Tool Usage
89
+ Use Antigravity tools: `shell` (cmd), `file_read`, `file_write`, MCP plugins
90
+ Interactive: text fallback (TUI-based; no formal AskUserQuestion)
91
+ Skill path: `.agents/skills/<skill>/SKILL.md` (project) or `~/.gemini/antigravity/skills/` (global)
92
+ Note: Gemini CLI deprecated June 18, 2026 — use Antigravity CLI.
93
+ </adapter>
94
+
95
+ <adapter id="codex">
96
+ ## A. Skill Invocation
97
+ Same trigger keywords as claude-code adapter.
98
+
99
+ ## C. Tool Usage
100
+ Use Codex tools: `container.exec` (sandboxed shell), `apply_patch` (file write), `web_search`
101
+ Interactive: text fallback (TUI Tab/Enter injection)
102
+ Config: `~/.codex/config.toml`
103
+ </adapter>
104
+
105
+ <adapter id="copilot">
106
+ ## A. Skill Invocation
107
+ Same trigger keywords as claude-code adapter.
108
+ Discovery: User-driven (`@agent-name` in GitHub Copilot Chat).
109
+
110
+ ## C. Tool Usage
111
+ Use Copilot tools: `runCommands` (shell), `read`/`readfile` (read), `edit`/`editFiles` (write),
112
+ `code_search`, `find_references`
113
+ Interactive: `askQuestions` (main agent only — NOT available in subagents; VS Code issue #293745)
114
+ Skill path: `.github/agents/<name>.agent.md`
115
+ </adapter>
67
116
  <scope_policy>
68
117
  ## ViePilot Namespace Guard (BUG-004)
69
118
  - Default mode: only use and reference `vp-*` skills in ViePilot workflows.
@@ -150,6 +199,8 @@ Optional flags:
150
199
  - `--tier3` : Run Tier 3 (stack best-practice) only
151
200
  - `--tier4` : Run Tier 4 (framework integrity) only
152
201
  - `--no-autolog` : Skip auto-logging of gaps to `.viepilot/requests/`; report-only mode
202
+ - `--visual` : Run browser-based visual + functional audit of running dev server (ENH-091; requires agent-browser)
203
+ - `--browser <url>` : Specify base URL for browser audit (default: `http://localhost:3000`)
153
204
 
154
205
  ### Auto-Log Behavior (ENH-070)
155
206
 
@@ -180,6 +231,17 @@ Execute workflow from `@$HOME/{envToolDir}/workflows/audit.md`
180
231
  **Step 0**: Detect project type (viepilot framework vs user project)
181
232
 
182
233
  **Step 1 — Tier 1**: ViePilot State Consistency
234
+
235
+ **Claude Code — invoke file-scanner-agent for state file discovery:**
236
+ ```
237
+ Agent({ subagent_type: "file-scanner-agent",
238
+ description: "file-scanner-agent: scan state files for stale phase refs",
239
+ prompt: "patterns: [\".viepilot/phases/*/PHASE-STATE.md\"]. keywords: [\"in_progress\", \"planned\"]. context: Find phases with stale in_progress or planned status."
240
+ })
241
+ ```
242
+ Non-Claude Code: run Glob/Grep inline.
243
+
244
+ Then cross-check results against:
183
245
  ```bash
184
246
  # TRACKER.md vs PHASE-STATE.md
185
247
  # ROADMAP.md vs PHASE-STATE.md
@@ -188,6 +250,17 @@ Execute workflow from `@$HOME/{envToolDir}/workflows/audit.md`
188
250
  ```
189
251
 
190
252
  **Step 2 — Tier 2**: Project Documentation Drift
253
+
254
+ **Claude Code — invoke file-scanner-agent for docs drift scan:**
255
+ ```
256
+ Agent({ subagent_type: "file-scanner-agent",
257
+ description: "file-scanner-agent: scan docs for stale refs and placeholder URLs",
258
+ prompt: "patterns: [\"docs/**/*.md\", \"README.md\", \"CHANGELOG.md\"]. keywords: [\"your-org\", \"YOUR_USERNAME\", \"TODO\", \"placeholder\"]. context: Find stale placeholder refs and docs drift."
259
+ })
260
+ ```
261
+ Non-Claude Code: run Glob/Grep inline.
262
+
263
+ Then check:
191
264
  ```bash
192
265
  # Detect version: package.json / pom.xml / pyproject.toml
193
266
  # README.md version mention
@@ -207,6 +280,29 @@ Execute workflow from `@$HOME/{envToolDir}/workflows/audit.md`
207
280
 
208
281
  **Step 5**: Generate full report
209
282
 
283
+ **Step 5B**: Browser Audit (when `--visual` flag present, Claude Code adapter only)
284
+
285
+ ```
286
+ When --visual is passed:
287
+ 1. Resolve base URL: --browser <url> → config.json audit.baseUrl → http://localhost:3000
288
+ 2. Dispatch browser-audit-agent:
289
+ Agent({ subagent_type: "browser-audit-agent",
290
+ description: "browser-audit-agent: audit running dev server",
291
+ prompt: `op: audit_routes. baseUrl: ${baseUrl}. projectRoot: ${projectRoot}` })
292
+ 3. On success: call writeAuditReport(result, projectRoot) → lib/audit/browser-runner.cjs
293
+ 4. Report path shown to user: .viepilot/audit/visual-report-{timestamp}.md
294
+ 5. On agent-browser not installed: show prerequisite message, skip browser audit (non-blocking)
295
+
296
+ After audit_routes: optionally run accessibility_check on same routes:
297
+ Agent({ subagent_type: "browser-audit-agent",
298
+ prompt: `op: accessibility_check. baseUrl: ${baseUrl}. routes: ${JSON.stringify(routes)}. projectRoot: ${projectRoot}` })
299
+
300
+ Baseline management:
301
+ - --visual first run: saves screenshots to .viepilot/audit/baselines/{slug}.png
302
+ - Subsequent runs: compares current vs baseline (visual_check op)
303
+ - --update-baseline: forces baseline refresh (passes updateBaseline: true to agent)
304
+ ```
305
+
210
306
  **Step 6**: Auto-fix (if requested)
211
307
  </process>
212
308
 
@@ -53,7 +53,7 @@ Silent if command unavailable or errors.
53
53
  </persona_context>
54
54
 
55
55
 
56
- <cursor_skill_adapter>
56
+ <adapter id="claude-code">
57
57
  ## A. Skill Invocation
58
58
  - Skill được gọi khi user mention `vp-auto`, `/vp-auto`, "auto", "vibe", "chạy tự động"
59
59
  - Treat all user text after the skill mention as `{{VP_ARGS}}`
@@ -62,11 +62,61 @@ Silent if command unavailable or errors.
62
62
  Prompt user conversationally with numbered list options at control points.
63
63
 
64
64
  ## C. Tool Usage
65
- Use Cursor tools: `Shell`, `ReadFile`, `Glob`, `rg`, `ApplyPatch`, `WebSearch`, `WebFetch`, `Subagent`
65
+ Use Claude Code tools: `Bash` (shell), `Read` (file), `Edit` + `Write` (file write/patch),
66
+ `Grep` (search), `Glob` (file patterns), `LS`, `WebSearch`, `WebFetch`,
67
+ `Agent` (spawn subagent — multi-level nesting supported)
68
+ Interactive: `AskUserQuestion` (deferred — preload via ToolSearch before first call)
66
69
 
67
70
  ## D. Subagent Spawning
68
- Use `Task(subagent_type="generalPurpose", ...)` for parallel execution.
69
- </cursor_skill_adapter>
71
+ Use `Agent` tool for subagent dispatch. For parallel task execution: fan-out with multiple
72
+ `Agent` calls per cluster (see ADAPTER_CONTEXT.orchestration — claude-code supports parallel: true).
73
+ </adapter>
74
+
75
+ <adapter id="cursor-agent">
76
+ ## A. Skill Invocation
77
+ Same trigger keywords as claude-code adapter.
78
+
79
+ ## C. Tool Usage
80
+ Use Cursor tools: `run_terminal_cmd` (shell), `read_file` (read), `edit_file` (write/edit),
81
+ `grep_search` (search), `web_search`, `codebase_search`, `list_dir`, `file_search`
82
+ Interactive: text list fallback (AskQuestion available in Plan Mode only; Agent Mode = text)
83
+ Subagent: `/multitask` (user command, single-level only — not a callable tool)
84
+ MCP limit: 40 tools
85
+ </adapter>
86
+
87
+ <adapter id="antigravity">
88
+ ## A. Skill Invocation
89
+ Same trigger keywords as claude-code adapter.
90
+ Skill discovery: LLM-driven (automatic, no slash command needed).
91
+
92
+ ## C. Tool Usage
93
+ Use Antigravity tools: `shell` (cmd), `file_read`, `file_write`, MCP plugins
94
+ Interactive: text fallback (TUI-based; no formal AskUserQuestion)
95
+ Skill path: `.agents/skills/<skill>/SKILL.md` (project) or `~/.gemini/antigravity/skills/` (global)
96
+ Note: Gemini CLI deprecated June 18, 2026 — use Antigravity CLI.
97
+ </adapter>
98
+
99
+ <adapter id="codex">
100
+ ## A. Skill Invocation
101
+ Same trigger keywords as claude-code adapter.
102
+
103
+ ## C. Tool Usage
104
+ Use Codex tools: `container.exec` (sandboxed shell), `apply_patch` (file write), `web_search`
105
+ Interactive: text fallback (TUI Tab/Enter injection)
106
+ Config: `~/.codex/config.toml`
107
+ </adapter>
108
+
109
+ <adapter id="copilot">
110
+ ## A. Skill Invocation
111
+ Same trigger keywords as claude-code adapter.
112
+ Discovery: User-driven (`@agent-name` in GitHub Copilot Chat).
113
+
114
+ ## C. Tool Usage
115
+ Use Copilot tools: `runCommands` (shell), `read`/`readfile` (read), `edit`/`editFiles` (write),
116
+ `code_search`, `find_references`
117
+ Interactive: `askQuestions` (main agent only — NOT available in subagents; VS Code issue #293745)
118
+ Skill path: `.github/agents/<name>.agent.md`
119
+ </adapter>
70
120
  <scope_policy>
71
121
  ## ViePilot Namespace Guard (BUG-004)
72
122
  - Default mode: only use and reference `vp-*` skills in ViePilot workflows.
@@ -53,7 +53,7 @@ Silent if command unavailable or errors.
53
53
  </persona_context>
54
54
 
55
55
 
56
- <cursor_skill_adapter>
56
+ <adapter id="claude-code">
57
57
  ## A. Skill Invocation
58
58
  - Skill được gọi khi user mention `vp-brainstorm`, `/vp-brainstorm`, hoặc yêu cầu "brainstorm"
59
59
  - Treat all user text after the skill mention as `{{VP_ARGS}}`
@@ -62,7 +62,10 @@ Silent if command unavailable or errors.
62
62
  Prompt user conversationally with numbered list options.
63
63
 
64
64
  ## C. Tool Usage
65
- Use Cursor tools: `Shell`, `ReadFile`, `Glob`, `rg`, `ApplyPatch`, `WebSearch`, `WebFetch`, `Subagent`
65
+ Use Claude Code tools: `Bash` (shell), `Read` (file), `Edit` + `Write` (file write/patch),
66
+ `Grep` (search), `Glob` (file patterns), `LS`, `WebSearch`, `WebFetch`,
67
+ `Agent` (spawn subagent — multi-level nesting supported)
68
+ Interactive: `AskUserQuestion` (deferred — preload via ToolSearch before first call)
66
69
 
67
70
  ## D. Session Actions — Proactive AUQ Triggers (BUG-026)
68
71
  **No slash sub-commands.** All session actions are triggered proactively by the AI via
@@ -76,7 +79,53 @@ Use Cursor tools: `Shell`, `ReadFile`, `Glob`, `rg`, `ApplyPatch`, `WebSearch`,
76
79
  | **Sync UI → Arch** | After arch update when UI workspace also active | "Sync architecture changes to UI Direction workspace?" |
77
80
 
78
81
  **Why:** In Claude Code terminal, any `/command` is intercepted by the shell as a skill invocation. Using proactive AUQ triggers works identically on ALL adapters — no adapter-specific typing rules needed.
79
- </cursor_skill_adapter>
82
+ </adapter>
83
+
84
+ <adapter id="cursor-agent">
85
+ ## A. Skill Invocation
86
+ Same trigger keywords as claude-code adapter.
87
+
88
+ ## C. Tool Usage
89
+ Use Cursor tools: `run_terminal_cmd` (shell), `read_file` (read), `edit_file` (write/edit),
90
+ `grep_search` (search), `web_search`, `codebase_search`, `list_dir`, `file_search`
91
+ Interactive: text list fallback (AskQuestion available in Plan Mode only; Agent Mode = text)
92
+ Subagent: `/multitask` (user command, single-level only — not a callable tool)
93
+ MCP limit: 40 tools
94
+ </adapter>
95
+
96
+ <adapter id="antigravity">
97
+ ## A. Skill Invocation
98
+ Same trigger keywords as claude-code adapter.
99
+ Skill discovery: LLM-driven (automatic, no slash command needed).
100
+
101
+ ## C. Tool Usage
102
+ Use Antigravity tools: `shell` (cmd), `file_read`, `file_write`, MCP plugins
103
+ Interactive: text fallback (TUI-based; no formal AskUserQuestion)
104
+ Skill path: `.agents/skills/<skill>/SKILL.md` (project) or `~/.gemini/antigravity/skills/` (global)
105
+ Note: Gemini CLI deprecated June 18, 2026 — use Antigravity CLI.
106
+ </adapter>
107
+
108
+ <adapter id="codex">
109
+ ## A. Skill Invocation
110
+ Same trigger keywords as claude-code adapter.
111
+
112
+ ## C. Tool Usage
113
+ Use Codex tools: `container.exec` (sandboxed shell), `apply_patch` (file write), `web_search`
114
+ Interactive: text fallback (TUI Tab/Enter injection)
115
+ Config: `~/.codex/config.toml`
116
+ </adapter>
117
+
118
+ <adapter id="copilot">
119
+ ## A. Skill Invocation
120
+ Same trigger keywords as claude-code adapter.
121
+ Discovery: User-driven (`@agent-name` in GitHub Copilot Chat).
122
+
123
+ ## C. Tool Usage
124
+ Use Copilot tools: `runCommands` (shell), `read`/`readfile` (read), `edit`/`editFiles` (write),
125
+ `code_search`, `find_references`
126
+ Interactive: `askQuestions` (main agent only — NOT available in subagents; VS Code issue #293745)
127
+ Skill path: `.github/agents/<name>.agent.md`
128
+ </adapter>
80
129
  <scope_policy>
81
130
  ## ViePilot Namespace Guard (BUG-004)
82
131
  - Default mode: only use and reference `vp-*` skills in ViePilot workflows.
@@ -152,6 +201,23 @@ Optional flags:
152
201
  - `--research` : Enable proactive research suggestions during the session
153
202
  - `--ui` : Enable UI Direction mode (live HTML/CSS direction artifacts + auto-generates `design.md` when design keywords present — ENH-076)
154
203
  - `--domain embedded` : Force-activate Embedded Domain Mode (hardware topology, RTOS, pin map, memory layout, protocol matrix, power budget pages + topic probes)
204
+
205
+ ### Reference URL Input (ENH-092)
206
+
207
+ When user provides a URL during brainstorm (competitor app, documentation, reference product):
208
+ - research-agent is dispatched with `op: browse_url` using `vercel-labs/agent-browser`
209
+ - Extracted insights added as `## Reference Research` section in session output
210
+ - Covers: features, UX patterns, pricing model, tech stack clues
211
+ - Available as context for crystallize step
212
+ - Falls back to WebFetch if agent-browser not installed (static HTML only — warns if page is JS-rendered)
213
+
214
+ **Examples:**
215
+ ```
216
+ /vp-brainstorm I want to build a task manager. Reference: https://linear.app
217
+ /vp-brainstorm --new Compare these two tools: https://notion.so https://coda.io
218
+ ```
219
+
220
+ **Multiple URLs**: dispatches `compare_products` op → side-by-side comparison table.
155
221
  </context>
156
222
 
157
223
  ### Embedded Domain Mode (ENH-071)