create-merlin-brain 5.0.2 → 5.3.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.
Files changed (101) hide show
  1. package/README.md +25 -4
  2. package/bin/install.cjs +20 -0
  3. package/dist/server/server.d.ts.map +1 -1
  4. package/dist/server/server.js +75 -0
  5. package/dist/server/server.js.map +1 -1
  6. package/dist/server/tools/context.d.ts.map +1 -1
  7. package/dist/server/tools/context.js +60 -0
  8. package/dist/server/tools/context.js.map +1 -1
  9. package/dist/server/tools/project-picture.d.ts +17 -0
  10. package/dist/server/tools/project-picture.d.ts.map +1 -0
  11. package/dist/server/tools/project-picture.js +204 -0
  12. package/dist/server/tools/project-picture.js.map +1 -0
  13. package/dist/server/tools/types.d.ts +24 -0
  14. package/dist/server/tools/types.d.ts.map +1 -1
  15. package/files/agents/challenger-academic.md +8 -0
  16. package/files/agents/challenger-arbiter.md +8 -0
  17. package/files/agents/challenger-insider.md +8 -0
  18. package/files/agents/code-organization-supervisor.md +8 -0
  19. package/files/agents/codex-planner.md +36 -9
  20. package/files/agents/context-guardian.md +8 -0
  21. package/files/agents/docs-keeper.md +8 -0
  22. package/files/agents/dry-refactor.md +8 -0
  23. package/files/agents/elite-code-refactorer.md +8 -0
  24. package/files/agents/hardening-guard.md +8 -0
  25. package/files/agents/implementation-dev.md +8 -0
  26. package/files/agents/merlin-access-control-reviewer.md +8 -0
  27. package/files/agents/merlin-api-designer.md +8 -0
  28. package/files/agents/merlin-codebase-mapper.md +8 -0
  29. package/files/agents/merlin-debugger.md +8 -0
  30. package/files/agents/merlin-dependency-auditor.md +8 -0
  31. package/files/agents/merlin-edge-case-hunter.md +8 -0
  32. package/files/agents/merlin-executor.md +8 -0
  33. package/files/agents/merlin-frontend.md +8 -0
  34. package/files/agents/merlin-input-validator.md +8 -0
  35. package/files/agents/merlin-integration-checker.md +8 -0
  36. package/files/agents/merlin-migrator.md +8 -0
  37. package/files/agents/merlin-milestone-auditor.md +8 -0
  38. package/files/agents/merlin-party-review.md +8 -0
  39. package/files/agents/merlin-performance.md +8 -0
  40. package/files/agents/merlin-planner.md +9 -1
  41. package/files/agents/merlin-researcher.md +8 -0
  42. package/files/agents/merlin-reviewer.md +8 -0
  43. package/files/agents/merlin-sast-reviewer.md +8 -0
  44. package/files/agents/merlin-secret-scanner.md +8 -0
  45. package/files/agents/merlin-security.md +8 -0
  46. package/files/agents/merlin-verifier.md +8 -0
  47. package/files/agents/merlin-work-verifier.md +8 -0
  48. package/files/agents/merlin.md +8 -0
  49. package/files/agents/ops-railway.md +8 -0
  50. package/files/agents/orchestrator-retrofit.md +8 -0
  51. package/files/agents/product-spec.md +8 -0
  52. package/files/agents/remotion.md +8 -0
  53. package/files/agents/system-architect.md +8 -0
  54. package/files/agents/tests-qa.md +8 -0
  55. package/files/commands/merlin/course-correct.md +8 -0
  56. package/files/commands/merlin/design-audit.md +92 -0
  57. package/files/commands/merlin/health.md +8 -0
  58. package/files/commands/merlin/next.md +8 -0
  59. package/files/commands/merlin/optimize.md +89 -0
  60. package/files/commands/merlin/polish.md +99 -0
  61. package/files/commands/merlin/quick.md +8 -0
  62. package/files/commands/merlin/readiness-gate.md +8 -0
  63. package/files/commands/merlin/redesign.md +108 -0
  64. package/files/loop/README.md +11 -0
  65. package/files/merlin/skills/SKILLS-INDEX.md +16 -1
  66. package/files/merlin/skills/TASK-OPTIMIZER.json +310 -0
  67. package/files/merlin/skills/coding/focus-mode.md +8 -0
  68. package/files/merlin/skills/design/emil-design-eng.md +31 -0
  69. package/files/merlin/skills/design/impeccable.md +36 -0
  70. package/files/merlin/skills/duo/offer.md +20 -7
  71. package/files/merlin/skills/duo/on.md +26 -17
  72. package/files/merlin/skills/duo/status.md +10 -3
  73. package/files/merlin/templates/DEBUG.md +11 -0
  74. package/files/merlin/templates/UAT.md +11 -0
  75. package/files/merlin/templates/phase-prompt.md +11 -0
  76. package/files/merlin/templates/project.md +11 -0
  77. package/files/merlin/templates/requirements.md +11 -0
  78. package/files/merlin/templates/roadmap.md +11 -0
  79. package/files/merlin/templates/state.md +11 -0
  80. package/files/merlin/templates/verification-report.md +11 -0
  81. package/files/merlin/workflows/execute-phase.md +11 -0
  82. package/files/merlin/workflows/plan-phase.md +11 -0
  83. package/files/merlin/workflows/progress.md +11 -0
  84. package/files/merlin/workflows/resume-project.md +11 -0
  85. package/files/merlin/workflows/verify-phase.md +11 -0
  86. package/files/merlin/workflows/verify-work.md +11 -0
  87. package/files/merlin-system-prompt.txt +35 -1
  88. package/files/rules/codex-routing.md +19 -0
  89. package/files/rules/duo-routing.md +109 -10
  90. package/files/rules/merlin-routing.md +40 -0
  91. package/files/scripts/codex-as.sh +5 -2
  92. package/files/scripts/design-intent-detect.sh +8 -0
  93. package/files/scripts/duo-badge.sh +3 -5
  94. package/files/scripts/duo-installed.sh +3 -3
  95. package/files/scripts/duo-mode-read.sh +30 -10
  96. package/files/scripts/duo-mode-write.sh +28 -3
  97. package/files/scripts/duo-pre-route.sh +2 -8
  98. package/files/scripts/install-design-skills.sh +86 -0
  99. package/files/scripts/merlin-codex.sh +9 -4
  100. package/files/scripts/task-optimize.sh +335 -0
  101. 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.0.2",
3
+ "version": "5.3.1",
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",