@hybridlabor-api/aos 4.5.0 → 4.5.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.
Files changed (49) hide show
  1. package/.claude/hooks/memb-inject.mjs +135 -40
  2. package/.claude/workflows/startcycle-dispatch.mjs +318 -3
  3. package/bin/aos-dashboard.mjs +8 -2
  4. package/installer.js +203 -7
  5. package/package.json +1 -1
  6. package/skills/global_config/aos-setup/SKILL.md +7 -9
  7. package/skills/global_config/aos-setup/scripts/aos-doctor.mjs +43 -1
  8. package/skills/global_config/openwiki-skill/scripts/install_daemon.ps1 +107 -18
  9. package/.agents/skills/firecrawl/SKILL.md +0 -149
  10. package/.agents/skills/firecrawl/rules/install.md +0 -82
  11. package/.agents/skills/firecrawl/rules/security.md +0 -26
  12. package/.agents/skills/firecrawl-agent/SKILL.md +0 -58
  13. package/.agents/skills/firecrawl-build/SKILL.md +0 -39
  14. package/.agents/skills/firecrawl-build-interact/SKILL.md +0 -68
  15. package/.agents/skills/firecrawl-build-onboarding/SKILL.md +0 -103
  16. package/.agents/skills/firecrawl-build-onboarding/references/auth-flow.md +0 -39
  17. package/.agents/skills/firecrawl-build-onboarding/references/project-setup.md +0 -20
  18. package/.agents/skills/firecrawl-build-onboarding/references/sdk-installation.md +0 -17
  19. package/.agents/skills/firecrawl-build-scrape/SKILL.md +0 -69
  20. package/.agents/skills/firecrawl-build-search/SKILL.md +0 -69
  21. package/.agents/skills/firecrawl-crawl/SKILL.md +0 -59
  22. package/.agents/skills/firecrawl-download/SKILL.md +0 -70
  23. package/.agents/skills/firecrawl-interact/SKILL.md +0 -84
  24. package/.agents/skills/firecrawl-map/SKILL.md +0 -51
  25. package/.agents/skills/firecrawl-scrape/SKILL.md +0 -69
  26. package/.agents/skills/firecrawl-search/SKILL.md +0 -60
  27. package/mcps/RhinoMCP/cc-plugin/.claude/settings.json +0 -10
  28. package/mcps/after-effects-mcp/build/index.js +0 -840
  29. package/mcps/after-effects-mcp/build/scripts/applyEffect.jsx +0 -153
  30. package/mcps/after-effects-mcp/build/scripts/applyEffectTemplate.jsx +0 -218
  31. package/mcps/after-effects-mcp/build/scripts/createComposition.jsx +0 -71
  32. package/mcps/after-effects-mcp/build/scripts/createShapeLayer.jsx +0 -147
  33. package/mcps/after-effects-mcp/build/scripts/createSolidLayer.jsx +0 -114
  34. package/mcps/after-effects-mcp/build/scripts/createTextLayer.jsx +0 -115
  35. package/mcps/after-effects-mcp/build/scripts/getLayerInfo.jsx +0 -192
  36. package/mcps/after-effects-mcp/build/scripts/getProjectInfo.jsx +0 -90
  37. package/mcps/after-effects-mcp/build/scripts/listCompositions.jsx +0 -50
  38. package/mcps/after-effects-mcp/build/scripts/mcp-bridge-auto.jsx +0 -1773
  39. package/mcps/after-effects-mcp/build/scripts/setLayerProperties.jsx +0 -160
  40. package/mcps/computer-use-mcp/dist/client.d.ts +0 -150
  41. package/mcps/computer-use-mcp/dist/client.js +0 -136
  42. package/mcps/computer-use-mcp/dist/entrypoint.d.ts +0 -16
  43. package/mcps/computer-use-mcp/dist/entrypoint.js +0 -26
  44. package/mcps/computer-use-mcp/dist/native.d.ts +0 -212
  45. package/mcps/computer-use-mcp/dist/native.js +0 -50
  46. package/mcps/computer-use-mcp/dist/server.d.ts +0 -32
  47. package/mcps/computer-use-mcp/dist/server.js +0 -342
  48. package/mcps/computer-use-mcp/dist/session.d.ts +0 -101
  49. package/mcps/computer-use-mcp/dist/session.js +0 -2372
package/installer.js CHANGED
@@ -3175,11 +3175,44 @@ function injectHarnessRules() {
3175
3175
  // most (a fresh install got it; every machine that already had AOS did not).
3176
3176
  function installGlobalHooks() {
3177
3177
  const hooksSrc = path.join(srcDir, '.claude', 'hooks');
3178
+ const workflowsSrc = path.join(srcDir, '.claude', 'workflows');
3179
+
3180
+ // 1. Claude Code
3178
3181
  if (fs.existsSync(hooksSrc)) {
3179
3182
  copyDirRecursiveSync(hooksSrc, path.join(homeDir, '.claude', 'hooks'));
3180
3183
  log.step(`Installed hooks to ${path.join(homeDir, '.claude', 'hooks')}`);
3181
3184
  }
3185
+ if (fs.existsSync(workflowsSrc)) {
3186
+ copyDirRecursiveSync(workflowsSrc, path.join(homeDir, '.claude', 'workflows'));
3187
+ }
3182
3188
  mergeBdbSettingsHooks(path.join(homeDir, '.claude', 'settings.json'));
3189
+
3190
+ // 2. Google Antigravity
3191
+ const agyHooksDir = path.join(geminiDir, 'config', 'hooks');
3192
+ const agyWorkflowsDir = path.join(geminiDir, 'config', 'workflows');
3193
+ if (fs.existsSync(hooksSrc)) {
3194
+ copyDirRecursiveSync(hooksSrc, agyHooksDir);
3195
+ log.step(`Installed hooks to ${agyHooksDir}`);
3196
+ }
3197
+ if (fs.existsSync(workflowsSrc)) {
3198
+ copyDirRecursiveSync(workflowsSrc, agyWorkflowsDir);
3199
+ }
3200
+ mergeAntigravityHooks(path.join(geminiDir, 'config', 'hooks.json'));
3201
+ if (fs.existsSync(path.join(geminiDir, 'antigravity-cli'))) {
3202
+ mergeAntigravityHooks(path.join(geminiDir, 'antigravity-cli', 'hooks.json'));
3203
+ }
3204
+
3205
+ // 3. OpenAI Codex CLI
3206
+ const codexHooksDir = path.join(homeDir, '.codex', 'hooks');
3207
+ const codexWorkflowsDir = path.join(homeDir, '.codex', 'workflows');
3208
+ if (fs.existsSync(hooksSrc)) {
3209
+ copyDirRecursiveSync(hooksSrc, codexHooksDir);
3210
+ log.step(`Installed hooks to ${codexHooksDir}`);
3211
+ }
3212
+ if (fs.existsSync(workflowsSrc)) {
3213
+ copyDirRecursiveSync(workflowsSrc, codexWorkflowsDir);
3214
+ }
3215
+ mergeCodexTomlHooks(path.join(homeDir, '.codex', 'config.toml'));
3183
3216
  }
3184
3217
 
3185
3218
  // Merge the BDB hooks -- the two gates plus the memB ambient-memory hook --
@@ -3258,12 +3291,161 @@ function mergeBdbSettingsHooks(settingsPath, { projectLocal = false } = {}) {
3258
3291
  }
3259
3292
 
3260
3293
  try {
3294
+ fs.mkdirSync(path.dirname(settingsPath), { recursive: true });
3261
3295
  fs.writeFileSync(settingsPath, JSON.stringify(buildMerged(existing), null, 2) + '\n');
3262
3296
  } catch (e) {
3263
3297
  log.warn(`Could not write ${settingsPath}: ${e.message}`);
3264
3298
  }
3265
3299
  }
3266
3300
 
3301
+ // Merge the BDB hooks into Google Antigravity's hooks.json (.agents/hooks.json or
3302
+ // ~/.gemini/config/hooks.json), preserving user-defined foreign hooks.
3303
+ function mergeAntigravityHooks(hooksPath, { projectLocal = false } = {}) {
3304
+ const bdbHookScripts = ['go-gate.mjs', 'graph-gate.mjs', 'memb-inject.mjs', 'startcycle-dispatch.mjs'];
3305
+ const isBdbEntry = (entry) => {
3306
+ const cmds = (entry && Array.isArray(entry.hooks) ? entry.hooks : [entry])
3307
+ .map((h) => (h && typeof h.command === 'string' ? h.command : (typeof h === 'string' ? h : '')))
3308
+ .join(' ');
3309
+ return bdbHookScripts.some((name) => cmds.includes(name));
3310
+ };
3311
+
3312
+ const baseDir = path.dirname(hooksPath);
3313
+ const hooksDir = projectLocal ? path.join(currentDir, '.agents', 'hooks') : path.join(baseDir, 'hooks');
3314
+ const workflowsDir = projectLocal ? path.join(currentDir, '.agents', 'workflows') : path.join(baseDir, 'workflows');
3315
+ const globalHooksDir = projectLocal ? path.join(homeDir, '.gemini', 'config', 'hooks') : hooksDir;
3316
+
3317
+ const bdbHooks = {
3318
+ PreToolUse: [
3319
+ {
3320
+ matcher: "run_command|Bash",
3321
+ hooks: [{ type: "command", command: `node "${path.join(hooksDir, 'go-gate.mjs')}"`, timeout: 10000 }]
3322
+ }
3323
+ ],
3324
+ Stop: [
3325
+ {
3326
+ hooks: [{ type: "command", command: `node "${path.join(hooksDir, 'graph-gate.mjs')}"`, timeout: 10000 }]
3327
+ }
3328
+ ],
3329
+ PreInvocation: [
3330
+ {
3331
+ hooks: [
3332
+ { type: "command", command: `node "${path.join(globalHooksDir, 'memb-inject.mjs')}"`, timeout: 8000 },
3333
+ { type: "command", command: `node "${path.join(workflowsDir, 'startcycle-dispatch.mjs')}"`, timeout: 30000 }
3334
+ ]
3335
+ }
3336
+ ]
3337
+ };
3338
+
3339
+ const buildMerged = (existing) => {
3340
+ const merged = existing && typeof existing === 'object' ? existing : {};
3341
+ merged.hooks = merged.hooks && typeof merged.hooks === 'object' ? merged.hooks : {};
3342
+ for (const [event, entries] of Object.entries(bdbHooks)) {
3343
+ const foreignEntries = (Array.isArray(merged.hooks[event]) ? merged.hooks[event] : [])
3344
+ .filter((e) => !isBdbEntry(e));
3345
+ merged.hooks[event] = [...foreignEntries, ...entries];
3346
+ }
3347
+ return merged;
3348
+ };
3349
+
3350
+ let existing = null;
3351
+ if (fs.existsSync(hooksPath)) {
3352
+ existing = readJsonFile(hooksPath);
3353
+ if (!existing) {
3354
+ const backupCopy = `${hooksPath}.corrupt_${timestamp}.bak`;
3355
+ const sideCarPath = `${hooksPath}.bdb-new.json`;
3356
+ let backupWritten = true;
3357
+ try {
3358
+ fs.copyFileSync(hooksPath, backupCopy);
3359
+ } catch (copyError) {
3360
+ backupWritten = false;
3361
+ log.warn(`Could not create the hooks backup: ${copyError.message}`);
3362
+ }
3363
+ try {
3364
+ fs.writeFileSync(sideCarPath, JSON.stringify(buildMerged(null), null, 2) + '\n');
3365
+ } catch (writeError) {
3366
+ log.warn(`Could not write ${path.basename(sideCarPath)}: ${writeError.message}`);
3367
+ }
3368
+ log.warn(`${path.basename(hooksPath)} is not valid JSON - hook merge skipped, nothing overwritten.`);
3369
+ if (backupWritten) log.warn(`Backup copy: ${backupCopy}`);
3370
+ log.warn(`BDB-wired hooks: ${sideCarPath}`);
3371
+ return;
3372
+ }
3373
+ }
3374
+
3375
+ try {
3376
+ fs.mkdirSync(path.dirname(hooksPath), { recursive: true });
3377
+ fs.writeFileSync(hooksPath, JSON.stringify(buildMerged(existing), null, 2) + '\n');
3378
+ } catch (e) {
3379
+ log.warn(`Could not write ${hooksPath}: ${e.message}`);
3380
+ }
3381
+ }
3382
+
3383
+ // Merge the BDB hooks into ChatGPT Codex CLI's config.toml (~/.codex/config.toml or
3384
+ // .codex/config.toml), ensuring [features] hooks = true and preserving existing
3385
+ // non-BDB settings, comments, and MCP servers.
3386
+ function mergeCodexTomlHooks(configTomlPath, { projectLocal = false } = {}) {
3387
+ const baseDir = path.dirname(configTomlPath);
3388
+ const hooksDir = projectLocal ? path.join(currentDir, '.codex', 'hooks') : path.join(baseDir, 'hooks');
3389
+ const workflowsDir = projectLocal ? path.join(currentDir, '.codex', 'workflows') : path.join(baseDir, 'workflows');
3390
+ const globalHooksDir = projectLocal ? path.join(homeDir, '.codex', 'hooks') : hooksDir;
3391
+
3392
+ const tomlSnippet = [
3393
+ '# AOS:HOOKS:START',
3394
+ '[[hooks.PreToolUse]]',
3395
+ 'matcher = "^(Bash|run_command)$"',
3396
+ '[[hooks.PreToolUse.hooks]]',
3397
+ 'type = "command"',
3398
+ `command = ${JSON.stringify(`node "${path.join(hooksDir, 'go-gate.mjs')}"`)}`,
3399
+ 'timeout = 30',
3400
+ '',
3401
+ '[[hooks.Stop]]',
3402
+ '[[hooks.Stop.hooks]]',
3403
+ 'type = "command"',
3404
+ `command = ${JSON.stringify(`node "${path.join(hooksDir, 'graph-gate.mjs')}"`)}`,
3405
+ 'timeout = 30',
3406
+ '',
3407
+ '[[hooks.UserPromptSubmit]]',
3408
+ '[[hooks.UserPromptSubmit.hooks]]',
3409
+ 'type = "command"',
3410
+ `command = ${JSON.stringify(`node "${path.join(globalHooksDir, 'memb-inject.mjs')}"`)}`,
3411
+ 'timeout = 30',
3412
+ '',
3413
+ '[[hooks.UserPromptSubmit]]',
3414
+ '[[hooks.UserPromptSubmit.hooks]]',
3415
+ 'type = "command"',
3416
+ `command = ${JSON.stringify(`node "${path.join(workflowsDir, 'startcycle-dispatch.mjs')}"`)}`,
3417
+ 'timeout = 30',
3418
+ '# AOS:HOOKS:END'
3419
+ ].join('\n');
3420
+
3421
+ let content = fs.existsSync(configTomlPath) ? fs.readFileSync(configTomlPath, 'utf8') : '';
3422
+
3423
+ // Ensure [features] hooks = true
3424
+ const hasHooksFeature = /(?:hooks|codex_hooks)\s*=\s*true/m.test(content);
3425
+ if (!hasHooksFeature) {
3426
+ if (/^\[features\]/m.test(content)) {
3427
+ content = content.replace(/^\[features\]/m, '[features]\nhooks = true');
3428
+ } else {
3429
+ content = `[features]\nhooks = true\n\n${content.trimStart()}`;
3430
+ }
3431
+ }
3432
+
3433
+ // Replace existing AOS:HOOKS block or append
3434
+ const hookBlockRegex = /# AOS:HOOKS:START[\s\S]*?# AOS:HOOKS:END/;
3435
+ if (hookBlockRegex.test(content)) {
3436
+ content = content.replace(hookBlockRegex, tomlSnippet);
3437
+ } else {
3438
+ content = `${content.trimEnd()}\n\n${tomlSnippet}\n`;
3439
+ }
3440
+
3441
+ try {
3442
+ fs.mkdirSync(path.dirname(configTomlPath), { recursive: true });
3443
+ fs.writeFileSync(configTomlPath, content, { mode: 0o600 });
3444
+ } catch (e) {
3445
+ log.warn(`Could not write ${configTomlPath}: ${e.message}`);
3446
+ }
3447
+ }
3448
+
3267
3449
  // Tier 9 (Local Project Harness): drop the dispatcher contract into the
3268
3450
  // CURRENT PROJECT only -- no writes to $HOME, no global skill/MCP sync, and
3269
3451
  // this runs as an early return BEFORE any global install step. A bare
@@ -3276,22 +3458,29 @@ function mergeBdbSettingsHooks(settingsPath, { projectLocal = false } = {}) {
3276
3458
  // non-interactively via `--project-harness` (combine with -y for CI).
3277
3459
  function installProjectHarness() {
3278
3460
  const projectClaudeDir = path.join(currentDir, '.claude');
3279
- installStep(`create ${projectClaudeDir}`, () => {
3461
+ const projectAgentsDir = path.join(currentDir, '.agents');
3462
+ const projectCodexDir = path.join(currentDir, '.codex');
3463
+
3464
+ installStep(`create harness directories`, () => {
3280
3465
  fs.mkdirSync(projectClaudeDir, { recursive: true });
3466
+ fs.mkdirSync(projectAgentsDir, { recursive: true });
3467
+ fs.mkdirSync(projectCodexDir, { recursive: true });
3281
3468
  }, 'The harness copies below will most likely fail as well.');
3282
3469
 
3283
3470
  installStep('copy .agents/ contract into project', () => {
3284
3471
  const agentsSrc = path.join(srcDir, '.agents');
3285
3472
  if (!fs.existsSync(agentsSrc)) throw new Error(`missing payload: ${agentsSrc}`);
3286
- copyDirRecursiveSync(agentsSrc, path.join(currentDir, '.agents'));
3287
- log.step(`Copied .agents/ contract to ${path.join(currentDir, '.agents')}`);
3473
+ copyDirRecursiveSync(agentsSrc, projectAgentsDir);
3474
+ log.step(`Copied .agents/ contract to ${projectAgentsDir}`);
3288
3475
  }, 'graph.md / state.schema.json may be missing in the project.');
3289
3476
 
3290
3477
  installStep('copy dispatcher workflows into project', () => {
3291
3478
  const workflowsSrc = path.join(srcDir, '.claude', 'workflows');
3292
3479
  if (fs.existsSync(workflowsSrc)) {
3293
3480
  copyDirRecursiveSync(workflowsSrc, path.join(projectClaudeDir, 'workflows'));
3294
- log.step(`Copied dispatcher workflows to ${path.join(projectClaudeDir, 'workflows')}`);
3481
+ copyDirRecursiveSync(workflowsSrc, path.join(projectAgentsDir, 'workflows'));
3482
+ copyDirRecursiveSync(workflowsSrc, path.join(projectCodexDir, 'workflows'));
3483
+ log.step(`Copied dispatcher workflows to project harnesses`);
3295
3484
  }
3296
3485
  }, '/startcycle dispatch degrades to graph.md as a manual guide.');
3297
3486
 
@@ -3299,7 +3488,9 @@ function installProjectHarness() {
3299
3488
  const hooksSrc = path.join(srcDir, '.claude', 'hooks');
3300
3489
  if (fs.existsSync(hooksSrc)) {
3301
3490
  copyDirRecursiveSync(hooksSrc, path.join(projectClaudeDir, 'hooks'));
3302
- log.step(`Copied hooks to ${path.join(projectClaudeDir, 'hooks')}`);
3491
+ copyDirRecursiveSync(hooksSrc, path.join(projectAgentsDir, 'hooks'));
3492
+ copyDirRecursiveSync(hooksSrc, path.join(projectCodexDir, 'hooks'));
3493
+ log.step(`Copied hooks to project harnesses`);
3303
3494
  }
3304
3495
  }, 'go-gate / graph-gate enforcement stays inactive in this project.');
3305
3496
 
@@ -3311,9 +3502,11 @@ function installProjectHarness() {
3311
3502
  }
3312
3503
  }, 'The dispatcher runs, but its agent-file pointers resolve to nothing.');
3313
3504
 
3314
- installStep('wire project .claude/settings.json to local hooks', () => {
3505
+ installStep('wire project harnesses to local hooks', () => {
3315
3506
  mergeBdbSettingsHooks(path.join(projectClaudeDir, 'settings.json'), { projectLocal: true });
3316
- log.step(`Wired project hooks in ${path.join(projectClaudeDir, 'settings.json')}`);
3507
+ mergeAntigravityHooks(path.join(projectAgentsDir, 'hooks.json'), { projectLocal: true });
3508
+ mergeCodexTomlHooks(path.join(projectCodexDir, 'config.toml'), { projectLocal: true });
3509
+ log.step(`Wired project hooks in Claude, Antigravity, and Codex configurations`);
3317
3510
  }, 'The gate hooks exist but are not auto-wired for this project.');
3318
3511
 
3319
3512
  log.success(`Project harness installed to ${currentDir}`);
@@ -4406,6 +4599,9 @@ module.exports = {
4406
4599
  detectPlatforms,
4407
4600
  markPlatformsExplicit,
4408
4601
  mergeBdbSettingsHooks,
4602
+ mergeAntigravityHooks,
4603
+ mergeCodexTomlHooks,
4604
+ mergeCodexHooks: mergeCodexTomlHooks,
4409
4605
  installGlobalHooks,
4410
4606
  installProjectHarness,
4411
4607
  mirrorMcpServersTo,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hybridlabor-api/aos",
3
- "version": "4.5.0",
3
+ "version": "4.5.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": {
@@ -36,15 +36,12 @@ believing memory works everywhere:
36
36
  | | Skills | MCP | Hooks |
37
37
  |---|---|---|---|
38
38
  | Claude Code | `~/.claude/skills` | `~/.claude.json` | **yes** |
39
- | Antigravity | `~/.gemini/config/skills` | `mcp_config.json` | no |
40
- | Codex | `~/.codex/skills` | `config.toml` | no |
39
+ | Antigravity | `~/.gemini/config/skills` | `mcp_config.json` | **yes** (.agents/hooks.json / hooks.json) |
40
+ | Codex | `~/.codex/skills` | `config.toml` | **yes** (config.toml) |
41
41
  | OpenCode | — | `opencode.jsonc` | no |
42
42
  | Cursor / Roo | `bdb-skills` | `mcp.json` | no |
43
43
 
44
- Only Claude Code has hooks, so only there can memB inject per prompt. Elsewhere
45
- the same context arrives through the rule files that harness loads at start —
46
- see section 5 — which means it is as fresh as the last write, not as fresh as
47
- the prompt. Say so plainly rather than letting someone assume parity.
44
+ Claude Code, Google Antigravity, and OpenAI Codex have native hook support, allowing memB to inject ambient memory per prompt across all three harnesses. For hookless harnesses (Cursor, Roo, OpenCode, Windsurf), the same context arrives through the rule files that harness loads at start — see section 5 — which means it is as fresh as the last write, not as fresh as the prompt. Say so plainly rather than letting someone assume parity.
48
45
 
49
46
  ---
50
47
 
@@ -173,7 +170,8 @@ injects the relevant memories as context. It fails open: any error exits `0`
173
170
  and the prompt proceeds untouched.
174
171
 
175
172
  Since v4.4.0 the installer ships `memb-inject.mjs` into `~/.claude/hooks/` and
176
- wires it as a `UserPromptSubmit` hook. **v4.4.0's Quick Update did not** — it
173
+ wires it as an automated prompt hook across Claude Code (`UserPromptSubmit` in `settings.json`),
174
+ Google Antigravity (`PreInvocation` in `hooks.json` / `.agents/hooks.json`), and OpenAI Codex (`UserPromptSubmit` in `config.toml`). **v4.4.0's Quick Update did not** — it
177
175
  refreshes skills and submodules, and hooks are harness plumbing rather than
178
176
  skills, so a machine that already had AOS updated to 4.4.0 without ever
179
177
  receiving the hook. Fixed in v4.4.1; a machine that took that update needs one
@@ -183,7 +181,7 @@ more run:
183
181
  npx -y @hybridlabor-api/aos@latest
184
182
  ```
185
183
 
186
- The wiring is merged into `~/.claude/settings.json`, never written over it:
184
+ The wiring is merged into `~/.claude/settings.json`, Antigravity `hooks.json`, and Codex `config.toml`, never written over them:
187
185
  user keys and foreign hook entries survive, and a re-run replaces the BDB entry
188
186
  rather than adding a second copy.
189
187
 
@@ -199,7 +197,7 @@ harness — the memB store is machine-global, and pointing it at
199
197
  `$CLAUDE_PROJECT_DIR` would make it fail on every prompt in any project the
200
198
  harness was never installed into.
201
199
 
202
- **On a harness without hooks** — everything except Claude Code — the same
200
+ **On hookless harnesses** — Cursor, Roo, OpenCode, Windsurf — native hooks are not supported, so the same
203
201
  context is written into the rule files that harness loads instead:
204
202
 
205
203
  ```bash
@@ -163,7 +163,7 @@ function checkHooks() {
163
163
  // the row would go green over a hook carrying a bug this version fixed.
164
164
  // Hooks that carry an `aos-hook-version:` line are checked against what this
165
165
  // release expects; the ones that do not are existence-only.
166
- const EXPECTED_VERSION = { 'memb-inject.mjs': 2 };
166
+ const EXPECTED_VERSION = { 'memb-inject.mjs': 3 };
167
167
  const versionOf = (text) => {
168
168
  const m = /^\/\/\s*aos-hook-version:\s*(\d+)/m.exec(text);
169
169
  return m ? Number(m[1]) : null;
@@ -195,6 +195,48 @@ function checkHooks() {
195
195
  add('hooks', `${file} wired`, wired.includes(file), wired.includes(file) ? `present in ${event}` : `not referenced in ~/.claude/settings.json`,
196
196
  'Add it under the matching hooks event in ~/.claude/settings.json (see this skill, section 4).');
197
197
  }
198
+
199
+ // Antigravity hook wiring
200
+ const agDir = h('.gemini', 'antigravity-cli');
201
+ const agHooksCandidate = existsSync(path.join(agDir, 'hooks.json'))
202
+ ? path.join(agDir, 'hooks.json')
203
+ : existsSync(h('.gemini', 'config', 'hooks.json'))
204
+ ? h('.gemini', 'config', 'hooks.json')
205
+ : existsSync(h('.agents', 'hooks.json'))
206
+ ? h('.agents', 'hooks.json')
207
+ : null;
208
+
209
+ if (existsSync(agDir) || existsSync(h('.gemini', 'config', 'skills')) || agHooksCandidate) {
210
+ const targetFile = agHooksCandidate || path.join(agDir, 'hooks.json');
211
+ if (existsSync(targetFile)) {
212
+ const agHooks = readJson(targetFile);
213
+ const agWired = JSON.stringify(agHooks?.hooks || {});
214
+ const hasMemb = agWired.includes('memb-inject.mjs');
215
+ add('hooks', 'Antigravity hooks.json wired', hasMemb,
216
+ hasMemb ? `wired in ${tilde(targetFile)}` : `memb-inject.mjs not wired in ${tilde(targetFile)}`,
217
+ 'Run installer or mergeAntigravityHooks to wire hooks in hooks.json.');
218
+ } else {
219
+ add('hooks', 'Antigravity hooks.json', false, `missing ${tilde(targetFile)}`,
220
+ 'Run installer to create and wire hooks.json for Antigravity.');
221
+ }
222
+ }
223
+
224
+ // Codex hook wiring
225
+ const codexDir = h('.codex');
226
+ const codexConf = path.join(codexDir, 'config.toml');
227
+ if (existsSync(codexDir) || existsSync(codexConf)) {
228
+ if (existsSync(codexConf)) {
229
+ let toml = '';
230
+ try { toml = readFileSync(codexConf, 'utf8'); } catch {}
231
+ const hasMemb = toml.includes('memb-inject.mjs');
232
+ add('hooks', 'Codex config.toml wired', hasMemb,
233
+ hasMemb ? `wired in ${tilde(codexConf)}` : `memb-inject.mjs not wired in ${tilde(codexConf)}`,
234
+ 'Run installer or mergeCodexTomlHooks to wire hooks in config.toml.');
235
+ } else {
236
+ add('hooks', 'Codex config.toml', false, `missing ${tilde(codexConf)}`,
237
+ 'Run installer to create and wire config.toml for Codex.');
238
+ }
239
+ }
198
240
  }
199
241
 
200
242
  // ---------------------------------------------------------------- memB
@@ -3,6 +3,8 @@
3
3
  $UserHome = [System.Environment]::GetFolderPath('UserProfile')
4
4
  $ScriptPath = "$UserHome\.gemini\config\skills\openwiki-skill\scripts\openwiki_daemon.py"
5
5
  $DaemonLogDir = "$UserHome\.openwiki"
6
+ $VenvDir = "$DaemonLogDir\venv"
7
+ $DaemonPython = ""
6
8
 
7
9
  Write-Host "=========================================================" -ForegroundColor Cyan
8
10
  Write-Host " Installing OpenWiki Background Daemon (Windows Task Scheduler)" -ForegroundColor Cyan
@@ -18,12 +20,73 @@ if (-not (Test-Path $ScriptPath)) {
18
20
  }
19
21
  }
20
22
 
21
- # 2. Install Python dependency
22
- Write-Host "Installing google-genai SDK..." -ForegroundColor Yellow
23
- try {
24
- pip install --quiet google-genai 2>$null
25
- } catch {
26
- Write-Host "Warning: pip install failed. Install google-genai manually." -ForegroundColor Yellow
23
+ # 2. Install Python dependency into a dedicated venv
24
+ Write-Host "Installing google-genai SDK into $VenvDir ..." -ForegroundColor Yellow
25
+ if (-not (Test-Path $DaemonLogDir)) {
26
+ New-Item -ItemType Directory -Force -Path $DaemonLogDir | Out-Null
27
+ }
28
+
29
+ $VenvPython = Join-Path $VenvDir "Scripts\python.exe"
30
+
31
+ $UvCmd = Get-Command uv -ErrorAction SilentlyContinue
32
+ if ($UvCmd) {
33
+ try {
34
+ & uv venv "$VenvDir" 2>&1 | Out-Null
35
+ if (Test-Path $VenvPython) {
36
+ $InstallOutput = & uv pip install --python $VenvPython --quiet google-genai 2>&1
37
+ if ($LASTEXITCODE -eq 0) {
38
+ $DaemonPython = $VenvPython
39
+ }
40
+ }
41
+ } catch {}
42
+ }
43
+
44
+ if ([string]::IsNullOrWhiteSpace($DaemonPython)) {
45
+ $SysCmd = Get-Command python -ErrorAction SilentlyContinue
46
+ if ($SysCmd) {
47
+ $SysPython = if ($SysCmd.Path) { $SysCmd.Path } elseif ($SysCmd.Source) { $SysCmd.Source } else { "python" }
48
+ try {
49
+ & $SysPython -m venv "$VenvDir" 2>&1 | Out-Null
50
+ if (Test-Path $VenvPython) {
51
+ $InstallOutput = & $VenvPython -m pip install --quiet google-genai 2>&1
52
+ if ($LASTEXITCODE -eq 0) {
53
+ $DaemonPython = $VenvPython
54
+ }
55
+ }
56
+ } catch {}
57
+ }
58
+ }
59
+
60
+ if ([string]::IsNullOrWhiteSpace($DaemonPython)) {
61
+ Write-Host "Warning: could not install google-genai into $VenvDir." -ForegroundColor Yellow
62
+ Write-Host " Reason (last attempt):" -ForegroundColor Yellow
63
+ $SysCmd = Get-Command python -ErrorAction SilentlyContinue
64
+ if ($SysCmd) {
65
+ $SysPython = if ($SysCmd.Path) { $SysCmd.Path } elseif ($SysCmd.Source) { $SysCmd.Source } else { "python" }
66
+ if (Test-Path $VenvPython) {
67
+ $diagOutput = & $VenvPython -m pip install google-genai 2>&1
68
+ if ($diagOutput) {
69
+ $diagOutput | Select-Object -First 8 | ForEach-Object { Write-Host " $_" -ForegroundColor DarkGray }
70
+ }
71
+ } else {
72
+ $diagVenv = & $SysPython -m venv "$VenvDir" 2>&1
73
+ if ($diagVenv) {
74
+ $diagVenv | Select-Object -First 5 | ForEach-Object { Write-Host " $_" -ForegroundColor DarkGray }
75
+ }
76
+ }
77
+ } else {
78
+ Write-Host " python not found on PATH." -ForegroundColor DarkGray
79
+ }
80
+ Write-Host " The daemon will run in collect-only mode until this is resolved." -ForegroundColor Yellow
81
+ if (Test-Path $VenvPython) {
82
+ $DaemonPython = $VenvPython
83
+ } elseif ($cmd = Get-Command python -ErrorAction SilentlyContinue) {
84
+ $DaemonPython = if ($cmd.Path) { $cmd.Path } elseif ($cmd.Source) { $cmd.Source } else { "python" }
85
+ } else {
86
+ $DaemonPython = "python"
87
+ }
88
+ } else {
89
+ Write-Host " -> google-genai installed; daemon will run under $DaemonPython" -ForegroundColor Green
27
90
  }
28
91
 
29
92
  # 3. Resolve provider and API key
@@ -63,7 +126,10 @@ if ($OpenwikiProvider -eq "google") {
63
126
  # and is discarded below.
64
127
  $env:GEMINI_API_KEY = $ApiKey
65
128
  $VerifyScript = Join-Path $PSScriptRoot "verify_api_key.py"
66
- $VerifyOutput = & python $VerifyScript 2>&1
129
+ if (-not (Test-Path $VerifyScript)) {
130
+ $VerifyScript = Join-Path (Split-Path $ScriptPath -Parent) "verify_api_key.py"
131
+ }
132
+ $VerifyOutput = & $DaemonPython $VerifyScript 2>&1
67
133
  $VerifyCode = $LASTEXITCODE
68
134
  if ($VerifyCode -eq 0 -and ($VerifyOutput -match "VERIFIED_OK")) {
69
135
  Write-Host " -> API key verified." -ForegroundColor Green
@@ -118,7 +184,7 @@ if (-not (Test-Path $DaemonLogDir)) {
118
184
  # write the key in clear text into the .cmd under
119
185
  # %APPDATA%\Microsoft\Windows\Start Menu\Programs\Startup\, readable by any
120
186
  # process in the user context and routinely swept up by backups and sync folders.
121
- $DaemonCommand = "& { python '$ScriptPath' --one-shot }"
187
+ $DaemonCommand = "& { & '$DaemonPython' '$ScriptPath' --one-shot }"
122
188
  $Action = New-ScheduledTaskAction -Execute "powershell.exe" -Argument "-NoProfile -WindowStyle Hidden -Command `"$DaemonCommand`""
123
189
 
124
190
  # 6. Trigger: every 2 hours via repetition
@@ -151,7 +217,7 @@ function Install-StartupFallback {
151
217
  Write-Host " * startup entry -> runs ONCE per logon (--one-shot)" -ForegroundColor Yellow
152
218
  Write-Host " Documentation is refreshed only when you log in, or when you run" -ForegroundColor Yellow
153
219
  Write-Host " the daemon manually:" -ForegroundColor Yellow
154
- Write-Host " python `"$ScriptPath`" --one-shot" -ForegroundColor DarkGray
220
+ Write-Host " & `"$DaemonPython`" `"$ScriptPath`" --one-shot" -ForegroundColor DarkGray
155
221
  Write-Host " To get the 2-hour schedule, re-run this installer from an elevated" -ForegroundColor Yellow
156
222
  Write-Host " PowerShell (Run as administrator)." -ForegroundColor Yellow
157
223
  }
@@ -191,17 +257,40 @@ if ($BlockReason -ne "") {
191
257
  Write-Host " a logon-only startup entry is used instead." -ForegroundColor DarkGray
192
258
  }
193
259
  Write-Host "Registering task '$TaskName'..." -ForegroundColor Yellow
194
- try {
195
- Register-ScheduledTask -TaskName $TaskName -Action $Action -Trigger $Trigger -Settings $Settings -Description "BDB OpenWiki Daemon - Gemma 4 API documentation generator" -Force -ErrorAction Stop | Out-Null
196
- $Registered = $true
260
+
261
+ $CurrentUserName = try { [System.Security.Principal.WindowsIdentity]::GetCurrent().Name } catch { $env:USERNAME }
262
+ $UserPrincipal = try {
263
+ New-ScheduledTaskPrincipal -UserId $CurrentUserName -LogonType Interactive
197
264
  } catch {
198
- $FailureMessage = $_.Exception.Message
199
- Write-Warning "Scheduled task registration failed ($FailureMessage)."
200
- $Reason = "scheduled task registration failed: $FailureMessage"
201
- if (-not $IsElevated) {
202
- $Reason = "$Reason (administrator rights are likely required on this system)"
265
+ $null
266
+ }
267
+
268
+ $TaskRegistered = $false
269
+ # First attempt: register using interactive user principal (needed for non-elevated users)
270
+ if ($UserPrincipal) {
271
+ try {
272
+ Register-ScheduledTask -TaskName $TaskName -Action $Action -Trigger $Trigger -Settings $Settings -Principal $UserPrincipal -Description "BDB OpenWiki Daemon - Gemma 4 API documentation generator" -Force -ErrorAction Stop | Out-Null
273
+ $TaskRegistered = $true
274
+ $Registered = $true
275
+ } catch {
276
+ Write-Verbose "Registration with interactive principal failed: $($_.Exception.Message). Trying default registration..."
277
+ }
278
+ }
279
+
280
+ # Second attempt: standard registration if principal registration was skipped or failed
281
+ if (-not $TaskRegistered) {
282
+ try {
283
+ Register-ScheduledTask -TaskName $TaskName -Action $Action -Trigger $Trigger -Settings $Settings -Description "BDB OpenWiki Daemon - Gemma 4 API documentation generator" -Force -ErrorAction Stop | Out-Null
284
+ $Registered = $true
285
+ } catch {
286
+ $FailureMessage = $_.Exception.Message
287
+ Write-Warning "Scheduled task registration failed ($FailureMessage)."
288
+ $Reason = "scheduled task registration failed: $FailureMessage"
289
+ if (-not $IsElevated) {
290
+ $Reason = "$Reason (administrator rights are likely required on this system)"
291
+ }
292
+ Install-StartupFallback -Command $DaemonCommand -Reason $Reason
203
293
  }
204
- Install-StartupFallback -Command $DaemonCommand -Reason $Reason
205
294
  }
206
295
  }
207
296