mirror of
https://github.com/getcompanion-ai/co-mono.git
synced 2026-04-19 17:04:41 +00:00
fix(coding-agent): ignore unknown skill frontmatter fields
This commit is contained in:
parent
fcfbc82ec2
commit
d0679dcfc0
5 changed files with 6 additions and 42 deletions
|
|
@ -24,6 +24,7 @@ read README.md, then ask which module(s) to work on. Based on the answer, read t
|
||||||
- Note: `npm run check` does not run tests.
|
- Note: `npm run check` does not run tests.
|
||||||
- NEVER run: `npm run dev`, `npm run build`, `npm test`
|
- NEVER run: `npm run dev`, `npm run build`, `npm test`
|
||||||
- Only run specific tests if user instructs: `npm test -- test/specific.test.ts`
|
- Only run specific tests if user instructs: `npm test -- test/specific.test.ts`
|
||||||
|
- Run tests from the package root, not the repo root.
|
||||||
- When writing tests, run them, identify issues in either the test or implementation, and iterate until fixed.
|
- When writing tests, run them, identify issues in either the test or implementation, and iterate until fixed.
|
||||||
- NEVER commit unless user asks
|
- NEVER commit unless user asks
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
|
- Ignored unknown skill frontmatter fields when loading skills
|
||||||
- Fixed `/reload` not picking up changes in global settings.json ([#1241](https://github.com/badlogic/pi-mono/issues/1241))
|
- Fixed `/reload` not picking up changes in global settings.json ([#1241](https://github.com/badlogic/pi-mono/issues/1241))
|
||||||
- Fixed Unix bash detection to fall back to PATH lookup when `/bin/bash` is unavailable, including Termux setups ([#1230](https://github.com/badlogic/pi-mono/pull/1230) by [@VaclavSynacek](https://github.com/VaclavSynacek))
|
- Fixed Unix bash detection to fall back to PATH lookup when `/bin/bash` is unavailable, including Termux setups ([#1230](https://github.com/badlogic/pi-mono/pull/1230) by [@VaclavSynacek](https://github.com/VaclavSynacek))
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -176,7 +176,8 @@ Pi validates skills against the Agent Skills standard. Most issues produce warni
|
||||||
- Name exceeds 64 characters or contains invalid characters
|
- Name exceeds 64 characters or contains invalid characters
|
||||||
- Name starts/ends with hyphen or has consecutive hyphens
|
- Name starts/ends with hyphen or has consecutive hyphens
|
||||||
- Description exceeds 1024 characters
|
- Description exceeds 1024 characters
|
||||||
- Unknown frontmatter fields
|
|
||||||
|
Unknown frontmatter fields are ignored.
|
||||||
|
|
||||||
**Exception:** Skills with missing description are not loaded.
|
**Exception:** Skills with missing description are not loaded.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,20 +5,6 @@ import { CONFIG_DIR_NAME, getAgentDir } from "../config.js";
|
||||||
import { parseFrontmatter } from "../utils/frontmatter.js";
|
import { parseFrontmatter } from "../utils/frontmatter.js";
|
||||||
import type { ResourceDiagnostic } from "./diagnostics.js";
|
import type { ResourceDiagnostic } from "./diagnostics.js";
|
||||||
|
|
||||||
/**
|
|
||||||
* Standard frontmatter fields per Agent Skills spec.
|
|
||||||
* See: https://agentskills.io/specification#frontmatter-required
|
|
||||||
*/
|
|
||||||
const ALLOWED_FRONTMATTER_FIELDS = new Set([
|
|
||||||
"name",
|
|
||||||
"description",
|
|
||||||
"license",
|
|
||||||
"compatibility",
|
|
||||||
"metadata",
|
|
||||||
"allowed-tools",
|
|
||||||
"disable-model-invocation",
|
|
||||||
]);
|
|
||||||
|
|
||||||
/** Max name length per spec */
|
/** Max name length per spec */
|
||||||
const MAX_NAME_LENGTH = 64;
|
const MAX_NAME_LENGTH = 64;
|
||||||
|
|
||||||
|
|
@ -91,19 +77,6 @@ function validateDescription(description: string | undefined): string[] {
|
||||||
return errors;
|
return errors;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Check for unknown frontmatter fields.
|
|
||||||
*/
|
|
||||||
function validateFrontmatterFields(keys: string[]): string[] {
|
|
||||||
const errors: string[] = [];
|
|
||||||
for (const key of keys) {
|
|
||||||
if (!ALLOWED_FRONTMATTER_FIELDS.has(key)) {
|
|
||||||
errors.push(`unknown frontmatter field "${key}"`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return errors;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface LoadSkillsFromDirOptions {
|
export interface LoadSkillsFromDirOptions {
|
||||||
/** Directory to scan for skills */
|
/** Directory to scan for skills */
|
||||||
dir: string;
|
dir: string;
|
||||||
|
|
@ -197,16 +170,9 @@ function loadSkillFromFile(
|
||||||
try {
|
try {
|
||||||
const rawContent = readFileSync(filePath, "utf-8");
|
const rawContent = readFileSync(filePath, "utf-8");
|
||||||
const { frontmatter } = parseFrontmatter<SkillFrontmatter>(rawContent);
|
const { frontmatter } = parseFrontmatter<SkillFrontmatter>(rawContent);
|
||||||
const allKeys = Object.keys(frontmatter);
|
|
||||||
const skillDir = dirname(filePath);
|
const skillDir = dirname(filePath);
|
||||||
const parentDirName = basename(skillDir);
|
const parentDirName = basename(skillDir);
|
||||||
|
|
||||||
// Validate frontmatter fields
|
|
||||||
const fieldErrors = validateFrontmatterFields(allKeys);
|
|
||||||
for (const error of fieldErrors) {
|
|
||||||
diagnostics.push({ type: "warning", message: error, path: filePath });
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate description
|
// Validate description
|
||||||
const descErrors = validateDescription(frontmatter.description);
|
const descErrors = validateDescription(frontmatter.description);
|
||||||
for (const error of descErrors) {
|
for (const error of descErrors) {
|
||||||
|
|
|
||||||
|
|
@ -65,19 +65,14 @@ describe("skills", () => {
|
||||||
expect(diagnostics.some((d: ResourceDiagnostic) => d.message.includes("description is required"))).toBe(true);
|
expect(diagnostics.some((d: ResourceDiagnostic) => d.message.includes("description is required"))).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should warn when unknown frontmatter fields are present", () => {
|
it("should ignore unknown frontmatter fields", () => {
|
||||||
const { skills, diagnostics } = loadSkillsFromDir({
|
const { skills, diagnostics } = loadSkillsFromDir({
|
||||||
dir: join(fixturesDir, "unknown-field"),
|
dir: join(fixturesDir, "unknown-field"),
|
||||||
source: "test",
|
source: "test",
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(skills).toHaveLength(1);
|
expect(skills).toHaveLength(1);
|
||||||
expect(
|
expect(diagnostics).toHaveLength(0);
|
||||||
diagnostics.some((d: ResourceDiagnostic) => d.message.includes('unknown frontmatter field "author"')),
|
|
||||||
).toBe(true);
|
|
||||||
expect(
|
|
||||||
diagnostics.some((d: ResourceDiagnostic) => d.message.includes('unknown frontmatter field "version"')),
|
|
||||||
).toBe(true);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should load nested skills recursively", () => {
|
it("should load nested skills recursively", () => {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue