106 lines
3.4 KiB
TypeScript
106 lines
3.4 KiB
TypeScript
import { tool } from "@opencode-ai/plugin";
|
|
|
|
import { SkillStore } from "../../src/skills/store.js";
|
|
import { ToolSessionContextStore } from "../../src/session/toolContextStore.js";
|
|
|
|
const toolContextStore = new ToolSessionContextStore();
|
|
const initializePromise = toolContextStore.initialize();
|
|
const skillStore = new SkillStore();
|
|
|
|
export default tool({
|
|
description:
|
|
"维护已验证、可复用、非敏感的 workflow 或方法模式。支持 list、append_pattern、remove_pattern、write_reference、remove_reference。",
|
|
args: {
|
|
action: tool.schema
|
|
.enum([
|
|
"list",
|
|
"append_pattern",
|
|
"remove_pattern",
|
|
"write_reference",
|
|
"remove_reference",
|
|
])
|
|
.describe("Skill maintenance operation."),
|
|
reason: tool.schema
|
|
.string()
|
|
.describe(
|
|
"Why this skill maintenance action is justified for future reuse.",
|
|
),
|
|
skill_path: tool.schema
|
|
.string()
|
|
.describe(
|
|
"Target skill directory path relative to .opencode/skills, for example analytics/simulation-analysis/leakage or platform/governance-observability/meta.",
|
|
),
|
|
pattern: tool.schema
|
|
.string()
|
|
.optional()
|
|
.describe("Pattern text used by append_pattern."),
|
|
target_id: tool.schema
|
|
.string()
|
|
.optional()
|
|
.describe("Stable learned pattern id used by remove_pattern."),
|
|
file_path: tool.schema
|
|
.string()
|
|
.optional()
|
|
.describe("Reference file path under references/, such as references/bottleneck-notes.md."),
|
|
content: tool.schema
|
|
.string()
|
|
.optional()
|
|
.describe("Reference markdown body used by write_reference."),
|
|
},
|
|
async execute(args, context) {
|
|
await initializePromise;
|
|
const sessionContext = await toolContextStore.read(context.sessionID);
|
|
if (!sessionContext) {
|
|
throw new Error(`session context not found for ${context.sessionID}`);
|
|
}
|
|
if (sessionContext.allowLearningWrite === false && args.action !== "list") {
|
|
return JSON.stringify({
|
|
ok: true,
|
|
kind: "skill",
|
|
decision: "rejected",
|
|
detail: "skill writes are disabled for this session",
|
|
});
|
|
}
|
|
if (args.action === "list") {
|
|
const result = await skillStore.list(args.skill_path);
|
|
if (!result) {
|
|
return JSON.stringify({
|
|
ok: true,
|
|
kind: "skill",
|
|
decision: "rejected",
|
|
detail:
|
|
"invalid skill_path; expected a relative path under .opencode/skills",
|
|
});
|
|
}
|
|
return JSON.stringify({
|
|
ok: true,
|
|
kind: "skill",
|
|
decision: "accepted",
|
|
detail: "skill listed",
|
|
...result,
|
|
});
|
|
}
|
|
|
|
const result =
|
|
args.action === "append_pattern"
|
|
? await skillStore.appendPattern(args.skill_path, args.pattern ?? "")
|
|
: args.action === "remove_pattern"
|
|
? await skillStore.removePattern(args.skill_path, args.target_id ?? "")
|
|
: args.action === "write_reference"
|
|
? await skillStore.writeReference(
|
|
args.skill_path,
|
|
args.file_path ?? "",
|
|
args.content ?? "",
|
|
)
|
|
: await skillStore.removeReference(args.skill_path, args.file_path ?? "");
|
|
|
|
return JSON.stringify({
|
|
ok: true,
|
|
kind: "skill",
|
|
decision: result.changed ? "accepted" : "rejected",
|
|
detail: result.detail,
|
|
target: result.target,
|
|
});
|
|
},
|
|
});
|