devloom 1.1.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/GUIDE.md CHANGED
@@ -29,7 +29,7 @@ and skill files to your OpenCode global config directory:
29
29
  | OS | Config directory |
30
30
  |---|---|
31
31
  | Linux | `~/.config/opencode/` |
32
- | macOS | `~/Library/Application Support/opencode/` |
32
+ | macOS | `~/.config/opencode/` |
33
33
  | Windows | `%APPDATA%\opencode\` |
34
34
 
35
35
  Expected output:
@@ -58,7 +58,7 @@ Installing commands (17 total):
58
58
  - /devloom, /devloom-init, /devloom-status, /devloom-resume
59
59
  - /devloom-save, /devloom-agents
60
60
  - /devloom-go, /devloom-go-economy, /devloom-go-flash, /devloom-deepseek
61
- - /devloom-mimo, /devloom-free, /devloom-auto
61
+ - /devloom-mimo, /devloom-deepseek-mimo, /devloom-free, /devloom-auto
62
62
  - /devloom-plan, /devloom-context, /devloom-loop, /devloom-loop-status
63
63
 
64
64
  Installing skills (21 total):
@@ -128,6 +128,7 @@ Start OpenCode and open the command palette by typing `/`:
128
128
  /devloom-go-economy → Switch to Go economy profile
129
129
  /devloom-go-flash → Switch to Go flash profile (cheapest paid)
130
130
  /devloom-mimo → Switch to the MiMo V2.6 Flash profile (multimodal all-role stack)
131
+ /devloom-deepseek-mimo → Switch to the hybrid profile (DeepSeek V4.1 general, MiMo V2.6 visual)
131
132
  /devloom-free → Switch to free-tier profile
132
133
  /devloom-auto → Auto-detect best profile for your environment
133
134
  /devloom-loop → Start/stop loop engineering patterns
@@ -176,6 +177,7 @@ DevLoom provides model profiles that trade off quality vs cost:
176
177
  | `deepseek` | DeepSeek-only stack | Go (paid) |
177
178
  | `glm` | GLM-only stack | Go (paid) |
178
179
  | `go-flash` | Maximum throughput, minimum cost | Go (paid) |
180
+ | `deepseek-mimo` | Fast general work (DeepSeek V4.1), MiMo V2.6 for graphical/complex roles | Go (paid) |
179
181
  | `mimo` | Multimodal all-role stack incl. vision | Go/MiMo |
180
182
  | `free` | Zero-cost experimentation | Free |
181
183
 
@@ -604,6 +606,28 @@ without starting a weave:
604
606
 
605
607
  ---
606
608
 
609
+ ## Incremental Briefing (token savings)
610
+
611
+ Sub-agents must not re-read the same files on every dispatch. DevLoom solves
612
+ this with a per-ticket briefing plus change-based invalidation:
613
+
614
+ - **Ticket start:** the orchestrator runs `briefing.mjs init <ticket>` and
615
+ writes `.opencode/devloom/context/briefing.md` ONCE (goal, constraints,
616
+ file map with `file:line` refs, decisions).
617
+ - **Every `task()` prompt** starts with `Read .opencode/devloom/context/briefing.md
618
+ first — do not re-discover` — sub-agents start from the map instead of
619
+ scanning the repo. A stable prompt prefix also gets provider prompt-caching.
620
+ - **Before each later dispatch:** `briefing.mjs changed` prints only the paths
621
+ whose content fingerprints differ from the last anchor (sha256, recorded at
622
+ `init`/`touch` — works inside or outside git and never re-flags a file that
623
+ was already re-read). The orchestrator re-reads ONLY those, patches the
624
+ briefing, runs `briefing.mjs touch`, and dispatches. Unchanged files are
625
+ never re-read.
626
+
627
+ Script discovery uses the same fallback chain as `profile.mjs`:
628
+ `~/.config/opencode/commands/briefing.mjs` → `~/.config/opencode/devloom-scripts/briefing.mjs`
629
+ → `scripts/briefing.mjs`.
630
+
607
631
  ## Visual Pipeline
608
632
 
609
633
  For substantial graphical work, DevLoom routes through an extra visual loop:
package/README.md CHANGED
@@ -114,6 +114,18 @@ npm install -g devloom
114
114
  npm install -g https://github.com/nsrau/devloom.git
115
115
  ```
116
116
 
117
+ **From GitHub Packages** (mirror `@nsrau/devloom`):
118
+
119
+ ```bash
120
+ # ~/.npmrc
121
+ @nsrau:registry=https://npm.pkg.github.com
122
+ //npm.pkg.github.com/:_authToken=<TOKEN> # PAT/granular token with read:packages
123
+ ```
124
+
125
+ ```bash
126
+ npm install -g @nsrau/devloom
127
+ ```
128
+
117
129
  **From source:**
118
130
 
119
131
  ```bash
@@ -349,6 +361,7 @@ maps every agent to a specific model optimized for its role.
349
361
  | **free** | Good | Zero | Evaluation, learning, hobby projects |
350
362
  | **glm** | High | Lower | GLM-only stack |
351
363
  | **mimo** | High | Low/Fast | MiMo V2.6 multimodal all-role stack |
364
+ | **deepseek-mimo** | High | Fast | DeepSeek V4.1 general + MiMo V2.6 graphical/complex hybrid |
352
365
 
353
366
  ### go profile (max quality)
354
367
 
@@ -482,6 +495,18 @@ same-family fallback such as `mimo-v2.6-pro` is allowed but always reported
482
495
  under "Fallbacks applied"). Tier overrides are locked for `mimo` (like
483
496
  `go-flash` and `free`). Switch with `/devloom-mimo` (`profile.mjs set mimo`).
484
497
 
498
+ ### deepseek-mimo profile (hybrid)
499
+
500
+ Fast everyday work on DeepSeek V4.1, graphical/complex work on MiMo V2.6.
501
+ The 7 general roles (orchestrator, planner, developer, qa, verifier, security,
502
+ documenter) resolve to `opencode-go/deepseek-v4.1-flash`; vision,
503
+ visual-director, and visual-critic resolve through the
504
+ `mimo/mimo-v2.6-flash` → `xiaomi/mimo-v2.6-flash` → `opencode-go/mimo-v2.6-flash`
505
+ chain. Both chains resolve strictly — if either finds nothing the profile exits
506
+ code 1 with the attempted candidates, never an unrelated substitution. Tier
507
+ overrides are allowed (deepseek pattern). Switch with
508
+ `/devloom-deepseek-mimo` (`profile.mjs set deepseek-mimo`).
509
+
485
510
  ### Model-routing table (go)
486
511
 
487
512
  Each of the 10 agents is assigned a model optimized for its role:
@@ -553,6 +578,7 @@ source first so the cache never receives stale compiled code.
553
578
  variants are hidden for every profile. The variants stay registered so
554
579
  orchestrator `task()` routing keeps working; they are simply not listed.
555
580
  - Switching profiles (`/devloom-go`, `/devloom-go-flash`, `/devloom-mimo`,
581
+ `/devloom-deepseek-mimo`,
556
582
  `/devloom-free`, ...)
557
583
  updates the installed agent files immediately; restart opencode to see the
558
584
  updated profile and models in the sidebar.
@@ -6,12 +6,16 @@ permission:
6
6
  edit: allow
7
7
  bash: allow
8
8
  task: deny
9
+ external_directory:
10
+ "~/.config/opencode/**": allow
9
11
  ---
10
12
 
11
13
  # DevLoom Developer Flash
12
14
 
13
15
  ENGLISH ONLY: All output MUST be in English. Never use any other language.
14
16
 
17
+ BEFORE SCANNING: if `.opencode/devloom/context/briefing.md` exists, read it FIRST and start from its file map — never re-read files it already summarizes.
18
+
15
19
  COMPLIANCE: Follow the RULES below + your skill LOAD. No rule may be skipped.
16
20
  RULES: EN | SOLID+TDD+CleanArch | tests+regr required | doing<=1 | FILES: use .opencode/devloom/.tmp/ | peer-review for high-risk | degrade on 2x failure
17
21
  LOAD: ~/.config/opencode/skills/build/development.md|~/.config/opencode/skills/build/simplify.md
@@ -6,12 +6,16 @@ permission:
6
6
  edit: allow
7
7
  bash: allow
8
8
  task: deny
9
+ external_directory:
10
+ "~/.config/opencode/**": allow
9
11
  ---
10
12
 
11
13
  # DevLoom Developer Senior
12
14
 
13
15
  ENGLISH ONLY: All output MUST be in English. Never use any other language.
14
16
 
17
+ BEFORE SCANNING: if `.opencode/devloom/context/briefing.md` exists, read it FIRST and start from its file map — never re-read files it already summarizes.
18
+
15
19
  COMPLIANCE: Follow the RULES below + your skill LOAD. No rule may be skipped.
16
20
  RULES: EN | SOLID+TDD+CleanArch | tests+regr required | doing<=1 | FILES: use .opencode/devloom/.tmp/ | peer-review for high-risk | degrade on 2x failure
17
21
  LOAD: ~/.config/opencode/skills/build/development.md|~/.config/opencode/skills/build/simplify.md
@@ -6,12 +6,16 @@ permission:
6
6
  edit: allow
7
7
  bash: allow
8
8
  task: deny
9
+ external_directory:
10
+ "~/.config/opencode/**": allow
9
11
  ---
10
12
 
11
13
  # DevLoom Developer
12
14
 
13
15
  ENGLISH ONLY: All output MUST be in English. Never use any other language.
14
16
 
17
+ BEFORE SCANNING: if `.opencode/devloom/context/briefing.md` exists, read it FIRST and start from its file map — never re-read files it already summarizes.
18
+
15
19
  COMPLIANCE: Follow the RULES below + your skill LOAD. No rule may be skipped.
16
20
  RULES: EN | SOLID+TDD+CleanArch | tests+regr required | doing<=1 | FILES: use .opencode/devloom/.tmp/ | peer-review for high-risk | degrade on 2x failure
17
21
  LOAD: ~/.config/opencode/skills/build/development.md|~/.config/opencode/skills/build/simplify.md
@@ -6,12 +6,16 @@ permission:
6
6
  edit: allow
7
7
  bash: allow
8
8
  task: deny
9
+ external_directory:
10
+ "~/.config/opencode/**": allow
9
11
  ---
10
12
 
11
13
  # DevLoom Documenter Flash
12
14
 
13
15
  ENGLISH ONLY: All output MUST be in English. Never use any other language.
14
16
 
17
+ BEFORE SCANNING: if `.opencode/devloom/context/briefing.md` exists, read it FIRST and start from its file map — never re-read files it already summarizes.
18
+
15
19
  COMPLIANCE: Follow the RULES below + your skill LOAD. No rule may be skipped.
16
20
  RULES: EN | SOLID+TDD+CleanArch | tests+regr required | doing<=1 | FILES: use .opencode/devloom/.tmp/ | peer-review for high-risk | degrade on 2x failure
17
21
  LOAD: ~/.config/opencode/skills/ship/documentation.md
@@ -6,12 +6,16 @@ permission:
6
6
  edit: allow
7
7
  bash: allow
8
8
  task: deny
9
+ external_directory:
10
+ "~/.config/opencode/**": allow
9
11
  ---
10
12
 
11
13
  # DevLoom Documenter
12
14
 
13
15
  ENGLISH ONLY: All output MUST be in English. Never use any other language.
14
16
 
17
+ BEFORE SCANNING: if `.opencode/devloom/context/briefing.md` exists, read it FIRST and start from its file map — never re-read files it already summarizes.
18
+
15
19
  COMPLIANCE: Follow the RULES below + your skill LOAD. No rule may be skipped.
16
20
  RULES: EN | SOLID+TDD+CleanArch | tests+regr required | doing<=1 | FILES: use .opencode/devloom/.tmp/ | peer-review for high-risk | degrade on 2x failure
17
21
  LOAD: ~/.config/opencode/skills/ship/documentation.md
@@ -12,6 +12,8 @@ permission:
12
12
  bash: allow
13
13
  glob: allow
14
14
  grep: allow
15
+ external_directory:
16
+ "~/.config/opencode/**": allow
15
17
  ---
16
18
 
17
19
  # DevLoom Orchestrator
@@ -103,6 +105,8 @@ TIER DEGRADATION: If a sub-agent fails twice consecutively (timeout, rate-limit,
103
105
 
104
106
  ARCHITECTURE ATLAS: Before delegating to any sub-agent, check if `.opencode/devloom/context/atlas.md` exists. If it does, pass the `ATLAS:` section to sub-agents so they understand the codebase structure without scanning files. The atlas is auto-generated by the plugin.
105
107
 
108
+ INCREMENTAL BRIEFING (never re-read unchanged files): On ticket start, locate `briefing.mjs` with the profile.mjs fallback chain (`~/.config/opencode/commands/briefing.mjs`, else `~/.config/opencode/devloom-scripts/briefing.mjs`, else `scripts/briefing.mjs`) and run `init <ticket>`, then write `.opencode/devloom/context/briefing.md` ONCE (goal, constraints, file map with file:line refs, decisions) — read each file only while building it. Start EVERY task() prompt with: `Read .opencode/devloom/context/briefing.md first — do not re-discover; read other files only for code-level detail beyond it.` Before each subsequent dispatch run `changed`: re-read ONLY the printed paths, patch the briefing, run `touch`, then dispatch. Files not listed must never be re-read; pass the briefing path (not file contents) to keep prompts stable for provider prompt-caching.
109
+
106
110
  VISUAL PIPELINE (UI work with VISUAL_REQUIRED=true only):
107
111
  1. visual-director FIRST: `task(subagent_type: "devloom-visual-director", ...)` — emits a `DESIGN_FILE` markdown block (tokens: colors, type, spacing, layout, motion, components).
108
112
  2. PERSIST DESIGN: you write the DESIGN_FILE body to `.opencode/devloom/context/design.md` (bash is allowed for this persistence). Pass that file's content as `DESIGN_CONTRACT:` to planner (if planning) and to every developer/critic delegation in the chain.
@@ -6,12 +6,16 @@ permission:
6
6
  edit: allow
7
7
  bash: allow
8
8
  task: deny
9
+ external_directory:
10
+ "~/.config/opencode/**": allow
9
11
  ---
10
12
 
11
13
  # DevLoom Planner Flash
12
14
 
13
15
  ENGLISH ONLY: All output MUST be in English. Never use any other language.
14
16
 
17
+ BEFORE SCANNING: if `.opencode/devloom/context/briefing.md` exists, read it FIRST and start from its file map — never re-read files it already summarizes.
18
+
15
19
  COMPLIANCE: Follow the RULES below + your skill LOAD. No rule may be skipped.
16
20
  RULES: EN | SOLID+TDD+CleanArch | tests+regr required | doing<=1 | FILES: use .opencode/devloom/.tmp/ | peer-review for high-risk | degrade on 2x failure
17
21
  LOAD: ~/.config/opencode/skills/plan/planning.md|~/.config/opencode/skills/plan/verification-planning.md
@@ -6,12 +6,16 @@ permission:
6
6
  edit: allow
7
7
  bash: allow
8
8
  task: deny
9
+ external_directory:
10
+ "~/.config/opencode/**": allow
9
11
  ---
10
12
 
11
13
  # DevLoom Planner Senior
12
14
 
13
15
  ENGLISH ONLY: All output MUST be in English. Never use any other language.
14
16
 
17
+ BEFORE SCANNING: if `.opencode/devloom/context/briefing.md` exists, read it FIRST and start from its file map — never re-read files it already summarizes.
18
+
15
19
  COMPLIANCE: Follow the RULES below + your skill LOAD. No rule may be skipped.
16
20
  RULES: EN | SOLID+TDD+CleanArch | tests+regr required | doing<=1 | FILES: use .opencode/devloom/.tmp/ | peer-review for high-risk | degrade on 2x failure
17
21
  LOAD: ~/.config/opencode/skills/plan/planning.md|~/.config/opencode/skills/plan/verification-planning.md
@@ -6,12 +6,16 @@ permission:
6
6
  edit: allow
7
7
  bash: allow
8
8
  task: deny
9
+ external_directory:
10
+ "~/.config/opencode/**": allow
9
11
  ---
10
12
 
11
13
  # DevLoom Planner
12
14
 
13
15
  ENGLISH ONLY: All output MUST be in English. Never use any other language.
14
16
 
17
+ BEFORE SCANNING: if `.opencode/devloom/context/briefing.md` exists, read it FIRST and start from its file map — never re-read files it already summarizes.
18
+
15
19
  COMPLIANCE: Follow the RULES below + your skill LOAD. No rule may be skipped.
16
20
  RULES: EN | SOLID+TDD+CleanArch | tests+regr required | doing<=1 | FILES: use .opencode/devloom/.tmp/ | peer-review for high-risk | degrade on 2x failure
17
21
  LOAD: ~/.config/opencode/skills/plan/planning.md|~/.config/opencode/skills/plan/verification-planning.md
@@ -6,12 +6,16 @@ permission:
6
6
  edit: allow
7
7
  bash: allow
8
8
  task: deny
9
+ external_directory:
10
+ "~/.config/opencode/**": allow
9
11
  ---
10
12
 
11
13
  # DevLoom QA Flash
12
14
 
13
15
  ENGLISH ONLY: All output MUST be in English. Never use any other language.
14
16
 
17
+ BEFORE SCANNING: if `.opencode/devloom/context/briefing.md` exists, read it FIRST and start from its file map — never re-read files it already summarizes.
18
+
15
19
  COMPLIANCE: Follow the RULES below + your skill LOAD. No rule may be skipped.
16
20
  RULES: EN | SOLID+TDD+CleanArch | tests+regr required | doing<=1 | FILES: use .opencode/devloom/.tmp/ | peer-review for high-risk | degrade on 2x failure
17
21
  LOAD: ~/.config/opencode/skills/verify/quality-assurance.md|~/.config/opencode/skills/build/simplify.md
@@ -6,12 +6,16 @@ permission:
6
6
  edit: allow
7
7
  bash: allow
8
8
  task: deny
9
+ external_directory:
10
+ "~/.config/opencode/**": allow
9
11
  ---
10
12
 
11
13
  # DevLoom QA
12
14
 
13
15
  ENGLISH ONLY: All output MUST be in English. Never use any other language.
14
16
 
17
+ BEFORE SCANNING: if `.opencode/devloom/context/briefing.md` exists, read it FIRST and start from its file map — never re-read files it already summarizes.
18
+
15
19
  COMPLIANCE: Follow the RULES below + your skill LOAD. No rule may be skipped.
16
20
  RULES: EN | SOLID+TDD+CleanArch | tests+regr required | doing<=1 | FILES: use .opencode/devloom/.tmp/ | peer-review for high-risk | degrade on 2x failure
17
21
  LOAD: ~/.config/opencode/skills/verify/quality-assurance.md|~/.config/opencode/skills/build/simplify.md
@@ -6,12 +6,16 @@ permission:
6
6
  edit: allow
7
7
  bash: allow
8
8
  task: deny
9
+ external_directory:
10
+ "~/.config/opencode/**": allow
9
11
  ---
10
12
 
11
13
  # DevLoom Security Senior
12
14
 
13
15
  ENGLISH ONLY: All output MUST be in English. Never use any other language.
14
16
 
17
+ BEFORE SCANNING: if `.opencode/devloom/context/briefing.md` exists, read it FIRST and start from its file map — never re-read files it already summarizes.
18
+
15
19
  COMPLIANCE: Follow the RULES below + your skill LOAD. No rule may be skipped.
16
20
  RULES: EN | SOLID+TDD+CleanArch | tests+regr required | doing<=1 | FILES: use .opencode/devloom/.tmp/ | peer-review for high-risk | degrade on 2x failure
17
21
  LOAD: ~/.config/opencode/skills/review/security-review.md
@@ -6,12 +6,16 @@ permission:
6
6
  edit: allow
7
7
  bash: allow
8
8
  task: deny
9
+ external_directory:
10
+ "~/.config/opencode/**": allow
9
11
  ---
10
12
 
11
13
  # DevLoom Security
12
14
 
13
15
  ENGLISH ONLY: All output MUST be in English. Never use any other language.
14
16
 
17
+ BEFORE SCANNING: if `.opencode/devloom/context/briefing.md` exists, read it FIRST and start from its file map — never re-read files it already summarizes.
18
+
15
19
  COMPLIANCE: Follow the RULES below + your skill LOAD. No rule may be skipped.
16
20
  RULES: EN | SOLID+TDD+CleanArch | tests+regr required | doing<=1 | FILES: use .opencode/devloom/.tmp/ | peer-review for high-risk | degrade on 2x failure
17
21
  LOAD: ~/.config/opencode/skills/review/security-review.md
@@ -6,12 +6,16 @@ permission:
6
6
  edit: allow
7
7
  bash: allow
8
8
  task: deny
9
+ external_directory:
10
+ "~/.config/opencode/**": allow
9
11
  ---
10
12
 
11
13
  # DevLoom Verifier
12
14
 
13
15
  ENGLISH ONLY: All output MUST be in English. Never use any other language.
14
16
 
17
+ BEFORE SCANNING: if `.opencode/devloom/context/briefing.md` exists, read it FIRST and start from its file map — never re-read files it already summarizes.
18
+
15
19
  COMPLIANCE: Follow the RULES below + your skill LOAD. No rule may be skipped.
16
20
  RULES: EN | SOLID+TDD+CleanArch | tests+regr required | doing<=1 | FILES: use .opencode/devloom/.tmp/ | peer-review for high-risk | degrade on 2x failure
17
21
  LOAD: ~/.config/opencode/skills/verify/app-verification.md
@@ -12,12 +12,16 @@ permission:
12
12
  grep: deny
13
13
  webfetch: deny
14
14
  task: deny
15
+ external_directory:
16
+ "~/.config/opencode/**": allow
15
17
  ---
16
18
 
17
19
  # DevLoom Vision
18
20
 
19
21
  ENGLISH ONLY: All output MUST be in English. Never use any other language.
20
22
 
23
+ BEFORE SCANNING: if `.opencode/devloom/context/briefing.md` exists, read it FIRST and start from its file map — never re-read files it already summarizes.
24
+
21
25
  ## ROLE — STRICT
22
26
 
23
27
  You are a **read-only image description machine**. Your ONLY job:
@@ -12,12 +12,16 @@ permission:
12
12
  grep: deny
13
13
  webfetch: deny
14
14
  task: deny
15
+ external_directory:
16
+ "~/.config/opencode/**": allow
15
17
  ---
16
18
 
17
19
  # DevLoom Visual Critic
18
20
 
19
21
  ENGLISH ONLY: All output MUST be in English. Never use any other language.
20
22
 
23
+ BEFORE SCANNING: if `.opencode/devloom/context/briefing.md` exists, read it FIRST and start from its file map — never re-read files it already summarizes.
24
+
21
25
  ## ROLE — STRICT
22
26
 
23
27
  You are an INDEPENDENT visual judge. You are READ-ONLY. You receive a brief, the design memory, screenshots/rendered state, viewport info, and the user goal. You produce visual judgment — nothing else.
@@ -12,12 +12,16 @@ permission:
12
12
  patch: deny
13
13
  webfetch: deny
14
14
  task: deny
15
+ external_directory:
16
+ "~/.config/opencode/**": allow
15
17
  ---
16
18
 
17
19
  # DevLoom Visual Director
18
20
 
19
21
  ENGLISH ONLY: All output MUST be in English. Never use any other language.
20
22
 
23
+ BEFORE SCANNING: if `.opencode/devloom/context/briefing.md` exists, read it FIRST and start from its file map — never re-read files it already summarizes.
24
+
21
25
  ## ROLE — STRICT
22
26
 
23
27
  You make VISUAL DECISIONS. You are READ-ONLY regarding application implementation. You may inspect requirements, plans, project code, existing design systems, screenshots, design references, and project context — you must NOT implement, edit, or create any file, and must NOT run shell commands.
@@ -44,6 +44,6 @@ node "$PROFILE_MJS" current 2>&1
44
44
  - `@devloom-visual-director` — ask for UI visual direction / a design brief
45
45
  - `@devloom-visual-critic` — ask for an independent screenshot critique
46
46
 
47
- Switch profiles: `/devloom-go`, `/devloom-go-economy`, `/devloom-free`, `/devloom-mimo`
47
+ Switch profiles: `/devloom-go`, `/devloom-go-economy`, `/devloom-free`, `/devloom-mimo`, `/devloom-deepseek-mimo`
48
48
 
49
49
  After switching profiles, restart opencode (or continue with `opencode --continue`) to see the updated profile and agents in the sidebar. If the sidebar does not reflect the current DevLoom code, run `/devloom-refresh`.
@@ -0,0 +1,11 @@
1
+ ---
2
+ description: "DevLoom DeepSeek+MiMo: fast general roles on DeepSeek V4.1, graphical/complex roles on MiMo V2.6"
3
+ ---
4
+
5
+ # DevLoom DeepSeek+MiMo
6
+
7
+ Switch DevLoom to the hybrid profile: the 7 general roles (orchestrator, planner, developer, qa, verifier, security, documenter) resolve to DeepSeek V4.1 Flash for fast everyday work; the graphical/complex roles (vision, visual-director, visual-critic) resolve to the MiMo V2.6 Flash candidate chain (`mimo/mimo-v2.6-flash` → `xiaomi/mimo-v2.6-flash` → `opencode-go/mimo-v2.6-flash`), against `opencode models`. DeepSeek chain: `opencode-go/deepseek-v4.1-flash`.
8
+
9
+ !`PM="$HOME/.config/opencode/commands/profile.mjs"; [ -f "$PM" ] || PM="$HOME/.config/opencode/devloom-scripts/profile.mjs"; [ -f "$PM" ] || { echo "profile.mjs not found - reinstall DevLoom"; exit 1; }; node "$PM" set deepseek-mimo 2>&1; node "$PM" current 2>&1`
10
+
11
+ If either chain resolves to nothing the profile is NOT activated and the attempted candidates are printed — fail fast, no unrelated substitution. On success: profile applied — restart opencode (or continue with `opencode --continue`) to see the updated profile and agents in the sidebar.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "devloom",
3
- "version": "1.1.0",
3
+ "version": "1.2.0",
4
4
  "description": "DevLoom – Autonomous Development Weaver for OpenCode. Transforms a single prompt into fully tested, documented software.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
package/postinstall.mjs CHANGED
@@ -11,9 +11,8 @@ export function getConfigDir(_platform = platform(), _homedir = homedir()) {
11
11
  if (_platform === "win32") {
12
12
  return resolve(process.env.APPDATA ?? _homedir, "opencode")
13
13
  }
14
- if (_platform === "darwin") {
15
- return resolve(_homedir, "Library", "Application Support", "opencode")
16
- }
14
+ // OpenCode resolves its global config from the XDG path on macOS too
15
+ // (~/.config/opencode), not ~/Library/Application Support.
17
16
  return resolve(
18
17
  process.env.XDG_CONFIG_HOME ?? join(_homedir, ".config"),
19
18
  "opencode"
@@ -148,7 +147,7 @@ const AGENTS = [
148
147
  "devloom-documenter-flash",
149
148
  ]
150
149
 
151
- const COMMANDS = ["devloom", "devloom-status", "devloom-resume", "devloom-init", "devloom-save", "devloom-auto", "devloom-go", "devloom-go-economy", "devloom-go-flash", "devloom-deepseek", "devloom-free", "devloom-mimo", "devloom-plan", "devloom-context", "devloom-agents", "devloom-refresh"]
150
+ const COMMANDS = ["devloom", "devloom-status", "devloom-resume", "devloom-init", "devloom-save", "devloom-auto", "devloom-go", "devloom-go-economy", "devloom-go-flash", "devloom-deepseek", "devloom-free", "devloom-mimo", "devloom-deepseek-mimo", "devloom-plan", "devloom-context", "devloom-agents", "devloom-refresh"]
152
151
 
153
152
  const SCRIPTS_DIR = resolve(CONFIG_DIR, "devloom-scripts")
154
153
 
@@ -200,6 +199,11 @@ function main() {
200
199
  resolve(COMMANDS_DIR, "model-capabilities.mjs"),
201
200
  "Model capabilities helper"
202
201
  )
202
+ installFile(
203
+ resolve(__dirname, "scripts", "briefing.mjs"),
204
+ resolve(COMMANDS_DIR, "briefing.mjs"),
205
+ "Briefing change tracker"
206
+ )
203
207
  installFile(
204
208
  resolve(__dirname, "scripts", "plugin-cache.mjs"),
205
209
  resolve(COMMANDS_DIR, "plugin-cache.mjs"),
@@ -95,6 +95,15 @@ A single multimodal stack: every role — including vision, visual-director, and
95
95
  |---|---|
96
96
  | all 10 roles | resolved MiMo V2.6 Flash candidate |
97
97
 
98
+ ### deepseek-mimo (hybrid, tier unlocked)
99
+
100
+ Fast everyday work on DeepSeek V4.1 Flash; graphical/complex work on MiMo V2.6 Flash. The 7 general roles (orchestrator, planner, developer, qa, verifier, security, documenter) resolve to `opencode-go/deepseek-v4.1-flash`; vision, visual-director, and visual-critic resolve through the `MIMO_V26_FLASH_CANDIDATES` chain. Both chains resolve strictly against `opencode models` — if either finds nothing the profile exits code 1 with the attempted candidates, never an unrelated substitution. Activate with `/devloom-deepseek-mimo`.
101
+
102
+ | Role | Model |
103
+ |---|---|
104
+ | 7 general roles | opencode-go/deepseek-v4.1-flash |
105
+ | vision, visual-director, visual-critic | resolved MiMo V2.6 Flash candidate |
106
+
98
107
  ## Model Guidance
99
108
 
100
109
  ### When to Use GLM 5.2
@@ -157,7 +166,7 @@ In `.opencode/devloom/project/config.json`:
157
166
  }
158
167
  ```
159
168
 
160
- Valid values: `"go"`, `"go-economy"`, `"deepseek"`, `"go-flash"`, `"free"`, `"mimo"`.
169
+ Valid values: `"go"`, `"go-economy"`, `"deepseek"`, `"go-flash"`, `"free"`, `"mimo"`, `"deepseek-mimo"`.
161
170
 
162
171
  ### Overrides
163
172
 
@@ -0,0 +1,192 @@
1
+ #!/usr/bin/env node
2
+ // Incremental briefing tracker: answers "what changed since the briefing was
3
+ // written?" so orchestrator/sub-agents re-read ONLY modified files.
4
+ //
5
+ // Signal: sha256 content fingerprints recorded at init/touch — the same
6
+ // content-addressed idea as git, but immune to commit/uncommitted state (a
7
+ // file already re-read after `touch` never reappears until it truly changes).
8
+ // The git baseCommit is kept for provenance/status only.
9
+ //
10
+ // Commands:
11
+ // briefing.mjs init <ticket> create briefing.md stub + record fingerprints
12
+ // briefing.mjs changed print changed paths (one per line), exit 0
13
+ // briefing.mjs touch re-anchor after the briefing was patched
14
+ // briefing.mjs status show ticket/base/counters
15
+ import { readFileSync, writeFileSync, existsSync, mkdirSync, readdirSync } from "fs"
16
+ import { execSync } from "child_process"
17
+ import { resolve, dirname, join, relative, sep } from "path"
18
+ import { fileURLToPath } from "url"
19
+ import { createHash } from "crypto"
20
+
21
+ const DIR = ".opencode/devloom/context"
22
+ export const BRIEFING_MD = `${DIR}/briefing.md`
23
+ export const BRIEFING_JSON = `${DIR}/briefing.json`
24
+
25
+ const EXCLUDE_DIRS = new Set([
26
+ "node_modules", ".git", "dist", ".angular", ".next", "build", "coverage",
27
+ "logs", ".tmp", "tmp", ".opencode", ".idea", ".vscode",
28
+ ])
29
+ const EXCLUDE_FILES = /^(package-lock\.json|pnpm-lock\.yaml|yarn\.lock|\.DS_Store|.*\.log)$/
30
+
31
+ export function walkFiles(root = process.cwd()) {
32
+ const out = []
33
+ const stack = [root]
34
+ while (stack.length > 0) {
35
+ const dir = stack.pop()
36
+ let entries = []
37
+ try {
38
+ entries = readdirSync(dir, { withFileTypes: true })
39
+ } catch {
40
+ continue
41
+ }
42
+ for (const entry of entries) {
43
+ if (entry.name.startsWith(".") && entry.isDirectory() && entry.name !== ".ai") continue
44
+ const full = join(dir, entry.name)
45
+ if (entry.isDirectory()) {
46
+ if (!EXCLUDE_DIRS.has(entry.name)) stack.push(full)
47
+ continue
48
+ }
49
+ if (!entry.isFile() || EXCLUDE_FILES.test(entry.name)) continue
50
+ const rel = relative(root, full).split(sep).join("/")
51
+ out.push(rel)
52
+ }
53
+ }
54
+ return out.sort()
55
+ }
56
+
57
+ export function hashFile(rel, root = process.cwd()) {
58
+ try {
59
+ const data = readFileSync(join(root, rel))
60
+ return createHash("sha256").update(data).digest("hex").slice(0, 12)
61
+ } catch {
62
+ return null
63
+ }
64
+ }
65
+
66
+ export function computeHashes(paths, root = process.cwd()) {
67
+ const files = {}
68
+ for (const p of paths) files[p] = hashFile(p, root)
69
+ return files
70
+ }
71
+
72
+ export function diffHashes(previous, paths, root = process.cwd()) {
73
+ const changed = []
74
+ const prev = previous || {}
75
+ for (const p of paths) {
76
+ if (prev[p] !== hashFile(p, root)) changed.push(p)
77
+ }
78
+ for (const p of Object.keys(prev)) {
79
+ if (!paths.includes(p) && prev[p] !== null) changed.push(p)
80
+ }
81
+ return [...new Set(changed)].sort()
82
+ }
83
+
84
+ function run(cmd, cwd = process.cwd()) {
85
+ try {
86
+ return execSync(cmd, { cwd, encoding: "utf8", timeout: 15000 }).trim()
87
+ } catch {
88
+ return null
89
+ }
90
+ }
91
+
92
+ export function gitAvailable(cwd = process.cwd()) {
93
+ return run("git rev-parse --is-inside-work-tree", cwd) === "true"
94
+ }
95
+
96
+ export function gitHead(cwd = process.cwd()) {
97
+ const out = run("git rev-parse HEAD", cwd)
98
+ return out && !out.includes("fatal") ? out : null
99
+ }
100
+
101
+ function readJson() {
102
+ try {
103
+ return JSON.parse(readFileSync(BRIEFING_JSON, "utf8"))
104
+ } catch {
105
+ return null
106
+ }
107
+ }
108
+
109
+ function writeJson(data) {
110
+ mkdirSync(DIR, { recursive: true })
111
+ writeFileSync(BRIEFING_JSON, JSON.stringify(data, null, 2) + "\n")
112
+ }
113
+
114
+ export function init(ticket = "ticket") {
115
+ mkdirSync(DIR, { recursive: true })
116
+ const paths = walkFiles()
117
+ const useGit = gitAvailable()
118
+ const data = {
119
+ ticket,
120
+ baseCommit: useGit ? gitHead() : null,
121
+ files: computeHashes(paths),
122
+ updatedAt: new Date().toISOString(),
123
+ }
124
+ writeJson(data)
125
+ if (!existsSync(BRIEFING_MD)) {
126
+ writeFileSync(
127
+ BRIEFING_MD,
128
+ `# Briefing — ${ticket}\n` +
129
+ `> Created: ${data.updatedAt}\n` +
130
+ `> Refresh rule: run \`briefing.mjs changed\`, re-read ONLY those paths, patch this file, then \`briefing.mjs touch\`.\n` +
131
+ `> Every task() prompt starts with: "Read .opencode/devloom/context/briefing.md first — do not re-discover."\n\n` +
132
+ `## Goal\n\n## Constraints\n\n## File map\n\n## Decisions\n`
133
+ )
134
+ }
135
+ return data
136
+ }
137
+
138
+ export function changed() {
139
+ const data = readJson()
140
+ if (!data) return []
141
+ return diffHashes(data.files, walkFiles())
142
+ }
143
+
144
+ export function touch() {
145
+ const data = readJson() || init("ticket")
146
+ const paths = walkFiles()
147
+ data.baseCommit = gitAvailable() ? gitHead() : null
148
+ data.files = computeHashes(paths)
149
+ data.updatedAt = new Date().toISOString()
150
+ writeJson(data)
151
+ return data
152
+ }
153
+
154
+ export function status() {
155
+ const data = readJson()
156
+ if (!data) {
157
+ console.log("No briefing recorded. Run: briefing.mjs init <ticket>")
158
+ return
159
+ }
160
+ console.log(`Ticket: ${data.ticket}`)
161
+ console.log(`Base commit: ${data.baseCommit || "(no git)"}`)
162
+ console.log(`Tracked files: ${Object.keys(data.files).length}`)
163
+ console.log(`Updated: ${data.updatedAt}`)
164
+ }
165
+
166
+ function main() {
167
+ const args = process.argv.slice(2)
168
+ const command = args[0] || ""
169
+ switch (command) {
170
+ case "init":
171
+ init(args[1] || "ticket")
172
+ console.log(`Briefing initialized${args[1] ? ` for ${args[1]}` : ""}: ${BRIEFING_MD}`)
173
+ break
174
+ case "changed": {
175
+ const paths = changed()
176
+ for (const p of paths) console.log(p)
177
+ break
178
+ }
179
+ case "touch":
180
+ touch()
181
+ console.log("Briefing anchors refreshed.")
182
+ break
183
+ case "status":
184
+ status()
185
+ break
186
+ default:
187
+ console.log("Usage: briefing.mjs <init <ticket>|changed|touch|status>")
188
+ }
189
+ }
190
+
191
+ const isMain = process.argv[1] ? fileURLToPath(import.meta.url) === resolve(process.argv[1]) : false
192
+ if (isMain) main()
@@ -35,11 +35,36 @@ export const MODEL_FAMILIES = {
35
35
  verification: "high",
36
36
  },
37
37
  },
38
+ deepseekV41Flash: {
39
+ id: "deepseekV41Flash",
40
+ label: "DeepSeek V4.1 Flash",
41
+ // Fast lane for general roles. Only verified ids enter the chain; the
42
+ // family pattern still discovers same-family variants at resolution time.
43
+ candidates: [
44
+ "opencode-go/deepseek-v4.1-flash",
45
+ ],
46
+ familyPattern: /(^|\/)deepseek-v4\.1/i,
47
+ capabilities: {
48
+ vision: false,
49
+ multimodal: false,
50
+ coding: "high",
51
+ reasoning: "high",
52
+ toolUse: true,
53
+ longContext: true,
54
+ speed: "fast",
55
+ costClass: "low",
56
+ orchestration: true,
57
+ verification: "high",
58
+ },
59
+ },
38
60
  }
39
61
 
40
62
  /** MiMo V2.6 Flash candidate chain (re-exported for profile manager + tests). */
41
63
  export const MIMO_V26_FLASH_CANDIDATES = MODEL_FAMILIES.mimoV26Flash.candidates
42
64
 
65
+ /** DeepSeek V4.1 Flash candidate chain (general/fast roles). */
66
+ export const DEEPSEEK_V41_FLASH_CANDIDATES = MODEL_FAMILIES.deepseekV41Flash.candidates
67
+
43
68
  /**
44
69
  * Vision-capable fallbacks used when a vision-required role (vision,
45
70
  * visual-critic) resolves to a text-only model. Ordered by existing DevLoom
@@ -20,12 +20,12 @@ export function getOpenCodeCacheDir(_platform = platform(), _homedir = homedir()
20
20
  return resolve(_env.LOCALAPPDATA ?? _homedir, "cache", "opencode")
21
21
  }
22
22
  if (_platform === "darwin") {
23
+ const xdg = resolve(_env.XDG_CACHE_HOME ?? join(_homedir, ".cache"), "opencode")
23
24
  const lib = resolve(_homedir, "Library", "Caches", "opencode")
24
- const legacy = resolve(_env.XDG_CACHE_HOME ?? join(_homedir, ".cache"), "opencode")
25
- // Some opencode builds keep using ~/.cache/opencode on macOS; prefer the
26
- // platform path unless only the legacy one exists on disk.
27
- if (!existsSync(lib) && existsSync(legacy)) return legacy
28
- return lib
25
+ // OpenCode uses the XDG cache path on macOS too; only fall back to the
26
+ // Library path when that is the one present on disk (older builds).
27
+ if (!existsSync(xdg) && existsSync(lib)) return lib
28
+ return xdg
29
29
  }
30
30
  return resolve(_env.XDG_CACHE_HOME ?? join(_homedir, ".cache"), "opencode")
31
31
  }
@@ -16,6 +16,7 @@ import {
16
16
  modelMeetsRole,
17
17
  roleRequiresVision,
18
18
  MIMO_V26_FLASH_CANDIDATES,
19
+ DEEPSEEK_V41_FLASH_CANDIDATES,
19
20
  MODEL_FAMILIES,
20
21
  VISION_FALLBACK_CANDIDATES,
21
22
  } from "./model-capabilities.mjs"
@@ -30,6 +31,7 @@ export {
30
31
  modelMeetsRole,
31
32
  roleRequiresVision,
32
33
  MIMO_V26_FLASH_CANDIDATES,
34
+ DEEPSEEK_V41_FLASH_CANDIDATES,
33
35
  MODEL_FAMILIES,
34
36
  VISION_FALLBACK_CANDIDATES,
35
37
  }
@@ -255,6 +257,35 @@ function containsKey(obj, key) {
255
257
  return Object.prototype.hasOwnProperty.call(obj, key)
256
258
  }
257
259
 
260
+ // Strict family resolution used by `deepseek-mimo`: resolves a candidate chain
261
+ // against the live catalog and FAILS FAST with diagnostics when nothing
262
+ // matches. Never substitutes unrelated models — a blocked dispatch is worse
263
+ // than a clear exit the user can act on.
264
+ function resolveStrictCandidate(label, candidates, available, familyPattern, profileName) {
265
+ const resolved = resolveModelCandidate(candidates, available, { familyPattern })
266
+ if (!resolved.model) {
267
+ console.error(`${label} is not available in this environment.`)
268
+ console.error("")
269
+ console.error("Attempted candidates:")
270
+ for (const candidate of resolved.attempted) {
271
+ console.error(` ${candidate} (not found)`)
272
+ }
273
+ const compatible = familyPattern ? available.filter((m) => familyPattern.test(m)) : []
274
+ console.error("")
275
+ if (compatible.length > 0) {
276
+ console.error(`Available compatible ${label} models:`)
277
+ for (const m of compatible) console.error(` ${m}`)
278
+ } else {
279
+ console.error(`No compatible ${label} models are available. Run: opencode models`)
280
+ }
281
+ console.error("")
282
+ console.error(`The ${profileName} profile was NOT activated. No unrelated models were substituted.`)
283
+ console.error("Valid profiles: auto, go, go-economy, deepseek, glm, go-flash, mimo, deepseek-mimo, free")
284
+ process.exit(1)
285
+ }
286
+ return resolved
287
+ }
288
+
258
289
  export function applyFallbacks(models, unavailable, available) {
259
290
  const result = { ...models }
260
291
  const fallbacks = {}
@@ -367,7 +398,7 @@ export function cmdSet(profileName) {
367
398
  const available = detectAvailableModels()
368
399
  const hasGo = hasGoModels(available)
369
400
 
370
- if ((profileName === "go" || profileName === "go-economy" || profileName === "deepseek" || profileName === "glm" || profileName === "go-flash") && !hasGo) {
401
+ if ((profileName === "go" || profileName === "go-economy" || profileName === "deepseek" || profileName === "glm" || profileName === "go-flash" || profileName === "deepseek-mimo") && !hasGo) {
371
402
  console.error("No OpenCode Go models detected. Run: opencode /connect to add OpenCode Go.")
372
403
  console.error("Fall back to: /devloom-auto or /devloom-free")
373
404
  process.exit(1)
@@ -450,7 +481,7 @@ export function cmdSet(profileName) {
450
481
  }
451
482
  console.error("")
452
483
  console.error("The mimo profile was NOT activated. No unrelated models were substituted.")
453
- console.error("Valid profiles: auto, go, go-economy, deepseek, glm, go-flash, mimo, free")
484
+ console.error("Valid profiles: auto, go, go-economy, deepseek, glm, go-flash, mimo, deepseek-mimo, free")
454
485
  process.exit(1)
455
486
  }
456
487
  models = Object.fromEntries(ALL_AGENTS.map((agent) => [agent, resolved.model]))
@@ -463,6 +494,39 @@ export function cmdSet(profileName) {
463
494
  }
464
495
  }
465
496
  }
497
+ } else if (profileName === "deepseek-mimo") {
498
+ // Hybrid: DeepSeek V4.1 Flash is the fast lane for the 7 general roles;
499
+ // MiMo V2.6 Flash serves the graphical/complex visual roles. Both chains
500
+ // resolve strictly — exit 1 with diagnostics instead of substituting.
501
+ const deepseekResolved = resolveStrictCandidate(
502
+ "DeepSeek V4.1 Flash",
503
+ DEEPSEEK_V41_FLASH_CANDIDATES,
504
+ available,
505
+ MODEL_FAMILIES.deepseekV41Flash.familyPattern,
506
+ "deepseek-mimo"
507
+ )
508
+ const mimoResolved = resolveStrictCandidate(
509
+ "MiMo V2.6 Flash",
510
+ MIMO_V26_FLASH_CANDIDATES,
511
+ available,
512
+ MODEL_FAMILIES.mimoV26Flash.familyPattern,
513
+ "deepseek-mimo"
514
+ )
515
+ const visualRoles = new Set(["vision", "visual-director", "visual-critic"])
516
+ models = Object.fromEntries(
517
+ ALL_AGENTS.map((agent) => [agent, visualRoles.has(agent) ? mimoResolved.model : deepseekResolved.model])
518
+ )
519
+ const recordChainFallback = (agent, resolved, chainLabel) => {
520
+ if (resolved.exact) return
521
+ fallbacks[agent] = {
522
+ from: `${chainLabel} (candidate chain)`,
523
+ to: resolved.model,
524
+ reason: "family fallback",
525
+ }
526
+ }
527
+ for (const agent of ALL_AGENTS) {
528
+ recordChainFallback(agent, visualRoles.has(agent) ? mimoResolved : deepseekResolved, visualRoles.has(agent) ? "mimo-v2.6-flash" : "deepseek-v4.1-flash")
529
+ }
466
530
  } else if (profileName === "auto") {
467
531
  if (hasGo) {
468
532
  models = { ...PROFILES["go-flash"] }
@@ -479,7 +543,7 @@ export function cmdSet(profileName) {
479
543
  }
480
544
  } else {
481
545
  console.error("Unknown profile:", profileName)
482
- console.error("Valid profiles: auto, go, go-economy, deepseek, glm, go-flash, mimo, free")
546
+ console.error("Valid profiles: auto, go, go-economy, deepseek, glm, go-flash, mimo, deepseek-mimo, free")
483
547
  process.exit(1)
484
548
  }
485
549
 
@@ -648,7 +712,7 @@ function main() {
648
712
 
649
713
  switch (command) {
650
714
  case "set":
651
- if (!args[1]) { console.error("Usage: devloom profile set <auto|go|go-economy|deepseek|glm|go-flash|mimo|free>"); process.exit(1) }
715
+ if (!args[1]) { console.error("Usage: devloom profile set <auto|go|go-economy|deepseek|glm|go-flash|mimo|deepseek-mimo|free>"); process.exit(1) }
652
716
  cmdSet(args[1])
653
717
  console.log(`DevLoom profile changed to: ${args[1]}`)
654
718
  if (args[1] !== "free") {
@@ -678,7 +742,7 @@ function main() {
678
742
  console.log("DevLoom Profile Manager")
679
743
  console.log("")
680
744
  console.log("Commands:")
681
- console.log(" devloom profile set <auto|go|go-economy|deepseek|glm|go-flash|mimo|free>")
745
+ console.log(" devloom profile set <auto|go|go-economy|deepseek|glm|go-flash|mimo|deepseek-mimo|free>")
682
746
  console.log(" devloom profile tier <senior|standard>")
683
747
  console.log(" devloom profile current")
684
748
  console.log(" devloom profile validate")