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.
Files changed (49) hide show
  1. package/CHANGELOG.md +234 -0
  2. package/README.md +1 -1
  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/lib/adapter-context.cjs +294 -0
  8. package/lib/adapters/antigravity.cjs +8 -2
  9. package/lib/adapters/claude-code.cjs +4 -0
  10. package/lib/audit/browser-runner.cjs +102 -0
  11. package/lib/intake/adapters/browser.cjs +58 -0
  12. package/lib/intake/adapters/excel-m365.cjs +114 -29
  13. package/lib/intake/auto-intake.cjs +194 -0
  14. package/lib/intake/channels.cjs +44 -3
  15. package/lib/intake/classifier.cjs +22 -4
  16. package/lib/intake/manifest.cjs +81 -0
  17. package/lib/intake/setup-wizard.cjs +215 -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 +349 -14
  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
@@ -53,14 +53,63 @@ 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-info`, `/vp-info`, "viepilot version", "phiên bản viepilot", "skills list bundle"
59
59
  - Treat all user text after the skill mention as `{{VP_ARGS}}`
60
60
 
61
61
  ## B. Tool Usage
62
- Use Cursor tools: `Shell`, `ReadFile`, `Glob`, `rg`, `ApplyPatch`, `WebSearch`, `WebFetch`, `Subagent`
63
- </cursor_skill_adapter>
62
+ Use Claude Code tools: `Bash` (shell), `Read` (file), `Edit` + `Write` (file write/patch),
63
+ `Grep` (search), `Glob` (file patterns), `LS`, `WebSearch`, `WebFetch`,
64
+ `Agent` (spawn subagent — multi-level nesting supported)
65
+ Interactive: `AskUserQuestion` (deferred — preload via ToolSearch before first call)
66
+ </adapter>
67
+
68
+ <adapter id="cursor-agent">
69
+ ## A. Skill Invocation
70
+ Same trigger keywords as claude-code adapter.
71
+
72
+ ## C. Tool Usage
73
+ Use Cursor tools: `run_terminal_cmd` (shell), `read_file` (read), `edit_file` (write/edit),
74
+ `grep_search` (search), `web_search`, `codebase_search`, `list_dir`, `file_search`
75
+ Interactive: text list fallback (AskQuestion available in Plan Mode only; Agent Mode = text)
76
+ Subagent: `/multitask` (user command, single-level only — not a callable tool)
77
+ MCP limit: 40 tools
78
+ </adapter>
79
+
80
+ <adapter id="antigravity">
81
+ ## A. Skill Invocation
82
+ Same trigger keywords as claude-code adapter.
83
+ Skill discovery: LLM-driven (automatic, no slash command needed).
84
+
85
+ ## C. Tool Usage
86
+ Use Antigravity tools: `shell` (cmd), `file_read`, `file_write`, MCP plugins
87
+ Interactive: text fallback (TUI-based; no formal AskUserQuestion)
88
+ Skill path: `.agents/skills/<skill>/SKILL.md` (project) or `~/.gemini/antigravity/skills/` (global)
89
+ Note: Gemini CLI deprecated June 18, 2026 — use Antigravity CLI.
90
+ </adapter>
91
+
92
+ <adapter id="codex">
93
+ ## A. Skill Invocation
94
+ Same trigger keywords as claude-code adapter.
95
+
96
+ ## C. Tool Usage
97
+ Use Codex tools: `container.exec` (sandboxed shell), `apply_patch` (file write), `web_search`
98
+ Interactive: text fallback (TUI Tab/Enter injection)
99
+ Config: `~/.codex/config.toml`
100
+ </adapter>
101
+
102
+ <adapter id="copilot">
103
+ ## A. Skill Invocation
104
+ Same trigger keywords as claude-code adapter.
105
+ Discovery: User-driven (`@agent-name` in GitHub Copilot Chat).
106
+
107
+ ## C. Tool Usage
108
+ Use Copilot tools: `runCommands` (shell), `read`/`readfile` (read), `edit`/`editFiles` (write),
109
+ `code_search`, `find_references`
110
+ Interactive: `askQuestions` (main agent only — NOT available in subagents; VS Code issue #293745)
111
+ Skill path: `.github/agents/<name>.agent.md`
112
+ </adapter>
64
113
  <scope_policy>
65
114
  ## ViePilot Namespace Guard (BUG-004)
66
115
  - Default mode: only use and reference `vp-*` skills in ViePilot workflows.
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: vp-intake
3
3
  description: "Import and triage tickets from Excel/M365 Online, Google Sheets, or CSV/TSV files — classify as BUG/ENH, accept/decline via AskUserQuestion, write back to source, generate TRIAGE report"
4
- version: 1.0.0
4
+ version: 1.1.0
5
5
  ---
6
6
 
7
7
  <greeting>
@@ -11,7 +11,7 @@ Output this banner as the **first** thing on every invocation — before questio
11
11
 
12
12
  ```
13
13
  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
14
- VIEPILOT ► VP-INTAKE v1.0.0 (fw 2.48.0)
14
+ VIEPILOT ► VP-INTAKE v1.1.0 (fw 2.50.0)
15
15
  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
16
16
  ```
17
17
  </greeting>
@@ -37,7 +37,7 @@ Inject the output as `## User Persona` context before any task execution.
37
37
  Silent if command unavailable or errors.
38
38
  </persona_context>
39
39
 
40
- <cursor_skill_adapter>
40
+ <adapter id="claude-code">
41
41
  ## A. Skill Invocation
42
42
  - Skill được gọi khi user mention `vp-intake`, `/vp-intake`, "import tickets", "nhập ticket", "đọc ticket từ", "triage ticket"
43
43
  - Treat all user text after the skill mention as `{{VP_ARGS}}`
@@ -46,8 +46,57 @@ Silent if command unavailable or errors.
46
46
  Prompt user conversationally with options.
47
47
 
48
48
  ## C. Tool Usage
49
- Use Cursor tools: `Shell`, `ReadFile`, `Glob`, `rg`, `ApplyPatch`, `WebSearch`, `WebFetch`, `Subagent`
50
- </cursor_skill_adapter>
49
+ Use Claude Code tools: `Bash` (shell), `Read` (file), `Edit` + `Write` (file write/patch),
50
+ `Grep` (search), `Glob` (file patterns), `LS`, `WebSearch`, `WebFetch`,
51
+ `Agent` (spawn subagent — multi-level nesting supported)
52
+ Interactive: `AskUserQuestion` (deferred — preload via ToolSearch before first call)
53
+ </adapter>
54
+
55
+ <adapter id="cursor-agent">
56
+ ## A. Skill Invocation
57
+ Same trigger keywords as claude-code adapter.
58
+
59
+ ## C. Tool Usage
60
+ Use Cursor tools: `run_terminal_cmd` (shell), `read_file` (read), `edit_file` (write/edit),
61
+ `grep_search` (search), `web_search`, `codebase_search`, `list_dir`, `file_search`
62
+ Interactive: text list fallback (AskQuestion available in Plan Mode only; Agent Mode = text)
63
+ Subagent: `/multitask` (user command, single-level only — not a callable tool)
64
+ MCP limit: 40 tools
65
+ </adapter>
66
+
67
+ <adapter id="antigravity">
68
+ ## A. Skill Invocation
69
+ Same trigger keywords as claude-code adapter.
70
+ Skill discovery: LLM-driven (automatic, no slash command needed).
71
+
72
+ ## C. Tool Usage
73
+ Use Antigravity tools: `shell` (cmd), `file_read`, `file_write`, MCP plugins
74
+ Interactive: text fallback (TUI-based; no formal AskUserQuestion)
75
+ Skill path: `.agents/skills/<skill>/SKILL.md` (project) or `~/.gemini/antigravity/skills/` (global)
76
+ Note: Gemini CLI deprecated June 18, 2026 — use Antigravity CLI.
77
+ </adapter>
78
+
79
+ <adapter id="codex">
80
+ ## A. Skill Invocation
81
+ Same trigger keywords as claude-code adapter.
82
+
83
+ ## C. Tool Usage
84
+ Use Codex tools: `container.exec` (sandboxed shell), `apply_patch` (file write), `web_search`
85
+ Interactive: text fallback (TUI Tab/Enter injection)
86
+ Config: `~/.codex/config.toml`
87
+ </adapter>
88
+
89
+ <adapter id="copilot">
90
+ ## A. Skill Invocation
91
+ Same trigger keywords as claude-code adapter.
92
+ Discovery: User-driven (`@agent-name` in GitHub Copilot Chat).
93
+
94
+ ## C. Tool Usage
95
+ Use Copilot tools: `runCommands` (shell), `read`/`readfile` (read), `edit`/`editFiles` (write),
96
+ `code_search`, `find_references`
97
+ Interactive: `askQuestions` (main agent only — NOT available in subagents; VS Code issue #293745)
98
+ Skill path: `.github/agents/<name>.agent.md`
99
+ </adapter>
51
100
  <scope_policy>
52
101
  ## ViePilot Namespace Guard (BUG-004)
53
102
  - Default mode: only use and reference `vp-*` skills in ViePilot workflows.
@@ -78,13 +127,26 @@ session report.
78
127
  Optional flags:
79
128
  - `--channel <id>` : Skip channel selection, use this channel ID directly
80
129
  - `--dry-run` : Classify and show tickets without creating requests or writing back
130
+ - `--setup` : Force setup wizard — configure a new channel now (even if channels already exist)
131
+ - `--config` : Alias for `--setup`
132
+ - `--skip-validation` : Skip Step 4.5 codebase validation (faster, no file-scanner-agent spawn)
133
+ - `--analyze` : Force re-analysis of source file structure via `analyze_structure` op, even if a fresh manifest exists (ENH-095)
134
+ - `--schedule "CRON"` : Create a CronCreate schedule for automated intake (Claude Code only)
135
+ - `--unschedule` : Delete the existing intake schedule (CronDelete)
136
+ - `--auto` : Run in headless auto mode — set by scheduler only, not for manual use
81
137
 
82
138
  **Supported channel types:**
83
139
  | Type | Auth | Config field |
84
140
  |------|------|-------------|
85
141
  | `csv` | None | `path` (local file) |
86
142
  | `google_sheets` | Service Account JSON | `spreadsheet_id` + `sheet_name` |
87
- | `excel_m365` | Azure App Registration | `workbook_id` + `sheet_name` |
143
+ | `excel_m365` (Graph API) | Azure App Registration | `workbook_id` + `sheet_name` |
144
+ | `excel_m365` (sharing link) | None — anonymous | `sharing_url` |
145
+ | `browser` | None — public URL only | `url` (https://...) |
146
+
147
+ **`browser` channel** — reads any publicly accessible URL via `vercel-labs/agent-browser`.
148
+ Supports: Google Sheets share links, GitHub Issues lists, Jira public boards, Trello boards, Notion pages.
149
+ Prerequisite (Claude Code only): `npx skills add vercel-labs/agent-browser`
88
150
 
89
151
  **Config file:** `.viepilot/intake/channels.json`
90
152
  **Credentials dir:** `.viepilot/.credentials/` (gitignored)
@@ -92,6 +154,106 @@ Optional flags:
92
154
 
93
155
  <process>
94
156
 
157
+ ### --schedule flag (ENH-088, Claude Code only)
158
+
159
+ ```js
160
+ // --schedule "0 9 * * 1"
161
+ // ToolSearch("select:CronCreate") must be called first (deferred tool)
162
+ CronCreate({ schedule: cronExpression, prompt: `/vp-intake --channel ${channelId} --auto` })
163
+ // Then write schedule.json via lib/intake/auto-intake.cjs createSchedule()
164
+ ```
165
+ Write `.viepilot/intake/schedule.json` with `{ cron, schedule_id, channel_id, created }`.
166
+ Output: `✅ Intake scheduled: ${humanReadableCron(cronExpression)}`
167
+
168
+ **Non-CC adapters**: print the following and exit:
169
+ ```
170
+ Scheduling requires Claude Code. Use cron + CLI instead.
171
+ Suggested: cron "0 9 * * 1 claude /vp-intake --channel default --auto"
172
+ ```
173
+
174
+ ### --unschedule flag (ENH-088)
175
+
176
+ ```js
177
+ // ToolSearch("select:CronDelete") must be called first
178
+ const { schedule_id } = readSchedule() // lib/intake/auto-intake.cjs
179
+ CronDelete({ schedule_id })
180
+ deleteSchedule(projectRoot) // removes schedule.json
181
+ ```
182
+ Output: `✅ Intake schedule removed`
183
+ If no schedule.json exists: `No intake schedule found.`
184
+
185
+ ### --auto mode (ENH-088, headless — set by scheduler only)
186
+
187
+ When `--auto` flag is present:
188
+ 1. Skip Step 0 setup wizard, Step 3 channel select (use `--channel` arg), Step 5 AUQ triage
189
+ 2. Call `runAutoIntake(tickets, channel, { projectRoot })` from `lib/intake/auto-intake.cjs`
190
+ 3. Auto-accept tickets with `confidence ≥ 0.7` → create request files
191
+ 4. Queue lower-confidence tickets → append to `.viepilot/intake/pending-review.json`
192
+ 5. Print auto-run summary and exit
193
+
194
+ Output format:
195
+ ```
196
+ [AUTO INTAKE] Channel: {name} | {N} tickets classified
197
+ Auto-accepted ({N}): BUG-030, ENH-090 (confidence ≥ 0.7)
198
+ Queued for review ({N}): TICKET-007 (confidence 0.4)
199
+ Report: .viepilot/intake/TRIAGE-auto-{timestamp}.md
200
+ ```
201
+
202
+ ### Pending Review Queue (ENH-088, manual runs only)
203
+
204
+ At the start of every manual `/vp-intake` (before Step 0), check for queued items:
205
+
206
+ ```js
207
+ const { readSchedule } = require('lib/intake/auto-intake.cjs');
208
+ const pendingPath = '.viepilot/intake/pending-review.json';
209
+ if (fs.existsSync(pendingPath)) {
210
+ const pending = JSON.parse(fs.readFileSync(pendingPath));
211
+ if (pending.items && pending.items.length > 0) {
212
+ // Show AUQ:
213
+ const lastDate = pending.items[pending.items.length - 1].queued_at?.split('T')[0];
214
+ AskUserQuestion({
215
+ question: `⚠️ ${pending.items.length} tickets in pending-review queue (from last auto-run ${lastDate}). Process them now?`,
216
+ options: [
217
+ { label: 'Yes', description: 'Load pending items into triage' },
218
+ { label: 'Skip', description: 'Proceed with fresh intake' },
219
+ ]
220
+ })
221
+ // If Yes: inject pending.items into the ticket list for Step 4 onwards
222
+ // If Skip: continue normally
223
+ }
224
+ }
225
+ ```
226
+
227
+ ### Step 0: Setup wizard detection (ENH-084)
228
+
229
+ **Check wizard trigger conditions:**
230
+
231
+ ```js
232
+ const args = parseArgs(VP_ARGS);
233
+ const forceSetup = args.includes('--setup') || args.includes('--config');
234
+
235
+ // Init dir + load channels
236
+ initIntakeDir(projectRoot); // lib/intake/channels.cjs
237
+ const { channels } = loadChannels(projectRoot);
238
+ const needsSetup = forceSetup || !hasRealChannels(channels); // lib/intake/channels.cjs
239
+ ```
240
+
241
+ **If `needsSetup` is true:**
242
+
243
+ Call `runSetupWizard(projectRoot, askFn)` from `lib/intake/setup-wizard.cjs`.
244
+
245
+ `askFn` wraps `AskUserQuestion` for structured option prompts, and falls back to free-text
246
+ input for open-ended fields (column names, file paths, URLs).
247
+
248
+ After wizard completes:
249
+ - Reload channels from channels.json
250
+ - If exactly 1 channel was just created → auto-select it and skip Step 3 (go to Step 4)
251
+ - Otherwise → continue to Step 3 (channel select AUQ with all available real channels)
252
+
253
+ **If `needsSetup` is false** and no `--setup` flag → skip Step 0 entirely, continue to Step 1.
254
+
255
+ ---
256
+
95
257
  ### Step 1: Init intake directory
96
258
 
97
259
  ```bash
@@ -102,13 +264,11 @@ This creates `.viepilot/intake/channels.json` (scaffold) and `.viepilot/.credent
102
264
 
103
265
  ### Step 2: Load channels
104
266
 
105
- Read `.viepilot/intake/channels.json`. If no channels configured (only example stubs remain),
106
- tell the user:
267
+ Read `.viepilot/intake/channels.json`. If no real channels exist after Step 0:
107
268
 
108
269
  ```
109
270
  No channels configured yet.
110
- Edit .viepilot/intake/channels.json to add your ticket sources.
111
- Run vp-tools intake-init to see the config scaffold.
271
+ Run /vp-intake --setup to configure a channel interactively.
112
272
  ```
113
273
 
114
274
  ### Step 3: Select channel (AUQ single-select)
@@ -119,15 +279,110 @@ header: "Channel"
119
279
  options: one per channel — label: "{channel.name} ({channel.type})", description: "{channel.id}"
120
280
  ```
121
281
 
282
+ ### Step 3.5: Sharing URL guard (ENH-089)
283
+
284
+ Before reading, check for `sharing_url` on `excel_m365` channels:
285
+ ```
286
+ if (channel.type === 'excel_m365' && channel.sharing_url) {
287
+ // Print warning — write-back will be disabled for this session
288
+ console.warn(
289
+ `⚠️ Channel "${channel.name}" uses a sharing_url — write-back is read-only for sharing links.\n` +
290
+ ` To enable write-back: configure workbook_id + .viepilot/.credentials/m365-credentials.json`
291
+ );
292
+ }
293
+ ```
294
+ Continue with intake — read-only is acceptable for triage.
295
+
296
+ ### Step 3.6: Manifest Lifecycle (ENH-095) — Claude Code only
297
+
298
+ Before reading tickets, check for an existing intake manifest and use it for column mapping.
299
+ Non-CC adapters skip this step and fall back to `autoDetectColumnMap()` as before.
300
+
301
+ ```js
302
+ const { loadManifest, isManifestFresh, saveManifest, getColumnMap } = require('lib/intake/manifest.cjs');
303
+
304
+ const channelId = channel.id || channel.name.toLowerCase().replace(/\s+/g, '-');
305
+ const ttlDays = config?.intake?.manifest_ttl_days ?? 7;
306
+ const existing = loadManifest(channelId, projectRoot);
307
+ const forceAnalyze = flags.includes('--analyze');
308
+
309
+ let manifest = null;
310
+
311
+ if (existing && isManifestFresh(existing, ttlDays) && !forceAnalyze) {
312
+ // Use cached manifest — skip re-analysis
313
+ manifest = existing;
314
+ console.log(`[vp-intake] Manifest: .viepilot/intake/${channelId}-manifest.json (${existing.analyzed_at})`);
315
+
316
+ } else if (channel.type === 'excel_m365' || channel.type === 'google_sheets') {
317
+ // Dispatch analyze_structure — AI reads entire file and maps structure
318
+ const agentType = channel.type === 'excel_m365' ? 'excel-intake-agent' : 'sheets-intake-agent';
319
+ const result = await Agent({
320
+ subagent_type: agentType,
321
+ description: `${agentType}: analyze_structure`,
322
+ prompt: `op: analyze_structure. channel_config: ${JSON.stringify(channel)}. projectRoot: ${projectRoot}`
323
+ });
324
+ if (result && !result.error) {
325
+ manifest = typeof result === 'string' ? JSON.parse(result) : result;
326
+ saveManifest(channelId, manifest, projectRoot);
327
+ console.log(`[vp-intake] Manifest saved → .viepilot/intake/${channelId}-manifest.json`);
328
+ }
329
+ }
330
+
331
+ // Override column_map from manifest when not explicitly configured
332
+ if (manifest && !channel.column_map) {
333
+ const detectedMap = getColumnMap(manifest, channel.sheet_name || null);
334
+ if (detectedMap && Object.keys(detectedMap).length > 0) {
335
+ channel = { ...channel, column_map: detectedMap };
336
+ console.log(`[vp-intake] Column map from manifest: ${JSON.stringify(detectedMap)}`);
337
+ }
338
+ }
339
+ ```
340
+
341
+ **`--analyze` flag**: forces re-analysis even if manifest is fresh (e.g., source file structure changed).
342
+
122
343
  ### Step 4: Read and classify tickets
123
344
 
124
- Dispatch to the correct adapter based on `channel.type`:
345
+ **Claude Code adapter** dispatch via native agents for Excel/Sheets/Browser:
346
+ ```js
347
+ if (channel.type === 'excel_m365') {
348
+ // Claude Code: use native excel-intake-agent
349
+ Agent({ subagent_type: "excel-intake-agent",
350
+ description: "excel-intake-agent: read tickets from Excel M365",
351
+ prompt: `op: read. channel_config: ${JSON.stringify(channel)}. projectRoot: ${projectRoot}`
352
+ })
353
+ } else if (channel.type === 'google_sheets') {
354
+ // Claude Code: use native sheets-intake-agent
355
+ Agent({ subagent_type: "sheets-intake-agent",
356
+ description: "sheets-intake-agent: read tickets from Google Sheets",
357
+ prompt: `op: read. channel_config: ${JSON.stringify(channel)}. projectRoot: ${projectRoot}`
358
+ })
359
+ } else if (channel.type === 'browser') {
360
+ // SharePoint xlsx sharing links → excel-intake-agent (ENH-094)
361
+ const { detectUrlType } = require('lib/intake/adapters/browser.cjs');
362
+ if (detectUrlType(channel.url) === 'sharepoint-xlsx') {
363
+ Agent({ subagent_type: "excel-intake-agent",
364
+ description: "excel-intake-agent: read SharePoint xlsx via sharing_url",
365
+ prompt: `op: read. channel_config: ${JSON.stringify({ ...channel, type: 'excel_m365', sharing_url: channel.url })}. projectRoot: ${projectRoot}`
366
+ })
367
+ } else {
368
+ // All other browser URLs → browser-intake-agent (requires vercel-labs/agent-browser)
369
+ Agent({ subagent_type: "browser-intake-agent",
370
+ description: "browser-intake-agent: read tickets from public URL",
371
+ prompt: `op: read_url. url: ${channel.url}. channel_config: ${JSON.stringify(channel)}. projectRoot: ${projectRoot}`
372
+ })
373
+ }
374
+ }
375
+ ```
376
+
377
+ **Non-CC adapters** — inline adapter dispatch (unchanged):
125
378
  - `csv` → `lib/intake/adapters/csv.cjs` → `readCsv(channel, projectRoot)`
126
379
  - `google_sheets` → `lib/intake/adapters/google-sheets.cjs` → `readGoogleSheet(channel, projectRoot)`
127
380
  - `excel_m365` → `lib/intake/adapters/excel-m365.cjs` → `readExcelM365(channel, projectRoot)`
381
+ - `browser` → `lib/intake/adapters/browser.cjs` → `readBrowserUrl(channel, projectRoot)` (throws — CC only)
128
382
 
129
383
  For each ticket, call `classifyTicket(ticket)` from `lib/intake/classifier.cjs`.
130
- Attach `ticket._classified = 'BUG' | 'ENH' | 'UNCLEAR'`.
384
+ Returns `{ classified: 'BUG'|'ENH'|'UNCLEAR', confidence: 0.0–1.0 }`.
385
+ Attach `ticket._classified = result.classified` and `ticket._confidence = result.confidence`.
131
386
 
132
387
  Display classification summary:
133
388
  ```
@@ -137,6 +392,35 @@ Read {N} tickets from {channel.name}
137
392
 
138
393
  If 0 tickets found, exit with message "No tickets found in this channel."
139
394
 
395
+ ### Step 4.5: Parallel Codebase Validation (ENH-087)
396
+
397
+ > Skip entirely if `--skip-validation` flag is set.
398
+
399
+ Call `validateTickets(classifiedTickets, projectRoot)` from `lib/intake/validator.cjs`.
400
+ Fan-out: one validation per ticket (batched ≤10 concurrently via `Promise.all`).
401
+
402
+ **Claude Code adapter** — each ticket dispatches a `file-scanner-agent`:
403
+ ```js
404
+ // Inside validateTicket (CC path override):
405
+ Agent({ subagent_type: "file-scanner-agent",
406
+ description: `file-scanner-agent: validate "${ticket.title}"`,
407
+ prompt: `keywords: ${JSON.stringify(keywords)}. projectRoot: ${projectRoot}. requests_dir: .viepilot/requests/`
408
+ })
409
+ ```
410
+
411
+ **Non-CC adapters** — inline `grep` + request scan (no agent spawn).
412
+
413
+ Display progress during validation:
414
+ ```
415
+ Validating {N} tickets against codebase... (parallel)
416
+ ✅ "Login button broken" — found: src/components/LoginButton.tsx
417
+ ⚠️ "Add CSV export" — similar: ENH-045
418
+ ❓ "Fix the performance issue" — no codebase match
419
+ ```
420
+
421
+ Validation results are attached as `ticket._validation` and shown as badges in Step 5 AUQ.
422
+ `--skip-validation` flag: skip Step 4.5 entirely, proceed directly to Step 5.
423
+
140
424
  ### Step 5: Triage (AUQ multi-select)
141
425
 
142
426
  Call `runTriage(tickets, channel, projectRoot, askFn)` from `lib/intake/triage-ux.cjs`.
@@ -151,10 +435,54 @@ async function askFn(question, options, multiSelect) {
151
435
  For each ticket: AUQ multi-select to accept/decline, then AUQ single-select for decline reason.
152
436
  UNCLEAR tickets get a 3-choice prompt: "Accept as BUG / Accept as ENH / Decline".
153
437
 
438
+ ### Step 5.5: Embed Intake Source in accepted-ticket request files (ENH-095)
439
+
440
+ When creating `.viepilot/requests/BUG-N.md` or `ENH-N.md` for an accepted ticket,
441
+ append the following block so `vp-auto` can write back task completion to the source row:
442
+
443
+ ```markdown
444
+ ## Intake Source
445
+ - channel_id: {channelId}
446
+ - sheet_name: {channel.sheet_name || null}
447
+ - source_row: {ticket._source_row}
448
+ - manifest_path: .viepilot/intake/{channelId}-manifest.json
449
+ - channel_type: {channel.type}
450
+ - workbook_id: {channel.workbook_id || null}
451
+ - sharing_url: {channel.sharing_url || null}
452
+ ```
453
+
454
+ This block is read by `vp-auto` post-PASS hook to call `writebackIntakeResponse()`.
455
+ For read-only channels (`sharing_url`, `browser`, `csv`), write-back will be skipped
456
+ silently — the block is still written for traceability.
457
+
154
458
  ### Step 6: Write-back + Report
155
459
 
460
+ **Claude Code adapter** — dispatch via native agents for Excel/Sheets write-back:
461
+ ```js
462
+ if (channel.type === 'excel_m365') {
463
+ Agent({ subagent_type: "excel-intake-agent",
464
+ description: "excel-intake-agent: write triage results back to Excel M365",
465
+ prompt: `op: write. channel_config: ${JSON.stringify(channel)}. tickets: ${JSON.stringify(triageResult)}. projectRoot: ${projectRoot}`
466
+ })
467
+ } else if (channel.type === 'google_sheets') {
468
+ Agent({ subagent_type: "sheets-intake-agent",
469
+ description: "sheets-intake-agent: write triage results back to Google Sheets",
470
+ prompt: `op: write. channel_config: ${JSON.stringify(channel)}. tickets: ${JSON.stringify(triageResult)}. projectRoot: ${projectRoot}`
471
+ })
472
+ } else if (channel.type === 'browser') {
473
+ // browser channels are read-only — no write-back (public URLs, no auth)
474
+ console.log(`ℹ️ Browser channel "${channel.name}" is read-only — skipping write-back.`);
475
+ } else {
476
+ await writeback(channel, triageResult, projectRoot); // lib/intake/writeback.cjs (csv + non-agent path)
477
+ }
478
+ ```
479
+
480
+ **Non-CC adapters** — all types via `lib/intake/writeback.cjs`:
481
+ ```js
482
+ await writeback(channel, triageResult, projectRoot);
483
+ ```
484
+
156
485
  ```js
157
- await writeback(channel, triageResult, projectRoot); // lib/intake/writeback.cjs
158
486
  const reportPath = generateTriageReport(channel, triageResult, projectRoot); // lib/intake/report.cjs
159
487
  ```
160
488
 
@@ -168,6 +496,7 @@ Write-back failure → warn (non-fatal), report is still generated.
168
496
  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
169
497
 
170
498
  Channel: {channel.name}
499
+ Manifest: .viepilot/intake/{channelId}-manifest.json (shown only when manifest was used)
171
500
  Accepted: {N} → {request IDs}
172
501
  Declined: {N}
173
502
  Report: .viepilot/intake/TRIAGE-{timestamp}.md
@@ -188,12 +517,15 @@ options:
188
517
  </process>
189
518
 
190
519
  <success_criteria>
520
+ - [ ] Setup wizard triggers automatically when no real channels exist (or --setup flag)
521
+ - [ ] Wizard collects channel config via AUQ and writes to channels.json
191
522
  - [ ] Channels loaded from `.viepilot/intake/channels.json`
192
523
  - [ ] Correct adapter dispatched based on channel type
193
524
  - [ ] Tickets classified as BUG / ENH / UNCLEAR
194
525
  - [ ] AUQ multi-select triage completed with accept/decline per ticket
195
526
  - [ ] Decline reasons collected and attached
196
- - [ ] Accepted tickets auto-create `.viepilot/requests/` files
527
+ - [ ] Accepted tickets auto-create `.viepilot/requests/` files with `## Intake Source` block for write-back traceability
528
+ - [ ] Manifest Lifecycle (ENH-095): manifest checked/analyzed before Step 4; column_map overridden from manifest when not explicitly set; `--analyze` flag forces re-analysis
197
529
  - [ ] Write-back updates source (non-fatal on failure)
198
530
  - [ ] TRIAGE session report generated at `.viepilot/intake/TRIAGE-{timestamp}.md`
199
531
  </success_criteria>
@@ -211,6 +543,7 @@ options:
211
543
  | GitHub Copilot | ✅ Text fallback | Via `.agent.md` |
212
544
 
213
545
  **Prompts in this skill:**
546
+ - Setup wizard: channel type, display name, field config, preview+confirm (Step 0)
214
547
  - Channel selection (Step 3)
215
548
  - Ticket accept/decline multi-select (Step 5)
216
549
  - Decline reason (Step 5)
@@ -218,6 +551,8 @@ options:
218
551
  - Next action (Step 8)
219
552
 
220
553
  ## Capabilities
554
+ - **Setup wizard** (`--setup`): AUQ-driven channel configuration — writes directly to channels.json
555
+ - Read tickets from SharePoint sharing links (anonymous WOPI download, no credentials)
221
556
  - Read tickets from Excel/Microsoft 365 Online via Microsoft Graph API
222
557
  - Read tickets from Google Sheets via Sheets API v4
223
558
  - Read tickets from local CSV/TSV files
@@ -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-pause`, `/vp-pause`, "pause", "dừng", "tạm nghỉ"
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
  Prompt user conversationally to gather state info.
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.