viepilot 3.9.0 → 3.10.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,30 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
9
9
 
10
10
  ---
11
11
 
12
+ ## [3.10.0] - 2026-05-25
13
+
14
+ ### Added
15
+ - ENH-101: `vp-crystallize` now generates adapter-specific AI context files at Step 1F
16
+ - ENH-101: New `vp-tools context-files` subcommand generates CLAUDE.md (Claude Code),
17
+ GEMINI.md (Antigravity), AGENTS.md (Codex), .cursorrules + .cursor/rules/ (Cursor),
18
+ .github/copilot-instructions.md (Copilot) from `.viepilot/` sources
19
+ - ENH-101: `--all` flag generates all 5 adapter files simultaneously
20
+ - ENH-101: Content sourced from AI-GUIDE.md, PROJECT-CONTEXT.md, SYSTEM-RULES.md, STACKS.md
21
+
22
+ ---
23
+
24
+ ## [3.9.1] - 2026-05-25
25
+
26
+ ### Fixed
27
+ - BUG-031: `vp-tools hooks install` wrote wrong hook path (`~/.viepilot/hooks/
28
+ brainstorm-staleness.cjs`) which does not exist; corrected to
29
+ `{adapter.viepilotDir}/lib/hooks/brainstorm-staleness.cjs`; Stop hook no longer
30
+ exits non-zero each turn
31
+ - BUG-031: `vp-tools hooks install` (re-run) now detects and removes stale wrong-path
32
+ entries from settings.json before writing the correct entry
33
+
34
+ ---
35
+
12
36
  ## [3.9.0] - 2026-05-24
13
37
 
14
38
  ### Changed
package/bin/vp-tools.cjs CHANGED
@@ -1007,7 +1007,7 @@ ${colors.cyan}Examples:${colors.reset}
1007
1007
  matcher: {},
1008
1008
  hooks: [{
1009
1009
  type: 'command',
1010
- command: `node ${path.join(home, '.viepilot', 'hooks', 'brainstorm-staleness.cjs')}`
1010
+ command: `node ${path.join(adapter.viepilotDir(home), 'lib', 'hooks', 'brainstorm-staleness.cjs')}`
1011
1011
  }]
1012
1012
  }]
1013
1013
  }
@@ -1024,7 +1024,7 @@ ${colors.cyan}Examples:${colors.reset}
1024
1024
  }
1025
1025
  const home = os.homedir();
1026
1026
  const configPath = adapter.hooks.configFile(home);
1027
- const hookCommand = `node ${path.join(home, '.viepilot', 'hooks', 'brainstorm-staleness.cjs')}`;
1027
+ const hookCommand = `node ${path.join(adapter.viepilotDir(home), 'lib', 'hooks', 'brainstorm-staleness.cjs')}`;
1028
1028
 
1029
1029
  // Read existing settings.json (create if missing)
1030
1030
  let settings = {};
@@ -1032,15 +1032,22 @@ ${colors.cyan}Examples:${colors.reset}
1032
1032
  try { settings = JSON.parse(fs.readFileSync(configPath, 'utf8')); } catch (_e) { settings = {}; }
1033
1033
  }
1034
1034
 
1035
- // Merge hook entry — idempotent check
1035
+ // Merge hook entry — migration + idempotent check
1036
1036
  if (!settings.hooks) settings.hooks = {};
1037
1037
  if (!settings.hooks.Stop) settings.hooks.Stop = [];
1038
1038
 
1039
+ // Step A: remove any stale entry with the OLD wrong path
1040
+ const wrongPath = `node ${path.join(home, '.viepilot', 'hooks', 'brainstorm-staleness.cjs')}`;
1041
+ settings.hooks.Stop = settings.hooks.Stop.filter((entry) =>
1042
+ !(Array.isArray(entry.hooks) &&
1043
+ entry.hooks.some((h) => h.type === 'command' && h.command === wrongPath))
1044
+ );
1045
+
1046
+ // Step B: idempotent check for correct path
1039
1047
  const alreadyInstalled = settings.hooks.Stop.some((entry) =>
1040
1048
  Array.isArray(entry.hooks) &&
1041
1049
  entry.hooks.some((h) => h.type === 'command' && h.command === hookCommand)
1042
1050
  );
1043
-
1044
1051
  if (alreadyInstalled) {
1045
1052
  console.log(formatSuccess('ViePilot staleness hook already installed.'));
1046
1053
  console.log(` Config: ${configPath}`);
@@ -1203,6 +1210,44 @@ ${colors.cyan}Examples:${colors.reset}
1203
1210
  });
1204
1211
  },
1205
1212
 
1213
+ /**
1214
+ * ENH-101: Generate adapter context files (CLAUDE.md, GEMINI.md, AGENTS.md, .cursorrules, .github/copilot-instructions.md)
1215
+ * Usage: vp-tools context-files [--all]
1216
+ */
1217
+ 'context-files': (args) => {
1218
+ const { generateAll, generateClaudeMd, generateGeminiMd, generateAgentsMd,
1219
+ generateCursorRules, generateCursorMdc, generateCopilotInstructions }
1220
+ = require('../lib/context-file-generators.cjs');
1221
+ const allFlag = args.includes('--all');
1222
+ const projectRoot = process.cwd();
1223
+
1224
+ // detect adapter
1225
+ const adapterCtx = require('../lib/adapter-context.cjs');
1226
+ const adapterId = adapterCtx.detectAdapter ? adapterCtx.detectAdapter() : 'claude-code';
1227
+
1228
+ const targets = allFlag ? generateAll(projectRoot) : (() => {
1229
+ const map = {
1230
+ 'claude-code': [{ path: 'CLAUDE.md', content: generateClaudeMd(projectRoot) }],
1231
+ 'antigravity': [{ path: 'GEMINI.md', content: generateGeminiMd(projectRoot) }],
1232
+ 'codex': [{ path: 'AGENTS.md', content: generateAgentsMd(projectRoot) }],
1233
+ 'cursor-agent': [
1234
+ { path: '.cursorrules', content: generateCursorRules(projectRoot) },
1235
+ { path: '.cursor/rules/viepilot-context.mdc', content: generateCursorMdc(projectRoot) },
1236
+ ],
1237
+ 'copilot': [{ path: '.github/copilot-instructions.md', content: generateCopilotInstructions(projectRoot) }],
1238
+ };
1239
+ return map[adapterId] || map['claude-code'];
1240
+ })();
1241
+
1242
+ for (const { path: relPath, content } of targets) {
1243
+ const absPath = require('path').join(projectRoot, relPath);
1244
+ require('fs').mkdirSync(require('path').dirname(absPath), { recursive: true });
1245
+ require('fs').writeFileSync(absPath, content, 'utf8');
1246
+ console.log(formatSuccess(`Written: ${relPath}`));
1247
+ }
1248
+ process.exit(0);
1249
+ },
1250
+
1206
1251
  /**
1207
1252
  * ENH-073: Manage cross-project personas.
1208
1253
  * persona get → print active persona JSON
@@ -1620,6 +1665,7 @@ ${colors.cyan}Commands:${colors.reset}
1620
1665
  ${colors.bold}get-registry${colors.reset} [--id <id>] Output global skill registry as JSON
1621
1666
  ${colors.bold}scan-skills${colors.reset} Scan installed skills → ~/.viepilot/skill-registry.json
1622
1667
  ${colors.bold}check-update${colors.reset} [--silent] Check for latest ViePilot version on npm (24h cached)
1668
+ ${colors.bold}context-files${colors.reset} [--all] Generate adapter context files (CLAUDE.md, GEMINI.md, etc.)
1623
1669
  ${colors.bold}persona${colors.reset} <op> Manage user personas (get|infer|list|set|auto-switch|context)
1624
1670
  ${colors.bold}detect-adapter${colors.reset} [--json] Detect active adapter (claude-code/cursor/antigravity/codex/copilot)
1625
1671
  ${colors.bold}validate${colors.reset} --adapter <id> Validate adapter capability requirements; exits 1 on critical gaps
@@ -0,0 +1,147 @@
1
+ 'use strict';
2
+ const fs = require('fs');
3
+ const path = require('path');
4
+
5
+ /**
6
+ * Read a .viepilot/ source file, return '' if missing.
7
+ */
8
+ function readSource(projectRoot, relPath) {
9
+ try {
10
+ return fs.readFileSync(path.join(projectRoot, relPath), 'utf8');
11
+ } catch { return ''; }
12
+ }
13
+
14
+ /**
15
+ * Build the shared core block used by all adapters.
16
+ * Sources: AI-GUIDE.md, PROJECT-CONTEXT.md, SYSTEM-RULES.md, STACKS.md
17
+ */
18
+ function buildCoreBlock(projectRoot) {
19
+ const meta = readSource(projectRoot, '.viepilot/PROJECT-META.md');
20
+ const guide = readSource(projectRoot, '.viepilot/AI-GUIDE.md');
21
+ const context = readSource(projectRoot, '.viepilot/PROJECT-CONTEXT.md');
22
+ const rules = readSource(projectRoot, '.viepilot/SYSTEM-RULES.md');
23
+ const stacks = readSource(projectRoot, '.viepilot/STACKS.md');
24
+
25
+ // Extract project name from PROJECT-META.md header or package.json
26
+ let projectName = 'Project';
27
+ const nameMatch = meta.match(/^#\s+(.+)/m);
28
+ if (nameMatch) projectName = nameMatch[1].trim();
29
+ else {
30
+ try {
31
+ projectName = require(path.join(projectRoot, 'package.json')).name || 'Project';
32
+ } catch { /* noop */ }
33
+ }
34
+
35
+ return { projectName, guide, context, rules, stacks };
36
+ }
37
+
38
+ /**
39
+ * CLAUDE.md — Claude Code context file (full Markdown, no frontmatter)
40
+ */
41
+ function generateClaudeMd(projectRoot) {
42
+ const { projectName, guide, context, rules, stacks } = buildCoreBlock(projectRoot);
43
+ const sections = [];
44
+ sections.push(`# ${projectName} — Claude Code Context\n`);
45
+ if (guide) sections.push(`## Navigation\n\n${guide}`);
46
+ if (context) sections.push(`## Domain Context\n\n${context}`);
47
+ if (rules) sections.push(`## Coding Standards\n\n${rules}`);
48
+ if (stacks) sections.push(`## Stack\n\n${stacks}`);
49
+ sections.push(`\n## ViePilot Workflow\n\n` +
50
+ `- Run \`/vp-auto\` to execute planned phases\n` +
51
+ `- Run \`/vp-request\` to log bugs or features\n` +
52
+ `- Current state: \`.viepilot/TRACKER.md\`\n`);
53
+ return sections.join('\n\n---\n\n');
54
+ }
55
+
56
+ /**
57
+ * GEMINI.md — Antigravity / Gemini CLI context file
58
+ */
59
+ function generateGeminiMd(projectRoot) {
60
+ // Same structure as CLAUDE.md; different header note
61
+ const content = generateClaudeMd(projectRoot);
62
+ return content.replace('Claude Code Context', 'Gemini / Antigravity Context');
63
+ }
64
+
65
+ /**
66
+ * AGENTS.md — Codex CLI context file
67
+ * Codex is patch-based + sequential; emphasize apply_patch conventions
68
+ */
69
+ function generateAgentsMd(projectRoot) {
70
+ const { projectName, context, rules, stacks } = buildCoreBlock(projectRoot);
71
+ const sections = [];
72
+ sections.push(`# ${projectName} — Codex Agent Instructions\n`);
73
+ sections.push(`## Conventions\n\n` +
74
+ `- Use \`apply_patch\` for all file edits\n` +
75
+ `- No interactive prompts — work sequentially\n` +
76
+ `- Commit after each logical unit\n`);
77
+ if (context) sections.push(`## Domain Context\n\n${context}`);
78
+ if (rules) sections.push(`## Coding Standards\n\n${rules}`);
79
+ if (stacks) sections.push(`## Stack\n\n${stacks}`);
80
+ sections.push(`\n## ViePilot\n\nPhase state: \`.viepilot/TRACKER.md\`\n`);
81
+ return sections.join('\n\n---\n\n');
82
+ }
83
+
84
+ /**
85
+ * .cursorrules — Cursor legacy flat-file format
86
+ */
87
+ function generateCursorRules(projectRoot) {
88
+ const { projectName, context, rules, stacks } = buildCoreBlock(projectRoot);
89
+ const parts = [`# ${projectName} — Cursor Rules\n`];
90
+ if (rules) parts.push(rules);
91
+ if (context) parts.push(`## Domain\n\n${context}`);
92
+ if (stacks) parts.push(`## Stack\n\n${stacks}`);
93
+ return parts.join('\n\n');
94
+ }
95
+
96
+ /**
97
+ * .cursor/rules/viepilot-context.mdc — Cursor new MDC format
98
+ */
99
+ function generateCursorMdc(projectRoot) {
100
+ const body = generateCursorRules(projectRoot)
101
+ .replace(/^# .+\n/, ''); // strip header — MDC description field covers it
102
+ const { projectName } = buildCoreBlock(projectRoot);
103
+ return `---
104
+ description: ${projectName} coding standards and architecture context
105
+ globs: ["**/*"]
106
+ alwaysApply: true
107
+ ---
108
+
109
+ ${body}`;
110
+ }
111
+
112
+ /**
113
+ * .github/copilot-instructions.md — GitHub Copilot
114
+ * Copilot has shorter context; be concise, focus on coding standards
115
+ */
116
+ function generateCopilotInstructions(projectRoot) {
117
+ const { projectName, rules, stacks } = buildCoreBlock(projectRoot);
118
+ const parts = [`# Copilot Instructions — ${projectName}\n`];
119
+ if (rules) parts.push(rules);
120
+ if (stacks) parts.push(`## Stack\n\n${stacks}`);
121
+ return parts.join('\n\n');
122
+ }
123
+
124
+ /**
125
+ * Generate all 5 adapter context files.
126
+ * Returns { path: string, content: string }[] — caller writes them.
127
+ */
128
+ function generateAll(projectRoot) {
129
+ return [
130
+ { path: 'CLAUDE.md', content: generateClaudeMd(projectRoot) },
131
+ { path: 'GEMINI.md', content: generateGeminiMd(projectRoot) },
132
+ { path: 'AGENTS.md', content: generateAgentsMd(projectRoot) },
133
+ { path: '.cursorrules', content: generateCursorRules(projectRoot) },
134
+ { path: '.cursor/rules/viepilot-context.mdc', content: generateCursorMdc(projectRoot) },
135
+ { path: '.github/copilot-instructions.md', content: generateCopilotInstructions(projectRoot) },
136
+ ];
137
+ }
138
+
139
+ module.exports = {
140
+ generateClaudeMd,
141
+ generateGeminiMd,
142
+ generateAgentsMd,
143
+ generateCursorRules,
144
+ generateCursorMdc,
145
+ generateCopilotInstructions,
146
+ generateAll,
147
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "viepilot",
3
- "version": "3.9.0",
3
+ "version": "3.10.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": {
@@ -2172,8 +2172,40 @@ Append to `.viepilot/PROJECT-CONTEXT.md`:
2172
2172
  **Lock semantics**: once written, `## Skills` is the authoritative skill decision for the project. `vp-auto` reads it and **never re-prompts**.
2173
2173
  </step>
2174
2174
 
2175
+ <step name="adapter_context_files">
2176
+ ## Step 1F: Generate Adapter Context Files (ENH-101)
2177
+
2178
+ Generate the native AI context file for the active adapter so it starts with full project context.
2179
+
2180
+ **Detect adapter and generate:**
2181
+ ```bash
2182
+ node bin/vp-tools.cjs context-files
2183
+ # or with --all to generate all 5 adapters at once:
2184
+ node bin/vp-tools.cjs context-files --all
2185
+ ```
2186
+
2187
+ **What gets generated:**
2188
+ | Adapter | File |
2189
+ |---------|------|
2190
+ | Claude Code | `CLAUDE.md` (project root) |
2191
+ | Cursor | `.cursorrules` + `.cursor/rules/viepilot-context.mdc` |
2192
+ | Codex | `AGENTS.md` (project root) |
2193
+ | Antigravity | `GEMINI.md` (project root) |
2194
+ | GitHub Copilot | `.github/copilot-instructions.md` |
2195
+
2196
+ **Content sources** (from `.viepilot/` — skip if not yet generated):
2197
+ - `AI-GUIDE.md` → navigation + architecture summary
2198
+ - `PROJECT-CONTEXT.md` → domain knowledge, business rules
2199
+ - `SYSTEM-RULES.md` → coding standards, patterns
2200
+ - `STACKS.md` → tech stack, versions
2201
+
2202
+ **Note**: Re-running overwrites existing files with latest `.viepilot/` content.
2203
+ Step 1F is non-blocking — if `.viepilot/` sources are incomplete, outputs a partial file
2204
+ with a `<!-- viepilot: incomplete -->` comment at the top as a re-run reminder.
2205
+ </step>
2206
+
2175
2207
  <step name="cross_reference_gate">
2176
- ## Step 1F: Cross-Reference Gate (ENH-064)
2208
+ ## Step 1G: Cross-Reference Gate (ENH-064)
2177
2209
 
2178
2210
  Run when BOTH `architect_read_complete: true` AND `ui_direction_read_complete: true` are set in working notes:
2179
2211
 
@@ -2208,9 +2240,9 @@ Run when BOTH `architect_read_complete: true` AND `ui_direction_read_complete: t
2208
2240
  <step name="stakeholder_review_gate">
2209
2241
  ---
2210
2242
 
2211
- ## Step 1G: Stakeholder Review Gate (ENH-098)
2243
+ ## Step 1H: Stakeholder Review Gate (ENH-098)
2212
2244
 
2213
- **Trigger**: Runs automatically after Step 1F, before Step 2.
2245
+ **Trigger**: Runs automatically after Step 1G, before Step 2.
2214
2246
 
2215
2247
  **Skip conditions**:
2216
2248
  - `--no-stakeholders` flag passed to `/vp-crystallize`