@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,599 @@
|
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
|
2
|
+
|
|
3
|
+
"""Plugin source-tree assembler and manifest generator.
|
|
4
|
+
|
|
5
|
+
This module materializes the canonical ``.claude-plugin`` distribution unit:
|
|
6
|
+
a self-contained bundle of the apothem engine plus its catalog (skills,
|
|
7
|
+
agents, commands, rules, hooks) that runs directly from the assembled tree —
|
|
8
|
+
the engine and its vendored dependencies import in place, and no package
|
|
9
|
+
installation occurs at any point.
|
|
10
|
+
|
|
11
|
+
:func:`assemble_plugin_tree` copies the ``apothem`` package verbatim into
|
|
12
|
+
``lib/apothem`` and writes a thin ``apothem_lib`` shim re-exporting its
|
|
13
|
+
public submodules; the engine is never renamed or rewritten.
|
|
14
|
+
|
|
15
|
+
The layout contract produced is::
|
|
16
|
+
|
|
17
|
+
<plugin_root>/.claude-plugin/plugin.json
|
|
18
|
+
<plugin_root>/lib/apothem/ (verbatim engine copy)
|
|
19
|
+
<plugin_root>/lib/apothem_lib.py (re-export shim)
|
|
20
|
+
<plugin_root>/lib/apothem/_vendor/ (vendored deps, or empty .keep)
|
|
21
|
+
<plugin_root>/skills|agents|commands|hooks|rules/ (catalog copies)
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
from __future__ import annotations
|
|
25
|
+
|
|
26
|
+
import json
|
|
27
|
+
import re
|
|
28
|
+
import shutil
|
|
29
|
+
from pathlib import Path
|
|
30
|
+
from typing import Final
|
|
31
|
+
|
|
32
|
+
import jsonschema
|
|
33
|
+
|
|
34
|
+
#: Plugin identifier. The plugin *is* apothem (verbatim engine reuse).
|
|
35
|
+
PLUGIN_NAME: Final[str] = "apothem"
|
|
36
|
+
|
|
37
|
+
#: Capability-led description embedded in the generated manifest.
|
|
38
|
+
PLUGIN_DESCRIPTION: Final[str] = (
|
|
39
|
+
"Host-agnostic AI-harness configuration catalog: review and audit "
|
|
40
|
+
"commands, research and quality agents, and reusable skills, bundled "
|
|
41
|
+
"with the self-contained apothem engine."
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
#: Manifest metadata embedded in every generated plugin manifest.
|
|
45
|
+
PLUGIN_AUTHOR: Final[dict[str, str]] = {
|
|
46
|
+
"name": "Ahmed G. Gad",
|
|
47
|
+
"email": "me@ahmedgad.com",
|
|
48
|
+
"url": "https://github.com/ahmed-g-gad",
|
|
49
|
+
}
|
|
50
|
+
PLUGIN_HOMEPAGE: Final[str] = "https://apothem.ahmedgad.com"
|
|
51
|
+
PLUGIN_REPOSITORY: Final[str] = "https://github.com/ahmed-g-gad/apothem"
|
|
52
|
+
PLUGIN_LICENSE: Final[str] = "MIT"
|
|
53
|
+
PLUGIN_KEYWORDS: Final[tuple[str, ...]] = (
|
|
54
|
+
"configuration",
|
|
55
|
+
"profiles",
|
|
56
|
+
"skills",
|
|
57
|
+
"agents",
|
|
58
|
+
"commands",
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
#: Documentation files that live beside catalog members but are never members.
|
|
62
|
+
_CATALOG_DOC_FILES: Final[frozenset[str]] = frozenset({"README.md", "AGENTS.md"})
|
|
63
|
+
|
|
64
|
+
#: Directory names that are pruned from the engine copy (never distributed).
|
|
65
|
+
_COPY_IGNORE: Final[tuple[str, ...]] = (
|
|
66
|
+
"__pycache__",
|
|
67
|
+
"*.pyc",
|
|
68
|
+
"*.pyo",
|
|
69
|
+
".pytest_cache",
|
|
70
|
+
".mypy_cache",
|
|
71
|
+
".ruff_cache",
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
#: Catalog directory names copied from the engine source into the plugin root.
|
|
75
|
+
_CATALOG_DIRS: Final[tuple[str, ...]] = (
|
|
76
|
+
"skills",
|
|
77
|
+
"agents",
|
|
78
|
+
"commands",
|
|
79
|
+
"rules",
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
#: Source-relative location of the hook message catalog.
|
|
83
|
+
_HOOK_MESSAGES_REL: Final[Path] = Path("hooks") / "messages"
|
|
84
|
+
|
|
85
|
+
#: Plugin-root-relative engine prefix for the assembled tree. The engine is
|
|
86
|
+
#: copied verbatim to ``lib/apothem`` by :func:`assemble_plugin_tree`, so the
|
|
87
|
+
#: bootstrap stubs, the dispatcher, and the message catalog all resolve under
|
|
88
|
+
#: this prefix when the plugin is installed alone from the assembled bundle.
|
|
89
|
+
_ASSEMBLED_ENGINE_ROOT_REL: Final[str] = "lib/apothem"
|
|
90
|
+
|
|
91
|
+
#: Plugin-root-relative engine prefix for the repository-root manifest. When
|
|
92
|
+
#: the repository root *is* the plugin root (the marketplace install whose
|
|
93
|
+
#: plugin root is the checkout), the engine lives at ``src/apothem``.
|
|
94
|
+
_REPO_ENGINE_ROOT_REL: Final[str] = "src/apothem"
|
|
95
|
+
|
|
96
|
+
#: The hook-event block, mirrored from the engine's claude_code
|
|
97
|
+
#: ``settings.json`` template. Each entry is a dispatch-routable event the
|
|
98
|
+
#: shell bootstrap stub (``hooks/lib/bootstrap.{sh,ps1}``) can drive: the stub
|
|
99
|
+
#: self-locates a CPython interpreter and execs ``hooks/dispatch.py`` with the
|
|
100
|
+
#: event name and, where the event emits a Markdown context, the message file's
|
|
101
|
+
#: full path. Conformity-gate entries from ``settings.json``
|
|
102
|
+
#: (``gate.py --hook``) are intentionally NOT mirrored here: the bootstrap stub
|
|
103
|
+
#: drives only the dispatcher, and the gate's user-scope default applies under
|
|
104
|
+
#: a harness root (``~/.claude`` / ``~/.codex``), not a plugin-alone project
|
|
105
|
+
#: write — wiring it plugin-alone would require an engine install. The per-event
|
|
106
|
+
#: timeouts and matchers track the ``settings.json`` block verbatim so the
|
|
107
|
+
#: plugin-alone and engine-install postures stay coherent.
|
|
108
|
+
#:
|
|
109
|
+
#: Each tuple is ``(event_name, matcher, timeout_seconds, message_basename)``;
|
|
110
|
+
#: ``message_basename`` is ``None`` for events the dispatcher handles without a
|
|
111
|
+
#: Markdown context (``SessionStart`` runs the session-start bootstrap directly).
|
|
112
|
+
_PLUGIN_HOOK_ENTRIES: Final[tuple[tuple[str, str, int, str | None], ...]] = (
|
|
113
|
+
("SessionStart", "*", 30, None),
|
|
114
|
+
("PreToolUse", "Write", 10, "pretooluse-write"),
|
|
115
|
+
("PreToolUse", "Write", 10, "pretooluse-write-header-guard"),
|
|
116
|
+
("PreToolUse", "Write", 10, "pretooluse-write-plan-guard"),
|
|
117
|
+
("PreToolUse", "Write", 10, "pretooluse-dependency-guard"),
|
|
118
|
+
("PreToolUse", "Write", 10, "pretooluse-eval-guard"),
|
|
119
|
+
("PreToolUse", "Edit", 10, "pretooluse-edit"),
|
|
120
|
+
("PreToolUse", "Edit", 10, "pretooluse-edit-header-guard"),
|
|
121
|
+
("PreToolUse", "Edit", 10, "pretooluse-write-plan-guard"),
|
|
122
|
+
("PreToolUse", "Edit", 10, "pretooluse-dependency-guard"),
|
|
123
|
+
("PreToolUse", "Edit", 10, "pretooluse-eval-guard"),
|
|
124
|
+
("PreToolUse", "NotebookEdit", 10, "pretooluse-notebookedit"),
|
|
125
|
+
("PreToolUse", "NotebookEdit", 10, "pretooluse-write-plan-guard"),
|
|
126
|
+
("PreToolUse", "Bash", 10, "pretooluse-bash"),
|
|
127
|
+
("PreToolUse", "Bash", 10, "pretooluse-bash-plan-guard"),
|
|
128
|
+
("PreToolUse", "Bash", 10, "pretooluse-eval-guard"),
|
|
129
|
+
("PreToolUse", "AskUserQuestion", 10, "pretooluse-askuserquestion-recommended"),
|
|
130
|
+
("PostToolUse", "*", 10, "posttooluse-proactive-compaction"),
|
|
131
|
+
("PreCompact", "*", 30, "precompact"),
|
|
132
|
+
("PostCompact", "*", 30, "postcompact"),
|
|
133
|
+
("Stop", "*", 60, "stop"),
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
#: The thin alias shim re-exporting the engine's public submodules.
|
|
137
|
+
_SHIM_BODY: Final[str] = '''# SPDX-License-Identifier: MIT
|
|
138
|
+
|
|
139
|
+
"""Thin alias shim re-exporting the bundled apothem engine's public API.
|
|
140
|
+
|
|
141
|
+
Importing ``apothem_lib`` makes the engine's public submodules reachable
|
|
142
|
+
under one flat namespace (``apothem_lib.profile``, ``apothem_lib.adapters``,
|
|
143
|
+
...) without requiring callers to know the bundled package layout.
|
|
144
|
+
"""
|
|
145
|
+
|
|
146
|
+
from __future__ import annotations
|
|
147
|
+
|
|
148
|
+
from apothem import __version__
|
|
149
|
+
from apothem import harnesses as adapters
|
|
150
|
+
from apothem.lib import profile, propagation, reporter
|
|
151
|
+
|
|
152
|
+
__all__ = [
|
|
153
|
+
"__version__",
|
|
154
|
+
"adapters",
|
|
155
|
+
"profile",
|
|
156
|
+
"propagation",
|
|
157
|
+
"reporter",
|
|
158
|
+
]
|
|
159
|
+
'''
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
class PluginAssemblyError(RuntimeError):
|
|
163
|
+
"""Raised when the plugin source tree cannot be assembled.
|
|
164
|
+
|
|
165
|
+
The message names the offending path so the caller can diagnose a
|
|
166
|
+
malformed source (e.g. a missing catalog directory). The assembler
|
|
167
|
+
never silently skips a malformed input.
|
|
168
|
+
"""
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
def _version_from_pyproject() -> str | None:
|
|
172
|
+
"""Read ``[project].version`` from the repo's ``pyproject.toml`` when present.
|
|
173
|
+
|
|
174
|
+
The repo's own manifest must track the authoritative in-tree version
|
|
175
|
+
rather than the version of whatever copy is installed in site-packages
|
|
176
|
+
(an editable install can be stale). Walks upward from this module to the
|
|
177
|
+
nearest ``pyproject.toml``.
|
|
178
|
+
|
|
179
|
+
Returns:
|
|
180
|
+
The declared version, or ``None`` when no ``pyproject.toml`` is found
|
|
181
|
+
or it declares no project version.
|
|
182
|
+
"""
|
|
183
|
+
for parent in Path(__file__).resolve().parents:
|
|
184
|
+
candidate = parent / "pyproject.toml"
|
|
185
|
+
if not candidate.is_file():
|
|
186
|
+
continue
|
|
187
|
+
text = candidate.read_text(encoding="utf-8")
|
|
188
|
+
match = re.search(
|
|
189
|
+
r"^\s*version\s*=\s*[\"']([^\"']+)[\"']", text, flags=re.MULTILINE
|
|
190
|
+
)
|
|
191
|
+
return match.group(1) if match else None
|
|
192
|
+
return None
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
def _resolve_version(version: str | None) -> str:
|
|
196
|
+
"""Resolve the manifest version from the authoritative source.
|
|
197
|
+
|
|
198
|
+
Precedence: an explicit argument, then the repo's ``pyproject.toml``,
|
|
199
|
+
then the installed ``apothem.__version__`` as a last resort.
|
|
200
|
+
|
|
201
|
+
Args:
|
|
202
|
+
version: Explicit version, or ``None`` to resolve automatically.
|
|
203
|
+
|
|
204
|
+
Returns:
|
|
205
|
+
A ``MAJOR.MINOR.PATCH`` SemVer string.
|
|
206
|
+
"""
|
|
207
|
+
if version is not None:
|
|
208
|
+
return version
|
|
209
|
+
declared = _version_from_pyproject()
|
|
210
|
+
if declared is not None:
|
|
211
|
+
return declared
|
|
212
|
+
from apothem import __version__
|
|
213
|
+
|
|
214
|
+
return __version__
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
def _enumerate_members(catalog_root: Path) -> dict[str, list[str]]:
|
|
218
|
+
"""Enumerate catalog members under ``catalog_root``.
|
|
219
|
+
|
|
220
|
+
Skills are directories carrying a ``SKILL.md`` entry point (id = dirname).
|
|
221
|
+
Agents, commands, and rules are flat ``*.md`` files (id = stem). Hooks are
|
|
222
|
+
the hook message ``*.md`` files (id = stem). ``README.md`` and
|
|
223
|
+
``AGENTS.md`` index files are excluded — they are documentation, not
|
|
224
|
+
catalog members.
|
|
225
|
+
|
|
226
|
+
Args:
|
|
227
|
+
catalog_root: The apothem source package directory.
|
|
228
|
+
|
|
229
|
+
Returns:
|
|
230
|
+
A mapping of member kind to a sorted list of member ids.
|
|
231
|
+
|
|
232
|
+
Raises:
|
|
233
|
+
PluginAssemblyError: When a required catalog directory is absent.
|
|
234
|
+
"""
|
|
235
|
+
|
|
236
|
+
def _require_dir(rel: Path) -> Path:
|
|
237
|
+
path = catalog_root / rel
|
|
238
|
+
if not path.is_dir():
|
|
239
|
+
raise PluginAssemblyError(f"required catalog directory missing: {path}")
|
|
240
|
+
return path
|
|
241
|
+
|
|
242
|
+
skills_dir = _require_dir(Path("skills"))
|
|
243
|
+
skills = sorted(
|
|
244
|
+
child.name
|
|
245
|
+
for child in skills_dir.iterdir()
|
|
246
|
+
if child.is_dir() and (child / "SKILL.md").is_file()
|
|
247
|
+
)
|
|
248
|
+
|
|
249
|
+
def _markdown_stems(rel: Path) -> list[str]:
|
|
250
|
+
directory = _require_dir(rel)
|
|
251
|
+
return sorted(
|
|
252
|
+
md.stem
|
|
253
|
+
for md in directory.glob("*.md")
|
|
254
|
+
if md.name not in _CATALOG_DOC_FILES
|
|
255
|
+
)
|
|
256
|
+
|
|
257
|
+
return {
|
|
258
|
+
"skills": skills,
|
|
259
|
+
"agents": _markdown_stems(Path("agents")),
|
|
260
|
+
"commands": _markdown_stems(Path("commands")),
|
|
261
|
+
"rules": _markdown_stems(Path("rules")),
|
|
262
|
+
"hooks": _markdown_stems(_HOOK_MESSAGES_REL),
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
def build_plugin_manifest(
|
|
267
|
+
catalog_root: Path,
|
|
268
|
+
*,
|
|
269
|
+
version: str | None = None,
|
|
270
|
+
catalog_prefix: str = "./",
|
|
271
|
+
address_default_command_dir: bool = False,
|
|
272
|
+
) -> dict[str, object]:
|
|
273
|
+
"""Build a Claude Code plugin manifest dict from the apothem catalog.
|
|
274
|
+
|
|
275
|
+
Enumerates every catalog member under ``catalog_root`` and assembles a
|
|
276
|
+
manifest conforming to ``plugin.schema.json``. Commands and agents are
|
|
277
|
+
declared as explicit file paths so the documentation files beside them
|
|
278
|
+
are never registered as components; skills are declared as the skills
|
|
279
|
+
directory (each immediate child carries a ``SKILL.md`` entry point).
|
|
280
|
+
Rules and hook message contexts are engine cohorts consumed by the
|
|
281
|
+
apothem materializers, not plugin components, so they carry no manifest
|
|
282
|
+
field. Component arrays are sorted for determinism: the same catalog
|
|
283
|
+
always yields the same manifest.
|
|
284
|
+
|
|
285
|
+
Args:
|
|
286
|
+
catalog_root: The apothem source package directory (the dir holding
|
|
287
|
+
``skills/``, ``agents/``, ``commands/``, ``rules/``, ``hooks/``).
|
|
288
|
+
version: Explicit SemVer string, or ``None`` to resolve from
|
|
289
|
+
``pyproject.toml`` / ``apothem.__version__``.
|
|
290
|
+
catalog_prefix: Plugin-root-relative prefix of the catalog
|
|
291
|
+
directories, beginning with ``./`` — ``./`` when the catalog
|
|
292
|
+
sits at the plugin root (the assembled tree), or
|
|
293
|
+
``./src/apothem/`` when the repository root is the plugin root.
|
|
294
|
+
address_default_command_dir: When ``True``, prepend the plugin-root
|
|
295
|
+
default ``./commands/`` directory to the ``commands`` array. The
|
|
296
|
+
repository root is simultaneously this Claude Code plugin's root
|
|
297
|
+
and the Gemini / Qwen extension root, so a bare ``./commands/``
|
|
298
|
+
(holding the extensions' TOML passthrough command, not a Claude
|
|
299
|
+
``*.md`` command) sits at the plugin root. Claude Code v2.1.140+
|
|
300
|
+
flags that default folder as "ignored" unless the manifest
|
|
301
|
+
addresses it explicitly; listing it — the folder holds no
|
|
302
|
+
``*.md``, so no Claude command loads from it — suppresses the
|
|
303
|
+
note while the explicit nested ``commands`` paths stay
|
|
304
|
+
authoritative. Set ``True`` only when ``catalog_prefix`` is
|
|
305
|
+
nested (the repository-root manifest); the assembled tree's
|
|
306
|
+
commands already point into its own ``./commands/`` and need no
|
|
307
|
+
extra entry.
|
|
308
|
+
|
|
309
|
+
Returns:
|
|
310
|
+
A manifest dict validating against ``plugin.schema.json``.
|
|
311
|
+
|
|
312
|
+
Raises:
|
|
313
|
+
PluginAssemblyError: When a required catalog directory is absent.
|
|
314
|
+
jsonschema.ValidationError: When the assembled manifest does not
|
|
315
|
+
conform to the plugin schema.
|
|
316
|
+
"""
|
|
317
|
+
members = _enumerate_members(catalog_root)
|
|
318
|
+
command_paths = sorted(
|
|
319
|
+
f"{catalog_prefix}commands/{stem}.md" for stem in members["commands"]
|
|
320
|
+
)
|
|
321
|
+
if address_default_command_dir:
|
|
322
|
+
command_paths = ["./commands/", *command_paths]
|
|
323
|
+
engine_root_rel = _engine_root_rel_for(catalog_prefix)
|
|
324
|
+
manifest: dict[str, object] = {
|
|
325
|
+
"name": PLUGIN_NAME,
|
|
326
|
+
"version": _resolve_version(version),
|
|
327
|
+
"description": PLUGIN_DESCRIPTION,
|
|
328
|
+
"author": dict(PLUGIN_AUTHOR),
|
|
329
|
+
"homepage": PLUGIN_HOMEPAGE,
|
|
330
|
+
"repository": PLUGIN_REPOSITORY,
|
|
331
|
+
"license": PLUGIN_LICENSE,
|
|
332
|
+
"keywords": list(PLUGIN_KEYWORDS),
|
|
333
|
+
"commands": command_paths,
|
|
334
|
+
"agents": sorted(
|
|
335
|
+
f"{catalog_prefix}agents/{stem}.md" for stem in members["agents"]
|
|
336
|
+
),
|
|
337
|
+
"skills": [f"{catalog_prefix}skills/"],
|
|
338
|
+
"hooks": f"./{engine_root_rel}/hooks/hooks.json",
|
|
339
|
+
}
|
|
340
|
+
_validate_manifest(manifest)
|
|
341
|
+
return manifest
|
|
342
|
+
|
|
343
|
+
|
|
344
|
+
def _engine_root_rel_for(catalog_prefix: str) -> str:
|
|
345
|
+
"""Map a catalog prefix to its plugin-root-relative engine prefix.
|
|
346
|
+
|
|
347
|
+
The catalog directories and the engine sit at distinct locations in the
|
|
348
|
+
two supported layouts:
|
|
349
|
+
|
|
350
|
+
* Assembled tree (``catalog_prefix == "./"``): the catalog is at the
|
|
351
|
+
plugin root, but the verbatim engine copy lives under ``lib/apothem``.
|
|
352
|
+
* Repository-root manifest (``catalog_prefix == "./src/apothem/"``): the
|
|
353
|
+
catalog and the engine share the ``src/apothem`` prefix.
|
|
354
|
+
|
|
355
|
+
Args:
|
|
356
|
+
catalog_prefix: The plugin-root-relative catalog prefix.
|
|
357
|
+
|
|
358
|
+
Returns:
|
|
359
|
+
The plugin-root-relative engine prefix (no leading ``./``, no
|
|
360
|
+
trailing slash).
|
|
361
|
+
"""
|
|
362
|
+
if catalog_prefix == "./":
|
|
363
|
+
return _ASSEMBLED_ENGINE_ROOT_REL
|
|
364
|
+
return catalog_prefix.strip("./")
|
|
365
|
+
|
|
366
|
+
|
|
367
|
+
def _bootstrap_command(engine_root_rel: str, shell: str, suffix: str) -> str:
|
|
368
|
+
"""Render a single bootstrap-stub ``command`` string for a hook entry.
|
|
369
|
+
|
|
370
|
+
The command invokes the shell bootstrap stub under the plugin install
|
|
371
|
+
directory (``${CLAUDE_PLUGIN_ROOT}``, the placeholder Claude Code
|
|
372
|
+
substitutes and exports). The stub self-locates a CPython >= 3.10
|
|
373
|
+
interpreter and execs ``hooks/dispatch.py``; no engine install or
|
|
374
|
+
install-time ``${PYTHON_BIN}`` substitution is required.
|
|
375
|
+
|
|
376
|
+
Args:
|
|
377
|
+
engine_root_rel: Plugin-root-relative engine prefix
|
|
378
|
+
(``lib/apothem`` for the assembled tree, ``src/apothem`` for the
|
|
379
|
+
repository-root manifest).
|
|
380
|
+
shell: ``"bash"`` (drives ``bootstrap.sh``) or ``"powershell"``
|
|
381
|
+
(drives ``bootstrap.ps1``).
|
|
382
|
+
suffix: The argument string appended after the stub path — the event
|
|
383
|
+
name plus, where present, the message-file path. Already shaped
|
|
384
|
+
for the target shell's argument convention.
|
|
385
|
+
|
|
386
|
+
Returns:
|
|
387
|
+
The ``command`` string for one ``hooks.json`` command entry.
|
|
388
|
+
"""
|
|
389
|
+
if shell == "powershell":
|
|
390
|
+
stub = f"${{CLAUDE_PLUGIN_ROOT}}/{engine_root_rel}/hooks/lib/bootstrap.ps1"
|
|
391
|
+
return f'pwsh -NoProfile -File "{stub}" {suffix}'
|
|
392
|
+
stub = f"${{CLAUDE_PLUGIN_ROOT}}/{engine_root_rel}/hooks/lib/bootstrap.sh"
|
|
393
|
+
return f'"{stub}" {suffix}'
|
|
394
|
+
|
|
395
|
+
|
|
396
|
+
def _entry_command_block(
|
|
397
|
+
engine_root_rel: str, event: str, timeout: int, message: str | None
|
|
398
|
+
) -> list[dict[str, object]]:
|
|
399
|
+
"""Return the bash + powershell command pair for one hook entry.
|
|
400
|
+
|
|
401
|
+
Each dispatch-routable entry is mirrored as two ``type: command`` hooks —
|
|
402
|
+
one driving the POSIX ``bootstrap.sh`` (``shell: bash``) and one driving
|
|
403
|
+
``bootstrap.ps1`` (``shell: powershell``) — so the hook fires on every
|
|
404
|
+
host. The bootstrap stubs are fail-open (exit 0 on any error) so neither
|
|
405
|
+
entry can stall the harness.
|
|
406
|
+
|
|
407
|
+
Args:
|
|
408
|
+
engine_root_rel: Plugin-root-relative engine prefix.
|
|
409
|
+
event: The hook event name (e.g. ``PreToolUse``).
|
|
410
|
+
timeout: Per-entry timeout in seconds, mirrored from the engine's
|
|
411
|
+
``settings.json`` block.
|
|
412
|
+
message: The message-file basename (without ``.md``), or ``None`` for
|
|
413
|
+
events the dispatcher handles without a Markdown context.
|
|
414
|
+
|
|
415
|
+
Returns:
|
|
416
|
+
A two-element list of command-hook dicts (bash then powershell).
|
|
417
|
+
"""
|
|
418
|
+
if message is None:
|
|
419
|
+
suffix = event
|
|
420
|
+
else:
|
|
421
|
+
msg_rel = f"{engine_root_rel}/hooks/messages/{message}.md"
|
|
422
|
+
suffix = f'{event} "${{CLAUDE_PLUGIN_ROOT}}/{msg_rel}"'
|
|
423
|
+
return [
|
|
424
|
+
{
|
|
425
|
+
"type": "command",
|
|
426
|
+
"command": _bootstrap_command(engine_root_rel, shell, suffix),
|
|
427
|
+
"timeout": timeout,
|
|
428
|
+
"shell": shell,
|
|
429
|
+
}
|
|
430
|
+
for shell in ("bash", "powershell")
|
|
431
|
+
]
|
|
432
|
+
|
|
433
|
+
|
|
434
|
+
def build_plugin_hooks_json(engine_root_rel: str) -> dict[str, object]:
|
|
435
|
+
"""Build a Claude Code ``hooks.json`` mirroring the engine hook block.
|
|
436
|
+
|
|
437
|
+
Produces the plugin-alone hook configuration: a ``hooks`` map keyed by
|
|
438
|
+
event name, each event carrying matcher-grouped command blocks. Every
|
|
439
|
+
command drives the shell bootstrap stub under ``${CLAUDE_PLUGIN_ROOT}``
|
|
440
|
+
(one ``bash`` + one ``powershell`` entry per dispatch-routable hook), so
|
|
441
|
+
the conformity nudges, the session bootstrap, and the compaction / stop
|
|
442
|
+
handlers fire when the plugin is installed alone — without an apothem
|
|
443
|
+
engine install or the install-time ``${PYTHON_BIN}`` / ``${HARNESS_ROOT}``
|
|
444
|
+
substitution the engine ``settings.json`` relies on.
|
|
445
|
+
|
|
446
|
+
The event set, matchers, and per-event timeouts mirror the engine's
|
|
447
|
+
claude_code ``settings.json`` block verbatim, minus the ``gate.py --hook``
|
|
448
|
+
conformity entries (the bootstrap stub drives only the dispatcher, and the
|
|
449
|
+
gate's scope default targets a harness root, not a plugin-alone project
|
|
450
|
+
write — see ``_PLUGIN_HOOK_ENTRIES``).
|
|
451
|
+
|
|
452
|
+
Args:
|
|
453
|
+
engine_root_rel: Plugin-root-relative engine prefix —
|
|
454
|
+
``lib/apothem`` for the assembled tree (``catalog_prefix="./"``),
|
|
455
|
+
or ``src/apothem`` for the repository-root manifest
|
|
456
|
+
(``catalog_prefix="./src/apothem/"``).
|
|
457
|
+
|
|
458
|
+
Returns:
|
|
459
|
+
A ``hooks.json`` dict carrying the ``hooks`` event map.
|
|
460
|
+
"""
|
|
461
|
+
# Accumulate one command-list per (event, matcher) so consecutive entries
|
|
462
|
+
# sharing a matcher (e.g. the three Write guards) collapse into a single
|
|
463
|
+
# matcher group with an ordered command list — mirroring the engine
|
|
464
|
+
# ``settings.json`` grouping. ``dict`` preserves first-seen order, so the
|
|
465
|
+
# emitted event and matcher ordering is deterministic across runs.
|
|
466
|
+
accumulators: dict[tuple[str, str], list[dict[str, object]]] = {}
|
|
467
|
+
for event, matcher, timeout, message in _PLUGIN_HOOK_ENTRIES:
|
|
468
|
+
commands = accumulators.setdefault((event, matcher), [])
|
|
469
|
+
commands.extend(_entry_command_block(engine_root_rel, event, timeout, message))
|
|
470
|
+
events: dict[str, list[dict[str, object]]] = {}
|
|
471
|
+
for (event, matcher), commands in accumulators.items():
|
|
472
|
+
events.setdefault(event, []).append({"matcher": matcher, "hooks": commands})
|
|
473
|
+
return {"hooks": events}
|
|
474
|
+
|
|
475
|
+
|
|
476
|
+
def _plugin_schema_path() -> Path:
|
|
477
|
+
"""Return the path to the bundled ``plugin.schema.json`` fixture."""
|
|
478
|
+
return Path(__file__).resolve().parent.parent / "schemas" / "plugin.schema.json"
|
|
479
|
+
|
|
480
|
+
|
|
481
|
+
def _validate_manifest(manifest: dict[str, object]) -> None:
|
|
482
|
+
"""Validate a manifest against ``plugin.schema.json``.
|
|
483
|
+
|
|
484
|
+
Args:
|
|
485
|
+
manifest: The manifest dict to validate.
|
|
486
|
+
|
|
487
|
+
Raises:
|
|
488
|
+
jsonschema.ValidationError: When the manifest does not conform.
|
|
489
|
+
"""
|
|
490
|
+
schema = json.loads(_plugin_schema_path().read_text(encoding="utf-8"))
|
|
491
|
+
jsonschema.validate(instance=manifest, schema=schema)
|
|
492
|
+
|
|
493
|
+
|
|
494
|
+
def assemble_plugin_tree(src_root: Path, dest_root: Path) -> Path:
|
|
495
|
+
"""Materialize the canonical plugin source tree at ``dest_root``.
|
|
496
|
+
|
|
497
|
+
Copies the apothem engine verbatim into ``lib/apothem`` (AD-1 reuse),
|
|
498
|
+
writes the ``apothem_lib`` re-export shim, ensures the ``_vendor``
|
|
499
|
+
directory exists, copies the catalog directories, and writes the
|
|
500
|
+
generated ``plugin.json``. The operation is idempotent: re-running
|
|
501
|
+
overwrites the engine copy, shim, and catalog cleanly.
|
|
502
|
+
|
|
503
|
+
Args:
|
|
504
|
+
src_root: The apothem source package directory.
|
|
505
|
+
dest_root: The plugin root to populate.
|
|
506
|
+
|
|
507
|
+
Returns:
|
|
508
|
+
``dest_root``.
|
|
509
|
+
|
|
510
|
+
Raises:
|
|
511
|
+
PluginAssemblyError: When ``src_root`` is not a directory or a
|
|
512
|
+
required catalog directory is absent.
|
|
513
|
+
"""
|
|
514
|
+
if not src_root.is_dir():
|
|
515
|
+
raise PluginAssemblyError(f"source package directory missing: {src_root}")
|
|
516
|
+
|
|
517
|
+
# Build the manifest first so a malformed source fails before any write.
|
|
518
|
+
manifest = build_plugin_manifest(src_root)
|
|
519
|
+
|
|
520
|
+
dest_root.mkdir(parents=True, exist_ok=True)
|
|
521
|
+
|
|
522
|
+
# 1. Engine copy: lib/apothem (verbatim, pruned of caches).
|
|
523
|
+
lib_dir = dest_root / "lib"
|
|
524
|
+
lib_dir.mkdir(parents=True, exist_ok=True)
|
|
525
|
+
engine_dest = lib_dir / "apothem"
|
|
526
|
+
if engine_dest.exists():
|
|
527
|
+
shutil.rmtree(engine_dest)
|
|
528
|
+
shutil.copytree(
|
|
529
|
+
src_root,
|
|
530
|
+
engine_dest,
|
|
531
|
+
ignore=shutil.ignore_patterns(*_COPY_IGNORE),
|
|
532
|
+
)
|
|
533
|
+
|
|
534
|
+
# 2. Vendored deps: copy src _vendor if present, else create empty .keep.
|
|
535
|
+
vendor_dest = engine_dest / "_vendor"
|
|
536
|
+
src_vendor = src_root / "_vendor"
|
|
537
|
+
if src_vendor.is_dir():
|
|
538
|
+
if vendor_dest.exists():
|
|
539
|
+
shutil.rmtree(vendor_dest)
|
|
540
|
+
shutil.copytree(
|
|
541
|
+
src_vendor,
|
|
542
|
+
vendor_dest,
|
|
543
|
+
ignore=shutil.ignore_patterns(*_COPY_IGNORE),
|
|
544
|
+
)
|
|
545
|
+
else:
|
|
546
|
+
vendor_dest.mkdir(parents=True, exist_ok=True)
|
|
547
|
+
(vendor_dest / ".keep").write_text("", encoding="utf-8")
|
|
548
|
+
|
|
549
|
+
# 3. Alias shim: lib/apothem_lib.py.
|
|
550
|
+
(lib_dir / "apothem_lib.py").write_text(_SHIM_BODY, encoding="utf-8")
|
|
551
|
+
|
|
552
|
+
# 4. Catalog dirs: skills/agents/commands/rules copied to plugin root.
|
|
553
|
+
for name in _CATALOG_DIRS:
|
|
554
|
+
src_dir = src_root / name
|
|
555
|
+
if not src_dir.is_dir():
|
|
556
|
+
raise PluginAssemblyError(f"required catalog directory missing: {src_dir}")
|
|
557
|
+
dest_dir = dest_root / name
|
|
558
|
+
if dest_dir.exists():
|
|
559
|
+
shutil.rmtree(dest_dir)
|
|
560
|
+
shutil.copytree(
|
|
561
|
+
src_dir,
|
|
562
|
+
dest_dir,
|
|
563
|
+
ignore=shutil.ignore_patterns(*_COPY_IGNORE),
|
|
564
|
+
)
|
|
565
|
+
|
|
566
|
+
# 5. Hook message catalog: hooks/messages copied to plugin-root hooks/.
|
|
567
|
+
hooks_src = src_root / _HOOK_MESSAGES_REL
|
|
568
|
+
if not hooks_src.is_dir():
|
|
569
|
+
raise PluginAssemblyError(f"required catalog directory missing: {hooks_src}")
|
|
570
|
+
hooks_dest = dest_root / "hooks"
|
|
571
|
+
if hooks_dest.exists():
|
|
572
|
+
shutil.rmtree(hooks_dest)
|
|
573
|
+
shutil.copytree(
|
|
574
|
+
hooks_src,
|
|
575
|
+
hooks_dest,
|
|
576
|
+
ignore=shutil.ignore_patterns(*_COPY_IGNORE),
|
|
577
|
+
)
|
|
578
|
+
|
|
579
|
+
# 6. Plugin hooks.json: written beside the verbatim engine copy so the
|
|
580
|
+
# bootstrap stubs, dispatcher, and message catalog all resolve under the
|
|
581
|
+
# same ``lib/apothem`` prefix the manifest's ``hooks`` field points at.
|
|
582
|
+
# The engine copy (step 1) already materialized ``lib/apothem/hooks/``,
|
|
583
|
+
# so the directory exists.
|
|
584
|
+
hooks_json = build_plugin_hooks_json(_ASSEMBLED_ENGINE_ROOT_REL)
|
|
585
|
+
engine_hooks_dir = engine_dest / "hooks"
|
|
586
|
+
(engine_hooks_dir / "hooks.json").write_text(
|
|
587
|
+
json.dumps(hooks_json, indent=2, sort_keys=False) + "\n",
|
|
588
|
+
encoding="utf-8",
|
|
589
|
+
)
|
|
590
|
+
|
|
591
|
+
# 7. Manifest: .claude-plugin/plugin.json.
|
|
592
|
+
plugin_meta_dir = dest_root / ".claude-plugin"
|
|
593
|
+
plugin_meta_dir.mkdir(parents=True, exist_ok=True)
|
|
594
|
+
(plugin_meta_dir / "plugin.json").write_text(
|
|
595
|
+
json.dumps(manifest, indent=2, sort_keys=False) + "\n",
|
|
596
|
+
encoding="utf-8",
|
|
597
|
+
)
|
|
598
|
+
|
|
599
|
+
return dest_root
|