orquesta-cli 0.2.29 → 0.2.31

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.
@@ -765,6 +765,16 @@ export class LLMClient {
765
765
  };
766
766
  }
767
767
  handleError(error, requestContext) {
768
+ if (axios.isAxiosError(error) && error.response?.status === 401) {
769
+ const data = error.response.data;
770
+ const detail = data?.error?.message || data?.message || 'Unauthorized';
771
+ logger.error(`Not logged in to Orquesta (${detail}).`);
772
+ return new APIError('Not logged in to Orquesta', 401, this.baseUrl, {
773
+ isRecoverable: false,
774
+ userMessage: `You're not logged in to Orquesta (${detail}).\n` +
775
+ `Run \`orquesta --login\` to sign in again, then retry.`,
776
+ });
777
+ }
768
778
  logger.error('LLM Client Error', error);
769
779
  if (requestContext) {
770
780
  logger.debug('Request Context', requestContext);
@@ -2,6 +2,9 @@ import { sessionManager } from './session/session-manager.js';
2
2
  import { usageTracker } from './usage-tracker.js';
3
3
  import { logger } from '../utils/logger.js';
4
4
  import { fullSync } from '../orquesta/config-sync.js';
5
+ import { readHookConfig, writeHookFiles, disableHooks } from '../orquesta/hook-init.js';
6
+ import { checkForCliUpdate, runCliUpdate, setSkippedVersion } from '../utils/update-checker.js';
7
+ import { createRequire } from 'module';
5
8
  import { configManager } from './config/config-manager.js';
6
9
  import { getForcedTier, setForcedTier, resetBatutaSession } from './routing-state.js';
7
10
  import { auditLog } from '../orchestration/audit-log.js';
@@ -511,6 +514,72 @@ ${executorLines}
511
514
  context.setMessages(updatedMessages);
512
515
  return { handled: true, shouldContinue: false, updatedContext: { messages: updatedMessages } };
513
516
  }
517
+ if (trimmedCommand === '/update') {
518
+ logger.flow('Update command received');
519
+ const reply = (content) => {
520
+ const updatedMessages = [...context.messages, { role: 'assistant', content }];
521
+ context.setMessages(updatedMessages);
522
+ return { handled: true, shouldContinue: false, updatedContext: { messages: updatedMessages } };
523
+ };
524
+ let currentVersion = '0.0.0';
525
+ try {
526
+ const require = createRequire(import.meta.url);
527
+ currentVersion = require('../../package.json').version;
528
+ }
529
+ catch { }
530
+ const info = await checkForCliUpdate(currentVersion);
531
+ if (!info)
532
+ return reply(`✅ You're on the latest version (v${currentVersion}).`);
533
+ context.setMessages([...context.messages, { role: 'assistant', content: `🚀 Updating v${info.current} → v${info.latest}…` }]);
534
+ const result = await runCliUpdate(info.latest);
535
+ if (result.success) {
536
+ setSkippedVersion(info.latest);
537
+ return reply(`✅ Updated to v${info.latest}. Restart orquesta (\`/exit\` then run it again) to use it.`);
538
+ }
539
+ return reply(`❌ Update failed: ${result.error}`);
540
+ }
541
+ if (trimmedCommand === '/hook' || trimmedCommand.startsWith('/hook ')) {
542
+ logger.flow('Hook command received');
543
+ const sub = trimmedCommand.split(/\s+/)[1] || 'status';
544
+ const reply = (content) => {
545
+ const updatedMessages = [...context.messages, { role: 'assistant', content }];
546
+ context.setMessages(updatedMessages);
547
+ return { handled: true, shouldContinue: false, updatedContext: { messages: updatedMessages } };
548
+ };
549
+ const oc = configManager.getOrquestaConfig();
550
+ const binding = readHookConfig(process.cwd());
551
+ if (sub === 'status') {
552
+ if (!binding)
553
+ return reply('🔌 Claude Code hook: not enabled in this directory.\nEnable it with /hook enable.');
554
+ const name = oc?.projectId === binding.projectId && oc?.projectName ? oc.projectName : binding.projectId;
555
+ return reply(`🟢 Claude Code hook: enabled here → streaming into ${name}\n Project ID: ${binding.projectId}\n Disable with /hook disable.`);
556
+ }
557
+ if (sub === 'disable') {
558
+ if (!binding)
559
+ return reply('Nothing to do — no Orquesta hook is configured in this directory.');
560
+ const changed = disableHooks(process.cwd(), { quiet: true });
561
+ return reply(changed
562
+ ? '✅ Hook disabled. Claude Code runs in this directory no longer report to Orquesta.'
563
+ : 'Nothing to do — no Orquesta hook was configured here.');
564
+ }
565
+ if (sub === 'enable') {
566
+ if (!oc?.token)
567
+ return reply('❌ Not logged in. Run /login first, then /hook enable.');
568
+ if (!oc.projectId) {
569
+ return reply('⚠️ No project selected for this session.\nRun `orquesta hook enable` (or `orquesta hook switch`) in a shell to pick one — it can show the project list interactively.');
570
+ }
571
+ const ok = writeHookFiles({
572
+ projectId: oc.projectId,
573
+ token: oc.token,
574
+ apiUrl: binding?.apiUrl || 'https://getorquesta.com',
575
+ quiet: true,
576
+ });
577
+ return reply(ok
578
+ ? `🟢 Hook enabled → streaming into ${oc.projectName || oc.projectId}.\nRun \`claude\` in this directory and sessions stream into Orquesta.`
579
+ : '❌ Could not write hook files (check directory permissions).');
580
+ }
581
+ return reply('Usage: /hook status | /hook enable | /hook disable\n(To move a directory to a different project, run `orquesta hook switch` in a shell.)');
582
+ }
514
583
  if (trimmedCommand === '/help') {
515
584
  const helpMessage = `
516
585
  Available commands:
@@ -529,6 +598,8 @@ Available commands:
529
598
  /login - Sign in to Orquesta via browser (opens getorquesta.com)
530
599
  /logout - Sign out of Orquesta (clears token, keeps local LLM configs)
531
600
  /whoami - Show current Orquesta connection
601
+ /hook - Claude Code hook here: /hook status | enable | disable
602
+ /update - Update orquesta-cli to the latest version
532
603
 
533
604
  Keyboard shortcuts:
534
605
  Ctrl+C - Exit
@@ -14,6 +14,7 @@ import { logger } from '../utils/logger.js';
14
14
  import { getStreamLogger } from '../utils/json-stream-logger.js';
15
15
  import { detectGitRepo } from '../utils/git-utils.js';
16
16
  import { formatErrorMessage, buildTodoContext, findActiveTodo, getTodoStats } from './utils.js';
17
+ import { BaseError } from '../errors/base.js';
17
18
  import { runParallelGraph, shouldUseParallelOrchestrator } from './parallel-orchestrator.js';
18
19
  import { memoryStore } from './memory-store.js';
19
20
  import { auditLog } from './audit-log.js';
@@ -237,7 +238,12 @@ export class PlanExecutor {
237
238
  }
238
239
  runError = error;
239
240
  auditLog.emit(auditSid, 'run.error', { runId, message: error?.message });
240
- logger.error('Plan mode execution failed', error);
241
+ if (error instanceof BaseError) {
242
+ logger.error(`Plan mode execution failed: ${error.message}`);
243
+ }
244
+ else {
245
+ logger.error('Plan mode execution failed', error);
246
+ }
241
247
  const errorMessage = formatErrorMessage(error);
242
248
  callbacks.setMessages((prev) => {
243
249
  const updatedMessages = [
@@ -1,5 +1,6 @@
1
1
  import { configManager } from '../core/config/config-manager.js';
2
2
  import { logger } from '../utils/logger.js';
3
+ import { readHookConfig } from './hook-init.js';
3
4
  const ORQUESTA_API = process.env['ORQUESTA_API_URL'] || 'https://getorquesta.com';
4
5
  export async function fetchOrquestaProjects(token) {
5
6
  try {
@@ -194,6 +195,8 @@ export async function submitPromptToOrquesta(prompt) {
194
195
  if (!orquestaConfig?.token) {
195
196
  return { success: false, error: 'Not connected to Orquesta' };
196
197
  }
198
+ const cwd = prompt.context?.['cwd'] || process.cwd();
199
+ const effectiveProjectId = readHookConfig(cwd)?.projectId || orquestaConfig.projectId;
197
200
  try {
198
201
  const response = await fetch(`${ORQUESTA_API}/api/orquesta-cli/prompts`, {
199
202
  method: 'POST',
@@ -206,6 +209,7 @@ export async function submitPromptToOrquesta(prompt) {
206
209
  context: prompt.context,
207
210
  cliType: 'orquesta-cli',
208
211
  llmConfig: prompt.llmConfig,
212
+ projectId: effectiveProjectId,
209
213
  }),
210
214
  });
211
215
  if (!response.ok) {
@@ -11,5 +11,7 @@ export declare function readHookConfig(cwd?: string): {
11
11
  projectId: string;
12
12
  apiUrl?: string;
13
13
  } | null;
14
- export declare function disableHooks(cwd?: string): void;
14
+ export declare function disableHooks(cwd?: string, opts?: {
15
+ quiet?: boolean;
16
+ }): boolean;
15
17
  //# sourceMappingURL=hook-init.d.ts.map
@@ -170,8 +170,10 @@ export function readHookConfig(cwd = process.cwd()) {
170
170
  return null;
171
171
  }
172
172
  }
173
- export function disableHooks(cwd = process.cwd()) {
174
- console.log('\n Disabling Orquesta hook for this directory...\n');
173
+ export function disableHooks(cwd = process.cwd(), opts = {}) {
174
+ const log = (m) => { if (!opts.quiet)
175
+ console.log(m); };
176
+ log('\n Disabling Orquesta hook for this directory...\n');
175
177
  let changed = false;
176
178
  const settingsPath = path.join(cwd, '.claude', 'settings.json');
177
179
  if (fs.existsSync(settingsPath)) {
@@ -194,11 +196,12 @@ export function disableHooks(cwd = process.cwd()) {
194
196
  delete settings.hooks;
195
197
  fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + '\n');
196
198
  if (changed)
197
- console.log(' Removed Orquesta hooks from .claude/settings.json');
199
+ log(' Removed Orquesta hooks from .claude/settings.json');
198
200
  }
199
201
  }
200
202
  catch {
201
- console.warn(' \x1b[33m⚠ Could not parse .claude/settings.json — left untouched.\x1b[0m');
203
+ if (!opts.quiet)
204
+ console.warn(' \x1b[33m⚠ Could not parse .claude/settings.json — left untouched.\x1b[0m');
202
205
  }
203
206
  }
204
207
  const orquestaJson = path.join(cwd, '.orquesta.json');
@@ -206,14 +209,16 @@ export function disableHooks(cwd = process.cwd()) {
206
209
  try {
207
210
  fs.unlinkSync(orquestaJson);
208
211
  changed = true;
209
- console.log(' Removed .orquesta.json');
212
+ log(' Removed .orquesta.json');
210
213
  }
211
214
  catch {
212
- console.warn(' \x1b[33m⚠ Could not remove .orquesta.json.\x1b[0m');
215
+ if (!opts.quiet)
216
+ console.warn(' \x1b[33m⚠ Could not remove .orquesta.json.\x1b[0m');
213
217
  }
214
218
  }
215
- console.log(changed
219
+ log(changed
216
220
  ? '\n Done. Claude Code runs in this directory no longer report to Orquesta.\n'
217
221
  : '\n Nothing to do — no Orquesta hook was configured here.\n');
222
+ return changed;
218
223
  }
219
224
  //# sourceMappingURL=hook-init.js.map
@@ -515,7 +515,7 @@ export const PlanExecuteApp = ({ llmClient: initialLlmClient, modelInfo }) => {
515
515
  logger.debug('Ctrl+C pressed - waiting for double-tap to exit');
516
516
  return;
517
517
  }
518
- if (updatePhase === 'available' && !isProcessing && input.trim() === '') {
518
+ if (updatePhase === 'available' && !isProcessing) {
519
519
  if (inputChar === 'y' || inputChar === 'Y') {
520
520
  setUpdateError('');
521
521
  setUpdateProgress('');
@@ -1344,7 +1344,7 @@ export const PlanExecuteApp = ({ llmClient: initialLlmClient, modelInfo }) => {
1344
1344
  ? "Press ESC to close settings..."
1345
1345
  : showDocsBrowser
1346
1346
  ? "Select a doc source or press ESC..."
1347
- : "Type your message... (@ files, / commands, Alt+Enter newline)", focus: !showSessionBrowser && !showSettings && !showDocsBrowser && !planExecutionState.askUserRequest })),
1347
+ : "Type your message... (@ files, / commands, Alt+Enter newline)", focus: !showSessionBrowser && !showSettings && !showDocsBrowser && !planExecutionState.askUserRequest && updatePhase === 'hidden' })),
1348
1348
  input.length > 0 && (React.createElement(Text, { color: input.length > 4000 ? 'red' : input.length > 2000 ? 'yellow' : 'gray', dimColor: true }, input.length.toLocaleString())))),
1349
1349
  fileBrowserState.showFileBrowser && !isProcessing && (React.createElement(Box, { marginTop: 0 }, fileBrowserState.isLoadingFiles ? (React.createElement(Box, { borderStyle: "single", borderColor: "yellow", paddingX: 1 },
1350
1350
  React.createElement(Spinner, { type: "dots" }),
@@ -57,6 +57,14 @@ export const SLASH_COMMANDS = [
57
57
  name: '/whoami',
58
58
  description: 'Show current Orquesta connection',
59
59
  },
60
+ {
61
+ name: '/hook',
62
+ description: 'Claude Code hook here: status | enable | disable',
63
+ },
64
+ {
65
+ name: '/update',
66
+ description: 'Update orquesta-cli to the latest version',
67
+ },
60
68
  {
61
69
  name: '/help',
62
70
  description: 'Show help message',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "orquesta-cli",
3
- "version": "0.2.29",
3
+ "version": "0.2.31",
4
4
  "description": "Orquesta CLI - AI-powered coding assistant with team collaboration",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",