ningzhi-agent-cli 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (84) hide show
  1. package/CLAUDE.md +197 -0
  2. package/README.md +35 -0
  3. package/dist/agent/agent.d.ts +24 -0
  4. package/dist/agent/agent.js +432 -0
  5. package/dist/agent/cli.d.ts +1 -0
  6. package/dist/agent/cli.js +209 -0
  7. package/dist/agent/colors.d.ts +19 -0
  8. package/dist/agent/colors.js +92 -0
  9. package/dist/agent/commands.d.ts +7 -0
  10. package/dist/agent/commands.js +73 -0
  11. package/dist/agent/config.d.ts +38 -0
  12. package/dist/agent/config.js +119 -0
  13. package/dist/agent/context.d.ts +17 -0
  14. package/dist/agent/context.js +146 -0
  15. package/dist/agent/db.d.ts +25 -0
  16. package/dist/agent/db.js +185 -0
  17. package/dist/agent/hooks/engine.d.ts +27 -0
  18. package/dist/agent/hooks/engine.js +122 -0
  19. package/dist/agent/hooks/index.d.ts +1 -0
  20. package/dist/agent/hooks/index.js +11 -0
  21. package/dist/agent/mcp/client.d.ts +31 -0
  22. package/dist/agent/mcp/client.js +138 -0
  23. package/dist/agent/mcp/index.d.ts +4 -0
  24. package/dist/agent/mcp/index.js +27 -0
  25. package/dist/agent/mcp/wrapper.d.ts +7 -0
  26. package/dist/agent/mcp/wrapper.js +26 -0
  27. package/dist/agent/model.d.ts +5 -0
  28. package/dist/agent/model.js +35 -0
  29. package/dist/agent/permission/dangerous-path.json +115 -0
  30. package/dist/agent/permission/exec.d.ts +14 -0
  31. package/dist/agent/permission/exec.js +35 -0
  32. package/dist/agent/permission/is-dangerous-path.d.ts +8 -0
  33. package/dist/agent/permission/is-dangerous-path.js +96 -0
  34. package/dist/agent/permission/is-safe-domains.d.ts +1 -0
  35. package/dist/agent/permission/is-safe-domains.js +145 -0
  36. package/dist/agent/permission/network.d.ts +8 -0
  37. package/dist/agent/permission/network.js +14 -0
  38. package/dist/agent/permission/read.d.ts +9 -0
  39. package/dist/agent/permission/read.js +17 -0
  40. package/dist/agent/permission/util.d.ts +11 -0
  41. package/dist/agent/permission/util.js +347 -0
  42. package/dist/agent/permission/write.d.ts +11 -0
  43. package/dist/agent/permission/write.js +21 -0
  44. package/dist/agent/prompt.d.ts +1 -0
  45. package/dist/agent/prompt.js +52 -0
  46. package/dist/agent/skills/oppose/SKILL.md +100 -0
  47. package/dist/agent/skills/planner/SKILL.md +90 -0
  48. package/dist/agent/skills.d.ts +8 -0
  49. package/dist/agent/skills.js +103 -0
  50. package/dist/agent/tools/agent_tool.d.ts +3 -0
  51. package/dist/agent/tools/agent_tool.js +44 -0
  52. package/dist/agent/tools/exec_tool.d.ts +3 -0
  53. package/dist/agent/tools/exec_tool.js +33 -0
  54. package/dist/agent/tools/load_skill_tool.d.ts +3 -0
  55. package/dist/agent/tools/load_skill_tool.js +16 -0
  56. package/dist/agent/tools/memory_create_tool.d.ts +6 -0
  57. package/dist/agent/tools/memory_create_tool.js +39 -0
  58. package/dist/agent/tools/memory_delete_tool.d.ts +3 -0
  59. package/dist/agent/tools/memory_delete_tool.js +28 -0
  60. package/dist/agent/tools/memory_retrieve_tool.d.ts +4 -0
  61. package/dist/agent/tools/memory_retrieve_tool.js +26 -0
  62. package/dist/agent/tools/profile_update_tool.d.ts +3 -0
  63. package/dist/agent/tools/profile_update_tool.js +51 -0
  64. package/dist/agent/tools/read_file_tool.d.ts +3 -0
  65. package/dist/agent/tools/read_file_tool.js +16 -0
  66. package/dist/agent/tools/run_js_tool.d.ts +3 -0
  67. package/dist/agent/tools/run_js_tool.js +47 -0
  68. package/dist/agent/tools/run_py_tool.d.ts +3 -0
  69. package/dist/agent/tools/run_py_tool.js +37 -0
  70. package/dist/agent/tools/web_fetch_tool.d.ts +3 -0
  71. package/dist/agent/tools/web_fetch_tool.js +28 -0
  72. package/dist/agent/tools/web_search_tool.d.ts +3 -0
  73. package/dist/agent/tools/web_search_tool.js +38 -0
  74. package/dist/agent/tools/write_file_tool.d.ts +4 -0
  75. package/dist/agent/tools/write_file_tool.js +15 -0
  76. package/dist/agent/tools.d.ts +14 -0
  77. package/dist/agent/tools.js +218 -0
  78. package/dist/agent/utils.d.ts +11 -0
  79. package/dist/agent/utils.js +43 -0
  80. package/dist/index.d.ts +2 -0
  81. package/dist/index.js +81 -0
  82. package/dist/install.d.ts +1 -0
  83. package/dist/install.js +150 -0
  84. package/package.json +56 -0
@@ -0,0 +1,347 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.isInProjectDir = isInProjectDir;
7
+ exports.isChangingDirectory = isChangingDirectory;
8
+ exports.isScriptExecution = isScriptExecution;
9
+ exports.isSafeCommand = isSafeCommand;
10
+ exports.isDangerousOperation = isDangerousOperation;
11
+ const node_path_1 = __importDefault(require("node:path"));
12
+ const node_os_1 = __importDefault(require("node:os"));
13
+ function isInProjectDir(filepath) {
14
+ const resolved = node_path_1.default.resolve(filepath);
15
+ const cwd = node_path_1.default.resolve(process.cwd());
16
+ // 检查是否在项目根目录下
17
+ if (resolved === cwd || resolved.startsWith(cwd + node_path_1.default.sep)) {
18
+ return true;
19
+ }
20
+ const ningzhiDir = node_path_1.default.resolve(node_os_1.default.homedir(), ".ningzhiAgentCli");
21
+ // 检查是否在项目根目录下的子目录下
22
+ return resolved === ningzhiDir || resolved.startsWith(ningzhiDir + node_path_1.default.sep);
23
+ }
24
+ /*
25
+ `(?:...)` 的`?:` 表示 非捕获组 ,只分组不保存匹配内容,
26
+ 比普通`()` 开销略小且不污染`match()` 结果,
27
+ 是纯布尔判断(`.test()` )场景下的惯用写法。
28
+ */
29
+ function isChangingDirectory(command) {
30
+ return /(?:^|[\s;|&])(?:cd|chdir|pushd|popd)(?:\s|$)/i.test(command);
31
+ }
32
+ // ==========================================================================
33
+ // exec tool 中不能执行 py js 等脚本语言
34
+ const PYTHON_TOOLS = ["python", "python3", "python2"];
35
+ const JS_TOOLS = ["node", "nodejs", "tsx", "ts-node"];
36
+ const OTHER_LANG_TOOLS = [
37
+ "java",
38
+ "javac",
39
+ "dotnet",
40
+ "go",
41
+ "cargo",
42
+ "rustc",
43
+ "ruby",
44
+ "php",
45
+ "perl",
46
+ "lua",
47
+ "rscript",
48
+ ];
49
+ // 非 shell/bash/sh 脚本的文件扩展名
50
+ const BLOCKED_EXTENSIONS = [
51
+ ".py",
52
+ ".js",
53
+ ".ts",
54
+ ".mjs",
55
+ ".cjs",
56
+ ".java",
57
+ ".cs",
58
+ ".go",
59
+ ".rb",
60
+ ".rs",
61
+ ".php",
62
+ ".pl",
63
+ ".lua",
64
+ ];
65
+ // 获取命令的第一个 token
66
+ // const trimmed = "git commit -m 'hello'" ;
67
+ // trimmed.match ( /^([^\s]+)/ ); // ---> match[1] === "git"
68
+ function getFirstToken(command) {
69
+ const trimmed = command.trim();
70
+ const match = trimmed.match(/^([^\s]+)/);
71
+ return match ? match[1] : trimmed;
72
+ }
73
+ function isScriptExecution(command) {
74
+ // 把一条可能包含 多段 shell 命令链 的输入,拆分成单个子命令
75
+ // "ls && npm test; echo done".split( /[;|&]+/ ).filter( Boolean )
76
+ // --→ ["ls ", " npm test", " echo done"]
77
+ const subCommands = command.split(/[;|&]+/).filter(Boolean);
78
+ for (const sub of subCommands) {
79
+ const token = getFirstToken(sub);
80
+ const tool = node_path_1.default.basename(token).toLowerCase();
81
+ const ext = node_path_1.default.extname(token).toLowerCase();
82
+ if (PYTHON_TOOLS.includes(tool) || ext === ".py") {
83
+ return {
84
+ blocked: true,
85
+ reason: "检测到 Python 脚本执行,请使用 run_py tool 执行 Python 代码。",
86
+ };
87
+ }
88
+ if (JS_TOOLS.includes(tool) ||
89
+ [".js", ".ts", ".mjs", ".cjs"].includes(ext)) {
90
+ return {
91
+ blocked: true,
92
+ reason: "检测到 JavaScript/TypeScript 脚本执行,请使用 run_js tool 执行 JavaScript/TypeScript 代码。",
93
+ };
94
+ }
95
+ if (OTHER_LANG_TOOLS.includes(tool) || BLOCKED_EXTENSIONS.includes(ext)) {
96
+ return {
97
+ blocked: true,
98
+ reason: `检测到 ${tool || ext} 脚本执行,exec tool 仅允许执行 shell/bash/sh 脚本。`,
99
+ };
100
+ }
101
+ }
102
+ return { blocked: false };
103
+ }
104
+ // ===============================================================================================
105
+ // 允许的命令
106
+ const SAFE_COMMANDS = [
107
+ "ls",
108
+ "pwd",
109
+ "cat",
110
+ "head",
111
+ "tail",
112
+ "grep",
113
+ "find",
114
+ "echo",
115
+ "date",
116
+ "whoami",
117
+ "id",
118
+ "uname",
119
+ "wc",
120
+ "sort",
121
+ "uniq",
122
+ "diff",
123
+ "which",
124
+ "whereis",
125
+ "file",
126
+ "stat",
127
+ "df",
128
+ "du",
129
+ "ps",
130
+ "pgrep",
131
+ "pstree",
132
+ "uptime",
133
+ "hostname",
134
+ "printf",
135
+ "readlink",
136
+ "realpath",
137
+ "tput",
138
+ "clear",
139
+ "reset",
140
+ "seq",
141
+ "yes",
142
+ "true",
143
+ "false",
144
+ "dir",
145
+ "type",
146
+ "findstr",
147
+ "more",
148
+ "tree",
149
+ "ver",
150
+ "vol",
151
+ "gci",
152
+ "gc",
153
+ "gl",
154
+ "sls",
155
+ "write",
156
+ ];
157
+ // 允许的 git 子命令
158
+ const SAFE_GIT_SUBCOMMANDS = ["status", "diff", "log"];
159
+ // 获取命令的第二个 token
160
+ function getSecondToken(command) {
161
+ const tokens = command.trim().split(/\s+/);
162
+ return tokens[1] || null;
163
+ }
164
+ // 检查命令是否安全
165
+ function isSafeCommand(command) {
166
+ if (hasOutputRedirect(command))
167
+ return false;
168
+ const subCommands = command.split(/[;|&]+/).filter(Boolean);
169
+ for (const sub of subCommands) {
170
+ const token = getFirstToken(sub);
171
+ const tool = node_path_1.default.basename(token).toLowerCase();
172
+ if (SAFE_COMMANDS.includes(tool)) {
173
+ continue;
174
+ }
175
+ if (tool === "git") {
176
+ const subCommand = getSecondToken(sub)?.toLowerCase();
177
+ if (subCommand && SAFE_GIT_SUBCOMMANDS.includes(subCommand)) {
178
+ continue;
179
+ }
180
+ }
181
+ return false;
182
+ }
183
+ return true;
184
+ }
185
+ // ===============================================================================================
186
+ // 阻止危险命令
187
+ const DANGEROUS_COMMANDS = [
188
+ "sudo",
189
+ "doas",
190
+ "rm",
191
+ "rmdir",
192
+ "unlink",
193
+ "shred",
194
+ "del",
195
+ "erase",
196
+ "rd",
197
+ "remove-item",
198
+ "clear-item",
199
+ "mv",
200
+ "move",
201
+ "rename",
202
+ "ren",
203
+ "cp",
204
+ "copy",
205
+ "xcopy",
206
+ "robocopy",
207
+ "touch",
208
+ "dd",
209
+ "truncate",
210
+ "tee",
211
+ "mkdir",
212
+ "md",
213
+ "mkfifo",
214
+ "mknod",
215
+ "new-item",
216
+ "chmod",
217
+ "chown",
218
+ "chgrp",
219
+ "umask",
220
+ "setfacl",
221
+ "chflags",
222
+ "icacls",
223
+ "cacls",
224
+ "takeown",
225
+ "attrib",
226
+ "kill",
227
+ "killall",
228
+ "pkill",
229
+ "xkill",
230
+ "systemctl",
231
+ "service",
232
+ "launchctl",
233
+ "init",
234
+ "shutdown",
235
+ "reboot",
236
+ "halt",
237
+ "poweroff",
238
+ "crontab",
239
+ "at",
240
+ "batch",
241
+ "taskkill",
242
+ "sc",
243
+ "net",
244
+ "netsh",
245
+ "stop-process",
246
+ "start-process",
247
+ "restart-computer",
248
+ "useradd",
249
+ "usermod",
250
+ "userdel",
251
+ "groupadd",
252
+ "groupmod",
253
+ "groupdel",
254
+ "passwd",
255
+ "chsh",
256
+ "chfn",
257
+ "chage",
258
+ "su",
259
+ "runas",
260
+ "dscl",
261
+ "dseditgroup",
262
+ "env",
263
+ "printenv",
264
+ "history",
265
+ "ssh",
266
+ "scp",
267
+ "sftp",
268
+ "ftp",
269
+ "telnet",
270
+ "nc",
271
+ "netcat",
272
+ "ncat",
273
+ "curl",
274
+ "wget",
275
+ "nmap",
276
+ "masscan",
277
+ "zmap",
278
+ "iptables",
279
+ "ufw",
280
+ "firewall-cmd",
281
+ "nft",
282
+ "socat",
283
+ "proxychains",
284
+ ];
285
+ // 匹配「外层 shell 再包一层」的调用形态,如:
286
+ // bash -c "rm -rf /" → -c 分支,内层命令落在捕获组 1
287
+ // cmd /c "del C:\data" → /c 分支,内层命令落在捕获组 2
288
+ // powershell -Command "..." → -Command 分支,内层命令落在捕获组 3
289
+ // 三个分支共用 ("..."|'...'|\S+) 抓取内层命令体,(?:...) 为非捕获组
290
+ const SHELL_COMMAND_SWITCH = /(?:bash|sh|zsh|cmd|powershell)\s+(?:-c\s+(".*?"|'.*?'|\S+)|\/c\s+(".*?"|'.*?'|\S+)|-Command\s+(".*?"|'.*?'|\S+))/i;
291
+ // 去掉首尾成对的引号
292
+ // `"abc"` → abc,`'abc'` → abc,`abc` → abc(原样返回)
293
+ function unquote(str) {
294
+ if ((str.startsWith('"') && str.endsWith('"')) ||
295
+ (str.startsWith("'") && str.endsWith("'"))) {
296
+ return str.slice(1, -1);
297
+ }
298
+ return str;
299
+ }
300
+ // 从 bash -c "..." / cmd /c "..." / powershell -Command "..." 中剥出内层命令
301
+ // 取非空的第一个捕获组(哪个分支匹配成功就用哪个),再去掉引号
302
+ function extractShellSubCommand(command) {
303
+ const match = command.match(SHELL_COMMAND_SWITCH);
304
+ if (!match)
305
+ return null;
306
+ return unquote(match[1] || match[2] || match[3] || "");
307
+ }
308
+ // 检测输出重定向(覆盖 `> file`、`>> file`、`2> file` 等形态)
309
+ // (^|[\s;|&]) → 重定向符必须出现在开头或分隔符之后,避免误伤 `a>b` 这类比较写法
310
+ // \d? → 可选的文件描述符编号(2> 1>)
311
+ // >>? → 覆盖覆盖写 `>` 与追加写 `>>`;\s 要求后面跟空白
312
+ function hasOutputRedirect(command) {
313
+ return /(^|[\s;|&])\d?>>?\s/.test(command);
314
+ }
315
+ function isDangerousOperation(command) {
316
+ // 第一步:穿透「shell 包一层」的伪装
317
+ // "bash -c 'rm -rf /'" 本身第一个 token 是 bash(不在黑名单),
318
+ // 必须先剥出内层命令,递归检查一遍才能发现里面的 rm
319
+ const subCommand = extractShellSubCommand(command);
320
+ if (subCommand) {
321
+ const result = isDangerousOperation(subCommand);
322
+ if (result.blocked)
323
+ return result;
324
+ }
325
+ // 第二步:拆分命令链,逐段检查第一个 token 是否命中黑名单
326
+ // "ls && rm -rf /tmp" → ["ls ", " rm -rf /tmp"] → 第二段命中 rm
327
+ const subCommands = command.split(/[;|&]+/).filter(Boolean);
328
+ for (const sub of subCommands) {
329
+ const token = getFirstToken(sub);
330
+ const tool = node_path_1.default.basename(token).toLowerCase();
331
+ if (DANGEROUS_COMMANDS.includes(tool)) {
332
+ return {
333
+ blocked: true,
334
+ reason: `检测到危险操作 "${tool}",exec tool 禁止执行该命令。如有需要请手动操作。`,
335
+ };
336
+ }
337
+ }
338
+ // 第三步:检查输出重定向(> 会改写文件系统,同样属于危险操作)
339
+ if (hasOutputRedirect(command)) {
340
+ return {
341
+ blocked: true,
342
+ reason: "检测到输出重定向,exec tool 禁止修改文件系统。如有需要请手动操作。",
343
+ };
344
+ }
345
+ return { blocked: false };
346
+ }
347
+ // ==========================================================================
@@ -0,0 +1,11 @@
1
+ export declare function checkWritePermission(toolCall: {
2
+ name: string;
3
+ args: Record<string, any>;
4
+ }): {
5
+ action: "allow";
6
+ } | {
7
+ action: "block";
8
+ reason: string;
9
+ } | {
10
+ action: "confirm";
11
+ };
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.checkWritePermission = checkWritePermission;
4
+ const is_dangerous_path_1 = require("./is-dangerous-path");
5
+ const util_1 = require("./util");
6
+ function checkWritePermission(toolCall) {
7
+ const filepath = toolCall.args?.filepath;
8
+ if (filepath == null) {
9
+ return { action: "allow" };
10
+ }
11
+ if ((0, is_dangerous_path_1.isDangerousPath)(filepath)) {
12
+ return {
13
+ action: "block",
14
+ reason: `路径 "${filepath}" 是系统敏感目录,为保护系统安全,禁止访问。如有需要请手动操作。`,
15
+ };
16
+ }
17
+ if ((0, util_1.isInProjectDir)(filepath)) {
18
+ return { action: "allow" };
19
+ }
20
+ return { action: "confirm" };
21
+ }
@@ -0,0 +1 @@
1
+ export declare const systemPrompt: string;
@@ -0,0 +1,52 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.systemPrompt = void 0;
7
+ const node_fs_1 = __importDefault(require("node:fs"));
8
+ const node_path_1 = __importDefault(require("node:path"));
9
+ const node_os_1 = require("node:os");
10
+ const skills_1 = require("./skills");
11
+ (0, skills_1.discoverSkills)();
12
+ const skillsText = (0, skills_1.getSkillsListText)();
13
+ // 基础提示
14
+ const basePrompt = "You are a helpful assistant. Note that before answering any questions related to timeliness, first check the latest date and do not use your own expired date. For example, execute a 'new Date()' JS script to query";
15
+ // 读取用户个人信息
16
+ function readProfile() {
17
+ try {
18
+ const filePath = node_path_1.default.join((0, node_os_1.homedir)(), ".ningzhiAgentCli", ".data", "profile.md");
19
+ const content = node_fs_1.default.readFileSync(filePath, "utf-8").trim();
20
+ return `<profile_info>${content}</profile_info>`;
21
+ }
22
+ catch {
23
+ return "<profile_info></profile_info>";
24
+ }
25
+ }
26
+ // 用户个人信息提示
27
+ const profilePrompt = `## User Profile
28
+
29
+ When you learn information about the user that fits the following categories, store it as profile information for future personalization.
30
+
31
+ <profile_template>
32
+ - Basic identity: name, nickname, gender, age, region, language
33
+ - Appearance: height, weight, skin tone, body type
34
+ - Personality and communication preferences
35
+ - Hobbies and interests
36
+ - Skills
37
+ - Work / occupation
38
+ </profile_template>
39
+
40
+ Here is the user's current profile information:
41
+ ${readProfile()}`;
42
+ // 记忆管理规则
43
+ const memoryPrompt = `## Memory Management Rules
44
+
45
+ - When deleting a memory, first use memory_retrieve to find its id, then call memory_delete with that id. If no matching memory is found, politely inform the user.
46
+ - When updating a memory, first delete the old memory, then create a new one.
47
+ - When information fits the categories in <profile_template>, do not record it as a memory. It will be stored in the profile file instead.`;
48
+ // 组装系统提示词
49
+ exports.systemPrompt = skillsText
50
+ ? `${basePrompt}\n\n${profilePrompt}\n\n${memoryPrompt}\n\n
51
+ ## 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}`
52
+ : `${basePrompt}\n\n${profilePrompt}\n\n${memoryPrompt}`;
@@ -0,0 +1,100 @@
1
+ ---
2
+ name: oppose
3
+ description: >
4
+ 当用户表达观点、提出计划、做出决策、寻求建议、进行头脑风暴或表现出强烈立场时,
5
+ 使用此 skill 主动提供建设性的反对意见和反面思考。即使用户没有明确要求质疑,
6
+ 只要对话涉及观点讨论、方案评估、决策分析、策略制定、因果推断或价值判断,
7
+ 就必须触发此 skill。如果用户只是询问客观事实、请求代码实现、要求生成文件、
8
+ 进行纯技术操作或寻求情感支持,则不要触发。
9
+ ---
10
+
11
+ # Oppose:建设性反对者
12
+
13
+ ## 目的
14
+
15
+ 默认情况下,AI 倾向于顺从用户、强化用户的既有观点。此 skill 要求你在适当场景下主动扮演"魔鬼代言人"(Devil's Advocate),提出有理有据的反对意见或反面思考,帮助用户发现盲点、检验假设、规避确认偏误。
16
+
17
+ ## 何时触发
18
+
19
+ **必须触发的情况:**
20
+ - 用户陈述个人观点("我觉得..."、"我认为..."、"显然...")
21
+ - 用户提出计划、方案或策略("我想..."、"我们应该..."、"方案是...")
22
+ - 用户做出判断或决策("这个更好"、"选 A 不选 B"、"肯定是...的原因")
23
+ - 用户寻求认同或建议("你觉得对吗?"、"给我点建议")
24
+ - 用户进行因果推断或预测("因为 A,所以 B 一定会...")
25
+ - 用户表达强烈情绪化的立场
26
+ - 用户要求对比选项并倾向某一方的场景
27
+
28
+ **不触发的情况:**
29
+ - 纯粹的事实查询("Python 的 len() 函数怎么用?")
30
+ - 纯技术操作请求("把这个文件转成 PDF"、"写一段排序代码")
31
+ - 明确的情感支持请求("我今天很难过")
32
+ - 用户明确要求你不要反对或只想听一面之词(尊重用户当下的明确指令)
33
+
34
+ ## 如何提出反对意见
35
+
36
+ ### 核心原则
37
+ 1. **建设性优先**:反对不是为了赢,而是为了帮助思考更完整。
38
+ 2. **针对观点,不针对人**:永远不对用户的人格、动机进行攻击。
39
+ 3. **有理有据**:基于逻辑、事实、数据或合理的类比提出质疑,而非主观臆断。
40
+ 4. **适度反对**:通常提出 1-2 个关键反对角度即可,避免过度辩论让用户反感。
41
+
42
+ ### 反对的角度(选择最适合的 1-2 个)
43
+ - **反例法**:是否存在与该结论相反的实例或场景?
44
+ - **前提质疑**:这个结论依赖的假设是否一定成立?如果前提变了,结论还成立吗?
45
+ - **因果倒置/混淆**:有没有可能是其他因素导致了这个结果?相关性不等于因果性。
46
+ - **极端化检验**:如果把这个逻辑推到极端,是否会产生荒谬的结果?
47
+ - **机会成本**:选择了 A,会失去什么?未选择的 B 有哪些被忽视的价值?
48
+ - **时间维度**:这个方案短期有效,长期呢?或者反过来。
49
+ - **受益者与受损者**:这个决策对谁有利?是否有被忽视的弱势群体或副作用?
50
+ - **归因偏差**:是否把成功归因于能力、把失败归因于运气?(或反之)
51
+ - **幸存者偏差**:你看到的数据是否是经过筛选的?失败案例被忽略了吗?
52
+ - **黑 swan 风险**:有没有低概率但高影响的负面可能性?
53
+
54
+ ### 语气与措辞
55
+ - 使用试探性语言:"不过,有没有可能..."、"但如果从另一个角度看..."、"这里有一个潜在的盲区..."
56
+ - 使用提问引导:"你是否考虑过...的情况?"、"如果 X 不成立,结论会如何变化?"
57
+ - 承认对方合理之处后再补充:"你的思路在...方面很合理,但有一个风险点..."
58
+ - 避免绝对化表达:不要说你错了,而说"另一个视角是..."
59
+
60
+ ## 输出格式
61
+
62
+ **第一步:正常回应**
63
+ 先直接、诚实地回应用户的问题或观点。如果用户问了问题,先回答;如果用户表达了观点,先表示理解。
64
+
65
+ **第二步:提出反对/质疑**
66
+ 在回应之后,自然地插入反对意见。使用分隔或过渡句,例如:
67
+ - "不过,作为另一种思考角度..."
68
+ - "但如果扮演一下'魔鬼代言人',有几个点值得质疑:"
69
+ - "当然,这个判断有一个潜在的盲区:"
70
+
71
+ 反对内容应简明扼要,1-2 点即可,每点 1-3 句话。
72
+
73
+ **第三步:回归用户主权**
74
+ 结尾把判断权交还给用户:
75
+ - "当然,你掌握的实际背景信息比我多,这只是供你交叉验证的一个角度。"
76
+ - "这些反对意见未必成立,但检验它们或许能让你的结论更扎实。"
77
+
78
+ ## 示例
79
+
80
+ **示例 1**
81
+ 用户:我觉得我们应该把全部预算投到短视频广告上,因为年轻人都在刷短视频。
82
+ AI 回应:
83
+ > 投短视频广告确实能触达大量年轻用户,逻辑上是合理的。
84
+ >
85
+ > 不过,如果换个角度想想:
86
+ > 1. **幸存者偏差**:你看到"年轻人都在刷",可能因为你所在的圈子和算法推荐让你看到的内容高度集中。那些不刷短视频或已经对其免疫的年轻群体,是否正好是你的目标用户?
87
+ > 2. **机会成本**:全部预算押注单一渠道意味着放弃搜索广告、私域运营等其他可能带来更高转化率的渠道。如果短视频流量成本突然上涨或平台政策变化,团队是否有退路?
88
+ >
89
+ > 当然,你对产品和市场的实际感知比我更直接,这两个问题只是供你在最终决策前快速检验一下。
90
+
91
+ **示例 2**
92
+ 用户:这个项目延期肯定是因为程序员效率太低。
93
+ AI 回应:
94
+ > 团队效率确实是影响交付的一个因素。
95
+ >
96
+ > 但从另一个视角看,"程序员效率低"这个归因可能过于简单:
97
+ > - 需求是否在开发过程中频繁变更?如果是,表面上的"效率低"实际上是需求管理成本的体现。
98
+ > - 延期是否从项目初期就可预见(比如估算阶段过于乐观)?如果是,问题根源在规划而非执行。
99
+ >
100
+ > 当然,如果你已经详细复盘过流程并有数据支撑你的判断,那我的质疑就不成立。
@@ -0,0 +1,90 @@
1
+ ---
2
+ name: planner
3
+ description: 帮用户创建 todo list、任务清单、日程计划或项目计划。当用户提到"待办"、"todo"、"计划"、"任务清单"、"日程"、"schedule"、"plan"、"任务分解"、"行程"时触发此 skill。即使用户只是说"帮我列一下要做的事"或"我想规划一下",也应使用此 skill。
4
+ ---
5
+
6
+ # Planner
7
+
8
+ 帮助用户创建结构清晰、可执行的 todo list 或计划。
9
+
10
+ ## 核心原则
11
+
12
+ - **具体可执行**:每个任务应清晰到可以直接行动,避免模糊表述
13
+ - **合理分组**:将相关任务归类,方便用户聚焦
14
+ - **优先级明确**:区分紧急/重要,帮助用户知道从哪里开始
15
+ - **时间感知**:有 deadline 时给出时间节点,无 deadline 时建议大致耗时
16
+
17
+ ---
18
+
19
+ ## 输出格式
20
+
21
+ 根据用户需求选择合适格式:
22
+
23
+ ### 简单 Todo List
24
+
25
+ 适用于:日常任务、购物清单、零散待办
26
+
27
+ ```
28
+ ## ✅ 今日待办
29
+
30
+ ### 🔴 优先处理
31
+ - [ ] 任务 A(预计 30 分钟)
32
+ - [ ] 任务 B
33
+
34
+ ### 🟡 今天内完成
35
+ - [ ] 任务 C
36
+ - [ ] 任务 D
37
+
38
+ ### 🟢 有空再做
39
+ - [ ] 任务 E
40
+ ```
41
+
42
+ ### 项目计划
43
+
44
+ 适用于:多步骤目标、有时间跨度的任务
45
+
46
+ ```
47
+ ## 📋 项目:[项目名]
48
+
49
+ **目标**:一句话说清楚要达成什么
50
+ **周期**:起止时间或预计时长
51
+
52
+ ### 阶段一:[名称](第 1-3 天)
53
+ - [ ] 子任务 1
54
+ - [ ] 子任务 2
55
+
56
+ ### 阶段二:[名称](第 4-7 天)
57
+ - [ ] 子任务 3
58
+ ```
59
+
60
+ ### 日程安排
61
+
62
+ 适用于:某一天或某段时间的时间表
63
+
64
+ ```
65
+ ## 🗓️ [日期] 日程
66
+
67
+ | 时间 | 事项 | 备注 |
68
+ |------|------|------|
69
+ | 09:00 | 任务 A | |
70
+ | 10:30 | 任务 B | 需提前准备材料 |
71
+ ```
72
+
73
+ ---
74
+
75
+ ## 操作步骤
76
+
77
+ 1. **理解需求**:从用户描述中提取目标、时间范围、资源限制
78
+ 2. **补充信息**(如有必要):若信息太少,问 1 个最关键的问题(不要问多个)
79
+ 3. **选择格式**:根据任务性质选择上方合适的模板
80
+ 4. **生成计划**:输出结构化内容,加入适当 emoji 提升可读性
81
+ 5. **给出建议**:末尾可简短说明"从哪里开始"或注意事项
82
+
83
+ ---
84
+
85
+ ## 注意事项
86
+
87
+ - 用户若未指定语言,跟随用户语言(中文输入→中文输出)
88
+ - 任务数量控制在合理范围:单日 todo 不超过 10 项,项目计划不超过 5 个阶段
89
+ - 若用户提供的目标过于宏大(如"学会编程"),主动拆解为近期可执行的第一步
90
+ - 允许用户说"太多了"或"再简单一点",灵活调整粒度
@@ -0,0 +1,8 @@
1
+ export interface SkillInfo {
2
+ name: string;
3
+ description: string;
4
+ dirPath: string;
5
+ }
6
+ export declare function discoverSkills(): SkillInfo[];
7
+ export declare function loadSkill(name: string): string | null;
8
+ export declare function getSkillsListText(): string;