mirror of
https://github.com/pbakaus/impeccable.git
synced 2026-09-19 01:26:29 +03:00
Prefix all skills (incl. frontend-design), fix cross-references, move toggle inline
- Prefix ALL skills in prefixed bundles, not just user-invokable ones - Add prefixSkillReferences() to update /skillname and "the skillname skill" cross-references in skill body text when prefixing - Add prefix/outputSuffix support to agents transformer - Move prefix toggle inline next to the download ZIP button for better discoverability Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
ee7eeae301
commit
aadaee5c37
+26
-23
@@ -2208,26 +2208,28 @@ code {
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
/* Download options / prefix toggle */
|
||||
.download-options {
|
||||
margin-bottom: var(--spacing-lg);
|
||||
text-align: center;
|
||||
/* Prefix toggle (inline with download button) */
|
||||
.install-terminal-cmd--download {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--spacing-sm);
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.prefix-toggle {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: var(--spacing-sm);
|
||||
gap: 8px;
|
||||
cursor: pointer;
|
||||
padding: var(--spacing-sm) var(--spacing-md);
|
||||
background: var(--color-cream);
|
||||
border: 1px solid var(--color-mist);
|
||||
border-radius: 8px;
|
||||
padding: 6px 12px;
|
||||
background: rgba(0, 0, 0, 0.06);
|
||||
border: 1px solid rgba(0, 0, 0, 0.08);
|
||||
border-radius: 6px;
|
||||
transition: border-color var(--duration-fast) var(--ease-out);
|
||||
}
|
||||
|
||||
.prefix-toggle:hover {
|
||||
border-color: var(--color-ash);
|
||||
border-color: rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.prefix-toggle input {
|
||||
@@ -2239,10 +2241,10 @@ code {
|
||||
|
||||
.prefix-toggle-slider {
|
||||
position: relative;
|
||||
width: 44px;
|
||||
height: 24px;
|
||||
width: 32px;
|
||||
height: 18px;
|
||||
background: var(--color-mist);
|
||||
border-radius: 24px;
|
||||
border-radius: 18px;
|
||||
transition: background var(--duration-fast) var(--ease-out);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
@@ -2250,13 +2252,13 @@ code {
|
||||
.prefix-toggle-slider::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 3px;
|
||||
left: 3px;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
top: 2px;
|
||||
left: 2px;
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
background: var(--color-paper);
|
||||
border-radius: 50%;
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.15);
|
||||
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.15);
|
||||
transition: transform var(--duration-fast) var(--ease-out);
|
||||
}
|
||||
|
||||
@@ -2265,7 +2267,7 @@ code {
|
||||
}
|
||||
|
||||
.prefix-toggle input:checked + .prefix-toggle-slider::after {
|
||||
transform: translateX(20px);
|
||||
transform: translateX(14px);
|
||||
}
|
||||
|
||||
.prefix-toggle input:focus-visible + .prefix-toggle-slider {
|
||||
@@ -2274,17 +2276,18 @@ code {
|
||||
}
|
||||
|
||||
.prefix-toggle-label {
|
||||
font-size: 0.875rem;
|
||||
font-size: 0.75rem;
|
||||
color: var(--color-charcoal);
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.prefix-toggle-label code {
|
||||
font-family: var(--font-mono);
|
||||
font-size: 0.8125rem;
|
||||
font-size: 0.6875rem;
|
||||
background: var(--color-accent-dim);
|
||||
color: var(--color-accent);
|
||||
padding: 2px 6px;
|
||||
border-radius: 4px;
|
||||
padding: 1px 4px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.download-tip {
|
||||
|
||||
+3920
-1
File diff suppressed because one or more lines are too long
+9
-11
@@ -232,13 +232,20 @@
|
||||
<div class="install-terminal-divider"></div>
|
||||
<div class="install-terminal-row">
|
||||
<span class="install-terminal-label">Manual</span>
|
||||
<div class="install-terminal-cmd">
|
||||
<div class="install-terminal-cmd install-terminal-cmd--download">
|
||||
<button class="btn btn-secondary btn-small" data-bundle="universal" aria-label="Download universal ZIP">
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true">
|
||||
<path d="M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4M7 10l5 5 5-5M12 15V3"/>
|
||||
</svg>
|
||||
<span>Download universal ZIP</span>
|
||||
</button>
|
||||
<label class="prefix-toggle">
|
||||
<input type="checkbox" id="prefix-toggle" />
|
||||
<span class="prefix-toggle-slider"></span>
|
||||
<span class="prefix-toggle-label">
|
||||
Prefix with <code>/i-</code>
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
<span class="install-terminal-note">Contains all provider directories — extract to project root</span>
|
||||
</div>
|
||||
@@ -277,16 +284,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="download-options" data-reveal>
|
||||
<label class="prefix-toggle">
|
||||
<input type="checkbox" id="prefix-toggle" />
|
||||
<span class="prefix-toggle-slider"></span>
|
||||
<span class="prefix-toggle-label">
|
||||
Prefix skills with <code>/i-</code> to avoid conflicts
|
||||
</span>
|
||||
</label>
|
||||
<p class="download-tip">Once installed, keep the <a href="/cheatsheet">command cheatsheet</a> handy for quick reference.</p>
|
||||
</div>
|
||||
<p class="download-tip" data-reveal>Once installed, keep the <a href="/cheatsheet">command cheatsheet</a> handy for quick reference.</p>
|
||||
</section>
|
||||
|
||||
<!-- 5. CHANGELOG -->
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import path from 'path';
|
||||
import { cleanDir, ensureDir, writeFile, generateYamlFrontmatter, replacePlaceholders } from '../utils.js';
|
||||
import { cleanDir, ensureDir, writeFile, generateYamlFrontmatter, replacePlaceholders, prefixSkillReferences } from '../utils.js';
|
||||
|
||||
/**
|
||||
* Agents Transformer (VS Code Copilot + Antigravity)
|
||||
@@ -20,9 +20,10 @@ export function transformAgents(skills, distDir, patterns = null, options = {})
|
||||
cleanDir(agentsDir);
|
||||
ensureDir(skillsDir);
|
||||
|
||||
const allSkillNames = skills.map(s => s.name);
|
||||
let refCount = 0;
|
||||
for (const skill of skills) {
|
||||
const skillName = skill.userInvokable ? `${prefix}${skill.name}` : skill.name;
|
||||
const skillName = `${prefix}${skill.name}`;
|
||||
const skillDir = path.join(skillsDir, skillName);
|
||||
|
||||
const frontmatterObj = {
|
||||
@@ -41,7 +42,8 @@ export function transformAgents(skills, distDir, patterns = null, options = {})
|
||||
}
|
||||
|
||||
const frontmatter = generateYamlFrontmatter(frontmatterObj);
|
||||
const skillBody = replacePlaceholders(skill.body, 'agents');
|
||||
let skillBody = replacePlaceholders(skill.body, 'agents');
|
||||
if (prefix) skillBody = prefixSkillReferences(skillBody, prefix, allSkillNames);
|
||||
const content = `${frontmatter}\n\n${skillBody}`;
|
||||
const outputPath = path.join(skillDir, 'SKILL.md');
|
||||
writeFile(outputPath, content);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import path from 'path';
|
||||
import { cleanDir, ensureDir, writeFile, generateYamlFrontmatter, replacePlaceholders } from '../utils.js';
|
||||
import { cleanDir, ensureDir, writeFile, generateYamlFrontmatter, replacePlaceholders, prefixSkillReferences } from '../utils.js';
|
||||
|
||||
/**
|
||||
* Claude Code Transformer (Skills Only)
|
||||
@@ -22,9 +22,10 @@ export function transformClaudeCode(skills, distDir, patterns = null, options =
|
||||
cleanDir(claudeDir);
|
||||
ensureDir(skillsDir);
|
||||
|
||||
const allSkillNames = skills.map(s => s.name);
|
||||
let refCount = 0;
|
||||
for (const skill of skills) {
|
||||
const skillName = skill.userInvokable ? `${prefix}${skill.name}` : skill.name;
|
||||
const skillName = `${prefix}${skill.name}`;
|
||||
const skillDir = path.join(skillsDir, skillName);
|
||||
|
||||
const frontmatterObj = {
|
||||
@@ -40,7 +41,8 @@ export function transformClaudeCode(skills, distDir, patterns = null, options =
|
||||
if (skill.allowedTools) frontmatterObj['allowed-tools'] = skill.allowedTools;
|
||||
|
||||
const frontmatter = generateYamlFrontmatter(frontmatterObj);
|
||||
const skillBody = replacePlaceholders(skill.body, 'claude-code');
|
||||
let skillBody = replacePlaceholders(skill.body, 'claude-code');
|
||||
if (prefix) skillBody = prefixSkillReferences(skillBody, prefix, allSkillNames);
|
||||
const content = `${frontmatter}\n\n${skillBody}`;
|
||||
const outputPath = path.join(skillDir, 'SKILL.md');
|
||||
writeFile(outputPath, content);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import path from 'path';
|
||||
import { cleanDir, ensureDir, writeFile, generateYamlFrontmatter, replacePlaceholders } from '../utils.js';
|
||||
import { cleanDir, ensureDir, writeFile, generateYamlFrontmatter, replacePlaceholders, prefixSkillReferences } from '../utils.js';
|
||||
|
||||
/**
|
||||
* Codex Transformer (Skills Only)
|
||||
@@ -23,9 +23,10 @@ export function transformCodex(skills, distDir, patterns = null, options = {}) {
|
||||
cleanDir(codexDir);
|
||||
ensureDir(skillsDir);
|
||||
|
||||
const allSkillNames = skills.map(s => s.name);
|
||||
let refCount = 0;
|
||||
for (const skill of skills) {
|
||||
const skillName = skill.userInvokable ? `${prefix}${skill.name}` : skill.name;
|
||||
const skillName = `${prefix}${skill.name}`;
|
||||
const skillDir = path.join(skillsDir, skillName);
|
||||
|
||||
const frontmatterObj = {
|
||||
@@ -45,6 +46,7 @@ export function transformCodex(skills, distDir, patterns = null, options = {}) {
|
||||
const frontmatter = generateYamlFrontmatter(frontmatterObj);
|
||||
|
||||
let skillBody = replacePlaceholders(skill.body, 'codex');
|
||||
if (prefix) skillBody = prefixSkillReferences(skillBody, prefix, allSkillNames);
|
||||
// For user-invokable skills, transform remaining {{argname}} to $ARGNAME
|
||||
if (skill.userInvokable) {
|
||||
skillBody = skillBody.replace(/\{\{([^}]+)\}\}/g, (match, argName) => {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import path from 'path';
|
||||
import { cleanDir, ensureDir, writeFile, generateYamlFrontmatter, replacePlaceholders } from '../utils.js';
|
||||
import { cleanDir, ensureDir, writeFile, generateYamlFrontmatter, replacePlaceholders, prefixSkillReferences } from '../utils.js';
|
||||
|
||||
/**
|
||||
* Cursor Transformer (Skills Only)
|
||||
@@ -22,9 +22,10 @@ export function transformCursor(skills, distDir, patterns = null, options = {})
|
||||
cleanDir(cursorDir);
|
||||
ensureDir(skillsDir);
|
||||
|
||||
const allSkillNames = skills.map(s => s.name);
|
||||
let refCount = 0;
|
||||
for (const skill of skills) {
|
||||
const skillName = skill.userInvokable ? `${prefix}${skill.name}` : skill.name;
|
||||
const skillName = `${prefix}${skill.name}`;
|
||||
const skillDir = path.join(skillsDir, skillName);
|
||||
|
||||
const frontmatterObj = {
|
||||
@@ -34,7 +35,8 @@ export function transformCursor(skills, distDir, patterns = null, options = {})
|
||||
if (skill.license) frontmatterObj.license = skill.license;
|
||||
|
||||
const frontmatter = generateYamlFrontmatter(frontmatterObj);
|
||||
const skillBody = replacePlaceholders(skill.body, 'cursor');
|
||||
let skillBody = replacePlaceholders(skill.body, 'cursor');
|
||||
if (prefix) skillBody = prefixSkillReferences(skillBody, prefix, allSkillNames);
|
||||
const content = `${frontmatter}\n\n${skillBody}`;
|
||||
const outputPath = path.join(skillDir, 'SKILL.md');
|
||||
writeFile(outputPath, content);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import path from 'path';
|
||||
import { cleanDir, ensureDir, writeFile, generateYamlFrontmatter, replacePlaceholders } from '../utils.js';
|
||||
import { cleanDir, ensureDir, writeFile, generateYamlFrontmatter, replacePlaceholders, prefixSkillReferences } from '../utils.js';
|
||||
|
||||
/**
|
||||
* Gemini Transformer (Skills Only)
|
||||
@@ -23,9 +23,10 @@ export function transformGemini(skills, distDir, patterns = null, options = {})
|
||||
cleanDir(geminiDir);
|
||||
ensureDir(skillsDir);
|
||||
|
||||
const allSkillNames = skills.map(s => s.name);
|
||||
let refCount = 0;
|
||||
for (const skill of skills) {
|
||||
const skillName = skill.userInvokable ? `${prefix}${skill.name}` : skill.name;
|
||||
const skillName = `${prefix}${skill.name}`;
|
||||
const skillDir = path.join(skillsDir, skillName);
|
||||
|
||||
const frontmatter = generateYamlFrontmatter({
|
||||
@@ -34,6 +35,7 @@ export function transformGemini(skills, distDir, patterns = null, options = {})
|
||||
});
|
||||
|
||||
let skillBody = replacePlaceholders(skill.body, 'gemini');
|
||||
if (prefix) skillBody = prefixSkillReferences(skillBody, prefix, allSkillNames);
|
||||
// For user-invokable skills, replace remaining {{arg}} placeholders with {{args}}
|
||||
if (skill.userInvokable) {
|
||||
skillBody = skillBody.replace(/\{\{[^}]+\}\}/g, '{{args}}');
|
||||
|
||||
@@ -294,6 +294,38 @@ export const PROVIDER_PLACEHOLDERS = {
|
||||
/**
|
||||
* Replace all {{placeholder}} tokens with provider-specific values
|
||||
*/
|
||||
/**
|
||||
* Prefix skill cross-references in body text.
|
||||
* Replaces patterns like `/skillname` and `the skillname skill` with prefixed versions.
|
||||
*
|
||||
* @param {string} content - The skill body text
|
||||
* @param {string} prefix - The prefix to add (e.g., 'i-')
|
||||
* @param {string[]} skillNames - Array of all skill names
|
||||
*/
|
||||
export function prefixSkillReferences(content, prefix, skillNames) {
|
||||
if (!prefix || !skillNames || skillNames.length === 0) return content;
|
||||
|
||||
let result = content;
|
||||
// Sort by length descending to avoid partial matches (e.g. 'teach-impeccable' before 'teach')
|
||||
const sorted = [...skillNames].sort((a, b) => b.length - a.length);
|
||||
|
||||
for (const name of sorted) {
|
||||
const prefixed = `${prefix}${name}`;
|
||||
|
||||
// Replace `/skillname` references (command invocations)
|
||||
result = result.replace(new RegExp(`\\/(?=${escapeRegex(name)}(?:[^a-zA-Z0-9_-]|$))`, 'g'), `/${prefix}`);
|
||||
|
||||
// Replace `the skillname skill` references
|
||||
result = result.replace(new RegExp(`the ${escapeRegex(name)} skill`, 'gi'), `the ${prefixed} skill`);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function escapeRegex(str) {
|
||||
return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
||||
}
|
||||
|
||||
export function replacePlaceholders(content, provider) {
|
||||
const placeholders = PROVIDER_PLACEHOLDERS[provider] || PROVIDER_PLACEHOLDERS['cursor'];
|
||||
|
||||
|
||||
Reference in New Issue
Block a user