@agntk/agent-harness 0.1.4 → 0.1.6

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 (178) hide show
  1. package/README.md +104 -43
  2. package/dist/{analytics-RPT73WNM.js → analytics-L24W3B7U.js} +1 -2
  3. package/dist/auto-processor-QIRUOGEI.js +12 -0
  4. package/dist/{chunk-UPLBF4RZ.js → chunk-2UVWCTAY.js} +2 -2
  5. package/dist/{chunk-CSL3ERUI.js → chunk-4P6TRFPZ.js} +3 -3
  6. package/dist/chunk-4P6TRFPZ.js.map +1 -0
  7. package/dist/{chunk-4CWAGBNS.js → chunk-4TQQZILG.js} +73 -3
  8. package/dist/chunk-4TQQZILG.js.map +1 -0
  9. package/dist/{chunk-DA7IKHC4.js → chunk-5CO5JTYT.js} +2 -2
  10. package/dist/chunk-5CO5JTYT.js.map +1 -0
  11. package/dist/{chunk-A7BJPQQ6.js → chunk-5O5OGOOQ.js} +2 -2
  12. package/dist/{chunk-UWQTZMNI.js → chunk-7GZ4D6V6.js} +2 -2
  13. package/dist/{chunk-FLZU44SV.js → chunk-AN6Y4MDD.js} +6 -6
  14. package/dist/{chunk-4FDUOGSZ.js → chunk-D7AWV24Z.js} +3 -3
  15. package/dist/{chunk-CHJ5GNZC.js → chunk-EC42HQQH.js} +2 -2
  16. package/dist/{chunk-YIJY5DBV.js → chunk-KLYMGWQJ.js} +4 -4
  17. package/dist/chunk-KLYMGWQJ.js.map +1 -0
  18. package/dist/{chunk-YUFNYN2H.js → chunk-M62KLIEK.js} +4 -4
  19. package/dist/chunk-M62KLIEK.js.map +1 -0
  20. package/dist/{chunk-GJNNR2RA.js → chunk-M6PDMK2O.js} +3 -3
  21. package/dist/{chunk-M7NXUK55.js → chunk-NVC2WY4K.js} +2 -2
  22. package/dist/{chunk-XTBKL5BI.js → chunk-PMFAYKBD.js} +2 -2
  23. package/dist/chunk-PMFAYKBD.js.map +1 -0
  24. package/dist/{chunk-274RV3YO.js → chunk-QMOIVORH.js} +3 -3
  25. package/dist/chunk-QMOIVORH.js.map +1 -0
  26. package/dist/{chunk-RY3ZFII7.js → chunk-SEHAQTBO.js} +6 -6
  27. package/dist/{chunk-MPZ3BPUI.js → chunk-UMXPOYZR.js} +4 -4
  28. package/dist/{chunk-W4T7PGI2.js → chunk-UXCHAS3Z.js} +4 -4
  29. package/dist/cli/index.js +153 -167
  30. package/dist/cli/index.js.map +1 -1
  31. package/dist/config-PYSS3QY6.js +12 -0
  32. package/dist/context-loader-RSXXFW5R.js +12 -0
  33. package/dist/{conversation-QDEIDQPH.js → conversation-TBTFIJVU.js} +6 -7
  34. package/dist/{cost-tracker-RS3W7SVY.js → cost-tracker-NZRZEHVA.js} +1 -2
  35. package/dist/{delegate-VJCJLYEK.js → delegate-3KJAL4NZ.js} +7 -8
  36. package/dist/{emotional-state-VQVRA6ED.js → emotional-state-IN4ZUL2Q.js} +1 -2
  37. package/dist/{emotional-state-VQVRA6ED.js.map → emotional-state-IN4ZUL2Q.js.map} +1 -1
  38. package/dist/{env-discovery-2BLVMAIM.js → env-discovery-PXBRE5FX.js} +1 -2
  39. package/dist/{env-discovery-2BLVMAIM.js.map → env-discovery-PXBRE5FX.js.map} +1 -1
  40. package/dist/{export-6GCYHEHQ.js → export-GYLWROMB.js} +3 -4
  41. package/dist/{export-6GCYHEHQ.js.map → export-GYLWROMB.js.map} +1 -1
  42. package/dist/graph-LEEO37L3.js +13 -0
  43. package/dist/{harness-WE4SLCML.js → harness-R5FKRICG.js} +8 -9
  44. package/dist/{health-NZ6WNIMV.js → health-HL2JYHIY.js} +1 -2
  45. package/dist/indexer-L5UC6J2V.js +15 -0
  46. package/dist/{instinct-learner-SRM72DHF.js → instinct-learner-QGAMIS3X.js} +5 -6
  47. package/dist/{intake-4M3HNU43.js → intake-SVJKFHTL.js} +5 -6
  48. package/dist/{intelligence-HJOCA4SJ.js → intelligence-XPV3MC5U.js} +10 -13
  49. package/dist/intelligence-XPV3MC5U.js.map +1 -0
  50. package/dist/{journal-WANJL3MI.js → journal-ITUMKT6U.js} +5 -6
  51. package/dist/{loader-C3TKIKZR.js → loader-27PLDCOJ.js} +3 -4
  52. package/dist/{mcp-WTQJJZAO.js → mcp-JSIUJJZV.js} +1 -2
  53. package/dist/{mcp-discovery-WPAQFL6S.js → mcp-discovery-DG3RQYLM.js} +1 -2
  54. package/dist/{mcp-discovery-WPAQFL6S.js.map → mcp-discovery-DG3RQYLM.js.map} +1 -1
  55. package/dist/{mcp-installer-6O2XXD3V.js → mcp-installer-X2TJ2S2G.js} +3 -4
  56. package/dist/{mcp-installer-6O2XXD3V.js.map → mcp-installer-X2TJ2S2G.js.map} +1 -1
  57. package/dist/{metrics-KXGNFAAB.js → metrics-2MNINXNQ.js} +1 -2
  58. package/dist/{primitive-registry-I6VTIR4W.js → primitive-registry-ZMGGXSO5.js} +3 -4
  59. package/dist/{primitive-registry-I6VTIR4W.js.map → primitive-registry-ZMGGXSO5.js.map} +1 -1
  60. package/dist/{project-discovery-C4UMD7JI.js → project-discovery-FQLAZKEM.js} +1 -2
  61. package/dist/project-discovery-FQLAZKEM.js.map +1 -0
  62. package/dist/{provider-SXPQZ74H.js → provider-HQY6SPZI.js} +1 -2
  63. package/dist/{rate-limiter-RLRVM325.js → rate-limiter-PH5DCVU4.js} +1 -2
  64. package/dist/{rule-engine-YGQ3RYZM.js → rule-engine-DM26S77N.js} +3 -4
  65. package/dist/{rule-engine-YGQ3RYZM.js.map → rule-engine-DM26S77N.js.map} +1 -1
  66. package/dist/{scaffold-A3VRRCBV.js → scaffold-2F36YVW6.js} +5 -6
  67. package/dist/{scaffold-A3VRRCBV.js.map → scaffold-2F36YVW6.js.map} +1 -1
  68. package/dist/{scheduler-XHHIVHRI.js → scheduler-Q7GB2KCW.js} +11 -12
  69. package/dist/{scheduler-XHHIVHRI.js.map → scheduler-Q7GB2KCW.js.map} +1 -1
  70. package/dist/{search-V3W5JMJG.js → search-6Y6NCOLQ.js} +3 -4
  71. package/dist/search-6Y6NCOLQ.js.map +1 -0
  72. package/dist/{semantic-search-2DTOO5UX.js → semantic-search-FN6FZIXI.js} +3 -4
  73. package/dist/semantic-search-FN6FZIXI.js.map +1 -0
  74. package/dist/{serve-DTQ3HENY.js → serve-MXRTP2HE.js} +10 -11
  75. package/dist/serve-MXRTP2HE.js.map +1 -0
  76. package/dist/{sessions-CZGVXKQE.js → sessions-G6SZZXWS.js} +1 -2
  77. package/dist/{sources-RW5DT56F.js → sources-7LDYO5GK.js} +1 -2
  78. package/dist/{starter-packs-76YUVHEU.js → starter-packs-OR7NI5NA.js} +1 -2
  79. package/dist/{starter-packs-76YUVHEU.js.map → starter-packs-OR7NI5NA.js.map} +1 -1
  80. package/dist/{state-GMXILIHW.js → state-25IQEC5C.js} +1 -2
  81. package/dist/{state-merge-NKO5FRBA.js → state-merge-E333OEIQ.js} +1 -2
  82. package/dist/{state-merge-NKO5FRBA.js.map → state-merge-E333OEIQ.js.map} +1 -1
  83. package/dist/{telemetry-UC6PBXC7.js → telemetry-RS2JZUZP.js} +4 -5
  84. package/dist/{tool-executor-MJ7IG7PQ.js → tool-executor-6I5PHQDY.js} +5 -6
  85. package/dist/{tools-DZ4KETET.js → tools-NDFJNVHK.js} +4 -5
  86. package/dist/{types-EW7AIB3R.js → types-NPJZAI72.js} +2 -3
  87. package/dist/{universal-installer-AAXXYM5A.js → universal-installer-LCAZHFZR.js} +91 -7
  88. package/dist/universal-installer-LCAZHFZR.js.map +1 -0
  89. package/dist/validator-LM7RZWSH.js +21 -0
  90. package/dist/{verification-gate-FYXUX6LH.js → verification-gate-2O6DF2B7.js} +3 -4
  91. package/dist/verification-gate-2O6DF2B7.js.map +1 -0
  92. package/dist/{versioning-Z3XNE2Q2.js → versioning-WEGF6KJG.js} +1 -2
  93. package/dist/versioning-WEGF6KJG.js.map +1 -0
  94. package/dist/{watcher-ISJC7YKL.js → watcher-GZWQSWZ6.js} +5 -6
  95. package/dist/{watcher-ISJC7YKL.js.map → watcher-GZWQSWZ6.js.map} +1 -1
  96. package/dist/{web-server-DD7ZOP46.js → web-server-2Y4CHD2W.js} +8 -9
  97. package/package.json +1 -9
  98. package/dist/agent-framework-K4GUIICH.js +0 -344
  99. package/dist/agent-framework-K4GUIICH.js.map +0 -1
  100. package/dist/auto-processor-OLE45UI3.js +0 -13
  101. package/dist/chunk-274RV3YO.js.map +0 -1
  102. package/dist/chunk-4CWAGBNS.js.map +0 -1
  103. package/dist/chunk-CSL3ERUI.js.map +0 -1
  104. package/dist/chunk-DA7IKHC4.js.map +0 -1
  105. package/dist/chunk-DGUM43GV.js +0 -11
  106. package/dist/chunk-FD55B3IO.js +0 -204
  107. package/dist/chunk-FD55B3IO.js.map +0 -1
  108. package/dist/chunk-GUJTBGVS.js +0 -2212
  109. package/dist/chunk-GUJTBGVS.js.map +0 -1
  110. package/dist/chunk-KFX54TQM.js +0 -165
  111. package/dist/chunk-KFX54TQM.js.map +0 -1
  112. package/dist/chunk-XTBKL5BI.js.map +0 -1
  113. package/dist/chunk-YIJY5DBV.js.map +0 -1
  114. package/dist/chunk-YUFNYN2H.js.map +0 -1
  115. package/dist/chunk-ZZJOFKAT.js +0 -13
  116. package/dist/config-WVMRUOCA.js +0 -13
  117. package/dist/context-loader-3ORBPMHJ.js +0 -13
  118. package/dist/graph-YUIPOSOO.js +0 -14
  119. package/dist/harness-LCHA3DWP.js +0 -10
  120. package/dist/index.d.ts +0 -3612
  121. package/dist/index.js +0 -13713
  122. package/dist/index.js.map +0 -1
  123. package/dist/indexer-LONANRRM.js +0 -16
  124. package/dist/intelligence-HJOCA4SJ.js.map +0 -1
  125. package/dist/project-discovery-C4UMD7JI.js.map +0 -1
  126. package/dist/provider-LQHQX7Z7.js +0 -26
  127. package/dist/search-V3W5JMJG.js.map +0 -1
  128. package/dist/semantic-search-2DTOO5UX.js.map +0 -1
  129. package/dist/serve-DTQ3HENY.js.map +0 -1
  130. package/dist/tools-DZ4KETET.js.map +0 -1
  131. package/dist/types-EW7AIB3R.js.map +0 -1
  132. package/dist/types-WGDLSPO6.js +0 -16
  133. package/dist/types-WGDLSPO6.js.map +0 -1
  134. package/dist/universal-installer-AAXXYM5A.js.map +0 -1
  135. package/dist/validator-7WXMDIHH.js +0 -22
  136. package/dist/validator-7WXMDIHH.js.map +0 -1
  137. package/dist/verification-gate-FYXUX6LH.js.map +0 -1
  138. package/dist/versioning-Z3XNE2Q2.js.map +0 -1
  139. package/dist/web-server-DD7ZOP46.js.map +0 -1
  140. /package/dist/{analytics-RPT73WNM.js.map → analytics-L24W3B7U.js.map} +0 -0
  141. /package/dist/{auto-processor-OLE45UI3.js.map → auto-processor-QIRUOGEI.js.map} +0 -0
  142. /package/dist/{chunk-UPLBF4RZ.js.map → chunk-2UVWCTAY.js.map} +0 -0
  143. /package/dist/{chunk-A7BJPQQ6.js.map → chunk-5O5OGOOQ.js.map} +0 -0
  144. /package/dist/{chunk-UWQTZMNI.js.map → chunk-7GZ4D6V6.js.map} +0 -0
  145. /package/dist/{chunk-FLZU44SV.js.map → chunk-AN6Y4MDD.js.map} +0 -0
  146. /package/dist/{chunk-4FDUOGSZ.js.map → chunk-D7AWV24Z.js.map} +0 -0
  147. /package/dist/{chunk-CHJ5GNZC.js.map → chunk-EC42HQQH.js.map} +0 -0
  148. /package/dist/{chunk-GJNNR2RA.js.map → chunk-M6PDMK2O.js.map} +0 -0
  149. /package/dist/{chunk-M7NXUK55.js.map → chunk-NVC2WY4K.js.map} +0 -0
  150. /package/dist/{chunk-RY3ZFII7.js.map → chunk-SEHAQTBO.js.map} +0 -0
  151. /package/dist/{chunk-MPZ3BPUI.js.map → chunk-UMXPOYZR.js.map} +0 -0
  152. /package/dist/{chunk-W4T7PGI2.js.map → chunk-UXCHAS3Z.js.map} +0 -0
  153. /package/dist/{chunk-DGUM43GV.js.map → config-PYSS3QY6.js.map} +0 -0
  154. /package/dist/{chunk-ZZJOFKAT.js.map → context-loader-RSXXFW5R.js.map} +0 -0
  155. /package/dist/{config-WVMRUOCA.js.map → conversation-TBTFIJVU.js.map} +0 -0
  156. /package/dist/{context-loader-3ORBPMHJ.js.map → cost-tracker-NZRZEHVA.js.map} +0 -0
  157. /package/dist/{conversation-QDEIDQPH.js.map → delegate-3KJAL4NZ.js.map} +0 -0
  158. /package/dist/{cost-tracker-RS3W7SVY.js.map → graph-LEEO37L3.js.map} +0 -0
  159. /package/dist/{delegate-VJCJLYEK.js.map → harness-R5FKRICG.js.map} +0 -0
  160. /package/dist/{graph-YUIPOSOO.js.map → health-HL2JYHIY.js.map} +0 -0
  161. /package/dist/{harness-LCHA3DWP.js.map → indexer-L5UC6J2V.js.map} +0 -0
  162. /package/dist/{harness-WE4SLCML.js.map → instinct-learner-QGAMIS3X.js.map} +0 -0
  163. /package/dist/{health-NZ6WNIMV.js.map → intake-SVJKFHTL.js.map} +0 -0
  164. /package/dist/{indexer-LONANRRM.js.map → journal-ITUMKT6U.js.map} +0 -0
  165. /package/dist/{instinct-learner-SRM72DHF.js.map → loader-27PLDCOJ.js.map} +0 -0
  166. /package/dist/{intake-4M3HNU43.js.map → mcp-JSIUJJZV.js.map} +0 -0
  167. /package/dist/{journal-WANJL3MI.js.map → metrics-2MNINXNQ.js.map} +0 -0
  168. /package/dist/{loader-C3TKIKZR.js.map → provider-HQY6SPZI.js.map} +0 -0
  169. /package/dist/{mcp-WTQJJZAO.js.map → rate-limiter-PH5DCVU4.js.map} +0 -0
  170. /package/dist/{metrics-KXGNFAAB.js.map → sessions-G6SZZXWS.js.map} +0 -0
  171. /package/dist/{provider-LQHQX7Z7.js.map → sources-7LDYO5GK.js.map} +0 -0
  172. /package/dist/{provider-SXPQZ74H.js.map → state-25IQEC5C.js.map} +0 -0
  173. /package/dist/{rate-limiter-RLRVM325.js.map → telemetry-RS2JZUZP.js.map} +0 -0
  174. /package/dist/{sessions-CZGVXKQE.js.map → tool-executor-6I5PHQDY.js.map} +0 -0
  175. /package/dist/{sources-RW5DT56F.js.map → tools-NDFJNVHK.js.map} +0 -0
  176. /package/dist/{state-GMXILIHW.js.map → types-NPJZAI72.js.map} +0 -0
  177. /package/dist/{telemetry-UC6PBXC7.js.map → validator-LM7RZWSH.js.map} +0 -0
  178. /package/dist/{tool-executor-MJ7IG7PQ.js.map → web-server-2Y4CHD2W.js.map} +0 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/runtime/intelligence.ts"],"sourcesContent":["import { existsSync, readdirSync, readFileSync, statSync } from 'fs';\nimport { join, relative } from 'path';\nimport { loadDirectoryWithErrors, loadDirectory } from '../primitives/loader.js';\nimport { buildDependencyGraph } from './graph.js';\nimport { getPrimitiveDirs } from '../core/types.js';\nimport type { HarnessConfig, HarnessDocument } from '../core/types.js';\nimport { loadConfig } from '../core/config.js';\nimport type { InstinctCandidate } from './instinct-learner.js';\nimport { installInstinct } from './instinct-learner.js';\nimport { log } from '../core/logger.js';\nimport { checkRateLimit } from './rate-limiter.js';\nimport { buildRateLimits } from './guardrails.js';\nimport { checkBudget } from './cost-tracker.js';\nimport { validateHarness } from './validator.js';\n\n// --- Auto-Promote Instincts ---\n\nexport interface PatternOccurrence {\n behavior: string;\n journalDates: string[];\n count: number;\n}\n\nexport interface AutoPromoteResult {\n patterns: PatternOccurrence[];\n promoted: string[];\n skipped: string[];\n journalsScanned: number;\n}\n\n/**\n * Scan all journals for instinct candidates that appear 3+ times.\n * These repeated patterns suggest strong behavioral signals worth auto-promoting.\n *\n * The function:\n * 1. Reads all journal files\n * 2. Extracts \"## Instinct Candidates\" sections\n * 3. Normalizes behavior text for fuzzy matching\n * 4. Groups by similar behavior (normalized string comparison)\n * 5. Returns patterns with 3+ occurrences across different journal dates\n * 6. Optionally auto-installs promoted instincts\n */\nexport function autoPromoteInstincts(\n harnessDir: string,\n options?: { threshold?: number; install?: boolean },\n): AutoPromoteResult {\n const threshold = options?.threshold ?? 3;\n const journalDir = join(harnessDir, 'memory', 'journal');\n\n if (!existsSync(journalDir)) {\n return { patterns: [], promoted: [], skipped: [], journalsScanned: 0 };\n }\n\n const files = readdirSync(journalDir)\n .filter((f) => f.endsWith('.md') && /^\\d{4}-\\d{2}-\\d{2}/.test(f))\n .sort();\n\n // Collect all instinct candidate behaviors with their journal dates\n const behaviorMap = new Map<string, { original: string; dates: Set<string> }>();\n\n for (const file of files) {\n const content = readFileSync(join(journalDir, file), 'utf-8');\n const dateMatch = file.match(/^(\\d{4}-\\d{2}-\\d{2})/);\n if (!dateMatch) continue;\n const journalDate = dateMatch[1];\n\n // Extract instinct candidates section\n const sectionMatch = content.match(/## Instinct Candidates\\n([\\s\\S]*?)(?=\\n## |\\n*$)/);\n if (!sectionMatch) continue;\n\n const lines = sectionMatch[1]\n .split('\\n')\n .filter((l) => l.startsWith('- '))\n .map((l) => l.slice(2).trim().replace(/^INSTINCT:\\s*/i, ''));\n\n for (const line of lines) {\n if (!line) continue;\n const normalized = normalizeBehavior(line);\n if (!normalized) continue;\n\n const existing = behaviorMap.get(normalized);\n if (existing) {\n existing.dates.add(journalDate);\n } else {\n behaviorMap.set(normalized, { original: line, dates: new Set([journalDate]) });\n }\n }\n }\n\n // Filter to patterns with threshold+ occurrences across different dates\n const patterns: PatternOccurrence[] = [];\n for (const [, value] of behaviorMap) {\n if (value.dates.size >= threshold) {\n patterns.push({\n behavior: value.original,\n journalDates: [...value.dates].sort(),\n count: value.dates.size,\n });\n }\n }\n\n // Sort by count descending\n patterns.sort((a, b) => b.count - a.count);\n\n // Deduplicate against existing instincts\n const existingIds = new Set<string>();\n const existingBehaviors = new Set<string>();\n const instinctsDir = join(harnessDir, 'instincts');\n if (existsSync(instinctsDir)) {\n const docs = loadDirectory(instinctsDir);\n for (const doc of docs) {\n existingIds.add(doc.frontmatter.id);\n if (doc.l0) existingBehaviors.add(normalizeBehavior(doc.l0));\n }\n }\n\n const promoted: string[] = [];\n const skipped: string[] = [];\n\n for (const pattern of patterns) {\n const normalized = normalizeBehavior(pattern.behavior);\n const id = behaviorToId(pattern.behavior);\n\n if (existingIds.has(id) || existingBehaviors.has(normalized)) {\n skipped.push(id);\n continue;\n }\n\n if (options?.install) {\n const candidate: InstinctCandidate = {\n id,\n behavior: pattern.behavior,\n provenance: `auto-promote:${pattern.journalDates.length}x across ${pattern.journalDates[0]} to ${pattern.journalDates[pattern.journalDates.length - 1]}`,\n confidence: Math.min(0.9, 0.5 + pattern.count * 0.1),\n };\n\n const path = installInstinct(harnessDir, candidate);\n if (path) {\n promoted.push(id);\n } else {\n skipped.push(id);\n }\n }\n }\n\n return { patterns, promoted, skipped, journalsScanned: files.length };\n}\n\n/**\n * Normalize behavior text for fuzzy matching.\n * Lowercases, strips punctuation, collapses whitespace.\n */\nfunction normalizeBehavior(text: string): string {\n return text\n .toLowerCase()\n .replace(/[^a-z0-9\\s]/g, '')\n .replace(/\\s+/g, ' ')\n .trim();\n}\n\n/**\n * Convert a behavior string to a kebab-case ID.\n */\nfunction behaviorToId(behavior: string): string {\n return behavior\n .toLowerCase()\n .replace(/[^a-z0-9\\s-]/g, '')\n .replace(/\\s+/g, '-')\n .slice(0, 50)\n .replace(/-+$/, '');\n}\n\n// --- Dead Primitive Detection ---\n\nexport interface DeadPrimitive {\n id: string;\n path: string;\n directory: string;\n lastModified: string;\n daysSinceModified: number;\n reason: string;\n}\n\nexport interface DeadPrimitiveResult {\n dead: DeadPrimitive[];\n totalScanned: number;\n thresholdDays: number;\n}\n\n/**\n * Detect \"dead\" primitives — files that are:\n * 1. Orphaned (no incoming or outgoing references via related:/with:)\n * 2. Not modified in the last N days (default 30)\n *\n * Excludes session and journal directories (memory files).\n * Does NOT flag recently created primitives even if orphaned.\n */\nexport function detectDeadPrimitives(\n harnessDir: string,\n config?: HarnessConfig,\n options?: { thresholdDays?: number },\n): DeadPrimitiveResult {\n const thresholdDays = options?.thresholdDays ?? 30;\n const now = Date.now();\n\n // Build dependency graph to find orphans\n const graph = buildDependencyGraph(harnessDir, config);\n const orphanIds = new Set(graph.orphans);\n\n // Also find nodes with only broken refs (effectively orphaned)\n const connectedIds = new Set<string>();\n for (const edge of graph.edges) {\n connectedIds.add(edge.from);\n connectedIds.add(edge.to);\n }\n\n const dead: DeadPrimitive[] = [];\n let totalScanned = 0;\n\n for (const node of graph.nodes) {\n totalScanned++;\n\n // Skip non-orphans\n if (!orphanIds.has(node.id)) continue;\n\n // Check file modification time\n const absPath = join(harnessDir, node.path);\n if (!existsSync(absPath)) continue;\n\n try {\n const stat = statSync(absPath);\n const mtime = stat.mtime.getTime();\n const daysSince = Math.floor((now - mtime) / (24 * 60 * 60 * 1000));\n\n if (daysSince >= thresholdDays) {\n dead.push({\n id: node.id,\n path: node.path,\n directory: node.directory,\n lastModified: stat.mtime.toISOString().split('T')[0],\n daysSinceModified: daysSince,\n reason: `Orphaned (no references) and not modified in ${daysSince} days`,\n });\n }\n } catch (err) {\n log.warn(`Failed to stat ${absPath}: ${err instanceof Error ? err.message : String(err)}`);\n }\n }\n\n // Sort by days since modified (most stale first)\n dead.sort((a, b) => b.daysSinceModified - a.daysSinceModified);\n\n return { dead, totalScanned, thresholdDays };\n}\n\n// --- Contradiction Detection ---\n\nexport interface Contradiction {\n primitiveA: { id: string; path: string; type: string; text: string };\n primitiveB: { id: string; path: string; type: string; text: string };\n reason: string;\n severity: 'low' | 'medium' | 'high';\n}\n\nexport interface ContradictionResult {\n contradictions: Contradiction[];\n rulesChecked: number;\n instinctsChecked: number;\n}\n\n/**\n * Detect contradictions between rules and instincts.\n *\n * Checks for:\n * 1. Direct negation patterns (\"always X\" vs \"never X\", \"do X\" vs \"don't X\")\n * 2. Conflicting tag overlap with opposing behavioral signals\n * 3. Same topic with contradictory directives\n *\n * This is a heuristic-based detector (no LLM needed).\n * Returns candidate contradictions for human review.\n */\nexport function detectContradictions(\n harnessDir: string,\n): ContradictionResult {\n const rulesDir = join(harnessDir, 'rules');\n const instinctsDir = join(harnessDir, 'instincts');\n\n const rules: HarnessDocument[] = existsSync(rulesDir) ? loadDirectory(rulesDir) : [];\n const instincts: HarnessDocument[] = existsSync(instinctsDir) ? loadDirectory(instinctsDir) : [];\n\n const contradictions: Contradiction[] = [];\n\n // Build a lookup of behavioral directives from each document\n const ruleDirectives = rules.map((doc) => ({\n doc,\n directives: extractDirectives(doc),\n topics: extractTopics(doc),\n }));\n\n const instinctDirectives = instincts.map((doc) => ({\n doc,\n directives: extractDirectives(doc),\n topics: extractTopics(doc),\n }));\n\n // Cross-check rules vs instincts\n for (const rule of ruleDirectives) {\n for (const instinct of instinctDirectives) {\n // Check directive negation patterns\n for (const rd of rule.directives) {\n for (const id of instinct.directives) {\n const negation = checkNegation(rd, id);\n if (negation) {\n contradictions.push({\n primitiveA: {\n id: rule.doc.frontmatter.id,\n path: relative(harnessDir, rule.doc.path),\n type: 'rule',\n text: rd.raw,\n },\n primitiveB: {\n id: instinct.doc.frontmatter.id,\n path: relative(harnessDir, instinct.doc.path),\n type: 'instinct',\n text: id.raw,\n },\n reason: negation,\n severity: 'high',\n });\n }\n }\n }\n\n // Check topic conflicts (same topic, opposing signals)\n const sharedTopics = rule.topics.filter((t) => instinct.topics.includes(t));\n if (sharedTopics.length > 0) {\n // Check if one says \"always\" and other says \"never\" about shared topic\n const ruleText = (rule.doc.l0 + ' ' + rule.doc.body).toLowerCase();\n const instinctText = (instinct.doc.l0 + ' ' + instinct.doc.body).toLowerCase();\n\n for (const topic of sharedTopics) {\n const ruleHasAlways = hasPositiveDirective(ruleText, topic);\n const instinctHasNever = hasNegativeDirective(instinctText, topic);\n const ruleHasNever = hasNegativeDirective(ruleText, topic);\n const instinctHasAlways = hasPositiveDirective(instinctText, topic);\n\n if ((ruleHasAlways && instinctHasNever) || (ruleHasNever && instinctHasAlways)) {\n // Avoid duplicate if already caught by directive check\n const alreadyCaught = contradictions.some(\n (c) =>\n c.primitiveA.id === rule.doc.frontmatter.id &&\n c.primitiveB.id === instinct.doc.frontmatter.id,\n );\n if (!alreadyCaught) {\n contradictions.push({\n primitiveA: {\n id: rule.doc.frontmatter.id,\n path: relative(harnessDir, rule.doc.path),\n type: 'rule',\n text: rule.doc.l0 || rule.doc.frontmatter.id,\n },\n primitiveB: {\n id: instinct.doc.frontmatter.id,\n path: relative(harnessDir, instinct.doc.path),\n type: 'instinct',\n text: instinct.doc.l0 || instinct.doc.frontmatter.id,\n },\n reason: `Conflicting directives about \"${topic}\"`,\n severity: 'medium',\n });\n }\n }\n }\n }\n }\n }\n\n // Also check rules vs rules and instincts vs instincts\n checkIntraGroupContradictions(ruleDirectives, 'rule', harnessDir, contradictions);\n checkIntraGroupContradictions(instinctDirectives, 'instinct', harnessDir, contradictions);\n\n return {\n contradictions,\n rulesChecked: rules.length,\n instinctsChecked: instincts.length,\n };\n}\n\ninterface Directive {\n action: 'positive' | 'negative';\n verb: string;\n subject: string;\n raw: string;\n}\n\n/**\n * Extract behavioral directives from a document.\n * Looks for patterns like \"always X\", \"never Y\", \"do X\", \"don't Y\", \"avoid X\", \"prefer Y\".\n */\nfunction extractDirectives(doc: HarnessDocument): Directive[] {\n const directives: Directive[] = [];\n const text = (doc.l0 + '\\n' + doc.body).trim();\n\n // Process line by line\n for (const line of text.split('\\n')) {\n const trimmed = line.trim().toLowerCase();\n if (!trimmed || trimmed.startsWith('#')) continue;\n\n // Strip list markers\n const cleaned = trimmed.replace(/^[-*]\\s+/, '').replace(/^\\d+\\.\\s+/, '');\n\n // Positive patterns\n const positiveMatch = cleaned.match(\n /^(always|must|should|prefer|ensure|require|use)\\s+(.+)/,\n );\n if (positiveMatch) {\n directives.push({\n action: 'positive',\n verb: positiveMatch[1],\n subject: positiveMatch[2].replace(/[.!]$/, ''),\n raw: cleaned,\n });\n continue;\n }\n\n // Negative patterns\n const negativeMatch = cleaned.match(\n /^(never|don'?t|avoid|do not|must not|should not|shouldn'?t)\\s+(.+)/,\n );\n if (negativeMatch) {\n directives.push({\n action: 'negative',\n verb: negativeMatch[1],\n subject: negativeMatch[2].replace(/[.!]$/, ''),\n raw: cleaned,\n });\n }\n }\n\n return directives;\n}\n\n/**\n * Extract topic keywords from a document (from tags, ID, and L0).\n */\nfunction extractTopics(doc: HarnessDocument): string[] {\n const topics: string[] = [];\n\n // Tags as topics\n for (const tag of doc.frontmatter.tags) {\n topics.push(tag.toLowerCase());\n }\n\n // ID words as topics\n const idParts = doc.frontmatter.id.split('-').filter((p) => p.length > 2);\n topics.push(...idParts.map((p) => p.toLowerCase()));\n\n return [...new Set(topics)];\n}\n\n/**\n * Check if two directives are negations of each other.\n */\nfunction checkNegation(a: Directive, b: Directive): string | null {\n // One positive, one negative\n if (a.action === b.action) return null;\n\n // Normalize subjects for comparison\n const subA = a.subject.toLowerCase().replace(/\\s+/g, ' ').trim();\n const subB = b.subject.toLowerCase().replace(/\\s+/g, ' ').trim();\n\n // Direct subject match\n if (subA === subB) {\n return `Direct contradiction: \"${a.raw}\" vs \"${b.raw}\"`;\n }\n\n // Fuzzy match: check if one subject is a substring of the other (with word boundaries)\n const wordsA = subA.split(' ').filter((w) => w.length > 3);\n const wordsB = subB.split(' ').filter((w) => w.length > 3);\n const overlap = wordsA.filter((w) => wordsB.includes(w));\n\n if (overlap.length >= 2 && overlap.length >= Math.min(wordsA.length, wordsB.length) * 0.6) {\n return `Likely contradiction (shared terms: ${overlap.join(', ')}): \"${a.raw}\" vs \"${b.raw}\"`;\n }\n\n return null;\n}\n\nfunction hasPositiveDirective(text: string, topic: string): boolean {\n const patterns = [\n new RegExp(`always\\\\s+\\\\w*${topic}`, 'i'),\n new RegExp(`must\\\\s+\\\\w*${topic}`, 'i'),\n new RegExp(`should\\\\s+\\\\w*${topic}`, 'i'),\n new RegExp(`prefer\\\\s+\\\\w*${topic}`, 'i'),\n new RegExp(`use\\\\s+\\\\w*${topic}`, 'i'),\n ];\n return patterns.some((p) => p.test(text));\n}\n\nfunction hasNegativeDirective(text: string, topic: string): boolean {\n const patterns = [\n new RegExp(`never\\\\s+\\\\w*${topic}`, 'i'),\n new RegExp(`avoid\\\\s+\\\\w*${topic}`, 'i'),\n new RegExp(`don'?t\\\\s+\\\\w*${topic}`, 'i'),\n new RegExp(`do not\\\\s+\\\\w*${topic}`, 'i'),\n ];\n return patterns.some((p) => p.test(text));\n}\n\nfunction checkIntraGroupContradictions(\n group: Array<{ doc: HarnessDocument; directives: Directive[]; topics: string[] }>,\n type: string,\n harnessDir: string,\n contradictions: Contradiction[],\n): void {\n for (let i = 0; i < group.length; i++) {\n for (let j = i + 1; j < group.length; j++) {\n const a = group[i];\n const b = group[j];\n\n for (const da of a.directives) {\n for (const db of b.directives) {\n const negation = checkNegation(da, db);\n if (negation) {\n contradictions.push({\n primitiveA: {\n id: a.doc.frontmatter.id,\n path: relative(harnessDir, a.doc.path),\n type,\n text: da.raw,\n },\n primitiveB: {\n id: b.doc.frontmatter.id,\n path: relative(harnessDir, b.doc.path),\n type,\n text: db.raw,\n },\n reason: negation,\n severity: 'medium',\n });\n }\n }\n }\n }\n }\n}\n\n// --- Session Enrichment ---\n\nexport interface SessionEnrichment {\n sessionId: string;\n topics: string[];\n tokenCount: number;\n stepCount: number;\n model: string;\n toolsUsed: string[];\n primitivesReferenced: string[];\n duration: string;\n}\n\nexport interface EnrichmentResult {\n enriched: SessionEnrichment[];\n sessionsScanned: number;\n}\n\n/**\n * Enrich sessions with extracted metadata.\n *\n * Scans session files and extracts:\n * - Topics (from prompt text, frequent nouns, matched primitive IDs)\n * - Token/step counts (from frontmatter or markdown body)\n * - Model used\n * - Tools used (from tool call sections)\n * - Referenced primitives (IDs mentioned in session text)\n * - Duration\n */\nexport function enrichSessions(\n harnessDir: string,\n config?: HarnessConfig,\n options?: { from?: string; to?: string },\n): EnrichmentResult {\n const sessionsDir = join(harnessDir, 'memory', 'sessions');\n if (!existsSync(sessionsDir)) {\n return { enriched: [], sessionsScanned: 0 };\n }\n\n // Load all primitive IDs for cross-reference\n const primitiveIds = new Set<string>();\n const dirs = getPrimitiveDirs(config);\n for (const dir of dirs) {\n const fullPath = join(harnessDir, dir);\n if (!existsSync(fullPath)) continue;\n const { docs } = loadDirectoryWithErrors(fullPath);\n for (const doc of docs) {\n primitiveIds.add(doc.frontmatter.id);\n }\n }\n\n const files = readdirSync(sessionsDir)\n .filter((f) => f.endsWith('.md') && !f.startsWith('.') && !f.startsWith('_'))\n .sort();\n\n // Filter by date range\n const filtered = files.filter((f) => {\n if (!options?.from && !options?.to) return true;\n const dateMatch = f.match(/^(\\d{4}-\\d{2}-\\d{2})/);\n if (!dateMatch) return false;\n const d = dateMatch[1];\n if (options?.from && d < options.from) return false;\n if (options?.to && d > options.to) return false;\n return true;\n });\n\n const enriched: SessionEnrichment[] = [];\n\n for (const file of filtered) {\n const content = readFileSync(join(sessionsDir, file), 'utf-8');\n const sessionId = file.replace(/\\.md$/, '');\n\n const enrichment = enrichSession(content, sessionId, primitiveIds);\n enriched.push(enrichment);\n }\n\n return { enriched, sessionsScanned: filtered.length };\n}\n\nfunction enrichSession(\n content: string,\n sessionId: string,\n primitiveIds: Set<string>,\n): SessionEnrichment {\n // Extract metadata from frontmatter/body\n const tokensMatch = content.match(/[Tt]okens?[:\\s]+(\\d[\\d,]*)/);\n const tokenCount = tokensMatch ? parseInt(tokensMatch[1].replace(/,/g, ''), 10) : 0;\n\n const stepsMatch = content.match(/[Ss]teps?[:\\s]+(\\d+)/);\n const stepCount = stepsMatch ? parseInt(stepsMatch[1], 10) : 0;\n\n const modelMatch = content.match(/[Mm]odel[:\\s]+([^\\n]+)/);\n const model = modelMatch ? modelMatch[1].trim() : 'unknown';\n\n const durationMatch = content.match(/[Dd]uration[:\\s]+([^\\n]+)/);\n const duration = durationMatch ? durationMatch[1].trim() : '';\n\n // Extract tools used from tool call sections\n const toolsUsed: string[] = [];\n const toolMatches = content.matchAll(/### Tool(?:\\s+Call)?:\\s*(\\S+)/g);\n for (const match of toolMatches) {\n const toolName = match[1];\n if (!toolsUsed.includes(toolName)) {\n toolsUsed.push(toolName);\n }\n }\n\n // Also check for tool_calls in frontmatter-style sections\n const toolCallMatches = content.matchAll(/toolName[:\\s]+[\"']?(\\S+)[\"']?/g);\n for (const match of toolCallMatches) {\n const toolName = match[1];\n if (!toolsUsed.includes(toolName)) {\n toolsUsed.push(toolName);\n }\n }\n\n // Find referenced primitives (any primitive ID that appears in text)\n const primitivesReferenced: string[] = [];\n for (const id of primitiveIds) {\n if (id.length < 3) continue; // Skip very short IDs to avoid false positives\n if (content.includes(id)) {\n primitivesReferenced.push(id);\n }\n }\n\n // Extract topics from prompt section\n const topics = extractSessionTopics(content);\n\n return {\n sessionId,\n topics,\n tokenCount,\n stepCount,\n model,\n toolsUsed,\n primitivesReferenced,\n duration,\n };\n}\n\n/**\n * Extract topic keywords from session content.\n * Uses a simple frequency-based approach on meaningful words.\n */\nfunction extractSessionTopics(content: string): string[] {\n // Extract prompt section specifically\n const promptMatch = content.match(/## Prompt\\n([\\s\\S]*?)(?=\\n## |$)/);\n const promptText = promptMatch ? promptMatch[1] : '';\n\n // Also include summary\n const summaryMatch = content.match(/## Summary\\n([\\s\\S]*?)(?=\\n## |$)/);\n const summaryText = summaryMatch ? summaryMatch[1] : '';\n\n const text = (promptText + ' ' + summaryText).toLowerCase();\n\n // Common stop words to filter out\n const stopWords = new Set([\n 'the', 'a', 'an', 'is', 'are', 'was', 'were', 'be', 'been', 'being',\n 'have', 'has', 'had', 'do', 'does', 'did', 'will', 'would', 'could',\n 'should', 'may', 'might', 'shall', 'can', 'need', 'must', 'to', 'of',\n 'in', 'for', 'on', 'with', 'at', 'by', 'from', 'as', 'into', 'about',\n 'like', 'through', 'after', 'before', 'between', 'under', 'during',\n 'and', 'or', 'but', 'not', 'no', 'nor', 'so', 'yet', 'both', 'either',\n 'neither', 'each', 'every', 'all', 'any', 'few', 'more', 'most', 'other',\n 'some', 'such', 'than', 'too', 'very', 'just', 'also', 'this', 'that',\n 'these', 'those', 'it', 'its', 'i', 'me', 'my', 'we', 'our', 'you',\n 'your', 'he', 'she', 'they', 'them', 'their', 'what', 'which', 'who',\n 'when', 'where', 'how', 'why', 'if', 'then', 'else', 'while', 'up',\n 'out', 'off', 'over', 'only', 'own', 'same', 'get', 'got', 'make',\n 'made', 'use', 'used', 'using', 'one', 'two', 'new',\n ]);\n\n // Count word frequencies\n const words = text\n .replace(/[^a-z0-9\\s-]/g, '')\n .split(/\\s+/)\n .filter((w) => w.length > 3 && !stopWords.has(w));\n\n const freq = new Map<string, number>();\n for (const word of words) {\n freq.set(word, (freq.get(word) ?? 0) + 1);\n }\n\n // Return top 5 most frequent meaningful words\n return Array.from(freq.entries())\n .filter(([, count]) => count >= 1)\n .sort((a, b) => b[1] - a[1])\n .slice(0, 5)\n .map(([word]) => word);\n}\n\n// --- Capability Suggestions ---\n\nexport interface CapabilitySuggestion {\n topic: string;\n frequency: number;\n sessionDates: string[];\n suggestion: string;\n suggestedType: 'skill' | 'playbook';\n}\n\nexport interface CapabilitySuggestionResult {\n suggestions: CapabilitySuggestion[];\n topicsAnalyzed: number;\n sessionsScanned: number;\n}\n\n/**\n * Suggest capabilities (skills/playbooks) for frequent session topics\n * that don't have existing coverage.\n *\n * Scans sessions for recurring topics, cross-references against existing\n * skills/playbooks, and suggests new ones for uncovered topics.\n */\nexport function suggestCapabilities(\n harnessDir: string,\n config?: HarnessConfig,\n options?: { minFrequency?: number },\n): CapabilitySuggestionResult {\n const minFrequency = options?.minFrequency ?? 3;\n\n // Enrich sessions to get topics\n const { enriched, sessionsScanned } = enrichSessions(harnessDir, config);\n\n // Collect topic frequency across sessions\n const topicOccurrences = new Map<string, Set<string>>();\n for (const session of enriched) {\n const dateMatch = session.sessionId.match(/^(\\d{4}-\\d{2}-\\d{2})/);\n const date = dateMatch ? dateMatch[1] : session.sessionId;\n\n for (const topic of session.topics) {\n if (!topicOccurrences.has(topic)) {\n topicOccurrences.set(topic, new Set());\n }\n topicOccurrences.get(topic)!.add(date);\n }\n }\n\n // Load existing skills and playbooks\n const coveredTopics = new Set<string>();\n const skillsDir = join(harnessDir, 'skills');\n const playbooksDir = join(harnessDir, 'playbooks');\n\n for (const dir of [skillsDir, playbooksDir]) {\n if (!existsSync(dir)) continue;\n const docs = loadDirectory(dir);\n for (const doc of docs) {\n // Add ID parts as covered topics\n for (const part of doc.frontmatter.id.split('-')) {\n if (part.length > 2) coveredTopics.add(part.toLowerCase());\n }\n // Add tags as covered topics\n for (const tag of doc.frontmatter.tags) {\n coveredTopics.add(tag.toLowerCase());\n }\n }\n }\n\n // Find frequent uncovered topics\n const suggestions: CapabilitySuggestion[] = [];\n\n for (const [topic, dates] of topicOccurrences) {\n if (dates.size < minFrequency) continue;\n if (coveredTopics.has(topic)) continue;\n\n const suggestedType = dates.size >= 5 ? 'playbook' : 'skill';\n suggestions.push({\n topic,\n frequency: dates.size,\n sessionDates: [...dates].sort(),\n suggestion: `Create a ${suggestedType} for \"${topic}\" — appeared in ${dates.size} session(s)`,\n suggestedType,\n });\n }\n\n // Sort by frequency\n suggestions.sort((a, b) => b.frequency - a.frequency);\n\n return {\n suggestions,\n topicsAnalyzed: topicOccurrences.size,\n sessionsScanned,\n };\n}\n\n// --- Failure Taxonomy ---\n\n/**\n * Named failure modes with recovery strategies.\n * Based on common agent failure patterns (context overflow, tool errors,\n * budget exhaustion, hallucination, stale primitives, circular delegation).\n */\nexport type FailureMode =\n | 'context_overflow'\n | 'tool_execution_error'\n | 'budget_exhausted'\n | 'rate_limited'\n | 'llm_timeout'\n | 'llm_error'\n | 'hallucination_detected'\n | 'stale_primitive'\n | 'circular_delegation'\n | 'missing_dependency'\n | 'parse_error'\n | 'config_invalid'\n | 'mcp_connection_failed'\n | 'state_corruption'\n | 'unknown';\n\nexport interface FailureRecord {\n mode: FailureMode;\n timestamp: string;\n sessionId?: string;\n message: string;\n context?: Record<string, unknown>;\n recoveryAttempted?: string;\n recovered: boolean;\n}\n\nexport interface FailureTaxonomy {\n modes: Record<FailureMode, {\n description: string;\n severity: 'low' | 'medium' | 'high' | 'critical';\n recoveryStrategies: string[];\n autoRecoverable: boolean;\n }>;\n}\n\n/**\n * The canonical failure taxonomy for agent-harness.\n * Each mode has a description, severity level, recovery strategies,\n * and whether automatic recovery is possible.\n */\nexport const FAILURE_TAXONOMY: FailureTaxonomy = {\n modes: {\n context_overflow: {\n description: 'System prompt + conversation exceeds model context window',\n severity: 'high',\n recoveryStrategies: [\n 'Trim oldest messages from conversation history',\n 'Reduce primitive loading level (L2 → L1 → L0)',\n 'Archive old sessions to free memory budget',\n 'Split into sub-conversations with summarized context',\n ],\n autoRecoverable: true,\n },\n tool_execution_error: {\n description: 'An MCP or HTTP tool call failed during execution',\n severity: 'medium',\n recoveryStrategies: [\n 'Retry with exponential backoff',\n 'Fall back to alternative tool if available',\n 'Report error to LLM and ask for alternative approach',\n 'Skip tool and proceed with available context',\n ],\n autoRecoverable: true,\n },\n budget_exhausted: {\n description: 'Daily or monthly spending limit has been reached',\n severity: 'critical',\n recoveryStrategies: [\n 'Wait until next budget period',\n 'Switch to cheaper model (fast_model or summary_model)',\n 'Queue non-urgent tasks for later execution',\n 'Alert operator to increase budget',\n ],\n autoRecoverable: false,\n },\n rate_limited: {\n description: 'LLM API rate limit hit (per-minute/hour/day)',\n severity: 'medium',\n recoveryStrategies: [\n 'Wait for retryAfterMs from rate limiter',\n 'Reduce request frequency',\n 'Queue and batch requests',\n ],\n autoRecoverable: true,\n },\n llm_timeout: {\n description: 'LLM API call timed out without response',\n severity: 'medium',\n recoveryStrategies: [\n 'Retry with same prompt',\n 'Retry with shorter prompt (reduce context)',\n 'Switch to faster model',\n 'Increase timeout_ms in config',\n ],\n autoRecoverable: true,\n },\n llm_error: {\n description: 'LLM API returned an error response (4xx/5xx)',\n severity: 'high',\n recoveryStrategies: [\n 'Retry with exponential backoff (max_retries in config)',\n 'Switch to fallback model',\n 'Check API key validity',\n 'Log error details for debugging',\n ],\n autoRecoverable: true,\n },\n hallucination_detected: {\n description: 'LLM output contains fabricated facts or references to non-existent primitives',\n severity: 'medium',\n recoveryStrategies: [\n 'Re-prompt with explicit grounding: \"Only reference primitives that exist\"',\n 'Validate output against known primitive IDs',\n 'Add validation step before acting on LLM output',\n 'Record in journal for future training',\n ],\n autoRecoverable: false,\n },\n stale_primitive: {\n description: 'A referenced primitive is outdated, deprecated, or orphaned',\n severity: 'low',\n recoveryStrategies: [\n 'Run detectDeadPrimitives() to identify stale files',\n 'Archive deprecated primitives',\n 'Update references to point to current versions',\n 'Auto-flag via validator',\n ],\n autoRecoverable: true,\n },\n circular_delegation: {\n description: 'Agent delegation loop detected (A delegates to B delegates to A)',\n severity: 'high',\n recoveryStrategies: [\n 'Track delegation chain and break on cycle detection',\n 'Set max delegation depth (default: 3)',\n 'Return partial result from last agent in chain',\n 'Log delegation graph for debugging',\n ],\n autoRecoverable: true,\n },\n missing_dependency: {\n description: 'A required dependency (primitive, MCP server, API key) is missing',\n severity: 'high',\n recoveryStrategies: [\n 'Run doctorHarness() to auto-fix missing files',\n 'Check .env for required API keys',\n 'Install missing MCP servers',\n 'Prompt user to install missing bundle',\n ],\n autoRecoverable: false,\n },\n parse_error: {\n description: 'A primitive file has invalid YAML frontmatter or malformed content',\n severity: 'medium',\n recoveryStrategies: [\n 'Run fixCapability() to auto-repair frontmatter',\n 'Skip the malformed file and log a warning',\n 'Use default frontmatter values',\n 'Report to user for manual fix',\n ],\n autoRecoverable: true,\n },\n config_invalid: {\n description: 'config.yaml fails schema validation',\n severity: 'critical',\n recoveryStrategies: [\n 'Fall back to CONFIG_DEFAULTS',\n 'Report specific validation errors to user',\n 'Run harness doctor to attempt repair',\n ],\n autoRecoverable: false,\n },\n mcp_connection_failed: {\n description: 'Failed to connect to an MCP server (process spawn or HTTP)',\n severity: 'medium',\n recoveryStrategies: [\n 'Retry connection with backoff',\n 'Disable the server and continue without its tools',\n 'Check command/URL/env configuration',\n 'Fall back to built-in tools only',\n ],\n autoRecoverable: true,\n },\n state_corruption: {\n description: 'state.md is unreadable or contains invalid data',\n severity: 'high',\n recoveryStrategies: [\n 'Fall back to DEFAULT_STATE',\n 'Rebuild state from session history',\n 'Reset state.md and log the event',\n ],\n autoRecoverable: true,\n },\n unknown: {\n description: 'An unclassified error occurred',\n severity: 'high',\n recoveryStrategies: [\n 'Log full error with stack trace',\n 'Record in health.json failure counter',\n 'Alert operator',\n 'Graceful shutdown if critical path',\n ],\n autoRecoverable: false,\n },\n },\n};\n\nexport interface FailureAnalysis {\n recentFailures: FailureRecord[];\n modeFrequency: Record<string, number>;\n mostCommonMode: FailureMode | null;\n suggestedRecovery: string[];\n healthImplication: 'healthy' | 'degraded' | 'unhealthy';\n}\n\n/**\n * Classify an error into a failure mode.\n */\nexport function classifyFailure(error: Error | string, context?: Record<string, unknown>): FailureMode {\n const msg = typeof error === 'string' ? error.toLowerCase() : error.message.toLowerCase();\n\n if (msg.includes('context') && (msg.includes('overflow') || msg.includes('too long') || msg.includes('exceed'))) {\n return 'context_overflow';\n }\n if (msg.includes('tool') && (msg.includes('fail') || msg.includes('error') || msg.includes('timeout'))) {\n return 'tool_execution_error';\n }\n if (msg.includes('budget') || msg.includes('spending') || msg.includes('limit exceeded')) {\n return 'budget_exhausted';\n }\n if (msg.includes('rate limit') || msg.includes('429') || msg.includes('too many requests')) {\n return 'rate_limited';\n }\n if (msg.includes('timeout') || msg.includes('timed out') || msg.includes('ETIMEDOUT')) {\n return 'llm_timeout';\n }\n if (msg.includes('mcp') && (msg.includes('connect') || msg.includes('spawn') || msg.includes('failed'))) {\n return 'mcp_connection_failed';\n }\n if (msg.includes('parse') || msg.includes('yaml') || msg.includes('frontmatter') || msg.includes('malformed')) {\n return 'parse_error';\n }\n if (msg.includes('config') && (msg.includes('invalid') || msg.includes('validation'))) {\n return 'config_invalid';\n }\n if (msg.includes('state') && (msg.includes('corrupt') || msg.includes('invalid') || msg.includes('unreadable'))) {\n return 'state_corruption';\n }\n if (msg.includes('circular') || msg.includes('delegation loop') || msg.includes('cycle')) {\n return 'circular_delegation';\n }\n if (msg.includes('missing') || msg.includes('not found') || msg.includes('dependency')) {\n return 'missing_dependency';\n }\n if (msg.includes('401') || msg.includes('403') || msg.includes('500') || msg.includes('502') || msg.includes('503')) {\n return 'llm_error';\n }\n\n return 'unknown';\n}\n\n/**\n * Get recovery strategies for a failure mode.\n */\nexport function getRecoveryStrategies(mode: FailureMode): string[] {\n return FAILURE_TAXONOMY.modes[mode]?.recoveryStrategies ?? ['Log error and alert operator'];\n}\n\n/**\n * Analyze failure patterns from session history and health data.\n * Returns frequency analysis and recovery suggestions.\n */\nexport function analyzeFailures(\n harnessDir: string,\n options?: { days?: number },\n): FailureAnalysis {\n const days = options?.days ?? 7;\n const now = Date.now();\n const cutoffMs = days * 24 * 60 * 60 * 1000;\n\n const recentFailures: FailureRecord[] = [];\n\n // Scan health.json for failures\n const healthPath = join(harnessDir, 'memory', 'health.json');\n if (existsSync(healthPath)) {\n try {\n const health = JSON.parse(readFileSync(healthPath, 'utf-8'));\n if (health.lastError) {\n const mode = classifyFailure(health.lastError);\n recentFailures.push({\n mode,\n timestamp: health.lastFailure || new Date().toISOString(),\n message: health.lastError,\n recovered: health.consecutiveFailures === 0,\n });\n }\n } catch {\n // Malformed health.json\n }\n }\n\n // Scan sessions for error indicators\n const sessionsDir = join(harnessDir, 'memory', 'sessions');\n if (existsSync(sessionsDir)) {\n const files = readdirSync(sessionsDir)\n .filter((f) => f.endsWith('.md') && !f.startsWith('.'))\n .sort()\n .reverse();\n\n for (const file of files) {\n const dateMatch = file.match(/^(\\d{4}-\\d{2}-\\d{2})/);\n if (dateMatch) {\n const fileDate = new Date(dateMatch[1]).getTime();\n if (now - fileDate > cutoffMs) break;\n }\n\n try {\n const content = readFileSync(join(sessionsDir, file), 'utf-8');\n // Look for error patterns in session content\n const errorLines = content.split('\\n').filter((l) =>\n l.toLowerCase().includes('error') ||\n l.toLowerCase().includes('failed') ||\n l.toLowerCase().includes('timeout'),\n );\n\n for (const line of errorLines.slice(0, 3)) {\n const mode = classifyFailure(line);\n if (mode !== 'unknown') {\n recentFailures.push({\n mode,\n timestamp: dateMatch?.[1] ?? 'unknown',\n sessionId: file.replace('.md', ''),\n message: line.trim().slice(0, 200),\n recovered: true,\n });\n }\n }\n } catch {\n // Skip unreadable sessions\n }\n }\n }\n\n // Calculate frequency\n const modeFrequency: Record<string, number> = {};\n for (const f of recentFailures) {\n modeFrequency[f.mode] = (modeFrequency[f.mode] ?? 0) + 1;\n }\n\n // Find most common mode\n let mostCommonMode: FailureMode | null = null;\n let maxFreq = 0;\n for (const [mode, count] of Object.entries(modeFrequency)) {\n if (count > maxFreq) {\n maxFreq = count;\n mostCommonMode = mode as FailureMode;\n }\n }\n\n // Suggest recovery\n const suggestedRecovery = mostCommonMode\n ? getRecoveryStrategies(mostCommonMode)\n : [];\n\n // Determine health implication\n let healthImplication: 'healthy' | 'degraded' | 'unhealthy' = 'healthy';\n if (recentFailures.length > 5) {\n healthImplication = 'unhealthy';\n } else if (recentFailures.length > 0) {\n healthImplication = 'degraded';\n }\n\n return {\n recentFailures: recentFailures.slice(0, 20),\n modeFrequency,\n mostCommonMode,\n suggestedRecovery,\n healthImplication,\n };\n}\n\n// --- Verification Gates ---\n\nexport type GateStatus = 'pass' | 'fail' | 'warn' | 'skip';\n\nexport interface GateCheck {\n name: string;\n description: string;\n status: GateStatus;\n message: string;\n details?: Record<string, unknown>;\n}\n\nexport interface VerificationGateResult {\n gateName: string;\n passed: boolean;\n checks: GateCheck[];\n summary: string;\n}\n\nexport type GateDefinition = {\n name: string;\n description: string;\n check: (harnessDir: string, config?: HarnessConfig) => GateCheck[];\n};\n\n/**\n * Built-in verification gates for the harness.\n * Each gate is a set of checks that must pass at a specific stage.\n */\nexport const BUILTIN_GATES: GateDefinition[] = [\n {\n name: 'pre-boot',\n description: 'Checks before agent boot: config valid, CORE.md exists, API key available',\n check: (harnessDir: string) => {\n const checks: GateCheck[] = [];\n\n // CORE.md exists\n checks.push(existsSync(join(harnessDir, 'CORE.md'))\n ? { name: 'core-md', description: 'CORE.md exists', status: 'pass', message: 'CORE.md present' }\n : { name: 'core-md', description: 'CORE.md exists', status: 'fail', message: 'Missing CORE.md — required for agent identity' });\n\n // Config valid\n try {\n loadConfig(harnessDir);\n checks.push({ name: 'config-valid', description: 'config.yaml valid', status: 'pass', message: 'Config parsed successfully' });\n } catch (err) {\n checks.push({ name: 'config-valid', description: 'config.yaml valid', status: 'fail', message: `Config error: ${err instanceof Error ? err.message : String(err)}` });\n }\n\n // API key available\n const hasKey = !!(process.env.OPENROUTER_API_KEY || process.env.OPENAI_API_KEY || process.env.ANTHROPIC_API_KEY);\n checks.push(hasKey\n ? { name: 'api-key', description: 'API key available', status: 'pass', message: 'API key found in environment' }\n : { name: 'api-key', description: 'API key available', status: 'warn', message: 'No API key in environment — will need --api-key flag' });\n\n // Memory directory\n const memDir = join(harnessDir, 'memory');\n checks.push(existsSync(memDir)\n ? { name: 'memory-dir', description: 'Memory directory exists', status: 'pass', message: 'memory/ directory present' }\n : { name: 'memory-dir', description: 'Memory directory exists', status: 'warn', message: 'memory/ directory missing — will be created on first run' });\n\n return checks;\n },\n },\n {\n name: 'pre-run',\n description: 'Checks before each LLM call: budget, rate limits, context budget',\n check: (harnessDir: string) => {\n const checks: GateCheck[] = [];\n\n let config: HarnessConfig;\n try {\n config = loadConfig(harnessDir);\n } catch {\n checks.push({ name: 'config-load', description: 'Config loadable', status: 'fail', message: 'Cannot load config' });\n return checks;\n }\n\n // Budget check\n try {\n const budgetStatus = checkBudget(harnessDir, config.budget);\n const exceeded = (budgetStatus.daily_remaining_usd !== null && budgetStatus.daily_remaining_usd <= 0) ||\n (budgetStatus.monthly_remaining_usd !== null && budgetStatus.monthly_remaining_usd <= 0);\n if (exceeded) {\n checks.push({ name: 'budget', description: 'Budget not exceeded', status: 'fail', message: 'Budget limit exceeded', details: { ...budgetStatus } });\n } else {\n checks.push({ name: 'budget', description: 'Budget not exceeded', status: 'pass', message: 'Within budget' });\n }\n } catch (err) {\n checks.push({ name: 'budget', description: 'Budget not exceeded', status: 'skip', message: `Budget check unavailable: ${err instanceof Error ? err.message : String(err)}` });\n }\n\n // Rate limit check\n try {\n const limits = buildRateLimits(config);\n if (limits.length === 0) {\n checks.push({ name: 'rate-limit', description: 'Rate limit not hit', status: 'pass', message: 'No rate limits configured' });\n } else {\n let blocked = false;\n for (const limit of limits) {\n const rateCheck = checkRateLimit(harnessDir, limit);\n if (!rateCheck.allowed) {\n const windowLabel = limit.window_ms <= 60_000 ? 'minute' : limit.window_ms <= 3_600_000 ? 'hour' : 'day';\n checks.push({ name: 'rate-limit', description: 'Rate limit not hit', status: 'fail', message: `Rate limited (${windowLabel}): ${rateCheck.current}/${rateCheck.max}. Retry after ${Math.ceil(rateCheck.retry_after_ms / 1000)}s` });\n blocked = true;\n break;\n }\n }\n if (!blocked) {\n checks.push({ name: 'rate-limit', description: 'Rate limit not hit', status: 'pass', message: 'Within rate limits' });\n }\n }\n } catch (err) {\n checks.push({ name: 'rate-limit', description: 'Rate limit not hit', status: 'skip', message: `Rate limit check unavailable: ${err instanceof Error ? err.message : String(err)}` });\n }\n\n // Health check\n const healthPath = join(harnessDir, 'memory', 'health.json');\n if (existsSync(healthPath)) {\n try {\n const health = JSON.parse(readFileSync(healthPath, 'utf-8'));\n if (health.consecutiveFailures >= 3) {\n checks.push({ name: 'health', description: 'Agent healthy', status: 'warn', message: `${health.consecutiveFailures} consecutive failures detected` });\n } else {\n checks.push({ name: 'health', description: 'Agent healthy', status: 'pass', message: 'No recent failure pattern' });\n }\n } catch {\n checks.push({ name: 'health', description: 'Agent healthy', status: 'skip', message: 'Health data unavailable' });\n }\n }\n\n return checks;\n },\n },\n {\n name: 'post-session',\n description: 'Checks after a session: session recorded, no parse errors, primitives intact',\n check: (harnessDir: string) => {\n const checks: GateCheck[] = [];\n\n // Sessions directory exists and has files\n const sessionsDir = join(harnessDir, 'memory', 'sessions');\n if (existsSync(sessionsDir)) {\n const files = readdirSync(sessionsDir).filter((f) => f.endsWith('.md') && !f.startsWith('.'));\n checks.push({\n name: 'sessions-recorded',\n description: 'Sessions being recorded',\n status: files.length > 0 ? 'pass' : 'warn',\n message: `${files.length} session file(s) in memory`,\n });\n } else {\n checks.push({ name: 'sessions-recorded', description: 'Sessions being recorded', status: 'warn', message: 'No sessions directory' });\n }\n\n // Check for parse errors in primitives\n const dirs = getPrimitiveDirs();\n let totalParseErrors = 0;\n for (const dir of dirs) {\n const fullPath = join(harnessDir, dir);\n if (!existsSync(fullPath)) continue;\n const { errors } = loadDirectoryWithErrors(fullPath);\n totalParseErrors += errors.length;\n }\n\n checks.push(totalParseErrors === 0\n ? { name: 'parse-errors', description: 'No primitive parse errors', status: 'pass', message: 'All primitives parse cleanly' }\n : { name: 'parse-errors', description: 'No primitive parse errors', status: 'warn', message: `${totalParseErrors} parse error(s) in primitives` });\n\n return checks;\n },\n },\n {\n name: 'pre-deploy',\n description: 'Checks before deployment: validator passes, no dead primitives, no contradictions',\n check: (harnessDir: string, config?: HarnessConfig) => {\n const checks: GateCheck[] = [];\n\n // Run validator\n try {\n const validation = validateHarness(harnessDir);\n if (validation.errors.length > 0) {\n checks.push({ name: 'validator', description: 'Validator passes', status: 'fail', message: `${validation.errors.length} error(s)`, details: { errors: validation.errors } });\n } else if (validation.warnings.length > 0) {\n checks.push({ name: 'validator', description: 'Validator passes', status: 'warn', message: `${validation.warnings.length} warning(s)` });\n } else {\n checks.push({ name: 'validator', description: 'Validator passes', status: 'pass', message: 'Validation clean' });\n }\n } catch (err) {\n checks.push({ name: 'validator', description: 'Validator passes', status: 'fail', message: `Validator error: ${err instanceof Error ? err.message : String(err)}` });\n }\n\n // Check for dead primitives\n const deadResult = detectDeadPrimitives(harnessDir, config);\n checks.push(deadResult.dead.length === 0\n ? { name: 'dead-primitives', description: 'No dead primitives', status: 'pass', message: 'All primitives referenced or recently modified' }\n : { name: 'dead-primitives', description: 'No dead primitives', status: 'warn', message: `${deadResult.dead.length} dead primitive(s) found`, details: { dead: deadResult.dead.map((d) => d.id) } });\n\n // Check for contradictions\n const contradictionResult = detectContradictions(harnessDir);\n checks.push(contradictionResult.contradictions.length === 0\n ? { name: 'contradictions', description: 'No contradictions', status: 'pass', message: 'No conflicting rules/instincts' }\n : { name: 'contradictions', description: 'No contradictions', status: 'warn', message: `${contradictionResult.contradictions.length} potential contradiction(s)` });\n\n return checks;\n },\n },\n];\n\n/**\n * Run a verification gate by name.\n * Returns all check results and an overall pass/fail status.\n */\nexport function runGate(\n gateName: string,\n harnessDir: string,\n config?: HarnessConfig,\n): VerificationGateResult {\n const gate = BUILTIN_GATES.find((g) => g.name === gateName);\n if (!gate) {\n return {\n gateName,\n passed: false,\n checks: [{ name: 'gate-not-found', description: 'Gate exists', status: 'fail', message: `Unknown gate: ${gateName}` }],\n summary: `Gate \"${gateName}\" not found. Available: ${BUILTIN_GATES.map((g) => g.name).join(', ')}`,\n };\n }\n\n const checks = gate.check(harnessDir, config);\n const hasFails = checks.some((c) => c.status === 'fail');\n\n const passed = !hasFails;\n const passCount = checks.filter((c) => c.status === 'pass').length;\n const failCount = checks.filter((c) => c.status === 'fail').length;\n const warnCount = checks.filter((c) => c.status === 'warn').length;\n\n let summary = `${gate.name}: ${passCount} passed`;\n if (failCount > 0) summary += `, ${failCount} failed`;\n if (warnCount > 0) summary += `, ${warnCount} warnings`;\n\n return { gateName, passed, checks, summary };\n}\n\n/**\n * Run all built-in verification gates.\n */\nexport function runAllGates(\n harnessDir: string,\n config?: HarnessConfig,\n): VerificationGateResult[] {\n return BUILTIN_GATES.map((gate) => runGate(gate.name, harnessDir, config));\n}\n\n/**\n * List available gate names and descriptions.\n */\nexport function listGates(): Array<{ name: string; description: string }> {\n return BUILTIN_GATES.map((g) => ({ name: g.name, description: g.description }));\n}\n"],"mappings":"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","names":[]}
@@ -7,12 +7,11 @@ import {
7
7
  parseJournalSynthesis,
8
8
  synthesizeJournal,
9
9
  synthesizeJournalRange
10
- } from "./chunk-CSL3ERUI.js";
11
- import "./chunk-UPLBF4RZ.js";
10
+ } from "./chunk-4P6TRFPZ.js";
11
+ import "./chunk-2UVWCTAY.js";
12
12
  import "./chunk-IZ6UZ3ZL.js";
13
- import "./chunk-CHJ5GNZC.js";
14
- import "./chunk-4CWAGBNS.js";
15
- import "./chunk-ZZJOFKAT.js";
13
+ import "./chunk-EC42HQQH.js";
14
+ import "./chunk-4TQQZILG.js";
16
15
  export {
17
16
  compressJournals,
18
17
  listJournals,
@@ -21,4 +20,4 @@ export {
21
20
  synthesizeJournal,
22
21
  synthesizeJournalRange
23
22
  };
24
- //# sourceMappingURL=journal-WANJL3MI.js.map
23
+ //# sourceMappingURL=journal-ITUMKT6U.js.map
@@ -8,9 +8,8 @@ import {
8
8
  loadDirectory,
9
9
  loadDirectoryWithErrors,
10
10
  parseHarnessDocument
11
- } from "./chunk-UPLBF4RZ.js";
12
- import "./chunk-4CWAGBNS.js";
13
- import "./chunk-ZZJOFKAT.js";
11
+ } from "./chunk-2UVWCTAY.js";
12
+ import "./chunk-4TQQZILG.js";
14
13
  export {
15
14
  estimateTokens,
16
15
  getAtLevel,
@@ -20,4 +19,4 @@ export {
20
19
  loadDirectoryWithErrors,
21
20
  parseHarnessDocument
22
21
  };
23
- //# sourceMappingURL=loader-C3TKIKZR.js.map
22
+ //# sourceMappingURL=loader-27PLDCOJ.js.map
@@ -6,10 +6,9 @@ import {
6
6
  validateMcpConfig
7
7
  } from "./chunk-5H34JPMB.js";
8
8
  import "./chunk-BSKDOFRT.js";
9
- import "./chunk-ZZJOFKAT.js";
10
9
  export {
11
10
  createMcpManager,
12
11
  loadMcpTools,
13
12
  validateMcpConfig
14
13
  };
15
- //# sourceMappingURL=mcp-WTQJJZAO.js.map
14
+ //# sourceMappingURL=mcp-JSIUJJZV.js.map
@@ -1,6 +1,5 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- import "./chunk-ZZJOFKAT.js";
4
3
 
5
4
  // src/runtime/mcp-discovery.ts
6
5
  import { existsSync, readFileSync } from "fs";
@@ -374,4 +373,4 @@ export {
374
373
  filterUnsafeServers,
375
374
  getScannedTools
376
375
  };
377
- //# sourceMappingURL=mcp-discovery-WPAQFL6S.js.map
376
+ //# sourceMappingURL=mcp-discovery-DG3RQYLM.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/runtime/mcp-discovery.ts"],"sourcesContent":["import { existsSync, readFileSync } from 'fs';\nimport { join } from 'path';\nimport { homedir, platform } from 'os';\n\n// --- Types ---\n\n/** A discovered MCP server from an external tool's config */\nexport interface DiscoveredMcpServer {\n /** Server name (key from the source config) */\n name: string;\n /** Transport type inferred from config */\n transport: 'stdio' | 'http' | 'sse';\n /** Command for stdio transport */\n command?: string;\n /** Args for stdio transport */\n args?: string[];\n /** Environment variables */\n env?: Record<string, string>;\n /** Working directory */\n cwd?: string;\n /** URL for http/sse transport */\n url?: string;\n /** Headers for http/sse transport */\n headers?: Record<string, string>;\n}\n\n/** Result of scanning a single tool's config */\nexport interface DiscoverySource {\n /** Tool name (e.g. \"Claude Desktop\", \"Cursor\") */\n tool: string;\n /** Config file path that was scanned */\n configPath: string;\n /** Whether the config file exists */\n found: boolean;\n /** Servers discovered from this tool */\n servers: DiscoveredMcpServer[];\n /** Error encountered while reading/parsing */\n error?: string;\n}\n\n/** Aggregated discovery results */\nexport interface DiscoveryResult {\n /** All sources scanned */\n sources: DiscoverySource[];\n /** Deduplicated servers (by name, preferring first seen) */\n servers: DiscoveredMcpServer[];\n /** Total sources that had config files */\n sourcesFound: number;\n /** Total unique servers discovered */\n totalServers: number;\n}\n\n// --- Known tool config locations ---\n\ninterface ToolConfig {\n tool: string;\n /** Function returning config file path for the current platform */\n path: () => string;\n /** JSON key containing server definitions */\n rootKey: string;\n /** Whether servers use explicit 'type' field instead of inferring transport */\n usesTypeField?: boolean;\n /** Custom extraction for non-standard config structures. Returns merged server map. */\n extractServers?: (root: Record<string, unknown>) => Record<string, unknown>;\n}\n\nfunction vscodeGlobalStoragePath(h: string, mac: boolean, extensionId: string): string {\n if (mac) {\n return join(h, 'Library', 'Application Support', 'Code', 'User', 'globalStorage', extensionId);\n }\n return join(h, '.config', 'Code', 'User', 'globalStorage', extensionId);\n}\n\n/** Claude Code extractServers — handles nested projects.<path>.mcpServers */\nfunction extractClaudeCodeServers(root: Record<string, unknown>): Record<string, unknown> {\n const merged: Record<string, unknown> = {};\n // Check top-level mcpServers\n const topLevel = root.mcpServers;\n if (topLevel && typeof topLevel === 'object' && !Array.isArray(topLevel)) {\n Object.assign(merged, topLevel);\n }\n // Check per-project mcpServers under projects.<path>.mcpServers\n const projects = root.projects;\n if (projects && typeof projects === 'object' && !Array.isArray(projects)) {\n for (const projectData of Object.values(projects as Record<string, unknown>)) {\n if (!projectData || typeof projectData !== 'object' || Array.isArray(projectData)) continue;\n const proj = projectData as Record<string, unknown>;\n const projServers = proj.mcpServers;\n if (projServers && typeof projServers === 'object' && !Array.isArray(projServers)) {\n for (const [name, config] of Object.entries(projServers as Record<string, unknown>)) {\n if (!(name in merged)) {\n merged[name] = config;\n }\n }\n }\n }\n }\n return merged;\n}\n\n/**\n * Build the list of tool configs for the given home directory and platform.\n * Evaluated lazily so tests can control the home/platform values.\n */\nfunction buildToolConfigs(h: string, mac: boolean): ToolConfig[] {\n return [\n {\n tool: 'Claude Desktop',\n path: () => mac\n ? join(h, 'Library', 'Application Support', 'Claude', 'claude_desktop_config.json')\n : join(h, '.config', 'Claude', 'claude_desktop_config.json'),\n rootKey: 'mcpServers',\n },\n {\n tool: 'Claude Code',\n path: () => join(h, '.claude.json'),\n rootKey: 'mcpServers',\n usesTypeField: true,\n extractServers: extractClaudeCodeServers,\n },\n {\n tool: 'Cursor',\n path: () => join(h, '.cursor', 'mcp.json'),\n rootKey: 'mcpServers',\n },\n {\n tool: 'Windsurf',\n path: () => join(h, '.codeium', 'windsurf', 'mcp_config.json'),\n rootKey: 'mcpServers',\n },\n {\n tool: 'Copilot CLI',\n path: () => join(h, '.copilot', 'mcp-config.json'),\n rootKey: 'mcpServers',\n },\n {\n tool: 'Cline',\n path: () => join(vscodeGlobalStoragePath(h, mac, 'saoudrizwan.claude-dev'), 'settings', 'cline_mcp_settings.json'),\n rootKey: 'mcpServers',\n },\n {\n tool: 'Roo Code',\n path: () => join(vscodeGlobalStoragePath(h, mac, 'rooveterinaryinc.roo-cline'), 'settings', 'mcp_settings.json'),\n rootKey: 'mcpServers',\n },\n {\n tool: 'VS Code',\n path: () => mac\n ? join(h, 'Library', 'Application Support', 'Code', 'User', 'mcp.json')\n : join(h, '.config', 'Code', 'User', 'mcp.json'),\n rootKey: 'servers',\n usesTypeField: true,\n },\n {\n tool: 'Zed',\n path: () => join(h, '.config', 'zed', 'settings.json'),\n rootKey: 'context_servers',\n },\n ];\n}\n\n// --- Parsing ---\n\n/**\n * Infer transport type from a server config object.\n * Most tools use 'command' = stdio, 'url' = http/sse.\n * VS Code uses explicit 'type' field.\n */\nfunction inferTransport(\n serverObj: Record<string, unknown>,\n usesTypeField?: boolean,\n): 'stdio' | 'http' | 'sse' {\n if (usesTypeField && typeof serverObj.type === 'string') {\n const t = serverObj.type.toLowerCase();\n if (t === 'sse') return 'sse';\n if (t === 'http') return 'http';\n if (t === 'stdio') return 'stdio';\n }\n\n if (typeof serverObj.command === 'string') return 'stdio';\n if (typeof serverObj.url === 'string' || typeof serverObj.serverUrl === 'string') {\n // If the URL looks like an SSE endpoint\n const url = (serverObj.url ?? serverObj.serverUrl) as string;\n if (url.includes('/sse') || serverObj.type === 'sse') return 'sse';\n return 'http';\n }\n\n // Default to stdio if we can't determine\n return 'stdio';\n}\n\n/**\n * Parse a server config object into a DiscoveredMcpServer.\n */\nfunction parseServer(\n name: string,\n serverObj: Record<string, unknown>,\n usesTypeField?: boolean,\n): DiscoveredMcpServer {\n const transport = inferTransport(serverObj, usesTypeField);\n const server: DiscoveredMcpServer = { name, transport };\n\n if (transport === 'stdio') {\n if (typeof serverObj.command === 'string') server.command = serverObj.command;\n if (Array.isArray(serverObj.args)) {\n server.args = serverObj.args\n .filter((a): a is string => typeof a === 'string')\n .map((a) => redactArgValue(a));\n }\n if (serverObj.env && typeof serverObj.env === 'object' && !Array.isArray(serverObj.env)) {\n server.env = redactEnv(filterStringRecord(serverObj.env as Record<string, unknown>));\n }\n if (typeof serverObj.cwd === 'string') server.cwd = serverObj.cwd;\n } else {\n const url = serverObj.url ?? serverObj.serverUrl;\n if (typeof url === 'string') server.url = url;\n if (serverObj.headers && typeof serverObj.headers === 'object' && !Array.isArray(serverObj.headers)) {\n // Redact auth headers\n const headers = filterStringRecord(serverObj.headers as Record<string, unknown>);\n for (const [k, v] of Object.entries(headers)) {\n if (SECRET_PATTERNS.test(k) || SECRET_PATTERNS.test(v)) {\n headers[k] = `\\${${k.toUpperCase().replace(/[^A-Z0-9]/g, '_')}}`;\n }\n }\n server.headers = headers;\n }\n }\n\n return server;\n}\n\n/** Filter an object to only string values */\nfunction filterStringRecord(obj: Record<string, unknown>): Record<string, string> {\n const result: Record<string, string> = {};\n for (const [k, v] of Object.entries(obj)) {\n if (typeof v === 'string') result[k] = v;\n }\n return result;\n}\n\n/** Patterns that indicate a secret value in env vars */\nconst SECRET_PATTERNS = /(?:api[_-]?key|secret|token|password|bearer|auth)/i;\n/** Patterns for non-secret env vars that are always safe to copy */\nconst SAFE_ENV_KEYS = /^(?:PATH|HOME|NODE_ENV|NODE_OPTIONS|SHELL|LANG|LC_\\w+|TZ|TERM|EDITOR)$/;\n\n/** Pattern for Bearer tokens or similar auth values in args */\nconst BEARER_PATTERN = /^(Authorization:\\s*Bearer\\s+)\\S+$/i;\nconst TOKEN_ARG_PATTERN = /^(--(?:token|api-key|secret|password)[=:])\\S+$/i;\n\n/**\n * Redact sensitive values that may appear in command args.\n * E.g., \"Authorization: Bearer abc123\" → \"Authorization: Bearer ${BEARER_TOKEN}\"\n */\nfunction redactArgValue(arg: string): string {\n const bearerMatch = BEARER_PATTERN.exec(arg);\n if (bearerMatch) return `${bearerMatch[1]}\\${BEARER_TOKEN}`;\n\n const tokenMatch = TOKEN_ARG_PATTERN.exec(arg);\n if (tokenMatch) return `${tokenMatch[1]}\\${TOKEN}`;\n\n return arg;\n}\n\n/**\n * Redact sensitive env values, replacing them with a placeholder.\n * Safe keys (PATH, HOME, etc.) are kept. Keys matching secret patterns\n * get their values replaced with \"${KEY_NAME}\" for the user to fill in.\n */\nfunction redactEnv(env: Record<string, string>): Record<string, string> {\n const result: Record<string, string> = {};\n for (const [k, v] of Object.entries(env)) {\n if (SAFE_ENV_KEYS.test(k)) {\n result[k] = v;\n } else if (SECRET_PATTERNS.test(k)) {\n result[k] = `\\${${k}}`;\n } else {\n // For non-secret, non-safe keys, keep the value\n result[k] = v;\n }\n }\n return result;\n}\n\n/**\n * Read and parse a JSON config file safely.\n * Handles JSONC (comments) by stripping them outside of string literals.\n * Also handles control characters that some tools leave in their configs.\n */\nfunction readJsonSafe(filePath: string): unknown {\n let raw = readFileSync(filePath, 'utf-8');\n\n // Strip control characters (except \\n, \\r, \\t) that break JSON.parse\n // Some tools write configs with embedded control chars in string values.\n raw = raw.replace(/[\\x00-\\x08\\x0b\\x0c\\x0e-\\x1f]/g, '');\n\n // Strip JSONC comments outside of string literals.\n // Walk character by character to avoid stripping // inside strings.\n let result = '';\n let inString = false;\n let escape = false;\n let i = 0;\n while (i < raw.length) {\n const ch = raw[i];\n\n if (escape) {\n result += ch;\n escape = false;\n i++;\n continue;\n }\n\n if (inString) {\n if (ch === '\\\\') {\n escape = true;\n result += ch;\n } else if (ch === '\"') {\n inString = false;\n result += ch;\n } else {\n result += ch;\n }\n i++;\n continue;\n }\n\n // Not in a string\n if (ch === '\"') {\n inString = true;\n result += ch;\n i++;\n } else if (ch === '/' && i + 1 < raw.length && raw[i + 1] === '/') {\n // Single-line comment — skip to end of line\n while (i < raw.length && raw[i] !== '\\n') i++;\n } else if (ch === '/' && i + 1 < raw.length && raw[i + 1] === '*') {\n // Multi-line comment — skip to */\n i += 2;\n while (i + 1 < raw.length && !(raw[i] === '*' && raw[i + 1] === '/')) i++;\n i += 2; // skip */\n } else {\n result += ch;\n i++;\n }\n }\n\n return JSON.parse(result);\n}\n\n// --- Discovery ---\n\n/**\n * Scan a single tool's config file for MCP server definitions.\n */\nfunction scanTool(toolConfig: ToolConfig): DiscoverySource {\n const configPath = toolConfig.path();\n const source: DiscoverySource = {\n tool: toolConfig.tool,\n configPath,\n found: false,\n servers: [],\n };\n\n if (!existsSync(configPath)) {\n return source;\n }\n\n source.found = true;\n\n try {\n const parsed = readJsonSafe(configPath);\n if (!parsed || typeof parsed !== 'object') {\n source.error = 'Config file is not a JSON object';\n return source;\n }\n\n const root = parsed as Record<string, unknown>;\n\n // Use custom extractor if provided, otherwise look up rootKey directly\n const serversObj = toolConfig.extractServers\n ? toolConfig.extractServers(root)\n : root[toolConfig.rootKey];\n\n if (!serversObj || typeof serversObj !== 'object' || Array.isArray(serversObj)) {\n // No servers section found — not an error, just nothing configured\n return source;\n }\n\n const entries = Object.entries(serversObj as Record<string, unknown>);\n for (const [name, value] of entries) {\n if (!value || typeof value !== 'object' || Array.isArray(value)) continue;\n\n const serverObj = value as Record<string, unknown>;\n\n // Skip disabled servers\n if (serverObj.disabled === true || serverObj.enabled === false) continue;\n\n try {\n const server = parseServer(name, serverObj, toolConfig.usesTypeField);\n // Only include servers that have enough info to be useful\n if (server.command || server.url) {\n source.servers.push(server);\n }\n } catch {\n // Skip individual servers that fail to parse\n }\n }\n } catch (err) {\n source.error = err instanceof Error ? err.message : String(err);\n }\n\n return source;\n}\n\n/** Options for discovery — primarily used for testing */\nexport interface DiscoveryOptions {\n /** Override home directory (default: os.homedir()) */\n homeDir?: string;\n /** Override platform detection (default: os.platform() === 'darwin') */\n isMac?: boolean;\n}\n\n/**\n * Scan all known tool config locations for MCP servers.\n * Returns deduplicated results with source tracking.\n */\nexport function discoverMcpServers(options?: DiscoveryOptions): DiscoveryResult {\n const h = options?.homeDir ?? homedir();\n const mac = options?.isMac ?? platform() === 'darwin';\n const toolConfigs = buildToolConfigs(h, mac);\n\n const sources: DiscoverySource[] = [];\n const seenNames = new Set<string>();\n const uniqueServers: DiscoveredMcpServer[] = [];\n\n for (const toolConfig of toolConfigs) {\n const source = scanTool(toolConfig);\n sources.push(source);\n\n // Dedupe by server name (first seen wins)\n for (const server of source.servers) {\n if (!seenNames.has(server.name)) {\n seenNames.add(server.name);\n uniqueServers.push(server);\n }\n }\n }\n\n return {\n sources,\n servers: uniqueServers,\n sourcesFound: sources.filter((s) => s.found).length,\n totalServers: uniqueServers.length,\n };\n}\n\n/** Binary basenames that are safe to strip from absolute paths — PATH will resolve them */\nconst NORMALIZABLE_BINARIES = new Set(['npx', 'node', 'python', 'python3']);\n\n/**\n * If `command` is an absolute path whose basename is a well-known interpreter\n * (npx/node/python/python3), return just the basename. Otherwise return as-is.\n *\n * This prevents leaking user-specific paths like\n * `/Users/foo/.nvm/versions/node/v22/bin/npx` into scaffolds.\n */\nfunction normalizeCommand(command: string): string {\n if (!command.startsWith('/')) return command;\n const base = command.substring(command.lastIndexOf('/') + 1);\n if (NORMALIZABLE_BINARIES.has(base)) return base;\n return command;\n}\n\n/** Check if an http/sse server has any form of Authorization configured */\nfunction hasAuthConfigured(server: DiscoveredMcpServer): boolean {\n if (server.headers) {\n for (const k of Object.keys(server.headers)) {\n if (k.toLowerCase() === 'authorization') return true;\n }\n }\n return false;\n}\n\n/** Find ${VAR} placeholders in a string. Returns var names. */\nfunction findEnvPlaceholders(value: string): string[] {\n const matches = value.matchAll(/\\$\\{([A-Z_][A-Z0-9_]*)\\}/gi);\n return Array.from(matches, (m) => m[1]);\n}\n\n/**\n * Filter out servers that won't work on this machine:\n * - http/sse servers with no Authorization header (will 401 silently)\n * - servers referencing env vars that aren't set in the current process\n *\n * Logs a warning to stderr for each skipped server explaining why.\n */\nexport function filterUnsafeServers(servers: DiscoveredMcpServer[]): DiscoveredMcpServer[] {\n const result: DiscoveredMcpServer[] = [];\n for (const server of servers) {\n // Drop unauth http/sse\n if (server.transport === 'http' || server.transport === 'sse') {\n if (!hasAuthConfigured(server)) {\n console.warn(`[mcp-discovery] skipping ${server.name}: ${server.transport} transport with no Authorization header`);\n continue;\n }\n }\n\n // Drop servers with unresolved env var references\n let missingVar: string | undefined;\n if (server.env) {\n for (const v of Object.values(server.env)) {\n for (const varName of findEnvPlaceholders(v)) {\n if (!(varName in process.env) || !process.env[varName]) {\n missingVar = varName;\n break;\n }\n }\n if (missingVar) break;\n }\n }\n if (!missingVar && server.headers) {\n for (const v of Object.values(server.headers)) {\n for (const varName of findEnvPlaceholders(v)) {\n if (!(varName in process.env) || !process.env[varName]) {\n missingVar = varName;\n break;\n }\n }\n if (missingVar) break;\n }\n }\n if (missingVar) {\n console.warn(`[mcp-discovery] skipping ${server.name}: required env var ${missingVar} not set`);\n continue;\n }\n\n result.push(server);\n }\n return result;\n}\n\n/**\n * Convert discovered servers to the harness config YAML format.\n * Returns a string that can be appended to config.yaml.\n *\n * Normalizes absolute paths to well-known binaries (npx/node/python) to bare\n * names so the YAML is portable across machines. When the command is normalized,\n * any PATH env var entry is dropped — it was only needed to find the absolute\n * binary location.\n */\nexport function discoveredServersToYaml(servers: DiscoveredMcpServer[]): string {\n if (servers.length === 0) return '';\n\n const lines: string[] = ['mcp:', ' servers:'];\n\n for (const server of servers) {\n lines.push(` ${server.name}:`);\n lines.push(` transport: ${server.transport}`);\n\n if (server.transport === 'stdio') {\n let normalizedCommand: string | undefined;\n if (server.command) {\n normalizedCommand = normalizeCommand(server.command);\n lines.push(` command: ${normalizedCommand}`);\n }\n const wasNormalized = !!server.command && normalizedCommand !== server.command;\n if (server.args && server.args.length > 0) {\n lines.push(` args: [${server.args.map((a) => `\"${a}\"`).join(', ')}]`);\n }\n // When the command was normalized to a bare binary name, drop PATH —\n // the system PATH will resolve it. Custom env vars are still kept.\n const filteredEnv = server.env\n ? Object.fromEntries(Object.entries(server.env).filter(([k]) => !(wasNormalized && k === 'PATH')))\n : undefined;\n if (filteredEnv && Object.keys(filteredEnv).length > 0) {\n lines.push(' env:');\n for (const [k, v] of Object.entries(filteredEnv)) {\n lines.push(` ${k}: \"${v}\"`);\n }\n }\n if (server.cwd) lines.push(` cwd: \"${server.cwd}\"`);\n } else {\n if (server.url) lines.push(` url: \"${server.url}\"`);\n if (server.headers && Object.keys(server.headers).length > 0) {\n lines.push(' headers:');\n for (const [k, v] of Object.entries(server.headers)) {\n lines.push(` ${k}: \"${v}\"`);\n }\n }\n }\n }\n\n return lines.join('\\n');\n}\n\n/** Get the list of tools that are scanned (for display purposes) */\nexport function getScannedTools(): string[] {\n // Tool names are the same regardless of home/platform\n return buildToolConfigs('', true).map((t) => t.tool);\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/runtime/mcp-discovery.ts"],"sourcesContent":["import { existsSync, readFileSync } from 'fs';\nimport { join } from 'path';\nimport { homedir, platform } from 'os';\n\n// --- Types ---\n\n/** A discovered MCP server from an external tool's config */\nexport interface DiscoveredMcpServer {\n /** Server name (key from the source config) */\n name: string;\n /** Transport type inferred from config */\n transport: 'stdio' | 'http' | 'sse';\n /** Command for stdio transport */\n command?: string;\n /** Args for stdio transport */\n args?: string[];\n /** Environment variables */\n env?: Record<string, string>;\n /** Working directory */\n cwd?: string;\n /** URL for http/sse transport */\n url?: string;\n /** Headers for http/sse transport */\n headers?: Record<string, string>;\n}\n\n/** Result of scanning a single tool's config */\nexport interface DiscoverySource {\n /** Tool name (e.g. \"Claude Desktop\", \"Cursor\") */\n tool: string;\n /** Config file path that was scanned */\n configPath: string;\n /** Whether the config file exists */\n found: boolean;\n /** Servers discovered from this tool */\n servers: DiscoveredMcpServer[];\n /** Error encountered while reading/parsing */\n error?: string;\n}\n\n/** Aggregated discovery results */\nexport interface DiscoveryResult {\n /** All sources scanned */\n sources: DiscoverySource[];\n /** Deduplicated servers (by name, preferring first seen) */\n servers: DiscoveredMcpServer[];\n /** Total sources that had config files */\n sourcesFound: number;\n /** Total unique servers discovered */\n totalServers: number;\n}\n\n// --- Known tool config locations ---\n\ninterface ToolConfig {\n tool: string;\n /** Function returning config file path for the current platform */\n path: () => string;\n /** JSON key containing server definitions */\n rootKey: string;\n /** Whether servers use explicit 'type' field instead of inferring transport */\n usesTypeField?: boolean;\n /** Custom extraction for non-standard config structures. Returns merged server map. */\n extractServers?: (root: Record<string, unknown>) => Record<string, unknown>;\n}\n\nfunction vscodeGlobalStoragePath(h: string, mac: boolean, extensionId: string): string {\n if (mac) {\n return join(h, 'Library', 'Application Support', 'Code', 'User', 'globalStorage', extensionId);\n }\n return join(h, '.config', 'Code', 'User', 'globalStorage', extensionId);\n}\n\n/** Claude Code extractServers — handles nested projects.<path>.mcpServers */\nfunction extractClaudeCodeServers(root: Record<string, unknown>): Record<string, unknown> {\n const merged: Record<string, unknown> = {};\n // Check top-level mcpServers\n const topLevel = root.mcpServers;\n if (topLevel && typeof topLevel === 'object' && !Array.isArray(topLevel)) {\n Object.assign(merged, topLevel);\n }\n // Check per-project mcpServers under projects.<path>.mcpServers\n const projects = root.projects;\n if (projects && typeof projects === 'object' && !Array.isArray(projects)) {\n for (const projectData of Object.values(projects as Record<string, unknown>)) {\n if (!projectData || typeof projectData !== 'object' || Array.isArray(projectData)) continue;\n const proj = projectData as Record<string, unknown>;\n const projServers = proj.mcpServers;\n if (projServers && typeof projServers === 'object' && !Array.isArray(projServers)) {\n for (const [name, config] of Object.entries(projServers as Record<string, unknown>)) {\n if (!(name in merged)) {\n merged[name] = config;\n }\n }\n }\n }\n }\n return merged;\n}\n\n/**\n * Build the list of tool configs for the given home directory and platform.\n * Evaluated lazily so tests can control the home/platform values.\n */\nfunction buildToolConfigs(h: string, mac: boolean): ToolConfig[] {\n return [\n {\n tool: 'Claude Desktop',\n path: () => mac\n ? join(h, 'Library', 'Application Support', 'Claude', 'claude_desktop_config.json')\n : join(h, '.config', 'Claude', 'claude_desktop_config.json'),\n rootKey: 'mcpServers',\n },\n {\n tool: 'Claude Code',\n path: () => join(h, '.claude.json'),\n rootKey: 'mcpServers',\n usesTypeField: true,\n extractServers: extractClaudeCodeServers,\n },\n {\n tool: 'Cursor',\n path: () => join(h, '.cursor', 'mcp.json'),\n rootKey: 'mcpServers',\n },\n {\n tool: 'Windsurf',\n path: () => join(h, '.codeium', 'windsurf', 'mcp_config.json'),\n rootKey: 'mcpServers',\n },\n {\n tool: 'Copilot CLI',\n path: () => join(h, '.copilot', 'mcp-config.json'),\n rootKey: 'mcpServers',\n },\n {\n tool: 'Cline',\n path: () => join(vscodeGlobalStoragePath(h, mac, 'saoudrizwan.claude-dev'), 'settings', 'cline_mcp_settings.json'),\n rootKey: 'mcpServers',\n },\n {\n tool: 'Roo Code',\n path: () => join(vscodeGlobalStoragePath(h, mac, 'rooveterinaryinc.roo-cline'), 'settings', 'mcp_settings.json'),\n rootKey: 'mcpServers',\n },\n {\n tool: 'VS Code',\n path: () => mac\n ? join(h, 'Library', 'Application Support', 'Code', 'User', 'mcp.json')\n : join(h, '.config', 'Code', 'User', 'mcp.json'),\n rootKey: 'servers',\n usesTypeField: true,\n },\n {\n tool: 'Zed',\n path: () => join(h, '.config', 'zed', 'settings.json'),\n rootKey: 'context_servers',\n },\n ];\n}\n\n// --- Parsing ---\n\n/**\n * Infer transport type from a server config object.\n * Most tools use 'command' = stdio, 'url' = http/sse.\n * VS Code uses explicit 'type' field.\n */\nfunction inferTransport(\n serverObj: Record<string, unknown>,\n usesTypeField?: boolean,\n): 'stdio' | 'http' | 'sse' {\n if (usesTypeField && typeof serverObj.type === 'string') {\n const t = serverObj.type.toLowerCase();\n if (t === 'sse') return 'sse';\n if (t === 'http') return 'http';\n if (t === 'stdio') return 'stdio';\n }\n\n if (typeof serverObj.command === 'string') return 'stdio';\n if (typeof serverObj.url === 'string' || typeof serverObj.serverUrl === 'string') {\n // If the URL looks like an SSE endpoint\n const url = (serverObj.url ?? serverObj.serverUrl) as string;\n if (url.includes('/sse') || serverObj.type === 'sse') return 'sse';\n return 'http';\n }\n\n // Default to stdio if we can't determine\n return 'stdio';\n}\n\n/**\n * Parse a server config object into a DiscoveredMcpServer.\n */\nfunction parseServer(\n name: string,\n serverObj: Record<string, unknown>,\n usesTypeField?: boolean,\n): DiscoveredMcpServer {\n const transport = inferTransport(serverObj, usesTypeField);\n const server: DiscoveredMcpServer = { name, transport };\n\n if (transport === 'stdio') {\n if (typeof serverObj.command === 'string') server.command = serverObj.command;\n if (Array.isArray(serverObj.args)) {\n server.args = serverObj.args\n .filter((a): a is string => typeof a === 'string')\n .map((a) => redactArgValue(a));\n }\n if (serverObj.env && typeof serverObj.env === 'object' && !Array.isArray(serverObj.env)) {\n server.env = redactEnv(filterStringRecord(serverObj.env as Record<string, unknown>));\n }\n if (typeof serverObj.cwd === 'string') server.cwd = serverObj.cwd;\n } else {\n const url = serverObj.url ?? serverObj.serverUrl;\n if (typeof url === 'string') server.url = url;\n if (serverObj.headers && typeof serverObj.headers === 'object' && !Array.isArray(serverObj.headers)) {\n // Redact auth headers\n const headers = filterStringRecord(serverObj.headers as Record<string, unknown>);\n for (const [k, v] of Object.entries(headers)) {\n if (SECRET_PATTERNS.test(k) || SECRET_PATTERNS.test(v)) {\n headers[k] = `\\${${k.toUpperCase().replace(/[^A-Z0-9]/g, '_')}}`;\n }\n }\n server.headers = headers;\n }\n }\n\n return server;\n}\n\n/** Filter an object to only string values */\nfunction filterStringRecord(obj: Record<string, unknown>): Record<string, string> {\n const result: Record<string, string> = {};\n for (const [k, v] of Object.entries(obj)) {\n if (typeof v === 'string') result[k] = v;\n }\n return result;\n}\n\n/** Patterns that indicate a secret value in env vars */\nconst SECRET_PATTERNS = /(?:api[_-]?key|secret|token|password|bearer|auth)/i;\n/** Patterns for non-secret env vars that are always safe to copy */\nconst SAFE_ENV_KEYS = /^(?:PATH|HOME|NODE_ENV|NODE_OPTIONS|SHELL|LANG|LC_\\w+|TZ|TERM|EDITOR)$/;\n\n/** Pattern for Bearer tokens or similar auth values in args */\nconst BEARER_PATTERN = /^(Authorization:\\s*Bearer\\s+)\\S+$/i;\nconst TOKEN_ARG_PATTERN = /^(--(?:token|api-key|secret|password)[=:])\\S+$/i;\n\n/**\n * Redact sensitive values that may appear in command args.\n * E.g., \"Authorization: Bearer abc123\" → \"Authorization: Bearer ${BEARER_TOKEN}\"\n */\nfunction redactArgValue(arg: string): string {\n const bearerMatch = BEARER_PATTERN.exec(arg);\n if (bearerMatch) return `${bearerMatch[1]}\\${BEARER_TOKEN}`;\n\n const tokenMatch = TOKEN_ARG_PATTERN.exec(arg);\n if (tokenMatch) return `${tokenMatch[1]}\\${TOKEN}`;\n\n return arg;\n}\n\n/**\n * Redact sensitive env values, replacing them with a placeholder.\n * Safe keys (PATH, HOME, etc.) are kept. Keys matching secret patterns\n * get their values replaced with \"${KEY_NAME}\" for the user to fill in.\n */\nfunction redactEnv(env: Record<string, string>): Record<string, string> {\n const result: Record<string, string> = {};\n for (const [k, v] of Object.entries(env)) {\n if (SAFE_ENV_KEYS.test(k)) {\n result[k] = v;\n } else if (SECRET_PATTERNS.test(k)) {\n result[k] = `\\${${k}}`;\n } else {\n // For non-secret, non-safe keys, keep the value\n result[k] = v;\n }\n }\n return result;\n}\n\n/**\n * Read and parse a JSON config file safely.\n * Handles JSONC (comments) by stripping them outside of string literals.\n * Also handles control characters that some tools leave in their configs.\n */\nfunction readJsonSafe(filePath: string): unknown {\n let raw = readFileSync(filePath, 'utf-8');\n\n // Strip control characters (except \\n, \\r, \\t) that break JSON.parse\n // Some tools write configs with embedded control chars in string values.\n raw = raw.replace(/[\\x00-\\x08\\x0b\\x0c\\x0e-\\x1f]/g, '');\n\n // Strip JSONC comments outside of string literals.\n // Walk character by character to avoid stripping // inside strings.\n let result = '';\n let inString = false;\n let escape = false;\n let i = 0;\n while (i < raw.length) {\n const ch = raw[i];\n\n if (escape) {\n result += ch;\n escape = false;\n i++;\n continue;\n }\n\n if (inString) {\n if (ch === '\\\\') {\n escape = true;\n result += ch;\n } else if (ch === '\"') {\n inString = false;\n result += ch;\n } else {\n result += ch;\n }\n i++;\n continue;\n }\n\n // Not in a string\n if (ch === '\"') {\n inString = true;\n result += ch;\n i++;\n } else if (ch === '/' && i + 1 < raw.length && raw[i + 1] === '/') {\n // Single-line comment — skip to end of line\n while (i < raw.length && raw[i] !== '\\n') i++;\n } else if (ch === '/' && i + 1 < raw.length && raw[i + 1] === '*') {\n // Multi-line comment — skip to */\n i += 2;\n while (i + 1 < raw.length && !(raw[i] === '*' && raw[i + 1] === '/')) i++;\n i += 2; // skip */\n } else {\n result += ch;\n i++;\n }\n }\n\n return JSON.parse(result);\n}\n\n// --- Discovery ---\n\n/**\n * Scan a single tool's config file for MCP server definitions.\n */\nfunction scanTool(toolConfig: ToolConfig): DiscoverySource {\n const configPath = toolConfig.path();\n const source: DiscoverySource = {\n tool: toolConfig.tool,\n configPath,\n found: false,\n servers: [],\n };\n\n if (!existsSync(configPath)) {\n return source;\n }\n\n source.found = true;\n\n try {\n const parsed = readJsonSafe(configPath);\n if (!parsed || typeof parsed !== 'object') {\n source.error = 'Config file is not a JSON object';\n return source;\n }\n\n const root = parsed as Record<string, unknown>;\n\n // Use custom extractor if provided, otherwise look up rootKey directly\n const serversObj = toolConfig.extractServers\n ? toolConfig.extractServers(root)\n : root[toolConfig.rootKey];\n\n if (!serversObj || typeof serversObj !== 'object' || Array.isArray(serversObj)) {\n // No servers section found — not an error, just nothing configured\n return source;\n }\n\n const entries = Object.entries(serversObj as Record<string, unknown>);\n for (const [name, value] of entries) {\n if (!value || typeof value !== 'object' || Array.isArray(value)) continue;\n\n const serverObj = value as Record<string, unknown>;\n\n // Skip disabled servers\n if (serverObj.disabled === true || serverObj.enabled === false) continue;\n\n try {\n const server = parseServer(name, serverObj, toolConfig.usesTypeField);\n // Only include servers that have enough info to be useful\n if (server.command || server.url) {\n source.servers.push(server);\n }\n } catch {\n // Skip individual servers that fail to parse\n }\n }\n } catch (err) {\n source.error = err instanceof Error ? err.message : String(err);\n }\n\n return source;\n}\n\n/** Options for discovery — primarily used for testing */\nexport interface DiscoveryOptions {\n /** Override home directory (default: os.homedir()) */\n homeDir?: string;\n /** Override platform detection (default: os.platform() === 'darwin') */\n isMac?: boolean;\n}\n\n/**\n * Scan all known tool config locations for MCP servers.\n * Returns deduplicated results with source tracking.\n */\nexport function discoverMcpServers(options?: DiscoveryOptions): DiscoveryResult {\n const h = options?.homeDir ?? homedir();\n const mac = options?.isMac ?? platform() === 'darwin';\n const toolConfigs = buildToolConfigs(h, mac);\n\n const sources: DiscoverySource[] = [];\n const seenNames = new Set<string>();\n const uniqueServers: DiscoveredMcpServer[] = [];\n\n for (const toolConfig of toolConfigs) {\n const source = scanTool(toolConfig);\n sources.push(source);\n\n // Dedupe by server name (first seen wins)\n for (const server of source.servers) {\n if (!seenNames.has(server.name)) {\n seenNames.add(server.name);\n uniqueServers.push(server);\n }\n }\n }\n\n return {\n sources,\n servers: uniqueServers,\n sourcesFound: sources.filter((s) => s.found).length,\n totalServers: uniqueServers.length,\n };\n}\n\n/** Binary basenames that are safe to strip from absolute paths — PATH will resolve them */\nconst NORMALIZABLE_BINARIES = new Set(['npx', 'node', 'python', 'python3']);\n\n/**\n * If `command` is an absolute path whose basename is a well-known interpreter\n * (npx/node/python/python3), return just the basename. Otherwise return as-is.\n *\n * This prevents leaking user-specific paths like\n * `/Users/foo/.nvm/versions/node/v22/bin/npx` into scaffolds.\n */\nfunction normalizeCommand(command: string): string {\n if (!command.startsWith('/')) return command;\n const base = command.substring(command.lastIndexOf('/') + 1);\n if (NORMALIZABLE_BINARIES.has(base)) return base;\n return command;\n}\n\n/** Check if an http/sse server has any form of Authorization configured */\nfunction hasAuthConfigured(server: DiscoveredMcpServer): boolean {\n if (server.headers) {\n for (const k of Object.keys(server.headers)) {\n if (k.toLowerCase() === 'authorization') return true;\n }\n }\n return false;\n}\n\n/** Find ${VAR} placeholders in a string. Returns var names. */\nfunction findEnvPlaceholders(value: string): string[] {\n const matches = value.matchAll(/\\$\\{([A-Z_][A-Z0-9_]*)\\}/gi);\n return Array.from(matches, (m) => m[1]);\n}\n\n/**\n * Filter out servers that won't work on this machine:\n * - http/sse servers with no Authorization header (will 401 silently)\n * - servers referencing env vars that aren't set in the current process\n *\n * Logs a warning to stderr for each skipped server explaining why.\n */\nexport function filterUnsafeServers(servers: DiscoveredMcpServer[]): DiscoveredMcpServer[] {\n const result: DiscoveredMcpServer[] = [];\n for (const server of servers) {\n // Drop unauth http/sse\n if (server.transport === 'http' || server.transport === 'sse') {\n if (!hasAuthConfigured(server)) {\n console.warn(`[mcp-discovery] skipping ${server.name}: ${server.transport} transport with no Authorization header`);\n continue;\n }\n }\n\n // Drop servers with unresolved env var references\n let missingVar: string | undefined;\n if (server.env) {\n for (const v of Object.values(server.env)) {\n for (const varName of findEnvPlaceholders(v)) {\n if (!(varName in process.env) || !process.env[varName]) {\n missingVar = varName;\n break;\n }\n }\n if (missingVar) break;\n }\n }\n if (!missingVar && server.headers) {\n for (const v of Object.values(server.headers)) {\n for (const varName of findEnvPlaceholders(v)) {\n if (!(varName in process.env) || !process.env[varName]) {\n missingVar = varName;\n break;\n }\n }\n if (missingVar) break;\n }\n }\n if (missingVar) {\n console.warn(`[mcp-discovery] skipping ${server.name}: required env var ${missingVar} not set`);\n continue;\n }\n\n result.push(server);\n }\n return result;\n}\n\n/**\n * Convert discovered servers to the harness config YAML format.\n * Returns a string that can be appended to config.yaml.\n *\n * Normalizes absolute paths to well-known binaries (npx/node/python) to bare\n * names so the YAML is portable across machines. When the command is normalized,\n * any PATH env var entry is dropped — it was only needed to find the absolute\n * binary location.\n */\nexport function discoveredServersToYaml(servers: DiscoveredMcpServer[]): string {\n if (servers.length === 0) return '';\n\n const lines: string[] = ['mcp:', ' servers:'];\n\n for (const server of servers) {\n lines.push(` ${server.name}:`);\n lines.push(` transport: ${server.transport}`);\n\n if (server.transport === 'stdio') {\n let normalizedCommand: string | undefined;\n if (server.command) {\n normalizedCommand = normalizeCommand(server.command);\n lines.push(` command: ${normalizedCommand}`);\n }\n const wasNormalized = !!server.command && normalizedCommand !== server.command;\n if (server.args && server.args.length > 0) {\n lines.push(` args: [${server.args.map((a) => `\"${a}\"`).join(', ')}]`);\n }\n // When the command was normalized to a bare binary name, drop PATH —\n // the system PATH will resolve it. Custom env vars are still kept.\n const filteredEnv = server.env\n ? Object.fromEntries(Object.entries(server.env).filter(([k]) => !(wasNormalized && k === 'PATH')))\n : undefined;\n if (filteredEnv && Object.keys(filteredEnv).length > 0) {\n lines.push(' env:');\n for (const [k, v] of Object.entries(filteredEnv)) {\n lines.push(` ${k}: \"${v}\"`);\n }\n }\n if (server.cwd) lines.push(` cwd: \"${server.cwd}\"`);\n } else {\n if (server.url) lines.push(` url: \"${server.url}\"`);\n if (server.headers && Object.keys(server.headers).length > 0) {\n lines.push(' headers:');\n for (const [k, v] of Object.entries(server.headers)) {\n lines.push(` ${k}: \"${v}\"`);\n }\n }\n }\n }\n\n return lines.join('\\n');\n}\n\n/** Get the list of tools that are scanned (for display purposes) */\nexport function getScannedTools(): string[] {\n // Tool names are the same regardless of home/platform\n return buildToolConfigs('', true).map((t) => t.tool);\n}\n"],"mappings":"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","names":[]}
@@ -9,9 +9,8 @@ import {
9
9
  } from "./chunk-BSKDOFRT.js";
10
10
  import {
11
11
  loadConfig
12
- } from "./chunk-CHJ5GNZC.js";
13
- import "./chunk-4CWAGBNS.js";
14
- import "./chunk-ZZJOFKAT.js";
12
+ } from "./chunk-EC42HQQH.js";
13
+ import "./chunk-4TQQZILG.js";
15
14
 
16
15
  // src/runtime/mcp-installer.ts
17
16
  import { existsSync, readFileSync, writeFileSync, mkdirSync } from "fs";
@@ -391,4 +390,4 @@ export {
391
390
  serverExistsInConfig,
392
391
  updateConfigWithServer
393
392
  };
394
- //# sourceMappingURL=mcp-installer-6O2XXD3V.js.map
393
+ //# sourceMappingURL=mcp-installer-X2TJ2S2G.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/runtime/mcp-installer.ts","../src/runtime/mcp-registry.ts"],"sourcesContent":["import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'fs';\nimport { join } from 'path';\nimport YAML from 'yaml';\nimport type { HarnessConfig } from '../core/types.js';\nimport { loadConfig } from '../core/config.js';\nimport type { McpServerConfig } from './mcp.js';\nimport { createMcpManager, validateMcpConfig } from './mcp.js';\nimport type { ResolvedServer, RegistryEnvVar, RegistryServer, RegistrySearchResponse } from './mcp-registry.js';\nimport { findServer, searchServers, searchRegistry, getRegistryServer } from './mcp-registry.js';\nimport { log } from '../core/logger.js';\n\n// Re-export registry types and functions for consumers\nexport { searchRegistry, getRegistryServer };\nexport type { RegistryServer, RegistrySearchResponse, RegistryEnvVar, ResolvedServer };\nexport type { RegistryPackage, RegistryRemote, RegistrySearchResult } from './mcp-registry.js';\n\n// --- Types ---\n\n/** Result of installing an MCP server */\nexport interface McpInstallResult {\n /** Whether installation succeeded */\n installed: boolean;\n /** Server config name in config.yaml */\n name: string;\n /** The resolved server details */\n server?: ResolvedServer;\n /** Connection test results */\n connectionTest?: {\n connected: boolean;\n toolCount: number;\n toolNames: string[];\n error?: string;\n };\n /** Generated tool doc paths */\n generatedDocs: string[];\n /** Env vars that need user configuration */\n pendingEnvVars: RegistryEnvVar[];\n /** Error message if installation failed */\n error?: string;\n}\n\n/** Options for the install command */\nexport interface McpInstallOptions {\n /** Harness directory */\n dir: string;\n /** Skip connection testing */\n skipTest?: boolean;\n /** Skip tool doc generation */\n skipDocs?: boolean;\n /** Force overwrite if server already exists */\n force?: boolean;\n /** Custom name override for the server in config */\n name?: string;\n}\n\n// --- Config Update ---\n\nconst CONFIG_FILENAMES = ['config.yaml', 'config.yml', 'harness.yaml', 'harness.yml'];\n\n/**\n * Find the config file path in a harness directory.\n */\nfunction findConfigPath(dir: string): string {\n for (const filename of CONFIG_FILENAMES) {\n const configPath = join(dir, filename);\n if (existsSync(configPath)) {\n return configPath;\n }\n }\n return join(dir, 'config.yaml');\n}\n\n/**\n * Add or update an MCP server entry in the config file.\n * Preserves existing config structure and comments where possible.\n */\nexport function updateConfigWithServer(\n dir: string,\n serverName: string,\n serverConfig: McpServerConfig,\n): void {\n const configPath = findConfigPath(dir);\n const content = existsSync(configPath) ? readFileSync(configPath, 'utf-8') : '';\n\n // Parse existing YAML, preserving structure\n const doc = YAML.parseDocument(content);\n\n // Ensure mcp.servers exists\n if (!doc.has('mcp')) {\n doc.set('mcp', doc.createNode({ servers: {} }));\n }\n const mcp = doc.get('mcp') as YAML.YAMLMap;\n if (!mcp.has('servers')) {\n mcp.set('servers', doc.createNode({}));\n }\n const servers = mcp.get('servers') as YAML.YAMLMap;\n\n // Build the server config node\n const configNode: Record<string, unknown> = {\n transport: serverConfig.transport,\n };\n\n if (serverConfig.transport === 'stdio') {\n if (serverConfig.command) configNode['command'] = serverConfig.command;\n if (serverConfig.args && serverConfig.args.length > 0) configNode['args'] = serverConfig.args;\n if (serverConfig.env && Object.keys(serverConfig.env).length > 0) configNode['env'] = serverConfig.env;\n if (serverConfig.cwd) configNode['cwd'] = serverConfig.cwd;\n } else {\n if (serverConfig.url) configNode['url'] = serverConfig.url;\n if (serverConfig.headers && Object.keys(serverConfig.headers).length > 0) {\n configNode['headers'] = serverConfig.headers;\n }\n }\n\n // Set the server entry (add or overwrite)\n servers.set(serverName, doc.createNode(configNode));\n\n // Write back\n writeFileSync(configPath, doc.toString(), 'utf-8');\n}\n\n/**\n * Check if a server name already exists in the config.\n */\nexport function serverExistsInConfig(dir: string, serverName: string): boolean {\n try {\n const config = loadConfig(dir);\n return Boolean(config.mcp?.servers?.[serverName]);\n } catch {\n return false;\n }\n}\n\n// --- Tool Doc Generation ---\n\n/**\n * Generate a tools/*.md knowledge doc from a connected MCP server's tools.\n * Creates one file per MCP server with descriptions of all available tools.\n */\nexport function generateToolDocs(\n dir: string,\n serverName: string,\n toolNames: string[],\n description?: string,\n): string[] {\n const toolsDir = join(dir, 'tools');\n if (!existsSync(toolsDir)) {\n mkdirSync(toolsDir, { recursive: true });\n }\n\n const docPath = join(toolsDir, `${serverName}.md`);\n const lines: string[] = [\n '---',\n `id: tool-${serverName}`,\n `created: ${new Date().toISOString().split('T')[0]}`,\n `tags: [mcp, ${serverName}]`,\n '---',\n '',\n `# ${serverName} MCP Server`,\n '',\n ];\n\n if (description) {\n lines.push(description, '');\n }\n\n lines.push('## Available Tools', '');\n\n for (const toolName of toolNames) {\n lines.push(`- **${toolName}**`);\n }\n\n lines.push('', `> Auto-generated by \\`harness mcp install ${serverName}\\``);\n\n writeFileSync(docPath, lines.join('\\n'), 'utf-8');\n return [docPath];\n}\n\n// --- Connection Test ---\n\n/**\n * Test an MCP server connection and return tool info.\n */\nasync function testConnection(\n dir: string,\n serverName: string,\n serverConfig: McpServerConfig,\n): Promise<{ connected: boolean; toolCount: number; toolNames: string[]; error?: string }> {\n // Build a minimal config for testing just this server\n const config = loadConfig(dir);\n const testConfig: HarnessConfig = {\n ...config,\n mcp: { servers: { [serverName]: { ...serverConfig, enabled: serverConfig.enabled ?? true } } },\n };\n\n // Validate first\n const validationErrors = validateMcpConfig(testConfig);\n if (validationErrors.length > 0) {\n return {\n connected: false,\n toolCount: 0,\n toolNames: [],\n error: validationErrors.map((e) => e.error).join('; '),\n };\n }\n\n const manager = createMcpManager(testConfig);\n try {\n await manager.connect();\n const summaries = manager.getSummaries();\n const summary = summaries.find((s) => s.name === serverName);\n\n if (summary?.connected) {\n return {\n connected: true,\n toolCount: summary.toolCount,\n toolNames: summary.toolNames,\n };\n }\n\n return {\n connected: false,\n toolCount: 0,\n toolNames: [],\n error: summary?.error ?? 'Connection failed',\n };\n } catch (err) {\n return {\n connected: false,\n toolCount: 0,\n toolNames: [],\n error: err instanceof Error ? err.message : String(err),\n };\n } finally {\n await manager.close();\n }\n}\n\n// --- Main Install Flow ---\n\n/**\n * Install an MCP server by name or search query.\n *\n * Flow:\n * 1. Search the MCP registry for the server\n * 2. Resolve the best package/transport configuration\n * 3. Add/update the server entry in config.yaml\n * 4. Optionally test the connection\n * 5. Optionally generate tools/*.md knowledge docs\n */\nexport async function installMcpServer(\n query: string,\n options: McpInstallOptions,\n): Promise<McpInstallResult> {\n const { dir, skipTest, skipDocs, force, name: nameOverride } = options;\n\n // Step 1: Find the server in the registry\n log.info(`Searching MCP registry for \"${query}\"...`);\n let resolved: ResolvedServer | null;\n try {\n resolved = await findServer(query);\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n return {\n installed: false,\n name: query,\n generatedDocs: [],\n pendingEnvVars: [],\n error: `Registry lookup failed: ${message}`,\n };\n }\n\n if (!resolved) {\n return {\n installed: false,\n name: query,\n generatedDocs: [],\n pendingEnvVars: [],\n error: `No MCP server found matching \"${query}\" in the registry`,\n };\n }\n\n const serverName = nameOverride ?? resolved.name;\n\n // Step 2: Check if already exists\n if (!force && serverExistsInConfig(dir, serverName)) {\n return {\n installed: false,\n name: serverName,\n server: resolved,\n generatedDocs: [],\n pendingEnvVars: resolved.requiredEnv,\n error: `Server \"${serverName}\" already exists in config. Use --force to overwrite.`,\n };\n }\n\n // Step 3: Write to config.yaml\n log.info(`Adding \"${serverName}\" to config.yaml...`);\n updateConfigWithServer(dir, serverName, resolved.config);\n\n const result: McpInstallResult = {\n installed: true,\n name: serverName,\n server: resolved,\n generatedDocs: [],\n pendingEnvVars: resolved.requiredEnv,\n };\n\n // Step 4: Test connection (optional)\n if (!skipTest) {\n log.info(`Testing connection to \"${serverName}\"...`);\n result.connectionTest = await testConnection(dir, serverName, resolved.config);\n }\n\n // Step 5: Generate tool docs (optional)\n if (!skipDocs && result.connectionTest?.connected && result.connectionTest.toolNames.length > 0) {\n log.info(`Generating tool docs for \"${serverName}\"...`);\n result.generatedDocs = generateToolDocs(\n dir,\n serverName,\n result.connectionTest.toolNames,\n resolved.description,\n );\n }\n\n return result;\n}\n\n/**\n * List available servers from the registry for a given query.\n */\nexport async function listRegistryServers(\n query: string,\n options?: { limit?: number },\n): Promise<ResolvedServer[]> {\n return searchServers(query, options);\n}\n\n/**\n * Format a registry search result for CLI display.\n */\nexport function formatRegistryServer(entry: { server: RegistryServer }): string {\n const s = entry.server;\n const lines: string[] = [];\n\n lines.push(` ${s.name} (v${s.version})`);\n if (s.title) lines.push(` ${s.title}`);\n if (s.description) {\n const desc = s.description.length > 100 ? s.description.slice(0, 97) + '...' : s.description;\n lines.push(` ${desc}`);\n }\n\n // Show packages\n const npmPkgs = (s.packages ?? []).filter((p) => p.registryType === 'npm');\n const pypiPkgs = (s.packages ?? []).filter((p) => p.registryType === 'pypi');\n if (npmPkgs.length > 0) {\n lines.push(` npm: ${npmPkgs.map((p) => p.identifier).join(', ')}`);\n }\n if (pypiPkgs.length > 0) {\n lines.push(` pypi: ${pypiPkgs.map((p) => p.identifier).join(', ')}`);\n }\n\n // Show remotes\n if (s.remotes && s.remotes.length > 0) {\n lines.push(` remote: ${s.remotes[0].transportType} ${s.remotes[0].url}`);\n }\n\n // Show required env vars\n const allEnvVars = (s.packages ?? [])\n .flatMap((p) => p.environmentVariables ?? [])\n .filter((v) => v.isRequired);\n if (allEnvVars.length > 0) {\n lines.push(` requires: ${allEnvVars.map((v) => v.name).join(', ')}`);\n }\n\n return lines.join('\\n');\n}\n","import type { McpServerConfig } from './mcp.js';\n\n// --- Types ---\n\n/** Environment variable from registry server entry */\nexport interface RegistryEnvVar {\n name: string;\n description?: string;\n isRequired?: boolean;\n format?: string;\n}\n\n/** Package entry from registry server */\nexport interface RegistryPackage {\n registryType: 'npm' | 'pypi' | 'oci' | 'nuget' | 'mcpb';\n identifier: string;\n version: string;\n transport: { type: 'stdio' | 'streamable-http' | 'sse' };\n environmentVariables?: RegistryEnvVar[];\n runtimeHint?: 'npx' | 'uvx' | 'docker' | 'dnx';\n packageArguments?: unknown[];\n runtimeArguments?: unknown[];\n}\n\n/** Remote endpoint from registry server */\nexport interface RegistryRemote {\n transportType: 'streamable-http' | 'sse';\n url: string;\n headers?: Record<string, string>;\n}\n\n/** A single server entry from the MCP registry API */\nexport interface RegistryServer {\n $schema?: string;\n name: string;\n description?: string;\n title?: string;\n version: string;\n repository?: { url: string; source?: string; subfolder?: string };\n websiteUrl?: string;\n packages?: RegistryPackage[];\n remotes?: RegistryRemote[];\n}\n\n/** A server result from the registry search API */\nexport interface RegistrySearchResult {\n server: RegistryServer;\n _meta?: Record<string, unknown>;\n}\n\n/** Full search response from the registry */\nexport interface RegistrySearchResponse {\n servers: RegistrySearchResult[];\n metadata?: { nextCursor?: string; count?: number };\n}\n\n/** Resolved server config ready for installation */\nexport interface ResolvedServer {\n /** Display name for the server */\n name: string;\n /** Server description from registry */\n description?: string;\n /** Registry name (e.g. \"io.github.foo/bar\") */\n registryName: string;\n /** Source package info */\n package?: RegistryPackage;\n /** Source remote info */\n remote?: RegistryRemote;\n /** Generated harness config */\n config: McpServerConfig;\n /** Environment variables that need to be set */\n requiredEnv: RegistryEnvVar[];\n /** All environment variables (required + optional) */\n allEnv: RegistryEnvVar[];\n}\n\n// --- Constants ---\n\nconst REGISTRY_BASE = 'https://registry.modelcontextprotocol.io';\nconst REGISTRY_API_VERSION = 'v0.1';\nconst DEFAULT_SEARCH_LIMIT = 10;\n\n// --- Registry Client ---\n\n/**\n * Search the MCP registry for servers matching a query.\n */\nexport async function searchRegistry(\n query: string,\n options?: { limit?: number; cursor?: string },\n): Promise<RegistrySearchResponse> {\n const limit = options?.limit ?? DEFAULT_SEARCH_LIMIT;\n const params = new URLSearchParams({\n search: query,\n limit: String(limit),\n version: 'latest',\n });\n if (options?.cursor) {\n params.set('cursor', options.cursor);\n }\n\n const url = `${REGISTRY_BASE}/${REGISTRY_API_VERSION}/servers?${params}`;\n const response = await fetch(url);\n\n if (!response.ok) {\n throw new Error(`MCP registry search failed (${response.status}): ${response.statusText}`);\n }\n\n const data = (await response.json()) as RegistrySearchResponse;\n return data;\n}\n\n/**\n * Get a specific server by its registry name and version.\n * Name must be URL-encoded (e.g. \"io.github.foo/bar\" -> \"io.github.foo%2Fbar\").\n */\nexport async function getRegistryServer(\n name: string,\n version: string = 'latest',\n): Promise<RegistryServer> {\n const encodedName = encodeURIComponent(name);\n const encodedVersion = encodeURIComponent(version);\n const url = `${REGISTRY_BASE}/${REGISTRY_API_VERSION}/servers/${encodedName}/versions/${encodedVersion}`;\n const response = await fetch(url);\n\n if (!response.ok) {\n if (response.status === 404) {\n throw new Error(`MCP server \"${name}\" not found in registry`);\n }\n throw new Error(`MCP registry lookup failed (${response.status}): ${response.statusText}`);\n }\n\n const data = (await response.json()) as RegistryServer;\n return data;\n}\n\n// --- Runtime hint mapping ---\n\nconst RUNTIME_COMMANDS: Record<string, { command: string; prefix: string[] }> = {\n npx: { command: 'npx', prefix: ['-y'] },\n uvx: { command: 'uvx', prefix: [] },\n docker: { command: 'docker', prefix: ['run', '-i', '--rm'] },\n dnx: { command: 'dnx', prefix: [] },\n};\n\n/**\n * Derive a short config name from a registry server name.\n * \"io.github.foo/bar-server\" -> \"bar-server\"\n * \"io.github.foo/mcp-something\" -> \"mcp-something\"\n */\nexport function deriveConfigName(registryName: string): string {\n const parts = registryName.split('/');\n return parts[parts.length - 1];\n}\n\n/**\n * Resolve a registry server entry into a harness McpServerConfig.\n * Prefers npm stdio packages, falls back to pypi, then remotes.\n */\nexport function resolveServerConfig(server: RegistryServer): ResolvedServer {\n const name = deriveConfigName(server.name);\n\n // Strategy 1: Look for an npm stdio package\n const npmPkg = server.packages?.find((p) => p.registryType === 'npm' && p.transport.type === 'stdio');\n if (npmPkg) {\n return resolveStdioPackage(name, server, npmPkg, 'npx');\n }\n\n // Strategy 2: Look for any stdio package with runtimeHint\n const stdioPkg = server.packages?.find((p) => p.transport.type === 'stdio');\n if (stdioPkg) {\n const hint = stdioPkg.runtimeHint ?? (stdioPkg.registryType === 'pypi' ? 'uvx' : 'npx');\n return resolveStdioPackage(name, server, stdioPkg, hint);\n }\n\n // Strategy 3: Look for remotes (HTTP/SSE endpoints)\n if (server.remotes && server.remotes.length > 0) {\n const remote = server.remotes[0];\n return resolveRemote(name, server, remote);\n }\n\n // Strategy 4: Look for any package with HTTP transport\n const httpPkg = server.packages?.find(\n (p) => p.transport.type === 'streamable-http' || p.transport.type === 'sse',\n );\n if (httpPkg) {\n return resolveHttpPackage(name, server, httpPkg);\n }\n\n throw new Error(\n `Cannot resolve MCP server \"${server.name}\": no supported package or remote configuration found`,\n );\n}\n\nfunction resolveStdioPackage(\n name: string,\n server: RegistryServer,\n pkg: RegistryPackage,\n runtimeHint: string,\n): ResolvedServer {\n const runtime = RUNTIME_COMMANDS[runtimeHint] ?? RUNTIME_COMMANDS['npx'];\n const args = [...runtime.prefix, pkg.identifier];\n\n const envVars = pkg.environmentVariables ?? [];\n const env: Record<string, string> = {};\n for (const ev of envVars) {\n env[ev.name] = `\\${${ev.name}}`;\n }\n\n const config: McpServerConfig = {\n transport: 'stdio',\n command: runtime.command,\n args,\n ...(Object.keys(env).length > 0 ? { env } : {}),\n };\n\n return {\n name,\n description: server.description,\n registryName: server.name,\n package: pkg,\n config,\n requiredEnv: envVars.filter((e) => e.isRequired),\n allEnv: envVars,\n };\n}\n\nfunction resolveRemote(\n name: string,\n server: RegistryServer,\n remote: RegistryRemote,\n): ResolvedServer {\n const transport = remote.transportType === 'sse' ? 'sse' as const : 'http' as const;\n\n const config: McpServerConfig = {\n transport,\n url: remote.url,\n ...(remote.headers && Object.keys(remote.headers).length > 0 ? { headers: remote.headers } : {}),\n };\n\n return {\n name,\n description: server.description,\n registryName: server.name,\n remote,\n config,\n requiredEnv: [],\n allEnv: [],\n };\n}\n\nfunction resolveHttpPackage(\n name: string,\n server: RegistryServer,\n pkg: RegistryPackage,\n): ResolvedServer {\n // HTTP packages typically have a URL in the package identifier or need a remote\n const transport = pkg.transport.type === 'sse' ? 'sse' as const : 'http' as const;\n\n const envVars = pkg.environmentVariables ?? [];\n const config: McpServerConfig = {\n transport,\n url: pkg.identifier,\n };\n\n return {\n name,\n description: server.description,\n registryName: server.name,\n package: pkg,\n config,\n requiredEnv: envVars.filter((e) => e.isRequired),\n allEnv: envVars,\n };\n}\n\n/**\n * Search the registry and return the best match for a query.\n * If the query looks like a registry name (contains \"/\" or \".\"), try exact lookup first.\n * Otherwise, search and return the first result.\n */\nexport async function findServer(query: string): Promise<ResolvedServer | null> {\n // If it looks like an exact registry name, try direct lookup\n if (query.includes('/') || query.includes('io.')) {\n try {\n const server = await getRegistryServer(query);\n return resolveServerConfig(server);\n } catch {\n // Fall through to search\n }\n }\n\n // Search the registry\n const results = await searchRegistry(query, { limit: 5 });\n if (results.servers.length === 0) {\n return null;\n }\n\n // Return the first match\n return resolveServerConfig(results.servers[0].server);\n}\n\n/**\n * Search the registry and return all matches for display.\n */\nexport async function searchServers(\n query: string,\n options?: { limit?: number },\n): Promise<ResolvedServer[]> {\n const results = await searchRegistry(query, { limit: options?.limit ?? DEFAULT_SEARCH_LIMIT });\n const resolved: ResolvedServer[] = [];\n\n for (const result of results.servers) {\n try {\n resolved.push(resolveServerConfig(result.server));\n } catch {\n // Skip servers that can't be resolved\n }\n }\n\n return resolved;\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/runtime/mcp-installer.ts","../src/runtime/mcp-registry.ts"],"sourcesContent":["import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'fs';\nimport { join } from 'path';\nimport YAML from 'yaml';\nimport type { HarnessConfig } from '../core/types.js';\nimport { loadConfig } from '../core/config.js';\nimport type { McpServerConfig } from './mcp.js';\nimport { createMcpManager, validateMcpConfig } from './mcp.js';\nimport type { ResolvedServer, RegistryEnvVar, RegistryServer, RegistrySearchResponse } from './mcp-registry.js';\nimport { findServer, searchServers, searchRegistry, getRegistryServer } from './mcp-registry.js';\nimport { log } from '../core/logger.js';\n\n// Re-export registry types and functions for consumers\nexport { searchRegistry, getRegistryServer };\nexport type { RegistryServer, RegistrySearchResponse, RegistryEnvVar, ResolvedServer };\nexport type { RegistryPackage, RegistryRemote, RegistrySearchResult } from './mcp-registry.js';\n\n// --- Types ---\n\n/** Result of installing an MCP server */\nexport interface McpInstallResult {\n /** Whether installation succeeded */\n installed: boolean;\n /** Server config name in config.yaml */\n name: string;\n /** The resolved server details */\n server?: ResolvedServer;\n /** Connection test results */\n connectionTest?: {\n connected: boolean;\n toolCount: number;\n toolNames: string[];\n error?: string;\n };\n /** Generated tool doc paths */\n generatedDocs: string[];\n /** Env vars that need user configuration */\n pendingEnvVars: RegistryEnvVar[];\n /** Error message if installation failed */\n error?: string;\n}\n\n/** Options for the install command */\nexport interface McpInstallOptions {\n /** Harness directory */\n dir: string;\n /** Skip connection testing */\n skipTest?: boolean;\n /** Skip tool doc generation */\n skipDocs?: boolean;\n /** Force overwrite if server already exists */\n force?: boolean;\n /** Custom name override for the server in config */\n name?: string;\n}\n\n// --- Config Update ---\n\nconst CONFIG_FILENAMES = ['config.yaml', 'config.yml', 'harness.yaml', 'harness.yml'];\n\n/**\n * Find the config file path in a harness directory.\n */\nfunction findConfigPath(dir: string): string {\n for (const filename of CONFIG_FILENAMES) {\n const configPath = join(dir, filename);\n if (existsSync(configPath)) {\n return configPath;\n }\n }\n return join(dir, 'config.yaml');\n}\n\n/**\n * Add or update an MCP server entry in the config file.\n * Preserves existing config structure and comments where possible.\n */\nexport function updateConfigWithServer(\n dir: string,\n serverName: string,\n serverConfig: McpServerConfig,\n): void {\n const configPath = findConfigPath(dir);\n const content = existsSync(configPath) ? readFileSync(configPath, 'utf-8') : '';\n\n // Parse existing YAML, preserving structure\n const doc = YAML.parseDocument(content);\n\n // Ensure mcp.servers exists\n if (!doc.has('mcp')) {\n doc.set('mcp', doc.createNode({ servers: {} }));\n }\n const mcp = doc.get('mcp') as YAML.YAMLMap;\n if (!mcp.has('servers')) {\n mcp.set('servers', doc.createNode({}));\n }\n const servers = mcp.get('servers') as YAML.YAMLMap;\n\n // Build the server config node\n const configNode: Record<string, unknown> = {\n transport: serverConfig.transport,\n };\n\n if (serverConfig.transport === 'stdio') {\n if (serverConfig.command) configNode['command'] = serverConfig.command;\n if (serverConfig.args && serverConfig.args.length > 0) configNode['args'] = serverConfig.args;\n if (serverConfig.env && Object.keys(serverConfig.env).length > 0) configNode['env'] = serverConfig.env;\n if (serverConfig.cwd) configNode['cwd'] = serverConfig.cwd;\n } else {\n if (serverConfig.url) configNode['url'] = serverConfig.url;\n if (serverConfig.headers && Object.keys(serverConfig.headers).length > 0) {\n configNode['headers'] = serverConfig.headers;\n }\n }\n\n // Set the server entry (add or overwrite)\n servers.set(serverName, doc.createNode(configNode));\n\n // Write back\n writeFileSync(configPath, doc.toString(), 'utf-8');\n}\n\n/**\n * Check if a server name already exists in the config.\n */\nexport function serverExistsInConfig(dir: string, serverName: string): boolean {\n try {\n const config = loadConfig(dir);\n return Boolean(config.mcp?.servers?.[serverName]);\n } catch {\n return false;\n }\n}\n\n// --- Tool Doc Generation ---\n\n/**\n * Generate a tools/*.md knowledge doc from a connected MCP server's tools.\n * Creates one file per MCP server with descriptions of all available tools.\n */\nexport function generateToolDocs(\n dir: string,\n serverName: string,\n toolNames: string[],\n description?: string,\n): string[] {\n const toolsDir = join(dir, 'tools');\n if (!existsSync(toolsDir)) {\n mkdirSync(toolsDir, { recursive: true });\n }\n\n const docPath = join(toolsDir, `${serverName}.md`);\n const lines: string[] = [\n '---',\n `id: tool-${serverName}`,\n `created: ${new Date().toISOString().split('T')[0]}`,\n `tags: [mcp, ${serverName}]`,\n '---',\n '',\n `# ${serverName} MCP Server`,\n '',\n ];\n\n if (description) {\n lines.push(description, '');\n }\n\n lines.push('## Available Tools', '');\n\n for (const toolName of toolNames) {\n lines.push(`- **${toolName}**`);\n }\n\n lines.push('', `> Auto-generated by \\`harness mcp install ${serverName}\\``);\n\n writeFileSync(docPath, lines.join('\\n'), 'utf-8');\n return [docPath];\n}\n\n// --- Connection Test ---\n\n/**\n * Test an MCP server connection and return tool info.\n */\nasync function testConnection(\n dir: string,\n serverName: string,\n serverConfig: McpServerConfig,\n): Promise<{ connected: boolean; toolCount: number; toolNames: string[]; error?: string }> {\n // Build a minimal config for testing just this server\n const config = loadConfig(dir);\n const testConfig: HarnessConfig = {\n ...config,\n mcp: { servers: { [serverName]: { ...serverConfig, enabled: serverConfig.enabled ?? true } } },\n };\n\n // Validate first\n const validationErrors = validateMcpConfig(testConfig);\n if (validationErrors.length > 0) {\n return {\n connected: false,\n toolCount: 0,\n toolNames: [],\n error: validationErrors.map((e) => e.error).join('; '),\n };\n }\n\n const manager = createMcpManager(testConfig);\n try {\n await manager.connect();\n const summaries = manager.getSummaries();\n const summary = summaries.find((s) => s.name === serverName);\n\n if (summary?.connected) {\n return {\n connected: true,\n toolCount: summary.toolCount,\n toolNames: summary.toolNames,\n };\n }\n\n return {\n connected: false,\n toolCount: 0,\n toolNames: [],\n error: summary?.error ?? 'Connection failed',\n };\n } catch (err) {\n return {\n connected: false,\n toolCount: 0,\n toolNames: [],\n error: err instanceof Error ? err.message : String(err),\n };\n } finally {\n await manager.close();\n }\n}\n\n// --- Main Install Flow ---\n\n/**\n * Install an MCP server by name or search query.\n *\n * Flow:\n * 1. Search the MCP registry for the server\n * 2. Resolve the best package/transport configuration\n * 3. Add/update the server entry in config.yaml\n * 4. Optionally test the connection\n * 5. Optionally generate tools/*.md knowledge docs\n */\nexport async function installMcpServer(\n query: string,\n options: McpInstallOptions,\n): Promise<McpInstallResult> {\n const { dir, skipTest, skipDocs, force, name: nameOverride } = options;\n\n // Step 1: Find the server in the registry\n log.info(`Searching MCP registry for \"${query}\"...`);\n let resolved: ResolvedServer | null;\n try {\n resolved = await findServer(query);\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n return {\n installed: false,\n name: query,\n generatedDocs: [],\n pendingEnvVars: [],\n error: `Registry lookup failed: ${message}`,\n };\n }\n\n if (!resolved) {\n return {\n installed: false,\n name: query,\n generatedDocs: [],\n pendingEnvVars: [],\n error: `No MCP server found matching \"${query}\" in the registry`,\n };\n }\n\n const serverName = nameOverride ?? resolved.name;\n\n // Step 2: Check if already exists\n if (!force && serverExistsInConfig(dir, serverName)) {\n return {\n installed: false,\n name: serverName,\n server: resolved,\n generatedDocs: [],\n pendingEnvVars: resolved.requiredEnv,\n error: `Server \"${serverName}\" already exists in config. Use --force to overwrite.`,\n };\n }\n\n // Step 3: Write to config.yaml\n log.info(`Adding \"${serverName}\" to config.yaml...`);\n updateConfigWithServer(dir, serverName, resolved.config);\n\n const result: McpInstallResult = {\n installed: true,\n name: serverName,\n server: resolved,\n generatedDocs: [],\n pendingEnvVars: resolved.requiredEnv,\n };\n\n // Step 4: Test connection (optional)\n if (!skipTest) {\n log.info(`Testing connection to \"${serverName}\"...`);\n result.connectionTest = await testConnection(dir, serverName, resolved.config);\n }\n\n // Step 5: Generate tool docs (optional)\n if (!skipDocs && result.connectionTest?.connected && result.connectionTest.toolNames.length > 0) {\n log.info(`Generating tool docs for \"${serverName}\"...`);\n result.generatedDocs = generateToolDocs(\n dir,\n serverName,\n result.connectionTest.toolNames,\n resolved.description,\n );\n }\n\n return result;\n}\n\n/**\n * List available servers from the registry for a given query.\n */\nexport async function listRegistryServers(\n query: string,\n options?: { limit?: number },\n): Promise<ResolvedServer[]> {\n return searchServers(query, options);\n}\n\n/**\n * Format a registry search result for CLI display.\n */\nexport function formatRegistryServer(entry: { server: RegistryServer }): string {\n const s = entry.server;\n const lines: string[] = [];\n\n lines.push(` ${s.name} (v${s.version})`);\n if (s.title) lines.push(` ${s.title}`);\n if (s.description) {\n const desc = s.description.length > 100 ? s.description.slice(0, 97) + '...' : s.description;\n lines.push(` ${desc}`);\n }\n\n // Show packages\n const npmPkgs = (s.packages ?? []).filter((p) => p.registryType === 'npm');\n const pypiPkgs = (s.packages ?? []).filter((p) => p.registryType === 'pypi');\n if (npmPkgs.length > 0) {\n lines.push(` npm: ${npmPkgs.map((p) => p.identifier).join(', ')}`);\n }\n if (pypiPkgs.length > 0) {\n lines.push(` pypi: ${pypiPkgs.map((p) => p.identifier).join(', ')}`);\n }\n\n // Show remotes\n if (s.remotes && s.remotes.length > 0) {\n lines.push(` remote: ${s.remotes[0].transportType} ${s.remotes[0].url}`);\n }\n\n // Show required env vars\n const allEnvVars = (s.packages ?? [])\n .flatMap((p) => p.environmentVariables ?? [])\n .filter((v) => v.isRequired);\n if (allEnvVars.length > 0) {\n lines.push(` requires: ${allEnvVars.map((v) => v.name).join(', ')}`);\n }\n\n return lines.join('\\n');\n}\n","import type { McpServerConfig } from './mcp.js';\n\n// --- Types ---\n\n/** Environment variable from registry server entry */\nexport interface RegistryEnvVar {\n name: string;\n description?: string;\n isRequired?: boolean;\n format?: string;\n}\n\n/** Package entry from registry server */\nexport interface RegistryPackage {\n registryType: 'npm' | 'pypi' | 'oci' | 'nuget' | 'mcpb';\n identifier: string;\n version: string;\n transport: { type: 'stdio' | 'streamable-http' | 'sse' };\n environmentVariables?: RegistryEnvVar[];\n runtimeHint?: 'npx' | 'uvx' | 'docker' | 'dnx';\n packageArguments?: unknown[];\n runtimeArguments?: unknown[];\n}\n\n/** Remote endpoint from registry server */\nexport interface RegistryRemote {\n transportType: 'streamable-http' | 'sse';\n url: string;\n headers?: Record<string, string>;\n}\n\n/** A single server entry from the MCP registry API */\nexport interface RegistryServer {\n $schema?: string;\n name: string;\n description?: string;\n title?: string;\n version: string;\n repository?: { url: string; source?: string; subfolder?: string };\n websiteUrl?: string;\n packages?: RegistryPackage[];\n remotes?: RegistryRemote[];\n}\n\n/** A server result from the registry search API */\nexport interface RegistrySearchResult {\n server: RegistryServer;\n _meta?: Record<string, unknown>;\n}\n\n/** Full search response from the registry */\nexport interface RegistrySearchResponse {\n servers: RegistrySearchResult[];\n metadata?: { nextCursor?: string; count?: number };\n}\n\n/** Resolved server config ready for installation */\nexport interface ResolvedServer {\n /** Display name for the server */\n name: string;\n /** Server description from registry */\n description?: string;\n /** Registry name (e.g. \"io.github.foo/bar\") */\n registryName: string;\n /** Source package info */\n package?: RegistryPackage;\n /** Source remote info */\n remote?: RegistryRemote;\n /** Generated harness config */\n config: McpServerConfig;\n /** Environment variables that need to be set */\n requiredEnv: RegistryEnvVar[];\n /** All environment variables (required + optional) */\n allEnv: RegistryEnvVar[];\n}\n\n// --- Constants ---\n\nconst REGISTRY_BASE = 'https://registry.modelcontextprotocol.io';\nconst REGISTRY_API_VERSION = 'v0.1';\nconst DEFAULT_SEARCH_LIMIT = 10;\n\n// --- Registry Client ---\n\n/**\n * Search the MCP registry for servers matching a query.\n */\nexport async function searchRegistry(\n query: string,\n options?: { limit?: number; cursor?: string },\n): Promise<RegistrySearchResponse> {\n const limit = options?.limit ?? DEFAULT_SEARCH_LIMIT;\n const params = new URLSearchParams({\n search: query,\n limit: String(limit),\n version: 'latest',\n });\n if (options?.cursor) {\n params.set('cursor', options.cursor);\n }\n\n const url = `${REGISTRY_BASE}/${REGISTRY_API_VERSION}/servers?${params}`;\n const response = await fetch(url);\n\n if (!response.ok) {\n throw new Error(`MCP registry search failed (${response.status}): ${response.statusText}`);\n }\n\n const data = (await response.json()) as RegistrySearchResponse;\n return data;\n}\n\n/**\n * Get a specific server by its registry name and version.\n * Name must be URL-encoded (e.g. \"io.github.foo/bar\" -> \"io.github.foo%2Fbar\").\n */\nexport async function getRegistryServer(\n name: string,\n version: string = 'latest',\n): Promise<RegistryServer> {\n const encodedName = encodeURIComponent(name);\n const encodedVersion = encodeURIComponent(version);\n const url = `${REGISTRY_BASE}/${REGISTRY_API_VERSION}/servers/${encodedName}/versions/${encodedVersion}`;\n const response = await fetch(url);\n\n if (!response.ok) {\n if (response.status === 404) {\n throw new Error(`MCP server \"${name}\" not found in registry`);\n }\n throw new Error(`MCP registry lookup failed (${response.status}): ${response.statusText}`);\n }\n\n const data = (await response.json()) as RegistryServer;\n return data;\n}\n\n// --- Runtime hint mapping ---\n\nconst RUNTIME_COMMANDS: Record<string, { command: string; prefix: string[] }> = {\n npx: { command: 'npx', prefix: ['-y'] },\n uvx: { command: 'uvx', prefix: [] },\n docker: { command: 'docker', prefix: ['run', '-i', '--rm'] },\n dnx: { command: 'dnx', prefix: [] },\n};\n\n/**\n * Derive a short config name from a registry server name.\n * \"io.github.foo/bar-server\" -> \"bar-server\"\n * \"io.github.foo/mcp-something\" -> \"mcp-something\"\n */\nexport function deriveConfigName(registryName: string): string {\n const parts = registryName.split('/');\n return parts[parts.length - 1];\n}\n\n/**\n * Resolve a registry server entry into a harness McpServerConfig.\n * Prefers npm stdio packages, falls back to pypi, then remotes.\n */\nexport function resolveServerConfig(server: RegistryServer): ResolvedServer {\n const name = deriveConfigName(server.name);\n\n // Strategy 1: Look for an npm stdio package\n const npmPkg = server.packages?.find((p) => p.registryType === 'npm' && p.transport.type === 'stdio');\n if (npmPkg) {\n return resolveStdioPackage(name, server, npmPkg, 'npx');\n }\n\n // Strategy 2: Look for any stdio package with runtimeHint\n const stdioPkg = server.packages?.find((p) => p.transport.type === 'stdio');\n if (stdioPkg) {\n const hint = stdioPkg.runtimeHint ?? (stdioPkg.registryType === 'pypi' ? 'uvx' : 'npx');\n return resolveStdioPackage(name, server, stdioPkg, hint);\n }\n\n // Strategy 3: Look for remotes (HTTP/SSE endpoints)\n if (server.remotes && server.remotes.length > 0) {\n const remote = server.remotes[0];\n return resolveRemote(name, server, remote);\n }\n\n // Strategy 4: Look for any package with HTTP transport\n const httpPkg = server.packages?.find(\n (p) => p.transport.type === 'streamable-http' || p.transport.type === 'sse',\n );\n if (httpPkg) {\n return resolveHttpPackage(name, server, httpPkg);\n }\n\n throw new Error(\n `Cannot resolve MCP server \"${server.name}\": no supported package or remote configuration found`,\n );\n}\n\nfunction resolveStdioPackage(\n name: string,\n server: RegistryServer,\n pkg: RegistryPackage,\n runtimeHint: string,\n): ResolvedServer {\n const runtime = RUNTIME_COMMANDS[runtimeHint] ?? RUNTIME_COMMANDS['npx'];\n const args = [...runtime.prefix, pkg.identifier];\n\n const envVars = pkg.environmentVariables ?? [];\n const env: Record<string, string> = {};\n for (const ev of envVars) {\n env[ev.name] = `\\${${ev.name}}`;\n }\n\n const config: McpServerConfig = {\n transport: 'stdio',\n command: runtime.command,\n args,\n ...(Object.keys(env).length > 0 ? { env } : {}),\n };\n\n return {\n name,\n description: server.description,\n registryName: server.name,\n package: pkg,\n config,\n requiredEnv: envVars.filter((e) => e.isRequired),\n allEnv: envVars,\n };\n}\n\nfunction resolveRemote(\n name: string,\n server: RegistryServer,\n remote: RegistryRemote,\n): ResolvedServer {\n const transport = remote.transportType === 'sse' ? 'sse' as const : 'http' as const;\n\n const config: McpServerConfig = {\n transport,\n url: remote.url,\n ...(remote.headers && Object.keys(remote.headers).length > 0 ? { headers: remote.headers } : {}),\n };\n\n return {\n name,\n description: server.description,\n registryName: server.name,\n remote,\n config,\n requiredEnv: [],\n allEnv: [],\n };\n}\n\nfunction resolveHttpPackage(\n name: string,\n server: RegistryServer,\n pkg: RegistryPackage,\n): ResolvedServer {\n // HTTP packages typically have a URL in the package identifier or need a remote\n const transport = pkg.transport.type === 'sse' ? 'sse' as const : 'http' as const;\n\n const envVars = pkg.environmentVariables ?? [];\n const config: McpServerConfig = {\n transport,\n url: pkg.identifier,\n };\n\n return {\n name,\n description: server.description,\n registryName: server.name,\n package: pkg,\n config,\n requiredEnv: envVars.filter((e) => e.isRequired),\n allEnv: envVars,\n };\n}\n\n/**\n * Search the registry and return the best match for a query.\n * If the query looks like a registry name (contains \"/\" or \".\"), try exact lookup first.\n * Otherwise, search and return the first result.\n */\nexport async function findServer(query: string): Promise<ResolvedServer | null> {\n // If it looks like an exact registry name, try direct lookup\n if (query.includes('/') || query.includes('io.')) {\n try {\n const server = await getRegistryServer(query);\n return resolveServerConfig(server);\n } catch {\n // Fall through to search\n }\n }\n\n // Search the registry\n const results = await searchRegistry(query, { limit: 5 });\n if (results.servers.length === 0) {\n return null;\n }\n\n // Return the first match\n return resolveServerConfig(results.servers[0].server);\n}\n\n/**\n * Search the registry and return all matches for display.\n */\nexport async function searchServers(\n query: string,\n options?: { limit?: number },\n): Promise<ResolvedServer[]> {\n const results = await searchRegistry(query, { limit: options?.limit ?? DEFAULT_SEARCH_LIMIT });\n const resolved: ResolvedServer[] = [];\n\n for (const result of results.servers) {\n try {\n resolved.push(resolveServerConfig(result.server));\n } catch {\n // Skip servers that can't be resolved\n }\n }\n\n return resolved;\n}\n"],"mappings":"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","names":[]}
@@ -8,7 +8,6 @@ import {
8
8
  recordRun,
9
9
  saveMetrics
10
10
  } from "./chunk-6EMOEYGU.js";
11
- import "./chunk-ZZJOFKAT.js";
12
11
  export {
13
12
  clearMetrics,
14
13
  getAllWorkflowStats,
@@ -17,4 +16,4 @@ export {
17
16
  recordRun,
18
17
  saveMetrics
19
18
  };
20
- //# sourceMappingURL=metrics-KXGNFAAB.js.map
19
+ //# sourceMappingURL=metrics-2MNINXNQ.js.map