zhitalk 0.0.5 → 0.0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dist/agent/agent.js +86 -63
- package/dist/agent/cli.js +39 -73
- package/dist/agent/colors.js +6 -44
- package/dist/agent/commands.js +22 -28
- package/dist/agent/config.js +20 -62
- package/dist/agent/context.js +14 -12
- package/dist/agent/db.js +17 -27
- package/dist/agent/hooks.d.ts +1 -1
- package/dist/agent/hooks.js +13 -22
- package/dist/agent/mcp/client.js +25 -29
- package/dist/agent/mcp/index.d.ts +1 -1
- package/dist/agent/mcp/index.js +8 -13
- package/dist/agent/mcp/wrapper.d.ts +2 -2
- package/dist/agent/mcp/wrapper.js +5 -8
- package/dist/agent/model.js +6 -10
- package/dist/agent/permission/exec.js +8 -11
- package/dist/agent/permission/is-dangerous-path.js +126 -18
- package/dist/agent/permission/is-safe-domains.js +1 -4
- package/dist/agent/permission/network.js +3 -6
- package/dist/agent/permission/read.js +3 -6
- package/dist/agent/permission/util.js +16 -26
- package/dist/agent/permission/write.js +5 -8
- package/dist/agent/prompt.js +9 -15
- package/dist/agent/skills.js +19 -24
- package/dist/agent/tools/agent_tool.js +2 -38
- package/dist/agent/tools/exec_tool.js +4 -7
- package/dist/agent/tools/load_skill_tool.js +3 -6
- package/dist/agent/tools/memory_create_tool.js +4 -10
- package/dist/agent/tools/memory_delete_tool.js +4 -10
- package/dist/agent/tools/memory_retrieve_tool.js +3 -6
- package/dist/agent/tools/profile_update_tool.js +11 -14
- package/dist/agent/tools/read_file_tool.js +4 -6
- package/dist/agent/tools/run_js_tool.js +3 -6
- package/dist/agent/tools/run_py_tool.js +4 -7
- package/dist/agent/tools/search.js +1 -4
- package/dist/agent/tools/web_fetch_tool.js +1 -4
- package/dist/agent/tools/web_search_tool.js +5 -8
- package/dist/agent/tools/write_file_tool.js +8 -9
- package/dist/agent/tools.js +76 -81
- package/dist/agent/utils.d.ts +1 -0
- package/dist/agent/utils.js +13 -6
- package/dist/index.js +23 -49
- package/dist/install.js +14 -50
- package/jest.config.js +11 -0
- package/package.json +8 -7
- package/dist/agent/permission/dangerous-path.json +0 -115
|
@@ -1,34 +1,31 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
exports.checkExecPermission = checkExecPermission;
|
|
4
|
-
const util_1 = require("./util");
|
|
5
|
-
function checkExecPermission(toolCall) {
|
|
1
|
+
import { isSafeCommand, isChangingDirectory, isScriptExecution, isDangerousOperation } from './util.js';
|
|
2
|
+
export function checkExecPermission(toolCall) {
|
|
6
3
|
const command = toolCall.args?.command;
|
|
7
4
|
if (typeof command !== 'string') {
|
|
8
|
-
return { action: '
|
|
5
|
+
return { action: 'allow' };
|
|
9
6
|
}
|
|
10
|
-
if (
|
|
7
|
+
if (isSafeCommand(command)) {
|
|
11
8
|
return { action: 'allow' };
|
|
12
9
|
}
|
|
13
|
-
if (
|
|
10
|
+
if (isChangingDirectory(command)) {
|
|
14
11
|
return {
|
|
15
12
|
action: 'block',
|
|
16
13
|
reason: `命令包含切换目录操作,为防止目录逃逸,禁止执行。如有需要请手动操作。`,
|
|
17
14
|
};
|
|
18
15
|
}
|
|
19
|
-
const scriptCheck =
|
|
16
|
+
const scriptCheck = isScriptExecution(command);
|
|
20
17
|
if (scriptCheck.blocked) {
|
|
21
18
|
return {
|
|
22
19
|
action: 'block',
|
|
23
20
|
reason: scriptCheck.reason,
|
|
24
21
|
};
|
|
25
22
|
}
|
|
26
|
-
const dangerCheck =
|
|
23
|
+
const dangerCheck = isDangerousOperation(command);
|
|
27
24
|
if (dangerCheck.blocked) {
|
|
28
25
|
return {
|
|
29
26
|
action: 'block',
|
|
30
27
|
reason: dangerCheck.reason,
|
|
31
28
|
};
|
|
32
29
|
}
|
|
33
|
-
return { action: '
|
|
30
|
+
return { action: 'allow' };
|
|
34
31
|
}
|
|
@@ -1,24 +1,132 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import path from 'path';
|
|
2
|
+
import os from 'os';
|
|
3
|
+
const dangerousPaths = {
|
|
4
|
+
macos: [
|
|
5
|
+
'~/.ssh',
|
|
6
|
+
'~/.gnupg',
|
|
7
|
+
'~/.aws',
|
|
8
|
+
'~/.azure',
|
|
9
|
+
'~/.config/gcloud',
|
|
10
|
+
'~/.kube',
|
|
11
|
+
'~/.docker',
|
|
12
|
+
'~/.netrc',
|
|
13
|
+
'~/Library/Keychains',
|
|
14
|
+
'~/Library/Application Support/Google/Chrome',
|
|
15
|
+
'~/Library/Application Support/Chromium',
|
|
16
|
+
'~/Library/Application Support/Firefox',
|
|
17
|
+
'~/Library/Application Support/Slack',
|
|
18
|
+
'~/Library/Application Support/Discord',
|
|
19
|
+
'~/Library/Application Support/Telegram',
|
|
20
|
+
'~/Library/Application Support/Signal',
|
|
21
|
+
'~/.zshrc',
|
|
22
|
+
'~/.bashrc',
|
|
23
|
+
'~/.bash_profile',
|
|
24
|
+
'~/.profile',
|
|
25
|
+
'~/.env',
|
|
26
|
+
'~/.env.*',
|
|
27
|
+
'~/.gitconfig',
|
|
28
|
+
'~/.git-credentials',
|
|
29
|
+
'~/.npmrc',
|
|
30
|
+
'~/.pnpmrc',
|
|
31
|
+
'~/.yarnrc',
|
|
32
|
+
'~/.pypirc',
|
|
33
|
+
'~/.m2',
|
|
34
|
+
'~/.gradle',
|
|
35
|
+
'~/.terraform',
|
|
36
|
+
'~/.config/gh',
|
|
37
|
+
'~/.config/gitlab',
|
|
38
|
+
'~/.circleci',
|
|
39
|
+
'~/.vercel',
|
|
40
|
+
'~/.netlify',
|
|
41
|
+
'~/Library/Containers',
|
|
42
|
+
'/private',
|
|
43
|
+
'/etc',
|
|
44
|
+
'/var',
|
|
45
|
+
],
|
|
46
|
+
linux: [
|
|
47
|
+
'~/.ssh',
|
|
48
|
+
'~/.gnupg',
|
|
49
|
+
'~/.pki',
|
|
50
|
+
'~/.aws',
|
|
51
|
+
'~/.azure',
|
|
52
|
+
'~/.config/gcloud',
|
|
53
|
+
'~/.kube',
|
|
54
|
+
'~/.docker',
|
|
55
|
+
'~/.netrc',
|
|
56
|
+
'~/.local/share/keyrings',
|
|
57
|
+
'~/.config/google-chrome',
|
|
58
|
+
'~/.config/chromium',
|
|
59
|
+
'~/.mozilla/firefox',
|
|
60
|
+
'~/.config/Slack',
|
|
61
|
+
'~/.config/discord',
|
|
62
|
+
'~/.local/share/TelegramDesktop',
|
|
63
|
+
'~/.zshrc',
|
|
64
|
+
'~/.bashrc',
|
|
65
|
+
'~/.bash_profile',
|
|
66
|
+
'~/.profile',
|
|
67
|
+
'~/.env',
|
|
68
|
+
'~/.env.*',
|
|
69
|
+
'~/.gitconfig',
|
|
70
|
+
'~/.git-credentials',
|
|
71
|
+
'~/.npmrc',
|
|
72
|
+
'~/.pnpmrc',
|
|
73
|
+
'~/.yarnrc',
|
|
74
|
+
'~/.pypirc',
|
|
75
|
+
'~/.m2',
|
|
76
|
+
'~/.gradle',
|
|
77
|
+
'~/.terraform',
|
|
78
|
+
'~/.config/gh',
|
|
79
|
+
'~/.config/gitlab',
|
|
80
|
+
'~/.circleci',
|
|
81
|
+
'~/.vercel',
|
|
82
|
+
'~/.netlify',
|
|
83
|
+
'/etc/shadow',
|
|
84
|
+
'/etc/sudoers',
|
|
85
|
+
'/proc',
|
|
86
|
+
'/sys',
|
|
87
|
+
'/var/lib',
|
|
88
|
+
'/root',
|
|
89
|
+
],
|
|
90
|
+
windows: [
|
|
91
|
+
'%USERPROFILE%\\.ssh',
|
|
92
|
+
'%USERPROFILE%\\.gnupg',
|
|
93
|
+
'%USERPROFILE%\\.aws',
|
|
94
|
+
'%USERPROFILE%\\.azure',
|
|
95
|
+
'%USERPROFILE%\\.kube',
|
|
96
|
+
'%USERPROFILE%\\.docker',
|
|
97
|
+
'%LOCALAPPDATA%\\Google\\Chrome\\User Data',
|
|
98
|
+
'%LOCALAPPDATA%\\Chromium\\User Data',
|
|
99
|
+
'%APPDATA%\\Mozilla\\Firefox',
|
|
100
|
+
'%APPDATA%\\Slack',
|
|
101
|
+
'%APPDATA%\\Discord',
|
|
102
|
+
'%APPDATA%\\Telegram Desktop',
|
|
103
|
+
'%USERPROFILE%\\.gitconfig',
|
|
104
|
+
'%USERPROFILE%\\.git-credentials',
|
|
105
|
+
'%USERPROFILE%\\.npmrc',
|
|
106
|
+
'%USERPROFILE%\\.pnpmrc',
|
|
107
|
+
'%USERPROFILE%\\.yarnrc',
|
|
108
|
+
'%USERPROFILE%\\.m2',
|
|
109
|
+
'%USERPROFILE%\\.gradle',
|
|
110
|
+
'%APPDATA%',
|
|
111
|
+
'%LOCALAPPDATA%',
|
|
112
|
+
'C:\\Windows\\System32',
|
|
113
|
+
'C:\\Windows\\System32\\config\\SAM',
|
|
114
|
+
'C:\\Windows\\System32\\config\\SECURITY',
|
|
115
|
+
'C:\\Windows\\System32\\config\\SYSTEM',
|
|
116
|
+
],
|
|
4
117
|
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.isDangerousPath = isDangerousPath;
|
|
7
|
-
const path_1 = __importDefault(require("path"));
|
|
8
|
-
const os_1 = __importDefault(require("os"));
|
|
9
|
-
const dangerous_path_json_1 = __importDefault(require("./dangerous-path.json"));
|
|
10
118
|
function expandPath(filepath) {
|
|
11
119
|
let expanded = filepath;
|
|
12
120
|
if (expanded.startsWith('~')) {
|
|
13
|
-
expanded =
|
|
121
|
+
expanded = path.join(os.homedir(), expanded.slice(1));
|
|
14
122
|
}
|
|
15
|
-
expanded = expanded.replace(/%USERPROFILE%/gi,
|
|
16
|
-
expanded = expanded.replace(/%APPDATA%/gi, process.env.APPDATA ||
|
|
17
|
-
expanded = expanded.replace(/%LOCALAPPDATA%/gi, process.env.LOCALAPPDATA ||
|
|
18
|
-
if (!
|
|
19
|
-
expanded =
|
|
123
|
+
expanded = expanded.replace(/%USERPROFILE%/gi, os.homedir());
|
|
124
|
+
expanded = expanded.replace(/%APPDATA%/gi, process.env.APPDATA || path.join(os.homedir(), 'AppData', 'Roaming'));
|
|
125
|
+
expanded = expanded.replace(/%LOCALAPPDATA%/gi, process.env.LOCALAPPDATA || path.join(os.homedir(), 'AppData', 'Local'));
|
|
126
|
+
if (!path.isAbsolute(expanded)) {
|
|
127
|
+
expanded = path.resolve(expanded);
|
|
20
128
|
}
|
|
21
|
-
expanded =
|
|
129
|
+
expanded = path.normalize(expanded);
|
|
22
130
|
return expanded;
|
|
23
131
|
}
|
|
24
132
|
function globToRegex(pattern) {
|
|
@@ -27,13 +135,13 @@ function globToRegex(pattern) {
|
|
|
27
135
|
.replace(/\*/g, '[^\\\\/]*');
|
|
28
136
|
return new RegExp(`^${escaped}(?:[\\\\/].*)?$`);
|
|
29
137
|
}
|
|
30
|
-
function isDangerousPath(filepath) {
|
|
138
|
+
export function isDangerousPath(filepath) {
|
|
31
139
|
const platform = process.platform === 'win32'
|
|
32
140
|
? 'windows'
|
|
33
141
|
: process.platform === 'darwin'
|
|
34
142
|
? 'macos'
|
|
35
143
|
: 'linux';
|
|
36
|
-
const dangerousList =
|
|
144
|
+
const dangerousList = dangerousPaths[platform];
|
|
37
145
|
const expandedInput = expandPath(filepath);
|
|
38
146
|
for (const dangerous of dangerousList) {
|
|
39
147
|
const expandedDangerous = expandPath(dangerous);
|
|
@@ -45,7 +153,7 @@ function isDangerousPath(filepath) {
|
|
|
45
153
|
else {
|
|
46
154
|
if (expandedInput === expandedDangerous)
|
|
47
155
|
return true;
|
|
48
|
-
if (expandedInput.startsWith(expandedDangerous +
|
|
156
|
+
if (expandedInput.startsWith(expandedDangerous + path.sep))
|
|
49
157
|
return true;
|
|
50
158
|
}
|
|
51
159
|
}
|
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.isSafeDomain = isSafeDomain;
|
|
4
1
|
const commonDomainsCNForDevelopers = [
|
|
5
2
|
// 搜索 / AI
|
|
6
3
|
'google.com',
|
|
@@ -127,7 +124,7 @@ const commonDomainsCNForDevelopers = [
|
|
|
127
124
|
'tavily.com',
|
|
128
125
|
'smith.langchain.com',
|
|
129
126
|
];
|
|
130
|
-
function isSafeDomain(url) {
|
|
127
|
+
export function isSafeDomain(url) {
|
|
131
128
|
let hostname;
|
|
132
129
|
try {
|
|
133
130
|
hostname = new URL(url).hostname.toLowerCase();
|
|
@@ -1,13 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
exports.checkNetworkPermission = checkNetworkPermission;
|
|
4
|
-
const is_safe_domains_1 = require("./is-safe-domains");
|
|
5
|
-
function checkNetworkPermission(toolCall) {
|
|
1
|
+
import { isSafeDomain } from './is-safe-domains.js';
|
|
2
|
+
export function checkNetworkPermission(toolCall) {
|
|
6
3
|
const url = toolCall.args?.url;
|
|
7
4
|
if (typeof url !== 'string') {
|
|
8
5
|
return { action: 'allow' };
|
|
9
6
|
}
|
|
10
|
-
if (
|
|
7
|
+
if (isSafeDomain(url)) {
|
|
11
8
|
return { action: 'allow' };
|
|
12
9
|
}
|
|
13
10
|
return { action: 'confirm' };
|
|
@@ -1,13 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
exports.checkReadPermission = checkReadPermission;
|
|
4
|
-
const is_dangerous_path_1 = require("./is-dangerous-path");
|
|
5
|
-
function checkReadPermission(toolCall) {
|
|
1
|
+
import { isDangerousPath } from './is-dangerous-path.js';
|
|
2
|
+
export function checkReadPermission(toolCall) {
|
|
6
3
|
const filepath = toolCall.args?.filepath;
|
|
7
4
|
if (filepath == null) {
|
|
8
5
|
return { action: 'allow' };
|
|
9
6
|
}
|
|
10
|
-
if (
|
|
7
|
+
if (isDangerousPath(filepath)) {
|
|
11
8
|
return {
|
|
12
9
|
action: 'block',
|
|
13
10
|
reason: `路径 "${filepath}" 是系统敏感目录,为保护系统安全,禁止访问。如有需要请手动操作。`,
|
|
@@ -1,25 +1,15 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
exports.isChangingDirectory = isChangingDirectory;
|
|
8
|
-
exports.isScriptExecution = isScriptExecution;
|
|
9
|
-
exports.isSafeCommand = isSafeCommand;
|
|
10
|
-
exports.isDangerousOperation = isDangerousOperation;
|
|
11
|
-
const path_1 = __importDefault(require("path"));
|
|
12
|
-
const config_1 = require("../config");
|
|
13
|
-
function isInProjectDir(filepath) {
|
|
14
|
-
const resolved = path_1.default.resolve(filepath);
|
|
15
|
-
const cwd = path_1.default.resolve(process.cwd());
|
|
16
|
-
if (resolved === cwd || resolved.startsWith(cwd + path_1.default.sep)) {
|
|
1
|
+
import path from 'path';
|
|
2
|
+
import { WORKSPACE_DIR } from '../config.js';
|
|
3
|
+
export function isInProjectDir(filepath) {
|
|
4
|
+
const resolved = path.resolve(filepath);
|
|
5
|
+
const cwd = path.resolve(process.cwd());
|
|
6
|
+
if (resolved === cwd || resolved.startsWith(cwd + path.sep)) {
|
|
17
7
|
return true;
|
|
18
8
|
}
|
|
19
|
-
const zhitalkDir =
|
|
20
|
-
return resolved === zhitalkDir || resolved.startsWith(zhitalkDir +
|
|
9
|
+
const zhitalkDir = WORKSPACE_DIR;
|
|
10
|
+
return resolved === zhitalkDir || resolved.startsWith(zhitalkDir + path.sep);
|
|
21
11
|
}
|
|
22
|
-
function isChangingDirectory(command) {
|
|
12
|
+
export function isChangingDirectory(command) {
|
|
23
13
|
return /(?:^|[\s;|&])(?:cd|chdir|pushd|popd)(?:\s|$)/i.test(command);
|
|
24
14
|
}
|
|
25
15
|
const PYTHON_TOOLS = ['python', 'python3', 'python2'];
|
|
@@ -37,12 +27,12 @@ function getFirstToken(command) {
|
|
|
37
27
|
const match = trimmed.match(/^([^\s]+)/);
|
|
38
28
|
return match ? match[1] : trimmed;
|
|
39
29
|
}
|
|
40
|
-
function isScriptExecution(command) {
|
|
30
|
+
export function isScriptExecution(command) {
|
|
41
31
|
const subCommands = command.split(/[;|&]+/).filter(Boolean);
|
|
42
32
|
for (const sub of subCommands) {
|
|
43
33
|
const token = getFirstToken(sub);
|
|
44
|
-
const tool =
|
|
45
|
-
const ext =
|
|
34
|
+
const tool = path.basename(token).toLowerCase();
|
|
35
|
+
const ext = path.extname(token).toLowerCase();
|
|
46
36
|
if (PYTHON_TOOLS.includes(tool) || ext === '.py') {
|
|
47
37
|
return { blocked: true, reason: '检测到 Python 脚本执行,请使用 run_py tool 执行 Python 代码。' };
|
|
48
38
|
}
|
|
@@ -118,13 +108,13 @@ function getSecondToken(command) {
|
|
|
118
108
|
const tokens = command.trim().split(/\s+/);
|
|
119
109
|
return tokens[1] || null;
|
|
120
110
|
}
|
|
121
|
-
function isSafeCommand(command) {
|
|
111
|
+
export function isSafeCommand(command) {
|
|
122
112
|
if (hasOutputRedirect(command))
|
|
123
113
|
return false;
|
|
124
114
|
const subCommands = command.split(/[;|&]+/).filter(Boolean);
|
|
125
115
|
for (const sub of subCommands) {
|
|
126
116
|
const token = getFirstToken(sub);
|
|
127
|
-
const tool =
|
|
117
|
+
const tool = path.basename(token).toLowerCase();
|
|
128
118
|
if (SAFE_COMMANDS.includes(tool)) {
|
|
129
119
|
continue;
|
|
130
120
|
}
|
|
@@ -138,7 +128,7 @@ function isSafeCommand(command) {
|
|
|
138
128
|
}
|
|
139
129
|
return true;
|
|
140
130
|
}
|
|
141
|
-
function isDangerousOperation(command) {
|
|
131
|
+
export function isDangerousOperation(command) {
|
|
142
132
|
const subCommand = extractShellSubCommand(command);
|
|
143
133
|
if (subCommand) {
|
|
144
134
|
const result = isDangerousOperation(subCommand);
|
|
@@ -148,7 +138,7 @@ function isDangerousOperation(command) {
|
|
|
148
138
|
const subCommands = command.split(/[;|&]+/).filter(Boolean);
|
|
149
139
|
for (const sub of subCommands) {
|
|
150
140
|
const token = getFirstToken(sub);
|
|
151
|
-
const tool =
|
|
141
|
+
const tool = path.basename(token).toLowerCase();
|
|
152
142
|
if (DANGEROUS_COMMANDS.includes(tool)) {
|
|
153
143
|
return { blocked: true, reason: `检测到危险操作 "${tool}",exec tool 禁止执行该命令。如有需要请手动操作。` };
|
|
154
144
|
}
|
|
@@ -1,20 +1,17 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
const is_dangerous_path_1 = require("./is-dangerous-path");
|
|
5
|
-
const util_1 = require("./util");
|
|
6
|
-
function checkWritePermission(toolCall) {
|
|
1
|
+
import { isDangerousPath } from './is-dangerous-path.js';
|
|
2
|
+
import { isInProjectDir } from './util.js';
|
|
3
|
+
export function checkWritePermission(toolCall) {
|
|
7
4
|
const filepath = toolCall.args?.filepath;
|
|
8
5
|
if (filepath == null) {
|
|
9
6
|
return { action: 'allow' };
|
|
10
7
|
}
|
|
11
|
-
if (
|
|
8
|
+
if (isDangerousPath(filepath)) {
|
|
12
9
|
return {
|
|
13
10
|
action: 'block',
|
|
14
11
|
reason: `路径 "${filepath}" 是系统敏感目录,为保护系统安全,禁止访问。如有需要请手动操作。`,
|
|
15
12
|
};
|
|
16
13
|
}
|
|
17
|
-
if (
|
|
14
|
+
if (isInProjectDir(filepath)) {
|
|
18
15
|
return { action: 'allow' };
|
|
19
16
|
}
|
|
20
17
|
return { action: 'confirm' };
|
package/dist/agent/prompt.js
CHANGED
|
@@ -1,21 +1,15 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.systemPrompt = void 0;
|
|
7
|
-
const fs_1 = __importDefault(require("fs"));
|
|
8
|
-
const path_1 = __importDefault(require("path"));
|
|
9
|
-
const skills_1 = require("./skills");
|
|
10
|
-
const config_1 = require("./config");
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { discoverSkills, getSkillsListText } from './skills.js';
|
|
4
|
+
import { WORKSPACE_DIR } from './config.js';
|
|
11
5
|
// ── Skills ────────────────────────────────────────────────
|
|
12
|
-
|
|
13
|
-
const skillsText =
|
|
6
|
+
discoverSkills();
|
|
7
|
+
const skillsText = getSkillsListText();
|
|
14
8
|
const basePrompt = 'You are zhitalk, 中文名字叫智语, a personal AI Agent like OpenClaw, including tools, skills, memory, hook, sub-agent, MCP server, etc. Run in the terminal.';
|
|
15
9
|
function readProfile() {
|
|
16
10
|
try {
|
|
17
|
-
const filePath =
|
|
18
|
-
const content =
|
|
11
|
+
const filePath = path.join(WORKSPACE_DIR, '.data', 'profile.md');
|
|
12
|
+
const content = fs.readFileSync(filePath, 'utf-8').trim();
|
|
19
13
|
return `<profile_info>${content}</profile_info>`;
|
|
20
14
|
}
|
|
21
15
|
catch {
|
|
@@ -45,6 +39,6 @@ const memoryPrompt = `## Memory Management Rules
|
|
|
45
39
|
const dateTimePrompt = `## Current DateTime
|
|
46
40
|
|
|
47
41
|
${new Date().toLocaleString('zh-CN', { timeZone: 'Asia/Shanghai' })}`;
|
|
48
|
-
|
|
42
|
+
export const systemPrompt = skillsText
|
|
49
43
|
? `${basePrompt}\n\n${profilePrompt}\n\n${memoryPrompt}\n\n## Available Skills\n\nYou have access to the following skills. When a user's request matches a skill's description, you MUST call the \`load_skill\` tool to load that skill's full instructions, then follow them.\n\n${skillsText}\n\n${dateTimePrompt}`
|
|
50
44
|
: `${basePrompt}\n\n${profilePrompt}\n\n${memoryPrompt}\n\n${dateTimePrompt}`;
|
package/dist/agent/skills.js
CHANGED
|
@@ -1,12 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
exports.getSkillsListText = getSkillsListText;
|
|
6
|
-
const fs_1 = require("fs");
|
|
7
|
-
const path_1 = require("path");
|
|
8
|
-
const os_1 = require("os");
|
|
9
|
-
const config_1 = require("./config");
|
|
1
|
+
import { readdirSync, readFileSync, statSync } from 'fs';
|
|
2
|
+
import { join } from 'path';
|
|
3
|
+
import { homedir } from 'os';
|
|
4
|
+
import { WORKSPACE_DIR } from './config.js';
|
|
10
5
|
const skills = [];
|
|
11
6
|
const skillContentMap = new Map();
|
|
12
7
|
function parseFrontmatter(content) {
|
|
@@ -30,30 +25,30 @@ function parseFrontmatter(content) {
|
|
|
30
25
|
}
|
|
31
26
|
return { name: result.name || '', description: result.description || '' };
|
|
32
27
|
}
|
|
33
|
-
function discoverSkills() {
|
|
28
|
+
export function discoverSkills() {
|
|
34
29
|
skills.length = 0;
|
|
35
30
|
skillContentMap.clear();
|
|
36
31
|
const skillDirs = [
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
32
|
+
join(homedir(), '.agents', 'skills'),
|
|
33
|
+
join(WORKSPACE_DIR, '.agents', 'skills'),
|
|
34
|
+
join(WORKSPACE_DIR, 'skills'),
|
|
40
35
|
];
|
|
41
36
|
for (const skillsDir of skillDirs) {
|
|
42
37
|
let entries;
|
|
43
38
|
try {
|
|
44
|
-
entries =
|
|
39
|
+
entries = readdirSync(skillsDir);
|
|
45
40
|
}
|
|
46
41
|
catch {
|
|
47
42
|
continue;
|
|
48
43
|
}
|
|
49
44
|
for (const entry of entries) {
|
|
50
|
-
const entryPath =
|
|
45
|
+
const entryPath = join(skillsDir, entry);
|
|
51
46
|
try {
|
|
52
|
-
const entryStat =
|
|
47
|
+
const entryStat = statSync(entryPath);
|
|
53
48
|
if (!entryStat.isDirectory())
|
|
54
49
|
continue;
|
|
55
|
-
const skillMdPath =
|
|
56
|
-
const content =
|
|
50
|
+
const skillMdPath = join(entryPath, 'SKILL.md');
|
|
51
|
+
const content = readFileSync(skillMdPath, 'utf-8');
|
|
57
52
|
const { name, description } = parseFrontmatter(content);
|
|
58
53
|
if (name && description) {
|
|
59
54
|
const existing = skills.find((s) => s.name === name);
|
|
@@ -74,18 +69,18 @@ function discoverSkills() {
|
|
|
74
69
|
}
|
|
75
70
|
return skills;
|
|
76
71
|
}
|
|
77
|
-
function loadSkill(name) {
|
|
72
|
+
export function loadSkill(name) {
|
|
78
73
|
return skillContentMap.get(name) ?? null;
|
|
79
74
|
}
|
|
80
|
-
function getSkillsListText() {
|
|
75
|
+
export function getSkillsListText() {
|
|
81
76
|
const lines = skills.map((s) => `- **${s.name}**: ${s.description}`);
|
|
82
77
|
if (skills.length > 0) {
|
|
83
78
|
lines.push('');
|
|
84
79
|
lines.push('skills 的目录:');
|
|
85
|
-
lines.push(
|
|
86
|
-
lines.push(
|
|
87
|
-
lines.push(
|
|
88
|
-
lines.push(`如果增加新 skill,必须放在 ${
|
|
80
|
+
lines.push(join(homedir(), '.agents', 'skills'));
|
|
81
|
+
lines.push(join(WORKSPACE_DIR, '.agents', 'skills'));
|
|
82
|
+
lines.push(join(WORKSPACE_DIR, 'skills'));
|
|
83
|
+
lines.push(`如果增加新 skill,必须放在 ${join(WORKSPACE_DIR, 'skills')} 目录下`);
|
|
89
84
|
}
|
|
90
85
|
return lines.join('\n');
|
|
91
86
|
}
|
|
@@ -1,44 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
-
if (k2 === undefined) k2 = k;
|
|
4
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
-
}
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
-
}) : function(o, v) {
|
|
16
|
-
o["default"] = v;
|
|
17
|
-
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
-
var ownKeys = function(o) {
|
|
20
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
-
var ar = [];
|
|
22
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
-
return ar;
|
|
24
|
-
};
|
|
25
|
-
return ownKeys(o);
|
|
26
|
-
};
|
|
27
|
-
return function (mod) {
|
|
28
|
-
if (mod && mod.__esModule) return mod;
|
|
29
|
-
var result = {};
|
|
30
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
-
__setModuleDefault(result, mod);
|
|
32
|
-
return result;
|
|
33
|
-
};
|
|
34
|
-
})();
|
|
35
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
-
exports.agentTool = agentTool;
|
|
37
|
-
async function agentTool({ prompt }) {
|
|
1
|
+
export async function agentTool({ prompt }) {
|
|
38
2
|
if (!prompt || !prompt.trim()) {
|
|
39
3
|
return 'Error: prompt is empty.';
|
|
40
4
|
}
|
|
41
5
|
// 动态 import 避免循环依赖:agent.ts -> tools.ts -> agent_tool.ts -> agent.ts
|
|
42
|
-
const { runSubAgent } = await
|
|
6
|
+
const { runSubAgent } = await import('../agent.js');
|
|
43
7
|
return runSubAgent(prompt.trim());
|
|
44
8
|
}
|
|
@@ -1,10 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
const util_1 = require("util");
|
|
6
|
-
const execAsync = (0, util_1.promisify)(child_process_1.exec);
|
|
7
|
-
async function execTool({ command }) {
|
|
1
|
+
import { exec } from 'child_process';
|
|
2
|
+
import { promisify } from 'util';
|
|
3
|
+
const execAsync = promisify(exec);
|
|
4
|
+
export async function execTool({ command }) {
|
|
8
5
|
const trimmed = command.trim();
|
|
9
6
|
if (!trimmed) {
|
|
10
7
|
return 'Error: command is empty.';
|
|
@@ -1,13 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
exports.loadSkillTool = loadSkillTool;
|
|
4
|
-
const skills_1 = require("../skills");
|
|
5
|
-
async function loadSkillTool({ name }) {
|
|
1
|
+
import { loadSkill } from '../skills.js';
|
|
2
|
+
export async function loadSkillTool({ name }) {
|
|
6
3
|
const trimmed = name.trim();
|
|
7
4
|
if (!trimmed) {
|
|
8
5
|
return 'Error: skill name is empty.';
|
|
9
6
|
}
|
|
10
|
-
const content =
|
|
7
|
+
const content = loadSkill(trimmed);
|
|
11
8
|
if (!content) {
|
|
12
9
|
return `Error: skill "${trimmed}" not found.`;
|
|
13
10
|
}
|
|
@@ -1,13 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.memoryCreateTool = memoryCreateTool;
|
|
7
|
-
const better_sqlite3_1 = __importDefault(require("better-sqlite3"));
|
|
8
|
-
const db_1 = require("../db");
|
|
1
|
+
import Database from 'better-sqlite3';
|
|
2
|
+
import { DB_PATH } from '../db.js';
|
|
9
3
|
const VALID_TYPES = ['fact', 'event', 'preference', 'skill'];
|
|
10
|
-
async function memoryCreateTool({ type, content, keywords, importance, }, config) {
|
|
4
|
+
export async function memoryCreateTool({ type, content, keywords, importance, }, config) {
|
|
11
5
|
if (!VALID_TYPES.includes(type)) {
|
|
12
6
|
return `Error: type must be one of ${VALID_TYPES.join(', ')}.`;
|
|
13
7
|
}
|
|
@@ -21,7 +15,7 @@ async function memoryCreateTool({ type, content, keywords, importance, }, config
|
|
|
21
15
|
}
|
|
22
16
|
const sessionId = config?.configurable?.thread_id ?? 'default-session';
|
|
23
17
|
const keywordsJson = keywords && keywords.length > 0 ? JSON.stringify(keywords) : null;
|
|
24
|
-
const db = new
|
|
18
|
+
const db = new Database(DB_PATH);
|
|
25
19
|
try {
|
|
26
20
|
const stmt = db.prepare(`INSERT INTO memory (type, content, keywords, importance, session_id)
|
|
27
21
|
VALUES (?, ?, ?, ?, ?)`);
|
|
@@ -1,16 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.memoryDeleteTool = memoryDeleteTool;
|
|
7
|
-
const better_sqlite3_1 = __importDefault(require("better-sqlite3"));
|
|
8
|
-
const db_1 = require("../db");
|
|
9
|
-
async function memoryDeleteTool({ id, }, _config) {
|
|
1
|
+
import Database from 'better-sqlite3';
|
|
2
|
+
import { DB_PATH } from '../db.js';
|
|
3
|
+
export async function memoryDeleteTool({ id, }, _config) {
|
|
10
4
|
if (!Number.isFinite(id) || id <= 0) {
|
|
11
5
|
return 'Error: id must be a positive integer.';
|
|
12
6
|
}
|
|
13
|
-
const db = new
|
|
7
|
+
const db = new Database(DB_PATH);
|
|
14
8
|
try {
|
|
15
9
|
const existing = db.prepare('SELECT 1 FROM memory WHERE id = ?').get(id);
|
|
16
10
|
if (!existing) {
|
|
@@ -1,8 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
exports.memoryRetrieveTool = memoryRetrieveTool;
|
|
4
|
-
const db_1 = require("../db");
|
|
5
|
-
async function memoryRetrieveTool({ query, limit, }, config) {
|
|
1
|
+
import { searchMemories } from '../db.js';
|
|
2
|
+
export async function memoryRetrieveTool({ query, limit, }, config) {
|
|
6
3
|
const trimmedQueries = query?.map((q) => q.trim()).filter((q) => q.length > 0);
|
|
7
4
|
if (!trimmedQueries || trimmedQueries.length === 0) {
|
|
8
5
|
return 'Error: query is required.';
|
|
@@ -12,7 +9,7 @@ async function memoryRetrieveTool({ query, limit, }, config) {
|
|
|
12
9
|
return 'Error: limit must be between 1 and 50.';
|
|
13
10
|
}
|
|
14
11
|
try {
|
|
15
|
-
const results =
|
|
12
|
+
const results = searchMemories(trimmedQueries, limitValue);
|
|
16
13
|
if (results.length === 0) {
|
|
17
14
|
return 'No relevant memories found.';
|
|
18
15
|
}
|