@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,368 @@
|
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
|
2
|
+
|
|
3
|
+
"""Flag registry capability cells not backed by install / materializer / projection evidence.
|
|
4
|
+
|
|
5
|
+
Why this validator exists. The harness registry
|
|
6
|
+
(``src/apothem/lib/harness_registry.py``) declares, per harness, a
|
|
7
|
+
capability matrix whose cells claim how each cohort class
|
|
8
|
+
(``commands``, ``skills``, ``hooks``, ``agents``, ``rules``,
|
|
9
|
+
``templates``, ``statuslines``, ``output_styles``) and each agentic
|
|
10
|
+
surface (``mcp_servers``, ``sub_agent_dispatch``,
|
|
11
|
+
``tool_surface_restrictions``, ``system_prompt_templates``,
|
|
12
|
+
``agent_memory``) is delivered. A cell that claims a delivery status
|
|
13
|
+
(``native`` / ``converted`` / ``support-tree`` / ``profile-projected``)
|
|
14
|
+
without a corresponding install entry, materializer, or documented
|
|
15
|
+
projection is a silent over-claim: the matrix advertises a surface the
|
|
16
|
+
adapter does not actually author. This validator is the mechanical guard
|
|
17
|
+
against that drift across the full 17-harness cohort.
|
|
18
|
+
|
|
19
|
+
What counts as evidence. A capability cell whose status is *not* in the
|
|
20
|
+
exempt set ``{unsupported, not-applicable, discovery-pending}`` MUST be
|
|
21
|
+
backed by one of:
|
|
22
|
+
|
|
23
|
+
- **A manifest install entry.** For a cohort class the propagation
|
|
24
|
+
manifest (``src/apothem/lib/propagation-manifest.yaml``) must carry an
|
|
25
|
+
``install`` entry whose ``source`` materializes that class — the cohort
|
|
26
|
+
source directory (``commands/`` … ``output-styles/``) for the cohort
|
|
27
|
+
classes, or the adapter's own instruction / rules template (one of the
|
|
28
|
+
registry entry's ``template_sources``) for the ``rules`` and
|
|
29
|
+
``system_prompt_templates`` surfaces.
|
|
30
|
+
- **A materializer that renders the surface.** An ``mcp_servers`` cell
|
|
31
|
+
claiming ``native`` MUST be backed by a sibling ``materializer.py`` that
|
|
32
|
+
exposes ``materialize_native_config`` and renders an MCP block — the
|
|
33
|
+
three authoring harnesses (opencode / qwen-code / hermes). An
|
|
34
|
+
operator-owned-recognized MCP surface (status ``discovery-pending``) is
|
|
35
|
+
exempt: apothem names but does not author it.
|
|
36
|
+
- **A documented projection.** ``sub_agent_dispatch`` is a harness
|
|
37
|
+
*behavior*, not a file the adapter writes; a present (non-exempt) status
|
|
38
|
+
is backed by the capabilities-dossier ``sub_agent_dispatch`` flag.
|
|
39
|
+
``agent_memory``, when not exempt, is likewise a projection-backed cell.
|
|
40
|
+
|
|
41
|
+
Detection. The validator imports the registry, reads the propagation
|
|
42
|
+
manifest, and inspects each harness sub-package for a materializer. For
|
|
43
|
+
each registry entry, every non-exempt cell is checked against the
|
|
44
|
+
evidence rules above; an unbacked cell produces a structured finding
|
|
45
|
+
naming the harness, the cell, its claimed status, and the missing
|
|
46
|
+
evidence class.
|
|
47
|
+
|
|
48
|
+
Exit semantics. Exits 0 when every non-exempt cell across all 15
|
|
49
|
+
harnesses is backed by evidence; exits 2 on any over-claim. The exit-2
|
|
50
|
+
convention matches the conformity-gate orchestrator's ``EXIT_FAIL``
|
|
51
|
+
constant.
|
|
52
|
+
"""
|
|
53
|
+
|
|
54
|
+
from __future__ import annotations
|
|
55
|
+
|
|
56
|
+
import json
|
|
57
|
+
import sys
|
|
58
|
+
from dataclasses import asdict, dataclass, field
|
|
59
|
+
from pathlib import Path
|
|
60
|
+
from typing import Any, Final
|
|
61
|
+
|
|
62
|
+
import yaml
|
|
63
|
+
|
|
64
|
+
GREP_NAME: Final[str] = "registry-capability-consistency-grep"
|
|
65
|
+
RULE_ANCHOR: Final[str] = (
|
|
66
|
+
"rules/agent-capability-discipline.md — registry capability cells backed by evidence"
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
EXIT_PASS: Final[int] = 0
|
|
70
|
+
EXIT_FAIL: Final[int] = 2
|
|
71
|
+
|
|
72
|
+
# Capability statuses that need no backing evidence: a surface apothem does
|
|
73
|
+
# not deliver (unsupported), an axis that does not apply to the harness
|
|
74
|
+
# (not-applicable), or an operator-owned surface apothem names but does not
|
|
75
|
+
# author (discovery-pending).
|
|
76
|
+
EXEMPT_STATUSES: Final[frozenset[str]] = frozenset(
|
|
77
|
+
{"unsupported", "not-applicable", "discovery-pending"}
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
# Cohort capability cells backed by a manifest install entry whose ``source``
|
|
81
|
+
# is the cohort source directory. The value is the manifest ``source`` prefix.
|
|
82
|
+
_COHORT_SOURCE: Final[dict[str, str]] = {
|
|
83
|
+
"commands": "commands/",
|
|
84
|
+
"skills": "skills/",
|
|
85
|
+
"hooks": "hooks/",
|
|
86
|
+
"agents": "agents/",
|
|
87
|
+
"templates": "templates/",
|
|
88
|
+
"statuslines": "statuslines/",
|
|
89
|
+
"output_styles": "output-styles/",
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
# The cohort rules source directory; the rules / system-prompt surfaces accept
|
|
93
|
+
# either this directory entry or the adapter's own instruction template.
|
|
94
|
+
_RULES_SOURCE: Final[str] = "rules/"
|
|
95
|
+
|
|
96
|
+
# The harness package keys whose ``materializer.py`` authors a native MCP
|
|
97
|
+
# block. The validator confirms the materializer is present and renders MCP
|
|
98
|
+
# rather than trusting the registry status alone.
|
|
99
|
+
_MATERIALIZER_FILENAME: Final[str] = "materializer.py"
|
|
100
|
+
_MATERIALIZER_FUNCTION: Final[str] = "materialize_native_config"
|
|
101
|
+
_MCP_RENDER_MARKERS: Final[tuple[str, ...]] = (
|
|
102
|
+
"render_mcp_opencode",
|
|
103
|
+
"render_mcp_standard",
|
|
104
|
+
"render_mcp",
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
@dataclass(frozen=True)
|
|
109
|
+
class Finding:
|
|
110
|
+
"""One over-claimed capability cell lacking backing evidence."""
|
|
111
|
+
|
|
112
|
+
harness: str
|
|
113
|
+
capability: str
|
|
114
|
+
status: str
|
|
115
|
+
detail: str
|
|
116
|
+
evidence_class: str
|
|
117
|
+
rule: str = RULE_ANCHOR
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
@dataclass(frozen=True)
|
|
121
|
+
class GrepResult:
|
|
122
|
+
"""Aggregated walk result for a single registry-consistency sweep."""
|
|
123
|
+
|
|
124
|
+
grep: str
|
|
125
|
+
root: str
|
|
126
|
+
harnesses_checked: int
|
|
127
|
+
cells_checked: int
|
|
128
|
+
passed: bool
|
|
129
|
+
findings: list[Finding] = field(default_factory=list)
|
|
130
|
+
|
|
131
|
+
def to_json(self) -> str:
|
|
132
|
+
payload = {
|
|
133
|
+
"grep": self.grep,
|
|
134
|
+
"root": self.root,
|
|
135
|
+
"harnesses_checked": self.harnesses_checked,
|
|
136
|
+
"cells_checked": self.cells_checked,
|
|
137
|
+
"passed": self.passed,
|
|
138
|
+
"findings": [asdict(f) for f in self.findings],
|
|
139
|
+
}
|
|
140
|
+
return json.dumps(payload, indent=2)
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def _manifest_path(root: Path) -> Path:
|
|
144
|
+
return root / "src" / "apothem" / "lib" / "propagation-manifest.yaml"
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def _load_manifest(root: Path) -> dict[str, Any]:
|
|
148
|
+
"""Load the propagation manifest. Missing / unparseable → empty mapping."""
|
|
149
|
+
path = _manifest_path(root)
|
|
150
|
+
try:
|
|
151
|
+
raw = path.read_text(encoding="utf-8")
|
|
152
|
+
except OSError:
|
|
153
|
+
return {}
|
|
154
|
+
try:
|
|
155
|
+
loaded = yaml.safe_load(raw)
|
|
156
|
+
except yaml.YAMLError:
|
|
157
|
+
return {}
|
|
158
|
+
return loaded if isinstance(loaded, dict) else {}
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
def _install_sources(manifest: dict[str, Any], package_key: str) -> set[str]:
|
|
162
|
+
"""Return the set of ``source`` values declared for a harness install list."""
|
|
163
|
+
harnesses = manifest.get("harnesses")
|
|
164
|
+
if not isinstance(harnesses, dict):
|
|
165
|
+
return set()
|
|
166
|
+
entry = harnesses.get(package_key)
|
|
167
|
+
if not isinstance(entry, dict):
|
|
168
|
+
return set()
|
|
169
|
+
install = entry.get("install")
|
|
170
|
+
if not isinstance(install, list):
|
|
171
|
+
return set()
|
|
172
|
+
sources: set[str] = set()
|
|
173
|
+
for item in install:
|
|
174
|
+
if isinstance(item, dict):
|
|
175
|
+
source = item.get("source")
|
|
176
|
+
if isinstance(source, str):
|
|
177
|
+
sources.add(source)
|
|
178
|
+
return sources
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
def _materializer_authors_mcp(root: Path, package_key: str) -> bool:
|
|
182
|
+
"""Return True when the harness sub-package materializes a native MCP block.
|
|
183
|
+
|
|
184
|
+
The harness's ``materializer.py`` must expose ``materialize_native_config``
|
|
185
|
+
and reference an MCP render helper; a markdown-template-only adapter has no
|
|
186
|
+
materializer and authors no MCP surface.
|
|
187
|
+
"""
|
|
188
|
+
path = root / "src" / "apothem" / "harnesses" / package_key / _MATERIALIZER_FILENAME
|
|
189
|
+
try:
|
|
190
|
+
text = path.read_text(encoding="utf-8")
|
|
191
|
+
except OSError:
|
|
192
|
+
return False
|
|
193
|
+
if _MATERIALIZER_FUNCTION not in text:
|
|
194
|
+
return False
|
|
195
|
+
return any(marker in text for marker in _MCP_RENDER_MARKERS)
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
def _rules_backed(sources: set[str], template_sources: set[str]) -> bool:
|
|
199
|
+
"""A rules / system-prompt surface is backed by the cohort rules dir or a template."""
|
|
200
|
+
if _RULES_SOURCE in sources:
|
|
201
|
+
return True
|
|
202
|
+
return bool(template_sources & sources)
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
def check(root: Path) -> GrepResult:
|
|
206
|
+
"""Walk the registry under ``root``; flag capability cells lacking evidence.
|
|
207
|
+
|
|
208
|
+
Imports the registry lazily so the module loads cleanly even when the
|
|
209
|
+
package is mounted at an installed layout whose ``sys.path`` differs from
|
|
210
|
+
the repository checkout. When the registry cannot be imported the sweep
|
|
211
|
+
degrades to an informational pass (zero cells checked) rather than a hard
|
|
212
|
+
error — the gate must not fail-close on an import boundary it cannot reach.
|
|
213
|
+
"""
|
|
214
|
+
try:
|
|
215
|
+
from apothem.lib.harness_registry import (
|
|
216
|
+
HARNESS_REGISTRY,
|
|
217
|
+
REQUIRED_CAPABILITIES,
|
|
218
|
+
)
|
|
219
|
+
except Exception: # import boundary: degrade to informational pass
|
|
220
|
+
return GrepResult(
|
|
221
|
+
grep=GREP_NAME,
|
|
222
|
+
root=str(root),
|
|
223
|
+
harnesses_checked=0,
|
|
224
|
+
cells_checked=0,
|
|
225
|
+
passed=True,
|
|
226
|
+
findings=[],
|
|
227
|
+
)
|
|
228
|
+
|
|
229
|
+
manifest = _load_manifest(root)
|
|
230
|
+
findings: list[Finding] = []
|
|
231
|
+
harnesses_checked = 0
|
|
232
|
+
cells_checked = 0
|
|
233
|
+
|
|
234
|
+
for entry in HARNESS_REGISTRY:
|
|
235
|
+
harnesses_checked += 1
|
|
236
|
+
package_key = entry.package_key
|
|
237
|
+
sources = _install_sources(manifest, package_key)
|
|
238
|
+
template_sources = set(entry.template_sources)
|
|
239
|
+
authors_mcp = _materializer_authors_mcp(root, package_key)
|
|
240
|
+
|
|
241
|
+
for capability in REQUIRED_CAPABILITIES:
|
|
242
|
+
status = entry.capability_status[capability]
|
|
243
|
+
if status in EXEMPT_STATUSES:
|
|
244
|
+
continue
|
|
245
|
+
cells_checked += 1
|
|
246
|
+
finding = _classify_cell(
|
|
247
|
+
harness=entry.public_id,
|
|
248
|
+
capability=capability,
|
|
249
|
+
status=status,
|
|
250
|
+
sources=sources,
|
|
251
|
+
template_sources=template_sources,
|
|
252
|
+
authors_mcp=authors_mcp,
|
|
253
|
+
)
|
|
254
|
+
if finding is not None:
|
|
255
|
+
findings.append(finding)
|
|
256
|
+
|
|
257
|
+
return GrepResult(
|
|
258
|
+
grep=GREP_NAME,
|
|
259
|
+
root=str(root),
|
|
260
|
+
harnesses_checked=harnesses_checked,
|
|
261
|
+
cells_checked=cells_checked,
|
|
262
|
+
passed=not findings,
|
|
263
|
+
findings=findings,
|
|
264
|
+
)
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
def _classify_cell(
|
|
268
|
+
*,
|
|
269
|
+
harness: str,
|
|
270
|
+
capability: str,
|
|
271
|
+
status: str,
|
|
272
|
+
sources: set[str],
|
|
273
|
+
template_sources: set[str],
|
|
274
|
+
authors_mcp: bool,
|
|
275
|
+
) -> Finding | None:
|
|
276
|
+
"""Return a Finding when *capability* is over-claimed, else None.
|
|
277
|
+
|
|
278
|
+
The capability is matched against its evidence class. A backed cell
|
|
279
|
+
returns None; an unbacked cell returns a structured finding naming the
|
|
280
|
+
missing evidence class.
|
|
281
|
+
"""
|
|
282
|
+
# Cohort classes delivered through a manifest install source directory.
|
|
283
|
+
if capability in _COHORT_SOURCE:
|
|
284
|
+
if _COHORT_SOURCE[capability] in sources:
|
|
285
|
+
return None
|
|
286
|
+
return Finding(
|
|
287
|
+
harness=harness,
|
|
288
|
+
capability=capability,
|
|
289
|
+
status=status,
|
|
290
|
+
detail=(
|
|
291
|
+
f"capability claims '{status}' but no propagation-manifest "
|
|
292
|
+
f"install entry sources '{_COHORT_SOURCE[capability]}' for "
|
|
293
|
+
f"this harness"
|
|
294
|
+
),
|
|
295
|
+
evidence_class="manifest-install-entry",
|
|
296
|
+
)
|
|
297
|
+
|
|
298
|
+
# The rules surface and the system-prompt template surface: backed by the
|
|
299
|
+
# cohort rules directory or the adapter's own instruction template.
|
|
300
|
+
if capability in {"rules", "system_prompt_templates"}:
|
|
301
|
+
if _rules_backed(sources, template_sources):
|
|
302
|
+
return None
|
|
303
|
+
return Finding(
|
|
304
|
+
harness=harness,
|
|
305
|
+
capability=capability,
|
|
306
|
+
status=status,
|
|
307
|
+
detail=(
|
|
308
|
+
f"capability claims '{status}' but no install entry sources "
|
|
309
|
+
f"'{_RULES_SOURCE}' nor any declared instruction template "
|
|
310
|
+
f"({sorted(template_sources) or 'none'}) for this harness"
|
|
311
|
+
),
|
|
312
|
+
evidence_class="manifest-install-entry",
|
|
313
|
+
)
|
|
314
|
+
|
|
315
|
+
# The MCP surface: a 'native' cell requires a materializer that authors MCP.
|
|
316
|
+
if capability == "mcp_servers":
|
|
317
|
+
if authors_mcp:
|
|
318
|
+
return None
|
|
319
|
+
return Finding(
|
|
320
|
+
harness=harness,
|
|
321
|
+
capability=capability,
|
|
322
|
+
status=status,
|
|
323
|
+
detail=(
|
|
324
|
+
f"capability claims '{status}' but no sibling "
|
|
325
|
+
f"{_MATERIALIZER_FILENAME} exposes {_MATERIALIZER_FUNCTION} "
|
|
326
|
+
f"rendering a native MCP block for this harness"
|
|
327
|
+
),
|
|
328
|
+
evidence_class="materializer",
|
|
329
|
+
)
|
|
330
|
+
|
|
331
|
+
# Documented-projection cells: harness behaviors, not adapter-written files.
|
|
332
|
+
# A present (non-exempt) status is itself the documented projection.
|
|
333
|
+
if capability in {
|
|
334
|
+
"sub_agent_dispatch",
|
|
335
|
+
"tool_surface_restrictions",
|
|
336
|
+
"agent_memory",
|
|
337
|
+
}:
|
|
338
|
+
return None
|
|
339
|
+
|
|
340
|
+
# An unrecognized capability axis with a non-exempt status: surface it so a
|
|
341
|
+
# new axis cannot slip past the gate unbacked.
|
|
342
|
+
return Finding(
|
|
343
|
+
harness=harness,
|
|
344
|
+
capability=capability,
|
|
345
|
+
status=status,
|
|
346
|
+
detail=(
|
|
347
|
+
f"capability claims '{status}' but the validator recognizes no "
|
|
348
|
+
f"evidence class for this axis"
|
|
349
|
+
),
|
|
350
|
+
evidence_class="unrecognized-axis",
|
|
351
|
+
)
|
|
352
|
+
|
|
353
|
+
|
|
354
|
+
def _read_input(argv: list[str]) -> Path:
|
|
355
|
+
if len(argv) >= 2:
|
|
356
|
+
return Path(argv[1])
|
|
357
|
+
return Path.cwd()
|
|
358
|
+
|
|
359
|
+
|
|
360
|
+
def _main(argv: list[str]) -> int:
|
|
361
|
+
root = _read_input(argv)
|
|
362
|
+
result = check(root)
|
|
363
|
+
print(result.to_json())
|
|
364
|
+
return EXIT_PASS if result.passed else EXIT_FAIL
|
|
365
|
+
|
|
366
|
+
|
|
367
|
+
if __name__ == "__main__":
|
|
368
|
+
sys.exit(_main(sys.argv))
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
|
2
|
+
|
|
3
|
+
"""Block emission of artifacts containing hardcoded credentials.
|
|
4
|
+
|
|
5
|
+
Why this enforcement exists. The production-ready discipline M15 + the
|
|
6
|
+
code-craft security sub-discipline M13.8 both forbid hardcoded secrets in
|
|
7
|
+
source. A leaked AWS access key, GitHub token, JWT, or RSA private key
|
|
8
|
+
costs hours of credential rotation and may produce a real-world breach.
|
|
9
|
+
The pre-emission gate's mechanical bar 15 (M15 supply-chain) catches the literal patterns
|
|
10
|
+
distinctive enough that false-positive rate is acceptable while
|
|
11
|
+
true-positive coverage is high. The patterns curated below are the
|
|
12
|
+
common vendor-issued credential prefixes plus a generic high-entropy
|
|
13
|
+
fallback.
|
|
14
|
+
|
|
15
|
+
Detection strategy. The grep applies a list of named patterns each
|
|
16
|
+
tagged with the credential class. A hit on any pattern produces a
|
|
17
|
+
finding with the line, the redacted match (first eight characters
|
|
18
|
+
plus ellipsis to avoid re-leaking the secret in the report), and the
|
|
19
|
+
class label. Fenced code blocks are NOT excluded — secrets pasted
|
|
20
|
+
into a code block are still secrets in source control. The
|
|
21
|
+
canonical-banner allow-list now lives here (merged from the retired
|
|
22
|
+
secret-scan matcher): lines carrying a verbatim authorship-banner
|
|
23
|
+
identifier are exempted from both the pattern and entropy checks.
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
from __future__ import annotations
|
|
27
|
+
|
|
28
|
+
import math
|
|
29
|
+
import re
|
|
30
|
+
import sys
|
|
31
|
+
from dataclasses import dataclass
|
|
32
|
+
from pathlib import Path
|
|
33
|
+
from typing import Final
|
|
34
|
+
|
|
35
|
+
from apothem.conformity._grep_base import GrepResult, run_grep
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
@dataclass(frozen=True)
|
|
39
|
+
class SecretPattern:
|
|
40
|
+
"""One named credential-class regex with its display label."""
|
|
41
|
+
|
|
42
|
+
label: str
|
|
43
|
+
pattern: re.Pattern[str]
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
# Curated credential-class patterns. Each label names the issuer or class
|
|
47
|
+
# so the operator can route the rotation. Patterns are anchored on the
|
|
48
|
+
# distinctive vendor prefix where one exists; the entropy heuristic catches
|
|
49
|
+
# the rest.
|
|
50
|
+
SECRET_PATTERNS: Final[tuple[SecretPattern, ...]] = (
|
|
51
|
+
SecretPattern("AWS access key", re.compile(r"\bAKIA[0-9A-Z]{16}\b")),
|
|
52
|
+
SecretPattern(
|
|
53
|
+
"GitHub personal access token", re.compile(r"\bghp_[A-Za-z0-9]{36}\b")
|
|
54
|
+
),
|
|
55
|
+
SecretPattern("GitHub OAuth access token", re.compile(r"\bgho_[A-Za-z0-9]{36}\b")),
|
|
56
|
+
SecretPattern(
|
|
57
|
+
"GitHub server-to-server token", re.compile(r"\bghs_[A-Za-z0-9]{36}\b")
|
|
58
|
+
),
|
|
59
|
+
SecretPattern(
|
|
60
|
+
"GitHub app/refresh token",
|
|
61
|
+
re.compile(r"\b(?:ghu|ghr)_[A-Za-z0-9]{36}\b"),
|
|
62
|
+
),
|
|
63
|
+
SecretPattern("OpenAI-style API key", re.compile(r"\bsk-[A-Za-z0-9]{32,}\b")),
|
|
64
|
+
SecretPattern("Google API key", re.compile(r"\bAIza[0-9A-Za-z_\-]{35}\b")),
|
|
65
|
+
SecretPattern(
|
|
66
|
+
"JWT (JSON Web Token)",
|
|
67
|
+
re.compile(r"\beyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\b"),
|
|
68
|
+
),
|
|
69
|
+
SecretPattern(
|
|
70
|
+
"RSA private key header",
|
|
71
|
+
re.compile(r"-----BEGIN (?:RSA |OPENSSH |EC |DSA |PGP )?PRIVATE KEY-----"),
|
|
72
|
+
),
|
|
73
|
+
SecretPattern("Slack token", re.compile(r"\bxox[abprs]-[A-Za-z0-9-]{10,}\b")),
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
# Canonical-banner identifiers. Lines containing any of these substrings
|
|
77
|
+
# are exempted from secret findings (both pattern and entropy checks)
|
|
78
|
+
# because the banner is a verbatim authorship surface, not a credential.
|
|
79
|
+
# Merged from the retired secret-scan matcher.
|
|
80
|
+
BANNER_ALLOW_LIST: Final[tuple[str, ...]] = (
|
|
81
|
+
"ahmedgad.com",
|
|
82
|
+
"me@ahmedgad.com",
|
|
83
|
+
"github.com/ahmed-g-gad",
|
|
84
|
+
"Ahmed G. Gad",
|
|
85
|
+
"@ahmed-g-gad",
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
# Generic high-entropy heuristic. A standalone alphanumeric token of this
|
|
89
|
+
# minimum length whose Shannon entropy exceeds the threshold is suspect.
|
|
90
|
+
# These thresholds favor precision over recall — common identifier shapes
|
|
91
|
+
# (kebab-case names, hex-coded constants) sit well below the entropy bar.
|
|
92
|
+
ENTROPY_TOKEN_MIN_LENGTH: Final[int] = 32
|
|
93
|
+
ENTROPY_BITS_PER_CHAR_THRESHOLD: Final[float] = 4.5
|
|
94
|
+
ENTROPY_TOKEN_RE: Final[re.Pattern[str]] = re.compile(r"\b[A-Za-z0-9_+/=-]{32,}\b")
|
|
95
|
+
|
|
96
|
+
# How many leading characters to keep when redacting the matched value
|
|
97
|
+
# in the finding report. Leaking the full secret in the audit log defeats
|
|
98
|
+
# the grep's purpose.
|
|
99
|
+
REDACT_PREFIX_LENGTH: Final[int] = 8
|
|
100
|
+
|
|
101
|
+
GREP_NAME: Final[str] = "secret-leak-grep"
|
|
102
|
+
RULE_ANCHOR: Final[str] = "M13.8 security + M15 production-ready"
|
|
103
|
+
EXIT_PASS: Final[int] = 0
|
|
104
|
+
EXIT_FAIL: Final[int] = 2
|
|
105
|
+
STDIN_FLAG: Final[str] = "--stdin"
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
@dataclass(frozen=True)
|
|
109
|
+
class Finding:
|
|
110
|
+
"""One credential-leak occurrence."""
|
|
111
|
+
|
|
112
|
+
line: int
|
|
113
|
+
label: str
|
|
114
|
+
redacted_match: str
|
|
115
|
+
rule: str = RULE_ANCHOR
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def _line_in_banner(line: str) -> bool:
|
|
119
|
+
"""True iff the line carries a canonical-banner identifier."""
|
|
120
|
+
return any(token in line for token in BANNER_ALLOW_LIST)
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def _redact(value: str) -> str:
|
|
124
|
+
"""Show the first few characters; mask the rest."""
|
|
125
|
+
if len(value) <= REDACT_PREFIX_LENGTH:
|
|
126
|
+
return "***"
|
|
127
|
+
return f"{value[:REDACT_PREFIX_LENGTH]}…(redacted, {len(value)} chars)"
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def _shannon_entropy_bits_per_char(value: str) -> float:
|
|
131
|
+
"""Compute Shannon entropy in bits per character."""
|
|
132
|
+
if not value:
|
|
133
|
+
return 0.0
|
|
134
|
+
counts: dict[str, int] = {}
|
|
135
|
+
for char in value:
|
|
136
|
+
counts[char] = counts.get(char, 0) + 1
|
|
137
|
+
total = len(value)
|
|
138
|
+
return -sum((c / total) * math.log2(c / total) for c in counts.values())
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def check(content: str, path: Path | None = None) -> GrepResult:
|
|
142
|
+
"""Scan content; return a structured result.
|
|
143
|
+
|
|
144
|
+
Pre-conditions: `content` is the artifact body about to be emitted.
|
|
145
|
+
Post-conditions: `result.passed` is True iff zero credential patterns
|
|
146
|
+
matched and no high-entropy token exceeded the heuristic threshold.
|
|
147
|
+
"""
|
|
148
|
+
findings: list[Finding] = []
|
|
149
|
+
lines = content.splitlines()
|
|
150
|
+
for line_index, line in enumerate(lines, start=1):
|
|
151
|
+
# Canonical-banner allow-list: a line carrying a verbatim
|
|
152
|
+
# authorship-banner identifier is exempted from both the pattern
|
|
153
|
+
# and entropy checks (merged from the retired secret-scan matcher).
|
|
154
|
+
if _line_in_banner(line):
|
|
155
|
+
continue
|
|
156
|
+
for secret in SECRET_PATTERNS:
|
|
157
|
+
for match in secret.pattern.finditer(line):
|
|
158
|
+
findings.append(
|
|
159
|
+
Finding(
|
|
160
|
+
line=line_index,
|
|
161
|
+
label=secret.label,
|
|
162
|
+
redacted_match=_redact(match.group()),
|
|
163
|
+
)
|
|
164
|
+
)
|
|
165
|
+
# Generic high-entropy fallback. Skip lines already covered by a
|
|
166
|
+
# named pattern to avoid double-reporting the same token.
|
|
167
|
+
if any(s.pattern.search(line) for s in SECRET_PATTERNS):
|
|
168
|
+
continue
|
|
169
|
+
for match in ENTROPY_TOKEN_RE.finditer(line):
|
|
170
|
+
token = match.group()
|
|
171
|
+
entropy = _shannon_entropy_bits_per_char(token)
|
|
172
|
+
if entropy >= ENTROPY_BITS_PER_CHAR_THRESHOLD:
|
|
173
|
+
findings.append(
|
|
174
|
+
Finding(
|
|
175
|
+
line=line_index,
|
|
176
|
+
label=(
|
|
177
|
+
f"high-entropy token "
|
|
178
|
+
f"({entropy:.2f} bits/char ≥ "
|
|
179
|
+
f"{ENTROPY_BITS_PER_CHAR_THRESHOLD})"
|
|
180
|
+
),
|
|
181
|
+
redacted_match=_redact(token),
|
|
182
|
+
)
|
|
183
|
+
)
|
|
184
|
+
return GrepResult(
|
|
185
|
+
grep=GREP_NAME,
|
|
186
|
+
path=str(path) if path is not None else None,
|
|
187
|
+
passed=not findings,
|
|
188
|
+
findings=findings,
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
if __name__ == "__main__":
|
|
193
|
+
sys.exit(run_grep(check, sys.argv))
|