sqlew 3.5.3 → 3.6.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/CHANGELOG.md +247 -1772
- package/README.md +70 -304
- package/assets/config.example.toml +106 -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 +11 -0
- package/dist/config/types.d.ts.map +1 -1
- package/dist/config/types.js.map +1 -1
- package/dist/database.d.ts +56 -121
- package/dist/database.d.ts.map +1 -1
- package/dist/database.js +266 -414
- package/dist/database.js.map +1 -1
- package/dist/index.js +329 -245
- 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/knex/20251027000000_add_agent_reuse_system.d.ts +14 -0
- package/dist/migrations/knex/20251027000000_add_agent_reuse_system.d.ts.map +1 -0
- package/dist/migrations/knex/20251027000000_add_agent_reuse_system.js +34 -0
- package/dist/migrations/knex/20251027000000_add_agent_reuse_system.js.map +1 -0
- package/dist/migrations/knex/20251027010000_add_task_constraint_to_decision_context.d.ts +4 -0
- package/dist/migrations/knex/20251027010000_add_task_constraint_to_decision_context.d.ts.map +1 -0
- package/dist/migrations/knex/20251027010000_add_task_constraint_to_decision_context.js +24 -0
- package/dist/migrations/knex/20251027010000_add_task_constraint_to_decision_context.js.map +1 -0
- package/dist/migrations/knex/20251027020000_update_agent_reusability.d.ts +16 -0
- package/dist/migrations/knex/20251027020000_update_agent_reusability.d.ts.map +1 -0
- package/dist/migrations/knex/20251027020000_update_agent_reusability.js +27 -0
- package/dist/migrations/knex/20251027020000_update_agent_reusability.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/agent-reuse.test.d.ts +6 -0
- package/dist/tests/agent-reuse.test.d.ts.map +1 -0
- package/dist/tests/agent-reuse.test.js +242 -0
- package/dist/tests/agent-reuse.test.js.map +1 -0
- package/dist/tests/all-features.test.d.ts +7 -0
- package/dist/tests/all-features.test.d.ts.map +1 -0
- package/dist/tests/all-features.test.js +514 -0
- package/dist/tests/all-features.test.js.map +1 -0
- 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 +73 -51
- 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 +441 -340
- 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 +239 -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 +519 -442
- 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 +90 -122
- package/dist/tools/utils.js.map +1 -1
- package/dist/types.d.ts +1 -0
- package/dist/types.d.ts.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 +24 -14
- package/dist/utils/cleanup.d.ts.map +1 -1
- package/dist/utils/cleanup.js +37 -27
- package/dist/utils/cleanup.js.map +1 -1
- package/dist/utils/debug-logger.d.ts +99 -0
- package/dist/utils/debug-logger.d.ts.map +1 -0
- package/dist/utils/debug-logger.js +267 -0
- package/dist/utils/debug-logger.js.map +1 -0
- package/dist/utils/error-handler.d.ts +28 -0
- package/dist/utils/error-handler.d.ts.map +1 -0
- package/dist/utils/error-handler.js +121 -0
- package/dist/utils/error-handler.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/param-parser.d.ts +23 -0
- package/dist/utils/param-parser.d.ts.map +1 -0
- package/dist/utils/param-parser.js +52 -0
- package/dist/utils/param-parser.js.map +1 -0
- package/dist/utils/retention.d.ts +17 -7
- package/dist/utils/retention.d.ts.map +1 -1
- package/dist/utils/retention.js +31 -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 +17 -4
package/dist/database.js
CHANGED
|
@@ -1,245 +1,201 @@
|
|
|
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
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
5
|
+
import knexConfig from './knexfile.js';
|
|
6
|
+
import { createDatabaseAdapter } from './adapters/index.js';
|
|
7
|
+
// Built-in Claude Code agent types that should be normalized/pooled
|
|
8
|
+
const BUILTIN_AGENT_TYPES = [
|
|
9
|
+
'general-purpose',
|
|
10
|
+
'statusline-setup',
|
|
11
|
+
'output-style-setup',
|
|
12
|
+
'Explore'
|
|
13
|
+
];
|
|
14
|
+
// Global adapter instance
|
|
15
|
+
let adapterInstance = null;
|
|
15
16
|
/**
|
|
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
|
|
17
|
+
* Initialize database with adapter pattern
|
|
22
18
|
*/
|
|
23
|
-
export function initializeDatabase(
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
return dbInstance;
|
|
19
|
+
export async function initializeDatabase(config) {
|
|
20
|
+
if (adapterInstance) {
|
|
21
|
+
return adapterInstance;
|
|
27
22
|
}
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
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}`);
|
|
23
|
+
const dbType = config?.databaseType || 'sqlite';
|
|
24
|
+
const adapter = createDatabaseAdapter(dbType);
|
|
25
|
+
// Determine if running from compiled code (dist/) or source (src/)
|
|
26
|
+
const isCompiledCode = import.meta.url.includes('/dist/');
|
|
27
|
+
const environment = isCompiledCode ? 'production' : 'development';
|
|
28
|
+
// Use config from knexfile or provided config
|
|
29
|
+
const baseConfig = knexConfig[environment] || knexConfig.development;
|
|
30
|
+
const knexConnConfig = config?.connection
|
|
31
|
+
? { ...baseConfig, connection: config.connection }
|
|
32
|
+
: baseConfig;
|
|
33
|
+
await adapter.connect(knexConnConfig);
|
|
34
|
+
// Run migrations if needed
|
|
35
|
+
const knex = adapter.getKnex();
|
|
36
|
+
await knex.migrate.latest();
|
|
37
|
+
console.log(`✓ Database initialized with Knex adapter (${environment})`);
|
|
38
|
+
adapterInstance = adapter;
|
|
39
|
+
return adapter;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Get current database adapter
|
|
43
|
+
*/
|
|
44
|
+
export function getAdapter() {
|
|
45
|
+
if (!adapterInstance) {
|
|
46
|
+
throw new Error('Database not initialized. Call initializeDatabase() first.');
|
|
188
47
|
}
|
|
48
|
+
return adapterInstance;
|
|
189
49
|
}
|
|
190
50
|
/**
|
|
191
51
|
* Close database connection
|
|
192
52
|
*/
|
|
193
|
-
export function closeDatabase() {
|
|
194
|
-
if (
|
|
195
|
-
|
|
196
|
-
|
|
53
|
+
export async function closeDatabase() {
|
|
54
|
+
if (adapterInstance) {
|
|
55
|
+
await adapterInstance.disconnect();
|
|
56
|
+
adapterInstance = null;
|
|
197
57
|
console.log('✓ Database connection closed');
|
|
198
58
|
}
|
|
199
59
|
}
|
|
60
|
+
// ============================================================================
|
|
61
|
+
// Helper Functions for Master Table Management (Async)
|
|
62
|
+
// ============================================================================
|
|
200
63
|
/**
|
|
201
|
-
*
|
|
202
|
-
* Throws error if not initialized
|
|
64
|
+
* Determines if an agent name is user-specified (meaningful) or generic
|
|
203
65
|
*
|
|
204
|
-
*
|
|
66
|
+
* Agent Classification:
|
|
67
|
+
* - System agents: 'system', 'migration-manager' (not reusable, permanently protected)
|
|
68
|
+
* - Built-in agents: Claude Code default agents like 'Explore' (reusable, normalized)
|
|
69
|
+
* - User-defined agents: Custom agents like 'rust-architecture-expert' (reusable, exact name preserved)
|
|
70
|
+
* - Generic pool: Empty names or 'generic-N' pattern (reusable, automatically allocated)
|
|
71
|
+
*
|
|
72
|
+
* @returns true if agent should NOT be reusable (system agents only)
|
|
205
73
|
*/
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
74
|
+
function isUserSpecifiedAgent(name) {
|
|
75
|
+
// Empty names use generic pool (reusable)
|
|
76
|
+
if (!name || name.trim().length === 0) {
|
|
77
|
+
return false;
|
|
78
|
+
}
|
|
79
|
+
// Core system agents that should NOT be reusable
|
|
80
|
+
const systemAgents = ['system', 'migration-manager'];
|
|
81
|
+
if (systemAgents.includes(name.toLowerCase())) {
|
|
82
|
+
return true;
|
|
83
|
+
}
|
|
84
|
+
// Built-in Claude Code agents (reusable, normalized)
|
|
85
|
+
if (BUILTIN_AGENT_TYPES.includes(name)) {
|
|
86
|
+
return false;
|
|
209
87
|
}
|
|
210
|
-
|
|
88
|
+
// Everything else (user-defined agents, generic-N patterns) is reusable
|
|
89
|
+
return false;
|
|
211
90
|
}
|
|
212
|
-
// ============================================================================
|
|
213
|
-
// Helper Functions for Master Table Management
|
|
214
|
-
// ============================================================================
|
|
215
91
|
/**
|
|
216
|
-
* Get or create agent by name
|
|
217
|
-
* Uses INSERT OR IGNORE for idempotent operation
|
|
92
|
+
* Get or create agent by name with reuse logic
|
|
218
93
|
*
|
|
219
|
-
*
|
|
220
|
-
*
|
|
221
|
-
*
|
|
94
|
+
* - Empty/whitespace names: Allocate reusable slot (find inactive or create new generic-N)
|
|
95
|
+
* - Named generic agents (e.g., 'generic-5', 'agent-123'): Create with exact name, mark reusable
|
|
96
|
+
* - User-specified agents (e.g., 'rust-expert'): Create permanently, non-reusable
|
|
222
97
|
*/
|
|
223
|
-
export function getOrCreateAgent(
|
|
224
|
-
|
|
225
|
-
|
|
98
|
+
export async function getOrCreateAgent(adapter, name, trx) {
|
|
99
|
+
const knex = trx || adapter.getKnex();
|
|
100
|
+
const now = Math.floor(Date.now() / 1000);
|
|
101
|
+
// Empty name: allocate a reusable generic slot
|
|
102
|
+
if (!name || name.trim().length === 0) {
|
|
103
|
+
// Try to reuse an inactive slot
|
|
104
|
+
const inactiveSlot = await knex('m_agents')
|
|
105
|
+
.where({
|
|
106
|
+
is_reusable: true,
|
|
107
|
+
in_use: false
|
|
108
|
+
})
|
|
109
|
+
.orderBy('id', 'asc')
|
|
110
|
+
.first();
|
|
111
|
+
if (inactiveSlot) {
|
|
112
|
+
// Reuse this slot
|
|
113
|
+
await knex('m_agents')
|
|
114
|
+
.where({ id: inactiveSlot.id })
|
|
115
|
+
.update({
|
|
116
|
+
in_use: true,
|
|
117
|
+
last_active_ts: now
|
|
118
|
+
});
|
|
119
|
+
return inactiveSlot.id;
|
|
120
|
+
}
|
|
121
|
+
else {
|
|
122
|
+
// No inactive slots available, create a new generic agent
|
|
123
|
+
const maxGeneric = await knex('m_agents')
|
|
124
|
+
.where('name', 'like', 'generic-%')
|
|
125
|
+
.orderBy('name', 'desc')
|
|
126
|
+
.first('name');
|
|
127
|
+
let nextNumber = 1;
|
|
128
|
+
if (maxGeneric && maxGeneric.name) {
|
|
129
|
+
const match = maxGeneric.name.match(/^generic-(\d+)$/);
|
|
130
|
+
if (match) {
|
|
131
|
+
nextNumber = parseInt(match[1], 10) + 1;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
const genericName = `generic-${nextNumber}`;
|
|
135
|
+
const [id] = await knex('m_agents').insert({
|
|
136
|
+
name: genericName,
|
|
137
|
+
is_reusable: true,
|
|
138
|
+
in_use: true,
|
|
139
|
+
last_active_ts: now
|
|
140
|
+
});
|
|
141
|
+
return id;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
// Check if this is a user-specified agent or a named generic agent
|
|
145
|
+
const isUserSpecified = isUserSpecifiedAgent(name);
|
|
146
|
+
const isReusable = !isUserSpecified;
|
|
147
|
+
// Named agent: create/get with exact name
|
|
148
|
+
// Try to insert (will be ignored if exists)
|
|
149
|
+
await knex('m_agents')
|
|
150
|
+
.insert({
|
|
151
|
+
name,
|
|
152
|
+
is_reusable: isReusable,
|
|
153
|
+
in_use: true,
|
|
154
|
+
last_active_ts: now
|
|
155
|
+
})
|
|
156
|
+
.onConflict('name')
|
|
157
|
+
.ignore();
|
|
158
|
+
// Update activity timestamp and in_use flag
|
|
159
|
+
await knex('m_agents')
|
|
160
|
+
.where({ name })
|
|
161
|
+
.update({
|
|
162
|
+
in_use: true,
|
|
163
|
+
last_active_ts: now
|
|
164
|
+
});
|
|
226
165
|
// Get the ID
|
|
227
|
-
const result =
|
|
166
|
+
const result = await knex('m_agents').where({ name }).first('id');
|
|
228
167
|
if (!result) {
|
|
229
168
|
throw new Error(`Failed to get or create agent: ${name}`);
|
|
230
169
|
}
|
|
231
170
|
return result.id;
|
|
232
171
|
}
|
|
172
|
+
/**
|
|
173
|
+
* Release an agent slot (mark as inactive)
|
|
174
|
+
* This allows generic agents to be reused
|
|
175
|
+
*/
|
|
176
|
+
export async function releaseAgent(adapter, agentId, trx) {
|
|
177
|
+
const knex = trx || adapter.getKnex();
|
|
178
|
+
await knex('m_agents')
|
|
179
|
+
.where({ id: agentId, is_reusable: true })
|
|
180
|
+
.update({ in_use: false });
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Update agent activity timestamp
|
|
184
|
+
*/
|
|
185
|
+
export async function updateAgentActivity(adapter, agentId, trx) {
|
|
186
|
+
const knex = trx || adapter.getKnex();
|
|
187
|
+
const now = Math.floor(Date.now() / 1000);
|
|
188
|
+
await knex('m_agents')
|
|
189
|
+
.where({ id: agentId })
|
|
190
|
+
.update({ last_active_ts: now });
|
|
191
|
+
}
|
|
233
192
|
/**
|
|
234
193
|
* 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
194
|
*/
|
|
240
|
-
export function getOrCreateContextKey(
|
|
241
|
-
|
|
242
|
-
|
|
195
|
+
export async function getOrCreateContextKey(adapter, key, trx) {
|
|
196
|
+
const knex = trx || adapter.getKnex();
|
|
197
|
+
await knex('m_context_keys').insert({ key }).onConflict('key').ignore();
|
|
198
|
+
const result = await knex('m_context_keys').where({ key }).first('id');
|
|
243
199
|
if (!result) {
|
|
244
200
|
throw new Error(`Failed to get or create context key: ${key}`);
|
|
245
201
|
}
|
|
@@ -247,14 +203,11 @@ export function getOrCreateContextKey(db, key) {
|
|
|
247
203
|
}
|
|
248
204
|
/**
|
|
249
205
|
* Get or create file by path
|
|
250
|
-
*
|
|
251
|
-
* @param db - Database instance
|
|
252
|
-
* @param path - File path
|
|
253
|
-
* @returns File ID
|
|
254
206
|
*/
|
|
255
|
-
export function getOrCreateFile(
|
|
256
|
-
|
|
257
|
-
|
|
207
|
+
export async function getOrCreateFile(adapter, path, trx) {
|
|
208
|
+
const knex = trx || adapter.getKnex();
|
|
209
|
+
await knex('m_files').insert({ path }).onConflict('path').ignore();
|
|
210
|
+
const result = await knex('m_files').where({ path }).first('id');
|
|
258
211
|
if (!result) {
|
|
259
212
|
throw new Error(`Failed to get or create file: ${path}`);
|
|
260
213
|
}
|
|
@@ -262,14 +215,11 @@ export function getOrCreateFile(db, path) {
|
|
|
262
215
|
}
|
|
263
216
|
/**
|
|
264
217
|
* Get or create tag by name
|
|
265
|
-
*
|
|
266
|
-
* @param db - Database instance
|
|
267
|
-
* @param name - Tag name
|
|
268
|
-
* @returns Tag ID
|
|
269
218
|
*/
|
|
270
|
-
export function getOrCreateTag(
|
|
271
|
-
|
|
272
|
-
|
|
219
|
+
export async function getOrCreateTag(adapter, name, trx) {
|
|
220
|
+
const knex = trx || adapter.getKnex();
|
|
221
|
+
await knex('m_tags').insert({ name }).onConflict('name').ignore();
|
|
222
|
+
const result = await knex('m_tags').where({ name }).first('id');
|
|
273
223
|
if (!result) {
|
|
274
224
|
throw new Error(`Failed to get or create tag: ${name}`);
|
|
275
225
|
}
|
|
@@ -277,14 +227,11 @@ export function getOrCreateTag(db, name) {
|
|
|
277
227
|
}
|
|
278
228
|
/**
|
|
279
229
|
* Get or create scope by name
|
|
280
|
-
*
|
|
281
|
-
* @param db - Database instance
|
|
282
|
-
* @param name - Scope name
|
|
283
|
-
* @returns Scope ID
|
|
284
230
|
*/
|
|
285
|
-
export function getOrCreateScope(
|
|
286
|
-
|
|
287
|
-
|
|
231
|
+
export async function getOrCreateScope(adapter, name, trx) {
|
|
232
|
+
const knex = trx || adapter.getKnex();
|
|
233
|
+
await knex('m_scopes').insert({ name }).onConflict('name').ignore();
|
|
234
|
+
const result = await knex('m_scopes').where({ name }).first('id');
|
|
288
235
|
if (!result) {
|
|
289
236
|
throw new Error(`Failed to get or create scope: ${name}`);
|
|
290
237
|
}
|
|
@@ -292,17 +239,11 @@ export function getOrCreateScope(db, name) {
|
|
|
292
239
|
}
|
|
293
240
|
/**
|
|
294
241
|
* 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
242
|
*/
|
|
301
|
-
export function getOrCreateCategoryId(
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
const result = db.prepare('SELECT id FROM m_constraint_categories WHERE name = ?').get(category);
|
|
243
|
+
export async function getOrCreateCategoryId(adapter, category, trx) {
|
|
244
|
+
const knex = trx || adapter.getKnex();
|
|
245
|
+
await knex('m_constraint_categories').insert({ name: category }).onConflict('name').ignore();
|
|
246
|
+
const result = await knex('m_constraint_categories').where({ name: category }).first('id');
|
|
306
247
|
if (!result) {
|
|
307
248
|
throw new Error(`Failed to get or create category: ${category}`);
|
|
308
249
|
}
|
|
@@ -310,77 +251,56 @@ export function getOrCreateCategoryId(db, category) {
|
|
|
310
251
|
}
|
|
311
252
|
/**
|
|
312
253
|
* 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
254
|
*/
|
|
319
|
-
export function getLayerId(
|
|
320
|
-
const
|
|
255
|
+
export async function getLayerId(adapter, name, trx) {
|
|
256
|
+
const knex = trx || adapter.getKnex();
|
|
257
|
+
const result = await knex('m_layers').where({ name }).first('id');
|
|
321
258
|
return result ? result.id : null;
|
|
322
259
|
}
|
|
323
260
|
/**
|
|
324
261
|
* 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
262
|
*/
|
|
331
|
-
export function getCategoryId(
|
|
332
|
-
const
|
|
263
|
+
export async function getCategoryId(adapter, name, trx) {
|
|
264
|
+
const knex = trx || adapter.getKnex();
|
|
265
|
+
const result = await knex('m_constraint_categories').where({ name }).first('id');
|
|
333
266
|
return result ? result.id : null;
|
|
334
267
|
}
|
|
335
268
|
// ============================================================================
|
|
336
|
-
// Configuration Management
|
|
269
|
+
// Configuration Management (Async)
|
|
337
270
|
// ============================================================================
|
|
338
271
|
/**
|
|
339
272
|
* 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
273
|
*/
|
|
345
|
-
export function getConfigValue(
|
|
346
|
-
const
|
|
274
|
+
export async function getConfigValue(adapter, key) {
|
|
275
|
+
const knex = adapter.getKnex();
|
|
276
|
+
const result = await knex('m_config').where({ key }).first('value');
|
|
347
277
|
return result ? result.value : null;
|
|
348
278
|
}
|
|
349
279
|
/**
|
|
350
280
|
* 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
281
|
*/
|
|
356
|
-
export function setConfigValue(
|
|
282
|
+
export async function setConfigValue(adapter, key, value) {
|
|
283
|
+
const knex = adapter.getKnex();
|
|
357
284
|
const stringValue = String(value);
|
|
358
|
-
|
|
285
|
+
await knex('m_config')
|
|
286
|
+
.insert({ key, value: stringValue })
|
|
287
|
+
.onConflict('key')
|
|
288
|
+
.merge({ value: stringValue });
|
|
359
289
|
}
|
|
360
290
|
/**
|
|
361
291
|
* 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
292
|
*/
|
|
368
|
-
export function getConfigBool(
|
|
369
|
-
const value = getConfigValue(
|
|
293
|
+
export async function getConfigBool(adapter, key, defaultValue = false) {
|
|
294
|
+
const value = await getConfigValue(adapter, key);
|
|
370
295
|
if (value === null)
|
|
371
296
|
return defaultValue;
|
|
372
297
|
return value === '1' || value.toLowerCase() === 'true';
|
|
373
298
|
}
|
|
374
299
|
/**
|
|
375
300
|
* 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
301
|
*/
|
|
382
|
-
export function getConfigInt(
|
|
383
|
-
const value = getConfigValue(
|
|
302
|
+
export async function getConfigInt(adapter, key, defaultValue = 0) {
|
|
303
|
+
const value = await getConfigValue(adapter, key);
|
|
384
304
|
if (value === null)
|
|
385
305
|
return defaultValue;
|
|
386
306
|
const parsed = parseInt(value, 10);
|
|
@@ -388,12 +308,10 @@ export function getConfigInt(db, key, defaultValue = 0) {
|
|
|
388
308
|
}
|
|
389
309
|
/**
|
|
390
310
|
* Get all configuration as an object
|
|
391
|
-
*
|
|
392
|
-
* @param db - Database instance
|
|
393
|
-
* @returns Object with all m_config key-value pairs
|
|
394
311
|
*/
|
|
395
|
-
export function getAllConfig(
|
|
396
|
-
const
|
|
312
|
+
export async function getAllConfig(adapter) {
|
|
313
|
+
const knex = adapter.getKnex();
|
|
314
|
+
const rows = await knex('m_config').select('key', 'value');
|
|
397
315
|
const config = {};
|
|
398
316
|
for (const row of rows) {
|
|
399
317
|
config[row.key] = row.value;
|
|
@@ -401,35 +319,10 @@ export function getAllConfig(db) {
|
|
|
401
319
|
return config;
|
|
402
320
|
}
|
|
403
321
|
// ============================================================================
|
|
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)
|
|
322
|
+
// Decision Context Management (Async - v3.2.2)
|
|
428
323
|
// ============================================================================
|
|
429
324
|
/**
|
|
430
325
|
* 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
326
|
*/
|
|
434
327
|
function validateAlternativesJson(alternatives) {
|
|
435
328
|
if (alternatives === null || alternatives === undefined)
|
|
@@ -449,8 +342,6 @@ function validateAlternativesJson(alternatives) {
|
|
|
449
342
|
}
|
|
450
343
|
/**
|
|
451
344
|
* 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
345
|
*/
|
|
455
346
|
function validateTradeoffsJson(tradeoffs) {
|
|
456
347
|
if (tradeoffs === null || tradeoffs === undefined)
|
|
@@ -460,7 +351,6 @@ function validateTradeoffsJson(tradeoffs) {
|
|
|
460
351
|
if (typeof parsed !== 'object' || parsed === null) {
|
|
461
352
|
throw new Error('tradeoffs must be a JSON object');
|
|
462
353
|
}
|
|
463
|
-
// Optional: Check for pros/cons keys if provided
|
|
464
354
|
if (parsed.pros !== undefined && !Array.isArray(parsed.pros)) {
|
|
465
355
|
throw new Error('tradeoffs.pros must be an array');
|
|
466
356
|
}
|
|
@@ -477,85 +367,55 @@ function validateTradeoffsJson(tradeoffs) {
|
|
|
477
367
|
}
|
|
478
368
|
/**
|
|
479
369
|
* 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
370
|
*/
|
|
491
|
-
export function addDecisionContext(
|
|
371
|
+
export async function addDecisionContext(adapter, decisionKey, rationale, alternatives = null, tradeoffs = null, decidedBy = null, relatedTaskId = null, relatedConstraintId = null) {
|
|
492
372
|
// Validate JSON inputs
|
|
493
373
|
validateAlternativesJson(alternatives);
|
|
494
374
|
validateTradeoffsJson(tradeoffs);
|
|
375
|
+
const knex = adapter.getKnex();
|
|
495
376
|
// Get decision key ID
|
|
496
|
-
const keyId = getOrCreateContextKey(
|
|
377
|
+
const keyId = await getOrCreateContextKey(adapter, decisionKey);
|
|
497
378
|
// Get agent ID if provided
|
|
498
379
|
let agentId = null;
|
|
499
380
|
if (decidedBy) {
|
|
500
|
-
agentId = getOrCreateAgent(
|
|
381
|
+
agentId = await getOrCreateAgent(adapter, decidedBy);
|
|
501
382
|
}
|
|
502
383
|
// Insert context
|
|
503
|
-
const
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
return
|
|
384
|
+
const [id] = await knex('t_decision_context').insert({
|
|
385
|
+
decision_key_id: keyId,
|
|
386
|
+
rationale,
|
|
387
|
+
alternatives_considered: alternatives,
|
|
388
|
+
tradeoffs,
|
|
389
|
+
agent_id: agentId,
|
|
390
|
+
related_task_id: relatedTaskId,
|
|
391
|
+
related_constraint_id: relatedConstraintId,
|
|
392
|
+
decision_date: Math.floor(Date.now() / 1000),
|
|
393
|
+
ts: Math.floor(Date.now() / 1000),
|
|
394
|
+
});
|
|
395
|
+
return id;
|
|
515
396
|
}
|
|
516
397
|
/**
|
|
517
398
|
* 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
399
|
*/
|
|
523
|
-
export function getDecisionWithContext(
|
|
400
|
+
export async function getDecisionWithContext(adapter, decisionKey) {
|
|
401
|
+
const knex = adapter.getKnex();
|
|
524
402
|
// 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);
|
|
403
|
+
const decision = await knex('t_decisions as d')
|
|
404
|
+
.join('m_context_keys as k', 'd.key_id', 'k.id')
|
|
405
|
+
.leftJoin('m_layers as l', 'd.layer_id', 'l.id')
|
|
406
|
+
.leftJoin('m_agents as a', 'd.agent_id', 'a.id')
|
|
407
|
+
.where('k.key', decisionKey)
|
|
408
|
+
.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`))
|
|
409
|
+
.first();
|
|
540
410
|
if (!decision)
|
|
541
411
|
return null;
|
|
542
412
|
// 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);
|
|
413
|
+
const contexts = await knex('t_decision_context as dc')
|
|
414
|
+
.join('m_context_keys as k', 'dc.decision_key_id', 'k.id')
|
|
415
|
+
.leftJoin('m_agents as a', 'dc.agent_id', 'a.id')
|
|
416
|
+
.where('k.key', decisionKey)
|
|
417
|
+
.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')
|
|
418
|
+
.orderBy('dc.decision_date', 'desc');
|
|
559
419
|
return {
|
|
560
420
|
...decision,
|
|
561
421
|
context: contexts,
|
|
@@ -563,54 +423,46 @@ export function getDecisionWithContext(db, decisionKey) {
|
|
|
563
423
|
}
|
|
564
424
|
/**
|
|
565
425
|
* List decision contexts with optional filters
|
|
566
|
-
*
|
|
567
|
-
* @param db - Database instance
|
|
568
|
-
* @param filters - Optional filters
|
|
569
|
-
* @returns Array of decision contexts
|
|
570
426
|
*/
|
|
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 = [];
|
|
427
|
+
export async function listDecisionContexts(adapter, filters) {
|
|
428
|
+
const knex = adapter.getKnex();
|
|
429
|
+
let query = knex('t_decision_context as dc')
|
|
430
|
+
.join('m_context_keys as k', 'dc.decision_key_id', 'k.id')
|
|
431
|
+
.leftJoin('m_agents as a', 'dc.agent_id', 'a.id')
|
|
432
|
+
.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
433
|
if (filters?.decisionKey) {
|
|
590
|
-
query
|
|
591
|
-
params.push(filters.decisionKey);
|
|
434
|
+
query = query.where('k.key', filters.decisionKey);
|
|
592
435
|
}
|
|
593
436
|
if (filters?.relatedTaskId !== undefined) {
|
|
594
|
-
query
|
|
595
|
-
params.push(filters.relatedTaskId);
|
|
437
|
+
query = query.where('dc.related_task_id', filters.relatedTaskId);
|
|
596
438
|
}
|
|
597
439
|
if (filters?.relatedConstraintId !== undefined) {
|
|
598
|
-
query
|
|
599
|
-
params.push(filters.relatedConstraintId);
|
|
440
|
+
query = query.where('dc.related_constraint_id', filters.relatedConstraintId);
|
|
600
441
|
}
|
|
601
442
|
if (filters?.decidedBy) {
|
|
602
|
-
query
|
|
603
|
-
params.push(filters.decidedBy);
|
|
443
|
+
query = query.where('a.name', filters.decidedBy);
|
|
604
444
|
}
|
|
605
|
-
query
|
|
445
|
+
query = query.orderBy('dc.decision_date', 'desc');
|
|
606
446
|
if (filters?.limit) {
|
|
607
|
-
query
|
|
608
|
-
params.push(filters.limit);
|
|
447
|
+
query = query.limit(filters.limit);
|
|
609
448
|
}
|
|
610
449
|
if (filters?.offset) {
|
|
611
|
-
query
|
|
612
|
-
params.push(filters.offset);
|
|
450
|
+
query = query.offset(filters.offset);
|
|
613
451
|
}
|
|
614
|
-
return
|
|
452
|
+
return await query;
|
|
453
|
+
}
|
|
454
|
+
/**
|
|
455
|
+
* Backwards compatibility alias for getAdapter
|
|
456
|
+
*/
|
|
457
|
+
export function getDatabase() {
|
|
458
|
+
return getAdapter();
|
|
459
|
+
}
|
|
460
|
+
/**
|
|
461
|
+
* Execute a function within a database transaction
|
|
462
|
+
*/
|
|
463
|
+
export async function transaction(callback) {
|
|
464
|
+
const adapter = getAdapter();
|
|
465
|
+
const knex = adapter.getKnex();
|
|
466
|
+
return await knex.transaction(callback);
|
|
615
467
|
}
|
|
616
468
|
//# sourceMappingURL=database.js.map
|