slashdev 0.1.0 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.gitmodules +3 -0
- package/CLAUDE.md +87 -0
- package/README.md +158 -21
- package/bin/check-setup.js +27 -0
- package/claude-skills/agentswarm/SKILL.md +479 -0
- package/claude-skills/bug-diagnosis/SKILL.md +34 -0
- package/claude-skills/code-review/SKILL.md +26 -0
- package/claude-skills/frontend-design/LICENSE.txt +177 -0
- package/claude-skills/frontend-design/SKILL.md +42 -0
- package/claude-skills/pr-description/SKILL.md +35 -0
- package/claude-skills/scope-estimate/SKILL.md +37 -0
- package/hooks/post-response.sh +242 -0
- package/package.json +11 -3
- package/skills/front-end-design/prompts/system.md +37 -0
- package/skills/front-end-testing/prompts/system.md +66 -0
- package/skills/github-manager/prompts/system.md +79 -0
- package/skills/product-expert/prompts/system.md +52 -0
- package/skills/server-admin/prompts/system.md +39 -0
- package/src/auth/index.js +115 -0
- package/src/cli.js +188 -18
- package/src/commands/setup-internals.js +137 -0
- package/src/commands/setup.js +104 -0
- package/src/commands/update.js +60 -0
- package/src/connections/index.js +449 -0
- package/src/connections/providers/github.js +71 -0
- package/src/connections/providers/servers.js +175 -0
- package/src/connections/registry.js +21 -0
- package/src/core/claude.js +78 -0
- package/src/core/codebase.js +119 -0
- package/src/core/config.js +110 -0
- package/src/index.js +8 -1
- package/src/info.js +54 -21
- package/src/skills/index.js +252 -0
- package/src/utils/ssh-keys.js +67 -0
- package/vendor/gstack/.env.example +5 -0
- package/vendor/gstack/autoplan/SKILL.md +1116 -0
- package/vendor/gstack/browse/SKILL.md +538 -0
- package/vendor/gstack/canary/SKILL.md +587 -0
- package/vendor/gstack/careful/SKILL.md +59 -0
- package/vendor/gstack/codex/SKILL.md +862 -0
- package/vendor/gstack/connect-chrome/SKILL.md +549 -0
- package/vendor/gstack/cso/ACKNOWLEDGEMENTS.md +14 -0
- package/vendor/gstack/cso/SKILL.md +929 -0
- package/vendor/gstack/design-consultation/SKILL.md +962 -0
- package/vendor/gstack/design-review/SKILL.md +1314 -0
- package/vendor/gstack/design-shotgun/SKILL.md +730 -0
- package/vendor/gstack/document-release/SKILL.md +718 -0
- package/vendor/gstack/freeze/SKILL.md +82 -0
- package/vendor/gstack/gstack-upgrade/SKILL.md +232 -0
- package/vendor/gstack/guard/SKILL.md +82 -0
- package/vendor/gstack/investigate/SKILL.md +504 -0
- package/vendor/gstack/land-and-deploy/SKILL.md +1367 -0
- package/vendor/gstack/office-hours/SKILL.md +1317 -0
- package/vendor/gstack/plan-ceo-review/SKILL.md +1537 -0
- package/vendor/gstack/plan-design-review/SKILL.md +1227 -0
- package/vendor/gstack/plan-eng-review/SKILL.md +1120 -0
- package/vendor/gstack/qa/SKILL.md +1136 -0
- package/vendor/gstack/qa/references/issue-taxonomy.md +85 -0
- package/vendor/gstack/qa/templates/qa-report-template.md +126 -0
- package/vendor/gstack/qa-only/SKILL.md +726 -0
- package/vendor/gstack/retro/SKILL.md +1197 -0
- package/vendor/gstack/review/SKILL.md +1138 -0
- package/vendor/gstack/review/TODOS-format.md +62 -0
- package/vendor/gstack/review/checklist.md +220 -0
- package/vendor/gstack/review/design-checklist.md +132 -0
- package/vendor/gstack/review/greptile-triage.md +220 -0
- package/vendor/gstack/setup-browser-cookies/SKILL.md +348 -0
- package/vendor/gstack/setup-deploy/SKILL.md +528 -0
- package/vendor/gstack/ship/SKILL.md +1931 -0
- package/vendor/gstack/unfreeze/SKILL.md +40 -0
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# GitHub Manager
|
|
2
|
+
|
|
3
|
+
You are a GitHub workflow expert from the Slashdev team. You help engineers work effectively with Git and GitHub.
|
|
4
|
+
|
|
5
|
+
## Your Expertise
|
|
6
|
+
|
|
7
|
+
- **Pull Requests**: Creating clear, reviewable PRs
|
|
8
|
+
- **Commit Messages**: Writing meaningful commit history
|
|
9
|
+
- **Issues**: Structuring bug reports and feature requests
|
|
10
|
+
- **Releases**: Changelogs, release notes, versioning
|
|
11
|
+
- **Branch Strategy**: Git flow, trunk-based development
|
|
12
|
+
- **Code Review**: Providing constructive feedback
|
|
13
|
+
|
|
14
|
+
## PR Description Template
|
|
15
|
+
|
|
16
|
+
```markdown
|
|
17
|
+
## Summary
|
|
18
|
+
Brief description of what this PR does.
|
|
19
|
+
|
|
20
|
+
## Changes
|
|
21
|
+
- Change 1
|
|
22
|
+
- Change 2
|
|
23
|
+
- Change 3
|
|
24
|
+
|
|
25
|
+
## Testing
|
|
26
|
+
How to test these changes.
|
|
27
|
+
|
|
28
|
+
## Screenshots (if applicable)
|
|
29
|
+
Before/after or relevant UI changes.
|
|
30
|
+
|
|
31
|
+
## Checklist
|
|
32
|
+
- [ ] Tests pass
|
|
33
|
+
- [ ] Code follows style guidelines
|
|
34
|
+
- [ ] Documentation updated (if needed)
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Commit Message Format
|
|
38
|
+
|
|
39
|
+
```
|
|
40
|
+
<type>(<scope>): <subject>
|
|
41
|
+
|
|
42
|
+
<body>
|
|
43
|
+
|
|
44
|
+
<footer>
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Types: feat, fix, docs, style, refactor, test, chore
|
|
48
|
+
|
|
49
|
+
## Release Notes Style
|
|
50
|
+
|
|
51
|
+
```markdown
|
|
52
|
+
## [1.2.0] - 2024-01-15
|
|
53
|
+
|
|
54
|
+
### Added
|
|
55
|
+
- New feature X
|
|
56
|
+
|
|
57
|
+
### Changed
|
|
58
|
+
- Improved Y
|
|
59
|
+
|
|
60
|
+
### Fixed
|
|
61
|
+
- Bug in Z
|
|
62
|
+
|
|
63
|
+
### Security
|
|
64
|
+
- Updated vulnerable dependency
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Your Approach
|
|
68
|
+
|
|
69
|
+
1. **Context Matters**: Ask about the changes to provide relevant suggestions
|
|
70
|
+
2. **Reviewable PRs**: Keep changes focused and well-organized
|
|
71
|
+
3. **Clear History**: Commits tell a story
|
|
72
|
+
4. **Conventional Commits**: Follow established conventions
|
|
73
|
+
|
|
74
|
+
## Response Style
|
|
75
|
+
|
|
76
|
+
- Provide ready-to-use templates
|
|
77
|
+
- Explain GitHub best practices
|
|
78
|
+
- Consider team workflow implications
|
|
79
|
+
- Be practical and actionable
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# Product Expert
|
|
2
|
+
|
|
3
|
+
You are a senior product manager from the Slashdev team. You help engineers think through product decisions and create clear documentation.
|
|
4
|
+
|
|
5
|
+
## Your Expertise
|
|
6
|
+
|
|
7
|
+
- **PRDs**: Writing clear, comprehensive Product Requirements Documents
|
|
8
|
+
- **User Stories**: Creating actionable stories with acceptance criteria
|
|
9
|
+
- **Specifications**: Detailed technical and functional specifications
|
|
10
|
+
- **Roadmaps**: Strategic planning and prioritization
|
|
11
|
+
- **Communication**: Stakeholder updates, release notes, announcements
|
|
12
|
+
|
|
13
|
+
## PRD Structure
|
|
14
|
+
|
|
15
|
+
When writing PRDs, include:
|
|
16
|
+
|
|
17
|
+
1. **Overview**: What problem are we solving?
|
|
18
|
+
2. **Goals & Success Metrics**: How will we measure success?
|
|
19
|
+
3. **User Stories**: Who benefits and how?
|
|
20
|
+
4. **Requirements**: Must-have vs nice-to-have
|
|
21
|
+
5. **Technical Considerations**: Constraints and dependencies
|
|
22
|
+
6. **Timeline**: Milestones and phases
|
|
23
|
+
7. **Open Questions**: What still needs to be decided?
|
|
24
|
+
|
|
25
|
+
## User Story Format
|
|
26
|
+
|
|
27
|
+
```
|
|
28
|
+
As a [type of user],
|
|
29
|
+
I want to [action],
|
|
30
|
+
So that [benefit].
|
|
31
|
+
|
|
32
|
+
Acceptance Criteria:
|
|
33
|
+
- [ ] Criterion 1
|
|
34
|
+
- [ ] Criterion 2
|
|
35
|
+
- [ ] Criterion 3
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Your Approach
|
|
39
|
+
|
|
40
|
+
1. **Ask Questions**: Clarify requirements before writing
|
|
41
|
+
2. **Be Specific**: Avoid vague language
|
|
42
|
+
3. **Think Edge Cases**: Consider what could go wrong
|
|
43
|
+
4. **Prioritize**: Help distinguish must-haves from nice-to-haves
|
|
44
|
+
5. **Stay User-Focused**: Always tie back to user value
|
|
45
|
+
|
|
46
|
+
## Response Style
|
|
47
|
+
|
|
48
|
+
- Use clear, professional language
|
|
49
|
+
- Structure documents with headers and lists
|
|
50
|
+
- Include examples where helpful
|
|
51
|
+
- Be thorough but not verbose
|
|
52
|
+
- Highlight open questions and assumptions
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# Server Admin
|
|
2
|
+
|
|
3
|
+
You are an expert Linux server administrator from the Slashdev team. You help engineers manage, troubleshoot, and secure their servers.
|
|
4
|
+
|
|
5
|
+
## Your Expertise
|
|
6
|
+
|
|
7
|
+
- **System Administration**: Package management, service management, cron jobs, log analysis
|
|
8
|
+
- **Security**: Firewall configuration (ufw, iptables), SSH hardening, fail2ban, user permissions
|
|
9
|
+
- **Web Servers**: Nginx, Apache, Caddy configuration and optimization
|
|
10
|
+
- **Databases**: MySQL/MariaDB, PostgreSQL, MongoDB, Redis administration
|
|
11
|
+
- **Containers**: Docker, Docker Compose, container orchestration
|
|
12
|
+
- **Monitoring**: System metrics, disk usage, process management, log rotation
|
|
13
|
+
- **Networking**: DNS, SSL/TLS certificates (Let's Encrypt), port management, VPN
|
|
14
|
+
- **Deployment**: Git-based deployment, CI/CD pipelines, zero-downtime deploys
|
|
15
|
+
- **Performance**: Server tuning, caching strategies, load balancing
|
|
16
|
+
|
|
17
|
+
## Your Approach
|
|
18
|
+
|
|
19
|
+
1. **Safety First**: Always warn about destructive commands. Suggest dry-runs when available
|
|
20
|
+
2. **Explain Commands**: Provide context for what each command does and why
|
|
21
|
+
3. **Server Context**: Reference the connected servers by name when relevant
|
|
22
|
+
4. **Best Practices**: Follow security hardening and performance best practices
|
|
23
|
+
5. **Step-by-Step**: Break complex procedures into clear, numbered steps
|
|
24
|
+
|
|
25
|
+
## Command Format
|
|
26
|
+
|
|
27
|
+
When providing server commands, always:
|
|
28
|
+
- Include the full command with flags explained
|
|
29
|
+
- Show expected output when helpful
|
|
30
|
+
- Prefix destructive commands with warnings
|
|
31
|
+
- Suggest backing up before making changes
|
|
32
|
+
|
|
33
|
+
## Response Style
|
|
34
|
+
|
|
35
|
+
- Provide ready-to-use commands
|
|
36
|
+
- Explain the "why" behind each step
|
|
37
|
+
- Consider the connected server context
|
|
38
|
+
- Warn about common pitfalls
|
|
39
|
+
- Suggest verification steps after changes
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import inquirer from 'inquirer';
|
|
2
|
+
import ora from 'ora';
|
|
3
|
+
import chalk from 'chalk';
|
|
4
|
+
import { setApiKey, setUser, getApiKey, getUser, clearApiKey, isAuthenticated } from '../core/config.js';
|
|
5
|
+
import { validateApiKey } from '../core/claude.js';
|
|
6
|
+
import { blue } from '../banner.js';
|
|
7
|
+
|
|
8
|
+
export async function login() {
|
|
9
|
+
console.log();
|
|
10
|
+
|
|
11
|
+
if (isAuthenticated()) {
|
|
12
|
+
const user = getUser();
|
|
13
|
+
console.log(chalk.hex('#4d7fff')(` You're already logged in${user ? ` as ${chalk.bold(user)}` : ''}.`));
|
|
14
|
+
console.log(chalk.dim(` Run ${chalk.hex('#215ff6')('slashdev logout')} to sign out first.`));
|
|
15
|
+
console.log();
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
console.log(chalk.hex('#215ff6').bold(' Slashdev Login'));
|
|
20
|
+
console.log(chalk.hex('#4d7fff')(' ─────────────────────────────────────────'));
|
|
21
|
+
console.log();
|
|
22
|
+
console.log(chalk.dim(' To use Slashdev skills, you need an Anthropic API key.'));
|
|
23
|
+
console.log(chalk.dim(' Get one at: ') + chalk.hex('#215ff6').underline('https://console.anthropic.com/settings/keys'));
|
|
24
|
+
console.log();
|
|
25
|
+
|
|
26
|
+
const answers = await inquirer.prompt([
|
|
27
|
+
{
|
|
28
|
+
type: 'password',
|
|
29
|
+
name: 'apiKey',
|
|
30
|
+
message: chalk.hex('#7a9fff')('Enter your Anthropic API key:'),
|
|
31
|
+
mask: '*',
|
|
32
|
+
validate: (input) => {
|
|
33
|
+
if (!input || input.trim().length === 0) {
|
|
34
|
+
return 'API key is required';
|
|
35
|
+
}
|
|
36
|
+
if (!input.startsWith('sk-ant-')) {
|
|
37
|
+
return 'Invalid API key format. Should start with sk-ant-';
|
|
38
|
+
}
|
|
39
|
+
return true;
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
type: 'input',
|
|
44
|
+
name: 'name',
|
|
45
|
+
message: chalk.hex('#7a9fff')('What should we call you? (optional):'),
|
|
46
|
+
default: '',
|
|
47
|
+
},
|
|
48
|
+
]);
|
|
49
|
+
|
|
50
|
+
const spinner = ora({
|
|
51
|
+
text: chalk.hex('#4d7fff')('Validating API key...'),
|
|
52
|
+
color: 'blue',
|
|
53
|
+
}).start();
|
|
54
|
+
|
|
55
|
+
const isValid = await validateApiKey(answers.apiKey.trim());
|
|
56
|
+
|
|
57
|
+
if (!isValid) {
|
|
58
|
+
spinner.fail(chalk.red('Invalid API key. Please check and try again.'));
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
setApiKey(answers.apiKey.trim());
|
|
63
|
+
if (answers.name) {
|
|
64
|
+
setUser(answers.name.trim());
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
spinner.succeed(chalk.hex('#4d7fff')('API key validated and saved!'));
|
|
68
|
+
console.log();
|
|
69
|
+
console.log(chalk.hex('#215ff6').bold(` Welcome to Slashdev${answers.name ? `, ${answers.name}` : ''}!`));
|
|
70
|
+
console.log();
|
|
71
|
+
console.log(chalk.dim(' Try running a skill:'));
|
|
72
|
+
console.log(` ${chalk.hex('#4d7fff')('slashdev design')} ${chalk.dim('"review my Button component"')}`);
|
|
73
|
+
console.log(` ${chalk.hex('#4d7fff')('slashdev product')} ${chalk.dim('"write a PRD for user auth"')}`);
|
|
74
|
+
console.log();
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export async function logout() {
|
|
78
|
+
console.log();
|
|
79
|
+
|
|
80
|
+
if (!isAuthenticated()) {
|
|
81
|
+
console.log(chalk.hex('#7a9fff')(' You\'re not logged in.'));
|
|
82
|
+
console.log();
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const user = getUser();
|
|
87
|
+
clearApiKey();
|
|
88
|
+
|
|
89
|
+
console.log(chalk.hex('#4d7fff')(` Logged out${user ? ` ${user}` : ''} successfully.`));
|
|
90
|
+
console.log(chalk.dim(` Run ${chalk.hex('#215ff6')('slashdev login')} to sign in again.`));
|
|
91
|
+
console.log();
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export function whoami() {
|
|
95
|
+
console.log();
|
|
96
|
+
|
|
97
|
+
if (!isAuthenticated()) {
|
|
98
|
+
console.log(chalk.hex('#7a9fff')(' Not logged in.'));
|
|
99
|
+
console.log(chalk.dim(` Run ${chalk.hex('#215ff6')('slashdev login')} to authenticate.`));
|
|
100
|
+
console.log();
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const user = getUser();
|
|
105
|
+
const apiKey = getApiKey();
|
|
106
|
+
const maskedKey = apiKey ? `${apiKey.slice(0, 10)}...${apiKey.slice(-4)}` : 'unknown';
|
|
107
|
+
|
|
108
|
+
console.log(chalk.hex('#215ff6').bold(' Slashdev Account'));
|
|
109
|
+
console.log(chalk.hex('#4d7fff')(' ─────────────────────────────────────────'));
|
|
110
|
+
console.log();
|
|
111
|
+
console.log(` ${chalk.hex('#7a9fff')('User:')} ${user || chalk.dim('(not set)')}`);
|
|
112
|
+
console.log(` ${chalk.hex('#7a9fff')('API Key:')} ${chalk.dim(maskedKey)}`);
|
|
113
|
+
console.log(` ${chalk.hex('#7a9fff')('Status:')} ${chalk.hex('#4d7fff')('● Authenticated')}`);
|
|
114
|
+
console.log();
|
|
115
|
+
}
|
package/src/cli.js
CHANGED
|
@@ -1,23 +1,193 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
1
2
|
import { displayBanner } from './banner.js';
|
|
2
3
|
import { displayInfo, displayVersion, displayHelp } from './info.js';
|
|
4
|
+
import { login, logout, whoami } from './auth/index.js';
|
|
5
|
+
import { listSkills, runSkill, getSkillByCommand } from './skills/index.js';
|
|
6
|
+
import { connect, disconnect, listConnections } from './connections/index.js';
|
|
7
|
+
import { setup } from './commands/setup.js';
|
|
8
|
+
import { update } from './commands/update.js';
|
|
9
|
+
import { readFileSync } from 'fs';
|
|
10
|
+
import { fileURLToPath } from 'url';
|
|
11
|
+
import { dirname, join } from 'path';
|
|
12
|
+
|
|
13
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
14
|
+
const __dirname = dirname(__filename);
|
|
15
|
+
const pkg = JSON.parse(readFileSync(join(__dirname, '..', 'package.json'), 'utf-8'));
|
|
16
|
+
|
|
17
|
+
const program = new Command();
|
|
18
|
+
|
|
19
|
+
program
|
|
20
|
+
.name('slashdev')
|
|
21
|
+
.description('Skills & workflows for AI agents in the Slashdev ecosystem')
|
|
22
|
+
.version(pkg.version, '-v, --version', 'Show version number');
|
|
23
|
+
|
|
24
|
+
// Claude Code integration commands
|
|
25
|
+
program
|
|
26
|
+
.command('setup')
|
|
27
|
+
.description('Install Claude Code hooks and skills for your Slashdev account')
|
|
28
|
+
.action(async () => {
|
|
29
|
+
displayBanner();
|
|
30
|
+
await setup();
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
program
|
|
34
|
+
.command('update')
|
|
35
|
+
.description('Update Claude Code hooks and skills to the latest version')
|
|
36
|
+
.action(async () => {
|
|
37
|
+
displayBanner();
|
|
38
|
+
await update();
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
// Default action (no command) - show banner
|
|
42
|
+
program
|
|
43
|
+
.action(async () => {
|
|
44
|
+
displayBanner();
|
|
45
|
+
displayInfo();
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
// Authentication commands
|
|
49
|
+
program
|
|
50
|
+
.command('login')
|
|
51
|
+
.description('Authenticate with your Anthropic API key')
|
|
52
|
+
.action(async () => {
|
|
53
|
+
displayBanner();
|
|
54
|
+
await login();
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
program
|
|
58
|
+
.command('logout')
|
|
59
|
+
.description('Clear stored credentials')
|
|
60
|
+
.action(async () => {
|
|
61
|
+
await logout();
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
program
|
|
65
|
+
.command('whoami')
|
|
66
|
+
.description('Show current authentication status')
|
|
67
|
+
.action(() => {
|
|
68
|
+
whoami();
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
// Connection commands
|
|
72
|
+
program
|
|
73
|
+
.command('connect [service]')
|
|
74
|
+
.description('Connect an external service (e.g. github)')
|
|
75
|
+
.action(async (service) => {
|
|
76
|
+
displayBanner();
|
|
77
|
+
await connect(service);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
program
|
|
81
|
+
.command('disconnect [service]')
|
|
82
|
+
.description('Remove a connected service')
|
|
83
|
+
.action(async (service) => {
|
|
84
|
+
await disconnect(service);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
program
|
|
88
|
+
.command('connections')
|
|
89
|
+
.description('List all connections and their status')
|
|
90
|
+
.action(() => {
|
|
91
|
+
displayBanner();
|
|
92
|
+
listConnections();
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
// Skills command
|
|
96
|
+
program
|
|
97
|
+
.command('skills')
|
|
98
|
+
.description('List installed skills')
|
|
99
|
+
.action(() => {
|
|
100
|
+
displayBanner();
|
|
101
|
+
listSkills();
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
// Design skill
|
|
105
|
+
program
|
|
106
|
+
.command('design [prompt...]')
|
|
107
|
+
.description('Front-end design assistance')
|
|
108
|
+
.action(async (promptParts) => {
|
|
109
|
+
const prompt = promptParts.join(' ');
|
|
110
|
+
if (!prompt) {
|
|
111
|
+
console.log('\n Usage: slashdev design "your design question"\n');
|
|
112
|
+
console.log(' Examples:');
|
|
113
|
+
console.log(' slashdev design "review my Button component for accessibility"');
|
|
114
|
+
console.log(' slashdev design "create a dark mode color palette"');
|
|
115
|
+
console.log(' slashdev design "improve the mobile layout"\n');
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
await runSkill('front-end-design', prompt);
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
// Product skill
|
|
122
|
+
program
|
|
123
|
+
.command('product [prompt...]')
|
|
124
|
+
.description('Product management assistance')
|
|
125
|
+
.action(async (promptParts) => {
|
|
126
|
+
const prompt = promptParts.join(' ');
|
|
127
|
+
if (!prompt) {
|
|
128
|
+
console.log('\n Usage: slashdev product "your product question"\n');
|
|
129
|
+
console.log(' Examples:');
|
|
130
|
+
console.log(' slashdev product "write a PRD for user authentication"');
|
|
131
|
+
console.log(' slashdev product "create user stories for checkout flow"');
|
|
132
|
+
console.log(' slashdev product "draft release notes for v2.0"\n');
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
await runSkill('product-expert', prompt);
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
// GitHub skill
|
|
139
|
+
program
|
|
140
|
+
.command('github [prompt...]')
|
|
141
|
+
.alias('gh')
|
|
142
|
+
.description('GitHub workflow assistance')
|
|
143
|
+
.action(async (promptParts) => {
|
|
144
|
+
const prompt = promptParts.join(' ');
|
|
145
|
+
if (!prompt) {
|
|
146
|
+
console.log('\n Usage: slashdev github "your github question"\n');
|
|
147
|
+
console.log(' Examples:');
|
|
148
|
+
console.log(' slashdev github "write a PR description for adding auth"');
|
|
149
|
+
console.log(' slashdev github "create release notes for v1.2.0"');
|
|
150
|
+
console.log(' slashdev github "structure an issue for a login bug"\n');
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
await runSkill('github-manager', prompt);
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
// Test skill
|
|
157
|
+
program
|
|
158
|
+
.command('test [prompt...]')
|
|
159
|
+
.description('Front-end testing assistance')
|
|
160
|
+
.action(async (promptParts) => {
|
|
161
|
+
const prompt = promptParts.join(' ');
|
|
162
|
+
if (!prompt) {
|
|
163
|
+
console.log('\n Usage: slashdev test "your testing question"\n');
|
|
164
|
+
console.log(' Examples:');
|
|
165
|
+
console.log(' slashdev test "write unit tests for the Button component"');
|
|
166
|
+
console.log(' slashdev test "create E2E tests for user login flow"');
|
|
167
|
+
console.log(' slashdev test "improve test coverage for the utils"\n');
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
await runSkill('front-end-testing', prompt);
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
// Server admin skill
|
|
174
|
+
program
|
|
175
|
+
.command('server [prompt...]')
|
|
176
|
+
.alias('ssh')
|
|
177
|
+
.description('Server administration assistance')
|
|
178
|
+
.action(async (promptParts) => {
|
|
179
|
+
const prompt = promptParts.join(' ');
|
|
180
|
+
if (!prompt) {
|
|
181
|
+
console.log('\n Usage: slashdev server "your server question"\n');
|
|
182
|
+
console.log(' Examples:');
|
|
183
|
+
console.log(' slashdev server "harden SSH config on prod-web"');
|
|
184
|
+
console.log(' slashdev server "set up nginx reverse proxy"');
|
|
185
|
+
console.log(' slashdev server "check disk usage and clean up"\n');
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
await runSkill('server-admin', prompt);
|
|
189
|
+
});
|
|
3
190
|
|
|
4
191
|
export async function run(args) {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
// Handle --version / -v
|
|
8
|
-
if (flags.has('--version') || flags.has('-v')) {
|
|
9
|
-
displayVersion();
|
|
10
|
-
return;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
// Handle --help / -h
|
|
14
|
-
if (flags.has('--help') || flags.has('-h')) {
|
|
15
|
-
await displayBanner();
|
|
16
|
-
displayHelp();
|
|
17
|
-
return;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
// Default: show banner + info
|
|
21
|
-
await displayBanner();
|
|
22
|
-
displayInfo();
|
|
192
|
+
await program.parseAsync(['node', 'slashdev', ...args]);
|
|
23
193
|
}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
// Shared install functions used by both setup and update commands
|
|
2
|
+
import { homedir } from 'os';
|
|
3
|
+
import { join, dirname } from 'path';
|
|
4
|
+
import { fileURLToPath } from 'url';
|
|
5
|
+
import {
|
|
6
|
+
existsSync,
|
|
7
|
+
mkdirSync,
|
|
8
|
+
readdirSync,
|
|
9
|
+
readFileSync,
|
|
10
|
+
writeFileSync,
|
|
11
|
+
copyFileSync,
|
|
12
|
+
chmodSync,
|
|
13
|
+
} from 'fs';
|
|
14
|
+
|
|
15
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
16
|
+
const __dirname = dirname(__filename);
|
|
17
|
+
const PACKAGE_ROOT = join(__dirname, '..', '..');
|
|
18
|
+
|
|
19
|
+
const CLAUDE_DIR = join(homedir(), '.claude');
|
|
20
|
+
const CLAUDE_HOOKS_DIR = join(CLAUDE_DIR, 'hooks');
|
|
21
|
+
const CLAUDE_SKILLS_DIR = join(CLAUDE_DIR, 'skills');
|
|
22
|
+
const CLAUDE_SETTINGS_FILE = join(CLAUDE_DIR, 'settings.json');
|
|
23
|
+
|
|
24
|
+
function ensureDir(dir) {
|
|
25
|
+
if (!existsSync(dir)) {
|
|
26
|
+
mkdirSync(dir, { recursive: true });
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function copyDirRecursive(src, dest) {
|
|
31
|
+
ensureDir(dest);
|
|
32
|
+
const entries = readdirSync(src, { withFileTypes: true });
|
|
33
|
+
for (const entry of entries) {
|
|
34
|
+
const srcPath = join(src, entry.name);
|
|
35
|
+
const destPath = join(dest, entry.name);
|
|
36
|
+
if (entry.isDirectory()) {
|
|
37
|
+
copyDirRecursive(srcPath, destPath);
|
|
38
|
+
} else {
|
|
39
|
+
copyFileSync(srcPath, destPath);
|
|
40
|
+
if (entry.name.endsWith('.sh')) {
|
|
41
|
+
chmodSync(destPath, 0o755);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function installHooks() {
|
|
48
|
+
const hooksSource = join(PACKAGE_ROOT, 'hooks');
|
|
49
|
+
if (!existsSync(hooksSource)) {
|
|
50
|
+
return { installed: 0 };
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
ensureDir(CLAUDE_HOOKS_DIR);
|
|
54
|
+
copyDirRecursive(hooksSource, CLAUDE_HOOKS_DIR);
|
|
55
|
+
|
|
56
|
+
// Register hooks in Claude Code settings.json
|
|
57
|
+
let settings = {};
|
|
58
|
+
if (existsSync(CLAUDE_SETTINGS_FILE)) {
|
|
59
|
+
try {
|
|
60
|
+
settings = JSON.parse(readFileSync(CLAUDE_SETTINGS_FILE, 'utf-8'));
|
|
61
|
+
} catch {
|
|
62
|
+
settings = {};
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (!settings.hooks) {
|
|
67
|
+
settings.hooks = {};
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const hookCommand = join(CLAUDE_HOOKS_DIR, 'post-response.sh');
|
|
71
|
+
const stopHooks = settings.hooks.Stop || [];
|
|
72
|
+
|
|
73
|
+
const alreadyRegistered = stopHooks.some(
|
|
74
|
+
(h) => h.hooks && h.hooks.some((inner) => inner.command && inner.command.includes('post-response.sh'))
|
|
75
|
+
);
|
|
76
|
+
|
|
77
|
+
if (!alreadyRegistered) {
|
|
78
|
+
stopHooks.push({
|
|
79
|
+
matcher: '',
|
|
80
|
+
hooks: [
|
|
81
|
+
{
|
|
82
|
+
type: 'command',
|
|
83
|
+
command: hookCommand,
|
|
84
|
+
},
|
|
85
|
+
],
|
|
86
|
+
});
|
|
87
|
+
settings.hooks.Stop = stopHooks;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
writeFileSync(CLAUDE_SETTINGS_FILE, JSON.stringify(settings, null, 2), 'utf-8');
|
|
91
|
+
|
|
92
|
+
const hookFiles = readdirSync(hooksSource).filter((f) => f.endsWith('.sh'));
|
|
93
|
+
return { installed: hookFiles.length };
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export function installSkills() {
|
|
97
|
+
const skillsSource = join(PACKAGE_ROOT, 'claude-skills');
|
|
98
|
+
if (!existsSync(skillsSource)) {
|
|
99
|
+
return { installed: 0 };
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
ensureDir(CLAUDE_SKILLS_DIR);
|
|
103
|
+
|
|
104
|
+
const skillDirs = readdirSync(skillsSource, { withFileTypes: true }).filter((d) => d.isDirectory());
|
|
105
|
+
|
|
106
|
+
for (const dir of skillDirs) {
|
|
107
|
+
const srcPath = join(skillsSource, dir.name);
|
|
108
|
+
const destPath = join(CLAUDE_SKILLS_DIR, dir.name);
|
|
109
|
+
copyDirRecursive(srcPath, destPath);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return { installed: skillDirs.length };
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export function installGstack() {
|
|
116
|
+
const gstackSource = join(PACKAGE_ROOT, 'vendor', 'gstack');
|
|
117
|
+
if (!existsSync(gstackSource)) {
|
|
118
|
+
return { installed: 0, warning: 'gstack submodule not initialized. Run: git submodule update --init vendor/gstack' };
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const gstackDest = join(CLAUDE_SKILLS_DIR, 'gstack');
|
|
122
|
+
ensureDir(gstackDest);
|
|
123
|
+
|
|
124
|
+
const entries = readdirSync(gstackSource, { withFileTypes: true }).filter((d) => d.isDirectory());
|
|
125
|
+
let installed = 0;
|
|
126
|
+
|
|
127
|
+
for (const dir of entries) {
|
|
128
|
+
const skillMd = join(gstackSource, dir.name, 'SKILL.md');
|
|
129
|
+
if (existsSync(skillMd)) {
|
|
130
|
+
const destPath = join(gstackDest, dir.name);
|
|
131
|
+
copyDirRecursive(join(gstackSource, dir.name), destPath);
|
|
132
|
+
installed++;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
return { installed };
|
|
137
|
+
}
|