@code4bug/jarvis-agent 1.0.2

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 (80) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +227 -0
  3. package/dist/agents/code-reviewer.md +69 -0
  4. package/dist/agents/dba.md +68 -0
  5. package/dist/agents/finance-advisor.md +81 -0
  6. package/dist/agents/index.d.ts +31 -0
  7. package/dist/agents/index.js +86 -0
  8. package/dist/agents/jarvis.md +95 -0
  9. package/dist/agents/stock-trader.md +81 -0
  10. package/dist/cli.d.ts +2 -0
  11. package/dist/cli.js +9 -0
  12. package/dist/commands/index.d.ts +19 -0
  13. package/dist/commands/index.js +79 -0
  14. package/dist/commands/init.d.ts +15 -0
  15. package/dist/commands/init.js +283 -0
  16. package/dist/components/DangerConfirm.d.ts +17 -0
  17. package/dist/components/DangerConfirm.js +50 -0
  18. package/dist/components/MarkdownText.d.ts +12 -0
  19. package/dist/components/MarkdownText.js +166 -0
  20. package/dist/components/MessageItem.d.ts +8 -0
  21. package/dist/components/MessageItem.js +78 -0
  22. package/dist/components/MultilineInput.d.ts +34 -0
  23. package/dist/components/MultilineInput.js +437 -0
  24. package/dist/components/SlashCommandMenu.d.ts +16 -0
  25. package/dist/components/SlashCommandMenu.js +43 -0
  26. package/dist/components/StatusBar.d.ts +8 -0
  27. package/dist/components/StatusBar.js +26 -0
  28. package/dist/components/StreamingText.d.ts +6 -0
  29. package/dist/components/StreamingText.js +10 -0
  30. package/dist/components/WelcomeHeader.d.ts +6 -0
  31. package/dist/components/WelcomeHeader.js +25 -0
  32. package/dist/config/agentState.d.ts +16 -0
  33. package/dist/config/agentState.js +65 -0
  34. package/dist/config/constants.d.ts +25 -0
  35. package/dist/config/constants.js +67 -0
  36. package/dist/config/loader.d.ts +30 -0
  37. package/dist/config/loader.js +64 -0
  38. package/dist/config/systemInfo.d.ts +12 -0
  39. package/dist/config/systemInfo.js +95 -0
  40. package/dist/core/QueryEngine.d.ts +52 -0
  41. package/dist/core/QueryEngine.js +246 -0
  42. package/dist/core/hint.d.ts +14 -0
  43. package/dist/core/hint.js +279 -0
  44. package/dist/core/query.d.ts +24 -0
  45. package/dist/core/query.js +245 -0
  46. package/dist/core/safeguard.d.ts +96 -0
  47. package/dist/core/safeguard.js +236 -0
  48. package/dist/hooks/useFocus.d.ts +12 -0
  49. package/dist/hooks/useFocus.js +35 -0
  50. package/dist/hooks/useInputHistory.d.ts +14 -0
  51. package/dist/hooks/useInputHistory.js +102 -0
  52. package/dist/index.d.ts +1 -0
  53. package/dist/index.js +6 -0
  54. package/dist/screens/repl.d.ts +1 -0
  55. package/dist/screens/repl.js +842 -0
  56. package/dist/services/api/llm.d.ts +27 -0
  57. package/dist/services/api/llm.js +314 -0
  58. package/dist/services/api/mock.d.ts +9 -0
  59. package/dist/services/api/mock.js +102 -0
  60. package/dist/skills/index.d.ts +23 -0
  61. package/dist/skills/index.js +232 -0
  62. package/dist/skills/loader.d.ts +45 -0
  63. package/dist/skills/loader.js +108 -0
  64. package/dist/tools/createSkill.d.ts +8 -0
  65. package/dist/tools/createSkill.js +255 -0
  66. package/dist/tools/index.d.ts +16 -0
  67. package/dist/tools/index.js +23 -0
  68. package/dist/tools/listDirectory.d.ts +2 -0
  69. package/dist/tools/listDirectory.js +20 -0
  70. package/dist/tools/readFile.d.ts +2 -0
  71. package/dist/tools/readFile.js +17 -0
  72. package/dist/tools/runCommand.d.ts +2 -0
  73. package/dist/tools/runCommand.js +69 -0
  74. package/dist/tools/searchFiles.d.ts +2 -0
  75. package/dist/tools/searchFiles.js +45 -0
  76. package/dist/tools/writeFile.d.ts +2 -0
  77. package/dist/tools/writeFile.js +42 -0
  78. package/dist/types/index.d.ts +86 -0
  79. package/dist/types/index.js +2 -0
  80. package/package.json +55 -0
@@ -0,0 +1,27 @@
1
+ /**
2
+ * 真实 LLM 服务 - 支持 OpenAI 兼容 API(流式)
3
+ *
4
+ * 配置来源(优先级从低到高):
5
+ * 1. 环境变量 API_KEY / LLM_MODEL / API_BASE_URL
6
+ * 2. ~/.jarvis/config.json
7
+ * 3. ./.jarvis/config.json
8
+ */
9
+ import { LLMService, StreamCallbacks, TranscriptMessage, Tool, AbortSignal as AppAbortSignal } from '../../types/index.js';
10
+ import { ModelConfig } from '../../config/loader.js';
11
+ export interface LLMConfig {
12
+ apiKey: string;
13
+ model: string;
14
+ maxTokens: number;
15
+ baseUrl?: string;
16
+ temperature?: number;
17
+ }
18
+ /** 从配置文件构建 LLMConfig,找不到则回退环境变量 */
19
+ export declare function getDefaultConfig(): LLMConfig;
20
+ /** 将 ModelConfig 转为 LLMConfig */
21
+ export declare function fromModelConfig(mc: ModelConfig): LLMConfig;
22
+ export declare class LLMServiceImpl implements LLMService {
23
+ private config;
24
+ private systemPrompt;
25
+ constructor(config?: LLMConfig);
26
+ streamMessage(transcript: TranscriptMessage[], tools: Tool[], callbacks: StreamCallbacks, abortSignal?: AppAbortSignal): Promise<void>;
27
+ }
@@ -0,0 +1,314 @@
1
+ /**
2
+ * 真实 LLM 服务 - 支持 OpenAI 兼容 API(流式)
3
+ *
4
+ * 配置来源(优先级从低到高):
5
+ * 1. 环境变量 API_KEY / LLM_MODEL / API_BASE_URL
6
+ * 2. ~/.jarvis/config.json
7
+ * 3. ./.jarvis/config.json
8
+ */
9
+ import { loadConfig, getActiveModel } from '../../config/loader.js';
10
+ import { getAgent } from '../../agents/index.js';
11
+ import { DEFAULT_AGENT } from '../../config/constants.js';
12
+ import { getActiveAgent } from '../../config/agentState.js';
13
+ import { getSystemInfoPrompt } from '../../config/systemInfo.js';
14
+ /** 从配置文件构建 LLMConfig,找不到则回退环境变量 */
15
+ export function getDefaultConfig() {
16
+ const jarvisCfg = loadConfig();
17
+ const active = getActiveModel(jarvisCfg);
18
+ if (active) {
19
+ return fromModelConfig(active);
20
+ }
21
+ // 回退:环境变量
22
+ return {
23
+ apiKey: process.env.API_KEY || '',
24
+ model: process.env.LLM_MODEL || 'claude-sonnet-4-20250514',
25
+ maxTokens: 4096,
26
+ baseUrl: process.env.API_BASE_URL,
27
+ };
28
+ }
29
+ /** 将 ModelConfig 转为 LLMConfig */
30
+ export function fromModelConfig(mc) {
31
+ return {
32
+ apiKey: mc.api_key,
33
+ model: mc.model,
34
+ maxTokens: mc.max_tokens ?? 4096,
35
+ baseUrl: mc.api_url,
36
+ temperature: mc.temperature,
37
+ };
38
+ }
39
+ /** 将内部 TranscriptMessage[] 转为 OpenAI messages 格式 */
40
+ function toOpenAIMessages(transcript, systemPrompt) {
41
+ const messages = [];
42
+ // 系统提示 — 来自当前激活的智能体定义
43
+ messages.push({
44
+ role: 'system',
45
+ content: systemPrompt,
46
+ });
47
+ for (const msg of transcript) {
48
+ if (msg.role === 'user') {
49
+ messages.push({ role: 'user', content: msg.content });
50
+ }
51
+ else if (msg.role === 'assistant') {
52
+ // 兼容 content 为 string(旧会话恢复)或 ContentBlock[] 两种情况
53
+ if (typeof msg.content === 'string') {
54
+ messages.push({ role: 'assistant', content: msg.content || '(empty)' });
55
+ }
56
+ else {
57
+ const blocks = msg.content;
58
+ let text = '';
59
+ const toolCalls = [];
60
+ for (const block of blocks) {
61
+ if (block.type === 'text') {
62
+ text += block.text;
63
+ }
64
+ else if (block.type === 'tool_use') {
65
+ toolCalls.push({
66
+ id: block.id,
67
+ type: 'function',
68
+ function: {
69
+ name: block.name,
70
+ arguments: JSON.stringify(block.input),
71
+ },
72
+ });
73
+ }
74
+ }
75
+ const assistantMsg = { role: 'assistant' };
76
+ // 确保 content 不为空,避免 API MissingParameter 错误
77
+ assistantMsg.content = text || null;
78
+ if (toolCalls.length > 0)
79
+ assistantMsg.tool_calls = toolCalls;
80
+ messages.push(assistantMsg);
81
+ }
82
+ }
83
+ else if (msg.role === 'tool_result') {
84
+ messages.push({
85
+ role: 'tool',
86
+ tool_call_id: msg.toolUseId || '',
87
+ content: msg.content,
88
+ });
89
+ }
90
+ }
91
+ return messages;
92
+ }
93
+ /** 将内部 Tool[] 转为 OpenAI tools 格式 */
94
+ function toOpenAITools(tools) {
95
+ if (tools.length === 0)
96
+ return [];
97
+ return tools.map((t) => ({
98
+ type: 'function',
99
+ function: {
100
+ name: t.name,
101
+ description: t.description,
102
+ parameters: {
103
+ type: 'object',
104
+ properties: Object.fromEntries(Object.entries(t.parameters).map(([k, v]) => [k, { type: v.type, description: v.description }])),
105
+ required: Object.entries(t.parameters)
106
+ .filter(([, v]) => v.required !== false)
107
+ .map(([k]) => k),
108
+ },
109
+ },
110
+ }));
111
+ }
112
+ /** 解析单行 SSE data,返回 delta 或 null([DONE] / 空行) */
113
+ function parseSSELine(line) {
114
+ if (!line.startsWith('data: '))
115
+ return null;
116
+ const data = line.slice(6).trim();
117
+ if (data === '[DONE]')
118
+ return null;
119
+ try {
120
+ const parsed = JSON.parse(data);
121
+ return parsed.choices?.[0]?.delta ?? null;
122
+ }
123
+ catch {
124
+ return null;
125
+ }
126
+ }
127
+ // ===== LLMServiceImpl =====
128
+ export class LLMServiceImpl {
129
+ config;
130
+ systemPrompt;
131
+ constructor(config) {
132
+ this.config = config ?? getDefaultConfig();
133
+ if (!this.config.apiKey) {
134
+ throw new Error('API_KEY 未配置。请在 .jarvis/config.json 或环境变量中设置。');
135
+ }
136
+ // 从当前激活的智能体加载 system prompt(运行时动态读取)
137
+ const currentAgent = getActiveAgent(DEFAULT_AGENT);
138
+ const agent = getAgent(currentAgent);
139
+ const agentPrompt = agent?.systemPrompt
140
+ ?? '你是一个强大的终端智能助手。回答时使用中文,简洁明了。当需要操作文件系统或执行命令时,请调用对应的工具。';
141
+ // 追加通用角色边界约束(兜底)+ 安全围栏说明
142
+ const roleBoundary = '\n\n---\n[系统约束] 你必须严格遵守智能体定义中的角色边界。' +
143
+ '只回答与你角色职责相关的问题。' +
144
+ '对于超出角色范围的请求,礼貌拒绝并引导用户回到你的专业领域。' +
145
+ '不要为了讨好用户而突破角色边界。' +
146
+ '\n\n[安全围栏] 系统已内置安全围栏(Safeguard),会自动拦截危险命令并弹出交互式确认菜单。' +
147
+ '当你需要执行任何命令时,直接调用 Bash 工具即可,不要自行判断命令是否危险,不要用文字询问用户"是否确认执行"。' +
148
+ '安全围栏会自动处理拦截和用户确认流程。';
149
+ // 追加系统环境信息,帮助 LLM 感知用户运行环境
150
+ const systemInfo = getSystemInfoPrompt();
151
+ this.systemPrompt = agentPrompt + roleBoundary + systemInfo;
152
+ }
153
+ async streamMessage(transcript, tools, callbacks, abortSignal) {
154
+ const messages = toOpenAIMessages(transcript, this.systemPrompt);
155
+ const openaiTools = toOpenAITools(tools);
156
+ const body = {
157
+ model: this.config.model,
158
+ messages,
159
+ max_tokens: this.config.maxTokens,
160
+ stream: true,
161
+ };
162
+ if (this.config.temperature !== undefined) {
163
+ body.temperature = this.config.temperature;
164
+ }
165
+ if (openaiTools.length > 0) {
166
+ body.tools = openaiTools;
167
+ body.tool_choice = 'auto';
168
+ }
169
+ const url = this.config.baseUrl || 'https://api.openai.com/v1/chat/completions';
170
+ // 创建 AbortController 用于取消 HTTP 请求
171
+ const controller = new AbortController();
172
+ // 如果外部已经 abort,直接返回
173
+ if (abortSignal?.aborted) {
174
+ callbacks.onComplete();
175
+ return;
176
+ }
177
+ let response;
178
+ try {
179
+ response = await fetch(url, {
180
+ method: 'POST',
181
+ headers: {
182
+ 'Content-Type': 'application/json',
183
+ Authorization: `Bearer ${this.config.apiKey}`,
184
+ },
185
+ body: JSON.stringify(body),
186
+ signal: controller.signal,
187
+ });
188
+ }
189
+ catch (err) {
190
+ if (err.name === 'AbortError') {
191
+ callbacks.onComplete();
192
+ return;
193
+ }
194
+ callbacks.onError(new Error(`网络请求失败: ${err.message}`));
195
+ return;
196
+ }
197
+ if (!response.ok) {
198
+ let errBody = '';
199
+ try {
200
+ errBody = await response.text();
201
+ }
202
+ catch { /* ignore */ }
203
+ callbacks.onError(new Error(`API 错误 ${response.status}: ${errBody.slice(0, 500)}`));
204
+ return;
205
+ }
206
+ // 流式读取 SSE
207
+ const reader = response.body?.getReader();
208
+ if (!reader) {
209
+ callbacks.onError(new Error('无法获取响应流'));
210
+ return;
211
+ }
212
+ const decoder = new TextDecoder();
213
+ let buffer = '';
214
+ // 用于累积 tool_calls(可能跨多个 chunk)
215
+ const pendingToolCalls = new Map();
216
+ try {
217
+ while (true) {
218
+ // 检查是否需要中断
219
+ if (abortSignal?.aborted) {
220
+ controller.abort();
221
+ break;
222
+ }
223
+ const { done, value } = await reader.read();
224
+ if (done)
225
+ break;
226
+ // 读取后再次检查
227
+ if (abortSignal?.aborted) {
228
+ controller.abort();
229
+ break;
230
+ }
231
+ buffer += decoder.decode(value, { stream: true });
232
+ const lines = buffer.split('\n');
233
+ // 最后一行可能不完整,保留
234
+ buffer = lines.pop() || '';
235
+ for (const line of lines) {
236
+ const delta = parseSSELine(line);
237
+ if (!delta)
238
+ continue;
239
+ // 思考过程(reasoning_content)
240
+ if (delta.reasoning_content && callbacks.onThinking) {
241
+ callbacks.onThinking(delta.reasoning_content);
242
+ }
243
+ // 文本内容
244
+ if (delta.content) {
245
+ callbacks.onText(delta.content);
246
+ }
247
+ // 工具调用(增量拼接)
248
+ if (delta.tool_calls) {
249
+ for (const tc of delta.tool_calls) {
250
+ const idx = tc.index;
251
+ if (!pendingToolCalls.has(idx)) {
252
+ pendingToolCalls.set(idx, { id: '', name: '', args: '' });
253
+ }
254
+ const entry = pendingToolCalls.get(idx);
255
+ if (tc.id)
256
+ entry.id = tc.id;
257
+ if (tc.function?.name)
258
+ entry.name += tc.function.name;
259
+ if (tc.function?.arguments)
260
+ entry.args += tc.function.arguments;
261
+ }
262
+ }
263
+ }
264
+ }
265
+ // 处理 buffer 中剩余的数据
266
+ if (buffer.trim()) {
267
+ const delta = parseSSELine(buffer);
268
+ if (delta?.reasoning_content && callbacks.onThinking)
269
+ callbacks.onThinking(delta.reasoning_content);
270
+ if (delta?.content)
271
+ callbacks.onText(delta.content);
272
+ if (delta?.tool_calls) {
273
+ for (const tc of delta.tool_calls) {
274
+ const idx = tc.index;
275
+ if (!pendingToolCalls.has(idx)) {
276
+ pendingToolCalls.set(idx, { id: '', name: '', args: '' });
277
+ }
278
+ const entry = pendingToolCalls.get(idx);
279
+ if (tc.id)
280
+ entry.id = tc.id;
281
+ if (tc.function?.name)
282
+ entry.name += tc.function.name;
283
+ if (tc.function?.arguments)
284
+ entry.args += tc.function.arguments;
285
+ }
286
+ }
287
+ }
288
+ // 流结束后,触发工具调用回调(只取第一个,与 agentic loop 单步设计一致)
289
+ if (pendingToolCalls.size > 0) {
290
+ const first = pendingToolCalls.get(0);
291
+ if (first && first.name) {
292
+ let input = {};
293
+ try {
294
+ input = JSON.parse(first.args);
295
+ }
296
+ catch { /* 参数解析失败则传空 */ }
297
+ callbacks.onToolUse(first.id, first.name, input);
298
+ return; // tool_use 时不触发 onComplete
299
+ }
300
+ }
301
+ callbacks.onComplete();
302
+ }
303
+ catch (err) {
304
+ if (err.name === 'AbortError' || abortSignal?.aborted) {
305
+ callbacks.onComplete();
306
+ return;
307
+ }
308
+ callbacks.onError(new Error(`流式读取失败: ${err.message}`));
309
+ }
310
+ finally {
311
+ reader.releaseLock();
312
+ }
313
+ }
314
+ }
@@ -0,0 +1,9 @@
1
+ import { LLMService, StreamCallbacks, TranscriptMessage, Tool, AbortSignal as AppAbortSignal } from '../../types/index.js';
2
+ /**
3
+ * Mock LLM 服务 - 模拟智能体行为,支持工具调用
4
+ */
5
+ export declare class MockService implements LLMService {
6
+ streamMessage(transcript: TranscriptMessage[], _tools: Tool[], callbacks: StreamCallbacks, abortSignal?: AppAbortSignal): Promise<void>;
7
+ /** 模拟流式逐字输出 */
8
+ private streamText;
9
+ }
@@ -0,0 +1,102 @@
1
+ /**
2
+ * Mock LLM 服务 - 模拟智能体行为,支持工具调用
3
+ */
4
+ export class MockService {
5
+ async streamMessage(transcript, _tools, callbacks, abortSignal) {
6
+ const lastMsg = transcript[transcript.length - 1];
7
+ const userText = typeof lastMsg?.content === 'string'
8
+ ? lastMsg.content
9
+ : '';
10
+ // 检查是否是工具结果的后续回复
11
+ const hasToolResult = transcript.some((m) => m.role === 'tool_result');
12
+ if (hasToolResult && !userText) {
13
+ // 对工具结果做总结
14
+ const toolResultMsg = [...transcript].reverse().find((m) => m.role === 'tool_result');
15
+ const resultText = typeof toolResultMsg?.content === 'string' ? toolResultMsg.content : '';
16
+ const summary = `以上是工具执行的结果。\n\n${resultText.slice(0, 200)}${resultText.length > 200 ? '...' : ''}`;
17
+ if (await this.streamText(summary, callbacks, abortSignal))
18
+ return;
19
+ callbacks.onComplete();
20
+ return;
21
+ }
22
+ const input = userText.toLowerCase().trim();
23
+ // 路由:根据用户输入决定是否调用工具
24
+ if (input.includes('ls') || input.includes('列出') || input.includes('目录')) {
25
+ if (await this.streamText('好的,让我查看一下当前目录的内容。\n', callbacks, abortSignal))
26
+ return;
27
+ callbacks.onToolUse('tool_1', 'list_directory', { path: '.' });
28
+ return;
29
+ }
30
+ if (input.includes('读取') || input.includes('read') || input.includes('查看文件')) {
31
+ const fileMatch = userText.match(/(?:读取|read|查看文件)\s+(\S+)/i);
32
+ const filePath = fileMatch?.[1] || 'package.json';
33
+ if (await this.streamText(`好的,我来读取文件 ${filePath}。\n`, callbacks, abortSignal))
34
+ return;
35
+ callbacks.onToolUse('tool_2', 'read_file', { path: filePath });
36
+ return;
37
+ }
38
+ if (input.includes('搜索') || input.includes('search') || input.includes('查找')) {
39
+ const keyword = userText.replace(/^.*(搜索|search|查找)\s*/i, '').trim() || 'TODO';
40
+ if (await this.streamText(`好的,我来搜索包含 "${keyword}" 的文件。\n`, callbacks, abortSignal))
41
+ return;
42
+ callbacks.onToolUse('tool_3', 'search_files', { path: '.', pattern: keyword });
43
+ return;
44
+ }
45
+ if (input.includes('执行') || input.includes('运行') || input.includes('run')) {
46
+ const cmd = userText.replace(/^.*(执行|运行|run)\s*/i, '').trim() || 'echo hello';
47
+ if (await this.streamText(`好的,我来执行命令: \`${cmd}\`\n`, callbacks, abortSignal))
48
+ return;
49
+ callbacks.onToolUse('tool_4', 'run_command', { command: cmd });
50
+ return;
51
+ }
52
+ if (input.includes('时间') || input.includes('time') || input.includes('几点')) {
53
+ if (await this.streamText(`当前时间是: ${new Date().toLocaleString('zh-CN')}`, callbacks, abortSignal))
54
+ return;
55
+ callbacks.onComplete();
56
+ return;
57
+ }
58
+ if (input.includes('帮助') || input.includes('help')) {
59
+ const helpText = [
60
+ '我是 Jarvis,你的终端智能助手。我可以帮你:',
61
+ '',
62
+ '文件操作:「列出目录」「读取 <文件>」「搜索 <关键词>」',
63
+ '执行命令:「执行 <命令>」「运行 ls -la」',
64
+ '实用工具:「时间」「帮助」',
65
+ '',
66
+ '快捷键:Ctrl+L 清屏 | Ctrl+C 退出 | Esc 终止任务/清空输入',
67
+ ].join('\n');
68
+ if (await this.streamText(helpText, callbacks, abortSignal))
69
+ return;
70
+ callbacks.onComplete();
71
+ return;
72
+ }
73
+ // 默认回复
74
+ const reply = `你好!我是 Jarvis 智能助手。你说了「${userText}」。\n\n输入「帮助」查看我能做什么。`;
75
+ // 模拟思考过程
76
+ if (callbacks.onThinking) {
77
+ const thinkText = `分析用户输入: "${userText}"\n考虑最佳回复方式...`;
78
+ for (const char of thinkText) {
79
+ if (abortSignal?.aborted)
80
+ return;
81
+ callbacks.onThinking(char);
82
+ await sleep(10);
83
+ }
84
+ }
85
+ if (await this.streamText(reply, callbacks, abortSignal))
86
+ return;
87
+ callbacks.onComplete();
88
+ }
89
+ /** 模拟流式逐字输出 */
90
+ async streamText(text, callbacks, abortSignal) {
91
+ for (const char of text) {
92
+ if (abortSignal?.aborted)
93
+ return true;
94
+ callbacks.onText(char);
95
+ await sleep(15 + Math.random() * 20);
96
+ }
97
+ return false;
98
+ }
99
+ }
100
+ function sleep(ms) {
101
+ return new Promise((r) => setTimeout(r, ms));
102
+ }
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Skill 注册中心
3
+ *
4
+ * 负责:
5
+ * 1. 启动时扫描 ~/.jarvis/skills/ 加载外部 skill
6
+ * 2. 将 skill 转换为 Tool 格式,与系统内置 tools 合并
7
+ * 3. 提供统一的工具查找接口
8
+ *
9
+ * 如果 skill 目录下存在 skill.py,execute 时直接调用 Python 脚本获取真实结果;
10
+ * 否则回退为返回 skill 指令文本(由 LLM 解释执行)。
11
+ */
12
+ import { Tool } from '../types/index.js';
13
+ import { SkillDefinition } from './loader.js';
14
+ /** 加载所有外部 skills(带缓存) */
15
+ export declare function loadExternalSkills(): SkillDefinition[];
16
+ /** 获取合并后的所有工具:内置 tools + 外部 skills */
17
+ export declare function getMergedTools(): Tool[];
18
+ /** 从合并工具列表中按名称查找 */
19
+ export declare function findMergedTool(name: string): Tool | undefined;
20
+ /** 获取已加载的 skill 列表(用于斜杠命令等) */
21
+ export declare function listSkills(): SkillDefinition[];
22
+ /** 清除缓存,强制下次重新扫描 */
23
+ export declare function reloadSkills(): void;