@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,657 @@
1
+ # SPDX-License-Identifier: MIT
2
+
3
+ """Walk an ecosystem root and produce a machine-readable inventory.
4
+
5
+ Why this tool exists. Subsequent audit work (drift detection, header
6
+ coverage, multi-surface conventions, validator authoring) needs a single
7
+ authoritative description of every file in the working tree: its path,
8
+ size, content hash, line count, ecosystem-class, and authorship-banner
9
+ status. Without that one source of truth, each consumer would re-walk
10
+ the tree with subtly different filters and produce drift between scans.
11
+ This tool emits ``inventory.json`` once; every downstream pass reads it.
12
+
13
+ What the inventory captures. Per file: ``path`` (relative to root),
14
+ ``size`` (bytes), ``mtime`` (ISO 8601 UTC), ``sha256`` (hex digest),
15
+ ``line-count`` (text files only — None for binaries), ``class`` (one of
16
+ the canonical thirteen classes below), ``header-status`` (
17
+ ``present-canonical`` / ``present-malformed`` / ``absent`` /
18
+ ``not-applicable``), and ``header-variant`` (the comment-syntax family
19
+ the file's authorship banner would use, when applicable).
20
+
21
+ What the inventory excludes. Version-control internals (``.git``),
22
+ Python bytecode caches (``__pycache__``), virtual environments
23
+ (``.venv`` / ``venv`` / ``env`` / ``.tox``), node modules, and the
24
+ audit-output directory itself (``.audit``). The exclusion list is
25
+ deliberate: these are derivative state, not source artifacts, and
26
+ including them would inflate aggregate counts without informing any
27
+ downstream consumer.
28
+ """
29
+
30
+ from __future__ import annotations
31
+
32
+ import argparse
33
+ import hashlib
34
+ import json
35
+ import sys
36
+ from collections.abc import Iterator
37
+ from dataclasses import dataclass, field
38
+ from datetime import datetime, timezone
39
+ from pathlib import Path
40
+ from typing import Final
41
+
42
+ # Thirteen canonical ecosystem classes. The classification matrix at the
43
+ # next audit phase refines these verdicts; this pass assigns one class per
44
+ # file via path-prefix lookup with extension-based fallbacks.
45
+ CLASS_MEMORY: Final[str] = "memory"
46
+ CLASS_AGENT: Final[str] = "agent"
47
+ CLASS_COMMAND: Final[str] = "command"
48
+ CLASS_SKILL: Final[str] = "skill"
49
+ CLASS_HOOK: Final[str] = "hook"
50
+ CLASS_OUTPUT_STYLE: Final[str] = "output-style"
51
+ CLASS_STATUSLINE: Final[str] = "statusline"
52
+ CLASS_SETTINGS: Final[str] = "settings"
53
+ CLASS_MCP: Final[str] = "mcp"
54
+ CLASS_DOCS: Final[str] = "docs"
55
+ CLASS_SCAFFOLDING: Final[str] = "scaffolding"
56
+ CLASS_PLAN_ARTIFACT: Final[str] = "plan-artifact"
57
+ CLASS_UNKNOWN: Final[str] = "unknown"
58
+
59
+ ALL_CLASSES: Final[tuple[str, ...]] = (
60
+ CLASS_MEMORY,
61
+ CLASS_AGENT,
62
+ CLASS_COMMAND,
63
+ CLASS_SKILL,
64
+ CLASS_HOOK,
65
+ CLASS_OUTPUT_STYLE,
66
+ CLASS_STATUSLINE,
67
+ CLASS_SETTINGS,
68
+ CLASS_MCP,
69
+ CLASS_DOCS,
70
+ CLASS_SCAFFOLDING,
71
+ CLASS_PLAN_ARTIFACT,
72
+ CLASS_UNKNOWN,
73
+ )
74
+
75
+ # Header-status taxonomy. The four values are mutually exclusive per file.
76
+ HEADER_PRESENT_CANONICAL: Final[str] = "present-canonical"
77
+ HEADER_PRESENT_MALFORMED: Final[str] = "present-malformed"
78
+ HEADER_ABSENT: Final[str] = "absent"
79
+ HEADER_NOT_APPLICABLE: Final[str] = "not-applicable"
80
+
81
+ ALL_HEADER_STATUSES: Final[tuple[str, ...]] = (
82
+ HEADER_PRESENT_CANONICAL,
83
+ HEADER_PRESENT_MALFORMED,
84
+ HEADER_ABSENT,
85
+ HEADER_NOT_APPLICABLE,
86
+ )
87
+
88
+ # Path-prefix to class mapping. The ordering is irrelevant — every key is
89
+ # matched against the file's relative-path top-level segment. Files outside
90
+ # any registered prefix fall through to extension-based classification.
91
+ PATH_PREFIX_CLASSES: Final[dict[str, str]] = {
92
+ "memory": CLASS_MEMORY,
93
+ "projects": CLASS_MEMORY,
94
+ "agents": CLASS_AGENT,
95
+ "commands": CLASS_COMMAND,
96
+ "skills": CLASS_SKILL,
97
+ "hooks": CLASS_HOOK,
98
+ "output-styles": CLASS_OUTPUT_STYLE,
99
+ "statusline": CLASS_STATUSLINE,
100
+ "statuslines": CLASS_STATUSLINE,
101
+ "settings": CLASS_SETTINGS,
102
+ "mcp": CLASS_MCP,
103
+ "docs": CLASS_DOCS,
104
+ "rules": CLASS_DOCS,
105
+ "examples": CLASS_DOCS,
106
+ "schemas": CLASS_SCAFFOLDING,
107
+ "scripts": CLASS_SCAFFOLDING,
108
+ "tests": CLASS_SCAFFOLDING,
109
+ "templates": CLASS_SCAFFOLDING,
110
+ "assets": CLASS_SCAFFOLDING,
111
+ ".github": CLASS_SCAFFOLDING,
112
+ "packaging": CLASS_SCAFFOLDING,
113
+ ".plans": CLASS_PLAN_ARTIFACT,
114
+ }
115
+
116
+ # Settings files at the working-tree root carry the settings class regardless
117
+ # of the absent ``settings/`` directory. The Claude-Code installable example
118
+ # (``settings.json``) lives under
119
+ # ``examples/harnesses/claude-code/native-install/`` and is classified via the
120
+ # ``examples`` path-prefix entry, not by root-filename.
121
+ ROOT_SETTINGS_FILES: Final[frozenset[str]] = frozenset(
122
+ {
123
+ ".mcp.json",
124
+ ".credentials.json",
125
+ }
126
+ )
127
+
128
+ # Files at the working-tree root that contribute to the scaffolding class.
129
+ # Covers the build-runner, lint configs, line-ending policy, install/update/
130
+ # uninstall scripts, version pin, secret-scan config, license header policy,
131
+ # and contributor mapping that any modern dotfiles repository carries at root.
132
+ ROOT_SCAFFOLDING_FILES: Final[frozenset[str]] = frozenset(
133
+ {
134
+ "Makefile",
135
+ "VERSION",
136
+ "pyproject.toml",
137
+ ".gitignore",
138
+ ".gitattributes",
139
+ ".editorconfig",
140
+ ".gitleaks.toml",
141
+ ".gitmessage",
142
+ ".licenserc.yaml",
143
+ ".mailmap",
144
+ ".markdownlint.json",
145
+ ".markdownlint.jsonc",
146
+ ".markdownlint-cli2.jsonc",
147
+ ".prettierrc",
148
+ ".prettierignore",
149
+ ".shellcheckrc",
150
+ ".pre-commit-config.yaml",
151
+ "scripts/installer/install.sh",
152
+ "scripts/installer/install.ps1",
153
+ "scripts/installer/update.sh",
154
+ "scripts/installer/update.ps1",
155
+ "scripts/installer/uninstall.sh",
156
+ "scripts/installer/uninstall.ps1",
157
+ }
158
+ )
159
+
160
+ # Documentation files at the working-tree root.
161
+ ROOT_DOCS_FILES: Final[frozenset[str]] = frozenset(
162
+ {
163
+ "CLAUDE.md",
164
+ "README.md",
165
+ "LICENSE",
166
+ "CHANGELOG.md",
167
+ "CONTRIBUTING.md",
168
+ "CODE_OF_CONDUCT.md",
169
+ "SECURITY.md",
170
+ "AUTHORS",
171
+ ".mcp.json.notes.md",
172
+ }
173
+ )
174
+
175
+ # Directory names skipped during the walk. These are derivative state, not
176
+ # source artifacts. The set covers four distinct origins: version-control
177
+ # internals (``.git``), language-tooling caches (``__pycache__``,
178
+ # ``.pytest_cache``, ``.mypy_cache``, ``.ruff_cache``, ``node_modules``,
179
+ # ``.tox``), virtual environments (``.venv``, ``venv``, ``env``,
180
+ # ``apothem.egg-info``), and Claude Code harness-managed state
181
+ # (``file-history``, ``debug``, ``todos``, ``shell-snapshots``, ``ide``,
182
+ # ``sessions``, ``session-env``, ``runtime-data``, ``cache``, ``downloads``,
183
+ # ``backups``, ``statsig``, ``telemetry``, ``plugins``). The audit-output
184
+ # directory itself (``.audit``) is also skipped to keep self-reference out
185
+ # of the inventory.
186
+ SKIPPED_DIRS: Final[frozenset[str]] = frozenset(
187
+ {
188
+ # Version-control internals.
189
+ ".git",
190
+ # Language-tooling caches.
191
+ "__pycache__",
192
+ ".pytest_cache",
193
+ ".mypy_cache",
194
+ ".ruff_cache",
195
+ ".tox",
196
+ "node_modules",
197
+ # Virtual environments and Python build artifacts.
198
+ ".venv",
199
+ "venv",
200
+ "env",
201
+ "apothem.egg-info",
202
+ # Audit output (self-reference).
203
+ ".audit",
204
+ # IDE workspace state.
205
+ ".vscode",
206
+ ".idea",
207
+ # Claude Code harness-managed state.
208
+ "file-history",
209
+ "debug",
210
+ "todos",
211
+ "shell-snapshots",
212
+ "ide",
213
+ "sessions",
214
+ "session-env",
215
+ "runtime-data",
216
+ "cache",
217
+ "downloads",
218
+ "backups",
219
+ "statsig",
220
+ "telemetry",
221
+ "plugins",
222
+ }
223
+ )
224
+
225
+ # File extensions treated as binary. The header-status check skips these and
226
+ # the line-count returns None.
227
+ BINARY_EXTENSIONS: Final[frozenset[str]] = frozenset(
228
+ {
229
+ ".png",
230
+ ".jpg",
231
+ ".jpeg",
232
+ ".gif",
233
+ ".ico",
234
+ ".webp",
235
+ ".pdf",
236
+ ".zip",
237
+ ".tar",
238
+ ".gz",
239
+ ".tgz",
240
+ ".7z",
241
+ ".rar",
242
+ ".exe",
243
+ ".dll",
244
+ ".so",
245
+ ".dylib",
246
+ ".bin",
247
+ ".pyc",
248
+ ".pyo",
249
+ ".woff",
250
+ ".woff2",
251
+ ".ttf",
252
+ ".otf",
253
+ ".eot",
254
+ ".mp3",
255
+ ".mp4",
256
+ ".webm",
257
+ ".mov",
258
+ ".avi",
259
+ }
260
+ )
261
+
262
+ # Lockfiles, generated assets, and vendored data are all not-applicable for
263
+ # the authorship banner. Adding a banner to JSON / lockfiles would break
264
+ # parsing; vendored content carries upstream attribution and must not be
265
+ # overwritten.
266
+ NOT_APPLICABLE_EXTENSIONS: Final[frozenset[str]] = frozenset(
267
+ {
268
+ ".json",
269
+ ".lock",
270
+ ".min.js",
271
+ ".min.css",
272
+ }
273
+ )
274
+
275
+ # Filename patterns that mark a file as not-applicable for banner injection.
276
+ NOT_APPLICABLE_NAMES: Final[frozenset[str]] = frozenset(
277
+ {
278
+ "LICENSE",
279
+ "VERSION",
280
+ "go.sum",
281
+ "package-lock.json",
282
+ "yarn.lock",
283
+ "pnpm-lock.yaml",
284
+ "Pipfile.lock",
285
+ "poetry.lock",
286
+ "uv.lock",
287
+ }
288
+ )
289
+
290
+ # Header-variant family per filetype. The variants enumerate which comment
291
+ # syntax the canonical 5-line authorship banner uses on this filetype. The
292
+ # values are intent labels; the actual banner bytes live in the
293
+ # ``src/apothem/schemas/authorship-header.txt`` fixture authored at the next phase.
294
+ HEADER_VARIANT_HASH: Final[str] = "hash" # `# ...` line-comments
295
+ HEADER_VARIANT_DOUBLE_SLASH: Final[str] = "double-slash" # `// ...`
296
+ HEADER_VARIANT_HTML: Final[str] = "html" # `<!-- ... -->`
297
+ HEADER_VARIANT_C_BLOCK: Final[str] = "c-block" # `/* ... */`
298
+ HEADER_VARIANT_SEMICOLON: Final[str] = "semicolon" # `; ...`
299
+ HEADER_VARIANT_DOUBLE_DASH: Final[str] = "double-dash" # `-- ...`
300
+ HEADER_VARIANT_NONE: Final[str] = "none" # not-applicable
301
+
302
+ EXTENSION_VARIANT: Final[dict[str, str]] = {
303
+ ".py": HEADER_VARIANT_HASH,
304
+ ".sh": HEADER_VARIANT_HASH,
305
+ ".bash": HEADER_VARIANT_HASH,
306
+ ".zsh": HEADER_VARIANT_HASH,
307
+ ".ps1": HEADER_VARIANT_HASH,
308
+ ".psm1": HEADER_VARIANT_HASH,
309
+ ".psd1": HEADER_VARIANT_HASH,
310
+ ".yaml": HEADER_VARIANT_HASH,
311
+ ".yml": HEADER_VARIANT_HASH,
312
+ ".toml": HEADER_VARIANT_HASH,
313
+ ".ini": HEADER_VARIANT_SEMICOLON,
314
+ ".cfg": HEADER_VARIANT_HASH,
315
+ ".conf": HEADER_VARIANT_HASH,
316
+ ".md": HEADER_VARIANT_HTML,
317
+ ".markdown": HEADER_VARIANT_HTML,
318
+ ".html": HEADER_VARIANT_HTML,
319
+ ".htm": HEADER_VARIANT_HTML,
320
+ ".xml": HEADER_VARIANT_HTML,
321
+ ".svg": HEADER_VARIANT_HTML,
322
+ ".js": HEADER_VARIANT_DOUBLE_SLASH,
323
+ ".ts": HEADER_VARIANT_DOUBLE_SLASH,
324
+ ".jsx": HEADER_VARIANT_DOUBLE_SLASH,
325
+ ".tsx": HEADER_VARIANT_DOUBLE_SLASH,
326
+ ".java": HEADER_VARIANT_DOUBLE_SLASH,
327
+ ".go": HEADER_VARIANT_DOUBLE_SLASH,
328
+ ".rs": HEADER_VARIANT_DOUBLE_SLASH,
329
+ ".swift": HEADER_VARIANT_DOUBLE_SLASH,
330
+ ".kt": HEADER_VARIANT_DOUBLE_SLASH,
331
+ ".c": HEADER_VARIANT_C_BLOCK,
332
+ ".cpp": HEADER_VARIANT_C_BLOCK,
333
+ ".h": HEADER_VARIANT_C_BLOCK,
334
+ ".hpp": HEADER_VARIANT_C_BLOCK,
335
+ ".css": HEADER_VARIANT_C_BLOCK,
336
+ ".scss": HEADER_VARIANT_C_BLOCK,
337
+ ".sql": HEADER_VARIANT_DOUBLE_DASH,
338
+ ".lua": HEADER_VARIANT_DOUBLE_DASH,
339
+ ".hs": HEADER_VARIANT_DOUBLE_DASH,
340
+ }
341
+
342
+ # Banner heuristic markers. The narrowed canonical header is the single
343
+ # ``SPDX-License-Identifier:`` line; its presence near the file head marks
344
+ # ``present-canonical``. The retired branded-banner author mark is retained
345
+ # as a legacy signal: a file still carrying it (but not the SPDX line) is a
346
+ # not-yet-narrowed header and counts ``present-malformed``. This stays a
347
+ # coarse sizing heuristic; the per-file authoritative verdict is recomputed
348
+ # downstream by the header-coverage scanner.
349
+ SPDX_PREFIX_TEXT: Final[str] = "SPDX-License-Identifier:"
350
+ LEGACY_AUTHOR_MARK: Final[str] = "Copyright (c) Ahmed G. Gad"
351
+
352
+ # Number of leading lines scanned for banner presence. Forty lines covers
353
+ # every shebang + interpreter-pragma + copyright-block prelude shape the
354
+ # canonical filetype variants emit.
355
+ BANNER_SCAN_LINE_BUDGET: Final[int] = 40
356
+
357
+ # Buffer size for SHA-256 streaming digest. Sized to balance syscall count
358
+ # against memory residency for the small-to-medium file sizes typical in
359
+ # this ecosystem.
360
+ SHA256_BUFFER_BYTES: Final[int] = 65_536
361
+
362
+ EXIT_OK: Final[int] = 0
363
+ EXIT_ERROR: Final[int] = 1
364
+
365
+
366
+ @dataclass(slots=True)
367
+ class FileRecord:
368
+ """Per-file inventory row written to ``inventory.json``."""
369
+
370
+ path: str
371
+ size: int
372
+ mtime: str
373
+ sha256: str
374
+ line_count: int | None
375
+ file_class: str
376
+ header_status: str
377
+ header_variant: str
378
+
379
+ def to_json(self) -> dict[str, object]:
380
+ """Render the record as the JSON-friendly dict the inventory emits."""
381
+ return {
382
+ "path": self.path,
383
+ "size": self.size,
384
+ "mtime": self.mtime,
385
+ "sha256": self.sha256,
386
+ "line-count": self.line_count,
387
+ "class": self.file_class,
388
+ "header-status": self.header_status,
389
+ "header-variant": self.header_variant,
390
+ }
391
+
392
+
393
+ @dataclass(slots=True)
394
+ class InventoryStats:
395
+ """Aggregate counts emitted alongside the per-file array."""
396
+
397
+ total_files: int = 0
398
+ by_class: dict[str, int] = field(default_factory=dict)
399
+ by_header_status: dict[str, int] = field(default_factory=dict)
400
+ by_header_variant: dict[str, int] = field(default_factory=dict)
401
+ skipped_directories: list[str] = field(default_factory=list)
402
+
403
+
404
+ def classify_file(relative_path: Path) -> str:
405
+ """Resolve the ecosystem-class for a file given its path relative to root.
406
+
407
+ Path-prefix lookup is the primary signal; root-level filename matches
408
+ cover the scaffolding / docs / settings cases at the working-tree root
409
+ where no enclosing directory carries a registered prefix.
410
+ """
411
+ parts = relative_path.parts
412
+ if not parts:
413
+ return CLASS_UNKNOWN
414
+
415
+ if len(parts) == 1:
416
+ name = parts[0]
417
+ if name in ROOT_SETTINGS_FILES:
418
+ return CLASS_SETTINGS
419
+ if name in ROOT_SCAFFOLDING_FILES:
420
+ return CLASS_SCAFFOLDING
421
+ if name in ROOT_DOCS_FILES:
422
+ return CLASS_DOCS
423
+ return CLASS_UNKNOWN
424
+
425
+ top = parts[0]
426
+ if top in PATH_PREFIX_CLASSES:
427
+ return PATH_PREFIX_CLASSES[top]
428
+
429
+ return CLASS_UNKNOWN
430
+
431
+
432
+ def header_variant_for(relative_path: Path) -> str:
433
+ """Resolve the comment-syntax variant the canonical banner would use."""
434
+ name = relative_path.name
435
+ if name in NOT_APPLICABLE_NAMES:
436
+ return HEADER_VARIANT_NONE
437
+
438
+ suffix = relative_path.suffix.lower()
439
+ if suffix in NOT_APPLICABLE_EXTENSIONS:
440
+ return HEADER_VARIANT_NONE
441
+ if suffix in BINARY_EXTENSIONS:
442
+ return HEADER_VARIANT_NONE
443
+
444
+ return EXTENSION_VARIANT.get(suffix, HEADER_VARIANT_NONE)
445
+
446
+
447
+ def is_binary_file(relative_path: Path) -> bool:
448
+ """Treat the file as binary based on its extension.
449
+
450
+ Conservative — when the suffix is not a known binary extension and the
451
+ file is otherwise text-shaped (carries a banner-eligible variant or
452
+ falls outside the binary set entirely), the file is read as text.
453
+ """
454
+ return relative_path.suffix.lower() in BINARY_EXTENSIONS
455
+
456
+
457
+ def compute_sha256(absolute_path: Path) -> str:
458
+ """Stream the file through SHA-256 and return the lowercase hex digest."""
459
+ digest = hashlib.sha256()
460
+ with absolute_path.open("rb") as handle:
461
+ while chunk := handle.read(SHA256_BUFFER_BYTES):
462
+ digest.update(chunk)
463
+ return digest.hexdigest()
464
+
465
+
466
+ def count_lines(absolute_path: Path) -> int | None:
467
+ """Count newline-terminated lines; return None when the file is binary."""
468
+ try:
469
+ with absolute_path.open("rb") as handle:
470
+ count = 0
471
+ for _ in handle:
472
+ count += 1
473
+ return count
474
+ except OSError:
475
+ return None
476
+
477
+
478
+ def scan_banner(absolute_path: Path, variant: str) -> str:
479
+ """Inspect the file's leading lines for the canonical banner pattern.
480
+
481
+ The scan returns one of the four header-status values. Files whose
482
+ variant resolves to ``none`` (binary, lockfile, vendored, generated)
483
+ short-circuit to ``not-applicable``. Otherwise the leading scan-line
484
+ budget is read as text; the count of distinct banner marks observed
485
+ determines the verdict.
486
+ """
487
+ if variant == HEADER_VARIANT_NONE:
488
+ return HEADER_NOT_APPLICABLE
489
+
490
+ try:
491
+ with absolute_path.open("r", encoding="utf-8", errors="replace") as handle:
492
+ head_lines: list[str] = []
493
+ for _, line in zip(range(BANNER_SCAN_LINE_BUDGET), handle, strict=False):
494
+ head_lines.append(line)
495
+ except OSError:
496
+ return HEADER_ABSENT
497
+
498
+ head_text = "".join(head_lines)
499
+
500
+ if SPDX_PREFIX_TEXT in head_text:
501
+ return HEADER_PRESENT_CANONICAL
502
+ if LEGACY_AUTHOR_MARK in head_text:
503
+ return HEADER_PRESENT_MALFORMED
504
+ return HEADER_ABSENT
505
+
506
+
507
+ def walk_root(root: Path) -> tuple[list[FileRecord], list[str]]:
508
+ """Walk the working tree and emit a record per file plus the skipped dirs."""
509
+ records: list[FileRecord] = []
510
+ skipped: list[str] = []
511
+
512
+ for current, dirnames, filenames in _ordered_walk(root):
513
+ original = list(dirnames)
514
+ dirnames[:] = [name for name in dirnames if name not in SKIPPED_DIRS]
515
+ for removed in original:
516
+ if removed in SKIPPED_DIRS:
517
+ rel = (current / removed).relative_to(root).as_posix()
518
+ skipped.append(rel)
519
+
520
+ for name in sorted(filenames):
521
+ absolute = current / name
522
+ if not absolute.is_file():
523
+ continue
524
+ try:
525
+ relative = absolute.relative_to(root)
526
+ except ValueError:
527
+ continue
528
+
529
+ try:
530
+ stat = absolute.stat()
531
+ except OSError:
532
+ continue
533
+
534
+ file_class = classify_file(relative)
535
+ variant = header_variant_for(relative)
536
+ try:
537
+ sha256 = compute_sha256(absolute)
538
+ except OSError:
539
+ continue
540
+
541
+ line_count = None if is_binary_file(relative) else count_lines(absolute)
542
+ header_status = scan_banner(absolute, variant)
543
+ mtime = datetime.fromtimestamp(stat.st_mtime, tz=timezone.utc).isoformat()
544
+
545
+ records.append(
546
+ FileRecord(
547
+ path=relative.as_posix(),
548
+ size=stat.st_size,
549
+ mtime=mtime,
550
+ sha256=sha256,
551
+ line_count=line_count,
552
+ file_class=file_class,
553
+ header_status=header_status,
554
+ header_variant=variant,
555
+ )
556
+ )
557
+
558
+ return records, skipped
559
+
560
+
561
+ def _ordered_walk(
562
+ root: Path,
563
+ ) -> Iterator[tuple[Path, list[str], list[str]]]:
564
+ """Deterministic os.walk-equivalent — sorts directory names in place."""
565
+ import os
566
+
567
+ for current, dirnames, filenames in os.walk(root):
568
+ dirnames.sort()
569
+ yield Path(current), dirnames, filenames
570
+
571
+
572
+ def aggregate_stats(records: list[FileRecord], skipped: list[str]) -> InventoryStats:
573
+ """Build the stats block emitted alongside the per-file array."""
574
+ stats = InventoryStats(skipped_directories=sorted(set(skipped)))
575
+ stats.total_files = len(records)
576
+ for cls in ALL_CLASSES:
577
+ stats.by_class[cls] = 0
578
+ for status in ALL_HEADER_STATUSES:
579
+ stats.by_header_status[status] = 0
580
+ for record in records:
581
+ stats.by_class[record.file_class] = stats.by_class.get(record.file_class, 0) + 1
582
+ stats.by_header_status[record.header_status] = (
583
+ stats.by_header_status.get(record.header_status, 0) + 1
584
+ )
585
+ stats.by_header_variant[record.header_variant] = (
586
+ stats.by_header_variant.get(record.header_variant, 0) + 1
587
+ )
588
+ return stats
589
+
590
+
591
+ def emit_inventory(
592
+ root: Path, records: list[FileRecord], stats: InventoryStats, output: Path
593
+ ) -> None:
594
+ """Serialise the inventory to ``output`` as pretty-printed JSON."""
595
+ payload = {
596
+ "root": str(root),
597
+ "generated-at": datetime.now(tz=timezone.utc).isoformat(),
598
+ "total-files": stats.total_files,
599
+ "by-class": stats.by_class,
600
+ "by-header-status": stats.by_header_status,
601
+ "by-header-variant": stats.by_header_variant,
602
+ "skipped-directories": stats.skipped_directories,
603
+ "files": [record.to_json() for record in records],
604
+ }
605
+ output.parent.mkdir(parents=True, exist_ok=True)
606
+ with output.open("w", encoding="utf-8") as handle:
607
+ json.dump(payload, handle, indent=2, sort_keys=False, ensure_ascii=False)
608
+ handle.write("\n")
609
+
610
+
611
+ def parse_arguments(argv: list[str]) -> argparse.Namespace:
612
+ """CLI surface — root and output are required."""
613
+ parser = argparse.ArgumentParser(
614
+ prog="build_inventory",
615
+ description="Walk an ecosystem root and emit inventory.json.",
616
+ )
617
+ parser.add_argument(
618
+ "--root",
619
+ type=Path,
620
+ required=True,
621
+ help="Root directory to inventory (e.g., the working tree).",
622
+ )
623
+ parser.add_argument(
624
+ "--output",
625
+ type=Path,
626
+ required=True,
627
+ help="Output JSON path (e.g., .audit/inventory.json).",
628
+ )
629
+ return parser.parse_args(argv)
630
+
631
+
632
+ def main(argv: list[str]) -> int:
633
+ """Entry point — returns the exit code."""
634
+ args = parse_arguments(argv)
635
+ root = args.root.resolve()
636
+ output = args.output.resolve()
637
+
638
+ if not root.is_dir():
639
+ print(f"error: root is not a directory: {root}", file=sys.stderr)
640
+ return EXIT_ERROR
641
+
642
+ records, skipped = walk_root(root)
643
+ stats = aggregate_stats(records, skipped)
644
+ emit_inventory(root, records, stats, output)
645
+
646
+ unknown_count = stats.by_class.get(CLASS_UNKNOWN, 0)
647
+ unknown_pct = (unknown_count / stats.total_files * 100) if stats.total_files else 0
648
+ print(
649
+ f"inventory: {stats.total_files} files; "
650
+ f"unknown={unknown_count} ({unknown_pct:.1f}%); "
651
+ f"output={output}"
652
+ )
653
+ return EXIT_OK
654
+
655
+
656
+ if __name__ == "__main__":
657
+ raise SystemExit(main(sys.argv[1:]))