@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,233 @@
|
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
|
2
|
+
|
|
3
|
+
"""Flag freshness-narrative phrases on shipped public surfaces.
|
|
4
|
+
|
|
5
|
+
Why this enforcement exists. The freshness-facade rule
|
|
6
|
+
(``rules/freshness-facade.md``) keeps every shipped public surface a
|
|
7
|
+
current-version-only facade: no AI-disclosure, no backward / legacy /
|
|
8
|
+
obsolete replacement narrative, no placeholder / deferral, no
|
|
9
|
+
fix-and-refinement story-of-becoming. This matcher is the freshness analogue
|
|
10
|
+
of ``plain_language_grep`` — a distinct, non-overlapping token-class on the same
|
|
11
|
+
shipped-surface scope. ``plain_language_grep`` forbids mechanistic vocabulary
|
|
12
|
+
(``AI`` / ``agent`` / harness brands); this matcher forbids the narrative
|
|
13
|
+
PHRASES that mark a surface telling the story of its own becoming.
|
|
14
|
+
|
|
15
|
+
High-precision PHRASES, never bare words. Bare common words — ``legacy``,
|
|
16
|
+
``deprecated``, ``retired``, ``placeholder``, ``backward`` — have legitimate
|
|
17
|
+
uses (a Keep-a-Changelog ``Deprecated`` heading, a documented deprecation
|
|
18
|
+
policy, a ``placeholder identity`` product feature) and are deliberately NOT
|
|
19
|
+
denylisted. Only the narrative phrase forms ("replaces the legacy …",
|
|
20
|
+
"coming soon", "AI-generated") are flagged, so the matcher catches the
|
|
21
|
+
story-of-becoming without flagging the dictionary word. The forbidden phrases
|
|
22
|
+
live in the package-relative denylist data file, never in this module.
|
|
23
|
+
|
|
24
|
+
Scope. Corpus-level standalone validator. Walks the working tree under the
|
|
25
|
+
supplied root and inspects ONLY shipped public surfaces: the repository
|
|
26
|
+
root ``README.md`` and every ``*.md`` / ``*.mdx`` under
|
|
27
|
+
``site/content/docs/``. Developer-only surfaces (rules, commands,
|
|
28
|
+
matchers, plans, adapter modules) are out of scope — their process
|
|
29
|
+
vocabulary is load-bearing, exactly as the plain-language boundary draws it.
|
|
30
|
+
|
|
31
|
+
Detection. Each denylist phrase is matched case-insensitively. Pure-alpha
|
|
32
|
+
phrases anchor at word boundaries; punctuation- or space-bearing phrases
|
|
33
|
+
match as escaped literals. Matches inside fenced code blocks (triple
|
|
34
|
+
backtick) are excluded — docs legitimately quote a forbidden phrase inside a
|
|
35
|
+
sample or tool-output fence.
|
|
36
|
+
|
|
37
|
+
Posture. The result carries an advisory flag, honoring apothem's agnostic
|
|
38
|
+
gate posture; CI opts into strict enforcement separately. Exits 0 when zero
|
|
39
|
+
findings across every in-scope file; exits 2 on any finding.
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
from __future__ import annotations
|
|
43
|
+
|
|
44
|
+
import json
|
|
45
|
+
import re
|
|
46
|
+
import sys
|
|
47
|
+
from dataclasses import asdict, dataclass, field
|
|
48
|
+
from pathlib import Path
|
|
49
|
+
from typing import Final
|
|
50
|
+
|
|
51
|
+
GREP_NAME: Final[str] = "freshness-token-grep"
|
|
52
|
+
RULE_ANCHOR: Final[str] = "rules/freshness-facade.md"
|
|
53
|
+
|
|
54
|
+
EXIT_PASS: Final[int] = 0
|
|
55
|
+
EXIT_FAIL: Final[int] = 2
|
|
56
|
+
|
|
57
|
+
# The denylist data file is package-relative: the conformity package ships
|
|
58
|
+
# beside its ``schemas/`` sibling in both the repo checkout
|
|
59
|
+
# (``src/apothem/{conformity,schemas}``) and the installed tree, so one parent
|
|
60
|
+
# hop reaches it in either shape. The data file holds the forbidden phrases so
|
|
61
|
+
# this module never names them.
|
|
62
|
+
_DENYLIST_PATH: Final[Path] = (
|
|
63
|
+
Path(__file__).resolve().parents[1] / "schemas" / "freshness-token-denylist.txt"
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
# Shipped-surface scope. The root README and the documentation tree are the
|
|
67
|
+
# user-facing release facade the freshness mandate holds to the current-version
|
|
68
|
+
# bar.
|
|
69
|
+
_README_FILENAME: Final[str] = "README.md"
|
|
70
|
+
_DOCS_DIR: Final[str] = "site/content/docs"
|
|
71
|
+
_TEXT_SUFFIXES: Final[frozenset[str]] = frozenset({".md", ".mdx"})
|
|
72
|
+
|
|
73
|
+
# Fenced code-block delimiter — a line whose first non-whitespace content is
|
|
74
|
+
# ```. Opening and closing fences both match; the scanner toggles state.
|
|
75
|
+
_CODE_FENCE_RE: Final[re.Pattern[str]] = re.compile(r"^[ \t]*```")
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
@dataclass(frozen=True)
|
|
79
|
+
class Finding:
|
|
80
|
+
"""One freshness-narrative phrase on a shipped public surface."""
|
|
81
|
+
|
|
82
|
+
surface: str
|
|
83
|
+
kind: str
|
|
84
|
+
detail: str
|
|
85
|
+
rule: str = RULE_ANCHOR
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
@dataclass(frozen=True)
|
|
89
|
+
class GrepResult:
|
|
90
|
+
"""Aggregated walk result for a single corpus sweep."""
|
|
91
|
+
|
|
92
|
+
grep: str
|
|
93
|
+
root: str
|
|
94
|
+
files_inspected: int
|
|
95
|
+
passed: bool
|
|
96
|
+
findings: list[Finding] = field(default_factory=list)
|
|
97
|
+
|
|
98
|
+
def to_json(self) -> str:
|
|
99
|
+
payload = {
|
|
100
|
+
"grep": self.grep,
|
|
101
|
+
"root": self.root,
|
|
102
|
+
"files_inspected": self.files_inspected,
|
|
103
|
+
"passed": self.passed,
|
|
104
|
+
"findings": [asdict(f) for f in self.findings],
|
|
105
|
+
"advisory": True,
|
|
106
|
+
}
|
|
107
|
+
return json.dumps(payload, indent=2)
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def _load_tokens() -> list[str]:
|
|
111
|
+
"""Parse the denylist data file into a list of forbidden phrases.
|
|
112
|
+
|
|
113
|
+
Pre-conditions: the package-relative denylist file exists.
|
|
114
|
+
Post-conditions: returns every non-empty, non-comment line, stripped.
|
|
115
|
+
"""
|
|
116
|
+
try:
|
|
117
|
+
raw = _DENYLIST_PATH.read_text(encoding="utf-8")
|
|
118
|
+
except OSError:
|
|
119
|
+
return []
|
|
120
|
+
tokens: list[str] = []
|
|
121
|
+
for line in raw.splitlines():
|
|
122
|
+
stripped = line.strip()
|
|
123
|
+
if not stripped or stripped.startswith("#"):
|
|
124
|
+
continue
|
|
125
|
+
tokens.append(stripped)
|
|
126
|
+
return tokens
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def _compile_tokens(tokens: list[str]) -> re.Pattern[str] | None:
|
|
130
|
+
"""Compile the denylist into one case-insensitive alternation regex.
|
|
131
|
+
|
|
132
|
+
Pure-alphabetic phrases anchor at word boundaries (``\\bphrase\\b``) so a
|
|
133
|
+
larger word is not flagged. Space- or punctuation-bearing phrases are
|
|
134
|
+
escaped literals — word boundaries do not apply cleanly across spaces and
|
|
135
|
+
``-`` characters. Returns None when the denylist is empty. Longer phrases
|
|
136
|
+
are ordered first so the alternation prefers the most specific match.
|
|
137
|
+
"""
|
|
138
|
+
if not tokens:
|
|
139
|
+
return None
|
|
140
|
+
alternatives: list[str] = []
|
|
141
|
+
for token in sorted(tokens, key=len, reverse=True):
|
|
142
|
+
if token.isalpha():
|
|
143
|
+
alternatives.append(r"\b" + re.escape(token) + r"\b")
|
|
144
|
+
else:
|
|
145
|
+
alternatives.append(re.escape(token))
|
|
146
|
+
return re.compile(r"(?i)(?:" + "|".join(alternatives) + r")")
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
def _is_in_scope(rel_posix: str) -> bool:
|
|
150
|
+
"""Return True iff a relative POSIX path is a shipped public surface.
|
|
151
|
+
|
|
152
|
+
In scope: the repository root ``README.md`` and any ``.md`` / ``.mdx``
|
|
153
|
+
under ``site/content/docs/``. Every other path is out of scope.
|
|
154
|
+
"""
|
|
155
|
+
if rel_posix == _README_FILENAME:
|
|
156
|
+
return True
|
|
157
|
+
return rel_posix.startswith(_DOCS_DIR + "/")
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def _scan_file(path: Path, rel_posix: str, pattern: re.Pattern[str]) -> list[Finding]:
|
|
161
|
+
"""Scan one text file; return findings outside fenced code blocks."""
|
|
162
|
+
try:
|
|
163
|
+
content = path.read_text(encoding="utf-8")
|
|
164
|
+
except (OSError, UnicodeDecodeError):
|
|
165
|
+
return []
|
|
166
|
+
findings: list[Finding] = []
|
|
167
|
+
inside_fence = False
|
|
168
|
+
for line_index, line in enumerate(content.splitlines(), start=1):
|
|
169
|
+
if _CODE_FENCE_RE.match(line):
|
|
170
|
+
inside_fence = not inside_fence
|
|
171
|
+
continue
|
|
172
|
+
if inside_fence:
|
|
173
|
+
continue
|
|
174
|
+
for match in pattern.finditer(line):
|
|
175
|
+
findings.append(
|
|
176
|
+
Finding(
|
|
177
|
+
surface=rel_posix,
|
|
178
|
+
kind="freshness-token",
|
|
179
|
+
detail=f"line {line_index}: '{match.group()}'",
|
|
180
|
+
)
|
|
181
|
+
)
|
|
182
|
+
return findings
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
def check(root: Path) -> GrepResult:
|
|
186
|
+
"""Walk the corpus under ``root``; flag freshness-narrative phrases.
|
|
187
|
+
|
|
188
|
+
Pre-conditions: ``root`` is the repository root (or an arbitrary subtree).
|
|
189
|
+
Post-conditions: ``result.passed`` is True iff every shipped public
|
|
190
|
+
surface contained zero denylisted phrases outside fenced code blocks.
|
|
191
|
+
"""
|
|
192
|
+
tokens = _load_tokens()
|
|
193
|
+
pattern = _compile_tokens(tokens)
|
|
194
|
+
findings: list[Finding] = []
|
|
195
|
+
inspected = 0
|
|
196
|
+
root_resolved = root.resolve()
|
|
197
|
+
if pattern is not None:
|
|
198
|
+
for suffix in sorted(_TEXT_SUFFIXES):
|
|
199
|
+
for path in root.rglob(f"*{suffix}"):
|
|
200
|
+
if not path.is_file():
|
|
201
|
+
continue
|
|
202
|
+
try:
|
|
203
|
+
rel_posix = path.resolve().relative_to(root_resolved).as_posix()
|
|
204
|
+
except ValueError:
|
|
205
|
+
continue
|
|
206
|
+
if not _is_in_scope(rel_posix):
|
|
207
|
+
continue
|
|
208
|
+
inspected += 1
|
|
209
|
+
findings.extend(_scan_file(path, rel_posix, pattern))
|
|
210
|
+
return GrepResult(
|
|
211
|
+
grep=GREP_NAME,
|
|
212
|
+
root=str(root),
|
|
213
|
+
files_inspected=inspected,
|
|
214
|
+
passed=not findings,
|
|
215
|
+
findings=findings,
|
|
216
|
+
)
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
def _read_input(argv: list[str]) -> Path:
|
|
220
|
+
if len(argv) >= 2:
|
|
221
|
+
return Path(argv[1])
|
|
222
|
+
return Path.cwd()
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
def _main(argv: list[str]) -> int:
|
|
226
|
+
root = _read_input(argv)
|
|
227
|
+
result = check(root)
|
|
228
|
+
print(result.to_json())
|
|
229
|
+
return EXIT_PASS if result.passed else EXIT_FAIL
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
if __name__ == "__main__":
|
|
233
|
+
sys.exit(_main(sys.argv))
|
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
|
2
|
+
|
|
3
|
+
"""Validate per-artifact-class YAML frontmatter against required-key sets.
|
|
4
|
+
|
|
5
|
+
Why this enforcement exists. Spec section 4.2 ratifies that every
|
|
6
|
+
artifact carries YAML frontmatter delimited by ``---`` lines, with
|
|
7
|
+
required-key sets that vary per artifact class (rules, agents, skills,
|
|
8
|
+
commands, output styles each carry their own minimum schema). This
|
|
9
|
+
validator extends the grep family with frontmatter checks; per-class
|
|
10
|
+
JSON Schemas feed this validator's strict-mode.
|
|
11
|
+
|
|
12
|
+
Detection strategy. The validator reads a file, locates the leading
|
|
13
|
+
``---``-delimited frontmatter block, parses the YAML, and checks the
|
|
14
|
+
required-key set for the inferred artifact class (inferred by the
|
|
15
|
+
file's path under rules/, agents/, skills/, commands/, output-styles/).
|
|
16
|
+
A missing required key, an unparseable YAML block, or an absent
|
|
17
|
+
frontmatter block on a class that requires one is a finding. Frontmatter
|
|
18
|
+
keys must use kebab-case per spec section 4.1; non-kebab-case keys are
|
|
19
|
+
flagged. Schema-presence-driven strict-mode activates when per-class
|
|
20
|
+
JSON Schemas exist at ``frontmatter/<class>.json`` inside the
|
|
21
|
+
``schemas/`` directory shipped beside this package (one parent hop above
|
|
22
|
+
this module in both the repo-checkout and installed layouts); until then
|
|
23
|
+
the validator runs the baseline required-key check.
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
from __future__ import annotations
|
|
27
|
+
|
|
28
|
+
import json
|
|
29
|
+
import re
|
|
30
|
+
import sys
|
|
31
|
+
from dataclasses import asdict, dataclass, field
|
|
32
|
+
from pathlib import Path
|
|
33
|
+
from typing import Final
|
|
34
|
+
|
|
35
|
+
# Required-key sets per artifact class, derived from the canonical
|
|
36
|
+
# frontmatter schema declared at site/content/docs/reference/frontmatter-schema.mdx and the
|
|
37
|
+
# minimum schemas observed across the rules/ + agents/ + skills/ +
|
|
38
|
+
# commands/ + output-styles/ corpora.
|
|
39
|
+
REQUIRED_KEYS: Final[dict[str, frozenset[str]]] = {
|
|
40
|
+
"rules": frozenset({"name", "description", "alwaysApply"}),
|
|
41
|
+
"agents": frozenset({"name", "description"}),
|
|
42
|
+
"skills": frozenset({"name", "description"}),
|
|
43
|
+
"commands": frozenset({"name", "description"}),
|
|
44
|
+
"output-styles": frozenset({"name", "description"}),
|
|
45
|
+
"hooks-messages": frozenset({"description"}),
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
# Kebab-case key shape per spec section 4.1. Frontmatter keys are
|
|
49
|
+
# matched against this regex; uppercase / underscore / camelCase keys
|
|
50
|
+
# are findings. The harness honors an explicit allow-list for keys
|
|
51
|
+
# that originated in pre-ratification artifacts and are preserved by
|
|
52
|
+
# sibling-convergence per host-discovery.
|
|
53
|
+
KEBAB_KEY_RE: Final[re.Pattern[str]] = re.compile(r"^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$")
|
|
54
|
+
KEY_ALLOW_LIST: Final[frozenset[str]] = frozenset(
|
|
55
|
+
{
|
|
56
|
+
# Path-filter keys observed across rules/ predate the
|
|
57
|
+
# kebab-case ratification.
|
|
58
|
+
"pathFilter",
|
|
59
|
+
"alwaysApply",
|
|
60
|
+
# Argument-hint and disable-model-invocation surface inside
|
|
61
|
+
# commands frontmatter; both predate ratification.
|
|
62
|
+
"argument-hint",
|
|
63
|
+
"disable-model-invocation",
|
|
64
|
+
"allowed-tools",
|
|
65
|
+
# User-invocability flag on skills; camelCase predates the
|
|
66
|
+
# kebab-case ratification and is uniform across the skill corpus
|
|
67
|
+
# (preserved by sibling-convergence per host-discovery).
|
|
68
|
+
"userInvocable",
|
|
69
|
+
# Tools-list key on agents.
|
|
70
|
+
"tools",
|
|
71
|
+
"model",
|
|
72
|
+
}
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
FRONTMATTER_DELIMITER: Final[str] = "---"
|
|
76
|
+
GREP_NAME: Final[str] = "frontmatter-grep"
|
|
77
|
+
RULE_ANCHOR: Final[str] = (
|
|
78
|
+
"the per-class frontmatter JSON Schemas (src/apothem/schemas/)"
|
|
79
|
+
)
|
|
80
|
+
EXIT_PASS: Final[int] = 0
|
|
81
|
+
EXIT_FAIL: Final[int] = 2
|
|
82
|
+
STDIN_FLAG: Final[str] = "--stdin"
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
@dataclass(frozen=True)
|
|
86
|
+
class Finding:
|
|
87
|
+
detail: str
|
|
88
|
+
rule: str = RULE_ANCHOR
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
@dataclass(frozen=True)
|
|
92
|
+
class GrepResult:
|
|
93
|
+
grep: str
|
|
94
|
+
path: str | None
|
|
95
|
+
artifact_class: str | None
|
|
96
|
+
passed: bool
|
|
97
|
+
findings: list[Finding] = field(default_factory=list)
|
|
98
|
+
|
|
99
|
+
def to_json(self) -> str:
|
|
100
|
+
payload = {
|
|
101
|
+
"grep": self.grep,
|
|
102
|
+
"path": self.path,
|
|
103
|
+
"artifact-class": self.artifact_class,
|
|
104
|
+
"passed": self.passed,
|
|
105
|
+
"findings": [asdict(f) for f in self.findings],
|
|
106
|
+
}
|
|
107
|
+
return json.dumps(payload, indent=2)
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def _infer_class(path: Path) -> str | None:
|
|
111
|
+
"""Infer the artifact class from the path's top-level directory."""
|
|
112
|
+
parts = path.parts
|
|
113
|
+
if "rules" in parts:
|
|
114
|
+
return "rules"
|
|
115
|
+
if "agents" in parts:
|
|
116
|
+
return "agents"
|
|
117
|
+
if "skills" in parts:
|
|
118
|
+
return "skills"
|
|
119
|
+
if "commands" in parts:
|
|
120
|
+
return "commands"
|
|
121
|
+
if "output-styles" in parts:
|
|
122
|
+
return "output-styles"
|
|
123
|
+
if "hooks" in parts and "messages" in parts:
|
|
124
|
+
return "hooks-messages"
|
|
125
|
+
return None
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def _extract_frontmatter(content: str) -> tuple[str | None, int]:
|
|
129
|
+
"""Return (frontmatter-yaml, body-start-line) or (None, 0).
|
|
130
|
+
|
|
131
|
+
The established sibling-convention across commands/, agents/, skills/,
|
|
132
|
+
rules/, and output-styles/ places the canonical authorship-header
|
|
133
|
+
banner (an HTML comment block opened with ``<!--`` and closed with
|
|
134
|
+
``-->``) before the YAML frontmatter, separated by a blank line. The
|
|
135
|
+
extractor therefore tolerates a leading region of blank lines plus
|
|
136
|
+
one optional HTML-comment block before locating the opening ``---``
|
|
137
|
+
delimiter. Within the comment region, the closing ``-->`` may sit on
|
|
138
|
+
its own line or share a line with trailing content; the extractor
|
|
139
|
+
advances past the first line whose stripped contents end with
|
|
140
|
+
``-->``.
|
|
141
|
+
"""
|
|
142
|
+
lines = content.splitlines()
|
|
143
|
+
index = 0
|
|
144
|
+
|
|
145
|
+
while index < len(lines) and not lines[index].strip():
|
|
146
|
+
index += 1
|
|
147
|
+
|
|
148
|
+
if index < len(lines) and lines[index].lstrip().startswith("<!--"):
|
|
149
|
+
first_comment_line = lines[index].strip()
|
|
150
|
+
if first_comment_line.endswith("-->") and first_comment_line != "<!--":
|
|
151
|
+
index += 1
|
|
152
|
+
else:
|
|
153
|
+
index += 1
|
|
154
|
+
while index < len(lines) and not lines[index].strip().endswith("-->"):
|
|
155
|
+
index += 1
|
|
156
|
+
if index < len(lines):
|
|
157
|
+
index += 1
|
|
158
|
+
|
|
159
|
+
while index < len(lines) and not lines[index].strip():
|
|
160
|
+
index += 1
|
|
161
|
+
|
|
162
|
+
if index >= len(lines) or lines[index].strip() != FRONTMATTER_DELIMITER:
|
|
163
|
+
return None, 0
|
|
164
|
+
|
|
165
|
+
open_index = index
|
|
166
|
+
for close_index in range(open_index + 1, len(lines)):
|
|
167
|
+
if lines[close_index].strip() == FRONTMATTER_DELIMITER:
|
|
168
|
+
return (
|
|
169
|
+
"\n".join(lines[open_index + 1 : close_index]),
|
|
170
|
+
close_index + 1,
|
|
171
|
+
)
|
|
172
|
+
return None, 0
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
def _parse_keys(yaml_block: str) -> list[str]:
|
|
176
|
+
"""Extract top-level keys from the YAML block via line-shape parsing.
|
|
177
|
+
|
|
178
|
+
The validator does not depend on PyYAML; the frontmatter's surface
|
|
179
|
+
is shallow (top-level scalar keys plus the occasional list value)
|
|
180
|
+
and the shape can be read from the first column of each line. Keys
|
|
181
|
+
inside nested mappings or lists are intentionally excluded — only
|
|
182
|
+
top-level keys count toward the required-set check.
|
|
183
|
+
"""
|
|
184
|
+
keys: list[str] = []
|
|
185
|
+
for raw in yaml_block.splitlines():
|
|
186
|
+
if not raw.strip() or raw.lstrip().startswith("#"):
|
|
187
|
+
continue
|
|
188
|
+
if raw[0].isspace() or raw.startswith("-"):
|
|
189
|
+
continue
|
|
190
|
+
if ":" not in raw:
|
|
191
|
+
continue
|
|
192
|
+
key = raw.split(":", 1)[0].strip()
|
|
193
|
+
if key:
|
|
194
|
+
keys.append(key)
|
|
195
|
+
return keys
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
def check(content: str, path: Path | None = None) -> GrepResult:
|
|
199
|
+
"""Validate frontmatter for the inferred artifact class."""
|
|
200
|
+
findings: list[Finding] = []
|
|
201
|
+
artifact_class = _infer_class(path) if path is not None else None
|
|
202
|
+
yaml_block, _ = _extract_frontmatter(content)
|
|
203
|
+
|
|
204
|
+
if yaml_block is None:
|
|
205
|
+
if artifact_class is not None:
|
|
206
|
+
findings.append(
|
|
207
|
+
Finding(
|
|
208
|
+
detail=(
|
|
209
|
+
f"frontmatter block absent on artifact-class "
|
|
210
|
+
f"{artifact_class!r}; required by spec section 4.2"
|
|
211
|
+
)
|
|
212
|
+
)
|
|
213
|
+
)
|
|
214
|
+
return GrepResult(
|
|
215
|
+
grep=GREP_NAME,
|
|
216
|
+
path=str(path) if path is not None else None,
|
|
217
|
+
artifact_class=artifact_class,
|
|
218
|
+
passed=not findings,
|
|
219
|
+
findings=findings,
|
|
220
|
+
)
|
|
221
|
+
|
|
222
|
+
keys = _parse_keys(yaml_block)
|
|
223
|
+
keyset = set(keys)
|
|
224
|
+
|
|
225
|
+
if artifact_class is not None:
|
|
226
|
+
required = REQUIRED_KEYS.get(artifact_class, frozenset())
|
|
227
|
+
missing = required - keyset
|
|
228
|
+
for key in sorted(missing):
|
|
229
|
+
findings.append(
|
|
230
|
+
Finding(
|
|
231
|
+
detail=(
|
|
232
|
+
f"required key {key!r} missing from "
|
|
233
|
+
f"{artifact_class!r} frontmatter"
|
|
234
|
+
)
|
|
235
|
+
)
|
|
236
|
+
)
|
|
237
|
+
|
|
238
|
+
for key in keys:
|
|
239
|
+
if key in KEY_ALLOW_LIST:
|
|
240
|
+
continue
|
|
241
|
+
if not KEBAB_KEY_RE.match(key):
|
|
242
|
+
findings.append(
|
|
243
|
+
Finding(
|
|
244
|
+
detail=(
|
|
245
|
+
f"frontmatter key {key!r} is not kebab-case (spec section 4.1)"
|
|
246
|
+
)
|
|
247
|
+
)
|
|
248
|
+
)
|
|
249
|
+
|
|
250
|
+
return GrepResult(
|
|
251
|
+
grep=GREP_NAME,
|
|
252
|
+
path=str(path) if path is not None else None,
|
|
253
|
+
artifact_class=artifact_class,
|
|
254
|
+
passed=not findings,
|
|
255
|
+
findings=findings,
|
|
256
|
+
)
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
def _read_input(argv: list[str]) -> tuple[str, Path | None]:
|
|
260
|
+
if len(argv) >= 2 and argv[1] != STDIN_FLAG:
|
|
261
|
+
path = Path(argv[1])
|
|
262
|
+
return path.read_text(encoding="utf-8"), path
|
|
263
|
+
return sys.stdin.read(), None
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
def _main(argv: list[str]) -> int:
|
|
267
|
+
content, path = _read_input(argv)
|
|
268
|
+
result = check(content, path)
|
|
269
|
+
print(result.to_json())
|
|
270
|
+
return EXIT_PASS if result.passed else EXIT_FAIL
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
if __name__ == "__main__":
|
|
274
|
+
sys.exit(_main(sys.argv))
|