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.
Files changed (54) hide show
  1. package/CHANGELOG.md +98 -0
  2. package/README.md +3 -3
  3. package/bin/viepilot.cjs +7 -5
  4. package/bin/vp-tools.cjs +193 -0
  5. package/dev-install.sh +34 -13
  6. package/docs/user/features/hooks.md +93 -0
  7. package/lib/adapters/claude-code.cjs +42 -0
  8. package/lib/adapters/cursor.cjs +31 -0
  9. package/lib/adapters/index.cjs +26 -0
  10. package/lib/hooks/brainstorm-staleness.cjs +231 -0
  11. package/lib/viepilot-config.cjs +103 -0
  12. package/lib/viepilot-install.cjs +128 -153
  13. package/package.json +1 -1
  14. package/skills/vp-audit/SKILL.md +21 -21
  15. package/skills/vp-auto/SKILL.md +21 -7
  16. package/skills/vp-brainstorm/SKILL.md +42 -36
  17. package/skills/vp-crystallize/SKILL.md +22 -16
  18. package/skills/vp-debug/SKILL.md +2 -2
  19. package/skills/vp-docs/SKILL.md +7 -7
  20. package/skills/vp-evolve/SKILL.md +25 -12
  21. package/skills/vp-info/SKILL.md +23 -23
  22. package/skills/vp-pause/SKILL.md +5 -5
  23. package/skills/vp-request/SKILL.md +12 -12
  24. package/skills/vp-resume/SKILL.md +4 -4
  25. package/skills/vp-rollback/SKILL.md +3 -3
  26. package/skills/vp-status/SKILL.md +4 -4
  27. package/skills/vp-task/SKILL.md +2 -2
  28. package/skills/vp-ui-components/SKILL.md +12 -12
  29. package/skills/vp-update/SKILL.md +17 -17
  30. package/templates/architect/apis.html +11 -10
  31. package/templates/architect/architect-actions.js +217 -0
  32. package/templates/architect/architecture.html +8 -7
  33. package/templates/architect/data-flow.html +5 -4
  34. package/templates/architect/decisions.html +4 -3
  35. package/templates/architect/deployment.html +10 -9
  36. package/templates/architect/erd.html +7 -6
  37. package/templates/architect/feature-map.html +5 -4
  38. package/templates/architect/sequence-diagram.html +6 -5
  39. package/templates/architect/style.css +146 -0
  40. package/templates/architect/tech-notes.html +3 -2
  41. package/templates/architect/tech-stack.html +8 -7
  42. package/templates/architect/user-use-cases.html +8 -7
  43. package/templates/project/AI-GUIDE.md +49 -49
  44. package/workflows/audit.md +3 -3
  45. package/workflows/autonomous.md +38 -5
  46. package/workflows/brainstorm.md +398 -222
  47. package/workflows/crystallize.md +46 -33
  48. package/workflows/debug.md +9 -9
  49. package/workflows/documentation.md +5 -5
  50. package/workflows/evolve.md +44 -12
  51. package/workflows/pause-work.md +2 -2
  52. package/workflows/request.md +8 -8
  53. package/workflows/resume-work.md +1 -1
  54. package/workflows/rollback.md +1 -1
@@ -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,150 +131,86 @@ 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);
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 });
165
+ }
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 });
170
+ }
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 });
175
+ }
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 });
180
+ }
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);
184
191
  steps.push(ent.isDirectory() ? { kind: 'copy_dir', from: src, to: dest } : { kind: 'copy_file', from: src, to: dest });
185
192
  }
193
+ }
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') });
186
196
 
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') });
197
+ // chmod bin files
198
+ for (const f of binFiles) {
199
+ steps.push({ kind: 'chmod', path: path.join(vpDir, 'bin', f), mode: 0o755 });
200
+ }
196
201
 
197
- // Rewrite execution_context paths in mirrored skill files
202
+ // path rewrite in skills (if adapter requires it — e.g. claude-code rewrites .cursor → .claude)
203
+ if (adapter.pathRewrite) {
198
204
  steps.push({
199
205
  kind: 'rewrite_paths_in_dir',
200
- dir: claudeSkillsDir,
206
+ dir: skillsDir,
201
207
  glob: '**/*.md',
202
- from: '.cursor/viepilot',
203
- to: '.claude/viepilot',
208
+ from: adapter.pathRewrite.from,
209
+ to: adapter.pathRewrite.to,
204
210
  });
205
211
  }
206
212
  }
207
213
 
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 });
215
- }
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 });
225
- }
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 });
235
- }
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 });
247
- }
248
- }
249
- }
250
-
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
- }
262
-
263
- for (const f of binFiles) {
264
- steps.push({
265
- kind: 'chmod',
266
- path: path.join(viepilotDir, 'bin', f),
267
- mode: 0o755,
268
- });
269
- }
270
-
271
214
  steps.push({
272
215
  kind: 'note',
273
216
  id: 'cloc_optional',
@@ -275,27 +218,43 @@ function buildInstallPlan(packageRoot, envSource = process.env, opts = {}) {
275
218
  'Check optional cloc for README metrics (brew/apt/dnf/choco); installation continues if missing.',
276
219
  });
277
220
 
221
+ const primaryAdapter = selectedAdapters[0];
222
+ const primaryVpDir = primaryAdapter.viepilotDir(home);
223
+
278
224
  if (wantPathShim) {
279
225
  steps.push({
280
226
  kind: 'path_shim',
281
227
  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') },
228
+ { path: '/usr/local/bin/vp-tools', target: path.join(primaryVpDir, 'bin', 'vp-tools.cjs') },
229
+ { path: '/usr/local/bin/viepilot', target: path.join(primaryVpDir, 'bin', 'viepilot.cjs') },
284
230
  ],
285
231
  note: 'Unix typical; on Windows native, PATH shim may be skipped or manual.',
286
232
  });
287
233
  }
288
234
 
235
+ // ENH-032: prompt for language config at end of install
236
+ steps.push({
237
+ kind: 'language_config_prompt',
238
+ autoYes: env.autoYes,
239
+ home,
240
+ });
241
+
242
+ // Build paths object — legacy keys preserved for backward compat with existing tests/CLI.
243
+ const cursorAdapter = selectedAdapters.find((a) => a.id === 'cursor');
244
+ const claudeAdapter = selectedAdapters.find((a) => a.id === 'claude-code');
245
+
289
246
  return {
290
247
  version: 1,
291
248
  packageRoot: root,
292
249
  env,
293
250
  home,
294
251
  paths: {
295
- cursorSkillsDir,
296
- claudeSkillsDir,
297
- claudeViepilotDir,
298
- viepilotDir,
252
+ skillsDir: primaryAdapter.skillsDir(home),
253
+ viepilotDir: primaryAdapter.viepilotDir(home),
254
+ // Legacy keys for backward compat:
255
+ cursorSkillsDir: cursorAdapter ? cursorAdapter.skillsDir(home) : null,
256
+ claudeSkillsDir: claudeAdapter ? claudeAdapter.skillsDir(home) : null,
257
+ claudeViepilotDir: claudeAdapter ? claudeAdapter.viepilotDir(home) : null,
299
258
  viepilotUserDataDir,
300
259
  viepilotProfilesDir,
301
260
  viepilotProfileMapPath,
@@ -313,12 +272,15 @@ function formatPlanLines(plan) {
313
272
  const lines = [];
314
273
  lines.push('ViePilot install plan (dry-run)');
315
274
  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) {
275
+ lines.push(` profile: ${plan.env.profile}`);
276
+ lines.push(` skills: ${plan.paths.skillsDir}`);
277
+ if (plan.paths.cursorSkillsDir && plan.paths.cursorSkillsDir !== plan.paths.skillsDir) {
278
+ lines.push(` skills (Cursor): ${plan.paths.cursorSkillsDir}`);
279
+ }
280
+ if (plan.paths.claudeSkillsDir && plan.paths.claudeSkillsDir !== plan.paths.skillsDir) {
319
281
  lines.push(` skills (Claude Code): ${plan.paths.claudeSkillsDir}`);
320
282
  }
321
- if (plan.paths.claudeViepilotDir) {
283
+ if (plan.paths.claudeViepilotDir && plan.paths.claudeViepilotDir !== plan.paths.viepilotDir) {
322
284
  lines.push(` viepilot (Claude Code): ${plan.paths.claudeViepilotDir}`);
323
285
  }
324
286
  lines.push(` viepilot: ${plan.paths.viepilotDir}`);
@@ -358,6 +320,9 @@ function formatPlanLines(plan) {
358
320
  case 'rewrite_paths_in_dir':
359
321
  lines.push(`${n}. rewrite_paths_in_dir ${s.dir}: "${s.from}" → "${s.to}" in ${s.glob}`);
360
322
  break;
323
+ case 'language_config_prompt':
324
+ lines.push(`${n}. language_config_prompt: write ~/.viepilot/config.json (communication + document language; ${s.autoYes ? 'auto-yes → defaults' : 'interactive'})`);
325
+ break;
361
326
  default:
362
327
  lines.push(`${n}. ${JSON.stringify(s)}`);
363
328
  }
@@ -503,7 +468,7 @@ function applyInstallPlan(plan, options = {}) {
503
468
  break;
504
469
  case 'path_shim':
505
470
  if (process.platform === 'win32') {
506
- logs.push('path_shim: skipped on Windows (add ~/.cursor/viepilot/bin to PATH manually if needed).');
471
+ logs.push('path_shim: skipped on Windows (add viepilot/bin to PATH manually if needed).');
507
472
  break;
508
473
  }
509
474
  if (dryRun) {
@@ -544,6 +509,16 @@ function applyInstallPlan(plan, options = {}) {
544
509
  rewriteDir(step.dir);
545
510
  break;
546
511
  }
512
+ case 'language_config_prompt': {
513
+ if (dryRun) {
514
+ logs.push(`[dry-run] language_config_prompt: would write ~/.viepilot/config.json with communication=en, document=en`);
515
+ break;
516
+ }
517
+ // Write defaults; future interactive installer can prompt here
518
+ writeConfig({ language: { communication: 'en', document: 'en' } }, step.home);
519
+ logs.push(`language_config_prompt: wrote ${step.home}/.viepilot/config.json (communication=en, document=en)`);
520
+ break;
521
+ }
547
522
  default:
548
523
  logs.push(`unknown step kind: ${JSON.stringify(step)}`);
549
524
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "viepilot",
3
- "version": "1.14.0",
3
+ "version": "2.1.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": {
@@ -25,50 +25,50 @@ 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>
@@ -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,6 +88,11 @@ 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>
@@ -96,12 +101,21 @@ If required task details are missing, do not implement until task contract is re
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>