claude-dev-env 2.8.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 +8 -2
- 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 +62 -562
- package/agents/code-quality-agent.md +10 -2
- package/agents/code-verifier.md +1 -1
- package/agents/test_agent_frontmatter.py +58 -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/CODE_RULES.md +4 -2
- package/docs/references/CLAUDE.md +3 -2
- package/docs/references/advisor-tool.md +44 -6
- package/docs/references/prose-style-enforcement.md +25 -0
- package/docs/references/team-advisor-skill.md +14 -8
- 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/code_rules_path_utils_constants.py +1 -0
- 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 +15 -0
- package/package.json +5 -2
- package/rules/CLAUDE.md +1 -0
- package/rules/code-standards.md +33 -7
- package/rules/durable-post-artifacts.md +2 -2
- package/rules/eli11-replies.md +7 -2
- 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 +13 -2
- 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 +3 -1
- 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/grok_run_ledger_constants.py +50 -0
- package/scripts/dev_env_scripts_constants/grok_worker_constants.py +104 -0
- package/scripts/dev_env_scripts_constants/verify_installable_package_constants.py +116 -0
- package/scripts/grok_patch_artifacts.py +123 -0
- package/scripts/grok_run_ledger.py +318 -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 +556 -9
- 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_grok_patch_artifacts.py +82 -0
- package/scripts/test_grok_run_ledger.py +116 -0
- package/scripts/test_resolve_worker_spawn.py +2 -2
- package/scripts/test_spawn_grok_batch.py +295 -0
- 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 +6 -2
- package/skills/_shared/CLAUDE.md +37 -4
- package/skills/_shared/advisor/CLAUDE.md +9 -0
- package/skills/_shared/advisor/advisor-protocol.md +5 -0
- package/skills/_shared/advisor/scripts/README.md +9 -0
- package/skills/_shared/end-of-run-gotcha-recommendations.md +156 -0
- package/skills/_shared/pr-loop/CLAUDE.md +18 -1
- package/skills/_shared/pr-loop/audit-contract.md +5 -0
- package/skills/_shared/pr-loop/audit-reply-template.md +5 -0
- package/skills/_shared/pr-loop/code-rules-gate.md +5 -0
- package/skills/_shared/pr-loop/fix-protocol.md +5 -0
- package/skills/_shared/pr-loop/gh-payloads.md +5 -0
- package/skills/_shared/pr-loop/post-audit-thread-contract.md +5 -0
- package/skills/_shared/pr-loop/precatch-rubric.md +5 -0
- package/skills/_shared/pr-loop/scripts/CLAUDE.md +9 -1
- package/skills/_shared/pr-loop/scripts/RUNTIME_SCRIPTS.md +29 -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/_shared/pr-loop/state-schema.md +5 -0
- package/skills/_shared/pr-loop/worker-spawn.md +5 -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 +29 -3
- 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/reference/runner-selection.md +40 -0
- 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/__init__.py +1 -0
- package/skills/e-code-review/scripts/e_code_review_scripts_constants/finding_pipeline_constants.py +49 -0
- package/skills/e-code-review/scripts/e_code_review_scripts_constants/grok_code_review_constants.py +55 -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 +230 -0
- 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 +289 -0
- package/skills/grok-spawn/SKILL.md +5 -0
- package/skills/orchestrator/SKILL.md +5 -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/skills/task-build/reference/tool-routing.md +3 -0
- package/skills/team-advisor/SKILL.md +23 -44
- package/system-prompts/software-engineer.xml +17 -5
- package/commands/initialize.md +0 -90
- package/commands/stubcheck.md +0 -88
- package/skills/test_markdown_link_integrity.py +0 -107
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
"""Recognize and allow fixtures for pytest_invocation segment classification."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import pytest
|
|
6
|
+
|
|
7
|
+
from hooks_constants.pytest_invocation import (
|
|
8
|
+
segment_reports_a_pytest_exit_code,
|
|
9
|
+
segment_runs_pytest,
|
|
10
|
+
)
|
|
11
|
+
from hooks_constants.shell_command_pipeline import pipeline_segments_for_command
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def _first_segment_tokens(command: str) -> list[str]:
|
|
15
|
+
all_pairs = pipeline_segments_for_command(command)
|
|
16
|
+
assert all_pairs, f"expected at least one segment for {command!r}"
|
|
17
|
+
return all_pairs[0][0]
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@pytest.mark.parametrize(
|
|
21
|
+
"all_segment_tokens",
|
|
22
|
+
[
|
|
23
|
+
["pytest", "tests"],
|
|
24
|
+
["pytest.exe", "tests"],
|
|
25
|
+
["pytest.bat", "tests"],
|
|
26
|
+
["py.test", "tests"],
|
|
27
|
+
["python", "-m", "pytest"],
|
|
28
|
+
["python", "-mpytest"],
|
|
29
|
+
["python3", "-m", "pytest", "tests"],
|
|
30
|
+
["pythonw", "-m", "pytest"],
|
|
31
|
+
["pypy3", "-m", "pytest", "tests"],
|
|
32
|
+
["pypy3", "--jit", "off", "-m", "pytest"],
|
|
33
|
+
["py", "-3", "-m", "pytest"],
|
|
34
|
+
[r"C:\Python313\python.exe", "-m", "pytest"],
|
|
35
|
+
["time", "pytest", "tests"],
|
|
36
|
+
["sudo", "pytest", "tests"],
|
|
37
|
+
["sudo", "-u", "ci", "pytest", "tests"],
|
|
38
|
+
["sudo", "-nu", "ci", "pytest", "tests"],
|
|
39
|
+
["uv", "run", "pytest", "tests"],
|
|
40
|
+
["uv", "run", "--frozen", "pytest", "tests"],
|
|
41
|
+
["uv", "tool", "run", "pytest", "tests"],
|
|
42
|
+
["uvx", "pytest", "tests"],
|
|
43
|
+
["poetry", "run", "pytest", "tests"],
|
|
44
|
+
["pipenv", "run", "pytest", "tests"],
|
|
45
|
+
["pdm", "run", "pytest", "tests"],
|
|
46
|
+
["hatch", "run", "pytest", "tests"],
|
|
47
|
+
["rye", "run", "pytest", "tests"],
|
|
48
|
+
["coverage", "run", "-m", "pytest", "tests"],
|
|
49
|
+
["python", "-m", "coverage", "run", "-m", "pytest"],
|
|
50
|
+
["sudo", "uv", "run", "pytest", "tests"],
|
|
51
|
+
],
|
|
52
|
+
)
|
|
53
|
+
def test_should_recognize_pytest_invocation_forms(
|
|
54
|
+
all_segment_tokens: list[str],
|
|
55
|
+
) -> None:
|
|
56
|
+
assert segment_runs_pytest(all_segment_tokens) is True
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
@pytest.mark.parametrize(
|
|
60
|
+
"all_segment_tokens",
|
|
61
|
+
[
|
|
62
|
+
["git", "status"],
|
|
63
|
+
["python", "-m", "mypy", "."],
|
|
64
|
+
["python", "myscript.py", "-m", "pytest"],
|
|
65
|
+
["uv", "run", "--with", "pytest", "mypy", "."],
|
|
66
|
+
["uv", "sync"],
|
|
67
|
+
["uv", "tool", "install", "pytest"],
|
|
68
|
+
["coverage", "run", "-m", "mypy", "."],
|
|
69
|
+
["coverage", "report"],
|
|
70
|
+
["sudo", "apt", "update"],
|
|
71
|
+
["pyright", "-m", "pytest", "."],
|
|
72
|
+
["echo", "pytest"],
|
|
73
|
+
["ls", "pytest"],
|
|
74
|
+
[],
|
|
75
|
+
],
|
|
76
|
+
)
|
|
77
|
+
def test_should_allow_non_pytest_segment_forms(
|
|
78
|
+
all_segment_tokens: list[str],
|
|
79
|
+
) -> None:
|
|
80
|
+
assert segment_runs_pytest(all_segment_tokens) is False
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
@pytest.mark.parametrize(
|
|
84
|
+
"all_segment_tokens",
|
|
85
|
+
[
|
|
86
|
+
["bash", "-c", "pytest tests"],
|
|
87
|
+
["bash", "-euc", "pytest tests"],
|
|
88
|
+
["bash", "-euo", "pipefail", "-c", "python -m pytest"],
|
|
89
|
+
["sh", "-c", "pytest tests"],
|
|
90
|
+
["pwsh", "-Command", "pytest tests"],
|
|
91
|
+
["cmd", "/c", "python", "-m", "pytest", "tests"],
|
|
92
|
+
],
|
|
93
|
+
)
|
|
94
|
+
def test_should_recognize_shell_string_exec_pytest(
|
|
95
|
+
all_segment_tokens: list[str],
|
|
96
|
+
) -> None:
|
|
97
|
+
assert segment_reports_a_pytest_exit_code(all_segment_tokens) is True
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
@pytest.mark.parametrize(
|
|
101
|
+
"all_segment_tokens",
|
|
102
|
+
[
|
|
103
|
+
["bash", "scripts/ci.sh", "-c", "pytest tests"],
|
|
104
|
+
["bash", "--", "-c", "pytest tests"],
|
|
105
|
+
["bash", "-Cu", "script.sh"],
|
|
106
|
+
["pwsh", "-NonInteractive", "-File", "a.ps1"],
|
|
107
|
+
["bash", "script.sh"],
|
|
108
|
+
],
|
|
109
|
+
)
|
|
110
|
+
def test_should_allow_shell_forms_that_do_not_run_pytest(
|
|
111
|
+
all_segment_tokens: list[str],
|
|
112
|
+
) -> None:
|
|
113
|
+
assert segment_reports_a_pytest_exit_code(all_segment_tokens) is False
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def test_should_classify_segments_from_pipeline_parser() -> None:
|
|
117
|
+
all_pairs = pipeline_segments_for_command("uv run pytest tests | tee out.log")
|
|
118
|
+
assert len(all_pairs) >= 2
|
|
119
|
+
assert segment_runs_pytest(all_pairs[0][0]) is True
|
|
120
|
+
assert segment_runs_pytest(all_pairs[1][0]) is False
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def test_should_not_false_classify_option_value_named_pytest() -> None:
|
|
124
|
+
all_tokens = _first_segment_tokens("uv run --with pytest mypy .")
|
|
125
|
+
assert segment_runs_pytest(all_tokens) is False
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def test_should_not_false_classify_script_argument_named_pytest() -> None:
|
|
129
|
+
all_tokens = _first_segment_tokens("python myscript.py -m pytest")
|
|
130
|
+
assert segment_runs_pytest(all_tokens) is False
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
"""Tests for the shared SessionStart context injector."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
import pytest
|
|
7
|
+
|
|
8
|
+
from hooks_constants.session_start_injector import (
|
|
9
|
+
InjectionResult,
|
|
10
|
+
InjectorConfiguration,
|
|
11
|
+
build_additional_context_payload,
|
|
12
|
+
default_injector_configuration,
|
|
13
|
+
inject_session_start_context,
|
|
14
|
+
injector_enabled_in_environment,
|
|
15
|
+
normalize_session_start_source,
|
|
16
|
+
)
|
|
17
|
+
from hooks_constants.session_start_injector_constants import (
|
|
18
|
+
INJECTION_STATUS_DISABLED,
|
|
19
|
+
INJECTION_STATUS_OK,
|
|
20
|
+
INJECTION_STATUS_TIMEOUT,
|
|
21
|
+
INJECTION_STATUS_UNKNOWN_SOURCE,
|
|
22
|
+
SESSION_START_INJECTOR_ENABLED_ENV_VAR,
|
|
23
|
+
SESSION_START_SOURCE_CLEAR,
|
|
24
|
+
SESSION_START_SOURCE_COMPACT,
|
|
25
|
+
SESSION_START_SOURCE_RESUME,
|
|
26
|
+
SESSION_START_SOURCE_STARTUP,
|
|
27
|
+
SESSION_START_SOURCE_UNKNOWN,
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def _configuration(
|
|
32
|
+
*,
|
|
33
|
+
is_enabled: bool = True,
|
|
34
|
+
timeout_milliseconds: int = 50,
|
|
35
|
+
context_by_source: dict[str, str] | None = None,
|
|
36
|
+
default_context_for_unknown: str = "",
|
|
37
|
+
) -> InjectorConfiguration:
|
|
38
|
+
return InjectorConfiguration(
|
|
39
|
+
is_enabled=is_enabled,
|
|
40
|
+
timeout_milliseconds=timeout_milliseconds,
|
|
41
|
+
context_by_source=context_by_source
|
|
42
|
+
or {
|
|
43
|
+
SESSION_START_SOURCE_STARTUP: "startup-context",
|
|
44
|
+
SESSION_START_SOURCE_RESUME: "resume-context",
|
|
45
|
+
SESSION_START_SOURCE_CLEAR: "clear-context",
|
|
46
|
+
SESSION_START_SOURCE_COMPACT: "compact-context",
|
|
47
|
+
},
|
|
48
|
+
default_context_for_unknown=default_context_for_unknown,
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def _fixed_clock(all_readings: list[float]) -> object:
|
|
53
|
+
index = {"value": 0}
|
|
54
|
+
|
|
55
|
+
def _read() -> float:
|
|
56
|
+
reading = all_readings[min(index["value"], len(all_readings) - 1)]
|
|
57
|
+
index["value"] += 1
|
|
58
|
+
return reading
|
|
59
|
+
|
|
60
|
+
return _read
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
@pytest.mark.parametrize(
|
|
64
|
+
("source", "expected_context"),
|
|
65
|
+
[
|
|
66
|
+
(SESSION_START_SOURCE_STARTUP, "startup-context"),
|
|
67
|
+
(SESSION_START_SOURCE_RESUME, "resume-context"),
|
|
68
|
+
(SESSION_START_SOURCE_CLEAR, "clear-context"),
|
|
69
|
+
(SESSION_START_SOURCE_COMPACT, "compact-context"),
|
|
70
|
+
],
|
|
71
|
+
)
|
|
72
|
+
def test_inject_known_source_returns_ok_with_context(
|
|
73
|
+
source: str, expected_context: str
|
|
74
|
+
) -> None:
|
|
75
|
+
result = inject_session_start_context(
|
|
76
|
+
{"source": source},
|
|
77
|
+
_configuration(),
|
|
78
|
+
clock=_fixed_clock([1.0, 1.002]),
|
|
79
|
+
)
|
|
80
|
+
assert result.status == INJECTION_STATUS_OK
|
|
81
|
+
assert result.source == source
|
|
82
|
+
assert result.additional_context == expected_context
|
|
83
|
+
assert result.is_context_injected is True
|
|
84
|
+
assert result.latency_milliseconds == pytest.approx(2.0)
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def test_inject_disabled_returns_quickly_without_context() -> None:
|
|
88
|
+
result = inject_session_start_context(
|
|
89
|
+
{"source": SESSION_START_SOURCE_STARTUP},
|
|
90
|
+
_configuration(is_enabled=False),
|
|
91
|
+
clock=_fixed_clock([0.0, 0.0001]),
|
|
92
|
+
)
|
|
93
|
+
assert result.status == INJECTION_STATUS_DISABLED
|
|
94
|
+
assert result.additional_context == ""
|
|
95
|
+
assert result.is_context_injected is False
|
|
96
|
+
assert result.latency_milliseconds < 1.0
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def test_inject_timeout_budget_zero_returns_timeout() -> None:
|
|
100
|
+
result = inject_session_start_context(
|
|
101
|
+
{"source": SESSION_START_SOURCE_RESUME},
|
|
102
|
+
_configuration(timeout_milliseconds=0),
|
|
103
|
+
clock=_fixed_clock([0.0, 0.0]),
|
|
104
|
+
)
|
|
105
|
+
assert result.status == INJECTION_STATUS_TIMEOUT
|
|
106
|
+
assert result.is_context_injected is False
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def test_inject_unknown_source_is_explicit() -> None:
|
|
110
|
+
result = inject_session_start_context(
|
|
111
|
+
{"source": "not-a-real-source"},
|
|
112
|
+
_configuration(default_context_for_unknown="fallback"),
|
|
113
|
+
clock=_fixed_clock([0.0, 0.001]),
|
|
114
|
+
)
|
|
115
|
+
assert result.status == INJECTION_STATUS_UNKNOWN_SOURCE
|
|
116
|
+
assert result.source == SESSION_START_SOURCE_UNKNOWN
|
|
117
|
+
assert result.additional_context == "fallback"
|
|
118
|
+
assert result.is_context_injected is True
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def test_normalize_session_start_source_handles_missing_and_case() -> None:
|
|
122
|
+
assert normalize_session_start_source({}) == SESSION_START_SOURCE_UNKNOWN
|
|
123
|
+
assert (
|
|
124
|
+
normalize_session_start_source({"source": " STARTUP "})
|
|
125
|
+
== SESSION_START_SOURCE_STARTUP
|
|
126
|
+
)
|
|
127
|
+
assert normalize_session_start_source({"source": 12}) == SESSION_START_SOURCE_UNKNOWN
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def test_build_additional_context_payload_only_when_injected() -> None:
|
|
131
|
+
injected = InjectionResult(
|
|
132
|
+
source=SESSION_START_SOURCE_STARTUP,
|
|
133
|
+
status=INJECTION_STATUS_OK,
|
|
134
|
+
additional_context="hello",
|
|
135
|
+
latency_milliseconds=1.0,
|
|
136
|
+
is_context_injected=True,
|
|
137
|
+
)
|
|
138
|
+
empty = InjectionResult(
|
|
139
|
+
source=SESSION_START_SOURCE_STARTUP,
|
|
140
|
+
status=INJECTION_STATUS_DISABLED,
|
|
141
|
+
additional_context="",
|
|
142
|
+
latency_milliseconds=0.1,
|
|
143
|
+
is_context_injected=False,
|
|
144
|
+
)
|
|
145
|
+
assert build_additional_context_payload(injected) == {
|
|
146
|
+
"additionalContext": "hello"
|
|
147
|
+
}
|
|
148
|
+
assert build_additional_context_payload(empty) == {}
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
def test_injector_enabled_in_environment_default_on(
|
|
152
|
+
monkeypatch: pytest.MonkeyPatch,
|
|
153
|
+
) -> None:
|
|
154
|
+
monkeypatch.delenv(SESSION_START_INJECTOR_ENABLED_ENV_VAR, raising=False)
|
|
155
|
+
assert injector_enabled_in_environment() is True
|
|
156
|
+
monkeypatch.setenv(SESSION_START_INJECTOR_ENABLED_ENV_VAR, "0")
|
|
157
|
+
assert injector_enabled_in_environment() is False
|
|
158
|
+
monkeypatch.setenv(SESSION_START_INJECTOR_ENABLED_ENV_VAR, "true")
|
|
159
|
+
assert injector_enabled_in_environment() is True
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def test_default_injector_configuration_reads_env(
|
|
163
|
+
monkeypatch: pytest.MonkeyPatch,
|
|
164
|
+
) -> None:
|
|
165
|
+
monkeypatch.setenv(SESSION_START_INJECTOR_ENABLED_ENV_VAR, "off")
|
|
166
|
+
configuration = default_injector_configuration()
|
|
167
|
+
assert configuration.is_enabled is False
|
|
168
|
+
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
"""Real command fixtures for the quote-aware shell pipeline parser.
|
|
2
|
+
|
|
3
|
+
Covers quotes, operators, heredocs, continuations, groups, wrappers, and
|
|
4
|
+
compound shells. No pytest-specific classification runs here.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import pathlib
|
|
10
|
+
import sys
|
|
11
|
+
|
|
12
|
+
_HOOKS_ROOT = pathlib.Path(__file__).resolve().parent.parent
|
|
13
|
+
if str(_HOOKS_ROOT) not in sys.path:
|
|
14
|
+
sys.path.insert(0, str(_HOOKS_ROOT))
|
|
15
|
+
|
|
16
|
+
from hooks_constants.shell_command_pipeline import ( # noqa: E402
|
|
17
|
+
all_operator_aware_tokenizations,
|
|
18
|
+
join_line_continuations,
|
|
19
|
+
live_command_lines,
|
|
20
|
+
pipeline_segments_for_command,
|
|
21
|
+
scannable_command_lines,
|
|
22
|
+
segments_with_following_operator,
|
|
23
|
+
)
|
|
24
|
+
from hooks_constants.shell_command_segments import ( # noqa: E402
|
|
25
|
+
split_into_segments,
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def test_pipe_segments_pair_with_following_operator() -> None:
|
|
30
|
+
all_tokens = ["pytest", "tests", "|", "tee", "out.log"]
|
|
31
|
+
all_pairs = segments_with_following_operator(all_tokens)
|
|
32
|
+
assert all_pairs[0] == (["pytest", "tests"], "|")
|
|
33
|
+
assert all_pairs[1] == (["tee", "out.log"], "")
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def test_quoted_pipe_inside_k_expression_stays_in_one_segment() -> None:
|
|
37
|
+
"""Quote-aware tokenization keeps pytest -k \"a|b\" as one segment."""
|
|
38
|
+
command = 'pytest -k "a|b" tests'
|
|
39
|
+
all_tokenizations = all_operator_aware_tokenizations(command)
|
|
40
|
+
assert all_tokenizations
|
|
41
|
+
all_pairs = segments_with_following_operator(all_tokenizations[0])
|
|
42
|
+
assert len(all_pairs) == 1
|
|
43
|
+
segment_tokens, following_operator = all_pairs[0]
|
|
44
|
+
assert following_operator == ""
|
|
45
|
+
assert any("a|b" in each_token or each_token == "a|b" for each_token in segment_tokens)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def test_quote_unaware_segments_split_on_pipe_inside_quotes() -> None:
|
|
49
|
+
"""Divergence: shell_command_segments peels | inside a|b; quote-aware path does not."""
|
|
50
|
+
all_segments = split_into_segments(["pytest", "-k", "a|b", "tests"])
|
|
51
|
+
assert len(all_segments) > 1
|
|
52
|
+
quote_aware_pairs = pipeline_segments_for_command('pytest -k "a|b" tests')
|
|
53
|
+
assert len(quote_aware_pairs) >= 1
|
|
54
|
+
assert all(each_operator != "|" for _, each_operator in quote_aware_pairs)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def test_heredoc_body_is_dropped_from_live_lines() -> None:
|
|
58
|
+
all_lines = [
|
|
59
|
+
"cat > run.sh <<'EOF'",
|
|
60
|
+
"pytest tests | tee out.log",
|
|
61
|
+
"EOF",
|
|
62
|
+
"echo done",
|
|
63
|
+
]
|
|
64
|
+
assert live_command_lines(all_lines) == ["cat > run.sh <<'EOF'", "echo done"]
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def test_scannable_lines_drop_heredoc_body_and_keep_later_command() -> None:
|
|
68
|
+
command = "cat > run.sh <<'EOF'\npytest tests | tee out.log\nEOF\necho done"
|
|
69
|
+
all_lines = scannable_command_lines(join_line_continuations(command))
|
|
70
|
+
assert "pytest tests | tee out.log" not in all_lines
|
|
71
|
+
assert any("echo done" in each_line for each_line in all_lines)
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def test_line_continuation_joins_pipe_onto_one_line() -> None:
|
|
75
|
+
command = "echo hi \\\n| cat"
|
|
76
|
+
joined = join_line_continuations(command)
|
|
77
|
+
assert "\n" not in joined
|
|
78
|
+
assert "| cat" in joined
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def test_comment_does_not_hide_following_line() -> None:
|
|
82
|
+
command = "echo hi # note\necho bye"
|
|
83
|
+
all_lines = scannable_command_lines(command)
|
|
84
|
+
assert any("echo bye" in each_line for each_line in all_lines)
|
|
85
|
+
assert all("# note" not in each_line for each_line in all_lines)
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def test_paren_group_joins_multiline_subshell_before_pipe() -> None:
|
|
89
|
+
command = "(\necho hi\n) | cat"
|
|
90
|
+
all_lines = scannable_command_lines(command)
|
|
91
|
+
assert len(all_lines) == 1
|
|
92
|
+
assert "|" in all_lines[0]
|
|
93
|
+
assert "echo hi" in all_lines[0]
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def test_glued_close_paren_and_pipe_split_into_operator_token() -> None:
|
|
97
|
+
all_pairs = segments_with_following_operator(["(", "echo", "hi", ")|", "cat"])
|
|
98
|
+
assert any(each_operator == "|" for _, each_operator in all_pairs)
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def test_and_operator_resets_segment() -> None:
|
|
102
|
+
all_pairs = segments_with_following_operator(
|
|
103
|
+
["echo", "a", "&&", "echo", "b"]
|
|
104
|
+
)
|
|
105
|
+
assert all_pairs[0] == (["echo", "a"], "&&")
|
|
106
|
+
assert all_pairs[1][0] == ["echo", "b"]
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def test_pipeline_segments_for_command_on_simple_pipe() -> None:
|
|
110
|
+
all_pairs = pipeline_segments_for_command("echo hi | cat")
|
|
111
|
+
leading_programs = [
|
|
112
|
+
each_segment[0] for each_segment, _ in all_pairs if each_segment
|
|
113
|
+
]
|
|
114
|
+
assert "echo" in leading_programs
|
|
115
|
+
assert "cat" in leading_programs
|
|
116
|
+
assert any(each_operator == "|" for _, each_operator in all_pairs)
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def test_wrapper_tokens_remain_visible_in_segment() -> None:
|
|
120
|
+
"""Wrappers stay in the segment; classification is a later slice."""
|
|
121
|
+
all_pairs = pipeline_segments_for_command("sudo -n echo hi | cat")
|
|
122
|
+
first_pipe_segment = next(
|
|
123
|
+
each_segment for each_segment, each_operator in all_pairs if each_operator == "|"
|
|
124
|
+
)
|
|
125
|
+
assert first_pipe_segment[0] in {"sudo", "sudo.exe"} or "sudo" in first_pipe_segment[0]
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def test_compound_shell_if_fi_tokens_survive_tokenization() -> None:
|
|
129
|
+
command = "if true; then echo hi; fi"
|
|
130
|
+
all_tokenizations = all_operator_aware_tokenizations(command)
|
|
131
|
+
assert all_tokenizations
|
|
132
|
+
flat_tokens = all_tokenizations[0]
|
|
133
|
+
assert "if" in flat_tokens
|
|
134
|
+
assert "then" in flat_tokens
|
|
135
|
+
assert "fi" in flat_tokens
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"""Working-style prompt text for the SessionStart working_style_prompt hook."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
__all__ = [
|
|
6
|
+
"WORKING_STYLE_PROMPT",
|
|
7
|
+
]
|
|
8
|
+
|
|
9
|
+
WORKING_STYLE_PROMPT = (
|
|
10
|
+
"document everything in a location you can find later, rather than relying on "
|
|
11
|
+
"memory. scratch txt file you'll keep running as you go; ledger, if you will."
|
|
12
|
+
"Always, without exception, write in plain english. Keep responses focused, "
|
|
13
|
+
"brief, and concise. Keep disclaimers and caveats short, and spend most of the "
|
|
14
|
+
"response on the main answer. When asked to explain something, give a high-level "
|
|
15
|
+
"summary unless an in-depth explanation is specifically requested."
|
|
16
|
+
"Before your first tool call, say in one sentence what you're about to do. While "
|
|
17
|
+
"working, give a brief update only when you find something important or change "
|
|
18
|
+
"direction. When you finish, lead with the outcome: your first sentence should "
|
|
19
|
+
"answer \"what happened\" or \"what did you find,\" with supporting detail after "
|
|
20
|
+
"it for readers who want it."
|
|
21
|
+
"Match the length of written documents to what the task needs: cover the "
|
|
22
|
+
"substance, but do not pad with filler sections, redundant summaries, or "
|
|
23
|
+
"boilerplate."
|
|
24
|
+
"Deliver what was asked, at the scope intended. Make routine judgment calls "
|
|
25
|
+
"yourself, and check in only when different readings of the request would lead "
|
|
26
|
+
"to materially different work. If the request seems mistaken or a better "
|
|
27
|
+
"approach exists, say so in a sentence and continue with the task as asked "
|
|
28
|
+
"rather than quietly narrowing, widening, or transforming it. Finish the whole "
|
|
29
|
+
"task, and stop short of actions that are clearly beyond what was asked."
|
|
30
|
+
)
|
|
@@ -8,8 +8,10 @@ PostToolUse hooks that record agent behavior for later review. These hooks do no
|
|
|
8
8
|
|---|---|---|
|
|
9
9
|
| `instructions_loaded_logger.py` | PostToolUse (file load events) | Appends a JSONL record to `~/.claude/logs/instructions_loaded.jsonl` each time Claude Code loads a context file (CLAUDE.md, rules, skills), capturing the file path, load reason, memory type, and session ID |
|
|
10
10
|
| `session_file_edit_tracker.py` | PostToolUse (Write/Edit/MultiEdit) | Records the resolved absolute path of each file edited this session into a per-session JSON file under the system temp directory, for the session edit stage gate to read at commit time |
|
|
11
|
+
| `prose_matcher_advisory.py` | Advisory emit from prose matchers | Privacy-safe JSONL candidates (`matcher_id`, surface, context fingerprint, label) plus keep/narrow/drop/advisory classification against the sample floor |
|
|
11
12
|
| `test_instructions_loaded_logger.py` | — | Tests for `instructions_loaded_logger.py` |
|
|
12
13
|
| `test_session_file_edit_tracker.py` | — | Tests for `session_file_edit_tracker.py` |
|
|
14
|
+
| `test_prose_matcher_advisory.py` | — | Tests for advisory emit and precision classification |
|
|
13
15
|
|
|
14
16
|
## Conventions
|
|
15
17
|
|