cc-codeconductor 0.2.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 (93) hide show
  1. package/LICENSE +18 -0
  2. package/README.md +319 -0
  3. package/dist/index.js +12741 -0
  4. package/package.json +51 -0
  5. package/policy.yml +124 -0
  6. package/presets/claude/CLAUDE.md +598 -0
  7. package/presets/claude/commands/cc/feature.md +115 -0
  8. package/presets/claude/commands/cc/fix.md +121 -0
  9. package/presets/claude/commands/cc/refactor.md +148 -0
  10. package/presets/claude/commands/cc/review.md +126 -0
  11. package/presets/claude/commands/cc/tdd-cycle.md +226 -0
  12. package/presets/claude/commands/cc/test-plan.md +138 -0
  13. package/presets/claude/settings.json +37 -0
  14. package/presets/claude/skills/api-versioning/SKILL.md +389 -0
  15. package/presets/claude/skills/django-orm/SKILL.md +455 -0
  16. package/presets/claude/skills/django-testing/SKILL.md +409 -0
  17. package/presets/claude/skills/jpa-postgres/SKILL.md +618 -0
  18. package/presets/claude/skills/python/SKILL.md +606 -0
  19. package/presets/claude/skills/python-django-stack/SKILL.md +492 -0
  20. package/presets/claude/skills/python-fastapi-stack/SKILL.md +457 -0
  21. package/presets/claude/skills/spring-boot-feature/SKILL.md +558 -0
  22. package/presets/claude/skills/spring-boot-kotlin/SKILL.md +400 -0
  23. package/presets/claude/skills/sqlalchemy/SKILL.md +466 -0
  24. package/presets/claude/skills/testing-strategy/SKILL.md +479 -0
  25. package/presets/codex/AGENTS.md +883 -0
  26. package/presets/codex/README.md +102 -0
  27. package/presets/codex/skills/api-versioning/SKILL.md +389 -0
  28. package/presets/codex/skills/django-orm/SKILL.md +455 -0
  29. package/presets/codex/skills/django-testing/SKILL.md +409 -0
  30. package/presets/codex/skills/jpa-postgres/SKILL.md +618 -0
  31. package/presets/codex/skills/python/SKILL.md +606 -0
  32. package/presets/codex/skills/python-django-stack/SKILL.md +492 -0
  33. package/presets/codex/skills/python-fastapi-stack/SKILL.md +457 -0
  34. package/presets/codex/skills/spring-boot-feature/SKILL.md +558 -0
  35. package/presets/codex/skills/spring-boot-kotlin/SKILL.md +400 -0
  36. package/presets/codex/skills/sqlalchemy/SKILL.md +466 -0
  37. package/presets/codex/skills/testing-strategy/SKILL.md +479 -0
  38. package/presets/opencode/README.md +191 -0
  39. package/presets/opencode/agents/architect.md +133 -0
  40. package/presets/opencode/agents/docs.md +113 -0
  41. package/presets/opencode/agents/implementer.md +116 -0
  42. package/presets/opencode/agents/orchestrator.md +248 -0
  43. package/presets/opencode/agents/repo-explorer.md +119 -0
  44. package/presets/opencode/agents/reviewer.md +130 -0
  45. package/presets/opencode/agents/task-coach.md +103 -0
  46. package/presets/opencode/agents/tester.md +269 -0
  47. package/presets/opencode/commands/cc-feature.md +114 -0
  48. package/presets/opencode/commands/cc-fix.md +123 -0
  49. package/presets/opencode/commands/cc-refactor.md +148 -0
  50. package/presets/opencode/commands/cc-review.md +141 -0
  51. package/presets/opencode/commands/cc-tdd-cycle.md +225 -0
  52. package/presets/opencode/commands/cc-test-plan.md +144 -0
  53. package/presets/opencode/opencode.jsonc +89 -0
  54. package/presets/opencode/prompts/v0.1.0/architect.md +213 -0
  55. package/presets/opencode/prompts/v0.1.0/docs.md +181 -0
  56. package/presets/opencode/prompts/v0.1.0/implementer.md +154 -0
  57. package/presets/opencode/prompts/v0.1.0/orchestrator.md +169 -0
  58. package/presets/opencode/prompts/v0.1.0/repo-explorer.md +102 -0
  59. package/presets/opencode/prompts/v0.1.0/reviewer.md +183 -0
  60. package/presets/opencode/prompts/v0.1.0/task-coach.md +142 -0
  61. package/presets/opencode/prompts/v0.1.0/tester.md +160 -0
  62. package/presets/opencode/prompts/v0.2.0/architect.md +219 -0
  63. package/presets/opencode/prompts/v0.2.0/docs.md +187 -0
  64. package/presets/opencode/prompts/v0.2.0/implementer.md +160 -0
  65. package/presets/opencode/prompts/v0.2.0/orchestrator.md +238 -0
  66. package/presets/opencode/prompts/v0.2.0/repo-explorer.md +108 -0
  67. package/presets/opencode/prompts/v0.2.0/reviewer.md +190 -0
  68. package/presets/opencode/prompts/v0.2.0/task-coach.md +153 -0
  69. package/presets/opencode/prompts/v0.2.0/tester.md +249 -0
  70. package/presets/opencode/skills/api-versioning/SKILL.md +388 -0
  71. package/presets/opencode/skills/astro/SKILL.md +313 -0
  72. package/presets/opencode/skills/code-review/SKILL.md +202 -0
  73. package/presets/opencode/skills/django-orm/SKILL.md +455 -0
  74. package/presets/opencode/skills/django-testing/SKILL.md +408 -0
  75. package/presets/opencode/skills/django-uv/SKILL.md +400 -0
  76. package/presets/opencode/skills/jpa-postgres/SKILL.md +617 -0
  77. package/presets/opencode/skills/nextjs-typescript/SKILL.md +385 -0
  78. package/presets/opencode/skills/python/SKILL.md +605 -0
  79. package/presets/opencode/skills/python-django-stack/SKILL.md +491 -0
  80. package/presets/opencode/skills/python-fastapi-stack/SKILL.md +456 -0
  81. package/presets/opencode/skills/security/SKILL.md +376 -0
  82. package/presets/opencode/skills/spring-boot-feature/SKILL.md +557 -0
  83. package/presets/opencode/skills/spring-boot-kotlin/SKILL.md +399 -0
  84. package/presets/opencode/skills/spring-boot-testing-strategy/SKILL.md +470 -0
  85. package/presets/opencode/skills/sqlalchemy/SKILL.md +465 -0
  86. package/presets/opencode/skills/testing-tdd/SKILL.md +586 -0
  87. package/src/presets/council/council.yml +59 -0
  88. package/src/presets/manifests/claude.yml +23 -0
  89. package/src/presets/manifests/codex.yml +12 -0
  90. package/src/presets/manifests/opencode.yml +18 -0
  91. package/src/presets/models/claude.yml +37 -0
  92. package/src/presets/models/codex.yml +38 -0
  93. package/src/presets/models/opencode.yml +37 -0
@@ -0,0 +1,479 @@
1
+ ---
2
+ id: testing-strategy
3
+ version: 1.0.0
4
+ name: Testing Strategy
5
+ description:
6
+ Provides expert knowledge of the testing pyramid, test design principles,
7
+ MockK patterns, and integration testing conventions for Spring Boot + Kotlin
8
+ projects.
9
+
10
+ compatibility:
11
+ tools: [claude, codex, opencode]
12
+ stacks:
13
+ languages: [kotlin, java]
14
+ frameworks: [spring-boot, junit5, mockk, testcontainers, assertj]
15
+ databases: [postgresql, h2]
16
+
17
+ risk:
18
+ level: low
19
+ can_execute_shell: false
20
+ can_modify_files: true
21
+ requires_network: false
22
+
23
+ inputs:
24
+ - source_files
25
+ - test_files
26
+ - build.gradle.kts
27
+
28
+ outputs:
29
+ - unit test classes
30
+ - integration test classes
31
+ - test data factories
32
+ - contract test scaffolding
33
+
34
+ quality:
35
+ reviewed_by: codeconductor-core
36
+ version: 0.1.0
37
+ ---
38
+
39
+ # Testing Strategy
40
+
41
+ ## Testing Pyramid
42
+
43
+ ```text
44
+ /\
45
+ / \
46
+ / E2E\ 10% — full API, happy path + main error cases
47
+ /------\
48
+ / Integ \ 20% — components with real dependencies (DB, HTTP)
49
+ /----------\
50
+ / Unit \ 70% — isolated, mocked dependencies, fast
51
+ /______________\
52
+ ```
53
+
54
+ Unit tests are the foundation. They are fast, deterministic, and cheap to run.
55
+ Integration tests validate that components work together. E2E tests validate
56
+ that the system works end to end — keep them minimal.
57
+
58
+ If you find yourself writing more integration tests than unit tests, the code
59
+ under test has too many responsibilities bundled together.
60
+
61
+ ## Test Naming Convention
62
+
63
+ Format: `should [expected behavior] when [condition]`
64
+
65
+ ```kotlin
66
+ @Test
67
+ fun `should return user when found by id`() { ... }
68
+
69
+ @Test
70
+ fun `should return 404 when user does not exist`() { ... }
71
+
72
+ @Test
73
+ fun `should throw ConflictException when email already exists`() { ... }
74
+
75
+ @Test
76
+ fun `should not return deleted users in list`() { ... }
77
+ ```
78
+
79
+ Group related tests with `@Nested`:
80
+
81
+ ```kotlin
82
+ @ExtendWith(MockKExtension::class)
83
+ class UserServiceTest {
84
+
85
+ @Nested
86
+ inner class GetById {
87
+ @Test
88
+ fun `should return user when found`() { ... }
89
+
90
+ @Test
91
+ fun `should return NotFound when user does not exist`() { ... }
92
+ }
93
+
94
+ @Nested
95
+ inner class Create {
96
+ @Test
97
+ fun `should create and return user when email is unique`() { ... }
98
+
99
+ @Test
100
+ fun `should throw ConflictException when email already exists`() { ... }
101
+ }
102
+ }
103
+ ```
104
+
105
+ ## Unit Test Structure (AAA)
106
+
107
+ Every test follows Arrange → Act → Assert. Use blank lines to separate each
108
+ phase.
109
+
110
+ ```kotlin
111
+ @Test
112
+ fun `should return user when found by id`() {
113
+ // Arrange
114
+ val userId = UUID.randomUUID()
115
+ val user = User(id = userId, email = "user@example.com", name = "Test User")
116
+ every { userRepository.findById(userId) } returns Optional.of(user)
117
+
118
+ // Act
119
+ val result = userService.getById(userId)
120
+
121
+ // Assert
122
+ assertThat(result).isInstanceOf(UserResult.Found::class.java)
123
+ val found = result as UserResult.Found
124
+ assertThat(found.user.id).isEqualTo(userId)
125
+ verify(exactly = 1) { userRepository.findById(userId) }
126
+ }
127
+ ```
128
+
129
+ No inline comments between phases once the structure is clear. The blank lines
130
+ are enough.
131
+
132
+ ## What NOT to Test
133
+
134
+ **Framework wiring.** Spring handles dependency injection. Do not write tests
135
+ that verify `@Autowired` works.
136
+
137
+ **JPA mapping.** Do not test that `@Column(name = "email")` maps to the right
138
+ column. That is Hibernate's job.
139
+
140
+ **Trivial getters and setters.** A data class property has no logic. There is
141
+ nothing to test.
142
+
143
+ **Private methods directly.** Private methods are implementation details. Test
144
+ them through the public behavior that uses them. If a private method is complex
145
+ enough to need its own test, it should be extracted into a separate class.
146
+
147
+ **Implementation, not behavior.** Tests that verify HOW something is done (mock
148
+ call order, internal state) are brittle. Test WHAT the output or side effect is.
149
+
150
+ ```kotlin
151
+ // bad — tests implementation detail
152
+ verify { userRepository.findById(any()) }
153
+ verify { cacheService.put(any(), any()) }
154
+ verify(ordering = Ordering.ORDERED) { // this is too coupled to internals
155
+ userRepository.findById(userId)
156
+ cacheService.put(userId, user)
157
+ }
158
+
159
+ // good — tests observable behavior
160
+ assertThat(result).isEqualTo(expectedUser)
161
+ ```
162
+
163
+ ## MockK Patterns
164
+
165
+ ### Basic Setup
166
+
167
+ ```kotlin
168
+ @ExtendWith(MockKExtension::class)
169
+ class UserServiceTest {
170
+
171
+ @MockK
172
+ private lateinit var userRepository: UserRepository
173
+
174
+ @MockK
175
+ private lateinit var emailService: EmailService
176
+
177
+ private lateinit var userService: UserService
178
+
179
+ @BeforeEach
180
+ fun setUp() {
181
+ userService = UserService(userRepository, emailService)
182
+ }
183
+ }
184
+ ```
185
+
186
+ ### Stubbing
187
+
188
+ ```kotlin
189
+ // Return value
190
+ every { userRepository.findById(userId) } returns Optional.of(user)
191
+
192
+ // Return null (for nullable return types)
193
+ every { userRepository.findByEmail(any()) } returns null
194
+
195
+ // Throw exception
196
+ every { userRepository.save(any()) } throws DataIntegrityViolationException("Duplicate")
197
+
198
+ // Return different values on successive calls
199
+ every { userRepository.findById(any()) } returnsMany listOf(Optional.of(user), Optional.empty())
200
+
201
+ // Answer with computation
202
+ every { userRepository.save(any()) } answers { firstArg() }
203
+ ```
204
+
205
+ ### Verification
206
+
207
+ ```kotlin
208
+ // Verify called exactly once with specific argument
209
+ verify(exactly = 1) { userRepository.findById(userId) }
210
+
211
+ // Verify called with any argument
212
+ verify { emailService.sendWelcome(any()) }
213
+
214
+ // Verify never called
215
+ verify(exactly = 0) { emailService.sendWelcome(any()) }
216
+
217
+ // Verify called with specific argument
218
+ verify { emailService.sendWelcome(match { it.email == "user@example.com" }) }
219
+ ```
220
+
221
+ ### Relaxed Mocks
222
+
223
+ ```kotlin
224
+ // relaxed = true — returns default values (null, 0, false, empty list)
225
+ // Use only when you do not care about the return value of any method
226
+ val logger = mockk<Logger>(relaxed = true)
227
+ ```
228
+
229
+ Do not use `relaxed = true` on mocks where you need to verify behavior — a
230
+ relaxed mock does not fail on unexpected calls, which can hide bugs.
231
+
232
+ ### Coroutines
233
+
234
+ ```kotlin
235
+ // Stubbing suspend functions
236
+ coEvery { userRepository.findById(userId) } returns user
237
+
238
+ // Verifying suspend functions
239
+ coVerify(exactly = 1) { userRepository.findById(userId) }
240
+ ```
241
+
242
+ ### Argument Matchers
243
+
244
+ ```kotlin
245
+ // Any value
246
+ every { service.process(any()) } returns result
247
+
248
+ // Specific type
249
+ every { service.process(any<UserRequest>()) } returns result
250
+
251
+ // Custom predicate
252
+ every { service.notify(match { it.email.endsWith("@example.com") }) } just Runs
253
+
254
+ // Capture for assertion
255
+ val slot = slot<User>()
256
+ every { userRepository.save(capture(slot)) } returns savedUser
257
+ // ... trigger the action ...
258
+ assertThat(slot.captured.email).isEqualTo("expected@example.com")
259
+ ```
260
+
261
+ ## Integration Test Patterns
262
+
263
+ ### Controller Tests with MockMvc
264
+
265
+ ```kotlin
266
+ @SpringBootTest
267
+ @AutoConfigureMockMvc
268
+ class UserControllerTest {
269
+
270
+ @Autowired
271
+ private lateinit var mockMvc: MockMvc
272
+
273
+ @Autowired
274
+ private lateinit var objectMapper: ObjectMapper
275
+
276
+ @Test
277
+ fun `should return 200 with user when found`() {
278
+ mockMvc.perform(
279
+ get("/api/v1/users/${existingUser.id}")
280
+ .contentType(MediaType.APPLICATION_JSON)
281
+ )
282
+ .andExpect(status().isOk)
283
+ .andExpect(jsonPath("$.email").value(existingUser.email))
284
+ .andExpect(jsonPath("$.id").value(existingUser.id.toString()))
285
+ }
286
+
287
+ @Test
288
+ fun `should return 400 when request body is invalid`() {
289
+ val invalidRequest = mapOf("email" to "not-an-email")
290
+
291
+ mockMvc.perform(
292
+ post("/api/v1/users")
293
+ .contentType(MediaType.APPLICATION_JSON)
294
+ .content(objectMapper.writeValueAsString(invalidRequest))
295
+ )
296
+ .andExpect(status().isBadRequest)
297
+ .andExpect(jsonPath("$.code").value("VALIDATION_ERROR"))
298
+ }
299
+
300
+ @Test
301
+ fun `should return 404 when user is not found`() {
302
+ mockMvc.perform(get("/api/v1/users/${UUID.randomUUID()}"))
303
+ .andExpect(status().isNotFound)
304
+ }
305
+ }
306
+ ```
307
+
308
+ `@AutoConfigureMockMvc` configures MockMvc without starting a real HTTP server.
309
+ The test is faster than `RANDOM_PORT` but still tests the full Spring MVC stack.
310
+
311
+ Use `RANDOM_PORT` only when you need real HTTP (e.g., testing HTTP client
312
+ behavior, WebSockets, or SSE).
313
+
314
+ ### Database Tests with Testcontainers
315
+
316
+ ```kotlin
317
+ @SpringBootTest
318
+ @Testcontainers
319
+ class UserRepositoryIntegrationTest {
320
+
321
+ companion object {
322
+ @Container
323
+ @JvmStatic
324
+ val postgres = PostgreSQLContainer<Nothing>("postgres:16").apply {
325
+ withDatabaseName("testdb")
326
+ withUsername("test")
327
+ withPassword("test")
328
+ }
329
+
330
+ @DynamicPropertySource
331
+ @JvmStatic
332
+ fun overrideProperties(registry: DynamicPropertyRegistry) {
333
+ registry.add("spring.datasource.url", postgres::getJdbcUrl)
334
+ registry.add("spring.datasource.username", postgres::getUsername)
335
+ registry.add("spring.datasource.password", postgres::getPassword)
336
+ }
337
+ }
338
+
339
+ @Autowired
340
+ private lateinit var userRepository: UserRepository
341
+
342
+ @Test
343
+ fun `should enforce unique email at database level`() {
344
+ userRepository.save(User(email = "unique@example.com", name = "First"))
345
+ assertThrows<DataIntegrityViolationException> {
346
+ userRepository.save(User(email = "unique@example.com", name = "Second"))
347
+ }
348
+ }
349
+ }
350
+ ```
351
+
352
+ Share the container across test classes using a base class with `@Container` on
353
+ a static field. Testcontainers reuses the same container if the configuration is
354
+ identical.
355
+
356
+ ### Seed Data with @Sql
357
+
358
+ ```kotlin
359
+ @SpringBootTest
360
+ @AutoConfigureMockMvc
361
+ @Sql("/test-data/users.sql") // runs before each test in this class
362
+ class UserControllerTest { ... }
363
+
364
+ // Or per-test
365
+ @Test
366
+ @Sql("/test-data/users-with-orders.sql")
367
+ fun `should return orders when user has orders`() { ... }
368
+ ```
369
+
370
+ SQL files go in `src/test/resources/test-data/`. Use descriptive names that
371
+ explain what state they create, not what feature they serve.
372
+
373
+ ## Test Data Factories
374
+
375
+ Never hardcode UUIDs or share mutable state between tests.
376
+
377
+ ```kotlin
378
+ object UserTestFactory {
379
+
380
+ fun create(
381
+ id: UUID = UUID.randomUUID(),
382
+ email: String = "user-${UUID.randomUUID()}@example.com",
383
+ name: String = "Test User",
384
+ role: UserRole = UserRole.USER
385
+ ): User = User(
386
+ id = id,
387
+ email = email,
388
+ name = name,
389
+ role = role
390
+ )
391
+
392
+ fun createRequest(
393
+ email: String = "user-${UUID.randomUUID()}@example.com",
394
+ name: String = "Test User"
395
+ ): CreateUserRequest = CreateUserRequest(
396
+ email = email,
397
+ name = name
398
+ )
399
+
400
+ fun createAdmin(): User = create(role = UserRole.ADMIN)
401
+
402
+ fun createList(count: Int): List<User> = (1..count).map { create() }
403
+ }
404
+ ```
405
+
406
+ Usage in tests:
407
+
408
+ ```kotlin
409
+ val user = UserTestFactory.create(email = "specific@example.com")
410
+ val admin = UserTestFactory.createAdmin()
411
+ val users = UserTestFactory.createList(5)
412
+ ```
413
+
414
+ Default values use `UUID.randomUUID()` to ensure tests do not share state
415
+ through hardcoded IDs or emails.
416
+
417
+ ## When Tests Are Required
418
+
419
+ **New behavior.** Every new feature requires at minimum one unit test and one
420
+ integration test covering the happy path. Edge cases and error paths require
421
+ additional unit tests.
422
+
423
+ **Bug fix.** Write a regression test that reproduces the bug before fixing it.
424
+ The test must fail before the fix and pass after. This prevents the bug from
425
+ reappearing silently.
426
+
427
+ ```kotlin
428
+ // Example: bug where findByEmail returned deleted users
429
+ @Test
430
+ fun `should not return deleted user — regression for bug #42`() {
431
+ val user = userRepository.save(UserTestFactory.create(email = "gone@example.com"))
432
+ userRepository.delete(user)
433
+ val result = userRepository.findByEmail("gone@example.com")
434
+ assertThat(result).isNull()
435
+ }
436
+ ```
437
+
438
+ **Refactor.** Do not write new tests. The existing tests must pass unchanged. If
439
+ they do not, the refactor changed behavior — that is a bug.
440
+
441
+ **High-risk paths.** Security, payment processing, data migrations, and
442
+ permission checks require contract tests in addition to unit and integration
443
+ tests. These paths have severe consequences if broken.
444
+
445
+ ## CI Integration
446
+
447
+ Tests must run in CI on every pull request. The pipeline must fail if any test
448
+ fails.
449
+
450
+ Recommended test execution order:
451
+
452
+ 1. Unit tests (fastest — fail fast)
453
+ 2. Integration tests with H2 (`@DataJpaTest`)
454
+ 3. Integration tests with Testcontainers (slowest — run last)
455
+
456
+ Separate test tasks in Gradle:
457
+
458
+ ```kotlin
459
+ // build.gradle.kts
460
+ tasks.register<Test>("integrationTest") {
461
+ description = "Runs integration tests"
462
+ group = "verification"
463
+ useJUnitPlatform {
464
+ includeTags("integration")
465
+ }
466
+ shouldRunAfter(tasks.test)
467
+ }
468
+ ```
469
+
470
+ Tag integration tests:
471
+
472
+ ```kotlin
473
+ @Tag("integration")
474
+ @SpringBootTest
475
+ class UserRepositoryIntegrationTest { ... }
476
+ ```
477
+
478
+ Do not mix unit and integration test tags in the same class. A test class is
479
+ either one or the other.
@@ -0,0 +1,191 @@
1
+ # OpenCode Preset for CodeConductor
2
+
3
+ ## Model Selection Guide
4
+
5
+ This preset supports both **Claude (Anthropic)** and **OpenCode Go (Chinese
6
+ providers)** models.
7
+
8
+ ---
9
+
10
+ ## Model Reference
11
+
12
+ ### Claude Models (Anthropic)
13
+
14
+ | Model | Strength | Best For |
15
+ | --------------------------- | ------------------------------- | ------------------------------- |
16
+ | `claude-opus-4-7` | Complex reasoning, architecture | Architect, complex design |
17
+ | `claude-sonnet-4-6` | Balanced, general purpose | Default for most agents |
18
+ | `claude-haiku-4-5-20251001` | Fast, lightweight | Task Coach, Docs, Repo Explorer |
19
+
20
+ ### OpenCode Go Models (Chinese Providers)
21
+
22
+ | Model | Provider | Strength | Best For |
23
+ | ----------------- | --------------- | -------------------------------- | -------------------------- |
24
+ | `deepseek-v4-pro` | DeepSeek | Reasoning complejo, arquitectura | Architect, orchestrator |
25
+ | `mimo-v2.5-pro` | MiniMax | Razonamiento matemático, código | Implementer |
26
+ | `minimax-m2.7` | MiniMax | Tareas equilibradas | Tester, orchestrator |
27
+ | `qwen-3.6-plus` | Qwen | Rápido, eficiente | Reviewer, Task Coach, Docs |
28
+ | `kimi-k2-6` | Kimi (Moonshot) | Conversación natural | Task Coach, Docs |
29
+
30
+ ---
31
+
32
+ ## Agent Model Matrix
33
+
34
+ | Agent | Claude (Default) | OpenCode Go (Recommended) | Alternative |
35
+ | ----------------- | --------------------------- | ------------------------- | ----------------- |
36
+ | **Orchestrator** | `claude-sonnet-4-6` | `deepseek-v4-pro` | `minimax-m2.7` |
37
+ | **Architect** | `claude-opus-4-7` | `deepseek-v4-pro` | `mimo-v2.5-pro` |
38
+ | **Implementer** | `claude-sonnet-4-6` | `mimo-v2.5-pro` | `minimax-m2.7` |
39
+ | **Tester** | `claude-sonnet-4-6` | `minimax-m2.7` | `deepseek-v4-pro` |
40
+ | **Reviewer** | `claude-sonnet-4-6` | `qwen-3.6-plus` | `minimax-m2.7` |
41
+ | **Task Coach** | `claude-haiku-4-5-20251001` | `qwen-3.6-plus` | `kimi-k2.6` |
42
+ | **Docs** | `claude-haiku-4-5-20251001` | `qwen-3.6-plus` | `kimi-k2.6` |
43
+ | **Repo Explorer** | `claude-haiku-4-5-20251001` | `qwen-3.6-plus` | `kimi-k2.6` |
44
+
45
+ ---
46
+
47
+ ## Agent Modes
48
+
49
+ | Agent | Mode | Description |
50
+ | ----------------- | --------- | ---------------------------------------------------- |
51
+ | **Orchestrator** | primary | Main coordinator — Tab to switch to it |
52
+ | **Architect** | subagent | Invoked by Orchestrator for design work |
53
+ | **Implementer** | subagent | Invoked by Orchestrator for code implementation |
54
+ | **Tester** | subagent | Invoked by Orchestrator for test generation |
55
+ | **Reviewer** | subagent | Invoked by Orchestrator for code review |
56
+ | **Task Coach** | subagent | Invoked by Orchestrator for intake clarification |
57
+ | **Docs** | subagent | Invoked by Orchestrator for documentation updates |
58
+ | **Repo Explorer** | subagent | Invoked by Orchestrator for codebase exploration |
59
+
60
+ ---
61
+
62
+ ## Permission System
63
+
64
+ This preset uses OpenCode's permission system (v1.1.1+) with granular control:
65
+
66
+ - **Global defaults**: Most operations require approval (`ask`)
67
+ - **Read access**: Allowed by default, with sensitive files denied
68
+ - **Write/Edit**: Requires approval, with protected paths denied
69
+ - **Bash commands**: Read-only git commands allowed, destructive commands denied
70
+ - **Agent-specific**: Each agent has tailored permissions matching its role
71
+
72
+ ### Protected Paths
73
+
74
+ The following paths are denied by default:
75
+
76
+ - `.env`, `.env.*` — environment secrets
77
+ - `secrets/**` — secret files
78
+ - `~/.ssh/**`, `~/.aws/**`, `~/.kube/**` — system credentials
79
+ - `.git/**`, `.opencode/**`, `.claude/**` — tool configuration
80
+
81
+ ---
82
+
83
+ ## Model Selection by Task Complexity
84
+
85
+ ### Simple Tasks (Q&A, intake, documentation)
86
+
87
+ **Recommended:** `qwen-3.6-plus` (OpenCode Go) or `claude-haiku-4-5-20251001`
88
+ (Claude)
89
+
90
+ - Task Coach intake
91
+ - Repo Explorer mapping
92
+ - Docs updates
93
+
94
+ ### Medium Tasks (Implementation, testing)
95
+
96
+ **Recommended:** `mimo-v2.5-pro` or `minimax-m2.7` (OpenCode Go) or
97
+ `claude-sonnet-4-6` (Claude)
98
+
99
+ - Implementer code writing
100
+ - Tester test generation
101
+ - Reviewer standard reviews
102
+
103
+ ### Complex Tasks (Architecture, security, multi-agent coordination)
104
+
105
+ **Recommended:** `deepseek-v4-pro` (OpenCode Go) or `claude-opus-4-7` (Claude)
106
+
107
+ - Architect technical design
108
+ - Orchestrator routing decisions
109
+ - Reviewer security analysis
110
+
111
+ ---
112
+
113
+ ## Usage in Agent Contracts
114
+
115
+ Each agent file in `agents/` contains a model selection table in its
116
+ frontmatter:
117
+
118
+ ```yaml
119
+ ---
120
+ description: ...
121
+ # Model Selection
122
+ | Provider | Model | Use Case |
123
+ |----------|-------|----------|
124
+ | Claude | claude-opus-4-7 | Complex architecture, ADRs |
125
+ | OpenCode Go | deepseek-v4-pro | Best — excels at reasoning |
126
+ ---
127
+ ```
128
+
129
+ To override, edit the `model` field in the agent's YAML frontmatter or use the
130
+ configuration in `opencode.jsonc`.
131
+
132
+ ---
133
+
134
+ ## Configuration Priority
135
+
136
+ 1. **Agent frontmatter** — highest priority (per-agent override)
137
+ 2. **opencode.jsonc model override** — applies to specific agents
138
+ 3. **opencode.jsonc default** — fallback for all agents
139
+
140
+ ---
141
+
142
+ ## Environment Variables
143
+
144
+ OpenCode Go requires appropriate API keys. Set these in your environment:
145
+
146
+ ```bash
147
+ # DeepSeek
148
+ export DEEPSEEK_API_KEY="your-key"
149
+
150
+ # Qwen (Alibaba)
151
+ export DASHSCOPE_API_KEY="your-key"
152
+
153
+ # MiniMax
154
+ export MINIMAX_API_KEY="your-key"
155
+
156
+ # Kimi (Moonshot)
157
+ export KIMI_API_KEY="your-key"
158
+ ```
159
+
160
+ ---
161
+
162
+ ## Selecting Between Claude and OpenCode Go
163
+
164
+ | Scenario | Recommended |
165
+ | ------------------------------- | -------------------------------------------------- |
166
+ | Complex reasoning, architecture | OpenCode Go (`deepseek-v4-pro`) or Claude (`opus`) |
167
+ | Fast iteration, simple tasks | OpenCode Go (`qwen-3.6-plus`) or Claude (`haiku`) |
168
+ | Code implementation | OpenCode Go (`mimo-v2.5-pro`) or Claude (`sonnet`) |
169
+ | Budget constraints | OpenCode Go (generally lower cost) |
170
+ | Availability issues | Switch to alternative from the matrix |
171
+
172
+ ## Approach
173
+
174
+ - Think before acting. Read existing files before writing code.
175
+ - Be concise in output but thorough in reasoning.
176
+ - Prefer editing over rewriting whole files.
177
+ - Do not re-read files you have already read unless the file may have changed.
178
+ - Skip files over 100KB unless explicitly required.
179
+ - Suggest running /cost when a session is running long to monitor cache ratio.
180
+ - Recommend starting a new session when switching to an unrelated task.
181
+ - Test your code before declaring done.
182
+ - No sycophantic openers or closing fluff.
183
+ - Keep solutions simple and direct.
184
+ - User instructions always override this file.
185
+ - When using tools, be precise and minimal with context.
186
+
187
+ ## Context Budget
188
+
189
+ - If the task type differs from the previous one, execute "/clear" before
190
+ starting.
191
+ - Delegate verbose operations to sub-agents.