orquesta-cli 0.2.25 → 0.2.27

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/cli.js CHANGED
@@ -53,7 +53,8 @@ program
53
53
  .option('--sync', 'Sync configurations with Orquesta and exit')
54
54
  .option('--scan', 'Scan for available LLM providers (env vars + local ports)')
55
55
  .option('--add-provider <providerId>', 'Add a specific provider by ID (e.g., openai, anthropic, ollama)')
56
- .option('--init', 'Initialize Claude Code hook integration for this project (requires --token)')
56
+ .option('--init', 'Enable the Claude Code hook in this directory (reuses your login; or pass --token)')
57
+ .option('--disable-hook', 'Disable the Claude Code hook in this directory')
57
58
  .action(async (options) => {
58
59
  if (options.appendSystemPrompt) {
59
60
  setAppendedSystemPrompt(options.appendSystemPrompt);
@@ -63,12 +64,22 @@ program
63
64
  return;
64
65
  }
65
66
  if (options.init) {
66
- if (!options.token) {
67
- console.error('Error: --init requires --token. Usage: orquesta --init --token <token>');
67
+ await configManager.initialize();
68
+ const saved = configManager.getOrquestaConfig();
69
+ const token = options.token || saved?.token;
70
+ if (!token) {
71
+ console.error(chalk.red('Error: not connected to Orquesta.'));
72
+ console.error('Run ' + chalk.cyan('orquesta --login') + ' first, or pass ' + chalk.cyan('orquesta --init --token oat_…') + '.');
68
73
  process.exit(1);
69
74
  }
70
75
  const { initHooks } = await import('./orquesta/hook-init.js');
71
- await initHooks(options.token);
76
+ const preferredProjectId = options.project || (options.token ? undefined : saved?.projectId);
77
+ await initHooks(token, undefined, preferredProjectId);
78
+ return;
79
+ }
80
+ if (options.disableHook) {
81
+ const { disableHooks } = await import('./orquesta/hook-init.js');
82
+ disableHooks();
72
83
  return;
73
84
  }
74
85
  await configManager.initialize();
@@ -312,7 +323,9 @@ program.on('command:*', () => {
312
323
  console.log(chalk.white(' --token <token> Connect to Orquesta dashboard\n'));
313
324
  console.log(chalk.white(' --project <id> Select project when connecting\n'));
314
325
  console.log(chalk.white(' --switch-project [id] Switch to a different project\n'));
315
- console.log(chalk.white(' --status Show Orquesta connection status\n'));
326
+ console.log(chalk.white(' --status Show connection + hook status\n'));
327
+ console.log(chalk.white(' --init Enable the Claude Code hook here\n'));
328
+ console.log(chalk.white(' --disable-hook Disable the Claude Code hook here\n'));
316
329
  console.log(chalk.white(' --sync Sync configurations with Orquesta\n'));
317
330
  console.log(chalk.white(' --disconnect Disconnect from Orquesta\n'));
318
331
  console.log(chalk.white(' --scan Scan for available LLM providers\n'));
@@ -1,4 +1,4 @@
1
- export declare function initHooks(token: string, apiUrl?: string): Promise<void>;
1
+ export declare function initHooks(token: string, apiUrl?: string, preferredProjectId?: string): Promise<void>;
2
2
  export declare function writeHookFiles(opts: {
3
3
  projectId: string;
4
4
  token: string;
@@ -7,4 +7,9 @@ export declare function writeHookFiles(opts: {
7
7
  agentBin?: string;
8
8
  quiet?: boolean;
9
9
  }): boolean;
10
+ export declare function readHookConfig(cwd?: string): {
11
+ projectId: string;
12
+ apiUrl?: string;
13
+ } | null;
14
+ export declare function disableHooks(cwd?: string): void;
10
15
  //# sourceMappingURL=hook-init.d.ts.map
@@ -31,12 +31,47 @@ function resolveAgentBin() {
31
31
  console.warn(' \x1b[33m npm install -g orquesta-agent\x1b[0m\n');
32
32
  return 'orquesta-agent';
33
33
  }
34
- export async function initHooks(token, apiUrl = 'https://getorquesta.com') {
34
+ async function resolveTargetProject(projects, preferredProjectId) {
35
+ if (preferredProjectId) {
36
+ const match = projects.find((p) => p.id === preferredProjectId);
37
+ if (match) {
38
+ console.log(` Project: ${match.name}\n`);
39
+ return match;
40
+ }
41
+ console.warn(` \x1b[33m⚠ Your saved project isn't visible to this token — pick one below.\x1b[0m\n`);
42
+ }
43
+ if (projects.length === 1) {
44
+ console.log(` Project: ${projects[0].name}\n`);
45
+ return projects[0];
46
+ }
47
+ if (!process.stdin.isTTY) {
48
+ console.error(` Error: this token can reach ${projects.length} projects and none was specified.\n` +
49
+ ` Re-run with: orquesta --init --project <projectId>`);
50
+ process.exit(1);
51
+ }
52
+ console.log(' Which project should this directory report to?\n');
53
+ projects.forEach((p, i) => console.log(` ${i + 1}. ${p.name}`));
54
+ console.log();
55
+ const readline = await import('readline');
56
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
57
+ const answer = await new Promise((resolve) => {
58
+ rl.question(` Enter number (1-${projects.length}): `, (a) => { rl.close(); resolve(a.trim()); });
59
+ });
60
+ const num = parseInt(answer, 10);
61
+ const pick = (!isNaN(num) && num >= 1 && num <= projects.length) ? projects[num - 1] : null;
62
+ if (!pick) {
63
+ console.error(' Invalid selection.');
64
+ process.exit(1);
65
+ throw new Error();
66
+ }
67
+ console.log(` Project: ${pick.name}\n`);
68
+ return pick;
69
+ }
70
+ export async function initHooks(token, apiUrl = 'https://getorquesta.com', preferredProjectId) {
35
71
  console.log('\n Initializing Orquesta hook integration...\n');
36
72
  const agentBin = resolveAgentBin();
37
73
  console.log(' Validating token...');
38
- let projectId;
39
- let projectName;
74
+ let projects;
40
75
  try {
41
76
  const res = await fetch(`${apiUrl}/api/orquesta-cli/projects`, {
42
77
  headers: { 'Authorization': `Bearer ${token}` },
@@ -46,27 +81,25 @@ export async function initHooks(token, apiUrl = 'https://getorquesta.com') {
46
81
  console.error(` Error: Invalid token`);
47
82
  process.exit(1);
48
83
  }
49
- const firstProject = data.projects?.[0];
50
- if (!firstProject) {
51
- console.error(' Error: No projects found for this token');
52
- process.exit(1);
53
- throw new Error();
54
- }
55
- projectId = firstProject.id;
56
- projectName = firstProject.name;
57
- console.log(` Connected to: ${projectName}\n`);
84
+ projects = data.projects ?? [];
58
85
  }
59
86
  catch (err) {
60
87
  const msg = err instanceof Error ? err.message : 'Unknown error';
61
88
  console.error(` Error: Could not reach ${apiUrl} (${msg})`);
62
89
  process.exit(1);
90
+ throw new Error();
63
91
  }
64
- writeHookFiles({ projectId, token, apiUrl, agentBin });
92
+ if (projects.length === 0) {
93
+ console.error(' Error: No projects found for this token');
94
+ process.exit(1);
95
+ }
96
+ const chosen = await resolveTargetProject(projects, preferredProjectId);
97
+ writeHookFiles({ projectId: chosen.id, token, apiUrl, agentBin });
65
98
  console.log(`
66
- Done! Now run \`claude\` in this directory — all activity will
67
- be tracked in Orquesta automatically.
99
+ Done! "${chosen.name}" is wired to this directory.
100
+ Run \`claude\` here and every session streams into Orquesta automatically.
68
101
 
69
- Dashboard: ${apiUrl}/dashboard/projects/${projectId}
102
+ Dashboard: ${apiUrl}/dashboard/projects/${chosen.id}
70
103
  `);
71
104
  }
72
105
  export function writeHookFiles(opts) {
@@ -123,4 +156,64 @@ export function writeHookFiles(opts) {
123
156
  return false;
124
157
  }
125
158
  }
159
+ export function readHookConfig(cwd = process.cwd()) {
160
+ try {
161
+ const p = path.join(cwd, '.orquesta.json');
162
+ if (!fs.existsSync(p))
163
+ return null;
164
+ const data = JSON.parse(fs.readFileSync(p, 'utf8'));
165
+ if (!data.projectId)
166
+ return null;
167
+ return { projectId: data.projectId, apiUrl: data.apiUrl };
168
+ }
169
+ catch {
170
+ return null;
171
+ }
172
+ }
173
+ export function disableHooks(cwd = process.cwd()) {
174
+ console.log('\n Disabling Orquesta hook for this directory...\n');
175
+ let changed = false;
176
+ const settingsPath = path.join(cwd, '.claude', 'settings.json');
177
+ if (fs.existsSync(settingsPath)) {
178
+ try {
179
+ const settings = JSON.parse(fs.readFileSync(settingsPath, 'utf8'));
180
+ if (settings.hooks && typeof settings.hooks === 'object') {
181
+ for (const event of Object.keys(settings.hooks)) {
182
+ const before = settings.hooks[event];
183
+ if (!Array.isArray(before))
184
+ continue;
185
+ const after = before.filter((e) => !e.hooks?.some((h) => h.command?.includes('orquesta-agent hook')));
186
+ if (after.length !== before.length)
187
+ changed = true;
188
+ if (after.length === 0)
189
+ delete settings.hooks[event];
190
+ else
191
+ settings.hooks[event] = after;
192
+ }
193
+ if (Object.keys(settings.hooks).length === 0)
194
+ delete settings.hooks;
195
+ fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + '\n');
196
+ if (changed)
197
+ console.log(' Removed Orquesta hooks from .claude/settings.json');
198
+ }
199
+ }
200
+ catch {
201
+ console.warn(' \x1b[33m⚠ Could not parse .claude/settings.json — left untouched.\x1b[0m');
202
+ }
203
+ }
204
+ const orquestaJson = path.join(cwd, '.orquesta.json');
205
+ if (fs.existsSync(orquestaJson)) {
206
+ try {
207
+ fs.unlinkSync(orquestaJson);
208
+ changed = true;
209
+ console.log(' Removed .orquesta.json');
210
+ }
211
+ catch {
212
+ console.warn(' \x1b[33m⚠ Could not remove .orquesta.json.\x1b[0m');
213
+ }
214
+ }
215
+ console.log(changed
216
+ ? '\n Done. Claude Code runs in this directory no longer report to Orquesta.\n'
217
+ : '\n Nothing to do — no Orquesta hook was configured here.\n');
218
+ }
126
219
  //# sourceMappingURL=hook-init.js.map
@@ -4,7 +4,7 @@ import * as fs from 'fs';
4
4
  import * as path from 'path';
5
5
  import { configManager } from '../core/config/config-manager.js';
6
6
  import { syncOrquestaConfigs, fetchOrquestaProjects } from '../orquesta/config-sync.js';
7
- import { writeHookFiles } from '../orquesta/hook-init.js';
7
+ import { writeHookFiles, readHookConfig } from '../orquesta/hook-init.js';
8
8
  import { scanProviders, toEndpointConfig } from '../core/config/auto-detect.js';
9
9
  const ORQUESTA_API_URL = process.env['ORQUESTA_API_URL'] || 'https://getorquesta.com';
10
10
  export function needsFirstRunSetup() {
@@ -283,5 +283,19 @@ export function showConnectionStatus() {
283
283
  console.log(chalk.dim(` Connected: ${orquestaConfig.connectedAt ? new Date(orquestaConfig.connectedAt).toLocaleString() : 'Unknown'}`));
284
284
  console.log(chalk.dim(` Last sync: ${orquestaConfig.lastSyncAt ? new Date(orquestaConfig.lastSyncAt).toLocaleString() : 'Never'}`));
285
285
  console.log(chalk.dim(` Auto-sync: ${orquestaConfig.autoSync !== false ? 'Enabled' : 'Disabled'}`));
286
+ const hook = readHookConfig();
287
+ console.log();
288
+ if (hook) {
289
+ const sameAsConnected = hook.projectId === orquestaConfig.projectId;
290
+ const name = sameAsConnected && orquestaConfig.projectName ? orquestaConfig.projectName : hook.projectId;
291
+ console.log(chalk.green('Claude Code hook: enabled in this directory'));
292
+ console.log(chalk.dim(` Streaming into: ${name}`));
293
+ console.log(chalk.dim(` Project ID: ${hook.projectId}`));
294
+ console.log(chalk.dim(` Disable with: orquesta --disable-hook`));
295
+ }
296
+ else {
297
+ console.log(chalk.yellow('Claude Code hook: not enabled in this directory'));
298
+ console.log(chalk.dim(` Enable with: orquesta --init`));
299
+ }
286
300
  }
287
301
  //# sourceMappingURL=first-run-setup.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "orquesta-cli",
3
- "version": "0.2.25",
3
+ "version": "0.2.27",
4
4
  "description": "Orquesta CLI - AI-powered coding assistant with team collaboration",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",