@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.
- package/CHANGELOG.md +60 -0
- package/LICENSE +21 -0
- package/LICENSES/MIT.txt +18 -0
- package/LICENSES/PSF-2.0.txt +47 -0
- package/README.md +549 -0
- package/bin/README.md +37 -0
- package/bin/apothem.mjs +78 -0
- package/package.json +75 -0
- package/pyproject.toml +347 -0
- package/src/apothem/README.md +52 -0
- package/src/apothem/__init__.py +66 -0
- package/src/apothem/__main__.py +28 -0
- package/src/apothem/_vendor/.keep +0 -0
- package/src/apothem/_vendor/__init__.py +25 -0
- package/src/apothem/_vendor/attr/__init__.py +104 -0
- package/src/apothem/_vendor/attr/__init__.pyi +389 -0
- package/src/apothem/_vendor/attr/_cmp.py +160 -0
- package/src/apothem/_vendor/attr/_cmp.pyi +13 -0
- package/src/apothem/_vendor/attr/_compat.py +99 -0
- package/src/apothem/_vendor/attr/_config.py +31 -0
- package/src/apothem/_vendor/attr/_funcs.py +497 -0
- package/src/apothem/_vendor/attr/_make.py +3406 -0
- package/src/apothem/_vendor/attr/_next_gen.py +674 -0
- package/src/apothem/_vendor/attr/_typing_compat.pyi +15 -0
- package/src/apothem/_vendor/attr/_version_info.py +89 -0
- package/src/apothem/_vendor/attr/_version_info.pyi +9 -0
- package/src/apothem/_vendor/attr/converters.py +162 -0
- package/src/apothem/_vendor/attr/converters.pyi +19 -0
- package/src/apothem/_vendor/attr/exceptions.py +95 -0
- package/src/apothem/_vendor/attr/exceptions.pyi +17 -0
- package/src/apothem/_vendor/attr/filters.py +72 -0
- package/src/apothem/_vendor/attr/filters.pyi +6 -0
- package/src/apothem/_vendor/attr/py.typed +0 -0
- package/src/apothem/_vendor/attr/setters.py +79 -0
- package/src/apothem/_vendor/attr/setters.pyi +20 -0
- package/src/apothem/_vendor/attr/validators.py +750 -0
- package/src/apothem/_vendor/attr/validators.pyi +140 -0
- package/src/apothem/_vendor/attr.LICENSE +21 -0
- package/src/apothem/_vendor/attrs/__init__.py +72 -0
- package/src/apothem/_vendor/attrs/__init__.pyi +314 -0
- package/src/apothem/_vendor/attrs/converters.py +3 -0
- package/src/apothem/_vendor/attrs/exceptions.py +3 -0
- package/src/apothem/_vendor/attrs/filters.py +3 -0
- package/src/apothem/_vendor/attrs/py.typed +0 -0
- package/src/apothem/_vendor/attrs/setters.py +3 -0
- package/src/apothem/_vendor/attrs/validators.py +3 -0
- package/src/apothem/_vendor/attrs.LICENSE +21 -0
- package/src/apothem/_vendor/jsonschema/__init__.py +120 -0
- package/src/apothem/_vendor/jsonschema/__main__.py +6 -0
- package/src/apothem/_vendor/jsonschema/_format.py +546 -0
- package/src/apothem/_vendor/jsonschema/_keywords.py +449 -0
- package/src/apothem/_vendor/jsonschema/_legacy_keywords.py +449 -0
- package/src/apothem/_vendor/jsonschema/_types.py +204 -0
- package/src/apothem/_vendor/jsonschema/_typing.py +29 -0
- package/src/apothem/_vendor/jsonschema/_utils.py +355 -0
- package/src/apothem/_vendor/jsonschema/benchmarks/__init__.py +5 -0
- package/src/apothem/_vendor/jsonschema/benchmarks/const_vs_enum.py +30 -0
- package/src/apothem/_vendor/jsonschema/benchmarks/contains.py +28 -0
- package/src/apothem/_vendor/jsonschema/benchmarks/import_benchmark.py +31 -0
- package/src/apothem/_vendor/jsonschema/benchmarks/issue232/issue.json +2653 -0
- package/src/apothem/_vendor/jsonschema/benchmarks/issue232.py +25 -0
- package/src/apothem/_vendor/jsonschema/benchmarks/json_schema_test_suite.py +12 -0
- package/src/apothem/_vendor/jsonschema/benchmarks/nested_schemas.py +56 -0
- package/src/apothem/_vendor/jsonschema/benchmarks/subcomponents.py +42 -0
- package/src/apothem/_vendor/jsonschema/benchmarks/unused_registry.py +35 -0
- package/src/apothem/_vendor/jsonschema/benchmarks/useless_applicator_schemas.py +106 -0
- package/src/apothem/_vendor/jsonschema/benchmarks/useless_keywords.py +32 -0
- package/src/apothem/_vendor/jsonschema/benchmarks/validator_creation.py +14 -0
- package/src/apothem/_vendor/jsonschema/cli.py +292 -0
- package/src/apothem/_vendor/jsonschema/exceptions.py +490 -0
- package/src/apothem/_vendor/jsonschema/protocols.py +230 -0
- package/src/apothem/_vendor/jsonschema/validators.py +1410 -0
- package/src/apothem/_vendor/jsonschema.LICENSE +19 -0
- package/src/apothem/_vendor/jsonschema_specifications/__init__.py +12 -0
- package/src/apothem/_vendor/jsonschema_specifications/_core.py +38 -0
- package/src/apothem/_vendor/jsonschema_specifications/schemas/draft201909/metaschema.json +42 -0
- package/src/apothem/_vendor/jsonschema_specifications/schemas/draft201909/vocabularies/applicator +56 -0
- package/src/apothem/_vendor/jsonschema_specifications/schemas/draft201909/vocabularies/content +17 -0
- package/src/apothem/_vendor/jsonschema_specifications/schemas/draft201909/vocabularies/core +57 -0
- package/src/apothem/_vendor/jsonschema_specifications/schemas/draft201909/vocabularies/format +14 -0
- package/src/apothem/_vendor/jsonschema_specifications/schemas/draft201909/vocabularies/meta-data +37 -0
- package/src/apothem/_vendor/jsonschema_specifications/schemas/draft201909/vocabularies/validation +98 -0
- package/src/apothem/_vendor/jsonschema_specifications/schemas/draft202012/metaschema.json +58 -0
- package/src/apothem/_vendor/jsonschema_specifications/schemas/draft202012/vocabularies/applicator +48 -0
- package/src/apothem/_vendor/jsonschema_specifications/schemas/draft202012/vocabularies/content +17 -0
- package/src/apothem/_vendor/jsonschema_specifications/schemas/draft202012/vocabularies/core +51 -0
- package/src/apothem/_vendor/jsonschema_specifications/schemas/draft202012/vocabularies/format-annotation +14 -0
- package/src/apothem/_vendor/jsonschema_specifications/schemas/draft202012/vocabularies/format-assertion +14 -0
- package/src/apothem/_vendor/jsonschema_specifications/schemas/draft202012/vocabularies/meta-data +37 -0
- package/src/apothem/_vendor/jsonschema_specifications/schemas/draft202012/vocabularies/unevaluated +15 -0
- package/src/apothem/_vendor/jsonschema_specifications/schemas/draft202012/vocabularies/validation +98 -0
- package/src/apothem/_vendor/jsonschema_specifications/schemas/draft3/metaschema.json +172 -0
- package/src/apothem/_vendor/jsonschema_specifications/schemas/draft4/metaschema.json +149 -0
- package/src/apothem/_vendor/jsonschema_specifications/schemas/draft6/metaschema.json +153 -0
- package/src/apothem/_vendor/jsonschema_specifications/schemas/draft7/metaschema.json +166 -0
- package/src/apothem/_vendor/jsonschema_specifications.LICENSE +19 -0
- package/src/apothem/_vendor/referencing/__init__.py +7 -0
- package/src/apothem/_vendor/referencing/_attrs.py +31 -0
- package/src/apothem/_vendor/referencing/_attrs.pyi +21 -0
- package/src/apothem/_vendor/referencing/_core.py +739 -0
- package/src/apothem/_vendor/referencing/exceptions.py +165 -0
- package/src/apothem/_vendor/referencing/jsonschema.py +642 -0
- package/src/apothem/_vendor/referencing/py.typed +0 -0
- package/src/apothem/_vendor/referencing/retrieval.py +94 -0
- package/src/apothem/_vendor/referencing/typing.py +61 -0
- package/src/apothem/_vendor/referencing.LICENSE +19 -0
- package/src/apothem/_vendor/rpds/__init__.py +251 -0
- package/src/apothem/_vendor/typing_extensions.LICENSE +279 -0
- package/src/apothem/_vendor/typing_extensions.py +4317 -0
- package/src/apothem/_vendor/vendor.txt +22 -0
- package/src/apothem/_vendor/yaml/__init__.py +389 -0
- package/src/apothem/_vendor/yaml/composer.py +138 -0
- package/src/apothem/_vendor/yaml/constructor.py +748 -0
- package/src/apothem/_vendor/yaml/cyaml.py +100 -0
- package/src/apothem/_vendor/yaml/dumper.py +61 -0
- package/src/apothem/_vendor/yaml/emitter.py +1137 -0
- package/src/apothem/_vendor/yaml/error.py +74 -0
- package/src/apothem/_vendor/yaml/events.py +85 -0
- package/src/apothem/_vendor/yaml/loader.py +63 -0
- package/src/apothem/_vendor/yaml/nodes.py +48 -0
- package/src/apothem/_vendor/yaml/parser.py +588 -0
- package/src/apothem/_vendor/yaml/reader.py +185 -0
- package/src/apothem/_vendor/yaml/representer.py +388 -0
- package/src/apothem/_vendor/yaml/resolver.py +226 -0
- package/src/apothem/_vendor/yaml/scanner.py +1435 -0
- package/src/apothem/_vendor/yaml/serializer.py +110 -0
- package/src/apothem/_vendor/yaml/tokens.py +103 -0
- package/src/apothem/_vendor/yaml.LICENSE +20 -0
- package/src/apothem/agents/README.md +60 -0
- package/src/apothem/agents/codebase-explorer.md +91 -0
- package/src/apothem/agents/convention-auditor.md +93 -0
- package/src/apothem/agents/dependency-auditor.md +97 -0
- package/src/apothem/agents/fact-checker.md +84 -0
- package/src/apothem/agents/mcp-builder.md +86 -0
- package/src/apothem/agents/memory-auditor.md +93 -0
- package/src/apothem/agents/prompt-evaluator.md +87 -0
- package/src/apothem/agents/quality-gate.md +103 -0
- package/src/apothem/agents/refactor-surgeon.md +74 -0
- package/src/apothem/agents/research-scout.md +73 -0
- package/src/apothem/agents/security-scanner.md +83 -0
- package/src/apothem/agents/test-runner.md +84 -0
- package/src/apothem/audit/README.md +73 -0
- package/src/apothem/audit/_scan_lib.py +182 -0
- package/src/apothem/audit/analyze_graph.py +260 -0
- package/src/apothem/audit/build_capability_graph.py +607 -0
- package/src/apothem/audit/build_inventory.py +657 -0
- package/src/apothem/audit/build_plans_provenance.py +997 -0
- package/src/apothem/audit/check_links.py +389 -0
- package/src/apothem/audit/classify_artifacts.py +381 -0
- package/src/apothem/audit/deprecated-tokens.txt +10 -0
- package/src/apothem/audit/execute_plans_migration.py +491 -0
- package/src/apothem/audit/known-projects.txt +15 -0
- package/src/apothem/audit/render_capability_index.py +467 -0
- package/src/apothem/audit/render_inventory.py +405 -0
- package/src/apothem/audit/scan_ai_surfaces.py +1125 -0
- package/src/apothem/audit/scan_ai_surfaces_coarse.py +261 -0
- package/src/apothem/audit/scan_drift_features.py +143 -0
- package/src/apothem/audit/scan_frontmatter.py +293 -0
- package/src/apothem/audit/scan_header_coverage.py +1134 -0
- package/src/apothem/audit/scan_plan_leakage.py +540 -0
- package/src/apothem/audit/scan_plans_discipline.py +188 -0
- package/src/apothem/audit/scan_secrets_pii.py +245 -0
- package/src/apothem/audit/scan_stale_tokens.py +296 -0
- package/src/apothem/audit/synthesize_drift.py +205 -0
- package/src/apothem/benchmarks/README.md +33 -0
- package/src/apothem/benchmarks/__init__.py +3 -0
- package/src/apothem/benchmarks/bench_agents.py +63 -0
- package/src/apothem/benchmarks/bench_hooks.py +93 -0
- package/src/apothem/benchmarks/bench_install.py +58 -0
- package/src/apothem/benchmarks/bench_tests.py +93 -0
- package/src/apothem/benchmarks/bench_validate_ecosystem.py +84 -0
- package/src/apothem/cli/README.md +33 -0
- package/src/apothem/cli/__init__.py +229 -0
- package/src/apothem/cli/_cmd_completion.py +88 -0
- package/src/apothem/cli/_cmd_diff.py +181 -0
- package/src/apothem/cli/_cmd_doctor.py +143 -0
- package/src/apothem/cli/_cmd_harnesses.py +167 -0
- package/src/apothem/cli/_cmd_install.py +327 -0
- package/src/apothem/cli/_cmd_migrate_workspace.py +143 -0
- package/src/apothem/cli/_cmd_profile.py +341 -0
- package/src/apothem/cli/_cmd_status.py +180 -0
- package/src/apothem/cli/_cmd_uninstall.py +215 -0
- package/src/apothem/cli/_cmd_update.py +397 -0
- package/src/apothem/cli/_cmd_verify.py +194 -0
- package/src/apothem/cli/_common_flags.py +90 -0
- package/src/apothem/cli/_epilogs.py +296 -0
- package/src/apothem/cli/_helpers.py +857 -0
- package/src/apothem/cli/_json_formatter.py +21 -0
- package/src/apothem/cli/_materialize.py +376 -0
- package/src/apothem/cli/completions/apothem.bash +30 -0
- package/src/apothem/cli/completions/apothem.fish +19 -0
- package/src/apothem/cli/completions/apothem.ps1 +27 -0
- package/src/apothem/cli/completions/apothem.zsh +42 -0
- package/src/apothem/cli/reference_export.py +126 -0
- package/src/apothem/commands/README.md +125 -0
- package/src/apothem/commands/a11y-audit.md +203 -0
- package/src/apothem/commands/architecture-review.md +194 -0
- package/src/apothem/commands/audit.md +165 -0
- package/src/apothem/commands/code-audit.md +218 -0
- package/src/apothem/commands/code-review.md +193 -0
- package/src/apothem/commands/dependency-audit.md +209 -0
- package/src/apothem/commands/docs-review.md +199 -0
- package/src/apothem/commands/elevate.md +285 -0
- package/src/apothem/commands/eval.md +149 -0
- package/src/apothem/commands/fortress.md +172 -0
- package/src/apothem/commands/freshify.md +168 -0
- package/src/apothem/commands/github-deploy-fresh.md +178 -0
- package/src/apothem/commands/github-deploy-next.md +167 -0
- package/src/apothem/commands/perf-audit.md +198 -0
- package/src/apothem/commands/plan-amend.md +104 -0
- package/src/apothem/commands/plan-audit.md +127 -0
- package/src/apothem/commands/plan-design.md +257 -0
- package/src/apothem/commands/plan-execute.md +495 -0
- package/src/apothem/commands/plan-generate.md +351 -0
- package/src/apothem/commands/plan-review.md +555 -0
- package/src/apothem/commands/plan-spec.md +359 -0
- package/src/apothem/commands/plan-status.md +222 -0
- package/src/apothem/commands/plan.md +173 -0
- package/src/apothem/commands/projectify.md +142 -0
- package/src/apothem/commands/release-readiness.md +142 -0
- package/src/apothem/commands/research-analysis.md +241 -0
- package/src/apothem/commands/research-design.md +231 -0
- package/src/apothem/commands/research-disseminate.md +225 -0
- package/src/apothem/commands/research-experiment.md +232 -0
- package/src/apothem/commands/research-ideate.md +213 -0
- package/src/apothem/commands/research-paper.md +252 -0
- package/src/apothem/commands/research-proposal.md +220 -0
- package/src/apothem/commands/research-publish.md +255 -0
- package/src/apothem/commands/research-review.md +251 -0
- package/src/apothem/commands/research-sources.md +266 -0
- package/src/apothem/commands/research-spec.md +255 -0
- package/src/apothem/commands/research-synthesis.md +233 -0
- package/src/apothem/commands/research-theory.md +218 -0
- package/src/apothem/commands/research.md +181 -0
- package/src/apothem/commands/security-audit.md +196 -0
- package/src/apothem/commands/supply-chain-audit.md +192 -0
- package/src/apothem/commands/test-suite.md +146 -0
- package/src/apothem/commands/threat-model-audit.md +199 -0
- package/src/apothem/commands/ux-review.md +202 -0
- package/src/apothem/commands/workflow.md +162 -0
- package/src/apothem/conformity/README.md +173 -0
- package/src/apothem/conformity/__init__.py +1 -0
- package/src/apothem/conformity/_grep_base.py +93 -0
- package/src/apothem/conformity/agent_capability_grep.py +306 -0
- package/src/apothem/conformity/agents_md_coverage_grep.py +382 -0
- package/src/apothem/conformity/agnosticism_grep.py +311 -0
- package/src/apothem/conformity/always_on_budget_grep.py +318 -0
- package/src/apothem/conformity/bare_except_grep.py +115 -0
- package/src/apothem/conformity/binding_reciprocity_grep.py +151 -0
- package/src/apothem/conformity/brand_mark_grep.py +272 -0
- package/src/apothem/conformity/commented_out_code_grep.py +176 -0
- package/src/apothem/conformity/completion_claim_grep.py +169 -0
- package/src/apothem/conformity/conventional_commit_grep.py +319 -0
- package/src/apothem/conformity/copilot_instructions_presence_grep.py +324 -0
- package/src/apothem/conformity/cross_platform_matrix_grep.py +297 -0
- package/src/apothem/conformity/determinism_grep.py +306 -0
- package/src/apothem/conformity/diagram_staleness_grep.py +154 -0
- package/src/apothem/conformity/dynamism_grep.py +284 -0
- package/src/apothem/conformity/editorconfig_presence_grep.py +281 -0
- package/src/apothem/conformity/file_header_grep.py +502 -0
- package/src/apothem/conformity/freshness_token_grep.py +233 -0
- package/src/apothem/conformity/frontmatter_grep.py +274 -0
- package/src/apothem/conformity/frontmatter_value_grep.py +386 -0
- package/src/apothem/conformity/gate.py +1386 -0
- package/src/apothem/conformity/gitattributes_presence_grep.py +238 -0
- package/src/apothem/conformity/harden_runner_grep.py +320 -0
- package/src/apothem/conformity/hedging_grep.py +129 -0
- package/src/apothem/conformity/license_author_consistency_grep.py +204 -0
- package/src/apothem/conformity/link_check.py +327 -0
- package/src/apothem/conformity/magic_number_grep.py +182 -0
- package/src/apothem/conformity/multi_surface_coherence_grep.py +620 -0
- package/src/apothem/conformity/naming_grep.py +224 -0
- package/src/apothem/conformity/no_global_plans_grep.py +339 -0
- package/src/apothem/conformity/no_toplevel_docs_grep.py +120 -0
- package/src/apothem/conformity/oidc_trusted_publishing_grep.py +291 -0
- package/src/apothem/conformity/option_annotation_grep.py +352 -0
- package/src/apothem/conformity/orphan_output_grep.py +206 -0
- package/src/apothem/conformity/permissions_minimum_scope_grep.py +299 -0
- package/src/apothem/conformity/plain_language_grep.py +559 -0
- package/src/apothem/conformity/plan_next_step_consistency_grep.py +450 -0
- package/src/apothem/conformity/plan_suite_structure_grep.py +534 -0
- package/src/apothem/conformity/plans_discipline_language_grep.py +245 -0
- package/src/apothem/conformity/production_ready_pr_grep.py +200 -0
- package/src/apothem/conformity/recommend_next_step_grep.py +250 -0
- package/src/apothem/conformity/redundancy_grep.py +401 -0
- package/src/apothem/conformity/reference_token_grep.py +230 -0
- package/src/apothem/conformity/registry_capability_consistency_grep.py +368 -0
- package/src/apothem/conformity/secret_leak_grep.py +193 -0
- package/src/apothem/conformity/semver_stability_grep.py +358 -0
- package/src/apothem/conformity/smoke_install_grep.py +194 -0
- package/src/apothem/conformity/static_version_grep.py +284 -0
- package/src/apothem/conformity/token_efficiency_grep.py +185 -0
- package/src/apothem/conformity/unpinned_action_grep.py +115 -0
- package/src/apothem/conformity/user_confirm_grep.py +74 -0
- package/src/apothem/conformity/workflow_concurrency_grep.py +283 -0
- package/src/apothem/harnesses/README.md +63 -0
- package/src/apothem/harnesses/__init__.py +16 -0
- package/src/apothem/harnesses/_shared/README.md +36 -0
- package/src/apothem/harnesses/_shared/__init__.py +12 -0
- package/src/apothem/harnesses/_shared/install_driver.py +281 -0
- package/src/apothem/harnesses/_shared/install_driver_apply.py +612 -0
- package/src/apothem/harnesses/_shared/install_driver_backup.py +535 -0
- package/src/apothem/harnesses/_shared/install_driver_converters.py +310 -0
- package/src/apothem/harnesses/_shared/install_driver_lifecycle.py +495 -0
- package/src/apothem/harnesses/_shared/install_driver_materialize.py +675 -0
- package/src/apothem/harnesses/_shared/install_driver_merge.py +656 -0
- package/src/apothem/harnesses/_shared/install_driver_pathsafety.py +137 -0
- package/src/apothem/harnesses/_shared/install_driver_planvalidation.py +240 -0
- package/src/apothem/harnesses/_shared/install_driver_removal.py +366 -0
- package/src/apothem/harnesses/_shared/install_driver_treeops.py +248 -0
- package/src/apothem/harnesses/_shared/install_driver_types.py +330 -0
- package/src/apothem/harnesses/_shared/wrapper_factories.py +448 -0
- package/src/apothem/harnesses/antigravity/STANDARD-CONVENTION-PIN.md +91 -0
- package/src/apothem/harnesses/antigravity/__init__.py +70 -0
- package/src/apothem/harnesses/antigravity/capabilities.yml +40 -0
- package/src/apothem/harnesses/antigravity/install.py +63 -0
- package/src/apothem/harnesses/antigravity/templates/GEMINI.md +40 -0
- package/src/apothem/harnesses/antigravity/templates/plugin.json +5 -0
- package/src/apothem/harnesses/antigravity/uninstall.py +22 -0
- package/src/apothem/harnesses/antigravity/update.py +10 -0
- package/src/apothem/harnesses/antigravity/verify.py +11 -0
- package/src/apothem/harnesses/claude_code/STANDARD-CONVENTION-PIN.md +65 -0
- package/src/apothem/harnesses/claude_code/__init__.py +107 -0
- package/src/apothem/harnesses/claude_code/capabilities.yml +42 -0
- package/src/apothem/harnesses/claude_code/install.py +147 -0
- package/src/apothem/harnesses/claude_code/templates/settings.json +351 -0
- package/src/apothem/harnesses/claude_code/uninstall.py +23 -0
- package/src/apothem/harnesses/claude_code/update.py +10 -0
- package/src/apothem/harnesses/claude_code/verify.py +11 -0
- package/src/apothem/harnesses/codebuddy/STANDARD-CONVENTION-PIN.md +74 -0
- package/src/apothem/harnesses/codebuddy/__init__.py +49 -0
- package/src/apothem/harnesses/codebuddy/capabilities.yml +34 -0
- package/src/apothem/harnesses/codebuddy/install.py +40 -0
- package/src/apothem/harnesses/codebuddy/templates/apothem-rules.md +37 -0
- package/src/apothem/harnesses/codebuddy/uninstall.py +25 -0
- package/src/apothem/harnesses/codebuddy/update.py +10 -0
- package/src/apothem/harnesses/codebuddy/verify.py +11 -0
- package/src/apothem/harnesses/codex/STANDARD-CONVENTION-PIN.md +79 -0
- package/src/apothem/harnesses/codex/__init__.py +72 -0
- package/src/apothem/harnesses/codex/capabilities.yml +40 -0
- package/src/apothem/harnesses/codex/install.py +69 -0
- package/src/apothem/harnesses/codex/templates/AGENTS.md +40 -0
- package/src/apothem/harnesses/codex/templates/hooks.json +127 -0
- package/src/apothem/harnesses/codex/uninstall.py +23 -0
- package/src/apothem/harnesses/codex/update.py +10 -0
- package/src/apothem/harnesses/codex/verify.py +11 -0
- package/src/apothem/harnesses/cursor/STANDARD-CONVENTION-PIN.md +79 -0
- package/src/apothem/harnesses/cursor/__init__.py +48 -0
- package/src/apothem/harnesses/cursor/capabilities.yml +42 -0
- package/src/apothem/harnesses/cursor/install.py +38 -0
- package/src/apothem/harnesses/cursor/templates/apothem-rules.mdc +40 -0
- package/src/apothem/harnesses/cursor/uninstall.py +25 -0
- package/src/apothem/harnesses/cursor/update.py +10 -0
- package/src/apothem/harnesses/cursor/verify.py +11 -0
- package/src/apothem/harnesses/gemini_cli/STANDARD-CONVENTION-PIN.md +102 -0
- package/src/apothem/harnesses/gemini_cli/__init__.py +52 -0
- package/src/apothem/harnesses/gemini_cli/capabilities.yml +43 -0
- package/src/apothem/harnesses/gemini_cli/install.py +43 -0
- package/src/apothem/harnesses/gemini_cli/templates/GEMINI.md +38 -0
- package/src/apothem/harnesses/gemini_cli/uninstall.py +25 -0
- package/src/apothem/harnesses/gemini_cli/update.py +10 -0
- package/src/apothem/harnesses/gemini_cli/verify.py +11 -0
- package/src/apothem/harnesses/github_copilot/STANDARD-CONVENTION-PIN.md +84 -0
- package/src/apothem/harnesses/github_copilot/__init__.py +47 -0
- package/src/apothem/harnesses/github_copilot/capabilities.yml +42 -0
- package/src/apothem/harnesses/github_copilot/install.py +40 -0
- package/src/apothem/harnesses/github_copilot/templates/copilot-instructions.md +33 -0
- package/src/apothem/harnesses/github_copilot/uninstall.py +25 -0
- package/src/apothem/harnesses/github_copilot/update.py +10 -0
- package/src/apothem/harnesses/github_copilot/verify.py +11 -0
- package/src/apothem/harnesses/glm/STANDARD-CONVENTION-PIN.md +77 -0
- package/src/apothem/harnesses/glm/__init__.py +56 -0
- package/src/apothem/harnesses/glm/capabilities.yml +33 -0
- package/src/apothem/harnesses/glm/install.py +45 -0
- package/src/apothem/harnesses/glm/templates/glm.toml +58 -0
- package/src/apothem/harnesses/glm/uninstall.py +25 -0
- package/src/apothem/harnesses/glm/update.py +10 -0
- package/src/apothem/harnesses/glm/verify.py +11 -0
- package/src/apothem/harnesses/hermes/STANDARD-CONVENTION-PIN.md +57 -0
- package/src/apothem/harnesses/hermes/__init__.py +33 -0
- package/src/apothem/harnesses/hermes/capabilities.yml +36 -0
- package/src/apothem/harnesses/hermes/install.py +17 -0
- package/src/apothem/harnesses/hermes/materializer.py +35 -0
- package/src/apothem/harnesses/hermes/uninstall.py +33 -0
- package/src/apothem/harnesses/hermes/update.py +10 -0
- package/src/apothem/harnesses/hermes/verify.py +11 -0
- package/src/apothem/harnesses/kimi_code/STANDARD-CONVENTION-PIN.md +128 -0
- package/src/apothem/harnesses/kimi_code/__init__.py +59 -0
- package/src/apothem/harnesses/kimi_code/capabilities.yml +40 -0
- package/src/apothem/harnesses/kimi_code/install.py +42 -0
- package/src/apothem/harnesses/kimi_code/templates/AGENTS.md +43 -0
- package/src/apothem/harnesses/kimi_code/uninstall.py +27 -0
- package/src/apothem/harnesses/kimi_code/update.py +10 -0
- package/src/apothem/harnesses/kimi_code/verify.py +11 -0
- package/src/apothem/harnesses/kiro/STANDARD-CONVENTION-PIN.md +77 -0
- package/src/apothem/harnesses/kiro/__init__.py +49 -0
- package/src/apothem/harnesses/kiro/capabilities.yml +36 -0
- package/src/apothem/harnesses/kiro/install.py +39 -0
- package/src/apothem/harnesses/kiro/templates/apothem-rules.md +36 -0
- package/src/apothem/harnesses/kiro/uninstall.py +25 -0
- package/src/apothem/harnesses/kiro/update.py +10 -0
- package/src/apothem/harnesses/kiro/verify.py +11 -0
- package/src/apothem/harnesses/open_claw/STANDARD-CONVENTION-PIN.md +62 -0
- package/src/apothem/harnesses/open_claw/__init__.py +35 -0
- package/src/apothem/harnesses/open_claw/capabilities.yml +35 -0
- package/src/apothem/harnesses/open_claw/install.py +17 -0
- package/src/apothem/harnesses/open_claw/materializer.py +36 -0
- package/src/apothem/harnesses/open_claw/uninstall.py +32 -0
- package/src/apothem/harnesses/open_claw/update.py +10 -0
- package/src/apothem/harnesses/open_claw/verify.py +11 -0
- package/src/apothem/harnesses/opencode/STANDARD-CONVENTION-PIN.md +76 -0
- package/src/apothem/harnesses/opencode/__init__.py +35 -0
- package/src/apothem/harnesses/opencode/capabilities.yml +43 -0
- package/src/apothem/harnesses/opencode/install.py +17 -0
- package/src/apothem/harnesses/opencode/materializer.py +31 -0
- package/src/apothem/harnesses/opencode/uninstall.py +34 -0
- package/src/apothem/harnesses/opencode/update.py +10 -0
- package/src/apothem/harnesses/opencode/verify.py +11 -0
- package/src/apothem/harnesses/qwen_code/STANDARD-CONVENTION-PIN.md +87 -0
- package/src/apothem/harnesses/qwen_code/__init__.py +37 -0
- package/src/apothem/harnesses/qwen_code/capabilities.yml +43 -0
- package/src/apothem/harnesses/qwen_code/install.py +19 -0
- package/src/apothem/harnesses/qwen_code/materializer.py +174 -0
- package/src/apothem/harnesses/qwen_code/templates/QWEN.md +30 -0
- package/src/apothem/harnesses/qwen_code/uninstall.py +34 -0
- package/src/apothem/harnesses/qwen_code/update.py +10 -0
- package/src/apothem/harnesses/qwen_code/verify.py +11 -0
- package/src/apothem/harnesses/trae/STANDARD-CONVENTION-PIN.md +70 -0
- package/src/apothem/harnesses/trae/__init__.py +49 -0
- package/src/apothem/harnesses/trae/capabilities.yml +34 -0
- package/src/apothem/harnesses/trae/install.py +38 -0
- package/src/apothem/harnesses/trae/templates/apothem-rules.md +37 -0
- package/src/apothem/harnesses/trae/uninstall.py +25 -0
- package/src/apothem/harnesses/trae/update.py +10 -0
- package/src/apothem/harnesses/trae/verify.py +11 -0
- package/src/apothem/harnesses/windsurf/STANDARD-CONVENTION-PIN.md +91 -0
- package/src/apothem/harnesses/windsurf/__init__.py +52 -0
- package/src/apothem/harnesses/windsurf/capabilities.yml +40 -0
- package/src/apothem/harnesses/windsurf/install.py +41 -0
- package/src/apothem/harnesses/windsurf/templates/apothem-rules.md +37 -0
- package/src/apothem/harnesses/windsurf/uninstall.py +25 -0
- package/src/apothem/harnesses/windsurf/update.py +10 -0
- package/src/apothem/harnesses/windsurf/verify.py +11 -0
- package/src/apothem/harnesses/zed/STANDARD-CONVENTION-PIN.md +92 -0
- package/src/apothem/harnesses/zed/__init__.py +57 -0
- package/src/apothem/harnesses/zed/capabilities.yml +38 -0
- package/src/apothem/harnesses/zed/install.py +41 -0
- package/src/apothem/harnesses/zed/templates/apothem-rules.md +32 -0
- package/src/apothem/harnesses/zed/uninstall.py +28 -0
- package/src/apothem/harnesses/zed/update.py +10 -0
- package/src/apothem/harnesses/zed/verify.py +11 -0
- package/src/apothem/hooks/README.md +81 -0
- package/src/apothem/hooks/__init__.py +24 -0
- package/src/apothem/hooks/askuserquestion_validator.py +380 -0
- package/src/apothem/hooks/dispatch.py +296 -0
- package/src/apothem/hooks/emit_hook_context.py +444 -0
- package/src/apothem/hooks/hooks.json +318 -0
- package/src/apothem/hooks/lib/README.md +39 -0
- package/src/apothem/hooks/lib/__init__.py +18 -0
- package/src/apothem/hooks/lib/bootstrap.ps1 +129 -0
- package/src/apothem/hooks/lib/bootstrap.sh +103 -0
- package/src/apothem/hooks/lib/events.py +51 -0
- package/src/apothem/hooks/lib/find-pwsh.ps1 +78 -0
- package/src/apothem/hooks/lib/find-pwsh.sh +76 -0
- package/src/apothem/hooks/lib/find-python.ps1 +63 -0
- package/src/apothem/hooks/lib/find-python.sh +97 -0
- package/src/apothem/hooks/lib/log.py +43 -0
- package/src/apothem/hooks/lib/resolve_root.py +264 -0
- package/src/apothem/hooks/messages/postcompact.md +14 -0
- package/src/apothem/hooks/messages/posttooluse-proactive-compaction.md +46 -0
- package/src/apothem/hooks/messages/precompact.md +14 -0
- package/src/apothem/hooks/messages/pretooluse-askuserquestion-recommended.md +65 -0
- package/src/apothem/hooks/messages/pretooluse-bash-plan-guard.md +97 -0
- package/src/apothem/hooks/messages/pretooluse-bash.md +39 -0
- package/src/apothem/hooks/messages/pretooluse-conformity.md +70 -0
- package/src/apothem/hooks/messages/pretooluse-dependency-guard.md +21 -0
- package/src/apothem/hooks/messages/pretooluse-edit-header-guard.md +61 -0
- package/src/apothem/hooks/messages/pretooluse-edit.md +21 -0
- package/src/apothem/hooks/messages/pretooluse-eval-guard.md +39 -0
- package/src/apothem/hooks/messages/pretooluse-notebookedit.md +11 -0
- package/src/apothem/hooks/messages/pretooluse-write-header-guard.md +45 -0
- package/src/apothem/hooks/messages/pretooluse-write-plan-guard.md +72 -0
- package/src/apothem/hooks/messages/pretooluse-write.md +21 -0
- package/src/apothem/hooks/messages/sessionstart.md +15 -0
- package/src/apothem/hooks/messages/stop.md +27 -0
- package/src/apothem/hooks/proactive_compaction_tracker.py +327 -0
- package/src/apothem/hooks/session_start_bootstrap.py +472 -0
- package/src/apothem/lib/README.md +42 -0
- package/src/apothem/lib/__init__.py +13 -0
- package/src/apothem/lib/atomic_io.py +189 -0
- package/src/apothem/lib/auditor.py +687 -0
- package/src/apothem/lib/clean_slate.py +396 -0
- package/src/apothem/lib/contexts.py +352 -0
- package/src/apothem/lib/data_home.py +255 -0
- package/src/apothem/lib/frontmatter.py +101 -0
- package/src/apothem/lib/harness_materializer.py +213 -0
- package/src/apothem/lib/harness_protocol.py +59 -0
- package/src/apothem/lib/harness_registry.py +282 -0
- package/src/apothem/lib/harness_registry_data.py +843 -0
- package/src/apothem/lib/install_ledger.py +347 -0
- package/src/apothem/lib/learning.py +540 -0
- package/src/apothem/lib/memory.py +347 -0
- package/src/apothem/lib/parallel_sweep.py +234 -0
- package/src/apothem/lib/plan_tiers.py +200 -0
- package/src/apothem/lib/plugin_bootstrap.py +132 -0
- package/src/apothem/lib/plugin_tree.py +599 -0
- package/src/apothem/lib/profile.py +755 -0
- package/src/apothem/lib/profile_projection.py +198 -0
- package/src/apothem/lib/propagation-manifest.yaml +878 -0
- package/src/apothem/lib/propagation.py +220 -0
- package/src/apothem/lib/python_resolver.py +189 -0
- package/src/apothem/lib/reporter.py +62 -0
- package/src/apothem/lib/workspace_migration.py +323 -0
- package/src/apothem/output-styles/README.md +41 -0
- package/src/apothem/output-styles/concise-engineer.md +49 -0
- package/src/apothem/output-styles/default-architect.md +52 -0
- package/src/apothem/output-styles/default.md +113 -0
- package/src/apothem/output-styles/forensic-auditor.md +63 -0
- package/src/apothem/py.typed +0 -0
- package/src/apothem/rules/README.md +121 -0
- package/src/apothem/rules/agent-capability-discipline-matrix.md +89 -0
- package/src/apothem/rules/agent-capability-discipline.md +78 -0
- package/src/apothem/rules/agent-orchestration-patterns.md +144 -0
- package/src/apothem/rules/agent-orchestration.md +65 -0
- package/src/apothem/rules/agents-md-convention.md +86 -0
- package/src/apothem/rules/agile-sprints-elements.md +135 -0
- package/src/apothem/rules/agile-sprints.md +64 -0
- package/src/apothem/rules/agnostic-posture-checklist.md +47 -0
- package/src/apothem/rules/agnostic-posture.md +48 -0
- package/src/apothem/rules/authoritative-referencing-quotation.md +50 -0
- package/src/apothem/rules/authoritative-referencing.md +66 -0
- package/src/apothem/rules/authority-inquiry-categories.md +58 -0
- package/src/apothem/rules/authority-inquiry.md +54 -0
- package/src/apothem/rules/auto-memory-topic-files.md +86 -0
- package/src/apothem/rules/auto-memory.md +67 -0
- package/src/apothem/rules/bidirectional-binding.md +123 -0
- package/src/apothem/rules/canonical-layout-reporting-tiers.md +212 -0
- package/src/apothem/rules/canonical-layout.md +60 -0
- package/src/apothem/rules/clean-architecture-layers.md +186 -0
- package/src/apothem/rules/clean-room-generation-protocols.md +124 -0
- package/src/apothem/rules/clean-room-generation.md +59 -0
- package/src/apothem/rules/code-craft-conventions.md +101 -0
- package/src/apothem/rules/code-craft-markdown.md +138 -0
- package/src/apothem/rules/code-craft-python.md +154 -0
- package/src/apothem/rules/code-craft-shell.md +192 -0
- package/src/apothem/rules/cognitive-identity-techniques.md +180 -0
- package/src/apothem/rules/cognitive-identity.md +81 -0
- package/src/apothem/rules/context-management-budget.md +46 -0
- package/src/apothem/rules/context-management-protocol.md +161 -0
- package/src/apothem/rules/context-management-scratch.md +128 -0
- package/src/apothem/rules/context-management.md +85 -0
- package/src/apothem/rules/definitiveness-virtues.md +67 -0
- package/src/apothem/rules/definitiveness.md +58 -0
- package/src/apothem/rules/determinism.md +81 -0
- package/src/apothem/rules/disclosure-ledger-markers.md +58 -0
- package/src/apothem/rules/disclosure-ledger.md +52 -0
- package/src/apothem/rules/dynamism.md +38 -0
- package/src/apothem/rules/etc-extension.md +57 -0
- package/src/apothem/rules/expertise-posture-elements.md +68 -0
- package/src/apothem/rules/expertise-posture.md +54 -0
- package/src/apothem/rules/freshness-facade.md +64 -0
- package/src/apothem/rules/harness-adapter-shape-schemas.md +162 -0
- package/src/apothem/rules/harness-adapter-shape.md +42 -0
- package/src/apothem/rules/host-discovery-manifests.md +50 -0
- package/src/apothem/rules/host-discovery.md +56 -0
- package/src/apothem/rules/i18n-discipline-locale-cohorts.md +120 -0
- package/src/apothem/rules/i18n-discipline.md +70 -0
- package/src/apothem/rules/interactive-questions-canonical-shapes.md +590 -0
- package/src/apothem/rules/interactive-questions-detail.md +41 -0
- package/src/apothem/rules/interactive-questions-sweep-matchers.md +184 -0
- package/src/apothem/rules/interactive-questions.md +89 -0
- package/src/apothem/rules/large-file-generation.md +112 -0
- package/src/apothem/rules/large-file-reading.md +59 -0
- package/src/apothem/rules/living-docs.md +85 -0
- package/src/apothem/rules/multi-agent-workflow.md +57 -0
- package/src/apothem/rules/operational-mandates-expanded.md +78 -0
- package/src/apothem/rules/operational-mandates.md +88 -0
- package/src/apothem/rules/option-annotation-form.md +60 -0
- package/src/apothem/rules/option-annotation.md +45 -0
- package/src/apothem/rules/own-voice-reimplementation.md +86 -0
- package/src/apothem/rules/performance-discipline.md +91 -0
- package/src/apothem/rules/persistent-conventions-vigilance-checklist.md +54 -0
- package/src/apothem/rules/persistent-conventions-vigilance.md +61 -0
- package/src/apothem/rules/plain-language.md +56 -0
- package/src/apothem/rules/planning-techniques.md +130 -0
- package/src/apothem/rules/pre-emission-gate-bars.md +86 -0
- package/src/apothem/rules/pre-emission-gate.md +54 -0
- package/src/apothem/rules/production-ready-prs-surfaces.md +162 -0
- package/src/apothem/rules/production-ready-prs.md +83 -0
- package/src/apothem/rules/propagation.md +63 -0
- package/src/apothem/rules/recommend-next-step.md +106 -0
- package/src/apothem/rules/refactoring-discipline.md +76 -0
- package/src/apothem/rules/session-closure.md +44 -0
- package/src/apothem/rules/sota-elevation-exemplars.md +76 -0
- package/src/apothem/rules/sota-elevation.md +52 -0
- package/src/apothem/rules/source-accessibility.md +58 -0
- package/src/apothem/rules/surgical-manipulation.md +48 -0
- package/src/apothem/rules/systemic-participation-relations.md +108 -0
- package/src/apothem/rules/systemic-participation.md +70 -0
- package/src/apothem/rules/ten-dimension-check-dimensions.md +52 -0
- package/src/apothem/rules/ten-dimension-check.md +59 -0
- package/src/apothem/rules/token-budget-discipline.md +81 -0
- package/src/apothem/rules/token-efficiency-rewrite-protocol.md +79 -0
- package/src/apothem/rules/token-efficiency-rewrite.md +77 -0
- package/src/apothem/rules/tool-use-discipline.md +48 -0
- package/src/apothem/rules/visual-leverage.md +102 -0
- package/src/apothem/schemas/NOTICE.md +9 -0
- package/src/apothem/schemas/README.md +104 -0
- package/src/apothem/schemas/__init__.py +176 -0
- package/src/apothem/schemas/advisory-finding.schema.json +111 -0
- package/src/apothem/schemas/agent.schema.json +106 -0
- package/src/apothem/schemas/authorship-header.txt +1 -0
- package/src/apothem/schemas/cohort-manifest.yaml +248 -0
- package/src/apothem/schemas/cohort-metadata-vocabulary.yaml +168 -0
- package/src/apothem/schemas/cohort.schema.json +113 -0
- package/src/apothem/schemas/command.schema.json +68 -0
- package/src/apothem/schemas/compatibility-matrix.yaml +432 -0
- package/src/apothem/schemas/context-fragment.schema.json +64 -0
- package/src/apothem/schemas/freshness-token-denylist.txt +51 -0
- package/src/apothem/schemas/handoff-manifest.yaml +353 -0
- package/src/apothem/schemas/header-exceptions.txt +141 -0
- package/src/apothem/schemas/header-visibility.yaml +39 -0
- package/src/apothem/schemas/learning-signal.schema.json +46 -0
- package/src/apothem/schemas/memory-record.schema.json +61 -0
- package/src/apothem/schemas/output-style.schema.json +40 -0
- package/src/apothem/schemas/plan.schema.json +51 -0
- package/src/apothem/schemas/plugin.schema.json +83 -0
- package/src/apothem/schemas/profile.example.yaml +70 -0
- package/src/apothem/schemas/profile.minimal.yaml +6 -0
- package/src/apothem/schemas/profile.schema.json +396 -0
- package/src/apothem/schemas/reference-token-denylist.txt +25 -0
- package/src/apothem/schemas/skill.schema.json +75 -0
- package/src/apothem/skills/README.md +93 -0
- package/src/apothem/skills/dependency-upgrade/SKILL.md +105 -0
- package/src/apothem/skills/dev-toolkit/SKILL.md +120 -0
- package/src/apothem/skills/diagram-authoring/SKILL.md +113 -0
- package/src/apothem/skills/document-authoring/SKILL.md +118 -0
- package/src/apothem/skills/ecosystem-audit/SKILL.md +108 -0
- package/src/apothem/skills/ecosystem-audit/references/audit-fortress.md +85 -0
- package/src/apothem/skills/ecosystem-audit/references/procedure.md +162 -0
- package/src/apothem/skills/eval-harness/SKILL.md +88 -0
- package/src/apothem/skills/incident-runbook/SKILL.md +92 -0
- package/src/apothem/skills/multi-source-research/SKILL.md +90 -0
- package/src/apothem/skills/plan-suite/SKILL.md +118 -0
- package/src/apothem/skills/plan-suite/master_template.md +1324 -0
- package/src/apothem/skills/projectify/SKILL.md +117 -0
- package/src/apothem/skills/prompt-engineering/SKILL.md +122 -0
- package/src/apothem/skills/refactor-extract/SKILL.md +85 -0
- package/src/apothem/skills/research-suite/SKILL.md +170 -0
- package/src/apothem/skills/research-suite/references/directory-structure.md +47 -0
- package/src/apothem/skills/research-suite/references/lifecycle.md +67 -0
- package/src/apothem/skills/research-suite/references/principal-investigator-framework.md +37 -0
- package/src/apothem/skills/research-suite/references/rigor-mandates.md +30 -0
- package/src/apothem/skills/research-suite/research_template.md +476 -0
- package/src/apothem/skills/secret-rotation/SKILL.md +87 -0
- package/src/apothem/skills/source-synthesis/SKILL.md +92 -0
- package/src/apothem/skills/surgical-guard/SKILL.md +118 -0
- package/src/apothem/skills/test-authoring/SKILL.md +85 -0
- package/src/apothem/skills/vuln-triage/SKILL.md +91 -0
- package/src/apothem/skills/workflow/SKILL.md +139 -0
- package/src/apothem/statuslines/README.md +26 -0
- package/src/apothem/statuslines/__init__.py +20 -0
- package/src/apothem/statuslines/conformity.json +5 -0
- package/src/apothem/statuslines/render.py +334 -0
- package/src/apothem/statuslines/statusline.md +50 -0
- package/src/apothem/templates/README.md +43 -0
- package/src/apothem/templates/agents-md-template.md +80 -0
- package/src/apothem/templates/consideration-log.md +39 -0
- package/src/apothem/templates/expertise-gap-log.md +56 -0
- package/src/apothem/templates/master-index-template.md +93 -0
- package/src/apothem/templates/potency-map.md +53 -0
- package/src/apothem/templates/preservation-audit.md +60 -0
- package/src/apothem/templates/question-resolution-audit.md +52 -0
- package/src/apothem/templates/trace-matrix-template.md +77 -0
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
|
2
|
+
|
|
3
|
+
"""Propagation-manifest loader for harness adapters.
|
|
4
|
+
|
|
5
|
+
The manifest at ``src/apothem/lib/propagation-manifest.yaml`` declares
|
|
6
|
+
per-harness install rules — ordered ``(source, target, mode)`` tuples,
|
|
7
|
+
exclude globs, stale-sweep entries, and per-directory filename filters.
|
|
8
|
+
This module loads the manifest and exposes typed accessors so adapter
|
|
9
|
+
modules consume the manifest declaratively instead of hardcoding the
|
|
10
|
+
rules as Python constants.
|
|
11
|
+
|
|
12
|
+
Status: manifest-driven. The canonical claude-code adapter at
|
|
13
|
+
``src/apothem/harnesses/claude_code/install.py`` consumes its
|
|
14
|
+
propagation rules from this loader via ``load_manifest`` — the manifest
|
|
15
|
+
IS the contract; the adapter holds no hardcoded install-layout
|
|
16
|
+
constants. The regression test at
|
|
17
|
+
``tests/unit/test_propagation_manifest.py`` verifies the manifest and
|
|
18
|
+
the adapter agree on the install layout.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
from __future__ import annotations
|
|
22
|
+
|
|
23
|
+
from dataclasses import dataclass, field
|
|
24
|
+
from functools import lru_cache
|
|
25
|
+
from importlib.resources import as_file, files
|
|
26
|
+
from pathlib import Path
|
|
27
|
+
from string import Template
|
|
28
|
+
from typing import Final
|
|
29
|
+
|
|
30
|
+
import yaml
|
|
31
|
+
|
|
32
|
+
# The five preserve-first ownership classes. Class assignment is by
|
|
33
|
+
# authorship origin and durability — who authored the bytes at the target
|
|
34
|
+
# path, and what the propagation driver may do to them:
|
|
35
|
+
#
|
|
36
|
+
# - apothem-owned — bytes Apothem authors and exclusively manages.
|
|
37
|
+
# - operator-owned — bytes the operator owns; Apothem merges, never
|
|
38
|
+
# silently overwrites (backup + diff + authorization).
|
|
39
|
+
# - vendor-reserved — bytes the harness vendor manages; never a write target.
|
|
40
|
+
# - generated — Apothem's reproducible derived output; regenerate freely.
|
|
41
|
+
# - immutable — append-only records; in-place rewrite is refused.
|
|
42
|
+
OWNERSHIP_CLASSES: Final[frozenset[str]] = frozenset(
|
|
43
|
+
{
|
|
44
|
+
"apothem-owned",
|
|
45
|
+
"operator-owned",
|
|
46
|
+
"vendor-reserved",
|
|
47
|
+
"generated",
|
|
48
|
+
"immutable",
|
|
49
|
+
}
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
_DEFAULT_OWNERSHIP_CLASS: Final[str] = "apothem-owned"
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
@dataclass(frozen=True)
|
|
56
|
+
class InstallEntry:
|
|
57
|
+
"""One propagation operation declared in the manifest."""
|
|
58
|
+
|
|
59
|
+
source: str
|
|
60
|
+
target: str
|
|
61
|
+
mode: str # write_text, tree mode, or harness-specific conversion mode
|
|
62
|
+
ownership_class: str = _DEFAULT_OWNERSHIP_CLASS
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
@dataclass(frozen=True)
|
|
66
|
+
class HarnessRules:
|
|
67
|
+
"""Per-harness propagation rules loaded from the manifest."""
|
|
68
|
+
|
|
69
|
+
install: list[InstallEntry]
|
|
70
|
+
exclude: list[str]
|
|
71
|
+
stale_sweep: list[str]
|
|
72
|
+
per_directory_filters: dict[str, list[str]] = field(default_factory=dict)
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
_MANIFEST_FILENAME: Final[str] = "propagation-manifest.yaml"
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def _manifest_path() -> Path:
|
|
79
|
+
"""Return the on-disk path to the propagation manifest.
|
|
80
|
+
|
|
81
|
+
Resolves via ``importlib.resources`` so the manifest is locatable
|
|
82
|
+
regardless of where the apothem package is installed (editable
|
|
83
|
+
development tree, pip-installed wheel, or zipfile distribution).
|
|
84
|
+
"""
|
|
85
|
+
traversable = files("apothem.lib") / _MANIFEST_FILENAME
|
|
86
|
+
with as_file(traversable) as concrete:
|
|
87
|
+
return Path(concrete)
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def load_manifest() -> dict[str, HarnessRules]:
|
|
91
|
+
"""Load the propagation manifest and return per-harness rules.
|
|
92
|
+
|
|
93
|
+
The parse is memoized per process and keyed by the manifest's path and
|
|
94
|
+
modification time, so a batch command (install-all / update-all) that
|
|
95
|
+
resolves rules once per harness parses the 27 KB manifest only once. A
|
|
96
|
+
manifest edited in place (different mtime) invalidates the cache on the
|
|
97
|
+
next call.
|
|
98
|
+
|
|
99
|
+
Returns:
|
|
100
|
+
Mapping from harness name (e.g. ``"claude_code"``) to its
|
|
101
|
+
``HarnessRules`` record. Harnesses absent from the manifest are
|
|
102
|
+
omitted from the returned mapping.
|
|
103
|
+
|
|
104
|
+
Raises:
|
|
105
|
+
FileNotFoundError: When the manifest file is missing from the
|
|
106
|
+
package resources.
|
|
107
|
+
ValueError: When the manifest is malformed (missing top-level
|
|
108
|
+
``harnesses`` key, unparseable YAML, or per-harness entries
|
|
109
|
+
failing the schema).
|
|
110
|
+
"""
|
|
111
|
+
path = _manifest_path()
|
|
112
|
+
if not path.is_file():
|
|
113
|
+
raise FileNotFoundError(f"propagation manifest not found at {path}")
|
|
114
|
+
return _load_manifest_cached(str(path), path.stat().st_mtime_ns)
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
@lru_cache(maxsize=1)
|
|
118
|
+
def _load_manifest_cached(path_str: str, mtime_ns: int) -> dict[str, HarnessRules]:
|
|
119
|
+
"""Read and parse the manifest at ``path_str``; memoized by (path, mtime).
|
|
120
|
+
|
|
121
|
+
Reading and YAML-parsing happen here so the bytes are read exactly once
|
|
122
|
+
per (path, mtime). ``lru_cache`` does not memoize exceptions, so a
|
|
123
|
+
malformed manifest re-parses (and re-raises ``ValueError``) on every call.
|
|
124
|
+
"""
|
|
125
|
+
text = Path(path_str).read_text(encoding="utf-8")
|
|
126
|
+
return _parse_manifest_text(text)
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def _parse_manifest_text(text: str) -> dict[str, HarnessRules]:
|
|
130
|
+
"""Parse the manifest YAML text into per-harness rules (schema-validated)."""
|
|
131
|
+
raw = yaml.safe_load(text)
|
|
132
|
+
if not isinstance(raw, dict) or "harnesses" not in raw:
|
|
133
|
+
raise ValueError(
|
|
134
|
+
"propagation manifest is missing the top-level 'harnesses' key"
|
|
135
|
+
)
|
|
136
|
+
harnesses_raw = raw["harnesses"] or {}
|
|
137
|
+
if not isinstance(harnesses_raw, dict):
|
|
138
|
+
raise ValueError("'harnesses' must be a mapping of harness-name to rules")
|
|
139
|
+
|
|
140
|
+
rules: dict[str, HarnessRules] = {}
|
|
141
|
+
for name, body in harnesses_raw.items():
|
|
142
|
+
if body is None:
|
|
143
|
+
continue
|
|
144
|
+
if not isinstance(body, dict):
|
|
145
|
+
raise ValueError(f"harness '{name}' entry must be a mapping")
|
|
146
|
+
install_raw = body.get("install") or []
|
|
147
|
+
install: list[InstallEntry] = []
|
|
148
|
+
for entry in install_raw:
|
|
149
|
+
if not isinstance(entry, dict):
|
|
150
|
+
raise ValueError(f"harness '{name}' install entry must be a mapping")
|
|
151
|
+
ownership_class = str(
|
|
152
|
+
entry.get("ownership_class", _DEFAULT_OWNERSHIP_CLASS)
|
|
153
|
+
)
|
|
154
|
+
if ownership_class not in OWNERSHIP_CLASSES:
|
|
155
|
+
raise ValueError(
|
|
156
|
+
f"harness '{name}' install entry declares unknown "
|
|
157
|
+
f"ownership_class '{ownership_class}'"
|
|
158
|
+
)
|
|
159
|
+
install.append(
|
|
160
|
+
InstallEntry(
|
|
161
|
+
source=str(entry["source"]),
|
|
162
|
+
target=str(entry["target"]),
|
|
163
|
+
mode=str(entry.get("mode", "replace_tree")),
|
|
164
|
+
ownership_class=ownership_class,
|
|
165
|
+
)
|
|
166
|
+
)
|
|
167
|
+
rules[name] = HarnessRules(
|
|
168
|
+
install=install,
|
|
169
|
+
exclude=list(body.get("exclude") or []),
|
|
170
|
+
stale_sweep=list(body.get("stale-sweep") or []),
|
|
171
|
+
per_directory_filters={
|
|
172
|
+
k: list(v or [])
|
|
173
|
+
for k, v in (body.get("per-directory-filters") or {}).items()
|
|
174
|
+
},
|
|
175
|
+
)
|
|
176
|
+
return rules
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
def resolve_target(
|
|
180
|
+
template: str,
|
|
181
|
+
*,
|
|
182
|
+
harness_root: Path | None = None,
|
|
183
|
+
project_root: Path | None = None,
|
|
184
|
+
) -> Path:
|
|
185
|
+
"""Substitute ``${HARNESS_ROOT}`` and ``${PROJECT_ROOT}`` into a path.
|
|
186
|
+
|
|
187
|
+
Shared substitution helper consumed by every adapter that resolves
|
|
188
|
+
manifest target paths into concrete filesystem locations. Supports
|
|
189
|
+
two placeholders:
|
|
190
|
+
|
|
191
|
+
- ``${HARNESS_ROOT}`` — the harness's user-scope configuration root
|
|
192
|
+
(e.g., ``~/.claude`` for the Claude Code harness, ``~/.gemini`` for
|
|
193
|
+
antigravity / gemini_cli).
|
|
194
|
+
- ``${PROJECT_ROOT}`` — the operator-supplied project root, required
|
|
195
|
+
by project-scope harnesses (e.g., ``<project>/.cursor/rules/`` for
|
|
196
|
+
the cursor harness; analogous targets for github_copilot and
|
|
197
|
+
gemini_cli project-scope entries).
|
|
198
|
+
|
|
199
|
+
Either placeholder may be absent from the template — substitution is
|
|
200
|
+
a no-op for the absent placeholder. When a template references a
|
|
201
|
+
placeholder whose corresponding root argument is ``None``, the
|
|
202
|
+
substitution leaves the literal placeholder in place; the caller is
|
|
203
|
+
responsible for surfacing the missing-root condition as an operator
|
|
204
|
+
inquiry before the resulting path is written to.
|
|
205
|
+
"""
|
|
206
|
+
mapping: dict[str, str] = {}
|
|
207
|
+
if harness_root is not None:
|
|
208
|
+
mapping["HARNESS_ROOT"] = str(harness_root)
|
|
209
|
+
if project_root is not None:
|
|
210
|
+
mapping["PROJECT_ROOT"] = str(project_root)
|
|
211
|
+
return Path(Template(template).safe_substitute(mapping))
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
__all__ = [
|
|
215
|
+
"OWNERSHIP_CLASSES",
|
|
216
|
+
"HarnessRules",
|
|
217
|
+
"InstallEntry",
|
|
218
|
+
"load_manifest",
|
|
219
|
+
"resolve_target",
|
|
220
|
+
]
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
|
2
|
+
|
|
3
|
+
"""Resolve a real CPython interpreter for install-time hook-command wiring.
|
|
4
|
+
|
|
5
|
+
The Claude Code ``settings.json`` hook entries must invoke a concrete
|
|
6
|
+
interpreter, never a bare ``python`` name that a host's ``PATH`` can resolve to
|
|
7
|
+
a Microsoft Store ``WindowsApps`` launcher stub (a zero-content shim that opens
|
|
8
|
+
the Store instead of running the script). This module supplies the absolute
|
|
9
|
+
interpreter path that the adapter substitutes for the template's
|
|
10
|
+
``${PYTHON_BIN}`` placeholder at install time.
|
|
11
|
+
|
|
12
|
+
Resolution order:
|
|
13
|
+
|
|
14
|
+
1. ``sys.executable`` — the interpreter running the install. apothem declares
|
|
15
|
+
``requires-python = ">=3.10"``, so a WindowsApps stub (which cannot import or
|
|
16
|
+
run apothem at all) can never be the running interpreter; ``sys.executable``
|
|
17
|
+
is therefore guaranteed a real CPython that satisfies the floor whenever it
|
|
18
|
+
is populated and points at a real file.
|
|
19
|
+
2. A ``PATH`` walk mirroring ``hooks/lib/find-python.sh`` — every candidate name
|
|
20
|
+
probed in ``PATH`` order, ``WindowsApps`` shims and sub-1024-byte stubs
|
|
21
|
+
rejected, each survivor version-probed against the floor. This is the
|
|
22
|
+
fallback for the (rare) embedded / frozen case where ``sys.executable`` is
|
|
23
|
+
unusable.
|
|
24
|
+
|
|
25
|
+
The PATH-walk fallback is the Python sibling of the shipped shell locators
|
|
26
|
+
(``find-python.sh`` / ``find-python.ps1``); it does not replace them — the
|
|
27
|
+
bootstrap stubs still consume the shell locators on the no-substitution path.
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
from __future__ import annotations
|
|
31
|
+
|
|
32
|
+
import os
|
|
33
|
+
import subprocess
|
|
34
|
+
import sys
|
|
35
|
+
from pathlib import Path
|
|
36
|
+
from typing import Final
|
|
37
|
+
|
|
38
|
+
# Minimum interpreter the hooks require, mirroring ``requires-python`` in
|
|
39
|
+
# ``pyproject.toml`` and the floor in ``hooks/lib/find-python.sh``.
|
|
40
|
+
MIN_MAJOR: Final[int] = 3
|
|
41
|
+
MIN_MINOR: Final[int] = 10
|
|
42
|
+
|
|
43
|
+
# Candidate executable names probed in ``PATH``, most-specific last so the
|
|
44
|
+
# bare names win when they already satisfy the floor (mirrors find-python.sh).
|
|
45
|
+
_CANDIDATE_NAMES: Final[tuple[str, ...]] = (
|
|
46
|
+
"python",
|
|
47
|
+
"python3",
|
|
48
|
+
"python3.14",
|
|
49
|
+
"python3.13",
|
|
50
|
+
"python3.12",
|
|
51
|
+
"python3.11",
|
|
52
|
+
"python3.10",
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
# Minimum byte size for a real interpreter; WindowsApps launcher shims are
|
|
56
|
+
# near-empty reparse stubs well under this floor (mirrors find-python.sh).
|
|
57
|
+
_MIN_INTERPRETER_BYTES: Final[int] = 1024
|
|
58
|
+
|
|
59
|
+
# Probe program: print ``"<major> <minor>"`` for the running interpreter.
|
|
60
|
+
_VERSION_PROBE: Final[str] = (
|
|
61
|
+
"import sys; "
|
|
62
|
+
"sys.stdout.write(str(sys.version_info.major) + chr(32) "
|
|
63
|
+
"+ str(sys.version_info.minor))"
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def _is_windowsapps_stub(path: Path) -> bool:
|
|
68
|
+
"""Return True when *path* is a Microsoft Store launcher shim.
|
|
69
|
+
|
|
70
|
+
Store shims live under ``...\\Microsoft\\WindowsApps\\`` and are near-empty
|
|
71
|
+
reparse stubs; either signal alone is conclusive, so both are rejected.
|
|
72
|
+
"""
|
|
73
|
+
if "WindowsApps" in path.parts:
|
|
74
|
+
return True
|
|
75
|
+
try:
|
|
76
|
+
return path.stat().st_size < _MIN_INTERPRETER_BYTES
|
|
77
|
+
except OSError:
|
|
78
|
+
return True
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def _probe_version(executable: Path) -> tuple[int, int] | None:
|
|
82
|
+
"""Return ``(major, minor)`` for *executable*, or None when unprobeable.
|
|
83
|
+
|
|
84
|
+
Runs the candidate with a literal probe program (no shell, no untrusted
|
|
85
|
+
input). Any failure — non-zero exit, unparseable output, OSError — yields
|
|
86
|
+
None so the caller skips the candidate rather than aborting the walk.
|
|
87
|
+
"""
|
|
88
|
+
try:
|
|
89
|
+
completed = subprocess.run( # noqa: S603 — literal argv, trusted probe program, no shell
|
|
90
|
+
[str(executable), "-c", _VERSION_PROBE],
|
|
91
|
+
capture_output=True,
|
|
92
|
+
text=True,
|
|
93
|
+
timeout=10,
|
|
94
|
+
check=False,
|
|
95
|
+
)
|
|
96
|
+
except (OSError, subprocess.SubprocessError):
|
|
97
|
+
return None
|
|
98
|
+
if completed.returncode != 0:
|
|
99
|
+
return None
|
|
100
|
+
parts = completed.stdout.strip().split()
|
|
101
|
+
if len(parts) < 2:
|
|
102
|
+
return None
|
|
103
|
+
try:
|
|
104
|
+
return int(parts[0]), int(parts[1])
|
|
105
|
+
except ValueError:
|
|
106
|
+
return None
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def _satisfies_floor(major: int, minor: int) -> bool:
|
|
110
|
+
"""Return True when ``(major, minor)`` meets the ``>= 3.10`` floor."""
|
|
111
|
+
return major > MIN_MAJOR or (major == MIN_MAJOR and minor >= MIN_MINOR)
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def _path_candidates(name: str) -> list[Path]:
|
|
115
|
+
"""Return every executable named *name* found across ``PATH``, in order.
|
|
116
|
+
|
|
117
|
+
The Python stand-in for ``find-python.sh``'s PATH walk: it lists *all*
|
|
118
|
+
matches rather than only the first, so a WindowsApps shim early on ``PATH``
|
|
119
|
+
does not mask a real interpreter found later. Empty ``PATH`` segments are
|
|
120
|
+
skipped, not resolved to the current directory — a cwd-resident interpreter
|
|
121
|
+
must never be discovered, version-probed (executed), and wired into the
|
|
122
|
+
hook command (defense-in-depth, mirroring ``find-python.sh``).
|
|
123
|
+
"""
|
|
124
|
+
found: list[Path] = []
|
|
125
|
+
raw_path = os.environ.get("PATH", "")
|
|
126
|
+
suffixes = [""]
|
|
127
|
+
if os.name == "nt":
|
|
128
|
+
pathext = os.environ.get("PATHEXT", ".EXE")
|
|
129
|
+
suffixes = [""] + [ext.lower() for ext in pathext.split(os.pathsep) if ext]
|
|
130
|
+
for directory in raw_path.split(os.pathsep):
|
|
131
|
+
# Skip an empty PATH segment (a doubled separator, or a leading /
|
|
132
|
+
# trailing one) rather than mapping it to cwd. POSIX resolves an empty
|
|
133
|
+
# segment to the current directory, but this resolver's result is
|
|
134
|
+
# version-probed (executed) and substituted for ``${PYTHON_BIN}`` in the
|
|
135
|
+
# hook command that runs on every tool use; trusting a cwd interpreter
|
|
136
|
+
# there is the footgun this guard closes. An explicit "." entry is
|
|
137
|
+
# non-empty and still honored below.
|
|
138
|
+
if not directory:
|
|
139
|
+
continue
|
|
140
|
+
for suffix in suffixes:
|
|
141
|
+
candidate = Path(directory) / (name + suffix)
|
|
142
|
+
if candidate.is_file() and os.access(candidate, os.X_OK):
|
|
143
|
+
found.append(candidate)
|
|
144
|
+
return found
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def _resolve_from_path() -> Path | None:
|
|
148
|
+
"""Walk ``PATH`` for a real CPython satisfying the floor, or None.
|
|
149
|
+
|
|
150
|
+
Mirrors ``find-python.sh``: probe each candidate name in priority order,
|
|
151
|
+
reject WindowsApps shims and sub-1024-byte stubs, and return the first
|
|
152
|
+
interpreter whose probed version meets the floor.
|
|
153
|
+
"""
|
|
154
|
+
for name in _CANDIDATE_NAMES:
|
|
155
|
+
for candidate in _path_candidates(name):
|
|
156
|
+
if _is_windowsapps_stub(candidate):
|
|
157
|
+
continue
|
|
158
|
+
version = _probe_version(candidate)
|
|
159
|
+
if version is None:
|
|
160
|
+
continue
|
|
161
|
+
if _satisfies_floor(*version):
|
|
162
|
+
return candidate.resolve()
|
|
163
|
+
return None
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
def resolve_python_bin() -> Path:
|
|
167
|
+
"""Return the absolute path to a real CPython >= 3.10 for hook wiring.
|
|
168
|
+
|
|
169
|
+
Prefers ``sys.executable`` (guaranteed a real, floor-satisfying interpreter
|
|
170
|
+
whenever it is populated and points at a real file, because apothem cannot
|
|
171
|
+
run under a WindowsApps stub). Falls back to a ``PATH`` walk that rejects
|
|
172
|
+
Store shims. Raises :class:`RuntimeError` when no real interpreter can be
|
|
173
|
+
found — the adapter surfaces that as an install-time failure rather than
|
|
174
|
+
wiring a bare ``python`` that could resolve to a stub at hook-run time.
|
|
175
|
+
"""
|
|
176
|
+
running = sys.executable
|
|
177
|
+
if running:
|
|
178
|
+
running_path = Path(running)
|
|
179
|
+
if running_path.is_file() and not _is_windowsapps_stub(running_path):
|
|
180
|
+
return running_path.resolve()
|
|
181
|
+
|
|
182
|
+
resolved = _resolve_from_path()
|
|
183
|
+
if resolved is not None:
|
|
184
|
+
return resolved
|
|
185
|
+
|
|
186
|
+
raise RuntimeError(
|
|
187
|
+
"no real CPython >= 3.10 interpreter found for hook wiring; "
|
|
188
|
+
"sys.executable is unusable and no PATH candidate satisfies the floor"
|
|
189
|
+
)
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
|
2
|
+
|
|
3
|
+
"""Structured pass/fail/warn reporting for validation tools."""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
import sys
|
|
8
|
+
from dataclasses import dataclass, field
|
|
9
|
+
from typing import Final, TextIO
|
|
10
|
+
|
|
11
|
+
_PASS: Final[str] = "[PASS]"
|
|
12
|
+
_FAIL: Final[str] = "[FAIL]"
|
|
13
|
+
_WARN: Final[str] = "[WARN]"
|
|
14
|
+
_INFO: Final[str] = "[INFO]"
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@dataclass
|
|
18
|
+
class Reporter:
|
|
19
|
+
"""Accumulates validation outcomes with running counts."""
|
|
20
|
+
|
|
21
|
+
stream: TextIO = field(default_factory=lambda: sys.stdout)
|
|
22
|
+
passed: int = 0
|
|
23
|
+
failed: int = 0
|
|
24
|
+
warned: int = 0
|
|
25
|
+
errors: list[str] = field(default_factory=list)
|
|
26
|
+
|
|
27
|
+
def ok(self, message: str) -> None:
|
|
28
|
+
"""Write a ``[PASS]`` line and increment the passed count."""
|
|
29
|
+
self.stream.write(f"{_PASS} {message}\n")
|
|
30
|
+
self.passed += 1
|
|
31
|
+
|
|
32
|
+
def fail(self, message: str) -> None:
|
|
33
|
+
"""Write a ``[FAIL]`` line, increment the failed count, and record
|
|
34
|
+
the message in :attr:`errors`."""
|
|
35
|
+
self.stream.write(f"{_FAIL} {message}\n")
|
|
36
|
+
self.failed += 1
|
|
37
|
+
self.errors.append(message)
|
|
38
|
+
|
|
39
|
+
def warn(self, message: str) -> None:
|
|
40
|
+
"""Write a ``[WARN]`` line and increment the warned count."""
|
|
41
|
+
self.stream.write(f"{_WARN} {message}\n")
|
|
42
|
+
self.warned += 1
|
|
43
|
+
|
|
44
|
+
def info(self, message: str) -> None:
|
|
45
|
+
"""Write an ``[INFO]`` line; carries no pass/fail/warn weight."""
|
|
46
|
+
self.stream.write(f"{_INFO} {message}\n")
|
|
47
|
+
|
|
48
|
+
def section(self, title: str) -> None:
|
|
49
|
+
"""Write a section header to group the subsequent lines."""
|
|
50
|
+
self.stream.write(f"\n=== {title} ===\n")
|
|
51
|
+
|
|
52
|
+
def summary(self) -> None:
|
|
53
|
+
"""Write the final summary block with the passed/failed/warned counts."""
|
|
54
|
+
self.stream.write("\n=== Summary ===\n")
|
|
55
|
+
self.stream.write(f"{_INFO} Passed: {self.passed}\n")
|
|
56
|
+
self.stream.write(f"{_INFO} Failed: {self.failed}\n")
|
|
57
|
+
self.stream.write(f"{_INFO} Warnings: {self.warned}\n")
|
|
58
|
+
|
|
59
|
+
@property
|
|
60
|
+
def exit_code(self) -> int:
|
|
61
|
+
"""Process exit code: ``0`` when no check failed, else ``1``."""
|
|
62
|
+
return 0 if self.failed == 0 else 1
|