返回 AiToEarn
1 #!/usr/bin/env node
2 /**
3 * Pin/unpin sub-commands as standalone skill shortcuts.
4 *
5 * Usage:
6 * node <scripts_path>/pin.mjs pin <command>
7 * node <scripts_path>/pin.mjs unpin <command>
8 *
9 * `pin audit` creates a lightweight /audit skill that redirects to /impeccable audit.
10 * `unpin audit` removes that shortcut.
11 *
12 * The script discovers harness directories (.claude/skills, .cursor/skills, etc.)
13 * in the project root and creates/removes the pin in all of them.
14 */
15
16 import { existsSync, readFileSync, writeFileSync, mkdirSync, rmSync, readdirSync } from 'node:fs';
17 import { join, resolve, dirname } from 'node:path';
18 import { fileURLToPath } from 'node:url';
19
20 const __dirname = dirname(fileURLToPath(import.meta.url));
21
22 // All known harness directories
23 const HARNESS_DIRS = [
24 '.claude', '.cursor', '.gemini', '.codex', '.agents',
25 '.trae', '.trae-cn', '.pi', '.opencode', '.kiro', '.rovodev',
26 ];
27
28 // Valid sub-command names
29 const VALID_COMMANDS = [
30 'craft', 'init', 'extract', 'document', 'shape',
31 'critique', 'audit',
32 'polish', 'bolder', 'quieter', 'distill', 'harden', 'onboard', 'live',
33 'animate', 'colorize', 'typeset', 'layout', 'delight', 'overdrive',
34 'clarify', 'adapt', 'optimize',
35 ];
36
37 // Marker to identify pinned skills (so unpin doesn't delete user skills)
38 const PIN_MARKER = '<!-- impeccable-pinned-skill -->';
39
40 /**
41 * Walk up from startDir to find a project root.
42 */
43 function findProjectRoot(startDir = process.cwd()) {
44 let dir = resolve(startDir);
45 while (dir !== '/') {
46 if (
47 existsSync(join(dir, 'package.json')) ||
48 existsSync(join(dir, '.git')) ||
49 existsSync(join(dir, 'skills-lock.json'))
50 ) {
51 return dir;
52 }
53 const parent = resolve(dir, '..');
54 if (parent === dir) break;
55 dir = parent;
56 }
57 return resolve(startDir);
58 }
59
60 /**
61 * Find harness skill directories that have an impeccable skill installed.
62 */
63 function findHarnessDirs(projectRoot) {
64 const dirs = [];
65 for (const harness of HARNESS_DIRS) {
66 const skillsDir = join(projectRoot, harness, 'skills');
67 // Only pin in harness dirs that already have impeccable installed
68 const impeccableDir = join(skillsDir, 'impeccable');
69 if (existsSync(impeccableDir) || existsSync(join(skillsDir, 'i-impeccable'))) {
70 dirs.push(skillsDir);
71 }
72 }
73 return dirs;
74 }
75
76 /**
77 * Load command metadata (descriptions for pinned skills).
78 */
79 function loadCommandMetadata() {
80 const metadataPath = join(__dirname, 'command-metadata.json');
81 if (existsSync(metadataPath)) {
82 return JSON.parse(readFileSync(metadataPath, 'utf-8'));
83 }
84 return {};
85 }
86
87 /**
88 * Generate a pinned skill's SKILL.md content.
89 */
90 function generatePinnedSkill(command, metadata) {
91 const desc = metadata[command]?.description || `Shortcut for /impeccable ${command}.`;
92 const hint = metadata[command]?.argumentHint || '[target]';
93
94 return `---
95 name: ${command}
96 description: "${desc}"
97 argument-hint: "${hint}"
98 user-invocable: true
99 ---
100
101 ${PIN_MARKER}
102
103 This is a pinned shortcut for \`{{command_prefix}}impeccable ${command}\`.
104
105 Invoke {{command_prefix}}impeccable ${command}, passing along any arguments provided here, and follow its instructions.
106 `;
107 }
108
109 /**
110 * Pin a command: create shortcut skill in all harness dirs.
111 */
112 function pin(command, projectRoot) {
113 const metadata = loadCommandMetadata();
114 const harnessDirs = findHarnessDirs(projectRoot);
115
116 if (harnessDirs.length === 0) {
117 console.log('No harness directories with impeccable installed found.');
118 return false;
119 }
120
121 const content = generatePinnedSkill(command, metadata);
122 let created = 0;
123
124 for (const skillsDir of harnessDirs) {
125 // Check if skill already exists (and isn't a pin)
126 const skillDir = join(skillsDir, command);
127 if (existsSync(skillDir)) {
128 const existingMd = join(skillDir, 'SKILL.md');
129 if (existsSync(existingMd)) {
130 const existing = readFileSync(existingMd, 'utf-8');
131 if (!existing.includes(PIN_MARKER)) {
132 console.log(` SKIP: ${skillDir} (non-pinned skill already exists)`);
133 continue;
134 }
135 }
136 }
137
138 mkdirSync(skillDir, { recursive: true });
139 writeFileSync(join(skillDir, 'SKILL.md'), content, 'utf-8');
140 console.log(` + ${skillDir}`);
141 created++;
142 }
143
144 if (created > 0) {
145 console.log(`\nPinned '${command}' as a standalone shortcut in ${created} location(s).`);
146 console.log(`You can now use /${command} directly.`);
147 }
148
149 return created > 0;
150 }
151
152 /**
153 * Unpin a command: remove shortcut skill from all harness dirs.
154 */
155 function unpin(command, projectRoot) {
156 const harnessDirs = findHarnessDirs(projectRoot);
157 let removed = 0;
158
159 for (const skillsDir of harnessDirs) {
160 const skillDir = join(skillsDir, command);
161 if (!existsSync(skillDir)) continue;
162
163 const skillMd = join(skillDir, 'SKILL.md');
164 if (!existsSync(skillMd)) continue;
165
166 // Safety: only remove if it's a pinned skill
167 const content = readFileSync(skillMd, 'utf-8');
168 if (!content.includes(PIN_MARKER)) {
169 console.log(` SKIP: ${skillDir} (not a pinned skill)`);
170 continue;
171 }
172
173 rmSync(skillDir, { recursive: true, force: true });
174 console.log(` - ${skillDir}`);
175 removed++;
176 }
177
178 if (removed > 0) {
179 console.log(`\nUnpinned '${command}' from ${removed} location(s).`);
180 console.log(`Use /impeccable ${command} to access it.`);
181 } else {
182 console.log(`No pinned '${command}' shortcut found.`);
183 }
184
185 return removed > 0;
186 }
187
188 // --- CLI ---
189 const [,, action, command] = process.argv;
190
191 if (!action || !command) {
192 console.log('Usage: node pin.mjs <pin|unpin> <command>');
193 console.log(`\nAvailable commands: ${VALID_COMMANDS.join(', ')}`);
194 process.exit(1);
195 }
196
197 if (action !== 'pin' && action !== 'unpin') {
198 console.error(`Unknown action: ${action}. Use 'pin' or 'unpin'.`);
199 process.exit(1);
200 }
201
202 if (!VALID_COMMANDS.includes(command)) {
203 console.error(`Unknown command: ${command}`);
204 console.error(`Available commands: ${VALID_COMMANDS.join(', ')}`);
205 process.exit(1);
206 }
207
208 const root = findProjectRoot();
209
210 if (action === 'pin') {
211 pin(command, root);
212 } else {
213 unpin(command, root);
214 }
215
215 lines Plain Text