viepilot 1.14.0 → 2.4.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.
Files changed (58) hide show
  1. package/CHANGELOG.md +191 -0
  2. package/README.md +27 -17
  3. package/bin/viepilot.cjs +19 -9
  4. package/bin/vp-tools.cjs +193 -0
  5. package/docs/user/features/adapters.md +74 -0
  6. package/docs/user/features/hooks.md +93 -0
  7. package/lib/adapters/antigravity.cjs +33 -0
  8. package/lib/adapters/claude-code.cjs +42 -0
  9. package/lib/adapters/codex.cjs +34 -0
  10. package/lib/adapters/cursor.cjs +32 -0
  11. package/lib/adapters/index.cjs +28 -0
  12. package/lib/hooks/brainstorm-staleness.cjs +231 -0
  13. package/lib/viepilot-config.cjs +103 -0
  14. package/lib/viepilot-install.cjs +125 -152
  15. package/package.json +1 -3
  16. package/skills/vp-audit/SKILL.md +23 -23
  17. package/skills/vp-auto/SKILL.md +23 -9
  18. package/skills/vp-brainstorm/SKILL.md +44 -38
  19. package/skills/vp-crystallize/SKILL.md +25 -19
  20. package/skills/vp-debug/SKILL.md +4 -4
  21. package/skills/vp-docs/SKILL.md +8 -8
  22. package/skills/vp-evolve/SKILL.md +26 -13
  23. package/skills/vp-info/SKILL.md +24 -24
  24. package/skills/vp-pause/SKILL.md +7 -7
  25. package/skills/vp-request/SKILL.md +14 -14
  26. package/skills/vp-resume/SKILL.md +6 -6
  27. package/skills/vp-rollback/SKILL.md +4 -4
  28. package/skills/vp-status/SKILL.md +4 -4
  29. package/skills/vp-task/SKILL.md +2 -2
  30. package/skills/vp-ui-components/SKILL.md +14 -14
  31. package/skills/vp-update/SKILL.md +18 -18
  32. package/templates/architect/apis.html +11 -10
  33. package/templates/architect/architect-actions.js +217 -0
  34. package/templates/architect/architecture.html +8 -7
  35. package/templates/architect/data-flow.html +5 -4
  36. package/templates/architect/decisions.html +4 -3
  37. package/templates/architect/deployment.html +10 -9
  38. package/templates/architect/erd.html +7 -6
  39. package/templates/architect/feature-map.html +5 -4
  40. package/templates/architect/sequence-diagram.html +6 -5
  41. package/templates/architect/style.css +146 -0
  42. package/templates/architect/tech-notes.html +3 -2
  43. package/templates/architect/tech-stack.html +8 -7
  44. package/templates/architect/user-use-cases.html +8 -7
  45. package/templates/project/AI-GUIDE.md +49 -49
  46. package/workflows/audit.md +3 -3
  47. package/workflows/autonomous.md +70 -5
  48. package/workflows/brainstorm.md +398 -222
  49. package/workflows/crystallize.md +51 -33
  50. package/workflows/debug.md +9 -9
  51. package/workflows/documentation.md +5 -5
  52. package/workflows/evolve.md +46 -12
  53. package/workflows/pause-work.md +2 -2
  54. package/workflows/request.md +8 -8
  55. package/workflows/resume-work.md +1 -1
  56. package/workflows/rollback.md +1 -1
  57. package/dev-install.sh +0 -150
  58. package/install.sh +0 -125
@@ -7,6 +7,8 @@ const fs = require('fs');
7
7
  const path = require('path');
8
8
  const os = require('os');
9
9
  const { resolveViepilotPackageRoot } = require('./viepilot-info.cjs');
10
+ const { writeConfig } = require('./viepilot-config.cjs');
11
+ const { getAdapter } = require('./adapters/index.cjs');
10
12
 
11
13
  /** @see docs/dev/global-profiles.md (FEAT-009) */
12
14
  const VIEPILOT_PROFILE_MAP_SEED = `# ViePilot profile map
@@ -24,7 +26,7 @@ Machine-level registry for reusable org/client profiles. Add rows when you creat
24
26
  function normalizeInstallEnv(envSource = process.env) {
25
27
  return {
26
28
  autoYes: envSource.VIEPILOT_AUTO_YES === '1',
27
- profile: envSource.VIEPILOT_INSTALL_PROFILE || 'cursor-ide',
29
+ profile: envSource.VIEPILOT_INSTALL_PROFILE || 'claude-code',
28
30
  addPath: envSource.VIEPILOT_ADD_PATH === '1',
29
31
  symlinkSkills: envSource.VIEPILOT_SYMLINK_SKILLS === '1',
30
32
  };
@@ -85,16 +87,34 @@ function buildInstallPlan(packageRoot, envSource = process.env, opts = {}) {
85
87
  const env = normalizeInstallEnv(envSource);
86
88
  const home =
87
89
  opts.overrideHomedir != null ? path.resolve(opts.overrideHomedir) : os.homedir();
88
- const cursorSkillsDir = path.join(home, '.cursor', 'skills');
89
- const viepilotDir = path.join(home, '.cursor', 'viepilot');
90
90
  const viepilotUserDataDir = path.join(home, '.viepilot');
91
91
  const viepilotProfilesDir = path.join(viepilotUserDataDir, 'profiles');
92
92
  const viepilotProfileMapPath = path.join(viepilotUserDataDir, 'profile-map.md');
93
93
 
94
94
  const installTargets = Array.isArray(opts.installTargets) ? opts.installTargets : [];
95
- const installClaudeSkills = installTargets.includes('claude-code');
96
- const claudeSkillsDir = installClaudeSkills ? path.join(home, '.claude', 'skills') : null;
97
- const claudeViepilotDir = installClaudeSkills ? path.join(home, '.claude', 'viepilot') : null;
95
+
96
+ // Resolve target IDs to adapters.
97
+ // Backward compat: if no installTargets, infer from env.profile (cursor-agent/cursor-ide cursor).
98
+ // Default: claude-code (FEAT-013 — Claude Code is primary platform).
99
+ let targetIds;
100
+ if (installTargets.length > 0) {
101
+ targetIds = installTargets;
102
+ } else if (env.profile === 'cursor-agent' || env.profile === 'cursor-ide') {
103
+ targetIds = [env.profile];
104
+ } else {
105
+ targetIds = ['claude-code'];
106
+ }
107
+
108
+ // Deduplicate by adapter canonical id (cursor-agent + cursor-ide → one cursor install).
109
+ const seenAdapterIds = new Set();
110
+ const selectedAdapters = [];
111
+ for (const id of targetIds) {
112
+ const adapter = getAdapter(id);
113
+ if (!seenAdapterIds.has(adapter.id)) {
114
+ seenAdapterIds.add(adapter.id);
115
+ selectedAdapters.push(adapter);
116
+ }
117
+ }
98
118
 
99
119
  let wantPathShim = opts.wantPathShim;
100
120
  if (wantPathShim === undefined) {
@@ -104,19 +124,6 @@ function buildInstallPlan(packageRoot, envSource = process.env, opts = {}) {
104
124
  /** @type {object[]} */
105
125
  const steps = [];
106
126
 
107
- const mkdirTargets = [
108
- cursorSkillsDir,
109
- path.join(viepilotDir, 'workflows'),
110
- path.join(viepilotDir, 'templates', 'project'),
111
- path.join(viepilotDir, 'templates', 'phase'),
112
- path.join(viepilotDir, 'bin'),
113
- path.join(viepilotDir, 'lib'),
114
- path.join(viepilotDir, 'ui-components'),
115
- ];
116
- for (const dir of mkdirTargets) {
117
- steps.push({ kind: 'mkdir', path: dir });
118
- }
119
-
120
127
  steps.push({ kind: 'mkdir', path: viepilotProfilesDir });
121
128
  steps.push({
122
129
  kind: 'write_file_if_missing',
@@ -124,147 +131,81 @@ function buildInstallPlan(packageRoot, envSource = process.env, opts = {}) {
124
131
  content: VIEPILOT_PROFILE_MAP_SEED,
125
132
  });
126
133
 
127
- for (const name of listSkillDirNames(root)) {
128
- const src = path.join(root, 'skills', name);
129
- const dest = path.join(cursorSkillsDir, name);
130
- if (env.symlinkSkills) {
131
- steps.push({
132
- kind: 'symlink_dir',
133
- target: dest,
134
- sourceAbsolute: path.resolve(src),
135
- });
136
- } else {
137
- steps.push({ kind: 'copy_dir', from: src, to: dest });
134
+ const binFiles = ['vp-tools.cjs', 'viepilot.cjs'];
135
+ const libFiles = ['cli-shared.cjs', 'viepilot-info.cjs', 'viepilot-update.cjs', 'viepilot-install.cjs', 'viepilot-config.cjs'];
136
+ const uiRoot = path.join(root, 'ui-components');
137
+
138
+ // One install loop per selected adapter (replaces cursor block + claude-code if-block).
139
+ for (const adapter of selectedAdapters) {
140
+ const skillsDir = adapter.skillsDir(home);
141
+ const vpDir = adapter.viepilotDir(home);
142
+
143
+ // mkdir: skills dir + all viepilot subdirs
144
+ steps.push({ kind: 'mkdir', path: skillsDir });
145
+ for (const sub of adapter.installSubdirs) {
146
+ steps.push({ kind: 'mkdir', path: path.join(vpDir, sub) });
138
147
  }
139
- }
140
148
 
141
- if (installClaudeSkills && claudeSkillsDir) {
142
- steps.push({ kind: 'mkdir', path: claudeSkillsDir });
149
+ // copy skills into skillsDir
143
150
  for (const name of listSkillDirNames(root)) {
144
151
  const src = path.join(root, 'skills', name);
145
- const dest = path.join(claudeSkillsDir, name);
152
+ const dest = path.join(skillsDir, name);
146
153
  if (env.symlinkSkills) {
147
- steps.push({
148
- kind: 'symlink_dir',
149
- target: dest,
150
- sourceAbsolute: path.resolve(src),
151
- });
154
+ steps.push({ kind: 'symlink_dir', target: dest, sourceAbsolute: path.resolve(src) });
152
155
  } else {
153
156
  steps.push({ kind: 'copy_dir', from: src, to: dest });
154
157
  }
155
158
  }
156
159
 
157
- // BUG-005: mirror workflows/templates/bin/lib to ~/.claude/viepilot/
158
- if (claudeViepilotDir) {
159
- for (const sub of [
160
- 'workflows',
161
- path.join('templates', 'project'),
162
- path.join('templates', 'phase'),
163
- 'bin',
164
- 'lib',
165
- ]) {
166
- steps.push({ kind: 'mkdir', path: path.join(claudeViepilotDir, sub) });
167
- }
168
-
169
- for (const ent of listDirEntries(root, 'workflows')) {
170
- const src = path.join(root, 'workflows', ent.name);
171
- const dest = path.join(claudeViepilotDir, 'workflows', ent.name);
172
- steps.push(ent.isDirectory() ? { kind: 'copy_dir', from: src, to: dest } : { kind: 'copy_file', from: src, to: dest });
173
- }
174
-
175
- for (const ent of listDirEntries(root, path.join('templates', 'project'))) {
176
- const src = path.join(root, 'templates', 'project', ent.name);
177
- const dest = path.join(claudeViepilotDir, 'templates', 'project', ent.name);
178
- steps.push(ent.isDirectory() ? { kind: 'copy_dir', from: src, to: dest } : { kind: 'copy_file', from: src, to: dest });
179
- }
180
-
181
- for (const ent of listDirEntries(root, path.join('templates', 'phase'))) {
182
- const src = path.join(root, 'templates', 'phase', ent.name);
183
- const dest = path.join(claudeViepilotDir, 'templates', 'phase', ent.name);
184
- steps.push(ent.isDirectory() ? { kind: 'copy_dir', from: src, to: dest } : { kind: 'copy_file', from: src, to: dest });
185
- }
186
-
187
- for (const f of ['vp-tools.cjs', 'viepilot.cjs']) {
188
- steps.push({ kind: 'copy_file', from: path.join(root, 'bin', f), to: path.join(claudeViepilotDir, 'bin', f) });
189
- }
190
- for (const f of ['cli-shared.cjs', 'viepilot-info.cjs', 'viepilot-update.cjs', 'viepilot-install.cjs']) {
191
- steps.push({ kind: 'copy_file', from: path.join(root, 'lib', f), to: path.join(claudeViepilotDir, 'lib', f) });
192
- }
193
- // BUG-007: copy package.json so resolveViepilotPackageRoot() finds the root
194
- // when vp-tools runs from ~/.claude/viepilot/ (CWD ≠ viepilot source repo)
195
- steps.push({ kind: 'copy_file', from: path.join(root, 'package.json'), to: path.join(claudeViepilotDir, 'package.json') });
196
-
197
- // Rewrite execution_context paths in mirrored skill files
198
- steps.push({
199
- kind: 'rewrite_paths_in_dir',
200
- dir: claudeSkillsDir,
201
- glob: '**/*.md',
202
- from: '.cursor/viepilot',
203
- to: '.claude/viepilot',
204
- });
160
+ // copy viepilot files into vpDir
161
+ for (const ent of listDirEntries(root, 'workflows')) {
162
+ const src = path.join(root, 'workflows', ent.name);
163
+ const dest = path.join(vpDir, 'workflows', ent.name);
164
+ steps.push(ent.isDirectory() ? { kind: 'copy_dir', from: src, to: dest } : { kind: 'copy_file', from: src, to: dest });
205
165
  }
206
- }
207
-
208
- for (const ent of listDirEntries(root, 'workflows')) {
209
- const src = path.join(root, 'workflows', ent.name);
210
- const dest = path.join(viepilotDir, 'workflows', ent.name);
211
- if (ent.isDirectory()) {
212
- steps.push({ kind: 'copy_dir', from: src, to: dest });
213
- } else if (ent.isFile()) {
214
- steps.push({ kind: 'copy_file', from: src, to: dest });
166
+ for (const ent of listDirEntries(root, path.join('templates', 'project'))) {
167
+ const src = path.join(root, 'templates', 'project', ent.name);
168
+ const dest = path.join(vpDir, 'templates', 'project', ent.name);
169
+ steps.push(ent.isDirectory() ? { kind: 'copy_dir', from: src, to: dest } : { kind: 'copy_file', from: src, to: dest });
215
170
  }
216
- }
217
-
218
- for (const ent of listDirEntries(root, path.join('templates', 'project'))) {
219
- const src = path.join(root, 'templates', 'project', ent.name);
220
- const dest = path.join(viepilotDir, 'templates', 'project', ent.name);
221
- if (ent.isDirectory()) {
222
- steps.push({ kind: 'copy_dir', from: src, to: dest });
223
- } else if (ent.isFile()) {
224
- steps.push({ kind: 'copy_file', from: src, to: dest });
171
+ for (const ent of listDirEntries(root, path.join('templates', 'phase'))) {
172
+ const src = path.join(root, 'templates', 'phase', ent.name);
173
+ const dest = path.join(vpDir, 'templates', 'phase', ent.name);
174
+ steps.push(ent.isDirectory() ? { kind: 'copy_dir', from: src, to: dest } : { kind: 'copy_file', from: src, to: dest });
225
175
  }
226
- }
227
-
228
- for (const ent of listDirEntries(root, path.join('templates', 'phase'))) {
229
- const src = path.join(root, 'templates', 'phase', ent.name);
230
- const dest = path.join(viepilotDir, 'templates', 'phase', ent.name);
231
- if (ent.isDirectory()) {
232
- steps.push({ kind: 'copy_dir', from: src, to: dest });
233
- } else if (ent.isFile()) {
234
- steps.push({ kind: 'copy_file', from: src, to: dest });
176
+ for (const ent of listDirEntries(root, path.join('templates', 'architect'))) {
177
+ const src = path.join(root, 'templates', 'architect', ent.name);
178
+ const dest = path.join(vpDir, 'templates', 'architect', ent.name);
179
+ steps.push(ent.isDirectory() ? { kind: 'copy_dir', from: src, to: dest } : { kind: 'copy_file', from: src, to: dest });
235
180
  }
236
- }
237
-
238
- const uiRoot = path.join(root, 'ui-components');
239
- if (fs.existsSync(uiRoot)) {
240
- for (const ent of listDirEntries(root, 'ui-components')) {
241
- const src = path.join(root, 'ui-components', ent.name);
242
- const dest = path.join(viepilotDir, 'ui-components', ent.name);
243
- if (ent.isDirectory()) {
244
- steps.push({ kind: 'copy_dir', from: src, to: dest });
245
- } else if (ent.isFile()) {
246
- steps.push({ kind: 'copy_file', from: src, to: dest });
181
+ for (const f of binFiles) {
182
+ steps.push({ kind: 'copy_file', from: path.join(root, 'bin', f), to: path.join(vpDir, 'bin', f) });
183
+ }
184
+ for (const f of libFiles) {
185
+ steps.push({ kind: 'copy_file', from: path.join(root, 'lib', f), to: path.join(vpDir, 'lib', f) });
186
+ }
187
+ if (fs.existsSync(uiRoot)) {
188
+ for (const ent of listDirEntries(root, 'ui-components')) {
189
+ const src = path.join(root, 'ui-components', ent.name);
190
+ const dest = path.join(vpDir, 'ui-components', ent.name);
191
+ steps.push(ent.isDirectory() ? { kind: 'copy_dir', from: src, to: dest } : { kind: 'copy_file', from: src, to: dest });
247
192
  }
248
193
  }
249
- }
194
+ // BUG-007: copy package.json so resolveViepilotPackageRoot() finds the root
195
+ steps.push({ kind: 'copy_file', from: path.join(root, 'package.json'), to: path.join(vpDir, 'package.json') });
250
196
 
251
- const binFiles = ['vp-tools.cjs', 'viepilot.cjs'];
252
- for (const f of binFiles) {
253
- steps.push({
254
- kind: 'copy_file',
255
- from: path.join(root, 'bin', f),
256
- to: path.join(viepilotDir, 'bin', f),
257
- });
258
- }
259
- for (const f of ['cli-shared.cjs', 'viepilot-info.cjs', 'viepilot-update.cjs', 'viepilot-install.cjs']) {
260
- steps.push({ kind: 'copy_file', from: path.join(root, 'lib', f), to: path.join(viepilotDir, 'lib', f) });
261
- }
197
+ // chmod bin files
198
+ for (const f of binFiles) {
199
+ steps.push({ kind: 'chmod', path: path.join(vpDir, 'bin', f), mode: 0o755 });
200
+ }
262
201
 
263
- for (const f of binFiles) {
202
+ // resolve {envToolDir} placeholder → adapter's actual install base (ENH-035)
264
203
  steps.push({
265
- kind: 'chmod',
266
- path: path.join(viepilotDir, 'bin', f),
267
- mode: 0o755,
204
+ kind: 'rewrite_paths_in_dir',
205
+ dir: skillsDir,
206
+ glob: '**/*.md',
207
+ from: '{envToolDir}',
208
+ to: adapter.executionContextBase,
268
209
  });
269
210
  }
270
211
 
@@ -275,27 +216,43 @@ function buildInstallPlan(packageRoot, envSource = process.env, opts = {}) {
275
216
  'Check optional cloc for README metrics (brew/apt/dnf/choco); installation continues if missing.',
276
217
  });
277
218
 
219
+ const primaryAdapter = selectedAdapters[0];
220
+ const primaryVpDir = primaryAdapter.viepilotDir(home);
221
+
278
222
  if (wantPathShim) {
279
223
  steps.push({
280
224
  kind: 'path_shim',
281
225
  links: [
282
- { path: '/usr/local/bin/vp-tools', target: path.join(viepilotDir, 'bin', 'vp-tools.cjs') },
283
- { path: '/usr/local/bin/viepilot', target: path.join(viepilotDir, 'bin', 'viepilot.cjs') },
226
+ { path: '/usr/local/bin/vp-tools', target: path.join(primaryVpDir, 'bin', 'vp-tools.cjs') },
227
+ { path: '/usr/local/bin/viepilot', target: path.join(primaryVpDir, 'bin', 'viepilot.cjs') },
284
228
  ],
285
229
  note: 'Unix typical; on Windows native, PATH shim may be skipped or manual.',
286
230
  });
287
231
  }
288
232
 
233
+ // ENH-032: prompt for language config at end of install
234
+ steps.push({
235
+ kind: 'language_config_prompt',
236
+ autoYes: env.autoYes,
237
+ home,
238
+ });
239
+
240
+ // Build paths object — legacy keys preserved for backward compat with existing tests/CLI.
241
+ const cursorAdapter = selectedAdapters.find((a) => a.id === 'cursor');
242
+ const claudeAdapter = selectedAdapters.find((a) => a.id === 'claude-code');
243
+
289
244
  return {
290
245
  version: 1,
291
246
  packageRoot: root,
292
247
  env,
293
248
  home,
294
249
  paths: {
295
- cursorSkillsDir,
296
- claudeSkillsDir,
297
- claudeViepilotDir,
298
- viepilotDir,
250
+ skillsDir: primaryAdapter.skillsDir(home),
251
+ viepilotDir: primaryAdapter.viepilotDir(home),
252
+ // Legacy keys for backward compat:
253
+ cursorSkillsDir: cursorAdapter ? cursorAdapter.skillsDir(home) : null,
254
+ claudeSkillsDir: claudeAdapter ? claudeAdapter.skillsDir(home) : null,
255
+ claudeViepilotDir: claudeAdapter ? claudeAdapter.viepilotDir(home) : null,
299
256
  viepilotUserDataDir,
300
257
  viepilotProfilesDir,
301
258
  viepilotProfileMapPath,
@@ -313,12 +270,15 @@ function formatPlanLines(plan) {
313
270
  const lines = [];
314
271
  lines.push('ViePilot install plan (dry-run)');
315
272
  lines.push(` packageRoot: ${plan.packageRoot}`);
316
- lines.push(` profile: ${plan.env.profile} (informational; Cursor paths always; Claude Code skills when target includes claude-code)`);
317
- lines.push(` skills (Cursor): ${plan.paths.cursorSkillsDir}`);
318
- if (plan.paths.claudeSkillsDir) {
273
+ lines.push(` profile: ${plan.env.profile}`);
274
+ lines.push(` skills: ${plan.paths.skillsDir}`);
275
+ if (plan.paths.cursorSkillsDir && plan.paths.cursorSkillsDir !== plan.paths.skillsDir) {
276
+ lines.push(` skills (Cursor): ${plan.paths.cursorSkillsDir}`);
277
+ }
278
+ if (plan.paths.claudeSkillsDir && plan.paths.claudeSkillsDir !== plan.paths.skillsDir) {
319
279
  lines.push(` skills (Claude Code): ${plan.paths.claudeSkillsDir}`);
320
280
  }
321
- if (plan.paths.claudeViepilotDir) {
281
+ if (plan.paths.claudeViepilotDir && plan.paths.claudeViepilotDir !== plan.paths.viepilotDir) {
322
282
  lines.push(` viepilot (Claude Code): ${plan.paths.claudeViepilotDir}`);
323
283
  }
324
284
  lines.push(` viepilot: ${plan.paths.viepilotDir}`);
@@ -358,6 +318,9 @@ function formatPlanLines(plan) {
358
318
  case 'rewrite_paths_in_dir':
359
319
  lines.push(`${n}. rewrite_paths_in_dir ${s.dir}: "${s.from}" → "${s.to}" in ${s.glob}`);
360
320
  break;
321
+ case 'language_config_prompt':
322
+ lines.push(`${n}. language_config_prompt: write ~/.viepilot/config.json (communication + document language; ${s.autoYes ? 'auto-yes → defaults' : 'interactive'})`);
323
+ break;
361
324
  default:
362
325
  lines.push(`${n}. ${JSON.stringify(s)}`);
363
326
  }
@@ -503,7 +466,7 @@ function applyInstallPlan(plan, options = {}) {
503
466
  break;
504
467
  case 'path_shim':
505
468
  if (process.platform === 'win32') {
506
- logs.push('path_shim: skipped on Windows (add ~/.cursor/viepilot/bin to PATH manually if needed).');
469
+ logs.push('path_shim: skipped on Windows (add viepilot/bin to PATH manually if needed).');
507
470
  break;
508
471
  }
509
472
  if (dryRun) {
@@ -544,6 +507,16 @@ function applyInstallPlan(plan, options = {}) {
544
507
  rewriteDir(step.dir);
545
508
  break;
546
509
  }
510
+ case 'language_config_prompt': {
511
+ if (dryRun) {
512
+ logs.push(`[dry-run] language_config_prompt: would write ~/.viepilot/config.json with communication=en, document=en`);
513
+ break;
514
+ }
515
+ // Write defaults; future interactive installer can prompt here
516
+ writeConfig({ language: { communication: 'en', document: 'en' } }, step.home);
517
+ logs.push(`language_config_prompt: wrote ${step.home}/.viepilot/config.json (communication=en, document=en)`);
518
+ break;
519
+ }
547
520
  default:
548
521
  logs.push(`unknown step kind: ${JSON.stringify(step)}`);
549
522
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "viepilot",
3
- "version": "1.14.0",
3
+ "version": "2.4.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": {
@@ -44,8 +44,6 @@
44
44
  "workflows/",
45
45
  "templates/",
46
46
  "ui-components/",
47
- "install.sh",
48
- "dev-install.sh",
49
47
  "README.md",
50
48
  "CHANGELOG.md",
51
49
  "LICENSE",
@@ -25,54 +25,54 @@ Use Cursor tools: `Shell`, `ReadFile`, `Glob`, `rg`, `ApplyPatch`, `WebSearch`,
25
25
  <implementation_routing_guard>
26
26
  ## Implementation routing guard (ENH-021)
27
27
 
28
- - **Báo cáo / gap** — **không** fix shipping mặc định; route **`/vp-request`** → **`/vp-evolve`** → **`/vp-auto`** hoặc user explicit. Xem `workflows/request.md`.
28
+ - **Report / gap** — do **not** fix shipping by default; route **`/vp-request`** → **`/vp-evolve`** → **`/vp-auto`** or user explicit. See `workflows/request.md`.
29
29
  </implementation_routing_guard>
30
30
 
31
31
 
32
32
  <objective>
33
- Audit ViePilot project state documentation để phát hiện drift.
34
- Hoạt động trên **bất kỳ project nào** đang dùng ViePilot (Java, Node, Python, v.v.).
35
- Auto-detect nếu đang chạy trong viepilot framework repo để thêm framework-specific checks.
33
+ Audit ViePilot project state and documentation to detect drift.
34
+ Works on **any project** using ViePilot (Java, Node, Python, etc.).
35
+ Auto-detects if running inside the viepilot framework repo to enable framework-specific checks.
36
36
 
37
- **Tier 1 — ViePilot State Consistency (mọi project):**
37
+ **Tier 1 — ViePilot State Consistency (all projects):**
38
38
  - `.viepilot/TRACKER.md` current state vs `.viepilot/phases/*/PHASE-STATE.md`
39
39
  - `.viepilot/ROADMAP.md` phase status vs PHASE-STATE.md
40
40
  - `.viepilot/HANDOFF.json` vs TRACKER.md (resume-state consistency)
41
41
  - Git tags `vp-p{N}-complete` vs completed phases in PHASE-STATE.md
42
42
 
43
- **Tier 2 — Project Documentation Drift (mọi project):**
43
+ **Tier 2 — Project Documentation Drift (all projects):**
44
44
  - `README.md` version vs `package.json` / `pom.xml` / `pyproject.toml`
45
45
  - `CHANGELOG.md` vs recent git commits
46
- - Placeholder URLs trong `docs/` (`your-org`, `YOUR_USERNAME`, v.v.)
47
- - Features mới (từ phases gần đây) chưa documentation
46
+ - Placeholder URLs in `docs/` (`your-org`, `YOUR_USERNAME`, etc.)
47
+ - New features (from recent phases) without documentation
48
48
  - `ARCHITECTURE.md` diagram applicability matrix consistency:
49
49
  - `required` diagrams must have Mermaid content
50
50
  - `optional` diagrams may be omitted/merged with explicit note
51
51
  - `N/A` diagrams must have rationale line
52
52
  - **ENH-022 (recommended check):** when a diagram type is `required` (or `optional` with a real Mermaid block) and crystallize policy applies, verify **`.viepilot/architecture/<type>.mermaid`** exists and that **Diagram source** lines in `ARCHITECTURE.md` match; for `N/A`, sidecar file should be absent (no empty stubs)
53
53
 
54
- **Tier 3 — Stack Best Practices + Code Quality (mọi project, theo stack detect được):**
55
- - Detect stacks liên quan từ context/project manifests
56
- - So khớp code patterns với stack-specific Do/Don't + anti-patterns
57
- - Severity findings: `critical` / `high` / `medium` / `low` kèm file/module mapping
58
- - Fallback research bằng `WebSearch` + `WebFetch` khi cache stack thiếu/yếu
59
- - Sinh output guardrails/checklist để `vp-auto` tái sử dụng trong preflight
54
+ **Tier 3 — Stack Best Practices + Code Quality (all projects, for each detected stack):**
55
+ - Detect relevant stacks from context/project manifests
56
+ - Match code patterns against stack-specific Do/Don't + anti-patterns
57
+ - Severity findings: `critical` / `high` / `medium` / `low` with file/module mapping
58
+ - Fallback research using `WebSearch` + `WebFetch` when stack cache is missing/weak
59
+ - Generate guardrails/checklist output for `vp-auto` reuse during preflight
60
60
 
61
- **Tier 4 — Framework Integrity (chỉ khi detect viepilot framework repo):**
62
- - Auto-detect: `skills/vp-*/SKILL.md` tồn tại viepilot framework repo
63
- - `ARCHITECTURE.md` counts vs `skills/`, `workflows/`, CLI thực tế
61
+ **Tier 4 — Framework Integrity (only when viepilot framework repo is detected):**
62
+ - Auto-detect: `skills/vp-*/SKILL.md` presentthis is the viepilot framework repo
63
+ - `ARCHITECTURE.md` counts vs actual `skills/`, `workflows/`, CLI
64
64
  - `README.md` viepilot-specific badges (version, skills-N, workflows-N)
65
65
  - `docs/skills-reference.md` sections vs `skills/` directory
66
66
 
67
67
  **Output:**
68
- - Báo cáo theo 4 tiers, mỗi tier status riêng
69
- - Auto-fix option theo tier
70
- - Suggestions cho complex gaps
71
- - `vp-auto` compatible guardrails contract theo từng stack
68
+ - Report by 4 tiers, each tier with its own status
69
+ - Auto-fix option per tier
70
+ - Suggestions for complex gaps
71
+ - `vp-auto`-compatible guardrails contract per stack
72
72
  </objective>
73
73
 
74
74
  <execution_context>
75
- @$HOME/.cursor/viepilot/workflows/audit.md
75
+ @$HOME/{envToolDir}/workflows/audit.md
76
76
  </execution_context>
77
77
 
78
78
  <context>
@@ -89,7 +89,7 @@ Optional flags:
89
89
  </context>
90
90
 
91
91
  <process>
92
- Execute workflow from `@$HOME/.cursor/viepilot/workflows/audit.md`
92
+ Execute workflow from `@$HOME/{envToolDir}/workflows/audit.md`
93
93
 
94
94
  ### Quick Summary
95
95
 
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: vp-auto
3
- description: "Autonomous execution loop với control points recovery"
3
+ description: "Autonomous execution loop with control points and recovery"
4
4
  version: 0.2.2
5
5
  ---
6
6
 
@@ -10,7 +10,7 @@ version: 0.2.2
10
10
  - Treat all user text after the skill mention as `{{VP_ARGS}}`
11
11
 
12
12
  ## B. User Prompting
13
- Prompt user conversationally với numbered list options tại control points.
13
+ Prompt user conversationally with numbered list options at control points.
14
14
 
15
15
  ## C. Tool Usage
16
16
  Use Cursor tools: `Shell`, `ReadFile`, `Glob`, `rg`, `ApplyPatch`, `WebSearch`, `WebFetch`, `Subagent`
@@ -28,12 +28,12 @@ Use `Task(subagent_type="generalPurpose", ...)` for parallel execution.
28
28
  <implementation_routing_guard>
29
29
  ## Primary implementation lane (ENH-021)
30
30
 
31
- - **`/vp-auto`** + `workflows/autonomous.md` **lane mặc định** để **implement** work đã **phase/task plan** (doc-first **BUG-001**, git persistence **BUG-003**). **`/vp-request`** **`/vp-evolve`** **không** thay thế lane này trừ user **explicit** override.
31
+ - **`/vp-auto`** + `workflows/autonomous.md` is the **default lane** for **implementing** work that already has a **phase/task plan** (doc-first **BUG-001**, git persistence **BUG-003**). **`/vp-request`** and **`/vp-evolve`** do **not** replace this lane unless the user **explicitly** overrides.
32
32
  </implementation_routing_guard>
33
33
 
34
34
 
35
35
  <objective>
36
- Autonomous execution của project phases. Cho mỗi phase: analyze → plan → execute → verify → iterate.
36
+ Autonomous execution of project phases. For each phase: analyze → plan → execute → verify → iterate.
37
37
 
38
38
  Pauses at control points:
39
39
  - Conflicts detected
@@ -88,24 +88,38 @@ If required task details are missing, do not implement until task contract is re
88
88
  - `README.md` metrics — run `npm run readme:sync` when script exists; if `cloc` missing, continue with logged guidance (non-blocking)
89
89
 
90
90
  **After:** Project built, or paused for user intervention.
91
+
92
+ **Language configuration (ENH-032):**
93
+ - Initialize step reads `~/.viepilot/config.json` → `COMMUNICATION_LANG` (default: `en`).
94
+ - `COMMUNICATION_LANG` controls banners, control-point messages, and user-facing output.
95
+ - Configure via: `vp-tools config set language.communication vi`
91
96
  </objective>
92
97
 
93
98
  <execution_context>
94
- @$HOME/.cursor/viepilot/workflows/autonomous.md
99
+ @$HOME/{envToolDir}/workflows/autonomous.md
95
100
  </execution_context>
96
101
 
97
102
  <context>
98
103
  Optional flags:
99
- - `--from N` : Start từ phase N
100
- - `--phase N` : Chỉ chạy phase N
104
+ - `--from N` : Start from phase N
105
+ - `--phase N` : Run only phase N
101
106
  - `--fast` : Skip optional verifications
102
107
  - `--dry-run` : Plan only, no execution
103
108
 
104
- No extra args: chỉ nghĩa các cờ trên **tắt** — **không** phải rule “dừng bắt buộc sau mỗi task”. Trong chat, một turn thường ~một task; tiếp tục bằng lượt sau hoặc `/vp-auto` lại. Doc: `docs/user/features/autonomous-mode.md`.
109
+ No extra args: simply means the flags above are **off** — **not** a rule of mandatory stop after each task”. In chat, one turn is typically ~one task; continue with the next turn or invoke `/vp-auto` again. Doc: `docs/user/features/autonomous-mode.md`.
110
+
111
+ **Task path validation (BUG-009):**
112
+ Before executing each task, validates all paths in the `## Paths` block.
113
+ If any path starts with `~/` or `/` (absolute):
114
+ - Execution stops for that task with a ⛔ error message
115
+ - User must fix the task file to use repo-relative paths before continuing
116
+ - This prevents accidentally editing `~/.claude/` (live install) instead of source
117
+
118
+ See preflight check in `workflows/autonomous.md` → “Preflight: Task Paths Validation (BUG-009)”.
105
119
  </context>
106
120
 
107
121
  <process>
108
- Execute workflow from `@$HOME/.cursor/viepilot/workflows/autonomous.md`
122
+ Execute workflow from `@$HOME/{envToolDir}/workflows/autonomous.md`
109
123
 
110
124
  ### 1. Initialize
111
125
  ```bash