@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,101 @@
1
+ # SPDX-License-Identifier: MIT
2
+
3
+ """YAML frontmatter field probing and value extraction for ecosystem files.
4
+
5
+ Provides three layers of inspection:
6
+
7
+ * :func:`extract_frontmatter` — return the raw YAML block.
8
+ * :func:`field_value` — return the value of a single declared field as a string,
9
+ or ``None`` when the field is absent.
10
+ * :func:`has_field` / :func:`has_all_fields` — boolean presence checks.
11
+
12
+ The probe is intentionally regex-based rather than a full YAML parser so that
13
+ the validator surface has zero third-party dependencies and remains
14
+ import-time cheap. It supports the small subset of YAML actually used in
15
+ ecosystem frontmatter: scalar values (quoted or unquoted) on a single line.
16
+ """
17
+
18
+ from __future__ import annotations
19
+
20
+ import re
21
+ from pathlib import Path
22
+ from typing import Final
23
+
24
+ _FRONTMATTER_BLOCK: Final[re.Pattern[str]] = re.compile(
25
+ r"\A---\s*\n(.*?)\n---", re.DOTALL
26
+ )
27
+ _LEADING_HTML_COMMENT: Final[re.Pattern[str]] = re.compile(
28
+ r"\A\s*<!--.*?-->\s*", re.DOTALL
29
+ )
30
+
31
+
32
+ def extract_frontmatter(path: Path) -> str | None:
33
+ """Return the raw YAML block at the top of ``path``, or ``None``.
34
+
35
+ A leading HTML-comment block (the canonical authorship banner injected
36
+ ecosystem-wide above the YAML frontmatter on every Markdown file) is
37
+ skipped before matching the frontmatter delimiter so the probe sees
38
+ the YAML block at the correct anchor.
39
+
40
+ Args:
41
+ path: File to read.
42
+
43
+ Returns:
44
+ The block content between the opening and closing ``---`` lines, or
45
+ ``None`` when the file is unreadable or has no frontmatter.
46
+ """
47
+ try:
48
+ text = path.read_text(encoding="utf-8", errors="replace")
49
+ except OSError:
50
+ return None
51
+ text = _LEADING_HTML_COMMENT.sub("", text, count=1)
52
+ match = _FRONTMATTER_BLOCK.match(text)
53
+ return match.group(1) if match else None
54
+
55
+
56
+ def field_value(path: Path, field_name: str) -> str | None:
57
+ """Return the unquoted scalar value of ``field_name`` from ``path``.
58
+
59
+ Recognizes single-line ``key: "value"`` and ``key: value`` declarations.
60
+ Multi-line YAML constructs (block scalars, lists, mappings) return the
61
+ raw matched suffix and should be treated as opaque by callers.
62
+
63
+ Args:
64
+ path: File to read.
65
+ field_name: YAML key to look up.
66
+
67
+ Returns:
68
+ Stripped, dequoted value, or ``None`` when the field is absent.
69
+ """
70
+ block = extract_frontmatter(path)
71
+ if block is None:
72
+ return None
73
+ pattern = re.compile(
74
+ rf"^{re.escape(field_name)}\s*:\s*(.*?)\s*$",
75
+ re.MULTILINE,
76
+ )
77
+ match = pattern.search(block)
78
+ if match is None:
79
+ return None
80
+ raw = match.group(1).strip()
81
+ if (raw.startswith('"') and raw.endswith('"')) or (
82
+ raw.startswith("'") and raw.endswith("'")
83
+ ):
84
+ # Dequote by stripping the outer quotes only. Backslash escape
85
+ # sequences inside a double-quoted scalar (``"a \"b\""``) are NOT
86
+ # decoded — the inner text is returned verbatim. Apothem's own
87
+ # frontmatter values are plain prose with no escaped quotes, so this
88
+ # is sufficient in practice; a value needing escape decoding must be
89
+ # parsed with a full YAML loader by the caller.
90
+ return raw[1:-1]
91
+ return raw
92
+
93
+
94
+ def has_field(path: Path, field_name: str) -> bool:
95
+ """Return True when frontmatter in ``path`` declares ``field_name:``."""
96
+ return field_value(path, field_name) is not None
97
+
98
+
99
+ def has_all_fields(path: Path, field_names: list[str]) -> bool:
100
+ """Return True when every field in ``field_names`` is declared."""
101
+ return all(has_field(path, name) for name in field_names)
@@ -0,0 +1,213 @@
1
+ # SPDX-License-Identifier: MIT
2
+
3
+ """Shared building blocks for per-harness materializers.
4
+
5
+ These helpers cover profile-to-Markdown field extraction and the legacy
6
+ managed-YAML body shape retained for compatibility tests. Current harness
7
+ materializers that target strict vendor schemas should emit only documented
8
+ keys instead of using the generic managed body.
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ from collections.abc import Mapping
14
+ from dataclasses import dataclass, field
15
+ from typing import Any, Final
16
+
17
+ import yaml
18
+
19
+ from apothem.lib.profile import DEFAULT_LANGUAGE, DEFAULT_STYLE, coerce_profile
20
+
21
+ # Canonical sentinel pair delimiting the Apothem-managed block inside an
22
+ # operator-owned Markdown instruction anchor (AGENTS.md, GEMINI.md, QWEN.md,
23
+ # copilot-instructions.md, and the project-scope rule files). The pair is the
24
+ # stable, documented merge boundary each harness dossier references: Apothem
25
+ # owns only the bytes between the sentinels; operator prose outside is
26
+ # preserved verbatim. Changing these strings would orphan every previously
27
+ # installed managed block, so they are a frozen contract.
28
+ APOTHEM_BLOCK_BEGIN: Final[str] = "<!-- BEGIN APOTHEM MANAGED BLOCK -->"
29
+ APOTHEM_BLOCK_END: Final[str] = "<!-- END APOTHEM MANAGED BLOCK -->"
30
+
31
+
32
+ def wrap_managed_block(body: str) -> str:
33
+ """Return *body* wrapped in the canonical Apothem managed-block sentinels.
34
+
35
+ The wrapped form is the unit Apothem writes into an operator-owned
36
+ Markdown anchor. The body is stripped of surrounding blank lines so the
37
+ block is byte-stable across re-installs (idempotency depends on the
38
+ wrapped form being identical for identical bodies).
39
+ """
40
+ return f"{APOTHEM_BLOCK_BEGIN}\n{body.strip()}\n{APOTHEM_BLOCK_END}\n"
41
+
42
+
43
+ def extract_managed_block(text: str) -> str | None:
44
+ """Return the managed-block region found in *text*, or ``None``.
45
+
46
+ A well-formed block (begin sentinel followed by an end sentinel) returns
47
+ both sentinels and the body between them, exactly as in *text*. An *orphan*
48
+ begin sentinel with no following end sentinel (a truncated or
49
+ operator-mangled block) returns the degenerate region from the begin
50
+ sentinel to end-of-text, so :func:`merge_managed_block` REPLACES it in place
51
+ rather than appending a second block. Appending on an orphan would leave a
52
+ two-begin file whose next merge swallows the operator text trapped between
53
+ the orphan begin and the appended block — so the orphan is reclaimed on the
54
+ first merge instead (operator prose *before* the orphan begin is always
55
+ preserved; the caller writes a backup of the prior file). ``None`` only when
56
+ no begin sentinel is present at all.
57
+ """
58
+ begin = text.find(APOTHEM_BLOCK_BEGIN)
59
+ if begin == -1:
60
+ return None
61
+ end = text.find(APOTHEM_BLOCK_END, begin + len(APOTHEM_BLOCK_BEGIN))
62
+ if end == -1:
63
+ return text[begin:]
64
+ return text[begin : end + len(APOTHEM_BLOCK_END)]
65
+
66
+
67
+ def merge_managed_block(existing: str, body: str) -> str:
68
+ """Merge an Apothem managed block carrying *body* into *existing* text.
69
+
70
+ Three cases, all preserving operator prose:
71
+
72
+ - *existing* is empty — return the wrapped block alone.
73
+ - *existing* already carries a managed block — replace that block in
74
+ place, leaving operator prose before and after it untouched.
75
+ - *existing* carries no managed block — append the wrapped block after
76
+ the operator's content, separated by one blank line.
77
+
78
+ The merge is idempotent: re-merging the same *body* into text that
79
+ already carries the resulting block returns byte-identical output.
80
+ """
81
+ wrapped = wrap_managed_block(body)
82
+ if not existing.strip():
83
+ return wrapped
84
+ current_block = extract_managed_block(existing)
85
+ if current_block is not None:
86
+ return existing.replace(current_block, wrapped.rstrip("\n"), 1)
87
+ separator = "" if existing.endswith("\n") else "\n"
88
+ return f"{existing}{separator}\n{wrapped}"
89
+
90
+
91
+ def remove_managed_block(existing: str) -> str:
92
+ """Return *existing* with the Apothem managed block surgically removed.
93
+
94
+ The inverse of :func:`merge_managed_block`: strips the managed block (and the
95
+ blank-line separator the merge inserted around it) while preserving operator
96
+ prose before and after it. When the remainder is whitespace-only — i.e. the
97
+ anchor was Apothem-only — returns the empty string so the caller can delete
98
+ the now-empty file rather than leaving a stub. Returns *existing* unchanged
99
+ when it carries no managed block (including a degenerate orphan-begin block,
100
+ which :func:`extract_managed_block` reclaims).
101
+
102
+ Round-trips byte-for-byte for the canonical case: an operator prose block
103
+ ending in a single newline, merged-then-removed, returns the original.
104
+ """
105
+ block = extract_managed_block(existing)
106
+ if block is None:
107
+ return existing
108
+ index = existing.find(block)
109
+ before = existing[:index].rstrip("\n")
110
+ after = existing[index + len(block) :].lstrip("\n")
111
+ if before and after:
112
+ remainder = f"{before}\n\n{after}"
113
+ elif before:
114
+ remainder = f"{before}\n"
115
+ else:
116
+ remainder = after
117
+ return "" if not remainder.strip() else remainder
118
+
119
+
120
+ @dataclass(frozen=True)
121
+ class MarkdownProfileFields:
122
+ """The profile fields the Markdown-output adapters render into their body.
123
+
124
+ Carries the full identity (name/role/email/website/github), both
125
+ preference scalars (language/style), the seriousness band, the rules
126
+ list (+ its pre-rendered block), and the opted-in enforcement flags. The
127
+ optional fields default to absent so an empty profile renders cleanly; the
128
+ projection seam omits any field with no value.
129
+ """
130
+
131
+ name: str
132
+ role: str
133
+ language: str
134
+ rules: list[str]
135
+ seriousness: str
136
+ extra_rules_block: str
137
+ style: str = DEFAULT_STYLE
138
+ email: str | None = None
139
+ website: str | None = None
140
+ github: str | None = None
141
+ enforcement: Mapping[str, bool] = field(default_factory=dict)
142
+
143
+
144
+ def extract_markdown_fields(profile: dict[str, Any]) -> MarkdownProfileFields:
145
+ """Extract the canonical Markdown-output field set from *profile*.
146
+
147
+ The defaults are delegated to the canonical profile model so Markdown
148
+ helpers and schema-backed profile loading cannot drift. Accepts either a
149
+ raw profile dict or a ``CanonicalProfile.for_harness`` projection.
150
+ """
151
+ canonical = coerce_profile(profile)
152
+ normalized = canonical.to_dict()
153
+ identity = normalized["identity"]
154
+ preferences = normalized["preferences"]
155
+ rules: list[str] = normalized["rules"]
156
+ return MarkdownProfileFields(
157
+ name=identity["name"],
158
+ role=identity["role"],
159
+ language=preferences["language"],
160
+ rules=rules,
161
+ seriousness=normalized["seriousness"],
162
+ extra_rules_block=_render_extra_rules_block(rules),
163
+ style=preferences["style"],
164
+ email=identity.get("email"),
165
+ website=identity.get("website"),
166
+ github=identity.get("github"),
167
+ enforcement=dict(normalized["enforcement"]),
168
+ )
169
+
170
+
171
+ def _render_extra_rules_block(rules: list[str]) -> str:
172
+ """Format the optional ``## Custom Rules`` Markdown block.
173
+
174
+ Empty when *rules* is empty; otherwise a two-newline-separated
175
+ section header followed by a bullet list of rules.
176
+ """
177
+ if not rules:
178
+ return ""
179
+ body = "\n".join(f"- {r}" for r in rules)
180
+ return f"\n\n## Custom Rules\n\n{body}"
181
+
182
+
183
+ def build_yaml_managed_config(profile: dict[str, Any], header: str) -> str:
184
+ """Render the canonical ``apothem_managed`` YAML config body.
185
+
186
+ Retained for compatibility with earlier generic materializers. The body
187
+ carries the apothem-managed sentinel, the full identity + preferences
188
+ sub-dicts (with their per-key defaults), the seriousness band, and an
189
+ optional rules list.
190
+ """
191
+ normalized = coerce_profile(profile).to_dict()
192
+ identity = normalized["identity"]
193
+ preferences = normalized["preferences"]
194
+ rules: list[str] = normalized["rules"]
195
+
196
+ config: dict[str, Any] = {
197
+ "apothem_managed": True,
198
+ "identity": {
199
+ "name": identity.get("name", ""),
200
+ "role": identity.get("role", ""),
201
+ "email": identity.get("email", ""),
202
+ },
203
+ "preferences": {
204
+ "language": preferences.get("language", DEFAULT_LANGUAGE),
205
+ "style": preferences.get("style", DEFAULT_STYLE),
206
+ },
207
+ "apothem_support": "apothem",
208
+ "seriousness": normalized["seriousness"],
209
+ }
210
+ if rules:
211
+ config["rules"] = rules
212
+
213
+ return header + yaml.safe_dump(config, default_flow_style=False, allow_unicode=True)
@@ -0,0 +1,59 @@
1
+ # SPDX-License-Identifier: MIT
2
+
3
+ """Apothem harness adapter protocol — the foundation contract.
4
+
5
+ Defines :class:`HarnessAdapter`, the structural contract every concrete
6
+ adapter must satisfy. It lives in the foundation ``lib`` layer so the harness
7
+ registry can reference the contract without the foundation importing its
8
+ consumers; :mod:`apothem.harnesses` re-exports it as the public surface
9
+ (importable as ``apothem.harnesses.HarnessAdapter``), and the adapters that
10
+ satisfy it are declared in :mod:`apothem.lib.harness_registry`.
11
+ """
12
+
13
+ from __future__ import annotations
14
+
15
+ from pathlib import Path
16
+ from typing import Any, Protocol, runtime_checkable
17
+
18
+
19
+ @runtime_checkable
20
+ class HarnessAdapter(Protocol):
21
+ """Protocol every apothem harness adapter must satisfy.
22
+
23
+ Adapters translate a shared :class:`dict` profile into a
24
+ harness-native configuration on disk. The lifecycle contract is small:
25
+ an adapter owns one primary ``output_path`` anchor, implements the
26
+ install/update/uninstall/verify methods, and keeps the richer identity,
27
+ target, docs, capability, and package-data obligations in the central
28
+ registry.
29
+ """
30
+
31
+ @property
32
+ def name(self) -> str:
33
+ """Canonical kebab-case harness identifier (e.g. ``'claude-code'``)."""
34
+ ...
35
+
36
+ @property
37
+ def output_path(self) -> Path:
38
+ """Absolute path to the target configuration file."""
39
+ ...
40
+
41
+ def install(self, profile: dict[str, Any]) -> object:
42
+ """Materialize the harness configuration from the shared profile dict."""
43
+ ...
44
+
45
+ def update(self, profile: dict[str, Any]) -> object:
46
+ """Re-materialize the harness configuration from the updated profile."""
47
+ ...
48
+
49
+ def uninstall(self) -> None:
50
+ """Remove the harness configuration file if present."""
51
+ ...
52
+
53
+ def is_installed(self) -> bool:
54
+ """Return ``True`` if the harness configuration file exists on disk."""
55
+ ...
56
+
57
+ def verify(self) -> bool:
58
+ """Return ``True`` if the installed harness configuration is valid."""
59
+ ...
@@ -0,0 +1,282 @@
1
+ # SPDX-License-Identifier: MIT
2
+
3
+ """Central registry for supported Apothem harness adapters.
4
+
5
+ The declarative ``HARNESS_REGISTRY`` data table lives in the sibling
6
+ ``harness_registry_data`` module; this module imports it and carries the
7
+ resolution / discovery logic plus the derived lookup indexes. It re-exports the
8
+ full public surface (data + types + functions) so
9
+ ``from apothem.lib.harness_registry import X`` is unchanged for every consumer.
10
+ This module is authoritative at runtime.
11
+ """
12
+
13
+ from __future__ import annotations
14
+
15
+ import importlib
16
+ import importlib.util
17
+ import sys
18
+ from collections.abc import Mapping
19
+ from pathlib import Path
20
+ from types import ModuleType
21
+
22
+ from apothem.lib.harness_protocol import HarnessAdapter
23
+ from apothem.lib.harness_registry_data import (
24
+ HARNESS_REGISTRY as HARNESS_REGISTRY,
25
+ )
26
+ from apothem.lib.harness_registry_data import (
27
+ REQUIRED_CAPABILITIES as REQUIRED_CAPABILITIES,
28
+ )
29
+ from apothem.lib.harness_registry_data import (
30
+ SUPPORTED_HARNESS_COUNT as SUPPORTED_HARNESS_COUNT,
31
+ )
32
+ from apothem.lib.harness_registry_data import (
33
+ CapabilityStatus as CapabilityStatus,
34
+ )
35
+ from apothem.lib.harness_registry_data import (
36
+ HarnessRegistryEntry as HarnessRegistryEntry,
37
+ )
38
+ from apothem.lib.harness_registry_data import (
39
+ HarnessScope as HarnessScope,
40
+ )
41
+
42
+ SUPPORTED_HARNESS_IDS: tuple[str, ...] = tuple(
43
+ entry.public_id for entry in HARNESS_REGISTRY
44
+ )
45
+ SUPPORTED_PACKAGE_KEYS: tuple[str, ...] = tuple(
46
+ entry.package_key for entry in HARNESS_REGISTRY
47
+ )
48
+
49
+ _BY_PUBLIC_ID: Mapping[str, HarnessRegistryEntry] = {
50
+ entry.public_id: entry for entry in HARNESS_REGISTRY
51
+ }
52
+ _BY_PACKAGE_KEY: Mapping[str, HarnessRegistryEntry] = {
53
+ entry.package_key: entry for entry in HARNESS_REGISTRY
54
+ }
55
+
56
+ if len(HARNESS_REGISTRY) != SUPPORTED_HARNESS_COUNT: # pragma: no cover
57
+ raise RuntimeError(
58
+ "the supported harness registry must contain exactly "
59
+ f"{SUPPORTED_HARNESS_COUNT} entries"
60
+ )
61
+ if len(set(SUPPORTED_HARNESS_IDS)) != SUPPORTED_HARNESS_COUNT: # pragma: no cover
62
+ raise RuntimeError("duplicate public harness ids in registry")
63
+ if len(set(SUPPORTED_PACKAGE_KEYS)) != SUPPORTED_HARNESS_COUNT: # pragma: no cover
64
+ raise RuntimeError("duplicate harness package keys in registry")
65
+
66
+
67
+ def normalize_harness_reference(value: object) -> str:
68
+ """Normalize a public id or package key reference for registry lookup."""
69
+ return str(value).strip().lower()
70
+
71
+
72
+ def iter_harness_entries() -> tuple[HarnessRegistryEntry, ...]:
73
+ """Return registry entries in deterministic public-id order."""
74
+ return HARNESS_REGISTRY
75
+
76
+
77
+ def get_harness_entry(value: object) -> HarnessRegistryEntry:
78
+ """Return the registry entry addressed by public id or package key."""
79
+ key = normalize_harness_reference(value)
80
+ if key in _BY_PUBLIC_ID:
81
+ return _BY_PUBLIC_ID[key]
82
+ if key in _BY_PACKAGE_KEY:
83
+ return _BY_PACKAGE_KEY[key]
84
+ raise KeyError(key)
85
+
86
+
87
+ def public_id_for_package_key(package_key: object) -> str:
88
+ """Return the public harness id for a Python package key."""
89
+ return get_harness_entry(package_key).public_id
90
+
91
+
92
+ def package_key_for_public_id(public_id: object) -> str:
93
+ """Return the Python package key for a public harness id."""
94
+ return get_harness_entry(public_id).package_key
95
+
96
+
97
+ def load_adapter_class(entry: HarnessRegistryEntry) -> type[object]:
98
+ """Import and return the adapter class declared by a registry entry."""
99
+ module = importlib.import_module(entry.adapter_module)
100
+ adapter_class = getattr(module, entry.adapter_class_name)
101
+ if not isinstance(adapter_class, type):
102
+ raise TypeError(f"{entry.entry_point} did not resolve to a class")
103
+ return adapter_class
104
+
105
+
106
+ class AdapterDiscoveryError(RuntimeError):
107
+ """Raised when a harness sub-package fails convention-based adapter resolution.
108
+
109
+ The error message names the offending directory so a silently-dropped
110
+ adapter never becomes an invisible coverage regression.
111
+ """
112
+
113
+
114
+ # Directory names under ``harnesses/`` that hold shared helpers rather than a
115
+ # concrete adapter sub-package. They are skipped during discovery alongside
116
+ # private (underscore / dot prefixed) and cache directories.
117
+ _DISCOVERY_HELPER_DIRS: frozenset[str] = frozenset({"_shared", "templates"})
118
+
119
+
120
+ def _adapter_canonical_name(package_key: str) -> str:
121
+ """Return the canonical kebab-case name for a harness package directory.
122
+
123
+ Reuses the static registry's ``package_key`` -> ``public_id`` mapping for
124
+ fidelity (e.g. ``claude_code`` -> ``claude-code``). Falls back to a direct
125
+ underscore-to-hyphen transform when the directory is not yet registered,
126
+ so a newly-added adapter is still discoverable before the static registry
127
+ is updated.
128
+ """
129
+ entry = _BY_PACKAGE_KEY.get(package_key)
130
+ if entry is not None:
131
+ return entry.public_id
132
+ return package_key.replace("_", "-")
133
+
134
+
135
+ def _resolve_adapter_class(package_key: str, module: object) -> type[object]:
136
+ """Find the single ``*Adapter`` class exposed by an imported harness module.
137
+
138
+ The class name is resolved by convention rather than computed from the
139
+ directory name: the snake-to-Pascal transform is not reliable (for
140
+ instance ``github_copilot`` exposes ``GitHubCopilotAdapter`` with an
141
+ internal capital ``H``). Instead every attribute whose name ends with
142
+ ``Adapter`` and is a class is collected; exactly one must be present.
143
+ """
144
+ candidates: list[type[object]] = []
145
+ for attr_name in dir(module):
146
+ if not attr_name.endswith("Adapter"):
147
+ continue
148
+ attr = getattr(module, attr_name)
149
+ if isinstance(attr, type) and attr is not HarnessAdapter:
150
+ candidates.append(attr)
151
+
152
+ # De-duplicate while preserving identity (an adapter may be re-exported
153
+ # under more than one attribute name pointing at the same class object).
154
+ unique = list(dict.fromkeys(candidates))
155
+ if not unique:
156
+ raise AdapterDiscoveryError(
157
+ f"harnesses/{package_key}: no '*Adapter' class found in the package module"
158
+ )
159
+ if len(unique) > 1:
160
+ names = ", ".join(sorted(cls.__name__ for cls in unique))
161
+ raise AdapterDiscoveryError(
162
+ f"harnesses/{package_key}: expected exactly one '*Adapter' class, "
163
+ f"found {len(unique)} ({names})"
164
+ )
165
+ return unique[0]
166
+
167
+
168
+ def _is_installed_package_root(root: Path) -> bool:
169
+ """Return ``True`` when ``root`` is the installed ``apothem`` package dir."""
170
+ import apothem
171
+
172
+ apothem_file = getattr(apothem, "__file__", None)
173
+ if apothem_file is None: # pragma: no cover - namespace package edge
174
+ return False
175
+ return Path(apothem_file).resolve().parent == root.resolve()
176
+
177
+
178
+ def _import_harness_module(root: Path, name: str, package_dir: Path) -> ModuleType:
179
+ """Import the harness sub-package ``name`` found under ``root / harnesses``.
180
+
181
+ When ``root`` is the installed ``apothem`` package directory the robust
182
+ namespace import (``apothem.harnesses.<name>``) is used — this honors
183
+ editable installs and namespace-package layouts. Otherwise the module is
184
+ loaded directly from its ``__init__.py`` on disk, so an arbitrary ``root``
185
+ (for instance a test fixture tree) is enumerated against the actual files
186
+ it contains rather than the installed package.
187
+ """
188
+ if _is_installed_package_root(root):
189
+ return importlib.import_module(f"apothem.harnesses.{name}")
190
+
191
+ fq_name = f"_apothem_discovered_harness_{name}"
192
+ spec = importlib.util.spec_from_file_location(fq_name, package_dir / "__init__.py")
193
+ if spec is None or spec.loader is None: # pragma: no cover - defensive
194
+ raise AdapterDiscoveryError(
195
+ f"harnesses/{name}: package could not be loaded from {package_dir}"
196
+ )
197
+ module = importlib.util.module_from_spec(spec)
198
+ sys.modules[fq_name] = module
199
+ spec.loader.exec_module(module)
200
+ return module
201
+
202
+
203
+ def discover_adapters(root: Path) -> dict[str, type[object]]:
204
+ """Discover harness adapters by filesystem convention under ``root``.
205
+
206
+ Conformance utility, NOT the runtime resolver. The authoritative
207
+ adapter source at runtime is the static :data:`HARNESS_REGISTRY`; this
208
+ discovery exists so a parity test can assert the on-disk adapter set
209
+ equals the registry, catching a convention-correct but unregistered
210
+ adapter as a coverage regression. No CLI / runtime path calls this.
211
+
212
+ Scans the immediate subdirectories of ``root / "harnesses"``, importing
213
+ each package and resolving its ``*Adapter`` class by convention. The
214
+ returned mapping is keyed by the adapter's canonical kebab-case name and
215
+ ordered deterministically (sorted by key).
216
+
217
+ Args:
218
+ root: The ``apothem`` package directory whose ``harnesses/``
219
+ subdirectory holds the per-harness adapter sub-packages.
220
+
221
+ Returns:
222
+ A ``dict`` mapping canonical harness name (e.g. ``'claude-code'``)
223
+ to the resolved adapter class, in sorted key order.
224
+
225
+ Raises:
226
+ AdapterDiscoveryError: When a candidate sub-package exposes zero or
227
+ more than one ``*Adapter`` class. The message names the directory.
228
+ """
229
+ harnesses_dir = root / "harnesses"
230
+ discovered: dict[str, type[object]] = {}
231
+ for child in sorted(harnesses_dir.iterdir()):
232
+ if not child.is_dir():
233
+ continue
234
+ name = child.name
235
+ if name.startswith(("_", ".")) or name in _DISCOVERY_HELPER_DIRS:
236
+ continue
237
+ if not (child / "__init__.py").is_file():
238
+ continue
239
+ module = _import_harness_module(root, name, child)
240
+ adapter_class = _resolve_adapter_class(name, module)
241
+ discovered[_adapter_canonical_name(name)] = adapter_class
242
+ return dict(sorted(discovered.items()))
243
+
244
+
245
+ def discovered_adapter_map() -> dict[str, type[object]]:
246
+ """Discover adapters under the installed ``apothem`` package directory.
247
+
248
+ Convenience wrapper over :func:`discover_adapters` that defaults ``root``
249
+ to the directory of the installed ``apothem`` package, so callers need
250
+ not compute it themselves. Like :func:`discover_adapters`, this is a
251
+ conformance/parity utility (registry-vs-filesystem agreement), not the
252
+ runtime adapter resolver — the static :data:`HARNESS_REGISTRY` is
253
+ authoritative at runtime.
254
+
255
+ Returns:
256
+ A ``dict`` mapping canonical harness name to adapter class, sorted.
257
+ """
258
+ import apothem
259
+
260
+ package_root = Path(apothem.__file__).resolve().parent
261
+ return discover_adapters(package_root)
262
+
263
+
264
+ __all__ = [
265
+ "HARNESS_REGISTRY",
266
+ "REQUIRED_CAPABILITIES",
267
+ "SUPPORTED_HARNESS_COUNT",
268
+ "SUPPORTED_HARNESS_IDS",
269
+ "SUPPORTED_PACKAGE_KEYS",
270
+ "AdapterDiscoveryError",
271
+ "CapabilityStatus",
272
+ "HarnessRegistryEntry",
273
+ "HarnessScope",
274
+ "discover_adapters",
275
+ "discovered_adapter_map",
276
+ "get_harness_entry",
277
+ "iter_harness_entries",
278
+ "load_adapter_class",
279
+ "normalize_harness_reference",
280
+ "package_key_for_public_id",
281
+ "public_id_for_package_key",
282
+ ]