viepilot 3.11.0 → 3.12.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.
package/CHANGELOG.md CHANGED
@@ -9,6 +9,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
9
9
 
10
10
  ---
11
11
 
12
+ ## [3.12.0] - 2026-05-25
13
+ ### Changed
14
+ - `workflows/autonomous.md`: tracker-agent now rewrites Current State block (find-and-replace)
15
+ instead of appending — TRACKER.md stays ≤ 20 lines regardless of phase count (DEBT-002)
16
+ - `workflows/autonomous.md`: auto-compact size guard — if TRACKER.md > 400 lines, runs
17
+ `vp-tools tracker compact` before reading (DEBT-002)
18
+ - `workflows/autonomous.md`: Decision Log capped at 20 rows; older rows archived to TRACKER-HISTORY.md
19
+
20
+ ### Added
21
+ - `lib/tracker-compact.cjs`: compact() + rewriteCurrentState() — rescue tool for bloated TRACKER.md files
22
+ - `bin/vp-tools.cjs`: `tracker compact [--keep N]` subcommand — compact bloated TRACKER.md, archive history to TRACKER-HISTORY.md
23
+
24
+ ---
25
+
12
26
  ## [3.11.0] - 2026-05-25
13
27
 
14
28
  ### Added
package/bin/vp-tools.cjs CHANGED
@@ -1389,6 +1389,70 @@ ${colors.cyan}Examples:${colors.reset}
1389
1389
  console.log();
1390
1390
  },
1391
1391
 
1392
+ /**
1393
+ * Compact bloated TRACKER.md and archive history (DEBT-002)
1394
+ * Usage: vp-tools tracker compact [--keep N] [--dry-run] [--path <file>]
1395
+ */
1396
+ tracker: (args) => {
1397
+ const sub = args[0];
1398
+
1399
+ if (!sub || sub === 'help') {
1400
+ console.log(`${colors.cyan}Usage:${colors.reset}
1401
+ vp-tools tracker compact [--keep N] [--dry-run] [--path <file>]
1402
+
1403
+ ${colors.cyan}Subcommands:${colors.reset}
1404
+ compact Compact bloated TRACKER.md, archive history
1405
+
1406
+ ${colors.cyan}Options:${colors.reset}
1407
+ --keep N Number of rows to keep (default: 5)
1408
+ --dry-run Preview changes without writing
1409
+ --path <file> Path to TRACKER.md (default: .viepilot/TRACKER.md)
1410
+
1411
+ ${colors.cyan}Examples:${colors.reset}
1412
+ ${colors.gray}$${colors.reset} vp-tools tracker compact
1413
+ ${colors.gray}$${colors.reset} vp-tools tracker compact --keep 10 --dry-run
1414
+ ${colors.gray}$${colors.reset} vp-tools tracker compact --path /path/to/TRACKER.md`);
1415
+ return;
1416
+ }
1417
+
1418
+ if (sub === 'compact') {
1419
+ // Parse options
1420
+ const keepIdx = args.indexOf('--keep');
1421
+ const keepN = keepIdx !== -1 ? parseInt(args[keepIdx + 1], 10) : 5;
1422
+ const isDryRun = args.includes('--dry-run');
1423
+
1424
+ const pathIdx = args.indexOf('--path');
1425
+ const argPath = pathIdx !== -1 ? args[pathIdx + 1] : null;
1426
+
1427
+ const trackerPath = argPath || path.join(process.cwd(), '.viepilot', 'TRACKER.md');
1428
+
1429
+ try {
1430
+ const { compact } = require(path.join(__dirname, '../lib/tracker-compact.cjs'));
1431
+ const result = compact(trackerPath, { keep: keepN, dryRun: isDryRun });
1432
+
1433
+ if (isDryRun) {
1434
+ console.log(`[dry-run] Would compact ${trackerPath}`);
1435
+ console.log(` Lines to remove: ${result.linesRemoved}`);
1436
+ console.log(` Rows to archive: ${result.rowsArchived}`);
1437
+ console.log(' No files written.');
1438
+ } else {
1439
+ console.log(formatSuccess(`Compacted ${trackerPath}`));
1440
+ console.log(` Lines removed: ${result.linesRemoved}`);
1441
+ console.log(` Rows archived: ${result.rowsArchived}`);
1442
+ console.log(` History file: ${result.historyFile}`);
1443
+ console.log(` Result: ${result.trackerLines} lines remaining`);
1444
+ }
1445
+ process.exit(0);
1446
+ } catch (err) {
1447
+ console.error(formatError('tracker compact error:', err.message));
1448
+ process.exit(1);
1449
+ }
1450
+ }
1451
+
1452
+ console.error(formatError(`Unknown tracker subcommand: ${sub}`, 'Use: compact'));
1453
+ process.exit(1);
1454
+ },
1455
+
1392
1456
  /**
1393
1457
  * Validate adapter capability requirements (FEAT-021 Phase 127)
1394
1458
  * Usage: vp-tools validate --adapter <id>
@@ -1618,6 +1682,20 @@ ${colors.cyan}Examples:${colors.reset}
1618
1682
  'vp-tools update --global --dry-run',
1619
1683
  ],
1620
1684
  },
1685
+ 'tracker': {
1686
+ usage: 'vp-tools tracker compact [--keep N] [--dry-run] [--path <file>]',
1687
+ description: 'Compact bloated TRACKER.md and archive history',
1688
+ options: [
1689
+ '--keep N: Number of rows to keep (default: 5)',
1690
+ '--dry-run: Preview changes without writing',
1691
+ '--path <file>: Path to TRACKER.md (default: .viepilot/TRACKER.md)',
1692
+ ],
1693
+ examples: [
1694
+ 'vp-tools tracker compact',
1695
+ 'vp-tools tracker compact --keep 10 --dry-run',
1696
+ 'vp-tools tracker compact --path /path/to/TRACKER.md',
1697
+ ],
1698
+ },
1621
1699
  };
1622
1700
 
1623
1701
  if (command && commandHelp[command]) {
@@ -1669,6 +1747,7 @@ ${colors.cyan}Commands:${colors.reset}
1669
1747
  ${colors.bold}persona${colors.reset} <op> Manage user personas (get|infer|list|set|auto-switch|context)
1670
1748
  ${colors.bold}detect-adapter${colors.reset} [--json] Detect active adapter (claude-code/cursor/antigravity/codex/copilot)
1671
1749
  ${colors.bold}validate${colors.reset} --adapter <id> Validate adapter capability requirements; exits 1 on critical gaps
1750
+ ${colors.bold}tracker${colors.reset} compact [--keep N] Compact bloated TRACKER.md, archive history
1672
1751
  ${colors.bold}help${colors.reset} [command] Show help (optionally for specific command)
1673
1752
 
1674
1753
  ${colors.cyan}Examples:${colors.reset}
@@ -643,3 +643,39 @@ controls output style, stack preferences, and team-size assumptions injected int
643
643
  ### Output
644
644
  - `~/.viepilot/config.json` updated
645
645
  - `## User Persona` injected into each skill session
646
+
647
+ ---
648
+
649
+ ## /vp-qa
650
+
651
+ **Version**: v1.0.0 (fw 2.19.0)
652
+ **Phase**: 148 (ENH-100)
653
+
654
+ Scan-first QA agent team generator. Researches the codebase first (stack detection, file sampling,
655
+ domain mapping), then generates a context-tailored QA agent team using the Write tool directly — no
656
+ templates. Number of agents and their domains are determined by research output.
657
+
658
+ ### Triggers
659
+ `vp-qa`, `/vp-qa`, "qa agents", "generate qa team", "quality assurance agents"
660
+
661
+ ### Flags
662
+ - `/vp-qa` — auto-detect adapter + stack, generate QA team
663
+ - `/vp-qa --run` — generate + immediately invoke qa-orchestrator
664
+ - `/vp-qa --focus sec` — bias research toward security domains
665
+ - `/vp-qa --focus perf` — bias research toward performance domains
666
+ - `/vp-qa --target <id>` — override adapter detection (claude-code / cursor-agent / antigravity / codex / copilot)
667
+
668
+ ### Output by Adapter
669
+ | Adapter | Output Location | Format |
670
+ |---------|----------------|--------|
671
+ | claude-code | `.claude/agents/qa-orchestrator.md` + `*-scanner.md` | Multi-file, parallel fan-out |
672
+ | cursor-agent | `.cursor/rules/qa-checklist.mdc` | Single MDC rule file |
673
+ | codex | `AGENTS.md` (appended) | `## QA Agent Instructions` section |
674
+ | antigravity | `.agents/skills/qa-orchestrator/SKILL.md` | Multi-file skill |
675
+ | copilot | `.github/agents/qa-orchestrator.agent.md` | Single agent file |
676
+
677
+ ### Flow
678
+ 1. **Research phase**: reads codebase, detects stack, samples files, reads `agents/qa-templates/rules/{stack}.md`
679
+ 2. **Adapter routing**: `lib/qa-router.cjs` determines output spec
680
+ 3. **LLM generates**: Write tool creates agent files directly from research output
681
+ 4. **Post-scan**: generated `qa-orchestrator` creates `.viepilot/requests/BUG-{N}.md` entries; `AskUserQuestion` for accept/decline
@@ -0,0 +1,124 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+
4
+ function rewriteCurrentState(content, newBlock) {
5
+ const match = content.match(/^## Current State\n/m);
6
+ if (!match) return content;
7
+ const start = match.index + match[0].length;
8
+ const after = content.slice(start).search(/^## /m);
9
+ const before = content.slice(0, match.index);
10
+ const nextSection = after === -1 ? '' : content.slice(start + after);
11
+ return before + '## Current State\n' + newBlock + nextSection;
12
+ }
13
+
14
+ function parseStanzas(lines) {
15
+ const stanzas = [];
16
+ let current = [];
17
+ for (const line of lines) {
18
+ if (line.match(/\*\*Current Phase\*\*/)) {
19
+ if (current.length > 0) stanzas.push(current);
20
+ current = [line];
21
+ } else if (current.length > 0) {
22
+ current.push(line);
23
+ }
24
+ }
25
+ if (current.length > 0) stanzas.push(current);
26
+ return stanzas;
27
+ }
28
+
29
+ function compact(trackerPath, options = {}) {
30
+ if (!fs.existsSync(trackerPath)) {
31
+ throw new Error('TRACKER.md not found: ' + trackerPath);
32
+ }
33
+
34
+ const { keep = 5, dryRun = false } = options;
35
+ const maxRows = Math.max(keep * 4, 20);
36
+ const content = fs.readFileSync(trackerPath, 'utf8');
37
+ const lines = content.split('\n');
38
+
39
+ let linesRemoved = 0;
40
+ let rowsArchived = 0;
41
+ let archiveBlock = '';
42
+
43
+ // Process Current State
44
+ const csIdx = lines.findIndex(l => l === '## Current State');
45
+ const nextIdx = lines.findIndex((l, i) => i > csIdx && l.match(/^## /));
46
+
47
+ if (csIdx !== -1 && nextIdx !== -1) {
48
+ const csLines = lines.slice(csIdx + 1, nextIdx);
49
+ const stanzas = parseStanzas(csLines);
50
+ if (stanzas.length > 1) {
51
+ const archived = stanzas.slice(0, -1).map(s => s.join('\n')).join('\n');
52
+ linesRemoved = archived.split('\n').length;
53
+ archiveBlock += '## Archived Current State\n' + archived + '\n\n';
54
+ }
55
+ }
56
+
57
+ // Process Decision Log
58
+ const dlIdx = lines.findIndex(l => l === '## Decision Log');
59
+ const dlEnd = nextIdx > dlIdx ? nextIdx : lines.length;
60
+
61
+ if (dlIdx !== -1) {
62
+ const tlines = lines.slice(dlIdx + 1, dlEnd);
63
+ let hIdx = -1, sIdx = -1;
64
+ for (let i = 0; i < tlines.length; i++) {
65
+ if (tlines[i].match(/\|\s*Date\s*\|/)) hIdx = i;
66
+ if (hIdx !== -1 && tlines[i].match(/^\|\s*[-:]+/)) {
67
+ sIdx = i;
68
+ break;
69
+ }
70
+ }
71
+ if (hIdx !== -1 && sIdx !== -1) {
72
+ const rows = tlines.slice(sIdx + 1).filter(l => l.trim().match(/^\|/));
73
+ if (rows.length > maxRows) {
74
+ rowsArchived = rows.length - maxRows;
75
+ archiveBlock += '## Archived Decision Log Rows\n' + rows.slice(0, -maxRows).join('\n') + '\n';
76
+ }
77
+ }
78
+ }
79
+
80
+ if (!dryRun && archiveBlock) {
81
+ const histPath = path.join(path.dirname(trackerPath), 'TRACKER-HISTORY.md');
82
+ fs.appendFileSync(histPath, '\n---\n# TRACKER Archive — ' + new Date().toISOString().split('T')[0] + '\n\n' + archiveBlock);
83
+
84
+ // Rewrite TRACKER.md
85
+ let newContent = content;
86
+ if (csIdx !== -1 && nextIdx !== -1) {
87
+ const stanzas = parseStanzas(lines.slice(csIdx + 1, nextIdx));
88
+ if (stanzas.length > 0) {
89
+ newContent = rewriteCurrentState(newContent, stanzas[stanzas.length - 1].join('\n') + '\n');
90
+ }
91
+ }
92
+
93
+ if (dlIdx !== -1) {
94
+ const tlines = lines.slice(dlIdx + 1, dlEnd);
95
+ let hIdx = -1, sIdx = -1;
96
+ for (let i = 0; i < tlines.length; i++) {
97
+ if (tlines[i].match(/\|\s*Date\s*\|/)) hIdx = i;
98
+ if (hIdx !== -1 && tlines[i].match(/^\|\s*[-:]+/)) {
99
+ sIdx = i;
100
+ break;
101
+ }
102
+ }
103
+ if (hIdx !== -1 && sIdx !== -1) {
104
+ const rows = tlines.slice(sIdx + 1).filter(l => l.trim().match(/^\|/));
105
+ const kept = rows.slice(-maxRows);
106
+ const newTable = [tlines[hIdx], tlines[sIdx], ...kept].join('\n');
107
+ const before = lines.slice(0, dlIdx + 1).join('\n');
108
+ const after = lines.slice(dlEnd).join('\n');
109
+ newContent = before + '\n' + newTable + '\n' + after;
110
+ }
111
+ }
112
+
113
+ fs.writeFileSync(trackerPath, newContent, 'utf8');
114
+ }
115
+
116
+ return {
117
+ linesRemoved,
118
+ rowsArchived,
119
+ historyFile: path.join(path.dirname(trackerPath), 'TRACKER-HISTORY.md'),
120
+ trackerLines: lines.length - linesRemoved
121
+ };
122
+ }
123
+
124
+ module.exports = { compact, rewriteCurrentState };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "viepilot",
3
- "version": "3.11.0",
3
+ "version": "3.12.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": {
@@ -74,6 +74,13 @@ Parse `{{VP_ARGS}}` for flags:
74
74
  Load context:
75
75
  ```bash
76
76
  cat .viepilot/AI-GUIDE.md
77
+
78
+ # Size guard — auto-compact if TRACKER.md is bloated (> 400 lines)
79
+ if [ -f ".viepilot/TRACKER.md" ] && [ "$(wc -l < .viepilot/TRACKER.md)" -gt 400 ]; then
80
+ echo "⚠️ TRACKER.md is large ($(wc -l < .viepilot/TRACKER.md) lines) — auto-compacting..."
81
+ node bin/vp-tools.cjs tracker compact --keep 5
82
+ fi
83
+
77
84
  cat .viepilot/TRACKER.md
78
85
  cat .viepilot/ROADMAP.md
79
86
  ```
@@ -907,9 +914,10 @@ After phase quality gate passes, send a desktop + phone alert:
907
914
  ```
908
915
  Agent({ subagent_type: "tracker-agent",
909
916
  description: "Update TRACKER.md — phase {N} complete",
910
- prompt: "operation: update-current-state. data: Last completed phase {N} — {phase_name}. version: {version}."
917
+ prompt: "operation: rewrite-current-state. data: Last completed phase {N} — {phase_name}. version: {version}. IMPORTANT: REPLACE the entire '## Current State' section (find the section, overwrite it) — do NOT append new lines. The section must stay ≤ 20 lines total. Also cap '## Decision Log' table at 20 rows — if > 20 rows exist, archive older rows to .viepilot/TRACKER-HISTORY.md."
911
918
  })
912
919
  ```
920
+ > Rescue bloated TRACKER.md: `node bin/vp-tools.cjs tracker compact [--keep N]` (DEBT-002)
913
921
  7. Push branch + tags — spawn vp-git-agent:
914
922
  ```
915
923
  Agent({ subagent_type: "vp-git-agent",