@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,206 @@
|
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
|
2
|
+
|
|
3
|
+
"""Flag artifacts emitted to non-canonical locations or without provenance.
|
|
4
|
+
|
|
5
|
+
Why this enforcement exists. The canonical-layout rule M12 + the
|
|
6
|
+
systemicity rule M14 require every emitted artifact to land at a
|
|
7
|
+
ratified output directory and to declare its producer (provenance
|
|
8
|
+
frontmatter, header comment, or registry entry). Orphan outputs —
|
|
9
|
+
artifacts the host's reference graph does not consume — fragment the
|
|
10
|
+
project surface and resist maintenance. The pre-emission gate's
|
|
11
|
+
mechanical bar 12 (M12 canonical layout) catches two failure modes: location drift (artifact
|
|
12
|
+
not under any ratified output directory) and provenance absence (no
|
|
13
|
+
frontmatter / header / registry entry naming the producer).
|
|
14
|
+
|
|
15
|
+
Detection strategy. The grep accepts a content body and an artifact
|
|
16
|
+
path. It checks the path against the ratified output-directory list
|
|
17
|
+
discovered from the host's existing layout (`src/`, `lib/`, `tests/`,
|
|
18
|
+
`site/`, `.github/`, plus the host-discovery default harness-config
|
|
19
|
+
subtree — for example `~/.claude/` for the Claude Code harness).
|
|
20
|
+
When the path is outside every ratified directory, that is one finding. When the content body lacks a provenance marker
|
|
21
|
+
(YAML frontmatter `provenance:` field, comment-line `provenance:`
|
|
22
|
+
marker, or a `Bindings` section binding the artifact to its producer),
|
|
23
|
+
that is another finding.
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
from __future__ import annotations
|
|
27
|
+
|
|
28
|
+
import re
|
|
29
|
+
import sys
|
|
30
|
+
from dataclasses import dataclass
|
|
31
|
+
from pathlib import Path
|
|
32
|
+
from typing import Final
|
|
33
|
+
|
|
34
|
+
from apothem.conformity._grep_base import GrepResult, run_grep
|
|
35
|
+
|
|
36
|
+
# Ratified output-directory prefixes. The set is conservative — it covers
|
|
37
|
+
# the canonical surfaces the host project emits to. New artifact classes
|
|
38
|
+
# either land at one of these prefixes or surface an inquiry to the
|
|
39
|
+
# operator before silent installation.
|
|
40
|
+
RATIFIED_OUTPUT_PREFIXES: Final[tuple[str, ...]] = (
|
|
41
|
+
"src/",
|
|
42
|
+
"lib/",
|
|
43
|
+
"tests/",
|
|
44
|
+
"site/",
|
|
45
|
+
".github/",
|
|
46
|
+
"rules/",
|
|
47
|
+
"skills/",
|
|
48
|
+
"commands/",
|
|
49
|
+
"agents/",
|
|
50
|
+
"hooks/",
|
|
51
|
+
"tools/",
|
|
52
|
+
"scripts/",
|
|
53
|
+
"assets/",
|
|
54
|
+
"schemas/",
|
|
55
|
+
"templates/",
|
|
56
|
+
"memory/",
|
|
57
|
+
".plans/",
|
|
58
|
+
"output-styles/",
|
|
59
|
+
"statuslines/",
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
# Root-level singletons that are canonical by filename, not by directory
|
|
63
|
+
# prefix. README, LICENSE, CHANGELOG, CONTRIBUTING etc. land at the repo
|
|
64
|
+
# root by universal convention. Harness-config singleton settings.json
|
|
65
|
+
# lives at the harness config root for every integrated harness —
|
|
66
|
+
# `~/.claude/settings.json` for Claude Code, `.vscode/settings.json` for
|
|
67
|
+
# VS Code, `~/.cursor/settings.json` for Cursor, etc. — and is canonical
|
|
68
|
+
# by basename per the module docstring's harness-config-subtree clause.
|
|
69
|
+
RATIFIED_ROOT_FILENAMES = frozenset(
|
|
70
|
+
{
|
|
71
|
+
"README.md",
|
|
72
|
+
"LICENSE",
|
|
73
|
+
"LICENSE.md",
|
|
74
|
+
"LICENSE.txt",
|
|
75
|
+
"CHANGELOG.md",
|
|
76
|
+
"CONTRIBUTING.md",
|
|
77
|
+
"SECURITY.md",
|
|
78
|
+
"SUPPORT.md",
|
|
79
|
+
"CODE_OF_CONDUCT.md",
|
|
80
|
+
"GOVERNANCE.md",
|
|
81
|
+
"AUTHORS.md",
|
|
82
|
+
"NOTICE",
|
|
83
|
+
"NOTICE.md",
|
|
84
|
+
"NOTICE.txt",
|
|
85
|
+
"CODEOWNERS",
|
|
86
|
+
"FUNDING.yml",
|
|
87
|
+
"CITATION.cff",
|
|
88
|
+
".gitignore",
|
|
89
|
+
".gitattributes",
|
|
90
|
+
".editorconfig",
|
|
91
|
+
"pyproject.toml",
|
|
92
|
+
"setup.py",
|
|
93
|
+
"setup.cfg",
|
|
94
|
+
"requirements.txt",
|
|
95
|
+
"package.json",
|
|
96
|
+
"Cargo.toml",
|
|
97
|
+
"go.mod",
|
|
98
|
+
"Makefile",
|
|
99
|
+
"CLAUDE.md",
|
|
100
|
+
"settings.json",
|
|
101
|
+
}
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
# Provenance markers — any one of these in the content body satisfies
|
|
105
|
+
# the M14 producer-attribution requirement. Source-language artifacts
|
|
106
|
+
# satisfy provenance via the canonical SPDX authorship-header banner
|
|
107
|
+
# enforced by `file-header-grep`; the SPDX-License-Identifier line is
|
|
108
|
+
# the machine-readable producer-attribution marker per the REUSE
|
|
109
|
+
# specification, mirroring how Markdown artifacts satisfy it via the
|
|
110
|
+
# `## Bindings` section or frontmatter `description:` field.
|
|
111
|
+
PROVENANCE_RE: Final[re.Pattern[str]] = re.compile(
|
|
112
|
+
r"(?:provenance:|## Bindings\b|^---\s*$.*?\bdescription:|SPDX-License-Identifier:)",
|
|
113
|
+
re.MULTILINE | re.DOTALL,
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
# Trivial-by-extension carve-out: data files (`.json`, `.yml`, `.yaml`,
|
|
117
|
+
# `.toml`) and lockfiles can satisfy provenance via their parent
|
|
118
|
+
# package-manifest contract; per-file frontmatter is not required for
|
|
119
|
+
# these classes.
|
|
120
|
+
TRIVIAL_PROVENANCE_EXTENSIONS: Final[frozenset[str]] = frozenset(
|
|
121
|
+
{".json", ".yaml", ".yml", ".toml", ".lock", ".cfg", ".ini"}
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
# Trivial-by-basename carve-out: files whose format convention does not admit
|
|
125
|
+
# a per-file provenance banner and whose provenance is satisfied structurally.
|
|
126
|
+
# `.SRCINFO` is a machine-parseable manifest generated from `PKGBUILD` — a
|
|
127
|
+
# banner would corrupt the parser's input. `py.typed` is the PEP 561 inline-
|
|
128
|
+
# type marker: conventionally zero-byte (a banner would defeat the marker), and
|
|
129
|
+
# its provenance is the package contract that ships it — the `apothem`
|
|
130
|
+
# `[tool.setuptools.package-data]` entry in `pyproject.toml`.
|
|
131
|
+
TRIVIAL_PROVENANCE_BASENAMES: Final[frozenset[str]] = frozenset(
|
|
132
|
+
{".SRCINFO", "py.typed"}
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
# Memory-index carve-out: the auto-memory convention defines `MEMORY.md` as a
|
|
136
|
+
# frontmatter-free index — one index line per memory, no per-file frontmatter.
|
|
137
|
+
# Its producer attribution is structural: it is the index every memory topic
|
|
138
|
+
# file points back to, and the topic files it indexes carry their own
|
|
139
|
+
# frontmatter. Requiring a provenance marker from the index itself is a
|
|
140
|
+
# category error; a memory-index write would otherwise fail-close the harness's
|
|
141
|
+
# per-project memory tree (the `MEMORY.md` write the operator makes at session
|
|
142
|
+
# end has no frontmatter to satisfy `PROVENANCE_RE`).
|
|
143
|
+
MEMORY_INDEX_BASENAMES: Final[frozenset[str]] = frozenset({"MEMORY.md"})
|
|
144
|
+
|
|
145
|
+
GREP_NAME: Final[str] = "orphan-output-grep"
|
|
146
|
+
RULE_ANCHOR: Final[str] = "M12 canonical-layout + M14 systemicity"
|
|
147
|
+
EXIT_PASS: Final[int] = 0
|
|
148
|
+
EXIT_FAIL: Final[int] = 2
|
|
149
|
+
STDIN_FLAG: Final[str] = "--stdin"
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
@dataclass(frozen=True)
|
|
153
|
+
class Finding:
|
|
154
|
+
issue: str
|
|
155
|
+
detail: str
|
|
156
|
+
rule: str = RULE_ANCHOR
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def _is_under_ratified_prefix(path: Path) -> bool:
|
|
160
|
+
"""Return True iff the path is under a ratified prefix or is a root-level singleton."""
|
|
161
|
+
posix = path.as_posix()
|
|
162
|
+
if any(prefix in posix for prefix in RATIFIED_OUTPUT_PREFIXES):
|
|
163
|
+
return True
|
|
164
|
+
# Root-level canonical files (README, LICENSE, etc.) match by name.
|
|
165
|
+
return path.name in RATIFIED_ROOT_FILENAMES
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
def check(content: str, path: Path | None = None) -> GrepResult:
|
|
169
|
+
"""Scan content + path; return a structured result."""
|
|
170
|
+
findings: list[Finding] = []
|
|
171
|
+
if path is not None and not _is_under_ratified_prefix(path):
|
|
172
|
+
findings.append(
|
|
173
|
+
Finding(
|
|
174
|
+
issue="non-canonical location",
|
|
175
|
+
detail=(
|
|
176
|
+
f"path {path.as_posix()!r} is not under any ratified output "
|
|
177
|
+
f"prefix; surface as an inquiry before emission"
|
|
178
|
+
),
|
|
179
|
+
)
|
|
180
|
+
)
|
|
181
|
+
requires_provenance = path is None or (
|
|
182
|
+
path.suffix.lower() not in TRIVIAL_PROVENANCE_EXTENSIONS
|
|
183
|
+
and path.name not in TRIVIAL_PROVENANCE_BASENAMES
|
|
184
|
+
and path.name not in MEMORY_INDEX_BASENAMES
|
|
185
|
+
)
|
|
186
|
+
if requires_provenance and PROVENANCE_RE.search(content) is None:
|
|
187
|
+
findings.append(
|
|
188
|
+
Finding(
|
|
189
|
+
issue="provenance absent",
|
|
190
|
+
detail=(
|
|
191
|
+
"content lacks a provenance marker (YAML `provenance:` field, "
|
|
192
|
+
"header `## Bindings` section, or frontmatter description); "
|
|
193
|
+
"the producer of this artifact is unrecorded"
|
|
194
|
+
),
|
|
195
|
+
)
|
|
196
|
+
)
|
|
197
|
+
return GrepResult(
|
|
198
|
+
grep=GREP_NAME,
|
|
199
|
+
path=str(path) if path is not None else None,
|
|
200
|
+
passed=not findings,
|
|
201
|
+
findings=findings,
|
|
202
|
+
)
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
if __name__ == "__main__":
|
|
206
|
+
sys.exit(run_grep(check, sys.argv))
|
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
|
2
|
+
|
|
3
|
+
"""Verify every GitHub Actions workflow declares a minimum-scope ``permissions:`` block.
|
|
4
|
+
|
|
5
|
+
Why this enforcement exists. GitHub Actions workflows inherit a
|
|
6
|
+
default-broad permissions surface (``write-all`` on the repository
|
|
7
|
+
token) unless they explicitly narrow it. A workflow that silently
|
|
8
|
+
consumes the default scope hands every job the keys to the repository
|
|
9
|
+
- contents write, packages write, deployments write, issues write -
|
|
10
|
+
regardless of whether the job's actual work requires any of them. The
|
|
11
|
+
minimum-scope discipline requires each workflow to declare a top-level
|
|
12
|
+
``permissions:`` block enumerating only the scopes its jobs need; the
|
|
13
|
+
default-broad surface is replaced with an audit-friendly,
|
|
14
|
+
least-privilege grant.
|
|
15
|
+
|
|
16
|
+
Detection. Walks ``<root>/.github/workflows/*.yml`` (and ``.yaml``).
|
|
17
|
+
For each workflow:
|
|
18
|
+
|
|
19
|
+
1. Verify a top-level ``permissions:`` key exists.
|
|
20
|
+
2. Verify the block is NOT ``permissions: write-all`` (the over-scoped
|
|
21
|
+
default).
|
|
22
|
+
3. Surface an advisory when ``contents: write`` is granted but no
|
|
23
|
+
push-style step is detected (heuristic for undocumented permission
|
|
24
|
+
elevation).
|
|
25
|
+
|
|
26
|
+
Drift classes:
|
|
27
|
+
|
|
28
|
+
- ``missing-top-level-permissions-block``
|
|
29
|
+
- ``overscoped-permission`` (``write-all`` declared)
|
|
30
|
+
- ``undocumented-permission-elevation`` (advisory; ``contents: write``
|
|
31
|
+
declared without an observable push step)
|
|
32
|
+
|
|
33
|
+
Special case. When ``<root>/.github/workflows/`` does not exist, the
|
|
34
|
+
validator exits 0 with ``not-yet-materialised: true`` so projects that
|
|
35
|
+
have not yet introduced CI workflows are not blocked by the gate.
|
|
36
|
+
|
|
37
|
+
Exit semantics. Exits 0 when every workflow carries a conformant
|
|
38
|
+
permissions block; exits 2 with a JSON report listing per-workflow
|
|
39
|
+
drift classes otherwise. Advisory-severity findings (the
|
|
40
|
+
``undocumented-permission-elevation`` class) do NOT trigger exit 2 on
|
|
41
|
+
their own — they surface in the JSON for operator review without
|
|
42
|
+
blocking the gate.
|
|
43
|
+
"""
|
|
44
|
+
|
|
45
|
+
from __future__ import annotations
|
|
46
|
+
|
|
47
|
+
import json
|
|
48
|
+
import sys
|
|
49
|
+
from dataclasses import asdict, dataclass, field
|
|
50
|
+
from pathlib import Path
|
|
51
|
+
from typing import Any, Final
|
|
52
|
+
|
|
53
|
+
import yaml
|
|
54
|
+
|
|
55
|
+
GREP_NAME: Final[str] = "permissions-minimum-scope-grep"
|
|
56
|
+
RULE_ANCHOR: Final[str] = "minimum-scope workflow permissions"
|
|
57
|
+
|
|
58
|
+
EXIT_PASS: Final[int] = 0
|
|
59
|
+
EXIT_FAIL: Final[int] = 2
|
|
60
|
+
|
|
61
|
+
SEVERITY_BLOCKING: Final[str] = "blocking"
|
|
62
|
+
SEVERITY_ADVISORY: Final[str] = "advisory"
|
|
63
|
+
|
|
64
|
+
# Drift class identifiers surfaced in finding payloads.
|
|
65
|
+
DRIFT_MISSING: Final[str] = "missing-top-level-permissions-block"
|
|
66
|
+
DRIFT_OVERSCOPED: Final[str] = "overscoped-permission"
|
|
67
|
+
DRIFT_UNDOCUMENTED_ELEVATION: Final[str] = "undocumented-permission-elevation"
|
|
68
|
+
|
|
69
|
+
# Heuristic tokens that suggest a workflow performs a push-style write to
|
|
70
|
+
# the repository — when any of these appears in a step's ``run`` body or
|
|
71
|
+
# in a third-party action ``uses:`` reference, the ``contents: write``
|
|
72
|
+
# grant is considered justified and the advisory does not fire.
|
|
73
|
+
_PUSH_STEP_TOKENS: Final[tuple[str, ...]] = (
|
|
74
|
+
"git push",
|
|
75
|
+
"git commit",
|
|
76
|
+
"git tag",
|
|
77
|
+
"peter-evans/create-pull-request",
|
|
78
|
+
"stefanzweifel/git-auto-commit-action",
|
|
79
|
+
"ad-m/github-push-action",
|
|
80
|
+
"softprops/action-gh-release",
|
|
81
|
+
"actions/create-release",
|
|
82
|
+
"ncipollo/release-action",
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
@dataclass(frozen=True)
|
|
87
|
+
class Finding:
|
|
88
|
+
"""One workflow-level permissions drift."""
|
|
89
|
+
|
|
90
|
+
workflow: str
|
|
91
|
+
drift_class: str
|
|
92
|
+
severity: str
|
|
93
|
+
detail: str
|
|
94
|
+
rule: str = RULE_ANCHOR
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
@dataclass(frozen=True)
|
|
98
|
+
class GrepResult:
|
|
99
|
+
"""Aggregated walk result for a single workflows-directory sweep."""
|
|
100
|
+
|
|
101
|
+
grep: str
|
|
102
|
+
root: str
|
|
103
|
+
workflows_dir: str
|
|
104
|
+
workflow_count: int
|
|
105
|
+
passed: bool
|
|
106
|
+
not_yet_materialised: bool = False
|
|
107
|
+
findings: list[Finding] = field(default_factory=list)
|
|
108
|
+
|
|
109
|
+
def to_json(self) -> str:
|
|
110
|
+
payload = {
|
|
111
|
+
"grep": self.grep,
|
|
112
|
+
"root": self.root,
|
|
113
|
+
"workflows_dir": self.workflows_dir,
|
|
114
|
+
"workflow_count": self.workflow_count,
|
|
115
|
+
"not_yet_materialised": self.not_yet_materialised,
|
|
116
|
+
"passed": self.passed,
|
|
117
|
+
"findings": [asdict(f) for f in self.findings],
|
|
118
|
+
}
|
|
119
|
+
return json.dumps(payload, indent=2)
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def _iter_workflow_files(workflows_dir: Path) -> list[Path]:
|
|
123
|
+
"""Return every ``*.yml`` / ``*.yaml`` file at the workflows root.
|
|
124
|
+
|
|
125
|
+
The walk is non-recursive: GitHub Actions only picks up workflows
|
|
126
|
+
that sit directly under ``.github/workflows/``; nested directories
|
|
127
|
+
are ignored by the platform and so by this validator.
|
|
128
|
+
"""
|
|
129
|
+
files: list[Path] = []
|
|
130
|
+
for pattern in ("*.yml", "*.yaml"):
|
|
131
|
+
files.extend(workflows_dir.glob(pattern))
|
|
132
|
+
return sorted(files)
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def _has_push_step(parsed: dict[str, Any]) -> bool:
|
|
136
|
+
"""Heuristic: does any job step suggest a push-style write?
|
|
137
|
+
|
|
138
|
+
Inspects every job's ``steps`` list for a ``run:`` body containing
|
|
139
|
+
a push-style command, or a ``uses:`` reference to a known
|
|
140
|
+
push-style action. The heuristic is intentionally permissive — a
|
|
141
|
+
false negative (advisory fires when push is actually present)
|
|
142
|
+
surfaces in the JSON for operator triage rather than blocking.
|
|
143
|
+
"""
|
|
144
|
+
jobs = parsed.get("jobs")
|
|
145
|
+
if not isinstance(jobs, dict):
|
|
146
|
+
return False
|
|
147
|
+
for job in jobs.values():
|
|
148
|
+
if not isinstance(job, dict):
|
|
149
|
+
continue
|
|
150
|
+
steps = job.get("steps")
|
|
151
|
+
if not isinstance(steps, list):
|
|
152
|
+
continue
|
|
153
|
+
for step in steps:
|
|
154
|
+
if not isinstance(step, dict):
|
|
155
|
+
continue
|
|
156
|
+
run_body = step.get("run")
|
|
157
|
+
uses_ref = step.get("uses")
|
|
158
|
+
haystack_parts: list[str] = []
|
|
159
|
+
if isinstance(run_body, str):
|
|
160
|
+
haystack_parts.append(run_body)
|
|
161
|
+
if isinstance(uses_ref, str):
|
|
162
|
+
haystack_parts.append(uses_ref)
|
|
163
|
+
haystack = "\n".join(haystack_parts).lower()
|
|
164
|
+
if any(token.lower() in haystack for token in _PUSH_STEP_TOKENS):
|
|
165
|
+
return True
|
|
166
|
+
return False
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
def _grants_contents_write(permissions: object) -> bool:
|
|
170
|
+
"""Return True when the ``permissions:`` block grants ``contents: write``."""
|
|
171
|
+
if not isinstance(permissions, dict):
|
|
172
|
+
return False
|
|
173
|
+
contents = permissions.get("contents")
|
|
174
|
+
return isinstance(contents, str) and contents.strip().lower() == "write"
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
def _classify_workflow(
|
|
178
|
+
workflow: Path,
|
|
179
|
+
parsed: dict[str, Any] | None,
|
|
180
|
+
) -> list[Finding]:
|
|
181
|
+
"""Classify a single workflow's permissions surface.
|
|
182
|
+
|
|
183
|
+
Returns the per-workflow finding list. An unparseable workflow
|
|
184
|
+
(YAML error, non-mapping root) yields a blocking finding under
|
|
185
|
+
``missing-top-level-permissions-block`` because the validator
|
|
186
|
+
cannot prove the block exists.
|
|
187
|
+
"""
|
|
188
|
+
rel = workflow.name
|
|
189
|
+
if parsed is None or not isinstance(parsed, dict):
|
|
190
|
+
return [
|
|
191
|
+
Finding(
|
|
192
|
+
workflow=rel,
|
|
193
|
+
drift_class=DRIFT_MISSING,
|
|
194
|
+
severity=SEVERITY_BLOCKING,
|
|
195
|
+
detail=(
|
|
196
|
+
"workflow YAML did not parse as a mapping; cannot verify "
|
|
197
|
+
"top-level permissions block"
|
|
198
|
+
),
|
|
199
|
+
)
|
|
200
|
+
]
|
|
201
|
+
if "permissions" not in parsed:
|
|
202
|
+
return [
|
|
203
|
+
Finding(
|
|
204
|
+
workflow=rel,
|
|
205
|
+
drift_class=DRIFT_MISSING,
|
|
206
|
+
severity=SEVERITY_BLOCKING,
|
|
207
|
+
detail=(
|
|
208
|
+
"no top-level `permissions:` key; workflow inherits the "
|
|
209
|
+
"default-broad GitHub Actions permissions surface "
|
|
210
|
+
"(equivalent to write-all)"
|
|
211
|
+
),
|
|
212
|
+
)
|
|
213
|
+
]
|
|
214
|
+
permissions = parsed["permissions"]
|
|
215
|
+
findings: list[Finding] = []
|
|
216
|
+
if isinstance(permissions, str) and permissions.strip().lower() == "write-all":
|
|
217
|
+
findings.append(
|
|
218
|
+
Finding(
|
|
219
|
+
workflow=rel,
|
|
220
|
+
drift_class=DRIFT_OVERSCOPED,
|
|
221
|
+
severity=SEVERITY_BLOCKING,
|
|
222
|
+
detail=(
|
|
223
|
+
"`permissions: write-all` grants the default-broad surface; "
|
|
224
|
+
"replace with explicit per-scope grants (e.g. `contents: read`)"
|
|
225
|
+
),
|
|
226
|
+
)
|
|
227
|
+
)
|
|
228
|
+
if _grants_contents_write(permissions) and not _has_push_step(parsed):
|
|
229
|
+
findings.append(
|
|
230
|
+
Finding(
|
|
231
|
+
workflow=rel,
|
|
232
|
+
drift_class=DRIFT_UNDOCUMENTED_ELEVATION,
|
|
233
|
+
severity=SEVERITY_ADVISORY,
|
|
234
|
+
detail=(
|
|
235
|
+
"`contents: write` granted but no push-style step "
|
|
236
|
+
"(git push / release-action / create-pull-request) "
|
|
237
|
+
"was detected; verify the elevation is required"
|
|
238
|
+
),
|
|
239
|
+
)
|
|
240
|
+
)
|
|
241
|
+
return findings
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
def check(root: Path) -> GrepResult:
|
|
245
|
+
"""Walk every workflow under ``<root>/.github/workflows/``.
|
|
246
|
+
|
|
247
|
+
Pre-conditions: ``root`` is the project root containing a
|
|
248
|
+
``.github/workflows/`` directory (or not — the absence case is
|
|
249
|
+
tolerated). Post-conditions: ``result.passed`` is True iff every
|
|
250
|
+
workflow carries a top-level ``permissions:`` block that is not
|
|
251
|
+
``write-all``; advisory findings do not affect ``passed``.
|
|
252
|
+
"""
|
|
253
|
+
workflows_dir = root / ".github" / "workflows"
|
|
254
|
+
if not workflows_dir.is_dir():
|
|
255
|
+
return GrepResult(
|
|
256
|
+
grep=GREP_NAME,
|
|
257
|
+
root=str(root),
|
|
258
|
+
workflows_dir=str(workflows_dir),
|
|
259
|
+
workflow_count=0,
|
|
260
|
+
passed=True,
|
|
261
|
+
not_yet_materialised=True,
|
|
262
|
+
findings=[],
|
|
263
|
+
)
|
|
264
|
+
workflows = _iter_workflow_files(workflows_dir)
|
|
265
|
+
findings: list[Finding] = []
|
|
266
|
+
for workflow in workflows:
|
|
267
|
+
try:
|
|
268
|
+
raw = workflow.read_text(encoding="utf-8")
|
|
269
|
+
parsed = yaml.safe_load(raw)
|
|
270
|
+
except (OSError, UnicodeDecodeError, yaml.YAMLError):
|
|
271
|
+
parsed = None
|
|
272
|
+
findings.extend(_classify_workflow(workflow, parsed))
|
|
273
|
+
blocking = [f for f in findings if f.severity == SEVERITY_BLOCKING]
|
|
274
|
+
return GrepResult(
|
|
275
|
+
grep=GREP_NAME,
|
|
276
|
+
root=str(root),
|
|
277
|
+
workflows_dir=str(workflows_dir),
|
|
278
|
+
workflow_count=len(workflows),
|
|
279
|
+
passed=not blocking,
|
|
280
|
+
not_yet_materialised=False,
|
|
281
|
+
findings=findings,
|
|
282
|
+
)
|
|
283
|
+
|
|
284
|
+
|
|
285
|
+
def _read_input(argv: list[str]) -> Path:
|
|
286
|
+
if len(argv) >= 2:
|
|
287
|
+
return Path(argv[1])
|
|
288
|
+
return Path.cwd()
|
|
289
|
+
|
|
290
|
+
|
|
291
|
+
def _main(argv: list[str]) -> int:
|
|
292
|
+
root = _read_input(argv)
|
|
293
|
+
result = check(root)
|
|
294
|
+
print(result.to_json())
|
|
295
|
+
return EXIT_PASS if result.passed else EXIT_FAIL
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
if __name__ == "__main__":
|
|
299
|
+
sys.exit(_main(sys.argv))
|