@unerr-ai/unerr 0.2.1 → 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 +1 -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,208 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Session Continuity Protocol — BA-1.2
|
|
3
|
-
*
|
|
4
|
-
* On session start (first tool call), queries the shadow ledger for
|
|
5
|
-
* the previous session's entries, identifies incomplete work chains,
|
|
6
|
-
* and injects structured resume context into the response.
|
|
7
|
-
*
|
|
8
|
-
* Incomplete work detection:
|
|
9
|
-
* - Entities modified but callers not updated (from blast radius)
|
|
10
|
-
* - Files with uncommitted changes at session end
|
|
11
|
-
* - Entities that were being actively worked on (high tool call count)
|
|
12
|
-
*
|
|
13
|
-
* For complex resumes (>50 ledger entries), generates an agent-as-LLM
|
|
14
|
-
* sub-prompt requesting the host agent summarize the raw data.
|
|
15
|
-
*/
|
|
16
|
-
import { Behavior, } from "./framework.js";
|
|
17
|
-
const AGENT_LLM_THRESHOLD = 50;
|
|
18
|
-
const MAX_RESUME_ITEMS = 5;
|
|
19
|
-
export class SessionContinuityBehavior extends Behavior {
|
|
20
|
-
id = "session_continuity";
|
|
21
|
-
hooks = ["session_start"];
|
|
22
|
-
defaultLevel = "suggestion";
|
|
23
|
-
ledger = null;
|
|
24
|
-
continuityConfig;
|
|
25
|
-
constructor(config) {
|
|
26
|
-
super(config, "suggestion");
|
|
27
|
-
this.continuityConfig = {
|
|
28
|
-
enabled: true,
|
|
29
|
-
level: "suggestion",
|
|
30
|
-
includeIncompleteWork: true,
|
|
31
|
-
maxResumeItems: MAX_RESUME_ITEMS,
|
|
32
|
-
...config,
|
|
33
|
-
};
|
|
34
|
-
}
|
|
35
|
-
attachLedger(ledger) {
|
|
36
|
-
this.ledger = ledger;
|
|
37
|
-
}
|
|
38
|
-
async onSessionStart(ctx) {
|
|
39
|
-
if (!this.ledger)
|
|
40
|
-
return null;
|
|
41
|
-
const allEntries = this.ledger.readAllEntries();
|
|
42
|
-
if (allEntries.length === 0)
|
|
43
|
-
return null;
|
|
44
|
-
const previousSessionEntries = extractPreviousSession(allEntries, ctx.sessionId);
|
|
45
|
-
if (previousSessionEntries.length === 0)
|
|
46
|
-
return null;
|
|
47
|
-
const resume = buildResumePayload(previousSessionEntries, this.continuityConfig.maxResumeItems);
|
|
48
|
-
if (previousSessionEntries.length > AGENT_LLM_THRESHOLD) {
|
|
49
|
-
resume.use_agent_llm = true;
|
|
50
|
-
resume.agent_llm_prompt = buildAgentLlmPrompt(resume);
|
|
51
|
-
}
|
|
52
|
-
return {
|
|
53
|
-
behaviorId: this.id,
|
|
54
|
-
level: this.level,
|
|
55
|
-
relatedSkillId: "session-context-preservation",
|
|
56
|
-
_meta: {
|
|
57
|
-
behavior: this.id,
|
|
58
|
-
previous_session_entries: previousSessionEntries.length,
|
|
59
|
-
incomplete_items: resume.incomplete_work.length,
|
|
60
|
-
},
|
|
61
|
-
_context: {
|
|
62
|
-
session_resume: resume,
|
|
63
|
-
},
|
|
64
|
-
};
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
function extractPreviousSession(allEntries, currentSessionId) {
|
|
68
|
-
const sessionIds = new Set();
|
|
69
|
-
for (const entry of allEntries) {
|
|
70
|
-
if (entry.session_id !== currentSessionId) {
|
|
71
|
-
sessionIds.add(entry.session_id);
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
if (sessionIds.size === 0)
|
|
75
|
-
return [];
|
|
76
|
-
let lastSessionId = null;
|
|
77
|
-
let lastTimestamp = 0;
|
|
78
|
-
for (const entry of allEntries) {
|
|
79
|
-
if (entry.session_id === currentSessionId)
|
|
80
|
-
continue;
|
|
81
|
-
const ts = new Date(entry.ts).getTime();
|
|
82
|
-
if (ts > lastTimestamp) {
|
|
83
|
-
lastTimestamp = ts;
|
|
84
|
-
lastSessionId = entry.session_id;
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
if (!lastSessionId)
|
|
88
|
-
return [];
|
|
89
|
-
return allEntries.filter((e) => e.session_id === lastSessionId);
|
|
90
|
-
}
|
|
91
|
-
function buildResumePayload(entries, maxItems) {
|
|
92
|
-
const firstTs = new Date(entries[0]?.ts ?? 0).getTime();
|
|
93
|
-
const lastTs = new Date(entries[entries.length - 1]?.ts ?? 0).getTime();
|
|
94
|
-
const durationMs = lastTs - firstTs;
|
|
95
|
-
const elapsedSinceMs = Date.now() - lastTs;
|
|
96
|
-
const filesModified = new Set();
|
|
97
|
-
const entityModifications = new Map();
|
|
98
|
-
const committedEntities = new Set();
|
|
99
|
-
const toolsUsed = new Map();
|
|
100
|
-
let lastBranch = null;
|
|
101
|
-
for (const entry of entries) {
|
|
102
|
-
toolsUsed.set(entry.tool, (toolsUsed.get(entry.tool) ?? 0) + 1);
|
|
103
|
-
if (entry.branch)
|
|
104
|
-
lastBranch = entry.branch;
|
|
105
|
-
const args = entry.args_summary;
|
|
106
|
-
if (args.files && Array.isArray(args.files)) {
|
|
107
|
-
for (const f of args.files) {
|
|
108
|
-
const path = typeof f === "string" ? f : f.path;
|
|
109
|
-
if (path)
|
|
110
|
-
filesModified.add(path);
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
if (typeof args.key === "string" && args.key.includes("/")) {
|
|
114
|
-
const entityKey = args.key;
|
|
115
|
-
filesModified.add(entityKey.includes("::") ? entityKey.split("::")[0] : entityKey);
|
|
116
|
-
entityModifications.set(entityKey, (entityModifications.get(entityKey) ?? 0) + 1);
|
|
117
|
-
}
|
|
118
|
-
if (entry.result_summary?.commit_sha) {
|
|
119
|
-
for (const [key] of entityModifications) {
|
|
120
|
-
committedEntities.add(key);
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
const incompleteEntities = [...entityModifications.entries()]
|
|
125
|
-
.filter(([key]) => !committedEntities.has(key) && key !== "")
|
|
126
|
-
.sort(([, a], [, b]) => b - a);
|
|
127
|
-
const incompleteWork = incompleteEntities
|
|
128
|
-
.slice(0, maxItems)
|
|
129
|
-
.map(([entity, modCount]) => ({
|
|
130
|
-
entity,
|
|
131
|
-
status: `Modified ${modCount} time(s), not committed`,
|
|
132
|
-
remaining: [],
|
|
133
|
-
risk: modCount >= 3
|
|
134
|
-
? "high"
|
|
135
|
-
: modCount >= 2
|
|
136
|
-
? "medium"
|
|
137
|
-
: "low",
|
|
138
|
-
}));
|
|
139
|
-
const uncommittedFiles = [...filesModified]
|
|
140
|
-
.filter((f) => !committedEntities.has(f))
|
|
141
|
-
.slice(0, 10);
|
|
142
|
-
const topTool = [...toolsUsed.entries()].sort(([, a], [, b]) => b - a)[0];
|
|
143
|
-
const summaryParts = [];
|
|
144
|
-
if (topTool)
|
|
145
|
-
summaryParts.push(`Primary activity: ${topTool[0]} (${topTool[1]}x)`);
|
|
146
|
-
if (filesModified.size > 0)
|
|
147
|
-
summaryParts.push(`${filesModified.size} file(s) touched`);
|
|
148
|
-
if (incompleteWork.length > 0)
|
|
149
|
-
summaryParts.push(`${incompleteWork.length} incomplete item(s)`);
|
|
150
|
-
const suggestedNext = incompleteWork.length > 0
|
|
151
|
-
? `Complete outstanding work on ${incompleteWork[0]?.entity} (${incompleteWork[0]?.status})`
|
|
152
|
-
: "No outstanding items from last session — ready for new work.";
|
|
153
|
-
return {
|
|
154
|
-
last_session: {
|
|
155
|
-
when: formatElapsed(elapsedSinceMs),
|
|
156
|
-
duration: formatDuration(durationMs),
|
|
157
|
-
summary: summaryParts.join(". "),
|
|
158
|
-
tool_calls: entries.length,
|
|
159
|
-
},
|
|
160
|
-
incomplete_work: incompleteWork,
|
|
161
|
-
working_state: {
|
|
162
|
-
last_branch: lastBranch,
|
|
163
|
-
files_modified: filesModified.size,
|
|
164
|
-
uncommitted_files: uncommittedFiles,
|
|
165
|
-
},
|
|
166
|
-
suggested_next: suggestedNext,
|
|
167
|
-
};
|
|
168
|
-
}
|
|
169
|
-
function buildAgentLlmPrompt(resume) {
|
|
170
|
-
const parts = [
|
|
171
|
-
"Summarize the following session state for the developer in 2-3 natural sentences.",
|
|
172
|
-
"Focus on: what was being worked on, what's incomplete, and what to do next.",
|
|
173
|
-
"",
|
|
174
|
-
`Last session: ${resume.last_session.when} (${resume.last_session.duration}, ${resume.last_session.tool_calls} tool calls)`,
|
|
175
|
-
`Summary: ${resume.last_session.summary}`,
|
|
176
|
-
];
|
|
177
|
-
if (resume.incomplete_work.length > 0) {
|
|
178
|
-
parts.push("", "Incomplete items:");
|
|
179
|
-
for (const item of resume.incomplete_work) {
|
|
180
|
-
parts.push(` - ${item.entity}: ${item.status} (risk: ${item.risk})`);
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
parts.push("", `Suggested next: ${resume.suggested_next}`);
|
|
184
|
-
return parts.join("\n");
|
|
185
|
-
}
|
|
186
|
-
function formatElapsed(ms) {
|
|
187
|
-
const seconds = Math.floor(ms / 1000);
|
|
188
|
-
if (seconds < 60)
|
|
189
|
-
return `${seconds}s ago`;
|
|
190
|
-
const minutes = Math.floor(seconds / 60);
|
|
191
|
-
if (minutes < 60)
|
|
192
|
-
return `${minutes}m ago`;
|
|
193
|
-
const hours = Math.floor(minutes / 60);
|
|
194
|
-
if (hours < 24)
|
|
195
|
-
return `${hours}h ago`;
|
|
196
|
-
const days = Math.floor(hours / 24);
|
|
197
|
-
return `${days}d ago`;
|
|
198
|
-
}
|
|
199
|
-
function formatDuration(ms) {
|
|
200
|
-
if (ms < 60_000)
|
|
201
|
-
return `${Math.round(ms / 1000)}s`;
|
|
202
|
-
const minutes = Math.round(ms / 60_000);
|
|
203
|
-
if (minutes < 60)
|
|
204
|
-
return `${minutes} min`;
|
|
205
|
-
const hours = Math.floor(minutes / 60);
|
|
206
|
-
const remainingMinutes = minutes % 60;
|
|
207
|
-
return remainingMinutes > 0 ? `${hours}h ${remainingMinutes}m` : `${hours}h`;
|
|
208
|
-
}
|
|
@@ -1,97 +0,0 @@
|
|
|
1
|
-
import * as fs from "node:fs";
|
|
2
|
-
import * as path from "node:path";
|
|
3
|
-
import { gitQuery } from "../utils/exec.js";
|
|
4
|
-
export async function listLocalBranches(cwd) {
|
|
5
|
-
const raw = await gitQuery([
|
|
6
|
-
"branch",
|
|
7
|
-
"-a",
|
|
8
|
-
"--format=%(HEAD)|%(refname:short)|%(upstream:track)|%(committerdate:relative)",
|
|
9
|
-
], cwd);
|
|
10
|
-
if (!raw)
|
|
11
|
-
return [];
|
|
12
|
-
const driftCounts = loadBranchDriftCounts(cwd);
|
|
13
|
-
const branches = [];
|
|
14
|
-
for (const line of raw.split("\n")) {
|
|
15
|
-
const trimmed = line.trim();
|
|
16
|
-
if (!trimmed)
|
|
17
|
-
continue;
|
|
18
|
-
const parts = trimmed.split("|");
|
|
19
|
-
const isCurrent = parts[0] === "*";
|
|
20
|
-
const name = parts[1] ?? "";
|
|
21
|
-
const tracking = parts[2] ?? "";
|
|
22
|
-
const lastCommit = parts[3] ?? "";
|
|
23
|
-
if (!name)
|
|
24
|
-
continue;
|
|
25
|
-
branches.push({
|
|
26
|
-
name,
|
|
27
|
-
isCurrent,
|
|
28
|
-
lastCommit,
|
|
29
|
-
tracking,
|
|
30
|
-
driftEntityCount: driftCounts.get(name) ?? 0,
|
|
31
|
-
});
|
|
32
|
-
}
|
|
33
|
-
return branches;
|
|
34
|
-
}
|
|
35
|
-
function loadBranchDriftCounts(cwd) {
|
|
36
|
-
const counts = new Map();
|
|
37
|
-
const branchDir = path.join(cwd, ".unerr", "state", "branch-overlays");
|
|
38
|
-
if (!fs.existsSync(branchDir))
|
|
39
|
-
return counts;
|
|
40
|
-
try {
|
|
41
|
-
const entries = fs.readdirSync(branchDir, { withFileTypes: true });
|
|
42
|
-
for (const entry of entries) {
|
|
43
|
-
if (!entry.isDirectory())
|
|
44
|
-
continue;
|
|
45
|
-
const overlayPath = path.join(branchDir, entry.name, "overlay_snapshot.json");
|
|
46
|
-
if (!fs.existsSync(overlayPath))
|
|
47
|
-
continue;
|
|
48
|
-
try {
|
|
49
|
-
const raw = fs.readFileSync(overlayPath, "utf-8");
|
|
50
|
-
const snapshot = JSON.parse(raw);
|
|
51
|
-
if (snapshot.branch && snapshot.entities) {
|
|
52
|
-
counts.set(snapshot.branch, snapshot.entities.length);
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
catch {
|
|
56
|
-
// Skip corrupt snapshots
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
catch {
|
|
61
|
-
// Non-blocking
|
|
62
|
-
}
|
|
63
|
-
return counts;
|
|
64
|
-
}
|
|
65
|
-
export function registerBranchesCommand(program) {
|
|
66
|
-
program
|
|
67
|
-
.command("branches")
|
|
68
|
-
.description("Show branches for this repository")
|
|
69
|
-
.action(async () => {
|
|
70
|
-
try {
|
|
71
|
-
const cwd = process.cwd();
|
|
72
|
-
const branches = await listLocalBranches(cwd);
|
|
73
|
-
if (branches.length === 0) {
|
|
74
|
-
process.stderr.write("[unerr] No branches found.\n");
|
|
75
|
-
return;
|
|
76
|
-
}
|
|
77
|
-
let withDrift = 0;
|
|
78
|
-
process.stderr.write("\n");
|
|
79
|
-
for (const branch of branches) {
|
|
80
|
-
const marker = branch.isCurrent ? "* " : " ";
|
|
81
|
-
const trackingSuffix = branch.tracking ? ` ${branch.tracking}` : "";
|
|
82
|
-
let driftSuffix = "";
|
|
83
|
-
if (branch.driftEntityCount > 0) {
|
|
84
|
-
driftSuffix = ` (${branch.driftEntityCount} drift entities)`;
|
|
85
|
-
withDrift++;
|
|
86
|
-
}
|
|
87
|
-
process.stderr.write(`${marker}${branch.name.padEnd(30)} ${branch.lastCommit}${trackingSuffix}${driftSuffix}\n`);
|
|
88
|
-
}
|
|
89
|
-
process.stderr.write(`\n ${branches.length} branches · ${withDrift} with drift overlays\n\n`);
|
|
90
|
-
}
|
|
91
|
-
catch (error) {
|
|
92
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
93
|
-
console.error(`Error: ${message}`);
|
|
94
|
-
process.exit(1);
|
|
95
|
-
}
|
|
96
|
-
});
|
|
97
|
-
}
|
|
@@ -1,225 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* unerr check-commit — Pre-commit convention validation.
|
|
3
|
-
*
|
|
4
|
-
* Called by the pre-commit git hook to check staged changes
|
|
5
|
-
* against project conventions. Non-blocking by default.
|
|
6
|
-
*
|
|
7
|
-
* Sprint 4 (Task 4.2): Full convention checking implementation.
|
|
8
|
-
*
|
|
9
|
-
* Flow:
|
|
10
|
-
* 1. Load CozoDB graph from local snapshot
|
|
11
|
-
* 2. Get staged files from git
|
|
12
|
-
* 3. Evaluate rules against each staged file
|
|
13
|
-
* 4. Display violations with formatted output
|
|
14
|
-
* 5. Exit 1 if blocking mode and violations found
|
|
15
|
-
*
|
|
16
|
-
* Exit codes:
|
|
17
|
-
* 0 — All checks pass (or no checks available)
|
|
18
|
-
* 1 — Violations found (only when blocking mode is enabled)
|
|
19
|
-
*/
|
|
20
|
-
import { existsSync, readFileSync } from "node:fs";
|
|
21
|
-
import { extname, join } from "node:path";
|
|
22
|
-
import { gunzipSync } from "node:zlib";
|
|
23
|
-
import pc from "picocolors";
|
|
24
|
-
import { getStagedFiles } from "../utils/git.js";
|
|
25
|
-
import { logInfo } from "../utils/log.js";
|
|
26
|
-
import { detail, fail, info, section, success, warn } from "../utils/ui.js";
|
|
27
|
-
/** File extensions we can evaluate rules against. */
|
|
28
|
-
const SUPPORTED_EXTENSIONS = new Set([
|
|
29
|
-
".ts",
|
|
30
|
-
".tsx",
|
|
31
|
-
".js",
|
|
32
|
-
".jsx",
|
|
33
|
-
".py",
|
|
34
|
-
".go",
|
|
35
|
-
]);
|
|
36
|
-
export function registerCheckCommitCommand(program) {
|
|
37
|
-
program
|
|
38
|
-
.command("check-commit")
|
|
39
|
-
.description("Check staged changes against project conventions (pre-commit hook)")
|
|
40
|
-
.option("--blocking", "Exit with code 1 on violations (default: non-blocking)")
|
|
41
|
-
.option("--verbose", "Show detailed output including passing files")
|
|
42
|
-
.action(async (opts) => {
|
|
43
|
-
const cwd = process.cwd();
|
|
44
|
-
// ── Blocking mode detection ──────────────────────────────────
|
|
45
|
-
let blockingMode = opts.blocking ?? false;
|
|
46
|
-
const settingsPath = join(cwd, ".unerr", "settings.json");
|
|
47
|
-
if (!blockingMode && existsSync(settingsPath)) {
|
|
48
|
-
try {
|
|
49
|
-
const settings = JSON.parse(readFileSync(settingsPath, "utf-8"));
|
|
50
|
-
blockingMode = settings.hooks?.precommit?.blocking ?? false;
|
|
51
|
-
}
|
|
52
|
-
catch {
|
|
53
|
-
/* ignore malformed settings */
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
logInfo("check-commit invoked", { blocking: blockingMode });
|
|
57
|
-
// ── Config gating ────────────────────────────────────────────
|
|
58
|
-
const configPath = join(cwd, ".unerr", "config.json");
|
|
59
|
-
if (!existsSync(configPath)) {
|
|
60
|
-
logInfo("check-commit: no .unerr/config.json, skipping");
|
|
61
|
-
return;
|
|
62
|
-
}
|
|
63
|
-
let repoId;
|
|
64
|
-
try {
|
|
65
|
-
const config = JSON.parse(readFileSync(configPath, "utf-8"));
|
|
66
|
-
if (!config.repoId) {
|
|
67
|
-
logInfo("check-commit: no repoId in config, skipping");
|
|
68
|
-
return;
|
|
69
|
-
}
|
|
70
|
-
repoId = config.repoId;
|
|
71
|
-
}
|
|
72
|
-
catch {
|
|
73
|
-
logInfo("check-commit: invalid config.json, skipping");
|
|
74
|
-
return;
|
|
75
|
-
}
|
|
76
|
-
// ── Get staged files ─────────────────────────────────────────
|
|
77
|
-
const stagedFiles = await getStagedFiles(cwd);
|
|
78
|
-
if (stagedFiles.length === 0) {
|
|
79
|
-
logInfo("check-commit: no staged files");
|
|
80
|
-
return;
|
|
81
|
-
}
|
|
82
|
-
// Filter to supported file types
|
|
83
|
-
const checkableFiles = stagedFiles.filter((f) => SUPPORTED_EXTENSIONS.has(extname(f)));
|
|
84
|
-
if (checkableFiles.length === 0) {
|
|
85
|
-
if (opts.verbose) {
|
|
86
|
-
section("unerr pre-commit check");
|
|
87
|
-
detail(`${stagedFiles.length} staged file${stagedFiles.length !== 1 ? "s" : ""} — none with supported extensions`);
|
|
88
|
-
}
|
|
89
|
-
return;
|
|
90
|
-
}
|
|
91
|
-
// ── Load graph ───────────────────────────────────────────────
|
|
92
|
-
const snapshotsDir = join(cwd, ".unerr", "snapshots");
|
|
93
|
-
const manifestsDir = join(cwd, ".unerr", "manifests");
|
|
94
|
-
const localGraph = await loadGraphForCheckCommit(repoId, snapshotsDir, manifestsDir);
|
|
95
|
-
if (!localGraph) {
|
|
96
|
-
if (opts.verbose) {
|
|
97
|
-
section("unerr pre-commit check");
|
|
98
|
-
detail("No local graph available — skipping convention checks");
|
|
99
|
-
}
|
|
100
|
-
logInfo("check-commit: no graph available, skipping");
|
|
101
|
-
return;
|
|
102
|
-
}
|
|
103
|
-
// Check if any rules exist
|
|
104
|
-
if (!(await localGraph.hasRules())) {
|
|
105
|
-
if (opts.verbose) {
|
|
106
|
-
section("unerr pre-commit check");
|
|
107
|
-
detail("No rules defined — skipping convention checks");
|
|
108
|
-
}
|
|
109
|
-
logInfo("check-commit: no rules in graph, skipping");
|
|
110
|
-
return;
|
|
111
|
-
}
|
|
112
|
-
// ── Evaluate rules per file ──────────────────────────────────
|
|
113
|
-
const { evaluateRules } = await import("../intelligence/rule-evaluator.js");
|
|
114
|
-
const allFileViolations = [];
|
|
115
|
-
let totalRulesEvaluated = 0;
|
|
116
|
-
let filesChecked = 0;
|
|
117
|
-
for (const filePath of checkableFiles) {
|
|
118
|
-
const absPath = join(cwd, filePath);
|
|
119
|
-
if (!existsSync(absPath))
|
|
120
|
-
continue;
|
|
121
|
-
let content;
|
|
122
|
-
try {
|
|
123
|
-
content = readFileSync(absPath, "utf-8");
|
|
124
|
-
}
|
|
125
|
-
catch {
|
|
126
|
-
continue; // Skip unreadable files
|
|
127
|
-
}
|
|
128
|
-
const rules = await localGraph.getRules(filePath);
|
|
129
|
-
if (rules.length === 0)
|
|
130
|
-
continue;
|
|
131
|
-
filesChecked++;
|
|
132
|
-
totalRulesEvaluated += rules.length;
|
|
133
|
-
try {
|
|
134
|
-
const result = await evaluateRules(rules, filePath, content, localGraph);
|
|
135
|
-
if (result.violations.length > 0) {
|
|
136
|
-
allFileViolations.push({
|
|
137
|
-
filePath,
|
|
138
|
-
violations: result.violations,
|
|
139
|
-
});
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
catch (err) {
|
|
143
|
-
logInfo(`check-commit: rule evaluation failed for ${filePath}`, err);
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
const totalViolations = allFileViolations.reduce((sum, fv) => sum + fv.violations.length, 0);
|
|
147
|
-
// ── Display results ──────────────────────────────────────────
|
|
148
|
-
section("unerr pre-commit check");
|
|
149
|
-
if (totalViolations === 0) {
|
|
150
|
-
success(`${filesChecked} file${filesChecked !== 1 ? "s" : ""} checked, ${totalRulesEvaluated} rules evaluated — all clear`);
|
|
151
|
-
process.exitCode = 0;
|
|
152
|
-
return;
|
|
153
|
-
}
|
|
154
|
-
// Display violations grouped by file
|
|
155
|
-
for (const fv of allFileViolations) {
|
|
156
|
-
fail(`${fv.filePath}`);
|
|
157
|
-
for (const v of fv.violations) {
|
|
158
|
-
const location = v.line ? `:${v.line}` : "";
|
|
159
|
-
const severityColor = v.severity === "error"
|
|
160
|
-
? pc.red
|
|
161
|
-
: v.severity === "warning"
|
|
162
|
-
? pc.yellow
|
|
163
|
-
: pc.dim;
|
|
164
|
-
info(`${severityColor(`[${v.severity}]`)} ${v.message}${location ? pc.dim(` (line ${v.line})`) : ""}`);
|
|
165
|
-
if (v.matchedCode && opts.verbose) {
|
|
166
|
-
detail(` → ${v.matchedCode.slice(0, 80)}`);
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
// Summary line
|
|
171
|
-
const errorCount = allFileViolations.reduce((sum, fv) => sum + fv.violations.filter((v) => v.severity === "error").length, 0);
|
|
172
|
-
const warningCount = totalViolations - errorCount;
|
|
173
|
-
const errorSuffix = errorCount > 0
|
|
174
|
-
? ` (${errorCount} error${errorCount !== 1 ? "s" : ""})`
|
|
175
|
-
: "";
|
|
176
|
-
const warnSuffix = warningCount > 0
|
|
177
|
-
? ` (${warningCount} warning${warningCount !== 1 ? "s" : ""})`
|
|
178
|
-
: "";
|
|
179
|
-
warn(`${totalViolations} violation${totalViolations !== 1 ? "s" : ""} found${errorSuffix}${warnSuffix}`);
|
|
180
|
-
if (blockingMode) {
|
|
181
|
-
fail("Commit blocked — fix violations or use --no-verify to bypass");
|
|
182
|
-
process.exitCode = 1;
|
|
183
|
-
}
|
|
184
|
-
else {
|
|
185
|
-
detail("Non-blocking mode — commit will proceed");
|
|
186
|
-
detail("Enable blocking: set hooks.precommit.blocking=true in .unerr/settings.json");
|
|
187
|
-
process.exitCode = 0;
|
|
188
|
-
}
|
|
189
|
-
});
|
|
190
|
-
}
|
|
191
|
-
// ── Graph Loading ────────────────────────────────────────────────────
|
|
192
|
-
/**
|
|
193
|
-
* Load CozoDB graph for standalone check-commit (proxy may not be running).
|
|
194
|
-
* Returns null if no snapshot available.
|
|
195
|
-
*/
|
|
196
|
-
async function loadGraphForCheckCommit(repoId, snapshotsDir, manifestsDir) {
|
|
197
|
-
// Check manifest exists
|
|
198
|
-
const manifestPath = join(manifestsDir, `${repoId}.json`);
|
|
199
|
-
if (!existsSync(manifestPath))
|
|
200
|
-
return null;
|
|
201
|
-
// Find snapshot file
|
|
202
|
-
let snapshotPath = join(snapshotsDir, `${repoId}.msgpack.gz`);
|
|
203
|
-
if (!existsSync(snapshotPath)) {
|
|
204
|
-
snapshotPath = join(snapshotsDir, `${repoId}.msgpack`);
|
|
205
|
-
}
|
|
206
|
-
if (!existsSync(snapshotPath))
|
|
207
|
-
return null;
|
|
208
|
-
try {
|
|
209
|
-
// Dynamic imports to keep cold start fast when no check needed
|
|
210
|
-
const { default: CozoDbConstructor } = await import("cozo-node");
|
|
211
|
-
const { CozoGraphStore } = await import("../intelligence/local-graph.js");
|
|
212
|
-
const { unpack } = await import("msgpackr");
|
|
213
|
-
const db = new CozoDbConstructor();
|
|
214
|
-
const localGraph = await CozoGraphStore.create(db);
|
|
215
|
-
const raw = readFileSync(snapshotPath);
|
|
216
|
-
const buffer = snapshotPath.endsWith(".gz") ? gunzipSync(raw) : raw;
|
|
217
|
-
const envelope = unpack(buffer);
|
|
218
|
-
await localGraph.loadSnapshot(envelope);
|
|
219
|
-
return localGraph;
|
|
220
|
-
}
|
|
221
|
-
catch (err) {
|
|
222
|
-
logInfo("check-commit: failed to load graph", err);
|
|
223
|
-
return null;
|
|
224
|
-
}
|
|
225
|
-
}
|
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* `unerr compress-output` — stdin→stdout compression for CLI hooks.
|
|
3
|
-
*
|
|
4
|
-
* S6.4: Reads text from stdin, compresses with the output compressor,
|
|
5
|
-
* writes compressed output to stdout. When graph is available, passes
|
|
6
|
-
* entity risk map for graph-aware prioritization.
|
|
7
|
-
*
|
|
8
|
-
* Usage (in hooks): echo "$OUTPUT" | unerr compress-output
|
|
9
|
-
*/
|
|
10
|
-
import { existsSync, readFileSync } from "node:fs";
|
|
11
|
-
import { join } from "node:path";
|
|
12
|
-
import { compressOutput, } from "../proxy/output-compressor.js";
|
|
13
|
-
/**
|
|
14
|
-
* Load entity risk map from the local graph state if available.
|
|
15
|
-
* Returns empty map if graph is not initialized.
|
|
16
|
-
*/
|
|
17
|
-
function loadEntityRiskMap(cwd) {
|
|
18
|
-
const riskMap = new Map();
|
|
19
|
-
try {
|
|
20
|
-
const unerrDir = join(cwd, ".unerr");
|
|
21
|
-
const configPath = join(unerrDir, "config.json");
|
|
22
|
-
if (!existsSync(configPath))
|
|
23
|
-
return riskMap;
|
|
24
|
-
const config = JSON.parse(readFileSync(configPath, "utf-8"));
|
|
25
|
-
if (!config.repoId)
|
|
26
|
-
return riskMap;
|
|
27
|
-
const riskCachePath = join(unerrDir, "state", "entity_risk_cache.json");
|
|
28
|
-
if (!existsSync(riskCachePath))
|
|
29
|
-
return riskMap;
|
|
30
|
-
const data = JSON.parse(readFileSync(riskCachePath, "utf-8"));
|
|
31
|
-
for (const [key, info] of Object.entries(data)) {
|
|
32
|
-
riskMap.set(key, info);
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
catch {
|
|
36
|
-
// Risk map loading is best-effort — compression works without it
|
|
37
|
-
}
|
|
38
|
-
return riskMap;
|
|
39
|
-
}
|
|
40
|
-
export function registerCompressOutputCommand(program) {
|
|
41
|
-
program
|
|
42
|
-
.command("compress-output")
|
|
43
|
-
.description("Compress text from stdin using graph-aware compression (for hooks)")
|
|
44
|
-
.option("--budget <tokens>", "Token budget", "2000")
|
|
45
|
-
.option("--no-graph", "Skip loading graph risk map")
|
|
46
|
-
.action(async (opts) => {
|
|
47
|
-
const budget = Number.parseInt(opts.budget, 10) || 2000;
|
|
48
|
-
let input = "";
|
|
49
|
-
for await (const chunk of process.stdin) {
|
|
50
|
-
input += chunk;
|
|
51
|
-
}
|
|
52
|
-
if (input.length === 0) {
|
|
53
|
-
process.exit(0);
|
|
54
|
-
return;
|
|
55
|
-
}
|
|
56
|
-
// S6.4: Load entity risk map from graph when available
|
|
57
|
-
const entityRiskMap = opts.graph !== false ? loadEntityRiskMap(process.cwd()) : undefined;
|
|
58
|
-
const result = compressOutput(input, {
|
|
59
|
-
tokenBudget: budget,
|
|
60
|
-
entityRiskMap: entityRiskMap && entityRiskMap.size > 0 ? entityRiskMap : undefined,
|
|
61
|
-
});
|
|
62
|
-
process.stdout.write(result.output);
|
|
63
|
-
});
|
|
64
|
-
}
|