@jaggerxtrm/specialists 2.1.2 → 2.1.3
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 +92 -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.sh');
|
|
12
16
|
const MCP_NAME = 'specialists';
|
|
13
17
|
const GITHUB_PKG = '@jaggerxtrm/specialists';
|
|
14
18
|
|
|
@@ -72,8 +76,80 @@ function registerMCP() {
|
|
|
72
76
|
return true;
|
|
73
77
|
}
|
|
74
78
|
|
|
79
|
+
// ── Hook installation ─────────────────────────────────────────────────────────
|
|
80
|
+
|
|
81
|
+
const HOOK_SCRIPT = `#!/usr/bin/env bash
|
|
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
|
+
BRANCH=$(git branch --show-current 2>/dev/null)
|
|
89
|
+
|
|
90
|
+
# Not in a git repo or not on a protected branch — allow
|
|
91
|
+
if [ -z "$BRANCH" ] || { [ "$BRANCH" != "main" ] && [ "$BRANCH" != "master" ]; }; then
|
|
92
|
+
exit 0
|
|
93
|
+
fi
|
|
94
|
+
|
|
95
|
+
INPUT=$(cat)
|
|
96
|
+
TOOL=$(echo "$INPUT" | jq -r '.tool_name' 2>/dev/null)
|
|
97
|
+
|
|
98
|
+
BLOCK_MSG="⛔ Direct edits on '$BRANCH' are not allowed.
|
|
99
|
+
Create a feature branch first: git checkout -b feature/<name>"
|
|
100
|
+
|
|
101
|
+
case "$TOOL" in
|
|
102
|
+
Edit|Write|MultiEdit|NotebookEdit)
|
|
103
|
+
echo "$BLOCK_MSG" >&2
|
|
104
|
+
exit 2
|
|
105
|
+
;;
|
|
106
|
+
Bash)
|
|
107
|
+
CMD=$(echo "$INPUT" | jq -r '.tool_input.command' 2>/dev/null)
|
|
108
|
+
if echo "$CMD" | grep -qE '^git (commit|push)'; then
|
|
109
|
+
echo "$BLOCK_MSG" >&2
|
|
110
|
+
exit 2
|
|
111
|
+
fi
|
|
112
|
+
exit 0
|
|
113
|
+
;;
|
|
114
|
+
*)
|
|
115
|
+
exit 0
|
|
116
|
+
;;
|
|
117
|
+
esac
|
|
118
|
+
`;
|
|
119
|
+
|
|
120
|
+
const HOOK_ENTRY = {
|
|
121
|
+
matcher: 'Edit|Write|MultiEdit|NotebookEdit|Bash',
|
|
122
|
+
hooks: [{ type: 'command', command: HOOK_FILE }],
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
function installHook() {
|
|
126
|
+
// 1. Write hook script
|
|
127
|
+
mkdirSync(HOOKS_DIR, { recursive: true });
|
|
128
|
+
writeFileSync(HOOK_FILE, HOOK_SCRIPT, 'utf8');
|
|
129
|
+
chmodSync(HOOK_FILE, 0o755);
|
|
130
|
+
|
|
131
|
+
// 2. Merge into ~/.claude/settings.json
|
|
132
|
+
let settings = {};
|
|
133
|
+
if (existsSync(SETTINGS_FILE)) {
|
|
134
|
+
try { settings = JSON.parse(readFileSync(SETTINGS_FILE, 'utf8')); } catch { /* malformed, overwrite */ }
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
if (!Array.isArray(settings.hooks?.PreToolUse)) {
|
|
138
|
+
settings.hooks = settings.hooks ?? {};
|
|
139
|
+
settings.hooks.PreToolUse = [];
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Idempotent: remove any previous specialists-main-guard entry, re-add
|
|
143
|
+
settings.hooks.PreToolUse = settings.hooks.PreToolUse
|
|
144
|
+
.filter(e => !e.hooks?.some(h => h.command?.includes('specialists-main-guard')));
|
|
145
|
+
settings.hooks.PreToolUse.push(HOOK_ENTRY);
|
|
146
|
+
|
|
147
|
+
mkdirSync(CLAUDE_DIR, { recursive: true });
|
|
148
|
+
writeFileSync(SETTINGS_FILE, JSON.stringify(settings, null, 2) + '\n', 'utf8');
|
|
149
|
+
}
|
|
150
|
+
|
|
75
151
|
// ── Main ──────────────────────────────────────────────────────────────────────
|
|
76
|
-
console.log('\n' + bold('
|
|
152
|
+
console.log('\n' + bold(' Specialists — full-stack installer'));
|
|
77
153
|
|
|
78
154
|
// 1. pi
|
|
79
155
|
section('pi (coding agent runtime)');
|
|
@@ -119,7 +195,16 @@ if (!existsSync(SPECIALISTS_DIR)) {
|
|
|
119
195
|
skip('~/.agents/specialists/ already exists');
|
|
120
196
|
}
|
|
121
197
|
|
|
122
|
-
// 6.
|
|
198
|
+
// 6. Claude Code hooks
|
|
199
|
+
section('Claude Code hooks');
|
|
200
|
+
const hookExisted = existsSync(HOOK_FILE);
|
|
201
|
+
installHook();
|
|
202
|
+
hookExisted
|
|
203
|
+
? ok('main-guard hook updated')
|
|
204
|
+
: ok('main-guard hook installed → ~/.claude/hooks/specialists-main-guard.sh');
|
|
205
|
+
info('Blocks Edit/Write/git commit/push on main or master branch');
|
|
206
|
+
|
|
207
|
+
// 7. Health check
|
|
123
208
|
section('Health check');
|
|
124
209
|
if (isInstalled('pi')) {
|
|
125
210
|
const r = spawnSync('pi', ['--list-models'], { encoding: 'utf8' });
|
|
@@ -128,9 +213,9 @@ if (isInstalled('pi')) {
|
|
|
128
213
|
: skip('No active provider — run pi config to set one up');
|
|
129
214
|
}
|
|
130
215
|
|
|
131
|
-
//
|
|
216
|
+
// 8. Done
|
|
132
217
|
console.log('\n' + bold(green(' Done!')));
|
|
133
218
|
console.log('\n' + bold(' Next steps:'));
|
|
134
219
|
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`);
|
|
220
|
+
console.log(` 2. ${bold('Restart Claude Code')} to load the MCP and hooks`);
|
|
136
221
|
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.3",
|
|
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",
|