jinzd-ai-cli 0.1.48 → 0.1.49
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/CLAUDE.md
CHANGED
|
@@ -38,8 +38,8 @@ src/
|
|
|
38
38
|
│ ├── config-manager.ts # 读写 ~/.aicli/config.json,三层优先级:env > file > default
|
|
39
39
|
│ └── env-loader.ts # AICLI_API_KEY_* 等环境变量映射
|
|
40
40
|
├── session/
|
|
41
|
-
│ ├── session.ts # Session(addMessage / clear / compact / toJSON / fromJSON)
|
|
42
|
-
│ └── session-manager.ts # CRUD for ~/.aicli/history/*.json
|
|
41
|
+
│ ├── session.ts # Session(addMessage / clear / compact / fork / toJSON / fromJSON)
|
|
42
|
+
│ └── session-manager.ts # CRUD + forkSession for ~/.aicli/history/*.json
|
|
43
43
|
├── repl/
|
|
44
44
|
│ ├── repl.ts # 主 REPL 循环(MAX_TOOL_ROUNDS=20,handleChatWithTools agentic loop)
|
|
45
45
|
│ ├── renderer.ts # 终端输出(renderStream / renderResponse 均不加前置 \n)
|
|
@@ -47,7 +47,7 @@ src/
|
|
|
47
47
|
│ ├── dev-state.ts # 开发状态交接(provider/model 切换时快照生成、save/load/clear)
|
|
48
48
|
│ ├── setup-wizard.ts # @inquirer/prompts 首次运行交互式设置
|
|
49
49
|
│ └── commands/
|
|
50
|
-
│ └── index.ts # CommandRegistry +
|
|
50
|
+
│ └── index.ts # CommandRegistry + 35个命令(/help /about /provider /model /clear /compact /plan /session /system /context /status /search /undo /export /copy /cost /init /skill /tools /plugins /mcp /config /checkpoint /review /commands /test /scaffold /add-dir /memory /doctor /bug /think /diff /fork /exit)
|
|
51
51
|
│ ├── custom-commands.ts # CustomCommandManager(~/.aicli/commands/*.md 用户自定义命令)
|
|
52
52
|
├── skills/
|
|
53
53
|
│ ├── types.ts # Skill/SkillMeta 接口、parseSkillFile(YAML frontmatter 解析)
|
|
@@ -350,6 +350,81 @@ const stdinLines = Array.isArray(rawStdin) ? rawStdin.map(String)
|
|
|
350
350
|
- [x] **web_fetch DNS 解析时 SSRF 防护**(v0.1.25):新增 `resolveAndCheck()` 函数,用 `dns.promises.lookup()` 预解析域名,检查结果 IP 是否为私有地址。初始 URL 和 redirect 目标均校验。
|
|
351
351
|
- [ ] **`persistentCwd` 全局状态**:bash 工具的当前工作目录是模块级全局变量,多 session 并发时可能串扰。现阶段单 session REPL 无影响,GUI 多会话扩展时需重构为 per-session 状态。
|
|
352
352
|
|
|
353
|
+
## 本轮开发完成记录(2026-03-08,v0.1.47 → v0.1.48)
|
|
354
|
+
|
|
355
|
+
### Tier 2 体验增强:/undo 增强 + /fork 对话分支
|
|
356
|
+
|
|
357
|
+
**Feature 1:/undo 增强 — bash 工具文件追踪 + 命令增强**
|
|
358
|
+
|
|
359
|
+
| 文件 | 变更 |
|
|
360
|
+
|------|------|
|
|
361
|
+
| `src/tools/undo-stack.ts` | `UndoEntry` 新增 `isDirectory?: boolean` + `pushNewFile()` / `pushNewDir()` 方法 + `undo()` 目录分支 |
|
|
362
|
+
| `src/tools/builtin/bash.ts` | 三辅助函数 + `execute()` 集成 |
|
|
363
|
+
| `src/repl/commands/index.ts` | `/undo` 重写为 `/undo [list\|<n>]` |
|
|
364
|
+
|
|
365
|
+
- **UndoStack 扩展**:新增 `pushNewFile(filePath, desc)` 和 `pushNewDir(dirPath, desc)` 方法,previousContent=null 表示新建(undo 时删除)。`undo()` 新增 `isDirectory` 分支,用 `rmdirSync` 尝试删除空目录,非空给出提示。
|
|
366
|
+
- **Bash 工具文件追踪**(浅层 CWD 快照 + 命令模式解析):
|
|
367
|
+
- `snapshotDir(dir)` — `readdirSync` 获取目录下所有条目的绝对路径 Set
|
|
368
|
+
- `parseCreationTargets(command, cwd)` — 正则解析 touch/mkdir/echo>/cp/New-Item 等常见创建命令的目标路径
|
|
369
|
+
- `pushBashUndoEntries(beforeSnapshot, parsedTargetsBefore, cwd)` — 对比前后快照 + 解析目标,为新建文件/目录推入 undo 条目
|
|
370
|
+
- 集成到 `execute()`:执行前快照 + 构建目标存在状态 Map;执行后(成功/失败均)调用 pushBashUndoEntries
|
|
371
|
+
- **/undo 命令增强**:
|
|
372
|
+
- `/undo` — 撤销最近 1 次(向后兼容)
|
|
373
|
+
- `/undo list` — 显示完整 undo 栈(编号、类型标签 `[new]`/`[mod]`/`[dir]`、描述、时间)
|
|
374
|
+
- `/undo <n>` — 连续撤销最近 N 次操作,逐条显示进度
|
|
375
|
+
|
|
376
|
+
**Feature 2:/fork 对话分支**
|
|
377
|
+
|
|
378
|
+
| 文件 | 变更 |
|
|
379
|
+
|------|------|
|
|
380
|
+
| `src/session/session.ts` | 静态 `fork(original, newId, messageCount, newTitle?)` 方法 |
|
|
381
|
+
| `src/session/session-manager.ts` | `forkSession(messageCount, title?)` 方法 |
|
|
382
|
+
| `src/repl/commands/index.ts` | `/fork` 命令 + `CommandContext.forkSession` |
|
|
383
|
+
| `src/repl/repl.ts` | ctx.forkSession 注入 + tab 补全(/undo list, /fork checkpoint 名称) |
|
|
384
|
+
|
|
385
|
+
- **Session.fork()**:复制 messages[0..messageCount],保留范围内 checkpoints(深拷贝),新 UUID,title 默认 "Fork of <原标题>"
|
|
386
|
+
- **SessionManager.forkSession()**:先保存原始 session → Session.fork 创建分叉 → 设为当前 → 保存并返回
|
|
387
|
+
- **/fork 命令**:
|
|
388
|
+
- `/fork` — 从当前位置分叉(复制全部消息)
|
|
389
|
+
- `/fork <checkpoint-name>` — 从指定 checkpoint 分叉(仅复制到该 checkpoint 的消息)
|
|
390
|
+
- 显示原始/新 session ID、title、消息数、保留 checkpoint 数
|
|
391
|
+
- 提示用户 `/session load <id>` 切回原始
|
|
392
|
+
- **Tab 补全**:`/undo` 提供 `list`;`/fork` 提供当前 session 的 checkpoint 名称列表
|
|
393
|
+
|
|
394
|
+
### 版本与收尾
|
|
395
|
+
- `src/core/constants.ts`:VERSION `0.1.47` → `0.1.48`
|
|
396
|
+
- `package.json`:version 同步
|
|
397
|
+
- `src/repl/renderer.ts`:命令计数 34 → 35,命令列表新增 `/fork`,新增 1 条特性(/fork 对话分支),更新 /undo 描述
|
|
398
|
+
- 构建验证:`npm run build` 零错误(ESM + CJS 双产物)
|
|
399
|
+
- 发布:`npm publish` → `jinzd-ai-cli@0.1.48`
|
|
400
|
+
|
|
401
|
+
### 本轮变更文件汇总
|
|
402
|
+
|
|
403
|
+
| 文件 | 变更类型 | 说明 |
|
|
404
|
+
|------|---------|------|
|
|
405
|
+
| `src/core/constants.ts` | 修改 | VERSION 0.1.47 → 0.1.48 |
|
|
406
|
+
| `src/tools/undo-stack.ts` | 修改 | isDirectory 字段 + pushNewFile/pushNewDir + undo 目录支持 |
|
|
407
|
+
| `src/tools/builtin/bash.ts` | 修改 | snapshotDir + parseCreationTargets + pushBashUndoEntries + execute 集成 |
|
|
408
|
+
| `src/session/session.ts` | 修改 | 静态 fork() 方法 |
|
|
409
|
+
| `src/session/session-manager.ts` | 修改 | forkSession() 方法 |
|
|
410
|
+
| `src/repl/commands/index.ts` | 修改 | /undo 增强 + /fork 命令 + CommandContext.forkSession + /help 更新 |
|
|
411
|
+
| `src/repl/repl.ts` | 修改 | ctx.forkSession 注入 + tab 补全(/undo, /fork) |
|
|
412
|
+
| `src/repl/renderer.ts` | 修改 | /about 35 命令 + 特性更新 |
|
|
413
|
+
| `package.json` | 修改 | version 0.1.47 → 0.1.48 |
|
|
414
|
+
|
|
415
|
+
### 下一步建议
|
|
416
|
+
|
|
417
|
+
#### Tier 2 — 体验增强(剩余)
|
|
418
|
+
1. **L1 低危**:`run-tests.ts` 的 `JSON.parse(package.json)` 细粒度错误处理
|
|
419
|
+
2. **IDE 集成**:VS Code 扩展(架构已准备就绪,core/providers/tools 无终端依赖)
|
|
420
|
+
3. **OAuth/浏览器登录**:无需手动填 API Key,打开浏览器完成 OAuth 流程自动保存 token
|
|
421
|
+
|
|
422
|
+
#### Tier 3 — 长远方向
|
|
423
|
+
4. **Web UI**:基于 EventBus + WebSocket 的浏览器界面,复用 core/providers/tools 层
|
|
424
|
+
5. **GitHub 仓库迁移**:从 gitee 迁移到 GitHub,npm 包受众更广
|
|
425
|
+
|
|
426
|
+
---
|
|
427
|
+
|
|
353
428
|
## 本轮开发完成记录(2026-03-07,v0.1.40 → v0.1.41)
|
|
354
429
|
|
|
355
430
|
### Bug 修复:Kimi 虚假完成声明(方案 C)
|
|
@@ -536,8 +611,8 @@ const stdinLines = Array.isArray(rawStdin) ? rawStdin.map(String)
|
|
|
536
611
|
4. **L1 低危**:`run-tests.ts` 的 `JSON.parse(package.json)` 细粒度错误处理
|
|
537
612
|
5. **IDE 集成**:VS Code 扩展(架构已准备就绪,core/providers/tools 无终端依赖)
|
|
538
613
|
6. **OAuth/浏览器登录**:无需手动填 API Key,打开浏览器完成 OAuth 流程自动保存 token
|
|
539
|
-
7.
|
|
540
|
-
8.
|
|
614
|
+
7. ~~**`/undo` 增强**~~:✅ 已在 v0.1.48 实现(bash 文件追踪 + /undo list + /undo <n>)
|
|
615
|
+
8. ~~**对话分支(fork)**~~:✅ 已在 v0.1.48 实现(/fork [checkpoint-name])
|
|
541
616
|
|
|
542
617
|
#### Tier 3 — 长远方向
|
|
543
618
|
9. **Web UI**:基于 EventBus + WebSocket 的浏览器界面,复用 core/providers/tools 层
|
package/dist/index.js
CHANGED
|
@@ -29,7 +29,7 @@ import {
|
|
|
29
29
|
SUBAGENT_MAX_ROUNDS_LIMIT,
|
|
30
30
|
VERSION,
|
|
31
31
|
runTestsTool
|
|
32
|
-
} from "./chunk-
|
|
32
|
+
} from "./chunk-P4XKHPXU.js";
|
|
33
33
|
|
|
34
34
|
// src/index.ts
|
|
35
35
|
import { program } from "commander";
|
|
@@ -2061,8 +2061,8 @@ var SessionManager = class {
|
|
|
2061
2061
|
|
|
2062
2062
|
// src/repl/repl.ts
|
|
2063
2063
|
import * as readline from "readline";
|
|
2064
|
-
import { existsSync as existsSync19, readFileSync as readFileSync13, readdirSync as
|
|
2065
|
-
import { join as join14, resolve as
|
|
2064
|
+
import { existsSync as existsSync19, readFileSync as readFileSync13, readdirSync as readdirSync11, statSync as statSync8 } from "fs";
|
|
2065
|
+
import { join as join14, resolve as resolve5, extname as extname4, dirname as dirname6, basename as basename6 } from "path";
|
|
2066
2066
|
import chalk10 from "chalk";
|
|
2067
2067
|
|
|
2068
2068
|
// src/repl/renderer.ts
|
|
@@ -2426,8 +2426,8 @@ var Renderer = class {
|
|
|
2426
2426
|
process.stdout.write("\n\n");
|
|
2427
2427
|
}
|
|
2428
2428
|
if (fileStream) {
|
|
2429
|
-
await new Promise((
|
|
2430
|
-
fileStream.end((err) => err ? reject(err) :
|
|
2429
|
+
await new Promise((resolve6, reject) => {
|
|
2430
|
+
fileStream.end((err) => err ? reject(err) : resolve6());
|
|
2431
2431
|
});
|
|
2432
2432
|
const kb = (Buffer.byteLength(fullContent, "utf-8") / 1024).toFixed(1);
|
|
2433
2433
|
process.stdout.write(theme.success(` \u2705 \u5DF2\u4FDD\u5B58: ${options.saveToFile} (${kb} KB)
|
|
@@ -2482,7 +2482,7 @@ var Renderer = class {
|
|
|
2482
2482
|
process.stdout.write(displayed.slice(pos, end));
|
|
2483
2483
|
pos = end;
|
|
2484
2484
|
if (pos < displayed.length) {
|
|
2485
|
-
await new Promise((
|
|
2485
|
+
await new Promise((resolve6) => setTimeout(resolve6, DELAY_MS));
|
|
2486
2486
|
}
|
|
2487
2487
|
}
|
|
2488
2488
|
process.stdout.write("\n\n");
|
|
@@ -4288,7 +4288,7 @@ ${hint}` : "")
|
|
|
4288
4288
|
description: "Run project tests and show structured report",
|
|
4289
4289
|
usage: "/test [command|filter]",
|
|
4290
4290
|
async execute(args, _ctx) {
|
|
4291
|
-
const { executeTests } = await import("./run-tests-
|
|
4291
|
+
const { executeTests } = await import("./run-tests-JJVZWQKI.js");
|
|
4292
4292
|
const argStr = args.join(" ").trim();
|
|
4293
4293
|
let testArgs = {};
|
|
4294
4294
|
if (argStr) {
|
|
@@ -4685,7 +4685,7 @@ var IGNORE_ENTER_MS = 80;
|
|
|
4685
4685
|
function selectFromList(prompt, items, initialIndex = 0) {
|
|
4686
4686
|
if (items.length === 0) return Promise.resolve(null);
|
|
4687
4687
|
const PAGE = 12;
|
|
4688
|
-
return new Promise((
|
|
4688
|
+
return new Promise((resolve6) => {
|
|
4689
4689
|
let selected = Math.max(0, Math.min(initialIndex, items.length - 1));
|
|
4690
4690
|
let windowStart = Math.max(0, selected - Math.floor(PAGE / 2));
|
|
4691
4691
|
let lastRenderedLines = 0;
|
|
@@ -4760,7 +4760,7 @@ function selectFromList(prompt, items, initialIndex = 0) {
|
|
|
4760
4760
|
process.stdout.write(chalk5.dim(` \u2714 ${result}
|
|
4761
4761
|
`));
|
|
4762
4762
|
}
|
|
4763
|
-
|
|
4763
|
+
resolve6(result);
|
|
4764
4764
|
};
|
|
4765
4765
|
const handleSequence = (seq) => {
|
|
4766
4766
|
if (seq === "\x1B") {
|
|
@@ -4824,13 +4824,13 @@ function selectFromList(prompt, items, initialIndex = 0) {
|
|
|
4824
4824
|
}
|
|
4825
4825
|
};
|
|
4826
4826
|
if (!process.stdin.isTTY) {
|
|
4827
|
-
|
|
4827
|
+
resolve6(items[0]?.value ?? null);
|
|
4828
4828
|
return;
|
|
4829
4829
|
}
|
|
4830
4830
|
try {
|
|
4831
4831
|
process.stdin.setRawMode(true);
|
|
4832
4832
|
} catch {
|
|
4833
|
-
|
|
4833
|
+
resolve6(items[0]?.value ?? null);
|
|
4834
4834
|
return;
|
|
4835
4835
|
}
|
|
4836
4836
|
savedDataListeners = process.stdin.rawListeners("data");
|
|
@@ -5051,8 +5051,9 @@ function updateCwdFromCommand(command, baseCwd) {
|
|
|
5051
5051
|
}
|
|
5052
5052
|
|
|
5053
5053
|
// src/tools/builtin/read-file.ts
|
|
5054
|
-
import { readFileSync as readFileSync5, existsSync as existsSync8, statSync as statSync4 } from "fs";
|
|
5055
|
-
import {
|
|
5054
|
+
import { readFileSync as readFileSync5, existsSync as existsSync8, statSync as statSync4, readdirSync as readdirSync4 } from "fs";
|
|
5055
|
+
import { execSync as execSync5 } from "child_process";
|
|
5056
|
+
import { extname, resolve as resolve3, basename as basename2, sep, dirname as dirname3 } from "path";
|
|
5056
5057
|
import { homedir as homedir2 } from "os";
|
|
5057
5058
|
var MAX_FILE_BYTES = 10 * 1024 * 1024;
|
|
5058
5059
|
function getSensitiveWarning(normalizedPath) {
|
|
@@ -5120,6 +5121,55 @@ function isBinaryBuffer(buf) {
|
|
|
5120
5121
|
}
|
|
5121
5122
|
return nullCount / sample.length > 0.1;
|
|
5122
5123
|
}
|
|
5124
|
+
function findSimilarFiles(filePath) {
|
|
5125
|
+
const targetName = basename2(filePath).toLowerCase();
|
|
5126
|
+
if (!targetName) return [];
|
|
5127
|
+
const cwd = process.cwd();
|
|
5128
|
+
const candidates = [];
|
|
5129
|
+
try {
|
|
5130
|
+
for (const entry of readdirSync4(cwd, { withFileTypes: true })) {
|
|
5131
|
+
if (entry.isFile() && entry.name.toLowerCase() === targetName) {
|
|
5132
|
+
candidates.push(entry.name);
|
|
5133
|
+
}
|
|
5134
|
+
if (entry.isDirectory() && !entry.name.startsWith(".") && entry.name !== "node_modules") {
|
|
5135
|
+
try {
|
|
5136
|
+
const subDir = resolve3(cwd, entry.name);
|
|
5137
|
+
for (const sub of readdirSync4(subDir, { withFileTypes: true })) {
|
|
5138
|
+
if (sub.isFile() && sub.name.toLowerCase() === targetName) {
|
|
5139
|
+
candidates.push(`${entry.name}/${sub.name}`);
|
|
5140
|
+
}
|
|
5141
|
+
}
|
|
5142
|
+
} catch {
|
|
5143
|
+
}
|
|
5144
|
+
}
|
|
5145
|
+
}
|
|
5146
|
+
} catch {
|
|
5147
|
+
}
|
|
5148
|
+
return candidates.slice(0, 5);
|
|
5149
|
+
}
|
|
5150
|
+
function tryExtractPdfText(absPath) {
|
|
5151
|
+
try {
|
|
5152
|
+
const output = execSync5(`pdftotext "${absPath}" -`, {
|
|
5153
|
+
timeout: 15e3,
|
|
5154
|
+
encoding: "utf-8",
|
|
5155
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
5156
|
+
});
|
|
5157
|
+
if (output.trim().length > 0) return output;
|
|
5158
|
+
} catch {
|
|
5159
|
+
}
|
|
5160
|
+
try {
|
|
5161
|
+
const pyScript = `import sys; exec("try:\\n from pdfminer.high_level import extract_text\\n print(extract_text(sys.argv[1]))\\nexcept: pass")`;
|
|
5162
|
+
const output = execSync5(`python -c "${pyScript}" "${absPath}"`, {
|
|
5163
|
+
timeout: 15e3,
|
|
5164
|
+
encoding: "utf-8",
|
|
5165
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
5166
|
+
env: { ...process.env, PYTHONUTF8: "1", PYTHONIOENCODING: "utf-8" }
|
|
5167
|
+
});
|
|
5168
|
+
if (output.trim().length > 0) return output;
|
|
5169
|
+
} catch {
|
|
5170
|
+
}
|
|
5171
|
+
return null;
|
|
5172
|
+
}
|
|
5123
5173
|
var readFileTool = {
|
|
5124
5174
|
definition: {
|
|
5125
5175
|
name: "read_file",
|
|
@@ -5144,7 +5194,23 @@ var readFileTool = {
|
|
|
5144
5194
|
const encoding = args["encoding"] ?? "utf-8";
|
|
5145
5195
|
if (!filePath) throw new Error("path is required");
|
|
5146
5196
|
const normalizedPath = resolve3(filePath);
|
|
5147
|
-
if (!existsSync8(normalizedPath))
|
|
5197
|
+
if (!existsSync8(normalizedPath)) {
|
|
5198
|
+
const suggestions = findSimilarFiles(filePath);
|
|
5199
|
+
if (suggestions.length > 0) {
|
|
5200
|
+
throw new Error(
|
|
5201
|
+
`File not found: ${filePath}
|
|
5202
|
+
\u5F53\u524D\u5DE5\u4F5C\u76EE\u5F55: ${process.cwd()}
|
|
5203
|
+
\u627E\u5230\u540C\u540D\u6587\u4EF6\uFF0C\u4F60\u662F\u5426\u8981\u627E\uFF1A
|
|
5204
|
+
` + suggestions.map((s) => ` \u2192 ${s}`).join("\n") + `
|
|
5205
|
+
\u8BF7\u4F7F\u7528\u6B63\u786E\u7684\u76F8\u5BF9\u8DEF\u5F84\u91CD\u8BD5\u3002`
|
|
5206
|
+
);
|
|
5207
|
+
}
|
|
5208
|
+
throw new Error(
|
|
5209
|
+
`File not found: ${filePath}
|
|
5210
|
+
\u5F53\u524D\u5DE5\u4F5C\u76EE\u5F55: ${process.cwd()}
|
|
5211
|
+
\u8BF7\u4F7F\u7528 list_dir \u786E\u8BA4\u6587\u4EF6\u8DEF\u5F84\u540E\u91CD\u8BD5\u3002`
|
|
5212
|
+
);
|
|
5213
|
+
}
|
|
5148
5214
|
const { size } = statSync4(normalizedPath);
|
|
5149
5215
|
if (size > MAX_FILE_BYTES) {
|
|
5150
5216
|
const mb = (size / 1024 / 1024).toFixed(1);
|
|
@@ -5157,12 +5223,31 @@ var readFileTool = {
|
|
|
5157
5223
|
}
|
|
5158
5224
|
const sensitiveWarning = getSensitiveWarning(normalizedPath);
|
|
5159
5225
|
const ext = extname(normalizedPath).toLowerCase();
|
|
5226
|
+
if (ext === ".pdf") {
|
|
5227
|
+
const pdfText = tryExtractPdfText(normalizedPath);
|
|
5228
|
+
if (pdfText) {
|
|
5229
|
+
const lines2 = pdfText.split("\n").length;
|
|
5230
|
+
return `[PDF extracted: ${filePath} | ${lines2} lines]
|
|
5231
|
+
|
|
5232
|
+
${pdfText}`;
|
|
5233
|
+
}
|
|
5234
|
+
const dir = dirname3(normalizedPath);
|
|
5235
|
+
const nameNoExt = basename2(normalizedPath, ext);
|
|
5236
|
+
const textAlts = [".md", ".txt", ".html"].map((e) => resolve3(dir, nameNoExt + e)).filter(existsSync8);
|
|
5237
|
+
return `[Binary file: ${filePath}]
|
|
5238
|
+
PDF \u6587\u672C\u63D0\u53D6\u5931\u8D25\uFF08pdftotext \u548C pdfminer \u5747\u4E0D\u53EF\u7528\uFF09\u3002
|
|
5239
|
+
` + (textAlts.length > 0 ? `\u627E\u5230\u53EF\u66FF\u4EE3\u7684\u6587\u672C\u7248\u672C\uFF1A
|
|
5240
|
+
${textAlts.map((p) => ` \u2192 ${basename2(p)}`).join("\n")}
|
|
5241
|
+
\u8BF7\u4F7F\u7528 read_file \u8BFB\u53D6\u4E0A\u8FF0\u6587\u4EF6\u3002` : `\u5982\u9700\u4F7F\u7528\u5176\u5185\u5BB9\uFF0C\u8BF7\u4F7F\u7528 bash \u5DE5\u5177\u5B89\u88C5\u5E76\u8C03\u7528 pdftotext\uFF1A
|
|
5242
|
+
pip install pdfminer.six # \u5B89\u88C5 Python PDF \u5E93
|
|
5243
|
+
\u6216\u5C06 PDF \u624B\u52A8\u8F6C\u6362\u4E3A .md / .txt \u6587\u4EF6\u540E\u518D\u8BFB\u53D6\u3002`);
|
|
5244
|
+
}
|
|
5160
5245
|
if (BINARY_EXTENSIONS.has(ext)) {
|
|
5161
5246
|
return `[Binary file: ${filePath}]
|
|
5162
5247
|
\u6B64\u6587\u4EF6\u4E3A\u4E8C\u8FDB\u5236\u683C\u5F0F\uFF08${ext}\uFF09\uFF0C\u65E0\u6CD5\u4F5C\u4E3A\u6587\u672C\u8BFB\u53D6\u3002
|
|
5163
5248
|
\u5982\u9700\u4F7F\u7528\u5176\u5185\u5BB9\uFF0C\u8BF7\u8003\u8651\uFF1A
|
|
5164
5249
|
1. \u5C06\u6587\u4EF6\u8F6C\u6362\u4E3A\u6587\u672C\u683C\u5F0F\u540E\u518D\u8BFB\u53D6
|
|
5165
|
-
2. \u4F7F\u7528 bash \u5DE5\u5177\u8C03\u7528\u5916\u90E8\u8F6C\u6362\u7A0B\u5E8F\uFF08\u5982
|
|
5250
|
+
2. \u4F7F\u7528 bash \u5DE5\u5177\u8C03\u7528\u5916\u90E8\u8F6C\u6362\u7A0B\u5E8F\uFF08\u5982 pandoc \u7B49\uFF09
|
|
5166
5251
|
3. \u82E5\u6709\u5BF9\u5E94\u7684\u7EAF\u6587\u672C\u7248\u672C\uFF08.md / .txt\uFF09\uFF0C\u8BF7\u76F4\u63A5\u8BFB\u53D6\u90A3\u4E2A\u6587\u4EF6`;
|
|
5167
5252
|
}
|
|
5168
5253
|
const buf = readFileSync5(normalizedPath);
|
|
@@ -5186,7 +5271,7 @@ ${content}`;
|
|
|
5186
5271
|
|
|
5187
5272
|
// src/tools/builtin/write-file.ts
|
|
5188
5273
|
import { writeFileSync as writeFileSync5, appendFileSync as appendFileSync2, mkdirSync as mkdirSync5 } from "fs";
|
|
5189
|
-
import { dirname as
|
|
5274
|
+
import { dirname as dirname4 } from "path";
|
|
5190
5275
|
var writeFileTool = {
|
|
5191
5276
|
definition: {
|
|
5192
5277
|
name: "write_file",
|
|
@@ -5226,7 +5311,7 @@ var writeFileTool = {
|
|
|
5226
5311
|
if (!appendMode) {
|
|
5227
5312
|
undoStack.push(filePath, `write_file: ${filePath}`);
|
|
5228
5313
|
}
|
|
5229
|
-
mkdirSync5(
|
|
5314
|
+
mkdirSync5(dirname4(filePath), { recursive: true });
|
|
5230
5315
|
if (appendMode) {
|
|
5231
5316
|
appendFileSync2(filePath, content, encoding);
|
|
5232
5317
|
} else {
|
|
@@ -5458,8 +5543,8 @@ function truncatePreview(str, maxLen = 80) {
|
|
|
5458
5543
|
}
|
|
5459
5544
|
|
|
5460
5545
|
// src/tools/builtin/list-dir.ts
|
|
5461
|
-
import { readdirSync as
|
|
5462
|
-
import { join as join6 } from "path";
|
|
5546
|
+
import { readdirSync as readdirSync5, statSync as statSync5, existsSync as existsSync10 } from "fs";
|
|
5547
|
+
import { join as join6, basename as basename3 } from "path";
|
|
5463
5548
|
var listDirTool = {
|
|
5464
5549
|
definition: {
|
|
5465
5550
|
name: "list_dir",
|
|
@@ -5481,7 +5566,33 @@ var listDirTool = {
|
|
|
5481
5566
|
async execute(args) {
|
|
5482
5567
|
const dirPath = String(args["path"] ?? process.cwd());
|
|
5483
5568
|
const recursive = Boolean(args["recursive"] ?? false);
|
|
5484
|
-
if (!existsSync10(dirPath))
|
|
5569
|
+
if (!existsSync10(dirPath)) {
|
|
5570
|
+
const targetName = basename3(dirPath).toLowerCase();
|
|
5571
|
+
const cwd = process.cwd();
|
|
5572
|
+
const suggestions = [];
|
|
5573
|
+
try {
|
|
5574
|
+
for (const entry of readdirSync5(cwd, { withFileTypes: true })) {
|
|
5575
|
+
if (entry.isDirectory() && entry.name.toLowerCase() === targetName) {
|
|
5576
|
+
suggestions.push(entry.name);
|
|
5577
|
+
}
|
|
5578
|
+
}
|
|
5579
|
+
} catch {
|
|
5580
|
+
}
|
|
5581
|
+
if (suggestions.length > 0) {
|
|
5582
|
+
throw new Error(
|
|
5583
|
+
`Directory not found: ${dirPath}
|
|
5584
|
+
\u5F53\u524D\u5DE5\u4F5C\u76EE\u5F55: ${cwd}
|
|
5585
|
+
\u627E\u5230\u540C\u540D\u76EE\u5F55\uFF1A
|
|
5586
|
+
` + suggestions.map((s) => ` \u2192 ${s}`).join("\n") + `
|
|
5587
|
+
\u8BF7\u4F7F\u7528\u6B63\u786E\u7684\u76F8\u5BF9\u8DEF\u5F84\u91CD\u8BD5\u3002`
|
|
5588
|
+
);
|
|
5589
|
+
}
|
|
5590
|
+
throw new Error(
|
|
5591
|
+
`Directory not found: ${dirPath}
|
|
5592
|
+
\u5F53\u524D\u5DE5\u4F5C\u76EE\u5F55: ${cwd}
|
|
5593
|
+
\u8BF7\u5148\u4F7F\u7528 list_dir\uFF08\u4E0D\u4F20 path\uFF09\u67E5\u770B\u5F53\u524D\u76EE\u5F55\u7ED3\u6784\u3002`
|
|
5594
|
+
);
|
|
5595
|
+
}
|
|
5485
5596
|
const lines = [`Directory: ${dirPath}
|
|
5486
5597
|
`];
|
|
5487
5598
|
listRecursive(dirPath, "", recursive, lines);
|
|
@@ -5491,7 +5602,7 @@ var listDirTool = {
|
|
|
5491
5602
|
function listRecursive(basePath, indent, recursive, lines) {
|
|
5492
5603
|
let entries;
|
|
5493
5604
|
try {
|
|
5494
|
-
entries =
|
|
5605
|
+
entries = readdirSync5(basePath, { withFileTypes: true });
|
|
5495
5606
|
} catch {
|
|
5496
5607
|
lines.push(`${indent}(permission denied)`);
|
|
5497
5608
|
return;
|
|
@@ -5530,7 +5641,7 @@ function formatSize(bytes) {
|
|
|
5530
5641
|
}
|
|
5531
5642
|
|
|
5532
5643
|
// src/tools/builtin/grep-files.ts
|
|
5533
|
-
import { readdirSync as
|
|
5644
|
+
import { readdirSync as readdirSync6, readFileSync as readFileSync7, statSync as statSync6, existsSync as existsSync11 } from "fs";
|
|
5534
5645
|
import { join as join7, relative } from "path";
|
|
5535
5646
|
var grepFilesTool = {
|
|
5536
5647
|
definition: {
|
|
@@ -5647,7 +5758,7 @@ function collectFiles(dirPath, filePattern, results, regex, contextLines, maxRes
|
|
|
5647
5758
|
if (results.length >= maxResults) return;
|
|
5648
5759
|
let entries;
|
|
5649
5760
|
try {
|
|
5650
|
-
entries =
|
|
5761
|
+
entries = readdirSync6(dirPath, { withFileTypes: true });
|
|
5651
5762
|
} catch {
|
|
5652
5763
|
return;
|
|
5653
5764
|
}
|
|
@@ -5700,8 +5811,8 @@ function searchInFile(fullPath, displayPath, regex, contextLines, maxResults, re
|
|
|
5700
5811
|
}
|
|
5701
5812
|
|
|
5702
5813
|
// src/tools/builtin/glob-files.ts
|
|
5703
|
-
import { readdirSync as
|
|
5704
|
-
import { join as join8, relative as relative2, basename as
|
|
5814
|
+
import { readdirSync as readdirSync7, statSync as statSync7, existsSync as existsSync12 } from "fs";
|
|
5815
|
+
import { join as join8, relative as relative2, basename as basename4 } from "path";
|
|
5705
5816
|
var globFilesTool = {
|
|
5706
5817
|
definition: {
|
|
5707
5818
|
name: "glob_files",
|
|
@@ -5805,7 +5916,7 @@ function collectMatchingFiles(dirPath, rootPath, regex, results, maxResults) {
|
|
|
5805
5916
|
if (results.length >= maxResults) return;
|
|
5806
5917
|
let entries;
|
|
5807
5918
|
try {
|
|
5808
|
-
entries =
|
|
5919
|
+
entries = readdirSync7(dirPath, { withFileTypes: true });
|
|
5809
5920
|
} catch {
|
|
5810
5921
|
return;
|
|
5811
5922
|
}
|
|
@@ -5817,7 +5928,7 @@ function collectMatchingFiles(dirPath, rootPath, regex, results, maxResults) {
|
|
|
5817
5928
|
collectMatchingFiles(fullPath, rootPath, regex, results, maxResults);
|
|
5818
5929
|
} else if (entry.isFile()) {
|
|
5819
5930
|
const relPath = relative2(rootPath, fullPath).replace(/\\/g, "/");
|
|
5820
|
-
if (regex.test(relPath) || regex.test(
|
|
5931
|
+
if (regex.test(relPath) || regex.test(basename4(relPath))) {
|
|
5821
5932
|
try {
|
|
5822
5933
|
const stat = statSync7(fullPath);
|
|
5823
5934
|
results.push({ relPath, absPath: fullPath, mtime: stat.mtimeMs });
|
|
@@ -5893,7 +6004,7 @@ var runInteractiveTool = {
|
|
|
5893
6004
|
PYTHONDONTWRITEBYTECODE: "1"
|
|
5894
6005
|
};
|
|
5895
6006
|
const prefixWarnings = [argsTypeWarning, stdinTypeWarning].filter(Boolean).join("");
|
|
5896
|
-
return new Promise((
|
|
6007
|
+
return new Promise((resolve6) => {
|
|
5897
6008
|
const child = spawn(executable, cmdArgs.map(String), {
|
|
5898
6009
|
cwd: process.cwd(),
|
|
5899
6010
|
env,
|
|
@@ -5926,22 +6037,22 @@ var runInteractiveTool = {
|
|
|
5926
6037
|
setTimeout(writeNextLine, 400);
|
|
5927
6038
|
const timer = setTimeout(() => {
|
|
5928
6039
|
child.kill();
|
|
5929
|
-
|
|
6040
|
+
resolve6(`${prefixWarnings}[Timeout after ${timeout}ms]
|
|
5930
6041
|
${buildOutput(stdout, stderr)}`);
|
|
5931
6042
|
}, timeout);
|
|
5932
6043
|
child.on("close", (code) => {
|
|
5933
6044
|
clearTimeout(timer);
|
|
5934
6045
|
const output = buildOutput(stdout, stderr);
|
|
5935
6046
|
if (code !== 0 && code !== null) {
|
|
5936
|
-
|
|
6047
|
+
resolve6(`${prefixWarnings}Exit code ${code}:
|
|
5937
6048
|
${output}`);
|
|
5938
6049
|
} else {
|
|
5939
|
-
|
|
6050
|
+
resolve6(`${prefixWarnings}${output || "(no output)"}`);
|
|
5940
6051
|
}
|
|
5941
6052
|
});
|
|
5942
6053
|
child.on("error", (err) => {
|
|
5943
6054
|
clearTimeout(timer);
|
|
5944
|
-
|
|
6055
|
+
resolve6(
|
|
5945
6056
|
`${prefixWarnings}Failed to start process "${executable}": ${err.message}
|
|
5946
6057
|
Hint: On Windows, use the full path to the executable, e.g.:
|
|
5947
6058
|
C:\\Users\\Jinzd\\anaconda3\\envs\\python312\\python.exe`
|
|
@@ -6162,7 +6273,7 @@ var webFetchTool = {
|
|
|
6162
6273
|
|
|
6163
6274
|
// src/tools/builtin/save-last-response.ts
|
|
6164
6275
|
import { writeFileSync as writeFileSync7, mkdirSync as mkdirSync6 } from "fs";
|
|
6165
|
-
import { dirname as
|
|
6276
|
+
import { dirname as dirname5 } from "path";
|
|
6166
6277
|
var lastResponseStore = { content: "" };
|
|
6167
6278
|
var saveLastResponseTool = {
|
|
6168
6279
|
definition: {
|
|
@@ -6195,7 +6306,7 @@ var saveLastResponseTool = {
|
|
|
6195
6306
|
throw new Error("\u6CA1\u6709\u53EF\u4FDD\u5B58\u7684\u5185\u5BB9\uFF1AAI \u5C1A\u672A\u4EA7\u751F\u4EFB\u4F55\u56DE\u590D\uFF0C\u6216\u4E0A\u6B21\u56DE\u590D\u4E3A\u7A7A\u3002");
|
|
6196
6307
|
}
|
|
6197
6308
|
undoStack.push(filePath, `save_last_response: ${filePath}`);
|
|
6198
|
-
mkdirSync6(
|
|
6309
|
+
mkdirSync6(dirname5(filePath), { recursive: true });
|
|
6199
6310
|
writeFileSync7(filePath, content, "utf-8");
|
|
6200
6311
|
const lines = content.split("\n").length;
|
|
6201
6312
|
return `File saved: ${filePath} (${lines} lines, ${content.length} bytes)`;
|
|
@@ -6288,7 +6399,7 @@ function promptUser(rl, question) {
|
|
|
6288
6399
|
console.log();
|
|
6289
6400
|
console.log(chalk6.cyan("\u2753 ") + chalk6.bold(question));
|
|
6290
6401
|
process.stdout.write(chalk6.cyan("> "));
|
|
6291
|
-
return new Promise((
|
|
6402
|
+
return new Promise((resolve6) => {
|
|
6292
6403
|
let completed = false;
|
|
6293
6404
|
const cleanup = (answer) => {
|
|
6294
6405
|
if (completed) return;
|
|
@@ -6298,7 +6409,7 @@ function promptUser(rl, question) {
|
|
|
6298
6409
|
rl.pause();
|
|
6299
6410
|
rlAny.output = savedOutput;
|
|
6300
6411
|
askUserContext.prompting = false;
|
|
6301
|
-
|
|
6412
|
+
resolve6(answer);
|
|
6302
6413
|
};
|
|
6303
6414
|
const onLine = (line) => {
|
|
6304
6415
|
cleanup(line);
|
|
@@ -6790,7 +6901,7 @@ var spawnAgentTool = {
|
|
|
6790
6901
|
|
|
6791
6902
|
// src/tools/registry.ts
|
|
6792
6903
|
import { pathToFileURL } from "url";
|
|
6793
|
-
import { existsSync as existsSync14, mkdirSync as mkdirSync8, readdirSync as
|
|
6904
|
+
import { existsSync as existsSync14, mkdirSync as mkdirSync8, readdirSync as readdirSync8 } from "fs";
|
|
6794
6905
|
import { join as join10 } from "path";
|
|
6795
6906
|
var ToolRegistry = class {
|
|
6796
6907
|
tools = /* @__PURE__ */ new Map();
|
|
@@ -6873,7 +6984,7 @@ var ToolRegistry = class {
|
|
|
6873
6984
|
}
|
|
6874
6985
|
let files;
|
|
6875
6986
|
try {
|
|
6876
|
-
files =
|
|
6987
|
+
files = readdirSync8(pluginsDir).filter((f) => f.endsWith(".js"));
|
|
6877
6988
|
} catch {
|
|
6878
6989
|
return 0;
|
|
6879
6990
|
}
|
|
@@ -6927,7 +7038,7 @@ import chalk9 from "chalk";
|
|
|
6927
7038
|
import { existsSync as existsSync15, readFileSync as readFileSync9 } from "fs";
|
|
6928
7039
|
|
|
6929
7040
|
// src/tools/hooks.ts
|
|
6930
|
-
import { execSync as
|
|
7041
|
+
import { execSync as execSync6 } from "child_process";
|
|
6931
7042
|
function runHook(template, vars) {
|
|
6932
7043
|
if (!template) return;
|
|
6933
7044
|
let cmd = template;
|
|
@@ -6936,7 +7047,7 @@ function runHook(template, vars) {
|
|
|
6936
7047
|
cmd = cmd.replace(/\{args\}/g, vars.args ?? "");
|
|
6937
7048
|
cmd = cmd.replace(/\{status\}/g, vars.status ?? "");
|
|
6938
7049
|
try {
|
|
6939
|
-
|
|
7050
|
+
execSync6(cmd, {
|
|
6940
7051
|
timeout: 5e3,
|
|
6941
7052
|
stdio: ["pipe", "pipe", "pipe"],
|
|
6942
7053
|
encoding: "utf-8"
|
|
@@ -7197,7 +7308,7 @@ var ToolExecutor = class {
|
|
|
7197
7308
|
rl.resume();
|
|
7198
7309
|
process.stdout.write(prompt);
|
|
7199
7310
|
this.confirming = true;
|
|
7200
|
-
return new Promise((
|
|
7311
|
+
return new Promise((resolve6) => {
|
|
7201
7312
|
let completed = false;
|
|
7202
7313
|
const cleanup = (result) => {
|
|
7203
7314
|
if (completed) return;
|
|
@@ -7207,7 +7318,7 @@ var ToolExecutor = class {
|
|
|
7207
7318
|
rl.pause();
|
|
7208
7319
|
rlAny.output = savedOutput;
|
|
7209
7320
|
this.confirming = false;
|
|
7210
|
-
|
|
7321
|
+
resolve6(result);
|
|
7211
7322
|
};
|
|
7212
7323
|
const onLine = (line) => {
|
|
7213
7324
|
const input2 = line.trim().toLowerCase();
|
|
@@ -7368,7 +7479,7 @@ var ToolExecutor = class {
|
|
|
7368
7479
|
rl.resume();
|
|
7369
7480
|
process.stdout.write(color("Proceed? [y/N] (type y + Enter to confirm) "));
|
|
7370
7481
|
this.confirming = true;
|
|
7371
|
-
return new Promise((
|
|
7482
|
+
return new Promise((resolve6) => {
|
|
7372
7483
|
let completed = false;
|
|
7373
7484
|
const cleanup = (answer) => {
|
|
7374
7485
|
if (completed) return;
|
|
@@ -7378,7 +7489,7 @@ var ToolExecutor = class {
|
|
|
7378
7489
|
rl.pause();
|
|
7379
7490
|
rlAny.output = savedOutput;
|
|
7380
7491
|
this.confirming = false;
|
|
7381
|
-
|
|
7492
|
+
resolve6(answer === "y");
|
|
7382
7493
|
};
|
|
7383
7494
|
const onLine = (line) => {
|
|
7384
7495
|
cleanup(line.trim().toLowerCase());
|
|
@@ -7633,9 +7744,9 @@ Managing ${displayName} API Key`);
|
|
|
7633
7744
|
};
|
|
7634
7745
|
|
|
7635
7746
|
// src/repl/custom-commands.ts
|
|
7636
|
-
import { existsSync as existsSync16, readFileSync as readFileSync10, readdirSync as
|
|
7747
|
+
import { existsSync as existsSync16, readFileSync as readFileSync10, readdirSync as readdirSync9, mkdirSync as mkdirSync9 } from "fs";
|
|
7637
7748
|
import { join as join11, extname as extname3 } from "path";
|
|
7638
|
-
import { execSync as
|
|
7749
|
+
import { execSync as execSync7 } from "child_process";
|
|
7639
7750
|
function parseSimpleYaml(text) {
|
|
7640
7751
|
const result = {};
|
|
7641
7752
|
for (const line of text.split("\n")) {
|
|
@@ -7680,7 +7791,7 @@ function expandTemplate(template, args) {
|
|
|
7680
7791
|
});
|
|
7681
7792
|
result = result.replace(/\{\{git-diff\}\}/g, () => {
|
|
7682
7793
|
try {
|
|
7683
|
-
return
|
|
7794
|
+
return execSync7("git diff", { encoding: "utf-8", timeout: 1e4 }).trim();
|
|
7684
7795
|
} catch {
|
|
7685
7796
|
return "[No git diff available]";
|
|
7686
7797
|
}
|
|
@@ -7703,7 +7814,7 @@ var CustomCommandManager = class {
|
|
|
7703
7814
|
return 0;
|
|
7704
7815
|
}
|
|
7705
7816
|
let count = 0;
|
|
7706
|
-
for (const file of
|
|
7817
|
+
for (const file of readdirSync9(this.commandsDir)) {
|
|
7707
7818
|
if (extname3(file) !== ".md") continue;
|
|
7708
7819
|
const cmd = parseCommandFile(join11(this.commandsDir, file));
|
|
7709
7820
|
if (cmd) {
|
|
@@ -7930,7 +8041,7 @@ var McpClient = class {
|
|
|
7930
8041
|
// 内部方法:JSON-RPC 通信
|
|
7931
8042
|
// ══════════════════════════════════════════════════════════════════
|
|
7932
8043
|
sendRequest(method, params) {
|
|
7933
|
-
return new Promise((
|
|
8044
|
+
return new Promise((resolve6, reject) => {
|
|
7934
8045
|
if (!this.process?.stdin?.writable) {
|
|
7935
8046
|
return reject(new Error(`MCP server [${this.serverId}] stdin not writable`));
|
|
7936
8047
|
}
|
|
@@ -7953,7 +8064,7 @@ var McpClient = class {
|
|
|
7953
8064
|
this.pendingRequests.set(id, {
|
|
7954
8065
|
resolve: (result) => {
|
|
7955
8066
|
cleanup();
|
|
7956
|
-
|
|
8067
|
+
resolve6(result);
|
|
7957
8068
|
},
|
|
7958
8069
|
reject: (error) => {
|
|
7959
8070
|
cleanup();
|
|
@@ -8024,13 +8135,13 @@ var McpClient = class {
|
|
|
8024
8135
|
}
|
|
8025
8136
|
/** Promise 超时包装 */
|
|
8026
8137
|
withTimeout(promise, ms, label) {
|
|
8027
|
-
return new Promise((
|
|
8138
|
+
return new Promise((resolve6, reject) => {
|
|
8028
8139
|
const timer = setTimeout(() => {
|
|
8029
8140
|
reject(new Error(`MCP [${this.serverId}] ${label} timed out after ${ms}ms`));
|
|
8030
8141
|
}, ms);
|
|
8031
8142
|
promise.then((val) => {
|
|
8032
8143
|
clearTimeout(timer);
|
|
8033
|
-
|
|
8144
|
+
resolve6(val);
|
|
8034
8145
|
}).catch((err) => {
|
|
8035
8146
|
clearTimeout(timer);
|
|
8036
8147
|
reject(err);
|
|
@@ -8295,12 +8406,12 @@ var McpManager = class {
|
|
|
8295
8406
|
};
|
|
8296
8407
|
|
|
8297
8408
|
// src/skills/manager.ts
|
|
8298
|
-
import { existsSync as existsSync18, readdirSync as
|
|
8409
|
+
import { existsSync as existsSync18, readdirSync as readdirSync10, mkdirSync as mkdirSync11 } from "fs";
|
|
8299
8410
|
import { join as join13 } from "path";
|
|
8300
8411
|
|
|
8301
8412
|
// src/skills/types.ts
|
|
8302
8413
|
import { readFileSync as readFileSync12 } from "fs";
|
|
8303
|
-
import { basename as
|
|
8414
|
+
import { basename as basename5 } from "path";
|
|
8304
8415
|
function parseSimpleYaml2(yaml) {
|
|
8305
8416
|
const result = {};
|
|
8306
8417
|
for (const line of yaml.split("\n")) {
|
|
@@ -8329,7 +8440,7 @@ function parseSkillFile(filePath) {
|
|
|
8329
8440
|
if (!frontmatterMatch) {
|
|
8330
8441
|
return {
|
|
8331
8442
|
meta: {
|
|
8332
|
-
name:
|
|
8443
|
+
name: basename5(filePath, ".md"),
|
|
8333
8444
|
description: ""
|
|
8334
8445
|
},
|
|
8335
8446
|
content: raw.trim(),
|
|
@@ -8340,7 +8451,7 @@ function parseSkillFile(filePath) {
|
|
|
8340
8451
|
const parsed = parseSimpleYaml2(yaml);
|
|
8341
8452
|
return {
|
|
8342
8453
|
meta: {
|
|
8343
|
-
name: parsed["name"] ??
|
|
8454
|
+
name: parsed["name"] ?? basename5(filePath, ".md"),
|
|
8344
8455
|
description: parsed["description"] ?? "",
|
|
8345
8456
|
tools: parsed["tools"] ? parseYamlArray(parsed["tools"]) : void 0
|
|
8346
8457
|
},
|
|
@@ -8370,7 +8481,7 @@ var SkillManager = class {
|
|
|
8370
8481
|
}
|
|
8371
8482
|
let entries;
|
|
8372
8483
|
try {
|
|
8373
|
-
entries =
|
|
8484
|
+
entries = readdirSync10(this.skillsDir);
|
|
8374
8485
|
} catch {
|
|
8375
8486
|
return 0;
|
|
8376
8487
|
}
|
|
@@ -8481,7 +8592,7 @@ function parseAtReferences(input2, cwd) {
|
|
|
8481
8592
|
let match;
|
|
8482
8593
|
while ((match = atPattern.exec(input2)) !== null) {
|
|
8483
8594
|
const rawPath = match[1] ?? match[2] ?? match[3] ?? "";
|
|
8484
|
-
const absPath =
|
|
8595
|
+
const absPath = resolve5(cwd, rawPath);
|
|
8485
8596
|
const ext = extname4(rawPath).toLowerCase();
|
|
8486
8597
|
const mime = IMAGE_MIME[ext];
|
|
8487
8598
|
if (!existsSync19(absPath)) {
|
|
@@ -8687,7 +8798,7 @@ var Repl = class {
|
|
|
8687
8798
|
if (depth > 2 || entryCount >= MAX_TREE_ENTRIES) return;
|
|
8688
8799
|
let entries;
|
|
8689
8800
|
try {
|
|
8690
|
-
entries =
|
|
8801
|
+
entries = readdirSync11(dir);
|
|
8691
8802
|
} catch {
|
|
8692
8803
|
return;
|
|
8693
8804
|
}
|
|
@@ -8721,7 +8832,7 @@ ${treeLines.join("\n")}`
|
|
|
8721
8832
|
if (totalChars >= MAX_TOTAL_CHARS) return;
|
|
8722
8833
|
let entries;
|
|
8723
8834
|
try {
|
|
8724
|
-
entries =
|
|
8835
|
+
entries = readdirSync11(dir);
|
|
8725
8836
|
} catch {
|
|
8726
8837
|
return;
|
|
8727
8838
|
}
|
|
@@ -8772,7 +8883,7 @@ ${content}
|
|
|
8772
8883
|
* 已添加时返回 added=false(无重复添加)。
|
|
8773
8884
|
*/
|
|
8774
8885
|
addExtraContextDir(dirPath) {
|
|
8775
|
-
const absPath =
|
|
8886
|
+
const absPath = resolve5(dirPath);
|
|
8776
8887
|
if (!existsSync19(absPath)) {
|
|
8777
8888
|
return { success: false, charCount: 0, added: false, error: `Directory not found: ${dirPath}` };
|
|
8778
8889
|
}
|
|
@@ -8795,7 +8906,7 @@ ${content}
|
|
|
8795
8906
|
}
|
|
8796
8907
|
/** 从额外上下文中移除目录。返回 true 表示成功移除,false 表示未找到。 */
|
|
8797
8908
|
removeExtraContextDir(dirPath) {
|
|
8798
|
-
const absPath =
|
|
8909
|
+
const absPath = resolve5(dirPath);
|
|
8799
8910
|
const idx = this.extraContextDirs.findIndex((d) => d.dir === absPath);
|
|
8800
8911
|
if (idx === -1) return false;
|
|
8801
8912
|
this.extraContextDirs.splice(idx, 1);
|
|
@@ -8878,8 +8989,8 @@ ${content}
|
|
|
8878
8989
|
if (setting === false) return { layers: [], mergedContent: "" };
|
|
8879
8990
|
const cwd = process.cwd();
|
|
8880
8991
|
if (setting !== "auto") {
|
|
8881
|
-
const fullPath =
|
|
8882
|
-
if (!fullPath.startsWith(
|
|
8992
|
+
const fullPath = resolve5(cwd, String(setting));
|
|
8993
|
+
if (!fullPath.startsWith(resolve5(cwd))) {
|
|
8883
8994
|
process.stderr.write(
|
|
8884
8995
|
`[Warning] contextFile path "${setting}" is outside current directory, ignoring.
|
|
8885
8996
|
`
|
|
@@ -8922,8 +9033,8 @@ ${content}
|
|
|
8922
9033
|
charCount: projectCtx.content.length
|
|
8923
9034
|
});
|
|
8924
9035
|
}
|
|
8925
|
-
const normalizedCwd =
|
|
8926
|
-
const normalizedRoot =
|
|
9036
|
+
const normalizedCwd = resolve5(cwd);
|
|
9037
|
+
const normalizedRoot = resolve5(projectRoot);
|
|
8927
9038
|
if (normalizedCwd !== normalizedRoot) {
|
|
8928
9039
|
const localCtx = this.findContextFile(cwd);
|
|
8929
9040
|
if (localCtx) {
|
|
@@ -9329,7 +9440,7 @@ Session '${this.resumeSessionId}' not found.
|
|
|
9329
9440
|
this.handleExit();
|
|
9330
9441
|
});
|
|
9331
9442
|
this.showPrompt();
|
|
9332
|
-
await new Promise((
|
|
9443
|
+
await new Promise((resolve6) => {
|
|
9333
9444
|
let processing = false;
|
|
9334
9445
|
this.rl.on("line", async (line) => {
|
|
9335
9446
|
if (this.toolExecutor.confirming) return;
|
|
@@ -9364,13 +9475,13 @@ Session '${this.resumeSessionId}' not found.
|
|
|
9364
9475
|
process.stdin.resume();
|
|
9365
9476
|
this.showPrompt();
|
|
9366
9477
|
} else {
|
|
9367
|
-
|
|
9478
|
+
resolve6();
|
|
9368
9479
|
}
|
|
9369
9480
|
}
|
|
9370
9481
|
});
|
|
9371
9482
|
this.rl.on("close", () => {
|
|
9372
9483
|
if (!processing) {
|
|
9373
|
-
|
|
9484
|
+
resolve6();
|
|
9374
9485
|
}
|
|
9375
9486
|
});
|
|
9376
9487
|
});
|
|
@@ -9645,11 +9756,11 @@ Session '${this.resumeSessionId}' not found.
|
|
|
9645
9756
|
completeFilePath(partial) {
|
|
9646
9757
|
try {
|
|
9647
9758
|
const normalized = partial.replace(/\\/g, "/");
|
|
9648
|
-
const dir = normalized.includes("/") ?
|
|
9649
|
-
const prefix = normalized.includes("/") ?
|
|
9650
|
-
const absDir =
|
|
9759
|
+
const dir = normalized.includes("/") ? dirname6(normalized) : ".";
|
|
9760
|
+
const prefix = normalized.includes("/") ? basename6(normalized) : normalized;
|
|
9761
|
+
const absDir = resolve5(process.cwd(), dir);
|
|
9651
9762
|
if (!existsSync19(absDir)) return [];
|
|
9652
|
-
const entries =
|
|
9763
|
+
const entries = readdirSync11(absDir);
|
|
9653
9764
|
const results = [];
|
|
9654
9765
|
for (const entry of entries) {
|
|
9655
9766
|
if (entry.startsWith(".")) continue;
|
|
@@ -10315,10 +10426,10 @@ async function setupProxy(configProxy) {
|
|
|
10315
10426
|
}
|
|
10316
10427
|
async function readStdin() {
|
|
10317
10428
|
if (process.stdin.isTTY) return "";
|
|
10318
|
-
return new Promise((
|
|
10429
|
+
return new Promise((resolve6, reject) => {
|
|
10319
10430
|
const chunks = [];
|
|
10320
10431
|
process.stdin.on("data", (chunk) => chunks.push(chunk));
|
|
10321
|
-
process.stdin.on("end", () =>
|
|
10432
|
+
process.stdin.on("end", () => resolve6(Buffer.concat(chunks).toString("utf-8").trimEnd()));
|
|
10322
10433
|
process.stdin.on("error", reject);
|
|
10323
10434
|
});
|
|
10324
10435
|
}
|