@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,297 @@
1
+ # SPDX-License-Identifier: MIT
2
+
3
+ """Verify CI declares a cross-platform OS x Python matrix.
4
+
5
+ Why this enforcement exists. The cross-platform discipline ratified at
6
+ the supply-chain SOTA-rigor contract requires the canonical CI workflow at
7
+ ``.github/workflows/ci-matrix.yml`` to declare a strategy matrix
8
+ spanning three operating systems (``ubuntu-latest``, ``macos-latest``,
9
+ ``windows-latest``) and four Python versions (``3.10``, ``3.11``,
10
+ ``3.12``, ``3.13``), yielding twelve matrix cells. The validator parses
11
+ the workflow YAML, walks every job's ``strategy.matrix`` block, and
12
+ reports drift on five named classes: ``workflow-absent`` (informational
13
+ pass when the workflow has not yet been materialised),
14
+ ``strategy-matrix-absent``, ``os-missing``, ``python-version-missing``,
15
+ and ``matrix-cell-count-mismatch``.
16
+
17
+ Detection strategy. The validator searches ``<root>/.github/workflows/``
18
+ for files matching ``*ci-matrix*.yml``. If no such file exists, the
19
+ result carries ``not-yet-materialised: true`` and exits 0
20
+ (informational tolerance — the workflow may be authored in a later
21
+ phase). When the workflow exists, the validator parses it with
22
+ ``yaml.safe_load`` (falling back to a permissive string-scan if PyYAML
23
+ is absent), enumerates every job's ``strategy.matrix``, and verifies
24
+ the required OS and Python axes plus the expected twelve-cell product.
25
+ """
26
+
27
+ from __future__ import annotations
28
+
29
+ import json
30
+ import re
31
+ import sys
32
+ from dataclasses import asdict, dataclass, field
33
+ from pathlib import Path
34
+ from typing import Any, Final
35
+
36
+ REQUIRED_OS: Final[tuple[str, ...]] = (
37
+ "ubuntu-latest",
38
+ "macos-latest",
39
+ "windows-latest",
40
+ )
41
+ REQUIRED_PYTHON: Final[tuple[str, ...]] = ("3.10", "3.11", "3.12", "3.13")
42
+ EXPECTED_CELL_COUNT: Final[int] = len(REQUIRED_OS) * len(REQUIRED_PYTHON)
43
+
44
+ GREP_NAME: Final[str] = "cross-platform-matrix-grep"
45
+ RULE_ANCHOR: Final[str] = "cross-platform OS x Python CI matrix"
46
+ EXIT_PASS: Final[int] = 0
47
+ EXIT_FAIL: Final[int] = 2
48
+
49
+ WORKFLOW_DIR: Final[str] = ".github/workflows"
50
+ WORKFLOW_GLOB: Final[str] = "*ci-matrix*.yml"
51
+
52
+
53
+ @dataclass(frozen=True)
54
+ class Finding:
55
+ drift_class: str
56
+ detail: str
57
+ rule: str = RULE_ANCHOR
58
+
59
+
60
+ @dataclass(frozen=True)
61
+ class GrepResult:
62
+ grep: str
63
+ root: str
64
+ passed: bool
65
+ workflow_path: str | None = None
66
+ not_yet_materialised: bool = False
67
+ findings: list[Finding] = field(default_factory=list)
68
+
69
+ def to_json(self) -> str:
70
+ payload: dict[str, Any] = {
71
+ "grep": self.grep,
72
+ "root": self.root,
73
+ "passed": self.passed,
74
+ "workflow_path": self.workflow_path,
75
+ "not-yet-materialised": self.not_yet_materialised,
76
+ "findings": [asdict(f) for f in self.findings],
77
+ }
78
+ return json.dumps(payload, indent=2)
79
+
80
+
81
+ def _find_workflow(root: Path) -> Path | None:
82
+ """Locate the canonical CI-matrix workflow, if present."""
83
+ workflows = root / WORKFLOW_DIR
84
+ if not workflows.is_dir():
85
+ return None
86
+ # Prefer the exact canonical name; fall back to the glob match.
87
+ canonical = workflows / "ci-matrix.yml"
88
+ if canonical.exists():
89
+ return canonical
90
+ candidates = sorted(workflows.glob(WORKFLOW_GLOB))
91
+ return candidates[0] if candidates else None
92
+
93
+
94
+ def _parse_yaml(content: str) -> dict[str, Any] | None:
95
+ """Parse YAML via PyYAML; return None when PyYAML is unavailable."""
96
+ try:
97
+ import yaml
98
+ except ImportError:
99
+ return None
100
+ try:
101
+ loaded = yaml.safe_load(content)
102
+ except yaml.YAMLError:
103
+ return None
104
+ return loaded if isinstance(loaded, dict) else None
105
+
106
+
107
+ def _normalize_versions(values: list[Any]) -> list[str]:
108
+ """Coerce matrix python-version entries to strings (YAML may yield floats)."""
109
+ out: list[str] = []
110
+ for v in values:
111
+ if isinstance(v, str):
112
+ out.append(v)
113
+ elif isinstance(v, (int, float)):
114
+ out.append(str(v))
115
+ return out
116
+
117
+
118
+ def _collect_matrices(workflow: dict[str, Any]) -> list[dict[str, Any]]:
119
+ """Walk every job's strategy.matrix block."""
120
+ matrices: list[dict[str, Any]] = []
121
+ jobs = workflow.get("jobs")
122
+ if not isinstance(jobs, dict):
123
+ return matrices
124
+ for job in jobs.values():
125
+ if not isinstance(job, dict):
126
+ continue
127
+ strategy = job.get("strategy")
128
+ if not isinstance(strategy, dict):
129
+ continue
130
+ matrix = strategy.get("matrix")
131
+ if isinstance(matrix, dict):
132
+ matrices.append(matrix)
133
+ return matrices
134
+
135
+
136
+ def _check_parsed(workflow: dict[str, Any], findings: list[Finding]) -> None:
137
+ """Verify the parsed workflow declares the canonical 3x4 matrix."""
138
+ matrices = _collect_matrices(workflow)
139
+ if not matrices:
140
+ findings.append(
141
+ Finding(
142
+ drift_class="strategy-matrix-absent",
143
+ detail="no job declares a strategy.matrix block",
144
+ )
145
+ )
146
+ return
147
+ # Union axes across every job's matrix so a multi-job split still passes.
148
+ all_os: set[str] = set()
149
+ all_py: set[str] = set()
150
+ for matrix in matrices:
151
+ raw_os = matrix.get("os")
152
+ if isinstance(raw_os, list):
153
+ all_os.update(s for s in raw_os if isinstance(s, str))
154
+ raw_py = (
155
+ matrix.get("python-version")
156
+ or matrix.get("python_version")
157
+ or matrix.get("python")
158
+ )
159
+ if isinstance(raw_py, list):
160
+ all_py.update(_normalize_versions(raw_py))
161
+ missing_os = [o for o in REQUIRED_OS if o not in all_os]
162
+ if missing_os:
163
+ findings.append(
164
+ Finding(
165
+ drift_class="os-missing",
166
+ detail=f"OS axis missing: {', '.join(missing_os)}",
167
+ )
168
+ )
169
+ missing_py = [p for p in REQUIRED_PYTHON if p not in all_py]
170
+ if missing_py:
171
+ findings.append(
172
+ Finding(
173
+ drift_class="python-version-missing",
174
+ detail=f"Python-version axis missing: {', '.join(missing_py)}",
175
+ )
176
+ )
177
+ # Cell-count check uses the intersection of required-and-present axes
178
+ # to avoid double-reporting when an axis is already flagged missing.
179
+ present_os = [o for o in REQUIRED_OS if o in all_os]
180
+ present_py = [p for p in REQUIRED_PYTHON if p in all_py]
181
+ actual_cells = len(present_os) * len(present_py)
182
+ if actual_cells != EXPECTED_CELL_COUNT:
183
+ findings.append(
184
+ Finding(
185
+ drift_class="matrix-cell-count-mismatch",
186
+ detail=(
187
+ f"expected {EXPECTED_CELL_COUNT} cells "
188
+ f"(3 OS x 4 Python); actual {actual_cells}"
189
+ ),
190
+ )
191
+ )
192
+
193
+
194
+ _OS_LINE_RE: Final[re.Pattern[str]] = re.compile(
195
+ r"(ubuntu-latest|macos-latest|windows-latest)"
196
+ )
197
+ _PY_LINE_RE: Final[re.Pattern[str]] = re.compile(
198
+ r'["\']?(3\.10|3\.11|3\.12|3\.13)["\']?'
199
+ )
200
+
201
+
202
+ def _check_string_fallback(content: str, findings: list[Finding]) -> None:
203
+ """Permissive string-scan when PyYAML is unavailable.
204
+
205
+ Looks for a ``strategy:`` block plus a ``matrix:`` token. Verifies
206
+ OS and Python version names appear somewhere in the document. This
207
+ fallback is intentionally lenient — it cannot distinguish per-job
208
+ matrices and will under-report drift compared with YAML parsing.
209
+ """
210
+ if "strategy:" not in content or "matrix:" not in content:
211
+ findings.append(
212
+ Finding(
213
+ drift_class="strategy-matrix-absent",
214
+ detail="no `strategy:` / `matrix:` tokens found in workflow",
215
+ )
216
+ )
217
+ return
218
+ os_hits = {m.group(1) for m in _OS_LINE_RE.finditer(content)}
219
+ py_hits = {m.group(1) for m in _PY_LINE_RE.finditer(content)}
220
+ missing_os = [o for o in REQUIRED_OS if o not in os_hits]
221
+ if missing_os:
222
+ findings.append(
223
+ Finding(
224
+ drift_class="os-missing",
225
+ detail=f"OS axis missing: {', '.join(missing_os)}",
226
+ )
227
+ )
228
+ missing_py = [p for p in REQUIRED_PYTHON if p not in py_hits]
229
+ if missing_py:
230
+ findings.append(
231
+ Finding(
232
+ drift_class="python-version-missing",
233
+ detail=f"Python-version axis missing: {', '.join(missing_py)}",
234
+ )
235
+ )
236
+ present_os = [o for o in REQUIRED_OS if o in os_hits]
237
+ present_py = [p for p in REQUIRED_PYTHON if p in py_hits]
238
+ actual_cells = len(present_os) * len(present_py)
239
+ if actual_cells != EXPECTED_CELL_COUNT:
240
+ findings.append(
241
+ Finding(
242
+ drift_class="matrix-cell-count-mismatch",
243
+ detail=(
244
+ f"expected {EXPECTED_CELL_COUNT} cells "
245
+ f"(3 OS x 4 Python); actual {actual_cells}"
246
+ ),
247
+ )
248
+ )
249
+
250
+
251
+ def check(root: Path) -> GrepResult:
252
+ """Verify the CI-matrix workflow declares the canonical 3x4 matrix."""
253
+ workflow = _find_workflow(root)
254
+ if workflow is None:
255
+ # Workflow-absent is informational tolerance: the canonical
256
+ # ci-matrix.yml may not yet be materialised. Pass cleanly with
257
+ # the informational field set so callers can distinguish.
258
+ return GrepResult(
259
+ grep=GREP_NAME,
260
+ root=str(root),
261
+ passed=True,
262
+ workflow_path=None,
263
+ not_yet_materialised=True,
264
+ findings=[],
265
+ )
266
+ content = workflow.read_text(encoding="utf-8")
267
+ findings: list[Finding] = []
268
+ parsed = _parse_yaml(content)
269
+ if parsed is not None:
270
+ _check_parsed(parsed, findings)
271
+ else:
272
+ _check_string_fallback(content, findings)
273
+ return GrepResult(
274
+ grep=GREP_NAME,
275
+ root=str(root),
276
+ passed=not findings,
277
+ workflow_path=str(workflow),
278
+ not_yet_materialised=False,
279
+ findings=findings,
280
+ )
281
+
282
+
283
+ def _read_input(argv: list[str]) -> Path:
284
+ if len(argv) >= 2:
285
+ return Path(argv[1])
286
+ return Path.cwd()
287
+
288
+
289
+ def _main(argv: list[str]) -> int:
290
+ root = _read_input(argv)
291
+ result = check(root)
292
+ print(result.to_json())
293
+ return EXIT_PASS if result.passed else EXIT_FAIL
294
+
295
+
296
+ if __name__ == "__main__":
297
+ sys.exit(_main(sys.argv))
@@ -0,0 +1,306 @@
1
+ # SPDX-License-Identifier: MIT
2
+
3
+ """Prove every command and skill surface has a deterministic output shape.
4
+
5
+ Why this enforcement exists. apothem renders option sets and terminal
6
+ next-step blocks with a strictly expected output structure: identical
7
+ inputs produce identically-shaped output, the recommended marker sits in
8
+ a fixed position, and every terminal surface closes with a named next
9
+ step. This harness is the executable proof of that determinism contract
10
+ (`rules/determinism.md`): it reduces each surface to a canonical
11
+ structural signature, recomputes the signature across repeated reads, and
12
+ asserts the signature is byte-stable. A surface whose signature drifts
13
+ between runs carries undeclared non-determinism; a surface that fails the
14
+ minimal output-shape floor is structurally incomplete. Both are findings.
15
+
16
+ The signature is the deterministic structural fingerprint of a surface —
17
+ its SPDX-header presence, frontmatter key set, ordered H2 heading
18
+ sequence, recommended-marker count, fenced-code language multiset,
19
+ bindings-section presence, and terminal next-step form. The fingerprint
20
+ is sensitive to any shape change but immune to cosmetic prose edits, so
21
+ it captures "shape" rather than "content".
22
+
23
+ Division of labour. The per-dimension semantics of the recommended
24
+ marker and the next-step block are owned by `option_annotation_grep` and
25
+ `recommend_next_step_grep` respectively; this harness owns the *composite*
26
+ shape's stability and the minimal completeness floor that makes a
27
+ perturbed surface fail. The overlap on next-step presence is deliberate
28
+ defense-in-depth and extends coverage to skill surfaces.
29
+
30
+ Detection strategy. The validator walks `src/apothem/commands/*.md`
31
+ (excluding `README.md`) and `src/apothem/skills/*/SKILL.md`. For each
32
+ surface it computes the structural signature three times from fresh reads
33
+ and compares; inequality is a non-determinism finding. It then checks the
34
+ output-shape floor: an SPDX header, at least one H2 heading, and a
35
+ terminal next-step heading (`## Recommended Next Step` or `## Next
36
+ Steps`). A missing element is an incomplete-shape finding.
37
+
38
+ Exit semantics. Exits 0 when every surface passes; exits 2 on any
39
+ finding. The exit-2 convention matches the conformity-gate orchestrator's
40
+ EXIT_FAIL constant.
41
+ """
42
+
43
+ from __future__ import annotations
44
+
45
+ import hashlib
46
+ import json
47
+ import re
48
+ import sys
49
+ from dataclasses import asdict, dataclass, field
50
+ from pathlib import Path
51
+ from typing import Final
52
+
53
+ GREP_NAME: Final[str] = "determinism-grep"
54
+ RULE_ANCHOR: Final[str] = "rules/determinism.md"
55
+
56
+ EXIT_PASS: Final[int] = 0
57
+ EXIT_FAIL: Final[int] = 2
58
+
59
+ # Surface roots relative to the repository root supplied at invocation.
60
+ COMMANDS_DIR: Final[str] = "src/apothem/commands"
61
+ SKILLS_DIR: Final[str] = "src/apothem/skills"
62
+
63
+ # Filenames excluded from the command sweep — folder-companion docs, not
64
+ # commands: the human-facing directory index and the agent-facing companion.
65
+ EXCLUDED_FILES: Final[frozenset[str]] = frozenset({"README.md", "AGENTS.md"})
66
+
67
+ # Number of independent signature recomputations per surface. Identical
68
+ # inputs must yield identical signatures across every recomputation.
69
+ RECOMPUTE_RUNS: Final[int] = 3
70
+
71
+ _SPDX_RE: Final[re.Pattern[str]] = re.compile(r"SPDX-License-Identifier:\s*MIT")
72
+ _H2_RE: Final[re.Pattern[str]] = re.compile(r"^##\s+(?P<text>\S.*?)\s*$")
73
+ _FRONTMATTER_KEY_RE: Final[re.Pattern[str]] = re.compile(r"^(?P<key>[A-Za-z0-9_-]+):")
74
+ _FENCE_RE: Final[re.Pattern[str]] = re.compile(r"^```(?P<lang>[A-Za-z0-9_+-]*)")
75
+ _RECOMMENDED_MARKER_RE: Final[re.Pattern[str]] = re.compile(r"\*\*Recommended\*\*")
76
+ _RECOMMENDED_POSTFIX_RE: Final[re.Pattern[str]] = re.compile(r"\(Recommended\)")
77
+
78
+ _NEXTSTEP_SINGULAR_RE: Final[re.Pattern[str]] = re.compile(
79
+ r"^##\s+Recommended\s+Next\s+Step\s*$"
80
+ )
81
+ _NEXTSTEP_MULTI_RE: Final[re.Pattern[str]] = re.compile(r"^##\s+Next\s+Steps\s*$")
82
+ _BINDINGS_RE: Final[re.Pattern[str]] = re.compile(r"^##\s+Bindings\b")
83
+
84
+
85
+ @dataclass(frozen=True)
86
+ class Finding:
87
+ """One surface that violates the determinism / output-shape contract."""
88
+
89
+ surface: str
90
+ kind: str
91
+ detail: str
92
+ rule: str = RULE_ANCHOR
93
+
94
+
95
+ @dataclass(frozen=True)
96
+ class GrepResult:
97
+ """Aggregated sweep result across every command and skill surface."""
98
+
99
+ grep: str
100
+ root: str
101
+ files_inspected: int
102
+ passed: bool
103
+ findings: list[Finding] = field(default_factory=list)
104
+
105
+ def to_json(self) -> str:
106
+ payload = {
107
+ "grep": self.grep,
108
+ "root": self.root,
109
+ "files_inspected": self.files_inspected,
110
+ "passed": self.passed,
111
+ "findings": [asdict(f) for f in self.findings],
112
+ }
113
+ return json.dumps(payload, indent=2)
114
+
115
+
116
+ def _frontmatter_keys(lines: list[str]) -> list[str]:
117
+ """Return the sorted key set of a leading YAML frontmatter block.
118
+
119
+ The block is delimited by `---` on the first non-empty line and the
120
+ next `---`. Surfaces without frontmatter return an empty list.
121
+ """
122
+ start = next((i for i, ln in enumerate(lines) if ln.strip()), None)
123
+ if start is None or lines[start].strip() != "---":
124
+ return []
125
+ keys: list[str] = []
126
+ for line in lines[start + 1 :]:
127
+ if line.strip() == "---":
128
+ break
129
+ match = _FRONTMATTER_KEY_RE.match(line)
130
+ if match:
131
+ keys.append(match.group("key"))
132
+ return sorted(keys)
133
+
134
+
135
+ def _fenced_languages(lines: list[str]) -> list[str]:
136
+ """Return the sorted multiset of fenced-code language tags.
137
+
138
+ Only opening fences are counted: every second fence in a file is a
139
+ close. Untagged fences contribute an empty-string entry.
140
+ """
141
+ langs: list[str] = []
142
+ in_fence = False
143
+ for line in lines:
144
+ match = _FENCE_RE.match(line)
145
+ if match is None:
146
+ continue
147
+ if not in_fence:
148
+ langs.append(match.group("lang"))
149
+ in_fence = not in_fence
150
+ return sorted(langs)
151
+
152
+
153
+ def _terminal_nextstep_form(lines: list[str]) -> str:
154
+ """Return the terminal next-step form: 'singular', 'multi', or 'absent'."""
155
+ for line in lines:
156
+ if _NEXTSTEP_SINGULAR_RE.match(line):
157
+ return "singular"
158
+ if _NEXTSTEP_MULTI_RE.match(line):
159
+ return "multi"
160
+ return "absent"
161
+
162
+
163
+ def _structural_shape(body: str) -> dict[str, object]:
164
+ """Reduce a surface to its canonical, order-stable structural shape."""
165
+ lines = body.splitlines()
166
+ h2_sequence = [m.group("text") for ln in lines if (m := _H2_RE.match(ln))]
167
+ return {
168
+ "spdx": bool(_SPDX_RE.search(body)),
169
+ "frontmatter_keys": _frontmatter_keys(lines),
170
+ "h2_sequence": h2_sequence,
171
+ "recommended_marker_count": len(_RECOMMENDED_MARKER_RE.findall(body)),
172
+ "recommended_postfix_count": len(_RECOMMENDED_POSTFIX_RE.findall(body)),
173
+ "fenced_languages": _fenced_languages(lines),
174
+ "bindings_present": any(_BINDINGS_RE.match(ln) for ln in lines),
175
+ "nextstep_form": _terminal_nextstep_form(lines),
176
+ }
177
+
178
+
179
+ def _signature(body: str) -> str:
180
+ """Compute the deterministic structural fingerprint of a surface."""
181
+ canonical = json.dumps(_structural_shape(body), sort_keys=True)
182
+ return hashlib.sha256(canonical.encode("utf-8")).hexdigest()
183
+
184
+
185
+ def _classify_surface(path: Path) -> list[Finding]:
186
+ """Return findings for one surface — non-determinism + shape floor."""
187
+ surface = str(path)
188
+ findings: list[Finding] = []
189
+
190
+ signatures: list[str] = []
191
+ bodies: list[str] = []
192
+ for _ in range(RECOMPUTE_RUNS):
193
+ try:
194
+ body = path.read_text(encoding="utf-8")
195
+ except (OSError, UnicodeDecodeError) as exc:
196
+ return [
197
+ Finding(
198
+ surface=surface,
199
+ kind="unreadable",
200
+ detail=f"could not read surface: {exc}",
201
+ )
202
+ ]
203
+ bodies.append(body)
204
+ signatures.append(_signature(body))
205
+
206
+ if len(set(signatures)) != 1:
207
+ findings.append(
208
+ Finding(
209
+ surface=surface,
210
+ kind="non-deterministic-signature",
211
+ detail=(
212
+ f"structural signature drifted across {RECOMPUTE_RUNS} reads "
213
+ f"({sorted(set(signatures))}); identical input must yield an "
214
+ "identically-shaped output or declare its non-determinism source"
215
+ ),
216
+ )
217
+ )
218
+
219
+ shape = _structural_shape(bodies[0])
220
+ if not shape["spdx"]:
221
+ findings.append(
222
+ Finding(
223
+ surface=surface,
224
+ kind="incomplete-shape",
225
+ detail="no SPDX-License-Identifier header found",
226
+ )
227
+ )
228
+ if not shape["h2_sequence"]:
229
+ findings.append(
230
+ Finding(
231
+ surface=surface,
232
+ kind="incomplete-shape",
233
+ detail="no H2 section heading found; the surface has no structure",
234
+ )
235
+ )
236
+ if shape["nextstep_form"] == "absent":
237
+ findings.append(
238
+ Finding(
239
+ surface=surface,
240
+ kind="incomplete-shape",
241
+ detail=(
242
+ "no terminal `## Recommended Next Step` or `## Next Steps` "
243
+ "heading; every terminal surface closes with a named next step"
244
+ ),
245
+ )
246
+ )
247
+ return findings
248
+
249
+
250
+ def _iter_surfaces(root: Path) -> list[Path]:
251
+ """Return the sorted command + skill surface set under root."""
252
+ surfaces: list[Path] = []
253
+ commands_dir = root / COMMANDS_DIR
254
+ if commands_dir.is_dir():
255
+ surfaces.extend(
256
+ p for p in commands_dir.glob("*.md") if p.name not in EXCLUDED_FILES
257
+ )
258
+ skills_dir = root / SKILLS_DIR
259
+ if skills_dir.is_dir():
260
+ surfaces.extend(skills_dir.glob("*/SKILL.md"))
261
+ return sorted(surfaces)
262
+
263
+
264
+ def check(root: Path) -> GrepResult:
265
+ """Sweep every command and skill surface for a deterministic shape."""
266
+ findings: list[Finding] = []
267
+ surfaces = _iter_surfaces(root)
268
+ missing: list[str] = []
269
+ if not (root / COMMANDS_DIR).is_dir():
270
+ missing.append(COMMANDS_DIR)
271
+ if not (root / SKILLS_DIR).is_dir():
272
+ missing.append(SKILLS_DIR)
273
+ for rel in missing:
274
+ findings.append(
275
+ Finding(
276
+ surface=rel,
277
+ kind="missing-surface-root",
278
+ detail=f"surface directory absent at canonical path {root / rel}",
279
+ )
280
+ )
281
+ for path in surfaces:
282
+ findings.extend(_classify_surface(path))
283
+ return GrepResult(
284
+ grep=GREP_NAME,
285
+ root=str(root),
286
+ files_inspected=len(surfaces),
287
+ passed=not findings,
288
+ findings=findings,
289
+ )
290
+
291
+
292
+ def _read_input(argv: list[str]) -> Path:
293
+ if len(argv) >= 2:
294
+ return Path(argv[1])
295
+ return Path.cwd()
296
+
297
+
298
+ def _main(argv: list[str]) -> int:
299
+ root = _read_input(argv)
300
+ result = check(root)
301
+ print(result.to_json())
302
+ return EXIT_PASS if result.passed else EXIT_FAIL
303
+
304
+
305
+ if __name__ == "__main__":
306
+ sys.exit(_main(sys.argv))