@pushary/agent-hooks 0.98.0 → 1.0.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.
Files changed (81) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/dist/bin/pushary-bell-hook.js +20 -19
  3. package/dist/bin/pushary-bell.js +7 -6
  4. package/dist/bin/pushary-claude.js +24 -25
  5. package/dist/bin/pushary-clean.js +61 -18
  6. package/dist/bin/pushary-codex-bridge.js +11 -10
  7. package/dist/bin/pushary-codex-hook.js +43 -35
  8. package/dist/bin/pushary-codex.js +7 -7
  9. package/dist/bin/pushary-connect.js +7 -6
  10. package/dist/bin/pushary-cowork.js +3 -3
  11. package/dist/bin/pushary-daemon.js +24 -23
  12. package/dist/bin/pushary-disconnect.js +48 -7
  13. package/dist/bin/pushary-doctor.js +61 -21
  14. package/dist/bin/pushary-elicitation-hook.js +19 -18
  15. package/dist/bin/pushary-gemini-bridge.js +10 -9
  16. package/dist/bin/pushary-gemini-hook.js +27 -28
  17. package/dist/bin/pushary-hook.js +63 -34
  18. package/dist/bin/pushary-login.js +8 -7
  19. package/dist/bin/pushary-logout.js +5 -4
  20. package/dist/bin/pushary-mode.js +3 -3
  21. package/dist/bin/pushary-notification-hook.js +28 -22
  22. package/dist/bin/pushary-opencode-hook.js +29 -30
  23. package/dist/bin/pushary-permission-denied-hook.js +26 -24
  24. package/dist/bin/pushary-permission-hook.js +34 -25
  25. package/dist/bin/pushary-post-hook.js +32 -23
  26. package/dist/bin/pushary-prompt-hook.js +28 -22
  27. package/dist/bin/pushary-session-end-hook.js +28 -22
  28. package/dist/bin/pushary-session-start-hook.js +32 -26
  29. package/dist/bin/pushary-setup.js +146 -37
  30. package/dist/bin/pushary-stats.js +2 -2
  31. package/dist/bin/pushary-status.js +11 -10
  32. package/dist/bin/pushary-stop-hook.js +29 -23
  33. package/dist/bin/pushary-stopfailure-hook.js +28 -22
  34. package/dist/bin/pushary-transcript-register.js +18 -17
  35. package/dist/bin/pushary-transcripts.js +6 -5
  36. package/dist/bin/pushary-upgrade.js +15 -14
  37. package/dist/bin/pushary-wait.js +3 -3
  38. package/dist/{chunk-HFQ7WSHZ.js → chunk-2PS3DTHQ.js} +7 -4
  39. package/dist/{chunk-W2MG2P46.js → chunk-2SL5X7UB.js} +9 -6
  40. package/dist/{chunk-53QCTSWY.js → chunk-3C4ERARU.js} +4 -4
  41. package/dist/{chunk-LBNAGGLV.js → chunk-3UUAK2AK.js} +3 -2
  42. package/dist/{chunk-V47GITZV.js → chunk-447SBIC7.js} +15 -2
  43. package/dist/{chunk-VZD2GEKB.js → chunk-4LL6LG5X.js} +1 -43
  44. package/dist/{chunk-WU6LSXJ6.js → chunk-7WMFPYR5.js} +2 -2
  45. package/dist/{chunk-XPPXOKIG.js → chunk-7ZDFVWRR.js} +1 -1
  46. package/dist/chunk-A3D43VXO.js +44 -0
  47. package/dist/{chunk-3RVCQDC4.js → chunk-BOZETFXB.js} +1 -1
  48. package/dist/{chunk-LX4NNYII.js → chunk-BPD4P3NN.js} +2 -2
  49. package/dist/{chunk-IRLTW6KF.js → chunk-CDTGL7TT.js} +1 -1
  50. package/dist/{chunk-CP34FI4B.js → chunk-D2N2EJH2.js} +2 -2
  51. package/dist/{chunk-J44RPHUZ.js → chunk-DMJTWULU.js} +2 -2
  52. package/dist/{chunk-SJLGOVC6.js → chunk-DOYARIYB.js} +4 -2
  53. package/dist/{chunk-XOOWC62T.js → chunk-DUOG74E7.js} +1 -1
  54. package/dist/{chunk-OPR2BPD4.js → chunk-EMCNSBF3.js} +1 -1
  55. package/dist/{chunk-5KLAETSU.js → chunk-GTR72MIU.js} +1 -1
  56. package/dist/{chunk-5B5QV3KC.js → chunk-HGEI7SVM.js} +8 -8
  57. package/dist/chunk-HS2KEAFD.js +224 -0
  58. package/dist/{chunk-6CYB77VD.js → chunk-I3TWY7GH.js} +30 -29
  59. package/dist/{chunk-NQGYPTJQ.js → chunk-IBU7UU3R.js} +1 -1
  60. package/dist/{chunk-M4TVEMLH.js → chunk-IWZG2Y4Z.js} +1 -1
  61. package/dist/{chunk-TZHDKRMG.js → chunk-IYA3NCZG.js} +2 -2
  62. package/dist/{chunk-XL3RKJYQ.js → chunk-LTPQKBJH.js} +65 -30
  63. package/dist/{chunk-YJDRHQLG.js → chunk-MDV7ZRAT.js} +698 -43
  64. package/dist/{chunk-QXIF6R4P.js → chunk-MGI6MLFU.js} +16 -16
  65. package/dist/{chunk-DOD3A6EU.js → chunk-MSOFZYI3.js} +4 -4
  66. package/dist/{chunk-JQU6PMH4.js → chunk-R744XA2M.js} +62 -45
  67. package/dist/{chunk-FAJ4R7IB.js → chunk-RFNMSC6N.js} +1 -1
  68. package/dist/{chunk-NIXVMMJM.js → chunk-S2W4ZSHE.js} +1 -1
  69. package/dist/{chunk-OUAE3GKO.js → chunk-TTBRBWAK.js} +278 -1
  70. package/dist/{chunk-6C6MS57P.js → chunk-TVY7AT6O.js} +1 -1
  71. package/dist/{chunk-2GLOHBYB.js → chunk-UWQVODHT.js} +1 -1
  72. package/dist/{chunk-6LRGZJFB.js → chunk-XK34HTMW.js} +10 -6
  73. package/dist/{chunk-SI7GYBQP.js → chunk-XKDKUFMP.js} +1 -1
  74. package/dist/{chunk-HB2KB7QL.js → chunk-XNXIMIFY.js} +1 -1
  75. package/dist/{chunk-K6CH5NVW.js → chunk-Y55JNI2I.js} +1 -1
  76. package/dist/{chunk-OBYC6SN4.js → chunk-YKLCWWEI.js} +1 -1
  77. package/dist/{chunk-B35OCFXH.js → chunk-ZZIUQVAN.js} +4 -4
  78. package/dist/reapply-I4NB5SSM.js +30 -0
  79. package/dist/src/index.js +13 -10
  80. package/package.json +1 -1
  81. package/dist/chunk-A76GNRIK.js +0 -608
@@ -1,17 +1,17 @@
1
1
  import {
2
2
  CLAUDE_HOOK_EVENTS,
3
3
  supportedClaudeEvents
4
- } from "./chunk-HFQ7WSHZ.js";
4
+ } from "./chunk-2PS3DTHQ.js";
5
5
  import {
6
6
  guardBinary,
7
7
  holdBudgetOf,
8
8
  isProtectedByScope,
9
9
  registerHookEntry
10
- } from "./chunk-XL3RKJYQ.js";
10
+ } from "./chunk-LTPQKBJH.js";
11
11
  import {
12
12
  HOOK_LOCATOR_BINARY,
13
13
  withHookHoldBudget
14
- } from "./chunk-OUAE3GKO.js";
14
+ } from "./chunk-TTBRBWAK.js";
15
15
 
16
16
  // src/claude-config.ts
17
17
  import { join } from "path";
@@ -113,6 +113,16 @@ var removeClaudeMcpServers = (config) => {
113
113
  }
114
114
  return changed;
115
115
  };
116
+ var removePusharyToolPermissions = (settings) => {
117
+ const permissions = asRecord(settings.permissions);
118
+ if (!permissions || !Array.isArray(permissions.allow)) return false;
119
+ const filtered = permissions.allow.filter((rule) => !isPusharyPermission(rule));
120
+ if (filtered.length === permissions.allow.length) return false;
121
+ if (filtered.length === 0) delete permissions.allow;
122
+ else permissions.allow = filtered;
123
+ if (Object.keys(permissions).length === 0) delete settings.permissions;
124
+ return true;
125
+ };
116
126
  var addPusharyToolPermissions = (settings) => {
117
127
  const permissions = ensureRecord(settings, "permissions");
118
128
  const allow = Array.isArray(permissions.allow) ? permissions.allow : [];
@@ -151,19 +161,7 @@ var entry = (definition, resolve) => ({
151
161
  });
152
162
  var removePusharySettings = (settings, scope) => {
153
163
  let changed = scope?.keepMcpServer === true ? false : removeClaudeMcpServers(settings);
154
- const permissions = scope?.keepMcpServer === true ? void 0 : asRecord(settings.permissions);
155
- if (permissions && Array.isArray(permissions.allow)) {
156
- const filtered = permissions.allow.filter((rule) => !isPusharyPermission(rule));
157
- if (filtered.length !== permissions.allow.length) {
158
- if (filtered.length === 0) {
159
- delete permissions.allow;
160
- } else {
161
- permissions.allow = filtered;
162
- }
163
- if (Object.keys(permissions).length === 0) delete settings.permissions;
164
- changed = true;
165
- }
166
- }
164
+ if (scope?.keepMcpServer !== true && removePusharyToolPermissions(settings)) changed = true;
167
165
  const hooks = asRecord(settings.hooks);
168
166
  if (hooks) {
169
167
  for (const { event: key } of CLAUDE_HOOK_EVENTS) {
@@ -185,12 +183,14 @@ var removePusharySettings = (settings, scope) => {
185
183
  };
186
184
 
187
185
  export {
186
+ isPusharyHook,
188
187
  addClaudeMcpServer,
189
188
  isClaudeMcpSnapshot,
190
189
  captureClaudeMcpServer,
191
190
  updateClaudeMcpSnapshot,
192
191
  restoreClaudeMcpServer,
193
192
  removeClaudeMcpServers,
193
+ removePusharyToolPermissions,
194
194
  addPusharyToolPermissions,
195
195
  addPusharyHooks,
196
196
  removePusharySettings
@@ -4,23 +4,23 @@ import {
4
4
  } from "./chunk-XHKBHWLX.js";
5
5
  import {
6
6
  getMachineId
7
- } from "./chunk-WU6LSXJ6.js";
7
+ } from "./chunk-7WMFPYR5.js";
8
8
  import {
9
9
  pusharyDir,
10
10
  resolveGlobalBinary
11
- } from "./chunk-XL3RKJYQ.js";
11
+ } from "./chunk-LTPQKBJH.js";
12
12
  import {
13
13
  getPackageVersion
14
14
  } from "./chunk-DINDL2CF.js";
15
15
  import {
16
16
  readControlMode
17
- } from "./chunk-SJLGOVC6.js";
17
+ } from "./chunk-DOYARIYB.js";
18
18
  import {
19
19
  readJsonSafe,
20
20
  writeFileAtomic,
21
21
  writeJsonAtomic,
22
22
  writeJsonAtomicDurable
23
- } from "./chunk-VZD2GEKB.js";
23
+ } from "./chunk-4LL6LG5X.js";
24
24
  import {
25
25
  getApiKey
26
26
  } from "./chunk-2UMNXADU.js";
@@ -1,17 +1,20 @@
1
1
  import {
2
2
  detectClaudeVersion
3
- } from "./chunk-K6CH5NVW.js";
3
+ } from "./chunk-Y55JNI2I.js";
4
4
  import {
5
5
  addClaudeMcpServer,
6
6
  addPusharyHooks,
7
7
  addPusharyToolPermissions
8
- } from "./chunk-QXIF6R4P.js";
8
+ } from "./chunk-MGI6MLFU.js";
9
9
  import {
10
+ claudeUnprovenHolds,
10
11
  claudeWired,
12
+ codexUnprovenHolds,
11
13
  codexWired,
12
14
  geminiWired
13
- } from "./chunk-6LRGZJFB.js";
15
+ } from "./chunk-XK34HTMW.js";
14
16
  import {
17
+ CLAUDE_HOOK_BINARY,
15
18
  CLAUDE_HOOK_EVENTS,
16
19
  GEMINI_HOOK_BINARY,
17
20
  addGeminiHooks,
@@ -21,7 +24,7 @@ import {
21
24
  needsReregistration,
22
25
  registrationSignatures,
23
26
  supportedClaudeEvents
24
- } from "./chunk-HFQ7WSHZ.js";
27
+ } from "./chunk-2PS3DTHQ.js";
25
28
  import {
26
29
  CODEX_HOOK_BINARY,
27
30
  addCodexHookTrust,
@@ -31,69 +34,48 @@ import {
31
34
  codexConfigToml,
32
35
  codexHookPositions,
33
36
  codexHooksJson,
37
+ codexRegistrationHolder,
34
38
  codexVacatedIndexes,
35
39
  dropVacatedCodexHookTrust,
36
40
  guardBinary,
37
41
  hasCodexHooks,
38
- resolveGlobalBinDir,
42
+ namesOurBinary,
43
+ registeredCommands,
44
+ registrationHolderOf,
39
45
  resolveGlobalBinary,
40
46
  vscodePluginMcp
41
- } from "./chunk-XL3RKJYQ.js";
47
+ } from "./chunk-LTPQKBJH.js";
42
48
  import {
43
49
  readJsonSafe,
50
+ writeFileAtomic,
44
51
  writeJsonAtomic
45
- } from "./chunk-VZD2GEKB.js";
46
- import {
47
- HOOK_LOCATOR_BINARY
48
- } from "./chunk-OUAE3GKO.js";
52
+ } from "./chunk-4LL6LG5X.js";
49
53
 
50
54
  // src/reapply.ts
51
- import { join } from "path";
55
+ import { dirname, join } from "path";
52
56
  import { homedir } from "os";
53
- import { readFileSync, writeFileSync } from "fs";
57
+ import { readFileSync, statSync } from "fs";
54
58
  import { parse as parseTOML, stringify as stringifyTOML } from "smol-toml";
55
59
 
56
60
  // src/reregister.ts
57
61
  var asRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value) ? value : null;
58
- var commandsUnder = (settings) => {
59
- const hooks = asRecord(settings?.hooks);
60
- if (!hooks) return [];
61
- const found = [];
62
- for (const [event, value] of Object.entries(hooks)) {
63
- if (!Array.isArray(value)) continue;
64
- for (const item of value) {
65
- const inner = asRecord(item)?.hooks;
66
- if (!Array.isArray(inner)) continue;
67
- for (const hook of inner) {
68
- const command = asRecord(hook)?.command;
69
- if (typeof command === "string") found.push({ event, command });
70
- }
71
- }
72
- }
73
- return found;
74
- };
75
- var segments = (command) => command.split(/[\s/\\"']+/).map((segment) => segment.replace(/\.(cmd|exe)$/i, ""));
76
- var CLI_BINARIES = new Set(CLAUDE_HOOK_EVENTS.map((hook) => hook.binary));
77
- var namesLocator = (command) => segments(command).includes(HOOK_LOCATOR_BINARY);
78
- var namesCliBinary = (command) => segments(command).some((segment) => CLI_BINARIES.has(segment));
79
- var registrationHolder = (settings) => {
80
- const commands = commandsUnder(settings);
81
- if (commands.some(({ command }) => namesLocator(command))) return "app";
82
- return commands.some(({ command }) => namesCliBinary(command)) ? "cli" : "none";
83
- };
62
+ var CLAUDE_CLI_BINARIES = new Set(CLAUDE_HOOK_EVENTS.map((hook) => hook.binary));
63
+ var registrationHolder = (settings) => registrationHolderOf(settings, CLAUDE_CLI_BINARIES);
84
64
  var registeredPusharyEvents = (settings) => new Set(
85
- commandsUnder(settings).filter(({ command }) => namesLocator(command) || namesCliBinary(command)).map(({ event }) => event)
65
+ registeredCommands(settings).filter(({ command }) => namesOurBinary(command, CLAUDE_CLI_BINARIES)).map(({ event }) => event)
86
66
  );
87
67
  var reregisterVerdict = ({ settings, stored, version }) => {
88
68
  const holder = registrationHolder(settings);
89
69
  if (holder === "none") return { kind: "not_wired" };
90
70
  if (holder === "app") return { kind: "app_owned" };
91
- if (!needsReregistration(stored, version)) return { kind: "current" };
71
+ const unproven = claudeUnprovenHolds(settings);
72
+ if (unproven.length === 0 && !needsReregistration(stored, version)) return { kind: "current" };
92
73
  const supported = new Set(supportedClaudeEvents(version).map((hook) => hook.event));
93
74
  const present = registeredPusharyEvents(settings);
94
75
  const missing = [...supported].filter((event) => !present.has(event));
95
76
  const extra = [...present].filter((event) => !supported.has(event));
96
- return missing.length > 0 || extra.length > 0 ? { kind: "stale", version, missing, extra } : { kind: "record_only", version };
77
+ const repairable = unproven.filter((event) => supported.has(event));
78
+ return missing.length > 0 || extra.length > 0 || repairable.length > 0 ? { kind: "stale", version, missing, extra, unproven: repairable } : { kind: "record_only", version };
97
79
  };
98
80
  var receiptFor = (version, now = /* @__PURE__ */ new Date()) => ({
99
81
  signatures: registrationSignatures(supportedClaudeEvents(version), version),
@@ -119,8 +101,17 @@ var readJson = (filePath) => {
119
101
  return null;
120
102
  };
121
103
  var writeJson = (filePath, data) => writeJsonAtomic(filePath, data);
122
- var hasPusharyClaudeHooks = (settings) => JSON.stringify(settings.hooks ?? {}).includes("pushary-hook");
104
+ var hasPusharyClaudeHooks = (settings) => JSON.stringify(settings.hooks ?? {}).includes(CLAUDE_HOOK_BINARY);
123
105
  var hasPusharyMcp = (claudeJson) => !!claudeJson.mcpServers?.pushary;
106
+ var PRIVATE_TO_OWNER = 384;
107
+ var modeToPreserve = (filePath) => {
108
+ try {
109
+ return statSync(filePath).mode & 511;
110
+ } catch {
111
+ return PRIVATE_TO_OWNER;
112
+ }
113
+ };
114
+ var wouldWriteBareNames = (resolve, probe) => resolve(probe) === void 0;
124
115
  var reapplyClaudeSettings = (apiKey, paths = {}) => {
125
116
  const settingsPath = paths.settingsPath ?? CLAUDE_SETTINGS;
126
117
  const claudeJsonPath = paths.claudeJsonPath ?? CLAUDE_JSON;
@@ -136,7 +127,8 @@ var reapplyClaudeSettings = (apiKey, paths = {}) => {
136
127
  if (!settingsWritable) throw new Error("settings.json could not be parsed");
137
128
  const s = settings;
138
129
  addPusharyToolPermissions(s);
139
- addPusharyHooks(s, resolveGlobalBinDir("pushary-hook"), (paths.detectVersion ?? detectClaudeVersion)());
130
+ const binary = (paths.resolveBinary ?? resolveGlobalBinary)(CLAUDE_HOOK_BINARY);
131
+ addPusharyHooks(s, binary ? dirname(binary) : void 0, (paths.detectVersion ?? detectClaudeVersion)());
140
132
  writeJson(settingsPath, s);
141
133
  hooks = true;
142
134
  } catch {
@@ -168,6 +160,10 @@ var reapplyClaudeForVersion = (apiKey, paths = {}) => {
168
160
  if (verdict.kind === "not_wired" || verdict.kind === "app_owned" || verdict.kind === "current") {
169
161
  return { verdict: verdict.kind, rewrote: false, missing: [] };
170
162
  }
163
+ const resolveBinary = paths.resolveBinary ?? resolveGlobalBinary;
164
+ if (verdict.kind === "stale" && wouldWriteBareNames(resolveBinary, CLAUDE_HOOK_BINARY)) {
165
+ return { verdict: "stale", rewrote: false, missing: verdict.missing };
166
+ }
171
167
  const rewrote = verdict.kind === "stale" ? reapplyClaudeSettings(apiKey, { ...paths, settingsPath }).hooks : false;
172
168
  if (verdict.kind === "stale" && !rewrote) return { verdict: "stale", rewrote: false, missing: verdict.missing };
173
169
  writeJsonAtomic(receiptPath, receiptFor(version));
@@ -213,7 +209,7 @@ var reapplyCodexSettings = (apiKey, paths = {}) => {
213
209
  const hooksConfig = readJson(hooksPath);
214
210
  const configured = config != null && !!config.mcp_servers?.pushary || hooksConfig != null && hasCodexHooks(hooksConfig);
215
211
  if (!configured) return { configured: false, reapplied: false, hooks: false, mcp: false };
216
- const command = guardBinary(resolveGlobalBinary(CODEX_HOOK_BINARY) ?? CODEX_HOOK_BINARY);
212
+ const command = guardBinary((paths.resolveBinary ?? resolveGlobalBinary)(CODEX_HOOK_BINARY) ?? CODEX_HOOK_BINARY);
217
213
  let hooks = false;
218
214
  let mcp = false;
219
215
  const vacated = codexVacatedIndexes(hooksConfig ?? void 0);
@@ -235,12 +231,27 @@ var reapplyCodexSettings = (apiKey, paths = {}) => {
235
231
  addCodexMcpServer(config, apiKey);
236
232
  mcp = true;
237
233
  }
238
- if (hooks || apiKey) writeFileSync(configPath, stringifyTOML(config), "utf-8");
234
+ if (hooks || apiKey) {
235
+ writeFileAtomic(configPath, stringifyTOML(config), modeToPreserve(configPath), "utf-8");
236
+ }
239
237
  } catch {
240
238
  }
241
239
  }
242
240
  return { configured: true, reapplied: hooks || mcp, hooks, mcp };
243
241
  };
242
+ var repairCodexHolds = (paths = {}) => {
243
+ const idle = { configured: false, reapplied: false, hooks: false, mcp: false };
244
+ try {
245
+ const hooksPath = paths.hooksPath ?? codexHooksJson();
246
+ const hooksConfig = readJson(hooksPath);
247
+ if (!hooksConfig || codexRegistrationHolder(hooksConfig) !== "cli") return idle;
248
+ if (codexUnprovenHolds(hooksConfig).length === 0) return idle;
249
+ if (wouldWriteBareNames(paths.resolveBinary ?? resolveGlobalBinary, CODEX_HOOK_BINARY)) return idle;
250
+ return reapplyCodexSettings(void 0, paths);
251
+ } catch {
252
+ return idle;
253
+ }
254
+ };
244
255
  var reapplyGeminiSettings = (apiKey, paths = {}) => {
245
256
  const settingsPath = paths.settingsPath ?? join(homedir(), ".gemini", "settings.json");
246
257
  const settings = readJson(settingsPath);
@@ -300,7 +311,13 @@ var reapplyAllAgents = (apiKey) => {
300
311
  };
301
312
 
302
313
  export {
314
+ reapplyClaudeSettings,
303
315
  reapplyClaudeForVersion,
304
316
  inspectConfiguredManagedProviders,
317
+ reapplyCodexSettings,
318
+ repairCodexHolds,
319
+ reapplyGeminiSettings,
320
+ reapplyCursorPlugin,
321
+ reapplyVsCodePlugin,
305
322
  reapplyAllAgents
306
323
  };
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  resolvePathBinary
3
- } from "./chunk-XL3RKJYQ.js";
3
+ } from "./chunk-LTPQKBJH.js";
4
4
 
5
5
  // src/hermes-config.ts
6
6
  import { execFileSync } from "child_process";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  readConfigFileKey
3
- } from "./chunk-SJLGOVC6.js";
3
+ } from "./chunk-DOYARIYB.js";
4
4
 
5
5
  // src/bell/fleet.ts
6
6
  import { closeSync, constants, futimesSync, lstatSync, mkdirSync, openSync, readdirSync, unlinkSync } from "fs";