@codemcp/workflows-core 3.1.22 → 3.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +8 -3
- package/resources/templates/architecture/arc42/arc42-template-EN.md +1077 -0
- package/resources/templates/architecture/arc42/images/01_2_iso-25010-topics-EN.drawio-2023.png +0 -0
- package/resources/templates/architecture/arc42/images/01_2_iso-25010-topics-EN.drawio.png +0 -0
- package/resources/templates/architecture/arc42/images/05_building_blocks-EN.png +0 -0
- package/resources/templates/architecture/arc42/images/08-concepts-EN.drawio.png +0 -0
- package/resources/templates/architecture/arc42/images/arc42-logo.png +0 -0
- package/resources/templates/architecture/c4.md +224 -0
- package/resources/templates/architecture/freestyle.md +53 -0
- package/resources/templates/architecture/none.md +17 -0
- package/resources/templates/design/comprehensive.md +207 -0
- package/resources/templates/design/freestyle.md +37 -0
- package/resources/templates/design/none.md +17 -0
- package/resources/templates/requirements/ears.md +90 -0
- package/resources/templates/requirements/freestyle.md +42 -0
- package/resources/templates/requirements/none.md +17 -0
- package/resources/workflows/big-bang-conversion.yaml +539 -0
- package/resources/workflows/boundary-testing.yaml +334 -0
- package/resources/workflows/bugfix.yaml +185 -0
- package/resources/workflows/business-analysis.yaml +671 -0
- package/resources/workflows/c4-analysis.yaml +485 -0
- package/resources/workflows/epcc.yaml +161 -0
- package/resources/workflows/greenfield.yaml +189 -0
- package/resources/workflows/minor.yaml +127 -0
- package/resources/workflows/posts.yaml +207 -0
- package/resources/workflows/slides.yaml +256 -0
- package/resources/workflows/tdd.yaml +157 -0
- package/resources/workflows/waterfall.yaml +195 -0
- package/.turbo/turbo-build.log +0 -4
- package/src/config-manager.ts +0 -96
- package/src/conversation-manager.ts +0 -489
- package/src/database.ts +0 -427
- package/src/file-detection-manager.ts +0 -302
- package/src/git-manager.ts +0 -64
- package/src/index.ts +0 -28
- package/src/instruction-generator.ts +0 -210
- package/src/interaction-logger.ts +0 -109
- package/src/logger.ts +0 -353
- package/src/path-validation-utils.ts +0 -261
- package/src/plan-manager.ts +0 -323
- package/src/project-docs-manager.ts +0 -523
- package/src/state-machine-loader.ts +0 -365
- package/src/state-machine-types.ts +0 -72
- package/src/state-machine.ts +0 -370
- package/src/system-prompt-generator.ts +0 -122
- package/src/template-manager.ts +0 -328
- package/src/transition-engine.ts +0 -386
- package/src/types.ts +0 -60
- package/src/workflow-manager.ts +0 -606
- package/test/unit/conversation-manager.test.ts +0 -179
- package/test/unit/custom-workflow-loading.test.ts +0 -174
- package/test/unit/directory-linking-and-extensions.test.ts +0 -338
- package/test/unit/file-linking-integration.test.ts +0 -256
- package/test/unit/git-commit-integration.test.ts +0 -91
- package/test/unit/git-manager.test.ts +0 -86
- package/test/unit/install-workflow.test.ts +0 -138
- package/test/unit/instruction-generator.test.ts +0 -247
- package/test/unit/list-workflows-filtering.test.ts +0 -68
- package/test/unit/none-template-functionality.test.ts +0 -224
- package/test/unit/project-docs-manager.test.ts +0 -337
- package/test/unit/state-machine-loader.test.ts +0 -234
- package/test/unit/template-manager.test.ts +0 -217
- package/test/unit/validate-workflow-name.test.ts +0 -150
- package/test/unit/workflow-domain-filtering.test.ts +0 -75
- package/test/unit/workflow-enum-generation.test.ts +0 -92
- package/test/unit/workflow-manager-enhanced-path-resolution.test.ts +0 -369
- package/test/unit/workflow-manager-path-resolution.test.ts +0 -150
- package/test/unit/workflow-migration.test.ts +0 -155
- package/test/unit/workflow-override-by-name.test.ts +0 -116
- package/test/unit/workflow-prioritization.test.ts +0 -38
- package/test/unit/workflow-validation.test.ts +0 -303
- package/test/utils/e2e-test-setup.ts +0 -453
- package/test/utils/run-server-in-dir.sh +0 -27
- package/test/utils/temp-files.ts +0 -308
- package/test/utils/test-access.ts +0 -79
- package/test/utils/test-helpers.ts +0 -286
- package/test/utils/test-setup.ts +0 -78
- package/tsconfig.build.json +0 -21
- package/tsconfig.json +0 -8
- package/vitest.config.ts +0 -18
@@ -1,302 +0,0 @@
|
|
1
|
-
/**
|
2
|
-
* File Detection Manager
|
3
|
-
*
|
4
|
-
* Handles pattern-based file discovery and suggestions for existing documentation files.
|
5
|
-
* Supports auto-detection of common documentation patterns in projects.
|
6
|
-
*/
|
7
|
-
|
8
|
-
import { readdir, access } from 'node:fs/promises';
|
9
|
-
import { join, basename } from 'node:path';
|
10
|
-
import { createLogger } from './logger.js';
|
11
|
-
import { PathValidationUtils } from './path-validation-utils.js';
|
12
|
-
|
13
|
-
const logger = createLogger('FileDetectionManager');
|
14
|
-
|
15
|
-
export interface DetectedFile {
|
16
|
-
path: string;
|
17
|
-
relativePath: string;
|
18
|
-
type: 'architecture' | 'requirements' | 'design';
|
19
|
-
confidence: 'high' | 'medium' | 'low';
|
20
|
-
}
|
21
|
-
|
22
|
-
export interface FileDetectionResult {
|
23
|
-
architecture: DetectedFile[];
|
24
|
-
requirements: DetectedFile[];
|
25
|
-
design: DetectedFile[];
|
26
|
-
}
|
27
|
-
|
28
|
-
export class FileDetectionManager {
|
29
|
-
private projectPath: string;
|
30
|
-
|
31
|
-
constructor(projectPath: string) {
|
32
|
-
this.projectPath = projectPath;
|
33
|
-
}
|
34
|
-
|
35
|
-
/**
|
36
|
-
* Detect existing documentation files in the project
|
37
|
-
*/
|
38
|
-
async detectDocumentationFiles(): Promise<FileDetectionResult> {
|
39
|
-
logger.debug('Starting documentation file detection', {
|
40
|
-
projectPath: this.projectPath,
|
41
|
-
});
|
42
|
-
|
43
|
-
const searchLocations = this.getSearchLocations();
|
44
|
-
const patterns = PathValidationUtils.getCommonDocumentationPatterns();
|
45
|
-
|
46
|
-
const result: FileDetectionResult = {
|
47
|
-
architecture: [],
|
48
|
-
requirements: [],
|
49
|
-
design: [],
|
50
|
-
};
|
51
|
-
|
52
|
-
// Search in each location
|
53
|
-
for (const location of searchLocations) {
|
54
|
-
try {
|
55
|
-
await access(location);
|
56
|
-
const files = await this.scanLocation(location);
|
57
|
-
|
58
|
-
// Match files against patterns
|
59
|
-
for (const file of files) {
|
60
|
-
const matches = this.matchFileToPatterns(file, patterns);
|
61
|
-
|
62
|
-
for (const match of matches) {
|
63
|
-
result[match.type].push({
|
64
|
-
path: file.path,
|
65
|
-
relativePath: file.relativePath,
|
66
|
-
type: match.type,
|
67
|
-
confidence: match.confidence,
|
68
|
-
});
|
69
|
-
}
|
70
|
-
}
|
71
|
-
} catch (error) {
|
72
|
-
logger.debug('Search location not accessible', {
|
73
|
-
location,
|
74
|
-
error: error instanceof Error ? error.message : 'Unknown error',
|
75
|
-
});
|
76
|
-
}
|
77
|
-
}
|
78
|
-
|
79
|
-
// Sort by confidence and remove duplicates
|
80
|
-
result.architecture = this.sortAndDeduplicate(result.architecture);
|
81
|
-
result.requirements = this.sortAndDeduplicate(result.requirements);
|
82
|
-
result.design = this.sortAndDeduplicate(result.design);
|
83
|
-
|
84
|
-
logger.info('Documentation file detection completed', {
|
85
|
-
found: {
|
86
|
-
architecture: result.architecture.length,
|
87
|
-
requirements: result.requirements.length,
|
88
|
-
design: result.design.length,
|
89
|
-
},
|
90
|
-
});
|
91
|
-
|
92
|
-
return result;
|
93
|
-
}
|
94
|
-
|
95
|
-
/**
|
96
|
-
* Get search locations for documentation files
|
97
|
-
*/
|
98
|
-
private getSearchLocations(): string[] {
|
99
|
-
return [
|
100
|
-
this.projectPath, // Project root
|
101
|
-
join(this.projectPath, 'docs'), // docs/ folder
|
102
|
-
join(this.projectPath, 'doc'), // doc/ folder
|
103
|
-
join(this.projectPath, '.vibe', 'docs'), // .vibe/docs/ folder
|
104
|
-
join(this.projectPath, 'documentation'), // documentation/ folder
|
105
|
-
];
|
106
|
-
}
|
107
|
-
|
108
|
-
/**
|
109
|
-
* Scan a location for files
|
110
|
-
*/
|
111
|
-
private async scanLocation(
|
112
|
-
location: string
|
113
|
-
): Promise<Array<{ path: string; relativePath: string }>> {
|
114
|
-
try {
|
115
|
-
const entries = await readdir(location, { withFileTypes: true });
|
116
|
-
const files: Array<{ path: string; relativePath: string }> = [];
|
117
|
-
|
118
|
-
for (const entry of entries) {
|
119
|
-
if (entry.isFile()) {
|
120
|
-
const fullPath = join(location, entry.name);
|
121
|
-
const relativePath = fullPath.replace(this.projectPath + '/', '');
|
122
|
-
|
123
|
-
files.push({
|
124
|
-
path: fullPath,
|
125
|
-
relativePath,
|
126
|
-
});
|
127
|
-
}
|
128
|
-
}
|
129
|
-
|
130
|
-
return files;
|
131
|
-
} catch (error) {
|
132
|
-
logger.debug('Failed to scan location', {
|
133
|
-
location,
|
134
|
-
error: error instanceof Error ? error.message : 'Unknown error',
|
135
|
-
});
|
136
|
-
return [];
|
137
|
-
}
|
138
|
-
}
|
139
|
-
|
140
|
-
/**
|
141
|
-
* Match a file against documentation patterns
|
142
|
-
*/
|
143
|
-
private matchFileToPatterns(
|
144
|
-
file: { path: string; relativePath: string },
|
145
|
-
patterns: ReturnType<
|
146
|
-
typeof PathValidationUtils.getCommonDocumentationPatterns
|
147
|
-
>
|
148
|
-
): Array<{
|
149
|
-
type: 'architecture' | 'requirements' | 'design';
|
150
|
-
confidence: 'high' | 'medium' | 'low';
|
151
|
-
}> {
|
152
|
-
const fileName = basename(file.path).toLowerCase();
|
153
|
-
const relativePath = file.relativePath.toLowerCase();
|
154
|
-
const matches: Array<{
|
155
|
-
type: 'architecture' | 'requirements' | 'design';
|
156
|
-
confidence: 'high' | 'medium' | 'low';
|
157
|
-
}> = [];
|
158
|
-
|
159
|
-
// Check architecture patterns
|
160
|
-
if (this.matchesPatterns(fileName, relativePath, patterns.architecture)) {
|
161
|
-
const confidence = this.getConfidence(fileName, 'architecture');
|
162
|
-
matches.push({ type: 'architecture', confidence });
|
163
|
-
}
|
164
|
-
|
165
|
-
// Check requirements patterns
|
166
|
-
if (this.matchesPatterns(fileName, relativePath, patterns.requirements)) {
|
167
|
-
const confidence = this.getConfidence(fileName, 'requirements');
|
168
|
-
matches.push({ type: 'requirements', confidence });
|
169
|
-
}
|
170
|
-
|
171
|
-
// Check design patterns
|
172
|
-
if (this.matchesPatterns(fileName, relativePath, patterns.design)) {
|
173
|
-
const confidence = this.getConfidence(fileName, 'design');
|
174
|
-
matches.push({ type: 'design', confidence });
|
175
|
-
}
|
176
|
-
|
177
|
-
return matches;
|
178
|
-
}
|
179
|
-
|
180
|
-
/**
|
181
|
-
* Check if file matches any of the patterns
|
182
|
-
*/
|
183
|
-
private matchesPatterns(
|
184
|
-
fileName: string,
|
185
|
-
relativePath: string,
|
186
|
-
patterns: string[]
|
187
|
-
): boolean {
|
188
|
-
return patterns.some(pattern => {
|
189
|
-
const normalizedPattern = pattern.toLowerCase();
|
190
|
-
|
191
|
-
// Exact filename match
|
192
|
-
if (fileName === normalizedPattern) {
|
193
|
-
return true;
|
194
|
-
}
|
195
|
-
|
196
|
-
// Relative path match
|
197
|
-
if (relativePath === normalizedPattern) {
|
198
|
-
return true;
|
199
|
-
}
|
200
|
-
|
201
|
-
// Pattern matching with wildcards
|
202
|
-
if (normalizedPattern.includes('*')) {
|
203
|
-
const regex = new RegExp(normalizedPattern.replace(/\*/g, '.*'));
|
204
|
-
return regex.test(fileName) || regex.test(relativePath);
|
205
|
-
}
|
206
|
-
|
207
|
-
return false;
|
208
|
-
});
|
209
|
-
}
|
210
|
-
|
211
|
-
/**
|
212
|
-
* Determine confidence level for a match
|
213
|
-
*/
|
214
|
-
private getConfidence(
|
215
|
-
fileName: string,
|
216
|
-
type: string
|
217
|
-
): 'high' | 'medium' | 'low' {
|
218
|
-
// High confidence for exact type matches
|
219
|
-
if (fileName.includes(type.toLowerCase())) {
|
220
|
-
return 'high';
|
221
|
-
}
|
222
|
-
|
223
|
-
// Medium confidence for README files (could contain any type)
|
224
|
-
if (fileName.includes('readme')) {
|
225
|
-
return 'medium';
|
226
|
-
}
|
227
|
-
|
228
|
-
// Low confidence for other matches
|
229
|
-
return 'low';
|
230
|
-
}
|
231
|
-
|
232
|
-
/**
|
233
|
-
* Sort by confidence and remove duplicates
|
234
|
-
*/
|
235
|
-
private sortAndDeduplicate(files: DetectedFile[]): DetectedFile[] {
|
236
|
-
// Remove duplicates by path
|
237
|
-
const unique = files.filter(
|
238
|
-
(file, index, array) =>
|
239
|
-
array.findIndex(f => f.path === file.path) === index
|
240
|
-
);
|
241
|
-
|
242
|
-
// Sort by confidence (high first) and then by path length (shorter first)
|
243
|
-
return unique.sort((a, b) => {
|
244
|
-
const confidenceOrder = { high: 0, medium: 1, low: 2 };
|
245
|
-
const confidenceDiff =
|
246
|
-
confidenceOrder[a.confidence] - confidenceOrder[b.confidence];
|
247
|
-
|
248
|
-
if (confidenceDiff !== 0) {
|
249
|
-
return confidenceDiff;
|
250
|
-
}
|
251
|
-
|
252
|
-
return a.relativePath.length - b.relativePath.length;
|
253
|
-
});
|
254
|
-
}
|
255
|
-
|
256
|
-
/**
|
257
|
-
* Format file suggestions for LLM responses
|
258
|
-
*/
|
259
|
-
formatSuggestions(detectionResult: FileDetectionResult): string {
|
260
|
-
const suggestions: string[] = [];
|
261
|
-
|
262
|
-
if (detectionResult.architecture.length > 0) {
|
263
|
-
suggestions.push(`**Architecture files found:**`);
|
264
|
-
for (const file of detectionResult.architecture.slice(0, 3)) {
|
265
|
-
suggestions.push(
|
266
|
-
` - ${file.relativePath} (${file.confidence} confidence)`
|
267
|
-
);
|
268
|
-
}
|
269
|
-
}
|
270
|
-
|
271
|
-
if (detectionResult.requirements.length > 0) {
|
272
|
-
suggestions.push(`**Requirements files found:**`);
|
273
|
-
for (const file of detectionResult.requirements.slice(0, 3)) {
|
274
|
-
suggestions.push(
|
275
|
-
` - ${file.relativePath} (${file.confidence} confidence)`
|
276
|
-
);
|
277
|
-
}
|
278
|
-
}
|
279
|
-
|
280
|
-
if (detectionResult.design.length > 0) {
|
281
|
-
suggestions.push(`**Design files found:**`);
|
282
|
-
for (const file of detectionResult.design.slice(0, 3)) {
|
283
|
-
suggestions.push(
|
284
|
-
` - ${file.relativePath} (${file.confidence} confidence)`
|
285
|
-
);
|
286
|
-
}
|
287
|
-
}
|
288
|
-
|
289
|
-
if (suggestions.length === 0) {
|
290
|
-
return 'No existing documentation files detected.';
|
291
|
-
}
|
292
|
-
|
293
|
-
return [
|
294
|
-
'Existing documentation files detected:',
|
295
|
-
'',
|
296
|
-
...suggestions,
|
297
|
-
'',
|
298
|
-
'You can use these files with `setup_project_docs` by providing the file paths instead of template names.',
|
299
|
-
'Example: `setup_project_docs({ architecture: "README.md", requirements: "docs/requirements.md", design: "freestyle" })`',
|
300
|
-
].join('\n');
|
301
|
-
}
|
302
|
-
}
|
package/src/git-manager.ts
DELETED
@@ -1,64 +0,0 @@
|
|
1
|
-
import { execSync } from 'node:child_process';
|
2
|
-
import { existsSync } from 'node:fs';
|
3
|
-
import { createLogger } from './logger.js';
|
4
|
-
|
5
|
-
const logger = createLogger('GitManager');
|
6
|
-
|
7
|
-
export class GitManager {
|
8
|
-
/**
|
9
|
-
* Check if a directory is a git repository
|
10
|
-
*/
|
11
|
-
static isGitRepository(projectPath: string): boolean {
|
12
|
-
return existsSync(`${projectPath}/.git`);
|
13
|
-
}
|
14
|
-
|
15
|
-
/**
|
16
|
-
* Get the current git branch for a project
|
17
|
-
*/
|
18
|
-
static getCurrentBranch(projectPath: string): string {
|
19
|
-
try {
|
20
|
-
if (!this.isGitRepository(projectPath)) {
|
21
|
-
logger.debug('Not a git repository, using "default" as branch name', {
|
22
|
-
projectPath,
|
23
|
-
});
|
24
|
-
return 'default';
|
25
|
-
}
|
26
|
-
|
27
|
-
const branch = execSync('git rev-parse --abbrev-ref HEAD', {
|
28
|
-
cwd: projectPath,
|
29
|
-
encoding: 'utf-8',
|
30
|
-
stdio: ['ignore', 'pipe', 'ignore'],
|
31
|
-
}).trim();
|
32
|
-
|
33
|
-
logger.debug('Detected git branch', { projectPath, branch });
|
34
|
-
return branch;
|
35
|
-
} catch (_error) {
|
36
|
-
logger.debug('Failed to get git branch, using "default" as branch name', {
|
37
|
-
projectPath,
|
38
|
-
});
|
39
|
-
return 'default';
|
40
|
-
}
|
41
|
-
}
|
42
|
-
|
43
|
-
/**
|
44
|
-
* Get the current HEAD commit hash (for tracking start of development)
|
45
|
-
*/
|
46
|
-
static getCurrentCommitHash(projectPath: string): string | null {
|
47
|
-
try {
|
48
|
-
if (!this.isGitRepository(projectPath)) {
|
49
|
-
return null;
|
50
|
-
}
|
51
|
-
|
52
|
-
const hash = execSync('git rev-parse HEAD', {
|
53
|
-
cwd: projectPath,
|
54
|
-
encoding: 'utf-8',
|
55
|
-
stdio: ['ignore', 'pipe', 'ignore'],
|
56
|
-
}).trim();
|
57
|
-
|
58
|
-
return hash;
|
59
|
-
} catch (error) {
|
60
|
-
logger.debug('Failed to get current commit hash', { projectPath, error });
|
61
|
-
return null;
|
62
|
-
}
|
63
|
-
}
|
64
|
-
}
|
package/src/index.ts
DELETED
@@ -1,28 +0,0 @@
|
|
1
|
-
// Core types and interfaces
|
2
|
-
export * from './types.js';
|
3
|
-
export * from './state-machine-types.js';
|
4
|
-
|
5
|
-
// State machine and workflow management
|
6
|
-
export * from './state-machine.js';
|
7
|
-
export * from './state-machine-loader.js';
|
8
|
-
export * from './workflow-manager.js';
|
9
|
-
export * from './transition-engine.js';
|
10
|
-
|
11
|
-
// Data management
|
12
|
-
export * from './database.js';
|
13
|
-
export * from './conversation-manager.js';
|
14
|
-
|
15
|
-
// Project and plan management
|
16
|
-
export * from './plan-manager.js';
|
17
|
-
export * from './template-manager.js';
|
18
|
-
export * from './project-docs-manager.js';
|
19
|
-
export * from './file-detection-manager.js';
|
20
|
-
export * from './config-manager.js';
|
21
|
-
export * from './git-manager.js';
|
22
|
-
|
23
|
-
// Utilities and generators
|
24
|
-
export * from './logger.js';
|
25
|
-
export * from './interaction-logger.js';
|
26
|
-
export * from './instruction-generator.js';
|
27
|
-
export * from './system-prompt-generator.js';
|
28
|
-
export * from './path-validation-utils.js';
|
@@ -1,210 +0,0 @@
|
|
1
|
-
/**
|
2
|
-
* Instruction Generator
|
3
|
-
*
|
4
|
-
* Creates phase-specific guidance for the LLM based on current conversation state.
|
5
|
-
* Customizes instructions based on project context and development phase.
|
6
|
-
* Supports custom state machine definitions for dynamic instruction generation.
|
7
|
-
* Handles variable substitution for project artifact references.
|
8
|
-
*/
|
9
|
-
|
10
|
-
import type { ConversationContext } from './types.js';
|
11
|
-
import { PlanManager } from './plan-manager.js';
|
12
|
-
import { ProjectDocsManager } from './project-docs-manager.js';
|
13
|
-
import type { YamlStateMachine } from './state-machine-types.js';
|
14
|
-
|
15
|
-
export interface InstructionContext {
|
16
|
-
phase: string;
|
17
|
-
conversationContext: ConversationContext;
|
18
|
-
transitionReason: string;
|
19
|
-
isModeled: boolean;
|
20
|
-
planFileExists: boolean;
|
21
|
-
}
|
22
|
-
|
23
|
-
export interface GeneratedInstructions {
|
24
|
-
instructions: string;
|
25
|
-
planFileGuidance: string;
|
26
|
-
metadata: {
|
27
|
-
phase: string;
|
28
|
-
planFilePath: string;
|
29
|
-
transitionReason: string;
|
30
|
-
isModeled: boolean;
|
31
|
-
};
|
32
|
-
}
|
33
|
-
|
34
|
-
export class InstructionGenerator {
|
35
|
-
private planManager: PlanManager;
|
36
|
-
private projectDocsManager: ProjectDocsManager;
|
37
|
-
private stateMachine: YamlStateMachine | null = null;
|
38
|
-
|
39
|
-
constructor(planManager: PlanManager) {
|
40
|
-
this.planManager = planManager;
|
41
|
-
this.projectDocsManager = new ProjectDocsManager();
|
42
|
-
}
|
43
|
-
|
44
|
-
/**
|
45
|
-
* Set the state machine definition for dynamic instruction generation
|
46
|
-
*/
|
47
|
-
setStateMachine(stateMachine: YamlStateMachine): void {
|
48
|
-
this.stateMachine = stateMachine;
|
49
|
-
}
|
50
|
-
|
51
|
-
/**
|
52
|
-
* Generate comprehensive instructions for the LLM
|
53
|
-
*/
|
54
|
-
async generateInstructions(
|
55
|
-
baseInstructions: string,
|
56
|
-
context: InstructionContext
|
57
|
-
): Promise<GeneratedInstructions> {
|
58
|
-
// Apply variable substitution to base instructions
|
59
|
-
const substitutedInstructions = this.applyVariableSubstitution(
|
60
|
-
baseInstructions,
|
61
|
-
context.conversationContext.projectPath
|
62
|
-
);
|
63
|
-
|
64
|
-
// Get plan file guidance
|
65
|
-
const planFileGuidance = this.planManager.generatePlanFileGuidance(
|
66
|
-
context.phase
|
67
|
-
);
|
68
|
-
|
69
|
-
// Enhance base instructions with context-specific guidance
|
70
|
-
const enhancedInstructions = await this.enhanceInstructions(
|
71
|
-
substitutedInstructions,
|
72
|
-
context,
|
73
|
-
planFileGuidance
|
74
|
-
);
|
75
|
-
|
76
|
-
return {
|
77
|
-
instructions: enhancedInstructions,
|
78
|
-
planFileGuidance,
|
79
|
-
metadata: {
|
80
|
-
phase: context.phase,
|
81
|
-
planFilePath: context.conversationContext.planFilePath,
|
82
|
-
transitionReason: context.transitionReason,
|
83
|
-
isModeled: context.isModeled,
|
84
|
-
},
|
85
|
-
};
|
86
|
-
}
|
87
|
-
|
88
|
-
/**
|
89
|
-
* Apply variable substitution to instructions
|
90
|
-
* Replaces project artifact variables with actual file paths
|
91
|
-
*/
|
92
|
-
private applyVariableSubstitution(
|
93
|
-
instructions: string,
|
94
|
-
projectPath: string
|
95
|
-
): string {
|
96
|
-
const substitutions =
|
97
|
-
this.projectDocsManager.getVariableSubstitutions(projectPath);
|
98
|
-
|
99
|
-
let result = instructions;
|
100
|
-
for (const [variable, value] of Object.entries(substitutions)) {
|
101
|
-
// Use global replace to handle multiple occurrences
|
102
|
-
result = result.replace(
|
103
|
-
new RegExp(this.escapeRegExp(variable), 'g'),
|
104
|
-
value
|
105
|
-
);
|
106
|
-
}
|
107
|
-
|
108
|
-
return result;
|
109
|
-
}
|
110
|
-
|
111
|
-
/**
|
112
|
-
* Escape special regex characters in variable names
|
113
|
-
*/
|
114
|
-
private escapeRegExp(string: string): string {
|
115
|
-
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
116
|
-
}
|
117
|
-
|
118
|
-
/**
|
119
|
-
* Enhance base instructions with context-specific information
|
120
|
-
*/
|
121
|
-
private async enhanceInstructions(
|
122
|
-
baseInstructions: string,
|
123
|
-
context: InstructionContext,
|
124
|
-
_planFileGuidance: string
|
125
|
-
): Promise<string> {
|
126
|
-
const {
|
127
|
-
phase,
|
128
|
-
conversationContext,
|
129
|
-
transitionReason,
|
130
|
-
isModeled,
|
131
|
-
planFileExists,
|
132
|
-
} = context;
|
133
|
-
|
134
|
-
// Build plan-file-referential instructions
|
135
|
-
let enhanced = `Check your plan file at \`${conversationContext.planFilePath}\` and focus on the "${this.capitalizePhase(phase)}" section.
|
136
|
-
|
137
|
-
${baseInstructions}
|
138
|
-
|
139
|
-
**Plan File Guidance:**
|
140
|
-
- Work on the tasks listed in the ${this.capitalizePhase(phase)} section
|
141
|
-
- Mark completed tasks with [x] as you finish them
|
142
|
-
- Add new tasks as they are identified during your work with the user
|
143
|
-
- Update the "Key Decisions" section with important choices made
|
144
|
-
- Add relevant notes to help maintain context`;
|
145
|
-
|
146
|
-
// Add project context
|
147
|
-
enhanced += `\n\n**Project Context:**
|
148
|
-
- Project: ${conversationContext.projectPath}
|
149
|
-
- Branch: ${conversationContext.gitBranch}
|
150
|
-
- Current Phase: ${phase}`;
|
151
|
-
|
152
|
-
// Add transition context if this is a modeled transition
|
153
|
-
if (isModeled && transitionReason) {
|
154
|
-
enhanced += `\n\n**Phase Context:**
|
155
|
-
- ${transitionReason}`;
|
156
|
-
}
|
157
|
-
|
158
|
-
// Add plan file creation note if needed
|
159
|
-
if (!planFileExists) {
|
160
|
-
enhanced +=
|
161
|
-
'\n\n**Note**: Plan file will be created when you first update it.';
|
162
|
-
}
|
163
|
-
|
164
|
-
return enhanced;
|
165
|
-
}
|
166
|
-
|
167
|
-
/**
|
168
|
-
* Get phase-specific contextual information based on state machine
|
169
|
-
*/
|
170
|
-
private getPhaseSpecificContext(phase: string): string {
|
171
|
-
if (this.stateMachine) {
|
172
|
-
const phaseDefinition = this.stateMachine.states[phase];
|
173
|
-
if (phaseDefinition) {
|
174
|
-
return `**Context**: ${phaseDefinition.description}`;
|
175
|
-
}
|
176
|
-
}
|
177
|
-
|
178
|
-
throw new Error(
|
179
|
-
`State machine not set or unknown phase: ${phase}. This should not happen as state machine is always loaded.`
|
180
|
-
);
|
181
|
-
}
|
182
|
-
|
183
|
-
/**
|
184
|
-
* Get default phase context for standard phases
|
185
|
-
/**
|
186
|
-
* Get phase-specific reminders and best practices based on state machine
|
187
|
-
*/
|
188
|
-
private getPhaseReminders(phase: string): string {
|
189
|
-
if (this.stateMachine) {
|
190
|
-
const phaseDefinition = this.stateMachine.states[phase];
|
191
|
-
if (phaseDefinition) {
|
192
|
-
return `**Remember**: \n- Focus on: ${phaseDefinition.description}\n- Update plan file with ${phase} progress\n- Mark completed tasks with [x]\n- Stay focused on current phase objectives`;
|
193
|
-
}
|
194
|
-
}
|
195
|
-
|
196
|
-
throw new Error(
|
197
|
-
`State machine not set or unknown phase: ${phase}. This should not happen as state machine is always loaded.`
|
198
|
-
);
|
199
|
-
}
|
200
|
-
|
201
|
-
/**
|
202
|
-
* Capitalize phase name for display
|
203
|
-
*/
|
204
|
-
private capitalizePhase(phase: string): string {
|
205
|
-
return phase
|
206
|
-
.split('_')
|
207
|
-
.map(word => word.charAt(0).toUpperCase() + word.slice(1))
|
208
|
-
.join(' ');
|
209
|
-
}
|
210
|
-
}
|
@@ -1,109 +0,0 @@
|
|
1
|
-
/**
|
2
|
-
* Interaction Logger module
|
3
|
-
*
|
4
|
-
* Handles logging of tool interactions to the database for auditing and debugging.
|
5
|
-
*/
|
6
|
-
|
7
|
-
import { Database } from './database.js';
|
8
|
-
import { createLogger } from './logger.js';
|
9
|
-
|
10
|
-
import type { InteractionLog } from './types.js';
|
11
|
-
|
12
|
-
const logger = createLogger('InteractionLogger');
|
13
|
-
|
14
|
-
/**
|
15
|
-
* Handles logging of tool interactions to the database
|
16
|
-
*/
|
17
|
-
export class InteractionLogger {
|
18
|
-
private database: Database;
|
19
|
-
|
20
|
-
/**
|
21
|
-
* Create a new InteractionLogger
|
22
|
-
*
|
23
|
-
* @param database - Database instance to use for logging
|
24
|
-
*/
|
25
|
-
constructor(database: Database) {
|
26
|
-
this.database = database;
|
27
|
-
logger.debug('InteractionLogger initialized');
|
28
|
-
}
|
29
|
-
|
30
|
-
/**
|
31
|
-
* Log an interaction with a tool
|
32
|
-
*
|
33
|
-
* @param conversationId - ID of the conversation
|
34
|
-
* @param toolName - Name of the tool that was called
|
35
|
-
* @param inputParams - Input parameters to the tool (will be stringified)
|
36
|
-
* @param responseData - Response data from the tool (will be stringified)
|
37
|
-
* @param currentPhase - Current development phase
|
38
|
-
* @returns Promise that resolves when the log is saved
|
39
|
-
*/
|
40
|
-
async logInteraction(
|
41
|
-
conversationId: string,
|
42
|
-
toolName: string,
|
43
|
-
inputParams: unknown,
|
44
|
-
responseData: unknown,
|
45
|
-
currentPhase: string
|
46
|
-
): Promise<void> {
|
47
|
-
logger.debug('Logging interaction', {
|
48
|
-
conversationId,
|
49
|
-
toolName,
|
50
|
-
currentPhase,
|
51
|
-
});
|
52
|
-
|
53
|
-
try {
|
54
|
-
const timestamp = new Date().toISOString();
|
55
|
-
|
56
|
-
const log: InteractionLog = {
|
57
|
-
conversationId,
|
58
|
-
toolName,
|
59
|
-
inputParams: JSON.stringify(inputParams),
|
60
|
-
responseData: JSON.stringify(responseData),
|
61
|
-
currentPhase,
|
62
|
-
timestamp,
|
63
|
-
};
|
64
|
-
|
65
|
-
await this.database.logInteraction(log);
|
66
|
-
|
67
|
-
logger.info('Interaction logged successfully', {
|
68
|
-
conversationId,
|
69
|
-
toolName,
|
70
|
-
timestamp,
|
71
|
-
});
|
72
|
-
} catch (error) {
|
73
|
-
logger.error('Failed to log interaction', error as Error, {
|
74
|
-
conversationId,
|
75
|
-
toolName,
|
76
|
-
});
|
77
|
-
// Don't throw the error - logging should not break the main flow
|
78
|
-
}
|
79
|
-
}
|
80
|
-
|
81
|
-
/**
|
82
|
-
* Get all interactions for a specific conversation
|
83
|
-
*
|
84
|
-
* @param conversationId - ID of the conversation to get logs for
|
85
|
-
* @returns Promise that resolves to an array of interaction logs
|
86
|
-
*/
|
87
|
-
async getInteractionsByConversationId(
|
88
|
-
conversationId: string
|
89
|
-
): Promise<InteractionLog[]> {
|
90
|
-
logger.debug('Getting interactions by conversation ID', { conversationId });
|
91
|
-
|
92
|
-
try {
|
93
|
-
const logs =
|
94
|
-
await this.database.getInteractionsByConversationId(conversationId);
|
95
|
-
|
96
|
-
logger.info('Retrieved interaction logs', {
|
97
|
-
conversationId,
|
98
|
-
count: logs.length,
|
99
|
-
});
|
100
|
-
|
101
|
-
return logs;
|
102
|
-
} catch (error) {
|
103
|
-
logger.error('Failed to get interaction logs', error as Error, {
|
104
|
-
conversationId,
|
105
|
-
});
|
106
|
-
throw error;
|
107
|
-
}
|
108
|
-
}
|
109
|
-
}
|