@codemcp/workflows 5.0.1 → 5.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +6 -2
- package/skill/SKILL.md +23 -0
- package/.prettierignore +0 -2
- package/.turbo/turbo-build.log +0 -4
- package/.vibe/conversation-state.sqlite +0 -0
- package/src/components/beads/beads-instruction-generator.ts +0 -230
- package/src/components/beads/beads-plan-manager.ts +0 -333
- package/src/components/beads/beads-task-backend-client.ts +0 -229
- package/src/index.ts +0 -93
- package/src/notification-service.ts +0 -23
- package/src/plugin-system/beads-plugin.ts +0 -649
- package/src/plugin-system/commit-plugin.ts +0 -252
- package/src/plugin-system/index.ts +0 -20
- package/src/plugin-system/plugin-interfaces.ts +0 -153
- package/src/plugin-system/plugin-registry.ts +0 -190
- package/src/resource-handlers/conversation-state.ts +0 -55
- package/src/resource-handlers/development-plan.ts +0 -48
- package/src/resource-handlers/index.ts +0 -73
- package/src/resource-handlers/system-prompt.ts +0 -55
- package/src/resource-handlers/workflow-resource.ts +0 -132
- package/src/response-renderer.ts +0 -116
- package/src/server-config.ts +0 -760
- package/src/server-helpers.ts +0 -245
- package/src/server-implementation.ts +0 -277
- package/src/server.ts +0 -9
- package/src/tool-handlers/base-tool-handler.ts +0 -151
- package/src/tool-handlers/conduct-review.ts +0 -190
- package/src/tool-handlers/get-tool-info.ts +0 -273
- package/src/tool-handlers/index.ts +0 -115
- package/src/tool-handlers/list-workflows.ts +0 -78
- package/src/tool-handlers/no-idea.ts +0 -47
- package/src/tool-handlers/proceed-to-phase.ts +0 -296
- package/src/tool-handlers/reset-development.ts +0 -90
- package/src/tool-handlers/resume-workflow.ts +0 -378
- package/src/tool-handlers/setup-project-docs.ts +0 -232
- package/src/tool-handlers/start-development.ts +0 -746
- package/src/tool-handlers/whats-next.ts +0 -246
- package/src/types.ts +0 -135
- package/src/version-info.ts +0 -213
- package/test/e2e/beads-plugin-integration.test.ts +0 -1623
- package/test/e2e/commit-plugin-integration.test.ts +0 -222
- package/test/e2e/core-functionality.test.ts +0 -167
- package/test/e2e/git-branch-detection.test.ts +0 -351
- package/test/e2e/mcp-contract.test.ts +0 -509
- package/test/e2e/plan-management.test.ts +0 -334
- package/test/e2e/plugin-system-integration.test.ts +0 -1410
- package/test/e2e/state-management.test.ts +0 -387
- package/test/e2e/workflow-integration.test.ts +0 -498
- package/test/unit/beads-instruction-generator.test.ts +0 -979
- package/test/unit/beads-phase-task-id-integration.test.ts +0 -535
- package/test/unit/beads-plugin-behavioral.test.ts +0 -545
- package/test/unit/beads-plugin.test.ts +0 -117
- package/test/unit/commit-plugin.test.ts +0 -196
- package/test/unit/conduct-review.test.ts +0 -151
- package/test/unit/conversation-not-found-error.test.ts +0 -120
- package/test/unit/plugin-error-handling.test.ts +0 -240
- package/test/unit/proceed-to-phase-plugin-integration.test.ts +0 -150
- package/test/unit/reset-functionality.test.ts +0 -72
- package/test/unit/resume-workflow.test.ts +0 -193
- package/test/unit/server-config-plugin-registry.test.ts +0 -99
- package/test/unit/server-tools.test.ts +0 -310
- package/test/unit/setup-project-docs-handler.test.ts +0 -268
- package/test/unit/start-development-artifact-detection.test.ts +0 -387
- package/test/unit/start-development-gitignore.test.ts +0 -178
- package/test/unit/start-development-goal-extraction.test.ts +0 -226
- package/test/unit/system-prompt-resource.test.ts +0 -102
- package/test/unit/tool-handlers/no-idea.test.ts +0 -40
- package/test/utils/e2e-test-setup.ts +0 -451
- package/test/utils/run-server-in-dir.sh +0 -27
- package/test/utils/temp-files.ts +0 -320
- package/test/utils/test-access.ts +0 -79
- package/test/utils/test-helpers.ts +0 -288
- package/test/utils/test-setup.ts +0 -77
- package/tsconfig.build.json +0 -10
- package/tsconfig.build.tsbuildinfo +0 -1
- package/tsconfig.json +0 -12
- package/vitest.config.ts +0 -19
|
@@ -1,150 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Test plugin hook integration in proceed-to-phase
|
|
3
|
-
* Focus on testing that plugin hooks are called correctly
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { describe, it, expect, beforeEach, vi } from 'vitest';
|
|
7
|
-
import { ProceedToPhaseHandler } from '../../src/tool-handlers/proceed-to-phase.js';
|
|
8
|
-
import { PluginRegistry } from '../../src/plugin-system/plugin-registry.js';
|
|
9
|
-
import type { ServerContext } from '../../src/types.js';
|
|
10
|
-
import type { PluginHookContext } from '../../src/plugin-system/plugin-interfaces.js';
|
|
11
|
-
|
|
12
|
-
// Mock dependencies
|
|
13
|
-
vi.mock('@codemcp/workflows-core', () => ({
|
|
14
|
-
createLogger: vi.fn(() => ({
|
|
15
|
-
debug: vi.fn(),
|
|
16
|
-
info: vi.fn(),
|
|
17
|
-
warn: vi.fn(),
|
|
18
|
-
error: vi.fn(),
|
|
19
|
-
})),
|
|
20
|
-
}));
|
|
21
|
-
|
|
22
|
-
describe('ProceedToPhase Plugin Integration', () => {
|
|
23
|
-
let handler: ProceedToPhaseHandler;
|
|
24
|
-
let mockPluginRegistry: PluginRegistry;
|
|
25
|
-
let mockContext: ServerContext;
|
|
26
|
-
|
|
27
|
-
beforeEach(() => {
|
|
28
|
-
mockPluginRegistry = new PluginRegistry();
|
|
29
|
-
|
|
30
|
-
mockContext = {
|
|
31
|
-
conversationManager: {
|
|
32
|
-
getConversationContext: vi.fn().mockResolvedValue({
|
|
33
|
-
conversationId: 'test-conversation',
|
|
34
|
-
planFilePath: '/test/plan.md',
|
|
35
|
-
currentPhase: 'plan',
|
|
36
|
-
workflowName: 'epcc',
|
|
37
|
-
projectPath: '/test/project',
|
|
38
|
-
gitBranch: 'main',
|
|
39
|
-
}),
|
|
40
|
-
updateConversationState: vi.fn().mockResolvedValue(undefined),
|
|
41
|
-
},
|
|
42
|
-
transitionEngine: {
|
|
43
|
-
handleExplicitTransition: vi.fn().mockReturnValue({
|
|
44
|
-
newPhase: 'code',
|
|
45
|
-
transitionReason: 'Test transition',
|
|
46
|
-
isModeled: true,
|
|
47
|
-
instructions: 'Test transition instructions',
|
|
48
|
-
}),
|
|
49
|
-
},
|
|
50
|
-
planManager: {
|
|
51
|
-
getPlanFileInfo: vi.fn().mockResolvedValue({ exists: true }),
|
|
52
|
-
},
|
|
53
|
-
instructionGenerator: {
|
|
54
|
-
generateInstructions: vi.fn().mockResolvedValue({
|
|
55
|
-
instructions: 'Test instructions',
|
|
56
|
-
}),
|
|
57
|
-
},
|
|
58
|
-
workflowManager: {
|
|
59
|
-
loadWorkflowForProject: vi.fn().mockReturnValue({
|
|
60
|
-
name: 'epcc',
|
|
61
|
-
states: { plan: {}, code: {} },
|
|
62
|
-
}),
|
|
63
|
-
},
|
|
64
|
-
interactionLogger: {
|
|
65
|
-
logInteraction: vi.fn().mockResolvedValue(undefined),
|
|
66
|
-
},
|
|
67
|
-
projectPath: '/test/project',
|
|
68
|
-
pluginRegistry: mockPluginRegistry,
|
|
69
|
-
} as unknown as ServerContext;
|
|
70
|
-
|
|
71
|
-
handler = new ProceedToPhaseHandler();
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
it('should call beforePhaseTransition plugin hook during phase transition', async () => {
|
|
75
|
-
const hookSpy = vi.fn().mockResolvedValue(undefined);
|
|
76
|
-
|
|
77
|
-
// Register a mock plugin with beforePhaseTransition hook
|
|
78
|
-
const mockPlugin = {
|
|
79
|
-
getName: () => 'TestPlugin',
|
|
80
|
-
getSequence: () => 100,
|
|
81
|
-
isEnabled: () => true,
|
|
82
|
-
getHooks: () => ({
|
|
83
|
-
beforePhaseTransition: hookSpy,
|
|
84
|
-
}),
|
|
85
|
-
};
|
|
86
|
-
|
|
87
|
-
mockPluginRegistry.registerPlugin(mockPlugin);
|
|
88
|
-
|
|
89
|
-
// Execute the proceed_to_phase handler
|
|
90
|
-
await handler.handle(
|
|
91
|
-
{
|
|
92
|
-
target_phase: 'code',
|
|
93
|
-
reason: 'Testing plugin integration',
|
|
94
|
-
review_state: 'not-required',
|
|
95
|
-
},
|
|
96
|
-
mockContext
|
|
97
|
-
);
|
|
98
|
-
|
|
99
|
-
// Verify the hook was called with correct parameters
|
|
100
|
-
expect(hookSpy).toHaveBeenCalledOnce();
|
|
101
|
-
|
|
102
|
-
const [pluginContext, currentPhase, targetPhase] = hookSpy.mock.calls[0];
|
|
103
|
-
|
|
104
|
-
// Verify plugin context structure
|
|
105
|
-
expect(pluginContext).toMatchObject<Partial<PluginHookContext>>({
|
|
106
|
-
conversationId: 'test-conversation',
|
|
107
|
-
planFilePath: '/test/plan.md',
|
|
108
|
-
currentPhase: 'plan',
|
|
109
|
-
workflow: 'epcc',
|
|
110
|
-
projectPath: '/test/project',
|
|
111
|
-
gitBranch: 'main',
|
|
112
|
-
targetPhase: 'code',
|
|
113
|
-
});
|
|
114
|
-
|
|
115
|
-
// Verify phase parameters
|
|
116
|
-
expect(currentPhase).toBe('plan');
|
|
117
|
-
expect(targetPhase).toBe('code');
|
|
118
|
-
});
|
|
119
|
-
|
|
120
|
-
it('should handle plugin hook errors by returning error result', async () => {
|
|
121
|
-
const hookError = new Error('Plugin validation failed');
|
|
122
|
-
const hookSpy = vi.fn().mockRejectedValue(hookError);
|
|
123
|
-
|
|
124
|
-
// Register a mock plugin that throws an error
|
|
125
|
-
const mockPlugin = {
|
|
126
|
-
getName: () => 'TestPlugin',
|
|
127
|
-
getSequence: () => 100,
|
|
128
|
-
isEnabled: () => true,
|
|
129
|
-
getHooks: () => ({
|
|
130
|
-
beforePhaseTransition: hookSpy,
|
|
131
|
-
}),
|
|
132
|
-
};
|
|
133
|
-
|
|
134
|
-
mockPluginRegistry.registerPlugin(mockPlugin);
|
|
135
|
-
|
|
136
|
-
// Execute the handler and expect it to return error result
|
|
137
|
-
const result = await handler.handle(
|
|
138
|
-
{
|
|
139
|
-
target_phase: 'code',
|
|
140
|
-
reason: 'Testing plugin error handling',
|
|
141
|
-
review_state: 'not-required',
|
|
142
|
-
},
|
|
143
|
-
mockContext
|
|
144
|
-
);
|
|
145
|
-
|
|
146
|
-
expect(result.success).toBe(false);
|
|
147
|
-
expect(result.error).toContain('Plugin validation failed');
|
|
148
|
-
expect(hookSpy).toHaveBeenCalledOnce();
|
|
149
|
-
});
|
|
150
|
-
});
|
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Test for reset functionality
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
6
|
-
import { ResponsibleVibeMCPServer } from '../../src/server.js';
|
|
7
|
-
import { TempProject } from '../utils/temp-files.js';
|
|
8
|
-
import { ServerTestHelper, MockDocsHelper } from '../utils/test-helpers.js';
|
|
9
|
-
|
|
10
|
-
describe('Reset Functionality', () => {
|
|
11
|
-
let server: ResponsibleVibeMCPServer;
|
|
12
|
-
let tempProject: TempProject;
|
|
13
|
-
|
|
14
|
-
beforeEach(async () => {
|
|
15
|
-
tempProject = new TempProject({ projectName: 'reset-test' });
|
|
16
|
-
MockDocsHelper.addToTempProject(tempProject);
|
|
17
|
-
|
|
18
|
-
server = await ServerTestHelper.createServer(tempProject.projectPath);
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
afterEach(async () => {
|
|
22
|
-
await ServerTestHelper.cleanupServer(server);
|
|
23
|
-
tempProject.cleanup();
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
it('should require confirmation to reset', async () => {
|
|
27
|
-
// Try to reset without confirmation
|
|
28
|
-
try {
|
|
29
|
-
await server.handleResetDevelopment({ confirm: false });
|
|
30
|
-
expect.fail('Should have thrown an error');
|
|
31
|
-
} catch (error) {
|
|
32
|
-
expect(error.message).toContain(
|
|
33
|
-
'Reset operation requires explicit confirmation'
|
|
34
|
-
);
|
|
35
|
-
}
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
it('should successfully reset with confirmation', async () => {
|
|
39
|
-
// First, initialize development with a workflow
|
|
40
|
-
await server.handleStartDevelopment({
|
|
41
|
-
workflow: 'waterfall',
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
// Then create some state by calling whats_next
|
|
45
|
-
await server.handleWhatsNext({
|
|
46
|
-
context: 'test context',
|
|
47
|
-
user_input: 'test input',
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
// Now reset with confirmation
|
|
51
|
-
const result = await server.handleResetDevelopment({
|
|
52
|
-
confirm: true,
|
|
53
|
-
reason: 'test reset',
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
expect(result.success).toBe(true);
|
|
57
|
-
expect(result.resetItems).toContain('interaction_logs');
|
|
58
|
-
expect(result.resetItems).toContain('conversation_state');
|
|
59
|
-
expect(result.resetItems).toContain('plan_file');
|
|
60
|
-
expect(result.message).toContain('Successfully reset conversation');
|
|
61
|
-
expect(result.message).toContain('test reset');
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
it('should validate confirm parameter type', async () => {
|
|
65
|
-
try {
|
|
66
|
-
await server.handleResetDevelopment({ confirm: 'true' }); // string instead of boolean
|
|
67
|
-
expect.fail('Should have thrown an error');
|
|
68
|
-
} catch (error) {
|
|
69
|
-
expect(error.message).toContain('confirm parameter must be a boolean');
|
|
70
|
-
}
|
|
71
|
-
});
|
|
72
|
-
});
|
|
@@ -1,193 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
2
|
-
import { ResponsibleVibeMCPServer } from '../packages/mcp-server/src/server.js';
|
|
3
|
-
import { tmpdir } from 'node:os';
|
|
4
|
-
import { join } from 'node:path';
|
|
5
|
-
import { mkdtemp, rm, writeFile, mkdir } from 'node:fs/promises';
|
|
6
|
-
import { ServerTestHelper, MockDocsHelper } from '../utils/test-helpers.js';
|
|
7
|
-
|
|
8
|
-
describe('resume_workflow tool', () => {
|
|
9
|
-
let server: ResponsibleVibeMCPServer;
|
|
10
|
-
let tempDir: string;
|
|
11
|
-
|
|
12
|
-
beforeEach(async () => {
|
|
13
|
-
// Create temporary directory for testing
|
|
14
|
-
tempDir = await mkdtemp(join(tmpdir(), 'responsible-vibe-test-'));
|
|
15
|
-
MockDocsHelper.addToProject(tempDir);
|
|
16
|
-
|
|
17
|
-
server = await ServerTestHelper.createServer(tempDir);
|
|
18
|
-
|
|
19
|
-
// Initialize development with waterfall workflow before testing
|
|
20
|
-
await server.handleStartDevelopment({ workflow: 'waterfall' });
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
afterEach(async () => {
|
|
24
|
-
await ServerTestHelper.cleanupServer(server);
|
|
25
|
-
await rm(tempDir, { recursive: true, force: true });
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
it('should return comprehensive workflow resumption information', async () => {
|
|
29
|
-
const result = await server.handleResumeWorkflow({});
|
|
30
|
-
|
|
31
|
-
expect(result).toBeTypeOf('object');
|
|
32
|
-
expect(result.workflow_status).toBeDefined();
|
|
33
|
-
expect(result.plan_status).toBeDefined();
|
|
34
|
-
expect(result.system_prompt).toBeDefined(); // Default: include system prompt
|
|
35
|
-
expect(result.recommendations).toBeDefined();
|
|
36
|
-
expect(result.generated_at).toBeDefined();
|
|
37
|
-
expect(result.tool_version).toBeDefined();
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
it('should include workflow status information', async () => {
|
|
41
|
-
const result = await server.handleResumeWorkflow({});
|
|
42
|
-
|
|
43
|
-
const workflowStatus = result.workflow_status;
|
|
44
|
-
expect(workflowStatus.current_phase).toBeTypeOf('string');
|
|
45
|
-
expect(workflowStatus.project_path).toBe(tempDir);
|
|
46
|
-
expect(workflowStatus.git_branch).toBeTypeOf('string');
|
|
47
|
-
expect(workflowStatus.state_machine).toBeDefined();
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
it('should include plan status information', async () => {
|
|
51
|
-
const result = await server.handleResumeWorkflow({});
|
|
52
|
-
|
|
53
|
-
const planStatus = result.plan_status;
|
|
54
|
-
expect(planStatus.exists).toBeTypeOf('boolean');
|
|
55
|
-
expect(planStatus.path).toBeTypeOf('string');
|
|
56
|
-
expect(planStatus.path).toContain('.vibe/development-plan');
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
it('should include system prompt by default', async () => {
|
|
60
|
-
const result = await server.handleResumeWorkflow({});
|
|
61
|
-
|
|
62
|
-
expect(result.system_prompt).toBeTypeOf('string');
|
|
63
|
-
// Streamlined prompt is ~400-600 chars (was 2000+ before)
|
|
64
|
-
expect(result.system_prompt.length).toBeGreaterThan(200);
|
|
65
|
-
expect(result.system_prompt.length).toBeLessThan(1000);
|
|
66
|
-
expect(result.system_prompt).toContain('responsible-vibe-mcp');
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
it('should exclude system prompt when requested', async () => {
|
|
70
|
-
const result = await server.handleResumeWorkflow({
|
|
71
|
-
include_system_prompt: false,
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
expect(result.system_prompt).toBeNull();
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
it('should provide phase-specific recommendations', async () => {
|
|
78
|
-
const result = await server.handleResumeWorkflow({});
|
|
79
|
-
|
|
80
|
-
const recommendations = result.recommendations;
|
|
81
|
-
expect(recommendations.immediate_actions).toBeInstanceOf(Array);
|
|
82
|
-
expect(recommendations.immediate_actions.length).toBeGreaterThan(0);
|
|
83
|
-
expect(recommendations.phase_guidance).toBeTypeOf('string');
|
|
84
|
-
expect(recommendations.potential_issues).toBeInstanceOf(Array);
|
|
85
|
-
|
|
86
|
-
// Should always recommend calling whats_next
|
|
87
|
-
const hasWhatsNext = recommendations.immediate_actions.some(
|
|
88
|
-
(action: string) => action.includes('whats_next')
|
|
89
|
-
);
|
|
90
|
-
expect(hasWhatsNext).toBe(true);
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
it('should analyze plan file content when available', async () => {
|
|
94
|
-
// Create a mock plan file
|
|
95
|
-
const planContent = `# Development Plan
|
|
96
|
-
|
|
97
|
-
## Implementation
|
|
98
|
-
|
|
99
|
-
### Tasks
|
|
100
|
-
- [x] Completed task 1
|
|
101
|
-
- [x] Completed task 2
|
|
102
|
-
- [ ] Active task 1
|
|
103
|
-
- [ ] Active task 2
|
|
104
|
-
|
|
105
|
-
## Decision Log
|
|
106
|
-
- Decision 1: Use TypeScript
|
|
107
|
-
- Decision 2: Use Vitest for testing
|
|
108
|
-
`;
|
|
109
|
-
|
|
110
|
-
// Create .vibe directory and write plan file to expected location
|
|
111
|
-
const planDir = join(tempDir, '.vibe');
|
|
112
|
-
await mkdir(planDir, { recursive: true });
|
|
113
|
-
await writeFile(join(planDir, 'development-plan-default.md'), planContent);
|
|
114
|
-
|
|
115
|
-
const result = await server.handleResumeWorkflow({});
|
|
116
|
-
|
|
117
|
-
const planStatus = result.plan_status;
|
|
118
|
-
expect(planStatus.exists).toBe(true);
|
|
119
|
-
expect(planStatus.analysis).toBeDefined();
|
|
120
|
-
|
|
121
|
-
const analysis = planStatus.analysis;
|
|
122
|
-
expect(analysis.active_tasks).toContain('Active task 1');
|
|
123
|
-
expect(analysis.active_tasks).toContain('Active task 2');
|
|
124
|
-
expect(analysis.completed_tasks).toContain('Completed task 1');
|
|
125
|
-
expect(analysis.completed_tasks).toContain('Completed task 2');
|
|
126
|
-
|
|
127
|
-
// Phase should come from database, not plan file
|
|
128
|
-
expect(result.workflow_status.current_phase).toBeDefined();
|
|
129
|
-
expect(result.workflow_status.current_phase).toBeTypeOf('string');
|
|
130
|
-
});
|
|
131
|
-
|
|
132
|
-
it('should handle missing plan file gracefully', async () => {
|
|
133
|
-
// Create a custom test that mocks the plan manager to report no plan file
|
|
134
|
-
const originalPlanManager = (server as unknown).components?.context
|
|
135
|
-
?.planManager;
|
|
136
|
-
|
|
137
|
-
try {
|
|
138
|
-
// Replace plan manager with a mock that reports no plan file
|
|
139
|
-
if ((server as unknown).components?.context) {
|
|
140
|
-
(server as unknown).components.context.planManager = {
|
|
141
|
-
getPlanFileInfo: async () => ({
|
|
142
|
-
exists: false,
|
|
143
|
-
path: '/fake/path.md',
|
|
144
|
-
}),
|
|
145
|
-
ensurePlanFile: async () => {},
|
|
146
|
-
setStateMachine: () => {},
|
|
147
|
-
};
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
const result = await server.handleResumeWorkflow({});
|
|
151
|
-
|
|
152
|
-
const planStatus = result.plan_status;
|
|
153
|
-
expect(planStatus.exists).toBe(false);
|
|
154
|
-
expect(planStatus.analysis).toBeNull();
|
|
155
|
-
} finally {
|
|
156
|
-
// Restore original plan manager
|
|
157
|
-
if ((server as unknown).components?.context && originalPlanManager) {
|
|
158
|
-
(server as unknown).components.context.planManager =
|
|
159
|
-
originalPlanManager;
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
});
|
|
163
|
-
|
|
164
|
-
it('should provide different recommendations based on phase', async () => {
|
|
165
|
-
// This test would ideally set up different phases and verify recommendations
|
|
166
|
-
// For now, just verify the structure is correct
|
|
167
|
-
const result = await server.handleResumeWorkflow({});
|
|
168
|
-
|
|
169
|
-
const recommendations = result.recommendations;
|
|
170
|
-
expect(recommendations.immediate_actions).toBeInstanceOf(Array);
|
|
171
|
-
expect(recommendations.phase_guidance).toBeTypeOf('string');
|
|
172
|
-
expect(recommendations.potential_issues).toBeInstanceOf(Array);
|
|
173
|
-
});
|
|
174
|
-
|
|
175
|
-
it('should handle errors gracefully', async () => {
|
|
176
|
-
// This test ensures the method doesn't throw for any reasonable input
|
|
177
|
-
await expect(server.handleResumeWorkflow({})).resolves.toBeDefined();
|
|
178
|
-
await expect(
|
|
179
|
-
server.handleResumeWorkflow({ include_system_prompt: true })
|
|
180
|
-
).resolves.toBeDefined();
|
|
181
|
-
await expect(
|
|
182
|
-
server.handleResumeWorkflow({ include_system_prompt: false })
|
|
183
|
-
).resolves.toBeDefined();
|
|
184
|
-
});
|
|
185
|
-
|
|
186
|
-
it('should include metadata in response', async () => {
|
|
187
|
-
const result = await server.handleResumeWorkflow({});
|
|
188
|
-
|
|
189
|
-
expect(result.generated_at).toBeTypeOf('string');
|
|
190
|
-
expect(result.tool_version).toBeTypeOf('string');
|
|
191
|
-
expect(new Date(result.generated_at)).toBeInstanceOf(Date);
|
|
192
|
-
});
|
|
193
|
-
});
|
|
@@ -1,99 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Test plugin registration in server-config
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import { describe, it, expect, beforeEach, vi, afterEach } from 'vitest';
|
|
6
|
-
import { initializeServerComponents } from '../../src/server-config.js';
|
|
7
|
-
import { mkdtemp, rm } from 'node:fs/promises';
|
|
8
|
-
import { tmpdir } from 'node:os';
|
|
9
|
-
import { join } from 'node:path';
|
|
10
|
-
import { execSync } from 'node:child_process';
|
|
11
|
-
|
|
12
|
-
// Mock child_process to control bd command availability
|
|
13
|
-
vi.mock('node:child_process', () => ({
|
|
14
|
-
execSync: vi.fn(),
|
|
15
|
-
}));
|
|
16
|
-
|
|
17
|
-
describe('Server Config Plugin Registration', () => {
|
|
18
|
-
let tempDir: string;
|
|
19
|
-
|
|
20
|
-
beforeEach(async () => {
|
|
21
|
-
vi.resetAllMocks(); // Reset mock implementations, not just call history
|
|
22
|
-
tempDir = await mkdtemp(join(tmpdir(), 'server-config-test-'));
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
afterEach(async () => {
|
|
26
|
-
vi.resetAllMocks();
|
|
27
|
-
try {
|
|
28
|
-
await rm(tempDir, { recursive: true, force: true });
|
|
29
|
-
} catch {
|
|
30
|
-
// Ignore cleanup errors
|
|
31
|
-
}
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
it('should register BeadsPlugin when TASK_BACKEND is beads and bd is available', async () => {
|
|
35
|
-
vi.stubEnv('TASK_BACKEND', 'beads');
|
|
36
|
-
|
|
37
|
-
// Mock bd --version to return success
|
|
38
|
-
vi.mocked(execSync).mockReturnValue('beads v1.0.0\n');
|
|
39
|
-
|
|
40
|
-
const components = await initializeServerComponents({
|
|
41
|
-
projectPath: tempDir,
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
expect(components.context.pluginRegistry).toBeDefined();
|
|
45
|
-
|
|
46
|
-
// Check that BeadsPlugin was registered
|
|
47
|
-
const pluginNames = components.context.pluginRegistry.getPluginNames();
|
|
48
|
-
expect(pluginNames).toContain('BeadsPlugin');
|
|
49
|
-
|
|
50
|
-
// Check that it's enabled
|
|
51
|
-
const enabledPlugins =
|
|
52
|
-
components.context.pluginRegistry.getEnabledPlugins();
|
|
53
|
-
expect(enabledPlugins).toHaveLength(1);
|
|
54
|
-
expect(enabledPlugins[0].getName()).toBe('BeadsPlugin');
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
it('should not register BeadsPlugin when TASK_BACKEND is markdown', async () => {
|
|
58
|
-
// Explicitly set markdown to disable beads
|
|
59
|
-
vi.stubEnv('TASK_BACKEND', 'markdown');
|
|
60
|
-
|
|
61
|
-
const components = await initializeServerComponents({
|
|
62
|
-
projectPath: tempDir,
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
expect(components.context.pluginRegistry).toBeDefined();
|
|
66
|
-
|
|
67
|
-
// Check that no plugins are registered
|
|
68
|
-
const pluginNames = components.context.pluginRegistry.getPluginNames();
|
|
69
|
-
expect(pluginNames).toHaveLength(0);
|
|
70
|
-
|
|
71
|
-
// Check that no plugins are enabled
|
|
72
|
-
const enabledPlugins =
|
|
73
|
-
components.context.pluginRegistry.getEnabledPlugins();
|
|
74
|
-
expect(enabledPlugins).toHaveLength(0);
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
it('should not register BeadsPlugin when bd is not available', async () => {
|
|
78
|
-
// Explicitly clear TASK_BACKEND - triggers auto-detection
|
|
79
|
-
delete process.env.TASK_BACKEND;
|
|
80
|
-
|
|
81
|
-
// Mock bd --version to throw (command not found)
|
|
82
|
-
vi.mocked(execSync).mockImplementation(() => {
|
|
83
|
-
throw new Error('command not found: bd');
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
const components = await initializeServerComponents({
|
|
87
|
-
projectPath: tempDir,
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
expect(components.context.pluginRegistry).toBeDefined();
|
|
91
|
-
expect(components.context.pluginRegistry.getPluginNames()).toHaveLength(0);
|
|
92
|
-
expect(components.context.pluginRegistry.getEnabledPlugins()).toHaveLength(
|
|
93
|
-
0
|
|
94
|
-
);
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
// Note: Auto-detection tests are covered in E2E tests (beads-plugin-integration.test.ts)
|
|
98
|
-
// because mocking child_process across package boundaries requires E2E-style server setup
|
|
99
|
-
});
|