@hasna/hooks 0.9.3 → 0.9.4

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/hooks-mcp.js CHANGED
@@ -2708,7 +2708,7 @@ function mementosCommandSuffix(value) {
2708
2708
  return value.enabled ? ` --enable-mementos${value.secretRef ? ` --openrouter-secret-ref ${value.secretRef}` : ""}` : " --disable-mementos";
2709
2709
  }
2710
2710
  function retainedMementosRegistration(commands) {
2711
- const values = commands.filter((command) => /^hooks run mementos-context(?:\s|$)/.test(command)).map((command) => {
2711
+ const values = commands.map((command) => command.trim()).filter((command) => /^hooks run mementos-context(?:\s|$)/.test(command)).map((command) => {
2712
2712
  const args = command.trim().split(/\s+/).slice(3);
2713
2713
  const flags = {};
2714
2714
  const seen = new Set;
@@ -3436,8 +3436,36 @@ function registerHook(name, scope = "global", target = "claude", profile, mement
3436
3436
  if (!settings.hooks)
3437
3437
  settings.hooks = {};
3438
3438
  const retained = name === "mementos-context" && mementos === undefined ? retainedMementosRegistration(Object.values(settings.hooks).flatMap((groups) => Array.isArray(groups) ? groups.flatMap((group) => Array.isArray(group.hooks) ? group.hooks.map((hook) => hook.command).filter((command) => typeof command === "string") : []) : [])) : undefined;
3439
- removeHookFromAllEvents(settings, name);
3440
3439
  const hookCommand = (profile ? `hooks run ${name} --profile ${profile}` : `hooks run ${name}`) + mementosCommandSuffix(mementos ?? retained);
3440
+ if (target === "codex") {
3441
+ const positions = [];
3442
+ for (const [event, groups] of Object.entries(settings.hooks)) {
3443
+ for (const entry of groups) {
3444
+ for (let index = 0;index < entry.hooks.length; index++) {
3445
+ const command = entry.hooks[index]?.command;
3446
+ const match = typeof command === "string" && command.trim().match(/^(?:hooks run ([\w-]+)(?:\s|$)|hook-([\w-]+)$)/);
3447
+ if (match && (match[1] ?? match[2]) === name) {
3448
+ if (registrationName(command) !== name)
3449
+ throw new Error("Unrecognized Codex registration; reconcile it before updating this hook.");
3450
+ positions.push({ event, entry, index });
3451
+ }
3452
+ }
3453
+ }
3454
+ }
3455
+ if (positions.length) {
3456
+ if (positions.length !== uniqueEventKeys.length || uniqueEventKeys.some((event) => positions.filter((position) => position.event === event).length !== 1) || positions.some((position) => (position.entry.matcher ?? "") !== (meta.matcher ?? ""))) {
3457
+ throw new Error("Codex hook events, matchers or duplicate registrations require explicit reconciliation; no settings were changed.");
3458
+ }
3459
+ for (const { entry, index } of positions) {
3460
+ entry.hooks[index] = { ...entry.hooks[index], type: "command", command: hookCommand };
3461
+ if (typeof meta.timeoutSeconds === "number" && meta.timeoutSeconds > 0)
3462
+ entry.hooks[index].timeout = meta.timeoutSeconds;
3463
+ }
3464
+ writeSettings(settings, scope, target);
3465
+ return;
3466
+ }
3467
+ }
3468
+ removeHookFromAllEvents(settings, name);
3441
3469
  for (const eventKey of uniqueEventKeys) {
3442
3470
  if (!settings.hooks[eventKey])
3443
3471
  settings.hooks[eventKey] = [];
package/bin/index.js CHANGED
@@ -4528,7 +4528,7 @@ function mementosCommandSuffix(value) {
4528
4528
  return value.enabled ? ` --enable-mementos${value.secretRef ? ` --openrouter-secret-ref ${value.secretRef}` : ""}` : " --disable-mementos";
4529
4529
  }
4530
4530
  function retainedMementosRegistration(commands) {
4531
- const values = commands.filter((command) => /^hooks run mementos-context(?:\s|$)/.test(command)).map((command) => {
4531
+ const values = commands.map((command) => command.trim()).filter((command) => /^hooks run mementos-context(?:\s|$)/.test(command)).map((command) => {
4532
4532
  const args = command.trim().split(/\s+/).slice(3);
4533
4533
  const flags = {};
4534
4534
  const seen = new Set;
@@ -5790,8 +5790,36 @@ function registerHook(name, scope = "global", target = "claude", profile, mement
5790
5790
  if (!settings.hooks)
5791
5791
  settings.hooks = {};
5792
5792
  const retained = name === "mementos-context" && mementos === undefined ? retainedMementosRegistration(Object.values(settings.hooks).flatMap((groups) => Array.isArray(groups) ? groups.flatMap((group) => Array.isArray(group.hooks) ? group.hooks.map((hook) => hook.command).filter((command) => typeof command === "string") : []) : [])) : undefined;
5793
- removeHookFromAllEvents(settings, name);
5794
5793
  const hookCommand = (profile ? `hooks run ${name} --profile ${profile}` : `hooks run ${name}`) + mementosCommandSuffix(mementos ?? retained);
5794
+ if (target === "codex") {
5795
+ const positions = [];
5796
+ for (const [event, groups] of Object.entries(settings.hooks)) {
5797
+ for (const entry of groups) {
5798
+ for (let index = 0;index < entry.hooks.length; index++) {
5799
+ const command = entry.hooks[index]?.command;
5800
+ const match = typeof command === "string" && command.trim().match(/^(?:hooks run ([\w-]+)(?:\s|$)|hook-([\w-]+)$)/);
5801
+ if (match && (match[1] ?? match[2]) === name) {
5802
+ if (registrationName(command) !== name)
5803
+ throw new Error("Unrecognized Codex registration; reconcile it before updating this hook.");
5804
+ positions.push({ event, entry, index });
5805
+ }
5806
+ }
5807
+ }
5808
+ }
5809
+ if (positions.length) {
5810
+ if (positions.length !== uniqueEventKeys.length || uniqueEventKeys.some((event) => positions.filter((position) => position.event === event).length !== 1) || positions.some((position) => (position.entry.matcher ?? "") !== (meta.matcher ?? ""))) {
5811
+ throw new Error("Codex hook events, matchers or duplicate registrations require explicit reconciliation; no settings were changed.");
5812
+ }
5813
+ for (const { entry, index } of positions) {
5814
+ entry.hooks[index] = { ...entry.hooks[index], type: "command", command: hookCommand };
5815
+ if (typeof meta.timeoutSeconds === "number" && meta.timeoutSeconds > 0)
5816
+ entry.hooks[index].timeout = meta.timeoutSeconds;
5817
+ }
5818
+ writeSettings(settings, scope, target);
5819
+ return;
5820
+ }
5821
+ }
5822
+ removeHookFromAllEvents(settings, name);
5795
5823
  for (const eventKey of uniqueEventKeys) {
5796
5824
  if (!settings.hooks[eventKey])
5797
5825
  settings.hooks[eventKey] = [];
package/dist/index.js CHANGED
@@ -7473,7 +7473,7 @@ function mementosCommandSuffix(value) {
7473
7473
  return value.enabled ? ` --enable-mementos${value.secretRef ? ` --openrouter-secret-ref ${value.secretRef}` : ""}` : " --disable-mementos";
7474
7474
  }
7475
7475
  function retainedMementosRegistration(commands) {
7476
- const values = commands.filter((command) => /^hooks run mementos-context(?:\s|$)/.test(command)).map((command) => {
7476
+ const values = commands.map((command) => command.trim()).filter((command) => /^hooks run mementos-context(?:\s|$)/.test(command)).map((command) => {
7477
7477
  const args = command.trim().split(/\s+/).slice(3);
7478
7478
  const flags = {};
7479
7479
  const seen = new Set;
@@ -8015,8 +8015,36 @@ function registerHook(name, scope = "global", target = "claude", profile, mement
8015
8015
  if (!settings.hooks)
8016
8016
  settings.hooks = {};
8017
8017
  const retained = name === "mementos-context" && mementos === undefined ? retainedMementosRegistration(Object.values(settings.hooks).flatMap((groups) => Array.isArray(groups) ? groups.flatMap((group) => Array.isArray(group.hooks) ? group.hooks.map((hook) => hook.command).filter((command) => typeof command === "string") : []) : [])) : undefined;
8018
- removeHookFromAllEvents(settings, name);
8019
8018
  const hookCommand = (profile ? `hooks run ${name} --profile ${profile}` : `hooks run ${name}`) + mementosCommandSuffix(mementos ?? retained);
8019
+ if (target === "codex") {
8020
+ const positions = [];
8021
+ for (const [event, groups] of Object.entries(settings.hooks)) {
8022
+ for (const entry of groups) {
8023
+ for (let index = 0;index < entry.hooks.length; index++) {
8024
+ const command = entry.hooks[index]?.command;
8025
+ const match = typeof command === "string" && command.trim().match(/^(?:hooks run ([\w-]+)(?:\s|$)|hook-([\w-]+)$)/);
8026
+ if (match && (match[1] ?? match[2]) === name) {
8027
+ if (registrationName(command) !== name)
8028
+ throw new Error("Unrecognized Codex registration; reconcile it before updating this hook.");
8029
+ positions.push({ event, entry, index });
8030
+ }
8031
+ }
8032
+ }
8033
+ }
8034
+ if (positions.length) {
8035
+ if (positions.length !== uniqueEventKeys.length || uniqueEventKeys.some((event) => positions.filter((position) => position.event === event).length !== 1) || positions.some((position) => (position.entry.matcher ?? "") !== (meta.matcher ?? ""))) {
8036
+ throw new Error("Codex hook events, matchers or duplicate registrations require explicit reconciliation; no settings were changed.");
8037
+ }
8038
+ for (const { entry, index } of positions) {
8039
+ entry.hooks[index] = { ...entry.hooks[index], type: "command", command: hookCommand };
8040
+ if (typeof meta.timeoutSeconds === "number" && meta.timeoutSeconds > 0)
8041
+ entry.hooks[index].timeout = meta.timeoutSeconds;
8042
+ }
8043
+ writeSettings(settings, scope, target);
8044
+ return;
8045
+ }
8046
+ }
8047
+ removeHookFromAllEvents(settings, name);
8020
8048
  for (const eventKey of uniqueEventKeys) {
8021
8049
  if (!settings.hooks[eventKey])
8022
8050
  settings.hooks[eventKey] = [];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hasna/hooks",
3
- "version": "0.9.3",
3
+ "version": "0.9.4",
4
4
  "description": "Open source hooks library for AI coding agents - Install safety, quality, and automation hooks with a single command",
5
5
  "type": "module",
6
6
  "bin": {