vigthoria-cli 1.11.2 → 1.11.4

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.
@@ -15,5 +15,6 @@ export declare class BackgroundCommand {
15
15
  status(jobId: string, options?: BackgroundOptions): Promise<void>;
16
16
  apply(jobId: string, options?: BackgroundOptions): Promise<void>;
17
17
  cancel(jobId: string, options?: BackgroundOptions): Promise<void>;
18
+ private extractBackgroundOutcome;
18
19
  }
19
20
  export {};
@@ -82,8 +82,9 @@ export class BackgroundCommand {
82
82
  try {
83
83
  const job = await this.api.getV3BackgroundJob(jobId);
84
84
  spinner.stop();
85
+ const outcome = this.extractBackgroundOutcome(job);
85
86
  if (options.json) {
86
- console.log(JSON.stringify(job, null, 2));
87
+ console.log(JSON.stringify({ ...job, outcomeTruth: outcome }, null, 2));
87
88
  return;
88
89
  }
89
90
  console.log(chalk.bold(`\nBackground Job ${chalk.cyan(job.job_id)}\n`));
@@ -91,6 +92,12 @@ export class BackgroundCommand {
91
92
  console.log(`Files: ${job.file_count || 0}`);
92
93
  console.log(`Updated: ${job.updated_at || '-'}`);
93
94
  console.log(`Workspace: ${job.local_workspace_path || job.workspace_name || '-'}`);
95
+ if (outcome.tasksTotal > 0 || outcome.tasksCompleted > 0) {
96
+ console.log(`Tasks: ${outcome.tasksCompleted}/${outcome.tasksTotal}${outcome.success === true ? chalk.green(' (complete)') : outcome.success === false ? chalk.red(' (incomplete)') : ''}`);
97
+ }
98
+ if (outcome.headline) {
99
+ console.log(chalk.gray(`Outcome: ${outcome.headline}`));
100
+ }
94
101
  if (job.error)
95
102
  console.log(chalk.red(`Error: ${job.error}`));
96
103
  console.log(chalk.gray(`\n${String(job.request || '').slice(0, 500)}`));
@@ -157,4 +164,51 @@ export class BackgroundCommand {
157
164
  this.logger.error(error?.message || String(error));
158
165
  }
159
166
  }
167
+ extractBackgroundOutcome(job) {
168
+ const result = (job?.result && typeof job.result === 'object') ? job.result : {};
169
+ const events = Array.isArray(job?.events) ? job.events : [];
170
+ let tasksCompleted = Number(result.tasks_completed ?? result.tasksCompleted ?? 0);
171
+ let tasksTotal = Number(result.tasks_total ?? result.tasksTotal ?? 0);
172
+ let success = typeof result.success === 'boolean' ? result.success : null;
173
+ for (const event of events) {
174
+ if (!event || typeof event !== 'object')
175
+ continue;
176
+ if (event.type === 'result' || event.event === 'result') {
177
+ const payload = event.data || event.payload || event;
178
+ if (payload && typeof payload === 'object') {
179
+ if (payload.tasks_completed != null)
180
+ tasksCompleted = Number(payload.tasks_completed);
181
+ if (payload.tasksCompleted != null)
182
+ tasksCompleted = Number(payload.tasksCompleted);
183
+ if (payload.tasks_total != null)
184
+ tasksTotal = Number(payload.tasks_total);
185
+ if (payload.tasksTotal != null)
186
+ tasksTotal = Number(payload.tasksTotal);
187
+ if (typeof payload.success === 'boolean')
188
+ success = payload.success;
189
+ }
190
+ }
191
+ }
192
+ if (success == null && tasksTotal > 0) {
193
+ success = tasksCompleted >= tasksTotal;
194
+ }
195
+ if (success == null && job?.status === 'completed') {
196
+ success = true;
197
+ }
198
+ if (success == null && job?.status === 'failed') {
199
+ success = false;
200
+ }
201
+ let headline = '';
202
+ if (tasksTotal > 0) {
203
+ headline = success === true
204
+ ? `All ${tasksTotal} task(s) completed`
205
+ : success === false
206
+ ? `${tasksCompleted}/${tasksTotal} task(s) completed`
207
+ : `${tasksCompleted}/${tasksTotal} task(s) reported`;
208
+ }
209
+ else if (typeof result.message === 'string' && result.message.trim()) {
210
+ headline = result.message.trim();
211
+ }
212
+ return { tasksCompleted, tasksTotal, success, headline };
213
+ }
160
214
  }
@@ -17,6 +17,8 @@ interface ChatOptions {
17
17
  json?: boolean;
18
18
  bridge?: string;
19
19
  grant?: boolean;
20
+ retry?: boolean;
21
+ continue?: boolean;
20
22
  }
21
23
  export declare class ChatCommand {
22
24
  private config;
@@ -147,6 +149,8 @@ export declare class ChatCommand {
147
149
  private runOperatorDirectAnswer;
148
150
  private runSimplePrompt;
149
151
  private runAgentTurn;
152
+ private buildLocalLoopLiveOutcome;
153
+ private persistLocalLoopOutcome;
150
154
  private runLocalAgentLoop;
151
155
  private primeBypassedTargetFileContext;
152
156
  private tryDirectSingleFileFlow;
@@ -477,6 +477,10 @@ export class ChatCommand {
477
477
  */
478
478
  isOperatorDirectAnswerable(prompt) {
479
479
  const trimmed = prompt.trim();
480
+ // Infrastructure status pings are never repo-grounded analysis.
481
+ if (/^(status(\s+check)?|health(\s+check)?|ping)$/i.test(trimmed)) {
482
+ return true;
483
+ }
480
484
  // ── Exclusion guard: any prompt that references code artefacts,
481
485
  // file paths, or analysis verbs MUST go through a tool-backed path
482
486
  // (BMAD or agent loop), never the toolless direct-answer shortcut.
@@ -711,6 +715,9 @@ export class ChatCommand {
711
715
  }
712
716
  }
713
717
  writeV3StreamText(spinner, text) {
718
+ if (this.jsonOutput) {
719
+ return;
720
+ }
714
721
  const safeText = this.sanitizeV3VisibleStreamText(this.sanitizeServerPath(text));
715
722
  if (!safeText) {
716
723
  return;
@@ -1137,14 +1144,14 @@ export class ChatCommand {
1137
1144
  }
1138
1145
  console.log(chalk.green(`Using workspace: ${this.currentProjectPath}`));
1139
1146
  }
1140
- if (this.jsonOutput && !options.prompt) {
1141
- throw new Error('--json is only supported together with --prompt.');
1147
+ if (this.jsonOutput && !options.prompt && !options.retry && !options.continue) {
1148
+ throw new Error('--json is only supported together with --prompt, --retry, or --continue.');
1142
1149
  }
1143
1150
  this.ensureProjectWorkspace();
1144
1151
  this.directPromptMode = Boolean(options.prompt);
1145
1152
  this.directToolContinuationCount = 0;
1146
1153
  this.tools = new AgenticTools(this.logger, this.currentProjectPath, async (action) => this.requestPermission(action), this.autoApprove);
1147
- this.initializeSession(options.resume === true);
1154
+ this.initializeSession(options.resume === true || options.retry === true || options.continue === true);
1148
1155
  // ── Commando Bridge: connect if --bridge was specified ──────────
1149
1156
  if (options.bridge) {
1150
1157
  const bridgeClient = new BridgeClient({
@@ -1169,6 +1176,18 @@ export class ChatCommand {
1169
1176
  this.logger.error(this.operatorAccessMessage());
1170
1177
  return;
1171
1178
  }
1179
+ if (options.retry || options.continue) {
1180
+ if (!this.agentMode) {
1181
+ throw new Error('--retry and --continue require agent mode.');
1182
+ }
1183
+ const followUp = options.retry ? this.buildRetryPrompt() : this.buildContinuePrompt();
1184
+ if (!followUp) {
1185
+ throw new Error('No previous agent run to retry or continue. Run an agent prompt first with the same --project, or use --resume.');
1186
+ }
1187
+ this.directPromptMode = true;
1188
+ await this.runAgentTurn(followUp);
1189
+ return;
1190
+ }
1172
1191
  if (options.prompt) {
1173
1192
  const bridgePromptTimeoutMs = (() => {
1174
1193
  const rawValue = process.env.VIGTHORIA_BRIDGE_PROMPT_TIMEOUT_MS || process.env.V3_BRIDGE_PROMPT_TIMEOUT_MS;
@@ -1404,6 +1423,9 @@ export class ChatCommand {
1404
1423
  this.agentMode = this.currentSession.agentMode || this.agentMode;
1405
1424
  this.operatorMode = this.currentSession.operatorMode || this.operatorMode;
1406
1425
  this.currentModel = this.currentSession.model || this.currentModel;
1426
+ if (this.currentSession.lastAgentRunOutcome) {
1427
+ this.lastAgentRunOutcome = this.currentSession.lastAgentRunOutcome;
1428
+ }
1407
1429
  return;
1408
1430
  }
1409
1431
  }
@@ -1918,6 +1940,42 @@ export class ChatCommand {
1918
1940
  }
1919
1941
  await this.runLocalAgentLoop(prompt);
1920
1942
  }
1943
+ buildLocalLoopLiveOutcome(prompt) {
1944
+ const liveOutcome = createLiveOutcome();
1945
+ liveOutcome.requiresWorkspaceChanges = this.taskRequiresWorkspaceChanges(prompt);
1946
+ liveOutcome.analysisToolsUsed = this.agentToolEvidence.discovery;
1947
+ liveOutcome.changedFileCount = this.agentToolEvidence.mutation;
1948
+ liveOutcome.workspaceHasOutput = this.agentToolEvidence.mutation > 0;
1949
+ if (this.agentToolEvidence.searchFailed > 0 && this.agentToolEvidence.discovery === 0) {
1950
+ liveOutcome.executorFailed = true;
1951
+ liveOutcome.executorError = 'Search tools failed before gathering workspace evidence';
1952
+ }
1953
+ return liveOutcome;
1954
+ }
1955
+ persistLocalLoopOutcome(prompt, evaluation, liveOutcome) {
1956
+ this.lastAgentRunOutcome = {
1957
+ prompt,
1958
+ taskId: null,
1959
+ contextId: null,
1960
+ tasksSucceeded: liveOutcome.tasksSucceeded,
1961
+ tasksTotal: liveOutcome.tasksTotal,
1962
+ failedTaskIds: [...liveOutcome.failedTaskIds],
1963
+ unfinishedTaskIds: [],
1964
+ qualityScore: null,
1965
+ qualityMissing: [],
1966
+ qualityBlockers: evaluation.executorSucceeded ? [] : [evaluation.statusHeadline],
1967
+ hasOutput: liveOutcome.workspaceHasOutput,
1968
+ selfHealStatus: 'skipped',
1969
+ selfHealTool: null,
1970
+ plannerError: null,
1971
+ executorError: liveOutcome.executorError,
1972
+ clientToolErrors: [],
1973
+ transportErrors: [],
1974
+ workspacePath: this.currentProjectPath || null,
1975
+ workspaceSyncIssue: null,
1976
+ finishedAt: Date.now(),
1977
+ };
1978
+ }
1921
1979
  async runLocalAgentLoop(prompt) {
1922
1980
  if (!this.tools) {
1923
1981
  throw new Error('Agent tools are not initialized.');
@@ -1953,14 +2011,26 @@ export class ChatCommand {
1953
2011
  const evidenceSummary = this.synthesizeEvidenceFromHistory();
1954
2012
  if (evidenceSummary) {
1955
2013
  const fallbackContent = this.sanitizeDirectModeOutput(evidenceSummary);
2014
+ const liveOutcome = this.buildLocalLoopLiveOutcome(prompt);
2015
+ const evaluation = evaluateExecutorSuccess(liveOutcome);
2016
+ this.persistLocalLoopOutcome(prompt, evaluation, liveOutcome);
1956
2017
  if (this.jsonOutput) {
2018
+ process.exitCode = evaluation.executorSucceeded ? 0 : 1;
1957
2019
  console.log(JSON.stringify({
1958
- success: true,
2020
+ success: evaluation.executorSucceeded,
1959
2021
  mode: 'agent',
1960
2022
  model: this.currentModel,
1961
- partial: true,
2023
+ partial: !evaluation.executorSucceeded,
1962
2024
  content: fallbackContent,
1963
- metadata: { executionPath: 'local-agent-loop', recovered: true },
2025
+ statusHeadline: evaluation.statusHeadline,
2026
+ metadata: {
2027
+ executionPath: 'local-agent-loop',
2028
+ recovered: true,
2029
+ outcomeTruth: {
2030
+ executorSucceeded: evaluation.executorSucceeded,
2031
+ uiTheme: evaluation.uiTheme,
2032
+ },
2033
+ },
1964
2034
  }, null, 2));
1965
2035
  }
1966
2036
  else {
@@ -2056,15 +2126,26 @@ export class ChatCommand {
2056
2126
  continue;
2057
2127
  }
2058
2128
  const finalContent = this.resolveDirectModeCompletion(prompt, visibleText);
2129
+ const liveOutcome = this.buildLocalLoopLiveOutcome(prompt);
2130
+ const evaluation = evaluateExecutorSuccess(liveOutcome);
2131
+ this.persistLocalLoopOutcome(prompt, evaluation, liveOutcome);
2059
2132
  if (this.jsonOutput) {
2133
+ process.exitCode = evaluation.executorSucceeded ? 0 : 1;
2060
2134
  console.log(JSON.stringify({
2061
- success: true,
2135
+ success: evaluation.executorSucceeded,
2062
2136
  mode: 'agent',
2063
2137
  model: this.currentModel,
2064
- partial: false,
2138
+ partial: !evaluation.executorSucceeded,
2065
2139
  content: finalContent,
2140
+ statusHeadline: evaluation.statusHeadline,
2066
2141
  metadata: {
2067
2142
  executionPath: 'local-agent-loop',
2143
+ outcomeTruth: {
2144
+ executorSucceeded: evaluation.executorSucceeded,
2145
+ uiTheme: evaluation.uiTheme,
2146
+ analysisToolsUsed: liveOutcome.analysisToolsUsed,
2147
+ changedFileCount: liveOutcome.changedFileCount,
2148
+ },
2068
2149
  },
2069
2150
  }, null, 2));
2070
2151
  }
@@ -2109,6 +2190,13 @@ export class ChatCommand {
2109
2190
  return;
2110
2191
  }
2111
2192
  }
2193
+ const liveOutcome = this.buildLocalLoopLiveOutcome(prompt);
2194
+ const exhaustedEval = evaluateExecutorSuccess({
2195
+ ...liveOutcome,
2196
+ executorFailed: true,
2197
+ executorError: 'Agent exhausted the maximum local tool loop turns before reaching a clean completion.',
2198
+ });
2199
+ this.persistLocalLoopOutcome(prompt, exhaustedEval, liveOutcome);
2112
2200
  if (this.jsonOutput) {
2113
2201
  process.exitCode = 1;
2114
2202
  console.log(JSON.stringify({
@@ -2118,8 +2206,13 @@ export class ChatCommand {
2118
2206
  partial: true,
2119
2207
  content: 'Task complete.',
2120
2208
  error: 'Agent exhausted the maximum local tool loop turns before reaching a clean completion.',
2209
+ statusHeadline: exhaustedEval.statusHeadline,
2121
2210
  metadata: {
2122
2211
  executionPath: 'local-agent-loop',
2212
+ outcomeTruth: {
2213
+ executorSucceeded: false,
2214
+ uiTheme: exhaustedEval.uiTheme,
2215
+ },
2123
2216
  },
2124
2217
  }, null, 2));
2125
2218
  }
@@ -4150,6 +4243,7 @@ export class ChatCommand {
4150
4243
  this.currentSession.agentMode = this.agentMode;
4151
4244
  this.currentSession.operatorMode = this.operatorMode;
4152
4245
  this.currentSession.messages = [...this.messages];
4246
+ this.currentSession.lastAgentRunOutcome = this.lastAgentRunOutcome;
4153
4247
  this.currentSession = this.sessionManager.compactInMemory(this.currentSession);
4154
4248
  this.messages = [...this.currentSession.messages];
4155
4249
  this.sessionManager.save(this.currentSession);
package/dist/index.js CHANGED
@@ -614,6 +614,8 @@ export async function main(args) {
614
614
  .option('--prompt <text>', 'Run a single agent prompt directly and exit')
615
615
  .option('-w, --workflow <selector>', 'Run the prompt through a named or explicit VigFlow workflow target')
616
616
  .option('--json', 'Emit machine-readable JSON output for direct prompt runs', false)
617
+ .option('--retry', 'Retry failed tasks from the last agent run in this workspace session', false)
618
+ .option('--continue', 'Continue unfinished tasks from the last agent run in this workspace session', false)
617
619
  .option('--auto-approve', 'Auto-approve all actions (dangerous!)', false)
618
620
  .option('--bridge <url>', 'Connect to Vigthoria Commando Bridge for remote admin observability')
619
621
  .action(async (options) => {
@@ -630,6 +632,8 @@ export async function main(args) {
630
632
  autoApprove: options.autoApprove,
631
633
  prompt: options.prompt,
632
634
  bridge: options.bridge,
635
+ retry: options.retry,
636
+ continue: options.continue,
633
637
  });
634
638
  });
635
639
  program
package/dist/utils/api.js CHANGED
@@ -1648,6 +1648,8 @@ export class APIClient {
1648
1648
  const serverWorkspacePath = this.resolveServerBindableWorkspacePath(resolvedContext);
1649
1649
  const localWorkspaceName = this.getDisplayWorkspaceName(targetPath);
1650
1650
  const localWorkspaceRef = localWorkspaceName ? `vigthoria://local-workspace/${localWorkspaceName}` : null;
1651
+ const rawBrain = resolvedContext.vigthoriaBrain || resolvedContext.vigthoria_brain || null;
1652
+ const dedupedBrain = rawBrain ? this.dedupeBrainPayload(rawBrain) : null;
1651
1653
  return JSON.stringify({
1652
1654
  workspace: this.buildPublicWorkspaceDescriptor(resolvedContext.workspace, {
1653
1655
  localWorkspacePath: targetPath,
@@ -1681,8 +1683,8 @@ export class APIClient {
1681
1683
  contextId: resolvedContext.contextId,
1682
1684
  traceId: resolvedContext.traceId,
1683
1685
  requestStartedAt: resolvedContext.requestStartedAt,
1684
- vigthoriaBrain: resolvedContext.vigthoriaBrain || null,
1685
- vigthoria_brain: resolvedContext.vigthoriaBrain || null,
1686
+ vigthoriaBrain: dedupedBrain,
1687
+ vigthoria_brain: dedupedBrain,
1686
1688
  });
1687
1689
  }
1688
1690
  extractEmergencyAppName(message = '', fallback = 'Signal Desk') {
@@ -46,6 +46,7 @@ export interface Session {
46
46
  updatedAt: string;
47
47
  agentMode: boolean;
48
48
  operatorMode?: boolean;
49
+ lastAgentRunOutcome?: Record<string, unknown> | null;
49
50
  }
50
51
  export declare class SessionManager {
51
52
  private sessionsDir;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vigthoria-cli",
3
- "version": "1.11.2",
3
+ "version": "1.11.4",
4
4
  "description": "Vigthoria Coder CLI - AI-powered terminal coding assistant",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -76,7 +76,7 @@ $CLI hyper-loop status >/tmp/vig-hyperloop.txt
76
76
  $CLI devtools connect >/tmp/vig-devtools.txt
77
77
 
78
78
  echo "[6.11] operator flow"
79
- $CLI operator --prompt "status check" --json >/tmp/vig-operator.json
79
+ $CLI operator --prompt "reply with exactly: OK" --json >/tmp/vig-operator.json
80
80
  python3 - << 'PY'
81
81
  import json
82
82
  j=json.load(open('/tmp/vig-operator.json'))