viepilot 2.45.3 → 2.45.5

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
@@ -7,6 +7,25 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [2.45.5] - 2026-04-27
11
+
12
+ ### Enhanced
13
+ - **ENH-078**: `npx viepilot install` now prompts users to select their preferred
14
+ communication language (the language ViePilot uses for banners and AI prompts)
15
+ via keyboard selector or numbered-list fallback; chosen language written to
16
+ `~/.viepilot/config.json → language.communication`; supports 10 languages
17
+ (en, vi, fr, ja, de, es, zh, ko, pt, id); `--yes` mode skips prompt and
18
+ defaults to `en` (ENH-078)
19
+
20
+ ## [2.45.4] - 2026-04-27
21
+
22
+ ### Fixed
23
+ - **BUG-025**: brownfield crystallize initial mode-selection prompt ("Proceed / Run brainstorm
24
+ first / Cancel") was rendered as plain-text numbered list on Claude Code terminal because
25
+ `workflows/crystallize.md` Step 0-B had no AUQ spec for the auto-detected entry gate.
26
+ Fix: added adapter-aware `AskUserQuestion` block with 3 labelled options before scanner
27
+ execution; text fallback preserved for Cursor/Codex/Antigravity (BUG-025)
28
+
10
29
  ## [2.45.3] - 2026-04-25
11
30
 
12
31
  ### Fixed
package/bin/viepilot.cjs CHANGED
@@ -17,6 +17,19 @@ const { buildInstallPlan, applyInstallPlan, resolveViepilotPackageRoot } = requi
17
17
  const { adapters: adapterMap } = require(path.join(__dirname, '..', 'lib', 'adapters', 'index.cjs'));
18
18
 
19
19
  // UI target list — keep cursor-agent and cursor-ide as distinct choices for users.
20
+ const LANGUAGES = [
21
+ { id: 'en', label: 'English (en)' },
22
+ { id: 'vi', label: 'Vietnamese — Tiếng Việt (vi)' },
23
+ { id: 'fr', label: 'French — Français (fr)' },
24
+ { id: 'ja', label: 'Japanese — 日本語 (ja)' },
25
+ { id: 'de', label: 'German — Deutsch (de)' },
26
+ { id: 'es', label: 'Spanish — Español (es)' },
27
+ { id: 'zh', label: 'Chinese Simplified — 中文 (zh)' },
28
+ { id: 'ko', label: 'Korean — 한국어 (ko)' },
29
+ { id: 'pt', label: 'Portuguese — Português (pt)' },
30
+ { id: 'id', label: 'Indonesian — Bahasa Indonesia (id)' },
31
+ ];
32
+
20
33
  const TARGETS = [
21
34
  { id: 'claude-code', label: adapterMap['claude-code'].name + ' (default)' },
22
35
  { id: 'cursor-agent', label: 'Cursor Agent' },
@@ -235,6 +248,19 @@ function ask(question) {
235
248
  });
236
249
  }
237
250
 
251
+ async function interactiveLanguageSelection() {
252
+ if (!process.stdin.isTTY || !process.stdout.isTTY) {
253
+ console.log('\nSelect communication language (ViePilot banners and prompts):');
254
+ LANGUAGES.forEach((l, idx) => console.log(` ${idx + 1}. ${l.label}`));
255
+ const answer = await ask('Language [1 = English]: ');
256
+ const idx = parseInt(answer, 10);
257
+ const lang = LANGUAGES[Number.isInteger(idx) && idx >= 1 && idx <= LANGUAGES.length ? idx - 1 : 0];
258
+ return lang.id;
259
+ }
260
+ const selected = await runKeyboardSelector(LANGUAGES, 'single', 'Select communication language');
261
+ return (selected && selected[0]) || 'en';
262
+ }
263
+
238
264
  async function interactiveTargetSelection() {
239
265
  if (!process.stdin.isTTY || !process.stdout.isTTY) {
240
266
  console.log('\nSelect install targets (comma-separated numbers):');
@@ -261,7 +287,7 @@ async function interactiveTargetSelection() {
261
287
  * @param {boolean} dryRun
262
288
  * @returns {{ ok: boolean, code: number }}
263
289
  */
264
- function runInstallViaNode(selectedTargets, dryRun) {
290
+ function runInstallViaNode(selectedTargets, dryRun, communicationLang = 'en') {
265
291
  const fallbackRoot = path.join(__dirname, '..');
266
292
  const pkgRoot = resolveViepilotPackageRoot(fallbackRoot) || fallbackRoot;
267
293
  const profile = selectedTargets[0] || 'claude-code';
@@ -269,6 +295,7 @@ function runInstallViaNode(selectedTargets, dryRun) {
269
295
  ...process.env,
270
296
  VIEPILOT_AUTO_YES: '1',
271
297
  VIEPILOT_INSTALL_PROFILE: profile,
298
+ VIEPILOT_COMM_LANG: communicationLang,
272
299
  };
273
300
  const wantPathShim = env.VIEPILOT_ADD_PATH === '1';
274
301
 
@@ -328,8 +355,14 @@ async function installCommand(rawArgs) {
328
355
  }
329
356
  }
330
357
 
358
+ let communicationLang = 'en';
359
+ if (!options.yes) {
360
+ communicationLang = await interactiveLanguageSelection();
361
+ }
362
+
331
363
  console.log(`\nSelected targets: ${selectedTargets.join(', ')}`);
332
- const run = runInstallViaNode(selectedTargets, options.dryRun);
364
+ console.log(`Communication language: ${communicationLang}`);
365
+ const run = runInstallViaNode(selectedTargets, options.dryRun, communicationLang);
333
366
  const results = selectedTargets.map((target) => ({
334
367
  ok: run.ok,
335
368
  target,
@@ -29,6 +29,7 @@ function normalizeInstallEnv(envSource = process.env) {
29
29
  profile: envSource.VIEPILOT_INSTALL_PROFILE || 'claude-code',
30
30
  addPath: envSource.VIEPILOT_ADD_PATH === '1',
31
31
  symlinkSkills: envSource.VIEPILOT_SYMLINK_SKILLS === '1',
32
+ communicationLang: envSource.VIEPILOT_COMM_LANG || 'en',
32
33
  };
33
34
  }
34
35
 
@@ -241,9 +242,10 @@ function buildInstallPlan(packageRoot, envSource = process.env, opts = {}) {
241
242
  });
242
243
  }
243
244
 
244
- // ENH-032: prompt for language config at end of install
245
+ // ENH-032 / ENH-078: write selected communication language; lang chosen interactively in CLI
245
246
  steps.push({
246
247
  kind: 'language_config_prompt',
248
+ communicationLang: env.communicationLang,
247
249
  autoYes: env.autoYes,
248
250
  home,
249
251
  });
@@ -519,13 +521,13 @@ function applyInstallPlan(plan, options = {}) {
519
521
  break;
520
522
  }
521
523
  case 'language_config_prompt': {
524
+ const commLang = step.communicationLang ?? 'en';
522
525
  if (dryRun) {
523
- logs.push(`[dry-run] language_config_prompt: would write ~/.viepilot/config.json with communication=en, document=en`);
526
+ logs.push(`[dry-run] language_config_prompt: would write ~/.viepilot/config.json with communication=${commLang}, document=en`);
524
527
  break;
525
528
  }
526
- // Write defaults; future interactive installer can prompt here
527
- writeConfig({ language: { communication: 'en', document: 'en' } }, step.home);
528
- logs.push(`language_config_prompt: wrote ${step.home}/.viepilot/config.json (communication=en, document=en)`);
529
+ writeConfig({ language: { communication: commLang, document: 'en' } }, step.home);
530
+ logs.push(`language_config_prompt: wrote ${step.home}/.viepilot/config.json (communication=${commLang}, document=en)`);
529
531
  break;
530
532
  }
531
533
  default:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "viepilot",
3
- "version": "2.45.3",
3
+ "version": "2.45.5",
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": {
@@ -238,6 +238,36 @@ Store all metadata for template generation.
238
238
  >
239
239
  > Ask: "Continue? (y/n)" — abort if n.
240
240
 
241
+ **Initial brownfield entry gate (BUG-025) — auto-detected only (no explicit `--brownfield` flag):**
242
+
243
+ When brownfield was triggered automatically (not via `--brownfield`), present the mode-selection choice **before** running the scanner:
244
+
245
+ > **Claude Code (terminal) — REQUIRED:** Call `AskUserQuestion` tool. Only fall back to text menu if the tool errors or is unavailable. AUQ spec:
246
+ > - question: "No brainstorm session found — this looks like an existing project. How would you like to proceed?"
247
+ > - header: "Brownfield Mode Detected"
248
+ > - options:
249
+ > - label: "Proceed with Brownfield Mode (Recommended)"
250
+ > description: "Scan codebase across 12 signal categories → auto-generate .viepilot/ artifacts"
251
+ > - label: "Run brainstorm first → /vp-brainstorm"
252
+ > description: "Capture vision/scope manually, then run /vp-crystallize to convert"
253
+ > - label: "Cancel"
254
+ > description: "Exit without changes"
255
+ > - multiSelect: false
256
+ >
257
+ > **Cursor / Codex / Antigravity / other:** use text menu below:
258
+ > ```
259
+ > No brainstorm session found — existing project detected.
260
+ > 1. Proceed with Brownfield Mode — scan across 12 signal categories → generate .viepilot/ artifacts
261
+ > 2. Run brainstorm first — /vp-brainstorm to capture vision/scope, then /vp-crystallize
262
+ > 3. Cancel — exit without changes
263
+ > Which would you like? (1 / 2 / 3)
264
+ > ```
265
+ >
266
+ > **On selection:**
267
+ > - "Proceed with Brownfield Mode" / "1" → continue to scanner below
268
+ > - "Run brainstorm first" / "2" → print: "Run `/vp-brainstorm` to capture project vision, then `/vp-crystallize` to generate .viepilot/ artifacts." and exit
269
+ > - "Cancel" / "3" → exit without changes
270
+
241
271
  **When brownfield mode is active:**
242
272
  1. Run the full 12-category codebase scanner (Signal Categories 1–12 below).
243
273
  2. Produce a structured **Scan Report** (see schema at end of this step).