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.
- package/.changeset/config.json +9 -9
- package/.claude/settings.local.json +25 -0
- package/.github/workflows/claude-code-review.yml +44 -0
- package/.github/workflows/claude.yml +85 -0
- package/CONTRIBUTING.md +186 -0
- package/NOTES/NOTES +44 -0
- package/README.md +206 -3
- package/biome.json +1 -1
- package/dist/chunk-23UPXDNL.js +3044 -0
- package/dist/chunk-2W7MO2DL.js +1366 -0
- package/dist/chunk-3NUAZGMA.js +1689 -0
- package/dist/chunk-7TOWB2XB.js +366 -0
- package/dist/chunk-7XOTOADQ.js +3065 -0
- package/dist/chunk-AH2PDM2K.js +3042 -0
- package/dist/chunk-BNXWSZ63.js +3742 -0
- package/dist/chunk-BTL5DJVU.js +3222 -0
- package/dist/chunk-HDHYG7E4.js +104 -0
- package/dist/chunk-HLR4KZBP.js +3234 -0
- package/dist/chunk-IP3FRFEB.js +1045 -0
- package/dist/chunk-KHU56VDO.js +3042 -0
- package/dist/chunk-KRYIFLQR.js +85 -89
- package/dist/chunk-LBSDNLEM.js +287 -0
- package/dist/chunk-MNTQ7HCP.js +2643 -0
- package/dist/chunk-MUJELQQ6.js +1387 -0
- package/dist/chunk-MXJGMSLV.js +2199 -0
- package/dist/chunk-N6QJGC3Z.js +2636 -0
- package/dist/chunk-OBELGBPM.js +1713 -0
- package/dist/chunk-OT7R5XTA.js +3192 -0
- package/dist/chunk-P7X4RA2T.js +106 -0
- package/dist/chunk-PIDUQNC2.js +3185 -0
- package/dist/chunk-POGCDIH4.js +3187 -0
- package/dist/chunk-PSIEOQGZ.js +3043 -0
- package/dist/chunk-PVRT3IHA.js +3238 -0
- package/dist/chunk-QNN4TT23.js +1430 -0
- package/dist/chunk-RE3R45RJ.js +3042 -0
- package/dist/chunk-S7E6TFX6.js +718 -657
- package/dist/chunk-SG6GLU4U.js +1378 -0
- package/dist/chunk-SJCDV2ST.js +274 -0
- package/dist/chunk-SYE5XLF3.js +104 -0
- package/dist/chunk-T5VLYBZD.js +103 -0
- package/dist/chunk-TOQB7VWU.js +3238 -0
- package/dist/chunk-VFNMZ4ZQ.js +3228 -0
- package/dist/chunk-VVTGZNBT.js +1533 -1423
- package/dist/chunk-W7Q4RFEV.js +104 -0
- package/dist/chunk-XTYYVRLO.js +3190 -0
- package/dist/chunk-Y6MDYVJD.js +3063 -0
- package/dist/cli/main.js +4072 -629
- package/dist/index.d.ts +420 -33
- package/dist/index.js +8 -15
- package/dist/mcp/server.js +103 -7
- package/dist/schema-BAWSG7KY.js +22 -0
- package/dist/schema-E3QUPL26.js +20 -0
- package/dist/schema-EHL7WUT6.js +20 -0
- package/docs/019-USAGE.md +44 -5
- package/docs/020-current-implementation.md +8 -8
- package/docs/021-DOGFOODING-FINDINGS.md +1 -1
- package/docs/CONFIG.md +1123 -0
- package/docs/ERRORS.md +383 -0
- package/docs/summarization.md +320 -0
- package/justfile +40 -0
- package/package.json +39 -33
- package/research/INDEX.md +315 -0
- package/research/code-review/README.md +90 -0
- package/research/code-review/cli-error-handling-review.md +979 -0
- package/research/code-review/code-review-validation-report.md +464 -0
- package/research/code-review/main-ts-review.md +1128 -0
- package/research/config-docs/SUMMARY.md +357 -0
- package/research/config-docs/TEST-RESULTS.md +776 -0
- package/research/config-docs/TODO.md +542 -0
- package/research/config-docs/analysis.md +744 -0
- package/research/config-docs/fix-validation.md +502 -0
- package/research/config-docs/help-audit.md +264 -0
- package/research/config-docs/help-system-analysis.md +890 -0
- package/research/frontmatter/COMMENTS-ARE-SKIPPED.md +149 -0
- package/research/frontmatter/LLM-CODE-NAVIGATION.md +276 -0
- package/research/issue-review.md +603 -0
- package/research/llm-summarization/agent-cli-tools-2026.md +1082 -0
- package/research/llm-summarization/alternative-providers-2026.md +1428 -0
- package/research/llm-summarization/anthropic-2026.md +367 -0
- package/research/llm-summarization/claude-cli-integration.md +1706 -0
- package/research/llm-summarization/cli-integration-patterns.md +3155 -0
- package/research/llm-summarization/openai-2026.md +473 -0
- package/research/llm-summarization/openai-compatible-providers-2026.md +1022 -0
- package/research/llm-summarization/opencode-cli-integration.md +1552 -0
- package/research/llm-summarization/prompt-engineering-2026.md +1426 -0
- package/research/llm-summarization/prototype-results.md +56 -0
- package/research/llm-summarization/provider-switching-patterns-2026.md +2153 -0
- package/research/llm-summarization/typescript-llm-libraries-2026.md +2436 -0
- package/research/mdcontext-pudding/00-EXECUTIVE-SUMMARY.md +282 -0
- package/research/mdcontext-pudding/01-index-embed.md +956 -0
- package/research/mdcontext-pudding/02-search-COMMANDS.md +142 -0
- package/research/mdcontext-pudding/02-search-SUMMARY.md +146 -0
- package/research/mdcontext-pudding/02-search.md +970 -0
- package/research/mdcontext-pudding/03-context.md +779 -0
- package/research/mdcontext-pudding/04-navigation-and-analytics.md +803 -0
- package/research/mdcontext-pudding/04-tree.md +704 -0
- package/research/mdcontext-pudding/05-config.md +1038 -0
- package/research/mdcontext-pudding/06-links-summary.txt +87 -0
- package/research/mdcontext-pudding/06-links.md +679 -0
- package/research/mdcontext-pudding/07-stats.md +693 -0
- package/research/mdcontext-pudding/BUG-FIX-PLAN.md +388 -0
- package/research/mdcontext-pudding/P0-BUG-VALIDATION.md +167 -0
- package/research/mdcontext-pudding/README.md +168 -0
- package/research/mdcontext-pudding/TESTING-SUMMARY.md +128 -0
- package/research/research-quality-review.md +834 -0
- package/research/semantic-search/embedding-text-analysis.md +156 -0
- package/research/semantic-search/multi-word-failure-reproduction.md +171 -0
- package/research/semantic-search/query-processing-analysis.md +207 -0
- package/research/semantic-search/root-cause-and-solution.md +114 -0
- package/research/semantic-search/threshold-validation-report.md +69 -0
- package/research/semantic-search/vector-search-analysis.md +63 -0
- package/research/test-path-issues.md +276 -0
- package/review/ALP-76/1-error-type-design.md +962 -0
- package/review/ALP-76/2-error-handling-patterns.md +906 -0
- package/review/ALP-76/3-error-presentation.md +624 -0
- package/review/ALP-76/4-test-coverage.md +625 -0
- package/review/ALP-76/5-migration-completeness.md +440 -0
- package/review/ALP-76/6-effect-best-practices.md +755 -0
- package/scripts/apply-branch-protection.sh +47 -0
- package/scripts/branch-protection-templates.json +79 -0
- package/scripts/prototype-summarization.ts +346 -0
- package/scripts/rebuild-hnswlib.js +32 -37
- package/scripts/setup-branch-protection.sh +64 -0
- package/src/__tests__/fixtures/semantic-search/multi-word-corpus/.mdcontext/active-provider.json +7 -0
- package/src/__tests__/fixtures/semantic-search/multi-word-corpus/.mdcontext/bm25.json +541 -0
- package/src/__tests__/fixtures/semantic-search/multi-word-corpus/.mdcontext/bm25.meta.json +5 -0
- package/src/__tests__/fixtures/semantic-search/multi-word-corpus/.mdcontext/config.json +8 -0
- package/src/__tests__/fixtures/semantic-search/multi-word-corpus/.mdcontext/embeddings/openai_text-embedding-3-small_512/vectors.bin +0 -0
- package/src/__tests__/fixtures/semantic-search/multi-word-corpus/.mdcontext/embeddings/openai_text-embedding-3-small_512/vectors.meta.bin +0 -0
- package/src/__tests__/fixtures/semantic-search/multi-word-corpus/.mdcontext/indexes/documents.json +60 -0
- package/src/__tests__/fixtures/semantic-search/multi-word-corpus/.mdcontext/indexes/links.json +13 -0
- package/src/__tests__/fixtures/semantic-search/multi-word-corpus/.mdcontext/indexes/sections.json +1197 -0
- package/src/__tests__/fixtures/semantic-search/multi-word-corpus/configuration-management.md +99 -0
- package/src/__tests__/fixtures/semantic-search/multi-word-corpus/distributed-systems.md +92 -0
- package/src/__tests__/fixtures/semantic-search/multi-word-corpus/error-handling.md +78 -0
- package/src/__tests__/fixtures/semantic-search/multi-word-corpus/failure-automation.md +55 -0
- package/src/__tests__/fixtures/semantic-search/multi-word-corpus/job-context.md +69 -0
- package/src/__tests__/fixtures/semantic-search/multi-word-corpus/process-orchestration.md +99 -0
- package/src/cli/argv-preprocessor.test.ts +2 -2
- package/src/cli/cli.test.ts +230 -33
- package/src/cli/commands/config-cmd.ts +642 -0
- package/src/cli/commands/context.ts +97 -9
- package/src/cli/commands/duplicates.ts +122 -0
- package/src/cli/commands/embeddings.ts +529 -0
- package/src/cli/commands/index-cmd.ts +210 -30
- package/src/cli/commands/index.ts +3 -0
- package/src/cli/commands/search.ts +894 -64
- package/src/cli/commands/stats.ts +3 -0
- package/src/cli/commands/tree.ts +26 -5
- package/src/cli/config-layer.ts +176 -0
- package/src/cli/error-handler.test.ts +235 -0
- package/src/cli/error-handler.ts +655 -0
- package/src/cli/flag-schemas.ts +66 -0
- package/src/cli/help.ts +209 -7
- package/src/cli/main.ts +348 -58
- package/src/cli/options.ts +10 -0
- package/src/cli/shared-error-handling.ts +199 -0
- package/src/cli/utils.ts +150 -17
- package/src/config/file-provider.test.ts +320 -0
- package/src/config/file-provider.ts +273 -0
- package/src/config/index.ts +72 -0
- package/src/config/integration.test.ts +667 -0
- package/src/config/precedence.test.ts +277 -0
- package/src/config/precedence.ts +451 -0
- package/src/config/schema.test.ts +414 -0
- package/src/config/schema.ts +603 -0
- package/src/config/service.test.ts +320 -0
- package/src/config/service.ts +243 -0
- package/src/config/testing.test.ts +264 -0
- package/src/config/testing.ts +110 -0
- package/src/core/types.ts +6 -33
- package/src/duplicates/detector.test.ts +183 -0
- package/src/duplicates/detector.ts +414 -0
- package/src/duplicates/index.ts +18 -0
- package/src/embeddings/embedding-namespace.test.ts +300 -0
- package/src/embeddings/embedding-namespace.ts +947 -0
- package/src/embeddings/heading-boost.test.ts +222 -0
- package/src/embeddings/hnsw-build-options.test.ts +198 -0
- package/src/embeddings/hyde.test.ts +272 -0
- package/src/embeddings/hyde.ts +264 -0
- package/src/embeddings/index.ts +2 -0
- package/src/embeddings/openai-provider.ts +332 -83
- package/src/embeddings/pricing.json +22 -0
- package/src/embeddings/provider-constants.ts +204 -0
- package/src/embeddings/provider-errors.test.ts +967 -0
- package/src/embeddings/provider-errors.ts +565 -0
- package/src/embeddings/provider-factory.test.ts +240 -0
- package/src/embeddings/provider-factory.ts +225 -0
- package/src/embeddings/provider-integration.test.ts +788 -0
- package/src/embeddings/query-preprocessing.test.ts +187 -0
- package/src/embeddings/semantic-search-threshold.test.ts +508 -0
- package/src/embeddings/semantic-search.ts +780 -93
- package/src/embeddings/types.ts +293 -16
- package/src/embeddings/vector-store.ts +486 -77
- package/src/embeddings/voyage-provider.ts +313 -0
- package/src/errors/errors.test.ts +845 -0
- package/src/errors/index.ts +533 -0
- package/src/index/ignore-patterns.test.ts +354 -0
- package/src/index/ignore-patterns.ts +305 -0
- package/src/index/indexer.ts +286 -48
- package/src/index/storage.ts +94 -30
- package/src/index/types.ts +40 -2
- package/src/index/watcher.ts +67 -9
- package/src/index.ts +22 -0
- package/src/integration/search-keyword.test.ts +678 -0
- package/src/mcp/server.ts +135 -6
- package/src/parser/parser.ts +18 -19
- package/src/parser/section-filter.test.ts +277 -0
- package/src/parser/section-filter.ts +125 -3
- package/src/search/__tests__/hybrid-search.test.ts +650 -0
- package/src/search/bm25-store.ts +366 -0
- package/src/search/cross-encoder.test.ts +253 -0
- package/src/search/cross-encoder.ts +406 -0
- package/src/search/fuzzy-search.test.ts +419 -0
- package/src/search/fuzzy-search.ts +273 -0
- package/src/search/hybrid-search.ts +448 -0
- package/src/search/path-matcher.test.ts +276 -0
- package/src/search/path-matcher.ts +33 -0
- package/src/search/searcher.test.ts +99 -1
- package/src/search/searcher.ts +189 -67
- package/src/search/wink-bm25.d.ts +30 -0
- package/src/summarization/cli-providers/claude.ts +202 -0
- package/src/summarization/cli-providers/detection.test.ts +273 -0
- package/src/summarization/cli-providers/detection.ts +118 -0
- package/src/summarization/cli-providers/index.ts +8 -0
- package/src/summarization/cost.test.ts +139 -0
- package/src/summarization/cost.ts +102 -0
- package/src/summarization/error-handler.test.ts +127 -0
- package/src/summarization/error-handler.ts +111 -0
- package/src/summarization/index.ts +102 -0
- package/src/summarization/pipeline.test.ts +498 -0
- package/src/summarization/pipeline.ts +231 -0
- package/src/summarization/prompts.test.ts +269 -0
- package/src/summarization/prompts.ts +133 -0
- package/src/summarization/provider-factory.test.ts +396 -0
- package/src/summarization/provider-factory.ts +178 -0
- package/src/summarization/types.ts +184 -0
- package/src/summarize/summarizer.ts +104 -35
- package/src/types/huggingface-transformers.d.ts +66 -0
- package/tests/fixtures/cli/.mdcontext/active-provider.json +7 -0
- package/tests/fixtures/cli/.mdcontext/embeddings/openai_text-embedding-3-small_512/vectors.bin +0 -0
- package/tests/fixtures/cli/.mdcontext/embeddings/openai_text-embedding-3-small_512/vectors.meta.bin +0 -0
- package/tests/fixtures/cli/.mdcontext/indexes/documents.json +4 -4
- package/tests/fixtures/cli/.mdcontext/indexes/sections.json +14 -0
- package/tests/integration/embed-index.test.ts +712 -0
- package/tests/integration/search-context.test.ts +469 -0
- package/tests/integration/search-semantic.test.ts +522 -0
- package/vitest.config.ts +1 -6
- package/AGENTS.md +0 -46
- package/tests/fixtures/cli/.mdcontext/vectors.bin +0 -0
- 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.
|
|
Binary file
|