@xagent-ai/cli 1.3.0 → 1.3.1

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 (190) hide show
  1. package/.github/release.yml +76 -0
  2. package/.github/workflows/ci.yml +3 -0
  3. package/.github/workflows/release.yml +11 -17
  4. package/README.md +2 -2
  5. package/README_CN.md +2 -2
  6. package/dist/agents.d.ts.map +1 -1
  7. package/dist/agents.js +7 -3
  8. package/dist/agents.js.map +1 -1
  9. package/dist/ai-client/factory.d.ts +0 -12
  10. package/dist/ai-client/factory.d.ts.map +1 -1
  11. package/dist/ai-client/factory.js +0 -32
  12. package/dist/ai-client/factory.js.map +1 -1
  13. package/dist/ai-client/index.js +1 -1
  14. package/dist/ai-client/index.js.map +1 -1
  15. package/dist/ai-client/providers/anthropic.d.ts.map +1 -1
  16. package/dist/ai-client/providers/anthropic.js +10 -4
  17. package/dist/ai-client/providers/anthropic.js.map +1 -1
  18. package/dist/ai-client/providers/openai.d.ts.map +1 -1
  19. package/dist/ai-client/providers/openai.js +8 -4
  20. package/dist/ai-client/providers/openai.js.map +1 -1
  21. package/dist/ai-client/providers/remote.d.ts +0 -1
  22. package/dist/ai-client/providers/remote.d.ts.map +1 -1
  23. package/dist/ai-client/providers/remote.js +11 -10
  24. package/dist/ai-client/providers/remote.js.map +1 -1
  25. package/dist/ai-client/types.d.ts +14 -0
  26. package/dist/ai-client/types.d.ts.map +1 -1
  27. package/dist/ai-client/types.js +17 -0
  28. package/dist/ai-client/types.js.map +1 -1
  29. package/dist/ai-client-factory.d.ts.map +1 -1
  30. package/dist/ai-client-factory.js +4 -4
  31. package/dist/ai-client-factory.js.map +1 -1
  32. package/dist/auth.d.ts.map +1 -1
  33. package/dist/auth.js +10 -12
  34. package/dist/auth.js.map +1 -1
  35. package/dist/cancellation.d.ts.map +1 -1
  36. package/dist/cancellation.js +3 -5
  37. package/dist/cancellation.js.map +1 -1
  38. package/dist/checkpoint.d.ts +1 -0
  39. package/dist/checkpoint.d.ts.map +1 -1
  40. package/dist/checkpoint.js +37 -4
  41. package/dist/checkpoint.js.map +1 -1
  42. package/dist/cli.js +132 -32
  43. package/dist/cli.js.map +1 -1
  44. package/dist/config.js +1 -1
  45. package/dist/config.js.map +1 -1
  46. package/dist/context-compressor.d.ts +1 -2
  47. package/dist/context-compressor.d.ts.map +1 -1
  48. package/dist/context-compressor.js +23 -18
  49. package/dist/context-compressor.js.map +1 -1
  50. package/dist/conversation.d.ts +1 -1
  51. package/dist/conversation.d.ts.map +1 -1
  52. package/dist/conversation.js +8 -7
  53. package/dist/conversation.js.map +1 -1
  54. package/dist/gui-subagent/action-parser/actionParser.js +2 -2
  55. package/dist/gui-subagent/action-parser/actionParser.js.map +1 -1
  56. package/dist/gui-subagent/agent/gui-agent.d.ts +10 -0
  57. package/dist/gui-subagent/agent/gui-agent.d.ts.map +1 -1
  58. package/dist/gui-subagent/agent/gui-agent.js +105 -32
  59. package/dist/gui-subagent/agent/gui-agent.js.map +1 -1
  60. package/dist/gui-subagent/index.d.ts +7 -0
  61. package/dist/gui-subagent/index.d.ts.map +1 -1
  62. package/dist/gui-subagent/index.js +2 -0
  63. package/dist/gui-subagent/index.js.map +1 -1
  64. package/dist/gui-subagent/operator/computer-operator.d.ts.map +1 -1
  65. package/dist/gui-subagent/operator/computer-operator.js +2 -0
  66. package/dist/gui-subagent/operator/computer-operator.js.map +1 -1
  67. package/dist/input-processor.js +2 -2
  68. package/dist/input-processor.js.map +1 -1
  69. package/dist/logger.d.ts.map +1 -1
  70. package/dist/logger.js +1 -1
  71. package/dist/logger.js.map +1 -1
  72. package/dist/mcp.d.ts +2 -1
  73. package/dist/mcp.d.ts.map +1 -1
  74. package/dist/mcp.js +83 -21
  75. package/dist/mcp.js.map +1 -1
  76. package/dist/memory.d.ts.map +1 -1
  77. package/dist/memory.js +3 -3
  78. package/dist/memory.js.map +1 -1
  79. package/dist/output-util.d.ts +27 -0
  80. package/dist/output-util.d.ts.map +1 -0
  81. package/dist/output-util.js +74 -0
  82. package/dist/output-util.js.map +1 -0
  83. package/dist/retry.js +1 -1
  84. package/dist/retry.js.map +1 -1
  85. package/dist/ripgrep.d.ts.map +1 -1
  86. package/dist/ripgrep.js +5 -3
  87. package/dist/ripgrep.js.map +1 -1
  88. package/dist/sdk-output-adapter.d.ts +265 -0
  89. package/dist/sdk-output-adapter.d.ts.map +1 -0
  90. package/dist/sdk-output-adapter.js +701 -0
  91. package/dist/sdk-output-adapter.js.map +1 -0
  92. package/dist/sdk-session.d.ts +13 -0
  93. package/dist/sdk-session.d.ts.map +1 -0
  94. package/dist/sdk-session.js +50 -0
  95. package/dist/sdk-session.js.map +1 -0
  96. package/dist/session-manager.js +3 -3
  97. package/dist/session-manager.js.map +1 -1
  98. package/dist/session.d.ts +96 -2
  99. package/dist/session.d.ts.map +1 -1
  100. package/dist/session.js +849 -262
  101. package/dist/session.js.map +1 -1
  102. package/dist/shell.d.ts.map +1 -1
  103. package/dist/shell.js +5 -4
  104. package/dist/shell.js.map +1 -1
  105. package/dist/skill-installer.js +3 -3
  106. package/dist/skill-installer.js.map +1 -1
  107. package/dist/skill-invoker.d.ts +1 -1
  108. package/dist/skill-invoker.d.ts.map +1 -1
  109. package/dist/skill-invoker.js +2 -2
  110. package/dist/skill-invoker.js.map +1 -1
  111. package/dist/skill-loader.js +6 -5
  112. package/dist/skill-loader.js.map +1 -1
  113. package/dist/skill-manager.d.ts.map +1 -1
  114. package/dist/skill-manager.js +3 -2
  115. package/dist/skill-manager.js.map +1 -1
  116. package/dist/slash-commands.d.ts +1 -1
  117. package/dist/slash-commands.d.ts.map +1 -1
  118. package/dist/slash-commands.js +24 -11
  119. package/dist/slash-commands.js.map +1 -1
  120. package/dist/smart-approval.d.ts +20 -1
  121. package/dist/smart-approval.d.ts.map +1 -1
  122. package/dist/smart-approval.js +58 -1
  123. package/dist/smart-approval.js.map +1 -1
  124. package/dist/system-prompt-generator.js +3 -3
  125. package/dist/system-prompt-generator.js.map +1 -1
  126. package/dist/theme.d.ts.map +1 -1
  127. package/dist/theme.js +8 -7
  128. package/dist/theme.js.map +1 -1
  129. package/dist/tools.d.ts +15 -0
  130. package/dist/tools.d.ts.map +1 -1
  131. package/dist/tools.js +487 -215
  132. package/dist/tools.js.map +1 -1
  133. package/dist/types.d.ts +57 -0
  134. package/dist/types.d.ts.map +1 -1
  135. package/dist/types.js +49 -0
  136. package/dist/types.js.map +1 -1
  137. package/dist/update.d.ts.map +1 -1
  138. package/dist/update.js +12 -9
  139. package/dist/update.js.map +1 -1
  140. package/dist/workflow.d.ts.map +1 -1
  141. package/dist/workflow.js +1 -2
  142. package/dist/workflow.js.map +1 -1
  143. package/docs/third-party-models.md +16 -15
  144. package/package.json +3 -1
  145. package/src/agents.ts +7 -3
  146. package/src/ai-client/factory.ts +1 -36
  147. package/src/ai-client/index.ts +1 -1
  148. package/src/ai-client/providers/anthropic.ts +12 -3
  149. package/src/ai-client/providers/openai.ts +10 -4
  150. package/src/ai-client/providers/remote.ts +13 -10
  151. package/src/ai-client/types.ts +19 -0
  152. package/src/ai-client-factory.ts +5 -5
  153. package/src/auth.ts +11 -13
  154. package/src/cancellation.ts +3 -6
  155. package/src/checkpoint.ts +40 -4
  156. package/src/cli.ts +154 -37
  157. package/src/config.ts +1 -1
  158. package/src/context-compressor.ts +28 -23
  159. package/src/conversation.ts +9 -7
  160. package/src/gui-subagent/action-parser/actionParser.ts +2 -2
  161. package/src/gui-subagent/agent/gui-agent.ts +117 -34
  162. package/src/gui-subagent/index.ts +8 -0
  163. package/src/gui-subagent/operator/computer-operator.ts +2 -1
  164. package/src/input-processor.ts +2 -2
  165. package/src/logger.ts +2 -4
  166. package/src/mcp.ts +86 -23
  167. package/src/memory.ts +3 -4
  168. package/src/output-util.ts +80 -0
  169. package/src/retry.ts +1 -1
  170. package/src/ripgrep.ts +5 -3
  171. package/src/sdk-output-adapter.ts +842 -0
  172. package/src/sdk-session.ts +62 -0
  173. package/src/session-manager.ts +3 -3
  174. package/src/session.ts +942 -302
  175. package/src/shell.ts +6 -5
  176. package/src/skill-installer.ts +3 -3
  177. package/src/skill-invoker.ts +3 -4
  178. package/src/skill-loader.ts +7 -7
  179. package/src/skill-manager.ts +4 -3
  180. package/src/slash-commands.ts +24 -16
  181. package/src/smart-approval.ts +76 -1
  182. package/src/system-prompt-generator.ts +3 -3
  183. package/src/theme.ts +9 -8
  184. package/src/tools.ts +563 -267
  185. package/src/types.ts +118 -0
  186. package/src/update.ts +12 -9
  187. package/src/workflow.ts +2 -4
  188. package/test/cli-launch.test.ts +279 -0
  189. package/vitest.config.ts +2 -0
  190. /package/{.eslintrc.js → .eslintrc.cjs} +0 -0
package/src/shell.ts CHANGED
@@ -1,10 +1,11 @@
1
1
  import { existsSync } from 'fs';
2
2
  import { spawn, spawnSync } from 'child_process';
3
+ import { output as logOutput } from './output-util.js';
3
4
 
4
5
  /**
5
6
  * Find bash executable on PATH (Windows).
6
7
  */
7
- function findBashOnPath(): string | null {
8
+ function _findBashOnPath(): string | null {
8
9
  try {
9
10
  const result = spawnSync('where', ['bash.exe'], { encoding: 'utf-8', timeout: 5000 });
10
11
  if (result.status === 0 && result.stdout) {
@@ -115,19 +116,19 @@ export function killProcessTree(pid: number): void {
115
116
  detached: true,
116
117
  });
117
118
  } catch (error) {
118
- console.warn(`[shell] Failed to kill process tree (PID ${pid}): ${error instanceof Error ? error.message : String(error)}`);
119
+ logOutput('warning', `[shell] Failed to kill process tree (PID ${pid})`, { error: error instanceof Error ? error.message : String(error) });
119
120
  }
120
121
  } else {
121
122
  // Use SIGKILL on Unix/Linux/Mac
122
123
  try {
123
124
  process.kill(-pid, 'SIGKILL');
124
- } catch (error) {
125
+ } catch {
125
126
  // Fallback to killing just the child if process group kill fails
126
127
  try {
127
128
  process.kill(pid, 'SIGKILL');
128
129
  } catch (fallbackError) {
129
- console.warn(`[shell] Failed to kill process (PID ${pid}): ${fallbackError instanceof Error ? fallbackError.message : String(fallbackError)}`);
130
+ logOutput('warning', `[shell] Failed to kill process (PID ${pid})`, { error: fallbackError instanceof Error ? fallbackError.message : String(fallbackError) });
130
131
  }
131
132
  }
132
133
  }
133
- }
134
+ }
@@ -12,7 +12,7 @@ import simpleGit from 'simple-git';
12
12
  import { getConfigManager } from './config.js';
13
13
  import { getLogger } from './logger.js';
14
14
 
15
- const logger = getLogger();
15
+ const _logger = getLogger();
16
16
  const CLONE_TIMEOUT_MS = 60000; // 60 seconds
17
17
 
18
18
  export interface RemoteSource {
@@ -317,8 +317,8 @@ async function installFromGitHub(source: RemoteSource): Promise<InstallResult> {
317
317
  return { success: false, error: 'Invalid GitHub URL' };
318
318
  }
319
319
 
320
- const [, owner, repo] = urlMatch;
321
- const cleanRepo = repo.replace(/\.git$/, '');
320
+ const [, _owner, repo] = urlMatch;
321
+ const _cleanRepo = repo.replace(/\.git$/, '');
322
322
  const ref = source.ref || 'main';
323
323
 
324
324
  let tempDir: string | null = null;
@@ -2,8 +2,7 @@ import fs from 'fs/promises';
2
2
  import path from 'path';
3
3
  import os from 'os';
4
4
  import { getSkillLoader, SkillInfo, SkillLoader } from './skill-loader.js';
5
- import { getToolRegistry } from './tools.js';
6
- import { ExecutionMode, Tool } from './types.js';
5
+ import { ExecutionMode } from './types.js';
7
6
  import { getConfigManager } from './config.js';
8
7
 
9
8
  // Re-export SkillInfo for other modules
@@ -579,7 +578,7 @@ export class SkillInvoker {
579
578
  * NOTE: SKILL_TRIGGERS disabled. Let LLM decide based on system prompt.
580
579
  * Returns null to indicate no explicit match - LLM should use its own judgment.
581
580
  */
582
- async matchSkill(userInput: string): Promise<SkillMatcherResult | null> {
581
+ async matchSkill(_userInput: string): Promise<SkillMatcherResult | null> {
583
582
  // SKILL_TRIGGERS is disabled for experiment purposes.
584
583
  // The LLM should decide which skill to use based on system prompt information.
585
584
  return null;
@@ -684,7 +683,7 @@ export class SkillInvoker {
684
683
  * Get executor for skill
685
684
  * Unified dynamic approach - all skills use GenericSkillExecutor
686
685
  */
687
- private getSkillExecutor(skillId: string): SkillExecutor {
686
+ private getSkillExecutor(_skillId: string): SkillExecutor {
688
687
  return new GenericSkillExecutor();
689
688
  }
690
689
 
@@ -1,7 +1,7 @@
1
1
  import fs from 'fs/promises';
2
- import fsSync from 'fs';
2
+ import _fsSync from 'fs';
3
3
  import path from 'path';
4
- import { fileURLToPath } from 'url';
4
+ import os from 'os';
5
5
  import { WorkflowConfig } from './workflow.js';
6
6
  import { getConfigManager } from './config.js';
7
7
 
@@ -59,7 +59,7 @@ export class SkillLoader {
59
59
  } else {
60
60
  const configManager = getConfigManager();
61
61
  const userPath = configManager.getUserSkillsPath();
62
- this.skillsRootPath = userPath || path.join(require('os').homedir(), '.xagent', 'skills');
62
+ this.skillsRootPath = userPath || path.join(os.homedir(), '.xagent', 'skills');
63
63
  }
64
64
  }
65
65
  async loadAllSkills(): Promise<SkillInfo[]> {
@@ -356,19 +356,19 @@ export class SkillLoader {
356
356
  // Try to extract frontmatter - support both formats:
357
357
  // 1. Standard YAML: ---name: docx...--- 2. No opening ---: name: docx...
358
358
  let frontmatter = '';
359
- let contentStart = 0;
359
+ let _contentStart = 0;
360
360
 
361
- const frontmatterMatch = normalizedContent.match(/^---\n([\s\S]*?)\n---/);
361
+ const frontmatterMatch = normalizedContent.match(/^---([\s\S]*?)\n---/);
362
362
  if (frontmatterMatch) {
363
363
  // Standard format with --- at start and end
364
364
  frontmatter = frontmatterMatch[1];
365
- contentStart = frontmatterMatch[0].length;
365
+ _contentStart = frontmatterMatch[0].length;
366
366
  } else {
367
367
  // Check for format without opening --- (just YAML at the start)
368
368
  const yamlMatch = normalizedContent.match(/^([\s\S]*?)\n---/);
369
369
  if (yamlMatch) {
370
370
  frontmatter = yamlMatch[1];
371
- contentStart = yamlMatch[0].length;
371
+ _contentStart = yamlMatch[0].length;
372
372
  }
373
373
  }
374
374
 
@@ -5,7 +5,8 @@
5
5
  * - Removes distinction between built-in and user skills
6
6
  */
7
7
  import fs from 'fs/promises';
8
- import fsSync from 'fs';
8
+ import os from 'os';
9
+ import _fsSync from 'fs';
9
10
  import path from 'path';
10
11
  import { fileURLToPath } from 'url';
11
12
  import { getConfigManager } from './config.js';
@@ -50,7 +51,7 @@ export class SkillManager {
50
51
  this.userSkillsRoot = config.userSkillsRoot;
51
52
  } else {
52
53
  this.userSkillsRoot = configManager.getUserSkillsPath() ||
53
- path.join(require('os').homedir(), '.xagent', 'skills');
54
+ path.join(os.homedir(), '.xagent', 'skills');
54
55
  }
55
56
  }
56
57
 
@@ -184,7 +185,7 @@ export class SkillManager {
184
185
  try {
185
186
  const resolvedPath = path.resolve(sourcePath);
186
187
  const skillName = path.basename(resolvedPath);
187
- const destPath = path.join(this.userSkillsRoot, skillName);
188
+ const _destPath = path.join(this.userSkillsRoot, skillName);
188
189
 
189
190
  // Check if source exists
190
191
  await fs.access(resolvedPath);
@@ -1,11 +1,9 @@
1
- import readline from 'readline';
2
1
  import { select, confirm, text } from '@clack/prompts';
3
2
  import chalk from 'chalk';
4
3
  import ora from 'ora';
5
4
  import fs from 'fs/promises';
6
5
  import path from 'path';
7
- import { ExecutionMode, ChatMessage, InputType, ToolCall, Checkpoint, AgentConfig, CompressionConfig, AuthType } from './types.js';
8
- import { Message, detectThinkingKeywords, getThinkingTokens } from './ai-client/types.js';
6
+ import { ExecutionMode, ChatMessage, InputType, Checkpoint, AgentConfig, CompressionConfig, AuthType } from './types.js';
9
7
  import { fetchDefaultModels } from './ai-client/providers/remote.js';
10
8
  import { getToolRegistry } from './tools.js';
11
9
  import { getAgentManager } from './agents.js';
@@ -21,7 +19,6 @@ import {
21
19
  } from './context-compressor.js';
22
20
  import { getConversationManager, ConversationManager } from './conversation.js';
23
21
  import { icons, colors } from './theme.js';
24
- import { SystemPromptGenerator } from './system-prompt-generator.js';
25
22
  import { ensureTtySane } from './terminal.js';
26
23
  import { AuthService, selectAuthType, ThirdPartyProvider, THIRD_PARTY_PROVIDERS, VLM_PROVIDERS, VLMProviderInfo } from './auth.js';
27
24
 
@@ -626,7 +623,6 @@ export class SlashCommandHandler {
626
623
  const success = await authService.authenticate();
627
624
 
628
625
  if (success) {
629
- const newConfig = this.configManager.getAuthConfig();
630
626
  console.log(chalk.green('\n✅ Login successful!'));
631
627
  console.log(chalk.cyan(` Token saved to: ~/.xagent/settings.json`));
632
628
  console.log(chalk.gray(' You can now use xAgent CLI with remote AI services.\n'));
@@ -1216,7 +1212,7 @@ export class SlashCommandHandler {
1216
1212
  }
1217
1213
 
1218
1214
  private async addMcpServerInteractive(serverName?: string): Promise<void> {
1219
- let name = (await text({
1215
+ const name = (await text({
1220
1216
  message: 'Enter MCP server name:',
1221
1217
  defaultValue: serverName,
1222
1218
  validate: (value: string | undefined) => {
@@ -1345,10 +1341,8 @@ export class SlashCommandHandler {
1345
1341
 
1346
1342
  this.mcpManager.registerServer(name, config);
1347
1343
 
1348
- let connected = false;
1349
1344
  try {
1350
1345
  await this.mcpManager.connectServer(name);
1351
- connected = true;
1352
1346
  } catch (error: any) {
1353
1347
  this.mcpManager.disconnectServer(name);
1354
1348
  this.configManager.removeMcpServer(name);
@@ -1403,11 +1397,6 @@ export class SlashCommandHandler {
1403
1397
 
1404
1398
  private async removeMcpServer(serverName: string): Promise<void> {
1405
1399
  try {
1406
- // Get server info before disconnecting to notify LLM
1407
- const server = this.mcpManager.getServer(serverName);
1408
- const removedTools = server ? server.getToolNames() : [];
1409
- const removedToolNames = removedTools.map((t: string) => `${serverName}__${t}`).join(', ');
1410
-
1411
1400
  // Disconnect
1412
1401
  this.mcpManager.disconnectServer(serverName);
1413
1402
 
@@ -1909,7 +1898,6 @@ export class SlashCommandHandler {
1909
1898
  private async handleSkill(args: string[]): Promise<void> {
1910
1899
  const os = await import('os');
1911
1900
  const path = await import('path');
1912
- const { fileURLToPath } = await import('url');
1913
1901
  const { promises: fs } = await import('fs');
1914
1902
 
1915
1903
  const action = args[0] || 'list';
@@ -2146,10 +2134,11 @@ export class SlashCommandHandler {
2146
2134
  }
2147
2135
  }
2148
2136
 
2149
- export function parseInput(input: string): InputType[] {
2137
+ export async function parseInput(input: string): Promise<InputType[]> {
2150
2138
  const inputs: InputType[] = [];
2151
2139
  let remaining = input;
2152
2140
 
2141
+ // Match @ followed by any non-whitespace sequence
2153
2142
  const fileRefRegex = /@([^\s]+)/g;
2154
2143
  let match;
2155
2144
  while ((match = fileRefRegex.exec(remaining)) !== null) {
@@ -2161,7 +2150,14 @@ export function parseInput(input: string): InputType[] {
2161
2150
  inputs.push({ type: 'text', content: beforeMatch.trim() });
2162
2151
  }
2163
2152
 
2164
- inputs.push({ type: 'file', content: filePath });
2153
+ // Only treat as file reference if the file actually exists
2154
+ const exists = await fileExists(filePath);
2155
+ if (exists) {
2156
+ inputs.push({ type: 'file', content: filePath });
2157
+ } else {
2158
+ // Not a file, treat as regular text (preserving the @ symbol)
2159
+ inputs.push({ type: 'text', content: '@' + filePath });
2160
+ }
2165
2161
  remaining = afterMatch;
2166
2162
  }
2167
2163
 
@@ -2176,6 +2172,18 @@ export function parseInput(input: string): InputType[] {
2176
2172
  return inputs;
2177
2173
  }
2178
2174
 
2175
+ // Helper function to check if a file path exists
2176
+ async function fileExists(filePath: string): Promise<boolean> {
2177
+ try {
2178
+ // Resolve to absolute path
2179
+ const absolutePath = path.resolve(filePath);
2180
+ await fs.access(absolutePath);
2181
+ return true;
2182
+ } catch {
2183
+ return false;
2184
+ }
2185
+ }
2186
+
2179
2187
  export function detectImageInput(input: string): boolean {
2180
2188
  return input.includes('[Pasted image') || input.includes('<image');
2181
2189
  }
@@ -6,6 +6,7 @@ import { AuthType } from './types.js';
6
6
  import { getLogger } from './logger.js';
7
7
  import { colors, icons } from './theme.js';
8
8
  import { getCancellationManager } from './cancellation.js';
9
+ import { SdkOutputAdapter } from './sdk-output-adapter.js';
9
10
 
10
11
  const logger = getLogger();
11
12
 
@@ -494,6 +495,8 @@ export class SmartApprovalEngine {
494
495
  private blacklistChecker: BlacklistChecker;
495
496
  private aiChecker: AIApprovalChecker;
496
497
  private debugMode: boolean;
498
+ private sdkAdapter: SdkOutputAdapter | null = null;
499
+ private isSdkMode: boolean = false;
497
500
 
498
501
  constructor(debugMode: boolean = false) {
499
502
  this.whitelistChecker = new WhitelistChecker();
@@ -502,6 +505,28 @@ export class SmartApprovalEngine {
502
505
  this.debugMode = debugMode;
503
506
  }
504
507
 
508
+ /**
509
+ * Set SDK mode and output adapter for SDK mode support
510
+ */
511
+ setSdkMode(enabled: boolean, adapter: SdkOutputAdapter | null): void {
512
+ this.isSdkMode = enabled;
513
+ this.sdkAdapter = adapter;
514
+ }
515
+
516
+ /**
517
+ * Check if in SDK mode
518
+ */
519
+ getIsSdkMode(): boolean {
520
+ return this.isSdkMode;
521
+ }
522
+
523
+ /**
524
+ * Get SDK adapter
525
+ */
526
+ getSdkAdapter(): SdkOutputAdapter | null {
527
+ return this.sdkAdapter;
528
+ }
529
+
505
530
  /**
506
531
  * Evaluate tool call
507
532
  */
@@ -583,7 +608,17 @@ export class SmartApprovalEngine {
583
608
  /**
584
609
  * Request user confirmation
585
610
  */
586
- async requestConfirmation(result: ApprovalResult): Promise<boolean> {
611
+ async requestConfirmation(
612
+ result: ApprovalResult,
613
+ toolName?: string,
614
+ toolParams?: Record<string, unknown>
615
+ ): Promise<boolean> {
616
+ // Check if in SDK mode with adapter
617
+ if (this.isSdkMode && this.sdkAdapter) {
618
+ return this.requestConfirmationSdk(result, toolName, toolParams);
619
+ }
620
+
621
+ // Regular TUI mode
587
622
  const separator = icons.separator.repeat(40);
588
623
  console.log('');
589
624
  console.log(
@@ -624,6 +659,46 @@ export class SmartApprovalEngine {
624
659
  }
625
660
  }
626
661
 
662
+ /**
663
+ * Request user confirmation in SDK mode
664
+ */
665
+ private async requestConfirmationSdk(
666
+ result: ApprovalResult,
667
+ toolName?: string,
668
+ toolParams?: Record<string, unknown>
669
+ ): Promise<boolean> {
670
+ const requestId = `approval_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
671
+
672
+ // Output approval request through SDK adapter
673
+ this.sdkAdapter!.outputApprovalRequest({
674
+ requestId,
675
+ toolName: toolName || 'unknown',
676
+ params: toolParams || {},
677
+ riskLevel: result.riskLevel,
678
+ description: result.description,
679
+ aiAnalysis: result.aiAnalysis
680
+ });
681
+
682
+ // Wait for SDK response using session
683
+ try {
684
+ const { getSingletonSession } = await import('./session.js');
685
+ const session = getSingletonSession();
686
+ if (!session) {
687
+ logger.error('SDK session not available');
688
+ return false;
689
+ }
690
+ const approved = await session.waitForApprovalResponse(requestId);
691
+ this.sdkAdapter!.outputApprovalResponse(requestId, approved);
692
+ return approved;
693
+ } catch (error) {
694
+ logger.error(
695
+ 'Failed to get SDK approval response',
696
+ error instanceof Error ? error.message : String(error)
697
+ );
698
+ return false;
699
+ }
700
+ }
701
+
627
702
  /**
628
703
  * Get risk level display
629
704
  */
@@ -44,7 +44,7 @@ export class SystemPromptGenerator {
44
44
  */
45
45
  private generateEnvironmentInfo(): string {
46
46
  const platform = os.platform();
47
- const arch = os.arch();
47
+ const _arch = os.arch();
48
48
  const nodeVersion = process.version;
49
49
  const cwd = process.cwd();
50
50
  const homeDir = os.homedir();
@@ -104,7 +104,7 @@ export class SystemPromptGenerator {
104
104
  }
105
105
 
106
106
  // Get available local tools (includes MCP wrapper tools registered via registerMCPTools)
107
- let allAvailableTools = localTools;
107
+ const allAvailableTools = localTools;
108
108
 
109
109
  let enhancedPrompt = baseSystemPrompt;
110
110
 
@@ -739,7 +739,7 @@ Remember: You are in a conversational mode, not a tool-execution mode. Just talk
739
739
  return output;
740
740
  }
741
741
 
742
- private generateDecisionMakingGuide(availableTools: any[], skills: SkillInfo[] = []): string {
742
+ private generateDecisionMakingGuide(availableTools: any[], _skills: SkillInfo[] = []): string {
743
743
  // Tool name to short description mapping
744
744
  const toolDescriptions: Record<string, string> = {
745
745
  'Read': 'When you need to understand existing code, configuration, or documentation',
package/src/theme.ts CHANGED
@@ -8,7 +8,7 @@ function getTerminalBackground(): 'dark' | 'light' {
8
8
  // Check common environment variables
9
9
  const colorfgbg = process.env.COLORFGBG; // e.g., "15;0" (light fg, dark bg)
10
10
  const termProgram = process.env.TERM_PROGRAM;
11
- const termProgramVersion = process.env.TERM_PROGRAM_VERSION;
11
+ const _termProgramVersion = process.env.TERM_PROGRAM_VERSION;
12
12
 
13
13
  // Try to parse COLORFGBG (format: "fg;bg" or "fg;color;bg")
14
14
  if (colorfgbg) {
@@ -82,7 +82,7 @@ interface SubAgentBoxOptions {
82
82
  accentColor?: ColorFunction;
83
83
  }
84
84
 
85
- interface BoxFunctions {
85
+ interface _BoxFunctions {
86
86
  single: (content: string, options?: BoxOptions) => string;
87
87
  double: (content: string, options?: BoxOptions) => string;
88
88
  minimal: (content: string, options?: Omit<BoxOptions, 'title' | 'titleAlign'>) => string;
@@ -284,13 +284,13 @@ export const styleHelpers = {
284
284
  const chars = styleHelpers.border.single;
285
285
  const availableWidth = width - 4;
286
286
 
287
- let lines: string[] = [];
287
+ const lines: string[] = [];
288
288
 
289
289
  if (title) {
290
290
  const titleContent = ` ${title} `;
291
291
  const paddingNeeded = availableWidth - titleContent.length;
292
- let leftPad = titleAlign === 'center' ? Math.floor(paddingNeeded / 2) : (titleAlign === 'right' ? paddingNeeded : 0);
293
- let rightPad = titleAlign === 'center' ? Math.ceil(paddingNeeded / 2) : (titleAlign === 'right' ? 0 : paddingNeeded);
292
+ const leftPad = titleAlign === 'center' ? Math.floor(paddingNeeded / 2) : (titleAlign === 'right' ? paddingNeeded : 0);
293
+ const rightPad = titleAlign === 'center' ? Math.ceil(paddingNeeded / 2) : (titleAlign === 'right' ? 0 : paddingNeeded);
294
294
 
295
295
  lines.push(`${indent}${chars.topLeft}${' '.repeat(leftPad)}${titleContent}${' '.repeat(rightPad)}${chars.topRight}`);
296
296
  } else {
@@ -314,13 +314,13 @@ export const styleHelpers = {
314
314
  const chars = styleHelpers.border.double;
315
315
  const availableWidth = width - 4;
316
316
 
317
- let lines: string[] = [];
317
+ const lines: string[] = [];
318
318
 
319
319
  if (title) {
320
320
  const titleContent = ` ${title} `;
321
321
  const paddingNeeded = availableWidth - titleContent.length;
322
- let leftPad = titleAlign === 'center' ? Math.floor(paddingNeeded / 2) : (titleAlign === 'right' ? paddingNeeded : 0);
323
- let rightPad = titleAlign === 'center' ? Math.ceil(paddingNeeded / 2) : (titleAlign === 'right' ? 0 : paddingNeeded);
322
+ const leftPad = titleAlign === 'center' ? Math.floor(paddingNeeded / 2) : (titleAlign === 'right' ? paddingNeeded : 0);
323
+ const rightPad = titleAlign === 'center' ? Math.ceil(paddingNeeded / 2) : (titleAlign === 'right' ? 0 : paddingNeeded);
324
324
 
325
325
  lines.push(`${indent}${chars.topLeft}${' '.repeat(leftPad)}${titleContent}${' '.repeat(rightPad)}${chars.topRight}`);
326
326
  } else {
@@ -364,6 +364,7 @@ export const styleHelpers = {
364
364
  const availableWidth = width - 2;
365
365
 
366
366
  const headerContent = `${colors.primaryBright(agentName)}: ${description}`;
367
+ // eslint-disable-next-line no-control-regex
367
368
  const headerContentLength = headerContent.replace(/\x1b\[[0-9;]*m/g, '').length;
368
369
  const headerFillLength = Math.max(0, availableWidth - 3 - headerContentLength);
369
370
  const headerLine = `${indent}${accentColor(chars.topLeft)}${accentColor('─── ')}${headerContent} ${accentColor('─'.repeat(headerFillLength))}${accentColor(chars.topRight)}`;