@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,21 @@
|
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
|
2
|
+
|
|
3
|
+
"""JSON-output helper for the apothem CLI.
|
|
4
|
+
|
|
5
|
+
Writes a single JSON document to stdout terminated by a newline, so
|
|
6
|
+
shell consumers can ``jq .`` the result directly.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import json
|
|
12
|
+
import sys
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def emit_json(data: object) -> None:
|
|
16
|
+
"""Serialize *data* to stdout as a JSON document followed by a newline.
|
|
17
|
+
|
|
18
|
+
Uses ``default=str`` so :class:`pathlib.Path` and other non-JSON-native
|
|
19
|
+
types round-trip to their string form rather than raising.
|
|
20
|
+
"""
|
|
21
|
+
sys.stdout.write(json.dumps(data, default=str) + "\n")
|
|
@@ -0,0 +1,376 @@
|
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
|
2
|
+
|
|
3
|
+
"""Shared install/update materialization orchestrators.
|
|
4
|
+
|
|
5
|
+
``_materialize`` drives a single-harness or batch install/update from the
|
|
6
|
+
shared profile (blast-radius preview, placeholder advisory, fail-fast batch
|
|
7
|
+
error handling — the first harness failure aborts the run and emits the
|
|
8
|
+
partial result accumulated so far, lifecycle envelope);
|
|
9
|
+
``_dry_run_materialization`` returns a
|
|
10
|
+
no-write plan run. Both are consumed by the install / update / quickstart
|
|
11
|
+
command modules. Patchable-helper call sites resolve through the
|
|
12
|
+
``apothem.cli`` package (``_pkg``)."""
|
|
13
|
+
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
from pathlib import Path
|
|
17
|
+
from typing import cast
|
|
18
|
+
|
|
19
|
+
import click
|
|
20
|
+
|
|
21
|
+
import apothem.cli as _pkg
|
|
22
|
+
from apothem.cli._common_flags import get_console
|
|
23
|
+
from apothem.cli._helpers import (
|
|
24
|
+
_EXIT_EXPECTED,
|
|
25
|
+
_EXIT_PARTIAL,
|
|
26
|
+
_Adapter,
|
|
27
|
+
_adapter_resolve_output_path,
|
|
28
|
+
_CliUserError,
|
|
29
|
+
_emit_expected_error,
|
|
30
|
+
_invoke_with_project,
|
|
31
|
+
_lifecycle_envelope,
|
|
32
|
+
_load_profile,
|
|
33
|
+
_materialization_error,
|
|
34
|
+
_partition_blast_radius,
|
|
35
|
+
_placeholder_advisory_entry,
|
|
36
|
+
_placeholder_identity_fields,
|
|
37
|
+
_profile_error,
|
|
38
|
+
_render_blast_radius,
|
|
39
|
+
_resolve_profile_path,
|
|
40
|
+
_resolve_project_root,
|
|
41
|
+
_result_dicts,
|
|
42
|
+
_warning_dicts,
|
|
43
|
+
)
|
|
44
|
+
from apothem.cli._json_formatter import emit_json
|
|
45
|
+
from apothem.harnesses._shared.install_driver import (
|
|
46
|
+
MaterializationError,
|
|
47
|
+
MaterializationResult,
|
|
48
|
+
MaterializationRun,
|
|
49
|
+
)
|
|
50
|
+
from apothem.lib.clean_slate import CleanSlateError, run_clean_slate
|
|
51
|
+
from apothem.lib.profile import ProfileValidationError
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def _materialize(
|
|
55
|
+
harness: str,
|
|
56
|
+
profile: str | None,
|
|
57
|
+
dry_run: bool,
|
|
58
|
+
verb_present: str,
|
|
59
|
+
verb_past: str,
|
|
60
|
+
*,
|
|
61
|
+
fmt: str = "plain",
|
|
62
|
+
quiet: bool = False,
|
|
63
|
+
no_color: bool = False,
|
|
64
|
+
project: str | None = None,
|
|
65
|
+
clean: bool = False,
|
|
66
|
+
assume_yes: bool = False,
|
|
67
|
+
verbose: bool = False,
|
|
68
|
+
payload_sink: list[dict[str, object]] | None = None,
|
|
69
|
+
) -> None:
|
|
70
|
+
"""Install or update a harness adapter from the shared profile.
|
|
71
|
+
|
|
72
|
+
The command accepts a single harness id or ``all``. Profiles are
|
|
73
|
+
validated before dry-runs and writes, and JSON output always uses the
|
|
74
|
+
lifecycle envelope required by the product contract.
|
|
75
|
+
"""
|
|
76
|
+
con = get_console(no_color=no_color, quiet=quiet)
|
|
77
|
+
profile_path = _resolve_profile_path(profile)
|
|
78
|
+
try:
|
|
79
|
+
project_root = _resolve_project_root(project)
|
|
80
|
+
shared_profile = _load_profile(profile_path)
|
|
81
|
+
selected = _pkg._selected_harness_ids(
|
|
82
|
+
harness,
|
|
83
|
+
shared_profile=shared_profile,
|
|
84
|
+
apply_excludes=harness.strip().lower() == "all",
|
|
85
|
+
)
|
|
86
|
+
_pkg._require_project_for_selection(selected, project_root)
|
|
87
|
+
adapters = [
|
|
88
|
+
(
|
|
89
|
+
harness_id,
|
|
90
|
+
_pkg._load_adapter_for_entry(_pkg.get_harness_entry(harness_id)),
|
|
91
|
+
)
|
|
92
|
+
for harness_id in selected
|
|
93
|
+
]
|
|
94
|
+
except ProfileValidationError as exc:
|
|
95
|
+
_emit_expected_error(
|
|
96
|
+
command=verb_present,
|
|
97
|
+
fmt=fmt,
|
|
98
|
+
harness=harness,
|
|
99
|
+
profile_path=profile_path,
|
|
100
|
+
error=_profile_error(exc),
|
|
101
|
+
)
|
|
102
|
+
return
|
|
103
|
+
except _CliUserError as exc:
|
|
104
|
+
_emit_expected_error(
|
|
105
|
+
command=verb_present,
|
|
106
|
+
fmt=fmt,
|
|
107
|
+
harness=harness,
|
|
108
|
+
profile_path=profile_path,
|
|
109
|
+
error=exc.to_dict(),
|
|
110
|
+
)
|
|
111
|
+
return
|
|
112
|
+
|
|
113
|
+
# Opt-in clean-slate removal runs after the profile is loaded into memory
|
|
114
|
+
# (above) and before any fresh materialization below. The profile file is
|
|
115
|
+
# one of the bounded removal targets, so its bytes are captured here and
|
|
116
|
+
# restored after removal — the shared profile is the operator's identity
|
|
117
|
+
# source of truth, not stale install state, and the timestamped backup
|
|
118
|
+
# preserves every removed target for recovery.
|
|
119
|
+
if clean:
|
|
120
|
+
profile_bytes = profile_path.read_bytes() if profile_path.is_file() else None
|
|
121
|
+
try:
|
|
122
|
+
run_clean_slate(
|
|
123
|
+
Path.home(),
|
|
124
|
+
dry_run=dry_run,
|
|
125
|
+
assume_yes=assume_yes,
|
|
126
|
+
# Use the patchable seam (not raw sys.stdin.isatty()) so the
|
|
127
|
+
# clean-slate interactivity gate is test-reachable, matching the
|
|
128
|
+
# blast-radius confirmation below.
|
|
129
|
+
interactive=_pkg._stdin_is_interactive(),
|
|
130
|
+
echo=(con.print if fmt != "json" else None),
|
|
131
|
+
)
|
|
132
|
+
except CleanSlateError as exc:
|
|
133
|
+
_emit_expected_error(
|
|
134
|
+
command=verb_present,
|
|
135
|
+
fmt=fmt,
|
|
136
|
+
harness=harness,
|
|
137
|
+
profile_path=profile_path,
|
|
138
|
+
error=_CliUserError(
|
|
139
|
+
code="clean_slate.refused",
|
|
140
|
+
message="Clean-slate removal was refused.",
|
|
141
|
+
field="clean",
|
|
142
|
+
reason=str(exc),
|
|
143
|
+
fix="Re-run interactively, pass --yes for non-interactive use, "
|
|
144
|
+
"or correct the unsafe target before retrying.",
|
|
145
|
+
).to_dict(),
|
|
146
|
+
)
|
|
147
|
+
return
|
|
148
|
+
if not dry_run and profile_bytes is not None:
|
|
149
|
+
profile_path.parent.mkdir(parents=True, exist_ok=True)
|
|
150
|
+
profile_path.write_bytes(profile_bytes)
|
|
151
|
+
if dry_run:
|
|
152
|
+
return
|
|
153
|
+
|
|
154
|
+
# Blast-radius disclosure (multi-harness install only). An ``all`` install
|
|
155
|
+
# fans out into mixed scopes: project-scope adapters write under --project,
|
|
156
|
+
# but user-scope adapters write under the operator's real home directory —
|
|
157
|
+
# a surprise worth disclosing before any byte is written. The grouped
|
|
158
|
+
# preview prints in plain mode; when the selection writes outside the
|
|
159
|
+
# supplied project root, an interactive confirmation guards the home-rooted
|
|
160
|
+
# writes. --yes, a non-interactive context, --dry-run, and --format json
|
|
161
|
+
# each bypass the prompt (dry-run and json still print/return the preview
|
|
162
|
+
# data without mutating). Single-named installs and update are out of scope.
|
|
163
|
+
if harness.strip().lower() == "all" and verb_present == "install" and fmt != "json":
|
|
164
|
+
within, outside = _partition_blast_radius(adapters, project_root)
|
|
165
|
+
_render_blast_radius(con, within, outside, project_root)
|
|
166
|
+
if not dry_run and outside and not assume_yes and _pkg._stdin_is_interactive():
|
|
167
|
+
click.confirm("Proceed and write these files?", abort=True)
|
|
168
|
+
|
|
169
|
+
all_files_written: list[str] = []
|
|
170
|
+
all_results: list[dict[str, object]] = []
|
|
171
|
+
all_warnings: list[dict[str, object]] = []
|
|
172
|
+
|
|
173
|
+
# Placeholder-identity advisory (install + update). When the loaded profile
|
|
174
|
+
# still carries the shipped scaffold identity, surface a single advisory so a
|
|
175
|
+
# fake identity is not silently projected — advisory only, never blocking
|
|
176
|
+
# (identity is never fabricated or coerced). It rides the JSON warnings array
|
|
177
|
+
# and prints once in plain mode, regardless of the harness selection.
|
|
178
|
+
placeholder_fields = _placeholder_identity_fields(shared_profile)
|
|
179
|
+
if placeholder_fields:
|
|
180
|
+
advisory = _placeholder_advisory_entry(profile_path, placeholder_fields)
|
|
181
|
+
all_warnings.append(advisory)
|
|
182
|
+
if fmt != "json":
|
|
183
|
+
con.print(f"[yellow]Note:[/] {advisory['message']}")
|
|
184
|
+
last_output_path: Path | None = None
|
|
185
|
+
last_materialization: MaterializationRun | None = None
|
|
186
|
+
|
|
187
|
+
for harness_id, adapter in adapters:
|
|
188
|
+
output_path = _adapter_resolve_output_path(adapter, project_root)
|
|
189
|
+
last_output_path = output_path
|
|
190
|
+
materialization: MaterializationRun | None
|
|
191
|
+
|
|
192
|
+
try:
|
|
193
|
+
if dry_run:
|
|
194
|
+
materialization = _dry_run_materialization(
|
|
195
|
+
harness_id,
|
|
196
|
+
adapter,
|
|
197
|
+
output_path,
|
|
198
|
+
project_root,
|
|
199
|
+
)
|
|
200
|
+
elif verb_present == "update":
|
|
201
|
+
maybe_run = _invoke_with_project(
|
|
202
|
+
adapter.update,
|
|
203
|
+
shared_profile,
|
|
204
|
+
project=project_root,
|
|
205
|
+
)
|
|
206
|
+
materialization = (
|
|
207
|
+
maybe_run if isinstance(maybe_run, MaterializationRun) else None
|
|
208
|
+
)
|
|
209
|
+
else:
|
|
210
|
+
maybe_run = _invoke_with_project(
|
|
211
|
+
adapter.install,
|
|
212
|
+
shared_profile,
|
|
213
|
+
project=project_root,
|
|
214
|
+
)
|
|
215
|
+
materialization = (
|
|
216
|
+
maybe_run if isinstance(maybe_run, MaterializationRun) else None
|
|
217
|
+
)
|
|
218
|
+
except MaterializationError as exc:
|
|
219
|
+
error = _materialization_error(exc, files_written=exc.run.files_written)
|
|
220
|
+
results = [*all_results, *_result_dicts(harness_id, exc.run)]
|
|
221
|
+
warnings = [*all_warnings, *_warning_dicts(harness_id, exc.run)]
|
|
222
|
+
files_written = [*all_files_written, *exc.run.files_written]
|
|
223
|
+
exit_code = _EXIT_PARTIAL if files_written else _EXIT_EXPECTED
|
|
224
|
+
_emit_expected_error(
|
|
225
|
+
command=verb_present,
|
|
226
|
+
fmt=fmt,
|
|
227
|
+
harness=harness,
|
|
228
|
+
profile_path=profile_path,
|
|
229
|
+
project_root=project_root,
|
|
230
|
+
error=error,
|
|
231
|
+
files_written=files_written,
|
|
232
|
+
results=results,
|
|
233
|
+
warnings=warnings,
|
|
234
|
+
exit_code=exit_code,
|
|
235
|
+
)
|
|
236
|
+
return
|
|
237
|
+
except NotImplementedError:
|
|
238
|
+
error = _CliUserError(
|
|
239
|
+
code="harness.not_implemented",
|
|
240
|
+
message="Apothem adapter is not implemented.",
|
|
241
|
+
field="harness",
|
|
242
|
+
reason=f"{harness_id} does not implement {verb_present}.",
|
|
243
|
+
fix="Choose another harness or update the adapter package.",
|
|
244
|
+
).to_dict()
|
|
245
|
+
_emit_expected_error(
|
|
246
|
+
command=verb_present,
|
|
247
|
+
fmt=fmt,
|
|
248
|
+
harness=harness,
|
|
249
|
+
profile_path=profile_path,
|
|
250
|
+
project_root=project_root,
|
|
251
|
+
error=error,
|
|
252
|
+
files_written=all_files_written,
|
|
253
|
+
results=all_results,
|
|
254
|
+
warnings=all_warnings,
|
|
255
|
+
exit_code=_EXIT_PARTIAL if all_files_written else _EXIT_EXPECTED,
|
|
256
|
+
)
|
|
257
|
+
return
|
|
258
|
+
except _CliUserError as exc:
|
|
259
|
+
_emit_expected_error(
|
|
260
|
+
command=verb_present,
|
|
261
|
+
fmt=fmt,
|
|
262
|
+
harness=harness,
|
|
263
|
+
profile_path=profile_path,
|
|
264
|
+
project_root=project_root,
|
|
265
|
+
error=exc.to_dict(),
|
|
266
|
+
files_written=all_files_written,
|
|
267
|
+
results=all_results,
|
|
268
|
+
warnings=all_warnings,
|
|
269
|
+
exit_code=_EXIT_PARTIAL if all_files_written else _EXIT_EXPECTED,
|
|
270
|
+
)
|
|
271
|
+
return
|
|
272
|
+
|
|
273
|
+
last_materialization = materialization
|
|
274
|
+
if materialization is not None:
|
|
275
|
+
all_files_written.extend(materialization.files_written)
|
|
276
|
+
all_results.extend(_result_dicts(harness_id, materialization))
|
|
277
|
+
all_warnings.extend(_warning_dicts(harness_id, materialization))
|
|
278
|
+
|
|
279
|
+
if fmt != "json":
|
|
280
|
+
if materialization is not None and materialization.warnings:
|
|
281
|
+
if verbose:
|
|
282
|
+
for warning in materialization.warnings:
|
|
283
|
+
con.print(
|
|
284
|
+
f"[yellow]Warning:[/] {harness_id} - {warning.message}"
|
|
285
|
+
)
|
|
286
|
+
else:
|
|
287
|
+
count = len(materialization.warnings)
|
|
288
|
+
noun = "capability" if count == 1 else "capabilities"
|
|
289
|
+
con.print(
|
|
290
|
+
f"[dim]Note:[/] {harness_id} - {count} {noun} not "
|
|
291
|
+
"projected; pass --verbose for detail"
|
|
292
|
+
)
|
|
293
|
+
if dry_run:
|
|
294
|
+
con.print(
|
|
295
|
+
f"[bold yellow]DRY RUN[/] would {verb_present} "
|
|
296
|
+
f"[cyan]{harness_id}[/] -> {output_path}"
|
|
297
|
+
)
|
|
298
|
+
else:
|
|
299
|
+
con.print(
|
|
300
|
+
f"[green]✓[/] {verb_past} [cyan]{harness_id}[/] -> {output_path}"
|
|
301
|
+
)
|
|
302
|
+
|
|
303
|
+
if fmt == "json":
|
|
304
|
+
status = "dry_run" if dry_run else "success"
|
|
305
|
+
action = "dry_run" if dry_run else verb_past.lower()
|
|
306
|
+
payload = _lifecycle_envelope(
|
|
307
|
+
status=status,
|
|
308
|
+
command=verb_present,
|
|
309
|
+
action=action,
|
|
310
|
+
harness=harness,
|
|
311
|
+
profile_path=profile_path,
|
|
312
|
+
project_root=project_root,
|
|
313
|
+
files_written=all_files_written,
|
|
314
|
+
results=all_results,
|
|
315
|
+
warnings=all_warnings,
|
|
316
|
+
output_path=last_output_path if len(adapters) == 1 else None,
|
|
317
|
+
materialization=last_materialization if len(adapters) == 1 else None,
|
|
318
|
+
)
|
|
319
|
+
# When a caller supplies a sink (the guided ``quickstart`` flow), hand it
|
|
320
|
+
# the success envelope instead of emitting, so the caller can fold it into
|
|
321
|
+
# one combined JSON summary rather than printing two documents.
|
|
322
|
+
if payload_sink is not None:
|
|
323
|
+
payload_sink.append(payload)
|
|
324
|
+
else:
|
|
325
|
+
emit_json(payload)
|
|
326
|
+
elif len(adapters) > 1 and not quiet:
|
|
327
|
+
con.print(
|
|
328
|
+
f"[green]✓[/] {verb_past} {len(adapters)} harnesses "
|
|
329
|
+
f"({len(all_files_written)} files written)."
|
|
330
|
+
)
|
|
331
|
+
|
|
332
|
+
if clean and not dry_run and fmt != "json" and not quiet:
|
|
333
|
+
verify_harness = harness if harness.strip().lower() != "all" else "all"
|
|
334
|
+
con.print(
|
|
335
|
+
"[bold]Next step:[/] confirm the fresh install with "
|
|
336
|
+
f"[cyan]apothem verify --harness {verify_harness}[/]"
|
|
337
|
+
)
|
|
338
|
+
|
|
339
|
+
|
|
340
|
+
def _dry_run_materialization(
|
|
341
|
+
harness_id: str,
|
|
342
|
+
adapter: _Adapter,
|
|
343
|
+
output_path: Path,
|
|
344
|
+
project_root: Path | None,
|
|
345
|
+
) -> MaterializationRun:
|
|
346
|
+
"""Return a no-write materialization run using the adapter plan surface."""
|
|
347
|
+
plan_entries: list[dict[str, str]] = []
|
|
348
|
+
plan_fn = getattr(adapter, "plan", None)
|
|
349
|
+
if callable(plan_fn):
|
|
350
|
+
try:
|
|
351
|
+
candidate = _invoke_with_project(plan_fn, output_path, project=project_root)
|
|
352
|
+
except Exception as exc:
|
|
353
|
+
raise _CliUserError(
|
|
354
|
+
code="materialization.plan_failed",
|
|
355
|
+
message="Apothem dry-run planning failed.",
|
|
356
|
+
field="harness",
|
|
357
|
+
reason=str(exc),
|
|
358
|
+
fix="Check the harness adapter and project path before retrying.",
|
|
359
|
+
) from exc
|
|
360
|
+
if isinstance(candidate, list):
|
|
361
|
+
plan_entries = cast("list[dict[str, str]]", candidate)
|
|
362
|
+
results = tuple(
|
|
363
|
+
MaterializationResult(
|
|
364
|
+
outcome="skipped",
|
|
365
|
+
operation=entry.get("mode", "plan"),
|
|
366
|
+
path=entry.get("target", str(output_path)),
|
|
367
|
+
source=entry.get("source"),
|
|
368
|
+
message="dry run: no filesystem changes made",
|
|
369
|
+
)
|
|
370
|
+
for entry in plan_entries
|
|
371
|
+
)
|
|
372
|
+
return MaterializationRun(
|
|
373
|
+
harness=_pkg.get_harness_entry(harness_id).package_key,
|
|
374
|
+
dry_run=True,
|
|
375
|
+
results=results,
|
|
376
|
+
)
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
|
2
|
+
|
|
3
|
+
_apothem_completion() {
|
|
4
|
+
local IFS=$'\n'
|
|
5
|
+
local response
|
|
6
|
+
|
|
7
|
+
response=$(env COMP_WORDS="${COMP_WORDS[*]}" COMP_CWORD=$COMP_CWORD _APOTHEM_COMPLETE=bash_complete $1)
|
|
8
|
+
|
|
9
|
+
for completion in $response; do
|
|
10
|
+
IFS=',' read type value <<< "$completion"
|
|
11
|
+
|
|
12
|
+
if [[ $type == 'dir' ]]; then
|
|
13
|
+
COMPREPLY=()
|
|
14
|
+
compopt -o dirnames
|
|
15
|
+
elif [[ $type == 'file' ]]; then
|
|
16
|
+
COMPREPLY=()
|
|
17
|
+
compopt -o default
|
|
18
|
+
elif [[ $type == 'plain' ]]; then
|
|
19
|
+
COMPREPLY+=($value)
|
|
20
|
+
fi
|
|
21
|
+
done
|
|
22
|
+
|
|
23
|
+
return 0
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
_apothem_completion_setup() {
|
|
27
|
+
complete -o nosort -F _apothem_completion apothem
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
_apothem_completion_setup;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
|
2
|
+
|
|
3
|
+
function _apothem_completion;
|
|
4
|
+
set -l response (env _APOTHEM_COMPLETE=fish_complete COMP_WORDS=(commandline -cp) COMP_CWORD=(commandline -t) apothem);
|
|
5
|
+
|
|
6
|
+
for completion in $response;
|
|
7
|
+
set -l metadata (string split "," $completion);
|
|
8
|
+
|
|
9
|
+
if test $metadata[1] = "dir";
|
|
10
|
+
__fish_complete_directories $metadata[2];
|
|
11
|
+
else if test $metadata[1] = "file";
|
|
12
|
+
__fish_complete_path $metadata[2];
|
|
13
|
+
else if test $metadata[1] = "plain";
|
|
14
|
+
echo $metadata[2];
|
|
15
|
+
end;
|
|
16
|
+
end;
|
|
17
|
+
end;
|
|
18
|
+
|
|
19
|
+
complete --no-files --command apothem --arguments "(_apothem_completion)";
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
|
2
|
+
|
|
3
|
+
# SPDX-License-Identifier: MIT
|
|
4
|
+
# Apothem PowerShell completion stub.
|
|
5
|
+
#
|
|
6
|
+
# Click 8 does not ship a native PowerShell completer. Source this script
|
|
7
|
+
# from your $PROFILE to register dynamic completion for `apothem`. It
|
|
8
|
+
# shells out to the apothem binary with _APOTHEM_COMPLETE=powershell_complete
|
|
9
|
+
# on each TAB, asking Click to compute the candidate list. If your apothem
|
|
10
|
+
# entry-point is not on PATH, adjust the call path below.
|
|
11
|
+
Register-ArgumentCompleter -Native -CommandName apothem -ScriptBlock {
|
|
12
|
+
param($wordToComplete, $commandAst, $cursorPosition)
|
|
13
|
+
$env:_APOTHEM_COMPLETE = "powershell_complete"
|
|
14
|
+
$env:COMP_WORDS = $commandAst.ToString()
|
|
15
|
+
$env:COMP_CWORD = $commandAst.CommandElements.Count - 1
|
|
16
|
+
$result = apothem 2>$null
|
|
17
|
+
Remove-Item Env:_APOTHEM_COMPLETE -ErrorAction SilentlyContinue
|
|
18
|
+
Remove-Item Env:COMP_WORDS -ErrorAction SilentlyContinue
|
|
19
|
+
Remove-Item Env:COMP_CWORD -ErrorAction SilentlyContinue
|
|
20
|
+
foreach ($line in $result -split "`n") {
|
|
21
|
+
if ($line) {
|
|
22
|
+
[System.Management.Automation.CompletionResult]::new(
|
|
23
|
+
$line, $line, "ParameterValue", $line
|
|
24
|
+
)
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
|
2
|
+
|
|
3
|
+
#compdef apothem
|
|
4
|
+
|
|
5
|
+
_apothem_completion() {
|
|
6
|
+
local -a completions
|
|
7
|
+
local -a completions_with_descriptions
|
|
8
|
+
local -a response
|
|
9
|
+
(( ! $+commands[apothem] )) && return 1
|
|
10
|
+
|
|
11
|
+
response=("${(@f)$(env COMP_WORDS="${words[*]}" COMP_CWORD=$((CURRENT-1)) _APOTHEM_COMPLETE=zsh_complete apothem)}")
|
|
12
|
+
|
|
13
|
+
for type key descr in ${response}; do
|
|
14
|
+
if [[ "$type" == "plain" ]]; then
|
|
15
|
+
if [[ "$descr" == "_" ]]; then
|
|
16
|
+
completions+=("$key")
|
|
17
|
+
else
|
|
18
|
+
completions_with_descriptions+=("$key":"$descr")
|
|
19
|
+
fi
|
|
20
|
+
elif [[ "$type" == "dir" ]]; then
|
|
21
|
+
_path_files -/
|
|
22
|
+
elif [[ "$type" == "file" ]]; then
|
|
23
|
+
_path_files -f
|
|
24
|
+
fi
|
|
25
|
+
done
|
|
26
|
+
|
|
27
|
+
if [ -n "$completions_with_descriptions" ]; then
|
|
28
|
+
_describe -V unsorted completions_with_descriptions -U
|
|
29
|
+
fi
|
|
30
|
+
|
|
31
|
+
if [ -n "$completions" ]; then
|
|
32
|
+
compadd -U -V unsorted -a completions
|
|
33
|
+
fi
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if [[ $zsh_eval_context[-1] == loadautofunc ]]; then
|
|
37
|
+
# autoload from fpath, call function directly
|
|
38
|
+
_apothem_completion "$@"
|
|
39
|
+
else
|
|
40
|
+
# eval/source/. command, register function for later
|
|
41
|
+
compdef _apothem_completion apothem
|
|
42
|
+
fi
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
|
2
|
+
|
|
3
|
+
"""Deterministic JSON exporter for source-generated documentation reference.
|
|
4
|
+
|
|
5
|
+
Runnable as ``python -m apothem.cli.reference_export <kind>`` where ``<kind>``
|
|
6
|
+
is one of ``cli`` or ``harnesses``. The emitter introspects Apothem's own Click
|
|
7
|
+
command tree and harness registry — the single source of truth — so the
|
|
8
|
+
generated reference pages cannot drift from the implementation.
|
|
9
|
+
|
|
10
|
+
Output is a single JSON document with stable key order and sorted collections,
|
|
11
|
+
so repeated runs against an unchanged tree are byte-identical.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
import json
|
|
17
|
+
import sys
|
|
18
|
+
|
|
19
|
+
import click
|
|
20
|
+
|
|
21
|
+
from apothem.cli import main as cli_main
|
|
22
|
+
from apothem.lib.harness_registry import iter_harness_entries
|
|
23
|
+
|
|
24
|
+
# Continuous-form CLI aliases (``Installing`` / ``installing`` and the
|
|
25
|
+
# capitalized / lowercase ``Updating`` / ``Uninstalling`` variants) duplicate
|
|
26
|
+
# the canonical lowercase verbs. They are excluded from the reference so each
|
|
27
|
+
# command appears exactly once under its canonical name.
|
|
28
|
+
_ALIAS_COMMAND_NAMES: frozenset[str] = frozenset(
|
|
29
|
+
{
|
|
30
|
+
"Installing",
|
|
31
|
+
"installing",
|
|
32
|
+
"Updating",
|
|
33
|
+
"updating",
|
|
34
|
+
"Uninstalling",
|
|
35
|
+
"uninstalling",
|
|
36
|
+
}
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def _option_signatures(command: click.Command) -> list[str]:
|
|
41
|
+
"""Return the sorted flag signatures declared by *command*.
|
|
42
|
+
|
|
43
|
+
Each entry is the comma-joined option declaration string (for example
|
|
44
|
+
``--harness`` or ``-h, --help``). The implicit ``--help`` option Click adds
|
|
45
|
+
is included so the reference matches the observable CLI surface.
|
|
46
|
+
"""
|
|
47
|
+
signatures: list[str] = []
|
|
48
|
+
for param in command.params:
|
|
49
|
+
if not isinstance(param, click.Option):
|
|
50
|
+
continue
|
|
51
|
+
signatures.append(", ".join(param.opts))
|
|
52
|
+
return sorted(signatures)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def _command_entry(name: str, command: click.Command) -> dict[str, object]:
|
|
56
|
+
"""Build the deterministic export record for one leaf command."""
|
|
57
|
+
return {
|
|
58
|
+
"name": name,
|
|
59
|
+
"flags": _option_signatures(command),
|
|
60
|
+
"description": (command.help or command.short_help or "").strip(),
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def _walk_commands(prefix: str, group: click.Group) -> list[dict[str, object]]:
|
|
65
|
+
"""Recursively collect leaf-command records under *group*.
|
|
66
|
+
|
|
67
|
+
Sub-groups (``profile``, ``harnesses``) recurse so their subcommands are
|
|
68
|
+
emitted as ``profile show``, ``harnesses list``, and so on. Alias commands
|
|
69
|
+
are skipped at the top level.
|
|
70
|
+
"""
|
|
71
|
+
entries: list[dict[str, object]] = []
|
|
72
|
+
for child_name in group.commands:
|
|
73
|
+
if not prefix and child_name in _ALIAS_COMMAND_NAMES:
|
|
74
|
+
continue
|
|
75
|
+
child = group.commands[child_name]
|
|
76
|
+
full_name = f"{prefix} {child_name}".strip()
|
|
77
|
+
if isinstance(child, click.Group):
|
|
78
|
+
entries.extend(_walk_commands(full_name, child))
|
|
79
|
+
else:
|
|
80
|
+
entries.append(_command_entry(full_name, child))
|
|
81
|
+
return entries
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def export_cli() -> dict[str, object]:
|
|
85
|
+
"""Return the deterministic CLI command-and-flag export."""
|
|
86
|
+
commands = _walk_commands("", cli_main)
|
|
87
|
+
commands.sort(key=lambda entry: str(entry["name"]))
|
|
88
|
+
return {"kind": "cli", "commands": commands}
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def export_harnesses() -> dict[str, object]:
|
|
92
|
+
"""Return the deterministic harness-adapter registry export."""
|
|
93
|
+
harnesses = [
|
|
94
|
+
{
|
|
95
|
+
"id": entry.public_id,
|
|
96
|
+
"display_name": entry.display_name,
|
|
97
|
+
"scope": entry.scope,
|
|
98
|
+
"output_format": entry.output_format,
|
|
99
|
+
"target_paths": sorted(entry.target_paths),
|
|
100
|
+
}
|
|
101
|
+
for entry in iter_harness_entries()
|
|
102
|
+
]
|
|
103
|
+
harnesses.sort(key=lambda entry: str(entry["id"]))
|
|
104
|
+
return {"kind": "harnesses", "harnesses": harnesses}
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
_EXPORTERS = {
|
|
108
|
+
"cli": export_cli,
|
|
109
|
+
"harnesses": export_harnesses,
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def main(argv: list[str] | None = None) -> int:
|
|
114
|
+
"""Emit the requested export kind as deterministic JSON on stdout."""
|
|
115
|
+
args = sys.argv[1:] if argv is None else argv
|
|
116
|
+
if len(args) != 1 or args[0] not in _EXPORTERS:
|
|
117
|
+
kinds = ", ".join(sorted(_EXPORTERS))
|
|
118
|
+
sys.stderr.write(f"usage: python -m apothem.cli.reference_export <{kinds}>\n")
|
|
119
|
+
return 2
|
|
120
|
+
payload = _EXPORTERS[args[0]]()
|
|
121
|
+
sys.stdout.write(json.dumps(payload, indent=2, sort_keys=True) + "\n")
|
|
122
|
+
return 0
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
if __name__ == "__main__":
|
|
126
|
+
raise SystemExit(main())
|