claude-dev-env 2.9.0 → 2.10.0
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/CLAUDE.md +1 -1
- package/_shared/pr-loop/CLAUDE.md +1 -1
- package/_shared/pr-loop/audit-contract.md +17 -6
- package/_shared/pr-loop/audit-reply-template.md +4 -4
- package/_shared/pr-loop/code-rules-gate.md +3 -5
- package/_shared/pr-loop/fix-protocol.md +2 -3
- package/_shared/pr-loop/gh-payloads.md +1 -1
- package/_shared/pr-loop/scripts/CLAUDE.md +1 -1
- package/_shared/pr-loop/scripts/README.md +1 -1
- package/_shared/pr-loop/scripts/code_rules_gate.py +2 -0
- package/_shared/pr-loop/scripts/code_rules_gate_parts/gate_running.py +16 -1
- package/_shared/pr-loop/scripts/code_rules_gate_parts/git_blob_readers.py +11 -5
- package/_shared/pr-loop/scripts/preflight.py +9 -4
- package/_shared/pr-loop/scripts/reviews_disabled.py +50 -22
- package/_shared/pr-loop/scripts/tests/conftest.py +20 -0
- package/_shared/pr-loop/scripts/tests/test_claude_permissions_common.py +6 -6
- package/_shared/pr-loop/scripts/tests/test_reviews_disabled.py +50 -6
- package/_shared/pr-loop/scripts/tests/test_revoke_project_claude_permissions.py +1 -1
- package/_shared/pr-loop/state-schema.md +5 -14
- package/agents/CLAUDE.md +2 -2
- package/agents/clean-coder.md +58 -548
- package/agents/code-quality-agent.md +10 -2
- package/agents/code-verifier.md +1 -1
- package/agents/test_agent_frontmatter.py +32 -40
- package/audit-rubrics/CLAUDE.md +2 -1
- package/audit-rubrics/audit-categories.json +704 -0
- package/audit-rubrics/prompts/category-i-concurrency.md +1 -1
- package/bin/CLAUDE.md +16 -5
- package/bin/ever-shipped-skills.mjs +2 -0
- package/bin/install-plan.mjs +402 -0
- package/bin/install-transaction.mjs +455 -0
- package/bin/install.mjs +593 -147
- package/bin/install.plan.test.mjs +194 -0
- package/bin/install.profile-root.test.mjs +154 -0
- package/bin/install.profiles.test.mjs +253 -0
- package/bin/install.settings-defaults.test.mjs +200 -0
- package/bin/install.transaction.test.mjs +400 -0
- package/bin/install.uninstall-transaction.test.mjs +418 -0
- package/bin/merge_managed_permissions.mjs +130 -0
- package/bin/resolve-install-root.mjs +181 -0
- package/bin/select-install-targets.mjs +401 -0
- package/commands/CLAUDE.md +0 -2
- package/docs/references/CLAUDE.md +1 -0
- package/docs/references/advisor-tool.md +1 -1
- package/docs/references/prose-style-enforcement.md +25 -0
- package/hooks/blocking/CLAUDE.md +6 -6
- package/hooks/blocking/_path_setup.py +9 -5
- package/hooks/blocking/code_rules_docstrings.py +124 -30
- package/hooks/blocking/code_rules_enforcer.py +161 -16
- package/hooks/blocking/code_rules_shared.py +40 -23
- package/hooks/blocking/config/CLAUDE.md +3 -5
- package/hooks/blocking/config/prose_style_enforcement_constants.py +38 -0
- package/hooks/blocking/config/test_prose_style_enforcement_constants.py +45 -0
- package/hooks/blocking/eli11_reply_enforcer.py +70 -113
- package/hooks/blocking/hedging_language_blocker.py +103 -20
- package/hooks/blocking/hook_prose_detector_consistency.py +6 -0
- package/hooks/blocking/intent_only_ending_blocker.py +6 -0
- package/hooks/blocking/plain_language_blocker.py +139 -20
- package/hooks/blocking/pre_tool_use_dispatcher.py +102 -20
- package/hooks/blocking/state_description_blocker.py +7 -1
- package/hooks/blocking/tdd_enforcer.py +8 -0
- package/hooks/blocking/test__path_setup.py +28 -0
- package/hooks/blocking/test_code_rules_enforcer_agent_home_tooling.py +99 -0
- package/hooks/blocking/test_code_rules_enforcer_docstring_args_span_scope.py +232 -10
- package/hooks/blocking/test_code_rules_enforcer_ephemeral.py +1 -1
- package/hooks/blocking/test_code_rules_enforcer_join_separator_magic.py +41 -0
- package/hooks/blocking/test_code_rules_enforcer_string_magic.py +98 -0
- package/hooks/blocking/test_eli11_reply_enforcer.py +98 -165
- package/hooks/blocking/test_hedging_language_blocker.py +120 -1
- package/hooks/blocking/test_hook_prose_detector_consistency.py +28 -8
- package/hooks/blocking/test_intent_only_ending_blocker.py +27 -2
- package/hooks/blocking/test_package_inventory_stale_blocker.py +11 -4
- package/hooks/blocking/test_plain_language_blocker.py +129 -19
- package/hooks/blocking/test_plain_language_blocker_allowlist.py +70 -26
- package/hooks/blocking/test_pre_tool_use_dispatcher.py +99 -26
- package/hooks/blocking/test_pre_tool_use_dispatcher_native.py +87 -50
- package/hooks/blocking/test_state_description_blocker.py +45 -2
- package/hooks/blocking/test_stop_dispatcher.py +11 -7
- package/hooks/blocking/test_volatile_path_in_post_blocker.py +12 -12
- package/hooks/blocking/volatile_path_in_post_blocker.py +2 -2
- package/hooks/hooks.json +15 -0
- package/hooks/hooks_constants/CLAUDE.md +14 -3
- package/hooks/hooks_constants/ask_user_question_shape.py +281 -0
- package/hooks/hooks_constants/code_rules_enforcer_constants.py +2 -1
- package/hooks/hooks_constants/eli11_reply_enforcer_constants.py +5 -12
- package/hooks/hooks_constants/hedging_uncertainty_constants.py +42 -0
- package/hooks/hooks_constants/issue_tracker_session_starter_constants.py +23 -0
- package/hooks/hooks_constants/orchestrator_auto_starter_constants.py +23 -0
- package/hooks/hooks_constants/piped_pytest_blocker_constants.py +4 -1
- package/hooks/hooks_constants/plain_language_blocker_constants.py +4 -1
- package/hooks/hooks_constants/pre_tool_use_dispatcher_constants.py +6 -0
- package/hooks/hooks_constants/project_paths_reader.py +31 -4
- package/hooks/hooks_constants/prose_matcher_precision_constants.py +40 -0
- package/hooks/hooks_constants/pytest_invocation.py +354 -0
- package/hooks/hooks_constants/session_start_injector.py +163 -0
- package/hooks/hooks_constants/session_start_injector_constants.py +46 -0
- package/hooks/hooks_constants/shell_command_pipeline.py +397 -0
- package/hooks/hooks_constants/shell_command_segments.py +5 -0
- package/hooks/hooks_constants/test_ask_user_question_shape.py +167 -0
- package/hooks/hooks_constants/test_project_paths_reader.py +29 -0
- package/hooks/hooks_constants/test_prose_metrics_parity.py +8 -0
- package/hooks/hooks_constants/test_pytest_invocation.py +130 -0
- package/hooks/hooks_constants/test_session_start_injector.py +168 -0
- package/hooks/hooks_constants/test_shell_command_pipeline.py +135 -0
- package/hooks/hooks_constants/volatile_path_in_post_blocker_constants.py +1 -1
- package/hooks/hooks_constants/working_style_prompt_constants.py +30 -0
- package/hooks/observability/CLAUDE.md +2 -0
- package/hooks/observability/prose_matcher_advisory.py +237 -0
- package/hooks/observability/test_prose_matcher_advisory.py +143 -0
- package/hooks/session/CLAUDE.md +9 -1
- package/hooks/session/_path_setup.py +13 -0
- package/hooks/session/issue_tracker_session_starter.py +135 -0
- package/hooks/session/orchestrator_auto_starter.py +100 -0
- package/hooks/session/test__path_setup.py +28 -0
- package/hooks/session/test_issue_tracker_session_starter.py +104 -0
- package/hooks/session/test_orchestrator_auto_starter.py +99 -0
- package/hooks/session/test_working_style_prompt.py +47 -0
- package/hooks/session/untracked_repo_detector.py +1 -24
- package/hooks/session/working_style_prompt.py +36 -0
- package/hooks/validators/_path_setup.py +19 -0
- package/hooks/validators/run_all_validators.py +8 -13
- package/installable-surfaces.manifest.json +21 -0
- package/output-styles/CLAUDE.md +1 -3
- package/package.json +4 -2
- package/rules/CLAUDE.md +1 -0
- package/rules/durable-post-artifacts.md +2 -2
- package/rules/eli11-replies.md +6 -1
- package/rules/hedging-claims.md +4 -2
- package/rules/long-horizon-autonomy.md +3 -1
- package/rules/opus5-communication-contract.md +45 -0
- package/rules/plain-language.md +2 -2
- package/rules/research-mode.md +1 -1
- package/scripts/CLAUDE.md +11 -0
- package/scripts/Sync-RepoMain.ps1 +215 -0
- package/scripts/active_capability_references.py +218 -0
- package/scripts/ci/windows-installer-lifecycle.ps1 +78 -0
- package/scripts/claude_chain_runner.py +394 -6
- package/scripts/claude_chain_usage.py +1 -1
- package/scripts/codex_compat_materializer.py +105 -85
- package/scripts/dev_env_scripts_constants/CLAUDE.md +2 -0
- package/scripts/dev_env_scripts_constants/active_capability_constants.py +46 -0
- package/scripts/dev_env_scripts_constants/claude_chain_constants.py +74 -0
- package/scripts/dev_env_scripts_constants/verify_installable_package_constants.py +116 -0
- package/scripts/profile-isolation-launchers/config/mcp-bundles.json +25 -0
- package/scripts/profile-isolation-launchers/config/profile-isolation-constants.mjs +60 -0
- package/scripts/profile-isolation-launchers/config/profiles.manifest.json +54 -0
- package/scripts/profile-isolation-launchers/config/shared-allowlist.json +64 -0
- package/scripts/profile-isolation-launchers/launcher-runtime.mjs +180 -0
- package/scripts/profile-isolation-launchers/lib/profile-manifest.mjs +288 -0
- package/scripts/profile-isolation-launchers/mcp-bundles.mjs +275 -0
- package/scripts/profile-isolation-launchers/profile-isolation-contract.test.mjs +221 -0
- package/scripts/profile-isolation-launchers/tests/launcher-runtime.test.mjs +108 -0
- package/scripts/profile-isolation-launchers/tests/mcp-bundles.test.mjs +147 -0
- package/scripts/profile-isolation-launchers/tests/shortcut-contract.test.ps1 +102 -0
- package/scripts/profile-isolation-launchers/tests/version-compatibility.test.mjs +210 -0
- package/scripts/profile-isolation-launchers/version-compatibility.mjs +299 -0
- package/scripts/profile-isolation-launchers/windows/shortcut-inventory.ps1 +127 -0
- package/scripts/profile-isolation-launchers/windows/shortcut-manifest.json +51 -0
- package/scripts/profile-isolation-launchers/windows/shortcut-reconcile.ps1 +77 -0
- package/scripts/spawn_grok_batch.py +3 -0
- package/scripts/test_active_capability_references.py +108 -0
- package/scripts/test_claude_chain_runner.py +414 -82
- package/scripts/test_claude_chain_usage.py +12 -12
- package/scripts/test_resolve_worker_spawn.py +2 -2
- package/scripts/test_verify_installable_package.py +208 -0
- package/scripts/tests/test_codex_compat_materializer.py +33 -0
- package/scripts/verify_installable_package.py +612 -0
- package/settings.json +10 -0
- package/skills/CLAUDE.md +2 -0
- package/skills/_shared/pr-loop/scripts/CLAUDE.md +1 -0
- package/skills/_shared/pr-loop/scripts/audit_category_schema.py +355 -0
- package/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/CLAUDE.md +1 -0
- package/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/audit_category_schema_constants.py +32 -0
- package/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/path_resolver_constants.py +7 -19
- package/skills/_shared/pr-loop/scripts/test_audit_category_schema.py +94 -0
- package/skills/_shared/pr-loop/scripts/test_build_audit_prompt.py +21 -0
- package/skills/autoconverge/reference/convergence.md +2 -1
- package/skills/autoconverge/reference/stop-conditions.md +5 -3
- package/skills/beat-sheet/SKILL.md +54 -0
- package/skills/beat-sheet/reference/visual-beats.md +29 -0
- package/skills/bugteam/CONSTRAINTS.md +4 -4
- package/skills/bugteam/EXAMPLES.md +1 -1
- package/skills/bugteam/reference/README.md +1 -1
- package/skills/e-code-review/SKILL.md +26 -5
- package/skills/e-code-review/reference/effort-evaluation.md +35 -0
- package/skills/e-code-review/reference/medium.md +15 -4
- package/skills/e-code-review/scripts/config/e_code_review_effort_constants/__init__.py +41 -0
- package/skills/e-code-review/scripts/config/e_code_review_effort_constants/effort_constants.py +40 -0
- package/skills/e-code-review/scripts/e_code_review_scripts_constants/finding_pipeline_constants.py +49 -0
- package/skills/e-code-review/scripts/effort_defaults_evidence.json +186 -0
- package/skills/e-code-review/scripts/effort_evaluation.py +362 -0
- package/skills/e-code-review/scripts/finding_pipeline.py +140 -0
- package/skills/e-code-review/scripts/fixtures/demanding.json +26 -0
- package/skills/e-code-review/scripts/fixtures/easy.json +14 -0
- package/skills/e-code-review/scripts/fixtures/medium.json +20 -0
- package/skills/e-code-review/scripts/grok_code_review.py +16 -7
- package/skills/e-code-review/scripts/test_effort_evaluation.py +180 -0
- package/skills/e-code-review/scripts/test_finding_pipeline.py +197 -0
- package/skills/e-code-review/scripts/test_grok_code_review.py +77 -0
- package/skills/plan-to-pr/scripts/create_packet.py +4 -4
- package/skills/plan-to-pr/scripts/load_skill_constants.py +41 -0
- package/skills/plan-to-pr/scripts/validate_packet.py +4 -4
- package/skills/plan-to-pr/scripts/validate_protocol.py +4 -1
- package/skills/plan-to-pr/scripts/validate_run.py +4 -1
- package/skills/pr-converge/scripts/check_convergence.py +21 -19
- package/skills/pr-converge/scripts/check_convergence_availability.py +50 -7
- package/skills/pr-converge/scripts/conftest.py +35 -0
- package/skills/pr-converge/scripts/test_check_convergence_availability.py +65 -0
- package/skills/pr-converge/scripts/test_check_convergence_codex.py +11 -1
- package/skills/pr-converge/scripts/test_check_convergence_contract.py +9 -2
- package/skills/pr-loop-cloud-transport/SKILL.md +1 -1
- package/skills/rebase/SKILL.md +15 -3
- package/skills/reviewer-gates/SKILL.md +2 -2
- package/skills/show/SKILL.md +51 -0
- package/skills/show/references/accessibility.md +7 -0
- package/skills/show/references/art.md +3 -0
- package/skills/show/references/charts.md +3 -0
- package/skills/show/references/core-design.md +14 -0
- package/skills/show/references/erds.md +3 -0
- package/skills/show/references/flowcharts.md +3 -0
- package/skills/show/references/host-and-html.md +3 -0
- package/skills/show/references/illustrative-diagrams.md +10 -0
- package/skills/show/references/interaction.md +3 -0
- package/skills/show/references/mockups.md +3 -0
- package/skills/show/references/quality-gates.md +7 -0
- package/skills/show/references/structural-diagrams.md +3 -0
- package/skills/show/references/subject-inventory.md +21 -0
- package/skills/show/references/svg-contract.md +22 -0
- package/skills/show/routing.yaml +30 -0
- package/skills/show/samples/pr1262-v2.svg +222 -0
- package/skills/show/scripts/README.md +6 -0
- package/skills/show/scripts/validate-artifact.py +91 -0
- package/skills/show/scripts/validate-package.py +18 -0
- package/skills/show/templates/html-widget.html +4 -0
- package/skills/show/templates/svg-base.svg +19 -0
- package/skills/show/tests/fixtures/css-var.svg +6 -0
- package/skills/show/tests/fixtures/dead-ref.svg +7 -0
- package/skills/show/tests/fixtures/filled-glyph.svg +8 -0
- package/skills/show/tests/fixtures/inherited-fill.svg +18 -0
- package/skills/show/tests/fixtures/invalid.svg +1 -0
- package/skills/show/tests/fixtures/large-canvas.svg +21 -0
- package/skills/show/tests/fixtures/unfilled-connector.svg +15 -0
- package/skills/show/tests/fixtures/valid.html +1 -0
- package/skills/show/tests/test_validate-artifact.py +74 -0
- package/skills/show/tests/test_validators.py +59 -0
- package/skills/show/workflows/create-visual.md +13 -0
- package/skills/show/workflows/review-visual.md +20 -0
- package/skills/split-pr/SKILL.md +85 -0
- package/skills/split-pr/reference/path-layers.md +16 -0
- package/skills/split-pr/reference/proposal-format.md +15 -0
- package/skills/split-pr/reference/split-further-loop.md +10 -0
- package/skills/split-pr/reference/splitting-principles.md +26 -0
- package/skills/split-pr/scripts/analyze_pr.py +279 -0
- package/skills/split-pr/scripts/categorize_files.py +106 -0
- package/skills/split-pr/scripts/config/__init__.py +1 -0
- package/skills/split-pr/scripts/config/dependency_constants.py +14 -0
- package/skills/split-pr/scripts/config/git_operations_constants.py +36 -0
- package/skills/split-pr/scripts/config/packing_constants.py +61 -0
- package/skills/split-pr/scripts/config/plan_constants.py +49 -0
- package/skills/split-pr/scripts/config/split_pr_constants.py +110 -0
- package/skills/split-pr/scripts/execute_split_slices.py +82 -0
- package/skills/split-pr/scripts/pack_files_into_slices.py +212 -0
- package/skills/split-pr/scripts/split_pr_dependency_graph.py +70 -0
- package/skills/split-pr/scripts/split_pr_git_operations.py +184 -0
- package/skills/split-pr/scripts/split_pr_layer_order.py +58 -0
- package/skills/split-pr/scripts/split_pr_paginate.py +119 -0
- package/skills/split-pr/scripts/split_pr_process_runner.py +52 -0
- package/skills/split-pr/scripts/split_pr_script_types.py +126 -0
- package/skills/split-pr/scripts/split_pr_title.py +41 -0
- package/skills/split-pr/scripts/test_analyze_pr.py +228 -0
- package/skills/split-pr/scripts/test_categorize_files.py +55 -0
- package/skills/split-pr/scripts/test_categorize_files_packing.py +59 -0
- package/skills/split-pr/scripts/test_execute_split_slices.py +99 -0
- package/skills/split-pr/scripts/test_split_pr_dependency_graph.py +47 -0
- package/skills/split-pr/scripts/test_split_pr_git_operations.py +125 -0
- package/skills/split-pr/scripts/test_split_pr_layer_order.py +36 -0
- package/skills/split-pr/scripts/test_split_pr_paginate.py +65 -0
- package/skills/split-pr/scripts/test_split_pr_script_types.py +73 -0
- package/skills/split-pr/scripts/test_split_pr_title.py +28 -0
- package/skills/split-pr/scripts/test_verify_dependency_graph.py +46 -0
- package/skills/split-pr/scripts/test_verify_plan.py +56 -0
- package/skills/split-pr/scripts/test_verify_plan_contract.py +50 -0
- package/skills/split-pr/scripts/test_verify_plan_path_normalization.py +45 -0
- package/skills/split-pr/scripts/verify_dependency_graph.py +111 -0
- package/skills/split-pr/scripts/verify_plan.py +139 -0
- package/system-prompts/software-engineer.xml +11 -2
- package/commands/initialize.md +0 -90
- package/commands/stubcheck.md +0 -88
- package/output-styles/caveman-agent.md +0 -37
|
@@ -84,7 +84,7 @@ def test_remaining_percent_is_full_scale_minus_utilization(
|
|
|
84
84
|
tmp_path,
|
|
85
85
|
[
|
|
86
86
|
_entry("claude", credentials_path=PLACEHOLDER_CREDENTIALS_PRIMARY),
|
|
87
|
-
_entry("claude-
|
|
87
|
+
_entry("claude-profile-c", credentials_path=PLACEHOLDER_CREDENTIALS_SECONDARY),
|
|
88
88
|
],
|
|
89
89
|
)
|
|
90
90
|
monkeypatch.setattr(
|
|
@@ -100,7 +100,7 @@ def test_remaining_percent_is_full_scale_minus_utilization(
|
|
|
100
100
|
all_reports = usage.report_chain_weekly_usage(config_path=config_file)
|
|
101
101
|
assert [each_report.command for each_report in all_reports] == [
|
|
102
102
|
"claude",
|
|
103
|
-
"claude-
|
|
103
|
+
"claude-profile-c",
|
|
104
104
|
]
|
|
105
105
|
assert all_reports[0].weekly_remaining_percent == pytest.approx(
|
|
106
106
|
FULL_WEEKLY_PERCENT - 42.0
|
|
@@ -143,7 +143,7 @@ def test_probe_failure_yields_null_remaining_and_error_string(
|
|
|
143
143
|
tmp_path,
|
|
144
144
|
[
|
|
145
145
|
_entry("claude", credentials_path=PLACEHOLDER_CREDENTIALS_PRIMARY),
|
|
146
|
-
_entry("claude-
|
|
146
|
+
_entry("claude-profile-c", credentials_path=PLACEHOLDER_CREDENTIALS_SECONDARY),
|
|
147
147
|
],
|
|
148
148
|
)
|
|
149
149
|
monkeypatch.setattr(
|
|
@@ -217,7 +217,7 @@ def test_entry_credentials_path_is_passed_to_probe(
|
|
|
217
217
|
) -> None:
|
|
218
218
|
config_file = _write_chain_config(
|
|
219
219
|
tmp_path,
|
|
220
|
-
[_entry("claude-
|
|
220
|
+
[_entry("claude-profile-c", credentials_path=PLACEHOLDER_CREDENTIALS_TERTIARY)],
|
|
221
221
|
)
|
|
222
222
|
probed_paths: list[Path] = []
|
|
223
223
|
|
|
@@ -236,7 +236,7 @@ def test_entry_credentials_path_expands_user_home(
|
|
|
236
236
|
tilde_credentials_path = "~/.claude-accounts/secondary/.credentials.json"
|
|
237
237
|
config_file = _write_chain_config(
|
|
238
238
|
tmp_path,
|
|
239
|
-
[_entry("claude-
|
|
239
|
+
[_entry("claude-profile-c", credentials_path=tilde_credentials_path)],
|
|
240
240
|
)
|
|
241
241
|
probed_paths: list[Path] = []
|
|
242
242
|
|
|
@@ -254,7 +254,7 @@ def test_load_chain_carries_optional_credentials_path(tmp_path: Path) -> None:
|
|
|
254
254
|
tmp_path,
|
|
255
255
|
[
|
|
256
256
|
_entry("claude"),
|
|
257
|
-
_entry("claude-
|
|
257
|
+
_entry("claude-profile-c", credentials_path=PLACEHOLDER_CREDENTIALS_SECONDARY),
|
|
258
258
|
],
|
|
259
259
|
)
|
|
260
260
|
all_entries = chain_runner.load_chain(config_file)
|
|
@@ -286,7 +286,7 @@ def test_reports_to_json_payload_matches_cli_contract() -> None:
|
|
|
286
286
|
all_reports = [
|
|
287
287
|
usage.AccountUsageReport(command="claude", weekly_remaining_percent=58.0),
|
|
288
288
|
usage.AccountUsageReport(
|
|
289
|
-
command="claude-
|
|
289
|
+
command="claude-profile-c",
|
|
290
290
|
weekly_remaining_percent=None,
|
|
291
291
|
error="probe failed",
|
|
292
292
|
),
|
|
@@ -299,7 +299,7 @@ def test_reports_to_json_payload_matches_cli_contract() -> None:
|
|
|
299
299
|
JSON_WEEKLY_REMAINING_PERCENT_KEY: 58.0,
|
|
300
300
|
},
|
|
301
301
|
{
|
|
302
|
-
JSON_COMMAND_KEY: "claude-
|
|
302
|
+
JSON_COMMAND_KEY: "claude-profile-c",
|
|
303
303
|
JSON_WEEKLY_REMAINING_PERCENT_KEY: None,
|
|
304
304
|
JSON_ERROR_KEY: "probe failed",
|
|
305
305
|
},
|
|
@@ -314,7 +314,7 @@ def test_cli_writes_json_accounts_report(
|
|
|
314
314
|
tmp_path,
|
|
315
315
|
[
|
|
316
316
|
_entry("claude", credentials_path=PLACEHOLDER_CREDENTIALS_PRIMARY),
|
|
317
|
-
_entry("claude-
|
|
317
|
+
_entry("claude-profile-c", credentials_path=PLACEHOLDER_CREDENTIALS_SECONDARY),
|
|
318
318
|
],
|
|
319
319
|
)
|
|
320
320
|
monkeypatch.setattr(
|
|
@@ -341,7 +341,7 @@ def test_cli_writes_json_accounts_report(
|
|
|
341
341
|
JSON_WEEKLY_REMAINING_PERCENT_KEY: FULL_WEEKLY_PERCENT - 42.0,
|
|
342
342
|
},
|
|
343
343
|
{
|
|
344
|
-
JSON_COMMAND_KEY: "claude-
|
|
344
|
+
JSON_COMMAND_KEY: "claude-profile-c",
|
|
345
345
|
JSON_WEEKLY_REMAINING_PERCENT_KEY: None,
|
|
346
346
|
JSON_ERROR_KEY: "no token",
|
|
347
347
|
},
|
|
@@ -428,7 +428,7 @@ def test_probe_ignores_ingress_when_credential_token_missing(
|
|
|
428
428
|
) -> None:
|
|
429
429
|
config_file = _write_chain_config(
|
|
430
430
|
tmp_path,
|
|
431
|
-
[_entry("claude-
|
|
431
|
+
[_entry("claude-profile-c", credentials_path=PLACEHOLDER_CREDENTIALS_SECONDARY)],
|
|
432
432
|
)
|
|
433
433
|
|
|
434
434
|
class _FakeResolver:
|
|
@@ -462,7 +462,7 @@ def test_load_failure_yields_per_account_error_and_cli_exits_zero(
|
|
|
462
462
|
tmp_path,
|
|
463
463
|
[
|
|
464
464
|
_entry("claude", credentials_path=PLACEHOLDER_CREDENTIALS_PRIMARY),
|
|
465
|
-
_entry("claude-
|
|
465
|
+
_entry("claude-profile-c", credentials_path=PLACEHOLDER_CREDENTIALS_SECONDARY),
|
|
466
466
|
],
|
|
467
467
|
)
|
|
468
468
|
|
|
@@ -1141,7 +1141,7 @@ def test_usage_limit_fallover_delivers_full_prompt_to_each_binary(
|
|
|
1141
1141
|
"load_chain",
|
|
1142
1142
|
lambda _config_path: [
|
|
1143
1143
|
chain_runner.ChainEntry(command="claude", extra_args=()),
|
|
1144
|
-
chain_runner.ChainEntry(command="claude-
|
|
1144
|
+
chain_runner.ChainEntry(command="claude-profile-c", extra_args=()),
|
|
1145
1145
|
],
|
|
1146
1146
|
)
|
|
1147
1147
|
prompt_text_by_command: dict[str, str] = {}
|
|
@@ -1179,6 +1179,6 @@ def test_usage_limit_fallover_delivers_full_prompt_to_each_binary(
|
|
|
1179
1179
|
)
|
|
1180
1180
|
|
|
1181
1181
|
assert prompt_text_by_command["claude"] == FIXTURE_PROMPT_TEXT
|
|
1182
|
-
assert prompt_text_by_command["claude-
|
|
1182
|
+
assert prompt_text_by_command["claude-profile-c"] == FIXTURE_PROMPT_TEXT
|
|
1183
1183
|
assert outcome.tier_used == TIER_CLAUDE_HEADLESS
|
|
1184
1184
|
assert outcome.is_ok is True
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
"""Unit tests for verify_installable_package helpers.
|
|
2
|
+
|
|
3
|
+
Covers path extraction, manifest loading, tarball surface matching, and
|
|
4
|
+
command walking without requiring a full npm pack on every case.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import sys
|
|
10
|
+
import tarfile
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
|
|
13
|
+
_SCRIPTS_DIR = Path(__file__).resolve().parent
|
|
14
|
+
if str(_SCRIPTS_DIR) not in sys.path:
|
|
15
|
+
sys.path.insert(0, str(_SCRIPTS_DIR))
|
|
16
|
+
|
|
17
|
+
import verify_installable_package as mod
|
|
18
|
+
from dev_env_scripts_constants.verify_installable_package_constants import (
|
|
19
|
+
CLASS_PACKAGED,
|
|
20
|
+
MANIFEST_FILENAME,
|
|
21
|
+
PLUGIN_ROOT_TOKEN,
|
|
22
|
+
TARBALL_PACKAGE_PREFIX,
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
PACKAGE_ROOT = Path(__file__).resolve().parent.parent
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def test_load_manifest_lists_hooks_and_claude_md() -> None:
|
|
29
|
+
manifest = mod.load_installable_surfaces_manifest(from_package_root=PACKAGE_ROOT)
|
|
30
|
+
all_directories = mod.required_directory_names(manifest)
|
|
31
|
+
all_root_files = mod.required_root_file_names(manifest)
|
|
32
|
+
assert "hooks" in all_directories
|
|
33
|
+
assert "output-styles" in all_directories
|
|
34
|
+
assert "CLAUDE.md" in all_root_files
|
|
35
|
+
assert MANIFEST_FILENAME in all_root_files
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def test_extract_python_script_token_from_plugin_root_command() -> None:
|
|
39
|
+
command = (
|
|
40
|
+
f"python3 {PLUGIN_ROOT_TOKEN}hooks/blocking/pre_tool_use_dispatcher.py"
|
|
41
|
+
)
|
|
42
|
+
script_token = mod.extract_python_script_token(command)
|
|
43
|
+
assert script_token == f"{PLUGIN_ROOT_TOKEN}hooks/blocking/pre_tool_use_dispatcher.py"
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def test_package_relative_hook_script_path_strips_plugin_root() -> None:
|
|
47
|
+
script_token = f"{PLUGIN_ROOT_TOKEN}hooks/blocking/stop_dispatcher.py"
|
|
48
|
+
relative_path = mod.package_relative_hook_script_path(script_token)
|
|
49
|
+
assert relative_path == "hooks/blocking/stop_dispatcher.py"
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def test_extract_python_script_token_skips_inline_c_command() -> None:
|
|
53
|
+
command = 'python3 -c "import sys; print(1)"'
|
|
54
|
+
assert mod.extract_python_script_token(command) is None
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def test_surface_appears_in_tarball_members_for_directory_children() -> None:
|
|
58
|
+
all_members = frozenset(
|
|
59
|
+
{
|
|
60
|
+
f"{TARBALL_PACKAGE_PREFIX}hooks/hooks.json",
|
|
61
|
+
f"{TARBALL_PACKAGE_PREFIX}CLAUDE.md",
|
|
62
|
+
}
|
|
63
|
+
)
|
|
64
|
+
assert mod.surface_appears_in_tarball_members("hooks", all_members)
|
|
65
|
+
assert mod.surface_appears_in_tarball_members("CLAUDE.md", all_members)
|
|
66
|
+
assert not mod.surface_appears_in_tarball_members("missing-dir", all_members)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def test_missing_manifest_surfaces_in_tarball_reports_absent_root_file() -> None:
|
|
70
|
+
manifest = {
|
|
71
|
+
"directories": ["hooks"],
|
|
72
|
+
"root_files": ["CLAUDE.md", "codex-capability-map.json"],
|
|
73
|
+
}
|
|
74
|
+
all_members = frozenset(
|
|
75
|
+
{
|
|
76
|
+
f"{TARBALL_PACKAGE_PREFIX}hooks/hooks.json",
|
|
77
|
+
f"{TARBALL_PACKAGE_PREFIX}CLAUDE.md",
|
|
78
|
+
}
|
|
79
|
+
)
|
|
80
|
+
missing = mod.missing_manifest_surfaces_in_tarball(manifest, all_members)
|
|
81
|
+
assert missing == ["codex-capability-map.json"]
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def test_walk_json_command_fields_collects_nested_commands() -> None:
|
|
85
|
+
payload = {
|
|
86
|
+
"hooks": {
|
|
87
|
+
"PreToolUse": [
|
|
88
|
+
{"hooks": [{"type": "command", "command": "python3 a.py"}]},
|
|
89
|
+
],
|
|
90
|
+
"Stop": [{"hooks": [{"command": "python3 b.py"}]}],
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
all_commands = mod.walk_json_command_fields(payload)
|
|
94
|
+
assert all_commands == ["python3 a.py", "python3 b.py"]
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def test_list_npm_pack_tarball_members_reads_archive(tmp_path: Path) -> None:
|
|
98
|
+
tarball_path = tmp_path / "sample.tgz"
|
|
99
|
+
with tarfile.open(tarball_path, mode="w:gz") as archive:
|
|
100
|
+
member_path = tmp_path / "member.txt"
|
|
101
|
+
member_path.write_text("ok", encoding="utf-8")
|
|
102
|
+
archive.add(member_path, arcname=f"{TARBALL_PACKAGE_PREFIX}hooks/hooks.json")
|
|
103
|
+
all_members = mod.list_npm_pack_tarball_members(from_tarball_path=tarball_path)
|
|
104
|
+
assert f"{TARBALL_PACKAGE_PREFIX}hooks/hooks.json" in all_members
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def test_classify_surface_marks_hooks_packaged() -> None:
|
|
108
|
+
packaged_names = mod.load_package_json_file_entries(from_package_root=PACKAGE_ROOT)
|
|
109
|
+
classification = mod.classify_surface(
|
|
110
|
+
"hooks",
|
|
111
|
+
packaged_names,
|
|
112
|
+
from_package_root=PACKAGE_ROOT,
|
|
113
|
+
)
|
|
114
|
+
assert classification == CLASS_PACKAGED
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def test_load_hook_python_script_paths_returns_committed_hooks_paths() -> None:
|
|
118
|
+
all_paths = mod.load_hook_python_script_paths(from_package_root=PACKAGE_ROOT)
|
|
119
|
+
assert all_paths
|
|
120
|
+
assert all(each_path.startswith("hooks/") for each_path in all_paths)
|
|
121
|
+
assert all(each_path.endswith(".py") for each_path in all_paths)
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def test_package_root_path_points_at_package_json() -> None:
|
|
125
|
+
package_root = mod.package_root_path()
|
|
126
|
+
assert (package_root / "package.json").is_file()
|
|
127
|
+
assert package_root.name == "claude-dev-env"
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def test_repository_root_path_contains_packages_directory() -> None:
|
|
131
|
+
repository_root = mod.repository_root_path()
|
|
132
|
+
assert (repository_root / "packages" / "claude-dev-env").is_dir()
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def test_resolve_npm_executable_returns_existing_path() -> None:
|
|
136
|
+
npm_path = mod.resolve_npm_executable()
|
|
137
|
+
assert Path(npm_path).exists()
|
|
138
|
+
assert "npm" in Path(npm_path).name.lower()
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def test_tarball_member_prefix_for_surface_uses_package_prefix() -> None:
|
|
142
|
+
member_prefix = mod.tarball_member_prefix_for_surface("hooks")
|
|
143
|
+
assert member_prefix == f"{TARBALL_PACKAGE_PREFIX}hooks"
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def test_run_npm_pack_writes_tarball(tmp_path: Path) -> None:
|
|
147
|
+
tarball_path = mod.run_npm_pack(
|
|
148
|
+
into_directory=tmp_path,
|
|
149
|
+
from_package_root=PACKAGE_ROOT,
|
|
150
|
+
)
|
|
151
|
+
assert tarball_path.is_file()
|
|
152
|
+
assert tarball_path.suffix == ".tgz"
|
|
153
|
+
all_members = mod.list_npm_pack_tarball_members(from_tarball_path=tarball_path)
|
|
154
|
+
assert any(each_member.startswith(f"{TARBALL_PACKAGE_PREFIX}hooks/") for each_member in all_members)
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
def test_list_git_tracked_paths_under_includes_hooks_json() -> None:
|
|
158
|
+
all_tracked = mod.list_git_tracked_paths_under(
|
|
159
|
+
under_relative_path="packages/claude-dev-env/hooks",
|
|
160
|
+
from_repository_root=mod.repository_root_path(),
|
|
161
|
+
)
|
|
162
|
+
assert "packages/claude-dev-env/hooks/hooks.json" in all_tracked
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
def test_untracked_or_missing_hook_scripts_empty_for_live_hooks() -> None:
|
|
166
|
+
all_hook_scripts = mod.load_hook_python_script_paths(from_package_root=PACKAGE_ROOT)
|
|
167
|
+
missing_scripts = mod.untracked_or_missing_hook_scripts(
|
|
168
|
+
all_package_relative_scripts=all_hook_scripts,
|
|
169
|
+
from_package_root=PACKAGE_ROOT,
|
|
170
|
+
from_repository_root=mod.repository_root_path(),
|
|
171
|
+
)
|
|
172
|
+
assert missing_scripts == []
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
def test_untracked_or_missing_hook_scripts_flags_unknown_path() -> None:
|
|
176
|
+
missing_scripts = mod.untracked_or_missing_hook_scripts(
|
|
177
|
+
all_package_relative_scripts=["hooks/does_not_exist_anywhere.py"],
|
|
178
|
+
from_package_root=PACKAGE_ROOT,
|
|
179
|
+
from_repository_root=mod.repository_root_path(),
|
|
180
|
+
)
|
|
181
|
+
assert missing_scripts == ["hooks/does_not_exist_anywhere.py"]
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
def test_smoke_compile_python_scripts_accepts_live_hooks() -> None:
|
|
185
|
+
all_hook_scripts = mod.load_hook_python_script_paths(from_package_root=PACKAGE_ROOT)
|
|
186
|
+
compile_failures = mod.smoke_compile_python_scripts(
|
|
187
|
+
all_package_relative_scripts=all_hook_scripts,
|
|
188
|
+
from_package_root=PACKAGE_ROOT,
|
|
189
|
+
)
|
|
190
|
+
assert compile_failures == []
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
def test_smoke_check_install_entrypoint_passes() -> None:
|
|
194
|
+
entrypoint_failure = mod.smoke_check_install_entrypoint(from_package_root=PACKAGE_ROOT)
|
|
195
|
+
assert entrypoint_failure is None
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
def test_verify_packed_manifest_surfaces_finds_no_gaps() -> None:
|
|
199
|
+
missing_surfaces = mod.verify_packed_manifest_surfaces(from_package_root=PACKAGE_ROOT)
|
|
200
|
+
assert missing_surfaces == []
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
def test_run_all_installable_package_checks_passes_on_live_package() -> None:
|
|
204
|
+
all_failures = mod.run_all_installable_package_checks(
|
|
205
|
+
from_package_root=PACKAGE_ROOT,
|
|
206
|
+
from_repository_root=mod.repository_root_path(),
|
|
207
|
+
)
|
|
208
|
+
assert all_failures == []
|
|
@@ -630,3 +630,36 @@ def test_cli_reports_publication_and_rollback_failure_as_json(
|
|
|
630
630
|
assert report["reconcile_required"] is True
|
|
631
631
|
assert report["errors"] == 2
|
|
632
632
|
assert all(str(tmp_path) not in message for message in report["error_details"])
|
|
633
|
+
|
|
634
|
+
def test_frontmatter_accepts_empty_tools_list() -> None:
|
|
635
|
+
agent = parse_frontmatter(
|
|
636
|
+
Path("agent.md"),
|
|
637
|
+
"---\nname: x\ndescription: y\ntools: []\n---\n",
|
|
638
|
+
"agent.md",
|
|
639
|
+
)
|
|
640
|
+
assert agent.tools == ()
|
|
641
|
+
|
|
642
|
+
|
|
643
|
+
def test_frontmatter_accepts_block_scalar_description() -> None:
|
|
644
|
+
source_text = (
|
|
645
|
+
"---\n"
|
|
646
|
+
"name: x\n"
|
|
647
|
+
"description: |\n"
|
|
648
|
+
" line one\n"
|
|
649
|
+
" line two\n"
|
|
650
|
+
"---\n"
|
|
651
|
+
"body\n"
|
|
652
|
+
)
|
|
653
|
+
agent = parse_frontmatter(Path("agent.md"), source_text, "agent.md")
|
|
654
|
+
assert "line one" in agent.description
|
|
655
|
+
assert "line two" in agent.description
|
|
656
|
+
|
|
657
|
+
|
|
658
|
+
def test_frontmatter_rejects_non_string_name() -> None:
|
|
659
|
+
with pytest.raises(MaterializerError):
|
|
660
|
+
parse_frontmatter(
|
|
661
|
+
Path("bad.md"),
|
|
662
|
+
"---\nname: 1\ndescription: y\n---\n",
|
|
663
|
+
"bad.md",
|
|
664
|
+
)
|
|
665
|
+
|