@guildai/cli 0.10.0 → 0.11.0

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.
Files changed (64) hide show
  1. package/dist/commands/agent/chat.js +10 -7
  2. package/dist/commands/agent/clone.js +2 -0
  3. package/dist/commands/agent/fork.js +2 -0
  4. package/dist/commands/agent/init.js +57 -44
  5. package/dist/commands/agent/list.js +2 -2
  6. package/dist/commands/agent/logs.d.ts +3 -0
  7. package/dist/commands/agent/logs.js +62 -0
  8. package/dist/commands/agent/owners.js +3 -3
  9. package/dist/commands/agent/pull.js +8 -12
  10. package/dist/commands/agent/save.js +2 -3
  11. package/dist/commands/agent/search.js +2 -2
  12. package/dist/commands/agent/test.js +9 -6
  13. package/dist/commands/agent/update.js +9 -1
  14. package/dist/commands/agent/versions.js +2 -2
  15. package/dist/commands/agent/workspaces.js +2 -2
  16. package/dist/commands/auth/login.js +1 -3
  17. package/dist/commands/chat.js +99 -28
  18. package/dist/commands/config/get.js +4 -4
  19. package/dist/commands/config/list.js +2 -3
  20. package/dist/commands/config/path.js +2 -3
  21. package/dist/commands/config/set.js +12 -12
  22. package/dist/commands/credentials/endpoint-list.js +2 -2
  23. package/dist/commands/credentials/list.js +2 -2
  24. package/dist/commands/credentials/policy-list.js +2 -2
  25. package/dist/commands/doctor.js +5 -5
  26. package/dist/commands/integration/connect.js +2 -2
  27. package/dist/commands/integration/create.js +2 -2
  28. package/dist/commands/integration/get.js +2 -2
  29. package/dist/commands/integration/list.js +2 -2
  30. package/dist/commands/integration/operation/create.js +4 -4
  31. package/dist/commands/integration/operation/list.js +2 -2
  32. package/dist/commands/integration/update.js +2 -2
  33. package/dist/commands/integration/version/build.js +2 -2
  34. package/dist/commands/integration/version/create.js +2 -2
  35. package/dist/commands/integration/version/get.js +2 -2
  36. package/dist/commands/integration/version/list.js +2 -2
  37. package/dist/commands/integration/version/publish.js +2 -2
  38. package/dist/commands/integration/version/test.js +2 -2
  39. package/dist/commands/job/get.js +2 -2
  40. package/dist/commands/session/list.js +2 -2
  41. package/dist/commands/session/tasks.js +2 -2
  42. package/dist/commands/setup.d.ts +16 -0
  43. package/dist/commands/setup.js +76 -46
  44. package/dist/commands/trigger/list.js +2 -2
  45. package/dist/commands/workspace/agent/list.js +2 -2
  46. package/dist/commands/workspace/context/list.js +2 -2
  47. package/dist/commands/workspace/list.js +2 -2
  48. package/dist/index.js +15 -4
  49. package/dist/lib/auth.d.ts +1 -1
  50. package/dist/lib/auth.js +2 -2
  51. package/dist/lib/output-mode.d.ts +9 -2
  52. package/dist/lib/output-mode.js +23 -2
  53. package/dist/lib/output.d.ts +7 -1
  54. package/dist/lib/output.js +32 -1
  55. package/dist/lib/session-events.d.ts +13 -2
  56. package/dist/lib/session-events.js +15 -1
  57. package/dist/lib/session-polling.js +9 -3
  58. package/dist/lib/session-resume.d.ts +15 -1
  59. package/dist/lib/session-resume.js +149 -16
  60. package/dist/lib/splash.js +3 -2
  61. package/dist/lib/stdin.d.ts +5 -1
  62. package/dist/lib/stdin.js +8 -1
  63. package/dist/lib/version-helpers.js +24 -8
  64. package/package.json +1 -1
@@ -2,7 +2,7 @@
2
2
  // SPDX-License-Identifier: Apache-2.0
3
3
  import React, { useState, useEffect, useRef } from 'react';
4
4
  import { Box, Text, Static, render, useInput, useApp } from 'ink';
5
- import { Command } from 'commander';
5
+ import { Command, Option } from 'commander';
6
6
  import { getAuthToken } from '../lib/auth.js';
7
7
  import { GuildAPIClient } from '../lib/api-client.js';
8
8
  import { handleAxiosError, ErrorCodes, debug, isDebugMode, retry, } from '../lib/errors.js';
@@ -13,8 +13,8 @@ import chalk from 'chalk';
13
13
  import { readFileSync } from 'fs';
14
14
  import path from 'path';
15
15
  import { fileURLToPath } from 'url';
16
- import { isUnfulfilledAgentInstallRequest, isFilteredTaskName, getTaskDisplayName, getAgentName, getAgentNotificationText, isDoneResponseStreamEvent, isResponseStreamEvent, isRootTaskEvent, } from '../lib/session-events.js';
17
- import { printResumeHint, fetchSession, fetchSessionEvents, eventsToDisplayMessages, } from '../lib/session-resume.js';
16
+ import { isUnfulfilledAgentInstallRequest, isFilteredTaskName, getTaskDisplayName, getAgentName, getAgentNotificationText, applyResponseStreamText, isDoneResponseStreamEvent, isResponseStreamEvent, isRootTaskEvent, } from '../lib/session-events.js';
17
+ import { printResumeHint, fetchSession, fetchSessionEvents, prepareSessionResumeDisplay, } from '../lib/session-resume.js';
18
18
  import { DEFAULT_EVENT_TYPES, parseEventFilter, shouldShowEvent, } from '../lib/event-filter.js';
19
19
  import { fetchEvents, fetchTasks } from '../lib/session-events-fetch.js';
20
20
  import { AgentInstallPrompt } from '../components/AgentInstallPrompt.js';
@@ -112,6 +112,11 @@ function renderAssistantMessage(text, taskName) {
112
112
  const rendered = fixListItemMarkdown(marked.parse(text));
113
113
  return `${chalk.green('●')} ${chalk.bold(taskName)}\n${rendered.trim()}`;
114
114
  }
115
+ function applyResponseStreamContentsInSequence(contents) {
116
+ return [...contents]
117
+ .sort((left, right) => left.sequence - right.sequence)
118
+ .reduce((currentText, content) => applyResponseStreamText(currentText, content), '');
119
+ }
115
120
  /**
116
121
  * Output the result of a --once mode session.
117
122
  * Handles both JSON and human-readable output formats.
@@ -124,9 +129,18 @@ async function outputOnceResult(sessionId, events, mode) {
124
129
  const finalAgentMessages = events.filter((e) => e.type === 'agent_notification_message' &&
125
130
  isRootTaskEvent(e) &&
126
131
  !isResponseStreamEvent(e));
127
- const streamFallbacks = events.filter((e) => e.type === 'agent_notification_message' &&
128
- isRootTaskEvent(e) &&
129
- isDoneResponseStreamEvent(e));
132
+ const responseStreamContents = new Map();
133
+ const doneStreamIds = [];
134
+ for (const event of events) {
135
+ if (!isResponseStreamEvent(event) || !isRootTaskEvent(event))
136
+ continue;
137
+ const streamContents = responseStreamContents.get(event.content.stream_id) ?? [];
138
+ streamContents.push(event.content);
139
+ responseStreamContents.set(event.content.stream_id, streamContents);
140
+ if (isDoneResponseStreamEvent(event)) {
141
+ doneStreamIds.push(event.content.stream_id);
142
+ }
143
+ }
130
144
  if (finalAgentMessages.length > 0) {
131
145
  const messageContent = extractMessageText(getAgentNotificationText(finalAgentMessages[finalAgentMessages.length - 1]));
132
146
  const rendered = fixListItemMarkdown(await marked(messageContent));
@@ -142,9 +156,12 @@ async function outputOnceResult(sessionId, events, mode) {
142
156
  return;
143
157
  }
144
158
  }
145
- if (streamFallbacks.length > 0) {
146
- const messageContent = extractMessageText(getAgentNotificationText(streamFallbacks[streamFallbacks.length - 1]));
147
- const rendered = fixListItemMarkdown(await marked(messageContent));
159
+ const lastDoneStreamId = doneStreamIds[doneStreamIds.length - 1];
160
+ const streamFallbackText = lastDoneStreamId !== undefined
161
+ ? applyResponseStreamContentsInSequence(responseStreamContents.get(lastDoneStreamId) ?? [])
162
+ : null;
163
+ if (streamFallbackText !== null) {
164
+ const rendered = fixListItemMarkdown(await marked(streamFallbackText));
148
165
  console.log(rendered.trim());
149
166
  }
150
167
  }
@@ -303,6 +320,7 @@ export function ChatApp({ initialPrompt, version, workspaceId, versionId, agentN
303
320
  };
304
321
  connect();
305
322
  }, [workspaceId, initialPrompt, versionId]);
323
+ const chatInstanceKey = `${connectedSession?.id ?? 'pending'}:${resumeEvents?.[resumeEvents.length - 1]?.id ?? 'live'}`;
306
324
  // Render both splash and chat, but only show one at a time
307
325
  // ChatUIWithConnection is always mounted (when connected) so it can stream events in background
308
326
  return (React.createElement(React.Fragment, null,
@@ -316,7 +334,7 @@ export function ChatApp({ initialPrompt, version, workspaceId, versionId, agentN
316
334
  }
317
335
  // If not connected yet, ignore escape (let connection complete)
318
336
  } })),
319
- phase === 'chat' && (React.createElement(ChatUIWithConnection, { initialPrompt: initialPrompt, version: version, versionId: versionId, agentName: agentName, client: connectedClient, session: connectedSession, onFirstMessage: () => setFirstMessageReceived(true), resumeEvents: resumeEvents, resumeCommand: resumeCommand, eventFilter: eventFilter })),
337
+ phase === 'chat' && (React.createElement(ChatUIWithConnection, { key: chatInstanceKey, initialPrompt: initialPrompt, version: version, versionId: versionId, agentName: agentName, client: connectedClient, session: connectedSession, onFirstMessage: () => setFirstMessageReceived(true), resumeEvents: resumeEvents, resumeCommand: resumeCommand, eventFilter: eventFilter })),
320
338
  (phase === 'splash' || phase === 'finalizing') &&
321
339
  connectedSession &&
322
340
  connectedClient && (React.createElement(Box, { display: "none" },
@@ -336,9 +354,9 @@ function ChatUIWithConnection({ initialPrompt, version: _version, versionId: _ve
336
354
  // Only include initial prompt when active (not during splash)
337
355
  // Static component writes to stdout even with display="none"
338
356
  // When resuming, show past events instead of initial prompt
339
- const resumeDisplayMessages = resumeEvents
340
- ? eventsToDisplayMessages(resumeEvents)
341
- : null;
357
+ const [resumeDisplay] = useState(() => resumeEvents ? prepareSessionResumeDisplay(resumeEvents) : null);
358
+ const resumeDisplayMessages = resumeDisplay?.displayMessages ?? null;
359
+ const responseStreamResumeState = resumeDisplay?.responseStreamState ?? null;
342
360
  const sessionLinkMessage = isActive && preConnectedSession?.session_url
343
361
  ? [
344
362
  {
@@ -502,23 +520,53 @@ function ChatUIWithConnection({ initialPrompt, version: _version, versionId: _ve
502
520
  const isPolling = useRef(false);
503
521
  const receivedResponseSinceLastInput = useRef(false);
504
522
  const firstMessageNotified = useRef(!!resumeEvents);
505
- const responseStreamKeys = useRef(new Map());
506
- const responseStreamTimestamps = useRef(new Map());
507
- const responseStreamKeysByTask = useRef(new Map());
508
- const clearResponseStreamsForTask = (taskId) => {
523
+ const responseStreamKeys = useRef(responseStreamResumeState?.keys ?? new Map());
524
+ const responseStreamContents = useRef(responseStreamResumeState?.contents ?? new Map());
525
+ const responseStreamTexts = useRef(responseStreamResumeState?.texts ?? new Map());
526
+ const responseStreamTimestamps = useRef(responseStreamResumeState?.timestamps ?? new Map());
527
+ const responseStreamStatuses = useRef(responseStreamResumeState?.statuses ??
528
+ new Map());
529
+ const responseStreamKeysByTask = useRef(responseStreamResumeState?.keysByTask ?? new Map());
530
+ const clearResponseStreamsForTask = (taskId, options = {}) => {
509
531
  if (!taskId)
510
532
  return;
511
533
  const keys = responseStreamKeysByTask.current.get(taskId);
512
534
  if (!keys?.size)
513
535
  return;
536
+ // Removed keys disappear from the transcript; detached keys stay rendered
537
+ // but are no longer tracked as active streams for future cleanup.
538
+ const removedKeys = new Set();
539
+ const detachedKeys = new Set();
514
540
  for (const [streamId, key] of responseStreamKeys.current.entries()) {
515
541
  if (keys.has(key)) {
542
+ if (options.preserveContinued &&
543
+ responseStreamStatuses.current.get(streamId) === 'continued' &&
544
+ responseStreamTexts.current.get(streamId) !== options.finalText) {
545
+ responseStreamKeys.current.delete(streamId);
546
+ responseStreamContents.current.delete(streamId);
547
+ responseStreamTexts.current.delete(streamId);
548
+ responseStreamTimestamps.current.delete(streamId);
549
+ responseStreamStatuses.current.delete(streamId);
550
+ detachedKeys.add(key);
551
+ continue;
552
+ }
516
553
  responseStreamKeys.current.delete(streamId);
554
+ responseStreamContents.current.delete(streamId);
555
+ responseStreamTexts.current.delete(streamId);
517
556
  responseStreamTimestamps.current.delete(streamId);
557
+ responseStreamStatuses.current.delete(streamId);
558
+ removedKeys.add(key);
518
559
  }
519
560
  }
520
- responseStreamKeysByTask.current.delete(taskId);
521
- setMessages((prev) => prev.filter((message) => !keys.has(message.key)));
561
+ const inactiveKeys = new Set([...removedKeys, ...detachedKeys]);
562
+ const remainingKeys = new Set([...keys].filter((key) => !inactiveKeys.has(key)));
563
+ if (remainingKeys.size > 0) {
564
+ responseStreamKeysByTask.current.set(taskId, remainingKeys);
565
+ }
566
+ else {
567
+ responseStreamKeysByTask.current.delete(taskId);
568
+ }
569
+ setMessages((prev) => prev.filter((message) => !removedKeys.has(message.key)));
522
570
  };
523
571
  const upsertResponseStreamMessage = (event) => {
524
572
  if (!isResponseStreamEvent(event))
@@ -529,9 +577,12 @@ function ChatUIWithConnection({ initialPrompt, version: _version, versionId: _ve
529
577
  const taskId = event.task?.id;
530
578
  const existingKey = responseStreamKeys.current.get(streamId);
531
579
  if (event.content.status === 'aborted') {
580
+ responseStreamContents.current.delete(streamId);
581
+ responseStreamTexts.current.delete(streamId);
582
+ responseStreamTimestamps.current.delete(streamId);
583
+ responseStreamStatuses.current.delete(streamId);
532
584
  if (existingKey) {
533
585
  responseStreamKeys.current.delete(streamId);
534
- responseStreamTimestamps.current.delete(streamId);
535
586
  if (taskId) {
536
587
  const keys = responseStreamKeysByTask.current.get(taskId);
537
588
  keys?.delete(existingKey);
@@ -542,11 +593,22 @@ function ChatUIWithConnection({ initialPrompt, version: _version, versionId: _ve
542
593
  }
543
594
  return;
544
595
  }
545
- const text = event.content.text;
596
+ const streamContents = responseStreamContents.current.get(streamId) ?? [];
597
+ const existingContentIndex = streamContents.findIndex((content) => content.sequence === event.content.sequence);
598
+ if (existingContentIndex === -1) {
599
+ streamContents.push(event.content);
600
+ }
601
+ else {
602
+ streamContents[existingContentIndex] = event.content;
603
+ }
604
+ responseStreamContents.current.set(streamId, streamContents);
605
+ const text = applyResponseStreamContentsInSequence(streamContents);
546
606
  if (!text.trim())
547
607
  return;
548
608
  const key = existingKey ?? `response-stream-${streamId}`;
549
609
  responseStreamKeys.current.set(streamId, key);
610
+ responseStreamTexts.current.set(streamId, text);
611
+ responseStreamStatuses.current.set(streamId, event.content.status);
550
612
  if (taskId) {
551
613
  const keys = responseStreamKeysByTask.current.get(taskId) ?? new Set();
552
614
  keys.add(key);
@@ -798,7 +860,10 @@ function ChatUIWithConnection({ initialPrompt, version: _version, versionId: _ve
798
860
  }
799
861
  const text = extractMessageText(getAgentNotificationText(event));
800
862
  if (text.trim()) {
801
- clearResponseStreamsForTask(taskInfo?.id);
863
+ clearResponseStreamsForTask(taskInfo?.id, {
864
+ preserveContinued: true,
865
+ finalText: text,
866
+ });
802
867
  const taskName = agentName || 'assistant';
803
868
  const messageContent = renderAssistantMessage(text, taskName);
804
869
  setMessages((prev) => [
@@ -867,13 +932,16 @@ function ChatUIWithConnection({ initialPrompt, version: _version, versionId: _ve
867
932
  event.content !== undefined &&
868
933
  taskInfo &&
869
934
  'agent' in taskInfo) {
870
- clearResponseStreamsForTask(taskInfo.id);
871
935
  // One-shot agents may complete with runtime_done without sending
872
936
  // agent_notification_message. Display the output if we haven't
873
937
  // already shown a response for this input cycle.
874
938
  const contentStr = typeof event.content === 'string'
875
939
  ? event.content
876
940
  : JSON.stringify(event.content);
941
+ clearResponseStreamsForTask(taskInfo.id, {
942
+ preserveContinued: true,
943
+ finalText: contentStr,
944
+ });
877
945
  if (contentStr && contentStr !== '{}' && contentStr !== 'null') {
878
946
  const rendered = fixListItemMarkdown(marked.parse(contentStr));
879
947
  const taskName = agentName || 'assistant';
@@ -1170,11 +1238,13 @@ export function createChatCommand() {
1170
1238
  .argument('[prompt...]', 'Optional initial prompt (multiple words)')
1171
1239
  .option('--agent <identifier>', 'Agent ID or full name, e.g., foo~bar (default: assistant)')
1172
1240
  .option('--once', 'One-shot mode: send message, wait for response, exit (non-interactive)')
1173
- .option('--mode <format>', 'Machine-readable output format: json or jsonl')
1174
1241
  .option('--workspace <identifier>', 'Workspace ID or full name (e.g., owner/workspace-name)')
1175
1242
  .option('--no-splash', 'Skip the splash screen animation')
1176
1243
  .option('--resume <session-id>', 'Resume an existing session')
1177
1244
  .option('--events <types>', 'Event types to show (default: user). Shorthands: none, user, system, all, or comma-separated type names (e.g. agent_console,llm_start)')
1245
+ // Accept --mode so `guild chat --mode json` works when re-parsed.
1246
+ // The actual value is read from process.argv by getOutputMode().
1247
+ .addOption(new Option('--mode <format>').hideHelp())
1178
1248
  .addHelpText('after', '\nTo chat with a local agent under development: guild agent chat')
1179
1249
  .action(async (promptArgs, options) => {
1180
1250
  const initialPrompt = promptArgs.length > 0 ? promptArgs.join(' ') : 'Hello';
@@ -1245,7 +1315,8 @@ export function createChatCommand() {
1245
1315
  if (hasRootTaskError) {
1246
1316
  debug('Found error event from root agent, exiting --once mode');
1247
1317
  const errorEvents = allEvents.filter((e) => e.type === 'runtime_error' || e.type === 'agent_notification_error');
1248
- if (errorEvents.length > 0 && !options.mode) {
1318
+ const outputMode = getOutputMode();
1319
+ if (errorEvents.length > 0 && outputMode === 'interactive') {
1249
1320
  const lastError = errorEvents[errorEvents.length - 1];
1250
1321
  const content = lastError.content;
1251
1322
  if (content?.data) {
@@ -1255,7 +1326,7 @@ export function createChatCommand() {
1255
1326
  console.error(chalk.red('Agent failed to start'));
1256
1327
  }
1257
1328
  }
1258
- else if (options.mode === 'json') {
1329
+ else if (outputMode === 'json') {
1259
1330
  console.log(JSON.stringify({
1260
1331
  session_id: session.id,
1261
1332
  events: allEvents,
@@ -1266,14 +1337,14 @@ export function createChatCommand() {
1266
1337
  }
1267
1338
  if (hasRootTaskDone || hasAgentMessage || hasUIPromptMessage) {
1268
1339
  debug('Found completion event from root agent, exiting --once mode');
1269
- await outputOnceResult(session.id, allEvents, options.mode);
1340
+ await outputOnceResult(session.id, allEvents, getOutputMode());
1270
1341
  process.exit(0);
1271
1342
  }
1272
1343
  // Timeout if no activity for too long
1273
1344
  if (inactivityCounter >= maxInactivityAttempts) {
1274
1345
  debug(`Inactivity timeout reached (${maxInactivityAttempts} attempts with no new events)`);
1275
1346
  debug(`Exiting with ${allEvents.length} events total`);
1276
- await outputOnceResult(session.id, allEvents, options.mode);
1347
+ await outputOnceResult(session.id, allEvents, getOutputMode());
1277
1348
  process.exit(0);
1278
1349
  }
1279
1350
  }
@@ -2,7 +2,7 @@
2
2
  // SPDX-License-Identifier: Apache-2.0
3
3
  import { Command } from 'commander';
4
4
  import { loadConfig, } from '../../lib/guild-config.js';
5
- import { getOutputMode } from '../../lib/output-mode.js';
5
+ import { isMachineReadable } from '../../lib/output-mode.js';
6
6
  import { createOutputWriter } from '../../lib/output.js';
7
7
  /**
8
8
  * All valid config keys across global and local configs.
@@ -25,7 +25,7 @@ export function createConfigGetCommand() {
25
25
  .action(async (key) => {
26
26
  const output = createOutputWriter();
27
27
  const config = await loadConfig();
28
- const mode = getOutputMode();
28
+ const jsonMode = isMachineReadable();
29
29
  if (!ALL_VALID_KEYS.includes(key)) {
30
30
  output.error(`Unknown config key: ${key}\n\nValid keys:\n Global: ${VALID_GLOBAL_KEYS.join(', ')}\n Local: ${VALID_LOCAL_KEYS.join(', ')}`);
31
31
  process.exit(1);
@@ -44,7 +44,7 @@ export function createConfigGetCommand() {
44
44
  source = 'local';
45
45
  }
46
46
  if (value === undefined) {
47
- if (mode === 'json') {
47
+ if (jsonMode) {
48
48
  output.data({ key, value: null, source: null });
49
49
  }
50
50
  else {
@@ -52,7 +52,7 @@ export function createConfigGetCommand() {
52
52
  }
53
53
  process.exit(1);
54
54
  }
55
- if (mode === 'json') {
55
+ if (jsonMode) {
56
56
  output.data({ key, value, source });
57
57
  }
58
58
  else {
@@ -3,15 +3,14 @@
3
3
  import { Command } from 'commander';
4
4
  import chalk from 'chalk';
5
5
  import { loadConfig } from '../../lib/guild-config.js';
6
- import { getOutputMode } from '../../lib/output-mode.js';
6
+ import { isMachineReadable } from '../../lib/output-mode.js';
7
7
  import { createOutputWriter } from '../../lib/output.js';
8
8
  export function createConfigListCommand() {
9
9
  const cmd = new Command('list');
10
10
  cmd.description('Show all configuration values').action(async () => {
11
11
  const output = createOutputWriter();
12
12
  const config = await loadConfig();
13
- const mode = getOutputMode();
14
- if (mode === 'json') {
13
+ if (isMachineReadable()) {
15
14
  output.data({
16
15
  global: config.global || null,
17
16
  local: config.local || null,
@@ -4,7 +4,7 @@ import { Command } from 'commander';
4
4
  import chalk from 'chalk';
5
5
  import * as fs from 'fs/promises';
6
6
  import { getGlobalConfigPath, getLocalConfigPath } from '../../lib/guild-config.js';
7
- import { getOutputMode } from '../../lib/output-mode.js';
7
+ import { isMachineReadable } from '../../lib/output-mode.js';
8
8
  import { createOutputWriter } from '../../lib/output.js';
9
9
  async function fileExists(filePath) {
10
10
  return fs
@@ -18,10 +18,9 @@ export function createConfigPathCommand() {
18
18
  const output = createOutputWriter();
19
19
  const globalPath = getGlobalConfigPath();
20
20
  const localPath = getLocalConfigPath();
21
- const mode = getOutputMode();
22
21
  const globalExists = await fileExists(globalPath);
23
22
  const localExists = await fileExists(localPath);
24
- if (mode === 'json') {
23
+ if (isMachineReadable()) {
25
24
  output.data({
26
25
  global: { path: globalPath, exists: globalExists },
27
26
  local: { path: localPath, exists: localExists },
@@ -3,7 +3,7 @@
3
3
  import { Command } from 'commander';
4
4
  import chalk from 'chalk';
5
5
  import { saveGlobalConfig } from '../../lib/guild-config.js';
6
- import { getOutputMode } from '../../lib/output-mode.js';
6
+ import { isMachineReadable } from '../../lib/output-mode.js';
7
7
  import { createOutputWriter } from '../../lib/output.js';
8
8
  import { GuildAPIClient } from '../../lib/api-client.js';
9
9
  import { debug } from '../../lib/errors.js';
@@ -46,8 +46,8 @@ async function resolveWorkspaceName(workspaceId) {
46
46
  return undefined;
47
47
  }
48
48
  }
49
- function printResult(key, value, mode, output) {
50
- if (mode === 'json') {
49
+ function printResult(key, value, jsonMode, output) {
50
+ if (jsonMode) {
51
51
  output.data({ key, value });
52
52
  }
53
53
  else {
@@ -62,7 +62,7 @@ export function createConfigSetCommand() {
62
62
  .argument('<value>', 'Value to set')
63
63
  .action(async (key, value) => {
64
64
  const output = createOutputWriter();
65
- const mode = getOutputMode();
65
+ const jsonMode = isMachineReadable();
66
66
  if (!VALID_GLOBAL_KEYS.includes(key)) {
67
67
  output.error(`Unknown config key: ${key}\n\nValid keys:\n${VALID_GLOBAL_KEYS.map((k) => ` ${k}`).join('\n')}`);
68
68
  process.exit(1);
@@ -71,7 +71,7 @@ export function createConfigSetCommand() {
71
71
  if (BOOLEAN_KEYS.has(typedKey)) {
72
72
  const boolValue = parseBoolean(value, key, output);
73
73
  await saveGlobalConfig({ [typedKey]: boolValue });
74
- printResult(key, boolValue, mode, output);
74
+ printResult(key, boolValue, jsonMode, output);
75
75
  return;
76
76
  }
77
77
  if (typedKey === 'default_workspace') {
@@ -80,14 +80,14 @@ export function createConfigSetCommand() {
80
80
  default_workspace: value,
81
81
  default_workspace_name: name,
82
82
  });
83
- printResult(key, value, mode, output);
83
+ printResult(key, value, jsonMode, output);
84
84
  if (name) {
85
- if (mode !== 'json') {
85
+ if (!jsonMode) {
86
86
  output.progress(` Workspace name: ${name}`);
87
87
  }
88
88
  }
89
89
  else {
90
- if (mode !== 'json') {
90
+ if (!jsonMode) {
91
91
  output.error('Could not resolve workspace name (not authenticated?)');
92
92
  }
93
93
  }
@@ -111,21 +111,21 @@ export function createConfigSetCommand() {
111
111
  default_owner: ownerId,
112
112
  default_owner_name: ownerName,
113
113
  });
114
- printResult(key, ownerId, mode, output);
114
+ printResult(key, ownerId, jsonMode, output);
115
115
  if (ownerName) {
116
- if (mode !== 'json') {
116
+ if (!jsonMode) {
117
117
  output.progress(` Owner name: ${ownerName}`);
118
118
  }
119
119
  }
120
120
  else {
121
- if (mode !== 'json') {
121
+ if (!jsonMode) {
122
122
  output.error('Could not resolve owner name (not authenticated?)');
123
123
  }
124
124
  }
125
125
  return;
126
126
  }
127
127
  await saveGlobalConfig({ [typedKey]: value });
128
- printResult(key, value, mode, output);
128
+ printResult(key, value, jsonMode, output);
129
129
  });
130
130
  return cmd;
131
131
  }
@@ -5,7 +5,7 @@ import chalk from 'chalk';
5
5
  import { GuildAPIClient } from '../../lib/api-client.js';
6
6
  import { getAuthToken } from '../../lib/auth.js';
7
7
  import { handleAxiosError } from '../../lib/errors.js';
8
- import { getOutputMode } from '../../lib/output-mode.js';
8
+ import { isMachineReadable } from '../../lib/output-mode.js';
9
9
  import { createOutputWriter } from '../../lib/output.js';
10
10
  import { DEFAULT_PAGE_LIMIT } from '../../lib/api-types.js';
11
11
  import { Table } from '../../lib/table.js';
@@ -37,7 +37,7 @@ export function createCredentialsEndpointListCommand() {
37
37
  params.append('search', options.search);
38
38
  }
39
39
  const response = await client.get(`/credentials/${credentialId}/endpoints?${params.toString()}`);
40
- if (getOutputMode() === 'json') {
40
+ if (isMachineReadable()) {
41
41
  console.log(JSON.stringify(response, null, 2));
42
42
  }
43
43
  else {
@@ -4,7 +4,7 @@ import { Command } from 'commander';
4
4
  import { GuildAPIClient } from '../../lib/api-client.js';
5
5
  import { getAuthToken } from '../../lib/auth.js';
6
6
  import { handleAxiosError } from '../../lib/errors.js';
7
- import { getOutputMode } from '../../lib/output-mode.js';
7
+ import { isMachineReadable } from '../../lib/output-mode.js';
8
8
  import { createOutputWriter, formatCredentialsTable } from '../../lib/output.js';
9
9
  import { DEFAULT_PAGE_LIMIT } from '../../lib/api-types.js';
10
10
  export function createCredentialsListCommand() {
@@ -32,7 +32,7 @@ export function createCredentialsListCommand() {
32
32
  params.append('search', options.search);
33
33
  }
34
34
  const response = await client.get(`/accounts/${accountId}/credentials?${params.toString()}`);
35
- if (getOutputMode() === 'json') {
35
+ if (isMachineReadable()) {
36
36
  console.log(JSON.stringify(response, null, 2));
37
37
  }
38
38
  else {
@@ -4,7 +4,7 @@ import { Command } from 'commander';
4
4
  import { GuildAPIClient } from '../../lib/api-client.js';
5
5
  import { getAuthToken } from '../../lib/auth.js';
6
6
  import { handleAxiosError } from '../../lib/errors.js';
7
- import { getOutputMode } from '../../lib/output-mode.js';
7
+ import { isMachineReadable } from '../../lib/output-mode.js';
8
8
  import { createOutputWriter, formatPoliciesTable } from '../../lib/output.js';
9
9
  import { DEFAULT_PAGE_LIMIT } from '../../lib/api-types.js';
10
10
  export function createCredentialsPolicyListCommand() {
@@ -27,7 +27,7 @@ export function createCredentialsPolicyListCommand() {
27
27
  params.append('limit', options.limit);
28
28
  params.append('offset', options.offset);
29
29
  const response = await client.get(`/credentials/${credentialId}/policies?${params.toString()}`);
30
- if (getOutputMode() === 'json') {
30
+ if (isMachineReadable()) {
31
31
  console.log(JSON.stringify(response, null, 2));
32
32
  }
33
33
  else {
@@ -6,7 +6,7 @@ import axios from 'axios';
6
6
  import { getAuthToken } from '../lib/auth.js';
7
7
  import { getGuildcoreUrl } from '../lib/config.js';
8
8
  import { loadGlobalConfig, isAgentDirectory, getGlobalConfigPath, loadLocalConfig, } from '../lib/guild-config.js';
9
- import { getOutputMode } from '../lib/output-mode.js';
9
+ import { isMachineReadable } from '../lib/output-mode.js';
10
10
  import { createOutputWriter } from '../lib/output.js';
11
11
  async function checkAuth() {
12
12
  const token = await getAuthToken();
@@ -178,9 +178,9 @@ export function createDoctorCommand() {
178
178
  const cmd = new Command('doctor');
179
179
  cmd.description('Check CLI setup and diagnose issues').action(async () => {
180
180
  const output = createOutputWriter();
181
- const mode = getOutputMode();
181
+ const jsonMode = isMachineReadable();
182
182
  const checks = [];
183
- if (mode !== 'json') {
183
+ if (!jsonMode) {
184
184
  output.progress('\nChecking Guild CLI setup...\n');
185
185
  }
186
186
  const runners = [
@@ -195,7 +195,7 @@ export function createDoctorCommand() {
195
195
  for (const runner of runners) {
196
196
  const result = await runner();
197
197
  checks.push(result);
198
- if (mode !== 'json') {
198
+ if (!jsonMode) {
199
199
  const icon = result.status === 'pass'
200
200
  ? chalk.green('✓')
201
201
  : result.status === 'fail'
@@ -212,7 +212,7 @@ export function createDoctorCommand() {
212
212
  const passed = checks.filter((c) => c.status === 'pass').length;
213
213
  const failed = checks.filter((c) => c.status === 'fail').length;
214
214
  const skipped = checks.filter((c) => c.status === 'skip').length;
215
- if (mode === 'json') {
215
+ if (jsonMode) {
216
216
  output.data({ checks, passed, failed, skipped });
217
217
  }
218
218
  else {
@@ -5,7 +5,7 @@ import chalk from 'chalk';
5
5
  import { GuildAPIClient } from '../../lib/api-client.js';
6
6
  import { getAuthToken } from '../../lib/auth.js';
7
7
  import { handleAxiosError } from '../../lib/errors.js';
8
- import { getOutputMode } from '../../lib/output-mode.js';
8
+ import { isMachineReadable } from '../../lib/output-mode.js';
9
9
  import { createOutputWriter } from '../../lib/output.js';
10
10
  import { isInteractive } from '../../lib/stdin.js';
11
11
  export function createIntegrationConnectCommand() {
@@ -54,7 +54,7 @@ export function createIntegrationConnectCommand() {
54
54
  params.append('auth_config_id', integration.auth_config.id);
55
55
  params.append('owner_id', options.owner);
56
56
  const credential = await client.post(`/credentials/api-key?${params.toString()}`, { tokens: { token: apiKey } });
57
- if (getOutputMode() === 'json') {
57
+ if (isMachineReadable()) {
58
58
  output.data(credential);
59
59
  }
60
60
  else {
@@ -5,7 +5,7 @@ import chalk from 'chalk';
5
5
  import { GuildAPIClient } from '../../lib/api-client.js';
6
6
  import { getAuthToken } from '../../lib/auth.js';
7
7
  import { handleAxiosError } from '../../lib/errors.js';
8
- import { getOutputMode } from '../../lib/output-mode.js';
8
+ import { isMachineReadable } from '../../lib/output-mode.js';
9
9
  import { createOutputWriter } from '../../lib/output.js';
10
10
  import { resolveOwnerId } from '../../lib/owner-helpers.js';
11
11
  import { isInteractive } from '../../lib/stdin.js';
@@ -280,7 +280,7 @@ export function createIntegrationCreateCommand() {
280
280
  body.webhook_config = webhookConfig;
281
281
  }
282
282
  const response = await client.post(`/accounts/${owner.name}/integrations`, body);
283
- if (getOutputMode() === 'json') {
283
+ if (isMachineReadable()) {
284
284
  output.data(response);
285
285
  }
286
286
  else {
@@ -5,7 +5,7 @@ import chalk from 'chalk';
5
5
  import { GuildAPIClient } from '../../lib/api-client.js';
6
6
  import { getAuthToken } from '../../lib/auth.js';
7
7
  import { handleAxiosError } from '../../lib/errors.js';
8
- import { getOutputMode } from '../../lib/output-mode.js';
8
+ import { isMachineReadable } from '../../lib/output-mode.js';
9
9
  import { createOutputWriter } from '../../lib/output.js';
10
10
  function formatDate(dateStr) {
11
11
  return new Date(dateStr).toLocaleString('en-US', {
@@ -77,7 +77,7 @@ export function createIntegrationGetCommand() {
77
77
  }
78
78
  const client = new GuildAPIClient();
79
79
  const response = await client.get(`/integrations/${identifier}`);
80
- if (getOutputMode() === 'json') {
80
+ if (isMachineReadable()) {
81
81
  output.data(response);
82
82
  }
83
83
  else {
@@ -4,7 +4,7 @@ import { Command } from 'commander';
4
4
  import { GuildAPIClient } from '../../lib/api-client.js';
5
5
  import { getAuthToken } from '../../lib/auth.js';
6
6
  import { handleAxiosError } from '../../lib/errors.js';
7
- import { getOutputMode } from '../../lib/output-mode.js';
7
+ import { isMachineReadable } from '../../lib/output-mode.js';
8
8
  import { createOutputWriter, formatIntegrationTable } from '../../lib/output.js';
9
9
  import { DEFAULT_PAGE_LIMIT } from '../../lib/api-types.js';
10
10
  const SORT_MAP = {
@@ -44,7 +44,7 @@ export function createIntegrationListCommand() {
44
44
  params.append('sort_by', sortField);
45
45
  }
46
46
  const response = await client.get(`/integrations?${params.toString()}`);
47
- if (getOutputMode() === 'json') {
47
+ if (isMachineReadable()) {
48
48
  output.data(response);
49
49
  }
50
50
  else {
@@ -6,7 +6,7 @@ import { readFileSync, existsSync } from 'fs';
6
6
  import { GuildAPIClient } from '../../../lib/api-client.js';
7
7
  import { getAuthToken } from '../../../lib/auth.js';
8
8
  import { handleAxiosError } from '../../../lib/errors.js';
9
- import { getOutputMode } from '../../../lib/output-mode.js';
9
+ import { isMachineReadable } from '../../../lib/output-mode.js';
10
10
  import { createOutputWriter } from '../../../lib/output.js';
11
11
  import { resolveVersionId } from '../../../lib/integration-helpers.js';
12
12
  import { isInteractive } from '../../../lib/stdin.js';
@@ -43,13 +43,13 @@ export function createIntegrationOperationCreateCommand() {
43
43
  }
44
44
  const content = readFileSync(options.openapi, 'utf-8');
45
45
  const response = await client.post(`/integration_versions/${versionId}/endpoint_generators`, { type: 'openapi', content });
46
- if (getOutputMode() === 'json') {
46
+ if (isMachineReadable()) {
47
47
  output.data(response);
48
48
  }
49
49
  else {
50
50
  console.log(chalk.green('OpenAPI operation generation triggered'));
51
51
  console.log();
52
- console.log("Operations will be generated asynchronously. Use 'guild integration operation list' to check results.");
52
+ console.log(`Operations will be generated asynchronously. Use 'guild integration operation list ${identifier}' to check results.`);
53
53
  }
54
54
  }
55
55
  else {
@@ -138,7 +138,7 @@ export function createIntegrationOperationCreateCommand() {
138
138
  body.output_body_type = JSON.parse(readFileSync(options.outputBodySchema, 'utf-8'));
139
139
  }
140
140
  const response = await client.post(`/integration_versions/${versionId}/endpoints`, body);
141
- if (getOutputMode() === 'json') {
141
+ if (isMachineReadable()) {
142
142
  output.data(response);
143
143
  }
144
144
  else {