@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.
Files changed (363) hide show
  1. package/README.md +36 -45
  2. package/dist/cli.js +37443 -36022
  3. package/package.json +2 -1
  4. package/dist/behaviors/agent-llm-bridge.js +0 -166
  5. package/dist/behaviors/architecture-guard.js +0 -256
  6. package/dist/behaviors/auto-doc.js +0 -247
  7. package/dist/behaviors/cascade-guard.js +0 -289
  8. package/dist/behaviors/change-narrative.js +0 -270
  9. package/dist/behaviors/convention-drift.js +0 -290
  10. package/dist/behaviors/framework.js +0 -235
  11. package/dist/behaviors/guard-formatter.js +0 -44
  12. package/dist/behaviors/incomplete-work.js +0 -270
  13. package/dist/behaviors/loop-breaker.js +0 -300
  14. package/dist/behaviors/session-continuity.js +0 -208
  15. package/dist/commands/branches.js +0 -97
  16. package/dist/commands/check-commit.js +0 -225
  17. package/dist/commands/compress-output.js +0 -64
  18. package/dist/commands/config-verify.js +0 -243
  19. package/dist/commands/daemon.js +0 -905
  20. package/dist/commands/dashboard.js +0 -52
  21. package/dist/commands/debug.js +0 -200
  22. package/dist/commands/enrich.js +0 -184
  23. package/dist/commands/exec.js +0 -233
  24. package/dist/commands/gain.js +0 -156
  25. package/dist/commands/hook.js +0 -88
  26. package/dist/commands/index.js +0 -88
  27. package/dist/commands/init.js +0 -74
  28. package/dist/commands/install.js +0 -505
  29. package/dist/commands/learn.js +0 -116
  30. package/dist/commands/manifest.js +0 -193
  31. package/dist/commands/rewind.js +0 -103
  32. package/dist/commands/serve.js +0 -19
  33. package/dist/commands/setup-wizard.js +0 -414
  34. package/dist/commands/skills.js +0 -64
  35. package/dist/commands/stats.js +0 -20
  36. package/dist/commands/status.js +0 -654
  37. package/dist/commands/timeline.js +0 -139
  38. package/dist/commands/uninstall.js +0 -230
  39. package/dist/components/App.js +0 -109
  40. package/dist/components/Banner.js +0 -12
  41. package/dist/components/ConfirmPrompt.js +0 -25
  42. package/dist/components/DriftSummary.js +0 -23
  43. package/dist/components/GradeBadge.js +0 -15
  44. package/dist/components/HealthCard.js +0 -18
  45. package/dist/components/InkSpinner.js +0 -22
  46. package/dist/components/InputBox.js +0 -17
  47. package/dist/components/KeyValue.js +0 -13
  48. package/dist/components/MessageList.js +0 -14
  49. package/dist/components/ProgressBar.js +0 -26
  50. package/dist/components/Section.js +0 -16
  51. package/dist/components/SessionSummaryCard.js +0 -73
  52. package/dist/components/StartupDisplay.js +0 -24
  53. package/dist/components/StatusDashboard.js +0 -57
  54. package/dist/components/StatusLine.js +0 -8
  55. package/dist/components/StepLine.js +0 -22
  56. package/dist/components/Theme.js +0 -20
  57. package/dist/components/ToolProgress.js +0 -8
  58. package/dist/components/ViolationList.js +0 -21
  59. package/dist/components/render.js +0 -13
  60. package/dist/config/agent-registry.js +0 -237
  61. package/dist/config/claude-settings-hooks.js +0 -304
  62. package/dist/config/hook-installer.js +0 -65
  63. package/dist/config/instruction-writer.js +0 -388
  64. package/dist/config/mcp-config-writer.js +0 -266
  65. package/dist/config/settings.js +0 -174
  66. package/dist/config/tool-detector.js +0 -42
  67. package/dist/config/value-surfacing.js +0 -119
  68. package/dist/core/context-assembly.js +0 -108
  69. package/dist/core/conversation.js +0 -33
  70. package/dist/core/local-chat-provider.js +0 -475
  71. package/dist/core/provider-factory.js +0 -55
  72. package/dist/core/providers.js +0 -90
  73. package/dist/core/query-engine.js +0 -174
  74. package/dist/daemon/api.js +0 -312
  75. package/dist/daemon/autostart.js +0 -119
  76. package/dist/daemon/bootstrap.js +0 -39
  77. package/dist/daemon/client.js +0 -164
  78. package/dist/daemon/detect-ci.js +0 -81
  79. package/dist/daemon/platform-linux.js +0 -146
  80. package/dist/daemon/platform-macos.js +0 -134
  81. package/dist/daemon/platform-windows.js +0 -116
  82. package/dist/daemon/process-manager.js +0 -299
  83. package/dist/daemon/protocol.js +0 -23
  84. package/dist/daemon/registry.js +0 -270
  85. package/dist/daemon/settings-schema.js +0 -72
  86. package/dist/daemon/system-health.js +0 -134
  87. package/dist/daemon/version-checker.js +0 -262
  88. package/dist/daemon/warm-start.js +0 -223
  89. package/dist/entrypoints/cli.js +0 -1043
  90. package/dist/entrypoints/daemon.js +0 -380
  91. package/dist/entrypoints/repl.js +0 -147
  92. package/dist/hooks/adapters/claude-code.js +0 -90
  93. package/dist/hooks/adapters/cline.js +0 -100
  94. package/dist/hooks/adapters/cursor.js +0 -98
  95. package/dist/hooks/hook-dedup.js +0 -79
  96. package/dist/hooks/hook-runner.js +0 -113
  97. package/dist/hooks/navigation-hooks.js +0 -175
  98. package/dist/hooks/prompt-hooks.js +0 -63
  99. package/dist/hooks/shell-hooks.js +0 -47
  100. package/dist/ignore.js +0 -111
  101. package/dist/intelligence/approach-suggester.js +0 -61
  102. package/dist/intelligence/ast-extractor.js +0 -2615
  103. package/dist/intelligence/ast-worker.js +0 -34
  104. package/dist/intelligence/background-indexer.js +0 -121
  105. package/dist/intelligence/blast-radius.js +0 -200
  106. package/dist/intelligence/community-detection.js +0 -691
  107. package/dist/intelligence/community-detector.js +0 -184
  108. package/dist/intelligence/computation-scheduler.js +0 -75
  109. package/dist/intelligence/confidence-propagation.js +0 -47
  110. package/dist/intelligence/convention-detector.js +0 -242
  111. package/dist/intelligence/convention-learner.js +0 -205
  112. package/dist/intelligence/convention-matcher.js +0 -205
  113. package/dist/intelligence/cozo-schema.js +0 -376
  114. package/dist/intelligence/decision-point-detector.js +0 -90
  115. package/dist/intelligence/deep-dive-tools.js +0 -586
  116. package/dist/intelligence/durability-scorer.js +0 -84
  117. package/dist/intelligence/exploration-cost.js +0 -204
  118. package/dist/intelligence/exploration-pattern-tracker.js +0 -61
  119. package/dist/intelligence/fact-generator.js +0 -322
  120. package/dist/intelligence/facts-schema.js +0 -90
  121. package/dist/intelligence/file-intelligence.js +0 -59
  122. package/dist/intelligence/graph-holder.js +0 -220
  123. package/dist/intelligence/graph-temporal-joiner.js +0 -238
  124. package/dist/intelligence/health-grade.js +0 -423
  125. package/dist/intelligence/health-grader.js +0 -200
  126. package/dist/intelligence/health-map-data.js +0 -259
  127. package/dist/intelligence/import-symbols.js +0 -136
  128. package/dist/intelligence/incremental-indexer.js +0 -658
  129. package/dist/intelligence/indexer/centrality.js +0 -62
  130. package/dist/intelligence/indexer/cfg-context.js +0 -95
  131. package/dist/intelligence/indexer/confidence.js +0 -34
  132. package/dist/intelligence/indexer/cross-file-resolver.js +0 -104
  133. package/dist/intelligence/indexer/edge-repair.js +0 -89
  134. package/dist/intelligence/indexer/entity-key.js +0 -17
  135. package/dist/intelligence/indexer/export-map.js +0 -132
  136. package/dist/intelligence/indexer/git-cochange.js +0 -128
  137. package/dist/intelligence/indexer/graph-patch.js +0 -147
  138. package/dist/intelligence/indexer/incremental.js +0 -78
  139. package/dist/intelligence/indexer/ingest.js +0 -160
  140. package/dist/intelligence/indexer/language-detect.js +0 -226
  141. package/dist/intelligence/indexer/metadata.js +0 -63
  142. package/dist/intelligence/indexer/mutation-tracker.js +0 -79
  143. package/dist/intelligence/indexer/orchestrator.js +0 -155
  144. package/dist/intelligence/indexer/plugin-interface.js +0 -31
  145. package/dist/intelligence/indexer/plugins/csharp.js +0 -440
  146. package/dist/intelligence/indexer/plugins/go.js +0 -335
  147. package/dist/intelligence/indexer/plugins/java.js +0 -370
  148. package/dist/intelligence/indexer/plugins/python.js +0 -358
  149. package/dist/intelligence/indexer/plugins/regex-fallback.js +0 -82
  150. package/dist/intelligence/indexer/plugins/ruby.js +0 -290
  151. package/dist/intelligence/indexer/plugins/rust.js +0 -484
  152. package/dist/intelligence/indexer/plugins/tier2-generic.js +0 -310
  153. package/dist/intelligence/indexer/plugins/typescript.js +0 -456
  154. package/dist/intelligence/indexer/resource-monitor.js +0 -93
  155. package/dist/intelligence/indexer/scip/decoder.js +0 -253
  156. package/dist/intelligence/indexer/scip/detector.js +0 -232
  157. package/dist/intelligence/indexer/scip/downloader.js +0 -427
  158. package/dist/intelligence/indexer/scip/fallback.js +0 -34
  159. package/dist/intelligence/indexer/scip/merger.js +0 -109
  160. package/dist/intelligence/indexer/scip/orchestrator.js +0 -433
  161. package/dist/intelligence/indexer/scip/runner.js +0 -98
  162. package/dist/intelligence/indexer/snapshot.js +0 -66
  163. package/dist/intelligence/indexer/test-detector.js +0 -196
  164. package/dist/intelligence/indexer/watch-integration.js +0 -61
  165. package/dist/intelligence/indexer/worker.js +0 -85
  166. package/dist/intelligence/local-convention-detector.js +0 -437
  167. package/dist/intelligence/local-embeddings.js +0 -190
  168. package/dist/intelligence/local-graph.js +0 -1946
  169. package/dist/intelligence/local-indexer.js +0 -1575
  170. package/dist/intelligence/local-llm.js +0 -163
  171. package/dist/intelligence/local-rule-generator.js +0 -154
  172. package/dist/intelligence/local-snapshot.js +0 -213
  173. package/dist/intelligence/negative-knowledge.js +0 -103
  174. package/dist/intelligence/persistent-db.js +0 -85
  175. package/dist/intelligence/query-router.js +0 -2556
  176. package/dist/intelligence/risk-classifier.js +0 -116
  177. package/dist/intelligence/rule-evaluator.js +0 -380
  178. package/dist/intelligence/rule-generator.js +0 -49
  179. package/dist/intelligence/search-index.js +0 -173
  180. package/dist/intelligence/semantic/docstring-extractor.js +0 -67
  181. package/dist/intelligence/semantic/embedding-store.js +0 -52
  182. package/dist/intelligence/semantic/enrichment-orchestrator.js +0 -48
  183. package/dist/intelligence/semantic/git-message-miner.js +0 -114
  184. package/dist/intelligence/semantic/identifier-tokenizer.js +0 -51
  185. package/dist/intelligence/semantic/node2vec-embeddings.js +0 -71
  186. package/dist/intelligence/semantic/node2vec-walks.js +0 -103
  187. package/dist/intelligence/semantic/path-domain-inference.js +0 -112
  188. package/dist/intelligence/semantic/similarity-engine.js +0 -60
  189. package/dist/intelligence/semantic/tfidf-vectors.js +0 -88
  190. package/dist/intelligence/session-brief-builder.js +0 -159
  191. package/dist/intelligence/session-context.js +0 -221
  192. package/dist/intelligence/session-health-monitor.js +0 -211
  193. package/dist/intelligence/session-narrative.js +0 -197
  194. package/dist/intelligence/session-pattern-analyzer.js +0 -218
  195. package/dist/intelligence/signal-scorer.js +0 -390
  196. package/dist/intelligence/signal-show-store.js +0 -182
  197. package/dist/intelligence/smart-truncate.js +0 -158
  198. package/dist/intelligence/subgraph-cache.js +0 -88
  199. package/dist/intelligence/temporal-facts.js +0 -494
  200. package/dist/intelligence/token-estimator.js +0 -100
  201. package/dist/intelligence/tool-injector.js +0 -87
  202. package/dist/intelligence/tree-sitter-loader.js +0 -71
  203. package/dist/intelligence/worker-pool.js +0 -116
  204. package/dist/proxy/arg-validator.js +0 -79
  205. package/dist/proxy/auto-bootstrap.js +0 -167
  206. package/dist/proxy/bridge.js +0 -147
  207. package/dist/proxy/budget-enforcer.js +0 -70
  208. package/dist/proxy/compression-quality-monitor.js +0 -160
  209. package/dist/proxy/compression-stats.js +0 -51
  210. package/dist/proxy/context-rot-detector.js +0 -137
  211. package/dist/proxy/drift-detector.js +0 -139
  212. package/dist/proxy/efficiency-tracker.js +0 -79
  213. package/dist/proxy/fact-ranking.js +0 -154
  214. package/dist/proxy/format-encoder.js +0 -266
  215. package/dist/proxy/http-transport.js +0 -90
  216. package/dist/proxy/lifecycle-actor.js +0 -55
  217. package/dist/proxy/lifecycle-machine.js +0 -187
  218. package/dist/proxy/log-tailer.js +0 -265
  219. package/dist/proxy/model-pricing.js +0 -98
  220. package/dist/proxy/network-firewall.js +0 -141
  221. package/dist/proxy/nudge-state.js +0 -93
  222. package/dist/proxy/output-compressor.js +0 -185
  223. package/dist/proxy/pid-lock.js +0 -291
  224. package/dist/proxy/proxy-context.js +0 -11
  225. package/dist/proxy/proxy.js +0 -2633
  226. package/dist/proxy/response-enrichment.js +0 -32
  227. package/dist/proxy/response-envelope.js +0 -313
  228. package/dist/proxy/session-dedup.js +0 -82
  229. package/dist/proxy/session-legend.js +0 -30
  230. package/dist/proxy/session-persistence.js +0 -210
  231. package/dist/proxy/session-resume.js +0 -94
  232. package/dist/proxy/session-stats.js +0 -513
  233. package/dist/proxy/shell-classifier.js +0 -1346
  234. package/dist/proxy/shell-compression-log.js +0 -93
  235. package/dist/proxy/shell-compressor.js +0 -390
  236. package/dist/proxy/shell-graph-boost.js +0 -202
  237. package/dist/proxy/shell-monitor-map.js +0 -18
  238. package/dist/proxy/shell-stats.js +0 -54
  239. package/dist/proxy/shell-strategies/cloud.js +0 -215
  240. package/dist/proxy/shell-strategies/diff.js +0 -159
  241. package/dist/proxy/shell-strategies/error-diagnostic.js +0 -796
  242. package/dist/proxy/shell-strategies/filter-dsl.js +0 -358
  243. package/dist/proxy/shell-strategies/git-status.js +0 -177
  244. package/dist/proxy/shell-strategies/key-value.js +0 -193
  245. package/dist/proxy/shell-strategies/log-text.js +0 -154
  246. package/dist/proxy/shell-strategies/omni.js +0 -188
  247. package/dist/proxy/shell-strategies/progress.js +0 -55
  248. package/dist/proxy/shell-strategies/redact.js +0 -76
  249. package/dist/proxy/shell-strategies/structured.js +0 -241
  250. package/dist/proxy/shell-strategies/tabular.js +0 -243
  251. package/dist/proxy/shell-strategies/test-results-types.js +0 -13
  252. package/dist/proxy/shell-strategies/test-results.js +0 -784
  253. package/dist/proxy/shell-strategies/tree-paths.js +0 -144
  254. package/dist/proxy/shell-strategies/yaml.js +0 -182
  255. package/dist/proxy/shell-tee.js +0 -111
  256. package/dist/proxy/signal-dedup.js +0 -171
  257. package/dist/proxy/startup-renderer.js +0 -158
  258. package/dist/proxy/task-token-display.js +0 -38
  259. package/dist/proxy/token-counter.js +0 -61
  260. package/dist/proxy/tool-clusters.js +0 -273
  261. package/dist/proxy/tool-definitions.js +0 -525
  262. package/dist/proxy/transport-mux.js +0 -229
  263. package/dist/proxy/wire-cap.js +0 -268
  264. package/dist/rules/developer.mozilla.org.json +0 -9
  265. package/dist/rules/github.com.json +0 -21
  266. package/dist/schemas/api/skills.js +0 -19
  267. package/dist/schemas/common/errors.js +0 -7
  268. package/dist/schemas/common/headers.js +0 -5
  269. package/dist/schemas/entities/edge.js +0 -25
  270. package/dist/schemas/entities/entity.js +0 -22
  271. package/dist/schemas/entities/rule.js +0 -18
  272. package/dist/schemas/index.js +0 -14
  273. package/dist/server/event-bus.js +0 -59
  274. package/dist/server/http.js +0 -156
  275. package/dist/server/middleware.js +0 -70
  276. package/dist/server/routes/drift.js +0 -97
  277. package/dist/server/routes/intelligence.js +0 -1217
  278. package/dist/server/routes/reasoning-quality.js +0 -444
  279. package/dist/server/routes/session.js +0 -86
  280. package/dist/server/routes/stream.js +0 -120
  281. package/dist/server/routes/system.js +0 -73
  282. package/dist/server/routes/temporal.js +0 -170
  283. package/dist/server/routes/timeline.js +0 -232
  284. package/dist/server/routes/token-flow.js +0 -403
  285. package/dist/skills/effectiveness-tracker.js +0 -93
  286. package/dist/skills/local-pack.js +0 -380
  287. package/dist/skills/resolver.js +0 -495
  288. package/dist/state-detector.js +0 -83
  289. package/dist/timeline/intent-detector.js +0 -263
  290. package/dist/timeline/loop-miner.js +0 -140
  291. package/dist/timeline/open-threads.js +0 -49
  292. package/dist/timeline/signal-reinforcer.js +0 -62
  293. package/dist/timeline/timeline-bootstrap.js +0 -151
  294. package/dist/timeline/timeline-store.js +0 -618
  295. package/dist/tools/coding/bash.js +0 -49
  296. package/dist/tools/coding/file-edit.js +0 -72
  297. package/dist/tools/coding/file-outline.js +0 -227
  298. package/dist/tools/coding/file-read-protocol.js +0 -425
  299. package/dist/tools/coding/file-read.js +0 -35
  300. package/dist/tools/coding/file-write.js +0 -43
  301. package/dist/tools/coding/glob-tool.js +0 -109
  302. package/dist/tools/coding/grep.js +0 -162
  303. package/dist/tools/coding/index.js +0 -27
  304. package/dist/tools/intelligence/index.js +0 -269
  305. package/dist/tools/intelligence/record-fact.js +0 -48
  306. package/dist/tools/intelligence/timeline-markers.js +0 -130
  307. package/dist/tools/registry.js +0 -47
  308. package/dist/tools/types.js +0 -8
  309. package/dist/tracking/auto-snapshot-triggers.js +0 -246
  310. package/dist/tracking/branch-context.js +0 -115
  311. package/dist/tracking/branch-snapshot.js +0 -217
  312. package/dist/tracking/causal-bridge.js +0 -317
  313. package/dist/tracking/circuit-breaker.js +0 -147
  314. package/dist/tracking/commit-watcher.js +0 -114
  315. package/dist/tracking/context-ledger.js +0 -119
  316. package/dist/tracking/correction-detector.js +0 -324
  317. package/dist/tracking/drift-tracker.js +0 -874
  318. package/dist/tracking/durability-tracker.js +0 -94
  319. package/dist/tracking/entity-rewind.js +0 -200
  320. package/dist/tracking/file-hash-state.js +0 -114
  321. package/dist/tracking/git-attribution.js +0 -132
  322. package/dist/tracking/git-trailers.js +0 -171
  323. package/dist/tracking/intelligence-counter.js +0 -46
  324. package/dist/tracking/intent-correlator.js +0 -202
  325. package/dist/tracking/intent-encoder.js +0 -52
  326. package/dist/tracking/intent-token-tracker.js +0 -159
  327. package/dist/tracking/ledger-archiver.js +0 -94
  328. package/dist/tracking/ledger-chains.js +0 -245
  329. package/dist/tracking/metrics-store.js +0 -361
  330. package/dist/tracking/native-watcher.js +0 -131
  331. package/dist/tracking/offline-rewind.js +0 -295
  332. package/dist/tracking/pending-violations.js +0 -74
  333. package/dist/tracking/persistence-effectiveness.js +0 -167
  334. package/dist/tracking/prompt-durability.js +0 -202
  335. package/dist/tracking/quality-signals.js +0 -213
  336. package/dist/tracking/redactor.js +0 -73
  337. package/dist/tracking/rewind-engine.js +0 -161
  338. package/dist/tracking/session-history.js +0 -128
  339. package/dist/tracking/session-receipt.js +0 -88
  340. package/dist/tracking/session-summary-writer.js +0 -157
  341. package/dist/tracking/shadow-ledger.js +0 -321
  342. package/dist/tracking/stash-manager.js +0 -258
  343. package/dist/tracking/timeline-fork.js +0 -213
  344. package/dist/tracking/timeline.js +0 -69
  345. package/dist/tracking/token-flow.js +0 -276
  346. package/dist/tracking/turn-segmenter.js +0 -122
  347. package/dist/tracking/weekly-accumulator.js +0 -179
  348. package/dist/tracking/working-snapshots.js +0 -188
  349. package/dist/tracking/workspace-manifest.js +0 -176
  350. package/dist/transport/http.js +0 -102
  351. package/dist/utils/counterfactual.js +0 -65
  352. package/dist/utils/deep-link.js +0 -34
  353. package/dist/utils/detect.js +0 -193
  354. package/dist/utils/exec.js +0 -73
  355. package/dist/utils/file-logger.js +0 -87
  356. package/dist/utils/format-error.js +0 -29
  357. package/dist/utils/git.js +0 -181
  358. package/dist/utils/log.js +0 -57
  359. package/dist/utils/logger.js +0 -35
  360. package/dist/utils/mcp-content-json.js +0 -8
  361. package/dist/utils/session-logger.js +0 -154
  362. package/dist/utils/startup-log.js +0 -512
  363. package/dist/utils/ui.js +0 -56
@@ -1,388 +0,0 @@
1
- /**
2
- * Instruction Writer — injects tool-preference instructions into agent instruction files.
3
- *
4
- * Registry-driven: uses agent-registry.ts to determine instruction file path and format.
5
- * Idempotent — uses sentinel markers to create/update/skip sections without clobbering user content.
6
- *
7
- * Supported formats:
8
- * - markdown: CLAUDE.md, AGENTS.md, GEMINI.md, copilot-instructions.md, .clinerules
9
- * Uses <!-- unerr:start --> / <!-- unerr:end --> sentinel markers
10
- * - mdc: .cursor/rules/*.mdc files (standalone file, overwrite entire file)
11
- */
12
- import { existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync, } from "node:fs";
13
- import { dirname, join } from "node:path";
14
- import { getAgent } from "./agent-registry.js";
15
- const SENTINEL_START = "<!-- unerr:start -->";
16
- const SENTINEL_END = "<!-- unerr:end -->";
17
- /**
18
- * The tool-preference instructions injected into agent instruction files.
19
- * Concise: tells the agent WHEN and WHY to use unerr tools over built-ins.
20
- */
21
- function getInstructionContent(ide) {
22
- const isClaudeCode = ide === "claude-code";
23
- // Claude Code requires built-in Read before Edit (readFileState constraint).
24
- // Other agents can use file_read directly before editing.
25
- const readForEditRow = isClaudeCode
26
- ? `| Understand a file before editing | \`file_read\` with \`purpose:'explore'\` to understand, then built-in \`Read\` (offset/limit) on target lines before Edit | Reading entire file |`
27
- : "| Read a file before editing | `file_read` with `entity` param or offset/limit for targeted access | Reading entire file |";
28
- const twoStepSection = isClaudeCode
29
- ? `
30
- ### IMPORTANT: Two-step Read Routing (Claude Code specific)
31
-
32
- **Why this matters:** Claude Code's Edit tool requires built-in \`Read\` to have been called on the file first. \`file_read\` (unerr MCP) does NOT satisfy this because it's a separate MCP tool. Meanwhile, built-in Read misses project conventions and facts that \`file_read\` auto-injects.
33
-
34
- **The rule — two paths, choose by intent:**
35
-
36
- | Intent | Tool | Why |
37
- |--------|------|-----|
38
- | Reading to understand code | \`file_read\` (unerr MCP) | Auto-injects conventions, facts, drift status |
39
- | Reading immediately before Edit | Built-in \`Read\` with offset/limit | Required by Edit tool — \`file_read\` does NOT satisfy this. Use targeted reads (offset/limit) for only the lines you plan to edit. |
40
-
41
- When your next action is Edit, use built-in Read with offset/limit on the target lines. For everything else, use \`file_read\`.
42
-
43
- **Common failure mode:** Using \`file_read\` to understand a file, then attempting Edit without calling built-in Read first. The Edit tool WILL reject with "File has not been read yet". Always call built-in Read (with offset/limit) immediately before Edit.`
44
- : "";
45
- const summaryEditNote = isClaudeCode
46
- ? "\nNEVER use built-in Read/Grep/Glob for code navigation. EXCEPTION: built-in Read (with offset/limit) is REQUIRED immediately before Edit (file_read cannot substitute — Edit will fail without it)."
47
- : "\nNEVER use built-in Read/Grep/Glob for code navigation — use unerr MCP tools instead.";
48
- return `## REQUIRED: Use unerr Graph Intelligence Tools (22 MCP tools)
49
-
50
- This project has unerr MCP tools installed. You MUST use these instead of built-in Read/Grep/Glob for code navigation. unerr tools are graph-backed, return results in <5ms, and include project context that built-in tools miss.
51
-
52
- ### Tool Routing (MANDATORY — match your goal before calling any tool)
53
-
54
- | If you need to... | You MUST call | DO NOT use |
55
- |---|---|---|
56
- | Find a function, class, or type | \`search_code\` | Grep, Glob |
57
- | Find callers or callees | \`get_references\` (direction: callers/callees) | Grep for function name |
58
- | Read a file for understanding | \`file_read\` with \`purpose:'explore'\` (default, auto-injects conventions/facts) | Built-in Read/Grep/Glob |
59
- ${readForEditRow}
60
- | Get file structure overview | \`file_outline\` | Reading the whole file |
61
- | Get a specific function or class | \`get_entity\` or \`file_read\` with \`entity\` param | Reading entire file |
62
- | Trace imports/dependencies | \`get_imports\` or \`get_references\` (direction: callees) | Manual import scanning |
63
- | Find hotspots / high fan-in / blast-radius candidates | \`get_critical_nodes\` | \`get_entity\` (won't show ranked list), guessing |
64
- | Run a shell command | Automatic — routed through shell intelligence | N/A |
65
-
66
- ### FORBIDDEN Patterns (these waste tokens and miss context)
67
-
68
- - Reading an entire file to find one function -> use \`get_entity\` or \`file_read\` with \`entity\` param
69
- - Grep for a function name to find callers -> use \`get_references\` (finds indirect refs too)
70
- - Glob + Grep to search for code -> use \`search_code\` (indexes ALL entities, <5ms)
71
- - Reading multiple files to understand conventions -> use \`get_conventions\`
72
- - Guessing code style for new code -> use \`get_conventions\`
73
- - Guessing which entity has the highest fan-in / is the biggest hotspot -> use \`get_critical_nodes\`
74
- - Reading a full file when you only need a section -> use \`file_read\` with \`entity\` param or offset/limit${twoStepSection}
75
-
76
- ### Tool Reference
77
-
78
- #### Graph Navigation (6 tools)
79
-
80
- | Task | Tool | Replaces |
81
- |------|------|----------|
82
- | Find callers or callees | \`get_references\` (direction: callers/callees) | Grep for function name / manual import tracing |
83
- | Search code entities | \`search_code\` | Glob + Grep across files |
84
- | Get entity details | \`get_entity\` | Reading full file for one function/class |
85
- | Get file summary | \`get_file\` | Reading entire file top-to-bottom |
86
- | Trace imports | \`get_imports\` | Scanning import statements |
87
- | Detect conventions | \`get_conventions\` | Guessing code style |
88
-
89
- #### Structural Analysis (5 tools)
90
-
91
- | Task | Tool | Replaces |
92
- |------|------|----------|
93
- | Find chokepoint entities | \`get_critical_nodes\` | Manually tracing callers across files |
94
- | Find cross-module coupling | \`get_cross_boundary_links\` | Manually tracing imports across directories |
95
- | Project overview stats | \`get_project_stats\` | Counting files / reading multiple files |
96
- | File dependency neighborhood | \`file_connections\` | Scanning import statements across codebase |
97
- | Find tests for an entity | \`get_test_coverage\` | Grepping for function names in test files |
98
-
99
- #### File Protocol (2 tools)
100
-
101
- | Task | Tool | Replaces |
102
- |------|------|----------|
103
- | File structure overview | \`file_outline\` | Reading entire large files |
104
- | Read file with context | \`file_read\` | Built-in Read (misses conventions) |
105
-
106
- \`file_read\` auto-injects relevant facts and conventions. For files >50 lines, call \`file_outline\` first, then \`file_read\` with \`entity\` param for targeted access.
107
-
108
- **\`purpose\` parameter:** Controls read behavior — set it to match your intent:
109
- - \`purpose:'explore'\` (default) — budget-capped, returns outline for large files. Use for browsing and pre-edit understanding.
110
- - \`purpose:'reference'\` — tight budget, entity/offset reads only. Use for quick lookups.
111
-
112
- #### Shell Compression (automatic)
113
-
114
- All shell commands automatically route through unerr's compression layer. It strips ANSI codes, classifies output (diffs, test results, logs, errors), and returns compressed summaries — saving tokens without losing critical information.
115
-
116
- #### Response Signal Prefix \`ur|<tag>\`
117
-
118
- unerr tool responses may begin with one or more \`ur|<tag> <message>\` lines BEFORE the actual content. These are anti-drift signals injected directly in the body (because MCP \`_meta\` is filtered by clients before reaching you). Treat them as high-priority instructions and act on them before consuming the rest of the response.
119
-
120
- The bare \`ur|\` prefix is deliberately short — it tokenizes to 1-2 BPE tokens, and the 3-char tag is another token. Total signal overhead: ~2-3 tokens.
121
-
122
- | Tag | Meaning | What to do |
123
- |---|---|---|
124
- | \`hlt\` | halt — loop / circuit-break detected | Stop retrying this entity; switch approach |
125
- | \`dft\` | drift — file or entity changed since last seen | Re-read with \`file_read\`/\`get_entity\` before editing |
126
- | \`rsk\` | risk — high blast radius (many callers/callees) | Check callers via \`get_references\` before editing |
127
- | \`wrn\` | warn — anti-pattern / negative fact | Avoid the listed failure mode |
128
- | \`hnt\` | hint — guidance / co-change suggestion | Consider co-modifying the listed files |
129
- | \`fct\` | fact — surfaced project fact (subtype in [brackets]: procedural, convention, semantic) | Use as session/project context |
130
- | \`ctx\` | context already delivered for this entity | Do not re-query; use what was already returned |
131
- | \`hth\` | health — session degraded | Consider starting a new session |
132
- | \`hst\` | hist — prior failures on this entity | Read failure modes carefully before retrying |
133
- | (no tag) | \`ur| <msg>\` generic nudge | Read the message |
134
-
135
- Example:
136
- \`\`\`
137
- ur|rsk fan_in=24 fan_out=3 (high blast radius — get_references first)
138
- ur|dft modified on main by intent-abc
139
-
140
- {actual tool response data here…}
141
- \`\`\`
142
-
143
- When you see one of these prefixes, act on it. Do not strip or ignore them in your reasoning.
144
-
145
- #### Pagination & Narrowing
146
-
147
- unerr tool responses are universally capped (typical default 5-30 items per call) and may show this hint right after the prefix:
148
- \`\`\`
149
- ur| <tool>: N more available — pass limit:N or <filter>:V to narrow (use token_budget bump only for full payloads)
150
- \`\`\`
151
- **Prefer narrowing over budget bumps.** When you see the page hint:
152
- - Pass a more specific filter — \`fact_type:negative\`, \`entity:<name>\`, \`kind:function\`, \`direction:callees\`. This returns the *missing* slice.
153
- - Bump \`limit:N\` only if you genuinely need more items of the same kind.
154
- - \`token_budget:N\` is a special-case escape hatch — use only when you must read a full payload (e.g., reading a complete function body to refactor it).
155
-
156
- #### Response Body Formats
157
-
158
- Three on-the-wire shapes; the body's first line tells you which:
159
- - \`{...JSON...}\` — minified JSON (default for single objects)
160
- - \`_fmt:columnar\` — pipe-delimited table; line 2 is the column header (\`col1|col2|...\`), rows below
161
- - \`_fmt:multi\` — multi-section: \`@meta k=v|...\` for scalars, then \`@<arrayName>[col1|col2|...]\` for tabular sections, \`@<arrayName>[]\` for string lists. Used by \`file_outline\`, \`file_connections\`, \`get_conventions\`.
162
-
163
- A cell is escaped if it contains \`|\` or \`"\` — wrapped in double quotes, internal quotes doubled. Newlines in cell values become literal \`\\n\`.
164
-
165
- #### Persistent Intelligence (2 tools)
166
-
167
- | Task | Tool |
168
- |------|------|
169
- | Record a project fact/convention/anti-pattern | \`record_fact\` |
170
- | Recall stored facts | \`recall_facts\` |
171
-
172
- When the user says "remember this" or states a convention/anti-pattern, call \`record_fact\`. Facts also auto-detect from coding sessions — conventions, hot files, file coupling, and modification history are learned automatically. Episodic facts capture what was built, why, and how — they surface as \`ur|fct\` prefix lines on \`file_read\` / \`recall_facts\` responses when you work on previously-modified files.
173
-
174
- #### Session Narrative — Markers (4 tools)
175
-
176
- | Task | Tool |
177
- |------|------|
178
- | Mark the start of a non-trivial task (one short sentence) | \`mark_intent\` |
179
- | Record a deliberate choice between approaches | \`mark_decision\` |
180
- | Flag an unresolved obstacle you hit this turn | \`mark_blocker\` |
181
- | Resolve a previously marked blocker | \`mark_resolution\` (pass the marker_id from mark_blocker as \`blocker_ref\`) |
182
-
183
- Emit markers inline as you work — NOT as an end-of-turn summary. Each is one short string; mark_intent ≤80 chars, the rest ≤140. Unresolved blockers carry into the next session's resume strip. Markers are persisted to the shadow ledger and timeline.db; they power turn titles, intent stitching, and loop/blocker mining. Optional: layer is useful without them, but agents that mark intent + decisions make the timeline dramatically more readable.
184
-
185
- ### When to fall back to built-in tools
186
-
187
- ONLY use built-in Read/Grep/Glob when:
188
- - The unerr MCP server is not responding
189
- - You need to read a non-code file (images, binaries, PDFs)
190
- - You need complex regex patterns that \`search_code\` doesn't support
191
-
192
- ### Summary (CRITICAL — read this even if you skimmed above)
193
-
194
- ALWAYS use unerr MCP tools: \`search_code\`, \`get_references\`, \`file_read\`, \`file_outline\`, \`get_entity\`.${summaryEditNote}
195
- Before writing code: \`get_conventions\`. To record decisions: \`record_fact\`.`;
196
- }
197
- /**
198
- * Generate MDC-formatted instruction content for Cursor rules.
199
- */
200
- function getMdcContent() {
201
- return `---
202
- description: REQUIRED — use unerr graph intelligence tools instead of built-in Read/Grep/Glob
203
- alwaysApply: true
204
- ---
205
-
206
- ${getInstructionContent("cursor")}
207
- `;
208
- }
209
- /**
210
- * Write tool-preference instructions into the agent's instruction file.
211
- * Idempotent: creates, updates, or skips based on current state.
212
- */
213
- export function writeInstructionFile(cwd, ide) {
214
- const agentDef = getAgent(ide);
215
- if (!agentDef?.instructionFilePath) {
216
- return { path: "", action: "skipped" };
217
- }
218
- const filePath = join(cwd, agentDef.instructionFilePath);
219
- if (agentDef.instructionFormat === "mdc") {
220
- return writeMdcInstructionFile(filePath);
221
- }
222
- if (agentDef.instructionFormat === "windsurf-rule") {
223
- mkdirSync(dirname(filePath), { recursive: true });
224
- const content = getInstructionContent(ide);
225
- // Windsurf enforces 6K char limit per rule
226
- const truncatedContent = content.length > 5800
227
- ? `${content.slice(0, 5800)}\n\n[Truncated — full content exceeds Windsurf 6K limit]`
228
- : content;
229
- const windsurfContent = `---\ntrigger: always_on\ndescription: "Tool routing instructions for unerr MCP integration"\n---\n\n${truncatedContent}\n`;
230
- const existed = existsSync(filePath);
231
- if (existed) {
232
- const existing = readFileSync(filePath, "utf-8");
233
- if (existing === windsurfContent) {
234
- return { path: filePath, action: "skipped" };
235
- }
236
- }
237
- writeFileSync(filePath, windsurfContent, "utf-8");
238
- return { path: filePath, action: existed ? "updated" : "created" };
239
- }
240
- if (agentDef.instructionFormat === "antigravity-rule") {
241
- mkdirSync(dirname(filePath), { recursive: true });
242
- const content = getInstructionContent(ide);
243
- const antigravityContent = `---\nname: unerr-instructions\ndescription: Tool routing instructions for unerr MCP integration\ntype: manual\n---\n\n${content}\n`;
244
- const existed = existsSync(filePath);
245
- if (existed) {
246
- const existing = readFileSync(filePath, "utf-8");
247
- if (existing === antigravityContent) {
248
- return { path: filePath, action: "skipped" };
249
- }
250
- }
251
- writeFileSync(filePath, antigravityContent, "utf-8");
252
- return { path: filePath, action: existed ? "updated" : "created" };
253
- }
254
- // markdown format (CLAUDE.md, AGENTS.md, GEMINI.md, copilot-instructions.md, .clinerules)
255
- return mergeMarkdownSection(filePath, getInstructionContent(ide));
256
- }
257
- /**
258
- * Merge a sentinel-wrapped section into a markdown file.
259
- * - File doesn't exist → create with sentinel-wrapped content → "created"
260
- * - File exists, no sentinel → append at end → "updated"
261
- * - File exists, sentinel present, same content → "skipped"
262
- * - File exists, sentinel present, different → replace block → "updated"
263
- */
264
- function mergeMarkdownSection(filePath, content) {
265
- const wrappedContent = `${SENTINEL_START}\n${content}\n${SENTINEL_END}`;
266
- if (!existsSync(filePath)) {
267
- // Create new file with sentinel-wrapped content
268
- const dir = dirname(filePath);
269
- if (!existsSync(dir))
270
- mkdirSync(dir, { recursive: true });
271
- writeFileSync(filePath, `${wrappedContent}\n`);
272
- return { path: filePath, action: "created" };
273
- }
274
- const existing = readFileSync(filePath, "utf-8");
275
- const startIdx = existing.indexOf(SENTINEL_START);
276
- const endIdx = existing.indexOf(SENTINEL_END);
277
- if (startIdx === -1 || endIdx === -1) {
278
- // No sentinel markers — prepend at top (primacy bias: top-positioned instructions
279
- // are followed 2-3x more often by LLMs than bottom-positioned ones)
280
- writeFileSync(filePath, `${wrappedContent}\n\n${existing}`);
281
- return { path: filePath, action: "updated" };
282
- }
283
- // Sentinel markers found — check if content is identical
284
- const existingBlock = existing.slice(startIdx, endIdx + SENTINEL_END.length);
285
- if (existingBlock === wrappedContent) {
286
- return { path: filePath, action: "skipped" };
287
- }
288
- // Replace existing block
289
- const before = existing.slice(0, startIdx);
290
- const after = existing.slice(endIdx + SENTINEL_END.length);
291
- writeFileSync(filePath, `${before}${wrappedContent}${after}`);
292
- return { path: filePath, action: "updated" };
293
- }
294
- /**
295
- * Write a standalone .mdc instruction file for Cursor.
296
- * Overwrites entire file (it's ours). Returns "created" or "skipped".
297
- */
298
- function writeMdcInstructionFile(filePath) {
299
- const content = getMdcContent();
300
- const alreadyExists = existsSync(filePath);
301
- if (alreadyExists) {
302
- const existing = readFileSync(filePath, "utf-8");
303
- if (existing === content) {
304
- return { path: filePath, action: "skipped" };
305
- }
306
- }
307
- const dir = dirname(filePath);
308
- if (!existsSync(dir))
309
- mkdirSync(dir, { recursive: true });
310
- writeFileSync(filePath, content);
311
- return {
312
- path: filePath,
313
- action: alreadyExists ? "updated" : "created",
314
- };
315
- }
316
- /**
317
- * Remove unerr instruction section from the agent's instruction file.
318
- * For markdown: removes sentinel-wrapped block. For mdc: deletes the file.
319
- */
320
- export function removeInstructionSection(cwd, ide) {
321
- const agentDef = getAgent(ide);
322
- if (!agentDef?.instructionFilePath)
323
- return false;
324
- const filePath = join(cwd, agentDef.instructionFilePath);
325
- if (!existsSync(filePath))
326
- return false;
327
- if (agentDef.instructionFormat === "mdc") {
328
- // Delete the entire file (it's ours)
329
- unlinkSync(filePath);
330
- return true;
331
- }
332
- if (agentDef.instructionFormat === "windsurf-rule") {
333
- if (existsSync(filePath)) {
334
- unlinkSync(filePath);
335
- return true;
336
- }
337
- return false;
338
- }
339
- if (agentDef.instructionFormat === "antigravity-rule") {
340
- if (existsSync(filePath)) {
341
- unlinkSync(filePath);
342
- return true;
343
- }
344
- return false;
345
- }
346
- // Markdown: remove sentinel block
347
- const content = readFileSync(filePath, "utf-8");
348
- const startIdx = content.indexOf(SENTINEL_START);
349
- const endIdx = content.indexOf(SENTINEL_END);
350
- if (startIdx === -1 || endIdx === -1)
351
- return false;
352
- const before = content.slice(0, startIdx);
353
- const after = content.slice(endIdx + SENTINEL_END.length);
354
- // Clean up extra blank lines left behind
355
- const cleaned = `${(before + after).replace(/\n{3,}/g, "\n\n").trimEnd()}\n`;
356
- // If nothing meaningful remains, delete the file only if we created it
357
- const trimmed = cleaned.replace(/\s/g, "");
358
- if (trimmed.length === 0) {
359
- unlinkSync(filePath);
360
- }
361
- else {
362
- writeFileSync(filePath, cleaned);
363
- }
364
- return true;
365
- }
366
- /**
367
- * Generate formatted custom instructions text for --show-instructions output.
368
- */
369
- export function generateCustomInstructions(ide) {
370
- const content = getInstructionContent(ide);
371
- if (ide && ide !== "other") {
372
- const agentDef = getAgent(ide);
373
- if (agentDef?.instructionFilePath) {
374
- return [
375
- `Add the following to ${agentDef.instructionFilePath}:`,
376
- "",
377
- content,
378
- ].join("\n");
379
- }
380
- }
381
- // Generic instructions for any agent
382
- return [
383
- "Add the following to your agent's instruction file",
384
- "(CLAUDE.md, AGENTS.md, .cursorrules, GEMINI.md, etc.):",
385
- "",
386
- content,
387
- ].join("\n");
388
- }
@@ -1,266 +0,0 @@
1
- /**
2
- * MCP Config Writer — generates per-agent MCP config with unerr server configuration.
3
- *
4
- * Registry-driven: uses agent-registry.ts to determine config path and format.
5
- * Idempotent — never overwrites user customizations, only adds/merges unerr entry.
6
- *
7
- * Supported formats:
8
- * - mcp-json: { mcpServers: { unerr: { ... } } }
9
- * - settings-json: { "mcp": { "servers": { unerr: { ... } } } } (Gemini CLI)
10
- * - continue-config: { mcpServers: [{ name: "unerr", ... }] } (Continue.dev)
11
- */
12
- import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
13
- import { homedir } from "node:os";
14
- import { dirname, join } from "node:path";
15
- import { getAgent, } from "./agent-registry.js";
16
- const UNERR_SERVER_KEY = "unerr";
17
- function createUnerrServerEntry() {
18
- return {
19
- type: "stdio",
20
- command: "unerr",
21
- args: ["--mcp"],
22
- };
23
- }
24
- function createCopilotServerEntry() {
25
- return {
26
- type: "local",
27
- command: "unerr",
28
- args: ["--mcp"],
29
- };
30
- }
31
- /**
32
- * Write MCP config for a detected agent.
33
- * Idempotent: if config exists with unerr entry, skips.
34
- * If config exists without unerr entry, merges without overwriting.
35
- */
36
- export function writeMcpConfig(cwd, ide) {
37
- const agent = getAgent(ide);
38
- if (!agent) {
39
- return { path: "", action: "skipped" };
40
- }
41
- const configPath = agent.configScope === "global"
42
- ? join(homedir(), agent.projectConfigPath)
43
- : join(cwd, agent.projectConfigPath);
44
- const dir = dirname(configPath);
45
- if (!existsSync(dir)) {
46
- mkdirSync(dir, { recursive: true });
47
- }
48
- switch (agent.configFormat) {
49
- case "mcp-json":
50
- return writeMcpJsonFormat(configPath);
51
- case "settings-json":
52
- return writeSettingsJsonFormat(configPath);
53
- case "copilot-json":
54
- return writeCopilotJsonFormat(configPath);
55
- case "continue-config":
56
- return writeContinueFormat(configPath);
57
- default:
58
- return writeMcpJsonFormat(configPath);
59
- }
60
- }
61
- /**
62
- * Write config for ALL detected agents at once.
63
- */
64
- export function writeAllMcpConfigs(cwd, agents) {
65
- return agents.map((ide) => {
66
- const result = writeMcpConfig(cwd, ide);
67
- return { ide, ...result };
68
- });
69
- }
70
- /**
71
- * Remove unerr entry from MCP config.
72
- */
73
- export function removeMcpConfig(cwd, ide) {
74
- const agent = getAgent(ide);
75
- if (!agent)
76
- return false;
77
- const configPath = agent.configScope === "global"
78
- ? join(homedir(), agent.projectConfigPath)
79
- : join(cwd, agent.projectConfigPath);
80
- if (!existsSync(configPath))
81
- return false;
82
- try {
83
- const existing = JSON.parse(readFileSync(configPath, "utf-8"));
84
- if (agent.configFormat === "continue-config") {
85
- if (!Array.isArray(existing.mcpServers))
86
- return false;
87
- existing.mcpServers = existing.mcpServers.filter((s) => s.name !== UNERR_SERVER_KEY);
88
- }
89
- else if (agent.configFormat === "settings-json") {
90
- if (!existing.mcp?.servers?.[UNERR_SERVER_KEY])
91
- return false;
92
- delete existing.mcp.servers[UNERR_SERVER_KEY];
93
- }
94
- else if (agent.configFormat === "copilot-json") {
95
- if (!existing.mcpServers?.[UNERR_SERVER_KEY])
96
- return false;
97
- delete existing.mcpServers[UNERR_SERVER_KEY];
98
- }
99
- else {
100
- if (!existing.mcpServers?.[UNERR_SERVER_KEY])
101
- return false;
102
- delete existing.mcpServers[UNERR_SERVER_KEY];
103
- }
104
- writeFileSync(configPath, JSON.stringify(existing, null, 2), "utf-8");
105
- return true;
106
- }
107
- catch {
108
- return false;
109
- }
110
- }
111
- /**
112
- * Check if unerr is already configured for an IDE.
113
- */
114
- export function isConfigured(cwd, ide) {
115
- const agent = getAgent(ide);
116
- if (!agent)
117
- return false;
118
- const configPath = agent.configScope === "global"
119
- ? join(homedir(), agent.projectConfigPath)
120
- : join(cwd, agent.projectConfigPath);
121
- if (!existsSync(configPath))
122
- return false;
123
- try {
124
- const existing = JSON.parse(readFileSync(configPath, "utf-8"));
125
- if (agent.configFormat === "continue-config") {
126
- return (Array.isArray(existing.mcpServers) &&
127
- existing.mcpServers.some((s) => s.name === UNERR_SERVER_KEY));
128
- }
129
- if (agent.configFormat === "settings-json") {
130
- return !!existing.mcp?.servers?.[UNERR_SERVER_KEY];
131
- }
132
- if (agent.configFormat === "copilot-json") {
133
- return !!existing.mcpServers?.[UNERR_SERVER_KEY];
134
- }
135
- return !!existing.mcpServers?.[UNERR_SERVER_KEY];
136
- }
137
- catch {
138
- return false;
139
- }
140
- }
141
- /**
142
- * Generate the MCP config JSON string for manual copy-paste.
143
- * Useful for agents that can't be auto-configured.
144
- */
145
- export function generateConfigSnippet(ide) {
146
- const agent = getAgent(ide);
147
- if (!agent)
148
- return "";
149
- const entry = createUnerrServerEntry();
150
- switch (agent.configFormat) {
151
- case "settings-json":
152
- return JSON.stringify({ mcp: { servers: { [UNERR_SERVER_KEY]: entry } } }, null, 2);
153
- case "copilot-json": {
154
- const copilotEntry = createCopilotServerEntry();
155
- return JSON.stringify({ mcpServers: { [UNERR_SERVER_KEY]: copilotEntry } }, null, 2);
156
- }
157
- case "continue-config":
158
- return JSON.stringify({ mcpServers: [{ name: UNERR_SERVER_KEY, ...entry }] }, null, 2);
159
- default:
160
- return JSON.stringify({ mcpServers: { [UNERR_SERVER_KEY]: entry } }, null, 2);
161
- }
162
- }
163
- /**
164
- * Get config info for display purposes.
165
- */
166
- export function getConfigInfo(ide) {
167
- const agent = getAgent(ide);
168
- if (!agent)
169
- return null;
170
- return {
171
- path: agent.projectConfigPath,
172
- format: agent.configFormat,
173
- };
174
- }
175
- // ── Format-specific writers ─────────────────────────────────────
176
- function writeMcpJsonFormat(configPath) {
177
- if (existsSync(configPath)) {
178
- try {
179
- const existing = JSON.parse(readFileSync(configPath, "utf-8"));
180
- if (existing.mcpServers?.[UNERR_SERVER_KEY]) {
181
- return { path: configPath, action: "skipped" };
182
- }
183
- existing.mcpServers = existing.mcpServers ?? {};
184
- existing.mcpServers[UNERR_SERVER_KEY] = createUnerrServerEntry();
185
- writeFileSync(configPath, JSON.stringify(existing, null, 2), "utf-8");
186
- return { path: configPath, action: "updated" };
187
- }
188
- catch {
189
- return { path: configPath, action: "skipped" };
190
- }
191
- }
192
- const config = {
193
- mcpServers: { [UNERR_SERVER_KEY]: createUnerrServerEntry() },
194
- };
195
- writeFileSync(configPath, JSON.stringify(config, null, 2), "utf-8");
196
- return { path: configPath, action: "created" };
197
- }
198
- function writeSettingsJsonFormat(configPath) {
199
- if (existsSync(configPath)) {
200
- try {
201
- const existing = JSON.parse(readFileSync(configPath, "utf-8"));
202
- const mcp = (existing.mcp ?? {});
203
- const servers = (mcp.servers ?? {});
204
- if (servers[UNERR_SERVER_KEY]) {
205
- return { path: configPath, action: "skipped" };
206
- }
207
- servers[UNERR_SERVER_KEY] = createUnerrServerEntry();
208
- mcp.servers = servers;
209
- existing.mcp = mcp;
210
- writeFileSync(configPath, JSON.stringify(existing, null, 2), "utf-8");
211
- return { path: configPath, action: "updated" };
212
- }
213
- catch {
214
- return { path: configPath, action: "skipped" };
215
- }
216
- }
217
- const config = {
218
- mcp: { servers: { [UNERR_SERVER_KEY]: createUnerrServerEntry() } },
219
- };
220
- writeFileSync(configPath, JSON.stringify(config, null, 2), "utf-8");
221
- return { path: configPath, action: "created" };
222
- }
223
- function writeContinueFormat(configPath) {
224
- const entry = { name: UNERR_SERVER_KEY, ...createUnerrServerEntry() };
225
- if (existsSync(configPath)) {
226
- try {
227
- const existing = JSON.parse(readFileSync(configPath, "utf-8"));
228
- const servers = (existing.mcpServers ?? []);
229
- if (servers.some((s) => s.name === UNERR_SERVER_KEY)) {
230
- return { path: configPath, action: "skipped" };
231
- }
232
- servers.push(entry);
233
- existing.mcpServers = servers;
234
- writeFileSync(configPath, JSON.stringify(existing, null, 2), "utf-8");
235
- return { path: configPath, action: "updated" };
236
- }
237
- catch {
238
- return { path: configPath, action: "skipped" };
239
- }
240
- }
241
- const config = { mcpServers: [entry] };
242
- writeFileSync(configPath, JSON.stringify(config, null, 2), "utf-8");
243
- return { path: configPath, action: "created" };
244
- }
245
- function writeCopilotJsonFormat(configPath) {
246
- if (existsSync(configPath)) {
247
- try {
248
- const existing = JSON.parse(readFileSync(configPath, "utf-8"));
249
- if (existing.mcpServers?.[UNERR_SERVER_KEY]) {
250
- return { path: configPath, action: "skipped" };
251
- }
252
- existing.mcpServers = existing.mcpServers ?? {};
253
- existing.mcpServers[UNERR_SERVER_KEY] = createCopilotServerEntry();
254
- writeFileSync(configPath, JSON.stringify(existing, null, 2), "utf-8");
255
- return { path: configPath, action: "updated" };
256
- }
257
- catch {
258
- return { path: configPath, action: "skipped" };
259
- }
260
- }
261
- const config = {
262
- mcpServers: { [UNERR_SERVER_KEY]: createCopilotServerEntry() },
263
- };
264
- writeFileSync(configPath, JSON.stringify(config, null, 2), "utf-8");
265
- return { path: configPath, action: "created" };
266
- }