vigthoria-cli 1.12.3 → 1.13.7

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.
@@ -2,6 +2,22 @@
2
2
  * Brain Hub client — sync workspace index + fetch account brain context
3
3
  * via coder.vigthoria.io (JWT-authenticated).
4
4
  */
5
+ // Brain Hub calls are an optional personalization enhancement to an agent
6
+ // turn, not on the critical path. Native `fetch()` has NO default timeout,
7
+ // so a slow/unresponsive coder.vigthoria.io backend previously caused the
8
+ // ENTIRE agent turn to hang indefinitely with zero console output, well
9
+ // before any "ROUTING DECISION" text is printed (2026-07-10 root-cause).
10
+ const BRAIN_HUB_TIMEOUT_MS = 3500;
11
+ async function fetchWithTimeout(url, init = {}, timeoutMs = BRAIN_HUB_TIMEOUT_MS) {
12
+ const controller = new AbortController();
13
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
14
+ try {
15
+ return await fetch(url, { ...init, signal: controller.signal });
16
+ }
17
+ finally {
18
+ clearTimeout(timer);
19
+ }
20
+ }
5
21
  export class BrainHubClient {
6
22
  apiBase;
7
23
  getAuthToken;
@@ -22,27 +38,39 @@ export class BrainHubClient {
22
38
  if (!headers.Authorization) {
23
39
  return { ok: false, skipped: true, reason: 'not_authenticated' };
24
40
  }
25
- const resp = await fetch(`${this.apiBase}/api/brain/sync-index`, {
26
- method: 'POST',
27
- headers,
28
- body: JSON.stringify(payload),
29
- });
30
- const data = await resp.json().catch(() => ({}));
31
- if (!resp.ok) {
32
- return { ok: false, error: data.error || `HTTP ${resp.status}` };
41
+ try {
42
+ const resp = await fetchWithTimeout(`${this.apiBase}/api/brain/sync-index`, {
43
+ method: 'POST',
44
+ headers,
45
+ body: JSON.stringify(payload),
46
+ });
47
+ const data = await resp.json().catch(() => ({}));
48
+ if (!resp.ok) {
49
+ return { ok: false, error: data.error || `HTTP ${resp.status}` };
50
+ }
51
+ return data;
52
+ }
53
+ catch (error) {
54
+ const timedOut = error instanceof Error && error.name === 'AbortError';
55
+ return { ok: false, error: timedOut ? `timeout after ${BRAIN_HUB_TIMEOUT_MS}ms` : String(error) };
33
56
  }
34
- return data;
35
57
  }
36
58
  async fetchAccountContext(limit = 25) {
37
59
  const headers = await this.headers();
38
60
  if (!headers.Authorization) {
39
61
  return { ok: false, formattedText: '', memories: [] };
40
62
  }
41
- const resp = await fetch(`${this.apiBase}/api/brain/context?limit=${limit}`, { headers });
42
- const data = await resp.json().catch(() => ({}));
43
- if (!resp.ok) {
44
- return { ok: false, formattedText: '', error: data.error || `HTTP ${resp.status}` };
63
+ try {
64
+ const resp = await fetchWithTimeout(`${this.apiBase}/api/brain/context?limit=${limit}`, { headers });
65
+ const data = await resp.json().catch(() => ({}));
66
+ if (!resp.ok) {
67
+ return { ok: false, formattedText: '', error: data.error || `HTTP ${resp.status}` };
68
+ }
69
+ return data;
70
+ }
71
+ catch (error) {
72
+ const timedOut = error instanceof Error && error.name === 'AbortError';
73
+ return { ok: false, formattedText: '', error: timedOut ? `timeout after ${BRAIN_HUB_TIMEOUT_MS}ms` : String(error) };
45
74
  }
46
- return data;
47
75
  }
48
76
  }
@@ -23,6 +23,7 @@ export class CodebaseIndexer {
23
23
  '.DS_Store', 'package-lock.json', 'yarn.lock', 'pnpm-lock.yaml',
24
24
  '.vsix', '.map', '.min.js', '.min.css', 'coverage',
25
25
  '.cache', '.tmp', 'tmp', 'temp', '.idea', '.vscode',
26
+ '.vigthoria',
26
27
  ];
27
28
  supportedExtensions = new Set([
28
29
  '.js', '.ts', '.jsx', '.tsx', '.py', '.java', '.go', '.rs',
@@ -96,10 +97,23 @@ export class CodebaseIndexer {
96
97
  for (const filePath of files) {
97
98
  this.indexFile(filePath);
98
99
  }
99
- const indexHash = crypto.createHash('sha256')
100
- .update(`${this.indexedFileCount}:${this.totalChunks}:${files.length}`)
101
- .digest('hex')
102
- .slice(0, 16);
100
+ // The synchronization contract is content-addressed. Stable file and
101
+ // chunk counts must never hide a source modification from the Brain.
102
+ const contentHasher = crypto.createHash('sha256');
103
+ const contentEntries = Array.from(this.fileHashes.entries())
104
+ .map(([filePath, fileHash]) => [
105
+ path.relative(this.workspaceRoot, filePath).split(path.sep).join('/'),
106
+ fileHash,
107
+ ])
108
+ .sort(([left], [right]) => left.localeCompare(right));
109
+ for (const [relativePath, fileHash] of contentEntries) {
110
+ contentHasher.update(relativePath, 'utf8');
111
+ contentHasher.update('\0', 'utf8');
112
+ contentHasher.update(fileHash, 'ascii');
113
+ contentHasher.update('\n', 'utf8');
114
+ }
115
+ // Empty workspaces still receive a deterministic, valid SHA-256 value.
116
+ const indexHash = contentHasher.digest('hex');
103
117
  const topFiles = files.slice(0, 12).map((filePath) => path.relative(this.workspaceRoot, filePath));
104
118
  const meta = {
105
119
  indexedFileCount: this.indexedFileCount,
@@ -308,7 +322,16 @@ export class CodebaseIndexer {
308
322
  shouldIgnore(name, fullPath) {
309
323
  if (name.startsWith('.') && name !== '.env')
310
324
  return true;
311
- const lowerPath = fullPath.toLowerCase();
312
- return this.ignorePatterns.some((pattern) => lowerPath.includes(pattern));
325
+ const relativePath = path.relative(this.workspaceRoot, fullPath);
326
+ const segments = relativePath.split(path.sep).map((segment) => segment.toLowerCase());
327
+ const fileName = segments.at(-1) || '';
328
+ const suffixPatterns = new Set(['.vsix', '.map', '.min.js', '.min.css']);
329
+ return this.ignorePatterns.some((rawPattern) => {
330
+ const pattern = rawPattern.toLowerCase();
331
+ if (suffixPatterns.has(pattern)) {
332
+ return fileName.endsWith(pattern);
333
+ }
334
+ return segments.includes(pattern);
335
+ });
313
336
  }
314
337
  }
@@ -0,0 +1,11 @@
1
+ import type { Command } from 'commander';
2
+ export type CommandFamily = 'Vigthoria Repo Management' | 'Agent Mode' | 'System & Configuration';
3
+ export interface CommandCatalogEntry {
4
+ path: string;
5
+ description: string;
6
+ family: CommandFamily;
7
+ command: Command;
8
+ }
9
+ export declare function buildCommandCatalog(program: Command): CommandCatalogEntry[];
10
+ export declare function renderDynamicHelp(program: Command): string;
11
+ export declare function selectInteractiveCommand(program: Command): Promise<string[] | null>;
@@ -0,0 +1,97 @@
1
+ import inquirer from 'inquirer';
2
+ const AGENT_COMMANDS = new Set([
3
+ 'agent', 'chat', 'chat-resume', 'operator', 'legion', 'workflow',
4
+ 'history', 'replay', 'fork', 'cancel',
5
+ ]);
6
+ function familyFor(commandPath) {
7
+ const root = commandPath.split(' ')[0];
8
+ if (root === 'repo')
9
+ return 'Vigthoria Repo Management';
10
+ if (AGENT_COMMANDS.has(root))
11
+ return 'Agent Mode';
12
+ return 'System & Configuration';
13
+ }
14
+ export function buildCommandCatalog(program) {
15
+ const entries = [];
16
+ const visit = (command, parents) => {
17
+ const commandPath = [...parents, command.name()].join(' ');
18
+ entries.push({
19
+ path: commandPath,
20
+ description: command.description() || '',
21
+ family: familyFor(commandPath),
22
+ command,
23
+ });
24
+ for (const child of command.commands)
25
+ visit(child, [...parents, command.name()]);
26
+ };
27
+ for (const command of program.commands)
28
+ visit(command, []);
29
+ return entries.sort((left, right) => left.family.localeCompare(right.family) || left.path.localeCompare(right.path));
30
+ }
31
+ export function renderDynamicHelp(program) {
32
+ const catalog = buildCommandCatalog(program);
33
+ const lines = ['Commands:'];
34
+ const families = [
35
+ 'Vigthoria Repo Management',
36
+ 'Agent Mode',
37
+ 'System & Configuration',
38
+ ];
39
+ for (const family of families) {
40
+ lines.push('', `${family}:`);
41
+ for (const entry of catalog.filter((candidate) => candidate.family === family)) {
42
+ const aliases = entry.command.aliases();
43
+ const displayPath = aliases.length
44
+ ? `${entry.path} (${aliases.join(', ')})`
45
+ : entry.path;
46
+ lines.push(` ${displayPath.padEnd(30)} ${entry.description}`);
47
+ }
48
+ }
49
+ return lines.join('\n');
50
+ }
51
+ async function collectRequiredArguments(entry) {
52
+ const values = [];
53
+ for (const argument of entry.command.registeredArguments) {
54
+ if (!argument.required)
55
+ continue;
56
+ const answer = await inquirer.prompt([{
57
+ type: 'input',
58
+ name: 'value',
59
+ message: `${argument.name()}:`,
60
+ validate: (value) => value.trim().length > 0 || `${argument.name()} is required`,
61
+ }]);
62
+ values.push(String(answer.value).trim());
63
+ }
64
+ return values;
65
+ }
66
+ export async function selectInteractiveCommand(program) {
67
+ const catalog = buildCommandCatalog(program);
68
+ const familyAnswer = await inquirer.prompt([{
69
+ type: 'list',
70
+ name: 'family',
71
+ message: 'Select a command family:',
72
+ choices: [
73
+ 'Vigthoria Repo Management',
74
+ 'Agent Mode',
75
+ 'System & Configuration',
76
+ new inquirer.Separator(),
77
+ { name: 'Exit', value: null },
78
+ ],
79
+ }]);
80
+ if (!familyAnswer.family)
81
+ return null;
82
+ const entries = catalog.filter((entry) => entry.family === familyAnswer.family && entry.command.commands.length === 0);
83
+ const commandAnswer = await inquirer.prompt([{
84
+ type: 'list',
85
+ name: 'path',
86
+ message: `${familyAnswer.family}:`,
87
+ pageSize: 20,
88
+ choices: entries.map((entry) => ({
89
+ name: `${entry.path.padEnd(24)} ${entry.description}`,
90
+ value: entry.path,
91
+ })),
92
+ }]);
93
+ const selected = entries.find((entry) => entry.path === commandAnswer.path);
94
+ if (!selected)
95
+ return null;
96
+ return [...selected.path.split(' '), ...await collectRequiredArguments(selected)];
97
+ }
@@ -14,7 +14,13 @@ export function emitDeckEvent(event) {
14
14
  ...event,
15
15
  ts: event.ts ?? Date.now(),
16
16
  };
17
- process.stdout.write(`${DECK_PREFIX}${JSON.stringify(payload)}\n`);
17
+ // Force the marker onto its own fresh line. Callers may have just updated an
18
+ // in-place spinner (e.g. ora) which redraws via "\r" and never emits a
19
+ // trailing "\n", so without this leading newline the marker can land stuck
20
+ // mid-line after visible text (e.g. "Executing plan...@vigthoria-deck:{...}")
21
+ // and the Workbench's line-start prefix check won't strip it, leaking raw
22
+ // JSON into the terminal.
23
+ process.stdout.write(`\n${DECK_PREFIX}${JSON.stringify(payload)}\n`);
18
24
  }
19
25
  export function parseDeckLine(line) {
20
26
  const trimmed = line.trim();
@@ -9,6 +9,8 @@ export type IntentContext = {
9
9
  export declare function stripExecutionShaping(prompt: string): string;
10
10
  export declare function hasBuildVerb(prompt: string): boolean;
11
11
  export declare function hasArtifactNoun(prompt: string): boolean;
12
+ export type GameEngineHint = 'babylon' | 'three' | 'canvas-2d';
13
+ export declare function inferGameEngine(prompt: string): GameEngineHint;
12
14
  export declare function hasGameIntent(prompt: string): boolean;
13
15
  /** True when the CLI already expanded a /continue-style follow-up prompt. */
14
16
  export declare function isBuiltContinuePrompt(prompt: string): boolean;
@@ -52,4 +54,6 @@ export declare function buildExecutionHints(agentTaskType: string, prompt: strin
52
54
  classify_from: string;
53
55
  workflow_type: 'analysis_only' | 'full';
54
56
  fast_path?: string;
57
+ game_engine?: GameEngineHint;
58
+ render_transport?: 'client-state';
55
59
  };
@@ -7,7 +7,7 @@ const READONLY_SHAPING_BLOCK = /(?:^|\n\n)(?:Read-only analysis mode is active\.
7
7
  const BUILD_VERBS = /\b(build|create|make|implement|complete|fix|repair|edit|modify|write|generate|add|finish|scaffold|develop|update|change|refactor|want|need|require|looking for|let's|let us|we need|we should|erstelle|erstellen|schreib|schreibe|bearbeite)\b/i;
8
8
  const ARTIFACT_NOUNS = /\b(file|files|project|game|spiel|app|website|html5|frontend|component|feature|code|workspace|repo|clone|replica|remake|page|site|screen|button|popup|modal|alert|canvas|sprite|level|levels|enemy|enemies|character|npc|world|scene|script|module|api|database|config|landing|dashboard|platformer|arcade|playable|collectible|scoreboard|leaderboard|pitfall|pacman|rogue|html|css|javascript|typescript)\b/i;
9
9
  /** Canvas/playable games only — bare "html5" or "website" must NOT match. */
10
- const GAME_INTENT = /\b(game|spiel|playable|html5\s+(?:game|canvas)|html5\s+game|canvas\s+game|game\s+canvas|arcade|platformer|side[- ]?scroller|pitfall|pac[- ]?man|tetris|snake|breakout|pong|roguelike|metroidvania|tower\s+defense|clone\s+of\s+(?:a\s+)?(?:game|pitfall|pac|mario|zelda|arcade)|gameplay|collectible|boss\s+fight|level\s+design|sprite\s+sheet|wild\s+(?:boar|pig|forest)|forest\s+pig)\b/i;
10
+ const GAME_INTENT = /\b(game|spiel|playable|babylon(?:\.js|js)?|vge|webgl|3d\s+(?:game|runner|prototype|world|experience)|html5\s+(?:game|canvas)|html5\s+game|canvas\s+game|game\s+canvas|arcade|platformer|side[- ]?scroller|pitfall|pac[- ]?man|tetris|snake|breakout|pong|roguelike|metroidvania|tower\s+defense|clone\s+of\s+(?:a\s+)?(?:game|pitfall|pac|mario|zelda|arcade)|gameplay|collectible|boss\s+fight|level\s+design|sprite\s+sheet|wild\s+(?:boar|pig|forest)|forest\s+pig)\b/i;
11
11
  const WEB_PAGE_INTENT = /\b(website|web\s*site|webpage|web\s*page|landing\s+page|home\s*page|index\.html|html\s+page|static\s+page|single\s+page|popup|alert|modal|hello\s+world)\b/i;
12
12
  const CLONE_BUILD = /\bclone\s+of\b|\b(build|create|make)\s+(?:a|an|the|us|me)?\s*(?:clone|replica|remake|port)\b/i;
13
13
  const READ_ONLY_INTENT = /\b(analy[sz]e|analyse|analysis|audit|review|inspect|proof|understand|summari[sz]e|scan|read[\s-]?only|where we left|what is|how does|show me|tell me|identify\s+gaps?|gap\s+analysis|production\s+blockers?)\b/i;
@@ -34,6 +34,16 @@ export function hasArtifactNoun(prompt) {
34
34
  }
35
35
  /** Game clones require a game target — generic "clone of X" must not imply game-build. */
36
36
  const GAME_CLONE_TARGET = /\bclone\s+of\s+(?:a\s+)?(?:the\s+)?(?:game|pitfall|pac[- ]?man|mario|zelda|tetris|snake|arcade|spiel)\b/i;
37
+ export function inferGameEngine(prompt) {
38
+ const text = stripExecutionShaping(prompt);
39
+ if (/\bthree(?:\.js|js)?\b/i.test(text))
40
+ return 'three';
41
+ if (/\bbabylon(?:\.js|js)?\b|\bvge\b|vigthoria gaming engine/i.test(text))
42
+ return 'babylon';
43
+ if (/\b3d\s+(?:game|runner|prototype|world|experience)\b|\bwebgl\b/i.test(text))
44
+ return 'babylon';
45
+ return 'canvas-2d';
46
+ }
37
47
  export function hasGameIntent(prompt) {
38
48
  const text = stripExecutionShaping(prompt);
39
49
  if (GAME_INTENT.test(text)) {
@@ -370,6 +380,11 @@ export function buildExecutionHints(agentTaskType, prompt, options = {}) {
370
380
  classify_from: String(prompt || ''),
371
381
  workflow_type: workflowType,
372
382
  };
383
+ if (kind === 'game-build' || hasGameIntent(prompt)) {
384
+ hints.game_engine = inferGameEngine(prompt);
385
+ if (hints.game_engine === 'babylon')
386
+ hints.render_transport = 'client-state';
387
+ }
373
388
  if (options.fastPath) {
374
389
  hints.fast_path = options.fastPath;
375
390
  }
@@ -2104,6 +2104,24 @@ export class AgenticTools {
2104
2104
  result = await response.json();
2105
2105
  if (response.ok && result.success)
2106
2106
  break;
2107
+ if (result.code === 'REPOSITORY_SECURITY_REVIEW') {
2108
+ return {
2109
+ success: false,
2110
+ error: result.error || 'Repository security review required',
2111
+ metadata: {
2112
+ code: result.code,
2113
+ review: {
2114
+ reviewId: result.reviewId,
2115
+ status: result.status,
2116
+ riskScore: result.riskScore,
2117
+ findings: result.findings || [],
2118
+ actionItems: result.actionItems || [],
2119
+ retryCommand: result.retryCommand,
2120
+ },
2121
+ },
2122
+ suggestion: `Inspect with "vigthoria repo review ${result.reviewId}" and retry corrected content with "${result.retryCommand || `vigthoria repo push --retry ${result.reviewId}`}".`,
2123
+ };
2124
+ }
2107
2125
  lastError = result.error || result.message || `Status ${response.status}`;
2108
2126
  result = null;
2109
2127
  }
package/install.ps1 CHANGED
@@ -5,7 +5,7 @@
5
5
  $ErrorActionPreference = "Stop"
6
6
 
7
7
  # Configuration
8
- $CLI_VERSION = "1.11.42"
8
+ $CLI_VERSION = "1.13.7"
9
9
  $INSTALL_DIR = "$env:USERPROFILE\.vigthoria"
10
10
  $NPM_PACKAGE = "vigthoria-cli"
11
11
  $GIT_PACKAGE_URL = "git+https://market.vigthoria.io/vigthoria/vigthoria-cli.git"
@@ -74,11 +74,12 @@ function Test-Prerequisites {
74
74
  try {
75
75
  $nodeVersion = node -v 2>$null
76
76
  if ($nodeVersion) {
77
- $major = [int]($nodeVersion -replace 'v(\d+)\..*', '$1')
78
- if ($major -ge 18) {
77
+ $versionText = $nodeVersion.TrimStart('v')
78
+ $parsedVersion = [version]$versionText
79
+ if ($parsedVersion -ge [version]"20.19.0") {
79
80
  Write-Host "✓ Node.js $nodeVersion" -ForegroundColor Green
80
81
  } else {
81
- Write-Host "✗ Node.js 18+ required (found $nodeVersion)" -ForegroundColor Red
82
+ Write-Host "✗ Node.js 20.19+ required (found $nodeVersion)" -ForegroundColor Red
82
83
  Write-Host " Download from: https://nodejs.org/" -ForegroundColor Yellow
83
84
  return $false
84
85
  }
package/install.sh CHANGED
@@ -26,7 +26,7 @@ else
26
26
  fi
27
27
 
28
28
  # Configuration
29
- CLI_VERSION="1.11.42"
29
+ CLI_VERSION="1.13.7"
30
30
  INSTALL_DIR="$HOME/.vigthoria"
31
31
  REPO_URL="https://market.vigthoria.io/vigthoria/vigthoria-cli"
32
32
  GIT_PACKAGE_URL="git+https://market.vigthoria.io/vigthoria/vigthoria-cli.git"
@@ -130,7 +130,7 @@ check_requirements() {
130
130
  if ! command -v node &> /dev/null; then
131
131
  echo -e "${RED}✗ Node.js is not installed${NC}"
132
132
  echo ""
133
- echo " Please install Node.js 18 or later:"
133
+ echo " Please install Node.js 20.19 or later:"
134
134
  if [ "$PLATFORM" = "macos" ]; then
135
135
  echo " brew install node"
136
136
  echo " or download from: https://nodejs.org/"
@@ -144,9 +144,11 @@ check_requirements() {
144
144
  exit 1
145
145
  fi
146
146
 
147
- NODE_VERSION=$(node -v | cut -d'v' -f2 | cut -d'.' -f1)
148
- if [ "$NODE_VERSION" -lt 18 ]; then
149
- echo -e "${RED}✗ Node.js version must be 18 or higher (found: v$NODE_VERSION)${NC}"
147
+ NODE_VERSION_FULL=$(node -v | sed 's/^v//')
148
+ NODE_VERSION_MAJOR=$(printf '%s' "$NODE_VERSION_FULL" | cut -d'.' -f1)
149
+ NODE_VERSION_MINOR=$(printf '%s' "$NODE_VERSION_FULL" | cut -d'.' -f2)
150
+ if [ "$NODE_VERSION_MAJOR" -lt 20 ] || { [ "$NODE_VERSION_MAJOR" -eq 20 ] && [ "$NODE_VERSION_MINOR" -lt 19 ]; }; then
151
+ echo -e "${RED}✗ Node.js version must be 20.19 or higher (found: v$NODE_VERSION_FULL)${NC}"
150
152
  exit 1
151
153
  fi
152
154
  echo -e "${GREEN}✓ Node.js v$(node -v | cut -d'v' -f2)${NC}"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vigthoria-cli",
3
- "version": "1.12.3",
3
+ "version": "1.13.7",
4
4
  "description": "Vigthoria Coder CLI - AI-powered terminal coding assistant",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -60,10 +60,12 @@
60
60
  "test:package:hygiene": "npm run build && node scripts/verify-package-hygiene.js",
61
61
  "test:v3:platforms": "npm run build && node scripts/test-v3-platforms-e2e.js",
62
62
  "test:repo:live": "npm run build && node scripts/test-live-repo-e2e.js",
63
+ "test:repo:auth-fallback": "node scripts/test-community-repo-auth-fallback.mjs",
64
+ "test:repo:payload-fallback": "node scripts/test-repo-payload-fallback.mjs",
63
65
  "test:game:live": "npm run build && node scripts/test-live-game-push.js",
64
66
  "proof:agent": "npm run build && node scripts/proof-agent-mode.js",
65
67
  "test:fresh-install": "node scripts/test-fresh-install.js",
66
- "prepublishOnly": "npm run build && npm run validate:no-go && node scripts/verify-package-hygiene.js && node scripts/test-fresh-install.js",
68
+ "prepublishOnly": "npm run build && npm run validate:no-go && npm run test:repo:auth-fallback && npm run test:repo:payload-fallback && node scripts/verify-package-hygiene.js && node scripts/test-fresh-install.js",
67
69
  "precheck:services": "node scripts/precheck-local-services.js",
68
70
  "test:model:governance": "npm run build && node scripts/test-model-governance-no-agent.js",
69
71
  "validate:no-go": "bash scripts/release/validate-no-go-gates.sh",
@@ -73,7 +75,9 @@
73
75
  "test:session:project-match": "npm run build && node scripts/test-session-project-match.mjs",
74
76
  "test:v3-stream-mutation": "npm run build && node scripts/test-v3-stream-mutation.js",
75
77
  "test:context:budget": "npm run build && node scripts/test-context-budget.js",
76
- "test:pitfall:context": "npm run build && node scripts/test-pitfall-context-smoke.js"
78
+ "test:v3-client-tool-quality": "npm run build && node scripts/test-v3-client-tool-quality.mjs",
79
+ "test:pitfall:context": "npm run build && node scripts/test-pitfall-context-smoke.js",
80
+ "test:game:command": "node scripts/test-game-command.mjs"
77
81
  },
78
82
  "keywords": [
79
83
  "ai",
@@ -87,7 +91,7 @@
87
91
  "author": "Vigthoria Technologies",
88
92
  "license": "MIT",
89
93
  "dependencies": {
90
- "archiver": "^7.0.1",
94
+ "archiver": "^8.0.0",
91
95
  "axios": "^1.6.0",
92
96
  "chalk": "^5.3.0",
93
97
  "chokidar": "^5.0.0",
@@ -114,9 +118,10 @@
114
118
  "typescript": "^5.3.2"
115
119
  },
116
120
  "engines": {
117
- "node": ">=18.0.0"
121
+ "node": ">=20.19.0"
118
122
  },
119
123
  "overrides": {
120
- "glob": "^13.0.6"
124
+ "glob": "^13.0.6",
125
+ "brace-expansion": "^5.0.8"
121
126
  }
122
127
  }
@@ -4,7 +4,7 @@ Use this checklist on real user machines before and after rollout. The goal is t
4
4
 
5
5
  ## 1. Preflight
6
6
 
7
- - [ ] Node.js `>=18` available
7
+ - [ ] Node.js `>=20.19` available
8
8
  - [ ] `npm` available
9
9
  - [ ] No stale shell aliases shadowing `vigthoria`
10
10
  - [ ] Network access to:
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env bash
2
2
  set -euo pipefail
3
3
 
4
- ROOT="/var/www/vigthoria-coder/VigthoriaCoderMain/vigthoria-cli"
4
+ ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
5
5
  cd "$ROOT"
6
6
 
7
7
  CLI="node dist/index.js"