@xiaodye/runeclaw 1.0.0 → 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.
- package/README.md +23 -13
- package/dist/index.js +124 -48
- package/package.json +8 -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
|
+

|
|
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
|
|
18
|
+
- RAG 知识库:启动时加 `--rag` 自动导入 `docs/`,也支持手动导入其它文档
|
|
17
19
|
- MCP 接入:可挂载外部工具,当前默认有 GitHub mock 示例
|
|
18
20
|
- 子 Agent:支持任务分发与执行记录查看
|
|
19
21
|
- 定时任务:支持查看任务与执行日志
|
|
@@ -68,24 +70,32 @@ runeclaw
|
|
|
68
70
|
|
|
69
71
|
`runeclaw` 支持以下命令:
|
|
70
72
|
|
|
71
|
-
| 命令
|
|
72
|
-
|
|
|
73
|
-
| `runeclaw` / `runeclaw start`
|
|
74
|
-
| `runeclaw init`
|
|
75
|
-
| `runeclaw continue`
|
|
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
|
|
|
81
|
-
包名是 `@xiaodye/runeclaw`,`publishConfig.access: public` 已配置好(scoped
|
|
83
|
+
包名是 `@xiaodye/runeclaw`,`publishConfig.access: public` 已配置好(scoped 包默认私有,必须显式公开)。
|
|
84
|
+
|
|
85
|
+
一键发布(`scripts/release.mjs` 自动完成:**版本号 +1 → 官方源发布 → 输出结果**):
|
|
82
86
|
|
|
83
87
|
```bash
|
|
84
|
-
|
|
85
|
-
pnpm
|
|
86
|
-
|
|
88
|
+
pnpm run release # 默认补丁号 +1(1.0.0 → 1.0.1)
|
|
89
|
+
pnpm run release -- minor # 次版本号 +1(1.0.0 → 1.1.0)
|
|
90
|
+
pnpm run release -- major # 主版本号 +1(1.0.0 → 2.0.0)
|
|
87
91
|
```
|
|
88
92
|
|
|
93
|
+
> 说明:
|
|
94
|
+
>
|
|
95
|
+
> - 发布用 `npm publish --registry https://registry.npmjs.org/`,命令行参数优先级最高,能覆盖任何 `.npmrc` 里的镜像源配置(镜像源只读,无法发布),也无需改动全局 npm 配置;
|
|
96
|
+
> - `npm publish` 的 `prepublishOnly` 会自动执行 `pnpm build`,无需手动构建;
|
|
97
|
+
> - 首次发布前需要先 `npm login`。
|
|
98
|
+
|
|
89
99
|
发布后其他人可以这样安装使用:
|
|
90
100
|
|
|
91
101
|
```bash
|
|
@@ -215,7 +225,7 @@ src/
|
|
|
215
225
|
|
|
216
226
|
## 说明
|
|
217
227
|
|
|
218
|
-
-
|
|
228
|
+
- 启动时默认**不**自动导入知识库;加 `--rag`(或 `pnpm start:rag`)才会导入 `docs/` 下的 `.md` 文档
|
|
219
229
|
- 当前项目里的飞书配置只应通过 `.env` 提供,不要把 secret 写进 `runeclaw.config.json`
|
|
220
230
|
- Web 搜索工具会优先使用 `TAVILY_API_KEY`,没有时可用 `SERPER_API_KEY`
|
|
221
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((
|
|
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
|
|
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
|
-
|
|
1333
|
-
|
|
1332
|
+
c2 += inputLike * p.input / 1e6;
|
|
1333
|
+
c2 += s.outputTokens * p.output / 1e6;
|
|
1334
1334
|
}
|
|
1335
|
-
return
|
|
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
|
-
|
|
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
|
-
`
|
|
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(`
|
|
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(
|
|
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 ?
|
|
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)
|
|
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(
|
|
1790
|
+
console.log(` ${c.dim}\u2192 \u7EE7\u7EED\u4E0B\u4E00\u6B65...${c.reset}`);
|
|
1746
1791
|
}
|
|
1747
1792
|
if (step >= MAX_STEPS) {
|
|
1748
|
-
console.log(
|
|
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
|
|
3041
|
-
if (
|
|
3042
|
-
else if (
|
|
3043
|
-
else rowCells.push(fg(
|
|
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((
|
|
3935
|
+
chunks.map((c2) => c2.text)
|
|
3888
3936
|
);
|
|
3889
3937
|
vectorStore2.addBatch(
|
|
3890
|
-
chunks.map((
|
|
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((
|
|
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 (
|
|
4867
|
-
const body = await
|
|
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
|
|
4932
|
+
return c2.json({ code: 0 });
|
|
4885
4933
|
});
|
|
4886
|
-
app.get("/", (
|
|
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
|
|
5008
|
+
return c2.html(html);
|
|
4961
5009
|
});
|
|
4962
|
-
app.get("/health", (
|
|
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.
|
|
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(
|
|
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
|
-
|
|
6188
|
-
|
|
6189
|
-
|
|
6190
|
-
|
|
6191
|
-
|
|
6192
|
-
|
|
6193
|
-
|
|
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
|
-
|
|
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
|
-
|
|
6199
|
-
|
|
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((
|
|
6283
|
+
chunks.map((c2) => c2.text)
|
|
6213
6284
|
);
|
|
6214
6285
|
vectorStore.addBatch(
|
|
6215
|
-
chunks.map((
|
|
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.
|
|
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",
|
|
@@ -20,7 +20,9 @@
|
|
|
20
20
|
"start": "node dist/index.js",
|
|
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",
|
|
25
|
+
"start:rag": "node dist/index.js --rag"
|
|
24
26
|
},
|
|
25
27
|
"keywords": [
|
|
26
28
|
"ai",
|
|
@@ -43,12 +45,15 @@
|
|
|
43
45
|
"@modelcontextprotocol/client": "2.0.0-alpha.2",
|
|
44
46
|
"ai": "^6.0.206",
|
|
45
47
|
"better-sqlite3": "^12.10.1",
|
|
48
|
+
"boxen": "^8.0.1",
|
|
46
49
|
"croner": "^10.0.1",
|
|
47
50
|
"dotenv": "^17.4.2",
|
|
48
51
|
"fast-glob": "^3.3.3",
|
|
49
52
|
"hono": "^4.12.27",
|
|
50
53
|
"ink": "^7.1.1",
|
|
51
54
|
"ink-select-input": "^6.2.0",
|
|
55
|
+
"marked": "^18.0.10",
|
|
56
|
+
"marked-terminal": "^7.3.0",
|
|
52
57
|
"react": "^19.2.8",
|
|
53
58
|
"sqlite-vec": "^0.1.9",
|
|
54
59
|
"turndown": "^7.2.4",
|
|
@@ -56,9 +61,9 @@
|
|
|
56
61
|
},
|
|
57
62
|
"devDependencies": {
|
|
58
63
|
"@types/better-sqlite3": "^7.6.13",
|
|
59
|
-
"@types/react": "^19.2.18",
|
|
60
64
|
"@types/json-schema": "^7.0.15",
|
|
61
65
|
"@types/node": "^25.9.3",
|
|
66
|
+
"@types/react": "^19.2.18",
|
|
62
67
|
"@types/turndown": "^5.0.6",
|
|
63
68
|
"ajv": "^8.20.0",
|
|
64
69
|
"esbuild": "^0.28.1",
|