@ahmed-g-gad/apothem 0.1.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 (674) hide show
  1. package/CHANGELOG.md +60 -0
  2. package/LICENSE +21 -0
  3. package/LICENSES/MIT.txt +18 -0
  4. package/LICENSES/PSF-2.0.txt +47 -0
  5. package/README.md +549 -0
  6. package/bin/README.md +37 -0
  7. package/bin/apothem.mjs +78 -0
  8. package/package.json +75 -0
  9. package/pyproject.toml +347 -0
  10. package/src/apothem/README.md +52 -0
  11. package/src/apothem/__init__.py +66 -0
  12. package/src/apothem/__main__.py +28 -0
  13. package/src/apothem/_vendor/.keep +0 -0
  14. package/src/apothem/_vendor/__init__.py +25 -0
  15. package/src/apothem/_vendor/attr/__init__.py +104 -0
  16. package/src/apothem/_vendor/attr/__init__.pyi +389 -0
  17. package/src/apothem/_vendor/attr/_cmp.py +160 -0
  18. package/src/apothem/_vendor/attr/_cmp.pyi +13 -0
  19. package/src/apothem/_vendor/attr/_compat.py +99 -0
  20. package/src/apothem/_vendor/attr/_config.py +31 -0
  21. package/src/apothem/_vendor/attr/_funcs.py +497 -0
  22. package/src/apothem/_vendor/attr/_make.py +3406 -0
  23. package/src/apothem/_vendor/attr/_next_gen.py +674 -0
  24. package/src/apothem/_vendor/attr/_typing_compat.pyi +15 -0
  25. package/src/apothem/_vendor/attr/_version_info.py +89 -0
  26. package/src/apothem/_vendor/attr/_version_info.pyi +9 -0
  27. package/src/apothem/_vendor/attr/converters.py +162 -0
  28. package/src/apothem/_vendor/attr/converters.pyi +19 -0
  29. package/src/apothem/_vendor/attr/exceptions.py +95 -0
  30. package/src/apothem/_vendor/attr/exceptions.pyi +17 -0
  31. package/src/apothem/_vendor/attr/filters.py +72 -0
  32. package/src/apothem/_vendor/attr/filters.pyi +6 -0
  33. package/src/apothem/_vendor/attr/py.typed +0 -0
  34. package/src/apothem/_vendor/attr/setters.py +79 -0
  35. package/src/apothem/_vendor/attr/setters.pyi +20 -0
  36. package/src/apothem/_vendor/attr/validators.py +750 -0
  37. package/src/apothem/_vendor/attr/validators.pyi +140 -0
  38. package/src/apothem/_vendor/attr.LICENSE +21 -0
  39. package/src/apothem/_vendor/attrs/__init__.py +72 -0
  40. package/src/apothem/_vendor/attrs/__init__.pyi +314 -0
  41. package/src/apothem/_vendor/attrs/converters.py +3 -0
  42. package/src/apothem/_vendor/attrs/exceptions.py +3 -0
  43. package/src/apothem/_vendor/attrs/filters.py +3 -0
  44. package/src/apothem/_vendor/attrs/py.typed +0 -0
  45. package/src/apothem/_vendor/attrs/setters.py +3 -0
  46. package/src/apothem/_vendor/attrs/validators.py +3 -0
  47. package/src/apothem/_vendor/attrs.LICENSE +21 -0
  48. package/src/apothem/_vendor/jsonschema/__init__.py +120 -0
  49. package/src/apothem/_vendor/jsonschema/__main__.py +6 -0
  50. package/src/apothem/_vendor/jsonschema/_format.py +546 -0
  51. package/src/apothem/_vendor/jsonschema/_keywords.py +449 -0
  52. package/src/apothem/_vendor/jsonschema/_legacy_keywords.py +449 -0
  53. package/src/apothem/_vendor/jsonschema/_types.py +204 -0
  54. package/src/apothem/_vendor/jsonschema/_typing.py +29 -0
  55. package/src/apothem/_vendor/jsonschema/_utils.py +355 -0
  56. package/src/apothem/_vendor/jsonschema/benchmarks/__init__.py +5 -0
  57. package/src/apothem/_vendor/jsonschema/benchmarks/const_vs_enum.py +30 -0
  58. package/src/apothem/_vendor/jsonschema/benchmarks/contains.py +28 -0
  59. package/src/apothem/_vendor/jsonschema/benchmarks/import_benchmark.py +31 -0
  60. package/src/apothem/_vendor/jsonschema/benchmarks/issue232/issue.json +2653 -0
  61. package/src/apothem/_vendor/jsonschema/benchmarks/issue232.py +25 -0
  62. package/src/apothem/_vendor/jsonschema/benchmarks/json_schema_test_suite.py +12 -0
  63. package/src/apothem/_vendor/jsonschema/benchmarks/nested_schemas.py +56 -0
  64. package/src/apothem/_vendor/jsonschema/benchmarks/subcomponents.py +42 -0
  65. package/src/apothem/_vendor/jsonschema/benchmarks/unused_registry.py +35 -0
  66. package/src/apothem/_vendor/jsonschema/benchmarks/useless_applicator_schemas.py +106 -0
  67. package/src/apothem/_vendor/jsonschema/benchmarks/useless_keywords.py +32 -0
  68. package/src/apothem/_vendor/jsonschema/benchmarks/validator_creation.py +14 -0
  69. package/src/apothem/_vendor/jsonschema/cli.py +292 -0
  70. package/src/apothem/_vendor/jsonschema/exceptions.py +490 -0
  71. package/src/apothem/_vendor/jsonschema/protocols.py +230 -0
  72. package/src/apothem/_vendor/jsonschema/validators.py +1410 -0
  73. package/src/apothem/_vendor/jsonschema.LICENSE +19 -0
  74. package/src/apothem/_vendor/jsonschema_specifications/__init__.py +12 -0
  75. package/src/apothem/_vendor/jsonschema_specifications/_core.py +38 -0
  76. package/src/apothem/_vendor/jsonschema_specifications/schemas/draft201909/metaschema.json +42 -0
  77. package/src/apothem/_vendor/jsonschema_specifications/schemas/draft201909/vocabularies/applicator +56 -0
  78. package/src/apothem/_vendor/jsonschema_specifications/schemas/draft201909/vocabularies/content +17 -0
  79. package/src/apothem/_vendor/jsonschema_specifications/schemas/draft201909/vocabularies/core +57 -0
  80. package/src/apothem/_vendor/jsonschema_specifications/schemas/draft201909/vocabularies/format +14 -0
  81. package/src/apothem/_vendor/jsonschema_specifications/schemas/draft201909/vocabularies/meta-data +37 -0
  82. package/src/apothem/_vendor/jsonschema_specifications/schemas/draft201909/vocabularies/validation +98 -0
  83. package/src/apothem/_vendor/jsonschema_specifications/schemas/draft202012/metaschema.json +58 -0
  84. package/src/apothem/_vendor/jsonschema_specifications/schemas/draft202012/vocabularies/applicator +48 -0
  85. package/src/apothem/_vendor/jsonschema_specifications/schemas/draft202012/vocabularies/content +17 -0
  86. package/src/apothem/_vendor/jsonschema_specifications/schemas/draft202012/vocabularies/core +51 -0
  87. package/src/apothem/_vendor/jsonschema_specifications/schemas/draft202012/vocabularies/format-annotation +14 -0
  88. package/src/apothem/_vendor/jsonschema_specifications/schemas/draft202012/vocabularies/format-assertion +14 -0
  89. package/src/apothem/_vendor/jsonschema_specifications/schemas/draft202012/vocabularies/meta-data +37 -0
  90. package/src/apothem/_vendor/jsonschema_specifications/schemas/draft202012/vocabularies/unevaluated +15 -0
  91. package/src/apothem/_vendor/jsonschema_specifications/schemas/draft202012/vocabularies/validation +98 -0
  92. package/src/apothem/_vendor/jsonschema_specifications/schemas/draft3/metaschema.json +172 -0
  93. package/src/apothem/_vendor/jsonschema_specifications/schemas/draft4/metaschema.json +149 -0
  94. package/src/apothem/_vendor/jsonschema_specifications/schemas/draft6/metaschema.json +153 -0
  95. package/src/apothem/_vendor/jsonschema_specifications/schemas/draft7/metaschema.json +166 -0
  96. package/src/apothem/_vendor/jsonschema_specifications.LICENSE +19 -0
  97. package/src/apothem/_vendor/referencing/__init__.py +7 -0
  98. package/src/apothem/_vendor/referencing/_attrs.py +31 -0
  99. package/src/apothem/_vendor/referencing/_attrs.pyi +21 -0
  100. package/src/apothem/_vendor/referencing/_core.py +739 -0
  101. package/src/apothem/_vendor/referencing/exceptions.py +165 -0
  102. package/src/apothem/_vendor/referencing/jsonschema.py +642 -0
  103. package/src/apothem/_vendor/referencing/py.typed +0 -0
  104. package/src/apothem/_vendor/referencing/retrieval.py +94 -0
  105. package/src/apothem/_vendor/referencing/typing.py +61 -0
  106. package/src/apothem/_vendor/referencing.LICENSE +19 -0
  107. package/src/apothem/_vendor/rpds/__init__.py +251 -0
  108. package/src/apothem/_vendor/typing_extensions.LICENSE +279 -0
  109. package/src/apothem/_vendor/typing_extensions.py +4317 -0
  110. package/src/apothem/_vendor/vendor.txt +22 -0
  111. package/src/apothem/_vendor/yaml/__init__.py +389 -0
  112. package/src/apothem/_vendor/yaml/composer.py +138 -0
  113. package/src/apothem/_vendor/yaml/constructor.py +748 -0
  114. package/src/apothem/_vendor/yaml/cyaml.py +100 -0
  115. package/src/apothem/_vendor/yaml/dumper.py +61 -0
  116. package/src/apothem/_vendor/yaml/emitter.py +1137 -0
  117. package/src/apothem/_vendor/yaml/error.py +74 -0
  118. package/src/apothem/_vendor/yaml/events.py +85 -0
  119. package/src/apothem/_vendor/yaml/loader.py +63 -0
  120. package/src/apothem/_vendor/yaml/nodes.py +48 -0
  121. package/src/apothem/_vendor/yaml/parser.py +588 -0
  122. package/src/apothem/_vendor/yaml/reader.py +185 -0
  123. package/src/apothem/_vendor/yaml/representer.py +388 -0
  124. package/src/apothem/_vendor/yaml/resolver.py +226 -0
  125. package/src/apothem/_vendor/yaml/scanner.py +1435 -0
  126. package/src/apothem/_vendor/yaml/serializer.py +110 -0
  127. package/src/apothem/_vendor/yaml/tokens.py +103 -0
  128. package/src/apothem/_vendor/yaml.LICENSE +20 -0
  129. package/src/apothem/agents/README.md +60 -0
  130. package/src/apothem/agents/codebase-explorer.md +91 -0
  131. package/src/apothem/agents/convention-auditor.md +93 -0
  132. package/src/apothem/agents/dependency-auditor.md +97 -0
  133. package/src/apothem/agents/fact-checker.md +84 -0
  134. package/src/apothem/agents/mcp-builder.md +86 -0
  135. package/src/apothem/agents/memory-auditor.md +93 -0
  136. package/src/apothem/agents/prompt-evaluator.md +87 -0
  137. package/src/apothem/agents/quality-gate.md +103 -0
  138. package/src/apothem/agents/refactor-surgeon.md +74 -0
  139. package/src/apothem/agents/research-scout.md +73 -0
  140. package/src/apothem/agents/security-scanner.md +83 -0
  141. package/src/apothem/agents/test-runner.md +84 -0
  142. package/src/apothem/audit/README.md +73 -0
  143. package/src/apothem/audit/_scan_lib.py +182 -0
  144. package/src/apothem/audit/analyze_graph.py +260 -0
  145. package/src/apothem/audit/build_capability_graph.py +607 -0
  146. package/src/apothem/audit/build_inventory.py +657 -0
  147. package/src/apothem/audit/build_plans_provenance.py +997 -0
  148. package/src/apothem/audit/check_links.py +389 -0
  149. package/src/apothem/audit/classify_artifacts.py +381 -0
  150. package/src/apothem/audit/deprecated-tokens.txt +10 -0
  151. package/src/apothem/audit/execute_plans_migration.py +491 -0
  152. package/src/apothem/audit/known-projects.txt +15 -0
  153. package/src/apothem/audit/render_capability_index.py +467 -0
  154. package/src/apothem/audit/render_inventory.py +405 -0
  155. package/src/apothem/audit/scan_ai_surfaces.py +1125 -0
  156. package/src/apothem/audit/scan_ai_surfaces_coarse.py +261 -0
  157. package/src/apothem/audit/scan_drift_features.py +143 -0
  158. package/src/apothem/audit/scan_frontmatter.py +293 -0
  159. package/src/apothem/audit/scan_header_coverage.py +1134 -0
  160. package/src/apothem/audit/scan_plan_leakage.py +540 -0
  161. package/src/apothem/audit/scan_plans_discipline.py +188 -0
  162. package/src/apothem/audit/scan_secrets_pii.py +245 -0
  163. package/src/apothem/audit/scan_stale_tokens.py +296 -0
  164. package/src/apothem/audit/synthesize_drift.py +205 -0
  165. package/src/apothem/benchmarks/README.md +33 -0
  166. package/src/apothem/benchmarks/__init__.py +3 -0
  167. package/src/apothem/benchmarks/bench_agents.py +63 -0
  168. package/src/apothem/benchmarks/bench_hooks.py +93 -0
  169. package/src/apothem/benchmarks/bench_install.py +58 -0
  170. package/src/apothem/benchmarks/bench_tests.py +93 -0
  171. package/src/apothem/benchmarks/bench_validate_ecosystem.py +84 -0
  172. package/src/apothem/cli/README.md +33 -0
  173. package/src/apothem/cli/__init__.py +229 -0
  174. package/src/apothem/cli/_cmd_completion.py +88 -0
  175. package/src/apothem/cli/_cmd_diff.py +181 -0
  176. package/src/apothem/cli/_cmd_doctor.py +143 -0
  177. package/src/apothem/cli/_cmd_harnesses.py +167 -0
  178. package/src/apothem/cli/_cmd_install.py +327 -0
  179. package/src/apothem/cli/_cmd_migrate_workspace.py +143 -0
  180. package/src/apothem/cli/_cmd_profile.py +341 -0
  181. package/src/apothem/cli/_cmd_status.py +180 -0
  182. package/src/apothem/cli/_cmd_uninstall.py +215 -0
  183. package/src/apothem/cli/_cmd_update.py +397 -0
  184. package/src/apothem/cli/_cmd_verify.py +194 -0
  185. package/src/apothem/cli/_common_flags.py +90 -0
  186. package/src/apothem/cli/_epilogs.py +296 -0
  187. package/src/apothem/cli/_helpers.py +857 -0
  188. package/src/apothem/cli/_json_formatter.py +21 -0
  189. package/src/apothem/cli/_materialize.py +376 -0
  190. package/src/apothem/cli/completions/apothem.bash +30 -0
  191. package/src/apothem/cli/completions/apothem.fish +19 -0
  192. package/src/apothem/cli/completions/apothem.ps1 +27 -0
  193. package/src/apothem/cli/completions/apothem.zsh +42 -0
  194. package/src/apothem/cli/reference_export.py +126 -0
  195. package/src/apothem/commands/README.md +125 -0
  196. package/src/apothem/commands/a11y-audit.md +203 -0
  197. package/src/apothem/commands/architecture-review.md +194 -0
  198. package/src/apothem/commands/audit.md +165 -0
  199. package/src/apothem/commands/code-audit.md +218 -0
  200. package/src/apothem/commands/code-review.md +193 -0
  201. package/src/apothem/commands/dependency-audit.md +209 -0
  202. package/src/apothem/commands/docs-review.md +199 -0
  203. package/src/apothem/commands/elevate.md +285 -0
  204. package/src/apothem/commands/eval.md +149 -0
  205. package/src/apothem/commands/fortress.md +172 -0
  206. package/src/apothem/commands/freshify.md +168 -0
  207. package/src/apothem/commands/github-deploy-fresh.md +178 -0
  208. package/src/apothem/commands/github-deploy-next.md +167 -0
  209. package/src/apothem/commands/perf-audit.md +198 -0
  210. package/src/apothem/commands/plan-amend.md +104 -0
  211. package/src/apothem/commands/plan-audit.md +127 -0
  212. package/src/apothem/commands/plan-design.md +257 -0
  213. package/src/apothem/commands/plan-execute.md +495 -0
  214. package/src/apothem/commands/plan-generate.md +351 -0
  215. package/src/apothem/commands/plan-review.md +555 -0
  216. package/src/apothem/commands/plan-spec.md +359 -0
  217. package/src/apothem/commands/plan-status.md +222 -0
  218. package/src/apothem/commands/plan.md +173 -0
  219. package/src/apothem/commands/projectify.md +142 -0
  220. package/src/apothem/commands/release-readiness.md +142 -0
  221. package/src/apothem/commands/research-analysis.md +241 -0
  222. package/src/apothem/commands/research-design.md +231 -0
  223. package/src/apothem/commands/research-disseminate.md +225 -0
  224. package/src/apothem/commands/research-experiment.md +232 -0
  225. package/src/apothem/commands/research-ideate.md +213 -0
  226. package/src/apothem/commands/research-paper.md +252 -0
  227. package/src/apothem/commands/research-proposal.md +220 -0
  228. package/src/apothem/commands/research-publish.md +255 -0
  229. package/src/apothem/commands/research-review.md +251 -0
  230. package/src/apothem/commands/research-sources.md +266 -0
  231. package/src/apothem/commands/research-spec.md +255 -0
  232. package/src/apothem/commands/research-synthesis.md +233 -0
  233. package/src/apothem/commands/research-theory.md +218 -0
  234. package/src/apothem/commands/research.md +181 -0
  235. package/src/apothem/commands/security-audit.md +196 -0
  236. package/src/apothem/commands/supply-chain-audit.md +192 -0
  237. package/src/apothem/commands/test-suite.md +146 -0
  238. package/src/apothem/commands/threat-model-audit.md +199 -0
  239. package/src/apothem/commands/ux-review.md +202 -0
  240. package/src/apothem/commands/workflow.md +162 -0
  241. package/src/apothem/conformity/README.md +173 -0
  242. package/src/apothem/conformity/__init__.py +1 -0
  243. package/src/apothem/conformity/_grep_base.py +93 -0
  244. package/src/apothem/conformity/agent_capability_grep.py +306 -0
  245. package/src/apothem/conformity/agents_md_coverage_grep.py +382 -0
  246. package/src/apothem/conformity/agnosticism_grep.py +311 -0
  247. package/src/apothem/conformity/always_on_budget_grep.py +318 -0
  248. package/src/apothem/conformity/bare_except_grep.py +115 -0
  249. package/src/apothem/conformity/binding_reciprocity_grep.py +151 -0
  250. package/src/apothem/conformity/brand_mark_grep.py +272 -0
  251. package/src/apothem/conformity/commented_out_code_grep.py +176 -0
  252. package/src/apothem/conformity/completion_claim_grep.py +169 -0
  253. package/src/apothem/conformity/conventional_commit_grep.py +319 -0
  254. package/src/apothem/conformity/copilot_instructions_presence_grep.py +324 -0
  255. package/src/apothem/conformity/cross_platform_matrix_grep.py +297 -0
  256. package/src/apothem/conformity/determinism_grep.py +306 -0
  257. package/src/apothem/conformity/diagram_staleness_grep.py +154 -0
  258. package/src/apothem/conformity/dynamism_grep.py +284 -0
  259. package/src/apothem/conformity/editorconfig_presence_grep.py +281 -0
  260. package/src/apothem/conformity/file_header_grep.py +502 -0
  261. package/src/apothem/conformity/freshness_token_grep.py +233 -0
  262. package/src/apothem/conformity/frontmatter_grep.py +274 -0
  263. package/src/apothem/conformity/frontmatter_value_grep.py +386 -0
  264. package/src/apothem/conformity/gate.py +1386 -0
  265. package/src/apothem/conformity/gitattributes_presence_grep.py +238 -0
  266. package/src/apothem/conformity/harden_runner_grep.py +320 -0
  267. package/src/apothem/conformity/hedging_grep.py +129 -0
  268. package/src/apothem/conformity/license_author_consistency_grep.py +204 -0
  269. package/src/apothem/conformity/link_check.py +327 -0
  270. package/src/apothem/conformity/magic_number_grep.py +182 -0
  271. package/src/apothem/conformity/multi_surface_coherence_grep.py +620 -0
  272. package/src/apothem/conformity/naming_grep.py +224 -0
  273. package/src/apothem/conformity/no_global_plans_grep.py +339 -0
  274. package/src/apothem/conformity/no_toplevel_docs_grep.py +120 -0
  275. package/src/apothem/conformity/oidc_trusted_publishing_grep.py +291 -0
  276. package/src/apothem/conformity/option_annotation_grep.py +352 -0
  277. package/src/apothem/conformity/orphan_output_grep.py +206 -0
  278. package/src/apothem/conformity/permissions_minimum_scope_grep.py +299 -0
  279. package/src/apothem/conformity/plain_language_grep.py +559 -0
  280. package/src/apothem/conformity/plan_next_step_consistency_grep.py +450 -0
  281. package/src/apothem/conformity/plan_suite_structure_grep.py +534 -0
  282. package/src/apothem/conformity/plans_discipline_language_grep.py +245 -0
  283. package/src/apothem/conformity/production_ready_pr_grep.py +200 -0
  284. package/src/apothem/conformity/recommend_next_step_grep.py +250 -0
  285. package/src/apothem/conformity/redundancy_grep.py +401 -0
  286. package/src/apothem/conformity/reference_token_grep.py +230 -0
  287. package/src/apothem/conformity/registry_capability_consistency_grep.py +368 -0
  288. package/src/apothem/conformity/secret_leak_grep.py +193 -0
  289. package/src/apothem/conformity/semver_stability_grep.py +358 -0
  290. package/src/apothem/conformity/smoke_install_grep.py +194 -0
  291. package/src/apothem/conformity/static_version_grep.py +284 -0
  292. package/src/apothem/conformity/token_efficiency_grep.py +185 -0
  293. package/src/apothem/conformity/unpinned_action_grep.py +115 -0
  294. package/src/apothem/conformity/user_confirm_grep.py +74 -0
  295. package/src/apothem/conformity/workflow_concurrency_grep.py +283 -0
  296. package/src/apothem/harnesses/README.md +63 -0
  297. package/src/apothem/harnesses/__init__.py +16 -0
  298. package/src/apothem/harnesses/_shared/README.md +36 -0
  299. package/src/apothem/harnesses/_shared/__init__.py +12 -0
  300. package/src/apothem/harnesses/_shared/install_driver.py +281 -0
  301. package/src/apothem/harnesses/_shared/install_driver_apply.py +612 -0
  302. package/src/apothem/harnesses/_shared/install_driver_backup.py +535 -0
  303. package/src/apothem/harnesses/_shared/install_driver_converters.py +310 -0
  304. package/src/apothem/harnesses/_shared/install_driver_lifecycle.py +495 -0
  305. package/src/apothem/harnesses/_shared/install_driver_materialize.py +675 -0
  306. package/src/apothem/harnesses/_shared/install_driver_merge.py +656 -0
  307. package/src/apothem/harnesses/_shared/install_driver_pathsafety.py +137 -0
  308. package/src/apothem/harnesses/_shared/install_driver_planvalidation.py +240 -0
  309. package/src/apothem/harnesses/_shared/install_driver_removal.py +366 -0
  310. package/src/apothem/harnesses/_shared/install_driver_treeops.py +248 -0
  311. package/src/apothem/harnesses/_shared/install_driver_types.py +330 -0
  312. package/src/apothem/harnesses/_shared/wrapper_factories.py +448 -0
  313. package/src/apothem/harnesses/antigravity/STANDARD-CONVENTION-PIN.md +91 -0
  314. package/src/apothem/harnesses/antigravity/__init__.py +70 -0
  315. package/src/apothem/harnesses/antigravity/capabilities.yml +40 -0
  316. package/src/apothem/harnesses/antigravity/install.py +63 -0
  317. package/src/apothem/harnesses/antigravity/templates/GEMINI.md +40 -0
  318. package/src/apothem/harnesses/antigravity/templates/plugin.json +5 -0
  319. package/src/apothem/harnesses/antigravity/uninstall.py +22 -0
  320. package/src/apothem/harnesses/antigravity/update.py +10 -0
  321. package/src/apothem/harnesses/antigravity/verify.py +11 -0
  322. package/src/apothem/harnesses/claude_code/STANDARD-CONVENTION-PIN.md +65 -0
  323. package/src/apothem/harnesses/claude_code/__init__.py +107 -0
  324. package/src/apothem/harnesses/claude_code/capabilities.yml +42 -0
  325. package/src/apothem/harnesses/claude_code/install.py +147 -0
  326. package/src/apothem/harnesses/claude_code/templates/settings.json +351 -0
  327. package/src/apothem/harnesses/claude_code/uninstall.py +23 -0
  328. package/src/apothem/harnesses/claude_code/update.py +10 -0
  329. package/src/apothem/harnesses/claude_code/verify.py +11 -0
  330. package/src/apothem/harnesses/codebuddy/STANDARD-CONVENTION-PIN.md +74 -0
  331. package/src/apothem/harnesses/codebuddy/__init__.py +49 -0
  332. package/src/apothem/harnesses/codebuddy/capabilities.yml +34 -0
  333. package/src/apothem/harnesses/codebuddy/install.py +40 -0
  334. package/src/apothem/harnesses/codebuddy/templates/apothem-rules.md +37 -0
  335. package/src/apothem/harnesses/codebuddy/uninstall.py +25 -0
  336. package/src/apothem/harnesses/codebuddy/update.py +10 -0
  337. package/src/apothem/harnesses/codebuddy/verify.py +11 -0
  338. package/src/apothem/harnesses/codex/STANDARD-CONVENTION-PIN.md +79 -0
  339. package/src/apothem/harnesses/codex/__init__.py +72 -0
  340. package/src/apothem/harnesses/codex/capabilities.yml +40 -0
  341. package/src/apothem/harnesses/codex/install.py +69 -0
  342. package/src/apothem/harnesses/codex/templates/AGENTS.md +40 -0
  343. package/src/apothem/harnesses/codex/templates/hooks.json +127 -0
  344. package/src/apothem/harnesses/codex/uninstall.py +23 -0
  345. package/src/apothem/harnesses/codex/update.py +10 -0
  346. package/src/apothem/harnesses/codex/verify.py +11 -0
  347. package/src/apothem/harnesses/cursor/STANDARD-CONVENTION-PIN.md +79 -0
  348. package/src/apothem/harnesses/cursor/__init__.py +48 -0
  349. package/src/apothem/harnesses/cursor/capabilities.yml +42 -0
  350. package/src/apothem/harnesses/cursor/install.py +38 -0
  351. package/src/apothem/harnesses/cursor/templates/apothem-rules.mdc +40 -0
  352. package/src/apothem/harnesses/cursor/uninstall.py +25 -0
  353. package/src/apothem/harnesses/cursor/update.py +10 -0
  354. package/src/apothem/harnesses/cursor/verify.py +11 -0
  355. package/src/apothem/harnesses/gemini_cli/STANDARD-CONVENTION-PIN.md +102 -0
  356. package/src/apothem/harnesses/gemini_cli/__init__.py +52 -0
  357. package/src/apothem/harnesses/gemini_cli/capabilities.yml +43 -0
  358. package/src/apothem/harnesses/gemini_cli/install.py +43 -0
  359. package/src/apothem/harnesses/gemini_cli/templates/GEMINI.md +38 -0
  360. package/src/apothem/harnesses/gemini_cli/uninstall.py +25 -0
  361. package/src/apothem/harnesses/gemini_cli/update.py +10 -0
  362. package/src/apothem/harnesses/gemini_cli/verify.py +11 -0
  363. package/src/apothem/harnesses/github_copilot/STANDARD-CONVENTION-PIN.md +84 -0
  364. package/src/apothem/harnesses/github_copilot/__init__.py +47 -0
  365. package/src/apothem/harnesses/github_copilot/capabilities.yml +42 -0
  366. package/src/apothem/harnesses/github_copilot/install.py +40 -0
  367. package/src/apothem/harnesses/github_copilot/templates/copilot-instructions.md +33 -0
  368. package/src/apothem/harnesses/github_copilot/uninstall.py +25 -0
  369. package/src/apothem/harnesses/github_copilot/update.py +10 -0
  370. package/src/apothem/harnesses/github_copilot/verify.py +11 -0
  371. package/src/apothem/harnesses/glm/STANDARD-CONVENTION-PIN.md +77 -0
  372. package/src/apothem/harnesses/glm/__init__.py +56 -0
  373. package/src/apothem/harnesses/glm/capabilities.yml +33 -0
  374. package/src/apothem/harnesses/glm/install.py +45 -0
  375. package/src/apothem/harnesses/glm/templates/glm.toml +58 -0
  376. package/src/apothem/harnesses/glm/uninstall.py +25 -0
  377. package/src/apothem/harnesses/glm/update.py +10 -0
  378. package/src/apothem/harnesses/glm/verify.py +11 -0
  379. package/src/apothem/harnesses/hermes/STANDARD-CONVENTION-PIN.md +57 -0
  380. package/src/apothem/harnesses/hermes/__init__.py +33 -0
  381. package/src/apothem/harnesses/hermes/capabilities.yml +36 -0
  382. package/src/apothem/harnesses/hermes/install.py +17 -0
  383. package/src/apothem/harnesses/hermes/materializer.py +35 -0
  384. package/src/apothem/harnesses/hermes/uninstall.py +33 -0
  385. package/src/apothem/harnesses/hermes/update.py +10 -0
  386. package/src/apothem/harnesses/hermes/verify.py +11 -0
  387. package/src/apothem/harnesses/kimi_code/STANDARD-CONVENTION-PIN.md +128 -0
  388. package/src/apothem/harnesses/kimi_code/__init__.py +59 -0
  389. package/src/apothem/harnesses/kimi_code/capabilities.yml +40 -0
  390. package/src/apothem/harnesses/kimi_code/install.py +42 -0
  391. package/src/apothem/harnesses/kimi_code/templates/AGENTS.md +43 -0
  392. package/src/apothem/harnesses/kimi_code/uninstall.py +27 -0
  393. package/src/apothem/harnesses/kimi_code/update.py +10 -0
  394. package/src/apothem/harnesses/kimi_code/verify.py +11 -0
  395. package/src/apothem/harnesses/kiro/STANDARD-CONVENTION-PIN.md +77 -0
  396. package/src/apothem/harnesses/kiro/__init__.py +49 -0
  397. package/src/apothem/harnesses/kiro/capabilities.yml +36 -0
  398. package/src/apothem/harnesses/kiro/install.py +39 -0
  399. package/src/apothem/harnesses/kiro/templates/apothem-rules.md +36 -0
  400. package/src/apothem/harnesses/kiro/uninstall.py +25 -0
  401. package/src/apothem/harnesses/kiro/update.py +10 -0
  402. package/src/apothem/harnesses/kiro/verify.py +11 -0
  403. package/src/apothem/harnesses/open_claw/STANDARD-CONVENTION-PIN.md +62 -0
  404. package/src/apothem/harnesses/open_claw/__init__.py +35 -0
  405. package/src/apothem/harnesses/open_claw/capabilities.yml +35 -0
  406. package/src/apothem/harnesses/open_claw/install.py +17 -0
  407. package/src/apothem/harnesses/open_claw/materializer.py +36 -0
  408. package/src/apothem/harnesses/open_claw/uninstall.py +32 -0
  409. package/src/apothem/harnesses/open_claw/update.py +10 -0
  410. package/src/apothem/harnesses/open_claw/verify.py +11 -0
  411. package/src/apothem/harnesses/opencode/STANDARD-CONVENTION-PIN.md +76 -0
  412. package/src/apothem/harnesses/opencode/__init__.py +35 -0
  413. package/src/apothem/harnesses/opencode/capabilities.yml +43 -0
  414. package/src/apothem/harnesses/opencode/install.py +17 -0
  415. package/src/apothem/harnesses/opencode/materializer.py +31 -0
  416. package/src/apothem/harnesses/opencode/uninstall.py +34 -0
  417. package/src/apothem/harnesses/opencode/update.py +10 -0
  418. package/src/apothem/harnesses/opencode/verify.py +11 -0
  419. package/src/apothem/harnesses/qwen_code/STANDARD-CONVENTION-PIN.md +87 -0
  420. package/src/apothem/harnesses/qwen_code/__init__.py +37 -0
  421. package/src/apothem/harnesses/qwen_code/capabilities.yml +43 -0
  422. package/src/apothem/harnesses/qwen_code/install.py +19 -0
  423. package/src/apothem/harnesses/qwen_code/materializer.py +174 -0
  424. package/src/apothem/harnesses/qwen_code/templates/QWEN.md +30 -0
  425. package/src/apothem/harnesses/qwen_code/uninstall.py +34 -0
  426. package/src/apothem/harnesses/qwen_code/update.py +10 -0
  427. package/src/apothem/harnesses/qwen_code/verify.py +11 -0
  428. package/src/apothem/harnesses/trae/STANDARD-CONVENTION-PIN.md +70 -0
  429. package/src/apothem/harnesses/trae/__init__.py +49 -0
  430. package/src/apothem/harnesses/trae/capabilities.yml +34 -0
  431. package/src/apothem/harnesses/trae/install.py +38 -0
  432. package/src/apothem/harnesses/trae/templates/apothem-rules.md +37 -0
  433. package/src/apothem/harnesses/trae/uninstall.py +25 -0
  434. package/src/apothem/harnesses/trae/update.py +10 -0
  435. package/src/apothem/harnesses/trae/verify.py +11 -0
  436. package/src/apothem/harnesses/windsurf/STANDARD-CONVENTION-PIN.md +91 -0
  437. package/src/apothem/harnesses/windsurf/__init__.py +52 -0
  438. package/src/apothem/harnesses/windsurf/capabilities.yml +40 -0
  439. package/src/apothem/harnesses/windsurf/install.py +41 -0
  440. package/src/apothem/harnesses/windsurf/templates/apothem-rules.md +37 -0
  441. package/src/apothem/harnesses/windsurf/uninstall.py +25 -0
  442. package/src/apothem/harnesses/windsurf/update.py +10 -0
  443. package/src/apothem/harnesses/windsurf/verify.py +11 -0
  444. package/src/apothem/harnesses/zed/STANDARD-CONVENTION-PIN.md +92 -0
  445. package/src/apothem/harnesses/zed/__init__.py +57 -0
  446. package/src/apothem/harnesses/zed/capabilities.yml +38 -0
  447. package/src/apothem/harnesses/zed/install.py +41 -0
  448. package/src/apothem/harnesses/zed/templates/apothem-rules.md +32 -0
  449. package/src/apothem/harnesses/zed/uninstall.py +28 -0
  450. package/src/apothem/harnesses/zed/update.py +10 -0
  451. package/src/apothem/harnesses/zed/verify.py +11 -0
  452. package/src/apothem/hooks/README.md +81 -0
  453. package/src/apothem/hooks/__init__.py +24 -0
  454. package/src/apothem/hooks/askuserquestion_validator.py +380 -0
  455. package/src/apothem/hooks/dispatch.py +296 -0
  456. package/src/apothem/hooks/emit_hook_context.py +444 -0
  457. package/src/apothem/hooks/hooks.json +318 -0
  458. package/src/apothem/hooks/lib/README.md +39 -0
  459. package/src/apothem/hooks/lib/__init__.py +18 -0
  460. package/src/apothem/hooks/lib/bootstrap.ps1 +129 -0
  461. package/src/apothem/hooks/lib/bootstrap.sh +103 -0
  462. package/src/apothem/hooks/lib/events.py +51 -0
  463. package/src/apothem/hooks/lib/find-pwsh.ps1 +78 -0
  464. package/src/apothem/hooks/lib/find-pwsh.sh +76 -0
  465. package/src/apothem/hooks/lib/find-python.ps1 +63 -0
  466. package/src/apothem/hooks/lib/find-python.sh +97 -0
  467. package/src/apothem/hooks/lib/log.py +43 -0
  468. package/src/apothem/hooks/lib/resolve_root.py +264 -0
  469. package/src/apothem/hooks/messages/postcompact.md +14 -0
  470. package/src/apothem/hooks/messages/posttooluse-proactive-compaction.md +46 -0
  471. package/src/apothem/hooks/messages/precompact.md +14 -0
  472. package/src/apothem/hooks/messages/pretooluse-askuserquestion-recommended.md +65 -0
  473. package/src/apothem/hooks/messages/pretooluse-bash-plan-guard.md +97 -0
  474. package/src/apothem/hooks/messages/pretooluse-bash.md +39 -0
  475. package/src/apothem/hooks/messages/pretooluse-conformity.md +70 -0
  476. package/src/apothem/hooks/messages/pretooluse-dependency-guard.md +21 -0
  477. package/src/apothem/hooks/messages/pretooluse-edit-header-guard.md +61 -0
  478. package/src/apothem/hooks/messages/pretooluse-edit.md +21 -0
  479. package/src/apothem/hooks/messages/pretooluse-eval-guard.md +39 -0
  480. package/src/apothem/hooks/messages/pretooluse-notebookedit.md +11 -0
  481. package/src/apothem/hooks/messages/pretooluse-write-header-guard.md +45 -0
  482. package/src/apothem/hooks/messages/pretooluse-write-plan-guard.md +72 -0
  483. package/src/apothem/hooks/messages/pretooluse-write.md +21 -0
  484. package/src/apothem/hooks/messages/sessionstart.md +15 -0
  485. package/src/apothem/hooks/messages/stop.md +27 -0
  486. package/src/apothem/hooks/proactive_compaction_tracker.py +327 -0
  487. package/src/apothem/hooks/session_start_bootstrap.py +472 -0
  488. package/src/apothem/lib/README.md +42 -0
  489. package/src/apothem/lib/__init__.py +13 -0
  490. package/src/apothem/lib/atomic_io.py +189 -0
  491. package/src/apothem/lib/auditor.py +687 -0
  492. package/src/apothem/lib/clean_slate.py +396 -0
  493. package/src/apothem/lib/contexts.py +352 -0
  494. package/src/apothem/lib/data_home.py +255 -0
  495. package/src/apothem/lib/frontmatter.py +101 -0
  496. package/src/apothem/lib/harness_materializer.py +213 -0
  497. package/src/apothem/lib/harness_protocol.py +59 -0
  498. package/src/apothem/lib/harness_registry.py +282 -0
  499. package/src/apothem/lib/harness_registry_data.py +843 -0
  500. package/src/apothem/lib/install_ledger.py +347 -0
  501. package/src/apothem/lib/learning.py +540 -0
  502. package/src/apothem/lib/memory.py +347 -0
  503. package/src/apothem/lib/parallel_sweep.py +234 -0
  504. package/src/apothem/lib/plan_tiers.py +200 -0
  505. package/src/apothem/lib/plugin_bootstrap.py +132 -0
  506. package/src/apothem/lib/plugin_tree.py +599 -0
  507. package/src/apothem/lib/profile.py +755 -0
  508. package/src/apothem/lib/profile_projection.py +198 -0
  509. package/src/apothem/lib/propagation-manifest.yaml +878 -0
  510. package/src/apothem/lib/propagation.py +220 -0
  511. package/src/apothem/lib/python_resolver.py +189 -0
  512. package/src/apothem/lib/reporter.py +62 -0
  513. package/src/apothem/lib/workspace_migration.py +323 -0
  514. package/src/apothem/output-styles/README.md +41 -0
  515. package/src/apothem/output-styles/concise-engineer.md +49 -0
  516. package/src/apothem/output-styles/default-architect.md +52 -0
  517. package/src/apothem/output-styles/default.md +113 -0
  518. package/src/apothem/output-styles/forensic-auditor.md +63 -0
  519. package/src/apothem/py.typed +0 -0
  520. package/src/apothem/rules/README.md +121 -0
  521. package/src/apothem/rules/agent-capability-discipline-matrix.md +89 -0
  522. package/src/apothem/rules/agent-capability-discipline.md +78 -0
  523. package/src/apothem/rules/agent-orchestration-patterns.md +144 -0
  524. package/src/apothem/rules/agent-orchestration.md +65 -0
  525. package/src/apothem/rules/agents-md-convention.md +86 -0
  526. package/src/apothem/rules/agile-sprints-elements.md +135 -0
  527. package/src/apothem/rules/agile-sprints.md +64 -0
  528. package/src/apothem/rules/agnostic-posture-checklist.md +47 -0
  529. package/src/apothem/rules/agnostic-posture.md +48 -0
  530. package/src/apothem/rules/authoritative-referencing-quotation.md +50 -0
  531. package/src/apothem/rules/authoritative-referencing.md +66 -0
  532. package/src/apothem/rules/authority-inquiry-categories.md +58 -0
  533. package/src/apothem/rules/authority-inquiry.md +54 -0
  534. package/src/apothem/rules/auto-memory-topic-files.md +86 -0
  535. package/src/apothem/rules/auto-memory.md +67 -0
  536. package/src/apothem/rules/bidirectional-binding.md +123 -0
  537. package/src/apothem/rules/canonical-layout-reporting-tiers.md +212 -0
  538. package/src/apothem/rules/canonical-layout.md +60 -0
  539. package/src/apothem/rules/clean-architecture-layers.md +186 -0
  540. package/src/apothem/rules/clean-room-generation-protocols.md +124 -0
  541. package/src/apothem/rules/clean-room-generation.md +59 -0
  542. package/src/apothem/rules/code-craft-conventions.md +101 -0
  543. package/src/apothem/rules/code-craft-markdown.md +138 -0
  544. package/src/apothem/rules/code-craft-python.md +154 -0
  545. package/src/apothem/rules/code-craft-shell.md +192 -0
  546. package/src/apothem/rules/cognitive-identity-techniques.md +180 -0
  547. package/src/apothem/rules/cognitive-identity.md +81 -0
  548. package/src/apothem/rules/context-management-budget.md +46 -0
  549. package/src/apothem/rules/context-management-protocol.md +161 -0
  550. package/src/apothem/rules/context-management-scratch.md +128 -0
  551. package/src/apothem/rules/context-management.md +85 -0
  552. package/src/apothem/rules/definitiveness-virtues.md +67 -0
  553. package/src/apothem/rules/definitiveness.md +58 -0
  554. package/src/apothem/rules/determinism.md +81 -0
  555. package/src/apothem/rules/disclosure-ledger-markers.md +58 -0
  556. package/src/apothem/rules/disclosure-ledger.md +52 -0
  557. package/src/apothem/rules/dynamism.md +38 -0
  558. package/src/apothem/rules/etc-extension.md +57 -0
  559. package/src/apothem/rules/expertise-posture-elements.md +68 -0
  560. package/src/apothem/rules/expertise-posture.md +54 -0
  561. package/src/apothem/rules/freshness-facade.md +64 -0
  562. package/src/apothem/rules/harness-adapter-shape-schemas.md +162 -0
  563. package/src/apothem/rules/harness-adapter-shape.md +42 -0
  564. package/src/apothem/rules/host-discovery-manifests.md +50 -0
  565. package/src/apothem/rules/host-discovery.md +56 -0
  566. package/src/apothem/rules/i18n-discipline-locale-cohorts.md +120 -0
  567. package/src/apothem/rules/i18n-discipline.md +70 -0
  568. package/src/apothem/rules/interactive-questions-canonical-shapes.md +590 -0
  569. package/src/apothem/rules/interactive-questions-detail.md +41 -0
  570. package/src/apothem/rules/interactive-questions-sweep-matchers.md +184 -0
  571. package/src/apothem/rules/interactive-questions.md +89 -0
  572. package/src/apothem/rules/large-file-generation.md +112 -0
  573. package/src/apothem/rules/large-file-reading.md +59 -0
  574. package/src/apothem/rules/living-docs.md +85 -0
  575. package/src/apothem/rules/multi-agent-workflow.md +57 -0
  576. package/src/apothem/rules/operational-mandates-expanded.md +78 -0
  577. package/src/apothem/rules/operational-mandates.md +88 -0
  578. package/src/apothem/rules/option-annotation-form.md +60 -0
  579. package/src/apothem/rules/option-annotation.md +45 -0
  580. package/src/apothem/rules/own-voice-reimplementation.md +86 -0
  581. package/src/apothem/rules/performance-discipline.md +91 -0
  582. package/src/apothem/rules/persistent-conventions-vigilance-checklist.md +54 -0
  583. package/src/apothem/rules/persistent-conventions-vigilance.md +61 -0
  584. package/src/apothem/rules/plain-language.md +56 -0
  585. package/src/apothem/rules/planning-techniques.md +130 -0
  586. package/src/apothem/rules/pre-emission-gate-bars.md +86 -0
  587. package/src/apothem/rules/pre-emission-gate.md +54 -0
  588. package/src/apothem/rules/production-ready-prs-surfaces.md +162 -0
  589. package/src/apothem/rules/production-ready-prs.md +83 -0
  590. package/src/apothem/rules/propagation.md +63 -0
  591. package/src/apothem/rules/recommend-next-step.md +106 -0
  592. package/src/apothem/rules/refactoring-discipline.md +76 -0
  593. package/src/apothem/rules/session-closure.md +44 -0
  594. package/src/apothem/rules/sota-elevation-exemplars.md +76 -0
  595. package/src/apothem/rules/sota-elevation.md +52 -0
  596. package/src/apothem/rules/source-accessibility.md +58 -0
  597. package/src/apothem/rules/surgical-manipulation.md +48 -0
  598. package/src/apothem/rules/systemic-participation-relations.md +108 -0
  599. package/src/apothem/rules/systemic-participation.md +70 -0
  600. package/src/apothem/rules/ten-dimension-check-dimensions.md +52 -0
  601. package/src/apothem/rules/ten-dimension-check.md +59 -0
  602. package/src/apothem/rules/token-budget-discipline.md +81 -0
  603. package/src/apothem/rules/token-efficiency-rewrite-protocol.md +79 -0
  604. package/src/apothem/rules/token-efficiency-rewrite.md +77 -0
  605. package/src/apothem/rules/tool-use-discipline.md +48 -0
  606. package/src/apothem/rules/visual-leverage.md +102 -0
  607. package/src/apothem/schemas/NOTICE.md +9 -0
  608. package/src/apothem/schemas/README.md +104 -0
  609. package/src/apothem/schemas/__init__.py +176 -0
  610. package/src/apothem/schemas/advisory-finding.schema.json +111 -0
  611. package/src/apothem/schemas/agent.schema.json +106 -0
  612. package/src/apothem/schemas/authorship-header.txt +1 -0
  613. package/src/apothem/schemas/cohort-manifest.yaml +248 -0
  614. package/src/apothem/schemas/cohort-metadata-vocabulary.yaml +168 -0
  615. package/src/apothem/schemas/cohort.schema.json +113 -0
  616. package/src/apothem/schemas/command.schema.json +68 -0
  617. package/src/apothem/schemas/compatibility-matrix.yaml +432 -0
  618. package/src/apothem/schemas/context-fragment.schema.json +64 -0
  619. package/src/apothem/schemas/freshness-token-denylist.txt +51 -0
  620. package/src/apothem/schemas/handoff-manifest.yaml +353 -0
  621. package/src/apothem/schemas/header-exceptions.txt +141 -0
  622. package/src/apothem/schemas/header-visibility.yaml +39 -0
  623. package/src/apothem/schemas/learning-signal.schema.json +46 -0
  624. package/src/apothem/schemas/memory-record.schema.json +61 -0
  625. package/src/apothem/schemas/output-style.schema.json +40 -0
  626. package/src/apothem/schemas/plan.schema.json +51 -0
  627. package/src/apothem/schemas/plugin.schema.json +83 -0
  628. package/src/apothem/schemas/profile.example.yaml +70 -0
  629. package/src/apothem/schemas/profile.minimal.yaml +6 -0
  630. package/src/apothem/schemas/profile.schema.json +396 -0
  631. package/src/apothem/schemas/reference-token-denylist.txt +25 -0
  632. package/src/apothem/schemas/skill.schema.json +75 -0
  633. package/src/apothem/skills/README.md +93 -0
  634. package/src/apothem/skills/dependency-upgrade/SKILL.md +105 -0
  635. package/src/apothem/skills/dev-toolkit/SKILL.md +120 -0
  636. package/src/apothem/skills/diagram-authoring/SKILL.md +113 -0
  637. package/src/apothem/skills/document-authoring/SKILL.md +118 -0
  638. package/src/apothem/skills/ecosystem-audit/SKILL.md +108 -0
  639. package/src/apothem/skills/ecosystem-audit/references/audit-fortress.md +85 -0
  640. package/src/apothem/skills/ecosystem-audit/references/procedure.md +162 -0
  641. package/src/apothem/skills/eval-harness/SKILL.md +88 -0
  642. package/src/apothem/skills/incident-runbook/SKILL.md +92 -0
  643. package/src/apothem/skills/multi-source-research/SKILL.md +90 -0
  644. package/src/apothem/skills/plan-suite/SKILL.md +118 -0
  645. package/src/apothem/skills/plan-suite/master_template.md +1324 -0
  646. package/src/apothem/skills/projectify/SKILL.md +117 -0
  647. package/src/apothem/skills/prompt-engineering/SKILL.md +122 -0
  648. package/src/apothem/skills/refactor-extract/SKILL.md +85 -0
  649. package/src/apothem/skills/research-suite/SKILL.md +170 -0
  650. package/src/apothem/skills/research-suite/references/directory-structure.md +47 -0
  651. package/src/apothem/skills/research-suite/references/lifecycle.md +67 -0
  652. package/src/apothem/skills/research-suite/references/principal-investigator-framework.md +37 -0
  653. package/src/apothem/skills/research-suite/references/rigor-mandates.md +30 -0
  654. package/src/apothem/skills/research-suite/research_template.md +476 -0
  655. package/src/apothem/skills/secret-rotation/SKILL.md +87 -0
  656. package/src/apothem/skills/source-synthesis/SKILL.md +92 -0
  657. package/src/apothem/skills/surgical-guard/SKILL.md +118 -0
  658. package/src/apothem/skills/test-authoring/SKILL.md +85 -0
  659. package/src/apothem/skills/vuln-triage/SKILL.md +91 -0
  660. package/src/apothem/skills/workflow/SKILL.md +139 -0
  661. package/src/apothem/statuslines/README.md +26 -0
  662. package/src/apothem/statuslines/__init__.py +20 -0
  663. package/src/apothem/statuslines/conformity.json +5 -0
  664. package/src/apothem/statuslines/render.py +334 -0
  665. package/src/apothem/statuslines/statusline.md +50 -0
  666. package/src/apothem/templates/README.md +43 -0
  667. package/src/apothem/templates/agents-md-template.md +80 -0
  668. package/src/apothem/templates/consideration-log.md +39 -0
  669. package/src/apothem/templates/expertise-gap-log.md +56 -0
  670. package/src/apothem/templates/master-index-template.md +93 -0
  671. package/src/apothem/templates/potency-map.md +53 -0
  672. package/src/apothem/templates/preservation-audit.md +60 -0
  673. package/src/apothem/templates/question-resolution-audit.md +52 -0
  674. package/src/apothem/templates/trace-matrix-template.md +77 -0
@@ -0,0 +1,599 @@
1
+ # SPDX-License-Identifier: MIT
2
+
3
+ """Plugin source-tree assembler and manifest generator.
4
+
5
+ This module materializes the canonical ``.claude-plugin`` distribution unit:
6
+ a self-contained bundle of the apothem engine plus its catalog (skills,
7
+ agents, commands, rules, hooks) that runs directly from the assembled tree —
8
+ the engine and its vendored dependencies import in place, and no package
9
+ installation occurs at any point.
10
+
11
+ :func:`assemble_plugin_tree` copies the ``apothem`` package verbatim into
12
+ ``lib/apothem`` and writes a thin ``apothem_lib`` shim re-exporting its
13
+ public submodules; the engine is never renamed or rewritten.
14
+
15
+ The layout contract produced is::
16
+
17
+ <plugin_root>/.claude-plugin/plugin.json
18
+ <plugin_root>/lib/apothem/ (verbatim engine copy)
19
+ <plugin_root>/lib/apothem_lib.py (re-export shim)
20
+ <plugin_root>/lib/apothem/_vendor/ (vendored deps, or empty .keep)
21
+ <plugin_root>/skills|agents|commands|hooks|rules/ (catalog copies)
22
+ """
23
+
24
+ from __future__ import annotations
25
+
26
+ import json
27
+ import re
28
+ import shutil
29
+ from pathlib import Path
30
+ from typing import Final
31
+
32
+ import jsonschema
33
+
34
+ #: Plugin identifier. The plugin *is* apothem (verbatim engine reuse).
35
+ PLUGIN_NAME: Final[str] = "apothem"
36
+
37
+ #: Capability-led description embedded in the generated manifest.
38
+ PLUGIN_DESCRIPTION: Final[str] = (
39
+ "Host-agnostic AI-harness configuration catalog: review and audit "
40
+ "commands, research and quality agents, and reusable skills, bundled "
41
+ "with the self-contained apothem engine."
42
+ )
43
+
44
+ #: Manifest metadata embedded in every generated plugin manifest.
45
+ PLUGIN_AUTHOR: Final[dict[str, str]] = {
46
+ "name": "Ahmed G. Gad",
47
+ "email": "me@ahmedgad.com",
48
+ "url": "https://github.com/ahmed-g-gad",
49
+ }
50
+ PLUGIN_HOMEPAGE: Final[str] = "https://apothem.ahmedgad.com"
51
+ PLUGIN_REPOSITORY: Final[str] = "https://github.com/ahmed-g-gad/apothem"
52
+ PLUGIN_LICENSE: Final[str] = "MIT"
53
+ PLUGIN_KEYWORDS: Final[tuple[str, ...]] = (
54
+ "configuration",
55
+ "profiles",
56
+ "skills",
57
+ "agents",
58
+ "commands",
59
+ )
60
+
61
+ #: Documentation files that live beside catalog members but are never members.
62
+ _CATALOG_DOC_FILES: Final[frozenset[str]] = frozenset({"README.md", "AGENTS.md"})
63
+
64
+ #: Directory names that are pruned from the engine copy (never distributed).
65
+ _COPY_IGNORE: Final[tuple[str, ...]] = (
66
+ "__pycache__",
67
+ "*.pyc",
68
+ "*.pyo",
69
+ ".pytest_cache",
70
+ ".mypy_cache",
71
+ ".ruff_cache",
72
+ )
73
+
74
+ #: Catalog directory names copied from the engine source into the plugin root.
75
+ _CATALOG_DIRS: Final[tuple[str, ...]] = (
76
+ "skills",
77
+ "agents",
78
+ "commands",
79
+ "rules",
80
+ )
81
+
82
+ #: Source-relative location of the hook message catalog.
83
+ _HOOK_MESSAGES_REL: Final[Path] = Path("hooks") / "messages"
84
+
85
+ #: Plugin-root-relative engine prefix for the assembled tree. The engine is
86
+ #: copied verbatim to ``lib/apothem`` by :func:`assemble_plugin_tree`, so the
87
+ #: bootstrap stubs, the dispatcher, and the message catalog all resolve under
88
+ #: this prefix when the plugin is installed alone from the assembled bundle.
89
+ _ASSEMBLED_ENGINE_ROOT_REL: Final[str] = "lib/apothem"
90
+
91
+ #: Plugin-root-relative engine prefix for the repository-root manifest. When
92
+ #: the repository root *is* the plugin root (the marketplace install whose
93
+ #: plugin root is the checkout), the engine lives at ``src/apothem``.
94
+ _REPO_ENGINE_ROOT_REL: Final[str] = "src/apothem"
95
+
96
+ #: The hook-event block, mirrored from the engine's claude_code
97
+ #: ``settings.json`` template. Each entry is a dispatch-routable event the
98
+ #: shell bootstrap stub (``hooks/lib/bootstrap.{sh,ps1}``) can drive: the stub
99
+ #: self-locates a CPython interpreter and execs ``hooks/dispatch.py`` with the
100
+ #: event name and, where the event emits a Markdown context, the message file's
101
+ #: full path. Conformity-gate entries from ``settings.json``
102
+ #: (``gate.py --hook``) are intentionally NOT mirrored here: the bootstrap stub
103
+ #: drives only the dispatcher, and the gate's user-scope default applies under
104
+ #: a harness root (``~/.claude`` / ``~/.codex``), not a plugin-alone project
105
+ #: write — wiring it plugin-alone would require an engine install. The per-event
106
+ #: timeouts and matchers track the ``settings.json`` block verbatim so the
107
+ #: plugin-alone and engine-install postures stay coherent.
108
+ #:
109
+ #: Each tuple is ``(event_name, matcher, timeout_seconds, message_basename)``;
110
+ #: ``message_basename`` is ``None`` for events the dispatcher handles without a
111
+ #: Markdown context (``SessionStart`` runs the session-start bootstrap directly).
112
+ _PLUGIN_HOOK_ENTRIES: Final[tuple[tuple[str, str, int, str | None], ...]] = (
113
+ ("SessionStart", "*", 30, None),
114
+ ("PreToolUse", "Write", 10, "pretooluse-write"),
115
+ ("PreToolUse", "Write", 10, "pretooluse-write-header-guard"),
116
+ ("PreToolUse", "Write", 10, "pretooluse-write-plan-guard"),
117
+ ("PreToolUse", "Write", 10, "pretooluse-dependency-guard"),
118
+ ("PreToolUse", "Write", 10, "pretooluse-eval-guard"),
119
+ ("PreToolUse", "Edit", 10, "pretooluse-edit"),
120
+ ("PreToolUse", "Edit", 10, "pretooluse-edit-header-guard"),
121
+ ("PreToolUse", "Edit", 10, "pretooluse-write-plan-guard"),
122
+ ("PreToolUse", "Edit", 10, "pretooluse-dependency-guard"),
123
+ ("PreToolUse", "Edit", 10, "pretooluse-eval-guard"),
124
+ ("PreToolUse", "NotebookEdit", 10, "pretooluse-notebookedit"),
125
+ ("PreToolUse", "NotebookEdit", 10, "pretooluse-write-plan-guard"),
126
+ ("PreToolUse", "Bash", 10, "pretooluse-bash"),
127
+ ("PreToolUse", "Bash", 10, "pretooluse-bash-plan-guard"),
128
+ ("PreToolUse", "Bash", 10, "pretooluse-eval-guard"),
129
+ ("PreToolUse", "AskUserQuestion", 10, "pretooluse-askuserquestion-recommended"),
130
+ ("PostToolUse", "*", 10, "posttooluse-proactive-compaction"),
131
+ ("PreCompact", "*", 30, "precompact"),
132
+ ("PostCompact", "*", 30, "postcompact"),
133
+ ("Stop", "*", 60, "stop"),
134
+ )
135
+
136
+ #: The thin alias shim re-exporting the engine's public submodules.
137
+ _SHIM_BODY: Final[str] = '''# SPDX-License-Identifier: MIT
138
+
139
+ """Thin alias shim re-exporting the bundled apothem engine's public API.
140
+
141
+ Importing ``apothem_lib`` makes the engine's public submodules reachable
142
+ under one flat namespace (``apothem_lib.profile``, ``apothem_lib.adapters``,
143
+ ...) without requiring callers to know the bundled package layout.
144
+ """
145
+
146
+ from __future__ import annotations
147
+
148
+ from apothem import __version__
149
+ from apothem import harnesses as adapters
150
+ from apothem.lib import profile, propagation, reporter
151
+
152
+ __all__ = [
153
+ "__version__",
154
+ "adapters",
155
+ "profile",
156
+ "propagation",
157
+ "reporter",
158
+ ]
159
+ '''
160
+
161
+
162
+ class PluginAssemblyError(RuntimeError):
163
+ """Raised when the plugin source tree cannot be assembled.
164
+
165
+ The message names the offending path so the caller can diagnose a
166
+ malformed source (e.g. a missing catalog directory). The assembler
167
+ never silently skips a malformed input.
168
+ """
169
+
170
+
171
+ def _version_from_pyproject() -> str | None:
172
+ """Read ``[project].version`` from the repo's ``pyproject.toml`` when present.
173
+
174
+ The repo's own manifest must track the authoritative in-tree version
175
+ rather than the version of whatever copy is installed in site-packages
176
+ (an editable install can be stale). Walks upward from this module to the
177
+ nearest ``pyproject.toml``.
178
+
179
+ Returns:
180
+ The declared version, or ``None`` when no ``pyproject.toml`` is found
181
+ or it declares no project version.
182
+ """
183
+ for parent in Path(__file__).resolve().parents:
184
+ candidate = parent / "pyproject.toml"
185
+ if not candidate.is_file():
186
+ continue
187
+ text = candidate.read_text(encoding="utf-8")
188
+ match = re.search(
189
+ r"^\s*version\s*=\s*[\"']([^\"']+)[\"']", text, flags=re.MULTILINE
190
+ )
191
+ return match.group(1) if match else None
192
+ return None
193
+
194
+
195
+ def _resolve_version(version: str | None) -> str:
196
+ """Resolve the manifest version from the authoritative source.
197
+
198
+ Precedence: an explicit argument, then the repo's ``pyproject.toml``,
199
+ then the installed ``apothem.__version__`` as a last resort.
200
+
201
+ Args:
202
+ version: Explicit version, or ``None`` to resolve automatically.
203
+
204
+ Returns:
205
+ A ``MAJOR.MINOR.PATCH`` SemVer string.
206
+ """
207
+ if version is not None:
208
+ return version
209
+ declared = _version_from_pyproject()
210
+ if declared is not None:
211
+ return declared
212
+ from apothem import __version__
213
+
214
+ return __version__
215
+
216
+
217
+ def _enumerate_members(catalog_root: Path) -> dict[str, list[str]]:
218
+ """Enumerate catalog members under ``catalog_root``.
219
+
220
+ Skills are directories carrying a ``SKILL.md`` entry point (id = dirname).
221
+ Agents, commands, and rules are flat ``*.md`` files (id = stem). Hooks are
222
+ the hook message ``*.md`` files (id = stem). ``README.md`` and
223
+ ``AGENTS.md`` index files are excluded — they are documentation, not
224
+ catalog members.
225
+
226
+ Args:
227
+ catalog_root: The apothem source package directory.
228
+
229
+ Returns:
230
+ A mapping of member kind to a sorted list of member ids.
231
+
232
+ Raises:
233
+ PluginAssemblyError: When a required catalog directory is absent.
234
+ """
235
+
236
+ def _require_dir(rel: Path) -> Path:
237
+ path = catalog_root / rel
238
+ if not path.is_dir():
239
+ raise PluginAssemblyError(f"required catalog directory missing: {path}")
240
+ return path
241
+
242
+ skills_dir = _require_dir(Path("skills"))
243
+ skills = sorted(
244
+ child.name
245
+ for child in skills_dir.iterdir()
246
+ if child.is_dir() and (child / "SKILL.md").is_file()
247
+ )
248
+
249
+ def _markdown_stems(rel: Path) -> list[str]:
250
+ directory = _require_dir(rel)
251
+ return sorted(
252
+ md.stem
253
+ for md in directory.glob("*.md")
254
+ if md.name not in _CATALOG_DOC_FILES
255
+ )
256
+
257
+ return {
258
+ "skills": skills,
259
+ "agents": _markdown_stems(Path("agents")),
260
+ "commands": _markdown_stems(Path("commands")),
261
+ "rules": _markdown_stems(Path("rules")),
262
+ "hooks": _markdown_stems(_HOOK_MESSAGES_REL),
263
+ }
264
+
265
+
266
+ def build_plugin_manifest(
267
+ catalog_root: Path,
268
+ *,
269
+ version: str | None = None,
270
+ catalog_prefix: str = "./",
271
+ address_default_command_dir: bool = False,
272
+ ) -> dict[str, object]:
273
+ """Build a Claude Code plugin manifest dict from the apothem catalog.
274
+
275
+ Enumerates every catalog member under ``catalog_root`` and assembles a
276
+ manifest conforming to ``plugin.schema.json``. Commands and agents are
277
+ declared as explicit file paths so the documentation files beside them
278
+ are never registered as components; skills are declared as the skills
279
+ directory (each immediate child carries a ``SKILL.md`` entry point).
280
+ Rules and hook message contexts are engine cohorts consumed by the
281
+ apothem materializers, not plugin components, so they carry no manifest
282
+ field. Component arrays are sorted for determinism: the same catalog
283
+ always yields the same manifest.
284
+
285
+ Args:
286
+ catalog_root: The apothem source package directory (the dir holding
287
+ ``skills/``, ``agents/``, ``commands/``, ``rules/``, ``hooks/``).
288
+ version: Explicit SemVer string, or ``None`` to resolve from
289
+ ``pyproject.toml`` / ``apothem.__version__``.
290
+ catalog_prefix: Plugin-root-relative prefix of the catalog
291
+ directories, beginning with ``./`` — ``./`` when the catalog
292
+ sits at the plugin root (the assembled tree), or
293
+ ``./src/apothem/`` when the repository root is the plugin root.
294
+ address_default_command_dir: When ``True``, prepend the plugin-root
295
+ default ``./commands/`` directory to the ``commands`` array. The
296
+ repository root is simultaneously this Claude Code plugin's root
297
+ and the Gemini / Qwen extension root, so a bare ``./commands/``
298
+ (holding the extensions' TOML passthrough command, not a Claude
299
+ ``*.md`` command) sits at the plugin root. Claude Code v2.1.140+
300
+ flags that default folder as "ignored" unless the manifest
301
+ addresses it explicitly; listing it — the folder holds no
302
+ ``*.md``, so no Claude command loads from it — suppresses the
303
+ note while the explicit nested ``commands`` paths stay
304
+ authoritative. Set ``True`` only when ``catalog_prefix`` is
305
+ nested (the repository-root manifest); the assembled tree's
306
+ commands already point into its own ``./commands/`` and need no
307
+ extra entry.
308
+
309
+ Returns:
310
+ A manifest dict validating against ``plugin.schema.json``.
311
+
312
+ Raises:
313
+ PluginAssemblyError: When a required catalog directory is absent.
314
+ jsonschema.ValidationError: When the assembled manifest does not
315
+ conform to the plugin schema.
316
+ """
317
+ members = _enumerate_members(catalog_root)
318
+ command_paths = sorted(
319
+ f"{catalog_prefix}commands/{stem}.md" for stem in members["commands"]
320
+ )
321
+ if address_default_command_dir:
322
+ command_paths = ["./commands/", *command_paths]
323
+ engine_root_rel = _engine_root_rel_for(catalog_prefix)
324
+ manifest: dict[str, object] = {
325
+ "name": PLUGIN_NAME,
326
+ "version": _resolve_version(version),
327
+ "description": PLUGIN_DESCRIPTION,
328
+ "author": dict(PLUGIN_AUTHOR),
329
+ "homepage": PLUGIN_HOMEPAGE,
330
+ "repository": PLUGIN_REPOSITORY,
331
+ "license": PLUGIN_LICENSE,
332
+ "keywords": list(PLUGIN_KEYWORDS),
333
+ "commands": command_paths,
334
+ "agents": sorted(
335
+ f"{catalog_prefix}agents/{stem}.md" for stem in members["agents"]
336
+ ),
337
+ "skills": [f"{catalog_prefix}skills/"],
338
+ "hooks": f"./{engine_root_rel}/hooks/hooks.json",
339
+ }
340
+ _validate_manifest(manifest)
341
+ return manifest
342
+
343
+
344
+ def _engine_root_rel_for(catalog_prefix: str) -> str:
345
+ """Map a catalog prefix to its plugin-root-relative engine prefix.
346
+
347
+ The catalog directories and the engine sit at distinct locations in the
348
+ two supported layouts:
349
+
350
+ * Assembled tree (``catalog_prefix == "./"``): the catalog is at the
351
+ plugin root, but the verbatim engine copy lives under ``lib/apothem``.
352
+ * Repository-root manifest (``catalog_prefix == "./src/apothem/"``): the
353
+ catalog and the engine share the ``src/apothem`` prefix.
354
+
355
+ Args:
356
+ catalog_prefix: The plugin-root-relative catalog prefix.
357
+
358
+ Returns:
359
+ The plugin-root-relative engine prefix (no leading ``./``, no
360
+ trailing slash).
361
+ """
362
+ if catalog_prefix == "./":
363
+ return _ASSEMBLED_ENGINE_ROOT_REL
364
+ return catalog_prefix.strip("./")
365
+
366
+
367
+ def _bootstrap_command(engine_root_rel: str, shell: str, suffix: str) -> str:
368
+ """Render a single bootstrap-stub ``command`` string for a hook entry.
369
+
370
+ The command invokes the shell bootstrap stub under the plugin install
371
+ directory (``${CLAUDE_PLUGIN_ROOT}``, the placeholder Claude Code
372
+ substitutes and exports). The stub self-locates a CPython >= 3.10
373
+ interpreter and execs ``hooks/dispatch.py``; no engine install or
374
+ install-time ``${PYTHON_BIN}`` substitution is required.
375
+
376
+ Args:
377
+ engine_root_rel: Plugin-root-relative engine prefix
378
+ (``lib/apothem`` for the assembled tree, ``src/apothem`` for the
379
+ repository-root manifest).
380
+ shell: ``"bash"`` (drives ``bootstrap.sh``) or ``"powershell"``
381
+ (drives ``bootstrap.ps1``).
382
+ suffix: The argument string appended after the stub path — the event
383
+ name plus, where present, the message-file path. Already shaped
384
+ for the target shell's argument convention.
385
+
386
+ Returns:
387
+ The ``command`` string for one ``hooks.json`` command entry.
388
+ """
389
+ if shell == "powershell":
390
+ stub = f"${{CLAUDE_PLUGIN_ROOT}}/{engine_root_rel}/hooks/lib/bootstrap.ps1"
391
+ return f'pwsh -NoProfile -File "{stub}" {suffix}'
392
+ stub = f"${{CLAUDE_PLUGIN_ROOT}}/{engine_root_rel}/hooks/lib/bootstrap.sh"
393
+ return f'"{stub}" {suffix}'
394
+
395
+
396
+ def _entry_command_block(
397
+ engine_root_rel: str, event: str, timeout: int, message: str | None
398
+ ) -> list[dict[str, object]]:
399
+ """Return the bash + powershell command pair for one hook entry.
400
+
401
+ Each dispatch-routable entry is mirrored as two ``type: command`` hooks —
402
+ one driving the POSIX ``bootstrap.sh`` (``shell: bash``) and one driving
403
+ ``bootstrap.ps1`` (``shell: powershell``) — so the hook fires on every
404
+ host. The bootstrap stubs are fail-open (exit 0 on any error) so neither
405
+ entry can stall the harness.
406
+
407
+ Args:
408
+ engine_root_rel: Plugin-root-relative engine prefix.
409
+ event: The hook event name (e.g. ``PreToolUse``).
410
+ timeout: Per-entry timeout in seconds, mirrored from the engine's
411
+ ``settings.json`` block.
412
+ message: The message-file basename (without ``.md``), or ``None`` for
413
+ events the dispatcher handles without a Markdown context.
414
+
415
+ Returns:
416
+ A two-element list of command-hook dicts (bash then powershell).
417
+ """
418
+ if message is None:
419
+ suffix = event
420
+ else:
421
+ msg_rel = f"{engine_root_rel}/hooks/messages/{message}.md"
422
+ suffix = f'{event} "${{CLAUDE_PLUGIN_ROOT}}/{msg_rel}"'
423
+ return [
424
+ {
425
+ "type": "command",
426
+ "command": _bootstrap_command(engine_root_rel, shell, suffix),
427
+ "timeout": timeout,
428
+ "shell": shell,
429
+ }
430
+ for shell in ("bash", "powershell")
431
+ ]
432
+
433
+
434
+ def build_plugin_hooks_json(engine_root_rel: str) -> dict[str, object]:
435
+ """Build a Claude Code ``hooks.json`` mirroring the engine hook block.
436
+
437
+ Produces the plugin-alone hook configuration: a ``hooks`` map keyed by
438
+ event name, each event carrying matcher-grouped command blocks. Every
439
+ command drives the shell bootstrap stub under ``${CLAUDE_PLUGIN_ROOT}``
440
+ (one ``bash`` + one ``powershell`` entry per dispatch-routable hook), so
441
+ the conformity nudges, the session bootstrap, and the compaction / stop
442
+ handlers fire when the plugin is installed alone — without an apothem
443
+ engine install or the install-time ``${PYTHON_BIN}`` / ``${HARNESS_ROOT}``
444
+ substitution the engine ``settings.json`` relies on.
445
+
446
+ The event set, matchers, and per-event timeouts mirror the engine's
447
+ claude_code ``settings.json`` block verbatim, minus the ``gate.py --hook``
448
+ conformity entries (the bootstrap stub drives only the dispatcher, and the
449
+ gate's scope default targets a harness root, not a plugin-alone project
450
+ write — see ``_PLUGIN_HOOK_ENTRIES``).
451
+
452
+ Args:
453
+ engine_root_rel: Plugin-root-relative engine prefix —
454
+ ``lib/apothem`` for the assembled tree (``catalog_prefix="./"``),
455
+ or ``src/apothem`` for the repository-root manifest
456
+ (``catalog_prefix="./src/apothem/"``).
457
+
458
+ Returns:
459
+ A ``hooks.json`` dict carrying the ``hooks`` event map.
460
+ """
461
+ # Accumulate one command-list per (event, matcher) so consecutive entries
462
+ # sharing a matcher (e.g. the three Write guards) collapse into a single
463
+ # matcher group with an ordered command list — mirroring the engine
464
+ # ``settings.json`` grouping. ``dict`` preserves first-seen order, so the
465
+ # emitted event and matcher ordering is deterministic across runs.
466
+ accumulators: dict[tuple[str, str], list[dict[str, object]]] = {}
467
+ for event, matcher, timeout, message in _PLUGIN_HOOK_ENTRIES:
468
+ commands = accumulators.setdefault((event, matcher), [])
469
+ commands.extend(_entry_command_block(engine_root_rel, event, timeout, message))
470
+ events: dict[str, list[dict[str, object]]] = {}
471
+ for (event, matcher), commands in accumulators.items():
472
+ events.setdefault(event, []).append({"matcher": matcher, "hooks": commands})
473
+ return {"hooks": events}
474
+
475
+
476
+ def _plugin_schema_path() -> Path:
477
+ """Return the path to the bundled ``plugin.schema.json`` fixture."""
478
+ return Path(__file__).resolve().parent.parent / "schemas" / "plugin.schema.json"
479
+
480
+
481
+ def _validate_manifest(manifest: dict[str, object]) -> None:
482
+ """Validate a manifest against ``plugin.schema.json``.
483
+
484
+ Args:
485
+ manifest: The manifest dict to validate.
486
+
487
+ Raises:
488
+ jsonschema.ValidationError: When the manifest does not conform.
489
+ """
490
+ schema = json.loads(_plugin_schema_path().read_text(encoding="utf-8"))
491
+ jsonschema.validate(instance=manifest, schema=schema)
492
+
493
+
494
+ def assemble_plugin_tree(src_root: Path, dest_root: Path) -> Path:
495
+ """Materialize the canonical plugin source tree at ``dest_root``.
496
+
497
+ Copies the apothem engine verbatim into ``lib/apothem`` (AD-1 reuse),
498
+ writes the ``apothem_lib`` re-export shim, ensures the ``_vendor``
499
+ directory exists, copies the catalog directories, and writes the
500
+ generated ``plugin.json``. The operation is idempotent: re-running
501
+ overwrites the engine copy, shim, and catalog cleanly.
502
+
503
+ Args:
504
+ src_root: The apothem source package directory.
505
+ dest_root: The plugin root to populate.
506
+
507
+ Returns:
508
+ ``dest_root``.
509
+
510
+ Raises:
511
+ PluginAssemblyError: When ``src_root`` is not a directory or a
512
+ required catalog directory is absent.
513
+ """
514
+ if not src_root.is_dir():
515
+ raise PluginAssemblyError(f"source package directory missing: {src_root}")
516
+
517
+ # Build the manifest first so a malformed source fails before any write.
518
+ manifest = build_plugin_manifest(src_root)
519
+
520
+ dest_root.mkdir(parents=True, exist_ok=True)
521
+
522
+ # 1. Engine copy: lib/apothem (verbatim, pruned of caches).
523
+ lib_dir = dest_root / "lib"
524
+ lib_dir.mkdir(parents=True, exist_ok=True)
525
+ engine_dest = lib_dir / "apothem"
526
+ if engine_dest.exists():
527
+ shutil.rmtree(engine_dest)
528
+ shutil.copytree(
529
+ src_root,
530
+ engine_dest,
531
+ ignore=shutil.ignore_patterns(*_COPY_IGNORE),
532
+ )
533
+
534
+ # 2. Vendored deps: copy src _vendor if present, else create empty .keep.
535
+ vendor_dest = engine_dest / "_vendor"
536
+ src_vendor = src_root / "_vendor"
537
+ if src_vendor.is_dir():
538
+ if vendor_dest.exists():
539
+ shutil.rmtree(vendor_dest)
540
+ shutil.copytree(
541
+ src_vendor,
542
+ vendor_dest,
543
+ ignore=shutil.ignore_patterns(*_COPY_IGNORE),
544
+ )
545
+ else:
546
+ vendor_dest.mkdir(parents=True, exist_ok=True)
547
+ (vendor_dest / ".keep").write_text("", encoding="utf-8")
548
+
549
+ # 3. Alias shim: lib/apothem_lib.py.
550
+ (lib_dir / "apothem_lib.py").write_text(_SHIM_BODY, encoding="utf-8")
551
+
552
+ # 4. Catalog dirs: skills/agents/commands/rules copied to plugin root.
553
+ for name in _CATALOG_DIRS:
554
+ src_dir = src_root / name
555
+ if not src_dir.is_dir():
556
+ raise PluginAssemblyError(f"required catalog directory missing: {src_dir}")
557
+ dest_dir = dest_root / name
558
+ if dest_dir.exists():
559
+ shutil.rmtree(dest_dir)
560
+ shutil.copytree(
561
+ src_dir,
562
+ dest_dir,
563
+ ignore=shutil.ignore_patterns(*_COPY_IGNORE),
564
+ )
565
+
566
+ # 5. Hook message catalog: hooks/messages copied to plugin-root hooks/.
567
+ hooks_src = src_root / _HOOK_MESSAGES_REL
568
+ if not hooks_src.is_dir():
569
+ raise PluginAssemblyError(f"required catalog directory missing: {hooks_src}")
570
+ hooks_dest = dest_root / "hooks"
571
+ if hooks_dest.exists():
572
+ shutil.rmtree(hooks_dest)
573
+ shutil.copytree(
574
+ hooks_src,
575
+ hooks_dest,
576
+ ignore=shutil.ignore_patterns(*_COPY_IGNORE),
577
+ )
578
+
579
+ # 6. Plugin hooks.json: written beside the verbatim engine copy so the
580
+ # bootstrap stubs, dispatcher, and message catalog all resolve under the
581
+ # same ``lib/apothem`` prefix the manifest's ``hooks`` field points at.
582
+ # The engine copy (step 1) already materialized ``lib/apothem/hooks/``,
583
+ # so the directory exists.
584
+ hooks_json = build_plugin_hooks_json(_ASSEMBLED_ENGINE_ROOT_REL)
585
+ engine_hooks_dir = engine_dest / "hooks"
586
+ (engine_hooks_dir / "hooks.json").write_text(
587
+ json.dumps(hooks_json, indent=2, sort_keys=False) + "\n",
588
+ encoding="utf-8",
589
+ )
590
+
591
+ # 7. Manifest: .claude-plugin/plugin.json.
592
+ plugin_meta_dir = dest_root / ".claude-plugin"
593
+ plugin_meta_dir.mkdir(parents=True, exist_ok=True)
594
+ (plugin_meta_dir / "plugin.json").write_text(
595
+ json.dumps(manifest, indent=2, sort_keys=False) + "\n",
596
+ encoding="utf-8",
597
+ )
598
+
599
+ return dest_root