@jaggerxtrm/specialists 2.1.5 → 2.1.7
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/README.md +14 -4
- package/bin/install.js +32 -15
- package/dist/index.js +1 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -14,13 +14,12 @@
|
|
|
14
14
|
|
|
15
15
|
## How it works
|
|
16
16
|
|
|
17
|
-
Specialists are `.specialist.yaml` files that define an autonomous agent: its model, system prompt, task template, and permission tier. The server discovers them across
|
|
17
|
+
Specialists are `.specialist.yaml` files that define an autonomous agent: its model, system prompt, task template, and permission tier. The server discovers them across two scopes:
|
|
18
18
|
|
|
19
19
|
| Scope | Location | Purpose |
|
|
20
20
|
|-------|----------|---------|
|
|
21
21
|
| **project** | `./specialists/` | Per-project specialists |
|
|
22
|
-
| **user** | `~/.agents/specialists/` |
|
|
23
|
-
| **system** | bundled with the package | Built-in defaults |
|
|
22
|
+
| **user** | `~/.agents/specialists/` | Built-in defaults (copied on install) + your own |
|
|
24
23
|
|
|
25
24
|
When a specialist runs, the server spawns a `pi` subprocess with the right model, tools, and system prompt injected. Output streams back in real time via cursor-based polling.
|
|
26
25
|
|
|
@@ -170,8 +169,19 @@ specialist:
|
|
|
170
169
|
|
|
171
170
|
communication:
|
|
172
171
|
publishes: [result]
|
|
172
|
+
|
|
173
|
+
# Optional: run scripts before/after the specialist
|
|
174
|
+
skills:
|
|
175
|
+
scripts:
|
|
176
|
+
- path: ./scripts/health-check.sh
|
|
177
|
+
phase: pre # runs before the task prompt
|
|
178
|
+
inject_output: true # output injected as $pre_script_output
|
|
179
|
+
- path: ./scripts/cleanup.sh
|
|
180
|
+
phase: post # runs after the specialist completes
|
|
173
181
|
```
|
|
174
182
|
|
|
183
|
+
Pre-script output is formatted as `<pre_flight_context>` XML and available in `task_template` via `$pre_script_output`. Scripts run locally via the host shell — not inside the pi agent. Failed scripts include their exit code so the specialist can reason about failures.
|
|
184
|
+
|
|
175
185
|
**Model IDs** use the full provider/model format: `anthropic/claude-sonnet-4-6`, `google-gemini-cli/gemini-3-flash-preview`, `anthropic/claude-haiku-4-5`.
|
|
176
186
|
|
|
177
187
|
---
|
|
@@ -187,7 +197,7 @@ bun test
|
|
|
187
197
|
```
|
|
188
198
|
|
|
189
199
|
- **Build**: `bun build src/index.ts --target=node --outfile=dist/index.js`
|
|
190
|
-
- **Test**: `bun --bun vitest run` (
|
|
200
|
+
- **Test**: `bun --bun vitest run` (68 unit tests)
|
|
191
201
|
- **Lint**: `tsc --noEmit`
|
|
192
202
|
|
|
193
203
|
See [CLAUDE.md](CLAUDE.md) for the full architecture guide and [ROADMAP.md](ROADMAP.md) for planned features.
|
package/bin/install.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// Usage: npx --package=@jaggerxtrm/specialists install
|
|
4
4
|
|
|
5
5
|
import { spawnSync } from 'node:child_process';
|
|
6
|
-
import { existsSync, mkdirSync, writeFileSync, readFileSync, chmodSync } from 'node:fs';
|
|
6
|
+
import { existsSync, mkdirSync, writeFileSync, readFileSync, copyFileSync, readdirSync, chmodSync } from 'node:fs';
|
|
7
7
|
import { homedir } from 'node:os';
|
|
8
8
|
import { join } from 'node:path';
|
|
9
9
|
|
|
@@ -16,6 +16,9 @@ const HOOK_FILE = join(HOOKS_DIR, 'specialists-main-guard.mjs');
|
|
|
16
16
|
const MCP_NAME = 'specialists';
|
|
17
17
|
const GITHUB_PKG = '@jaggerxtrm/specialists';
|
|
18
18
|
|
|
19
|
+
// Bundled specialists dir — resolved relative to this file (bin/../specialists/)
|
|
20
|
+
const BUNDLED_SPECIALISTS_DIR = new URL('../specialists', import.meta.url).pathname;
|
|
21
|
+
|
|
19
22
|
// ── ANSI helpers ──────────────────────────────────────────────────────────────
|
|
20
23
|
const dim = (s) => `\x1b[2m${s}\x1b[0m`;
|
|
21
24
|
const green = (s) => `\x1b[32m${s}\x1b[0m`;
|
|
@@ -136,15 +139,13 @@ const HOOK_ENTRY = {
|
|
|
136
139
|
};
|
|
137
140
|
|
|
138
141
|
function installHook() {
|
|
139
|
-
// 1. Write hook script
|
|
140
142
|
mkdirSync(HOOKS_DIR, { recursive: true });
|
|
141
143
|
writeFileSync(HOOK_FILE, HOOK_SCRIPT, 'utf8');
|
|
142
144
|
chmodSync(HOOK_FILE, 0o755);
|
|
143
145
|
|
|
144
|
-
// 2. Merge into ~/.claude/settings.json
|
|
145
146
|
let settings = {};
|
|
146
147
|
if (existsSync(SETTINGS_FILE)) {
|
|
147
|
-
try { settings = JSON.parse(readFileSync(SETTINGS_FILE, 'utf8')); } catch {
|
|
148
|
+
try { settings = JSON.parse(readFileSync(SETTINGS_FILE, 'utf8')); } catch {}
|
|
148
149
|
}
|
|
149
150
|
|
|
150
151
|
if (!Array.isArray(settings.hooks?.PreToolUse)) {
|
|
@@ -152,7 +153,6 @@ function installHook() {
|
|
|
152
153
|
settings.hooks.PreToolUse = [];
|
|
153
154
|
}
|
|
154
155
|
|
|
155
|
-
// Idempotent: remove any previous specialists-main-guard entry, re-add
|
|
156
156
|
settings.hooks.PreToolUse = settings.hooks.PreToolUse
|
|
157
157
|
.filter(e => !e.hooks?.some(h => h.command?.includes('specialists-main-guard')));
|
|
158
158
|
settings.hooks.PreToolUse.push(HOOK_ENTRY);
|
|
@@ -199,23 +199,39 @@ registered
|
|
|
199
199
|
? ok(`MCP '${MCP_NAME}' registered at user scope`)
|
|
200
200
|
: skip(`MCP '${MCP_NAME}' already registered`);
|
|
201
201
|
|
|
202
|
-
// 5. Scaffold
|
|
203
|
-
section('
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
202
|
+
// 5. Scaffold + copy built-in specialists
|
|
203
|
+
section('Specialists');
|
|
204
|
+
mkdirSync(SPECIALISTS_DIR, { recursive: true });
|
|
205
|
+
|
|
206
|
+
const yamlFiles = existsSync(BUNDLED_SPECIALISTS_DIR)
|
|
207
|
+
? readdirSync(BUNDLED_SPECIALISTS_DIR).filter(f => f.endsWith('.specialist.yaml'))
|
|
208
|
+
: [];
|
|
209
|
+
|
|
210
|
+
let installed = 0;
|
|
211
|
+
let skipped = 0;
|
|
212
|
+
for (const file of yamlFiles) {
|
|
213
|
+
const dest = join(SPECIALISTS_DIR, file);
|
|
214
|
+
if (existsSync(dest)) {
|
|
215
|
+
skipped++;
|
|
216
|
+
} else {
|
|
217
|
+
copyFileSync(join(BUNDLED_SPECIALISTS_DIR, file), dest);
|
|
218
|
+
installed++;
|
|
219
|
+
}
|
|
209
220
|
}
|
|
210
221
|
|
|
222
|
+
if (installed > 0) ok(`${installed} specialist(s) installed → ~/.agents/specialists/`);
|
|
223
|
+
if (skipped > 0) skip(`${skipped} specialist(s) already exist (user-modified, keeping)`);
|
|
224
|
+
if (installed === 0 && skipped === 0) skip('No built-in specialists found');
|
|
225
|
+
info('Edit any .specialist.yaml in ~/.agents/specialists/ to customise models, prompts, permissions');
|
|
226
|
+
|
|
211
227
|
// 6. Claude Code hooks
|
|
212
228
|
section('Claude Code hooks');
|
|
213
229
|
const hookExisted = existsSync(HOOK_FILE);
|
|
214
230
|
installHook();
|
|
215
231
|
hookExisted
|
|
216
232
|
? ok('main-guard hook updated')
|
|
217
|
-
: ok('main-guard hook installed → ~/.claude/hooks/specialists-main-guard.
|
|
218
|
-
info('Blocks Edit/Write/git commit/push on main or master branch
|
|
233
|
+
: ok('main-guard hook installed → ~/.claude/hooks/specialists-main-guard.mjs');
|
|
234
|
+
info('Blocks Edit/Write/git commit/push on main or master branch');
|
|
219
235
|
|
|
220
236
|
// 7. Health check
|
|
221
237
|
section('Health check');
|
|
@@ -231,4 +247,5 @@ console.log('\n' + bold(green(' Done!')));
|
|
|
231
247
|
console.log('\n' + bold(' Next steps:'));
|
|
232
248
|
console.log(` 1. ${bold('Configure pi:')} run ${yellow('pi')} then ${yellow('pi config')} to enable model providers`);
|
|
233
249
|
console.log(` 2. ${bold('Restart Claude Code')} to load the MCP and hooks`);
|
|
234
|
-
console.log(` 3. ${bold('
|
|
250
|
+
console.log(` 3. ${bold('Customise specialists:')} edit files in ${yellow('~/.agents/specialists/')}`);
|
|
251
|
+
console.log(` 4. ${bold('Update later:')} re-run this installer (existing specialists preserved)\n`);
|
package/dist/index.js
CHANGED
|
@@ -24786,19 +24786,16 @@ class SpecialistLoader {
|
|
|
24786
24786
|
cache = new Map;
|
|
24787
24787
|
projectDir;
|
|
24788
24788
|
userDir;
|
|
24789
|
-
systemDir;
|
|
24790
24789
|
constructor(options = {}) {
|
|
24791
24790
|
this.projectDir = options.projectDir ?? process.cwd();
|
|
24792
24791
|
this.userDir = options.userDir ?? join(homedir(), ".agents", "specialists");
|
|
24793
|
-
this.systemDir = options.systemDir ?? join(new URL(import.meta.url).pathname, "..", "..", "specialists");
|
|
24794
24792
|
}
|
|
24795
24793
|
getScanDirs() {
|
|
24796
24794
|
return [
|
|
24797
24795
|
{ path: join(this.projectDir, "specialists"), scope: "project" },
|
|
24798
24796
|
{ path: join(this.projectDir, ".claude", "specialists"), scope: "project" },
|
|
24799
24797
|
{ path: join(this.projectDir, ".agent-forge", "specialists"), scope: "project" },
|
|
24800
|
-
{ path: this.userDir, scope: "user" }
|
|
24801
|
-
{ path: this.systemDir, scope: "system" }
|
|
24798
|
+
{ path: this.userDir, scope: "user" }
|
|
24802
24799
|
].filter((d) => existsSync(d.path));
|
|
24803
24800
|
}
|
|
24804
24801
|
async list(category) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jaggerxtrm/specialists",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.7",
|
|
4
4
|
"description": "OmniSpecialist — 7-tool MCP orchestration layer powered by the Specialist System. Discover and execute .specialist.yaml files across project/user/system scopes via pi.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"type": "module",
|