vigthoria-cli 1.12.2 → 1.13.6
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/dist/commands/chat.d.ts +3 -1
- package/dist/commands/chat.js +43 -43
- package/dist/commands/game.d.ts +26 -0
- package/dist/commands/game.js +113 -0
- package/dist/commands/repo.d.ts +9 -1
- package/dist/commands/repo.js +178 -29
- package/dist/commands/v4.d.ts +3 -0
- package/dist/commands/v4.js +12 -0
- package/dist/index.js +123 -59
- package/dist/utils/api.d.ts +1 -0
- package/dist/utils/api.js +83 -27
- package/dist/utils/brain-hub-client.js +42 -14
- package/dist/utils/codebase-indexer.js +29 -6
- package/dist/utils/command-menu.d.ts +11 -0
- package/dist/utils/command-menu.js +97 -0
- package/dist/utils/deckEvents.js +7 -1
- package/dist/utils/logger.js +6 -1
- package/dist/utils/requestIntent.d.ts +4 -0
- package/dist/utils/requestIntent.js +16 -1
- package/dist/utils/tools.js +18 -0
- package/package.json +6 -3
|
@@ -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
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
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
|
|
312
|
-
|
|
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
|
+
}
|
package/dist/utils/deckEvents.js
CHANGED
|
@@ -14,7 +14,13 @@ export function emitDeckEvent(event) {
|
|
|
14
14
|
...event,
|
|
15
15
|
ts: event.ts ?? Date.now(),
|
|
16
16
|
};
|
|
17
|
-
|
|
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();
|
package/dist/utils/logger.js
CHANGED
|
@@ -162,11 +162,16 @@ export class Logger {
|
|
|
162
162
|
}
|
|
163
163
|
// Section header
|
|
164
164
|
section(title) {
|
|
165
|
+
// process.stdout.columns is not always a reliable indicator of how many
|
|
166
|
+
// columns actually render visually (seen mismatching badly in some
|
|
167
|
+
// embedded/split-pane terminal hosts on Windows) -- cap at a
|
|
168
|
+
// conservative width so the separator line never overflows even when
|
|
169
|
+
// the reported column count is larger than the real usable width.
|
|
165
170
|
const width = Math.max(24, Number(process.stdout.columns || 80));
|
|
166
171
|
const cleanTitle = String(title || '').replace(/\s+/g, ' ').trim();
|
|
167
172
|
const maxTitle = Math.max(10, width - 10);
|
|
168
173
|
const fittedTitle = cleanTitle.length > maxTitle ? `${cleanTitle.slice(0, maxTitle - 1)}…` : cleanTitle;
|
|
169
|
-
const side = Math.max(3, Math.floor((Math.min(width,
|
|
174
|
+
const side = Math.max(3, Math.floor((Math.min(width, 44) - fittedTitle.length - 2) / 2));
|
|
170
175
|
console.log();
|
|
171
176
|
console.log(chalk.bold.cyan(`${CH.hDouble.repeat(side)} ${fittedTitle} ${CH.hDouble.repeat(side)}`));
|
|
172
177
|
console.log();
|
|
@@ -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
|
}
|
package/dist/utils/tools.js
CHANGED
|
@@ -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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vigthoria-cli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.13.6",
|
|
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,8 @@
|
|
|
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:pitfall:context": "npm run build && node scripts/test-pitfall-context-smoke.js",
|
|
79
|
+
"test:game:command": "node scripts/test-game-command.mjs"
|
|
77
80
|
},
|
|
78
81
|
"keywords": [
|
|
79
82
|
"ai",
|