@soleri/core 2.1.0 → 2.5.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/dist/brain/brain.d.ts +10 -1
- package/dist/brain/brain.d.ts.map +1 -1
- package/dist/brain/brain.js +116 -13
- package/dist/brain/brain.js.map +1 -1
- package/dist/brain/intelligence.d.ts +36 -1
- package/dist/brain/intelligence.d.ts.map +1 -1
- package/dist/brain/intelligence.js +119 -14
- package/dist/brain/intelligence.js.map +1 -1
- package/dist/brain/types.d.ts +34 -2
- package/dist/brain/types.d.ts.map +1 -1
- package/dist/cognee/client.d.ts +3 -0
- package/dist/cognee/client.d.ts.map +1 -1
- package/dist/cognee/client.js +17 -0
- package/dist/cognee/client.js.map +1 -1
- package/dist/cognee/sync-manager.d.ts +94 -0
- package/dist/cognee/sync-manager.d.ts.map +1 -0
- package/dist/cognee/sync-manager.js +293 -0
- package/dist/cognee/sync-manager.js.map +1 -0
- package/dist/control/identity-manager.d.ts +22 -0
- package/dist/control/identity-manager.d.ts.map +1 -0
- package/dist/control/identity-manager.js +233 -0
- package/dist/control/identity-manager.js.map +1 -0
- package/dist/control/intent-router.d.ts +32 -0
- package/dist/control/intent-router.d.ts.map +1 -0
- package/dist/control/intent-router.js +242 -0
- package/dist/control/intent-router.js.map +1 -0
- package/dist/control/types.d.ts +68 -0
- package/dist/control/types.d.ts.map +1 -0
- package/dist/control/types.js +9 -0
- package/dist/control/types.js.map +1 -0
- package/dist/curator/curator.d.ts +37 -1
- package/dist/curator/curator.d.ts.map +1 -1
- package/dist/curator/curator.js +199 -1
- package/dist/curator/curator.js.map +1 -1
- package/dist/errors/classify.d.ts +13 -0
- package/dist/errors/classify.d.ts.map +1 -0
- package/dist/errors/classify.js +97 -0
- package/dist/errors/classify.js.map +1 -0
- package/dist/errors/index.d.ts +6 -0
- package/dist/errors/index.d.ts.map +1 -0
- package/dist/errors/index.js +4 -0
- package/dist/errors/index.js.map +1 -0
- package/dist/errors/retry.d.ts +40 -0
- package/dist/errors/retry.d.ts.map +1 -0
- package/dist/errors/retry.js +97 -0
- package/dist/errors/retry.js.map +1 -0
- package/dist/errors/types.d.ts +48 -0
- package/dist/errors/types.d.ts.map +1 -0
- package/dist/errors/types.js +59 -0
- package/dist/errors/types.js.map +1 -0
- package/dist/facades/types.d.ts +1 -1
- package/dist/governance/governance.d.ts +42 -0
- package/dist/governance/governance.d.ts.map +1 -0
- package/dist/governance/governance.js +488 -0
- package/dist/governance/governance.js.map +1 -0
- package/dist/governance/index.d.ts +3 -0
- package/dist/governance/index.d.ts.map +1 -0
- package/dist/governance/index.js +2 -0
- package/dist/governance/index.js.map +1 -0
- package/dist/governance/types.d.ts +102 -0
- package/dist/governance/types.d.ts.map +1 -0
- package/dist/governance/types.js +3 -0
- package/dist/governance/types.js.map +1 -0
- package/dist/index.d.ts +52 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +47 -1
- package/dist/index.js.map +1 -1
- package/dist/intake/content-classifier.d.ts +14 -0
- package/dist/intake/content-classifier.d.ts.map +1 -0
- package/dist/intake/content-classifier.js +125 -0
- package/dist/intake/content-classifier.js.map +1 -0
- package/dist/intake/dedup-gate.d.ts +17 -0
- package/dist/intake/dedup-gate.d.ts.map +1 -0
- package/dist/intake/dedup-gate.js +66 -0
- package/dist/intake/dedup-gate.js.map +1 -0
- package/dist/intake/intake-pipeline.d.ts +63 -0
- package/dist/intake/intake-pipeline.d.ts.map +1 -0
- package/dist/intake/intake-pipeline.js +373 -0
- package/dist/intake/intake-pipeline.js.map +1 -0
- package/dist/intake/types.d.ts +65 -0
- package/dist/intake/types.d.ts.map +1 -0
- package/dist/intake/types.js +3 -0
- package/dist/intake/types.js.map +1 -0
- package/dist/intelligence/loader.js +1 -1
- package/dist/intelligence/loader.js.map +1 -1
- package/dist/intelligence/types.d.ts +3 -1
- package/dist/intelligence/types.d.ts.map +1 -1
- package/dist/logging/logger.d.ts +37 -0
- package/dist/logging/logger.d.ts.map +1 -0
- package/dist/logging/logger.js +145 -0
- package/dist/logging/logger.js.map +1 -0
- package/dist/logging/types.d.ts +19 -0
- package/dist/logging/types.d.ts.map +1 -0
- package/dist/logging/types.js +2 -0
- package/dist/logging/types.js.map +1 -0
- package/dist/loop/loop-manager.d.ts +100 -0
- package/dist/loop/loop-manager.d.ts.map +1 -0
- package/dist/loop/loop-manager.js +379 -0
- package/dist/loop/loop-manager.js.map +1 -0
- package/dist/loop/types.d.ts +103 -0
- package/dist/loop/types.d.ts.map +1 -0
- package/dist/loop/types.js +11 -0
- package/dist/loop/types.js.map +1 -0
- package/dist/persistence/index.d.ts +3 -0
- package/dist/persistence/index.d.ts.map +1 -0
- package/dist/persistence/index.js +2 -0
- package/dist/persistence/index.js.map +1 -0
- package/dist/persistence/sqlite-provider.d.ts +25 -0
- package/dist/persistence/sqlite-provider.d.ts.map +1 -0
- package/dist/persistence/sqlite-provider.js +59 -0
- package/dist/persistence/sqlite-provider.js.map +1 -0
- package/dist/persistence/types.d.ts +36 -0
- package/dist/persistence/types.d.ts.map +1 -0
- package/dist/persistence/types.js +8 -0
- package/dist/persistence/types.js.map +1 -0
- package/dist/planning/gap-analysis.d.ts +72 -0
- package/dist/planning/gap-analysis.d.ts.map +1 -0
- package/dist/planning/gap-analysis.js +442 -0
- package/dist/planning/gap-analysis.js.map +1 -0
- package/dist/planning/gap-types.d.ts +29 -0
- package/dist/planning/gap-types.d.ts.map +1 -0
- package/dist/planning/gap-types.js +28 -0
- package/dist/planning/gap-types.js.map +1 -0
- package/dist/planning/planner.d.ts +421 -4
- package/dist/planning/planner.d.ts.map +1 -1
- package/dist/planning/planner.js +949 -21
- package/dist/planning/planner.js.map +1 -1
- package/dist/playbooks/generic/brainstorming.d.ts +9 -0
- package/dist/playbooks/generic/brainstorming.d.ts.map +1 -0
- package/dist/playbooks/generic/brainstorming.js +105 -0
- package/dist/playbooks/generic/brainstorming.js.map +1 -0
- package/dist/playbooks/generic/code-review.d.ts +11 -0
- package/dist/playbooks/generic/code-review.d.ts.map +1 -0
- package/dist/playbooks/generic/code-review.js +176 -0
- package/dist/playbooks/generic/code-review.js.map +1 -0
- package/dist/playbooks/generic/subagent-execution.d.ts +9 -0
- package/dist/playbooks/generic/subagent-execution.d.ts.map +1 -0
- package/dist/playbooks/generic/subagent-execution.js +68 -0
- package/dist/playbooks/generic/subagent-execution.js.map +1 -0
- package/dist/playbooks/generic/systematic-debugging.d.ts +9 -0
- package/dist/playbooks/generic/systematic-debugging.d.ts.map +1 -0
- package/dist/playbooks/generic/systematic-debugging.js +87 -0
- package/dist/playbooks/generic/systematic-debugging.js.map +1 -0
- package/dist/playbooks/generic/tdd.d.ts +9 -0
- package/dist/playbooks/generic/tdd.d.ts.map +1 -0
- package/dist/playbooks/generic/tdd.js +70 -0
- package/dist/playbooks/generic/tdd.js.map +1 -0
- package/dist/playbooks/generic/verification.d.ts +9 -0
- package/dist/playbooks/generic/verification.d.ts.map +1 -0
- package/dist/playbooks/generic/verification.js +74 -0
- package/dist/playbooks/generic/verification.js.map +1 -0
- package/dist/playbooks/index.d.ts +4 -0
- package/dist/playbooks/index.d.ts.map +1 -0
- package/dist/playbooks/index.js +5 -0
- package/dist/playbooks/index.js.map +1 -0
- package/dist/playbooks/playbook-registry.d.ts +42 -0
- package/dist/playbooks/playbook-registry.d.ts.map +1 -0
- package/dist/playbooks/playbook-registry.js +227 -0
- package/dist/playbooks/playbook-registry.js.map +1 -0
- package/dist/playbooks/playbook-seeder.d.ts +47 -0
- package/dist/playbooks/playbook-seeder.d.ts.map +1 -0
- package/dist/playbooks/playbook-seeder.js +104 -0
- package/dist/playbooks/playbook-seeder.js.map +1 -0
- package/dist/playbooks/playbook-types.d.ts +132 -0
- package/dist/playbooks/playbook-types.d.ts.map +1 -0
- package/dist/playbooks/playbook-types.js +12 -0
- package/dist/playbooks/playbook-types.js.map +1 -0
- package/dist/project/project-registry.d.ts +79 -0
- package/dist/project/project-registry.d.ts.map +1 -0
- package/dist/project/project-registry.js +274 -0
- package/dist/project/project-registry.js.map +1 -0
- package/dist/project/types.d.ts +28 -0
- package/dist/project/types.d.ts.map +1 -0
- package/dist/project/types.js +5 -0
- package/dist/project/types.js.map +1 -0
- package/dist/prompts/index.d.ts +4 -0
- package/dist/prompts/index.d.ts.map +1 -0
- package/dist/prompts/index.js +3 -0
- package/dist/prompts/index.js.map +1 -0
- package/dist/prompts/parser.d.ts +17 -0
- package/dist/prompts/parser.d.ts.map +1 -0
- package/dist/prompts/parser.js +47 -0
- package/dist/prompts/parser.js.map +1 -0
- package/dist/prompts/template-manager.d.ts +25 -0
- package/dist/prompts/template-manager.d.ts.map +1 -0
- package/dist/prompts/template-manager.js +71 -0
- package/dist/prompts/template-manager.js.map +1 -0
- package/dist/prompts/types.d.ts +26 -0
- package/dist/prompts/types.d.ts.map +1 -0
- package/dist/prompts/types.js +5 -0
- package/dist/prompts/types.js.map +1 -0
- package/dist/runtime/admin-extra-ops.d.ts +15 -0
- package/dist/runtime/admin-extra-ops.d.ts.map +1 -0
- package/dist/runtime/admin-extra-ops.js +595 -0
- package/dist/runtime/admin-extra-ops.js.map +1 -0
- package/dist/runtime/admin-ops.d.ts +15 -0
- package/dist/runtime/admin-ops.d.ts.map +1 -0
- package/dist/runtime/admin-ops.js +329 -0
- package/dist/runtime/admin-ops.js.map +1 -0
- package/dist/runtime/capture-ops.d.ts +15 -0
- package/dist/runtime/capture-ops.d.ts.map +1 -0
- package/dist/runtime/capture-ops.js +363 -0
- package/dist/runtime/capture-ops.js.map +1 -0
- package/dist/runtime/cognee-sync-ops.d.ts +12 -0
- package/dist/runtime/cognee-sync-ops.d.ts.map +1 -0
- package/dist/runtime/cognee-sync-ops.js +55 -0
- package/dist/runtime/cognee-sync-ops.js.map +1 -0
- package/dist/runtime/core-ops.d.ts +9 -3
- package/dist/runtime/core-ops.d.ts.map +1 -1
- package/dist/runtime/core-ops.js +693 -10
- package/dist/runtime/core-ops.js.map +1 -1
- package/dist/runtime/curator-extra-ops.d.ts +9 -0
- package/dist/runtime/curator-extra-ops.d.ts.map +1 -0
- package/dist/runtime/curator-extra-ops.js +71 -0
- package/dist/runtime/curator-extra-ops.js.map +1 -0
- package/dist/runtime/domain-ops.d.ts.map +1 -1
- package/dist/runtime/domain-ops.js +61 -15
- package/dist/runtime/domain-ops.js.map +1 -1
- package/dist/runtime/grading-ops.d.ts +14 -0
- package/dist/runtime/grading-ops.d.ts.map +1 -0
- package/dist/runtime/grading-ops.js +105 -0
- package/dist/runtime/grading-ops.js.map +1 -0
- package/dist/runtime/intake-ops.d.ts +14 -0
- package/dist/runtime/intake-ops.d.ts.map +1 -0
- package/dist/runtime/intake-ops.js +110 -0
- package/dist/runtime/intake-ops.js.map +1 -0
- package/dist/runtime/loop-ops.d.ts +14 -0
- package/dist/runtime/loop-ops.d.ts.map +1 -0
- package/dist/runtime/loop-ops.js +251 -0
- package/dist/runtime/loop-ops.js.map +1 -0
- package/dist/runtime/memory-cross-project-ops.d.ts +12 -0
- package/dist/runtime/memory-cross-project-ops.d.ts.map +1 -0
- package/dist/runtime/memory-cross-project-ops.js +165 -0
- package/dist/runtime/memory-cross-project-ops.js.map +1 -0
- package/dist/runtime/memory-extra-ops.d.ts +13 -0
- package/dist/runtime/memory-extra-ops.d.ts.map +1 -0
- package/dist/runtime/memory-extra-ops.js +173 -0
- package/dist/runtime/memory-extra-ops.js.map +1 -0
- package/dist/runtime/orchestrate-ops.d.ts +17 -0
- package/dist/runtime/orchestrate-ops.d.ts.map +1 -0
- package/dist/runtime/orchestrate-ops.js +246 -0
- package/dist/runtime/orchestrate-ops.js.map +1 -0
- package/dist/runtime/planning-extra-ops.d.ts +25 -0
- package/dist/runtime/planning-extra-ops.d.ts.map +1 -0
- package/dist/runtime/planning-extra-ops.js +663 -0
- package/dist/runtime/planning-extra-ops.js.map +1 -0
- package/dist/runtime/playbook-ops.d.ts +14 -0
- package/dist/runtime/playbook-ops.d.ts.map +1 -0
- package/dist/runtime/playbook-ops.js +141 -0
- package/dist/runtime/playbook-ops.js.map +1 -0
- package/dist/runtime/project-ops.d.ts +15 -0
- package/dist/runtime/project-ops.d.ts.map +1 -0
- package/dist/runtime/project-ops.js +186 -0
- package/dist/runtime/project-ops.js.map +1 -0
- package/dist/runtime/runtime.d.ts.map +1 -1
- package/dist/runtime/runtime.js +65 -3
- package/dist/runtime/runtime.js.map +1 -1
- package/dist/runtime/types.d.ts +29 -0
- package/dist/runtime/types.d.ts.map +1 -1
- package/dist/runtime/vault-extra-ops.d.ts +10 -0
- package/dist/runtime/vault-extra-ops.d.ts.map +1 -0
- package/dist/runtime/vault-extra-ops.js +536 -0
- package/dist/runtime/vault-extra-ops.js.map +1 -0
- package/dist/telemetry/telemetry.d.ts +48 -0
- package/dist/telemetry/telemetry.d.ts.map +1 -0
- package/dist/telemetry/telemetry.js +87 -0
- package/dist/telemetry/telemetry.js.map +1 -0
- package/dist/vault/playbook.d.ts +34 -0
- package/dist/vault/playbook.d.ts.map +1 -0
- package/dist/vault/playbook.js +60 -0
- package/dist/vault/playbook.js.map +1 -0
- package/dist/vault/vault.d.ts +97 -4
- package/dist/vault/vault.d.ts.map +1 -1
- package/dist/vault/vault.js +424 -65
- package/dist/vault/vault.js.map +1 -1
- package/package.json +7 -3
- package/src/__tests__/admin-extra-ops.test.ts +467 -0
- package/src/__tests__/admin-ops.test.ts +271 -0
- package/src/__tests__/brain-intelligence.test.ts +205 -0
- package/src/__tests__/brain.test.ts +134 -3
- package/src/__tests__/capture-ops.test.ts +509 -0
- package/src/__tests__/cognee-integration.test.ts +80 -0
- package/src/__tests__/cognee-sync-manager.test.ts +103 -0
- package/src/__tests__/core-ops.test.ts +292 -2
- package/src/__tests__/curator-extra-ops.test.ts +381 -0
- package/src/__tests__/domain-ops.test.ts +66 -0
- package/src/__tests__/errors.test.ts +388 -0
- package/src/__tests__/governance.test.ts +522 -0
- package/src/__tests__/grading-ops.test.ts +361 -0
- package/src/__tests__/identity-manager.test.ts +243 -0
- package/src/__tests__/intake-pipeline.test.ts +162 -0
- package/src/__tests__/intent-router.test.ts +222 -0
- package/src/__tests__/logger.test.ts +200 -0
- package/src/__tests__/loop-ops.test.ts +469 -0
- package/src/__tests__/memory-cross-project-ops.test.ts +248 -0
- package/src/__tests__/memory-extra-ops.test.ts +352 -0
- package/src/__tests__/orchestrate-ops.test.ts +289 -0
- package/src/__tests__/persistence.test.ts +225 -0
- package/src/__tests__/planner.test.ts +416 -7
- package/src/__tests__/planning-extra-ops.test.ts +706 -0
- package/src/__tests__/playbook-registry.test.ts +326 -0
- package/src/__tests__/playbook-seeder.test.ts +163 -0
- package/src/__tests__/playbook.test.ts +389 -0
- package/src/__tests__/project-ops.test.ts +381 -0
- package/src/__tests__/template-manager.test.ts +222 -0
- package/src/__tests__/vault-extra-ops.test.ts +482 -0
- package/src/brain/brain.ts +185 -16
- package/src/brain/intelligence.ts +179 -10
- package/src/brain/types.ts +40 -2
- package/src/cognee/client.ts +18 -0
- package/src/cognee/sync-manager.ts +389 -0
- package/src/control/identity-manager.ts +354 -0
- package/src/control/intent-router.ts +326 -0
- package/src/control/types.ts +102 -0
- package/src/curator/curator.ts +295 -1
- package/src/errors/classify.ts +102 -0
- package/src/errors/index.ts +5 -0
- package/src/errors/retry.ts +132 -0
- package/src/errors/types.ts +81 -0
- package/src/governance/governance.ts +698 -0
- package/src/governance/index.ts +18 -0
- package/src/governance/types.ts +111 -0
- package/src/index.ts +213 -2
- package/src/intake/content-classifier.ts +146 -0
- package/src/intake/dedup-gate.ts +92 -0
- package/src/intake/intake-pipeline.ts +503 -0
- package/src/intake/types.ts +69 -0
- package/src/intelligence/loader.ts +1 -1
- package/src/intelligence/types.ts +3 -1
- package/src/logging/logger.ts +154 -0
- package/src/logging/types.ts +21 -0
- package/src/loop/loop-manager.ts +448 -0
- package/src/loop/types.ts +115 -0
- package/src/persistence/index.ts +7 -0
- package/src/persistence/sqlite-provider.ts +62 -0
- package/src/persistence/types.ts +44 -0
- package/src/planning/gap-analysis.ts +775 -0
- package/src/planning/gap-types.ts +61 -0
- package/src/planning/planner.ts +1273 -24
- package/src/playbooks/generic/brainstorming.ts +110 -0
- package/src/playbooks/generic/code-review.ts +181 -0
- package/src/playbooks/generic/subagent-execution.ts +74 -0
- package/src/playbooks/generic/systematic-debugging.ts +92 -0
- package/src/playbooks/generic/tdd.ts +75 -0
- package/src/playbooks/generic/verification.ts +79 -0
- package/src/playbooks/index.ts +27 -0
- package/src/playbooks/playbook-registry.ts +284 -0
- package/src/playbooks/playbook-seeder.ts +119 -0
- package/src/playbooks/playbook-types.ts +162 -0
- package/src/project/project-registry.ts +370 -0
- package/src/project/types.ts +31 -0
- package/src/prompts/index.ts +3 -0
- package/src/prompts/parser.ts +59 -0
- package/src/prompts/template-manager.ts +77 -0
- package/src/prompts/types.ts +28 -0
- package/src/runtime/admin-extra-ops.ts +652 -0
- package/src/runtime/admin-ops.ts +340 -0
- package/src/runtime/capture-ops.ts +404 -0
- package/src/runtime/cognee-sync-ops.ts +63 -0
- package/src/runtime/core-ops.ts +787 -9
- package/src/runtime/curator-extra-ops.ts +85 -0
- package/src/runtime/domain-ops.ts +67 -15
- package/src/runtime/grading-ops.ts +130 -0
- package/src/runtime/intake-ops.ts +126 -0
- package/src/runtime/loop-ops.ts +277 -0
- package/src/runtime/memory-cross-project-ops.ts +191 -0
- package/src/runtime/memory-extra-ops.ts +186 -0
- package/src/runtime/orchestrate-ops.ts +278 -0
- package/src/runtime/planning-extra-ops.ts +718 -0
- package/src/runtime/playbook-ops.ts +169 -0
- package/src/runtime/project-ops.ts +202 -0
- package/src/runtime/runtime.ts +77 -3
- package/src/runtime/types.ts +29 -0
- package/src/runtime/vault-extra-ops.ts +606 -0
- package/src/telemetry/telemetry.ts +118 -0
- package/src/vault/playbook.ts +87 -0
- package/src/vault/vault.ts +575 -98
package/src/vault/vault.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
3
|
-
import { dirname } from 'node:path';
|
|
1
|
+
import type { PersistenceProvider } from '../persistence/types.js';
|
|
2
|
+
import { SQLitePersistenceProvider } from '../persistence/sqlite-provider.js';
|
|
4
3
|
import type { IntelligenceEntry } from '../intelligence/types.js';
|
|
5
4
|
|
|
6
5
|
export interface SearchResult {
|
|
@@ -39,21 +38,43 @@ export interface MemoryStats {
|
|
|
39
38
|
}
|
|
40
39
|
|
|
41
40
|
export class Vault {
|
|
42
|
-
private
|
|
41
|
+
private provider: PersistenceProvider;
|
|
42
|
+
private sqliteProvider: SQLitePersistenceProvider | null;
|
|
43
|
+
private syncManager: import('../cognee/sync-manager.js').CogneeSyncManager | null = null;
|
|
43
44
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
45
|
+
/**
|
|
46
|
+
* Create a Vault with a PersistenceProvider or a SQLite path (backward compat).
|
|
47
|
+
*/
|
|
48
|
+
constructor(providerOrPath: PersistenceProvider | string = ':memory:') {
|
|
49
|
+
if (typeof providerOrPath === 'string') {
|
|
50
|
+
const sqlite = new SQLitePersistenceProvider(providerOrPath);
|
|
51
|
+
this.provider = sqlite;
|
|
52
|
+
this.sqliteProvider = sqlite;
|
|
53
|
+
// SQLite-specific pragmas
|
|
54
|
+
this.provider.run('PRAGMA journal_mode = WAL');
|
|
55
|
+
this.provider.run('PRAGMA foreign_keys = ON');
|
|
56
|
+
} else {
|
|
57
|
+
this.provider = providerOrPath;
|
|
58
|
+
this.sqliteProvider =
|
|
59
|
+
providerOrPath instanceof SQLitePersistenceProvider ? providerOrPath : null;
|
|
60
|
+
}
|
|
49
61
|
this.initialize();
|
|
50
62
|
}
|
|
51
63
|
|
|
64
|
+
setSyncManager(mgr: import('../cognee/sync-manager.js').CogneeSyncManager): void {
|
|
65
|
+
this.syncManager = mgr;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/** Backward-compatible factory. */
|
|
69
|
+
static createWithSQLite(dbPath: string = ':memory:'): Vault {
|
|
70
|
+
return new Vault(dbPath);
|
|
71
|
+
}
|
|
72
|
+
|
|
52
73
|
private initialize(): void {
|
|
53
|
-
this.
|
|
74
|
+
this.provider.execSql(`
|
|
54
75
|
CREATE TABLE IF NOT EXISTS entries (
|
|
55
76
|
id TEXT PRIMARY KEY,
|
|
56
|
-
type TEXT NOT NULL CHECK(type IN ('pattern', 'anti-pattern', 'rule')),
|
|
77
|
+
type TEXT NOT NULL CHECK(type IN ('pattern', 'anti-pattern', 'rule', 'playbook')),
|
|
57
78
|
domain TEXT NOT NULL,
|
|
58
79
|
title TEXT NOT NULL,
|
|
59
80
|
severity TEXT NOT NULL CHECK(severity IN ('critical', 'warning', 'suggestion')),
|
|
@@ -121,25 +142,87 @@ export class Vault {
|
|
|
121
142
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
122
143
|
query TEXT NOT NULL,
|
|
123
144
|
entry_id TEXT NOT NULL,
|
|
124
|
-
action TEXT NOT NULL CHECK(action IN ('accepted', 'dismissed')),
|
|
145
|
+
action TEXT NOT NULL CHECK(action IN ('accepted', 'dismissed', 'modified', 'failed')),
|
|
146
|
+
source TEXT NOT NULL DEFAULT 'search',
|
|
147
|
+
confidence REAL NOT NULL DEFAULT 0.6,
|
|
148
|
+
duration INTEGER,
|
|
149
|
+
context TEXT NOT NULL DEFAULT '{}',
|
|
150
|
+
reason TEXT,
|
|
125
151
|
created_at INTEGER NOT NULL DEFAULT (unixepoch())
|
|
126
152
|
);
|
|
127
153
|
CREATE INDEX IF NOT EXISTS idx_brain_feedback_query ON brain_feedback(query);
|
|
128
154
|
`);
|
|
155
|
+
this.migrateBrainSchema();
|
|
156
|
+
this.migrateTemporalSchema();
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
private migrateTemporalSchema(): void {
|
|
160
|
+
try {
|
|
161
|
+
this.provider.run('ALTER TABLE entries ADD COLUMN valid_from INTEGER');
|
|
162
|
+
} catch {
|
|
163
|
+
// Column already exists
|
|
164
|
+
}
|
|
165
|
+
try {
|
|
166
|
+
this.provider.run('ALTER TABLE entries ADD COLUMN valid_until INTEGER');
|
|
167
|
+
} catch {
|
|
168
|
+
// Column already exists
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
private migrateBrainSchema(): void {
|
|
173
|
+
const columns = this.provider.all<{ name: string }>('PRAGMA table_info(brain_feedback)');
|
|
174
|
+
const hasSource = columns.some((c) => c.name === 'source');
|
|
175
|
+
|
|
176
|
+
if (!hasSource && columns.length > 0) {
|
|
177
|
+
this.provider.transaction(() => {
|
|
178
|
+
this.provider.run(`
|
|
179
|
+
CREATE TABLE brain_feedback_new (
|
|
180
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
181
|
+
query TEXT NOT NULL,
|
|
182
|
+
entry_id TEXT NOT NULL,
|
|
183
|
+
action TEXT NOT NULL CHECK(action IN ('accepted', 'dismissed', 'modified', 'failed')),
|
|
184
|
+
source TEXT NOT NULL DEFAULT 'search',
|
|
185
|
+
confidence REAL NOT NULL DEFAULT 0.6,
|
|
186
|
+
duration INTEGER,
|
|
187
|
+
context TEXT NOT NULL DEFAULT '{}',
|
|
188
|
+
reason TEXT,
|
|
189
|
+
created_at INTEGER NOT NULL DEFAULT (unixepoch())
|
|
190
|
+
)
|
|
191
|
+
`);
|
|
192
|
+
this.provider.run(`
|
|
193
|
+
INSERT INTO brain_feedback_new (id, query, entry_id, action, created_at)
|
|
194
|
+
SELECT id, query, entry_id, action, created_at FROM brain_feedback
|
|
195
|
+
`);
|
|
196
|
+
this.provider.run('DROP TABLE brain_feedback');
|
|
197
|
+
this.provider.run('ALTER TABLE brain_feedback_new RENAME TO brain_feedback');
|
|
198
|
+
this.provider.run(
|
|
199
|
+
'CREATE INDEX IF NOT EXISTS idx_brain_feedback_query ON brain_feedback(query)',
|
|
200
|
+
);
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
try {
|
|
205
|
+
const sessionCols = this.provider.all<{ name: string }>('PRAGMA table_info(brain_sessions)');
|
|
206
|
+
if (sessionCols.length > 0 && !sessionCols.some((c) => c.name === 'extracted_at')) {
|
|
207
|
+
this.provider.run('ALTER TABLE brain_sessions ADD COLUMN extracted_at TEXT');
|
|
208
|
+
}
|
|
209
|
+
} catch {
|
|
210
|
+
// brain_sessions table doesn't exist yet — BrainIntelligence will create it
|
|
211
|
+
}
|
|
129
212
|
}
|
|
130
213
|
|
|
131
214
|
seed(entries: IntelligenceEntry[]): number {
|
|
132
|
-
const
|
|
133
|
-
INSERT INTO entries (id,type,domain,title,severity,description,context,example,counter_example,why,tags,applies_to)
|
|
134
|
-
VALUES (@id,@type,@domain,@title,@severity,@description,@context,@example,@counterExample,@why,@tags,@appliesTo)
|
|
215
|
+
const sql = `
|
|
216
|
+
INSERT INTO entries (id,type,domain,title,severity,description,context,example,counter_example,why,tags,applies_to,valid_from,valid_until)
|
|
217
|
+
VALUES (@id,@type,@domain,@title,@severity,@description,@context,@example,@counterExample,@why,@tags,@appliesTo,@validFrom,@validUntil)
|
|
135
218
|
ON CONFLICT(id) DO UPDATE SET type=excluded.type,domain=excluded.domain,title=excluded.title,severity=excluded.severity,
|
|
136
219
|
description=excluded.description,context=excluded.context,example=excluded.example,counter_example=excluded.counter_example,
|
|
137
|
-
why=excluded.why,tags=excluded.tags,applies_to=excluded.applies_to,updated_at=unixepoch()
|
|
138
|
-
|
|
139
|
-
|
|
220
|
+
why=excluded.why,tags=excluded.tags,applies_to=excluded.applies_to,valid_from=excluded.valid_from,valid_until=excluded.valid_until,updated_at=unixepoch()
|
|
221
|
+
`;
|
|
222
|
+
return this.provider.transaction(() => {
|
|
140
223
|
let count = 0;
|
|
141
|
-
for (const entry of
|
|
142
|
-
|
|
224
|
+
for (const entry of entries) {
|
|
225
|
+
this.provider.run(sql, {
|
|
143
226
|
id: entry.id,
|
|
144
227
|
type: entry.type,
|
|
145
228
|
domain: entry.domain,
|
|
@@ -152,21 +235,31 @@ export class Vault {
|
|
|
152
235
|
why: entry.why ?? null,
|
|
153
236
|
tags: JSON.stringify(entry.tags),
|
|
154
237
|
appliesTo: JSON.stringify(entry.appliesTo ?? []),
|
|
238
|
+
validFrom: entry.validFrom ?? null,
|
|
239
|
+
validUntil: entry.validUntil ?? null,
|
|
155
240
|
});
|
|
156
241
|
count++;
|
|
242
|
+
if (this.syncManager) {
|
|
243
|
+
this.syncManager.enqueue('ingest', entry.id, entry);
|
|
244
|
+
}
|
|
157
245
|
}
|
|
158
246
|
return count;
|
|
159
247
|
});
|
|
160
|
-
return tx(entries);
|
|
161
248
|
}
|
|
162
249
|
|
|
163
250
|
search(
|
|
164
251
|
query: string,
|
|
165
|
-
options?: {
|
|
252
|
+
options?: {
|
|
253
|
+
domain?: string;
|
|
254
|
+
type?: string;
|
|
255
|
+
severity?: string;
|
|
256
|
+
limit?: number;
|
|
257
|
+
includeExpired?: boolean;
|
|
258
|
+
},
|
|
166
259
|
): SearchResult[] {
|
|
167
260
|
const limit = options?.limit ?? 10;
|
|
168
261
|
const filters: string[] = [];
|
|
169
|
-
const fp: Record<string,
|
|
262
|
+
const fp: Record<string, unknown> = {};
|
|
170
263
|
if (options?.domain) {
|
|
171
264
|
filters.push('e.domain = @domain');
|
|
172
265
|
fp.domain = options.domain;
|
|
@@ -179,13 +272,18 @@ export class Vault {
|
|
|
179
272
|
filters.push('e.severity = @severity');
|
|
180
273
|
fp.severity = options.severity;
|
|
181
274
|
}
|
|
275
|
+
if (!options?.includeExpired) {
|
|
276
|
+
const now = Math.floor(Date.now() / 1000);
|
|
277
|
+
filters.push('(e.valid_until IS NULL OR e.valid_until > @now)');
|
|
278
|
+
filters.push('(e.valid_from IS NULL OR e.valid_from <= @now)');
|
|
279
|
+
fp.now = now;
|
|
280
|
+
}
|
|
182
281
|
const wc = filters.length > 0 ? `AND ${filters.join(' AND ')}` : '';
|
|
183
282
|
try {
|
|
184
|
-
const rows = this.
|
|
185
|
-
.
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
.all({ query, limit, ...fp }) as Array<Record<string, unknown>>;
|
|
283
|
+
const rows = this.provider.all<Record<string, unknown>>(
|
|
284
|
+
`SELECT e.*, -rank as score FROM entries_fts fts JOIN entries e ON e.rowid = fts.rowid WHERE entries_fts MATCH @query ${wc} ORDER BY score DESC LIMIT @limit`,
|
|
285
|
+
{ query, limit, ...fp },
|
|
286
|
+
);
|
|
189
287
|
return rows.map(rowToSearchResult);
|
|
190
288
|
} catch {
|
|
191
289
|
return [];
|
|
@@ -193,9 +291,9 @@ export class Vault {
|
|
|
193
291
|
}
|
|
194
292
|
|
|
195
293
|
get(id: string): IntelligenceEntry | null {
|
|
196
|
-
const row = this.
|
|
197
|
-
|
|
198
|
-
|
|
294
|
+
const row = this.provider.get<Record<string, unknown>>('SELECT * FROM entries WHERE id = ?', [
|
|
295
|
+
id,
|
|
296
|
+
]);
|
|
199
297
|
return row ? rowToEntry(row) : null;
|
|
200
298
|
}
|
|
201
299
|
|
|
@@ -206,6 +304,7 @@ export class Vault {
|
|
|
206
304
|
tags?: string[];
|
|
207
305
|
limit?: number;
|
|
208
306
|
offset?: number;
|
|
307
|
+
includeExpired?: boolean;
|
|
209
308
|
}): IntelligenceEntry[] {
|
|
210
309
|
const filters: string[] = [];
|
|
211
310
|
const params: Record<string, unknown> = {};
|
|
@@ -228,26 +327,29 @@ export class Vault {
|
|
|
228
327
|
});
|
|
229
328
|
filters.push(`(${c.join(' OR ')})`);
|
|
230
329
|
}
|
|
330
|
+
if (!options?.includeExpired) {
|
|
331
|
+
const now = Math.floor(Date.now() / 1000);
|
|
332
|
+
filters.push('(valid_until IS NULL OR valid_until > @now)');
|
|
333
|
+
filters.push('(valid_from IS NULL OR valid_from <= @now)');
|
|
334
|
+
params.now = now;
|
|
335
|
+
}
|
|
231
336
|
const wc = filters.length > 0 ? `WHERE ${filters.join(' AND ')}` : '';
|
|
232
|
-
const rows = this.
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
.all({ ...params, limit: options?.limit ?? 50, offset: options?.offset ?? 0 }) as Array<
|
|
237
|
-
Record<string, unknown>
|
|
238
|
-
>;
|
|
337
|
+
const rows = this.provider.all<Record<string, unknown>>(
|
|
338
|
+
`SELECT * FROM entries ${wc} ORDER BY severity, domain, title LIMIT @limit OFFSET @offset`,
|
|
339
|
+
{ ...params, limit: options?.limit ?? 50, offset: options?.offset ?? 0 },
|
|
340
|
+
);
|
|
239
341
|
return rows.map(rowToEntry);
|
|
240
342
|
}
|
|
241
343
|
|
|
242
344
|
stats(): VaultStats {
|
|
243
|
-
const total = (
|
|
244
|
-
|
|
245
|
-
)
|
|
345
|
+
const total = this.provider.get<{ count: number }>(
|
|
346
|
+
'SELECT COUNT(*) as count FROM entries',
|
|
347
|
+
)!.count;
|
|
246
348
|
return {
|
|
247
349
|
totalEntries: total,
|
|
248
|
-
byType: gc(this.
|
|
249
|
-
byDomain: gc(this.
|
|
250
|
-
bySeverity: gc(this.
|
|
350
|
+
byType: gc(this.provider, 'type'),
|
|
351
|
+
byDomain: gc(this.provider, 'domain'),
|
|
352
|
+
bySeverity: gc(this.provider, 'severity'),
|
|
251
353
|
};
|
|
252
354
|
}
|
|
253
355
|
|
|
@@ -255,28 +357,186 @@ export class Vault {
|
|
|
255
357
|
this.seed([entry]);
|
|
256
358
|
}
|
|
257
359
|
remove(id: string): boolean {
|
|
258
|
-
|
|
360
|
+
const deleted = this.provider.run('DELETE FROM entries WHERE id = ?', [id]).changes > 0;
|
|
361
|
+
if (deleted && this.syncManager) {
|
|
362
|
+
this.syncManager.enqueue('delete', id);
|
|
363
|
+
}
|
|
364
|
+
return deleted;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
update(
|
|
368
|
+
id: string,
|
|
369
|
+
fields: Partial<
|
|
370
|
+
Pick<
|
|
371
|
+
IntelligenceEntry,
|
|
372
|
+
| 'title'
|
|
373
|
+
| 'description'
|
|
374
|
+
| 'context'
|
|
375
|
+
| 'example'
|
|
376
|
+
| 'counterExample'
|
|
377
|
+
| 'why'
|
|
378
|
+
| 'tags'
|
|
379
|
+
| 'appliesTo'
|
|
380
|
+
| 'severity'
|
|
381
|
+
| 'type'
|
|
382
|
+
| 'domain'
|
|
383
|
+
| 'validFrom'
|
|
384
|
+
| 'validUntil'
|
|
385
|
+
>
|
|
386
|
+
>,
|
|
387
|
+
): IntelligenceEntry | null {
|
|
388
|
+
const existing = this.get(id);
|
|
389
|
+
if (!existing) return null;
|
|
390
|
+
const merged: IntelligenceEntry = { ...existing, ...fields };
|
|
391
|
+
this.seed([merged]);
|
|
392
|
+
return this.get(id);
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
setTemporal(id: string, validFrom?: number, validUntil?: number): boolean {
|
|
396
|
+
const sets: string[] = [];
|
|
397
|
+
const params: Record<string, unknown> = { id };
|
|
398
|
+
if (validFrom !== undefined) {
|
|
399
|
+
sets.push('valid_from = @validFrom');
|
|
400
|
+
params.validFrom = validFrom;
|
|
401
|
+
}
|
|
402
|
+
if (validUntil !== undefined) {
|
|
403
|
+
sets.push('valid_until = @validUntil');
|
|
404
|
+
params.validUntil = validUntil;
|
|
405
|
+
}
|
|
406
|
+
if (sets.length === 0) return false;
|
|
407
|
+
sets.push('updated_at = unixepoch()');
|
|
408
|
+
return (
|
|
409
|
+
this.provider.run(`UPDATE entries SET ${sets.join(', ')} WHERE id = @id`, params).changes > 0
|
|
410
|
+
);
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
findExpiring(withinDays: number): IntelligenceEntry[] {
|
|
414
|
+
const now = Math.floor(Date.now() / 1000);
|
|
415
|
+
const cutoff = now + withinDays * 86400;
|
|
416
|
+
const rows = this.provider.all<Record<string, unknown>>(
|
|
417
|
+
'SELECT * FROM entries WHERE valid_until IS NOT NULL AND valid_until > @now AND valid_until <= @cutoff ORDER BY valid_until ASC',
|
|
418
|
+
{ now, cutoff },
|
|
419
|
+
);
|
|
420
|
+
return rows.map(rowToEntry);
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
findExpired(limit: number = 50): IntelligenceEntry[] {
|
|
424
|
+
const now = Math.floor(Date.now() / 1000);
|
|
425
|
+
const rows = this.provider.all<Record<string, unknown>>(
|
|
426
|
+
'SELECT * FROM entries WHERE valid_until IS NOT NULL AND valid_until <= @now ORDER BY valid_until DESC LIMIT @limit',
|
|
427
|
+
{ now, limit },
|
|
428
|
+
);
|
|
429
|
+
return rows.map(rowToEntry);
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
bulkRemove(ids: string[]): number {
|
|
433
|
+
return this.provider.transaction(() => {
|
|
434
|
+
let count = 0;
|
|
435
|
+
for (const id of ids) {
|
|
436
|
+
count += this.provider.run('DELETE FROM entries WHERE id = ?', [id]).changes;
|
|
437
|
+
if (this.syncManager) {
|
|
438
|
+
this.syncManager.enqueue('delete', id);
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
return count;
|
|
442
|
+
});
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
getTags(): Array<{ tag: string; count: number }> {
|
|
446
|
+
const rows = this.provider.all<{ tags: string }>('SELECT tags FROM entries');
|
|
447
|
+
const counts = new Map<string, number>();
|
|
448
|
+
for (const row of rows) {
|
|
449
|
+
const tags: string[] = JSON.parse(row.tags || '[]');
|
|
450
|
+
for (const tag of tags) {
|
|
451
|
+
counts.set(tag, (counts.get(tag) ?? 0) + 1);
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
return Array.from(counts.entries())
|
|
455
|
+
.map(([tag, count]) => ({ tag, count }))
|
|
456
|
+
.sort((a, b) => b.count - a.count);
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
getDomains(): Array<{ domain: string; count: number }> {
|
|
460
|
+
return this.provider.all<{ domain: string; count: number }>(
|
|
461
|
+
'SELECT domain, COUNT(*) as count FROM entries GROUP BY domain ORDER BY count DESC',
|
|
462
|
+
);
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
getRecent(limit: number = 20): IntelligenceEntry[] {
|
|
466
|
+
const rows = this.provider.all<Record<string, unknown>>(
|
|
467
|
+
'SELECT * FROM entries ORDER BY updated_at DESC LIMIT ?',
|
|
468
|
+
[limit],
|
|
469
|
+
);
|
|
470
|
+
return rows.map(rowToEntry);
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
exportAll(): { entries: IntelligenceEntry[]; exportedAt: number; count: number } {
|
|
474
|
+
const rows = this.provider.all<Record<string, unknown>>(
|
|
475
|
+
'SELECT * FROM entries ORDER BY domain, title',
|
|
476
|
+
);
|
|
477
|
+
const entries = rows.map(rowToEntry);
|
|
478
|
+
return { entries, exportedAt: Math.floor(Date.now() / 1000), count: entries.length };
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
getAgeReport(): {
|
|
482
|
+
total: number;
|
|
483
|
+
buckets: Array<{ label: string; count: number; minDays: number; maxDays: number }>;
|
|
484
|
+
oldestTimestamp: number | null;
|
|
485
|
+
newestTimestamp: number | null;
|
|
486
|
+
} {
|
|
487
|
+
const rows = this.provider.all<{ created_at: number; updated_at: number }>(
|
|
488
|
+
'SELECT created_at, updated_at FROM entries',
|
|
489
|
+
);
|
|
490
|
+
const now = Math.floor(Date.now() / 1000);
|
|
491
|
+
const bucketDefs = [
|
|
492
|
+
{ label: 'today', minDays: 0, maxDays: 1 },
|
|
493
|
+
{ label: 'this_week', minDays: 1, maxDays: 7 },
|
|
494
|
+
{ label: 'this_month', minDays: 7, maxDays: 30 },
|
|
495
|
+
{ label: 'this_quarter', minDays: 30, maxDays: 90 },
|
|
496
|
+
{ label: 'older', minDays: 90, maxDays: Infinity },
|
|
497
|
+
];
|
|
498
|
+
const counts = new Array(bucketDefs.length).fill(0) as number[];
|
|
499
|
+
let oldest: number | null = null;
|
|
500
|
+
let newest: number | null = null;
|
|
501
|
+
for (const row of rows) {
|
|
502
|
+
const ts = row.created_at;
|
|
503
|
+
if (oldest === null || ts < oldest) oldest = ts;
|
|
504
|
+
if (newest === null || ts > newest) newest = ts;
|
|
505
|
+
const ageDays = (now - ts) / 86400;
|
|
506
|
+
for (let i = 0; i < bucketDefs.length; i++) {
|
|
507
|
+
if (ageDays >= bucketDefs[i].minDays && ageDays < bucketDefs[i].maxDays) {
|
|
508
|
+
counts[i]++;
|
|
509
|
+
break;
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
return {
|
|
514
|
+
total: rows.length,
|
|
515
|
+
buckets: bucketDefs.map((b, i) => ({ ...b, count: counts[i] })),
|
|
516
|
+
oldestTimestamp: oldest,
|
|
517
|
+
newestTimestamp: newest,
|
|
518
|
+
};
|
|
259
519
|
}
|
|
260
520
|
|
|
261
521
|
registerProject(path: string, name?: string): ProjectInfo {
|
|
262
522
|
const projectName = name ?? path.replace(/\/$/, '').split('/').pop() ?? path;
|
|
263
523
|
const existing = this.getProject(path);
|
|
264
524
|
if (existing) {
|
|
265
|
-
this.
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
.run(path);
|
|
525
|
+
this.provider.run(
|
|
526
|
+
'UPDATE projects SET last_seen_at = unixepoch(), session_count = session_count + 1 WHERE path = ?',
|
|
527
|
+
[path],
|
|
528
|
+
);
|
|
270
529
|
return this.getProject(path)!;
|
|
271
530
|
}
|
|
272
|
-
this.
|
|
531
|
+
this.provider.run('INSERT INTO projects (path, name) VALUES (?, ?)', [path, projectName]);
|
|
273
532
|
return this.getProject(path)!;
|
|
274
533
|
}
|
|
275
534
|
|
|
276
535
|
getProject(path: string): ProjectInfo | null {
|
|
277
|
-
const row = this.
|
|
278
|
-
|
|
279
|
-
|
|
536
|
+
const row = this.provider.get<Record<string, unknown>>(
|
|
537
|
+
'SELECT * FROM projects WHERE path = ?',
|
|
538
|
+
[path],
|
|
539
|
+
);
|
|
280
540
|
if (!row) return null;
|
|
281
541
|
return {
|
|
282
542
|
path: row.path as string,
|
|
@@ -288,9 +548,9 @@ export class Vault {
|
|
|
288
548
|
}
|
|
289
549
|
|
|
290
550
|
listProjects(): ProjectInfo[] {
|
|
291
|
-
const rows = this.
|
|
292
|
-
|
|
293
|
-
|
|
551
|
+
const rows = this.provider.all<Record<string, unknown>>(
|
|
552
|
+
'SELECT * FROM projects ORDER BY last_seen_at DESC',
|
|
553
|
+
);
|
|
294
554
|
return rows.map((row) => ({
|
|
295
555
|
path: row.path as string,
|
|
296
556
|
name: row.name as string,
|
|
@@ -302,11 +562,9 @@ export class Vault {
|
|
|
302
562
|
|
|
303
563
|
captureMemory(memory: Omit<Memory, 'id' | 'createdAt' | 'archivedAt'>): Memory {
|
|
304
564
|
const id = `mem-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
305
|
-
this.
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
)
|
|
309
|
-
.run({
|
|
565
|
+
this.provider.run(
|
|
566
|
+
`INSERT INTO memories (id, project_path, type, context, summary, topics, files_modified, tools_used) VALUES (@id, @projectPath, @type, @context, @summary, @topics, @filesModified, @toolsUsed)`,
|
|
567
|
+
{
|
|
310
568
|
id,
|
|
311
569
|
projectPath: memory.projectPath,
|
|
312
570
|
type: memory.type,
|
|
@@ -315,7 +573,8 @@ export class Vault {
|
|
|
315
573
|
topics: JSON.stringify(memory.topics),
|
|
316
574
|
filesModified: JSON.stringify(memory.filesModified),
|
|
317
575
|
toolsUsed: JSON.stringify(memory.toolsUsed),
|
|
318
|
-
}
|
|
576
|
+
},
|
|
577
|
+
);
|
|
319
578
|
return this.getMemory(id)!;
|
|
320
579
|
}
|
|
321
580
|
|
|
@@ -336,11 +595,10 @@ export class Vault {
|
|
|
336
595
|
}
|
|
337
596
|
const wc = filters.length > 0 ? `AND ${filters.join(' AND ')}` : '';
|
|
338
597
|
try {
|
|
339
|
-
const rows = this.
|
|
340
|
-
.
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
.all({ query, limit, ...fp }) as Array<Record<string, unknown>>;
|
|
598
|
+
const rows = this.provider.all<Record<string, unknown>>(
|
|
599
|
+
`SELECT m.* FROM memories_fts fts JOIN memories m ON m.rowid = fts.rowid WHERE memories_fts MATCH @query ${wc} ORDER BY rank LIMIT @limit`,
|
|
600
|
+
{ query, limit, ...fp },
|
|
601
|
+
);
|
|
344
602
|
return rows.map(rowToMemory);
|
|
345
603
|
} catch {
|
|
346
604
|
return [];
|
|
@@ -364,30 +622,23 @@ export class Vault {
|
|
|
364
622
|
params.projectPath = options.projectPath;
|
|
365
623
|
}
|
|
366
624
|
const wc = `WHERE ${filters.join(' AND ')}`;
|
|
367
|
-
const rows = this.
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
>;
|
|
625
|
+
const rows = this.provider.all<Record<string, unknown>>(
|
|
626
|
+
`SELECT * FROM memories ${wc} ORDER BY created_at DESC LIMIT @limit OFFSET @offset`,
|
|
627
|
+
{ ...params, limit: options?.limit ?? 50, offset: options?.offset ?? 0 },
|
|
628
|
+
);
|
|
372
629
|
return rows.map(rowToMemory);
|
|
373
630
|
}
|
|
374
631
|
|
|
375
632
|
memoryStats(): MemoryStats {
|
|
376
|
-
const total = (
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
.all() as Array<{ key: string; count: number }>;
|
|
386
|
-
const byProjectRows = this.db
|
|
387
|
-
.prepare(
|
|
388
|
-
'SELECT project_path as key, COUNT(*) as count FROM memories WHERE archived_at IS NULL GROUP BY project_path',
|
|
389
|
-
)
|
|
390
|
-
.all() as Array<{ key: string; count: number }>;
|
|
633
|
+
const total = this.provider.get<{ count: number }>(
|
|
634
|
+
'SELECT COUNT(*) as count FROM memories WHERE archived_at IS NULL',
|
|
635
|
+
)!.count;
|
|
636
|
+
const byTypeRows = this.provider.all<{ key: string; count: number }>(
|
|
637
|
+
'SELECT type as key, COUNT(*) as count FROM memories WHERE archived_at IS NULL GROUP BY type',
|
|
638
|
+
);
|
|
639
|
+
const byProjectRows = this.provider.all<{ key: string; count: number }>(
|
|
640
|
+
'SELECT project_path as key, COUNT(*) as count FROM memories WHERE archived_at IS NULL GROUP BY project_path',
|
|
641
|
+
);
|
|
391
642
|
return {
|
|
392
643
|
total,
|
|
393
644
|
byType: Object.fromEntries(byTypeRows.map((r) => [r.key, r.count])),
|
|
@@ -396,25 +647,249 @@ export class Vault {
|
|
|
396
647
|
}
|
|
397
648
|
|
|
398
649
|
getMemory(id: string): Memory | null {
|
|
399
|
-
const row = this.
|
|
400
|
-
|
|
401
|
-
|
|
650
|
+
const row = this.provider.get<Record<string, unknown>>('SELECT * FROM memories WHERE id = ?', [
|
|
651
|
+
id,
|
|
652
|
+
]);
|
|
402
653
|
return row ? rowToMemory(row) : null;
|
|
403
654
|
}
|
|
404
655
|
|
|
405
|
-
|
|
406
|
-
return this.
|
|
656
|
+
deleteMemory(id: string): boolean {
|
|
657
|
+
return this.provider.run('DELETE FROM memories WHERE id = ?', [id]).changes > 0;
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
memoryStatsDetailed(options?: {
|
|
661
|
+
projectPath?: string;
|
|
662
|
+
fromDate?: number;
|
|
663
|
+
toDate?: number;
|
|
664
|
+
}): MemoryStats & { oldest: number | null; newest: number | null; archivedCount: number } {
|
|
665
|
+
const filters: string[] = [];
|
|
666
|
+
const params: Record<string, unknown> = {};
|
|
667
|
+
if (options?.projectPath) {
|
|
668
|
+
filters.push('project_path = @projectPath');
|
|
669
|
+
params.projectPath = options.projectPath;
|
|
670
|
+
}
|
|
671
|
+
if (options?.fromDate) {
|
|
672
|
+
filters.push('created_at >= @fromDate');
|
|
673
|
+
params.fromDate = options.fromDate;
|
|
674
|
+
}
|
|
675
|
+
if (options?.toDate) {
|
|
676
|
+
filters.push('created_at <= @toDate');
|
|
677
|
+
params.toDate = options.toDate;
|
|
678
|
+
}
|
|
679
|
+
const wc = filters.length > 0 ? `WHERE ${filters.join(' AND ')}` : '';
|
|
680
|
+
|
|
681
|
+
const total = this.provider.get<{ count: number }>(
|
|
682
|
+
`SELECT COUNT(*) as count FROM memories ${wc}${wc ? ' AND' : ' WHERE'} archived_at IS NULL`,
|
|
683
|
+
params,
|
|
684
|
+
)!.count;
|
|
685
|
+
|
|
686
|
+
const archivedCount = this.provider.get<{ count: number }>(
|
|
687
|
+
`SELECT COUNT(*) as count FROM memories ${wc}${wc ? ' AND' : ' WHERE'} archived_at IS NOT NULL`,
|
|
688
|
+
params,
|
|
689
|
+
)!.count;
|
|
690
|
+
|
|
691
|
+
const byTypeRows = this.provider.all<{ key: string; count: number }>(
|
|
692
|
+
`SELECT type as key, COUNT(*) as count FROM memories ${wc}${wc ? ' AND' : ' WHERE'} archived_at IS NULL GROUP BY type`,
|
|
693
|
+
params,
|
|
694
|
+
);
|
|
695
|
+
|
|
696
|
+
const byProjectRows = this.provider.all<{ key: string; count: number }>(
|
|
697
|
+
`SELECT project_path as key, COUNT(*) as count FROM memories ${wc}${wc ? ' AND' : ' WHERE'} archived_at IS NULL GROUP BY project_path`,
|
|
698
|
+
params,
|
|
699
|
+
);
|
|
700
|
+
|
|
701
|
+
const dateRange = this.provider.get<{ oldest: number | null; newest: number | null }>(
|
|
702
|
+
`SELECT MIN(created_at) as oldest, MAX(created_at) as newest FROM memories ${wc}${wc ? ' AND' : ' WHERE'} archived_at IS NULL`,
|
|
703
|
+
params,
|
|
704
|
+
)!;
|
|
705
|
+
|
|
706
|
+
return {
|
|
707
|
+
total,
|
|
708
|
+
byType: Object.fromEntries(byTypeRows.map((r) => [r.key, r.count])),
|
|
709
|
+
byProject: Object.fromEntries(byProjectRows.map((r) => [r.key, r.count])),
|
|
710
|
+
oldest: dateRange.oldest,
|
|
711
|
+
newest: dateRange.newest,
|
|
712
|
+
archivedCount,
|
|
713
|
+
};
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
exportMemories(options?: {
|
|
717
|
+
projectPath?: string;
|
|
718
|
+
type?: string;
|
|
719
|
+
includeArchived?: boolean;
|
|
720
|
+
}): Memory[] {
|
|
721
|
+
const filters: string[] = [];
|
|
722
|
+
const params: Record<string, unknown> = {};
|
|
723
|
+
if (!options?.includeArchived) {
|
|
724
|
+
filters.push('archived_at IS NULL');
|
|
725
|
+
}
|
|
726
|
+
if (options?.projectPath) {
|
|
727
|
+
filters.push('project_path = @projectPath');
|
|
728
|
+
params.projectPath = options.projectPath;
|
|
729
|
+
}
|
|
730
|
+
if (options?.type) {
|
|
731
|
+
filters.push('type = @type');
|
|
732
|
+
params.type = options.type;
|
|
733
|
+
}
|
|
734
|
+
const wc = filters.length > 0 ? `WHERE ${filters.join(' AND ')}` : '';
|
|
735
|
+
const rows = this.provider.all<Record<string, unknown>>(
|
|
736
|
+
`SELECT * FROM memories ${wc} ORDER BY created_at ASC`,
|
|
737
|
+
Object.keys(params).length > 0 ? params : undefined,
|
|
738
|
+
);
|
|
739
|
+
return rows.map(rowToMemory);
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
importMemories(memories: Memory[]): { imported: number; skipped: number } {
|
|
743
|
+
const sql = `
|
|
744
|
+
INSERT OR IGNORE INTO memories (id, project_path, type, context, summary, topics, files_modified, tools_used, created_at, archived_at)
|
|
745
|
+
VALUES (@id, @projectPath, @type, @context, @summary, @topics, @filesModified, @toolsUsed, @createdAt, @archivedAt)
|
|
746
|
+
`;
|
|
747
|
+
let imported = 0;
|
|
748
|
+
let skipped = 0;
|
|
749
|
+
this.provider.transaction(() => {
|
|
750
|
+
for (const m of memories) {
|
|
751
|
+
const result = this.provider.run(sql, {
|
|
752
|
+
id: m.id,
|
|
753
|
+
projectPath: m.projectPath,
|
|
754
|
+
type: m.type,
|
|
755
|
+
context: m.context,
|
|
756
|
+
summary: m.summary,
|
|
757
|
+
topics: JSON.stringify(m.topics),
|
|
758
|
+
filesModified: JSON.stringify(m.filesModified),
|
|
759
|
+
toolsUsed: JSON.stringify(m.toolsUsed),
|
|
760
|
+
createdAt: m.createdAt,
|
|
761
|
+
archivedAt: m.archivedAt,
|
|
762
|
+
});
|
|
763
|
+
if (result.changes > 0) imported++;
|
|
764
|
+
else skipped++;
|
|
765
|
+
}
|
|
766
|
+
});
|
|
767
|
+
return { imported, skipped };
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
pruneMemories(olderThanDays: number): { pruned: number } {
|
|
771
|
+
const cutoff = Math.floor(Date.now() / 1000) - olderThanDays * 86400;
|
|
772
|
+
const result = this.provider.run(
|
|
773
|
+
'DELETE FROM memories WHERE created_at < ? AND archived_at IS NULL',
|
|
774
|
+
[cutoff],
|
|
775
|
+
);
|
|
776
|
+
return { pruned: result.changes };
|
|
777
|
+
}
|
|
778
|
+
|
|
779
|
+
deduplicateMemories(): { removed: number; groups: Array<{ kept: string; removed: string[] }> } {
|
|
780
|
+
const dupeRows = this.provider.all<{ id1: string; id2: string }>(`
|
|
781
|
+
SELECT m1.id as id1, m2.id as id2
|
|
782
|
+
FROM memories m1
|
|
783
|
+
JOIN memories m2 ON m1.summary = m2.summary
|
|
784
|
+
AND m1.project_path = m2.project_path
|
|
785
|
+
AND m1.type = m2.type
|
|
786
|
+
AND m1.id < m2.id
|
|
787
|
+
AND m1.archived_at IS NULL
|
|
788
|
+
AND m2.archived_at IS NULL
|
|
789
|
+
`);
|
|
790
|
+
|
|
791
|
+
const groupMap = new Map<string, Set<string>>();
|
|
792
|
+
for (const row of dupeRows) {
|
|
793
|
+
if (!groupMap.has(row.id1)) groupMap.set(row.id1, new Set());
|
|
794
|
+
groupMap.get(row.id1)!.add(row.id2);
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
const groups: Array<{ kept: string; removed: string[] }> = [];
|
|
798
|
+
const toRemove = new Set<string>();
|
|
799
|
+
for (const [kept, removedSet] of groupMap) {
|
|
800
|
+
const removed = [...removedSet].filter((id) => !toRemove.has(id));
|
|
801
|
+
if (removed.length > 0) {
|
|
802
|
+
groups.push({ kept, removed });
|
|
803
|
+
for (const id of removed) toRemove.add(id);
|
|
804
|
+
}
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
if (toRemove.size > 0) {
|
|
808
|
+
this.provider.transaction(() => {
|
|
809
|
+
for (const id of toRemove) {
|
|
810
|
+
this.provider.run('DELETE FROM memories WHERE id = ?', [id]);
|
|
811
|
+
}
|
|
812
|
+
});
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
return { removed: toRemove.size, groups };
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
memoryTopics(): Array<{ topic: string; count: number }> {
|
|
819
|
+
const rows = this.provider.all<{ topics: string }>(
|
|
820
|
+
'SELECT topics FROM memories WHERE archived_at IS NULL',
|
|
821
|
+
);
|
|
822
|
+
|
|
823
|
+
const topicCounts = new Map<string, number>();
|
|
824
|
+
for (const row of rows) {
|
|
825
|
+
const topics: string[] = JSON.parse(row.topics || '[]');
|
|
826
|
+
for (const topic of topics) {
|
|
827
|
+
topicCounts.set(topic, (topicCounts.get(topic) ?? 0) + 1);
|
|
828
|
+
}
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
return [...topicCounts.entries()]
|
|
832
|
+
.map(([topic, count]) => ({ topic, count }))
|
|
833
|
+
.sort((a, b) => b.count - a.count);
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
memoriesByProject(): Array<{ project: string; count: number; memories: Memory[] }> {
|
|
837
|
+
const rows = this.provider.all<{ project: string; count: number }>(
|
|
838
|
+
'SELECT project_path as project, COUNT(*) as count FROM memories WHERE archived_at IS NULL GROUP BY project_path ORDER BY count DESC',
|
|
839
|
+
);
|
|
840
|
+
|
|
841
|
+
return rows.map((row) => {
|
|
842
|
+
const memories = this.provider.all<Record<string, unknown>>(
|
|
843
|
+
'SELECT * FROM memories WHERE project_path = ? AND archived_at IS NULL ORDER BY created_at DESC',
|
|
844
|
+
[row.project],
|
|
845
|
+
);
|
|
846
|
+
return {
|
|
847
|
+
project: row.project,
|
|
848
|
+
count: row.count,
|
|
849
|
+
memories: memories.map(rowToMemory),
|
|
850
|
+
};
|
|
851
|
+
});
|
|
852
|
+
}
|
|
853
|
+
|
|
854
|
+
/**
|
|
855
|
+
* Rebuild the FTS5 index for the entries table.
|
|
856
|
+
* Useful after bulk operations or if the index gets out of sync.
|
|
857
|
+
*/
|
|
858
|
+
rebuildFtsIndex(): void {
|
|
859
|
+
try {
|
|
860
|
+
this.provider.run("INSERT INTO entries_fts(entries_fts) VALUES('rebuild')");
|
|
861
|
+
} catch {
|
|
862
|
+
// Graceful degradation — FTS rebuild failed (e.g. table doesn't exist yet)
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
/**
|
|
867
|
+
* Get the underlying persistence provider.
|
|
868
|
+
*/
|
|
869
|
+
getProvider(): PersistenceProvider {
|
|
870
|
+
return this.provider;
|
|
871
|
+
}
|
|
872
|
+
|
|
873
|
+
/**
|
|
874
|
+
* Get the raw better-sqlite3 Database (backward compat).
|
|
875
|
+
* Throws if the provider is not SQLite.
|
|
876
|
+
*/
|
|
877
|
+
getDb(): import('better-sqlite3').Database {
|
|
878
|
+
if (this.sqliteProvider) {
|
|
879
|
+
return this.sqliteProvider.getDatabase();
|
|
880
|
+
}
|
|
881
|
+
throw new Error('getDb() is only available with SQLite provider');
|
|
407
882
|
}
|
|
408
883
|
|
|
409
884
|
close(): void {
|
|
410
|
-
this.
|
|
885
|
+
this.provider.close();
|
|
411
886
|
}
|
|
412
887
|
}
|
|
413
888
|
|
|
414
|
-
function gc(
|
|
415
|
-
const rows =
|
|
416
|
-
|
|
417
|
-
|
|
889
|
+
function gc(provider: PersistenceProvider, col: string): Record<string, number> {
|
|
890
|
+
const rows = provider.all<{ key: string; count: number }>(
|
|
891
|
+
`SELECT ${col} as key, COUNT(*) as count FROM entries GROUP BY ${col}`,
|
|
892
|
+
);
|
|
418
893
|
return Object.fromEntries(rows.map((r) => [r.key, r.count]));
|
|
419
894
|
}
|
|
420
895
|
|
|
@@ -432,6 +907,8 @@ function rowToEntry(row: Record<string, unknown>): IntelligenceEntry {
|
|
|
432
907
|
why: (row.why as string) ?? undefined,
|
|
433
908
|
tags: JSON.parse((row.tags as string) || '[]'),
|
|
434
909
|
appliesTo: JSON.parse((row.applies_to as string) || '[]'),
|
|
910
|
+
validFrom: (row.valid_from as number) ?? undefined,
|
|
911
|
+
validUntil: (row.valid_until as number) ?? undefined,
|
|
435
912
|
};
|
|
436
913
|
}
|
|
437
914
|
|