@hustle-together/api-dev-tools 1.7.0 → 1.7.1
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/hooks/enforce-external-research.py +195 -185
- package/package.json +1 -1
|
@@ -1,13 +1,20 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
2
|
"""
|
|
3
3
|
Hook: UserPromptSubmit
|
|
4
|
-
Purpose:
|
|
4
|
+
Purpose: ALWAYS enforce research before answering technical questions
|
|
5
5
|
|
|
6
|
-
This hook runs BEFORE Claude processes the user's prompt. It
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
This hook runs BEFORE Claude processes the user's prompt. It aggressively
|
|
7
|
+
detects ANY technical question and requires comprehensive research using
|
|
8
|
+
BOTH Context7 AND multiple WebSearches before answering.
|
|
9
9
|
|
|
10
|
-
Philosophy: "
|
|
10
|
+
Philosophy: "ALWAYS research. Training data is NEVER trustworthy for technical info."
|
|
11
|
+
|
|
12
|
+
The hook triggers on:
|
|
13
|
+
- ANY mention of APIs, SDKs, libraries, packages, frameworks
|
|
14
|
+
- ANY technical "how to" or capability questions
|
|
15
|
+
- ANY code-related questions (functions, methods, parameters, types)
|
|
16
|
+
- ANY questions about tools, services, or platforms
|
|
17
|
+
- ANY request for implementation, editing, or changes
|
|
11
18
|
|
|
12
19
|
Returns:
|
|
13
20
|
- Prints context to stdout (injected into conversation)
|
|
@@ -23,134 +30,172 @@ from datetime import datetime
|
|
|
23
30
|
STATE_FILE = Path(__file__).parent.parent / "api-dev-state.json"
|
|
24
31
|
|
|
25
32
|
# ============================================================================
|
|
26
|
-
#
|
|
33
|
+
# AGGRESSIVE DETECTION PATTERNS
|
|
27
34
|
# ============================================================================
|
|
28
35
|
|
|
29
|
-
#
|
|
30
|
-
|
|
31
|
-
#
|
|
32
|
-
r"
|
|
33
|
-
r"\b
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
r"\b(?:
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
r"\b(?:
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
r"\
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
r"(?:
|
|
36
|
+
# Technical terms that ALWAYS trigger research
|
|
37
|
+
TECHNICAL_TERMS = [
|
|
38
|
+
# Code/Development
|
|
39
|
+
r"\b(?:function|method|class|interface|type|schema|model)\b",
|
|
40
|
+
r"\b(?:parameter|argument|option|config|setting|property)\b",
|
|
41
|
+
r"\b(?:import|export|require|module|package|library|dependency)\b",
|
|
42
|
+
r"\b(?:api|sdk|framework|runtime|engine|platform)\b",
|
|
43
|
+
r"\b(?:endpoint|route|url|path|request|response|header)\b",
|
|
44
|
+
r"\b(?:database|query|table|collection|document|record)\b",
|
|
45
|
+
r"\b(?:authentication|authorization|token|key|secret|credential)\b",
|
|
46
|
+
r"\b(?:error|exception|bug|issue|problem|fix)\b",
|
|
47
|
+
r"\b(?:test|spec|coverage|mock|stub|fixture)\b",
|
|
48
|
+
r"\b(?:deploy|build|compile|bundle|publish|release)\b",
|
|
49
|
+
r"\b(?:install|setup|configure|initialize|migrate)\b",
|
|
50
|
+
r"\b(?:provider|service|client|server|handler|middleware)\b",
|
|
51
|
+
r"\b(?:stream|async|await|promise|callback|event)\b",
|
|
52
|
+
r"\b(?:component|widget|element|view|layout|template)\b",
|
|
53
|
+
r"\b(?:state|store|reducer|action|context|hook)\b",
|
|
54
|
+
r"\b(?:validate|parse|serialize|transform|convert)\b",
|
|
55
|
+
|
|
56
|
+
# Package patterns
|
|
57
|
+
r"@[\w-]+/[\w-]+", # @scope/package
|
|
58
|
+
r"\b[\w-]+-(?:sdk|api|js|ts|py|go|rs)\b", # something-sdk, something-api
|
|
59
|
+
|
|
60
|
+
# Version patterns
|
|
61
|
+
r"\bv?\d+\.\d+(?:\.\d+)?(?:-[\w.]+)?\b", # v1.2.3, 2.0.0-beta
|
|
62
|
+
|
|
63
|
+
# File patterns
|
|
64
|
+
r"\b[\w-]+\.(?:ts|js|tsx|jsx|py|go|rs|json|yaml|yml|toml|env)\b",
|
|
46
65
|
]
|
|
47
66
|
|
|
48
|
-
#
|
|
49
|
-
|
|
50
|
-
#
|
|
51
|
-
r"
|
|
52
|
-
r"
|
|
53
|
-
|
|
54
|
-
#
|
|
55
|
-
r"(?:
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
r"
|
|
62
|
-
r"
|
|
63
|
-
r"
|
|
64
|
-
|
|
65
|
-
#
|
|
66
|
-
r"
|
|
67
|
-
r"
|
|
67
|
+
# Question patterns that indicate asking about functionality
|
|
68
|
+
QUESTION_PATTERNS = [
|
|
69
|
+
# Direct questions
|
|
70
|
+
r"\b(?:what|which|where|when|why|how)\b",
|
|
71
|
+
r"\b(?:can|could|would|should|will|does|do|is|are)\b.*\?",
|
|
72
|
+
|
|
73
|
+
# Requests
|
|
74
|
+
r"\b(?:show|tell|explain|describe|list|find|get|give)\b",
|
|
75
|
+
r"\b(?:help|need|want|looking for|trying to)\b",
|
|
76
|
+
|
|
77
|
+
# Actions
|
|
78
|
+
r"\b(?:create|make|build|add|implement|write|generate)\b",
|
|
79
|
+
r"\b(?:update|change|modify|edit|fix|refactor|improve)\b",
|
|
80
|
+
r"\b(?:delete|remove|drop|clear|reset)\b",
|
|
81
|
+
r"\b(?:connect|integrate|link|sync|merge)\b",
|
|
82
|
+
r"\b(?:debug|trace|log|monitor|track)\b",
|
|
83
|
+
|
|
84
|
+
# Comparisons
|
|
85
|
+
r"\b(?:difference|compare|versus|vs|between|or)\b",
|
|
86
|
+
r"\b(?:better|best|recommended|preferred|alternative)\b",
|
|
68
87
|
]
|
|
69
88
|
|
|
70
|
-
#
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
"
|
|
74
|
-
"
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
"
|
|
78
|
-
"
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
"
|
|
82
|
-
"
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
# Data/Analytics
|
|
86
|
-
"segment", "mixpanel", "amplitude", "datadog", "sentry", "grafana",
|
|
87
|
-
|
|
88
|
-
# Media/Content
|
|
89
|
-
"cloudinary", "imgix", "mux", "brandfetch", "unsplash", "pexels",
|
|
90
|
-
|
|
91
|
-
# Auth
|
|
92
|
-
"auth0", "okta", "clerk", "nextauth", "passport",
|
|
89
|
+
# Phrases that ALWAYS require research (no exceptions)
|
|
90
|
+
ALWAYS_RESEARCH_PHRASES = [
|
|
91
|
+
r"how (?:to|do|does|can|should|would)",
|
|
92
|
+
r"what (?:is|are|does|can|should)",
|
|
93
|
+
r"(?:does|can|will|should) .+ (?:support|have|handle|work|do)",
|
|
94
|
+
r"(?:list|show|get|find) (?:all|available|supported)",
|
|
95
|
+
r"example (?:of|for|using|with|code)",
|
|
96
|
+
r"(?:implement|add|create|build|write|generate) .+",
|
|
97
|
+
r"(?:update|change|modify|edit|fix) .+",
|
|
98
|
+
r"(?:configure|setup|install|deploy) .+",
|
|
99
|
+
r"(?:error|issue|problem|bug|not working)",
|
|
100
|
+
r"(?:api|sdk|library|package|module|framework)",
|
|
101
|
+
r"(?:documentation|docs|reference|guide)",
|
|
102
|
+
]
|
|
93
103
|
|
|
94
|
-
|
|
95
|
-
|
|
104
|
+
# Exclusion patterns - things that DON'T need research
|
|
105
|
+
EXCLUDE_PATTERNS = [
|
|
106
|
+
r"^(?:hi|hello|hey|thanks|thank you|ok|okay|yes|no|sure)[\s!?.]*$",
|
|
107
|
+
r"^(?:good morning|good afternoon|good evening|goodbye|bye)[\s!?.]*$",
|
|
108
|
+
r"^(?:please|sorry|excuse me)[\s!?.]*$",
|
|
109
|
+
r"^(?:\d+[\s+\-*/]\d+|calculate|math).*$", # Simple math
|
|
96
110
|
]
|
|
97
111
|
|
|
98
112
|
# ============================================================================
|
|
99
113
|
# DETECTION LOGIC
|
|
100
114
|
# ============================================================================
|
|
101
115
|
|
|
102
|
-
def
|
|
116
|
+
def is_excluded(prompt: str) -> bool:
|
|
117
|
+
"""Check if prompt is a simple greeting or non-technical."""
|
|
118
|
+
prompt_clean = prompt.strip().lower()
|
|
119
|
+
|
|
120
|
+
# Very short prompts that are just greetings
|
|
121
|
+
if len(prompt_clean) < 20:
|
|
122
|
+
for pattern in EXCLUDE_PATTERNS:
|
|
123
|
+
if re.match(pattern, prompt_clean, re.IGNORECASE):
|
|
124
|
+
return True
|
|
125
|
+
|
|
126
|
+
return False
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def detect_technical_question(prompt: str) -> dict:
|
|
103
130
|
"""
|
|
104
|
-
|
|
131
|
+
Aggressively detect if the prompt is technical and requires research.
|
|
105
132
|
|
|
106
133
|
Returns:
|
|
107
134
|
{
|
|
108
135
|
"detected": bool,
|
|
109
136
|
"terms": list of detected terms,
|
|
110
|
-
"patterns_matched": list of pattern types
|
|
111
|
-
"confidence": "high" | "medium" | "low"
|
|
137
|
+
"patterns_matched": list of pattern types,
|
|
138
|
+
"confidence": "critical" | "high" | "medium" | "low" | "none"
|
|
112
139
|
}
|
|
113
140
|
"""
|
|
141
|
+
if is_excluded(prompt):
|
|
142
|
+
return {
|
|
143
|
+
"detected": False,
|
|
144
|
+
"terms": [],
|
|
145
|
+
"patterns_matched": [],
|
|
146
|
+
"confidence": "none",
|
|
147
|
+
}
|
|
148
|
+
|
|
114
149
|
prompt_lower = prompt.lower()
|
|
115
150
|
detected_terms = []
|
|
116
151
|
patterns_matched = []
|
|
117
152
|
|
|
118
|
-
# Check for
|
|
119
|
-
for
|
|
120
|
-
if
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
153
|
+
# Check for ALWAYS_RESEARCH_PHRASES first (highest priority)
|
|
154
|
+
for pattern in ALWAYS_RESEARCH_PHRASES:
|
|
155
|
+
if re.search(pattern, prompt_lower, re.IGNORECASE):
|
|
156
|
+
patterns_matched.append("always_research")
|
|
157
|
+
# Extract the matched phrase
|
|
158
|
+
match = re.search(pattern, prompt_lower, re.IGNORECASE)
|
|
159
|
+
if match:
|
|
160
|
+
detected_terms.append(match.group(0)[:50])
|
|
161
|
+
|
|
162
|
+
# Check technical terms
|
|
163
|
+
for pattern in TECHNICAL_TERMS:
|
|
126
164
|
matches = re.findall(pattern, prompt_lower, re.IGNORECASE)
|
|
127
165
|
if matches:
|
|
128
|
-
detected_terms.extend(matches)
|
|
129
|
-
patterns_matched.append("
|
|
166
|
+
detected_terms.extend(matches[:3]) # Limit per pattern
|
|
167
|
+
patterns_matched.append("technical_term")
|
|
130
168
|
|
|
131
|
-
# Check
|
|
132
|
-
for pattern in
|
|
169
|
+
# Check question patterns
|
|
170
|
+
for pattern in QUESTION_PATTERNS:
|
|
133
171
|
if re.search(pattern, prompt_lower, re.IGNORECASE):
|
|
134
|
-
patterns_matched.append("
|
|
172
|
+
patterns_matched.append("question_pattern")
|
|
135
173
|
break
|
|
136
174
|
|
|
137
175
|
# Deduplicate
|
|
138
|
-
detected_terms = list(
|
|
176
|
+
detected_terms = list(dict.fromkeys(detected_terms))[:10]
|
|
139
177
|
patterns_matched = list(set(patterns_matched))
|
|
140
178
|
|
|
141
|
-
# Determine confidence
|
|
142
|
-
if "
|
|
179
|
+
# Determine confidence - MUCH more aggressive
|
|
180
|
+
if "always_research" in patterns_matched:
|
|
181
|
+
confidence = "critical"
|
|
182
|
+
elif "technical_term" in patterns_matched and "question_pattern" in patterns_matched:
|
|
143
183
|
confidence = "high"
|
|
144
|
-
elif "
|
|
145
|
-
confidence = "
|
|
146
|
-
elif patterns_matched:
|
|
147
|
-
confidence = "
|
|
184
|
+
elif "technical_term" in patterns_matched:
|
|
185
|
+
confidence = "high" # Technical terms alone = high
|
|
186
|
+
elif "question_pattern" in patterns_matched and len(prompt) > 30:
|
|
187
|
+
confidence = "medium" # Questions longer than 30 chars
|
|
188
|
+
elif len(prompt) > 50:
|
|
189
|
+
confidence = "low" # Longer prompts default to low (still triggers)
|
|
148
190
|
else:
|
|
149
191
|
confidence = "none"
|
|
150
192
|
|
|
193
|
+
# AGGRESSIVE: Trigger on anything except "none"
|
|
194
|
+
detected = confidence != "none"
|
|
195
|
+
|
|
151
196
|
return {
|
|
152
|
-
"detected":
|
|
153
|
-
"terms": detected_terms
|
|
197
|
+
"detected": detected,
|
|
198
|
+
"terms": detected_terms,
|
|
154
199
|
"patterns_matched": patterns_matched,
|
|
155
200
|
"confidence": confidence,
|
|
156
201
|
}
|
|
@@ -165,11 +210,11 @@ def check_active_workflow() -> bool:
|
|
|
165
210
|
state = json.loads(STATE_FILE.read_text())
|
|
166
211
|
phases = state.get("phases", {})
|
|
167
212
|
|
|
168
|
-
# Check if any phase is in progress
|
|
169
213
|
for phase_key, phase_data in phases.items():
|
|
170
214
|
if isinstance(phase_data, dict):
|
|
171
215
|
status = phase_data.get("status", "")
|
|
172
|
-
if status in ["in_progress", "pending"]:
|
|
216
|
+
if status in ["in_progress", "pending", "complete"]:
|
|
217
|
+
# If ANY phase has been touched, we're in a workflow
|
|
173
218
|
return True
|
|
174
219
|
|
|
175
220
|
return False
|
|
@@ -177,50 +222,13 @@ def check_active_workflow() -> bool:
|
|
|
177
222
|
return False
|
|
178
223
|
|
|
179
224
|
|
|
180
|
-
def
|
|
181
|
-
"""Check which terms have already been researched."""
|
|
182
|
-
if not STATE_FILE.exists():
|
|
183
|
-
return []
|
|
184
|
-
|
|
185
|
-
try:
|
|
186
|
-
state = json.loads(STATE_FILE.read_text())
|
|
187
|
-
research_queries = state.get("research_queries", [])
|
|
188
|
-
|
|
189
|
-
# Also check sources in phases
|
|
190
|
-
phases = state.get("phases", {})
|
|
191
|
-
all_sources = []
|
|
192
|
-
for phase_data in phases.values():
|
|
193
|
-
if isinstance(phase_data, dict):
|
|
194
|
-
sources = phase_data.get("sources", [])
|
|
195
|
-
all_sources.extend(sources)
|
|
196
|
-
|
|
197
|
-
# Combine all research text
|
|
198
|
-
all_research_text = " ".join(str(s) for s in all_sources)
|
|
199
|
-
all_research_text += " ".join(
|
|
200
|
-
str(q.get("query", "")) + " " + str(q.get("term", ""))
|
|
201
|
-
for q in research_queries
|
|
202
|
-
if isinstance(q, dict)
|
|
203
|
-
)
|
|
204
|
-
all_research_text = all_research_text.lower()
|
|
205
|
-
|
|
206
|
-
# Find which terms were already researched
|
|
207
|
-
already_researched = []
|
|
208
|
-
for term in terms:
|
|
209
|
-
if term.lower() in all_research_text:
|
|
210
|
-
already_researched.append(term)
|
|
211
|
-
|
|
212
|
-
return already_researched
|
|
213
|
-
except (json.JSONDecodeError, Exception):
|
|
214
|
-
return []
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
def log_detection(prompt: str, detection: dict) -> None:
|
|
225
|
+
def log_detection(prompt: str, detection: dict, injected: bool) -> None:
|
|
218
226
|
"""Log this detection for debugging/auditing."""
|
|
219
|
-
if not STATE_FILE.exists():
|
|
220
|
-
return
|
|
221
|
-
|
|
222
227
|
try:
|
|
223
|
-
|
|
228
|
+
if STATE_FILE.exists():
|
|
229
|
+
state = json.loads(STATE_FILE.read_text())
|
|
230
|
+
else:
|
|
231
|
+
state = {"prompt_detections": []}
|
|
224
232
|
|
|
225
233
|
if "prompt_detections" not in state:
|
|
226
234
|
state["prompt_detections"] = []
|
|
@@ -229,11 +237,13 @@ def log_detection(prompt: str, detection: dict) -> None:
|
|
|
229
237
|
"timestamp": datetime.now().isoformat(),
|
|
230
238
|
"prompt_preview": prompt[:100] + "..." if len(prompt) > 100 else prompt,
|
|
231
239
|
"detection": detection,
|
|
240
|
+
"injected": injected,
|
|
232
241
|
})
|
|
233
242
|
|
|
234
|
-
# Keep only last
|
|
235
|
-
state["prompt_detections"] = state["prompt_detections"][-
|
|
243
|
+
# Keep only last 50 detections
|
|
244
|
+
state["prompt_detections"] = state["prompt_detections"][-50:]
|
|
236
245
|
|
|
246
|
+
STATE_FILE.parent.mkdir(parents=True, exist_ok=True)
|
|
237
247
|
STATE_FILE.write_text(json.dumps(state, indent=2))
|
|
238
248
|
except Exception:
|
|
239
249
|
pass # Don't fail the hook on logging errors
|
|
@@ -248,69 +258,69 @@ def main():
|
|
|
248
258
|
try:
|
|
249
259
|
input_data = json.load(sys.stdin)
|
|
250
260
|
except json.JSONDecodeError:
|
|
251
|
-
# If we can't parse input, allow without injection
|
|
252
261
|
sys.exit(0)
|
|
253
262
|
|
|
254
263
|
prompt = input_data.get("prompt", "")
|
|
255
264
|
|
|
256
|
-
if not prompt:
|
|
265
|
+
if not prompt or len(prompt.strip()) < 5:
|
|
257
266
|
sys.exit(0)
|
|
258
267
|
|
|
259
|
-
# Check if in active workflow mode
|
|
268
|
+
# Check if in active workflow mode
|
|
260
269
|
active_workflow = check_active_workflow()
|
|
261
270
|
|
|
262
|
-
# Detect
|
|
263
|
-
detection =
|
|
264
|
-
|
|
265
|
-
# Log for debugging
|
|
266
|
-
if detection["detected"] or active_workflow:
|
|
267
|
-
log_detection(prompt, detection)
|
|
271
|
+
# Detect technical questions
|
|
272
|
+
detection = detect_technical_question(prompt)
|
|
268
273
|
|
|
269
|
-
#
|
|
270
|
-
|
|
271
|
-
|
|
274
|
+
# In active workflow, ALWAYS inject (even for low confidence)
|
|
275
|
+
if active_workflow and detection["confidence"] != "none":
|
|
276
|
+
detection["detected"] = True
|
|
272
277
|
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
if detection["confidence"] in ["high", "medium", "low"]:
|
|
276
|
-
should_inject = True
|
|
277
|
-
inject_reason = "active_workflow"
|
|
278
|
-
elif detection["detected"]:
|
|
279
|
-
# Check if already researched
|
|
280
|
-
already_researched = check_already_researched(detection["terms"])
|
|
281
|
-
unresearched_terms = [t for t in detection["terms"] if t not in already_researched]
|
|
278
|
+
# Log all detections
|
|
279
|
+
log_detection(prompt, detection, detection["detected"])
|
|
282
280
|
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
# Inject context if needed
|
|
289
|
-
if should_inject:
|
|
290
|
-
terms_str = ", ".join(detection.get("unresearched", detection["terms"])[:5])
|
|
281
|
+
# Inject context if detected
|
|
282
|
+
if detection["detected"]:
|
|
283
|
+
terms_str = ", ".join(detection["terms"][:5]) if detection["terms"] else "technical question"
|
|
284
|
+
confidence = detection["confidence"]
|
|
291
285
|
|
|
286
|
+
# Build the injection message
|
|
292
287
|
injection = f"""
|
|
293
288
|
<user-prompt-submit-hook>
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
{"
|
|
297
|
-
|
|
298
|
-
MANDATORY
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
289
|
+
RESEARCH REQUIRED - {confidence.upper()} CONFIDENCE
|
|
290
|
+
Detected: {terms_str}
|
|
291
|
+
{"MODE: Active API Development Workflow - STRICT ENFORCEMENT" if active_workflow else ""}
|
|
292
|
+
|
|
293
|
+
MANDATORY BEFORE ANSWERING:
|
|
294
|
+
|
|
295
|
+
1. USE CONTEXT7 FIRST:
|
|
296
|
+
- Call mcp__context7__resolve-library-id to find the library
|
|
297
|
+
- Call mcp__context7__get-library-docs to get CURRENT documentation
|
|
298
|
+
- This gives you the ACTUAL source of truth
|
|
299
|
+
|
|
300
|
+
2. USE WEBSEARCH (2-3 SEARCHES MINIMUM):
|
|
301
|
+
- Search for official documentation
|
|
302
|
+
- Search with different phrasings to get comprehensive coverage
|
|
303
|
+
- Search for recent updates, changes, or known issues
|
|
304
|
+
- Example searches:
|
|
305
|
+
* "[topic] official documentation"
|
|
306
|
+
* "[topic] API reference guide"
|
|
307
|
+
* "[topic] latest updates 2024 2025"
|
|
308
|
+
|
|
309
|
+
3. NEVER TRUST TRAINING DATA:
|
|
310
|
+
- Training data can be months or years outdated
|
|
311
|
+
- APIs change constantly
|
|
312
|
+
- Features get added, deprecated, or modified
|
|
313
|
+
- Parameter names and types change
|
|
314
|
+
|
|
315
|
+
4. CITE YOUR SOURCES:
|
|
316
|
+
- After researching, mention where the information came from
|
|
317
|
+
- Include links when available
|
|
318
|
+
|
|
319
|
+
RESEARCH FIRST. ANSWER SECOND.
|
|
309
320
|
</user-prompt-submit-hook>
|
|
310
321
|
"""
|
|
311
322
|
print(injection)
|
|
312
323
|
|
|
313
|
-
# Always allow the prompt to proceed
|
|
314
324
|
sys.exit(0)
|
|
315
325
|
|
|
316
326
|
|
package/package.json
CHANGED