feat(hooks): track model-skill-injector in workspace repo
This commit is contained in:
39
hooks/model-skill-injector/HOOK.md
Normal file
39
hooks/model-skill-injector/HOOK.md
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
---
|
||||||
|
name: model-skill-injector
|
||||||
|
description: "Injects model-specific best-practice context into bootstrap depending on the active LLM (Anthropic vs OpenAI)"
|
||||||
|
metadata:
|
||||||
|
{
|
||||||
|
"openclaw": {
|
||||||
|
"emoji": "🧠",
|
||||||
|
"events": ["agent:bootstrap"],
|
||||||
|
"requires": { "config": ["workspace.dir"] }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
---
|
||||||
|
|
||||||
|
# model-skill-injector
|
||||||
|
|
||||||
|
Fires on `agent:bootstrap` and appends a model-family-specific best-practices
|
||||||
|
hint file to the bootstrap context, so the agent always knows which tool-design
|
||||||
|
rules apply for the currently active model — without bloating the base system
|
||||||
|
prompt.
|
||||||
|
|
||||||
|
## What it does
|
||||||
|
|
||||||
|
1. Reads the active model string from the event config (`agents.defaults.model.primary`).
|
||||||
|
2. Detects the model family by model name only:
|
||||||
|
- **Anthropic** — any model containing `claude`
|
||||||
|
- **OpenAI** — any model containing `gpt`
|
||||||
|
3. Injects the corresponding hint file from the workspace `skills/llm-tool-best-practices/` folder as a virtual bootstrap file named `MODEL_HINTS.md`.
|
||||||
|
4. If the model is unknown / not matched, injects a neutral fallback noting both families.
|
||||||
|
|
||||||
|
## Files
|
||||||
|
|
||||||
|
- Anthropic hints: `skills/llm-tool-best-practices/hints/anthropic.md`
|
||||||
|
- OpenAI hints: `skills/llm-tool-best-practices/hints/openai.md`
|
||||||
|
- Fallback: `skills/llm-tool-best-practices/hints/generic.md`
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
- Runs read-only; never writes files.
|
||||||
|
- Only injects the targeted section — does not load the full best-practices doc each turn.
|
||||||
68
hooks/model-skill-injector/handler.ts
Normal file
68
hooks/model-skill-injector/handler.ts
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
import * as fs from "fs";
|
||||||
|
import * as path from "path";
|
||||||
|
|
||||||
|
const handler = async (event) => {
|
||||||
|
if (event.type !== "agent" || event.action !== "bootstrap") return;
|
||||||
|
|
||||||
|
const cfg = event.context?.cfg;
|
||||||
|
const workspaceDir = event.context?.workspaceDir;
|
||||||
|
if (!workspaceDir) return;
|
||||||
|
|
||||||
|
// Resolve active model string
|
||||||
|
const modelRaw: string =
|
||||||
|
(typeof cfg?.agents?.defaults?.model === "string"
|
||||||
|
? cfg.agents.defaults.model
|
||||||
|
: cfg?.agents?.defaults?.model?.primary) ?? "";
|
||||||
|
|
||||||
|
const model = modelRaw.toLowerCase();
|
||||||
|
|
||||||
|
// Detect model family by model name (provider-agnostic)
|
||||||
|
let family: "anthropic" | "openai" | "generic";
|
||||||
|
if (model.includes("claude")) {
|
||||||
|
family = "anthropic";
|
||||||
|
} else if (model.includes("gpt")) {
|
||||||
|
family = "openai";
|
||||||
|
} else {
|
||||||
|
family = "generic";
|
||||||
|
}
|
||||||
|
|
||||||
|
const hintsDir = path.join(
|
||||||
|
workspaceDir,
|
||||||
|
"skills",
|
||||||
|
"llm-tool-best-practices",
|
||||||
|
"hints"
|
||||||
|
);
|
||||||
|
const hintsFile = path.join(hintsDir, `${family}.md`);
|
||||||
|
|
||||||
|
let content: string;
|
||||||
|
try {
|
||||||
|
content = fs.readFileSync(hintsFile, "utf8");
|
||||||
|
} catch {
|
||||||
|
// Hints file missing — skip silently
|
||||||
|
console.warn(
|
||||||
|
`[model-skill-injector] Hints file not found: ${hintsFile} (family=${family}, model=${modelRaw})`
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inject as a virtual bootstrap file
|
||||||
|
const bootstrapFiles = event.context?.bootstrapFiles;
|
||||||
|
if (!Array.isArray(bootstrapFiles)) return;
|
||||||
|
|
||||||
|
// Remove any previous injection (idempotent on re-runs)
|
||||||
|
const existing = bootstrapFiles.findIndex((f) => f.name === "MODEL_HINTS.md");
|
||||||
|
if (existing !== -1) bootstrapFiles.splice(existing, 1);
|
||||||
|
|
||||||
|
bootstrapFiles.push({
|
||||||
|
name: "MODEL_HINTS.md",
|
||||||
|
content,
|
||||||
|
// Mark virtual so it doesn't try to read from disk
|
||||||
|
virtual: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
`[model-skill-injector] Injected ${family} hints (model=${modelRaw})`
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default handler;
|
||||||
Reference in New Issue
Block a user