vigthoria-cli 1.6.32 → 1.6.34
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 +6 -0
- package/dist/commands/chat.js +15 -0
- package/dist/commands/edit.d.ts +4 -0
- package/dist/commands/edit.js +55 -5
- package/dist/utils/api.d.ts +5 -0
- package/dist/utils/api.js +97 -14
- package/package.json +1 -1
package/dist/commands/chat.d.ts
CHANGED
|
@@ -53,6 +53,12 @@ export declare class ChatCommand {
|
|
|
53
53
|
*/
|
|
54
54
|
private isAnalysisLookupPrompt;
|
|
55
55
|
private isBrowserTaskPrompt;
|
|
56
|
+
/**
|
|
57
|
+
* Returns true when a prompt can be answered directly without the full
|
|
58
|
+
* BMAD operator workflow. Matches analysis/lookup questions.
|
|
59
|
+
* Infrastructure action verbs are already filtered at the routing level.
|
|
60
|
+
*/
|
|
61
|
+
private isOperatorDirectAnswerable;
|
|
56
62
|
private inferAgentTaskType;
|
|
57
63
|
private buildTaskShapingInstructions;
|
|
58
64
|
private buildExecutionPrompt;
|
package/dist/commands/chat.js
CHANGED
|
@@ -206,6 +206,14 @@ class ChatCommand {
|
|
|
206
206
|
isBrowserTaskPrompt(prompt) {
|
|
207
207
|
return /(browser|chrome|devtools|console|dom|network tab|network request|frontend runtime|client-side|client side|rendering|page load|websocket|ui bug|inspect element)/i.test(prompt);
|
|
208
208
|
}
|
|
209
|
+
/**
|
|
210
|
+
* Returns true when a prompt can be answered directly without the full
|
|
211
|
+
* BMAD operator workflow. Matches analysis/lookup questions.
|
|
212
|
+
* Infrastructure action verbs are already filtered at the routing level.
|
|
213
|
+
*/
|
|
214
|
+
isOperatorDirectAnswerable(prompt) {
|
|
215
|
+
return this.isAnalysisLookupPrompt(prompt.trim());
|
|
216
|
+
}
|
|
209
217
|
inferAgentTaskType(prompt) {
|
|
210
218
|
if (this.isDiagnosticPrompt(prompt))
|
|
211
219
|
return 'debugging';
|
|
@@ -665,6 +673,13 @@ class ChatCommand {
|
|
|
665
673
|
this.logger.error(this.operatorAccessMessage());
|
|
666
674
|
return;
|
|
667
675
|
}
|
|
676
|
+
// Smart routing: infrastructure action verbs always need the BMAD
|
|
677
|
+
// workflow. Everything else can be answered with a direct chat.
|
|
678
|
+
const isInfraAction = /(deploy|provision|scale|replicas|rollback|roll back|migrate|tear.?down|restart|stop\s+\w+|start\s+\w+|configure|set.?up|upgrade|pipeline)/i.test(prompt.trim());
|
|
679
|
+
if (!isInfraAction && (this.isSimpleDirectPrompt(prompt) || this.isOperatorDirectAnswerable(prompt))) {
|
|
680
|
+
await this.runSimplePrompt(prompt);
|
|
681
|
+
return;
|
|
682
|
+
}
|
|
668
683
|
await this.runOperatorTurn(prompt);
|
|
669
684
|
return;
|
|
670
685
|
}
|
package/dist/commands/edit.d.ts
CHANGED
|
@@ -27,6 +27,10 @@ export declare class EditCommand {
|
|
|
27
27
|
* Applies iteratively until no further duplicates are found.
|
|
28
28
|
*/
|
|
29
29
|
private deduplicateCode;
|
|
30
|
+
/**
|
|
31
|
+
* Append missing closing delimiters if dedup left brackets unbalanced.
|
|
32
|
+
*/
|
|
33
|
+
private repairBrackets;
|
|
30
34
|
private deduplicateOnce;
|
|
31
35
|
private showDiffAndConfirm;
|
|
32
36
|
private showFullDiff;
|
package/dist/commands/edit.js
CHANGED
|
@@ -231,8 +231,55 @@ Return the complete modified file content:`,
|
|
|
231
231
|
break;
|
|
232
232
|
result = deduped;
|
|
233
233
|
}
|
|
234
|
+
// Safety net: if dedup accidentally removed structural delimiters,
|
|
235
|
+
// re-balance braces/parens/brackets by appending missing closers.
|
|
236
|
+
if (result !== code) {
|
|
237
|
+
result = this.repairBrackets(result);
|
|
238
|
+
}
|
|
234
239
|
return result;
|
|
235
240
|
}
|
|
241
|
+
/**
|
|
242
|
+
* Append missing closing delimiters if dedup left brackets unbalanced.
|
|
243
|
+
*/
|
|
244
|
+
repairBrackets(code) {
|
|
245
|
+
const stack = [];
|
|
246
|
+
const closer = { '{': '}', '(': ')', '[': ']' };
|
|
247
|
+
const openers = new Set(Object.keys(closer));
|
|
248
|
+
const closers = new Set(Object.values(closer));
|
|
249
|
+
let inString = null;
|
|
250
|
+
let escaped = false;
|
|
251
|
+
for (const ch of code) {
|
|
252
|
+
if (escaped) {
|
|
253
|
+
escaped = false;
|
|
254
|
+
continue;
|
|
255
|
+
}
|
|
256
|
+
if (ch === '\\') {
|
|
257
|
+
escaped = true;
|
|
258
|
+
continue;
|
|
259
|
+
}
|
|
260
|
+
if (inString) {
|
|
261
|
+
if (ch === inString)
|
|
262
|
+
inString = null;
|
|
263
|
+
continue;
|
|
264
|
+
}
|
|
265
|
+
if (ch === '"' || ch === "'" || ch === '`') {
|
|
266
|
+
inString = ch;
|
|
267
|
+
continue;
|
|
268
|
+
}
|
|
269
|
+
if (openers.has(ch))
|
|
270
|
+
stack.push(ch);
|
|
271
|
+
else if (closers.has(ch)) {
|
|
272
|
+
if (stack.length > 0 && closer[stack[stack.length - 1]] === ch) {
|
|
273
|
+
stack.pop();
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
if (stack.length === 0)
|
|
278
|
+
return code;
|
|
279
|
+
// Append missing closers in reverse order (innermost first)
|
|
280
|
+
const suffix = stack.reverse().map(o => closer[o]).join('\n');
|
|
281
|
+
return code + '\n' + suffix;
|
|
282
|
+
}
|
|
236
283
|
deduplicateOnce(code) {
|
|
237
284
|
const lines = code.split('\n');
|
|
238
285
|
const len = lines.length;
|
|
@@ -252,7 +299,10 @@ Return the complete modified file content:`,
|
|
|
252
299
|
i++;
|
|
253
300
|
continue;
|
|
254
301
|
}
|
|
255
|
-
// Count the run length of identical consecutive lines
|
|
302
|
+
// Count the run length of identical consecutive lines.
|
|
303
|
+
// For runs of 3+, trimmed comparison is fine (clear stutter).
|
|
304
|
+
// For trailing pairs, require EXACT match (including indentation)
|
|
305
|
+
// so that e.g. " }" and "}" (different blocks) are not collapsed.
|
|
256
306
|
let runEnd = i + 1;
|
|
257
307
|
while (runEnd < lines.length && lines[runEnd].trim() === trimmed) {
|
|
258
308
|
runEnd++;
|
|
@@ -263,12 +313,12 @@ Return the complete modified file content:`,
|
|
|
263
313
|
const isAtEffectiveEnd = runEnd === lines.length
|
|
264
314
|
|| lines.slice(runEnd).every(l => l.trim() === '');
|
|
265
315
|
if (runLen >= 3) {
|
|
266
|
-
// 3+ identical lines is almost certainly stutter — keep one
|
|
316
|
+
// 3+ identical (trimmed) lines is almost certainly stutter — keep one
|
|
267
317
|
deduped.push(lines[i]);
|
|
268
318
|
}
|
|
269
|
-
else if (runLen === 2 && isAtEffectiveEnd) {
|
|
270
|
-
// Exactly 2 identical lines
|
|
271
|
-
deduped.push(lines[i]);
|
|
319
|
+
else if (runLen === 2 && isAtEffectiveEnd && lines[i].trimEnd() === lines[i + 1].trimEnd()) {
|
|
320
|
+
// Exactly 2 identical lines (same leading indent) at end
|
|
321
|
+
deduped.push(lines[i].trimEnd());
|
|
272
322
|
}
|
|
273
323
|
else {
|
|
274
324
|
// 1 line, or a pair in the middle — keep all
|
package/dist/utils/api.d.ts
CHANGED
|
@@ -351,6 +351,11 @@ export declare class APIClient {
|
|
|
351
351
|
* Ensure code has balanced curly braces by appending missing closing braces.
|
|
352
352
|
*/
|
|
353
353
|
private ensureBalancedBraces;
|
|
354
|
+
/**
|
|
355
|
+
* Quick JS/TS syntax validation using Node's built-in parser.
|
|
356
|
+
* Returns true if the code parses without errors.
|
|
357
|
+
*/
|
|
358
|
+
private validateJsSyntax;
|
|
354
359
|
/**
|
|
355
360
|
* Extract the first complete function/class from code.
|
|
356
361
|
* Used as last-resort when the model keeps over-producing.
|
package/dist/utils/api.js
CHANGED
|
@@ -3454,21 +3454,76 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
3454
3454
|
* Ensure code has balanced curly braces by appending missing closing braces.
|
|
3455
3455
|
*/
|
|
3456
3456
|
ensureBalancedBraces(code) {
|
|
3457
|
-
|
|
3458
|
-
|
|
3457
|
+
// Count braces/parens/brackets outside strings and comments
|
|
3458
|
+
let braces = 0, parens = 0, brackets = 0;
|
|
3459
|
+
let inStr = null;
|
|
3460
|
+
let inLine = false, inBlock = false;
|
|
3461
|
+
for (let i = 0; i < code.length; i++) {
|
|
3462
|
+
const ch = code[i], nx = code[i + 1] || '';
|
|
3463
|
+
if (inLine) {
|
|
3464
|
+
if (ch === '\n')
|
|
3465
|
+
inLine = false;
|
|
3466
|
+
continue;
|
|
3467
|
+
}
|
|
3468
|
+
if (inBlock) {
|
|
3469
|
+
if (ch === '*' && nx === '/') {
|
|
3470
|
+
inBlock = false;
|
|
3471
|
+
i++;
|
|
3472
|
+
}
|
|
3473
|
+
continue;
|
|
3474
|
+
}
|
|
3475
|
+
if (inStr) {
|
|
3476
|
+
if (ch === inStr && code[i - 1] !== '\\')
|
|
3477
|
+
inStr = null;
|
|
3478
|
+
continue;
|
|
3479
|
+
}
|
|
3480
|
+
if (ch === '/' && nx === '/') {
|
|
3481
|
+
inLine = true;
|
|
3482
|
+
continue;
|
|
3483
|
+
}
|
|
3484
|
+
if (ch === '/' && nx === '*') {
|
|
3485
|
+
inBlock = true;
|
|
3486
|
+
continue;
|
|
3487
|
+
}
|
|
3488
|
+
if (ch === '"' || ch === "'" || ch === '`') {
|
|
3489
|
+
inStr = ch;
|
|
3490
|
+
continue;
|
|
3491
|
+
}
|
|
3459
3492
|
if (ch === '{')
|
|
3460
|
-
|
|
3493
|
+
braces++;
|
|
3461
3494
|
else if (ch === '}')
|
|
3462
|
-
|
|
3495
|
+
braces--;
|
|
3496
|
+
else if (ch === '(')
|
|
3497
|
+
parens++;
|
|
3498
|
+
else if (ch === ')')
|
|
3499
|
+
parens--;
|
|
3500
|
+
else if (ch === '[')
|
|
3501
|
+
brackets++;
|
|
3502
|
+
else if (ch === ']')
|
|
3503
|
+
brackets--;
|
|
3504
|
+
}
|
|
3505
|
+
let result = code.trimEnd();
|
|
3506
|
+
for (let i = 0; i < braces; i++)
|
|
3507
|
+
result += '\n}';
|
|
3508
|
+
for (let i = 0; i < parens; i++)
|
|
3509
|
+
result += ')';
|
|
3510
|
+
for (let i = 0; i < brackets; i++)
|
|
3511
|
+
result += ']';
|
|
3512
|
+
return braces > 0 || parens > 0 || brackets > 0 ? result : code;
|
|
3513
|
+
}
|
|
3514
|
+
/**
|
|
3515
|
+
* Quick JS/TS syntax validation using Node's built-in parser.
|
|
3516
|
+
* Returns true if the code parses without errors.
|
|
3517
|
+
*/
|
|
3518
|
+
validateJsSyntax(code) {
|
|
3519
|
+
try {
|
|
3520
|
+
// Use Function constructor to check syntax without executing
|
|
3521
|
+
new Function(code);
|
|
3522
|
+
return true;
|
|
3463
3523
|
}
|
|
3464
|
-
|
|
3465
|
-
|
|
3466
|
-
for (let i = 0; i < depth; i++) {
|
|
3467
|
-
result += '\n}';
|
|
3468
|
-
}
|
|
3469
|
-
code = result;
|
|
3524
|
+
catch {
|
|
3525
|
+
return false;
|
|
3470
3526
|
}
|
|
3471
|
-
return code;
|
|
3472
3527
|
}
|
|
3473
3528
|
/**
|
|
3474
3529
|
* Extract the first complete function/class from code.
|
|
@@ -3589,9 +3644,10 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
3589
3644
|
'- Return concrete, line-specific issues with severity.',
|
|
3590
3645
|
'- Every issue MUST reference a line number.',
|
|
3591
3646
|
'- Report each distinct bug ONCE. Do NOT report the same bug multiple times with different wording.',
|
|
3592
|
-
'- For trivial/short code (< 10 lines), report
|
|
3647
|
+
'- For trivial/short code (< 10 lines), report ONLY actual bugs. Do NOT pad with style, robustness, or best-practice suggestions.',
|
|
3648
|
+
'- If you find a real bug (wrong operator, logic error, type mismatch), report ONLY that bug. Do NOT also suggest input validation, type checking, or error handling unless those are ACTUAL bugs.',
|
|
3593
3649
|
'- Prioritize REAL BUGS: wrong operators, logic errors, off-by-one, type mismatches.',
|
|
3594
|
-
'-
|
|
3650
|
+
'- Do NOT suggest adding error handling, input validation, or documentation as issues unless the user explicitly asked for a style review.',
|
|
3595
3651
|
'- Return ONLY the JSON object, no markdown fences or extra text.',
|
|
3596
3652
|
].join('\n');
|
|
3597
3653
|
let raw = {};
|
|
@@ -3608,8 +3664,13 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
3608
3664
|
const suggestions = Array.isArray(raw.suggestions) ? raw.suggestions : [];
|
|
3609
3665
|
// Merge client-side heuristics, but with tight dedup to avoid
|
|
3610
3666
|
// redundant over-reporting when the model already found the bug.
|
|
3667
|
+
const modelFoundError = issues.some(i => i.severity === 'error');
|
|
3611
3668
|
const heuristic = this.heuristicCodeIssues(code, language);
|
|
3612
3669
|
for (const h of heuristic) {
|
|
3670
|
+
// If the model already found a real error, skip non-error heuristics
|
|
3671
|
+
// entirely — they're just padding (style, robustness, etc.)
|
|
3672
|
+
if (modelFoundError && h.severity !== 'error')
|
|
3673
|
+
continue;
|
|
3613
3674
|
// Semantic duplicate check: same line + (similar type OR overlapping
|
|
3614
3675
|
// keywords in the message). This catches cases where the model
|
|
3615
3676
|
// and heuristic describe the same bug with different wording.
|
|
@@ -3783,9 +3844,11 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
3783
3844
|
const sysPrompt = [
|
|
3784
3845
|
`You are a ${language} code fixer. Fix the code for: ${fixType}.`,
|
|
3785
3846
|
'Return a JSON object with:',
|
|
3786
|
-
' "fixed": the corrected code as a string,',
|
|
3847
|
+
' "fixed": the COMPLETE corrected source code as a string (not a snippet — the full file),',
|
|
3787
3848
|
' "changes": [{ "line": number, "before": string, "after": string, "reason": string }]',
|
|
3788
3849
|
'Rules:',
|
|
3850
|
+
'- The "fixed" field MUST contain the entire corrected source code with ALL lines, including unchanged lines.',
|
|
3851
|
+
'- The "fixed" code MUST have balanced braces, parentheses, and brackets.',
|
|
3789
3852
|
'- Fix ONLY the issues related to the fix type.',
|
|
3790
3853
|
'- Do not add comments, do not restructure beyond the minimal fix.',
|
|
3791
3854
|
'- Return ONLY the JSON object, no markdown fences.',
|
|
@@ -3825,6 +3888,26 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
3825
3888
|
if (fixType === 'syntax' && fixed !== code) {
|
|
3826
3889
|
fixed = this.repairBracketBalance(code, fixed);
|
|
3827
3890
|
}
|
|
3891
|
+
// Final bracket-balance guarantee — ensure the emitted code has
|
|
3892
|
+
// balanced braces/parens/brackets regardless of what the model returned.
|
|
3893
|
+
fixed = this.ensureBalancedBraces(fixed);
|
|
3894
|
+
// For JS/TS syntax fixes, validate the output actually parses.
|
|
3895
|
+
// If it doesn't, attempt a more aggressive bracket repair.
|
|
3896
|
+
if ((fixType === 'syntax' || fixType === 'bugs') && fixed !== code) {
|
|
3897
|
+
const lang = language.toLowerCase();
|
|
3898
|
+
if (['javascript', 'js', 'typescript', 'ts'].includes(lang)) {
|
|
3899
|
+
if (!this.validateJsSyntax(fixed)) {
|
|
3900
|
+
// Try once more: strip any remaining injected comments and re-balance
|
|
3901
|
+
let repaired = this.stripInjectedComments(code, fixed, language);
|
|
3902
|
+
repaired = this.ensureBalancedBraces(repaired);
|
|
3903
|
+
if (this.validateJsSyntax(repaired)) {
|
|
3904
|
+
fixed = repaired;
|
|
3905
|
+
}
|
|
3906
|
+
// If still invalid, return the best-effort fix — better than
|
|
3907
|
+
// silently reverting to the original broken code.
|
|
3908
|
+
}
|
|
3909
|
+
}
|
|
3910
|
+
}
|
|
3828
3911
|
// If there are still no changes but the fixed code differs, compute
|
|
3829
3912
|
// a semantic diff using LCS so inserted/removed lines don't cause
|
|
3830
3913
|
// every subsequent line to appear as changed.
|