sqlew 3.5.3 → 3.6.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/CHANGELOG.md +247 -1772
- package/README.md +70 -304
- package/assets/config.example.toml +97 -0
- package/dist/adapters/index.d.ts +11 -0
- package/dist/adapters/index.d.ts.map +1 -0
- package/dist/adapters/index.js +21 -0
- package/dist/adapters/index.js.map +1 -0
- package/dist/adapters/mysql-adapter.d.ts +31 -0
- package/dist/adapters/mysql-adapter.d.ts.map +1 -0
- package/dist/adapters/mysql-adapter.js +63 -0
- package/dist/adapters/mysql-adapter.js.map +1 -0
- package/dist/adapters/postgresql-adapter.d.ts +31 -0
- package/dist/adapters/postgresql-adapter.d.ts.map +1 -0
- package/dist/adapters/postgresql-adapter.js +63 -0
- package/dist/adapters/postgresql-adapter.js.map +1 -0
- package/dist/adapters/sqlite-adapter.d.ts +37 -0
- package/dist/adapters/sqlite-adapter.d.ts.map +1 -0
- package/dist/adapters/sqlite-adapter.js +129 -0
- package/dist/adapters/sqlite-adapter.js.map +1 -0
- package/dist/adapters/types.d.ts +33 -0
- package/dist/adapters/types.d.ts.map +1 -0
- package/dist/adapters/types.js +2 -0
- package/dist/adapters/types.js.map +1 -0
- package/dist/cli.js +55 -54
- package/dist/cli.js.map +1 -1
- package/dist/config/example-generator.d.ts +11 -0
- package/dist/config/example-generator.d.ts.map +1 -0
- package/dist/config/example-generator.js +48 -0
- package/dist/config/example-generator.js.map +1 -0
- package/dist/config/loader.d.ts.map +1 -1
- package/dist/config/loader.js +4 -0
- package/dist/config/loader.js.map +1 -1
- package/dist/config/types.d.ts +9 -0
- package/dist/config/types.d.ts.map +1 -1
- package/dist/config/types.js.map +1 -1
- package/dist/database.d.ts +44 -122
- package/dist/database.d.ts.map +1 -1
- package/dist/database.js +145 -416
- package/dist/database.js.map +1 -1
- package/dist/index.js +215 -185
- package/dist/index.js.map +1 -1
- package/dist/knexfile.d.ts +6 -0
- package/dist/knexfile.d.ts.map +1 -0
- package/dist/knexfile.js +85 -0
- package/dist/knexfile.js.map +1 -0
- package/dist/migrations/add-help-system-tables.d.ts +35 -0
- package/dist/migrations/add-help-system-tables.d.ts.map +1 -0
- package/dist/migrations/add-help-system-tables.js +206 -0
- package/dist/migrations/add-help-system-tables.js.map +1 -0
- package/dist/migrations/add-token-tracking.d.ts +28 -0
- package/dist/migrations/add-token-tracking.d.ts.map +1 -0
- package/dist/migrations/add-token-tracking.js +108 -0
- package/dist/migrations/add-token-tracking.js.map +1 -0
- package/dist/migrations/index.d.ts +25 -12
- package/dist/migrations/index.d.ts.map +1 -1
- package/dist/migrations/index.js +147 -20
- package/dist/migrations/index.js.map +1 -1
- package/dist/migrations/knex/20251025020452_create_master_tables.d.ts +4 -0
- package/dist/migrations/knex/20251025020452_create_master_tables.d.ts.map +1 -0
- package/dist/migrations/knex/20251025020452_create_master_tables.js +65 -0
- package/dist/migrations/knex/20251025020452_create_master_tables.js.map +1 -0
- package/dist/migrations/knex/20251025021152_create_transaction_tables.d.ts +4 -0
- package/dist/migrations/knex/20251025021152_create_transaction_tables.d.ts.map +1 -0
- package/dist/migrations/knex/20251025021152_create_transaction_tables.js +235 -0
- package/dist/migrations/knex/20251025021152_create_transaction_tables.js.map +1 -0
- package/dist/migrations/knex/20251025021351_create_indexes.d.ts +4 -0
- package/dist/migrations/knex/20251025021351_create_indexes.d.ts.map +1 -0
- package/dist/migrations/knex/20251025021351_create_indexes.js +62 -0
- package/dist/migrations/knex/20251025021351_create_indexes.js.map +1 -0
- package/dist/migrations/knex/20251025021416_seed_master_data.d.ts +4 -0
- package/dist/migrations/knex/20251025021416_seed_master_data.d.ts.map +1 -0
- package/dist/migrations/knex/20251025021416_seed_master_data.js +58 -0
- package/dist/migrations/knex/20251025021416_seed_master_data.js.map +1 -0
- package/dist/migrations/knex/20251025070349_create_views.d.ts +4 -0
- package/dist/migrations/knex/20251025070349_create_views.d.ts.map +1 -0
- package/dist/migrations/knex/20251025070349_create_views.js +143 -0
- package/dist/migrations/knex/20251025070349_create_views.js.map +1 -0
- package/dist/migrations/knex/20251025081221_add_link_type_to_task_decision_links.d.ts +4 -0
- package/dist/migrations/knex/20251025081221_add_link_type_to_task_decision_links.d.ts.map +1 -0
- package/dist/migrations/knex/20251025081221_add_link_type_to_task_decision_links.js +15 -0
- package/dist/migrations/knex/20251025081221_add_link_type_to_task_decision_links.js.map +1 -0
- package/dist/migrations/knex/20251025082220_fix_task_dependencies_columns.d.ts +8 -0
- package/dist/migrations/knex/20251025082220_fix_task_dependencies_columns.d.ts.map +1 -0
- package/dist/migrations/knex/20251025082220_fix_task_dependencies_columns.js +12 -0
- package/dist/migrations/knex/20251025082220_fix_task_dependencies_columns.js.map +1 -0
- package/dist/migrations/knex/20251025090000_create_help_system_tables.d.ts +19 -0
- package/dist/migrations/knex/20251025090000_create_help_system_tables.d.ts.map +1 -0
- package/dist/migrations/knex/20251025090000_create_help_system_tables.js +115 -0
- package/dist/migrations/knex/20251025090000_create_help_system_tables.js.map +1 -0
- package/dist/migrations/knex/20251025090100_seed_help_categories_and_use_cases.d.ts +13 -0
- package/dist/migrations/knex/20251025090100_seed_help_categories_and_use_cases.d.ts.map +1 -0
- package/dist/migrations/knex/20251025090100_seed_help_categories_and_use_cases.js +377 -0
- package/dist/migrations/knex/20251025090100_seed_help_categories_and_use_cases.js.map +1 -0
- package/dist/migrations/knex/20251025100000_seed_help_metadata.d.ts +15 -0
- package/dist/migrations/knex/20251025100000_seed_help_metadata.d.ts.map +1 -0
- package/dist/migrations/knex/20251025100000_seed_help_metadata.js +253 -0
- package/dist/migrations/knex/20251025100000_seed_help_metadata.js.map +1 -0
- package/dist/migrations/knex/20251025100100_seed_remaining_use_cases.d.ts +16 -0
- package/dist/migrations/knex/20251025100100_seed_remaining_use_cases.d.ts.map +1 -0
- package/dist/migrations/knex/20251025100100_seed_remaining_use_cases.js +276 -0
- package/dist/migrations/knex/20251025100100_seed_remaining_use_cases.js.map +1 -0
- package/dist/migrations/knex/20251025120000_add_cascade_to_task_dependencies.d.ts +8 -0
- package/dist/migrations/knex/20251025120000_add_cascade_to_task_dependencies.d.ts.map +1 -0
- package/dist/migrations/knex/20251025120000_add_cascade_to_task_dependencies.js +64 -0
- package/dist/migrations/knex/20251025120000_add_cascade_to_task_dependencies.js.map +1 -0
- package/dist/migrations/seed-help-data.d.ts +48 -0
- package/dist/migrations/seed-help-data.d.ts.map +1 -0
- package/dist/migrations/seed-help-data.js +1466 -0
- package/dist/migrations/seed-help-data.js.map +1 -0
- package/dist/migrations/seed-tool-metadata.d.ts +24 -0
- package/dist/migrations/seed-tool-metadata.d.ts.map +1 -0
- package/dist/migrations/seed-tool-metadata.js +392 -0
- package/dist/migrations/seed-tool-metadata.js.map +1 -0
- package/dist/migrations/v3.6.0-help-system-refactor.d.ts +46 -0
- package/dist/migrations/v3.6.0-help-system-refactor.d.ts.map +1 -0
- package/dist/migrations/v3.6.0-help-system-refactor.js +223 -0
- package/dist/migrations/v3.6.0-help-system-refactor.js.map +1 -0
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +2 -0
- package/dist/schema.js.map +1 -1
- package/dist/tests/git-aware-completion.test.js +89 -70
- package/dist/tests/git-aware-completion.test.js.map +1 -1
- package/dist/tests/help-system.test.d.ts +23 -0
- package/dist/tests/help-system.test.d.ts.map +1 -0
- package/dist/tests/help-system.test.js +374 -0
- package/dist/tests/help-system.test.js.map +1 -0
- package/dist/tests/tasks.auto-pruning-decision-link.test.js +92 -78
- package/dist/tests/tasks.auto-pruning-decision-link.test.js.map +1 -1
- package/dist/tests/tasks.auto-pruning-partial.test.js +106 -95
- package/dist/tests/tasks.auto-pruning-partial.test.js.map +1 -1
- package/dist/tests/tasks.auto-pruning-persistence.test.js +115 -97
- package/dist/tests/tasks.auto-pruning-persistence.test.js.map +1 -1
- package/dist/tests/tasks.auto-pruning-safety.test.js +124 -103
- package/dist/tests/tasks.auto-pruning-safety.test.js.map +1 -1
- package/dist/tests/tasks.dependencies.test.js +338 -307
- package/dist/tests/tasks.dependencies.test.js.map +1 -1
- package/dist/tests/tasks.link-file-backward-compat.test.js +116 -104
- package/dist/tests/tasks.link-file-backward-compat.test.js.map +1 -1
- package/dist/tests/tasks.watch-files-action.test.js +122 -101
- package/dist/tests/tasks.watch-files-action.test.js.map +1 -1
- package/dist/tests/tasks.watch-files-parameter.test.js +105 -94
- package/dist/tests/tasks.watch-files-parameter.test.js.map +1 -1
- package/dist/tests/two-step-git-completion.test.js +176 -133
- package/dist/tests/two-step-git-completion.test.js.map +1 -1
- package/dist/tests/vcs-staging.test.js +1 -1
- package/dist/tests/vcs-staging.test.js.map +1 -1
- package/dist/tools/config.d.ts +9 -6
- package/dist/tools/config.d.ts.map +1 -1
- package/dist/tools/config.js +16 -14
- package/dist/tools/config.js.map +1 -1
- package/dist/tools/constraints.d.ts +10 -7
- package/dist/tools/constraints.d.ts.map +1 -1
- package/dist/tools/constraints.js +66 -48
- package/dist/tools/constraints.js.map +1 -1
- package/dist/tools/context.d.ts +36 -33
- package/dist/tools/context.d.ts.map +1 -1
- package/dist/tools/context.js +374 -330
- package/dist/tools/context.js.map +1 -1
- package/dist/tools/files.d.ts +12 -9
- package/dist/tools/files.d.ts.map +1 -1
- package/dist/tools/files.js +173 -95
- package/dist/tools/files.js.map +1 -1
- package/dist/tools/help-queries.d.ts +130 -0
- package/dist/tools/help-queries.d.ts.map +1 -0
- package/dist/tools/help-queries.js +393 -0
- package/dist/tools/help-queries.js.map +1 -0
- package/dist/tools/messaging.d.ts +14 -11
- package/dist/tools/messaging.d.ts.map +1 -1
- package/dist/tools/messaging.js +217 -133
- package/dist/tools/messaging.js.map +1 -1
- package/dist/tools/tasks.d.ts +18 -16
- package/dist/tools/tasks.d.ts.map +1 -1
- package/dist/tools/tasks.js +513 -439
- package/dist/tools/tasks.js.map +1 -1
- package/dist/tools/utils.d.ts +14 -11
- package/dist/tools/utils.d.ts.map +1 -1
- package/dist/tools/utils.js +86 -121
- package/dist/tools/utils.js.map +1 -1
- package/dist/utils/activity-logging.d.ts +114 -0
- package/dist/utils/activity-logging.d.ts.map +1 -0
- package/dist/utils/activity-logging.js +162 -0
- package/dist/utils/activity-logging.js.map +1 -0
- package/dist/utils/batch.d.ts +2 -2
- package/dist/utils/batch.d.ts.map +1 -1
- package/dist/utils/batch.js +8 -8
- package/dist/utils/batch.js.map +1 -1
- package/dist/utils/cleanup.d.ts +21 -13
- package/dist/utils/cleanup.d.ts.map +1 -1
- package/dist/utils/cleanup.js +31 -24
- package/dist/utils/cleanup.js.map +1 -1
- package/dist/utils/debug-logger.d.ts +44 -0
- package/dist/utils/debug-logger.d.ts.map +1 -0
- package/dist/utils/debug-logger.js +116 -0
- package/dist/utils/debug-logger.js.map +1 -0
- package/dist/utils/help-tracking.d.ts +55 -0
- package/dist/utils/help-tracking.d.ts.map +1 -0
- package/dist/utils/help-tracking.js +88 -0
- package/dist/utils/help-tracking.js.map +1 -0
- package/dist/utils/retention.d.ts +7 -7
- package/dist/utils/retention.d.ts.map +1 -1
- package/dist/utils/retention.js +12 -12
- package/dist/utils/retention.js.map +1 -1
- package/dist/utils/task-stale-detection.d.ts +15 -13
- package/dist/utils/task-stale-detection.d.ts.map +1 -1
- package/dist/utils/task-stale-detection.js +100 -302
- package/dist/utils/task-stale-detection.js.map +1 -1
- package/dist/utils/token-estimation.d.ts +72 -0
- package/dist/utils/token-estimation.d.ts.map +1 -0
- package/dist/utils/token-estimation.js +71 -0
- package/dist/utils/token-estimation.js.map +1 -0
- package/dist/utils/token-logging.d.ts +48 -0
- package/dist/utils/token-logging.d.ts.map +1 -0
- package/dist/utils/token-logging.js +112 -0
- package/dist/utils/token-logging.js.map +1 -0
- package/dist/utils/view-queries.d.ts +34 -0
- package/dist/utils/view-queries.d.ts.map +1 -0
- package/dist/utils/view-queries.js +192 -0
- package/dist/utils/view-queries.js.map +1 -0
- package/dist/watcher/file-watcher.d.ts.map +1 -1
- package/dist/watcher/file-watcher.js +25 -11
- package/dist/watcher/file-watcher.js.map +1 -1
- package/docs/BEST_PRACTICES.md +56 -448
- package/docs/MIGRATION_v3.6.0.md +170 -0
- package/docs/SHARED_CONCEPTS.md +63 -208
- package/docs/TASK_OVERVIEW.md +2 -2
- package/docs/TOOL_SELECTION.md +41 -248
- package/package.json +16 -4
package/dist/database.js
CHANGED
|
@@ -1,230 +1,67 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Database connection and initialization module
|
|
3
|
-
* Handles
|
|
3
|
+
* Handles database setup with Knex.js and DatabaseAdapter pattern
|
|
4
4
|
*/
|
|
5
|
-
import
|
|
6
|
-
import {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
import { DEFAULT_DB_PATH, DB_BUSY_TIMEOUT } from './constants.js';
|
|
10
|
-
import { performAutoCleanup } from './utils/cleanup.js';
|
|
11
|
-
import { runAllMigrations, needsAnyMigrations } from './migrations/index.js';
|
|
12
|
-
import { detectAndTransitionStaleTasks, autoArchiveOldDoneTasks, detectAndTransitionToReview } from './utils/task-stale-detection.js';
|
|
13
|
-
import { loadConfigFile, loadAndFlattenConfig, validateConfig } from './config/loader.js';
|
|
14
|
-
let dbInstance = null;
|
|
5
|
+
import knexConfig from './knexfile.js';
|
|
6
|
+
import { createDatabaseAdapter } from './adapters/index.js';
|
|
7
|
+
// Global adapter instance
|
|
8
|
+
let adapterInstance = null;
|
|
15
9
|
/**
|
|
16
|
-
* Initialize database
|
|
17
|
-
* Creates database file and folder if they don't exist
|
|
18
|
-
* Initializes schema on first run
|
|
19
|
-
*
|
|
20
|
-
* @param dbPath - Optional database path (defaults to .sqlew/sqlew.db)
|
|
21
|
-
* @returns SQLite database instance
|
|
10
|
+
* Initialize database with adapter pattern
|
|
22
11
|
*/
|
|
23
|
-
export function initializeDatabase(
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
return dbInstance;
|
|
27
|
-
}
|
|
28
|
-
try {
|
|
29
|
-
// Load config file to get database path and other settings
|
|
30
|
-
const config = loadConfigFile(configPath);
|
|
31
|
-
// Validate config
|
|
32
|
-
const validation = validateConfig(config);
|
|
33
|
-
if (!validation.valid) {
|
|
34
|
-
console.warn('⚠️ Configuration validation warnings:');
|
|
35
|
-
validation.errors.forEach(err => console.warn(` - ${err}`));
|
|
36
|
-
}
|
|
37
|
-
// Determine database path with priority:
|
|
38
|
-
// 1. CLI argument (dbPath parameter)
|
|
39
|
-
// 2. Config file (config.database.path)
|
|
40
|
-
// 3. Default path (DEFAULT_DB_PATH)
|
|
41
|
-
const finalPath = dbPath || config.database?.path || DEFAULT_DB_PATH;
|
|
42
|
-
// Convert to absolute path if relative
|
|
43
|
-
const absolutePath = isAbsolute(finalPath)
|
|
44
|
-
? finalPath
|
|
45
|
-
: resolve(process.cwd(), finalPath);
|
|
46
|
-
// Create directory if it doesn't exist
|
|
47
|
-
const dbDir = dirname(absolutePath);
|
|
48
|
-
if (!existsSync(dbDir)) {
|
|
49
|
-
mkdirSync(dbDir, { recursive: true });
|
|
50
|
-
console.log(`✓ Created database directory: ${dbDir}`);
|
|
51
|
-
}
|
|
52
|
-
// Open database connection
|
|
53
|
-
const db = new Database(absolutePath, {
|
|
54
|
-
verbose: process.env.DEBUG_SQL ? console.log : undefined,
|
|
55
|
-
});
|
|
56
|
-
// Configure database
|
|
57
|
-
db.pragma('journal_mode = WAL'); // Write-Ahead Logging for better concurrency
|
|
58
|
-
db.pragma('foreign_keys = ON'); // Enforce foreign key constraints
|
|
59
|
-
db.pragma('synchronous = NORMAL'); // Balance between safety and performance
|
|
60
|
-
db.pragma(`busy_timeout = ${DB_BUSY_TIMEOUT}`); // Set busy timeout
|
|
61
|
-
console.log(`✓ Connected to database: ${absolutePath}`);
|
|
62
|
-
// Check if database has existing schema
|
|
63
|
-
const schemaExists = isSchemaInitialized(db);
|
|
64
|
-
if (schemaExists) {
|
|
65
|
-
// Run all pending migrations using orchestrator
|
|
66
|
-
// This handles upgrades from any version (v1.0.0, v1.1.x, v2.0.0, v2.1.x) to latest
|
|
67
|
-
if (needsAnyMigrations(db)) {
|
|
68
|
-
const migrationResults = runAllMigrations(db);
|
|
69
|
-
// Check if any migration failed
|
|
70
|
-
const failed = migrationResults.find(r => !r.success);
|
|
71
|
-
if (failed) {
|
|
72
|
-
console.error('\n❌ ERROR: Migration failed!');
|
|
73
|
-
console.error(failed.message);
|
|
74
|
-
db.close();
|
|
75
|
-
process.exit(1);
|
|
76
|
-
}
|
|
77
|
-
// After table prefix migration, run schema initialization to create views/triggers
|
|
78
|
-
// (tables already exist, CREATE TABLE IF NOT EXISTS will skip them)
|
|
79
|
-
const hadPrefixMigration = migrationResults.some(r => r.message.toLowerCase().includes('table prefix') ||
|
|
80
|
-
r.message.toLowerCase().includes('prefix'));
|
|
81
|
-
if (hadPrefixMigration) {
|
|
82
|
-
console.log('\n→ Creating views and triggers for new schema...');
|
|
83
|
-
initializeSchema(db);
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
// Validate existing schema integrity (after migrations)
|
|
87
|
-
console.log('→ Validating existing database schema...');
|
|
88
|
-
const validation = verifySchemaIntegrity(db);
|
|
89
|
-
if (!validation.valid) {
|
|
90
|
-
// Schema is invalid - display error and exit
|
|
91
|
-
console.error('\n❌ ERROR: Database schema validation failed!');
|
|
92
|
-
console.error('\nThe existing database file has an incompatible schema.');
|
|
93
|
-
console.error(`Database location: ${absolutePath}`);
|
|
94
|
-
if (validation.missing.length > 0) {
|
|
95
|
-
console.error('\n📋 Missing components:');
|
|
96
|
-
validation.missing.forEach(item => console.error(` - ${item}`));
|
|
97
|
-
}
|
|
98
|
-
if (validation.errors.length > 0) {
|
|
99
|
-
console.error('\n⚠️ Validation errors:');
|
|
100
|
-
validation.errors.forEach(error => console.error(` - ${error}`));
|
|
101
|
-
}
|
|
102
|
-
console.error('\n💡 Possible solutions:');
|
|
103
|
-
console.error(' 1. Backup and delete the existing database file to start fresh');
|
|
104
|
-
console.error(' 2. Use a different database path with --db-path option');
|
|
105
|
-
console.error(' 3. Restore from a backup if available\n');
|
|
106
|
-
// Close database and exit
|
|
107
|
-
db.close();
|
|
108
|
-
process.exit(1);
|
|
109
|
-
}
|
|
110
|
-
console.log('✓ Database schema validation passed');
|
|
111
|
-
}
|
|
112
|
-
else {
|
|
113
|
-
// Initialize new schema
|
|
114
|
-
console.log('→ Initializing database schema...');
|
|
115
|
-
initializeSchema(db);
|
|
116
|
-
}
|
|
117
|
-
// Populate database with config file values (if any)
|
|
118
|
-
try {
|
|
119
|
-
const flatConfig = loadAndFlattenConfig(configPath);
|
|
120
|
-
let configUpdates = 0;
|
|
121
|
-
for (const [key, value] of Object.entries(flatConfig)) {
|
|
122
|
-
if (value !== undefined) {
|
|
123
|
-
const stringValue = typeof value === 'boolean' ? (value ? '1' : '0') : String(value);
|
|
124
|
-
db.prepare('INSERT OR REPLACE INTO m_config (key, value) VALUES (?, ?)').run(key, stringValue);
|
|
125
|
-
configUpdates++;
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
if (configUpdates > 0) {
|
|
129
|
-
console.log(`✓ Loaded ${configUpdates} config values from file`);
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
catch (error) {
|
|
133
|
-
console.warn('⚠️ Failed to load config file values:', error instanceof Error ? error.message : String(error));
|
|
134
|
-
}
|
|
135
|
-
// Initialize v3.4.0 config keys (git-aware auto-complete)
|
|
136
|
-
// Use INSERT OR IGNORE to avoid overwriting existing values
|
|
137
|
-
const v340Configs = [
|
|
138
|
-
{ key: 'git_auto_complete_enabled', value: '1' },
|
|
139
|
-
{ key: 'require_all_files_committed', value: '1' },
|
|
140
|
-
{ key: 'stale_review_notification_hours', value: '48' },
|
|
141
|
-
];
|
|
142
|
-
const configInsert = db.prepare('INSERT OR IGNORE INTO m_config (key, value) VALUES (?, ?)');
|
|
143
|
-
for (const config of v340Configs) {
|
|
144
|
-
configInsert.run(config.key, config.value);
|
|
145
|
-
}
|
|
146
|
-
// Initialize v3.5.2 config keys (two-step git-aware workflow)
|
|
147
|
-
// Use INSERT OR IGNORE to avoid overwriting existing values
|
|
148
|
-
const v352Configs = [
|
|
149
|
-
{ key: 'git_auto_complete_on_stage', value: '1' }, // Auto-complete on git add
|
|
150
|
-
{ key: 'git_auto_archive_on_commit', value: '1' }, // Auto-archive on git commit
|
|
151
|
-
{ key: 'require_all_files_staged', value: '1' }, // Require ALL files staged (vs ANY)
|
|
152
|
-
{ key: 'require_all_files_committed_for_archive', value: '1' }, // Require ALL files committed for archiving
|
|
153
|
-
];
|
|
154
|
-
for (const config of v352Configs) {
|
|
155
|
-
configInsert.run(config.key, config.value);
|
|
156
|
-
}
|
|
157
|
-
// Store instance
|
|
158
|
-
dbInstance = db;
|
|
159
|
-
// Perform initial cleanup and task maintenance
|
|
160
|
-
// Run synchronous tasks first
|
|
161
|
-
const staleTransitions = detectAndTransitionStaleTasks(db);
|
|
162
|
-
const archivedTasks = autoArchiveOldDoneTasks(db);
|
|
163
|
-
const cleanupResult = performAutoCleanup(db);
|
|
164
|
-
// Run async smart review detection in background (non-blocking)
|
|
165
|
-
detectAndTransitionToReview(db).then(reviewTransitions => {
|
|
166
|
-
const taskMaintenance = [];
|
|
167
|
-
if (staleTransitions > 0)
|
|
168
|
-
taskMaintenance.push(`${staleTransitions} stale task transitions`);
|
|
169
|
-
if (archivedTasks > 0)
|
|
170
|
-
taskMaintenance.push(`${archivedTasks} done tasks archived`);
|
|
171
|
-
if (reviewTransitions > 0)
|
|
172
|
-
taskMaintenance.push(`${reviewTransitions} quality-based review transitions`);
|
|
173
|
-
if (cleanupResult.messagesDeleted > 0)
|
|
174
|
-
taskMaintenance.push(`${cleanupResult.messagesDeleted} messages`);
|
|
175
|
-
if (cleanupResult.fileChangesDeleted > 0)
|
|
176
|
-
taskMaintenance.push(`${cleanupResult.fileChangesDeleted} file changes`);
|
|
177
|
-
if (taskMaintenance.length > 0) {
|
|
178
|
-
console.log(`✓ Cleanup: ${taskMaintenance.join(', ')}`);
|
|
179
|
-
}
|
|
180
|
-
}).catch(error => {
|
|
181
|
-
console.warn('⚠️ Initial cleanup failed:', error instanceof Error ? error.message : String(error));
|
|
182
|
-
});
|
|
183
|
-
return db;
|
|
184
|
-
}
|
|
185
|
-
catch (error) {
|
|
186
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
187
|
-
throw new Error(`Failed to initialize database: ${message}`);
|
|
12
|
+
export async function initializeDatabase(config) {
|
|
13
|
+
if (adapterInstance) {
|
|
14
|
+
return adapterInstance;
|
|
188
15
|
}
|
|
16
|
+
const dbType = config?.databaseType || 'sqlite';
|
|
17
|
+
const adapter = createDatabaseAdapter(dbType);
|
|
18
|
+
// Determine if running from compiled code (dist/) or source (src/)
|
|
19
|
+
const isCompiledCode = import.meta.url.includes('/dist/');
|
|
20
|
+
const environment = isCompiledCode ? 'production' : 'development';
|
|
21
|
+
// Use config from knexfile or provided config
|
|
22
|
+
const baseConfig = knexConfig[environment] || knexConfig.development;
|
|
23
|
+
const knexConnConfig = config?.connection
|
|
24
|
+
? { ...baseConfig, connection: config.connection }
|
|
25
|
+
: baseConfig;
|
|
26
|
+
await adapter.connect(knexConnConfig);
|
|
27
|
+
// Run migrations if needed
|
|
28
|
+
const knex = adapter.getKnex();
|
|
29
|
+
await knex.migrate.latest();
|
|
30
|
+
console.log(`✓ Database initialized with Knex adapter (${environment})`);
|
|
31
|
+
adapterInstance = adapter;
|
|
32
|
+
return adapter;
|
|
189
33
|
}
|
|
190
34
|
/**
|
|
191
|
-
*
|
|
35
|
+
* Get current database adapter
|
|
192
36
|
*/
|
|
193
|
-
export function
|
|
194
|
-
if (
|
|
195
|
-
|
|
196
|
-
dbInstance = null;
|
|
197
|
-
console.log('✓ Database connection closed');
|
|
37
|
+
export function getAdapter() {
|
|
38
|
+
if (!adapterInstance) {
|
|
39
|
+
throw new Error('Database not initialized. Call initializeDatabase() first.');
|
|
198
40
|
}
|
|
41
|
+
return adapterInstance;
|
|
199
42
|
}
|
|
200
43
|
/**
|
|
201
|
-
*
|
|
202
|
-
* Throws error if not initialized
|
|
203
|
-
*
|
|
204
|
-
* @returns Current database instance
|
|
44
|
+
* Close database connection
|
|
205
45
|
*/
|
|
206
|
-
export function
|
|
207
|
-
if (
|
|
208
|
-
|
|
46
|
+
export async function closeDatabase() {
|
|
47
|
+
if (adapterInstance) {
|
|
48
|
+
await adapterInstance.disconnect();
|
|
49
|
+
adapterInstance = null;
|
|
50
|
+
console.log('✓ Database connection closed');
|
|
209
51
|
}
|
|
210
|
-
return dbInstance;
|
|
211
52
|
}
|
|
212
53
|
// ============================================================================
|
|
213
|
-
// Helper Functions for Master Table Management
|
|
54
|
+
// Helper Functions for Master Table Management (Async)
|
|
214
55
|
// ============================================================================
|
|
215
56
|
/**
|
|
216
57
|
* Get or create agent by name
|
|
217
|
-
* Uses INSERT OR IGNORE for idempotent operation
|
|
218
|
-
*
|
|
219
|
-
* @param db - Database instance
|
|
220
|
-
* @param name - Agent name
|
|
221
|
-
* @returns Agent ID
|
|
222
58
|
*/
|
|
223
|
-
export function getOrCreateAgent(
|
|
224
|
-
|
|
225
|
-
|
|
59
|
+
export async function getOrCreateAgent(adapter, name, trx) {
|
|
60
|
+
const knex = trx || adapter.getKnex();
|
|
61
|
+
// Try to insert (will be ignored if exists)
|
|
62
|
+
await knex('m_agents').insert({ name }).onConflict('name').ignore();
|
|
226
63
|
// Get the ID
|
|
227
|
-
const result =
|
|
64
|
+
const result = await knex('m_agents').where({ name }).first('id');
|
|
228
65
|
if (!result) {
|
|
229
66
|
throw new Error(`Failed to get or create agent: ${name}`);
|
|
230
67
|
}
|
|
@@ -232,14 +69,11 @@ export function getOrCreateAgent(db, name) {
|
|
|
232
69
|
}
|
|
233
70
|
/**
|
|
234
71
|
* Get or create context key by name
|
|
235
|
-
*
|
|
236
|
-
* @param db - Database instance
|
|
237
|
-
* @param key - Context key name
|
|
238
|
-
* @returns Context key ID
|
|
239
72
|
*/
|
|
240
|
-
export function getOrCreateContextKey(
|
|
241
|
-
|
|
242
|
-
|
|
73
|
+
export async function getOrCreateContextKey(adapter, key, trx) {
|
|
74
|
+
const knex = trx || adapter.getKnex();
|
|
75
|
+
await knex('m_context_keys').insert({ key }).onConflict('key').ignore();
|
|
76
|
+
const result = await knex('m_context_keys').where({ key }).first('id');
|
|
243
77
|
if (!result) {
|
|
244
78
|
throw new Error(`Failed to get or create context key: ${key}`);
|
|
245
79
|
}
|
|
@@ -247,14 +81,11 @@ export function getOrCreateContextKey(db, key) {
|
|
|
247
81
|
}
|
|
248
82
|
/**
|
|
249
83
|
* Get or create file by path
|
|
250
|
-
*
|
|
251
|
-
* @param db - Database instance
|
|
252
|
-
* @param path - File path
|
|
253
|
-
* @returns File ID
|
|
254
84
|
*/
|
|
255
|
-
export function getOrCreateFile(
|
|
256
|
-
|
|
257
|
-
|
|
85
|
+
export async function getOrCreateFile(adapter, path, trx) {
|
|
86
|
+
const knex = trx || adapter.getKnex();
|
|
87
|
+
await knex('m_files').insert({ path }).onConflict('path').ignore();
|
|
88
|
+
const result = await knex('m_files').where({ path }).first('id');
|
|
258
89
|
if (!result) {
|
|
259
90
|
throw new Error(`Failed to get or create file: ${path}`);
|
|
260
91
|
}
|
|
@@ -262,14 +93,11 @@ export function getOrCreateFile(db, path) {
|
|
|
262
93
|
}
|
|
263
94
|
/**
|
|
264
95
|
* Get or create tag by name
|
|
265
|
-
*
|
|
266
|
-
* @param db - Database instance
|
|
267
|
-
* @param name - Tag name
|
|
268
|
-
* @returns Tag ID
|
|
269
96
|
*/
|
|
270
|
-
export function getOrCreateTag(
|
|
271
|
-
|
|
272
|
-
|
|
97
|
+
export async function getOrCreateTag(adapter, name, trx) {
|
|
98
|
+
const knex = trx || adapter.getKnex();
|
|
99
|
+
await knex('m_tags').insert({ name }).onConflict('name').ignore();
|
|
100
|
+
const result = await knex('m_tags').where({ name }).first('id');
|
|
273
101
|
if (!result) {
|
|
274
102
|
throw new Error(`Failed to get or create tag: ${name}`);
|
|
275
103
|
}
|
|
@@ -277,14 +105,11 @@ export function getOrCreateTag(db, name) {
|
|
|
277
105
|
}
|
|
278
106
|
/**
|
|
279
107
|
* Get or create scope by name
|
|
280
|
-
*
|
|
281
|
-
* @param db - Database instance
|
|
282
|
-
* @param name - Scope name
|
|
283
|
-
* @returns Scope ID
|
|
284
108
|
*/
|
|
285
|
-
export function getOrCreateScope(
|
|
286
|
-
|
|
287
|
-
|
|
109
|
+
export async function getOrCreateScope(adapter, name, trx) {
|
|
110
|
+
const knex = trx || adapter.getKnex();
|
|
111
|
+
await knex('m_scopes').insert({ name }).onConflict('name').ignore();
|
|
112
|
+
const result = await knex('m_scopes').where({ name }).first('id');
|
|
288
113
|
if (!result) {
|
|
289
114
|
throw new Error(`Failed to get or create scope: ${name}`);
|
|
290
115
|
}
|
|
@@ -292,17 +117,11 @@ export function getOrCreateScope(db, name) {
|
|
|
292
117
|
}
|
|
293
118
|
/**
|
|
294
119
|
* Get or create category ID
|
|
295
|
-
* Uses INSERT to create if doesn't exist
|
|
296
|
-
*
|
|
297
|
-
* @param db - Database instance
|
|
298
|
-
* @param category - Category name
|
|
299
|
-
* @returns Category ID
|
|
300
120
|
*/
|
|
301
|
-
export function getOrCreateCategoryId(
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
const result = db.prepare('SELECT id FROM m_constraint_categories WHERE name = ?').get(category);
|
|
121
|
+
export async function getOrCreateCategoryId(adapter, category, trx) {
|
|
122
|
+
const knex = trx || adapter.getKnex();
|
|
123
|
+
await knex('m_constraint_categories').insert({ name: category }).onConflict('name').ignore();
|
|
124
|
+
const result = await knex('m_constraint_categories').where({ name: category }).first('id');
|
|
306
125
|
if (!result) {
|
|
307
126
|
throw new Error(`Failed to get or create category: ${category}`);
|
|
308
127
|
}
|
|
@@ -310,77 +129,56 @@ export function getOrCreateCategoryId(db, category) {
|
|
|
310
129
|
}
|
|
311
130
|
/**
|
|
312
131
|
* Get layer ID by name
|
|
313
|
-
* Does not auto-create (layers are predefined)
|
|
314
|
-
*
|
|
315
|
-
* @param db - Database instance
|
|
316
|
-
* @param name - Layer name
|
|
317
|
-
* @returns Layer ID or null if not found
|
|
318
132
|
*/
|
|
319
|
-
export function getLayerId(
|
|
320
|
-
const
|
|
133
|
+
export async function getLayerId(adapter, name, trx) {
|
|
134
|
+
const knex = trx || adapter.getKnex();
|
|
135
|
+
const result = await knex('m_layers').where({ name }).first('id');
|
|
321
136
|
return result ? result.id : null;
|
|
322
137
|
}
|
|
323
138
|
/**
|
|
324
139
|
* Get constraint category ID by name
|
|
325
|
-
* Does not auto-create (categories are predefined)
|
|
326
|
-
*
|
|
327
|
-
* @param db - Database instance
|
|
328
|
-
* @param name - Category name
|
|
329
|
-
* @returns Category ID or null if not found
|
|
330
140
|
*/
|
|
331
|
-
export function getCategoryId(
|
|
332
|
-
const
|
|
141
|
+
export async function getCategoryId(adapter, name, trx) {
|
|
142
|
+
const knex = trx || adapter.getKnex();
|
|
143
|
+
const result = await knex('m_constraint_categories').where({ name }).first('id');
|
|
333
144
|
return result ? result.id : null;
|
|
334
145
|
}
|
|
335
146
|
// ============================================================================
|
|
336
|
-
// Configuration Management
|
|
147
|
+
// Configuration Management (Async)
|
|
337
148
|
// ============================================================================
|
|
338
149
|
/**
|
|
339
150
|
* Get configuration value from m_config table
|
|
340
|
-
*
|
|
341
|
-
* @param db - Database instance
|
|
342
|
-
* @param key - Config key
|
|
343
|
-
* @returns Config value as string or null if not found
|
|
344
151
|
*/
|
|
345
|
-
export function getConfigValue(
|
|
346
|
-
const
|
|
152
|
+
export async function getConfigValue(adapter, key) {
|
|
153
|
+
const knex = adapter.getKnex();
|
|
154
|
+
const result = await knex('m_config').where({ key }).first('value');
|
|
347
155
|
return result ? result.value : null;
|
|
348
156
|
}
|
|
349
157
|
/**
|
|
350
158
|
* Set configuration value in m_config table
|
|
351
|
-
*
|
|
352
|
-
* @param db - Database instance
|
|
353
|
-
* @param key - Config key
|
|
354
|
-
* @param value - Config value (will be converted to string)
|
|
355
159
|
*/
|
|
356
|
-
export function setConfigValue(
|
|
160
|
+
export async function setConfigValue(adapter, key, value) {
|
|
161
|
+
const knex = adapter.getKnex();
|
|
357
162
|
const stringValue = String(value);
|
|
358
|
-
|
|
163
|
+
await knex('m_config')
|
|
164
|
+
.insert({ key, value: stringValue })
|
|
165
|
+
.onConflict('key')
|
|
166
|
+
.merge({ value: stringValue });
|
|
359
167
|
}
|
|
360
168
|
/**
|
|
361
169
|
* Get configuration value as boolean
|
|
362
|
-
*
|
|
363
|
-
* @param db - Database instance
|
|
364
|
-
* @param key - Config key
|
|
365
|
-
* @param defaultValue - Default value if key not found
|
|
366
|
-
* @returns Boolean value
|
|
367
170
|
*/
|
|
368
|
-
export function getConfigBool(
|
|
369
|
-
const value = getConfigValue(
|
|
171
|
+
export async function getConfigBool(adapter, key, defaultValue = false) {
|
|
172
|
+
const value = await getConfigValue(adapter, key);
|
|
370
173
|
if (value === null)
|
|
371
174
|
return defaultValue;
|
|
372
175
|
return value === '1' || value.toLowerCase() === 'true';
|
|
373
176
|
}
|
|
374
177
|
/**
|
|
375
178
|
* Get configuration value as integer
|
|
376
|
-
*
|
|
377
|
-
* @param db - Database instance
|
|
378
|
-
* @param key - Config key
|
|
379
|
-
* @param defaultValue - Default value if key not found
|
|
380
|
-
* @returns Integer value
|
|
381
179
|
*/
|
|
382
|
-
export function getConfigInt(
|
|
383
|
-
const value = getConfigValue(
|
|
180
|
+
export async function getConfigInt(adapter, key, defaultValue = 0) {
|
|
181
|
+
const value = await getConfigValue(adapter, key);
|
|
384
182
|
if (value === null)
|
|
385
183
|
return defaultValue;
|
|
386
184
|
const parsed = parseInt(value, 10);
|
|
@@ -388,12 +186,10 @@ export function getConfigInt(db, key, defaultValue = 0) {
|
|
|
388
186
|
}
|
|
389
187
|
/**
|
|
390
188
|
* Get all configuration as an object
|
|
391
|
-
*
|
|
392
|
-
* @param db - Database instance
|
|
393
|
-
* @returns Object with all m_config key-value pairs
|
|
394
189
|
*/
|
|
395
|
-
export function getAllConfig(
|
|
396
|
-
const
|
|
190
|
+
export async function getAllConfig(adapter) {
|
|
191
|
+
const knex = adapter.getKnex();
|
|
192
|
+
const rows = await knex('m_config').select('key', 'value');
|
|
397
193
|
const config = {};
|
|
398
194
|
for (const row of rows) {
|
|
399
195
|
config[row.key] = row.value;
|
|
@@ -401,35 +197,10 @@ export function getAllConfig(db) {
|
|
|
401
197
|
return config;
|
|
402
198
|
}
|
|
403
199
|
// ============================================================================
|
|
404
|
-
//
|
|
405
|
-
// ============================================================================
|
|
406
|
-
/**
|
|
407
|
-
* Execute a function within a transaction
|
|
408
|
-
* Automatically handles commit/rollback
|
|
409
|
-
*
|
|
410
|
-
* @param db - Database instance
|
|
411
|
-
* @param fn - Function to execute in transaction
|
|
412
|
-
* @returns Result from function
|
|
413
|
-
*/
|
|
414
|
-
export function transaction(db, fn) {
|
|
415
|
-
db.exec('BEGIN TRANSACTION');
|
|
416
|
-
try {
|
|
417
|
-
const result = fn();
|
|
418
|
-
db.exec('COMMIT');
|
|
419
|
-
return result;
|
|
420
|
-
}
|
|
421
|
-
catch (error) {
|
|
422
|
-
db.exec('ROLLBACK');
|
|
423
|
-
throw error;
|
|
424
|
-
}
|
|
425
|
-
}
|
|
426
|
-
// ============================================================================
|
|
427
|
-
// Decision Context Management (v3.2.2)
|
|
200
|
+
// Decision Context Management (Async - v3.2.2)
|
|
428
201
|
// ============================================================================
|
|
429
202
|
/**
|
|
430
203
|
* Validate JSON structure for alternatives array
|
|
431
|
-
* @param alternatives - JSON string or null
|
|
432
|
-
* @throws Error if JSON is invalid or not an array
|
|
433
204
|
*/
|
|
434
205
|
function validateAlternativesJson(alternatives) {
|
|
435
206
|
if (alternatives === null || alternatives === undefined)
|
|
@@ -449,8 +220,6 @@ function validateAlternativesJson(alternatives) {
|
|
|
449
220
|
}
|
|
450
221
|
/**
|
|
451
222
|
* Validate JSON structure for tradeoffs object
|
|
452
|
-
* @param tradeoffs - JSON string or null
|
|
453
|
-
* @throws Error if JSON is invalid or doesn't have pros/cons structure
|
|
454
223
|
*/
|
|
455
224
|
function validateTradeoffsJson(tradeoffs) {
|
|
456
225
|
if (tradeoffs === null || tradeoffs === undefined)
|
|
@@ -460,7 +229,6 @@ function validateTradeoffsJson(tradeoffs) {
|
|
|
460
229
|
if (typeof parsed !== 'object' || parsed === null) {
|
|
461
230
|
throw new Error('tradeoffs must be a JSON object');
|
|
462
231
|
}
|
|
463
|
-
// Optional: Check for pros/cons keys if provided
|
|
464
232
|
if (parsed.pros !== undefined && !Array.isArray(parsed.pros)) {
|
|
465
233
|
throw new Error('tradeoffs.pros must be an array');
|
|
466
234
|
}
|
|
@@ -477,85 +245,54 @@ function validateTradeoffsJson(tradeoffs) {
|
|
|
477
245
|
}
|
|
478
246
|
/**
|
|
479
247
|
* Add decision context to a decision
|
|
480
|
-
*
|
|
481
|
-
* @param db - Database instance
|
|
482
|
-
* @param decisionKey - Decision key to attach context to
|
|
483
|
-
* @param rationale - Rationale for the decision (required)
|
|
484
|
-
* @param alternatives - JSON array of alternatives considered (optional)
|
|
485
|
-
* @param tradeoffs - JSON object with pros/cons (optional)
|
|
486
|
-
* @param decidedBy - Agent name who decided (optional)
|
|
487
|
-
* @param relatedTaskId - Related task ID (optional)
|
|
488
|
-
* @param relatedConstraintId - Related constraint ID (optional)
|
|
489
|
-
* @returns Context ID
|
|
490
248
|
*/
|
|
491
|
-
export function addDecisionContext(
|
|
249
|
+
export async function addDecisionContext(adapter, decisionKey, rationale, alternatives = null, tradeoffs = null, decidedBy = null, relatedTaskId = null, relatedConstraintId = null) {
|
|
492
250
|
// Validate JSON inputs
|
|
493
251
|
validateAlternativesJson(alternatives);
|
|
494
252
|
validateTradeoffsJson(tradeoffs);
|
|
253
|
+
const knex = adapter.getKnex();
|
|
495
254
|
// Get decision key ID
|
|
496
|
-
const keyId = getOrCreateContextKey(
|
|
255
|
+
const keyId = await getOrCreateContextKey(adapter, decisionKey);
|
|
497
256
|
// Get agent ID if provided
|
|
498
257
|
let agentId = null;
|
|
499
258
|
if (decidedBy) {
|
|
500
|
-
agentId = getOrCreateAgent(
|
|
259
|
+
agentId = await getOrCreateAgent(adapter, decidedBy);
|
|
501
260
|
}
|
|
502
261
|
// Insert context
|
|
503
|
-
const
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
)
|
|
513
|
-
|
|
514
|
-
return result.lastInsertRowid;
|
|
262
|
+
const [id] = await knex('t_decision_context').insert({
|
|
263
|
+
decision_key_id: keyId,
|
|
264
|
+
rationale,
|
|
265
|
+
alternatives_considered: alternatives,
|
|
266
|
+
tradeoffs,
|
|
267
|
+
decided_by_agent_id: agentId,
|
|
268
|
+
related_task_id: relatedTaskId,
|
|
269
|
+
related_constraint_id: relatedConstraintId,
|
|
270
|
+
decision_date: Math.floor(Date.now() / 1000),
|
|
271
|
+
});
|
|
272
|
+
return id;
|
|
515
273
|
}
|
|
516
274
|
/**
|
|
517
275
|
* Get decision with context
|
|
518
|
-
*
|
|
519
|
-
* @param db - Database instance
|
|
520
|
-
* @param decisionKey - Decision key
|
|
521
|
-
* @returns Decision with context or null if not found
|
|
522
276
|
*/
|
|
523
|
-
export function getDecisionWithContext(
|
|
277
|
+
export async function getDecisionWithContext(adapter, decisionKey) {
|
|
278
|
+
const knex = adapter.getKnex();
|
|
524
279
|
// First get the decision
|
|
525
|
-
const decision =
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
a.name as decided_by,
|
|
533
|
-
datetime(d.ts, 'unixepoch') as updated
|
|
534
|
-
FROM t_decisions d
|
|
535
|
-
JOIN m_context_keys k ON d.key_id = k.id
|
|
536
|
-
LEFT JOIN m_layers l ON d.layer_id = l.id
|
|
537
|
-
LEFT JOIN m_agents a ON d.agent_id = a.id
|
|
538
|
-
WHERE k.key = ?
|
|
539
|
-
`).get(decisionKey);
|
|
280
|
+
const decision = await knex('t_decisions as d')
|
|
281
|
+
.join('m_context_keys as k', 'd.key_id', 'k.id')
|
|
282
|
+
.leftJoin('m_layers as l', 'd.layer_id', 'l.id')
|
|
283
|
+
.leftJoin('m_agents as a', 'd.agent_id', 'a.id')
|
|
284
|
+
.where('k.key', decisionKey)
|
|
285
|
+
.select('k.key', 'd.value', 'd.version', knex.raw(`CASE d.status WHEN 1 THEN 'active' WHEN 2 THEN 'deprecated' ELSE 'draft' END as status`), 'l.name as layer', 'a.name as decided_by', knex.raw(`datetime(d.ts, 'unixepoch') as updated`))
|
|
286
|
+
.first();
|
|
540
287
|
if (!decision)
|
|
541
288
|
return null;
|
|
542
289
|
// Get all contexts for this decision
|
|
543
|
-
const contexts =
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
a.name as decided_by,
|
|
550
|
-
datetime(dc.decision_date, 'unixepoch') as decision_date,
|
|
551
|
-
dc.related_task_id,
|
|
552
|
-
dc.related_constraint_id
|
|
553
|
-
FROM t_decision_context dc
|
|
554
|
-
JOIN m_context_keys k ON dc.decision_key_id = k.id
|
|
555
|
-
LEFT JOIN m_agents a ON dc.decided_by_agent_id = a.id
|
|
556
|
-
WHERE k.key = ?
|
|
557
|
-
ORDER BY dc.decision_date DESC
|
|
558
|
-
`).all(decisionKey);
|
|
290
|
+
const contexts = await knex('t_decision_context as dc')
|
|
291
|
+
.join('m_context_keys as k', 'dc.decision_key_id', 'k.id')
|
|
292
|
+
.leftJoin('m_agents as a', 'dc.decided_by_agent_id', 'a.id')
|
|
293
|
+
.where('k.key', decisionKey)
|
|
294
|
+
.select('dc.id', 'dc.rationale', 'dc.alternatives_considered', 'dc.tradeoffs', 'a.name as decided_by', knex.raw(`datetime(dc.decision_date, 'unixepoch') as decision_date`), 'dc.related_task_id', 'dc.related_constraint_id')
|
|
295
|
+
.orderBy('dc.decision_date', 'desc');
|
|
559
296
|
return {
|
|
560
297
|
...decision,
|
|
561
298
|
context: contexts,
|
|
@@ -563,54 +300,46 @@ export function getDecisionWithContext(db, decisionKey) {
|
|
|
563
300
|
}
|
|
564
301
|
/**
|
|
565
302
|
* List decision contexts with optional filters
|
|
566
|
-
*
|
|
567
|
-
* @param db - Database instance
|
|
568
|
-
* @param filters - Optional filters
|
|
569
|
-
* @returns Array of decision contexts
|
|
570
303
|
*/
|
|
571
|
-
export function listDecisionContexts(
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
dc.alternatives_considered,
|
|
578
|
-
dc.tradeoffs,
|
|
579
|
-
a.name as decided_by,
|
|
580
|
-
datetime(dc.decision_date, 'unixepoch') as decision_date,
|
|
581
|
-
dc.related_task_id,
|
|
582
|
-
dc.related_constraint_id
|
|
583
|
-
FROM t_decision_context dc
|
|
584
|
-
JOIN m_context_keys k ON dc.decision_key_id = k.id
|
|
585
|
-
LEFT JOIN m_agents a ON dc.decided_by_agent_id = a.id
|
|
586
|
-
WHERE 1=1
|
|
587
|
-
`;
|
|
588
|
-
const params = [];
|
|
304
|
+
export async function listDecisionContexts(adapter, filters) {
|
|
305
|
+
const knex = adapter.getKnex();
|
|
306
|
+
let query = knex('t_decision_context as dc')
|
|
307
|
+
.join('m_context_keys as k', 'dc.decision_key_id', 'k.id')
|
|
308
|
+
.leftJoin('m_agents as a', 'dc.decided_by_agent_id', 'a.id')
|
|
309
|
+
.select('dc.id', 'k.key as decision_key', 'dc.rationale', 'dc.alternatives_considered', 'dc.tradeoffs', 'a.name as decided_by', knex.raw(`datetime(dc.decision_date, 'unixepoch') as decision_date`), 'dc.related_task_id', 'dc.related_constraint_id');
|
|
589
310
|
if (filters?.decisionKey) {
|
|
590
|
-
query
|
|
591
|
-
params.push(filters.decisionKey);
|
|
311
|
+
query = query.where('k.key', filters.decisionKey);
|
|
592
312
|
}
|
|
593
313
|
if (filters?.relatedTaskId !== undefined) {
|
|
594
|
-
query
|
|
595
|
-
params.push(filters.relatedTaskId);
|
|
314
|
+
query = query.where('dc.related_task_id', filters.relatedTaskId);
|
|
596
315
|
}
|
|
597
316
|
if (filters?.relatedConstraintId !== undefined) {
|
|
598
|
-
query
|
|
599
|
-
params.push(filters.relatedConstraintId);
|
|
317
|
+
query = query.where('dc.related_constraint_id', filters.relatedConstraintId);
|
|
600
318
|
}
|
|
601
319
|
if (filters?.decidedBy) {
|
|
602
|
-
query
|
|
603
|
-
params.push(filters.decidedBy);
|
|
320
|
+
query = query.where('a.name', filters.decidedBy);
|
|
604
321
|
}
|
|
605
|
-
query
|
|
322
|
+
query = query.orderBy('dc.decision_date', 'desc');
|
|
606
323
|
if (filters?.limit) {
|
|
607
|
-
query
|
|
608
|
-
params.push(filters.limit);
|
|
324
|
+
query = query.limit(filters.limit);
|
|
609
325
|
}
|
|
610
326
|
if (filters?.offset) {
|
|
611
|
-
query
|
|
612
|
-
params.push(filters.offset);
|
|
327
|
+
query = query.offset(filters.offset);
|
|
613
328
|
}
|
|
614
|
-
return
|
|
329
|
+
return await query;
|
|
330
|
+
}
|
|
331
|
+
/**
|
|
332
|
+
* Backwards compatibility alias for getAdapter
|
|
333
|
+
*/
|
|
334
|
+
export function getDatabase() {
|
|
335
|
+
return getAdapter();
|
|
336
|
+
}
|
|
337
|
+
/**
|
|
338
|
+
* Execute a function within a database transaction
|
|
339
|
+
*/
|
|
340
|
+
export async function transaction(callback) {
|
|
341
|
+
const adapter = getAdapter();
|
|
342
|
+
const knex = adapter.getKnex();
|
|
343
|
+
return await knex.transaction(callback);
|
|
615
344
|
}
|
|
616
345
|
//# sourceMappingURL=database.js.map
|