claude-dev-env 2.9.0 → 2.11.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.
Files changed (318) hide show
  1. package/CLAUDE.md +2 -2
  2. package/_shared/advisor/CLAUDE.md +3 -2
  3. package/_shared/advisor/advisor-protocol.md +74 -108
  4. package/_shared/advisor/reference/advisor-block.md +37 -0
  5. package/_shared/advisor/reference/cli-chain.md +45 -0
  6. package/_shared/advisor/reference/consult-format.md +41 -0
  7. package/_shared/advisor/reference/lifecycle.md +21 -0
  8. package/_shared/advisor/reference/sol-rung.md +31 -0
  9. package/_shared/advisor/reference/spawn-walk-log.md +31 -0
  10. package/_shared/advisor/reference/third-party-bind.md +30 -0
  11. package/_shared/advisor/reference/warm-up.md +33 -0
  12. package/_shared/advisor/scripts/codex_sol_advisor.py +449 -0
  13. package/_shared/advisor/scripts/config/advisor_scripts_constants/advisor_route_constants.py +21 -0
  14. package/_shared/advisor/scripts/config/advisor_scripts_constants/model_tier_run_validator_constants.py +19 -17
  15. package/_shared/advisor/scripts/config/advisor_scripts_constants/sol_advisor_constants.py +28 -0
  16. package/_shared/advisor/scripts/model_tier_run_validator.py +32 -9
  17. package/_shared/advisor/scripts/tests/test_codex_sol_advisor.py +474 -0
  18. package/_shared/advisor/scripts/tests/test_model_tier_run_validator.py +79 -0
  19. package/_shared/advisor/scripts/tests/test_tier_model_ids.py +39 -17
  20. package/_shared/advisor/scripts/tier_model_ids.py +24 -0
  21. package/_shared/pr-loop/CLAUDE.md +1 -1
  22. package/_shared/pr-loop/audit-contract.md +17 -6
  23. package/_shared/pr-loop/audit-reply-template.md +4 -4
  24. package/_shared/pr-loop/code-rules-gate.md +3 -5
  25. package/_shared/pr-loop/fix-protocol.md +2 -3
  26. package/_shared/pr-loop/gh-payloads.md +1 -1
  27. package/_shared/pr-loop/scripts/CLAUDE.md +1 -1
  28. package/_shared/pr-loop/scripts/README.md +1 -1
  29. package/_shared/pr-loop/scripts/code_rules_gate.py +2 -0
  30. package/_shared/pr-loop/scripts/code_rules_gate_parts/gate_running.py +16 -1
  31. package/_shared/pr-loop/scripts/code_rules_gate_parts/git_blob_readers.py +11 -5
  32. package/_shared/pr-loop/scripts/preflight.py +9 -4
  33. package/_shared/pr-loop/scripts/reviews_disabled.py +50 -22
  34. package/_shared/pr-loop/scripts/tests/conftest.py +20 -0
  35. package/_shared/pr-loop/scripts/tests/test_claude_permissions_common.py +6 -6
  36. package/_shared/pr-loop/scripts/tests/test_reviews_disabled.py +50 -6
  37. package/_shared/pr-loop/scripts/tests/test_revoke_project_claude_permissions.py +1 -1
  38. package/_shared/pr-loop/state-schema.md +5 -14
  39. package/agents/CLAUDE.md +2 -2
  40. package/agents/clean-coder.md +58 -548
  41. package/agents/code-quality-agent.md +10 -2
  42. package/agents/code-verifier.md +1 -1
  43. package/agents/test_agent_frontmatter.py +32 -40
  44. package/audit-rubrics/CLAUDE.md +2 -1
  45. package/audit-rubrics/audit-categories.json +704 -0
  46. package/audit-rubrics/prompts/category-i-concurrency.md +1 -1
  47. package/bin/CLAUDE.md +16 -5
  48. package/bin/ever-shipped-skills.mjs +2 -0
  49. package/bin/install-plan.mjs +402 -0
  50. package/bin/install-transaction.mjs +455 -0
  51. package/bin/install.mjs +593 -147
  52. package/bin/install.plan.test.mjs +194 -0
  53. package/bin/install.profile-root.test.mjs +154 -0
  54. package/bin/install.profiles.test.mjs +253 -0
  55. package/bin/install.settings-defaults.test.mjs +200 -0
  56. package/bin/install.transaction.test.mjs +400 -0
  57. package/bin/install.uninstall-transaction.test.mjs +418 -0
  58. package/bin/merge_managed_permissions.mjs +130 -0
  59. package/bin/resolve-install-root.mjs +181 -0
  60. package/bin/select-install-targets.mjs +401 -0
  61. package/commands/CLAUDE.md +0 -2
  62. package/docs/references/CLAUDE.md +3 -1
  63. package/docs/references/advisor-tool.md +25 -7
  64. package/docs/references/prose-style-enforcement.md +25 -0
  65. package/docs/references/team-advisor-skill.md +3 -3
  66. package/docs/references/weak-executor-advisor.md +91 -0
  67. package/hooks/blocking/CLAUDE.md +6 -6
  68. package/hooks/blocking/_path_setup.py +9 -5
  69. package/hooks/blocking/code_rules_docstrings.py +124 -30
  70. package/hooks/blocking/code_rules_enforcer.py +161 -16
  71. package/hooks/blocking/code_rules_shared.py +40 -23
  72. package/hooks/blocking/config/CLAUDE.md +3 -5
  73. package/hooks/blocking/config/prose_style_enforcement_constants.py +38 -0
  74. package/hooks/blocking/config/test_prose_style_enforcement_constants.py +45 -0
  75. package/hooks/blocking/eli11_reply_enforcer.py +70 -113
  76. package/hooks/blocking/hedging_language_blocker.py +103 -20
  77. package/hooks/blocking/hook_prose_detector_consistency.py +6 -0
  78. package/hooks/blocking/intent_only_ending_blocker.py +6 -0
  79. package/hooks/blocking/plain_language_blocker.py +139 -20
  80. package/hooks/blocking/pre_tool_use_dispatcher.py +102 -20
  81. package/hooks/blocking/state_description_blocker.py +7 -1
  82. package/hooks/blocking/tdd_enforcer.py +8 -0
  83. package/hooks/blocking/test__path_setup.py +28 -0
  84. package/hooks/blocking/test_code_rules_enforcer_agent_home_tooling.py +99 -0
  85. package/hooks/blocking/test_code_rules_enforcer_docstring_args_span_scope.py +232 -10
  86. package/hooks/blocking/test_code_rules_enforcer_ephemeral.py +1 -1
  87. package/hooks/blocking/test_code_rules_enforcer_join_separator_magic.py +41 -0
  88. package/hooks/blocking/test_code_rules_enforcer_string_magic.py +98 -0
  89. package/hooks/blocking/test_eli11_reply_enforcer.py +98 -165
  90. package/hooks/blocking/test_fable_spawn_gate.py +18 -11
  91. package/hooks/blocking/test_hedging_language_blocker.py +120 -1
  92. package/hooks/blocking/test_hook_prose_detector_consistency.py +28 -8
  93. package/hooks/blocking/test_intent_only_ending_blocker.py +27 -2
  94. package/hooks/blocking/test_package_inventory_stale_blocker.py +11 -4
  95. package/hooks/blocking/test_plain_language_blocker.py +129 -19
  96. package/hooks/blocking/test_plain_language_blocker_allowlist.py +70 -26
  97. package/hooks/blocking/test_pre_tool_use_dispatcher.py +99 -26
  98. package/hooks/blocking/test_pre_tool_use_dispatcher_native.py +87 -50
  99. package/hooks/blocking/test_state_description_blocker.py +45 -2
  100. package/hooks/blocking/test_stop_dispatcher.py +11 -7
  101. package/hooks/blocking/test_volatile_path_in_post_blocker.py +12 -12
  102. package/hooks/blocking/volatile_path_in_post_blocker.py +2 -2
  103. package/hooks/hooks.json +15 -0
  104. package/hooks/hooks_constants/CLAUDE.md +14 -3
  105. package/hooks/hooks_constants/ask_user_question_shape.py +281 -0
  106. package/hooks/hooks_constants/code_rules_enforcer_constants.py +2 -1
  107. package/hooks/hooks_constants/eli11_reply_enforcer_constants.py +5 -12
  108. package/hooks/hooks_constants/hedging_uncertainty_constants.py +42 -0
  109. package/hooks/hooks_constants/issue_tracker_session_starter_constants.py +23 -0
  110. package/hooks/hooks_constants/orchestrator_auto_starter_constants.py +23 -0
  111. package/hooks/hooks_constants/piped_pytest_blocker_constants.py +4 -1
  112. package/hooks/hooks_constants/plain_language_blocker_constants.py +4 -1
  113. package/hooks/hooks_constants/pre_tool_use_dispatcher_constants.py +6 -0
  114. package/hooks/hooks_constants/project_paths_reader.py +31 -4
  115. package/hooks/hooks_constants/prose_matcher_precision_constants.py +40 -0
  116. package/hooks/hooks_constants/pytest_invocation.py +354 -0
  117. package/hooks/hooks_constants/session_start_injector.py +163 -0
  118. package/hooks/hooks_constants/session_start_injector_constants.py +46 -0
  119. package/hooks/hooks_constants/shell_command_pipeline.py +397 -0
  120. package/hooks/hooks_constants/shell_command_segments.py +5 -0
  121. package/hooks/hooks_constants/test_ask_user_question_shape.py +167 -0
  122. package/hooks/hooks_constants/test_project_paths_reader.py +29 -0
  123. package/hooks/hooks_constants/test_prose_metrics_parity.py +8 -0
  124. package/hooks/hooks_constants/test_pytest_invocation.py +130 -0
  125. package/hooks/hooks_constants/test_session_start_injector.py +168 -0
  126. package/hooks/hooks_constants/test_shell_command_pipeline.py +135 -0
  127. package/hooks/hooks_constants/volatile_path_in_post_blocker_constants.py +1 -1
  128. package/hooks/hooks_constants/working_style_prompt_constants.py +30 -0
  129. package/hooks/observability/CLAUDE.md +2 -0
  130. package/hooks/observability/prose_matcher_advisory.py +237 -0
  131. package/hooks/observability/test_prose_matcher_advisory.py +143 -0
  132. package/hooks/session/CLAUDE.md +9 -1
  133. package/hooks/session/_path_setup.py +13 -0
  134. package/hooks/session/issue_tracker_session_starter.py +135 -0
  135. package/hooks/session/orchestrator_auto_starter.py +100 -0
  136. package/hooks/session/test__path_setup.py +28 -0
  137. package/hooks/session/test_issue_tracker_session_starter.py +104 -0
  138. package/hooks/session/test_orchestrator_auto_starter.py +99 -0
  139. package/hooks/session/test_working_style_prompt.py +47 -0
  140. package/hooks/session/untracked_repo_detector.py +1 -24
  141. package/hooks/session/working_style_prompt.py +36 -0
  142. package/hooks/validators/_path_setup.py +19 -0
  143. package/hooks/validators/run_all_validators.py +8 -13
  144. package/installable-surfaces.manifest.json +21 -0
  145. package/output-styles/CLAUDE.md +1 -3
  146. package/package.json +4 -2
  147. package/rules/CLAUDE.md +1 -0
  148. package/rules/durable-post-artifacts.md +2 -2
  149. package/rules/eli11-replies.md +6 -1
  150. package/rules/hedging-claims.md +4 -2
  151. package/rules/long-horizon-autonomy.md +3 -1
  152. package/rules/opus5-communication-contract.md +45 -0
  153. package/rules/plain-language.md +2 -2
  154. package/rules/research-mode.md +1 -1
  155. package/scripts/CLAUDE.md +11 -0
  156. package/scripts/Sync-RepoMain.ps1 +215 -0
  157. package/scripts/active_capability_references.py +218 -0
  158. package/scripts/ci/windows-installer-lifecycle.ps1 +78 -0
  159. package/scripts/claude_chain_runner.py +394 -6
  160. package/scripts/claude_chain_usage.py +1 -1
  161. package/scripts/codex_compat_materializer.py +105 -85
  162. package/scripts/dev_env_scripts_constants/CLAUDE.md +2 -0
  163. package/scripts/dev_env_scripts_constants/active_capability_constants.py +46 -0
  164. package/scripts/dev_env_scripts_constants/claude_chain_constants.py +74 -0
  165. package/scripts/dev_env_scripts_constants/verify_installable_package_constants.py +116 -0
  166. package/scripts/profile-isolation-launchers/config/mcp-bundles.json +25 -0
  167. package/scripts/profile-isolation-launchers/config/profile-isolation-constants.mjs +60 -0
  168. package/scripts/profile-isolation-launchers/config/profiles.manifest.json +54 -0
  169. package/scripts/profile-isolation-launchers/config/shared-allowlist.json +64 -0
  170. package/scripts/profile-isolation-launchers/launcher-runtime.mjs +180 -0
  171. package/scripts/profile-isolation-launchers/lib/profile-manifest.mjs +288 -0
  172. package/scripts/profile-isolation-launchers/mcp-bundles.mjs +275 -0
  173. package/scripts/profile-isolation-launchers/profile-isolation-contract.test.mjs +221 -0
  174. package/scripts/profile-isolation-launchers/tests/launcher-runtime.test.mjs +108 -0
  175. package/scripts/profile-isolation-launchers/tests/mcp-bundles.test.mjs +147 -0
  176. package/scripts/profile-isolation-launchers/tests/shortcut-contract.test.ps1 +102 -0
  177. package/scripts/profile-isolation-launchers/tests/version-compatibility.test.mjs +210 -0
  178. package/scripts/profile-isolation-launchers/version-compatibility.mjs +299 -0
  179. package/scripts/profile-isolation-launchers/windows/shortcut-inventory.ps1 +127 -0
  180. package/scripts/profile-isolation-launchers/windows/shortcut-manifest.json +51 -0
  181. package/scripts/profile-isolation-launchers/windows/shortcut-reconcile.ps1 +77 -0
  182. package/scripts/spawn_grok_batch.py +3 -0
  183. package/scripts/test_active_capability_references.py +108 -0
  184. package/scripts/test_claude_chain_runner.py +414 -82
  185. package/scripts/test_claude_chain_usage.py +12 -12
  186. package/scripts/test_resolve_worker_spawn.py +2 -2
  187. package/scripts/test_verify_installable_package.py +208 -0
  188. package/scripts/tests/test_codex_compat_materializer.py +33 -0
  189. package/scripts/verify_installable_package.py +612 -0
  190. package/settings.json +10 -0
  191. package/skills/CLAUDE.md +2 -0
  192. package/skills/_shared/advisor/CLAUDE.md +1 -1
  193. package/skills/_shared/advisor/scripts/README.md +2 -0
  194. package/skills/_shared/pr-loop/scripts/CLAUDE.md +1 -0
  195. package/skills/_shared/pr-loop/scripts/audit_category_schema.py +355 -0
  196. package/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/CLAUDE.md +1 -0
  197. package/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/audit_category_schema_constants.py +32 -0
  198. package/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/path_resolver_constants.py +7 -19
  199. package/skills/_shared/pr-loop/scripts/test_audit_category_schema.py +94 -0
  200. package/skills/_shared/pr-loop/scripts/test_build_audit_prompt.py +21 -0
  201. package/skills/autoconverge/reference/convergence.md +2 -1
  202. package/skills/autoconverge/reference/stop-conditions.md +5 -3
  203. package/skills/beat-sheet/SKILL.md +54 -0
  204. package/skills/beat-sheet/reference/visual-beats.md +29 -0
  205. package/skills/bugteam/CONSTRAINTS.md +4 -4
  206. package/skills/bugteam/EXAMPLES.md +1 -1
  207. package/skills/bugteam/reference/README.md +1 -1
  208. package/skills/e-code-review/SKILL.md +26 -5
  209. package/skills/e-code-review/reference/effort-evaluation.md +35 -0
  210. package/skills/e-code-review/reference/medium.md +15 -4
  211. package/skills/e-code-review/scripts/config/e_code_review_effort_constants/__init__.py +41 -0
  212. package/skills/e-code-review/scripts/config/e_code_review_effort_constants/effort_constants.py +40 -0
  213. package/skills/e-code-review/scripts/e_code_review_scripts_constants/finding_pipeline_constants.py +49 -0
  214. package/skills/e-code-review/scripts/effort_defaults_evidence.json +186 -0
  215. package/skills/e-code-review/scripts/effort_evaluation.py +362 -0
  216. package/skills/e-code-review/scripts/finding_pipeline.py +140 -0
  217. package/skills/e-code-review/scripts/fixtures/demanding.json +26 -0
  218. package/skills/e-code-review/scripts/fixtures/easy.json +14 -0
  219. package/skills/e-code-review/scripts/fixtures/medium.json +20 -0
  220. package/skills/e-code-review/scripts/grok_code_review.py +16 -7
  221. package/skills/e-code-review/scripts/test_effort_evaluation.py +180 -0
  222. package/skills/e-code-review/scripts/test_finding_pipeline.py +197 -0
  223. package/skills/e-code-review/scripts/test_grok_code_review.py +77 -0
  224. package/skills/grokify/SKILL.md +1 -1
  225. package/skills/grokify/templates/handoff-template.md +2 -2
  226. package/skills/orchestrator/SKILL.md +5 -4
  227. package/skills/plan-to-pr/scripts/create_packet.py +4 -4
  228. package/skills/plan-to-pr/scripts/load_skill_constants.py +41 -0
  229. package/skills/plan-to-pr/scripts/validate_packet.py +4 -4
  230. package/skills/plan-to-pr/scripts/validate_protocol.py +4 -1
  231. package/skills/plan-to-pr/scripts/validate_run.py +4 -1
  232. package/skills/pr-converge/scripts/check_convergence.py +21 -19
  233. package/skills/pr-converge/scripts/check_convergence_availability.py +50 -7
  234. package/skills/pr-converge/scripts/conftest.py +35 -0
  235. package/skills/pr-converge/scripts/test_check_convergence_availability.py +65 -0
  236. package/skills/pr-converge/scripts/test_check_convergence_codex.py +11 -1
  237. package/skills/pr-converge/scripts/test_check_convergence_contract.py +9 -2
  238. package/skills/pr-loop-cloud-transport/SKILL.md +1 -1
  239. package/skills/rebase/SKILL.md +15 -3
  240. package/skills/reviewer-gates/SKILL.md +2 -2
  241. package/skills/show/SKILL.md +51 -0
  242. package/skills/show/references/accessibility.md +7 -0
  243. package/skills/show/references/art.md +3 -0
  244. package/skills/show/references/charts.md +3 -0
  245. package/skills/show/references/core-design.md +14 -0
  246. package/skills/show/references/erds.md +3 -0
  247. package/skills/show/references/flowcharts.md +3 -0
  248. package/skills/show/references/host-and-html.md +3 -0
  249. package/skills/show/references/illustrative-diagrams.md +10 -0
  250. package/skills/show/references/interaction.md +3 -0
  251. package/skills/show/references/mockups.md +3 -0
  252. package/skills/show/references/quality-gates.md +7 -0
  253. package/skills/show/references/structural-diagrams.md +3 -0
  254. package/skills/show/references/subject-inventory.md +21 -0
  255. package/skills/show/references/svg-contract.md +22 -0
  256. package/skills/show/routing.yaml +30 -0
  257. package/skills/show/samples/pr1262-v2.svg +222 -0
  258. package/skills/show/scripts/README.md +6 -0
  259. package/skills/show/scripts/validate-artifact.py +91 -0
  260. package/skills/show/scripts/validate-package.py +18 -0
  261. package/skills/show/templates/html-widget.html +4 -0
  262. package/skills/show/templates/svg-base.svg +19 -0
  263. package/skills/show/tests/fixtures/css-var.svg +6 -0
  264. package/skills/show/tests/fixtures/dead-ref.svg +7 -0
  265. package/skills/show/tests/fixtures/filled-glyph.svg +8 -0
  266. package/skills/show/tests/fixtures/inherited-fill.svg +18 -0
  267. package/skills/show/tests/fixtures/invalid.svg +1 -0
  268. package/skills/show/tests/fixtures/large-canvas.svg +21 -0
  269. package/skills/show/tests/fixtures/unfilled-connector.svg +15 -0
  270. package/skills/show/tests/fixtures/valid.html +1 -0
  271. package/skills/show/tests/test_validate-artifact.py +74 -0
  272. package/skills/show/tests/test_validators.py +59 -0
  273. package/skills/show/workflows/create-visual.md +13 -0
  274. package/skills/show/workflows/review-visual.md +20 -0
  275. package/skills/split-pr/SKILL.md +85 -0
  276. package/skills/split-pr/reference/path-layers.md +16 -0
  277. package/skills/split-pr/reference/proposal-format.md +15 -0
  278. package/skills/split-pr/reference/split-further-loop.md +10 -0
  279. package/skills/split-pr/reference/splitting-principles.md +26 -0
  280. package/skills/split-pr/scripts/analyze_pr.py +279 -0
  281. package/skills/split-pr/scripts/categorize_files.py +106 -0
  282. package/skills/split-pr/scripts/config/__init__.py +1 -0
  283. package/skills/split-pr/scripts/config/dependency_constants.py +14 -0
  284. package/skills/split-pr/scripts/config/git_operations_constants.py +36 -0
  285. package/skills/split-pr/scripts/config/packing_constants.py +61 -0
  286. package/skills/split-pr/scripts/config/plan_constants.py +49 -0
  287. package/skills/split-pr/scripts/config/split_pr_constants.py +110 -0
  288. package/skills/split-pr/scripts/execute_split_slices.py +82 -0
  289. package/skills/split-pr/scripts/pack_files_into_slices.py +212 -0
  290. package/skills/split-pr/scripts/split_pr_dependency_graph.py +70 -0
  291. package/skills/split-pr/scripts/split_pr_git_operations.py +184 -0
  292. package/skills/split-pr/scripts/split_pr_layer_order.py +58 -0
  293. package/skills/split-pr/scripts/split_pr_paginate.py +119 -0
  294. package/skills/split-pr/scripts/split_pr_process_runner.py +52 -0
  295. package/skills/split-pr/scripts/split_pr_script_types.py +126 -0
  296. package/skills/split-pr/scripts/split_pr_title.py +41 -0
  297. package/skills/split-pr/scripts/test_analyze_pr.py +228 -0
  298. package/skills/split-pr/scripts/test_categorize_files.py +55 -0
  299. package/skills/split-pr/scripts/test_categorize_files_packing.py +59 -0
  300. package/skills/split-pr/scripts/test_execute_split_slices.py +99 -0
  301. package/skills/split-pr/scripts/test_split_pr_dependency_graph.py +47 -0
  302. package/skills/split-pr/scripts/test_split_pr_git_operations.py +125 -0
  303. package/skills/split-pr/scripts/test_split_pr_layer_order.py +36 -0
  304. package/skills/split-pr/scripts/test_split_pr_paginate.py +65 -0
  305. package/skills/split-pr/scripts/test_split_pr_script_types.py +73 -0
  306. package/skills/split-pr/scripts/test_split_pr_title.py +28 -0
  307. package/skills/split-pr/scripts/test_verify_dependency_graph.py +46 -0
  308. package/skills/split-pr/scripts/test_verify_plan.py +56 -0
  309. package/skills/split-pr/scripts/test_verify_plan_contract.py +50 -0
  310. package/skills/split-pr/scripts/test_verify_plan_path_normalization.py +45 -0
  311. package/skills/split-pr/scripts/verify_dependency_graph.py +111 -0
  312. package/skills/split-pr/scripts/verify_plan.py +139 -0
  313. package/skills/team-advisor/SKILL.md +7 -4
  314. package/skills/team-advisor/reference/advisor-docs-review.md +207 -0
  315. package/system-prompts/software-engineer.xml +11 -2
  316. package/commands/initialize.md +0 -90
  317. package/commands/stubcheck.md +0 -88
  318. 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-ev", credentials_path=PLACEHOLDER_CREDENTIALS_SECONDARY),
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-ev",
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-ev", credentials_path=PLACEHOLDER_CREDENTIALS_SECONDARY),
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-ev", credentials_path=PLACEHOLDER_CREDENTIALS_TERTIARY)],
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-ev", credentials_path=tilde_credentials_path)],
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-ev", credentials_path=PLACEHOLDER_CREDENTIALS_SECONDARY),
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-ev",
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-ev",
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-ev", credentials_path=PLACEHOLDER_CREDENTIALS_SECONDARY),
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-ev",
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-ev", credentials_path=PLACEHOLDER_CREDENTIALS_SECONDARY)],
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-ev", credentials_path=PLACEHOLDER_CREDENTIALS_SECONDARY),
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-ev", extra_args=()),
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-ev"] == FIXTURE_PROMPT_TEXT
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
+