mirror of
https://github.com/obra/superpowers.git
synced 2026-04-22 09:29:03 +08:00
Compare commits
14 Commits
v4.0.2
...
fix/openco
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4b6cef98ac | ||
|
|
03087b13b8 | ||
|
|
493ac18dfe | ||
|
|
35d4fbcd0b | ||
|
|
19c70afc99 | ||
|
|
405a025eea | ||
|
|
36fcd57626 | ||
|
|
3964d18670 | ||
|
|
a01a135fe1 | ||
|
|
ac471e69c2 | ||
|
|
a08f088968 | ||
|
|
b9e16498b9 | ||
|
|
f6d50c74b2 | ||
|
|
3dac35e0b3 |
@@ -9,7 +9,7 @@
|
|||||||
{
|
{
|
||||||
"name": "superpowers",
|
"name": "superpowers",
|
||||||
"description": "Core skills library for Claude Code: TDD, debugging, collaboration patterns, and proven techniques",
|
"description": "Core skills library for Claude Code: TDD, debugging, collaboration patterns, and proven techniques",
|
||||||
"version": "4.0.2",
|
"version": "4.0.3",
|
||||||
"source": "./",
|
"source": "./",
|
||||||
"author": {
|
"author": {
|
||||||
"name": "Jesse Vincent",
|
"name": "Jesse Vincent",
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "superpowers",
|
"name": "superpowers",
|
||||||
"description": "Core skills library for Claude Code: TDD, debugging, collaboration patterns, and proven techniques",
|
"description": "Core skills library for Claude Code: TDD, debugging, collaboration patterns, and proven techniques",
|
||||||
"version": "4.0.2",
|
"version": "4.1.1",
|
||||||
"author": {
|
"author": {
|
||||||
"name": "Jesse Vincent",
|
"name": "Jesse Vincent",
|
||||||
"email": "jesse@fsck.com"
|
"email": "jesse@fsck.com"
|
||||||
|
|||||||
17
.gitattributes
vendored
Normal file
17
.gitattributes
vendored
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
# Ensure shell scripts always have LF line endings
|
||||||
|
*.sh text eol=lf
|
||||||
|
|
||||||
|
# Ensure the polyglot wrapper keeps LF (it's parsed by both cmd and bash)
|
||||||
|
*.cmd text eol=lf
|
||||||
|
|
||||||
|
# Common text files
|
||||||
|
*.md text eol=lf
|
||||||
|
*.json text eol=lf
|
||||||
|
*.js text eol=lf
|
||||||
|
*.mjs text eol=lf
|
||||||
|
*.ts text eol=lf
|
||||||
|
|
||||||
|
# Explicitly mark binary files
|
||||||
|
*.png binary
|
||||||
|
*.jpg binary
|
||||||
|
*.gif binary
|
||||||
@@ -3,15 +3,13 @@
|
|||||||
## Prerequisites
|
## Prerequisites
|
||||||
|
|
||||||
- [OpenCode.ai](https://opencode.ai) installed
|
- [OpenCode.ai](https://opencode.ai) installed
|
||||||
- Node.js installed
|
|
||||||
- Git installed
|
- Git installed
|
||||||
|
|
||||||
## Installation Steps
|
## Installation Steps
|
||||||
|
|
||||||
### 1. Install Superpowers
|
### 1. Clone Superpowers
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
mkdir -p ~/.config/opencode/superpowers
|
|
||||||
git clone https://github.com/obra/superpowers.git ~/.config/opencode/superpowers
|
git clone https://github.com/obra/superpowers.git ~/.config/opencode/superpowers
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -20,32 +18,43 @@ git clone https://github.com/obra/superpowers.git ~/.config/opencode/superpowers
|
|||||||
Create a symlink so OpenCode discovers the plugin:
|
Create a symlink so OpenCode discovers the plugin:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
mkdir -p ~/.config/opencode/plugin
|
mkdir -p ~/.config/opencode/plugins
|
||||||
ln -sf ~/.config/opencode/superpowers/.opencode/plugin/superpowers.js ~/.config/opencode/plugin/superpowers.js
|
rm -f ~/.config/opencode/plugins/superpowers.js
|
||||||
|
ln -s ~/.config/opencode/superpowers/.opencode/plugins/superpowers.js ~/.config/opencode/plugins/superpowers.js
|
||||||
```
|
```
|
||||||
|
|
||||||
### 3. Restart OpenCode
|
### 3. Symlink Skills
|
||||||
|
|
||||||
Restart OpenCode. The plugin will automatically inject superpowers context via the chat.message hook.
|
Create a symlink so OpenCode's native skill tool discovers superpowers skills:
|
||||||
|
|
||||||
You should see superpowers is active when you ask "do you have superpowers?"
|
```bash
|
||||||
|
mkdir -p ~/.config/opencode/skills
|
||||||
|
rm -rf ~/.config/opencode/skills/superpowers
|
||||||
|
ln -s ~/.config/opencode/superpowers/skills ~/.config/opencode/skills/superpowers
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. Restart OpenCode
|
||||||
|
|
||||||
|
Restart OpenCode. The plugin will automatically inject superpowers context.
|
||||||
|
|
||||||
|
Verify by asking: "do you have superpowers?"
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
### Finding Skills
|
### Finding Skills
|
||||||
|
|
||||||
Use the `find_skills` tool to list all available skills:
|
Use OpenCode's native `skill` tool to list available skills:
|
||||||
|
|
||||||
```
|
```
|
||||||
use find_skills tool
|
use skill tool to list skills
|
||||||
```
|
```
|
||||||
|
|
||||||
### Loading a Skill
|
### Loading a Skill
|
||||||
|
|
||||||
Use the `use_skill` tool to load a specific skill:
|
Use OpenCode's native `skill` tool to load a specific skill:
|
||||||
|
|
||||||
```
|
```
|
||||||
use use_skill tool with skill_name: "superpowers:brainstorming"
|
use skill tool to load superpowers/brainstorming
|
||||||
```
|
```
|
||||||
|
|
||||||
### Personal Skills
|
### Personal Skills
|
||||||
@@ -69,36 +78,11 @@ description: Use when [condition] - [what it does]
|
|||||||
[Your skill content here]
|
[Your skill content here]
|
||||||
```
|
```
|
||||||
|
|
||||||
Personal skills override superpowers skills with the same name.
|
|
||||||
|
|
||||||
### Project Skills
|
### Project Skills
|
||||||
|
|
||||||
Create project-specific skills in your OpenCode project:
|
Create project-specific skills in `.opencode/skills/` within your project.
|
||||||
|
|
||||||
```bash
|
**Skill Priority:** Project skills > Personal skills > Superpowers skills
|
||||||
# In your OpenCode project
|
|
||||||
mkdir -p .opencode/skills/my-project-skill
|
|
||||||
```
|
|
||||||
|
|
||||||
Create `.opencode/skills/my-project-skill/SKILL.md`:
|
|
||||||
|
|
||||||
```markdown
|
|
||||||
---
|
|
||||||
name: my-project-skill
|
|
||||||
description: Use when [condition] - [what it does]
|
|
||||||
---
|
|
||||||
|
|
||||||
# My Project Skill
|
|
||||||
|
|
||||||
[Your skill content here]
|
|
||||||
```
|
|
||||||
|
|
||||||
**Skill Priority:** Project skills override personal skills, which override superpowers skills.
|
|
||||||
|
|
||||||
**Skill Naming:**
|
|
||||||
- `project:skill-name` - Force project skill lookup
|
|
||||||
- `skill-name` - Searches project → personal → superpowers
|
|
||||||
- `superpowers:skill-name` - Force superpowers skill lookup
|
|
||||||
|
|
||||||
## Updating
|
## Updating
|
||||||
|
|
||||||
@@ -111,25 +95,25 @@ git pull
|
|||||||
|
|
||||||
### Plugin not loading
|
### Plugin not loading
|
||||||
|
|
||||||
1. Check plugin file exists: `ls ~/.config/opencode/superpowers/.opencode/plugin/superpowers.js`
|
1. Check plugin symlink: `ls -l ~/.config/opencode/plugins/superpowers.js`
|
||||||
2. Check OpenCode logs for errors
|
2. Check source exists: `ls ~/.config/opencode/superpowers/.opencode/plugins/superpowers.js`
|
||||||
3. Verify Node.js is installed: `node --version`
|
3. Check OpenCode logs for errors
|
||||||
|
|
||||||
### Skills not found
|
### Skills not found
|
||||||
|
|
||||||
1. Verify skills directory exists: `ls ~/.config/opencode/superpowers/skills`
|
1. Check skills symlink: `ls -l ~/.config/opencode/skills/superpowers`
|
||||||
2. Use `find_skills` tool to see what's discovered
|
2. Verify it points to: `~/.config/opencode/superpowers/skills`
|
||||||
3. Check file structure: each skill should have a `SKILL.md` file
|
3. Use `skill` tool to list what's discovered
|
||||||
|
|
||||||
### Tool mapping issues
|
### Tool mapping
|
||||||
|
|
||||||
When a skill references a Claude Code tool you don't have:
|
When skills reference Claude Code tools:
|
||||||
- `TodoWrite` → use `update_plan`
|
- `TodoWrite` → `update_plan`
|
||||||
- `Task` with subagents → use `@mention` syntax to invoke OpenCode subagents
|
- `Task` with subagents → `@mention` syntax
|
||||||
- `Skill` → use `use_skill` tool
|
- `Skill` tool → OpenCode's native `skill` tool
|
||||||
- File operations → use your native tools
|
- File operations → your native tools
|
||||||
|
|
||||||
## Getting Help
|
## Getting Help
|
||||||
|
|
||||||
- Report issues: https://github.com/obra/superpowers/issues
|
- Report issues: https://github.com/obra/superpowers/issues
|
||||||
- Documentation: https://github.com/obra/superpowers
|
- Full documentation: https://github.com/obra/superpowers/blob/main/docs/README.opencode.md
|
||||||
|
|||||||
@@ -1,215 +0,0 @@
|
|||||||
/**
|
|
||||||
* Superpowers plugin for OpenCode.ai
|
|
||||||
*
|
|
||||||
* Provides custom tools for loading and discovering skills,
|
|
||||||
* with prompt generation for agent configuration.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import path from 'path';
|
|
||||||
import fs from 'fs';
|
|
||||||
import os from 'os';
|
|
||||||
import { fileURLToPath } from 'url';
|
|
||||||
import { tool } from '@opencode-ai/plugin/tool';
|
|
||||||
import * as skillsCore from '../../lib/skills-core.js';
|
|
||||||
|
|
||||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
||||||
|
|
||||||
export const SuperpowersPlugin = async ({ client, directory }) => {
|
|
||||||
const homeDir = os.homedir();
|
|
||||||
const projectSkillsDir = path.join(directory, '.opencode/skills');
|
|
||||||
// Derive superpowers skills dir from plugin location (works for both symlinked and local installs)
|
|
||||||
const superpowersSkillsDir = path.resolve(__dirname, '../../skills');
|
|
||||||
const personalSkillsDir = path.join(homeDir, '.config/opencode/skills');
|
|
||||||
|
|
||||||
// Helper to generate bootstrap content
|
|
||||||
const getBootstrapContent = (compact = false) => {
|
|
||||||
const usingSuperpowersPath = skillsCore.resolveSkillPath('using-superpowers', superpowersSkillsDir, personalSkillsDir);
|
|
||||||
if (!usingSuperpowersPath) return null;
|
|
||||||
|
|
||||||
const fullContent = fs.readFileSync(usingSuperpowersPath.skillFile, 'utf8');
|
|
||||||
const content = skillsCore.stripFrontmatter(fullContent);
|
|
||||||
|
|
||||||
const toolMapping = compact
|
|
||||||
? `**Tool Mapping:** TodoWrite->update_plan, Task->@mention, Skill->use_skill
|
|
||||||
|
|
||||||
**Skills naming (priority order):** project: > personal > superpowers:`
|
|
||||||
: `**Tool Mapping for OpenCode:**
|
|
||||||
When skills reference tools you don't have, substitute OpenCode equivalents:
|
|
||||||
- \`TodoWrite\` → \`update_plan\`
|
|
||||||
- \`Task\` tool with subagents → Use OpenCode's subagent system (@mention)
|
|
||||||
- \`Skill\` tool → \`use_skill\` custom tool
|
|
||||||
- \`Read\`, \`Write\`, \`Edit\`, \`Bash\` → Your native tools
|
|
||||||
|
|
||||||
**Skills naming (priority order):**
|
|
||||||
- Project skills: \`project:skill-name\` (in .opencode/skills/)
|
|
||||||
- Personal skills: \`skill-name\` (in ~/.config/opencode/skills/)
|
|
||||||
- Superpowers skills: \`superpowers:skill-name\`
|
|
||||||
- Project skills override personal, which override superpowers when names match`;
|
|
||||||
|
|
||||||
return `<EXTREMELY_IMPORTANT>
|
|
||||||
You have superpowers.
|
|
||||||
|
|
||||||
**IMPORTANT: The using-superpowers skill content is included below. It is ALREADY LOADED - you are currently following it. Do NOT use the use_skill tool to load "using-superpowers" - that would be redundant. Use use_skill only for OTHER skills.**
|
|
||||||
|
|
||||||
${content}
|
|
||||||
|
|
||||||
${toolMapping}
|
|
||||||
</EXTREMELY_IMPORTANT>`;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Helper to inject bootstrap via session.prompt
|
|
||||||
const injectBootstrap = async (sessionID, compact = false) => {
|
|
||||||
const bootstrapContent = getBootstrapContent(compact);
|
|
||||||
if (!bootstrapContent) return false;
|
|
||||||
|
|
||||||
try {
|
|
||||||
await client.session.prompt({
|
|
||||||
path: { id: sessionID },
|
|
||||||
body: {
|
|
||||||
noReply: true,
|
|
||||||
parts: [{ type: "text", text: bootstrapContent, synthetic: true }]
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return true;
|
|
||||||
} catch (err) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return {
|
|
||||||
tool: {
|
|
||||||
use_skill: tool({
|
|
||||||
description: 'Load and read a specific skill to guide your work. Skills contain proven workflows, mandatory processes, and expert techniques.',
|
|
||||||
args: {
|
|
||||||
skill_name: tool.schema.string().describe('Name of the skill to load (e.g., "superpowers:brainstorming", "my-custom-skill", or "project:my-skill")')
|
|
||||||
},
|
|
||||||
execute: async (args, context) => {
|
|
||||||
const { skill_name } = args;
|
|
||||||
|
|
||||||
// Resolve with priority: project > personal > superpowers
|
|
||||||
// Check for project: prefix first
|
|
||||||
const forceProject = skill_name.startsWith('project:');
|
|
||||||
const actualSkillName = forceProject ? skill_name.replace(/^project:/, '') : skill_name;
|
|
||||||
|
|
||||||
let resolved = null;
|
|
||||||
|
|
||||||
// Try project skills first (if project: prefix or no prefix)
|
|
||||||
if (forceProject || !skill_name.startsWith('superpowers:')) {
|
|
||||||
const projectPath = path.join(projectSkillsDir, actualSkillName);
|
|
||||||
const projectSkillFile = path.join(projectPath, 'SKILL.md');
|
|
||||||
if (fs.existsSync(projectSkillFile)) {
|
|
||||||
resolved = {
|
|
||||||
skillFile: projectSkillFile,
|
|
||||||
sourceType: 'project',
|
|
||||||
skillPath: actualSkillName
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fall back to personal/superpowers resolution
|
|
||||||
if (!resolved && !forceProject) {
|
|
||||||
resolved = skillsCore.resolveSkillPath(skill_name, superpowersSkillsDir, personalSkillsDir);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!resolved) {
|
|
||||||
return `Error: Skill "${skill_name}" not found.\n\nRun find_skills to see available skills.`;
|
|
||||||
}
|
|
||||||
|
|
||||||
const fullContent = fs.readFileSync(resolved.skillFile, 'utf8');
|
|
||||||
const { name, description } = skillsCore.extractFrontmatter(resolved.skillFile);
|
|
||||||
const content = skillsCore.stripFrontmatter(fullContent);
|
|
||||||
const skillDirectory = path.dirname(resolved.skillFile);
|
|
||||||
|
|
||||||
const skillHeader = `# ${name || skill_name}
|
|
||||||
# ${description || ''}
|
|
||||||
# Supporting tools and docs are in ${skillDirectory}
|
|
||||||
# ============================================`;
|
|
||||||
|
|
||||||
// Insert as user message with noReply for persistence across compaction
|
|
||||||
try {
|
|
||||||
await client.session.prompt({
|
|
||||||
path: { id: context.sessionID },
|
|
||||||
body: {
|
|
||||||
noReply: true,
|
|
||||||
parts: [
|
|
||||||
{ type: "text", text: `Loading skill: ${name || skill_name}`, synthetic: true },
|
|
||||||
{ type: "text", text: `${skillHeader}\n\n${content}`, synthetic: true }
|
|
||||||
]
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} catch (err) {
|
|
||||||
// Fallback: return content directly if message insertion fails
|
|
||||||
return `${skillHeader}\n\n${content}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
return `Launching skill: ${name || skill_name}`;
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
find_skills: tool({
|
|
||||||
description: 'List all available skills in the project, personal, and superpowers skill libraries.',
|
|
||||||
args: {},
|
|
||||||
execute: async (args, context) => {
|
|
||||||
const projectSkills = skillsCore.findSkillsInDir(projectSkillsDir, 'project', 3);
|
|
||||||
const personalSkills = skillsCore.findSkillsInDir(personalSkillsDir, 'personal', 3);
|
|
||||||
const superpowersSkills = skillsCore.findSkillsInDir(superpowersSkillsDir, 'superpowers', 3);
|
|
||||||
|
|
||||||
// Priority: project > personal > superpowers
|
|
||||||
const allSkills = [...projectSkills, ...personalSkills, ...superpowersSkills];
|
|
||||||
|
|
||||||
if (allSkills.length === 0) {
|
|
||||||
return 'No skills found. Install superpowers skills to ~/.config/opencode/superpowers/skills/ or add project skills to .opencode/skills/';
|
|
||||||
}
|
|
||||||
|
|
||||||
let output = 'Available skills:\n\n';
|
|
||||||
|
|
||||||
for (const skill of allSkills) {
|
|
||||||
let namespace;
|
|
||||||
switch (skill.sourceType) {
|
|
||||||
case 'project':
|
|
||||||
namespace = 'project:';
|
|
||||||
break;
|
|
||||||
case 'personal':
|
|
||||||
namespace = '';
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
namespace = 'superpowers:';
|
|
||||||
}
|
|
||||||
const skillName = skill.name || path.basename(skill.path);
|
|
||||||
|
|
||||||
output += `${namespace}${skillName}\n`;
|
|
||||||
if (skill.description) {
|
|
||||||
output += ` ${skill.description}\n`;
|
|
||||||
}
|
|
||||||
output += ` Directory: ${skill.path}\n\n`;
|
|
||||||
}
|
|
||||||
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
|
||||||
event: async ({ event }) => {
|
|
||||||
// Extract sessionID from various event structures
|
|
||||||
const getSessionID = () => {
|
|
||||||
return event.properties?.info?.id ||
|
|
||||||
event.properties?.sessionID ||
|
|
||||||
event.session?.id;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Inject bootstrap at session creation (before first user message)
|
|
||||||
if (event.type === 'session.created') {
|
|
||||||
const sessionID = getSessionID();
|
|
||||||
if (sessionID) {
|
|
||||||
await injectBootstrap(sessionID, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Re-inject bootstrap after context compaction (compact version to save tokens)
|
|
||||||
if (event.type === 'session.compacted') {
|
|
||||||
const sessionID = getSessionID();
|
|
||||||
if (sessionID) {
|
|
||||||
await injectBootstrap(sessionID, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
|
||||||
95
.opencode/plugins/superpowers.js
Normal file
95
.opencode/plugins/superpowers.js
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
/**
|
||||||
|
* Superpowers plugin for OpenCode.ai
|
||||||
|
*
|
||||||
|
* Injects superpowers bootstrap context via system prompt transform.
|
||||||
|
* Skills are discovered via OpenCode's native skill tool from symlinked directory.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import path from 'path';
|
||||||
|
import fs from 'fs';
|
||||||
|
import os from 'os';
|
||||||
|
import { fileURLToPath } from 'url';
|
||||||
|
|
||||||
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||||
|
|
||||||
|
// Simple frontmatter extraction (avoid dependency on skills-core for bootstrap)
|
||||||
|
const extractAndStripFrontmatter = (content) => {
|
||||||
|
const match = content.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
|
||||||
|
if (!match) return { frontmatter: {}, content };
|
||||||
|
|
||||||
|
const frontmatterStr = match[1];
|
||||||
|
const body = match[2];
|
||||||
|
const frontmatter = {};
|
||||||
|
|
||||||
|
for (const line of frontmatterStr.split('\n')) {
|
||||||
|
const colonIdx = line.indexOf(':');
|
||||||
|
if (colonIdx > 0) {
|
||||||
|
const key = line.slice(0, colonIdx).trim();
|
||||||
|
const value = line.slice(colonIdx + 1).trim().replace(/^["']|["']$/g, '');
|
||||||
|
frontmatter[key] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return { frontmatter, content: body };
|
||||||
|
};
|
||||||
|
|
||||||
|
// Normalize a path: trim whitespace, expand ~, resolve to absolute
|
||||||
|
const normalizePath = (p, homeDir) => {
|
||||||
|
if (!p || typeof p !== 'string') return null;
|
||||||
|
let normalized = p.trim();
|
||||||
|
if (!normalized) return null;
|
||||||
|
if (normalized.startsWith('~/')) {
|
||||||
|
normalized = path.join(homeDir, normalized.slice(2));
|
||||||
|
} else if (normalized === '~') {
|
||||||
|
normalized = homeDir;
|
||||||
|
}
|
||||||
|
return path.resolve(normalized);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const SuperpowersPlugin = async ({ client, directory }) => {
|
||||||
|
const homeDir = os.homedir();
|
||||||
|
const superpowersSkillsDir = path.resolve(__dirname, '../../skills');
|
||||||
|
const envConfigDir = normalizePath(process.env.OPENCODE_CONFIG_DIR, homeDir);
|
||||||
|
const configDir = envConfigDir || path.join(homeDir, '.config/opencode');
|
||||||
|
|
||||||
|
// Helper to generate bootstrap content
|
||||||
|
const getBootstrapContent = () => {
|
||||||
|
// Try to load using-superpowers skill
|
||||||
|
const skillPath = path.join(superpowersSkillsDir, 'using-superpowers', 'SKILL.md');
|
||||||
|
if (!fs.existsSync(skillPath)) return null;
|
||||||
|
|
||||||
|
const fullContent = fs.readFileSync(skillPath, 'utf8');
|
||||||
|
const { content } = extractAndStripFrontmatter(fullContent);
|
||||||
|
|
||||||
|
const toolMapping = `**Tool Mapping for OpenCode:**
|
||||||
|
When skills reference tools you don't have, substitute OpenCode equivalents:
|
||||||
|
- \`TodoWrite\` → \`update_plan\`
|
||||||
|
- \`Task\` tool with subagents → Use OpenCode's subagent system (@mention)
|
||||||
|
- \`Skill\` tool → OpenCode's native \`skill\` tool
|
||||||
|
- \`Read\`, \`Write\`, \`Edit\`, \`Bash\` → Your native tools
|
||||||
|
|
||||||
|
**Skills location:**
|
||||||
|
Superpowers skills are in \`${configDir}/skills/superpowers/\`
|
||||||
|
Use OpenCode's native \`skill\` tool to list and load skills.`;
|
||||||
|
|
||||||
|
return `<EXTREMELY_IMPORTANT>
|
||||||
|
You have superpowers.
|
||||||
|
|
||||||
|
**IMPORTANT: The using-superpowers skill content is included below. It is ALREADY LOADED - you are currently following it. Do NOT use the skill tool to load "using-superpowers" again - that would be redundant.**
|
||||||
|
|
||||||
|
${content}
|
||||||
|
|
||||||
|
${toolMapping}
|
||||||
|
</EXTREMELY_IMPORTANT>`;
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
// Use system prompt transform to inject bootstrap (fixes #226 agent reset bug)
|
||||||
|
'experimental.chat.system.transform': async (_input, output) => {
|
||||||
|
const bootstrap = getBootstrapContent();
|
||||||
|
if (bootstrap) {
|
||||||
|
(output.system ||= []).push(bootstrap);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
@@ -1,5 +1,74 @@
|
|||||||
# Superpowers Release Notes
|
# Superpowers Release Notes
|
||||||
|
|
||||||
|
## v4.1.1 (2026-01-23)
|
||||||
|
|
||||||
|
### Fixes
|
||||||
|
|
||||||
|
**OpenCode: Standardized on `plugins/` directory per official docs (#343)**
|
||||||
|
|
||||||
|
OpenCode's official documentation uses `~/.config/opencode/plugins/` (plural). Our docs previously used `plugin/` (singular). While OpenCode accepts both forms, we've standardized on the official convention to avoid confusion.
|
||||||
|
|
||||||
|
Changes:
|
||||||
|
- Renamed `.opencode/plugin/` to `.opencode/plugins/` in repo structure
|
||||||
|
- Updated all installation docs (INSTALL.md, README.opencode.md) across all platforms
|
||||||
|
- Updated test scripts to match
|
||||||
|
|
||||||
|
**OpenCode: Fixed symlink instructions (#339, #342)**
|
||||||
|
|
||||||
|
- Added explicit `rm` before `ln -s` (fixes "file already exists" errors on reinstall)
|
||||||
|
- Added missing skills symlink step that was absent from INSTALL.md
|
||||||
|
- Updated from deprecated `use_skill`/`find_skills` to native `skill` tool references
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## v4.1.0 (2026-01-23)
|
||||||
|
|
||||||
|
### Breaking Changes
|
||||||
|
|
||||||
|
**OpenCode: Switched to native skills system**
|
||||||
|
|
||||||
|
Superpowers for OpenCode now uses OpenCode's native `skill` tool instead of custom `use_skill`/`find_skills` tools. This is a cleaner integration that works with OpenCode's built-in skill discovery.
|
||||||
|
|
||||||
|
**Migration required:** Skills must be symlinked to `~/.config/opencode/skills/superpowers/` (see updated installation docs).
|
||||||
|
|
||||||
|
### Fixes
|
||||||
|
|
||||||
|
**OpenCode: Fixed agent reset on session start (#226)**
|
||||||
|
|
||||||
|
The previous bootstrap injection method using `session.prompt({ noReply: true })` caused OpenCode to reset the selected agent to "build" on first message. Now uses `experimental.chat.system.transform` hook which modifies the system prompt directly without side effects.
|
||||||
|
|
||||||
|
**OpenCode: Fixed Windows installation (#232)**
|
||||||
|
|
||||||
|
- Removed dependency on `skills-core.js` (eliminates broken relative imports when file is copied instead of symlinked)
|
||||||
|
- Added comprehensive Windows installation docs for cmd.exe, PowerShell, and Git Bash
|
||||||
|
- Documented proper symlink vs junction usage for each platform
|
||||||
|
|
||||||
|
**Claude Code: Fixed Windows hook execution for Claude Code 2.1.x**
|
||||||
|
|
||||||
|
Claude Code 2.1.x changed how hooks execute on Windows: it now auto-detects `.sh` files in commands and prepends `bash `. This broke the polyglot wrapper pattern because `bash "run-hook.cmd" session-start.sh` tries to execute the .cmd file as a bash script.
|
||||||
|
|
||||||
|
Fix: hooks.json now calls session-start.sh directly. Claude Code 2.1.x handles the bash invocation automatically. Also added .gitattributes to enforce LF line endings for shell scripts (fixes CRLF issues on Windows checkout).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## v4.0.3 (2025-12-26)
|
||||||
|
|
||||||
|
### Improvements
|
||||||
|
|
||||||
|
**Strengthened using-superpowers skill for explicit skill requests**
|
||||||
|
|
||||||
|
Addressed a failure mode where Claude would skip invoking a skill even when the user explicitly requested it by name (e.g., "subagent-driven-development, please"). Claude would think "I know what that means" and start working directly instead of loading the skill.
|
||||||
|
|
||||||
|
Changes:
|
||||||
|
- Updated "The Rule" to say "Invoke relevant or requested skills" instead of "Check for skills" - emphasizing active invocation over passive checking
|
||||||
|
- Added "BEFORE any response or action" - the original wording only mentioned "response" but Claude would sometimes take action without responding first
|
||||||
|
- Added reassurance that invoking a wrong skill is okay - reduces hesitation
|
||||||
|
- Added new red flag: "I know what that means" → Knowing the concept ≠ using the skill
|
||||||
|
|
||||||
|
**Added explicit skill request tests**
|
||||||
|
|
||||||
|
New test suite in `tests/explicit-skill-requests/` that verifies Claude correctly invokes skills when users request them by name. Includes single-turn and multi-turn test scenarios.
|
||||||
|
|
||||||
## v4.0.2 (2025-12-23)
|
## v4.0.2 (2025-12-23)
|
||||||
|
|
||||||
### Fixes
|
### Fixes
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ Complete guide for using Superpowers with [OpenCode.ai](https://opencode.ai).
|
|||||||
Tell OpenCode:
|
Tell OpenCode:
|
||||||
|
|
||||||
```
|
```
|
||||||
Clone https://github.com/obra/superpowers to ~/.config/opencode/superpowers, then create directory ~/.config/opencode/plugin, then symlink ~/.config/opencode/superpowers/.opencode/plugin/superpowers.js to ~/.config/opencode/plugin/superpowers.js, then restart opencode.
|
Clone https://github.com/obra/superpowers to ~/.config/opencode/superpowers, then create directory ~/.config/opencode/plugins, then symlink ~/.config/opencode/superpowers/.opencode/plugins/superpowers.js to ~/.config/opencode/plugins/superpowers.js, then symlink ~/.config/opencode/superpowers/skills to ~/.config/opencode/skills/superpowers, then restart opencode.
|
||||||
```
|
```
|
||||||
|
|
||||||
## Manual Installation
|
## Manual Installation
|
||||||
@@ -15,59 +15,175 @@ Clone https://github.com/obra/superpowers to ~/.config/opencode/superpowers, the
|
|||||||
### Prerequisites
|
### Prerequisites
|
||||||
|
|
||||||
- [OpenCode.ai](https://opencode.ai) installed
|
- [OpenCode.ai](https://opencode.ai) installed
|
||||||
- Node.js installed
|
|
||||||
- Git installed
|
- Git installed
|
||||||
|
|
||||||
### Installation Steps
|
### macOS / Linux
|
||||||
|
|
||||||
#### 1. Install Superpowers
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
mkdir -p ~/.config/opencode/superpowers
|
# 1. Install Superpowers (or update existing)
|
||||||
|
if [ -d ~/.config/opencode/superpowers ]; then
|
||||||
|
cd ~/.config/opencode/superpowers && git pull
|
||||||
|
else
|
||||||
|
git clone https://github.com/obra/superpowers.git ~/.config/opencode/superpowers
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 2. Create directories
|
||||||
|
mkdir -p ~/.config/opencode/plugins ~/.config/opencode/skills
|
||||||
|
|
||||||
|
# 3. Remove old symlinks/directories if they exist
|
||||||
|
rm -f ~/.config/opencode/plugins/superpowers.js
|
||||||
|
rm -rf ~/.config/opencode/skills/superpowers
|
||||||
|
|
||||||
|
# 4. Create symlinks
|
||||||
|
ln -s ~/.config/opencode/superpowers/.opencode/plugins/superpowers.js ~/.config/opencode/plugins/superpowers.js
|
||||||
|
ln -s ~/.config/opencode/superpowers/skills ~/.config/opencode/skills/superpowers
|
||||||
|
|
||||||
|
# 5. Restart OpenCode
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Verify Installation
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ls -l ~/.config/opencode/plugins/superpowers.js
|
||||||
|
ls -l ~/.config/opencode/skills/superpowers
|
||||||
|
```
|
||||||
|
|
||||||
|
Both should show symlinks pointing to the superpowers directory.
|
||||||
|
|
||||||
|
### Windows
|
||||||
|
|
||||||
|
**Prerequisites:**
|
||||||
|
- Git installed
|
||||||
|
- Either **Developer Mode** enabled OR **Administrator privileges**
|
||||||
|
- Windows 10: Settings → Update & Security → For developers
|
||||||
|
- Windows 11: Settings → System → For developers
|
||||||
|
|
||||||
|
Pick your shell below: [Command Prompt](#command-prompt) | [PowerShell](#powershell) | [Git Bash](#git-bash)
|
||||||
|
|
||||||
|
#### Command Prompt
|
||||||
|
|
||||||
|
Run as Administrator, or with Developer Mode enabled:
|
||||||
|
|
||||||
|
```cmd
|
||||||
|
:: 1. Install Superpowers
|
||||||
|
git clone https://github.com/obra/superpowers.git "%USERPROFILE%\.config\opencode\superpowers"
|
||||||
|
|
||||||
|
:: 2. Create directories
|
||||||
|
mkdir "%USERPROFILE%\.config\opencode\plugins" 2>nul
|
||||||
|
mkdir "%USERPROFILE%\.config\opencode\skills" 2>nul
|
||||||
|
|
||||||
|
:: 3. Remove existing links (safe for reinstalls)
|
||||||
|
del "%USERPROFILE%\.config\opencode\plugins\superpowers.js" 2>nul
|
||||||
|
rmdir "%USERPROFILE%\.config\opencode\skills\superpowers" 2>nul
|
||||||
|
|
||||||
|
:: 4. Create plugin symlink (requires Developer Mode or Admin)
|
||||||
|
mklink "%USERPROFILE%\.config\opencode\plugins\superpowers.js" "%USERPROFILE%\.config\opencode\superpowers\.opencode\plugins\superpowers.js"
|
||||||
|
|
||||||
|
:: 5. Create skills junction (works without special privileges)
|
||||||
|
mklink /J "%USERPROFILE%\.config\opencode\skills\superpowers" "%USERPROFILE%\.config\opencode\superpowers\skills"
|
||||||
|
|
||||||
|
:: 6. Restart OpenCode
|
||||||
|
```
|
||||||
|
|
||||||
|
#### PowerShell
|
||||||
|
|
||||||
|
Run as Administrator, or with Developer Mode enabled:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
# 1. Install Superpowers
|
||||||
|
git clone https://github.com/obra/superpowers.git "$env:USERPROFILE\.config\opencode\superpowers"
|
||||||
|
|
||||||
|
# 2. Create directories
|
||||||
|
New-Item -ItemType Directory -Force -Path "$env:USERPROFILE\.config\opencode\plugins"
|
||||||
|
New-Item -ItemType Directory -Force -Path "$env:USERPROFILE\.config\opencode\skills"
|
||||||
|
|
||||||
|
# 3. Remove existing links (safe for reinstalls)
|
||||||
|
Remove-Item "$env:USERPROFILE\.config\opencode\plugins\superpowers.js" -Force -ErrorAction SilentlyContinue
|
||||||
|
Remove-Item "$env:USERPROFILE\.config\opencode\skills\superpowers" -Force -ErrorAction SilentlyContinue
|
||||||
|
|
||||||
|
# 4. Create plugin symlink (requires Developer Mode or Admin)
|
||||||
|
New-Item -ItemType SymbolicLink -Path "$env:USERPROFILE\.config\opencode\plugins\superpowers.js" -Target "$env:USERPROFILE\.config\opencode\superpowers\.opencode\plugins\superpowers.js"
|
||||||
|
|
||||||
|
# 5. Create skills junction (works without special privileges)
|
||||||
|
New-Item -ItemType Junction -Path "$env:USERPROFILE\.config\opencode\skills\superpowers" -Target "$env:USERPROFILE\.config\opencode\superpowers\skills"
|
||||||
|
|
||||||
|
# 6. Restart OpenCode
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Git Bash
|
||||||
|
|
||||||
|
Note: Git Bash's native `ln` command copies files instead of creating symlinks. Use `cmd //c mklink` instead (the `//c` is Git Bash syntax for `/c`).
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. Install Superpowers
|
||||||
git clone https://github.com/obra/superpowers.git ~/.config/opencode/superpowers
|
git clone https://github.com/obra/superpowers.git ~/.config/opencode/superpowers
|
||||||
|
|
||||||
|
# 2. Create directories
|
||||||
|
mkdir -p ~/.config/opencode/plugins ~/.config/opencode/skills
|
||||||
|
|
||||||
|
# 3. Remove existing links (safe for reinstalls)
|
||||||
|
rm -f ~/.config/opencode/plugins/superpowers.js 2>/dev/null
|
||||||
|
rm -rf ~/.config/opencode/skills/superpowers 2>/dev/null
|
||||||
|
|
||||||
|
# 4. Create plugin symlink (requires Developer Mode or Admin)
|
||||||
|
cmd //c "mklink \"$(cygpath -w ~/.config/opencode/plugins/superpowers.js)\" \"$(cygpath -w ~/.config/opencode/superpowers/.opencode/plugins/superpowers.js)\""
|
||||||
|
|
||||||
|
# 5. Create skills junction (works without special privileges)
|
||||||
|
cmd //c "mklink /J \"$(cygpath -w ~/.config/opencode/skills/superpowers)\" \"$(cygpath -w ~/.config/opencode/superpowers/skills)\""
|
||||||
|
|
||||||
|
# 6. Restart OpenCode
|
||||||
```
|
```
|
||||||
|
|
||||||
#### 2. Register the Plugin
|
#### WSL Users
|
||||||
|
|
||||||
OpenCode discovers plugins from `~/.config/opencode/plugin/`. Create a symlink:
|
If running OpenCode inside WSL, use the [macOS / Linux](#macos--linux) instructions instead.
|
||||||
|
|
||||||
```bash
|
#### Verify Installation
|
||||||
mkdir -p ~/.config/opencode/plugin
|
|
||||||
ln -sf ~/.config/opencode/superpowers/.opencode/plugin/superpowers.js ~/.config/opencode/plugin/superpowers.js
|
**Command Prompt:**
|
||||||
|
```cmd
|
||||||
|
dir /AL "%USERPROFILE%\.config\opencode\plugins"
|
||||||
|
dir /AL "%USERPROFILE%\.config\opencode\skills"
|
||||||
```
|
```
|
||||||
|
|
||||||
Alternatively, for project-local installation:
|
**PowerShell:**
|
||||||
|
```powershell
|
||||||
```bash
|
Get-ChildItem "$env:USERPROFILE\.config\opencode\plugins" | Where-Object { $_.LinkType }
|
||||||
# In your OpenCode project
|
Get-ChildItem "$env:USERPROFILE\.config\opencode\skills" | Where-Object { $_.LinkType }
|
||||||
mkdir -p .opencode/plugin
|
|
||||||
ln -sf ~/.config/opencode/superpowers/.opencode/plugin/superpowers.js .opencode/plugin/superpowers.js
|
|
||||||
```
|
```
|
||||||
|
|
||||||
#### 3. Restart OpenCode
|
Look for `<SYMLINK>` or `<JUNCTION>` in the output.
|
||||||
|
|
||||||
Restart OpenCode to load the plugin. Superpowers will automatically activate.
|
#### Troubleshooting Windows
|
||||||
|
|
||||||
|
**"You do not have sufficient privilege" error:**
|
||||||
|
- Enable Developer Mode in Windows Settings, OR
|
||||||
|
- Right-click your terminal → "Run as Administrator"
|
||||||
|
|
||||||
|
**"Cannot create a file when that file already exists":**
|
||||||
|
- Run the removal commands (step 3) first, then retry
|
||||||
|
|
||||||
|
**Symlinks not working after git clone:**
|
||||||
|
- Run `git config --global core.symlinks true` and re-clone
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
### Finding Skills
|
### Finding Skills
|
||||||
|
|
||||||
Use the `find_skills` tool to list all available skills:
|
Use OpenCode's native `skill` tool to list all available skills:
|
||||||
|
|
||||||
```
|
```
|
||||||
use find_skills tool
|
use skill tool to list skills
|
||||||
```
|
```
|
||||||
|
|
||||||
### Loading a Skill
|
### Loading a Skill
|
||||||
|
|
||||||
Use the `use_skill` tool to load a specific skill:
|
Use OpenCode's native `skill` tool to load a specific skill:
|
||||||
|
|
||||||
```
|
```
|
||||||
use use_skill tool with skill_name: "superpowers:brainstorming"
|
use skill tool to load superpowers/brainstorming
|
||||||
```
|
```
|
||||||
|
|
||||||
Skills are automatically inserted into the conversation and persist across context compaction.
|
|
||||||
|
|
||||||
### Personal Skills
|
### Personal Skills
|
||||||
|
|
||||||
Create your own skills in `~/.config/opencode/skills/`:
|
Create your own skills in `~/.config/opencode/skills/`:
|
||||||
@@ -111,66 +227,48 @@ description: Use when [condition] - [what it does]
|
|||||||
[Your skill content here]
|
[Your skill content here]
|
||||||
```
|
```
|
||||||
|
|
||||||
## Skill Priority
|
## Skill Locations
|
||||||
|
|
||||||
Skills are resolved with this priority order:
|
OpenCode discovers skills from these locations:
|
||||||
|
|
||||||
1. **Project skills** (`.opencode/skills/`) - Highest priority
|
1. **Project skills** (`.opencode/skills/`) - Highest priority
|
||||||
2. **Personal skills** (`~/.config/opencode/skills/`)
|
2. **Personal skills** (`~/.config/opencode/skills/`)
|
||||||
3. **Superpowers skills** (`~/.config/opencode/superpowers/skills/`)
|
3. **Superpowers skills** (`~/.config/opencode/skills/superpowers/`) - via symlink
|
||||||
|
|
||||||
You can force resolution to a specific level:
|
|
||||||
- `project:skill-name` - Force project skill
|
|
||||||
- `skill-name` - Search project → personal → superpowers
|
|
||||||
- `superpowers:skill-name` - Force superpowers skill
|
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
### Automatic Context Injection
|
### Automatic Context Injection
|
||||||
|
|
||||||
The plugin automatically injects superpowers context via the chat.message hook on every session. No manual configuration needed.
|
The plugin automatically injects superpowers context via the `experimental.chat.system.transform` hook. This adds the "using-superpowers" skill content to the system prompt on every request.
|
||||||
|
|
||||||
### Message Insertion Pattern
|
### Native Skills Integration
|
||||||
|
|
||||||
When you load a skill with `use_skill`, it's inserted as a user message with `noReply: true`. This ensures skills persist throughout long conversations, even when OpenCode compacts context.
|
Superpowers uses OpenCode's native `skill` tool for skill discovery and loading. Skills are symlinked into `~/.config/opencode/skills/superpowers/` so they appear alongside your personal and project skills.
|
||||||
|
|
||||||
### Compaction Resilience
|
|
||||||
|
|
||||||
The plugin listens for `session.compacted` events and automatically re-injects the core superpowers bootstrap to maintain functionality after context compaction.
|
|
||||||
|
|
||||||
### Tool Mapping
|
### Tool Mapping
|
||||||
|
|
||||||
Skills written for Claude Code are automatically adapted for OpenCode. The plugin provides mapping instructions:
|
Skills written for Claude Code are automatically adapted for OpenCode. The bootstrap provides mapping instructions:
|
||||||
|
|
||||||
- `TodoWrite` → `update_plan`
|
- `TodoWrite` → `update_plan`
|
||||||
- `Task` with subagents → OpenCode's `@mention` system
|
- `Task` with subagents → OpenCode's `@mention` system
|
||||||
- `Skill` tool → `use_skill` custom tool
|
- `Skill` tool → OpenCode's native `skill` tool
|
||||||
- File operations → Native OpenCode tools
|
- File operations → Native OpenCode tools
|
||||||
|
|
||||||
## Architecture
|
## Architecture
|
||||||
|
|
||||||
### Plugin Structure
|
### Plugin Structure
|
||||||
|
|
||||||
**Location:** `~/.config/opencode/superpowers/.opencode/plugin/superpowers.js`
|
**Location:** `~/.config/opencode/superpowers/.opencode/plugins/superpowers.js`
|
||||||
|
|
||||||
**Components:**
|
**Components:**
|
||||||
- Two custom tools: `use_skill`, `find_skills`
|
- `experimental.chat.system.transform` hook for bootstrap injection
|
||||||
- chat.message hook for initial context injection
|
- Reads and injects the "using-superpowers" skill content
|
||||||
- event handler for session.compacted re-injection
|
|
||||||
- Uses shared `lib/skills-core.js` module (also used by Codex)
|
|
||||||
|
|
||||||
### Shared Core Module
|
### Skills
|
||||||
|
|
||||||
**Location:** `~/.config/opencode/superpowers/lib/skills-core.js`
|
**Location:** `~/.config/opencode/skills/superpowers/` (symlink to `~/.config/opencode/superpowers/skills/`)
|
||||||
|
|
||||||
**Functions:**
|
Skills are discovered by OpenCode's native skill system. Each skill has a `SKILL.md` file with YAML frontmatter.
|
||||||
- `extractFrontmatter()` - Parse skill metadata
|
|
||||||
- `stripFrontmatter()` - Remove metadata from content
|
|
||||||
- `findSkillsInDir()` - Recursive skill discovery
|
|
||||||
- `resolveSkillPath()` - Skill resolution with shadowing
|
|
||||||
- `checkForUpdates()` - Git update detection
|
|
||||||
|
|
||||||
This module is shared between OpenCode and Codex implementations for code reuse.
|
|
||||||
|
|
||||||
## Updating
|
## Updating
|
||||||
|
|
||||||
@@ -185,28 +283,28 @@ Restart OpenCode to load the updates.
|
|||||||
|
|
||||||
### Plugin not loading
|
### Plugin not loading
|
||||||
|
|
||||||
1. Check plugin file exists: `ls ~/.config/opencode/superpowers/.opencode/plugin/superpowers.js`
|
1. Check plugin exists: `ls ~/.config/opencode/superpowers/.opencode/plugins/superpowers.js`
|
||||||
2. Check symlink: `ls -l ~/.config/opencode/plugin/superpowers.js`
|
2. Check symlink/junction: `ls -l ~/.config/opencode/plugins/` (macOS/Linux) or `dir /AL %USERPROFILE%\.config\opencode\plugins` (Windows)
|
||||||
3. Check OpenCode logs: `opencode run "test" --print-logs --log-level DEBUG`
|
3. Check OpenCode logs: `opencode run "test" --print-logs --log-level DEBUG`
|
||||||
4. Look for: `service=plugin path=file:///.../superpowers.js loading plugin`
|
4. Look for plugin loading message in logs
|
||||||
|
|
||||||
### Skills not found
|
### Skills not found
|
||||||
|
|
||||||
1. Verify skills directory: `ls ~/.config/opencode/superpowers/skills`
|
1. Verify skills symlink: `ls -l ~/.config/opencode/skills/superpowers` (should point to superpowers/skills/)
|
||||||
2. Use `find_skills` tool to see what's discovered
|
2. Use OpenCode's `skill` tool to list available skills
|
||||||
3. Check skill structure: each skill needs a `SKILL.md` file
|
3. Check skill structure: each skill needs a `SKILL.md` file with valid frontmatter
|
||||||
|
|
||||||
### Tools not working
|
### Windows: Module not found error
|
||||||
|
|
||||||
1. Verify plugin loaded: Check OpenCode logs for plugin loading message
|
If you see `Cannot find module` errors on Windows:
|
||||||
2. Check Node.js version: The plugin requires Node.js for ES modules
|
- **Cause:** Git Bash `ln -sf` copies files instead of creating symlinks
|
||||||
3. Test plugin manually: `node --input-type=module -e "import('file://~/.config/opencode/plugin/superpowers.js').then(m => console.log(Object.keys(m)))"`
|
- **Fix:** Use `mklink /J` directory junctions instead (see Windows installation steps)
|
||||||
|
|
||||||
### Context not injecting
|
### Bootstrap not appearing
|
||||||
|
|
||||||
1. Check if chat.message hook is working
|
1. Verify using-superpowers skill exists: `ls ~/.config/opencode/superpowers/skills/using-superpowers/SKILL.md`
|
||||||
2. Verify using-superpowers skill exists
|
2. Check OpenCode version supports `experimental.chat.system.transform` hook
|
||||||
3. Check OpenCode version (requires recent version with plugin support)
|
3. Restart OpenCode after plugin changes
|
||||||
|
|
||||||
## Getting Help
|
## Getting Help
|
||||||
|
|
||||||
@@ -216,19 +314,17 @@ Restart OpenCode to load the updates.
|
|||||||
|
|
||||||
## Testing
|
## Testing
|
||||||
|
|
||||||
The implementation includes an automated test suite at `tests/opencode/`:
|
Verify your installation:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Run all tests
|
# Check plugin loads
|
||||||
./tests/opencode/run-tests.sh --integration --verbose
|
opencode run --print-logs "hello" 2>&1 | grep -i superpowers
|
||||||
|
|
||||||
# Run specific test
|
# Check skills are discoverable
|
||||||
./tests/opencode/run-tests.sh --test test-tools.sh
|
opencode run "use skill tool to list all skills" 2>&1 | grep -i superpowers
|
||||||
|
|
||||||
|
# Check bootstrap injection
|
||||||
|
opencode run "what superpowers do you have?"
|
||||||
```
|
```
|
||||||
|
|
||||||
Tests verify:
|
The agent should mention having superpowers and be able to list skills from `superpowers/`.
|
||||||
- Plugin loading
|
|
||||||
- Skills-core library functionality
|
|
||||||
- Tool execution (use_skill, find_skills)
|
|
||||||
- Skill priority resolution
|
|
||||||
- Proper isolation with temp HOME
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
"hooks": [
|
"hooks": [
|
||||||
{
|
{
|
||||||
"type": "command",
|
"type": "command",
|
||||||
"command": "\"${CLAUDE_PLUGIN_ROOT}/hooks/run-hook.cmd\" session-start.sh"
|
"command": "${CLAUDE_PLUGIN_ROOT}/hooks/session-start.sh"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,30 @@
|
|||||||
: << 'CMDBLOCK'
|
: << 'CMDBLOCK'
|
||||||
@echo off
|
@echo off
|
||||||
REM Polyglot wrapper: runs .sh scripts cross-platform
|
REM ============================================================================
|
||||||
|
REM DEPRECATED: This polyglot wrapper is no longer used as of Claude Code 2.1.x
|
||||||
|
REM ============================================================================
|
||||||
|
REM
|
||||||
|
REM Claude Code 2.1.x changed the Windows execution model for hooks:
|
||||||
|
REM
|
||||||
|
REM Before (2.0.x): Hooks ran with shell:true, using the system default shell.
|
||||||
|
REM This wrapper provided cross-platform compatibility by
|
||||||
|
REM being both a valid .cmd file (Windows) and bash script.
|
||||||
|
REM
|
||||||
|
REM After (2.1.x): Claude Code now auto-detects .sh files in hook commands
|
||||||
|
REM and prepends "bash " on Windows. This broke the wrapper
|
||||||
|
REM because the command:
|
||||||
|
REM "run-hook.cmd" session-start.sh
|
||||||
|
REM became:
|
||||||
|
REM bash "run-hook.cmd" session-start.sh
|
||||||
|
REM ...and bash cannot execute a .cmd file.
|
||||||
|
REM
|
||||||
|
REM The fix: hooks.json now calls session-start.sh directly. Claude Code 2.1.x
|
||||||
|
REM handles the bash invocation automatically on Windows.
|
||||||
|
REM
|
||||||
|
REM This file is kept for reference and potential backward compatibility.
|
||||||
|
REM ============================================================================
|
||||||
|
REM
|
||||||
|
REM Original purpose: Polyglot wrapper to run .sh scripts cross-platform
|
||||||
REM Usage: run-hook.cmd <script-name> [args...]
|
REM Usage: run-hook.cmd <script-name> [args...]
|
||||||
REM The script should be in the same directory as this wrapper
|
REM The script should be in the same directory as this wrapper
|
||||||
|
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ This is not negotiable. This is not optional. You cannot rationalize your way ou
|
|||||||
|
|
||||||
## The Rule
|
## The Rule
|
||||||
|
|
||||||
**Check for skills BEFORE ANY RESPONSE.** This includes clarifying questions. Even 1% chance means invoke the Skill tool first.
|
**Invoke relevant or requested skills BEFORE any response or action.** Even a 1% chance a skill might apply means that you should invoke the skill to check. If an invoked skill turns out to be wrong for the situation, you don't need to use it.
|
||||||
|
|
||||||
```dot
|
```dot
|
||||||
digraph skill_flow {
|
digraph skill_flow {
|
||||||
@@ -62,6 +62,7 @@ These thoughts mean STOP—you're rationalizing:
|
|||||||
| "The skill is overkill" | Simple things become complex. Use it. |
|
| "The skill is overkill" | Simple things become complex. Use it. |
|
||||||
| "I'll just do this one thing first" | Check BEFORE doing anything. |
|
| "I'll just do this one thing first" | Check BEFORE doing anything. |
|
||||||
| "This feels productive" | Undisciplined action wastes time. Skills prevent this. |
|
| "This feels productive" | Undisciplined action wastes time. Skills prevent this. |
|
||||||
|
| "I know what that means" | Knowing the concept ≠ using the skill. Invoke it. |
|
||||||
|
|
||||||
## Skill Priority
|
## Skill Priority
|
||||||
|
|
||||||
|
|||||||
@@ -324,7 +324,7 @@ Before deploying skill, verify you followed RED-GREEN-REFACTOR:
|
|||||||
- [ ] Added explicit counters for each loophole
|
- [ ] Added explicit counters for each loophole
|
||||||
- [ ] Updated rationalization table
|
- [ ] Updated rationalization table
|
||||||
- [ ] Updated red flags list
|
- [ ] Updated red flags list
|
||||||
- [ ] Updated description ith violation symptoms
|
- [ ] Updated description with violation symptoms
|
||||||
- [ ] Re-tested - agent still complies
|
- [ ] Re-tested - agent still complies
|
||||||
- [ ] Meta-tested to verify clarity
|
- [ ] Meta-tested to verify clarity
|
||||||
- [ ] Agent follows rule under maximum pressure
|
- [ ] Agent follows rule under maximum pressure
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
The plan is done. docs/plans/auth-system.md has everything.
|
||||||
|
|
||||||
|
Do subagent-driven development on this - start with Task 1, dispatch a subagent, then we'll review.
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
Great, the plan is complete. I've saved it to docs/plans/auth-system.md.
|
||||||
|
|
||||||
|
Here's a summary of what we designed:
|
||||||
|
- Task 1: Add User Model with email/password fields
|
||||||
|
- Task 2: Create auth routes for login/register
|
||||||
|
- Task 3: Add JWT middleware for protected routes
|
||||||
|
- Task 4: Write tests for all auth functionality
|
||||||
|
|
||||||
|
Two execution options:
|
||||||
|
1. Subagent-Driven (this session) - dispatch a fresh subagent per task
|
||||||
|
2. Parallel Session (separate) - open new Claude Code session
|
||||||
|
|
||||||
|
Which approach do you want?
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
subagent-driven-development, please
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
[Previous assistant message]:
|
||||||
|
Plan complete and saved to docs/plans/auth-system.md.
|
||||||
|
|
||||||
|
Two execution options:
|
||||||
|
1. Subagent-Driven (this session) - I dispatch a fresh subagent per task, review between tasks, fast iteration within this conversation
|
||||||
|
2. Parallel Session (separate) - Open a new Claude Code session with the execute-plan skill, batch execution with review checkpoints
|
||||||
|
|
||||||
|
Which approach do you want to use for implementation?
|
||||||
|
|
||||||
|
[Your response]:
|
||||||
|
subagent-driven-development, please
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
I have my implementation plan ready at docs/plans/auth-system.md.
|
||||||
|
|
||||||
|
I want to use subagent-driven-development to execute it. That means:
|
||||||
|
- Dispatch a fresh subagent for each task in the plan
|
||||||
|
- Review the output between tasks
|
||||||
|
- Keep iteration fast within this conversation
|
||||||
|
|
||||||
|
Let's start - please read the plan and begin dispatching subagents for each task.
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
I have a plan at docs/plans/auth-system.md that's ready to implement.
|
||||||
|
|
||||||
|
subagent-driven-development, please
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
please use the brainstorming skill to help me think through this feature
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
Plan is at docs/plans/auth-system.md.
|
||||||
|
|
||||||
|
subagent-driven-development, please. Don't waste time - just read the plan and start dispatching subagents immediately.
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
subagent-driven-development, please
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
use systematic-debugging to figure out what's wrong
|
||||||
70
tests/explicit-skill-requests/run-all.sh
Executable file
70
tests/explicit-skill-requests/run-all.sh
Executable file
@@ -0,0 +1,70 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Run all explicit skill request tests
|
||||||
|
# Usage: ./run-all.sh
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
PROMPTS_DIR="$SCRIPT_DIR/prompts"
|
||||||
|
|
||||||
|
echo "=== Running All Explicit Skill Request Tests ==="
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
PASSED=0
|
||||||
|
FAILED=0
|
||||||
|
RESULTS=""
|
||||||
|
|
||||||
|
# Test: subagent-driven-development, please
|
||||||
|
echo ">>> Test 1: subagent-driven-development-please"
|
||||||
|
if "$SCRIPT_DIR/run-test.sh" "subagent-driven-development" "$PROMPTS_DIR/subagent-driven-development-please.txt"; then
|
||||||
|
PASSED=$((PASSED + 1))
|
||||||
|
RESULTS="$RESULTS\nPASS: subagent-driven-development-please"
|
||||||
|
else
|
||||||
|
FAILED=$((FAILED + 1))
|
||||||
|
RESULTS="$RESULTS\nFAIL: subagent-driven-development-please"
|
||||||
|
fi
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Test: use systematic-debugging
|
||||||
|
echo ">>> Test 2: use-systematic-debugging"
|
||||||
|
if "$SCRIPT_DIR/run-test.sh" "systematic-debugging" "$PROMPTS_DIR/use-systematic-debugging.txt"; then
|
||||||
|
PASSED=$((PASSED + 1))
|
||||||
|
RESULTS="$RESULTS\nPASS: use-systematic-debugging"
|
||||||
|
else
|
||||||
|
FAILED=$((FAILED + 1))
|
||||||
|
RESULTS="$RESULTS\nFAIL: use-systematic-debugging"
|
||||||
|
fi
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Test: please use brainstorming
|
||||||
|
echo ">>> Test 3: please-use-brainstorming"
|
||||||
|
if "$SCRIPT_DIR/run-test.sh" "brainstorming" "$PROMPTS_DIR/please-use-brainstorming.txt"; then
|
||||||
|
PASSED=$((PASSED + 1))
|
||||||
|
RESULTS="$RESULTS\nPASS: please-use-brainstorming"
|
||||||
|
else
|
||||||
|
FAILED=$((FAILED + 1))
|
||||||
|
RESULTS="$RESULTS\nFAIL: please-use-brainstorming"
|
||||||
|
fi
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Test: mid-conversation execute plan
|
||||||
|
echo ">>> Test 4: mid-conversation-execute-plan"
|
||||||
|
if "$SCRIPT_DIR/run-test.sh" "subagent-driven-development" "$PROMPTS_DIR/mid-conversation-execute-plan.txt"; then
|
||||||
|
PASSED=$((PASSED + 1))
|
||||||
|
RESULTS="$RESULTS\nPASS: mid-conversation-execute-plan"
|
||||||
|
else
|
||||||
|
FAILED=$((FAILED + 1))
|
||||||
|
RESULTS="$RESULTS\nFAIL: mid-conversation-execute-plan"
|
||||||
|
fi
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
echo "=== Summary ==="
|
||||||
|
echo -e "$RESULTS"
|
||||||
|
echo ""
|
||||||
|
echo "Passed: $PASSED"
|
||||||
|
echo "Failed: $FAILED"
|
||||||
|
echo "Total: $((PASSED + FAILED))"
|
||||||
|
|
||||||
|
if [ "$FAILED" -gt 0 ]; then
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
100
tests/explicit-skill-requests/run-claude-describes-sdd.sh
Executable file
100
tests/explicit-skill-requests/run-claude-describes-sdd.sh
Executable file
@@ -0,0 +1,100 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Test where Claude explicitly describes subagent-driven-development before user requests it
|
||||||
|
# This mimics the original failure scenario
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
PLUGIN_DIR="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
||||||
|
|
||||||
|
TIMESTAMP=$(date +%s)
|
||||||
|
OUTPUT_DIR="/tmp/superpowers-tests/${TIMESTAMP}/explicit-skill-requests/claude-describes"
|
||||||
|
mkdir -p "$OUTPUT_DIR"
|
||||||
|
|
||||||
|
PROJECT_DIR="$OUTPUT_DIR/project"
|
||||||
|
mkdir -p "$PROJECT_DIR/docs/plans"
|
||||||
|
|
||||||
|
echo "=== Test: Claude Describes SDD First ==="
|
||||||
|
echo "Output dir: $OUTPUT_DIR"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
cd "$PROJECT_DIR"
|
||||||
|
|
||||||
|
# Create a plan
|
||||||
|
cat > "$PROJECT_DIR/docs/plans/auth-system.md" << 'EOF'
|
||||||
|
# Auth System Implementation Plan
|
||||||
|
|
||||||
|
## Task 1: Add User Model
|
||||||
|
Create user model with email and password fields.
|
||||||
|
|
||||||
|
## Task 2: Add Auth Routes
|
||||||
|
Create login and register endpoints.
|
||||||
|
|
||||||
|
## Task 3: Add JWT Middleware
|
||||||
|
Protect routes with JWT validation.
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# Turn 1: Have Claude describe execution options including SDD
|
||||||
|
echo ">>> Turn 1: Ask Claude to describe execution options..."
|
||||||
|
claude -p "I have a plan at docs/plans/auth-system.md. Tell me about my options for executing it, including what subagent-driven-development means and how it works." \
|
||||||
|
--model haiku \
|
||||||
|
--plugin-dir "$PLUGIN_DIR" \
|
||||||
|
--dangerously-skip-permissions \
|
||||||
|
--max-turns 3 \
|
||||||
|
--output-format stream-json \
|
||||||
|
> "$OUTPUT_DIR/turn1.json" 2>&1 || true
|
||||||
|
echo "Done."
|
||||||
|
|
||||||
|
# Turn 2: THE CRITICAL TEST - now that Claude has explained it
|
||||||
|
echo ">>> Turn 2: Request subagent-driven-development..."
|
||||||
|
FINAL_LOG="$OUTPUT_DIR/turn2.json"
|
||||||
|
claude -p "subagent-driven-development, please" \
|
||||||
|
--continue \
|
||||||
|
--model haiku \
|
||||||
|
--plugin-dir "$PLUGIN_DIR" \
|
||||||
|
--dangerously-skip-permissions \
|
||||||
|
--max-turns 2 \
|
||||||
|
--output-format stream-json \
|
||||||
|
> "$FINAL_LOG" 2>&1 || true
|
||||||
|
echo "Done."
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
echo "=== Results ==="
|
||||||
|
|
||||||
|
# Check Turn 1 to see if Claude described SDD
|
||||||
|
echo "Turn 1 - Claude's description of options (excerpt):"
|
||||||
|
grep '"type":"assistant"' "$OUTPUT_DIR/turn1.json" | head -1 | jq -r '.message.content[0].text // .message.content' 2>/dev/null | head -c 800 || echo " (could not extract)"
|
||||||
|
echo ""
|
||||||
|
echo "---"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Check final turn
|
||||||
|
SKILL_PATTERN='"skill":"([^"]*:)?subagent-driven-development"'
|
||||||
|
if grep -q '"name":"Skill"' "$FINAL_LOG" && grep -qE "$SKILL_PATTERN" "$FINAL_LOG"; then
|
||||||
|
echo "PASS: Skill was triggered after Claude described it"
|
||||||
|
TRIGGERED=true
|
||||||
|
else
|
||||||
|
echo "FAIL: Skill was NOT triggered (Claude may have thought it already knew)"
|
||||||
|
TRIGGERED=false
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "Tools invoked in final turn:"
|
||||||
|
grep '"type":"tool_use"' "$FINAL_LOG" | grep -o '"name":"[^"]*"' | sort -u | head -10 || echo " (none)"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "Final turn response:"
|
||||||
|
grep '"type":"assistant"' "$FINAL_LOG" | head -1 | jq -r '.message.content[0].text // .message.content' 2>/dev/null | head -c 800 || echo " (could not extract)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "Skills triggered in final turn:"
|
||||||
|
grep -o '"skill":"[^"]*"' "$FINAL_LOG" 2>/dev/null | sort -u || echo " (none)"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "Logs in: $OUTPUT_DIR"
|
||||||
|
|
||||||
|
if [ "$TRIGGERED" = "true" ]; then
|
||||||
|
exit 0
|
||||||
|
else
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
113
tests/explicit-skill-requests/run-extended-multiturn-test.sh
Executable file
113
tests/explicit-skill-requests/run-extended-multiturn-test.sh
Executable file
@@ -0,0 +1,113 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Extended multi-turn test with more conversation history
|
||||||
|
# This tries to reproduce the failure by building more context
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
PLUGIN_DIR="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
||||||
|
|
||||||
|
TIMESTAMP=$(date +%s)
|
||||||
|
OUTPUT_DIR="/tmp/superpowers-tests/${TIMESTAMP}/explicit-skill-requests/extended-multiturn"
|
||||||
|
mkdir -p "$OUTPUT_DIR"
|
||||||
|
|
||||||
|
PROJECT_DIR="$OUTPUT_DIR/project"
|
||||||
|
mkdir -p "$PROJECT_DIR/docs/plans"
|
||||||
|
|
||||||
|
echo "=== Extended Multi-Turn Test ==="
|
||||||
|
echo "Output dir: $OUTPUT_DIR"
|
||||||
|
echo "Plugin dir: $PLUGIN_DIR"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
cd "$PROJECT_DIR"
|
||||||
|
|
||||||
|
# Turn 1: Start brainstorming
|
||||||
|
echo ">>> Turn 1: Brainstorming request..."
|
||||||
|
claude -p "I want to add user authentication to my app. Help me think through this." \
|
||||||
|
--plugin-dir "$PLUGIN_DIR" \
|
||||||
|
--dangerously-skip-permissions \
|
||||||
|
--max-turns 3 \
|
||||||
|
--output-format stream-json \
|
||||||
|
> "$OUTPUT_DIR/turn1.json" 2>&1 || true
|
||||||
|
echo "Done."
|
||||||
|
|
||||||
|
# Turn 2: Answer a brainstorming question
|
||||||
|
echo ">>> Turn 2: Answering questions..."
|
||||||
|
claude -p "Let's use JWT tokens with 24-hour expiry. Email/password registration." \
|
||||||
|
--continue \
|
||||||
|
--plugin-dir "$PLUGIN_DIR" \
|
||||||
|
--dangerously-skip-permissions \
|
||||||
|
--max-turns 3 \
|
||||||
|
--output-format stream-json \
|
||||||
|
> "$OUTPUT_DIR/turn2.json" 2>&1 || true
|
||||||
|
echo "Done."
|
||||||
|
|
||||||
|
# Turn 3: Ask to write a plan
|
||||||
|
echo ">>> Turn 3: Requesting plan..."
|
||||||
|
claude -p "Great, write this up as an implementation plan." \
|
||||||
|
--continue \
|
||||||
|
--plugin-dir "$PLUGIN_DIR" \
|
||||||
|
--dangerously-skip-permissions \
|
||||||
|
--max-turns 3 \
|
||||||
|
--output-format stream-json \
|
||||||
|
> "$OUTPUT_DIR/turn3.json" 2>&1 || true
|
||||||
|
echo "Done."
|
||||||
|
|
||||||
|
# Turn 4: Confirm plan looks good
|
||||||
|
echo ">>> Turn 4: Confirming plan..."
|
||||||
|
claude -p "The plan looks good. What are my options for executing it?" \
|
||||||
|
--continue \
|
||||||
|
--plugin-dir "$PLUGIN_DIR" \
|
||||||
|
--dangerously-skip-permissions \
|
||||||
|
--max-turns 2 \
|
||||||
|
--output-format stream-json \
|
||||||
|
> "$OUTPUT_DIR/turn4.json" 2>&1 || true
|
||||||
|
echo "Done."
|
||||||
|
|
||||||
|
# Turn 5: THE CRITICAL TEST
|
||||||
|
echo ">>> Turn 5: Requesting subagent-driven-development..."
|
||||||
|
FINAL_LOG="$OUTPUT_DIR/turn5.json"
|
||||||
|
claude -p "subagent-driven-development, please" \
|
||||||
|
--continue \
|
||||||
|
--plugin-dir "$PLUGIN_DIR" \
|
||||||
|
--dangerously-skip-permissions \
|
||||||
|
--max-turns 2 \
|
||||||
|
--output-format stream-json \
|
||||||
|
> "$FINAL_LOG" 2>&1 || true
|
||||||
|
echo "Done."
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
echo "=== Results ==="
|
||||||
|
|
||||||
|
# Check final turn
|
||||||
|
SKILL_PATTERN='"skill":"([^"]*:)?subagent-driven-development"'
|
||||||
|
if grep -q '"name":"Skill"' "$FINAL_LOG" && grep -qE "$SKILL_PATTERN" "$FINAL_LOG"; then
|
||||||
|
echo "PASS: Skill was triggered"
|
||||||
|
TRIGGERED=true
|
||||||
|
else
|
||||||
|
echo "FAIL: Skill was NOT triggered"
|
||||||
|
TRIGGERED=false
|
||||||
|
|
||||||
|
# Show what was invoked instead
|
||||||
|
echo ""
|
||||||
|
echo "Tools invoked in final turn:"
|
||||||
|
grep '"type":"tool_use"' "$FINAL_LOG" | jq -r '.content[] | select(.type=="tool_use") | .name' 2>/dev/null | head -10 || \
|
||||||
|
grep -o '"name":"[^"]*"' "$FINAL_LOG" | head -10 || echo " (none found)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "Skills triggered:"
|
||||||
|
grep -o '"skill":"[^"]*"' "$FINAL_LOG" 2>/dev/null | sort -u || echo " (none)"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "Final turn response (first 500 chars):"
|
||||||
|
grep '"type":"assistant"' "$FINAL_LOG" | head -1 | jq -r '.message.content[0].text // .message.content' 2>/dev/null | head -c 500 || echo " (could not extract)"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "Logs in: $OUTPUT_DIR"
|
||||||
|
|
||||||
|
if [ "$TRIGGERED" = "true" ]; then
|
||||||
|
exit 0
|
||||||
|
else
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
144
tests/explicit-skill-requests/run-haiku-test.sh
Executable file
144
tests/explicit-skill-requests/run-haiku-test.sh
Executable file
@@ -0,0 +1,144 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Test with haiku model and user's CLAUDE.md
|
||||||
|
# This tests whether a cheaper/faster model fails more easily
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
PLUGIN_DIR="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
||||||
|
|
||||||
|
TIMESTAMP=$(date +%s)
|
||||||
|
OUTPUT_DIR="/tmp/superpowers-tests/${TIMESTAMP}/explicit-skill-requests/haiku"
|
||||||
|
mkdir -p "$OUTPUT_DIR"
|
||||||
|
|
||||||
|
PROJECT_DIR="$OUTPUT_DIR/project"
|
||||||
|
mkdir -p "$PROJECT_DIR/docs/plans"
|
||||||
|
mkdir -p "$PROJECT_DIR/.claude"
|
||||||
|
|
||||||
|
echo "=== Haiku Model Test with User CLAUDE.md ==="
|
||||||
|
echo "Output dir: $OUTPUT_DIR"
|
||||||
|
echo "Plugin dir: $PLUGIN_DIR"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
cd "$PROJECT_DIR"
|
||||||
|
|
||||||
|
# Copy user's CLAUDE.md to simulate real environment
|
||||||
|
if [ -f "$HOME/.claude/CLAUDE.md" ]; then
|
||||||
|
cp "$HOME/.claude/CLAUDE.md" "$PROJECT_DIR/.claude/CLAUDE.md"
|
||||||
|
echo "Copied user CLAUDE.md"
|
||||||
|
else
|
||||||
|
echo "No user CLAUDE.md found, proceeding without"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Create a dummy plan file
|
||||||
|
cat > "$PROJECT_DIR/docs/plans/auth-system.md" << 'EOF'
|
||||||
|
# Auth System Implementation Plan
|
||||||
|
|
||||||
|
## Task 1: Add User Model
|
||||||
|
Create user model with email and password fields.
|
||||||
|
|
||||||
|
## Task 2: Add Auth Routes
|
||||||
|
Create login and register endpoints.
|
||||||
|
|
||||||
|
## Task 3: Add JWT Middleware
|
||||||
|
Protect routes with JWT validation.
|
||||||
|
|
||||||
|
## Task 4: Write Tests
|
||||||
|
Add comprehensive test coverage.
|
||||||
|
EOF
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Turn 1: Start brainstorming
|
||||||
|
echo ">>> Turn 1: Brainstorming request..."
|
||||||
|
claude -p "I want to add user authentication to my app. Help me think through this." \
|
||||||
|
--model haiku \
|
||||||
|
--plugin-dir "$PLUGIN_DIR" \
|
||||||
|
--dangerously-skip-permissions \
|
||||||
|
--max-turns 3 \
|
||||||
|
--output-format stream-json \
|
||||||
|
> "$OUTPUT_DIR/turn1.json" 2>&1 || true
|
||||||
|
echo "Done."
|
||||||
|
|
||||||
|
# Turn 2: Answer questions
|
||||||
|
echo ">>> Turn 2: Answering questions..."
|
||||||
|
claude -p "Let's use JWT tokens with 24-hour expiry. Email/password registration." \
|
||||||
|
--continue \
|
||||||
|
--model haiku \
|
||||||
|
--plugin-dir "$PLUGIN_DIR" \
|
||||||
|
--dangerously-skip-permissions \
|
||||||
|
--max-turns 3 \
|
||||||
|
--output-format stream-json \
|
||||||
|
> "$OUTPUT_DIR/turn2.json" 2>&1 || true
|
||||||
|
echo "Done."
|
||||||
|
|
||||||
|
# Turn 3: Ask to write a plan
|
||||||
|
echo ">>> Turn 3: Requesting plan..."
|
||||||
|
claude -p "Great, write this up as an implementation plan." \
|
||||||
|
--continue \
|
||||||
|
--model haiku \
|
||||||
|
--plugin-dir "$PLUGIN_DIR" \
|
||||||
|
--dangerously-skip-permissions \
|
||||||
|
--max-turns 3 \
|
||||||
|
--output-format stream-json \
|
||||||
|
> "$OUTPUT_DIR/turn3.json" 2>&1 || true
|
||||||
|
echo "Done."
|
||||||
|
|
||||||
|
# Turn 4: Confirm plan looks good
|
||||||
|
echo ">>> Turn 4: Confirming plan..."
|
||||||
|
claude -p "The plan looks good. What are my options for executing it?" \
|
||||||
|
--continue \
|
||||||
|
--model haiku \
|
||||||
|
--plugin-dir "$PLUGIN_DIR" \
|
||||||
|
--dangerously-skip-permissions \
|
||||||
|
--max-turns 2 \
|
||||||
|
--output-format stream-json \
|
||||||
|
> "$OUTPUT_DIR/turn4.json" 2>&1 || true
|
||||||
|
echo "Done."
|
||||||
|
|
||||||
|
# Turn 5: THE CRITICAL TEST
|
||||||
|
echo ">>> Turn 5: Requesting subagent-driven-development..."
|
||||||
|
FINAL_LOG="$OUTPUT_DIR/turn5.json"
|
||||||
|
claude -p "subagent-driven-development, please" \
|
||||||
|
--continue \
|
||||||
|
--model haiku \
|
||||||
|
--plugin-dir "$PLUGIN_DIR" \
|
||||||
|
--dangerously-skip-permissions \
|
||||||
|
--max-turns 2 \
|
||||||
|
--output-format stream-json \
|
||||||
|
> "$FINAL_LOG" 2>&1 || true
|
||||||
|
echo "Done."
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
echo "=== Results (Haiku) ==="
|
||||||
|
|
||||||
|
# Check final turn
|
||||||
|
SKILL_PATTERN='"skill":"([^"]*:)?subagent-driven-development"'
|
||||||
|
if grep -q '"name":"Skill"' "$FINAL_LOG" && grep -qE "$SKILL_PATTERN" "$FINAL_LOG"; then
|
||||||
|
echo "PASS: Skill was triggered"
|
||||||
|
TRIGGERED=true
|
||||||
|
else
|
||||||
|
echo "FAIL: Skill was NOT triggered"
|
||||||
|
TRIGGERED=false
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "Tools invoked in final turn:"
|
||||||
|
grep '"type":"tool_use"' "$FINAL_LOG" | grep -o '"name":"[^"]*"' | head -10 || echo " (none)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "Skills triggered:"
|
||||||
|
grep -o '"skill":"[^"]*"' "$FINAL_LOG" 2>/dev/null | sort -u || echo " (none)"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "Final turn response (first 500 chars):"
|
||||||
|
grep '"type":"assistant"' "$FINAL_LOG" | head -1 | jq -r '.message.content[0].text // .message.content' 2>/dev/null | head -c 500 || echo " (could not extract)"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "Logs in: $OUTPUT_DIR"
|
||||||
|
|
||||||
|
if [ "$TRIGGERED" = "true" ]; then
|
||||||
|
exit 0
|
||||||
|
else
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
143
tests/explicit-skill-requests/run-multiturn-test.sh
Executable file
143
tests/explicit-skill-requests/run-multiturn-test.sh
Executable file
@@ -0,0 +1,143 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Test explicit skill requests in multi-turn conversations
|
||||||
|
# Usage: ./run-multiturn-test.sh
|
||||||
|
#
|
||||||
|
# This test builds actual conversation history to reproduce the failure mode
|
||||||
|
# where Claude skips skill invocation after extended conversation
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
PLUGIN_DIR="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
||||||
|
|
||||||
|
TIMESTAMP=$(date +%s)
|
||||||
|
OUTPUT_DIR="/tmp/superpowers-tests/${TIMESTAMP}/explicit-skill-requests/multiturn"
|
||||||
|
mkdir -p "$OUTPUT_DIR"
|
||||||
|
|
||||||
|
# Create project directory (conversation is cwd-based)
|
||||||
|
PROJECT_DIR="$OUTPUT_DIR/project"
|
||||||
|
mkdir -p "$PROJECT_DIR/docs/plans"
|
||||||
|
|
||||||
|
echo "=== Multi-Turn Explicit Skill Request Test ==="
|
||||||
|
echo "Output dir: $OUTPUT_DIR"
|
||||||
|
echo "Project dir: $PROJECT_DIR"
|
||||||
|
echo "Plugin dir: $PLUGIN_DIR"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
cd "$PROJECT_DIR"
|
||||||
|
|
||||||
|
# Create a dummy plan file
|
||||||
|
cat > "$PROJECT_DIR/docs/plans/auth-system.md" << 'EOF'
|
||||||
|
# Auth System Implementation Plan
|
||||||
|
|
||||||
|
## Task 1: Add User Model
|
||||||
|
Create user model with email and password fields.
|
||||||
|
|
||||||
|
## Task 2: Add Auth Routes
|
||||||
|
Create login and register endpoints.
|
||||||
|
|
||||||
|
## Task 3: Add JWT Middleware
|
||||||
|
Protect routes with JWT validation.
|
||||||
|
|
||||||
|
## Task 4: Write Tests
|
||||||
|
Add comprehensive test coverage.
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# Turn 1: Start a planning conversation
|
||||||
|
echo ">>> Turn 1: Starting planning conversation..."
|
||||||
|
TURN1_LOG="$OUTPUT_DIR/turn1.json"
|
||||||
|
claude -p "I need to implement an authentication system. Let's plan this out. The requirements are: user registration with email/password, JWT tokens, and protected routes." \
|
||||||
|
--plugin-dir "$PLUGIN_DIR" \
|
||||||
|
--dangerously-skip-permissions \
|
||||||
|
--max-turns 2 \
|
||||||
|
--output-format stream-json \
|
||||||
|
> "$TURN1_LOG" 2>&1 || true
|
||||||
|
|
||||||
|
echo "Turn 1 complete."
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Turn 2: Continue with more planning detail
|
||||||
|
echo ">>> Turn 2: Continuing planning..."
|
||||||
|
TURN2_LOG="$OUTPUT_DIR/turn2.json"
|
||||||
|
claude -p "Good analysis. I've already written the plan to docs/plans/auth-system.md. Now I'm ready to implement. What are my options for execution?" \
|
||||||
|
--continue \
|
||||||
|
--plugin-dir "$PLUGIN_DIR" \
|
||||||
|
--dangerously-skip-permissions \
|
||||||
|
--max-turns 2 \
|
||||||
|
--output-format stream-json \
|
||||||
|
> "$TURN2_LOG" 2>&1 || true
|
||||||
|
|
||||||
|
echo "Turn 2 complete."
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Turn 3: The critical test - ask for subagent-driven-development
|
||||||
|
echo ">>> Turn 3: Requesting subagent-driven-development..."
|
||||||
|
TURN3_LOG="$OUTPUT_DIR/turn3.json"
|
||||||
|
claude -p "subagent-driven-development, please" \
|
||||||
|
--continue \
|
||||||
|
--plugin-dir "$PLUGIN_DIR" \
|
||||||
|
--dangerously-skip-permissions \
|
||||||
|
--max-turns 2 \
|
||||||
|
--output-format stream-json \
|
||||||
|
> "$TURN3_LOG" 2>&1 || true
|
||||||
|
|
||||||
|
echo "Turn 3 complete."
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
echo "=== Results ==="
|
||||||
|
|
||||||
|
# Check if skill was triggered in Turn 3
|
||||||
|
SKILL_PATTERN='"skill":"([^"]*:)?subagent-driven-development"'
|
||||||
|
if grep -q '"name":"Skill"' "$TURN3_LOG" && grep -qE "$SKILL_PATTERN" "$TURN3_LOG"; then
|
||||||
|
echo "PASS: Skill 'subagent-driven-development' was triggered in Turn 3"
|
||||||
|
TRIGGERED=true
|
||||||
|
else
|
||||||
|
echo "FAIL: Skill 'subagent-driven-development' was NOT triggered in Turn 3"
|
||||||
|
TRIGGERED=false
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Show what skills were triggered
|
||||||
|
echo ""
|
||||||
|
echo "Skills triggered in Turn 3:"
|
||||||
|
grep -o '"skill":"[^"]*"' "$TURN3_LOG" 2>/dev/null | sort -u || echo " (none)"
|
||||||
|
|
||||||
|
# Check for premature action in Turn 3
|
||||||
|
echo ""
|
||||||
|
echo "Checking for premature action in Turn 3..."
|
||||||
|
FIRST_SKILL_LINE=$(grep -n '"name":"Skill"' "$TURN3_LOG" | head -1 | cut -d: -f1)
|
||||||
|
if [ -n "$FIRST_SKILL_LINE" ]; then
|
||||||
|
PREMATURE_TOOLS=$(head -n "$FIRST_SKILL_LINE" "$TURN3_LOG" | \
|
||||||
|
grep '"type":"tool_use"' | \
|
||||||
|
grep -v '"name":"Skill"' | \
|
||||||
|
grep -v '"name":"TodoWrite"' || true)
|
||||||
|
if [ -n "$PREMATURE_TOOLS" ]; then
|
||||||
|
echo "WARNING: Tools invoked BEFORE Skill tool in Turn 3:"
|
||||||
|
echo "$PREMATURE_TOOLS" | head -5
|
||||||
|
else
|
||||||
|
echo "OK: No premature tool invocations detected"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "WARNING: No Skill invocation found in Turn 3"
|
||||||
|
# Show what WAS invoked
|
||||||
|
echo ""
|
||||||
|
echo "Tools invoked in Turn 3:"
|
||||||
|
grep '"type":"tool_use"' "$TURN3_LOG" | grep -o '"name":"[^"]*"' | head -10 || echo " (none)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Show Turn 3 assistant response
|
||||||
|
echo ""
|
||||||
|
echo "Turn 3 first assistant response (truncated):"
|
||||||
|
grep '"type":"assistant"' "$TURN3_LOG" | head -1 | jq -r '.message.content[0].text // .message.content' 2>/dev/null | head -c 500 || echo " (could not extract)"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "Logs:"
|
||||||
|
echo " Turn 1: $TURN1_LOG"
|
||||||
|
echo " Turn 2: $TURN2_LOG"
|
||||||
|
echo " Turn 3: $TURN3_LOG"
|
||||||
|
echo "Timestamp: $TIMESTAMP"
|
||||||
|
|
||||||
|
if [ "$TRIGGERED" = "true" ]; then
|
||||||
|
exit 0
|
||||||
|
else
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
136
tests/explicit-skill-requests/run-test.sh
Executable file
136
tests/explicit-skill-requests/run-test.sh
Executable file
@@ -0,0 +1,136 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Test explicit skill requests (user names a skill directly)
|
||||||
|
# Usage: ./run-test.sh <skill-name> <prompt-file>
|
||||||
|
#
|
||||||
|
# Tests whether Claude invokes a skill when the user explicitly requests it by name
|
||||||
|
# (without using the plugin namespace prefix)
|
||||||
|
#
|
||||||
|
# Uses isolated HOME to avoid user context interference
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
SKILL_NAME="$1"
|
||||||
|
PROMPT_FILE="$2"
|
||||||
|
MAX_TURNS="${3:-3}"
|
||||||
|
|
||||||
|
if [ -z "$SKILL_NAME" ] || [ -z "$PROMPT_FILE" ]; then
|
||||||
|
echo "Usage: $0 <skill-name> <prompt-file> [max-turns]"
|
||||||
|
echo "Example: $0 subagent-driven-development ./prompts/subagent-driven-development-please.txt"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Get the directory where this script lives
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
# Get the superpowers plugin root (two levels up)
|
||||||
|
PLUGIN_DIR="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
||||||
|
|
||||||
|
TIMESTAMP=$(date +%s)
|
||||||
|
OUTPUT_DIR="/tmp/superpowers-tests/${TIMESTAMP}/explicit-skill-requests/${SKILL_NAME}"
|
||||||
|
mkdir -p "$OUTPUT_DIR"
|
||||||
|
|
||||||
|
# Read prompt from file
|
||||||
|
PROMPT=$(cat "$PROMPT_FILE")
|
||||||
|
|
||||||
|
echo "=== Explicit Skill Request Test ==="
|
||||||
|
echo "Skill: $SKILL_NAME"
|
||||||
|
echo "Prompt file: $PROMPT_FILE"
|
||||||
|
echo "Max turns: $MAX_TURNS"
|
||||||
|
echo "Output dir: $OUTPUT_DIR"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Copy prompt for reference
|
||||||
|
cp "$PROMPT_FILE" "$OUTPUT_DIR/prompt.txt"
|
||||||
|
|
||||||
|
# Create a minimal project directory for the test
|
||||||
|
PROJECT_DIR="$OUTPUT_DIR/project"
|
||||||
|
mkdir -p "$PROJECT_DIR/docs/plans"
|
||||||
|
|
||||||
|
# Create a dummy plan file for mid-conversation tests
|
||||||
|
cat > "$PROJECT_DIR/docs/plans/auth-system.md" << 'EOF'
|
||||||
|
# Auth System Implementation Plan
|
||||||
|
|
||||||
|
## Task 1: Add User Model
|
||||||
|
Create user model with email and password fields.
|
||||||
|
|
||||||
|
## Task 2: Add Auth Routes
|
||||||
|
Create login and register endpoints.
|
||||||
|
|
||||||
|
## Task 3: Add JWT Middleware
|
||||||
|
Protect routes with JWT validation.
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# Run Claude with isolated environment
|
||||||
|
LOG_FILE="$OUTPUT_DIR/claude-output.json"
|
||||||
|
cd "$PROJECT_DIR"
|
||||||
|
|
||||||
|
echo "Plugin dir: $PLUGIN_DIR"
|
||||||
|
echo "Running claude -p with explicit skill request..."
|
||||||
|
echo "Prompt: $PROMPT"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
timeout 300 claude -p "$PROMPT" \
|
||||||
|
--plugin-dir "$PLUGIN_DIR" \
|
||||||
|
--dangerously-skip-permissions \
|
||||||
|
--max-turns "$MAX_TURNS" \
|
||||||
|
--output-format stream-json \
|
||||||
|
> "$LOG_FILE" 2>&1 || true
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "=== Results ==="
|
||||||
|
|
||||||
|
# Check if skill was triggered (look for Skill tool invocation)
|
||||||
|
# Match either "skill":"skillname" or "skill":"namespace:skillname"
|
||||||
|
SKILL_PATTERN='"skill":"([^"]*:)?'"${SKILL_NAME}"'"'
|
||||||
|
if grep -q '"name":"Skill"' "$LOG_FILE" && grep -qE "$SKILL_PATTERN" "$LOG_FILE"; then
|
||||||
|
echo "PASS: Skill '$SKILL_NAME' was triggered"
|
||||||
|
TRIGGERED=true
|
||||||
|
else
|
||||||
|
echo "FAIL: Skill '$SKILL_NAME' was NOT triggered"
|
||||||
|
TRIGGERED=false
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Show what skills WERE triggered
|
||||||
|
echo ""
|
||||||
|
echo "Skills triggered in this run:"
|
||||||
|
grep -o '"skill":"[^"]*"' "$LOG_FILE" 2>/dev/null | sort -u || echo " (none)"
|
||||||
|
|
||||||
|
# Check if Claude took action BEFORE invoking the skill (the failure mode)
|
||||||
|
echo ""
|
||||||
|
echo "Checking for premature action..."
|
||||||
|
|
||||||
|
# Look for tool invocations before the Skill invocation
|
||||||
|
# This detects the failure mode where Claude starts doing work without loading the skill
|
||||||
|
FIRST_SKILL_LINE=$(grep -n '"name":"Skill"' "$LOG_FILE" | head -1 | cut -d: -f1)
|
||||||
|
if [ -n "$FIRST_SKILL_LINE" ]; then
|
||||||
|
# Check if any non-Skill, non-system tools were invoked before the first Skill invocation
|
||||||
|
# Filter out system messages, TodoWrite (planning is ok), and other non-action tools
|
||||||
|
PREMATURE_TOOLS=$(head -n "$FIRST_SKILL_LINE" "$LOG_FILE" | \
|
||||||
|
grep '"type":"tool_use"' | \
|
||||||
|
grep -v '"name":"Skill"' | \
|
||||||
|
grep -v '"name":"TodoWrite"' || true)
|
||||||
|
if [ -n "$PREMATURE_TOOLS" ]; then
|
||||||
|
echo "WARNING: Tools invoked BEFORE Skill tool:"
|
||||||
|
echo "$PREMATURE_TOOLS" | head -5
|
||||||
|
echo ""
|
||||||
|
echo "This indicates Claude started working before loading the requested skill."
|
||||||
|
else
|
||||||
|
echo "OK: No premature tool invocations detected"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "WARNING: No Skill invocation found at all"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Show first assistant message
|
||||||
|
echo ""
|
||||||
|
echo "First assistant response (truncated):"
|
||||||
|
grep '"type":"assistant"' "$LOG_FILE" | head -1 | jq -r '.message.content[0].text // .message.content' 2>/dev/null | head -c 500 || echo " (could not extract)"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "Full log: $LOG_FILE"
|
||||||
|
echo "Timestamp: $TIMESTAMP"
|
||||||
|
|
||||||
|
if [ "$TRIGGERED" = "true" ]; then
|
||||||
|
exit 0
|
||||||
|
else
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
@@ -18,13 +18,13 @@ cp -r "$REPO_ROOT/lib" "$HOME/.config/opencode/superpowers/"
|
|||||||
cp -r "$REPO_ROOT/skills" "$HOME/.config/opencode/superpowers/"
|
cp -r "$REPO_ROOT/skills" "$HOME/.config/opencode/superpowers/"
|
||||||
|
|
||||||
# Copy plugin directory
|
# Copy plugin directory
|
||||||
mkdir -p "$HOME/.config/opencode/superpowers/.opencode/plugin"
|
mkdir -p "$HOME/.config/opencode/superpowers/.opencode/plugins"
|
||||||
cp "$REPO_ROOT/.opencode/plugin/superpowers.js" "$HOME/.config/opencode/superpowers/.opencode/plugin/"
|
cp "$REPO_ROOT/.opencode/plugins/superpowers.js" "$HOME/.config/opencode/superpowers/.opencode/plugins/"
|
||||||
|
|
||||||
# Register plugin via symlink
|
# Register plugin via symlink
|
||||||
mkdir -p "$HOME/.config/opencode/plugin"
|
mkdir -p "$HOME/.config/opencode/plugins"
|
||||||
ln -sf "$HOME/.config/opencode/superpowers/.opencode/plugin/superpowers.js" \
|
ln -sf "$HOME/.config/opencode/superpowers/.opencode/plugins/superpowers.js" \
|
||||||
"$HOME/.config/opencode/plugin/superpowers.js"
|
"$HOME/.config/opencode/plugins/superpowers.js"
|
||||||
|
|
||||||
# Create test skills in different locations for testing
|
# Create test skills in different locations for testing
|
||||||
|
|
||||||
@@ -57,8 +57,8 @@ PROJECT_SKILL_MARKER_67890
|
|||||||
EOF
|
EOF
|
||||||
|
|
||||||
echo "Setup complete: $TEST_HOME"
|
echo "Setup complete: $TEST_HOME"
|
||||||
echo "Plugin installed to: $HOME/.config/opencode/superpowers/.opencode/plugin/superpowers.js"
|
echo "Plugin installed to: $HOME/.config/opencode/superpowers/.opencode/plugins/superpowers.js"
|
||||||
echo "Plugin registered at: $HOME/.config/opencode/plugin/superpowers.js"
|
echo "Plugin registered at: $HOME/.config/opencode/plugins/superpowers.js"
|
||||||
echo "Test project at: $TEST_HOME/test-project"
|
echo "Test project at: $TEST_HOME/test-project"
|
||||||
|
|
||||||
# Helper function for cleanup (call from tests or trap)
|
# Helper function for cleanup (call from tests or trap)
|
||||||
|
|||||||
@@ -15,15 +15,15 @@ trap cleanup_test_env EXIT
|
|||||||
|
|
||||||
# Test 1: Verify plugin file exists and is registered
|
# Test 1: Verify plugin file exists and is registered
|
||||||
echo "Test 1: Checking plugin registration..."
|
echo "Test 1: Checking plugin registration..."
|
||||||
if [ -L "$HOME/.config/opencode/plugin/superpowers.js" ]; then
|
if [ -L "$HOME/.config/opencode/plugins/superpowers.js" ]; then
|
||||||
echo " [PASS] Plugin symlink exists"
|
echo " [PASS] Plugin symlink exists"
|
||||||
else
|
else
|
||||||
echo " [FAIL] Plugin symlink not found at $HOME/.config/opencode/plugin/superpowers.js"
|
echo " [FAIL] Plugin symlink not found at $HOME/.config/opencode/plugins/superpowers.js"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Verify symlink target exists
|
# Verify symlink target exists
|
||||||
if [ -f "$(readlink -f "$HOME/.config/opencode/plugin/superpowers.js")" ]; then
|
if [ -f "$(readlink -f "$HOME/.config/opencode/plugins/superpowers.js")" ]; then
|
||||||
echo " [PASS] Plugin symlink target exists"
|
echo " [PASS] Plugin symlink target exists"
|
||||||
else
|
else
|
||||||
echo " [FAIL] Plugin symlink target does not exist"
|
echo " [FAIL] Plugin symlink target does not exist"
|
||||||
@@ -60,7 +60,7 @@ fi
|
|||||||
|
|
||||||
# Test 5: Verify plugin JavaScript syntax (basic check)
|
# Test 5: Verify plugin JavaScript syntax (basic check)
|
||||||
echo "Test 5: Checking plugin JavaScript syntax..."
|
echo "Test 5: Checking plugin JavaScript syntax..."
|
||||||
plugin_file="$HOME/.config/opencode/superpowers/.opencode/plugin/superpowers.js"
|
plugin_file="$HOME/.config/opencode/superpowers/.opencode/plugins/superpowers.js"
|
||||||
if node --check "$plugin_file" 2>/dev/null; then
|
if node --check "$plugin_file" 2>/dev/null; then
|
||||||
echo " [PASS] Plugin JavaScript syntax is valid"
|
echo " [PASS] Plugin JavaScript syntax is valid"
|
||||||
else
|
else
|
||||||
|
|||||||
Reference in New Issue
Block a user