@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,32 @@
|
|
|
1
|
+
<!-- SPDX-License-Identifier: MIT -->
|
|
2
|
+
|
|
3
|
+
# Apothem — Zed Bootstrap
|
|
4
|
+
|
|
5
|
+
This file is materialised by `apothem install --harness zed --project <path>` and lands at `<project>/.rules`, the current Zed project agent-instruction surface per https://zed.dev/docs/ai/instructions. Zed auto-includes this flat project-root file (alongside the `AGENTS.md` / `CLAUDE.md` family) as agent instructions; there is no per-tool rules directory, so the whole apothem surface lands in this single file. Any pre-existing `.rules` is backed up before this file replaces it.
|
|
6
|
+
|
|
7
|
+
## What Apothem governs in this project
|
|
8
|
+
|
|
9
|
+
Apothem propagates a shared governance and convention surface across every supported AI harness in this project's host environment. Inside Zed, the surface manifests as this `.rules` instruction file:
|
|
10
|
+
|
|
11
|
+
- **Rules** — engineering rules applied on every interaction. The full rule cohort lives at the apothem source repository under `src/apothem/rules/`; this Zed-facing anchor names the disciplines the operator may consult in detail.
|
|
12
|
+
- **Skills, commands, agents** — reusable techniques, slash-style workflows, and persistent sub-agent definitions. Apothem's canonical-master cohort at the apothem source repository defines them; Zed does not auto-discover them as separate surfaces, so they manifest here as referenced discipline.
|
|
13
|
+
- **Context servers (MCP)** — Zed's MCP surface (`.zed/settings.json` `context_servers`) is operator-owned, not apothem-managed.
|
|
14
|
+
- **Global instructions** — Zed's global instruction surface (`~/.config/zed/AGENTS.md`) is operator-owned, not apothem-managed.
|
|
15
|
+
|
|
16
|
+
## Engineering disciplines in force
|
|
17
|
+
|
|
18
|
+
Apothem's foundational mandates apply uniformly across every harness, including Zed:
|
|
19
|
+
|
|
20
|
+
- **Plans-Locality.** Plan-suite artefacts (PROGRESS.md, PLAN-NOTES.md, PHASE.md, REPORT.md) live under `<project>/.apothem/plans/{suite}/` — the sole canonical home; a legacy `<project>/.plans/` tree upgrades via `apothem migrate-workspace`. They never land in a global plans directory or any other global-ecosystem location.
|
|
21
|
+
- **Authority hygiene.** Never fabricate identity, scope, security posture, or version-pin data. Surface ambiguity through a question instead of inventing a plausible-looking value.
|
|
22
|
+
- **Definitiveness.** Hedging vocabulary (`maybe`, `might`, `usually`, `generally`, `typically`, `probably`) is eliminated where binding prescription is possible. Pre / post / failure conditions are stated on every contract.
|
|
23
|
+
- **Production-ready discipline.** Every change ships in production-ready form — tests, docs, CHANGELOG entry, conformant commit message, CI green — in the same change-set.
|
|
24
|
+
- **Plain-language.** Codebase artefacts and user-facing prose read as natural domain language with zero trace of internal planning structure.
|
|
25
|
+
- **Human-only authorship.** Git commit messages, PR descriptions, branch names, and tag annotations carry human contributors only — never the underlying language model, runtime, IDE extension, or any automated tool as a co-author.
|
|
26
|
+
- **Lean-context delegation.** Where the host provides delegated-worker dispatch, broad reads and heavy workloads route to a spawned worker by default so the main working context stays lean. The heavy parallel apparatus stays opt-in; routing delegable work away from the main thread is the standing posture once dispatch is available.
|
|
27
|
+
- **Observe-decide-act discipline.** Tool use runs as a loop — observe the current state, decide the next move from what was observed, then act — never an edit before a read or a result asserted before a check. Independent reads and searches run together in one pass, not one per turn; the loop closes on a verifiable condition (a gate green, a read confirmed), never a fixed number of tries. Advisory, never an auto-applied behavior.
|
|
28
|
+
- **Source accessibility.** When the authoritative source for a claim, convention, or version pin is closed, paywalled, login-gated, or otherwise unreachable, ask the operator for it in full rather than substituting a lower-trust open page. Trust outranks reachability.
|
|
29
|
+
|
|
30
|
+
## Refreshing the file
|
|
31
|
+
|
|
32
|
+
Re-run `apothem install --harness zed --project <this-project-root>` to overwrite this file with the latest apothem template. The operation is idempotent. `apothem uninstall --harness zed --project <this-project-root>` renames the file to a timestamped backup; `apothem verify --harness zed --project <this-project-root>` checks the file is present and non-empty.
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
|
2
|
+
|
|
3
|
+
"""Uninstall logic for the zed harness adapter."""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
|
|
9
|
+
from apothem.harnesses._shared import install_driver
|
|
10
|
+
|
|
11
|
+
_HARNESS_NAME: str = "zed"
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def uninstall(output_path: Path, *, project: Path | None = None) -> None:
|
|
15
|
+
"""Remove Apothem-managed Zed targets surgically.
|
|
16
|
+
|
|
17
|
+
Zed's flat-file divergence: the target is ``<project>/.rules`` (one
|
|
18
|
+
level below the project root), so the project root is the file's
|
|
19
|
+
direct parent — unlike the cohort's nested rules-directory targets. The
|
|
20
|
+
``.rules`` ``sentinel_merge`` anchor is cleaned by the shared driver, which
|
|
21
|
+
strips only Apothem's managed block (operator prose survives), deletes an
|
|
22
|
+
Apothem-only file, and backs the file up under the Apothem backup root — no
|
|
23
|
+
whole-file ``.bak`` sibling is left beside the operator's file.
|
|
24
|
+
"""
|
|
25
|
+
install_driver.run_uninstall(
|
|
26
|
+
_HARNESS_NAME,
|
|
27
|
+
project_root=project or output_path.parent,
|
|
28
|
+
)
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
|
2
|
+
|
|
3
|
+
"""Update logic for the zed harness adapter."""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
from apothem.harnesses._shared.wrapper_factories import make_update_project
|
|
8
|
+
from apothem.harnesses.zed.install import install
|
|
9
|
+
|
|
10
|
+
update = make_update_project(install)
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
|
2
|
+
|
|
3
|
+
"""Verify logic for the zed harness adapter."""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
from apothem.harnesses._shared.wrapper_factories import make_verify_project
|
|
8
|
+
|
|
9
|
+
_HARNESS_NAME: str = "zed"
|
|
10
|
+
|
|
11
|
+
verify = make_verify_project(_HARNESS_NAME)
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
<!-- SPDX-License-Identifier: MIT -->
|
|
2
|
+
|
|
3
|
+
# hooks
|
|
4
|
+
|
|
5
|
+
> **Role.** The hook runtime. A unified Python dispatcher handles every
|
|
6
|
+
> harness hook event, emits per-event Markdown context, and runs the
|
|
7
|
+
> session-start bootstrap.
|
|
8
|
+
|
|
9
|
+
```mermaid
|
|
10
|
+
%%{ init: { "theme": "neutral" } }%%
|
|
11
|
+
%% verified: 2026-06-23 %%
|
|
12
|
+
%% provenance: hooks/README.md — the hook-dispatch runtime flow %%
|
|
13
|
+
%% cross-reference: src/apothem/hooks/dispatch.py (router); src/apothem/hooks/lib/bootstrap.sh + bootstrap.ps1 (stubs); src/apothem/hooks/messages/ (per-event context) %%
|
|
14
|
+
flowchart TD
|
|
15
|
+
Event["Harness fires a hook event<br/>PreToolUse · PostToolUse · SessionStart · Stop · PreCompact · …"]
|
|
16
|
+
Stub["Bootstrap stub — lib/bootstrap.sh / bootstrap.ps1<br/>locates a Python interpreter, then execs the dispatcher<br/>(Claude Code settings.json invokes an install-resolved interpreter on dispatch.py directly)"]
|
|
17
|
+
Dispatch["dispatch.py — unified router<br/>routes each event to its handler"]
|
|
18
|
+
Context["Per-event Markdown context<br/>from messages/<event>.md"]
|
|
19
|
+
Bootstrap["session_start_bootstrap.py<br/>(SessionStart only)"]
|
|
20
|
+
|
|
21
|
+
Event --> Stub --> Dispatch
|
|
22
|
+
Dispatch -->|most events| Context
|
|
23
|
+
Dispatch -->|SessionStart| Bootstrap
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Entry points
|
|
27
|
+
|
|
28
|
+
| File | Purpose |
|
|
29
|
+
|------|---------|
|
|
30
|
+
| `dispatch.py` | Unified Python entrypoint for all hook events — routes each event to its handler. |
|
|
31
|
+
| `emit_hook_context.py` | Emit structured JSON context for hook events. |
|
|
32
|
+
| `session_start_bootstrap.py` | Session-start bootstrap hook for the Apothem ecosystem. |
|
|
33
|
+
| `__init__.py` | Package marker. |
|
|
34
|
+
|
|
35
|
+
## `lib/` — dispatcher support
|
|
36
|
+
|
|
37
|
+
| File | Purpose |
|
|
38
|
+
|------|---------|
|
|
39
|
+
| `bootstrap.sh` / `bootstrap.ps1` | Shell bootstrap stubs that locate an interpreter (via the locators below) and `exec` the dispatcher. Shipped as the documented shell entry path; the Claude Code `settings.json` entries instead invoke an install-resolved absolute interpreter on `dispatch.py` directly. |
|
|
40
|
+
| `find-python.sh` / `find-python.ps1` | Python interpreter locators. |
|
|
41
|
+
| `find-pwsh.sh` / `find-pwsh.ps1` | PowerShell interpreter locators. |
|
|
42
|
+
| `events.py` | Single source of truth for the supported hook-event vocabulary. |
|
|
43
|
+
| `log.py` | Shared logger factory for the hook scripts. |
|
|
44
|
+
| `resolve_root.py` | Project-root resolution for the Apothem ecosystem. |
|
|
45
|
+
| `__init__.py` | Package marker. |
|
|
46
|
+
|
|
47
|
+
## `messages/` — per-event context
|
|
48
|
+
|
|
49
|
+
Markdown context files emitted into the conversation for each hook event:
|
|
50
|
+
|
|
51
|
+
| File | Event |
|
|
52
|
+
|------|-------|
|
|
53
|
+
| `sessionstart.md` | SessionStart. |
|
|
54
|
+
| `stop.md` | Stop (session-end). |
|
|
55
|
+
| `precompact.md` / `postcompact.md` | PreCompact / PostCompact. |
|
|
56
|
+
| `pretooluse-write.md` / `pretooluse-write-header-guard.md` / `pretooluse-write-plan-guard.md` | PreToolUse Write / apply_patch — base context plus the authorship-header and plans-discipline guards. |
|
|
57
|
+
| `pretooluse-edit.md` / `pretooluse-edit-header-guard.md` | PreToolUse Edit — base context plus the authorship-header guard. |
|
|
58
|
+
| `pretooluse-notebookedit.md` | PreToolUse NotebookEdit. |
|
|
59
|
+
| `pretooluse-bash.md` / `pretooluse-bash-plan-guard.md` | PreToolUse Bash — base context plus the plans-discipline redirection guard. |
|
|
60
|
+
| `pretooluse-conformity.md` | PreToolUse conformity-gate context. |
|
|
61
|
+
| `pretooluse-dependency-guard.md` | PreToolUse Write / Edit — advisory flag on unpinned or untrusted dependency additions to manifests and lockfiles. |
|
|
62
|
+
| `pretooluse-eval-guard.md` | PreToolUse Write / Edit / Bash — advisory flag on dynamic evaluation of untrusted or model-derived input. |
|
|
63
|
+
| `pretooluse-askuserquestion-recommended.md` | PreToolUse AskUserQuestion — advisory `(Recommended)`-marker guard on rendered option sets. |
|
|
64
|
+
| `posttooluse-proactive-compaction.md` | PostToolUse — proactive-compaction tracker context (CM-19). |
|
|
65
|
+
|
|
66
|
+
## Conventions
|
|
67
|
+
|
|
68
|
+
- The Claude Code `settings.json` hook entries invoke an install-resolved absolute CPython interpreter on `dispatch.py` directly — the `${PYTHON_BIN}` placeholder is substituted at install time (per `apothem.lib.python_resolver`) so no entry runs a bare `python`. The shell bootstrap stubs (`bootstrap.sh` / `bootstrap.ps1`) are the documented shell entry path that locates an interpreter and hands off to `dispatch.py`; they are not the `settings.json` entry point.
|
|
69
|
+
|
|
70
|
+
## Operating in this folder
|
|
71
|
+
|
|
72
|
+
- **Fail-open is the dispatcher contract.** `dispatch.py::main` always exits 0; any uncaught exception is converted to a valid JSON failure envelope on stdout so the host harness never sees a raw traceback and never stalls. A handler that can raise past `main` is a contract breach — preserve the `# noqa: BLE001` intent-marker on the outermost boundary, and never widen the dispatcher's fail-open boundary.
|
|
73
|
+
- **The event vocabulary has one source of truth:** `lib/events.py` (`SUPPORTED_EVENTS`, `HOOK_SPECIFIC_OUTPUT_EVENTS`). Never hard-code an event-name list elsewhere; import from `events`.
|
|
74
|
+
- **Per-event timeout budgets bound runtime** (PreToolUse / PostToolUse / UserPromptSubmit / Notification tighter; SessionStart / PreCompact / PostCompact wider; Stop widest). A handler exceeding its event's budget is silently skipped by the runtime — keep handler work inside the budget; the per-event runtime benchmark is [`../benchmarks/bench_hooks.py`](../benchmarks/bench_hooks.py).
|
|
75
|
+
- **Adding an event:** extend `lib/events.py` first, then the dispatcher routing, then a `messages/<event>.md` context file. A new `messages/<event>.md` carries the markdown SPDX comment line and is registered in the table above in the same change.
|
|
76
|
+
- Validate a change with `python -m ruff check`, `python -m mypy` (cli + harnesses scope), and `python -m pytest`. Surface any unresolved decision as `TODO(clarify): ...` rather than guessing.
|
|
77
|
+
|
|
78
|
+
## Related
|
|
79
|
+
|
|
80
|
+
- [`conformity/gate.py`](../conformity/gate.py) — the conformity gate the PreToolUse Write/Edit hooks invoke.
|
|
81
|
+
- [`benchmarks/bench_hooks.py`](../benchmarks/bench_hooks.py) — the per-event hook-handler runtime benchmark.
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
|
2
|
+
|
|
3
|
+
"""Hook dispatcher and per-event context emitters.
|
|
4
|
+
|
|
5
|
+
This package carries the apothem hook-event pipeline. The runtime
|
|
6
|
+
entry-points are:
|
|
7
|
+
|
|
8
|
+
* ``apothem.hooks.dispatch`` — the harness-side dispatcher. Harness
|
|
9
|
+
hook entries invoke the materialized copy by absolute script path
|
|
10
|
+
(``<harness-root>/apothem/hooks/dispatch.py`` or the harness's hook
|
|
11
|
+
subtree), so no importable ``apothem`` package is required on the
|
|
12
|
+
host. Receives an event name and an optional message name; emits the
|
|
13
|
+
per-event Markdown context to stdout for the harness to surface in
|
|
14
|
+
the operator's chat session.
|
|
15
|
+
* ``apothem.hooks.emit_hook_context`` — the per-event context emitter
|
|
16
|
+
that materializes a Markdown payload from the bundled message
|
|
17
|
+
fixtures and the live ecosystem state.
|
|
18
|
+
* ``apothem.hooks.session_start_bootstrap`` — the SessionStart-event
|
|
19
|
+
bootstrap that surfaces the conformity posture, memory summary, plan
|
|
20
|
+
summary, and session-source banner at every fresh session.
|
|
21
|
+
|
|
22
|
+
Shared helpers live under ``apothem.hooks.lib`` (event taxonomy,
|
|
23
|
+
logging, root resolution).
|
|
24
|
+
"""
|
|
@@ -0,0 +1,380 @@
|
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
|
2
|
+
|
|
3
|
+
"""Call-time validator for the ``AskUserQuestion`` tool's option payload.
|
|
4
|
+
|
|
5
|
+
Why this validator exists. The option-annotation matchers
|
|
6
|
+
(``conformity/option_annotation_grep.py``) scan committed ``*.md`` artifacts —
|
|
7
|
+
they verify the canonical ``(Recommended)`` marker on rendered option sets that
|
|
8
|
+
already live in the tree. Nothing inspected the LIVE ``AskUserQuestion`` tool
|
|
9
|
+
payload at the moment the agent asks the operator, so a malformed or absent
|
|
10
|
+
marker reached the operator unchecked. This module is the dispatch-routed
|
|
11
|
+
``PreToolUse`` handler that closes that gap: it receives the tool input (a
|
|
12
|
+
``questions`` array, each question carrying ``options[].label`` and
|
|
13
|
+
``multiSelect``), validates marker well-formedness against the canonical rule,
|
|
14
|
+
and emits an advisory ``systemMessage`` (default) or — under the repo's strict
|
|
15
|
+
opt-in — a blocking ``decision`` envelope.
|
|
16
|
+
|
|
17
|
+
What it CAN guarantee. Well-formedness: a marker present on a label is in the
|
|
18
|
+
canonical form (capital ``(Recommended)``, exact end-of-label placement, one
|
|
19
|
+
leading space, not on a destructive option, at most one per single-select
|
|
20
|
+
question). These are objectively decidable from the payload alone.
|
|
21
|
+
|
|
22
|
+
What it CANNOT guarantee. The native ``AskUserQuestion`` payload carries no
|
|
23
|
+
separate "recommended" field — the marker IS the only signal of which option is
|
|
24
|
+
recommended. So this validator cannot prove a missing marker is a defect: a
|
|
25
|
+
single-select question with two substantive options and zero markers is merely
|
|
26
|
+
*advised* (a NUDGE) to mark its recommended option, never blocked. Forcing a
|
|
27
|
+
recommendation to exist is a behavioral-convention obligation the rules carry,
|
|
28
|
+
not a fact this runtime check can decide.
|
|
29
|
+
|
|
30
|
+
Contract. Fast (sub-second; pure-Python regex over a small payload),
|
|
31
|
+
idempotent, and FAIL-OPEN: any exception → allow the call (an empty envelope),
|
|
32
|
+
never crash the operator's question. The strict opt-in mirrors the conformity
|
|
33
|
+
gate's: the ``--strict`` flag or a truthy ``APOTHEM_CONFORMITY_STRICT``
|
|
34
|
+
environment variable escalates WELL-FORMEDNESS findings (never the NUDGE) to a
|
|
35
|
+
``decision: block``.
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
from __future__ import annotations
|
|
39
|
+
|
|
40
|
+
import json
|
|
41
|
+
import os
|
|
42
|
+
import re
|
|
43
|
+
import sys
|
|
44
|
+
from dataclasses import dataclass, field
|
|
45
|
+
from typing import Final
|
|
46
|
+
|
|
47
|
+
# Strict opt-in mirrors the conformity gate's (``conformity/gate.py``). Reused
|
|
48
|
+
# verbatim here so the AskUserQuestion validator and the per-Write gate share
|
|
49
|
+
# one strict-mode contract: the operator enables blocking once, for both.
|
|
50
|
+
STRICT_ENV: Final[str] = "APOTHEM_CONFORMITY_STRICT"
|
|
51
|
+
_STRICT_TRUTHY: Final[frozenset[str]] = frozenset({"1", "true", "yes", "on"})
|
|
52
|
+
|
|
53
|
+
# Canonical recommended-marker forms, kept in lockstep with the static matcher
|
|
54
|
+
# at ``conformity/option_annotation_grep.py``. The canonical postfix is capital
|
|
55
|
+
# ``(Recommended)`` at the exact end of the label with one leading space; the
|
|
56
|
+
# lowercase ``(recommended)`` form is a banned variant. We re-derive the same
|
|
57
|
+
# anchored patterns here because the static matcher operates on Markdown lines
|
|
58
|
+
# (``- `Label`:``) while this validator operates on a structured label string,
|
|
59
|
+
# so the line-shaped label regexes there do not apply — but the postfix rules
|
|
60
|
+
# (case, bracket, end-anchor) are identical and MUST NOT diverge.
|
|
61
|
+
_CANONICAL_POSTFIX_RE: Final[re.Pattern[str]] = re.compile(r" \(Recommended\)$")
|
|
62
|
+
_LOWERCASE_POSTFIX_RE: Final[re.Pattern[str]] = re.compile(r"\(recommended\)\s*$")
|
|
63
|
+
# Any non-canonical bracketing / spacing of the recommended token at the label
|
|
64
|
+
# tail: ``[Recommended]``, ``(Rec)``, ``(Recommended )``, ``( Recommended )`` —
|
|
65
|
+
# anything that names "recommend" near the tail but is not the exact canonical
|
|
66
|
+
# ``" (Recommended)"`` postfix.
|
|
67
|
+
_NONCANONICAL_TAIL_RE: Final[re.Pattern[str]] = re.compile(
|
|
68
|
+
r"[\[(]\s*rec(?:ommend(?:ed)?)?\s*[\])]\s*$", re.IGNORECASE
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
# Destructive-intent label tokens. A recommended marker on a clearly-destructive
|
|
72
|
+
# option contradicts the no-default floor for irreversible operations
|
|
73
|
+
# (``rules/interactive-questions-canonical-shapes.md`` §5.8): a recommendation
|
|
74
|
+
# marker on an irreversible action steers the operator toward the dangerous path.
|
|
75
|
+
_DESTRUCTIVE_LABEL_RE: Final[re.Pattern[str]] = re.compile(
|
|
76
|
+
r"\b("
|
|
77
|
+
r"delete|remove|retire|discard|destroy|wipe|erase|drop|purge|"
|
|
78
|
+
r"overwrite|reset|revert|rm|truncate|force[- ]?push"
|
|
79
|
+
r")\b",
|
|
80
|
+
re.IGNORECASE,
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
# A "substantive" option for the NUDGE heuristic: anything that is not the
|
|
84
|
+
# harness-implicit free-text escape hatch (the ``Other`` option the tool adds).
|
|
85
|
+
_IMPLICIT_OTHER_RE: Final[re.Pattern[str]] = re.compile(r"^\s*other\s*$", re.IGNORECASE)
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
@dataclass(frozen=True)
|
|
89
|
+
class Finding:
|
|
90
|
+
"""One validation result against the canonical marker rules.
|
|
91
|
+
|
|
92
|
+
``severity`` is ``"finding"`` for an objectively-decidable well-formedness
|
|
93
|
+
violation (escalatable to a block under strict mode) or ``"nudge"`` for the
|
|
94
|
+
heuristic missing-marker advisory (never escalated — it can only advise).
|
|
95
|
+
"""
|
|
96
|
+
|
|
97
|
+
severity: str
|
|
98
|
+
kind: str
|
|
99
|
+
question_index: int
|
|
100
|
+
label: str
|
|
101
|
+
detail: str
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
@dataclass(frozen=True)
|
|
105
|
+
class ValidationResult:
|
|
106
|
+
"""Aggregate outcome of validating one ``AskUserQuestion`` payload."""
|
|
107
|
+
|
|
108
|
+
findings: list[Finding] = field(default_factory=list)
|
|
109
|
+
|
|
110
|
+
@property
|
|
111
|
+
def wellformedness_findings(self) -> list[Finding]:
|
|
112
|
+
"""Objectively-decidable violations (block-eligible under strict mode)."""
|
|
113
|
+
return [f for f in self.findings if f.severity == "finding"]
|
|
114
|
+
|
|
115
|
+
@property
|
|
116
|
+
def nudges(self) -> list[Finding]:
|
|
117
|
+
"""Heuristic missing-marker advisories (never block)."""
|
|
118
|
+
return [f for f in self.findings if f.severity == "nudge"]
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def strict_enabled(argv: list[str] | None = None) -> bool:
|
|
122
|
+
"""Return True when the strict opt-in is active.
|
|
123
|
+
|
|
124
|
+
Mirrors ``conformity/gate.py``: the ``--strict`` flag or a truthy
|
|
125
|
+
``APOTHEM_CONFORMITY_STRICT`` environment variable enables blocking.
|
|
126
|
+
"""
|
|
127
|
+
args = argv if argv is not None else sys.argv[1:]
|
|
128
|
+
if "--strict" in args:
|
|
129
|
+
return True
|
|
130
|
+
return os.environ.get(STRICT_ENV, "").strip().lower() in _STRICT_TRUTHY
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def _label_is_canonical_marked(label: str) -> bool:
|
|
134
|
+
"""True iff the label ends with the exact canonical ``" (Recommended)"``."""
|
|
135
|
+
return _CANONICAL_POSTFIX_RE.search(label) is not None
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def _label_has_lowercase_marker(label: str) -> bool:
|
|
139
|
+
"""True iff the label ends with the banned lowercase ``(recommended)``."""
|
|
140
|
+
return _LOWERCASE_POSTFIX_RE.search(label) is not None
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def _label_has_noncanonical_marker(label: str) -> bool:
|
|
144
|
+
"""True iff the label tail names a recommended token in a non-canonical form.
|
|
145
|
+
|
|
146
|
+
Catches ``[Recommended]``, ``(Rec)``, ``(Recommended )``, ``( Recommended )``
|
|
147
|
+
and similar bracketed tail variants. The canonical ``" (Recommended)"`` form
|
|
148
|
+
is excluded (it is the legitimate postfix); the lowercase variant is reported
|
|
149
|
+
separately as ``non-canonical-postfix-case`` so its detail is precise.
|
|
150
|
+
"""
|
|
151
|
+
if _label_is_canonical_marked(label):
|
|
152
|
+
return False
|
|
153
|
+
if _label_has_lowercase_marker(label):
|
|
154
|
+
return False
|
|
155
|
+
return _NONCANONICAL_TAIL_RE.search(label) is not None
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
def _is_destructive_label(label: str) -> bool:
|
|
159
|
+
"""True iff the label names a clearly-destructive / irreversible action."""
|
|
160
|
+
return _DESTRUCTIVE_LABEL_RE.search(label) is not None
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def _is_substantive_option(label: str) -> bool:
|
|
164
|
+
"""True iff the option is a real choice, not the implicit ``Other`` escape."""
|
|
165
|
+
return bool(label.strip()) and _IMPLICIT_OTHER_RE.match(label) is None
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
def _option_labels(question: dict[str, object]) -> list[str]:
|
|
169
|
+
"""Extract the option labels from one question, tolerating shape drift."""
|
|
170
|
+
raw_options = question.get("options")
|
|
171
|
+
if not isinstance(raw_options, list):
|
|
172
|
+
return []
|
|
173
|
+
labels: list[str] = []
|
|
174
|
+
for option in raw_options:
|
|
175
|
+
if isinstance(option, str):
|
|
176
|
+
labels.append(option)
|
|
177
|
+
elif isinstance(option, dict):
|
|
178
|
+
label = option.get("label")
|
|
179
|
+
if isinstance(label, str):
|
|
180
|
+
labels.append(label)
|
|
181
|
+
return labels
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
def _validate_question(index: int, question: dict[str, object]) -> list[Finding]:
|
|
185
|
+
"""Validate one question's option labels against the canonical marker rules."""
|
|
186
|
+
findings: list[Finding] = []
|
|
187
|
+
labels = _option_labels(question)
|
|
188
|
+
if not labels:
|
|
189
|
+
return findings
|
|
190
|
+
multi_select = bool(question.get("multiSelect", False))
|
|
191
|
+
|
|
192
|
+
canonical_marked = 0
|
|
193
|
+
for label in labels:
|
|
194
|
+
if _label_has_lowercase_marker(label):
|
|
195
|
+
findings.append(
|
|
196
|
+
Finding(
|
|
197
|
+
severity="finding",
|
|
198
|
+
kind="non-canonical-postfix-case",
|
|
199
|
+
question_index=index,
|
|
200
|
+
label=label,
|
|
201
|
+
detail=(
|
|
202
|
+
"label uses lowercase (recommended); the canonical form "
|
|
203
|
+
"is the capital (Recommended) postfix"
|
|
204
|
+
),
|
|
205
|
+
)
|
|
206
|
+
)
|
|
207
|
+
elif _label_has_noncanonical_marker(label):
|
|
208
|
+
findings.append(
|
|
209
|
+
Finding(
|
|
210
|
+
severity="finding",
|
|
211
|
+
kind="non-canonical-postfix-form",
|
|
212
|
+
question_index=index,
|
|
213
|
+
label=label,
|
|
214
|
+
detail=(
|
|
215
|
+
"label names a recommended token in a non-canonical "
|
|
216
|
+
"bracket/spacing form; the canonical form is the exact "
|
|
217
|
+
'" (Recommended)" postfix with one leading space at the '
|
|
218
|
+
"end of the label"
|
|
219
|
+
),
|
|
220
|
+
)
|
|
221
|
+
)
|
|
222
|
+
elif _label_is_canonical_marked(label):
|
|
223
|
+
canonical_marked += 1
|
|
224
|
+
if _is_destructive_label(label):
|
|
225
|
+
findings.append(
|
|
226
|
+
Finding(
|
|
227
|
+
severity="finding",
|
|
228
|
+
kind="recommended-on-destructive",
|
|
229
|
+
question_index=index,
|
|
230
|
+
label=label,
|
|
231
|
+
detail=(
|
|
232
|
+
"the (Recommended) marker rides a clearly-destructive "
|
|
233
|
+
"option; an irreversible action must not be the "
|
|
234
|
+
"recommended (and easy) path"
|
|
235
|
+
),
|
|
236
|
+
)
|
|
237
|
+
)
|
|
238
|
+
|
|
239
|
+
if not multi_select and canonical_marked > 1:
|
|
240
|
+
findings.append(
|
|
241
|
+
Finding(
|
|
242
|
+
severity="finding",
|
|
243
|
+
kind="single-select-multi-recommended",
|
|
244
|
+
question_index=index,
|
|
245
|
+
label="",
|
|
246
|
+
detail=(
|
|
247
|
+
f"single-select question carries {canonical_marked} "
|
|
248
|
+
"(Recommended) markers; at most one is permitted "
|
|
249
|
+
"(set multiSelect: true to recommend several)"
|
|
250
|
+
),
|
|
251
|
+
)
|
|
252
|
+
)
|
|
253
|
+
|
|
254
|
+
# NUDGE: a single-select question with two or more substantive options and
|
|
255
|
+
# zero markers. Heuristic only — the native payload carries no separate
|
|
256
|
+
# "recommended" field, so a missing marker cannot be proven a defect.
|
|
257
|
+
substantive = [lab for lab in labels if _is_substantive_option(lab)]
|
|
258
|
+
if not multi_select and len(substantive) >= 2 and canonical_marked == 0:
|
|
259
|
+
findings.append(
|
|
260
|
+
Finding(
|
|
261
|
+
severity="nudge",
|
|
262
|
+
kind="no-recommended-marker",
|
|
263
|
+
question_index=index,
|
|
264
|
+
label="",
|
|
265
|
+
detail=(
|
|
266
|
+
f"single-select question offers {len(substantive)} "
|
|
267
|
+
"substantive options but marks none (Recommended); the "
|
|
268
|
+
"option-annotation discipline advises marking the "
|
|
269
|
+
"recommended option so the operator sees the agent's "
|
|
270
|
+
"evaluation"
|
|
271
|
+
),
|
|
272
|
+
)
|
|
273
|
+
)
|
|
274
|
+
return findings
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
def validate_payload(payload: dict[str, object] | None) -> ValidationResult:
|
|
278
|
+
"""Validate an ``AskUserQuestion`` tool payload's option markers.
|
|
279
|
+
|
|
280
|
+
Pre-condition: *payload* is the harness ``PreToolUse`` stdin object (or
|
|
281
|
+
``None`` when stdin carried no parseable object). Post-condition: every
|
|
282
|
+
well-formedness violation and missing-marker nudge across every question is
|
|
283
|
+
enumerated; an unparseable or shapeless payload yields an empty result
|
|
284
|
+
(fail-open — nothing to validate).
|
|
285
|
+
"""
|
|
286
|
+
if not payload:
|
|
287
|
+
return ValidationResult()
|
|
288
|
+
tool_input = payload.get("tool_input")
|
|
289
|
+
if not isinstance(tool_input, dict):
|
|
290
|
+
return ValidationResult()
|
|
291
|
+
questions = tool_input.get("questions")
|
|
292
|
+
if not isinstance(questions, list):
|
|
293
|
+
return ValidationResult()
|
|
294
|
+
findings: list[Finding] = []
|
|
295
|
+
for index, question in enumerate(questions):
|
|
296
|
+
if isinstance(question, dict):
|
|
297
|
+
findings.extend(_validate_question(index, question))
|
|
298
|
+
return ValidationResult(findings=findings)
|
|
299
|
+
|
|
300
|
+
|
|
301
|
+
def _read_payload() -> dict[str, object] | None:
|
|
302
|
+
"""Read and parse the hook's stdin JSON payload, fail-open on any error."""
|
|
303
|
+
try:
|
|
304
|
+
if sys.stdin.isatty():
|
|
305
|
+
return None
|
|
306
|
+
raw = sys.stdin.read()
|
|
307
|
+
except OSError:
|
|
308
|
+
return None
|
|
309
|
+
if not raw or not raw.strip():
|
|
310
|
+
return None
|
|
311
|
+
try:
|
|
312
|
+
parsed = json.loads(raw)
|
|
313
|
+
except (json.JSONDecodeError, ValueError):
|
|
314
|
+
return None
|
|
315
|
+
return parsed if isinstance(parsed, dict) else None
|
|
316
|
+
|
|
317
|
+
|
|
318
|
+
def _format_findings(findings: list[Finding]) -> str:
|
|
319
|
+
"""Render findings as one human-readable line each."""
|
|
320
|
+
lines: list[str] = []
|
|
321
|
+
for finding in findings:
|
|
322
|
+
label = f" label={finding.label!r}" if finding.label else ""
|
|
323
|
+
lines.append(
|
|
324
|
+
f" - [Q{finding.question_index}] {finding.kind}{label}: {finding.detail}"
|
|
325
|
+
)
|
|
326
|
+
return "\n".join(lines)
|
|
327
|
+
|
|
328
|
+
|
|
329
|
+
def build_envelope(result: ValidationResult, *, strict: bool) -> dict[str, object]:
|
|
330
|
+
"""Map a validation result to a hook-output envelope.
|
|
331
|
+
|
|
332
|
+
Default (advisory): a ``systemMessage`` surfacing well-formedness findings
|
|
333
|
+
and nudges; the question proceeds. Strict: a ``decision: block`` envelope
|
|
334
|
+
when well-formedness findings exist (nudges never block). A clean result
|
|
335
|
+
yields an empty envelope (the question proceeds silently).
|
|
336
|
+
"""
|
|
337
|
+
wellformedness = result.wellformedness_findings
|
|
338
|
+
nudges = result.nudges
|
|
339
|
+
if strict and wellformedness:
|
|
340
|
+
reason_lines = [
|
|
341
|
+
"Apothem AskUserQuestion guard (strict): the option set carries "
|
|
342
|
+
"(Recommended)-marker well-formedness violation(s). Fix the "
|
|
343
|
+
"label(s) and re-ask:",
|
|
344
|
+
_format_findings(wellformedness),
|
|
345
|
+
]
|
|
346
|
+
if nudges:
|
|
347
|
+
reason_lines.append("Advisory (not blocking):\n" + _format_findings(nudges))
|
|
348
|
+
return {"decision": "block", "reason": "\n".join(reason_lines)}
|
|
349
|
+
|
|
350
|
+
advisory: list[Finding] = [*wellformedness, *nudges]
|
|
351
|
+
if not advisory:
|
|
352
|
+
return {}
|
|
353
|
+
header = (
|
|
354
|
+
"Apothem AskUserQuestion guard (advisory): review the "
|
|
355
|
+
"(Recommended)-marker finding(s) below; the question proceeds. "
|
|
356
|
+
"Set APOTHEM_CONFORMITY_STRICT=1 (or pass --strict) to block on "
|
|
357
|
+
"well-formedness violations."
|
|
358
|
+
)
|
|
359
|
+
return {"systemMessage": header + "\n" + _format_findings(advisory)}
|
|
360
|
+
|
|
361
|
+
|
|
362
|
+
def main(argv: list[str] | None = None) -> None:
|
|
363
|
+
"""Entry point. Validates the payload and emits one envelope; never raises.
|
|
364
|
+
|
|
365
|
+
FAIL-OPEN: any unexpected exception is swallowed and an empty (allow)
|
|
366
|
+
envelope is written, so a validator bug never crashes the operator's
|
|
367
|
+
question.
|
|
368
|
+
"""
|
|
369
|
+
try:
|
|
370
|
+
strict = strict_enabled(argv)
|
|
371
|
+
payload = _read_payload()
|
|
372
|
+
result = validate_payload(payload)
|
|
373
|
+
envelope = build_envelope(result, strict=strict)
|
|
374
|
+
sys.stdout.write(json.dumps(envelope, separators=(",", ":")) + "\n")
|
|
375
|
+
except Exception: # noqa: BLE001, RUF100 - fail-open boundary: a validator error must never block the operator's question; emit an allow envelope and proceed (BLE001 is the intent marker; RUF100 self-suppresses because ruff's BLE family is not active)
|
|
376
|
+
sys.stdout.write("{}\n")
|
|
377
|
+
|
|
378
|
+
|
|
379
|
+
if __name__ == "__main__":
|
|
380
|
+
main()
|