@jaggerxtrm/specialists 3.0.2 → 3.2.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/bin/install.js +71 -4
- package/dist/index.js +980 -51
- package/hooks/specialists-session-start.mjs +105 -0
- package/package.json +2 -2
- package/specialists/bug-hunt.specialist.yaml +53 -20
- package/specialists/codebase-explorer.specialist.yaml +42 -23
- package/specialists/feature-design.specialist.yaml +48 -29
package/bin/install.js
CHANGED
|
@@ -112,7 +112,13 @@ const BEADS_CLOSE_MEMORY_PROMPT_ENTRY = {
|
|
|
112
112
|
const SPECIALISTS_COMPLETE_FILE = join(HOOKS_DIR, 'specialists-complete.mjs');
|
|
113
113
|
const SPECIALISTS_COMPLETE_ENTRY = {
|
|
114
114
|
hooks: [{ type: 'command', command: SPECIALISTS_COMPLETE_FILE, timeout: 5000 }],
|
|
115
|
+
}
|
|
116
|
+
const SPECIALISTS_SESSION_START_FILE = join(HOOKS_DIR, 'specialists-session-start.mjs');
|
|
117
|
+
const SPECIALISTS_SESSION_START_ENTRY = {
|
|
118
|
+
hooks: [{ type: 'command', command: SPECIALISTS_SESSION_START_FILE, timeout: 8000 }],
|
|
115
119
|
};
|
|
120
|
+
const BUNDLED_SKILLS_DIR = new URL('../skills', import.meta.url).pathname;
|
|
121
|
+
const CLAUDE_SKILLS_DIR = join(CLAUDE_DIR, 'skills');;
|
|
116
122
|
|
|
117
123
|
function promptYN(question) {
|
|
118
124
|
if (!process.stdin.isTTY) return true; // non-interactive: default yes
|
|
@@ -132,7 +138,8 @@ function getHookDrift() {
|
|
|
132
138
|
['beads-commit-gate.mjs', BEADS_COMMIT_GATE_FILE],
|
|
133
139
|
['beads-stop-gate.mjs', BEADS_STOP_GATE_FILE],
|
|
134
140
|
['beads-close-memory-prompt.mjs', BEADS_CLOSE_MEMORY_PROMPT_FILE],
|
|
135
|
-
['specialists-complete.mjs',
|
|
141
|
+
['specialists-complete.mjs', SPECIALISTS_COMPLETE_FILE],
|
|
142
|
+
['specialists-session-start.mjs', SPECIALISTS_SESSION_START_FILE],
|
|
136
143
|
];
|
|
137
144
|
return pairs
|
|
138
145
|
.map(([bundled, dest]) => ({
|
|
@@ -162,6 +169,8 @@ function installHook() {
|
|
|
162
169
|
chmodSync(BEADS_CLOSE_MEMORY_PROMPT_FILE, 0o755);
|
|
163
170
|
copyFileSync(join(BUNDLED_HOOKS_DIR, 'specialists-complete.mjs'), SPECIALISTS_COMPLETE_FILE);
|
|
164
171
|
chmodSync(SPECIALISTS_COMPLETE_FILE, 0o755);
|
|
172
|
+
copyFileSync(join(BUNDLED_HOOKS_DIR, 'specialists-session-start.mjs'), SPECIALISTS_SESSION_START_FILE);
|
|
173
|
+
chmodSync(SPECIALISTS_SESSION_START_FILE, 0o755);
|
|
165
174
|
|
|
166
175
|
let settings = {};
|
|
167
176
|
if (existsSync(SETTINGS_FILE)) {
|
|
@@ -204,10 +213,59 @@ function installHook() {
|
|
|
204
213
|
);
|
|
205
214
|
settings.hooks.UserPromptSubmit.push(SPECIALISTS_COMPLETE_ENTRY);
|
|
206
215
|
|
|
216
|
+
// SessionStart — replace any existing specialists-session-start entry
|
|
217
|
+
if (!Array.isArray(settings.hooks.SessionStart)) settings.hooks.SessionStart = [];
|
|
218
|
+
settings.hooks.SessionStart = settings.hooks.SessionStart.filter(e =>
|
|
219
|
+
!e.hooks?.some(h => h.command?.includes('specialists-session-start'))
|
|
220
|
+
);
|
|
221
|
+
settings.hooks.SessionStart.push(SPECIALISTS_SESSION_START_ENTRY);
|
|
222
|
+
|
|
207
223
|
mkdirSync(CLAUDE_DIR, { recursive: true });
|
|
208
224
|
writeFileSync(SETTINGS_FILE, JSON.stringify(settings, null, 2) + '\n', 'utf8');
|
|
209
225
|
}
|
|
210
226
|
|
|
227
|
+
|
|
228
|
+
function installSkills() {
|
|
229
|
+
if (!existsSync(BUNDLED_SKILLS_DIR)) return { installed: 0, skipped: 0 };
|
|
230
|
+
mkdirSync(CLAUDE_SKILLS_DIR, { recursive: true });
|
|
231
|
+
|
|
232
|
+
let installed = 0;
|
|
233
|
+
let skippedCount = 0;
|
|
234
|
+
let skillNames;
|
|
235
|
+
try {
|
|
236
|
+
skillNames = readdirSync(BUNDLED_SKILLS_DIR, { withFileTypes: true })
|
|
237
|
+
.filter(d => d.isDirectory())
|
|
238
|
+
.map(d => d.name);
|
|
239
|
+
} catch {
|
|
240
|
+
return { installed: 0, skipped: 0 };
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
for (const skillName of skillNames) {
|
|
244
|
+
const srcDir = join(BUNDLED_SKILLS_DIR, skillName);
|
|
245
|
+
const destDir = join(CLAUDE_SKILLS_DIR, skillName);
|
|
246
|
+
const skillFile = join(srcDir, 'SKILL.md');
|
|
247
|
+
const destSkillFile = join(destDir, 'SKILL.md');
|
|
248
|
+
|
|
249
|
+
if (!existsSync(skillFile)) continue;
|
|
250
|
+
|
|
251
|
+
if (existsSync(destSkillFile)) {
|
|
252
|
+
// Check if content matches bundled version
|
|
253
|
+
try {
|
|
254
|
+
if (readFileSync(skillFile, 'utf8') === readFileSync(destSkillFile, 'utf8')) {
|
|
255
|
+
skippedCount++;
|
|
256
|
+
continue;
|
|
257
|
+
}
|
|
258
|
+
} catch { /* fall through to copy */ }
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
mkdirSync(destDir, { recursive: true });
|
|
262
|
+
copyFileSync(skillFile, destSkillFile);
|
|
263
|
+
installed++;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
return { installed, skipped: skippedCount };
|
|
267
|
+
}
|
|
268
|
+
|
|
211
269
|
// ── Main ──────────────────────────────────────────────────────────────────────
|
|
212
270
|
console.log('\n' + bold(' Specialists — full-stack installer'));
|
|
213
271
|
|
|
@@ -283,7 +341,7 @@ if (!hooksExist) {
|
|
|
283
341
|
skip('hooks up to date');
|
|
284
342
|
} else {
|
|
285
343
|
const label = (h) => h.missing ? red('missing') : yellow('updated');
|
|
286
|
-
console.log(` ${yellow('○')} ${drift.length} of
|
|
344
|
+
console.log(` ${yellow('○')} ${drift.length} of 7 hook(s) have changes:`);
|
|
287
345
|
for (const h of drift) info(` ${h.name} ${label(h)}`);
|
|
288
346
|
console.log();
|
|
289
347
|
const confirmed = promptYN(' Update hooks?');
|
|
@@ -300,8 +358,17 @@ info('beads-commit-gate: requires issues closed before git commit');
|
|
|
300
358
|
info('beads-stop-gate: requires issues closed before session end');
|
|
301
359
|
info('beads-close-memory-prompt: nudges knowledge capture after bd close');
|
|
302
360
|
info('specialists-complete: injects completion banners for background jobs');
|
|
361
|
+
info('specialists-session-start: injects context (jobs, specialists, commands) at session start');
|
|
362
|
+
|
|
363
|
+
// 7. Skills
|
|
364
|
+
section('Skills');
|
|
365
|
+
const skillResult = installSkills();
|
|
366
|
+
if (skillResult.installed > 0) ok(`${skillResult.installed} skill(s) installed → ~/.claude/skills/`);
|
|
367
|
+
if (skillResult.skipped > 0) skip(`${skillResult.skipped} skill(s) already up to date`);
|
|
368
|
+
if (skillResult.installed === 0 && skillResult.skipped === 0) skip('No bundled skills found');
|
|
369
|
+
info("specialists-usage: teaches agents when/how to use specialists CLI and MCP tools");
|
|
303
370
|
|
|
304
|
-
//
|
|
371
|
+
// 8. Health check
|
|
305
372
|
section('Health check');
|
|
306
373
|
if (isInstalled('pi')) {
|
|
307
374
|
const r = spawnSync('pi', ['--list-models'], { encoding: 'utf8' });
|
|
@@ -310,7 +377,7 @@ if (isInstalled('pi')) {
|
|
|
310
377
|
: skip('No active provider — run pi config to set one up');
|
|
311
378
|
}
|
|
312
379
|
|
|
313
|
-
//
|
|
380
|
+
// 9. Done
|
|
314
381
|
console.log('\n' + bold(green(' Done!')));
|
|
315
382
|
console.log('\n' + bold(' Next steps:'));
|
|
316
383
|
console.log(` 1. ${bold('Configure pi:')} run ${yellow('pi')} then ${yellow('pi config')} to enable model providers`);
|