@guildai/cli 0.9.0 → 0.10.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.d.ts +1 -0
  2. package/dist/commands/agent/chat.js +24 -1
  3. package/dist/commands/agent/fork.js +1 -0
  4. package/dist/commands/agent/init.js +2 -1
  5. package/dist/commands/agent/list.js +3 -2
  6. package/dist/commands/agent/pull.js +1 -1
  7. package/dist/commands/agent/save.js +4 -4
  8. package/dist/commands/agent/search.js +3 -2
  9. package/dist/commands/agent/test.d.ts +1 -0
  10. package/dist/commands/agent/test.js +92 -34
  11. package/dist/commands/agent/versions.js +3 -2
  12. package/dist/commands/agent/workspaces.js +3 -2
  13. package/dist/commands/chat.d.ts +10 -0
  14. package/dist/commands/chat.js +215 -50
  15. package/dist/commands/credentials/endpoint-list.js +3 -2
  16. package/dist/commands/credentials/list.js +3 -2
  17. package/dist/commands/credentials/policy-list.js +3 -2
  18. package/dist/commands/integration/connect.js +1 -1
  19. package/dist/commands/integration/create.js +36 -36
  20. package/dist/commands/integration/list.js +3 -2
  21. package/dist/commands/integration/operation/create.js +2 -1
  22. package/dist/commands/integration/operation/list.js +26 -17
  23. package/dist/commands/integration/version/list.js +3 -2
  24. package/dist/commands/mcp.js +1 -1
  25. package/dist/commands/session/create.js +1 -1
  26. package/dist/commands/session/events.js +19 -9
  27. package/dist/commands/session/list.js +3 -2
  28. package/dist/commands/session/send.js +1 -1
  29. package/dist/commands/session/tasks.js +3 -2
  30. package/dist/commands/trigger/list.js +3 -2
  31. package/dist/commands/trigger/sessions.js +3 -2
  32. package/dist/commands/workspace/agent/add.js +16 -3
  33. package/dist/commands/workspace/agent/list.js +17 -3
  34. package/dist/commands/workspace/agent/remove.js +14 -1
  35. package/dist/commands/workspace/clear.d.ts +3 -0
  36. package/dist/commands/workspace/clear.js +45 -0
  37. package/dist/commands/workspace/context/list.js +3 -2
  38. package/dist/commands/workspace/list.js +3 -2
  39. package/dist/commands/workspace/select.js +3 -1
  40. package/dist/index.js +53 -6
  41. package/dist/lib/api-types.d.ts +5 -0
  42. package/dist/lib/api-types.js +4 -0
  43. package/dist/lib/generated-types.d.ts +1 -1
  44. package/dist/lib/generated-types.js +1 -0
  45. package/dist/lib/guild-config.d.ts +13 -0
  46. package/dist/lib/guild-config.js +19 -0
  47. package/dist/lib/npmrc.js +6 -2
  48. package/dist/lib/output.js +4 -4
  49. package/dist/lib/owner-helpers.d.ts +3 -0
  50. package/dist/lib/owner-helpers.js +17 -10
  51. package/dist/lib/session-events.d.ts +32 -16
  52. package/dist/lib/session-events.js +22 -0
  53. package/dist/lib/session-polling.d.ts +4 -3
  54. package/dist/lib/session-polling.js +75 -17
  55. package/dist/lib/session-resume.js +4 -1
  56. package/dist/lib/stdin.d.ts +4 -0
  57. package/dist/lib/stdin.js +23 -0
  58. package/dist/lib/validate-input-schema.d.ts +19 -0
  59. package/dist/lib/validate-input-schema.js +208 -0
  60. package/dist/lib/workspace-helpers.d.ts +20 -0
  61. package/dist/lib/workspace-helpers.js +49 -0
  62. package/dist/mcp/tools.js +8 -52
  63. package/docs/skills/agent-dev.md +191 -128
  64. package/package.json +2 -1
@@ -13,12 +13,12 @@ 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, } from '../lib/session-events.js';
16
+ import { isUnfulfilledAgentInstallRequest, isFilteredTaskName, getTaskDisplayName, getAgentName, getAgentNotificationText, isDoneResponseStreamEvent, isResponseStreamEvent, isRootTaskEvent, } from '../lib/session-events.js';
17
17
  import { printResumeHint, fetchSession, fetchSessionEvents, eventsToDisplayMessages, } 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';
21
- import { getWorkspaceId } from '../lib/guild-config.js';
21
+ import { getWorkspaceId, getWorkspaceSourceLabel } from '../lib/guild-config.js';
22
22
  import { ensureInteractiveStdin } from '../lib/stdin.js';
23
23
  import { brand, BRAND_COLOR, code as codeColor, hyperlink } from '../lib/colors.js';
24
24
  import { SplashAnimation } from '../components/SplashAnimation.js';
@@ -31,6 +31,28 @@ import { getOutputMode, isQuietMode } from '../lib/output-mode.js';
31
31
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
32
32
  // Read version from package.json
33
33
  const packageJson = JSON.parse(readFileSync(path.join(__dirname, '../../package.json'), 'utf-8'));
34
+ // ---------------------------------------------------------------------------
35
+ // Workspace error types
36
+ // ---------------------------------------------------------------------------
37
+ /** Thrown when no workspace is configured (no --workspace flag, no config). */
38
+ export class WorkspaceNotConfiguredError extends Error {
39
+ constructor() {
40
+ super('No workspace configured.');
41
+ this.name = 'WorkspaceNotConfiguredError';
42
+ }
43
+ }
44
+ /** Thrown when the specified workspace ID is not found in the backend. */
45
+ export class WorkspaceNotFoundError extends Error {
46
+ workspaceId;
47
+ constructor(workspaceId) {
48
+ super(`Workspace ${workspaceId} not found.`);
49
+ this.name = 'WorkspaceNotFoundError';
50
+ this.workspaceId = workspaceId;
51
+ }
52
+ }
53
+ /** User-facing error messages for workspace resolution failures. */
54
+ const WORKSPACE_NOT_CONFIGURED_MSG = 'No workspace configured. Pass a --workspace <id_or_name> argument or run guild workspace select';
55
+ const WORKSPACE_NOT_FOUND_MSG = "The workspace doesn't exist.";
34
56
  // Configure marked for terminal
35
57
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
36
58
  marked.use(markedTerminal({}, { theme: {} }));
@@ -48,6 +70,48 @@ function fixListItemMarkdown(text) {
48
70
  text = text.replace(/(?<![\\w])_([^_]+)_(?![\\w])/g, (_, content) => chalk.italic(content));
49
71
  return text;
50
72
  }
73
+ function extractMessageText(text) {
74
+ const trimmed = text.trim();
75
+ if (!trimmed.startsWith('{') || !trimmed.endsWith('}'))
76
+ return text;
77
+ try {
78
+ const parsed = JSON.parse(trimmed);
79
+ if (typeof parsed === 'object' && parsed !== null && 'message' in parsed) {
80
+ return typeof parsed.message === 'string' ? parsed.message : text;
81
+ }
82
+ }
83
+ catch {
84
+ // If parsing fails, use the content as-is.
85
+ }
86
+ return text;
87
+ }
88
+ function extractRuntimeDoneText(content) {
89
+ if (content === null || content === undefined)
90
+ return null;
91
+ if (typeof content === 'string')
92
+ return content.trim() ? content : null;
93
+ if (typeof content !== 'object' || Array.isArray(content)) {
94
+ return JSON.stringify(content);
95
+ }
96
+ const record = content;
97
+ if (Object.keys(record).length === 0)
98
+ return null;
99
+ if (record.type === 'text') {
100
+ if (typeof record.text === 'string')
101
+ return record.text;
102
+ if (typeof record.data === 'string')
103
+ return record.data;
104
+ }
105
+ if (typeof record.message === 'string')
106
+ return record.message;
107
+ // runtime_done content is agent output, not notification content. Preserve
108
+ // structured outputs rather than trying to render notification-only shapes.
109
+ return JSON.stringify(content);
110
+ }
111
+ function renderAssistantMessage(text, taskName) {
112
+ const rendered = fixListItemMarkdown(marked.parse(text));
113
+ return `${chalk.green('●')} ${chalk.bold(taskName)}\n${rendered.trim()}`;
114
+ }
51
115
  /**
52
116
  * Output the result of a --once mode session.
53
117
  * Handles both JSON and human-readable output formats.
@@ -57,21 +121,29 @@ async function outputOnceResult(sessionId, events, mode) {
57
121
  console.log(JSON.stringify({ session_id: sessionId, events }, null, 2));
58
122
  }
59
123
  else {
60
- const agentMessages = events.filter((e) => e.type === 'agent_notification_message');
61
- if (agentMessages.length > 0) {
62
- let messageContent = agentMessages[agentMessages.length - 1].content.data;
63
- const trimmed = messageContent.trim();
64
- if (trimmed.startsWith('{') && trimmed.endsWith('}')) {
65
- try {
66
- const parsed = JSON.parse(trimmed);
67
- if (typeof parsed === 'object' && parsed !== null && 'message' in parsed) {
68
- messageContent = parsed.message;
69
- }
70
- }
71
- catch {
72
- // If parsing fails, use content as-is
73
- }
124
+ const finalAgentMessages = events.filter((e) => e.type === 'agent_notification_message' &&
125
+ isRootTaskEvent(e) &&
126
+ !isResponseStreamEvent(e));
127
+ const streamFallbacks = events.filter((e) => e.type === 'agent_notification_message' &&
128
+ isRootTaskEvent(e) &&
129
+ isDoneResponseStreamEvent(e));
130
+ if (finalAgentMessages.length > 0) {
131
+ const messageContent = extractMessageText(getAgentNotificationText(finalAgentMessages[finalAgentMessages.length - 1]));
132
+ const rendered = fixListItemMarkdown(await marked(messageContent));
133
+ console.log(rendered.trim());
134
+ return;
135
+ }
136
+ const runtimeDoneEvents = events.filter((e) => e.type === 'runtime_done' && isRootTaskEvent(e) && e.content);
137
+ const runtimeDoneWithContent = runtimeDoneEvents[runtimeDoneEvents.length - 1];
138
+ if (runtimeDoneWithContent?.type === 'runtime_done') {
139
+ const runtimeDoneText = extractRuntimeDoneText(runtimeDoneWithContent.content);
140
+ if (runtimeDoneText) {
141
+ console.log(runtimeDoneText);
142
+ return;
74
143
  }
144
+ }
145
+ if (streamFallbacks.length > 0) {
146
+ const messageContent = extractMessageText(getAgentNotificationText(streamFallbacks[streamFallbacks.length - 1]));
75
147
  const rendered = fixListItemMarkdown(await marked(messageContent));
76
148
  console.log(rendered.trim());
77
149
  }
@@ -214,8 +286,11 @@ export function ChatApp({ initialPrompt, version, workspaceId, versionId, agentN
214
286
  formattedError.code === ErrorCodes.AUTH_TOKEN_INVALID) {
215
287
  format.error('Not authenticated. Run: guild auth login');
216
288
  }
217
- else if (details.includes('workspace')) {
218
- format.error('Workspace not found. Run: guild workspace select');
289
+ else if (error instanceof WorkspaceNotConfiguredError) {
290
+ format.error(WORKSPACE_NOT_CONFIGURED_MSG);
291
+ }
292
+ else if (error instanceof WorkspaceNotFoundError) {
293
+ format.error(WORKSPACE_NOT_FOUND_MSG);
219
294
  }
220
295
  else if (details.includes('agent')) {
221
296
  format.error('Agent not found in workspace.');
@@ -427,6 +502,83 @@ function ChatUIWithConnection({ initialPrompt, version: _version, versionId: _ve
427
502
  const isPolling = useRef(false);
428
503
  const receivedResponseSinceLastInput = useRef(false);
429
504
  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) => {
509
+ if (!taskId)
510
+ return;
511
+ const keys = responseStreamKeysByTask.current.get(taskId);
512
+ if (!keys?.size)
513
+ return;
514
+ for (const [streamId, key] of responseStreamKeys.current.entries()) {
515
+ if (keys.has(key)) {
516
+ responseStreamKeys.current.delete(streamId);
517
+ responseStreamTimestamps.current.delete(streamId);
518
+ }
519
+ }
520
+ responseStreamKeysByTask.current.delete(taskId);
521
+ setMessages((prev) => prev.filter((message) => !keys.has(message.key)));
522
+ };
523
+ const upsertResponseStreamMessage = (event) => {
524
+ if (!isResponseStreamEvent(event))
525
+ return;
526
+ if (!isRootTaskEvent(event))
527
+ return;
528
+ const streamId = event.content.stream_id;
529
+ const taskId = event.task?.id;
530
+ const existingKey = responseStreamKeys.current.get(streamId);
531
+ if (event.content.status === 'aborted') {
532
+ if (existingKey) {
533
+ responseStreamKeys.current.delete(streamId);
534
+ responseStreamTimestamps.current.delete(streamId);
535
+ if (taskId) {
536
+ const keys = responseStreamKeysByTask.current.get(taskId);
537
+ keys?.delete(existingKey);
538
+ if (keys?.size === 0)
539
+ responseStreamKeysByTask.current.delete(taskId);
540
+ }
541
+ setMessages((prev) => prev.filter((message) => message.key !== existingKey));
542
+ }
543
+ return;
544
+ }
545
+ const text = event.content.text;
546
+ if (!text.trim())
547
+ return;
548
+ const key = existingKey ?? `response-stream-${streamId}`;
549
+ responseStreamKeys.current.set(streamId, key);
550
+ if (taskId) {
551
+ const keys = responseStreamKeysByTask.current.get(taskId) ?? new Set();
552
+ keys.add(key);
553
+ responseStreamKeysByTask.current.set(taskId, keys);
554
+ }
555
+ const taskName = agentName || 'assistant';
556
+ const messageContent = renderAssistantMessage(text, taskName);
557
+ const timestamp = responseStreamTimestamps.current.get(streamId) ?? new Date().toLocaleTimeString();
558
+ responseStreamTimestamps.current.set(streamId, timestamp);
559
+ setMessages((prev) => {
560
+ const index = prev.findIndex((message) => message.key === key);
561
+ const message = {
562
+ key,
563
+ content: messageContent,
564
+ type: 'assistant',
565
+ timestamp,
566
+ };
567
+ if (index === -1)
568
+ return [...prev, message];
569
+ const next = [...prev];
570
+ next[index] = message;
571
+ return next;
572
+ });
573
+ if (!firstMessageNotified.current && onFirstMessage) {
574
+ firstMessageNotified.current = true;
575
+ onFirstMessage();
576
+ }
577
+ if (event.content.status === 'done') {
578
+ receivedResponseSinceLastInput.current = true;
579
+ setCurrentOperation('');
580
+ }
581
+ };
430
582
  // Mark initial prompt as sent (skip for resume — we already have the events)
431
583
  useEffect(() => {
432
584
  if (!resumeEvents) {
@@ -526,6 +678,7 @@ function ChatUIWithConnection({ initialPrompt, version: _version, versionId: _ve
526
678
  }
527
679
  // Process events that affect the chat UI (task state comes from tasks poll)
528
680
  if (event.type === 'runtime_error') {
681
+ clearResponseStreamsForTask(taskInfo?.id);
529
682
  // Always clear the spinner on runtime errors so the UI doesn't get stuck
530
683
  setCurrentOperation('');
531
684
  // Show runtime errors in the chat (gated on --events filter)
@@ -639,32 +792,15 @@ function ChatUIWithConnection({ initialPrompt, version: _version, versionId: _ve
639
792
  }
640
793
  }
641
794
  else if (event.type === 'agent_notification_message') {
642
- let text = typeof event.content === 'string'
643
- ? event.content
644
- : event.content?.data || '';
645
- // Extract message content, handling JSON responses with "message" field
646
- const trimmed = text.trim();
647
- if (trimmed) {
648
- // Check if content looks like JSON object with "message" field
649
- if (trimmed.startsWith('{') && trimmed.endsWith('}')) {
650
- try {
651
- const parsed = JSON.parse(trimmed);
652
- // If JSON has a "message" field, extract it (some agents wrap responses this way)
653
- if (typeof parsed === 'object' &&
654
- parsed !== null &&
655
- 'message' in parsed) {
656
- text = parsed.message;
657
- }
658
- // Otherwise use full JSON content as-is (legitimate JSON responses)
659
- }
660
- catch {
661
- // If parsing fails, use the content as-is
662
- }
663
- }
664
- const rendered = fixListItemMarkdown(marked.parse(text));
665
- // Add attribution with task name
795
+ if (isResponseStreamEvent(event)) {
796
+ upsertResponseStreamMessage(event);
797
+ continue;
798
+ }
799
+ const text = extractMessageText(getAgentNotificationText(event));
800
+ if (text.trim()) {
801
+ clearResponseStreamsForTask(taskInfo?.id);
666
802
  const taskName = agentName || 'assistant';
667
- const messageContent = `${chalk.green('●')} ${chalk.bold(taskName)}\n${rendered.trim()}`;
803
+ const messageContent = renderAssistantMessage(text, taskName);
668
804
  setMessages((prev) => [
669
805
  ...prev,
670
806
  {
@@ -684,6 +820,7 @@ function ChatUIWithConnection({ initialPrompt, version: _version, versionId: _ve
684
820
  setCurrentOperation('');
685
821
  }
686
822
  else if (event.type === 'agent_notification_error') {
823
+ clearResponseStreamsForTask(taskInfo?.id);
687
824
  // Show error in chat (task status is updated via tasks poll)
688
825
  const errorText = typeof event.content === 'string'
689
826
  ? event.content
@@ -730,6 +867,7 @@ function ChatUIWithConnection({ initialPrompt, version: _version, versionId: _ve
730
867
  event.content !== undefined &&
731
868
  taskInfo &&
732
869
  'agent' in taskInfo) {
870
+ clearResponseStreamsForTask(taskInfo.id);
733
871
  // One-shot agents may complete with runtime_done without sending
734
872
  // agent_notification_message. Display the output if we haven't
735
873
  // already shown a response for this input cycle.
@@ -759,6 +897,7 @@ function ChatUIWithConnection({ initialPrompt, version: _version, versionId: _ve
759
897
  }
760
898
  }
761
899
  else if (event.type === 'interrupted') {
900
+ clearResponseStreamsForTask(taskInfo?.id);
762
901
  // Session was interrupted — interrupted sessions are terminal on the backend
763
902
  setMessages((prev) => [
764
903
  ...prev,
@@ -987,10 +1126,14 @@ export async function createSession(client, workspaceId, initialPrompt, versionI
987
1126
  const resolved = await getWorkspaceId();
988
1127
  if (resolved) {
989
1128
  workspaceId = resolved.workspaceId;
1129
+ const sourceLabel = getWorkspaceSourceLabel(resolved.source);
1130
+ if (sourceLabel) {
1131
+ progress(`Using workspace from ${sourceLabel}`);
1132
+ }
990
1133
  }
991
1134
  }
992
1135
  if (!workspaceId) {
993
- throw new Error('No workspace configured. Run: guild workspace select, or pass --workspace <id>');
1136
+ throw new WorkspaceNotConfiguredError();
994
1137
  }
995
1138
  progress('Creating session');
996
1139
  const sessionData = {
@@ -1009,7 +1152,7 @@ export async function createSession(client, workspaceId, initialPrompt, versionI
1009
1152
  catch (error) {
1010
1153
  const err = handleAxiosError(error);
1011
1154
  if (err.code === ErrorCodes.NOT_FOUND) {
1012
- throw new Error(`Workspace ${workspaceId} not found. Run: guild workspace select`);
1155
+ throw new WorkspaceNotFoundError(workspaceId);
1013
1156
  }
1014
1157
  throw error;
1015
1158
  }
@@ -1045,7 +1188,9 @@ export function createChatCommand() {
1045
1188
  try {
1046
1189
  await ensureAuthenticated();
1047
1190
  const client = new GuildAPIClient();
1048
- const session = await createSession(client, options.workspace, initialPrompt, options.agent);
1191
+ const session = await createSession(client, options.workspace, initialPrompt, options.agent, (status) => {
1192
+ spinner.text = status;
1193
+ });
1049
1194
  spinner.succeed('Connected');
1050
1195
  if (session.session_url) {
1051
1196
  const sessionLink = hyperlink(session.id, session.session_url);
@@ -1084,12 +1229,18 @@ export function createChatCommand() {
1084
1229
  else {
1085
1230
  inactivityCounter++;
1086
1231
  }
1087
- const isRootTask = (e) => !e.task?.parent_task;
1088
- const hasRootTaskDone = allEvents.some((e) => e.type === 'runtime_done' && isRootTask(e));
1089
- const hasAgentMessage = allEvents.some((e) => e.type === 'agent_notification_message' && isRootTask(e));
1090
- const hasRootTaskError = allEvents.some((e) => e.type === 'runtime_error' && isRootTask(e));
1232
+ // Check if we got a completion response from the root agent.
1233
+ // Stream done events are only a rendering fallback; wait for the
1234
+ // final root message or runtime completion so child drafts cannot
1235
+ // terminate --once early.
1236
+ const hasRootTaskDone = allEvents.some((e) => e.type === 'runtime_done' && isRootTaskEvent(e));
1237
+ const hasAgentMessage = allEvents.some((e) => e.type === 'agent_notification_message' &&
1238
+ isRootTaskEvent(e) &&
1239
+ !isResponseStreamEvent(e));
1240
+ const hasRootTaskError = allEvents.some((e) => e.type === 'runtime_error' && isRootTaskEvent(e));
1091
1241
  // Check for a ui_prompt request... that ends the game.
1092
1242
  const hasUIPromptMessage = allEvents.some((e) => e.type === 'agent_notification_message' &&
1243
+ !isResponseStreamEvent(e) &&
1093
1244
  e.task?.tool_name === 'ui_prompt');
1094
1245
  if (hasRootTaskError) {
1095
1246
  debug('Found error event from root agent, exiting --once mode');
@@ -1130,6 +1281,14 @@ export function createChatCommand() {
1130
1281
  catch (error) {
1131
1282
  spinner.fail('Connection failed');
1132
1283
  console.error('');
1284
+ if (error instanceof WorkspaceNotConfiguredError) {
1285
+ format.error(WORKSPACE_NOT_CONFIGURED_MSG);
1286
+ process.exit(1);
1287
+ }
1288
+ if (error instanceof WorkspaceNotFoundError) {
1289
+ format.error(WORKSPACE_NOT_FOUND_MSG);
1290
+ process.exit(1);
1291
+ }
1133
1292
  const formattedError = handleAxiosError(error);
1134
1293
  console.error(`Error: ${formattedError.error}`);
1135
1294
  console.error(formattedError.details);
@@ -1177,4 +1336,10 @@ export function createChatCommand() {
1177
1336
  });
1178
1337
  return cmd;
1179
1338
  }
1339
+ // Thin wrapper for lazy-loading from index.ts (avoids importing React at startup)
1340
+ export async function handleChatAction(_promptArgs, _options) {
1341
+ const cmd = createChatCommand();
1342
+ // Re-parse the original argv so the command's own action handler runs
1343
+ await cmd.parseAsync(process.argv.slice(2), { from: 'user' });
1344
+ }
1180
1345
  //# sourceMappingURL=chat.js.map
@@ -7,6 +7,7 @@ import { getAuthToken } from '../../lib/auth.js';
7
7
  import { handleAxiosError } from '../../lib/errors.js';
8
8
  import { getOutputMode } from '../../lib/output-mode.js';
9
9
  import { createOutputWriter } from '../../lib/output.js';
10
+ import { DEFAULT_PAGE_LIMIT } from '../../lib/api-types.js';
10
11
  import { Table } from '../../lib/table.js';
11
12
  export function createCredentialsEndpointListCommand() {
12
13
  const cmd = new Command('list');
@@ -15,8 +16,8 @@ export function createCredentialsEndpointListCommand() {
15
16
  .argument('<credential-id>', 'Credential ID')
16
17
  .option('--include-previous-versions', 'Include endpoints from previous versions')
17
18
  .option('--search <query>', 'Search by operation name')
18
- .option('--limit <number>', 'Number of results to return', '20')
19
- .option('--offset <number>', 'Offset for pagination', '0')
19
+ .option('--limit <number>', `Number of results to return (default: ${DEFAULT_PAGE_LIMIT})`, String(DEFAULT_PAGE_LIMIT))
20
+ .option('--offset <number>', 'Offset for pagination (default: 0)', '0')
20
21
  .action(async (credentialId, options) => {
21
22
  const output = createOutputWriter();
22
23
  try {
@@ -6,14 +6,15 @@ import { getAuthToken } from '../../lib/auth.js';
6
6
  import { handleAxiosError } from '../../lib/errors.js';
7
7
  import { getOutputMode } from '../../lib/output-mode.js';
8
8
  import { createOutputWriter, formatCredentialsTable } from '../../lib/output.js';
9
+ import { DEFAULT_PAGE_LIMIT } from '../../lib/api-types.js';
9
10
  export function createCredentialsListCommand() {
10
11
  const cmd = new Command('list');
11
12
  cmd
12
13
  .description('List credentials for an account')
13
14
  .requiredOption('--owner <account>', 'Account name or ID')
14
15
  .option('--search <query>', 'Filter by integration name')
15
- .option('--limit <number>', 'Number of results to return', '20')
16
- .option('--offset <number>', 'Offset for pagination', '0')
16
+ .option('--limit <number>', `Number of results to return (default: ${DEFAULT_PAGE_LIMIT})`, String(DEFAULT_PAGE_LIMIT))
17
+ .option('--offset <number>', 'Offset for pagination (default: 0)', '0')
17
18
  .action(async (options) => {
18
19
  const output = createOutputWriter();
19
20
  try {
@@ -6,13 +6,14 @@ import { getAuthToken } from '../../lib/auth.js';
6
6
  import { handleAxiosError } from '../../lib/errors.js';
7
7
  import { getOutputMode } from '../../lib/output-mode.js';
8
8
  import { createOutputWriter, formatPoliciesTable } from '../../lib/output.js';
9
+ import { DEFAULT_PAGE_LIMIT } from '../../lib/api-types.js';
9
10
  export function createCredentialsPolicyListCommand() {
10
11
  const cmd = new Command('list');
11
12
  cmd
12
13
  .description('List policies for a credential')
13
14
  .argument('<credential-id>', 'Credential ID')
14
- .option('--limit <number>', 'Number of results to return', '20')
15
- .option('--offset <number>', 'Offset for pagination', '0')
15
+ .option('--limit <number>', `Number of results to return (default: ${DEFAULT_PAGE_LIMIT})`, String(DEFAULT_PAGE_LIMIT))
16
+ .option('--offset <number>', 'Offset for pagination (default: 0)', '0')
16
17
  .action(async (credentialId, options) => {
17
18
  const output = createOutputWriter();
18
19
  try {
@@ -2,7 +2,6 @@
2
2
  // SPDX-License-Identifier: Apache-2.0
3
3
  import { Command } from 'commander';
4
4
  import chalk from 'chalk';
5
- import inquirer from 'inquirer';
6
5
  import { GuildAPIClient } from '../../lib/api-client.js';
7
6
  import { getAuthToken } from '../../lib/auth.js';
8
7
  import { handleAxiosError } from '../../lib/errors.js';
@@ -40,6 +39,7 @@ export function createIntegrationConnectCommand() {
40
39
  }
41
40
  let apiKey = options.token;
42
41
  if (!apiKey) {
42
+ const { default: inquirer } = await import('inquirer');
43
43
  const answer = await inquirer.prompt([
44
44
  {
45
45
  type: 'password',
@@ -2,7 +2,6 @@
2
2
  // SPDX-License-Identifier: Apache-2.0
3
3
  import { Command } from 'commander';
4
4
  import chalk from 'chalk';
5
- import inquirer from 'inquirer';
6
5
  import { GuildAPIClient } from '../../lib/api-client.js';
7
6
  import { getAuthToken } from '../../lib/auth.js';
8
7
  import { handleAxiosError } from '../../lib/errors.js';
@@ -66,47 +65,44 @@ export function createIntegrationCreateCommand() {
66
65
  });
67
66
  // Resolve description
68
67
  let description = options.description;
69
- if (!description) {
70
- if (interactive) {
71
- const answer = await inquirer.prompt([
72
- { type: 'input', name: 'description', message: 'Description:' },
73
- ]);
74
- description = answer.description;
75
- }
68
+ if (!description && interactive) {
69
+ const { default: inquirer } = await import('inquirer');
70
+ const answer = await inquirer.prompt([
71
+ { type: 'input', name: 'description', message: 'Description:' },
72
+ ]);
73
+ description = answer.description;
76
74
  }
77
75
  // Resolve base URL
78
76
  let baseUrl = options.baseUrl;
79
- if (!baseUrl) {
80
- if (interactive) {
81
- const answer = await inquirer.prompt([
82
- { type: 'input', name: 'baseUrl', message: 'Base URL:' },
83
- ]);
84
- baseUrl = answer.baseUrl;
85
- }
77
+ if (!baseUrl && interactive) {
78
+ const { default: inquirer } = await import('inquirer');
79
+ const answer = await inquirer.prompt([
80
+ { type: 'input', name: 'baseUrl', message: 'Base URL:' },
81
+ ]);
82
+ baseUrl = answer.baseUrl;
86
83
  }
87
84
  // Resolve auth scheme
88
85
  let authScheme = options.authScheme;
89
- if (!authScheme) {
90
- if (interactive) {
91
- const answer = await inquirer.prompt([
92
- {
93
- type: 'list',
94
- name: 'authScheme',
95
- message: 'Select authentication scheme:',
96
- choices: [
97
- {
98
- name: 'API Key - Static token sent in request headers',
99
- value: 'api-key',
100
- },
101
- {
102
- name: 'OAuth 2.0 - User grants access via authorization flow',
103
- value: 'oauth',
104
- },
105
- ],
106
- },
107
- ]);
108
- authScheme = answer.authScheme;
109
- }
86
+ if (!authScheme && interactive) {
87
+ const { default: inquirer } = await import('inquirer');
88
+ const answer = await inquirer.prompt([
89
+ {
90
+ type: 'list',
91
+ name: 'authScheme',
92
+ message: 'Select authentication scheme:',
93
+ choices: [
94
+ {
95
+ name: 'API Key - Static token sent in request headers',
96
+ value: 'api-key',
97
+ },
98
+ {
99
+ name: 'OAuth 2.0 - User grants access via authorization flow',
100
+ value: 'oauth',
101
+ },
102
+ ],
103
+ },
104
+ ]);
105
+ authScheme = answer.authScheme;
110
106
  }
111
107
  // Validate required fields in non-interactive mode
112
108
  const missing = [];
@@ -126,6 +122,7 @@ export function createIntegrationCreateCommand() {
126
122
  if (normalizedScheme === 'api-key') {
127
123
  let headerTemplate = options.headerTemplate || 'X-API-Key: {token}';
128
124
  if (interactive && !options.headerTemplate) {
125
+ const { default: inquirer } = await import('inquirer');
129
126
  const answer = await inquirer.prompt([
130
127
  {
131
128
  type: 'input',
@@ -151,6 +148,7 @@ export function createIntegrationCreateCommand() {
151
148
  let clientId = options.clientId;
152
149
  let clientSecret = options.clientSecret;
153
150
  if (interactive) {
151
+ const { default: inquirer } = await import('inquirer');
154
152
  if (!installUrl) {
155
153
  const a = await inquirer.prompt([
156
154
  { type: 'input', name: 'val', message: 'Install URL:' },
@@ -205,6 +203,7 @@ export function createIntegrationCreateCommand() {
205
203
  authConfig.scopes = options.scopes.split(',').map((s) => s.trim());
206
204
  }
207
205
  else if (interactive) {
206
+ const { default: inquirer } = await import('inquirer');
208
207
  const answer = await inquirer.prompt([
209
208
  {
210
209
  type: 'input',
@@ -237,6 +236,7 @@ export function createIntegrationCreateCommand() {
237
236
  }
238
237
  }
239
238
  else if (interactive) {
239
+ const { default: inquirer } = await import('inquirer');
240
240
  const answer = await inquirer.prompt([
241
241
  {
242
242
  type: 'confirm',
@@ -6,6 +6,7 @@ import { getAuthToken } from '../../lib/auth.js';
6
6
  import { handleAxiosError } from '../../lib/errors.js';
7
7
  import { getOutputMode } from '../../lib/output-mode.js';
8
8
  import { createOutputWriter, formatIntegrationTable } from '../../lib/output.js';
9
+ import { DEFAULT_PAGE_LIMIT } from '../../lib/api-types.js';
9
10
  const SORT_MAP = {
10
11
  updated: 'updated_at',
11
12
  newest: 'created_at',
@@ -18,8 +19,8 @@ export function createIntegrationListCommand() {
18
19
  .option('--search <query>', 'Search integrations by name or description')
19
20
  .option('--sort <field>', 'Sort by: updated, newest, name (default: updated)', 'updated')
20
21
  .option('--published', 'Only show integrations with at least one published version')
21
- .option('--limit <number>', 'Number of results to return', '20')
22
- .option('--offset <number>', 'Offset for pagination', '0')
22
+ .option('--limit <number>', `Number of results to return (default: ${DEFAULT_PAGE_LIMIT})`, String(DEFAULT_PAGE_LIMIT))
23
+ .option('--offset <number>', 'Offset for pagination (default: 0)', '0')
23
24
  .action(async (options) => {
24
25
  const output = createOutputWriter();
25
26
  try {
@@ -2,7 +2,6 @@
2
2
  // SPDX-License-Identifier: Apache-2.0
3
3
  import { Command } from 'commander';
4
4
  import chalk from 'chalk';
5
- import inquirer from 'inquirer';
6
5
  import { readFileSync, existsSync } from 'fs';
7
6
  import { GuildAPIClient } from '../../../lib/api-client.js';
8
7
  import { getAuthToken } from '../../../lib/auth.js';
@@ -59,6 +58,7 @@ export function createIntegrationOperationCreateCommand() {
59
58
  let method = options.method;
60
59
  let operationPath = options.path;
61
60
  if (isInteractive()) {
61
+ const { default: inquirer } = await import('inquirer');
62
62
  if (!operation) {
63
63
  const a = await inquirer.prompt([
64
64
  {
@@ -110,6 +110,7 @@ export function createIntegrationOperationCreateCommand() {
110
110
  };
111
111
  let summary = options.summary;
112
112
  if (!summary && isInteractive()) {
113
+ const { default: inquirer } = await import('inquirer');
113
114
  const a = await inquirer.prompt([
114
115
  { type: 'input', name: 'val', message: 'Summary (optional):' },
115
116
  ]);