mirror of
https://github.com/pbakaus/impeccable.git
synced 2026-09-19 01:26:29 +03:00
Update provider configs from official docs, add HARNESSES.md reference
Research each harness's official documentation to verify and correct
provider frontmatter configs. Remove Codex/Gemini body transforms that
targeted their commands systems, not skills.
- Add compatibility + metadata to Cursor and Agents (Copilot)
- Add allowed-tools to Pi
- Remove Codex $ARGNAME and Gemini {{args}} body transforms
- Add HARNESSES.md as source of truth for harness capabilities
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
6de73abf1b
commit
2d4c7899dd
@@ -1,6 +1,7 @@
|
||||
---
|
||||
name: frontend-design
|
||||
description: Create distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, artifacts, posters, or applications. Generates creative, polished code that avoids generic AI aesthetics.
|
||||
license: Apache 2.0. Based on Anthropic's frontend-design skill. See NOTICE.md for attribution.
|
||||
---
|
||||
|
||||
This skill guides creation of distinctive, production-grade frontend interfaces that avoid generic "AI slop" aesthetics. Implement real working code with exceptional attention to aesthetic details and creative choices.
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
# Harness Skills Capabilities Reference
|
||||
|
||||
Source of truth for what each AI coding harness supports in terms of agent skills.
|
||||
Used to inform provider configs in `scripts/lib/transformers/providers.js`.
|
||||
|
||||
Last verified: 2026-03-24
|
||||
|
||||
## Official Documentation
|
||||
|
||||
| Harness | Docs URL |
|
||||
|---------|----------|
|
||||
| Claude Code | https://code.claude.com/docs/en/skills |
|
||||
| Cursor | https://cursor.com/docs/context/skills |
|
||||
| Gemini CLI | https://geminicli.com/docs/cli/skills/ |
|
||||
| Codex CLI | https://developers.openai.com/codex/skills |
|
||||
| GitHub Copilot (Agents) | https://code.visualstudio.com/docs/copilot/customization/agent-skills |
|
||||
| Kiro | https://kiro.dev/docs/skills/ |
|
||||
| OpenCode | https://opencode.ai/docs/skills/ |
|
||||
| Pi | https://github.com/badlogic/pi-mono/blob/main/packages/coding-agent/docs/skills.md |
|
||||
|
||||
## Spec Compliance
|
||||
|
||||
All harnesses follow the [Agent Skills specification](https://agentskills.io/specification) to varying degrees. The spec defines these frontmatter fields: `name`, `description`, `license`, `compatibility`, `metadata`, `allowed-tools`.
|
||||
|
||||
Provider-specific extensions beyond the spec: `user-invocable`, `argument-hint`, `disable-model-invocation`, `allowed-tools` (extended syntax), `model`, `effort`, `context`, `agent`, `hooks`, `subtask`, `mcp`.
|
||||
|
||||
## Frontmatter Support
|
||||
|
||||
Fields marked with * are spec-standard. Others are provider extensions.
|
||||
|
||||
| Field | Claude Code | Cursor | Gemini | Codex | Copilot | Kiro | OpenCode | Pi |
|
||||
|-------|:-----------:|:------:|:------:|:-----:|:-------:|:----:|:--------:|:--:|
|
||||
| `name`* | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes |
|
||||
| `description`* | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes |
|
||||
| `license`* | Yes | Yes | Ignored | No | Yes | Yes | Yes | Yes |
|
||||
| `compatibility`* | Yes | Yes | Ignored | No | Yes | Yes | Yes | Yes |
|
||||
| `metadata`* | Yes | Yes | Ignored | No | Yes | Yes | Yes | Yes |
|
||||
| `allowed-tools`* | Yes | No | Ignored | No | No | No | Yes | Yes |
|
||||
| `user-invocable` | Yes | No | No | No | Yes | No | Yes | No |
|
||||
| `argument-hint` | Yes | No | No | No | Yes | No | Yes | No |
|
||||
| `disable-model-invocation` | Yes | Yes | No | No | Yes | No | Yes | Yes |
|
||||
| `model` | Yes | No | No | No | No | No | Yes | No |
|
||||
| `effort` | Yes | No | No | No | No | No | No | No |
|
||||
| `context` | Yes | No | No | No | No | No | No | No |
|
||||
| `agent` | Yes | No | No | No | No | No | Yes | No |
|
||||
| `hooks` | Yes | No | No | No | No | No | No | No |
|
||||
|
||||
Notes:
|
||||
- Gemini CLI validates only `name` and `description`; other spec fields are parsed but ignored.
|
||||
- Codex CLI uses a separate `agents/openai.yaml` sidecar for extended metadata (icons, branding, MCP tools, invocation control).
|
||||
- Kiro recognizes `user-invocable` and `disable-model-invocation` per community reports but does not formally document them.
|
||||
- Unknown fields are silently ignored by all harnesses.
|
||||
|
||||
## Skill Directory Structure
|
||||
|
||||
| Harness | Native directory | Also reads |
|
||||
|---------|-----------------|------------|
|
||||
| Claude Code | `.claude/skills/` | - |
|
||||
| Cursor | `.cursor/skills/` | `.agents/skills/`, `.claude/skills/`, `.codex/skills/` |
|
||||
| Gemini CLI | `.gemini/skills/` | `.agents/skills/` |
|
||||
| Codex CLI | `.agents/skills/` (primary) | - |
|
||||
| GitHub Copilot | `.github/skills/` | `.agents/skills/`, `.claude/skills/` |
|
||||
| Kiro | `.kiro/skills/` | - |
|
||||
| OpenCode | `.opencode/skills/` | `.agents/skills/`, `.claude/skills/` |
|
||||
| Pi | `.pi/skills/` | `.agents/skills/` |
|
||||
|
||||
All harnesses support the `{skill-name}/SKILL.md` directory structure with optional `reference/`, `scripts/`, and `assets/` subdirectories.
|
||||
|
||||
## Placeholder / Variable Substitution
|
||||
|
||||
Claude Code supports runtime variable substitution directly in SKILL.md bodies: `$ARGUMENTS`, `$0`-`$N`, `${CLAUDE_SKILL_DIR}`, `${CLAUDE_SESSION_ID}`. No other harness supports substitution in skills.
|
||||
|
||||
Some harnesses have separate "custom commands" systems (distinct from skills) with their own substitution:
|
||||
|
||||
| Harness | Command system | Substitution syntax |
|
||||
|---------|---------------|-------------------|
|
||||
| Gemini CLI | `.gemini/commands/` (TOML) | `{{args}}`, `!{shell}`, `@{file}` |
|
||||
| Codex CLI | `.codex/prompts/` | `$ARGNAME` |
|
||||
| OpenCode | `.opencode/commands/` | `$ARGUMENTS`, `$1`-`$N`, `` !`shell` `` |
|
||||
|
||||
Our build system handles cross-provider placeholders at compile time via `replacePlaceholders()` for `{{model}}`, `{{config_file}}`, `{{ask_instruction}}`, and `{{available_commands}}`.
|
||||
@@ -13,7 +13,7 @@ export const PROVIDERS = {
|
||||
provider: 'cursor',
|
||||
configDir: '.cursor',
|
||||
displayName: 'Cursor',
|
||||
frontmatterFields: ['license'],
|
||||
frontmatterFields: ['license', 'compatibility', 'metadata'],
|
||||
},
|
||||
'claude-code': {
|
||||
provider: 'claude-code',
|
||||
@@ -26,30 +26,18 @@ export const PROVIDERS = {
|
||||
configDir: '.gemini',
|
||||
displayName: 'Gemini',
|
||||
frontmatterFields: [],
|
||||
bodyTransform: (body, skill) => {
|
||||
if (skill.userInvocable) {
|
||||
return body.replace(/\{\{[^}]+\}\}/g, '{{args}}');
|
||||
}
|
||||
return body;
|
||||
},
|
||||
},
|
||||
codex: {
|
||||
provider: 'codex',
|
||||
configDir: '.codex',
|
||||
displayName: 'Codex',
|
||||
frontmatterFields: ['argument-hint', 'license'],
|
||||
bodyTransform: (body, skill) => {
|
||||
if (skill.userInvocable) {
|
||||
return body.replace(/\{\{([^}]+)\}\}/g, (_, argName) => `$${argName.toUpperCase()}`);
|
||||
}
|
||||
return body;
|
||||
},
|
||||
},
|
||||
agents: {
|
||||
provider: 'agents',
|
||||
configDir: '.agents',
|
||||
displayName: 'Agents',
|
||||
frontmatterFields: ['user-invocable', 'argument-hint'],
|
||||
frontmatterFields: ['user-invocable', 'argument-hint', 'license', 'compatibility', 'metadata'],
|
||||
},
|
||||
kiro: {
|
||||
provider: 'kiro',
|
||||
@@ -67,6 +55,6 @@ export const PROVIDERS = {
|
||||
provider: 'pi',
|
||||
configDir: '.pi',
|
||||
displayName: 'Pi',
|
||||
frontmatterFields: ['license', 'compatibility', 'metadata'],
|
||||
frontmatterFields: ['license', 'compatibility', 'metadata', 'allowed-tools'],
|
||||
},
|
||||
};
|
||||
|
||||
+2
-2
@@ -203,13 +203,13 @@ Please audit {{target}} for technical quality. Ask {{model}} for help.`;
|
||||
// Verify Gemini: skill in skills directory
|
||||
expect(fs.existsSync(path.join(DIST_DIR, 'gemini/.gemini/skills/audit/SKILL.md'))).toBe(true);
|
||||
const geminiContent = fs.readFileSync(path.join(DIST_DIR, 'gemini/.gemini/skills/audit/SKILL.md'), 'utf-8');
|
||||
expect(geminiContent).toContain('{{args}}'); // Replaced for user-invocable in Gemini
|
||||
expect(geminiContent).toContain('{{target}}'); // No body transform, placeholder preserved
|
||||
expect(geminiContent).toContain('Gemini');
|
||||
|
||||
// Verify Codex: skill in skills directory
|
||||
expect(fs.existsSync(path.join(DIST_DIR, 'codex/.codex/skills/audit/SKILL.md'))).toBe(true);
|
||||
const codexContent = fs.readFileSync(path.join(DIST_DIR, 'codex/.codex/skills/audit/SKILL.md'), 'utf-8');
|
||||
expect(codexContent).toContain('$TARGET'); // Replaced for user-invocable in Codex
|
||||
expect(codexContent).toContain('{{target}}'); // No body transform, placeholder preserved
|
||||
expect(codexContent).toContain('GPT');
|
||||
});
|
||||
|
||||
|
||||
@@ -190,115 +190,3 @@ for (const [key, config] of Object.entries(PROVIDERS)) {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Provider-specific body transform tests
|
||||
describe('Codex body transform', () => {
|
||||
const transform = createTransformer(PROVIDERS.codex);
|
||||
|
||||
beforeEach(() => {
|
||||
if (fs.existsSync(TEST_DIR)) fs.rmSync(TEST_DIR, { recursive: true, force: true });
|
||||
});
|
||||
afterEach(() => {
|
||||
if (fs.existsSync(TEST_DIR)) fs.rmSync(TEST_DIR, { recursive: true, force: true });
|
||||
});
|
||||
|
||||
test('should transform {{argname}} to $ARGNAME for user-invocable skills', () => {
|
||||
const skills = [{
|
||||
name: 'test',
|
||||
description: 'Test',
|
||||
userInvocable: true,
|
||||
argumentHint: '[target]',
|
||||
body: 'Process {{target}} now.'
|
||||
}];
|
||||
transform(skills, TEST_DIR);
|
||||
const content = fs.readFileSync(
|
||||
path.join(TEST_DIR, `codex/.codex/skills/test/SKILL.md`), 'utf-8'
|
||||
);
|
||||
expect(content).toContain('Process $TARGET now.');
|
||||
});
|
||||
|
||||
test('should not transform {{argname}} for non-user-invocable skills', () => {
|
||||
const skills = [{
|
||||
name: 'test',
|
||||
description: 'Test',
|
||||
userInvocable: false,
|
||||
body: 'Process {{target}} now.'
|
||||
}];
|
||||
transform(skills, TEST_DIR);
|
||||
const content = fs.readFileSync(
|
||||
path.join(TEST_DIR, `codex/.codex/skills/test/SKILL.md`), 'utf-8'
|
||||
);
|
||||
// {{target}} should have been left as-is (not transformed by replacePlaceholders either,
|
||||
// since 'target' is not a known placeholder)
|
||||
expect(content).toContain('Process {{target}} now.');
|
||||
});
|
||||
|
||||
test('should replace system placeholders before body transform', () => {
|
||||
const skills = [{
|
||||
name: 'test',
|
||||
description: 'Test',
|
||||
userInvocable: true,
|
||||
body: 'Ask {{model}} about {{target}}.'
|
||||
}];
|
||||
transform(skills, TEST_DIR);
|
||||
const content = fs.readFileSync(
|
||||
path.join(TEST_DIR, `codex/.codex/skills/test/SKILL.md`), 'utf-8'
|
||||
);
|
||||
// {{model}} replaced first by replacePlaceholders, then {{target}} by bodyTransform
|
||||
expect(content).toContain('Ask GPT about $TARGET.');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Gemini body transform', () => {
|
||||
const transform = createTransformer(PROVIDERS.gemini);
|
||||
|
||||
beforeEach(() => {
|
||||
if (fs.existsSync(TEST_DIR)) fs.rmSync(TEST_DIR, { recursive: true, force: true });
|
||||
});
|
||||
afterEach(() => {
|
||||
if (fs.existsSync(TEST_DIR)) fs.rmSync(TEST_DIR, { recursive: true, force: true });
|
||||
});
|
||||
|
||||
test('should transform all remaining {{*}} to {{args}} for user-invocable skills', () => {
|
||||
const skills = [{
|
||||
name: 'test',
|
||||
description: 'Test',
|
||||
userInvocable: true,
|
||||
body: 'Process {{target}} and {{format}}.'
|
||||
}];
|
||||
transform(skills, TEST_DIR);
|
||||
const content = fs.readFileSync(
|
||||
path.join(TEST_DIR, `gemini/.gemini/skills/test/SKILL.md`), 'utf-8'
|
||||
);
|
||||
expect(content).toContain('Process {{args}} and {{args}}.');
|
||||
});
|
||||
|
||||
test('should not transform for non-user-invocable skills', () => {
|
||||
const skills = [{
|
||||
name: 'test',
|
||||
description: 'Test',
|
||||
userInvocable: false,
|
||||
body: 'Process {{target}}.'
|
||||
}];
|
||||
transform(skills, TEST_DIR);
|
||||
const content = fs.readFileSync(
|
||||
path.join(TEST_DIR, `gemini/.gemini/skills/test/SKILL.md`), 'utf-8'
|
||||
);
|
||||
expect(content).toContain('Process {{target}}.');
|
||||
});
|
||||
|
||||
test('should replace system placeholders before body transform', () => {
|
||||
const skills = [{
|
||||
name: 'test',
|
||||
description: 'Test',
|
||||
userInvocable: true,
|
||||
body: 'Ask {{model}} about {{target}}.'
|
||||
}];
|
||||
transform(skills, TEST_DIR);
|
||||
const content = fs.readFileSync(
|
||||
path.join(TEST_DIR, `gemini/.gemini/skills/test/SKILL.md`), 'utf-8'
|
||||
);
|
||||
// {{model}} replaced first, then remaining {{target}} becomes {{args}}
|
||||
expect(content).toContain('Ask Gemini about {{args}}.');
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user