@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,188 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Sprint 10.1: Working Snapshot Management.
|
|
3
|
-
*
|
|
4
|
-
* Manages "known-good" state snapshots. The developer (or agent) marks
|
|
5
|
-
* "this works" after tests pass, before risky changes, or at session start.
|
|
6
|
-
* These snapshots become rewind targets for deterministic rewind (Task 10.2).
|
|
7
|
-
*
|
|
8
|
-
* Storage: `.unerr/snapshots/{id}.json`
|
|
9
|
-
* Processing: deferred to ledger flush.
|
|
10
|
-
*
|
|
11
|
-
* Design authority: Phase 5.5 Flow 2 (State Validated → Working Snapshot Created)
|
|
12
|
-
*/
|
|
13
|
-
import { randomBytes } from "node:crypto";
|
|
14
|
-
import { existsSync, mkdirSync, readFileSync, readdirSync, rmSync, writeFileSync, } from "node:fs";
|
|
15
|
-
import { join } from "node:path";
|
|
16
|
-
/** stderr logger */
|
|
17
|
-
const _log = {
|
|
18
|
-
info: (msg) => process.stderr.write(`[unerr:snapshot] ${msg}\n`),
|
|
19
|
-
warn: (msg) => process.stderr.write(`[unerr:snapshot] WARN: ${msg}\n`),
|
|
20
|
-
};
|
|
21
|
-
/** Maximum snapshots retained locally (oldest evicted). */
|
|
22
|
-
const MAX_SNAPSHOTS = 50;
|
|
23
|
-
/** Auto-snapshot cooldown: don't auto-snapshot within 1 hour of last. */
|
|
24
|
-
const AUTO_SNAPSHOT_COOLDOWN_MS = 60 * 60 * 1000;
|
|
25
|
-
export class WorkingSnapshotStore {
|
|
26
|
-
snapshotDir;
|
|
27
|
-
unerrDir;
|
|
28
|
-
constructor(unerrDir) {
|
|
29
|
-
this.unerrDir = unerrDir;
|
|
30
|
-
this.snapshotDir = join(unerrDir, "snapshots");
|
|
31
|
-
if (!existsSync(this.snapshotDir)) {
|
|
32
|
-
mkdirSync(this.snapshotDir, { recursive: true });
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
/**
|
|
36
|
-
* Create a new working snapshot.
|
|
37
|
-
*/
|
|
38
|
-
create(opts) {
|
|
39
|
-
const id = `snap_${randomBytes(6).toString("hex")}`;
|
|
40
|
-
const snapshot = {
|
|
41
|
-
id,
|
|
42
|
-
commitSha: opts.commitSha,
|
|
43
|
-
reason: opts.reason,
|
|
44
|
-
timestamp: new Date().toISOString(),
|
|
45
|
-
branch: opts.branch,
|
|
46
|
-
timelineBranch: opts.timelineBranch,
|
|
47
|
-
sessionId: opts.sessionId,
|
|
48
|
-
processed: false,
|
|
49
|
-
};
|
|
50
|
-
writeFileSync(join(this.snapshotDir, `${id}.json`), JSON.stringify(snapshot, null, 2), "utf-8");
|
|
51
|
-
_log.info(`Created snapshot ${id} at ${opts.commitSha.slice(0, 8)} (${opts.reason})`);
|
|
52
|
-
// Enforce cap
|
|
53
|
-
this.enforceCap();
|
|
54
|
-
return snapshot;
|
|
55
|
-
}
|
|
56
|
-
/**
|
|
57
|
-
* Get a snapshot by ID.
|
|
58
|
-
*/
|
|
59
|
-
get(snapshotId) {
|
|
60
|
-
const filePath = join(this.snapshotDir, `${snapshotId}.json`);
|
|
61
|
-
if (!existsSync(filePath))
|
|
62
|
-
return null;
|
|
63
|
-
try {
|
|
64
|
-
return JSON.parse(readFileSync(filePath, "utf-8"));
|
|
65
|
-
}
|
|
66
|
-
catch {
|
|
67
|
-
return null;
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
/**
|
|
71
|
-
* List all snapshots, most recent first.
|
|
72
|
-
*/
|
|
73
|
-
list() {
|
|
74
|
-
if (!existsSync(this.snapshotDir))
|
|
75
|
-
return [];
|
|
76
|
-
const files = readdirSync(this.snapshotDir).filter((f) => f.endsWith(".json"));
|
|
77
|
-
const snapshots = [];
|
|
78
|
-
for (const file of files) {
|
|
79
|
-
try {
|
|
80
|
-
const raw = readFileSync(join(this.snapshotDir, file), "utf-8");
|
|
81
|
-
snapshots.push(JSON.parse(raw));
|
|
82
|
-
}
|
|
83
|
-
catch {
|
|
84
|
-
// Skip corrupted files
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
// Sort by timestamp descending
|
|
88
|
-
snapshots.sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime());
|
|
89
|
-
return snapshots;
|
|
90
|
-
}
|
|
91
|
-
/**
|
|
92
|
-
* Get the most recent snapshot (rewind target).
|
|
93
|
-
*/
|
|
94
|
-
getLatest() {
|
|
95
|
-
const snapshots = this.list();
|
|
96
|
-
return snapshots[0] ?? null;
|
|
97
|
-
}
|
|
98
|
-
/**
|
|
99
|
-
* Get the most recent snapshot for a specific branch.
|
|
100
|
-
*/
|
|
101
|
-
getLatestForBranch(branch) {
|
|
102
|
-
const snapshots = this.list();
|
|
103
|
-
return snapshots.find((s) => s.branch === branch) ?? null;
|
|
104
|
-
}
|
|
105
|
-
/**
|
|
106
|
-
* Check if auto-snapshot should be created (no recent snapshot within cooldown).
|
|
107
|
-
*/
|
|
108
|
-
shouldAutoSnapshot() {
|
|
109
|
-
const latest = this.getLatest();
|
|
110
|
-
if (!latest)
|
|
111
|
-
return true;
|
|
112
|
-
const age = Date.now() - new Date(latest.timestamp).getTime();
|
|
113
|
-
return age > AUTO_SNAPSHOT_COOLDOWN_MS;
|
|
114
|
-
}
|
|
115
|
-
/**
|
|
116
|
-
* Mark a snapshot as processed.
|
|
117
|
-
*/
|
|
118
|
-
markProcessed(snapshotId) {
|
|
119
|
-
const snapshot = this.get(snapshotId);
|
|
120
|
-
if (!snapshot)
|
|
121
|
-
return;
|
|
122
|
-
snapshot.processed = true;
|
|
123
|
-
writeFileSync(join(this.snapshotDir, `${snapshotId}.json`), JSON.stringify(snapshot, null, 2), "utf-8");
|
|
124
|
-
}
|
|
125
|
-
/**
|
|
126
|
-
* Get all unprocessed snapshots (for ledger flush).
|
|
127
|
-
*/
|
|
128
|
-
getUnprocessed() {
|
|
129
|
-
return this.list().filter((s) => !s.processed);
|
|
130
|
-
}
|
|
131
|
-
/**
|
|
132
|
-
* Delete a snapshot.
|
|
133
|
-
*/
|
|
134
|
-
delete(snapshotId) {
|
|
135
|
-
const filePath = join(this.snapshotDir, `${snapshotId}.json`);
|
|
136
|
-
if (!existsSync(filePath))
|
|
137
|
-
return false;
|
|
138
|
-
rmSync(filePath);
|
|
139
|
-
return true;
|
|
140
|
-
}
|
|
141
|
-
/**
|
|
142
|
-
* Get the timeline branch counter from branch_context.json.
|
|
143
|
-
*/
|
|
144
|
-
getTimelineBranch() {
|
|
145
|
-
const contextPath = join(this.unerrDir, "branch_context.json");
|
|
146
|
-
if (!existsSync(contextPath))
|
|
147
|
-
return 0;
|
|
148
|
-
try {
|
|
149
|
-
const ctx = JSON.parse(readFileSync(contextPath, "utf-8"));
|
|
150
|
-
return ctx.timelineBranch ?? 0;
|
|
151
|
-
}
|
|
152
|
-
catch {
|
|
153
|
-
return 0;
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
/**
|
|
157
|
-
* Increment the timeline branch counter. Returns the new value.
|
|
158
|
-
*/
|
|
159
|
-
incrementTimelineBranch() {
|
|
160
|
-
const contextPath = join(this.unerrDir, "branch_context.json");
|
|
161
|
-
let ctx = {};
|
|
162
|
-
if (existsSync(contextPath)) {
|
|
163
|
-
try {
|
|
164
|
-
ctx = JSON.parse(readFileSync(contextPath, "utf-8"));
|
|
165
|
-
}
|
|
166
|
-
catch {
|
|
167
|
-
ctx = {};
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
const current = ctx.timelineBranch ?? 0;
|
|
171
|
-
ctx.timelineBranch = current + 1;
|
|
172
|
-
writeFileSync(contextPath, JSON.stringify(ctx, null, 2), "utf-8");
|
|
173
|
-
return current + 1;
|
|
174
|
-
}
|
|
175
|
-
/**
|
|
176
|
-
* Enforce max snapshot cap — evict oldest beyond limit.
|
|
177
|
-
*/
|
|
178
|
-
enforceCap() {
|
|
179
|
-
const snapshots = this.list();
|
|
180
|
-
if (snapshots.length <= MAX_SNAPSHOTS)
|
|
181
|
-
return;
|
|
182
|
-
const toRemove = snapshots.slice(MAX_SNAPSHOTS);
|
|
183
|
-
for (const snapshot of toRemove) {
|
|
184
|
-
this.delete(snapshot.id);
|
|
185
|
-
_log.info(`Evicted old snapshot: ${snapshot.id}`);
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
}
|
|
@@ -1,176 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Workspace Manifest — Causal Bridge between intents and commits.
|
|
3
|
-
*
|
|
4
|
-
* Maintains `.unerr/manifest.json` — a structured record linking AI intent
|
|
5
|
-
* chains to git commits. This file is the single source of truth for
|
|
6
|
-
* attributing "why" a change was made (AI-assisted or human-only).
|
|
7
|
-
*
|
|
8
|
-
* Flow:
|
|
9
|
-
* 1. Shadow Ledger records every tool call with correlation IDs
|
|
10
|
-
* 2. Intent Correlator groups related calls into pending correlations
|
|
11
|
-
* 3. Commit Watcher associates pending correlations with commit SHAs
|
|
12
|
-
* 4. Workspace Manifest snapshots committed correlations into attribution records
|
|
13
|
-
* 5. On push/PR → manifest is persisted locally for PR badges + attribution
|
|
14
|
-
*
|
|
15
|
-
* File: .unerr/manifest.json
|
|
16
|
-
*
|
|
17
|
-
* All logging to stderr (stdout reserved for MCP).
|
|
18
|
-
*/
|
|
19
|
-
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
20
|
-
import { join } from "node:path";
|
|
21
|
-
// ── Manifest Manager ────────────────────────────────────────────────
|
|
22
|
-
export class WorkspaceManifest {
|
|
23
|
-
unerrDir;
|
|
24
|
-
repoId;
|
|
25
|
-
sessionId;
|
|
26
|
-
manifestPath;
|
|
27
|
-
data;
|
|
28
|
-
constructor(unerrDir, repoId, sessionId) {
|
|
29
|
-
this.unerrDir = unerrDir;
|
|
30
|
-
this.repoId = repoId;
|
|
31
|
-
this.sessionId = sessionId;
|
|
32
|
-
this.manifestPath = join(unerrDir, "manifest.json");
|
|
33
|
-
this.data = this.load();
|
|
34
|
-
}
|
|
35
|
-
/**
|
|
36
|
-
* Record a committed correlation as an attribution.
|
|
37
|
-
* Called by CommitWatcher when it associates a pending correlation with a commit.
|
|
38
|
-
*/
|
|
39
|
-
recordAttribution(correlation, branch) {
|
|
40
|
-
if (!correlation.commitSha)
|
|
41
|
-
return; // Only committed correlations
|
|
42
|
-
const record = {
|
|
43
|
-
intentId: correlation.rootIntentId,
|
|
44
|
-
commitSha: correlation.commitSha,
|
|
45
|
-
branch,
|
|
46
|
-
sessionId: this.sessionId,
|
|
47
|
-
prompt: correlation.prompt,
|
|
48
|
-
toolChain: correlation.toolChain,
|
|
49
|
-
entitiesAffected: correlation.entities,
|
|
50
|
-
filesChanged: correlation.files,
|
|
51
|
-
correlatedAt: correlation.createdAt,
|
|
52
|
-
committedAt: new Date().toISOString(),
|
|
53
|
-
flushed: false,
|
|
54
|
-
};
|
|
55
|
-
this.data.records.push(record);
|
|
56
|
-
this.save();
|
|
57
|
-
}
|
|
58
|
-
/**
|
|
59
|
-
* Get unflushed attribution records.
|
|
60
|
-
*/
|
|
61
|
-
getUnflushedRecords() {
|
|
62
|
-
return this.data.records.filter((r) => !r.flushed);
|
|
63
|
-
}
|
|
64
|
-
/**
|
|
65
|
-
* Mark records as flushed after successful processing.
|
|
66
|
-
*/
|
|
67
|
-
markFlushed(intentIds) {
|
|
68
|
-
const idSet = new Set(intentIds);
|
|
69
|
-
for (const record of this.data.records) {
|
|
70
|
-
if (idSet.has(record.intentId)) {
|
|
71
|
-
record.flushed = true;
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
this.data.lastFlushedAt = new Date().toISOString();
|
|
75
|
-
this.data.totalFlushed += intentIds.length;
|
|
76
|
-
this.save();
|
|
77
|
-
// Prune old flushed records (keep last 50)
|
|
78
|
-
this.pruneOldRecords();
|
|
79
|
-
}
|
|
80
|
-
/**
|
|
81
|
-
* Get manifest stats for display.
|
|
82
|
-
*/
|
|
83
|
-
getStats() {
|
|
84
|
-
return {
|
|
85
|
-
total: this.data.records.length,
|
|
86
|
-
unflushed: this.data.records.filter((r) => !r.flushed).length,
|
|
87
|
-
orphanedIntents: this.data.orphanedIntents?.length ?? 0,
|
|
88
|
-
lastFlushedAt: this.data.lastFlushedAt,
|
|
89
|
-
};
|
|
90
|
-
}
|
|
91
|
-
/**
|
|
92
|
-
* Record an orphaned intent — a pending correlation that was never committed.
|
|
93
|
-
* Called when the proxy detects stale correlations (e.g., session shutdown
|
|
94
|
-
* with pending correlations older than the correlation window).
|
|
95
|
-
*/
|
|
96
|
-
recordOrphanedIntents(intents) {
|
|
97
|
-
if (intents.length === 0)
|
|
98
|
-
return;
|
|
99
|
-
if (!this.data.orphanedIntents) {
|
|
100
|
-
this.data.orphanedIntents = [];
|
|
101
|
-
}
|
|
102
|
-
for (const intent of intents) {
|
|
103
|
-
this.data.orphanedIntents.push({
|
|
104
|
-
intentId: intent.rootIntentId,
|
|
105
|
-
prompt: intent.prompt,
|
|
106
|
-
toolChain: intent.toolChain,
|
|
107
|
-
filesAffected: intent.files,
|
|
108
|
-
detectedAt: new Date().toISOString(),
|
|
109
|
-
originalAt: intent.createdAt,
|
|
110
|
-
});
|
|
111
|
-
}
|
|
112
|
-
// Keep only the last 100 orphaned intents
|
|
113
|
-
if (this.data.orphanedIntents.length > 100) {
|
|
114
|
-
this.data.orphanedIntents = this.data.orphanedIntents.slice(-100);
|
|
115
|
-
}
|
|
116
|
-
this.save();
|
|
117
|
-
}
|
|
118
|
-
/**
|
|
119
|
-
* Get orphaned intents for display in workspace views.
|
|
120
|
-
*/
|
|
121
|
-
getOrphanedIntents() {
|
|
122
|
-
return this.data.orphanedIntents ?? [];
|
|
123
|
-
}
|
|
124
|
-
// ── Internal ─────────────────────────────────────────────────────
|
|
125
|
-
load() {
|
|
126
|
-
if (!existsSync(this.manifestPath)) {
|
|
127
|
-
return {
|
|
128
|
-
version: 1,
|
|
129
|
-
repoId: this.repoId,
|
|
130
|
-
records: [],
|
|
131
|
-
lastFlushedAt: null,
|
|
132
|
-
totalFlushed: 0,
|
|
133
|
-
};
|
|
134
|
-
}
|
|
135
|
-
try {
|
|
136
|
-
const raw = readFileSync(this.manifestPath, "utf-8");
|
|
137
|
-
const parsed = JSON.parse(raw);
|
|
138
|
-
// Validate it's for this repo
|
|
139
|
-
if (parsed.repoId !== this.repoId) {
|
|
140
|
-
return {
|
|
141
|
-
version: 1,
|
|
142
|
-
repoId: this.repoId,
|
|
143
|
-
records: [],
|
|
144
|
-
lastFlushedAt: null,
|
|
145
|
-
totalFlushed: 0,
|
|
146
|
-
};
|
|
147
|
-
}
|
|
148
|
-
return parsed;
|
|
149
|
-
}
|
|
150
|
-
catch {
|
|
151
|
-
return {
|
|
152
|
-
version: 1,
|
|
153
|
-
repoId: this.repoId,
|
|
154
|
-
records: [],
|
|
155
|
-
lastFlushedAt: null,
|
|
156
|
-
totalFlushed: 0,
|
|
157
|
-
};
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
save() {
|
|
161
|
-
if (!existsSync(this.unerrDir)) {
|
|
162
|
-
mkdirSync(this.unerrDir, { recursive: true });
|
|
163
|
-
}
|
|
164
|
-
writeFileSync(this.manifestPath, JSON.stringify(this.data, null, 2), "utf-8");
|
|
165
|
-
}
|
|
166
|
-
pruneOldRecords() {
|
|
167
|
-
const flushed = this.data.records.filter((r) => r.flushed);
|
|
168
|
-
if (flushed.length <= 50)
|
|
169
|
-
return;
|
|
170
|
-
// Keep only unflushed + last 50 flushed
|
|
171
|
-
const unflushed = this.data.records.filter((r) => !r.flushed);
|
|
172
|
-
const recentFlushed = flushed.slice(-50);
|
|
173
|
-
this.data.records = [...recentFlushed, ...unflushed];
|
|
174
|
-
this.save();
|
|
175
|
-
}
|
|
176
|
-
}
|
package/dist/transport/http.js
DELETED
|
@@ -1,102 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* HTTP Transport — Hono-based HTTP server for the intelligence proxy.
|
|
3
|
-
*
|
|
4
|
-
* Provides an alternative transport to stdio MCP. Disabled by default,
|
|
5
|
-
* enabled via `unerr --http` or `unerr serve --http-port 3141`.
|
|
6
|
-
*
|
|
7
|
-
* Endpoints:
|
|
8
|
-
* GET /health → { status, version, uptime_ms }
|
|
9
|
-
* GET /ui/* → Static files from packages/ui/dist/ (dashboard)
|
|
10
|
-
* GET /ui → Redirect to /ui/
|
|
11
|
-
*
|
|
12
|
-
* Invariant: The HTTP server never interferes with MCP stdio transport.
|
|
13
|
-
* Both can run simultaneously — HTTP on a port, MCP on stdin/stdout.
|
|
14
|
-
*/
|
|
15
|
-
import { existsSync } from "node:fs";
|
|
16
|
-
import { readFile } from "node:fs/promises";
|
|
17
|
-
import { extname, join } from "node:path";
|
|
18
|
-
import { serve } from "@hono/node-server";
|
|
19
|
-
import { Hono } from "hono";
|
|
20
|
-
import { createModuleLogger } from "../utils/logger.js";
|
|
21
|
-
const log = createModuleLogger("http");
|
|
22
|
-
const startedAt = Date.now();
|
|
23
|
-
const MIME_TYPES = {
|
|
24
|
-
".html": "text/html",
|
|
25
|
-
".js": "application/javascript",
|
|
26
|
-
".css": "text/css",
|
|
27
|
-
".json": "application/json",
|
|
28
|
-
".svg": "image/svg+xml",
|
|
29
|
-
".png": "image/png",
|
|
30
|
-
".ico": "image/x-icon",
|
|
31
|
-
".woff": "font/woff",
|
|
32
|
-
".woff2": "font/woff2",
|
|
33
|
-
};
|
|
34
|
-
function resolveUiDistDir() {
|
|
35
|
-
const candidates = [
|
|
36
|
-
join(process.cwd(), "packages", "ui", "dist"),
|
|
37
|
-
join(process.cwd(), "..", "packages", "ui", "dist"),
|
|
38
|
-
];
|
|
39
|
-
for (const dir of candidates) {
|
|
40
|
-
if (existsSync(join(dir, "index.html")))
|
|
41
|
-
return dir;
|
|
42
|
-
}
|
|
43
|
-
return null;
|
|
44
|
-
}
|
|
45
|
-
export function createHttpTransport(options = {}) {
|
|
46
|
-
const port = options.port ?? 3141;
|
|
47
|
-
const hostname = options.hostname ?? "127.0.0.1";
|
|
48
|
-
const app = new Hono();
|
|
49
|
-
app.get("/health", (c) => c.json({
|
|
50
|
-
status: "ok",
|
|
51
|
-
version: "0.1.0",
|
|
52
|
-
uptime_ms: Date.now() - startedAt,
|
|
53
|
-
}));
|
|
54
|
-
app.get("/ui", (c) => c.redirect("/ui/"));
|
|
55
|
-
app.get("/ui/*", async (c) => {
|
|
56
|
-
const uiDir = resolveUiDistDir();
|
|
57
|
-
if (!uiDir) {
|
|
58
|
-
return c.json({ error: "UI not built. Run: pnpm --filter @unerr/ui build" }, 404);
|
|
59
|
-
}
|
|
60
|
-
let filePath = c.req.path.replace(/^\/ui\/?/, "");
|
|
61
|
-
if (!filePath || filePath === "/")
|
|
62
|
-
filePath = "index.html";
|
|
63
|
-
const fullPath = join(uiDir, filePath);
|
|
64
|
-
if (!fullPath.startsWith(uiDir)) {
|
|
65
|
-
return c.json({ error: "forbidden" }, 403);
|
|
66
|
-
}
|
|
67
|
-
try {
|
|
68
|
-
const content = await readFile(fullPath);
|
|
69
|
-
const ext = extname(filePath);
|
|
70
|
-
const contentType = MIME_TYPES[ext] ?? "application/octet-stream";
|
|
71
|
-
return new Response(content, {
|
|
72
|
-
headers: { "Content-Type": contentType },
|
|
73
|
-
});
|
|
74
|
-
}
|
|
75
|
-
catch {
|
|
76
|
-
const indexPath = join(uiDir, "index.html");
|
|
77
|
-
try {
|
|
78
|
-
const indexContent = await readFile(indexPath);
|
|
79
|
-
return new Response(indexContent, {
|
|
80
|
-
headers: { "Content-Type": "text/html" },
|
|
81
|
-
});
|
|
82
|
-
}
|
|
83
|
-
catch {
|
|
84
|
-
return c.json({ error: "not_found" }, 404);
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
});
|
|
88
|
-
app.onError((err, c) => {
|
|
89
|
-
log.error("Unhandled HTTP error:", err.message);
|
|
90
|
-
return c.json({ error: "internal_error", message: err.message }, 500);
|
|
91
|
-
});
|
|
92
|
-
app.notFound((c) => c.json({
|
|
93
|
-
error: "not_found",
|
|
94
|
-
message: `${c.req.method} ${c.req.path} not found`,
|
|
95
|
-
}, 404));
|
|
96
|
-
const start = () => {
|
|
97
|
-
const server = serve({ fetch: app.fetch, port, hostname });
|
|
98
|
-
log.info(`HTTP transport listening on http://${hostname}:${port}`);
|
|
99
|
-
return server;
|
|
100
|
-
};
|
|
101
|
-
return { app, start, port };
|
|
102
|
-
}
|
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Counterfactual Framing Utility — wraps all savings displays in
|
|
3
|
-
* "without unerr" framing for value perception.
|
|
4
|
-
*
|
|
5
|
-
* VS.8: Every savings display must use counterfactual language:
|
|
6
|
-
* "Without unerr, this session would have cost $X.XX more"
|
|
7
|
-
* "Without unerr, the agent would have explored 15 files to find this"
|
|
8
|
-
*/
|
|
9
|
-
import { formatDollars } from "../proxy/model-pricing.js";
|
|
10
|
-
/**
|
|
11
|
-
* Frame a token savings as a counterfactual statement.
|
|
12
|
-
*/
|
|
13
|
-
export function frameTokenSavings(tokensSaved, dollarsSaved) {
|
|
14
|
-
if (tokensSaved <= 0)
|
|
15
|
-
return "";
|
|
16
|
-
const tokensStr = tokensSaved >= 1000
|
|
17
|
-
? `${(tokensSaved / 1000).toFixed(1)}K`
|
|
18
|
-
: String(tokensSaved);
|
|
19
|
-
return `Without unerr, ${tokensStr} additional tokens (${formatDollars(dollarsSaved)}) would have been consumed.`;
|
|
20
|
-
}
|
|
21
|
-
/**
|
|
22
|
-
* Frame a guard moment (prevented cost).
|
|
23
|
-
*/
|
|
24
|
-
export function frameGuardMoment(description, dollarsPrevented) {
|
|
25
|
-
return `[unerr] ⚠ Prevented: ${description}. Without unerr, est. cost: ${formatDollars(dollarsPrevented)}`;
|
|
26
|
-
}
|
|
27
|
-
/**
|
|
28
|
-
* Frame exploration savings.
|
|
29
|
-
*/
|
|
30
|
-
export function frameExplorationSaving(queryType, filesAvoided) {
|
|
31
|
-
return `Without unerr, the agent would have read ~${filesAvoided} files to get this information.`;
|
|
32
|
-
}
|
|
33
|
-
/**
|
|
34
|
-
* Frame session summary with counterfactual.
|
|
35
|
-
*/
|
|
36
|
-
export function frameSessionSummary(tokensSaved, dollarsSaved, guardsFirered) {
|
|
37
|
-
const parts = [];
|
|
38
|
-
if (tokensSaved > 0) {
|
|
39
|
-
const tokensStr = tokensSaved >= 1000
|
|
40
|
-
? `${(tokensSaved / 1000).toFixed(1)}K`
|
|
41
|
-
: String(tokensSaved);
|
|
42
|
-
parts.push(`${tokensStr} tokens saved (${formatDollars(dollarsSaved)})`);
|
|
43
|
-
}
|
|
44
|
-
if (guardsFirered > 0) {
|
|
45
|
-
parts.push(`${guardsFirered} issue(s) prevented`);
|
|
46
|
-
}
|
|
47
|
-
if (parts.length === 0) {
|
|
48
|
-
return "Graph intelligence active — monitoring for savings opportunities.";
|
|
49
|
-
}
|
|
50
|
-
return `Without unerr: ${parts.join(", ")} would have been wasted.`;
|
|
51
|
-
}
|
|
52
|
-
/**
|
|
53
|
-
* Frame a weekly trend delta.
|
|
54
|
-
*/
|
|
55
|
-
export function frameWeeklyTrend(thisWeekSaved, lastWeekSaved, dollarsSaved) {
|
|
56
|
-
const trend = thisWeekSaved > lastWeekSaved
|
|
57
|
-
? "↑"
|
|
58
|
-
: thisWeekSaved < lastWeekSaved
|
|
59
|
-
? "↓"
|
|
60
|
-
: "→";
|
|
61
|
-
const tokensStr = thisWeekSaved >= 1000
|
|
62
|
-
? `${(thisWeekSaved / 1000).toFixed(1)}K`
|
|
63
|
-
: String(thisWeekSaved);
|
|
64
|
-
return `This week: ${tokensStr} tokens saved (${formatDollars(dollarsSaved)}) ${trend}`;
|
|
65
|
-
}
|
package/dist/utils/deep-link.js
DELETED
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Deep-link URL generation for bridging CLI → web dashboard.
|
|
3
|
-
*
|
|
4
|
-
* §0.7 Constraint #8: "Every terminal display must include a deep link."
|
|
5
|
-
* Used by: startup display, status command, health check, session summary.
|
|
6
|
-
*/
|
|
7
|
-
const BASE_URL = "https://app.unerr.dev";
|
|
8
|
-
/**
|
|
9
|
-
* Generate a context-aware deep link to the unerr web dashboard.
|
|
10
|
-
*
|
|
11
|
-
* @param repoId - Repository identifier (e.g. "repo_abc123"). If empty/undefined, returns generic landing.
|
|
12
|
-
* @param options - Query parameters for view targeting and analytics.
|
|
13
|
-
* @returns Full URL string with query parameters.
|
|
14
|
-
*/
|
|
15
|
-
export function buildDeepLink(repoId, options) {
|
|
16
|
-
if (!repoId) {
|
|
17
|
-
// Fallback: generic landing when repo context unavailable
|
|
18
|
-
const params = new URLSearchParams();
|
|
19
|
-
params.set("utm_source", options?.utm_source ?? "cli");
|
|
20
|
-
return `${BASE_URL}?${params.toString()}`;
|
|
21
|
-
}
|
|
22
|
-
const base = `${BASE_URL}/r/${repoId}`;
|
|
23
|
-
const params = new URLSearchParams();
|
|
24
|
-
if (options?.view)
|
|
25
|
-
params.set("view", options.view);
|
|
26
|
-
if (options?.branch)
|
|
27
|
-
params.set("branch", options.branch);
|
|
28
|
-
if (options?.entities?.length)
|
|
29
|
-
params.set("entities", options.entities.join(","));
|
|
30
|
-
if (options?.intents?.length)
|
|
31
|
-
params.set("intents", options.intents.join(","));
|
|
32
|
-
params.set("utm_source", options?.utm_source ?? "cli");
|
|
33
|
-
return `${base}?${params.toString()}`;
|
|
34
|
-
}
|