@jaggerxtrm/specialists 2.1.16 → 2.1.17

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.
Files changed (2) hide show
  1. package/bin/install.js +51 -5
  2. package/package.json +1 -1
package/bin/install.js CHANGED
@@ -122,6 +122,36 @@ const BEADS_STOP_GATE_ENTRY = {
122
122
  hooks: [{ type: 'command', command: BEADS_STOP_GATE_FILE, timeout: 10000 }],
123
123
  };
124
124
 
125
+ function promptYN(question) {
126
+ if (!process.stdin.isTTY) return true; // non-interactive: default yes
127
+ process.stdout.write(`${question} [Y/n]: `);
128
+ const r = spawnSync('/bin/sh', ['-c', 'read ans; printf "%s" "$ans"'], {
129
+ stdio: ['inherit', 'pipe', 'inherit'],
130
+ encoding: 'utf8',
131
+ });
132
+ const ans = (r.stdout ?? '').trim().toLowerCase();
133
+ return ans === '' || ans === 'y' || ans === 'yes';
134
+ }
135
+
136
+ function getHookDrift() {
137
+ const pairs = [
138
+ ['specialists-main-guard.mjs', HOOK_FILE],
139
+ ['beads-edit-gate.mjs', BEADS_EDIT_GATE_FILE],
140
+ ['beads-commit-gate.mjs', BEADS_COMMIT_GATE_FILE],
141
+ ['beads-stop-gate.mjs', BEADS_STOP_GATE_FILE],
142
+ ];
143
+ return pairs
144
+ .map(([bundled, dest]) => ({
145
+ name: bundled,
146
+ dest,
147
+ missing: !existsSync(dest),
148
+ changed: existsSync(dest) &&
149
+ readFileSync(join(BUNDLED_HOOKS_DIR, bundled), 'utf8') !==
150
+ readFileSync(dest, 'utf8'),
151
+ }))
152
+ .filter(h => h.missing || h.changed);
153
+ }
154
+
125
155
  function installHook() {
126
156
  mkdirSync(HOOKS_DIR, { recursive: true });
127
157
 
@@ -231,11 +261,27 @@ info('Edit any .specialist.yaml in ~/.agents/specialists/ to customise models, p
231
261
 
232
262
  // 6. Claude Code hooks
233
263
  section('Claude Code hooks');
234
- const hookExisted = existsSync(HOOK_FILE);
235
- installHook();
236
- hookExisted
237
- ? ok('hooks updated (main-guard + beads gates)')
238
- : ok('hooks installed → ~/.claude/hooks/');
264
+ const drift = getHookDrift();
265
+ const hooksExist = existsSync(HOOK_FILE);
266
+
267
+ if (!hooksExist) {
268
+ installHook();
269
+ ok('hooks installed → ~/.claude/hooks/');
270
+ } else if (drift.length === 0) {
271
+ skip('hooks up to date');
272
+ } else {
273
+ const label = (h) => h.missing ? red('missing') : yellow('updated');
274
+ console.log(` ${yellow('○')} ${drift.length} of 4 hook(s) have changes:`);
275
+ for (const h of drift) info(` ${h.name} ${label(h)}`);
276
+ console.log();
277
+ const confirmed = promptYN(' Update hooks?');
278
+ if (confirmed) {
279
+ installHook();
280
+ ok('hooks updated');
281
+ } else {
282
+ skip('hooks update skipped');
283
+ }
284
+ }
239
285
  info('main-guard: blocks file edits and direct master pushes (enforces PR workflow)');
240
286
  info('beads-edit-gate: requires in_progress bead before editing files');
241
287
  info('beads-commit-gate: requires issues closed before git commit');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jaggerxtrm/specialists",
3
- "version": "2.1.16",
3
+ "version": "2.1.17",
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",