claude-dev-env 1.17.2 → 1.19.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 (32) hide show
  1. package/bin/install.mjs +145 -63
  2. package/hooks/blocking/content-search-to-zoekt-redirector.py +55 -0
  3. package/hooks/blocking/content_search_zoekt_bash_block_reason.py +25 -0
  4. package/hooks/blocking/content_search_zoekt_block_payload.py +17 -0
  5. package/hooks/blocking/content_search_zoekt_indexed_paths.py +24 -0
  6. package/hooks/blocking/content_search_zoekt_indexed_roots_config.py +131 -0
  7. package/hooks/blocking/content_search_zoekt_redirect_guidance.py +19 -0
  8. package/hooks/blocking/destructive-command-blocker.py +53 -4
  9. package/hooks/blocking/test_content_search_to_zoekt_redirector_integration.py +54 -0
  10. package/hooks/blocking/test_content_search_to_zoekt_redirector_unit.py +51 -0
  11. package/hooks/blocking/test_content_search_zoekt_indexed_roots_config.py +102 -0
  12. package/hooks/blocking/test_destructive_command_blocker.py +108 -0
  13. package/package.json +4 -1
  14. package/skills/rule-audit/SKILL.md +2 -2
  15. package/hooks/HOOK_SPECS_PROMPT_WORKFLOW.md +0 -64
  16. package/hooks/blocking/prompt_workflow_clipboard.py +0 -63
  17. package/hooks/blocking/prompt_workflow_gate_config.py +0 -113
  18. package/hooks/blocking/prompt_workflow_gate_core.py +0 -289
  19. package/hooks/blocking/prompt_workflow_validate.py +0 -218
  20. package/hooks/blocking/test_prompt_workflow_clipboard.py +0 -54
  21. package/hooks/blocking/test_prompt_workflow_gate_core.py +0 -195
  22. package/hooks/blocking/test_prompt_workflow_validate.py +0 -339
  23. package/rules/prompt-workflow-context-controls.md +0 -48
  24. package/skills/agent-prompt/SKILL.md +0 -199
  25. package/skills/prompt-generator/ARCHITECTURE.md +0 -18
  26. package/skills/prompt-generator/REFERENCE.md +0 -254
  27. package/skills/prompt-generator/REFINEMENT_PIPELINE_RUNBOOK.md +0 -177
  28. package/skills/prompt-generator/SKILL.md +0 -354
  29. package/skills/prompt-generator/TARGET_OUTPUT.md +0 -133
  30. package/skills/prompt-generator/evals/prompt-generator.json +0 -207
  31. package/skills/prompt-generator/templates/skill-from-ground-up.md +0 -104
  32. package/skills/prompt-generator/templates/skill-refinement-package.md +0 -109
@@ -0,0 +1,54 @@
1
+ """Subprocess integration tests for content-search-to-zoekt-redirector PreToolUse hook."""
2
+
3
+ import json
4
+ import pathlib
5
+ import subprocess
6
+ import sys
7
+ import unittest
8
+ from typing import Any
9
+
10
+
11
+ class ContentSearchHookIntegrationTests(unittest.TestCase):
12
+ def test_bash_grep_command_emits_stdout_json_deny(self) -> None:
13
+ hook_directory = pathlib.Path(__file__).resolve().parent
14
+ if str(hook_directory) not in sys.path:
15
+ sys.path.insert(0, str(hook_directory))
16
+ from content_search_zoekt_redirect_guidance import get_zoekt_redirect_guidance
17
+
18
+ hook_path = hook_directory / "content-search-to-zoekt-redirector.py"
19
+ destructive_gate_label_prefix = "[destructive-gate]"
20
+ destructive_gate_label_prefix_value = f"{destructive_gate_label_prefix} "
21
+ expected_decision = "deny"
22
+ hook_stdin_payload = json.dumps(
23
+ {"tool_name": "Bash", "tool_input": {"command": "grep foo bar"}},
24
+ )
25
+
26
+ completed = subprocess.run(
27
+ [sys.executable, str(hook_path)],
28
+ input=hook_stdin_payload,
29
+ capture_output=True,
30
+ text=True,
31
+ cwd=str(hook_directory),
32
+ )
33
+ self.assertEqual(completed.returncode, 0)
34
+ self.assertEqual(completed.stderr, "")
35
+ payload: dict[str, Any] = json.loads(completed.stdout)
36
+ self.assertTrue(
37
+ payload["systemMessage"].startswith(destructive_gate_label_prefix_value),
38
+ )
39
+ self.assertEqual(
40
+ payload["hookSpecificOutput"]["permissionDecisionReason"],
41
+ get_zoekt_redirect_guidance(),
42
+ )
43
+ self.assertEqual(
44
+ payload["hookSpecificOutput"]["permissionDecision"],
45
+ expected_decision,
46
+ )
47
+ self.assertEqual(
48
+ payload["hookSpecificOutput"]["hookEventName"],
49
+ "PreToolUse",
50
+ )
51
+
52
+
53
+ if __name__ == "__main__":
54
+ unittest.main()
@@ -0,0 +1,51 @@
1
+ """Unit tests for Zoekt redirector PreToolUse deny payload (build_block_payload)."""
2
+
3
+ import json
4
+ import pathlib
5
+ import sys
6
+ import unittest
7
+ from typing import Any
8
+
9
+ HOOK_DIRECTORY = pathlib.Path(__file__).resolve().parent
10
+ if str(HOOK_DIRECTORY) not in sys.path:
11
+ sys.path.insert(0, str(HOOK_DIRECTORY))
12
+
13
+ from content_search_zoekt_block_payload import build_block_payload
14
+ from content_search_zoekt_redirect_guidance import get_zoekt_redirect_guidance
15
+
16
+
17
+ class BuildBlockPayloadTests(unittest.TestCase):
18
+ def test_payload_matches_pretooluse_contract(self) -> None:
19
+ destructive_gate_label_prefix = "[destructive-gate]"
20
+ payload: dict[str, Any] = build_block_payload("demo", "body")
21
+ prefix_with_space = f"{destructive_gate_label_prefix} "
22
+ self.assertTrue(payload["systemMessage"].startswith(prefix_with_space))
23
+ self.assertEqual(
24
+ payload["hookSpecificOutput"]["permissionDecisionReason"],
25
+ "body",
26
+ )
27
+ self.assertEqual(payload["hookSpecificOutput"]["permissionDecision"], "deny")
28
+ self.assertEqual(
29
+ payload["hookSpecificOutput"]["hookEventName"],
30
+ "PreToolUse",
31
+ )
32
+ self.assertEqual(payload["suppressOutput"], True)
33
+ self.assertNotIn("decision", payload)
34
+ self.assertNotIn("reason", payload)
35
+
36
+ def test_serialized_payload_under_documented_context_cap(self) -> None:
37
+ cap_characters = 10_000
38
+ payload = build_block_payload(
39
+ brief_label="blocked Bash(grep); use Zoekt MCP",
40
+ permission_decision_reason=get_zoekt_redirect_guidance(),
41
+ )
42
+ serialized = json.dumps(payload)
43
+ self.assertLessEqual(
44
+ len(serialized),
45
+ cap_characters,
46
+ msg="Hooks doc caps additionalContext/systemMessage/plain stdout injection at 10,000 characters",
47
+ )
48
+
49
+
50
+ if __name__ == "__main__":
51
+ unittest.main()
@@ -0,0 +1,102 @@
1
+ """Tests for Zoekt indexed root resolution (env, file, empty built-in fallback, WSL variants)."""
2
+
3
+ import json
4
+ import os
5
+ import pathlib
6
+ import sys
7
+ import tempfile
8
+ import unittest
9
+ from unittest.mock import patch
10
+
11
+ HOOK_DIRECTORY = pathlib.Path(__file__).resolve().parent
12
+ if str(HOOK_DIRECTORY) not in sys.path:
13
+ sys.path.insert(0, str(HOOK_DIRECTORY))
14
+
15
+ from content_search_zoekt_indexed_paths import is_in_indexed_repo
16
+ from content_search_zoekt_indexed_roots_config import (
17
+ clear_indexed_root_prefixes_cache,
18
+ indexed_root_prefixes,
19
+ )
20
+
21
+
22
+ class IndexedRootsConfigTests(unittest.TestCase):
23
+ def tearDown(self) -> None:
24
+ clear_indexed_root_prefixes_cache()
25
+
26
+ def test_environment_json_array_defines_prefixes(self) -> None:
27
+ roots_json = json.dumps(["Y:/OnlyOne/Indexed/"])
28
+ with patch.dict(os.environ, {"ZOEKT_REDIRECT_INDEXED_ROOTS": roots_json}, clear=False):
29
+ clear_indexed_root_prefixes_cache()
30
+ prefixes = indexed_root_prefixes()
31
+ self.assertIn("y:/onlyone/indexed/", prefixes)
32
+ self.assertIn("/mnt/y/onlyone/indexed/", prefixes)
33
+
34
+ def test_empty_environment_array_yields_no_prefixes(self) -> None:
35
+ with patch.dict(os.environ, {"ZOEKT_REDIRECT_INDEXED_ROOTS": "[]"}, clear=False):
36
+ clear_indexed_root_prefixes_cache()
37
+ prefixes = indexed_root_prefixes()
38
+ self.assertEqual(prefixes, ())
39
+
40
+ def test_json_file_used_when_env_missing(self) -> None:
41
+ with tempfile.TemporaryDirectory() as tmp_str:
42
+ home = pathlib.Path(tmp_str)
43
+ config_dir = home / ".claude"
44
+ config_dir.mkdir(parents=True)
45
+ roots_payload = {"roots": ["Y:/FromFile/Project/"]}
46
+ (config_dir / "zoekt-indexed-roots.json").write_text(
47
+ json.dumps(roots_payload),
48
+ encoding="utf-8",
49
+ )
50
+ with patch("pathlib.Path.home", return_value=home):
51
+ saved = os.environ.pop("ZOEKT_REDIRECT_INDEXED_ROOTS", None)
52
+ try:
53
+ clear_indexed_root_prefixes_cache()
54
+ prefixes = indexed_root_prefixes()
55
+ finally:
56
+ if saved is not None:
57
+ os.environ["ZOEKT_REDIRECT_INDEXED_ROOTS"] = saved
58
+ self.assertIn("y:/fromfile/project/", prefixes)
59
+
60
+ def test_environment_overrides_file(self) -> None:
61
+ with tempfile.TemporaryDirectory() as tmp_str:
62
+ home = pathlib.Path(tmp_str)
63
+ config_dir = home / ".claude"
64
+ config_dir.mkdir(parents=True)
65
+ (config_dir / "zoekt-indexed-roots.json").write_text(
66
+ json.dumps({"roots": ["Y:/FromFile/"]}),
67
+ encoding="utf-8",
68
+ )
69
+ with patch("pathlib.Path.home", return_value=home):
70
+ with patch.dict(
71
+ os.environ,
72
+ {"ZOEKT_REDIRECT_INDEXED_ROOTS": json.dumps(["Y:/FromEnv/"])},
73
+ clear=False,
74
+ ):
75
+ clear_indexed_root_prefixes_cache()
76
+ prefixes = indexed_root_prefixes()
77
+ self.assertIn("y:/fromenv/", prefixes)
78
+ self.assertNotIn("y:/fromfile/", prefixes)
79
+
80
+ def test_longer_prefix_matches_before_shorter_parent(self) -> None:
81
+ roots_json = json.dumps(["Y:/parent/", "Y:/parent/child/"])
82
+ with patch.dict(os.environ, {"ZOEKT_REDIRECT_INDEXED_ROOTS": roots_json}, clear=False):
83
+ clear_indexed_root_prefixes_cache()
84
+ self.assertTrue(is_in_indexed_repo("Y:/parent/child/file.py"))
85
+
86
+ def test_invalid_environment_json_falls_through_to_empty_builtin(self) -> None:
87
+ with patch(
88
+ "content_search_zoekt_indexed_roots_config._roots_from_json_file",
89
+ return_value=None,
90
+ ):
91
+ with patch.dict(
92
+ os.environ,
93
+ {"ZOEKT_REDIRECT_INDEXED_ROOTS": "not-json"},
94
+ clear=False,
95
+ ):
96
+ clear_indexed_root_prefixes_cache()
97
+ prefixes = indexed_root_prefixes()
98
+ self.assertEqual(prefixes, ())
99
+
100
+
101
+ if __name__ == "__main__":
102
+ unittest.main()
@@ -0,0 +1,108 @@
1
+ """Tests for destructive-command-blocker hook."""
2
+
3
+ import json
4
+ import subprocess
5
+ import sys
6
+ from pathlib import Path
7
+
8
+
9
+ SCRIPT_PATH = Path(__file__).parent / "destructive-command-blocker.py"
10
+ GH_GATE_USER_FACING_PREFIX = "[gh-gate]"
11
+
12
+
13
+ def _run_hook(payload: dict) -> subprocess.CompletedProcess[str]:
14
+ return subprocess.run(
15
+ [sys.executable, str(SCRIPT_PATH)],
16
+ input=json.dumps(payload),
17
+ text=True,
18
+ capture_output=True,
19
+ check=False,
20
+ )
21
+
22
+
23
+ def _make_bash_payload(command: str) -> dict:
24
+ return {
25
+ "tool_name": "Bash",
26
+ "tool_input": {"command": command},
27
+ }
28
+
29
+
30
+ def test_denies_gh_issue_comment_as_redirect_duplicate_guard() -> None:
31
+ payload = _make_bash_payload(
32
+ 'gh issue comment 83 --repo jl-cmd/claude-code-config --body "hello"'
33
+ )
34
+ result = _run_hook(payload)
35
+ response = json.loads(result.stdout)
36
+ assert response["hookSpecificOutput"]["permissionDecision"] == "deny"
37
+ assert (
38
+ "gh issue comment" in response["hookSpecificOutput"]["permissionDecisionReason"]
39
+ )
40
+ assert (
41
+ "duplicate execution"
42
+ in response["hookSpecificOutput"]["permissionDecisionReason"]
43
+ )
44
+
45
+
46
+ def test_denies_gh_pr_comment_as_redirect_duplicate_guard() -> None:
47
+ payload = _make_bash_payload('gh pr comment 42 --body "ok"')
48
+ result = _run_hook(payload)
49
+ response = json.loads(result.stdout)
50
+ assert response["hookSpecificOutput"]["permissionDecision"] == "deny"
51
+ assert "gh pr comment" in response["hookSpecificOutput"]["permissionDecisionReason"]
52
+
53
+
54
+ def test_denies_gh_pr_review_as_redirect_duplicate_guard() -> None:
55
+ payload = _make_bash_payload("gh pr review 42 --approve")
56
+ result = _run_hook(payload)
57
+ response = json.loads(result.stdout)
58
+ assert response["hookSpecificOutput"]["permissionDecision"] == "deny"
59
+ assert "gh pr review" in response["hookSpecificOutput"]["permissionDecisionReason"]
60
+
61
+
62
+ def test_denies_gh_api_post_comment_as_redirect_duplicate_guard() -> None:
63
+ payload = _make_bash_payload(
64
+ "gh api /repos/owner/name/issues/1/comments -X POST -f body=hello"
65
+ )
66
+ result = _run_hook(payload)
67
+ response = json.loads(result.stdout)
68
+ assert response["hookSpecificOutput"]["permissionDecision"] == "deny"
69
+
70
+
71
+ def test_suppresses_output_on_gh_redirect_deny() -> None:
72
+ payload = _make_bash_payload('gh issue comment 1 --body "x"')
73
+ result = _run_hook(payload)
74
+ response = json.loads(result.stdout)
75
+ assert response["suppressOutput"] is True
76
+ assert response["systemMessage"].startswith(GH_GATE_USER_FACING_PREFIX)
77
+
78
+
79
+ def test_asks_on_rm_rf_still_works() -> None:
80
+ payload = _make_bash_payload("rm -rf /tmp/somewhere")
81
+ result = _run_hook(payload)
82
+ response = json.loads(result.stdout)
83
+ assert response["hookSpecificOutput"]["permissionDecision"] == "ask"
84
+ assert "rm -rf" in response["hookSpecificOutput"]["permissionDecisionReason"]
85
+
86
+
87
+ def test_asks_on_git_push_force_still_works() -> None:
88
+ payload = _make_bash_payload("git push --force origin main")
89
+ result = _run_hook(payload)
90
+ response = json.loads(result.stdout)
91
+ assert response["hookSpecificOutput"]["permissionDecision"] == "ask"
92
+ assert (
93
+ "git push --force" in response["hookSpecificOutput"]["permissionDecisionReason"]
94
+ )
95
+
96
+
97
+ def test_allows_plain_command_without_destructive_pattern() -> None:
98
+ payload = _make_bash_payload("ls -la")
99
+ result = _run_hook(payload)
100
+ assert result.stdout.strip() == ""
101
+ assert result.returncode == 0
102
+
103
+
104
+ def test_ignores_non_bash_tool() -> None:
105
+ payload = {"tool_name": "Read", "tool_input": {"file_path": "/tmp/x"}}
106
+ result = _run_hook(payload)
107
+ assert result.stdout.strip() == ""
108
+ assert result.returncode == 0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-dev-env",
3
- "version": "1.17.2",
3
+ "version": "1.19.0",
4
4
  "description": "Claude Code development standards — rules, hooks, agents, commands, and skills",
5
5
  "type": "module",
6
6
  "bin": {
@@ -22,6 +22,9 @@
22
22
  "tdd",
23
23
  "code-quality"
24
24
  ],
25
+ "dependencies": {
26
+ "@jl-cmd/prompt-generator": "^1.0.0"
27
+ },
25
28
  "license": "MIT",
26
29
  "repository": {
27
30
  "type": "git",
@@ -89,8 +89,8 @@ For EACH hook entry in settings.json (both layers):
89
89
  - hook_script_path (extract from the command string after the last quote)
90
90
  - Read the actual script file
91
91
  - purpose (what rule does this hook enforce?)
92
- - enforcement_type: "blocking" (exit 2 / permissionDecision deny) | "advisory" (stdout message) | "validation" (post-check)
93
- - method: "exit_code_2" (deprecated) | "permissionDecision" (current) | "stdout" | "other"
92
+ - enforcement_type: "blocking" (exit 2 stderr, or PreToolUse exit 0 + JSON deny) | "advisory" (stdout message) | "validation" (post-check)
93
+ - method: "exit_code_2_stderr" | "pretooluse_json_stdout" (hookSpecificOutput.permissionDecision; see https://code.claude.com/docs/en/hooks) | "stdout" | "other"
94
94
  - which_rule_file (which .Codex/rules/*.md or AGENTS.md rule does this correspond to?)
95
95
  - orphaned (hook exists on disk but NOT in settings.json?)
96
96
  ```
@@ -1,64 +0,0 @@
1
- # Prompt Workflow Hook Specs
2
-
3
- Deterministic runtime gates for prompt workflows.
4
-
5
- ## PreToolUse Task/Agent (removed)
6
-
7
- The former `agent-execution-intent-gate.py` hook is **removed**. Native Agent/Task launches do not carry stable custom metadata; enforcing scope text on every spawn blocked legitimate `/agent-prompt` and refinement delegations. Scope and checklist rules remain enforced by the Stop guard when a prompt-workflow response is detected.
8
-
9
- ## Gate: Leakage + Checklist + Scope (file-based validation loop)
10
-
11
- - Validator: `hooks/blocking/prompt_workflow_validate.py`
12
- - Invocation: CLI against `data/prompts/.draft-prompt.xml` (exit 0 allowed, exit 2 blocked)
13
- - Fail conditions:
14
- - Raw internal refinement object appears in assistant output without explicit debug intent
15
- - Prompt-workflow response detected but deterministic checklist container is missing
16
- - Prompt-workflow response detected and required deterministic checklist rows are missing
17
- - Prompt-workflow response detected and required scope anchors are missing
18
- - Prompt-workflow response detected and runtime context-control signals are missing
19
- - Scope-bound text uses banned ambiguous scope terms
20
- - Banned negative keywords found inside fenced XML artifact
21
- - Fenced XML artifact missing required sections
22
- - Enforcement: The drafting subagent writes the draft file, runs the validator, reads stderr violations (each prefixed with `[reason_code]`), edits the file, and re-runs until exit 0.
23
-
24
- ## Required Scope Anchors
25
-
26
- - `target_local_roots`
27
- - `target_canonical_roots`
28
- - `target_file_globs`
29
- - `comparison_basis`
30
- - `completion_boundary`
31
-
32
- ## Required Deterministic Checklist Rows
33
-
34
- - `structured_scoped_instructions`
35
- - `sequential_steps_present`
36
- - `positive_framing`
37
- - `acceptance_criteria_defined`
38
- - `safety_reversibility_language`
39
- - `reversible_action_and_safety_check_guidance`
40
- - `concrete_output_contract`
41
- - `scope_boundary_present`
42
- - `explicit_scope_anchors_present`
43
- - `all_instructions_artifact_bound`
44
- - `scope_terms_explicit_and_anchored`
45
- - `completion_boundary_measurable`
46
- - `citation_grounding_policy_present`
47
- - `source_priority_rules_present`
48
-
49
- ## Runtime Context-Control Signals
50
-
51
- - `base_minimal_instruction_layer: true`
52
- - `on_demand_skill_loading: true`
53
-
54
- These two signals are checked by the validator CLI whenever a prompt-workflow response is detected.
55
-
56
- ## Deterministic Boundary
57
-
58
- These hooks enforce only structural/runtime checks. Semantic quality remains in auditor layer.
59
-
60
- ## Reviewing Flattened Transcript Exports
61
-
62
- - Live prompt-workflow responses still require an explicit `Audit:` line plus one outer `xml` fence. The Stop guard and clipboard path continue to evaluate that literal boundary.
63
- - Saved transcript exports can flatten blocked retry turns and omit the outer fence lines. Normalize those files with `prompt_workflow_gate_core.normalize_prompt_workflow_export(...)`, then evaluate the rebuilt message with `extract_fenced_xml_content(...)` or `extract_fenced_xml_content_from_export(...)`.
64
- - Fence-relative evals review the **last successful Audit + artifact pair** after normalization. Earlier blocked retries in the flattened transcript remain diagnostic evidence and do not count as extra delivered artifacts.
@@ -1,63 +0,0 @@
1
- #!/usr/bin/env python3
2
- """Cross-platform clipboard writes for prompt-workflow XML artifacts."""
3
-
4
- from __future__ import annotations
5
-
6
- import os
7
-
8
-
9
- def _clipboard_disabled_by_env() -> bool:
10
- flag = os.environ.get("PROMPT_WORKFLOW_SKIP_CLIPBOARD", "").strip().lower()
11
- return flag in {"1", "true", "yes", "on"}
12
-
13
-
14
- def copy_text_to_system_clipboard(text: str) -> bool:
15
- """Write ``text`` to the OS clipboard using Python-only backends.
16
-
17
- Tries :mod:`tkinter` (stdlib) first, then optional ``pyperclip`` if installed.
18
- Returns ``True`` when a backend reports success, ``False`` otherwise
19
- (missing dependency, headless display, empty payload, or env opt-out).
20
- """
21
- if not text.strip():
22
- return False
23
- if _clipboard_disabled_by_env():
24
- return False
25
- if _copy_via_tkinter(text):
26
- return True
27
- return _copy_via_pyperclip(text)
28
-
29
-
30
- def _copy_via_tkinter(text: str) -> bool:
31
- try:
32
- import tkinter as tk
33
- except ImportError:
34
- return False
35
- root: tk.Tk | None = None
36
- try:
37
- root = tk.Tk()
38
- root.withdraw()
39
- root.clipboard_clear()
40
- root.clipboard_append(text)
41
- root.update_idletasks()
42
- root.update()
43
- return True
44
- except Exception:
45
- return False
46
- finally:
47
- if root is not None:
48
- try:
49
- root.destroy()
50
- except Exception:
51
- pass
52
-
53
-
54
- def _copy_via_pyperclip(text: str) -> bool:
55
- try:
56
- import pyperclip
57
- except ImportError:
58
- return False
59
- try:
60
- pyperclip.copy(text)
61
- return True
62
- except Exception:
63
- return False
@@ -1,113 +0,0 @@
1
- """Static lists and compiled regexes for prompt-workflow gate checks.
2
-
3
- Edit this file to change scope anchors, checklist rows, markers, or keyword lists
4
- without touching gate logic in prompt_workflow_gate_core.py.
5
- """
6
-
7
- from __future__ import annotations
8
-
9
- import re
10
-
11
- REQUIRED_SCOPE_ANCHORS: tuple[str, ...] = (
12
- "target_local_roots",
13
- "target_canonical_roots",
14
- "target_file_globs",
15
- "comparison_basis",
16
- "completion_boundary",
17
- )
18
-
19
- REQUIRED_CHECKLIST_ROWS: tuple[str, ...] = (
20
- "structured_scoped_instructions",
21
- "sequential_steps_present",
22
- "positive_framing",
23
- "acceptance_criteria_defined",
24
- "safety_reversibility_language",
25
- "reversible_action_and_safety_check_guidance",
26
- "concrete_output_contract",
27
- "scope_boundary_present",
28
- "explicit_scope_anchors_present",
29
- "all_instructions_artifact_bound",
30
- "scope_terms_explicit_and_anchored",
31
- "completion_boundary_measurable",
32
- "citation_grounding_policy_present",
33
- "source_priority_rules_present",
34
- "artifact_language_confidence",
35
- )
36
-
37
- AMBIGUOUS_SCOPE_TERMS: tuple[str, ...] = (
38
- "this session",
39
- "current files",
40
- "here",
41
- "above",
42
- "as needed",
43
- )
44
-
45
- INTERNAL_OBJECT_MARKERS: tuple[str, ...] = (
46
- '"pipeline_mode": "internal_section_refinement_with_final_audit"',
47
- '"scope_block": {',
48
- '"required_sections": [',
49
- '"section_output_contract": {',
50
- '"merge_output_contract": {',
51
- '"audit_output_contract": {',
52
- )
53
-
54
- PROMPT_WORKFLOW_RESPONSE_MARKERS: tuple[str, ...] = (
55
- "checklist_results",
56
- "overall_status",
57
- "scope anchors",
58
- "target_local_roots",
59
- "target_canonical_roots",
60
- "target_file_globs",
61
- "comparison_basis",
62
- "completion_boundary",
63
- )
64
-
65
- DEBUG_INTENT_MARKERS: tuple[str, ...] = (
66
- "debug",
67
- "show internal",
68
- "raw internal object",
69
- "pipeline object",
70
- )
71
-
72
- NEGATIVE_KEYWORDS_IN_ARTIFACT: tuple[str, ...] = (
73
- "no",
74
- "not",
75
- "don't",
76
- "do not",
77
- "never",
78
- "avoid",
79
- "without",
80
- "refrain",
81
- "stop",
82
- "prevent",
83
- "exclude",
84
- "prohibit",
85
- "forbid",
86
- "reject",
87
- "cannot",
88
- "unless",
89
- )
90
-
91
- NEGATIVE_INDIRECT_PATTERNS_IN_ARTIFACT: tuple[str, ...] = (
92
- r"instead of\s+\w+",
93
- r"rather than\s+\w+",
94
- r"as opposed to\s+\w+",
95
- )
96
-
97
- REQUIRED_XML_SECTIONS: tuple[str, ...] = (
98
- "role",
99
- "background",
100
- "instructions",
101
- "constraints",
102
- "output_format",
103
- )
104
-
105
- COMPILED_NEGATIVE_KEYWORD_PATTERNS: tuple[re.Pattern[str], ...] = tuple(
106
- re.compile(rf"\b{re.escape(keyword)}\b", re.IGNORECASE)
107
- for keyword in NEGATIVE_KEYWORDS_IN_ARTIFACT
108
- )
109
-
110
- COMPILED_NEGATIVE_INDIRECT_PATTERNS: tuple[re.Pattern[str], ...] = tuple(
111
- re.compile(pattern, re.IGNORECASE)
112
- for pattern in NEGATIVE_INDIRECT_PATTERNS_IN_ARTIFACT
113
- )