@unerr-ai/unerr 0.2.0 → 0.2.2
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 +6 -0
- package/dist/cli.js +37236 -35793
- package/package.json +6 -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,259 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* HealthMapData — builds a hierarchical health map of the codebase.
|
|
3
|
-
*
|
|
4
|
-
* Combines CozoDB graph metrics (fan_in, fan_out, risk_level) with
|
|
5
|
-
* temporal facts (change frequency, coupling count, durability) to
|
|
6
|
-
* produce a per-file/per-directory health score.
|
|
7
|
-
*
|
|
8
|
-
* Part of Layer C of the Three-Layer Experience System.
|
|
9
|
-
*/
|
|
10
|
-
/**
|
|
11
|
-
* Compute health score from metrics.
|
|
12
|
-
*
|
|
13
|
-
* Formula: 0.3*(1-normalized_risk) + 0.2*durability + 0.2*convention_adherence
|
|
14
|
-
* + 0.15*(1-normalized_coupling) + 0.15*(1-normalized_change_frequency)
|
|
15
|
-
*/
|
|
16
|
-
function computeHealthScore(metrics) {
|
|
17
|
-
const normalizedRisk = Math.min((metrics.fan_in + metrics.fan_out) / 40, 1.0);
|
|
18
|
-
const normalizedCoupling = Math.min(metrics.coupling_count / 10, 1.0);
|
|
19
|
-
const normalizedChangeFreq = Math.min(metrics.change_frequency / 10, 1.0);
|
|
20
|
-
return (0.3 * (1 - normalizedRisk) +
|
|
21
|
-
0.2 * metrics.durability +
|
|
22
|
-
0.2 * metrics.convention_adherence +
|
|
23
|
-
0.15 * (1 - normalizedCoupling) +
|
|
24
|
-
0.15 * (1 - normalizedChangeFreq));
|
|
25
|
-
}
|
|
26
|
-
/** Map health score to risk level. */
|
|
27
|
-
function scoreToRisk(score) {
|
|
28
|
-
if (score >= 0.75)
|
|
29
|
-
return "low";
|
|
30
|
-
if (score >= 0.5)
|
|
31
|
-
return "medium";
|
|
32
|
-
if (score >= 0.25)
|
|
33
|
-
return "high";
|
|
34
|
-
return "critical";
|
|
35
|
-
}
|
|
36
|
-
export class HealthMapData {
|
|
37
|
-
localGraph;
|
|
38
|
-
factStore;
|
|
39
|
-
constructor(localGraph, factStore) {
|
|
40
|
-
this.localGraph = localGraph;
|
|
41
|
-
this.factStore = factStore;
|
|
42
|
-
}
|
|
43
|
-
/**
|
|
44
|
-
* Build a hierarchical health map tree for the codebase.
|
|
45
|
-
* Groups entities by directory, computes per-file and per-directory health.
|
|
46
|
-
*/
|
|
47
|
-
async buildTree(rootPath) {
|
|
48
|
-
// Get all entities from graph
|
|
49
|
-
const stats = await this.localGraph.getLocalProjectStats();
|
|
50
|
-
const criticalNodes = await this.localGraph.getCriticalNodes(500);
|
|
51
|
-
// Build entity-to-file map
|
|
52
|
-
const fileEntities = new Map();
|
|
53
|
-
for (const entity of criticalNodes) {
|
|
54
|
-
const filePath = entity.file_path;
|
|
55
|
-
if (rootPath && !filePath.startsWith(rootPath))
|
|
56
|
-
continue;
|
|
57
|
-
const existing = fileEntities.get(filePath);
|
|
58
|
-
if (existing) {
|
|
59
|
-
existing.push(entity);
|
|
60
|
-
}
|
|
61
|
-
else {
|
|
62
|
-
fileEntities.set(filePath, [entity]);
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
// Get coupling facts for all files
|
|
66
|
-
const couplingCounts = new Map();
|
|
67
|
-
const changeCounts = new Map();
|
|
68
|
-
if (this.factStore) {
|
|
69
|
-
try {
|
|
70
|
-
const projectFacts = await this.factStore.recallByScope("project", 0.0);
|
|
71
|
-
for (const fact of projectFacts) {
|
|
72
|
-
if (fact.subject.startsWith("coupling:")) {
|
|
73
|
-
const pair = fact.subject.replace("coupling:", "");
|
|
74
|
-
const files = pair.split("↔");
|
|
75
|
-
for (const f of files) {
|
|
76
|
-
if (f)
|
|
77
|
-
couplingCounts.set(f, (couplingCounts.get(f) ?? 0) + 1);
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
// Count change frequency from episodic facts per file
|
|
82
|
-
for (const filePath of fileEntities.keys()) {
|
|
83
|
-
try {
|
|
84
|
-
const fileFacts = await this.factStore.recallForFile(filePath);
|
|
85
|
-
const episodic = fileFacts.filter((f) => f.fact_type === "episodic");
|
|
86
|
-
if (episodic.length > 0) {
|
|
87
|
-
changeCounts.set(filePath, episodic.length);
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
catch {
|
|
91
|
-
// Non-critical
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
catch {
|
|
96
|
-
// Non-critical — proceed with graph-only data
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
// Get convention adherence
|
|
100
|
-
let avgAdherence = 0.8; // Default
|
|
101
|
-
try {
|
|
102
|
-
const conventions = await this.localGraph.getConventions();
|
|
103
|
-
if (conventions && conventions.length > 0) {
|
|
104
|
-
avgAdherence =
|
|
105
|
-
conventions.reduce((sum, c) => sum + c.adherence_rate, 0) /
|
|
106
|
-
conventions.length /
|
|
107
|
-
100;
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
catch {
|
|
111
|
-
// Non-critical
|
|
112
|
-
}
|
|
113
|
-
// Build file nodes
|
|
114
|
-
const fileNodes = [];
|
|
115
|
-
for (const [filePath, entities] of fileEntities) {
|
|
116
|
-
const totalFanIn = entities.reduce((s, e) => s + e.fan_in, 0);
|
|
117
|
-
const totalFanOut = entities.reduce((s, e) => s + e.fan_out, 0);
|
|
118
|
-
const highRiskCount = entities.filter((e) => e.risk_level === "high" || e.risk_level === "critical").length;
|
|
119
|
-
const metrics = {
|
|
120
|
-
fan_in: totalFanIn,
|
|
121
|
-
fan_out: totalFanOut,
|
|
122
|
-
durability: highRiskCount > 0 ? Math.max(0.2, 1 - highRiskCount * 0.15) : 0.8,
|
|
123
|
-
convention_adherence: avgAdherence,
|
|
124
|
-
change_frequency: changeCounts.get(filePath) ?? 0,
|
|
125
|
-
coupling_count: couplingCounts.get(filePath) ?? 0,
|
|
126
|
-
};
|
|
127
|
-
const healthScore = computeHealthScore(metrics);
|
|
128
|
-
const name = filePath.split("/").pop() ?? filePath;
|
|
129
|
-
fileNodes.push({
|
|
130
|
-
path: filePath,
|
|
131
|
-
name,
|
|
132
|
-
health_score: Math.round(healthScore * 100) / 100,
|
|
133
|
-
risk_level: scoreToRisk(healthScore),
|
|
134
|
-
metrics,
|
|
135
|
-
entity_count: entities.length,
|
|
136
|
-
});
|
|
137
|
-
}
|
|
138
|
-
// Group into directory tree
|
|
139
|
-
const dirMap = new Map();
|
|
140
|
-
for (const node of fileNodes) {
|
|
141
|
-
const dir = node.path.split("/").slice(0, -1).join("/") || ".";
|
|
142
|
-
const existing = dirMap.get(dir);
|
|
143
|
-
if (existing) {
|
|
144
|
-
existing.push(node);
|
|
145
|
-
}
|
|
146
|
-
else {
|
|
147
|
-
dirMap.set(dir, [node]);
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
// Build directory nodes
|
|
151
|
-
const dirNodes = [];
|
|
152
|
-
for (const [dirPath, children] of dirMap) {
|
|
153
|
-
const dirHealth = children.reduce((s, c) => s + c.health_score, 0) / children.length;
|
|
154
|
-
const totalEntities = children.reduce((s, c) => s + c.entity_count, 0);
|
|
155
|
-
const aggregatedMetrics = {
|
|
156
|
-
fan_in: children.reduce((s, c) => s + c.metrics.fan_in, 0),
|
|
157
|
-
fan_out: children.reduce((s, c) => s + c.metrics.fan_out, 0),
|
|
158
|
-
durability: children.reduce((s, c) => s + c.metrics.durability, 0) /
|
|
159
|
-
children.length,
|
|
160
|
-
convention_adherence: children.reduce((s, c) => s + c.metrics.convention_adherence, 0) /
|
|
161
|
-
children.length,
|
|
162
|
-
change_frequency: children.reduce((s, c) => s + c.metrics.change_frequency, 0),
|
|
163
|
-
coupling_count: children.reduce((s, c) => s + c.metrics.coupling_count, 0),
|
|
164
|
-
};
|
|
165
|
-
dirNodes.push({
|
|
166
|
-
path: dirPath,
|
|
167
|
-
name: dirPath.split("/").pop() ?? dirPath,
|
|
168
|
-
health_score: Math.round(dirHealth * 100) / 100,
|
|
169
|
-
risk_level: scoreToRisk(dirHealth),
|
|
170
|
-
metrics: aggregatedMetrics,
|
|
171
|
-
entity_count: totalEntities,
|
|
172
|
-
children: children.sort((a, b) => a.health_score - b.health_score),
|
|
173
|
-
});
|
|
174
|
-
}
|
|
175
|
-
// Root node
|
|
176
|
-
const rootHealth = dirNodes.length > 0
|
|
177
|
-
? dirNodes.reduce((s, d) => s + d.health_score, 0) / dirNodes.length
|
|
178
|
-
: 0.5;
|
|
179
|
-
const totalEntities = dirNodes.reduce((s, d) => s + d.entity_count, 0);
|
|
180
|
-
return {
|
|
181
|
-
path: rootPath ?? ".",
|
|
182
|
-
name: rootPath?.split("/").pop() ?? "project",
|
|
183
|
-
health_score: Math.round(rootHealth * 100) / 100,
|
|
184
|
-
risk_level: scoreToRisk(rootHealth),
|
|
185
|
-
metrics: {
|
|
186
|
-
fan_in: dirNodes.reduce((s, d) => s + d.metrics.fan_in, 0),
|
|
187
|
-
fan_out: dirNodes.reduce((s, d) => s + d.metrics.fan_out, 0),
|
|
188
|
-
durability: dirNodes.length > 0
|
|
189
|
-
? dirNodes.reduce((s, d) => s + d.metrics.durability, 0) /
|
|
190
|
-
dirNodes.length
|
|
191
|
-
: 0.5,
|
|
192
|
-
convention_adherence: avgAdherence,
|
|
193
|
-
change_frequency: dirNodes.reduce((s, d) => s + d.metrics.change_frequency, 0),
|
|
194
|
-
coupling_count: dirNodes.reduce((s, d) => s + d.metrics.coupling_count, 0),
|
|
195
|
-
},
|
|
196
|
-
entity_count: totalEntities,
|
|
197
|
-
children: dirNodes.sort((a, b) => a.health_score - b.health_score),
|
|
198
|
-
};
|
|
199
|
-
}
|
|
200
|
-
/**
|
|
201
|
-
* Get health data for a single file.
|
|
202
|
-
*/
|
|
203
|
-
async getFileHealth(filePath) {
|
|
204
|
-
const entities = await this.localGraph.getEntitiesByFile(filePath);
|
|
205
|
-
if (entities.length === 0)
|
|
206
|
-
return null;
|
|
207
|
-
const totalFanIn = entities.reduce((s, e) => s + e.fan_in, 0);
|
|
208
|
-
const totalFanOut = entities.reduce((s, e) => s + e.fan_out, 0);
|
|
209
|
-
const highRiskCount = entities.filter((e) => e.risk_level === "high" || e.risk_level === "critical").length;
|
|
210
|
-
let couplingCount = 0;
|
|
211
|
-
let changeFrequency = 0;
|
|
212
|
-
if (this.factStore) {
|
|
213
|
-
try {
|
|
214
|
-
const projectFacts = await this.factStore.recallByScope("project", 0.0);
|
|
215
|
-
for (const fact of projectFacts) {
|
|
216
|
-
if (fact.subject.startsWith("coupling:") &&
|
|
217
|
-
fact.subject.includes(filePath)) {
|
|
218
|
-
couplingCount++;
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
const fileFacts = await this.factStore.recallForFile(filePath);
|
|
222
|
-
changeFrequency = fileFacts.filter((f) => f.fact_type === "episodic").length;
|
|
223
|
-
}
|
|
224
|
-
catch {
|
|
225
|
-
// Non-critical
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
let conventionAdherence = 0.8;
|
|
229
|
-
try {
|
|
230
|
-
const conventions = await this.localGraph.getConventions();
|
|
231
|
-
if (conventions && conventions.length > 0) {
|
|
232
|
-
conventionAdherence =
|
|
233
|
-
conventions.reduce((s, c) => s + c.adherence_rate, 0) /
|
|
234
|
-
conventions.length /
|
|
235
|
-
100;
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
catch {
|
|
239
|
-
// Non-critical
|
|
240
|
-
}
|
|
241
|
-
const metrics = {
|
|
242
|
-
fan_in: totalFanIn,
|
|
243
|
-
fan_out: totalFanOut,
|
|
244
|
-
durability: highRiskCount > 0 ? Math.max(0.2, 1 - highRiskCount * 0.15) : 0.8,
|
|
245
|
-
convention_adherence: conventionAdherence,
|
|
246
|
-
change_frequency: changeFrequency,
|
|
247
|
-
coupling_count: couplingCount,
|
|
248
|
-
};
|
|
249
|
-
const healthScore = computeHealthScore(metrics);
|
|
250
|
-
return {
|
|
251
|
-
path: filePath,
|
|
252
|
-
name: filePath.split("/").pop() ?? filePath,
|
|
253
|
-
health_score: Math.round(healthScore * 100) / 100,
|
|
254
|
-
risk_level: scoreToRisk(healthScore),
|
|
255
|
-
metrics,
|
|
256
|
-
entity_count: entities.length,
|
|
257
|
-
};
|
|
258
|
-
}
|
|
259
|
-
}
|
|
@@ -1,136 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Lightweight, regex-based import-symbol extractor for the `get_imports` MCP
|
|
3
|
-
* tool. The graph stores file→file import edges but does NOT persist the
|
|
4
|
-
* imported symbol names; capturing them at the entity level would require a
|
|
5
|
-
* schema change. This module reads the source file on demand and pairs each
|
|
6
|
-
* resolved module path with the symbols imported from it.
|
|
7
|
-
*
|
|
8
|
-
* Languages covered: TypeScript / JavaScript (ESM `import` and CommonJS
|
|
9
|
-
* `require`). For other languages the call returns an empty map and the
|
|
10
|
-
* handler falls back to path-only output — identical to legacy behavior.
|
|
11
|
-
*/
|
|
12
|
-
import { promises as fs } from "node:fs";
|
|
13
|
-
import { isAbsolute, resolve } from "node:path";
|
|
14
|
-
const ESM_IMPORT_RE = /import\s+(?:type\s+)?(?:([^"';]*?)\s+from\s+)?["']([^"']+)["']/g;
|
|
15
|
-
const REQUIRE_RE = /(?:const|let|var)\s+([^=]+?)\s*=\s*require\s*\(\s*["']([^"']+)["']\s*\)/g;
|
|
16
|
-
function parseClause(clause) {
|
|
17
|
-
// clause can be:
|
|
18
|
-
// "X" — default import
|
|
19
|
-
// "* as X" — namespace import
|
|
20
|
-
// "{ a, b as c }" — named imports
|
|
21
|
-
// "X, { a, b }" — default + named
|
|
22
|
-
// "X, * as Ns" — default + namespace
|
|
23
|
-
const symbols = [];
|
|
24
|
-
const trimmed = clause.trim();
|
|
25
|
-
if (!trimmed)
|
|
26
|
-
return symbols;
|
|
27
|
-
// Split off the named-imports block if present.
|
|
28
|
-
const braceStart = trimmed.indexOf("{");
|
|
29
|
-
let head = trimmed;
|
|
30
|
-
let braced = "";
|
|
31
|
-
if (braceStart !== -1) {
|
|
32
|
-
const braceEnd = trimmed.indexOf("}", braceStart);
|
|
33
|
-
if (braceEnd !== -1) {
|
|
34
|
-
head = trimmed.slice(0, braceStart).replace(/,\s*$/, "").trim();
|
|
35
|
-
braced = trimmed.slice(braceStart + 1, braceEnd);
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
if (head) {
|
|
39
|
-
for (const part of head.split(",")) {
|
|
40
|
-
const piece = part.trim();
|
|
41
|
-
if (!piece)
|
|
42
|
-
continue;
|
|
43
|
-
const nsMatch = piece.match(/^\*\s+as\s+(\w+)$/);
|
|
44
|
-
if (nsMatch)
|
|
45
|
-
symbols.push(`* as ${nsMatch[1]}`);
|
|
46
|
-
else if (/^\w+$/.test(piece))
|
|
47
|
-
symbols.push(piece);
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
if (braced) {
|
|
51
|
-
for (const item of braced.split(",")) {
|
|
52
|
-
const piece = item.trim().replace(/^type\s+/, "");
|
|
53
|
-
if (!piece)
|
|
54
|
-
continue;
|
|
55
|
-
// "a as b" — record the original (exported) name.
|
|
56
|
-
const original = piece.split(/\s+as\s+/)[0]?.trim();
|
|
57
|
-
if (original && /^\w+$/.test(original))
|
|
58
|
-
symbols.push(original);
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
return symbols;
|
|
62
|
-
}
|
|
63
|
-
function parseRequireClause(clause) {
|
|
64
|
-
// Patterns:
|
|
65
|
-
// "X" — const X = require(...)
|
|
66
|
-
// "{ a, b: c }" — destructured
|
|
67
|
-
// "{ a, b: c } = ..." — already trimmed by caller
|
|
68
|
-
const symbols = [];
|
|
69
|
-
const trimmed = clause.trim();
|
|
70
|
-
if (!trimmed)
|
|
71
|
-
return symbols;
|
|
72
|
-
if (trimmed.startsWith("{") && trimmed.endsWith("}")) {
|
|
73
|
-
for (const item of trimmed.slice(1, -1).split(",")) {
|
|
74
|
-
const piece = item.trim();
|
|
75
|
-
if (!piece)
|
|
76
|
-
continue;
|
|
77
|
-
const original = piece.split(/\s*:\s*/)[0]?.trim();
|
|
78
|
-
if (original && /^\w+$/.test(original))
|
|
79
|
-
symbols.push(original);
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
else if (/^\w+$/.test(trimmed)) {
|
|
83
|
-
symbols.push(trimmed);
|
|
84
|
-
}
|
|
85
|
-
return symbols;
|
|
86
|
-
}
|
|
87
|
-
/**
|
|
88
|
-
* Parse import statements from a TS/JS source string. Returns a map keyed by
|
|
89
|
-
* the literal source path/module specifier exactly as written. Comments and
|
|
90
|
-
* strings outside import statements are tolerated; the regex anchors on
|
|
91
|
-
* `import ... from "..."` and `require("...")` so false positives are rare.
|
|
92
|
-
*/
|
|
93
|
-
export function parseImportSymbols(source) {
|
|
94
|
-
const out = new Map();
|
|
95
|
-
const append = (key, syms) => {
|
|
96
|
-
const existing = out.get(key);
|
|
97
|
-
if (existing)
|
|
98
|
-
existing.push(...syms);
|
|
99
|
-
else
|
|
100
|
-
out.set(key, syms.slice());
|
|
101
|
-
};
|
|
102
|
-
// Strip line and block comments so they can't trigger the regex (cheap pass).
|
|
103
|
-
const stripped = source
|
|
104
|
-
.replace(/\/\*[\s\S]*?\*\//g, "")
|
|
105
|
-
.replace(/^\s*\/\/.*$/gm, "");
|
|
106
|
-
for (const m of stripped.matchAll(ESM_IMPORT_RE)) {
|
|
107
|
-
const clause = m[1] ?? "";
|
|
108
|
-
const src = m[2];
|
|
109
|
-
if (!src)
|
|
110
|
-
continue;
|
|
111
|
-
append(src, parseClause(clause));
|
|
112
|
-
}
|
|
113
|
-
for (const m of stripped.matchAll(REQUIRE_RE)) {
|
|
114
|
-
const clause = m[1] ?? "";
|
|
115
|
-
const src = m[2];
|
|
116
|
-
if (!src)
|
|
117
|
-
continue;
|
|
118
|
-
append(src, parseRequireClause(clause));
|
|
119
|
-
}
|
|
120
|
-
return out;
|
|
121
|
-
}
|
|
122
|
-
/**
|
|
123
|
-
* Best-effort load of a source file and parse its imports. Returns an empty
|
|
124
|
-
* map on any IO/parse failure — get_imports must remain useful even when the
|
|
125
|
-
* file moved or the language isn't TS/JS.
|
|
126
|
-
*/
|
|
127
|
-
export async function loadImportSymbols(projectRoot, filePath) {
|
|
128
|
-
const abs = isAbsolute(filePath) ? filePath : resolve(projectRoot, filePath);
|
|
129
|
-
try {
|
|
130
|
-
const text = await fs.readFile(abs, "utf8");
|
|
131
|
-
return parseImportSymbols(text);
|
|
132
|
-
}
|
|
133
|
-
catch {
|
|
134
|
-
return new Map();
|
|
135
|
-
}
|
|
136
|
-
}
|