@guildai/cli 0.6.1 → 0.7.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.
@@ -15,6 +15,7 @@ import path from 'path';
15
15
  import { fileURLToPath } from 'url';
16
16
  import { isUnfulfilledAgentInstallRequest, isFilteredTaskName, getTaskDisplayName, matchesAgent, getAgentName, } from '../lib/session-events.js';
17
17
  import { printResumeHint, fetchSession, fetchSessionEvents, eventsToDisplayMessages, } from '../lib/session-resume.js';
18
+ import { DEFAULT_EVENT_TYPES, parseEventFilter, shouldShowEvent, } from '../lib/event-filter.js';
18
19
  import { fetchEvents, fetchTasks } from '../lib/session-events-fetch.js';
19
20
  import { AgentInstallPrompt } from '../components/AgentInstallPrompt.js';
20
21
  import { getWorkspaceId } from '../lib/guild-config.js';
@@ -117,7 +118,7 @@ function InputWrapper({ isReady, isInterrupted, input, setInput, handleSubmit, t
117
118
  React.createElement(Text, { color: isReady ? BRAND_COLOR : 'gray' }, "> "),
118
119
  isReady && isActive ? (React.createElement(CustomInput, { value: input, onChange: setInput, onSubmit: handleSubmit, trackedTasksSize: trackedTasksSize, setShowTaskPanel: setShowTaskPanel, isActive: isActive })) : isReady ? (React.createElement(Text, null, input)) : (React.createElement(Text, null, chalk.dim('(connecting...)')))));
119
120
  }
120
- export function ChatApp({ initialPrompt, version, workspaceId, versionId, agentName, showSplash = true, resumeSession, resumeEvents, resumeCommand, openDashboard, }) {
121
+ export function ChatApp({ initialPrompt, version, workspaceId, versionId, agentName, showSplash = true, resumeSession, resumeEvents, resumeCommand, openDashboard, eventFilter, }) {
121
122
  const { exit } = useApp();
122
123
  const isResuming = !!resumeSession;
123
124
  const [phase, setPhase] = useState(isResuming || !showSplash ? 'chat' : 'splash');
@@ -241,13 +242,14 @@ export function ChatApp({ initialPrompt, version, workspaceId, versionId, agentN
241
242
  }
242
243
  // If not connected yet, ignore escape (let connection complete)
243
244
  } })),
244
- phase === 'chat' && (React.createElement(ChatUIWithConnection, { initialPrompt: initialPrompt, version: version, versionId: versionId, agentName: agentName, client: connectedClient, session: connectedSession, onFirstMessage: () => setFirstMessageReceived(true), resumeEvents: resumeEvents, resumeCommand: resumeCommand })),
245
+ 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 })),
245
246
  (phase === 'splash' || phase === 'finalizing') &&
246
247
  connectedSession &&
247
248
  connectedClient && (React.createElement(Box, { display: "none" },
248
- React.createElement(ChatUIWithConnection, { initialPrompt: initialPrompt, version: version, versionId: versionId, agentName: agentName, client: connectedClient, session: connectedSession, onFirstMessage: () => setFirstMessageReceived(true), isActive: false })))));
249
+ React.createElement(ChatUIWithConnection, { initialPrompt: initialPrompt, version: version, versionId: versionId, agentName: agentName, client: connectedClient, session: connectedSession, onFirstMessage: () => setFirstMessageReceived(true), isActive: false, eventFilter: eventFilter })))));
249
250
  }
250
- function ChatUIWithConnection({ initialPrompt, version: _version, versionId: _versionId, agentName, client: preConnectedClient, session: preConnectedSession, onFirstMessage, isActive = true, resumeEvents, resumeCommand, }) {
251
+ function ChatUIWithConnection({ initialPrompt, version: _version, versionId: _versionId, agentName, client: preConnectedClient, session: preConnectedSession, onFirstMessage, isActive = true, resumeEvents, resumeCommand, eventFilter, }) {
252
+ const activeFilter = eventFilter ?? DEFAULT_EVENT_TYPES;
251
253
  // Note: We handle SIGINT directly via process.on, not using useApp().exit
252
254
  // Task panel state - managed at this level to handle keyboard input before TextInput
253
255
  // Default to showing task panel (Ctrl-T to toggle)
@@ -485,9 +487,9 @@ function ChatUIWithConnection({ initialPrompt, version: _version, versionId: _ve
485
487
  isPollingTasks.current = false;
486
488
  }
487
489
  };
488
- // Poll immediately and then every second (like web)
490
+ // Poll immediately and then every 2 seconds
489
491
  pollTasks();
490
- const interval = setInterval(pollTasks, 1000);
492
+ const interval = setInterval(pollTasks, 2000);
491
493
  return () => {
492
494
  clearInterval(interval);
493
495
  };
@@ -525,18 +527,107 @@ function ChatUIWithConnection({ initialPrompt, version: _version, versionId: _ve
525
527
  }
526
528
  // Process events that affect the chat UI (task state comes from tasks poll)
527
529
  if (event.type === 'runtime_error') {
528
- // Show runtime errors in the chat
529
- const errorText = typeof event.content === 'string' ? event.content : 'Unknown error';
530
- const taskName = agentName || 'assistant';
531
- setMessages((prev) => [
532
- ...prev,
533
- {
534
- key: `error-${Date.now()}`,
535
- content: `${chalk.red('●')} ${chalk.bold(taskName)}\n${chalk.red(`Error: ${errorText}`)}`,
536
- type: 'assistant',
537
- },
538
- ]);
530
+ // Always clear the spinner on runtime errors so the UI doesn't get stuck
539
531
  setCurrentOperation('');
532
+ // Show runtime errors in the chat (gated on --events filter)
533
+ if (shouldShowEvent('runtime_error', activeFilter)) {
534
+ const errorText = typeof event.content === 'string' ? event.content : 'Unknown error';
535
+ const taskName = agentName || 'assistant';
536
+ setMessages((prev) => [
537
+ ...prev,
538
+ {
539
+ key: `error-${Date.now()}`,
540
+ content: `${chalk.red('●')} ${chalk.bold(taskName)}\n${chalk.red(`Error: ${errorText}`)}`,
541
+ type: 'assistant',
542
+ },
543
+ ]);
544
+ }
545
+ }
546
+ else if (event.type === 'runtime_start') {
547
+ if (shouldShowEvent('runtime_start', activeFilter)) {
548
+ setMessages((prev) => [
549
+ ...prev,
550
+ {
551
+ key: `runtime-start-${Date.now()}`,
552
+ content: chalk.dim('[runtime/start]'),
553
+ type: 'assistant',
554
+ },
555
+ ]);
556
+ }
557
+ }
558
+ else if (event.type === 'runtime_running') {
559
+ if (shouldShowEvent('runtime_running', activeFilter)) {
560
+ setMessages((prev) => [
561
+ ...prev,
562
+ {
563
+ key: `runtime-running-${Date.now()}`,
564
+ content: chalk.dim('[runtime/running]'),
565
+ type: 'assistant',
566
+ },
567
+ ]);
568
+ }
569
+ }
570
+ else if (event.type === 'runtime_waiting') {
571
+ if (shouldShowEvent('runtime_waiting', activeFilter)) {
572
+ setMessages((prev) => [
573
+ ...prev,
574
+ {
575
+ key: `runtime-waiting-${Date.now()}`,
576
+ content: chalk.dim('[runtime/waiting]'),
577
+ type: 'assistant',
578
+ },
579
+ ]);
580
+ }
581
+ }
582
+ else if (event.type === 'trigger_message') {
583
+ if (shouldShowEvent('trigger_message', activeFilter)) {
584
+ const triggerText = typeof event.content === 'object' ? event.content?.data || '' : '';
585
+ setMessages((prev) => [
586
+ ...prev,
587
+ {
588
+ key: `trigger-${Date.now()}`,
589
+ content: `${chalk.cyan('[trigger]')} ${triggerText}`,
590
+ type: 'assistant',
591
+ },
592
+ ]);
593
+ }
594
+ }
595
+ else if (event.type === 'system_error') {
596
+ if (shouldShowEvent('system_error', activeFilter)) {
597
+ const errText = typeof event.content === 'object' ? event.content?.data || '' : '';
598
+ setMessages((prev) => [
599
+ ...prev,
600
+ {
601
+ key: `system-error-${Date.now()}`,
602
+ content: `${chalk.red('[system_error]')} ${errText}`,
603
+ type: 'assistant',
604
+ },
605
+ ]);
606
+ }
607
+ }
608
+ else if (event.type === 'llm_start') {
609
+ if (shouldShowEvent('llm_start', activeFilter)) {
610
+ setMessages((prev) => [
611
+ ...prev,
612
+ {
613
+ key: `llm-start-${Date.now()}`,
614
+ content: chalk.dim(`[llm_start] provider:${event.provider}`),
615
+ type: 'assistant',
616
+ },
617
+ ]);
618
+ }
619
+ }
620
+ else if (event.type === 'llm_done') {
621
+ if (shouldShowEvent('llm_done', activeFilter)) {
622
+ setMessages((prev) => [
623
+ ...prev,
624
+ {
625
+ key: `llm-done-${Date.now()}`,
626
+ content: chalk.dim(`[llm_done] HTTP ${event.status_code}`),
627
+ type: 'assistant',
628
+ },
629
+ ]);
630
+ }
540
631
  }
541
632
  else if (event.type === 'agent_notification_progress') {
542
633
  // Update status line with progress text (task tracking is done by tasks poll)
@@ -609,48 +700,63 @@ function ChatUIWithConnection({ initialPrompt, version: _version, versionId: _ve
609
700
  ]);
610
701
  setCurrentOperation('');
611
702
  }
612
- else if (event.type === 'agent_console' && isDebugMode()) {
613
- // Show console logs when debug mode is enabled (matches www debug checkbox behavior)
614
- const content = typeof event.content === 'string' ? event.content : '';
615
- setMessages((prev) => [
616
- ...prev,
617
- {
618
- key: `console-${Date.now()}-${Math.random()}`,
619
- content: chalk.dim(`[console.${event.level}] ${content}`),
620
- type: 'assistant',
621
- },
622
- ]);
703
+ else if (event.type === 'agent_console') {
704
+ // Show console logs when enabled via --events or --debug
705
+ // --debug continues to show console logs for backwards compatibility
706
+ if (shouldShowEvent('agent_console', activeFilter) || isDebugMode()) {
707
+ const content = typeof event.content === 'string' ? event.content : '';
708
+ setMessages((prev) => [
709
+ ...prev,
710
+ {
711
+ key: `console-${Date.now()}-${Math.random()}`,
712
+ content: chalk.dim(`[console.${event.level}] ${content}`),
713
+ type: 'assistant',
714
+ },
715
+ ]);
716
+ }
623
717
  }
624
- else if (event.type === 'runtime_done' &&
625
- !receivedResponseSinceLastInput.current &&
626
- event.content !== undefined &&
627
- taskInfo &&
628
- 'agent' in taskInfo) {
629
- // One-shot agents may complete with runtime_done without sending
630
- // agent_notification_message. Display the output if we haven't
631
- // already shown a response for this input cycle.
632
- const contentStr = typeof event.content === 'string'
633
- ? event.content
634
- : JSON.stringify(event.content);
635
- if (contentStr && contentStr !== '{}' && contentStr !== 'null') {
636
- const rendered = fixListItemMarkdown(marked.parse(contentStr));
637
- const taskName = agentName || 'assistant';
638
- const messageContent = `${chalk.green('●')} ${chalk.bold(taskName)}\n${rendered.trim()}`;
718
+ else if (event.type === 'runtime_done') {
719
+ if (shouldShowEvent('runtime_done', activeFilter)) {
720
+ // Show runtime_done as a system event when enabled via --events
639
721
  setMessages((prev) => [
640
722
  ...prev,
641
723
  {
642
- key: `msg-${Date.now()}-${Math.random()}`,
643
- content: messageContent,
724
+ key: `runtime-done-${Date.now()}`,
725
+ content: chalk.dim('[runtime/done]'),
644
726
  type: 'assistant',
645
- timestamp: new Date().toLocaleTimeString(),
646
727
  },
647
728
  ]);
648
- if (!firstMessageNotified.current && onFirstMessage) {
649
- firstMessageNotified.current = true;
650
- onFirstMessage();
729
+ }
730
+ if (!receivedResponseSinceLastInput.current &&
731
+ event.content !== undefined &&
732
+ taskInfo &&
733
+ 'agent' in taskInfo) {
734
+ // One-shot agents may complete with runtime_done without sending
735
+ // agent_notification_message. Display the output if we haven't
736
+ // already shown a response for this input cycle.
737
+ const contentStr = typeof event.content === 'string'
738
+ ? event.content
739
+ : JSON.stringify(event.content);
740
+ if (contentStr && contentStr !== '{}' && contentStr !== 'null') {
741
+ const rendered = fixListItemMarkdown(marked.parse(contentStr));
742
+ const taskName = agentName || 'assistant';
743
+ const messageContent = `${chalk.green('●')} ${chalk.bold(taskName)}\n${rendered.trim()}`;
744
+ setMessages((prev) => [
745
+ ...prev,
746
+ {
747
+ key: `msg-${Date.now()}-${Math.random()}`,
748
+ content: messageContent,
749
+ type: 'assistant',
750
+ timestamp: new Date().toLocaleTimeString(),
751
+ },
752
+ ]);
753
+ if (!firstMessageNotified.current && onFirstMessage) {
754
+ firstMessageNotified.current = true;
755
+ onFirstMessage();
756
+ }
757
+ receivedResponseSinceLastInput.current = true;
758
+ setCurrentOperation('');
651
759
  }
652
- receivedResponseSinceLastInput.current = true;
653
- setCurrentOperation('');
654
760
  }
655
761
  }
656
762
  else if (event.type === 'interrupted') {
@@ -684,7 +790,7 @@ function ChatUIWithConnection({ initialPrompt, version: _version, versionId: _ve
684
790
  isPolling.current = false;
685
791
  }
686
792
  };
687
- pollInterval.current = setInterval(poll, 500);
793
+ pollInterval.current = setInterval(poll, 2000);
688
794
  poll();
689
795
  return () => {
690
796
  if (pollInterval.current) {
@@ -887,29 +993,7 @@ export async function createSession(client, workspaceId, initialPrompt, versionI
887
993
  }
888
994
  }
889
995
  if (!workspaceId) {
890
- // Fall back to fetching workspaces from API
891
- progress('Fetching workspaces');
892
- const response = await client.get('/me/workspaces');
893
- if (!response) {
894
- throw new Error('Failed to fetch workspaces');
895
- }
896
- // Handle both array and paginated object responses
897
- const workspaces = (Array.isArray(response)
898
- ? response
899
- : response?.items || []);
900
- if (workspaces.length === 0) {
901
- // Get current user to use as workspace owner
902
- progress('Creating workspace');
903
- const me = (await client.get('/me'));
904
- const newWorkspace = (await client.post('/workspaces', {
905
- name: 'My Workspace',
906
- owner_id: me.id,
907
- }));
908
- workspaceId = newWorkspace.id;
909
- }
910
- else {
911
- workspaceId = workspaces[0].id;
912
- }
996
+ throw new Error('No workspace configured. Run: guild workspace select, or pass --workspace <id>');
913
997
  }
914
998
  progress('Creating session');
915
999
  const sessionData = {
@@ -950,9 +1034,13 @@ export function createChatCommand() {
950
1034
  .option('--workspace <identifier>', 'Workspace ID or full name (e.g., owner/workspace-name)')
951
1035
  .option('--no-splash', 'Skip the splash screen animation')
952
1036
  .option('--resume <session-id>', 'Resume an existing session')
1037
+ .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)')
953
1038
  .addHelpText('after', '\nTo chat with a local agent under development: guild agent chat')
954
1039
  .action(async (promptArgs, options) => {
955
1040
  const initialPrompt = promptArgs.length > 0 ? promptArgs.join(' ') : 'Hello';
1041
+ const eventFilter = options.events
1042
+ ? parseEventFilter(options.events)
1043
+ : DEFAULT_EVENT_TYPES;
956
1044
  if (options.once) {
957
1045
  // --once mode: use old spinner-based approach
958
1046
  const spinner = createSpinner('Connecting to Guild servers...');
@@ -972,7 +1060,7 @@ export function createChatCommand() {
972
1060
  // Timeout after 5 minutes of INACTIVITY (no new messages)
973
1061
  // Agent initialization can take 1-2 minutes with no events
974
1062
  const inactivityTimeoutMs = 300000; // 5 minutes
975
- const pollIntervalMs = 500;
1063
+ const pollIntervalMs = 2000;
976
1064
  const maxInactivityAttempts = inactivityTimeoutMs / pollIntervalMs;
977
1065
  let lastSeenEventId;
978
1066
  const allEvents = [];
@@ -1075,7 +1163,7 @@ export function createChatCommand() {
1075
1163
  if (shouldShowSplash) {
1076
1164
  suppressScrollbackClear();
1077
1165
  }
1078
- const { waitUntilExit } = render(React.createElement(ChatApp, { initialPrompt: initialPrompt, version: packageJson.version, workspaceId: options.workspace, versionId: options.agent, showSplash: shouldShowSplash, resumeSession: resumeSession, resumeEvents: resumeSessionEvents, resumeCommand: resumeCommand }), {
1166
+ const { waitUntilExit } = render(React.createElement(ChatApp, { initialPrompt: initialPrompt, version: packageJson.version, workspaceId: options.workspace, versionId: options.agent, showSplash: shouldShowSplash, resumeSession: resumeSession, resumeEvents: resumeSessionEvents, resumeCommand: resumeCommand, eventFilter: eventFilter }), {
1079
1167
  exitOnCtrlC: false, // We handle Ctrl-C in useInput (raw mode)
1080
1168
  });
1081
1169
  await waitUntilExit();
@@ -0,0 +1,3 @@
1
+ import { Command } from 'commander';
2
+ export declare function createSessionInterruptCommand(): Command;
3
+ //# sourceMappingURL=interrupt.d.ts.map
@@ -0,0 +1,33 @@
1
+ // Copyright 2026 Guild.ai
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ import { Command } from 'commander';
4
+ import { GuildAPIClient } from '../../lib/api-client.js';
5
+ import { getAuthToken } from '../../lib/auth.js';
6
+ import { handleAxiosError } from '../../lib/errors.js';
7
+ import { createOutputWriter } from '../../lib/output.js';
8
+ export function createSessionInterruptCommand() {
9
+ const cmd = new Command('interrupt');
10
+ cmd
11
+ .description('Interrupt a running session')
12
+ .argument('<session-id>', 'Session ID')
13
+ .action(async (sessionId) => {
14
+ const output = createOutputWriter();
15
+ try {
16
+ const token = await getAuthToken();
17
+ if (!token) {
18
+ output.error('Not authenticated. Run: guild auth login');
19
+ process.exit(1);
20
+ }
21
+ const client = new GuildAPIClient();
22
+ const response = await client.post(`/sessions/${sessionId}/interrupt`, {});
23
+ output.data(response);
24
+ }
25
+ catch (error) {
26
+ const formattedError = handleAxiosError(error);
27
+ output.error(`Failed to interrupt session: ${formattedError.details}`);
28
+ process.exit(1);
29
+ }
30
+ });
31
+ return cmd;
32
+ }
33
+ //# sourceMappingURL=interrupt.js.map
@@ -11,7 +11,7 @@ const __filename = fileURLToPath(import.meta.url);
11
11
  const __dirname = path.dirname(__filename);
12
12
  const packageRoot = path.resolve(__dirname, '..', '..');
13
13
  const docsDir = path.join(packageRoot, 'docs');
14
- const SKILL_FILES = [
14
+ const CLAUDE_SKILL_FILES = [
15
15
  {
16
16
  src: path.join(docsDir, 'skills', 'agent-dev.md'),
17
17
  dest: path.join('.claude', 'skills', 'agent-dev', 'skill.md'),
@@ -22,6 +22,18 @@ const SKILL_FILES = [
22
22
  dest: path.join('.claude', 'skills', 'guild-cli-workflow', 'skill.md'),
23
23
  label: '.claude/skills/guild-cli-workflow/skill.md',
24
24
  },
25
+ {
26
+ src: path.join(docsDir, 'skills', 'integrations.md'),
27
+ dest: path.join('.claude', 'skills', 'integrations', 'skill.md'),
28
+ label: '.claude/skills/integrations/skill.md',
29
+ },
30
+ ];
31
+ const CODEX_SKILL_FILES = [
32
+ {
33
+ src: path.join(docsDir, 'skills', 'codex-agent-dev.md'),
34
+ dest: path.join('.agents', 'skills', 'guild-agent-dev', 'SKILL.md'),
35
+ label: '.agents/skills/guild-agent-dev/SKILL.md',
36
+ },
25
37
  ];
26
38
  const CLAUDE_MD_TEMPLATE = `# CLAUDE.md
27
39
 
@@ -38,6 +50,22 @@ guild agent save --message "Initial version" --wait --publish
38
50
 
39
51
  See \`.claude/skills/agent-dev/skill.md\` for SDK reference, patterns, and anti-hallucination guide.
40
52
  `;
53
+ const AGENTS_MD_TEMPLATE = `# AGENTS.md
54
+
55
+ ## Guild Agent Development
56
+
57
+ This project uses Guild for agent development. Codex instructions are installed in \`.agents/skills/guild-agent-dev/SKILL.md\`.
58
+
59
+ ### Quick Start
60
+
61
+ \`\`\`bash
62
+ guild agent init --name my-agent --template LLM
63
+ guild agent test --ephemeral
64
+ guild agent save -A --message "Initial version"
65
+ \`\`\`
66
+
67
+ Use \`guild doctor\` to check authentication, server connectivity, workspace selection, and git setup.
68
+ `;
41
69
  async function fileExists(filePath) {
42
70
  try {
43
71
  await fs.access(filePath);
@@ -82,21 +110,29 @@ async function setupMcp(options) {
82
110
  }
83
111
  async function setup(options) {
84
112
  const output = createOutputWriter();
85
- // Verify source docs exist
86
- const docsExist = await fileExists(path.join(docsDir, 'skills', 'agent-dev.md'));
87
- if (!docsExist) {
88
- output.error('Could not find Guild CLI docs. Reinstall the CLI: npm install -g @guildai/cli');
113
+ if (options.agentsMd && !options.codex) {
114
+ output.error('--agents-md requires --codex', 'Create Codex setup files with:\n guild setup --codex --agents-md');
115
+ process.exit(1);
116
+ }
117
+ if (options.claudeMd && options.codex) {
118
+ output.error('--claude-md cannot be used with --codex', 'Create Claude setup with:\n guild setup --claude-md\n\nCreate Codex setup with:\n guild setup --codex --agents-md');
89
119
  process.exit(1);
90
120
  }
121
+ const skillFiles = options.codex ? CODEX_SKILL_FILES : CLAUDE_SKILL_FILES;
122
+ // Verify source docs exist
123
+ for (const file of skillFiles) {
124
+ if (!(await fileExists(file.src))) {
125
+ output.error('Could not find Guild CLI docs. Reinstall the CLI: npm install -g @guildai/cli');
126
+ process.exit(1);
127
+ }
128
+ }
91
129
  output.progress('Setting up Guild CLI skills...');
92
130
  output.progress('');
93
- // Ensure .claude/skills/ directory exists
94
- const skillsDir = path.join(process.cwd(), '.claude', 'skills');
95
- await fs.mkdir(skillsDir, { recursive: true });
96
131
  let filesCreated = 0;
97
132
  let filesSkipped = 0;
133
+ let codexProjectFilesChanged = false;
98
134
  // Copy skill files
99
- for (const file of SKILL_FILES) {
135
+ for (const file of skillFiles) {
100
136
  const destPath = path.join(process.cwd(), file.dest);
101
137
  const exists = await fileExists(destPath);
102
138
  if (exists && !options.force) {
@@ -113,6 +149,9 @@ async function setup(options) {
113
149
  output.success(`Created ${file.label}`);
114
150
  }
115
151
  filesCreated++;
152
+ if (options.codex) {
153
+ codexProjectFilesChanged = true;
154
+ }
116
155
  }
117
156
  }
118
157
  // Handle --mcp flag
@@ -139,6 +178,21 @@ async function setup(options) {
139
178
  filesCreated++;
140
179
  }
141
180
  }
181
+ // Handle AGENTS.md template for Codex setup
182
+ if (options.agentsMd) {
183
+ const agentsMdPath = path.join(process.cwd(), 'AGENTS.md');
184
+ const exists = await fileExists(agentsMdPath);
185
+ if (exists) {
186
+ output.progress('AGENTS.md already exists (not overwriting)');
187
+ filesSkipped++;
188
+ }
189
+ else {
190
+ await fs.writeFile(agentsMdPath, AGENTS_MD_TEMPLATE, 'utf-8');
191
+ output.success('Created AGENTS.md');
192
+ filesCreated++;
193
+ codexProjectFilesChanged = true;
194
+ }
195
+ }
142
196
  // Summary
143
197
  output.progress('');
144
198
  if (filesCreated > 0 && filesSkipped === 0) {
@@ -155,13 +209,18 @@ async function setup(options) {
155
209
  else {
156
210
  output.success('Guild CLI skills installed.');
157
211
  }
212
+ if (codexProjectFilesChanged) {
213
+ output.progress('Restart Codex to pick up the new project instructions.');
214
+ }
158
215
  }
159
216
  export function createSetupCommand() {
160
217
  const cmd = new Command('setup');
161
218
  cmd
162
- .description('Set up Guild CLI skills for coding assistants (Claude Code, etc.)')
163
- .option('--force', 'Overwrite existing skill files', false)
219
+ .description('Set up Guild CLI skills for coding assistants (Claude Code, Codex, etc.)')
220
+ .option('--force', 'Overwrite existing skill files and Guild MCP config', false)
221
+ .option('--codex', 'Install Codex skill files instead of Claude Code skills', false)
164
222
  .option('--claude-md', 'Also create a CLAUDE.md template in the project root', false)
223
+ .option('--agents-md', 'With --codex, also create an AGENTS.md template in the project root', false)
165
224
  .option('--no-mcp', 'Skip MCP server configuration')
166
225
  .action(async (options) => {
167
226
  await setup(options);
package/dist/index.js CHANGED
@@ -59,6 +59,7 @@ import { createSessionEventsCommand } from './commands/session/events.js';
59
59
  import { createSessionTasksCommand } from './commands/session/tasks.js';
60
60
  import { createSessionCreateCommand } from './commands/session/create.js';
61
61
  import { createSessionSendCommand } from './commands/session/send.js';
62
+ import { createSessionInterruptCommand } from './commands/session/interrupt.js';
62
63
  import { createJobGetCommand } from './commands/job/get.js';
63
64
  import { createJobStepGetCommand } from './commands/job/step-get.js';
64
65
  import { createConfigListCommand } from './commands/config/list.js';
@@ -276,6 +277,7 @@ sessionCmd.addCommand(createSessionEventsCommand());
276
277
  sessionCmd.addCommand(createSessionTasksCommand());
277
278
  sessionCmd.addCommand(createSessionCreateCommand());
278
279
  sessionCmd.addCommand(createSessionSendCommand());
280
+ sessionCmd.addCommand(createSessionInterruptCommand());
279
281
  // Job command group
280
282
  const jobCmd = program.command('job').description('Job management');
281
283
  jobCmd.addCommand(createJobGetCommand());
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Event type filtering for the CLI `--events` flag.
3
+ *
4
+ * Mirrors the web UI's "Filter by type" modal, which groups event types into
5
+ * two categories: user-facing events (on by default) and system/debug events
6
+ * (off by default).
7
+ *
8
+ * The canonical event type list is generated from the backend EventType enum
9
+ * (see generated-types.ts). The user/system grouping is a UI concern defined
10
+ * here and in www/src/components/EventTypeFilters/types.ts.
11
+ */
12
+ import type { EventType } from './generated-types.js';
13
+ /**
14
+ * User-facing event types — shown by default (mirrors web UI defaults).
15
+ */
16
+ export declare const USER_EVENT_TYPES: readonly EventType[];
17
+ /**
18
+ * System / debug event types — hidden by default (mirrors web UI defaults).
19
+ */
20
+ export declare const SYSTEM_EVENT_TYPES: readonly EventType[];
21
+ /**
22
+ * Default active filter: all user-facing types, no system types.
23
+ */
24
+ export declare const DEFAULT_EVENT_TYPES: Set<string>;
25
+ /**
26
+ * Parse the value of the `--events` flag into a Set of event type names.
27
+ *
28
+ * Whatever you pass replaces the defaults entirely:
29
+ * - `none` → empty set (no event types shown)
30
+ * - `user` → all USER_EVENT_TYPES (same as default)
31
+ * - `system` → only SYSTEM_EVENT_TYPES
32
+ * - `all` → both USER_EVENT_TYPES + SYSTEM_EVENT_TYPES
33
+ *
34
+ * Comma-separated for fine-grained control:
35
+ * - `agent_console,llm_start` → only those two types
36
+ * - `user,system` → same as `all`
37
+ */
38
+ export declare function parseEventFilter(raw: string): Set<string>;
39
+ /**
40
+ * Check whether an event should be shown given the active filter.
41
+ *
42
+ * Returns `true` when the event type is in the filter set.
43
+ *
44
+ * Note: certain event types (e.g. `agent_notification_progress`,
45
+ * `agent_notification_message`) drive core UI state (spinner, chat history)
46
+ * and are always processed regardless of the filter; only their *display* is
47
+ * gated here for new/optional event types.
48
+ */
49
+ export declare function shouldShowEvent(type: string, filter: Set<string>): boolean;
50
+ //# sourceMappingURL=event-filter.d.ts.map