fix: allow custom skill config bag

Co-authored-by: VACInc <3279061+VACInc@users.noreply.github.com>
This commit is contained in:
Peter Steinberger
2026-01-20 15:56:52 +00:00
parent 74f382f732
commit 76698ed296
5 changed files with 54 additions and 1 deletions

View File

@@ -72,6 +72,7 @@ Docs: https://docs.clawd.bot
- TUI: show generic empty-state text for searchable pickers. (#1201) — thanks @vignesh07.
- Doctor: canonicalize legacy session keys in session stores to prevent stale metadata. (#1169)
- CLI: centralize CLI command registration to keep fast-path routing and program wiring in sync. (#1207) — thanks @gumadeiras.
- Config: allow custom fields under `skills.entries.<name>.config` for skill credentials/config. (#1226) — thanks @VACInc. (fixes #1225)
## 2026.1.18-5

View File

@@ -155,6 +155,10 @@ Bundled/managed skills can be toggled and supplied with env values:
apiKey: "GEMINI_KEY_HERE",
env: {
GEMINI_API_KEY: "GEMINI_KEY_HERE"
},
config: {
endpoint: "https://example.invalid",
model: "nano-pro"
}
},
peekaboo: { enabled: true },
@@ -173,6 +177,7 @@ Rules:
- `enabled: false` disables the skill even if its bundled/installed.
- `env`: injected **only if** the variable isnt already set in the process.
- `apiKey`: convenience for skills that declare `metadata.clawdbot.primaryEnv`.
- `config`: optional bag for custom per-skill fields; custom keys must live here.
- `allowBundled`: optional allowlist for **bundled** skills only. If set, only
bundled skills in the list are eligible (managed/workspace skills unaffected).

View File

@@ -0,0 +1,46 @@
import { describe, expect, it } from "vitest";
import { ClawdbotSchema } from "./zod-schema.js";
describe("skills entries config schema", () => {
it("accepts custom fields under config", () => {
const res = ClawdbotSchema.safeParse({
skills: {
entries: {
"custom-skill": {
enabled: true,
config: {
url: "https://example.invalid",
token: "abc123",
},
},
},
},
});
expect(res.success).toBe(true);
});
it("rejects unknown top-level fields", () => {
const res = ClawdbotSchema.safeParse({
skills: {
entries: {
"custom-skill": {
url: "https://example.invalid",
},
},
},
});
expect(res.success).toBe(false);
if (res.success) return;
expect(
res.error.issues.some(
(issue) =>
issue.path.join(".") === "skills.entries.custom-skill" &&
issue.message.toLowerCase().includes("unrecognized"),
),
).toBe(true);
});
});

View File

@@ -2,7 +2,7 @@ export type SkillConfig = {
enabled?: boolean;
apiKey?: string;
env?: Record<string, string>;
[key: string]: unknown;
config?: Record<string, unknown>;
};
export type SkillsLoadConfig = {

View File

@@ -380,6 +380,7 @@ export const ClawdbotSchema = z
enabled: z.boolean().optional(),
apiKey: z.string().optional(),
env: z.record(z.string(), z.string()).optional(),
config: z.record(z.string(), z.unknown()).optional(),
})
.strict(),
)