pigpig-agent 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. package/.skills/code-review/SKILL.md +42 -0
  2. package/dist/agent/loop-detection.js +135 -0
  3. package/dist/agent/loop.js +123 -0
  4. package/dist/agent/retry.js +39 -0
  5. package/dist/agents/registry.js +59 -0
  6. package/dist/agents/spawn.js +124 -0
  7. package/dist/agents/types.js +5 -0
  8. package/dist/commands/agent.js +33 -0
  9. package/dist/commands/context.js +27 -0
  10. package/dist/commands/cron.js +38 -0
  11. package/dist/commands/debug.js +44 -0
  12. package/dist/commands/dream.js +36 -0
  13. package/dist/commands/index.js +10 -0
  14. package/dist/commands/memory.js +40 -0
  15. package/dist/commands/plugin.js +73 -0
  16. package/dist/commands/rag.js +25 -0
  17. package/dist/commands/security.js +44 -0
  18. package/dist/commands/skill.js +84 -0
  19. package/dist/config/init.js +69 -0
  20. package/dist/config/loader.js +52 -0
  21. package/dist/config/schema.js +55 -0
  22. package/dist/context/compressor.js +165 -0
  23. package/dist/context/defense.js +201 -0
  24. package/dist/context/prompt-builder.js +56 -0
  25. package/dist/context/prompt-pipes.js +14 -0
  26. package/dist/context/tool-result-output.js +25 -0
  27. package/dist/context/view.js +185 -0
  28. package/dist/cron/parser.js +27 -0
  29. package/dist/cron/service.js +211 -0
  30. package/dist/cron/store.js +53 -0
  31. package/dist/cron/types.js +1 -0
  32. package/dist/index.js +9 -0
  33. package/dist/main.js +270 -0
  34. package/dist/memory/store.js +175 -0
  35. package/dist/memory/validator.js +62 -0
  36. package/dist/mock-model.js +534 -0
  37. package/dist/plugins/manager.js +97 -0
  38. package/dist/plugins/supabase-plugin.js +111 -0
  39. package/dist/plugins/types.js +1 -0
  40. package/dist/rag/chunker.js +54 -0
  41. package/dist/rag/embedder.js +53 -0
  42. package/dist/rag/search.js +123 -0
  43. package/dist/rag/sqlite-store.js +195 -0
  44. package/dist/rag/store.js +29 -0
  45. package/dist/security/bash-classifier.js +39 -0
  46. package/dist/security/hook.js +53 -0
  47. package/dist/security/roles.js +16 -0
  48. package/dist/session/store.js +60 -0
  49. package/dist/skills/loader.js +100 -0
  50. package/dist/tools/cron-tools.js +94 -0
  51. package/dist/tools/file-tools.js +115 -0
  52. package/dist/tools/index.js +17 -0
  53. package/dist/tools/mcp-client.js +100 -0
  54. package/dist/tools/memory-tools.js +81 -0
  55. package/dist/tools/rag-tools.js +54 -0
  56. package/dist/tools/registry.js +270 -0
  57. package/dist/tools/search-tools.js +116 -0
  58. package/dist/tools/shell-tools.js +39 -0
  59. package/dist/tools/spawn-tools.js +35 -0
  60. package/dist/tools/tool-search.js +17 -0
  61. package/dist/tools/web-search.js +150 -0
  62. package/dist/usage/tracker.js +83 -0
  63. package/package.json +55 -0
  64. package/readme.md +131 -0
@@ -0,0 +1,211 @@
1
+ import { parseSchedule, getNextCronTime } from './parser.js';
2
+ import { CronStore } from './store.js';
3
+ const QUOTES = [
4
+ '"知之为知之,不知为不知,是知也。" —— 孔子',
5
+ '"学而不思则罔,思而不学则殆。" —— 孔子',
6
+ '"千里之行,始于足下。" —— 老子',
7
+ '"天行健,君子以自强不息。" —— 《周易》',
8
+ '"不积跬步,无以至千里。" —— 荀子',
9
+ '"Stay hungry, stay foolish." —— Steve Jobs',
10
+ '"The best way to predict the future is to invent it." —— Alan Kay',
11
+ '"Talk is cheap. Show me the code." —— Linus Torvalds',
12
+ '"Simplicity is the ultimate sophistication." —— Leonardo da Vinci',
13
+ '"First, solve the problem. Then, write the code." —— John Johnson',
14
+ ];
15
+ export class CronService {
16
+ jobs = new Map();
17
+ store;
18
+ executor;
19
+ running = false;
20
+ constructor(baseDir = '.') {
21
+ this.store = new CronStore(baseDir); // 初始化存储
22
+ this.store.init();
23
+ }
24
+ setExecutor(executor) {
25
+ this.executor = executor; // 设置执行器
26
+ }
27
+ load() {
28
+ const configs = this.store.loadJobs(); // 读到所有任务配置
29
+ for (const config of configs) {
30
+ if (config.enabled) {
31
+ this.jobs.set(config.id, {
32
+ config, timerId: null, consecutiveFailures: 0, running: false,
33
+ });
34
+ }
35
+ }
36
+ }
37
+ start() {
38
+ if (this.running)
39
+ return;
40
+ this.running = true;
41
+ for (const state of this.jobs.values()) {
42
+ if (state.config.enabled)
43
+ this.scheduleJob(state);
44
+ }
45
+ }
46
+ stop() {
47
+ this.running = false;
48
+ for (const state of this.jobs.values()) {
49
+ if (state.timerId) {
50
+ clearTimeout(state.timerId);
51
+ state.timerId = null;
52
+ }
53
+ }
54
+ }
55
+ add(config) {
56
+ if (this.jobs.has(config.id))
57
+ throw new Error(`任务 ${config.id} 已存在`);
58
+ const state = { config, timerId: null, consecutiveFailures: 0, running: false };
59
+ this.jobs.set(config.id, state);
60
+ this.persist();
61
+ if (this.running && config.enabled)
62
+ this.scheduleJob(state);
63
+ }
64
+ remove(id) {
65
+ const state = this.jobs.get(id);
66
+ if (!state)
67
+ return false;
68
+ if (state.timerId)
69
+ clearTimeout(state.timerId);
70
+ this.jobs.delete(id);
71
+ this.persist();
72
+ return true;
73
+ }
74
+ enable(id) {
75
+ const state = this.jobs.get(id);
76
+ if (!state)
77
+ return false;
78
+ state.config.enabled = true;
79
+ state.consecutiveFailures = 0;
80
+ this.persist();
81
+ if (this.running)
82
+ this.scheduleJob(state);
83
+ return true;
84
+ }
85
+ disable(id) {
86
+ const state = this.jobs.get(id);
87
+ if (!state)
88
+ return false;
89
+ state.config.enabled = false;
90
+ if (state.timerId) {
91
+ clearTimeout(state.timerId);
92
+ state.timerId = null;
93
+ }
94
+ this.persist();
95
+ return true;
96
+ }
97
+ list() {
98
+ return Array.from(this.jobs.values()).map(state => ({
99
+ config: state.config,
100
+ status: state.running ? 'running'
101
+ : !state.config.enabled ? 'disabled'
102
+ : state.timerId ? 'scheduled' : 'idle',
103
+ lastRun: state.lastRun,
104
+ }));
105
+ }
106
+ async runNow(id) {
107
+ const state = this.jobs.get(id);
108
+ if (!state)
109
+ return `任务 ${id} 不存在`;
110
+ return this.executeJob(state);
111
+ }
112
+ getRecentLogs(jobId, limit) {
113
+ return this.store.getRecentLogs(jobId, limit);
114
+ }
115
+ scheduleJob(state) {
116
+ if (state.timerId) {
117
+ clearTimeout(state.timerId);
118
+ state.timerId = null;
119
+ }
120
+ try {
121
+ const parsed = parseSchedule(state.config.schedule);
122
+ let delayMs;
123
+ switch (parsed.type) {
124
+ case 'interval':
125
+ delayMs = parsed.intervalMs;
126
+ break;
127
+ case 'once': {
128
+ const diff = parsed.onceAt.getTime() - Date.now();
129
+ if (diff <= 0) {
130
+ this.executeJob(state);
131
+ return;
132
+ }
133
+ delayMs = diff;
134
+ break;
135
+ }
136
+ case 'cron':
137
+ delayMs = getNextCronTime(parsed.cronInstance);
138
+ break;
139
+ }
140
+ state.timerId = setTimeout(async () => {
141
+ await this.executeJob(state);
142
+ if (parsed.type !== 'once' && state.config.enabled && this.running) {
143
+ this.scheduleJob(state);
144
+ }
145
+ else if (parsed.type === 'once') {
146
+ this.remove(state.config.id);
147
+ }
148
+ }, delayMs);
149
+ }
150
+ catch (err) {
151
+ console.log(` [cron] ✗ 调度失败 ${state.config.id}: ${err.message}`);
152
+ }
153
+ }
154
+ async executeJob(state) {
155
+ if (state.running)
156
+ return '任务正在执行中';
157
+ state.running = true;
158
+ const startedAt = new Date().toISOString();
159
+ let output = '';
160
+ let status = 'success';
161
+ let error;
162
+ try {
163
+ const timeout = state.config.timeout || 60000;
164
+ output = await this.runPayload(state.config.payload, timeout);
165
+ state.consecutiveFailures = 0;
166
+ }
167
+ catch (err) {
168
+ status = err.message?.includes('timeout') ? 'timeout' : 'error';
169
+ error = err.message;
170
+ output = `执行失败: ${err.message}`;
171
+ state.consecutiveFailures++;
172
+ const maxRetries = state.config.maxRetries ?? 3;
173
+ if (state.consecutiveFailures >= maxRetries) {
174
+ state.config.enabled = false;
175
+ console.log(` [cron] ✗ ${state.config.id} 连续失败 ${maxRetries} 次,已自动禁用`);
176
+ this.persist();
177
+ }
178
+ }
179
+ finally {
180
+ state.running = false;
181
+ }
182
+ const log = { jobId: state.config.id, startedAt, finishedAt: new Date().toISOString(), status, output: output.slice(0, 1000), error };
183
+ state.lastRun = log;
184
+ this.store.appendLog(log);
185
+ if (this.executor?.notify) {
186
+ const icon = status === 'success' ? '✓' : '✗';
187
+ this.executor.notify(`[cron] ${icon} ${state.config.name}: ${output.slice(0, 200)}`);
188
+ }
189
+ return output;
190
+ }
191
+ async runPayload(payload, timeout) {
192
+ if (!this.executor)
193
+ return '[cron] 未设置执行器,无法运行任务';
194
+ if (payload.type === 'agent') {
195
+ return this.executor.runAgentPrompt(payload.prompt, timeout);
196
+ }
197
+ if (payload.type === 'handler') {
198
+ if (payload.handler === 'random-quote') {
199
+ return QUOTES[Math.floor(Math.random() * QUOTES.length)];
200
+ }
201
+ return `[handler] ${payload.handler} — handler 类型需要通过插件注册`;
202
+ }
203
+ return '未知 payload 类型';
204
+ }
205
+ persist() {
206
+ const configs = Array.from(this.jobs.values())
207
+ .filter(s => s.config.source === 'runtime').map(s => s.config);
208
+ const existing = this.store.loadJobs().filter(j => j.source === 'config');
209
+ this.store.saveJobs([...existing, ...configs]);
210
+ }
211
+ }
@@ -0,0 +1,53 @@
1
+ import fs from 'node:fs';
2
+ const JOBS_FILE = '.cron/jobs.json';
3
+ const LOGS_FILE = '.cron/logs.jsonl';
4
+ export class CronStore {
5
+ baseDir;
6
+ constructor(baseDir = '.') {
7
+ this.baseDir = baseDir;
8
+ }
9
+ get jobsPath() { return `${this.baseDir}/${JOBS_FILE}`; }
10
+ get logsPath() { return `${this.baseDir}/${LOGS_FILE}`; }
11
+ init() {
12
+ const dir = `${this.baseDir}/.cron`;
13
+ if (!fs.existsSync(dir))
14
+ fs.mkdirSync(dir, { recursive: true });
15
+ }
16
+ loadJobs() {
17
+ if (!fs.existsSync(this.jobsPath))
18
+ return [];
19
+ try {
20
+ const data = JSON.parse(fs.readFileSync(this.jobsPath, 'utf-8'));
21
+ return data.jobs || [];
22
+ }
23
+ catch {
24
+ return [];
25
+ }
26
+ }
27
+ saveJobs(jobs) {
28
+ this.init();
29
+ fs.writeFileSync(this.jobsPath, JSON.stringify({ jobs }, null, 2));
30
+ }
31
+ appendLog(log) {
32
+ this.init();
33
+ fs.appendFileSync(this.logsPath, JSON.stringify(log) + '\n');
34
+ }
35
+ getRecentLogs(jobId, limit = 10) {
36
+ if (!fs.existsSync(this.logsPath))
37
+ return [];
38
+ const lines = fs.readFileSync(this.logsPath, 'utf-8')
39
+ .split('\n')
40
+ .filter(Boolean);
41
+ let logs = lines.map(l => {
42
+ try {
43
+ return JSON.parse(l);
44
+ }
45
+ catch {
46
+ return null;
47
+ }
48
+ }).filter(Boolean);
49
+ if (jobId)
50
+ logs = logs.filter(l => l.jobId === jobId);
51
+ return logs.slice(-limit);
52
+ }
53
+ }
@@ -0,0 +1 @@
1
+ export {};
package/dist/index.js ADDED
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env node
2
+ const command = process.argv[2];
3
+ if (command === 'init') {
4
+ import('./config/init.js').then((m) => m.runInit());
5
+ }
6
+ else {
7
+ import('./main.js').then((m) => m.startAgent().catch(console.error));
8
+ }
9
+ export {};
package/dist/main.js ADDED
@@ -0,0 +1,270 @@
1
+ import 'dotenv/config';
2
+ import { loadConfig } from './config/loader.js';
3
+ import fs from 'node:fs';
4
+ import { createOpenAI } from '@ai-sdk/openai';
5
+ import { createMockModel } from './mock-model.js';
6
+ import { createInterface } from 'node:readline';
7
+ import { ToolRegistry } from './tools/registry.js';
8
+ import { allTools } from './tools/index.js';
9
+ import { createToolSearchTool } from './tools/tool-search.js';
10
+ import { createMemoryTool } from './tools/memory-tools.js';
11
+ import { createRagTools } from './tools/rag-tools.js';
12
+ import { agentLoop } from './agent/loop.js';
13
+ import { SessionStore } from './session/store.js';
14
+ import { PromptBuilder, coreRules, toolGuide, deferredTools, sessionContext, } from './context/prompt-builder.js';
15
+ import { estimateMessageTokens } from './context/defense.js';
16
+ import { UsageTracker } from './usage/tracker.js';
17
+ import { MemoryStore } from './memory/store.js';
18
+ import { memoryContext, ragContext } from './context/prompt-pipes.js';
19
+ import { chunkDocument } from './rag/chunker.js';
20
+ import { createDashScopeEmbedder, embed } from './rag/embedder.js';
21
+ import { SqliteVectorStore as VectorStore } from './rag/sqlite-store.js';
22
+ import { createDispatcher } from './commands/index.js';
23
+ import { debugCommands } from './commands/debug.js';
24
+ import { contextCommands } from './commands/context.js';
25
+ import { memoryCommands } from './commands/memory.js';
26
+ import { ragCommands } from './commands/rag.js';
27
+ import { dreamCommands } from './commands/dream.js';
28
+ import { SkillLoader } from './skills/loader.js';
29
+ import { createSkillCommands } from './commands/skill.js';
30
+ import { PluginManager } from './plugins/manager.js';
31
+ import { supabasePlugin } from './plugins/supabase-plugin.js';
32
+ import { createPluginCommands } from './commands/plugin.js';
33
+ import { HookPipeline } from './security/hook.js';
34
+ import { createSecurityCommands } from './commands/security.js';
35
+ import { CronService } from './cron/service.js';
36
+ import { createCronTool } from './tools/cron-tools.js';
37
+ import { createCronCommands } from './commands/cron.js';
38
+ import { SubAgentRegistry } from './agents/registry.js';
39
+ import { createSpawnTool } from './tools/spawn-tools.js';
40
+ import { createAgentCommands } from './commands/agent.js';
41
+ import { MCPClient } from './tools/mcp-client.js';
42
+ // ── 加载配置 ────────────────────────────────
43
+ const config = loadConfig();
44
+ function createModel(cfg) {
45
+ if (!cfg.apiKey)
46
+ return createMockModel();
47
+ const provider = createOpenAI({ baseURL: cfg.baseURL, apiKey: cfg.apiKey });
48
+ return provider.chat(cfg.name);
49
+ }
50
+ const model = createModel(config.model);
51
+ // ── Registry ────────────────────────────────────────
52
+ const registry = new ToolRegistry();
53
+ registry.register(...allTools);
54
+ registry.register(createToolSearchTool(registry));
55
+ // ── Memory ────────────────────────────────────────
56
+ const memoryStore = new MemoryStore(config.memory.dataDir);
57
+ memoryStore.init();
58
+ registry.register(createMemoryTool(memoryStore));
59
+ // ── RAG ────────────────────────────────────────
60
+ const vectorStore = new VectorStore();
61
+ const embedFn = config.model.apiKey
62
+ ? createDashScopeEmbedder(config.model.apiKey)
63
+ : embed;
64
+ registry.register(...createRagTools(vectorStore, embedFn));
65
+ async function connectMCP() {
66
+ const token = process.env.GITHUB_PERSONAL_ACCESS_TOKEN;
67
+ if (!token) {
68
+ console.log(' ⚠ 未设置 GITHUB_PERSONAL_ACCESS_TOKEN,跳过 GitHub MCP');
69
+ return;
70
+ }
71
+ try {
72
+ const mcpClient = new MCPClient('pnpm', ['dlx', '@modelcontextprotocol/server-github'], { GITHUB_PERSONAL_ACCESS_TOKEN: token });
73
+ const tools = await registry.registerMCPServer('github', mcpClient);
74
+ console.log(` 已注册 ${tools.length} 个 MCP 工具`);
75
+ }
76
+ catch (err) {
77
+ console.log(` ⚠ GitHub MCP 加载失败: ${err.message}`);
78
+ }
79
+ }
80
+ // ── Skills ────────────────────────────────────────
81
+ const skillLoader = new SkillLoader('.');
82
+ const loadedSkills = skillLoader.load();
83
+ const activeSkills = new Set();
84
+ // ── Plugins ────────────────────────────────────────
85
+ const pluginManager = new PluginManager(registry);
86
+ const availablePlugins = new Map([
87
+ ['supabase', supabasePlugin],
88
+ ]);
89
+ // ── Security: Hook Pipeline ────────────────────────────────────────
90
+ const hookPipeline = new HookPipeline();
91
+ hookPipeline.registerPre('audit-log', (toolName, input) => {
92
+ if (toolName === 'write_file' || toolName === 'edit_file') {
93
+ const path = input?.path || 'unknown';
94
+ console.log(` [audit] 文件写入操作: ${toolName} → ${path}`);
95
+ }
96
+ return { action: 'allow' };
97
+ });
98
+ hookPipeline.registerPost('bash-timestamp', (toolName, _input, output) => {
99
+ if (toolName === 'bash') {
100
+ const timestamp = new Date().toISOString();
101
+ return {
102
+ action: 'modify',
103
+ modifiedOutput: `[${timestamp}]\n${output}`,
104
+ };
105
+ }
106
+ return { action: 'allow' };
107
+ });
108
+ registry.setHookPipeline(hookPipeline);
109
+ // ── Cron Service ────────────────────────────────────────
110
+ const cronService = new CronService(config.cron.dataDir);
111
+ registry.register(createCronTool(cronService));
112
+ // ── Sub-Agent ────────────────────────────────────────
113
+ const agentRegistry = new SubAgentRegistry({
114
+ maxSpawnDepth: config.agents.maxSpawnDepth,
115
+ maxConcurrent: config.agents.maxConcurrent,
116
+ });
117
+ function getSpawnCtx() {
118
+ return {
119
+ model,
120
+ registry,
121
+ agentRegistry,
122
+ buildSystem: () => builder.build(makePromptCtx()),
123
+ currentDepth: 0,
124
+ };
125
+ }
126
+ registry.register(createSpawnTool(agentRegistry, getSpawnCtx));
127
+ // ── Prompt Builder ────────────────────────────────────────
128
+ const builder = new PromptBuilder()
129
+ .pipe('coreRules', coreRules())
130
+ .pipe('toolGuide', toolGuide())
131
+ .pipe('deferredTools', deferredTools())
132
+ .pipe('memoryContext', memoryContext(memoryStore))
133
+ .pipe('ragContext', ragContext(vectorStore))
134
+ .pipe('skillContext', () => skillLoader.buildPromptSection(activeSkills))
135
+ .pipe('sessionContext', sessionContext());
136
+ // ── Commands ────────────────────────────────────────
137
+ const dispatch = createDispatcher([
138
+ ...debugCommands,
139
+ ...contextCommands,
140
+ ...memoryCommands,
141
+ ...ragCommands,
142
+ ...dreamCommands,
143
+ ...createSkillCommands(skillLoader, activeSkills),
144
+ ...createPluginCommands(pluginManager, availablePlugins),
145
+ ...createSecurityCommands(registry, hookPipeline),
146
+ ...createCronCommands(cronService),
147
+ ...createAgentCommands(agentRegistry),
148
+ ]);
149
+ function makePromptCtx() {
150
+ return {
151
+ toolCount: registry.getActiveTools().length,
152
+ deferredToolSummary: registry.getDeferredToolSummary(),
153
+ sessionMessageCount: 0,
154
+ sessionId: config.session.id,
155
+ };
156
+ }
157
+ export async function startAgent() {
158
+ await connectMCP();
159
+ // 加载插件
160
+ console.log(' 加载插件...');
161
+ for (const [name, def] of availablePlugins) {
162
+ try {
163
+ const tools = await pluginManager.load(def);
164
+ console.log(` ✓ ${name} — ${tools.length} 个工具`);
165
+ }
166
+ catch {
167
+ console.log(` ✗ ${name} — 加载失败`);
168
+ }
169
+ }
170
+ // 启动 Cron
171
+ cronService.load();
172
+ cronService.setExecutor({
173
+ runAgentPrompt: async (prompt, timeout) => {
174
+ const cronMessages = [{ role: 'user', content: prompt }];
175
+ const system = builder.build(makePromptCtx());
176
+ await agentLoop(model, registry, cronMessages, system);
177
+ const lastMsg = cronMessages[cronMessages.length - 1];
178
+ if (!lastMsg)
179
+ return '(无输出)';
180
+ if (typeof lastMsg.content === 'string')
181
+ return lastMsg.content;
182
+ if (Array.isArray(lastMsg.content)) {
183
+ return lastMsg.content
184
+ .filter((p) => p.type === 'text')
185
+ .map((p) => p.text)
186
+ .join('') || '(无输出)';
187
+ }
188
+ return String(lastMsg.content);
189
+ },
190
+ notify: (message) => {
191
+ console.log(`\n${message}`);
192
+ },
193
+ });
194
+ cronService.start();
195
+ const cronJobs = cronService.list();
196
+ const store = new SessionStore('default');
197
+ let messages = [];
198
+ const timestamps = new Map();
199
+ const tracker = new UsageTracker('.usage/today.jsonl');
200
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
201
+ function ask() {
202
+ rl.question('\nYou: ', async (input) => {
203
+ const trimmed = input.trim();
204
+ if (!trimmed || trimmed === 'exit') {
205
+ console.log('Bye!');
206
+ cronService.stop();
207
+ await pluginManager.unloadAll();
208
+ rl.close();
209
+ return;
210
+ }
211
+ const ctx = {
212
+ messages, timestamps, registry, builder, tracker,
213
+ sessionStore: store, model, makePromptCtx, ask,
214
+ memoryStore, vectorStore,
215
+ };
216
+ const handled = dispatch(trimmed, ctx);
217
+ if (handled === 'async')
218
+ return;
219
+ if (handled) {
220
+ ask();
221
+ return;
222
+ }
223
+ const userMsg = { role: 'user', content: trimmed };
224
+ messages.push(userMsg);
225
+ timestamps.set(messages.length - 1, Date.now());
226
+ store.append(userMsg);
227
+ const currentSystem = builder.build(makePromptCtx());
228
+ const beforeLen = messages.length;
229
+ await agentLoop(model, registry, messages, currentSystem, tracker);
230
+ const newMessages = messages.slice(beforeLen);
231
+ const now = Date.now();
232
+ for (let i = beforeLen; i < messages.length; i++)
233
+ timestamps.set(i, now);
234
+ store.appendAll(newMessages);
235
+ console.log(` [Token] ~${estimateMessageTokens(messages)} tokens`);
236
+ ask();
237
+ });
238
+ }
239
+ const role = registry.getRole();
240
+ const toolCount = registry.getActiveTools().length;
241
+ const hooks = hookPipeline.list();
242
+ console.log('Super Agent v1.0 (type "exit" to quit)');
243
+ console.log('快捷命令:');
244
+ console.log(' /agents — 查看子 Agent 记录');
245
+ console.log(' /cron — 查看定时任务');
246
+ console.log(' /role [角色] — 查看/切换角色');
247
+ console.log('');
248
+ console.log(` 当前角色: ${role},可用工具: ${toolCount} 个`);
249
+ console.log(` Sub-Agent: 最大深度 ${agentRegistry.getConfig().maxSpawnDepth},最大并发 ${agentRegistry.getConfig().maxConcurrent}`);
250
+ console.log('');
251
+ console.log(' 试试:');
252
+ console.log(' 帮我对比 Hono、Fastify 和 Express 的性能和生态');
253
+ console.log(' /agents — 查看子 Agent 执行记录');
254
+ console.log('');
255
+ if (fs.existsSync('docs')) {
256
+ const files = fs.readdirSync('docs').filter(f => f.endsWith('.md'));
257
+ if (files.length > 0) {
258
+ console.log(` 发现 ${files.length} 个文档,自动导入知识库...`);
259
+ for (const f of files) {
260
+ const path = `docs/${f}`;
261
+ const text = fs.readFileSync(path, 'utf-8');
262
+ const chunks = chunkDocument(path, text);
263
+ const embeddings = await embed(embedFn, chunks.map(c => c.text));
264
+ vectorStore.addBatch(chunks.map((c, i) => ({ chunk: c, embedding: embeddings[i] })));
265
+ }
266
+ console.log(` 知识库就绪,共 ${vectorStore.size()} 个片段\n`);
267
+ }
268
+ }
269
+ ask();
270
+ }