mdcontext 0.1.0 → 0.2.0

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 (251) hide show
  1. package/.changeset/config.json +9 -9
  2. package/.claude/settings.local.json +25 -0
  3. package/.github/workflows/claude-code-review.yml +44 -0
  4. package/.github/workflows/claude.yml +85 -0
  5. package/CONTRIBUTING.md +186 -0
  6. package/NOTES/NOTES +44 -0
  7. package/README.md +206 -3
  8. package/biome.json +1 -1
  9. package/dist/chunk-23UPXDNL.js +3044 -0
  10. package/dist/chunk-2W7MO2DL.js +1366 -0
  11. package/dist/chunk-3NUAZGMA.js +1689 -0
  12. package/dist/chunk-7TOWB2XB.js +366 -0
  13. package/dist/chunk-7XOTOADQ.js +3065 -0
  14. package/dist/chunk-AH2PDM2K.js +3042 -0
  15. package/dist/chunk-BNXWSZ63.js +3742 -0
  16. package/dist/chunk-BTL5DJVU.js +3222 -0
  17. package/dist/chunk-HDHYG7E4.js +104 -0
  18. package/dist/chunk-HLR4KZBP.js +3234 -0
  19. package/dist/chunk-IP3FRFEB.js +1045 -0
  20. package/dist/chunk-KHU56VDO.js +3042 -0
  21. package/dist/chunk-KRYIFLQR.js +85 -89
  22. package/dist/chunk-LBSDNLEM.js +287 -0
  23. package/dist/chunk-MNTQ7HCP.js +2643 -0
  24. package/dist/chunk-MUJELQQ6.js +1387 -0
  25. package/dist/chunk-MXJGMSLV.js +2199 -0
  26. package/dist/chunk-N6QJGC3Z.js +2636 -0
  27. package/dist/chunk-OBELGBPM.js +1713 -0
  28. package/dist/chunk-OT7R5XTA.js +3192 -0
  29. package/dist/chunk-P7X4RA2T.js +106 -0
  30. package/dist/chunk-PIDUQNC2.js +3185 -0
  31. package/dist/chunk-POGCDIH4.js +3187 -0
  32. package/dist/chunk-PSIEOQGZ.js +3043 -0
  33. package/dist/chunk-PVRT3IHA.js +3238 -0
  34. package/dist/chunk-QNN4TT23.js +1430 -0
  35. package/dist/chunk-RE3R45RJ.js +3042 -0
  36. package/dist/chunk-S7E6TFX6.js +718 -657
  37. package/dist/chunk-SG6GLU4U.js +1378 -0
  38. package/dist/chunk-SJCDV2ST.js +274 -0
  39. package/dist/chunk-SYE5XLF3.js +104 -0
  40. package/dist/chunk-T5VLYBZD.js +103 -0
  41. package/dist/chunk-TOQB7VWU.js +3238 -0
  42. package/dist/chunk-VFNMZ4ZQ.js +3228 -0
  43. package/dist/chunk-VVTGZNBT.js +1533 -1423
  44. package/dist/chunk-W7Q4RFEV.js +104 -0
  45. package/dist/chunk-XTYYVRLO.js +3190 -0
  46. package/dist/chunk-Y6MDYVJD.js +3063 -0
  47. package/dist/cli/main.js +4072 -629
  48. package/dist/index.d.ts +420 -33
  49. package/dist/index.js +8 -15
  50. package/dist/mcp/server.js +103 -7
  51. package/dist/schema-BAWSG7KY.js +22 -0
  52. package/dist/schema-E3QUPL26.js +20 -0
  53. package/dist/schema-EHL7WUT6.js +20 -0
  54. package/docs/019-USAGE.md +44 -5
  55. package/docs/020-current-implementation.md +8 -8
  56. package/docs/021-DOGFOODING-FINDINGS.md +1 -1
  57. package/docs/CONFIG.md +1123 -0
  58. package/docs/ERRORS.md +383 -0
  59. package/docs/summarization.md +320 -0
  60. package/justfile +40 -0
  61. package/package.json +39 -33
  62. package/research/INDEX.md +315 -0
  63. package/research/code-review/README.md +90 -0
  64. package/research/code-review/cli-error-handling-review.md +979 -0
  65. package/research/code-review/code-review-validation-report.md +464 -0
  66. package/research/code-review/main-ts-review.md +1128 -0
  67. package/research/config-docs/SUMMARY.md +357 -0
  68. package/research/config-docs/TEST-RESULTS.md +776 -0
  69. package/research/config-docs/TODO.md +542 -0
  70. package/research/config-docs/analysis.md +744 -0
  71. package/research/config-docs/fix-validation.md +502 -0
  72. package/research/config-docs/help-audit.md +264 -0
  73. package/research/config-docs/help-system-analysis.md +890 -0
  74. package/research/frontmatter/COMMENTS-ARE-SKIPPED.md +149 -0
  75. package/research/frontmatter/LLM-CODE-NAVIGATION.md +276 -0
  76. package/research/issue-review.md +603 -0
  77. package/research/llm-summarization/agent-cli-tools-2026.md +1082 -0
  78. package/research/llm-summarization/alternative-providers-2026.md +1428 -0
  79. package/research/llm-summarization/anthropic-2026.md +367 -0
  80. package/research/llm-summarization/claude-cli-integration.md +1706 -0
  81. package/research/llm-summarization/cli-integration-patterns.md +3155 -0
  82. package/research/llm-summarization/openai-2026.md +473 -0
  83. package/research/llm-summarization/openai-compatible-providers-2026.md +1022 -0
  84. package/research/llm-summarization/opencode-cli-integration.md +1552 -0
  85. package/research/llm-summarization/prompt-engineering-2026.md +1426 -0
  86. package/research/llm-summarization/prototype-results.md +56 -0
  87. package/research/llm-summarization/provider-switching-patterns-2026.md +2153 -0
  88. package/research/llm-summarization/typescript-llm-libraries-2026.md +2436 -0
  89. package/research/mdcontext-pudding/00-EXECUTIVE-SUMMARY.md +282 -0
  90. package/research/mdcontext-pudding/01-index-embed.md +956 -0
  91. package/research/mdcontext-pudding/02-search-COMMANDS.md +142 -0
  92. package/research/mdcontext-pudding/02-search-SUMMARY.md +146 -0
  93. package/research/mdcontext-pudding/02-search.md +970 -0
  94. package/research/mdcontext-pudding/03-context.md +779 -0
  95. package/research/mdcontext-pudding/04-navigation-and-analytics.md +803 -0
  96. package/research/mdcontext-pudding/04-tree.md +704 -0
  97. package/research/mdcontext-pudding/05-config.md +1038 -0
  98. package/research/mdcontext-pudding/06-links-summary.txt +87 -0
  99. package/research/mdcontext-pudding/06-links.md +679 -0
  100. package/research/mdcontext-pudding/07-stats.md +693 -0
  101. package/research/mdcontext-pudding/BUG-FIX-PLAN.md +388 -0
  102. package/research/mdcontext-pudding/P0-BUG-VALIDATION.md +167 -0
  103. package/research/mdcontext-pudding/README.md +168 -0
  104. package/research/mdcontext-pudding/TESTING-SUMMARY.md +128 -0
  105. package/research/research-quality-review.md +834 -0
  106. package/research/semantic-search/embedding-text-analysis.md +156 -0
  107. package/research/semantic-search/multi-word-failure-reproduction.md +171 -0
  108. package/research/semantic-search/query-processing-analysis.md +207 -0
  109. package/research/semantic-search/root-cause-and-solution.md +114 -0
  110. package/research/semantic-search/threshold-validation-report.md +69 -0
  111. package/research/semantic-search/vector-search-analysis.md +63 -0
  112. package/research/test-path-issues.md +276 -0
  113. package/review/ALP-76/1-error-type-design.md +962 -0
  114. package/review/ALP-76/2-error-handling-patterns.md +906 -0
  115. package/review/ALP-76/3-error-presentation.md +624 -0
  116. package/review/ALP-76/4-test-coverage.md +625 -0
  117. package/review/ALP-76/5-migration-completeness.md +440 -0
  118. package/review/ALP-76/6-effect-best-practices.md +755 -0
  119. package/scripts/apply-branch-protection.sh +47 -0
  120. package/scripts/branch-protection-templates.json +79 -0
  121. package/scripts/prototype-summarization.ts +346 -0
  122. package/scripts/rebuild-hnswlib.js +32 -37
  123. package/scripts/setup-branch-protection.sh +64 -0
  124. package/src/__tests__/fixtures/semantic-search/multi-word-corpus/.mdcontext/active-provider.json +7 -0
  125. package/src/__tests__/fixtures/semantic-search/multi-word-corpus/.mdcontext/bm25.json +541 -0
  126. package/src/__tests__/fixtures/semantic-search/multi-word-corpus/.mdcontext/bm25.meta.json +5 -0
  127. package/src/__tests__/fixtures/semantic-search/multi-word-corpus/.mdcontext/config.json +8 -0
  128. package/src/__tests__/fixtures/semantic-search/multi-word-corpus/.mdcontext/embeddings/openai_text-embedding-3-small_512/vectors.bin +0 -0
  129. package/src/__tests__/fixtures/semantic-search/multi-word-corpus/.mdcontext/embeddings/openai_text-embedding-3-small_512/vectors.meta.bin +0 -0
  130. package/src/__tests__/fixtures/semantic-search/multi-word-corpus/.mdcontext/indexes/documents.json +60 -0
  131. package/src/__tests__/fixtures/semantic-search/multi-word-corpus/.mdcontext/indexes/links.json +13 -0
  132. package/src/__tests__/fixtures/semantic-search/multi-word-corpus/.mdcontext/indexes/sections.json +1197 -0
  133. package/src/__tests__/fixtures/semantic-search/multi-word-corpus/configuration-management.md +99 -0
  134. package/src/__tests__/fixtures/semantic-search/multi-word-corpus/distributed-systems.md +92 -0
  135. package/src/__tests__/fixtures/semantic-search/multi-word-corpus/error-handling.md +78 -0
  136. package/src/__tests__/fixtures/semantic-search/multi-word-corpus/failure-automation.md +55 -0
  137. package/src/__tests__/fixtures/semantic-search/multi-word-corpus/job-context.md +69 -0
  138. package/src/__tests__/fixtures/semantic-search/multi-word-corpus/process-orchestration.md +99 -0
  139. package/src/cli/argv-preprocessor.test.ts +2 -2
  140. package/src/cli/cli.test.ts +230 -33
  141. package/src/cli/commands/config-cmd.ts +642 -0
  142. package/src/cli/commands/context.ts +97 -9
  143. package/src/cli/commands/duplicates.ts +122 -0
  144. package/src/cli/commands/embeddings.ts +529 -0
  145. package/src/cli/commands/index-cmd.ts +210 -30
  146. package/src/cli/commands/index.ts +3 -0
  147. package/src/cli/commands/search.ts +894 -64
  148. package/src/cli/commands/stats.ts +3 -0
  149. package/src/cli/commands/tree.ts +26 -5
  150. package/src/cli/config-layer.ts +176 -0
  151. package/src/cli/error-handler.test.ts +235 -0
  152. package/src/cli/error-handler.ts +655 -0
  153. package/src/cli/flag-schemas.ts +66 -0
  154. package/src/cli/help.ts +209 -7
  155. package/src/cli/main.ts +348 -58
  156. package/src/cli/options.ts +10 -0
  157. package/src/cli/shared-error-handling.ts +199 -0
  158. package/src/cli/utils.ts +150 -17
  159. package/src/config/file-provider.test.ts +320 -0
  160. package/src/config/file-provider.ts +273 -0
  161. package/src/config/index.ts +72 -0
  162. package/src/config/integration.test.ts +667 -0
  163. package/src/config/precedence.test.ts +277 -0
  164. package/src/config/precedence.ts +451 -0
  165. package/src/config/schema.test.ts +414 -0
  166. package/src/config/schema.ts +603 -0
  167. package/src/config/service.test.ts +320 -0
  168. package/src/config/service.ts +243 -0
  169. package/src/config/testing.test.ts +264 -0
  170. package/src/config/testing.ts +110 -0
  171. package/src/core/types.ts +6 -33
  172. package/src/duplicates/detector.test.ts +183 -0
  173. package/src/duplicates/detector.ts +414 -0
  174. package/src/duplicates/index.ts +18 -0
  175. package/src/embeddings/embedding-namespace.test.ts +300 -0
  176. package/src/embeddings/embedding-namespace.ts +947 -0
  177. package/src/embeddings/heading-boost.test.ts +222 -0
  178. package/src/embeddings/hnsw-build-options.test.ts +198 -0
  179. package/src/embeddings/hyde.test.ts +272 -0
  180. package/src/embeddings/hyde.ts +264 -0
  181. package/src/embeddings/index.ts +2 -0
  182. package/src/embeddings/openai-provider.ts +332 -83
  183. package/src/embeddings/pricing.json +22 -0
  184. package/src/embeddings/provider-constants.ts +204 -0
  185. package/src/embeddings/provider-errors.test.ts +967 -0
  186. package/src/embeddings/provider-errors.ts +565 -0
  187. package/src/embeddings/provider-factory.test.ts +240 -0
  188. package/src/embeddings/provider-factory.ts +225 -0
  189. package/src/embeddings/provider-integration.test.ts +788 -0
  190. package/src/embeddings/query-preprocessing.test.ts +187 -0
  191. package/src/embeddings/semantic-search-threshold.test.ts +508 -0
  192. package/src/embeddings/semantic-search.ts +780 -93
  193. package/src/embeddings/types.ts +293 -16
  194. package/src/embeddings/vector-store.ts +486 -77
  195. package/src/embeddings/voyage-provider.ts +313 -0
  196. package/src/errors/errors.test.ts +845 -0
  197. package/src/errors/index.ts +533 -0
  198. package/src/index/ignore-patterns.test.ts +354 -0
  199. package/src/index/ignore-patterns.ts +305 -0
  200. package/src/index/indexer.ts +286 -48
  201. package/src/index/storage.ts +94 -30
  202. package/src/index/types.ts +40 -2
  203. package/src/index/watcher.ts +67 -9
  204. package/src/index.ts +22 -0
  205. package/src/integration/search-keyword.test.ts +678 -0
  206. package/src/mcp/server.ts +135 -6
  207. package/src/parser/parser.ts +18 -19
  208. package/src/parser/section-filter.test.ts +277 -0
  209. package/src/parser/section-filter.ts +125 -3
  210. package/src/search/__tests__/hybrid-search.test.ts +650 -0
  211. package/src/search/bm25-store.ts +366 -0
  212. package/src/search/cross-encoder.test.ts +253 -0
  213. package/src/search/cross-encoder.ts +406 -0
  214. package/src/search/fuzzy-search.test.ts +419 -0
  215. package/src/search/fuzzy-search.ts +273 -0
  216. package/src/search/hybrid-search.ts +448 -0
  217. package/src/search/path-matcher.test.ts +276 -0
  218. package/src/search/path-matcher.ts +33 -0
  219. package/src/search/searcher.test.ts +99 -1
  220. package/src/search/searcher.ts +189 -67
  221. package/src/search/wink-bm25.d.ts +30 -0
  222. package/src/summarization/cli-providers/claude.ts +202 -0
  223. package/src/summarization/cli-providers/detection.test.ts +273 -0
  224. package/src/summarization/cli-providers/detection.ts +118 -0
  225. package/src/summarization/cli-providers/index.ts +8 -0
  226. package/src/summarization/cost.test.ts +139 -0
  227. package/src/summarization/cost.ts +102 -0
  228. package/src/summarization/error-handler.test.ts +127 -0
  229. package/src/summarization/error-handler.ts +111 -0
  230. package/src/summarization/index.ts +102 -0
  231. package/src/summarization/pipeline.test.ts +498 -0
  232. package/src/summarization/pipeline.ts +231 -0
  233. package/src/summarization/prompts.test.ts +269 -0
  234. package/src/summarization/prompts.ts +133 -0
  235. package/src/summarization/provider-factory.test.ts +396 -0
  236. package/src/summarization/provider-factory.ts +178 -0
  237. package/src/summarization/types.ts +184 -0
  238. package/src/summarize/summarizer.ts +104 -35
  239. package/src/types/huggingface-transformers.d.ts +66 -0
  240. package/tests/fixtures/cli/.mdcontext/active-provider.json +7 -0
  241. package/tests/fixtures/cli/.mdcontext/embeddings/openai_text-embedding-3-small_512/vectors.bin +0 -0
  242. package/tests/fixtures/cli/.mdcontext/embeddings/openai_text-embedding-3-small_512/vectors.meta.bin +0 -0
  243. package/tests/fixtures/cli/.mdcontext/indexes/documents.json +4 -4
  244. package/tests/fixtures/cli/.mdcontext/indexes/sections.json +14 -0
  245. package/tests/integration/embed-index.test.ts +712 -0
  246. package/tests/integration/search-context.test.ts +469 -0
  247. package/tests/integration/search-semantic.test.ts +522 -0
  248. package/vitest.config.ts +1 -6
  249. package/AGENTS.md +0 -46
  250. package/tests/fixtures/cli/.mdcontext/vectors.bin +0 -0
  251. package/tests/fixtures/cli/.mdcontext/vectors.meta.json +0 -1264
@@ -0,0 +1,47 @@
1
+ #!/bin/bash
2
+ # Apply branch protection from a template
3
+ # Usage: ./apply-branch-protection.sh owner/repo template [branch]
4
+ #
5
+ # Templates: full-ci-matrix, simple, minimal, strict
6
+ #
7
+ # Examples:
8
+ # ./apply-branch-protection.sh mdcontext/mdcontext full-ci-matrix
9
+ # ./apply-branch-protection.sh myorg/myrepo simple main
10
+ # ./apply-branch-protection.sh myorg/myrepo minimal develop
11
+
12
+ set -e
13
+
14
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
15
+ TEMPLATES_FILE="$SCRIPT_DIR/branch-protection-templates.json"
16
+
17
+ REPO="${1:?Usage: $0 owner/repo template [branch]}"
18
+ TEMPLATE="${2:?Usage: $0 owner/repo template [branch] (templates: full-ci-matrix, simple, minimal, strict)}"
19
+ BRANCH="${3:-main}"
20
+
21
+ if [[ ! -f "$TEMPLATES_FILE" ]]; then
22
+ echo "Error: Templates file not found at $TEMPLATES_FILE"
23
+ exit 1
24
+ fi
25
+
26
+ # Extract template (remove description field for API)
27
+ CONFIG=$(jq -r ".[\"$TEMPLATE\"] | del(.description)" "$TEMPLATES_FILE")
28
+
29
+ if [[ "$CONFIG" == "null" ]]; then
30
+ echo "Error: Template '$TEMPLATE' not found"
31
+ echo "Available templates:"
32
+ jq -r 'keys[]' "$TEMPLATES_FILE"
33
+ exit 1
34
+ fi
35
+
36
+ DESCRIPTION=$(jq -r ".[\"$TEMPLATE\"].description // \"\"" "$TEMPLATES_FILE")
37
+
38
+ echo "Applying branch protection to $REPO ($BRANCH branch)"
39
+ echo "Template: $TEMPLATE"
40
+ [[ -n "$DESCRIPTION" ]] && echo " $DESCRIPTION"
41
+ echo ""
42
+
43
+ echo "$CONFIG" | gh api "repos/$REPO/branches/$BRANCH/protection" -X PUT --input - > /dev/null
44
+
45
+ echo "Done! Branch protection configured."
46
+ echo ""
47
+ echo "View settings: https://github.com/$REPO/settings/branches"
@@ -0,0 +1,79 @@
1
+ {
2
+ "full-ci-matrix": {
3
+ "description": "For projects with quality + multi-platform test matrix (Node 20/22, ubuntu/macos/windows)",
4
+ "required_status_checks": {
5
+ "strict": true,
6
+ "contexts": [
7
+ "quality",
8
+ "test (ubuntu-latest, 20)",
9
+ "test (ubuntu-latest, 22)",
10
+ "test (macos-latest, 20)",
11
+ "test (macos-latest, 22)",
12
+ "test (windows-latest, 20)",
13
+ "test (windows-latest, 22)"
14
+ ]
15
+ },
16
+ "enforce_admins": false,
17
+ "required_pull_request_reviews": {
18
+ "required_approving_review_count": 1,
19
+ "dismiss_stale_reviews": true,
20
+ "require_code_owner_reviews": false
21
+ },
22
+ "restrictions": null,
23
+ "required_linear_history": true,
24
+ "allow_force_pushes": false,
25
+ "allow_deletions": false
26
+ },
27
+
28
+ "simple": {
29
+ "description": "For simple projects with build + test jobs",
30
+ "required_status_checks": {
31
+ "strict": true,
32
+ "contexts": ["build", "test"]
33
+ },
34
+ "enforce_admins": false,
35
+ "required_pull_request_reviews": {
36
+ "required_approving_review_count": 1,
37
+ "dismiss_stale_reviews": true,
38
+ "require_code_owner_reviews": false
39
+ },
40
+ "restrictions": null,
41
+ "required_linear_history": true,
42
+ "allow_force_pushes": false,
43
+ "allow_deletions": false
44
+ },
45
+
46
+ "minimal": {
47
+ "description": "Minimal protection - just require PR reviews",
48
+ "required_status_checks": null,
49
+ "enforce_admins": false,
50
+ "required_pull_request_reviews": {
51
+ "required_approving_review_count": 1,
52
+ "dismiss_stale_reviews": true,
53
+ "require_code_owner_reviews": false
54
+ },
55
+ "restrictions": null,
56
+ "required_linear_history": false,
57
+ "allow_force_pushes": false,
58
+ "allow_deletions": false
59
+ },
60
+
61
+ "strict": {
62
+ "description": "Strict protection with admin enforcement and code owners",
63
+ "required_status_checks": {
64
+ "strict": true,
65
+ "contexts": ["ci"]
66
+ },
67
+ "enforce_admins": true,
68
+ "required_pull_request_reviews": {
69
+ "required_approving_review_count": 2,
70
+ "dismiss_stale_reviews": true,
71
+ "require_code_owner_reviews": true,
72
+ "require_last_push_approval": true
73
+ },
74
+ "restrictions": null,
75
+ "required_linear_history": true,
76
+ "allow_force_pushes": false,
77
+ "allow_deletions": false
78
+ }
79
+ }
@@ -0,0 +1,346 @@
1
+ #!/usr/bin/env npx tsx
2
+ /**
3
+ * Prototype: AI Summarization of Search Results
4
+ *
5
+ * Quick validation script to test the summarization approach.
6
+ * Uses the actual mdcontext search and Claude CLI integration.
7
+ *
8
+ * Usage:
9
+ * npx tsx scripts/prototype-summarization.ts
10
+ * npx tsx scripts/prototype-summarization.ts "your query here"
11
+ */
12
+
13
+ import { spawn } from 'node:child_process'
14
+ import * as path from 'node:path'
15
+ import * as fs from 'node:fs'
16
+
17
+ // Test queries to validate the approach
18
+ const TEST_QUERIES = [
19
+ 'error handling',
20
+ 'configuration',
21
+ 'search',
22
+ 'embeddings',
23
+ 'Effect patterns',
24
+ ]
25
+
26
+ interface SearchResult {
27
+ path: string
28
+ heading: string
29
+ score?: number
30
+ similarity?: number
31
+ }
32
+
33
+ interface SummaryResult {
34
+ query: string
35
+ searchResults: SearchResult[]
36
+ summary: string
37
+ durationMs: number
38
+ provider: string
39
+ }
40
+
41
+ /**
42
+ * Run mdcontext search and get JSON results
43
+ */
44
+ async function runSearch(query: string, dir: string): Promise<SearchResult[]> {
45
+ return new Promise((resolve, reject) => {
46
+ const proc = spawn('node', [
47
+ path.join(dir, 'dist/cli/main.js'),
48
+ 'search',
49
+ query,
50
+ dir,
51
+ '--json',
52
+ '--limit', '5',
53
+ ], {
54
+ stdio: ['ignore', 'pipe', 'pipe'],
55
+ })
56
+
57
+ let stdout = ''
58
+ let stderr = ''
59
+
60
+ proc.stdout.on('data', (data: Buffer) => {
61
+ stdout += data.toString()
62
+ })
63
+
64
+ proc.stderr.on('data', (data: Buffer) => {
65
+ stderr += data.toString()
66
+ })
67
+
68
+ proc.on('close', (code: number | null) => {
69
+ if (code !== 0) {
70
+ // If search fails (no index), return empty results
71
+ console.log(` Search returned code ${code}, using empty results`)
72
+ resolve([])
73
+ return
74
+ }
75
+
76
+ try {
77
+ const parsed = JSON.parse(stdout)
78
+ const results = (parsed.results || []).map((r: any) => ({
79
+ path: r.path,
80
+ heading: r.heading,
81
+ score: r.score,
82
+ similarity: r.similarity,
83
+ }))
84
+ resolve(results)
85
+ } catch (e) {
86
+ console.log(` Failed to parse search results: ${e}`)
87
+ resolve([])
88
+ }
89
+ })
90
+
91
+ proc.on('error', (err) => {
92
+ reject(err)
93
+ })
94
+ })
95
+ }
96
+
97
+ /**
98
+ * Format search results for LLM input
99
+ */
100
+ function formatResultsForLLM(query: string, results: SearchResult[]): string {
101
+ if (results.length === 0) {
102
+ return `Query: "${query}"\n\nNo search results found. The index may need to be built.`
103
+ }
104
+
105
+ const resultsText = results.map((r, i) => {
106
+ const score = r.similarity
107
+ ? `${(r.similarity * 100).toFixed(1)}% similarity`
108
+ : r.score
109
+ ? `score: ${r.score.toFixed(2)}`
110
+ : ''
111
+ return `${i + 1}. ${r.path}\n Section: ${r.heading}\n ${score}`
112
+ }).join('\n\n')
113
+
114
+ return `Query: "${query}"
115
+
116
+ Search Results (${results.length} found):
117
+
118
+ ${resultsText}
119
+
120
+ Please provide a concise summary of what these results tell us about "${query}" in this codebase.
121
+ Focus on:
122
+ 1. Key patterns and approaches found
123
+ 2. Important files to look at
124
+ 3. Actionable next steps for the developer`
125
+ }
126
+
127
+ /**
128
+ * Call Claude CLI for summarization
129
+ * SECURITY: Uses spawn() with argument arrays - never exec() with strings
130
+ */
131
+ async function callClaudeCLI(prompt: string): Promise<{ summary: string; durationMs: number }> {
132
+ const startTime = Date.now()
133
+
134
+ return new Promise((resolve, reject) => {
135
+ // SECURITY: spawn() with argument array - safe from shell injection
136
+ const proc = spawn('claude', [
137
+ '-p', prompt,
138
+ '--output-format', 'text',
139
+ ], {
140
+ stdio: ['ignore', 'pipe', 'pipe'],
141
+ })
142
+
143
+ let stdout = ''
144
+ let stderr = ''
145
+
146
+ proc.stdout.on('data', (data: Buffer) => {
147
+ stdout += data.toString()
148
+ })
149
+
150
+ proc.stderr.on('data', (data: Buffer) => {
151
+ stderr += data.toString()
152
+ })
153
+
154
+ // Timeout after 60 seconds
155
+ const timeout = setTimeout(() => {
156
+ proc.kill('SIGTERM')
157
+ reject(new Error('Claude CLI timeout after 60s'))
158
+ }, 60000)
159
+
160
+ proc.on('close', (code: number | null) => {
161
+ clearTimeout(timeout)
162
+ const durationMs = Date.now() - startTime
163
+
164
+ if (code !== 0) {
165
+ reject(new Error(`Claude CLI exited with code ${code}: ${stderr}`))
166
+ return
167
+ }
168
+
169
+ resolve({
170
+ summary: stdout.trim(),
171
+ durationMs,
172
+ })
173
+ })
174
+
175
+ proc.on('error', (err) => {
176
+ clearTimeout(timeout)
177
+ reject(new Error(`Failed to spawn Claude CLI: ${err.message}`))
178
+ })
179
+ })
180
+ }
181
+
182
+ /**
183
+ * Check if Claude CLI is available
184
+ */
185
+ async function checkClaudeCLI(): Promise<boolean> {
186
+ return new Promise((resolve) => {
187
+ const proc = spawn('which', ['claude'], {
188
+ stdio: ['ignore', 'pipe', 'ignore'],
189
+ })
190
+
191
+ proc.on('close', (code) => {
192
+ resolve(code === 0)
193
+ })
194
+
195
+ proc.on('error', () => {
196
+ resolve(false)
197
+ })
198
+ })
199
+ }
200
+
201
+ /**
202
+ * Run a single test query
203
+ */
204
+ async function testQuery(query: string, projectDir: string): Promise<SummaryResult> {
205
+ console.log(`\n${'='.repeat(60)}`)
206
+ console.log(`Query: "${query}"`)
207
+ console.log('='.repeat(60))
208
+
209
+ // 1. Run search
210
+ console.log('\n1. Running search...')
211
+ const searchResults = await runSearch(query, projectDir)
212
+ console.log(` Found ${searchResults.length} results`)
213
+
214
+ // 2. Format for LLM
215
+ const prompt = formatResultsForLLM(query, searchResults)
216
+
217
+ // 3. Call Claude CLI
218
+ console.log('\n2. Calling Claude CLI for summary...')
219
+ const { summary, durationMs } = await callClaudeCLI(prompt)
220
+
221
+ // 4. Display result
222
+ console.log(`\n3. Summary (${durationMs}ms):`)
223
+ console.log('-'.repeat(40))
224
+ console.log(summary)
225
+ console.log('-'.repeat(40))
226
+
227
+ return {
228
+ query,
229
+ searchResults,
230
+ summary,
231
+ durationMs,
232
+ provider: 'claude',
233
+ }
234
+ }
235
+
236
+ /**
237
+ * Main prototype function
238
+ */
239
+ async function main() {
240
+ const projectDir = path.resolve(import.meta.dirname, '..')
241
+ console.log('Prototype: AI Summarization of Search Results')
242
+ console.log(`Project: ${projectDir}`)
243
+
244
+ // Check Claude CLI
245
+ const hasClaudeCLI = await checkClaudeCLI()
246
+ if (!hasClaudeCLI) {
247
+ console.error('\nError: Claude CLI not found.')
248
+ console.error('Install it from: https://claude.ai/download')
249
+ process.exit(1)
250
+ }
251
+ console.log('Claude CLI: available')
252
+
253
+ // Get query from args or use test queries
254
+ const args = process.argv.slice(2)
255
+ const queries = args.length > 0 ? args : TEST_QUERIES
256
+
257
+ // Run tests
258
+ const results: SummaryResult[] = []
259
+ for (const query of queries) {
260
+ try {
261
+ const result = await testQuery(query, projectDir)
262
+ results.push(result)
263
+ } catch (err) {
264
+ console.error(`\nError testing "${query}":`, err)
265
+ }
266
+ }
267
+
268
+ // Summary
269
+ console.log('\n' + '='.repeat(60))
270
+ console.log('PROTOTYPE RESULTS SUMMARY')
271
+ console.log('='.repeat(60))
272
+ console.log(`\nQueries tested: ${results.length}`)
273
+ console.log(`Total duration: ${results.reduce((sum, r) => sum + r.durationMs, 0)}ms`)
274
+ console.log(`Average duration: ${Math.round(results.reduce((sum, r) => sum + r.durationMs, 0) / results.length)}ms`)
275
+
276
+ // Write results to file
277
+ const outputPath = path.join(projectDir, 'research/llm-summarization/prototype-results.md')
278
+ const outputDir = path.dirname(outputPath)
279
+
280
+ if (!fs.existsSync(outputDir)) {
281
+ fs.mkdirSync(outputDir, { recursive: true })
282
+ }
283
+
284
+ const markdown = generateResultsMarkdown(results)
285
+ fs.writeFileSync(outputPath, markdown)
286
+ console.log(`\nResults written to: ${outputPath}`)
287
+ }
288
+
289
+ /**
290
+ * Generate markdown report from results
291
+ */
292
+ function generateResultsMarkdown(results: SummaryResult[]): string {
293
+ const timestamp = new Date().toISOString()
294
+ const totalDuration = results.reduce((sum, r) => sum + r.durationMs, 0)
295
+ const avgDuration = Math.round(totalDuration / results.length)
296
+
297
+ let md = `# Prototype Results: AI Summarization
298
+
299
+ Generated: ${timestamp}
300
+
301
+ ## Summary
302
+
303
+ - **Queries tested:** ${results.length}
304
+ - **Total duration:** ${totalDuration}ms
305
+ - **Average duration:** ${avgDuration}ms
306
+ - **Provider:** Claude CLI (free with subscription)
307
+
308
+ ## Results
309
+
310
+ `
311
+
312
+ for (const result of results) {
313
+ md += `### Query: "${result.query}"
314
+
315
+ **Search Results:** ${result.searchResults.length} found
316
+ **Duration:** ${result.durationMs}ms
317
+
318
+ #### Summary
319
+
320
+ ${result.summary}
321
+
322
+ ---
323
+
324
+ `
325
+ }
326
+
327
+ md += `## Findings
328
+
329
+ ### What Works Well
330
+
331
+ - [ ] Add observations here after running prototype
332
+
333
+ ### Issues Discovered
334
+
335
+ - [ ] Add issues here after running prototype
336
+
337
+ ### Recommendations
338
+
339
+ - [ ] Add recommendations here after running prototype
340
+ `
341
+
342
+ return md
343
+ }
344
+
345
+ // Run
346
+ main().catch(console.error)
@@ -7,57 +7,52 @@ import { existsSync, readdirSync, statSync } from "node:fs";
7
7
  import { join } from "node:path";
8
8
 
9
9
  function findHnswlibDir() {
10
- const pnpmDir = join(process.cwd(), "node_modules", ".pnpm");
11
-
12
- if (!existsSync(pnpmDir)) {
13
- return null;
14
- }
15
-
16
- const entries = readdirSync(pnpmDir);
17
- for (const entry of entries) {
18
- if (entry.startsWith("hnswlib-node@")) {
19
- const candidate = join(
20
- pnpmDir,
21
- entry,
22
- "node_modules",
23
- "hnswlib-node"
24
- );
25
- if (existsSync(candidate) && statSync(candidate).isDirectory()) {
26
- return candidate;
27
- }
28
- }
29
- }
30
-
31
- return null;
10
+ const pnpmDir = join(process.cwd(), "node_modules", ".pnpm");
11
+
12
+ if (!existsSync(pnpmDir)) {
13
+ return null;
14
+ }
15
+
16
+ const entries = readdirSync(pnpmDir);
17
+ for (const entry of entries) {
18
+ if (entry.startsWith("hnswlib-node@")) {
19
+ const candidate = join(pnpmDir, entry, "node_modules", "hnswlib-node");
20
+ if (existsSync(candidate) && statSync(candidate).isDirectory()) {
21
+ return candidate;
22
+ }
23
+ }
24
+ }
25
+
26
+ return null;
32
27
  }
33
28
 
34
29
  const hnswlibDir = findHnswlibDir();
35
30
 
36
31
  if (!hnswlibDir) {
37
- console.log("hnswlib-node not found, skipping rebuild");
38
- process.exit(0);
32
+ console.log("hnswlib-node not found, skipping rebuild");
33
+ process.exit(0);
39
34
  }
40
35
 
41
36
  const addonPath = join(hnswlibDir, "build", "Release", "addon.node");
42
37
  if (existsSync(addonPath)) {
43
- console.log("hnswlib-node already built, skipping");
44
- process.exit(0);
38
+ console.log("hnswlib-node already built, skipping");
39
+ process.exit(0);
45
40
  }
46
41
 
47
42
  console.log("Building hnswlib-node native module...");
48
43
  console.log(` Directory: ${hnswlibDir}`);
49
44
 
50
45
  try {
51
- execSync("npx node-gyp rebuild", {
52
- cwd: hnswlibDir,
53
- stdio: "inherit",
54
- });
55
- console.log("hnswlib-node build complete");
46
+ execSync("npx node-gyp rebuild", {
47
+ cwd: hnswlibDir,
48
+ stdio: "inherit",
49
+ });
50
+ console.log("hnswlib-node build complete");
56
51
  } catch (error) {
57
- console.error("Failed to build hnswlib-node:", error.message);
58
- console.error("You may need to install build tools:");
59
- console.error(" - macOS: xcode-select --install");
60
- console.error(" - Ubuntu: apt install python3 make g++");
61
- console.error(" - Windows: npm install -g windows-build-tools");
62
- process.exit(1);
52
+ console.error("Failed to build hnswlib-node:", error.message);
53
+ console.error("You may need to install build tools:");
54
+ console.error(" - macOS: xcode-select --install");
55
+ console.error(" - Ubuntu: apt install python3 make g++");
56
+ console.error(" - Windows: npm install -g windows-build-tools");
57
+ process.exit(1);
63
58
  }
@@ -0,0 +1,64 @@
1
+ #!/bin/bash
2
+ # Setup branch protection for a GitHub repository
3
+ # Usage: ./setup-branch-protection.sh owner/repo [branch]
4
+ #
5
+ # Example:
6
+ # ./setup-branch-protection.sh mdcontext/mdcontext main
7
+ # ./setup-branch-protection.sh myorg/myrepo
8
+
9
+ set -e
10
+
11
+ REPO="${1:?Usage: $0 owner/repo [branch]}"
12
+ BRANCH="${2:-main}"
13
+
14
+ echo "Setting up branch protection for $REPO ($BRANCH branch)..."
15
+
16
+ # Customize these status checks for your CI workflow
17
+ # Comment out or modify based on your needs
18
+ STATUS_CHECKS='[
19
+ "quality",
20
+ "test (ubuntu-latest, 20)",
21
+ "test (ubuntu-latest, 22)",
22
+ "test (macos-latest, 20)",
23
+ "test (macos-latest, 22)",
24
+ "test (windows-latest, 20)",
25
+ "test (windows-latest, 22)"
26
+ ]'
27
+
28
+ # For simpler projects, you might use:
29
+ # STATUS_CHECKS='["build", "test"]'
30
+
31
+ cat << EOF | gh api "repos/$REPO/branches/$BRANCH/protection" -X PUT --input -
32
+ {
33
+ "required_status_checks": {
34
+ "strict": true,
35
+ "contexts": $STATUS_CHECKS
36
+ },
37
+ "enforce_admins": false,
38
+ "required_pull_request_reviews": {
39
+ "required_approving_review_count": 1,
40
+ "dismiss_stale_reviews": true,
41
+ "require_code_owner_reviews": false,
42
+ "require_last_push_approval": false
43
+ },
44
+ "restrictions": null,
45
+ "required_linear_history": true,
46
+ "allow_force_pushes": false,
47
+ "allow_deletions": false,
48
+ "block_creations": false,
49
+ "required_conversation_resolution": false,
50
+ "lock_branch": false,
51
+ "allow_fork_syncing": false
52
+ }
53
+ EOF
54
+
55
+ echo "Branch protection configured for $REPO ($BRANCH)"
56
+ echo ""
57
+ echo "Settings applied:"
58
+ echo " - Require status checks to pass before merging"
59
+ echo " - Require branches to be up to date before merging"
60
+ echo " - Require 1 approving review"
61
+ echo " - Dismiss stale reviews on new commits"
62
+ echo " - Require linear history (no merge commits)"
63
+ echo " - Block force pushes"
64
+ echo " - Block branch deletion"
@@ -0,0 +1,7 @@
1
+ {
2
+ "namespace": "openai_text-embedding-3-small_512",
3
+ "provider": "openai",
4
+ "model": "text-embedding-3-small",
5
+ "dimensions": 512,
6
+ "activatedAt": "2026-01-27T00:00:00.000Z"
7
+ }