@hasna/hooks 0.1.0 → 0.2.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 (111) hide show
  1. package/LICENSE +191 -0
  2. package/README.md +41 -6
  3. package/bin/index.js +138 -102
  4. package/dist/index.js +91 -42
  5. package/hooks/hook-agentmessages/CLAUDE.md +1 -1
  6. package/hooks/hook-agentmessages/LICENSE +191 -21
  7. package/hooks/hook-agentmessages/README.md +3 -3
  8. package/hooks/hook-agentmessages/package.json +5 -5
  9. package/hooks/hook-autoformat/README.md +1 -1
  10. package/hooks/hook-autoformat/package.json +1 -1
  11. package/hooks/hook-autoformat/tsconfig.json +25 -0
  12. package/hooks/hook-autostage/README.md +1 -1
  13. package/hooks/hook-autostage/package.json +1 -1
  14. package/hooks/hook-branchprotect/README.md +2 -2
  15. package/hooks/hook-branchprotect/package.json +4 -4
  16. package/hooks/hook-branchprotect/src/cli.ts +1 -1
  17. package/hooks/hook-branchprotect/tsconfig.json +25 -0
  18. package/hooks/hook-checkbugs/LICENSE +191 -21
  19. package/hooks/hook-checkbugs/README.md +5 -5
  20. package/hooks/hook-checkbugs/package.json +4 -4
  21. package/hooks/hook-checkbugs/src/cli.ts +2 -2
  22. package/hooks/hook-checkbugs/tsconfig.json +15 -0
  23. package/hooks/hook-checkdocs/README.md +5 -5
  24. package/hooks/hook-checkdocs/package.json +4 -4
  25. package/hooks/hook-checkdocs/src/cli.ts +2 -2
  26. package/hooks/hook-checkdocs/tsconfig.json +15 -0
  27. package/hooks/hook-checkfiles/LICENSE +191 -21
  28. package/hooks/hook-checkfiles/README.md +5 -5
  29. package/hooks/hook-checkfiles/package.json +4 -4
  30. package/hooks/hook-checkfiles/src/cli.ts +2 -2
  31. package/hooks/hook-checkfiles/tsconfig.json +15 -0
  32. package/hooks/hook-checklint/LICENSE +191 -21
  33. package/hooks/hook-checklint/README.md +5 -5
  34. package/hooks/hook-checklint/package.json +4 -4
  35. package/hooks/hook-checklint/src/cli.ts +2 -2
  36. package/hooks/hook-checklint/tsconfig.json +15 -0
  37. package/hooks/hook-checkpoint/README.md +2 -2
  38. package/hooks/hook-checkpoint/package.json +4 -4
  39. package/hooks/hook-checkpoint/src/cli.ts +1 -1
  40. package/hooks/hook-checkpoint/tsconfig.json +25 -0
  41. package/hooks/hook-checksecurity/LICENSE +191 -21
  42. package/hooks/hook-checksecurity/README.md +8 -8
  43. package/hooks/hook-checksecurity/package.json +4 -4
  44. package/hooks/hook-checksecurity/src/cli.ts +2 -2
  45. package/hooks/hook-checksecurity/tsconfig.json +15 -0
  46. package/hooks/hook-checktasks/README.md +5 -5
  47. package/hooks/hook-checktasks/package.json +4 -4
  48. package/hooks/hook-checktasks/src/cli.ts +2 -2
  49. package/hooks/hook-checktasks/tsconfig.json +20 -0
  50. package/hooks/hook-checktests/LICENSE +191 -21
  51. package/hooks/hook-checktests/README.md +5 -5
  52. package/hooks/hook-checktests/package.json +4 -4
  53. package/hooks/hook-checktests/src/cli.ts +2 -2
  54. package/hooks/hook-checktests/tsconfig.json +15 -0
  55. package/hooks/hook-commandlog/README.md +1 -1
  56. package/hooks/hook-commandlog/package.json +1 -1
  57. package/hooks/hook-contextrefresh/README.md +2 -2
  58. package/hooks/hook-contextrefresh/package.json +4 -4
  59. package/hooks/hook-contextrefresh/src/cli.ts +1 -1
  60. package/hooks/hook-contextrefresh/tsconfig.json +25 -0
  61. package/hooks/hook-costwatch/README.md +1 -1
  62. package/hooks/hook-costwatch/package.json +1 -1
  63. package/hooks/hook-desktopnotify/README.md +1 -1
  64. package/hooks/hook-desktopnotify/package.json +1 -1
  65. package/hooks/hook-desktopnotify/tsconfig.json +25 -0
  66. package/hooks/hook-envsetup/README.md +1 -1
  67. package/hooks/hook-envsetup/package.json +1 -1
  68. package/hooks/hook-envsetup/tsconfig.json +25 -0
  69. package/hooks/hook-errornotify/README.md +1 -1
  70. package/hooks/hook-errornotify/package.json +1 -1
  71. package/hooks/hook-gitguard/README.md +2 -2
  72. package/hooks/hook-gitguard/package.json +4 -4
  73. package/hooks/hook-gitguard/src/cli.ts +1 -1
  74. package/hooks/hook-gitguard/tsconfig.json +25 -0
  75. package/hooks/hook-packageage/README.md +2 -2
  76. package/hooks/hook-packageage/package.json +4 -4
  77. package/hooks/hook-packageage/src/cli.ts +1 -1
  78. package/hooks/hook-packageage/tsconfig.json +25 -0
  79. package/hooks/hook-permissionguard/README.md +1 -1
  80. package/hooks/hook-permissionguard/package.json +1 -1
  81. package/hooks/hook-permissionguard/tsconfig.json +25 -0
  82. package/hooks/hook-phonenotify/README.md +2 -2
  83. package/hooks/hook-phonenotify/package.json +4 -4
  84. package/hooks/hook-phonenotify/src/cli.ts +1 -1
  85. package/hooks/hook-phonenotify/tsconfig.json +25 -0
  86. package/hooks/hook-precompact/README.md +2 -2
  87. package/hooks/hook-precompact/package.json +4 -4
  88. package/hooks/hook-precompact/src/cli.ts +1 -1
  89. package/hooks/hook-precompact/tsconfig.json +25 -0
  90. package/hooks/hook-promptguard/README.md +1 -1
  91. package/hooks/hook-promptguard/package.json +1 -1
  92. package/hooks/hook-protectfiles/README.md +1 -1
  93. package/hooks/hook-protectfiles/package.json +1 -1
  94. package/hooks/hook-protectfiles/tsconfig.json +25 -0
  95. package/hooks/hook-sessionlog/README.md +1 -1
  96. package/hooks/hook-sessionlog/package.json +1 -1
  97. package/hooks/hook-slacknotify/README.md +1 -1
  98. package/hooks/hook-slacknotify/package.json +1 -1
  99. package/hooks/hook-soundnotify/README.md +1 -1
  100. package/hooks/hook-soundnotify/package.json +1 -1
  101. package/hooks/hook-taskgate/README.md +1 -1
  102. package/hooks/hook-taskgate/package.json +1 -1
  103. package/hooks/hook-tddguard/README.md +1 -1
  104. package/hooks/hook-tddguard/package.json +1 -1
  105. package/package.json +18 -4
  106. package/.claude/settings.json +0 -24
  107. package/.npmrc.example +0 -2
  108. package/AGENTS.md +0 -54
  109. package/CLAUDE.md +0 -70
  110. package/CONTRIBUTING.md +0 -45
  111. package/hooks/hook-agentmessages/bin/cli.ts +0 -125
package/bin/index.js CHANGED
@@ -1957,7 +1957,7 @@ var init_registry = __esm(() => {
1957
1957
  {
1958
1958
  name: "checkbugs",
1959
1959
  displayName: "Check Bugs",
1960
- description: "Checks for bugs via Codex headless agent",
1960
+ description: "Checks for bugs via headless agent",
1961
1961
  version: "0.1.6",
1962
1962
  category: "Code Quality",
1963
1963
  event: "PostToolUse",
@@ -1987,7 +1987,7 @@ var init_registry = __esm(() => {
1987
1987
  {
1988
1988
  name: "checksecurity",
1989
1989
  displayName: "Check Security",
1990
- description: "Runs security checks via Claude and Codex headless agents",
1990
+ description: "Runs security checks via headless agents",
1991
1991
  version: "0.1.6",
1992
1992
  category: "Security",
1993
1993
  event: "PostToolUse",
@@ -3842,30 +3842,46 @@ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
3842
3842
  import { join, dirname } from "path";
3843
3843
  import { homedir } from "os";
3844
3844
  import { fileURLToPath } from "url";
3845
- function getSettingsPath(scope = "global") {
3845
+ function normalizeHookName(name) {
3846
+ return name.startsWith("hook-") ? name : `hook-${name}`;
3847
+ }
3848
+ function shortHookName(name) {
3849
+ return normalizeHookName(name).replace("hook-", "");
3850
+ }
3851
+ function removeHookEntries(entries, hookCommand) {
3852
+ return entries.filter((entry) => !entry.hooks?.some((h) => h.command === hookCommand));
3853
+ }
3854
+ function getTargetSettingsDir(target) {
3855
+ if (target === "gemini")
3856
+ return ".gemini";
3857
+ return ".claude";
3858
+ }
3859
+ function getSettingsPath(scope = "global", target = "claude") {
3860
+ const dir = getTargetSettingsDir(target);
3846
3861
  if (scope === "project") {
3847
- return join(process.cwd(), ".claude", "settings.json");
3862
+ return join(process.cwd(), dir, "settings.json");
3848
3863
  }
3849
- return join(homedir(), ".claude", "settings.json");
3864
+ return join(homedir(), dir, "settings.json");
3850
3865
  }
3851
3866
  function getHookPath(name) {
3852
- const hookName = name.startsWith("hook-") ? name : `hook-${name}`;
3853
- return join(HOOKS_DIR, hookName);
3867
+ return join(HOOKS_DIR, normalizeHookName(name));
3854
3868
  }
3855
3869
  function hookExists(name) {
3856
3870
  return existsSync(getHookPath(name));
3857
3871
  }
3858
- function readSettings(scope = "global") {
3859
- const path = getSettingsPath(scope);
3872
+ function readSettings(scope = "global", target = "claude") {
3873
+ const path = getSettingsPath(scope, target);
3860
3874
  try {
3861
3875
  if (existsSync(path)) {
3862
3876
  return JSON.parse(readFileSync(path, "utf-8"));
3863
3877
  }
3864
- } catch {}
3878
+ } catch (error) {
3879
+ console.warn(`[hooks] Failed to read settings at ${path}: ${error instanceof Error ? error.message : String(error)}`);
3880
+ }
3865
3881
  return {};
3866
3882
  }
3867
- function writeSettings(settings, scope = "global") {
3868
- const path = getSettingsPath(scope);
3883
+ function writeSettings(settings, scope = "global", target = "claude") {
3884
+ const path = getSettingsPath(scope, target);
3869
3885
  const dir = dirname(path);
3870
3886
  if (!existsSync(dir)) {
3871
3887
  mkdirSync(dir, { recursive: true });
@@ -3873,40 +3889,51 @@ function writeSettings(settings, scope = "global") {
3873
3889
  writeFileSync(path, JSON.stringify(settings, null, 2) + `
3874
3890
  `);
3875
3891
  }
3876
- function installHook(name, options = {}) {
3877
- const { scope = "global", overwrite = false } = options;
3878
- const hookName = name.startsWith("hook-") ? name : `hook-${name}`;
3879
- const shortName = hookName.replace("hook-", "");
3892
+ function getTargetEventName(internalEvent, target) {
3893
+ return EVENT_MAP[target]?.[internalEvent] || internalEvent;
3894
+ }
3895
+ function installForTarget(name, scope, overwrite, target) {
3896
+ const shortName = shortHookName(name);
3880
3897
  if (!hookExists(shortName)) {
3881
- return { hook: shortName, success: false, error: `Hook '${shortName}' not found` };
3898
+ return { hook: shortName, success: false, error: `Hook '${shortName}' not found`, target };
3882
3899
  }
3883
- const registered = getRegisteredHooks(scope);
3900
+ const registered = getRegisteredHooksForTarget(scope, target);
3884
3901
  if (registered.includes(shortName) && !overwrite) {
3885
- return { hook: shortName, success: false, error: "Already installed. Use --overwrite to replace.", scope };
3902
+ return { hook: shortName, success: false, error: "Already installed. Use --overwrite to replace.", scope, target };
3886
3903
  }
3887
3904
  try {
3888
- registerHook(shortName, scope);
3889
- return { hook: shortName, success: true, scope };
3905
+ registerHook(shortName, scope, target);
3906
+ return { hook: shortName, success: true, scope, target };
3890
3907
  } catch (error) {
3891
3908
  return {
3892
3909
  hook: shortName,
3893
3910
  success: false,
3894
- error: error instanceof Error ? error.message : "Unknown error"
3911
+ error: error instanceof Error ? error.message : "Unknown error",
3912
+ target
3895
3913
  };
3896
3914
  }
3897
3915
  }
3898
- function registerHook(name, scope = "global") {
3916
+ function installHook(name, options = {}) {
3917
+ const { scope = "global", overwrite = false, target = "claude" } = options;
3918
+ if (target === "all") {
3919
+ const claudeResult = installForTarget(name, scope, overwrite, "claude");
3920
+ installForTarget(name, scope, overwrite, "gemini");
3921
+ return { ...claudeResult, target: "all" };
3922
+ }
3923
+ return installForTarget(name, scope, overwrite, target);
3924
+ }
3925
+ function registerHook(name, scope = "global", target = "claude") {
3899
3926
  const meta = getHook(name);
3900
3927
  if (!meta)
3901
3928
  return;
3902
- const settings = readSettings(scope);
3929
+ const settings = readSettings(scope, target);
3903
3930
  if (!settings.hooks)
3904
3931
  settings.hooks = {};
3905
- const eventKey = meta.event;
3932
+ const eventKey = getTargetEventName(meta.event, target);
3906
3933
  if (!settings.hooks[eventKey])
3907
3934
  settings.hooks[eventKey] = [];
3908
3935
  const hookCommand = `hooks run ${name}`;
3909
- settings.hooks[eventKey] = settings.hooks[eventKey].filter((entry2) => !entry2.hooks?.some((h) => h.command === hookCommand));
3936
+ settings.hooks[eventKey] = removeHookEntries(settings.hooks[eventKey], hookCommand);
3910
3937
  const entry = {
3911
3938
  hooks: [{ type: "command", command: hookCommand }]
3912
3939
  };
@@ -3914,30 +3941,30 @@ function registerHook(name, scope = "global") {
3914
3941
  entry.matcher = meta.matcher;
3915
3942
  }
3916
3943
  settings.hooks[eventKey].push(entry);
3917
- writeSettings(settings, scope);
3944
+ writeSettings(settings, scope, target);
3918
3945
  }
3919
- function unregisterHook(name, scope = "global") {
3946
+ function unregisterHook(name, scope = "global", target = "claude") {
3920
3947
  const meta = getHook(name);
3921
3948
  if (!meta)
3922
3949
  return;
3923
- const settings = readSettings(scope);
3950
+ const settings = readSettings(scope, target);
3924
3951
  if (!settings.hooks)
3925
3952
  return;
3926
- const eventKey = meta.event;
3953
+ const eventKey = getTargetEventName(meta.event, target);
3927
3954
  if (!settings.hooks[eventKey])
3928
3955
  return;
3929
3956
  const hookCommand = `hooks run ${name}`;
3930
- settings.hooks[eventKey] = settings.hooks[eventKey].filter((entry) => !entry.hooks?.some((h) => h.command === hookCommand));
3957
+ settings.hooks[eventKey] = removeHookEntries(settings.hooks[eventKey], hookCommand);
3931
3958
  if (settings.hooks[eventKey].length === 0) {
3932
3959
  delete settings.hooks[eventKey];
3933
3960
  }
3934
3961
  if (Object.keys(settings.hooks).length === 0) {
3935
3962
  delete settings.hooks;
3936
3963
  }
3937
- writeSettings(settings, scope);
3964
+ writeSettings(settings, scope, target);
3938
3965
  }
3939
- function getRegisteredHooks(scope = "global") {
3940
- const settings = readSettings(scope);
3966
+ function getRegisteredHooksForTarget(scope = "global", target = "claude") {
3967
+ const settings = readSettings(scope, target);
3941
3968
  if (!settings.hooks)
3942
3969
  return [];
3943
3970
  const registered = [];
@@ -3955,24 +3982,46 @@ function getRegisteredHooks(scope = "global") {
3955
3982
  }
3956
3983
  return [...new Set(registered)];
3957
3984
  }
3958
- function getInstalledHooks(scope = "global") {
3959
- return getRegisteredHooks(scope);
3985
+ function getRegisteredHooks(scope = "global") {
3986
+ return getRegisteredHooksForTarget(scope, "claude");
3987
+ }
3988
+ function removeHook(name, scope = "global", target = "claude") {
3989
+ const shortName = shortHookName(name);
3990
+ if (target === "all") {
3991
+ const claudeRemoved = removeHookForTarget(shortName, scope, "claude");
3992
+ const geminiRemoved = removeHookForTarget(shortName, scope, "gemini");
3993
+ return claudeRemoved || geminiRemoved;
3994
+ }
3995
+ return removeHookForTarget(shortName, scope, target);
3960
3996
  }
3961
- function removeHook(name, scope = "global") {
3962
- const hookName = name.startsWith("hook-") ? name : `hook-${name}`;
3963
- const shortName = hookName.replace("hook-", "");
3964
- const registered = getRegisteredHooks(scope);
3965
- if (!registered.includes(shortName)) {
3997
+ function removeHookForTarget(name, scope, target) {
3998
+ const registered = getRegisteredHooksForTarget(scope, target);
3999
+ if (!registered.includes(name)) {
3966
4000
  return false;
3967
4001
  }
3968
- unregisterHook(shortName, scope);
4002
+ unregisterHook(name, scope, target);
3969
4003
  return true;
3970
4004
  }
3971
- var __dirname2, HOOKS_DIR;
4005
+ var __dirname2, HOOKS_DIR, EVENT_MAP, getInstalledHooks;
3972
4006
  var init_installer = __esm(() => {
3973
4007
  init_registry();
3974
4008
  __dirname2 = dirname(fileURLToPath(import.meta.url));
3975
4009
  HOOKS_DIR = existsSync(join(__dirname2, "..", "..", "hooks", "hook-gitguard")) ? join(__dirname2, "..", "..", "hooks") : join(__dirname2, "..", "hooks");
4010
+ EVENT_MAP = {
4011
+ claude: {
4012
+ PreToolUse: "PreToolUse",
4013
+ PostToolUse: "PostToolUse",
4014
+ Stop: "Stop",
4015
+ Notification: "Notification"
4016
+ },
4017
+ gemini: {
4018
+ PreToolUse: "BeforeTool",
4019
+ PostToolUse: "AfterTool",
4020
+ Stop: "AfterAgent",
4021
+ Notification: "Notification"
4022
+ }
4023
+ };
4024
+ getInstalledHooks = getRegisteredHooks;
3976
4025
  });
3977
4026
 
3978
4027
  // src/mcp/server.ts
@@ -3989,13 +4038,24 @@ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
3989
4038
  import { z } from "zod";
3990
4039
  import { createServer } from "http";
3991
4040
  import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
3992
- import { join as join2 } from "path";
4041
+ import { join as join2, dirname as dirname2 } from "path";
4042
+ import { fileURLToPath as fileURLToPath2 } from "url";
4043
+ function formatInstallResults(results, extra) {
4044
+ const installed = results.filter((r) => r.success).map((r) => r.hook);
4045
+ const failed = results.filter((r) => !r.success).map((r) => ({ hook: r.hook, error: r.error }));
4046
+ return {
4047
+ content: [{
4048
+ type: "text",
4049
+ text: JSON.stringify({ installed, failed, total: results.length, success: installed.length, ...extra })
4050
+ }]
4051
+ };
4052
+ }
3993
4053
  function createHooksServer() {
3994
4054
  const server = new McpServer({
3995
4055
  name: "@hasna/hooks",
3996
- version: "0.0.7"
4056
+ version: pkg.version
3997
4057
  });
3998
- server.tool("hooks_list", "List all available Claude Code hooks, optionally filtered by category", { category: z.string().optional().describe("Filter by category name (e.g. 'Git Safety', 'Code Quality', 'Security', 'Notifications', 'Context Management')") }, async ({ category }) => {
4058
+ server.tool("hooks_list", "List all available hooks, optionally filtered by category", { category: z.string().optional().describe("Filter by category name (e.g. 'Git Safety', 'Code Quality', 'Security', 'Notifications', 'Context Management')") }, async ({ category }) => {
3999
4059
  if (category) {
4000
4060
  const cat = CATEGORIES.find((c) => c.toLowerCase() === category.toLowerCase());
4001
4061
  if (!cat) {
@@ -4022,24 +4082,13 @@ function createHooksServer() {
4022
4082
  const projectInstalled = getRegisteredHooks("project").includes(meta.name);
4023
4083
  return { content: [{ type: "text", text: JSON.stringify({ ...meta, global: globalInstalled, project: projectInstalled }) }] };
4024
4084
  });
4025
- server.tool("hooks_install", "Install one or more hooks by registering them in Claude settings. Use scope 'global' for ~/.claude/settings.json or 'project' for .claude/settings.json", {
4085
+ server.tool("hooks_install", "Install one or more hooks by registering them in agent settings", {
4026
4086
  hooks: z.array(z.string()).describe("Hook names to install"),
4027
4087
  scope: z.enum(["global", "project"]).default("global").describe("Install scope"),
4028
4088
  overwrite: z.boolean().default(false).describe("Overwrite if already installed")
4029
4089
  }, async ({ hooks, scope, overwrite }) => {
4030
4090
  const results = hooks.map((name) => installHook(name, { scope, overwrite }));
4031
- return {
4032
- content: [{
4033
- type: "text",
4034
- text: JSON.stringify({
4035
- installed: results.filter((r) => r.success).map((r) => r.hook),
4036
- failed: results.filter((r) => !r.success).map((r) => ({ hook: r.hook, error: r.error })),
4037
- total: results.length,
4038
- success: results.filter((r) => r.success).length,
4039
- scope
4040
- })
4041
- }]
4042
- };
4091
+ return formatInstallResults(results, { scope });
4043
4092
  });
4044
4093
  server.tool("hooks_install_category", "Install all hooks in a category", {
4045
4094
  category: z.string().describe("Category name"),
@@ -4052,37 +4101,16 @@ function createHooksServer() {
4052
4101
  }
4053
4102
  const hooks = getHooksByCategory(cat).map((h) => h.name);
4054
4103
  const results = hooks.map((name) => installHook(name, { scope, overwrite }));
4055
- return {
4056
- content: [{
4057
- type: "text",
4058
- text: JSON.stringify({
4059
- installed: results.filter((r) => r.success).map((r) => r.hook),
4060
- failed: results.filter((r) => !r.success).map((r) => ({ hook: r.hook, error: r.error })),
4061
- category: cat,
4062
- scope
4063
- })
4064
- }]
4065
- };
4104
+ return formatInstallResults(results, { category: cat, scope });
4066
4105
  });
4067
4106
  server.tool("hooks_install_all", "Install all available hooks", {
4068
4107
  scope: z.enum(["global", "project"]).default("global").describe("Install scope"),
4069
4108
  overwrite: z.boolean().default(false).describe("Overwrite if already installed")
4070
4109
  }, async ({ scope, overwrite }) => {
4071
4110
  const results = HOOKS.map((h) => installHook(h.name, { scope, overwrite }));
4072
- return {
4073
- content: [{
4074
- type: "text",
4075
- text: JSON.stringify({
4076
- installed: results.filter((r) => r.success).map((r) => r.hook),
4077
- failed: results.filter((r) => !r.success).map((r) => ({ hook: r.hook, error: r.error })),
4078
- total: results.length,
4079
- success: results.filter((r) => r.success).length,
4080
- scope
4081
- })
4082
- }]
4083
- };
4111
+ return formatInstallResults(results, { scope });
4084
4112
  });
4085
- server.tool("hooks_remove", "Remove (unregister) a hook from Claude settings", {
4113
+ server.tool("hooks_remove", "Remove (unregister) a hook from agent settings", {
4086
4114
  name: z.string().describe("Hook name to remove"),
4087
4115
  scope: z.enum(["global", "project"]).default("global").describe("Scope to remove from")
4088
4116
  }, async ({ name, scope }) => {
@@ -4153,7 +4181,7 @@ function createHooksServer() {
4153
4181
  content: [{
4154
4182
  type: "text",
4155
4183
  text: JSON.stringify({
4156
- overview: "Claude Code hooks are scripts that run at specific points in a Claude Code session. Install @hasna/hooks globally, then register hooks \u2014 no files are copied to your project.",
4184
+ overview: "Hooks are scripts that run at specific points in an AI coding agent session. Install @hasna/hooks globally, then register hooks \u2014 no files are copied to your project.",
4157
4185
  events: {
4158
4186
  PreToolUse: "Fires before a tool executes. Can block the operation.",
4159
4187
  PostToolUse: "Fires after a tool executes. Runs asynchronously.",
@@ -4209,7 +4237,7 @@ async function startSSEServer(port = MCP_PORT) {
4209
4237
  await transport.handlePostMessage(req, res, body);
4210
4238
  } else {
4211
4239
  res.writeHead(200, { "Content-Type": "application/json" });
4212
- res.end(JSON.stringify({ name: "@hasna/hooks", version: "0.0.7", transport: "sse", port }));
4240
+ res.end(JSON.stringify({ name: "@hasna/hooks", version: pkg.version, transport: "sse", port }));
4213
4241
  }
4214
4242
  });
4215
4243
  httpServer.listen(port, () => {
@@ -4222,10 +4250,12 @@ async function startStdioServer() {
4222
4250
  const transport = new StdioServerTransport;
4223
4251
  await server.connect(transport);
4224
4252
  }
4225
- var MCP_PORT = 39427;
4253
+ var __dirname3, pkg, MCP_PORT = 39427;
4226
4254
  var init_server = __esm(() => {
4227
4255
  init_registry();
4228
4256
  init_installer();
4257
+ __dirname3 = dirname2(fileURLToPath2(import.meta.url));
4258
+ pkg = JSON.parse(readFileSync2(join2(__dirname3, "..", "..", "package.json"), "utf-8"));
4229
4259
  });
4230
4260
 
4231
4261
  // src/cli/index.tsx
@@ -4702,10 +4732,12 @@ function CategorySelect({ onSelect, onBack }) {
4702
4732
  return /* @__PURE__ */ jsxDEV2(Box4, {
4703
4733
  flexDirection: "column",
4704
4734
  children: [
4705
- /* @__PURE__ */ jsxDEV2(Text4, {
4706
- bold: true,
4735
+ /* @__PURE__ */ jsxDEV2(Box4, {
4707
4736
  marginBottom: 1,
4708
- children: "Select a category:"
4737
+ children: /* @__PURE__ */ jsxDEV2(Text4, {
4738
+ bold: true,
4739
+ children: "Select a category:"
4740
+ }, undefined, false, undefined, this)
4709
4741
  }, undefined, false, undefined, this),
4710
4742
  /* @__PURE__ */ jsxDEV2(SelectInput_default, {
4711
4743
  items,
@@ -5268,9 +5300,11 @@ function App({ initialHooks, overwrite = false }) {
5268
5300
  view === "main" && /* @__PURE__ */ jsxDEV7(Box8, {
5269
5301
  flexDirection: "column",
5270
5302
  children: [
5271
- /* @__PURE__ */ jsxDEV7(Text10, {
5303
+ /* @__PURE__ */ jsxDEV7(Box8, {
5272
5304
  marginBottom: 1,
5273
- children: "What would you like to do?"
5305
+ children: /* @__PURE__ */ jsxDEV7(Text10, {
5306
+ children: "What would you like to do?"
5307
+ }, undefined, false, undefined, this)
5274
5308
  }, undefined, false, undefined, this),
5275
5309
  /* @__PURE__ */ jsxDEV7(SelectInput_default, {
5276
5310
  items: mainMenuItems,
@@ -5316,11 +5350,13 @@ function App({ initialHooks, overwrite = false }) {
5316
5350
  view === "done" && /* @__PURE__ */ jsxDEV7(Box8, {
5317
5351
  flexDirection: "column",
5318
5352
  children: [
5319
- /* @__PURE__ */ jsxDEV7(Text10, {
5320
- bold: true,
5321
- color: "green",
5353
+ /* @__PURE__ */ jsxDEV7(Box8, {
5322
5354
  marginBottom: 1,
5323
- children: "Installation complete!"
5355
+ children: /* @__PURE__ */ jsxDEV7(Text10, {
5356
+ bold: true,
5357
+ color: "green",
5358
+ children: "Installation complete!"
5359
+ }, undefined, false, undefined, this)
5324
5360
  }, undefined, false, undefined, this),
5325
5361
  results.filter((r) => r.success).length > 0 && /* @__PURE__ */ jsxDEV7(Box8, {
5326
5362
  flexDirection: "column",
@@ -5425,11 +5461,11 @@ function resolveScope(options) {
5425
5461
  return "project";
5426
5462
  return "global";
5427
5463
  }
5428
- program2.name("hooks").description("Install Claude Code hooks for your project").version("0.1.0");
5464
+ program2.name("hooks").description("Install hooks for AI coding agents").version("0.1.1");
5429
5465
  program2.command("interactive", { isDefault: true }).alias("i").description("Interactive hook browser").action(() => {
5430
5466
  render(/* @__PURE__ */ jsxDEV8(App, {}, undefined, false, undefined, this));
5431
5467
  });
5432
- program2.command("run").argument("<hook>", "Hook to run").description("Execute a hook (called by Claude Code)").action(async (hook) => {
5468
+ program2.command("run").argument("<hook>", "Hook to run").description("Execute a hook (called by AI coding agents)").action(async (hook) => {
5433
5469
  const meta = getHook(hook);
5434
5470
  if (!meta) {
5435
5471
  console.error(JSON.stringify({ error: `Hook '${hook}' not found` }));
@@ -5512,7 +5548,7 @@ Installing hooks (${scope})...
5512
5548
  console.log(chalk2.dim(`
5513
5549
  Registered in ${settingsFile}`));
5514
5550
  });
5515
- program2.command("list").alias("ls").option("-c, --category <category>", "Filter by category").option("-a, --all", "Show all available hooks", false).option("-i, --installed", "Show only installed hooks", false).option("-r, --registered", "Show hooks registered in Claude settings", false).option("-g, --global", "Check global settings", false).option("-p, --project", "Check project settings", false).option("-j, --json", "Output as JSON", false).description("List available or installed hooks").action((options) => {
5551
+ program2.command("list").alias("ls").option("-c, --category <category>", "Filter by category").option("-a, --all", "Show all available hooks", false).option("-i, --installed", "Show only installed hooks", false).option("-r, --registered", "Show registered hooks", false).option("-g, --global", "Check global settings", false).option("-p, --project", "Check project settings", false).option("-j, --json", "Output as JSON", false).description("List available or installed hooks").action((options) => {
5516
5552
  const scope = resolveScope(options);
5517
5553
  if (options.registered || options.installed) {
5518
5554
  const registered = getRegisteredHooks(scope);
@@ -5822,11 +5858,11 @@ ${meta.displayName} v${meta.version}
5822
5858
  return;
5823
5859
  }
5824
5860
  const generalDocs = {
5825
- overview: "Claude Code hooks are scripts that run at specific points in a Claude Code session. Install @hasna/hooks globally, then register hooks \u2014 no files are copied to your project.",
5861
+ overview: "Hooks are scripts that run at specific points in an AI coding agent session. Install @hasna/hooks globally, then register hooks \u2014 no files are copied to your project.",
5826
5862
  events: {
5827
5863
  PreToolUse: 'Fires before a tool executes. Can block the operation by returning { "decision": "block" }.',
5828
5864
  PostToolUse: "Fires after a tool executes. Runs asynchronously, cannot block.",
5829
- Stop: "Fires when a Claude Code session ends. Useful for notifications and cleanup.",
5865
+ Stop: "Fires when a session ends. Useful for notifications and cleanup.",
5830
5866
  Notification: "Fires on notification events like context compaction."
5831
5867
  },
5832
5868
  installation: {
@@ -5848,7 +5884,7 @@ ${meta.displayName} v${meta.version}
5848
5884
  howItWorks: {
5849
5885
  install: "bun install -g @hasna/hooks",
5850
5886
  register: "hooks install gitguard \u2192 writes to ~/.claude/settings.json",
5851
- execution: "Claude Code runs 'hooks run gitguard' \u2192 executes hook from global package",
5887
+ execution: "Agent runs 'hooks run gitguard' \u2192 executes hook from global package",
5852
5888
  noFileCopy: "No files are copied to your project. Hooks run from the global @hasna/hooks package."
5853
5889
  }
5854
5890
  };
@@ -5894,7 +5930,7 @@ ${meta.displayName} v${meta.version}
5894
5930
  console.log(` hooks docs --json Machine-readable documentation`);
5895
5931
  console.log();
5896
5932
  });
5897
- program2.command("mcp").option("-s, --stdio", "Use stdio transport (for Claude Code integration)", false).option("-p, --port <port>", "Port for SSE transport", "39427").description("Start MCP server for AI agent integration").action(async (options) => {
5933
+ program2.command("mcp").option("-s, --stdio", "Use stdio transport (for agent MCP integration)", false).option("-p, --port <port>", "Port for SSE transport", "39427").description("Start MCP server for AI agent integration").action(async (options) => {
5898
5934
  if (options.stdio) {
5899
5935
  const { startStdioServer: startStdioServer2 } = await Promise.resolve().then(() => (init_server(), exports_server));
5900
5936
  await startStdioServer2();