mirror of
https://github.com/obra/superpowers.git
synced 2026-04-21 00:49:06 +08:00
Drop installer script and AGENTS.md gatekeeper
Testing showed native skill discovery works without the AGENTS.md gatekeeper — using-superpowers bootstraps itself via SKILL.md frontmatter. Install is now just clone + symlink, driven by INSTALL.md. No Node.js dependency. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,33 +1,35 @@
|
||||
# Installing Superpowers for Codex
|
||||
|
||||
Quick setup to enable superpowers skills in Codex. The installer links your skills into Codex's native discovery path so they load automatically.
|
||||
Enable superpowers skills in Codex via native skill discovery. Just clone and symlink.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- [Node.js](https://nodejs.org/) (v16+)
|
||||
- Git
|
||||
|
||||
## Installation
|
||||
|
||||
1. **Clone superpowers repository**:
|
||||
1. **Clone the superpowers repository:**
|
||||
```bash
|
||||
git clone https://github.com/obra/superpowers.git ~/.codex/superpowers
|
||||
```
|
||||
|
||||
2. **Run the installer**:
|
||||
2. **Create the skills symlink:**
|
||||
```bash
|
||||
node ~/.codex/superpowers/.codex/install-codex.mjs
|
||||
mkdir -p ~/.agents/skills
|
||||
ln -s ~/.codex/superpowers/skills ~/.agents/skills/superpowers
|
||||
```
|
||||
|
||||
**Windows (PowerShell):**
|
||||
```powershell
|
||||
New-Item -ItemType Directory -Force -Path "$env:USERPROFILE\.agents\skills"
|
||||
cmd /c mklink /J "$env:USERPROFILE\.agents\skills\superpowers" "$env:USERPROFILE\.codex\superpowers\skills"
|
||||
```
|
||||
|
||||
3. **Restart Codex** (quit and relaunch the CLI) to discover the skills.
|
||||
|
||||
**Windows:** The installer creates a junction (`mklink /J`), which works without Developer Mode.
|
||||
## Migrating from old bootstrap
|
||||
|
||||
## What the installer does
|
||||
|
||||
- Links `~/.agents/skills/superpowers` → `~/.codex/superpowers/skills` (symlink on macOS/Linux, junction on Windows)
|
||||
- Adds a gatekeeper block to `~/.codex/AGENTS.md` that tells Codex to use superpowers skills
|
||||
- If you had the old bootstrap setup, it removes it automatically
|
||||
If your `~/.codex/AGENTS.md` contains a superpowers block that references `superpowers-codex bootstrap`, remove that block — it's no longer needed. Native skill discovery replaces the old bootstrap system.
|
||||
|
||||
## Verify
|
||||
|
||||
@@ -35,7 +37,7 @@ Quick setup to enable superpowers skills in Codex. The installer links your skil
|
||||
ls -la ~/.agents/skills/superpowers
|
||||
```
|
||||
|
||||
You should see a symlink (or junction) pointing to your superpowers skills directory.
|
||||
You should see a symlink (or junction on Windows) pointing to your superpowers skills directory.
|
||||
|
||||
## Updating
|
||||
|
||||
@@ -43,7 +45,7 @@ You should see a symlink (or junction) pointing to your superpowers skills direc
|
||||
cd ~/.codex/superpowers && git pull
|
||||
```
|
||||
|
||||
Skills update instantly through the link.
|
||||
Skills update instantly through the symlink.
|
||||
|
||||
## Uninstalling
|
||||
|
||||
@@ -51,4 +53,4 @@ Skills update instantly through the link.
|
||||
rm ~/.agents/skills/superpowers
|
||||
```
|
||||
|
||||
Then remove the block between `<!-- superpowers:begin -->` and `<!-- superpowers:end -->` from `~/.codex/AGENTS.md`. Optionally delete the clone: `rm -rf ~/.codex/superpowers`.
|
||||
Optionally delete the clone: `rm -rf ~/.codex/superpowers`.
|
||||
|
||||
@@ -1,199 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
// Minimal installer for Superpowers native Codex skill discovery.
|
||||
// Creates a symlink from ~/.agents/skills/superpowers → repo skills/
|
||||
// and updates ~/.codex/AGENTS.md with a gatekeeper block.
|
||||
|
||||
import { existsSync, readFileSync, writeFileSync, mkdirSync, symlinkSync, lstatSync, readlinkSync, unlinkSync } from 'fs';
|
||||
import { join, resolve, dirname } from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
import { homedir, platform } from 'os';
|
||||
import { execSync } from 'child_process';
|
||||
|
||||
const home = homedir();
|
||||
const isWindows = platform() === 'win32';
|
||||
|
||||
// Paths
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
const repoSkillsDir = resolve(__dirname, '..', 'skills');
|
||||
const agentsSkillsDir = join(home, '.agents', 'skills');
|
||||
const symlinkPath = join(agentsSkillsDir, 'superpowers');
|
||||
const agentsMdPath = join(home, '.codex', 'AGENTS.md');
|
||||
|
||||
const GATEKEEPER_BEGIN = '<!-- superpowers:begin -->';
|
||||
const GATEKEEPER_END = '<!-- superpowers:end -->';
|
||||
|
||||
const GATEKEEPER_BLOCK = `${GATEKEEPER_BEGIN}
|
||||
## Superpowers
|
||||
|
||||
**MANDATORY: Invoke $using-superpowers before proceeding with ANY task.**
|
||||
|
||||
IF A SKILL APPLIES TO YOUR TASK, YOU DO NOT HAVE A CHOICE. YOU MUST USE IT.
|
||||
|
||||
### Tool Mappings for Codex
|
||||
- \`TodoWrite\` → \`update_plan\`
|
||||
- \`Task\`/\`Subagent\` → \`spawn_agent\` + \`wait\` (or sequential if collab disabled)
|
||||
- \`Skill\` tool → native \`$skill-name\` mention
|
||||
- \`Read\`, \`Write\`, \`Edit\`, \`Bash\` → use your native equivalents
|
||||
${GATEKEEPER_END}`;
|
||||
|
||||
// --- Symlink creation ---
|
||||
|
||||
function createSymlink() {
|
||||
// Ensure parent dir exists
|
||||
mkdirSync(agentsSkillsDir, { recursive: true });
|
||||
|
||||
// Check if target already exists
|
||||
if (existsSync(symlinkPath)) {
|
||||
try {
|
||||
const stat = lstatSync(symlinkPath);
|
||||
if (stat.isSymbolicLink()) {
|
||||
const target = readlinkSync(symlinkPath);
|
||||
if (resolve(target) === resolve(repoSkillsDir)) {
|
||||
console.log(`✓ Symlink already exists: ${symlinkPath} → ${repoSkillsDir}`);
|
||||
return;
|
||||
}
|
||||
console.log(`! Existing symlink points to ${target}, updating to ${repoSkillsDir}`);
|
||||
unlinkSync(symlinkPath);
|
||||
} else {
|
||||
console.error(`✗ ${symlinkPath} already exists and is not a symlink.`);
|
||||
console.error(` Remove it manually and re-run the installer.`);
|
||||
process.exit(1);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(`✗ Cannot inspect ${symlinkPath}: ${err.message}`);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
// Create the link (symlink on macOS/Linux, junction on Windows)
|
||||
const linkType = isWindows ? 'junction' : 'dir';
|
||||
const linkLabel = isWindows ? 'junction' : 'symlink';
|
||||
try {
|
||||
symlinkSync(repoSkillsDir, symlinkPath, linkType);
|
||||
console.log(`✓ Created ${linkLabel}: ${symlinkPath} → ${repoSkillsDir}`);
|
||||
} catch (err) {
|
||||
if (isWindows) {
|
||||
// Node API failed on Windows — try junction via cmd.exe
|
||||
try {
|
||||
execSync(`cmd /c mklink /J "${symlinkPath}" "${repoSkillsDir}"`, { stdio: 'pipe' });
|
||||
console.log(`✓ Created junction: ${symlinkPath} → ${repoSkillsDir}`);
|
||||
} catch (junctionErr) {
|
||||
console.error(`✗ Failed to create junction: ${junctionErr.message}`);
|
||||
console.error(` Try running PowerShell as administrator.`);
|
||||
process.exit(1);
|
||||
}
|
||||
} else {
|
||||
console.error(`✗ Failed to create symlink: ${err.message}`);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// --- AGENTS.md update ---
|
||||
|
||||
// Patterns that identify the old bootstrap block
|
||||
const OLD_BLOCK_PATTERNS = [
|
||||
'superpowers-codex bootstrap',
|
||||
'superpowers-codex use-skill',
|
||||
'superpowers-codex find-skills',
|
||||
];
|
||||
|
||||
function removeOldBootstrapBlock(content) {
|
||||
const lines = content.split('\n');
|
||||
const result = [];
|
||||
let inOldBlock = false;
|
||||
|
||||
for (let i = 0; i < lines.length; i++) {
|
||||
const line = lines[i];
|
||||
|
||||
// Detect start of old block by its section header
|
||||
if (!inOldBlock && line.startsWith('## ') && line.includes('Superpowers')) {
|
||||
// Check if this section contains old bootstrap patterns (look ahead)
|
||||
const sectionEnd = lines.findIndex((l, j) => j > i && l.startsWith('## ') && !l.includes('Superpowers'));
|
||||
const sectionLines = lines.slice(i, sectionEnd === -1 ? lines.length : sectionEnd);
|
||||
const hasOldPatterns = sectionLines.some(l => OLD_BLOCK_PATTERNS.some(p => l.includes(p)));
|
||||
if (hasOldPatterns) {
|
||||
inOldBlock = true;
|
||||
// Also remove leading blank lines before the header
|
||||
while (result.length > 0 && result[result.length - 1].trim() === '') {
|
||||
result.pop();
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (inOldBlock) {
|
||||
// End of old block: next non-Superpowers section header
|
||||
if (line.startsWith('## ') && !line.includes('Superpowers')) {
|
||||
inOldBlock = false;
|
||||
result.push(line);
|
||||
continue;
|
||||
}
|
||||
// Skip this line (part of old block)
|
||||
continue;
|
||||
}
|
||||
|
||||
result.push(line);
|
||||
}
|
||||
|
||||
return result.join('\n');
|
||||
}
|
||||
|
||||
function updateAgentsMd() {
|
||||
let content = '';
|
||||
|
||||
if (existsSync(agentsMdPath)) {
|
||||
content = readFileSync(agentsMdPath, 'utf8');
|
||||
|
||||
// Check if gatekeeper already present
|
||||
if (content.includes(GATEKEEPER_BEGIN)) {
|
||||
// Replace existing gatekeeper block
|
||||
const beginIdx = content.indexOf(GATEKEEPER_BEGIN);
|
||||
const endIdx = content.indexOf(GATEKEEPER_END);
|
||||
if (endIdx > beginIdx) {
|
||||
content = content.slice(0, beginIdx) + GATEKEEPER_BLOCK + content.slice(endIdx + GATEKEEPER_END.length);
|
||||
writeFileSync(agentsMdPath, content, 'utf8');
|
||||
console.log(`✓ Updated existing gatekeeper block in ${agentsMdPath}`);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Remove old bootstrap block if present
|
||||
const hasOldBlock = OLD_BLOCK_PATTERNS.some(p => content.includes(p));
|
||||
if (hasOldBlock) {
|
||||
content = removeOldBootstrapBlock(content);
|
||||
console.log(` Removed old bootstrap block from AGENTS.md`);
|
||||
}
|
||||
} else {
|
||||
// Ensure directory exists
|
||||
mkdirSync(join(home, '.codex'), { recursive: true });
|
||||
}
|
||||
|
||||
// Append gatekeeper block
|
||||
const separator = content.length > 0 && !content.endsWith('\n\n') ? (content.endsWith('\n') ? '\n' : '\n\n') : '';
|
||||
content = content + separator + GATEKEEPER_BLOCK + '\n';
|
||||
writeFileSync(agentsMdPath, content, 'utf8');
|
||||
console.log(`✓ Added gatekeeper block to ${agentsMdPath}`);
|
||||
}
|
||||
|
||||
// --- Main ---
|
||||
|
||||
console.log('Superpowers — Codex Native Skills Installer');
|
||||
console.log('');
|
||||
|
||||
if (!existsSync(repoSkillsDir)) {
|
||||
console.error(`✗ Skills directory not found: ${repoSkillsDir}`);
|
||||
console.error(` Are you running this from the superpowers repo?`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
createSymlink();
|
||||
updateAgentsMd();
|
||||
|
||||
console.log('');
|
||||
console.log('Done! Restart Codex to discover superpowers skills natively.');
|
||||
console.log('');
|
||||
console.log('To update: cd ~/.codex/superpowers && git pull');
|
||||
console.log('To remove: rm ~/.agents/skills/superpowers');
|
||||
console.log(' Then remove the superpowers block from ~/.codex/AGENTS.md');
|
||||
Reference in New Issue
Block a user