mirror of
https://github.com/evdboom/Bindery.git
synced 2026-07-22 06:49:36 +00:00
feat(ai-setup): generate .agents skills for agents target across hosts
- write .agents/skills/<skill>/SKILL.md when agents target is selected - apply parity updates in MCP, VS Code extension, and Obsidian plugin - add/extend ai-setup tests for agents skill generation
This commit is contained in:
parent
bcfa6817ed
commit
355dea98a8
5 changed files with 53 additions and 3 deletions
|
|
@ -2,7 +2,8 @@
|
|||
* AI instruction file generation for Bindery (MCP server).
|
||||
*
|
||||
* Generates CLAUDE.md, .github/copilot-instructions.md, .cursor/rules,
|
||||
* AGENTS.md, and .claude/skills/<skill>/SKILL.md from the book's
|
||||
* AGENTS.md, and skills under .claude/skills/<skill>/SKILL.md and
|
||||
* .agents/skills/<skill>/SKILL.md from the book's
|
||||
* .bindery/settings.json.
|
||||
*
|
||||
* Template content is maintained in bindery-core/src/templates/*.ts
|
||||
|
|
@ -169,6 +170,10 @@ export function setupAiFiles(options: AiSetupOptions): AiSetupResult {
|
|||
break;
|
||||
case 'agents':
|
||||
writeFile(root, 'AGENTS.md', renderTemplate('agents', ctx), overwrite, versionFile, result);
|
||||
for (const skill of skills) {
|
||||
const skillMd = path.join('.agents', 'skills', skill, 'SKILL.md');
|
||||
writeFile(root, skillMd, renderTemplate(skill, ctx), overwrite, versionFile, result);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -98,4 +98,25 @@ describe('setupAiFiles skill generation', () => {
|
|||
expect(result.skillZipManifest.created).toEqual([]);
|
||||
expect(fs.readFileSync(path.join(root, '.claude', 'skills', 'read-aloud.zip'), 'utf-8')).toBe('legacy zip bytes');
|
||||
});
|
||||
|
||||
it('generates requested skill markdown files for agents target under .agents/skills', async () => {
|
||||
const { setupAiFiles } = await loadAiSetup();
|
||||
|
||||
const root = makeRoot();
|
||||
write(path.join(root, '.bindery', 'settings.json'), JSON.stringify({
|
||||
bookTitle: 'Test Book',
|
||||
storyFolder: 'Story',
|
||||
languages: [{ code: 'EN', folderName: 'EN' }],
|
||||
}, null, 2) + '\n');
|
||||
|
||||
const result = setupAiFiles({
|
||||
root,
|
||||
targets: ['agents'],
|
||||
skills: ['read-in'],
|
||||
overwrite: true,
|
||||
});
|
||||
|
||||
expect(result.regenerated).toContain('.agents/skills/read-in/SKILL.md');
|
||||
expect(fs.existsSync(path.join(root, '.agents', 'skills', 'read-in', 'SKILL.md'))).toBe(true);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
* Obsidian-specific AI setup wrapper.
|
||||
*
|
||||
* Generates AI instruction files (CLAUDE.md, copilot-instructions.md, .cursor/rules, AGENTS.md)
|
||||
* and Claude skill templates from the book's .bindery/settings.json.
|
||||
* and skill templates from the book's .bindery/settings.json.
|
||||
*
|
||||
* For Obsidian, this is a simpler adaptation since we don't have the VS Code LM API,
|
||||
* so we just generate the files to the filesystem directly.
|
||||
|
|
@ -89,6 +89,10 @@ export function setupAiFiles(
|
|||
break;
|
||||
case 'agents':
|
||||
writeFile(bookRoot, 'AGENTS.md', renderTemplate('agents', ctx), overwrite, result);
|
||||
for (const skill of skills) {
|
||||
const skillDir = path.join('.agents', 'skills', skill);
|
||||
writeFile(bookRoot, path.join(skillDir, 'SKILL.md'), renderTemplate(skill, ctx), overwrite, result);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -85,6 +85,22 @@ describe('AI Setup', () => {
|
|||
expect(fs.existsSync(agentsPath)).toBe(true);
|
||||
});
|
||||
|
||||
it('should create skill markdown files under .agents/skills when agents target requested', async () => {
|
||||
const settingsPath = path.join(tempRoot, '.bindery', 'settings.json');
|
||||
fs.mkdirSync(path.dirname(settingsPath), { recursive: true });
|
||||
fs.writeFileSync(settingsPath, JSON.stringify({
|
||||
bookTitle: 'Test Book',
|
||||
}, null, 2), 'utf-8');
|
||||
|
||||
await setupAiFiles(mockApp, tempRoot, ['agents'], ['read-in', 'review'], false);
|
||||
|
||||
const readInSkillPath = path.join(tempRoot, '.agents', 'skills', 'read-in', 'SKILL.md');
|
||||
const reviewSkillPath = path.join(tempRoot, '.agents', 'skills', 'review', 'SKILL.md');
|
||||
|
||||
expect(fs.existsSync(readInSkillPath)).toBe(true);
|
||||
expect(fs.existsSync(reviewSkillPath)).toBe(true);
|
||||
});
|
||||
|
||||
it('should skip existing files when overwrite is false', async () => {
|
||||
const settingsPath = path.join(tempRoot, '.bindery', 'settings.json');
|
||||
fs.mkdirSync(path.dirname(settingsPath), { recursive: true });
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
* claude → CLAUDE.md + .claude/skills/<skill>/SKILL.md
|
||||
* copilot → .github/copilot-instructions.md
|
||||
* cursor → .cursor/rules
|
||||
* agents → AGENTS.md (OpenAI Agents, Aider, Codex, etc.)
|
||||
* agents → AGENTS.md + .agents/skills/<skill>/SKILL.md (OpenAI Agents, Aider, Codex, etc.)
|
||||
*
|
||||
* Templates and TemplateContext live in @bindery/core (single source of truth).
|
||||
*/
|
||||
|
|
@ -92,6 +92,10 @@ export function setupAiFiles(options: AiSetupOptions): AiSetupResult {
|
|||
|
||||
case 'agents':
|
||||
writeFile(root, 'AGENTS.md', renderTemplate('agents', ctx), overwrite, result);
|
||||
for (const skill of skills) {
|
||||
const skillDir = path.join('.agents', 'skills', skill);
|
||||
writeFile(root, path.join(skillDir, 'SKILL.md'), renderTemplate(skill, ctx), overwrite, result);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue