jinzd-ai-cli 0.1.22 → 0.1.23
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/dist/index.js +835 -181
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -129,12 +129,13 @@ var EnvLoader = class {
|
|
|
129
129
|
};
|
|
130
130
|
|
|
131
131
|
// src/core/constants.ts
|
|
132
|
-
var VERSION = "0.1.
|
|
132
|
+
var VERSION = "0.1.23";
|
|
133
133
|
var APP_NAME = "ai-cli";
|
|
134
134
|
var CONFIG_DIR_NAME = ".aicli";
|
|
135
135
|
var CONFIG_FILE_NAME = "config.json";
|
|
136
136
|
var HISTORY_DIR_NAME = "history";
|
|
137
137
|
var PLUGINS_DIR_NAME = "plugins";
|
|
138
|
+
var SKILLS_DIR_NAME = "skills";
|
|
138
139
|
var CONTEXT_FILE_CANDIDATES = ["AICLI.md", "CLAUDE.md"];
|
|
139
140
|
var MEMORY_FILE_NAME = "memory.md";
|
|
140
141
|
var MEMORY_MAX_CHARS = 1e4;
|
|
@@ -1563,8 +1564,8 @@ var SessionManager = class {
|
|
|
1563
1564
|
|
|
1564
1565
|
// src/repl/repl.ts
|
|
1565
1566
|
import * as readline from "readline";
|
|
1566
|
-
import { existsSync as
|
|
1567
|
-
import { join as
|
|
1567
|
+
import { existsSync as existsSync17, readFileSync as readFileSync12 } from "fs";
|
|
1568
|
+
import { join as join12, resolve as resolve4, extname as extname3 } from "path";
|
|
1568
1569
|
import chalk10 from "chalk";
|
|
1569
1570
|
|
|
1570
1571
|
// src/repl/renderer.ts
|
|
@@ -1668,10 +1669,10 @@ var Renderer = class {
|
|
|
1668
1669
|
console.log(tool("write_todos", "\u62C6\u89E3\u4EFB\u52A1\u4E3A\u5B50\u4EFB\u52A1\u5217\u8868\uFF0C\u5B9E\u65F6\u663E\u793A\u8FDB\u5EA6"));
|
|
1669
1670
|
console.log(tool("spawn_agent", "\u59D4\u6D3E\u72EC\u7ACB\u5B50\u4EE3\u7406\u6267\u884C\u7279\u5B9A\u4EFB\u52A1\uFF08\u9694\u79BB\u5BF9\u8BDD + \u81EA\u52A8\u5DE5\u5177\u8C03\u7528\u5FAA\u73AF\uFF09"));
|
|
1670
1671
|
console.log(HR);
|
|
1671
|
-
console.log(chalk.gray(" REPL \u547D\u4EE4\
|
|
1672
|
+
console.log(chalk.gray(" REPL \u547D\u4EE4\uFF0823\u4E2A\uFF09\uFF1A"));
|
|
1672
1673
|
console.log(chalk.dim(" /help /about /provider /model /clear /compact /plan /session"));
|
|
1673
|
-
console.log(chalk.dim(" /system /context /status /search /undo /export"));
|
|
1674
|
-
console.log(chalk.dim(" /tools /plugins /mcp /config /exit"));
|
|
1674
|
+
console.log(chalk.dim(" /system /context /status /search /undo /export /copy /cost"));
|
|
1675
|
+
console.log(chalk.dim(" /init /skill /tools /plugins /mcp /config /exit"));
|
|
1675
1676
|
console.log(HR);
|
|
1676
1677
|
console.log(chalk.gray(" \u4E3B\u8981\u7279\u6027\uFF1A"));
|
|
1677
1678
|
console.log(feat("Agentic \u5FAA\u73AF\uFF08\u6700\u591A 20 \u8F6E\u5DE5\u5177\u8C03\u7528\uFF0C\u6700\u7EC8\u56DE\u7B54\u6D41\u5F0F\u8F93\u51FA\uFF09"));
|
|
@@ -1689,6 +1690,9 @@ var Renderer = class {
|
|
|
1689
1690
|
console.log(feat("/compact \u4E0A\u4E0B\u6587\u538B\u7F29\uFF1A\u751F\u6210\u6458\u8981\u66FF\u6362\u65E7\u6D88\u606F\uFF0C\u4FDD\u7559\u6700\u8FD1 4 \u6761\uFF0C\u89E3\u51B3 context \u6EA2\u51FA"));
|
|
1690
1691
|
console.log(feat("Kimi \u5DE5\u5177\u8C03\u7528\u53EF\u9760\u6027\u589E\u5F3A\uFF1AXML \u4F2A\u8C03\u7528\u81EA\u52A8\u68C0\u6D4B\u5E76\u8F6C\u6362\u4E3A\u771F\u5B9E API \u8C03\u7528\uFF08v0.1.19\uFF09"));
|
|
1691
1692
|
console.log(feat("Sub-Agent \u5B50\u4EE3\u7406\uFF1Aspawn_agent \u59D4\u6D3E\u72EC\u7ACB\u5B50\u4EE3\u7406\uFF0C\u9694\u79BB\u5FAA\u73AF\u6267\u884C\u590D\u6742\u5B50\u4EFB\u52A1"));
|
|
1693
|
+
console.log(feat("Agent Skills\uFF1A~/.aicli/skills/ \u53EF\u590D\u7528\u6280\u80FD\u5305\uFF0C\u6CE8\u5165 system prompt + \u5DE5\u5177\u767D\u540D\u5355"));
|
|
1694
|
+
console.log(feat("/init \u9879\u76EE\u521D\u59CB\u5316\uFF1A\u626B\u63CF\u9879\u76EE\u7ED3\u6784\uFF0CAI \u751F\u6210 AICLI.md \u4E0A\u4E0B\u6587\u6587\u4EF6"));
|
|
1695
|
+
console.log(feat("\u591A\u6587\u4EF6\u7F16\u8F91\u9884\u89C8\uFF1A\u6279\u91CF diff preview + \u9009\u62E9\u6027 approve/reject"));
|
|
1692
1696
|
console.log(feat("\u72EC\u7ACB\u53EF\u6267\u884C\u6587\u4EF6\u6253\u5305\uFF08~56MB\uFF0C\u65E0\u9700 Node.js \u73AF\u5883\uFF09"));
|
|
1693
1697
|
console.log();
|
|
1694
1698
|
}
|
|
@@ -1831,12 +1835,104 @@ Error: ${message}
|
|
|
1831
1835
|
};
|
|
1832
1836
|
|
|
1833
1837
|
// src/repl/commands/index.ts
|
|
1834
|
-
import { writeFileSync as writeFileSync4, mkdirSync as mkdirSync4 } from "fs";
|
|
1835
|
-
import {
|
|
1838
|
+
import { writeFileSync as writeFileSync4, mkdirSync as mkdirSync4, existsSync as existsSync5, readFileSync as readFileSync4, readdirSync as readdirSync2, statSync } from "fs";
|
|
1839
|
+
import { execSync as execSync2 } from "child_process";
|
|
1840
|
+
import { platform } from "os";
|
|
1841
|
+
import { resolve, dirname as dirname2, join as join4 } from "path";
|
|
1836
1842
|
import chalk2 from "chalk";
|
|
1837
1843
|
|
|
1844
|
+
// src/tools/git-context.ts
|
|
1845
|
+
import { execSync } from "child_process";
|
|
1846
|
+
import { existsSync as existsSync3 } from "fs";
|
|
1847
|
+
import { join as join3 } from "path";
|
|
1848
|
+
function runGit(cmd, cwd) {
|
|
1849
|
+
try {
|
|
1850
|
+
return execSync(`git ${cmd}`, {
|
|
1851
|
+
cwd,
|
|
1852
|
+
encoding: "utf-8",
|
|
1853
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
1854
|
+
timeout: 5e3
|
|
1855
|
+
}).trim();
|
|
1856
|
+
} catch {
|
|
1857
|
+
return null;
|
|
1858
|
+
}
|
|
1859
|
+
}
|
|
1860
|
+
function getGitRoot(cwd = process.cwd()) {
|
|
1861
|
+
return runGit("rev-parse --show-toplevel", cwd);
|
|
1862
|
+
}
|
|
1863
|
+
function getGitContext(cwd = process.cwd()) {
|
|
1864
|
+
if (!existsSync3(join3(cwd, ".git"))) {
|
|
1865
|
+
const result = runGit("rev-parse --git-dir", cwd);
|
|
1866
|
+
if (!result) return null;
|
|
1867
|
+
}
|
|
1868
|
+
const branch = runGit("rev-parse --abbrev-ref HEAD", cwd);
|
|
1869
|
+
if (!branch) return null;
|
|
1870
|
+
const statusOutput = runGit("status --porcelain", cwd) ?? "";
|
|
1871
|
+
const statusLines = statusOutput ? statusOutput.split("\n").filter(Boolean) : [];
|
|
1872
|
+
const stagedFiles = [];
|
|
1873
|
+
const changedFiles = [];
|
|
1874
|
+
for (const line of statusLines) {
|
|
1875
|
+
const xy = line.slice(0, 2);
|
|
1876
|
+
const file = line.slice(3).trim();
|
|
1877
|
+
const indexStatus = xy[0];
|
|
1878
|
+
const workStatus = xy[1];
|
|
1879
|
+
if (indexStatus && indexStatus !== " " && indexStatus !== "?") {
|
|
1880
|
+
stagedFiles.push(`${indexStatus} ${file}`);
|
|
1881
|
+
}
|
|
1882
|
+
if (workStatus && workStatus !== " ") {
|
|
1883
|
+
changedFiles.push(`${workStatus} ${file}`);
|
|
1884
|
+
}
|
|
1885
|
+
}
|
|
1886
|
+
const logOutput = runGit("log --oneline -3", cwd) ?? "";
|
|
1887
|
+
const recentCommits = logOutput ? logOutput.split("\n").filter(Boolean) : [];
|
|
1888
|
+
const unpushedOutput = runGit("log @{u}..HEAD --oneline 2>/dev/null", cwd);
|
|
1889
|
+
const hasUnpushed = unpushedOutput !== null && unpushedOutput.trim().length > 0;
|
|
1890
|
+
return {
|
|
1891
|
+
branch,
|
|
1892
|
+
changedFiles,
|
|
1893
|
+
stagedFiles,
|
|
1894
|
+
recentCommits,
|
|
1895
|
+
hasUnpushed
|
|
1896
|
+
};
|
|
1897
|
+
}
|
|
1898
|
+
function formatGitContextForPrompt(ctx) {
|
|
1899
|
+
const lines = ["# Git Repository Status", ""];
|
|
1900
|
+
lines.push(`- **Branch**: \`${ctx.branch}\``);
|
|
1901
|
+
if (ctx.stagedFiles.length > 0) {
|
|
1902
|
+
lines.push(`- **Staged** (${ctx.stagedFiles.length} files):`);
|
|
1903
|
+
for (const f of ctx.stagedFiles.slice(0, 10)) {
|
|
1904
|
+
lines.push(` - ${f}`);
|
|
1905
|
+
}
|
|
1906
|
+
if (ctx.stagedFiles.length > 10) {
|
|
1907
|
+
lines.push(` - ... and ${ctx.stagedFiles.length - 10} more`);
|
|
1908
|
+
}
|
|
1909
|
+
}
|
|
1910
|
+
if (ctx.changedFiles.length > 0) {
|
|
1911
|
+
lines.push(`- **Modified** (${ctx.changedFiles.length} files):`);
|
|
1912
|
+
for (const f of ctx.changedFiles.slice(0, 10)) {
|
|
1913
|
+
lines.push(` - ${f}`);
|
|
1914
|
+
}
|
|
1915
|
+
if (ctx.changedFiles.length > 10) {
|
|
1916
|
+
lines.push(` - ... and ${ctx.changedFiles.length - 10} more`);
|
|
1917
|
+
}
|
|
1918
|
+
}
|
|
1919
|
+
if (ctx.stagedFiles.length === 0 && ctx.changedFiles.length === 0) {
|
|
1920
|
+
lines.push("- **Working tree**: clean");
|
|
1921
|
+
}
|
|
1922
|
+
if (ctx.recentCommits.length > 0) {
|
|
1923
|
+
lines.push("- **Recent commits**:");
|
|
1924
|
+
for (const c of ctx.recentCommits) {
|
|
1925
|
+
lines.push(` - ${c}`);
|
|
1926
|
+
}
|
|
1927
|
+
}
|
|
1928
|
+
if (ctx.hasUnpushed) {
|
|
1929
|
+
lines.push("- \u26A0\uFE0F Has unpushed commits");
|
|
1930
|
+
}
|
|
1931
|
+
return lines.join("\n");
|
|
1932
|
+
}
|
|
1933
|
+
|
|
1838
1934
|
// src/tools/undo-stack.ts
|
|
1839
|
-
import { readFileSync as readFileSync3, writeFileSync as writeFileSync3, unlinkSync, existsSync as
|
|
1935
|
+
import { readFileSync as readFileSync3, writeFileSync as writeFileSync3, unlinkSync, existsSync as existsSync4 } from "fs";
|
|
1840
1936
|
var MAX_UNDO_DEPTH = 20;
|
|
1841
1937
|
var UndoStack = class {
|
|
1842
1938
|
stack = [];
|
|
@@ -1847,7 +1943,7 @@ var UndoStack = class {
|
|
|
1847
1943
|
*/
|
|
1848
1944
|
push(filePath, description) {
|
|
1849
1945
|
let previousContent = null;
|
|
1850
|
-
if (
|
|
1946
|
+
if (existsSync4(filePath)) {
|
|
1851
1947
|
try {
|
|
1852
1948
|
previousContent = readFileSync3(filePath, "utf-8");
|
|
1853
1949
|
} catch {
|
|
@@ -1873,7 +1969,7 @@ var UndoStack = class {
|
|
|
1873
1969
|
if (!entry) return null;
|
|
1874
1970
|
try {
|
|
1875
1971
|
if (entry.previousContent === null) {
|
|
1876
|
-
if (
|
|
1972
|
+
if (existsSync4(entry.filePath)) {
|
|
1877
1973
|
unlinkSync(entry.filePath);
|
|
1878
1974
|
}
|
|
1879
1975
|
return { entry, result: `Deleted newly created file: ${entry.filePath}` };
|
|
@@ -1911,6 +2007,200 @@ function fmtCtx(tokens) {
|
|
|
1911
2007
|
if (tokens >= 1e3) return `${Math.round(tokens / 1024)}K`;
|
|
1912
2008
|
return `${tokens}`;
|
|
1913
2009
|
}
|
|
2010
|
+
var SCAN_SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
2011
|
+
"node_modules",
|
|
2012
|
+
".git",
|
|
2013
|
+
"dist",
|
|
2014
|
+
"build",
|
|
2015
|
+
"out",
|
|
2016
|
+
"target",
|
|
2017
|
+
".next",
|
|
2018
|
+
".nuxt",
|
|
2019
|
+
"__pycache__",
|
|
2020
|
+
".venv",
|
|
2021
|
+
"venv",
|
|
2022
|
+
".tox",
|
|
2023
|
+
".mypy_cache",
|
|
2024
|
+
".pytest_cache",
|
|
2025
|
+
".gradle",
|
|
2026
|
+
".idea",
|
|
2027
|
+
".vscode",
|
|
2028
|
+
".vs",
|
|
2029
|
+
"coverage",
|
|
2030
|
+
".cache",
|
|
2031
|
+
".parcel-cache",
|
|
2032
|
+
"dist-cjs",
|
|
2033
|
+
"release",
|
|
2034
|
+
".output",
|
|
2035
|
+
".turbo",
|
|
2036
|
+
"vendor"
|
|
2037
|
+
]);
|
|
2038
|
+
function scanDirTree(dir, maxDepth = 2, maxEntries = 80) {
|
|
2039
|
+
const lines = [];
|
|
2040
|
+
let count = 0;
|
|
2041
|
+
const walk = (d, prefix, depth) => {
|
|
2042
|
+
if (depth > maxDepth || count >= maxEntries) return;
|
|
2043
|
+
let entries;
|
|
2044
|
+
try {
|
|
2045
|
+
entries = readdirSync2(d);
|
|
2046
|
+
} catch {
|
|
2047
|
+
return;
|
|
2048
|
+
}
|
|
2049
|
+
const filtered = entries.filter((e) => !e.startsWith(".") && !SCAN_SKIP_DIRS.has(e));
|
|
2050
|
+
const sorted = filtered.sort((a, b) => {
|
|
2051
|
+
const aIsDir = statSync(join4(d, a)).isDirectory();
|
|
2052
|
+
const bIsDir = statSync(join4(d, b)).isDirectory();
|
|
2053
|
+
if (aIsDir !== bIsDir) return aIsDir ? -1 : 1;
|
|
2054
|
+
return a.localeCompare(b);
|
|
2055
|
+
});
|
|
2056
|
+
for (let i = 0; i < sorted.length && count < maxEntries; i++) {
|
|
2057
|
+
const name = sorted[i];
|
|
2058
|
+
const fullPath = join4(d, name);
|
|
2059
|
+
const isLast = i === sorted.length - 1;
|
|
2060
|
+
const connector = isLast ? "\u2514\u2500\u2500 " : "\u251C\u2500\u2500 ";
|
|
2061
|
+
let isDir;
|
|
2062
|
+
try {
|
|
2063
|
+
isDir = statSync(fullPath).isDirectory();
|
|
2064
|
+
} catch {
|
|
2065
|
+
continue;
|
|
2066
|
+
}
|
|
2067
|
+
lines.push(prefix + connector + name + (isDir ? "/" : ""));
|
|
2068
|
+
count++;
|
|
2069
|
+
if (isDir) {
|
|
2070
|
+
walk(fullPath, prefix + (isLast ? " " : "\u2502 "), depth + 1);
|
|
2071
|
+
}
|
|
2072
|
+
}
|
|
2073
|
+
};
|
|
2074
|
+
walk(dir, "", 0);
|
|
2075
|
+
if (count >= maxEntries) lines.push("... (truncated)");
|
|
2076
|
+
return lines.join("\n");
|
|
2077
|
+
}
|
|
2078
|
+
function scanProject(cwd) {
|
|
2079
|
+
const info = {
|
|
2080
|
+
type: "unknown",
|
|
2081
|
+
language: "unknown",
|
|
2082
|
+
configFiles: [],
|
|
2083
|
+
directoryStructure: ""
|
|
2084
|
+
};
|
|
2085
|
+
const check = (file) => existsSync5(join4(cwd, file));
|
|
2086
|
+
const configCandidates = [
|
|
2087
|
+
"package.json",
|
|
2088
|
+
"tsconfig.json",
|
|
2089
|
+
"Cargo.toml",
|
|
2090
|
+
"pyproject.toml",
|
|
2091
|
+
"setup.py",
|
|
2092
|
+
"requirements.txt",
|
|
2093
|
+
"go.mod",
|
|
2094
|
+
"pom.xml",
|
|
2095
|
+
"build.gradle",
|
|
2096
|
+
"build.gradle.kts",
|
|
2097
|
+
"CMakeLists.txt",
|
|
2098
|
+
"Makefile",
|
|
2099
|
+
".csproj",
|
|
2100
|
+
".sln",
|
|
2101
|
+
"composer.json",
|
|
2102
|
+
"Gemfile",
|
|
2103
|
+
"mix.exs",
|
|
2104
|
+
"deno.json",
|
|
2105
|
+
"bun.lockb"
|
|
2106
|
+
];
|
|
2107
|
+
info.configFiles = configCandidates.filter(check);
|
|
2108
|
+
if (check("package.json")) {
|
|
2109
|
+
info.type = "node";
|
|
2110
|
+
info.language = check("tsconfig.json") ? "TypeScript" : "JavaScript";
|
|
2111
|
+
try {
|
|
2112
|
+
const pkg = JSON.parse(readFileSync4(join4(cwd, "package.json"), "utf-8"));
|
|
2113
|
+
const scripts = pkg.scripts ?? {};
|
|
2114
|
+
info.buildCommand = scripts.build ? `npm run build` : void 0;
|
|
2115
|
+
info.testCommand = scripts.test ? `npm test` : void 0;
|
|
2116
|
+
info.devCommand = scripts.dev ? `npm run dev` : void 0;
|
|
2117
|
+
const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
2118
|
+
if (allDeps["react"]) info.framework = "React";
|
|
2119
|
+
else if (allDeps["vue"]) info.framework = "Vue";
|
|
2120
|
+
else if (allDeps["@angular/core"]) info.framework = "Angular";
|
|
2121
|
+
else if (allDeps["next"]) info.framework = "Next.js";
|
|
2122
|
+
else if (allDeps["nuxt"]) info.framework = "Nuxt";
|
|
2123
|
+
else if (allDeps["express"]) info.framework = "Express";
|
|
2124
|
+
else if (allDeps["fastify"]) info.framework = "Fastify";
|
|
2125
|
+
else if (allDeps["svelte"]) info.framework = "Svelte";
|
|
2126
|
+
} catch {
|
|
2127
|
+
}
|
|
2128
|
+
} else if (check("Cargo.toml")) {
|
|
2129
|
+
info.type = "rust";
|
|
2130
|
+
info.language = "Rust";
|
|
2131
|
+
info.buildCommand = "cargo build";
|
|
2132
|
+
info.testCommand = "cargo test";
|
|
2133
|
+
} else if (check("pyproject.toml") || check("setup.py") || check("requirements.txt")) {
|
|
2134
|
+
info.type = "python";
|
|
2135
|
+
info.language = "Python";
|
|
2136
|
+
info.testCommand = check("pytest.ini") || check("pyproject.toml") ? "pytest" : "python -m unittest";
|
|
2137
|
+
} else if (check("go.mod")) {
|
|
2138
|
+
info.type = "go";
|
|
2139
|
+
info.language = "Go";
|
|
2140
|
+
info.buildCommand = "go build ./...";
|
|
2141
|
+
info.testCommand = "go test ./...";
|
|
2142
|
+
} else if (check("pom.xml")) {
|
|
2143
|
+
info.type = "java";
|
|
2144
|
+
info.language = "Java";
|
|
2145
|
+
info.buildCommand = "mvn package";
|
|
2146
|
+
info.testCommand = "mvn test";
|
|
2147
|
+
} else if (check("build.gradle") || check("build.gradle.kts")) {
|
|
2148
|
+
info.type = "java";
|
|
2149
|
+
info.language = "Java/Kotlin";
|
|
2150
|
+
info.buildCommand = "./gradlew build";
|
|
2151
|
+
info.testCommand = "./gradlew test";
|
|
2152
|
+
}
|
|
2153
|
+
info.directoryStructure = scanDirTree(cwd);
|
|
2154
|
+
return info;
|
|
2155
|
+
}
|
|
2156
|
+
function buildInitPrompt(info, cwd) {
|
|
2157
|
+
const parts = [
|
|
2158
|
+
`\u8BF7\u4E3A\u4EE5\u4E0B\u9879\u76EE\u751F\u6210\u4E00\u4E2A AICLI.md \u4E0A\u4E0B\u6587\u6587\u4EF6\uFF08Markdown \u683C\u5F0F\uFF09\u3002\u8FD9\u4E2A\u6587\u4EF6\u4F1A\u88AB\u6CE8\u5165\u5230 AI \u5BF9\u8BDD\u7684 system prompt \u4E2D\uFF0C\u5E2E\u52A9 AI \u7406\u89E3\u9879\u76EE\u7ED3\u6784\u548C\u7F16\u7801\u89C4\u8303\u3002`,
|
|
2159
|
+
`
|
|
2160
|
+
## \u9879\u76EE\u4FE1\u606F
|
|
2161
|
+
`,
|
|
2162
|
+
`- \u5DE5\u4F5C\u76EE\u5F55: ${cwd}`,
|
|
2163
|
+
`- \u7C7B\u578B: ${info.type}`,
|
|
2164
|
+
`- \u8BED\u8A00: ${info.language}`
|
|
2165
|
+
];
|
|
2166
|
+
if (info.framework) parts.push(`- \u6846\u67B6: ${info.framework}`);
|
|
2167
|
+
if (info.buildCommand) parts.push(`- \u6784\u5EFA\u547D\u4EE4: ${info.buildCommand}`);
|
|
2168
|
+
if (info.testCommand) parts.push(`- \u6D4B\u8BD5\u547D\u4EE4: ${info.testCommand}`);
|
|
2169
|
+
if (info.devCommand) parts.push(`- \u5F00\u53D1\u547D\u4EE4: ${info.devCommand}`);
|
|
2170
|
+
parts.push(`
|
|
2171
|
+
## \u53D1\u73B0\u7684\u914D\u7F6E\u6587\u4EF6
|
|
2172
|
+
${info.configFiles.map((f) => `- ${f}`).join("\n")}`);
|
|
2173
|
+
parts.push(`
|
|
2174
|
+
## \u76EE\u5F55\u7ED3\u6784
|
|
2175
|
+
\`\`\`
|
|
2176
|
+
${info.directoryStructure}
|
|
2177
|
+
\`\`\``);
|
|
2178
|
+
parts.push(`
|
|
2179
|
+
## \u8981\u6C42
|
|
2180
|
+
\u8BF7\u751F\u6210\u4E00\u4E2A\u7ED3\u6784\u5316\u7684 Markdown \u6587\u4EF6\uFF0C\u5305\u542B\uFF1A
|
|
2181
|
+
1. \u9879\u76EE\u7B80\u4ECB\uFF08\u4E00\u53E5\u8BDD\u6982\u8FF0\uFF09
|
|
2182
|
+
2. \u6280\u672F\u6808
|
|
2183
|
+
3. \u9879\u76EE\u7ED3\u6784\u8BF4\u660E\uFF08\u57FA\u4E8E\u4E0A\u9762\u7684\u76EE\u5F55\u7ED3\u6784\uFF09
|
|
2184
|
+
4. \u5E38\u7528\u547D\u4EE4\uFF08build, test, dev \u7B49\uFF09
|
|
2185
|
+
5. \u4EE3\u7801\u98CE\u683C\u548C\u7EA6\u5B9A\uFF08\u57FA\u4E8E\u914D\u7F6E\u6587\u4EF6\u63A8\u65AD\uFF09
|
|
2186
|
+
|
|
2187
|
+
\u76F4\u63A5\u8F93\u51FA Markdown \u5185\u5BB9\uFF0C\u4E0D\u8981\u7528\u4EE3\u7801\u5757\u5305\u88F9\u6574\u4E2A\u6587\u4EF6\u3002\u4FDD\u6301\u7B80\u6D01\uFF0C\u63A7\u5236\u5728 200 \u884C\u4EE5\u5185\u3002`);
|
|
2188
|
+
return parts.join("\n");
|
|
2189
|
+
}
|
|
2190
|
+
function copyToClipboard(text) {
|
|
2191
|
+
const plat = platform();
|
|
2192
|
+
if (plat === "win32") {
|
|
2193
|
+
execSync2("clip", { input: text, stdio: ["pipe", "ignore", "ignore"] });
|
|
2194
|
+
} else if (plat === "darwin") {
|
|
2195
|
+
execSync2("pbcopy", { input: text, stdio: ["pipe", "ignore", "ignore"] });
|
|
2196
|
+
} else {
|
|
2197
|
+
try {
|
|
2198
|
+
execSync2("xclip -selection clipboard", { input: text, stdio: ["pipe", "ignore", "ignore"] });
|
|
2199
|
+
} catch {
|
|
2200
|
+
execSync2("xsel --clipboard --input", { input: text, stdio: ["pipe", "ignore", "ignore"] });
|
|
2201
|
+
}
|
|
2202
|
+
}
|
|
2203
|
+
}
|
|
1914
2204
|
var CommandRegistry = class {
|
|
1915
2205
|
commands = /* @__PURE__ */ new Map();
|
|
1916
2206
|
register(command) {
|
|
@@ -1951,6 +2241,10 @@ function createDefaultCommands() {
|
|
|
1951
2241
|
" /plugins - Show plugin directory and loaded plugins",
|
|
1952
2242
|
" /mcp - Show MCP server connections and tools",
|
|
1953
2243
|
" /config - Open configuration wizard (API keys, proxy, etc.)",
|
|
2244
|
+
" /copy - Copy last AI response to clipboard",
|
|
2245
|
+
" /cost [reset] - Show session token usage (or reset counters)",
|
|
2246
|
+
" /init [--force] - Generate AICLI.md by scanning project structure",
|
|
2247
|
+
" /skill [name|off|list] - Manage agent skills (reusable prompt packs)",
|
|
1954
2248
|
" /exit - Exit"
|
|
1955
2249
|
] : [];
|
|
1956
2250
|
console.log("\nAvailable commands:");
|
|
@@ -2496,6 +2790,159 @@ ${text}
|
|
|
2496
2790
|
ctx.renderer.renderError("Usage: /plan [execute|exit|status]");
|
|
2497
2791
|
}
|
|
2498
2792
|
},
|
|
2793
|
+
{
|
|
2794
|
+
name: "skill",
|
|
2795
|
+
description: "Manage agent skills (reusable prompt packs)",
|
|
2796
|
+
usage: "/skill [name|off|list|reload]",
|
|
2797
|
+
execute(args, ctx) {
|
|
2798
|
+
const sub = args[0]?.toLowerCase();
|
|
2799
|
+
const manager = ctx.getSkillManager();
|
|
2800
|
+
if (!manager) {
|
|
2801
|
+
ctx.renderer.printInfo("Skill system not initialized.");
|
|
2802
|
+
return;
|
|
2803
|
+
}
|
|
2804
|
+
if (!sub || sub === "list") {
|
|
2805
|
+
const skills = manager.listSkills();
|
|
2806
|
+
const active = manager.getActive();
|
|
2807
|
+
console.log();
|
|
2808
|
+
console.log(chalk2.bold(" \u{1F3AF} Agent Skills"));
|
|
2809
|
+
console.log(chalk2.dim(" " + "\u2500".repeat(50)));
|
|
2810
|
+
if (skills.length === 0) {
|
|
2811
|
+
console.log(chalk2.dim(" No skills available."));
|
|
2812
|
+
console.log(chalk2.dim(` Place .md files in: ${manager.getSkillsDir()}`));
|
|
2813
|
+
console.log(chalk2.dim(" Format: YAML frontmatter (name/description/tools) + prompt body"));
|
|
2814
|
+
} else {
|
|
2815
|
+
for (const s of skills) {
|
|
2816
|
+
const isActive = active?.meta.name === s.meta.name;
|
|
2817
|
+
const badge = isActive ? chalk2.green(" \u2713 active") : "";
|
|
2818
|
+
const toolInfo = s.meta.tools ? chalk2.dim(` [${s.meta.tools.join(", ")}]`) : "";
|
|
2819
|
+
console.log(
|
|
2820
|
+
` ${chalk2.cyan(s.meta.name.padEnd(20))}${chalk2.dim(s.meta.description)}${toolInfo}${badge}`
|
|
2821
|
+
);
|
|
2822
|
+
}
|
|
2823
|
+
}
|
|
2824
|
+
console.log(chalk2.dim(" " + "\u2500".repeat(50)));
|
|
2825
|
+
console.log(chalk2.dim(` Directory: ${manager.getSkillsDir()}`));
|
|
2826
|
+
console.log();
|
|
2827
|
+
return;
|
|
2828
|
+
}
|
|
2829
|
+
if (sub === "off" || sub === "none" || sub === "deactivate") {
|
|
2830
|
+
if (!manager.getActive()) {
|
|
2831
|
+
ctx.renderer.printInfo("No skill is currently active.");
|
|
2832
|
+
return;
|
|
2833
|
+
}
|
|
2834
|
+
manager.deactivate();
|
|
2835
|
+
ctx.refreshPrompt();
|
|
2836
|
+
ctx.renderer.printSuccess("Skill deactivated.");
|
|
2837
|
+
return;
|
|
2838
|
+
}
|
|
2839
|
+
if (sub === "reload") {
|
|
2840
|
+
const count = manager.loadSkills();
|
|
2841
|
+
ctx.renderer.printSuccess(`Reloaded: ${count} skill(s) found.`);
|
|
2842
|
+
return;
|
|
2843
|
+
}
|
|
2844
|
+
const skill = manager.activate(sub);
|
|
2845
|
+
if (!skill) {
|
|
2846
|
+
ctx.renderer.renderError(`Skill '${sub}' not found. Use /skill list to see available skills.`);
|
|
2847
|
+
return;
|
|
2848
|
+
}
|
|
2849
|
+
ctx.refreshPrompt();
|
|
2850
|
+
ctx.renderer.printSuccess(`Skill activated: ${skill.meta.name} \u2014 ${skill.meta.description}`);
|
|
2851
|
+
if (skill.meta.tools) {
|
|
2852
|
+
ctx.renderer.printInfo(`Tool whitelist: ${skill.meta.tools.join(", ")}`);
|
|
2853
|
+
}
|
|
2854
|
+
}
|
|
2855
|
+
},
|
|
2856
|
+
{
|
|
2857
|
+
name: "init",
|
|
2858
|
+
description: "Generate project context file (AICLI.md) by scanning project structure",
|
|
2859
|
+
usage: "/init [--force]",
|
|
2860
|
+
async execute(args, ctx) {
|
|
2861
|
+
const cwd = process.cwd();
|
|
2862
|
+
const gitRoot = getGitRoot(cwd);
|
|
2863
|
+
const targetDir = gitRoot ?? cwd;
|
|
2864
|
+
const targetPath = join4(targetDir, "AICLI.md");
|
|
2865
|
+
const force = args.includes("--force");
|
|
2866
|
+
if (existsSync5(targetPath) && !force) {
|
|
2867
|
+
ctx.renderer.printInfo(`AICLI.md already exists at ${targetPath}`);
|
|
2868
|
+
ctx.renderer.printInfo("Use /init --force to overwrite, or edit it manually.");
|
|
2869
|
+
return;
|
|
2870
|
+
}
|
|
2871
|
+
ctx.renderer.printInfo("Scanning project structure...");
|
|
2872
|
+
const projectInfo = scanProject(cwd);
|
|
2873
|
+
if (projectInfo.type === "unknown" && projectInfo.configFiles.length === 0) {
|
|
2874
|
+
ctx.renderer.printInfo("Could not detect project type. Generating generic template...");
|
|
2875
|
+
}
|
|
2876
|
+
const prompt = buildInitPrompt(projectInfo, cwd);
|
|
2877
|
+
console.log(chalk2.dim(" Calling AI to generate AICLI.md..."));
|
|
2878
|
+
try {
|
|
2879
|
+
const content = await ctx.chatOnce(prompt, { temperature: 0.3, maxTokens: 4096 });
|
|
2880
|
+
writeFileSync4(targetPath, content, "utf-8");
|
|
2881
|
+
ctx.renderer.printSuccess(`Generated: ${targetPath} (${content.length} chars)`);
|
|
2882
|
+
ctx.renderer.printInfo("Review and edit as needed. Use /context reload to load it.");
|
|
2883
|
+
} catch (err) {
|
|
2884
|
+
ctx.renderer.renderError(`Failed to generate AICLI.md: ${err instanceof Error ? err.message : String(err)}`);
|
|
2885
|
+
}
|
|
2886
|
+
}
|
|
2887
|
+
},
|
|
2888
|
+
{
|
|
2889
|
+
name: "copy",
|
|
2890
|
+
description: "Copy last AI response to system clipboard",
|
|
2891
|
+
usage: "/copy",
|
|
2892
|
+
execute(_args, ctx) {
|
|
2893
|
+
const content = ctx.getLastResponse();
|
|
2894
|
+
if (!content) {
|
|
2895
|
+
ctx.renderer.printInfo("No AI response to copy.");
|
|
2896
|
+
return;
|
|
2897
|
+
}
|
|
2898
|
+
try {
|
|
2899
|
+
copyToClipboard(content);
|
|
2900
|
+
const chars = content.length;
|
|
2901
|
+
const lines = content.split("\n").length;
|
|
2902
|
+
ctx.renderer.printSuccess(`Copied to clipboard (${lines} lines, ${chars} chars)`);
|
|
2903
|
+
} catch (err) {
|
|
2904
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
2905
|
+
if (platform() === "linux" && msg.includes("not found")) {
|
|
2906
|
+
ctx.renderer.renderError("Clipboard tool not found. Install xclip or xsel: sudo apt install xclip");
|
|
2907
|
+
} else {
|
|
2908
|
+
ctx.renderer.renderError(`Failed to copy: ${msg}`);
|
|
2909
|
+
}
|
|
2910
|
+
}
|
|
2911
|
+
}
|
|
2912
|
+
},
|
|
2913
|
+
{
|
|
2914
|
+
name: "cost",
|
|
2915
|
+
description: "Show session token usage summary",
|
|
2916
|
+
usage: "/cost [reset]",
|
|
2917
|
+
execute(args, ctx) {
|
|
2918
|
+
const sub = args[0]?.toLowerCase();
|
|
2919
|
+
if (sub === "reset") {
|
|
2920
|
+
ctx.resetSessionTokenUsage();
|
|
2921
|
+
ctx.renderer.printSuccess("Session token counters reset.");
|
|
2922
|
+
return;
|
|
2923
|
+
}
|
|
2924
|
+
const usage = ctx.getSessionTokenUsage();
|
|
2925
|
+
const totalTokens = usage.inputTokens + usage.outputTokens;
|
|
2926
|
+
if (totalTokens === 0) {
|
|
2927
|
+
ctx.renderer.printInfo("No token usage recorded this session.");
|
|
2928
|
+
return;
|
|
2929
|
+
}
|
|
2930
|
+
console.log();
|
|
2931
|
+
console.log(chalk2.bold(" \u{1F4CA} Session Token Usage"));
|
|
2932
|
+
console.log(chalk2.dim(" " + "\u2500".repeat(40)));
|
|
2933
|
+
console.log(chalk2.gray(" Input tokens : ") + chalk2.white(usage.inputTokens.toLocaleString()));
|
|
2934
|
+
console.log(chalk2.gray(" Output tokens : ") + chalk2.white(usage.outputTokens.toLocaleString()));
|
|
2935
|
+
console.log(chalk2.gray(" Total tokens : ") + chalk2.bold.white(totalTokens.toLocaleString()));
|
|
2936
|
+
console.log(chalk2.dim(" " + "\u2500".repeat(40)));
|
|
2937
|
+
const session = ctx.sessions.current;
|
|
2938
|
+
if (session) {
|
|
2939
|
+
console.log(chalk2.gray(" Provider : ") + chalk2.dim(ctx.getCurrentProvider()));
|
|
2940
|
+
console.log(chalk2.gray(" Model : ") + chalk2.dim(ctx.getCurrentModel()));
|
|
2941
|
+
console.log(chalk2.gray(" Messages : ") + chalk2.dim(String(session.messages.length)));
|
|
2942
|
+
}
|
|
2943
|
+
console.log();
|
|
2944
|
+
}
|
|
2945
|
+
},
|
|
2499
2946
|
{
|
|
2500
2947
|
name: "exit",
|
|
2501
2948
|
description: "Exit the REPL",
|
|
@@ -2672,11 +3119,11 @@ function selectFromList(prompt, items, initialIndex = 0) {
|
|
|
2672
3119
|
}
|
|
2673
3120
|
|
|
2674
3121
|
// src/tools/builtin/bash.ts
|
|
2675
|
-
import { execSync } from "child_process";
|
|
2676
|
-
import { existsSync as
|
|
2677
|
-
import { platform } from "os";
|
|
3122
|
+
import { execSync as execSync3 } from "child_process";
|
|
3123
|
+
import { existsSync as existsSync6 } from "fs";
|
|
3124
|
+
import { platform as platform2 } from "os";
|
|
2678
3125
|
import { resolve as resolve2 } from "path";
|
|
2679
|
-
var IS_WINDOWS =
|
|
3126
|
+
var IS_WINDOWS = platform2() === "win32";
|
|
2680
3127
|
var SHELL = IS_WINDOWS ? "powershell.exe" : process.env["SHELL"] ?? "/bin/bash";
|
|
2681
3128
|
var persistentCwd = process.cwd();
|
|
2682
3129
|
var bashTool = {
|
|
@@ -2722,7 +3169,7 @@ var bashTool = {
|
|
|
2722
3169
|
let effectiveCwd = persistentCwd;
|
|
2723
3170
|
if (cwdArg) {
|
|
2724
3171
|
const resolved = resolve2(persistentCwd, cwdArg);
|
|
2725
|
-
if (
|
|
3172
|
+
if (existsSync6(resolved)) {
|
|
2726
3173
|
effectiveCwd = resolved;
|
|
2727
3174
|
persistentCwd = resolved;
|
|
2728
3175
|
} else {
|
|
@@ -2737,7 +3184,7 @@ var bashTool = {
|
|
|
2737
3184
|
actualCommand = command;
|
|
2738
3185
|
}
|
|
2739
3186
|
try {
|
|
2740
|
-
const output =
|
|
3187
|
+
const output = execSync3(actualCommand, {
|
|
2741
3188
|
timeout,
|
|
2742
3189
|
encoding: IS_WINDOWS ? "buffer" : "utf-8",
|
|
2743
3190
|
stdio: ["pipe", "pipe", "pipe"],
|
|
@@ -2793,7 +3240,7 @@ function updateCwdFromCommand(command, baseCwd) {
|
|
|
2793
3240
|
if (!target || target.startsWith("$") || target === "~") return;
|
|
2794
3241
|
try {
|
|
2795
3242
|
const newDir = resolve2(baseCwd, target);
|
|
2796
|
-
if (
|
|
3243
|
+
if (existsSync6(newDir)) {
|
|
2797
3244
|
persistentCwd = newDir;
|
|
2798
3245
|
}
|
|
2799
3246
|
} catch {
|
|
@@ -2801,14 +3248,14 @@ function updateCwdFromCommand(command, baseCwd) {
|
|
|
2801
3248
|
}
|
|
2802
3249
|
|
|
2803
3250
|
// src/tools/builtin/read-file.ts
|
|
2804
|
-
import { readFileSync as
|
|
2805
|
-
import { extname, resolve as resolve3, basename, sep } from "path";
|
|
3251
|
+
import { readFileSync as readFileSync5, existsSync as existsSync7, statSync as statSync2 } from "fs";
|
|
3252
|
+
import { extname, resolve as resolve3, basename as basename2, sep } from "path";
|
|
2806
3253
|
import { homedir as homedir2 } from "os";
|
|
2807
3254
|
var MAX_FILE_BYTES = 10 * 1024 * 1024;
|
|
2808
3255
|
function getSensitiveWarning(normalizedPath) {
|
|
2809
3256
|
const home = homedir2();
|
|
2810
3257
|
const p = normalizedPath.toLowerCase();
|
|
2811
|
-
const base =
|
|
3258
|
+
const base = basename2(normalizedPath).toLowerCase();
|
|
2812
3259
|
if (normalizedPath.startsWith(home) && p.includes(".aicli") && base === "config.json") {
|
|
2813
3260
|
return "[\u26A0 Security Warning: This file contains API keys. Be careful not to share this content.]\n\n";
|
|
2814
3261
|
}
|
|
@@ -2894,8 +3341,8 @@ var readFileTool = {
|
|
|
2894
3341
|
const encoding = args["encoding"] ?? "utf-8";
|
|
2895
3342
|
if (!filePath) throw new Error("path is required");
|
|
2896
3343
|
const normalizedPath = resolve3(filePath);
|
|
2897
|
-
if (!
|
|
2898
|
-
const { size } =
|
|
3344
|
+
if (!existsSync7(normalizedPath)) throw new Error(`File not found: ${filePath}`);
|
|
3345
|
+
const { size } = statSync2(normalizedPath);
|
|
2899
3346
|
if (size > MAX_FILE_BYTES) {
|
|
2900
3347
|
const mb = (size / 1024 / 1024).toFixed(1);
|
|
2901
3348
|
return `[File too large: ${filePath} (${mb} MB)]
|
|
@@ -2915,7 +3362,7 @@ var readFileTool = {
|
|
|
2915
3362
|
2. \u4F7F\u7528 bash \u5DE5\u5177\u8C03\u7528\u5916\u90E8\u8F6C\u6362\u7A0B\u5E8F\uFF08\u5982 pdftotext\u3001pandoc \u7B49\uFF09
|
|
2916
3363
|
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`;
|
|
2917
3364
|
}
|
|
2918
|
-
const buf =
|
|
3365
|
+
const buf = readFileSync5(normalizedPath);
|
|
2919
3366
|
if (encoding === "base64") {
|
|
2920
3367
|
return `[File: ${filePath} | base64]
|
|
2921
3368
|
|
|
@@ -2989,7 +3436,7 @@ var writeFileTool = {
|
|
|
2989
3436
|
};
|
|
2990
3437
|
|
|
2991
3438
|
// src/tools/builtin/edit-file.ts
|
|
2992
|
-
import { readFileSync as
|
|
3439
|
+
import { readFileSync as readFileSync6, writeFileSync as writeFileSync6, existsSync as existsSync8 } from "fs";
|
|
2993
3440
|
var editFileTool = {
|
|
2994
3441
|
definition: {
|
|
2995
3442
|
name: "edit_file",
|
|
@@ -3047,8 +3494,8 @@ var editFileTool = {
|
|
|
3047
3494
|
const filePath = String(args["path"] ?? "");
|
|
3048
3495
|
const encoding = args["encoding"] ?? "utf-8";
|
|
3049
3496
|
if (!filePath) throw new Error("path is required");
|
|
3050
|
-
if (!
|
|
3051
|
-
const original =
|
|
3497
|
+
if (!existsSync8(filePath)) throw new Error(`File not found: ${filePath}`);
|
|
3498
|
+
const original = readFileSync6(filePath, encoding);
|
|
3052
3499
|
if (args["old_str"] !== void 0) {
|
|
3053
3500
|
const oldStr = String(args["old_str"]);
|
|
3054
3501
|
const newStr = String(args["new_str"] ?? "");
|
|
@@ -3113,8 +3560,8 @@ function truncatePreview(str, maxLen = 80) {
|
|
|
3113
3560
|
}
|
|
3114
3561
|
|
|
3115
3562
|
// src/tools/builtin/list-dir.ts
|
|
3116
|
-
import { readdirSync as
|
|
3117
|
-
import { join as
|
|
3563
|
+
import { readdirSync as readdirSync3, statSync as statSync3, existsSync as existsSync9 } from "fs";
|
|
3564
|
+
import { join as join5 } from "path";
|
|
3118
3565
|
var listDirTool = {
|
|
3119
3566
|
definition: {
|
|
3120
3567
|
name: "list_dir",
|
|
@@ -3136,7 +3583,7 @@ var listDirTool = {
|
|
|
3136
3583
|
async execute(args) {
|
|
3137
3584
|
const dirPath = String(args["path"] ?? process.cwd());
|
|
3138
3585
|
const recursive = Boolean(args["recursive"] ?? false);
|
|
3139
|
-
if (!
|
|
3586
|
+
if (!existsSync9(dirPath)) throw new Error(`Directory not found: ${dirPath}`);
|
|
3140
3587
|
const lines = [`Directory: ${dirPath}
|
|
3141
3588
|
`];
|
|
3142
3589
|
listRecursive(dirPath, "", recursive, lines);
|
|
@@ -3146,7 +3593,7 @@ var listDirTool = {
|
|
|
3146
3593
|
function listRecursive(basePath, indent, recursive, lines) {
|
|
3147
3594
|
let entries;
|
|
3148
3595
|
try {
|
|
3149
|
-
entries =
|
|
3596
|
+
entries = readdirSync3(basePath, { withFileTypes: true });
|
|
3150
3597
|
} catch {
|
|
3151
3598
|
lines.push(`${indent}(permission denied)`);
|
|
3152
3599
|
return;
|
|
@@ -3165,11 +3612,11 @@ function listRecursive(basePath, indent, recursive, lines) {
|
|
|
3165
3612
|
if (entry.isDirectory()) {
|
|
3166
3613
|
lines.push(`${indent}\u{1F4C1} ${entry.name}/`);
|
|
3167
3614
|
if (recursive) {
|
|
3168
|
-
listRecursive(
|
|
3615
|
+
listRecursive(join5(basePath, entry.name), indent + " ", true, lines);
|
|
3169
3616
|
}
|
|
3170
3617
|
} else {
|
|
3171
3618
|
try {
|
|
3172
|
-
const stat =
|
|
3619
|
+
const stat = statSync3(join5(basePath, entry.name));
|
|
3173
3620
|
const size = formatSize(stat.size);
|
|
3174
3621
|
lines.push(`${indent}\u{1F4C4} ${entry.name} (${size})`);
|
|
3175
3622
|
} catch {
|
|
@@ -3185,8 +3632,8 @@ function formatSize(bytes) {
|
|
|
3185
3632
|
}
|
|
3186
3633
|
|
|
3187
3634
|
// src/tools/builtin/grep-files.ts
|
|
3188
|
-
import { readdirSync as
|
|
3189
|
-
import { join as
|
|
3635
|
+
import { readdirSync as readdirSync4, readFileSync as readFileSync7, statSync as statSync4, existsSync as existsSync10 } from "fs";
|
|
3636
|
+
import { join as join6, relative } from "path";
|
|
3190
3637
|
var grepFilesTool = {
|
|
3191
3638
|
definition: {
|
|
3192
3639
|
name: "grep_files",
|
|
@@ -3237,7 +3684,7 @@ var grepFilesTool = {
|
|
|
3237
3684
|
const contextLines = Math.max(0, Number(args["context_lines"] ?? 0));
|
|
3238
3685
|
const maxResults = Math.max(1, Number(args["max_results"] ?? 50));
|
|
3239
3686
|
if (!pattern) throw new Error("pattern is required");
|
|
3240
|
-
if (!
|
|
3687
|
+
if (!existsSync10(rootPath)) throw new Error(`Path not found: ${rootPath}`);
|
|
3241
3688
|
let regex;
|
|
3242
3689
|
try {
|
|
3243
3690
|
regex = new RegExp(pattern, ignoreCase ? "gi" : "g");
|
|
@@ -3246,7 +3693,7 @@ var grepFilesTool = {
|
|
|
3246
3693
|
regex = new RegExp(escaped, ignoreCase ? "gi" : "g");
|
|
3247
3694
|
}
|
|
3248
3695
|
const results = [];
|
|
3249
|
-
const stat =
|
|
3696
|
+
const stat = statSync4(rootPath);
|
|
3250
3697
|
if (stat.isFile()) {
|
|
3251
3698
|
searchInFile(rootPath, rootPath, regex, contextLines, maxResults, results);
|
|
3252
3699
|
} else {
|
|
@@ -3302,7 +3749,7 @@ function collectFiles(dirPath, filePattern, results, regex, contextLines, maxRes
|
|
|
3302
3749
|
if (results.length >= maxResults) return;
|
|
3303
3750
|
let entries;
|
|
3304
3751
|
try {
|
|
3305
|
-
entries =
|
|
3752
|
+
entries = readdirSync4(dirPath, { withFileTypes: true });
|
|
3306
3753
|
} catch {
|
|
3307
3754
|
return;
|
|
3308
3755
|
}
|
|
@@ -3310,11 +3757,11 @@ function collectFiles(dirPath, filePattern, results, regex, contextLines, maxRes
|
|
|
3310
3757
|
if (results.length >= maxResults) return;
|
|
3311
3758
|
if (entry.isDirectory()) {
|
|
3312
3759
|
if (SKIP_DIRS.has(entry.name) || entry.name.startsWith(".")) continue;
|
|
3313
|
-
collectFiles(
|
|
3760
|
+
collectFiles(join6(dirPath, entry.name), filePattern, results, regex, contextLines, maxResults, rootPath);
|
|
3314
3761
|
} else if (entry.isFile()) {
|
|
3315
3762
|
if (isBinary(entry.name)) continue;
|
|
3316
3763
|
if (filePattern && !matchesFilePattern(entry.name, filePattern)) continue;
|
|
3317
|
-
const fullPath =
|
|
3764
|
+
const fullPath = join6(dirPath, entry.name);
|
|
3318
3765
|
const relPath = relative(rootPath, fullPath);
|
|
3319
3766
|
searchInFile(fullPath, relPath, regex, contextLines, maxResults, results);
|
|
3320
3767
|
}
|
|
@@ -3323,7 +3770,7 @@ function collectFiles(dirPath, filePattern, results, regex, contextLines, maxRes
|
|
|
3323
3770
|
function searchInFile(fullPath, displayPath, regex, contextLines, maxResults, results) {
|
|
3324
3771
|
let content;
|
|
3325
3772
|
try {
|
|
3326
|
-
content =
|
|
3773
|
+
content = readFileSync7(fullPath, "utf-8");
|
|
3327
3774
|
} catch {
|
|
3328
3775
|
return;
|
|
3329
3776
|
}
|
|
@@ -3355,8 +3802,8 @@ function searchInFile(fullPath, displayPath, regex, contextLines, maxResults, re
|
|
|
3355
3802
|
}
|
|
3356
3803
|
|
|
3357
3804
|
// src/tools/builtin/glob-files.ts
|
|
3358
|
-
import { readdirSync as
|
|
3359
|
-
import { join as
|
|
3805
|
+
import { readdirSync as readdirSync5, statSync as statSync5, existsSync as existsSync11 } from "fs";
|
|
3806
|
+
import { join as join7, relative as relative2, basename as basename3 } from "path";
|
|
3360
3807
|
var globFilesTool = {
|
|
3361
3808
|
definition: {
|
|
3362
3809
|
name: "glob_files",
|
|
@@ -3389,7 +3836,7 @@ var globFilesTool = {
|
|
|
3389
3836
|
const rootPath = String(args["path"] ?? process.cwd());
|
|
3390
3837
|
const maxResults = Math.max(1, Number(args["max_results"] ?? 100));
|
|
3391
3838
|
if (!pattern) throw new Error("pattern is required");
|
|
3392
|
-
if (!
|
|
3839
|
+
if (!existsSync11(rootPath)) throw new Error(`Path not found: ${rootPath}`);
|
|
3393
3840
|
const regex = globToRegex(pattern);
|
|
3394
3841
|
const matches = [];
|
|
3395
3842
|
collectMatchingFiles(rootPath, rootPath, regex, matches, maxResults);
|
|
@@ -3460,21 +3907,21 @@ function collectMatchingFiles(dirPath, rootPath, regex, results, maxResults) {
|
|
|
3460
3907
|
if (results.length >= maxResults) return;
|
|
3461
3908
|
let entries;
|
|
3462
3909
|
try {
|
|
3463
|
-
entries =
|
|
3910
|
+
entries = readdirSync5(dirPath, { withFileTypes: true });
|
|
3464
3911
|
} catch {
|
|
3465
3912
|
return;
|
|
3466
3913
|
}
|
|
3467
3914
|
for (const entry of entries) {
|
|
3468
3915
|
if (results.length >= maxResults) break;
|
|
3469
|
-
const fullPath =
|
|
3916
|
+
const fullPath = join7(dirPath, entry.name);
|
|
3470
3917
|
if (entry.isDirectory()) {
|
|
3471
3918
|
if (SKIP_DIRS2.has(entry.name) || entry.name.startsWith(".")) continue;
|
|
3472
3919
|
collectMatchingFiles(fullPath, rootPath, regex, results, maxResults);
|
|
3473
3920
|
} else if (entry.isFile()) {
|
|
3474
3921
|
const relPath = relative2(rootPath, fullPath).replace(/\\/g, "/");
|
|
3475
|
-
if (regex.test(relPath) || regex.test(
|
|
3922
|
+
if (regex.test(relPath) || regex.test(basename3(relPath))) {
|
|
3476
3923
|
try {
|
|
3477
|
-
const stat =
|
|
3924
|
+
const stat = statSync5(fullPath);
|
|
3478
3925
|
results.push({ relPath, absPath: fullPath, mtime: stat.mtimeMs });
|
|
3479
3926
|
} catch {
|
|
3480
3927
|
results.push({ relPath, absPath: fullPath, mtime: 0 });
|
|
@@ -3486,8 +3933,8 @@ function collectMatchingFiles(dirPath, rootPath, regex, results, maxResults) {
|
|
|
3486
3933
|
|
|
3487
3934
|
// src/tools/builtin/run-interactive.ts
|
|
3488
3935
|
import { spawn } from "child_process";
|
|
3489
|
-
import { platform as
|
|
3490
|
-
var IS_WINDOWS2 =
|
|
3936
|
+
import { platform as platform3 } from "os";
|
|
3937
|
+
var IS_WINDOWS2 = platform3() === "win32";
|
|
3491
3938
|
var runInteractiveTool = {
|
|
3492
3939
|
definition: {
|
|
3493
3940
|
name: "run_interactive",
|
|
@@ -3818,11 +4265,11 @@ var saveLastResponseTool = {
|
|
|
3818
4265
|
};
|
|
3819
4266
|
|
|
3820
4267
|
// src/tools/builtin/save-memory.ts
|
|
3821
|
-
import { existsSync as
|
|
3822
|
-
import { join as
|
|
4268
|
+
import { existsSync as existsSync12, readFileSync as readFileSync8, appendFileSync as appendFileSync2, mkdirSync as mkdirSync7 } from "fs";
|
|
4269
|
+
import { join as join8 } from "path";
|
|
3823
4270
|
import { homedir as homedir3 } from "os";
|
|
3824
4271
|
function getMemoryFilePath() {
|
|
3825
|
-
return
|
|
4272
|
+
return join8(homedir3(), CONFIG_DIR_NAME, MEMORY_FILE_NAME);
|
|
3826
4273
|
}
|
|
3827
4274
|
function formatTimestamp() {
|
|
3828
4275
|
const now = /* @__PURE__ */ new Date();
|
|
@@ -3846,8 +4293,8 @@ var saveMemoryTool = {
|
|
|
3846
4293
|
const content = String(args["content"] ?? "").trim();
|
|
3847
4294
|
if (!content) throw new Error("content is required");
|
|
3848
4295
|
const memoryPath = getMemoryFilePath();
|
|
3849
|
-
const configDir =
|
|
3850
|
-
if (!
|
|
4296
|
+
const configDir = join8(homedir3(), CONFIG_DIR_NAME);
|
|
4297
|
+
if (!existsSync12(configDir)) {
|
|
3851
4298
|
mkdirSync7(configDir, { recursive: true });
|
|
3852
4299
|
}
|
|
3853
4300
|
const timestamp = formatTimestamp();
|
|
@@ -3856,7 +4303,7 @@ var saveMemoryTool = {
|
|
|
3856
4303
|
${content}
|
|
3857
4304
|
`;
|
|
3858
4305
|
appendFileSync2(memoryPath, entry, "utf-8");
|
|
3859
|
-
const fullContent =
|
|
4306
|
+
const fullContent = readFileSync8(memoryPath, "utf-8");
|
|
3860
4307
|
const entryCount = (fullContent.match(/^## \d{4}-\d{2}-\d{2}/gm) ?? []).length;
|
|
3861
4308
|
const byteSize = Buffer.byteLength(fullContent, "utf-8");
|
|
3862
4309
|
return `Memory saved successfully. Total: ${entryCount} entries, ${byteSize} bytes in ${MEMORY_FILE_NAME}`;
|
|
@@ -4137,6 +4584,9 @@ function formatResults(query, data, requested) {
|
|
|
4137
4584
|
import chalk6 from "chalk";
|
|
4138
4585
|
|
|
4139
4586
|
// src/tools/types.ts
|
|
4587
|
+
function isFileWriteTool(name) {
|
|
4588
|
+
return name === "write_file" || name === "edit_file";
|
|
4589
|
+
}
|
|
4140
4590
|
function getDangerLevel(toolName, args) {
|
|
4141
4591
|
if (toolName.startsWith("mcp__")) return "safe";
|
|
4142
4592
|
if (toolName === "bash") {
|
|
@@ -4434,8 +4884,8 @@ var spawnAgentTool = {
|
|
|
4434
4884
|
|
|
4435
4885
|
// src/tools/registry.ts
|
|
4436
4886
|
import { pathToFileURL } from "url";
|
|
4437
|
-
import { existsSync as
|
|
4438
|
-
import { join as
|
|
4887
|
+
import { existsSync as existsSync13, mkdirSync as mkdirSync8, readdirSync as readdirSync6 } from "fs";
|
|
4888
|
+
import { join as join9 } from "path";
|
|
4439
4889
|
var ToolRegistry = class {
|
|
4440
4890
|
tools = /* @__PURE__ */ new Map();
|
|
4441
4891
|
pluginToolNames = /* @__PURE__ */ new Set();
|
|
@@ -4507,7 +4957,7 @@ var ToolRegistry = class {
|
|
|
4507
4957
|
* Returns the number of successfully loaded plugins.
|
|
4508
4958
|
*/
|
|
4509
4959
|
async loadPlugins(pluginsDir, allowPlugins = false) {
|
|
4510
|
-
if (!
|
|
4960
|
+
if (!existsSync13(pluginsDir)) {
|
|
4511
4961
|
try {
|
|
4512
4962
|
mkdirSync8(pluginsDir, { recursive: true });
|
|
4513
4963
|
} catch {
|
|
@@ -4516,7 +4966,7 @@ var ToolRegistry = class {
|
|
|
4516
4966
|
}
|
|
4517
4967
|
let files;
|
|
4518
4968
|
try {
|
|
4519
|
-
files =
|
|
4969
|
+
files = readdirSync6(pluginsDir).filter((f) => f.endsWith(".js"));
|
|
4520
4970
|
} catch {
|
|
4521
4971
|
return 0;
|
|
4522
4972
|
}
|
|
@@ -4533,12 +4983,12 @@ var ToolRegistry = class {
|
|
|
4533
4983
|
process.stderr.write(
|
|
4534
4984
|
`
|
|
4535
4985
|
[plugins] \u26A0 Loading ${files.length} plugin(s) with FULL system privileges:
|
|
4536
|
-
` + files.map((f) => ` + ${
|
|
4986
|
+
` + files.map((f) => ` + ${join9(pluginsDir, f)}`).join("\n") + "\n\n"
|
|
4537
4987
|
);
|
|
4538
4988
|
let loaded = 0;
|
|
4539
4989
|
for (const file of files) {
|
|
4540
4990
|
try {
|
|
4541
|
-
const fileUrl = pathToFileURL(
|
|
4991
|
+
const fileUrl = pathToFileURL(join9(pluginsDir, file)).href;
|
|
4542
4992
|
const mod = await import(fileUrl);
|
|
4543
4993
|
const tool = mod.tool ?? mod.default?.tool ?? mod.default;
|
|
4544
4994
|
if (!tool || typeof tool.execute !== "function" || !tool.definition?.name) {
|
|
@@ -4567,7 +5017,7 @@ var ToolRegistry = class {
|
|
|
4567
5017
|
|
|
4568
5018
|
// src/tools/executor.ts
|
|
4569
5019
|
import chalk8 from "chalk";
|
|
4570
|
-
import { existsSync as
|
|
5020
|
+
import { existsSync as existsSync14, readFileSync as readFileSync9 } from "fs";
|
|
4571
5021
|
|
|
4572
5022
|
// src/tools/diff-utils.ts
|
|
4573
5023
|
import chalk7 from "chalk";
|
|
@@ -4821,12 +5271,130 @@ var ToolExecutor = class {
|
|
|
4821
5271
|
}
|
|
4822
5272
|
}
|
|
4823
5273
|
async executeAll(calls) {
|
|
5274
|
+
const safeCalls = [];
|
|
5275
|
+
const fileWriteCalls = [];
|
|
5276
|
+
const otherCalls = [];
|
|
5277
|
+
for (let i = 0; i < calls.length; i++) {
|
|
5278
|
+
const call = calls[i];
|
|
5279
|
+
const level = getDangerLevel(call.name, call.arguments);
|
|
5280
|
+
if (level === "safe") {
|
|
5281
|
+
safeCalls.push({ idx: i, call });
|
|
5282
|
+
} else if (isFileWriteTool(call.name) && level === "write") {
|
|
5283
|
+
fileWriteCalls.push({ idx: i, call });
|
|
5284
|
+
} else {
|
|
5285
|
+
otherCalls.push({ idx: i, call });
|
|
5286
|
+
}
|
|
5287
|
+
}
|
|
5288
|
+
const results = new Array(calls.length);
|
|
5289
|
+
for (const { idx, call } of safeCalls) {
|
|
5290
|
+
results[idx] = await this.execute(call);
|
|
5291
|
+
}
|
|
5292
|
+
if (fileWriteCalls.length === 1) {
|
|
5293
|
+
const { idx, call } = fileWriteCalls[0];
|
|
5294
|
+
results[idx] = await this.execute(call);
|
|
5295
|
+
} else if (fileWriteCalls.length >= 2) {
|
|
5296
|
+
const batchResults = await this.executeBatchFileWrites(fileWriteCalls.map((f) => f.call));
|
|
5297
|
+
for (let i = 0; i < fileWriteCalls.length; i++) {
|
|
5298
|
+
results[fileWriteCalls[i].idx] = batchResults[i];
|
|
5299
|
+
}
|
|
5300
|
+
}
|
|
5301
|
+
for (const { idx, call } of otherCalls) {
|
|
5302
|
+
results[idx] = await this.execute(call);
|
|
5303
|
+
}
|
|
5304
|
+
return results;
|
|
5305
|
+
}
|
|
5306
|
+
/**
|
|
5307
|
+
* 批量文件写入:展示所有文件的编号列表 + diff 预览,
|
|
5308
|
+
* 然后让用户 approve all / reject all / 选择性 approve。
|
|
5309
|
+
*/
|
|
5310
|
+
async executeBatchFileWrites(calls) {
|
|
5311
|
+
console.log();
|
|
5312
|
+
console.log(chalk8.bold.yellow(`\u270E Batch file writes (${calls.length} files):`));
|
|
5313
|
+
console.log(chalk8.dim("\u2500".repeat(50)));
|
|
5314
|
+
for (let i = 0; i < calls.length; i++) {
|
|
5315
|
+
const call = calls[i];
|
|
5316
|
+
const filePath = String(call.arguments["path"] ?? "");
|
|
5317
|
+
console.log(chalk8.yellow(` [${i + 1}] `) + chalk8.white(call.name) + chalk8.gray(": ") + chalk8.cyan(filePath));
|
|
5318
|
+
this.printDiffPreview(call);
|
|
5319
|
+
}
|
|
5320
|
+
console.log(chalk8.dim("\u2500".repeat(50)));
|
|
5321
|
+
const decision = await this.batchConfirm(calls.length);
|
|
4824
5322
|
const results = [];
|
|
4825
|
-
for (
|
|
4826
|
-
|
|
5323
|
+
for (let i = 0; i < calls.length; i++) {
|
|
5324
|
+
const call = calls[i];
|
|
5325
|
+
const approved = decision === "all" || decision !== "none" && decision.has(i + 1);
|
|
5326
|
+
if (approved) {
|
|
5327
|
+
const tool = this.registry.get(call.name);
|
|
5328
|
+
if (!tool) {
|
|
5329
|
+
results.push({ callId: call.id, content: `Unknown tool: ${call.name}`, isError: true });
|
|
5330
|
+
continue;
|
|
5331
|
+
}
|
|
5332
|
+
try {
|
|
5333
|
+
const rawContent = await tool.execute(call.arguments);
|
|
5334
|
+
const content = truncateOutput2(rawContent, call.name);
|
|
5335
|
+
const wasTruncated = content !== rawContent;
|
|
5336
|
+
this.printToolResult(call.name, rawContent, false, wasTruncated);
|
|
5337
|
+
results.push({ callId: call.id, content, isError: false });
|
|
5338
|
+
} catch (err) {
|
|
5339
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
5340
|
+
this.printToolResult(call.name, message, true, false);
|
|
5341
|
+
results.push({ callId: call.id, content: message, isError: true });
|
|
5342
|
+
}
|
|
5343
|
+
} else {
|
|
5344
|
+
console.log(chalk8.gray(` [${i + 1}] `) + chalk8.dim("rejected"));
|
|
5345
|
+
results.push({ callId: call.id, content: "User rejected the operation.", isError: false });
|
|
5346
|
+
}
|
|
4827
5347
|
}
|
|
4828
5348
|
return results;
|
|
4829
5349
|
}
|
|
5350
|
+
/**
|
|
5351
|
+
* 批量确认:让用户选择 approve all / reject all / 指定编号。
|
|
5352
|
+
* 返回 'all' | 'none' | Set<number>(1-based 编号)
|
|
5353
|
+
*/
|
|
5354
|
+
batchConfirm(count) {
|
|
5355
|
+
const prompt = chalk8.yellow(` [a]pprove all [r]eject all [1,${count > 1 ? count : "2"},..] approve specific: `);
|
|
5356
|
+
if (!this.rl) {
|
|
5357
|
+
process.stdout.write(chalk8.yellow("No readline: auto-rejected.\n"));
|
|
5358
|
+
return Promise.resolve("none");
|
|
5359
|
+
}
|
|
5360
|
+
const rl = this.rl;
|
|
5361
|
+
const rlAny = rl;
|
|
5362
|
+
const savedOutput = rlAny.output;
|
|
5363
|
+
rlAny.output = process.stdout;
|
|
5364
|
+
rl.resume();
|
|
5365
|
+
process.stdout.write(prompt);
|
|
5366
|
+
this.confirming = true;
|
|
5367
|
+
return new Promise((resolve5) => {
|
|
5368
|
+
const cleanup = (result) => {
|
|
5369
|
+
rl.removeListener("line", onLine);
|
|
5370
|
+
this.cancelConfirmFn = null;
|
|
5371
|
+
rl.pause();
|
|
5372
|
+
rlAny.output = savedOutput;
|
|
5373
|
+
this.confirming = false;
|
|
5374
|
+
resolve5(result);
|
|
5375
|
+
};
|
|
5376
|
+
const onLine = (line) => {
|
|
5377
|
+
const input2 = line.trim().toLowerCase();
|
|
5378
|
+
if (input2 === "a" || input2 === "all" || input2 === "y") {
|
|
5379
|
+
cleanup("all");
|
|
5380
|
+
} else if (input2 === "r" || input2 === "reject" || input2 === "n" || input2 === "") {
|
|
5381
|
+
cleanup("none");
|
|
5382
|
+
} else {
|
|
5383
|
+
const nums = input2.split(/[,\s]+/).map(Number).filter((n) => !isNaN(n) && n >= 1 && n <= count);
|
|
5384
|
+
if (nums.length > 0) {
|
|
5385
|
+
cleanup(new Set(nums));
|
|
5386
|
+
} else {
|
|
5387
|
+
cleanup("none");
|
|
5388
|
+
}
|
|
5389
|
+
}
|
|
5390
|
+
};
|
|
5391
|
+
this.cancelConfirmFn = () => {
|
|
5392
|
+
process.stdout.write(chalk8.gray("\n(cancelled)\n"));
|
|
5393
|
+
cleanup("none");
|
|
5394
|
+
};
|
|
5395
|
+
rl.once("line", onLine);
|
|
5396
|
+
});
|
|
5397
|
+
}
|
|
4830
5398
|
printToolCall(call) {
|
|
4831
5399
|
const dangerLevel = getDangerLevel(call.name, call.arguments);
|
|
4832
5400
|
console.log();
|
|
@@ -4857,10 +5425,10 @@ var ToolExecutor = class {
|
|
|
4857
5425
|
const filePath = String(call.arguments["path"] ?? "");
|
|
4858
5426
|
const newContent = String(call.arguments["content"] ?? "");
|
|
4859
5427
|
if (!filePath) return;
|
|
4860
|
-
if (
|
|
5428
|
+
if (existsSync14(filePath)) {
|
|
4861
5429
|
let oldContent;
|
|
4862
5430
|
try {
|
|
4863
|
-
oldContent =
|
|
5431
|
+
oldContent = readFileSync9(filePath, "utf-8");
|
|
4864
5432
|
} catch {
|
|
4865
5433
|
return;
|
|
4866
5434
|
}
|
|
@@ -4883,7 +5451,7 @@ var ToolExecutor = class {
|
|
|
4883
5451
|
}
|
|
4884
5452
|
} else if (call.name === "edit_file") {
|
|
4885
5453
|
const filePath = String(call.arguments["path"] ?? "");
|
|
4886
|
-
if (!filePath || !
|
|
5454
|
+
if (!filePath || !existsSync14(filePath)) return;
|
|
4887
5455
|
const oldStr = call.arguments["old_str"];
|
|
4888
5456
|
const newStr = call.arguments["new_str"];
|
|
4889
5457
|
if (oldStr !== void 0) {
|
|
@@ -4910,7 +5478,7 @@ var ToolExecutor = class {
|
|
|
4910
5478
|
const to = Number(call.arguments["delete_to_line"] ?? from);
|
|
4911
5479
|
let fileContent;
|
|
4912
5480
|
try {
|
|
4913
|
-
fileContent =
|
|
5481
|
+
fileContent = readFileSync9(filePath, "utf-8");
|
|
4914
5482
|
} catch {
|
|
4915
5483
|
return;
|
|
4916
5484
|
}
|
|
@@ -5216,8 +5784,8 @@ Managing ${displayName} API Key`);
|
|
|
5216
5784
|
};
|
|
5217
5785
|
|
|
5218
5786
|
// src/repl/dev-state.ts
|
|
5219
|
-
import { existsSync as
|
|
5220
|
-
import { join as
|
|
5787
|
+
import { existsSync as existsSync15, readFileSync as readFileSync10, writeFileSync as writeFileSync8, unlinkSync as unlinkSync2, mkdirSync as mkdirSync9 } from "fs";
|
|
5788
|
+
import { join as join10 } from "path";
|
|
5221
5789
|
import { homedir as homedir4 } from "os";
|
|
5222
5790
|
var DEV_STATE_MAX_CHARS = 4e3;
|
|
5223
5791
|
var SNAPSHOT_PROMPT = `You are about to be replaced by a different AI model. Please generate a structured development state snapshot so the next model can continue seamlessly.
|
|
@@ -5256,11 +5824,11 @@ function sessionHasMeaningfulContent(messages) {
|
|
|
5256
5824
|
return hasUser && hasAssistant;
|
|
5257
5825
|
}
|
|
5258
5826
|
function getDevStatePath() {
|
|
5259
|
-
return
|
|
5827
|
+
return join10(homedir4(), CONFIG_DIR_NAME, DEV_STATE_FILE_NAME);
|
|
5260
5828
|
}
|
|
5261
5829
|
function saveDevState(content) {
|
|
5262
|
-
const configDir =
|
|
5263
|
-
if (!
|
|
5830
|
+
const configDir = join10(homedir4(), CONFIG_DIR_NAME);
|
|
5831
|
+
if (!existsSync15(configDir)) {
|
|
5264
5832
|
mkdirSync9(configDir, { recursive: true });
|
|
5265
5833
|
}
|
|
5266
5834
|
let trimmed = content.trim();
|
|
@@ -5276,13 +5844,13 @@ function saveDevState(content) {
|
|
|
5276
5844
|
}
|
|
5277
5845
|
function loadDevState() {
|
|
5278
5846
|
const path = getDevStatePath();
|
|
5279
|
-
if (!
|
|
5280
|
-
const content =
|
|
5847
|
+
if (!existsSync15(path)) return null;
|
|
5848
|
+
const content = readFileSync10(path, "utf-8").trim();
|
|
5281
5849
|
return content || null;
|
|
5282
5850
|
}
|
|
5283
5851
|
function clearDevState() {
|
|
5284
5852
|
const path = getDevStatePath();
|
|
5285
|
-
if (
|
|
5853
|
+
if (existsSync15(path)) {
|
|
5286
5854
|
try {
|
|
5287
5855
|
unlinkSync2(path);
|
|
5288
5856
|
} catch {
|
|
@@ -5290,96 +5858,6 @@ function clearDevState() {
|
|
|
5290
5858
|
}
|
|
5291
5859
|
}
|
|
5292
5860
|
|
|
5293
|
-
// src/tools/git-context.ts
|
|
5294
|
-
import { execSync as execSync2 } from "child_process";
|
|
5295
|
-
import { existsSync as existsSync14 } from "fs";
|
|
5296
|
-
import { join as join9 } from "path";
|
|
5297
|
-
function runGit(cmd, cwd) {
|
|
5298
|
-
try {
|
|
5299
|
-
return execSync2(`git ${cmd}`, {
|
|
5300
|
-
cwd,
|
|
5301
|
-
encoding: "utf-8",
|
|
5302
|
-
stdio: ["pipe", "pipe", "pipe"],
|
|
5303
|
-
timeout: 5e3
|
|
5304
|
-
}).trim();
|
|
5305
|
-
} catch {
|
|
5306
|
-
return null;
|
|
5307
|
-
}
|
|
5308
|
-
}
|
|
5309
|
-
function getGitRoot(cwd = process.cwd()) {
|
|
5310
|
-
return runGit("rev-parse --show-toplevel", cwd);
|
|
5311
|
-
}
|
|
5312
|
-
function getGitContext(cwd = process.cwd()) {
|
|
5313
|
-
if (!existsSync14(join9(cwd, ".git"))) {
|
|
5314
|
-
const result = runGit("rev-parse --git-dir", cwd);
|
|
5315
|
-
if (!result) return null;
|
|
5316
|
-
}
|
|
5317
|
-
const branch = runGit("rev-parse --abbrev-ref HEAD", cwd);
|
|
5318
|
-
if (!branch) return null;
|
|
5319
|
-
const statusOutput = runGit("status --porcelain", cwd) ?? "";
|
|
5320
|
-
const statusLines = statusOutput ? statusOutput.split("\n").filter(Boolean) : [];
|
|
5321
|
-
const stagedFiles = [];
|
|
5322
|
-
const changedFiles = [];
|
|
5323
|
-
for (const line of statusLines) {
|
|
5324
|
-
const xy = line.slice(0, 2);
|
|
5325
|
-
const file = line.slice(3).trim();
|
|
5326
|
-
const indexStatus = xy[0];
|
|
5327
|
-
const workStatus = xy[1];
|
|
5328
|
-
if (indexStatus && indexStatus !== " " && indexStatus !== "?") {
|
|
5329
|
-
stagedFiles.push(`${indexStatus} ${file}`);
|
|
5330
|
-
}
|
|
5331
|
-
if (workStatus && workStatus !== " ") {
|
|
5332
|
-
changedFiles.push(`${workStatus} ${file}`);
|
|
5333
|
-
}
|
|
5334
|
-
}
|
|
5335
|
-
const logOutput = runGit("log --oneline -3", cwd) ?? "";
|
|
5336
|
-
const recentCommits = logOutput ? logOutput.split("\n").filter(Boolean) : [];
|
|
5337
|
-
const unpushedOutput = runGit("log @{u}..HEAD --oneline 2>/dev/null", cwd);
|
|
5338
|
-
const hasUnpushed = unpushedOutput !== null && unpushedOutput.trim().length > 0;
|
|
5339
|
-
return {
|
|
5340
|
-
branch,
|
|
5341
|
-
changedFiles,
|
|
5342
|
-
stagedFiles,
|
|
5343
|
-
recentCommits,
|
|
5344
|
-
hasUnpushed
|
|
5345
|
-
};
|
|
5346
|
-
}
|
|
5347
|
-
function formatGitContextForPrompt(ctx) {
|
|
5348
|
-
const lines = ["# Git Repository Status", ""];
|
|
5349
|
-
lines.push(`- **Branch**: \`${ctx.branch}\``);
|
|
5350
|
-
if (ctx.stagedFiles.length > 0) {
|
|
5351
|
-
lines.push(`- **Staged** (${ctx.stagedFiles.length} files):`);
|
|
5352
|
-
for (const f of ctx.stagedFiles.slice(0, 10)) {
|
|
5353
|
-
lines.push(` - ${f}`);
|
|
5354
|
-
}
|
|
5355
|
-
if (ctx.stagedFiles.length > 10) {
|
|
5356
|
-
lines.push(` - ... and ${ctx.stagedFiles.length - 10} more`);
|
|
5357
|
-
}
|
|
5358
|
-
}
|
|
5359
|
-
if (ctx.changedFiles.length > 0) {
|
|
5360
|
-
lines.push(`- **Modified** (${ctx.changedFiles.length} files):`);
|
|
5361
|
-
for (const f of ctx.changedFiles.slice(0, 10)) {
|
|
5362
|
-
lines.push(` - ${f}`);
|
|
5363
|
-
}
|
|
5364
|
-
if (ctx.changedFiles.length > 10) {
|
|
5365
|
-
lines.push(` - ... and ${ctx.changedFiles.length - 10} more`);
|
|
5366
|
-
}
|
|
5367
|
-
}
|
|
5368
|
-
if (ctx.stagedFiles.length === 0 && ctx.changedFiles.length === 0) {
|
|
5369
|
-
lines.push("- **Working tree**: clean");
|
|
5370
|
-
}
|
|
5371
|
-
if (ctx.recentCommits.length > 0) {
|
|
5372
|
-
lines.push("- **Recent commits**:");
|
|
5373
|
-
for (const c of ctx.recentCommits) {
|
|
5374
|
-
lines.push(` - ${c}`);
|
|
5375
|
-
}
|
|
5376
|
-
}
|
|
5377
|
-
if (ctx.hasUnpushed) {
|
|
5378
|
-
lines.push("- \u26A0\uFE0F Has unpushed commits");
|
|
5379
|
-
}
|
|
5380
|
-
return lines.join("\n");
|
|
5381
|
-
}
|
|
5382
|
-
|
|
5383
5861
|
// src/mcp/client.ts
|
|
5384
5862
|
import { spawn as spawn2 } from "child_process";
|
|
5385
5863
|
var McpClient = class {
|
|
@@ -5781,6 +6259,136 @@ var McpManager = class {
|
|
|
5781
6259
|
}
|
|
5782
6260
|
};
|
|
5783
6261
|
|
|
6262
|
+
// src/skills/manager.ts
|
|
6263
|
+
import { existsSync as existsSync16, readdirSync as readdirSync7, mkdirSync as mkdirSync10 } from "fs";
|
|
6264
|
+
import { join as join11 } from "path";
|
|
6265
|
+
|
|
6266
|
+
// src/skills/types.ts
|
|
6267
|
+
import { readFileSync as readFileSync11 } from "fs";
|
|
6268
|
+
import { basename as basename4 } from "path";
|
|
6269
|
+
function parseSimpleYaml(yaml) {
|
|
6270
|
+
const result = {};
|
|
6271
|
+
for (const line of yaml.split("\n")) {
|
|
6272
|
+
const match = line.match(/^(\w+)\s*:\s*(.+)$/);
|
|
6273
|
+
if (match) {
|
|
6274
|
+
result[match[1]] = match[2].trim();
|
|
6275
|
+
}
|
|
6276
|
+
}
|
|
6277
|
+
return result;
|
|
6278
|
+
}
|
|
6279
|
+
function parseYamlArray(value) {
|
|
6280
|
+
const bracketMatch = value.match(/^\[(.+)]$/);
|
|
6281
|
+
if (bracketMatch) {
|
|
6282
|
+
return bracketMatch[1].split(",").map((s) => s.trim().replace(/^['"]|['"]$/g, ""));
|
|
6283
|
+
}
|
|
6284
|
+
return [value.trim()];
|
|
6285
|
+
}
|
|
6286
|
+
function parseSkillFile(filePath) {
|
|
6287
|
+
let raw;
|
|
6288
|
+
try {
|
|
6289
|
+
raw = readFileSync11(filePath, "utf-8");
|
|
6290
|
+
} catch {
|
|
6291
|
+
return null;
|
|
6292
|
+
}
|
|
6293
|
+
const frontmatterMatch = raw.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n([\s\S]*)$/);
|
|
6294
|
+
if (!frontmatterMatch) {
|
|
6295
|
+
return {
|
|
6296
|
+
meta: {
|
|
6297
|
+
name: basename4(filePath, ".md"),
|
|
6298
|
+
description: ""
|
|
6299
|
+
},
|
|
6300
|
+
content: raw.trim(),
|
|
6301
|
+
filePath
|
|
6302
|
+
};
|
|
6303
|
+
}
|
|
6304
|
+
const [, yaml, content] = frontmatterMatch;
|
|
6305
|
+
const parsed = parseSimpleYaml(yaml);
|
|
6306
|
+
return {
|
|
6307
|
+
meta: {
|
|
6308
|
+
name: parsed["name"] ?? basename4(filePath, ".md"),
|
|
6309
|
+
description: parsed["description"] ?? "",
|
|
6310
|
+
tools: parsed["tools"] ? parseYamlArray(parsed["tools"]) : void 0
|
|
6311
|
+
},
|
|
6312
|
+
content: content.trim(),
|
|
6313
|
+
filePath
|
|
6314
|
+
};
|
|
6315
|
+
}
|
|
6316
|
+
|
|
6317
|
+
// src/skills/manager.ts
|
|
6318
|
+
var SKILL_CONTENT_WARN_CHARS = 5e3;
|
|
6319
|
+
var SkillManager = class {
|
|
6320
|
+
skills = /* @__PURE__ */ new Map();
|
|
6321
|
+
activeSkill = null;
|
|
6322
|
+
skillsDir;
|
|
6323
|
+
constructor(skillsDir) {
|
|
6324
|
+
this.skillsDir = skillsDir;
|
|
6325
|
+
}
|
|
6326
|
+
/** 发现并加载 skillsDir 下所有 .md 文件,返回加载数量 */
|
|
6327
|
+
loadSkills() {
|
|
6328
|
+
this.skills.clear();
|
|
6329
|
+
if (!existsSync16(this.skillsDir)) {
|
|
6330
|
+
try {
|
|
6331
|
+
mkdirSync10(this.skillsDir, { recursive: true });
|
|
6332
|
+
} catch {
|
|
6333
|
+
}
|
|
6334
|
+
return 0;
|
|
6335
|
+
}
|
|
6336
|
+
let entries;
|
|
6337
|
+
try {
|
|
6338
|
+
entries = readdirSync7(this.skillsDir);
|
|
6339
|
+
} catch {
|
|
6340
|
+
return 0;
|
|
6341
|
+
}
|
|
6342
|
+
for (const entry of entries) {
|
|
6343
|
+
if (!entry.endsWith(".md")) continue;
|
|
6344
|
+
const filePath = join11(this.skillsDir, entry);
|
|
6345
|
+
const skill = parseSkillFile(filePath);
|
|
6346
|
+
if (skill) {
|
|
6347
|
+
this.skills.set(skill.meta.name, skill);
|
|
6348
|
+
if (skill.content.length > SKILL_CONTENT_WARN_CHARS) {
|
|
6349
|
+
process.stderr.write(
|
|
6350
|
+
`\u26A0 Skill '${skill.meta.name}' is ${skill.content.length} chars (>${SKILL_CONTENT_WARN_CHARS}), may consume significant context.
|
|
6351
|
+
`
|
|
6352
|
+
);
|
|
6353
|
+
}
|
|
6354
|
+
}
|
|
6355
|
+
}
|
|
6356
|
+
return this.skills.size;
|
|
6357
|
+
}
|
|
6358
|
+
/** 列出所有已加载的技能 */
|
|
6359
|
+
listSkills() {
|
|
6360
|
+
return [...this.skills.values()];
|
|
6361
|
+
}
|
|
6362
|
+
/** 按 name 激活技能。返回被激活的 Skill,未找到返回 null。 */
|
|
6363
|
+
activate(name) {
|
|
6364
|
+
const skill = this.skills.get(name);
|
|
6365
|
+
if (!skill) return null;
|
|
6366
|
+
this.activeSkill = skill;
|
|
6367
|
+
return skill;
|
|
6368
|
+
}
|
|
6369
|
+
/** 停用当前技能 */
|
|
6370
|
+
deactivate() {
|
|
6371
|
+
this.activeSkill = null;
|
|
6372
|
+
}
|
|
6373
|
+
/** 获取当前激活的技能(null 表示无激活技能) */
|
|
6374
|
+
getActive() {
|
|
6375
|
+
return this.activeSkill;
|
|
6376
|
+
}
|
|
6377
|
+
/** 获取激活技能的 prompt 内容(null 表示无激活技能) */
|
|
6378
|
+
getActivePromptContent() {
|
|
6379
|
+
return this.activeSkill?.content ?? null;
|
|
6380
|
+
}
|
|
6381
|
+
/** 获取激活技能的工具白名单 Set(null 表示无限制) */
|
|
6382
|
+
getActiveToolFilter() {
|
|
6383
|
+
if (!this.activeSkill?.meta.tools) return null;
|
|
6384
|
+
return new Set(this.activeSkill.meta.tools);
|
|
6385
|
+
}
|
|
6386
|
+
/** 获取技能目录路径 */
|
|
6387
|
+
getSkillsDir() {
|
|
6388
|
+
return this.skillsDir;
|
|
6389
|
+
}
|
|
6390
|
+
};
|
|
6391
|
+
|
|
5784
6392
|
// src/repl/repl.ts
|
|
5785
6393
|
var IMAGE_MIME = {
|
|
5786
6394
|
".png": "image/png",
|
|
@@ -5800,12 +6408,12 @@ function parseAtReferences(input2, cwd) {
|
|
|
5800
6408
|
const absPath = resolve4(cwd, rawPath);
|
|
5801
6409
|
const ext = extname3(rawPath).toLowerCase();
|
|
5802
6410
|
const mime = IMAGE_MIME[ext];
|
|
5803
|
-
if (!
|
|
6411
|
+
if (!existsSync17(absPath)) {
|
|
5804
6412
|
refs.push({ path: rawPath, type: "notfound" });
|
|
5805
6413
|
continue;
|
|
5806
6414
|
}
|
|
5807
6415
|
if (mime) {
|
|
5808
|
-
const data =
|
|
6416
|
+
const data = readFileSync12(absPath).toString("base64");
|
|
5809
6417
|
imageParts.push({
|
|
5810
6418
|
type: "image_url",
|
|
5811
6419
|
image_url: { url: `data:${mime};base64,${data}` }
|
|
@@ -5813,7 +6421,7 @@ function parseAtReferences(input2, cwd) {
|
|
|
5813
6421
|
refs.push({ path: rawPath, type: "image" });
|
|
5814
6422
|
textBody = textBody.replace(match[0], "").trim();
|
|
5815
6423
|
} else {
|
|
5816
|
-
const content =
|
|
6424
|
+
const content = readFileSync12(absPath, "utf-8");
|
|
5817
6425
|
const inlined = `
|
|
5818
6426
|
|
|
5819
6427
|
[File: ${rawPath}]
|
|
@@ -5883,6 +6491,8 @@ var Repl = class {
|
|
|
5883
6491
|
* true 时:工具集过滤为只读工具,system prompt 注入规划说明,提示符加 [PLAN] 标记。
|
|
5884
6492
|
*/
|
|
5885
6493
|
planMode = false;
|
|
6494
|
+
/** 技能管理器 */
|
|
6495
|
+
skillManager = null;
|
|
5886
6496
|
/**
|
|
5887
6497
|
* 交互式列表选择器进行中标志。
|
|
5888
6498
|
* 与 toolExecutor.confirming 类似:主循环 line handler 在此为 true 时忽略 line 事件,
|
|
@@ -5895,9 +6505,9 @@ var Repl = class {
|
|
|
5895
6505
|
*/
|
|
5896
6506
|
findContextFile(dir, candidates = CONTEXT_FILE_CANDIDATES) {
|
|
5897
6507
|
for (const candidate of candidates) {
|
|
5898
|
-
const fullPath =
|
|
5899
|
-
if (
|
|
5900
|
-
const content =
|
|
6508
|
+
const fullPath = join12(dir, candidate);
|
|
6509
|
+
if (existsSync17(fullPath)) {
|
|
6510
|
+
const content = readFileSync12(fullPath, "utf-8").trim();
|
|
5901
6511
|
if (content) return { filePath: fullPath, content };
|
|
5902
6512
|
}
|
|
5903
6513
|
}
|
|
@@ -5921,9 +6531,9 @@ var Repl = class {
|
|
|
5921
6531
|
if (setting === false) return { layers: [], mergedContent: "" };
|
|
5922
6532
|
const cwd = process.cwd();
|
|
5923
6533
|
if (setting !== "auto") {
|
|
5924
|
-
const fullPath =
|
|
5925
|
-
if (
|
|
5926
|
-
const content =
|
|
6534
|
+
const fullPath = join12(cwd, setting);
|
|
6535
|
+
if (existsSync17(fullPath)) {
|
|
6536
|
+
const content = readFileSync12(fullPath, "utf-8").trim();
|
|
5927
6537
|
if (content) {
|
|
5928
6538
|
const layer = {
|
|
5929
6539
|
level: "project",
|
|
@@ -5980,9 +6590,9 @@ var Repl = class {
|
|
|
5980
6590
|
* 超过 MEMORY_MAX_CHARS 时只取末尾最新部分。
|
|
5981
6591
|
*/
|
|
5982
6592
|
loadMemoryContent() {
|
|
5983
|
-
const memoryPath =
|
|
5984
|
-
if (!
|
|
5985
|
-
let content =
|
|
6593
|
+
const memoryPath = join12(this.config.getConfigDir(), MEMORY_FILE_NAME);
|
|
6594
|
+
if (!existsSync17(memoryPath)) return null;
|
|
6595
|
+
let content = readFileSync12(memoryPath, "utf-8").trim();
|
|
5986
6596
|
if (!content) return null;
|
|
5987
6597
|
if (content.length > MEMORY_MAX_CHARS) {
|
|
5988
6598
|
content = content.slice(-MEMORY_MAX_CHARS);
|
|
@@ -6036,6 +6646,13 @@ ${memory.content}`);
|
|
|
6036
6646
|
if (this.activeSystemPrompt) {
|
|
6037
6647
|
parts.push(this.activeSystemPrompt);
|
|
6038
6648
|
}
|
|
6649
|
+
const skillContent = this.skillManager?.getActivePromptContent();
|
|
6650
|
+
if (skillContent) {
|
|
6651
|
+
const skillName = this.skillManager.getActive().meta.name;
|
|
6652
|
+
parts.push(`# Active Skill: ${skillName}
|
|
6653
|
+
|
|
6654
|
+
${skillContent}`);
|
|
6655
|
+
}
|
|
6039
6656
|
if (this.planMode) {
|
|
6040
6657
|
parts.push(PLAN_MODE_SYSTEM_ADDON);
|
|
6041
6658
|
}
|
|
@@ -6178,7 +6795,10 @@ ${response.content.trim()}
|
|
|
6178
6795
|
}
|
|
6179
6796
|
}
|
|
6180
6797
|
refreshPrompt() {
|
|
6181
|
-
const
|
|
6798
|
+
const activeSkill = this.skillManager?.getActive();
|
|
6799
|
+
const skillTag = activeSkill ? chalk10.magenta(`[${activeSkill.meta.name}]`) : "";
|
|
6800
|
+
const planTag = this.planMode ? chalk10.yellow("[PLAN]") : "";
|
|
6801
|
+
const promptStr = chalk10.green(`[${this.currentProvider}]`) + skillTag + planTag + chalk10.white(" > ");
|
|
6182
6802
|
this.rl.setPrompt(promptStr);
|
|
6183
6803
|
}
|
|
6184
6804
|
showPrompt() {
|
|
@@ -6246,6 +6866,13 @@ ${response.content.trim()}
|
|
|
6246
6866
|
}
|
|
6247
6867
|
if (pluginCount > 0) {
|
|
6248
6868
|
process.stdout.write(chalk10.dim(` \u{1F50C} Plugins loaded: ${pluginCount} tool(s) from plugins/
|
|
6869
|
+
`));
|
|
6870
|
+
}
|
|
6871
|
+
const skillsDir = join12(this.config.getConfigDir(), SKILLS_DIR_NAME);
|
|
6872
|
+
this.skillManager = new SkillManager(skillsDir);
|
|
6873
|
+
const skillCount = this.skillManager.loadSkills();
|
|
6874
|
+
if (skillCount > 0) {
|
|
6875
|
+
process.stdout.write(chalk10.dim(` \u{1F3AF} Skills: ${skillCount} available (use /skill to manage)
|
|
6249
6876
|
`));
|
|
6250
6877
|
}
|
|
6251
6878
|
const mcpServers = this.config.get("mcpServers");
|
|
@@ -6474,7 +7101,17 @@ ${response.content.trim()}
|
|
|
6474
7101
|
}
|
|
6475
7102
|
async handleChatWithTools(provider, messages) {
|
|
6476
7103
|
const session = this.sessions.current;
|
|
6477
|
-
|
|
7104
|
+
let toolDefs;
|
|
7105
|
+
if (this.planMode) {
|
|
7106
|
+
toolDefs = this.toolRegistry.getDefinitions().filter((t) => PLAN_MODE_READONLY_TOOLS.has(t.name));
|
|
7107
|
+
} else {
|
|
7108
|
+
const skillFilter = this.skillManager?.getActiveToolFilter();
|
|
7109
|
+
if (skillFilter) {
|
|
7110
|
+
toolDefs = this.toolRegistry.getDefinitions().filter((t) => skillFilter.has(t.name));
|
|
7111
|
+
} else {
|
|
7112
|
+
toolDefs = this.toolRegistry.getDefinitions();
|
|
7113
|
+
}
|
|
7114
|
+
}
|
|
6478
7115
|
const apiMessages = [...messages];
|
|
6479
7116
|
const extraMessages = [];
|
|
6480
7117
|
const systemPrompt = this.buildCurrentSystemPrompt();
|
|
@@ -6666,6 +7303,7 @@ ${response.content.trim()}
|
|
|
6666
7303
|
this.sessionTokenUsage = { inputTokens: 0, outputTokens: 0 };
|
|
6667
7304
|
},
|
|
6668
7305
|
getGitBranch: () => this.gitBranch,
|
|
7306
|
+
getLastResponse: () => lastResponseStore.content,
|
|
6669
7307
|
runSetupWizard: async () => {
|
|
6670
7308
|
const rlAny = this.rl;
|
|
6671
7309
|
rlAny.output = process.stdout;
|
|
@@ -6687,6 +7325,22 @@ ${response.content.trim()}
|
|
|
6687
7325
|
this.refreshPrompt();
|
|
6688
7326
|
},
|
|
6689
7327
|
getMcpManager: () => this.mcpManager,
|
|
7328
|
+
chatOnce: async (prompt, options) => {
|
|
7329
|
+
const provider = this.providers.get(this.currentProvider);
|
|
7330
|
+
const modelParams = this.getModelParams();
|
|
7331
|
+
const response = await provider.chat({
|
|
7332
|
+
messages: [{ role: "user", content: prompt, timestamp: /* @__PURE__ */ new Date() }],
|
|
7333
|
+
model: this.currentModel,
|
|
7334
|
+
systemPrompt: this.buildCurrentSystemPrompt(),
|
|
7335
|
+
stream: false,
|
|
7336
|
+
temperature: options?.temperature ?? 0.3,
|
|
7337
|
+
maxTokens: options?.maxTokens ?? modelParams.maxTokens ?? 4096,
|
|
7338
|
+
timeout: modelParams.timeout ?? 6e4
|
|
7339
|
+
});
|
|
7340
|
+
return response.content;
|
|
7341
|
+
},
|
|
7342
|
+
refreshPrompt: () => this.refreshPrompt(),
|
|
7343
|
+
getSkillManager: () => this.skillManager ?? null,
|
|
6690
7344
|
exit: () => this.handleExit()
|
|
6691
7345
|
};
|
|
6692
7346
|
await cmd.execute(args, ctx);
|