viepilot 2.49.0 → 3.7.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +234 -0
- package/README.md +1 -1
- package/bin/viepilot.cjs +1 -0
- package/bin/vp-tools.cjs +123 -1
- package/docs/brainstorm/session-2026-05-22.md +472 -0
- package/docs/dev/agents.md +51 -41
- package/lib/adapter-context.cjs +294 -0
- package/lib/adapters/antigravity.cjs +8 -2
- package/lib/adapters/claude-code.cjs +4 -0
- package/lib/audit/browser-runner.cjs +102 -0
- package/lib/intake/adapters/browser.cjs +58 -0
- package/lib/intake/adapters/excel-m365.cjs +114 -29
- package/lib/intake/auto-intake.cjs +194 -0
- package/lib/intake/channels.cjs +44 -3
- package/lib/intake/classifier.cjs +22 -4
- package/lib/intake/manifest.cjs +81 -0
- package/lib/intake/setup-wizard.cjs +215 -0
- package/lib/intake/triage-ux.cjs +10 -2
- package/lib/intake/validator.cjs +97 -0
- package/lib/intake/writeback.cjs +169 -3
- package/lib/request/url-enricher.cjs +69 -0
- package/lib/viepilot-install.cjs +15 -0
- package/package.json +1 -1
- package/skills/vp-audit/SKILL.md +99 -3
- package/skills/vp-auto/SKILL.md +54 -4
- package/skills/vp-brainstorm/SKILL.md +69 -3
- package/skills/vp-crystallize/SKILL.md +52 -3
- package/skills/vp-debug/SKILL.md +52 -3
- package/skills/vp-design/SKILL.md +52 -3
- package/skills/vp-docs/SKILL.md +52 -3
- package/skills/vp-evolve/SKILL.md +52 -3
- package/skills/vp-info/SKILL.md +52 -3
- package/skills/vp-intake/SKILL.md +349 -14
- package/skills/vp-pause/SKILL.md +52 -3
- package/skills/vp-persona/SKILL.md +52 -3
- package/skills/vp-proposal/SKILL.md +52 -3
- package/skills/vp-request/SKILL.md +72 -3
- package/skills/vp-resume/SKILL.md +52 -3
- package/skills/vp-rollback/SKILL.md +52 -3
- package/skills/vp-skills/SKILL.md +52 -3
- package/skills/vp-status/SKILL.md +52 -3
- package/skills/vp-task/SKILL.md +52 -3
- package/skills/vp-ui-components/SKILL.md +52 -3
- package/skills/vp-update/SKILL.md +52 -3
- package/workflows/autonomous.md +268 -18
- package/workflows/brainstorm.md +222 -7
- package/workflows/crystallize.md +124 -6
- package/workflows/design.md +62 -1
- package/workflows/request.md +54 -8
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* ADAPTER_CONTEXT — per-adapter capability map for ViePilot v3.
|
|
5
|
+
*
|
|
6
|
+
* Each adapter entry defines:
|
|
7
|
+
* tools{} — canonical tool name for each abstract operation
|
|
8
|
+
* interactive — "AUQ" | "text" | "text-plan-only" | "none"
|
|
9
|
+
* orchestration{} — parallel dispatch capability
|
|
10
|
+
* hooks{} — hook event support
|
|
11
|
+
* mcp{} — MCP support constraints
|
|
12
|
+
* subagent — "multi-level" | "single-level" | "command-only" | "none"
|
|
13
|
+
*
|
|
14
|
+
* Skills read ADAPTER_CONTEXT instead of maintaining inline compat tables.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
const ADAPTER_CONTEXTS = {
|
|
18
|
+
|
|
19
|
+
'claude-code': {
|
|
20
|
+
id: 'claude-code',
|
|
21
|
+
name: 'Claude Code',
|
|
22
|
+
tools: {
|
|
23
|
+
shell: 'Bash',
|
|
24
|
+
read: 'Read',
|
|
25
|
+
write: 'Write',
|
|
26
|
+
edit: 'Edit',
|
|
27
|
+
multi_edit: 'MultiEdit',
|
|
28
|
+
search: 'Grep',
|
|
29
|
+
glob: 'Glob',
|
|
30
|
+
ls: 'LS',
|
|
31
|
+
web_search: 'WebSearch',
|
|
32
|
+
web_fetch: 'WebFetch',
|
|
33
|
+
notebook_read: 'NotebookRead',
|
|
34
|
+
notebook_edit: 'NotebookEdit',
|
|
35
|
+
tool_search: 'ToolSearch',
|
|
36
|
+
todo_read: 'TodoRead',
|
|
37
|
+
todo_write: 'TodoWrite',
|
|
38
|
+
agent: 'Agent',
|
|
39
|
+
interactive: 'AskUserQuestion', // deferred — preload via ToolSearch first
|
|
40
|
+
},
|
|
41
|
+
interactive: 'AUQ', // must call ToolSearch before first AUQ
|
|
42
|
+
subagent: 'multi-level', // Agent tool supports nested spawning
|
|
43
|
+
orchestration: {
|
|
44
|
+
mode: 'agent-tool', // Agent tool = callable from skill
|
|
45
|
+
parallel: true,
|
|
46
|
+
teams: true, // CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1
|
|
47
|
+
background: true,
|
|
48
|
+
model_override: {
|
|
49
|
+
worker: 'claude-haiku-4-5',
|
|
50
|
+
orchestrator: 'claude-sonnet-4-6',
|
|
51
|
+
},
|
|
52
|
+
max_parallel_tasks: 5,
|
|
53
|
+
},
|
|
54
|
+
hooks: {
|
|
55
|
+
count: 28,
|
|
56
|
+
supported_events: [
|
|
57
|
+
'SessionStart', 'SessionEnd', 'Stop', 'StopFailure',
|
|
58
|
+
'UserPromptSubmit', 'PreToolUse', 'PostToolUse', 'PostToolUseFailure',
|
|
59
|
+
'FileChanged', 'SubagentStart', 'SubagentStop',
|
|
60
|
+
'TaskCreated', 'TaskCompleted', 'PreCompact', 'PostCompact',
|
|
61
|
+
],
|
|
62
|
+
},
|
|
63
|
+
mcp: { supported: true, tool_limit: null },
|
|
64
|
+
skill_path_project: '.claude/skills',
|
|
65
|
+
skill_path_global: '~/.claude/skills',
|
|
66
|
+
agents_dir: '.claude/agents',
|
|
67
|
+
},
|
|
68
|
+
|
|
69
|
+
'cursor-agent': {
|
|
70
|
+
id: 'cursor-agent',
|
|
71
|
+
name: 'Cursor (Agent Mode)',
|
|
72
|
+
tools: {
|
|
73
|
+
shell: 'run_terminal_cmd',
|
|
74
|
+
read: 'read_file',
|
|
75
|
+
write: 'edit_file',
|
|
76
|
+
edit: 'edit_file',
|
|
77
|
+
multi_edit: 'edit_file',
|
|
78
|
+
search: 'grep_search',
|
|
79
|
+
glob: 'file_search',
|
|
80
|
+
ls: 'list_dir',
|
|
81
|
+
web_search: 'web_search',
|
|
82
|
+
web_fetch: null, // not available
|
|
83
|
+
notebook_read: null,
|
|
84
|
+
notebook_edit: null,
|
|
85
|
+
tool_search: null,
|
|
86
|
+
todo_read: null,
|
|
87
|
+
todo_write: null,
|
|
88
|
+
agent: null, // /multitask is user command, not callable tool
|
|
89
|
+
interactive: null, // AskQuestion only in Plan Mode
|
|
90
|
+
},
|
|
91
|
+
interactive: 'text', // plain-text numbered list fallback
|
|
92
|
+
subagent: 'command-only', // /multitask user cmd, single-level, not callable
|
|
93
|
+
orchestration: {
|
|
94
|
+
mode: 'sequential',
|
|
95
|
+
parallel: false,
|
|
96
|
+
teams: false,
|
|
97
|
+
background: false,
|
|
98
|
+
model_override: null,
|
|
99
|
+
max_parallel_tasks: 1,
|
|
100
|
+
},
|
|
101
|
+
hooks: {
|
|
102
|
+
count: 5,
|
|
103
|
+
supported_events: [
|
|
104
|
+
'beforeShellExecution', 'beforeMCPExecution',
|
|
105
|
+
'beforeReadFile', 'afterFileEdit', 'stop',
|
|
106
|
+
],
|
|
107
|
+
},
|
|
108
|
+
mcp: { supported: true, tool_limit: 40 },
|
|
109
|
+
skill_path_project: '.cursor/skills',
|
|
110
|
+
skill_path_global: '~/.cursor/skills',
|
|
111
|
+
agents_dir: null,
|
|
112
|
+
},
|
|
113
|
+
|
|
114
|
+
'antigravity': {
|
|
115
|
+
id: 'antigravity',
|
|
116
|
+
name: 'Antigravity (Google)',
|
|
117
|
+
tools: {
|
|
118
|
+
shell: 'shell',
|
|
119
|
+
read: 'file_read',
|
|
120
|
+
write: 'file_write',
|
|
121
|
+
edit: 'file_write',
|
|
122
|
+
multi_edit: 'file_write',
|
|
123
|
+
search: null, // via shell or MCP
|
|
124
|
+
glob: null, // via shell
|
|
125
|
+
ls: null, // via shell
|
|
126
|
+
web_search: null, // via MCP plugin
|
|
127
|
+
web_fetch: null, // via MCP plugin
|
|
128
|
+
notebook_read: null,
|
|
129
|
+
notebook_edit: null,
|
|
130
|
+
tool_search: null,
|
|
131
|
+
todo_read: null,
|
|
132
|
+
todo_write: null,
|
|
133
|
+
agent: null, // async TUI dispatch only
|
|
134
|
+
interactive: null, // TUI-based, no formal AUQ
|
|
135
|
+
},
|
|
136
|
+
interactive: 'none',
|
|
137
|
+
subagent: 'none',
|
|
138
|
+
orchestration: {
|
|
139
|
+
mode: 'sequential',
|
|
140
|
+
parallel: false,
|
|
141
|
+
teams: false,
|
|
142
|
+
background: false,
|
|
143
|
+
model_override: null,
|
|
144
|
+
max_parallel_tasks: 1,
|
|
145
|
+
},
|
|
146
|
+
hooks: {
|
|
147
|
+
count: 3,
|
|
148
|
+
supported_events: ['before_tool', 'after_file_edit', 'session_start'],
|
|
149
|
+
},
|
|
150
|
+
mcp: { supported: true, tool_limit: null },
|
|
151
|
+
skill_path_project: '.agents/skills',
|
|
152
|
+
skill_path_global: '~/.gemini/antigravity/skills',
|
|
153
|
+
agents_dir: null,
|
|
154
|
+
deprecation_notice: 'Gemini CLI was deprecated June 18, 2026. Use Antigravity CLI instead.',
|
|
155
|
+
},
|
|
156
|
+
|
|
157
|
+
'codex': {
|
|
158
|
+
id: 'codex',
|
|
159
|
+
name: 'OpenAI Codex CLI',
|
|
160
|
+
tools: {
|
|
161
|
+
shell: 'container.exec', // sandboxed shell
|
|
162
|
+
read: null, // patch-based — no explicit read tool
|
|
163
|
+
write: 'apply_patch',
|
|
164
|
+
edit: 'apply_patch',
|
|
165
|
+
multi_edit: 'apply_patch',
|
|
166
|
+
search: null, // via shell
|
|
167
|
+
glob: null, // via shell
|
|
168
|
+
ls: null, // via shell
|
|
169
|
+
web_search: 'web_search', // native or MCP
|
|
170
|
+
web_fetch: null,
|
|
171
|
+
notebook_read: null,
|
|
172
|
+
notebook_edit: null,
|
|
173
|
+
tool_search: null,
|
|
174
|
+
todo_read: null,
|
|
175
|
+
todo_write: null,
|
|
176
|
+
agent: 'subagent', // subagents supported
|
|
177
|
+
interactive: null, // TUI Tab/Enter injection only
|
|
178
|
+
},
|
|
179
|
+
interactive: 'none',
|
|
180
|
+
subagent: 'single-level',
|
|
181
|
+
orchestration: {
|
|
182
|
+
mode: 'sequential',
|
|
183
|
+
parallel: false,
|
|
184
|
+
teams: false,
|
|
185
|
+
background: false,
|
|
186
|
+
model_override: null,
|
|
187
|
+
max_parallel_tasks: 1,
|
|
188
|
+
},
|
|
189
|
+
hooks: {
|
|
190
|
+
count: 0,
|
|
191
|
+
supported_events: [],
|
|
192
|
+
},
|
|
193
|
+
mcp: { supported: true, tool_limit: null },
|
|
194
|
+
skill_path_project: null,
|
|
195
|
+
skill_path_global: '~/.codex',
|
|
196
|
+
agents_dir: null,
|
|
197
|
+
},
|
|
198
|
+
|
|
199
|
+
'copilot': {
|
|
200
|
+
id: 'copilot',
|
|
201
|
+
name: 'GitHub Copilot',
|
|
202
|
+
tools: {
|
|
203
|
+
shell: 'runCommands',
|
|
204
|
+
read: 'read',
|
|
205
|
+
write: 'editFiles',
|
|
206
|
+
edit: 'edit',
|
|
207
|
+
multi_edit: 'editFiles',
|
|
208
|
+
search: 'code_search',
|
|
209
|
+
glob: null,
|
|
210
|
+
ls: null,
|
|
211
|
+
web_search: null,
|
|
212
|
+
web_fetch: null,
|
|
213
|
+
notebook_read: null,
|
|
214
|
+
notebook_edit: null,
|
|
215
|
+
tool_search: null,
|
|
216
|
+
todo_read: null,
|
|
217
|
+
todo_write: null,
|
|
218
|
+
agent: null, // explore/task are built-in agents, not callable
|
|
219
|
+
// askQuestions available in main agent ONLY — not in subagents (VS Code #293745)
|
|
220
|
+
interactive: 'askQuestions',
|
|
221
|
+
},
|
|
222
|
+
interactive: 'text', // askQuestions not available in subagents
|
|
223
|
+
subagent: 'none',
|
|
224
|
+
orchestration: {
|
|
225
|
+
mode: 'sequential',
|
|
226
|
+
parallel: false,
|
|
227
|
+
teams: false,
|
|
228
|
+
background: false,
|
|
229
|
+
model_override: null,
|
|
230
|
+
max_parallel_tasks: 1,
|
|
231
|
+
},
|
|
232
|
+
hooks: {
|
|
233
|
+
count: 0,
|
|
234
|
+
supported_events: [],
|
|
235
|
+
},
|
|
236
|
+
mcp: { supported: true, tool_limit: null },
|
|
237
|
+
skill_path_project: '.github/agents',
|
|
238
|
+
skill_path_global: null,
|
|
239
|
+
agents_dir: '.github/agents',
|
|
240
|
+
},
|
|
241
|
+
};
|
|
242
|
+
|
|
243
|
+
// Aliases
|
|
244
|
+
ADAPTER_CONTEXTS['cursor'] = ADAPTER_CONTEXTS['cursor-agent'];
|
|
245
|
+
ADAPTER_CONTEXTS['cursor-ide'] = ADAPTER_CONTEXTS['cursor-agent'];
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* Get ADAPTER_CONTEXT for a given adapter ID. Throws if unknown.
|
|
249
|
+
*/
|
|
250
|
+
function getAdapterContext(id) {
|
|
251
|
+
const ctx = ADAPTER_CONTEXTS[id];
|
|
252
|
+
if (!ctx) {
|
|
253
|
+
throw new Error(`Unknown adapter: "${id}". Known: ${Object.keys(ADAPTER_CONTEXTS).filter(k => !['cursor','cursor-ide'].includes(k)).join(', ')}`);
|
|
254
|
+
}
|
|
255
|
+
return ctx;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* List all canonical adapter IDs (no aliases).
|
|
260
|
+
*/
|
|
261
|
+
function listAdapterIds() {
|
|
262
|
+
return ['claude-code', 'cursor-agent', 'antigravity', 'codex', 'copilot'];
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* Detect the active adapter from environment heuristics.
|
|
267
|
+
* Returns the adapter ID string.
|
|
268
|
+
*/
|
|
269
|
+
function detectAdapter() {
|
|
270
|
+
const env = process.env;
|
|
271
|
+
|
|
272
|
+
// Env-var signals (strongest signals first)
|
|
273
|
+
if (env.CURSOR_TRACE || env.CURSOR_CHANNEL || env.CURSOR_ENABLED) return 'cursor-agent';
|
|
274
|
+
if (env.ANTIGRAVITY_SESSION || env.GEMINI_ANTIGRAVITY_SESSION) return 'antigravity';
|
|
275
|
+
if (env.CODEX_SESSION || env.OPENAI_CODEX_SESSION) return 'codex';
|
|
276
|
+
if (env.GITHUB_COPILOT_AGENT || env.COPILOT_AGENT) return 'copilot';
|
|
277
|
+
|
|
278
|
+
// TERM_PROGRAM / process name signals
|
|
279
|
+
const termProgram = (env.TERM_PROGRAM || '').toLowerCase();
|
|
280
|
+
if (termProgram === 'claude' || termProgram === 'claude-code') return 'claude-code';
|
|
281
|
+
|
|
282
|
+
// ~/.claude directory exists → likely Claude Code
|
|
283
|
+
const os = require('os');
|
|
284
|
+
const fs = require('fs');
|
|
285
|
+
const path = require('path');
|
|
286
|
+
const home = os.homedir();
|
|
287
|
+
if (fs.existsSync(path.join(home, '.claude', 'settings.json'))) return 'claude-code';
|
|
288
|
+
if (fs.existsSync(path.join(home, '.claude'))) return 'claude-code';
|
|
289
|
+
|
|
290
|
+
// Fallback: claude-code (most common in ViePilot context)
|
|
291
|
+
return 'claude-code';
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
module.exports = { ADAPTER_CONTEXTS, getAdapterContext, listAdapterIds, detectAdapter };
|
|
@@ -6,16 +6,21 @@ const fs = require('fs');
|
|
|
6
6
|
module.exports = {
|
|
7
7
|
id: 'antigravity',
|
|
8
8
|
name: 'Antigravity',
|
|
9
|
+
// Global install path (unchanged)
|
|
9
10
|
skillsDir: (home) => path.join(home, '.gemini', 'antigravity', 'skills'),
|
|
10
11
|
viepilotDir: (home) => path.join(home, '.gemini', 'antigravity', 'viepilot'),
|
|
12
|
+
// Phase 131 (FEAT-021): project-level install path (Antigravity v1 uses .agents/skills/)
|
|
13
|
+
projectSkillsDir: '.agents/skills',
|
|
11
14
|
// {envToolDir} in SKILL.md files resolves to this value at install time (ENH-035)
|
|
12
15
|
executionContextBase: '.gemini/antigravity/viepilot',
|
|
13
16
|
// Post-install hint shown in "Next actions" after viepilot install
|
|
14
17
|
postInstallHint: 'Open project and run /vp-status',
|
|
18
|
+
// Phase 131 (FEAT-021): Gemini CLI was deprecated June 18, 2026
|
|
19
|
+
deprecationNotice: '⚠️ Gemini CLI was deprecated June 18, 2026. This installs for Antigravity CLI (the successor). Skill path: .agents/skills/ (project) or ~/.gemini/antigravity/skills/ (global).',
|
|
15
20
|
hooks: {
|
|
16
21
|
configFile: null, // Antigravity has no programmatic hooks system
|
|
17
22
|
schema: 'antigravity',
|
|
18
|
-
supportedEvents: []
|
|
23
|
+
supportedEvents: ['before_tool', 'after_file_edit', 'session_start']
|
|
19
24
|
},
|
|
20
25
|
installSubdirs: [
|
|
21
26
|
'workflows',
|
|
@@ -30,6 +35,7 @@ module.exports = {
|
|
|
30
35
|
isAvailable: (home) => {
|
|
31
36
|
const h = home || os.homedir();
|
|
32
37
|
return fs.existsSync(path.join(h, '.gemini', 'antigravity'))
|
|
33
|
-
|| fs.existsSync(path.join(h, '.antigravity'))
|
|
38
|
+
|| fs.existsSync(path.join(h, '.antigravity'))
|
|
39
|
+
|| fs.existsSync(path.join(process.cwd(), '.agents'));
|
|
34
40
|
}
|
|
35
41
|
};
|
|
@@ -35,6 +35,10 @@ module.exports = {
|
|
|
35
35
|
'ui-components',
|
|
36
36
|
'agents'
|
|
37
37
|
],
|
|
38
|
+
// Phase 130 (FEAT-021): native Claude Code subagent definitions (.claude/agents/)
|
|
39
|
+
// Installed to ~/.claude/agents/ (Claude Code's native agent directory)
|
|
40
|
+
claudeAgentsDir: (home) => path.join(home, '.claude', 'agents'),
|
|
41
|
+
claudeAgentsSrc: 'agents/claude-code',
|
|
38
42
|
// Detection: is this platform available on the current machine?
|
|
39
43
|
isAvailable: (home) => {
|
|
40
44
|
const h = home || os.homedir();
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const path = require('path');
|
|
4
|
+
const fs = require('fs');
|
|
5
|
+
|
|
6
|
+
const DEFAULT_BASE_URL = 'http://localhost:3000';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Resolve base URL for browser audit.
|
|
10
|
+
* Priority: options.baseUrl → config.json audit.baseUrl → DEFAULT_BASE_URL
|
|
11
|
+
* @param {object} options - { baseUrl?: string }
|
|
12
|
+
* @param {string} projectRoot
|
|
13
|
+
* @returns {string}
|
|
14
|
+
*/
|
|
15
|
+
function resolveBaseUrl(options, projectRoot) {
|
|
16
|
+
if (options && options.baseUrl) return options.baseUrl;
|
|
17
|
+
try {
|
|
18
|
+
const configPath = path.join(projectRoot, '.viepilot', 'config.json');
|
|
19
|
+
if (fs.existsSync(configPath)) {
|
|
20
|
+
const cfg = JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
|
21
|
+
if (cfg.audit && cfg.audit.baseUrl) return cfg.audit.baseUrl;
|
|
22
|
+
}
|
|
23
|
+
} catch { /* config missing or malformed — use default */ }
|
|
24
|
+
return DEFAULT_BASE_URL;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Write a Markdown audit report to .viepilot/audit/visual-report-{timestamp}.md
|
|
29
|
+
* @param {object} report - structured audit report from browser-audit-agent
|
|
30
|
+
* @param {string} projectRoot
|
|
31
|
+
* @returns {string} absolute path to report file
|
|
32
|
+
*/
|
|
33
|
+
function writeAuditReport(report, projectRoot) {
|
|
34
|
+
const auditDir = path.join(projectRoot, '.viepilot', 'audit');
|
|
35
|
+
fs.mkdirSync(auditDir, { recursive: true });
|
|
36
|
+
|
|
37
|
+
const timestamp = Date.now();
|
|
38
|
+
const reportPath = path.join(auditDir, `visual-report-${timestamp}.md`);
|
|
39
|
+
|
|
40
|
+
const routes = report.routes || [];
|
|
41
|
+
const routeTable = routes.map(r => {
|
|
42
|
+
const status = r.status === 'ok' ? '✅ ok' : `❌ ${r.status}`;
|
|
43
|
+
const issues = (r.errors && r.errors.length > 0) ? r.errors.join(', ') : '—';
|
|
44
|
+
return `| ${r.url} | ${status} | ${issues} |`;
|
|
45
|
+
}).join('\n');
|
|
46
|
+
|
|
47
|
+
const accessibilitySection = routes
|
|
48
|
+
.filter(r => r.accessibility_issues && r.accessibility_issues.length > 0)
|
|
49
|
+
.map(r => `### ${r.url}\n${r.accessibility_issues.map(i => `- **${i.type}**: ${i.description}`).join('\n')}`)
|
|
50
|
+
.join('\n\n') || '_No accessibility issues found._';
|
|
51
|
+
|
|
52
|
+
const screenshotSection = routes
|
|
53
|
+
.filter(r => r.screenshot)
|
|
54
|
+
.map(r => `- \`${r.url}\`: \`${r.screenshot}\``)
|
|
55
|
+
.join('\n') || '_No screenshots captured._';
|
|
56
|
+
|
|
57
|
+
const content = `# Browser Audit Report — ${new Date(timestamp).toISOString()}
|
|
58
|
+
|
|
59
|
+
**Base URL**: ${report.baseUrl || DEFAULT_BASE_URL}
|
|
60
|
+
**Routes checked**: ${routes.length}
|
|
61
|
+
**Op**: ${report.op || 'audit_routes'}
|
|
62
|
+
|
|
63
|
+
## Routes
|
|
64
|
+
|
|
65
|
+
| Route | Status | Issues |
|
|
66
|
+
|-------|--------|--------|
|
|
67
|
+
${routeTable}
|
|
68
|
+
|
|
69
|
+
## Accessibility Issues
|
|
70
|
+
|
|
71
|
+
${accessibilitySection}
|
|
72
|
+
|
|
73
|
+
## Screenshots
|
|
74
|
+
|
|
75
|
+
${screenshotSection}
|
|
76
|
+
`;
|
|
77
|
+
|
|
78
|
+
fs.writeFileSync(reportPath, content, 'utf8');
|
|
79
|
+
return reportPath;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Run browser audit by dispatching browser-audit-agent (CC adapter only).
|
|
84
|
+
* On non-CC: throws with install instructions.
|
|
85
|
+
* @param {object} options - { baseUrl?, routes?, op?, updateBaseline? }
|
|
86
|
+
* @param {string} projectRoot
|
|
87
|
+
* @returns {Promise<object>} - report object
|
|
88
|
+
*/
|
|
89
|
+
async function runBrowserAudit(options, projectRoot) {
|
|
90
|
+
const baseUrl = resolveBaseUrl(options, projectRoot);
|
|
91
|
+
const op = (options && options.op) || 'audit_routes';
|
|
92
|
+
|
|
93
|
+
// CC adapter: Agent dispatch is handled by vp-audit SKILL.md (orchestrator layer).
|
|
94
|
+
// This module is for direct Node callers — emit clear error.
|
|
95
|
+
throw new Error(
|
|
96
|
+
`Browser audit requires Claude Code with the agent-browser skill.\n` +
|
|
97
|
+
`Install: npx skills add vercel-labs/agent-browser\n` +
|
|
98
|
+
`Then run: /vp-audit --visual --browser ${baseUrl}`
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
module.exports = { runBrowserAudit, resolveBaseUrl, writeAuditReport, DEFAULT_BASE_URL };
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const URL_PATTERNS = {
|
|
4
|
+
'google-sheets': /docs\.google\.com\/spreadsheets/,
|
|
5
|
+
'github-issues': /github\.com\/[^/]+\/[^/]+\/issues/,
|
|
6
|
+
'jira': /atlassian\.net\/browse\/[A-Z]+-\d+|atlassian\.net\/jira\/software\/projects/,
|
|
7
|
+
'trello': /trello\.com\/[bc]\//,
|
|
8
|
+
'notion': /notion\.so\/[a-f0-9]{8,}/,
|
|
9
|
+
'sharepoint-xlsx': /sharepoint\.com\/:[a-z]:\/[a-z]\//,
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Detect source type from URL string.
|
|
14
|
+
* @param {string} url
|
|
15
|
+
* @returns {'google-sheets'|'github-issues'|'jira'|'trello'|'notion'|'sharepoint-xlsx'|'generic-table'}
|
|
16
|
+
*/
|
|
17
|
+
function detectUrlType(url) {
|
|
18
|
+
if (!url || typeof url !== 'string') return 'generic-table';
|
|
19
|
+
for (const [type, pattern] of Object.entries(URL_PATTERNS)) {
|
|
20
|
+
if (pattern.test(url)) return type;
|
|
21
|
+
}
|
|
22
|
+
return 'generic-table';
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Read ticket rows from a public URL via browser-intake-agent (CC adapter only).
|
|
27
|
+
* Non-CC adapters receive a clear unsupported error.
|
|
28
|
+
* @param {object} channel - channel config with `url` field
|
|
29
|
+
* @param {string} projectRoot - absolute path to project root
|
|
30
|
+
* @returns {Promise<Array<{title,description,labels,priority,status}>>}
|
|
31
|
+
*/
|
|
32
|
+
async function readBrowserUrl(channel, projectRoot) {
|
|
33
|
+
if (!channel || !channel.url) {
|
|
34
|
+
throw new Error('browser channel requires a "url" field in channel config');
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const sourceType = detectUrlType(channel.url);
|
|
38
|
+
|
|
39
|
+
// CC adapter: Agent dispatch is handled by vp-intake SKILL.md (orchestrator layer).
|
|
40
|
+
// This module is used by non-CC adapters or direct Node callers — emit clear error.
|
|
41
|
+
throw new Error(
|
|
42
|
+
`Browser channel (${sourceType}) requires Claude Code with the agent-browser skill.\n` +
|
|
43
|
+
`Install: npx skills add vercel-labs/agent-browser\n` +
|
|
44
|
+
`Then run vp-intake from Claude Code.`
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Check whether a URL appears to be a publicly accessible intake source.
|
|
50
|
+
* Does not make network requests — pattern-match only.
|
|
51
|
+
* @param {string} url
|
|
52
|
+
* @returns {boolean}
|
|
53
|
+
*/
|
|
54
|
+
function isKnownPublicSource(url) {
|
|
55
|
+
return detectUrlType(url) !== 'generic-table';
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
module.exports = { readBrowserUrl, detectUrlType, isKnownPublicSource, URL_PATTERNS };
|