opencode-telos 1.0.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/README.md +927 -0
- package/dist/code-intelligence/analyzer.d.ts +9 -0
- package/dist/code-intelligence/analyzer.d.ts.map +1 -0
- package/dist/code-intelligence/analyzer.js +321 -0
- package/dist/code-intelligence/analyzer.js.map +1 -0
- package/dist/code-intelligence/ast/cache.d.ts +16 -0
- package/dist/code-intelligence/ast/cache.d.ts.map +1 -0
- package/dist/code-intelligence/ast/cache.js +44 -0
- package/dist/code-intelligence/ast/cache.js.map +1 -0
- package/dist/code-intelligence/ast/common.d.ts +8 -0
- package/dist/code-intelligence/ast/common.d.ts.map +1 -0
- package/dist/code-intelligence/ast/common.js +42 -0
- package/dist/code-intelligence/ast/common.js.map +1 -0
- package/dist/code-intelligence/ast/component.d.ts +10 -0
- package/dist/code-intelligence/ast/component.d.ts.map +1 -0
- package/dist/code-intelligence/ast/component.js +38 -0
- package/dist/code-intelligence/ast/component.js.map +1 -0
- package/dist/code-intelligence/ast/fallback.d.ts +9 -0
- package/dist/code-intelligence/ast/fallback.d.ts.map +1 -0
- package/dist/code-intelligence/ast/fallback.js +38 -0
- package/dist/code-intelligence/ast/fallback.js.map +1 -0
- package/dist/code-intelligence/ast/index.d.ts +5 -0
- package/dist/code-intelligence/ast/index.d.ts.map +1 -0
- package/dist/code-intelligence/ast/index.js +5 -0
- package/dist/code-intelligence/ast/index.js.map +1 -0
- package/dist/code-intelligence/ast/ir.d.ts +72 -0
- package/dist/code-intelligence/ast/ir.d.ts.map +1 -0
- package/dist/code-intelligence/ast/ir.js +2 -0
- package/dist/code-intelligence/ast/ir.js.map +1 -0
- package/dist/code-intelligence/ast/metrics.d.ts +25 -0
- package/dist/code-intelligence/ast/metrics.d.ts.map +1 -0
- package/dist/code-intelligence/ast/metrics.js +38 -0
- package/dist/code-intelligence/ast/metrics.js.map +1 -0
- package/dist/code-intelligence/ast/registry.d.ts +5 -0
- package/dist/code-intelligence/ast/registry.d.ts.map +1 -0
- package/dist/code-intelligence/ast/registry.js +22 -0
- package/dist/code-intelligence/ast/registry.js.map +1 -0
- package/dist/code-intelligence/ast/tree-sitter.d.ts +8 -0
- package/dist/code-intelligence/ast/tree-sitter.d.ts.map +1 -0
- package/dist/code-intelligence/ast/tree-sitter.js +247 -0
- package/dist/code-intelligence/ast/tree-sitter.js.map +1 -0
- package/dist/code-intelligence/ast/typescript.d.ts +8 -0
- package/dist/code-intelligence/ast/typescript.d.ts.map +1 -0
- package/dist/code-intelligence/ast/typescript.js +147 -0
- package/dist/code-intelligence/ast/typescript.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +18 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp/server.d.ts +27 -0
- package/dist/mcp/server.d.ts.map +1 -0
- package/dist/mcp/server.js +67 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/opencode/hooks.d.ts +9 -0
- package/dist/opencode/hooks.d.ts.map +1 -0
- package/dist/opencode/hooks.js +583 -0
- package/dist/opencode/hooks.js.map +1 -0
- package/dist/opencode/router/categories.d.ts +31 -0
- package/dist/opencode/router/categories.d.ts.map +1 -0
- package/dist/opencode/router/categories.js +165 -0
- package/dist/opencode/router/categories.js.map +1 -0
- package/dist/opencode/router/embeddings.d.ts +41 -0
- package/dist/opencode/router/embeddings.d.ts.map +1 -0
- package/dist/opencode/router/embeddings.js +136 -0
- package/dist/opencode/router/embeddings.js.map +1 -0
- package/dist/opencode/router/graph-state-snapshot.d.ts +35 -0
- package/dist/opencode/router/graph-state-snapshot.d.ts.map +1 -0
- package/dist/opencode/router/graph-state-snapshot.js +129 -0
- package/dist/opencode/router/graph-state-snapshot.js.map +1 -0
- package/dist/opencode/router/index.d.ts +21 -0
- package/dist/opencode/router/index.d.ts.map +1 -0
- package/dist/opencode/router/index.js +23 -0
- package/dist/opencode/router/index.js.map +1 -0
- package/dist/opencode/router/intent-classifier.d.ts +35 -0
- package/dist/opencode/router/intent-classifier.d.ts.map +1 -0
- package/dist/opencode/router/intent-classifier.js +108 -0
- package/dist/opencode/router/intent-classifier.js.map +1 -0
- package/dist/opencode/router/semantic-nudge.d.ts +42 -0
- package/dist/opencode/router/semantic-nudge.d.ts.map +1 -0
- package/dist/opencode/router/semantic-nudge.js +73 -0
- package/dist/opencode/router/semantic-nudge.js.map +1 -0
- package/dist/opencode/router/state-gate.d.ts +25 -0
- package/dist/opencode/router/state-gate.d.ts.map +1 -0
- package/dist/opencode/router/state-gate.js +178 -0
- package/dist/opencode/router/state-gate.js.map +1 -0
- package/dist/opencode/router/tool-embeddings.d.ts +36 -0
- package/dist/opencode/router/tool-embeddings.d.ts.map +1 -0
- package/dist/opencode/router/tool-embeddings.js +140 -0
- package/dist/opencode/router/tool-embeddings.js.map +1 -0
- package/dist/opencode/router/tool-registry.d.ts +45 -0
- package/dist/opencode/router/tool-registry.d.ts.map +1 -0
- package/dist/opencode/router/tool-registry.js +103 -0
- package/dist/opencode/router/tool-registry.js.map +1 -0
- package/dist/opencode/router/tool-taxonomy.d.ts +51 -0
- package/dist/opencode/router/tool-taxonomy.d.ts.map +1 -0
- package/dist/opencode/router/tool-taxonomy.js +250 -0
- package/dist/opencode/router/tool-taxonomy.js.map +1 -0
- package/dist/opencode/router/tools-composite.d.ts +21 -0
- package/dist/opencode/router/tools-composite.d.ts.map +1 -0
- package/dist/opencode/router/tools-composite.js +684 -0
- package/dist/opencode/router/tools-composite.js.map +1 -0
- package/dist/opencode/shell-hooks.d.ts +7 -0
- package/dist/opencode/shell-hooks.d.ts.map +1 -0
- package/dist/opencode/shell-hooks.js +96 -0
- package/dist/opencode/shell-hooks.js.map +1 -0
- package/dist/opencode/system-prompt.d.ts +10 -0
- package/dist/opencode/system-prompt.d.ts.map +1 -0
- package/dist/opencode/system-prompt.js +600 -0
- package/dist/opencode/system-prompt.js.map +1 -0
- package/dist/opencode/tools.d.ts +8 -0
- package/dist/opencode/tools.d.ts.map +1 -0
- package/dist/opencode/tools.js +3731 -0
- package/dist/opencode/tools.js.map +1 -0
- package/dist/opencode/workflows/chains.d.ts +52 -0
- package/dist/opencode/workflows/chains.d.ts.map +1 -0
- package/dist/opencode/workflows/chains.js +170 -0
- package/dist/opencode/workflows/chains.js.map +1 -0
- package/dist/opencode/workflows/executor.d.ts +52 -0
- package/dist/opencode/workflows/executor.d.ts.map +1 -0
- package/dist/opencode/workflows/executor.js +130 -0
- package/dist/opencode/workflows/executor.js.map +1 -0
- package/dist/opencode/workflows/index.d.ts +14 -0
- package/dist/opencode/workflows/index.d.ts.map +1 -0
- package/dist/opencode/workflows/index.js +15 -0
- package/dist/opencode/workflows/index.js.map +1 -0
- package/dist/opencode/workflows/tools-workflow.d.ts +24 -0
- package/dist/opencode/workflows/tools-workflow.d.ts.map +1 -0
- package/dist/opencode/workflows/tools-workflow.js +85 -0
- package/dist/opencode/workflows/tools-workflow.js.map +1 -0
- package/dist/opencode/workflows/types.d.ts +32 -0
- package/dist/opencode/workflows/types.d.ts.map +1 -0
- package/dist/opencode/workflows/types.js +13 -0
- package/dist/opencode/workflows/types.js.map +1 -0
- package/dist/sdd/analysis/compliance.d.ts +30 -0
- package/dist/sdd/analysis/compliance.d.ts.map +1 -0
- package/dist/sdd/analysis/compliance.js +251 -0
- package/dist/sdd/analysis/compliance.js.map +1 -0
- package/dist/sdd/analysis/scalability.d.ts +15 -0
- package/dist/sdd/analysis/scalability.d.ts.map +1 -0
- package/dist/sdd/analysis/scalability.js +72 -0
- package/dist/sdd/analysis/scalability.js.map +1 -0
- package/dist/sdd/analysis/security.d.ts +25 -0
- package/dist/sdd/analysis/security.d.ts.map +1 -0
- package/dist/sdd/analysis/security.js +94 -0
- package/dist/sdd/analysis/security.js.map +1 -0
- package/dist/sdd/brownfield/scanner.d.ts +31 -0
- package/dist/sdd/brownfield/scanner.d.ts.map +1 -0
- package/dist/sdd/brownfield/scanner.js +228 -0
- package/dist/sdd/brownfield/scanner.js.map +1 -0
- package/dist/sdd/cache/manager.d.ts +237 -0
- package/dist/sdd/cache/manager.d.ts.map +1 -0
- package/dist/sdd/cache/manager.js +770 -0
- package/dist/sdd/cache/manager.js.map +1 -0
- package/dist/sdd/cache/snapshot-store.d.ts +39 -0
- package/dist/sdd/cache/snapshot-store.d.ts.map +1 -0
- package/dist/sdd/cache/snapshot-store.js +156 -0
- package/dist/sdd/cache/snapshot-store.js.map +1 -0
- package/dist/sdd/changes/manager.d.ts +58 -0
- package/dist/sdd/changes/manager.d.ts.map +1 -0
- package/dist/sdd/changes/manager.js +235 -0
- package/dist/sdd/changes/manager.js.map +1 -0
- package/dist/sdd/cicd/generators.d.ts +16 -0
- package/dist/sdd/cicd/generators.d.ts.map +1 -0
- package/dist/sdd/cicd/generators.js +1042 -0
- package/dist/sdd/cicd/generators.js.map +1 -0
- package/dist/sdd/code-quality/complexity.d.ts +38 -0
- package/dist/sdd/code-quality/complexity.d.ts.map +1 -0
- package/dist/sdd/code-quality/complexity.js +161 -0
- package/dist/sdd/code-quality/complexity.js.map +1 -0
- package/dist/sdd/code-quality/conventions.d.ts +33 -0
- package/dist/sdd/code-quality/conventions.d.ts.map +1 -0
- package/dist/sdd/code-quality/conventions.js +193 -0
- package/dist/sdd/code-quality/conventions.js.map +1 -0
- package/dist/sdd/code-quality/dependencies.d.ts +27 -0
- package/dist/sdd/code-quality/dependencies.d.ts.map +1 -0
- package/dist/sdd/code-quality/dependencies.js +142 -0
- package/dist/sdd/code-quality/dependencies.js.map +1 -0
- package/dist/sdd/code-quality/import-analyzer.d.ts +49 -0
- package/dist/sdd/code-quality/import-analyzer.d.ts.map +1 -0
- package/dist/sdd/code-quality/import-analyzer.js +213 -0
- package/dist/sdd/code-quality/import-analyzer.js.map +1 -0
- package/dist/sdd/code-quality/metrics.d.ts +35 -0
- package/dist/sdd/code-quality/metrics.d.ts.map +1 -0
- package/dist/sdd/code-quality/metrics.js +172 -0
- package/dist/sdd/code-quality/metrics.js.map +1 -0
- package/dist/sdd/code-quality/smells.d.ts +38 -0
- package/dist/sdd/code-quality/smells.d.ts.map +1 -0
- package/dist/sdd/code-quality/smells.js +319 -0
- package/dist/sdd/code-quality/smells.js.map +1 -0
- package/dist/sdd/code-quality/symbol-parser.d.ts +28 -0
- package/dist/sdd/code-quality/symbol-parser.d.ts.map +1 -0
- package/dist/sdd/code-quality/symbol-parser.js +230 -0
- package/dist/sdd/code-quality/symbol-parser.js.map +1 -0
- package/dist/sdd/code-quality/usage-tracker.d.ts +59 -0
- package/dist/sdd/code-quality/usage-tracker.d.ts.map +1 -0
- package/dist/sdd/code-quality/usage-tracker.js +402 -0
- package/dist/sdd/code-quality/usage-tracker.js.map +1 -0
- package/dist/sdd/code-quality/utils.d.ts +6 -0
- package/dist/sdd/code-quality/utils.d.ts.map +1 -0
- package/dist/sdd/code-quality/utils.js +14 -0
- package/dist/sdd/code-quality/utils.js.map +1 -0
- package/dist/sdd/codegen/generator.d.ts +42 -0
- package/dist/sdd/codegen/generator.d.ts.map +1 -0
- package/dist/sdd/codegen/generator.js +1023 -0
- package/dist/sdd/codegen/generator.js.map +1 -0
- package/dist/sdd/constitution/validator.d.ts +37 -0
- package/dist/sdd/constitution/validator.d.ts.map +1 -0
- package/dist/sdd/constitution/validator.js +192 -0
- package/dist/sdd/constitution/validator.js.map +1 -0
- package/dist/sdd/cost/estimator.d.ts +31 -0
- package/dist/sdd/cost/estimator.d.ts.map +1 -0
- package/dist/sdd/cost/estimator.js +92 -0
- package/dist/sdd/cost/estimator.js.map +1 -0
- package/dist/sdd/coverage/tracker.d.ts +47 -0
- package/dist/sdd/coverage/tracker.d.ts.map +1 -0
- package/dist/sdd/coverage/tracker.js +251 -0
- package/dist/sdd/coverage/tracker.js.map +1 -0
- package/dist/sdd/disaster/recovery.d.ts +11 -0
- package/dist/sdd/disaster/recovery.d.ts.map +1 -0
- package/dist/sdd/disaster/recovery.js +55 -0
- package/dist/sdd/disaster/recovery.js.map +1 -0
- package/dist/sdd/discovery/adaptive.d.ts +14 -0
- package/dist/sdd/discovery/adaptive.d.ts.map +1 -0
- package/dist/sdd/discovery/adaptive.js +145 -0
- package/dist/sdd/discovery/adaptive.js.map +1 -0
- package/dist/sdd/discovery/briefing-analyzer.d.ts +66 -0
- package/dist/sdd/discovery/briefing-analyzer.d.ts.map +1 -0
- package/dist/sdd/discovery/briefing-analyzer.js +672 -0
- package/dist/sdd/discovery/briefing-analyzer.js.map +1 -0
- package/dist/sdd/discovery/briefing.d.ts +109 -0
- package/dist/sdd/discovery/briefing.d.ts.map +1 -0
- package/dist/sdd/discovery/briefing.js +617 -0
- package/dist/sdd/discovery/briefing.js.map +1 -0
- package/dist/sdd/discovery/graph-builder.d.ts +10 -0
- package/dist/sdd/discovery/graph-builder.d.ts.map +1 -0
- package/dist/sdd/discovery/graph-builder.js +510 -0
- package/dist/sdd/discovery/graph-builder.js.map +1 -0
- package/dist/sdd/documentation/generator.d.ts +7 -0
- package/dist/sdd/documentation/generator.d.ts.map +1 -0
- package/dist/sdd/documentation/generator.js +119 -0
- package/dist/sdd/documentation/generator.js.map +1 -0
- package/dist/sdd/domain/types.d.ts +460 -0
- package/dist/sdd/domain/types.d.ts.map +1 -0
- package/dist/sdd/domain/types.js +25 -0
- package/dist/sdd/domain/types.js.map +1 -0
- package/dist/sdd/drift/detector.d.ts +28 -0
- package/dist/sdd/drift/detector.d.ts.map +1 -0
- package/dist/sdd/drift/detector.js +379 -0
- package/dist/sdd/drift/detector.js.map +1 -0
- package/dist/sdd/drift/exclusion.d.ts +54 -0
- package/dist/sdd/drift/exclusion.d.ts.map +1 -0
- package/dist/sdd/drift/exclusion.js +179 -0
- package/dist/sdd/drift/exclusion.js.map +1 -0
- package/dist/sdd/drift/signals.d.ts +52 -0
- package/dist/sdd/drift/signals.d.ts.map +1 -0
- package/dist/sdd/drift/signals.js +470 -0
- package/dist/sdd/drift/signals.js.map +1 -0
- package/dist/sdd/enforcement/interceptor.d.ts +47 -0
- package/dist/sdd/enforcement/interceptor.d.ts.map +1 -0
- package/dist/sdd/enforcement/interceptor.js +365 -0
- package/dist/sdd/enforcement/interceptor.js.map +1 -0
- package/dist/sdd/enforcement/workflow-tracker.d.ts +66 -0
- package/dist/sdd/enforcement/workflow-tracker.d.ts.map +1 -0
- package/dist/sdd/enforcement/workflow-tracker.js +229 -0
- package/dist/sdd/enforcement/workflow-tracker.js.map +1 -0
- package/dist/sdd/graph/engine.d.ts +44 -0
- package/dist/sdd/graph/engine.d.ts.map +1 -0
- package/dist/sdd/graph/engine.js +203 -0
- package/dist/sdd/graph/engine.js.map +1 -0
- package/dist/sdd/graph/index.d.ts +160 -0
- package/dist/sdd/graph/index.d.ts.map +1 -0
- package/dist/sdd/graph/index.js +624 -0
- package/dist/sdd/graph/index.js.map +1 -0
- package/dist/sdd/graph/integrity-guard.d.ts +129 -0
- package/dist/sdd/graph/integrity-guard.d.ts.map +1 -0
- package/dist/sdd/graph/integrity-guard.js +263 -0
- package/dist/sdd/graph/integrity-guard.js.map +1 -0
- package/dist/sdd/graph/integrity.d.ts +79 -0
- package/dist/sdd/graph/integrity.d.ts.map +1 -0
- package/dist/sdd/graph/integrity.js +462 -0
- package/dist/sdd/graph/integrity.js.map +1 -0
- package/dist/sdd/graph/pruner.d.ts +27 -0
- package/dist/sdd/graph/pruner.d.ts.map +1 -0
- package/dist/sdd/graph/pruner.js +203 -0
- package/dist/sdd/graph/pruner.js.map +1 -0
- package/dist/sdd/graph/traverse.d.ts +43 -0
- package/dist/sdd/graph/traverse.d.ts.map +1 -0
- package/dist/sdd/graph/traverse.js +265 -0
- package/dist/sdd/graph/traverse.js.map +1 -0
- package/dist/sdd/incidents/manager.d.ts +10 -0
- package/dist/sdd/incidents/manager.d.ts.map +1 -0
- package/dist/sdd/incidents/manager.js +63 -0
- package/dist/sdd/incidents/manager.js.map +1 -0
- package/dist/sdd/knowledge/transfer.d.ts +24 -0
- package/dist/sdd/knowledge/transfer.d.ts.map +1 -0
- package/dist/sdd/knowledge/transfer.js +94 -0
- package/dist/sdd/knowledge/transfer.js.map +1 -0
- package/dist/sdd/migrations/fixes.d.ts +2 -0
- package/dist/sdd/migrations/fixes.d.ts.map +1 -0
- package/dist/sdd/migrations/fixes.js +159 -0
- package/dist/sdd/migrations/fixes.js.map +1 -0
- package/dist/sdd/migrations/index.d.ts +2 -0
- package/dist/sdd/migrations/index.d.ts.map +1 -0
- package/dist/sdd/migrations/index.js +5 -0
- package/dist/sdd/migrations/index.js.map +1 -0
- package/dist/sdd/migrations/migration-runner.d.ts +41 -0
- package/dist/sdd/migrations/migration-runner.d.ts.map +1 -0
- package/dist/sdd/migrations/migration-runner.js +91 -0
- package/dist/sdd/migrations/migration-runner.js.map +1 -0
- package/dist/sdd/monitoring/setup.d.ts +40 -0
- package/dist/sdd/monitoring/setup.d.ts.map +1 -0
- package/dist/sdd/monitoring/setup.js +177 -0
- package/dist/sdd/monitoring/setup.js.map +1 -0
- package/dist/sdd/monitoring/telemetry.d.ts +18 -0
- package/dist/sdd/monitoring/telemetry.d.ts.map +1 -0
- package/dist/sdd/monitoring/telemetry.js +37 -0
- package/dist/sdd/monitoring/telemetry.js.map +1 -0
- package/dist/sdd/patterns/anti-patterns.d.ts +21 -0
- package/dist/sdd/patterns/anti-patterns.d.ts.map +1 -0
- package/dist/sdd/patterns/anti-patterns.js +286 -0
- package/dist/sdd/patterns/anti-patterns.js.map +1 -0
- package/dist/sdd/patterns/ast-clones.d.ts +23 -0
- package/dist/sdd/patterns/ast-clones.d.ts.map +1 -0
- package/dist/sdd/patterns/ast-clones.js +171 -0
- package/dist/sdd/patterns/ast-clones.js.map +1 -0
- package/dist/sdd/patterns/config-drift.d.ts +16 -0
- package/dist/sdd/patterns/config-drift.d.ts.map +1 -0
- package/dist/sdd/patterns/config-drift.js +322 -0
- package/dist/sdd/patterns/config-drift.js.map +1 -0
- package/dist/sdd/patterns/contradictions.d.ts +38 -0
- package/dist/sdd/patterns/contradictions.d.ts.map +1 -0
- package/dist/sdd/patterns/contradictions.js +237 -0
- package/dist/sdd/patterns/contradictions.js.map +1 -0
- package/dist/sdd/patterns/learner.d.ts +49 -0
- package/dist/sdd/patterns/learner.d.ts.map +1 -0
- package/dist/sdd/patterns/learner.js +226 -0
- package/dist/sdd/patterns/learner.js.map +1 -0
- package/dist/sdd/permissions/access.d.ts +69 -0
- package/dist/sdd/permissions/access.d.ts.map +1 -0
- package/dist/sdd/permissions/access.js +333 -0
- package/dist/sdd/permissions/access.js.map +1 -0
- package/dist/sdd/persistence/repository.d.ts +60 -0
- package/dist/sdd/persistence/repository.d.ts.map +1 -0
- package/dist/sdd/persistence/repository.js +96 -0
- package/dist/sdd/persistence/repository.js.map +1 -0
- package/dist/sdd/persistence/sqlite.d.ts +122 -0
- package/dist/sdd/persistence/sqlite.d.ts.map +1 -0
- package/dist/sdd/persistence/sqlite.js +602 -0
- package/dist/sdd/persistence/sqlite.js.map +1 -0
- package/dist/sdd/persistence/yaml.d.ts +41 -0
- package/dist/sdd/persistence/yaml.d.ts.map +1 -0
- package/dist/sdd/persistence/yaml.js +382 -0
- package/dist/sdd/persistence/yaml.js.map +1 -0
- package/dist/sdd/promises/classifier.d.ts +48 -0
- package/dist/sdd/promises/classifier.d.ts.map +1 -0
- package/dist/sdd/promises/classifier.js +215 -0
- package/dist/sdd/promises/classifier.js.map +1 -0
- package/dist/sdd/promises/tracker.d.ts +20 -0
- package/dist/sdd/promises/tracker.d.ts.map +1 -0
- package/dist/sdd/promises/tracker.js +204 -0
- package/dist/sdd/promises/tracker.js.map +1 -0
- package/dist/sdd/quality/scorer.d.ts +44 -0
- package/dist/sdd/quality/scorer.d.ts.map +1 -0
- package/dist/sdd/quality/scorer.js +216 -0
- package/dist/sdd/quality/scorer.js.map +1 -0
- package/dist/sdd/rollback/manager.d.ts +45 -0
- package/dist/sdd/rollback/manager.d.ts.map +1 -0
- package/dist/sdd/rollback/manager.js +266 -0
- package/dist/sdd/rollback/manager.js.map +1 -0
- package/dist/sdd/session/handoff.d.ts +52 -0
- package/dist/sdd/session/handoff.d.ts.map +1 -0
- package/dist/sdd/session/handoff.js +264 -0
- package/dist/sdd/session/handoff.js.map +1 -0
- package/dist/sdd/sla/tracker.d.ts +11 -0
- package/dist/sdd/sla/tracker.d.ts.map +1 -0
- package/dist/sdd/sla/tracker.js +52 -0
- package/dist/sdd/sla/tracker.js.map +1 -0
- package/dist/sdd/sync/git-sync.d.ts +61 -0
- package/dist/sdd/sync/git-sync.d.ts.map +1 -0
- package/dist/sdd/sync/git-sync.js +297 -0
- package/dist/sdd/sync/git-sync.js.map +1 -0
- package/dist/sdd/toggle/state.d.ts +8 -0
- package/dist/sdd/toggle/state.d.ts.map +1 -0
- package/dist/sdd/toggle/state.js +32 -0
- package/dist/sdd/toggle/state.js.map +1 -0
- package/dist/sdd/transactions/manager.d.ts +12 -0
- package/dist/sdd/transactions/manager.d.ts.map +1 -0
- package/dist/sdd/transactions/manager.js +80 -0
- package/dist/sdd/transactions/manager.js.map +1 -0
- package/dist/sdd/validation/coverage-index.d.ts +117 -0
- package/dist/sdd/validation/coverage-index.d.ts.map +1 -0
- package/dist/sdd/validation/coverage-index.js +251 -0
- package/dist/sdd/validation/coverage-index.js.map +1 -0
- package/dist/sdd/validation/executable.d.ts +26 -0
- package/dist/sdd/validation/executable.d.ts.map +1 -0
- package/dist/sdd/validation/executable.js +122 -0
- package/dist/sdd/validation/executable.js.map +1 -0
- package/dist/sdd/validation/smart-validator.d.ts +53 -0
- package/dist/sdd/validation/smart-validator.d.ts.map +1 -0
- package/dist/sdd/validation/smart-validator.js +347 -0
- package/dist/sdd/validation/smart-validator.js.map +1 -0
- package/dist/sdd/validation/validator.d.ts +39 -0
- package/dist/sdd/validation/validator.d.ts.map +1 -0
- package/dist/sdd/validation/validator.js +512 -0
- package/dist/sdd/validation/validator.js.map +1 -0
- package/dist/sdd/workflow/exporter.d.ts +36 -0
- package/dist/sdd/workflow/exporter.d.ts.map +1 -0
- package/dist/sdd/workflow/exporter.js +112 -0
- package/dist/sdd/workflow/exporter.js.map +1 -0
- package/dist/sdd/workflows/ab-testing.d.ts +18 -0
- package/dist/sdd/workflows/ab-testing.d.ts.map +1 -0
- package/dist/sdd/workflows/ab-testing.js +67 -0
- package/dist/sdd/workflows/ab-testing.js.map +1 -0
- package/dist/sdd/workflows/bug-fix.d.ts +13 -0
- package/dist/sdd/workflows/bug-fix.d.ts.map +1 -0
- package/dist/sdd/workflows/bug-fix.js +65 -0
- package/dist/sdd/workflows/bug-fix.js.map +1 -0
- package/dist/sdd/workflows/data-migration.d.ts +14 -0
- package/dist/sdd/workflows/data-migration.d.ts.map +1 -0
- package/dist/sdd/workflows/data-migration.js +68 -0
- package/dist/sdd/workflows/data-migration.js.map +1 -0
- package/dist/sdd/workflows/deprecation.d.ts +15 -0
- package/dist/sdd/workflows/deprecation.d.ts.map +1 -0
- package/dist/sdd/workflows/deprecation.js +68 -0
- package/dist/sdd/workflows/deprecation.js.map +1 -0
- package/dist/sdd/workflows/feature-flags.d.ts +14 -0
- package/dist/sdd/workflows/feature-flags.d.ts.map +1 -0
- package/dist/sdd/workflows/feature-flags.js +73 -0
- package/dist/sdd/workflows/feature-flags.js.map +1 -0
- package/dist/sdd/workflows/hotfix.d.ts +13 -0
- package/dist/sdd/workflows/hotfix.d.ts.map +1 -0
- package/dist/sdd/workflows/hotfix.js +67 -0
- package/dist/sdd/workflows/hotfix.js.map +1 -0
- package/dist/sdd/workflows/multi-tenancy.d.ts +13 -0
- package/dist/sdd/workflows/multi-tenancy.d.ts.map +1 -0
- package/dist/sdd/workflows/multi-tenancy.js +68 -0
- package/dist/sdd/workflows/multi-tenancy.js.map +1 -0
- package/dist/sdd/workflows/onboarding.d.ts +7 -0
- package/dist/sdd/workflows/onboarding.d.ts.map +1 -0
- package/dist/sdd/workflows/onboarding.js +43 -0
- package/dist/sdd/workflows/onboarding.js.map +1 -0
- package/dist/sdd/workflows/refactoring.d.ts +14 -0
- package/dist/sdd/workflows/refactoring.d.ts.map +1 -0
- package/dist/sdd/workflows/refactoring.js +73 -0
- package/dist/sdd/workflows/refactoring.js.map +1 -0
- package/dist/server/events.d.ts +64 -0
- package/dist/server/events.d.ts.map +1 -0
- package/dist/server/events.js +149 -0
- package/dist/server/events.js.map +1 -0
- package/dist/server/server.d.ts +33 -0
- package/dist/server/server.d.ts.map +1 -0
- package/dist/server/server.js +1019 -0
- package/dist/server/server.js.map +1 -0
- package/marketplace.json +39 -0
- package/package.json +65 -0
- package/src/code-intelligence/analyzer.ts +350 -0
- package/src/code-intelligence/ast/cache.ts +61 -0
- package/src/code-intelligence/ast/common.ts +62 -0
- package/src/code-intelligence/ast/component.ts +39 -0
- package/src/code-intelligence/ast/fallback.ts +44 -0
- package/src/code-intelligence/ast/index.ts +4 -0
- package/src/code-intelligence/ast/ir.ts +91 -0
- package/src/code-intelligence/ast/metrics.ts +65 -0
- package/src/code-intelligence/ast/registry.ts +28 -0
- package/src/code-intelligence/ast/tree-sitter.ts +226 -0
- package/src/code-intelligence/ast/typescript.ts +140 -0
- package/src/index.ts +22 -0
- package/src/mcp/server.ts +77 -0
- package/src/opencode/hooks.ts +728 -0
- package/src/opencode/router/categories.ts +192 -0
- package/src/opencode/router/embeddings.ts +149 -0
- package/src/opencode/router/graph-state-snapshot.ts +163 -0
- package/src/opencode/router/index.ts +44 -0
- package/src/opencode/router/intent-classifier.ts +140 -0
- package/src/opencode/router/semantic-nudge.ts +89 -0
- package/src/opencode/router/state-gate.ts +192 -0
- package/src/opencode/router/tool-embeddings.ts +162 -0
- package/src/opencode/router/tool-registry.ts +135 -0
- package/src/opencode/router/tool-taxonomy.ts +284 -0
- package/src/opencode/router/tools-composite.ts +685 -0
- package/src/opencode/shell-hooks.ts +115 -0
- package/src/opencode/system-prompt.ts +617 -0
- package/src/opencode/tools.ts +4102 -0
- package/src/opencode/workflows/chains.ts +205 -0
- package/src/opencode/workflows/executor.ts +172 -0
- package/src/opencode/workflows/index.ts +21 -0
- package/src/opencode/workflows/tools-workflow.ts +91 -0
- package/src/opencode/workflows/types.ts +42 -0
- package/src/sdd/analysis/compliance.ts +279 -0
- package/src/sdd/analysis/scalability.ts +97 -0
- package/src/sdd/analysis/security.ts +135 -0
- package/src/sdd/brownfield/scanner.ts +276 -0
- package/src/sdd/cache/manager.ts +899 -0
- package/src/sdd/cache/snapshot-store.ts +185 -0
- package/src/sdd/changes/manager.ts +326 -0
- package/src/sdd/cicd/generators.ts +1095 -0
- package/src/sdd/code-quality/complexity.ts +235 -0
- package/src/sdd/code-quality/conventions.ts +220 -0
- package/src/sdd/code-quality/dependencies.ts +203 -0
- package/src/sdd/code-quality/import-analyzer.ts +294 -0
- package/src/sdd/code-quality/metrics.ts +232 -0
- package/src/sdd/code-quality/smells.ts +415 -0
- package/src/sdd/code-quality/symbol-parser.ts +335 -0
- package/src/sdd/code-quality/usage-tracker.ts +527 -0
- package/src/sdd/code-quality/utils.ts +13 -0
- package/src/sdd/codegen/generator.ts +1181 -0
- package/src/sdd/constitution/validator.ts +253 -0
- package/src/sdd/cost/estimator.ts +147 -0
- package/src/sdd/coverage/tracker.ts +342 -0
- package/src/sdd/disaster/recovery.ts +79 -0
- package/src/sdd/discovery/adaptive.ts +167 -0
- package/src/sdd/discovery/briefing-analyzer.ts +802 -0
- package/src/sdd/discovery/briefing.ts +770 -0
- package/src/sdd/discovery/graph-builder.ts +598 -0
- package/src/sdd/documentation/generator.ts +158 -0
- package/src/sdd/domain/types.ts +658 -0
- package/src/sdd/drift/detector.ts +465 -0
- package/src/sdd/drift/exclusion.ts +223 -0
- package/src/sdd/drift/signals.ts +577 -0
- package/src/sdd/enforcement/interceptor.ts +444 -0
- package/src/sdd/enforcement/workflow-tracker.ts +263 -0
- package/src/sdd/graph/engine.ts +277 -0
- package/src/sdd/graph/index.ts +700 -0
- package/src/sdd/graph/integrity-guard.ts +348 -0
- package/src/sdd/graph/integrity.ts +628 -0
- package/src/sdd/graph/pruner.ts +221 -0
- package/src/sdd/graph/traverse.ts +331 -0
- package/src/sdd/incidents/manager.ts +87 -0
- package/src/sdd/knowledge/transfer.ts +140 -0
- package/src/sdd/migrations/fixes.ts +154 -0
- package/src/sdd/migrations/index.ts +14 -0
- package/src/sdd/migrations/migration-runner.ts +118 -0
- package/src/sdd/monitoring/setup.ts +237 -0
- package/src/sdd/monitoring/telemetry.ts +44 -0
- package/src/sdd/patterns/anti-patterns.ts +333 -0
- package/src/sdd/patterns/ast-clones.ts +214 -0
- package/src/sdd/patterns/config-drift.ts +346 -0
- package/src/sdd/patterns/contradictions.ts +323 -0
- package/src/sdd/patterns/learner.ts +266 -0
- package/src/sdd/permissions/access.ts +478 -0
- package/src/sdd/persistence/repository.ts +166 -0
- package/src/sdd/persistence/sqlite.ts +703 -0
- package/src/sdd/persistence/yaml.ts +409 -0
- package/src/sdd/promises/classifier.ts +253 -0
- package/src/sdd/promises/tracker.ts +258 -0
- package/src/sdd/quality/scorer.ts +263 -0
- package/src/sdd/rollback/manager.ts +361 -0
- package/src/sdd/session/handoff.ts +354 -0
- package/src/sdd/sla/tracker.ts +68 -0
- package/src/sdd/sync/git-sync.ts +401 -0
- package/src/sdd/toggle/state.ts +38 -0
- package/src/sdd/transactions/manager.ts +93 -0
- package/src/sdd/validation/coverage-index.ts +339 -0
- package/src/sdd/validation/executable.ts +127 -0
- package/src/sdd/validation/smart-validator.ts +470 -0
- package/src/sdd/validation/validator.ts +641 -0
- package/src/sdd/workflow/exporter.ts +169 -0
- package/src/sdd/workflows/ab-testing.ts +90 -0
- package/src/sdd/workflows/bug-fix.ts +81 -0
- package/src/sdd/workflows/data-migration.ts +86 -0
- package/src/sdd/workflows/deprecation.ts +87 -0
- package/src/sdd/workflows/feature-flags.ts +91 -0
- package/src/sdd/workflows/hotfix.ts +83 -0
- package/src/sdd/workflows/multi-tenancy.ts +84 -0
- package/src/sdd/workflows/onboarding.ts +82 -0
- package/src/sdd/workflows/refactoring.ts +90 -0
- package/src/server/events.ts +184 -0
- package/src/server/server.ts +1088 -0
|
@@ -0,0 +1,899 @@
|
|
|
1
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync, unlinkSync } from "fs"
|
|
2
|
+
import { join, dirname } from "path"
|
|
3
|
+
import { createHash } from "crypto"
|
|
4
|
+
import { PerTypeGraphCache, IncrementalGraphHash } from "../graph/index.js"
|
|
5
|
+
import type { AnyNode, NodeType, Relationship } from "../domain/types.js"
|
|
6
|
+
import { getGraphSnapshotStore } from "./snapshot-store.js"
|
|
7
|
+
import type { KnowledgeGraph } from "../domain/types.js"
|
|
8
|
+
|
|
9
|
+
// ── Cache Entry Types ────────────────────────────────────────────────
|
|
10
|
+
|
|
11
|
+
interface CacheEntry<T> {
|
|
12
|
+
value: T
|
|
13
|
+
timestamp: number
|
|
14
|
+
version: number
|
|
15
|
+
accessCount: number
|
|
16
|
+
lastAccess: number
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
interface ToolCacheEntry {
|
|
20
|
+
response: string
|
|
21
|
+
timestamp: number
|
|
22
|
+
lastAccess: number
|
|
23
|
+
toolName: string
|
|
24
|
+
argsHash: string
|
|
25
|
+
graphVersion: string
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
interface AnalysisCacheEntry {
|
|
29
|
+
result: unknown
|
|
30
|
+
timestamp: number
|
|
31
|
+
lastAccess: number
|
|
32
|
+
graphVersion: number
|
|
33
|
+
nodeCount: number
|
|
34
|
+
type: string
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
interface PersistentCacheData {
|
|
38
|
+
version: number
|
|
39
|
+
toolResponses: Record<string, ToolCacheEntry>
|
|
40
|
+
analysisResults: Record<string, AnalysisCacheEntry>
|
|
41
|
+
graphSnapshot: {
|
|
42
|
+
nodeCount: number
|
|
43
|
+
relationshipCount: number
|
|
44
|
+
version: string
|
|
45
|
+
lastModified: number
|
|
46
|
+
} | null
|
|
47
|
+
graphHash: string
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// ── Granular Invalidation Tracking ───────────────────────────────────
|
|
51
|
+
|
|
52
|
+
interface InvalidationTracker {
|
|
53
|
+
lastFullInvalidation: number
|
|
54
|
+
dirtyTypes: Set<string>
|
|
55
|
+
dirtyNodeIds: Set<string>
|
|
56
|
+
dirtyRelTypes: Set<string>
|
|
57
|
+
version: number
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// ── Main Cache Manager ───────────────────────────────────────────────
|
|
61
|
+
|
|
62
|
+
const TOOL_RESPONSE_TTL = 5 * 60 * 1000 // 5 minutes
|
|
63
|
+
const ANALYSIS_TTL = 3 * 60 * 1000 // 3 minutes
|
|
64
|
+
const PERSISTENT_CACHE_FILE = ".sdd/cache.json"
|
|
65
|
+
const INVALIDATION_FILE = ".sdd/cache-invalidated.json"
|
|
66
|
+
|
|
67
|
+
export class CacheManager {
|
|
68
|
+
private projectDir: string
|
|
69
|
+
|
|
70
|
+
// In-memory caches
|
|
71
|
+
private toolResponses: Map<string, ToolCacheEntry> = new Map()
|
|
72
|
+
private analysisResults: Map<string, AnalysisCacheEntry> = new Map()
|
|
73
|
+
|
|
74
|
+
// Granular invalidation tracker
|
|
75
|
+
private invalidation: InvalidationTracker = {
|
|
76
|
+
lastFullInvalidation: 0,
|
|
77
|
+
dirtyTypes: new Set(),
|
|
78
|
+
dirtyNodeIds: new Set(),
|
|
79
|
+
dirtyRelTypes: new Set(),
|
|
80
|
+
version: 0,
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Version counter for lazy revalidation (F)
|
|
84
|
+
private invalidationVersionOnWrite: number = 0
|
|
85
|
+
|
|
86
|
+
// Per-type graph cache
|
|
87
|
+
private graphCache = new PerTypeGraphCache()
|
|
88
|
+
|
|
89
|
+
// Incremental graph hash
|
|
90
|
+
private graphHash = new IncrementalGraphHash()
|
|
91
|
+
|
|
92
|
+
// Statistics
|
|
93
|
+
private stats = {
|
|
94
|
+
toolHits: 0,
|
|
95
|
+
toolMisses: 0,
|
|
96
|
+
analysisHits: 0,
|
|
97
|
+
analysisMisses: 0,
|
|
98
|
+
invalidations: 0,
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
constructor(projectDir: string) {
|
|
102
|
+
this.projectDir = projectDir
|
|
103
|
+
this.loadInvalidationTracker()
|
|
104
|
+
this.invalidationVersionOnWrite = this.invalidation.version
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// ── Tool Response Cache ───────────────────────────────────────────
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Get cached tool response if valid.
|
|
111
|
+
* Checks: TTL (using lastAccess for freshness), graph version, invalidation status.
|
|
112
|
+
* F: Uses lazy revalidation — checks version before clearing.
|
|
113
|
+
*/
|
|
114
|
+
getToolResponse(toolName: string, args: Record<string, unknown>, graphVersion: string): string | null {
|
|
115
|
+
const key = this.toolCacheKey(toolName, args)
|
|
116
|
+
const entry = this.toolResponses.get(key)
|
|
117
|
+
|
|
118
|
+
if (!entry) {
|
|
119
|
+
this.stats.toolMisses++
|
|
120
|
+
return null
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// F: Lazy revalidation — if invalidation version changed, check if this entry is affected
|
|
124
|
+
if (this.invalidation.version !== this.invalidationVersionOnWrite) {
|
|
125
|
+
if (this.isToolAffectedByInvalidation(toolName, entry.argsHash)) {
|
|
126
|
+
this.toolResponses.delete(key)
|
|
127
|
+
this.clearDirtyTypesForTool(toolName)
|
|
128
|
+
this.stats.toolMisses++
|
|
129
|
+
return null
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// B: Check TTL using lastAccess (not timestamp) — entries accessed recently survive restore
|
|
134
|
+
const effectiveAge = Date.now() - (entry.lastAccess || entry.timestamp)
|
|
135
|
+
if (effectiveAge > TOOL_RESPONSE_TTL) {
|
|
136
|
+
this.toolResponses.delete(key)
|
|
137
|
+
this.stats.toolMisses++
|
|
138
|
+
return null
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Check graph version consistency
|
|
142
|
+
if (entry.graphVersion !== graphVersion) {
|
|
143
|
+
this.toolResponses.delete(key)
|
|
144
|
+
this.stats.toolMisses++
|
|
145
|
+
return null
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Update access stats — refresh lastAccess on hit
|
|
149
|
+
entry.lastAccess = Date.now()
|
|
150
|
+
this.stats.toolHits++
|
|
151
|
+
return entry.response
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Cache a tool response.
|
|
156
|
+
*/
|
|
157
|
+
setToolResponse(toolName: string, args: Record<string, unknown>, response: string, graphVersion: string): void {
|
|
158
|
+
const key = this.toolCacheKey(toolName, args)
|
|
159
|
+
const now = Date.now()
|
|
160
|
+
this.toolResponses.set(key, {
|
|
161
|
+
response,
|
|
162
|
+
timestamp: now,
|
|
163
|
+
lastAccess: now,
|
|
164
|
+
toolName,
|
|
165
|
+
argsHash: this.toolCacheKey(toolName, args).split(':')[1] || '',
|
|
166
|
+
graphVersion,
|
|
167
|
+
})
|
|
168
|
+
|
|
169
|
+
// Evict old entries if cache is too large
|
|
170
|
+
if (this.toolResponses.size > 200) {
|
|
171
|
+
this.evictOldestToolEntries(50)
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// ── Analysis Result Cache ─────────────────────────────────────────
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Get cached analysis result (validate, drift, quality, etc.).
|
|
179
|
+
* Revalidates based on graph state.
|
|
180
|
+
* F: Uses lazy revalidation.
|
|
181
|
+
*/
|
|
182
|
+
getAnalysisResult(type: string, graphVersion: number, nodeCount: number): unknown | null {
|
|
183
|
+
const key = `analysis:${type}`
|
|
184
|
+
const entry = this.analysisResults.get(key)
|
|
185
|
+
|
|
186
|
+
if (!entry) {
|
|
187
|
+
this.stats.analysisMisses++
|
|
188
|
+
return null
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// F: Lazy revalidation
|
|
192
|
+
if (this.invalidation.version !== this.invalidationVersionOnWrite) {
|
|
193
|
+
if (this.isAnalysisAffectedByInvalidation(type)) {
|
|
194
|
+
this.analysisResults.delete(key)
|
|
195
|
+
this.clearDirtyTypesForAnalysis(type)
|
|
196
|
+
this.stats.analysisMisses++
|
|
197
|
+
return null
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// B: Check TTL using lastAccess
|
|
202
|
+
const effectiveAge = Date.now() - (entry.lastAccess || entry.timestamp)
|
|
203
|
+
if (effectiveAge > ANALYSIS_TTL) {
|
|
204
|
+
this.analysisResults.delete(key)
|
|
205
|
+
this.stats.analysisMisses++
|
|
206
|
+
return null
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// Revalidation: if graph changed since analysis, invalidate
|
|
210
|
+
if (entry.graphVersion !== graphVersion || entry.nodeCount !== nodeCount) {
|
|
211
|
+
this.analysisResults.delete(key)
|
|
212
|
+
this.stats.analysisMisses++
|
|
213
|
+
return null
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// Update lastAccess
|
|
217
|
+
entry.lastAccess = Date.now()
|
|
218
|
+
this.stats.analysisHits++
|
|
219
|
+
return entry.result
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Cache an analysis result.
|
|
224
|
+
*/
|
|
225
|
+
setAnalysisResult(type: string, result: unknown, graphVersion: number, nodeCount: number): void {
|
|
226
|
+
const key = `analysis:${type}`
|
|
227
|
+
const now = Date.now()
|
|
228
|
+
this.analysisResults.set(key, {
|
|
229
|
+
result,
|
|
230
|
+
timestamp: now,
|
|
231
|
+
lastAccess: now,
|
|
232
|
+
graphVersion,
|
|
233
|
+
nodeCount,
|
|
234
|
+
type,
|
|
235
|
+
})
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// ── Granular Invalidation ─────────────────────────────────────────
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Mark specific node IDs as dirty.
|
|
242
|
+
*/
|
|
243
|
+
invalidateNodeIds(ids: string[]): void {
|
|
244
|
+
for (const id of ids) {
|
|
245
|
+
this.invalidation.dirtyNodeIds.add(id)
|
|
246
|
+
}
|
|
247
|
+
this.invalidation.version++
|
|
248
|
+
this.saveInvalidationTracker()
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Mark specific relationship types as dirty.
|
|
253
|
+
*/
|
|
254
|
+
invalidateRelTypes(types: string[]): void {
|
|
255
|
+
for (const type of types) {
|
|
256
|
+
this.invalidation.dirtyRelTypes.add(type)
|
|
257
|
+
}
|
|
258
|
+
this.invalidation.version++
|
|
259
|
+
this.saveInvalidationTracker()
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Full invalidation: clear everything.
|
|
264
|
+
* Now reserved only for explicit reset (H) or major structural changes.
|
|
265
|
+
*/
|
|
266
|
+
invalidateAll(): void {
|
|
267
|
+
this.toolResponses.clear()
|
|
268
|
+
this.analysisResults.clear()
|
|
269
|
+
this.graphCache.invalidateAll()
|
|
270
|
+
this.invalidation.lastFullInvalidation = Date.now()
|
|
271
|
+
this.invalidation.dirtyTypes.clear()
|
|
272
|
+
this.invalidation.dirtyNodeIds.clear()
|
|
273
|
+
this.invalidation.dirtyRelTypes.clear()
|
|
274
|
+
this.invalidation.version++
|
|
275
|
+
this.stats.invalidations++
|
|
276
|
+
this.saveInvalidationTracker()
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* Partial invalidation: clear only affected caches.
|
|
281
|
+
* A: Much faster than full invalidation for targeted changes.
|
|
282
|
+
* Called by repositories after saveGraph() with the dirty types.
|
|
283
|
+
*/
|
|
284
|
+
invalidatePartial(changedNodeTypes: string[], changedRelTypes: string[] = []): void {
|
|
285
|
+
// Invalidate tool responses that depend on changed types
|
|
286
|
+
for (const [key, entry] of this.toolResponses) {
|
|
287
|
+
if (this.doesToolDependOnTypes(key, changedNodeTypes, changedRelTypes)) {
|
|
288
|
+
this.toolResponses.delete(key)
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// Invalidate analysis results that depend on changed types
|
|
293
|
+
for (const [key, entry] of this.analysisResults) {
|
|
294
|
+
if (this.doesAnalysisDependOnTypes(key, changedNodeTypes, changedRelTypes)) {
|
|
295
|
+
this.analysisResults.delete(key)
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
// Track dirty types
|
|
300
|
+
this.invalidateNodeTypes(changedNodeTypes)
|
|
301
|
+
this.invalidateRelTypes(changedRelTypes)
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* Invalidate specific node IDs (e.g., when a node is updated).
|
|
306
|
+
*/
|
|
307
|
+
invalidateNodeId(nodeId: string, nodeType: string): void {
|
|
308
|
+
// Invalidate tool responses that mention this node
|
|
309
|
+
for (const [key, entry] of this.toolResponses) {
|
|
310
|
+
if (entry.response.includes(nodeId)) {
|
|
311
|
+
this.toolResponses.delete(key)
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
// Invalidate analysis results for this node type
|
|
316
|
+
const analysisTypes = this.getAnalysisTypesForNodeType(nodeType)
|
|
317
|
+
for (const type of analysisTypes) {
|
|
318
|
+
this.analysisResults.delete(`analysis:${type}`)
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
this.invalidation.dirtyNodeIds.add(nodeId)
|
|
322
|
+
this.invalidation.dirtyTypes.add(nodeType)
|
|
323
|
+
this.invalidation.version++
|
|
324
|
+
this.saveInvalidationTracker()
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
// ── Persistent Cache (Cross-Session) ──────────────────────────────
|
|
328
|
+
|
|
329
|
+
/**
|
|
330
|
+
* Load persistent cache from disk.
|
|
331
|
+
*/
|
|
332
|
+
loadPersistentCache(): PersistentCacheData | null {
|
|
333
|
+
const path = join(this.projectDir, PERSISTENT_CACHE_FILE)
|
|
334
|
+
if (!existsSync(path)) return null
|
|
335
|
+
try {
|
|
336
|
+
const data = JSON.parse(readFileSync(path, "utf-8"))
|
|
337
|
+
// Check if persistent cache is too old (>1 hour)
|
|
338
|
+
if (Date.now() - (data.timestamp || 0) > 60 * 60 * 1000) {
|
|
339
|
+
return null
|
|
340
|
+
}
|
|
341
|
+
return data
|
|
342
|
+
} catch {
|
|
343
|
+
return null
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
/**
|
|
348
|
+
* Save persistent cache to disk.
|
|
349
|
+
*/
|
|
350
|
+
savePersistentCache(data: PersistentCacheData): void {
|
|
351
|
+
const path = join(this.projectDir, PERSISTENT_CACHE_FILE)
|
|
352
|
+
const dir = dirname(path)
|
|
353
|
+
if (!existsSync(dir)) mkdirSync(dir, { recursive: true })
|
|
354
|
+
writeFileSync(path, JSON.stringify({ ...data, timestamp: Date.now() }, null, 2))
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
/**
|
|
358
|
+
* Restore tool responses from persistent cache.
|
|
359
|
+
* B: Uses lastAccess for TTL check so recently-accessed entries survive restore.
|
|
360
|
+
*/
|
|
361
|
+
restoreFromPersistentCache(): number {
|
|
362
|
+
const persistent = this.loadPersistentCache()
|
|
363
|
+
if (!persistent) return 0
|
|
364
|
+
|
|
365
|
+
let restored = 0
|
|
366
|
+
for (const [key, entry] of Object.entries(persistent.toolResponses)) {
|
|
367
|
+
// B: Use lastAccess for freshness check — entries accessed recently are valid
|
|
368
|
+
const effectiveAge = Date.now() - (entry.lastAccess || entry.timestamp)
|
|
369
|
+
if (effectiveAge < TOOL_RESPONSE_TTL) {
|
|
370
|
+
this.toolResponses.set(key, {
|
|
371
|
+
response: entry.response,
|
|
372
|
+
timestamp: entry.timestamp,
|
|
373
|
+
lastAccess: entry.lastAccess || entry.timestamp,
|
|
374
|
+
toolName: entry.toolName,
|
|
375
|
+
argsHash: entry.argsHash,
|
|
376
|
+
graphVersion: entry.graphVersion,
|
|
377
|
+
})
|
|
378
|
+
restored++
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
for (const [key, entry] of Object.entries(persistent.analysisResults)) {
|
|
383
|
+
const effectiveAge = Date.now() - (entry.lastAccess || entry.timestamp)
|
|
384
|
+
if (effectiveAge < ANALYSIS_TTL) {
|
|
385
|
+
this.analysisResults.set(key, {
|
|
386
|
+
result: entry.result,
|
|
387
|
+
timestamp: entry.timestamp,
|
|
388
|
+
lastAccess: entry.lastAccess || entry.timestamp,
|
|
389
|
+
graphVersion: entry.graphVersion,
|
|
390
|
+
nodeCount: entry.nodeCount,
|
|
391
|
+
type: key.replace("analysis:", ""),
|
|
392
|
+
})
|
|
393
|
+
restored++
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
return restored
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
/**
|
|
401
|
+
* Persist current cache to disk.
|
|
402
|
+
*/
|
|
403
|
+
persistToDisk(): void {
|
|
404
|
+
const toolResponses: Record<string, ToolCacheEntry> = {}
|
|
405
|
+
for (const [key, entry] of this.toolResponses) {
|
|
406
|
+
toolResponses[key] = {
|
|
407
|
+
response: entry.response,
|
|
408
|
+
timestamp: entry.timestamp,
|
|
409
|
+
lastAccess: entry.lastAccess,
|
|
410
|
+
toolName: entry.toolName,
|
|
411
|
+
argsHash: entry.argsHash,
|
|
412
|
+
graphVersion: entry.graphVersion,
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
const analysisResults: Record<string, AnalysisCacheEntry> = {}
|
|
417
|
+
for (const [key, entry] of this.analysisResults) {
|
|
418
|
+
analysisResults[key] = {
|
|
419
|
+
result: entry.result,
|
|
420
|
+
timestamp: entry.timestamp,
|
|
421
|
+
lastAccess: entry.lastAccess,
|
|
422
|
+
graphVersion: entry.graphVersion,
|
|
423
|
+
nodeCount: entry.nodeCount,
|
|
424
|
+
type: key.replace("analysis:", ""),
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
this.savePersistentCache({
|
|
429
|
+
version: 2,
|
|
430
|
+
toolResponses,
|
|
431
|
+
analysisResults,
|
|
432
|
+
graphSnapshot: null,
|
|
433
|
+
graphHash: this.graphHash.getHash(),
|
|
434
|
+
})
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
/**
|
|
438
|
+
* Save graph snapshot to disk (G).
|
|
439
|
+
* Called after graph mutations and on dispose.
|
|
440
|
+
*/
|
|
441
|
+
saveGraphSnapshot(graph: KnowledgeGraph): void {
|
|
442
|
+
try {
|
|
443
|
+
const store = getGraphSnapshotStore(this.projectDir)
|
|
444
|
+
store.save(graph, this.graphHash.getHash())
|
|
445
|
+
} catch {}
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
/**
|
|
449
|
+
* Load graph snapshot from disk (G).
|
|
450
|
+
* Returns null if no valid snapshot exists.
|
|
451
|
+
*/
|
|
452
|
+
loadGraphSnapshot(): { graph: KnowledgeGraph; graphHash: string } | null {
|
|
453
|
+
try {
|
|
454
|
+
const store = getGraphSnapshotStore(this.projectDir)
|
|
455
|
+
return store.load()
|
|
456
|
+
} catch {
|
|
457
|
+
return null
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
/**
|
|
462
|
+
* Invalidate graph snapshot on disk.
|
|
463
|
+
*/
|
|
464
|
+
invalidateGraphSnapshot(): void {
|
|
465
|
+
try {
|
|
466
|
+
const store = getGraphSnapshotStore(this.projectDir)
|
|
467
|
+
store.invalidate()
|
|
468
|
+
} catch {}
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
// ── Cross-Process Shared Cache ────────────────────────────────────
|
|
472
|
+
|
|
473
|
+
/**
|
|
474
|
+
* D: Check if another process has modified the graph since our last read.
|
|
475
|
+
* Uses file-based locking + PID liveness check for robust cross-process coordination.
|
|
476
|
+
*/
|
|
477
|
+
checkCrossProcessInvalidation(graphPath: string): boolean {
|
|
478
|
+
const lockPath = join(this.projectDir, ".sdd", ".cache-lock")
|
|
479
|
+
try {
|
|
480
|
+
if (existsSync(lockPath)) {
|
|
481
|
+
const lockData = JSON.parse(readFileSync(lockPath, "utf-8"))
|
|
482
|
+
|
|
483
|
+
// D: If lock is older than 30 seconds, check if the process is still alive
|
|
484
|
+
if (Date.now() - lockData.timestamp > 30000) {
|
|
485
|
+
// D: If the locking process is dead, treat as invalidation needed
|
|
486
|
+
if (lockData.pid && lockData.pid !== process.pid) {
|
|
487
|
+
try {
|
|
488
|
+
process.kill(lockData.pid, 0) // Signal 0 = check if alive
|
|
489
|
+
// Process is alive but lock is stale — another process is slow, ignore
|
|
490
|
+
return false
|
|
491
|
+
} catch {
|
|
492
|
+
// Process is dead — force invalidation
|
|
493
|
+
this.invalidation.lastFullInvalidation = Date.now()
|
|
494
|
+
this.invalidation.version++
|
|
495
|
+
return true
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
return false // Stale lock from same process or no PID
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
// If another process wrote after our last read
|
|
502
|
+
if (lockData.timestamp > (this.invalidation.lastFullInvalidation || 0)) {
|
|
503
|
+
return true
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
} catch {}
|
|
507
|
+
return false
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
/**
|
|
511
|
+
* Acquire cross-process lock before writing.
|
|
512
|
+
*/
|
|
513
|
+
acquireWriteLock(): boolean {
|
|
514
|
+
const lockPath = join(this.projectDir, ".sdd", ".cache-lock")
|
|
515
|
+
const dir = dirname(lockPath)
|
|
516
|
+
if (!existsSync(dir)) mkdirSync(dir, { recursive: true })
|
|
517
|
+
|
|
518
|
+
try {
|
|
519
|
+
// Try to create lock file
|
|
520
|
+
if (existsSync(lockPath)) {
|
|
521
|
+
const existing = JSON.parse(readFileSync(lockPath, "utf-8"))
|
|
522
|
+
// If lock is stale (>30s), override it
|
|
523
|
+
if (Date.now() - existing.timestamp > 30000) {
|
|
524
|
+
// Stale lock, ok to override
|
|
525
|
+
} else if (existing.pid !== process.pid) {
|
|
526
|
+
return false // Another process holds the lock
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
writeFileSync(lockPath, JSON.stringify({
|
|
531
|
+
pid: process.pid,
|
|
532
|
+
timestamp: Date.now(),
|
|
533
|
+
}))
|
|
534
|
+
return true
|
|
535
|
+
} catch {
|
|
536
|
+
return false
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
/**
|
|
541
|
+
* Release cross-process lock.
|
|
542
|
+
*/
|
|
543
|
+
releaseWriteLock(): void {
|
|
544
|
+
const lockPath = join(this.projectDir, ".sdd", ".cache-lock")
|
|
545
|
+
try {
|
|
546
|
+
if (existsSync(lockPath)) {
|
|
547
|
+
const lockData = JSON.parse(readFileSync(lockPath, "utf-8"))
|
|
548
|
+
if (lockData.pid === process.pid) {
|
|
549
|
+
unlinkSync(lockPath)
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
} catch {}
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
// ── Full Reset (H) ───────────────────────────────────────────────
|
|
556
|
+
|
|
557
|
+
/**
|
|
558
|
+
* H: Full cache reset — clears everything in memory and on disk.
|
|
559
|
+
* Used by /sdd cache reset command.
|
|
560
|
+
*/
|
|
561
|
+
fullReset(): { cleared: { memory: boolean; disk: boolean; snapshot: boolean; lock: boolean } } {
|
|
562
|
+
// Clear in-memory
|
|
563
|
+
this.toolResponses.clear()
|
|
564
|
+
this.analysisResults.clear()
|
|
565
|
+
this.graphCache.invalidateAll()
|
|
566
|
+
|
|
567
|
+
// Reset invalidation tracker
|
|
568
|
+
this.invalidation.lastFullInvalidation = Date.now()
|
|
569
|
+
this.invalidation.dirtyTypes.clear()
|
|
570
|
+
this.invalidation.dirtyNodeIds.clear()
|
|
571
|
+
this.invalidation.dirtyRelTypes.clear()
|
|
572
|
+
this.invalidation.version++
|
|
573
|
+
this.stats.invalidations++
|
|
574
|
+
this.saveInvalidationTracker()
|
|
575
|
+
|
|
576
|
+
// Clear persistent cache on disk
|
|
577
|
+
let disk = false
|
|
578
|
+
try {
|
|
579
|
+
const cachePath = join(this.projectDir, PERSISTENT_CACHE_FILE)
|
|
580
|
+
if (existsSync(cachePath)) {
|
|
581
|
+
unlinkSync(cachePath)
|
|
582
|
+
disk = true
|
|
583
|
+
}
|
|
584
|
+
} catch {}
|
|
585
|
+
|
|
586
|
+
// Clear graph snapshot (G)
|
|
587
|
+
let snapshot = false
|
|
588
|
+
try {
|
|
589
|
+
const store = getGraphSnapshotStore(this.projectDir)
|
|
590
|
+
if (store.isValid()) {
|
|
591
|
+
store.invalidate()
|
|
592
|
+
snapshot = true
|
|
593
|
+
}
|
|
594
|
+
} catch {}
|
|
595
|
+
|
|
596
|
+
// Release lock
|
|
597
|
+
let lock = false
|
|
598
|
+
try {
|
|
599
|
+
const lockPath = join(this.projectDir, ".sdd", ".cache-lock")
|
|
600
|
+
if (existsSync(lockPath)) {
|
|
601
|
+
this.releaseWriteLock()
|
|
602
|
+
lock = true
|
|
603
|
+
}
|
|
604
|
+
} catch {}
|
|
605
|
+
|
|
606
|
+
return { cleared: { memory: true, disk, snapshot, lock } }
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
/**
|
|
610
|
+
* Get current invalidation version (for external checks).
|
|
611
|
+
*/
|
|
612
|
+
getInvalidationVersion(): number {
|
|
613
|
+
return this.invalidation.version
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
/**
|
|
617
|
+
* Sync invalidation version after external write.
|
|
618
|
+
*/
|
|
619
|
+
syncInvalidationVersion(): void {
|
|
620
|
+
this.invalidationVersionOnWrite = this.invalidation.version
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
// ── Per-Type Graph Cache ──────────────────────────────────────────
|
|
624
|
+
|
|
625
|
+
/**
|
|
626
|
+
* Get cached nodes for a specific type.
|
|
627
|
+
* Only returns if the cache version matches.
|
|
628
|
+
*/
|
|
629
|
+
getCachedNodesByType(type: NodeType, graphVersion: number): AnyNode[] | null {
|
|
630
|
+
return this.graphCache.getNodesByType(type, graphVersion)
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
/**
|
|
634
|
+
* Cache nodes for a specific type.
|
|
635
|
+
*/
|
|
636
|
+
setCachedNodesByType(type: NodeType, nodes: AnyNode[], graphVersion: number): void {
|
|
637
|
+
this.graphCache.setType(type, nodes, graphVersion)
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
/**
|
|
641
|
+
* Invalidate cache for a specific node type only.
|
|
642
|
+
* Other types remain cached.
|
|
643
|
+
*/
|
|
644
|
+
invalidateNodeType(type: NodeType): void {
|
|
645
|
+
this.graphCache.invalidateType(type)
|
|
646
|
+
this.invalidateNodeTypes([type])
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
/**
|
|
650
|
+
* Invalidate cache for multiple node types.
|
|
651
|
+
*/
|
|
652
|
+
invalidateNodeTypes(types: string[]): void {
|
|
653
|
+
for (const type of types) {
|
|
654
|
+
this.invalidation.dirtyTypes.add(type)
|
|
655
|
+
this.graphCache.invalidateType(type as NodeType)
|
|
656
|
+
}
|
|
657
|
+
this.invalidation.version++
|
|
658
|
+
this.saveInvalidationTracker()
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
/**
|
|
662
|
+
* Get cached relationships.
|
|
663
|
+
*/
|
|
664
|
+
getCachedRelationships(graphVersion: number): Relationship[] | null {
|
|
665
|
+
return this.graphCache.getRelationships(graphVersion)
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
/**
|
|
669
|
+
* Cache relationships.
|
|
670
|
+
*/
|
|
671
|
+
setCachedRelationships(rels: Relationship[], graphVersion: number): void {
|
|
672
|
+
this.graphCache.setRelationships(rels, graphVersion)
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
// ── Incremental Graph Hash ────────────────────────────────────────
|
|
676
|
+
|
|
677
|
+
/**
|
|
678
|
+
* Initialize the incremental hash from graph state.
|
|
679
|
+
*/
|
|
680
|
+
initGraphHash(nodeCount: number, relCount: number): void {
|
|
681
|
+
this.graphHash.initialize(nodeCount, relCount)
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
/**
|
|
685
|
+
* Record a node mutation for incremental hash.
|
|
686
|
+
*/
|
|
687
|
+
recordNodeMutation(delta: number = 0): void {
|
|
688
|
+
this.graphHash.recordNodeCountChange(delta)
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
/**
|
|
692
|
+
* Record a relationship mutation for incremental hash.
|
|
693
|
+
*/
|
|
694
|
+
recordRelMutation(delta: number = 0): void {
|
|
695
|
+
this.graphHash.recordRelCountChange(delta)
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
/**
|
|
699
|
+
* Get current graph hash.
|
|
700
|
+
*/
|
|
701
|
+
getGraphHash(): string {
|
|
702
|
+
return this.graphHash.getHash()
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
/**
|
|
706
|
+
* Check if graph hash matches.
|
|
707
|
+
*/
|
|
708
|
+
isGraphHashValid(expectedHash: string): boolean {
|
|
709
|
+
return this.graphHash.matches(expectedHash)
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
/**
|
|
713
|
+
* Get current node count from incremental hash.
|
|
714
|
+
*/
|
|
715
|
+
getNodeCountFromHash(): number {
|
|
716
|
+
return this.graphHash.getNodeCount()
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
// ── Statistics ────────────────────────────────────────────────────
|
|
720
|
+
|
|
721
|
+
getStats() {
|
|
722
|
+
return {
|
|
723
|
+
...this.stats,
|
|
724
|
+
toolCacheSize: this.toolResponses.size,
|
|
725
|
+
analysisCacheSize: this.analysisResults.size,
|
|
726
|
+
invalidationVersion: this.invalidation.version,
|
|
727
|
+
hitRate: this.stats.toolHits + this.stats.toolMisses > 0
|
|
728
|
+
? (this.stats.toolHits / (this.stats.toolHits + this.stats.toolMisses) * 100).toFixed(1) + "%"
|
|
729
|
+
: "0%",
|
|
730
|
+
analysisHitRate: this.stats.analysisHits + this.stats.analysisMisses > 0
|
|
731
|
+
? (this.stats.analysisHits / (this.stats.analysisHits + this.stats.analysisMisses) * 100).toFixed(1) + "%"
|
|
732
|
+
: "0%",
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
// ── Private Helpers ───────────────────────────────────────────────
|
|
737
|
+
|
|
738
|
+
private toolCacheKey(toolName: string, args: Record<string, unknown>): string {
|
|
739
|
+
const argsStr = JSON.stringify(args, Object.keys(args).sort())
|
|
740
|
+
const argsHash = createHash("md5").update(argsStr).digest("hex").slice(0, 8)
|
|
741
|
+
return `${toolName}:${argsHash}`
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
private isToolAffectedByInvalidation(toolName: string, argsHash: string): boolean {
|
|
745
|
+
// Check if the tool's dependent types have been invalidated
|
|
746
|
+
const dependentTypes = this.getToolDependentTypes(toolName)
|
|
747
|
+
for (const type of dependentTypes) {
|
|
748
|
+
if (this.invalidation.dirtyTypes.has(type)) return true
|
|
749
|
+
}
|
|
750
|
+
return false
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
private isAnalysisAffectedByInvalidation(analysisType: string): boolean {
|
|
754
|
+
const dependentTypes = this.getAnalysisDependentTypes(analysisType)
|
|
755
|
+
for (const type of dependentTypes) {
|
|
756
|
+
if (this.invalidation.dirtyTypes.has(type)) return true
|
|
757
|
+
}
|
|
758
|
+
return false
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
/**
|
|
762
|
+
* Clear dirty types that are relevant to a specific analysis type.
|
|
763
|
+
* Called after the analysis cache entry has been invalidated due to dirty types.
|
|
764
|
+
* This allows the cache to work again after the analysis is recomputed.
|
|
765
|
+
*/
|
|
766
|
+
private clearDirtyTypesForAnalysis(analysisType: string): void {
|
|
767
|
+
const dependentTypes = this.getAnalysisDependentTypes(analysisType)
|
|
768
|
+
for (const type of dependentTypes) {
|
|
769
|
+
this.invalidation.dirtyTypes.delete(type)
|
|
770
|
+
}
|
|
771
|
+
this.invalidation.version++
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
/**
|
|
775
|
+
* Clear dirty types that are relevant to a specific tool.
|
|
776
|
+
*/
|
|
777
|
+
private clearDirtyTypesForTool(toolName: string): void {
|
|
778
|
+
const dependentTypes = this.getToolDependentTypes(toolName)
|
|
779
|
+
for (const type of dependentTypes) {
|
|
780
|
+
this.invalidation.dirtyTypes.delete(type)
|
|
781
|
+
}
|
|
782
|
+
this.invalidation.version++
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
private getToolDependentTypes(toolName: string): string[] {
|
|
786
|
+
const map: Record<string, string[]> = {
|
|
787
|
+
"sdd.query_graph": [], // depends on any type
|
|
788
|
+
"sdd.list_nodes": [], // depends on queried type
|
|
789
|
+
"sdd.get_nodes_by_status": [],
|
|
790
|
+
"sdd.count_nodes": [],
|
|
791
|
+
"sdd.inspect": [],
|
|
792
|
+
"sdd.validate": ["feature", "requirement", "entity", "api", "endpoint", "table", "task", "test", "change", "constitution", "business_rule"],
|
|
793
|
+
"sdd.detect_drift": ["file", "task", "change", "entity", "api"],
|
|
794
|
+
"sdd.quality": ["feature", "requirement", "entity", "api", "endpoint", "task", "test", "change", "constitution", "business_rule"],
|
|
795
|
+
"sdd.anti_patterns": ["feature", "requirement", "entity", "change", "task", "test"],
|
|
796
|
+
"sdd.contradictions": ["requirement", "business_rule", "constraint"],
|
|
797
|
+
"sdd.coverage": ["requirement", "test"],
|
|
798
|
+
"sdd.analyze_impact": [],
|
|
799
|
+
"sdd.get_context": [],
|
|
800
|
+
"sdd.pending_changes": ["change"],
|
|
801
|
+
"sdd.session_handoff": ["change", "decision"],
|
|
802
|
+
}
|
|
803
|
+
return map[toolName] || []
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
private getAnalysisDependentTypes(analysisType: string): string[] {
|
|
807
|
+
const map: Record<string, string[]> = {
|
|
808
|
+
"validate": ["feature", "requirement", "entity", "api", "endpoint", "table", "task", "test", "change", "constitution", "business_rule"],
|
|
809
|
+
"drift": ["file", "task", "change", "entity", "api"],
|
|
810
|
+
"quality": ["feature", "requirement", "entity", "api", "endpoint", "task", "test", "change", "constitution", "business_rule"],
|
|
811
|
+
"anti_patterns": ["feature", "requirement", "entity", "change", "task", "test"],
|
|
812
|
+
"contradictions": ["requirement", "business_rule", "constraint"],
|
|
813
|
+
"coverage": ["requirement", "test"],
|
|
814
|
+
"constitution": ["constitution", "feature", "requirement", "entity", "business_rule"],
|
|
815
|
+
"promises": ["requirement", "business_rule"],
|
|
816
|
+
"integrity": [],
|
|
817
|
+
}
|
|
818
|
+
return map[analysisType] || []
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
private getAnalysisTypesForNodeType(nodeType: string): string[] {
|
|
822
|
+
const map: Record<string, string[]> = {
|
|
823
|
+
"feature": ["validate", "quality", "anti_patterns"],
|
|
824
|
+
"requirement": ["validate", "quality", "coverage", "promises", "constitution"],
|
|
825
|
+
"entity": ["validate", "quality", "drift"],
|
|
826
|
+
"endpoint": ["validate", "quality"],
|
|
827
|
+
"api": ["validate", "quality"],
|
|
828
|
+
"business_rule": ["validate", "quality", "contradictions", "promises"],
|
|
829
|
+
"change": ["validate", "quality", "anti_patterns"],
|
|
830
|
+
"task": ["validate", "quality", "drift"],
|
|
831
|
+
"test": ["validate", "quality", "coverage"],
|
|
832
|
+
"file": ["drift"],
|
|
833
|
+
"constitution": ["validate", "constitution"],
|
|
834
|
+
"constraint": ["contradictions"],
|
|
835
|
+
}
|
|
836
|
+
return map[nodeType] || []
|
|
837
|
+
}
|
|
838
|
+
|
|
839
|
+
private doesToolDependOnTypes(key: string, types: string[], relTypes: string[]): boolean {
|
|
840
|
+
const toolName = key.split(":")[0]
|
|
841
|
+
const depTypes = this.getToolDependentTypes(toolName)
|
|
842
|
+
if (depTypes.length === 0) return true // Unknown dependency, invalidate to be safe
|
|
843
|
+
return types.some(t => depTypes.includes(t))
|
|
844
|
+
}
|
|
845
|
+
|
|
846
|
+
private doesAnalysisDependOnTypes(key: string, types: string[], relTypes: string[]): boolean {
|
|
847
|
+
const analysisType = key.replace("analysis:", "")
|
|
848
|
+
const depTypes = this.getAnalysisDependentTypes(analysisType)
|
|
849
|
+
if (depTypes.length === 0) return true
|
|
850
|
+
return types.some(t => depTypes.includes(t))
|
|
851
|
+
}
|
|
852
|
+
|
|
853
|
+
private evictOldestToolEntries(count: number): void {
|
|
854
|
+
const entries = [...this.toolResponses.entries()]
|
|
855
|
+
.sort((a, b) => (a[1].lastAccess || a[1].timestamp) - (b[1].lastAccess || b[1].timestamp))
|
|
856
|
+
for (let i = 0; i < count && i < entries.length; i++) {
|
|
857
|
+
this.toolResponses.delete(entries[i][0])
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
|
|
861
|
+
private loadInvalidationTracker(): void {
|
|
862
|
+
const path = join(this.projectDir, INVALIDATION_FILE)
|
|
863
|
+
if (!existsSync(path)) return
|
|
864
|
+
try {
|
|
865
|
+
const data = JSON.parse(readFileSync(path, "utf-8"))
|
|
866
|
+
this.invalidation.lastFullInvalidation = data.lastFullInvalidation || 0
|
|
867
|
+
this.invalidation.version = data.version || 0
|
|
868
|
+
this.invalidation.dirtyTypes = new Set(data.dirtyTypes || [])
|
|
869
|
+
this.invalidation.dirtyNodeIds = new Set(data.dirtyNodeIds || [])
|
|
870
|
+
this.invalidation.dirtyRelTypes = new Set(data.dirtyRelTypes || [])
|
|
871
|
+
} catch {}
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
private saveInvalidationTracker(): void {
|
|
875
|
+
const path = join(this.projectDir, INVALIDATION_FILE)
|
|
876
|
+
const dir = dirname(path)
|
|
877
|
+
if (!existsSync(dir)) mkdirSync(dir, { recursive: true })
|
|
878
|
+
writeFileSync(path, JSON.stringify({
|
|
879
|
+
lastFullInvalidation: this.invalidation.lastFullInvalidation,
|
|
880
|
+
version: this.invalidation.version,
|
|
881
|
+
dirtyTypes: [...this.invalidation.dirtyTypes],
|
|
882
|
+
dirtyNodeIds: [...this.invalidation.dirtyNodeIds],
|
|
883
|
+
dirtyRelTypes: [...this.invalidation.dirtyRelTypes],
|
|
884
|
+
}))
|
|
885
|
+
}
|
|
886
|
+
}
|
|
887
|
+
|
|
888
|
+
// ── Singleton per project ────────────────────────────────────────────
|
|
889
|
+
|
|
890
|
+
const instances = new Map<string, CacheManager>()
|
|
891
|
+
|
|
892
|
+
export function getCacheManager(projectDir: string): CacheManager {
|
|
893
|
+
let instance = instances.get(projectDir)
|
|
894
|
+
if (!instance) {
|
|
895
|
+
instance = new CacheManager(projectDir)
|
|
896
|
+
instances.set(projectDir, instance)
|
|
897
|
+
}
|
|
898
|
+
return instance
|
|
899
|
+
}
|