@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,382 @@
|
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
|
2
|
+
|
|
3
|
+
"""Flag stale agent-companion files under the root-only AGENTS.md convention.
|
|
4
|
+
|
|
5
|
+
Why this enforcement exists. The repository carries a single agent-facing
|
|
6
|
+
canon at the root ``AGENTS.md``; per-folder operating guidance lives in each
|
|
7
|
+
folder's ``README.md``, which serves both the human reader and the agent
|
|
8
|
+
reader. Per-folder ``AGENTS.md`` companions are no longer required — a
|
|
9
|
+
meaningful folder without one passes. The one defect this sweep still guards
|
|
10
|
+
is staleness: a per-folder companion that an author kept (the rare standalone
|
|
11
|
+
companion the convention permits) must not fall behind its folder after the
|
|
12
|
+
folder's artifacts or conventions changed. An absent companion is never a
|
|
13
|
+
finding; only a present, committed-but-stale companion is.
|
|
14
|
+
|
|
15
|
+
The meaningful-folder set. A folder is *meaningful* when it is navigable —
|
|
16
|
+
a contributor or an agent reasons about it as a unit — and load-bearing. The
|
|
17
|
+
enumeration is the navigable-folder set the README convention applies to; it
|
|
18
|
+
is retained here as the source-of-truth for the freshness check's
|
|
19
|
+
folder-ownership map and is reported as ``folders_inspected`` for information.
|
|
20
|
+
The set is the union of two reproducible rules, minus a fixed exclusion list:
|
|
21
|
+
|
|
22
|
+
- **(A) Companion-anchored.** Any folder that already carries a contributor
|
|
23
|
+
``README.md`` directly. Test-fixture leaf dirs (whose README is itself the
|
|
24
|
+
subject of a presence check) and single-artifact example scaffolds are
|
|
25
|
+
excluded — their README documents a fixture, not a navigable subsystem.
|
|
26
|
+
- **(B) Source-package.** Any ``src/apothem`` folder that directly contains a
|
|
27
|
+
Python source file but carries no README — the harness adapter packages,
|
|
28
|
+
the shared adapter helpers, the hook library. An agent operating inside one
|
|
29
|
+
of these packages needs the same orientation a README-bearing folder gives.
|
|
30
|
+
|
|
31
|
+
Constant exclusions across both rules: vendored trees, generated trees, the
|
|
32
|
+
git/cache/ephemera directories, rendered harness-output template leaves, and
|
|
33
|
+
the plan-suite scratch. Including any of those would pollute fixtures or
|
|
34
|
+
describe machinery no contributor navigates by hand. Materialized
|
|
35
|
+
harness-output ``AGENTS.md`` files under ``src/apothem/harnesses/*/templates/``
|
|
36
|
+
are product surfaces, not companions, and are excluded by the same rule.
|
|
37
|
+
|
|
38
|
+
Freshness. A *present* per-folder companion is *stale* when its folder's
|
|
39
|
+
directly-contained artifacts were last committed more recently than the
|
|
40
|
+
companion itself. The signal is the git commit timestamp, not the filesystem
|
|
41
|
+
mtime: a checkout rewrites mtimes, so only commit history carries the truth of
|
|
42
|
+
"what changed when". A single ``git log`` pass buckets every tracked file to
|
|
43
|
+
its nearest meaningful-folder ancestor; a folder is stale when its newest
|
|
44
|
+
content commit postdates its committed companion's commit. Folders with no
|
|
45
|
+
companion on disk are skipped entirely — absence is not a finding under the
|
|
46
|
+
root-only convention. Where git history is unavailable (a shallow clone, a
|
|
47
|
+
non-git tree), freshness is skipped and the sweep reports a clean pass — it
|
|
48
|
+
never blocks a fresh clone.
|
|
49
|
+
|
|
50
|
+
Posture. The result is advisory: the sweep surfaces findings and never
|
|
51
|
+
silent-blocks by default, honoring the agnostic gate posture. The orchestrator
|
|
52
|
+
reads the JSON ``advisory: true`` flag plus the inner ``passed`` field — a
|
|
53
|
+
stale-companion finding surfaces as ``advisory_findings_present`` without
|
|
54
|
+
gating the ``gate --all`` run. CI opts into strict enforcement separately.
|
|
55
|
+
``_main`` always exits 0 so the advisory posture holds regardless of findings;
|
|
56
|
+
the verdict travels in the JSON, not the exit code.
|
|
57
|
+
"""
|
|
58
|
+
|
|
59
|
+
from __future__ import annotations
|
|
60
|
+
|
|
61
|
+
import json
|
|
62
|
+
import subprocess
|
|
63
|
+
import sys
|
|
64
|
+
from dataclasses import asdict, dataclass, field
|
|
65
|
+
from pathlib import Path
|
|
66
|
+
from typing import Final
|
|
67
|
+
|
|
68
|
+
GREP_NAME: Final[str] = "agents-md-coverage-grep"
|
|
69
|
+
RULE_ANCHOR: Final[str] = "rules/agents-md-convention.md"
|
|
70
|
+
|
|
71
|
+
# Advisory posture: the sweep always exits 0 (the verdict travels in the JSON
|
|
72
|
+
# ``advisory`` / ``passed`` fields), so only the pass code is needed here.
|
|
73
|
+
EXIT_PASS: Final[int] = 0
|
|
74
|
+
|
|
75
|
+
# The agent-companion filename and its human-facing sibling.
|
|
76
|
+
COMPANION_NAME: Final[str] = "AGENTS.md"
|
|
77
|
+
README_NAME: Final[str] = "README.md"
|
|
78
|
+
|
|
79
|
+
# Bound the git invocation so a hung `git log` cannot stall the sweep; a
|
|
80
|
+
# timeout is treated as a transient git error and degrades to a clean pass.
|
|
81
|
+
GIT_TIMEOUT_SECONDS: Final[int] = 10
|
|
82
|
+
|
|
83
|
+
# Path segments that mark a directory as out of scope wherever they appear.
|
|
84
|
+
# Vendored trees, generated output, caches, and ephemera are never navigable
|
|
85
|
+
# subsystems an agent reasons about by hand.
|
|
86
|
+
_EXCLUDED_SEGMENTS: Final[frozenset[str]] = frozenset(
|
|
87
|
+
{
|
|
88
|
+
".git",
|
|
89
|
+
"node_modules",
|
|
90
|
+
"_vendor",
|
|
91
|
+
"dist",
|
|
92
|
+
".audit",
|
|
93
|
+
".plans",
|
|
94
|
+
".hypothesis",
|
|
95
|
+
".mypy_cache",
|
|
96
|
+
".pytest_cache",
|
|
97
|
+
".ruff_cache",
|
|
98
|
+
".venv",
|
|
99
|
+
"venv",
|
|
100
|
+
"__pycache__",
|
|
101
|
+
}
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
# Relative-path prefixes whose subtree is excluded as a whole.
|
|
105
|
+
_EXCLUDED_PREFIXES: Final[tuple[str, ...]] = (
|
|
106
|
+
"site/dist",
|
|
107
|
+
"site/node_modules",
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
# Rule-(A) carve-outs: a README under one of these is a fixture/example
|
|
111
|
+
# README, not a navigable-subsystem README. Matched as path substrings on the
|
|
112
|
+
# POSIX-relative folder path.
|
|
113
|
+
_FIXTURE_FOLDER_MARKERS: Final[tuple[str, ...]] = (
|
|
114
|
+
"tests/scripts/inject-header/fixtures",
|
|
115
|
+
"examples/minimal-",
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
# Rule-(B) scope: source-package folders live under this prefix.
|
|
119
|
+
_SOURCE_PACKAGE_PREFIX: Final[str] = "src/apothem"
|
|
120
|
+
_PYTHON_SUFFIX: Final[str] = ".py"
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
@dataclass(frozen=True)
|
|
124
|
+
class Finding:
|
|
125
|
+
"""One stale present per-folder agent-companion file."""
|
|
126
|
+
|
|
127
|
+
surface: str
|
|
128
|
+
kind: str
|
|
129
|
+
detail: str
|
|
130
|
+
rule: str = RULE_ANCHOR
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
@dataclass(frozen=True)
|
|
134
|
+
class GrepResult:
|
|
135
|
+
"""Aggregated coverage result for a single sweep."""
|
|
136
|
+
|
|
137
|
+
grep: str
|
|
138
|
+
root: str
|
|
139
|
+
folders_inspected: int
|
|
140
|
+
freshness_checked: bool
|
|
141
|
+
passed: bool
|
|
142
|
+
findings: list[Finding] = field(default_factory=list)
|
|
143
|
+
|
|
144
|
+
def to_json(self) -> str:
|
|
145
|
+
payload = {
|
|
146
|
+
"grep": self.grep,
|
|
147
|
+
"root": self.root,
|
|
148
|
+
"folders_inspected": self.folders_inspected,
|
|
149
|
+
"freshness_checked": self.freshness_checked,
|
|
150
|
+
"passed": self.passed,
|
|
151
|
+
"findings": [asdict(f) for f in self.findings],
|
|
152
|
+
"advisory": True,
|
|
153
|
+
}
|
|
154
|
+
return json.dumps(payload, indent=2)
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
def _has_excluded_segment(rel_posix: str) -> bool:
|
|
158
|
+
"""Return True iff any path segment marks the path out of scope."""
|
|
159
|
+
if not rel_posix or rel_posix == ".":
|
|
160
|
+
return False
|
|
161
|
+
if any(rel_posix == p or rel_posix.startswith(p + "/") for p in _EXCLUDED_PREFIXES):
|
|
162
|
+
return True
|
|
163
|
+
return any(segment in _EXCLUDED_SEGMENTS for segment in rel_posix.split("/"))
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
def _is_fixture_folder(rel_posix: str) -> bool:
|
|
167
|
+
"""Return True iff a folder is a test-fixture or single-artifact example leaf."""
|
|
168
|
+
if "/" in rel_posix:
|
|
169
|
+
parent, _, leaf = rel_posix.rpartition("/")
|
|
170
|
+
# tests/conformity/<matcher>/{fail,pass} — the README is the grep subject.
|
|
171
|
+
if leaf in {"fail", "pass"} and parent.startswith("tests/conformity/"):
|
|
172
|
+
return True
|
|
173
|
+
return any(marker in rel_posix for marker in _FIXTURE_FOLDER_MARKERS)
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
def _is_harness_template_leaf(rel_posix: str) -> bool:
|
|
177
|
+
"""Return True iff a folder is a rendered harness-output template leaf."""
|
|
178
|
+
parts = rel_posix.split("/")
|
|
179
|
+
# src/apothem/harnesses/<name>/templates[/...]
|
|
180
|
+
return (
|
|
181
|
+
len(parts) >= 5
|
|
182
|
+
and parts[:3] == ["src", "apothem", "harnesses"]
|
|
183
|
+
and "templates" in parts[4:5]
|
|
184
|
+
)
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
def _rel_posix(path: Path, root_resolved: Path) -> str | None:
|
|
188
|
+
try:
|
|
189
|
+
rel = path.resolve().relative_to(root_resolved).as_posix()
|
|
190
|
+
except ValueError:
|
|
191
|
+
return None
|
|
192
|
+
return "." if rel == "" else rel
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
def meaningful_folders(root: Path) -> list[str]:
|
|
196
|
+
"""Enumerate the meaningful-folder set under ``root``.
|
|
197
|
+
|
|
198
|
+
Pre-conditions: ``root`` is the repository root (or a subtree).
|
|
199
|
+
Post-conditions: returns the sorted, deduplicated POSIX-relative folder
|
|
200
|
+
paths (``.`` denotes the root) that satisfy rule (A) or rule (B) and pass
|
|
201
|
+
the constant exclusions. Re-running over an unchanged tree yields the
|
|
202
|
+
identical set — the enumeration is the single source of truth the
|
|
203
|
+
presence/freshness check and the documentation convention both consume.
|
|
204
|
+
"""
|
|
205
|
+
root_resolved = root.resolve()
|
|
206
|
+
folders: set[str] = set()
|
|
207
|
+
|
|
208
|
+
# Rule (A): folders carrying a contributor README, minus fixture/example leaves.
|
|
209
|
+
for readme in root.rglob(README_NAME):
|
|
210
|
+
if not readme.is_file():
|
|
211
|
+
continue
|
|
212
|
+
rel = _rel_posix(readme.parent, root_resolved)
|
|
213
|
+
if rel is None or _has_excluded_segment(rel):
|
|
214
|
+
continue
|
|
215
|
+
if _is_fixture_folder(rel):
|
|
216
|
+
continue
|
|
217
|
+
folders.add(rel)
|
|
218
|
+
|
|
219
|
+
# Rule (B): src/apothem source-package folders without a README, minus
|
|
220
|
+
# vendored trees and rendered harness-template leaves.
|
|
221
|
+
for py_file in root.rglob(f"*{_PYTHON_SUFFIX}"):
|
|
222
|
+
if not py_file.is_file():
|
|
223
|
+
continue
|
|
224
|
+
rel = _rel_posix(py_file.parent, root_resolved)
|
|
225
|
+
if rel is None or rel == ".":
|
|
226
|
+
continue
|
|
227
|
+
if not (
|
|
228
|
+
rel == _SOURCE_PACKAGE_PREFIX
|
|
229
|
+
or rel.startswith(_SOURCE_PACKAGE_PREFIX + "/")
|
|
230
|
+
):
|
|
231
|
+
continue
|
|
232
|
+
if _has_excluded_segment(rel) or _is_harness_template_leaf(rel):
|
|
233
|
+
continue
|
|
234
|
+
if (py_file.parent / README_NAME).is_file():
|
|
235
|
+
continue
|
|
236
|
+
folders.add(rel)
|
|
237
|
+
|
|
238
|
+
return sorted(folders)
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
def _last_commit_times(root: Path) -> dict[str, int] | None:
|
|
242
|
+
"""Return a map of tracked-file POSIX path → last-commit unix timestamp.
|
|
243
|
+
|
|
244
|
+
One ``git log`` pass, newest commit first: the first time a path is seen
|
|
245
|
+
is its last-touching commit. Returns None when git history is unavailable
|
|
246
|
+
(no repository, shallow clone with no log, git absent, or timeout) so the
|
|
247
|
+
caller degrades to a presence-only sweep.
|
|
248
|
+
"""
|
|
249
|
+
try:
|
|
250
|
+
completed = subprocess.run(
|
|
251
|
+
[
|
|
252
|
+
"git",
|
|
253
|
+
"-C",
|
|
254
|
+
str(root),
|
|
255
|
+
"log",
|
|
256
|
+
"--format=%x1e%ct",
|
|
257
|
+
"--name-only",
|
|
258
|
+
"--no-renames",
|
|
259
|
+
],
|
|
260
|
+
capture_output=True,
|
|
261
|
+
text=True,
|
|
262
|
+
timeout=GIT_TIMEOUT_SECONDS,
|
|
263
|
+
check=False,
|
|
264
|
+
)
|
|
265
|
+
except (OSError, subprocess.SubprocessError):
|
|
266
|
+
return None
|
|
267
|
+
if completed.returncode != 0 or not completed.stdout:
|
|
268
|
+
return None
|
|
269
|
+
|
|
270
|
+
times: dict[str, int] = {}
|
|
271
|
+
current_ct: int | None = None
|
|
272
|
+
for raw_line in completed.stdout.splitlines():
|
|
273
|
+
if raw_line.startswith("\x1e"):
|
|
274
|
+
stamp = raw_line[1:].strip()
|
|
275
|
+
current_ct = int(stamp) if stamp.isdigit() else None
|
|
276
|
+
continue
|
|
277
|
+
path = raw_line.strip()
|
|
278
|
+
if not path or current_ct is None:
|
|
279
|
+
continue
|
|
280
|
+
# Newest-first: keep only the first (most recent) commit per path.
|
|
281
|
+
if path not in times:
|
|
282
|
+
times[path] = current_ct
|
|
283
|
+
return times
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
def _owning_folder(file_posix: str, folder_set: frozenset[str]) -> str:
|
|
287
|
+
"""Map a tracked file to its nearest meaningful-folder ancestor.
|
|
288
|
+
|
|
289
|
+
A top-level file maps to ``.``. A file under a non-meaningful subdir walks
|
|
290
|
+
up to the nearest meaningful ancestor; a file under a child meaningful
|
|
291
|
+
folder is owned by that child, never the parent — ownership is disjoint.
|
|
292
|
+
"""
|
|
293
|
+
parent = file_posix.rpartition("/")[0]
|
|
294
|
+
while parent:
|
|
295
|
+
if parent in folder_set:
|
|
296
|
+
return parent
|
|
297
|
+
parent = parent.rpartition("/")[0]
|
|
298
|
+
return "."
|
|
299
|
+
|
|
300
|
+
|
|
301
|
+
def check(root: Path) -> GrepResult:
|
|
302
|
+
"""Enumerate meaningful folders; flag only present, stale companions.
|
|
303
|
+
|
|
304
|
+
Pre-conditions: ``root`` is the repository root (or an arbitrary subtree).
|
|
305
|
+
Post-conditions: under the root-only convention, a meaningful folder
|
|
306
|
+
without a per-folder ``AGENTS.md`` is never a finding — absence passes.
|
|
307
|
+
``result.passed`` is True iff (when git history is available) no *present*,
|
|
308
|
+
committed per-folder companion is stale relative to its folder's
|
|
309
|
+
directly-contained artifacts; it is always True when git history is
|
|
310
|
+
unavailable.
|
|
311
|
+
"""
|
|
312
|
+
folders = meaningful_folders(root)
|
|
313
|
+
folder_set = frozenset(folders)
|
|
314
|
+
findings: list[Finding] = []
|
|
315
|
+
|
|
316
|
+
# Freshness — git-commit truth; skipped when history is unavailable.
|
|
317
|
+
commit_times = _last_commit_times(root)
|
|
318
|
+
freshness_checked = commit_times is not None
|
|
319
|
+
if commit_times is not None:
|
|
320
|
+
content_ct: dict[str, int] = {}
|
|
321
|
+
companion_ct: dict[str, int] = {}
|
|
322
|
+
for path, ct in commit_times.items():
|
|
323
|
+
if _has_excluded_segment(path):
|
|
324
|
+
continue
|
|
325
|
+
owner = _owning_folder(path, folder_set)
|
|
326
|
+
if path.rpartition("/")[2] == COMPANION_NAME:
|
|
327
|
+
companion_folder = path.rpartition("/")[0] or "."
|
|
328
|
+
if companion_folder in folder_set:
|
|
329
|
+
companion_ct[companion_folder] = ct
|
|
330
|
+
continue
|
|
331
|
+
content_ct[owner] = max(content_ct.get(owner, 0), ct)
|
|
332
|
+
for folder in folders:
|
|
333
|
+
# Only committed companions can be stale; an uncommitted one is fresh.
|
|
334
|
+
if folder not in companion_ct:
|
|
335
|
+
continue
|
|
336
|
+
newest_content = content_ct.get(folder, 0)
|
|
337
|
+
if newest_content > companion_ct[folder]:
|
|
338
|
+
surface = (
|
|
339
|
+
COMPANION_NAME if folder == "." else f"{folder}/{COMPANION_NAME}"
|
|
340
|
+
)
|
|
341
|
+
findings.append(
|
|
342
|
+
Finding(
|
|
343
|
+
surface=surface,
|
|
344
|
+
kind="stale-companion",
|
|
345
|
+
detail=(
|
|
346
|
+
f"'{folder}' content changed after its {COMPANION_NAME} was "
|
|
347
|
+
"last updated; refresh the companion in the same change-set"
|
|
348
|
+
),
|
|
349
|
+
)
|
|
350
|
+
)
|
|
351
|
+
|
|
352
|
+
return GrepResult(
|
|
353
|
+
grep=GREP_NAME,
|
|
354
|
+
root=str(root),
|
|
355
|
+
folders_inspected=len(folders),
|
|
356
|
+
freshness_checked=freshness_checked,
|
|
357
|
+
passed=not findings,
|
|
358
|
+
findings=findings,
|
|
359
|
+
)
|
|
360
|
+
|
|
361
|
+
|
|
362
|
+
def _read_input(argv: list[str]) -> Path:
|
|
363
|
+
if len(argv) >= 2:
|
|
364
|
+
return Path(argv[1])
|
|
365
|
+
return Path.cwd()
|
|
366
|
+
|
|
367
|
+
|
|
368
|
+
def _main(argv: list[str]) -> int:
|
|
369
|
+
root = _read_input(argv)
|
|
370
|
+
result = check(root)
|
|
371
|
+
print(result.to_json())
|
|
372
|
+
# Advisory posture: always exit 0 so the sweep never silent-blocks by
|
|
373
|
+
# default. The verdict travels in the JSON — the orchestrator reads the
|
|
374
|
+
# ``advisory: true`` flag plus the inner ``passed`` field (via
|
|
375
|
+
# ``gate._advisory_verdict``) and surfaces a stale-companion finding as
|
|
376
|
+
# ``advisory_findings_present`` without failing the ``gate --all`` run.
|
|
377
|
+
# CI opts into strict enforcement separately.
|
|
378
|
+
return EXIT_PASS
|
|
379
|
+
|
|
380
|
+
|
|
381
|
+
if __name__ == "__main__":
|
|
382
|
+
sys.exit(_main(sys.argv))
|
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
|
2
|
+
|
|
3
|
+
"""Flag harness bias and re-introduced enforcement presets in shipped surfaces.
|
|
4
|
+
|
|
5
|
+
Why this enforcement exists. The agnostic-posture rule
|
|
6
|
+
(``rules/agnostic-posture.md``) requires every shipped surface to be
|
|
7
|
+
harness-neutral and free of imposed enforcement: a clean install privileges
|
|
8
|
+
no harness and pre-sets no model, effort, or workflow. This sweep is the
|
|
9
|
+
mechanical guard for that posture's two failure classes — it is the
|
|
10
|
+
agnosticism counterpart of ``plain_language_grep`` (the two sweeps are paired
|
|
11
|
+
by the phase that introduces them).
|
|
12
|
+
|
|
13
|
+
Scope. Corpus-level standalone validator. Walks the working tree under the
|
|
14
|
+
supplied root and inspects ONLY the four shipped-surface directories:
|
|
15
|
+
|
|
16
|
+
- ``src/apothem/rules/`` — behavioral instruction files.
|
|
17
|
+
- ``src/apothem/commands/`` — slash-command definitions.
|
|
18
|
+
- ``src/apothem/output-styles/`` — output-style definitions.
|
|
19
|
+
- ``src/apothem/statuslines/`` — statusline definitions.
|
|
20
|
+
|
|
21
|
+
Every other path is out of scope: tests, plans, harness adapter packages
|
|
22
|
+
(whose per-harness catalog content legitimately names every harness), and
|
|
23
|
+
all source code outside the four prose surfaces.
|
|
24
|
+
|
|
25
|
+
Detection — two closed classes.
|
|
26
|
+
|
|
27
|
+
1. **Harness bias.** The brand phrase ``Claude Code`` and the branded model
|
|
28
|
+
names ``Claude Opus`` / ``Claude Sonnet`` / ``Claude Haiku`` are
|
|
29
|
+
privileging forms: they tailor a shipped surface to one harness. The
|
|
30
|
+
canonical slug ``claude_code`` is the neutral catalog identifier — a line
|
|
31
|
+
that carries the slug is a per-harness catalog row (one entry among the
|
|
32
|
+
registered harnesses) and is exempt. Matches inside fenced code
|
|
33
|
+
blocks are excluded (a fenced example is not shipped prose).
|
|
34
|
+
|
|
35
|
+
2. **Enforcement preset.** A shipped command, skill, or agent that
|
|
36
|
+
re-introduces an ``effort:`` or ``model:`` frontmatter default imposes a
|
|
37
|
+
preference the end user is meant to supply in conversation. The sweep
|
|
38
|
+
flags any non-empty ``effort:`` / ``model:`` key inside the YAML
|
|
39
|
+
frontmatter block of a command, skill, or agent definition.
|
|
40
|
+
|
|
41
|
+
Exit semantics. Exits 0 when zero findings across every in-scope file;
|
|
42
|
+
exits 2 on any finding. The exit-2 convention matches the conformity-gate
|
|
43
|
+
orchestrator's EXIT_FAIL constant. The gate renders this sweep as an
|
|
44
|
+
advisory by default; ``--strict`` (or ``APOTHEM_CONFORMITY_STRICT``)
|
|
45
|
+
restores blocking, per the agnostic-posture advisory-gate discipline.
|
|
46
|
+
"""
|
|
47
|
+
|
|
48
|
+
from __future__ import annotations
|
|
49
|
+
|
|
50
|
+
import json
|
|
51
|
+
import re
|
|
52
|
+
import sys
|
|
53
|
+
from dataclasses import asdict, dataclass, field
|
|
54
|
+
from pathlib import Path
|
|
55
|
+
from typing import Final
|
|
56
|
+
|
|
57
|
+
GREP_NAME: Final[str] = "agnosticism-grep"
|
|
58
|
+
RULE_ANCHOR: Final[str] = (
|
|
59
|
+
"rules/agnostic-posture.md §3 (harness neutrality) + §1 (default-off)"
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
EXIT_PASS: Final[int] = 0
|
|
63
|
+
EXIT_FAIL: Final[int] = 2
|
|
64
|
+
|
|
65
|
+
# The four shipped-surface directories, relative to the repository root.
|
|
66
|
+
# A candidate Markdown file is in scope iff its POSIX-normalized relative
|
|
67
|
+
# path begins with one of these prefixes.
|
|
68
|
+
_SCOPE_PREFIXES: Final[tuple[str, ...]] = (
|
|
69
|
+
"src/apothem/rules/",
|
|
70
|
+
"src/apothem/commands/",
|
|
71
|
+
"src/apothem/output-styles/",
|
|
72
|
+
"src/apothem/statuslines/",
|
|
73
|
+
)
|
|
74
|
+
# Enforcement-preset scope. The shipped surfaces that carry an end-user-facing
|
|
75
|
+
# YAML frontmatter where a model/effort preset would impose a preference: the
|
|
76
|
+
# slash-commands, the skill definitions, and the agent definitions. The bias
|
|
77
|
+
# scan keeps its own (``_SCOPE_PREFIXES``) reach; the preset scan reaches all
|
|
78
|
+
# three so a re-introduced ``effort:`` / ``model:`` key is caught wherever a
|
|
79
|
+
# definition declares one.
|
|
80
|
+
_PRESET_PREFIXES: Final[tuple[str, ...]] = (
|
|
81
|
+
"src/apothem/commands/",
|
|
82
|
+
"src/apothem/skills/",
|
|
83
|
+
"src/apothem/agents/",
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
# Per-folder agent-companion files are agnostic shipped surfaces too: every
|
|
87
|
+
# AGENTS.md must privilege no single harness. They are swept for harness bias
|
|
88
|
+
# wherever they live in the tree. The rendered harness-output template fixture
|
|
89
|
+
# under a `templates/` leaf is NOT a companion — it is materialized output.
|
|
90
|
+
_COMPANION_BASENAME: Final[str] = "AGENTS.md"
|
|
91
|
+
_TEMPLATE_LEAF_FRAGMENT: Final[str] = "/templates/"
|
|
92
|
+
|
|
93
|
+
# Harness-bias forms. ``Claude Code`` (the brand phrase) and the branded
|
|
94
|
+
# model names privilege one harness; the slug ``claude_code`` is the
|
|
95
|
+
# neutral catalog identifier and is matched separately as the exemption
|
|
96
|
+
# signal. Whitespace between the two words is flexible so a line-wrapped
|
|
97
|
+
# brand phrase is still caught.
|
|
98
|
+
_BIAS_RE: Final[re.Pattern[str]] = re.compile(
|
|
99
|
+
r"(?i)\bClaude\s+(?:Code|Opus|Sonnet|Haiku)\b"
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
# A line carrying the canonical slug is a per-harness catalog row (one
|
|
103
|
+
# entry among the registered harnesses) and is exempt from the
|
|
104
|
+
# bias scan — the slug is the neutral identifier the agnostic posture
|
|
105
|
+
# permits.
|
|
106
|
+
_CATALOG_SLUG: Final[str] = "claude_code"
|
|
107
|
+
|
|
108
|
+
# Enforcement-preset frontmatter keys. A shipped command that pre-sets one
|
|
109
|
+
# of these imposes a preference the end user is meant to supply.
|
|
110
|
+
_PRESET_KEY_RE: Final[re.Pattern[str]] = re.compile(r"^(effort|model):\s*\S")
|
|
111
|
+
|
|
112
|
+
# Fenced code block delimiter — a line beginning with ``` at column 0.
|
|
113
|
+
_CODE_FENCE_RE: Final[re.Pattern[str]] = re.compile(r"^```")
|
|
114
|
+
|
|
115
|
+
# YAML frontmatter delimiter — a line that is exactly ``---``.
|
|
116
|
+
_FRONTMATTER_DELIM: Final[str] = "---"
|
|
117
|
+
|
|
118
|
+
_MARKDOWN_SUFFIX: Final[str] = ".md"
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
@dataclass(frozen=True)
|
|
122
|
+
class Finding:
|
|
123
|
+
"""One agnosticism violation discovered in a shipped surface."""
|
|
124
|
+
|
|
125
|
+
path: str
|
|
126
|
+
line: int
|
|
127
|
+
match: str
|
|
128
|
+
context: str
|
|
129
|
+
detail: str
|
|
130
|
+
rule: str = RULE_ANCHOR
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
@dataclass(frozen=True)
|
|
134
|
+
class GrepResult:
|
|
135
|
+
"""Aggregated walk result for a single corpus sweep."""
|
|
136
|
+
|
|
137
|
+
grep: str
|
|
138
|
+
root: str
|
|
139
|
+
scanned_count: int
|
|
140
|
+
passed: bool
|
|
141
|
+
findings: list[Finding] = field(default_factory=list)
|
|
142
|
+
|
|
143
|
+
def to_json(self) -> str:
|
|
144
|
+
payload = {
|
|
145
|
+
"grep": self.grep,
|
|
146
|
+
"root": self.root,
|
|
147
|
+
"scanned_count": self.scanned_count,
|
|
148
|
+
"passed": self.passed,
|
|
149
|
+
"findings": [asdict(f) for f in self.findings],
|
|
150
|
+
}
|
|
151
|
+
return json.dumps(payload, indent=2)
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def _relative_posix(path: Path, root: Path) -> str | None:
|
|
155
|
+
"""Return ``path`` relative to ``root`` in POSIX form, or None."""
|
|
156
|
+
try:
|
|
157
|
+
return path.resolve().relative_to(root.resolve()).as_posix()
|
|
158
|
+
except ValueError:
|
|
159
|
+
return None
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def _in_scope(rel_posix: str) -> bool:
|
|
163
|
+
"""Return True iff a relative path is a shipped-surface Markdown file.
|
|
164
|
+
|
|
165
|
+
Two surface classes are in scope: the four prose-surface directories
|
|
166
|
+
(rules / commands / output-styles / statuslines) and every per-folder
|
|
167
|
+
agent-companion file (``AGENTS.md``) wherever it lives — the companions
|
|
168
|
+
are agnostic shipped surfaces per the per-folder-AGENTS.md convention. A
|
|
169
|
+
rendered harness-output template fixture under a ``templates/`` leaf is
|
|
170
|
+
materialized output, not a companion, and is excluded.
|
|
171
|
+
"""
|
|
172
|
+
if "/" in rel_posix and rel_posix.rsplit("/", 1)[-1] == _COMPANION_BASENAME:
|
|
173
|
+
return _TEMPLATE_LEAF_FRAGMENT not in rel_posix
|
|
174
|
+
if not rel_posix.endswith(_MARKDOWN_SUFFIX):
|
|
175
|
+
return False
|
|
176
|
+
return rel_posix.startswith(_SCOPE_PREFIXES)
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
def _scan_bias(rel_posix: str, content: str) -> list[Finding]:
|
|
180
|
+
"""Flag privileging harness-brand phrases outside fenced code blocks.
|
|
181
|
+
|
|
182
|
+
A line carrying the canonical slug ``claude_code`` is a per-harness
|
|
183
|
+
catalog row and is exempt.
|
|
184
|
+
"""
|
|
185
|
+
findings: list[Finding] = []
|
|
186
|
+
inside_fence = False
|
|
187
|
+
for line_index, line in enumerate(content.splitlines(), start=1):
|
|
188
|
+
if _CODE_FENCE_RE.match(line):
|
|
189
|
+
inside_fence = not inside_fence
|
|
190
|
+
continue
|
|
191
|
+
if inside_fence:
|
|
192
|
+
continue
|
|
193
|
+
if _CATALOG_SLUG in line.lower():
|
|
194
|
+
continue
|
|
195
|
+
for match in _BIAS_RE.finditer(line):
|
|
196
|
+
findings.append(
|
|
197
|
+
Finding(
|
|
198
|
+
path=rel_posix,
|
|
199
|
+
line=line_index,
|
|
200
|
+
match=match.group(),
|
|
201
|
+
context=line.strip(),
|
|
202
|
+
detail=(
|
|
203
|
+
"harness-bias: privileging brand reference in a shipped "
|
|
204
|
+
"surface — name the harness by its slug (claude_code) as "
|
|
205
|
+
"one catalog entry among the registered harnesses, or genericize to "
|
|
206
|
+
"'the harness'"
|
|
207
|
+
),
|
|
208
|
+
)
|
|
209
|
+
)
|
|
210
|
+
return findings
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
def _scan_enforcement_preset(rel_posix: str, content: str) -> list[Finding]:
|
|
214
|
+
"""Flag a re-introduced ``effort:`` / ``model:`` frontmatter preset.
|
|
215
|
+
|
|
216
|
+
Scans only the leading YAML frontmatter block (delimited by a pair of
|
|
217
|
+
``---`` lines) of a command / skill / agent definition. Pre-setting
|
|
218
|
+
effort or model imposes a preference the end user is meant to supply in
|
|
219
|
+
conversation.
|
|
220
|
+
"""
|
|
221
|
+
if not rel_posix.startswith(_PRESET_PREFIXES):
|
|
222
|
+
return []
|
|
223
|
+
lines = content.splitlines()
|
|
224
|
+
if not lines or lines[0].strip() != _FRONTMATTER_DELIM:
|
|
225
|
+
return []
|
|
226
|
+
findings: list[Finding] = []
|
|
227
|
+
for line_index, line in enumerate(lines[1:], start=2):
|
|
228
|
+
if line.strip() == _FRONTMATTER_DELIM:
|
|
229
|
+
break
|
|
230
|
+
if _PRESET_KEY_RE.match(line):
|
|
231
|
+
findings.append(
|
|
232
|
+
Finding(
|
|
233
|
+
path=rel_posix,
|
|
234
|
+
line=line_index,
|
|
235
|
+
match=line.strip(),
|
|
236
|
+
context=line.strip(),
|
|
237
|
+
detail=(
|
|
238
|
+
"enforcement-preset: shipped surface pre-sets a "
|
|
239
|
+
"model/effort preference — strip the frontmatter key so "
|
|
240
|
+
"the preference resolves only from an in-conversation "
|
|
241
|
+
"end-user choice"
|
|
242
|
+
),
|
|
243
|
+
)
|
|
244
|
+
)
|
|
245
|
+
return findings
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
def _scan_file(path: Path, rel_posix: str) -> list[Finding]:
|
|
249
|
+
"""Scan one Markdown file for the applicable finding classes.
|
|
250
|
+
|
|
251
|
+
The bias scan applies only to the four prose surfaces and the AGENTS.md
|
|
252
|
+
companions (``_in_scope``); the enforcement-preset scan additionally
|
|
253
|
+
reaches the skill and agent definitions (``_PRESET_PREFIXES``), so the
|
|
254
|
+
two classes are gated independently.
|
|
255
|
+
"""
|
|
256
|
+
try:
|
|
257
|
+
content = path.read_text(encoding="utf-8")
|
|
258
|
+
except (OSError, UnicodeDecodeError):
|
|
259
|
+
return []
|
|
260
|
+
findings: list[Finding] = []
|
|
261
|
+
if _in_scope(rel_posix):
|
|
262
|
+
findings.extend(_scan_bias(rel_posix, content))
|
|
263
|
+
findings.extend(_scan_enforcement_preset(rel_posix, content))
|
|
264
|
+
return findings
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
def check(root: Path) -> GrepResult:
|
|
268
|
+
"""Walk the shipped surfaces under ``root``; flag agnosticism violations.
|
|
269
|
+
|
|
270
|
+
Pre-conditions: ``root`` is the repository root (or an arbitrary subtree
|
|
271
|
+
containing ``src/apothem/{rules,commands,output-styles,statuslines}/``).
|
|
272
|
+
Post-conditions: ``result.passed`` is True iff every in-scope shipped
|
|
273
|
+
surface is free of privileging harness-brand references (outside fenced
|
|
274
|
+
code) and re-introduced enforcement presets.
|
|
275
|
+
"""
|
|
276
|
+
findings: list[Finding] = []
|
|
277
|
+
scanned = 0
|
|
278
|
+
for path in sorted(root.rglob(f"*{_MARKDOWN_SUFFIX}")):
|
|
279
|
+
if not path.is_file():
|
|
280
|
+
continue
|
|
281
|
+
rel_posix = _relative_posix(path, root)
|
|
282
|
+
if rel_posix is None:
|
|
283
|
+
continue
|
|
284
|
+
if not (_in_scope(rel_posix) or rel_posix.startswith(_PRESET_PREFIXES)):
|
|
285
|
+
continue
|
|
286
|
+
scanned += 1
|
|
287
|
+
findings.extend(_scan_file(path, rel_posix))
|
|
288
|
+
return GrepResult(
|
|
289
|
+
grep=GREP_NAME,
|
|
290
|
+
root=str(root),
|
|
291
|
+
scanned_count=scanned,
|
|
292
|
+
passed=not findings,
|
|
293
|
+
findings=findings,
|
|
294
|
+
)
|
|
295
|
+
|
|
296
|
+
|
|
297
|
+
def _read_input(argv: list[str]) -> Path:
|
|
298
|
+
if len(argv) >= 2:
|
|
299
|
+
return Path(argv[1])
|
|
300
|
+
return Path.cwd()
|
|
301
|
+
|
|
302
|
+
|
|
303
|
+
def _main(argv: list[str]) -> int:
|
|
304
|
+
root = _read_input(argv)
|
|
305
|
+
result = check(root)
|
|
306
|
+
print(result.to_json())
|
|
307
|
+
return EXIT_PASS if result.passed else EXIT_FAIL
|
|
308
|
+
|
|
309
|
+
|
|
310
|
+
if __name__ == "__main__":
|
|
311
|
+
sys.exit(_main(sys.argv))
|