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,522 @@
1
+ /**
2
+ * Integration tests for semantic search with CLI flags
3
+ *
4
+ * Tests semantic search functionality including:
5
+ * - Basic semantic search
6
+ * - Context flags (-C, -A, -B)
7
+ * - Threshold filtering (--threshold)
8
+ * - Result limits (--limit)
9
+ * - Edge cases (no results, below threshold)
10
+ */
11
+
12
+ import * as fs from 'node:fs/promises'
13
+ import * as path from 'node:path'
14
+ import { Effect } from 'effect'
15
+ import { afterAll, beforeAll, describe, expect, it } from 'vitest'
16
+ import {
17
+ buildEmbeddings,
18
+ semanticSearchWithStats,
19
+ } from '../../src/embeddings/semantic-search.js'
20
+ import { buildIndex } from '../../src/index/indexer.js'
21
+
22
+ const TEST_DIR = path.join(
23
+ process.cwd(),
24
+ 'tests',
25
+ 'fixtures',
26
+ 'semantic-search',
27
+ )
28
+
29
+ const runEffect = <A, E>(effect: Effect.Effect<A, E>) =>
30
+ Effect.runPromise(effect)
31
+
32
+ const skipIfNoApiKey = () => {
33
+ if (!process.env.OPENAI_API_KEY && !process.env.INCLUDE_EMBED_TESTS) {
34
+ return true
35
+ }
36
+ return false
37
+ }
38
+
39
+ describe('semantic search integration', () => {
40
+ beforeAll(async () => {
41
+ if (skipIfNoApiKey()) {
42
+ console.log(
43
+ 'Skipping semantic search tests (set OPENAI_API_KEY or INCLUDE_EMBED_TESTS=true)',
44
+ )
45
+ return
46
+ }
47
+
48
+ await fs.mkdir(TEST_DIR, { recursive: true })
49
+
50
+ await fs.writeFile(
51
+ path.join(TEST_DIR, 'authentication.md'),
52
+ `# Authentication Guide
53
+
54
+ ## Overview
55
+
56
+ This document explains how authentication works in the system.
57
+
58
+ ## User Login
59
+
60
+ Users can log in using email and password. The system validates credentials
61
+ against the database and returns a JWT token for subsequent requests.
62
+
63
+ ### Password Requirements
64
+
65
+ Passwords must be at least 8 characters and contain uppercase, lowercase,
66
+ numbers, and special characters.
67
+
68
+ ## OAuth Integration
69
+
70
+ The system supports OAuth 2.0 for third-party authentication providers
71
+ including Google, GitHub, and Microsoft.
72
+
73
+ ### Configuration
74
+
75
+ Set the following environment variables:
76
+ - OAUTH_CLIENT_ID
77
+ - OAUTH_CLIENT_SECRET
78
+ - OAUTH_REDIRECT_URI
79
+
80
+ ## Session Management
81
+
82
+ Sessions are stored in Redis with a 30-minute expiration time.
83
+ Sessions can be refreshed by calling the /refresh endpoint.
84
+
85
+ ## Security Considerations
86
+
87
+ Always use HTTPS in production. Never store passwords in plain text.
88
+ Implement rate limiting on authentication endpoints to prevent brute force attacks.
89
+ `,
90
+ )
91
+
92
+ await fs.writeFile(
93
+ path.join(TEST_DIR, 'database.md'),
94
+ `# Database Schema
95
+
96
+ ## Tables
97
+
98
+ ### users
99
+
100
+ The users table stores account information.
101
+
102
+ Columns:
103
+ - id (primary key)
104
+ - email (unique)
105
+ - password_hash
106
+ - created_at
107
+ - updated_at
108
+
109
+ ### sessions
110
+
111
+ The sessions table tracks active user sessions.
112
+
113
+ Columns:
114
+ - id (primary key)
115
+ - user_id (foreign key)
116
+ - token
117
+ - expires_at
118
+
119
+ ## Migrations
120
+
121
+ Database migrations are managed using a migration tool.
122
+ Run migrations before deploying new code.
123
+
124
+ ## Backup Strategy
125
+
126
+ Daily backups are stored in S3 with 30-day retention.
127
+ Point-in-time recovery is available for the last 7 days.
128
+ `,
129
+ )
130
+
131
+ await fs.writeFile(
132
+ path.join(TEST_DIR, 'api.md'),
133
+ `# API Documentation
134
+
135
+ ## REST Endpoints
136
+
137
+ ### Authentication Endpoints
138
+
139
+ POST /api/auth/login - Authenticate user and get token
140
+ POST /api/auth/logout - Invalidate session
141
+ POST /api/auth/refresh - Refresh authentication token
142
+
143
+ ### User Endpoints
144
+
145
+ GET /api/users/:id - Get user profile
146
+ PUT /api/users/:id - Update user profile
147
+ DELETE /api/users/:id - Delete user account
148
+
149
+ ## Rate Limiting
150
+
151
+ API endpoints are rate limited to 100 requests per minute per IP address.
152
+ Authenticated users get 1000 requests per minute.
153
+
154
+ ## Error Handling
155
+
156
+ All errors return JSON with status code and message.
157
+ Use the error code to determine appropriate retry logic.
158
+ `,
159
+ )
160
+
161
+ await fs.writeFile(
162
+ path.join(TEST_DIR, 'deployment.md'),
163
+ `# Deployment Guide
164
+
165
+ ## Production Setup
166
+
167
+ Deploy to AWS using Docker containers.
168
+ Use environment-specific configuration files.
169
+
170
+ ## Environment Variables
171
+
172
+ Required variables:
173
+ - DATABASE_URL
174
+ - REDIS_URL
175
+ - JWT_SECRET
176
+ - OAUTH_CLIENT_ID
177
+
178
+ ## Monitoring
179
+
180
+ Set up CloudWatch alarms for:
181
+ - High error rates
182
+ - Slow response times
183
+ - Database connection issues
184
+
185
+ ## Rollback Procedure
186
+
187
+ If deployment fails, rollback using the previous Docker image tag.
188
+ Database migrations cannot be automatically rolled back.
189
+ `,
190
+ )
191
+
192
+ const shouldRebuild = process.env.REBUILD_TEST_INDEX === 'true'
193
+
194
+ await runEffect(buildIndex(TEST_DIR, { force: shouldRebuild }))
195
+
196
+ await runEffect(
197
+ buildEmbeddings(TEST_DIR, {
198
+ force: shouldRebuild,
199
+ }),
200
+ )
201
+ }, 60000)
202
+
203
+ afterAll(async () => {
204
+ if (skipIfNoApiKey()) return
205
+ await fs.rm(TEST_DIR, { recursive: true, force: true })
206
+ })
207
+
208
+ describe('basic semantic search', () => {
209
+ it('should find results for natural language query', async () => {
210
+ if (skipIfNoApiKey()) return
211
+
212
+ const result = await runEffect(
213
+ semanticSearchWithStats(TEST_DIR, 'how does user login work', {
214
+ limit: 5,
215
+ threshold: 0.3,
216
+ }),
217
+ )
218
+
219
+ expect(result.results.length).toBeGreaterThan(0)
220
+ expect(result.results[0]?.similarity).toBeGreaterThanOrEqual(0.3)
221
+
222
+ const topResult = result.results[0]
223
+ expect(topResult?.documentPath).toContain('.md')
224
+ expect(topResult?.heading).toBeTruthy()
225
+ expect(topResult?.sectionId).toBeTruthy()
226
+ })
227
+
228
+ it('should return results sorted by similarity score', async () => {
229
+ if (skipIfNoApiKey()) return
230
+
231
+ const result = await runEffect(
232
+ semanticSearchWithStats(TEST_DIR, 'authentication', {
233
+ limit: 10,
234
+ threshold: 0.2,
235
+ }),
236
+ )
237
+
238
+ expect(result.results.length).toBeGreaterThan(1)
239
+
240
+ for (let i = 1; i < result.results.length; i++) {
241
+ const prev = result.results[i - 1]
242
+ const curr = result.results[i]
243
+ expect(prev?.similarity).toBeGreaterThanOrEqual(curr?.similarity ?? 0)
244
+ }
245
+ })
246
+ })
247
+
248
+ describe('--threshold flag', () => {
249
+ it('should filter results below threshold', async () => {
250
+ if (skipIfNoApiKey()) return
251
+
252
+ const lowThreshold = await runEffect(
253
+ semanticSearchWithStats(TEST_DIR, 'deployment', {
254
+ limit: 10,
255
+ threshold: 0.2,
256
+ }),
257
+ )
258
+
259
+ const highThreshold = await runEffect(
260
+ semanticSearchWithStats(TEST_DIR, 'deployment', {
261
+ limit: 10,
262
+ threshold: 0.5,
263
+ }),
264
+ )
265
+
266
+ expect(lowThreshold.results.length).toBeGreaterThanOrEqual(
267
+ highThreshold.results.length,
268
+ )
269
+
270
+ for (const result of highThreshold.results) {
271
+ expect(result.similarity).toBeGreaterThanOrEqual(0.5)
272
+ }
273
+ })
274
+
275
+ it('should provide stats when no results meet threshold', async () => {
276
+ if (skipIfNoApiKey()) return
277
+
278
+ const result = await runEffect(
279
+ semanticSearchWithStats(TEST_DIR, 'authentication', {
280
+ limit: 5,
281
+ threshold: 0.99,
282
+ }),
283
+ )
284
+
285
+ expect(result.results.length).toBe(0)
286
+
287
+ if (result.belowThresholdCount !== undefined) {
288
+ expect(result.belowThresholdCount).toBeGreaterThan(0)
289
+ expect(result.belowThresholdHighest).toBeDefined()
290
+ expect(result.belowThresholdHighest).toBeLessThan(0.99)
291
+ }
292
+ })
293
+ })
294
+
295
+ describe('--limit flag', () => {
296
+ it('should respect limit parameter', async () => {
297
+ if (skipIfNoApiKey()) return
298
+
299
+ const result = await runEffect(
300
+ semanticSearchWithStats(TEST_DIR, 'api endpoints', {
301
+ limit: 3,
302
+ threshold: 0.2,
303
+ }),
304
+ )
305
+
306
+ expect(result.results.length).toBeLessThanOrEqual(3)
307
+ })
308
+
309
+ it('should provide totalAvailable when results exceed limit', async () => {
310
+ if (skipIfNoApiKey()) return
311
+
312
+ const result = await runEffect(
313
+ semanticSearchWithStats(TEST_DIR, 'user', {
314
+ limit: 2,
315
+ threshold: 0.2,
316
+ }),
317
+ )
318
+
319
+ if (result.totalAvailable !== undefined) {
320
+ expect(result.totalAvailable).toBeGreaterThanOrEqual(
321
+ result.results.length,
322
+ )
323
+ }
324
+ })
325
+
326
+ it('should handle limit larger than available results', async () => {
327
+ if (skipIfNoApiKey()) return
328
+
329
+ const result = await runEffect(
330
+ semanticSearchWithStats(TEST_DIR, 'authentication', {
331
+ limit: 100,
332
+ threshold: 0.3,
333
+ }),
334
+ )
335
+
336
+ expect(result.results.length).toBeLessThan(100)
337
+ expect(result.results.length).toBeGreaterThan(0)
338
+ })
339
+ })
340
+
341
+ describe('context flags (-C, -A, -B)', () => {
342
+ it('should include context lines when context flags are provided', async () => {
343
+ if (skipIfNoApiKey()) return
344
+
345
+ const result = await runEffect(
346
+ semanticSearchWithStats(TEST_DIR, 'authentication', {
347
+ limit: 5,
348
+ threshold: 0.3,
349
+ contextBefore: 2,
350
+ contextAfter: 2,
351
+ }),
352
+ )
353
+
354
+ expect(result.results.length).toBeGreaterThan(0)
355
+
356
+ const firstResult = result.results[0]
357
+ expect(firstResult?.contextLines).toBeDefined()
358
+ expect(firstResult!.contextLines!.length).toBeGreaterThan(0)
359
+
360
+ const matchingLine = firstResult!.contextLines!.find((ctx) => ctx.isMatch)
361
+ expect(matchingLine).toBeDefined()
362
+ expect(matchingLine?.lineNumber).toBeGreaterThan(0)
363
+ })
364
+ })
365
+
366
+ describe('edge cases', () => {
367
+ it('should handle query with no relevant results', async () => {
368
+ if (skipIfNoApiKey()) return
369
+
370
+ const result = await runEffect(
371
+ semanticSearchWithStats(TEST_DIR, 'quantum physics blockchain AI', {
372
+ limit: 5,
373
+ threshold: 0.7,
374
+ }),
375
+ )
376
+
377
+ expect(result.results.length).toBe(0)
378
+ })
379
+
380
+ it('should handle very short query', async () => {
381
+ if (skipIfNoApiKey()) return
382
+
383
+ const result = await runEffect(
384
+ semanticSearchWithStats(TEST_DIR, 'api', {
385
+ limit: 5,
386
+ threshold: 0.3,
387
+ }),
388
+ )
389
+
390
+ expect(result.results.length).toBeGreaterThanOrEqual(0)
391
+ })
392
+
393
+ it('should handle very long query', async () => {
394
+ if (skipIfNoApiKey()) return
395
+
396
+ const longQuery =
397
+ 'I need to understand how the authentication system works including user login with email and password, OAuth integration with third-party providers like Google and GitHub, session management with Redis, and security best practices for production deployment'
398
+
399
+ const result = await runEffect(
400
+ semanticSearchWithStats(TEST_DIR, longQuery, {
401
+ limit: 5,
402
+ threshold: 0.3,
403
+ }),
404
+ )
405
+
406
+ expect(result.results.length).toBeGreaterThan(0)
407
+ })
408
+
409
+ it('should handle query with special characters', async () => {
410
+ if (skipIfNoApiKey()) return
411
+
412
+ const result = await runEffect(
413
+ semanticSearchWithStats(TEST_DIR, 'OAuth 2.0 & JWT tokens', {
414
+ limit: 5,
415
+ threshold: 0.3,
416
+ }),
417
+ )
418
+
419
+ expect(result.results.length).toBeGreaterThanOrEqual(0)
420
+ })
421
+ })
422
+
423
+ describe('result format validation', () => {
424
+ it('should return correctly structured results', async () => {
425
+ if (skipIfNoApiKey()) return
426
+
427
+ const result = await runEffect(
428
+ semanticSearchWithStats(TEST_DIR, 'database schema', {
429
+ limit: 5,
430
+ threshold: 0.3,
431
+ }),
432
+ )
433
+
434
+ expect(result).toHaveProperty('results')
435
+ expect(Array.isArray(result.results)).toBe(true)
436
+
437
+ if (result.results.length > 0) {
438
+ const firstResult = result.results[0]
439
+ expect(firstResult).toHaveProperty('sectionId')
440
+ expect(firstResult).toHaveProperty('documentPath')
441
+ expect(firstResult).toHaveProperty('heading')
442
+ expect(firstResult).toHaveProperty('similarity')
443
+
444
+ expect(typeof firstResult?.sectionId).toBe('string')
445
+ expect(typeof firstResult?.documentPath).toBe('string')
446
+ expect(typeof firstResult?.heading).toBe('string')
447
+ expect(typeof firstResult?.similarity).toBe('number')
448
+
449
+ expect(firstResult?.similarity).toBeGreaterThan(0)
450
+ expect(firstResult?.similarity).toBeLessThanOrEqual(1)
451
+ }
452
+ })
453
+ })
454
+
455
+ describe('multi-document search', () => {
456
+ it('should search across multiple documents', async () => {
457
+ if (skipIfNoApiKey()) return
458
+
459
+ const result = await runEffect(
460
+ semanticSearchWithStats(
461
+ TEST_DIR,
462
+ 'configuration environment variables',
463
+ {
464
+ limit: 10,
465
+ threshold: 0.3,
466
+ },
467
+ ),
468
+ )
469
+
470
+ expect(result.results.length).toBeGreaterThan(0)
471
+
472
+ const uniqueDocs = new Set(result.results.map((r) => r.documentPath))
473
+ expect(uniqueDocs.size).toBeGreaterThan(1)
474
+ })
475
+
476
+ it('should find related content across different sections', async () => {
477
+ if (skipIfNoApiKey()) return
478
+
479
+ const result = await runEffect(
480
+ semanticSearchWithStats(TEST_DIR, 'security and passwords', {
481
+ limit: 10,
482
+ threshold: 0.3,
483
+ }),
484
+ )
485
+
486
+ expect(result.results.length).toBeGreaterThan(0)
487
+ })
488
+ })
489
+
490
+ describe('similarity score behavior', () => {
491
+ it('should give higher scores for exact topic matches', async () => {
492
+ if (skipIfNoApiKey()) return
493
+
494
+ const authResult = await runEffect(
495
+ semanticSearchWithStats(TEST_DIR, 'OAuth authentication', {
496
+ limit: 5,
497
+ threshold: 0.2,
498
+ }),
499
+ )
500
+
501
+ const unrelatedResult = await runEffect(
502
+ semanticSearchWithStats(TEST_DIR, 'database backup', {
503
+ limit: 5,
504
+ threshold: 0.2,
505
+ }),
506
+ )
507
+
508
+ if (authResult.results.length > 0 && unrelatedResult.results.length > 0) {
509
+ const authDoc = authResult.results.find((r) =>
510
+ r.documentPath.includes('authentication'),
511
+ )
512
+ const backupDoc = unrelatedResult.results.find((r) =>
513
+ r.documentPath.includes('database'),
514
+ )
515
+
516
+ if (authDoc && backupDoc) {
517
+ expect(authDoc.similarity).toBeGreaterThan(0.3)
518
+ }
519
+ }
520
+ })
521
+ })
522
+ })
package/vitest.config.ts CHANGED
@@ -4,15 +4,10 @@ export default defineConfig({
4
4
  test: {
5
5
  globals: true,
6
6
  environment: 'node',
7
- include: ['src/**/*.test.ts'],
7
+ include: ['src/**/*.test.ts', 'tests/**/*.test.ts'],
8
8
  testTimeout: 30000,
9
9
  setupFiles: ['./vitest.setup.ts'],
10
10
  pool: 'forks',
11
- poolOptions: {
12
- forks: {
13
- singleFork: true,
14
- },
15
- },
16
11
  coverage: {
17
12
  provider: 'v8',
18
13
  reporter: ['text', 'json', 'html'],
package/AGENTS.md DELETED
@@ -1,46 +0,0 @@
1
- # HumanWork - Partnership Guidelines
2
-
3
- ## Hello, my friend. I'm Stuart, your AI partner in building this product together. Let's create something amazing
4
-
5
- ## You Are a Co-Builder
6
-
7
- This is not a task queue. You are building this product with me. That means:
8
-
9
- - **Own the vision.** Understand where we're going and why. Push back when something doesn't fit.
10
- - **Anticipate.** Don't wait to be told. If you see a problem coming, flag it. If you see an opportunity, propose it.
11
- - **Think in WOW.** Every feature should make someone say "that's clever" or "I love that." Functional is the floor, not the ceiling.
12
- - **Challenge me.** If my idea is mediocre, say so. Offer something better. I want your best thinking, not compliance.
13
-
14
- ## How We Work
15
-
16
- **Brainstorm freely.** When exploring ideas, throw out multiple options. Think out loud. Connect dots I might miss.
17
-
18
- **Be decisive.** When it's time to build, commit fully. No hedging, no "we could maybe..." — make a call and execute.
19
-
20
- **Move fast, stay sharp.** Speed matters, but not at the cost of quality. If something feels hacky, say so and fix it right.
21
-
22
- **Show, don't ask.** If you can try something quickly, do it. Show me the result. Don't ask permission for exploration.
23
-
24
- **consequences, consequences, consequences** Always think about the consequences of our actions. What are down and up stream of the changes you are making. Code bases are complex. Even a one line change could break something else. Always look at the bigger picture. Think in terms of consequences.
25
-
26
- ## What I Expect
27
-
28
- - **Proactive ideas.** "Have you considered..." or "What if we..." — bring fresh thinking.
29
- - **Pattern recognition.** You've seen a lot of code. Spot when we're reinventing a wheel or missing an elegant solution.
30
- - **Quality instincts.** Care about the details. Names matter. Structure matters. UX matters.
31
- - **Energy.** This should be fun. Bring enthusiasm. We're building something cool.
32
-
33
- ## What I Don't Want
34
-
35
- - Asking obvious clarifying questions when you could just try it
36
- - Doing the minimum to satisfy a request
37
- - Waiting for instructions when you know what needs to happen
38
- - Treating every decision as equal weight — some things just need doing
39
-
40
- ## The Product
41
-
42
- A tool to help our main product HumanWork = Human intent + Agent agency, unified.
43
-
44
- We're building orchestration infrastructure where humans and AI agents collaborate on complex tasks. The human grounds direction, the agent executes with capability. Eventually, even the "human" role becomes an interface an agent can fill.
45
-
46
- This is ambitious. Build it like it matters.