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.
- package/README.md +25 -4
- package/bin/install.cjs +20 -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
|
@@ -14,23 +14,37 @@ Duo mode runs two brains on the same task: Claude and Codex. Tasks that produce
|
|
|
14
14
|
|
|
15
15
|
**Schema:**
|
|
16
16
|
```json
|
|
17
|
-
{"enabled": bool, "sinceISO": "ISO8601 | null", "lastToggleReason": "string | null"}
|
|
17
|
+
{"enabled": bool, "sinceISO": "ISO8601 | null", "lastToggleReason": "string | null", "pair": "claude+codex" | "claude+claude" | "codex+codex" | null}
|
|
18
18
|
```
|
|
19
19
|
|
|
20
20
|
**Default (first install):**
|
|
21
21
|
```json
|
|
22
|
-
{"enabled": false, "sinceISO": null, "lastToggleReason": null}
|
|
22
|
+
{"enabled": false, "sinceISO": null, "lastToggleReason": null, "pair": null}
|
|
23
23
|
```
|
|
24
24
|
|
|
25
25
|
**Auto-expire:** If `sinceISO` is more than 24 hours old at read time, treat as disabled. The file is NOT rewritten on read — expiry is a read-time interpretation. Approximately session-sticky.
|
|
26
26
|
|
|
27
27
|
**Toggle:** `Skill("merlin:duo", args="on|off|status|unsuppress")` or natural language (see `merlin-routing.md` for intent triggers).
|
|
28
28
|
|
|
29
|
-
**Install gate:**
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
29
|
+
**Install gate:** `duo-installed.sh` is always-pass — duo is always available. Pair selection (which specialists are used) is determined separately at toggle time via `codex-installed.sh`.
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## Pair selection
|
|
34
|
+
|
|
35
|
+
Duo runs as one of two pairs, selected at toggle-on time and persisted in `duo-mode.json` under the `pair` field:
|
|
36
|
+
|
|
37
|
+
| Pair | When selected | Behavior |
|
|
38
|
+
|---|---|---|
|
|
39
|
+
| `claude+codex` | Codex installed (`codex-installed.sh` exits 0) at toggle time | Claude + Codex collaboration. Existing duo behavior. |
|
|
40
|
+
| `claude+claude` | Codex not installed at toggle time | Twin-Claude — two different Claude specialists tag-team each task. |
|
|
41
|
+
| `codex+codex` | Running under merlin-codex (`MERLIN_RUNTIME=codex`) | Codex-only — Codex orchestrator + two Codex specialists per task. Used when Claude Code isn't available. |
|
|
42
|
+
|
|
43
|
+
Pair auto-detection at toggle-on time: `MERLIN_RUNTIME=codex` → `codex+codex`; else `codex-installed.sh` exit 0 → `claude+codex`; else → `claude+claude`.
|
|
44
|
+
|
|
45
|
+
Pair is fixed for the life of the duo session (24h auto-expire). Disable + re-enable to re-detect.
|
|
46
|
+
|
|
47
|
+
The `duo-installed.sh` gate is always-pass now — duo is always available. `codex-installed.sh` is what determines pair for claude-orchestrated sessions.
|
|
34
48
|
|
|
35
49
|
---
|
|
36
50
|
|
|
@@ -38,7 +52,7 @@ Duo mode runs two brains on the same task: Claude and Codex. Tasks that produce
|
|
|
38
52
|
|
|
39
53
|
When multiple modes are enabled simultaneously:
|
|
40
54
|
|
|
41
|
-
1. **Duo active** (enabled=true, within 24h
|
|
55
|
+
1. **Duo active** (enabled=true, within 24h) → duo rules in this file win
|
|
42
56
|
2. **Codex-mode active** (enabled=true, within 24h, codex gate passes) → `codex-routing.md` rules apply
|
|
43
57
|
3. **Neither** → solo routing (existing `merlin-routing.md` + `codex-routing.md` rules)
|
|
44
58
|
|
|
@@ -52,7 +66,7 @@ Compute the badge via `~/.claude/scripts/duo-badge.sh` before every action prefi
|
|
|
52
66
|
|
|
53
67
|
| Condition | Badge |
|
|
54
68
|
|---|---|
|
|
55
|
-
| Duo enabled + within 24h
|
|
69
|
+
| Duo enabled + within 24h | `⟡🔮↔🔮 MERLIN·DUO ›` |
|
|
56
70
|
| Any other state | `⟡🔮 MERLIN ›` |
|
|
57
71
|
|
|
58
72
|
**Text-only fallback:** If env `MERLIN_BADGE_TEXTONLY=1`, `duo-badge.sh` returns `[DUO] MERLIN ›` or `MERLIN ›` for terminals that mangle `↔` or emoji.
|
|
@@ -86,6 +100,30 @@ Both brains run independently on the same task. Results are routed to the arbite
|
|
|
86
100
|
|
|
87
101
|
---
|
|
88
102
|
|
|
103
|
+
## Parallel Planning Artifacts
|
|
104
|
+
|
|
105
|
+
Both planners and the arbiter write to canonical paths inside the phase directory. The orchestrator MUST pass these absolute paths in each agent's prompt.
|
|
106
|
+
|
|
107
|
+
| Agent | Writes to | Required prompt args |
|
|
108
|
+
|---|---|---|
|
|
109
|
+
| `merlin-planner` | `<phase-dir>/PLAN-CLAUDE.md` | `output_path` |
|
|
110
|
+
| `codex-planner` | `<phase-dir>/PLAN-CODEX.md` | `output_path` |
|
|
111
|
+
| `challenger-arbiter` | `<phase-dir>/PLAN-FINAL.md` | `output_path`, `claude_plan_path`, `codex_plan_path` |
|
|
112
|
+
|
|
113
|
+
`<phase-dir>` resolution:
|
|
114
|
+
- Numbered phases → `.planning/phases/<NN-slug>/`
|
|
115
|
+
- Ad-hoc duo workflows → `.planning/duo/<slug>/`
|
|
116
|
+
|
|
117
|
+
The orchestrator MUST `mkdir -p <phase-dir>` before spawning the agents.
|
|
118
|
+
|
|
119
|
+
**Contract for planning agents:**
|
|
120
|
+
- The file on disk at `output_path` is the canonical artifact for any agent that receives `output_path` in its prompt. Such agents MUST write the file themselves and MUST NOT return inline content as a fallback.
|
|
121
|
+
- Agents that are part of a parallel pair but do NOT receive `output_path` (e.g. `challenger-academic` in twin-Claude planning, whose frontmatter forbids Write) MAY return inline content; the orchestrator captures and writes the file in that case.
|
|
122
|
+
- If an agent cannot write `output_path` (tool denied, disk error, missing arg), it MUST return a structured error and stop. The orchestrator never reconstructs plans from stdout.
|
|
123
|
+
- If `output_path` is missing or not absolute, the agent MUST refuse with a clear error.
|
|
124
|
+
|
|
125
|
+
---
|
|
126
|
+
|
|
89
127
|
## Routing Matrix — SEQUENTIAL execution
|
|
90
128
|
|
|
91
129
|
One author at a time to prevent file conflicts. Sequential only for tasks that write or modify source files.
|
|
@@ -120,12 +158,74 @@ If decider returns `revise`:
|
|
|
120
158
|
|
|
121
159
|
---
|
|
122
160
|
|
|
161
|
+
## Twin-Claude pair (claude+claude) routing
|
|
162
|
+
|
|
163
|
+
When `pair == claude+claude`, the parallel and sequential matrices use these specialist pairings instead:
|
|
164
|
+
|
|
165
|
+
**PARALLEL:**
|
|
166
|
+
|
|
167
|
+
| Task | Brain A | Brain B | Decider |
|
|
168
|
+
|---|---|---|---|
|
|
169
|
+
| Planning (feature-dev / refactor / product-dev) | `merlin-planner` | `challenger-academic` | `challenger-arbiter` |
|
|
170
|
+
| Code review | `code-review` | `merlin-reviewer` | `challenger-arbiter` (merge + dedupe) |
|
|
171
|
+
| Testing | `tests-qa` | `merlin-edge-case-hunter` | `challenger-arbiter` (merge + dedupe) |
|
|
172
|
+
| Documentation | `docs-keeper` | — | falls back to solo (no second doc specialist available) |
|
|
173
|
+
|
|
174
|
+
**SEQUENTIAL (code write/modify):**
|
|
175
|
+
|
|
176
|
+
| Author | Reviewer | Decider |
|
|
177
|
+
|---|---|---|
|
|
178
|
+
| `implementation-dev` | `code-review` | `reviewer-decider` |
|
|
179
|
+
|
|
180
|
+
**Twin-Claude planning artifact paths:**
|
|
181
|
+
|
|
182
|
+
- `<phase-dir>/PLAN-CLAUDE.md` — written by `merlin-planner` (uses output_path arg).
|
|
183
|
+
- `<phase-dir>/PLAN-CHALLENGE.md` — challenger-academic does NOT receive output_path (its frontmatter forbids Write). The orchestrator captures the agent's returned content and writes the file.
|
|
184
|
+
- `<phase-dir>/PLAN-FINAL.md` — written by `challenger-arbiter`. In claude+claude, the arbiter's prompt receives `claude_plan_path` and `challenge_plan_path` instead of `codex_plan_path`.
|
|
185
|
+
|
|
186
|
+
`reviewer-decider` remains Claude-only — even in claude+claude where the author is also Claude. The gate authority is intentionally separated by being a fresh agent spawn with its own context, not by brain identity.
|
|
187
|
+
|
|
188
|
+
---
|
|
189
|
+
|
|
190
|
+
## Codex-only pair (codex+codex) routing
|
|
191
|
+
|
|
192
|
+
When `pair == codex+codex` (orchestrator is `merlin-codex`, no Claude available), the orchestrator drives duo via bash and `codex-as.sh`. Specialists are invoked as `codex exec`/`codex-as.sh <agent>` instead of as Skill/Agent calls.
|
|
193
|
+
|
|
194
|
+
**PARALLEL:**
|
|
195
|
+
|
|
196
|
+
| Task | Brain A | Brain B | Decider |
|
|
197
|
+
|---|---|---|---|
|
|
198
|
+
| Planning (feature-dev / refactor / product-dev) | `codex-planner` | `codex-as.sh challenger-academic` | `codex-as.sh challenger-arbiter` |
|
|
199
|
+
| Code review | `codex-code-review` | `codex-as.sh merlin-reviewer` | merge + dedupe inline (orchestrator) |
|
|
200
|
+
| Testing | `codex-as.sh tests-qa` | `codex-as.sh merlin-edge-case-hunter` | merge + dedupe inline (orchestrator) |
|
|
201
|
+
| Documentation | `codex-as.sh docs-keeper` | — | solo (single specialist) |
|
|
202
|
+
|
|
203
|
+
**SEQUENTIAL (code write/modify):**
|
|
204
|
+
|
|
205
|
+
| Author | Reviewer | Gate |
|
|
206
|
+
|---|---|---|
|
|
207
|
+
| `codex-as.sh implementation-dev` | `codex-as.sh code-review` | **inline orchestrator judgment** — no `reviewer-decider` (Claude-only by P0) |
|
|
208
|
+
|
|
209
|
+
**Codex-only planning artifact paths:**
|
|
210
|
+
|
|
211
|
+
- `<phase-dir>/PLAN-CODEX-A.md` — written by `codex-planner` (receives `output_path`).
|
|
212
|
+
- `<phase-dir>/PLAN-CODEX-B.md` — challenger-academic via `codex-as.sh` does NOT receive `output_path` (its frontmatter forbids Write). Orchestrator captures the agent's stdout and writes the file.
|
|
213
|
+
- `<phase-dir>/PLAN-FINAL.md` — written by `codex-as.sh challenger-arbiter` (receives `output_path` plus both A and B paths).
|
|
214
|
+
|
|
215
|
+
**Safety degradation (documented honestly):**
|
|
216
|
+
|
|
217
|
+
Sequential code in `codex+codex` has a weaker gate than `claude+codex` or `claude+claude`. In those pairs, `reviewer-decider` is a separate fresh-context Claude spawn that gates the author's diff. In `codex+codex`, no separate gate spawn is available — the orchestrator (Codex) consumes the reviewer's findings and decides approve/revise/reject inline within its own context. This is acceptable for non-critical changes but users should consider installing Claude Code for higher-stakes work.
|
|
218
|
+
|
|
219
|
+
---
|
|
220
|
+
|
|
123
221
|
## reviewer-decider is Claude-only (P0 safety)
|
|
124
222
|
|
|
125
223
|
`reviewer-decider` MUST NOT be invoked via `codex-as.sh`. Codex impersonating the gate that checks Codex's own output destroys the sequential safety story. The curated specialists list in `codex-routing.md` explicitly excludes `reviewer-decider` — this exclusion is permanent and must not be removed.
|
|
126
224
|
|
|
127
225
|
See also: `codex-routing.md` § "Curated specialists exclusion".
|
|
128
226
|
|
|
227
|
+
In `codex+codex` pair, no `reviewer-decider` invocation occurs — the orchestrator handles gate logic inline. This is the only pair where the gate is not a separate spawn, and it's a documented safety degradation, not an oversight.
|
|
228
|
+
|
|
129
229
|
---
|
|
130
230
|
|
|
131
231
|
## Auto-offer for risky tasks
|
|
@@ -142,7 +242,6 @@ When duo is OFF, Merlin runs a pre-route hook at the start of every routing deci
|
|
|
142
242
|
**Risk threshold:** `duo-risk-detect.sh` scores the task 0–100. `suggest_duo: true` when score ≥ 50. Threshold tunable via env `MERLIN_DUO_OFFER_THRESHOLD`.
|
|
143
243
|
|
|
144
244
|
**Offer fires only when ALL of the following are true:**
|
|
145
|
-
- `duo-installed.sh` exits 0 (Codex installed)
|
|
146
245
|
- Duo is currently OFF (or auto-expired)
|
|
147
246
|
- Risk score ≥ threshold
|
|
148
247
|
- Not suppressed (no `session_skip`, task hash not in `task_hashes_declined`, intent not in `never_for_intents`)
|
|
@@ -15,6 +15,46 @@ Route user intent to the matching workflow. Do not analyze or fix problems yours
|
|
|
15
15
|
| "build API" / "backend" / "endpoint" | `Skill("merlin:workflow", args='run api-build "<task>"')` |
|
|
16
16
|
| Small, isolated task | `merlin_smart_route(task="...")` then `merlin_route()` |
|
|
17
17
|
|
|
18
|
+
## Universal Task Optimization (RUN FIRST on every routing decision)
|
|
19
|
+
|
|
20
|
+
BEFORE every routing decision (workflow, agent, or specialist), run:
|
|
21
|
+
|
|
22
|
+
```
|
|
23
|
+
bash ~/.claude/scripts/task-optimize.sh --task "<full user task text>"
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
The optimizer returns single-line JSON: `{"score":<0-100>,"intent":"<id>","skills":[<ids>],"agent":"<name>","matched_phrases":[...]}`.
|
|
27
|
+
|
|
28
|
+
If `score >= 25`:
|
|
29
|
+
1. Read the matched skill files (`~/.claude/merlin/skills/<id>.md` or `~/.claude/skills/<external>/SKILL.md` for external skills).
|
|
30
|
+
2. Announce the pairing: `⟡🔮 MERLIN › Intent: <intent>. Loading <skills> + routing to <agent>.` (use the duo badge variant if duo mode is on)
|
|
31
|
+
3. Route to the optimizer-recommended `agent` with skill bodies prepended into context.
|
|
32
|
+
|
|
33
|
+
If `score < 25`: fall through to the existing routing tables below.
|
|
34
|
+
|
|
35
|
+
The registry of skill→agent pairings lives at `~/.claude/merlin/skills/TASK-OPTIMIZER.json`. To add a new skill, append an entry there — no code changes needed.
|
|
36
|
+
|
|
37
|
+
### Common skill+agent pairings (reference; full registry in TASK-OPTIMIZER.json)
|
|
38
|
+
|
|
39
|
+
| Task signal | Skills | Agent |
|
|
40
|
+
|---|---|---|
|
|
41
|
+
| Design / UI / typography / color / responsive | design/impeccable, design/emil-design-eng | ui-builder |
|
|
42
|
+
| Animation / polish / micro-interactions | design/emil-design-eng | animation-expert |
|
|
43
|
+
| Accessibility / WCAG / a11y | coding/accessibility | hardening-guard |
|
|
44
|
+
| Auth / OWASP / CSRF / hardening | coding/security-hardening | hardening-guard |
|
|
45
|
+
| API / REST / OpenAPI / GraphQL | coding/api-design | merlin-api-designer |
|
|
46
|
+
| Performance / Core Web Vitals / bundle | coding/performance | merlin-performance |
|
|
47
|
+
| Tests / TDD / Jest / Vitest | testing/tdd-workflow | tests-qa |
|
|
48
|
+
| Docker / containers / CI | devops/docker-containers | ops-railway |
|
|
49
|
+
| Bug / crash / error / debug | coding/debug-mode | merlin-debugger |
|
|
50
|
+
| Stripe / billing / webhooks / email | automation/*, communication/* | implementation-dev |
|
|
51
|
+
| Brainstorm / ideate / options | research/brainstorm | merlin-researcher |
|
|
52
|
+
| React / hooks / Next.js / components | coding/react-patterns | merlin-frontend |
|
|
53
|
+
|
|
54
|
+
Slash commands: `/merlin:optimize`, `/merlin:design-audit`, `/merlin:polish`, `/merlin:redesign`.
|
|
55
|
+
|
|
56
|
+
The optimizer is the AUTHORITY. Don't override its recommendations unless the user explicitly asks for a different agent.
|
|
57
|
+
|
|
18
58
|
## Agent Routing
|
|
19
59
|
|
|
20
60
|
Call `merlin_smart_route(task="...")` FIRST (searches 500+ community agents). Then:
|
|
@@ -69,6 +69,9 @@ FULL_PROMPT="${PROMPT_BODY}
|
|
|
69
69
|
|
|
70
70
|
${TASK_TEXT}"
|
|
71
71
|
|
|
72
|
-
# Invoke codex with
|
|
72
|
+
# Invoke codex with workspace-write sandbox to allow file modifications inside cwd.
|
|
73
|
+
# (The legacy --write flag was removed from `codex exec`; -s workspace-write is the
|
|
74
|
+
# current equivalent. Use --dangerously-bypass-approvals-and-sandbox only if you
|
|
75
|
+
# explicitly want to skip all prompts — workspace-write is the safer default.)
|
|
73
76
|
# shellcheck disable=SC2086
|
|
74
|
-
exec codex exec
|
|
77
|
+
exec codex exec -s workspace-write --cd "$PWD" $MODEL_FLAG "$FULL_PROMPT"
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# design-intent-detect.sh — thin wrapper around task-optimize.sh (v5.3+)
|
|
3
|
+
# Preserves the v5.2.0 contract for slash commands that reference this script.
|
|
4
|
+
# Usage: design-intent-detect.sh --task "<text>" [--self-test]
|
|
5
|
+
# Output: same JSON as task-optimize.sh
|
|
6
|
+
set -euo pipefail
|
|
7
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
8
|
+
exec "$SCRIPT_DIR/task-optimize.sh" "$@"
|
|
@@ -8,12 +8,10 @@ set -euo pipefail
|
|
|
8
8
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
9
9
|
STEP_PHRASE="${1:-}"
|
|
10
10
|
|
|
11
|
-
# Determine if duo is active:
|
|
11
|
+
# Determine if duo is active: mode is enabled (duo-installed.sh is always-pass, no longer checked)
|
|
12
12
|
DUO_ACTIVE=false
|
|
13
|
-
if "${SCRIPT_DIR}/duo-
|
|
14
|
-
|
|
15
|
-
DUO_ACTIVE=true
|
|
16
|
-
fi
|
|
13
|
+
if [[ "$("${SCRIPT_DIR}/duo-mode-read.sh" 2>/dev/null)" == "enabled" ]]; then
|
|
14
|
+
DUO_ACTIVE=true
|
|
17
15
|
fi
|
|
18
16
|
|
|
19
17
|
# Build badge
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
2
|
# duo-installed.sh — composite install gate for duo mode
|
|
3
|
-
#
|
|
3
|
+
# Always exits 0: duo is available regardless of Codex. Pair selection (claude+codex vs claude+claude)
|
|
4
|
+
# is determined separately via codex-installed.sh at toggle time.
|
|
4
5
|
|
|
5
6
|
set -euo pipefail
|
|
6
7
|
|
|
7
|
-
|
|
8
|
-
"${SCRIPT_DIR}/codex-installed.sh"
|
|
8
|
+
exit 0
|
|
@@ -1,51 +1,71 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
2
|
# duo-mode-read.sh — reads duo-mode.json, applies 24h auto-expire (read-time only, never modifies file)
|
|
3
|
-
#
|
|
3
|
+
# Without --pair: prints exactly "enabled" or "disabled" to stdout
|
|
4
|
+
# With --pair: prints the pair value: "claude+codex", "claude+claude", or "none"
|
|
4
5
|
|
|
5
6
|
set -euo pipefail
|
|
6
7
|
|
|
7
8
|
STATE_FILE="${HOME}/.claude/merlin-state/duo-mode.json"
|
|
9
|
+
PAIR_MODE=false
|
|
8
10
|
|
|
9
|
-
|
|
11
|
+
for arg in "$@"; do
|
|
12
|
+
if [[ "$arg" == "--pair" ]]; then
|
|
13
|
+
PAIR_MODE=true
|
|
14
|
+
fi
|
|
15
|
+
done
|
|
16
|
+
|
|
17
|
+
# If state file missing, default to disabled / none
|
|
10
18
|
if [[ ! -f "$STATE_FILE" ]]; then
|
|
11
|
-
|
|
19
|
+
if [[ "$PAIR_MODE" == "true" ]]; then
|
|
20
|
+
echo "none"
|
|
21
|
+
else
|
|
22
|
+
echo "disabled"
|
|
23
|
+
fi
|
|
12
24
|
exit 0
|
|
13
25
|
fi
|
|
14
26
|
|
|
15
|
-
python3 - "$STATE_FILE" <<'PYEOF'
|
|
27
|
+
python3 - "$STATE_FILE" "$PAIR_MODE" <<'PYEOF'
|
|
16
28
|
import sys
|
|
17
29
|
import json
|
|
18
30
|
from datetime import datetime, timezone, timedelta
|
|
19
31
|
|
|
20
32
|
state_path = sys.argv[1]
|
|
33
|
+
pair_mode = sys.argv[2] == "true"
|
|
21
34
|
|
|
22
35
|
try:
|
|
23
36
|
with open(state_path, "r") as f:
|
|
24
37
|
data = json.load(f)
|
|
25
38
|
except (json.JSONDecodeError, OSError):
|
|
26
|
-
print("disabled")
|
|
39
|
+
print("none" if pair_mode else "disabled")
|
|
27
40
|
sys.exit(0)
|
|
28
41
|
|
|
29
42
|
enabled = data.get("enabled", False)
|
|
30
43
|
since_iso = data.get("sinceISO")
|
|
44
|
+
pair = data.get("pair")
|
|
31
45
|
|
|
32
46
|
if not enabled or since_iso is None:
|
|
33
|
-
print("disabled")
|
|
47
|
+
print("none" if pair_mode else "disabled")
|
|
34
48
|
sys.exit(0)
|
|
35
49
|
|
|
36
50
|
# Parse sinceISO and apply 24h auto-expire (read-time interpretation, no file write)
|
|
37
51
|
try:
|
|
38
|
-
# Handle both Z suffix and +00:00 format
|
|
39
52
|
since_str = since_iso.replace("Z", "+00:00")
|
|
40
53
|
since_dt = datetime.fromisoformat(since_str)
|
|
41
54
|
now_dt = datetime.now(timezone.utc)
|
|
42
55
|
if (now_dt - since_dt) > timedelta(hours=24):
|
|
43
|
-
print("disabled")
|
|
56
|
+
print("none" if pair_mode else "disabled")
|
|
44
57
|
sys.exit(0)
|
|
45
58
|
except (ValueError, TypeError):
|
|
46
59
|
# Unparseable timestamp — treat as expired
|
|
47
|
-
print("disabled")
|
|
60
|
+
print("none" if pair_mode else "disabled")
|
|
48
61
|
sys.exit(0)
|
|
49
62
|
|
|
50
|
-
|
|
63
|
+
if pair_mode:
|
|
64
|
+
# Legacy state (no pair field) when enabled defaults to claude+codex
|
|
65
|
+
if pair is None:
|
|
66
|
+
print("claude+codex")
|
|
67
|
+
else:
|
|
68
|
+
print(pair)
|
|
69
|
+
else:
|
|
70
|
+
print("enabled")
|
|
51
71
|
PYEOF
|
|
@@ -1,18 +1,21 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
2
|
# duo-mode-write.sh — atomic write to duo-mode.json
|
|
3
|
-
# Usage: duo-mode-write.sh on "<reason>" |
|
|
3
|
+
# Usage: duo-mode-write.sh on "<reason>" [claude+codex|claude+claude]
|
|
4
|
+
# duo-mode-write.sh off "<reason>"
|
|
4
5
|
|
|
5
6
|
set -euo pipefail
|
|
6
7
|
|
|
7
8
|
if [[ $# -lt 2 ]]; then
|
|
8
|
-
echo "Usage: duo-mode-write.sh on|off \"<reason>\"" >&2
|
|
9
|
+
echo "Usage: duo-mode-write.sh on|off \"<reason>\" [claude+codex|claude+claude|codex+codex]" >&2
|
|
9
10
|
exit 1
|
|
10
11
|
fi
|
|
11
12
|
|
|
12
13
|
ACTION="$1"
|
|
13
14
|
REASON="$2"
|
|
15
|
+
PAIR_ARG="${3:-}"
|
|
14
16
|
STATE_FILE="${HOME}/.claude/merlin-state/duo-mode.json"
|
|
15
17
|
STATE_DIR="$(dirname "$STATE_FILE")"
|
|
18
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
16
19
|
|
|
17
20
|
# Ensure state directory exists
|
|
18
21
|
mkdir -p "$STATE_DIR"
|
|
@@ -20,9 +23,28 @@ mkdir -p "$STATE_DIR"
|
|
|
20
23
|
case "$ACTION" in
|
|
21
24
|
on)
|
|
22
25
|
ENABLED="true"
|
|
26
|
+
|
|
27
|
+
# Validate explicit pair arg if provided
|
|
28
|
+
if [[ -n "$PAIR_ARG" ]]; then
|
|
29
|
+
if [[ "$PAIR_ARG" != "claude+codex" && "$PAIR_ARG" != "claude+claude" && "$PAIR_ARG" != "codex+codex" ]]; then
|
|
30
|
+
echo "Error: pair must be 'claude+codex', 'claude+claude', or 'codex+codex', got: $PAIR_ARG" >&2
|
|
31
|
+
exit 1
|
|
32
|
+
fi
|
|
33
|
+
PAIR="$PAIR_ARG"
|
|
34
|
+
else
|
|
35
|
+
# Auto-detect pair: MERLIN_RUNTIME=codex → codex+codex; codex installed → claude+codex; else → claude+claude
|
|
36
|
+
if [[ "${MERLIN_RUNTIME:-}" == "codex" ]]; then
|
|
37
|
+
PAIR="codex+codex"
|
|
38
|
+
elif "${SCRIPT_DIR}/codex-installed.sh" 2>/dev/null; then
|
|
39
|
+
PAIR="claude+codex"
|
|
40
|
+
else
|
|
41
|
+
PAIR="claude+claude"
|
|
42
|
+
fi
|
|
43
|
+
fi
|
|
23
44
|
;;
|
|
24
45
|
off)
|
|
25
46
|
ENABLED="false"
|
|
47
|
+
PAIR="null"
|
|
26
48
|
;;
|
|
27
49
|
*)
|
|
28
50
|
echo "Error: first argument must be 'on' or 'off', got: $ACTION" >&2
|
|
@@ -31,7 +53,7 @@ case "$ACTION" in
|
|
|
31
53
|
esac
|
|
32
54
|
|
|
33
55
|
# Use python3 for JSON serialization and atomic write via mktemp + mv (same FS = atomic)
|
|
34
|
-
python3 - "$STATE_FILE" "$ENABLED" "$REASON" <<'PYEOF'
|
|
56
|
+
python3 - "$STATE_FILE" "$ENABLED" "$REASON" "$PAIR" <<'PYEOF'
|
|
35
57
|
import sys
|
|
36
58
|
import json
|
|
37
59
|
import os
|
|
@@ -41,13 +63,16 @@ from datetime import datetime, timezone
|
|
|
41
63
|
state_path = sys.argv[1]
|
|
42
64
|
enabled = sys.argv[2] == "true"
|
|
43
65
|
reason = sys.argv[3]
|
|
66
|
+
pair_arg = sys.argv[4]
|
|
44
67
|
|
|
45
68
|
now_iso = datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ") if enabled else None
|
|
69
|
+
pair_value = None if pair_arg == "null" else pair_arg
|
|
46
70
|
|
|
47
71
|
data = {
|
|
48
72
|
"enabled": enabled,
|
|
49
73
|
"sinceISO": now_iso,
|
|
50
74
|
"lastToggleReason": reason,
|
|
75
|
+
"pair": pair_value,
|
|
51
76
|
}
|
|
52
77
|
|
|
53
78
|
state_dir = os.path.dirname(state_path)
|
|
@@ -30,13 +30,7 @@ if [[ "$DUO_STATE" == "enabled" ]]; then
|
|
|
30
30
|
exit 0
|
|
31
31
|
fi
|
|
32
32
|
|
|
33
|
-
# Branch 2:
|
|
34
|
-
if ! "${SCRIPT_DIR}/duo-installed.sh" 2>/dev/null; then
|
|
35
|
-
echo "mode=solo"
|
|
36
|
-
exit 0
|
|
37
|
-
fi
|
|
38
|
-
|
|
39
|
-
# Branch 3: risk-detect suggestion
|
|
33
|
+
# Branch 2: risk-detect suggestion (duo-installed.sh is always-pass; offer fires regardless of Codex)
|
|
40
34
|
RISK_DETECT="${SCRIPT_DIR}/duo-risk-detect.sh"
|
|
41
35
|
if [[ ! -x "$RISK_DETECT" ]]; then
|
|
42
36
|
echo "mode=solo"
|
|
@@ -62,7 +56,7 @@ if [[ "$SUGGEST_DUO" != "true" ]]; then
|
|
|
62
56
|
exit 0
|
|
63
57
|
fi
|
|
64
58
|
|
|
65
|
-
# Branch
|
|
59
|
+
# Branch 2a: suppression check — use a temp python script to avoid heredoc-in-subshell issues
|
|
66
60
|
REASONS_JSON=$(python3 -c "
|
|
67
61
|
import json, sys
|
|
68
62
|
try:
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -e
|
|
3
|
+
|
|
4
|
+
# Merlin Design Skills Installer
|
|
5
|
+
# Installs and prioritizes emilkowalski/skill and pbakaus/impeccable
|
|
6
|
+
|
|
7
|
+
# Honor skip flag
|
|
8
|
+
if [ "${MERLIN_SKIP_DESIGN_SKILLS:-0}" = "1" ]; then
|
|
9
|
+
exit 0
|
|
10
|
+
fi
|
|
11
|
+
|
|
12
|
+
echo "[merlin] Installing priority design skills…"
|
|
13
|
+
|
|
14
|
+
SKILLS_DIR="${HOME}/.claude/skills"
|
|
15
|
+
mkdir -p "$SKILLS_DIR"
|
|
16
|
+
|
|
17
|
+
# ═══════════════════════════════════════════════════════════════
|
|
18
|
+
# Install emilkowalski/skill (install-only, no license vendoring)
|
|
19
|
+
# ═══════════════════════════════════════════════════════════════
|
|
20
|
+
|
|
21
|
+
if [ ! -f "$SKILLS_DIR/emil-design-eng/SKILL.md" ]; then
|
|
22
|
+
if command -v npx >/dev/null 2>&1; then
|
|
23
|
+
# Attempt install via skills CLI
|
|
24
|
+
if npx --yes skills add emilkowalski/skill 2>/dev/null; then
|
|
25
|
+
: # Success
|
|
26
|
+
else
|
|
27
|
+
# Non-fatal — user can install manually
|
|
28
|
+
echo "[merlin] emil skill install hint: npx --yes skills add emilkowalski/skill"
|
|
29
|
+
fi
|
|
30
|
+
else
|
|
31
|
+
echo "[merlin] npx not found; skipping emil skill (user can install: npx --yes skills add emilkowalski/skill)"
|
|
32
|
+
fi
|
|
33
|
+
fi
|
|
34
|
+
|
|
35
|
+
# ═══════════════════════════════════════════════════════════════
|
|
36
|
+
# Install pbakaus/impeccable (fetch + preserve Apache-2.0 attribution)
|
|
37
|
+
# ═══════════════════════════════════════════════════════════════
|
|
38
|
+
|
|
39
|
+
if [ ! -d "$SKILLS_DIR/impeccable" ]; then
|
|
40
|
+
if command -v git >/dev/null 2>&1; then
|
|
41
|
+
tmp=$(mktemp -d)
|
|
42
|
+
trap "rm -rf '$tmp'" EXIT
|
|
43
|
+
|
|
44
|
+
if git clone --depth 1 https://github.com/pbakaus/impeccable.git "$tmp" 2>/dev/null; then
|
|
45
|
+
# Copy the skill subtree (contains SKILL.md and commands/)
|
|
46
|
+
if [ -d "$tmp/dist/claude-code/.claude/skills/impeccable" ]; then
|
|
47
|
+
mkdir -p "$SKILLS_DIR"
|
|
48
|
+
cp -R "$tmp/dist/claude-code/.claude/skills/impeccable" "$SKILLS_DIR/"
|
|
49
|
+
|
|
50
|
+
# Preserve license attribution
|
|
51
|
+
if [ -f "$tmp/LICENSE" ]; then
|
|
52
|
+
cp "$tmp/LICENSE" "$SKILLS_DIR/impeccable/LICENSE"
|
|
53
|
+
fi
|
|
54
|
+
if [ -f "$tmp/NOTICE.md" ]; then
|
|
55
|
+
cp "$tmp/NOTICE.md" "$SKILLS_DIR/impeccable/NOTICE.md"
|
|
56
|
+
fi
|
|
57
|
+
else
|
|
58
|
+
echo "[merlin] impeccable skill structure not found in cloned repo"
|
|
59
|
+
fi
|
|
60
|
+
else
|
|
61
|
+
echo "[merlin] git clone failed for impeccable; user can install manually"
|
|
62
|
+
fi
|
|
63
|
+
else
|
|
64
|
+
echo "[merlin] git not found; skipping impeccable skill (user can install manually: git clone pbakaus/impeccable)"
|
|
65
|
+
fi
|
|
66
|
+
fi
|
|
67
|
+
|
|
68
|
+
# ═══════════════════════════════════════════════════════════════
|
|
69
|
+
# Verification
|
|
70
|
+
# ═══════════════════════════════════════════════════════════════
|
|
71
|
+
|
|
72
|
+
has_impeccable=0
|
|
73
|
+
has_emil=0
|
|
74
|
+
|
|
75
|
+
[ -d "$SKILLS_DIR/impeccable" ] && has_impeccable=1
|
|
76
|
+
[ -f "$SKILLS_DIR/emil-design-eng/SKILL.md" ] && has_emil=1
|
|
77
|
+
|
|
78
|
+
if [ "$has_impeccable" = "1" ] || [ "$has_emil" = "1" ]; then
|
|
79
|
+
echo "[merlin] Design skills ready:"
|
|
80
|
+
[ "$has_impeccable" = "1" ] && echo " ✓ impeccable"
|
|
81
|
+
[ "$has_emil" = "1" ] && echo " ✓ emil-design-eng"
|
|
82
|
+
else
|
|
83
|
+
echo "[merlin] Design skills install: no changes (already present or install failed)"
|
|
84
|
+
fi
|
|
85
|
+
|
|
86
|
+
exit 0
|
|
@@ -15,6 +15,8 @@
|
|
|
15
15
|
#
|
|
16
16
|
set -euo pipefail
|
|
17
17
|
|
|
18
|
+
export MERLIN_RUNTIME=codex
|
|
19
|
+
|
|
18
20
|
SCRIPTS_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
19
21
|
SYSTEM_PROMPT_FILE="${HOME}/.claude/merlin-system-prompt.txt"
|
|
20
22
|
FALLBACK_PROMPT="You are Merlin: orchestrator, not coder. Route implementation to specialists. Badge every action with ⟡🔮 MERLIN ›."
|
|
@@ -22,20 +24,23 @@ FALLBACK_PROMPT="You are Merlin: orchestrator, not coder. Route implementation t
|
|
|
22
24
|
# ── Pass-through subcommands (no Merlin prompt injection) ──────
|
|
23
25
|
PASSTHROUGH_CMDS="login logout mcp mcp-server app app-server completion sandbox debug"
|
|
24
26
|
|
|
25
|
-
# ── Determine duo mode for banner
|
|
27
|
+
# ── Determine duo mode and pair for banner ─────────────────────
|
|
26
28
|
_mode_label="Solo"
|
|
29
|
+
_pair_label=""
|
|
27
30
|
if [ -x "${SCRIPTS_DIR}/duo-mode-read.sh" ]; then
|
|
28
31
|
_duo=$("${SCRIPTS_DIR}/duo-mode-read.sh" 2>/dev/null) || _duo="disabled"
|
|
29
32
|
if [ "${_duo}" = "enabled" ]; then
|
|
30
|
-
|
|
31
|
-
|
|
33
|
+
_mode_label="Duo"
|
|
34
|
+
_pair=$("${SCRIPTS_DIR}/duo-mode-read.sh" --pair 2>/dev/null) || _pair="none"
|
|
35
|
+
if [ "${_pair}" != "none" ]; then
|
|
36
|
+
_pair_label=" (${_pair})"
|
|
32
37
|
fi
|
|
33
38
|
fi
|
|
34
39
|
fi
|
|
35
40
|
|
|
36
41
|
# ── Print banner to stderr (always) ───────────────────────────
|
|
37
42
|
printf '⟡🔮 MERLIN · entering Codex via merlin-codex\n' >&2
|
|
38
|
-
printf '🎯 Mode: %s\n' "${_mode_label}" >&2
|
|
43
|
+
printf '🎯 Mode: %s%s\n' "${_mode_label}" "${_pair_label}" >&2
|
|
39
44
|
printf '▶ Codex will inherit Merlin'"'"'s orchestrator instructions\n' >&2
|
|
40
45
|
|
|
41
46
|
# ── If no args, open interactive Codex shell ──────────────────
|