mirror of
https://github.com/pbakaus/impeccable.git
synced 2026-09-22 02:56:52 +03:00
Move PID file to project root (.impeccable-live.json)
os.tmpdir() returns /var/folders/.../T/ on macOS, not /tmp/. The skill reference was telling the agent to cat /tmp/impeccable-live.json which didn't exist. Moving the PID file to the project root makes it predictable across platforms and project-scoped (multiple projects can run independent live sessions). Changed in: live-server.mjs, live-poll.mjs, live.md reference. Added .impeccable-live.json to .gitignore. 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
4b52756edd
commit
4092ee5f22
+25
-4
@@ -7,8 +7,9 @@
|
||||
* - Cursor: .cursor/skills/
|
||||
* - Claude Code: .claude/skills/
|
||||
* - Gemini: .gemini/skills/
|
||||
* - Codex: .codex/skills/
|
||||
* - Agents: .agents/skills/ (VS Code Copilot + Antigravity)
|
||||
* - Codex: .codex/skills/ (Codex-specific compatibility bundle)
|
||||
* - Agents: .agents/skills/ (Codex repo/user installs)
|
||||
* - GitHub: .github/skills/ (GitHub Copilot)
|
||||
*
|
||||
* Also assembles a universal ZIP containing all providers,
|
||||
* and builds Tailwind CSS for production deployment.
|
||||
@@ -111,6 +112,19 @@ function generateCounts(rootDir, skills, buildDir) {
|
||||
return errors;
|
||||
}
|
||||
|
||||
function validateSkillFrontmatter(skills) {
|
||||
let errors = 0;
|
||||
|
||||
for (const skill of skills) {
|
||||
if (skill.description && skill.description.length > 1024) {
|
||||
console.error(`❌ ${skill.filePath}: invalid description: exceeds maximum length of 1024 characters (${skill.description.length})`);
|
||||
errors++;
|
||||
}
|
||||
}
|
||||
|
||||
return errors;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cross-validate that every detection rule with a `skillGuideline` has a
|
||||
* matching DON'T line in the right section of source/skills/impeccable/SKILL.md.
|
||||
@@ -406,8 +420,9 @@ This folder contains skills for all supported tools:
|
||||
.cursor/ -> Cursor
|
||||
.claude/ -> Claude Code
|
||||
.gemini/ -> Gemini CLI
|
||||
.codex/ -> Codex CLI
|
||||
.agents/ -> VS Code Copilot, Antigravity
|
||||
.codex/ -> Codex compatibility bundle
|
||||
.agents/ -> Codex CLI
|
||||
.github/ -> GitHub Copilot
|
||||
.kiro/ -> Kiro
|
||||
.opencode/ -> OpenCode
|
||||
.pi/ -> Pi
|
||||
@@ -415,6 +430,7 @@ This folder contains skills for all supported tools:
|
||||
.trae/ -> Trae International
|
||||
|
||||
To install, copy the relevant folder(s) into your project root.
|
||||
For Codex, repo and user skill installs come from .agents/skills.
|
||||
These are hidden folders (dotfiles). Press Cmd+Shift+. in Finder to see them.
|
||||
`);
|
||||
|
||||
@@ -638,6 +654,11 @@ async function build() {
|
||||
const userInvocableCount = skills.filter(s => s.userInvocable).length;
|
||||
console.log(`📖 Read ${skills.length} skills (${userInvocableCount} user-invocable) and ${patterns.patterns.length + patterns.antipatterns.length} pattern categories\n`);
|
||||
|
||||
const frontmatterErrors = validateSkillFrontmatter(skills);
|
||||
if (frontmatterErrors > 0) {
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Read skills version from plugin.json
|
||||
const pluginJson = JSON.parse(fs.readFileSync(path.join(ROOT_DIR, '.claude-plugin/plugin.json'), 'utf-8'));
|
||||
const skillsVersion = pluginJson.version;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import path from 'path';
|
||||
import { cleanDir, ensureDir, writeFile, generateYamlFrontmatter, replacePlaceholders } from '../utils.js';
|
||||
import { cleanDir, ensureDir, writeFile, generateYamlFrontmatter, generateYamlDocument, replacePlaceholders } from '../utils.js';
|
||||
import { SKILL_CATEGORIES, CATEGORY_ORDER } from '../sub-pages-data.js';
|
||||
|
||||
/**
|
||||
@@ -40,6 +40,31 @@ const FIELD_SPECS = {
|
||||
},
|
||||
};
|
||||
|
||||
function humanizeSkillName(name) {
|
||||
return name
|
||||
.split('-')
|
||||
.map(part => part.charAt(0).toUpperCase() + part.slice(1))
|
||||
.join(' ');
|
||||
}
|
||||
|
||||
function summarizeDescription(description, maxLength = 88) {
|
||||
if (!description || description.length <= maxLength) return description;
|
||||
const clipped = description.slice(0, maxLength - 1);
|
||||
const lastSpace = clipped.lastIndexOf(' ');
|
||||
return `${(lastSpace > 48 ? clipped.slice(0, lastSpace) : clipped).trimEnd()}...`;
|
||||
}
|
||||
|
||||
function buildOpenAIMetadata(skill) {
|
||||
const displayName = humanizeSkillName(skill.name);
|
||||
return {
|
||||
interface: {
|
||||
display_name: displayName,
|
||||
short_description: summarizeDescription(skill.description),
|
||||
default_prompt: `Use ${displayName} to redesign, critique, audit, or polish this frontend.`,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a transformer function for a given provider config.
|
||||
*
|
||||
@@ -47,7 +72,7 @@ const FIELD_SPECS = {
|
||||
* @returns {Function} transform(skills, distDir, options?)
|
||||
*/
|
||||
export function createTransformer(config) {
|
||||
const { provider, configDir, displayName, frontmatterFields = [], bodyTransform, placeholderProvider } = config;
|
||||
const { provider, configDir, displayName, frontmatterFields = [], bodyTransform, placeholderProvider, writeOpenAIMetadata = false, includeVersion = true } = config;
|
||||
const placeholderKey = placeholderProvider || provider;
|
||||
|
||||
const activeFields = frontmatterFields
|
||||
@@ -79,7 +104,7 @@ export function createTransformer(config) {
|
||||
name: skillName,
|
||||
description: skill.description,
|
||||
};
|
||||
if (skillsVersion) frontmatterObj.version = skillsVersion;
|
||||
if (skillsVersion && includeVersion) frontmatterObj.version = skillsVersion;
|
||||
|
||||
for (const spec of activeFields) {
|
||||
if (spec.condition && !spec.condition(skill)) continue;
|
||||
@@ -118,6 +143,11 @@ export function createTransformer(config) {
|
||||
const content = `${frontmatter}\n\n${skillBody}`;
|
||||
writeFile(path.join(skillDir, 'SKILL.md'), content);
|
||||
|
||||
if (writeOpenAIMetadata) {
|
||||
const openaiMetadata = buildOpenAIMetadata(skill);
|
||||
writeFile(path.join(skillDir, 'agents', 'openai.yaml'), generateYamlDocument(openaiMetadata));
|
||||
}
|
||||
|
||||
// Copy reference files
|
||||
if (skill.references && skill.references.length > 0) {
|
||||
const refDir = path.join(skillDir, 'reference');
|
||||
|
||||
@@ -9,6 +9,7 @@ export const transformClaudeCode = createTransformer(PROVIDERS['claude-code']);
|
||||
export const transformGemini = createTransformer(PROVIDERS.gemini);
|
||||
export const transformCodex = createTransformer(PROVIDERS.codex);
|
||||
export const transformAgents = createTransformer(PROVIDERS.agents);
|
||||
export const transformGitHub = createTransformer(PROVIDERS.github);
|
||||
export const transformKiro = createTransformer(PROVIDERS.kiro);
|
||||
export const transformOpenCode = createTransformer(PROVIDERS.opencode);
|
||||
export const transformPi = createTransformer(PROVIDERS.pi);
|
||||
|
||||
@@ -31,12 +31,24 @@ export const PROVIDERS = {
|
||||
provider: 'codex',
|
||||
configDir: '.codex',
|
||||
displayName: 'Codex',
|
||||
frontmatterFields: ['argument-hint', 'license'],
|
||||
frontmatterFields: [],
|
||||
includeVersion: false,
|
||||
writeOpenAIMetadata: true,
|
||||
},
|
||||
agents: {
|
||||
provider: 'agents',
|
||||
configDir: '.agents',
|
||||
displayName: 'Agents',
|
||||
displayName: 'Codex Repo Skills',
|
||||
placeholderProvider: 'codex',
|
||||
frontmatterFields: [],
|
||||
includeVersion: false,
|
||||
writeOpenAIMetadata: true,
|
||||
},
|
||||
github: {
|
||||
provider: 'github',
|
||||
configDir: '.github',
|
||||
displayName: 'GitHub Copilot',
|
||||
placeholderProvider: 'agents',
|
||||
frontmatterFields: ['user-invocable', 'argument-hint', 'license', 'compatibility', 'metadata'],
|
||||
},
|
||||
kiro: {
|
||||
|
||||
@@ -475,6 +475,31 @@ function formatYamlScalar(value) {
|
||||
return value;
|
||||
}
|
||||
|
||||
function appendYamlObject(lines, data, indent = 0) {
|
||||
const space = ' '.repeat(indent);
|
||||
|
||||
for (const [key, value] of Object.entries(data)) {
|
||||
if (Array.isArray(value)) {
|
||||
lines.push(`${space}${key}:`);
|
||||
for (const item of value) {
|
||||
if (item && typeof item === 'object' && !Array.isArray(item)) {
|
||||
lines.push(`${space} -`);
|
||||
appendYamlObject(lines, item, indent + 4);
|
||||
} else {
|
||||
lines.push(`${space} - ${formatYamlScalar(item)}`);
|
||||
}
|
||||
}
|
||||
} else if (value && typeof value === 'object') {
|
||||
lines.push(`${space}${key}:`);
|
||||
appendYamlObject(lines, value, indent + 2);
|
||||
} else if (typeof value === 'boolean') {
|
||||
lines.push(`${space}${key}: ${value}`);
|
||||
} else {
|
||||
lines.push(`${space}${key}: ${formatYamlScalar(value)}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate YAML frontmatter string
|
||||
*/
|
||||
@@ -503,3 +528,12 @@ export function generateYamlFrontmatter(data) {
|
||||
lines.push('---');
|
||||
return lines.join('\n');
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a plain YAML document string.
|
||||
*/
|
||||
export function generateYamlDocument(data) {
|
||||
const lines = [];
|
||||
appendYamlObject(lines, data);
|
||||
return lines.join('\n');
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user