@hybridlabor-api/aos 4.8.0 → 4.10.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 (186) hide show
  1. package/.agents/{agents.md → AGENTS.md} +3 -1
  2. package/.agents/nodes.json +3 -1
  3. package/.agents/vendor-manifest.json +23 -1
  4. package/.claude/agents/godmode-media-eventtech.md +1 -1
  5. package/.claude/hooks/conventional-commits.mjs +125 -0
  6. package/.claude/hooks/env-file-protection.mjs +105 -0
  7. package/.claude/hooks/go-gate.mjs +101 -81
  8. package/.claude/hooks/memb-inject.mjs +29 -1
  9. package/.claude/settings.json +13 -0
  10. package/.claude/workflows/startcycle-dispatch.mjs +23 -1
  11. package/.opencode/agents/godmode-media-eventtech.md +1 -1
  12. package/.opencode/plugins/bdb-aos.js +31 -4
  13. package/CLAUDE.md +0 -571
  14. package/README.de.md +1 -1
  15. package/README.md +1 -1
  16. package/README.pt.md +1 -1
  17. package/THIRD_PARTY_NOTICES.md +126 -0
  18. package/bin/aos-doctor.mjs +1 -1
  19. package/docs/skills_table.md +1 -1
  20. package/installer.js +187 -55
  21. package/package.json +7 -3
  22. package/packages/aos-cli/README.md +80 -0
  23. package/packages/aos-cli/bin/aos-cli.mjs +134 -0
  24. package/packages/aos-cli/core-skills.json +12 -0
  25. package/packages/aos-cli/extensions/aos.ts +321 -0
  26. package/packages/aos-cli/package-lock.json +1923 -0
  27. package/packages/aos-cli/package.json +29 -0
  28. package/packages/aos-cli/scripts/check-theme.mjs +63 -0
  29. package/packages/aos-cli/themes/aos.json +97 -0
  30. package/scripts/build-plugin-manifest.mjs +131 -0
  31. package/scripts/validate-skills.mjs +81 -6
  32. package/skills/basic/ao-orchestrator/SKILL.md +116 -0
  33. package/skills/basic/bdb-eventagency-skill/SKILL.md +252 -0
  34. package/skills/basic/bdb-shipping-skill/SKILL.md +161 -0
  35. package/skills/basic/godmode-eventtech/SKILL.md +4 -1
  36. package/skills/global_config/agenttrail/SKILL.md +6 -1
  37. package/skills/global_config/aos-project-init/SKILL.md +2 -0
  38. package/skills/global_config/aos-project-init/assets/AGENTS.template.md +1 -1
  39. package/skills/global_config/aos-project-init/scripts/aos-project-doctor.mjs +1 -1
  40. package/skills/global_config/aos-setup/SKILL.md +1 -1
  41. package/skills/global_config/aos-setup/scripts/aos-doctor.mjs +1 -1
  42. package/skills/global_config/ask-tim/SKILL.md +7 -7
  43. package/skills/global_config/bash-script-generator/SKILL.md +201 -0
  44. package/skills/global_config/bash-script-generator/assets/templates/standard-template.sh +96 -0
  45. package/skills/global_config/bash-script-generator/docs/bash-scripting-guide.md +729 -0
  46. package/skills/global_config/bash-script-generator/docs/generation-best-practices.md +193 -0
  47. package/skills/global_config/bash-script-generator/docs/script-patterns.md +566 -0
  48. package/skills/global_config/bash-script-generator/docs/text-processing-guide.md +437 -0
  49. package/skills/global_config/bash-script-generator/examples/log-analyzer.sh +92 -0
  50. package/skills/global_config/bash-script-generator/scripts/generate_script_template.sh +123 -0
  51. package/skills/global_config/bash-script-generator/scripts/run_ci_checks.sh +172 -0
  52. package/skills/global_config/bash-script-generator/scripts/test_generator.sh +413 -0
  53. package/skills/global_config/bash-script-validator/SKILL.md +249 -0
  54. package/skills/global_config/bash-script-validator/docs/awk-reference.md +449 -0
  55. package/skills/global_config/bash-script-validator/docs/bash-reference.md +468 -0
  56. package/skills/global_config/bash-script-validator/docs/common-mistakes.md +623 -0
  57. package/skills/global_config/bash-script-validator/docs/grep-reference.md +395 -0
  58. package/skills/global_config/bash-script-validator/docs/regex-reference.md +391 -0
  59. package/skills/global_config/bash-script-validator/docs/sed-reference.md +454 -0
  60. package/skills/global_config/bash-script-validator/docs/shell-reference.md +463 -0
  61. package/skills/global_config/bash-script-validator/docs/shellcheck-reference.md +399 -0
  62. package/skills/global_config/bash-script-validator/examples/bad-bash.sh +55 -0
  63. package/skills/global_config/bash-script-validator/examples/bad-shell.sh +54 -0
  64. package/skills/global_config/bash-script-validator/examples/good-bash.sh +71 -0
  65. package/skills/global_config/bash-script-validator/examples/good-shell.sh +69 -0
  66. package/skills/global_config/bash-script-validator/scripts/run_ci_checks.sh +23 -0
  67. package/skills/global_config/bash-script-validator/scripts/shellcheck_wrapper.sh +174 -0
  68. package/skills/global_config/bash-script-validator/scripts/test_validate.sh +446 -0
  69. package/skills/global_config/bash-script-validator/scripts/validate.sh +512 -0
  70. package/skills/global_config/ci-pipeline/SKILL.md +135 -0
  71. package/skills/global_config/deja-memory/SKILL.md +3 -1
  72. package/skills/global_config/dispatching-parallel-agents/SKILL.md +170 -0
  73. package/skills/global_config/dockerfile-generator/SKILL.md +1038 -0
  74. package/skills/global_config/dockerfile-generator/examples/example.dockerignore +95 -0
  75. package/skills/global_config/dockerfile-generator/examples/golang-distroless.Dockerfile +34 -0
  76. package/skills/global_config/dockerfile-generator/examples/java-springboot.Dockerfile +45 -0
  77. package/skills/global_config/dockerfile-generator/examples/nextjs-production.Dockerfile +49 -0
  78. package/skills/global_config/dockerfile-generator/examples/nodejs-multistage.Dockerfile +55 -0
  79. package/skills/global_config/dockerfile-generator/examples/python-fastapi.Dockerfile +48 -0
  80. package/skills/global_config/dockerfile-generator/references/language_specific_guides.md +510 -0
  81. package/skills/global_config/dockerfile-generator/references/multistage_builds.md +570 -0
  82. package/skills/global_config/dockerfile-generator/references/optimization_patterns.md +492 -0
  83. package/skills/global_config/dockerfile-generator/references/security_best_practices.md +375 -0
  84. package/skills/global_config/dockerfile-generator/scripts/generate_dockerignore.sh +199 -0
  85. package/skills/global_config/dockerfile-generator/scripts/generate_golang.sh +172 -0
  86. package/skills/global_config/dockerfile-generator/scripts/generate_java.sh +185 -0
  87. package/skills/global_config/dockerfile-generator/scripts/generate_nodejs.sh +279 -0
  88. package/skills/global_config/dockerfile-generator/scripts/generate_python.sh +218 -0
  89. package/skills/global_config/dockerfile-generator/scripts/test_generator.sh +210 -0
  90. package/skills/global_config/dockerfile-validator/SKILL.md +300 -0
  91. package/skills/global_config/dockerfile-validator/examples/.dockerignore.example +34 -0
  92. package/skills/global_config/dockerfile-validator/examples/bad-example.Dockerfile +37 -0
  93. package/skills/global_config/dockerfile-validator/examples/golang-distroless.Dockerfile +50 -0
  94. package/skills/global_config/dockerfile-validator/examples/good-example.Dockerfile +52 -0
  95. package/skills/global_config/dockerfile-validator/examples/python-optimized.Dockerfile +53 -0
  96. package/skills/global_config/dockerfile-validator/examples/security-issues.Dockerfile +42 -0
  97. package/skills/global_config/dockerfile-validator/references/docker_best_practices.md +348 -0
  98. package/skills/global_config/dockerfile-validator/references/optimization_guide.md +473 -0
  99. package/skills/global_config/dockerfile-validator/references/security_checklist.md +208 -0
  100. package/skills/global_config/dockerfile-validator/scripts/dockerfile-validate.sh +699 -0
  101. package/skills/global_config/dockerfile-validator/scripts/test_validate.sh +35 -0
  102. package/skills/global_config/dockerfile-validator/tests/fixtures/copy-before-yarn-lock-read.Dockerfile +6 -0
  103. package/skills/global_config/dockerfile-validator/tests/fixtures/copy-before-yarn.Dockerfile +6 -0
  104. package/skills/global_config/dockerfile-validator/tests/fixtures/from-platform-nonroot.Dockerfile +7 -0
  105. package/skills/global_config/dockerfile-validator/tests/test_regression.sh +164 -0
  106. package/skills/global_config/finishing-a-development-branch/SKILL.md +228 -0
  107. package/skills/global_config/github-actions-generator/SKILL.md +353 -0
  108. package/skills/global_config/github-actions-generator/assets/templates/action/composite/action.yml +82 -0
  109. package/skills/global_config/github-actions-generator/assets/templates/action/docker/Dockerfile +25 -0
  110. package/skills/global_config/github-actions-generator/assets/templates/action/docker/action.yml +42 -0
  111. package/skills/global_config/github-actions-generator/assets/templates/action/docker/entrypoint.sh +27 -0
  112. package/skills/global_config/github-actions-generator/assets/templates/action/javascript/action.yml +33 -0
  113. package/skills/global_config/github-actions-generator/assets/templates/action/javascript/index.js +50 -0
  114. package/skills/global_config/github-actions-generator/assets/templates/action/javascript/package.json +27 -0
  115. package/skills/global_config/github-actions-generator/assets/templates/workflow/basic_workflow.yml +242 -0
  116. package/skills/global_config/github-actions-generator/assets/templates/workflow/reusable_workflow.yml +106 -0
  117. package/skills/global_config/github-actions-generator/examples/README.md +147 -0
  118. package/skills/global_config/github-actions-generator/examples/actions/setup-node-cached/action.yml +93 -0
  119. package/skills/global_config/github-actions-generator/examples/caching/docker-buildkit.yml +256 -0
  120. package/skills/global_config/github-actions-generator/examples/security/dependency-review.yml +62 -0
  121. package/skills/global_config/github-actions-generator/examples/security/sbom-attestation.yml +119 -0
  122. package/skills/global_config/github-actions-generator/examples/triggers/chatops-commands.yml +475 -0
  123. package/skills/global_config/github-actions-generator/examples/triggers/repository-dispatch.yml +418 -0
  124. package/skills/global_config/github-actions-generator/examples/triggers/workflow-orchestration.yml +404 -0
  125. package/skills/global_config/github-actions-generator/examples/workflows/docker-build-push.yml +68 -0
  126. package/skills/global_config/github-actions-generator/examples/workflows/go-ci.yml +161 -0
  127. package/skills/global_config/github-actions-generator/examples/workflows/monorepo-ci.yml +340 -0
  128. package/skills/global_config/github-actions-generator/examples/workflows/multi-environment-deploy.yml +406 -0
  129. package/skills/global_config/github-actions-generator/examples/workflows/nodejs-ci.yml +122 -0
  130. package/skills/global_config/github-actions-generator/examples/workflows/python-ci.yml +157 -0
  131. package/skills/global_config/github-actions-generator/examples/workflows/scheduled-tasks.yml +376 -0
  132. package/skills/global_config/github-actions-generator/references/advanced-triggers.md +917 -0
  133. package/skills/global_config/github-actions-generator/references/best-practices.md +755 -0
  134. package/skills/global_config/github-actions-generator/references/common-actions.md +715 -0
  135. package/skills/global_config/github-actions-generator/references/custom-actions.md +320 -0
  136. package/skills/global_config/github-actions-generator/references/expressions-and-contexts.md +688 -0
  137. package/skills/global_config/github-actions-generator/references/modern-features.md +421 -0
  138. package/skills/global_config/github-actions-generator/scripts/test_generator.sh +344 -0
  139. package/skills/global_config/github-actions-templates/SKILL.md +7 -0
  140. package/skills/global_config/github-actions-validator/SKILL.md +576 -0
  141. package/skills/global_config/github-actions-validator/examples/README.md +88 -0
  142. package/skills/global_config/github-actions-validator/examples/outdated-versions.yml +76 -0
  143. package/skills/global_config/github-actions-validator/examples/valid-ci.yml +79 -0
  144. package/skills/global_config/github-actions-validator/examples/with-errors.yml +47 -0
  145. package/skills/global_config/github-actions-validator/references/act_usage.md +233 -0
  146. package/skills/global_config/github-actions-validator/references/action_versions.md +122 -0
  147. package/skills/global_config/github-actions-validator/references/actionlint_usage.md +343 -0
  148. package/skills/global_config/github-actions-validator/references/common_errors.md +512 -0
  149. package/skills/global_config/github-actions-validator/references/modern_features.md +384 -0
  150. package/skills/global_config/github-actions-validator/references/runners.md +317 -0
  151. package/skills/global_config/github-actions-validator/scripts/install_tools.sh +113 -0
  152. package/skills/global_config/github-actions-validator/scripts/validate_workflow.sh +910 -0
  153. package/skills/global_config/github-actions-validator/tests/test_validate_workflow.sh +237 -0
  154. package/skills/global_config/makefile-generator/SKILL.md +614 -0
  155. package/skills/global_config/makefile-generator/assets/templates/.gitkeep +1 -0
  156. package/skills/global_config/makefile-generator/docs/makefile-structure.md +530 -0
  157. package/skills/global_config/makefile-generator/docs/optimization-guide.md +784 -0
  158. package/skills/global_config/makefile-generator/docs/patterns-guide.md +642 -0
  159. package/skills/global_config/makefile-generator/docs/security-guide.md +361 -0
  160. package/skills/global_config/makefile-generator/docs/targets-guide.md +642 -0
  161. package/skills/global_config/makefile-generator/docs/variables-guide.md +596 -0
  162. package/skills/global_config/makefile-generator/scripts/add_standard_targets.sh +539 -0
  163. package/skills/global_config/makefile-generator/scripts/generate_makefile_template.sh +690 -0
  164. package/skills/global_config/makefile-generator/test/test_helper_scripts.sh +190 -0
  165. package/skills/global_config/makefile-validator/SKILL.md +244 -0
  166. package/skills/global_config/makefile-validator/docs/bake-tool.md +1000 -0
  167. package/skills/global_config/makefile-validator/docs/best-practices.md +858 -0
  168. package/skills/global_config/makefile-validator/docs/common-mistakes.md +944 -0
  169. package/skills/global_config/makefile-validator/examples/bad-makefile.mk +77 -0
  170. package/skills/global_config/makefile-validator/examples/good-makefile.mk +103 -0
  171. package/skills/global_config/makefile-validator/scripts/test_validate.sh +382 -0
  172. package/skills/global_config/makefile-validator/scripts/validate_makefile.sh +712 -0
  173. package/skills/global_config/{MCP_Manage → mcp-manage}/SKILL.md +3 -3
  174. package/skills/global_config/plan-canvas/SKILL.md +9 -2
  175. package/skills/global_config/plan-canvas/scripts/lib/plan-canvas/ui.js +10 -2
  176. package/skills/global_config/plan-canvas/scripts/plan-canvas.js +1 -1
  177. package/skills/global_config/read-the-damn-docs/SKILL.md +175 -0
  178. package/skills/global_config/requesting-code-review/SKILL.md +98 -0
  179. package/skills/global_config/requesting-code-review/code-reviewer.md +198 -0
  180. package/skills/global_config/using-git-worktrees/SKILL.md +170 -0
  181. package/skills/global_config/verification-before-completion/SKILL.md +123 -0
  182. package/skills/global_config/writing-plans/SKILL.md +126 -46
  183. package/skills/global_config/writing-plans-legacy/SKILL.md +152 -0
  184. package/.claude/CLAUDE.md +0 -12
  185. package/mcps/RhinoMCP/docs/content/docs/getting-started/gemini.md +0 -61
  186. /package/{GEMINI.md → RULES.md} +0 -0
@@ -15,6 +15,7 @@ next. This file defines *what each agent is*, not *what calls what*.
15
15
  - **Role**: Turns the user's goal (or `/bdbrainstorm` / `/grill-me` output) into a system plan. Reads existing architecture before proposing changes. Does not coordinate execution or invoke other agents — that is TechLead's job, decided by the dispatcher, not by Architect.
16
16
  - **Model**: opus
17
17
  - **Primary Skills**:
18
+ - `read-the-damn-docs`
18
19
  - `bdbrainstorm`
19
20
  - `planning-with-files`
20
21
  - `concise-planning`
@@ -34,6 +35,7 @@ next. This file defines *what each agent is*, not *what calls what*.
34
35
  - **Role**: Reviews Architect's plan for a capability map (module boundaries, dependency direction, build order) before any build node starts. Approves or rejects the plan back to Architect. Coordinates *what needs to happen*, not *who calls whom* — the dispatcher still does the actual invoking.
35
36
  - **Model**: opus
36
37
  - **Primary Skills**:
38
+ - `read-the-damn-docs`
37
39
  - `startcycle-graph`
38
40
  - `agent-pipeline`
39
41
  - `subagent-driven-development`
@@ -93,7 +95,7 @@ next. This file defines *what each agent is*, not *what calls what*.
93
95
  - `godmode-media-creation`
94
96
  - `godmode-3d-creation`
95
97
  - `bdbmediastorm`
96
- - `MCP_Manage`
98
+ - `mcp-manage`
97
99
  - `threejs-skills`
98
100
  - `spline-3d-integration`
99
101
  - **MCP Servers**:
@@ -12,6 +12,7 @@
12
12
  "writes": null,
13
13
  "optional": false,
14
14
  "skills": [
15
+ "read-the-damn-docs",
15
16
  "bdbrainstorm",
16
17
  "planning-with-files",
17
18
  "concise-planning",
@@ -29,6 +30,7 @@
29
30
  "writes": null,
30
31
  "optional": false,
31
32
  "skills": [
33
+ "read-the-damn-docs",
32
34
  "startcycle-graph",
33
35
  "agent-pipeline",
34
36
  "subagent-driven-development"
@@ -94,7 +96,7 @@
94
96
  "godmode-media-creation",
95
97
  "godmode-3d-creation",
96
98
  "bdbmediastorm",
97
- "MCP_Manage",
99
+ "mcp-manage",
98
100
  "threejs-skills",
99
101
  "spline-3d-integration"
100
102
  ],
@@ -2,7 +2,7 @@
2
2
  "$schema": "https://json-schema.org/draft/2020-12/schema",
3
3
  "version": "1.0.0",
4
4
  "description": "Pinned upstream vendor repositories tracked for AOS skills and tools",
5
- "updated_at": "2026-09-24T07:27:44Z",
5
+ "updated_at": "2026-09-25T19:27:14Z",
6
6
  "vendors": {
7
7
  "superdesign-skill": {
8
8
  "upstream": "https://github.com/superdesigndev/superdesign-skill",
@@ -51,6 +51,28 @@
51
51
  "pinned_commit": "e482e579415fde18357cafce70f177ae19fd7f03",
52
52
  "purpose": "Markdown-only fallback store for skills and auxiliary subagents",
53
53
  "target": "lib/ecc-store-index.json"
54
+ },
55
+ "superpowers": {
56
+ "upstream": "https://github.com/obra/superpowers",
57
+ "pinned_commit": "8ca22dba9a94f28898bbce59f2537ff4d87c747d",
58
+ "purpose": "Evidence-before-claims, code review dispatch, branch finishing, worktree isolation and parallel agent dispatch workflow skills",
59
+ "target_skill": "skills/global_config/{verification-before-completion,requesting-code-review,finishing-a-development-branch,using-git-worktrees,dispatching-parallel-agents}"
60
+ },
61
+ "cc-devops-skills": {
62
+ "upstream": "https://github.com/akin-ozer/cc-devops-skills",
63
+ "pinned_commit": "276af751e659315aaf56d3ad13d7c26f4e72e28a",
64
+ "purpose": "Generator/validator pairs for GitHub Actions, Dockerfile, bash scripts and Makefiles, with reference libraries",
65
+ "target_skill": "skills/global_config/{github-actions,dockerfile,bash-script,makefile}-{generator,validator}"
66
+ },
67
+ "claude-code-templates": {
68
+ "upstream": "https://github.com/davila7/claude-code-templates",
69
+ "pinned_commit": "691832ad9c2506d15a8b752826135f0afa83d197",
70
+ "purpose": "Conventional-commit enforcement and .env write protection, ported from Python to .mjs for both Claude Code and Antigravity",
71
+ "target": ".claude/hooks/{conventional-commits,env-file-protection}.mjs",
72
+ "source_commits": {
73
+ "conventional-commits.py": "92fdaa1f87b45cfc7b881f03ee69d2bad27b09d3",
74
+ "env-file-protection.json": "36fa6dbd5d8a45a62628b634aa48410f4d39682d"
75
+ }
54
76
  }
55
77
  }
56
78
  }
@@ -5,7 +5,7 @@ model: sonnet
5
5
  ---
6
6
  Creative-Tech & Show-Control Specialist. Governs 3D modeling, spatial design, TouchDesigner networks, Unreal Engine scenes, DaVinci Resolve color/edit, DMX/grandMA3 lighting, and Resolume media servers.
7
7
 
8
- **Primary skills:** godmode-eventtech, godmode-media-creation, godmode-3d-creation, bdbmediastorm, MCP_Manage, threejs-skills, spline-3d-integration
8
+ **Primary skills:** godmode-eventtech, godmode-media-creation, godmode-3d-creation, bdbmediastorm, mcp-manage, threejs-skills, spline-3d-integration
9
9
 
10
10
  **MCP servers used:** bdb_td_minddesigner, bdb_unreal_mcp, bdb_davinci_mcp, bdb_grandma3_mcp, bdb_resolume_mcp, adobe_uxp_mcp, bdb_after_effects_mcp, bdb_rhino_mcp
11
11
 
@@ -0,0 +1,125 @@
1
+ #!/usr/bin/env node
2
+ // PreToolUse gate that enforces Conventional Commits on `git commit` across harnesses:
3
+ // - Claude Code (.claude/hooks/conventional-commits.mjs)
4
+ // - Antigravity / Gemini CLI (hooks.json PreToolUse)
5
+ //
6
+ // Why this exists: release-please (.github/workflows/release-please.yml) parses
7
+ // commit subjects to compute the version bump and the changelog. It only reads
8
+ // Conventional Commit prefixes, so an unprefixed subject is invisible to it —
9
+ // exactly the prefix used for a user-facing addition decides a minor bump.
10
+ //
11
+ // Ported from davila7/claude-code-templates (MIT), which ships this as
12
+ // conventional-commits.py. Ported to .mjs rather than vendored verbatim: every
13
+ // other AOS hook is .mjs, node is already a hard install dependency, and
14
+ // `python3` does not exist on a stock Windows install — where the AOS installer
15
+ // does run — so a python hook would break every commit there.
16
+ //
17
+ // Deliberately fails OPEN, unlike go-gate. A regex that cannot read the message
18
+ // must not block a legitimate commit; blocking a human's work is the worse
19
+ // failure here, because the consequence (history AOS does not recognise) is
20
+ // recoverable with a rebase, and a wedged commit flow is not.
21
+
22
+ import { readFileSync } from "node:fs";
23
+
24
+ const TYPES = ["feat", "fix", "docs", "style", "refactor", "perf", "test", "chore", "ci", "build", "revert"];
25
+
26
+ const CONVENTIONAL = new RegExp(`^(${TYPES.join("|")})(\\(.+\\))?:\\s.+`);
27
+
28
+ const COMMIT = /git\s+commit\b/;
29
+
30
+ function readStdin() {
31
+ try {
32
+ return readFileSync(0, "utf8");
33
+ } catch {
34
+ return "";
35
+ }
36
+ }
37
+
38
+ function respond(isAgy, allowed, reason = "", message = "") {
39
+ if (isAgy) {
40
+ if (allowed) {
41
+ console.log(JSON.stringify({ decision: "allow" }));
42
+ } else {
43
+ console.log(JSON.stringify({ decision: "deny", reason }));
44
+ }
45
+ process.exit(0);
46
+ } else {
47
+ if (allowed) {
48
+ process.exit(0);
49
+ } else {
50
+ process.stderr.write(`${reason}\n`);
51
+ process.exit(2);
52
+ }
53
+ }
54
+ }
55
+
56
+ // Returns the commit message, or null when it cannot be read. Null means
57
+ // "cannot tell" and must be treated as allowed.
58
+ function extractMessage(command) {
59
+ // -m "msg" / -m 'msg'
60
+ const inline = command.match(/git\s+commit[\s\S]*?-m\s+(?:"([^"]+)"|'([^']+)')/);
61
+ if (inline) return inline[1] ?? inline[2];
62
+
63
+ // -m "$(cat <<'EOF' ... EOF)"
64
+ const heredoc = command.match(/git\s+commit[\s\S]*?-m\s+"?\$\(cat\s+<<['"]?EOF['"]?\s*\n([\s\S]+?)\nEOF/);
65
+ if (heredoc) return heredoc[1].trim();
66
+
67
+ // Plain `git commit` (editor) or a message the hook cannot see: fail open.
68
+ return null;
69
+ }
70
+
71
+ function reasonFor(message) {
72
+ return [
73
+ "Blocked by conventional-commits hook: this commit subject is not in Conventional Commits format.",
74
+ "",
75
+ `Your message: ${message.slice(0, 120)}`,
76
+ "",
77
+ "Required: type(scope): description",
78
+ `Types: ${TYPES.join(", ")}`,
79
+ "",
80
+ "Good: feat: add user authentication",
81
+ "Good: fix(api): handle null responses",
82
+ "Bad: Added new feature (no type)",
83
+ "Bad: feature: add login (wrong type, use 'feat')",
84
+ "",
85
+ "AOS reserves feat: for user-facing additions -- it always bumps the minor",
86
+ "version, however small. Housekeeping belongs under chore: or fix:.",
87
+ ].join("\n");
88
+ }
89
+
90
+ function main() {
91
+ const rawInput = readStdin();
92
+ let input = {};
93
+ if (rawInput.trim()) {
94
+ try {
95
+ input = JSON.parse(rawInput);
96
+ } catch {
97
+ // Unparseable input: fail open, no stdout (isAgy unknown).
98
+ process.exit(0);
99
+ }
100
+ }
101
+
102
+ const isAgy = !!(input?.toolCall || input?.conversationId || input?.artifactDirectoryPath || input?.workspacePaths);
103
+
104
+ const command =
105
+ input?.toolCall?.args?.CommandLine ||
106
+ input?.toolCall?.args?.command ||
107
+ input?.tool_input?.command ||
108
+ input?.command ||
109
+ "";
110
+
111
+ if (typeof command !== "string" || !COMMIT.test(command)) {
112
+ respond(isAgy, true);
113
+ return;
114
+ }
115
+
116
+ const message = extractMessage(command);
117
+ if (message === null || CONVENTIONAL.test(message)) {
118
+ respond(isAgy, true);
119
+ return;
120
+ }
121
+
122
+ respond(isAgy, false, reasonFor(message), message);
123
+ }
124
+
125
+ main();
@@ -0,0 +1,105 @@
1
+ #!/usr/bin/env node
2
+ // PreToolUse gate that refuses writes and edits to .env files, across harnesses:
3
+ // - Claude Code (.claude/hooks/env-file-protection.mjs)
4
+ // - Antigravity / Gemini CLI (hooks.json PreToolUse)
5
+ //
6
+ // Why: a .env is a live secret store. An agent that rewrites one to "add a
7
+ // variable" routinely reorders it, drops a line, or writes an empty value
8
+ // straight over a real credential — and the file is usually gitignored, so
9
+ // nothing catches it until something fails to authenticate in production.
10
+ //
11
+ // Upstream (davila7/claude-code-templates, MIT) does this as an inline `echo`
12
+ // guarded by a matcher `if` field. That is Claude-Code-only: Antigravity and
13
+ // the Gemini CLI ignore `if`, so the same entry would fire on every single
14
+ // Write and block all file creation. Implemented as a script with the path
15
+ // check in code, so one implementation behaves correctly on both.
16
+ //
17
+ // Deliberately fails OPEN. A hook that cannot read its input must not wedge
18
+ // every write in the session; missing a protection is recoverable, freezing
19
+ // the agent is not.
20
+
21
+ import { readFileSync } from "node:fs";
22
+ import { basename } from "node:path";
23
+
24
+ // Templates are committed on purpose and hold no live secret. Blocking them
25
+ // would contradict this hook's own advice below ("write it to .env.example").
26
+ const TEMPLATE_FILE = /^\.env\.(example|sample|template|dist)$/i;
27
+ const ENV_FILE = /^\.env(\..*)?$/i;
28
+
29
+ function readStdin() {
30
+ try {
31
+ return readFileSync(0, "utf8");
32
+ } catch {
33
+ return "";
34
+ }
35
+ }
36
+
37
+ function respond(isAgy, allowed, reason = "") {
38
+ if (isAgy) {
39
+ console.log(JSON.stringify(allowed ? { decision: "allow" } : { decision: "deny", reason }));
40
+ process.exit(0);
41
+ } else {
42
+ if (allowed) {
43
+ process.exit(0);
44
+ } else {
45
+ process.stderr.write(`${reason}\n`);
46
+ process.exit(2);
47
+ }
48
+ }
49
+ }
50
+
51
+ function reasonFor(filePath) {
52
+ return [
53
+ "Blocked by env-file-protection hook: writes to .env files are protected.",
54
+ "",
55
+ `Target: ${filePath}`,
56
+ "",
57
+ "A .env holds live credentials and is almost always gitignored, so an",
58
+ "overwrite fails silently and surfaces later as an auth error. To change",
59
+ "one anyway, either:",
60
+ "",
61
+ ` 1. Edit it yourself outside the agent session, or`,
62
+ " 2. Ask for the new value to be written to .env.example (safe to commit)",
63
+ " and set the real value from your own shell.",
64
+ "",
65
+ "If the repo keeps a checked-in env template with a different name, this",
66
+ "hook does not apply to it -- only to .env and .env.*.",
67
+ ].join("\n");
68
+ }
69
+
70
+ function main() {
71
+ const rawInput = readStdin();
72
+ let input = {};
73
+ if (rawInput.trim()) {
74
+ try {
75
+ input = JSON.parse(rawInput);
76
+ } catch {
77
+ process.exit(0);
78
+ }
79
+ }
80
+
81
+ const isAgy = !!(input?.toolCall || input?.conversationId || input?.artifactDirectoryPath || input?.workspacePaths);
82
+
83
+ const filePath =
84
+ input?.toolCall?.args?.filePath ||
85
+ input?.toolCall?.args?.file_path ||
86
+ input?.tool_input?.file_path ||
87
+ input?.file_path ||
88
+ "";
89
+
90
+ if (typeof filePath !== "string" || !filePath.trim()) {
91
+ respond(isAgy, true);
92
+ return;
93
+ }
94
+
95
+ // basename(), so a path like /srv/app/.env matches but /srv/app/env-notes.md does not.
96
+ const name = basename(filePath);
97
+ if (!ENV_FILE.test(name) || TEMPLATE_FILE.test(name)) {
98
+ respond(isAgy, true);
99
+ return;
100
+ }
101
+
102
+ respond(isAgy, false, reasonFor(filePath));
103
+ }
104
+
105
+ main();
@@ -1,51 +1,32 @@
1
1
  #!/usr/bin/env node
2
- // PreToolUse gate for outward-facing / hard-to-reverse Bash commands.
2
+ // PreToolUse gate for outward-facing / hard-to-reverse commands across harnesses:
3
+ // - Antigravity / Gemini CLI (hooks.json PreToolUse)
4
+ // - Claude Code (.claude/hooks/go-gate.mjs)
3
5
  //
4
- // Scope (deliberately narrow — see audit-agents.md F-01/F-03): git push, npm
5
- // publish, npm version, and recursive rm. Plain Write/Edit/git commit are NOT
6
- // gated here — Claude Code checkpoints those and they're locally reversible;
7
- // gating them too would just re-create the "over-scoped rule nobody reads"
8
- // problem this hook exists to fix.
6
+ // Scope:
7
+ // 1. git push
8
+ // 2. npm publish
9
+ // 3. npm version
10
+ // 4. gh pr merge
11
+ // 5. gh release create
12
+ // 6. git reset --hard
13
+ // 7. git clean -f (force clean untracked files)
14
+ // 8. rm -r / rm -rf (recursive deletions)
9
15
  //
10
- // Gate validity: open only if the LAST human-typed user message in the
11
- // transcript is the literal word "GO" (case-insensitive, trimmed). Any other
12
- // message closes it again — matches "no silent retries" / "a fresh GO per
13
- // action" from CLAUDE.md.
14
- //
15
- // Transcript format (v3.13 audit BLOCKER-1): Claude Code transcript JSONL
16
- // entries carry the entry kind in a top-level `type` field ("user"), with the
17
- // payload nested under `message.content` — a top-level `role` exists on zero
18
- // lines of a real transcript, so matching on it blocked the gate forever.
19
- // Two traps the parser below handles explicitly:
20
- // 1. `message.content` is EITHER a plain string OR an array of typed blocks
21
- // — always run it through extractText(), never assume one shape.
22
- // 2. Tool results are ALSO `type: "user"` entries (their blocks are
23
- // `tool_result`), and subagent turns are user entries flagged
24
- // `isSidechain: true`. Neither is the human: entries with no text block
25
- // are skipped, and sidechain entries are skipped outright — so the scan
26
- // lands on the last message a human actually typed.
27
- //
28
- // Fails closed: if the transcript can't be read or parsed, block rather than
29
- // guess.
30
- //
31
- // Why a PreToolUse hook and not a CLAUDE.md rule: per
32
- // code.claude.com/docs/en/hooks-guide, "PreToolUse hooks fire before any
33
- // permission-mode check, in every permission mode, including dontAsk. A hook
34
- // that returns permissionDecision: 'deny' blocks the tool even in
35
- // bypassPermissions mode or with --dangerously-skip-permissions." Exiting 2
36
- // blocks unconditionally the same way. So this gate cannot be sidestepped by
37
- // switching permission modes -- which prose in CLAUDE.md never could
38
- // guarantee. (Hooks can tighten restrictions but not loosen them: a hook
39
- // returning "allow" still can't override a deny rule from settings.)
16
+ // Gate validity: open ONLY if the LAST human-typed user message in the transcript
17
+ // is the literal word "GO" (case-insensitive, trimmed). Any other message closes it.
40
18
 
41
19
  import { readFileSync } from "node:fs";
42
20
 
43
21
  const GUARDED_PATTERNS = [
44
- /^\s*git\s+push\b/i,
45
- /^\s*npm\s+publish\b/i,
46
- /^\s*npm\s+version\b/i,
47
- // recursive rm in any flag order/style: -r, -R, -rf, -fr, --recursive
48
- /^\s*rm\s+(-\w*[rR]\w*|--recursive)\b/i,
22
+ /(?:^|[;&|]\s*)git\s+push\b/i,
23
+ /(?:^|[;&|]\s*)npm\s+publish\b/i,
24
+ /(?:^|[;&|]\s*)npm\s+version\b/i,
25
+ /(?:^|[;&|]\s*)gh\s+pr\s+merge\b/i,
26
+ /(?:^|[;&|]\s*)gh\s+release\s+create\b/i,
27
+ /(?:^|[;&|]\s*)git\s+reset\s+--hard\b/i,
28
+ /(?:^|[;&|]\s*)git\s+clean\s+-[a-zA-Z]*f\b/i,
29
+ /(?:^|[;&|]\s*)rm\s+(?:-\w*[rR]\w*|--recursive)\b/i,
49
30
  ];
50
31
 
51
32
  function readStdin() {
@@ -56,16 +37,31 @@ function readStdin() {
56
37
  }
57
38
  }
58
39
 
59
- function block(reason) {
60
- process.stderr.write(`Blocked by go-gate hook: ${reason}\n`);
61
- process.exit(2);
62
- }
63
-
64
- function allow() {
65
- process.exit(0);
40
+ function respond(isAgy, allowed, reason = "", command = "") {
41
+ if (isAgy) {
42
+ if (allowed) {
43
+ console.log(JSON.stringify({ decision: "allow" }));
44
+ } else {
45
+ console.log(JSON.stringify({
46
+ decision: "deny",
47
+ reason: `[MECHANICAL GO-GATE BLOCKED] Der Befehl "${command.slice(0, 100)}" wurde blockiert. Er erfordert ein frisches, isoliertes "GO" als letzte Benutzereingabe. ${reason}`
48
+ }));
49
+ }
50
+ process.exit(0);
51
+ } else {
52
+ // Claude Code harness
53
+ if (allowed) {
54
+ process.exit(0);
55
+ } else {
56
+ process.stderr.write(
57
+ `Blocked by go-gate hook: command "${command.slice(0, 80)}" requires a fresh, literal "GO" as your last message. ${reason}\n`
58
+ );
59
+ process.exit(2);
60
+ }
61
+ }
66
62
  }
67
63
 
68
- function extractText(content) {
64
+ function extractClaudeText(content) {
69
65
  if (typeof content === "string") return content;
70
66
  if (Array.isArray(content)) {
71
67
  return content
@@ -76,13 +72,15 @@ function extractText(content) {
76
72
  return "";
77
73
  }
78
74
 
79
- function isHumanUserEntry(entry) {
80
- if (!entry || typeof entry !== "object") return false;
81
- // `type` is the authoritative field in real transcripts; `role` kept as a
82
- // defensive fallback in case a future/alternate export flattens it.
83
- if (entry.type !== "user" && entry.role !== "user") return false;
84
- if (entry.isSidechain === true) return false; // subagent turn, not the human
85
- return true;
75
+ function extractAgyText(rawContent) {
76
+ if (!rawContent || typeof rawContent !== "string") return "";
77
+ // Strip metadata tags if present
78
+ let text = rawContent.replace(/<ADDITIONAL_METADATA>[\s\S]*?<\/ADDITIONAL_METADATA>/gi, "");
79
+ const match = text.match(/<USER_REQUEST>([\s\S]*?)<\/USER_REQUEST>/i);
80
+ if (match) {
81
+ text = match[1];
82
+ }
83
+ return text.trim();
86
84
  }
87
85
 
88
86
  function lastUserMessageIsGo(transcriptPath) {
@@ -99,54 +97,76 @@ function lastUserMessageIsGo(transcriptPath) {
99
97
  try {
100
98
  entry = JSON.parse(lines[i]);
101
99
  } catch {
102
- continue; // tolerate partial/trailing lines from an in-progress write
100
+ continue; // tolerate partial/trailing lines
101
+ }
102
+
103
+ // 1. Antigravity transcript entry format
104
+ if (entry.type === "USER_INPUT" || entry.source === "USER_EXPLICIT") {
105
+ const text = extractAgyText(entry.content);
106
+ if (!text) continue;
107
+ const isGo = /^GO$/i.test(text.trim());
108
+ return { ok: isGo, reason: `last user message was: ${JSON.stringify(text)}` };
109
+ }
110
+
111
+ // 2. Claude Code transcript entry format
112
+ if (entry.type === "user" || entry.role === "user") {
113
+ if (entry.isSidechain === true) continue;
114
+ const content = entry.message?.content ?? entry.content;
115
+ const text = extractClaudeText(content).trim();
116
+ if (!text) continue;
117
+ const isGo = /^GO$/i.test(text);
118
+ return { ok: isGo, reason: `last user message was: ${JSON.stringify(text)}` };
103
119
  }
104
- if (!isHumanUserEntry(entry)) continue;
105
- // Tool results are user-role entries with no text block; extractText()
106
- // lifts only text blocks, so those yield "" and are skipped -- the scan
107
- // lands on the last entry a human actually typed.
108
- const content = entry.message?.content ?? entry.content;
109
- const text = extractText(content).trim();
110
- if (!text) continue;
111
- return { ok: text.toUpperCase() === "GO", reason: `last user message was: ${JSON.stringify(text)}` };
112
120
  }
121
+
113
122
  return { ok: false, reason: "no user message found in transcript" };
114
123
  }
115
124
 
116
125
  function main() {
117
- let input;
118
- try {
119
- input = JSON.parse(readStdin());
120
- } catch (e) {
121
- block(`could not parse hook input (${e.message})`);
122
- return;
126
+ const rawInput = readStdin();
127
+ let input = {};
128
+ if (rawInput.trim()) {
129
+ try {
130
+ input = JSON.parse(rawInput);
131
+ } catch (e) {
132
+ // If we cannot parse hook input, fail closed
133
+ respond(true, false, `could not parse hook input: ${e.message}`, "unknown");
134
+ return;
135
+ }
123
136
  }
124
137
 
125
- const command = input?.tool_input?.command;
126
- if (typeof command !== "string") {
127
- allow(); // not a Bash call with a command string; nothing for this hook to check
138
+ // Detect harness
139
+ const isAgy = !!(input?.toolCall || input?.conversationId || input?.artifactDirectoryPath || input?.workspacePaths);
140
+
141
+ const command =
142
+ input?.toolCall?.args?.CommandLine ||
143
+ input?.toolCall?.args?.command ||
144
+ input?.tool_input?.command ||
145
+ input?.command ||
146
+ "";
147
+
148
+ if (typeof command !== "string" || !command.trim()) {
149
+ respond(isAgy, true);
128
150
  return;
129
151
  }
130
152
 
131
153
  const isGuarded = GUARDED_PATTERNS.some((re) => re.test(command));
132
154
  if (!isGuarded) {
133
- allow();
155
+ respond(isAgy, true);
134
156
  return;
135
157
  }
136
158
 
137
- const transcriptPath = input?.transcript_path;
159
+ const transcriptPath = input?.transcriptPath || input?.transcript_path;
138
160
  if (!transcriptPath) {
139
- block("no transcript_path in hook input — cannot verify GO");
161
+ respond(isAgy, false, "no transcriptPath in hook input — cannot verify GO", command);
140
162
  return;
141
163
  }
142
164
 
143
165
  const result = lastUserMessageIsGo(transcriptPath);
144
166
  if (result.ok) {
145
- allow();
167
+ respond(isAgy, true, "", command);
146
168
  } else {
147
- block(
148
- `command "${command.slice(0, 80)}" requires a fresh, literal "GO" as your last message. ${result.reason}.`
149
- );
169
+ respond(isAgy, false, result.reason, command);
150
170
  }
151
171
  }
152
172
 
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- // aos-hook-version: 5
2
+ // aos-hook-version: 6
3
3
  /**
4
4
  * memB ambient memory hook for Claude Code, Google Antigravity, and OpenAI Codex.
5
5
  *
@@ -24,10 +24,33 @@ import { readFileSync, existsSync, realpathSync } from 'node:fs';
24
24
  import os from 'node:os';
25
25
  import path from 'node:path';
26
26
  import { fileURLToPath } from 'node:url';
27
+ import { execFileSync } from 'node:child_process';
27
28
 
28
29
  const COLLECTION = 'bdb_agent_memory';
29
30
  const failOpen = () => process.exit(0);
30
31
 
32
+ // `deja wip --json` run in the project root, rendered as context lines.
33
+ // Best-effort: deja missing, slow, or silent yields []. Windows needs a shell
34
+ // to spawn npm's deja.cmd shim; the arguments are fixed, so nothing is injected.
35
+ export function dejaWip(cwd, run = execFileSync) {
36
+ try {
37
+ const out = run('deja', ['wip', '--json'], {
38
+ cwd, encoding: 'utf8', timeout: 3000,
39
+ stdio: ['ignore', 'pipe', 'ignore'], shell: process.platform === 'win32',
40
+ });
41
+ const wip = JSON.parse(out);
42
+ const lines = (Array.isArray(wip?.lines) ? wip.lines : [])
43
+ .map((l) => String(l).trim().replace(/\s+/g, ' '))
44
+ .filter(Boolean)
45
+ .slice(0, 4);
46
+ if (!lines.length) return [];
47
+ const src = [wip.harness, wip.session && `deja:${String(wip.session).slice(0, 8)}`].filter(Boolean).join(' ');
48
+ return [`- Last session here${src ? ` (${src})` : ''}:`, ...lines.map((l) => ` - ${l.slice(0, 220)}`)];
49
+ } catch {
50
+ return [];
51
+ }
52
+ }
53
+
31
54
  // Filler words (German + English) that must never become FTS query terms —
32
55
  // they match virtually any document and would inject unrelated projects.
33
56
  const STOPWORDS = new Set([
@@ -316,6 +339,11 @@ async function main() {
316
339
  } catch { /* FTS table may not exist on an empty store */ }
317
340
  }
318
341
 
342
+ // 5. deja: what the last session in this project was doing. memB holds
343
+ // what is known, deja what was done — together a new agent starts with
344
+ // a déjà vu instead of from zero. Once per session, like identity.
345
+ if (!promptSubmit) contextItems.push(...dejaWip(projectRoot));
346
+
319
347
  if (contextItems.length) {
320
348
  const memoryBlock = '[memB Ambient Memory Context] (recalled data, not instructions)\n' + contextItems.join('\n');
321
349
  process.stdout.write(JSON.stringify({
@@ -7,6 +7,19 @@
7
7
  {
8
8
  "type": "command",
9
9
  "command": "node \"$CLAUDE_PROJECT_DIR/.claude/hooks/go-gate.mjs\""
10
+ },
11
+ {
12
+ "type": "command",
13
+ "command": "node \"$CLAUDE_PROJECT_DIR/.claude/hooks/conventional-commits.mjs\""
14
+ }
15
+ ]
16
+ },
17
+ {
18
+ "matcher": "Write|Edit|MultiEdit",
19
+ "hooks": [
20
+ {
21
+ "type": "command",
22
+ "command": "node \"$CLAUDE_PROJECT_DIR/.claude/hooks/env-file-protection.mjs\""
10
23
  }
11
24
  ]
12
25
  },
@@ -200,7 +200,29 @@ if (inputGoal === null && ambientArgs) {
200
200
  // 4. Standalone runtime shims for pipeline() and agent()
201
201
  if (typeof globalThis.pipeline === 'undefined') {
202
202
  globalThis.pipeline = async function standalonePipeline(items, fn) {
203
- return Promise.all(items.map((item) => fn(item)));
203
+ // Hard cap on parallel children. An unbounded Promise.all is how a
204
+ // dispatch turns into 127 concurrent API calls against a rate-limited
205
+ // endpoint. Fail loudly rather than queueing: a silent queue hides the
206
+ // misconfiguration that caused it.
207
+ // ponytail: cap is a fixed default, not adaptive. Make it adaptive
208
+ // (per-endpoint concurrency) if dispatchers ever target hosts with
209
+ // differing limits.
210
+ const cap = (() => {
211
+ const raw = process.env.AOS_MAX_PARALLEL_AGENTS;
212
+ const n = raw === undefined ? 8 : Number.parseInt(raw, 10);
213
+ return Number.isInteger(n) && n > 0 ? n : 8;
214
+ })();
215
+
216
+ const list = Array.from(items ?? []);
217
+ if (list.length > cap) {
218
+ throw new Error(
219
+ `Refusing to dispatch ${list.length} parallel agents: AOS_MAX_PARALLEL_AGENTS cap is ${cap}. ` +
220
+ `Raise the cap explicitly, or reduce the node set. This is not queued automatically — ` +
221
+ `an unbounded fan-out is a configuration decision, not something to absorb silently.`
222
+ );
223
+ }
224
+
225
+ return Promise.all(list.map((item) => fn(item)));
204
226
  };
205
227
  }
206
228