@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,755 @@
1
+ # SPDX-License-Identifier: MIT
2
+
3
+ """Canonical shared-profile model, normalization, and diagnostics."""
4
+
5
+ from __future__ import annotations
6
+
7
+ import os
8
+ import re
9
+ from collections.abc import Callable, Iterable, Mapping
10
+ from dataclasses import dataclass
11
+ from pathlib import Path
12
+ from typing import Any, Final
13
+
14
+ import yaml
15
+ from jsonschema import Draft202012Validator, FormatChecker
16
+ from jsonschema.exceptions import ValidationError
17
+
18
+ from apothem.lib.harness_registry import SUPPORTED_HARNESS_IDS
19
+ from apothem.schemas import profile_schema_path
20
+
21
+ DEFAULT_PROFILE_PATH = Path("~/.config/apothem/profile.yaml")
22
+ DEFAULT_ROLE = "senior software engineer"
23
+ DEFAULT_LANGUAGE = "python"
24
+ DEFAULT_STYLE = "concise"
25
+ DEFAULT_SERIOUSNESS = "PERSONAL_USE"
26
+ DEFAULT_WORKSPACE_DIRECTORY_NAME = ".apothem"
27
+ DEFAULT_WORKSPACE_SCOPE = "project-local"
28
+
29
+ SERIOUSNESS_LEVELS = (
30
+ "EXPLORING",
31
+ "PERSONAL_USE",
32
+ "SHARED",
33
+ "PUBLIC_LAUNCH",
34
+ )
35
+
36
+ # Highest profile-schema version this engine understands. A version-less
37
+ # profile is treated as this version; a profile stamped higher is rejected
38
+ # with an upgrade-the-engine diagnostic before schema validation runs.
39
+ _CURRENT_SCHEMA_VERSION: Final[int] = 1
40
+
41
+ # MCP transports the schema and model both recognize; streamable-http is the
42
+ # modern replacement for sse. The schema↔code cross-check test pins these equal.
43
+ MCP_TRANSPORTS = ("stdio", "http", "sse", "streamable-http")
44
+
45
+ # The transport-conditional required field each server shape must carry. stdio
46
+ # servers launch a command; http-family servers carry a url. Mirrors the
47
+ # schema's if/then required-key blocks.
48
+ MCP_REQUIRED_FIELD_BY_TRANSPORT = {
49
+ "stdio": "command",
50
+ "http": "url",
51
+ "sse": "url",
52
+ "streamable-http": "url",
53
+ }
54
+
55
+ _SENSITIVE_KEY_PARTS = (
56
+ "token",
57
+ "secret",
58
+ "password",
59
+ "credential",
60
+ "api_key",
61
+ "apikey",
62
+ "private_key",
63
+ "authorization",
64
+ "auth",
65
+ "header",
66
+ )
67
+
68
+ _TOKEN_PATTERNS = (
69
+ re.compile(r"(?i)\b(?:bearer\s+)?sk-[A-Za-z0-9_-]{12,}\b"),
70
+ re.compile(r"\bgh[pousr]_[A-Za-z0-9_]{20,}\b"),
71
+ re.compile(r"\bxox[baprs]-[A-Za-z0-9-]{20,}\b"),
72
+ re.compile(r"\b[A-Za-z0-9_-]{32,}\.[A-Za-z0-9_-]{16,}\.[A-Za-z0-9_-]{16,}\b"),
73
+ )
74
+
75
+
76
+ @dataclass(frozen=True)
77
+ class IdentityProfile:
78
+ """Operator identity fields carried by the shared profile."""
79
+
80
+ name: str
81
+ role: str = DEFAULT_ROLE
82
+ email: str | None = None
83
+ website: str | None = None
84
+ github: str | None = None
85
+
86
+ def to_dict(self) -> dict[str, str]:
87
+ """Serialize to a dict, omitting any unset optional field
88
+ (``email`` / ``website`` / ``github``)."""
89
+ payload = {"name": self.name, "role": self.role}
90
+ if self.email:
91
+ payload["email"] = self.email
92
+ if self.website:
93
+ payload["website"] = self.website
94
+ if self.github:
95
+ payload["github"] = self.github
96
+ return payload
97
+
98
+
99
+ @dataclass(frozen=True)
100
+ class PreferenceProfile:
101
+ """Workflow and interaction defaults shared across harnesses."""
102
+
103
+ language: str = DEFAULT_LANGUAGE
104
+ style: str = DEFAULT_STYLE
105
+ formatter: str | None = None
106
+ test_framework: str | None = None
107
+
108
+ def to_dict(self) -> dict[str, str]:
109
+ """Serialize to a dict, omitting any unset optional field
110
+ (``formatter`` / ``test_framework``)."""
111
+ payload = {"language": self.language, "style": self.style}
112
+ if self.formatter:
113
+ payload["formatter"] = self.formatter
114
+ if self.test_framework:
115
+ payload["test_framework"] = self.test_framework
116
+ return payload
117
+
118
+
119
+ @dataclass(frozen=True)
120
+ class EnforcementFlags:
121
+ """Opt-in toggles for shipped behaviors that ship default-off.
122
+
123
+ Every flag defaults ``False``: a clean install imposes no sprint
124
+ ceremony, no agent dispatch, no parallel-task expectation, no
125
+ continuous multi-step advancement, and no learning capture. Each
126
+ behavior's machinery stays preserved and invokable; setting a flag
127
+ ``True`` opts the operator into that behavior explicitly, never the
128
+ reverse. Preferences carried as toggles live here; effort and model
129
+ selection are absent-by-default frontmatter resolved per request.
130
+ """
131
+
132
+ sprints: bool = False
133
+ agent_teams: bool = False
134
+ multitasking: bool = False
135
+ continuous_execution: bool = False
136
+ learning_loop: bool = False
137
+
138
+ def to_dict(self) -> dict[str, bool]:
139
+ """Serialize all five opt-in flags to a bool dict (none omitted)."""
140
+ return {
141
+ "sprints": self.sprints,
142
+ "agent_teams": self.agent_teams,
143
+ "multitasking": self.multitasking,
144
+ "continuous_execution": self.continuous_execution,
145
+ "learning_loop": self.learning_loop,
146
+ }
147
+
148
+
149
+ @dataclass(frozen=True)
150
+ class WorkspaceConfig:
151
+ """Where Apothem keeps its shared, gitignored working directory.
152
+
153
+ ``directory_name`` names the single working directory (default
154
+ ``.apothem``); ``scope`` roots it project-local (``<project-root>/…``,
155
+ the default) or in the user home (``~/…``). The defaults reproduce
156
+ today's behavior exactly: a profile that omits ``workspace`` resolves
157
+ to a project-local ``.apothem``. The field is unconsumed by any
158
+ resolver today — additive plumbing for a later relocation.
159
+ """
160
+
161
+ directory_name: str = ".apothem"
162
+ scope: str = "project-local" # "project-local" | "user-home"
163
+
164
+ def to_dict(self) -> dict[str, str]:
165
+ """Serialize the workspace config to a string dict (none omitted)."""
166
+ return {"directory_name": self.directory_name, "scope": self.scope}
167
+
168
+
169
+ @dataclass(frozen=True)
170
+ class McpServer:
171
+ """One MCP server entry from the shared profile's portability inventory.
172
+
173
+ stdio servers carry a ``command`` (and optional ``args``); http/sse/
174
+ streamable-http servers carry a ``url``. ``env`` is an optional string map
175
+ passed to the server process. The shape mirrors the schema's ``mcpServer``
176
+ ``$def`` and its transport-conditional required keys.
177
+ """
178
+
179
+ transport: str
180
+ command: str | None = None
181
+ args: tuple[str, ...] = ()
182
+ url: str | None = None
183
+ env: Mapping[str, str] | None = None
184
+
185
+ def to_dict(self) -> dict[str, Any]:
186
+ """Serialize to a dict, omitting unset optional fields and emitting
187
+ ``env`` with its keys sorted for deterministic output."""
188
+ payload: dict[str, Any] = {"transport": self.transport}
189
+ if self.command:
190
+ payload["command"] = self.command
191
+ if self.args:
192
+ payload["args"] = list(self.args)
193
+ if self.url:
194
+ payload["url"] = self.url
195
+ if self.env:
196
+ payload["env"] = {key: self.env[key] for key in sorted(self.env)}
197
+ return payload
198
+
199
+
200
+ @dataclass(frozen=True)
201
+ class CanonicalProfile:
202
+ """Normalized shared profile consumed by CLI and harness materializers."""
203
+
204
+ identity: IdentityProfile
205
+ preferences: PreferenceProfile
206
+ rules: tuple[str, ...] = ()
207
+ seriousness: str = DEFAULT_SERIOUSNESS
208
+ enforcement: EnforcementFlags = EnforcementFlags()
209
+ mcp_servers: Mapping[str, McpServer] | None = None
210
+ harnesses: Mapping[str, Mapping[str, Any]] | None = None
211
+ exclude_harnesses: tuple[str, ...] = ()
212
+ workspace: WorkspaceConfig = WorkspaceConfig()
213
+
214
+ def to_dict(self) -> dict[str, Any]:
215
+ """Serialize the full profile to a nested dict, sorting MCP server
216
+ keys and stabilizing harness-override mappings for deterministic
217
+ output."""
218
+ payload: dict[str, Any] = {
219
+ "identity": self.identity.to_dict(),
220
+ "preferences": self.preferences.to_dict(),
221
+ "rules": list(self.rules),
222
+ "seriousness": self.seriousness,
223
+ "enforcement": self.enforcement.to_dict(),
224
+ "mcp_servers": {
225
+ name: (self.mcp_servers or {})[name].to_dict()
226
+ for name in sorted(self.mcp_servers or {})
227
+ },
228
+ "harnesses": {
229
+ harness: _stable_mapping(override)
230
+ for harness, override in (self.harnesses or {}).items()
231
+ },
232
+ "exclude_harnesses": list(self.exclude_harnesses),
233
+ "workspace": self.workspace.to_dict(),
234
+ }
235
+ return payload
236
+
237
+ def for_harness(self, harness_id: str) -> dict[str, Any]:
238
+ """Return this profile with the named harness override applied."""
239
+ canonical_id = normalize_harness_id(harness_id)
240
+ if canonical_id not in SUPPORTED_HARNESS_IDS:
241
+ raise ValueError(f"Unsupported harness id: {harness_id}")
242
+ base = self.to_dict()
243
+ overrides = dict((self.harnesses or {}).get(canonical_id, {}))
244
+ merged = _deep_merge(base, overrides)
245
+ merged.pop("harnesses", None)
246
+ merged.pop("exclude_harnesses", None)
247
+ # workspace is project-global filesystem layout, not a per-harness
248
+ # surface — strip it from the per-harness projection (decision D-WS-2).
249
+ merged.pop("workspace", None)
250
+ return _stable_mapping(merged)
251
+
252
+
253
+ @dataclass(frozen=True)
254
+ class ProfileDiagnostic:
255
+ """Actionable profile validation error suitable for plain or JSON output."""
256
+
257
+ code: str
258
+ message: str
259
+ profile_path: str
260
+ field: str
261
+ reason: str
262
+ fix: str
263
+ safe_value: Any | None = None
264
+
265
+ def to_dict(self) -> dict[str, Any]:
266
+ """Serialize to a dict, appending an empty ``files_written`` list so
267
+ the payload matches the CLI lifecycle-envelope shape."""
268
+ return {
269
+ "code": self.code,
270
+ "message": self.message,
271
+ "profile_path": self.profile_path,
272
+ "field": self.field,
273
+ "reason": self.reason,
274
+ "fix": self.fix,
275
+ "safe_value": self.safe_value,
276
+ "files_written": [],
277
+ }
278
+
279
+ def format_plain(self) -> str:
280
+ """Render the diagnostic as a human-readable plain-text block."""
281
+ safe_value = "none" if self.safe_value is None else repr(self.safe_value)
282
+ return (
283
+ f"{self.message}\n"
284
+ f"Profile: {self.profile_path}\n"
285
+ f"Field: {self.field}\n"
286
+ f"Reason: {self.reason}\n"
287
+ f"Fix: {self.fix}\n"
288
+ f"Safe value: {safe_value}\n"
289
+ "Files written: none."
290
+ )
291
+
292
+
293
+ class ProfileValidationError(ValueError):
294
+ """Raised when a profile cannot be parsed, validated, or normalized."""
295
+
296
+ def __init__(self, diagnostic: ProfileDiagnostic) -> None:
297
+ super().__init__(diagnostic.format_plain())
298
+ self.diagnostic = diagnostic
299
+
300
+
301
+ def resolve_profile_path(profile: str | Path | None) -> Path:
302
+ """Return an absolute shared-profile path without dereferencing symlinks."""
303
+ selected = Path(profile) if profile is not None else DEFAULT_PROFILE_PATH
304
+ # os.path.abspath normalizes to an absolute path WITHOUT resolving symlinks;
305
+ # Path.resolve would dereference them and break the no-symlink contract above.
306
+ return Path(os.path.abspath(selected.expanduser())) # noqa: PTH100
307
+
308
+
309
+ def load_profile_file(profile_path: Path) -> CanonicalProfile:
310
+ """Read, validate, and normalize a profile YAML file."""
311
+ resolved = resolve_profile_path(profile_path)
312
+ if not resolved.exists():
313
+ raise ProfileValidationError(
314
+ ProfileDiagnostic(
315
+ code="profile.not_found",
316
+ message="Apothem validation failed.",
317
+ profile_path=str(resolved),
318
+ field="profile",
319
+ reason="profile file does not exist",
320
+ fix="Run 'apothem profile init' or pass --profile PATH.",
321
+ )
322
+ )
323
+
324
+ try:
325
+ raw_text = resolved.read_text(encoding="utf-8")
326
+ except OSError as exc:
327
+ raise ProfileValidationError(
328
+ ProfileDiagnostic(
329
+ code="profile.read_failed",
330
+ message="Apothem validation failed.",
331
+ profile_path=str(resolved),
332
+ field="profile",
333
+ reason=str(exc),
334
+ fix="Check the profile path and file permissions.",
335
+ )
336
+ ) from exc
337
+
338
+ try:
339
+ raw_profile = yaml.safe_load(raw_text)
340
+ except yaml.YAMLError as exc:
341
+ raise ProfileValidationError(
342
+ ProfileDiagnostic(
343
+ code="profile.yaml_invalid",
344
+ message="Apothem validation failed.",
345
+ profile_path=str(resolved),
346
+ field="profile",
347
+ reason=_one_line(str(exc)),
348
+ fix="Fix the YAML syntax before running the command again.",
349
+ )
350
+ ) from exc
351
+
352
+ if raw_profile is None:
353
+ raw_profile = {}
354
+ if not isinstance(raw_profile, Mapping):
355
+ raise ProfileValidationError(
356
+ ProfileDiagnostic(
357
+ code="profile.type_invalid",
358
+ message="Apothem validation failed.",
359
+ profile_path=str(resolved),
360
+ field="profile",
361
+ reason="profile document must be a mapping",
362
+ fix="Use YAML key/value fields such as identity.name.",
363
+ safe_value=redact_value(raw_profile),
364
+ )
365
+ )
366
+
367
+ return validate_profile(raw_profile, profile_path=resolved)
368
+
369
+
370
+ # Ordered profile-schema migration chain. Each entry maps a source version N to
371
+ # a callable that returns the profile upgraded to version N+1. The chain is a
372
+ # no-op while only v1 exists; a future v1->v2 migration registers as
373
+ # ``_MIGRATIONS[1] = _migrate_v1_to_v2`` and slots in without touching the
374
+ # driver loop in ``migrate_profile``.
375
+ _MIGRATIONS: dict[int, Callable[[Mapping[str, Any]], Mapping[str, Any]]] = {
376
+ # 1: _migrate_v1_to_v2, # registered when schema_version 2 ships.
377
+ }
378
+
379
+
380
+ def migrate_profile(
381
+ profile: Mapping[str, Any], *, profile_path: str | Path = "<memory>"
382
+ ) -> Mapping[str, Any]:
383
+ """Forward-migrate *profile* to the current schema version.
384
+
385
+ A version-less profile is treated as ``_CURRENT_SCHEMA_VERSION``. A profile
386
+ stamped with a version greater than this engine supports is rejected with an
387
+ upgrade-the-engine diagnostic before any schema validation runs, so a
388
+ newer-version profile surfaces an actionable message rather than an opaque
389
+ ``additionalProperties`` error. A non-integer or otherwise malformed
390
+ ``schema_version`` is left for the jsonschema validator to reject. The v1
391
+ migration chain is a no-op: the mapping is returned unchanged.
392
+ """
393
+ declared = profile.get("schema_version", _CURRENT_SCHEMA_VERSION)
394
+ # bool is an int subclass; treat a non-int (including bool) version as
395
+ # malformed and let the schema validator emit the precise diagnostic.
396
+ if isinstance(declared, int) and not isinstance(declared, bool):
397
+ if declared > _CURRENT_SCHEMA_VERSION:
398
+ raise ProfileValidationError(
399
+ ProfileDiagnostic(
400
+ code="profile.version_unsupported",
401
+ message="Apothem validation failed.",
402
+ profile_path=str(profile_path),
403
+ field="schema_version",
404
+ reason=(
405
+ f"profile declares schema_version {declared} but this "
406
+ f"apothem supports up to {_CURRENT_SCHEMA_VERSION}"
407
+ ),
408
+ fix=(
409
+ "Upgrade apothem to a version that supports this "
410
+ "profile (the profile was written by a newer apothem)."
411
+ ),
412
+ )
413
+ )
414
+ version = declared
415
+ while version < _CURRENT_SCHEMA_VERSION:
416
+ profile = _MIGRATIONS[version](profile)
417
+ version += 1
418
+ return profile
419
+
420
+
421
+ def validate_profile(
422
+ profile: Mapping[str, Any], *, profile_path: str | Path = "<memory>"
423
+ ) -> CanonicalProfile:
424
+ """Validate *profile* against the packaged schema, then normalize it."""
425
+ profile = migrate_profile(profile, profile_path=profile_path)
426
+ schema = yaml.safe_load(profile_schema_path().read_text(encoding="utf-8"))
427
+ validator = Draft202012Validator(schema, format_checker=FormatChecker())
428
+ errors = sorted(
429
+ validator.iter_errors(profile), key=lambda err: list(err.absolute_path)
430
+ )
431
+ if errors:
432
+ raise ProfileValidationError(
433
+ _diagnostic_from_validation_error(errors[0], profile_path)
434
+ )
435
+ return coerce_profile(profile)
436
+
437
+
438
+ def coerce_profile(profile: Mapping[str, Any]) -> CanonicalProfile:
439
+ """Apply profile defaults and deterministic normalization."""
440
+ identity = _mapping(profile.get("identity"))
441
+ preferences = _mapping(profile.get("preferences"))
442
+ enforcement = _mapping(profile.get("enforcement"))
443
+ workspace = _mapping(profile.get("workspace"))
444
+ harnesses = _mapping(profile.get("harnesses"))
445
+
446
+ normalized_harnesses: dict[str, Mapping[str, Any]] = {}
447
+ for harness_id in sorted(harnesses, key=_harness_sort_key):
448
+ value = harnesses[harness_id]
449
+ if isinstance(value, Mapping):
450
+ normalized_harnesses[normalize_harness_id(harness_id)] = _stable_mapping(
451
+ value
452
+ )
453
+
454
+ return CanonicalProfile(
455
+ identity=IdentityProfile(
456
+ name=_normalize_scalar(identity.get("name", "")),
457
+ role=_normalize_scalar(identity.get("role", DEFAULT_ROLE)) or DEFAULT_ROLE,
458
+ email=_optional_scalar(identity.get("email")),
459
+ website=_optional_scalar(identity.get("website")),
460
+ github=_optional_scalar(identity.get("github")),
461
+ ),
462
+ preferences=PreferenceProfile(
463
+ language=normalize_language(preferences.get("language", DEFAULT_LANGUAGE)),
464
+ style=_normalize_scalar(preferences.get("style", DEFAULT_STYLE))
465
+ or DEFAULT_STYLE,
466
+ formatter=_optional_scalar(preferences.get("formatter")),
467
+ test_framework=_optional_scalar(preferences.get("test_framework")),
468
+ ),
469
+ rules=tuple(_normalize_rules(profile.get("rules"))),
470
+ seriousness=_normalize_scalar(profile.get("seriousness", DEFAULT_SERIOUSNESS))
471
+ or DEFAULT_SERIOUSNESS,
472
+ enforcement=EnforcementFlags(
473
+ sprints=_normalize_flag(enforcement.get("sprints")),
474
+ agent_teams=_normalize_flag(enforcement.get("agent_teams")),
475
+ multitasking=_normalize_flag(enforcement.get("multitasking")),
476
+ continuous_execution=_normalize_flag(
477
+ enforcement.get("continuous_execution")
478
+ ),
479
+ learning_loop=_normalize_flag(enforcement.get("learning_loop")),
480
+ ),
481
+ mcp_servers=_normalize_mcp_servers(profile.get("mcp_servers")),
482
+ harnesses=normalized_harnesses,
483
+ exclude_harnesses=tuple(
484
+ sorted(
485
+ (
486
+ normalize_harness_id(item)
487
+ for item in _sequence(profile.get("exclude_harnesses"))
488
+ ),
489
+ key=_harness_sort_key,
490
+ )
491
+ ),
492
+ workspace=WorkspaceConfig(
493
+ directory_name=_normalize_scalar(workspace.get("directory_name"))
494
+ or DEFAULT_WORKSPACE_DIRECTORY_NAME,
495
+ scope=_normalize_scalar(workspace.get("scope")) or DEFAULT_WORKSPACE_SCOPE,
496
+ ),
497
+ )
498
+
499
+
500
+ def normalize_harness_id(value: object) -> str:
501
+ """Return the canonical spelling for an already supported harness id."""
502
+ return _normalize_scalar(value).lower()
503
+
504
+
505
+ def normalize_language(value: object) -> str:
506
+ """Normalize the profile language preference to its semantic form."""
507
+ return _normalize_scalar(value).lower() or DEFAULT_LANGUAGE
508
+
509
+
510
+ def redact_value(value: object, *, field_path: tuple[object, ...] = ()) -> object:
511
+ """Return *value* with secret-like keys and token-shaped strings redacted."""
512
+ if _path_is_sensitive(field_path):
513
+ return "<redacted>"
514
+ if isinstance(value, str):
515
+ if any(pattern.search(value) for pattern in _TOKEN_PATTERNS):
516
+ return "<redacted>"
517
+ return value
518
+ if isinstance(value, Mapping):
519
+ redacted: dict[str, Any] = {}
520
+ for key, item in value.items():
521
+ redacted[str(key)] = redact_value(item, field_path=(*field_path, key))
522
+ return redacted
523
+ if isinstance(value, list):
524
+ return [
525
+ redact_value(item, field_path=(*field_path, index))
526
+ for index, item in enumerate(value)
527
+ ]
528
+ return value
529
+
530
+
531
+ def _diagnostic_from_validation_error(
532
+ error: ValidationError, profile_path: str | Path
533
+ ) -> ProfileDiagnostic:
534
+ field = _format_field_path(error.absolute_path)
535
+ return ProfileDiagnostic(
536
+ code=_error_code(error.validator),
537
+ message="Apothem validation failed.",
538
+ profile_path=str(profile_path),
539
+ field=field,
540
+ reason=error.message,
541
+ fix=_suggest_fix(error),
542
+ safe_value=redact_value(error.instance, field_path=tuple(error.absolute_path)),
543
+ )
544
+
545
+
546
+ def _error_code(validator_name: object) -> str:
547
+ if not isinstance(validator_name, str):
548
+ return "profile.validation_failed"
549
+ return {
550
+ "additionalProperties": "profile.unknown_field",
551
+ "enum": "profile.invalid_choice",
552
+ "format": "profile.invalid_format",
553
+ "minLength": "profile.empty_value",
554
+ "required": "profile.required",
555
+ "type": "profile.invalid_type",
556
+ "uniqueItems": "profile.duplicate_value",
557
+ }.get(validator_name, "profile.validation_failed")
558
+
559
+
560
+ def _suggest_fix(error: ValidationError) -> str:
561
+ if error.validator == "required":
562
+ return "Add the missing required field."
563
+ if error.validator == "additionalProperties":
564
+ return "Remove the unsupported field or correct its spelling."
565
+ if error.validator == "enum":
566
+ validator_value = error.validator_value
567
+ if not isinstance(validator_value, Iterable) or isinstance(
568
+ validator_value, str | bytes
569
+ ):
570
+ return "Use one of the allowed values."
571
+ choices = ", ".join(repr(choice) for choice in validator_value)
572
+ return f"Use one of: {choices}."
573
+ if error.validator == "format":
574
+ return "Use a value that matches the required format."
575
+ if error.validator == "minLength":
576
+ return "Use a non-empty value."
577
+ if error.validator == "type":
578
+ return "Use a value with the expected type."
579
+ if error.validator == "uniqueItems":
580
+ return "Remove duplicate values."
581
+ return "Fix the profile field and run the command again."
582
+
583
+
584
+ def _format_field_path(path: Iterable[object]) -> str:
585
+ parts = [str(part) for part in path]
586
+ return ".".join(parts) if parts else "profile"
587
+
588
+
589
+ def _mapping(value: object) -> Mapping[str, Any]:
590
+ if isinstance(value, Mapping):
591
+ return value
592
+ return {}
593
+
594
+
595
+ def _sequence(value: object) -> list[object]:
596
+ if isinstance(value, list):
597
+ return list(value)
598
+ if value is None:
599
+ return []
600
+ return [value]
601
+
602
+
603
+ def _normalize_scalar(value: object) -> str:
604
+ if value is None:
605
+ return ""
606
+ return _normalize_newlines(str(value)).strip()
607
+
608
+
609
+ def _optional_scalar(value: object) -> str | None:
610
+ normalized = _normalize_scalar(value)
611
+ return normalized or None
612
+
613
+
614
+ def _normalize_flag(value: object) -> bool:
615
+ """Coerce an opt-in enforcement flag to bool; absent reads as default-off."""
616
+ return bool(value)
617
+
618
+
619
+ def _normalize_newlines(value: str) -> str:
620
+ return value.replace("\r\n", "\n").replace("\r", "\n")
621
+
622
+
623
+ def _normalize_rules(value: object) -> list[str]:
624
+ normalized: list[str] = []
625
+ for item in _sequence(value):
626
+ rule = _normalize_scalar(item)
627
+ if rule:
628
+ normalized.append(rule)
629
+ return normalized
630
+
631
+
632
+ def _normalize_mcp_servers(value: object) -> dict[str, McpServer]:
633
+ """Normalize the profile MCP inventory into name-keyed McpServer values.
634
+
635
+ Servers are emitted in sorted name order for deterministic output. Each
636
+ server's transport is lower-cased; stdio ``args`` and ``env`` values are
637
+ coerced to strings. Non-mapping entries are skipped (the schema rejects
638
+ them before this runs on a validated profile).
639
+ """
640
+ servers: dict[str, McpServer] = {}
641
+ mapping = _mapping(value)
642
+ for name in sorted(mapping):
643
+ spec = mapping[name]
644
+ if not isinstance(spec, Mapping):
645
+ continue
646
+ env_raw = _mapping(spec.get("env"))
647
+ env = {str(key): _normalize_scalar(item) for key, item in env_raw.items()}
648
+ servers[_normalize_scalar(name)] = McpServer(
649
+ transport=_normalize_scalar(spec.get("transport")).lower(),
650
+ command=_optional_scalar(spec.get("command")),
651
+ args=tuple(str(item) for item in _sequence(spec.get("args"))),
652
+ url=_optional_scalar(spec.get("url")),
653
+ env=env or None,
654
+ )
655
+ return servers
656
+
657
+
658
+ def _union_lists(base: list[Any], override: list[Any]) -> list[Any]:
659
+ """Return base ++ override with order preserved and duplicates removed."""
660
+ merged = list(base)
661
+ for item in override:
662
+ if item not in merged:
663
+ merged.append(item)
664
+ return merged
665
+
666
+
667
+ def _deep_merge(base: Mapping[str, Any], override: Mapping[str, Any]) -> dict[str, Any]:
668
+ """Merge *override* onto *base* with the ratified per-harness semantics.
669
+
670
+ Mappings merge recursively; list-valued overrides (e.g. ``rules``) UNION
671
+ additively (base first, then new, deduplicated) — the ratified "refine"
672
+ semantics, not silent replace. The MCP inventory unions by server name with
673
+ each named server replaced wholesale, so an overridden server carries only
674
+ its own transport's keys (no stale-field pollution). Other scalars override.
675
+ """
676
+ merged = dict(base)
677
+ for key, value in override.items():
678
+ existing = merged.get(key)
679
+ if (
680
+ key == "mcp_servers"
681
+ and isinstance(existing, Mapping)
682
+ and isinstance(value, Mapping)
683
+ ):
684
+ merged[key] = {**existing, **value}
685
+ elif isinstance(existing, Mapping) and isinstance(value, Mapping):
686
+ merged[key] = _deep_merge(existing, value)
687
+ elif isinstance(existing, list) and isinstance(value, list):
688
+ merged[key] = _union_lists(existing, value)
689
+ else:
690
+ merged[key] = value
691
+ return merged
692
+
693
+
694
+ def _stable_mapping(value: Mapping[str, Any]) -> dict[str, Any]:
695
+ stable: dict[str, Any] = {}
696
+ for key in sorted(value):
697
+ item = value[key]
698
+ if isinstance(item, Mapping):
699
+ stable[str(key)] = _stable_mapping(item)
700
+ elif isinstance(item, list):
701
+ stable[str(key)] = [
702
+ _stable_mapping(child) if isinstance(child, Mapping) else child
703
+ for child in item
704
+ ]
705
+ else:
706
+ stable[str(key)] = item
707
+ return stable
708
+
709
+
710
+ def _harness_sort_key(value: object) -> tuple[int, str]:
711
+ harness_id = normalize_harness_id(value)
712
+ try:
713
+ return (SUPPORTED_HARNESS_IDS.index(harness_id), harness_id)
714
+ except ValueError:
715
+ return (len(SUPPORTED_HARNESS_IDS), harness_id)
716
+
717
+
718
+ def _path_is_sensitive(path: tuple[object, ...]) -> bool:
719
+ joined = ".".join(str(part).lower() for part in path)
720
+ return any(part in joined for part in _SENSITIVE_KEY_PARTS)
721
+
722
+
723
+ def _one_line(value: str) -> str:
724
+ return " ".join(value.split())
725
+
726
+
727
+ __all__ = [
728
+ "DEFAULT_LANGUAGE",
729
+ "DEFAULT_PROFILE_PATH",
730
+ "DEFAULT_ROLE",
731
+ "DEFAULT_SERIOUSNESS",
732
+ "DEFAULT_STYLE",
733
+ "DEFAULT_WORKSPACE_DIRECTORY_NAME",
734
+ "DEFAULT_WORKSPACE_SCOPE",
735
+ "MCP_REQUIRED_FIELD_BY_TRANSPORT",
736
+ "MCP_TRANSPORTS",
737
+ "SERIOUSNESS_LEVELS",
738
+ "SUPPORTED_HARNESS_IDS",
739
+ "CanonicalProfile",
740
+ "EnforcementFlags",
741
+ "IdentityProfile",
742
+ "McpServer",
743
+ "PreferenceProfile",
744
+ "ProfileDiagnostic",
745
+ "ProfileValidationError",
746
+ "WorkspaceConfig",
747
+ "coerce_profile",
748
+ "load_profile_file",
749
+ "migrate_profile",
750
+ "normalize_harness_id",
751
+ "normalize_language",
752
+ "redact_value",
753
+ "resolve_profile_path",
754
+ "validate_profile",
755
+ ]