vigthoria-cli 1.6.18 → 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.
@@ -118,7 +118,13 @@ class ChatCommand {
118
118
  return this.getDefaultChatModel();
119
119
  }
120
120
  isLegacyAgentFallbackAllowed() {
121
- return process.env.VIGTHORIA_ALLOW_LEGACY_AGENT_FALLBACK === '1';
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;
@@ -657,11 +663,32 @@ class ChatCommand {
657
663
  if (spinner) {
658
664
  spinner.fail('Operator workflow failed');
659
665
  }
660
- this.logger.error(error.message);
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
693
  const spinner = this.jsonOutput ? null : (0, logger_js_1.createSpinner)({ text: 'Thinking...', spinner: 'clock' }).start();
667
694
  try {
@@ -6,6 +6,7 @@ import { Logger } from '../utils/logger.js';
6
6
  interface EditOptions {
7
7
  instruction?: string;
8
8
  model: string;
9
+ apply?: boolean;
9
10
  }
10
11
  interface FixOptions {
11
12
  type: string;
@@ -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
- await this.showDiffAndConfirm(file.path, file.content, modifiedCode);
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();
@@ -48,10 +48,23 @@ class WorkflowCommand {
48
48
  }
49
49
  async templates(options) {
50
50
  this.ensureAuthenticated(Boolean(options.json));
51
- const templates = await this.api.listVigFlowTemplates({
52
- category: options.category,
53
- search: options.search,
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
- const workflows = await this.api.listVigFlowWorkflows();
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);
@@ -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;
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
  }
@@ -888,6 +890,8 @@ class APIClient {
888
890
  return payload.execution;
889
891
  });
890
892
  }
893
+ /** Maximum serialized context length accepted by the V3 server. */
894
+ static V3_CONTEXT_CHAR_LIMIT = 95_000;
891
895
  buildV3AgentContext(context = {}) {
892
896
  const resolvedContext = this.ensureExecutionContext(context);
893
897
  const targetPath = resolvedContext.targetPath || resolvedContext.projectPath || resolvedContext.workspacePath || resolvedContext.projectRoot || process.cwd();
@@ -896,7 +900,7 @@ class APIClient {
896
900
  const localWorkspaceSummary = this.buildLocalWorkspaceSummary(localWorkspacePath);
897
901
  const requestedModel = String(resolvedContext.model || resolvedContext.requestedModel || 'agent');
898
902
  const resolvedModel = this.resolvePermittedModelId(requestedModel);
899
- return JSON.stringify({
903
+ const payload = {
900
904
  workspace: resolvedContext.workspace || null,
901
905
  activeFile: resolvedContext.activeFile || null,
902
906
  history: resolvedContext.history || [],
@@ -922,7 +926,89 @@ class APIClient {
922
926
  requestStartedAt: resolvedContext.requestStartedAt,
923
927
  subscriptionPlan: this.config.getNormalizedPlan() || null,
924
928
  email: this.config.get('email') || null,
925
- });
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;
926
1012
  }
927
1013
  buildMinimalV3AgentContext(context = {}) {
928
1014
  const resolvedContext = this.ensureExecutionContext(context);
@@ -3024,8 +3110,11 @@ document.addEventListener('DOMContentLoaded', () => {
3024
3110
  }
3025
3111
  // Code operations - Using Vigthoria Centralized API
3026
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}`;
3027
3116
  const response = await this.client.post('/api/ai/generate', {
3028
- prompt,
3117
+ prompt: scopedPrompt,
3029
3118
  language,
3030
3119
  model: this.resolvePermittedModelId(model),
3031
3120
  });
@@ -3058,7 +3147,12 @@ document.addEventListener('DOMContentLoaded', () => {
3058
3147
  code,
3059
3148
  language,
3060
3149
  });
3061
- return response.data;
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
+ };
3062
3156
  }
3063
3157
  async fixCode(code, language, fixType) {
3064
3158
  const response = await this.client.post('/api/ai/fix', {
@@ -3066,7 +3160,11 @@ document.addEventListener('DOMContentLoaded', () => {
3066
3160
  language,
3067
3161
  fixType,
3068
3162
  });
3069
- return response.data;
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
+ };
3070
3168
  }
3071
3169
  // Model resolution - maps Vigthoria model names to internal IDs
3072
3170
  // INTERNAL USE ONLY - users see only Vigthoria branding
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vigthoria-cli",
3
- "version": "1.6.18",
3
+ "version": "1.6.19",
4
4
  "description": "Vigthoria Coder CLI - AI-powered terminal coding assistant",
5
5
  "main": "dist/index.js",
6
6
  "files": [