@jaggerxtrm/specialists 2.1.2 → 2.1.4
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 +105 -7
- package/package.json +1 -1
package/bin/install.js
CHANGED
|
@@ -1,14 +1,18 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
//
|
|
3
|
-
// Usage: npx --package
|
|
2
|
+
// Specialists Installer
|
|
3
|
+
// Usage: npx --package=@jaggerxtrm/specialists install
|
|
4
4
|
|
|
5
5
|
import { spawnSync } from 'node:child_process';
|
|
6
|
-
import { existsSync, mkdirSync } from 'node:fs';
|
|
6
|
+
import { existsSync, mkdirSync, writeFileSync, readFileSync, chmodSync } from 'node:fs';
|
|
7
7
|
import { homedir } from 'node:os';
|
|
8
8
|
import { join } from 'node:path';
|
|
9
9
|
|
|
10
10
|
const HOME = homedir();
|
|
11
11
|
const SPECIALISTS_DIR = join(HOME, '.agents', 'specialists');
|
|
12
|
+
const CLAUDE_DIR = join(HOME, '.claude');
|
|
13
|
+
const HOOKS_DIR = join(CLAUDE_DIR, 'hooks');
|
|
14
|
+
const SETTINGS_FILE = join(CLAUDE_DIR, 'settings.json');
|
|
15
|
+
const HOOK_FILE = join(HOOKS_DIR, 'specialists-main-guard.mjs');
|
|
12
16
|
const MCP_NAME = 'specialists';
|
|
13
17
|
const GITHUB_PKG = '@jaggerxtrm/specialists';
|
|
14
18
|
|
|
@@ -72,8 +76,93 @@ function registerMCP() {
|
|
|
72
76
|
return true;
|
|
73
77
|
}
|
|
74
78
|
|
|
79
|
+
// ── Hook installation ─────────────────────────────────────────────────────────
|
|
80
|
+
|
|
81
|
+
const HOOK_SCRIPT = `#!/usr/bin/env node
|
|
82
|
+
// specialists — Claude Code PreToolUse hook
|
|
83
|
+
// Blocks writes and git commit/push on main/master branch.
|
|
84
|
+
// Exit 0: allow | Exit 2: block (message shown to user)
|
|
85
|
+
//
|
|
86
|
+
// Installed by: npx --package=@jaggerxtrm/specialists install
|
|
87
|
+
|
|
88
|
+
import { execSync } from 'node:child_process';
|
|
89
|
+
import { readFileSync } from 'node:fs';
|
|
90
|
+
|
|
91
|
+
let branch = '';
|
|
92
|
+
try {
|
|
93
|
+
branch = execSync('git branch --show-current', {
|
|
94
|
+
encoding: 'utf8',
|
|
95
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
96
|
+
}).trim();
|
|
97
|
+
} catch {}
|
|
98
|
+
|
|
99
|
+
if (!branch || (branch !== 'main' && branch !== 'master')) {
|
|
100
|
+
process.exit(0);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
let input;
|
|
104
|
+
try {
|
|
105
|
+
input = JSON.parse(readFileSync(0, 'utf8'));
|
|
106
|
+
} catch {
|
|
107
|
+
process.exit(0);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const tool = input.tool_name ?? '';
|
|
111
|
+
const blockMsg =
|
|
112
|
+
\`⛔ Direct edits on '\${branch}' are not allowed.\\n\` +
|
|
113
|
+
\`Create a feature branch first: git checkout -b feature/<name>\`;
|
|
114
|
+
|
|
115
|
+
const WRITE_TOOLS = new Set(['Edit', 'Write', 'MultiEdit', 'NotebookEdit']);
|
|
116
|
+
|
|
117
|
+
if (WRITE_TOOLS.has(tool)) {
|
|
118
|
+
process.stderr.write(blockMsg + '\\n');
|
|
119
|
+
process.exit(2);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (tool === 'Bash') {
|
|
123
|
+
const cmd = input.tool_input?.command ?? '';
|
|
124
|
+
if (/^git (commit|push)/.test(cmd)) {
|
|
125
|
+
process.stderr.write(blockMsg + '\\n');
|
|
126
|
+
process.exit(2);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
process.exit(0);
|
|
131
|
+
`;
|
|
132
|
+
|
|
133
|
+
const HOOK_ENTRY = {
|
|
134
|
+
matcher: 'Edit|Write|MultiEdit|NotebookEdit|Bash',
|
|
135
|
+
hooks: [{ type: 'command', command: HOOK_FILE }],
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
function installHook() {
|
|
139
|
+
// 1. Write hook script
|
|
140
|
+
mkdirSync(HOOKS_DIR, { recursive: true });
|
|
141
|
+
writeFileSync(HOOK_FILE, HOOK_SCRIPT, 'utf8');
|
|
142
|
+
chmodSync(HOOK_FILE, 0o755);
|
|
143
|
+
|
|
144
|
+
// 2. Merge into ~/.claude/settings.json
|
|
145
|
+
let settings = {};
|
|
146
|
+
if (existsSync(SETTINGS_FILE)) {
|
|
147
|
+
try { settings = JSON.parse(readFileSync(SETTINGS_FILE, 'utf8')); } catch { /* malformed, overwrite */ }
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
if (!Array.isArray(settings.hooks?.PreToolUse)) {
|
|
151
|
+
settings.hooks = settings.hooks ?? {};
|
|
152
|
+
settings.hooks.PreToolUse = [];
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// Idempotent: remove any previous specialists-main-guard entry, re-add
|
|
156
|
+
settings.hooks.PreToolUse = settings.hooks.PreToolUse
|
|
157
|
+
.filter(e => !e.hooks?.some(h => h.command?.includes('specialists-main-guard')));
|
|
158
|
+
settings.hooks.PreToolUse.push(HOOK_ENTRY);
|
|
159
|
+
|
|
160
|
+
mkdirSync(CLAUDE_DIR, { recursive: true });
|
|
161
|
+
writeFileSync(SETTINGS_FILE, JSON.stringify(settings, null, 2) + '\n', 'utf8');
|
|
162
|
+
}
|
|
163
|
+
|
|
75
164
|
// ── Main ──────────────────────────────────────────────────────────────────────
|
|
76
|
-
console.log('\n' + bold('
|
|
165
|
+
console.log('\n' + bold(' Specialists — full-stack installer'));
|
|
77
166
|
|
|
78
167
|
// 1. pi
|
|
79
168
|
section('pi (coding agent runtime)');
|
|
@@ -119,7 +208,16 @@ if (!existsSync(SPECIALISTS_DIR)) {
|
|
|
119
208
|
skip('~/.agents/specialists/ already exists');
|
|
120
209
|
}
|
|
121
210
|
|
|
122
|
-
// 6.
|
|
211
|
+
// 6. Claude Code hooks
|
|
212
|
+
section('Claude Code hooks');
|
|
213
|
+
const hookExisted = existsSync(HOOK_FILE);
|
|
214
|
+
installHook();
|
|
215
|
+
hookExisted
|
|
216
|
+
? ok('main-guard hook updated')
|
|
217
|
+
: ok('main-guard hook installed → ~/.claude/hooks/specialists-main-guard.sh');
|
|
218
|
+
info('Blocks Edit/Write/git commit/push on main or master branch (JS, no jq needed)');
|
|
219
|
+
|
|
220
|
+
// 7. Health check
|
|
123
221
|
section('Health check');
|
|
124
222
|
if (isInstalled('pi')) {
|
|
125
223
|
const r = spawnSync('pi', ['--list-models'], { encoding: 'utf8' });
|
|
@@ -128,9 +226,9 @@ if (isInstalled('pi')) {
|
|
|
128
226
|
: skip('No active provider — run pi config to set one up');
|
|
129
227
|
}
|
|
130
228
|
|
|
131
|
-
//
|
|
229
|
+
// 8. Done
|
|
132
230
|
console.log('\n' + bold(green(' Done!')));
|
|
133
231
|
console.log('\n' + bold(' Next steps:'));
|
|
134
232
|
console.log(` 1. ${bold('Configure pi:')} run ${yellow('pi')} then ${yellow('pi config')} to enable model providers`);
|
|
135
|
-
console.log(` 2. ${bold('Restart Claude Code')} to load the MCP`);
|
|
233
|
+
console.log(` 2. ${bold('Restart Claude Code')} to load the MCP and hooks`);
|
|
136
234
|
console.log(` 3. ${bold('Update later:')} re-run this installer\n`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jaggerxtrm/specialists",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.4",
|
|
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",
|