@xiaodye/runeclaw 1.0.1 → 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 (3) hide show
  1. package/README.md +12 -9
  2. package/dist/index.js +124 -48
  3. package/package.json +7 -3
package/README.md CHANGED
@@ -6,6 +6,8 @@
6
6
 
7
7
  RuneClaw 是一个基于 TypeScript 的本地 AI Agent CLI。它把模型调用、文件工具、RAG、记忆、MCP、子 Agent、定时任务、插件和飞书通道放在同一个运行时里,适合做可迭代的个人 Agent 实验。
8
8
 
9
+ ![alt text](./assets/image.png)
10
+
9
11
  ## 特性
10
12
 
11
13
  - DeepSeek 兼容模型接入,默认使用 `deepseek-v4-flash`
@@ -13,7 +15,7 @@ RuneClaw 是一个基于 TypeScript 的本地 AI Agent CLI。它把模型调用
13
15
  - 本地文件工具:读取、写入、编辑、列目录、grep、glob
14
16
  - Shell 工具:可执行命令并返回输出
15
17
  - 记忆系统:跨会话保存、读取、搜索记忆
16
- - RAG 知识库:自动导入 `docs/`,也支持手动导入其它文档
18
+ - RAG 知识库:启动时加 `--rag` 自动导入 `docs/`,也支持手动导入其它文档
17
19
  - MCP 接入:可挂载外部工具,当前默认有 GitHub mock 示例
18
20
  - 子 Agent:支持任务分发与执行记录查看
19
21
  - 定时任务:支持查看任务与执行日志
@@ -68,13 +70,13 @@ runeclaw
68
70
 
69
71
  `runeclaw` 支持以下命令:
70
72
 
71
- | 命令 | 说明 |
72
- | --- | --- |
73
- | `runeclaw` / `runeclaw start` | 启动交互式 Agent(默认) |
74
- | `runeclaw init` | 运行初始化向导,生成 `runeclaw.config.json` 与 `.env` |
75
- | `runeclaw continue` | 启动 Agent(预留会话续接) |
76
- | `runeclaw help` / `--help` | 查看帮助 |
77
- | `runeclaw version` / `--version` | 查看版本号 |
73
+ | 命令 | 说明 |
74
+ | -------------------------------- | ----------------------------------------------------- |
75
+ | `runeclaw` / `runeclaw start` | 启动交互式 Agent(默认) |
76
+ | `runeclaw init` | 运行初始化向导,生成 `runeclaw.config.json` 与 `.env` |
77
+ | `runeclaw continue` | 启动 Agent(预留会话续接) |
78
+ | `runeclaw help` / `--help` | 查看帮助 |
79
+ | `runeclaw version` / `--version` | 查看版本号 |
78
80
 
79
81
  ## 发布到 npm
80
82
 
@@ -89,6 +91,7 @@ pnpm run release -- major # 主版本号 +1(1.0.0 → 2.0.0)
89
91
  ```
90
92
 
91
93
  > 说明:
94
+ >
92
95
  > - 发布用 `npm publish --registry https://registry.npmjs.org/`,命令行参数优先级最高,能覆盖任何 `.npmrc` 里的镜像源配置(镜像源只读,无法发布),也无需改动全局 npm 配置;
93
96
  > - `npm publish` 的 `prepublishOnly` 会自动执行 `pnpm build`,无需手动构建;
94
97
  > - 首次发布前需要先 `npm login`。
@@ -222,7 +225,7 @@ src/
222
225
 
223
226
  ## 说明
224
227
 
225
- - 启动时会自动导入 `docs/` 下的 `.md` 文档到知识库
228
+ - 启动时默认**不**自动导入知识库;加 `--rag`(或 `pnpm start:rag`)才会导入 `docs/` 下的 `.md` 文档
226
229
  - 当前项目里的飞书配置只应通过 `.env` 提供,不要把 secret 写进 `runeclaw.config.json`
227
230
  - Web 搜索工具会优先使用 `TAVILY_API_KEY`,没有时可用 `SERPER_API_KEY`
228
231
 
package/dist/index.js CHANGED
@@ -265,7 +265,7 @@ function TextField(props) {
265
265
  onChange(value.slice(0, -1));
266
266
  return;
267
267
  }
268
- if (!input || input.split("").some((c) => c.charCodeAt(0) < 32)) return;
268
+ if (!input || input.split("").some((c2) => c2.charCodeAt(0) < 32)) return;
269
269
  onChange(value + input);
270
270
  });
271
271
  const display = mask && !reveal ? "\u2022".repeat(value.length) : value;
@@ -1325,14 +1325,14 @@ var init_tracker = __esm({
1325
1325
  const totalInputLike = t.inputTokens + t.cacheReadTokens + t.cacheWriteTokens;
1326
1326
  const hitRate = totalInputLike > 0 ? t.cacheReadTokens / totalInputLike : 0;
1327
1327
  const baselineCost = (() => {
1328
- let c = 0;
1328
+ let c2 = 0;
1329
1329
  for (const s of this.steps) {
1330
1330
  const p = PRICE_TABLE[s.model] || PRICE_TABLE["mock-model"];
1331
1331
  const inputLike = s.inputTokens + s.cacheReadTokens + s.cacheWriteTokens;
1332
- c += inputLike * p.input / 1e6;
1333
- c += s.outputTokens * p.output / 1e6;
1332
+ c2 += inputLike * p.input / 1e6;
1333
+ c2 += s.outputTokens * p.output / 1e6;
1334
1334
  }
1335
- return c;
1335
+ return c2;
1336
1336
  })();
1337
1337
  return {
1338
1338
  ...t,
@@ -1631,6 +1631,44 @@ var init_defense = __esm({
1631
1631
  }
1632
1632
  });
1633
1633
 
1634
+ // src/ui/theme.ts
1635
+ var c;
1636
+ var init_theme = __esm({
1637
+ "src/ui/theme.ts"() {
1638
+ "use strict";
1639
+ c = {
1640
+ reset: "\x1B[0m",
1641
+ dim: "\x1B[2m",
1642
+ bold: "\x1B[1m",
1643
+ red: "\x1B[31m",
1644
+ green: "\x1B[32m",
1645
+ yellow: "\x1B[33m",
1646
+ blue: "\x1B[34m",
1647
+ magenta: "\x1B[35m",
1648
+ cyan: "\x1B[36m",
1649
+ gray: "\x1B[90m"
1650
+ };
1651
+ }
1652
+ });
1653
+
1654
+ // src/ui/markdown.ts
1655
+ import { marked } from "marked";
1656
+ import { markedTerminal } from "marked-terminal";
1657
+ function renderMarkdown(text) {
1658
+ try {
1659
+ const result = marked.parse(text);
1660
+ return typeof result === "string" ? result : String(result);
1661
+ } catch {
1662
+ return text;
1663
+ }
1664
+ }
1665
+ var init_markdown = __esm({
1666
+ "src/ui/markdown.ts"() {
1667
+ "use strict";
1668
+ marked.use(markedTerminal());
1669
+ }
1670
+ });
1671
+
1634
1672
  // src/agent/loop.ts
1635
1673
  import { streamText } from "ai";
1636
1674
  async function agentLoop(model2, registry2, messages, system, tracker) {
@@ -1645,7 +1683,7 @@ async function agentLoop(model2, registry2, messages, system, tracker) {
1645
1683
  while (step < MAX_STEPS) {
1646
1684
  step++;
1647
1685
  console.log(`
1648
- --- Step ${step} ---`);
1686
+ ${c.dim}\u2500\u2500 Step ${step} \u2500\u2500${c.reset}`);
1649
1687
  let hasToolCall = false;
1650
1688
  let fullText = "";
1651
1689
  let shouldBreak = false;
@@ -1674,11 +1712,11 @@ async function agentLoop(model2, registry2, messages, system, tracker) {
1674
1712
  hasToolCall = true;
1675
1713
  lastToolCall = { name: part.toolName, input: part.input };
1676
1714
  console.log(
1677
- ` [\u8C03\u7528: ${part.toolName}(${JSON.stringify(part.input)})]`
1715
+ ` ${c.cyan}\u25B8 \u8C03\u7528 ${c.bold}${part.toolName}${c.reset}${c.dim}(${JSON.stringify(part.input)})${c.reset}`
1678
1716
  );
1679
1717
  const detection = detect(part.toolName, part.input);
1680
1718
  if (detection.stuck) {
1681
- console.log(` ${detection.message}`);
1719
+ console.log(` ${c.yellow}\u26A0 ${detection.message}${c.reset}`);
1682
1720
  if (detection.level === "critical") {
1683
1721
  shouldBreak = true;
1684
1722
  } else {
@@ -1694,7 +1732,7 @@ async function agentLoop(model2, registry2, messages, system, tracker) {
1694
1732
  case "tool-result": {
1695
1733
  const output = typeof part.output === "string" ? part.output : JSON.stringify(part.output);
1696
1734
  const preview = output.length > 120 ? output.slice(0, 120) + "..." : output;
1697
- console.log(` [\u7ED3\u679C: ${part.toolName}] ${preview}`);
1735
+ console.log(` ${c.gray}\u21B3 \u7ED3\u679C ${part.toolName}:${c.reset} ${c.dim}${preview}${c.reset}`);
1698
1736
  if (lastToolCall) {
1699
1737
  recordResult(lastToolCall.name, lastToolCall.input, part.output);
1700
1738
  }
@@ -1708,7 +1746,7 @@ async function agentLoop(model2, registry2, messages, system, tracker) {
1708
1746
  } catch (error) {
1709
1747
  if (attempt > MAX_RETRIES || !isRetryable(error)) throw error;
1710
1748
  const delay = calculateDelay(attempt);
1711
- console.log(` [\u91CD\u8BD5] \u7B2C ${attempt}/${MAX_RETRIES} \u6B21\uFF0C${delay}ms \u540E...`);
1749
+ console.log(` ${c.yellow}\u26A0 [\u91CD\u8BD5] \u7B2C ${attempt}/${MAX_RETRIES} \u6B21\uFF0C${delay}ms \u540E...${c.reset}`);
1712
1750
  await sleep(delay);
1713
1751
  hasToolCall = false;
1714
1752
  fullText = "";
@@ -1717,35 +1755,43 @@ async function agentLoop(model2, registry2, messages, system, tracker) {
1717
1755
  }
1718
1756
  }
1719
1757
  if (shouldBreak) {
1720
- console.log("\n[\u5FAA\u73AF\u68C0\u6D4B\u89E6\u53D1\uFF0CAgent \u5DF2\u505C\u6B62]");
1758
+ console.log(`
1759
+ ${c.yellow}[\u5FAA\u73AF\u68C0\u6D4B\u89E6\u53D1\uFF0CAgent \u5DF2\u505C\u6B62]${c.reset}`);
1721
1760
  break;
1722
1761
  }
1723
1762
  for (const msg of stepResponse.messages) pushMessage(msg);
1724
1763
  const norm = normalizeUsage(stepUsage);
1725
1764
  const stepRecord = tracker?.record(model2?.modelId || "mock-model", norm);
1726
1765
  if (stepRecord && (norm.cacheReadTokens > 0 || norm.cacheWriteTokens > 0)) {
1727
- const tag = norm.cacheReadTokens > 0 ? `\x1B[38;5;36m\u2713 cache hit\x1B[0m` : `\x1B[38;5;220m\u270E cache write\x1B[0m`;
1766
+ const tag = norm.cacheReadTokens > 0 ? `${c.green}\u2713 cache hit${c.reset}` : `${c.yellow}\u270E cache write${c.reset}`;
1728
1767
  const detail = norm.cacheReadTokens > 0 ? `read ${norm.cacheReadTokens}` : `write ${norm.cacheWriteTokens}`;
1729
- console.log(` [${tag}] ${detail} tokens \xB7 \u672C\u6B65 $${stepRecord.cost.toFixed(5)}`);
1768
+ console.log(` [${tag}] ${c.dim}${detail} tokens \xB7 \u672C\u6B65 $${stepRecord.cost.toFixed(5)}${c.reset}`);
1730
1769
  }
1731
1770
  if (estimateMessageTokens(messages) > COMPACT_TOKEN_THRESHOLD) {
1732
- console.log(` [Token] ~${estimateMessageTokens(messages)} \u8D85\u9884\u7B97\uFF0C\u89E6\u53D1\u538B\u7F29...`);
1771
+ console.log(` ${c.yellow}[Token] ~${estimateMessageTokens(messages)} \u8D85\u9884\u7B97\uFF0C\u89E6\u53D1\u538B\u7F29...${c.reset}`);
1733
1772
  const compacted = await compactContext(model2, messages, timestamps);
1734
1773
  messages.splice(0, messages.length, ...compacted.messages);
1735
1774
  timestamps.clear();
1736
1775
  for (let i = 0; i < messages.length; i++) timestamps.set(i, Date.now());
1737
1776
  console.log(
1738
- ` [\u538B\u7F29] TTL \u8F6F\u526A ${compacted.softPruned}/\u786C\u6E05 ${compacted.hardPruned}\uFF0C\u6458\u8981\u79FB\u9664 ${compacted.compressedCount} \u6761 \u2192 ~${compacted.tokenEstimate} tokens`
1777
+ ` ${c.yellow}[\u538B\u7F29] TTL \u8F6F\u526A ${compacted.softPruned}/\u786C\u6E05 ${compacted.hardPruned}\uFF0C\u6458\u8981\u79FB\u9664 ${compacted.compressedCount} \u6761 \u2192 ~${compacted.tokenEstimate} tokens${c.reset}`
1739
1778
  );
1740
1779
  }
1741
1780
  if (!hasToolCall) {
1742
- if (fullText) console.log();
1781
+ if (fullText) {
1782
+ const rawLines = fullText.split("\n").length;
1783
+ if (rawLines > 0) process.stdout.write(`\x1B[${rawLines}A\x1B[J`);
1784
+ const rendered = renderMarkdown(fullText);
1785
+ process.stdout.write(rendered);
1786
+ process.stdout.write("\n");
1787
+ }
1743
1788
  break;
1744
1789
  }
1745
- console.log(" \u2192 \u7EE7\u7EED\u4E0B\u4E00\u6B65...");
1790
+ console.log(` ${c.dim}\u2192 \u7EE7\u7EED\u4E0B\u4E00\u6B65...${c.reset}`);
1746
1791
  }
1747
1792
  if (step >= MAX_STEPS) {
1748
- console.log("\n[\u8FBE\u5230\u6700\u5927\u6B65\u6570]");
1793
+ console.log(`
1794
+ ${c.yellow}[\u8FBE\u5230\u6700\u5927\u6B65\u6570]${c.reset}`);
1749
1795
  }
1750
1796
  }
1751
1797
  var MAX_STEPS, MAX_RETRIES;
@@ -1756,6 +1802,8 @@ var init_loop = __esm({
1756
1802
  init_retry();
1757
1803
  init_tracker();
1758
1804
  init_defense();
1805
+ init_theme();
1806
+ init_markdown();
1759
1807
  MAX_STEPS = 15;
1760
1808
  MAX_RETRIES = 3;
1761
1809
  }
@@ -3037,10 +3085,10 @@ function renderContextMatrix(snapshot) {
3037
3085
  const rowCells = [];
3038
3086
  for (let col = 0; col < 16; col++) {
3039
3087
  const idx = row * 16 + col;
3040
- const c = cells[idx];
3041
- if (c === -1) rowCells.push(fg(COLORS.free, "\u25CB"));
3042
- else if (c === -2) rowCells.push(fg(COLORS.buffer, "\u25A2"));
3043
- else rowCells.push(fg(c, "\u25CF"));
3088
+ const c2 = cells[idx];
3089
+ if (c2 === -1) rowCells.push(fg(COLORS.free, "\u25CB"));
3090
+ else if (c2 === -2) rowCells.push(fg(COLORS.buffer, "\u25A2"));
3091
+ else rowCells.push(fg(c2, "\u25CF"));
3044
3092
  }
3045
3093
  lines.push(rowCells.join(" "));
3046
3094
  }
@@ -3884,10 +3932,10 @@ function createRagTools(vectorStore2, embedFn2) {
3884
3932
  const chunks = chunkDocument(path4, text);
3885
3933
  const embeddings = await embed(
3886
3934
  embedFn2,
3887
- chunks.map((c) => c.text)
3935
+ chunks.map((c2) => c2.text)
3888
3936
  );
3889
3937
  vectorStore2.addBatch(
3890
- chunks.map((c, i) => ({ chunk: c, embedding: embeddings[i] }))
3938
+ chunks.map((c2, i) => ({ chunk: c2, embedding: embeddings[i] }))
3891
3939
  );
3892
3940
  return `\u5DF2\u5BFC\u5165 ${chunks.length} \u4E2A\u6587\u6863\u7247\u6BB5\uFF08\u6765\u6E90: ${path4}\uFF09\u3002\u77E5\u8BC6\u5E93\u5171 ${vectorStore2.size()} \u4E2A\u7247\u6BB5\u3002`;
3893
3941
  } catch (e) {
@@ -4712,7 +4760,7 @@ var init_gateway = __esm({
4712
4760
  if (typeof content === "string") {
4713
4761
  replyText = content;
4714
4762
  } else if (Array.isArray(content)) {
4715
- replyText = content.filter((c) => c.type === "text").map((c) => c.text).join("");
4763
+ replyText = content.filter((c2) => c2.type === "text").map((c2) => c2.text).join("");
4716
4764
  }
4717
4765
  }
4718
4766
  if (replyText) {
@@ -4863,8 +4911,8 @@ var init_feishu = __esm({
4863
4911
  const { Hono } = await import("hono");
4864
4912
  const { serve } = await import("@hono/node-server");
4865
4913
  const app = new Hono();
4866
- app.post("/webhook/feishu", async (c) => {
4867
- const body = await c.req.json();
4914
+ app.post("/webhook/feishu", async (c2) => {
4915
+ const body = await c2.req.json();
4868
4916
  if (body.header?.event_type === "im.message.receive_v1") {
4869
4917
  const event = body.event;
4870
4918
  if (event.message?.message_type === "text") {
@@ -4881,9 +4929,9 @@ var init_feishu = __esm({
4881
4929
  }
4882
4930
  }
4883
4931
  }
4884
- return c.json({ code: 0 });
4932
+ return c2.json({ code: 0 });
4885
4933
  });
4886
- app.get("/", (c) => {
4934
+ app.get("/", (c2) => {
4887
4935
  const feishuStatus = this.config.appId ? "\u5DF2\u8FDE\u63A5\uFF08\u957F\u8FDE\u63A5\u6A21\u5F0F\uFF09" : "\u672A\u914D\u7F6E";
4888
4936
  const html = `<!DOCTYPE html>
4889
4937
  <html lang="zh">
@@ -4957,9 +5005,9 @@ var init_feishu = __esm({
4957
5005
  </script>
4958
5006
  </body>
4959
5007
  </html>`;
4960
- return c.html(html);
5008
+ return c2.html(html);
4961
5009
  });
4962
- app.get("/health", (c) => c.text("OK"));
5010
+ app.get("/health", (c2) => c2.text("OK"));
4963
5011
  this.httpServer = serve({ fetch: app.fetch, port: this.config.port });
4964
5012
  console.log(` Dashboard: http://localhost:${this.config.port}`);
4965
5013
  }
@@ -6075,6 +6123,7 @@ import process2 from "node:process";
6075
6123
  import { createOpenAI } from "@ai-sdk/openai";
6076
6124
  import fs8 from "node:fs";
6077
6125
  import { createInterface } from "node:readline";
6126
+ import boxen from "boxen";
6078
6127
  async function connectMCP() {
6079
6128
  const mockClient = new MockMCPClient();
6080
6129
  const tools = await registry.registerMCPServer("github", mockClient);
@@ -6098,6 +6147,18 @@ function makePromptCtx() {
6098
6147
  };
6099
6148
  }
6100
6149
  async function startAgent() {
6150
+ const restoreConsole = (() => {
6151
+ const originalLog = console.log;
6152
+ const originalInfo = console.info;
6153
+ console.log = () => {
6154
+ };
6155
+ console.info = () => {
6156
+ };
6157
+ return () => {
6158
+ console.log = originalLog;
6159
+ console.info = originalInfo;
6160
+ };
6161
+ })();
6101
6162
  await connectMCP();
6102
6163
  console.log(" \u52A0\u8F7D\u63D2\u4EF6...");
6103
6164
  for (const [name, def] of availablePlugins) {
@@ -6105,7 +6166,7 @@ async function startAgent() {
6105
6166
  const tools = await pluginManager.load(def);
6106
6167
  console.log(` \u2713 ${name} \u2014 ${tools.length} \u4E2A\u5DE5\u5177`);
6107
6168
  } catch {
6108
- console.log(` \u2717 ${name} \u2014 \u52A0\u8F7D\u5931\u8D25`);
6169
+ console.error(` \u2717 ${name} \u2014 \u52A0\u8F7D\u5931\u8D25`);
6109
6170
  }
6110
6171
  }
6111
6172
  console.log(" \u542F\u52A8 Channel...");
@@ -6131,13 +6192,15 @@ ${message}`);
6131
6192
  });
6132
6193
  cronService.start();
6133
6194
  const cronJobs = cronService.list();
6195
+ restoreConsole();
6134
6196
  const store = new SessionStore("default");
6135
6197
  let messages = [];
6136
6198
  const timestamps = /* @__PURE__ */ new Map();
6137
6199
  const tracker = new UsageTracker(".usage/today.jsonl");
6138
6200
  const rl = createInterface({ input: process2.stdin, output: process2.stdout });
6139
6201
  function ask() {
6140
- rl.question("\nYou: ", async (input) => {
6202
+ rl.question(`
6203
+ ${c.cyan}\u276F${c.reset} `, async (input) => {
6141
6204
  const trimmed = input.trim();
6142
6205
  if (!trimmed || trimmed === "exit") {
6143
6206
  console.log("Bye!");
@@ -6184,22 +6247,30 @@ ${message}`);
6184
6247
  const role = registry.getRole();
6185
6248
  const toolCount = registry.getActiveTools().length;
6186
6249
  const hooks = hookPipeline.list();
6187
- console.log('RuneClaw v1.0 (type "exit" to quit)');
6188
- console.log("\u5FEB\u6377\u547D\u4EE4\uFF1A");
6189
- console.log(" /agents \u2014 \u67E5\u770B\u5B50 Agent \u8BB0\u5F55");
6190
- console.log(" /cron \u2014 \u67E5\u770B\u5B9A\u65F6\u4EFB\u52A1");
6191
- console.log(" /role [\u89D2\u8272] \u2014 \u67E5\u770B/\u5207\u6362\u89D2\u8272");
6192
- console.log("");
6193
- console.log(` \u5F53\u524D\u89D2\u8272: ${role}\uFF0C\u53EF\u7528\u5DE5\u5177: ${toolCount} \u4E2A`);
6250
+ const modelName = model?.modelId || config.model.name || "unknown";
6251
+ const splash = boxen(
6252
+ [
6253
+ ` ${c.cyan}model:${c.reset} ${c.bold}${modelName}${c.reset}`,
6254
+ ` ${c.cyan}directory:${c.reset} ${c.bold}${process2.cwd()}${c.reset}`,
6255
+ ` ${c.cyan}role:${c.reset} ${c.yellow}${role}${c.reset} \xB7 \u53EF\u7528\u5DE5\u5177: ${c.green}${toolCount}${c.reset} \u4E2A`,
6256
+ "",
6257
+ ` ${c.dim}Sub-Agent \u6DF1\u5EA6 ${agentRegistry.getConfig().maxSpawnDepth} / \u5E76\u53D1 ${agentRegistry.getConfig().maxConcurrent} \xB7 exit \u9000\u51FA \xB7 /help \u547D\u4EE4${c.reset}`
6258
+ ].join("\n"),
6259
+ {
6260
+ title: "RuneClaw",
6261
+ titleAlignment: "left",
6262
+ borderStyle: "round",
6263
+ borderColor: "cyan",
6264
+ padding: 1
6265
+ }
6266
+ );
6267
+ console.log(splash);
6194
6268
  console.log(
6195
- ` Sub-Agent: \u6700\u5927\u6DF1\u5EA6 ${agentRegistry.getConfig().maxSpawnDepth}\uFF0C\u6700\u5927\u5E76\u53D1 ${agentRegistry.getConfig().maxConcurrent}`
6269
+ `${c.green}\u2713 \u5C31\u7EEA${c.reset} \xB7 ${toolCount} \u4E2A\u5DE5\u5177 \xB7 ${availablePlugins.size} \u4E2A\u63D2\u4EF6\u5DF2\u52A0\u8F7D`
6196
6270
  );
6197
6271
  console.log("");
6198
- console.log(" \u8BD5\u8BD5\uFF1A");
6199
- console.log(" \u5E2E\u6211\u5BF9\u6BD4 Hono\u3001Fastify \u548C Express \u7684\u6027\u80FD\u548C\u751F\u6001");
6200
- console.log(" /agents \u2014 \u67E5\u770B\u5B50 Agent \u6267\u884C\u8BB0\u5F55");
6201
- console.log("");
6202
- if (fs8.existsSync("docs")) {
6272
+ const enableRag = process2.argv.includes("--rag");
6273
+ if (enableRag && fs8.existsSync("docs")) {
6203
6274
  const files = fs8.readdirSync("docs").filter((f) => f.endsWith(".md"));
6204
6275
  if (files.length > 0) {
6205
6276
  console.log(` \u53D1\u73B0 ${files.length} \u4E2A\u6587\u6863\uFF0C\u81EA\u52A8\u5BFC\u5165\u77E5\u8BC6\u5E93...`);
@@ -6209,15 +6280,17 @@ ${message}`);
6209
6280
  const chunks = chunkDocument(path4, text);
6210
6281
  const embeddings = await embed(
6211
6282
  embedFn,
6212
- chunks.map((c) => c.text)
6283
+ chunks.map((c2) => c2.text)
6213
6284
  );
6214
6285
  vectorStore.addBatch(
6215
- chunks.map((c, i) => ({ chunk: c, embedding: embeddings[i] }))
6286
+ chunks.map((c2, i) => ({ chunk: c2, embedding: embeddings[i] }))
6216
6287
  );
6217
6288
  }
6218
6289
  console.log(` \u77E5\u8BC6\u5E93\u5C31\u7EEA\uFF0C\u5171 ${vectorStore.size()} \u4E2A\u7247\u6BB5
6219
6290
  `);
6220
6291
  }
6292
+ } else if (fs8.existsSync("docs")) {
6293
+ console.log(" \u63D0\u793A: \u68C0\u6D4B\u5230 docs/ \u76EE\u5F55\uFF0C\u52A0 --rag \u53C2\u6570\u53EF\u81EA\u52A8\u5BFC\u5165\u77E5\u8BC6\u5E93\n");
6221
6294
  }
6222
6295
  ask();
6223
6296
  }
@@ -6264,6 +6337,7 @@ var init_main = __esm({
6264
6337
  init_spawn_tools();
6265
6338
  init_agents();
6266
6339
  init_loader();
6340
+ init_theme();
6267
6341
  config = loadConfig();
6268
6342
  provider = createOpenAI({
6269
6343
  baseURL: config.model.baseURL,
@@ -6358,6 +6432,7 @@ var HELP_TEXT = `RuneClaw \u2014 \u672C\u5730 AI Agent \u8FD0\u884C\u65F6
6358
6432
  \u9009\u9879:
6359
6433
  -h, --help \u663E\u793A\u5E2E\u52A9
6360
6434
  -v, --version \u663E\u793A\u7248\u672C\u53F7
6435
+ --rag \u542F\u52A8\u65F6\u81EA\u52A8\u5BFC\u5165 docs/ \u4E0B\u7684\u6587\u6863\u5230\u77E5\u8BC6\u5E93
6361
6436
  `;
6362
6437
  function readVersion() {
6363
6438
  try {
@@ -6381,6 +6456,7 @@ async function main() {
6381
6456
  case "start":
6382
6457
  case "continue":
6383
6458
  case "--continue":
6459
+ case "--rag":
6384
6460
  case void 0: {
6385
6461
  const { startAgent: startAgent2 } = await Promise.resolve().then(() => (init_main(), main_exports));
6386
6462
  await startAgent2().catch((error) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xiaodye/runeclaw",
3
- "version": "1.0.1",
3
+ "version": "1.0.2",
4
4
  "description": "Local AI agent runtime for tools, memory, RAG, sub-agents, plugins, cron jobs, and channels.",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",
@@ -21,7 +21,8 @@
21
21
  "init": "node dist/index.js init",
22
22
  "continue": "node dist/index.js --continue",
23
23
  "release": "node scripts/release.mjs",
24
- "prepublishOnly": "pnpm build"
24
+ "prepublishOnly": "pnpm build",
25
+ "start:rag": "node dist/index.js --rag"
25
26
  },
26
27
  "keywords": [
27
28
  "ai",
@@ -44,12 +45,15 @@
44
45
  "@modelcontextprotocol/client": "2.0.0-alpha.2",
45
46
  "ai": "^6.0.206",
46
47
  "better-sqlite3": "^12.10.1",
48
+ "boxen": "^8.0.1",
47
49
  "croner": "^10.0.1",
48
50
  "dotenv": "^17.4.2",
49
51
  "fast-glob": "^3.3.3",
50
52
  "hono": "^4.12.27",
51
53
  "ink": "^7.1.1",
52
54
  "ink-select-input": "^6.2.0",
55
+ "marked": "^18.0.10",
56
+ "marked-terminal": "^7.3.0",
53
57
  "react": "^19.2.8",
54
58
  "sqlite-vec": "^0.1.9",
55
59
  "turndown": "^7.2.4",
@@ -57,9 +61,9 @@
57
61
  },
58
62
  "devDependencies": {
59
63
  "@types/better-sqlite3": "^7.6.13",
60
- "@types/react": "^19.2.18",
61
64
  "@types/json-schema": "^7.0.15",
62
65
  "@types/node": "^25.9.3",
66
+ "@types/react": "^19.2.18",
63
67
  "@types/turndown": "^5.0.6",
64
68
  "ajv": "^8.20.0",
65
69
  "esbuild": "^0.28.1",