@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,857 @@
1
+ # SPDX-License-Identifier: MIT
2
+
3
+ """Shared helpers, constants, and the ``AliasedGroup`` for the apothem CLI.
4
+
5
+ Carries the cross-command building blocks the per-command modules consume:
6
+ the structured CLI-error type, the adapter protocol + load helpers, the
7
+ profile read/write helpers, the lifecycle-envelope builders, harness
8
+ selection, project-root resolution, and the drift/plan helpers. Extracted
9
+ verbatim from the former monolithic ``cli/__init__.py``; the patchable-helper
10
+ call sites inside selection/load helpers resolve through the ``apothem.cli``
11
+ package (``_pkg``) so the test patch seams keep landing."""
12
+
13
+ from __future__ import annotations
14
+
15
+ import inspect
16
+ import sys
17
+ from collections.abc import Callable
18
+ from pathlib import Path
19
+ from typing import Any, Protocol, cast
20
+
21
+ import click
22
+ from click.shell_completion import CompletionItem
23
+ from rich.console import Console
24
+
25
+ import apothem.cli as _pkg
26
+ from apothem.cli._json_formatter import emit_json
27
+ from apothem.harnesses._shared import install_driver
28
+ from apothem.harnesses._shared.install_driver import (
29
+ MaterializationError,
30
+ MaterializationResult,
31
+ MaterializationRun,
32
+ check_fidelity,
33
+ fidelity_is_faithful,
34
+ write_bytes_safely,
35
+ )
36
+ from apothem.lib.harness_registry import (
37
+ SUPPORTED_HARNESS_IDS,
38
+ HarnessRegistryEntry,
39
+ iter_harness_entries,
40
+ load_adapter_class,
41
+ )
42
+ from apothem.lib.profile import (
43
+ ProfileValidationError,
44
+ load_profile_file,
45
+ resolve_profile_path,
46
+ )
47
+ from apothem.schemas import profile_minimal_path
48
+
49
+ console = Console(highlight=False)
50
+
51
+ #: Shared Click context settings (``-h`` / ``--help`` aliases) for ``main`` and
52
+ #: the ``profile`` / ``harnesses`` sub-groups.
53
+ _CONTEXT = {"help_option_names": ["-h", "--help"]}
54
+
55
+
56
+ _ADAPTER_LOAD_ERRORS = (ImportError, AttributeError, TypeError, OSError)
57
+
58
+
59
+ _EXIT_EXPECTED = 1
60
+
61
+
62
+ _EXIT_PARTIAL = 2
63
+
64
+
65
+ class _CliUserError(Exception):
66
+ """Expected operator-facing CLI failure."""
67
+
68
+ def __init__(
69
+ self,
70
+ *,
71
+ code: str,
72
+ message: str,
73
+ field: str,
74
+ reason: str,
75
+ fix: str,
76
+ safe_value: object | None = None,
77
+ files_written: tuple[str, ...] = (),
78
+ ) -> None:
79
+ super().__init__(message)
80
+ self.code = code
81
+ self.message = message
82
+ self.field = field
83
+ self.reason = reason
84
+ self.fix = fix
85
+ self.safe_value = safe_value
86
+ self.files_written = files_written
87
+
88
+ def to_dict(self) -> dict[str, object]:
89
+ """Return the machine-readable error object."""
90
+ return {
91
+ "code": self.code,
92
+ "message": self.message,
93
+ "field": self.field,
94
+ "reason": self.reason,
95
+ "fix": self.fix,
96
+ "safe_value": self.safe_value,
97
+ "files_written": list(self.files_written),
98
+ }
99
+
100
+
101
+ class _Adapter(Protocol):
102
+ @property
103
+ def name(self) -> str: ...
104
+ @property
105
+ def output_path(self) -> Path: ...
106
+ def install(self, profile: dict[str, Any]) -> object: ...
107
+ def update(self, profile: dict[str, Any]) -> object: ...
108
+ def uninstall(self) -> None: ...
109
+ def is_installed(self) -> bool: ...
110
+ def verify(self) -> bool: ...
111
+
112
+ # Optional project-scope extension (opt-in per adapter). Adapters
113
+ # that materialize into a project root rather than a user-scope
114
+ # configuration root declare ``requires_project = True`` and accept
115
+ # an optional ``project: Path | None`` keyword argument on their
116
+ # lifecycle methods. The CLI threads the operator-supplied
117
+ # ``--project <path>`` value through ``_materialize`` and routes it
118
+ # to the adapter only when the adapter's signature accepts it (via
119
+ # ``_invoke_with_project`` introspection).
120
+ requires_project: bool
121
+
122
+
123
+ def _adapter_requires_project(adapter: _Adapter) -> bool:
124
+ """Return True iff *adapter* opts into the project-scope contract.
125
+
126
+ The opt-in is the literal boolean ``True`` on the ``requires_project``
127
+ attribute. Truthy non-boolean values (e.g., ``MagicMock`` instances
128
+ in unit-test fixtures, sentinel objects from incomplete adapter
129
+ stubs) do not satisfy the opt-in — explicit ratification is required.
130
+ """
131
+ return getattr(adapter, "requires_project", False) is True
132
+
133
+
134
+ def _adapter_resolve_output_path(adapter: _Adapter, project: Path | None) -> Path:
135
+ """Resolve the adapter's output path, threading *project* when supported.
136
+
137
+ Project-scope adapters expose a ``resolve_output_path(project)``
138
+ method whose return is the concrete on-disk target under the
139
+ operator-supplied project root. Adapters without that method fall
140
+ back to the static ``output_path`` property — appropriate for
141
+ user-scope adapters whose target is determined by the harness's
142
+ home-rooted configuration directory.
143
+ """
144
+ resolve_fn = getattr(adapter, "resolve_output_path", None)
145
+ if callable(resolve_fn):
146
+ return cast(Path, resolve_fn(project))
147
+ return adapter.output_path
148
+
149
+
150
+ def _stdin_is_interactive() -> bool:
151
+ """Whether an interactive confirmation may be prompted (a TTY is attached).
152
+
153
+ A dedicated seam (rather than an inline ``sys.stdin.isatty()``) so the
154
+ blast-radius confirmation gate is exercisable under the test runner, where
155
+ stdin is a replayed stream rather than a terminal.
156
+ """
157
+ return sys.stdin.isatty()
158
+
159
+
160
+ def _path_is_within(target: Path, root: Path) -> bool:
161
+ """Return True iff *target* resolves inside *root* (lexical containment)."""
162
+ try:
163
+ target.resolve(strict=False).relative_to(root.resolve(strict=False))
164
+ except ValueError:
165
+ return False
166
+ return True
167
+
168
+
169
+ def _partition_blast_radius(
170
+ adapters: list[tuple[str, _Adapter]],
171
+ project_root: Path | None,
172
+ ) -> tuple[list[tuple[str, Path]], list[tuple[str, Path]]]:
173
+ """Split adapter targets into (under-project, outside-project/home-rooted).
174
+
175
+ Project-scope adapters materialize under the supplied ``--project`` root;
176
+ user-scope adapters materialize under the operator's real home directory,
177
+ i.e. outside that root. The partition is by concrete on-disk containment so
178
+ the disclosure stays truthful even if a home target happens to fall inside
179
+ an unusual project root.
180
+ """
181
+ within: list[tuple[str, Path]] = []
182
+ outside: list[tuple[str, Path]] = []
183
+ for harness_id, adapter in adapters:
184
+ target = _adapter_resolve_output_path(adapter, project_root)
185
+ if project_root is not None and _path_is_within(target, project_root):
186
+ within.append((harness_id, target))
187
+ else:
188
+ outside.append((harness_id, target))
189
+ return within, outside
190
+
191
+
192
+ def _render_blast_radius(
193
+ con: Console,
194
+ within: list[tuple[str, Path]],
195
+ outside: list[tuple[str, Path]],
196
+ project_root: Path | None,
197
+ ) -> None:
198
+ """Print the pre-write disclosure: targets grouped by project vs home root."""
199
+ total = len(within) + len(outside)
200
+ con.print(f"[bold]Apothem will write {total} configuration file(s):[/]")
201
+ width = max((len(hid) for hid, _ in [*within, *outside]), default=0)
202
+ if within:
203
+ con.print(f" [cyan]Under the project root[/] ({project_root}):")
204
+ for harness_id, target in within:
205
+ con.print(f" {harness_id.ljust(width)} -> {target}")
206
+ if outside:
207
+ con.print(" [yellow]Under your home directory[/] (outside --project):")
208
+ for harness_id, target in outside:
209
+ con.print(f" {harness_id.ljust(width)} -> {target}")
210
+
211
+
212
+ _PLACEHOLDER_IDENTITY: dict[str, str] = {
213
+ "name": "Example User",
214
+ "email": "dev@example.invalid",
215
+ "website": "https://example.invalid",
216
+ "github": "example-user",
217
+ }
218
+
219
+
220
+ def _placeholder_identity_fields(profile: dict[str, Any]) -> list[str]:
221
+ """Return the identity fields still set to their shipped placeholder value."""
222
+ identity = profile.get("identity")
223
+ if not isinstance(identity, dict):
224
+ return []
225
+ return [
226
+ field
227
+ for field, placeholder in _PLACEHOLDER_IDENTITY.items()
228
+ if str(identity.get(field, "")).strip() == placeholder
229
+ ]
230
+
231
+
232
+ def _placeholder_advisory_entry(
233
+ profile_path: Path, fields: list[str]
234
+ ) -> dict[str, object]:
235
+ """Build the lifecycle-envelope advisory for an unpersonalized identity."""
236
+ return {
237
+ "harness": None,
238
+ "outcome": "advisory",
239
+ "operation": "placeholder_identity",
240
+ "path": str(profile_path),
241
+ "fields": list(fields),
242
+ "message": (
243
+ "Profile identity is still the scaffold placeholder ("
244
+ + ", ".join(fields)
245
+ + "); personalize it so a real identity is projected. Edit the "
246
+ + "profile or run 'apothem profile set identity.name \"Your Name\"'."
247
+ ),
248
+ }
249
+
250
+
251
+ def _invoke_with_project(
252
+ fn: Callable[..., object], *args: object, project: Path | None
253
+ ) -> object:
254
+ """Invoke *fn* passing ``project=`` only when its signature accepts it.
255
+
256
+ Backward-compatible bridge: existing user-scope adapters declare
257
+ ``install(self, profile)`` / ``uninstall(self)`` and do not accept
258
+ a ``project`` parameter. Project-scope adapters declare
259
+ ``install(self, profile, project=None)`` and similar. Introspection
260
+ via ``inspect.signature`` lets the CLI thread the operator-supplied
261
+ project root to the adapters that consume it without breaking the
262
+ older signatures.
263
+ """
264
+ try:
265
+ sig = inspect.signature(fn)
266
+ except (TypeError, ValueError):
267
+ return fn(*args)
268
+ if "project" in sig.parameters:
269
+ return fn(*args, project=project)
270
+ return fn(*args)
271
+
272
+
273
+ def _load_adapter(harness: str) -> _Adapter:
274
+ """Load a harness adapter by name from the central registry."""
275
+ try:
276
+ entry = _pkg.get_harness_entry(harness)
277
+ except KeyError as exc:
278
+ available = ", ".join(SUPPORTED_HARNESS_IDS)
279
+ raise click.ClickException(
280
+ f"Unknown harness {harness!r}. Available harnesses: {available}."
281
+ ) from exc
282
+ try:
283
+ return cast(_Adapter, load_adapter_class(entry)())
284
+ except _ADAPTER_LOAD_ERRORS as exc:
285
+ raise click.ClickException(
286
+ f"Failed to load harness {entry.public_id!r}: {exc}"
287
+ ) from exc
288
+
289
+
290
+ def _all_adapters() -> list[_Adapter]:
291
+ """Return instances of all registered harness adapters.
292
+
293
+ A broken adapter is surfaced as a yellow warning and skipped rather
294
+ than silently dropped — the sweep still returns the healthy adapters
295
+ but the operator sees which ones failed to load.
296
+ """
297
+ adapters: list[_Adapter] = []
298
+ for entry in iter_harness_entries():
299
+ try:
300
+ adapters.append(cast(_Adapter, load_adapter_class(entry)()))
301
+ except _ADAPTER_LOAD_ERRORS as exc:
302
+ console.print(
303
+ f"[yellow]⚠[/] Failed to load adapter {entry.public_id!r}: {exc}",
304
+ style="yellow",
305
+ )
306
+ return adapters
307
+
308
+
309
+ def _resolve_profile_path(profile: str | None) -> Path:
310
+ """Return the shared-profile path."""
311
+ return resolve_profile_path(profile)
312
+
313
+
314
+ def _complete_harness(
315
+ ctx: click.Context, param: click.Parameter, incomplete: str
316
+ ) -> list[CompletionItem]:
317
+ """Shell-completion callback for the ``--harness`` option.
318
+
319
+ Offers the fifteen registered public harness ids plus the batch token
320
+ ``all``, filtered to those beginning with the operator's partial input. The
321
+ candidate set is the static registry's public-id form (e.g. ``claude-code``),
322
+ so completion never drifts from the harnesses ``install``/``verify`` accept.
323
+ """
324
+ candidates = [*SUPPORTED_HARNESS_IDS, "all"]
325
+ return [
326
+ CompletionItem(candidate)
327
+ for candidate in candidates
328
+ if candidate.startswith(incomplete)
329
+ ]
330
+
331
+
332
+ def _load_profile(profile_path: Path) -> dict[str, Any]:
333
+ """Load, validate, and normalize the shared profile YAML."""
334
+ return load_profile_file(profile_path).to_dict()
335
+
336
+
337
+ def _write_profile_text_safely(
338
+ profile_path: Path,
339
+ text: str,
340
+ *,
341
+ operation: str,
342
+ ) -> MaterializationResult:
343
+ """Write a profile document through the shared atomic write boundary."""
344
+ result = write_bytes_safely(
345
+ profile_path,
346
+ text.encode("utf-8"),
347
+ install_root=profile_path.parent,
348
+ harness_name="profile",
349
+ operation=operation,
350
+ )
351
+ if result.outcome == "error":
352
+ raise _CliUserError(
353
+ code="profile.write_failed",
354
+ message="Apothem profile write failed.",
355
+ field="profile",
356
+ reason=result.message,
357
+ fix=(
358
+ "Choose a profile path inside a normal directory and check "
359
+ "file permissions."
360
+ ),
361
+ )
362
+ return result
363
+
364
+
365
+ def _profile_error(exc: ProfileValidationError) -> dict[str, object]:
366
+ """Return the stable CLI error object for profile validation failures."""
367
+ return exc.diagnostic.to_dict()
368
+
369
+
370
+ def _parse_set_value(raw: str) -> object:
371
+ """Parse a ``profile set`` VALUE without YAML scalar-coercion footguns.
372
+
373
+ Explicit structured input (a value whose first non-space char is ``[`` or
374
+ ``{``) is parsed as YAML so list/map nodes can be set. Explicit booleans
375
+ (``true``/``false``, case-insensitive) coerce to bool for the enforcement
376
+ flags. Every other scalar is preserved as a literal string — no
377
+ Norway-problem coercion of ``no``/``yes``/``on``/``off``, no date or number
378
+ coercion (``2024-01-01``, ``1.0`` stay strings). Schema validation, not
379
+ guesswork, decides whether the literal is acceptable.
380
+ """
381
+ import yaml
382
+
383
+ stripped = raw.strip()
384
+ if stripped[:1] in {"[", "{"}:
385
+ try:
386
+ return yaml.safe_load(raw)
387
+ except yaml.YAMLError:
388
+ return raw
389
+ if stripped.lower() in {"true", "false"}:
390
+ return stripped.lower() == "true"
391
+ return raw
392
+
393
+
394
+ def _set_nested(data: dict[str, Any], dotted_key: str, value: object) -> dict[str, Any]:
395
+ """Assign *value* at the dotted path *dotted_key* within *data*.
396
+
397
+ Walks the addressed path, creating intermediate maps only along it, and
398
+ sets the leaf. A single key with no dot keeps whole-node behavior. Sibling
399
+ keys and unaddressed structure are preserved. Mutates and returns *data*.
400
+ """
401
+ parts = dotted_key.split(".")
402
+ node = data
403
+ for part in parts[:-1]:
404
+ existing = node.get(part)
405
+ if not isinstance(existing, dict):
406
+ existing = {}
407
+ node[part] = existing
408
+ node = existing
409
+ node[parts[-1]] = value
410
+ return data
411
+
412
+
413
+ def _format_error_plain(
414
+ error: dict[str, object], *, profile_path: Path | None = None
415
+ ) -> str:
416
+ """Format an expected error with the diagnostic fields operators need."""
417
+ files = error.get("files_written", [])
418
+ if isinstance(files, list) and files:
419
+ files_written = ", ".join(str(item) for item in files)
420
+ else:
421
+ files_written = "none"
422
+ lines = [str(error.get("message", "Apothem command failed."))]
423
+ if profile_path is not None:
424
+ lines.append(f"Profile: {profile_path}")
425
+ lines.extend(
426
+ [
427
+ f"Field: {error.get('field', 'command')}",
428
+ f"Reason: {error.get('reason', 'unknown failure')}",
429
+ f"Fix: {error.get('fix', 'Review the command input and retry.')}",
430
+ f"Files written: {files_written}.",
431
+ ]
432
+ )
433
+ safe_value = error.get("safe_value")
434
+ if safe_value is not None:
435
+ lines.insert(-1, f"Safe value: {safe_value!r}")
436
+ return "\n".join(lines)
437
+
438
+
439
+ def _error_envelope(
440
+ *,
441
+ command: str,
442
+ harness: str | None = None,
443
+ profile_path: Path | None = None,
444
+ project_root: Path | None = None,
445
+ error: dict[str, object],
446
+ files_written: list[str] | None = None,
447
+ results: list[dict[str, object]] | None = None,
448
+ warnings: list[dict[str, object]] | None = None,
449
+ status: str = "error",
450
+ ) -> dict[str, object]:
451
+ """Build the stable machine-readable lifecycle envelope."""
452
+ return {
453
+ "status": status,
454
+ "command": command,
455
+ "harness": harness,
456
+ "profile_path": str(profile_path) if profile_path is not None else None,
457
+ "project": str(project_root) if project_root is not None else None,
458
+ "files_written": files_written or [],
459
+ "results": results or [],
460
+ "warnings": warnings or [],
461
+ "error": error,
462
+ }
463
+
464
+
465
+ def _emit_expected_error(
466
+ *,
467
+ command: str,
468
+ fmt: str,
469
+ error: dict[str, object],
470
+ harness: str | None = None,
471
+ profile_path: Path | None = None,
472
+ project_root: Path | None = None,
473
+ files_written: list[str] | None = None,
474
+ results: list[dict[str, object]] | None = None,
475
+ warnings: list[dict[str, object]] | None = None,
476
+ exit_code: int = _EXIT_EXPECTED,
477
+ ) -> None:
478
+ """Emit an expected failure as JSON or a Click-formatted plain error."""
479
+ status = "partial" if exit_code == _EXIT_PARTIAL else "error"
480
+ if fmt == "json":
481
+ emit_json(
482
+ _error_envelope(
483
+ command=command,
484
+ harness=harness,
485
+ profile_path=profile_path,
486
+ project_root=project_root,
487
+ error=error,
488
+ files_written=files_written,
489
+ results=results,
490
+ warnings=warnings,
491
+ status=status,
492
+ )
493
+ )
494
+ sys.exit(exit_code)
495
+ message = _format_error_plain(error, profile_path=profile_path)
496
+ if exit_code == _EXIT_EXPECTED:
497
+ raise click.ClickException(message)
498
+ # click.ClickException hard-codes exit 1; emit the same `Error:` prefix
499
+ # by hand and preserve a non-default (partial) exit code in plain mode,
500
+ # for parity with the JSON branch above — a partial outcome exits 2 in
501
+ # both formats, never 2 under --json and 1 in plain.
502
+ click.echo(f"Error: {message}", err=True)
503
+ sys.exit(exit_code)
504
+
505
+
506
+ def _lifecycle_envelope(
507
+ *,
508
+ status: str,
509
+ command: str,
510
+ action: str,
511
+ harness: str | None,
512
+ profile_path: Path | None,
513
+ project_root: Path | None,
514
+ files_written: list[str],
515
+ results: list[dict[str, object]],
516
+ warnings: list[dict[str, object]],
517
+ output_path: Path | None = None,
518
+ materialization: MaterializationRun | None = None,
519
+ ) -> dict[str, object]:
520
+ """Build a stable success or dry-run envelope."""
521
+ payload: dict[str, object] = {
522
+ "status": status,
523
+ "command": command,
524
+ "action": action,
525
+ "harness": harness,
526
+ "profile_path": str(profile_path) if profile_path is not None else None,
527
+ "project": str(project_root) if project_root is not None else None,
528
+ "files_written": files_written,
529
+ "results": results,
530
+ "warnings": warnings,
531
+ "error": None,
532
+ }
533
+ if output_path is not None:
534
+ payload["output_path"] = str(output_path)
535
+ if materialization is not None:
536
+ payload["materialization"] = materialization.to_dict()
537
+ return payload
538
+
539
+
540
+ def _result_dicts(
541
+ harness: str, materialization: MaterializationRun | None
542
+ ) -> list[dict[str, object]]:
543
+ """Return materialization result dictionaries tagged by public harness id."""
544
+ if materialization is None:
545
+ return []
546
+ return [
547
+ {"harness": harness, **result.to_dict()} for result in materialization.results
548
+ ]
549
+
550
+
551
+ def _warning_dicts(
552
+ harness: str, materialization: MaterializationRun | None
553
+ ) -> list[dict[str, object]]:
554
+ """Return warning result dictionaries tagged by public harness id."""
555
+ if materialization is None:
556
+ return []
557
+ return [
558
+ {"harness": harness, **warning.to_dict()}
559
+ for warning in materialization.warnings
560
+ ]
561
+
562
+
563
+ def _adapter_failure_result(
564
+ harness_id: str,
565
+ operation: str,
566
+ output_path: Path | None,
567
+ exc: Exception,
568
+ ) -> dict[str, object]:
569
+ """Return a per-harness error result for an adapter that raised mid-batch.
570
+
571
+ Adapter lifecycle code is the untrusted edge: one adapter raising must degrade
572
+ that harness to a recorded error and let the batch continue, mirroring the
573
+ install/_materialize error model rather than aborting with a bare traceback.
574
+ """
575
+ return {
576
+ "harness": harness_id,
577
+ "outcome": "error",
578
+ "operation": operation,
579
+ "path": str(output_path) if output_path is not None else None,
580
+ "message": f"{type(exc).__name__}: {exc}",
581
+ }
582
+
583
+
584
+ def _load_adapter_for_entry(entry: HarnessRegistryEntry) -> _Adapter:
585
+ """Load an adapter for a registry entry with structured CLI errors."""
586
+ try:
587
+ return cast(_Adapter, load_adapter_class(entry)())
588
+ except _ADAPTER_LOAD_ERRORS as exc:
589
+ raise _CliUserError(
590
+ code="harness.load_failed",
591
+ message="Apothem adapter loading failed.",
592
+ field="harness",
593
+ reason=f"{entry.public_id}: {exc}",
594
+ fix="Reinstall Apothem or inspect the adapter package import.",
595
+ ) from exc
596
+
597
+
598
+ def _unknown_harness_error(harness: str) -> _CliUserError:
599
+ """Return a structured unknown-harness error."""
600
+ available = ", ".join(SUPPORTED_HARNESS_IDS)
601
+ return _CliUserError(
602
+ code="harness.unknown",
603
+ message="Apothem validation failed.",
604
+ field="harness",
605
+ reason=f"{harness!r} is not a supported harness.",
606
+ fix=f"Use one of: {available}, or use 'all' for batch lifecycle commands.",
607
+ )
608
+
609
+
610
+ def _selected_harness_ids(
611
+ harness: str, *, shared_profile: dict[str, Any] | None, apply_excludes: bool
612
+ ) -> list[str]:
613
+ """Resolve one harness or the deterministic registry-wide selection."""
614
+ requested = harness.strip().lower()
615
+ if requested == "all":
616
+ selected = list(SUPPORTED_HARNESS_IDS)
617
+ if apply_excludes and shared_profile is not None:
618
+ excluded = set(shared_profile.get("exclude_harnesses", []))
619
+ selected = [item for item in selected if item not in excluded]
620
+ if not selected:
621
+ raise _CliUserError(
622
+ code="harness.none_selected",
623
+ message="Apothem validation failed.",
624
+ field="harness",
625
+ reason="batch selection resolved to zero harnesses.",
626
+ fix="Remove at least one entry from exclude_harnesses or select a named harness.",
627
+ )
628
+ return selected
629
+ try:
630
+ return [_pkg.get_harness_entry(harness).public_id]
631
+ except KeyError as exc:
632
+ raise _unknown_harness_error(harness) from exc
633
+
634
+
635
+ def _require_project_for_selection(
636
+ harness_ids: list[str], project_root: Path | None
637
+ ) -> None:
638
+ """Reject selected project-scope harnesses when no project root exists."""
639
+ project_scoped = [
640
+ harness_id
641
+ for harness_id in harness_ids
642
+ if _pkg.get_harness_entry(harness_id).scope == "project"
643
+ ]
644
+ if project_scoped and project_root is None:
645
+ joined = ", ".join(project_scoped)
646
+ raise _CliUserError(
647
+ code="project.required",
648
+ message="Apothem validation failed.",
649
+ field="project",
650
+ reason=f"project-scope harnesses require --project: {joined}.",
651
+ fix="Pass --project PATH or select only user-scope harnesses.",
652
+ )
653
+
654
+
655
+ def _profile_scaffold_text() -> str:
656
+ """Return a schema-valid minimal profile scaffold."""
657
+ import yaml
658
+
659
+ data = yaml.safe_load(profile_minimal_path().read_text(encoding="utf-8"))
660
+ return yaml.safe_dump(data, sort_keys=False)
661
+
662
+
663
+ class AliasedGroup(click.Group):
664
+ """Click group that resolves subcommands case-insensitively."""
665
+
666
+ def get_command(self, ctx: click.Context, cmd_name: str) -> click.Command | None:
667
+ cmd = super().get_command(ctx, cmd_name)
668
+ if cmd is not None:
669
+ return cmd
670
+ lower = cmd_name.lower()
671
+ matches = [n for n in self.list_commands(ctx) if n.lower() == lower]
672
+ if len(matches) == 1:
673
+ return super().get_command(ctx, matches[0])
674
+ if len(matches) > 1:
675
+ ctx.fail(f"Ambiguous command {cmd_name!r}: matches {sorted(matches)}")
676
+ return None
677
+
678
+
679
+ def _resolve_project_root(project: str | None) -> Path | None:
680
+ """Resolve operator-supplied ``--project`` value to an absolute path."""
681
+ if project is None:
682
+ return None
683
+ project_root = Path(project).expanduser().resolve(strict=False)
684
+ if not project_root.exists():
685
+ raise _CliUserError(
686
+ code="project.not_found",
687
+ message="Apothem validation failed.",
688
+ field="project",
689
+ reason=f"project root does not exist: {project_root}",
690
+ fix="Create the project directory or pass an existing --project PATH.",
691
+ )
692
+ if not project_root.is_dir():
693
+ raise _CliUserError(
694
+ code="project.invalid_type",
695
+ message="Apothem validation failed.",
696
+ field="project",
697
+ reason=f"project root is not a directory: {project_root}",
698
+ fix="Pass a directory path as --project.",
699
+ )
700
+ return project_root
701
+
702
+
703
+ _project_option = click.option(
704
+ "--project",
705
+ default=None,
706
+ metavar="PATH",
707
+ help=(
708
+ "Project root for project-scope harnesses (e.g., cursor). Required "
709
+ "by adapters that materialize into a project tree rather than a "
710
+ "user-scope configuration directory."
711
+ ),
712
+ )
713
+
714
+ # Shared `--harness` decorator for the lifecycle commands whose option is
715
+ # byte-identical (install, uninstall, update, rollback, verify, and their
716
+ # continuous-form aliases): required, name-or-'all', shell-completed. The
717
+ # `diff` and `quickstart` variants differ (distinct help, and quickstart's
718
+ # `default="all"`) and declare their own `--harness` inline, so this is the
719
+ # single source of truth only for the identical form.
720
+ _harness_option = click.option(
721
+ "--harness",
722
+ required=True,
723
+ metavar="NAME",
724
+ help="Harness adapter name or 'all' for every supported harness.",
725
+ shell_complete=_complete_harness,
726
+ )
727
+
728
+ # Shared `--profile` decorator for every command that resolves the shared
729
+ # profile (install, update, diff, verify, status, profile show/init/set/edit,
730
+ # and the continuous-form aliases): an optional path to the shared profile
731
+ # YAML, defaulting to the host's standard location when omitted. The single
732
+ # source of truth so the flag surface stays uniform across the lifecycle.
733
+ _profile_option = click.option(
734
+ "--profile",
735
+ default=None,
736
+ metavar="PATH",
737
+ help="Path to shared profile YAML.",
738
+ )
739
+
740
+
741
+ def _require_project_if_needed(
742
+ adapter: _Adapter, harness: str, project_root: Path | None
743
+ ) -> None:
744
+ """Reject a project-scope adapter invoked without ``--project``.
745
+
746
+ Project-scope adapters (``requires_project = True``) materialize into
747
+ an operator-supplied project tree; every lifecycle command must be
748
+ told where that tree is. User-scope adapters are unaffected.
749
+ """
750
+ if _adapter_requires_project(adapter) and project_root is None:
751
+ raise _CliUserError(
752
+ code="project.required",
753
+ message="Apothem validation failed.",
754
+ field="project",
755
+ reason=(f"{harness!r} materializes a project-scope configuration surface."),
756
+ fix="Pass --project PATH for this harness.",
757
+ )
758
+
759
+
760
+ def _harness_roots(
761
+ entry: HarnessRegistryEntry,
762
+ adapter: _Adapter,
763
+ project_root: Path | None,
764
+ ) -> tuple[Path | None, Path | None]:
765
+ """Resolve the (harness_root, project_root) pair the install driver consumes.
766
+
767
+ The shared ``install_driver`` seams (``run_install`` / ``check_fidelity``)
768
+ take a user-scope ``harness_root`` (the home-rooted configuration directory)
769
+ XOR a project-scope ``project_root``. Project-scope adapters thread the
770
+ operator-supplied ``--project`` value; user-scope adapters resolve their
771
+ home root as the parent of the adapter's static ``output_path``.
772
+ """
773
+ if entry.scope == "project":
774
+ return None, project_root
775
+ return _adapter_resolve_output_path(adapter, project_root).parent, None
776
+
777
+
778
+ def _drift_state(
779
+ entry: HarnessRegistryEntry,
780
+ adapter: _Adapter,
781
+ installed: bool,
782
+ project_root: Path | None,
783
+ shared_profile: dict[str, Any],
784
+ ) -> str:
785
+ """Classify a harness's profile-fidelity drift for the ``status`` command.
786
+
787
+ ``absent`` when the adapter reports no install; otherwise the profile
788
+ fidelity verdict — ``in-sync`` when every projected anchor carries the
789
+ freshly-projected profile body verbatim, else ``drift``. The check is
790
+ read-only: it re-projects the profile in memory and compares against the
791
+ on-disk anchors, mutating nothing.
792
+ """
793
+ if not installed:
794
+ return "absent"
795
+ harness_root, scoped_project = _harness_roots(entry, adapter, project_root)
796
+ results = check_fidelity(
797
+ entry.package_key,
798
+ harness_root=harness_root,
799
+ project_root=scoped_project,
800
+ profile=shared_profile,
801
+ )
802
+ return "in-sync" if fidelity_is_faithful(results) else "drift"
803
+
804
+
805
+ def _dry_run_plan(
806
+ entry: HarnessRegistryEntry,
807
+ adapter: _Adapter,
808
+ project_root: Path | None,
809
+ shared_profile: dict[str, Any],
810
+ ) -> MaterializationRun:
811
+ """Return the no-write dry-run plan for one harness, diffs included.
812
+
813
+ Routes through the same ``install_driver.run_install(dry_run=True, profile=…)``
814
+ seam the install path uses, so the result carries the unified diff already
815
+ present in each operator-owned target's ``.detail`` — no re-derivation.
816
+ """
817
+ harness_root, scoped_project = _harness_roots(entry, adapter, project_root)
818
+ return install_driver.run_install(
819
+ entry.package_key,
820
+ harness_root=harness_root,
821
+ project_root=scoped_project,
822
+ dry_run=True,
823
+ profile=shared_profile,
824
+ )
825
+
826
+
827
+ def _materialization_error(
828
+ exc: MaterializationError, *, files_written: list[str]
829
+ ) -> dict[str, object]:
830
+ """Convert a materialization exception to the CLI diagnostic contract."""
831
+ first = exc.run.errors[0] if exc.run.errors else None
832
+ return _CliUserError(
833
+ code="materialization.failed",
834
+ message="Apothem materialization failed.",
835
+ field=first.operation if first is not None else "materialization",
836
+ reason=first.message if first is not None else str(exc),
837
+ fix="Review the target path, permissions, and harness support files before retrying.",
838
+ files_written=tuple(files_written),
839
+ ).to_dict()
840
+
841
+
842
+ def _configure_stdio() -> None:
843
+ """Force UTF-8 stdio on Windows so Rich output renders correctly.
844
+
845
+ Runs at CLI invocation only — never at import time — so it cannot
846
+ disturb a host process's captured streams. ``reconfigure`` mutates
847
+ the existing stream in place rather than replacing ``sys.stdout``,
848
+ which would orphan and later close a wrapping process's buffer
849
+ (e.g. pytest's capture buffer).
850
+ """
851
+ if sys.platform != "win32":
852
+ return
853
+ for stream_name in ("stdout", "stderr"):
854
+ stream = getattr(sys, stream_name)
855
+ reconfigure = getattr(stream, "reconfigure", None)
856
+ if reconfigure is not None:
857
+ reconfigure(encoding="utf-8", errors="replace")