@xiaodye/runeclaw 1.0.1 → 1.0.4

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 +13 -14
  2. package/dist/index.js +129 -49
  3. package/package.json +7 -3
package/README.md CHANGED
@@ -1,11 +1,9 @@
1
- <p align="center">
2
- <img src="./assets/runeclaw-logo.svg" alt="RuneClaw logo" width="720" />
3
- </p>
4
-
5
1
  # RuneClaw
6
2
 
7
3
  RuneClaw 是一个基于 TypeScript 的本地 AI Agent CLI。它把模型调用、文件工具、RAG、记忆、MCP、子 Agent、定时任务、插件和飞书通道放在同一个运行时里,适合做可迭代的个人 Agent 实验。
8
4
 
5
+ ![alt text](./assets/image.png)
6
+
9
7
  ## 特性
10
8
 
11
9
  - DeepSeek 兼容模型接入,默认使用 `deepseek-v4-flash`
@@ -13,7 +11,7 @@ RuneClaw 是一个基于 TypeScript 的本地 AI Agent CLI。它把模型调用
13
11
  - 本地文件工具:读取、写入、编辑、列目录、grep、glob
14
12
  - Shell 工具:可执行命令并返回输出
15
13
  - 记忆系统:跨会话保存、读取、搜索记忆
16
- - RAG 知识库:自动导入 `docs/`,也支持手动导入其它文档
14
+ - RAG 知识库:启动时加 `--rag` 自动导入 `docs/`,也支持手动导入其它文档
17
15
  - MCP 接入:可挂载外部工具,当前默认有 GitHub mock 示例
18
16
  - 子 Agent:支持任务分发与执行记录查看
19
17
  - 定时任务:支持查看任务与执行日志
@@ -68,13 +66,13 @@ runeclaw
68
66
 
69
67
  `runeclaw` 支持以下命令:
70
68
 
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` | 查看版本号 |
69
+ | 命令 | 说明 |
70
+ | -------------------------------- | ----------------------------------------------------- |
71
+ | `runeclaw` / `runeclaw start` | 启动交互式 Agent(默认) |
72
+ | `runeclaw init` | 运行初始化向导,生成 `runeclaw.config.json` 与 `.env` |
73
+ | `runeclaw continue` | 启动 Agent(预留会话续接) |
74
+ | `runeclaw help` / `--help` | 查看帮助 |
75
+ | `runeclaw version` / `--version` | 查看版本号 |
78
76
 
79
77
  ## 发布到 npm
80
78
 
@@ -89,6 +87,7 @@ pnpm run release -- major # 主版本号 +1(1.0.0 → 2.0.0)
89
87
  ```
90
88
 
91
89
  > 说明:
90
+ >
92
91
  > - 发布用 `npm publish --registry https://registry.npmjs.org/`,命令行参数优先级最高,能覆盖任何 `.npmrc` 里的镜像源配置(镜像源只读,无法发布),也无需改动全局 npm 配置;
93
92
  > - `npm publish` 的 `prepublishOnly` 会自动执行 `pnpm build`,无需手动构建;
94
93
  > - 首次发布前需要先 `npm login`。
@@ -150,7 +149,7 @@ SERPER_API_KEY=
150
149
 
151
150
  ### 基础
152
151
 
153
- - `exit` 退出
152
+ - `/exit` 退出
154
153
  - `/context` 查看上下文占用
155
154
  - `/usage` 查看 token 和成本统计
156
155
  - `status` 查看当前消息、记忆和知识库状态
@@ -222,7 +221,7 @@ src/
222
221
 
223
222
  ## 说明
224
223
 
225
- - 启动时会自动导入 `docs/` 下的 `.md` 文档到知识库
224
+ - 启动时默认**不**自动导入知识库;加 `--rag`(或 `pnpm start:rag`)才会导入 `docs/` 下的 `.md` 文档
226
225
  - 当前项目里的飞书配置只应通过 `.env` 提供,不要把 secret 写进 `runeclaw.config.json`
227
226
  - Web 搜索工具会优先使用 `TAVILY_API_KEY`,没有时可用 `SERPER_API_KEY`
228
227
 
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,20 +6192,26 @@ ${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
- if (!trimmed || trimmed === "exit") {
6205
+ if (trimmed === "/exit") {
6143
6206
  console.log("Bye!");
6144
6207
  cronService.stop();
6145
6208
  await gateway.stopAll();
6146
6209
  await pluginManager.unloadAll();
6147
6210
  rl.close();
6211
+ process2.exit(0);
6212
+ }
6213
+ if (!trimmed) {
6214
+ ask();
6148
6215
  return;
6149
6216
  }
6150
6217
  const ctx = {
@@ -6184,22 +6251,30 @@ ${message}`);
6184
6251
  const role = registry.getRole();
6185
6252
  const toolCount = registry.getActiveTools().length;
6186
6253
  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`);
6254
+ const modelName = model?.modelId || config.model.name || "unknown";
6255
+ const splash = boxen(
6256
+ [
6257
+ ` ${c.cyan}model:${c.reset} ${c.bold}${modelName}${c.reset}`,
6258
+ ` ${c.cyan}directory:${c.reset} ${c.bold}${process2.cwd()}${c.reset}`,
6259
+ ` ${c.cyan}role:${c.reset} ${c.yellow}${role}${c.reset} \xB7 \u53EF\u7528\u5DE5\u5177: ${c.green}${toolCount}${c.reset} \u4E2A`,
6260
+ "",
6261
+ ` ${c.dim}Sub-Agent \u6DF1\u5EA6 ${agentRegistry.getConfig().maxSpawnDepth} / \u5E76\u53D1 ${agentRegistry.getConfig().maxConcurrent} \xB7 /exit \u9000\u51FA \xB7 /help \u547D\u4EE4${c.reset}`
6262
+ ].join("\n"),
6263
+ {
6264
+ title: "RuneClaw",
6265
+ titleAlignment: "left",
6266
+ borderStyle: "round",
6267
+ borderColor: "cyan",
6268
+ padding: 1
6269
+ }
6270
+ );
6271
+ console.log(splash);
6194
6272
  console.log(
6195
- ` Sub-Agent: \u6700\u5927\u6DF1\u5EA6 ${agentRegistry.getConfig().maxSpawnDepth}\uFF0C\u6700\u5927\u5E76\u53D1 ${agentRegistry.getConfig().maxConcurrent}`
6273
+ `${c.green}\u2713 \u5C31\u7EEA${c.reset} \xB7 ${toolCount} \u4E2A\u5DE5\u5177 \xB7 ${availablePlugins.size} \u4E2A\u63D2\u4EF6\u5DF2\u52A0\u8F7D`
6196
6274
  );
6197
6275
  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")) {
6276
+ const enableRag = process2.argv.includes("--rag");
6277
+ if (enableRag && fs8.existsSync("docs")) {
6203
6278
  const files = fs8.readdirSync("docs").filter((f) => f.endsWith(".md"));
6204
6279
  if (files.length > 0) {
6205
6280
  console.log(` \u53D1\u73B0 ${files.length} \u4E2A\u6587\u6863\uFF0C\u81EA\u52A8\u5BFC\u5165\u77E5\u8BC6\u5E93...`);
@@ -6209,15 +6284,17 @@ ${message}`);
6209
6284
  const chunks = chunkDocument(path4, text);
6210
6285
  const embeddings = await embed(
6211
6286
  embedFn,
6212
- chunks.map((c) => c.text)
6287
+ chunks.map((c2) => c2.text)
6213
6288
  );
6214
6289
  vectorStore.addBatch(
6215
- chunks.map((c, i) => ({ chunk: c, embedding: embeddings[i] }))
6290
+ chunks.map((c2, i) => ({ chunk: c2, embedding: embeddings[i] }))
6216
6291
  );
6217
6292
  }
6218
6293
  console.log(` \u77E5\u8BC6\u5E93\u5C31\u7EEA\uFF0C\u5171 ${vectorStore.size()} \u4E2A\u7247\u6BB5
6219
6294
  `);
6220
6295
  }
6296
+ } else if (fs8.existsSync("docs")) {
6297
+ 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
6298
  }
6222
6299
  ask();
6223
6300
  }
@@ -6264,6 +6341,7 @@ var init_main = __esm({
6264
6341
  init_spawn_tools();
6265
6342
  init_agents();
6266
6343
  init_loader();
6344
+ init_theme();
6267
6345
  config = loadConfig();
6268
6346
  provider = createOpenAI({
6269
6347
  baseURL: config.model.baseURL,
@@ -6358,6 +6436,7 @@ var HELP_TEXT = `RuneClaw \u2014 \u672C\u5730 AI Agent \u8FD0\u884C\u65F6
6358
6436
  \u9009\u9879:
6359
6437
  -h, --help \u663E\u793A\u5E2E\u52A9
6360
6438
  -v, --version \u663E\u793A\u7248\u672C\u53F7
6439
+ --rag \u542F\u52A8\u65F6\u81EA\u52A8\u5BFC\u5165 docs/ \u4E0B\u7684\u6587\u6863\u5230\u77E5\u8BC6\u5E93
6361
6440
  `;
6362
6441
  function readVersion() {
6363
6442
  try {
@@ -6381,6 +6460,7 @@ async function main() {
6381
6460
  case "start":
6382
6461
  case "continue":
6383
6462
  case "--continue":
6463
+ case "--rag":
6384
6464
  case void 0: {
6385
6465
  const { startAgent: startAgent2 } = await Promise.resolve().then(() => (init_main(), main_exports));
6386
6466
  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.4",
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",