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,252 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import ora from 'ora';
|
|
3
|
+
import { isAuthenticated } from '../core/config.js';
|
|
4
|
+
import { chat } from '../core/claude.js';
|
|
5
|
+
import { getProjectContext, listFiles, readFile, getFrontendFiles } from '../core/codebase.js';
|
|
6
|
+
import { isConnected, getConnection } from '../connections/index.js';
|
|
7
|
+
import { fileURLToPath } from 'url';
|
|
8
|
+
import { dirname, join } from 'path';
|
|
9
|
+
|
|
10
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
11
|
+
const __dirname = dirname(__filename);
|
|
12
|
+
|
|
13
|
+
// Built-in skills registry
|
|
14
|
+
const SKILLS = {
|
|
15
|
+
'front-end-design': {
|
|
16
|
+
name: 'Front-end Design',
|
|
17
|
+
description: 'Expert UI/UX design assistance',
|
|
18
|
+
commands: ['design', 'ui', 'ux'],
|
|
19
|
+
promptFile: '../../skills/front-end-design/prompts/system.md',
|
|
20
|
+
},
|
|
21
|
+
'product-expert': {
|
|
22
|
+
name: 'Product Expert',
|
|
23
|
+
description: 'Product management and specifications',
|
|
24
|
+
commands: ['product', 'prd', 'spec'],
|
|
25
|
+
promptFile: '../../skills/product-expert/prompts/system.md',
|
|
26
|
+
},
|
|
27
|
+
'github-manager': {
|
|
28
|
+
name: 'GitHub Manager',
|
|
29
|
+
description: 'GitHub workflows and PR management',
|
|
30
|
+
commands: ['github', 'gh', 'pr'],
|
|
31
|
+
promptFile: '../../skills/github-manager/prompts/system.md',
|
|
32
|
+
requiredConnections: ['github'],
|
|
33
|
+
},
|
|
34
|
+
'front-end-testing': {
|
|
35
|
+
name: 'Front-end Testing',
|
|
36
|
+
description: 'Test generation and coverage analysis',
|
|
37
|
+
commands: ['test', 'testing', 'jest'],
|
|
38
|
+
promptFile: '../../skills/front-end-testing/prompts/system.md',
|
|
39
|
+
},
|
|
40
|
+
'server-admin': {
|
|
41
|
+
name: 'Server Admin',
|
|
42
|
+
description: 'AI-assisted server administration',
|
|
43
|
+
commands: ['server', 'ssh', 'admin'],
|
|
44
|
+
promptFile: '../../skills/server-admin/prompts/system.md',
|
|
45
|
+
requiredConnections: ['servers'],
|
|
46
|
+
},
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
export function listSkills() {
|
|
50
|
+
console.log();
|
|
51
|
+
console.log(chalk.hex('#215ff6').bold(' Installed Skills'));
|
|
52
|
+
console.log(chalk.hex('#4d7fff')(' ─────────────────────────────────────────'));
|
|
53
|
+
console.log();
|
|
54
|
+
|
|
55
|
+
for (const [id, skill] of Object.entries(SKILLS)) {
|
|
56
|
+
const commands = skill.commands.map(c => `slashdev ${c}`).join(', ');
|
|
57
|
+
console.log(` ${chalk.hex('#4d7fff')('◈')} ${chalk.hex('#215ff6').bold(skill.name)}`);
|
|
58
|
+
console.log(` ${chalk.dim(skill.description)}`);
|
|
59
|
+
console.log(` ${chalk.dim('Commands:')} ${chalk.hex('#7a9fff')(commands)}`);
|
|
60
|
+
console.log();
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export function getSkillByCommand(command) {
|
|
65
|
+
for (const [id, skill] of Object.entries(SKILLS)) {
|
|
66
|
+
if (skill.commands.includes(command)) {
|
|
67
|
+
return { id, ...skill };
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export async function runSkill(skillId, prompt, options = {}) {
|
|
74
|
+
const skill = SKILLS[skillId];
|
|
75
|
+
if (!skill) {
|
|
76
|
+
console.log(chalk.red(` Unknown skill: ${skillId}`));
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (!isAuthenticated()) {
|
|
81
|
+
console.log();
|
|
82
|
+
console.log(chalk.hex('#7a9fff')(' You need to login first.'));
|
|
83
|
+
console.log(chalk.dim(` Run ${chalk.hex('#215ff6')('slashdev login')} to authenticate.`));
|
|
84
|
+
console.log();
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (skill.requiredConnections) {
|
|
89
|
+
for (const connId of skill.requiredConnections) {
|
|
90
|
+
if (!isConnected(connId)) {
|
|
91
|
+
console.log();
|
|
92
|
+
console.log(
|
|
93
|
+
chalk.hex('#7a9fff')(
|
|
94
|
+
` This skill requires a ${connId} connection.`
|
|
95
|
+
)
|
|
96
|
+
);
|
|
97
|
+
console.log(
|
|
98
|
+
chalk.dim(
|
|
99
|
+
` Run ${chalk.hex('#215ff6')(`slashdev connect ${connId}`)} to set it up.`
|
|
100
|
+
)
|
|
101
|
+
);
|
|
102
|
+
console.log();
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const cwd = process.cwd();
|
|
109
|
+
const projectContext = getProjectContext(cwd);
|
|
110
|
+
|
|
111
|
+
// Load system prompt
|
|
112
|
+
let systemPrompt = '';
|
|
113
|
+
try {
|
|
114
|
+
const promptPath = join(__dirname, skill.promptFile);
|
|
115
|
+
systemPrompt = readFile(promptPath);
|
|
116
|
+
} catch (e) {
|
|
117
|
+
// Use default if file doesn't exist
|
|
118
|
+
systemPrompt = getDefaultPrompt(skillId);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Add project context to system prompt
|
|
122
|
+
systemPrompt += `\n\n## Current Project Context\n`;
|
|
123
|
+
systemPrompt += `- Framework: ${projectContext.framework}\n`;
|
|
124
|
+
systemPrompt += `- Has TypeScript: ${projectContext.hasTsConfig}\n`;
|
|
125
|
+
systemPrompt += `- Working Directory: ${cwd}\n`;
|
|
126
|
+
|
|
127
|
+
// Add connected services context
|
|
128
|
+
if (skill.requiredConnections) {
|
|
129
|
+
systemPrompt += `\n## Connected Services\n`;
|
|
130
|
+
for (const connId of skill.requiredConnections) {
|
|
131
|
+
const conn = getConnection(connId);
|
|
132
|
+
if (conn) {
|
|
133
|
+
if (Array.isArray(conn)) {
|
|
134
|
+
systemPrompt += `- ${connId}: ${conn.length} server(s) configured\n`;
|
|
135
|
+
for (const entry of conn) {
|
|
136
|
+
systemPrompt += ` - ${entry.name}: ${entry.username}@${entry.host}:${entry.port} (auth: ${entry.authType})\n`;
|
|
137
|
+
}
|
|
138
|
+
} else {
|
|
139
|
+
systemPrompt += `- ${connId}: connected as ${conn.username}\n`;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Get relevant files based on skill
|
|
146
|
+
if (skillId === 'front-end-design' || skillId === 'front-end-testing') {
|
|
147
|
+
const files = getFrontendFiles(cwd).slice(0, 5);
|
|
148
|
+
if (files.length > 0) {
|
|
149
|
+
systemPrompt += `\n## Relevant Files\n`;
|
|
150
|
+
for (const file of files) {
|
|
151
|
+
systemPrompt += `- ${file.relativePath}\n`;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
console.log();
|
|
157
|
+
console.log(chalk.hex('#215ff6').bold(` ${skill.name}`));
|
|
158
|
+
console.log(chalk.hex('#4d7fff')(' ─────────────────────────────────────────'));
|
|
159
|
+
console.log();
|
|
160
|
+
|
|
161
|
+
const spinner = ora({
|
|
162
|
+
text: chalk.hex('#4d7fff')('Thinking...'),
|
|
163
|
+
color: 'blue',
|
|
164
|
+
}).start();
|
|
165
|
+
|
|
166
|
+
try {
|
|
167
|
+
let response = '';
|
|
168
|
+
|
|
169
|
+
if (options.stream !== false) {
|
|
170
|
+
spinner.stop();
|
|
171
|
+
process.stdout.write(chalk.hex('#7a9fff')(' '));
|
|
172
|
+
|
|
173
|
+
response = await chat({
|
|
174
|
+
messages: [{ role: 'user', content: prompt }],
|
|
175
|
+
systemPrompt,
|
|
176
|
+
stream: true,
|
|
177
|
+
onStream: (text) => {
|
|
178
|
+
process.stdout.write(chalk.hex('#a6bfff')(text));
|
|
179
|
+
},
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
console.log('\n');
|
|
183
|
+
} else {
|
|
184
|
+
response = await chat({
|
|
185
|
+
messages: [{ role: 'user', content: prompt }],
|
|
186
|
+
systemPrompt,
|
|
187
|
+
});
|
|
188
|
+
spinner.stop();
|
|
189
|
+
console.log(chalk.hex('#a6bfff')(` ${response}`));
|
|
190
|
+
console.log();
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
return response;
|
|
194
|
+
} catch (error) {
|
|
195
|
+
spinner.fail(chalk.red(`Error: ${error.message}`));
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
function getDefaultPrompt(skillId) {
|
|
200
|
+
const prompts = {
|
|
201
|
+
'front-end-design': `You are a senior front-end design expert. You help with:
|
|
202
|
+
- UI/UX design decisions
|
|
203
|
+
- Component architecture
|
|
204
|
+
- CSS and styling best practices
|
|
205
|
+
- Accessibility (WCAG compliance)
|
|
206
|
+
- Responsive design
|
|
207
|
+
- Design systems
|
|
208
|
+
|
|
209
|
+
Provide clear, actionable advice. Use code examples when helpful.`,
|
|
210
|
+
|
|
211
|
+
'product-expert': `You are a senior product manager. You help with:
|
|
212
|
+
- Writing PRDs (Product Requirements Documents)
|
|
213
|
+
- Creating user stories with acceptance criteria
|
|
214
|
+
- Feature specifications
|
|
215
|
+
- Roadmap planning
|
|
216
|
+
- Stakeholder communication
|
|
217
|
+
|
|
218
|
+
Be thorough but concise. Focus on clarity and actionability.`,
|
|
219
|
+
|
|
220
|
+
'github-manager': `You are a GitHub workflow expert. You help with:
|
|
221
|
+
- Creating well-structured PRs
|
|
222
|
+
- Writing clear commit messages
|
|
223
|
+
- Issue management and triage
|
|
224
|
+
- Release notes and changelogs
|
|
225
|
+
- Branch strategies
|
|
226
|
+
- Code review best practices
|
|
227
|
+
|
|
228
|
+
Provide practical, ready-to-use suggestions.`,
|
|
229
|
+
|
|
230
|
+
'front-end-testing': `You are a front-end testing expert. You help with:
|
|
231
|
+
- Writing unit tests (Jest, Vitest)
|
|
232
|
+
- E2E tests (Playwright, Cypress)
|
|
233
|
+
- Component testing (Testing Library)
|
|
234
|
+
- Test coverage analysis
|
|
235
|
+
- Mock and stub strategies
|
|
236
|
+
- Testing best practices
|
|
237
|
+
|
|
238
|
+
Always provide complete, runnable test code.`,
|
|
239
|
+
|
|
240
|
+
'server-admin': `You are an expert Linux server administrator. You help with:
|
|
241
|
+
- System administration and service management
|
|
242
|
+
- Security hardening and firewall configuration
|
|
243
|
+
- Web server setup (Nginx, Apache, Caddy)
|
|
244
|
+
- Database administration
|
|
245
|
+
- Docker and container management
|
|
246
|
+
- Monitoring, networking, and deployment
|
|
247
|
+
|
|
248
|
+
Always warn about destructive commands and suggest backups before changes.`,
|
|
249
|
+
};
|
|
250
|
+
|
|
251
|
+
return prompts[skillId] || 'You are a helpful assistant.';
|
|
252
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { homedir } from 'os';
|
|
2
|
+
import { join } from 'path';
|
|
3
|
+
import { existsSync, readdirSync } from 'fs';
|
|
4
|
+
|
|
5
|
+
const KNOWN_KEY_NAMES = [
|
|
6
|
+
'id_rsa',
|
|
7
|
+
'id_ed25519',
|
|
8
|
+
'id_ecdsa',
|
|
9
|
+
'id_dsa',
|
|
10
|
+
'id_ed25519_sk',
|
|
11
|
+
'id_ecdsa_sk',
|
|
12
|
+
];
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Scans ~/.ssh/ for SSH key pairs.
|
|
16
|
+
* Works on Linux, macOS, and Windows (all use os.homedir() + '.ssh').
|
|
17
|
+
* @returns {Array<{ name: string, privatePath: string, hasPublic: boolean }>}
|
|
18
|
+
*/
|
|
19
|
+
export function detectSSHKeys() {
|
|
20
|
+
const sshDir = join(homedir(), '.ssh');
|
|
21
|
+
|
|
22
|
+
if (!existsSync(sshDir)) {
|
|
23
|
+
return [];
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
let entries;
|
|
27
|
+
try {
|
|
28
|
+
entries = readdirSync(sshDir);
|
|
29
|
+
} catch {
|
|
30
|
+
return [];
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const keys = [];
|
|
34
|
+
|
|
35
|
+
// Check known key names first
|
|
36
|
+
for (const name of KNOWN_KEY_NAMES) {
|
|
37
|
+
const privatePath = join(sshDir, name);
|
|
38
|
+
const publicPath = join(sshDir, `${name}.pub`);
|
|
39
|
+
|
|
40
|
+
if (existsSync(privatePath)) {
|
|
41
|
+
keys.push({
|
|
42
|
+
name,
|
|
43
|
+
privatePath,
|
|
44
|
+
hasPublic: existsSync(publicPath),
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Discover custom keys via .pub file matching
|
|
50
|
+
for (const entry of entries) {
|
|
51
|
+
if (entry.endsWith('.pub')) {
|
|
52
|
+
const baseName = entry.slice(0, -4);
|
|
53
|
+
if (!KNOWN_KEY_NAMES.includes(baseName)) {
|
|
54
|
+
const privatePath = join(sshDir, baseName);
|
|
55
|
+
if (existsSync(privatePath)) {
|
|
56
|
+
keys.push({
|
|
57
|
+
name: baseName,
|
|
58
|
+
privatePath,
|
|
59
|
+
hasPublic: true,
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return keys;
|
|
67
|
+
}
|