vigthoria-cli 1.6.17 → 1.6.19
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/auth.js +5 -5
- package/dist/commands/bridge.js +2 -2
- package/dist/commands/chat.js +35 -8
- package/dist/commands/deploy.js +9 -9
- package/dist/commands/edit.d.ts +1 -0
- package/dist/commands/edit.js +10 -5
- package/dist/commands/explain.js +2 -2
- package/dist/commands/generate.js +2 -2
- package/dist/commands/repo.js +10 -10
- package/dist/commands/review.js +2 -2
- package/dist/commands/workflow.js +31 -5
- package/dist/index.js +7 -1
- package/dist/utils/api.d.ts +17 -0
- package/dist/utils/api.js +275 -38
- package/dist/utils/logger.d.ts +7 -0
- package/dist/utils/logger.js +10 -0
- package/package.json +1 -1
package/dist/commands/auth.js
CHANGED
|
@@ -8,8 +8,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
8
8
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
9
|
exports.AuthCommand = void 0;
|
|
10
10
|
const chalk_1 = __importDefault(require("chalk"));
|
|
11
|
-
const ora_1 = __importDefault(require("ora"));
|
|
12
11
|
const inquirer_1 = __importDefault(require("inquirer"));
|
|
12
|
+
const logger_js_1 = require("../utils/logger.js");
|
|
13
13
|
const api_js_1 = require("../utils/api.js");
|
|
14
14
|
class AuthCommand {
|
|
15
15
|
config;
|
|
@@ -73,7 +73,7 @@ class AuthCommand {
|
|
|
73
73
|
validate: (input) => input.length >= 6 || 'Password must be at least 6 characters',
|
|
74
74
|
},
|
|
75
75
|
]);
|
|
76
|
-
const spinner = (0,
|
|
76
|
+
const spinner = (0, logger_js_1.createSpinner)('Logging in...').start();
|
|
77
77
|
const success = await this.api.login(credentials.email, credentials.password);
|
|
78
78
|
spinner.stop();
|
|
79
79
|
if (success) {
|
|
@@ -96,7 +96,7 @@ class AuthCommand {
|
|
|
96
96
|
await this.loginWithToken(token);
|
|
97
97
|
}
|
|
98
98
|
async loginWithToken(token) {
|
|
99
|
-
const spinner = (0,
|
|
99
|
+
const spinner = (0, logger_js_1.createSpinner)('Validating token...').start();
|
|
100
100
|
const success = await this.api.loginWithToken(token);
|
|
101
101
|
spinner.stop();
|
|
102
102
|
if (success) {
|
|
@@ -173,7 +173,7 @@ class AuthCommand {
|
|
|
173
173
|
});
|
|
174
174
|
console.log();
|
|
175
175
|
// API status
|
|
176
|
-
const spinner = (0,
|
|
176
|
+
const spinner = (0, logger_js_1.createSpinner)('Checking API status...').start();
|
|
177
177
|
const apiStatus = await this.api.getHealthStatus();
|
|
178
178
|
spinner.stop();
|
|
179
179
|
console.log(chalk_1.default.white('API Status:'));
|
|
@@ -198,7 +198,7 @@ class AuthCommand {
|
|
|
198
198
|
else {
|
|
199
199
|
console.log(chalk_1.default.gray(' Self-hosted Models: ') + chalk_1.default.gray('disabled'));
|
|
200
200
|
}
|
|
201
|
-
const capabilitySpinner = (0,
|
|
201
|
+
const capabilitySpinner = (0, logger_js_1.createSpinner)('Checking live capability truth...').start();
|
|
202
202
|
const capabilityStatus = await this.api.getCapabilityTruthStatus({
|
|
203
203
|
workspacePath: process.cwd(),
|
|
204
204
|
projectPath: process.cwd(),
|
package/dist/commands/bridge.js
CHANGED
|
@@ -5,7 +5,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.BridgeCommand = void 0;
|
|
7
7
|
const chalk_1 = __importDefault(require("chalk"));
|
|
8
|
-
const
|
|
8
|
+
const logger_js_1 = require("../utils/logger.js");
|
|
9
9
|
const api_js_1 = require("../utils/api.js");
|
|
10
10
|
class BridgeCommand {
|
|
11
11
|
api;
|
|
@@ -13,7 +13,7 @@ class BridgeCommand {
|
|
|
13
13
|
this.api = new api_js_1.APIClient(config, logger);
|
|
14
14
|
}
|
|
15
15
|
async status() {
|
|
16
|
-
const spinner = (0,
|
|
16
|
+
const spinner = (0, logger_js_1.createSpinner)('Checking DevTools Bridge...').start();
|
|
17
17
|
const bridge = await this.api.getDevtoolsBridgeStatus();
|
|
18
18
|
spinner.stop();
|
|
19
19
|
console.log();
|
package/dist/commands/chat.js
CHANGED
|
@@ -38,11 +38,11 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
38
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
39
|
exports.ChatCommand = void 0;
|
|
40
40
|
const chalk_1 = __importDefault(require("chalk"));
|
|
41
|
-
const ora_1 = __importDefault(require("ora"));
|
|
42
41
|
const fs = __importStar(require("fs"));
|
|
43
42
|
const os = __importStar(require("os"));
|
|
44
43
|
const path = __importStar(require("path"));
|
|
45
44
|
const readline = __importStar(require("readline"));
|
|
45
|
+
const logger_js_1 = require("../utils/logger.js");
|
|
46
46
|
const api_js_1 = require("../utils/api.js");
|
|
47
47
|
const tools_js_1 = require("../utils/tools.js");
|
|
48
48
|
const session_js_1 = require("../utils/session.js");
|
|
@@ -118,7 +118,13 @@ class ChatCommand {
|
|
|
118
118
|
return this.getDefaultChatModel();
|
|
119
119
|
}
|
|
120
120
|
isLegacyAgentFallbackAllowed() {
|
|
121
|
-
|
|
121
|
+
// CLI always has local file access, so fallback to local agent loop
|
|
122
|
+
// is safe and should be the default when V3 agent is unreachable or
|
|
123
|
+
// rejects the request (e.g. context too large).
|
|
124
|
+
if (process.env.VIGTHORIA_ALLOW_LEGACY_AGENT_FALLBACK === '0') {
|
|
125
|
+
return false;
|
|
126
|
+
}
|
|
127
|
+
return true;
|
|
122
128
|
}
|
|
123
129
|
resolveAgentExecutionPolicy(prompt) {
|
|
124
130
|
const explicitModel = this.modelExplicitlySelected;
|
|
@@ -543,7 +549,7 @@ class ChatCommand {
|
|
|
543
549
|
const runtimeContext = await this.getPromptRuntimeContext(prompt);
|
|
544
550
|
const resolvedWorkflow = await this.api.resolveVigFlowWorkflow(selector);
|
|
545
551
|
const invocationMode = this.operatorMode ? 'operator' : this.agentMode ? 'agent' : 'chat';
|
|
546
|
-
const spinner = this.jsonOutput ? null : (0,
|
|
552
|
+
const spinner = this.jsonOutput ? null : (0, logger_js_1.createSpinner)({ text: `Running workflow ${resolvedWorkflow.name}...`, spinner: 'clock' }).start();
|
|
547
553
|
try {
|
|
548
554
|
const execution = await this.api.runVigFlowWorkflow(resolvedWorkflow.id, {
|
|
549
555
|
data: {
|
|
@@ -612,7 +618,7 @@ class ChatCommand {
|
|
|
612
618
|
return;
|
|
613
619
|
}
|
|
614
620
|
const runtimeContext = await this.getPromptRuntimeContext(prompt);
|
|
615
|
-
const spinner = this.jsonOutput ? null : (0,
|
|
621
|
+
const spinner = this.jsonOutput ? null : (0, logger_js_1.createSpinner)({ text: 'Thinking like an operator...', spinner: 'clock' }).start();
|
|
616
622
|
const executionPrompt = this.buildExecutionPrompt(prompt);
|
|
617
623
|
const workflowType = this.isDiagnosticPrompt(prompt) ? 'analysis_only' : 'full_autonomy';
|
|
618
624
|
try {
|
|
@@ -657,13 +663,34 @@ class ChatCommand {
|
|
|
657
663
|
if (spinner) {
|
|
658
664
|
spinner.fail('Operator workflow failed');
|
|
659
665
|
}
|
|
660
|
-
|
|
666
|
+
const errorMsg = error.message || 'Operator workflow failed with an unknown error.';
|
|
667
|
+
if (this.jsonOutput) {
|
|
668
|
+
process.exitCode = 1;
|
|
669
|
+
console.log(JSON.stringify({
|
|
670
|
+
success: false,
|
|
671
|
+
mode: 'operator',
|
|
672
|
+
content: '',
|
|
673
|
+
error: errorMsg,
|
|
674
|
+
}, null, 2));
|
|
675
|
+
}
|
|
676
|
+
else {
|
|
677
|
+
this.logger.error(errorMsg);
|
|
678
|
+
}
|
|
661
679
|
}
|
|
662
680
|
}
|
|
663
681
|
async runSimplePrompt(prompt) {
|
|
664
682
|
this.lastActionableUserInput = prompt;
|
|
683
|
+
// In non-agent chat mode the model has no tool access. Inject a
|
|
684
|
+
// grounding constraint so it doesn't fabricate file contents.
|
|
685
|
+
const needsGrounding = /(repo|file|code|project|workspace|source|inspect|analyze|audit|review)/i.test(prompt);
|
|
686
|
+
if (needsGrounding && !this.messages.some(m => m.role === 'system' && m.content.includes('no direct file access'))) {
|
|
687
|
+
this.messages.push({
|
|
688
|
+
role: 'system',
|
|
689
|
+
content: 'You are in simple chat mode with no direct file access. Do not fabricate file contents, search results, or analysis steps. If the user asks about specific files, advise them to use agent mode (vig chat --agent) for grounded repo analysis.',
|
|
690
|
+
});
|
|
691
|
+
}
|
|
665
692
|
this.messages.push({ role: 'user', content: this.buildExecutionPrompt(prompt) });
|
|
666
|
-
const spinner = this.jsonOutput ? null : (0,
|
|
693
|
+
const spinner = this.jsonOutput ? null : (0, logger_js_1.createSpinner)({ text: 'Thinking...', spinner: 'clock' }).start();
|
|
667
694
|
try {
|
|
668
695
|
const response = await this.api.chat(this.getMessagesForModel(), this.currentModel);
|
|
669
696
|
if (spinner)
|
|
@@ -722,7 +749,7 @@ class ChatCommand {
|
|
|
722
749
|
this.saveSession();
|
|
723
750
|
const maxTurns = 10;
|
|
724
751
|
for (let turn = 0; turn < maxTurns; turn += 1) {
|
|
725
|
-
const spinner = this.jsonOutput ? null : (0,
|
|
752
|
+
const spinner = this.jsonOutput ? null : (0, logger_js_1.createSpinner)({ text: turn === 0 ? 'Planning...' : 'Continuing...', spinner: 'clock' }).start();
|
|
726
753
|
try {
|
|
727
754
|
const response = await this.api.chat(this.getMessagesForModel(), this.currentModel);
|
|
728
755
|
if (spinner)
|
|
@@ -935,7 +962,7 @@ class ChatCommand {
|
|
|
935
962
|
this.v3IterationCount = 0;
|
|
936
963
|
this.v3ToolCallCount = 0;
|
|
937
964
|
this.v3LastActivity = Date.now();
|
|
938
|
-
const spinner = this.jsonOutput ? null : (0,
|
|
965
|
+
const spinner = this.jsonOutput ? null : (0, logger_js_1.createSpinner)({
|
|
939
966
|
text: routingPolicy.cloudSelected ? 'Routing heavy task to Vigthoria Cloud...' : 'Routing to V3 Agent...',
|
|
940
967
|
spinner: 'clock',
|
|
941
968
|
}).start();
|
package/dist/commands/deploy.js
CHANGED
|
@@ -53,7 +53,7 @@ exports.DeployCommand = void 0;
|
|
|
53
53
|
const chalk_1 = __importDefault(require("chalk"));
|
|
54
54
|
const fs = __importStar(require("fs"));
|
|
55
55
|
const path = __importStar(require("path"));
|
|
56
|
-
const
|
|
56
|
+
const logger_js_1 = require("../utils/logger.js");
|
|
57
57
|
const inquirer_1 = __importDefault(require("inquirer"));
|
|
58
58
|
class DeployCommand {
|
|
59
59
|
config;
|
|
@@ -127,7 +127,7 @@ class DeployCommand {
|
|
|
127
127
|
* Deploy to preview URL (free)
|
|
128
128
|
*/
|
|
129
129
|
async deployToPreview(projectPath) {
|
|
130
|
-
const spinner = (0,
|
|
130
|
+
const spinner = (0, logger_js_1.createSpinner)('Deploying to preview...').start();
|
|
131
131
|
try {
|
|
132
132
|
const projectDir = projectPath || process.cwd();
|
|
133
133
|
const projectInfo = this.detectProjectInfo(projectDir);
|
|
@@ -160,7 +160,7 @@ class DeployCommand {
|
|
|
160
160
|
* Deploy to Vigthoria subdomain
|
|
161
161
|
*/
|
|
162
162
|
async deployToSubdomain(subdomain, projectPath) {
|
|
163
|
-
const spinner = (0,
|
|
163
|
+
const spinner = (0, logger_js_1.createSpinner)(`Deploying to ${subdomain}.vigthoria.io...`).start();
|
|
164
164
|
try {
|
|
165
165
|
// Validate subdomain format
|
|
166
166
|
if (!/^[a-z0-9][a-z0-9-]*[a-z0-9]$/.test(subdomain) || subdomain.length < 3) {
|
|
@@ -213,7 +213,7 @@ class DeployCommand {
|
|
|
213
213
|
* Deploy to custom domain
|
|
214
214
|
*/
|
|
215
215
|
async deployToCustomDomain(domain, projectPath) {
|
|
216
|
-
const spinner = (0,
|
|
216
|
+
const spinner = (0, logger_js_1.createSpinner)(`Setting up ${domain}...`).start();
|
|
217
217
|
try {
|
|
218
218
|
const projectDir = projectPath || process.cwd();
|
|
219
219
|
const projectInfo = this.detectProjectInfo(projectDir);
|
|
@@ -303,7 +303,7 @@ class DeployCommand {
|
|
|
303
303
|
* Show hosting plans
|
|
304
304
|
*/
|
|
305
305
|
async showPlans() {
|
|
306
|
-
const spinner = (0,
|
|
306
|
+
const spinner = (0, logger_js_1.createSpinner)('Fetching hosting plans...').start();
|
|
307
307
|
try {
|
|
308
308
|
const response = await fetch(`${this.apiBase}/api/hosting/plans`, {
|
|
309
309
|
headers: this.getAuthHeaders()
|
|
@@ -344,7 +344,7 @@ class DeployCommand {
|
|
|
344
344
|
*/
|
|
345
345
|
async list() {
|
|
346
346
|
this.requireAuth();
|
|
347
|
-
const spinner = (0,
|
|
347
|
+
const spinner = (0, logger_js_1.createSpinner)('Fetching deployments...').start();
|
|
348
348
|
try {
|
|
349
349
|
const response = await fetch(`${this.apiBase}/api/hosting/domains`, {
|
|
350
350
|
headers: this.getAuthHeaders()
|
|
@@ -384,7 +384,7 @@ class DeployCommand {
|
|
|
384
384
|
*/
|
|
385
385
|
async status(domain) {
|
|
386
386
|
this.requireAuth();
|
|
387
|
-
const spinner = (0,
|
|
387
|
+
const spinner = (0, logger_js_1.createSpinner)('Checking status...').start();
|
|
388
388
|
try {
|
|
389
389
|
const endpoint = domain
|
|
390
390
|
? `${this.apiBase}/api/hosting/domain/${encodeURIComponent(domain)}/status`
|
|
@@ -411,7 +411,7 @@ class DeployCommand {
|
|
|
411
411
|
*/
|
|
412
412
|
async verify(domain) {
|
|
413
413
|
this.requireAuth();
|
|
414
|
-
const spinner = (0,
|
|
414
|
+
const spinner = (0, logger_js_1.createSpinner)(`Verifying DNS for ${domain}...`).start();
|
|
415
415
|
try {
|
|
416
416
|
const response = await fetch(`${this.apiBase}/api/hosting/domain/verify`, {
|
|
417
417
|
method: 'POST',
|
|
@@ -454,7 +454,7 @@ class DeployCommand {
|
|
|
454
454
|
console.log(chalk_1.default.yellow('\n⚠️ Removal cancelled.\n'));
|
|
455
455
|
return;
|
|
456
456
|
}
|
|
457
|
-
const spinner = (0,
|
|
457
|
+
const spinner = (0, logger_js_1.createSpinner)(`Removing ${domain}...`).start();
|
|
458
458
|
try {
|
|
459
459
|
const response = await fetch(`${this.apiBase}/api/hosting/domain/${encodeURIComponent(domain)}`, {
|
|
460
460
|
method: 'DELETE',
|
package/dist/commands/edit.d.ts
CHANGED
package/dist/commands/edit.js
CHANGED
|
@@ -8,8 +8,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
8
8
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
9
|
exports.EditCommand = void 0;
|
|
10
10
|
const chalk_1 = __importDefault(require("chalk"));
|
|
11
|
-
const ora_1 = __importDefault(require("ora"));
|
|
12
11
|
const inquirer_1 = __importDefault(require("inquirer"));
|
|
12
|
+
const logger_js_1 = require("../utils/logger.js");
|
|
13
13
|
const api_js_1 = require("../utils/api.js");
|
|
14
14
|
const files_js_1 = require("../utils/files.js");
|
|
15
15
|
class EditCommand {
|
|
@@ -52,7 +52,7 @@ class EditCommand {
|
|
|
52
52
|
instruction = answer.instruction;
|
|
53
53
|
}
|
|
54
54
|
// Generate edit
|
|
55
|
-
const spinner = (0,
|
|
55
|
+
const spinner = (0, logger_js_1.createSpinner)({
|
|
56
56
|
text: 'Generating changes...',
|
|
57
57
|
spinner: 'dots',
|
|
58
58
|
}).start();
|
|
@@ -86,8 +86,13 @@ Return the complete modified code:`,
|
|
|
86
86
|
this.logger.error('Failed to generate valid code changes');
|
|
87
87
|
return;
|
|
88
88
|
}
|
|
89
|
-
// Show diff
|
|
90
|
-
|
|
89
|
+
// Show diff and apply
|
|
90
|
+
if (options.apply) {
|
|
91
|
+
await this.applyFix(file.path, file.content, modifiedCode);
|
|
92
|
+
}
|
|
93
|
+
else {
|
|
94
|
+
await this.showDiffAndConfirm(file.path, file.content, modifiedCode);
|
|
95
|
+
}
|
|
91
96
|
}
|
|
92
97
|
catch (error) {
|
|
93
98
|
spinner.stop();
|
|
@@ -109,7 +114,7 @@ Return the complete modified code:`,
|
|
|
109
114
|
this.logger.section(`Fixing: ${file.relativePath}`);
|
|
110
115
|
console.log(chalk_1.default.gray(`Fix type: ${options.type} | Language: ${file.language}`));
|
|
111
116
|
console.log();
|
|
112
|
-
const spinner = (0,
|
|
117
|
+
const spinner = (0, logger_js_1.createSpinner)({
|
|
113
118
|
text: `Analyzing for ${options.type} issues...`,
|
|
114
119
|
spinner: 'dots',
|
|
115
120
|
}).start();
|
package/dist/commands/explain.js
CHANGED
|
@@ -8,9 +8,9 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
8
8
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
9
|
exports.ExplainCommand = void 0;
|
|
10
10
|
const chalk_1 = __importDefault(require("chalk"));
|
|
11
|
-
const ora_1 = __importDefault(require("ora"));
|
|
12
11
|
const marked_1 = require("marked");
|
|
13
12
|
const marked_terminal_1 = require("marked-terminal");
|
|
13
|
+
const logger_js_1 = require("../utils/logger.js");
|
|
14
14
|
const api_js_1 = require("../utils/api.js");
|
|
15
15
|
const files_js_1 = require("../utils/files.js");
|
|
16
16
|
class ExplainCommand {
|
|
@@ -61,7 +61,7 @@ class ExplainCommand {
|
|
|
61
61
|
});
|
|
62
62
|
console.log(chalk_1.default.gray('─'.repeat(60)));
|
|
63
63
|
console.log();
|
|
64
|
-
const spinner = (0,
|
|
64
|
+
const spinner = (0, logger_js_1.createSpinner)({
|
|
65
65
|
text: 'Analyzing code...',
|
|
66
66
|
spinner: 'dots',
|
|
67
67
|
}).start();
|
|
@@ -10,8 +10,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
10
10
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
11
|
exports.GenerateCommand = void 0;
|
|
12
12
|
const chalk_1 = __importDefault(require("chalk"));
|
|
13
|
-
const ora_1 = __importDefault(require("ora"));
|
|
14
13
|
const inquirer_1 = __importDefault(require("inquirer"));
|
|
14
|
+
const logger_js_1 = require("../utils/logger.js");
|
|
15
15
|
const api_js_1 = require("../utils/api.js");
|
|
16
16
|
const files_js_1 = require("../utils/files.js");
|
|
17
17
|
class GenerateCommand {
|
|
@@ -40,7 +40,7 @@ class GenerateCommand {
|
|
|
40
40
|
console.log(chalk_1.default.cyan('Pro Mode: Planning → Generating → Quality Check'));
|
|
41
41
|
}
|
|
42
42
|
console.log();
|
|
43
|
-
const spinner = (0,
|
|
43
|
+
const spinner = (0, logger_js_1.createSpinner)({
|
|
44
44
|
text: proMode ? 'Phase 1: Planning project structure...' : 'Generating code...',
|
|
45
45
|
spinner: 'dots',
|
|
46
46
|
}).start();
|
package/dist/commands/repo.js
CHANGED
|
@@ -54,7 +54,7 @@ exports.RepoCommand = void 0;
|
|
|
54
54
|
const chalk_1 = __importDefault(require("chalk"));
|
|
55
55
|
const fs = __importStar(require("fs"));
|
|
56
56
|
const path = __importStar(require("path"));
|
|
57
|
-
const
|
|
57
|
+
const logger_js_1 = require("../utils/logger.js");
|
|
58
58
|
const inquirer_1 = __importDefault(require("inquirer"));
|
|
59
59
|
const archiver_1 = __importDefault(require("archiver"));
|
|
60
60
|
const fs_1 = require("fs");
|
|
@@ -313,7 +313,7 @@ class RepoCommand {
|
|
|
313
313
|
console.log(chalk_1.default.red(`\n❌ Path does not exist: ${projectPath}\n`));
|
|
314
314
|
return;
|
|
315
315
|
}
|
|
316
|
-
const spinner = (0,
|
|
316
|
+
const spinner = (0, logger_js_1.createSpinner)('Analyzing project...').start();
|
|
317
317
|
try {
|
|
318
318
|
const projectInfo = this.detectProjectInfo(projectPath);
|
|
319
319
|
spinner.succeed(`Project detected: ${chalk_1.default.cyan(projectInfo.name)}`);
|
|
@@ -363,7 +363,7 @@ class RepoCommand {
|
|
|
363
363
|
console.log(chalk_1.default.yellow('\n⚠️ Push cancelled.\n'));
|
|
364
364
|
return;
|
|
365
365
|
}
|
|
366
|
-
const uploadSpinner = (0,
|
|
366
|
+
const uploadSpinner = (0, logger_js_1.createSpinner)('Preparing project files...').start();
|
|
367
367
|
const files = this.collectProjectFiles(projectPath);
|
|
368
368
|
if (files.length === 0) {
|
|
369
369
|
throw new Error('No readable text files found to push');
|
|
@@ -409,7 +409,7 @@ class RepoCommand {
|
|
|
409
409
|
*/
|
|
410
410
|
async pull(projectName, options = {}) {
|
|
411
411
|
this.requireAuth();
|
|
412
|
-
const spinner = (0,
|
|
412
|
+
const spinner = (0, logger_js_1.createSpinner)(`Fetching project: ${projectName}...`).start();
|
|
413
413
|
try {
|
|
414
414
|
const repo = await this.resolveRepoByName(projectName);
|
|
415
415
|
const response = await this.repoFetch('/api/repo/pull', {
|
|
@@ -437,7 +437,7 @@ class RepoCommand {
|
|
|
437
437
|
return;
|
|
438
438
|
}
|
|
439
439
|
}
|
|
440
|
-
const downloadSpinner = (0,
|
|
440
|
+
const downloadSpinner = (0, logger_js_1.createSpinner)('Downloading project files...').start();
|
|
441
441
|
// Create output directory
|
|
442
442
|
fs.mkdirSync(outputPath, { recursive: true });
|
|
443
443
|
// If we have a download URL, fetch and extract
|
|
@@ -496,7 +496,7 @@ class RepoCommand {
|
|
|
496
496
|
*/
|
|
497
497
|
async list(options = {}) {
|
|
498
498
|
this.requireAuth();
|
|
499
|
-
const spinner = (0,
|
|
499
|
+
const spinner = (0, logger_js_1.createSpinner)('Fetching your projects...').start();
|
|
500
500
|
try {
|
|
501
501
|
const repos = await this.getMyRepos();
|
|
502
502
|
const filteredRepos = options.visibility
|
|
@@ -596,7 +596,7 @@ class RepoCommand {
|
|
|
596
596
|
this.requireAuth();
|
|
597
597
|
const projectPath = process.cwd();
|
|
598
598
|
const projectInfo = this.detectProjectInfo(projectPath);
|
|
599
|
-
const spinner = (0,
|
|
599
|
+
const spinner = (0, logger_js_1.createSpinner)('Checking sync status...').start();
|
|
600
600
|
try {
|
|
601
601
|
const response = await fetch(`${this.apiBase}/api/repo/status/${encodeURIComponent(projectInfo.name)}`, {
|
|
602
602
|
method: 'GET',
|
|
@@ -644,7 +644,7 @@ class RepoCommand {
|
|
|
644
644
|
*/
|
|
645
645
|
async share(projectName, options = {}) {
|
|
646
646
|
this.requireAuth();
|
|
647
|
-
const spinner = (0,
|
|
647
|
+
const spinner = (0, logger_js_1.createSpinner)('Generating share link...').start();
|
|
648
648
|
try {
|
|
649
649
|
const response = await fetch(`${this.apiBase}/api/repo/share`, {
|
|
650
650
|
method: 'POST',
|
|
@@ -686,7 +686,7 @@ class RepoCommand {
|
|
|
686
686
|
console.log(chalk_1.default.yellow('\n⚠️ Delete cancelled.\n'));
|
|
687
687
|
return;
|
|
688
688
|
}
|
|
689
|
-
const spinner = (0,
|
|
689
|
+
const spinner = (0, logger_js_1.createSpinner)('Deleting project...').start();
|
|
690
690
|
try {
|
|
691
691
|
const response = await fetch(`${this.apiBase}/api/repo/projects/${encodeURIComponent(projectName)}`, {
|
|
692
692
|
method: 'DELETE',
|
|
@@ -712,7 +712,7 @@ class RepoCommand {
|
|
|
712
712
|
// Parse project URL or name
|
|
713
713
|
const projectIdMatch = projectUrl.match(/preview\/(\d+)/);
|
|
714
714
|
const projectId = projectIdMatch ? projectIdMatch[1] : projectUrl;
|
|
715
|
-
const spinner = (0,
|
|
715
|
+
const spinner = (0, logger_js_1.createSpinner)(`Cloning project...`).start();
|
|
716
716
|
try {
|
|
717
717
|
const response = await fetch(`${this.apiBase}/api/repo/clone/${projectId}`, {
|
|
718
718
|
method: 'GET',
|
package/dist/commands/review.js
CHANGED
|
@@ -8,9 +8,9 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
8
8
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
9
|
exports.ReviewCommand = void 0;
|
|
10
10
|
const chalk_1 = __importDefault(require("chalk"));
|
|
11
|
-
const ora_1 = __importDefault(require("ora"));
|
|
12
11
|
const marked_1 = require("marked");
|
|
13
12
|
const marked_terminal_1 = require("marked-terminal");
|
|
13
|
+
const logger_js_1 = require("../utils/logger.js");
|
|
14
14
|
const api_js_1 = require("../utils/api.js");
|
|
15
15
|
const files_js_1 = require("../utils/files.js");
|
|
16
16
|
class ReviewCommand {
|
|
@@ -42,7 +42,7 @@ class ReviewCommand {
|
|
|
42
42
|
this.logger.section(`Reviewing: ${file.relativePath}`);
|
|
43
43
|
console.log(chalk_1.default.gray(`Language: ${file.language} | Lines: ${file.lines}`));
|
|
44
44
|
console.log();
|
|
45
|
-
const spinner = (0,
|
|
45
|
+
const spinner = (0, logger_js_1.createSpinner)({
|
|
46
46
|
text: 'Analyzing code quality...',
|
|
47
47
|
spinner: 'dots',
|
|
48
48
|
}).start();
|
|
@@ -48,10 +48,23 @@ class WorkflowCommand {
|
|
|
48
48
|
}
|
|
49
49
|
async templates(options) {
|
|
50
50
|
this.ensureAuthenticated(Boolean(options.json));
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
51
|
+
let templates;
|
|
52
|
+
try {
|
|
53
|
+
templates = await this.api.listVigFlowTemplates({
|
|
54
|
+
category: options.category,
|
|
55
|
+
search: options.search,
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
catch (error) {
|
|
59
|
+
const msg = `Workflow service is not reachable: ${error.message}`;
|
|
60
|
+
if (options.json) {
|
|
61
|
+
this.printJson({ success: false, error: msg, templates: [] });
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
this.logger.error(msg);
|
|
65
|
+
}
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
55
68
|
if (options.json) {
|
|
56
69
|
this.printJson({ success: true, templates });
|
|
57
70
|
return;
|
|
@@ -70,7 +83,20 @@ class WorkflowCommand {
|
|
|
70
83
|
}
|
|
71
84
|
async list(options) {
|
|
72
85
|
this.ensureAuthenticated(Boolean(options.json));
|
|
73
|
-
|
|
86
|
+
let workflows;
|
|
87
|
+
try {
|
|
88
|
+
workflows = await this.api.listVigFlowWorkflows();
|
|
89
|
+
}
|
|
90
|
+
catch (error) {
|
|
91
|
+
const msg = `Workflow service is not reachable: ${error.message}`;
|
|
92
|
+
if (options.json) {
|
|
93
|
+
this.printJson({ success: false, error: msg, workflows: [] });
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
this.logger.error(msg);
|
|
97
|
+
}
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
74
100
|
if (options.json) {
|
|
75
101
|
this.printJson({ success: true, workflows });
|
|
76
102
|
return;
|
package/dist/index.js
CHANGED
|
@@ -316,6 +316,7 @@ async function main() {
|
|
|
316
316
|
.description('Edit a file with AI assistance')
|
|
317
317
|
.option('-i, --instruction <text>', 'Editing instruction')
|
|
318
318
|
.option('-m, --model <model>', 'Select AI model', 'code')
|
|
319
|
+
.option('--apply', 'Automatically apply changes without confirmation', false)
|
|
319
320
|
.action(async (file, options) => {
|
|
320
321
|
const edit = new edit_js_1.EditCommand(config, logger);
|
|
321
322
|
await edit.run(file, options);
|
|
@@ -346,12 +347,17 @@ async function main() {
|
|
|
346
347
|
});
|
|
347
348
|
// Fix command - Fix code issues
|
|
348
349
|
program
|
|
349
|
-
.command('fix
|
|
350
|
+
.command('fix [file]')
|
|
350
351
|
.alias('f')
|
|
351
352
|
.description('Fix issues in a file')
|
|
352
353
|
.option('-t, --type <type>', 'Fix type (bugs, style, security, performance)', 'bugs')
|
|
353
354
|
.option('--apply', 'Automatically apply fixes', false)
|
|
354
355
|
.action(async (file, options) => {
|
|
356
|
+
if (!file) {
|
|
357
|
+
logger.error('Usage: vigthoria fix <file> [--type bugs|style|security|performance] [--apply]');
|
|
358
|
+
process.exitCode = 1;
|
|
359
|
+
return;
|
|
360
|
+
}
|
|
355
361
|
const edit = new edit_js_1.EditCommand(config, logger);
|
|
356
362
|
await edit.fix(file, options);
|
|
357
363
|
});
|
package/dist/utils/api.d.ts
CHANGED
|
@@ -227,7 +227,19 @@ export declare class APIClient {
|
|
|
227
227
|
executionOptions?: Record<string, unknown>;
|
|
228
228
|
}): Promise<VigFlowExecutionResult>;
|
|
229
229
|
getVigFlowExecutionStatus(executionId: string): Promise<VigFlowExecutionStatus>;
|
|
230
|
+
/** Maximum serialized context length accepted by the V3 server. */
|
|
231
|
+
private static readonly V3_CONTEXT_CHAR_LIMIT;
|
|
230
232
|
buildV3AgentContext(context?: Record<string, any>): string;
|
|
233
|
+
/**
|
|
234
|
+
* Compact a V3 context payload so the serialized JSON stays under
|
|
235
|
+
* the server's character limit. Progressively sheds bulk:
|
|
236
|
+
* 1. Trim workspaceFiles values to fit budget
|
|
237
|
+
* 2. Drop workspaceFiles entirely
|
|
238
|
+
* 3. Truncate history
|
|
239
|
+
* 4. Truncate file list
|
|
240
|
+
* 5. Drop readmeExcerpt
|
|
241
|
+
*/
|
|
242
|
+
private compactV3Context;
|
|
231
243
|
buildMinimalV3AgentContext(context?: Record<string, any>): string;
|
|
232
244
|
private extractEmergencyAppName;
|
|
233
245
|
private materializeEmergencySaaSWorkspace;
|
|
@@ -237,6 +249,11 @@ export declare class APIClient {
|
|
|
237
249
|
private isLikelyWindowsPath;
|
|
238
250
|
private resolveServerBindableWorkspacePath;
|
|
239
251
|
private buildLocalWorkspaceSummary;
|
|
252
|
+
/**
|
|
253
|
+
* Collect text file contents from the workspace for V3 agent hydration.
|
|
254
|
+
* Budget: up to ~2 MB total, per-file cap 200 KB, skip binary extensions.
|
|
255
|
+
*/
|
|
256
|
+
collectWorkspaceFileContents(rootPath: string, filePaths: string[]): Record<string, string>;
|
|
240
257
|
hasAgentWorkspaceOutput(context?: Record<string, any>): boolean;
|
|
241
258
|
getAgentWorkspaceSnapshot(rootPath: string): {
|
|
242
259
|
fileCount: number;
|
package/dist/utils/api.js
CHANGED
|
@@ -306,12 +306,14 @@ class APIClient {
|
|
|
306
306
|
return [...new Set(urls)];
|
|
307
307
|
}
|
|
308
308
|
getVigFlowBaseUrls() {
|
|
309
|
+
const configuredApiUrl = String(this.config.get('apiUrl') || 'https://coder.vigthoria.io').replace(/\/$/, '');
|
|
309
310
|
const urls = [
|
|
310
311
|
process.env.VIGTHORIA_VIGFLOW_URL,
|
|
311
312
|
process.env.VIGFLOW_URL,
|
|
312
313
|
process.env.WORKFLOW_BUILDER_URL,
|
|
313
314
|
'http://127.0.0.1:5060',
|
|
314
315
|
'http://127.0.0.1:5050',
|
|
316
|
+
`${configuredApiUrl}/api/vigflow`,
|
|
315
317
|
].filter(Boolean).map((url) => String(url).replace(/\/$/, ''));
|
|
316
318
|
return [...new Set(urls)];
|
|
317
319
|
}
|
|
@@ -593,26 +595,37 @@ class APIClient {
|
|
|
593
595
|
error: artifacts.error || 'Frontend preview gate could not collect frontend artifacts.',
|
|
594
596
|
};
|
|
595
597
|
}
|
|
596
|
-
|
|
597
|
-
const
|
|
598
|
-
const
|
|
598
|
+
// Cap artifact sizes to prevent 413 Payload Too Large
|
|
599
|
+
const PREVIEW_MAX_ARTIFACT_BYTES = 500 * 1024;
|
|
600
|
+
const html = artifacts.html.slice(0, PREVIEW_MAX_ARTIFACT_BYTES);
|
|
601
|
+
const css = (artifacts.css || '').slice(0, PREVIEW_MAX_ARTIFACT_BYTES);
|
|
602
|
+
const js = (artifacts.js || '').slice(0, PREVIEW_MAX_ARTIFACT_BYTES);
|
|
599
603
|
const errors = [];
|
|
600
604
|
for (const baseUrl of this.getTemplateServiceBaseUrls()) {
|
|
601
605
|
try {
|
|
606
|
+
const proofPayload = JSON.stringify({
|
|
607
|
+
vision: String(context.rawPrompt || message || '').slice(0, 2000),
|
|
608
|
+
html,
|
|
609
|
+
css,
|
|
610
|
+
js,
|
|
611
|
+
entryPath: artifacts.htmlPath,
|
|
612
|
+
workspaceName: path_1.default.basename(rootPath),
|
|
613
|
+
});
|
|
614
|
+
// Skip preview proof if payload is still too large (> 4 MB)
|
|
615
|
+
if (Buffer.byteLength(proofPayload, 'utf8') > 4 * 1024 * 1024) {
|
|
616
|
+
return {
|
|
617
|
+
required: true,
|
|
618
|
+
passed: true,
|
|
619
|
+
error: 'Preview proof skipped: payload exceeds size limit.',
|
|
620
|
+
};
|
|
621
|
+
}
|
|
602
622
|
const response = await fetch(`${baseUrl}/preview-proof`, {
|
|
603
623
|
method: 'POST',
|
|
604
624
|
headers: {
|
|
605
625
|
'Content-Type': 'application/json',
|
|
606
626
|
Accept: 'application/json',
|
|
607
627
|
},
|
|
608
|
-
body:
|
|
609
|
-
vision: String(context.rawPrompt || message || ''),
|
|
610
|
-
html,
|
|
611
|
-
css,
|
|
612
|
-
js,
|
|
613
|
-
entryPath: artifacts.htmlPath,
|
|
614
|
-
workspaceName: path_1.default.basename(rootPath),
|
|
615
|
-
}),
|
|
628
|
+
body: proofPayload,
|
|
616
629
|
});
|
|
617
630
|
if (!response.ok) {
|
|
618
631
|
const errorText = await response.text().catch(() => '');
|
|
@@ -877,6 +890,8 @@ class APIClient {
|
|
|
877
890
|
return payload.execution;
|
|
878
891
|
});
|
|
879
892
|
}
|
|
893
|
+
/** Maximum serialized context length accepted by the V3 server. */
|
|
894
|
+
static V3_CONTEXT_CHAR_LIMIT = 95_000;
|
|
880
895
|
buildV3AgentContext(context = {}) {
|
|
881
896
|
const resolvedContext = this.ensureExecutionContext(context);
|
|
882
897
|
const targetPath = resolvedContext.targetPath || resolvedContext.projectPath || resolvedContext.workspacePath || resolvedContext.projectRoot || process.cwd();
|
|
@@ -885,7 +900,7 @@ class APIClient {
|
|
|
885
900
|
const localWorkspaceSummary = this.buildLocalWorkspaceSummary(localWorkspacePath);
|
|
886
901
|
const requestedModel = String(resolvedContext.model || resolvedContext.requestedModel || 'agent');
|
|
887
902
|
const resolvedModel = this.resolvePermittedModelId(requestedModel);
|
|
888
|
-
|
|
903
|
+
const payload = {
|
|
889
904
|
workspace: resolvedContext.workspace || null,
|
|
890
905
|
activeFile: resolvedContext.activeFile || null,
|
|
891
906
|
history: resolvedContext.history || [],
|
|
@@ -911,7 +926,89 @@ class APIClient {
|
|
|
911
926
|
requestStartedAt: resolvedContext.requestStartedAt,
|
|
912
927
|
subscriptionPlan: this.config.getNormalizedPlan() || null,
|
|
913
928
|
email: this.config.get('email') || null,
|
|
914
|
-
}
|
|
929
|
+
};
|
|
930
|
+
return this.compactV3Context(payload);
|
|
931
|
+
}
|
|
932
|
+
/**
|
|
933
|
+
* Compact a V3 context payload so the serialized JSON stays under
|
|
934
|
+
* the server's character limit. Progressively sheds bulk:
|
|
935
|
+
* 1. Trim workspaceFiles values to fit budget
|
|
936
|
+
* 2. Drop workspaceFiles entirely
|
|
937
|
+
* 3. Truncate history
|
|
938
|
+
* 4. Truncate file list
|
|
939
|
+
* 5. Drop readmeExcerpt
|
|
940
|
+
*/
|
|
941
|
+
compactV3Context(payload) {
|
|
942
|
+
const LIMIT = APIClient.V3_CONTEXT_CHAR_LIMIT;
|
|
943
|
+
let json = JSON.stringify(payload);
|
|
944
|
+
if (json.length <= LIMIT)
|
|
945
|
+
return json;
|
|
946
|
+
// Phase 1 — shrink workspaceFiles to fit
|
|
947
|
+
const summary = payload.localWorkspaceSummary;
|
|
948
|
+
if (summary?.workspaceFiles && typeof summary.workspaceFiles === 'object') {
|
|
949
|
+
const fileEntries = Object.entries(summary.workspaceFiles);
|
|
950
|
+
const overhead = json.length - JSON.stringify(summary.workspaceFiles).length;
|
|
951
|
+
const budget = LIMIT - overhead - 512; // reserve a little headroom
|
|
952
|
+
if (budget > 0) {
|
|
953
|
+
const trimmed = {};
|
|
954
|
+
let used = 2; // {}
|
|
955
|
+
for (const [k, v] of fileEntries) {
|
|
956
|
+
const entryLen = JSON.stringify(k).length + 1 + JSON.stringify(v).length + 1;
|
|
957
|
+
if (used + entryLen > budget)
|
|
958
|
+
break;
|
|
959
|
+
trimmed[k] = v;
|
|
960
|
+
used += entryLen;
|
|
961
|
+
}
|
|
962
|
+
summary.workspaceFiles = trimmed;
|
|
963
|
+
}
|
|
964
|
+
else {
|
|
965
|
+
delete summary.workspaceFiles;
|
|
966
|
+
}
|
|
967
|
+
json = JSON.stringify(payload);
|
|
968
|
+
if (json.length <= LIMIT)
|
|
969
|
+
return json;
|
|
970
|
+
}
|
|
971
|
+
// Phase 2 — drop workspaceFiles entirely
|
|
972
|
+
if (summary?.workspaceFiles) {
|
|
973
|
+
delete summary.workspaceFiles;
|
|
974
|
+
json = JSON.stringify(payload);
|
|
975
|
+
if (json.length <= LIMIT)
|
|
976
|
+
return json;
|
|
977
|
+
}
|
|
978
|
+
// Phase 3 — truncate history to last 6 messages
|
|
979
|
+
if (Array.isArray(payload.history) && payload.history.length > 6) {
|
|
980
|
+
payload.history = payload.history.slice(-6);
|
|
981
|
+
json = JSON.stringify(payload);
|
|
982
|
+
if (json.length <= LIMIT)
|
|
983
|
+
return json;
|
|
984
|
+
}
|
|
985
|
+
// Phase 4 — truncate file list
|
|
986
|
+
if (summary?.files && Array.isArray(summary.files) && summary.files.length > 15) {
|
|
987
|
+
summary.files = summary.files.slice(0, 15);
|
|
988
|
+
json = JSON.stringify(payload);
|
|
989
|
+
if (json.length <= LIMIT)
|
|
990
|
+
return json;
|
|
991
|
+
}
|
|
992
|
+
// Phase 5 — drop readmeExcerpt
|
|
993
|
+
if (summary?.readmeExcerpt) {
|
|
994
|
+
delete summary.readmeExcerpt;
|
|
995
|
+
json = JSON.stringify(payload);
|
|
996
|
+
if (json.length <= LIMIT)
|
|
997
|
+
return json;
|
|
998
|
+
}
|
|
999
|
+
// Phase 6 — drop history entirely
|
|
1000
|
+
if (Array.isArray(payload.history) && payload.history.length > 0) {
|
|
1001
|
+
payload.history = [];
|
|
1002
|
+
json = JSON.stringify(payload);
|
|
1003
|
+
if (json.length <= LIMIT)
|
|
1004
|
+
return json;
|
|
1005
|
+
}
|
|
1006
|
+
// Phase 7 — drop localWorkspaceSummary entirely as last resort
|
|
1007
|
+
if (payload.localWorkspaceSummary) {
|
|
1008
|
+
payload.localWorkspaceSummary = { path: summary?.path, name: summary?.name, fileCount: summary?.fileCount };
|
|
1009
|
+
json = JSON.stringify(payload);
|
|
1010
|
+
}
|
|
1011
|
+
return json;
|
|
915
1012
|
}
|
|
916
1013
|
buildMinimalV3AgentContext(context = {}) {
|
|
917
1014
|
const resolvedContext = this.ensureExecutionContext(context);
|
|
@@ -1553,6 +1650,9 @@ menu {
|
|
|
1553
1650
|
if (fs_1.default.existsSync(readmePath)) {
|
|
1554
1651
|
summary.readmeExcerpt = fs_1.default.readFileSync(readmePath, 'utf8').slice(0, 2500);
|
|
1555
1652
|
}
|
|
1653
|
+
// Hydrate workspace: include actual file contents so the V3 server
|
|
1654
|
+
// can populate the remote workspace before the agent starts.
|
|
1655
|
+
summary.workspaceFiles = this.collectWorkspaceFileContents(rootPath, snapshot.paths);
|
|
1556
1656
|
return summary;
|
|
1557
1657
|
}
|
|
1558
1658
|
catch (error) {
|
|
@@ -1560,6 +1660,52 @@ menu {
|
|
|
1560
1660
|
return null;
|
|
1561
1661
|
}
|
|
1562
1662
|
}
|
|
1663
|
+
/**
|
|
1664
|
+
* Collect text file contents from the workspace for V3 agent hydration.
|
|
1665
|
+
* Budget: up to ~2 MB total, per-file cap 200 KB, skip binary extensions.
|
|
1666
|
+
*/
|
|
1667
|
+
collectWorkspaceFileContents(rootPath, filePaths) {
|
|
1668
|
+
const MAX_TOTAL_BYTES = 2 * 1024 * 1024;
|
|
1669
|
+
const MAX_FILE_BYTES = 200 * 1024;
|
|
1670
|
+
const BINARY_EXTENSIONS = new Set([
|
|
1671
|
+
'.png', '.jpg', '.jpeg', '.gif', '.bmp', '.ico', '.svg', '.webp', '.avif',
|
|
1672
|
+
'.mp3', '.mp4', '.wav', '.ogg', '.webm', '.flac', '.aac',
|
|
1673
|
+
'.zip', '.gz', '.tar', '.rar', '.7z', '.bz2',
|
|
1674
|
+
'.exe', '.dll', '.so', '.dylib', '.bin', '.dat',
|
|
1675
|
+
'.woff', '.woff2', '.ttf', '.eot', '.otf',
|
|
1676
|
+
'.pdf', '.doc', '.docx', '.xls', '.xlsx', '.ppt', '.pptx',
|
|
1677
|
+
'.db', '.sqlite', '.sqlite3',
|
|
1678
|
+
'.pyc', '.pyo', '.class', '.o', '.obj',
|
|
1679
|
+
'.DS_Store', '.lock',
|
|
1680
|
+
]);
|
|
1681
|
+
const result = {};
|
|
1682
|
+
let totalBytes = 0;
|
|
1683
|
+
for (const relativePath of filePaths) {
|
|
1684
|
+
if (totalBytes >= MAX_TOTAL_BYTES)
|
|
1685
|
+
break;
|
|
1686
|
+
const ext = path_1.default.extname(relativePath).toLowerCase();
|
|
1687
|
+
if (BINARY_EXTENSIONS.has(ext))
|
|
1688
|
+
continue;
|
|
1689
|
+
if (/(^|[\/\\])\.(git|hg)([\/\\]|$)/.test(relativePath))
|
|
1690
|
+
continue;
|
|
1691
|
+
const absolutePath = path_1.default.join(rootPath, relativePath);
|
|
1692
|
+
try {
|
|
1693
|
+
const stat = fs_1.default.statSync(absolutePath);
|
|
1694
|
+
if (!stat.isFile() || stat.size > MAX_FILE_BYTES || stat.size === 0)
|
|
1695
|
+
continue;
|
|
1696
|
+
const content = fs_1.default.readFileSync(absolutePath, 'utf8');
|
|
1697
|
+
// Skip likely binary content (high ratio of non-printable chars)
|
|
1698
|
+
if (/[\x00-\x08\x0e-\x1f]/.test(content.slice(0, 512)))
|
|
1699
|
+
continue;
|
|
1700
|
+
result[relativePath] = content;
|
|
1701
|
+
totalBytes += Buffer.byteLength(content, 'utf8');
|
|
1702
|
+
}
|
|
1703
|
+
catch {
|
|
1704
|
+
// Skip unreadable files
|
|
1705
|
+
}
|
|
1706
|
+
}
|
|
1707
|
+
return result;
|
|
1708
|
+
}
|
|
1563
1709
|
hasAgentWorkspaceOutput(context = {}) {
|
|
1564
1710
|
try {
|
|
1565
1711
|
const root = this.resolveAgentTargetPath(context);
|
|
@@ -2231,6 +2377,30 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
2231
2377
|
serverWorkspaceRoot = event.workspace_root.trim();
|
|
2232
2378
|
}
|
|
2233
2379
|
this.captureV3AgentStreamMutation(event, streamedFiles, serverWorkspaceRoot);
|
|
2380
|
+
// Empty workspace guard: if the remote agent lists its root
|
|
2381
|
+
// and finds nothing while our local workspace has files, the
|
|
2382
|
+
// workspace was not hydrated. Abort early with a clear error
|
|
2383
|
+
// instead of letting the agent spin on an empty directory.
|
|
2384
|
+
if (event.type === 'tool_result'
|
|
2385
|
+
&& event.name === 'list_directory'
|
|
2386
|
+
&& event.success === true
|
|
2387
|
+
&& serverWorkspaceRoot
|
|
2388
|
+
&& typeof event.output === 'string') {
|
|
2389
|
+
const listOutput = event.output.trim();
|
|
2390
|
+
const looksEmpty = listOutput === `[${serverWorkspaceRoot}]`
|
|
2391
|
+
|| listOutput === `[${serverWorkspaceRoot}/]`
|
|
2392
|
+
|| listOutput === `[${serverWorkspaceRoot}]\\n`
|
|
2393
|
+
|| /^\[\/tmp\/vig-remote-server-[^\]]+\]\s*$/.test(listOutput);
|
|
2394
|
+
if (looksEmpty) {
|
|
2395
|
+
const localPath = this.resolveAgentTargetPath(context);
|
|
2396
|
+
const localHasFiles = localPath && fs_1.default.existsSync(localPath) && this.hasAgentWorkspaceOutput({ ...context, projectPath: localPath, targetPath: localPath });
|
|
2397
|
+
if (localHasFiles) {
|
|
2398
|
+
throw new Error('Remote workspace is empty — the V3 server did not receive your project files. '
|
|
2399
|
+
+ 'Your local workspace has files but the remote agent sees an empty directory. '
|
|
2400
|
+
+ 'This is a workspace sync failure. Falling back to local agent loop.');
|
|
2401
|
+
}
|
|
2402
|
+
}
|
|
2403
|
+
}
|
|
2234
2404
|
if (typeof context.onStreamEvent === 'function') {
|
|
2235
2405
|
try {
|
|
2236
2406
|
context.onStreamEvent(event);
|
|
@@ -2940,8 +3110,11 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
2940
3110
|
}
|
|
2941
3111
|
// Code operations - Using Vigthoria Centralized API
|
|
2942
3112
|
async generateCode(prompt, language, model) {
|
|
3113
|
+
// Prepend scope-enforcement instruction so the model doesn't expand
|
|
3114
|
+
// a small task into an oversized glossy production page.
|
|
3115
|
+
const scopedPrompt = `IMPORTANT: Follow the user's scope literally. If they ask for something small or minimal, produce exactly that — no extra styling, no external dependencies, no landing-page treatment.\n\n${prompt}`;
|
|
2943
3116
|
const response = await this.client.post('/api/ai/generate', {
|
|
2944
|
-
prompt,
|
|
3117
|
+
prompt: scopedPrompt,
|
|
2945
3118
|
language,
|
|
2946
3119
|
model: this.resolvePermittedModelId(model),
|
|
2947
3120
|
});
|
|
@@ -2974,7 +3147,12 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
2974
3147
|
code,
|
|
2975
3148
|
language,
|
|
2976
3149
|
});
|
|
2977
|
-
|
|
3150
|
+
const raw = response.data ?? {};
|
|
3151
|
+
return {
|
|
3152
|
+
score: typeof raw.score === 'number' ? raw.score : 0,
|
|
3153
|
+
issues: Array.isArray(raw.issues) ? raw.issues : [],
|
|
3154
|
+
suggestions: Array.isArray(raw.suggestions) ? raw.suggestions : [],
|
|
3155
|
+
};
|
|
2978
3156
|
}
|
|
2979
3157
|
async fixCode(code, language, fixType) {
|
|
2980
3158
|
const response = await this.client.post('/api/ai/fix', {
|
|
@@ -2982,7 +3160,11 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
2982
3160
|
language,
|
|
2983
3161
|
fixType,
|
|
2984
3162
|
});
|
|
2985
|
-
|
|
3163
|
+
const raw = response.data ?? {};
|
|
3164
|
+
return {
|
|
3165
|
+
fixed: typeof raw.fixed === 'string' ? raw.fixed : (typeof raw.code === 'string' ? raw.code : code),
|
|
3166
|
+
changes: Array.isArray(raw.changes) ? raw.changes : [],
|
|
3167
|
+
};
|
|
2986
3168
|
}
|
|
2987
3169
|
// Model resolution - maps Vigthoria model names to internal IDs
|
|
2988
3170
|
// INTERNAL USE ONLY - users see only Vigthoria branding
|
|
@@ -3096,32 +3278,87 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
3096
3278
|
}
|
|
3097
3279
|
}
|
|
3098
3280
|
async getV3AgentHealth() {
|
|
3099
|
-
const
|
|
3100
|
-
|
|
3101
|
-
|
|
3102
|
-
|
|
3103
|
-
|
|
3104
|
-
}
|
|
3105
|
-
|
|
3281
|
+
const baseUrl = this.getV3AgentBaseUrls()[0];
|
|
3282
|
+
// Try multiple health endpoint patterns — the V3 backend may expose
|
|
3283
|
+
// different paths depending on whether it's local (8030) or remote.
|
|
3284
|
+
const candidates = [
|
|
3285
|
+
`${baseUrl}/api/v3-agent/health`,
|
|
3286
|
+
`${baseUrl}/api/health`,
|
|
3287
|
+
`${baseUrl}/health`,
|
|
3288
|
+
];
|
|
3289
|
+
const headers = await this.getV3AgentHeaders();
|
|
3290
|
+
for (const endpoint of candidates) {
|
|
3291
|
+
try {
|
|
3292
|
+
const controller = new AbortController();
|
|
3293
|
+
const timer = setTimeout(() => controller.abort(), 8000);
|
|
3294
|
+
const response = await fetch(endpoint, {
|
|
3295
|
+
method: 'GET',
|
|
3296
|
+
headers,
|
|
3297
|
+
signal: controller.signal,
|
|
3298
|
+
});
|
|
3299
|
+
clearTimeout(timer);
|
|
3300
|
+
if (response.ok) {
|
|
3301
|
+
const data = await response.json().catch(() => ({}));
|
|
3302
|
+
return {
|
|
3303
|
+
name: 'V3 Agent',
|
|
3304
|
+
endpoint,
|
|
3305
|
+
ok: true,
|
|
3306
|
+
details: { health: data },
|
|
3307
|
+
};
|
|
3308
|
+
}
|
|
3309
|
+
// 404 means this path doesn't exist — try next candidate
|
|
3310
|
+
if (response.status === 404)
|
|
3311
|
+
continue;
|
|
3312
|
+
// 405 Method Not Allowed — endpoint exists but rejects GET.
|
|
3313
|
+
// Treat as reachable since the run endpoint (POST) will work.
|
|
3314
|
+
if (response.status === 405) {
|
|
3315
|
+
return {
|
|
3316
|
+
name: 'V3 Agent',
|
|
3317
|
+
endpoint,
|
|
3318
|
+
ok: true,
|
|
3319
|
+
details: { health: { reachable: true, note: 'Health endpoint returned 405 but run endpoint is available' } },
|
|
3320
|
+
};
|
|
3321
|
+
}
|
|
3322
|
+
// Other error
|
|
3106
3323
|
throw new Error(`V3 health ${response.status}`);
|
|
3107
3324
|
}
|
|
3108
|
-
|
|
3109
|
-
|
|
3110
|
-
|
|
3111
|
-
|
|
3112
|
-
|
|
3113
|
-
|
|
3114
|
-
|
|
3115
|
-
|
|
3325
|
+
catch (error) {
|
|
3326
|
+
if (error instanceof Error && error.message.startsWith('V3 health')) {
|
|
3327
|
+
return {
|
|
3328
|
+
name: 'V3 Agent',
|
|
3329
|
+
endpoint,
|
|
3330
|
+
ok: false,
|
|
3331
|
+
error: error.message,
|
|
3332
|
+
};
|
|
3333
|
+
}
|
|
3334
|
+
// Network/timeout error — try next candidate
|
|
3335
|
+
}
|
|
3116
3336
|
}
|
|
3117
|
-
|
|
3118
|
-
|
|
3119
|
-
|
|
3120
|
-
|
|
3121
|
-
|
|
3122
|
-
|
|
3123
|
-
|
|
3337
|
+
// Last resort: probe the run endpoint with OPTIONS
|
|
3338
|
+
const runUrl = this.getV3AgentRunUrl(baseUrl);
|
|
3339
|
+
try {
|
|
3340
|
+
const controller = new AbortController();
|
|
3341
|
+
const timer = setTimeout(() => controller.abort(), 5000);
|
|
3342
|
+
const probe = await fetch(runUrl, { method: 'OPTIONS', headers, signal: controller.signal });
|
|
3343
|
+
clearTimeout(timer);
|
|
3344
|
+
if (probe.ok || probe.status === 204 || probe.status === 405) {
|
|
3345
|
+
return {
|
|
3346
|
+
name: 'V3 Agent',
|
|
3347
|
+
endpoint: runUrl,
|
|
3348
|
+
ok: true,
|
|
3349
|
+
details: { health: { reachable: true, method: 'OPTIONS' } },
|
|
3350
|
+
};
|
|
3351
|
+
}
|
|
3352
|
+
}
|
|
3353
|
+
catch {
|
|
3354
|
+
// final attempt failed
|
|
3124
3355
|
}
|
|
3356
|
+
return {
|
|
3357
|
+
name: 'V3 Agent',
|
|
3358
|
+
endpoint: candidates[0],
|
|
3359
|
+
ok: false,
|
|
3360
|
+
error: 'No V3 agent health endpoint responded.',
|
|
3361
|
+
};
|
|
3125
3362
|
}
|
|
3126
3363
|
async getHyperLoopHealth() {
|
|
3127
3364
|
const endpoint = process.env.VIGTHORIA_HYPERLOOP_URL || 'http://127.0.0.1:8020/api/hyperloop/health';
|
package/dist/utils/logger.d.ts
CHANGED
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Logger utility for Vigthoria CLI
|
|
3
3
|
*/
|
|
4
|
+
import { type Options as OraOptions, type Ora } from 'ora';
|
|
5
|
+
export type { Ora };
|
|
6
|
+
/**
|
|
7
|
+
* Create an ora spinner that writes to stderr so it never
|
|
8
|
+
* pollutes stdout JSON output or triggers PowerShell error styling.
|
|
9
|
+
*/
|
|
10
|
+
export declare function createSpinner(textOrOpts: string | OraOptions): Ora;
|
|
4
11
|
export type LogLevel = 'debug' | 'info' | 'warn' | 'error' | 'success';
|
|
5
12
|
export declare class Logger {
|
|
6
13
|
private verbose;
|
package/dist/utils/logger.js
CHANGED
|
@@ -7,7 +7,17 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
7
7
|
};
|
|
8
8
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
9
|
exports.Logger = void 0;
|
|
10
|
+
exports.createSpinner = createSpinner;
|
|
10
11
|
const chalk_1 = __importDefault(require("chalk"));
|
|
12
|
+
const ora_1 = __importDefault(require("ora"));
|
|
13
|
+
/**
|
|
14
|
+
* Create an ora spinner that writes to stderr so it never
|
|
15
|
+
* pollutes stdout JSON output or triggers PowerShell error styling.
|
|
16
|
+
*/
|
|
17
|
+
function createSpinner(textOrOpts) {
|
|
18
|
+
const opts = typeof textOrOpts === 'string' ? { text: textOrOpts } : textOrOpts;
|
|
19
|
+
return (0, ora_1.default)({ ...opts, stream: process.stderr });
|
|
20
|
+
}
|
|
11
21
|
class Logger {
|
|
12
22
|
verbose = false;
|
|
13
23
|
setVerbose(verbose) {
|