@pushary/agent-hooks 1.9.65 → 1.9.67

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 (129) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/data/SKILL.md +1 -1
  3. package/data/cursor-plugin/skills/pushary/SKILL.md +1 -1
  4. package/data/vscode-plugin/skills/pushary/SKILL.md +1 -1
  5. package/dist/{api-32B3XLJR.js → api-3WIPD5GC.js} +12 -10
  6. package/dist/bin/pushary-bell-hook.js +25 -27
  7. package/dist/bin/pushary-bell.js +10 -10
  8. package/dist/bin/pushary-claude.js +26 -28
  9. package/dist/bin/pushary-clean.js +74 -77
  10. package/dist/bin/pushary-codex-bridge.js +21 -22
  11. package/dist/bin/pushary-codex-hook.js +30 -32
  12. package/dist/bin/pushary-codex-session.js +8 -8
  13. package/dist/bin/pushary-codex.js +13 -13
  14. package/dist/bin/pushary-connect.js +28 -28
  15. package/dist/bin/pushary-cowork.js +5 -5
  16. package/dist/bin/pushary-daemon.js +40 -41
  17. package/dist/bin/pushary-disconnect.js +25 -26
  18. package/dist/bin/pushary-doctor.js +118 -76
  19. package/dist/bin/pushary-elicitation-hook.js +25 -27
  20. package/dist/bin/pushary-gemini-bridge.js +15 -16
  21. package/dist/bin/pushary-gemini-hook.js +27 -29
  22. package/dist/bin/pushary-hook.js +29 -31
  23. package/dist/bin/pushary-login.js +20 -19
  24. package/dist/bin/pushary-logout.js +26 -28
  25. package/dist/bin/pushary-mcp.js +11 -11
  26. package/dist/bin/pushary-mode.js +16 -15
  27. package/dist/bin/pushary-notification-hook.js +24 -26
  28. package/dist/bin/pushary-opencode-hook.js +26 -28
  29. package/dist/bin/pushary-permission-denied-hook.js +29 -31
  30. package/dist/bin/pushary-permission-hook.js +32 -31
  31. package/dist/bin/pushary-post-hook.js +24 -26
  32. package/dist/bin/pushary-prompt-hook.js +24 -26
  33. package/dist/bin/pushary-session-end-hook.js +26 -28
  34. package/dist/bin/pushary-session-start-hook.js +34 -35
  35. package/dist/bin/pushary-setup.js +111 -99
  36. package/dist/bin/pushary-stats.js +5 -5
  37. package/dist/bin/pushary-status.js +37 -36
  38. package/dist/bin/pushary-stop-hook.js +24 -26
  39. package/dist/bin/pushary-stopfailure-hook.js +24 -26
  40. package/dist/bin/pushary-suggestions.js +15 -14
  41. package/dist/bin/pushary-transcript-register.js +16 -18
  42. package/dist/bin/pushary-transcripts.js +18 -17
  43. package/dist/bin/pushary-upgrade.js +45 -39
  44. package/dist/bin/pushary-wait.js +26 -20
  45. package/dist/bin/pushary.js +7 -7
  46. package/dist/{chunk-Y64JGYTB.js → chunk-32OSSYGZ.js} +1 -1
  47. package/dist/{chunk-UHS5MMAI.js → chunk-3THDNNVL.js} +3 -3
  48. package/dist/{chunk-OA6EADFT.js → chunk-564IPPBL.js} +3 -3
  49. package/dist/chunk-5MOJCOCF.js +25 -0
  50. package/dist/{chunk-RRHHWYPX.js → chunk-6H4E5UBA.js} +6 -6
  51. package/dist/{chunk-UM2X7HL6.js → chunk-6LXXKMV6.js} +42 -16
  52. package/dist/chunk-76SWHCAR.js +299 -0
  53. package/dist/{chunk-AN7C3CHH.js → chunk-77L2TNJW.js} +1 -1
  54. package/dist/{chunk-XJIIU36L.js → chunk-7YJSDXLM.js} +1 -1
  55. package/dist/{chunk-XZCJC3QO.js → chunk-B3V2WOJX.js} +285 -50
  56. package/dist/{chunk-RABLE5R6.js → chunk-BBFGDYYI.js} +1 -1
  57. package/dist/{chunk-GTBWWCCM.js → chunk-BFF3ABFV.js} +115 -9
  58. package/dist/{chunk-NOH3EJFH.js → chunk-C4EDAHXI.js} +1 -1
  59. package/dist/{chunk-ITYKN2K3.js → chunk-CHYBOTQN.js} +2 -2
  60. package/dist/{chunk-FFSIYBBU.js → chunk-CJZLIAJF.js} +27 -10
  61. package/dist/{chunk-UMTMFDL2.js → chunk-CPLQ5OHX.js} +1 -1
  62. package/dist/{chunk-DTVHZDYM.js → chunk-D3U6CN7T.js} +26 -12
  63. package/dist/{chunk-ICFHMJTW.js → chunk-D7Z4USSB.js} +27 -8
  64. package/dist/{chunk-DXMUJTYF.js → chunk-DP6LU6BB.js} +2 -2
  65. package/dist/{chunk-GA257T2E.js → chunk-DXCHIKAN.js} +8 -8
  66. package/dist/{chunk-PP7FC3Y7.js → chunk-EQR6JSFE.js} +9 -7
  67. package/dist/{chunk-Z3M627TQ.js → chunk-FONWVVDD.js} +1 -1
  68. package/dist/{chunk-3KXBRSL3.js → chunk-GMHQYX3D.js} +1 -1
  69. package/dist/{chunk-WY6YAMZW.js → chunk-ICQCQAUO.js} +5 -4
  70. package/dist/{chunk-CZ65D6Q5.js → chunk-IPG3RS23.js} +2 -2
  71. package/dist/{chunk-JCWHOB36.js → chunk-IRV2MNMA.js} +53 -3
  72. package/dist/{chunk-HIYLAC3C.js → chunk-IVB5O6NY.js} +3 -3
  73. package/dist/{chunk-DM24VZOI.js → chunk-IXTVUINH.js} +3 -3
  74. package/dist/{chunk-BEQ3MEE3.js → chunk-JBRMIADM.js} +2 -2
  75. package/dist/{chunk-CU2PXLZ6.js → chunk-JHB5SR5B.js} +4 -4
  76. package/dist/{chunk-ED2SWTPC.js → chunk-JOBSKQ2Z.js} +4 -4
  77. package/dist/{chunk-W5L2ZLHE.js → chunk-JOD2325S.js} +3 -3
  78. package/dist/{chunk-CSKABVSH.js → chunk-JSPCGPSH.js} +3 -3
  79. package/dist/{chunk-R4FL5JHK.js → chunk-KE4TSJUD.js} +4 -4
  80. package/dist/{chunk-3LF7T3NW.js → chunk-KIXRHNCW.js} +6 -6
  81. package/dist/{chunk-BKCI53W6.js → chunk-KNUYUQBP.js} +4 -4
  82. package/dist/{chunk-4LUWNBGD.js → chunk-KTLE7NIK.js} +23 -8
  83. package/dist/{chunk-ADXHPGBZ.js → chunk-LJHDUKBB.js} +6 -6
  84. package/dist/{chunk-U2P2LQ5B.js → chunk-LS42NV6E.js} +15 -6
  85. package/dist/{chunk-QNZ6G3LY.js → chunk-LYTWK5TF.js} +5 -5
  86. package/dist/{chunk-2VADJ6JH.js → chunk-METUT7V4.js} +2 -2
  87. package/dist/{chunk-P5HZEGT7.js → chunk-NSS2AIGI.js} +6 -6
  88. package/dist/{chunk-QSW3HAKI.js → chunk-NWN6GGJL.js} +61 -2
  89. package/dist/{chunk-7ILQX47W.js → chunk-P4RXSZDT.js} +3 -3
  90. package/dist/{chunk-FWS2KLV2.js → chunk-PIBWX3EF.js} +2 -2
  91. package/dist/{chunk-F56GTJFG.js → chunk-PO2P3XDC.js} +5 -5
  92. package/dist/{chunk-EEVE7H5Z.js → chunk-R2BZH2NK.js} +4 -4
  93. package/dist/{chunk-I2UBF2SS.js → chunk-RO2B4YBF.js} +1 -1
  94. package/dist/{chunk-MNKIR2ZB.js → chunk-RQOT6QL3.js} +67 -6
  95. package/dist/{chunk-Q2BD4IBX.js → chunk-SB6TYV4M.js} +11 -13
  96. package/dist/{chunk-ATPJGHUP.js → chunk-TI4JYPGQ.js} +7 -3
  97. package/dist/{chunk-LMC5UPL7.js → chunk-UC2J5RR4.js} +1 -1
  98. package/dist/{chunk-IZVEBBDH.js → chunk-UQIPWXBL.js} +3 -3
  99. package/dist/{chunk-VHKK3US3.js → chunk-UXN73YAE.js} +3 -3
  100. package/dist/{chunk-WOHP7EL6.js → chunk-VEYFXRZ6.js} +7 -6
  101. package/dist/{chunk-2Z7I7DHT.js → chunk-VHMZPN3N.js} +1 -1
  102. package/dist/{chunk-HVOQUPSO.js → chunk-VKZ3RJ5U.js} +1 -1
  103. package/dist/chunk-VVIQWGS7.js +2298 -0
  104. package/dist/{chunk-SCMEVDAR.js → chunk-WJLHZ6E6.js} +3 -3
  105. package/dist/{chunk-BAGBCSXA.js → chunk-X2MTVD4W.js} +1 -1
  106. package/dist/{chunk-3UNAPXED.js → chunk-XFDWLDHG.js} +9 -9
  107. package/dist/{chunk-6CZX6YOE.js → chunk-XNUWKKGS.js} +3 -3
  108. package/dist/{chunk-NUEPJ5W6.js → chunk-XZKSUJ55.js} +3 -5
  109. package/dist/{chunk-HDGB6SQ4.js → chunk-Y4MRHAE5.js} +2 -2
  110. package/dist/{chunk-SZBMTOUZ.js → chunk-Y6ZN454E.js} +1 -1
  111. package/dist/{chunk-TK4JU2F7.js → chunk-YSD4SMHE.js} +1 -1
  112. package/dist/{chunk-SROWUG5W.js → chunk-ZK5WYAMI.js} +1 -1
  113. package/dist/{chunk-JFKMPLNF.js → chunk-ZPIYJLQ3.js} +2 -2
  114. package/dist/{codex-app-cards-switch-HK4RMOBI.js → codex-app-cards-switch-25TDTMUZ.js} +11 -11
  115. package/dist/{codex-desktop-launch-OCO6N7SA.js → codex-desktop-launch-JDKUCD2Y.js} +1 -1
  116. package/dist/{codex-session-launch-ETZQTVFS.js → codex-session-launch-6N35AAEO.js} +18 -19
  117. package/dist/{config-YY54G3CC.js → config-OBGYURFX.js} +3 -3
  118. package/dist/{global-upgrade-5DN5G3ZQ.js → global-upgrade-2QNQD5OM.js} +10 -10
  119. package/dist/{identity-EGL6HAWG.js → identity-CUPX2U7Q.js} +4 -4
  120. package/dist/{live-capabilities-WU3FYJJG.js → live-capabilities-CUEIWMSM.js} +5 -5
  121. package/dist/{paths-ZG6KDM6Y.js → paths-BHLBZ6FY.js} +7 -3
  122. package/dist/{reapply-AQLNCCTE.js → reapply-4GXLWZ5M.js} +27 -27
  123. package/dist/src/index.js +30 -20
  124. package/dist/{upgrade-lock-BIRXIKHJ.js → upgrade-lock-RDKTAHWR.js} +4 -4
  125. package/dist/{usage-43C2DHDU.js → usage-KX2HN47E.js} +3 -3
  126. package/package.json +1 -1
  127. package/dist/chunk-ANP3DIST.js +0 -118
  128. package/dist/chunk-ONBL2GCU.js +0 -615
  129. package/dist/chunk-TJUJAK5L.js +0 -62
@@ -8,20 +8,20 @@ import {
8
8
  everyProgramFileIsThere,
9
9
  installedCodexApp,
10
10
  shellQuoted
11
- } from "./chunk-JFKMPLNF.js";
11
+ } from "./chunk-ZPIYJLQ3.js";
12
12
  import {
13
13
  writeHuman
14
14
  } from "./chunk-QCGNGEGE.js";
15
15
  import {
16
16
  xml
17
- } from "./chunk-ADXHPGBZ.js";
17
+ } from "./chunk-LJHDUKBB.js";
18
18
  import {
19
19
  CODEX_APP_LAUNCH_AT_LOGIN_VERB,
20
20
  CODEX_APP_LAUNCH_VARIABLE
21
21
  } from "./chunk-E46NE7FB.js";
22
22
  import {
23
23
  writeFileAtomic
24
- } from "./chunk-SCMEVDAR.js";
24
+ } from "./chunk-WJLHZ6E6.js";
25
25
 
26
26
  // src/codex-app-cards-switch.ts
27
27
  import { execFileSync as execFileSync2 } from "child_process";
@@ -0,0 +1,25 @@
1
+ import {
2
+ absoluteUrl
3
+ } from "./chunk-3THDNNVL.js";
4
+ import {
5
+ KEY_SCOPE
6
+ } from "./chunk-RQOT6QL3.js";
7
+
8
+ // src/cli/approval-settings-pages.ts
9
+ var API_KEYS_PATH = "/dashboard/agent/settings";
10
+ var APPROVAL_RULES_PATH = "/dashboard/agent/policies";
11
+ var PUSH_WINDOW_COMMAND = "pushary wait <seconds>";
12
+ var PUSH_WINDOW_RESET_COMMAND = "pushary wait clear";
13
+ var keyScopeOf = (check) => check?.kind === "ok" ? check.identity.keyScope : null;
14
+ var canChangeApprovalSettings = (keyScope) => keyScope === KEY_SCOPE.full_access;
15
+ var pushWindowChangeStep = (keyScope, baseUrl) => canChangeApprovalSettings(keyScope) ? PUSH_WINDOW_COMMAND : absoluteUrl(baseUrl, APPROVAL_RULES_PATH);
16
+
17
+ export {
18
+ API_KEYS_PATH,
19
+ APPROVAL_RULES_PATH,
20
+ PUSH_WINDOW_COMMAND,
21
+ PUSH_WINDOW_RESET_COMMAND,
22
+ keyScopeOf,
23
+ canChangeApprovalSettings,
24
+ pushWindowChangeStep
25
+ };
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  announceSubscriptionRequired,
3
3
  isDeferAnswer
4
- } from "./chunk-UMTMFDL2.js";
4
+ } from "./chunk-CPLQ5OHX.js";
5
5
  import {
6
6
  AUTO_DECISION_WINDOW_MS,
7
7
  DEFAULT_SESSION,
@@ -10,19 +10,19 @@ import {
10
10
  permissionDeniedAction,
11
11
  releaseThrottle,
12
12
  reportEvent
13
- } from "./chunk-3UNAPXED.js";
13
+ } from "./chunk-XFDWLDHG.js";
14
14
  import {
15
15
  askUser,
16
16
  isDecisionStopped,
17
17
  isQuestionLimitRefusal,
18
18
  waitForQuestion
19
- } from "./chunk-DTVHZDYM.js";
19
+ } from "./chunk-D3U6CN7T.js";
20
20
  import {
21
21
  SubscriptionRequiredError
22
- } from "./chunk-BKCI53W6.js";
22
+ } from "./chunk-KNUYUQBP.js";
23
23
  import {
24
24
  getMachineId
25
- } from "./chunk-FWS2KLV2.js";
25
+ } from "./chunk-PIBWX3EF.js";
26
26
  import {
27
27
  ASK_CONTEXT_MAX,
28
28
  agentQuestionContext,
@@ -34,7 +34,7 @@ import {
34
34
  questionsShownAsASet,
35
35
  shouldDeferToNativeMode,
36
36
  wireVerdictFor
37
- } from "./chunk-MNKIR2ZB.js";
37
+ } from "./chunk-RQOT6QL3.js";
38
38
 
39
39
  // src/machine-decision-audit.ts
40
40
  import { basename } from "path";
@@ -1,13 +1,20 @@
1
1
  import {
2
2
  ensureCodexTrustsPusharyHooks
3
- } from "./chunk-HDGB6SQ4.js";
3
+ } from "./chunk-Y4MRHAE5.js";
4
+ import {
5
+ hermesReceiptPaths,
6
+ hermesSetupPredatesThePluginFolder,
7
+ installHermesPlugin,
8
+ refreshHermesPluginFiles,
9
+ shippedHermesPlugin
10
+ } from "./chunk-VVIQWGS7.js";
4
11
  import {
5
12
  fxIgnoresInstructionsFile,
6
13
  fxInstructionsBody,
7
14
  fxServerHolder,
8
15
  macAppFxBridge,
9
16
  refreshClineKey
10
- } from "./chunk-QNZ6G3LY.js";
17
+ } from "./chunk-LYTWK5TF.js";
11
18
  import {
12
19
  OPENCODE_HOOK_BINARY,
13
20
  agentHookOwner,
@@ -19,30 +26,28 @@ import {
19
26
  openCodePluginDir,
20
27
  openCodeWired,
21
28
  withOpenCodeAskRules
22
- } from "./chunk-P5HZEGT7.js";
29
+ } from "./chunk-NSS2AIGI.js";
23
30
  import {
24
31
  detectClaudeVersion
25
- } from "./chunk-HIYLAC3C.js";
32
+ } from "./chunk-IVB5O6NY.js";
26
33
  import {
27
34
  addCompatibleHooks,
28
35
  compatibleConfigFile,
29
36
  compatibleInstructionsFile,
30
37
  hasCompatibleHooks
31
- } from "./chunk-WY6YAMZW.js";
38
+ } from "./chunk-ICQCQAUO.js";
32
39
  import {
40
+ GEMINI_HOOK_BINARY,
41
+ addGeminiHooks,
42
+ addGeminiMcpServer,
33
43
  claudeUnprovenHolds,
34
44
  claudeWired,
35
45
  codexUnprovenHolds,
36
46
  codexWired,
37
47
  geminiWired,
48
+ hasGeminiHooks,
38
49
  planHooks
39
- } from "./chunk-GTBWWCCM.js";
40
- import {
41
- GEMINI_HOOK_BINARY,
42
- addGeminiHooks,
43
- addGeminiMcpServer,
44
- hasGeminiHooks
45
- } from "./chunk-ANP3DIST.js";
50
+ } from "./chunk-BFF3ABFV.js";
46
51
  import {
47
52
  CLAUDE_HOOK_BINARY,
48
53
  CLAUDE_HOOK_EVENTS,
@@ -87,7 +92,7 @@ import {
87
92
  renderAgentInstructions,
88
93
  supportedClaudeEvents,
89
94
  vscodePluginMcp
90
- } from "./chunk-ATPJGHUP.js";
95
+ } from "./chunk-TI4JYPGQ.js";
91
96
  import {
92
97
  formatVersion,
93
98
  readJsonSafe,
@@ -95,12 +100,12 @@ import {
95
100
  resolveGlobalBinary,
96
101
  writeFileAtomic,
97
102
  writeJsonAtomic
98
- } from "./chunk-SCMEVDAR.js";
103
+ } from "./chunk-WJLHZ6E6.js";
99
104
  import {
100
105
  HOOK_COMPATIBLE_PROFILES,
101
106
  isCompatibleAgentType,
102
107
  isRecord
103
- } from "./chunk-MNKIR2ZB.js";
108
+ } from "./chunk-RQOT6QL3.js";
104
109
 
105
110
  // src/reapply.ts
106
111
  import { dirname, join } from "path";
@@ -430,7 +435,8 @@ var SETUP_CHOICE_BY_REAPPLY_ID = {
430
435
  vscode: "vscode",
431
436
  opencode: "opencode",
432
437
  fx: "fx",
433
- cline: "cline"
438
+ cline: "cline",
439
+ hermes: "hermes"
434
440
  };
435
441
  var setupChoiceOf = (id) => isCompatibleAgentType(id) ? id : SETUP_CHOICE_BY_REAPPLY_ID[id];
436
442
  var reapplyHookOwner = (id, appAvailable) => agentHookOwner(setupChoiceOf(id), appAvailable);
@@ -439,6 +445,25 @@ var fxHeldByApp = (appAvailable) => appAvailable && fxHolder() === "app";
439
445
  var reapplyHolder = (id, appAvailable) => (id === "fx" ? fxHeldByApp(appAvailable) : hooksHeldByApp(id, appAvailable)) ? "app" : "cli";
440
446
  var KEPT_FOR_APP = { configured: true, reapplied: false, hooks: false, mcp: false };
441
447
  var FAILED_REAPPLY = { configured: false, reapplied: false, hooks: false, mcp: false, failed: true };
448
+ var HERMES_PLUGIN_WRITTEN = { configured: true, reapplied: true, hooks: false, mcp: false, pluginFiles: true, hermesRestartRequired: true };
449
+ var HERMES_REFRESH_RESULTS = {
450
+ refreshed: HERMES_PLUGIN_WRITTEN,
451
+ current: { configured: true, reapplied: false, hooks: false, mcp: false },
452
+ not_ours: { configured: false, reapplied: false, hooks: false, mcp: false },
453
+ failed: FAILED_REAPPLY
454
+ };
455
+ var HERMES_MIGRATION_RESULTS = {
456
+ installed: HERMES_PLUGIN_WRITTEN,
457
+ no_launcher: { configured: false, reapplied: false, hooks: false, mcp: false },
458
+ failed: FAILED_REAPPLY
459
+ };
460
+ var reapplyHermesPlugin = () => {
461
+ const plugin = shippedHermesPlugin();
462
+ if (hermesSetupPredatesThePluginFolder()) {
463
+ return HERMES_MIGRATION_RESULTS[installHermesPlugin({ installedBy: "cli", plugin, receipts: hermesReceiptPaths() }).kind];
464
+ }
465
+ return HERMES_REFRESH_RESULTS[refreshHermesPluginFiles("cli", plugin)];
466
+ };
442
467
  var noInstructionBlock = () => null;
443
468
  var reapplyAllAgents = (apiKey, options = {}) => {
444
469
  const appAvailable = (options.appAvailable ?? macAppBridgeAvailable)();
@@ -466,6 +491,7 @@ var reapplyAllAgents = (apiKey, options = {}) => {
466
491
  },
467
492
  { id: "fx", label: "fx", run: inspectFxWiring, instructions: fxInstructionTarget },
468
493
  { id: "cline", label: CLINE_LABEL, run: () => reapplyClineSettings(apiKey), instructions: clineInstructionTarget },
494
+ { id: "hermes", label: "Hermes", run: reapplyHermesPlugin, instructions: noInstructionBlock },
469
495
  ...HOOK_COMPATIBLE_PROFILES.map((profile) => ({
470
496
  id: profile.agentType,
471
497
  label: profile.label,
@@ -0,0 +1,299 @@
1
+ import {
2
+ openCodeConfigDirFrom
3
+ } from "./chunk-NSS2AIGI.js";
4
+ import {
5
+ nativeClaudeInstallPath
6
+ } from "./chunk-BBFGDYYI.js";
7
+ import {
8
+ grokHome
9
+ } from "./chunk-ICQCQAUO.js";
10
+ import {
11
+ claudeConfigDir,
12
+ clineDataDirectory,
13
+ codexHomeFrom
14
+ } from "./chunk-TI4JYPGQ.js";
15
+ import {
16
+ HOOK_COMPATIBLE_PROFILES
17
+ } from "./chunk-RQOT6QL3.js";
18
+
19
+ // src/vscode-config.ts
20
+ import { homedir } from "os";
21
+ import { join } from "path";
22
+ var PLUGIN_LOCATIONS_KEY = "chat.pluginLocations";
23
+ var blankJsonComments = (text) => {
24
+ let out = "";
25
+ let i = 0;
26
+ let inString = false;
27
+ let escaped = false;
28
+ while (i < text.length) {
29
+ const ch = text[i];
30
+ if (inString) {
31
+ out += ch;
32
+ if (escaped) escaped = false;
33
+ else if (ch === "\\") escaped = true;
34
+ else if (ch === '"') inString = false;
35
+ i += 1;
36
+ continue;
37
+ }
38
+ if (ch === '"') {
39
+ inString = true;
40
+ out += ch;
41
+ i += 1;
42
+ continue;
43
+ }
44
+ if (ch === "/" && text[i + 1] === "/") {
45
+ while (i < text.length && text[i] !== "\n") {
46
+ out += " ";
47
+ i += 1;
48
+ }
49
+ continue;
50
+ }
51
+ if (ch === "/" && text[i + 1] === "*") {
52
+ const end = text.indexOf("*/", i + 2);
53
+ const stop = end === -1 ? text.length : end + 2;
54
+ while (i < stop) {
55
+ out += text[i] === "\n" ? "\n" : " ";
56
+ i += 1;
57
+ }
58
+ continue;
59
+ }
60
+ out += ch;
61
+ i += 1;
62
+ }
63
+ return out;
64
+ };
65
+ var asRecord = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
66
+ var parseJsonc = (text) => {
67
+ const blanked = blankJsonComments(text);
68
+ const withoutTrailingCommas = blanked.replace(/,(\s*[}\]])/g, "$1");
69
+ try {
70
+ return asRecord(JSON.parse(withoutTrailingCommas));
71
+ } catch {
72
+ return void 0;
73
+ }
74
+ };
75
+ var rootBraceIndex = (text) => blankJsonComments(text).indexOf("{");
76
+ var detectIndent = (text) => {
77
+ const match = /\n([ \t]+)"/.exec(text);
78
+ return match?.[1] ?? " ";
79
+ };
80
+ var freshDocument = (pluginDir, indent = " ") => `${JSON.stringify({ [PLUGIN_LOCATIONS_KEY]: { [pluginDir]: true } }, null, indent)}
81
+ `;
82
+ var registerPluginLocation = (existing, pluginDir) => {
83
+ if (existing === null || existing.trim() === "") {
84
+ return { kind: "created", content: freshDocument(pluginDir) };
85
+ }
86
+ const indent = detectIndent(existing);
87
+ try {
88
+ const parsed2 = asRecord(JSON.parse(existing));
89
+ if (parsed2) {
90
+ const locations2 = asRecord(parsed2[PLUGIN_LOCATIONS_KEY]) ?? {};
91
+ if (locations2[pluginDir] === true) return { kind: "already" };
92
+ parsed2[PLUGIN_LOCATIONS_KEY] = { ...locations2, [pluginDir]: true };
93
+ return { kind: "merged", content: `${JSON.stringify(parsed2, null, indent)}
94
+ ` };
95
+ }
96
+ } catch {
97
+ }
98
+ const parsed = parseJsonc(existing);
99
+ if (!parsed) return { kind: "manual" };
100
+ const locations = asRecord(parsed[PLUGIN_LOCATIONS_KEY]);
101
+ if (locations?.[pluginDir] === true) return { kind: "already" };
102
+ if (parsed[PLUGIN_LOCATIONS_KEY] !== void 0) return { kind: "manual" };
103
+ const brace = rootBraceIndex(existing);
104
+ if (brace === -1) return { kind: "manual" };
105
+ const needsComma = Object.keys(parsed).length > 0;
106
+ const line = `
107
+ ${indent}${JSON.stringify(PLUGIN_LOCATIONS_KEY)}: { ${JSON.stringify(pluginDir)}: true }${needsComma ? "," : ""}`;
108
+ return {
109
+ kind: "inserted",
110
+ content: `${existing.slice(0, brace + 1)}${line}${existing.slice(brace + 1)}`
111
+ };
112
+ };
113
+ var unregisterPluginLocation = (existing, pluginDir) => {
114
+ if (existing === null || existing.trim() === "") return { kind: "absent" };
115
+ const indent = detectIndent(existing);
116
+ try {
117
+ const parsed2 = asRecord(JSON.parse(existing));
118
+ if (parsed2) {
119
+ const locations2 = asRecord(parsed2[PLUGIN_LOCATIONS_KEY]);
120
+ if (!locations2 || !(pluginDir in locations2)) return { kind: "absent" };
121
+ const remaining = { ...locations2 };
122
+ delete remaining[pluginDir];
123
+ if (Object.keys(remaining).length === 0) delete parsed2[PLUGIN_LOCATIONS_KEY];
124
+ else parsed2[PLUGIN_LOCATIONS_KEY] = remaining;
125
+ return { kind: "removed", content: `${JSON.stringify(parsed2, null, indent)}
126
+ ` };
127
+ }
128
+ } catch {
129
+ }
130
+ const parsed = parseJsonc(existing);
131
+ if (!parsed) return { kind: "manual" };
132
+ const locations = asRecord(parsed[PLUGIN_LOCATIONS_KEY]);
133
+ if (!locations || !(pluginDir in locations)) return { kind: "absent" };
134
+ const line = new RegExp(
135
+ `\\n[ \\t]*${escapeRegExp(JSON.stringify(PLUGIN_LOCATIONS_KEY))}\\s*:\\s*\\{\\s*${escapeRegExp(JSON.stringify(pluginDir))}\\s*:\\s*true\\s*\\},?`
136
+ );
137
+ if (!line.test(existing)) return { kind: "manual" };
138
+ return { kind: "removed", content: existing.replace(line, "") };
139
+ };
140
+ var escapeRegExp = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
141
+ var isPluginRegistered = (existing, pluginDir) => {
142
+ if (!existing) return false;
143
+ const parsed = parseJsonc(existing);
144
+ return asRecord(parsed?.[PLUGIN_LOCATIONS_KEY])?.[pluginDir] === true;
145
+ };
146
+ var pluginLocationSnippet = (pluginDir) => `"${PLUGIN_LOCATIONS_KEY}": {
147
+ ${JSON.stringify(pluginDir)}: true
148
+ }`;
149
+ var VSCODE_PRODUCTS = ["Code", "Code - Insiders"];
150
+ var settingsRoot = (home, platform) => {
151
+ if (platform === "darwin") return join(home, "Library", "Application Support");
152
+ if (platform === "win32") return process.env.APPDATA?.trim() || join(home, "AppData", "Roaming");
153
+ return process.env.XDG_CONFIG_HOME?.trim() || join(home, ".config");
154
+ };
155
+ var vscodeSettingsCandidates = (home = homedir(), platform = process.platform) => {
156
+ const root = settingsRoot(home, platform);
157
+ return VSCODE_PRODUCTS.map((product) => join(root, product, "User", "settings.json"));
158
+ };
159
+ var vscodeSettingsTargets = (exists, home, platform) => {
160
+ const candidates = vscodeSettingsCandidates(home, platform);
161
+ const present = candidates.filter((path) => exists(path));
162
+ return present.length > 0 ? present : candidates.slice(0, 1);
163
+ };
164
+
165
+ // src/setup/detect.ts
166
+ import { execSync } from "child_process";
167
+ import { existsSync } from "fs";
168
+ import { homedir as homedir2 } from "os";
169
+ import { dirname, join as join2 } from "path";
170
+ var whichCommand = () => process.platform === "win32" ? "where" : "which";
171
+ var binaryOnPath = (binary) => {
172
+ try {
173
+ execSync(`${whichCommand()} ${binary}`, { stdio: "ignore", timeout: 5e3 });
174
+ return true;
175
+ } catch {
176
+ return false;
177
+ }
178
+ };
179
+ var defaultDeps = { onPath: binaryOnPath, exists: existsSync };
180
+ var detectAgent = (probe, deps = defaultDeps) => {
181
+ const checked = [];
182
+ if (probe.binary) {
183
+ checked.push(`${whichCommand()} ${probe.binary}`);
184
+ if (deps.onPath(probe.binary)) {
185
+ return { kind: "installed", evidence: `${probe.binary} on PATH`, strong: true };
186
+ }
187
+ }
188
+ for (const path of probe.paths) {
189
+ checked.push(path);
190
+ if (deps.exists(path)) return { kind: "installed", evidence: path, strong: true };
191
+ }
192
+ for (const marker of probe.configMarkers) {
193
+ checked.push(marker);
194
+ if (deps.exists(marker)) return { kind: "installed", evidence: marker, strong: false };
195
+ }
196
+ if (probe.binary === null && probe.paths.length === 0 && probe.configMarkers.length === 0) {
197
+ return { kind: "unknown" };
198
+ }
199
+ return { kind: "not-found", checked };
200
+ };
201
+ var agentProbes = (home = homedir2(), platform = process.platform) => ({
202
+ claude_code: {
203
+ binary: "claude",
204
+ // The native installer puts it here, outside a default non-login PATH.
205
+ paths: [nativeClaudeInstallPath(home, platform)],
206
+ configMarkers: [claudeConfigDir(home)]
207
+ },
208
+ codex: {
209
+ binary: "codex",
210
+ paths: [],
211
+ configMarkers: [codexHomeFrom(home)]
212
+ },
213
+ gemini_cli: {
214
+ binary: "gemini",
215
+ paths: [],
216
+ configMarkers: [join2(home, ".gemini")]
217
+ },
218
+ hermes: {
219
+ binary: "hermes",
220
+ paths: [],
221
+ configMarkers: [join2(home, ".hermes")]
222
+ },
223
+ cursor: {
224
+ // GUI editor. `cursor` on PATH only exists if the user ran "Install 'cursor'
225
+ // command in PATH" from the command palette, which most never do.
226
+ binary: "cursor",
227
+ paths: platform === "darwin" ? ["/Applications/Cursor.app"] : platform === "win32" ? [join2(process.env.LOCALAPPDATA ?? join2(home, "AppData", "Local"), "Programs", "cursor")] : ["/usr/share/cursor", join2(home, ".local", "share", "cursor")],
228
+ configMarkers: [join2(home, ".cursor")]
229
+ },
230
+ vscode: {
231
+ // Like Cursor, a GUI editor: `code` on PATH only exists if the user ran
232
+ // "Shell Command: Install 'code' command in PATH", which on macOS is opt-in.
233
+ binary: "code",
234
+ paths: platform === "darwin" ? ["/Applications/Visual Studio Code.app"] : platform === "win32" ? [join2(process.env.LOCALAPPDATA ?? join2(home, "AppData", "Local"), "Programs", "Microsoft VS Code")] : ["/usr/share/code", join2(home, ".local", "share", "code")],
235
+ // The settings directory rather than ~/.vscode, because the settings
236
+ // directory is what this installer actually writes to. Insiders counts:
237
+ // someone running only Insiders still has VS Code.
238
+ configMarkers: vscodeSettingsCandidates(home).map((path) => dirname(path))
239
+ },
240
+ opencode: {
241
+ binary: "opencode",
242
+ paths: [],
243
+ // Resolved rather than spelled: OpenCode honours OPENCODE_CONFIG_DIR and then
244
+ // XDG_CONFIG_HOME, and `~/.opencode` — the obvious guess — is a path it has
245
+ // never used.
246
+ configMarkers: [openCodeConfigDirFrom(home)]
247
+ },
248
+ fx: {
249
+ binary: null,
250
+ paths: [join2(home, ".fx", "settings.json"), join2(home, ".fx", "sessions"), join2(home, ".fx", "history.jsonl")],
251
+ configMarkers: []
252
+ },
253
+ cline: {
254
+ binary: "cline",
255
+ paths: [clineDataDirectory({ home, environment: process.env })],
256
+ configMarkers: []
257
+ },
258
+ ...compatibleProbes(),
259
+ grok: { binary: "grok", paths: [join2(home, ".local", "bin", "grok")], configMarkers: [grokHome(home)] },
260
+ // "Other" is a set of printed instructions for any MCP or HTTP client. There is
261
+ // nothing to detect, and it must never be auto-selected.
262
+ custom: { binary: null, paths: [], configMarkers: [] }
263
+ });
264
+ var compatibleProbes = () => HOOK_COMPATIBLE_PROFILES.reduce((probes, profile) => ({
265
+ ...probes,
266
+ [profile.agentType]: {
267
+ binary: profile.binary,
268
+ paths: [],
269
+ configMarkers: []
270
+ }
271
+ }), {});
272
+ var detectAllAgents = (deps = defaultDeps, home) => {
273
+ const probes = agentProbes(home);
274
+ return Object.keys(probes).map((agent) => ({
275
+ agent,
276
+ result: detectAgent(probes[agent], deps)
277
+ }));
278
+ };
279
+ var isDetected = (result) => result.kind === "installed";
280
+ var shortenHome = (path, home = homedir2()) => home && path.startsWith(home) ? `~${path.slice(home.length)}` : path;
281
+ var describeDetection = (result, home) => {
282
+ if (result.kind !== "installed") return null;
283
+ const evidence = shortenHome(result.evidence, home);
284
+ return result.strong ? evidence : `${evidence}, config only`;
285
+ };
286
+
287
+ export {
288
+ registerPluginLocation,
289
+ unregisterPluginLocation,
290
+ isPluginRegistered,
291
+ pluginLocationSnippet,
292
+ vscodeSettingsTargets,
293
+ detectAgent,
294
+ agentProbes,
295
+ detectAllAgents,
296
+ isDetected,
297
+ shortenHome,
298
+ describeDetection
299
+ };
@@ -3,7 +3,7 @@ import {
3
3
  } from "./chunk-KZERVKTD.js";
4
4
  import {
5
5
  renderCommandHelp
6
- } from "./chunk-BAGBCSXA.js";
6
+ } from "./chunk-X2MTVD4W.js";
7
7
  import {
8
8
  getPackageVersion
9
9
  } from "./chunk-QTOLNRET.js";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  isSafeReadOnlyCommand
3
- } from "./chunk-MNKIR2ZB.js";
3
+ } from "./chunk-RQOT6QL3.js";
4
4
 
5
5
  // src/ledger.ts
6
6
  import { appendFileSync, existsSync, mkdirSync, readFileSync, renameSync, rmSync, statSync } from "fs";