@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.
Files changed (77) hide show
  1. package/package.json +6 -2
  2. package/skill/SKILL.md +23 -0
  3. package/.prettierignore +0 -2
  4. package/.turbo/turbo-build.log +0 -4
  5. package/.vibe/conversation-state.sqlite +0 -0
  6. package/src/components/beads/beads-instruction-generator.ts +0 -230
  7. package/src/components/beads/beads-plan-manager.ts +0 -333
  8. package/src/components/beads/beads-task-backend-client.ts +0 -229
  9. package/src/index.ts +0 -93
  10. package/src/notification-service.ts +0 -23
  11. package/src/plugin-system/beads-plugin.ts +0 -649
  12. package/src/plugin-system/commit-plugin.ts +0 -252
  13. package/src/plugin-system/index.ts +0 -20
  14. package/src/plugin-system/plugin-interfaces.ts +0 -153
  15. package/src/plugin-system/plugin-registry.ts +0 -190
  16. package/src/resource-handlers/conversation-state.ts +0 -55
  17. package/src/resource-handlers/development-plan.ts +0 -48
  18. package/src/resource-handlers/index.ts +0 -73
  19. package/src/resource-handlers/system-prompt.ts +0 -55
  20. package/src/resource-handlers/workflow-resource.ts +0 -132
  21. package/src/response-renderer.ts +0 -116
  22. package/src/server-config.ts +0 -760
  23. package/src/server-helpers.ts +0 -245
  24. package/src/server-implementation.ts +0 -277
  25. package/src/server.ts +0 -9
  26. package/src/tool-handlers/base-tool-handler.ts +0 -151
  27. package/src/tool-handlers/conduct-review.ts +0 -190
  28. package/src/tool-handlers/get-tool-info.ts +0 -273
  29. package/src/tool-handlers/index.ts +0 -115
  30. package/src/tool-handlers/list-workflows.ts +0 -78
  31. package/src/tool-handlers/no-idea.ts +0 -47
  32. package/src/tool-handlers/proceed-to-phase.ts +0 -296
  33. package/src/tool-handlers/reset-development.ts +0 -90
  34. package/src/tool-handlers/resume-workflow.ts +0 -378
  35. package/src/tool-handlers/setup-project-docs.ts +0 -232
  36. package/src/tool-handlers/start-development.ts +0 -746
  37. package/src/tool-handlers/whats-next.ts +0 -246
  38. package/src/types.ts +0 -135
  39. package/src/version-info.ts +0 -213
  40. package/test/e2e/beads-plugin-integration.test.ts +0 -1623
  41. package/test/e2e/commit-plugin-integration.test.ts +0 -222
  42. package/test/e2e/core-functionality.test.ts +0 -167
  43. package/test/e2e/git-branch-detection.test.ts +0 -351
  44. package/test/e2e/mcp-contract.test.ts +0 -509
  45. package/test/e2e/plan-management.test.ts +0 -334
  46. package/test/e2e/plugin-system-integration.test.ts +0 -1410
  47. package/test/e2e/state-management.test.ts +0 -387
  48. package/test/e2e/workflow-integration.test.ts +0 -498
  49. package/test/unit/beads-instruction-generator.test.ts +0 -979
  50. package/test/unit/beads-phase-task-id-integration.test.ts +0 -535
  51. package/test/unit/beads-plugin-behavioral.test.ts +0 -545
  52. package/test/unit/beads-plugin.test.ts +0 -117
  53. package/test/unit/commit-plugin.test.ts +0 -196
  54. package/test/unit/conduct-review.test.ts +0 -151
  55. package/test/unit/conversation-not-found-error.test.ts +0 -120
  56. package/test/unit/plugin-error-handling.test.ts +0 -240
  57. package/test/unit/proceed-to-phase-plugin-integration.test.ts +0 -150
  58. package/test/unit/reset-functionality.test.ts +0 -72
  59. package/test/unit/resume-workflow.test.ts +0 -193
  60. package/test/unit/server-config-plugin-registry.test.ts +0 -99
  61. package/test/unit/server-tools.test.ts +0 -310
  62. package/test/unit/setup-project-docs-handler.test.ts +0 -268
  63. package/test/unit/start-development-artifact-detection.test.ts +0 -387
  64. package/test/unit/start-development-gitignore.test.ts +0 -178
  65. package/test/unit/start-development-goal-extraction.test.ts +0 -226
  66. package/test/unit/system-prompt-resource.test.ts +0 -102
  67. package/test/unit/tool-handlers/no-idea.test.ts +0 -40
  68. package/test/utils/e2e-test-setup.ts +0 -451
  69. package/test/utils/run-server-in-dir.sh +0 -27
  70. package/test/utils/temp-files.ts +0 -320
  71. package/test/utils/test-access.ts +0 -79
  72. package/test/utils/test-helpers.ts +0 -288
  73. package/test/utils/test-setup.ts +0 -77
  74. package/tsconfig.build.json +0 -10
  75. package/tsconfig.build.tsbuildinfo +0 -1
  76. package/tsconfig.json +0 -12
  77. package/vitest.config.ts +0 -19
@@ -1,252 +0,0 @@
1
- /**
2
- * CommitPlugin Implementation
3
- *
4
- * Plugin that handles automatic git commits based on COMMIT_BEHAVIOR environment variable.
5
- * Supports step, phase, and end commit modes with configurable message templates.
6
- */
7
-
8
- import type {
9
- IPlugin,
10
- PluginHooks,
11
- PluginHookContext,
12
- StartDevelopmentArgs,
13
- StartDevelopmentResult,
14
- } from './plugin-interfaces.js';
15
- import { GitManager, createLogger } from '@codemcp/workflows-core';
16
-
17
- const logger = createLogger('CommitPlugin');
18
-
19
- /**
20
- * CommitPlugin class implementing the IPlugin interface
21
- *
22
- * Activation: Only when process.env.COMMIT_BEHAVIOR is set to valid value
23
- * Priority: Sequence 50 (before BeadsPlugin at 100)
24
- */
25
- export class CommitPlugin implements IPlugin {
26
- private projectPath: string;
27
- private initialCommitHash?: string;
28
-
29
- constructor(options: { projectPath: string }) {
30
- this.projectPath = options.projectPath;
31
- logger.debug('CommitPlugin initialized', { projectPath: this.projectPath });
32
- }
33
-
34
- getName(): string {
35
- return 'CommitPlugin';
36
- }
37
-
38
- getSequence(): number {
39
- return 50; // Before BeadsPlugin (100)
40
- }
41
-
42
- isEnabled(): boolean {
43
- const behavior = process.env.COMMIT_BEHAVIOR;
44
- const enabled =
45
- behavior && ['step', 'phase', 'end', 'none'].includes(behavior);
46
- logger.debug('CommitPlugin enablement check', {
47
- COMMIT_BEHAVIOR: behavior,
48
- enabled: !!enabled,
49
- });
50
- return !!enabled;
51
- }
52
-
53
- getHooks(): PluginHooks {
54
- return {
55
- afterStartDevelopment: this.handleAfterStartDevelopment.bind(this),
56
- beforePhaseTransition: this.handleBeforePhaseTransition.bind(this),
57
- afterPlanFileCreated: this.handleAfterPlanFileCreated.bind(this),
58
- };
59
- }
60
-
61
- /**
62
- * Handle afterStartDevelopment hook
63
- * Store initial commit hash for potential squashing later
64
- */
65
- private async handleAfterStartDevelopment(
66
- context: PluginHookContext,
67
- _args: StartDevelopmentArgs,
68
- _result: StartDevelopmentResult
69
- ): Promise<void> {
70
- logger.info('CommitPlugin: Setting up commit behavior', {
71
- conversationId: context.conversationId,
72
- behavior: process.env.COMMIT_BEHAVIOR,
73
- projectPath: context.projectPath,
74
- });
75
-
76
- try {
77
- if (GitManager.isGitRepository(context.projectPath)) {
78
- this.initialCommitHash =
79
- GitManager.getCurrentCommitHash(context.projectPath) || undefined;
80
- logger.debug('CommitPlugin: Stored initial commit hash', {
81
- conversationId: context.conversationId,
82
- initialCommitHash: this.initialCommitHash,
83
- });
84
- }
85
- } catch (error) {
86
- logger.warn('CommitPlugin: Failed to get initial commit hash', {
87
- error: error instanceof Error ? error.message : String(error),
88
- conversationId: context.conversationId,
89
- });
90
- }
91
- }
92
-
93
- /**
94
- * Handle beforePhaseTransition hook
95
- * Create WIP commits for phase and step modes
96
- */
97
- private async handleBeforePhaseTransition(
98
- context: PluginHookContext,
99
- currentPhase: string,
100
- targetPhase: string
101
- ): Promise<void> {
102
- const behavior = process.env.COMMIT_BEHAVIOR;
103
-
104
- if (behavior !== 'phase' && behavior !== 'step') {
105
- return; // Only commit on phase transitions for these modes
106
- }
107
-
108
- logger.info('CommitPlugin: Creating WIP commit before phase transition', {
109
- conversationId: context.conversationId,
110
- currentPhase,
111
- targetPhase,
112
- behavior,
113
- });
114
-
115
- try {
116
- if (!GitManager.isGitRepository(context.projectPath)) {
117
- logger.debug('CommitPlugin: Not a git repository, skipping commit');
118
- return;
119
- }
120
-
121
- if (!GitManager.hasUncommittedChanges(context.projectPath)) {
122
- logger.debug('CommitPlugin: No uncommitted changes, skipping commit');
123
- return;
124
- }
125
-
126
- const message = `WIP: transition to ${targetPhase}`;
127
- const success = GitManager.createCommit(message, context.projectPath);
128
-
129
- if (success) {
130
- logger.info('CommitPlugin: Created WIP commit successfully', {
131
- conversationId: context.conversationId,
132
- message,
133
- });
134
- } else {
135
- logger.warn('CommitPlugin: Failed to create WIP commit', {
136
- conversationId: context.conversationId,
137
- message,
138
- });
139
- }
140
- } catch (error) {
141
- logger.warn('CommitPlugin: Error during phase transition commit', {
142
- error: error instanceof Error ? error.message : String(error),
143
- conversationId: context.conversationId,
144
- });
145
- }
146
- }
147
-
148
- /**
149
- * Handle afterPlanFileCreated hook
150
- * Add final commit task for end mode or step/phase modes with squashing
151
- */
152
- private async handleAfterPlanFileCreated(
153
- context: PluginHookContext,
154
- planFilePath: string,
155
- content: string
156
- ): Promise<string> {
157
- const behavior = process.env.COMMIT_BEHAVIOR;
158
-
159
- if (!behavior || behavior === 'none') {
160
- return content; // No commit behavior
161
- }
162
-
163
- logger.debug('CommitPlugin: Adding final commit task to plan file', {
164
- conversationId: context.conversationId,
165
- behavior,
166
- planFilePath,
167
- });
168
-
169
- try {
170
- // Find the final phase (usually "Commit" or last phase)
171
- const lines = content.split('\n');
172
- let finalPhaseIndex = -1;
173
-
174
- // Look for "## Commit" section first
175
- for (let i = 0; i < lines.length; i++) {
176
- if (lines[i]?.trim() === '## Commit') {
177
- finalPhaseIndex = i;
178
- break;
179
- }
180
- }
181
-
182
- // If no Commit section, find the last ## section
183
- if (finalPhaseIndex === -1) {
184
- for (let i = lines.length - 1; i >= 0; i--) {
185
- const line = lines[i];
186
- if (
187
- line?.startsWith('## ') &&
188
- !line.includes('Notes') &&
189
- !line.includes('Key Decisions')
190
- ) {
191
- finalPhaseIndex = i;
192
- break;
193
- }
194
- }
195
- }
196
-
197
- if (finalPhaseIndex === -1) {
198
- logger.warn(
199
- 'CommitPlugin: Could not find final phase to add commit task'
200
- );
201
- return content;
202
- }
203
-
204
- // Generate commit task based on behavior
205
- let commitTask: string;
206
- const defaultMessage =
207
- process.env.COMMIT_MESSAGE_TEMPLATE ||
208
- 'Create a conventional commit. In the message, first summarize the intentions and key decisions from the development plan. Then, add a brief summary of the key changes and their side effects and dependencies';
209
-
210
- if (behavior === 'end') {
211
- // End mode: simple final commit
212
- commitTask = `- [ ] ${defaultMessage}`;
213
- } else {
214
- // Step/phase mode: squash WIP commits with instructions
215
- const squashInstructions = `Squash WIP commits: \`git reset --soft <first commit of this branch>. Then, ${defaultMessage}`;
216
- commitTask = `- [ ] ${squashInstructions}`;
217
- }
218
-
219
- // Find the Tasks section in the final phase and add the commit task
220
- let tasksIndex = -1;
221
- for (let i = finalPhaseIndex; i < lines.length; i++) {
222
- if (lines[i]?.trim() === '### Tasks') {
223
- tasksIndex = i;
224
- break;
225
- }
226
- }
227
-
228
- if (tasksIndex !== -1) {
229
- // Insert after ### Tasks line
230
- lines.splice(tasksIndex + 1, 0, commitTask);
231
- } else {
232
- // Add Tasks section if it doesn't exist
233
- lines.splice(finalPhaseIndex + 1, 0, '', '### Tasks', commitTask);
234
- }
235
-
236
- const updatedContent = lines.join('\n');
237
- logger.info('CommitPlugin: Added final commit task to plan file', {
238
- conversationId: context.conversationId,
239
- behavior,
240
- commitTask,
241
- });
242
-
243
- return updatedContent;
244
- } catch (error) {
245
- logger.warn('CommitPlugin: Failed to add commit task to plan file', {
246
- error: error instanceof Error ? error.message : String(error),
247
- conversationId: context.conversationId,
248
- });
249
- return content; // Return original content on error
250
- }
251
- }
252
- }
@@ -1,20 +0,0 @@
1
- /**
2
- * Plugin system exports
3
- *
4
- * This module provides the core plugin system for extending responsible-vibe-mcp
5
- * functionality without if-statements in the core application.
6
- */
7
-
8
- // Core plugin interfaces
9
- export type {
10
- IPlugin,
11
- IPluginRegistry,
12
- PluginHooks,
13
- PluginHookContext,
14
- StartDevelopmentArgs,
15
- StartDevelopmentResult,
16
- GeneratedInstructions,
17
- } from './plugin-interfaces.js';
18
-
19
- // Plugin registry implementation
20
- export { PluginRegistry } from './plugin-registry.js';
@@ -1,153 +0,0 @@
1
- /**
2
- * Plugin system interfaces for extending the responsible-vibe-mcp server
3
- *
4
- * Core Principle: Plugins receive only read-only context data and cannot
5
- * directly manipulate core server components. They extend behavior through
6
- * semantic lifecycle hooks only.
7
- */
8
-
9
- import type { YamlState } from '@codemcp/workflows-core';
10
-
11
- /**
12
- * Plugin interface - all plugins must implement this
13
- */
14
- export interface IPlugin {
15
- /** Unique plugin name */
16
- getName(): string;
17
-
18
- /** Execution sequence (lower numbers execute first) */
19
- getSequence(): number;
20
-
21
- /** Whether plugin is enabled (typically based on environment) */
22
- isEnabled(): boolean;
23
-
24
- /** Lifecycle hooks this plugin provides */
25
- getHooks(): PluginHooks;
26
- }
27
-
28
- /**
29
- * Lifecycle hooks that plugins can implement
30
- * All hooks receive standardized PluginHookContext as first parameter
31
- */
32
- export interface PluginHooks {
33
- /** Called before development workflow starts */
34
- beforeStartDevelopment?: (
35
- context: PluginHookContext,
36
- args: StartDevelopmentArgs
37
- ) => Promise<void>;
38
-
39
- /** Called after development workflow has started */
40
- afterStartDevelopment?: (
41
- context: PluginHookContext,
42
- args: StartDevelopmentArgs,
43
- result: StartDevelopmentResult
44
- ) => Promise<void>;
45
-
46
- /** Called after plan file is created - can modify content */
47
- afterPlanFileCreated?: (
48
- context: PluginHookContext,
49
- planFilePath: string,
50
- content: string
51
- ) => Promise<string>;
52
-
53
- /** Called before phase transition (can block by throwing) */
54
- beforePhaseTransition?: (
55
- context: PluginHookContext,
56
- currentPhase: string,
57
- targetPhase: string
58
- ) => Promise<void>;
59
-
60
- /** Called after instructions are generated - can modify them */
61
- afterInstructionsGenerated?: (
62
- context: PluginHookContext,
63
- instructions: GeneratedInstructions
64
- ) => Promise<GeneratedInstructions>;
65
- }
66
-
67
- /**
68
- * Standardized context provided to all plugin hooks
69
- * Contains ONLY read-only data - no server components
70
- */
71
- export interface PluginHookContext {
72
- /** Current conversation ID */
73
- conversationId: string;
74
-
75
- /** Path to the plan file */
76
- planFilePath: string;
77
-
78
- /** Current development phase */
79
- currentPhase: string;
80
-
81
- /** Active workflow name */
82
- workflow: string;
83
-
84
- /** Project directory path */
85
- projectPath: string;
86
-
87
- /** Git branch name */
88
- gitBranch: string;
89
-
90
- /** Target phase (only available in phase transitions) */
91
- targetPhase?: string;
92
-
93
- /** Workflow state machine definition (read-only) - available in afterStartDevelopment */
94
- stateMachine?: {
95
- readonly name: string;
96
- readonly description: string;
97
- readonly initial_state: string;
98
- readonly states: Record<string, YamlState>;
99
- };
100
-
101
- // EXPLICITLY EXCLUDED: No access to core server components like:
102
- // - conversationManager (could manipulate conversations)
103
- // - transitionEngine (could force transitions)
104
- // - planManager (could bypass hook system)
105
- // - instructionGenerator (could generate instructions outside flow)
106
- }
107
-
108
- /**
109
- * Plugin registry interface for managing and executing plugins
110
- */
111
- export interface IPluginRegistry {
112
- /** Register a plugin */
113
- registerPlugin(plugin: IPlugin): void;
114
-
115
- /** Get all enabled plugins sorted by sequence */
116
- getEnabledPlugins(): IPlugin[];
117
-
118
- /** Execute a specific hook on all plugins that implement it */
119
- executeHook<T extends keyof PluginHooks>(
120
- hookName: T,
121
- ...args: Parameters<NonNullable<PluginHooks[T]>>
122
- ): Promise<unknown>;
123
-
124
- /** Check if any plugin has a specific hook */
125
- hasHook(hookName: keyof PluginHooks): boolean;
126
-
127
- /** Get names of all registered plugins */
128
- getPluginNames(): string[];
129
-
130
- /** Clear all plugins (mainly for testing) */
131
- clear(): void;
132
- }
133
-
134
- // Supporting interfaces for hook parameters
135
-
136
- export interface StartDevelopmentArgs {
137
- workflow: string;
138
- require_reviews?: boolean;
139
- project_path?: string;
140
- }
141
-
142
- export interface StartDevelopmentResult {
143
- conversationId: string;
144
- planFilePath: string;
145
- phase: string;
146
- workflow: string;
147
- }
148
-
149
- export interface GeneratedInstructions {
150
- instructions: string;
151
- planFilePath: string;
152
- phase: string;
153
- }
@@ -1,190 +0,0 @@
1
- /**
2
- * Core PluginRegistry implementation for managing plugins and executing lifecycle hooks
3
- */
4
-
5
- import type {
6
- IPlugin,
7
- IPluginRegistry,
8
- PluginHooks,
9
- } from './plugin-interfaces.js';
10
-
11
- export class PluginRegistry implements IPluginRegistry {
12
- private plugins: Map<string, IPlugin> = new Map();
13
-
14
- /**
15
- * Register a plugin if it's enabled
16
- */
17
- registerPlugin(plugin: IPlugin): void {
18
- if (!plugin.isEnabled()) {
19
- return;
20
- }
21
-
22
- const name = plugin.getName();
23
- if (this.plugins.has(name)) {
24
- throw new Error(`Plugin with name '${name}' is already registered`);
25
- }
26
-
27
- this.plugins.set(name, plugin);
28
- }
29
-
30
- /**
31
- * Get all enabled plugins sorted by execution sequence
32
- */
33
- getEnabledPlugins(): IPlugin[] {
34
- return Array.from(this.plugins.values())
35
- .filter(plugin => plugin.isEnabled())
36
- .sort((a, b) => a.getSequence() - b.getSequence());
37
- }
38
-
39
- /**
40
- * Execute a specific hook on all plugins that implement it
41
- * Plugins are executed in sequence order
42
- *
43
- * Error Handling Strategy:
44
- * - Validation hooks (beforePhaseTransition): Always re-throw to block invalid transitions
45
- * - Critical startup hooks: Re-throw to fail fast and show critical errors
46
- * - Non-critical hooks: Log error and continue execution to enable graceful degradation
47
- * - Multiple plugins: If one plugin fails on non-critical hook, continue with next plugin
48
- */
49
- async executeHook<T extends keyof PluginHooks>(
50
- hookName: T,
51
- ...args: Parameters<NonNullable<PluginHooks[T]>>
52
- ): Promise<unknown> {
53
- const enabledPlugins = this.getEnabledPlugins();
54
- let result: unknown = undefined;
55
-
56
- for (const plugin of enabledPlugins) {
57
- const hooks = plugin.getHooks();
58
- const hook = hooks[hookName];
59
-
60
- if (hook) {
61
- try {
62
- // Type-safe hook execution using dispatch pattern
63
- result = await this.executeTypedHook(hookName, hook, args, result);
64
- } catch (error) {
65
- const pluginName = plugin.getName();
66
- const errorMessage =
67
- error instanceof Error ? error.message : String(error);
68
-
69
- // Validation hooks (beforePhaseTransition) should ALWAYS re-throw
70
- // These are intentional blocking errors, not graceful degradation
71
- if (hookName === 'beforePhaseTransition') {
72
- console.error(
73
- `Plugin '${pluginName}' validation failed for hook '${hookName}':`,
74
- errorMessage
75
- );
76
- throw error;
77
- }
78
-
79
- // For non-critical hooks, log the error but continue execution
80
- // This enables graceful degradation: the app continues even if a plugin hook fails
81
- console.warn(
82
- `Plugin '${pluginName}' hook '${hookName}' failed with non-critical error:`,
83
- errorMessage
84
- );
85
- console.warn(
86
- `Continuing with remaining plugins for hook '${hookName}' (graceful degradation enabled)`
87
- );
88
-
89
- // Continue to next plugin for non-critical errors
90
- // This allows multiple plugins to execute even if one fails
91
- }
92
- }
93
- }
94
-
95
- return result;
96
- }
97
-
98
- /**
99
- * Type-safe hook execution dispatcher
100
- * Handles the differences in hook signatures without type coercion
101
- */
102
- private async executeTypedHook<T extends keyof PluginHooks>(
103
- hookName: T,
104
- hook: NonNullable<PluginHooks[T]>,
105
- args: Parameters<NonNullable<PluginHooks[T]>>,
106
- previousResult: unknown
107
- ): Promise<unknown> {
108
- if (hookName === 'afterPlanFileCreated') {
109
- // Content-chaining hook: replaces the content parameter with previous result
110
- const typedHook = hook as NonNullable<
111
- PluginHooks['afterPlanFileCreated']
112
- >;
113
- const [context, planFilePath, content] = args as Parameters<
114
- typeof typedHook
115
- >;
116
- const contentToUse = ((previousResult as string | undefined) ??
117
- content) as string;
118
- return typedHook(context, planFilePath, contentToUse);
119
- }
120
-
121
- if (hookName === 'afterInstructionsGenerated') {
122
- // Content-chaining hook: replaces the instructions parameter with previous result
123
- const typedHook = hook as NonNullable<
124
- PluginHooks['afterInstructionsGenerated']
125
- >;
126
- const [context, instructions] = args as Parameters<typeof typedHook>;
127
- const instructionsToUse = (
128
- previousResult !== undefined
129
- ? (previousResult as Parameters<typeof typedHook>[1])
130
- : instructions
131
- ) as Parameters<typeof typedHook>[1];
132
- return typedHook(context, instructionsToUse);
133
- }
134
-
135
- if (hookName === 'beforeStartDevelopment') {
136
- const typedHook = hook as NonNullable<
137
- PluginHooks['beforeStartDevelopment']
138
- >;
139
- const [context, startArgs] = args as Parameters<typeof typedHook>;
140
- return typedHook(context, startArgs);
141
- }
142
-
143
- if (hookName === 'afterStartDevelopment') {
144
- const typedHook = hook as NonNullable<
145
- PluginHooks['afterStartDevelopment']
146
- >;
147
- const [context, startArgs, result] = args as Parameters<typeof typedHook>;
148
- return typedHook(context, startArgs, result);
149
- }
150
-
151
- if (hookName === 'beforePhaseTransition') {
152
- const typedHook = hook as NonNullable<
153
- PluginHooks['beforePhaseTransition']
154
- >;
155
- const [context, currentPhase, targetPhase] = args as Parameters<
156
- typeof typedHook
157
- >;
158
- return typedHook(context, currentPhase, targetPhase);
159
- }
160
-
161
- // This should never be reached due to type system, but ensures exhaustiveness
162
- const exhaustiveCheck: never = hookName;
163
- throw new Error(`Unknown hook: ${exhaustiveCheck}`);
164
- }
165
-
166
- /**
167
- * Check if any enabled plugin implements a specific hook
168
- */
169
- hasHook(hookName: keyof PluginHooks): boolean {
170
- const enabledPlugins = this.getEnabledPlugins();
171
- return enabledPlugins.some(plugin => {
172
- const hooks = plugin.getHooks();
173
- return hooks[hookName] !== undefined;
174
- });
175
- }
176
-
177
- /**
178
- * Get names of all registered plugins (for debugging)
179
- */
180
- getPluginNames(): string[] {
181
- return Array.from(this.plugins.keys());
182
- }
183
-
184
- /**
185
- * Clear all plugins (mainly for testing)
186
- */
187
- clear(): void {
188
- this.plugins.clear();
189
- }
190
- }
@@ -1,55 +0,0 @@
1
- /**
2
- * Conversation State Resource Handler
3
- *
4
- * Handles the conversation-state resource which provides access to current
5
- * conversation state and phase information including conversation ID, project context,
6
- * current development phase, and plan file location.
7
- */
8
-
9
- import { createLogger } from '@codemcp/workflows-core';
10
- import {
11
- ResourceHandler,
12
- ServerContext,
13
- HandlerResult,
14
- ResourceContent,
15
- } from '../types.js';
16
- import { safeExecute } from '../server-helpers.js';
17
-
18
- const logger = createLogger('ConversationStateResourceHandler');
19
-
20
- /**
21
- * Conversation State resource handler implementation
22
- */
23
- export class ConversationStateResourceHandler implements ResourceHandler {
24
- async handle(
25
- uri: URL,
26
- context: ServerContext
27
- ): Promise<HandlerResult<ResourceContent>> {
28
- logger.debug('Processing conversation state resource request', {
29
- uri: uri.href,
30
- });
31
-
32
- return safeExecute(async () => {
33
- // Get conversation context
34
- const conversationContext =
35
- await context.conversationManager.getConversationContext();
36
-
37
- // Build state information
38
- const stateInfo = {
39
- conversationId: conversationContext.conversationId,
40
- projectPath: conversationContext.projectPath,
41
- gitBranch: conversationContext.gitBranch,
42
- currentPhase: conversationContext.currentPhase,
43
- planFilePath: conversationContext.planFilePath,
44
- timestamp: new Date().toISOString(),
45
- description: 'Current state of the development workflow conversation',
46
- };
47
-
48
- return {
49
- uri: uri.href,
50
- text: JSON.stringify(stateInfo, null, 2),
51
- mimeType: 'application/json',
52
- };
53
- }, 'Failed to retrieve conversation state resource');
54
- }
55
- }