@steerable/agent-shell 0.6.15
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/LICENSE +91 -0
- package/contracts/tool-contract.json +326 -0
- package/dist/attachments.d.ts +41 -0
- package/dist/attachments.js +147 -0
- package/dist/brand.d.ts +24 -0
- package/dist/brand.js +92 -0
- package/dist/host/http-routes.d.ts +21 -0
- package/dist/host/http-routes.js +55 -0
- package/dist/host/ipc.d.ts +11 -0
- package/dist/host/ipc.js +20 -0
- package/dist/host/pack-assembly.d.ts +86 -0
- package/dist/host/pack-assembly.js +32 -0
- package/dist/host/runtime.d.ts +69 -0
- package/dist/host/runtime.js +207 -0
- package/dist/host/visible-terminal-exec.d.ts +21 -0
- package/dist/host/visible-terminal-exec.js +151 -0
- package/dist/hosted-web-search.d.ts +13 -0
- package/dist/hosted-web-search.js +82 -0
- package/dist/image-attachment.d.ts +39 -0
- package/dist/image-attachment.js +133 -0
- package/dist/insights/flush.d.ts +10 -0
- package/dist/insights/flush.js +139 -0
- package/dist/insights/record.d.ts +17 -0
- package/dist/insights/record.js +44 -0
- package/dist/json-store.d.ts +9 -0
- package/dist/json-store.js +22 -0
- package/dist/llm/index.d.ts +23 -0
- package/dist/llm/index.js +105 -0
- package/dist/llm/ollama.d.ts +23 -0
- package/dist/llm/ollama.js +242 -0
- package/dist/llm/openai-compat.d.ts +20 -0
- package/dist/llm/openai-compat.js +199 -0
- package/dist/llm/sidecar-provider.d.ts +37 -0
- package/dist/llm/sidecar-provider.js +163 -0
- package/dist/llm/tool-choice.d.ts +35 -0
- package/dist/llm/tool-choice.js +85 -0
- package/dist/llm/types.d.ts +122 -0
- package/dist/llm/types.js +1 -0
- package/dist/local-backend/agent-capability.d.ts +101 -0
- package/dist/local-backend/agent-capability.js +174 -0
- package/dist/local-backend/ai-title.d.ts +43 -0
- package/dist/local-backend/ai-title.js +173 -0
- package/dist/local-backend/auto-continue-helper.d.ts +80 -0
- package/dist/local-backend/auto-continue-helper.js +83 -0
- package/dist/local-backend/branch-helper.d.ts +24 -0
- package/dist/local-backend/branch-helper.js +27 -0
- package/dist/local-backend/context-compactor.d.ts +81 -0
- package/dist/local-backend/context-compactor.js +213 -0
- package/dist/local-backend/coreloop-stream.d.ts +245 -0
- package/dist/local-backend/coreloop-stream.js +277 -0
- package/dist/local-backend/deferred-detector.d.ts +15 -0
- package/dist/local-backend/deferred-detector.js +124 -0
- package/dist/local-backend/history-helper.d.ts +30 -0
- package/dist/local-backend/history-helper.js +34 -0
- package/dist/local-backend/interrupted-helper.d.ts +29 -0
- package/dist/local-backend/interrupted-helper.js +25 -0
- package/dist/local-backend/live-stream.d.ts +36 -0
- package/dist/local-backend/live-stream.js +23 -0
- package/dist/local-backend/llm-diagnose.d.ts +37 -0
- package/dist/local-backend/llm-diagnose.js +284 -0
- package/dist/local-backend/message-triggers.d.ts +27 -0
- package/dist/local-backend/message-triggers.js +67 -0
- package/dist/local-backend/pack-backend-routes.d.ts +31 -0
- package/dist/local-backend/pack-backend-routes.js +62 -0
- package/dist/local-backend/pack-turn-hooks.d.ts +37 -0
- package/dist/local-backend/pack-turn-hooks.js +67 -0
- package/dist/local-backend/prompt-builder.d.ts +101 -0
- package/dist/local-backend/prompt-builder.js +246 -0
- package/dist/local-backend/regenerate-helper.d.ts +62 -0
- package/dist/local-backend/regenerate-helper.js +75 -0
- package/dist/local-backend/router.d.ts +175 -0
- package/dist/local-backend/router.js +3139 -0
- package/dist/local-backend/skill-install.d.ts +19 -0
- package/dist/local-backend/skill-install.js +71 -0
- package/dist/local-backend/skill-loader.d.ts +92 -0
- package/dist/local-backend/skill-loader.js +146 -0
- package/dist/local-backend/skills/00-identity/SKILL.md +32 -0
- package/dist/local-backend/skills/10-goal/SKILL.md +59 -0
- package/dist/local-backend/skills/11-loop/SKILL.md +71 -0
- package/dist/local-backend/skills/12-create-skill/SKILL.md +88 -0
- package/dist/local-backend/skills/70-plan-mode/SKILL.md +58 -0
- package/dist/local-backend/skills/80-tool-usage/SKILL.md +70 -0
- package/dist/local-backend/skills/81-anti-deferred/SKILL.md +53 -0
- package/dist/local-backend/skills/82-data-grounding/SKILL.md +56 -0
- package/dist/local-backend/skills/85-local-exec/SKILL.md +86 -0
- package/dist/local-backend/skills/86-proactive-coding/SKILL.md +51 -0
- package/dist/local-backend/subagent-profiles.d.ts +30 -0
- package/dist/local-backend/subagent-profiles.js +74 -0
- package/dist/local-backend/task-process.d.ts +12 -0
- package/dist/local-backend/task-process.js +176 -0
- package/dist/local-backend/task-service.d.ts +135 -0
- package/dist/local-backend/task-service.js +565 -0
- package/dist/local-backend/turn-duration.d.ts +2 -0
- package/dist/local-backend/turn-duration.js +9 -0
- package/dist/local-backend/turn-timeline.d.ts +16 -0
- package/dist/local-backend/turn-timeline.js +42 -0
- package/dist/local-backend/worktree-service.d.ts +84 -0
- package/dist/local-backend/worktree-service.js +243 -0
- package/dist/local-edit.d.ts +48 -0
- package/dist/local-edit.js +44 -0
- package/dist/local-executor.d.ts +255 -0
- package/dist/local-executor.js +881 -0
- package/dist/local-script-registry.d.ts +28 -0
- package/dist/local-script-registry.js +63 -0
- package/dist/log.d.ts +13 -0
- package/dist/log.js +12 -0
- package/dist/main.d.ts +1 -0
- package/dist/main.js +855 -0
- package/dist/mcp-executor.d.ts +45 -0
- package/dist/mcp-executor.js +241 -0
- package/dist/mcp-server-registry.d.ts +104 -0
- package/dist/mcp-server-registry.js +234 -0
- package/dist/preload-default.d.ts +1 -0
- package/dist/preload-default.js +9 -0
- package/dist/preload.cjs +395 -0
- package/dist/preload.d.ts +20 -0
- package/dist/preload.js +411 -0
- package/dist/product-config.d.ts +43 -0
- package/dist/product-config.js +26 -0
- package/dist/project-registry.d.ts +55 -0
- package/dist/project-registry.js +106 -0
- package/dist/project-rules.d.ts +15 -0
- package/dist/project-rules.js +102 -0
- package/dist/runtime.d.ts +62 -0
- package/dist/runtime.js +217 -0
- package/dist/scenario/pack.d.ts +8 -0
- package/dist/scenario/pack.js +1 -0
- package/dist/scenario/registry.d.ts +24 -0
- package/dist/scenario/registry.js +31 -0
- package/dist/server/http-server.d.ts +39 -0
- package/dist/server/http-server.js +361 -0
- package/dist/server/index.d.ts +1 -0
- package/dist/server/index.js +107 -0
- package/dist/server/sse-bus.d.ts +14 -0
- package/dist/server/sse-bus.js +31 -0
- package/dist/shell-adapt.d.ts +21 -0
- package/dist/shell-adapt.js +104 -0
- package/dist/sidecar/boot.d.ts +36 -0
- package/dist/sidecar/boot.js +343 -0
- package/dist/sidecar/egress-hint.d.ts +15 -0
- package/dist/sidecar/egress-hint.js +46 -0
- package/dist/sidecar/egress-proxy.d.ts +183 -0
- package/dist/sidecar/egress-proxy.js +419 -0
- package/dist/sidecar/errors.d.ts +22 -0
- package/dist/sidecar/errors.js +38 -0
- package/dist/sidecar/exec-sandbox.d.ts +48 -0
- package/dist/sidecar/exec-sandbox.js +94 -0
- package/dist/sidecar/handle.d.ts +32 -0
- package/dist/sidecar/handle.js +53 -0
- package/dist/sidecar/index.d.ts +14 -0
- package/dist/sidecar/index.js +13 -0
- package/dist/sidecar/proxy-detect.d.ts +50 -0
- package/dist/sidecar/proxy-detect.js +182 -0
- package/dist/sidecar/reverse-approval.d.ts +55 -0
- package/dist/sidecar/reverse-approval.js +86 -0
- package/dist/sidecar/reverse-ask-user.d.ts +34 -0
- package/dist/sidecar/reverse-ask-user.js +59 -0
- package/dist/sidecar/reverse-spawn.d.ts +19 -0
- package/dist/sidecar/reverse-spawn.js +161 -0
- package/dist/sidecar/reverse-tools.d.ts +29 -0
- package/dist/sidecar/reverse-tools.js +106 -0
- package/dist/sidecar/safety-patterns.d.ts +41 -0
- package/dist/sidecar/safety-patterns.js +157 -0
- package/dist/sidecar/storage-path.d.ts +14 -0
- package/dist/sidecar/storage-path.js +35 -0
- package/dist/sidecar/supervisor.d.ts +218 -0
- package/dist/sidecar/supervisor.js +932 -0
- package/dist/sidecar/types.d.ts +601 -0
- package/dist/sidecar/types.js +1 -0
- package/dist/single-instance.d.ts +11 -0
- package/dist/single-instance.js +21 -0
- package/dist/storage/empty-chats.d.ts +9 -0
- package/dist/storage/empty-chats.js +16 -0
- package/dist/storage/index.d.ts +373 -0
- package/dist/storage/index.js +1158 -0
- package/dist/storage/insights-redact.d.ts +2 -0
- package/dist/storage/insights-redact.js +30 -0
- package/dist/storage/insights-settings.d.ts +53 -0
- package/dist/storage/insights-settings.js +92 -0
- package/dist/storage/llm-settings.d.ts +120 -0
- package/dist/storage/llm-settings.js +233 -0
- package/dist/storage/local-store-singleton.d.ts +28 -0
- package/dist/storage/local-store-singleton.js +38 -0
- package/dist/storage/message-order.d.ts +25 -0
- package/dist/storage/message-order.js +27 -0
- package/dist/storage/pack-migrations.d.ts +22 -0
- package/dist/storage/pack-migrations.js +24 -0
- package/dist/storage/pack-seeds.d.ts +36 -0
- package/dist/storage/pack-seeds.js +42 -0
- package/dist/storage/telemetry-settings.d.ts +38 -0
- package/dist/storage/telemetry-settings.js +59 -0
- package/dist/storage/usage-summary.d.ts +55 -0
- package/dist/storage/usage-summary.js +38 -0
- package/dist/storage/web-search-settings.d.ts +38 -0
- package/dist/storage/web-search-settings.js +74 -0
- package/dist/storage/write-lease.d.ts +26 -0
- package/dist/storage/write-lease.js +74 -0
- package/dist/terminal-manager.d.ts +83 -0
- package/dist/terminal-manager.js +506 -0
- package/dist/tool-router.d.ts +228 -0
- package/dist/tool-router.js +930 -0
- package/dist/tool-search-rank.d.ts +42 -0
- package/dist/tool-search-rank.js +96 -0
- package/package.json +67 -0
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copy a skill directory into the user skills root (settings import +
|
|
3
|
+
* auto-register when the agent writes a SKILL.md outside known roots).
|
|
4
|
+
*/
|
|
5
|
+
export declare function resolveUserPath(inputPath: string): string;
|
|
6
|
+
export declare function parseSkillNameFromMarkdown(skillMdContent: string, fallbackDirName: string): string;
|
|
7
|
+
export declare function installSkillFromDirectory(sourceDir: string): {
|
|
8
|
+
name: string;
|
|
9
|
+
dest: string;
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* After a successful write/edit of SKILL.md: if that skill dir is not
|
|
13
|
+
* already on a listed root (builtin / workspace / user), copy it into
|
|
14
|
+
* the user skills directory so Skill 设置 and `/` pick it up without a
|
|
15
|
+
* manual import. No-ops for anything that isn't `…/skills/<name>/SKILL.md`.
|
|
16
|
+
*/
|
|
17
|
+
export declare function maybeAutoInstallWrittenSkill(writtenPath: string): {
|
|
18
|
+
name: string;
|
|
19
|
+
} | null;
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copy a skill directory into the user skills root (settings import +
|
|
3
|
+
* auto-register when the agent writes a SKILL.md outside known roots).
|
|
4
|
+
*/
|
|
5
|
+
import fs from 'node:fs';
|
|
6
|
+
import os from 'node:os';
|
|
7
|
+
import path from 'node:path';
|
|
8
|
+
import { getSkillsDir, getUserSkillsDir, listSkillRoots } from './skill-loader.js';
|
|
9
|
+
export function resolveUserPath(inputPath) {
|
|
10
|
+
const expanded = inputPath.startsWith('~')
|
|
11
|
+
? path.join(os.homedir(), inputPath.slice(1))
|
|
12
|
+
: inputPath;
|
|
13
|
+
return path.resolve(expanded);
|
|
14
|
+
}
|
|
15
|
+
export function parseSkillNameFromMarkdown(skillMdContent, fallbackDirName) {
|
|
16
|
+
let skillName = '';
|
|
17
|
+
if (skillMdContent.startsWith('---')) {
|
|
18
|
+
const rest = skillMdContent.slice(3);
|
|
19
|
+
const end = rest.indexOf('\n---');
|
|
20
|
+
if (end !== -1) {
|
|
21
|
+
const fmRaw = rest.slice(0, end);
|
|
22
|
+
const nameMatch = fmRaw.match(/^name:\s*(.+)$/m);
|
|
23
|
+
if (nameMatch?.[1]) {
|
|
24
|
+
skillName = nameMatch[1].trim().replace(/^["']|["']$/g, '');
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
if (!skillName)
|
|
29
|
+
skillName = fallbackDirName;
|
|
30
|
+
return skillName.toLowerCase().replace(/[^a-z0-9-]/g, '-');
|
|
31
|
+
}
|
|
32
|
+
export function installSkillFromDirectory(sourceDir) {
|
|
33
|
+
const skillMdPath = path.join(sourceDir, 'SKILL.md');
|
|
34
|
+
if (!fs.existsSync(skillMdPath) || !fs.statSync(skillMdPath).isFile()) {
|
|
35
|
+
throw new Error(`未找到技能文件,请检查路径中是否存在 SKILL.md 文件: ${sourceDir}`);
|
|
36
|
+
}
|
|
37
|
+
const skillName = parseSkillNameFromMarkdown(fs.readFileSync(skillMdPath, 'utf8'), path.basename(sourceDir));
|
|
38
|
+
const dest = path.join(getUserSkillsDir(), skillName);
|
|
39
|
+
fs.mkdirSync(dest, { recursive: true });
|
|
40
|
+
fs.cpSync(sourceDir, dest, { recursive: true });
|
|
41
|
+
return { name: skillName, dest };
|
|
42
|
+
}
|
|
43
|
+
function isInside(candidate, root) {
|
|
44
|
+
const resolved = path.resolve(candidate);
|
|
45
|
+
const resolvedRoot = path.resolve(root);
|
|
46
|
+
return resolved === resolvedRoot || resolved.startsWith(resolvedRoot + path.sep);
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* After a successful write/edit of SKILL.md: if that skill dir is not
|
|
50
|
+
* already on a listed root (builtin / workspace / user), copy it into
|
|
51
|
+
* the user skills directory so Skill 设置 and `/` pick it up without a
|
|
52
|
+
* manual import. No-ops for anything that isn't `…/skills/<name>/SKILL.md`.
|
|
53
|
+
*/
|
|
54
|
+
export function maybeAutoInstallWrittenSkill(writtenPath) {
|
|
55
|
+
const filePath = resolveUserPath(writtenPath);
|
|
56
|
+
if (path.basename(filePath) !== 'SKILL.md')
|
|
57
|
+
return null;
|
|
58
|
+
if (!fs.existsSync(filePath) || !fs.statSync(filePath).isFile())
|
|
59
|
+
return null;
|
|
60
|
+
const skillDir = path.dirname(filePath);
|
|
61
|
+
const root = path.dirname(skillDir);
|
|
62
|
+
if (path.basename(root) !== 'skills')
|
|
63
|
+
return null;
|
|
64
|
+
if (isInside(skillDir, getUserSkillsDir()) || isInside(skillDir, getSkillsDir())) {
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
if (listSkillRoots().some((listed) => path.resolve(listed) === path.resolve(root))) {
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
return { name: installSkillFromDirectory(skillDir).name };
|
|
71
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Skill loader — desktop thin client over the framework's skill parser.
|
|
3
|
+
*
|
|
4
|
+
* SKILL.md parsing (frontmatter, `layer` derivation, condition matching,
|
|
5
|
+
* `{scripts}` resolution, user-root-overrides-builtin) is single-sourced in
|
|
6
|
+
* Python (`steerable_agent_runtime.skills`) and reached via the sidecar
|
|
7
|
+
* `skills.list` RPC. This module only resolves the skill roots (builtin,
|
|
8
|
+
* workspace `skills/` dirs, userData), calls the RPC, and returns the wire
|
|
9
|
+
* shape (which already matches `SkillModule`). The product concerns stay on
|
|
10
|
+
* top: eager-layer budget trimming (prompt-builder) and the import/uninstall
|
|
11
|
+
* file management (router / skill-install).
|
|
12
|
+
*
|
|
13
|
+
* The sidecar shares the filesystem with this host, so the roots below are
|
|
14
|
+
* readable by both. The sidecar is the only chat path and auto-restarts, so
|
|
15
|
+
* it is available both in-turn (prompt assembly) and for the management UI.
|
|
16
|
+
*/
|
|
17
|
+
/** builtin = 随应用分发; user = 设置页导入; workspace = 项目/工作区 `skills/`. */
|
|
18
|
+
export type SkillOrigin = 'builtin' | 'user' | 'workspace';
|
|
19
|
+
export declare function getUserSkillsDir(): string;
|
|
20
|
+
export declare function setPackSkillsDir(dir: string): void;
|
|
21
|
+
export declare function setWorkspaceSkillRootsProvider(provider: (() => Iterable<string>) | null): void;
|
|
22
|
+
/** Bind the default workspace roots: each project's `skills/`, cwd, app root. */
|
|
23
|
+
export declare function bindWorkspaceSkillRoots(registry: {
|
|
24
|
+
list: () => Array<{
|
|
25
|
+
folderPath: string;
|
|
26
|
+
}>;
|
|
27
|
+
}): void;
|
|
28
|
+
/**
|
|
29
|
+
* Skill roots in override order: builtin, workspace extras, user last
|
|
30
|
+
* (user wins on name clash, same as the framework provider merge).
|
|
31
|
+
*/
|
|
32
|
+
export declare function listSkillRoots(skillsDir?: string): string[];
|
|
33
|
+
export declare function classifySkillOrigin(skillsDir: string): SkillOrigin;
|
|
34
|
+
export type SkillLayer = 'eager' | 'catalog';
|
|
35
|
+
export interface SkillModule {
|
|
36
|
+
name: string;
|
|
37
|
+
/** 可读展示名(可以是中文,如「数据处理链」)。没配置时为空串。 */
|
|
38
|
+
displayName: string;
|
|
39
|
+
description: string;
|
|
40
|
+
priority: number;
|
|
41
|
+
tags: string[];
|
|
42
|
+
conditions: string[];
|
|
43
|
+
match: 'any' | 'all';
|
|
44
|
+
/**
|
|
45
|
+
* 分层披露:eager = 正文常驻系统提示词;catalog = 只进目录,模型调
|
|
46
|
+
* `skill` 工具按需加载正文。由框架解析器按 frontmatter `layer` 或
|
|
47
|
+
* priority 阈值派生。
|
|
48
|
+
*/
|
|
49
|
+
layer: SkillLayer;
|
|
50
|
+
/**
|
|
51
|
+
* false = 只能由用户通过 `/name` 触发(生态兼容:Claude/codex 的
|
|
52
|
+
* `disable-model-invocation: true`),不进 catalog、skill 工具拒绝加载。
|
|
53
|
+
*/
|
|
54
|
+
modelInvocable: boolean;
|
|
55
|
+
content: string;
|
|
56
|
+
dirName: string;
|
|
57
|
+
skillsDir: string;
|
|
58
|
+
}
|
|
59
|
+
export interface LoadSkillsOptions {
|
|
60
|
+
conditions?: Iterable<string>;
|
|
61
|
+
/** Override skills root (mainly for tests); bypasses built-in + user dirs. */
|
|
62
|
+
skillsDir?: string;
|
|
63
|
+
/** Kept for call-site compatibility; the RPC re-parses fresh each call. */
|
|
64
|
+
reload?: boolean;
|
|
65
|
+
ignoreConditions?: boolean;
|
|
66
|
+
/**
|
|
67
|
+
* Skill names (or dir names) to always drop, even when `ignoreConditions`
|
|
68
|
+
* is set. Used for mode-scoped skills (e.g. `plan-mode`) that must never
|
|
69
|
+
* leak into a turn they don't belong to.
|
|
70
|
+
*/
|
|
71
|
+
excludeSkillNames?: Iterable<string>;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Load skill modules matching the active runtime conditions, parsed by the
|
|
75
|
+
* framework. When the sidecar is still booting (renderer requests race app
|
|
76
|
+
* start), waits on the registered boot promise instead of degrading
|
|
77
|
+
* immediately. Returns [] when the sidecar is unavailable, boot failed, the
|
|
78
|
+
* wait times out, or the RPC fails (mirrors the old "missing dir → []"
|
|
79
|
+
* robustness; the prompt then falls back to its built-in minimal prompt).
|
|
80
|
+
*/
|
|
81
|
+
export declare function loadSkills(options?: LoadSkillsOptions): Promise<SkillModule[]>;
|
|
82
|
+
export declare function getSkillsDir(): string;
|
|
83
|
+
/**
|
|
84
|
+
* Resolve one skill by the aliases the "/" trigger accepts (name / dirName /
|
|
85
|
+
* displayName, case-insensitive), bypassing conditions — an explicit user
|
|
86
|
+
* trigger forces the skill. `exclude` keeps mode-scoped drops authoritative
|
|
87
|
+
* (e.g. execution skills stay out of plan mode even when typed explicitly).
|
|
88
|
+
*/
|
|
89
|
+
export declare function findSkill(name: string, options?: {
|
|
90
|
+
exclude?: Iterable<string>;
|
|
91
|
+
skillsDir?: string;
|
|
92
|
+
}): Promise<SkillModule | null>;
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Skill loader — desktop thin client over the framework's skill parser.
|
|
3
|
+
*
|
|
4
|
+
* SKILL.md parsing (frontmatter, `layer` derivation, condition matching,
|
|
5
|
+
* `{scripts}` resolution, user-root-overrides-builtin) is single-sourced in
|
|
6
|
+
* Python (`steerable_agent_runtime.skills`) and reached via the sidecar
|
|
7
|
+
* `skills.list` RPC. This module only resolves the skill roots (builtin,
|
|
8
|
+
* workspace `skills/` dirs, userData), calls the RPC, and returns the wire
|
|
9
|
+
* shape (which already matches `SkillModule`). The product concerns stay on
|
|
10
|
+
* top: eager-layer budget trimming (prompt-builder) and the import/uninstall
|
|
11
|
+
* file management (router / skill-install).
|
|
12
|
+
*
|
|
13
|
+
* The sidecar shares the filesystem with this host, so the roots below are
|
|
14
|
+
* readable by both. The sidecar is the only chat path and auto-restarts, so
|
|
15
|
+
* it is available both in-turn (prompt assembly) and for the management UI.
|
|
16
|
+
*/
|
|
17
|
+
import fs from 'node:fs';
|
|
18
|
+
import path from 'node:path';
|
|
19
|
+
import { fileURLToPath } from 'node:url';
|
|
20
|
+
import { getAppRootDir, getUserDataDir } from '../runtime.js';
|
|
21
|
+
import { getSidecarSupervisor, whenSidecarSupervisor } from '../sidecar/handle.js';
|
|
22
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
23
|
+
const DEFAULT_SKILLS_DIR = path.resolve(__dirname, 'skills');
|
|
24
|
+
export function getUserSkillsDir() {
|
|
25
|
+
const dir = path.join(getUserDataDir(), 'skills');
|
|
26
|
+
if (!fs.existsSync(dir)) {
|
|
27
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
28
|
+
}
|
|
29
|
+
return dir;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* 场景包技能根(3.2):产品构建把激活包的技能拷到产品产物的
|
|
33
|
+
* `pack-skills/` 目录,产品组装根经 setPackSkillsDir 注入。包技能随产品
|
|
34
|
+
* 分发,origin 归 'builtin'。未注入(纯 shell)时该根不参与。
|
|
35
|
+
*/
|
|
36
|
+
let packSkillsDir = null;
|
|
37
|
+
export function setPackSkillsDir(dir) {
|
|
38
|
+
packSkillsDir = dir;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Extra skill roots beyond builtin + userData (project folders, cwd, app
|
|
42
|
+
* root). Hosts register this at boot; tests leave it unset so roots stay
|
|
43
|
+
* [builtin, user].
|
|
44
|
+
*/
|
|
45
|
+
let workspaceSkillRootsProvider = null;
|
|
46
|
+
export function setWorkspaceSkillRootsProvider(provider) {
|
|
47
|
+
workspaceSkillRootsProvider = provider;
|
|
48
|
+
}
|
|
49
|
+
/** Bind the default workspace roots: each project's `skills/`, cwd, app root. */
|
|
50
|
+
export function bindWorkspaceSkillRoots(registry) {
|
|
51
|
+
setWorkspaceSkillRootsProvider(() => [
|
|
52
|
+
...registry.list().map((p) => path.join(p.folderPath, 'skills')),
|
|
53
|
+
path.join(process.cwd(), 'skills'),
|
|
54
|
+
path.join(getAppRootDir(), 'skills'),
|
|
55
|
+
]);
|
|
56
|
+
}
|
|
57
|
+
function samePath(a, b) {
|
|
58
|
+
return path.resolve(a) === path.resolve(b);
|
|
59
|
+
}
|
|
60
|
+
function uniqueExisting(dirs) {
|
|
61
|
+
const seen = new Set();
|
|
62
|
+
const out = [];
|
|
63
|
+
for (const dir of dirs) {
|
|
64
|
+
const resolved = path.resolve(dir);
|
|
65
|
+
if (seen.has(resolved))
|
|
66
|
+
continue;
|
|
67
|
+
seen.add(resolved);
|
|
68
|
+
if (!fs.existsSync(resolved) || !fs.statSync(resolved).isDirectory())
|
|
69
|
+
continue;
|
|
70
|
+
out.push(resolved);
|
|
71
|
+
}
|
|
72
|
+
return out;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Skill roots in override order: builtin, workspace extras, user last
|
|
76
|
+
* (user wins on name clash, same as the framework provider merge).
|
|
77
|
+
*/
|
|
78
|
+
export function listSkillRoots(skillsDir) {
|
|
79
|
+
if (skillsDir)
|
|
80
|
+
return [path.resolve(skillsDir)];
|
|
81
|
+
const builtin = path.resolve(DEFAULT_SKILLS_DIR);
|
|
82
|
+
const user = path.resolve(getUserSkillsDir());
|
|
83
|
+
const pack = packSkillsDir && fs.existsSync(packSkillsDir) ? [path.resolve(packSkillsDir)] : [];
|
|
84
|
+
const extras = uniqueExisting(workspaceSkillRootsProvider?.() ?? []).filter((dir) => !samePath(dir, builtin) && !samePath(dir, user) && !pack.some((p) => samePath(dir, p)));
|
|
85
|
+
return [builtin, ...pack, ...extras, user];
|
|
86
|
+
}
|
|
87
|
+
export function classifySkillOrigin(skillsDir) {
|
|
88
|
+
const resolved = path.resolve(skillsDir);
|
|
89
|
+
if (samePath(resolved, getUserSkillsDir()))
|
|
90
|
+
return 'user';
|
|
91
|
+
if (samePath(resolved, DEFAULT_SKILLS_DIR))
|
|
92
|
+
return 'builtin';
|
|
93
|
+
if (packSkillsDir && samePath(resolved, packSkillsDir))
|
|
94
|
+
return 'builtin';
|
|
95
|
+
return 'workspace';
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Load skill modules matching the active runtime conditions, parsed by the
|
|
99
|
+
* framework. When the sidecar is still booting (renderer requests race app
|
|
100
|
+
* start), waits on the registered boot promise instead of degrading
|
|
101
|
+
* immediately. Returns [] when the sidecar is unavailable, boot failed, the
|
|
102
|
+
* wait times out, or the RPC fails (mirrors the old "missing dir → []"
|
|
103
|
+
* robustness; the prompt then falls back to its built-in minimal prompt).
|
|
104
|
+
*/
|
|
105
|
+
export async function loadSkills(options = {}) {
|
|
106
|
+
const supervisor = getSidecarSupervisor() ?? (await whenSidecarSupervisor());
|
|
107
|
+
if (!supervisor) {
|
|
108
|
+
console.warn('[skill-loader] sidecar unavailable; no skills loaded');
|
|
109
|
+
return [];
|
|
110
|
+
}
|
|
111
|
+
try {
|
|
112
|
+
return await supervisor.listSkills({
|
|
113
|
+
roots: listSkillRoots(options.skillsDir),
|
|
114
|
+
conditions: options.conditions ? Array.from(options.conditions) : undefined,
|
|
115
|
+
exclude: options.excludeSkillNames ? Array.from(options.excludeSkillNames) : undefined,
|
|
116
|
+
ignoreConditions: options.ignoreConditions,
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
catch (err) {
|
|
120
|
+
console.warn('[skill-loader] skills.list failed', err);
|
|
121
|
+
return [];
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
export function getSkillsDir() {
|
|
125
|
+
return DEFAULT_SKILLS_DIR;
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Resolve one skill by the aliases the "/" trigger accepts (name / dirName /
|
|
129
|
+
* displayName, case-insensitive), bypassing conditions — an explicit user
|
|
130
|
+
* trigger forces the skill. `exclude` keeps mode-scoped drops authoritative
|
|
131
|
+
* (e.g. execution skills stay out of plan mode even when typed explicitly).
|
|
132
|
+
*/
|
|
133
|
+
export async function findSkill(name, options = {}) {
|
|
134
|
+
const key = name.toLowerCase().trim();
|
|
135
|
+
if (!key)
|
|
136
|
+
return null;
|
|
137
|
+
const all = await loadSkills({
|
|
138
|
+
ignoreConditions: true,
|
|
139
|
+
skillsDir: options.skillsDir,
|
|
140
|
+
excludeSkillNames: options.exclude,
|
|
141
|
+
});
|
|
142
|
+
const hit = all.find((m) => m.name.toLowerCase() === key ||
|
|
143
|
+
m.dirName.toLowerCase() === key ||
|
|
144
|
+
(m.displayName !== '' && m.displayName.toLowerCase() === key));
|
|
145
|
+
return hit ?? null;
|
|
146
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: identity
|
|
3
|
+
description: Defines the product agent's core role, working environment, and language convention. Always loaded as the foundational skill. Brand placeholders ({agentName}) are rendered from the product-injected brand.
|
|
4
|
+
priority: 1000
|
|
5
|
+
tags: [identity, base]
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# 角色
|
|
9
|
+
|
|
10
|
+
你是 **{agentName}**,一款本地桌面 AI 伙伴,运行在用户**自己的 Windows / macOS / Linux 机器**上。你的核心使命是:
|
|
11
|
+
|
|
12
|
+
1. 陪用户理清思路、拆解目标,把想法变成可执行的计划;
|
|
13
|
+
2. 协助用户在本机执行真实操作(运行 shell、读写文件、当前产品安装的场景工具等);
|
|
14
|
+
3. 把工具调用的真实结果汇报给用户——**不许凭空编造**。
|
|
15
|
+
|
|
16
|
+
> 用户问你"你是谁"时回答 "{agentName}",不要回答内部代号。
|
|
17
|
+
|
|
18
|
+
## 工作约束
|
|
19
|
+
|
|
20
|
+
- **不要凭空编造任何信息或工具返回值**。看不到结果的话就调工具去看,而不是猜。
|
|
21
|
+
- 用中文回答;技术名词可以保留英文(命令、API、路径、文件名等)。
|
|
22
|
+
- 工具失败或无权限时,把错误如实告诉用户,并建议下一步动作。
|
|
23
|
+
- 回复保持简洁;超过 6-8 行的长内容用 markdown 列表或代码块组织。
|
|
24
|
+
|
|
25
|
+
## 结构化提问(ask_user)使用规范
|
|
26
|
+
|
|
27
|
+
当你需要向用户收集结构化信息(提供选项让用户选择)时,使用 `ask_user` 工具,并遵守:
|
|
28
|
+
|
|
29
|
+
- **一个议题只对应一个问题**(`questions` 数组中的一个元素),不要为同一个议题创建多个问题。
|
|
30
|
+
- 有建议选项时,把问题设为 `type:"select"`,`options` 放 2-4 个建议;界面菜单底部已自动带「其他 / 自定义」输入框,用户可直接输入补充内容,所以**不要**再额外创建一个 `text` 问题作为“手动输入”。
|
|
31
|
+
- 只有某议题本身是自由文本、确实无法给出建议选项时,才把该问题设为 `type:"text"`。
|
|
32
|
+
- 多个议题可以放在同一次 `ask_user` 调用的 `questions` 数组里,按顺序排列,一次问完。
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: goal
|
|
3
|
+
displayName: 目标跟踪
|
|
4
|
+
description: 把一句话需求变成有验收标准的目标,用 todo_write 建清单、执行中更新状态、收尾逐条给出证据。适合多步骤、跨回合、需要明确「到底做完没有」的任务。
|
|
5
|
+
priority: 500
|
|
6
|
+
tags: [workflow, planning, goal]
|
|
7
|
+
disable-model-invocation: true
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# 目标跟踪(Goal)
|
|
11
|
+
|
|
12
|
+
用户希望这次不是随手做一件事,而是**盯住一个目标直到真正达成**。本技能的顺序是固定的:先立目标 → 落成清单 → 执行并更新 → 逐条验收。
|
|
13
|
+
|
|
14
|
+
## 1. 先立目标(不要跳过)
|
|
15
|
+
|
|
16
|
+
开工前,用不超过 6 行把目标说清楚:
|
|
17
|
+
|
|
18
|
+
- **目标**:一句话描述「做完之后世界变成什么样」,必须是结果,不是动作。
|
|
19
|
+
- ✅ 「本地能一条命令跑通测试并全绿」
|
|
20
|
+
- ❌ 「去看看测试」
|
|
21
|
+
- **验收标准**:2-4 条可检验的条件,每条都要能用一次工具调用或一段真实输出来证明。
|
|
22
|
+
- **不做什么**:明确本次不处理的范围,避免越做越大。
|
|
23
|
+
|
|
24
|
+
信息不足时,**先问最关键的 1-3 个问题再动手**,不要靠猜设定验收标准。用户已经给足信息时不要明知故问。
|
|
25
|
+
|
|
26
|
+
## 2. 落成清单(todo_write)
|
|
27
|
+
|
|
28
|
+
目标确认后立刻调 `todo_write` 建清单:
|
|
29
|
+
|
|
30
|
+
- 每条对应一个可独立验证的步骤,通常 3-8 条;一步就能做完的事不用本技能。
|
|
31
|
+
- 步骤顺序即执行顺序;有依赖关系的写在后面。
|
|
32
|
+
- 验收标准本身也要有对应条目(例如「跑一次测试确认全绿」),否则收尾时没有证据。
|
|
33
|
+
|
|
34
|
+
## 3. 执行并更新状态
|
|
35
|
+
|
|
36
|
+
- 开始某条 → 标 `in_progress`,**同时只能有一条**。
|
|
37
|
+
- 某条做完 → 立刻标 `completed`,不要攒到最后一起改。
|
|
38
|
+
- 范围变化(发现某步不必做、或必须新增一步)→ **重写清单**并用一句话说明为什么变,不要偷偷跳过。
|
|
39
|
+
- 清单还有未完成项时,**不允许**用纯文本汇报进度然后停手。
|
|
40
|
+
|
|
41
|
+
## 4. 收尾验收
|
|
42
|
+
|
|
43
|
+
所有条目完成后,按验收标准逐条给结论,每条都要带**真实证据**(工具返回的输出、文件路径、退出码、页面变化),不要只写「已完成」。
|
|
44
|
+
|
|
45
|
+
格式:
|
|
46
|
+
|
|
47
|
+
```
|
|
48
|
+
目标:<一句话>
|
|
49
|
+
- [x] 验收1:<结论> —— 证据:<真实输出摘要 / 文件路径>
|
|
50
|
+
- [x] 验收2:<结论> —— 证据:<...>
|
|
51
|
+
- [ ] 验收3:未达成 —— 原因:<真实原因>,建议:<下一步>
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
有未达成项时**如实列出**并给出下一步,不要为了收尾而宣称成功。
|
|
55
|
+
|
|
56
|
+
## 与其他机制的关系
|
|
57
|
+
|
|
58
|
+
- 目标很大、可以并行或上下文很重 → 子任务用 `delegate_subagent` 委派,跨回合的长任务用 `task_run`(有先后关系用 `dependsOn` 串起来),主线仍然靠本清单跟踪。
|
|
59
|
+
- 用户只想先看方案、暂时不执行 → 让他切到输入框底部的 **Plan 模式**,那里会产出可确认的计划;本技能负责的是「认领目标并做完」。
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: loop
|
|
3
|
+
displayName: 循环执行
|
|
4
|
+
description: 按固定间隔或自选节奏重复执行同一件事(如「每 5 分钟查一次构建状态」)。支持本轮内有限次循环和 task_run 后台长循环,必须先定好间隔、终止条件与最大次数。
|
|
5
|
+
priority: 500
|
|
6
|
+
tags: [workflow, automation, loop]
|
|
7
|
+
disable-model-invocation: true
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# 循环执行(Loop)
|
|
11
|
+
|
|
12
|
+
用法:`/loop [间隔] <要重复做的事>`,间隔写在前后都认。
|
|
13
|
+
|
|
14
|
+
- `/loop 5m 查一下构建状态`
|
|
15
|
+
- `/loop 每 30 秒看一次日志有没有 ERROR`
|
|
16
|
+
- `/loop 盯着下载进度`(没写间隔 = 由你自己定节奏)
|
|
17
|
+
|
|
18
|
+
## 1. 先定三要素(缺一不可)
|
|
19
|
+
|
|
20
|
+
开始循环前必须确定并**用一句话告诉用户**:
|
|
21
|
+
|
|
22
|
+
| 要素 | 规则 |
|
|
23
|
+
| --- | --- |
|
|
24
|
+
| **间隔** | 用户给了就按用户的;没给就自己选一个合理值(秒级任务 10-30s,分钟级 1-5m)并说明理由 |
|
|
25
|
+
| **终止条件** | 「什么情况下算成功、可以停」。用户没说就自己拟一个并告知,例如「状态变成 success 就停」 |
|
|
26
|
+
| **最大次数** | 硬上限,默认不超过 10 轮。到达上限必须停下汇报,不允许自动续命 |
|
|
27
|
+
|
|
28
|
+
用户只说「一直盯着」而没有终止条件时,仍然要自己设一个上限并说明——**绝不允许无限循环**。
|
|
29
|
+
|
|
30
|
+
## 2. 选机制
|
|
31
|
+
|
|
32
|
+
| 场景 | 机制 |
|
|
33
|
+
| --- | --- |
|
|
34
|
+
| 总时长在几分钟内、用户想实时看到每轮结果 | **本轮内循环**(见下方 A) |
|
|
35
|
+
| 单轮间隔长、总时长跨越本次回答、或用户要继续聊别的 | **后台任务循环**(见下方 B) |
|
|
36
|
+
|
|
37
|
+
判断不了时问用户一句:「要我现在盯着(几分钟内出结果),还是放后台跑?」
|
|
38
|
+
|
|
39
|
+
### A. 本轮内循环
|
|
40
|
+
|
|
41
|
+
一轮 = 等待 → 执行 → 汇报变化。等待用 `local_exec_shell`,按真实 shell 选方言:
|
|
42
|
+
|
|
43
|
+
| shell | 等待命令 |
|
|
44
|
+
| --- | --- |
|
|
45
|
+
| PowerShell(Windows 默认) | `Start-Sleep -Seconds 30` |
|
|
46
|
+
| bash / zsh | `sleep 30` |
|
|
47
|
+
|
|
48
|
+
- 等待命令的 `timeout` 参数要大于间隔本身,否则会被判超时掐断。
|
|
49
|
+
- 每轮只汇报**变化**(「第 3 轮:状态仍为 running,日志新增 2 行报错」),不要把同样的内容重复贴一遍。
|
|
50
|
+
- 达成终止条件、或到达最大次数 → 立刻停止并给结论。
|
|
51
|
+
|
|
52
|
+
### B. 后台任务循环(task_run)
|
|
53
|
+
|
|
54
|
+
把**整个循环**写进一次 `task_run` 的 `task` 里——后台任务是独立的推理循环,看不到本对话,指令必须自包含,至少包含:
|
|
55
|
+
|
|
56
|
+
1. 要重复执行的具体动作(含命令、路径、判断依据)
|
|
57
|
+
2. 每轮间隔与等待方式
|
|
58
|
+
3. 终止条件与最大轮数
|
|
59
|
+
4. 结束时要带回什么结论
|
|
60
|
+
|
|
61
|
+
发起后告诉用户 `taskId`,以及三个跟进方式:`task_status` 查进度、`task_result` 取最终结论、`task_send` 中途改范围或加信息;任务面板里可以随时停止。
|
|
62
|
+
|
|
63
|
+
**不要**用 `task_run` 起一个「只睡一觉然后再起一个任务」的链条来模拟定时器——一次任务里跑完所有轮次。
|
|
64
|
+
|
|
65
|
+
## 3. 纪律
|
|
66
|
+
|
|
67
|
+
- **先跑一轮再进入等待**:用户要的是结果,不是先等 5 分钟。
|
|
68
|
+
- **不重复起同一个循环**:新建之前先用 `task_status` 看有没有同目的的任务还在跑;有就复用或先停掉。
|
|
69
|
+
- **连续 2 轮同样失败就停**:把真实报错告诉用户并给建议,不要一直重试刷屏。
|
|
70
|
+
- **每轮都必须真调工具**:没有真实 tool_call 就等于这一轮没跑,不允许凭空写「第 2 轮仍在进行」。
|
|
71
|
+
- 用户说停 → 立刻停止本轮内循环;后台任务则说明用 `task_send` 还是任务面板停止,不要再起新的循环。
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: create-skill
|
|
3
|
+
displayName: 创建技能
|
|
4
|
+
description: 按本产品的 SKILL.md 规范创建一个新的本地技能,含目录结构、frontmatter 字段、写作要求。写到项目 skills/ 目录后自动出现在 Skill 设置与 / 菜单。用户说「做个技能 / 写个 skill / 把这套流程固化下来」时使用。
|
|
5
|
+
priority: 500
|
|
6
|
+
tags: [skill, authoring, meta]
|
|
7
|
+
disable-model-invocation: true
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# 创建技能(Create Skill)
|
|
11
|
+
|
|
12
|
+
技能 = 一个目录 + 一个 `SKILL.md`。它把一套「反复要用、你本来不知道」的做法固化下来,之后用户输入 `/技能名` 就能一次性加载。
|
|
13
|
+
|
|
14
|
+
## 1. 先问清楚(最多 3 个问题)
|
|
15
|
+
|
|
16
|
+
动手前确认这几件事,**已经能从上下文推断出来的不要问**:
|
|
17
|
+
|
|
18
|
+
1. **做什么、什么时候用**:具体任务是什么?用户在什么情况下会想触发它?
|
|
19
|
+
2. **要不要脚本**:是否需要附带可执行脚本(`scripts/` 目录),还是纯文字指引就够。
|
|
20
|
+
3. **有没有硬性格式**:输出模板、命令写法、既有范例是否要照搬。
|
|
21
|
+
|
|
22
|
+
用户给了原话(命令、话术、模板)时,**原样保留**,不要改写或扩写。
|
|
23
|
+
|
|
24
|
+
## 2. 目录结构
|
|
25
|
+
|
|
26
|
+
```
|
|
27
|
+
<project>/
|
|
28
|
+
└── skills/
|
|
29
|
+
└── skill-name/
|
|
30
|
+
├── SKILL.md # 必需,主指令
|
|
31
|
+
├── reference.md # 可选,详细资料(SKILL.md 里链过去,按需读)
|
|
32
|
+
└── scripts/ # 可选,可执行脚本
|
|
33
|
+
└── run.py
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
正文里用 `{scripts}/run.py` 引用脚本,加载时会自动替换成该技能 `scripts/` 的绝对路径,**不要**写死用户机器上的路径。
|
|
37
|
+
|
|
38
|
+
## 3. frontmatter 字段
|
|
39
|
+
|
|
40
|
+
```markdown
|
|
41
|
+
---
|
|
42
|
+
name: my-skill
|
|
43
|
+
displayName: 我的技能
|
|
44
|
+
description: 做什么 + 什么时候用,一句话说清。
|
|
45
|
+
priority: 500
|
|
46
|
+
tags: [workflow]
|
|
47
|
+
conditions: [tool:local_exec_shell]
|
|
48
|
+
match: any
|
|
49
|
+
disable-model-invocation: true
|
|
50
|
+
---
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
| 字段 | 说明 |
|
|
54
|
+
| --- | --- |
|
|
55
|
+
| `name` | **必需**。小写字母、数字、连字符,≤64 字符,不能出现连续连字符。用户用 `/name` 触发 |
|
|
56
|
+
| `displayName` | 可选。给人看的名字(可中文),`/` 菜单里显示在前面 |
|
|
57
|
+
| `description` | **必需**。≤1024 字符,第三人称,同时写清 **做什么** 和 **什么时候用** |
|
|
58
|
+
| `priority` | 默认 500。**≥850** 的技能正文会常驻系统提示词(eager 层),其余只进目录按需加载。新技能保持 500,不要随便抬高抢占系统提示词 |
|
|
59
|
+
| `tags` | 可选,仅作分类信息,不参与筛选 |
|
|
60
|
+
| `conditions` | 可选。满足条件才加载,形如 `tool:local_exec_shell`(本轮暴露了该工具)或 `has-tools`。不写 = 任何时候都可加载 |
|
|
61
|
+
| `match` | `any`(默认,命中任一条件即可)或 `all`(必须全部命中) |
|
|
62
|
+
| `disable-model-invocation` | `true` = 只能由用户 `/name` 手动触发,不进模型目录。**新技能默认写 true**,只有确实希望模型自己判断要不要用时才省略 |
|
|
63
|
+
|
|
64
|
+
## 4. 正文写作要求
|
|
65
|
+
|
|
66
|
+
- **假设读者很聪明**:只写它不可能知道的东西(你们的命令、路径、约定、坑),不要科普常识。
|
|
67
|
+
- **控制在 500 行以内**;细节资料拆到 `reference.md`,在 SKILL.md 里链一层过去。
|
|
68
|
+
- **给默认选项**,不要罗列「你可以用 A 也可以用 B 也可以用 C」;有例外就写清什么时候走例外。
|
|
69
|
+
- **术语前后一致**,同一个东西只用一个叫法。
|
|
70
|
+
- **不要写时效性内容**(「2026 年 8 月之前用旧接口」),过期就是错的。
|
|
71
|
+
- 路径统一用正斜杠 `scripts/run.py`。
|
|
72
|
+
|
|
73
|
+
好的结构通常是:用途一句话 → 触发条件 → 步骤(可编号,带真实命令)→ 输出格式模板 → 常见错误与对策。
|
|
74
|
+
|
|
75
|
+
## 5. 落地与验证(必须做完)
|
|
76
|
+
|
|
77
|
+
1. **写文件**:用 `local_write_file`(`createDirs: true`)把 `SKILL.md`(及脚本)写到当前项目的 `skills/<name>/`。项目模式就是项目根下的 `skills/`;没有绑定项目就写到当前工作目录的 `skills/`。用户指定了别的位置则尊重用户。写完后把绝对路径告诉他。
|
|
78
|
+
2. **自动可见**:写到 `skills/<name>/SKILL.md` 后,技能会自动出现在侧栏 **Skill 设置** 列表和输入框 `/` 菜单里,**不要**再让用户手动走「导入本地技能」。只有技能写在 `skills/` 以外的目录时,才提示用户到 Skill 设置里导入。
|
|
79
|
+
3. **验证**:让用户在输入框里敲 `/`,确认新技能出现在「指定运行的本地技能」分组里,再用 `/name` 真实触发一次,看行为是否符合预期。
|
|
80
|
+
4. 不符合预期 → 改 `SKILL.md` 后立刻生效(同目录会重新解析),不要靠在对话里补充说明来打补丁。
|
|
81
|
+
|
|
82
|
+
## 6. 反模式
|
|
83
|
+
|
|
84
|
+
- ❌ 名字含糊:`helper`、`utils`、`tools`;✅ 具体:`review-pr`、`export-well-logs`
|
|
85
|
+
- ❌ description 写成第一人称「我可以帮你…」;✅ 「解析 CSV 销售文件并输出统计报告。用户提到 CSV / 销售报表导出时使用。」
|
|
86
|
+
- ❌ 把大段通用编程知识抄进正文,挤占上下文
|
|
87
|
+
- ❌ `priority` 随手写 900 让正文常驻系统提示词
|
|
88
|
+
- ❌ 写死 `C:\Users\xxx\skills\...` 这类绝对路径,换台机器就失效
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: plan-mode
|
|
3
|
+
description: Loaded only in Plan mode. The agent must research with read-only tools and produce a structured plan, without executing any write/action.
|
|
4
|
+
priority: 950
|
|
5
|
+
tags: [mode, planning]
|
|
6
|
+
conditions: [plan-mode]
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# 计划模式(Plan Mode)
|
|
10
|
+
|
|
11
|
+
你现在处于**计划模式**。本模式的唯一目标是:先把「要做什么、怎么做」想清楚并写成一份结构化计划,
|
|
12
|
+
交给用户确认。**你现在不能执行任何会改变环境的动作。**
|
|
13
|
+
|
|
14
|
+
## 硬性约束
|
|
15
|
+
|
|
16
|
+
1. **只读调研只服务于计划**:本轮只暴露了只读工具(读文件、列卡片、查配置、探活等)。这些工具只能用于收集
|
|
17
|
+
**制定计划所需的上下文**,不能用于完成用户目标任务本身,绝不要凭空假设。
|
|
18
|
+
- 先判断工具调用的目的:如果调用结果会直接构成用户要的最终答案,就是在执行任务;Plan 模式下不要调用。
|
|
19
|
+
如果调用结果只是帮助你理解项目结构、已有卡片、配置约束或待办上下文,才属于计划调研。
|
|
20
|
+
- 例:用户说“检查我的电脑配置”时,读取 CPU/内存/磁盘真实信息就是执行任务;Plan 模式应写出稍后要执行的
|
|
21
|
+
Windows PowerShell/CIM 步骤,而不是先读取系统信息。
|
|
22
|
+
- 只读文件工具不是跨平台系统信息接口;除非路径来自用户明确提供或项目真实文件,**不要猜测读取**
|
|
23
|
+
`/proc/cpuinfo`、`/proc/meminfo`、`/sys/...`、`/etc/...` 这类操作系统专属路径。
|
|
24
|
+
- 必须先看系统提示里的「本地运行环境」。如果宿主系统是 Windows,计划里应使用 PowerShell / CIM
|
|
25
|
+
命令(如 `Get-CimInstance Win32_Processor`、`Get-CimInstance Win32_PhysicalMemory`),不要写
|
|
26
|
+
Linux/macOS 命令(`/proc`、`sysctl`、`lshw` 等)。
|
|
27
|
+
2. **禁止执行**:本轮**没有**暴露 shell、写文件、replay 卡片、MCP 等任何有副作用的工具,也不要尝试调用。
|
|
28
|
+
即使用户直接说"帮我看一下 / 跑一下 / 执行",在计划模式下也**只输出计划**,把要执行的命令写进计划步骤里。
|
|
29
|
+
3. **不要先答应再改口**:开场**不要**写"好的,我来帮你查看/执行…"这类承诺执行的话,也不要为"不能执行"道歉。
|
|
30
|
+
直接进入调研(如需要)和计划本身。
|
|
31
|
+
4. **不要假装已执行**:不要写"已完成 / 已修改 / 运行结果如下"这类话,因为你本轮不会真正执行。
|
|
32
|
+
|
|
33
|
+
## 输出格式:plan 代码块(必须遵守)
|
|
34
|
+
|
|
35
|
+
计划的**步骤清单必须**放在一个 `plan` 围栏代码块里,前端会把它渲染成可视化的 todolist。格式:
|
|
36
|
+
|
|
37
|
+
```plan
|
|
38
|
+
# 一句话计划标题
|
|
39
|
+
- [ ] 第一步:足够具体、可直接执行,标注用到的工具/命令/文件
|
|
40
|
+
- [ ] 第二步:……
|
|
41
|
+
- [ ] (可选)第三步:……
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
规则:
|
|
45
|
+
|
|
46
|
+
- 每个步骤一行,以 `- [ ] ` 开头;步骤要可执行、有顺序,通常 2-6 步。
|
|
47
|
+
- 标题行以 `# ` 开头,概括这份计划。
|
|
48
|
+
- 代码块里**只放**标题和步骤,不要放风险、问题等其他内容。
|
|
49
|
+
|
|
50
|
+
`plan` 代码块之后,再用普通 Markdown 补充(按需裁剪,保持简短):
|
|
51
|
+
|
|
52
|
+
- **风险与注意点**:可能出错、需要谨慎或不可逆的地方。
|
|
53
|
+
- **待确认问题**:任何影响方案的歧义或缺失信息(最多问最关键的 1-3 个)。
|
|
54
|
+
|
|
55
|
+
## 收尾
|
|
56
|
+
|
|
57
|
+
计划末尾用一句话提示用户:确认无误后,点击「开始执行计划」或切换到 Agent 模式,
|
|
58
|
+
我会按计划实际操作。若信息不足,请先回答上面的待确认问题。
|