@hybridlabor-api/aos 4.14.0 → 4.14.1

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.
@@ -8,6 +8,7 @@ let raw = '';
8
8
  try { for await (const c of process.stdin) raw += c; } catch {}
9
9
  let ev;
10
10
  try { ev = JSON.parse(raw); } catch { process.exit(0); }
11
+ if (!ev || typeof ev !== 'object') process.exit(0);
11
12
 
12
13
  ev.agent = arg('--agent') || ev.agent || 'claude';
13
14
  ev.hook_event_name ||= arg('--event');
@@ -6,11 +6,11 @@
6
6
  "hooks": [
7
7
  {
8
8
  "type": "command",
9
- "command": "node \"$CLAUDE_PROJECT_DIR/.claude/hooks/go-gate.mjs\""
9
+ "command": "node \"${CLAUDE_PROJECT_DIR}/.claude/hooks/go-gate.mjs\""
10
10
  },
11
11
  {
12
12
  "type": "command",
13
- "command": "node \"$CLAUDE_PROJECT_DIR/.claude/hooks/conventional-commits.mjs\""
13
+ "command": "node \"${CLAUDE_PROJECT_DIR}/.claude/hooks/conventional-commits.mjs\""
14
14
  }
15
15
  ]
16
16
  },
@@ -19,7 +19,7 @@
19
19
  "hooks": [
20
20
  {
21
21
  "type": "command",
22
- "command": "node \"$CLAUDE_PROJECT_DIR/.claude/hooks/env-file-protection.mjs\""
22
+ "command": "node \"${CLAUDE_PROJECT_DIR}/.claude/hooks/env-file-protection.mjs\""
23
23
  }
24
24
  ]
25
25
  },
@@ -48,7 +48,7 @@
48
48
  "hooks": [
49
49
  {
50
50
  "type": "command",
51
- "command": "node \"$CLAUDE_PROJECT_DIR/.claude/hooks/graph-gate.mjs\""
51
+ "command": "node \"${CLAUDE_PROJECT_DIR}/.claude/hooks/graph-gate.mjs\""
52
52
  }
53
53
  ]
54
54
  },
@@ -119,7 +119,7 @@
119
119
  "hooks": [
120
120
  {
121
121
  "type": "command",
122
- "command": "node \"$CLAUDE_PROJECT_DIR/.claude/hooks/go-token.mjs\""
122
+ "command": "node \"${CLAUDE_PROJECT_DIR}/.claude/hooks/go-token.mjs\""
123
123
  }
124
124
  ]
125
125
  },
package/installer.js CHANGED
@@ -4004,8 +4004,8 @@ function mergeBdbSettingsHooks(settingsPath, { projectLocal = false } = {}) {
4004
4004
  // Global installs must point at the copies installGlobalHooks put under
4005
4005
  // $HOME/.claude/hooks; $CLAUDE_PROJECT_DIR would not exist in other projects.
4006
4006
  const localize = (cmd) => (projectLocal
4007
- ? (machineGlobalHooks.some((n) => cmd.includes(n)) ? cmd : cmd.split('${HOME}').join('$CLAUDE_PROJECT_DIR'))
4008
- : cmd.split('$CLAUDE_PROJECT_DIR/.claude/hooks/').join('$HOME/.claude/hooks/'));
4007
+ ? (machineGlobalHooks.some((n) => cmd.includes(n)) ? cmd : cmd.split('${HOME}').join('${CLAUDE_PROJECT_DIR}').replace(/\$CLAUDE_PROJECT_DIR\b/g, '${CLAUDE_PROJECT_DIR}'))
4008
+ : cmd.replace(/\$\{?CLAUDE_PROJECT_DIR\}?\/\.claude\/hooks\//g, '$HOME/.claude/hooks/'));
4009
4009
  const cloneBdbEntries = (entries) =>
4010
4010
  JSON.parse(JSON.stringify(entries)).map((e) => ({
4011
4011
  ...e,
@@ -4067,18 +4067,61 @@ function mergeBdbSettingsHooks(settingsPath, { projectLocal = false } = {}) {
4067
4067
  }
4068
4068
  }
4069
4069
 
4070
+ // agy runs a hook command through `sh -c` / `cmd /c` with cwd = the hooks.json folder, and
4071
+ // cmd cannot parse the \" Go emits for quotes. So the command is relative and quote-free.
4072
+ function agyHookCommand(hooksJsonDir, scriptPath, args = '') {
4073
+ const rel = path.relative(hooksJsonDir, scriptPath).split(path.sep).join('/');
4074
+ if (!rel || rel.startsWith('..') || path.isAbsolute(rel) || /[\s"'&|<>^%]/.test(rel)) {
4075
+ throw new Error(`agy hook script must sit under ${hooksJsonDir} with a plain path, got ${scriptPath}`);
4076
+ }
4077
+ return `node ${rel}${args}`;
4078
+ }
4079
+
4080
+ // Runs every AOS command of a hooks.json once with `{}` on stdin, the way agy does.
4081
+ // Returns [{ command, reason }] for each failure; `spawn` is injectable for tests.
4082
+ function selfCheckAgyHooks(hooksPath, spawn = spawnSync) {
4083
+ const bdbScripts = ['go-gate.mjs', 'graph-gate.mjs', 'memb-inject.mjs', 'startcycle-dispatch.mjs', 'trail-relay.mjs', 'conventional-commits.mjs', 'env-file-protection.mjs'];
4084
+ const data = readJsonFile(hooksPath);
4085
+ const commands = [];
4086
+ const walk = (v) => {
4087
+ if (Array.isArray(v)) v.forEach(walk);
4088
+ else if (v && typeof v === 'object') {
4089
+ if (typeof v.command === 'string' && bdbScripts.some((n) => v.command.includes(n))) commands.push(v.command);
4090
+ Object.values(v).forEach(walk);
4091
+ }
4092
+ };
4093
+ walk(data && data.hooks);
4094
+ const [shell, flag] = process.platform === 'win32' ? ['cmd', '/c'] : ['sh', '-c'];
4095
+ const failures = [];
4096
+ for (const command of [...new Set(commands)]) {
4097
+ // AGENTTRAIL_PORT=1: the no-op payload must not show up on a live map.
4098
+ const r = spawn(shell, [flag, command], { cwd: path.dirname(hooksPath), input: '{}', encoding: 'utf8', timeout: 5000, windowsHide: true, env: { ...process.env, AGENTTRAIL_PORT: '1' } });
4099
+ const stderr = String((r && r.stderr) || '');
4100
+ let reason = null;
4101
+ if (!r || r.error) reason = `spawn failed: ${r && r.error ? r.error.message : 'no result'}`;
4102
+ else if (r.status !== 0) reason = `exit ${r.status}\n ${stderr.trim().slice(0, 500)}`;
4103
+ else if (/MODULE_NOT_FOUND|Cannot find module/.test(stderr)) reason = `module not found\n ${stderr.trim().slice(0, 500)}`;
4104
+ if (reason) failures.push({ command, reason });
4105
+ }
4106
+ return failures;
4107
+ }
4108
+
4070
4109
  // Merge the BDB hooks into Google Antigravity's hooks.json (.agents/hooks.json or
4071
4110
  // ~/.gemini/config/hooks.json), preserving user-defined foreign hooks.
4072
- function mergeAntigravityHooks(hooksPath, { projectLocal = false } = {}) {
4111
+ function mergeAntigravityHooks(hooksPath, { projectLocal = false, selfCheck = spawnSync } = {}) {
4073
4112
  const bdbHookScripts = ['go-gate.mjs', 'graph-gate.mjs', 'memb-inject.mjs', 'startcycle-dispatch.mjs', 'trail-relay.mjs', 'conventional-commits.mjs', 'env-file-protection.mjs'];
4074
4113
  const isBdbHandler = (h) => typeof h?.command === 'string' && bdbHookScripts.some((name) => h.command.includes(name));
4075
4114
 
4076
4115
  const baseDir = path.dirname(hooksPath);
4077
- const hooksDir = projectLocal ? path.join(currentDir, '.agents', 'hooks') : path.join(baseDir, 'hooks');
4078
- const workflowsDir = projectLocal ? path.join(currentDir, '.agents', 'workflows') : path.join(baseDir, 'workflows');
4079
- const globalHooksDir = projectLocal ? path.join(homeDir, '.gemini', 'config', 'hooks') : hooksDir;
4080
-
4081
- const cmd = (dir, script, args = '') => `node "${path.join(dir, script)}"${args}`;
4116
+ // Commands are relative to hooks.json's folder, so every script must live under it
4117
+ // (installProjectHarness copies the hooks into .agents/hooks for the project-local file).
4118
+ const hooksDir = path.join(baseDir, 'hooks');
4119
+ const workflowsDir = path.join(baseDir, 'workflows');
4120
+ const globalHooksDir = hooksDir;
4121
+
4122
+ const cmd = (dir, script, args = '') => agyHookCommand(baseDir, path.join(dir, script), args);
4123
+ // Observability hooks must never block a tool call when they cannot start.
4124
+ const obs = (dir, script, args = '') => cmd(dir, script, `${args} || exit 0`);
4082
4125
  const bdbHooks = {
4083
4126
  PreToolUse: [
4084
4127
  {
@@ -4094,15 +4137,15 @@ function mergeAntigravityHooks(hooksPath, { projectLocal = false } = {}) {
4094
4137
  },
4095
4138
  {
4096
4139
  matcher: "*",
4097
- hooks: [{ type: "command", command: cmd(globalHooksDir, 'trail-relay.mjs', ' --agent agy --event PreToolUse'), timeout: 2 }]
4140
+ hooks: [{ type: "command", command: obs(globalHooksDir, 'trail-relay.mjs', ' --agent agy --event PreToolUse'), timeout: 2 }]
4098
4141
  }
4099
4142
  ],
4100
4143
  Stop: [
4101
4144
  { type: "command", command: cmd(hooksDir, 'graph-gate.mjs'), timeout: 10 },
4102
- { type: "command", command: cmd(globalHooksDir, 'trail-relay.mjs', ' --agent agy --event Stop'), timeout: 2 }
4145
+ { type: "command", command: obs(globalHooksDir, 'trail-relay.mjs', ' --agent agy --event Stop'), timeout: 2 }
4103
4146
  ],
4104
4147
  PreInvocation: [
4105
- { type: "command", command: cmd(globalHooksDir, 'memb-inject.mjs'), timeout: 8 },
4148
+ { type: "command", command: obs(globalHooksDir, 'memb-inject.mjs'), timeout: 8 },
4106
4149
  { type: "command", command: cmd(workflowsDir, 'startcycle-dispatch.mjs'), timeout: 30 }
4107
4150
  ]
4108
4151
  };
@@ -4160,9 +4203,19 @@ function mergeAntigravityHooks(hooksPath, { projectLocal = false } = {}) {
4160
4203
 
4161
4204
  try {
4162
4205
  fs.mkdirSync(path.dirname(hooksPath), { recursive: true });
4206
+ const previousRaw = existing ? fs.readFileSync(hooksPath, 'utf8') : null;
4163
4207
  const tmpPath = `${hooksPath}.${process.pid}.tmp`;
4164
4208
  fs.writeFileSync(tmpPath, JSON.stringify(buildMerged(existing), null, 2) + '\n');
4165
4209
  fs.renameSync(tmpPath, hooksPath);
4210
+ if (!DRY_RUN) {
4211
+ const failures = selfCheckAgyHooks(hooksPath, selfCheck);
4212
+ if (failures.length) {
4213
+ if (previousRaw === null) fs.unlinkSync(hooksPath);
4214
+ else fs.writeFileSync(hooksPath, previousRaw);
4215
+ log.warn(`Antigravity hook self-check FAILED for ${hooksPath}; the previous hooks.json was ${previousRaw === null ? 'removed (none existed)' : 'restored'}.`);
4216
+ for (const f of failures) log.warn(` command: ${f.command}\n ${f.reason}`);
4217
+ }
4218
+ }
4166
4219
  } catch (e) {
4167
4220
  log.warn(`Could not write ${hooksPath}: ${e.message}`);
4168
4221
  }
@@ -5916,6 +5969,8 @@ module.exports = {
5916
5969
  keepExistingEnvValues,
5917
5970
  mergeBdbSettingsHooks,
5918
5971
  mergeAntigravityHooks,
5972
+ agyHookCommand,
5973
+ selfCheckAgyHooks,
5919
5974
  mergeCodexTomlHooks,
5920
5975
  mergeCodexHooks: mergeCodexTomlHooks,
5921
5976
  mergeCodexTomlMcpServers,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hybridlabor-api/aos",
3
- "version": "4.14.0",
3
+ "version": "4.14.1",
4
4
  "description": "AOS — A Curated AI AGENT OS. Optimized agent skills and add-ons like memB, OpenWiki, Heimdall Token Saver, and Godmode architectures.",
5
5
  "main": "installer.js",
6
6
  "engines": {
@@ -28,7 +28,7 @@
28
28
  "plugin:build": "node scripts/build-plugin-manifest.mjs",
29
29
  "plugin:check": "node scripts/build-plugin-manifest.mjs --check",
30
30
  "doctor": "node bin/aos-doctor.mjs",
31
- "test": "node scripts/validate-skills.mjs --selftest && node scripts/validate-skills.mjs && node scripts/build-plugin-manifest.mjs --check && node --test tests/opencode-graph-gate.test.js && node --test tests/cross-harness-hooks.test.js && node --test tests/plan-canvas-security.test.mjs && node --test tests/plan-canvas-modes.test.mjs && node --test tests/swarm-wiring.test.js && node tests/aos-store.test.mjs && node --test tests/aos-store-ui.test.mjs && node tests/aos-store-scenario.test.mjs && node tests/aos-doctor.test.mjs && node tests/build-plugin-manifest.test.mjs && node --test tests/agent-models.test.js && node --test tests/installer-p3.test.js && node --test tests/installer-binary.test.js && node --test tests/installer-version.test.js && node --test tests/mcsc-server.test.js && node --test tests/antigravity-hooks.test.js && node --test tests/plan-builder.test.mjs && node --test tests/plan-builder-trail.test.mjs && node --test tests/plan-builder-showcase.test.mjs && node --test tests/plan-builder-templates.test.mjs && node --test tests/plan-canvas-home.test.mjs && node --test tests/launchpad-installer.test.js && node --test tests/launchpad-plan-canvas.test.js && node --test tests/go-token.test.js && node --test tests/master-session-skill.test.js && node --test tests/aos-acp.test.js && node --test tests/opencode-go-token.test.js && node --test tests/opencode-adapter.test.js && node --test tests/pr-recap.test.mjs && node --test tests/bdb-visual-edit.test.mjs && node --test tests/opencode-trail-autostart.test.mjs && node --test tests/trail-autostart-hook.test.mjs && node --test tests/agenttrail-ensure.test.mjs"
31
+ "test": "node scripts/validate-skills.mjs --selftest && node scripts/validate-skills.mjs && node scripts/build-plugin-manifest.mjs --check && node --test tests/opencode-graph-gate.test.js && node --test tests/cross-harness-hooks.test.js && node --test tests/plan-canvas-security.test.mjs && node --test tests/plan-canvas-modes.test.mjs && node --test tests/swarm-wiring.test.js && node tests/aos-store.test.mjs && node --test tests/aos-store-ui.test.mjs && node tests/aos-store-scenario.test.mjs && node tests/aos-doctor.test.mjs && node tests/build-plugin-manifest.test.mjs && node --test tests/agent-models.test.js && node --test tests/installer-p3.test.js && node --test tests/installer-binary.test.js && node --test tests/installer-version.test.js && node --test tests/mcsc-server.test.js && node --test tests/antigravity-hooks.test.js && node --test tests/windows-hooks.test.js && node --test tests/plan-builder.test.mjs && node --test tests/plan-builder-trail.test.mjs && node --test tests/plan-builder-showcase.test.mjs && node --test tests/plan-builder-templates.test.mjs && node --test tests/plan-canvas-home.test.mjs && node --test tests/launchpad-installer.test.js && node --test tests/launchpad-plan-canvas.test.js && node --test tests/go-token.test.js && node --test tests/master-session-skill.test.js && node --test tests/aos-acp.test.js && node --test tests/opencode-go-token.test.js && node --test tests/opencode-adapter.test.js && node --test tests/pr-recap.test.mjs && node --test tests/bdb-visual-edit.test.mjs && node --test tests/opencode-trail-autostart.test.mjs && node --test tests/trail-autostart-hook.test.mjs && node --test tests/agenttrail-ensure.test.mjs"
32
32
  },
33
33
  "publishConfig": {
34
34
  "access": "public"