@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
package/dist/entrypoints/cli.js
DELETED
|
@@ -1,1043 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* unerr — Local-first code intelligence CLI.
|
|
4
|
-
*
|
|
5
|
-
* Boot State Machine:
|
|
6
|
-
* unerr — THE command. First-run: wizard → index → serve. Subsequent: resume → serve.
|
|
7
|
-
* unerr chat — Interactive AI assistant (Ink REPL)
|
|
8
|
-
* unerr status — Quick diagnostic dump
|
|
9
|
-
* unerr debug — Full diagnostic for support
|
|
10
|
-
*
|
|
11
|
-
* All other commands are hidden but remain callable for power users and scripts.
|
|
12
|
-
*/
|
|
13
|
-
import { existsSync, readFileSync } from "node:fs";
|
|
14
|
-
import { join } from "node:path";
|
|
15
|
-
import { Command } from "commander";
|
|
16
|
-
import { registerBranchesCommand } from "../commands/branches.js";
|
|
17
|
-
import { registerCheckCommitCommand } from "../commands/check-commit.js";
|
|
18
|
-
import { registerCompressOutputCommand } from "../commands/compress-output.js";
|
|
19
|
-
import { registerConfigVerifyCommand } from "../commands/config-verify.js";
|
|
20
|
-
import { registerDaemonCommand } from "../commands/daemon.js";
|
|
21
|
-
import { registerDashboardCommand } from "../commands/dashboard.js";
|
|
22
|
-
import { registerDebugCommand } from "../commands/debug.js";
|
|
23
|
-
import { registerEnrichCommand } from "../commands/enrich.js";
|
|
24
|
-
import { registerExecCommand } from "../commands/exec.js";
|
|
25
|
-
import { registerDiscoverCommand, registerGainCommand, } from "../commands/gain.js";
|
|
26
|
-
import { registerHookCommand } from "../commands/hook.js";
|
|
27
|
-
import { registerIndexCommand } from "../commands/index.js";
|
|
28
|
-
import { registerInitCommand } from "../commands/init.js";
|
|
29
|
-
import { registerInstallCommand } from "../commands/install.js";
|
|
30
|
-
import { registerLearnCommand } from "../commands/learn.js";
|
|
31
|
-
import { registerManifestCommand } from "../commands/manifest.js";
|
|
32
|
-
import { registerRewindCommand } from "../commands/rewind.js";
|
|
33
|
-
import { registerSkillsCommand } from "../commands/skills.js";
|
|
34
|
-
import { registerStatsCommand } from "../commands/stats.js";
|
|
35
|
-
import { registerStatusCommand } from "../commands/status.js";
|
|
36
|
-
import { registerTimelineCommand } from "../commands/timeline.js";
|
|
37
|
-
import { registerUninstallCommand } from "../commands/uninstall.js";
|
|
38
|
-
import { installFileLogger } from "../utils/file-logger.js";
|
|
39
|
-
import { initFileLog } from "../utils/startup-log.js";
|
|
40
|
-
// ── Helpers ─────────────────────────────────────────────────
|
|
41
|
-
/**
|
|
42
|
-
* Start the unified proxy.
|
|
43
|
-
*/
|
|
44
|
-
async function startProxy(repoId) {
|
|
45
|
-
const { startProxy: boot } = await import("../proxy/proxy.js");
|
|
46
|
-
const httpPort = Number.parseInt(process.env.UNERR_HTTP_PORT ?? "0", 10);
|
|
47
|
-
await boot({ repoId, httpPort: httpPort || undefined });
|
|
48
|
-
}
|
|
49
|
-
/**
|
|
50
|
-
* Auto-verify and repair IDE MCP configs before proxy start.
|
|
51
|
-
* Silently fixes stale MCP configs → local proxy format.
|
|
52
|
-
*/
|
|
53
|
-
async function autoVerifyIdeConfigs() {
|
|
54
|
-
try {
|
|
55
|
-
const { checkIdeConfig, repairIdeConfig } = await import("../commands/config-verify.js");
|
|
56
|
-
const os = await import("node:os");
|
|
57
|
-
const path = await import("node:path");
|
|
58
|
-
const ideConfigs = {
|
|
59
|
-
cursor: path.join(os.homedir(), ".cursor", "mcp.json"),
|
|
60
|
-
vscode: path.join(os.homedir(), ".vscode", "settings.json"),
|
|
61
|
-
windsurf: path.join(os.homedir(), ".windsurf", "mcp.json"),
|
|
62
|
-
};
|
|
63
|
-
for (const [ideName, configPath] of Object.entries(ideConfigs)) {
|
|
64
|
-
const result = checkIdeConfig(ideName, configPath);
|
|
65
|
-
if (result.found && result.needsMigration) {
|
|
66
|
-
const repaired = repairIdeConfig(ideName, configPath);
|
|
67
|
-
if (repaired) {
|
|
68
|
-
process.stderr.write(`[unerr] Auto-repaired ${ideName} MCP config (remote URL → local proxy)\n`);
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
catch {
|
|
74
|
-
// Non-blocking — config verification should never prevent proxy startup
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
/**
|
|
78
|
-
* Check if we're inside a git repository.
|
|
79
|
-
*/
|
|
80
|
-
async function isInsideGitRepo() {
|
|
81
|
-
const { isGitRepo } = await import("../utils/git.js");
|
|
82
|
-
return isGitRepo(process.cwd());
|
|
83
|
-
}
|
|
84
|
-
// ── Project Root Detection (Scored Multi-Signal Analysis) ─────
|
|
85
|
-
/**
|
|
86
|
-
* TIER 1: Definitive project markers — files whose presence at root
|
|
87
|
-
* guarantees this is a project directory. Grouped by ecosystem.
|
|
88
|
-
*/
|
|
89
|
-
const DEFINITIVE_MARKERS = [
|
|
90
|
-
// JavaScript / TypeScript
|
|
91
|
-
"package.json",
|
|
92
|
-
"tsconfig.json",
|
|
93
|
-
"jsconfig.json",
|
|
94
|
-
"deno.json",
|
|
95
|
-
"deno.jsonc",
|
|
96
|
-
"bun.lockb",
|
|
97
|
-
"bunfig.toml",
|
|
98
|
-
// Python
|
|
99
|
-
"pyproject.toml",
|
|
100
|
-
"setup.py",
|
|
101
|
-
"setup.cfg",
|
|
102
|
-
"Pipfile",
|
|
103
|
-
"hatch.toml",
|
|
104
|
-
// Rust
|
|
105
|
-
"Cargo.toml",
|
|
106
|
-
// Go
|
|
107
|
-
"go.mod",
|
|
108
|
-
// Java / Kotlin / Gradle
|
|
109
|
-
"pom.xml",
|
|
110
|
-
"build.gradle",
|
|
111
|
-
"build.gradle.kts",
|
|
112
|
-
"settings.gradle",
|
|
113
|
-
"settings.gradle.kts",
|
|
114
|
-
// C# / .NET
|
|
115
|
-
"Directory.Build.props",
|
|
116
|
-
"global.json",
|
|
117
|
-
"nuget.config",
|
|
118
|
-
// Ruby
|
|
119
|
-
"Gemfile",
|
|
120
|
-
"Rakefile",
|
|
121
|
-
// PHP
|
|
122
|
-
"composer.json",
|
|
123
|
-
// Swift / Obj-C / Apple
|
|
124
|
-
"Package.swift",
|
|
125
|
-
"Podfile",
|
|
126
|
-
// C / C++
|
|
127
|
-
"CMakeLists.txt",
|
|
128
|
-
"Makefile",
|
|
129
|
-
"configure.ac",
|
|
130
|
-
"meson.build",
|
|
131
|
-
"conanfile.txt",
|
|
132
|
-
"conanfile.py",
|
|
133
|
-
"vcpkg.json",
|
|
134
|
-
// Dart / Flutter
|
|
135
|
-
"pubspec.yaml",
|
|
136
|
-
// Elixir
|
|
137
|
-
"mix.exs",
|
|
138
|
-
// Scala
|
|
139
|
-
"build.sbt",
|
|
140
|
-
"build.sc",
|
|
141
|
-
// Haskell
|
|
142
|
-
"stack.yaml",
|
|
143
|
-
"cabal.project",
|
|
144
|
-
// Clojure
|
|
145
|
-
"project.clj",
|
|
146
|
-
"deps.edn",
|
|
147
|
-
"shadow-cljs.edn",
|
|
148
|
-
// Zig
|
|
149
|
-
"build.zig",
|
|
150
|
-
"build.zig.zon",
|
|
151
|
-
// Julia
|
|
152
|
-
"Project.toml",
|
|
153
|
-
// Terraform / IaC
|
|
154
|
-
"main.tf",
|
|
155
|
-
"terraform.tf",
|
|
156
|
-
"pulumi.yaml",
|
|
157
|
-
"serverless.yml",
|
|
158
|
-
"cdk.json",
|
|
159
|
-
"sam.yaml",
|
|
160
|
-
// Containers
|
|
161
|
-
"Dockerfile",
|
|
162
|
-
"docker-compose.yml",
|
|
163
|
-
"docker-compose.yaml",
|
|
164
|
-
// Monorepo
|
|
165
|
-
"lerna.json",
|
|
166
|
-
"nx.json",
|
|
167
|
-
"turbo.json",
|
|
168
|
-
"pnpm-workspace.yaml",
|
|
169
|
-
"rush.json",
|
|
170
|
-
"pants.toml",
|
|
171
|
-
// Bazel
|
|
172
|
-
"BUILD.bazel",
|
|
173
|
-
"WORKSPACE",
|
|
174
|
-
"WORKSPACE.bazel",
|
|
175
|
-
// General build / task
|
|
176
|
-
"Justfile",
|
|
177
|
-
"Taskfile.yml",
|
|
178
|
-
// Nix
|
|
179
|
-
"flake.nix",
|
|
180
|
-
"shell.nix",
|
|
181
|
-
"default.nix",
|
|
182
|
-
// Android
|
|
183
|
-
"AndroidManifest.xml",
|
|
184
|
-
];
|
|
185
|
-
/**
|
|
186
|
-
* TIER 2: Strong signals — VCS, IDE, CI/CD, lock files, editor configs.
|
|
187
|
-
* Not definitive alone (a stray .gitignore doesn't make a project) but
|
|
188
|
-
* contribute significant score.
|
|
189
|
-
*/
|
|
190
|
-
const STRONG_SIGNAL_FILES = [
|
|
191
|
-
// Lock files (imply a package manager was run here)
|
|
192
|
-
"package-lock.json",
|
|
193
|
-
"yarn.lock",
|
|
194
|
-
"pnpm-lock.yaml",
|
|
195
|
-
"Pipfile.lock",
|
|
196
|
-
"poetry.lock",
|
|
197
|
-
"Gemfile.lock",
|
|
198
|
-
"composer.lock",
|
|
199
|
-
"Cargo.lock",
|
|
200
|
-
"flake.lock",
|
|
201
|
-
"pubspec.lock",
|
|
202
|
-
"mix.lock",
|
|
203
|
-
"go.sum",
|
|
204
|
-
"uv.lock",
|
|
205
|
-
"pdm.lock",
|
|
206
|
-
// CI/CD (a CI config strongly implies project root)
|
|
207
|
-
".gitlab-ci.yml",
|
|
208
|
-
"Jenkinsfile",
|
|
209
|
-
".travis.yml",
|
|
210
|
-
"azure-pipelines.yml",
|
|
211
|
-
"bitbucket-pipelines.yml",
|
|
212
|
-
// Editor / LSP configs (placed at project root)
|
|
213
|
-
".editorconfig",
|
|
214
|
-
".prettierrc",
|
|
215
|
-
".prettierrc.json",
|
|
216
|
-
".eslintrc",
|
|
217
|
-
".eslintrc.json",
|
|
218
|
-
".eslintrc.js",
|
|
219
|
-
"biome.json",
|
|
220
|
-
"biome.jsonc",
|
|
221
|
-
"pyrightconfig.json",
|
|
222
|
-
"rust-toolchain.toml",
|
|
223
|
-
".clang-format",
|
|
224
|
-
".clangd",
|
|
225
|
-
"compile_commands.json",
|
|
226
|
-
".luarc.json",
|
|
227
|
-
"stylua.toml",
|
|
228
|
-
".rubocop.yml",
|
|
229
|
-
".php-cs-fixer.php",
|
|
230
|
-
// Environment
|
|
231
|
-
".env",
|
|
232
|
-
".envrc",
|
|
233
|
-
// Git-specific (at root level these are strong signals)
|
|
234
|
-
".gitignore",
|
|
235
|
-
".gitattributes",
|
|
236
|
-
];
|
|
237
|
-
/** TIER 2: Directories that are strong signals when present at root. */
|
|
238
|
-
const STRONG_SIGNAL_DIRS = [
|
|
239
|
-
".git",
|
|
240
|
-
".svn",
|
|
241
|
-
".hg",
|
|
242
|
-
".fossil",
|
|
243
|
-
".bzr",
|
|
244
|
-
"_darcs", // VCS
|
|
245
|
-
".github",
|
|
246
|
-
".circleci",
|
|
247
|
-
".buildkite", // CI/CD
|
|
248
|
-
".idea",
|
|
249
|
-
".vscode",
|
|
250
|
-
".vs",
|
|
251
|
-
".fleet",
|
|
252
|
-
".zed", // IDE
|
|
253
|
-
];
|
|
254
|
-
/** Glob patterns for file-extension markers (e.g., *.sln, *.csproj). */
|
|
255
|
-
const EXTENSION_MARKERS = [
|
|
256
|
-
".sln",
|
|
257
|
-
".csproj",
|
|
258
|
-
".fsproj",
|
|
259
|
-
".vbproj", // .NET
|
|
260
|
-
".xcodeproj",
|
|
261
|
-
".xcworkspace", // Xcode (dirs)
|
|
262
|
-
".cabal", // Haskell
|
|
263
|
-
".nimble", // Nim
|
|
264
|
-
".gemspec", // Ruby
|
|
265
|
-
".rockspec", // Lua
|
|
266
|
-
];
|
|
267
|
-
/**
|
|
268
|
-
* TIER 3: Code file extensions — comprehensive across all mainstream
|
|
269
|
-
* and emerging languages. Used to scan root + source dirs.
|
|
270
|
-
*/
|
|
271
|
-
const CODE_EXTENSIONS = new Set([
|
|
272
|
-
// Web / JS ecosystem
|
|
273
|
-
".ts",
|
|
274
|
-
".tsx",
|
|
275
|
-
".js",
|
|
276
|
-
".jsx",
|
|
277
|
-
".mjs",
|
|
278
|
-
".cjs",
|
|
279
|
-
".vue",
|
|
280
|
-
".svelte",
|
|
281
|
-
".astro",
|
|
282
|
-
// Systems
|
|
283
|
-
".rs",
|
|
284
|
-
".go",
|
|
285
|
-
".c",
|
|
286
|
-
".h",
|
|
287
|
-
".cpp",
|
|
288
|
-
".cc",
|
|
289
|
-
".cxx",
|
|
290
|
-
".hpp",
|
|
291
|
-
".hxx",
|
|
292
|
-
".zig",
|
|
293
|
-
// JVM
|
|
294
|
-
".java",
|
|
295
|
-
".kt",
|
|
296
|
-
".kts",
|
|
297
|
-
".scala",
|
|
298
|
-
".sc",
|
|
299
|
-
".clj",
|
|
300
|
-
".cljs",
|
|
301
|
-
".cljc",
|
|
302
|
-
// .NET
|
|
303
|
-
".cs",
|
|
304
|
-
".fs",
|
|
305
|
-
".fsx",
|
|
306
|
-
".vb",
|
|
307
|
-
// Scripting
|
|
308
|
-
".py",
|
|
309
|
-
".pyi",
|
|
310
|
-
".rb",
|
|
311
|
-
".php",
|
|
312
|
-
".lua",
|
|
313
|
-
".pl",
|
|
314
|
-
".pm",
|
|
315
|
-
".raku",
|
|
316
|
-
// Apple / Mobile
|
|
317
|
-
".swift",
|
|
318
|
-
".m",
|
|
319
|
-
".mm",
|
|
320
|
-
".dart",
|
|
321
|
-
// Functional
|
|
322
|
-
".hs",
|
|
323
|
-
".lhs",
|
|
324
|
-
".ml",
|
|
325
|
-
".mli",
|
|
326
|
-
".re",
|
|
327
|
-
".rei",
|
|
328
|
-
".elm",
|
|
329
|
-
".ex",
|
|
330
|
-
".exs",
|
|
331
|
-
".purs",
|
|
332
|
-
".idr",
|
|
333
|
-
".agda",
|
|
334
|
-
".lean",
|
|
335
|
-
// Data / Science
|
|
336
|
-
".r",
|
|
337
|
-
".R",
|
|
338
|
-
".jl",
|
|
339
|
-
// Infrastructure
|
|
340
|
-
".tf",
|
|
341
|
-
".hcl",
|
|
342
|
-
// Emerging / Niche
|
|
343
|
-
".nim",
|
|
344
|
-
".cr",
|
|
345
|
-
".d",
|
|
346
|
-
".v",
|
|
347
|
-
".sol",
|
|
348
|
-
".move",
|
|
349
|
-
".cairo",
|
|
350
|
-
".nr",
|
|
351
|
-
// Shell / Config as code
|
|
352
|
-
".sh",
|
|
353
|
-
".bash",
|
|
354
|
-
".zsh",
|
|
355
|
-
".fish",
|
|
356
|
-
".ps1",
|
|
357
|
-
]);
|
|
358
|
-
/**
|
|
359
|
-
* TIER 4: Anti-signals — files/dirs that indicate this is a home dir,
|
|
360
|
-
* system path, or otherwise NOT a project root. Each match subtracts score.
|
|
361
|
-
*/
|
|
362
|
-
const ANTI_SIGNAL_FILES = [
|
|
363
|
-
".bashrc",
|
|
364
|
-
".zshrc",
|
|
365
|
-
".bash_profile",
|
|
366
|
-
".profile",
|
|
367
|
-
".zprofile",
|
|
368
|
-
".zshenv",
|
|
369
|
-
".bash_history",
|
|
370
|
-
".zsh_history",
|
|
371
|
-
];
|
|
372
|
-
const ANTI_SIGNAL_DIRS = [
|
|
373
|
-
".config",
|
|
374
|
-
".local",
|
|
375
|
-
".cache",
|
|
376
|
-
".ssh",
|
|
377
|
-
".gnupg",
|
|
378
|
-
".npm",
|
|
379
|
-
".cargo",
|
|
380
|
-
"Desktop",
|
|
381
|
-
"Downloads",
|
|
382
|
-
"Documents",
|
|
383
|
-
"Pictures",
|
|
384
|
-
"Music",
|
|
385
|
-
"Movies",
|
|
386
|
-
"Library",
|
|
387
|
-
"Applications",
|
|
388
|
-
];
|
|
389
|
-
const ANTI_SIGNAL_PATHS = [
|
|
390
|
-
"/",
|
|
391
|
-
"/usr",
|
|
392
|
-
"/tmp",
|
|
393
|
-
"/var",
|
|
394
|
-
"/etc",
|
|
395
|
-
"/opt",
|
|
396
|
-
"/home",
|
|
397
|
-
"/Users",
|
|
398
|
-
"/root",
|
|
399
|
-
];
|
|
400
|
-
/**
|
|
401
|
-
* TIER 5: Well-known source directories — if these exist at root AND
|
|
402
|
-
* contain code files, that's a strong project signal.
|
|
403
|
-
*/
|
|
404
|
-
const SOURCE_DIRS = new Set([
|
|
405
|
-
"src",
|
|
406
|
-
"lib",
|
|
407
|
-
"app",
|
|
408
|
-
"apps",
|
|
409
|
-
"cmd",
|
|
410
|
-
"pkg",
|
|
411
|
-
"packages",
|
|
412
|
-
"internal",
|
|
413
|
-
"modules",
|
|
414
|
-
"components",
|
|
415
|
-
"pages",
|
|
416
|
-
"routes",
|
|
417
|
-
"api",
|
|
418
|
-
"server",
|
|
419
|
-
"client",
|
|
420
|
-
"web",
|
|
421
|
-
"core",
|
|
422
|
-
"common",
|
|
423
|
-
"shared",
|
|
424
|
-
"utils",
|
|
425
|
-
"helpers",
|
|
426
|
-
"services",
|
|
427
|
-
"models",
|
|
428
|
-
"views",
|
|
429
|
-
"controllers",
|
|
430
|
-
"handlers",
|
|
431
|
-
"middleware",
|
|
432
|
-
"plugins",
|
|
433
|
-
"test",
|
|
434
|
-
"tests",
|
|
435
|
-
"spec",
|
|
436
|
-
"__tests__",
|
|
437
|
-
"e2e",
|
|
438
|
-
"integration",
|
|
439
|
-
"scripts",
|
|
440
|
-
"tools",
|
|
441
|
-
"bin",
|
|
442
|
-
"examples",
|
|
443
|
-
"samples",
|
|
444
|
-
"proto",
|
|
445
|
-
"schemas",
|
|
446
|
-
"migrations",
|
|
447
|
-
"seeds",
|
|
448
|
-
]);
|
|
449
|
-
/**
|
|
450
|
-
* Scored project-root detection. Accumulates evidence across 5 tiers
|
|
451
|
-
* and subtracts anti-signals. A score >= THRESHOLD means "this is a project".
|
|
452
|
-
*
|
|
453
|
-
* Scoring weights:
|
|
454
|
-
* Definitive marker file: +10 (instant pass)
|
|
455
|
-
* VCS directory (.git, .hg): +8
|
|
456
|
-
* Strong signal file/dir: +4
|
|
457
|
-
* Extension marker (*.sln): +10
|
|
458
|
-
* Code files in root: +5
|
|
459
|
-
* Code files in source dir: +6
|
|
460
|
-
* Anti-signal file: -3
|
|
461
|
-
* Anti-signal directory: -2
|
|
462
|
-
* Anti-signal path: -15 (instant fail for /, /usr, etc.)
|
|
463
|
-
*
|
|
464
|
-
* Threshold: 5 (a single definitive marker or VCS dir passes)
|
|
465
|
-
*/
|
|
466
|
-
const DETECTION_THRESHOLD = 5;
|
|
467
|
-
async function detectProjectRoot(cwd) {
|
|
468
|
-
const { readdirSync } = await import("node:fs");
|
|
469
|
-
let score = 0;
|
|
470
|
-
const signals = [];
|
|
471
|
-
let hasGit = false;
|
|
472
|
-
// ── TIER 4 first: Anti-signal paths (early reject) ──────────
|
|
473
|
-
const normalizedCwd = cwd.replace(/\\/g, "/");
|
|
474
|
-
for (const ap of ANTI_SIGNAL_PATHS) {
|
|
475
|
-
if (normalizedCwd === ap || normalizedCwd === `${ap}/`) {
|
|
476
|
-
return {
|
|
477
|
-
isProject: false,
|
|
478
|
-
hasGit: false,
|
|
479
|
-
reason: `System/root path: ${cwd}`,
|
|
480
|
-
score: -15,
|
|
481
|
-
signals: [`anti-path: ${ap}`],
|
|
482
|
-
};
|
|
483
|
-
}
|
|
484
|
-
}
|
|
485
|
-
// Read root directory entries once
|
|
486
|
-
let rootEntries = [];
|
|
487
|
-
try {
|
|
488
|
-
rootEntries = readdirSync(cwd, { withFileTypes: true }).map((e) => ({
|
|
489
|
-
name: e.name,
|
|
490
|
-
isFile: e.isFile(),
|
|
491
|
-
isDir: e.isDirectory(),
|
|
492
|
-
}));
|
|
493
|
-
}
|
|
494
|
-
catch {
|
|
495
|
-
return {
|
|
496
|
-
isProject: false,
|
|
497
|
-
hasGit: false,
|
|
498
|
-
reason: "Cannot read directory",
|
|
499
|
-
score: -1,
|
|
500
|
-
signals: ["unreadable"],
|
|
501
|
-
};
|
|
502
|
-
}
|
|
503
|
-
const rootFileNames = new Set(rootEntries.filter((e) => e.isFile).map((e) => e.name));
|
|
504
|
-
const rootDirNames = new Set(rootEntries.filter((e) => e.isDir).map((e) => e.name));
|
|
505
|
-
// ── TIER 4: Anti-signal files & dirs at root ────────────────
|
|
506
|
-
for (const af of ANTI_SIGNAL_FILES) {
|
|
507
|
-
if (rootFileNames.has(af)) {
|
|
508
|
-
score -= 3;
|
|
509
|
-
signals.push(`anti-file: ${af}`);
|
|
510
|
-
}
|
|
511
|
-
}
|
|
512
|
-
for (const ad of ANTI_SIGNAL_DIRS) {
|
|
513
|
-
if (rootDirNames.has(ad)) {
|
|
514
|
-
score -= 2;
|
|
515
|
-
signals.push(`anti-dir: ${ad}/`);
|
|
516
|
-
}
|
|
517
|
-
}
|
|
518
|
-
// ── TIER 1: Definitive marker files ─────────────────────────
|
|
519
|
-
for (const marker of DEFINITIVE_MARKERS) {
|
|
520
|
-
if (rootFileNames.has(marker)) {
|
|
521
|
-
score += 10;
|
|
522
|
-
signals.push(`marker: ${marker}`);
|
|
523
|
-
break; // one is enough
|
|
524
|
-
}
|
|
525
|
-
}
|
|
526
|
-
// ── TIER 1: Extension-based markers (*.sln, *.csproj, etc.) ─
|
|
527
|
-
if (score < DETECTION_THRESHOLD) {
|
|
528
|
-
for (const entry of rootEntries) {
|
|
529
|
-
if (!entry.isFile && !entry.isDir)
|
|
530
|
-
continue;
|
|
531
|
-
const name = entry.name.toLowerCase();
|
|
532
|
-
for (const ext of EXTENSION_MARKERS) {
|
|
533
|
-
if (name.endsWith(ext)) {
|
|
534
|
-
score += 10;
|
|
535
|
-
signals.push(`ext-marker: ${entry.name}`);
|
|
536
|
-
break;
|
|
537
|
-
}
|
|
538
|
-
}
|
|
539
|
-
if (score >= DETECTION_THRESHOLD)
|
|
540
|
-
break;
|
|
541
|
-
}
|
|
542
|
-
}
|
|
543
|
-
// ── TIER 2: VCS directories ─────────────────────────────────
|
|
544
|
-
for (const vcs of STRONG_SIGNAL_DIRS.slice(0, 6)) {
|
|
545
|
-
if (rootDirNames.has(vcs)) {
|
|
546
|
-
if (vcs === ".git")
|
|
547
|
-
hasGit = true;
|
|
548
|
-
score += 8;
|
|
549
|
-
signals.push(`vcs: ${vcs}/`);
|
|
550
|
-
break;
|
|
551
|
-
}
|
|
552
|
-
}
|
|
553
|
-
// ── TIER 2: Strong signal files ─────────────────────────────
|
|
554
|
-
for (const sf of STRONG_SIGNAL_FILES) {
|
|
555
|
-
if (rootFileNames.has(sf)) {
|
|
556
|
-
score += 4;
|
|
557
|
-
signals.push(`strong: ${sf}`);
|
|
558
|
-
if (score >= DETECTION_THRESHOLD)
|
|
559
|
-
break;
|
|
560
|
-
}
|
|
561
|
-
}
|
|
562
|
-
// ── TIER 2: Strong signal dirs (IDE, CI) ────────────────────
|
|
563
|
-
for (const sd of STRONG_SIGNAL_DIRS.slice(6)) {
|
|
564
|
-
if (rootDirNames.has(sd)) {
|
|
565
|
-
score += 4;
|
|
566
|
-
signals.push(`strong: ${sd}/`);
|
|
567
|
-
if (score >= DETECTION_THRESHOLD)
|
|
568
|
-
break;
|
|
569
|
-
}
|
|
570
|
-
}
|
|
571
|
-
// ── TIER 2: Git repo check (may be in a parent .git) ───────
|
|
572
|
-
if (!hasGit) {
|
|
573
|
-
try {
|
|
574
|
-
hasGit = await isInsideGitRepo();
|
|
575
|
-
if (hasGit) {
|
|
576
|
-
score += 6;
|
|
577
|
-
signals.push("git: inside work tree");
|
|
578
|
-
}
|
|
579
|
-
}
|
|
580
|
-
catch {
|
|
581
|
-
// ignore
|
|
582
|
-
}
|
|
583
|
-
}
|
|
584
|
-
// Already passing? Skip expensive file scan.
|
|
585
|
-
if (score >= DETECTION_THRESHOLD) {
|
|
586
|
-
const topSignal = signals.find((s) => !s.startsWith("anti-")) ?? signals[0] ?? "unknown";
|
|
587
|
-
return { isProject: true, hasGit, reason: topSignal, score, signals };
|
|
588
|
-
}
|
|
589
|
-
// ── TIER 3 + 5: Code files in root, then source dirs ───────
|
|
590
|
-
function dirHasCodeFile(dir) {
|
|
591
|
-
try {
|
|
592
|
-
const entries = readdirSync(dir, { withFileTypes: true });
|
|
593
|
-
for (const entry of entries) {
|
|
594
|
-
if (!entry.isFile())
|
|
595
|
-
continue;
|
|
596
|
-
const dot = entry.name.lastIndexOf(".");
|
|
597
|
-
if (dot >= 0 &&
|
|
598
|
-
CODE_EXTENSIONS.has(entry.name.slice(dot).toLowerCase())) {
|
|
599
|
-
return entry.name;
|
|
600
|
-
}
|
|
601
|
-
}
|
|
602
|
-
}
|
|
603
|
-
catch {
|
|
604
|
-
// skip
|
|
605
|
-
}
|
|
606
|
-
return null;
|
|
607
|
-
}
|
|
608
|
-
// Code files directly in cwd
|
|
609
|
-
const rootCodeFile = dirHasCodeFile(cwd);
|
|
610
|
-
if (rootCodeFile) {
|
|
611
|
-
score += 5;
|
|
612
|
-
signals.push(`code-root: ${rootCodeFile}`);
|
|
613
|
-
}
|
|
614
|
-
if (score >= DETECTION_THRESHOLD) {
|
|
615
|
-
const topSignal = signals.find((s) => !s.startsWith("anti-")) ?? signals[0] ?? "unknown";
|
|
616
|
-
return { isProject: true, hasGit, reason: topSignal, score, signals };
|
|
617
|
-
}
|
|
618
|
-
// Code files inside well-known source dirs (+ one level deeper)
|
|
619
|
-
for (const dirEntry of rootEntries) {
|
|
620
|
-
if (!dirEntry.isDir || dirEntry.name.startsWith("."))
|
|
621
|
-
continue;
|
|
622
|
-
if (!SOURCE_DIRS.has(dirEntry.name.toLowerCase()))
|
|
623
|
-
continue;
|
|
624
|
-
const sourceDir = join(cwd, dirEntry.name);
|
|
625
|
-
const srcCodeFile = dirHasCodeFile(sourceDir);
|
|
626
|
-
if (srcCodeFile) {
|
|
627
|
-
score += 6;
|
|
628
|
-
signals.push(`code-src: ${dirEntry.name}/${srcCodeFile}`);
|
|
629
|
-
break;
|
|
630
|
-
}
|
|
631
|
-
// One level deeper (src/components/, lib/utils/)
|
|
632
|
-
try {
|
|
633
|
-
const subEntries = readdirSync(sourceDir, { withFileTypes: true });
|
|
634
|
-
let found = false;
|
|
635
|
-
for (const sub of subEntries) {
|
|
636
|
-
if (!sub.isDirectory() || sub.name.startsWith("."))
|
|
637
|
-
continue;
|
|
638
|
-
const deepCode = dirHasCodeFile(join(sourceDir, sub.name));
|
|
639
|
-
if (deepCode) {
|
|
640
|
-
score += 6;
|
|
641
|
-
signals.push(`code-src: ${dirEntry.name}/${sub.name}/${deepCode}`);
|
|
642
|
-
found = true;
|
|
643
|
-
break;
|
|
644
|
-
}
|
|
645
|
-
}
|
|
646
|
-
if (found)
|
|
647
|
-
break;
|
|
648
|
-
}
|
|
649
|
-
catch {
|
|
650
|
-
// skip
|
|
651
|
-
}
|
|
652
|
-
}
|
|
653
|
-
const isProject = score >= DETECTION_THRESHOLD;
|
|
654
|
-
const topSignal = signals.find((s) => !s.startsWith("anti-")) ?? signals[0] ?? "No signals";
|
|
655
|
-
return {
|
|
656
|
-
isProject,
|
|
657
|
-
hasGit,
|
|
658
|
-
reason: isProject ? topSignal : "No code files or project markers found",
|
|
659
|
-
score,
|
|
660
|
-
signals,
|
|
661
|
-
};
|
|
662
|
-
}
|
|
663
|
-
/**
|
|
664
|
-
* Read .unerr/config.json if it exists.
|
|
665
|
-
*/
|
|
666
|
-
function readLocalConfig(cwd) {
|
|
667
|
-
const configPath = join(cwd, ".unerr", "config.json");
|
|
668
|
-
if (!existsSync(configPath))
|
|
669
|
-
return null;
|
|
670
|
-
try {
|
|
671
|
-
return JSON.parse(readFileSync(configPath, "utf-8"));
|
|
672
|
-
}
|
|
673
|
-
catch {
|
|
674
|
-
return null;
|
|
675
|
-
}
|
|
676
|
-
}
|
|
677
|
-
// ── Boot State Machine ─────────────────────────────────────
|
|
678
|
-
/**
|
|
679
|
-
* Resume path: config exists, skip all interactive prompts.
|
|
680
|
-
*/
|
|
681
|
-
async function resumeBoot(config) {
|
|
682
|
-
initFileLog(process.cwd());
|
|
683
|
-
// Verify this is still a valid project directory (config may be stale)
|
|
684
|
-
const detection = await detectProjectRoot(process.cwd());
|
|
685
|
-
if (!detection.isProject) {
|
|
686
|
-
const antiSignals = detection.signals.filter((s) => s.startsWith("anti-"));
|
|
687
|
-
process.stderr.write(`\x1b[38;2;248;113;113m\u2717\x1b[0m No code project detected in this directory.\n Found .unerr/config.json but the directory doesn't look like a project root.\n Detection score: ${detection.score} (need ${DETECTION_THRESHOLD})\n${antiSignals.length > 0
|
|
688
|
-
? ` Negative signals: ${antiSignals.map((s) => s.replace("anti-", "")).join(", ")}\n`
|
|
689
|
-
: ""}\n unerr checks for: project files (package.json, Cargo.toml, go.mod, ...),\n VCS directories (.git, .hg), IDE configs, CI/CD files, lock files,\n and source code across 50+ languages.\n\n \u25b8 Run \x1b[1munerr\x1b[0m from a project root directory.\n`);
|
|
690
|
-
process.exit(1);
|
|
691
|
-
}
|
|
692
|
-
const { initSessionLogger, createSessionModuleLogger } = await import("../utils/session-logger.js");
|
|
693
|
-
initSessionLogger();
|
|
694
|
-
const log = createSessionModuleLogger("boot");
|
|
695
|
-
log.info({ msg: "Resume boot", mode: "local", repoId: config.repoId });
|
|
696
|
-
// Show update notice (non-blocking, from cache only)
|
|
697
|
-
try {
|
|
698
|
-
const { getCachedUpdateInfo } = await import("../daemon/version-checker.js");
|
|
699
|
-
const info = getCachedUpdateInfo();
|
|
700
|
-
if (info.available && !info.dismissed) {
|
|
701
|
-
process.stderr.write(`\x1b[38;2;34;211;238m▸\x1b[0m Update available: ${info.current} → ${info.latest} — run \x1b[38;2;139;92;246munerr daemon update\x1b[0m\n`);
|
|
702
|
-
}
|
|
703
|
-
}
|
|
704
|
-
catch {
|
|
705
|
-
// Non-blocking
|
|
706
|
-
}
|
|
707
|
-
process.stderr.write("[unerr] Starting proxy...\n");
|
|
708
|
-
await autoVerifyIdeConfigs();
|
|
709
|
-
await startProxy(config.repoId);
|
|
710
|
-
}
|
|
711
|
-
/**
|
|
712
|
-
* First-run path: no config exists. Auto-detect environment, prompt if needed.
|
|
713
|
-
*/
|
|
714
|
-
async function firstRunBoot() {
|
|
715
|
-
initFileLog(process.cwd());
|
|
716
|
-
const { initSessionLogger, createSessionModuleLogger } = await import("../utils/session-logger.js");
|
|
717
|
-
initSessionLogger();
|
|
718
|
-
const log = createSessionModuleLogger("boot");
|
|
719
|
-
// Must be in a project directory
|
|
720
|
-
const detection = await detectProjectRoot(process.cwd());
|
|
721
|
-
if (!detection.isProject) {
|
|
722
|
-
const antiSignals = detection.signals.filter((s) => s.startsWith("anti-"));
|
|
723
|
-
process.stderr.write(`\x1b[38;2;248;113;113m\u2717\x1b[0m No code project detected in this directory.\n unerr needs a folder containing source code to index.\n Detection score: ${detection.score} (need ${DETECTION_THRESHOLD})\n${antiSignals.length > 0
|
|
724
|
-
? ` Negative signals: ${antiSignals.map((s) => s.replace("anti-", "")).join(", ")}\n`
|
|
725
|
-
: ""}\n Checked for: project files (package.json, Cargo.toml, go.mod, pyproject.toml, ...),\n VCS directories (.git, .hg, .svn), IDE configs (.vscode, .idea),\n CI/CD files (.github, Jenkinsfile), lock files, and source code\n across 50+ languages in root + standard source directories.\n\n \u25b8 Run \x1b[1munerr\x1b[0m from a project root directory.\n`);
|
|
726
|
-
process.exit(1);
|
|
727
|
-
}
|
|
728
|
-
if (!detection.hasGit) {
|
|
729
|
-
process.stderr.write(`\x1b[38;2;251;191;36m\u26a0\x1b[0m Project detected (${detection.reason}) but no git repository found.\n unerr works best with git. Consider running \x1b[1mgit init\x1b[0m first.\n Continuing anyway...\n\n`);
|
|
730
|
-
}
|
|
731
|
-
log.info({
|
|
732
|
-
msg: "First-run boot, entering local setup",
|
|
733
|
-
detection: detection.reason,
|
|
734
|
-
score: detection.score,
|
|
735
|
-
hasGit: detection.hasGit,
|
|
736
|
-
});
|
|
737
|
-
const { runSetup } = await import("../commands/setup-wizard.js");
|
|
738
|
-
const result = await runSetup();
|
|
739
|
-
if (result.action === "setup") {
|
|
740
|
-
await autoVerifyIdeConfigs();
|
|
741
|
-
await startProxy(result.repoId);
|
|
742
|
-
return;
|
|
743
|
-
}
|
|
744
|
-
process.exit(0);
|
|
745
|
-
}
|
|
746
|
-
/**
|
|
747
|
-
* Daemon-child mode: spawned and managed by unerrd.
|
|
748
|
-
*
|
|
749
|
-
* Differences from standalone `unerr`:
|
|
750
|
-
* - No interactive prompts (process.stdin.isTTY is false)
|
|
751
|
-
* - IPC channel to parent: sends { type: "ready", sock }, { type: "activity" }
|
|
752
|
-
* - SIGTERM triggers snapshot + clean exit (no delay)
|
|
753
|
-
* - Orphan detection: checks ppid every 60s, self-exits if parent died
|
|
754
|
-
* - Activity reports: stats sent every 60s while running
|
|
755
|
-
*/
|
|
756
|
-
async function daemonChildBoot(cwd) {
|
|
757
|
-
installFileLogger({
|
|
758
|
-
filePath: join(cwd, ".unerr", "logs", `child-${process.pid}.log`),
|
|
759
|
-
maxBytes: 5_000_000,
|
|
760
|
-
keep: 5,
|
|
761
|
-
});
|
|
762
|
-
initFileLog(cwd);
|
|
763
|
-
const config = readLocalConfig(cwd);
|
|
764
|
-
if (!config) {
|
|
765
|
-
process.stderr.write("[unerr:child] No .unerr/config.json — run `unerr` interactively first.\n");
|
|
766
|
-
process.exit(1);
|
|
767
|
-
}
|
|
768
|
-
const originalPpid = process.ppid;
|
|
769
|
-
// Orphan detection: if parent (unerrd) dies, self-exit
|
|
770
|
-
const orphanTimer = setInterval(() => {
|
|
771
|
-
if (process.ppid !== originalPpid) {
|
|
772
|
-
process.stderr.write("[unerr:child] Parent died — orphan exit.\n");
|
|
773
|
-
clearInterval(orphanTimer);
|
|
774
|
-
clearInterval(statsTimer);
|
|
775
|
-
shutdownProxy("orphan");
|
|
776
|
-
}
|
|
777
|
-
}, 60_000);
|
|
778
|
-
orphanTimer.unref();
|
|
779
|
-
// SIGTERM from parent: graceful shutdown
|
|
780
|
-
process.on("SIGTERM", () => {
|
|
781
|
-
clearInterval(orphanTimer);
|
|
782
|
-
clearInterval(statsTimer);
|
|
783
|
-
shutdownProxy("sigterm");
|
|
784
|
-
});
|
|
785
|
-
// Start the proxy (bypass local wrapper to get shutdown handle + stats)
|
|
786
|
-
const { startProxy: bootProxy } = await import("../proxy/proxy.js");
|
|
787
|
-
const proxyResult = await bootProxy({
|
|
788
|
-
repoId: config.repoId,
|
|
789
|
-
daemonChild: true,
|
|
790
|
-
});
|
|
791
|
-
// Notify parent: ready
|
|
792
|
-
const stateDir = join(cwd, ".unerr", "state");
|
|
793
|
-
const sockPath = join(stateDir, "proxy.sock");
|
|
794
|
-
if (process.send) {
|
|
795
|
-
process.send({ type: "ready", sock: sockPath });
|
|
796
|
-
}
|
|
797
|
-
// Inject update notification into MCP _meta if behind >2 minor versions
|
|
798
|
-
try {
|
|
799
|
-
const { getCachedUpdateInfo } = await import("../daemon/version-checker.js");
|
|
800
|
-
const { setUpdateNotification } = await import("../proxy/response-envelope.js");
|
|
801
|
-
const info = getCachedUpdateInfo();
|
|
802
|
-
if (info.available && !info.dismissed && info.behindMinor > 2) {
|
|
803
|
-
setUpdateNotification(info.latest, info.current);
|
|
804
|
-
}
|
|
805
|
-
}
|
|
806
|
-
catch {
|
|
807
|
-
// Non-critical
|
|
808
|
-
}
|
|
809
|
-
function collectStats() {
|
|
810
|
-
const mem = process.memoryUsage();
|
|
811
|
-
return {
|
|
812
|
-
entities: proxyResult.stats.toolCallsLocal,
|
|
813
|
-
edges: 0,
|
|
814
|
-
memory: Math.round(mem.rss / 1024 / 1024),
|
|
815
|
-
};
|
|
816
|
-
}
|
|
817
|
-
// Periodic stats report to parent
|
|
818
|
-
const statsTimer = setInterval(() => {
|
|
819
|
-
if (!process.send)
|
|
820
|
-
return;
|
|
821
|
-
process.send({ type: "stats", ...collectStats() });
|
|
822
|
-
}, 60_000);
|
|
823
|
-
statsTimer.unref();
|
|
824
|
-
// IPC messages from parent
|
|
825
|
-
process.on("message", (msg) => {
|
|
826
|
-
if (msg.type === "shutdown") {
|
|
827
|
-
clearInterval(orphanTimer);
|
|
828
|
-
clearInterval(statsTimer);
|
|
829
|
-
shutdownProxy("parent-shutdown");
|
|
830
|
-
}
|
|
831
|
-
if (msg.type === "get-stats") {
|
|
832
|
-
if (!process.send)
|
|
833
|
-
return;
|
|
834
|
-
process.send({ type: "stats", ...collectStats() });
|
|
835
|
-
}
|
|
836
|
-
});
|
|
837
|
-
async function shutdownProxy(reason) {
|
|
838
|
-
process.stderr.write(`[unerr:child] Shutting down: ${reason}\n`);
|
|
839
|
-
try {
|
|
840
|
-
await proxyResult.shutdown();
|
|
841
|
-
}
|
|
842
|
-
catch (err) {
|
|
843
|
-
process.stderr.write(`[unerr:child] Shutdown error: ${err.message}\n`);
|
|
844
|
-
}
|
|
845
|
-
process.exit(0);
|
|
846
|
-
}
|
|
847
|
-
}
|
|
848
|
-
/**
|
|
849
|
-
* MCP mode: headless boot for IDE integration.
|
|
850
|
-
*
|
|
851
|
-
* Socket discovery order:
|
|
852
|
-
* 1. Per-repo proxy sock (`<cwd>/.unerr/state/proxy.sock`) — direct bridge
|
|
853
|
-
* 2. unerrd daemon sock (`~/.unerr/unerrd.sock`) — bridge if repo is registered
|
|
854
|
-
*
|
|
855
|
-
* The bridge NEVER spawns unerrd or registers repos. It only connects to
|
|
856
|
-
* what's already running. If nothing is available, it exits with an error.
|
|
857
|
-
*/
|
|
858
|
-
async function mcpBoot(cwd) {
|
|
859
|
-
installFileLogger({
|
|
860
|
-
filePath: join(cwd, ".unerr", "logs", `mcp-${process.pid}.log`),
|
|
861
|
-
maxBytes: 5_000_000,
|
|
862
|
-
keep: 5,
|
|
863
|
-
});
|
|
864
|
-
initFileLog(cwd);
|
|
865
|
-
const detection = await detectProjectRoot(cwd);
|
|
866
|
-
if (!detection.isProject) {
|
|
867
|
-
const antiSignals = detection.signals.filter((s) => s.startsWith("anti-"));
|
|
868
|
-
process.stderr.write(`\x1b[38;2;248;113;113m\u2717\x1b[0m No code project detected in this directory.\n unerr needs a folder containing source code to index.\n Detection score: ${detection.score} (need ${DETECTION_THRESHOLD})\n${antiSignals.length > 0
|
|
869
|
-
? ` Negative signals: ${antiSignals.map((s) => s.replace("anti-", "")).join(", ")}\n`
|
|
870
|
-
: ""}\n \u25b8 Run \x1b[1munerr --mcp\x1b[0m from a project root directory.\n`);
|
|
871
|
-
process.exit(1);
|
|
872
|
-
}
|
|
873
|
-
const { startUdsBridge } = await import("../proxy/bridge.js");
|
|
874
|
-
// ── Step 1: Try per-repo proxy sock (standalone `unerr` running) ──
|
|
875
|
-
const repoSock = join(cwd, ".unerr", "state", "proxy.sock");
|
|
876
|
-
if (existsSync(repoSock)) {
|
|
877
|
-
const { PidLock } = await import("../proxy/pid-lock.js");
|
|
878
|
-
const pidLock = new PidLock(join(cwd, ".unerr", "state"));
|
|
879
|
-
const probeResult = await pidLock.probe();
|
|
880
|
-
if (probeResult.alive) {
|
|
881
|
-
process.stderr.write(`[unerr:mcp] Bridging to running proxy (PID ${probeResult.pid})\n`);
|
|
882
|
-
return bridgeAndExit(startUdsBridge, repoSock);
|
|
883
|
-
}
|
|
884
|
-
}
|
|
885
|
-
// ── Step 2: Try unerrd (must already be running, repo must be registered) ──
|
|
886
|
-
const { daemonSockPath, probeDaemon, ensureRepo, connectRepo, disconnectRepo, sendActivity, } = await import("../daemon/client.js");
|
|
887
|
-
const { findRepo } = await import("../daemon/registry.js");
|
|
888
|
-
const daemonSock = daemonSockPath();
|
|
889
|
-
const daemonRunning = await probeDaemon(daemonSock);
|
|
890
|
-
if (!daemonRunning) {
|
|
891
|
-
process.stderr.write("\x1b[38;2;248;113;113m\u2717\x1b[0m No unerr process found for this project.\n\n" +
|
|
892
|
-
" To use standalone mode:\n" +
|
|
893
|
-
" unerr # start per-repo process in this directory\n\n" +
|
|
894
|
-
" To use daemon mode:\n" +
|
|
895
|
-
" unerr daemon initialize # one-time setup\n" +
|
|
896
|
-
" unerr install <agent> # register this repo\n");
|
|
897
|
-
process.exit(1);
|
|
898
|
-
}
|
|
899
|
-
// Daemon is running — check if this repo is registered
|
|
900
|
-
const repoEntry = findRepo(cwd);
|
|
901
|
-
if (!repoEntry) {
|
|
902
|
-
process.stderr.write("\x1b[38;2;248;113;113m\u2717\x1b[0m Repo not registered with unerrd.\n\n" +
|
|
903
|
-
" Run: unerr install <agent> # registers this repo with the daemon\n" +
|
|
904
|
-
" Or: unerr daemon add . # register without installing agent config\n");
|
|
905
|
-
process.exit(1);
|
|
906
|
-
}
|
|
907
|
-
// Daemon running + repo registered → ensure the per-repo process is up
|
|
908
|
-
let repoSockViaEnsure;
|
|
909
|
-
try {
|
|
910
|
-
repoSockViaEnsure = await ensureRepo(daemonSock, cwd);
|
|
911
|
-
}
|
|
912
|
-
catch (err) {
|
|
913
|
-
process.stderr.write(`\x1b[38;2;248;113;113m\u2717\x1b[0m Failed to ensure repo process: ${err.message}\n`);
|
|
914
|
-
process.exit(1);
|
|
915
|
-
}
|
|
916
|
-
// Register this bridge connection
|
|
917
|
-
try {
|
|
918
|
-
await connectRepo(daemonSock, cwd);
|
|
919
|
-
}
|
|
920
|
-
catch {
|
|
921
|
-
// Non-fatal — idle sweep may stop the repo eventually
|
|
922
|
-
}
|
|
923
|
-
process.stderr.write(`[unerr:mcp] Bridging to repo process via unerrd (sock: ${repoSockViaEnsure})\n`);
|
|
924
|
-
// Throttled activity reporting (1/min)
|
|
925
|
-
const ACTIVITY_THROTTLE_MS = 60_000;
|
|
926
|
-
let lastActivitySent = 0;
|
|
927
|
-
const activityInterval = setInterval(() => {
|
|
928
|
-
const now = Date.now();
|
|
929
|
-
if (now - lastActivitySent >= ACTIVITY_THROTTLE_MS) {
|
|
930
|
-
sendActivity(daemonSock, cwd);
|
|
931
|
-
lastActivitySent = now;
|
|
932
|
-
}
|
|
933
|
-
}, ACTIVITY_THROTTLE_MS);
|
|
934
|
-
activityInterval.unref();
|
|
935
|
-
// Bridge to the per-repo process
|
|
936
|
-
const bridgeResult = await startUdsBridge(repoSockViaEnsure);
|
|
937
|
-
// Cleanup: disconnect from daemon, stop activity reporting
|
|
938
|
-
clearInterval(activityInterval);
|
|
939
|
-
try {
|
|
940
|
-
await disconnectRepo(daemonSock, cwd);
|
|
941
|
-
}
|
|
942
|
-
catch {
|
|
943
|
-
// Best-effort — daemon may already be gone
|
|
944
|
-
}
|
|
945
|
-
if (bridgeResult.reason === "daemon_dead") {
|
|
946
|
-
process.stderr.write("\x1b[38;2;248;113;113m\u2717\x1b[0m unerr proxy disconnected mid-session.\n The repo process may have been idle-stopped or crashed.\n");
|
|
947
|
-
process.exit(1);
|
|
948
|
-
}
|
|
949
|
-
}
|
|
950
|
-
/** Bridge to a UDS sock and handle disconnect/error. */
|
|
951
|
-
async function bridgeAndExit(startBridge, sockPath) {
|
|
952
|
-
const result = await startBridge(sockPath);
|
|
953
|
-
if (result.reason === "daemon_dead") {
|
|
954
|
-
process.stderr.write("\x1b[38;2;248;113;113m\u2717\x1b[0m unerr proxy disconnected mid-session.\n");
|
|
955
|
-
process.exit(1);
|
|
956
|
-
}
|
|
957
|
-
}
|
|
958
|
-
// ── Commander Setup ─────────────────────────────────────────
|
|
959
|
-
const program = new Command();
|
|
960
|
-
program
|
|
961
|
-
.name("unerr")
|
|
962
|
-
.description("Code intelligence for AI agents")
|
|
963
|
-
.version("0.1.0")
|
|
964
|
-
.option("--ide <type>", "IDE type: cursor, vscode, claude-code, windsurf")
|
|
965
|
-
.option("--mcp", "Start in MCP server mode (stdio, no interactive prompts)")
|
|
966
|
-
.option("--daemon-child", "Run as a daemon-managed child process (internal, set by unerrd)")
|
|
967
|
-
.showHelpAfterError("(use --help for available commands)")
|
|
968
|
-
.action(async (opts) => {
|
|
969
|
-
const cwd = process.cwd();
|
|
970
|
-
// --daemon-child: managed child mode (spawned by unerrd)
|
|
971
|
-
if (opts.daemonChild) {
|
|
972
|
-
await daemonChildBoot(cwd);
|
|
973
|
-
return;
|
|
974
|
-
}
|
|
975
|
-
// --mcp: headless MCP server mode for IDE integration
|
|
976
|
-
if (opts.mcp) {
|
|
977
|
-
await mcpBoot(cwd);
|
|
978
|
-
return;
|
|
979
|
-
}
|
|
980
|
-
const config = readLocalConfig(cwd);
|
|
981
|
-
if (config) {
|
|
982
|
-
await resumeBoot(config);
|
|
983
|
-
}
|
|
984
|
-
else {
|
|
985
|
-
await firstRunBoot();
|
|
986
|
-
}
|
|
987
|
-
});
|
|
988
|
-
// ── Visible Commands (shown in --help) ──────────────────────
|
|
989
|
-
program
|
|
990
|
-
.command("chat")
|
|
991
|
-
.description("Interactive AI assistant (coming soon)")
|
|
992
|
-
.option("--model <model>", "Claude model to use")
|
|
993
|
-
.option("--no-graph", "Skip loading the code intelligence graph")
|
|
994
|
-
.action(async () => {
|
|
995
|
-
process.stderr.write("\n unerr chat is temporarily disabled.\n Use unerr as an MCP proxy with your preferred AI agent instead.\n\n");
|
|
996
|
-
process.exit(0);
|
|
997
|
-
});
|
|
998
|
-
registerStatusCommand(program);
|
|
999
|
-
registerStatsCommand(program);
|
|
1000
|
-
registerInstallCommand(program);
|
|
1001
|
-
registerDashboardCommand(program);
|
|
1002
|
-
registerDebugCommand(program);
|
|
1003
|
-
registerGainCommand(program);
|
|
1004
|
-
registerDiscoverCommand(program);
|
|
1005
|
-
registerDaemonCommand(program);
|
|
1006
|
-
// ── Hidden Commands (callable but not shown in --help) ──────
|
|
1007
|
-
const hiddenCommands = [
|
|
1008
|
-
registerBranchesCommand,
|
|
1009
|
-
registerCheckCommitCommand,
|
|
1010
|
-
registerCompressOutputCommand,
|
|
1011
|
-
registerConfigVerifyCommand,
|
|
1012
|
-
registerEnrichCommand,
|
|
1013
|
-
registerExecCommand,
|
|
1014
|
-
registerHookCommand,
|
|
1015
|
-
registerIndexCommand,
|
|
1016
|
-
registerInitCommand,
|
|
1017
|
-
registerLearnCommand,
|
|
1018
|
-
registerManifestCommand,
|
|
1019
|
-
registerRewindCommand,
|
|
1020
|
-
registerSkillsCommand,
|
|
1021
|
-
registerTimelineCommand,
|
|
1022
|
-
registerUninstallCommand,
|
|
1023
|
-
];
|
|
1024
|
-
for (const register of hiddenCommands) {
|
|
1025
|
-
register(program);
|
|
1026
|
-
}
|
|
1027
|
-
// Hide all commands except chat, status, debug from --help output
|
|
1028
|
-
const visibleCommands = new Set([
|
|
1029
|
-
"status",
|
|
1030
|
-
"stats",
|
|
1031
|
-
"install",
|
|
1032
|
-
"dashboard",
|
|
1033
|
-
"debug",
|
|
1034
|
-
"init",
|
|
1035
|
-
"daemon",
|
|
1036
|
-
]);
|
|
1037
|
-
for (const cmd of program.commands) {
|
|
1038
|
-
if (!visibleCommands.has(cmd.name())) {
|
|
1039
|
-
// Commander internal property — commands with _hidden=true are omitted from help
|
|
1040
|
-
cmd._hidden = true;
|
|
1041
|
-
}
|
|
1042
|
-
}
|
|
1043
|
-
program.parse();
|