viepilot 1.14.0 → 2.1.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 +98 -0
- package/README.md +3 -3
- package/bin/viepilot.cjs +7 -5
- package/bin/vp-tools.cjs +193 -0
- package/dev-install.sh +34 -13
- package/docs/user/features/hooks.md +93 -0
- package/lib/adapters/claude-code.cjs +42 -0
- package/lib/adapters/cursor.cjs +31 -0
- package/lib/adapters/index.cjs +26 -0
- package/lib/hooks/brainstorm-staleness.cjs +231 -0
- package/lib/viepilot-config.cjs +103 -0
- package/lib/viepilot-install.cjs +128 -153
- package/package.json +1 -1
- package/skills/vp-audit/SKILL.md +21 -21
- package/skills/vp-auto/SKILL.md +21 -7
- package/skills/vp-brainstorm/SKILL.md +42 -36
- package/skills/vp-crystallize/SKILL.md +22 -16
- package/skills/vp-debug/SKILL.md +2 -2
- package/skills/vp-docs/SKILL.md +7 -7
- package/skills/vp-evolve/SKILL.md +25 -12
- package/skills/vp-info/SKILL.md +23 -23
- package/skills/vp-pause/SKILL.md +5 -5
- package/skills/vp-request/SKILL.md +12 -12
- package/skills/vp-resume/SKILL.md +4 -4
- package/skills/vp-rollback/SKILL.md +3 -3
- package/skills/vp-status/SKILL.md +4 -4
- package/skills/vp-task/SKILL.md +2 -2
- package/skills/vp-ui-components/SKILL.md +12 -12
- package/skills/vp-update/SKILL.md +17 -17
- package/templates/architect/apis.html +11 -10
- package/templates/architect/architect-actions.js +217 -0
- package/templates/architect/architecture.html +8 -7
- package/templates/architect/data-flow.html +5 -4
- package/templates/architect/decisions.html +4 -3
- package/templates/architect/deployment.html +10 -9
- package/templates/architect/erd.html +7 -6
- package/templates/architect/feature-map.html +5 -4
- package/templates/architect/sequence-diagram.html +6 -5
- package/templates/architect/style.css +146 -0
- package/templates/architect/tech-notes.html +3 -2
- package/templates/architect/tech-stack.html +8 -7
- package/templates/architect/user-use-cases.html +8 -7
- package/templates/project/AI-GUIDE.md +49 -49
- package/workflows/audit.md +3 -3
- package/workflows/autonomous.md +38 -5
- package/workflows/brainstorm.md +398 -222
- package/workflows/crystallize.md +46 -33
- package/workflows/debug.md +9 -9
- package/workflows/documentation.md +5 -5
- package/workflows/evolve.md +44 -12
- package/workflows/pause-work.md +2 -2
- package/workflows/request.md +8 -8
- package/workflows/resume-work.md +1 -1
- package/workflows/rollback.md +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,104 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [2.1.0] - 2026-04-08
|
|
11
|
+
|
|
12
|
+
### Added (FEAT-012 — Phase 54)
|
|
13
|
+
- **Brainstorm staleness hook**: `lib/hooks/brainstorm-staleness.cjs` — Claude Code `Stop` event handler; fires after each AI response in a brainstorm session; detects stale architect HTML items via keyword matching; marks `data-arch-stale="true"` (amber badge, flag-only); non-blocking (exit 0 always)
|
|
14
|
+
- `bin/vp-tools.cjs`: `hooks install [--adapter <id>]` subcommand — merges ViePilot hook entry into `~/.claude/settings.json`; idempotent (re-run safe)
|
|
15
|
+
- `docs/user/features/hooks.md` — new user doc: install instructions, adapter table, troubleshooting
|
|
16
|
+
- `workflows/brainstorm.md`: `architect_delta_sync` step now references automatic hook mode; `/hooks-install` command added
|
|
17
|
+
|
|
18
|
+
### Tests
|
|
19
|
+
- Added `tests/unit/brainstorm-staleness-hook.test.js` (20 tests, 4 groups) — session discovery, keyword detection, HTML patching, install command
|
|
20
|
+
- Total: 578 tests (was 558)
|
|
21
|
+
|
|
22
|
+
## [2.0.0] - 2026-04-08
|
|
23
|
+
|
|
24
|
+
### Added (FEAT-013 — Phase 53)
|
|
25
|
+
- **Dynamic agent adapter system**: `lib/adapters/` module with `claude-code.cjs`, `cursor.cjs`, `index.cjs` registry — each platform is a self-contained adapter (skillsDir, viepilotDir, executionContextBase, hooks config, installSubdirs, isAvailable, pathRewrite)
|
|
26
|
+
- `lib/viepilot-install.cjs`: `buildInstallPlan()` now resolves all platform paths from adapter registry — no hardcoded `.cursor/` constants; single adapter loop replaces cursor + claude-code if-blocks
|
|
27
|
+
- `bin/viepilot.cjs`: TARGETS sourced from adapter registry; default non-interactive target = `claude-code`
|
|
28
|
+
- `dev-install.sh`: `VIEPILOT_ADAPTER` env var (default: `claude-code`); backward-compat `VIEPILOT_INSTALL_PROFILE` alias preserved
|
|
29
|
+
- `bin/vp-tools.cjs`: `hooks scaffold [--adapter <id>]` subcommand — prints `~/.claude/settings.json` hook registration snippet for Claude Code; Cursor: prints explanation
|
|
30
|
+
|
|
31
|
+
### BREAKING CHANGES
|
|
32
|
+
- Default install target changes from `cursor-ide` → `claude-code` (`~/.claude/`)
|
|
33
|
+
- Cursor users: set `VIEPILOT_ADAPTER=cursor-agent` (or `VIEPILOT_INSTALL_PROFILE=cursor-agent`) or pass `--target cursor-agent`
|
|
34
|
+
- `normalizeInstallEnv({})` default profile is now `claude-code` (was `cursor-ide`)
|
|
35
|
+
|
|
36
|
+
### Tests
|
|
37
|
+
- Added `tests/unit/viepilot-adapters.test.js` (19 tests, 4 groups) — adapter interface, registry, install plan, dev-install.sh
|
|
38
|
+
- Updated `tests/unit/viepilot-install.test.js` — 7 tests updated to reflect claude-code default; cursor-explicit tests use `VIEPILOT_INSTALL_PROFILE: 'cursor-ide'`
|
|
39
|
+
- Total: 558 tests (was 539)
|
|
40
|
+
|
|
41
|
+
## [1.19.0] - 2026-04-08
|
|
42
|
+
|
|
43
|
+
### Added (ENH-034 — Phase 52)
|
|
44
|
+
- **vp-brainstorm UI: Architect Delta Sync** — new `architect_delta_sync` step in `workflows/brainstorm.md`: bridges UI Direction Mode and Architect HTML workspace; when a UI session surfaces architect-related gaps, the step parses session for deltas, maps them to affected pages (using existing trigger keyword lists), updates `<tr>` / `<div class="card">` HTML content, marks changed items `data-updated="true"`, and records delta in `notes.md ## architect_sync`
|
|
45
|
+
- `/sync-arch` command: manual trigger for architect delta sync at any point in a brainstorm session
|
|
46
|
+
- `notes.md` YAML schema extended with `## architect_sync` section (synced_at, source_session, trigger, changes)
|
|
47
|
+
- `templates/architect/style.css`: `.arch-gap-badge` (amber) + `[data-arch-stale="true"]` amber left-border indicator — visually distinct from `.updated` (yellow = changed); light-mode overrides included
|
|
48
|
+
- `templates/architect/architect-actions.js`: `markStale(id, reason)` + `injectStaleBadges()` — auto-scans `[data-arch-stale="true"]` on DOMContentLoaded and injects amber "⚠ gap" badges; `window.vpMarkStale` exposed for browser console use
|
|
49
|
+
|
|
50
|
+
### Tests
|
|
51
|
+
- Added `tests/unit/vp-enh034-architect-delta-sync.test.js` (14 tests, 3 groups) — all pass (539 total)
|
|
52
|
+
|
|
53
|
+
## [1.18.1] - 2026-04-07
|
|
54
|
+
|
|
55
|
+
### Fixed (BUG-010 — Phase 51)
|
|
56
|
+
- **Approve/Edit buttons missing on Mermaid diagram cards:** added `data-arch-id` + `data-arch-title` to all 9 diagram cards across 6 pages (architecture ARCH-DIAG1/2, data-flow DF-DIAG1/2, erd ERD-DIAG1, use-cases UC-DIAG1, sequence SEQ-DIAG1/2, deployment DEP-DIAG1)
|
|
57
|
+
|
|
58
|
+
### Tests
|
|
59
|
+
- Added Test Group 5 to `tests/unit/vp-enh033-architect-item-actions.test.js` (9 new tests) — 525 total
|
|
60
|
+
|
|
61
|
+
## [1.18.0] - 2026-04-07
|
|
62
|
+
|
|
63
|
+
### Added (ENH-033 — Phase 50)
|
|
64
|
+
- **Architect HTML item actions:** stable `data-arch-id` IDs on every item across all 11 content pages (decisions D1–D2, architecture C1–C4, erd E1–E4, use-cases UC1–UC5, apis A1–A9, deployment DEP1–DEP7, data-flow DF1, sequence SEQ1–SEQ2, tech-stack TS1–TS6, tech-notes TN1, features F1–F3)
|
|
65
|
+
- `templates/architect/architect-actions.js` — new shared vanilla JS: `approvePrompt()`, `editPrompt()`, `copyText()` with clipboard API + execCommand fallback, button injection on DOMContentLoaded
|
|
66
|
+
- **✅ Approve button**: copies `[ARCH:{slug}:{id}] APPROVE — "{title}" on {slug} page. No changes needed.`
|
|
67
|
+
- **✏️ Edit button**: copies `[ARCH:{slug}:{id}] EDIT — "{title}" on {slug} page. Current: "{excerpt}". What should I change?`
|
|
68
|
+
- `templates/architect/style.css`: `.arch-id-badge`, `.arch-item-actions`, `.arch-btn`, `.arch-btn-approve`, `.arch-btn-edit`, `.arch-btn.copied`, `.arch-actions-cell` — hover-reveal pattern, light-mode overrides
|
|
69
|
+
- `workflows/brainstorm.md`: **Architect Item Actions (ENH-033)** section — isolation rule, APPROVE/EDIT prompt handling spec, cross-page cascade prohibition
|
|
70
|
+
|
|
71
|
+
### Tests
|
|
72
|
+
- Added `tests/unit/vp-enh033-architect-item-actions.test.js` (50 tests, 4 groups) — all pass (516 total)
|
|
73
|
+
|
|
74
|
+
## [1.17.0] - 2026-04-06
|
|
75
|
+
|
|
76
|
+
### Added (ENH-032 — Phase 49)
|
|
77
|
+
- **Language configuration system:** `~/.viepilot/config.json` stores `language.communication` and `language.document` (defaults: `en`/`en`)
|
|
78
|
+
- `lib/viepilot-config.cjs` — new module: `readConfig`, `writeConfig`, `resetConfig`, `getConfigPath`; deep-merge with defaults; missing-file safe
|
|
79
|
+
- `vp-tools config get/set/reset` — CLI commands to read and write language config
|
|
80
|
+
- `lib/viepilot-install.cjs`: `language_config_prompt` step writes config at end of install; `--yes` uses defaults without prompting
|
|
81
|
+
- `workflows/crystallize.md`: Step 0-A `load_language_config` — reads `DOCUMENT_LANG` + `COMMUNICATION_LANG` for file generation and session messages
|
|
82
|
+
- `workflows/brainstorm.md`: Step 0 `detect_session_language` — reads `BRAINSTORM_LANG` for file storage; user session language overrides config
|
|
83
|
+
- `workflows/autonomous.md`: `load_language_config` in Initialize — `COMMUNICATION_LANG` for banners and control-point messages
|
|
84
|
+
- `skills/vp-crystallize/SKILL.md`, `skills/vp-brainstorm/SKILL.md`, `skills/vp-auto/SKILL.md`: ENH-032 language config notes
|
|
85
|
+
|
|
86
|
+
### Tests
|
|
87
|
+
- Added `tests/unit/vp-enh032-language-config.test.js` (18 tests) — all pass (466 total)
|
|
88
|
+
|
|
89
|
+
## [1.16.0] - 2026-04-06
|
|
90
|
+
|
|
91
|
+
### Changed (ENH-031 — Phase 48)
|
|
92
|
+
- **Language standardization (English-primary):** all 12 workflows, all 16 skills, and `templates/project/AI-GUIDE.md` converted to English; Vietnamese retained only in `cursor_skill_adapter` invocation trigger keywords and UI scope signal detection patterns
|
|
93
|
+
|
|
94
|
+
### Tests
|
|
95
|
+
- Added `tests/unit/vp-enh031-language-standardization.test.js` (63 tests) — all pass (448 total)
|
|
96
|
+
- Updated `vp-fe010-ui-walkthrough-contracts.test.js` + `vp-enh026-ui-extraction-contracts.test.js` to match translated English strings
|
|
97
|
+
|
|
98
|
+
## [1.15.0] - 2026-04-06
|
|
99
|
+
|
|
100
|
+
### Fixed (BUG-009 — Phase 47)
|
|
101
|
+
- **Task path guard:** `workflows/evolve.md` now enforces repo-relative paths in task `## Paths` blocks (prevents generating `~/.claude/...` paths that would edit the live install instead of source)
|
|
102
|
+
- **Preflight validation:** `workflows/autonomous.md` aborts task execution if `## Paths` contains `~/` or absolute path — error message names offending path and task file
|
|
103
|
+
- `skills/vp-evolve/SKILL.md` + `skills/vp-auto/SKILL.md`: path convention documented in `<context>`
|
|
104
|
+
|
|
105
|
+
### Tests
|
|
106
|
+
- Added `tests/unit/vp-bug009-path-guard.test.js` (13 tests) — all pass (385 total)
|
|
107
|
+
|
|
10
108
|
## [1.14.0] - 2026-04-06
|
|
11
109
|
|
|
12
110
|
### Changed (ENH-030 — Phase 46)
|
package/README.md
CHANGED
|
@@ -2,12 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
**Autonomous Vibe Coding Framework / Bộ khung phát triển tự động có kiểm soát**
|
|
4
4
|
|
|
5
|
-
[](CHANGELOG.md)
|
|
6
6
|
[](LICENSE)
|
|
7
7
|
[](#skills-reference)
|
|
8
8
|
[](#workflows)
|
|
9
9
|
[](#templates)
|
|
10
|
-
[](tests/)
|
|
11
11
|
[](https://github.com/0-CODE/viepilot)
|
|
12
12
|
|
|
13
13
|
**Versioning:** Shield **1.9.5** is the **ViePilot framework SemVer** tracked in `.viepilot/TRACKER.md` and `CHANGELOG.md`. The npm `package.json` field `version` (**1.9.5**) is the Node package identifier for this repo; use the framework version for milestone releases and docs.
|
|
@@ -28,7 +28,7 @@ Nếu ViePilot giúp ích cho bạn, bạn có thể ủng hộ một ly cafe:
|
|
|
28
28
|
|
|
29
29
|
| Chỉ số / Metric | Giá trị / Value |
|
|
30
30
|
|-----------------|-----------------|
|
|
31
|
-
| Total LOC | **~
|
|
31
|
+
| Total LOC | **~34,308+** (`.md`, `.js`, `.cjs`, `.yml`, `.json`, `.sh`; không gồm `node_modules`) |
|
|
32
32
|
| Skills | **16** |
|
|
33
33
|
| Workflows | **12** |
|
|
34
34
|
| Templates | **17** (Project: 12, Phase: 5) |
|
package/bin/viepilot.cjs
CHANGED
|
@@ -14,11 +14,13 @@ const { buildInstallPlan, applyInstallPlan, resolveViepilotPackageRoot } = requi
|
|
|
14
14
|
'lib',
|
|
15
15
|
'viepilot-install.cjs',
|
|
16
16
|
));
|
|
17
|
+
const { adapters: adapterMap } = require(path.join(__dirname, '..', 'lib', 'adapters', 'index.cjs'));
|
|
17
18
|
|
|
19
|
+
// UI target list — keep cursor-agent and cursor-ide as distinct choices for users.
|
|
18
20
|
const TARGETS = [
|
|
19
|
-
{ id: 'claude-code',
|
|
21
|
+
{ id: 'claude-code', label: adapterMap['claude-code'].name + ' (default)' },
|
|
20
22
|
{ id: 'cursor-agent', label: 'Cursor Agent' },
|
|
21
|
-
{ id: 'cursor-ide',
|
|
23
|
+
{ id: 'cursor-ide', label: 'Cursor IDE' },
|
|
22
24
|
];
|
|
23
25
|
|
|
24
26
|
function printHelp() {
|
|
@@ -254,7 +256,7 @@ async function interactiveTargetSelection() {
|
|
|
254
256
|
function runInstallViaNode(selectedTargets, dryRun) {
|
|
255
257
|
const fallbackRoot = path.join(__dirname, '..');
|
|
256
258
|
const pkgRoot = resolveViepilotPackageRoot(fallbackRoot) || fallbackRoot;
|
|
257
|
-
const profile = selectedTargets[0] || '
|
|
259
|
+
const profile = selectedTargets[0] || 'claude-code';
|
|
258
260
|
const env = {
|
|
259
261
|
...process.env,
|
|
260
262
|
VIEPILOT_AUTO_YES: '1',
|
|
@@ -307,8 +309,8 @@ async function installCommand(rawArgs) {
|
|
|
307
309
|
|
|
308
310
|
if (!selectedTargets) {
|
|
309
311
|
if (options.yes) {
|
|
310
|
-
selectedTargets =
|
|
311
|
-
console.log('No --target provided with --yes; defaulting to
|
|
312
|
+
selectedTargets = ['claude-code'];
|
|
313
|
+
console.log('No --target provided with --yes; defaulting to claude-code.');
|
|
312
314
|
} else {
|
|
313
315
|
selectedTargets = await interactiveTargetSelection();
|
|
314
316
|
if (selectedTargets.length === 0) {
|
package/bin/vp-tools.cjs
CHANGED
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
const fs = require('fs');
|
|
14
14
|
const path = require('path');
|
|
15
15
|
const readline = require('readline');
|
|
16
|
+
const os = require('os');
|
|
16
17
|
|
|
17
18
|
const {
|
|
18
19
|
validators,
|
|
@@ -25,6 +26,7 @@ const {
|
|
|
25
26
|
|
|
26
27
|
const viepilotInfo = require(path.join(__dirname, '../lib/viepilot-info.cjs'));
|
|
27
28
|
const viepilotUpdate = require(path.join(__dirname, '../lib/viepilot-update.cjs'));
|
|
29
|
+
const viepilotConfig = require(path.join(__dirname, '../lib/viepilot-config.cjs'));
|
|
28
30
|
|
|
29
31
|
// ============================================================================
|
|
30
32
|
// Output Formatting (TTY-aware)
|
|
@@ -946,6 +948,195 @@ const commands = {
|
|
|
946
948
|
}, null, 2));
|
|
947
949
|
},
|
|
948
950
|
|
|
951
|
+
/**
|
|
952
|
+
* Hooks scaffold + install — FEAT-013/FEAT-012 adapter hook management
|
|
953
|
+
* Usage: vp-tools hooks <scaffold|install> [--adapter <id>]
|
|
954
|
+
*/
|
|
955
|
+
hooks: (args) => {
|
|
956
|
+
const sub = args[0];
|
|
957
|
+
if (!sub || sub === 'help') {
|
|
958
|
+
console.log(`${colors.cyan}Usage:${colors.reset}
|
|
959
|
+
vp-tools hooks scaffold [--adapter <id>]
|
|
960
|
+
vp-tools hooks install [--adapter <id>]
|
|
961
|
+
|
|
962
|
+
${colors.cyan}Subcommands:${colors.reset}
|
|
963
|
+
scaffold Print hook config snippet for the target adapter (default: claude-code)
|
|
964
|
+
install Install ViePilot staleness hook into adapter config file (idempotent)
|
|
965
|
+
|
|
966
|
+
${colors.cyan}Options:${colors.reset}
|
|
967
|
+
--adapter <id> Adapter ID: claude-code (default), cursor-agent, cursor-ide
|
|
968
|
+
|
|
969
|
+
${colors.cyan}Examples:${colors.reset}
|
|
970
|
+
${colors.gray}$${colors.reset} vp-tools hooks scaffold
|
|
971
|
+
${colors.gray}$${colors.reset} vp-tools hooks scaffold --adapter cursor-agent
|
|
972
|
+
${colors.gray}$${colors.reset} vp-tools hooks install
|
|
973
|
+
${colors.gray}$${colors.reset} vp-tools hooks install --adapter claude-code`);
|
|
974
|
+
return;
|
|
975
|
+
}
|
|
976
|
+
|
|
977
|
+
// ── shared adapter resolution ──────────────────────────────────────────
|
|
978
|
+
const adapterArgIdx = args.indexOf('--adapter');
|
|
979
|
+
const adapterId = adapterArgIdx !== -1 ? args[adapterArgIdx + 1] : 'claude-code';
|
|
980
|
+
if (adapterArgIdx !== -1 && !adapterId) {
|
|
981
|
+
console.error(formatError('--adapter requires a value (e.g. claude-code, cursor-agent)'));
|
|
982
|
+
process.exit(1);
|
|
983
|
+
}
|
|
984
|
+
const { getAdapter } = require(path.join(__dirname, '../lib/adapters/index.cjs'));
|
|
985
|
+
let adapter;
|
|
986
|
+
try {
|
|
987
|
+
adapter = getAdapter(adapterId);
|
|
988
|
+
} catch (e) {
|
|
989
|
+
console.error(formatError(e.message));
|
|
990
|
+
process.exit(1);
|
|
991
|
+
}
|
|
992
|
+
|
|
993
|
+
if (sub === 'scaffold') {
|
|
994
|
+
if (!adapter.hooks || !adapter.hooks.configFile) {
|
|
995
|
+
console.log(`Adapter "${adapterId}" (${adapter.name}) does not use a settings.json hook config.`);
|
|
996
|
+
console.log(`For Cursor, hooks are configured via .cursorrules or project MDC files.`);
|
|
997
|
+
process.exit(0);
|
|
998
|
+
}
|
|
999
|
+
const home = os.homedir();
|
|
1000
|
+
const configPath = adapter.hooks.configFile(home);
|
|
1001
|
+
console.log(`\nViePilot hooks scaffold for: ${adapter.name}`);
|
|
1002
|
+
console.log(`Config file: ${configPath}\n`);
|
|
1003
|
+
console.log(`Add the following to your ${configPath}:\n`);
|
|
1004
|
+
console.log(JSON.stringify({
|
|
1005
|
+
hooks: {
|
|
1006
|
+
Stop: [{
|
|
1007
|
+
matcher: {},
|
|
1008
|
+
hooks: [{
|
|
1009
|
+
type: 'command',
|
|
1010
|
+
command: `node ${path.join(home, '.viepilot', 'hooks', 'brainstorm-staleness.cjs')}`
|
|
1011
|
+
}]
|
|
1012
|
+
}]
|
|
1013
|
+
}
|
|
1014
|
+
}, null, 2));
|
|
1015
|
+
console.log(`\nRun 'vp-tools hooks install' to register this hook automatically.`);
|
|
1016
|
+
process.exit(0);
|
|
1017
|
+
}
|
|
1018
|
+
|
|
1019
|
+
if (sub === 'install') {
|
|
1020
|
+
if (!adapter.hooks || !adapter.hooks.configFile) {
|
|
1021
|
+
console.log(`Adapter "${adapterId}" (${adapter.name}) does not support settings.json hooks.`);
|
|
1022
|
+
console.log(`For Cursor, add hooks via .cursorrules or project MDC files.`);
|
|
1023
|
+
process.exit(0);
|
|
1024
|
+
}
|
|
1025
|
+
const home = os.homedir();
|
|
1026
|
+
const configPath = adapter.hooks.configFile(home);
|
|
1027
|
+
const hookCommand = `node ${path.join(home, '.viepilot', 'hooks', 'brainstorm-staleness.cjs')}`;
|
|
1028
|
+
|
|
1029
|
+
// Read existing settings.json (create if missing)
|
|
1030
|
+
let settings = {};
|
|
1031
|
+
if (fs.existsSync(configPath)) {
|
|
1032
|
+
try { settings = JSON.parse(fs.readFileSync(configPath, 'utf8')); } catch (_e) { settings = {}; }
|
|
1033
|
+
}
|
|
1034
|
+
|
|
1035
|
+
// Merge hook entry — idempotent check
|
|
1036
|
+
if (!settings.hooks) settings.hooks = {};
|
|
1037
|
+
if (!settings.hooks.Stop) settings.hooks.Stop = [];
|
|
1038
|
+
|
|
1039
|
+
const alreadyInstalled = settings.hooks.Stop.some((entry) =>
|
|
1040
|
+
Array.isArray(entry.hooks) &&
|
|
1041
|
+
entry.hooks.some((h) => h.type === 'command' && h.command === hookCommand)
|
|
1042
|
+
);
|
|
1043
|
+
|
|
1044
|
+
if (alreadyInstalled) {
|
|
1045
|
+
console.log(formatSuccess('ViePilot staleness hook already installed.'));
|
|
1046
|
+
console.log(` Config: ${configPath}`);
|
|
1047
|
+
process.exit(0);
|
|
1048
|
+
}
|
|
1049
|
+
|
|
1050
|
+
settings.hooks.Stop.push({
|
|
1051
|
+
matcher: {},
|
|
1052
|
+
hooks: [{ type: 'command', command: hookCommand }],
|
|
1053
|
+
});
|
|
1054
|
+
|
|
1055
|
+
try {
|
|
1056
|
+
fs.mkdirSync(path.dirname(configPath), { recursive: true });
|
|
1057
|
+
fs.writeFileSync(configPath, JSON.stringify(settings, null, 2) + '\n', 'utf8');
|
|
1058
|
+
} catch (e) {
|
|
1059
|
+
console.error(formatError(`Failed to write ${configPath}: ${e.message}`));
|
|
1060
|
+
process.exit(1);
|
|
1061
|
+
}
|
|
1062
|
+
|
|
1063
|
+
console.log(formatSuccess('ViePilot staleness hook installed.'));
|
|
1064
|
+
console.log(` Config: ${configPath}`);
|
|
1065
|
+
console.log(` Command: ${hookCommand}`);
|
|
1066
|
+
process.exit(0);
|
|
1067
|
+
}
|
|
1068
|
+
|
|
1069
|
+
console.error(formatError(`Unknown hooks subcommand: ${sub}`, 'Use: scaffold, install'));
|
|
1070
|
+
process.exit(1);
|
|
1071
|
+
},
|
|
1072
|
+
|
|
1073
|
+
/**
|
|
1074
|
+
* Config get/set/reset — ENH-032 language configuration
|
|
1075
|
+
*/
|
|
1076
|
+
config: (args) => {
|
|
1077
|
+
const sub = args[0];
|
|
1078
|
+
if (!sub || sub === 'help') {
|
|
1079
|
+
console.log(`${colors.cyan}Usage:${colors.reset}
|
|
1080
|
+
vp-tools config get <key>
|
|
1081
|
+
vp-tools config set <key> <value>
|
|
1082
|
+
vp-tools config reset
|
|
1083
|
+
|
|
1084
|
+
${colors.cyan}Keys:${colors.reset}
|
|
1085
|
+
language.communication Language for AI communication/banners (e.g. en, vi)
|
|
1086
|
+
language.document Language for generated project files (e.g. en, vi)
|
|
1087
|
+
|
|
1088
|
+
${colors.cyan}Examples:${colors.reset}
|
|
1089
|
+
${colors.gray}$${colors.reset} vp-tools config get language.communication
|
|
1090
|
+
${colors.gray}$${colors.reset} vp-tools config set language.communication vi
|
|
1091
|
+
${colors.gray}$${colors.reset} vp-tools config reset`);
|
|
1092
|
+
return;
|
|
1093
|
+
}
|
|
1094
|
+
if (sub === 'get') {
|
|
1095
|
+
const key = args[1];
|
|
1096
|
+
if (!key) {
|
|
1097
|
+
console.error(formatError('Missing key', 'Usage: vp-tools config get <key>'));
|
|
1098
|
+
process.exit(1);
|
|
1099
|
+
}
|
|
1100
|
+
const cfg = viepilotConfig.readConfig();
|
|
1101
|
+
const parts = key.split('.');
|
|
1102
|
+
let val = cfg;
|
|
1103
|
+
for (const p of parts) {
|
|
1104
|
+
if (val == null || typeof val !== 'object') { val = undefined; break; }
|
|
1105
|
+
val = val[p];
|
|
1106
|
+
}
|
|
1107
|
+
if (val === undefined) {
|
|
1108
|
+
console.error(formatError(`Unknown config key: ${key}`));
|
|
1109
|
+
process.exit(1);
|
|
1110
|
+
}
|
|
1111
|
+
console.log(val);
|
|
1112
|
+
return;
|
|
1113
|
+
}
|
|
1114
|
+
if (sub === 'set') {
|
|
1115
|
+
const key = args[1];
|
|
1116
|
+
const value = args[2];
|
|
1117
|
+
if (!key || value === undefined) {
|
|
1118
|
+
console.error(formatError('Usage: vp-tools config set <key> <value>'));
|
|
1119
|
+
process.exit(1);
|
|
1120
|
+
}
|
|
1121
|
+
const parts = key.split('.');
|
|
1122
|
+
if (parts.length !== 2) {
|
|
1123
|
+
console.error(formatError('Key must be in format <section>.<field> (e.g. language.communication)'));
|
|
1124
|
+
process.exit(1);
|
|
1125
|
+
}
|
|
1126
|
+
const [section, field] = parts;
|
|
1127
|
+
viepilotConfig.writeConfig({ [section]: { [field]: value } });
|
|
1128
|
+
console.log(formatSuccess(`Set ${key} = ${value}`));
|
|
1129
|
+
return;
|
|
1130
|
+
}
|
|
1131
|
+
if (sub === 'reset') {
|
|
1132
|
+
viepilotConfig.resetConfig();
|
|
1133
|
+
console.log(formatSuccess('Config reset to defaults (communication=en, document=en)'));
|
|
1134
|
+
return;
|
|
1135
|
+
}
|
|
1136
|
+
console.error(formatError(`Unknown config subcommand: ${sub}`, 'Use get, set, or reset'));
|
|
1137
|
+
process.exit(1);
|
|
1138
|
+
},
|
|
1139
|
+
|
|
949
1140
|
/**
|
|
950
1141
|
* Help
|
|
951
1142
|
*/
|
|
@@ -1088,6 +1279,8 @@ ${colors.cyan}Commands:${colors.reset}
|
|
|
1088
1279
|
${colors.bold}info${colors.reset} [--json] Show ViePilot version, npm latest, skills/workflows
|
|
1089
1280
|
${colors.bold}update${colors.reset} [--dry-run] Update viepilot via npm (use --yes non-interactive)
|
|
1090
1281
|
${colors.bold}conflicts${colors.reset} Check for potential conflicts
|
|
1282
|
+
${colors.bold}hooks${colors.reset} scaffold|install [--adapter] scaffold: print snippet; install: write to settings.json
|
|
1283
|
+
${colors.bold}config${colors.reset} <get|set|reset> Read/write language config (~/.viepilot/config.json)
|
|
1091
1284
|
${colors.bold}save-state${colors.reset} Save current state for precise resume
|
|
1092
1285
|
${colors.bold}help${colors.reset} [command] Show help (optionally for specific command)
|
|
1093
1286
|
|
package/dev-install.sh
CHANGED
|
@@ -3,8 +3,11 @@
|
|
|
3
3
|
# ViePilot Development Installation Script
|
|
4
4
|
# Installs development build without symlink dependency by default
|
|
5
5
|
#
|
|
6
|
-
# Optional
|
|
7
|
-
#
|
|
6
|
+
# Optional env vars:
|
|
7
|
+
# VIEPILOT_ADAPTER — target platform: claude-code (default), cursor, cursor-agent, cursor-ide
|
|
8
|
+
# VIEPILOT_INSTALL_PROFILE — backward-compat alias for VIEPILOT_ADAPTER
|
|
9
|
+
# VIEPILOT_AUTO_YES — skip confirmation prompt (set to 1)
|
|
10
|
+
# VIEPILOT_SYMLINK_SKILLS=1 — symlink each skills/vp-* instead of copy
|
|
8
11
|
|
|
9
12
|
set -e
|
|
10
13
|
|
|
@@ -17,10 +20,27 @@ NC='\033[0m'
|
|
|
17
20
|
|
|
18
21
|
# Get script directory (project root)
|
|
19
22
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
20
|
-
CURSOR_SKILLS_DIR="$HOME/.cursor/skills"
|
|
21
|
-
VIEPILOT_DIR="$HOME/.cursor/viepilot"
|
|
22
23
|
AUTO_YES="${VIEPILOT_AUTO_YES:-0}"
|
|
23
|
-
|
|
24
|
+
|
|
25
|
+
# Backward compat: VIEPILOT_INSTALL_PROFILE accepted as alias for VIEPILOT_ADAPTER
|
|
26
|
+
ADAPTER="${VIEPILOT_ADAPTER:-${VIEPILOT_INSTALL_PROFILE:-claude-code}}"
|
|
27
|
+
|
|
28
|
+
case "$ADAPTER" in
|
|
29
|
+
claude-code)
|
|
30
|
+
SKILLS_DIR="$HOME/.claude/skills"
|
|
31
|
+
VIEPILOT_DIR="$HOME/.claude/viepilot"
|
|
32
|
+
PROFILE_LABEL="Claude Code"
|
|
33
|
+
;;
|
|
34
|
+
cursor|cursor-agent|cursor-ide)
|
|
35
|
+
SKILLS_DIR="$HOME/.cursor/skills"
|
|
36
|
+
VIEPILOT_DIR="$HOME/.cursor/viepilot"
|
|
37
|
+
PROFILE_LABEL="Cursor"
|
|
38
|
+
;;
|
|
39
|
+
*)
|
|
40
|
+
echo "Unknown adapter: $ADAPTER. Use: claude-code (default), cursor-agent, cursor-ide"
|
|
41
|
+
exit 1
|
|
42
|
+
;;
|
|
43
|
+
esac
|
|
24
44
|
|
|
25
45
|
echo -e "${BLUE}"
|
|
26
46
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
@@ -33,9 +53,10 @@ if [ "${VIEPILOT_SYMLINK_SKILLS:-0}" = "1" ]; then
|
|
|
33
53
|
else
|
|
34
54
|
echo -e "${YELLOW}Development mode installation (copy-first for reliability)${NC}"
|
|
35
55
|
fi
|
|
36
|
-
echo " Source:
|
|
37
|
-
echo "
|
|
38
|
-
echo "
|
|
56
|
+
echo " Source: $SCRIPT_DIR"
|
|
57
|
+
echo " Skills: $SKILLS_DIR"
|
|
58
|
+
echo " Viepilot: $VIEPILOT_DIR"
|
|
59
|
+
echo " Adapter: $ADAPTER ($PROFILE_LABEL)"
|
|
39
60
|
echo ""
|
|
40
61
|
|
|
41
62
|
check_cloc_dependency() {
|
|
@@ -68,7 +89,7 @@ echo ""
|
|
|
68
89
|
echo -e "${BLUE}Removing old installations...${NC}"
|
|
69
90
|
|
|
70
91
|
# Remove old skill installations
|
|
71
|
-
for skill in "$
|
|
92
|
+
for skill in "$SKILLS_DIR"/vp-*/; do
|
|
72
93
|
if [ -d "$skill" ] || [ -L "$skill" ]; then
|
|
73
94
|
rm -rf "$skill"
|
|
74
95
|
echo " Removed: $(basename "$skill")"
|
|
@@ -85,7 +106,7 @@ echo ""
|
|
|
85
106
|
echo -e "${BLUE}Installing skills...${NC}"
|
|
86
107
|
|
|
87
108
|
# Install skills: copy (default) or symlink when VIEPILOT_SYMLINK_SKILLS=1
|
|
88
|
-
mkdir -p "$
|
|
109
|
+
mkdir -p "$SKILLS_DIR"
|
|
89
110
|
for skill in "$SCRIPT_DIR"/skills/vp-*/; do
|
|
90
111
|
skill_name=$(basename "$skill")
|
|
91
112
|
if [ "${VIEPILOT_SYMLINK_SKILLS:-0}" = "1" ]; then
|
|
@@ -94,10 +115,10 @@ for skill in "$SCRIPT_DIR"/skills/vp-*/; do
|
|
|
94
115
|
else
|
|
95
116
|
skill_abs=$(cd "$skill" && pwd)
|
|
96
117
|
fi
|
|
97
|
-
ln -sfn "$skill_abs" "$
|
|
118
|
+
ln -sfn "$skill_abs" "$SKILLS_DIR/$skill_name"
|
|
98
119
|
echo -e " ${GREEN}✓${NC} $skill_name (symlink)"
|
|
99
120
|
else
|
|
100
|
-
cp -R "$skill" "$
|
|
121
|
+
cp -R "$skill" "$SKILLS_DIR/$skill_name"
|
|
101
122
|
echo -e " ${GREEN}✓${NC} $skill_name"
|
|
102
123
|
fi
|
|
103
124
|
done
|
|
@@ -129,7 +150,7 @@ echo -e " ${GREEN}✓${NC} ui-components"
|
|
|
129
150
|
check_cloc_dependency
|
|
130
151
|
|
|
131
152
|
# Count installed
|
|
132
|
-
SKILL_COUNT=$(ls -d "$
|
|
153
|
+
SKILL_COUNT=$(ls -d "$SKILLS_DIR"/vp-*/ 2>/dev/null | wc -l | tr -d ' ')
|
|
133
154
|
WORKFLOW_COUNT=$(ls "$SCRIPT_DIR"/workflows/*.md 2>/dev/null | wc -l | tr -d ' ')
|
|
134
155
|
|
|
135
156
|
echo ""
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# ViePilot Hooks
|
|
2
|
+
|
|
3
|
+
ViePilot integrates with Claude Code's hook system to automate actions after each AI response. Hooks are shell commands that fire at specific Claude Code events — no manual typing required.
|
|
4
|
+
|
|
5
|
+
## Brainstorm Staleness Hook
|
|
6
|
+
|
|
7
|
+
**Event**: `Stop` (fires after each Claude response)
|
|
8
|
+
**Script**: `~/.viepilot/hooks/brainstorm-staleness.cjs`
|
|
9
|
+
**Config**: `~/.claude/settings.json`
|
|
10
|
+
|
|
11
|
+
### What it does
|
|
12
|
+
|
|
13
|
+
After each exchange in a `vp-brainstorm` session, the hook automatically:
|
|
14
|
+
|
|
15
|
+
1. Finds the most recently modified brainstorm session file (`notes.md` or `session-*.md`)
|
|
16
|
+
2. Scans session content for keywords that match architect HTML pages
|
|
17
|
+
3. Marks relevant architect items with `data-arch-stale="true"` — rendered as an amber "⚠ gap" badge in the browser
|
|
18
|
+
4. Exits cleanly — if it fails or finds nothing, your session continues normally
|
|
19
|
+
|
|
20
|
+
This is **flag-only** (Option A) — the hook does not rewrite architect HTML content, only marks items for review. Use `/sync-arch` to perform a full content sync.
|
|
21
|
+
|
|
22
|
+
### Install
|
|
23
|
+
|
|
24
|
+
Run once per machine:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
node bin/vp-tools.cjs hooks install
|
|
28
|
+
# or if installed globally:
|
|
29
|
+
vp-tools hooks install
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
This writes the hook entry into `~/.claude/settings.json`. Running it again is safe (idempotent).
|
|
33
|
+
|
|
34
|
+
To verify the install:
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
cat ~/.claude/settings.json | grep brainstorm-staleness
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### Preview (scaffold)
|
|
41
|
+
|
|
42
|
+
To see what will be written without installing:
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
node bin/vp-tools.cjs hooks scaffold
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### Cursor users
|
|
49
|
+
|
|
50
|
+
Cursor does not support `settings.json` hook events. Use `/sync-arch` manually within the brainstorm session to trigger architect delta sync (ENH-034).
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## Adapter support
|
|
55
|
+
|
|
56
|
+
Hook behavior is adapter-dependent:
|
|
57
|
+
|
|
58
|
+
| Adapter | Config file | Programmatic events |
|
|
59
|
+
|---------|-------------|---------------------|
|
|
60
|
+
| `claude-code` | `~/.claude/settings.json` | Stop, PreToolUse, PostToolUse, UserPromptSubmit, … |
|
|
61
|
+
| `cursor` | `.cursorrules` / MDC | None (no programmatic hook events) |
|
|
62
|
+
|
|
63
|
+
Check your adapter's supported events:
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
node bin/vp-tools.cjs hooks scaffold --adapter claude-code
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
---
|
|
70
|
+
|
|
71
|
+
## Adding custom hooks
|
|
72
|
+
|
|
73
|
+
1. Create your hook script in `~/.viepilot/hooks/` (or anywhere accessible)
|
|
74
|
+
2. Get the config format: `vp-tools hooks scaffold`
|
|
75
|
+
3. Add your command to the relevant event array in `~/.claude/settings.json`
|
|
76
|
+
|
|
77
|
+
Claude Code passes a JSON payload on stdin with `session_id`, `cwd`, `transcript_path`, and event-specific data. Your hook script should read from stdin and always exit 0.
|
|
78
|
+
|
|
79
|
+
---
|
|
80
|
+
|
|
81
|
+
## Troubleshooting
|
|
82
|
+
|
|
83
|
+
**Hook not firing**
|
|
84
|
+
- Confirm `~/.claude/settings.json` has a `Stop` entry: `vp-tools hooks install` (idempotent)
|
|
85
|
+
- Restart your Claude Code session after modifying `settings.json`
|
|
86
|
+
|
|
87
|
+
**No stale badges appearing**
|
|
88
|
+
- The hook only marks stale if session content matches architect keyword triggers
|
|
89
|
+
- Check that the brainstorm session notes file exists in `.viepilot/ui-direction/` or `docs/brainstorm/`
|
|
90
|
+
- The architect HTML files must exist at `templates/architect/` (repo) or `~/.claude/viepilot/templates/architect/` (installed)
|
|
91
|
+
|
|
92
|
+
**Hook error messages**
|
|
93
|
+
Hook errors appear on stderr (not in Claude's response). Check terminal output or Claude Code logs.
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
const os = require('os');
|
|
3
|
+
const path = require('path');
|
|
4
|
+
const fs = require('fs');
|
|
5
|
+
|
|
6
|
+
module.exports = {
|
|
7
|
+
id: 'claude-code',
|
|
8
|
+
name: 'Claude Code',
|
|
9
|
+
// Paths (home-relative; resolved at install time)
|
|
10
|
+
skillsDir: (home) => path.join(home, '.claude', 'skills'),
|
|
11
|
+
viepilotDir: (home) => path.join(home, '.claude', 'viepilot'),
|
|
12
|
+
// execution_context base for skill .md files
|
|
13
|
+
executionContextBase: '.claude/viepilot',
|
|
14
|
+
// Path rewrite rule: when mirroring skills, rewrite from → to in *.md files
|
|
15
|
+
pathRewrite: { from: '.cursor/viepilot', to: '.claude/viepilot' },
|
|
16
|
+
// Hooks configuration (Claude Code settings.json)
|
|
17
|
+
hooks: {
|
|
18
|
+
configFile: (home) => path.join(home, '.claude', 'settings.json'),
|
|
19
|
+
schema: 'claude-code',
|
|
20
|
+
supportedEvents: [
|
|
21
|
+
'SessionStart', 'SessionEnd', 'Stop', 'StopFailure',
|
|
22
|
+
'UserPromptSubmit', 'PreToolUse', 'PostToolUse', 'PostToolUseFailure',
|
|
23
|
+
'FileChanged', 'SubagentStart', 'SubagentStop',
|
|
24
|
+
'TaskCreated', 'TaskCompleted', 'PreCompact', 'PostCompact'
|
|
25
|
+
]
|
|
26
|
+
},
|
|
27
|
+
// Files/dirs to install into viepilotDir
|
|
28
|
+
installSubdirs: [
|
|
29
|
+
'workflows',
|
|
30
|
+
path.join('templates', 'project'),
|
|
31
|
+
path.join('templates', 'phase'),
|
|
32
|
+
path.join('templates', 'architect'),
|
|
33
|
+
'bin',
|
|
34
|
+
'lib',
|
|
35
|
+
'ui-components'
|
|
36
|
+
],
|
|
37
|
+
// Detection: is this platform available on the current machine?
|
|
38
|
+
isAvailable: (home) => {
|
|
39
|
+
const h = home || os.homedir();
|
|
40
|
+
return fs.existsSync(path.join(h, '.claude'));
|
|
41
|
+
}
|
|
42
|
+
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
const os = require('os');
|
|
3
|
+
const path = require('path');
|
|
4
|
+
const fs = require('fs');
|
|
5
|
+
|
|
6
|
+
module.exports = {
|
|
7
|
+
id: 'cursor', // maps to cursor-agent and cursor-ide (same paths)
|
|
8
|
+
name: 'Cursor',
|
|
9
|
+
skillsDir: (home) => path.join(home, '.cursor', 'skills'),
|
|
10
|
+
viepilotDir: (home) => path.join(home, '.cursor', 'viepilot'),
|
|
11
|
+
executionContextBase: '.cursor/viepilot',
|
|
12
|
+
pathRewrite: null, // no rewrite needed (source is cursor-first)
|
|
13
|
+
hooks: {
|
|
14
|
+
configFile: null, // Cursor uses .cursorrules/MDC, not settings.json hooks
|
|
15
|
+
schema: 'cursor',
|
|
16
|
+
supportedEvents: [] // no programmatic hook events
|
|
17
|
+
},
|
|
18
|
+
installSubdirs: [
|
|
19
|
+
'workflows',
|
|
20
|
+
path.join('templates', 'project'),
|
|
21
|
+
path.join('templates', 'phase'),
|
|
22
|
+
path.join('templates', 'architect'),
|
|
23
|
+
'bin',
|
|
24
|
+
'lib',
|
|
25
|
+
'ui-components'
|
|
26
|
+
],
|
|
27
|
+
isAvailable: (home) => {
|
|
28
|
+
const h = home || os.homedir();
|
|
29
|
+
return fs.existsSync(path.join(h, '.cursor'));
|
|
30
|
+
}
|
|
31
|
+
};
|