更新 tool 的传入参数,指定传入关键字名称

This commit is contained in:
2026-05-11 18:14:57 +08:00
parent 59de5c672f
commit 0d5435022a
3 changed files with 38 additions and 103 deletions
+20 -48
View File
@@ -2,7 +2,6 @@ import { tool } from "@opencode-ai/plugin";
import { join, posix } from "node:path";
import { config } from "../../src/config.js";
import { ResultReferenceStore } from "../../src/results/store.js";
import { ToolSessionContextStore } from "../../src/session/toolContextStore.js";
import {
atomicWriteFileWithHistory,
@@ -11,12 +10,8 @@ import {
} from "../../src/utils/fileStore.js";
import { sanitizePersistentLine } from "../../src/utils/persistencePolicy.js";
const resultStore = new ResultReferenceStore();
const toolContextStore = new ToolSessionContextStore();
const initializePromise = Promise.all([
resultStore.initialize(),
toolContextStore.initialize(),
]);
const initializePromise = toolContextStore.initialize();
const SKILLS_ROOT_DIR = ".opencode/skills";
// learned skill 与正式技能树同路径组织,但历史版本单独落到 data/history/skills 下。
const SKILLS_HISTORY_DIR = join(config.PERSISTENCE_HISTORY_DIR, "skills");
@@ -29,23 +24,14 @@ export default tool({
args: {
reason: tool.schema
.string()
.describe("Why this workflow or method should be learned for future reuse."),
.describe(
"The reusable workflow or method pattern to persist for future reuse, written as one concise sentence.",
),
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()
.describe("A reusable workflow pattern written as one concise bullet-like sentence."),
signal_type: tool.schema
.string()
.describe("Signal type, e.g. validated_workflow, successful_complex_convergence, analysis_method, tool_recovery_pattern."),
confidence: tool.schema
.number()
.describe("Confidence between 0 and 1. Only very high-confidence patterns are stored as learned skills."),
result_refs: tool.schema
.array(tool.schema.string())
.optional()
.describe("Optional authorized result_ref list used only for evidence validation before persisting the skill."),
.describe(
"Target skill directory path relative to .opencode/skills, for example analytics/simulation-analysis/leakage or platform/governance-observability/meta.",
),
},
async execute(args, context) {
await initializePromise;
@@ -59,39 +45,19 @@ export default tool({
ok: true,
kind: "skill",
decision: "rejected",
detail: "invalid skill_path; expected a relative path under .opencode/skills",
detail:
"invalid skill_path; expected a relative path under .opencode/skills",
});
}
const pattern = sanitizePersistentLine(args.pattern, 320);
const pattern = sanitizePersistentLine(args.reason, 320);
if (!pattern) {
return JSON.stringify({
ok: true,
kind: "skill",
decision: "rejected",
detail: "pattern rejected by persistence policy",
detail: "reason rejected by persistence policy",
});
}
if (args.confidence < 0.85) {
return JSON.stringify({
ok: true,
kind: "skill",
decision: "rejected",
detail: "only very high-confidence patterns can be stored as skills",
});
}
if (args.result_refs?.length) {
await Promise.all(
args.result_refs.map(async (resultRef) => {
const record = await resultStore.peekAuthorized(resultRef, {
actorKey: sessionContext.actorKey,
projectId: sessionContext.projectId,
});
if (!record) {
throw new Error(`unauthorized or missing result_ref: ${resultRef}`);
}
}),
);
}
const result = await appendLearnedSkillPattern(skillPath, pattern);
return JSON.stringify({
@@ -104,10 +70,14 @@ export default tool({
},
});
const appendLearnedSkillPattern = async (skillPath: string, pattern: string) => {
const appendLearnedSkillPattern = async (
skillPath: string,
pattern: string,
) => {
return serializeWrite(async () => {
const target = join(SKILLS_ROOT_DIR, skillPath, "SKILL.md");
const current = (await readTextFile(target)) ?? defaultLearnedSkill(skillPath);
const current =
(await readTextFile(target)) ?? defaultLearnedSkill(skillPath);
const existingPatterns = extractLearnedPatterns(current);
if (existingPatterns.includes(pattern)) {
return { changed: false, target };
@@ -155,7 +125,9 @@ version: 1.0.0
`;
const normalizeSkillPath = (rawSkillPath: string) => {
const normalized = posix.normalize(rawSkillPath.trim().replace(/^\/+|\/+$/g, ""));
const normalized = posix.normalize(
rawSkillPath.trim().replace(/^\/+|\/+$/g, ""),
);
if (!normalized || normalized === "." || normalized.startsWith("..")) {
return null;
}