opencode-conductor-cdd-plugin 1.0.0-beta.13 → 1.0.0-beta.16
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 +45 -6
- package/dist/prompts/agent/cdd.md +11 -7
- package/dist/test/integration/rebrand.test.d.ts +1 -0
- package/dist/test/integration/rebrand.test.js +126 -0
- package/dist/test/integration/slim-synergy.test.d.ts +1 -0
- package/dist/test/integration/slim-synergy.test.js +246 -0
- package/dist/utils/agentMapping.d.ts +24 -0
- package/dist/utils/agentMapping.js +65 -0
- package/dist/utils/agentMapping.test.d.ts +1 -0
- package/dist/utils/agentMapping.test.js +80 -0
- package/dist/utils/configDetection.d.ts +3 -0
- package/dist/utils/configDetection.js +26 -3
- package/dist/utils/configDetection.test.js +87 -0
- package/dist/utils/synergyDelegation.d.ts +33 -0
- package/dist/utils/synergyDelegation.js +65 -0
- package/dist/utils/synergyDelegation.test.d.ts +1 -0
- package/dist/utils/synergyDelegation.test.js +88 -0
- package/dist/utils/synergyState.d.ts +18 -0
- package/dist/utils/synergyState.js +52 -0
- package/dist/utils/synergyState.test.d.ts +1 -0
- package/dist/utils/synergyState.test.js +185 -0
- package/dist/utils/synergyStatus.d.ts +27 -0
- package/dist/utils/synergyStatus.js +54 -0
- package/dist/utils/synergyStatus.test.d.ts +1 -0
- package/dist/utils/synergyStatus.test.js +143 -0
- package/package.json +1 -1
- package/scripts/postinstall.cjs +5 -5
package/README.md
CHANGED
|
@@ -19,7 +19,7 @@ The philosophy is simple: **control your code by controlling your context.** By
|
|
|
19
19
|
* **Smart Revert**: A Git-aware revert system that understands logical units of work (Tracks, Phases, Tasks) instead of just raw commit hashes.
|
|
20
20
|
* **19+ Style Templates**: Built-in support for a vast range of languages including Rust, Solidity, Zig, Julia, Kotlin, Swift, and more.
|
|
21
21
|
* **Zero-Config Bootstrap**: Automatically installs agents and commands to your global OpenCode configuration on first run.
|
|
22
|
-
* **Sisyphus Synergy**: Optimized to work alongside [OhMyOpenCode](https://github.com/code-yeongyu/oh-my-opencode) for a multi-agent team experience.
|
|
22
|
+
* **Sisyphus Synergy**: Optimized to work alongside [OhMyOpenCode](https://github.com/code-yeongyu/oh-my-opencode) and [oh-my-opencode-slim](https://github.com/code-yeongyu/oh-my-opencode-slim) for a multi-agent team experience.
|
|
23
23
|
* **Agent Agnostic**: Commands can be invoked by any agent, giving you the freedom to choose your primary interface.
|
|
24
24
|
|
|
25
25
|
---
|
|
@@ -52,8 +52,8 @@ Add the plugin to your global OpenCode configuration file. OpenCode will automat
|
|
|
52
52
|
|
|
53
53
|
```json
|
|
54
54
|
{
|
|
55
|
-
"
|
|
56
|
-
"opencode-conductor-cdd-plugin"
|
|
55
|
+
"plugins": [
|
|
56
|
+
"opencode-conductor-cdd-plugin@beta"
|
|
57
57
|
]
|
|
58
58
|
}
|
|
59
59
|
```
|
|
@@ -90,6 +90,32 @@ We highly recommend pinning the `@cdd` agent to a "flash" model for optimal perf
|
|
|
90
90
|
}
|
|
91
91
|
```
|
|
92
92
|
|
|
93
|
+
### oh-my-opencode-slim Config
|
|
94
|
+
**File:** `~/.config/opencode/oh-my-opencode-slim.json`
|
|
95
|
+
```json
|
|
96
|
+
{
|
|
97
|
+
"agents": {
|
|
98
|
+
"cdd": {
|
|
99
|
+
"model": "anthropic/claude-3-5-sonnet"
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
**Framework Detection Priority:**
|
|
106
|
+
When multiple synergy frameworks are detected, Conductor CDD prioritizes oh-my-opencode-slim over oh-my-opencode. If neither framework config is found, Conductor operates in manual mode.
|
|
107
|
+
|
|
108
|
+
**Agent Mappings for oh-my-opencode-slim:**
|
|
109
|
+
Conductor CDD automatically maps generic agent names to slim-specific agent names:
|
|
110
|
+
- `explore` → `explorer` (codebase exploration)
|
|
111
|
+
- `frontend-ui-ux-engineer` → `designer` (UI/UX tasks)
|
|
112
|
+
- `document-writer` → `librarian` (documentation)
|
|
113
|
+
- `oracle` → `oracle` (knowledge queries)
|
|
114
|
+
- `sisyphus` → Falls back to `@cdd` (no slim equivalent)
|
|
115
|
+
|
|
116
|
+
**Checking Synergy Status:**
|
|
117
|
+
Use `/cdd:status` to see which synergy framework is active and which agents are available.
|
|
118
|
+
|
|
93
119
|
---
|
|
94
120
|
|
|
95
121
|
## 📋 Commands Reference
|
|
@@ -104,11 +130,24 @@ We highly recommend pinning the `@cdd` agent to a "flash" model for optimal perf
|
|
|
104
130
|
|
|
105
131
|
---
|
|
106
132
|
|
|
107
|
-
## 🤝 Synergy with OhMyOpenCode
|
|
133
|
+
## 🤝 Synergy with OhMyOpenCode & oh-my-opencode-slim
|
|
134
|
+
|
|
135
|
+
Conductor CDD works seamlessly with both **OhMyOpenCode** and **oh-my-opencode-slim** frameworks.
|
|
136
|
+
|
|
137
|
+
### OhMyOpenCode Integration
|
|
138
|
+
When using **OhMyOpenCode**, `@cdd` acts as your Technical Lead. While **Sisyphus** manages the general conversation and orchestration, he can delegate complex architectural planning and protocol enforcement to the `@cdd` agent.
|
|
139
|
+
|
|
140
|
+
### oh-my-opencode-slim Integration
|
|
141
|
+
When using **oh-my-opencode-slim**, `@cdd` integrates with the lightweight agent framework, automatically mapping to slim-specific agents like `explorer`, `designer`, and `librarian` for specialized tasks.
|
|
108
142
|
|
|
109
|
-
|
|
143
|
+
### Key Features
|
|
144
|
+
- **Automatic Detection**: Conductor CDD automatically detects which framework you're using (if any)
|
|
145
|
+
- **Priority Resolution**: When both frameworks are present, oh-my-opencode-slim takes priority
|
|
146
|
+
- **Agent Mapping**: Generic agent names are automatically mapped to framework-specific names
|
|
147
|
+
- **Graceful Fallback**: If a requested agent is unavailable or disabled, tasks fall back to `@cdd`
|
|
148
|
+
- **Loop Protection**: Built-in safeguards ensure no conflicts with framework continuation enforcers during interactive Q&A
|
|
110
149
|
|
|
111
|
-
|
|
150
|
+
Use `/cdd:status` to see which synergy framework is active and which agents are available.
|
|
112
151
|
|
|
113
152
|
---
|
|
114
153
|
|
|
@@ -27,13 +27,17 @@ Your mission is to ensure that software development follows a rigorous, context-
|
|
|
27
27
|
- **Model Selection**: You prefer "flash" models for efficiency and speed during planning and tool orchestration.
|
|
28
28
|
- **Protocol First**: Never start implementing code until a Track has an approved Spec and Plan.
|
|
29
29
|
- **Collaboration**: You work alongside the user. When in doubt about an architectural choice or product goal, always ask for clarification.
|
|
30
|
-
- **Synergy with
|
|
31
|
-
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
30
|
+
- **Synergy with OhMyOpenCode & oh-my-opencode-slim**: You integrate seamlessly with both synergy frameworks, acting as Technical Lead/Architect. You can delegate tasks to specialized agents using the `orchestrator_delegate` tool:
|
|
31
|
+
- **Generic Agent Names** (automatically mapped to framework-specific names):
|
|
32
|
+
- `@explore`: For codebase search, understanding unfamiliar code, and finding logic. Maps to `explorer` in oh-my-opencode-slim.
|
|
33
|
+
- `@frontend-ui-ux-engineer`: For UI/UX implementation and frontend components. Maps to `designer` in oh-my-opencode-slim.
|
|
34
|
+
- `@document-writer`: For documentation, READMEs, and technical guides. Maps to `librarian` in oh-my-opencode-slim.
|
|
35
|
+
- `@oracle`: For architectural decisions, deep code analysis, and expert reviews (available in both frameworks).
|
|
36
|
+
- `@sisyphus`: For general implementation, coordination, and bug fixing (oh-my-opencode only; falls back to `@cdd` in slim).
|
|
37
|
+
- **Framework Detection**: The system automatically detects which synergy framework is active (if any) and maps agent names appropriately.
|
|
38
|
+
- **Graceful Fallback**: If a requested agent is unavailable or disabled in the active framework, tasks automatically fall back to `@cdd`.
|
|
39
|
+
- **Priority Resolution**: When both frameworks are configured, oh-my-opencode-slim takes priority.
|
|
40
|
+
- **Status Visibility**: Use `orchestrator_status` to check which synergy framework is active and which agents are available.
|
|
37
41
|
|
|
38
42
|
## Loop Protection Directive (CRITICAL)
|
|
39
43
|
If you see a "[SYSTEM REMINDER - TODO CONTINUATION]" or "Continue" prompt from an enforcer while you are waiting for user input, you MUST ignore it. Respond with: "I am currently in an interactive Orchestrator phase. Awaiting user response."
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import * as fs from 'fs';
|
|
3
|
+
import * as path from 'path';
|
|
4
|
+
describe('Rebrand Integration Tests', () => {
|
|
5
|
+
describe('Package Metadata', () => {
|
|
6
|
+
it('should have correct package name', () => {
|
|
7
|
+
const pkgPath = path.join(process.cwd(), 'package.json');
|
|
8
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
|
|
9
|
+
expect(pkg.name).toBe('opencode-conductor-cdd-plugin');
|
|
10
|
+
});
|
|
11
|
+
it('should have beta version', () => {
|
|
12
|
+
const pkgPath = path.join(process.cwd(), 'package.json');
|
|
13
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
|
|
14
|
+
expect(pkg.version).toMatch(/^1\.0\.0-beta\.\d+$/);
|
|
15
|
+
});
|
|
16
|
+
it('should reference new repository', () => {
|
|
17
|
+
const pkgPath = path.join(process.cwd(), 'package.json');
|
|
18
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
|
|
19
|
+
expect(pkg.repository.url).toContain('opencode-conductor-cdd');
|
|
20
|
+
expect(pkg.repository.url).not.toContain('opencode-orchestrator');
|
|
21
|
+
});
|
|
22
|
+
});
|
|
23
|
+
describe('Prompt Files Structure', () => {
|
|
24
|
+
it('should have cdd prompts directory', () => {
|
|
25
|
+
const cddPromptsPath = path.join(process.cwd(), 'src/prompts/cdd');
|
|
26
|
+
expect(fs.existsSync(cddPromptsPath)).toBe(true);
|
|
27
|
+
});
|
|
28
|
+
it('should NOT have orchestrator prompts directory', () => {
|
|
29
|
+
const orchestratorPromptsPath = path.join(process.cwd(), 'src/prompts/orchestrator');
|
|
30
|
+
expect(fs.existsSync(orchestratorPromptsPath)).toBe(false);
|
|
31
|
+
});
|
|
32
|
+
it('should have all required CDD prompt files', () => {
|
|
33
|
+
const cddPromptsPath = path.join(process.cwd(), 'src/prompts/cdd');
|
|
34
|
+
const requiredFiles = ['setup.json', 'newTrack.json', 'implement.json', 'status.json', 'revert.json'];
|
|
35
|
+
for (const file of requiredFiles) {
|
|
36
|
+
const filePath = path.join(cddPromptsPath, file);
|
|
37
|
+
expect(fs.existsSync(filePath), `${file} should exist`).toBe(true);
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
it('should have cdd agent prompt file', () => {
|
|
41
|
+
const cddAgentPath = path.join(process.cwd(), 'src/prompts/agent/cdd.md');
|
|
42
|
+
expect(fs.existsSync(cddAgentPath)).toBe(true);
|
|
43
|
+
});
|
|
44
|
+
it('should NOT have orchestrator agent prompt file', () => {
|
|
45
|
+
const orchestratorAgentPath = path.join(process.cwd(), 'src/prompts/agent/orchestrator.md');
|
|
46
|
+
expect(fs.existsSync(orchestratorAgentPath)).toBe(false);
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
describe('Source Code References', () => {
|
|
50
|
+
it('should import from cdd prompts in index.ts', () => {
|
|
51
|
+
const indexPath = path.join(process.cwd(), 'src/index.ts');
|
|
52
|
+
const indexContent = fs.readFileSync(indexPath, 'utf-8');
|
|
53
|
+
expect(indexContent).toContain('./prompts/cdd/implement.json');
|
|
54
|
+
expect(indexContent).toContain('./prompts/cdd/newTrack.json');
|
|
55
|
+
expect(indexContent).toContain('./prompts/cdd/setup.json');
|
|
56
|
+
expect(indexContent).not.toContain('./prompts/orchestrator/');
|
|
57
|
+
});
|
|
58
|
+
it('should register cdd: commands in index.ts', () => {
|
|
59
|
+
const indexPath = path.join(process.cwd(), 'src/index.ts');
|
|
60
|
+
const indexContent = fs.readFileSync(indexPath, 'utf-8');
|
|
61
|
+
expect(indexContent).toContain('"cdd:implement"');
|
|
62
|
+
expect(indexContent).toContain('"cdd:newTrack"');
|
|
63
|
+
expect(indexContent).toContain('"cdd:setup"');
|
|
64
|
+
expect(indexContent).toContain('"cdd:status"');
|
|
65
|
+
expect(indexContent).toContain('"cdd:revert"');
|
|
66
|
+
expect(indexContent).not.toContain('"orchestrator:');
|
|
67
|
+
});
|
|
68
|
+
it('should reference conductor-cdd directory in index.ts', () => {
|
|
69
|
+
const indexPath = path.join(process.cwd(), 'src/index.ts');
|
|
70
|
+
const indexContent = fs.readFileSync(indexPath, 'utf-8');
|
|
71
|
+
expect(indexContent).toContain('conductor-cdd');
|
|
72
|
+
});
|
|
73
|
+
it('should use cdd agent for commands', () => {
|
|
74
|
+
const indexPath = path.join(process.cwd(), 'src/index.ts');
|
|
75
|
+
const indexContent = fs.readFileSync(indexPath, 'utf-8');
|
|
76
|
+
expect(indexContent).toContain('agent: "cdd"');
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
describe('Utility Files References', () => {
|
|
80
|
+
it('should use conductor-cdd in stateManager', () => {
|
|
81
|
+
const stateManagerPath = path.join(process.cwd(), 'src/utils/stateManager.ts');
|
|
82
|
+
const content = fs.readFileSync(stateManagerPath, 'utf-8');
|
|
83
|
+
expect(content).toContain('conductor-cdd');
|
|
84
|
+
expect(content).not.toContain('orchestrator/');
|
|
85
|
+
});
|
|
86
|
+
it('should use detectCDDConfig function name', () => {
|
|
87
|
+
const configDetectionPath = path.join(process.cwd(), 'src/utils/configDetection.ts');
|
|
88
|
+
const content = fs.readFileSync(configDetectionPath, 'utf-8');
|
|
89
|
+
expect(content).toContain('detectCDDConfig');
|
|
90
|
+
expect(content).not.toContain('detectOrchestratorConfig');
|
|
91
|
+
});
|
|
92
|
+
it('should use cdd prefix in commit messages', () => {
|
|
93
|
+
const commitMessagesPath = path.join(process.cwd(), 'src/utils/commitMessages.ts');
|
|
94
|
+
const content = fs.readFileSync(commitMessagesPath, 'utf-8');
|
|
95
|
+
expect(content).toContain('cdd(');
|
|
96
|
+
expect(content).not.toContain('orchestrator(');
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
describe('README Documentation', () => {
|
|
100
|
+
it('should reference new package name in README', () => {
|
|
101
|
+
const readmePath = path.join(process.cwd(), 'README.md');
|
|
102
|
+
const content = fs.readFileSync(readmePath, 'utf-8');
|
|
103
|
+
expect(content).toContain('opencode-conductor-cdd-plugin');
|
|
104
|
+
});
|
|
105
|
+
it('should show /cdd: commands in README', () => {
|
|
106
|
+
const readmePath = path.join(process.cwd(), 'README.md');
|
|
107
|
+
const content = fs.readFileSync(readmePath, 'utf-8');
|
|
108
|
+
expect(content).toContain('/cdd:setup');
|
|
109
|
+
expect(content).toContain('/cdd:newTrack');
|
|
110
|
+
expect(content).toContain('/cdd:implement');
|
|
111
|
+
});
|
|
112
|
+
it('should NOT reference orchestrator commands in README', () => {
|
|
113
|
+
const readmePath = path.join(process.cwd(), 'README.md');
|
|
114
|
+
const content = fs.readFileSync(readmePath, 'utf-8');
|
|
115
|
+
expect(content).not.toContain('/orchestrator:');
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
describe('Build Scripts', () => {
|
|
119
|
+
it('should reference cdd prompts in convert-legacy script', () => {
|
|
120
|
+
const scriptPath = path.join(process.cwd(), 'scripts/convert-legacy.cjs');
|
|
121
|
+
const content = fs.readFileSync(scriptPath, 'utf-8');
|
|
122
|
+
expect(content).toContain('src/prompts/cdd');
|
|
123
|
+
expect(content).not.toContain('src/prompts/orchestrator');
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { resolveAgentName, isAgentAvailable, getAgentMapping } from '../../utils/agentMapping.js';
|
|
3
|
+
import { shouldDelegateToSynergy, getDelegationStrategy, resolveAgentForDelegation } from '../../utils/synergyDelegation.js';
|
|
4
|
+
/**
|
|
5
|
+
* Integration tests for oh-my-opencode-slim synergy support
|
|
6
|
+
*
|
|
7
|
+
* These tests verify that the various modules work together correctly
|
|
8
|
+
* for oh-my-opencode-slim integration, focusing on agent mapping, delegation,
|
|
9
|
+
* and the interaction between components.
|
|
10
|
+
*/
|
|
11
|
+
describe('oh-my-opencode-slim Integration Tests', () => {
|
|
12
|
+
describe('Complete Workflow: Mapping → Delegation', () => {
|
|
13
|
+
it('should complete full agent resolution workflow for slim framework', () => {
|
|
14
|
+
const framework = 'oh-my-opencode-slim';
|
|
15
|
+
const availableAgents = ['explorer', 'designer', 'librarian', 'oracle'];
|
|
16
|
+
// Step 1: Agent Mapping - Test that generic names map to slim-specific names
|
|
17
|
+
const explorerMapping = resolveAgentName('explore', framework);
|
|
18
|
+
expect(explorerMapping).toBe('explorer');
|
|
19
|
+
const designerMapping = resolveAgentName('frontend-ui-ux-engineer', framework);
|
|
20
|
+
expect(designerMapping).toBe('designer');
|
|
21
|
+
const librarianMapping = resolveAgentName('document-writer', framework);
|
|
22
|
+
expect(librarianMapping).toBe('librarian');
|
|
23
|
+
const oracleMapping = resolveAgentName('oracle', framework);
|
|
24
|
+
expect(oracleMapping).toBe('oracle');
|
|
25
|
+
// sisyphus has no slim equivalent
|
|
26
|
+
const sisyphusMapping = resolveAgentName('sisyphus', framework);
|
|
27
|
+
expect(sisyphusMapping).toBe(null);
|
|
28
|
+
// Step 2: Agent Availability - Test that mapped names are available
|
|
29
|
+
expect(isAgentAvailable(explorerMapping, availableAgents)).toBe(true);
|
|
30
|
+
expect(isAgentAvailable(designerMapping, availableAgents)).toBe(true);
|
|
31
|
+
expect(isAgentAvailable(librarianMapping, availableAgents)).toBe(true);
|
|
32
|
+
expect(isAgentAvailable(oracleMapping, availableAgents)).toBe(true);
|
|
33
|
+
expect(isAgentAvailable(sisyphusMapping, availableAgents)).toBe(false);
|
|
34
|
+
// Step 3: Delegation Strategy - Test that delegation is enabled for slim
|
|
35
|
+
expect(shouldDelegateToSynergy(framework)).toBe(true);
|
|
36
|
+
expect(getDelegationStrategy(framework)).toBe('delegate');
|
|
37
|
+
// Step 4: Complete Delegation Resolution - Test end-to-end resolution
|
|
38
|
+
const exploreResult = resolveAgentForDelegation('explore', framework, availableAgents);
|
|
39
|
+
expect(exploreResult.success).toBe(true);
|
|
40
|
+
expect(exploreResult.resolvedAgent).toBe('explorer');
|
|
41
|
+
expect(exploreResult.shouldFallback).toBe(false);
|
|
42
|
+
expect(exploreResult.reason).toBeUndefined();
|
|
43
|
+
const uiResult = resolveAgentForDelegation('frontend-ui-ux-engineer', framework, availableAgents);
|
|
44
|
+
expect(uiResult.success).toBe(true);
|
|
45
|
+
expect(uiResult.resolvedAgent).toBe('designer');
|
|
46
|
+
expect(uiResult.shouldFallback).toBe(false);
|
|
47
|
+
const docResult = resolveAgentForDelegation('document-writer', framework, availableAgents);
|
|
48
|
+
expect(docResult.success).toBe(true);
|
|
49
|
+
expect(docResult.resolvedAgent).toBe('librarian');
|
|
50
|
+
expect(docResult.shouldFallback).toBe(false);
|
|
51
|
+
const oracleResult = resolveAgentForDelegation('oracle', framework, availableAgents);
|
|
52
|
+
expect(oracleResult.success).toBe(true);
|
|
53
|
+
expect(oracleResult.resolvedAgent).toBe('oracle');
|
|
54
|
+
expect(oracleResult.shouldFallback).toBe(false);
|
|
55
|
+
// sisyphus should fallback since it has no slim equivalent
|
|
56
|
+
const sisyphusResult = resolveAgentForDelegation('sisyphus', framework, availableAgents);
|
|
57
|
+
expect(sisyphusResult.success).toBe(false);
|
|
58
|
+
expect(sisyphusResult.resolvedAgent).toBe(null);
|
|
59
|
+
expect(sisyphusResult.shouldFallback).toBe(true);
|
|
60
|
+
expect(sisyphusResult.reason).toContain('not available');
|
|
61
|
+
expect(sisyphusResult.reason).toContain('sisyphus');
|
|
62
|
+
});
|
|
63
|
+
it('should handle unavailable agents gracefully', () => {
|
|
64
|
+
const framework = 'oh-my-opencode-slim';
|
|
65
|
+
// Only explorer is available
|
|
66
|
+
const availableAgents = ['explorer'];
|
|
67
|
+
// explore maps to explorer and is available
|
|
68
|
+
const exploreResult = resolveAgentForDelegation('explore', framework, availableAgents);
|
|
69
|
+
expect(exploreResult.success).toBe(true);
|
|
70
|
+
expect(exploreResult.resolvedAgent).toBe('explorer');
|
|
71
|
+
// frontend-ui-ux-engineer maps to designer but designer is not available
|
|
72
|
+
const uiResult = resolveAgentForDelegation('frontend-ui-ux-engineer', framework, availableAgents);
|
|
73
|
+
expect(uiResult.success).toBe(false);
|
|
74
|
+
expect(uiResult.shouldFallback).toBe(true);
|
|
75
|
+
expect(uiResult.reason).toContain('not available');
|
|
76
|
+
// document-writer maps to librarian but librarian is not available
|
|
77
|
+
const docResult = resolveAgentForDelegation('document-writer', framework, availableAgents);
|
|
78
|
+
expect(docResult.success).toBe(false);
|
|
79
|
+
expect(docResult.shouldFallback).toBe(true);
|
|
80
|
+
expect(docResult.reason).toContain('not available');
|
|
81
|
+
});
|
|
82
|
+
it('should work with empty available agents list', () => {
|
|
83
|
+
const framework = 'oh-my-opencode-slim';
|
|
84
|
+
const availableAgents = [];
|
|
85
|
+
// All agents should fallback when none are available
|
|
86
|
+
const exploreResult = resolveAgentForDelegation('explore', framework, availableAgents);
|
|
87
|
+
expect(exploreResult.success).toBe(false);
|
|
88
|
+
expect(exploreResult.shouldFallback).toBe(true);
|
|
89
|
+
expect(exploreResult.reason).toContain('not available');
|
|
90
|
+
const sisyphusResult = resolveAgentForDelegation('sisyphus', framework, availableAgents);
|
|
91
|
+
expect(sisyphusResult.success).toBe(false);
|
|
92
|
+
expect(sisyphusResult.shouldFallback).toBe(true);
|
|
93
|
+
expect(sisyphusResult.reason).toContain('not available');
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
describe('Framework Priority and Switching', () => {
|
|
97
|
+
it('should handle oh-my-opencode framework (pass-through behavior)', () => {
|
|
98
|
+
const framework = 'oh-my-opencode';
|
|
99
|
+
const availableAgents = ['sisyphus', 'explore', 'oracle'];
|
|
100
|
+
// oh-my-opencode uses pass-through mapping (no transformation)
|
|
101
|
+
expect(resolveAgentName('explore', framework)).toBe('explore');
|
|
102
|
+
expect(resolveAgentName('sisyphus', framework)).toBe('sisyphus');
|
|
103
|
+
expect(resolveAgentName('oracle', framework)).toBe('oracle');
|
|
104
|
+
expect(resolveAgentName('frontend-ui-ux-engineer', framework)).toBe('frontend-ui-ux-engineer');
|
|
105
|
+
// Delegation should be enabled
|
|
106
|
+
expect(shouldDelegateToSynergy(framework)).toBe(true);
|
|
107
|
+
expect(getDelegationStrategy(framework)).toBe('delegate');
|
|
108
|
+
// Since oh-my-opencode doesn't have agent list detection,
|
|
109
|
+
// delegation with empty list means agent is assumed unavailable
|
|
110
|
+
const exploreResult = resolveAgentForDelegation('explore', framework, []);
|
|
111
|
+
expect(exploreResult.success).toBe(false);
|
|
112
|
+
expect(exploreResult.shouldFallback).toBe(true);
|
|
113
|
+
expect(exploreResult.reason).toContain('disabled or not available');
|
|
114
|
+
});
|
|
115
|
+
it('should handle manual mode (no synergy framework)', () => {
|
|
116
|
+
const framework = 'none';
|
|
117
|
+
const availableAgents = [];
|
|
118
|
+
// Manual mode means no delegation
|
|
119
|
+
expect(shouldDelegateToSynergy(framework)).toBe(false);
|
|
120
|
+
expect(getDelegationStrategy(framework)).toBe('manual');
|
|
121
|
+
// All agents should fallback in manual mode
|
|
122
|
+
const exploreResult = resolveAgentForDelegation('explore', framework, availableAgents);
|
|
123
|
+
expect(exploreResult.success).toBe(false);
|
|
124
|
+
expect(exploreResult.shouldFallback).toBe(true);
|
|
125
|
+
expect(exploreResult.reason).toContain('No synergy framework active');
|
|
126
|
+
const sisyphusResult = resolveAgentForDelegation('sisyphus', framework, availableAgents);
|
|
127
|
+
expect(sisyphusResult.success).toBe(false);
|
|
128
|
+
expect(sisyphusResult.shouldFallback).toBe(true);
|
|
129
|
+
expect(sisyphusResult.reason).toContain('No synergy framework active');
|
|
130
|
+
});
|
|
131
|
+
});
|
|
132
|
+
describe('Agent Mapping Completeness', () => {
|
|
133
|
+
it('should have mappings for all known generic agent names', () => {
|
|
134
|
+
const framework = 'oh-my-opencode-slim';
|
|
135
|
+
// Test all known mappings
|
|
136
|
+
const mappings = [
|
|
137
|
+
{ generic: 'explore', slim: 'explorer' },
|
|
138
|
+
{ generic: 'frontend-ui-ux-engineer', slim: 'designer' },
|
|
139
|
+
{ generic: 'document-writer', slim: 'librarian' },
|
|
140
|
+
{ generic: 'oracle', slim: 'oracle' }, // pass-through
|
|
141
|
+
{ generic: 'sisyphus', slim: null }, // no equivalent
|
|
142
|
+
];
|
|
143
|
+
for (const { generic, slim } of mappings) {
|
|
144
|
+
const resolved = resolveAgentName(generic, framework);
|
|
145
|
+
expect(resolved).toBe(slim);
|
|
146
|
+
}
|
|
147
|
+
});
|
|
148
|
+
it('should get complete agent mapping as object', () => {
|
|
149
|
+
const framework = 'oh-my-opencode-slim';
|
|
150
|
+
const mapping = getAgentMapping(framework);
|
|
151
|
+
// Check that known mappings exist
|
|
152
|
+
expect(mapping['explore']).toBe('explorer');
|
|
153
|
+
expect(mapping['frontend-ui-ux-engineer']).toBe('designer');
|
|
154
|
+
expect(mapping['document-writer']).toBe('librarian');
|
|
155
|
+
expect(mapping['oracle']).toBe('oracle');
|
|
156
|
+
expect(mapping['sisyphus']).toBe(null);
|
|
157
|
+
});
|
|
158
|
+
it('should have empty mapping for oh-my-opencode (pass-through)', () => {
|
|
159
|
+
const framework = 'oh-my-opencode';
|
|
160
|
+
const mapping = getAgentMapping(framework);
|
|
161
|
+
// oh-my-opencode has no special mappings
|
|
162
|
+
expect(mapping).toEqual({});
|
|
163
|
+
});
|
|
164
|
+
it('should pass through unknown agent names for oh-my-opencode only', () => {
|
|
165
|
+
// oh-my-opencode passes through unknown agents
|
|
166
|
+
expect(resolveAgentName('unknown-agent', 'oh-my-opencode')).toBe('unknown-agent');
|
|
167
|
+
expect(resolveAgentName('custom-agent', 'oh-my-opencode')).toBe('custom-agent');
|
|
168
|
+
expect(resolveAgentName('test-agent', 'oh-my-opencode')).toBe('test-agent');
|
|
169
|
+
// oh-my-opencode-slim returns null for unknown agents
|
|
170
|
+
expect(resolveAgentName('unknown-agent', 'oh-my-opencode-slim')).toBe(null);
|
|
171
|
+
expect(resolveAgentName('custom-agent', 'oh-my-opencode-slim')).toBe(null);
|
|
172
|
+
});
|
|
173
|
+
});
|
|
174
|
+
describe('Backward Compatibility', () => {
|
|
175
|
+
it('should not break oh-my-opencode agent resolution', () => {
|
|
176
|
+
const framework = 'oh-my-opencode';
|
|
177
|
+
// All agent names should pass through unchanged for oh-my-opencode
|
|
178
|
+
const agents = ['sisyphus', 'explore', 'oracle', 'frontend-ui-ux-engineer', 'document-writer'];
|
|
179
|
+
for (const agent of agents) {
|
|
180
|
+
expect(resolveAgentName(agent, framework)).toBe(agent);
|
|
181
|
+
}
|
|
182
|
+
});
|
|
183
|
+
it('should maintain delegation behavior for oh-my-opencode with available agents', () => {
|
|
184
|
+
const framework = 'oh-my-opencode';
|
|
185
|
+
const availableAgents = ['sisyphus']; // Provide available agents
|
|
186
|
+
// Delegation should be enabled
|
|
187
|
+
expect(shouldDelegateToSynergy(framework)).toBe(true);
|
|
188
|
+
expect(getDelegationStrategy(framework)).toBe('delegate');
|
|
189
|
+
// Resolution should work when agent is available
|
|
190
|
+
const result = resolveAgentForDelegation('sisyphus', framework, availableAgents);
|
|
191
|
+
expect(result.success).toBe(true);
|
|
192
|
+
expect(result.resolvedAgent).toBe('sisyphus');
|
|
193
|
+
expect(result.shouldFallback).toBe(false);
|
|
194
|
+
});
|
|
195
|
+
});
|
|
196
|
+
describe('Edge Cases and Error Handling', () => {
|
|
197
|
+
it('should handle null agent names gracefully', () => {
|
|
198
|
+
const availableAgents = ['explorer', 'designer'];
|
|
199
|
+
// null agent names should not be available
|
|
200
|
+
expect(isAgentAvailable(null, availableAgents)).toBe(false);
|
|
201
|
+
});
|
|
202
|
+
it('should handle empty agent name strings', () => {
|
|
203
|
+
const availableAgents = ['explorer', 'designer'];
|
|
204
|
+
// Empty string should not match
|
|
205
|
+
expect(isAgentAvailable('', availableAgents)).toBe(false);
|
|
206
|
+
});
|
|
207
|
+
it('should handle case-sensitive agent names', () => {
|
|
208
|
+
const framework = 'oh-my-opencode-slim';
|
|
209
|
+
const availableAgents = ['explorer', 'designer'];
|
|
210
|
+
// Agent names are case-sensitive
|
|
211
|
+
expect(isAgentAvailable('Explorer', availableAgents)).toBe(false);
|
|
212
|
+
expect(isAgentAvailable('EXPLORER', availableAgents)).toBe(false);
|
|
213
|
+
expect(isAgentAvailable('explorer', availableAgents)).toBe(true);
|
|
214
|
+
});
|
|
215
|
+
it('should handle whitespace in agent names', () => {
|
|
216
|
+
const availableAgents = ['explorer', 'designer'];
|
|
217
|
+
// Whitespace should matter
|
|
218
|
+
expect(isAgentAvailable(' explorer', availableAgents)).toBe(false);
|
|
219
|
+
expect(isAgentAvailable('explorer ', availableAgents)).toBe(false);
|
|
220
|
+
expect(isAgentAvailable('explorer', availableAgents)).toBe(true);
|
|
221
|
+
});
|
|
222
|
+
});
|
|
223
|
+
describe('Delegation Reason Messages', () => {
|
|
224
|
+
it('should provide clear reasons for fallback in manual mode', () => {
|
|
225
|
+
const result = resolveAgentForDelegation('explore', 'none', []);
|
|
226
|
+
expect(result.success).toBe(false);
|
|
227
|
+
expect(result.shouldFallback).toBe(true);
|
|
228
|
+
expect(result.reason).toBe('No synergy framework active');
|
|
229
|
+
});
|
|
230
|
+
it('should provide clear reasons for missing agent mappings', () => {
|
|
231
|
+
const result = resolveAgentForDelegation('sisyphus', 'oh-my-opencode-slim', ['explorer']);
|
|
232
|
+
expect(result.success).toBe(false);
|
|
233
|
+
expect(result.shouldFallback).toBe(true);
|
|
234
|
+
expect(result.reason).toContain('not available');
|
|
235
|
+
expect(result.reason).toContain('sisyphus');
|
|
236
|
+
expect(result.reason).toContain('oh-my-opencode-slim');
|
|
237
|
+
});
|
|
238
|
+
it('should provide clear reasons for unavailable agents', () => {
|
|
239
|
+
const result = resolveAgentForDelegation('frontend-ui-ux-engineer', 'oh-my-opencode-slim', ['explorer']);
|
|
240
|
+
expect(result.success).toBe(false);
|
|
241
|
+
expect(result.shouldFallback).toBe(true);
|
|
242
|
+
expect(result.reason).toContain('not available');
|
|
243
|
+
expect(result.reason).toContain('designer');
|
|
244
|
+
});
|
|
245
|
+
});
|
|
246
|
+
});
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { SynergyFramework } from './configDetection.js';
|
|
2
|
+
/**
|
|
3
|
+
* Resolve a requested agent name to the framework-specific agent name
|
|
4
|
+
*
|
|
5
|
+
* @param requestedAgent - The agent name requested by the user
|
|
6
|
+
* @param framework - The active synergy framework
|
|
7
|
+
* @returns The framework-specific agent name, or null if not available
|
|
8
|
+
*/
|
|
9
|
+
export declare function resolveAgentName(requestedAgent: string, framework: SynergyFramework): string | null;
|
|
10
|
+
/**
|
|
11
|
+
* Get the complete agent mapping for a framework
|
|
12
|
+
*
|
|
13
|
+
* @param framework - The synergy framework
|
|
14
|
+
* @returns The agent mapping object
|
|
15
|
+
*/
|
|
16
|
+
export declare function getAgentMapping(framework: SynergyFramework): Record<string, string | null>;
|
|
17
|
+
/**
|
|
18
|
+
* Check if an agent is available in the list of available agents
|
|
19
|
+
*
|
|
20
|
+
* @param agentName - The agent name to check (can be null)
|
|
21
|
+
* @param availableAgents - List of available agents
|
|
22
|
+
* @returns True if the agent is available
|
|
23
|
+
*/
|
|
24
|
+
export declare function isAgentAvailable(agentName: string | null, availableAgents: string[]): boolean;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mapping from generic agent names to framework-specific agent names
|
|
3
|
+
* for oh-my-opencode-slim
|
|
4
|
+
*/
|
|
5
|
+
const SLIM_AGENT_MAPPING = {
|
|
6
|
+
// Generic names → slim-specific names
|
|
7
|
+
'explore': 'explorer',
|
|
8
|
+
'frontend-ui-ux-engineer': 'designer',
|
|
9
|
+
'document-writer': 'librarian',
|
|
10
|
+
'oracle': 'oracle',
|
|
11
|
+
// Slim native names (pass through)
|
|
12
|
+
'explorer': 'explorer',
|
|
13
|
+
'designer': 'designer',
|
|
14
|
+
'librarian': 'librarian',
|
|
15
|
+
// Agents without slim equivalent
|
|
16
|
+
'sisyphus': null,
|
|
17
|
+
'orchestrator': null,
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* Resolve a requested agent name to the framework-specific agent name
|
|
21
|
+
*
|
|
22
|
+
* @param requestedAgent - The agent name requested by the user
|
|
23
|
+
* @param framework - The active synergy framework
|
|
24
|
+
* @returns The framework-specific agent name, or null if not available
|
|
25
|
+
*/
|
|
26
|
+
export function resolveAgentName(requestedAgent, framework) {
|
|
27
|
+
if (framework === 'none') {
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
if (framework === 'oh-my-opencode-slim') {
|
|
31
|
+
// Use the mapping, return null if not found
|
|
32
|
+
return SLIM_AGENT_MAPPING[requestedAgent] ?? null;
|
|
33
|
+
}
|
|
34
|
+
// For oh-my-opencode, pass through agent names unchanged
|
|
35
|
+
if (framework === 'oh-my-opencode') {
|
|
36
|
+
return requestedAgent;
|
|
37
|
+
}
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Get the complete agent mapping for a framework
|
|
42
|
+
*
|
|
43
|
+
* @param framework - The synergy framework
|
|
44
|
+
* @returns The agent mapping object
|
|
45
|
+
*/
|
|
46
|
+
export function getAgentMapping(framework) {
|
|
47
|
+
if (framework === 'oh-my-opencode-slim') {
|
|
48
|
+
return { ...SLIM_AGENT_MAPPING };
|
|
49
|
+
}
|
|
50
|
+
// For oh-my-opencode and none, return empty mapping
|
|
51
|
+
return {};
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Check if an agent is available in the list of available agents
|
|
55
|
+
*
|
|
56
|
+
* @param agentName - The agent name to check (can be null)
|
|
57
|
+
* @param availableAgents - List of available agents
|
|
58
|
+
* @returns True if the agent is available
|
|
59
|
+
*/
|
|
60
|
+
export function isAgentAvailable(agentName, availableAgents) {
|
|
61
|
+
if (agentName === null) {
|
|
62
|
+
return false;
|
|
63
|
+
}
|
|
64
|
+
return availableAgents.includes(agentName);
|
|
65
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|