@codemcp/workflows 5.0.1 → 5.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/SKILL.md +23 -0
- package/package.json +6 -2
- 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,246 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* WhatsNext Tool Handler
|
|
3
|
-
*
|
|
4
|
-
* Handles the whats_next tool which analyzes conversation context and
|
|
5
|
-
* determines the next development phase with specific instructions for the LLM.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import { ConversationRequiredToolHandler } from './base-tool-handler.js';
|
|
9
|
-
import type { ConversationContext } from '@codemcp/workflows-core';
|
|
10
|
-
// TaskBackendManager and BeadsIntegration functionality now handled by injected components
|
|
11
|
-
import { ServerContext } from '../types.js';
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Arguments for the whats_next tool
|
|
15
|
-
*/
|
|
16
|
-
export interface WhatsNextArgs {
|
|
17
|
-
context?: string;
|
|
18
|
-
user_input?: string;
|
|
19
|
-
conversation_summary?: string;
|
|
20
|
-
recent_messages?: Array<{
|
|
21
|
-
role: 'user' | 'assistant';
|
|
22
|
-
content: string;
|
|
23
|
-
}>;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* Response from the whats_next tool
|
|
28
|
-
*/
|
|
29
|
-
export interface WhatsNextResult {
|
|
30
|
-
phase: string;
|
|
31
|
-
instructions: string;
|
|
32
|
-
plan_file_path: string;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* WhatsNext tool handler implementation
|
|
37
|
-
*/
|
|
38
|
-
export class WhatsNextHandler extends ConversationRequiredToolHandler<
|
|
39
|
-
WhatsNextArgs,
|
|
40
|
-
WhatsNextResult
|
|
41
|
-
> {
|
|
42
|
-
protected override async executeHandler(
|
|
43
|
-
args: WhatsNextArgs,
|
|
44
|
-
context: ServerContext
|
|
45
|
-
): Promise<WhatsNextResult> {
|
|
46
|
-
let conversationContext;
|
|
47
|
-
|
|
48
|
-
try {
|
|
49
|
-
conversationContext = await this.getConversationContext(context);
|
|
50
|
-
} catch (_error) {
|
|
51
|
-
// Use standard CONVERSATION_NOT_FOUND error
|
|
52
|
-
throw new Error('CONVERSATION_NOT_FOUND');
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
return this.executeWithConversation(args, context, conversationContext);
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
protected async executeWithConversation(
|
|
59
|
-
args: WhatsNextArgs,
|
|
60
|
-
context: ServerContext,
|
|
61
|
-
conversationContext: ConversationContext
|
|
62
|
-
): Promise<WhatsNextResult> {
|
|
63
|
-
const {
|
|
64
|
-
context: requestContext = '',
|
|
65
|
-
user_input = '',
|
|
66
|
-
conversation_summary = '',
|
|
67
|
-
recent_messages = [],
|
|
68
|
-
} = args;
|
|
69
|
-
|
|
70
|
-
const conversationId = conversationContext.conversationId;
|
|
71
|
-
const currentPhase = conversationContext.currentPhase;
|
|
72
|
-
|
|
73
|
-
this.logger.debug('Processing whats_next request', {
|
|
74
|
-
conversationId,
|
|
75
|
-
currentPhase,
|
|
76
|
-
hasContext: !!requestContext,
|
|
77
|
-
hasUserInput: !!user_input,
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
// Ensure state machine is loaded for this project
|
|
81
|
-
this.ensureStateMachineForProject(
|
|
82
|
-
context,
|
|
83
|
-
conversationContext.projectPath,
|
|
84
|
-
conversationContext.workflowName
|
|
85
|
-
);
|
|
86
|
-
|
|
87
|
-
// Ensure plan file exists
|
|
88
|
-
await context.planManager.ensurePlanFile(
|
|
89
|
-
conversationContext.planFilePath,
|
|
90
|
-
conversationContext.projectPath,
|
|
91
|
-
conversationContext.gitBranch
|
|
92
|
-
);
|
|
93
|
-
|
|
94
|
-
// Analyze phase transition
|
|
95
|
-
const transitionResult =
|
|
96
|
-
await context.transitionEngine.analyzePhaseTransition({
|
|
97
|
-
currentPhase,
|
|
98
|
-
projectPath: conversationContext.projectPath,
|
|
99
|
-
userInput: user_input,
|
|
100
|
-
context: requestContext,
|
|
101
|
-
conversationSummary: conversation_summary,
|
|
102
|
-
recentMessages: recent_messages,
|
|
103
|
-
conversationId: conversationContext.conversationId,
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
// Update conversation state if phase changed
|
|
107
|
-
if (transitionResult.newPhase !== currentPhase) {
|
|
108
|
-
const shouldUpdateState = await this.shouldUpdateConversationState(
|
|
109
|
-
currentPhase,
|
|
110
|
-
transitionResult.newPhase,
|
|
111
|
-
conversationContext,
|
|
112
|
-
context
|
|
113
|
-
);
|
|
114
|
-
|
|
115
|
-
if (shouldUpdateState) {
|
|
116
|
-
await context.conversationManager.updateConversationState(
|
|
117
|
-
conversationId,
|
|
118
|
-
{ currentPhase: transitionResult.newPhase }
|
|
119
|
-
);
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
// If this was a first-call auto-transition, regenerate the plan file
|
|
123
|
-
if (
|
|
124
|
-
transitionResult.transitionReason.includes(
|
|
125
|
-
'Starting development - defining criteria'
|
|
126
|
-
)
|
|
127
|
-
) {
|
|
128
|
-
this.logger.info(
|
|
129
|
-
'Regenerating plan file after first-call auto-transition',
|
|
130
|
-
{
|
|
131
|
-
from: currentPhase,
|
|
132
|
-
to: transitionResult.newPhase,
|
|
133
|
-
planFilePath: conversationContext.planFilePath,
|
|
134
|
-
}
|
|
135
|
-
);
|
|
136
|
-
|
|
137
|
-
await context.planManager.ensurePlanFile(
|
|
138
|
-
conversationContext.planFilePath,
|
|
139
|
-
conversationContext.projectPath,
|
|
140
|
-
conversationContext.gitBranch
|
|
141
|
-
);
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
this.logger.info('Phase transition completed', {
|
|
145
|
-
from: currentPhase,
|
|
146
|
-
to: transitionResult.newPhase,
|
|
147
|
-
reason: transitionResult.transitionReason,
|
|
148
|
-
});
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
// Check if plan file exists
|
|
152
|
-
const planInfo = await context.planManager.getPlanFileInfo(
|
|
153
|
-
conversationContext.planFilePath
|
|
154
|
-
);
|
|
155
|
-
|
|
156
|
-
// Generate enhanced instructions
|
|
157
|
-
const instructions =
|
|
158
|
-
await context.instructionGenerator.generateInstructions(
|
|
159
|
-
transitionResult.instructions,
|
|
160
|
-
{
|
|
161
|
-
phase: transitionResult.newPhase,
|
|
162
|
-
conversationContext: {
|
|
163
|
-
...conversationContext,
|
|
164
|
-
currentPhase: transitionResult.newPhase,
|
|
165
|
-
},
|
|
166
|
-
transitionReason: transitionResult.transitionReason,
|
|
167
|
-
isModeled: transitionResult.isModeled,
|
|
168
|
-
planFileExists: planInfo.exists,
|
|
169
|
-
instructionSource: 'whats_next',
|
|
170
|
-
}
|
|
171
|
-
);
|
|
172
|
-
|
|
173
|
-
// Note: Commit behavior now handled by CommitPlugin
|
|
174
|
-
// Note: Beads-specific instructions are now handled by BeadsInstructionGenerator via strategy pattern
|
|
175
|
-
|
|
176
|
-
// Prepare response
|
|
177
|
-
const response: WhatsNextResult = {
|
|
178
|
-
phase: transitionResult.newPhase,
|
|
179
|
-
instructions: instructions.instructions,
|
|
180
|
-
plan_file_path: conversationContext.planFilePath,
|
|
181
|
-
};
|
|
182
|
-
|
|
183
|
-
// Log interaction
|
|
184
|
-
await this.logInteraction(
|
|
185
|
-
context,
|
|
186
|
-
conversationId,
|
|
187
|
-
'whats_next',
|
|
188
|
-
args,
|
|
189
|
-
response,
|
|
190
|
-
transitionResult.newPhase
|
|
191
|
-
);
|
|
192
|
-
|
|
193
|
-
return response;
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
/**
|
|
197
|
-
* Determines whether conversation state should be updated for a phase transition
|
|
198
|
-
*/
|
|
199
|
-
private async shouldUpdateConversationState(
|
|
200
|
-
currentPhase: string,
|
|
201
|
-
newPhase: string,
|
|
202
|
-
conversationContext: ConversationContext,
|
|
203
|
-
context: ServerContext
|
|
204
|
-
): Promise<boolean> {
|
|
205
|
-
if (!conversationContext.requireReviewsBeforePhaseTransition) {
|
|
206
|
-
return true;
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
const stateMachine = context.workflowManager.loadWorkflowForProject(
|
|
210
|
-
conversationContext.projectPath,
|
|
211
|
-
conversationContext.workflowName
|
|
212
|
-
);
|
|
213
|
-
|
|
214
|
-
const currentState = stateMachine.states[currentPhase];
|
|
215
|
-
if (!currentState) {
|
|
216
|
-
return true;
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
const transition = currentState.transitions.find(t => t.to === newPhase);
|
|
220
|
-
if (!transition) {
|
|
221
|
-
return true;
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
const hasReviewPerspectives =
|
|
225
|
-
transition.review_perspectives &&
|
|
226
|
-
transition.review_perspectives.length > 0;
|
|
227
|
-
|
|
228
|
-
if (hasReviewPerspectives) {
|
|
229
|
-
this.logger.debug(
|
|
230
|
-
'Preventing state update - review required for transition',
|
|
231
|
-
{
|
|
232
|
-
from: currentPhase,
|
|
233
|
-
to: newPhase,
|
|
234
|
-
reviewPerspectives: transition.review_perspectives?.length || 0,
|
|
235
|
-
}
|
|
236
|
-
);
|
|
237
|
-
return false;
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
return true;
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
// Beads-specific instruction logic has been moved to BeadsInstructionGenerator strategy
|
|
244
|
-
|
|
245
|
-
// Utility methods moved to strategy implementations where needed
|
|
246
|
-
}
|
package/src/types.ts
DELETED
|
@@ -1,135 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Core types and interfaces for the refactored server architecture
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import { ConversationManager } from '@codemcp/workflows-core';
|
|
6
|
-
import { TransitionEngine } from '@codemcp/workflows-core';
|
|
7
|
-
import { IPlanManager } from '@codemcp/workflows-core';
|
|
8
|
-
import { IInstructionGenerator } from '@codemcp/workflows-core';
|
|
9
|
-
import { WorkflowManager } from '@codemcp/workflows-core';
|
|
10
|
-
import { InteractionLogger } from '@codemcp/workflows-core';
|
|
11
|
-
import type { TaskBackendConfig } from '@codemcp/workflows-core';
|
|
12
|
-
import type { IPluginRegistry } from './plugin-system/plugin-interfaces.js';
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Server context shared across all handlers
|
|
16
|
-
* Contains all the core dependencies needed by tool and resource handlers
|
|
17
|
-
*/
|
|
18
|
-
export interface ServerContext {
|
|
19
|
-
conversationManager: ConversationManager;
|
|
20
|
-
transitionEngine: TransitionEngine;
|
|
21
|
-
planManager: IPlanManager;
|
|
22
|
-
instructionGenerator: IInstructionGenerator;
|
|
23
|
-
workflowManager: WorkflowManager;
|
|
24
|
-
interactionLogger?: InteractionLogger;
|
|
25
|
-
projectPath: string;
|
|
26
|
-
pluginRegistry?: IPluginRegistry;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* Standard result format for all handler operations
|
|
31
|
-
* Separates business logic results from MCP protocol concerns
|
|
32
|
-
*/
|
|
33
|
-
export interface HandlerResult<T = unknown> {
|
|
34
|
-
success: boolean;
|
|
35
|
-
data?: T;
|
|
36
|
-
error?: string;
|
|
37
|
-
metadata?: Record<string, unknown>;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Resource content structure
|
|
42
|
-
*/
|
|
43
|
-
export interface ResourceContent {
|
|
44
|
-
uri: string;
|
|
45
|
-
text: string;
|
|
46
|
-
mimeType: string;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* MCP Tool Response format (compatible with MCP SDK)
|
|
51
|
-
*/
|
|
52
|
-
export interface McpToolResponse {
|
|
53
|
-
[x: string]: unknown;
|
|
54
|
-
content: Array<{
|
|
55
|
-
type: 'text';
|
|
56
|
-
text: string;
|
|
57
|
-
}>;
|
|
58
|
-
isError?: boolean;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* MCP Resource Response format (compatible with MCP SDK)
|
|
63
|
-
*/
|
|
64
|
-
export interface McpResourceResponse {
|
|
65
|
-
[x: string]: unknown;
|
|
66
|
-
contents: Array<{
|
|
67
|
-
uri: string;
|
|
68
|
-
text: string;
|
|
69
|
-
mimeType: string;
|
|
70
|
-
}>;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
/**
|
|
74
|
-
* Tool handler interface
|
|
75
|
-
* All tool handlers must implement this interface
|
|
76
|
-
*/
|
|
77
|
-
export interface ToolHandler<TArgs = unknown, TResult = unknown> {
|
|
78
|
-
handle(args: TArgs, context: ServerContext): Promise<HandlerResult<TResult>>;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
/**
|
|
82
|
-
* Resource handler interface
|
|
83
|
-
* All resource handlers must implement this interface
|
|
84
|
-
*/
|
|
85
|
-
export interface ResourceHandler {
|
|
86
|
-
handle(
|
|
87
|
-
uri: URL,
|
|
88
|
-
context: ServerContext
|
|
89
|
-
): Promise<HandlerResult<ResourceContent>>;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
/**
|
|
93
|
-
* Response renderer interface
|
|
94
|
-
* Handles translation between domain results and MCP protocol responses
|
|
95
|
-
*/
|
|
96
|
-
export interface ResponseRenderer {
|
|
97
|
-
renderToolResponse<T>(result: HandlerResult<T>): McpToolResponse;
|
|
98
|
-
renderResourceResponse(
|
|
99
|
-
result: HandlerResult<ResourceContent>
|
|
100
|
-
): McpResourceResponse;
|
|
101
|
-
renderError(error: Error | string): McpToolResponse;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
/**
|
|
105
|
-
* Tool registry interface
|
|
106
|
-
* Manages registration and lookup of tool handlers
|
|
107
|
-
*/
|
|
108
|
-
export interface ToolRegistry {
|
|
109
|
-
register<T extends ToolHandler>(name: string, handler: T): void;
|
|
110
|
-
get(name: string): ToolHandler | undefined;
|
|
111
|
-
list(): string[];
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
/**
|
|
115
|
-
* Resource registry interface
|
|
116
|
-
* Manages registration and lookup of resource handlers
|
|
117
|
-
*/
|
|
118
|
-
export interface ResourceRegistry {
|
|
119
|
-
register(pattern: string, handler: ResourceHandler): void;
|
|
120
|
-
resolve(uri: string): ResourceHandler | undefined;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
/**
|
|
124
|
-
* Server configuration options
|
|
125
|
-
*/
|
|
126
|
-
export interface ServerConfig {
|
|
127
|
-
/** Project path to operate on (defaults to process.cwd()) */
|
|
128
|
-
projectPath?: string;
|
|
129
|
-
/** Database path override */
|
|
130
|
-
databasePath?: string;
|
|
131
|
-
/** Enable interaction logging */
|
|
132
|
-
enableLogging?: boolean;
|
|
133
|
-
/** Task backend configuration override (for testing) */
|
|
134
|
-
taskBackend?: TaskBackendConfig;
|
|
135
|
-
}
|
package/src/version-info.ts
DELETED
|
@@ -1,213 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Version Information Utility
|
|
3
|
-
*
|
|
4
|
-
* Provides version information for the MCP server, supporting both
|
|
5
|
-
* build-time injection and runtime determination for local development.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import { execSync } from 'node:child_process';
|
|
9
|
-
import { readFileSync } from 'node:fs';
|
|
10
|
-
import { join, dirname } from 'node:path';
|
|
11
|
-
import { fileURLToPath } from 'node:url';
|
|
12
|
-
import { createLogger } from '@codemcp/workflows-core';
|
|
13
|
-
|
|
14
|
-
const logger = createLogger('VersionInfo');
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* Structure for version information
|
|
18
|
-
*/
|
|
19
|
-
export interface VersionInfo {
|
|
20
|
-
/** The semantic version (e.g., "4.8.0") */
|
|
21
|
-
version: string;
|
|
22
|
-
/** Git commit hash (short form, e.g., "bbb06ba") */
|
|
23
|
-
commit?: string;
|
|
24
|
-
/** Whether working directory has uncommitted changes */
|
|
25
|
-
isDirty?: boolean;
|
|
26
|
-
/** Full git describe output (e.g., "v4.8.0-1-gbbb06ba-dirty") */
|
|
27
|
-
gitDescribe?: string;
|
|
28
|
-
/** Source of version information */
|
|
29
|
-
source: 'package.json' | 'git' | 'build-time' | 'fallback';
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* Build-time version information (injected during build process)
|
|
34
|
-
* This will be replaced by the build system with actual values
|
|
35
|
-
*/
|
|
36
|
-
// @ts-ignore - This is replaced at build time
|
|
37
|
-
const BUILD_TIME_VERSION: VersionInfo | null = null;
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
* Gets version information from package.json
|
|
41
|
-
*/
|
|
42
|
-
function getVersionFromPackageJson(): VersionInfo | null {
|
|
43
|
-
try {
|
|
44
|
-
// Get the directory of this module
|
|
45
|
-
const currentDir = dirname(fileURLToPath(import.meta.url));
|
|
46
|
-
|
|
47
|
-
// Try to find package.json - first in the mcp-server package, then root
|
|
48
|
-
const packageJsonPaths = [
|
|
49
|
-
join(currentDir, '..', 'package.json'),
|
|
50
|
-
join(currentDir, '..', '..', '..', 'package.json'),
|
|
51
|
-
];
|
|
52
|
-
|
|
53
|
-
for (const packageJsonPath of packageJsonPaths) {
|
|
54
|
-
try {
|
|
55
|
-
const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf-8'));
|
|
56
|
-
if (packageJson.version) {
|
|
57
|
-
logger.debug('Found version in package.json', {
|
|
58
|
-
path: packageJsonPath,
|
|
59
|
-
version: packageJson.version,
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
return {
|
|
63
|
-
version: packageJson.version,
|
|
64
|
-
source: 'package.json',
|
|
65
|
-
};
|
|
66
|
-
}
|
|
67
|
-
} catch (error) {
|
|
68
|
-
logger.debug('Could not read package.json', {
|
|
69
|
-
path: packageJsonPath,
|
|
70
|
-
error: error instanceof Error ? error.message : String(error),
|
|
71
|
-
});
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
return null;
|
|
76
|
-
} catch (error) {
|
|
77
|
-
logger.debug('Error getting version from package.json', { error });
|
|
78
|
-
return null;
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
/**
|
|
83
|
-
* Gets version information from git
|
|
84
|
-
*/
|
|
85
|
-
function getVersionFromGit(): VersionInfo | null {
|
|
86
|
-
try {
|
|
87
|
-
// Get git describe output
|
|
88
|
-
const gitDescribe = execSync('git describe --tags --always --dirty', {
|
|
89
|
-
encoding: 'utf-8',
|
|
90
|
-
stdio: 'pipe',
|
|
91
|
-
}).trim();
|
|
92
|
-
|
|
93
|
-
logger.debug('Git describe output', { gitDescribe });
|
|
94
|
-
|
|
95
|
-
// Parse git describe output (e.g., "v4.8.0-1-gbbb06ba-dirty")
|
|
96
|
-
const isDirty = gitDescribe.endsWith('-dirty');
|
|
97
|
-
const cleanDescribe = isDirty ? gitDescribe.slice(0, -6) : gitDescribe;
|
|
98
|
-
|
|
99
|
-
// Try to extract version and commit
|
|
100
|
-
const parts = cleanDescribe.split('-');
|
|
101
|
-
let version: string;
|
|
102
|
-
let commit: string | undefined;
|
|
103
|
-
|
|
104
|
-
if (parts.length >= 3 && parts[0]?.startsWith('v')) {
|
|
105
|
-
// Format: v4.8.0-1-gbbb06ba
|
|
106
|
-
version = parts[0].slice(1); // Remove 'v' prefix
|
|
107
|
-
commit = parts[2]?.startsWith('g') ? parts[2].slice(1) : parts[2];
|
|
108
|
-
} else if (parts.length === 1 && parts[0]?.startsWith('v')) {
|
|
109
|
-
// Format: v4.8.0 (exact tag)
|
|
110
|
-
version = parts[0].slice(1);
|
|
111
|
-
} else if (parts.length === 1) {
|
|
112
|
-
// Format: commit hash only
|
|
113
|
-
version = 'unknown';
|
|
114
|
-
commit = parts[0];
|
|
115
|
-
} else {
|
|
116
|
-
// Fallback
|
|
117
|
-
version = 'unknown';
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
return {
|
|
121
|
-
version,
|
|
122
|
-
commit,
|
|
123
|
-
isDirty,
|
|
124
|
-
gitDescribe,
|
|
125
|
-
source: 'git',
|
|
126
|
-
};
|
|
127
|
-
} catch (error) {
|
|
128
|
-
logger.debug('Error getting version from git', {
|
|
129
|
-
error: error instanceof Error ? error.message : String(error),
|
|
130
|
-
});
|
|
131
|
-
return null;
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
/**
|
|
136
|
-
* Gets comprehensive version information, trying multiple sources
|
|
137
|
-
*/
|
|
138
|
-
export function getVersionInfo(): VersionInfo {
|
|
139
|
-
logger.debug('Determining version information');
|
|
140
|
-
|
|
141
|
-
// Try build-time version first (will be null in development)
|
|
142
|
-
if (BUILD_TIME_VERSION) {
|
|
143
|
-
logger.info('Using build-time version information', {
|
|
144
|
-
version: BUILD_TIME_VERSION.version,
|
|
145
|
-
source: BUILD_TIME_VERSION.source,
|
|
146
|
-
});
|
|
147
|
-
return BUILD_TIME_VERSION;
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
// Try git version info (best for development)
|
|
151
|
-
const gitVersion = getVersionFromGit();
|
|
152
|
-
if (gitVersion) {
|
|
153
|
-
// If we have git info, try to enhance with package.json version
|
|
154
|
-
const packageVersion = getVersionFromPackageJson();
|
|
155
|
-
if (packageVersion && gitVersion.version === 'unknown') {
|
|
156
|
-
logger.info('Using package.json version with git commit info', {
|
|
157
|
-
version: packageVersion.version,
|
|
158
|
-
commit: gitVersion.commit || 'unknown',
|
|
159
|
-
isDirty: String(gitVersion.isDirty || false),
|
|
160
|
-
});
|
|
161
|
-
|
|
162
|
-
return {
|
|
163
|
-
...gitVersion,
|
|
164
|
-
version: packageVersion.version,
|
|
165
|
-
source: 'git' as const,
|
|
166
|
-
};
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
logger.info('Using git version information', {
|
|
170
|
-
version: gitVersion.version,
|
|
171
|
-
source: gitVersion.source,
|
|
172
|
-
commit: gitVersion.commit || 'none',
|
|
173
|
-
isDirty: String(gitVersion.isDirty || false),
|
|
174
|
-
});
|
|
175
|
-
return gitVersion;
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
// Fallback to package.json only
|
|
179
|
-
const packageVersion = getVersionFromPackageJson();
|
|
180
|
-
if (packageVersion) {
|
|
181
|
-
logger.info('Using package.json version information', {
|
|
182
|
-
version: packageVersion.version,
|
|
183
|
-
source: packageVersion.source,
|
|
184
|
-
});
|
|
185
|
-
return packageVersion;
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
// Final fallback
|
|
189
|
-
logger.warn('Could not determine version information, using fallback');
|
|
190
|
-
return {
|
|
191
|
-
version: 'unknown',
|
|
192
|
-
source: 'fallback',
|
|
193
|
-
};
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
/**
|
|
197
|
-
* Gets a formatted version string suitable for display
|
|
198
|
-
*/
|
|
199
|
-
export function getFormattedVersion(): string {
|
|
200
|
-
const versionInfo = getVersionInfo();
|
|
201
|
-
|
|
202
|
-
let formatted = versionInfo.version;
|
|
203
|
-
|
|
204
|
-
if (versionInfo.commit) {
|
|
205
|
-
formatted += `+${versionInfo.commit}`;
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
if (versionInfo.isDirty) {
|
|
209
|
-
formatted += '.dirty';
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
return formatted;
|
|
213
|
-
}
|