create-merlin-brain 5.0.2 → 5.3.2
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/README.md +25 -4
- package/bin/install.cjs +95 -0
- package/dist/server/server.d.ts.map +1 -1
- package/dist/server/server.js +75 -0
- package/dist/server/server.js.map +1 -1
- package/dist/server/tools/context.d.ts.map +1 -1
- package/dist/server/tools/context.js +60 -0
- package/dist/server/tools/context.js.map +1 -1
- package/dist/server/tools/project-picture.d.ts +17 -0
- package/dist/server/tools/project-picture.d.ts.map +1 -0
- package/dist/server/tools/project-picture.js +204 -0
- package/dist/server/tools/project-picture.js.map +1 -0
- package/dist/server/tools/types.d.ts +24 -0
- package/dist/server/tools/types.d.ts.map +1 -1
- package/files/agents/challenger-academic.md +8 -0
- package/files/agents/challenger-arbiter.md +8 -0
- package/files/agents/challenger-insider.md +8 -0
- package/files/agents/code-organization-supervisor.md +8 -0
- package/files/agents/codex-planner.md +36 -9
- package/files/agents/context-guardian.md +8 -0
- package/files/agents/docs-keeper.md +8 -0
- package/files/agents/dry-refactor.md +8 -0
- package/files/agents/elite-code-refactorer.md +8 -0
- package/files/agents/hardening-guard.md +8 -0
- package/files/agents/implementation-dev.md +8 -0
- package/files/agents/merlin-access-control-reviewer.md +8 -0
- package/files/agents/merlin-api-designer.md +8 -0
- package/files/agents/merlin-codebase-mapper.md +8 -0
- package/files/agents/merlin-debugger.md +8 -0
- package/files/agents/merlin-dependency-auditor.md +8 -0
- package/files/agents/merlin-edge-case-hunter.md +8 -0
- package/files/agents/merlin-executor.md +8 -0
- package/files/agents/merlin-frontend.md +8 -0
- package/files/agents/merlin-input-validator.md +8 -0
- package/files/agents/merlin-integration-checker.md +8 -0
- package/files/agents/merlin-migrator.md +8 -0
- package/files/agents/merlin-milestone-auditor.md +8 -0
- package/files/agents/merlin-party-review.md +8 -0
- package/files/agents/merlin-performance.md +8 -0
- package/files/agents/merlin-planner.md +9 -1
- package/files/agents/merlin-researcher.md +8 -0
- package/files/agents/merlin-reviewer.md +8 -0
- package/files/agents/merlin-sast-reviewer.md +8 -0
- package/files/agents/merlin-secret-scanner.md +8 -0
- package/files/agents/merlin-security.md +8 -0
- package/files/agents/merlin-verifier.md +8 -0
- package/files/agents/merlin-work-verifier.md +8 -0
- package/files/agents/merlin.md +8 -0
- package/files/agents/ops-railway.md +8 -0
- package/files/agents/orchestrator-retrofit.md +8 -0
- package/files/agents/product-spec.md +8 -0
- package/files/agents/remotion.md +8 -0
- package/files/agents/system-architect.md +8 -0
- package/files/agents/tests-qa.md +8 -0
- package/files/commands/merlin/course-correct.md +8 -0
- package/files/commands/merlin/design-audit.md +92 -0
- package/files/commands/merlin/health.md +8 -0
- package/files/commands/merlin/next.md +8 -0
- package/files/commands/merlin/optimize.md +89 -0
- package/files/commands/merlin/polish.md +99 -0
- package/files/commands/merlin/quick.md +8 -0
- package/files/commands/merlin/readiness-gate.md +8 -0
- package/files/commands/merlin/redesign.md +108 -0
- package/files/loop/README.md +11 -0
- package/files/merlin/skills/SKILLS-INDEX.md +16 -1
- package/files/merlin/skills/TASK-OPTIMIZER.json +310 -0
- package/files/merlin/skills/coding/focus-mode.md +8 -0
- package/files/merlin/skills/design/emil-design-eng.md +31 -0
- package/files/merlin/skills/design/impeccable.md +36 -0
- package/files/merlin/skills/duo/offer.md +20 -7
- package/files/merlin/skills/duo/on.md +26 -17
- package/files/merlin/skills/duo/status.md +10 -3
- package/files/merlin/templates/DEBUG.md +11 -0
- package/files/merlin/templates/UAT.md +11 -0
- package/files/merlin/templates/phase-prompt.md +11 -0
- package/files/merlin/templates/project.md +11 -0
- package/files/merlin/templates/requirements.md +11 -0
- package/files/merlin/templates/roadmap.md +11 -0
- package/files/merlin/templates/state.md +11 -0
- package/files/merlin/templates/verification-report.md +11 -0
- package/files/merlin/workflows/execute-phase.md +11 -0
- package/files/merlin/workflows/plan-phase.md +11 -0
- package/files/merlin/workflows/progress.md +11 -0
- package/files/merlin/workflows/resume-project.md +11 -0
- package/files/merlin/workflows/verify-phase.md +11 -0
- package/files/merlin/workflows/verify-work.md +11 -0
- package/files/merlin-system-prompt.txt +35 -1
- package/files/rules/codex-routing.md +19 -0
- package/files/rules/duo-routing.md +109 -10
- package/files/rules/merlin-routing.md +40 -0
- package/files/scripts/codex-as.sh +5 -2
- package/files/scripts/design-intent-detect.sh +8 -0
- package/files/scripts/duo-badge.sh +3 -5
- package/files/scripts/duo-installed.sh +3 -3
- package/files/scripts/duo-mode-read.sh +30 -10
- package/files/scripts/duo-mode-write.sh +28 -3
- package/files/scripts/duo-pre-route.sh +2 -8
- package/files/scripts/install-design-skills.sh +86 -0
- package/files/scripts/merlin-codex.sh +9 -4
- package/files/scripts/task-optimize.sh +335 -0
- package/package.json +1 -1
|
@@ -0,0 +1,335 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# task-optimize.sh — Universal Task Optimizer for Merlin v5.3.0
|
|
3
|
+
# Usage: task-optimize.sh --task "<text>" [--registry <path>] [--self-test]
|
|
4
|
+
# Output: single JSON line {"score":<0-100>,"intent":"<id>","skills":["<id1>"],"agent":"<agent>","matched_phrases":["..."]}
|
|
5
|
+
# Always exits 0 in normal usage. --self-test exits non-zero on failure.
|
|
6
|
+
# Times out at 500ms.
|
|
7
|
+
|
|
8
|
+
set -euo pipefail
|
|
9
|
+
|
|
10
|
+
TASK=""
|
|
11
|
+
REGISTRY=""
|
|
12
|
+
SELF_TEST=false
|
|
13
|
+
|
|
14
|
+
while [[ $# -gt 0 ]]; do
|
|
15
|
+
case "$1" in
|
|
16
|
+
--task) TASK="${2:-}"; shift 2 ;;
|
|
17
|
+
--registry) REGISTRY="${2:-}"; shift 2 ;;
|
|
18
|
+
--self-test) SELF_TEST=true; shift ;;
|
|
19
|
+
*) shift ;;
|
|
20
|
+
esac
|
|
21
|
+
done
|
|
22
|
+
|
|
23
|
+
# Default registry locations
|
|
24
|
+
if [[ -z "$REGISTRY" ]]; then
|
|
25
|
+
if [[ -f "$HOME/.claude/merlin/skills/TASK-OPTIMIZER.json" ]]; then
|
|
26
|
+
REGISTRY="$HOME/.claude/merlin/skills/TASK-OPTIMIZER.json"
|
|
27
|
+
elif [[ -f "$HOME/.claude/skills/merlin/TASK-OPTIMIZER.json" ]]; then
|
|
28
|
+
REGISTRY="$HOME/.claude/skills/merlin/TASK-OPTIMIZER.json"
|
|
29
|
+
else
|
|
30
|
+
# Fallback to package location (for testing during development)
|
|
31
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
32
|
+
REGISTRY="$SCRIPT_DIR/../merlin/skills/TASK-OPTIMIZER.json"
|
|
33
|
+
fi
|
|
34
|
+
fi
|
|
35
|
+
|
|
36
|
+
# ═══════════════════════════════════════════════════════════════
|
|
37
|
+
# SELF-TEST MODE
|
|
38
|
+
# ═══════════════════════════════════════════════════════════════
|
|
39
|
+
if [[ "$SELF_TEST" == "true" ]]; then
|
|
40
|
+
TMPTEST=$(mktemp /tmp/task-opt-test.XXXXXX.py)
|
|
41
|
+
trap 'rm -f "$TMPTEST"' EXIT
|
|
42
|
+
|
|
43
|
+
cat > "$TMPTEST" << 'PYTESTEOF'
|
|
44
|
+
import sys
|
|
45
|
+
import os
|
|
46
|
+
import json
|
|
47
|
+
import subprocess
|
|
48
|
+
|
|
49
|
+
# Test cases: (task, expected_intent, expected_agent, min_score)
|
|
50
|
+
TEST_CASES = [
|
|
51
|
+
("this looks ugly, redesign it", "design", "ui-builder", 25),
|
|
52
|
+
("make it pretty", "design", "ui-builder", 25),
|
|
53
|
+
("redesign the homepage", "design", "ui-builder", 25),
|
|
54
|
+
("design review of the new component", "design", "ui-builder", 25),
|
|
55
|
+
("WCAG compliance audit", "a11y", "hardening-guard", 25),
|
|
56
|
+
("accessibility fixes for the form", "a11y", "hardening-guard", 25),
|
|
57
|
+
("responsive design issue on mobile", "design", "ui-builder", 25),
|
|
58
|
+
("mobile broken on iPhone", "design", "ui-builder", 25),
|
|
59
|
+
("dark mode toggle implementation", "design", "ui-builder", 25),
|
|
60
|
+
("theme switching support", "design", "ui-builder", 25),
|
|
61
|
+
("match Stripe design quality", "design", "ui-builder", 25),
|
|
62
|
+
("loading state for the form", "design", "ui-builder", 25),
|
|
63
|
+
("UX writing for empty states", "design", "ui-builder", 25),
|
|
64
|
+
("error messages copy review", "design", "ui-builder", 25),
|
|
65
|
+
("animate this drawer", "animation-polish", "animation-expert", 25),
|
|
66
|
+
("transitions feel stiff", "animation-polish", "animation-expert", 25),
|
|
67
|
+
("add motion to the cards", "animation-polish", "animation-expert", 25),
|
|
68
|
+
("polish the micro-interactions", "animation-polish", "animation-expert", 25),
|
|
69
|
+
("add OAuth login flow", "security", "hardening-guard", 25),
|
|
70
|
+
("CSRF protection check", "security", "hardening-guard", 25),
|
|
71
|
+
("input validation hardening", "security", "hardening-guard", 25),
|
|
72
|
+
("write unit tests for the parser", "testing", "tests-qa", 25),
|
|
73
|
+
("TDD workflow for the API", "testing", "tests-qa", 25),
|
|
74
|
+
("Jest test coverage", "testing", "tests-qa", 25),
|
|
75
|
+
("REST API endpoint design", "api", "merlin-api-designer", 25),
|
|
76
|
+
("OpenAPI spec for the v2 routes", "api", "merlin-api-designer", 25),
|
|
77
|
+
("GraphQL schema design", "api", "merlin-api-designer", 25),
|
|
78
|
+
("Core Web Vitals optimization", "performance", "merlin-performance", 25),
|
|
79
|
+
("bundle size reduction", "performance", "merlin-performance", 25),
|
|
80
|
+
("lighthouse score improvements", "performance", "merlin-performance", 25),
|
|
81
|
+
("debug this null pointer crash", "debug", "merlin-debugger", 25),
|
|
82
|
+
("investigate the broken deploy", "debug", "merlin-debugger", 25),
|
|
83
|
+
("Docker containerization for the worker", "docker", "ops-railway", 25),
|
|
84
|
+
("multi-stage build for the API", "docker", "ops-railway", 25),
|
|
85
|
+
("Stripe payment integration", "payments", "implementation-dev", 25),
|
|
86
|
+
("subscription billing setup", "payments", "implementation-dev", 25),
|
|
87
|
+
("webhook signature verification", "webhooks", "implementation-dev", 25),
|
|
88
|
+
("send email via Gmail API", "email", "implementation-dev", 25),
|
|
89
|
+
("WhatsApp business API integration", "whatsapp", "implementation-dev", 25),
|
|
90
|
+
("Telegram bot for notifications", "telegram", "implementation-dev", 25),
|
|
91
|
+
("Google Sheets automation", "sheets", "implementation-dev", 25),
|
|
92
|
+
("brainstorm options for onboarding", "brainstorm", "merlin-researcher", 25),
|
|
93
|
+
("React component refactor", "react", "merlin-frontend", 25),
|
|
94
|
+
("useState hook pattern", "react", "merlin-frontend", 25),
|
|
95
|
+
("Next.js server component", "react", "merlin-frontend", 25),
|
|
96
|
+
("fix the database query", "none", "", 0),
|
|
97
|
+
("rename a variable", "none", "", 0),
|
|
98
|
+
("update README", "none", "", 0),
|
|
99
|
+
]
|
|
100
|
+
|
|
101
|
+
def run_test(script_path, registry_path, task, expected_intent, expected_agent, min_score):
|
|
102
|
+
"""Run the optimizer and check result."""
|
|
103
|
+
try:
|
|
104
|
+
result = subprocess.run(
|
|
105
|
+
['bash', script_path, '--task', task, '--registry', registry_path],
|
|
106
|
+
capture_output=True,
|
|
107
|
+
text=True,
|
|
108
|
+
timeout=5
|
|
109
|
+
)
|
|
110
|
+
output = result.stdout.strip()
|
|
111
|
+
if not output:
|
|
112
|
+
return False, f"empty output (stderr: {result.stderr.strip()})"
|
|
113
|
+
|
|
114
|
+
data = json.loads(output)
|
|
115
|
+
actual_intent = data.get('intent', '')
|
|
116
|
+
actual_agent = data.get('agent', '')
|
|
117
|
+
actual_score = data.get('score', 0)
|
|
118
|
+
|
|
119
|
+
# Check conditions
|
|
120
|
+
if expected_intent == "none":
|
|
121
|
+
# For "none" cases, we expect score < min_score (which is 0, so score must be 0)
|
|
122
|
+
if actual_intent != "none" and actual_score >= 25:
|
|
123
|
+
return False, f"got intent={actual_intent}, agent={actual_agent}, score={actual_score} (expected none)"
|
|
124
|
+
return True, ""
|
|
125
|
+
else:
|
|
126
|
+
if actual_intent != expected_intent:
|
|
127
|
+
return False, f"intent={actual_intent} (expected {expected_intent})"
|
|
128
|
+
if actual_agent != expected_agent:
|
|
129
|
+
return False, f"agent={actual_agent} (expected {expected_agent})"
|
|
130
|
+
if actual_score < min_score:
|
|
131
|
+
return False, f"score={actual_score} (expected >= {min_score})"
|
|
132
|
+
return True, ""
|
|
133
|
+
except json.JSONDecodeError as e:
|
|
134
|
+
return False, f"invalid JSON: {e}"
|
|
135
|
+
except subprocess.TimeoutExpired:
|
|
136
|
+
return False, "timeout"
|
|
137
|
+
except Exception as e:
|
|
138
|
+
return False, str(e)
|
|
139
|
+
|
|
140
|
+
def main():
|
|
141
|
+
script_path = os.environ.get('_SCRIPT_PATH', '')
|
|
142
|
+
registry_path = os.environ.get('_REGISTRY_PATH', '')
|
|
143
|
+
|
|
144
|
+
if not script_path or not registry_path:
|
|
145
|
+
print("Error: _SCRIPT_PATH and _REGISTRY_PATH must be set", file=sys.stderr)
|
|
146
|
+
sys.exit(1)
|
|
147
|
+
|
|
148
|
+
print("\n" + "=" * 60)
|
|
149
|
+
print("Universal Task Optimizer — Self-Test Suite")
|
|
150
|
+
print("=" * 60 + "\n")
|
|
151
|
+
|
|
152
|
+
passed = 0
|
|
153
|
+
failed = 0
|
|
154
|
+
|
|
155
|
+
for task, expected_intent, expected_agent, min_score in TEST_CASES:
|
|
156
|
+
ok, reason = run_test(script_path, registry_path, task, expected_intent, expected_agent, min_score)
|
|
157
|
+
if ok:
|
|
158
|
+
print(f"PASS: '{task[:50]}...' => {expected_intent}")
|
|
159
|
+
passed += 1
|
|
160
|
+
else:
|
|
161
|
+
print(f"FAIL: '{task[:50]}...' => {reason}")
|
|
162
|
+
failed += 1
|
|
163
|
+
|
|
164
|
+
print("\n" + "=" * 60)
|
|
165
|
+
print(f"RESULTS: {passed} passed, {failed} failed")
|
|
166
|
+
print("=" * 60)
|
|
167
|
+
|
|
168
|
+
sys.exit(0 if failed == 0 else 1)
|
|
169
|
+
|
|
170
|
+
if __name__ == '__main__':
|
|
171
|
+
main()
|
|
172
|
+
PYTESTEOF
|
|
173
|
+
|
|
174
|
+
export _SCRIPT_PATH="${BASH_SOURCE[0]}"
|
|
175
|
+
export _REGISTRY_PATH="$REGISTRY"
|
|
176
|
+
python3 "$TMPTEST"
|
|
177
|
+
exit $?
|
|
178
|
+
fi
|
|
179
|
+
|
|
180
|
+
# ═══════════════════════════════════════════════════════════════
|
|
181
|
+
# MAIN SCORER
|
|
182
|
+
# ═══════════════════════════════════════════════════════════════
|
|
183
|
+
|
|
184
|
+
THRESHOLD="${MERLIN_TASK_OPT_THRESHOLD:-25}"
|
|
185
|
+
|
|
186
|
+
# Write the scorer to a temp file so timeout can exec it cleanly
|
|
187
|
+
TMPSCRIPT=$(mktemp /tmp/task-opt-score.XXXXXX.py)
|
|
188
|
+
trap 'rm -f "$TMPSCRIPT"' EXIT
|
|
189
|
+
|
|
190
|
+
cat > "$TMPSCRIPT" << 'PYEOF'
|
|
191
|
+
import os
|
|
192
|
+
import sys
|
|
193
|
+
import json
|
|
194
|
+
import re
|
|
195
|
+
|
|
196
|
+
def main():
|
|
197
|
+
task = os.environ.get("_OPT_TASK", "")
|
|
198
|
+
registry_path = os.environ.get("_OPT_REGISTRY", "")
|
|
199
|
+
threshold = int(os.environ.get("_OPT_THRESHOLD", "25"))
|
|
200
|
+
|
|
201
|
+
# Default output
|
|
202
|
+
default_output = {"score": 0, "intent": "none", "skills": [], "agent": "", "matched_phrases": []}
|
|
203
|
+
|
|
204
|
+
# Load registry
|
|
205
|
+
if not registry_path or not os.path.exists(registry_path):
|
|
206
|
+
print(json.dumps(default_output))
|
|
207
|
+
return
|
|
208
|
+
|
|
209
|
+
try:
|
|
210
|
+
with open(registry_path, 'r') as f:
|
|
211
|
+
registry = json.load(f)
|
|
212
|
+
except (json.JSONDecodeError, IOError):
|
|
213
|
+
print(json.dumps(default_output))
|
|
214
|
+
return
|
|
215
|
+
|
|
216
|
+
task_lower = task.lower()
|
|
217
|
+
skills_list = registry.get("skills", [])
|
|
218
|
+
|
|
219
|
+
# Score each skill
|
|
220
|
+
skill_scores = []
|
|
221
|
+
for skill in skills_list:
|
|
222
|
+
triggers = skill.get("triggers", [])
|
|
223
|
+
weight = skill.get("weight", 1.0)
|
|
224
|
+
matched = []
|
|
225
|
+
|
|
226
|
+
for trigger in triggers:
|
|
227
|
+
trigger_lower = trigger.lower()
|
|
228
|
+
# Use word boundary matching for single words, substring for multi-word
|
|
229
|
+
if ' ' in trigger_lower:
|
|
230
|
+
# Multi-word: simple substring match
|
|
231
|
+
if trigger_lower in task_lower:
|
|
232
|
+
matched.append(trigger_lower)
|
|
233
|
+
else:
|
|
234
|
+
# Single word: word boundary match
|
|
235
|
+
pattern = r'\b' + re.escape(trigger_lower) + r'\b'
|
|
236
|
+
if re.search(pattern, task_lower):
|
|
237
|
+
matched.append(trigger_lower)
|
|
238
|
+
|
|
239
|
+
if matched:
|
|
240
|
+
# Score: 25 per unique match * weight, capped at 100
|
|
241
|
+
raw_score = len(matched) * 25 * weight
|
|
242
|
+
score = min(int(raw_score), 100)
|
|
243
|
+
skill_scores.append({
|
|
244
|
+
"skill": skill,
|
|
245
|
+
"score": score,
|
|
246
|
+
"matched": matched
|
|
247
|
+
})
|
|
248
|
+
|
|
249
|
+
if not skill_scores:
|
|
250
|
+
print(json.dumps(default_output))
|
|
251
|
+
return
|
|
252
|
+
|
|
253
|
+
# Sort by score descending
|
|
254
|
+
skill_scores.sort(key=lambda x: x["score"], reverse=True)
|
|
255
|
+
top = skill_scores[0]
|
|
256
|
+
|
|
257
|
+
if top["score"] < threshold:
|
|
258
|
+
print(json.dumps(default_output))
|
|
259
|
+
return
|
|
260
|
+
|
|
261
|
+
top_skill = top["skill"]
|
|
262
|
+
result_skills = [top_skill["id"]]
|
|
263
|
+
all_matched = list(top["matched"])
|
|
264
|
+
|
|
265
|
+
# Check bundles_with
|
|
266
|
+
bundles = top_skill.get("bundles_with", [])
|
|
267
|
+
for bundle_id in bundles:
|
|
268
|
+
# Find the bundle skill and check if it also matched
|
|
269
|
+
for ss in skill_scores[1:]:
|
|
270
|
+
if ss["skill"]["id"] == bundle_id and ss["matched"]:
|
|
271
|
+
if bundle_id not in result_skills:
|
|
272
|
+
result_skills.append(bundle_id)
|
|
273
|
+
all_matched.extend(ss["matched"])
|
|
274
|
+
break
|
|
275
|
+
|
|
276
|
+
# Check intent_overrides
|
|
277
|
+
overrides = registry.get("intent_overrides", {})
|
|
278
|
+
for override_key, override_val in overrides.items():
|
|
279
|
+
match_all = override_val.get("match_all", [])
|
|
280
|
+
if all(ma.lower() in task_lower for ma in match_all):
|
|
281
|
+
# Override applies
|
|
282
|
+
result_skills = override_val.get("skills", result_skills)
|
|
283
|
+
result_intent = override_key
|
|
284
|
+
result_agent = override_val.get("agent", top_skill.get("agent", ""))
|
|
285
|
+
output = {
|
|
286
|
+
"score": top["score"],
|
|
287
|
+
"intent": result_intent,
|
|
288
|
+
"skills": result_skills,
|
|
289
|
+
"agent": result_agent,
|
|
290
|
+
"matched_phrases": list(set(all_matched))
|
|
291
|
+
}
|
|
292
|
+
print(json.dumps(output))
|
|
293
|
+
return
|
|
294
|
+
|
|
295
|
+
# Build normal output
|
|
296
|
+
output = {
|
|
297
|
+
"score": top["score"],
|
|
298
|
+
"intent": top_skill.get("intent", "none"),
|
|
299
|
+
"skills": result_skills,
|
|
300
|
+
"agent": top_skill.get("agent", ""),
|
|
301
|
+
"matched_phrases": list(set(all_matched))
|
|
302
|
+
}
|
|
303
|
+
print(json.dumps(output))
|
|
304
|
+
|
|
305
|
+
if __name__ == '__main__':
|
|
306
|
+
main()
|
|
307
|
+
PYEOF
|
|
308
|
+
|
|
309
|
+
# Export inputs via env — never interpolated into code
|
|
310
|
+
export _OPT_TASK="$TASK"
|
|
311
|
+
export _OPT_REGISTRY="$REGISTRY"
|
|
312
|
+
export _OPT_THRESHOLD="$THRESHOLD"
|
|
313
|
+
|
|
314
|
+
# Run with 500ms timeout — try gtimeout (macOS Homebrew coreutils), then timeout, then perl alarm
|
|
315
|
+
_TIMEOUT_CMD=""
|
|
316
|
+
if command -v gtimeout >/dev/null 2>&1; then
|
|
317
|
+
_TIMEOUT_CMD="gtimeout 0.5"
|
|
318
|
+
elif timeout --version >/dev/null 2>&1; then
|
|
319
|
+
_TIMEOUT_CMD="timeout 0.5"
|
|
320
|
+
fi
|
|
321
|
+
|
|
322
|
+
if [[ -n "$_TIMEOUT_CMD" ]]; then
|
|
323
|
+
OUT=$($_TIMEOUT_CMD python3 "$TMPSCRIPT" 2>/dev/null) || OUT=""
|
|
324
|
+
else
|
|
325
|
+
OUT=$(perl -e 'alarm 1; exec @ARGV' -- python3 "$TMPSCRIPT" 2>/dev/null) || OUT=""
|
|
326
|
+
fi
|
|
327
|
+
|
|
328
|
+
# Validate JSON; fall back to safe default
|
|
329
|
+
if python3 -c "import json,sys; json.loads(sys.stdin.read())" <<< "$OUT" 2>/dev/null; then
|
|
330
|
+
echo "$OUT"
|
|
331
|
+
else
|
|
332
|
+
echo '{"score":0,"intent":"none","skills":[],"agent":"","matched_phrases":[]}'
|
|
333
|
+
fi
|
|
334
|
+
|
|
335
|
+
exit 0
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-merlin-brain",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.3.2",
|
|
4
4
|
"description": "Merlin - The Ultimate AI Brain for Claude Code, Codex, and other AI CLIs. One install: workflows, agents, loop, and Sights MCP server.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/server/index.js",
|