claude-dev-env 1.4.0 → 1.8.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.
@@ -0,0 +1,170 @@
1
+ ---
2
+ name: deep-research
3
+ description: Use this agent for iterative, multi-source deep research that produces comprehensive Obsidian reports with full citations. Official-docs-first methodology with anti-hallucination constraints. Examples:
4
+
5
+ <example>
6
+ Context: User wants thorough research on a technical topic
7
+ user: "Research the current state of WebSocket authentication best practices"
8
+ assistant: "I'll use the deep-research agent to conduct iterative multi-source research and produce a cited report."
9
+ <commentary>
10
+ Multi-source research requiring iteration and synthesis — exactly what deep-research handles.
11
+ </commentary>
12
+ </example>
13
+
14
+ <example>
15
+ Context: User needs a landscape survey with citations
16
+ user: "Compare the major vector database options for production RAG systems in 2026"
17
+ assistant: "I'll launch the deep-research agent to survey the landscape across multiple sources."
18
+ <commentary>
19
+ Broad survey requiring many sources, comparison, and synthesis — deep-research with exhaustive depth.
20
+ </commentary>
21
+ </example>
22
+
23
+ model: opus
24
+ color: cyan
25
+ ---
26
+
27
+ You are a Deep Research agent. You conduct thorough, iterative research across many sources and produce comprehensive, fully-cited reports saved to Obsidian.
28
+
29
+ You receive a `<research_brief>` from the orchestrating skill. Your job is to execute the research.
30
+
31
+ ## Setup
32
+
33
+ On receiving the research brief, write the state file:
34
+
35
+ `.deep-research-state.md`:
36
+
37
+ ```markdown
38
+ ---
39
+ topic: "[from brief]"
40
+ brief: "[one-line summary from brief]"
41
+ iteration: 0
42
+ max_iterations: [from brief]
43
+ status: researching
44
+ source_count: 0
45
+ official_docs_found: false
46
+ ---
47
+
48
+ ## Sources Found
49
+
50
+ (none yet)
51
+
52
+ ## Key Findings
53
+
54
+ (none yet)
55
+
56
+ ## Gaps Remaining
57
+
58
+ - Initial broad survey needed
59
+
60
+ ## Next Iteration Focus
61
+
62
+ - Locate official vendor/creator documentation for the topic
63
+ - Broad survey searches on the topic
64
+ - Identify major themes and authoritative sources
65
+ ```
66
+
67
+ Then immediately begin the first iteration.
68
+
69
+ ## Anti-Hallucination Constraints (ALWAYS ACTIVE)
70
+
71
+ These three constraints apply to every claim, finding, and recommendation. Violating any invalidates the work.
72
+
73
+ ### 1. Say "I don't know"
74
+ No credible source for a claim? Say so. Don't guess. Don't infer. Record the gap in the state file.
75
+
76
+ ### 2. Cite everything
77
+ Every claim must cite: an external source with URL, a named expert/paper/researcher, or official documentation. If you cannot find a supporting source, retract the claim.
78
+
79
+ ### 3. Direct quotes for factual grounding
80
+ Extract actual text from sources before analyzing. Ground responses in word-for-word quotes, not paraphrased summaries.
81
+
82
+ ## Iteration Protocol
83
+
84
+ Each iteration, follow these steps in order:
85
+
86
+ ### Step 1: Read State
87
+
88
+ Read `.deep-research-state.md`. Understand: sources found, key findings, remaining gaps, next focus.
89
+
90
+ First iteration? State is empty — start with official docs, then broad survey.
91
+
92
+ ### Step 2: Research (Search + Analyze)
93
+
94
+ Use available search and fetch tools aggressively and in parallel.
95
+
96
+ **Official docs first** — In early iterations, your primary objective is to locate and deeply read the official vendor/creator documentation for the topic. This means documentation published by the organization or person who created the tool, library, API, or protocol being researched. Exhaust official sources before broadening to secondary ones.
97
+
98
+ If no official documentation exists for the primary topic, record this explicitly as a gap in the state file. The absence of official docs is itself a finding — do not silently move on.
99
+
100
+ **Strategy by iteration phase:**
101
+ - **Early (1-3)**: Official docs first. Locate vendor/creator documentation. Read it deeply, extract direct quotes. Only after official sources are covered, begin broad survey to identify themes and secondary sources.
102
+ - **Middle (4-8)**: Deep dives into secondary sources. Fill gaps that official docs don't cover. Cross-reference secondary claims against official docs where possible.
103
+ - **Late (9+)**: Synthesis and gap-filling. Target remaining gaps, resolve contradictions between sources. Prefer official docs when sources disagree.
104
+
105
+ **Source classification** — When recording sources in the state file, tag each as:
106
+ - `[official]` — published by the vendor, creator, or maintainer of the tool/technology
107
+ - `[secondary]` — everything else (blog posts, tutorials, community content, third-party analysis)
108
+
109
+ ### Step 3: Update State
110
+
111
+ Update `.deep-research-state.md` with:
112
+ - New sources (title, URL, one-line relevance summary, [official] or [secondary] tag)
113
+ - Key findings with citations
114
+ - Updated gaps list
115
+ - Next iteration focus (specific queries and angles)
116
+ - Increment `iteration` and `source_count` in frontmatter
117
+ - Update `official_docs_found` if official docs were located
118
+
119
+ ### Step 4: Continue or Complete?
120
+
121
+ **Continue** if:
122
+ - Significant gaps remain
123
+ - Key questions from the brief are unanswered
124
+ - Promising leads not yet followed
125
+ - Source count below the brief's target depth
126
+ - Current iteration < max_iterations
127
+
128
+ **Complete** if:
129
+ - All key questions answered with citations
130
+ - Source target met or exceeded
131
+ - Remaining gaps are minor or out of scope
132
+ - Diminishing returns from further searching
133
+
134
+ If continuing, loop back to Step 1 for the next iteration. If complete, proceed to the Completion Process.
135
+
136
+ ### Completion Process
137
+
138
+ 1. Compile findings from state file into a structured report:
139
+ - Executive Summary (2-3 paragraphs, cite everything)
140
+ - Detailed Findings (organized by theme, not source; direct quotes blockquoted; every claim cited)
141
+ - Analysis (cross-cutting synthesis grounded in findings above)
142
+ - Limitations and Gaps (unanswered questions, source biases, whether official docs were available)
143
+ - Sources (numbered bibliography with [official]/[secondary] tags)
144
+ - Research Methodology (iterations, source count, date)
145
+
146
+ 2. The report must note whether official vendor/creator documentation was available for the topic. If it was not, this is a stated limitation — the user needs to know the research rests on secondary sources only.
147
+
148
+ 3. Write to Obsidian via `mcp__obsidian__write_note`:
149
+ - Path: `Research/[topic-slug].md`
150
+ - Include YAML frontmatter: type (deep-research), topic, date, sources count, iterations, official_docs_available (true/false), tags
151
+ - Every factual claim has an inline citation
152
+ - Full numbered bibliography at the end with [official]/[secondary] tags
153
+
154
+ 4. If Obsidian MCP is unavailable, output the full report in the conversation so the user can save it manually.
155
+
156
+ ### If max iterations reached without completion
157
+
158
+ - Compile what you have into a partial report
159
+ - Mark incomplete sections clearly
160
+ - Add "Future Research" section listing remaining gaps
161
+ - Still write to Obsidian
162
+
163
+ ## Output to Parent
164
+
165
+ After completion, your return message to the parent should include:
166
+ - Obsidian note path where the report was saved (or "output inline" if MCP unavailable)
167
+ - Total sources consulted (with official vs secondary breakdown)
168
+ - Total iterations used
169
+ - Whether official vendor documentation was found
170
+ - Any significant gaps or limitations
package/bin/install.mjs CHANGED
@@ -13,6 +13,32 @@ const PACKAGE_NAME = 'claude-dev-env';
13
13
 
14
14
  const CONTENT_DIRECTORIES = ['rules', 'docs', 'commands', 'agents'];
15
15
 
16
+ const INSTALL_GROUPS = {
17
+ core: {
18
+ description: 'Development standards, hooks, agents, commands',
19
+ skills: [
20
+ 'anthropic-plan', 'everything-search', 'ingest',
21
+ 'npm-creator', 'pr-review-responder', 'readability-review',
22
+ 'recall', 'remember', 'rule-audit', 'rule-creator',
23
+ 'skill-writer', 'tdd-team'
24
+ ],
25
+ includeDirectories: ['rules', 'docs', 'commands', 'agents'],
26
+ includeAllHooks: true,
27
+ },
28
+ prompts: {
29
+ description: 'Prompt engineering tools',
30
+ skills: ['prompt-generator', 'agent-prompt'],
31
+ },
32
+ journal: {
33
+ description: 'Session logging and memory',
34
+ skills: ['dream', 'session-log', 'session-tidy'],
35
+ },
36
+ research: {
37
+ description: 'Deep research and citation tools',
38
+ skills: ['deep-research', 'research-mode'],
39
+ },
40
+ };
41
+
16
42
  function detectPython() {
17
43
  const candidates = [
18
44
  { command: 'python3', versionFlag: '--version' },
@@ -119,8 +145,9 @@ function writeManifest(installedFiles) {
119
145
  writeFileSync(MANIFEST_FILE, JSON.stringify(manifest, null, 2) + '\n');
120
146
  }
121
147
 
122
- function install() {
123
- console.log(`\nInstalling ${PACKAGE_NAME}...\n`);
148
+ function install(selectedGroups) {
149
+ const groupLabel = selectedGroups ? `groups: ${selectedGroups.join(', ')}` : 'all';
150
+ console.log(`\nInstalling ${PACKAGE_NAME} (${groupLabel})...\n`);
124
151
  const pythonCommand = detectPython();
125
152
  if (!pythonCommand) {
126
153
  console.error('ERROR: Python 3 not found. Install Python 3.8+ and ensure python3, python, or py is on PATH.');
@@ -128,9 +155,21 @@ function install() {
128
155
  }
129
156
  console.log(` Python: ${pythonCommand}`);
130
157
  mkdirSync(CLAUDE_HOME, { recursive: true });
158
+
159
+ const allowedSkills = selectedGroups
160
+ ? new Set(selectedGroups.flatMap(groupName => INSTALL_GROUPS[groupName].skills || []))
161
+ : null;
162
+ const allowedDirectories = selectedGroups
163
+ ? new Set(selectedGroups.flatMap(groupName => INSTALL_GROUPS[groupName].includeDirectories || []))
164
+ : null;
165
+ const shouldInstallHooks = selectedGroups
166
+ ? selectedGroups.some(groupName => INSTALL_GROUPS[groupName].includeAllHooks)
167
+ : true;
168
+
131
169
  const allInstalledFiles = [];
132
170
  const summary = {};
133
171
  for (const directory of CONTENT_DIRECTORIES) {
172
+ if (allowedDirectories && !allowedDirectories.has(directory)) continue;
134
173
  const sourceDir = join(PACKAGE_ROOT, directory);
135
174
  if (!existsSync(sourceDir)) continue;
136
175
  const destDir = join(CLAUDE_HOME, directory);
@@ -145,6 +184,7 @@ function install() {
145
184
  let skillsUpdated = 0;
146
185
  const skillPaths = [];
147
186
  for (const skillDir of skillDirs) {
187
+ if (allowedSkills && !allowedSkills.has(skillDir.name)) continue;
148
188
  const stats = copyTree(join(skillsSource, skillDir.name), join(CLAUDE_HOME, 'skills', skillDir.name));
149
189
  skillsCreated += stats.created;
150
190
  skillsUpdated += stats.updated;
@@ -153,26 +193,28 @@ function install() {
153
193
  summary.skills = { created: skillsCreated, updated: skillsUpdated, paths: skillPaths };
154
194
  allInstalledFiles.push(...skillPaths);
155
195
  }
156
- const hooksSource = join(PACKAGE_ROOT, 'hooks');
157
- if (existsSync(hooksSource)) {
158
- const hooksDestination = join(CLAUDE_HOME, 'hooks');
159
- const filesToCopy = collectFiles(hooksSource).filter(file => !file.endsWith('hooks.json'));
160
- let hooksCreated = 0;
161
- let hooksUpdated = 0;
162
- for (const sourceFile of filesToCopy) {
163
- const relativePath = relative(hooksSource, sourceFile);
164
- const destFile = join(hooksDestination, relativePath);
165
- mkdirSync(dirname(destFile), { recursive: true });
166
- const existed = existsSync(destFile);
167
- copyFileSync(sourceFile, destFile);
168
- allInstalledFiles.push(destFile);
169
- if (existed) { hooksUpdated++; } else { hooksCreated++; }
196
+ if (shouldInstallHooks) {
197
+ const hooksSource = join(PACKAGE_ROOT, 'hooks');
198
+ if (existsSync(hooksSource)) {
199
+ const hooksDestination = join(CLAUDE_HOME, 'hooks');
200
+ const filesToCopy = collectFiles(hooksSource).filter(file => !file.endsWith('hooks.json'));
201
+ let hooksCreated = 0;
202
+ let hooksUpdated = 0;
203
+ for (const sourceFile of filesToCopy) {
204
+ const relativePath = relative(hooksSource, sourceFile);
205
+ const destFile = join(hooksDestination, relativePath);
206
+ mkdirSync(dirname(destFile), { recursive: true });
207
+ const existed = existsSync(destFile);
208
+ copyFileSync(sourceFile, destFile);
209
+ allInstalledFiles.push(destFile);
210
+ if (existed) { hooksUpdated++; } else { hooksCreated++; }
211
+ }
212
+ summary.hookFiles = { created: hooksCreated, updated: hooksUpdated };
213
+ console.log(` Hook files: ${hooksCreated} new, ${hooksUpdated} updated`);
214
+ const groupCount = mergeHooks(pythonCommand);
215
+ summary.hookGroups = groupCount;
216
+ console.log(` Hook groups: ${groupCount} merged into settings.json`);
170
217
  }
171
- summary.hookFiles = { created: hooksCreated, updated: hooksUpdated };
172
- console.log(` Hook files: ${hooksCreated} new, ${hooksUpdated} updated`);
173
- const groupCount = mergeHooks(pythonCommand);
174
- summary.hookGroups = groupCount;
175
- console.log(` Hook groups: ${groupCount} merged into settings.json`);
176
218
  }
177
219
  writeManifest(allInstalledFiles);
178
220
  console.log(`\nInstalled ${PACKAGE_NAME}:`);
@@ -243,15 +285,45 @@ function printHelp() {
243
285
  ${PACKAGE_NAME} - Claude Code development standards installer
244
286
 
245
287
  Usage:
246
- npx ${PACKAGE_NAME} Install rules, hooks, agents, commands, skills
247
- npx ${PACKAGE_NAME} --uninstall Remove installed files and hooks
288
+ npx ${PACKAGE_NAME} Install everything
289
+ npx ${PACKAGE_NAME} --only X Install specific groups
290
+ npx ${PACKAGE_NAME} --uninstall Remove installed files
248
291
  npx ${PACKAGE_NAME} --help Show this help
249
292
 
293
+ Groups:
294
+ core Development standards, hooks, agents, commands
295
+ prompts Prompt engineering tools
296
+ journal Session logging and memory
297
+ research Deep research and citation tools
298
+
299
+ Examples:
300
+ npx ${PACKAGE_NAME} --only prompts
301
+ npx ${PACKAGE_NAME} --only prompts,research
302
+
250
303
  Install location: ~/.claude/
251
304
  `);
252
305
  }
253
306
 
254
307
  const args = process.argv.slice(2);
255
- if (args.includes('--help') || args.includes('-h')) printHelp();
256
- else if (args.includes('--uninstall')) uninstall();
257
- else install();
308
+ if (args.includes('--help') || args.includes('-h')) {
309
+ printHelp();
310
+ } else if (args.includes('--uninstall')) {
311
+ uninstall();
312
+ } else {
313
+ const onlyIndex = args.indexOf('--only');
314
+ let selectedGroups = null;
315
+ if (onlyIndex !== -1) {
316
+ const onlyValue = args[onlyIndex + 1];
317
+ if (!onlyValue || onlyValue.startsWith('--')) {
318
+ console.error(`ERROR: --only requires a comma-separated list of groups.\nAvailable groups: ${Object.keys(INSTALL_GROUPS).join(', ')}`);
319
+ process.exit(1);
320
+ }
321
+ selectedGroups = onlyValue.split(',').map(name => name.trim());
322
+ const invalidGroups = selectedGroups.filter(name => !INSTALL_GROUPS[name]);
323
+ if (invalidGroups.length > 0) {
324
+ console.error(`ERROR: Unknown group(s): ${invalidGroups.join(', ')}\nAvailable groups: ${Object.keys(INSTALL_GROUPS).join(', ')}`);
325
+ process.exit(1);
326
+ }
327
+ }
328
+ install(selectedGroups);
329
+ }
@@ -0,0 +1,68 @@
1
+ # Prompt Workflow Hook Specs
2
+
3
+ Deterministic runtime gates for prompt workflows.
4
+
5
+ ## Gate: Execution Intent (PreToolUse Task/Agent)
6
+
7
+ - Hook: `hooks/blocking/agent-execution-intent-gate.py`
8
+ - Event: `PreToolUse`
9
+ - Matcher: `Task|Agent`
10
+ - Fail condition:
11
+ - Missing structured execution intent contract field:
12
+ - `tool_input.execution_intent: explicit|execute|delegate`, or
13
+ - `tool_input.execution_intent_explicit: true`, or
14
+ - `tool_input.metadata.execution_intent: explicit|execute|delegate`
15
+ - Missing required scope anchors in launch payload (always enforced when execution launch is evaluated)
16
+ - Compatibility fallback:
17
+ - Text markers are only accepted when `PROMPT_WORKFLOW_ALLOW_TEXT_INTENT_FALLBACK=1` is set.
18
+ - Fallback usage is logged to stderr.
19
+ - Action: `deny` with concrete missing requirement list.
20
+
21
+ ## Gate: Leakage + Checklist + Scope (Stop)
22
+
23
+ - Hook: `hooks/blocking/prompt-workflow-stop-guard.py`
24
+ - Event: `Stop`
25
+ - Fail condition:
26
+ - Raw internal refinement object appears in assistant output without explicit debug intent
27
+ - Prompt-workflow response detected but deterministic checklist container is missing
28
+ - Prompt-workflow response detected and required deterministic checklist rows are missing
29
+ - Prompt-workflow response detected and required scope anchors are missing
30
+ - Prompt-workflow response detected and runtime context-control signals are missing
31
+ - Scope-bound text uses banned ambiguous scope terms
32
+ - Action: `block` with correction reason.
33
+
34
+ ## Required Scope Anchors
35
+
36
+ - `target_local_roots`
37
+ - `target_canonical_roots`
38
+ - `target_file_globs`
39
+ - `comparison_basis`
40
+ - `completion_boundary`
41
+
42
+ ## Required Deterministic Checklist Rows
43
+
44
+ - `structured_scoped_instructions`
45
+ - `sequential_steps_present`
46
+ - `positive_framing`
47
+ - `acceptance_criteria_defined`
48
+ - `safety_reversibility_language`
49
+ - `no_destructive_shortcuts_guidance`
50
+ - `concrete_output_contract`
51
+ - `scope_boundary_present`
52
+ - `explicit_scope_anchors_present`
53
+ - `all_instructions_artifact_bound`
54
+ - `no_ambiguous_scope_terms`
55
+ - `completion_boundary_measurable`
56
+ - `citation_grounding_policy_present`
57
+ - `source_priority_rules_present`
58
+
59
+ ## Runtime Context-Control Signals
60
+
61
+ - `base_minimal_instruction_layer: true`
62
+ - `on_demand_skill_loading: true`
63
+
64
+ These two signals are runtime-checked by the Stop guard whenever a prompt-workflow response is detected.
65
+
66
+ ## Deterministic Boundary
67
+
68
+ These hooks enforce only structural/runtime checks. Semantic quality remains in auditor layer.
@@ -0,0 +1,83 @@
1
+ #!/usr/bin/env python3
2
+ """PreToolUse gate for Task/Agent execution intent and scope anchors."""
3
+
4
+ from __future__ import annotations
5
+
6
+ import json
7
+ import os
8
+ import sys
9
+
10
+ from prompt_workflow_gate_core import (
11
+ has_explicit_execution_intent,
12
+ has_structured_execution_intent,
13
+ missing_scope_anchors,
14
+ )
15
+
16
+
17
+ def _deny(reason: str) -> None:
18
+ response = {
19
+ "hookSpecificOutput": {
20
+ "hookEventName": "PreToolUse",
21
+ "permissionDecision": "deny",
22
+ "permissionDecisionReason": reason,
23
+ }
24
+ }
25
+ print(json.dumps(response))
26
+
27
+
28
+ def main() -> None:
29
+ try:
30
+ hook_input = json.load(sys.stdin)
31
+ except json.JSONDecodeError:
32
+ sys.exit(0)
33
+
34
+ tool_name = str(hook_input.get("tool_name", ""))
35
+ if tool_name not in {"Task", "Agent"}:
36
+ sys.exit(0)
37
+
38
+ tool_input = hook_input.get("tool_input", {})
39
+ prompt_text = str(tool_input.get("prompt", ""))
40
+ description = str(tool_input.get("description", ""))
41
+ combined_text = f"{description}\n{prompt_text}"
42
+
43
+ if not has_structured_execution_intent(tool_input):
44
+ allow_text_fallback = os.getenv(
45
+ "PROMPT_WORKFLOW_ALLOW_TEXT_INTENT_FALLBACK", ""
46
+ ).strip().lower() in {"1", "true", "yes"}
47
+ text_intent_detected = has_explicit_execution_intent(combined_text)
48
+ if allow_text_fallback and text_intent_detected:
49
+ print(
50
+ "PROMPT-WORKFLOW GATE: compatibility text-intent fallback used; "
51
+ "structured execution intent contract should be provided.",
52
+ file=sys.stderr,
53
+ )
54
+ else:
55
+ fallback_note = ""
56
+ if text_intent_detected:
57
+ print(
58
+ "PROMPT-WORKFLOW GATE: text intent marker detected without structured "
59
+ "execution intent contract.",
60
+ file=sys.stderr,
61
+ )
62
+ fallback_note = " Legacy text marker was detected but is not sufficient."
63
+ _deny(
64
+ "BLOCKED: Missing structured execution intent signal for Agent/Task launch. "
65
+ "Provide `tool_input.execution_intent: explicit` or "
66
+ "`tool_input.execution_intent_explicit: true`."
67
+ + fallback_note
68
+ )
69
+ sys.exit(0)
70
+
71
+ missing_anchors = missing_scope_anchors(combined_text)
72
+ if missing_anchors:
73
+ _deny(
74
+ "BLOCKED: Scope anchors missing for prompt workflow execution: "
75
+ + ", ".join(missing_anchors)
76
+ )
77
+ sys.exit(0)
78
+
79
+ sys.exit(0)
80
+
81
+
82
+ if __name__ == "__main__":
83
+ main()
@@ -0,0 +1,131 @@
1
+ #!/usr/bin/env python3
2
+ """Stop hook gate for prompt-workflow leakage and deterministic audit coverage."""
3
+
4
+ from __future__ import annotations
5
+
6
+ import json
7
+ import sys
8
+
9
+ from prompt_workflow_gate_core import (
10
+ find_ambiguous_scope_terms,
11
+ has_debug_intent,
12
+ has_checklist_container,
13
+ has_internal_object_leak,
14
+ is_prompt_workflow_response,
15
+ missing_context_control_signals,
16
+ missing_checklist_rows,
17
+ missing_scope_anchors,
18
+ )
19
+
20
+
21
+ def _extract_user_context(hook_input: dict) -> str:
22
+ candidates = (
23
+ "last_user_message",
24
+ "user_message",
25
+ "user_prompt",
26
+ "prompt",
27
+ "input",
28
+ )
29
+ for key in candidates:
30
+ value = hook_input.get(key)
31
+ if isinstance(value, str) and value.strip():
32
+ return value
33
+ return ""
34
+
35
+
36
+ def _build_block(reason: str) -> dict:
37
+ return {
38
+ "decision": "block",
39
+ "reason": reason,
40
+ }
41
+
42
+
43
+ def main() -> None:
44
+ try:
45
+ hook_input = json.load(sys.stdin)
46
+ except json.JSONDecodeError:
47
+ sys.exit(0)
48
+
49
+ assistant_message = str(hook_input.get("last_assistant_message", ""))
50
+ if not assistant_message.strip():
51
+ sys.exit(0)
52
+
53
+ user_context = _extract_user_context(hook_input)
54
+ debug_requested = has_debug_intent(user_context)
55
+
56
+ if has_internal_object_leak(assistant_message) and not debug_requested:
57
+ print(
58
+ json.dumps(
59
+ _build_block(
60
+ "PROMPT-WORKFLOW GATE: Raw internal refinement object leakage detected. "
61
+ "Return sanitized user-facing output unless explicit debug intent is present."
62
+ )
63
+ )
64
+ )
65
+ sys.exit(0)
66
+
67
+ if is_prompt_workflow_response(assistant_message):
68
+ if not has_checklist_container(assistant_message):
69
+ print(
70
+ json.dumps(
71
+ _build_block(
72
+ "PROMPT-WORKFLOW GATE: Deterministic checklist container missing. "
73
+ "Include `checklist_results` with all required rows."
74
+ )
75
+ )
76
+ )
77
+ sys.exit(0)
78
+
79
+ missing_rows = missing_checklist_rows(assistant_message)
80
+ if missing_rows:
81
+ print(
82
+ json.dumps(
83
+ _build_block(
84
+ "PROMPT-WORKFLOW GATE: Deterministic checklist rows missing: "
85
+ + ", ".join(missing_rows)
86
+ )
87
+ )
88
+ )
89
+ sys.exit(0)
90
+
91
+ missing_anchors = missing_scope_anchors(assistant_message)
92
+ if missing_anchors:
93
+ print(
94
+ json.dumps(
95
+ _build_block(
96
+ "PROMPT-WORKFLOW GATE: Required scope anchors missing: "
97
+ + ", ".join(missing_anchors)
98
+ )
99
+ )
100
+ )
101
+ sys.exit(0)
102
+
103
+ missing_context_signals = missing_context_control_signals(assistant_message)
104
+ if missing_context_signals:
105
+ print(
106
+ json.dumps(
107
+ _build_block(
108
+ "PROMPT-WORKFLOW GATE: Runtime context-control signals missing: "
109
+ + ", ".join(missing_context_signals)
110
+ )
111
+ )
112
+ )
113
+ sys.exit(0)
114
+
115
+ ambiguous_terms = find_ambiguous_scope_terms(assistant_message)
116
+ if ambiguous_terms:
117
+ print(
118
+ json.dumps(
119
+ _build_block(
120
+ "PROMPT-WORKFLOW GATE: Ambiguous scope phrasing detected: "
121
+ + ", ".join(ambiguous_terms)
122
+ )
123
+ )
124
+ )
125
+ sys.exit(0)
126
+
127
+ sys.exit(0)
128
+
129
+
130
+ if __name__ == "__main__":
131
+ main()