@soleri/core 9.0.4 → 9.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/brain/intelligence.d.ts +27 -0
- package/dist/brain/intelligence.d.ts.map +1 -1
- package/dist/brain/intelligence.js +160 -14
- package/dist/brain/intelligence.js.map +1 -1
- package/dist/brain/learning-radar.d.ts +4 -0
- package/dist/brain/learning-radar.d.ts.map +1 -1
- package/dist/brain/learning-radar.js +20 -1
- package/dist/brain/learning-radar.js.map +1 -1
- package/dist/brain/strength-scorer.d.ts +31 -0
- package/dist/brain/strength-scorer.d.ts.map +1 -0
- package/dist/brain/strength-scorer.js +264 -0
- package/dist/brain/strength-scorer.js.map +1 -0
- package/dist/chat/agent-loop.d.ts.map +1 -1
- package/dist/chat/agent-loop.js +2 -0
- package/dist/chat/agent-loop.js.map +1 -1
- package/dist/chat/notifications.d.ts.map +1 -1
- package/dist/chat/notifications.js +2 -0
- package/dist/chat/notifications.js.map +1 -1
- package/dist/claudemd/compose.js +1 -1
- package/dist/claudemd/compose.js.map +1 -1
- package/dist/control/intent-router.d.ts.map +1 -1
- package/dist/control/intent-router.js +12 -4
- package/dist/control/intent-router.js.map +1 -1
- package/dist/curator/contradiction-detector.d.ts +27 -0
- package/dist/curator/contradiction-detector.d.ts.map +1 -0
- package/dist/curator/contradiction-detector.js +62 -0
- package/dist/curator/contradiction-detector.js.map +1 -0
- package/dist/curator/curator.d.ts +3 -4
- package/dist/curator/curator.d.ts.map +1 -1
- package/dist/curator/curator.js +90 -525
- package/dist/curator/curator.js.map +1 -1
- package/dist/curator/duplicate-detector.d.ts +14 -0
- package/dist/curator/duplicate-detector.d.ts.map +1 -0
- package/dist/curator/duplicate-detector.js +77 -0
- package/dist/curator/duplicate-detector.js.map +1 -0
- package/dist/curator/health-audit.d.ts +15 -0
- package/dist/curator/health-audit.d.ts.map +1 -0
- package/dist/curator/health-audit.js +97 -0
- package/dist/curator/health-audit.js.map +1 -0
- package/dist/curator/metadata-enricher.d.ts +17 -0
- package/dist/curator/metadata-enricher.d.ts.map +1 -0
- package/dist/curator/metadata-enricher.js +60 -0
- package/dist/curator/metadata-enricher.js.map +1 -0
- package/dist/curator/schema.d.ts +7 -0
- package/dist/curator/schema.d.ts.map +1 -0
- package/dist/curator/schema.js +62 -0
- package/dist/curator/schema.js.map +1 -0
- package/dist/curator/tag-manager.d.ts +36 -0
- package/dist/curator/tag-manager.d.ts.map +1 -0
- package/dist/curator/tag-manager.js +78 -0
- package/dist/curator/tag-manager.js.map +1 -0
- package/dist/engine/bin/soleri-engine.js +24 -3
- package/dist/engine/bin/soleri-engine.js.map +1 -1
- package/dist/engine/core-ops.d.ts.map +1 -1
- package/dist/engine/core-ops.js +23 -8
- package/dist/engine/core-ops.js.map +1 -1
- package/dist/engine/module-manifest.d.ts.map +1 -1
- package/dist/engine/module-manifest.js +22 -2
- package/dist/engine/module-manifest.js.map +1 -1
- package/dist/engine/register-engine.d.ts.map +1 -1
- package/dist/engine/register-engine.js +26 -2
- package/dist/engine/register-engine.js.map +1 -1
- package/dist/errors/retry.d.ts.map +1 -1
- package/dist/errors/retry.js +2 -0
- package/dist/errors/retry.js.map +1 -1
- package/dist/facades/types.d.ts +1 -1
- package/dist/flows/chain-types.d.ts +18 -18
- package/dist/flows/gate-evaluator.d.ts.map +1 -1
- package/dist/flows/gate-evaluator.js +22 -0
- package/dist/flows/gate-evaluator.js.map +1 -1
- package/dist/flows/types.d.ts +157 -28
- package/dist/flows/types.d.ts.map +1 -1
- package/dist/flows/types.js +4 -0
- package/dist/flows/types.js.map +1 -1
- package/dist/index.d.ts +10 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -1
- package/dist/index.js.map +1 -1
- package/dist/intake/intake-pipeline.d.ts.map +1 -1
- package/dist/intake/intake-pipeline.js +1 -0
- package/dist/intake/intake-pipeline.js.map +1 -1
- package/dist/intake/text-ingester.d.ts.map +1 -1
- package/dist/intake/text-ingester.js +2 -0
- package/dist/intake/text-ingester.js.map +1 -1
- package/dist/llm/key-pool.d.ts +1 -1
- package/dist/llm/key-pool.d.ts.map +1 -1
- package/dist/llm/key-pool.js +3 -4
- package/dist/llm/key-pool.js.map +1 -1
- package/dist/llm/utils.d.ts.map +1 -1
- package/dist/llm/utils.js +2 -0
- package/dist/llm/utils.js.map +1 -1
- package/dist/migrations/migration-runner.test-helpers.d.ts +13 -0
- package/dist/migrations/migration-runner.test-helpers.d.ts.map +1 -0
- package/dist/migrations/migration-runner.test-helpers.js +47 -0
- package/dist/migrations/migration-runner.test-helpers.js.map +1 -0
- package/dist/operator/operator-profile.d.ts +44 -0
- package/dist/operator/operator-profile.d.ts.map +1 -0
- package/dist/operator/operator-profile.js +377 -0
- package/dist/operator/operator-profile.js.map +1 -0
- package/dist/operator/operator-signals.d.ts +45 -0
- package/dist/operator/operator-signals.d.ts.map +1 -0
- package/dist/operator/operator-signals.js +228 -0
- package/dist/operator/operator-signals.js.map +1 -0
- package/dist/operator/operator-types.d.ts +360 -0
- package/dist/operator/operator-types.d.ts.map +1 -0
- package/dist/operator/operator-types.js +24 -0
- package/dist/operator/operator-types.js.map +1 -0
- package/dist/packs/types.d.ts +27 -27
- package/dist/paths.d.ts +40 -0
- package/dist/paths.d.ts.map +1 -0
- package/dist/paths.js +98 -0
- package/dist/paths.js.map +1 -0
- package/dist/persistence/index.d.ts +1 -1
- package/dist/persistence/index.d.ts.map +1 -1
- package/dist/persistence/index.js +1 -1
- package/dist/persistence/index.js.map +1 -1
- package/dist/persistence/sqlite-provider.d.ts +2 -0
- package/dist/persistence/sqlite-provider.d.ts.map +1 -1
- package/dist/persistence/sqlite-provider.js +8 -5
- package/dist/persistence/sqlite-provider.js.map +1 -1
- package/dist/planning/evidence-collector.d.ts +13 -1
- package/dist/planning/evidence-collector.d.ts.map +1 -1
- package/dist/planning/evidence-collector.js +33 -0
- package/dist/planning/evidence-collector.js.map +1 -1
- package/dist/planning/gap-analysis.d.ts +5 -4
- package/dist/planning/gap-analysis.d.ts.map +1 -1
- package/dist/planning/gap-analysis.js +7 -341
- package/dist/planning/gap-analysis.js.map +1 -1
- package/dist/planning/gap-passes.d.ts +19 -0
- package/dist/planning/gap-passes.d.ts.map +1 -0
- package/dist/planning/gap-passes.js +157 -0
- package/dist/planning/gap-passes.js.map +1 -0
- package/dist/planning/gap-patterns.d.ts +29 -0
- package/dist/planning/gap-patterns.d.ts.map +1 -0
- package/dist/planning/gap-patterns.js +129 -0
- package/dist/planning/gap-patterns.js.map +1 -0
- package/dist/planning/gap-types.d.ts +1 -1
- package/dist/planning/gap-types.d.ts.map +1 -1
- package/dist/planning/gap-types.js +1 -0
- package/dist/planning/gap-types.js.map +1 -1
- package/dist/planning/github-projection.d.ts +122 -0
- package/dist/planning/github-projection.d.ts.map +1 -0
- package/dist/planning/github-projection.js +294 -0
- package/dist/planning/github-projection.js.map +1 -0
- package/dist/planning/impact-analyzer.d.ts +26 -0
- package/dist/planning/impact-analyzer.d.ts.map +1 -0
- package/dist/planning/impact-analyzer.js +199 -0
- package/dist/planning/impact-analyzer.js.map +1 -0
- package/dist/planning/plan-lifecycle.d.ts +136 -0
- package/dist/planning/plan-lifecycle.d.ts.map +1 -0
- package/dist/planning/plan-lifecycle.js +296 -0
- package/dist/planning/plan-lifecycle.js.map +1 -0
- package/dist/planning/planner-types.d.ts +202 -0
- package/dist/planning/planner-types.d.ts.map +1 -0
- package/dist/planning/planner-types.js +6 -0
- package/dist/planning/planner-types.js.map +1 -0
- package/dist/planning/planner.d.ts +31 -383
- package/dist/planning/planner.d.ts.map +1 -1
- package/dist/planning/planner.js +154 -878
- package/dist/planning/planner.js.map +1 -1
- package/dist/planning/rationalization-detector.d.ts +32 -0
- package/dist/planning/rationalization-detector.d.ts.map +1 -0
- package/dist/planning/rationalization-detector.js +89 -0
- package/dist/planning/rationalization-detector.js.map +1 -0
- package/dist/planning/reconciliation-engine.d.ts +47 -0
- package/dist/planning/reconciliation-engine.d.ts.map +1 -0
- package/dist/planning/reconciliation-engine.js +128 -0
- package/dist/planning/reconciliation-engine.js.map +1 -0
- package/dist/planning/task-verifier.d.ts +85 -0
- package/dist/planning/task-verifier.d.ts.map +1 -0
- package/dist/planning/task-verifier.js +227 -0
- package/dist/planning/task-verifier.js.map +1 -0
- package/dist/plugins/types.d.ts +4 -4
- package/dist/runtime/admin-ops.d.ts +2 -2
- package/dist/runtime/admin-ops.d.ts.map +1 -1
- package/dist/runtime/admin-ops.js +44 -17
- package/dist/runtime/admin-ops.js.map +1 -1
- package/dist/runtime/admin-setup-ops.d.ts.map +1 -1
- package/dist/runtime/admin-setup-ops.js +21 -46
- package/dist/runtime/admin-setup-ops.js.map +1 -1
- package/dist/runtime/archive-ops.d.ts +10 -0
- package/dist/runtime/archive-ops.d.ts.map +1 -0
- package/dist/runtime/archive-ops.js +310 -0
- package/dist/runtime/archive-ops.js.map +1 -0
- package/dist/runtime/capture-ops.d.ts.map +1 -1
- package/dist/runtime/capture-ops.js +42 -7
- package/dist/runtime/capture-ops.js.map +1 -1
- package/dist/runtime/claude-md-helpers.js +1 -1
- package/dist/runtime/claude-md-helpers.js.map +1 -1
- package/dist/runtime/context-health.d.ts +31 -0
- package/dist/runtime/context-health.d.ts.map +1 -0
- package/dist/runtime/context-health.js +57 -0
- package/dist/runtime/context-health.js.map +1 -0
- package/dist/runtime/facades/archive-facade.d.ts +10 -0
- package/dist/runtime/facades/archive-facade.d.ts.map +1 -0
- package/dist/runtime/facades/archive-facade.js +11 -0
- package/dist/runtime/facades/archive-facade.js.map +1 -0
- package/dist/runtime/facades/brain-facade.d.ts.map +1 -1
- package/dist/runtime/facades/brain-facade.js +2 -0
- package/dist/runtime/facades/brain-facade.js.map +1 -1
- package/dist/runtime/facades/chat-facade.d.ts +7 -0
- package/dist/runtime/facades/chat-facade.d.ts.map +1 -1
- package/dist/runtime/facades/chat-facade.js +15 -800
- package/dist/runtime/facades/chat-facade.js.map +1 -1
- package/dist/runtime/facades/chat-service-ops.d.ts +9 -0
- package/dist/runtime/facades/chat-service-ops.d.ts.map +1 -0
- package/dist/runtime/facades/chat-service-ops.js +330 -0
- package/dist/runtime/facades/chat-service-ops.js.map +1 -0
- package/dist/runtime/facades/chat-session-ops.d.ts +8 -0
- package/dist/runtime/facades/chat-session-ops.d.ts.map +1 -0
- package/dist/runtime/facades/chat-session-ops.js +136 -0
- package/dist/runtime/facades/chat-session-ops.js.map +1 -0
- package/dist/runtime/facades/chat-state.d.ts +31 -0
- package/dist/runtime/facades/chat-state.d.ts.map +1 -0
- package/dist/runtime/facades/chat-state.js +32 -0
- package/dist/runtime/facades/chat-state.js.map +1 -0
- package/dist/runtime/facades/chat-transport-ops.d.ts +9 -0
- package/dist/runtime/facades/chat-transport-ops.d.ts.map +1 -0
- package/dist/runtime/facades/chat-transport-ops.js +337 -0
- package/dist/runtime/facades/chat-transport-ops.js.map +1 -0
- package/dist/runtime/facades/control-facade.d.ts.map +1 -1
- package/dist/runtime/facades/control-facade.js +4 -1
- package/dist/runtime/facades/control-facade.js.map +1 -1
- package/dist/runtime/facades/index.d.ts.map +1 -1
- package/dist/runtime/facades/index.js +6 -0
- package/dist/runtime/facades/index.js.map +1 -1
- package/dist/runtime/facades/memory-facade.d.ts.map +1 -1
- package/dist/runtime/facades/memory-facade.js +75 -6
- package/dist/runtime/facades/memory-facade.js.map +1 -1
- package/dist/runtime/facades/operator-facade.d.ts +8 -0
- package/dist/runtime/facades/operator-facade.d.ts.map +1 -0
- package/dist/runtime/facades/operator-facade.js +220 -0
- package/dist/runtime/facades/operator-facade.js.map +1 -0
- package/dist/runtime/facades/orchestrate-facade.js +3 -3
- package/dist/runtime/facades/orchestrate-facade.js.map +1 -1
- package/dist/runtime/facades/plan-facade.d.ts.map +1 -1
- package/dist/runtime/facades/plan-facade.js +39 -6
- package/dist/runtime/facades/plan-facade.js.map +1 -1
- package/dist/runtime/facades/review-facade.d.ts +7 -0
- package/dist/runtime/facades/review-facade.d.ts.map +1 -0
- package/dist/runtime/facades/review-facade.js +8 -0
- package/dist/runtime/facades/review-facade.js.map +1 -0
- package/dist/runtime/facades/sync-facade.d.ts +7 -0
- package/dist/runtime/facades/sync-facade.d.ts.map +1 -0
- package/dist/runtime/facades/sync-facade.js +8 -0
- package/dist/runtime/facades/sync-facade.js.map +1 -0
- package/dist/runtime/facades/vault-facade.d.ts +4 -1
- package/dist/runtime/facades/vault-facade.d.ts.map +1 -1
- package/dist/runtime/facades/vault-facade.js +13 -66
- package/dist/runtime/facades/vault-facade.js.map +1 -1
- package/dist/runtime/github-integration.d.ts +49 -0
- package/dist/runtime/github-integration.d.ts.map +1 -0
- package/dist/runtime/github-integration.js +113 -0
- package/dist/runtime/github-integration.js.map +1 -0
- package/dist/runtime/grading-ops.js +1 -1
- package/dist/runtime/grading-ops.js.map +1 -1
- package/dist/runtime/memory-extra-ops.d.ts.map +1 -1
- package/dist/runtime/memory-extra-ops.js +6 -2
- package/dist/runtime/memory-extra-ops.js.map +1 -1
- package/dist/runtime/orchestrate-ops.d.ts.map +1 -1
- package/dist/runtime/orchestrate-ops.js +367 -40
- package/dist/runtime/orchestrate-ops.js.map +1 -1
- package/dist/runtime/planning-extra-ops.d.ts.map +1 -1
- package/dist/runtime/planning-extra-ops.js +69 -4
- package/dist/runtime/planning-extra-ops.js.map +1 -1
- package/dist/runtime/review-ops.d.ts +10 -0
- package/dist/runtime/review-ops.d.ts.map +1 -0
- package/dist/runtime/review-ops.js +97 -0
- package/dist/runtime/review-ops.js.map +1 -0
- package/dist/runtime/runtime.d.ts.map +1 -1
- package/dist/runtime/runtime.js +27 -12
- package/dist/runtime/runtime.js.map +1 -1
- package/dist/runtime/session-briefing.d.ts +3 -0
- package/dist/runtime/session-briefing.d.ts.map +1 -1
- package/dist/runtime/session-briefing.js +68 -1
- package/dist/runtime/session-briefing.js.map +1 -1
- package/dist/runtime/sync-ops.d.ts +12 -0
- package/dist/runtime/sync-ops.d.ts.map +1 -0
- package/dist/runtime/sync-ops.js +288 -0
- package/dist/runtime/sync-ops.js.map +1 -0
- package/dist/runtime/types.d.ts +10 -4
- package/dist/runtime/types.d.ts.map +1 -1
- package/dist/runtime/vault-extra-ops.d.ts +5 -4
- package/dist/runtime/vault-extra-ops.d.ts.map +1 -1
- package/dist/runtime/vault-extra-ops.js +5 -300
- package/dist/runtime/vault-extra-ops.js.map +1 -1
- package/dist/runtime/vault-sharing-ops.d.ts +4 -4
- package/dist/runtime/vault-sharing-ops.d.ts.map +1 -1
- package/dist/runtime/vault-sharing-ops.js +5 -300
- package/dist/runtime/vault-sharing-ops.js.map +1 -1
- package/dist/skills/sync-skills.d.ts +27 -0
- package/dist/skills/sync-skills.d.ts.map +1 -0
- package/dist/skills/sync-skills.js +81 -0
- package/dist/skills/sync-skills.js.map +1 -0
- package/dist/update-check.d.ts +14 -0
- package/dist/update-check.d.ts.map +1 -0
- package/dist/update-check.js +96 -0
- package/dist/update-check.js.map +1 -0
- package/dist/vault/linking.d.ts +10 -12
- package/dist/vault/linking.d.ts.map +1 -1
- package/dist/vault/linking.js +104 -161
- package/dist/vault/linking.js.map +1 -1
- package/dist/vault/vault-entries.d.ts +69 -0
- package/dist/vault/vault-entries.d.ts.map +1 -0
- package/dist/vault/vault-entries.js +257 -0
- package/dist/vault/vault-entries.js.map +1 -0
- package/dist/vault/vault-interfaces.d.ts +153 -0
- package/dist/vault/vault-interfaces.d.ts.map +1 -0
- package/dist/vault/vault-interfaces.js +2 -0
- package/dist/vault/vault-interfaces.js.map +1 -0
- package/dist/vault/vault-maintenance.d.ts +40 -0
- package/dist/vault/vault-maintenance.d.ts.map +1 -0
- package/dist/vault/vault-maintenance.js +142 -0
- package/dist/vault/vault-maintenance.js.map +1 -0
- package/dist/vault/vault-markdown-sync.d.ts +22 -0
- package/dist/vault/vault-markdown-sync.d.ts.map +1 -0
- package/dist/vault/vault-markdown-sync.js +143 -0
- package/dist/vault/vault-markdown-sync.js.map +1 -0
- package/dist/vault/vault-memories.d.ts +61 -0
- package/dist/vault/vault-memories.d.ts.map +1 -0
- package/dist/vault/vault-memories.js +240 -0
- package/dist/vault/vault-memories.js.map +1 -0
- package/dist/vault/vault-schema.d.ts +9 -0
- package/dist/vault/vault-schema.d.ts.map +1 -0
- package/dist/vault/vault-schema.js +179 -0
- package/dist/vault/vault-schema.js.map +1 -0
- package/dist/vault/vault.d.ts +29 -81
- package/dist/vault/vault.d.ts.map +1 -1
- package/dist/vault/vault.js +78 -931
- package/dist/vault/vault.js.map +1 -1
- package/package.json +1 -1
- package/src/agency/agency-manager.test.ts +600 -0
- package/src/agency/default-rules.test.ts +228 -0
- package/src/{__tests__ → brain}/brain-intelligence.test.ts +37 -14
- package/src/{__tests__ → brain}/brain.test.ts +1 -1
- package/src/brain/intelligence.ts +196 -15
- package/src/brain/learning-radar.ts +22 -1
- package/src/{__tests__ → brain}/second-brain-features.test.ts +4 -4
- package/src/{__tests__ → brain}/session-lifecycle.test.ts +2 -2
- package/src/brain/strength-scorer.ts +404 -0
- package/src/capabilities/chain-mapping.test.ts +66 -0
- package/src/capabilities/registry.test.ts +369 -0
- package/src/chat/agent-loop.test.ts +394 -0
- package/src/chat/agent-loop.ts +2 -0
- package/src/{__tests__ → chat}/chat-differentiators.test.ts +3 -3
- package/src/{__tests__ → chat}/chat-enhanced.test.ts +4 -4
- package/src/{__tests__ → chat}/chat-transport.test.ts +6 -6
- package/src/chat/mcp-bridge.test.ts +173 -0
- package/src/chat/notifications.ts +2 -0
- package/src/chat/output-compressor.test.ts +164 -0
- package/src/claudemd/compose.test.ts +178 -0
- package/src/claudemd/compose.ts +1 -1
- package/src/claudemd/inject.test.ts +211 -0
- package/src/context/context-engine.test.ts +461 -0
- package/src/control/identity-manager.test.ts +305 -0
- package/src/control/intent-router.test.ts +360 -0
- package/src/control/intent-router.ts +13 -4
- package/src/curator/classifier.test.ts +104 -0
- package/src/curator/contradiction-detector.test.ts +180 -0
- package/src/curator/contradiction-detector.ts +87 -0
- package/src/{__tests__ → curator}/curator-pipeline-e2e.test.ts +10 -10
- package/src/{__tests__ → curator}/curator.test.ts +77 -1
- package/src/curator/curator.ts +115 -777
- package/src/curator/duplicate-detector.test.ts +183 -0
- package/src/curator/duplicate-detector.ts +103 -0
- package/src/curator/health-audit.ts +126 -0
- package/src/curator/metadata-enricher.ts +84 -0
- package/src/curator/quality-gate.test.ts +135 -0
- package/src/curator/schema.ts +65 -0
- package/src/curator/tag-manager.test.ts +165 -0
- package/src/curator/tag-manager.ts +109 -0
- package/src/domain-packs/inject-rules.test.ts +117 -0
- package/src/domain-packs/knowledge-installer.test.ts +171 -0
- package/src/domain-packs/loader.test.ts +86 -0
- package/src/domain-packs/pack-runtime.test.ts +140 -0
- package/src/domain-packs/skills-installer.test.ts +135 -0
- package/src/domain-packs/token-resolver.test.ts +150 -0
- package/src/domain-packs/types.test.ts +130 -0
- package/src/enforcement/adapters/claude-code.test.ts +216 -0
- package/src/enforcement/registry.test.ts +264 -0
- package/src/engine/bin/soleri-engine.ts +28 -4
- package/src/engine/core-ops.test.ts +254 -0
- package/src/engine/core-ops.ts +25 -8
- package/src/engine/module-manifest.test.ts +124 -0
- package/src/engine/module-manifest.ts +22 -2
- package/src/engine/register-engine.test.ts +230 -0
- package/src/engine/register-engine.ts +26 -2
- package/src/errors/classify.test.ts +199 -0
- package/src/errors/retry.test.ts +156 -0
- package/src/errors/retry.ts +2 -0
- package/src/errors/types.test.ts +108 -0
- package/src/events/event-bus.test.ts +149 -0
- package/src/extensions/middleware.test.ts +234 -0
- package/src/facades/facade-factory.test.ts +424 -0
- package/src/flows/chain-runner.test.ts +273 -0
- package/src/flows/context-router.test.ts +52 -0
- package/src/flows/dispatch-registry.test.ts +128 -0
- package/src/flows/epilogue.test.ts +107 -0
- package/src/flows/executor.test.ts +263 -0
- package/src/flows/gate-evaluator.test.ts +194 -0
- package/src/flows/gate-evaluator.ts +25 -0
- package/src/flows/types.ts +4 -0
- package/src/governance/governance.test.ts +726 -0
- package/src/health/health-registry.test.ts +186 -0
- package/src/health/vault-integrity.test.ts +110 -0
- package/src/index.ts +92 -0
- package/src/intake/content-classifier.test.ts +209 -0
- package/src/intake/dedup-gate.test.ts +131 -0
- package/src/intake/intake-pipeline.test.ts +506 -0
- package/src/intake/intake-pipeline.ts +1 -0
- package/src/intake/text-ingester.test.ts +194 -0
- package/src/intake/text-ingester.ts +2 -0
- package/src/llm/key-pool.test.ts +236 -0
- package/src/llm/key-pool.ts +3 -4
- package/src/llm/llm-client.test.ts +345 -0
- package/src/llm/oauth-discovery.test.ts +180 -0
- package/src/llm/utils.test.ts +327 -0
- package/src/llm/utils.ts +2 -0
- package/src/{__tests__ → logging}/logger.test.ts +41 -62
- package/src/loop/loop-manager.test.ts +519 -0
- package/src/migrations/migration-runner.edge-cases.test.ts +319 -0
- package/src/migrations/migration-runner.test-helpers.ts +64 -0
- package/src/migrations/migration-runner.test.ts +385 -0
- package/src/operator/auto-signal-pipeline.test.ts +207 -0
- package/src/operator/operator-profile-extended.test.ts +320 -0
- package/src/operator/operator-profile.test.ts +314 -0
- package/src/operator/operator-profile.ts +469 -0
- package/src/operator/operator-signals-extended.test.ts +245 -0
- package/src/operator/operator-signals.test.ts +281 -0
- package/src/operator/operator-signals.ts +261 -0
- package/src/operator/operator-types.ts +444 -0
- package/src/operator/prompts/hook-precompact-operator-dispatch.md +94 -0
- package/src/operator/prompts/subagent-soft-signal-extractor.md +125 -0
- package/src/operator/prompts/subagent-synthesis-cognition.md +181 -0
- package/src/operator/prompts/subagent-synthesis-communication.md +140 -0
- package/src/operator/prompts/subagent-synthesis-technical.md +160 -0
- package/src/operator/prompts/subagent-synthesis-trust.md +143 -0
- package/src/{__tests__ → packs}/pack-lockfile.test.ts +3 -3
- package/src/{__tests__ → packs}/pack-system.test.ts +2 -2
- package/src/paths.ts +115 -0
- package/src/persistence/index.ts +1 -1
- package/src/persistence/sqlite-provider.test.ts +540 -0
- package/src/persistence/sqlite-provider.ts +8 -5
- package/src/persona/defaults.test.ts +59 -0
- package/src/persona/loader.test.ts +67 -0
- package/src/persona/prompt-generator.test.ts +127 -0
- package/src/planning/evidence-collector.test.ts +406 -0
- package/src/planning/evidence-collector.ts +50 -0
- package/src/planning/gap-analysis-alternatives.test.ts +169 -0
- package/src/planning/gap-analysis.ts +21 -636
- package/src/planning/gap-passes.test.ts +372 -0
- package/src/planning/gap-passes.ts +298 -0
- package/src/planning/gap-patterns.test.ts +320 -0
- package/src/planning/gap-patterns.ts +234 -0
- package/src/planning/gap-types.ts +4 -1
- package/src/planning/github-projection.test.ts +177 -0
- package/src/planning/github-projection.ts +425 -0
- package/src/planning/impact-analyzer.test.ts +180 -0
- package/src/planning/impact-analyzer.ts +264 -0
- package/src/planning/plan-lifecycle.test.ts +312 -0
- package/src/planning/plan-lifecycle.ts +346 -0
- package/src/planning/planner-types.ts +215 -0
- package/src/{__tests__ → planning}/planner.test.ts +169 -15
- package/src/planning/planner.ts +197 -1228
- package/src/planning/rationalization-detector.test.ts +171 -0
- package/src/planning/rationalization-detector.ts +138 -0
- package/src/planning/reconciliation-engine.test.ts +141 -0
- package/src/planning/reconciliation-engine.ts +162 -0
- package/src/planning/task-verifier.test.ts +235 -0
- package/src/planning/task-verifier.ts +303 -0
- package/src/planning/verification-protocol.test.ts +201 -0
- package/src/playbooks/generic/generic-playbooks.test.ts +438 -0
- package/src/playbooks/index.test.ts +77 -0
- package/src/playbooks/playbook-executor.test.ts +255 -0
- package/src/playbooks/playbook-registry.test.ts +232 -0
- package/src/playbooks/playbook-seeder.test.ts +153 -0
- package/src/plugins/plugin-loader.test.ts +212 -0
- package/src/plugins/plugin-registry.test.ts +272 -0
- package/src/project/project-registry.test.ts +428 -0
- package/src/prompts/parser.test.ts +100 -0
- package/src/prompts/template-manager.test.ts +109 -0
- package/src/{__tests__ → queue}/async-infrastructure.test.ts +3 -3
- package/src/queue/job-queue.test.ts +331 -0
- package/src/queue/pipeline-runner.test.ts +209 -0
- package/src/runtime/admin-extra-ops.test.ts +527 -0
- package/src/runtime/admin-ops.test.ts +257 -0
- package/src/runtime/admin-ops.ts +45 -17
- package/src/runtime/admin-setup-ops.test.ts +328 -0
- package/src/runtime/admin-setup-ops.ts +20 -43
- package/src/runtime/archive-ops.test.ts +269 -0
- package/src/runtime/archive-ops.ts +347 -0
- package/src/runtime/capture-ops.test.ts +433 -0
- package/src/runtime/capture-ops.ts +50 -8
- package/src/runtime/chain-ops.test.ts +149 -0
- package/src/runtime/claude-md-helpers.test.ts +191 -0
- package/src/runtime/claude-md-helpers.ts +1 -1
- package/src/runtime/context-health.test.ts +78 -0
- package/src/runtime/context-health.ts +85 -0
- package/src/runtime/curator-extra-ops.test.ts +202 -0
- package/src/runtime/deprecation.test.ts +98 -0
- package/src/runtime/domain-ops.test.ts +268 -0
- package/src/runtime/facades/admin-facade.test.ts +333 -0
- package/src/runtime/facades/agency-facade.test.ts +278 -0
- package/src/runtime/facades/archive-facade.test.ts +294 -0
- package/src/runtime/facades/archive-facade.ts +14 -0
- package/src/runtime/facades/brain-facade.test.ts +714 -0
- package/src/runtime/facades/brain-facade.ts +2 -0
- package/src/runtime/facades/chat-facade.test.ts +166 -0
- package/src/runtime/facades/chat-facade.ts +15 -906
- package/src/runtime/facades/chat-service-ops.test.ts +276 -0
- package/src/runtime/facades/chat-service-ops.ts +374 -0
- package/src/runtime/facades/chat-session-ops.test.ts +197 -0
- package/src/runtime/facades/chat-session-ops.ts +146 -0
- package/src/runtime/facades/chat-state.ts +60 -0
- package/src/runtime/facades/chat-transport-ops.test.ts +269 -0
- package/src/runtime/facades/chat-transport-ops.ts +380 -0
- package/src/runtime/facades/context-facade.test.ts +108 -0
- package/src/runtime/facades/control-facade.test.ts +436 -0
- package/src/runtime/facades/control-facade.ts +6 -1
- package/src/runtime/facades/curator-facade.test.ts +303 -0
- package/src/runtime/facades/index.ts +6 -0
- package/src/runtime/facades/loop-facade.test.ts +245 -0
- package/src/runtime/facades/memory-facade.test.ts +269 -0
- package/src/runtime/facades/memory-facade.ts +78 -6
- package/src/runtime/facades/operator-facade.test.ts +208 -0
- package/src/runtime/facades/operator-facade.ts +236 -0
- package/src/runtime/facades/orchestrate-facade.test.ts +185 -0
- package/src/runtime/facades/orchestrate-facade.ts +3 -3
- package/src/runtime/facades/plan-facade.test.ts +266 -0
- package/src/runtime/facades/plan-facade.ts +42 -6
- package/src/runtime/facades/review-facade.test.ts +82 -0
- package/src/runtime/facades/review-facade.ts +11 -0
- package/src/runtime/facades/sync-facade.test.ts +113 -0
- package/src/runtime/facades/sync-facade.ts +11 -0
- package/src/runtime/facades/vault-facade.test.ts +631 -0
- package/src/runtime/facades/vault-facade.ts +15 -70
- package/src/runtime/feature-flags.test.ts +140 -0
- package/src/runtime/github-integration.test.ts +89 -0
- package/src/runtime/github-integration.ts +159 -0
- package/src/runtime/grading-ops.test.ts +141 -0
- package/src/runtime/grading-ops.ts +1 -1
- package/src/runtime/intake-ops.test.ts +208 -0
- package/src/runtime/loop-ops.test.ts +238 -0
- package/src/runtime/memory-cross-project-ops.test.ts +177 -0
- package/src/runtime/memory-extra-ops.test.ts +453 -0
- package/src/runtime/memory-extra-ops.ts +6 -2
- package/src/runtime/orchestrate-ops.test.ts +302 -0
- package/src/runtime/orchestrate-ops.ts +435 -46
- package/src/runtime/pack-ops.test.ts +158 -0
- package/src/runtime/planning-extra-ops.test.ts +583 -0
- package/src/runtime/planning-extra-ops.ts +72 -4
- package/src/{__tests__ → runtime}/playbook-ops-execution.test.ts +3 -3
- package/src/runtime/playbook-ops.test.ts +262 -0
- package/src/runtime/plugin-ops.test.ts +201 -0
- package/src/runtime/project-ops.test.ts +235 -0
- package/src/runtime/review-ops.test.ts +142 -0
- package/src/runtime/review-ops.ts +99 -0
- package/src/runtime/runtime.test.ts +363 -0
- package/src/runtime/runtime.ts +39 -12
- package/src/runtime/session-briefing.test.ts +302 -0
- package/src/runtime/session-briefing.ts +80 -1
- package/src/runtime/sync-ops.test.ts +221 -0
- package/src/runtime/sync-ops.ts +325 -0
- package/src/runtime/telemetry-ops.test.ts +132 -0
- package/src/runtime/types.ts +10 -4
- package/src/runtime/vault-extra-ops.test.ts +246 -0
- package/src/runtime/vault-extra-ops.ts +5 -332
- package/src/runtime/vault-linking-ops.test.ts +237 -0
- package/src/runtime/vault-sharing-ops.test.ts +130 -0
- package/src/runtime/vault-sharing-ops.ts +5 -329
- package/src/skills/sync-skills.ts +108 -0
- package/src/streams/normalize.test.ts +95 -0
- package/src/streams/replayable-stream.test.ts +166 -0
- package/src/telemetry/telemetry.test.ts +143 -0
- package/src/transport/http-server.test.ts +394 -0
- package/src/transport/lsp-server.test.ts +458 -0
- package/src/transport/rate-limiter.test.ts +126 -0
- package/src/transport/session-manager.test.ts +133 -0
- package/src/transport/token-auth.test.ts +136 -0
- package/src/transport/ws-server.test.ts +294 -0
- package/src/update-check.ts +111 -0
- package/src/vault/__tests__/vault-characterization.test.ts +168 -0
- package/src/vault/content-hash.test.ts +78 -0
- package/src/vault/git-vault-sync.test.ts +234 -0
- package/src/vault/knowledge-review.test.ts +269 -0
- package/src/vault/linking.test.ts +358 -0
- package/src/vault/linking.ts +149 -183
- package/src/vault/obsidian-sync.test.ts +342 -0
- package/src/vault/playbook.test.ts +152 -0
- package/src/vault/scope-detector.test.ts +187 -0
- package/src/vault/vault-branching.test.ts +250 -0
- package/src/{__tests__ → vault}/vault-connect.test.ts +1 -1
- package/src/vault/vault-entries.ts +282 -0
- package/src/vault/vault-interfaces.ts +56 -0
- package/src/vault/vault-maintenance.ts +205 -0
- package/src/vault/vault-manager.test.ts +206 -0
- package/src/vault/vault-markdown-sync.test.ts +203 -0
- package/src/vault/vault-markdown-sync.ts +160 -0
- package/src/vault/vault-memories.ts +339 -0
- package/src/{__tests__ → vault}/vault-scaling.test.ts +1 -1
- package/src/vault/vault-schema.ts +181 -0
- package/src/{__tests__ → vault}/vault-sharing.test.ts +4 -4
- package/src/{__tests__ → vault}/vault.test.ts +2 -2
- package/src/vault/vault.ts +89 -1171
- package/dist/cognee/client.d.ts +0 -43
- package/dist/cognee/client.d.ts.map +0 -1
- package/dist/cognee/client.js +0 -375
- package/dist/cognee/client.js.map +0 -1
- package/dist/cognee/sync-manager.d.ts +0 -153
- package/dist/cognee/sync-manager.d.ts.map +0 -1
- package/dist/cognee/sync-manager.js +0 -390
- package/dist/cognee/sync-manager.js.map +0 -1
- package/dist/cognee/types.d.ts +0 -62
- package/dist/cognee/types.d.ts.map +0 -1
- package/dist/cognee/types.js +0 -3
- package/dist/cognee/types.js.map +0 -1
- package/dist/governance/index.d.ts +0 -3
- package/dist/governance/index.d.ts.map +0 -1
- package/dist/governance/index.js +0 -2
- package/dist/governance/index.js.map +0 -1
- package/dist/health/doctor-checks.d.ts +0 -15
- package/dist/health/doctor-checks.d.ts.map +0 -1
- package/dist/health/doctor-checks.js +0 -98
- package/dist/health/doctor-checks.js.map +0 -1
- package/dist/persistence/postgres-provider.d.ts +0 -81
- package/dist/persistence/postgres-provider.d.ts.map +0 -1
- package/dist/persistence/postgres-provider.js +0 -256
- package/dist/persistence/postgres-provider.js.map +0 -1
- package/dist/runtime/cognee-sync-ops.d.ts +0 -12
- package/dist/runtime/cognee-sync-ops.d.ts.map +0 -1
- package/dist/runtime/cognee-sync-ops.js +0 -93
- package/dist/runtime/cognee-sync-ops.js.map +0 -1
- package/dist/runtime/core-ops.d.ts +0 -23
- package/dist/runtime/core-ops.d.ts.map +0 -1
- package/dist/runtime/core-ops.js +0 -1296
- package/dist/runtime/core-ops.js.map +0 -1
- package/dist/runtime/facades/cognee-facade.d.ts +0 -8
- package/dist/runtime/facades/cognee-facade.d.ts.map +0 -1
- package/dist/runtime/facades/cognee-facade.js +0 -156
- package/dist/runtime/facades/cognee-facade.js.map +0 -1
- package/src/__tests__/admin-extra-ops.test.ts +0 -484
- package/src/__tests__/admin-ops.test.ts +0 -268
- package/src/__tests__/admin-setup-ops.test.ts +0 -355
- package/src/__tests__/agency-manager.test.ts +0 -374
- package/src/__tests__/agent-loop.test.ts +0 -256
- package/src/__tests__/capture-ops.test.ts +0 -784
- package/src/__tests__/claudemd.test.ts +0 -282
- package/src/__tests__/content-hash.test.ts +0 -60
- package/src/__tests__/context-engine.test.ts +0 -251
- package/src/__tests__/core-ops.test.ts +0 -550
- package/src/__tests__/curator-extra-ops.test.ts +0 -383
- package/src/__tests__/deprecation.test.ts +0 -78
- package/src/__tests__/domain-ops.test.ts +0 -226
- package/src/__tests__/domain-packs.test.ts +0 -421
- package/src/__tests__/enforcement.test.ts +0 -153
- package/src/__tests__/errors.test.ts +0 -388
- package/src/__tests__/extensions.test.ts +0 -233
- package/src/__tests__/facade-factory.test.ts +0 -271
- package/src/__tests__/feature-flags.test.ts +0 -137
- package/src/__tests__/flows.test.ts +0 -604
- package/src/__tests__/git-vault-sync.test.ts +0 -230
- package/src/__tests__/governance.test.ts +0 -522
- package/src/__tests__/grading-ops.test.ts +0 -361
- package/src/__tests__/health-registry.test.ts +0 -173
- package/src/__tests__/identity-manager.test.ts +0 -243
- package/src/__tests__/intake-pipeline.test.ts +0 -162
- package/src/__tests__/intent-router.test.ts +0 -222
- package/src/__tests__/knowledge-review.test.ts +0 -104
- package/src/__tests__/llm-client.test.ts +0 -69
- package/src/__tests__/llm.test.ts +0 -556
- package/src/__tests__/loader.test.ts +0 -176
- package/src/__tests__/loop-ops.test.ts +0 -469
- package/src/__tests__/lsp-transport.test.ts +0 -442
- package/src/__tests__/memory-cross-project-ops.test.ts +0 -248
- package/src/__tests__/memory-extra-ops.test.ts +0 -352
- package/src/__tests__/migration-runner.test.ts +0 -170
- package/src/__tests__/module-manifest-drift.test.ts +0 -59
- package/src/__tests__/normalize.test.ts +0 -85
- package/src/__tests__/obsidian-sync.test.ts +0 -354
- package/src/__tests__/orchestrate-ops.test.ts +0 -289
- package/src/__tests__/pack-ops.test.ts +0 -146
- package/src/__tests__/persistence.test.ts +0 -291
- package/src/__tests__/planning-extra-ops.test.ts +0 -706
- package/src/__tests__/playbook-executor.test.ts +0 -249
- package/src/__tests__/playbook-registry.test.ts +0 -326
- package/src/__tests__/playbook-seeder.test.ts +0 -163
- package/src/__tests__/playbook.test.ts +0 -389
- package/src/__tests__/plugin-ops.test.ts +0 -411
- package/src/__tests__/plugin-system.test.ts +0 -509
- package/src/__tests__/project-ops.test.ts +0 -381
- package/src/__tests__/replayable-stream.test.ts +0 -177
- package/src/__tests__/runtime.test.ts +0 -95
- package/src/__tests__/scope-detector.test.ts +0 -121
- package/src/__tests__/template-manager.test.ts +0 -222
- package/src/__tests__/token-resolver.test.ts +0 -79
- package/src/__tests__/transport.test.ts +0 -758
- package/src/__tests__/vault-branching.test.ts +0 -274
- package/src/__tests__/vault-extra-ops.test.ts +0 -482
- package/src/__tests__/vault-integrity.test.ts +0 -71
- package/src/__tests__/vault-manager.test.ts +0 -238
- package/src/__tests__/ws-transport.test.ts +0 -479
package/src/curator/curator.ts
CHANGED
|
@@ -1,17 +1,10 @@
|
|
|
1
1
|
import type { Vault } from '../vault/vault.js';
|
|
2
2
|
import type { IntelligenceEntry } from '../intelligence/types.js';
|
|
3
3
|
import type { PersistenceProvider } from '../persistence/types.js';
|
|
4
|
-
import {
|
|
5
|
-
tokenize,
|
|
6
|
-
calculateTfIdf,
|
|
7
|
-
cosineSimilarity,
|
|
8
|
-
type SparseVector,
|
|
9
|
-
} from '../text/similarity.js';
|
|
10
4
|
import type {
|
|
11
5
|
CuratorStatus,
|
|
12
6
|
TagNormalizationResult,
|
|
13
7
|
CanonicalTag,
|
|
14
|
-
DuplicateCandidate,
|
|
15
8
|
DuplicateDetectionResult,
|
|
16
9
|
Contradiction,
|
|
17
10
|
ContradictionStatus,
|
|
@@ -23,927 +16,272 @@ import type {
|
|
|
23
16
|
HealthAuditResult,
|
|
24
17
|
} from './types.js';
|
|
25
18
|
|
|
19
|
+
import { detectDuplicates as detectDuplicatesPure, DEFAULT_DUPLICATE_THRESHOLD } from './duplicate-detector.js';
|
|
20
|
+
import { findContradictions, DEFAULT_CONTRADICTION_THRESHOLD, type ContradictionCandidate } from './contradiction-detector.js';
|
|
21
|
+
import {
|
|
22
|
+
normalizeTag as normalizeTagPure,
|
|
23
|
+
normalizeAndDedup,
|
|
24
|
+
addTagAlias as addTagAliasPure,
|
|
25
|
+
getCanonicalTags as getCanonicalTagsPure,
|
|
26
|
+
seedDefaultAliases,
|
|
27
|
+
type TagStore,
|
|
28
|
+
} from './tag-manager.js';
|
|
29
|
+
import { initializeTables } from './schema.js';
|
|
30
|
+
import { computeHealthAudit, type HealthDataProvider } from './health-audit.js';
|
|
31
|
+
import { enrichEntryMetadata } from './metadata-enricher.js';
|
|
32
|
+
|
|
26
33
|
// ─── Constants ──────────────────────────────────────────────────────
|
|
27
34
|
|
|
28
|
-
const DEFAULT_DUPLICATE_THRESHOLD = 0.45;
|
|
29
|
-
const MERGE_SUGGESTION_THRESHOLD = 0.65;
|
|
30
|
-
const DEFAULT_CONTRADICTION_THRESHOLD = 0.4;
|
|
31
35
|
const DEFAULT_STALE_DAYS = 90;
|
|
32
36
|
|
|
33
|
-
const DEFAULT_TAG_ALIASES: Array<[string, string]> = [
|
|
34
|
-
['a11y', 'accessibility'],
|
|
35
|
-
['ts', 'typescript'],
|
|
36
|
-
['js', 'javascript'],
|
|
37
|
-
['css', 'styling'],
|
|
38
|
-
['tailwind', 'styling'],
|
|
39
|
-
['tw', 'styling'],
|
|
40
|
-
['vitest', 'testing'],
|
|
41
|
-
['jest', 'testing'],
|
|
42
|
-
['perf', 'performance'],
|
|
43
|
-
['sec', 'security'],
|
|
44
|
-
['auth', 'authentication'],
|
|
45
|
-
['i18n', 'internationalization'],
|
|
46
|
-
['l10n', 'localization'],
|
|
47
|
-
];
|
|
48
|
-
|
|
49
37
|
// ─── Curator Class ──────────────────────────────────────────────────
|
|
50
38
|
|
|
51
39
|
export class Curator {
|
|
52
40
|
private vault: Vault;
|
|
53
41
|
private provider: PersistenceProvider;
|
|
42
|
+
private tagStore: TagStore;
|
|
54
43
|
|
|
55
44
|
constructor(vault: Vault) {
|
|
56
45
|
this.vault = vault;
|
|
57
46
|
this.provider = vault.getProvider();
|
|
58
|
-
this.
|
|
59
|
-
this.
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
// ─── Schema ─────────────────────────────────────────────────────
|
|
63
|
-
|
|
64
|
-
private initializeTables(): void {
|
|
65
|
-
this.provider.execSql(`
|
|
66
|
-
CREATE TABLE IF NOT EXISTS curator_entry_state (
|
|
67
|
-
entry_id TEXT PRIMARY KEY,
|
|
68
|
-
status TEXT NOT NULL DEFAULT 'active' CHECK(status IN ('active', 'stale', 'archived')),
|
|
69
|
-
confidence REAL NOT NULL DEFAULT 1.0,
|
|
70
|
-
source TEXT NOT NULL DEFAULT 'unknown' CHECK(source IN ('manual', 'capture', 'seed', 'unknown')),
|
|
71
|
-
last_groomed_at INTEGER,
|
|
72
|
-
created_at INTEGER NOT NULL DEFAULT (unixepoch())
|
|
73
|
-
);
|
|
74
|
-
|
|
75
|
-
CREATE TABLE IF NOT EXISTS curator_tag_canonical (
|
|
76
|
-
tag TEXT PRIMARY KEY,
|
|
77
|
-
description TEXT,
|
|
78
|
-
created_at INTEGER NOT NULL DEFAULT (unixepoch())
|
|
79
|
-
);
|
|
80
|
-
|
|
81
|
-
CREATE TABLE IF NOT EXISTS curator_tag_alias (
|
|
82
|
-
alias TEXT PRIMARY KEY,
|
|
83
|
-
canonical TEXT NOT NULL,
|
|
84
|
-
created_at INTEGER NOT NULL DEFAULT (unixepoch()),
|
|
85
|
-
FOREIGN KEY (canonical) REFERENCES curator_tag_canonical(tag)
|
|
86
|
-
);
|
|
87
|
-
|
|
88
|
-
CREATE TABLE IF NOT EXISTS curator_changelog (
|
|
89
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
90
|
-
action TEXT NOT NULL,
|
|
91
|
-
entry_id TEXT NOT NULL,
|
|
92
|
-
before_value TEXT,
|
|
93
|
-
after_value TEXT,
|
|
94
|
-
reason TEXT NOT NULL,
|
|
95
|
-
created_at INTEGER NOT NULL DEFAULT (unixepoch())
|
|
96
|
-
);
|
|
97
|
-
|
|
98
|
-
CREATE TABLE IF NOT EXISTS curator_entry_history (
|
|
99
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
100
|
-
entry_id TEXT NOT NULL,
|
|
101
|
-
snapshot TEXT NOT NULL,
|
|
102
|
-
changed_by TEXT DEFAULT 'system',
|
|
103
|
-
change_reason TEXT,
|
|
104
|
-
created_at INTEGER NOT NULL DEFAULT (unixepoch())
|
|
105
|
-
);
|
|
106
|
-
|
|
107
|
-
CREATE TABLE IF NOT EXISTS curator_contradictions (
|
|
108
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
109
|
-
pattern_id TEXT NOT NULL,
|
|
110
|
-
antipattern_id TEXT NOT NULL,
|
|
111
|
-
similarity REAL NOT NULL,
|
|
112
|
-
status TEXT NOT NULL DEFAULT 'open' CHECK(status IN ('open', 'resolved', 'dismissed')),
|
|
113
|
-
created_at INTEGER NOT NULL DEFAULT (unixepoch()),
|
|
114
|
-
resolved_at INTEGER,
|
|
115
|
-
UNIQUE(pattern_id, antipattern_id)
|
|
116
|
-
);
|
|
117
|
-
CREATE INDEX IF NOT EXISTS idx_curator_state_status ON curator_entry_state(status);
|
|
118
|
-
CREATE INDEX IF NOT EXISTS idx_curator_changelog_entry ON curator_changelog(entry_id);
|
|
119
|
-
`);
|
|
47
|
+
this.tagStore = this.createTagStore();
|
|
48
|
+
initializeTables(this.provider);
|
|
49
|
+
this.provider.transaction(() => seedDefaultAliases(this.tagStore));
|
|
120
50
|
}
|
|
121
51
|
|
|
122
|
-
private
|
|
123
|
-
this.provider
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
52
|
+
private createTagStore(): TagStore {
|
|
53
|
+
const p = this.provider;
|
|
54
|
+
return {
|
|
55
|
+
getAlias(lower: string) {
|
|
56
|
+
return p.get<{ canonical: string }>('SELECT canonical FROM curator_tag_alias WHERE alias = ?', [lower])?.canonical ?? null;
|
|
57
|
+
},
|
|
58
|
+
insertCanonical(tag: string) {
|
|
59
|
+
p.run('INSERT OR IGNORE INTO curator_tag_canonical (tag) VALUES (?)', [tag]);
|
|
60
|
+
},
|
|
61
|
+
upsertAlias(alias: string, canonical: string) {
|
|
62
|
+
p.run('INSERT OR REPLACE INTO curator_tag_alias (alias, canonical) VALUES (?, ?)', [alias, canonical]);
|
|
63
|
+
},
|
|
64
|
+
getCanonicalRows() {
|
|
65
|
+
return p.all<{ tag: string; description: string | null; alias_count: number }>(
|
|
66
|
+
`SELECT c.tag, c.description, (SELECT COUNT(*) FROM curator_tag_alias a WHERE a.canonical = c.tag) as alias_count FROM curator_tag_canonical c ORDER BY c.tag`,
|
|
132
67
|
);
|
|
133
|
-
}
|
|
134
|
-
|
|
68
|
+
},
|
|
69
|
+
countTagUsage(tag: string) {
|
|
70
|
+
return p.get<{ count: number }>('SELECT COUNT(*) as count FROM entries WHERE tags LIKE ?', [`%"${tag}"%`])?.count ?? 0;
|
|
71
|
+
},
|
|
72
|
+
};
|
|
135
73
|
}
|
|
136
74
|
|
|
137
75
|
// ─── Status ─────────────────────────────────────────────────────
|
|
138
76
|
|
|
139
77
|
getStatus(): CuratorStatus {
|
|
140
78
|
const tableCount = (table: string): number =>
|
|
141
|
-
(
|
|
142
|
-
this.provider.get<{ count: number }>(`SELECT COUNT(*) as count FROM ${table}`) ?? {
|
|
143
|
-
count: 0,
|
|
144
|
-
}
|
|
145
|
-
).count;
|
|
146
|
-
|
|
79
|
+
(this.provider.get<{ count: number }>(`SELECT COUNT(*) as count FROM ${table}`) ?? { count: 0 }).count;
|
|
147
80
|
const lastGroomed = this.provider.get<{ ts: number | null }>(
|
|
148
81
|
'SELECT MAX(last_groomed_at) as ts FROM curator_entry_state WHERE last_groomed_at IS NOT NULL',
|
|
149
82
|
) ?? { ts: null };
|
|
150
|
-
|
|
151
83
|
return {
|
|
152
84
|
initialized: true,
|
|
153
|
-
tables: {
|
|
154
|
-
entry_state: tableCount('curator_entry_state'),
|
|
155
|
-
tag_canonical: tableCount('curator_tag_canonical'),
|
|
156
|
-
tag_alias: tableCount('curator_tag_alias'),
|
|
157
|
-
changelog: tableCount('curator_changelog'),
|
|
158
|
-
contradictions: tableCount('curator_contradictions'),
|
|
159
|
-
},
|
|
85
|
+
tables: { entry_state: tableCount('curator_entry_state'), tag_canonical: tableCount('curator_tag_canonical'), tag_alias: tableCount('curator_tag_alias'), changelog: tableCount('curator_changelog'), contradictions: tableCount('curator_contradictions') },
|
|
160
86
|
lastGroomedAt: lastGroomed.ts,
|
|
161
87
|
};
|
|
162
88
|
}
|
|
163
89
|
|
|
164
|
-
// ───
|
|
90
|
+
// ─── Tags (delegates to tag-manager) ──────────────────────────
|
|
165
91
|
|
|
166
|
-
normalizeTag(tag: string): TagNormalizationResult {
|
|
167
|
-
const lower = tag.toLowerCase().trim();
|
|
168
|
-
const row = this.provider.get<{ canonical: string }>(
|
|
169
|
-
'SELECT canonical FROM curator_tag_alias WHERE alias = ?',
|
|
170
|
-
[lower],
|
|
171
|
-
);
|
|
172
|
-
if (row) {
|
|
173
|
-
return { original: tag, normalized: row.canonical, wasAliased: true };
|
|
174
|
-
}
|
|
175
|
-
return { original: tag, normalized: lower, wasAliased: false };
|
|
176
|
-
}
|
|
92
|
+
normalizeTag(tag: string): TagNormalizationResult { return normalizeTagPure(tag, this.tagStore); }
|
|
177
93
|
|
|
178
94
|
normalizeTags(entryId: string): TagNormalizationResult[] {
|
|
179
95
|
const entry = this.vault.get(entryId);
|
|
180
96
|
if (!entry) return [];
|
|
181
|
-
|
|
182
|
-
const results: TagNormalizationResult[] = [];
|
|
183
|
-
const normalizedTags: string[] = [];
|
|
184
|
-
let changed = false;
|
|
185
|
-
|
|
186
|
-
for (const tag of entry.tags) {
|
|
187
|
-
const result = this.normalizeTag(tag);
|
|
188
|
-
results.push(result);
|
|
189
|
-
normalizedTags.push(result.normalized);
|
|
190
|
-
if (result.normalized !== tag) changed = true;
|
|
191
|
-
}
|
|
192
|
-
|
|
97
|
+
const { results, dedupedTags, changed } = normalizeAndDedup(entry.tags, this.tagStore);
|
|
193
98
|
if (changed) {
|
|
194
|
-
|
|
195
|
-
this.
|
|
196
|
-
JSON.stringify(dedupedTags),
|
|
197
|
-
entryId,
|
|
198
|
-
]);
|
|
199
|
-
this.logChange(
|
|
200
|
-
'normalize_tags',
|
|
201
|
-
entryId,
|
|
202
|
-
JSON.stringify(entry.tags),
|
|
203
|
-
JSON.stringify(dedupedTags),
|
|
204
|
-
'Tag normalization',
|
|
205
|
-
);
|
|
99
|
+
this.provider.run('UPDATE entries SET tags = ?, updated_at = unixepoch() WHERE id = ?', [JSON.stringify(dedupedTags), entryId]);
|
|
100
|
+
this.logChange('normalize_tags', entryId, JSON.stringify(entry.tags), JSON.stringify(dedupedTags), 'Tag normalization');
|
|
206
101
|
}
|
|
207
|
-
|
|
208
102
|
return results;
|
|
209
103
|
}
|
|
210
104
|
|
|
211
|
-
addTagAlias(alias: string, canonical: string): void {
|
|
212
|
-
|
|
213
|
-
const canonicalLower = canonical.toLowerCase().trim();
|
|
214
|
-
this.provider.run('INSERT OR IGNORE INTO curator_tag_canonical (tag) VALUES (?)', [
|
|
215
|
-
canonicalLower,
|
|
216
|
-
]);
|
|
217
|
-
this.provider.run('INSERT OR REPLACE INTO curator_tag_alias (alias, canonical) VALUES (?, ?)', [
|
|
218
|
-
lower,
|
|
219
|
-
canonicalLower,
|
|
220
|
-
]);
|
|
221
|
-
}
|
|
105
|
+
addTagAlias(alias: string, canonical: string): void { addTagAliasPure(alias, canonical, this.tagStore); }
|
|
106
|
+
getCanonicalTags(): CanonicalTag[] { return getCanonicalTagsPure(this.tagStore); }
|
|
222
107
|
|
|
223
|
-
|
|
224
|
-
const rows = this.provider.all<{
|
|
225
|
-
tag: string;
|
|
226
|
-
description: string | null;
|
|
227
|
-
alias_count: number;
|
|
228
|
-
}>(
|
|
229
|
-
`SELECT c.tag, c.description,
|
|
230
|
-
(SELECT COUNT(*) FROM curator_tag_alias a WHERE a.canonical = c.tag) as alias_count
|
|
231
|
-
FROM curator_tag_canonical c
|
|
232
|
-
ORDER BY c.tag`,
|
|
233
|
-
);
|
|
234
|
-
|
|
235
|
-
return rows.map((row) => ({
|
|
236
|
-
tag: row.tag,
|
|
237
|
-
description: row.description,
|
|
238
|
-
usageCount: this.countTagUsage(row.tag),
|
|
239
|
-
aliasCount: row.alias_count,
|
|
240
|
-
}));
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
private countTagUsage(tag: string): number {
|
|
244
|
-
const row = this.provider.get<{ count: number }>(
|
|
245
|
-
'SELECT COUNT(*) as count FROM entries WHERE tags LIKE ?',
|
|
246
|
-
[`%"${tag}"%`],
|
|
247
|
-
);
|
|
248
|
-
return row?.count ?? 0;
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
// ─── Duplicate Detection ────────────────────────────────────────
|
|
108
|
+
// ─── Duplicates (delegates to duplicate-detector) ─────────────
|
|
252
109
|
|
|
253
110
|
detectDuplicates(entryId?: string, threshold?: number): DuplicateDetectionResult[] {
|
|
254
|
-
|
|
255
|
-
const entries = this.vault.list({ limit: 100000 });
|
|
256
|
-
if (entries.length === 0) return [];
|
|
257
|
-
|
|
258
|
-
// Build transient vocabulary
|
|
259
|
-
const vocabulary = this.buildVocabulary(entries);
|
|
260
|
-
|
|
261
|
-
// Build vectors for all entries
|
|
262
|
-
const vectors = new Map<string, SparseVector>();
|
|
263
|
-
for (const entry of entries) {
|
|
264
|
-
const text = [entry.title, entry.description, entry.context ?? '', entry.tags.join(' ')].join(
|
|
265
|
-
' ',
|
|
266
|
-
);
|
|
267
|
-
vectors.set(entry.id, calculateTfIdf(tokenize(text), vocabulary));
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
const targetEntries = entryId ? entries.filter((e) => e.id === entryId) : entries;
|
|
271
|
-
|
|
272
|
-
const results: DuplicateDetectionResult[] = [];
|
|
273
|
-
|
|
274
|
-
for (const entry of targetEntries) {
|
|
275
|
-
const entryVec = vectors.get(entry.id)!;
|
|
276
|
-
const matches: DuplicateCandidate[] = [];
|
|
277
|
-
|
|
278
|
-
for (const other of entries) {
|
|
279
|
-
if (other.id === entry.id) continue;
|
|
280
|
-
const otherVec = vectors.get(other.id)!;
|
|
281
|
-
const similarity = cosineSimilarity(entryVec, otherVec);
|
|
282
|
-
if (similarity >= effectiveThreshold) {
|
|
283
|
-
matches.push({
|
|
284
|
-
entryId: other.id,
|
|
285
|
-
title: other.title,
|
|
286
|
-
similarity,
|
|
287
|
-
suggestMerge: similarity >= MERGE_SUGGESTION_THRESHOLD,
|
|
288
|
-
});
|
|
289
|
-
}
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
if (matches.length > 0) {
|
|
293
|
-
matches.sort((a, b) => b.similarity - a.similarity);
|
|
294
|
-
results.push({
|
|
295
|
-
entryId: entry.id,
|
|
296
|
-
matches,
|
|
297
|
-
scannedCount: entries.length - 1,
|
|
298
|
-
});
|
|
299
|
-
}
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
return results;
|
|
111
|
+
return detectDuplicatesPure(this.vault.list({ limit: 100000 }), entryId, threshold);
|
|
303
112
|
}
|
|
304
113
|
|
|
305
|
-
// ─── Contradictions
|
|
114
|
+
// ─── Contradictions (delegates to contradiction-detector) ─────
|
|
306
115
|
|
|
307
116
|
detectContradictions(threshold?: number): Contradiction[] {
|
|
308
|
-
const
|
|
309
|
-
|
|
310
|
-
const antipatterns = entries.filter((e) => e.type === 'anti-pattern');
|
|
311
|
-
const patterns = entries.filter((e) => e.type === 'pattern');
|
|
312
|
-
|
|
313
|
-
if (antipatterns.length === 0 || patterns.length === 0) return [];
|
|
314
|
-
|
|
315
|
-
const vocabulary = this.buildVocabulary(entries);
|
|
316
|
-
const detected: Contradiction[] = [];
|
|
317
|
-
|
|
318
|
-
for (const ap of antipatterns) {
|
|
319
|
-
// Stage 1: FTS5 candidate retrieval (fall back to all patterns if FTS returns empty)
|
|
320
|
-
let candidates: IntelligenceEntry[];
|
|
321
|
-
try {
|
|
322
|
-
const searchResults = this.vault.search(ap.title, { type: 'pattern', limit: 20 });
|
|
323
|
-
candidates = searchResults.length > 0 ? searchResults.map((r) => r.entry) : patterns;
|
|
324
|
-
} catch {
|
|
325
|
-
candidates = patterns;
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
// Stage 2: TF-IDF cosine similarity
|
|
329
|
-
const apText = [ap.title, ap.description, ap.context ?? ''].join(' ');
|
|
330
|
-
const apVec = calculateTfIdf(tokenize(apText), vocabulary);
|
|
331
|
-
|
|
332
|
-
for (const pattern of candidates) {
|
|
333
|
-
const pText = [pattern.title, pattern.description, pattern.context ?? ''].join(' ');
|
|
334
|
-
const pVec = calculateTfIdf(tokenize(pText), vocabulary);
|
|
335
|
-
const similarity = cosineSimilarity(apVec, pVec);
|
|
336
|
-
|
|
337
|
-
if (similarity >= effectiveThreshold) {
|
|
338
|
-
const result = this.provider.run(
|
|
339
|
-
'INSERT OR IGNORE INTO curator_contradictions (pattern_id, antipattern_id, similarity) VALUES (?, ?, ?)',
|
|
340
|
-
[pattern.id, ap.id, similarity],
|
|
341
|
-
);
|
|
342
|
-
if (result.changes > 0) {
|
|
343
|
-
const row = this.provider.get<Record<string, unknown>>(
|
|
344
|
-
'SELECT * FROM curator_contradictions WHERE pattern_id = ? AND antipattern_id = ?',
|
|
345
|
-
[pattern.id, ap.id],
|
|
346
|
-
);
|
|
347
|
-
if (row) detected.push(this.rowToContradiction(row));
|
|
348
|
-
}
|
|
349
|
-
}
|
|
350
|
-
}
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
return detected;
|
|
117
|
+
const searchFn = (title: string) => this.vault.search(title, { type: 'pattern', limit: 20 }).map((r) => r.entry);
|
|
118
|
+
return this.persistContradictions(findContradictions(this.vault.list({ limit: 100000 }), threshold, searchFn));
|
|
354
119
|
}
|
|
355
120
|
|
|
356
121
|
getContradictions(status?: ContradictionStatus): Contradiction[] {
|
|
357
|
-
const query = status
|
|
358
|
-
|
|
359
|
-
: 'SELECT * FROM curator_contradictions ORDER BY similarity DESC';
|
|
360
|
-
const rows = this.provider.all<Record<string, unknown>>(query, status ? [status] : undefined);
|
|
361
|
-
return rows.map((r) => this.rowToContradiction(r));
|
|
122
|
+
const query = status ? 'SELECT * FROM curator_contradictions WHERE status = ? ORDER BY similarity DESC' : 'SELECT * FROM curator_contradictions ORDER BY similarity DESC';
|
|
123
|
+
return this.provider.all<Record<string, unknown>>(query, status ? [status] : undefined).map((r) => this.rowToContradiction(r));
|
|
362
124
|
}
|
|
363
125
|
|
|
364
126
|
resolveContradiction(id: number, resolution: 'resolved' | 'dismissed'): Contradiction | null {
|
|
365
|
-
this.provider.run(
|
|
366
|
-
|
|
367
|
-
[resolution, id],
|
|
368
|
-
);
|
|
369
|
-
const row = this.provider.get<Record<string, unknown>>(
|
|
370
|
-
'SELECT * FROM curator_contradictions WHERE id = ?',
|
|
371
|
-
[id],
|
|
372
|
-
);
|
|
127
|
+
this.provider.run('UPDATE curator_contradictions SET status = ?, resolved_at = unixepoch() WHERE id = ?', [resolution, id]);
|
|
128
|
+
const row = this.provider.get<Record<string, unknown>>('SELECT * FROM curator_contradictions WHERE id = ?', [id]);
|
|
373
129
|
return row ? this.rowToContradiction(row) : null;
|
|
374
130
|
}
|
|
375
131
|
|
|
376
|
-
async detectContradictionsHybrid(threshold?: number): Promise<{
|
|
377
|
-
|
|
378
|
-
method: 'tfidf-only';
|
|
379
|
-
}> {
|
|
380
|
-
const effectiveThreshold = threshold ?? DEFAULT_CONTRADICTION_THRESHOLD;
|
|
381
|
-
const entries = this.vault.list({ limit: 100000 });
|
|
382
|
-
const antipatterns = entries.filter((e) => e.type === 'anti-pattern');
|
|
383
|
-
const patterns = entries.filter((e) => e.type === 'pattern');
|
|
384
|
-
|
|
385
|
-
if (antipatterns.length === 0 || patterns.length === 0) {
|
|
386
|
-
return { contradictions: [], method: 'tfidf-only' };
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
const vocabulary = this.buildVocabulary(entries);
|
|
390
|
-
const detected: Contradiction[] = [];
|
|
391
|
-
|
|
392
|
-
for (const ap of antipatterns) {
|
|
393
|
-
let candidates: IntelligenceEntry[];
|
|
394
|
-
try {
|
|
395
|
-
const searchResults = this.vault.search(ap.title, { type: 'pattern', limit: 20 });
|
|
396
|
-
candidates = searchResults.length > 0 ? searchResults.map((r) => r.entry) : patterns;
|
|
397
|
-
} catch {
|
|
398
|
-
candidates = patterns;
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
const apText = [ap.title, ap.description, ap.context ?? ''].join(' ');
|
|
402
|
-
const apVec = calculateTfIdf(tokenize(apText), vocabulary);
|
|
403
|
-
|
|
404
|
-
for (const pattern of candidates) {
|
|
405
|
-
const pText = [pattern.title, pattern.description, pattern.context ?? ''].join(' ');
|
|
406
|
-
const pVec = calculateTfIdf(tokenize(pText), vocabulary);
|
|
407
|
-
const finalScore = cosineSimilarity(apVec, pVec);
|
|
408
|
-
|
|
409
|
-
if (finalScore >= effectiveThreshold) {
|
|
410
|
-
const result = this.provider.run(
|
|
411
|
-
'INSERT OR IGNORE INTO curator_contradictions (pattern_id, antipattern_id, similarity) VALUES (?, ?, ?)',
|
|
412
|
-
[pattern.id, ap.id, finalScore],
|
|
413
|
-
);
|
|
414
|
-
if (result.changes > 0) {
|
|
415
|
-
const row = this.provider.get<Record<string, unknown>>(
|
|
416
|
-
'SELECT * FROM curator_contradictions WHERE pattern_id = ? AND antipattern_id = ?',
|
|
417
|
-
[pattern.id, ap.id],
|
|
418
|
-
);
|
|
419
|
-
if (row) detected.push(this.rowToContradiction(row));
|
|
420
|
-
}
|
|
421
|
-
}
|
|
422
|
-
}
|
|
423
|
-
}
|
|
424
|
-
|
|
425
|
-
return {
|
|
426
|
-
contradictions: detected,
|
|
427
|
-
method: 'tfidf-only',
|
|
428
|
-
};
|
|
132
|
+
async detectContradictionsHybrid(threshold?: number): Promise<{ contradictions: Contradiction[]; method: 'tfidf-only' }> {
|
|
133
|
+
const searchFn = (title: string) => this.vault.search(title, { type: 'pattern', limit: 20 }).map((r) => r.entry);
|
|
134
|
+
return { contradictions: this.persistContradictions(findContradictions(this.vault.list({ limit: 100000 }), threshold, searchFn)), method: 'tfidf-only' };
|
|
429
135
|
}
|
|
430
136
|
|
|
431
|
-
// ─── Grooming
|
|
137
|
+
// ─── Grooming ─────────────────────────────────────────────────
|
|
432
138
|
|
|
433
139
|
groomEntry(entryId: string): GroomResult | null {
|
|
434
140
|
const entry = this.vault.get(entryId);
|
|
435
141
|
if (!entry) return null;
|
|
436
|
-
|
|
437
142
|
const tagsNormalized = this.normalizeTags(entryId);
|
|
438
|
-
|
|
439
|
-
// Check staleness based on entry's updated_at timestamp
|
|
440
|
-
const row = this.provider.get<{ updated_at: number }>(
|
|
441
|
-
'SELECT updated_at FROM entries WHERE id = ?',
|
|
442
|
-
[entryId],
|
|
443
|
-
);
|
|
143
|
+
const row = this.provider.get<{ updated_at: number }>('SELECT updated_at FROM entries WHERE id = ?', [entryId]);
|
|
444
144
|
const now = Math.floor(Date.now() / 1000);
|
|
445
145
|
const stale = row ? now - row.updated_at > DEFAULT_STALE_DAYS * 86400 : false;
|
|
446
|
-
|
|
447
146
|
const status = stale ? 'stale' : 'active';
|
|
448
|
-
|
|
449
|
-
// Upsert entry state
|
|
450
|
-
this.provider.run(
|
|
451
|
-
`INSERT INTO curator_entry_state (entry_id, status, last_groomed_at)
|
|
452
|
-
VALUES (?, ?, unixepoch())
|
|
453
|
-
ON CONFLICT(entry_id) DO UPDATE SET status = excluded.status, last_groomed_at = unixepoch()`,
|
|
454
|
-
[entryId, status],
|
|
455
|
-
);
|
|
456
|
-
|
|
147
|
+
this.provider.run(`INSERT INTO curator_entry_state (entry_id, status, last_groomed_at) VALUES (?, ?, unixepoch()) ON CONFLICT(entry_id) DO UPDATE SET status = excluded.status, last_groomed_at = unixepoch()`, [entryId, status]);
|
|
457
148
|
this.logChange('groom', entryId, null, `status=${status}`, 'Routine grooming');
|
|
458
|
-
|
|
459
|
-
return {
|
|
460
|
-
entryId,
|
|
461
|
-
tagsNormalized,
|
|
462
|
-
stale,
|
|
463
|
-
lastGroomedAt: now,
|
|
464
|
-
};
|
|
149
|
+
return { entryId, tagsNormalized, stale, lastGroomedAt: now };
|
|
465
150
|
}
|
|
466
151
|
|
|
467
152
|
groomAll(): GroomAllResult {
|
|
468
153
|
const start = Date.now();
|
|
469
154
|
const entries = this.vault.list({ limit: 100000 });
|
|
470
|
-
let tagsNormalized = 0;
|
|
471
|
-
let staleCount = 0;
|
|
472
|
-
|
|
155
|
+
let tagsNormalized = 0, staleCount = 0;
|
|
473
156
|
for (const entry of entries) {
|
|
474
157
|
const result = this.groomEntry(entry.id);
|
|
475
|
-
if (result) {
|
|
476
|
-
tagsNormalized += result.tagsNormalized.filter((t) => t.wasAliased).length;
|
|
477
|
-
if (result.stale) staleCount++;
|
|
478
|
-
}
|
|
158
|
+
if (result) { tagsNormalized += result.tagsNormalized.filter((t) => t.wasAliased).length; if (result.stale) staleCount++; }
|
|
479
159
|
}
|
|
480
|
-
|
|
481
|
-
return {
|
|
482
|
-
totalEntries: entries.length,
|
|
483
|
-
groomedCount: entries.length,
|
|
484
|
-
tagsNormalized,
|
|
485
|
-
staleCount,
|
|
486
|
-
durationMs: Date.now() - start,
|
|
487
|
-
};
|
|
160
|
+
return { totalEntries: entries.length, groomedCount: entries.length, tagsNormalized, staleCount, durationMs: Date.now() - start };
|
|
488
161
|
}
|
|
489
162
|
|
|
490
|
-
// ─── Consolidation
|
|
163
|
+
// ─── Consolidation ───────────────────────────────────────────
|
|
491
164
|
|
|
492
165
|
consolidate(options?: ConsolidationOptions): ConsolidationResult {
|
|
493
166
|
const start = Date.now();
|
|
494
167
|
const dryRun = options?.dryRun ?? true;
|
|
495
168
|
const staleDaysThreshold = options?.staleDaysThreshold ?? DEFAULT_STALE_DAYS;
|
|
496
169
|
const duplicateThreshold = options?.duplicateThreshold ?? DEFAULT_DUPLICATE_THRESHOLD;
|
|
497
|
-
const contradictionThreshold =
|
|
498
|
-
options?.contradictionThreshold ?? DEFAULT_CONTRADICTION_THRESHOLD;
|
|
499
|
-
|
|
500
|
-
// Detect duplicates
|
|
170
|
+
const contradictionThreshold = options?.contradictionThreshold ?? DEFAULT_CONTRADICTION_THRESHOLD;
|
|
501
171
|
const duplicates = this.detectDuplicates(undefined, duplicateThreshold);
|
|
502
|
-
|
|
503
|
-
// Detect stale entries
|
|
504
172
|
const now = Math.floor(Date.now() / 1000);
|
|
505
|
-
const
|
|
506
|
-
const staleRows = this.provider.all<{ id: string }>(
|
|
507
|
-
'SELECT id FROM entries WHERE updated_at < ?',
|
|
508
|
-
[staleThreshold],
|
|
509
|
-
);
|
|
173
|
+
const staleRows = this.provider.all<{ id: string }>('SELECT id FROM entries WHERE updated_at < ?', [now - staleDaysThreshold * 86400]);
|
|
510
174
|
const staleEntries = staleRows.map((r) => r.id);
|
|
511
|
-
|
|
512
|
-
// Detect contradictions
|
|
513
175
|
const contradictions = this.detectContradictions(contradictionThreshold);
|
|
514
|
-
|
|
515
176
|
let mutations = 0;
|
|
516
|
-
|
|
517
177
|
if (!dryRun) {
|
|
518
|
-
// Archive stale entries
|
|
519
178
|
for (const entryId of staleEntries) {
|
|
520
|
-
this.provider.run(
|
|
521
|
-
|
|
522
|
-
VALUES (?, 'archived', unixepoch())
|
|
523
|
-
ON CONFLICT(entry_id) DO UPDATE SET status = 'archived', last_groomed_at = unixepoch()`,
|
|
524
|
-
[entryId],
|
|
525
|
-
);
|
|
526
|
-
this.logChange(
|
|
527
|
-
'archive',
|
|
528
|
-
entryId,
|
|
529
|
-
'active',
|
|
530
|
-
'archived',
|
|
531
|
-
'Stale entry archived during consolidation',
|
|
532
|
-
);
|
|
179
|
+
this.provider.run(`INSERT INTO curator_entry_state (entry_id, status, last_groomed_at) VALUES (?, 'archived', unixepoch()) ON CONFLICT(entry_id) DO UPDATE SET status = 'archived', last_groomed_at = unixepoch()`, [entryId]);
|
|
180
|
+
this.logChange('archive', entryId, 'active', 'archived', 'Stale entry archived during consolidation');
|
|
533
181
|
mutations++;
|
|
534
182
|
}
|
|
535
|
-
|
|
536
|
-
// Remove lower-similarity duplicates (keep the first entry, remove matches)
|
|
537
183
|
const removed = new Set<string>();
|
|
538
184
|
for (const result of duplicates) {
|
|
539
185
|
for (const match of result.matches) {
|
|
540
186
|
if (!removed.has(match.entryId) && match.entryId !== result.entryId) {
|
|
541
187
|
this.vault.remove(match.entryId);
|
|
542
|
-
this.logChange(
|
|
543
|
-
'remove_duplicate',
|
|
544
|
-
match.entryId,
|
|
545
|
-
null,
|
|
546
|
-
null,
|
|
547
|
-
`Duplicate of ${result.entryId} (similarity: ${match.similarity.toFixed(3)})`,
|
|
548
|
-
);
|
|
188
|
+
this.logChange('remove_duplicate', match.entryId, null, null, `Duplicate of ${result.entryId} (similarity: ${match.similarity.toFixed(3)})`);
|
|
549
189
|
removed.add(match.entryId);
|
|
550
190
|
mutations++;
|
|
551
191
|
}
|
|
552
192
|
}
|
|
553
193
|
}
|
|
554
194
|
}
|
|
555
|
-
|
|
556
|
-
return {
|
|
557
|
-
dryRun,
|
|
558
|
-
duplicates,
|
|
559
|
-
staleEntries,
|
|
560
|
-
contradictions,
|
|
561
|
-
mutations,
|
|
562
|
-
durationMs: Date.now() - start,
|
|
563
|
-
};
|
|
195
|
+
return { dryRun, duplicates, staleEntries, contradictions, mutations, durationMs: Date.now() - start };
|
|
564
196
|
}
|
|
565
197
|
|
|
566
|
-
// ─── Changelog
|
|
198
|
+
// ─── Changelog ────────────────────────────────────────────────
|
|
567
199
|
|
|
568
200
|
getEntryHistory(entryId: string, limit?: number): ChangelogEntry[] {
|
|
569
|
-
|
|
570
|
-
'SELECT * FROM curator_changelog WHERE entry_id = ? ORDER BY created_at DESC, id DESC LIMIT ?',
|
|
571
|
-
[entryId, limit ?? 50],
|
|
572
|
-
);
|
|
573
|
-
return rows.map((r) => this.rowToChangelog(r));
|
|
201
|
+
return this.provider.all<Record<string, unknown>>('SELECT * FROM curator_changelog WHERE entry_id = ? ORDER BY created_at DESC, id DESC LIMIT ?', [entryId, limit ?? 50]).map((r) => this.rowToChangelog(r));
|
|
574
202
|
}
|
|
575
203
|
|
|
576
|
-
// ─── Health Audit
|
|
204
|
+
// ─── Health Audit (delegates to health-audit) ─────────────────
|
|
577
205
|
|
|
578
206
|
healthAudit(): HealthAuditResult {
|
|
579
207
|
const entries = this.vault.list({ limit: 100000 });
|
|
580
|
-
const
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
metrics: { coverage: 1, freshness: 1, quality: 1, tagHealth: 1 },
|
|
586
|
-
recommendations: ['Vault is empty — add knowledge entries to get started.'],
|
|
587
|
-
};
|
|
588
|
-
}
|
|
589
|
-
|
|
590
|
-
let score = 100;
|
|
591
|
-
|
|
592
|
-
// Coverage: penalize if no anti-patterns or no patterns
|
|
593
|
-
const typeCount: Record<string, number> = { pattern: 0, 'anti-pattern': 0, rule: 0 };
|
|
594
|
-
for (const e of entries) {
|
|
595
|
-
typeCount[e.type] = (typeCount[e.type] ?? 0) + 1;
|
|
596
|
-
}
|
|
597
|
-
const hasPatterns = typeCount.pattern > 0;
|
|
598
|
-
const hasAntiPatterns = typeCount['anti-pattern'] > 0;
|
|
599
|
-
const hasRules = typeCount.rule > 0;
|
|
600
|
-
let coverageScore = 1;
|
|
601
|
-
if (!hasPatterns) {
|
|
602
|
-
score -= 10;
|
|
603
|
-
coverageScore -= 0.33;
|
|
604
|
-
recommendations.push('No patterns found — add patterns to improve coverage.');
|
|
605
|
-
}
|
|
606
|
-
if (!hasAntiPatterns) {
|
|
607
|
-
score -= 5;
|
|
608
|
-
coverageScore -= 0.17;
|
|
609
|
-
recommendations.push('No anti-patterns found — add anti-patterns to detect contradictions.');
|
|
610
|
-
}
|
|
611
|
-
if (!hasRules) {
|
|
612
|
-
score -= 5;
|
|
613
|
-
coverageScore -= 0.17;
|
|
614
|
-
recommendations.push('No rules found — add rules for completeness.');
|
|
615
|
-
}
|
|
616
|
-
coverageScore = Math.max(0, coverageScore);
|
|
617
|
-
|
|
618
|
-
// Freshness: penalize stale entries
|
|
619
|
-
const now = Math.floor(Date.now() / 1000);
|
|
620
|
-
const staleThreshold = now - DEFAULT_STALE_DAYS * 86400;
|
|
621
|
-
const staleCount = (
|
|
622
|
-
this.provider.get<{ count: number }>(
|
|
623
|
-
'SELECT COUNT(*) as count FROM entries WHERE updated_at < ?',
|
|
624
|
-
[staleThreshold],
|
|
625
|
-
) ?? { count: 0 }
|
|
626
|
-
).count;
|
|
627
|
-
const staleRatio = staleCount / entries.length;
|
|
628
|
-
const freshnessScore = 1 - staleRatio;
|
|
629
|
-
if (staleRatio > 0.3) {
|
|
630
|
-
const penalty = Math.min(20, Math.round(staleRatio * 30));
|
|
631
|
-
score -= penalty;
|
|
632
|
-
recommendations.push(
|
|
633
|
-
`${staleCount} stale entries (${Math.round(staleRatio * 100)}%) — run grooming to update.`,
|
|
634
|
-
);
|
|
635
|
-
}
|
|
636
|
-
|
|
637
|
-
// Quality: penalize duplicates and contradictions
|
|
638
|
-
const duplicates = this.detectDuplicates();
|
|
639
|
-
const contradictions = this.getContradictions('open');
|
|
640
|
-
let qualityScore = 1;
|
|
641
|
-
if (duplicates.length > 0) {
|
|
642
|
-
const penalty = Math.min(15, duplicates.length * 3);
|
|
643
|
-
score -= penalty;
|
|
644
|
-
qualityScore -= penalty / 30;
|
|
645
|
-
recommendations.push(`${duplicates.length} entries have duplicates — run consolidation.`);
|
|
646
|
-
}
|
|
647
|
-
if (contradictions.length > 0) {
|
|
648
|
-
const penalty = Math.min(15, contradictions.length * 5);
|
|
649
|
-
score -= penalty;
|
|
650
|
-
qualityScore -= penalty / 30;
|
|
651
|
-
recommendations.push(`${contradictions.length} open contradictions — resolve or dismiss.`);
|
|
652
|
-
}
|
|
653
|
-
qualityScore = Math.max(0, qualityScore);
|
|
654
|
-
|
|
655
|
-
// Tag health: penalize entries with few or no tags
|
|
656
|
-
const lowTagEntries = entries.filter((e) => e.tags.length < 2);
|
|
657
|
-
const lowTagRatio = lowTagEntries.length / entries.length;
|
|
658
|
-
const tagHealthScore = 1 - lowTagRatio;
|
|
659
|
-
if (lowTagRatio > 0.3) {
|
|
660
|
-
const penalty = Math.min(10, Math.round(lowTagRatio * 15));
|
|
661
|
-
score -= penalty;
|
|
662
|
-
recommendations.push(
|
|
663
|
-
`${lowTagEntries.length} entries have fewer than 2 tags — improve tagging.`,
|
|
664
|
-
);
|
|
665
|
-
}
|
|
666
|
-
|
|
667
|
-
// Penalize ungroomed entries
|
|
668
|
-
const groomedCount = (
|
|
669
|
-
this.provider.get<{ count: number }>(
|
|
670
|
-
'SELECT COUNT(*) as count FROM curator_entry_state WHERE last_groomed_at IS NOT NULL',
|
|
671
|
-
) ?? { count: 0 }
|
|
672
|
-
).count;
|
|
673
|
-
if (groomedCount < entries.length) {
|
|
674
|
-
const ungroomed = entries.length - groomedCount;
|
|
675
|
-
const penalty = Math.min(10, Math.round((ungroomed / entries.length) * 10));
|
|
676
|
-
score -= penalty;
|
|
677
|
-
recommendations.push(`${ungroomed} entries never groomed — run groomAll().`);
|
|
678
|
-
}
|
|
679
|
-
|
|
680
|
-
score = Math.max(0, score);
|
|
681
|
-
|
|
682
|
-
if (recommendations.length === 0) {
|
|
683
|
-
recommendations.push('Vault is healthy — no issues detected.');
|
|
684
|
-
}
|
|
685
|
-
|
|
686
|
-
return {
|
|
687
|
-
score,
|
|
688
|
-
metrics: {
|
|
689
|
-
coverage: coverageScore,
|
|
690
|
-
freshness: freshnessScore,
|
|
691
|
-
quality: qualityScore,
|
|
692
|
-
tagHealth: tagHealthScore,
|
|
693
|
-
},
|
|
694
|
-
recommendations,
|
|
208
|
+
const dataProvider: HealthDataProvider = {
|
|
209
|
+
getStaleCount: (threshold) => (this.provider.get<{ count: number }>('SELECT COUNT(*) as count FROM entries WHERE updated_at < ?', [threshold]) ?? { count: 0 }).count,
|
|
210
|
+
getGroomedCount: () => (this.provider.get<{ count: number }>('SELECT COUNT(*) as count FROM curator_entry_state WHERE last_groomed_at IS NOT NULL') ?? { count: 0 }).count,
|
|
211
|
+
getDuplicates: () => this.detectDuplicates(),
|
|
212
|
+
getOpenContradictions: () => this.getContradictions('open'),
|
|
695
213
|
};
|
|
214
|
+
return computeHealthAudit(entries, dataProvider, DEFAULT_STALE_DAYS);
|
|
696
215
|
}
|
|
697
216
|
|
|
698
|
-
// ─── Entry History (Version Snapshots)
|
|
217
|
+
// ─── Entry History (Version Snapshots) ────────────────────────
|
|
699
218
|
|
|
700
|
-
recordSnapshot(
|
|
701
|
-
entryId: string,
|
|
702
|
-
changedBy?: string,
|
|
703
|
-
changeReason?: string,
|
|
704
|
-
): { recorded: boolean; historyId: number } {
|
|
219
|
+
recordSnapshot(entryId: string, changedBy?: string, changeReason?: string): { recorded: boolean; historyId: number } {
|
|
705
220
|
const entry = this.vault.get(entryId);
|
|
706
221
|
if (!entry) return { recorded: false, historyId: -1 };
|
|
707
|
-
|
|
708
|
-
const result = this.provider.run(
|
|
709
|
-
'INSERT INTO curator_entry_history (entry_id, snapshot, changed_by, change_reason, created_at) VALUES (?, ?, ?, ?, unixepoch())',
|
|
710
|
-
[entryId, JSON.stringify(entry), changedBy ?? 'system', changeReason ?? null],
|
|
711
|
-
);
|
|
712
|
-
|
|
222
|
+
const result = this.provider.run('INSERT INTO curator_entry_history (entry_id, snapshot, changed_by, change_reason, created_at) VALUES (?, ?, ?, ?, unixepoch())', [entryId, JSON.stringify(entry), changedBy ?? 'system', changeReason ?? null]);
|
|
713
223
|
return { recorded: true, historyId: Number(result.lastInsertRowid) };
|
|
714
224
|
}
|
|
715
225
|
|
|
716
|
-
getVersionHistory(entryId: string): Array<{
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
snapshot: IntelligenceEntry;
|
|
720
|
-
changedBy: string;
|
|
721
|
-
changeReason: string | null;
|
|
722
|
-
createdAt: number;
|
|
723
|
-
}> {
|
|
724
|
-
const rows = this.provider.all<Record<string, unknown>>(
|
|
725
|
-
'SELECT * FROM curator_entry_history WHERE entry_id = ? ORDER BY created_at ASC, id ASC',
|
|
726
|
-
[entryId],
|
|
727
|
-
);
|
|
728
|
-
|
|
729
|
-
return rows.map((row) => ({
|
|
730
|
-
historyId: row.id as number,
|
|
731
|
-
entryId: row.entry_id as string,
|
|
732
|
-
snapshot: JSON.parse(row.snapshot as string) as IntelligenceEntry,
|
|
733
|
-
changedBy: row.changed_by as string,
|
|
734
|
-
changeReason: (row.change_reason as string) ?? null,
|
|
735
|
-
createdAt: row.created_at as number,
|
|
226
|
+
getVersionHistory(entryId: string): Array<{ historyId: number; entryId: string; snapshot: IntelligenceEntry; changedBy: string; changeReason: string | null; createdAt: number }> {
|
|
227
|
+
return this.provider.all<Record<string, unknown>>('SELECT * FROM curator_entry_history WHERE entry_id = ? ORDER BY created_at ASC, id ASC', [entryId]).map((row) => ({
|
|
228
|
+
historyId: row.id as number, entryId: row.entry_id as string, snapshot: JSON.parse(row.snapshot as string) as IntelligenceEntry, changedBy: row.changed_by as string, changeReason: (row.change_reason as string) ?? null, createdAt: row.created_at as number,
|
|
736
229
|
}));
|
|
737
230
|
}
|
|
738
231
|
|
|
739
|
-
// ─── Queue Stats
|
|
740
|
-
|
|
741
|
-
getQueueStats(): {
|
|
742
|
-
totalEntries: number;
|
|
743
|
-
groomedEntries: number;
|
|
744
|
-
ungroomedEntries: number;
|
|
745
|
-
staleEntries: number;
|
|
746
|
-
freshEntries: number;
|
|
747
|
-
avgDaysSinceGroom: number;
|
|
748
|
-
} {
|
|
749
|
-
const totalEntries = (
|
|
750
|
-
this.provider.get<{ count: number }>('SELECT COUNT(*) as count FROM entries') ?? { count: 0 }
|
|
751
|
-
).count;
|
|
752
|
-
|
|
753
|
-
const groomedEntries = (
|
|
754
|
-
this.provider.get<{ count: number }>(
|
|
755
|
-
'SELECT COUNT(*) as count FROM curator_entry_state WHERE last_groomed_at IS NOT NULL',
|
|
756
|
-
) ?? { count: 0 }
|
|
757
|
-
).count;
|
|
758
|
-
|
|
759
|
-
const ungroomedEntries = totalEntries - groomedEntries;
|
|
232
|
+
// ─── Queue Stats ─────────────────────────────────────────────
|
|
760
233
|
|
|
234
|
+
getQueueStats(): { totalEntries: number; groomedEntries: number; ungroomedEntries: number; staleEntries: number; freshEntries: number; avgDaysSinceGroom: number } {
|
|
235
|
+
const p = this.provider;
|
|
236
|
+
const totalEntries = (p.get<{ count: number }>('SELECT COUNT(*) as count FROM entries') ?? { count: 0 }).count;
|
|
237
|
+
const groomedEntries = (p.get<{ count: number }>('SELECT COUNT(*) as count FROM curator_entry_state WHERE last_groomed_at IS NOT NULL') ?? { count: 0 }).count;
|
|
761
238
|
const now = Math.floor(Date.now() / 1000);
|
|
762
|
-
const
|
|
763
|
-
const
|
|
764
|
-
|
|
765
|
-
const staleEntries = (
|
|
766
|
-
this.provider.get<{ count: number }>(
|
|
767
|
-
'SELECT COUNT(*) as count FROM curator_entry_state WHERE last_groomed_at IS NOT NULL AND last_groomed_at < ?',
|
|
768
|
-
[staleThreshold],
|
|
769
|
-
) ?? { count: 0 }
|
|
770
|
-
).count;
|
|
771
|
-
|
|
772
|
-
const freshEntries = (
|
|
773
|
-
this.provider.get<{ count: number }>(
|
|
774
|
-
'SELECT COUNT(*) as count FROM curator_entry_state WHERE last_groomed_at IS NOT NULL AND last_groomed_at >= ?',
|
|
775
|
-
[freshThreshold],
|
|
776
|
-
) ?? { count: 0 }
|
|
777
|
-
).count;
|
|
778
|
-
|
|
239
|
+
const staleEntries = (p.get<{ count: number }>('SELECT COUNT(*) as count FROM curator_entry_state WHERE last_groomed_at IS NOT NULL AND last_groomed_at < ?', [now - 30 * 86400]) ?? { count: 0 }).count;
|
|
240
|
+
const freshEntries = (p.get<{ count: number }>('SELECT COUNT(*) as count FROM curator_entry_state WHERE last_groomed_at IS NOT NULL AND last_groomed_at >= ?', [now - 7 * 86400]) ?? { count: 0 }).count;
|
|
779
241
|
let avgDaysSinceGroom = 0;
|
|
780
242
|
if (groomedEntries > 0) {
|
|
781
|
-
const
|
|
782
|
-
'SELECT SUM(? - last_groomed_at) as total FROM curator_entry_state WHERE last_groomed_at IS NOT NULL',
|
|
783
|
-
[now],
|
|
784
|
-
) ?? { total: 0 };
|
|
785
|
-
const totalSeconds = sumRow.total ?? 0;
|
|
243
|
+
const totalSeconds = (p.get<{ total: number | null }>('SELECT SUM(? - last_groomed_at) as total FROM curator_entry_state WHERE last_groomed_at IS NOT NULL', [now]) ?? { total: 0 }).total ?? 0;
|
|
786
244
|
avgDaysSinceGroom = Math.round((totalSeconds / groomedEntries / 86400) * 100) / 100;
|
|
787
245
|
}
|
|
788
|
-
|
|
789
|
-
return {
|
|
790
|
-
totalEntries,
|
|
791
|
-
groomedEntries,
|
|
792
|
-
ungroomedEntries,
|
|
793
|
-
staleEntries,
|
|
794
|
-
freshEntries,
|
|
795
|
-
avgDaysSinceGroom,
|
|
796
|
-
};
|
|
246
|
+
return { totalEntries, groomedEntries, ungroomedEntries: totalEntries - groomedEntries, staleEntries, freshEntries, avgDaysSinceGroom };
|
|
797
247
|
}
|
|
798
248
|
|
|
799
|
-
// ─── Metadata Enrichment
|
|
249
|
+
// ─── Metadata Enrichment (delegates to metadata-enricher) ────
|
|
800
250
|
|
|
801
|
-
enrichMetadata(entryId: string): {
|
|
802
|
-
enriched: boolean;
|
|
803
|
-
changes: Array<{ field: string; before: string; after: string }>;
|
|
804
|
-
} {
|
|
251
|
+
enrichMetadata(entryId: string): { enriched: boolean; changes: Array<{ field: string; before: string; after: string }> } {
|
|
805
252
|
const entry = this.vault.get(entryId);
|
|
806
253
|
if (!entry) return { enriched: false, changes: [] };
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
const updates: Partial<
|
|
810
|
-
Pick<IntelligenceEntry, 'title' | 'description' | 'tags' | 'severity' | 'type'>
|
|
811
|
-
> = {};
|
|
812
|
-
|
|
813
|
-
// Auto-capitalize title
|
|
814
|
-
if (entry.title.length > 0 && entry.title[0] !== entry.title[0].toUpperCase()) {
|
|
815
|
-
const capitalized = entry.title[0].toUpperCase() + entry.title.slice(1);
|
|
816
|
-
changes.push({ field: 'title', before: entry.title, after: capitalized });
|
|
817
|
-
updates.title = capitalized;
|
|
818
|
-
}
|
|
819
|
-
|
|
820
|
-
// Normalize tags: lowercase, trim, dedup
|
|
821
|
-
const normalizedTags = [...new Set(entry.tags.map((t) => t.toLowerCase().trim()))];
|
|
822
|
-
const tagsChanged =
|
|
823
|
-
normalizedTags.length !== entry.tags.length ||
|
|
824
|
-
normalizedTags.some((t, i) => t !== entry.tags[i]);
|
|
825
|
-
if (tagsChanged) {
|
|
826
|
-
changes.push({
|
|
827
|
-
field: 'tags',
|
|
828
|
-
before: JSON.stringify(entry.tags),
|
|
829
|
-
after: JSON.stringify(normalizedTags),
|
|
830
|
-
});
|
|
831
|
-
updates.tags = normalizedTags;
|
|
832
|
-
}
|
|
833
|
-
|
|
834
|
-
// Infer severity from keywords if currently 'suggestion'
|
|
835
|
-
if (entry.severity === 'suggestion') {
|
|
836
|
-
const text = (entry.title + ' ' + entry.description).toLowerCase();
|
|
837
|
-
const criticalKeywords = ['never', 'must not', 'critical', 'security', 'vulnerability'];
|
|
838
|
-
const warningKeywords = ['avoid', 'should not', 'deprecated', 'careful', 'warning'];
|
|
839
|
-
if (criticalKeywords.some((k) => text.includes(k))) {
|
|
840
|
-
changes.push({ field: 'severity', before: entry.severity, after: 'critical' });
|
|
841
|
-
updates.severity = 'critical';
|
|
842
|
-
} else if (warningKeywords.some((k) => text.includes(k))) {
|
|
843
|
-
changes.push({ field: 'severity', before: entry.severity, after: 'warning' });
|
|
844
|
-
updates.severity = 'warning';
|
|
845
|
-
}
|
|
846
|
-
}
|
|
847
|
-
|
|
848
|
-
// Infer type from title patterns
|
|
849
|
-
if (entry.type === 'pattern') {
|
|
850
|
-
const titleLower = entry.title.toLowerCase();
|
|
851
|
-
if (
|
|
852
|
-
titleLower.startsWith('avoid') ||
|
|
853
|
-
titleLower.startsWith('never') ||
|
|
854
|
-
titleLower.startsWith("don't") ||
|
|
855
|
-
titleLower.startsWith('do not')
|
|
856
|
-
) {
|
|
857
|
-
changes.push({ field: 'type', before: entry.type, after: 'anti-pattern' });
|
|
858
|
-
updates.type = 'anti-pattern';
|
|
859
|
-
}
|
|
860
|
-
}
|
|
861
|
-
|
|
862
|
-
// Trim whitespace from description
|
|
863
|
-
const trimmed = entry.description.trim();
|
|
864
|
-
if (trimmed !== entry.description) {
|
|
865
|
-
changes.push({ field: 'description', before: entry.description, after: trimmed });
|
|
866
|
-
updates.description = trimmed;
|
|
867
|
-
}
|
|
868
|
-
|
|
869
|
-
if (changes.length === 0) {
|
|
870
|
-
return { enriched: false, changes: [] };
|
|
871
|
-
}
|
|
872
|
-
|
|
873
|
-
// Apply updates
|
|
254
|
+
const { changes, updates } = enrichEntryMetadata(entry);
|
|
255
|
+
if (changes.length === 0) return { enriched: false, changes: [] };
|
|
874
256
|
this.vault.update(entryId, updates);
|
|
875
|
-
|
|
876
|
-
// Record snapshot
|
|
877
257
|
this.recordSnapshot(entryId, 'curator', 'Metadata enrichment');
|
|
878
|
-
|
|
879
|
-
// Log change
|
|
880
|
-
this.logChange(
|
|
881
|
-
'enrich_metadata',
|
|
882
|
-
entryId,
|
|
883
|
-
JSON.stringify(changes.map((c) => c.field)),
|
|
884
|
-
JSON.stringify(changes.map((c) => c.after)),
|
|
885
|
-
'Rule-based metadata enrichment',
|
|
886
|
-
);
|
|
887
|
-
|
|
258
|
+
this.logChange('enrich_metadata', entryId, JSON.stringify(changes.map((c) => c.field)), JSON.stringify(changes.map((c) => c.after)), 'Rule-based metadata enrichment');
|
|
888
259
|
return { enriched: true, changes };
|
|
889
260
|
}
|
|
890
261
|
|
|
891
|
-
// ─── Private Helpers
|
|
262
|
+
// ─── Private Helpers ──────────────────────────────────────────
|
|
892
263
|
|
|
893
|
-
private
|
|
894
|
-
const
|
|
895
|
-
const
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
' ',
|
|
899
|
-
|
|
900
|
-
const tokens = new Set(tokenize(text));
|
|
901
|
-
for (const token of tokens) {
|
|
902
|
-
termDocFreq.set(token, (termDocFreq.get(token) ?? 0) + 1);
|
|
264
|
+
private persistContradictions(candidates: ContradictionCandidate[]): Contradiction[] {
|
|
265
|
+
const detected: Contradiction[] = [];
|
|
266
|
+
for (const c of candidates) {
|
|
267
|
+
const result = this.provider.run('INSERT OR IGNORE INTO curator_contradictions (pattern_id, antipattern_id, similarity) VALUES (?, ?, ?)', [c.patternId, c.antipatternId, c.similarity]);
|
|
268
|
+
if (result.changes > 0) {
|
|
269
|
+
const row = this.provider.get<Record<string, unknown>>('SELECT * FROM curator_contradictions WHERE pattern_id = ? AND antipattern_id = ?', [c.patternId, c.antipatternId]);
|
|
270
|
+
if (row) detected.push(this.rowToContradiction(row));
|
|
903
271
|
}
|
|
904
272
|
}
|
|
905
|
-
|
|
906
|
-
for (const [term, df] of termDocFreq) {
|
|
907
|
-
const idf = Math.log((docCount + 1) / (df + 1)) + 1;
|
|
908
|
-
vocabulary.set(term, idf);
|
|
909
|
-
}
|
|
910
|
-
return vocabulary;
|
|
273
|
+
return detected;
|
|
911
274
|
}
|
|
912
275
|
|
|
913
|
-
private logChange(
|
|
914
|
-
action
|
|
915
|
-
entryId: string,
|
|
916
|
-
beforeValue: string | null,
|
|
917
|
-
afterValue: string | null,
|
|
918
|
-
reason: string,
|
|
919
|
-
): void {
|
|
920
|
-
this.provider.run(
|
|
921
|
-
'INSERT INTO curator_changelog (action, entry_id, before_value, after_value, reason) VALUES (?, ?, ?, ?, ?)',
|
|
922
|
-
[action, entryId, beforeValue, afterValue, reason],
|
|
923
|
-
);
|
|
276
|
+
private logChange(action: string, entryId: string, beforeValue: string | null, afterValue: string | null, reason: string): void {
|
|
277
|
+
this.provider.run('INSERT INTO curator_changelog (action, entry_id, before_value, after_value, reason) VALUES (?, ?, ?, ?, ?)', [action, entryId, beforeValue, afterValue, reason]);
|
|
924
278
|
}
|
|
925
279
|
|
|
926
280
|
private rowToContradiction(row: Record<string, unknown>): Contradiction {
|
|
927
|
-
return {
|
|
928
|
-
id: row.id as number,
|
|
929
|
-
patternId: row.pattern_id as string,
|
|
930
|
-
antipatternId: row.antipattern_id as string,
|
|
931
|
-
similarity: row.similarity as number,
|
|
932
|
-
status: row.status as ContradictionStatus,
|
|
933
|
-
createdAt: row.created_at as number,
|
|
934
|
-
resolvedAt: (row.resolved_at as number) ?? null,
|
|
935
|
-
};
|
|
281
|
+
return { id: row.id as number, patternId: row.pattern_id as string, antipatternId: row.antipattern_id as string, similarity: row.similarity as number, status: row.status as ContradictionStatus, createdAt: row.created_at as number, resolvedAt: (row.resolved_at as number) ?? null };
|
|
936
282
|
}
|
|
937
283
|
|
|
938
284
|
private rowToChangelog(row: Record<string, unknown>): ChangelogEntry {
|
|
939
|
-
return {
|
|
940
|
-
id: row.id as number,
|
|
941
|
-
action: row.action as string,
|
|
942
|
-
entryId: row.entry_id as string,
|
|
943
|
-
beforeValue: (row.before_value as string) ?? null,
|
|
944
|
-
afterValue: (row.after_value as string) ?? null,
|
|
945
|
-
reason: row.reason as string,
|
|
946
|
-
createdAt: row.created_at as number,
|
|
947
|
-
};
|
|
285
|
+
return { id: row.id as number, action: row.action as string, entryId: row.entry_id as string, beforeValue: (row.before_value as string) ?? null, afterValue: (row.after_value as string) ?? null, reason: row.reason as string, createdAt: row.created_at as number };
|
|
948
286
|
}
|
|
949
287
|
}
|