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.
- package/agents/deep-research.md +170 -0
- package/bin/install.mjs +98 -26
- package/hooks/HOOK_SPECS_PROMPT_WORKFLOW.md +68 -0
- package/hooks/blocking/agent-execution-intent-gate.py +83 -0
- package/hooks/blocking/prompt-workflow-stop-guard.py +131 -0
- package/hooks/blocking/prompt_workflow_gate_core.py +161 -0
- package/hooks/blocking/test_agent_execution_intent_gate.py +106 -0
- package/hooks/blocking/test_context_control_policy_files.py +27 -0
- package/hooks/blocking/test_prompt_workflow_gate_core.py +68 -0
- package/hooks/blocking/test_prompt_workflow_stop_guard.py +144 -0
- package/hooks/hooks.json +10 -20
- package/package.json +3 -2
- package/rules/prompt-workflow-context-controls.md +48 -0
- package/skills/agent-prompt/SKILL.md +107 -9
- package/skills/deep-research/SKILL.md +80 -0
- package/skills/dream/SKILL.md +118 -0
- package/skills/prompt-generator/REFINEMENT_PIPELINE_RUNBOOK.md +174 -0
- package/skills/prompt-generator/SKILL.md +191 -12
- package/skills/research-mode/SKILL.md +53 -0
- package/skills/session-log/SKILL.md +237 -0
- package/skills/session-tidy/SKILL.md +181 -0
- package/skills/skill-writer/REFERENCE.md +160 -122
- package/skills/skill-writer/SKILL.md +131 -197
- package/LICENSE +0 -21
- package/README.md +0 -247
|
@@ -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
|
-
|
|
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
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
const
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
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
|
|
247
|
-
npx ${PACKAGE_NAME} --
|
|
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'))
|
|
256
|
-
|
|
257
|
-
else
|
|
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()
|