@agentuity/cli 0.1.33 → 0.1.35

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 (122) hide show
  1. package/bin/cli.ts +107 -6
  2. package/dist/cmd/ai/index.d.ts.map +1 -1
  3. package/dist/cmd/ai/index.js +0 -6
  4. package/dist/cmd/ai/index.js.map +1 -1
  5. package/dist/cmd/ai/opencode/uninstall.js +1 -1
  6. package/dist/cmd/ai/opencode/uninstall.js.map +1 -1
  7. package/dist/cmd/build/vite/bun-dev-server.d.ts +6 -0
  8. package/dist/cmd/build/vite/bun-dev-server.d.ts.map +1 -1
  9. package/dist/cmd/build/vite/bun-dev-server.js +100 -33
  10. package/dist/cmd/build/vite/bun-dev-server.js.map +1 -1
  11. package/dist/cmd/cloud/apikey/create.d.ts.map +1 -1
  12. package/dist/cmd/cloud/apikey/create.js +1 -1
  13. package/dist/cmd/cloud/apikey/create.js.map +1 -1
  14. package/dist/cmd/cloud/deploy.d.ts.map +1 -1
  15. package/dist/cmd/cloud/deploy.js +4 -1
  16. package/dist/cmd/cloud/deploy.js.map +1 -1
  17. package/dist/cmd/cloud/env/delete.d.ts.map +1 -1
  18. package/dist/cmd/cloud/env/delete.js +100 -45
  19. package/dist/cmd/cloud/env/delete.js.map +1 -1
  20. package/dist/cmd/cloud/env/pull.d.ts.map +1 -1
  21. package/dist/cmd/cloud/env/pull.js +1 -1
  22. package/dist/cmd/cloud/env/pull.js.map +1 -1
  23. package/dist/cmd/cloud/machine/delete.js +1 -1
  24. package/dist/cmd/cloud/machine/delete.js.map +1 -1
  25. package/dist/cmd/cloud/sandbox/exec.d.ts.map +1 -1
  26. package/dist/cmd/cloud/sandbox/exec.js +10 -35
  27. package/dist/cmd/cloud/sandbox/exec.js.map +1 -1
  28. package/dist/cmd/dev/index.d.ts.map +1 -1
  29. package/dist/cmd/dev/index.js +58 -10
  30. package/dist/cmd/dev/index.js.map +1 -1
  31. package/dist/cmd/index.d.ts.map +1 -1
  32. package/dist/cmd/index.js +1 -0
  33. package/dist/cmd/index.js.map +1 -1
  34. package/dist/cmd/support/index.d.ts +2 -0
  35. package/dist/cmd/support/index.d.ts.map +1 -0
  36. package/dist/cmd/support/index.js +11 -0
  37. package/dist/cmd/support/index.js.map +1 -0
  38. package/dist/cmd/support/logs/index.d.ts +3 -0
  39. package/dist/cmd/support/logs/index.d.ts.map +1 -0
  40. package/dist/cmd/support/logs/index.js +9 -0
  41. package/dist/cmd/support/logs/index.js.map +1 -0
  42. package/dist/cmd/support/logs/path.d.ts +3 -0
  43. package/dist/cmd/support/logs/path.d.ts.map +1 -0
  44. package/dist/cmd/support/logs/path.js +52 -0
  45. package/dist/cmd/support/logs/path.js.map +1 -0
  46. package/dist/cmd/support/logs/show.d.ts +3 -0
  47. package/dist/cmd/support/logs/show.d.ts.map +1 -0
  48. package/dist/cmd/support/logs/show.js +121 -0
  49. package/dist/cmd/support/logs/show.js.map +1 -0
  50. package/dist/cmd/support/report.d.ts +3 -0
  51. package/dist/cmd/support/report.d.ts.map +1 -0
  52. package/dist/cmd/support/report.js +299 -0
  53. package/dist/cmd/support/report.js.map +1 -0
  54. package/dist/cmd/support/system.d.ts +3 -0
  55. package/dist/cmd/support/system.d.ts.map +1 -0
  56. package/dist/cmd/support/system.js +120 -0
  57. package/dist/cmd/support/system.js.map +1 -0
  58. package/dist/cmd/version/index.d.ts.map +1 -1
  59. package/dist/cmd/version/index.js +1 -0
  60. package/dist/cmd/version/index.js.map +1 -1
  61. package/dist/composite-logger.d.ts +35 -0
  62. package/dist/composite-logger.d.ts.map +1 -0
  63. package/dist/composite-logger.js +78 -0
  64. package/dist/composite-logger.js.map +1 -0
  65. package/dist/index.d.ts +2 -0
  66. package/dist/index.d.ts.map +1 -1
  67. package/dist/index.js +2 -0
  68. package/dist/index.js.map +1 -1
  69. package/dist/internal-logger.d.ts +77 -0
  70. package/dist/internal-logger.d.ts.map +1 -0
  71. package/dist/internal-logger.js +340 -0
  72. package/dist/internal-logger.js.map +1 -0
  73. package/dist/types.d.ts +6 -0
  74. package/dist/types.d.ts.map +1 -1
  75. package/dist/types.js.map +1 -1
  76. package/dist/utils/installation-type.d.ts.map +1 -1
  77. package/dist/utils/installation-type.js +54 -16
  78. package/dist/utils/installation-type.js.map +1 -1
  79. package/package.json +6 -6
  80. package/src/cmd/ai/index.ts +0 -6
  81. package/src/cmd/ai/opencode/uninstall.ts +1 -1
  82. package/src/cmd/build/vite/bun-dev-server.ts +113 -36
  83. package/src/cmd/cloud/apikey/create.ts +3 -1
  84. package/src/cmd/cloud/deploy.ts +4 -1
  85. package/src/cmd/cloud/env/delete.ts +100 -45
  86. package/src/cmd/cloud/env/pull.ts +1 -6
  87. package/src/cmd/cloud/machine/delete.ts +1 -1
  88. package/src/cmd/cloud/sandbox/exec.ts +10 -41
  89. package/src/cmd/dev/index.ts +59 -11
  90. package/src/cmd/index.ts +1 -0
  91. package/src/cmd/support/index.ts +11 -0
  92. package/src/cmd/support/logs/index.ts +9 -0
  93. package/src/cmd/support/logs/path.ts +56 -0
  94. package/src/cmd/support/logs/show.ts +144 -0
  95. package/src/cmd/support/report.ts +364 -0
  96. package/src/cmd/support/system.ts +130 -0
  97. package/src/cmd/version/index.ts +1 -0
  98. package/src/composite-logger.ts +86 -0
  99. package/src/index.ts +7 -0
  100. package/src/internal-logger.ts +411 -0
  101. package/src/types.ts +6 -0
  102. package/src/utils/installation-type.ts +55 -16
  103. package/dist/cmd/ai/skills/generate.d.ts +0 -3
  104. package/dist/cmd/ai/skills/generate.d.ts.map +0 -1
  105. package/dist/cmd/ai/skills/generate.js +0 -65
  106. package/dist/cmd/ai/skills/generate.js.map +0 -1
  107. package/dist/cmd/ai/skills/generator.d.ts +0 -4
  108. package/dist/cmd/ai/skills/generator.d.ts.map +0 -1
  109. package/dist/cmd/ai/skills/generator.js +0 -410
  110. package/dist/cmd/ai/skills/generator.js.map +0 -1
  111. package/dist/cmd/ai/skills/index.d.ts +0 -4
  112. package/dist/cmd/ai/skills/index.d.ts.map +0 -1
  113. package/dist/cmd/ai/skills/index.js +0 -21
  114. package/dist/cmd/ai/skills/index.js.map +0 -1
  115. package/dist/cmd/dev/skills.d.ts +0 -10
  116. package/dist/cmd/dev/skills.d.ts.map +0 -1
  117. package/dist/cmd/dev/skills.js +0 -57
  118. package/dist/cmd/dev/skills.js.map +0 -1
  119. package/src/cmd/ai/skills/generate.ts +0 -75
  120. package/src/cmd/ai/skills/generator.ts +0 -527
  121. package/src/cmd/ai/skills/index.ts +0 -23
  122. package/src/cmd/dev/skills.ts +0 -82
@@ -7,8 +7,8 @@ import { getCommand } from '../../../command-prefix';
7
7
  import { sandboxExecute, executionGet, writeAndDrain } from '@agentuity/server';
8
8
  import type { Logger } from '@agentuity/core';
9
9
 
10
- const POLL_INTERVAL_MS = 500;
11
- const MAX_POLL_ATTEMPTS = 7200;
10
+ // Server-side long-poll wait duration (max 5 minutes supported by server)
11
+ const EXECUTION_WAIT_DURATION = '5m';
12
12
 
13
13
  const SandboxExecResponseSchema = z.object({
14
14
  executionId: z.string().describe('Unique execution identifier'),
@@ -115,41 +115,14 @@ export const execSubcommand = createCommand({
115
115
  }
116
116
  }
117
117
 
118
- let attempts = 0;
119
- let finalExecution = execution;
120
-
121
- while (attempts < MAX_POLL_ATTEMPTS) {
122
- if (abortController.signal.aborted) {
123
- throw new Error('Execution cancelled');
124
- }
125
-
126
- await sleep(POLL_INTERVAL_MS);
127
- attempts++;
128
-
129
- try {
130
- const execInfo = await executionGet(client, {
131
- executionId: execution.executionId,
132
- orgId,
133
- });
134
-
135
- if (
136
- execInfo.status === 'completed' ||
137
- execInfo.status === 'failed' ||
138
- execInfo.status === 'timeout' ||
139
- execInfo.status === 'cancelled'
140
- ) {
141
- finalExecution = {
142
- executionId: execInfo.executionId,
143
- status: execInfo.status,
144
- exitCode: execInfo.exitCode,
145
- durationMs: execInfo.durationMs,
146
- };
147
- break;
148
- }
149
- } catch {
150
- continue;
151
- }
152
- }
118
+ // Use server-side long-polling to wait for execution completion
119
+ // This is more efficient than client-side polling and provides immediate
120
+ // error detection if the sandbox is terminated
121
+ const finalExecution = await executionGet(client, {
122
+ executionId: execution.executionId,
123
+ orgId,
124
+ wait: EXECUTION_WAIT_DURATION,
125
+ });
153
126
 
154
127
  // Wait for all streams to reach EOF (Pulse blocks until true EOF)
155
128
  await Promise.all(streamPromises);
@@ -248,8 +221,4 @@ function createCaptureStream(onChunk: (chunk: string) => void): NodeJS.WritableS
248
221
  });
249
222
  }
250
223
 
251
- function sleep(ms: number): Promise<void> {
252
- return new Promise((resolve) => setTimeout(resolve, ms));
253
- }
254
-
255
224
  export default execSubcommand;
@@ -18,7 +18,6 @@ import { typecheck } from '../build/typecheck';
18
18
  import { validateGravityRequiresUpgrade } from '../../runtime';
19
19
  import { isTTY, hasLoggedInBefore } from '../../auth';
20
20
  import { createFileWatcher } from './file-watcher';
21
- import { regenerateSkillsAsync } from './skills';
22
21
  import { prepareDevLock, releaseLockSync } from './dev-lock';
23
22
  import { checkAndUpgradeDependencies } from '../../utils/dependency-checker';
24
23
  import { ErrorCode } from '../../errors';
@@ -81,6 +80,7 @@ async function killLingeringGravityProcesses(logger: {
81
80
  /**
82
81
  * Stop the existing Bun server if one is running.
83
82
  * Waits for the port to become available before returning (with timeout).
83
+ * Handles both in-process server and subprocess (when debugger is enabled).
84
84
  */
85
85
  async function stopBunServer(
86
86
  port: number,
@@ -88,6 +88,48 @@ async function stopBunServer(
88
88
  ): Promise<void> {
89
89
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
90
90
  const globalAny = globalThis as any;
91
+
92
+ // Check for subprocess first (used when debugger flags are enabled)
93
+ const bunSubprocess = globalAny.__AGENTUITY_BUN_SUBPROCESS__ as ProcessLike | undefined;
94
+ if (bunSubprocess) {
95
+ logger.debug('Stopping Bun subprocess...');
96
+ try {
97
+ bunSubprocess.kill('SIGTERM');
98
+ // After SIGTERM, wait and check multiple times before giving up
99
+ let attempts = 0;
100
+ while (bunSubprocess.exitCode === null && attempts < 3) {
101
+ await new Promise((resolve) => setTimeout(resolve, 100));
102
+ attempts++;
103
+ }
104
+ if (bunSubprocess.exitCode === null) {
105
+ bunSubprocess.kill('SIGKILL');
106
+ }
107
+ logger.debug('Bun subprocess killed');
108
+ } catch (err) {
109
+ logger.debug('Error killing Bun subprocess: %s', err);
110
+ }
111
+ globalAny.__AGENTUITY_BUN_SUBPROCESS__ = undefined;
112
+
113
+ // Wait for port to become available
114
+ const MAX_WAIT_ITERATIONS = 10;
115
+ for (let i = 0; i < MAX_WAIT_ITERATIONS; i++) {
116
+ try {
117
+ await fetch(`http://127.0.0.1:${port}/`, {
118
+ method: 'HEAD',
119
+ signal: AbortSignal.timeout(150),
120
+ });
121
+ // Still responding, wait a bit more
122
+ await new Promise((r) => setTimeout(r, 50));
123
+ } catch {
124
+ // Connection refused or timeout => server is down
125
+ logger.debug('Bun subprocess stopped');
126
+ break;
127
+ }
128
+ }
129
+ return;
130
+ }
131
+
132
+ // Handle in-process server
91
133
  const server = globalAny.__AGENTUITY_SERVER__ as BunServer | undefined;
92
134
  if (!server) {
93
135
  logger.debug('No Bun server to stop');
@@ -171,6 +213,15 @@ export const command = createCommand({
171
213
  .max(MAX_PORT)
172
214
  .default(getDefaultPort())
173
215
  .describe('The TCP port to start the dev server (also reads from PORT env)'),
216
+ inspect: z.boolean().optional().describe('Enable bun debugger on available port'),
217
+ inspectWait: z
218
+ .boolean()
219
+ .optional()
220
+ .describe('Enable bun debugger and wait for connection before executing'),
221
+ inspectBrk: z
222
+ .boolean()
223
+ .optional()
224
+ .describe('Enable bun debugger with breakpoint at first line'),
174
225
  }),
175
226
  },
176
227
  optional: { project: true },
@@ -489,12 +540,6 @@ export const command = createCommand({
489
540
  centerTitle: false,
490
541
  });
491
542
 
492
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
493
- const cliVersion = ((global as any).__CLI_SCHEMA__?.version as string) ?? '';
494
- if (cliVersion) {
495
- regenerateSkillsAsync(rootDir, cliVersion, logger).catch(() => {});
496
- }
497
-
498
543
  // Start Vite asset server ONCE before restart loop
499
544
  // Vite handles frontend HMR independently and stays running across backend restarts
500
545
  let viteServer: ServerLike | null = null;
@@ -961,7 +1006,7 @@ export const command = createCommand({
961
1006
  }
962
1007
  } catch (error) {
963
1008
  tui.error(`Failed to build dev bundle: ${error}`);
964
- tui.warn('Waiting for file changes to retry...');
1009
+ tui.warning('Waiting for file changes to retry...');
965
1010
 
966
1011
  // Resume watcher to detect changes for retry
967
1012
  fileWatcher.resume();
@@ -986,7 +1031,7 @@ export const command = createCommand({
986
1031
  if (sdkKey) {
987
1032
  process.env.AGENTUITY_SDK_KEY = sdkKey;
988
1033
  } else if (project) {
989
- tui.warn(
1034
+ tui.warning(
990
1035
  'AGENTUITY_SDK_KEY not found in .env file. Numerous features will be unavailable.'
991
1036
  );
992
1037
  tui.bullet(
@@ -1035,6 +1080,9 @@ export const command = createCommand({
1035
1080
  deploymentId,
1036
1081
  logger,
1037
1082
  vitePort, // Pass port of already-running Vite server
1083
+ inspect: opts.inspect,
1084
+ inspectWait: opts.inspectWait,
1085
+ inspectBrk: opts.inspectBrk,
1038
1086
  });
1039
1087
 
1040
1088
  // Wait for app.ts to finish loading (Vite is ready but app may still be initializing)
@@ -1047,7 +1095,7 @@ export const command = createCommand({
1047
1095
  }
1048
1096
  } catch (error) {
1049
1097
  tui.error(`Failed to start dev server: ${error}`);
1050
- tui.warn('Waiting for file changes to retry...');
1098
+ tui.warning('Waiting for file changes to retry...');
1051
1099
 
1052
1100
  // Wait for next restart trigger or shutdown
1053
1101
  await new Promise<void>((resolve) => {
@@ -1274,7 +1322,7 @@ export const command = createCommand({
1274
1322
  await Bun.sleep(500);
1275
1323
  } catch (error) {
1276
1324
  tui.error(`Error during server operation: ${error}`);
1277
- tui.warn('Waiting for file changes to retry...');
1325
+ tui.warning('Waiting for file changes to retry...');
1278
1326
 
1279
1327
  // Cleanup on error (Vite stays running)
1280
1328
  await cleanupForRestart();
package/src/cmd/index.ts CHANGED
@@ -15,6 +15,7 @@ export async function discoverCommands(): Promise<CommandDefinition[]> {
15
15
  import('./project').then((m) => m.command),
16
16
  import('./repl').then((m) => m.command),
17
17
  import('./setup').then((m) => m.command),
18
+ import('./support').then((m) => m.command),
18
19
  import('./upgrade').then((m) => m.command),
19
20
  import('./version').then((m) => m.command),
20
21
  ]);
@@ -0,0 +1,11 @@
1
+ import { createCommand } from '../../types';
2
+ import logs from './logs';
3
+ import report from './report';
4
+ import system from './system';
5
+
6
+ export const command = createCommand({
7
+ name: 'support',
8
+ description: 'Create support tickets and report issues',
9
+ skipInternalLogging: true,
10
+ subcommands: [logs, report, system],
11
+ });
@@ -0,0 +1,9 @@
1
+ import { createCommand } from '../../../types';
2
+ import show from './show';
3
+ import path from './path';
4
+
5
+ export default createCommand({
6
+ name: 'logs',
7
+ description: 'View and manage CLI execution logs',
8
+ subcommands: [show, path],
9
+ });
@@ -0,0 +1,56 @@
1
+ import { createSubcommand } from '../../../types';
2
+ import { z } from 'zod';
3
+ import { getLatestLogSession, getLogsDirPath } from '../../../internal-logger';
4
+ import * as tui from '../../../tui';
5
+
6
+ const argsSchema = z.object({});
7
+
8
+ const optionsSchema = z.object({
9
+ logs: z.boolean().optional().default(false).describe('Show path to logs directory instead of session'),
10
+ });
11
+
12
+ export default createSubcommand({
13
+ name: 'path',
14
+ description: 'Show the path to the most recent log session',
15
+ schema: {
16
+ args: argsSchema,
17
+ options: optionsSchema,
18
+ },
19
+ handler: async (ctx) => {
20
+ const { opts } = ctx;
21
+ const isJsonMode = ctx.options.json;
22
+
23
+ if (opts.logs) {
24
+ // Show the logs directory path
25
+ const logsDir = getLogsDirPath();
26
+ if (isJsonMode) {
27
+ console.log(JSON.stringify({ logsDir }));
28
+ } else {
29
+ console.log(logsDir);
30
+ }
31
+ return;
32
+ }
33
+
34
+ const sessionDir = getLatestLogSession();
35
+ if (!sessionDir) {
36
+ if (isJsonMode) {
37
+ console.log(
38
+ JSON.stringify({
39
+ error: 'No logs found',
40
+ logsDir: getLogsDirPath(),
41
+ })
42
+ );
43
+ } else {
44
+ tui.warning('No CLI logs found');
45
+ tui.info(`Logs directory: ${tui.muted(getLogsDirPath())}`);
46
+ }
47
+ return;
48
+ }
49
+
50
+ if (isJsonMode) {
51
+ console.log(JSON.stringify({ sessionDir }));
52
+ } else {
53
+ console.log(sessionDir);
54
+ }
55
+ },
56
+ });
@@ -0,0 +1,144 @@
1
+ import { createSubcommand } from '../../../types';
2
+ import { z } from 'zod';
3
+ import { readFileSync, existsSync } from 'node:fs';
4
+ import { getLatestLogSession, getLogsDirPath } from '../../../internal-logger';
5
+ import * as tui from '../../../tui';
6
+ import { join } from 'node:path';
7
+
8
+ const argsSchema = z.object({});
9
+
10
+ const optionsSchema = z.object({
11
+ session: z.boolean().optional().default(false).describe('Show session metadata only'),
12
+ tail: z.number().optional().describe('Show last N log entries'),
13
+ });
14
+
15
+ export default createSubcommand({
16
+ name: 'show',
17
+ description: 'Show the most recent CLI command logs',
18
+ schema: {
19
+ args: argsSchema,
20
+ options: optionsSchema,
21
+ },
22
+ handler: async (ctx) => {
23
+ const { opts } = ctx;
24
+ const isJsonMode = ctx.options.json;
25
+
26
+ const sessionDir = getLatestLogSession();
27
+ if (!sessionDir) {
28
+ if (isJsonMode) {
29
+ console.log(
30
+ JSON.stringify({
31
+ error: 'No logs found',
32
+ logsDir: getLogsDirPath(),
33
+ })
34
+ );
35
+ return;
36
+ }
37
+ tui.warning('No CLI logs found');
38
+ tui.info(`Logs are stored in: ${tui.muted(getLogsDirPath())}`);
39
+ return;
40
+ }
41
+
42
+ const sessionFile = join(sessionDir, 'session.json');
43
+ const logsFile = join(sessionDir, 'logs.jsonl');
44
+
45
+ if (!existsSync(sessionFile)) {
46
+ if (isJsonMode) {
47
+ console.log(JSON.stringify({ error: 'Session metadata not found' }));
48
+ return;
49
+ }
50
+ tui.error('Session metadata not found');
51
+ return;
52
+ }
53
+
54
+ // Read session metadata
55
+ const sessionData = JSON.parse(readFileSync(sessionFile, 'utf-8'));
56
+
57
+ if (opts.session) {
58
+ // Show session metadata only
59
+ if (isJsonMode) {
60
+ console.log(JSON.stringify(sessionData, null, 2));
61
+ } else {
62
+ tui.info(tui.bold('CLI Command Session'));
63
+ tui.newline();
64
+ tui.output(`Session ID: ${tui.colorPrimary(sessionData.sessionId)}`);
65
+ tui.output(`Command: ${tui.colorInfo(sessionData.command)}`);
66
+ tui.output(`Args: ${sessionData.args.join(' ')}`);
67
+ tui.output(`Timestamp: ${sessionData.timestamp}`);
68
+ tui.output(`CLI Version: ${sessionData.cli.version}`);
69
+ tui.output(`Platform: ${sessionData.system.platform}/${sessionData.system.arch}`);
70
+ tui.output(`CWD: ${sessionData.cwd}`);
71
+ tui.newline();
72
+ tui.output(tui.muted(`Session directory: ${sessionDir}`));
73
+ }
74
+ return;
75
+ }
76
+
77
+ // Read log entries
78
+ if (!existsSync(logsFile)) {
79
+ if (isJsonMode) {
80
+ console.log(JSON.stringify({ session: sessionData, logs: [] }));
81
+ return;
82
+ }
83
+ tui.warning('No log entries found for this session');
84
+ return;
85
+ }
86
+
87
+ const logLines = readFileSync(logsFile, 'utf-8')
88
+ .split('\n')
89
+ .filter((line) => line.trim());
90
+ const logEntries = logLines.map((line) => JSON.parse(line));
91
+
92
+ // Apply tail filter if specified
93
+ const displayEntries = opts.tail ? logEntries.slice(-opts.tail) : logEntries;
94
+
95
+ if (isJsonMode) {
96
+ console.log(
97
+ JSON.stringify(
98
+ {
99
+ session: sessionData,
100
+ logs: displayEntries,
101
+ },
102
+ null,
103
+ 2
104
+ )
105
+ );
106
+ } else {
107
+ // Human-readable output
108
+ tui.info(tui.bold('CLI Command Session'));
109
+ tui.newline();
110
+ tui.output(`Session ID: ${tui.colorPrimary(sessionData.sessionId)}`);
111
+ tui.output(`Command: ${tui.colorInfo(sessionData.command)}`);
112
+ tui.output(`Timestamp: ${sessionData.timestamp}`);
113
+ tui.output(`Total Logs: ${logEntries.length}`);
114
+ if (opts.tail) {
115
+ tui.output(`Showing: Last ${opts.tail} entries`);
116
+ }
117
+ tui.newline();
118
+
119
+ // Display log entries
120
+ for (const entry of displayEntries) {
121
+ const levelColor =
122
+ entry.level === 'error'
123
+ ? tui.colorError
124
+ : entry.level === 'warn'
125
+ ? tui.warn
126
+ : entry.level === 'info'
127
+ ? tui.colorInfo
128
+ : tui.muted;
129
+
130
+ const timestamp = new Date(entry.timestamp).toLocaleTimeString();
131
+ const level = entry.level.toUpperCase().padEnd(5);
132
+
133
+ tui.output(`${tui.muted(timestamp)} ${levelColor(level)} ${entry.message}`);
134
+
135
+ if (entry.context && Object.keys(entry.context).length > 0) {
136
+ tui.output(tui.muted(` Context: ${JSON.stringify(entry.context)}`));
137
+ }
138
+ }
139
+
140
+ tui.newline();
141
+ tui.output(tui.muted(`Session directory: ${sessionDir}`));
142
+ }
143
+ },
144
+ });