@codemcp/workflows-core 3.1.21 → 3.2.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/package.json +9 -5
- 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
package/src/database.ts
DELETED
@@ -1,427 +0,0 @@
|
|
1
|
-
/**
|
2
|
-
* Database Manager
|
3
|
-
*
|
4
|
-
* Handles SQLite database operations for conversation state persistence.
|
5
|
-
* Uses @sqlite.org/sqlite-wasm for reliable cross-platform WebAssembly bindings.
|
6
|
-
*/
|
7
|
-
|
8
|
-
import sqlite3InitModule, {
|
9
|
-
type Database as SqliteDatabase,
|
10
|
-
type Sqlite3Static,
|
11
|
-
} from '@sqlite.org/sqlite-wasm';
|
12
|
-
import { mkdir } from 'node:fs/promises';
|
13
|
-
import { dirname } from 'node:path';
|
14
|
-
import { createLogger } from './logger.js';
|
15
|
-
import type { ConversationState, InteractionLog } from './types.js';
|
16
|
-
|
17
|
-
const logger = createLogger('Database');
|
18
|
-
|
19
|
-
/**
|
20
|
-
* Database connection and operations manager
|
21
|
-
*/
|
22
|
-
export class Database {
|
23
|
-
private db: SqliteDatabase | null = null;
|
24
|
-
private sqlite3: Sqlite3Static | null = null;
|
25
|
-
private dbPath: string;
|
26
|
-
|
27
|
-
constructor(dbPath: string) {
|
28
|
-
this.dbPath = dbPath;
|
29
|
-
}
|
30
|
-
|
31
|
-
/**
|
32
|
-
* Initialize database connection and create tables
|
33
|
-
*/
|
34
|
-
async initialize(): Promise<void> {
|
35
|
-
try {
|
36
|
-
// Initialize SQLite WASM
|
37
|
-
this.sqlite3 = await sqlite3InitModule();
|
38
|
-
|
39
|
-
// Always use in-memory database (sqlite-wasm Node.js limitation)
|
40
|
-
this.db = new this.sqlite3.oo1.DB();
|
41
|
-
logger.debug('Database connection established (in-memory)', {
|
42
|
-
originalPath: this.dbPath,
|
43
|
-
});
|
44
|
-
|
45
|
-
// Create tables
|
46
|
-
await this.createTables();
|
47
|
-
|
48
|
-
// Load existing data from file if it exists
|
49
|
-
if (this.dbPath !== ':memory:' && this.dbPath) {
|
50
|
-
await this.loadFromFile();
|
51
|
-
}
|
52
|
-
|
53
|
-
logger.info('Database initialized successfully', { dbPath: this.dbPath });
|
54
|
-
} catch (error) {
|
55
|
-
logger.error('Failed to initialize database', error as Error);
|
56
|
-
throw error;
|
57
|
-
}
|
58
|
-
}
|
59
|
-
|
60
|
-
/**
|
61
|
-
* Load database content from file
|
62
|
-
*/
|
63
|
-
private async loadFromFile(): Promise<void> {
|
64
|
-
if (!this.db || !this.dbPath || this.dbPath === ':memory:') {
|
65
|
-
return;
|
66
|
-
}
|
67
|
-
|
68
|
-
try {
|
69
|
-
const { readFile, access } = await import('node:fs/promises');
|
70
|
-
await access(this.dbPath);
|
71
|
-
|
72
|
-
const data = await readFile(this.dbPath);
|
73
|
-
if (data.length > 0) {
|
74
|
-
// Close current in-memory DB and create new one from file data
|
75
|
-
this.db.close();
|
76
|
-
// Create new DB and deserialize data into it
|
77
|
-
|
78
|
-
//eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
79
|
-
this.db = new this.sqlite3!.oo1.DB();
|
80
|
-
if (!this.db.pointer) {
|
81
|
-
throw new Error('Failed to create database');
|
82
|
-
}
|
83
|
-
|
84
|
-
// Convert Buffer to Uint8Array
|
85
|
-
const uint8Data = new Uint8Array(data);
|
86
|
-
|
87
|
-
//eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
88
|
-
const wasmPtr = this.sqlite3!.wasm.allocFromTypedArray(uint8Data);
|
89
|
-
|
90
|
-
//eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
91
|
-
this.sqlite3!.capi.sqlite3_deserialize(
|
92
|
-
this.db.pointer,
|
93
|
-
'main',
|
94
|
-
wasmPtr,
|
95
|
-
data.length,
|
96
|
-
data.length,
|
97
|
-
0x01 // SQLITE_DESERIALIZE_FREEONCLOSE
|
98
|
-
);
|
99
|
-
logger.debug('Loaded database from file', {
|
100
|
-
dbPath: this.dbPath,
|
101
|
-
size: data.length,
|
102
|
-
});
|
103
|
-
}
|
104
|
-
} catch {
|
105
|
-
// File doesn't exist - that's OK for new databases
|
106
|
-
logger.debug('No existing database file to load', {
|
107
|
-
dbPath: this.dbPath,
|
108
|
-
});
|
109
|
-
}
|
110
|
-
}
|
111
|
-
|
112
|
-
/**
|
113
|
-
* Save database content to file
|
114
|
-
*/
|
115
|
-
private async saveToFile(): Promise<void> {
|
116
|
-
if (!this.db || !this.dbPath || this.dbPath === ':memory:') {
|
117
|
-
return;
|
118
|
-
}
|
119
|
-
|
120
|
-
try {
|
121
|
-
const { writeFile } = await import('node:fs/promises');
|
122
|
-
const dbDir = dirname(this.dbPath);
|
123
|
-
await mkdir(dbDir, { recursive: true });
|
124
|
-
|
125
|
-
// Export database to Uint8Array and save to file
|
126
|
-
if (!this.db.pointer) {
|
127
|
-
throw new Error('Database pointer is invalid');
|
128
|
-
}
|
129
|
-
//eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
130
|
-
const data = this.sqlite3!.capi.sqlite3_js_db_export(this.db.pointer);
|
131
|
-
await writeFile(this.dbPath, data);
|
132
|
-
logger.debug('Saved database to file', {
|
133
|
-
dbPath: this.dbPath,
|
134
|
-
size: data.length,
|
135
|
-
});
|
136
|
-
} catch (error) {
|
137
|
-
logger.warn('Failed to save database to file', {
|
138
|
-
error: error as Error,
|
139
|
-
dbPath: this.dbPath,
|
140
|
-
});
|
141
|
-
}
|
142
|
-
}
|
143
|
-
|
144
|
-
/**
|
145
|
-
* Create database tables if they don't exist
|
146
|
-
*/
|
147
|
-
private async createTables(): Promise<void> {
|
148
|
-
if (!this.db) {
|
149
|
-
throw new Error('Database not initialized');
|
150
|
-
}
|
151
|
-
|
152
|
-
const createConversationStateTable = `
|
153
|
-
CREATE TABLE IF NOT EXISTS conversation_state (
|
154
|
-
conversationId TEXT PRIMARY KEY,
|
155
|
-
projectPath TEXT NOT NULL,
|
156
|
-
gitBranch TEXT NOT NULL,
|
157
|
-
currentPhase TEXT NOT NULL,
|
158
|
-
planFilePath TEXT NOT NULL,
|
159
|
-
workflowName TEXT NOT NULL,
|
160
|
-
gitCommitConfig TEXT,
|
161
|
-
requireReviewsBeforePhaseTransition INTEGER NOT NULL DEFAULT 0,
|
162
|
-
createdAt TEXT NOT NULL,
|
163
|
-
updatedAt TEXT NOT NULL
|
164
|
-
)
|
165
|
-
`;
|
166
|
-
|
167
|
-
const createInteractionLogTable = `
|
168
|
-
CREATE TABLE IF NOT EXISTS interaction_log (
|
169
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
170
|
-
conversationId TEXT NOT NULL,
|
171
|
-
toolName TEXT NOT NULL,
|
172
|
-
inputParams TEXT NOT NULL,
|
173
|
-
responseData TEXT NOT NULL,
|
174
|
-
currentPhase TEXT NOT NULL,
|
175
|
-
timestamp TEXT NOT NULL,
|
176
|
-
FOREIGN KEY (conversationId) REFERENCES conversation_state(conversationId)
|
177
|
-
)
|
178
|
-
`;
|
179
|
-
|
180
|
-
this.db.exec(createConversationStateTable);
|
181
|
-
this.db.exec(createInteractionLogTable);
|
182
|
-
|
183
|
-
logger.debug('Database tables created');
|
184
|
-
}
|
185
|
-
|
186
|
-
/**
|
187
|
-
* Save conversation state to database
|
188
|
-
*/
|
189
|
-
async saveConversationState(state: ConversationState): Promise<void> {
|
190
|
-
if (!this.db) {
|
191
|
-
throw new Error('Database not initialized');
|
192
|
-
}
|
193
|
-
|
194
|
-
this.db.exec({
|
195
|
-
sql: `INSERT OR REPLACE INTO conversation_state
|
196
|
-
(conversationId, projectPath, gitBranch, currentPhase, planFilePath, workflowName,
|
197
|
-
gitCommitConfig, requireReviewsBeforePhaseTransition, createdAt, updatedAt)
|
198
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
199
|
-
bind: [
|
200
|
-
state.conversationId,
|
201
|
-
state.projectPath,
|
202
|
-
state.gitBranch,
|
203
|
-
state.currentPhase,
|
204
|
-
state.planFilePath,
|
205
|
-
state.workflowName,
|
206
|
-
state.gitCommitConfig ? JSON.stringify(state.gitCommitConfig) : null,
|
207
|
-
state.requireReviewsBeforePhaseTransition ? 1 : 0,
|
208
|
-
state.createdAt,
|
209
|
-
state.updatedAt,
|
210
|
-
],
|
211
|
-
});
|
212
|
-
|
213
|
-
// Persist to file
|
214
|
-
await this.saveToFile();
|
215
|
-
|
216
|
-
logger.debug('Conversation state saved', {
|
217
|
-
conversationId: state.conversationId,
|
218
|
-
currentPhase: state.currentPhase,
|
219
|
-
});
|
220
|
-
}
|
221
|
-
|
222
|
-
/**
|
223
|
-
* Get conversation state by ID
|
224
|
-
*/
|
225
|
-
async getConversationState(
|
226
|
-
conversationId: string
|
227
|
-
): Promise<ConversationState | null> {
|
228
|
-
if (!this.db) {
|
229
|
-
throw new Error('Database not initialized');
|
230
|
-
}
|
231
|
-
|
232
|
-
const result = this.db.exec({
|
233
|
-
sql: 'SELECT * FROM conversation_state WHERE conversationId = ?',
|
234
|
-
bind: [conversationId],
|
235
|
-
returnValue: 'resultRows',
|
236
|
-
});
|
237
|
-
|
238
|
-
if (!result || result.length === 0) {
|
239
|
-
return null;
|
240
|
-
}
|
241
|
-
|
242
|
-
const row = result[0];
|
243
|
-
return {
|
244
|
-
conversationId: row[0] as string,
|
245
|
-
projectPath: row[1] as string,
|
246
|
-
gitBranch: row[2] as string,
|
247
|
-
currentPhase: row[3] as string,
|
248
|
-
planFilePath: row[4] as string,
|
249
|
-
workflowName: row[5] as string,
|
250
|
-
gitCommitConfig: row[6] ? JSON.parse(row[6] as string) : null,
|
251
|
-
requireReviewsBeforePhaseTransition: Boolean(row[7]),
|
252
|
-
createdAt: row[8] as string,
|
253
|
-
updatedAt: row[9] as string,
|
254
|
-
};
|
255
|
-
}
|
256
|
-
|
257
|
-
/**
|
258
|
-
* Get all conversation states
|
259
|
-
*/
|
260
|
-
async getAllConversationStates(): Promise<ConversationState[]> {
|
261
|
-
if (!this.db) {
|
262
|
-
throw new Error('Database not initialized');
|
263
|
-
}
|
264
|
-
|
265
|
-
const result = this.db.exec({
|
266
|
-
sql: 'SELECT * FROM conversation_state ORDER BY updatedAt DESC',
|
267
|
-
returnValue: 'resultRows',
|
268
|
-
});
|
269
|
-
|
270
|
-
if (!result) {
|
271
|
-
return [];
|
272
|
-
}
|
273
|
-
|
274
|
-
return result.map(row => ({
|
275
|
-
conversationId: row[0] as string,
|
276
|
-
projectPath: row[1] as string,
|
277
|
-
gitBranch: row[2] as string,
|
278
|
-
currentPhase: row[3] as string,
|
279
|
-
planFilePath: row[4] as string,
|
280
|
-
workflowName: row[5] as string,
|
281
|
-
gitCommitConfig: row[6] ? JSON.parse(row[6] as string) : null,
|
282
|
-
requireReviewsBeforePhaseTransition: Boolean(row[7]),
|
283
|
-
createdAt: row[8] as string,
|
284
|
-
updatedAt: row[9] as string,
|
285
|
-
}));
|
286
|
-
}
|
287
|
-
|
288
|
-
/**
|
289
|
-
* Delete conversation state
|
290
|
-
*/
|
291
|
-
async deleteConversationState(conversationId: string): Promise<boolean> {
|
292
|
-
if (!this.db) {
|
293
|
-
throw new Error('Database not initialized');
|
294
|
-
}
|
295
|
-
|
296
|
-
this.db.exec({
|
297
|
-
sql: 'DELETE FROM conversation_state WHERE conversationId = ?',
|
298
|
-
bind: [conversationId],
|
299
|
-
});
|
300
|
-
|
301
|
-
// Persist to file
|
302
|
-
await this.saveToFile();
|
303
|
-
|
304
|
-
logger.debug('Conversation state deleted', { conversationId });
|
305
|
-
return true;
|
306
|
-
}
|
307
|
-
|
308
|
-
/**
|
309
|
-
* Log interaction
|
310
|
-
*/
|
311
|
-
async logInteraction(log: InteractionLog): Promise<void> {
|
312
|
-
if (!this.db) {
|
313
|
-
throw new Error('Database not initialized');
|
314
|
-
}
|
315
|
-
|
316
|
-
this.db.exec({
|
317
|
-
sql: `INSERT INTO interaction_log
|
318
|
-
(conversationId, toolName, inputParams, responseData, currentPhase, timestamp)
|
319
|
-
VALUES (?, ?, ?, ?, ?, ?)`,
|
320
|
-
bind: [
|
321
|
-
log.conversationId,
|
322
|
-
log.toolName,
|
323
|
-
JSON.stringify(log.inputParams),
|
324
|
-
JSON.stringify(log.responseData),
|
325
|
-
log.currentPhase,
|
326
|
-
log.timestamp,
|
327
|
-
],
|
328
|
-
});
|
329
|
-
|
330
|
-
// Persist to file
|
331
|
-
await this.saveToFile();
|
332
|
-
|
333
|
-
logger.debug('Interaction logged', {
|
334
|
-
conversationId: log.conversationId,
|
335
|
-
toolName: log.toolName,
|
336
|
-
});
|
337
|
-
}
|
338
|
-
|
339
|
-
/**
|
340
|
-
* Get interaction logs for a conversation
|
341
|
-
*/
|
342
|
-
async getInteractionLogs(conversationId: string): Promise<InteractionLog[]> {
|
343
|
-
if (!this.db) {
|
344
|
-
throw new Error('Database not initialized');
|
345
|
-
}
|
346
|
-
|
347
|
-
const result = this.db.exec({
|
348
|
-
sql: 'SELECT * FROM interaction_log WHERE conversationId = ? ORDER BY timestamp ASC',
|
349
|
-
bind: [conversationId],
|
350
|
-
returnValue: 'resultRows',
|
351
|
-
});
|
352
|
-
|
353
|
-
if (!result) {
|
354
|
-
return [];
|
355
|
-
}
|
356
|
-
|
357
|
-
return result.map(row => ({
|
358
|
-
id: row[0] as number,
|
359
|
-
conversationId: row[1] as string,
|
360
|
-
toolName: row[2] as string,
|
361
|
-
inputParams: JSON.parse(row[3] as string),
|
362
|
-
responseData: JSON.parse(row[4] as string),
|
363
|
-
currentPhase: row[5] as string,
|
364
|
-
timestamp: row[6] as string,
|
365
|
-
}));
|
366
|
-
}
|
367
|
-
|
368
|
-
/**
|
369
|
-
* Get interaction logs for a conversation (alias for compatibility)
|
370
|
-
*/
|
371
|
-
async getInteractionsByConversationId(
|
372
|
-
conversationId: string
|
373
|
-
): Promise<InteractionLog[]> {
|
374
|
-
return this.getInteractionLogs(conversationId);
|
375
|
-
}
|
376
|
-
|
377
|
-
/**
|
378
|
-
* Soft delete interaction logs (for compatibility - actually deletes them)
|
379
|
-
*/
|
380
|
-
async softDeleteInteractionLogs(conversationId: string): Promise<void> {
|
381
|
-
if (!this.db) {
|
382
|
-
throw new Error('Database not initialized');
|
383
|
-
}
|
384
|
-
|
385
|
-
this.db.exec({
|
386
|
-
sql: 'DELETE FROM interaction_log WHERE conversationId = ?',
|
387
|
-
bind: [conversationId],
|
388
|
-
});
|
389
|
-
|
390
|
-
// Persist to file
|
391
|
-
await this.saveToFile();
|
392
|
-
|
393
|
-
logger.debug('Interaction logs deleted', { conversationId });
|
394
|
-
}
|
395
|
-
|
396
|
-
/**
|
397
|
-
* Reset conversation state (for testing)
|
398
|
-
*/
|
399
|
-
async resetConversationState(conversationId: string): Promise<void> {
|
400
|
-
if (!this.db) {
|
401
|
-
throw new Error('Database not initialized');
|
402
|
-
}
|
403
|
-
|
404
|
-
const resetAt = new Date().toISOString();
|
405
|
-
|
406
|
-
this.db.exec({
|
407
|
-
sql: 'UPDATE conversation_state SET updatedAt = ? WHERE conversationId = ?',
|
408
|
-
bind: [resetAt, conversationId],
|
409
|
-
});
|
410
|
-
|
411
|
-
// Persist to file
|
412
|
-
await this.saveToFile();
|
413
|
-
|
414
|
-
logger.debug('Conversation state reset', { conversationId, resetAt });
|
415
|
-
}
|
416
|
-
|
417
|
-
/**
|
418
|
-
* Close database connection
|
419
|
-
*/
|
420
|
-
async close(): Promise<void> {
|
421
|
-
if (this.db) {
|
422
|
-
this.db.close();
|
423
|
-
this.db = null;
|
424
|
-
logger.debug('Database connection closed');
|
425
|
-
}
|
426
|
-
}
|
427
|
-
}
|