@unerr-ai/unerr 0.2.1 → 0.2.3
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 +36 -45
- package/dist/cli.js +37443 -36022
- package/package.json +2 -1
- package/dist/behaviors/agent-llm-bridge.js +0 -166
- package/dist/behaviors/architecture-guard.js +0 -256
- package/dist/behaviors/auto-doc.js +0 -247
- package/dist/behaviors/cascade-guard.js +0 -289
- package/dist/behaviors/change-narrative.js +0 -270
- package/dist/behaviors/convention-drift.js +0 -290
- package/dist/behaviors/framework.js +0 -235
- package/dist/behaviors/guard-formatter.js +0 -44
- package/dist/behaviors/incomplete-work.js +0 -270
- package/dist/behaviors/loop-breaker.js +0 -300
- package/dist/behaviors/session-continuity.js +0 -208
- package/dist/commands/branches.js +0 -97
- package/dist/commands/check-commit.js +0 -225
- package/dist/commands/compress-output.js +0 -64
- package/dist/commands/config-verify.js +0 -243
- package/dist/commands/daemon.js +0 -905
- package/dist/commands/dashboard.js +0 -52
- package/dist/commands/debug.js +0 -200
- package/dist/commands/enrich.js +0 -184
- package/dist/commands/exec.js +0 -233
- package/dist/commands/gain.js +0 -156
- package/dist/commands/hook.js +0 -88
- package/dist/commands/index.js +0 -88
- package/dist/commands/init.js +0 -74
- package/dist/commands/install.js +0 -505
- package/dist/commands/learn.js +0 -116
- package/dist/commands/manifest.js +0 -193
- package/dist/commands/rewind.js +0 -103
- package/dist/commands/serve.js +0 -19
- package/dist/commands/setup-wizard.js +0 -414
- package/dist/commands/skills.js +0 -64
- package/dist/commands/stats.js +0 -20
- package/dist/commands/status.js +0 -654
- package/dist/commands/timeline.js +0 -139
- package/dist/commands/uninstall.js +0 -230
- package/dist/components/App.js +0 -109
- package/dist/components/Banner.js +0 -12
- package/dist/components/ConfirmPrompt.js +0 -25
- package/dist/components/DriftSummary.js +0 -23
- package/dist/components/GradeBadge.js +0 -15
- package/dist/components/HealthCard.js +0 -18
- package/dist/components/InkSpinner.js +0 -22
- package/dist/components/InputBox.js +0 -17
- package/dist/components/KeyValue.js +0 -13
- package/dist/components/MessageList.js +0 -14
- package/dist/components/ProgressBar.js +0 -26
- package/dist/components/Section.js +0 -16
- package/dist/components/SessionSummaryCard.js +0 -73
- package/dist/components/StartupDisplay.js +0 -24
- package/dist/components/StatusDashboard.js +0 -57
- package/dist/components/StatusLine.js +0 -8
- package/dist/components/StepLine.js +0 -22
- package/dist/components/Theme.js +0 -20
- package/dist/components/ToolProgress.js +0 -8
- package/dist/components/ViolationList.js +0 -21
- package/dist/components/render.js +0 -13
- package/dist/config/agent-registry.js +0 -237
- package/dist/config/claude-settings-hooks.js +0 -304
- package/dist/config/hook-installer.js +0 -65
- package/dist/config/instruction-writer.js +0 -388
- package/dist/config/mcp-config-writer.js +0 -266
- package/dist/config/settings.js +0 -174
- package/dist/config/tool-detector.js +0 -42
- package/dist/config/value-surfacing.js +0 -119
- package/dist/core/context-assembly.js +0 -108
- package/dist/core/conversation.js +0 -33
- package/dist/core/local-chat-provider.js +0 -475
- package/dist/core/provider-factory.js +0 -55
- package/dist/core/providers.js +0 -90
- package/dist/core/query-engine.js +0 -174
- package/dist/daemon/api.js +0 -312
- package/dist/daemon/autostart.js +0 -119
- package/dist/daemon/bootstrap.js +0 -39
- package/dist/daemon/client.js +0 -164
- package/dist/daemon/detect-ci.js +0 -81
- package/dist/daemon/platform-linux.js +0 -146
- package/dist/daemon/platform-macos.js +0 -134
- package/dist/daemon/platform-windows.js +0 -116
- package/dist/daemon/process-manager.js +0 -299
- package/dist/daemon/protocol.js +0 -23
- package/dist/daemon/registry.js +0 -270
- package/dist/daemon/settings-schema.js +0 -72
- package/dist/daemon/system-health.js +0 -134
- package/dist/daemon/version-checker.js +0 -262
- package/dist/daemon/warm-start.js +0 -223
- package/dist/entrypoints/cli.js +0 -1043
- package/dist/entrypoints/daemon.js +0 -380
- package/dist/entrypoints/repl.js +0 -147
- package/dist/hooks/adapters/claude-code.js +0 -90
- package/dist/hooks/adapters/cline.js +0 -100
- package/dist/hooks/adapters/cursor.js +0 -98
- package/dist/hooks/hook-dedup.js +0 -79
- package/dist/hooks/hook-runner.js +0 -113
- package/dist/hooks/navigation-hooks.js +0 -175
- package/dist/hooks/prompt-hooks.js +0 -63
- package/dist/hooks/shell-hooks.js +0 -47
- package/dist/ignore.js +0 -111
- package/dist/intelligence/approach-suggester.js +0 -61
- package/dist/intelligence/ast-extractor.js +0 -2615
- package/dist/intelligence/ast-worker.js +0 -34
- package/dist/intelligence/background-indexer.js +0 -121
- package/dist/intelligence/blast-radius.js +0 -200
- package/dist/intelligence/community-detection.js +0 -691
- package/dist/intelligence/community-detector.js +0 -184
- package/dist/intelligence/computation-scheduler.js +0 -75
- package/dist/intelligence/confidence-propagation.js +0 -47
- package/dist/intelligence/convention-detector.js +0 -242
- package/dist/intelligence/convention-learner.js +0 -205
- package/dist/intelligence/convention-matcher.js +0 -205
- package/dist/intelligence/cozo-schema.js +0 -376
- package/dist/intelligence/decision-point-detector.js +0 -90
- package/dist/intelligence/deep-dive-tools.js +0 -586
- package/dist/intelligence/durability-scorer.js +0 -84
- package/dist/intelligence/exploration-cost.js +0 -204
- package/dist/intelligence/exploration-pattern-tracker.js +0 -61
- package/dist/intelligence/fact-generator.js +0 -322
- package/dist/intelligence/facts-schema.js +0 -90
- package/dist/intelligence/file-intelligence.js +0 -59
- package/dist/intelligence/graph-holder.js +0 -220
- package/dist/intelligence/graph-temporal-joiner.js +0 -238
- package/dist/intelligence/health-grade.js +0 -423
- package/dist/intelligence/health-grader.js +0 -200
- package/dist/intelligence/health-map-data.js +0 -259
- package/dist/intelligence/import-symbols.js +0 -136
- package/dist/intelligence/incremental-indexer.js +0 -658
- package/dist/intelligence/indexer/centrality.js +0 -62
- package/dist/intelligence/indexer/cfg-context.js +0 -95
- package/dist/intelligence/indexer/confidence.js +0 -34
- package/dist/intelligence/indexer/cross-file-resolver.js +0 -104
- package/dist/intelligence/indexer/edge-repair.js +0 -89
- package/dist/intelligence/indexer/entity-key.js +0 -17
- package/dist/intelligence/indexer/export-map.js +0 -132
- package/dist/intelligence/indexer/git-cochange.js +0 -128
- package/dist/intelligence/indexer/graph-patch.js +0 -147
- package/dist/intelligence/indexer/incremental.js +0 -78
- package/dist/intelligence/indexer/ingest.js +0 -160
- package/dist/intelligence/indexer/language-detect.js +0 -226
- package/dist/intelligence/indexer/metadata.js +0 -63
- package/dist/intelligence/indexer/mutation-tracker.js +0 -79
- package/dist/intelligence/indexer/orchestrator.js +0 -155
- package/dist/intelligence/indexer/plugin-interface.js +0 -31
- package/dist/intelligence/indexer/plugins/csharp.js +0 -440
- package/dist/intelligence/indexer/plugins/go.js +0 -335
- package/dist/intelligence/indexer/plugins/java.js +0 -370
- package/dist/intelligence/indexer/plugins/python.js +0 -358
- package/dist/intelligence/indexer/plugins/regex-fallback.js +0 -82
- package/dist/intelligence/indexer/plugins/ruby.js +0 -290
- package/dist/intelligence/indexer/plugins/rust.js +0 -484
- package/dist/intelligence/indexer/plugins/tier2-generic.js +0 -310
- package/dist/intelligence/indexer/plugins/typescript.js +0 -456
- package/dist/intelligence/indexer/resource-monitor.js +0 -93
- package/dist/intelligence/indexer/scip/decoder.js +0 -253
- package/dist/intelligence/indexer/scip/detector.js +0 -232
- package/dist/intelligence/indexer/scip/downloader.js +0 -427
- package/dist/intelligence/indexer/scip/fallback.js +0 -34
- package/dist/intelligence/indexer/scip/merger.js +0 -109
- package/dist/intelligence/indexer/scip/orchestrator.js +0 -433
- package/dist/intelligence/indexer/scip/runner.js +0 -98
- package/dist/intelligence/indexer/snapshot.js +0 -66
- package/dist/intelligence/indexer/test-detector.js +0 -196
- package/dist/intelligence/indexer/watch-integration.js +0 -61
- package/dist/intelligence/indexer/worker.js +0 -85
- package/dist/intelligence/local-convention-detector.js +0 -437
- package/dist/intelligence/local-embeddings.js +0 -190
- package/dist/intelligence/local-graph.js +0 -1946
- package/dist/intelligence/local-indexer.js +0 -1575
- package/dist/intelligence/local-llm.js +0 -163
- package/dist/intelligence/local-rule-generator.js +0 -154
- package/dist/intelligence/local-snapshot.js +0 -213
- package/dist/intelligence/negative-knowledge.js +0 -103
- package/dist/intelligence/persistent-db.js +0 -85
- package/dist/intelligence/query-router.js +0 -2556
- package/dist/intelligence/risk-classifier.js +0 -116
- package/dist/intelligence/rule-evaluator.js +0 -380
- package/dist/intelligence/rule-generator.js +0 -49
- package/dist/intelligence/search-index.js +0 -173
- package/dist/intelligence/semantic/docstring-extractor.js +0 -67
- package/dist/intelligence/semantic/embedding-store.js +0 -52
- package/dist/intelligence/semantic/enrichment-orchestrator.js +0 -48
- package/dist/intelligence/semantic/git-message-miner.js +0 -114
- package/dist/intelligence/semantic/identifier-tokenizer.js +0 -51
- package/dist/intelligence/semantic/node2vec-embeddings.js +0 -71
- package/dist/intelligence/semantic/node2vec-walks.js +0 -103
- package/dist/intelligence/semantic/path-domain-inference.js +0 -112
- package/dist/intelligence/semantic/similarity-engine.js +0 -60
- package/dist/intelligence/semantic/tfidf-vectors.js +0 -88
- package/dist/intelligence/session-brief-builder.js +0 -159
- package/dist/intelligence/session-context.js +0 -221
- package/dist/intelligence/session-health-monitor.js +0 -211
- package/dist/intelligence/session-narrative.js +0 -197
- package/dist/intelligence/session-pattern-analyzer.js +0 -218
- package/dist/intelligence/signal-scorer.js +0 -390
- package/dist/intelligence/signal-show-store.js +0 -182
- package/dist/intelligence/smart-truncate.js +0 -158
- package/dist/intelligence/subgraph-cache.js +0 -88
- package/dist/intelligence/temporal-facts.js +0 -494
- package/dist/intelligence/token-estimator.js +0 -100
- package/dist/intelligence/tool-injector.js +0 -87
- package/dist/intelligence/tree-sitter-loader.js +0 -71
- package/dist/intelligence/worker-pool.js +0 -116
- package/dist/proxy/arg-validator.js +0 -79
- package/dist/proxy/auto-bootstrap.js +0 -167
- package/dist/proxy/bridge.js +0 -147
- package/dist/proxy/budget-enforcer.js +0 -70
- package/dist/proxy/compression-quality-monitor.js +0 -160
- package/dist/proxy/compression-stats.js +0 -51
- package/dist/proxy/context-rot-detector.js +0 -137
- package/dist/proxy/drift-detector.js +0 -139
- package/dist/proxy/efficiency-tracker.js +0 -79
- package/dist/proxy/fact-ranking.js +0 -154
- package/dist/proxy/format-encoder.js +0 -266
- package/dist/proxy/http-transport.js +0 -90
- package/dist/proxy/lifecycle-actor.js +0 -55
- package/dist/proxy/lifecycle-machine.js +0 -187
- package/dist/proxy/log-tailer.js +0 -265
- package/dist/proxy/model-pricing.js +0 -98
- package/dist/proxy/network-firewall.js +0 -141
- package/dist/proxy/nudge-state.js +0 -93
- package/dist/proxy/output-compressor.js +0 -185
- package/dist/proxy/pid-lock.js +0 -291
- package/dist/proxy/proxy-context.js +0 -11
- package/dist/proxy/proxy.js +0 -2633
- package/dist/proxy/response-enrichment.js +0 -32
- package/dist/proxy/response-envelope.js +0 -313
- package/dist/proxy/session-dedup.js +0 -82
- package/dist/proxy/session-legend.js +0 -30
- package/dist/proxy/session-persistence.js +0 -210
- package/dist/proxy/session-resume.js +0 -94
- package/dist/proxy/session-stats.js +0 -513
- package/dist/proxy/shell-classifier.js +0 -1346
- package/dist/proxy/shell-compression-log.js +0 -93
- package/dist/proxy/shell-compressor.js +0 -390
- package/dist/proxy/shell-graph-boost.js +0 -202
- package/dist/proxy/shell-monitor-map.js +0 -18
- package/dist/proxy/shell-stats.js +0 -54
- package/dist/proxy/shell-strategies/cloud.js +0 -215
- package/dist/proxy/shell-strategies/diff.js +0 -159
- package/dist/proxy/shell-strategies/error-diagnostic.js +0 -796
- package/dist/proxy/shell-strategies/filter-dsl.js +0 -358
- package/dist/proxy/shell-strategies/git-status.js +0 -177
- package/dist/proxy/shell-strategies/key-value.js +0 -193
- package/dist/proxy/shell-strategies/log-text.js +0 -154
- package/dist/proxy/shell-strategies/omni.js +0 -188
- package/dist/proxy/shell-strategies/progress.js +0 -55
- package/dist/proxy/shell-strategies/redact.js +0 -76
- package/dist/proxy/shell-strategies/structured.js +0 -241
- package/dist/proxy/shell-strategies/tabular.js +0 -243
- package/dist/proxy/shell-strategies/test-results-types.js +0 -13
- package/dist/proxy/shell-strategies/test-results.js +0 -784
- package/dist/proxy/shell-strategies/tree-paths.js +0 -144
- package/dist/proxy/shell-strategies/yaml.js +0 -182
- package/dist/proxy/shell-tee.js +0 -111
- package/dist/proxy/signal-dedup.js +0 -171
- package/dist/proxy/startup-renderer.js +0 -158
- package/dist/proxy/task-token-display.js +0 -38
- package/dist/proxy/token-counter.js +0 -61
- package/dist/proxy/tool-clusters.js +0 -273
- package/dist/proxy/tool-definitions.js +0 -525
- package/dist/proxy/transport-mux.js +0 -229
- package/dist/proxy/wire-cap.js +0 -268
- package/dist/rules/developer.mozilla.org.json +0 -9
- package/dist/rules/github.com.json +0 -21
- package/dist/schemas/api/skills.js +0 -19
- package/dist/schemas/common/errors.js +0 -7
- package/dist/schemas/common/headers.js +0 -5
- package/dist/schemas/entities/edge.js +0 -25
- package/dist/schemas/entities/entity.js +0 -22
- package/dist/schemas/entities/rule.js +0 -18
- package/dist/schemas/index.js +0 -14
- package/dist/server/event-bus.js +0 -59
- package/dist/server/http.js +0 -156
- package/dist/server/middleware.js +0 -70
- package/dist/server/routes/drift.js +0 -97
- package/dist/server/routes/intelligence.js +0 -1217
- package/dist/server/routes/reasoning-quality.js +0 -444
- package/dist/server/routes/session.js +0 -86
- package/dist/server/routes/stream.js +0 -120
- package/dist/server/routes/system.js +0 -73
- package/dist/server/routes/temporal.js +0 -170
- package/dist/server/routes/timeline.js +0 -232
- package/dist/server/routes/token-flow.js +0 -403
- package/dist/skills/effectiveness-tracker.js +0 -93
- package/dist/skills/local-pack.js +0 -380
- package/dist/skills/resolver.js +0 -495
- package/dist/state-detector.js +0 -83
- package/dist/timeline/intent-detector.js +0 -263
- package/dist/timeline/loop-miner.js +0 -140
- package/dist/timeline/open-threads.js +0 -49
- package/dist/timeline/signal-reinforcer.js +0 -62
- package/dist/timeline/timeline-bootstrap.js +0 -151
- package/dist/timeline/timeline-store.js +0 -618
- package/dist/tools/coding/bash.js +0 -49
- package/dist/tools/coding/file-edit.js +0 -72
- package/dist/tools/coding/file-outline.js +0 -227
- package/dist/tools/coding/file-read-protocol.js +0 -425
- package/dist/tools/coding/file-read.js +0 -35
- package/dist/tools/coding/file-write.js +0 -43
- package/dist/tools/coding/glob-tool.js +0 -109
- package/dist/tools/coding/grep.js +0 -162
- package/dist/tools/coding/index.js +0 -27
- package/dist/tools/intelligence/index.js +0 -269
- package/dist/tools/intelligence/record-fact.js +0 -48
- package/dist/tools/intelligence/timeline-markers.js +0 -130
- package/dist/tools/registry.js +0 -47
- package/dist/tools/types.js +0 -8
- package/dist/tracking/auto-snapshot-triggers.js +0 -246
- package/dist/tracking/branch-context.js +0 -115
- package/dist/tracking/branch-snapshot.js +0 -217
- package/dist/tracking/causal-bridge.js +0 -317
- package/dist/tracking/circuit-breaker.js +0 -147
- package/dist/tracking/commit-watcher.js +0 -114
- package/dist/tracking/context-ledger.js +0 -119
- package/dist/tracking/correction-detector.js +0 -324
- package/dist/tracking/drift-tracker.js +0 -874
- package/dist/tracking/durability-tracker.js +0 -94
- package/dist/tracking/entity-rewind.js +0 -200
- package/dist/tracking/file-hash-state.js +0 -114
- package/dist/tracking/git-attribution.js +0 -132
- package/dist/tracking/git-trailers.js +0 -171
- package/dist/tracking/intelligence-counter.js +0 -46
- package/dist/tracking/intent-correlator.js +0 -202
- package/dist/tracking/intent-encoder.js +0 -52
- package/dist/tracking/intent-token-tracker.js +0 -159
- package/dist/tracking/ledger-archiver.js +0 -94
- package/dist/tracking/ledger-chains.js +0 -245
- package/dist/tracking/metrics-store.js +0 -361
- package/dist/tracking/native-watcher.js +0 -131
- package/dist/tracking/offline-rewind.js +0 -295
- package/dist/tracking/pending-violations.js +0 -74
- package/dist/tracking/persistence-effectiveness.js +0 -167
- package/dist/tracking/prompt-durability.js +0 -202
- package/dist/tracking/quality-signals.js +0 -213
- package/dist/tracking/redactor.js +0 -73
- package/dist/tracking/rewind-engine.js +0 -161
- package/dist/tracking/session-history.js +0 -128
- package/dist/tracking/session-receipt.js +0 -88
- package/dist/tracking/session-summary-writer.js +0 -157
- package/dist/tracking/shadow-ledger.js +0 -321
- package/dist/tracking/stash-manager.js +0 -258
- package/dist/tracking/timeline-fork.js +0 -213
- package/dist/tracking/timeline.js +0 -69
- package/dist/tracking/token-flow.js +0 -276
- package/dist/tracking/turn-segmenter.js +0 -122
- package/dist/tracking/weekly-accumulator.js +0 -179
- package/dist/tracking/working-snapshots.js +0 -188
- package/dist/tracking/workspace-manifest.js +0 -176
- package/dist/transport/http.js +0 -102
- package/dist/utils/counterfactual.js +0 -65
- package/dist/utils/deep-link.js +0 -34
- package/dist/utils/detect.js +0 -193
- package/dist/utils/exec.js +0 -73
- package/dist/utils/file-logger.js +0 -87
- package/dist/utils/format-error.js +0 -29
- package/dist/utils/git.js +0 -181
- package/dist/utils/log.js +0 -57
- package/dist/utils/logger.js +0 -35
- package/dist/utils/mcp-content-json.js +0 -8
- package/dist/utils/session-logger.js +0 -154
- package/dist/utils/startup-log.js +0 -512
- package/dist/utils/ui.js +0 -56
|
@@ -1,163 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* BYO-LLM Adapter Factory — Provider-agnostic adapter for local LLM endpoints.
|
|
3
|
-
*
|
|
4
|
-
* Supports Ollama, LM Studio, OpenAI-compatible, and Anthropic-direct providers.
|
|
5
|
-
* All adapters target the OpenAI-compatible /v1/embeddings endpoint pattern,
|
|
6
|
-
* which Ollama, LM Studio, and generic OpenAI-compatible servers all expose.
|
|
7
|
-
*
|
|
8
|
-
* Anthropic-direct uses the /v1/messages endpoint for chat but still routes
|
|
9
|
-
* embeddings through a local embedding server (the Anthropic API has no
|
|
10
|
-
* embedding endpoint, so the adapter falls back to the configured baseUrl).
|
|
11
|
-
*
|
|
12
|
-
* SECURITY: API keys are NEVER logged to stderr, included in MCP responses,
|
|
13
|
-
* or written to any ledger/file. They exist only in memory for Authorization headers.
|
|
14
|
-
*/
|
|
15
|
-
import { resolveEmbeddingEndpoint } from "../config/settings.js";
|
|
16
|
-
// ── Default Base URLs ────────────────────────────────────────
|
|
17
|
-
const DEFAULT_BASE_URLS = {
|
|
18
|
-
ollama: "http://localhost:11434",
|
|
19
|
-
"lm-studio": "http://localhost:1234",
|
|
20
|
-
"openai-compatible": "http://localhost:8080",
|
|
21
|
-
"anthropic-direct": "https://api.anthropic.com",
|
|
22
|
-
};
|
|
23
|
-
class OpenAiCompatibleAdapter {
|
|
24
|
-
provider;
|
|
25
|
-
baseUrl;
|
|
26
|
-
embeddingModel;
|
|
27
|
-
embeddingDimensions;
|
|
28
|
-
maxConcurrency;
|
|
29
|
-
apiKey;
|
|
30
|
-
constructor(config, providerLabel) {
|
|
31
|
-
const endpoint = resolveEmbeddingEndpoint(config);
|
|
32
|
-
this.provider = providerLabel;
|
|
33
|
-
this.baseUrl =
|
|
34
|
-
endpoint.baseUrl ?? DEFAULT_BASE_URLS[endpoint.provider] ?? "";
|
|
35
|
-
this.embeddingModel = endpoint.model;
|
|
36
|
-
this.embeddingDimensions = config.embeddingDimensions;
|
|
37
|
-
this.maxConcurrency = config.maxConcurrency;
|
|
38
|
-
this.apiKey = endpoint.apiKey;
|
|
39
|
-
}
|
|
40
|
-
async embed(texts) {
|
|
41
|
-
if (texts.length === 0) {
|
|
42
|
-
return { embeddings: [], model: this.embeddingModel, totalTokens: 0 };
|
|
43
|
-
}
|
|
44
|
-
const url = `${this.baseUrl.replace(/\/+$/, "")}/v1/embeddings`;
|
|
45
|
-
const headers = {
|
|
46
|
-
"Content-Type": "application/json",
|
|
47
|
-
};
|
|
48
|
-
if (this.apiKey) {
|
|
49
|
-
headers.Authorization = `Bearer ${this.apiKey}`;
|
|
50
|
-
}
|
|
51
|
-
const response = await fetch(url, {
|
|
52
|
-
method: "POST",
|
|
53
|
-
headers,
|
|
54
|
-
body: JSON.stringify({
|
|
55
|
-
model: this.embeddingModel,
|
|
56
|
-
input: texts,
|
|
57
|
-
}),
|
|
58
|
-
});
|
|
59
|
-
if (!response.ok) {
|
|
60
|
-
const body = await response.text().catch(() => "");
|
|
61
|
-
throw new Error(`[LocalLLM] ${this.provider} embedding request failed: ${response.status} ${response.statusText}${body ? ` — ${body.slice(0, 200)}` : ""}`);
|
|
62
|
-
}
|
|
63
|
-
const json = (await response.json());
|
|
64
|
-
// Sort by index to guarantee order matches input order
|
|
65
|
-
const sorted = [...json.data].sort((a, b) => a.index - b.index);
|
|
66
|
-
return {
|
|
67
|
-
embeddings: sorted.map((d) => d.embedding),
|
|
68
|
-
model: json.model ?? this.embeddingModel,
|
|
69
|
-
totalTokens: json.usage?.total_tokens ?? 0,
|
|
70
|
-
};
|
|
71
|
-
}
|
|
72
|
-
async isAvailable() {
|
|
73
|
-
try {
|
|
74
|
-
// Ollama: GET /api/tags lists models
|
|
75
|
-
// LM Studio: GET /v1/models lists models
|
|
76
|
-
// OpenAI-compatible: GET /v1/models lists models
|
|
77
|
-
// Try the most common endpoint first
|
|
78
|
-
const modelsUrl = this.provider === "ollama"
|
|
79
|
-
? `${this.baseUrl.replace(/\/+$/, "")}/api/tags`
|
|
80
|
-
: `${this.baseUrl.replace(/\/+$/, "")}/v1/models`;
|
|
81
|
-
const headers = {};
|
|
82
|
-
if (this.apiKey) {
|
|
83
|
-
headers.Authorization = `Bearer ${this.apiKey}`;
|
|
84
|
-
}
|
|
85
|
-
const response = await fetch(modelsUrl, {
|
|
86
|
-
method: "GET",
|
|
87
|
-
headers,
|
|
88
|
-
signal: AbortSignal.timeout(3000),
|
|
89
|
-
});
|
|
90
|
-
return response.ok;
|
|
91
|
-
}
|
|
92
|
-
catch {
|
|
93
|
-
return false;
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
// ── Anthropic Direct Adapter ─────────────────────────────────
|
|
98
|
-
// Anthropic has no embedding API. This adapter uses the configured
|
|
99
|
-
// baseUrl for embeddings (which must be a local embedding server)
|
|
100
|
-
// and only uses the Anthropic API for chat (not implemented here).
|
|
101
|
-
class AnthropicDirectAdapter {
|
|
102
|
-
provider = "anthropic-direct";
|
|
103
|
-
baseUrl;
|
|
104
|
-
embeddingModel;
|
|
105
|
-
embeddingDimensions;
|
|
106
|
-
maxConcurrency;
|
|
107
|
-
inner;
|
|
108
|
-
constructor(config) {
|
|
109
|
-
const endpoint = resolveEmbeddingEndpoint(config);
|
|
110
|
-
this.baseUrl =
|
|
111
|
-
endpoint.baseUrl ?? DEFAULT_BASE_URLS["anthropic-direct"] ?? "";
|
|
112
|
-
this.embeddingModel = endpoint.model;
|
|
113
|
-
this.embeddingDimensions = config.embeddingDimensions;
|
|
114
|
-
this.maxConcurrency = config.maxConcurrency;
|
|
115
|
-
this.inner = new OpenAiCompatibleAdapter(config, "anthropic-direct (embedding proxy)");
|
|
116
|
-
}
|
|
117
|
-
async embed(texts) {
|
|
118
|
-
return this.inner.embed(texts);
|
|
119
|
-
}
|
|
120
|
-
async isAvailable() {
|
|
121
|
-
return this.inner.isAvailable();
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
// ── Factory ──────────────────────────────────────────────────
|
|
125
|
-
/**
|
|
126
|
-
* Create a LocalLlmAdapter for the given settings.
|
|
127
|
-
* Returns null if no localLlm config is provided.
|
|
128
|
-
*/
|
|
129
|
-
export function createLocalLlmAdapter(config) {
|
|
130
|
-
if (!config)
|
|
131
|
-
return null;
|
|
132
|
-
const embeddingEndpoint = resolveEmbeddingEndpoint(config);
|
|
133
|
-
const effectiveProvider = embeddingEndpoint.provider;
|
|
134
|
-
switch (effectiveProvider) {
|
|
135
|
-
case "ollama":
|
|
136
|
-
return new OpenAiCompatibleAdapter(config, "ollama");
|
|
137
|
-
case "lm-studio":
|
|
138
|
-
return new OpenAiCompatibleAdapter(config, "lm-studio");
|
|
139
|
-
case "openai-compatible":
|
|
140
|
-
return new OpenAiCompatibleAdapter(config, "openai-compatible");
|
|
141
|
-
case "anthropic-direct":
|
|
142
|
-
return new AnthropicDirectAdapter(config);
|
|
143
|
-
default:
|
|
144
|
-
return new OpenAiCompatibleAdapter(config, effectiveProvider);
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
/**
|
|
148
|
-
* Create adapter and verify availability, logging to stderr.
|
|
149
|
-
* Returns the adapter (available or not) — callers check isAvailable() as needed.
|
|
150
|
-
*/
|
|
151
|
-
export async function createAndVerifyAdapter(config) {
|
|
152
|
-
const adapter = createLocalLlmAdapter(config);
|
|
153
|
-
if (!adapter)
|
|
154
|
-
return null;
|
|
155
|
-
const available = await adapter.isAvailable();
|
|
156
|
-
if (available) {
|
|
157
|
-
process.stderr.write(`[unerr] BYO-LLM: ${adapter.provider} at ${adapter.baseUrl} ✓ (model: ${adapter.embeddingModel})\n`);
|
|
158
|
-
}
|
|
159
|
-
else {
|
|
160
|
-
process.stderr.write(`[unerr] BYO-LLM: ${adapter.provider} at ${adapter.baseUrl} — not reachable (embeddings disabled)\n`);
|
|
161
|
-
}
|
|
162
|
-
return adapter;
|
|
163
|
-
}
|
|
@@ -1,154 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Sprint L6.2: Local Rule Generator from Conventions
|
|
3
|
-
*
|
|
4
|
-
* Converts detected convention patterns into evaluable rules compatible
|
|
5
|
-
* with the CozoDB `rules` relation and `rule-evaluator.ts`.
|
|
6
|
-
*
|
|
7
|
-
* Each generated rule uses `engine: "structural"` (tree-sitter / naming regex),
|
|
8
|
-
* NEVER LLM-based evaluation. Rules carry `source: "local-convention-detector"`
|
|
9
|
-
* attribution to distinguish from external rules.
|
|
10
|
-
*
|
|
11
|
-
* All logging to stderr. Never touches stdout.
|
|
12
|
-
*/
|
|
13
|
-
// ── Naming → Query Mapping ──────────────────────────────────────
|
|
14
|
-
/** Maps naming pattern IDs to tree-sitter query fragments for rule-evaluator.ts. */
|
|
15
|
-
const NAMING_QUERIES = {
|
|
16
|
-
camelCase: "^[a-z][a-zA-Z0-9]*$",
|
|
17
|
-
PascalCase: "^[A-Z][a-zA-Z0-9]*$",
|
|
18
|
-
snake_case: "^[a-z][a-z0-9]*(?:_[a-z0-9]+)*$",
|
|
19
|
-
SCREAMING_SNAKE: "^[A-Z][A-Z0-9]*(?:_[A-Z0-9]+)*$",
|
|
20
|
-
"kebab-case": "^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$",
|
|
21
|
-
};
|
|
22
|
-
/** Maps entity kinds to glob patterns. */
|
|
23
|
-
const KIND_TO_GLOB = {
|
|
24
|
-
function: "**/*.{ts,tsx,js,jsx,py,go}",
|
|
25
|
-
class: "**/*.{ts,tsx,js,jsx,py,java}",
|
|
26
|
-
interface: "**/*.{ts,tsx}",
|
|
27
|
-
type: "**/*.{ts,tsx}",
|
|
28
|
-
method: "**/*.{ts,tsx,js,jsx,py,go,java}",
|
|
29
|
-
variable: "**/*.{ts,tsx,js,jsx}",
|
|
30
|
-
component: "**/*.{tsx,jsx}",
|
|
31
|
-
};
|
|
32
|
-
// ── Main Generation Function ────────────────────────────────────
|
|
33
|
-
/**
|
|
34
|
-
* Generate evaluable rules from detected convention patterns.
|
|
35
|
-
*
|
|
36
|
-
* @param conventions - Output from detectLocalConventions()
|
|
37
|
-
* @param repoId - Repository ID for rule scoping
|
|
38
|
-
* @returns CompactRule[] ready for CozoGraphStore.loadRules()
|
|
39
|
-
*/
|
|
40
|
-
export function generateLocalRules(conventions, repoId) {
|
|
41
|
-
const rules = [];
|
|
42
|
-
let naming = 0;
|
|
43
|
-
let structural = 0;
|
|
44
|
-
let importDirection = 0;
|
|
45
|
-
for (const convention of conventions) {
|
|
46
|
-
const rule = conventionToRule(convention, repoId);
|
|
47
|
-
if (rule) {
|
|
48
|
-
rules.push(rule);
|
|
49
|
-
switch (convention.kind) {
|
|
50
|
-
case "naming":
|
|
51
|
-
naming++;
|
|
52
|
-
break;
|
|
53
|
-
case "structure":
|
|
54
|
-
structural++;
|
|
55
|
-
break;
|
|
56
|
-
case "import_direction":
|
|
57
|
-
importDirection++;
|
|
58
|
-
break;
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
return {
|
|
63
|
-
rules,
|
|
64
|
-
stats: {
|
|
65
|
-
naming,
|
|
66
|
-
structural,
|
|
67
|
-
importDirection,
|
|
68
|
-
total: rules.length,
|
|
69
|
-
},
|
|
70
|
-
};
|
|
71
|
-
}
|
|
72
|
-
// ── Convention → Rule Conversion ────────────────────────────────
|
|
73
|
-
function conventionToRule(convention, repoId) {
|
|
74
|
-
const ruleKey = `local-rule-${convention.key}`;
|
|
75
|
-
switch (convention.kind) {
|
|
76
|
-
case "naming":
|
|
77
|
-
return namingConventionToRule(convention, ruleKey, repoId);
|
|
78
|
-
case "structure":
|
|
79
|
-
return structureConventionToRule(convention, ruleKey, repoId);
|
|
80
|
-
case "import_direction":
|
|
81
|
-
return importDirectionConventionToRule(convention, ruleKey, repoId);
|
|
82
|
-
default:
|
|
83
|
-
return null;
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
function namingConventionToRule(convention, ruleKey, repoId) {
|
|
87
|
-
// Extract naming pattern ID from convention key (e.g., "naming-function-camelCase" → "camelCase")
|
|
88
|
-
const parts = convention.key.split("-");
|
|
89
|
-
const patternId = parts[parts.length - 1];
|
|
90
|
-
const entityKind = parts.slice(1, -1).join("-");
|
|
91
|
-
const query = NAMING_QUERIES[patternId] ?? "";
|
|
92
|
-
const glob = KIND_TO_GLOB[entityKind] ?? "**/*.{ts,tsx,js,jsx}";
|
|
93
|
-
return {
|
|
94
|
-
key: ruleKey,
|
|
95
|
-
name: convention.name,
|
|
96
|
-
scope: "repo",
|
|
97
|
-
severity: "warn",
|
|
98
|
-
engine: "structural",
|
|
99
|
-
query,
|
|
100
|
-
message: `Convention: ${convention.name} (${Math.round(convention.confidence * 100)}% adherence). ${convention.detail}`,
|
|
101
|
-
file_glob: glob,
|
|
102
|
-
enabled: true,
|
|
103
|
-
repo_id: repoId,
|
|
104
|
-
status: "active",
|
|
105
|
-
target_kinds: entityKind === "component" ? "function" : entityKind,
|
|
106
|
-
ast_grep_fix: "",
|
|
107
|
-
example: "",
|
|
108
|
-
decay_score: 0,
|
|
109
|
-
evaluations: 0,
|
|
110
|
-
overrides: 0,
|
|
111
|
-
};
|
|
112
|
-
}
|
|
113
|
-
function structureConventionToRule(convention, ruleKey, repoId) {
|
|
114
|
-
return {
|
|
115
|
-
key: ruleKey,
|
|
116
|
-
name: convention.name,
|
|
117
|
-
scope: "repo",
|
|
118
|
-
severity: "info",
|
|
119
|
-
engine: "structural",
|
|
120
|
-
query: "",
|
|
121
|
-
message: `Structure convention: ${convention.detail}`,
|
|
122
|
-
file_glob: "**/*.{ts,tsx,js,jsx}",
|
|
123
|
-
enabled: true,
|
|
124
|
-
repo_id: repoId,
|
|
125
|
-
status: "active",
|
|
126
|
-
target_kinds: "",
|
|
127
|
-
ast_grep_fix: "",
|
|
128
|
-
example: "",
|
|
129
|
-
decay_score: 0,
|
|
130
|
-
evaluations: 0,
|
|
131
|
-
overrides: 0,
|
|
132
|
-
};
|
|
133
|
-
}
|
|
134
|
-
function importDirectionConventionToRule(convention, ruleKey, repoId) {
|
|
135
|
-
return {
|
|
136
|
-
key: ruleKey,
|
|
137
|
-
name: convention.name,
|
|
138
|
-
scope: "repo",
|
|
139
|
-
severity: "warn",
|
|
140
|
-
engine: "structural",
|
|
141
|
-
query: "",
|
|
142
|
-
message: `Import direction convention: ${convention.detail}`,
|
|
143
|
-
file_glob: "**/*.{ts,tsx,js,jsx}",
|
|
144
|
-
enabled: true,
|
|
145
|
-
repo_id: repoId,
|
|
146
|
-
status: "active",
|
|
147
|
-
target_kinds: "",
|
|
148
|
-
ast_grep_fix: "",
|
|
149
|
-
example: "",
|
|
150
|
-
decay_score: 0,
|
|
151
|
-
evaluations: 0,
|
|
152
|
-
overrides: 0,
|
|
153
|
-
};
|
|
154
|
-
}
|
|
@@ -1,213 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Local Snapshot Persistence — Sprint L2.2
|
|
3
|
-
*
|
|
4
|
-
* Saves and loads locally-indexed graphs as msgpack.gz files at
|
|
5
|
-
* `<projectRoot>/.unerr/snapshots/graph.msgpack.gz`. On subsequent boots,
|
|
6
|
-
* loads from snapshot instead of re-indexing. Re-indexes only when
|
|
7
|
-
* source files have changed (mtime-based freshness check).
|
|
8
|
-
*
|
|
9
|
-
* Snapshot format is compatible with SnapshotEnvelope from local-graph.ts,
|
|
10
|
-
* enabling reuse of CozoGraphStore.loadSnapshot() for the load path.
|
|
11
|
-
*/
|
|
12
|
-
import { existsSync, mkdirSync, readFileSync, readdirSync, statSync, writeFileSync, } from "node:fs";
|
|
13
|
-
import { join } from "node:path";
|
|
14
|
-
// ── Constants ────────────────────────────────────────────────────
|
|
15
|
-
/** Snapshot file name within the repo's .unerr/snapshots/ directory */
|
|
16
|
-
const SNAPSHOT_FILENAME = "graph.msgpack.gz";
|
|
17
|
-
/** File extensions to check for freshness (must match local-indexer). */
|
|
18
|
-
const SOURCE_EXTENSIONS = new Set([
|
|
19
|
-
".ts",
|
|
20
|
-
".tsx",
|
|
21
|
-
".js",
|
|
22
|
-
".jsx",
|
|
23
|
-
".mjs",
|
|
24
|
-
".cjs",
|
|
25
|
-
".py",
|
|
26
|
-
".go",
|
|
27
|
-
".java",
|
|
28
|
-
".rs",
|
|
29
|
-
".c",
|
|
30
|
-
".h",
|
|
31
|
-
".cpp",
|
|
32
|
-
".cc",
|
|
33
|
-
".cxx",
|
|
34
|
-
".hpp",
|
|
35
|
-
]);
|
|
36
|
-
/** Directories to skip during freshness scan. */
|
|
37
|
-
const EXCLUDED_DIRS = new Set([
|
|
38
|
-
"node_modules",
|
|
39
|
-
"dist",
|
|
40
|
-
"build",
|
|
41
|
-
"out",
|
|
42
|
-
".git",
|
|
43
|
-
".hg",
|
|
44
|
-
".svn",
|
|
45
|
-
"coverage",
|
|
46
|
-
"__pycache__",
|
|
47
|
-
".mypy_cache",
|
|
48
|
-
".pytest_cache",
|
|
49
|
-
"vendor",
|
|
50
|
-
"target",
|
|
51
|
-
".next",
|
|
52
|
-
".nuxt",
|
|
53
|
-
".output",
|
|
54
|
-
".unerr",
|
|
55
|
-
".cache",
|
|
56
|
-
".turbo",
|
|
57
|
-
".parcel-cache",
|
|
58
|
-
]);
|
|
59
|
-
/** Path-prefix exclusions (relative to projectRoot). See local-indexer.ts. */
|
|
60
|
-
const EXCLUDED_PATH_PREFIXES = [
|
|
61
|
-
".claude/worktrees",
|
|
62
|
-
".cursor/worktrees",
|
|
63
|
-
".idea/worktrees",
|
|
64
|
-
];
|
|
65
|
-
function isExcludedPath(relPath) {
|
|
66
|
-
for (const prefix of EXCLUDED_PATH_PREFIXES) {
|
|
67
|
-
if (relPath === prefix || relPath.startsWith(`${prefix}/`))
|
|
68
|
-
return true;
|
|
69
|
-
}
|
|
70
|
-
return false;
|
|
71
|
-
}
|
|
72
|
-
// ── Public API ───────────────────────────────────────────────────
|
|
73
|
-
/**
|
|
74
|
-
* Generate the snapshot file path for a given project root.
|
|
75
|
-
* Stores within the repo's own .unerr/snapshots/ directory.
|
|
76
|
-
*/
|
|
77
|
-
export function snapshotPath(projectRoot) {
|
|
78
|
-
return join(projectRoot, ".unerr", "snapshots", SNAPSHOT_FILENAME);
|
|
79
|
-
}
|
|
80
|
-
/**
|
|
81
|
-
* Get snapshot metadata (path + existence).
|
|
82
|
-
*/
|
|
83
|
-
export function getSnapshotMeta(projectRoot) {
|
|
84
|
-
const path = snapshotPath(projectRoot);
|
|
85
|
-
return { path, exists: existsSync(path) };
|
|
86
|
-
}
|
|
87
|
-
/**
|
|
88
|
-
* Determine if the project needs re-indexing.
|
|
89
|
-
*
|
|
90
|
-
* Checks against the persistent DB file mtime first (preferred),
|
|
91
|
-
* falls back to snapshot file mtime for backward compatibility.
|
|
92
|
-
*
|
|
93
|
-
* Returns true if:
|
|
94
|
-
* - Neither persistent DB nor snapshot file exists
|
|
95
|
-
* - Any source file has mtime newer than the reference file
|
|
96
|
-
*/
|
|
97
|
-
export function shouldReindex(projectRoot) {
|
|
98
|
-
// Prefer persistent DB mtime as freshness reference
|
|
99
|
-
const dbPath = join(projectRoot, ".unerr", "graph.db");
|
|
100
|
-
const snPath = snapshotPath(projectRoot);
|
|
101
|
-
let referenceMtime = null;
|
|
102
|
-
// Check persistent DB first
|
|
103
|
-
if (existsSync(dbPath)) {
|
|
104
|
-
try {
|
|
105
|
-
referenceMtime = statSync(dbPath).mtimeMs;
|
|
106
|
-
}
|
|
107
|
-
catch {
|
|
108
|
-
// fall through
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
// Fall back to snapshot
|
|
112
|
-
if (referenceMtime === null && existsSync(snPath)) {
|
|
113
|
-
try {
|
|
114
|
-
referenceMtime = statSync(snPath).mtimeMs;
|
|
115
|
-
}
|
|
116
|
-
catch {
|
|
117
|
-
// fall through
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
// No reference — definitely needs indexing
|
|
121
|
-
if (referenceMtime === null)
|
|
122
|
-
return true;
|
|
123
|
-
return hasNewerSource(projectRoot, referenceMtime);
|
|
124
|
-
}
|
|
125
|
-
/**
|
|
126
|
-
* Save a local snapshot to disk.
|
|
127
|
-
*
|
|
128
|
-
* Creates the snapshots directory if needed. Writes a SnapshotEnvelope
|
|
129
|
-
* as gzipped msgpack.
|
|
130
|
-
*/
|
|
131
|
-
export async function persistLocalSnapshot(projectRoot, repoId, entities, edges) {
|
|
132
|
-
const snapshotsDir = join(projectRoot, ".unerr", "snapshots");
|
|
133
|
-
mkdirSync(snapshotsDir, { recursive: true });
|
|
134
|
-
const envelope = {
|
|
135
|
-
version: 1,
|
|
136
|
-
repoId,
|
|
137
|
-
orgId: "local",
|
|
138
|
-
entities,
|
|
139
|
-
edges,
|
|
140
|
-
generatedAt: new Date().toISOString(),
|
|
141
|
-
};
|
|
142
|
-
const { pack } = await import("msgpackr");
|
|
143
|
-
const { gzipSync } = await import("node:zlib");
|
|
144
|
-
const packed = pack(envelope);
|
|
145
|
-
const compressed = gzipSync(packed);
|
|
146
|
-
const path = snapshotPath(projectRoot);
|
|
147
|
-
writeFileSync(path, compressed);
|
|
148
|
-
return path;
|
|
149
|
-
}
|
|
150
|
-
/**
|
|
151
|
-
* Load a local snapshot from disk into a CozoGraphStore.
|
|
152
|
-
*
|
|
153
|
-
* Returns true if snapshot was loaded, false if no snapshot exists.
|
|
154
|
-
*/
|
|
155
|
-
export async function loadLocalSnapshot(projectRoot, graphStore) {
|
|
156
|
-
const path = snapshotPath(projectRoot);
|
|
157
|
-
if (!existsSync(path))
|
|
158
|
-
return false;
|
|
159
|
-
try {
|
|
160
|
-
const { unpack } = await import("msgpackr");
|
|
161
|
-
const { gunzipSync } = await import("node:zlib");
|
|
162
|
-
const raw = readFileSync(path);
|
|
163
|
-
const buffer = gunzipSync(raw);
|
|
164
|
-
const envelope = unpack(buffer);
|
|
165
|
-
await graphStore.loadSnapshot(envelope);
|
|
166
|
-
return true;
|
|
167
|
-
}
|
|
168
|
-
catch {
|
|
169
|
-
return false;
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
// ── Internal ─────────────────────────────────────────────────────
|
|
173
|
-
/**
|
|
174
|
-
* Scan project directory for any source file newer than the given mtime.
|
|
175
|
-
* Returns true as soon as one is found (short-circuit for performance).
|
|
176
|
-
*/
|
|
177
|
-
function hasNewerSource(projectRoot, snapshotMtime) {
|
|
178
|
-
return scanDirForNewer(projectRoot, snapshotMtime);
|
|
179
|
-
}
|
|
180
|
-
function scanDirForNewer(dir, threshold) {
|
|
181
|
-
let entries;
|
|
182
|
-
try {
|
|
183
|
-
entries = readdirSync(dir);
|
|
184
|
-
}
|
|
185
|
-
catch {
|
|
186
|
-
return false;
|
|
187
|
-
}
|
|
188
|
-
for (const entry of entries) {
|
|
189
|
-
if (EXCLUDED_DIRS.has(entry))
|
|
190
|
-
continue;
|
|
191
|
-
if (entry.startsWith(".") && entry !== ".")
|
|
192
|
-
continue;
|
|
193
|
-
const fullPath = join(dir, entry);
|
|
194
|
-
let stat;
|
|
195
|
-
try {
|
|
196
|
-
stat = statSync(fullPath);
|
|
197
|
-
}
|
|
198
|
-
catch {
|
|
199
|
-
continue;
|
|
200
|
-
}
|
|
201
|
-
if (stat.isDirectory()) {
|
|
202
|
-
if (scanDirForNewer(fullPath, threshold))
|
|
203
|
-
return true;
|
|
204
|
-
}
|
|
205
|
-
else if (stat.isFile()) {
|
|
206
|
-
const ext = fullPath.slice(fullPath.lastIndexOf(".")).toLowerCase();
|
|
207
|
-
if (SOURCE_EXTENSIONS.has(ext) && stat.mtimeMs > threshold) {
|
|
208
|
-
return true;
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
return false;
|
|
213
|
-
}
|
|
@@ -1,103 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Negative Knowledge — learns from rewinds and reverts.
|
|
3
|
-
*
|
|
4
|
-
* When a rewind occurs (detected from shadow ledger), analyzes what went wrong:
|
|
5
|
-
* - Entity modified then reverted → the modification pattern is an anti-pattern
|
|
6
|
-
* - Stores entity key + error description as a correction entry
|
|
7
|
-
*
|
|
8
|
-
* Corrections feed into convention enforcement (H.4) and session context (C.2).
|
|
9
|
-
* No LLM required — pure heuristic analysis from ledger + diff data.
|
|
10
|
-
*
|
|
11
|
-
* Temporal intelligence note: corrections are procedural memory (Section 13.2)
|
|
12
|
-
* with near-permanent decay — only overwritten by demonstrated evolution.
|
|
13
|
-
*/
|
|
14
|
-
/**
|
|
15
|
-
* Detect anti-patterns from a rewind event in the ledger.
|
|
16
|
-
*
|
|
17
|
-
* Scans entries before the rewind to find what was modified,
|
|
18
|
-
* then generates correction entries for the reverted changes.
|
|
19
|
-
*/
|
|
20
|
-
export function detectAntiPatterns(entries, rewindEntryId) {
|
|
21
|
-
const rewindIdx = entries.findIndex((e) => e.id === rewindEntryId);
|
|
22
|
-
if (rewindIdx < 0)
|
|
23
|
-
return [];
|
|
24
|
-
const rewindEntry = entries[rewindIdx];
|
|
25
|
-
const rewindResult = rewindEntry.result_summary;
|
|
26
|
-
const targetId = rewindResult.rewind_target_id;
|
|
27
|
-
if (!targetId)
|
|
28
|
-
return [];
|
|
29
|
-
const targetIdx = entries.findIndex((e) => e.id === targetId);
|
|
30
|
-
if (targetIdx < 0)
|
|
31
|
-
return [];
|
|
32
|
-
const revertedEntries = entries.slice(targetIdx + 1, rewindIdx);
|
|
33
|
-
const corrections = [];
|
|
34
|
-
const seenEntities = new Set();
|
|
35
|
-
for (const entry of revertedEntries) {
|
|
36
|
-
if (entry.tool !== "sync_local_diff")
|
|
37
|
-
continue;
|
|
38
|
-
const files = extractFiles(entry.args_summary);
|
|
39
|
-
for (const file of files) {
|
|
40
|
-
if (seenEntities.has(file))
|
|
41
|
-
continue;
|
|
42
|
-
seenEntities.add(file);
|
|
43
|
-
corrections.push({
|
|
44
|
-
id: `correction-${rewindEntryId}-${corrections.length}`,
|
|
45
|
-
entityKey: file,
|
|
46
|
-
pattern: "modified-then-reverted",
|
|
47
|
-
reason: `File ${file} was modified and subsequently reverted. The modification approach was incorrect.`,
|
|
48
|
-
detectedAt: new Date().toISOString(),
|
|
49
|
-
rewindEntryId,
|
|
50
|
-
confidence: 0.7,
|
|
51
|
-
});
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
return corrections;
|
|
55
|
-
}
|
|
56
|
-
/**
|
|
57
|
-
* Detect repeated modification patterns that indicate instability.
|
|
58
|
-
* Entity modified 3+ times in rapid succession → likely anti-pattern.
|
|
59
|
-
*/
|
|
60
|
-
export function detectInstableEntities(entries, windowMs = 10 * 60 * 1000) {
|
|
61
|
-
const entityTimestamps = new Map();
|
|
62
|
-
for (const entry of entries) {
|
|
63
|
-
if (entry.tool !== "sync_local_diff")
|
|
64
|
-
continue;
|
|
65
|
-
const files = extractFiles(entry.args_summary);
|
|
66
|
-
const ts = new Date(entry.ts).getTime();
|
|
67
|
-
for (const file of files) {
|
|
68
|
-
const timestamps = entityTimestamps.get(file) ?? [];
|
|
69
|
-
timestamps.push(ts);
|
|
70
|
-
entityTimestamps.set(file, timestamps);
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
const corrections = [];
|
|
74
|
-
for (const [entityKey, timestamps] of entityTimestamps) {
|
|
75
|
-
if (timestamps.length < 3)
|
|
76
|
-
continue;
|
|
77
|
-
timestamps.sort((a, b) => a - b);
|
|
78
|
-
for (let i = 2; i < timestamps.length; i++) {
|
|
79
|
-
const window = timestamps[i] - timestamps[i - 2];
|
|
80
|
-
if (window < windowMs) {
|
|
81
|
-
corrections.push({
|
|
82
|
-
id: `instability-${entityKey}-${i}`,
|
|
83
|
-
entityKey,
|
|
84
|
-
pattern: "rapid-modification",
|
|
85
|
-
reason: `${entityKey} was modified ${timestamps.length} times in rapid succession, suggesting instability.`,
|
|
86
|
-
detectedAt: new Date().toISOString(),
|
|
87
|
-
rewindEntryId: "",
|
|
88
|
-
confidence: 0.5 + Math.min(0.4, timestamps.length * 0.05),
|
|
89
|
-
});
|
|
90
|
-
break;
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
return corrections;
|
|
95
|
-
}
|
|
96
|
-
function extractFiles(args) {
|
|
97
|
-
const files = args.files;
|
|
98
|
-
if (!Array.isArray(files))
|
|
99
|
-
return [];
|
|
100
|
-
return files
|
|
101
|
-
.map((f) => typeof f === "string" ? f : (f?.path ?? ""))
|
|
102
|
-
.filter(Boolean);
|
|
103
|
-
}
|