mdcontext 0.0.1 → 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/README.md +28 -0
- package/.changeset/config.json +11 -0
- package/.claude/settings.local.json +25 -0
- package/.github/workflows/ci.yml +83 -0
- package/.github/workflows/claude-code-review.yml +44 -0
- package/.github/workflows/claude.yml +85 -0
- package/.github/workflows/release.yml +113 -0
- package/.tldrignore +112 -0
- package/BACKLOG.md +338 -0
- package/CONTRIBUTING.md +186 -0
- package/NOTES/NOTES +44 -0
- package/README.md +434 -11
- package/biome.json +36 -0
- package/cspell.config.yaml +14 -0
- 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 +88 -0
- 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 +803 -0
- 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 +1629 -0
- 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.d.ts +1 -0
- package/dist/cli/main.js +5458 -0
- package/dist/index.d.ts +653 -0
- package/dist/index.js +79 -0
- package/dist/mcp/server.d.ts +1 -0
- package/dist/mcp/server.js +472 -0
- 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 +625 -0
- package/docs/020-current-implementation.md +364 -0
- package/docs/021-DOGFOODING-FINDINGS.md +175 -0
- package/docs/BACKLOG.md +80 -0
- package/docs/CONFIG.md +1123 -0
- package/docs/DESIGN.md +439 -0
- package/docs/ERRORS.md +383 -0
- package/docs/PROJECT.md +88 -0
- package/docs/ROADMAP.md +407 -0
- package/docs/summarization.md +320 -0
- package/docs/test-links.md +9 -0
- package/justfile +40 -0
- package/package.json +74 -9
- package/pnpm-workspace.yaml +5 -0
- 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-analysis/01-current-implementation.md +470 -0
- package/research/config-analysis/02-strategy-recommendation.md +428 -0
- package/research/config-analysis/03-task-candidates.md +715 -0
- package/research/config-analysis/033-research-configuration-management.md +828 -0
- package/research/config-analysis/034-research-effect-cli-config.md +1504 -0
- package/research/config-analysis/04-consolidated-task-candidates.md +277 -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/dogfood/consolidated-tool-evaluation.md +373 -0
- package/research/dogfood/strategy-a/a-synthesis.md +184 -0
- package/research/dogfood/strategy-a/a1-docs.md +226 -0
- package/research/dogfood/strategy-a/a2-amorphic.md +156 -0
- package/research/dogfood/strategy-a/a3-llm.md +164 -0
- package/research/dogfood/strategy-b/b-synthesis.md +228 -0
- package/research/dogfood/strategy-b/b1-architecture.md +207 -0
- package/research/dogfood/strategy-b/b2-gaps.md +258 -0
- package/research/dogfood/strategy-b/b3-workflows.md +250 -0
- package/research/dogfood/strategy-c/c-synthesis.md +451 -0
- package/research/dogfood/strategy-c/c1-explorer.md +192 -0
- package/research/dogfood/strategy-c/c2-diver-memory.md +145 -0
- package/research/dogfood/strategy-c/c3-diver-control.md +148 -0
- package/research/dogfood/strategy-c/c4-diver-failure.md +151 -0
- package/research/dogfood/strategy-c/c5-diver-execution.md +221 -0
- package/research/dogfood/strategy-c/c6-diver-org.md +221 -0
- package/research/effect-cli-error-handling.md +845 -0
- package/research/effect-errors-as-values.md +943 -0
- package/research/errors-task-analysis/00-consolidated-tasks.md +207 -0
- package/research/errors-task-analysis/cli-commands-analysis.md +909 -0
- package/research/errors-task-analysis/embeddings-analysis.md +709 -0
- package/research/errors-task-analysis/index-search-analysis.md +812 -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-error-analysis.md +521 -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/npm_publish/011-npm-workflow-research-agent2.md +792 -0
- package/research/npm_publish/012-npm-workflow-research-agent1.md +530 -0
- package/research/npm_publish/013-npm-workflow-research-agent3.md +722 -0
- package/research/npm_publish/014-npm-workflow-synthesis.md +556 -0
- package/research/npm_publish/031-npm-workflow-task-analysis.md +134 -0
- package/research/research-quality-review.md +834 -0
- package/research/semantic-search/002-research-embedding-models.md +490 -0
- package/research/semantic-search/003-research-rag-alternatives.md +523 -0
- package/research/semantic-search/004-research-vector-search.md +841 -0
- package/research/semantic-search/032-research-semantic-search.md +427 -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/task-management-2026/00-synthesis-recommendations.md +295 -0
- package/research/task-management-2026/01-ai-workflow-tools.md +416 -0
- package/research/task-management-2026/02-agent-framework-patterns.md +476 -0
- package/research/task-management-2026/03-lightweight-file-based.md +567 -0
- package/research/task-management-2026/04-established-tools-ai-features.md +541 -0
- package/research/task-management-2026/linear/01-core-features-workflow.md +771 -0
- package/research/task-management-2026/linear/02-api-integrations.md +930 -0
- package/research/task-management-2026/linear/03-ai-features.md +368 -0
- package/research/task-management-2026/linear/04-pricing-setup.md +205 -0
- package/research/task-management-2026/linear/05-usage-patterns-best-practices.md +605 -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 +58 -0
- 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 +210 -0
- package/src/cli/argv-preprocessor.ts +202 -0
- package/src/cli/cli.test.ts +627 -0
- package/src/cli/commands/backlinks.ts +54 -0
- package/src/cli/commands/config-cmd.ts +642 -0
- package/src/cli/commands/context.ts +285 -0
- package/src/cli/commands/duplicates.ts +122 -0
- package/src/cli/commands/embeddings.ts +529 -0
- package/src/cli/commands/index-cmd.ts +480 -0
- package/src/cli/commands/index.ts +16 -0
- package/src/cli/commands/links.ts +52 -0
- package/src/cli/commands/search.ts +1281 -0
- package/src/cli/commands/stats.ts +149 -0
- package/src/cli/commands/tree.ts +128 -0
- 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 +341 -0
- package/src/cli/help.ts +588 -0
- package/src/cli/index.ts +9 -0
- package/src/cli/main.ts +435 -0
- package/src/cli/options.ts +41 -0
- package/src/cli/shared-error-handling.ts +199 -0
- package/src/cli/typo-suggester.test.ts +105 -0
- package/src/cli/typo-suggester.ts +130 -0
- package/src/cli/utils.ts +259 -0
- 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/index.ts +1 -0
- package/src/core/types.ts +113 -0
- 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 +10 -0
- package/src/embeddings/openai-provider.ts +414 -0
- 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 +1270 -0
- package/src/embeddings/types.ts +359 -0
- package/src/embeddings/vector-store.ts +708 -0
- 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/index.ts +4 -0
- package/src/index/indexer.ts +684 -0
- package/src/index/storage.ts +260 -0
- package/src/index/types.ts +147 -0
- package/src/index/watcher.ts +189 -0
- package/src/index.ts +30 -0
- package/src/integration/search-keyword.test.ts +678 -0
- package/src/mcp/server.ts +612 -0
- package/src/parser/index.ts +1 -0
- package/src/parser/parser.test.ts +291 -0
- package/src/parser/parser.ts +394 -0
- package/src/parser/section-filter.test.ts +277 -0
- package/src/parser/section-filter.ts +392 -0
- 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/query-parser.test.ts +260 -0
- package/src/search/query-parser.ts +319 -0
- package/src/search/searcher.test.ts +280 -0
- package/src/search/searcher.ts +724 -0
- 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/budget-bugs.test.ts +620 -0
- package/src/summarize/formatters.ts +419 -0
- package/src/summarize/index.ts +20 -0
- package/src/summarize/summarizer.test.ts +275 -0
- package/src/summarize/summarizer.ts +597 -0
- package/src/summarize/verify-bugs.test.ts +238 -0
- package/src/types/huggingface-transformers.d.ts +66 -0
- package/src/utils/index.ts +1 -0
- package/src/utils/tokens.test.ts +142 -0
- package/src/utils/tokens.ts +186 -0
- package/tests/fixtures/cli/.mdcontext/active-provider.json +7 -0
- package/tests/fixtures/cli/.mdcontext/config.json +8 -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 +33 -0
- package/tests/fixtures/cli/.mdcontext/indexes/links.json +12 -0
- package/tests/fixtures/cli/.mdcontext/indexes/sections.json +247 -0
- package/tests/fixtures/cli/README.md +9 -0
- package/tests/fixtures/cli/api-reference.md +11 -0
- package/tests/fixtures/cli/getting-started.md +11 -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/tsconfig.json +26 -0
- package/vitest.config.ts +16 -0
- package/vitest.setup.ts +12 -0
|
@@ -0,0 +1,620 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest'
|
|
2
|
+
import { countTokensApprox } from '../utils/tokens.js'
|
|
3
|
+
import { formatSummary } from './formatters.js'
|
|
4
|
+
import type { DocumentSummary } from './summarizer.js'
|
|
5
|
+
|
|
6
|
+
describe('token budget enforcement', () => {
|
|
7
|
+
describe('orphan rescue - children included when parent too large', () => {
|
|
8
|
+
it('rescues children when parent section is too large for budget', () => {
|
|
9
|
+
// Large parent that won't fit, but small children that would
|
|
10
|
+
const mockSummary: DocumentSummary = {
|
|
11
|
+
path: '/test/file.md',
|
|
12
|
+
title: 'Test',
|
|
13
|
+
originalTokens: 1000,
|
|
14
|
+
summaryTokens: 500,
|
|
15
|
+
compressionRatio: 0.5,
|
|
16
|
+
sections: [
|
|
17
|
+
{
|
|
18
|
+
heading: 'Large Parent',
|
|
19
|
+
level: 2,
|
|
20
|
+
originalTokens: 500,
|
|
21
|
+
summaryTokens: 200,
|
|
22
|
+
summary: 'Very large parent content. '.repeat(50), // ~1000 chars = ~250 tokens
|
|
23
|
+
children: [
|
|
24
|
+
{
|
|
25
|
+
heading: 'Small Child A',
|
|
26
|
+
level: 3,
|
|
27
|
+
originalTokens: 20,
|
|
28
|
+
summaryTokens: 10,
|
|
29
|
+
summary: 'Tiny child A.',
|
|
30
|
+
children: [],
|
|
31
|
+
hasCode: false,
|
|
32
|
+
hasList: false,
|
|
33
|
+
hasTable: false,
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
heading: 'Small Child B',
|
|
37
|
+
level: 3,
|
|
38
|
+
originalTokens: 20,
|
|
39
|
+
summaryTokens: 10,
|
|
40
|
+
summary: 'Tiny child B.',
|
|
41
|
+
children: [],
|
|
42
|
+
hasCode: false,
|
|
43
|
+
hasList: false,
|
|
44
|
+
hasTable: false,
|
|
45
|
+
},
|
|
46
|
+
],
|
|
47
|
+
hasCode: false,
|
|
48
|
+
hasList: false,
|
|
49
|
+
hasTable: false,
|
|
50
|
+
},
|
|
51
|
+
],
|
|
52
|
+
keyTopics: [],
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Budget that can't fit parent but can fit children
|
|
56
|
+
// Increased to account for enhanced truncation warning overhead
|
|
57
|
+
const output = formatSummary(mockSummary, { maxTokens: 200 })
|
|
58
|
+
|
|
59
|
+
// Children should be rescued and included even though parent was skipped
|
|
60
|
+
expect(output).toContain('Small Child A')
|
|
61
|
+
expect(output).toContain('Small Child B')
|
|
62
|
+
// Parent should NOT be included (too large)
|
|
63
|
+
expect(output).not.toContain('Large Parent')
|
|
64
|
+
expect(output).not.toContain('Very large parent content')
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
it('preserves hierarchy when parent fits but some children do not', () => {
|
|
68
|
+
const mockSummary: DocumentSummary = {
|
|
69
|
+
path: '/test/file.md',
|
|
70
|
+
title: 'Test',
|
|
71
|
+
originalTokens: 500,
|
|
72
|
+
summaryTokens: 200,
|
|
73
|
+
compressionRatio: 0.6,
|
|
74
|
+
sections: [
|
|
75
|
+
{
|
|
76
|
+
heading: 'Parent',
|
|
77
|
+
level: 2,
|
|
78
|
+
originalTokens: 100,
|
|
79
|
+
summaryTokens: 20,
|
|
80
|
+
summary: 'Short parent.',
|
|
81
|
+
children: [
|
|
82
|
+
{
|
|
83
|
+
heading: 'Child Fits',
|
|
84
|
+
level: 3,
|
|
85
|
+
originalTokens: 20,
|
|
86
|
+
summaryTokens: 10,
|
|
87
|
+
summary: 'Small.',
|
|
88
|
+
children: [],
|
|
89
|
+
hasCode: false,
|
|
90
|
+
hasList: false,
|
|
91
|
+
hasTable: false,
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
heading: 'Child Too Large',
|
|
95
|
+
level: 3,
|
|
96
|
+
originalTokens: 300,
|
|
97
|
+
summaryTokens: 150,
|
|
98
|
+
summary: 'Very long child. '.repeat(30),
|
|
99
|
+
children: [],
|
|
100
|
+
hasCode: false,
|
|
101
|
+
hasList: false,
|
|
102
|
+
hasTable: false,
|
|
103
|
+
},
|
|
104
|
+
],
|
|
105
|
+
hasCode: false,
|
|
106
|
+
hasList: false,
|
|
107
|
+
hasTable: false,
|
|
108
|
+
},
|
|
109
|
+
],
|
|
110
|
+
keyTopics: [],
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Budget increased to account for enhanced truncation warning overhead
|
|
114
|
+
const output = formatSummary(mockSummary, { maxTokens: 200 })
|
|
115
|
+
|
|
116
|
+
// Parent and first child should be included
|
|
117
|
+
expect(output).toContain('Parent')
|
|
118
|
+
expect(output).toContain('Child Fits')
|
|
119
|
+
// Large child should be truncated
|
|
120
|
+
expect(output).not.toContain('Child Too Large')
|
|
121
|
+
})
|
|
122
|
+
})
|
|
123
|
+
|
|
124
|
+
it('includes sections greedily up to budget', () => {
|
|
125
|
+
const mockSummary = {
|
|
126
|
+
path: '/test/file.md',
|
|
127
|
+
title: 'Test Document',
|
|
128
|
+
originalTokens: 1000,
|
|
129
|
+
summaryTokens: 500,
|
|
130
|
+
compressionRatio: 0.5,
|
|
131
|
+
sections: [
|
|
132
|
+
{
|
|
133
|
+
heading: 'Introduction',
|
|
134
|
+
level: 2,
|
|
135
|
+
originalTokens: 200,
|
|
136
|
+
summaryTokens: 100,
|
|
137
|
+
summary: 'Short intro.',
|
|
138
|
+
children: [
|
|
139
|
+
{
|
|
140
|
+
heading: 'Subsection A',
|
|
141
|
+
level: 3,
|
|
142
|
+
originalTokens: 50,
|
|
143
|
+
summaryTokens: 20,
|
|
144
|
+
summary: 'Small subsection A.',
|
|
145
|
+
children: [],
|
|
146
|
+
hasCode: false,
|
|
147
|
+
hasList: false,
|
|
148
|
+
hasTable: false,
|
|
149
|
+
},
|
|
150
|
+
{
|
|
151
|
+
heading: 'Subsection B',
|
|
152
|
+
level: 3,
|
|
153
|
+
originalTokens: 50,
|
|
154
|
+
summaryTokens: 20,
|
|
155
|
+
summary: 'Small subsection B.',
|
|
156
|
+
children: [],
|
|
157
|
+
hasCode: false,
|
|
158
|
+
hasList: false,
|
|
159
|
+
hasTable: false,
|
|
160
|
+
},
|
|
161
|
+
],
|
|
162
|
+
hasCode: false,
|
|
163
|
+
hasList: false,
|
|
164
|
+
hasTable: false,
|
|
165
|
+
},
|
|
166
|
+
],
|
|
167
|
+
keyTopics: ['topic1', 'topic2'],
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// Budget should fit header + all sections with small content
|
|
171
|
+
// Increased to account for enhanced truncation warning overhead
|
|
172
|
+
const output = formatSummary(mockSummary, { maxTokens: 250 })
|
|
173
|
+
|
|
174
|
+
// All sections should be included since they're small
|
|
175
|
+
expect(output).toContain('Introduction')
|
|
176
|
+
expect(output).toContain('Subsection A')
|
|
177
|
+
expect(output).toContain('Subsection B')
|
|
178
|
+
})
|
|
179
|
+
|
|
180
|
+
it('stays within budget for long paths and titles', () => {
|
|
181
|
+
const mockSummary = {
|
|
182
|
+
path: '/very/long/path/to/deeply/nested/directory/structure/that/keeps/going/file.md',
|
|
183
|
+
title:
|
|
184
|
+
'A Very Long Document Title That Uses Significantly More Tokens Than Expected',
|
|
185
|
+
originalTokens: 1000,
|
|
186
|
+
summaryTokens: 100,
|
|
187
|
+
compressionRatio: 0.9,
|
|
188
|
+
sections: [
|
|
189
|
+
{
|
|
190
|
+
heading: 'Section',
|
|
191
|
+
level: 2,
|
|
192
|
+
originalTokens: 100,
|
|
193
|
+
summaryTokens: 50,
|
|
194
|
+
summary: 'Some content.',
|
|
195
|
+
children: [],
|
|
196
|
+
hasCode: false,
|
|
197
|
+
hasList: false,
|
|
198
|
+
hasTable: false,
|
|
199
|
+
},
|
|
200
|
+
],
|
|
201
|
+
keyTopics: [
|
|
202
|
+
'topic1',
|
|
203
|
+
'topic2',
|
|
204
|
+
'topic3',
|
|
205
|
+
'topic4',
|
|
206
|
+
'topic5',
|
|
207
|
+
'long-topic-name',
|
|
208
|
+
],
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
const output = formatSummary(mockSummary, { maxTokens: 100 })
|
|
212
|
+
const actualTokens = countTokensApprox(output)
|
|
213
|
+
|
|
214
|
+
// Output should stay within budget
|
|
215
|
+
expect(actualTokens).toBeLessThanOrEqual(100)
|
|
216
|
+
})
|
|
217
|
+
|
|
218
|
+
it('drops topics when header overhead exceeds budget', () => {
|
|
219
|
+
const mockSummary = {
|
|
220
|
+
path: '/very/long/path/file.md',
|
|
221
|
+
title: 'Long Title Here',
|
|
222
|
+
originalTokens: 1000,
|
|
223
|
+
summaryTokens: 100,
|
|
224
|
+
compressionRatio: 0.9,
|
|
225
|
+
sections: [],
|
|
226
|
+
keyTopics: ['topic1', 'topic2', 'topic3', 'topic4'],
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// Very tight budget - should drop topics to make room
|
|
230
|
+
const output = formatSummary(mockSummary, { maxTokens: 50 })
|
|
231
|
+
|
|
232
|
+
// With tight budget, topics may be dropped to fit
|
|
233
|
+
const actualTokens = countTokensApprox(output)
|
|
234
|
+
expect(actualTokens).toBeLessThanOrEqual(50)
|
|
235
|
+
})
|
|
236
|
+
|
|
237
|
+
describe('strict budget enforcement', () => {
|
|
238
|
+
it('never exceeds specified token budget when budget is achievable', () => {
|
|
239
|
+
// Test with realistic budgets - the minimum header for this document is ~60 tokens
|
|
240
|
+
// (long path + long title + token line + truncation warning)
|
|
241
|
+
// So we test budgets that should be achievable
|
|
242
|
+
const budgets = [75, 100, 150, 200, 300, 500]
|
|
243
|
+
|
|
244
|
+
for (const budget of budgets) {
|
|
245
|
+
const mockSummary: DocumentSummary = {
|
|
246
|
+
path: '/project/src/components/deeply/nested/component.tsx',
|
|
247
|
+
title: 'A Component With A Moderately Long Title',
|
|
248
|
+
originalTokens: 2000,
|
|
249
|
+
summaryTokens: 800,
|
|
250
|
+
compressionRatio: 0.6,
|
|
251
|
+
sections: [
|
|
252
|
+
{
|
|
253
|
+
heading: 'Overview',
|
|
254
|
+
level: 2,
|
|
255
|
+
originalTokens: 200,
|
|
256
|
+
summaryTokens: 80,
|
|
257
|
+
summary:
|
|
258
|
+
'This component handles user authentication and session management with proper error handling.',
|
|
259
|
+
children: [
|
|
260
|
+
{
|
|
261
|
+
heading: 'Props',
|
|
262
|
+
level: 3,
|
|
263
|
+
originalTokens: 100,
|
|
264
|
+
summaryTokens: 40,
|
|
265
|
+
summary:
|
|
266
|
+
'Accepts user object, onLogin callback, and configuration options.',
|
|
267
|
+
children: [],
|
|
268
|
+
hasCode: true,
|
|
269
|
+
hasList: false,
|
|
270
|
+
hasTable: false,
|
|
271
|
+
},
|
|
272
|
+
],
|
|
273
|
+
hasCode: false,
|
|
274
|
+
hasList: false,
|
|
275
|
+
hasTable: false,
|
|
276
|
+
},
|
|
277
|
+
{
|
|
278
|
+
heading: 'Implementation',
|
|
279
|
+
level: 2,
|
|
280
|
+
originalTokens: 300,
|
|
281
|
+
summaryTokens: 120,
|
|
282
|
+
summary:
|
|
283
|
+
'Uses React hooks for state management. Implements OAuth2 flow with PKCE.',
|
|
284
|
+
children: [],
|
|
285
|
+
hasCode: true,
|
|
286
|
+
hasList: true,
|
|
287
|
+
hasTable: false,
|
|
288
|
+
},
|
|
289
|
+
],
|
|
290
|
+
keyTopics: ['react', 'authentication', 'oauth', 'hooks', 'security'],
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
const output = formatSummary(mockSummary, { maxTokens: budget })
|
|
294
|
+
const actualTokens = countTokensApprox(output)
|
|
295
|
+
|
|
296
|
+
expect(actualTokens).toBeLessThanOrEqual(budget)
|
|
297
|
+
}
|
|
298
|
+
})
|
|
299
|
+
|
|
300
|
+
it('handles tight budgets with short paths gracefully', () => {
|
|
301
|
+
// With a short path and title, we can achieve tighter budgets
|
|
302
|
+
// Minimum header for this doc is roughly:
|
|
303
|
+
// "# X\nPath: x\nTokens: XX (Y% reduction from Z)\n\n" ~= 20-25 tokens
|
|
304
|
+
const mockSummary: DocumentSummary = {
|
|
305
|
+
path: 'x',
|
|
306
|
+
title: 'X',
|
|
307
|
+
originalTokens: 100,
|
|
308
|
+
summaryTokens: 50,
|
|
309
|
+
compressionRatio: 0.5,
|
|
310
|
+
sections: [
|
|
311
|
+
{
|
|
312
|
+
heading: 'S',
|
|
313
|
+
level: 2,
|
|
314
|
+
originalTokens: 50,
|
|
315
|
+
summaryTokens: 25,
|
|
316
|
+
summary: 'Text.',
|
|
317
|
+
children: [],
|
|
318
|
+
hasCode: false,
|
|
319
|
+
hasList: false,
|
|
320
|
+
hasTable: false,
|
|
321
|
+
},
|
|
322
|
+
],
|
|
323
|
+
keyTopics: [],
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
// Test with a budget that should definitely fit the minimal header
|
|
327
|
+
const output = formatSummary(mockSummary, { maxTokens: 40 })
|
|
328
|
+
const actualTokens = countTokensApprox(output)
|
|
329
|
+
|
|
330
|
+
expect(actualTokens).toBeLessThanOrEqual(40)
|
|
331
|
+
})
|
|
332
|
+
|
|
333
|
+
it('stays within budget even with code-heavy content', () => {
|
|
334
|
+
const mockSummary: DocumentSummary = {
|
|
335
|
+
path: '/src/utils/parser.ts',
|
|
336
|
+
title: 'Parser Utilities',
|
|
337
|
+
originalTokens: 1500,
|
|
338
|
+
summaryTokens: 600,
|
|
339
|
+
compressionRatio: 0.6,
|
|
340
|
+
sections: [
|
|
341
|
+
{
|
|
342
|
+
heading: 'Functions',
|
|
343
|
+
level: 2,
|
|
344
|
+
originalTokens: 500,
|
|
345
|
+
summaryTokens: 200,
|
|
346
|
+
summary:
|
|
347
|
+
'```typescript\nfunction parse(input: string): AST {\n const tokens = tokenize(input);\n return buildTree(tokens);\n}\n```',
|
|
348
|
+
children: [],
|
|
349
|
+
hasCode: true,
|
|
350
|
+
hasList: false,
|
|
351
|
+
hasTable: false,
|
|
352
|
+
},
|
|
353
|
+
],
|
|
354
|
+
keyTopics: ['parser', 'ast', 'typescript'],
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
const output = formatSummary(mockSummary, { maxTokens: 100 })
|
|
358
|
+
const actualTokens = countTokensApprox(output)
|
|
359
|
+
|
|
360
|
+
expect(actualTokens).toBeLessThanOrEqual(100)
|
|
361
|
+
})
|
|
362
|
+
})
|
|
363
|
+
|
|
364
|
+
describe('deeply nested content', () => {
|
|
365
|
+
it('handles 3+ levels of nesting with orphan rescue', () => {
|
|
366
|
+
const mockSummary: DocumentSummary = {
|
|
367
|
+
path: '/test/file.md',
|
|
368
|
+
title: 'Deep',
|
|
369
|
+
originalTokens: 1000,
|
|
370
|
+
summaryTokens: 500,
|
|
371
|
+
compressionRatio: 0.5,
|
|
372
|
+
sections: [
|
|
373
|
+
{
|
|
374
|
+
heading: 'Large L1',
|
|
375
|
+
level: 2,
|
|
376
|
+
originalTokens: 400,
|
|
377
|
+
summaryTokens: 200,
|
|
378
|
+
summary: 'Large level 1 content. '.repeat(40),
|
|
379
|
+
children: [
|
|
380
|
+
{
|
|
381
|
+
heading: 'Large L2',
|
|
382
|
+
level: 3,
|
|
383
|
+
originalTokens: 200,
|
|
384
|
+
summaryTokens: 100,
|
|
385
|
+
summary: 'Large level 2 content. '.repeat(20),
|
|
386
|
+
children: [
|
|
387
|
+
{
|
|
388
|
+
heading: 'Small L3',
|
|
389
|
+
level: 4,
|
|
390
|
+
originalTokens: 20,
|
|
391
|
+
summaryTokens: 10,
|
|
392
|
+
summary: 'Small.',
|
|
393
|
+
children: [],
|
|
394
|
+
hasCode: false,
|
|
395
|
+
hasList: false,
|
|
396
|
+
hasTable: false,
|
|
397
|
+
},
|
|
398
|
+
],
|
|
399
|
+
hasCode: false,
|
|
400
|
+
hasList: false,
|
|
401
|
+
hasTable: false,
|
|
402
|
+
},
|
|
403
|
+
],
|
|
404
|
+
hasCode: false,
|
|
405
|
+
hasList: false,
|
|
406
|
+
hasTable: false,
|
|
407
|
+
},
|
|
408
|
+
],
|
|
409
|
+
keyTopics: [],
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
// Increased budget to account for enhanced truncation warning overhead
|
|
413
|
+
const output = formatSummary(mockSummary, { maxTokens: 180 })
|
|
414
|
+
|
|
415
|
+
// Deepest child should still be rescued
|
|
416
|
+
expect(output).toContain('Small L3')
|
|
417
|
+
// Large ancestors should be skipped
|
|
418
|
+
expect(output).not.toContain('Large L1')
|
|
419
|
+
expect(output).not.toContain('Large L2')
|
|
420
|
+
// And stay within budget
|
|
421
|
+
expect(countTokensApprox(output)).toBeLessThanOrEqual(180)
|
|
422
|
+
})
|
|
423
|
+
|
|
424
|
+
it('includes multiple siblings when all fit', () => {
|
|
425
|
+
const mockSummary: DocumentSummary = {
|
|
426
|
+
path: '/test/file.md',
|
|
427
|
+
title: 'Siblings',
|
|
428
|
+
originalTokens: 500,
|
|
429
|
+
summaryTokens: 200,
|
|
430
|
+
compressionRatio: 0.6,
|
|
431
|
+
sections: [
|
|
432
|
+
{
|
|
433
|
+
heading: 'A',
|
|
434
|
+
level: 2,
|
|
435
|
+
originalTokens: 30,
|
|
436
|
+
summaryTokens: 10,
|
|
437
|
+
summary: 'Content A.',
|
|
438
|
+
children: [],
|
|
439
|
+
hasCode: false,
|
|
440
|
+
hasList: false,
|
|
441
|
+
hasTable: false,
|
|
442
|
+
},
|
|
443
|
+
{
|
|
444
|
+
heading: 'B',
|
|
445
|
+
level: 2,
|
|
446
|
+
originalTokens: 30,
|
|
447
|
+
summaryTokens: 10,
|
|
448
|
+
summary: 'Content B.',
|
|
449
|
+
children: [],
|
|
450
|
+
hasCode: false,
|
|
451
|
+
hasList: false,
|
|
452
|
+
hasTable: false,
|
|
453
|
+
},
|
|
454
|
+
{
|
|
455
|
+
heading: 'C',
|
|
456
|
+
level: 2,
|
|
457
|
+
originalTokens: 30,
|
|
458
|
+
summaryTokens: 10,
|
|
459
|
+
summary: 'Content C.',
|
|
460
|
+
children: [],
|
|
461
|
+
hasCode: false,
|
|
462
|
+
hasList: false,
|
|
463
|
+
hasTable: false,
|
|
464
|
+
},
|
|
465
|
+
],
|
|
466
|
+
keyTopics: [],
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
// Increased budget to account for enhanced truncation warning overhead
|
|
470
|
+
const output = formatSummary(mockSummary, { maxTokens: 200 })
|
|
471
|
+
|
|
472
|
+
// All small sections should fit
|
|
473
|
+
expect(output).toContain('A')
|
|
474
|
+
expect(output).toContain('B')
|
|
475
|
+
expect(output).toContain('C')
|
|
476
|
+
expect(countTokensApprox(output)).toBeLessThanOrEqual(200)
|
|
477
|
+
})
|
|
478
|
+
})
|
|
479
|
+
|
|
480
|
+
describe('edge cases', () => {
|
|
481
|
+
it('handles empty sections array', () => {
|
|
482
|
+
const mockSummary: DocumentSummary = {
|
|
483
|
+
path: '/test/file.md',
|
|
484
|
+
title: 'Empty',
|
|
485
|
+
originalTokens: 100,
|
|
486
|
+
summaryTokens: 0,
|
|
487
|
+
compressionRatio: 1.0,
|
|
488
|
+
sections: [],
|
|
489
|
+
keyTopics: [],
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
const output = formatSummary(mockSummary, { maxTokens: 50 })
|
|
493
|
+
expect(output).toContain('# Empty')
|
|
494
|
+
expect(countTokensApprox(output)).toBeLessThanOrEqual(50)
|
|
495
|
+
})
|
|
496
|
+
|
|
497
|
+
it('handles section with empty summary', () => {
|
|
498
|
+
const mockSummary: DocumentSummary = {
|
|
499
|
+
path: '/test/file.md',
|
|
500
|
+
title: 'Test',
|
|
501
|
+
originalTokens: 100,
|
|
502
|
+
summaryTokens: 10,
|
|
503
|
+
compressionRatio: 0.9,
|
|
504
|
+
sections: [
|
|
505
|
+
{
|
|
506
|
+
heading: 'Empty',
|
|
507
|
+
level: 2,
|
|
508
|
+
originalTokens: 10,
|
|
509
|
+
summaryTokens: 5,
|
|
510
|
+
summary: '',
|
|
511
|
+
children: [],
|
|
512
|
+
hasCode: false,
|
|
513
|
+
hasList: false,
|
|
514
|
+
hasTable: false,
|
|
515
|
+
},
|
|
516
|
+
],
|
|
517
|
+
keyTopics: [],
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
// Use larger budget so section fits (increased for enhanced truncation warning overhead)
|
|
521
|
+
const output = formatSummary(mockSummary, { maxTokens: 150 })
|
|
522
|
+
expect(output).toContain('## Empty')
|
|
523
|
+
expect(countTokensApprox(output)).toBeLessThanOrEqual(150)
|
|
524
|
+
})
|
|
525
|
+
|
|
526
|
+
it('handles unicode characters in path and title', () => {
|
|
527
|
+
const mockSummary: DocumentSummary = {
|
|
528
|
+
path: '/docs/日本語/ファイル.md',
|
|
529
|
+
title: '日本語のドキュメント',
|
|
530
|
+
originalTokens: 200,
|
|
531
|
+
summaryTokens: 50,
|
|
532
|
+
compressionRatio: 0.75,
|
|
533
|
+
sections: [
|
|
534
|
+
{
|
|
535
|
+
heading: 'セクション',
|
|
536
|
+
level: 2,
|
|
537
|
+
originalTokens: 50,
|
|
538
|
+
summaryTokens: 20,
|
|
539
|
+
summary: 'コンテンツ。',
|
|
540
|
+
children: [],
|
|
541
|
+
hasCode: false,
|
|
542
|
+
hasList: false,
|
|
543
|
+
hasTable: false,
|
|
544
|
+
},
|
|
545
|
+
],
|
|
546
|
+
keyTopics: ['日本語'],
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
const output = formatSummary(mockSummary, { maxTokens: 100 })
|
|
550
|
+
expect(output).toContain('日本語のドキュメント')
|
|
551
|
+
expect(countTokensApprox(output)).toBeLessThanOrEqual(100)
|
|
552
|
+
})
|
|
553
|
+
|
|
554
|
+
it('drops truncation warning when budget is very tight', () => {
|
|
555
|
+
const mockSummary: DocumentSummary = {
|
|
556
|
+
path: '/test/file.md',
|
|
557
|
+
title: 'Short Title',
|
|
558
|
+
originalTokens: 500,
|
|
559
|
+
summaryTokens: 200,
|
|
560
|
+
compressionRatio: 0.6,
|
|
561
|
+
sections: [
|
|
562
|
+
{
|
|
563
|
+
heading: 'Large Section',
|
|
564
|
+
level: 2,
|
|
565
|
+
originalTokens: 400,
|
|
566
|
+
summaryTokens: 150,
|
|
567
|
+
summary: 'Large content that will be truncated. '.repeat(20),
|
|
568
|
+
children: [],
|
|
569
|
+
hasCode: false,
|
|
570
|
+
hasList: false,
|
|
571
|
+
hasTable: false,
|
|
572
|
+
},
|
|
573
|
+
],
|
|
574
|
+
keyTopics: [],
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
// Very tight budget that can fit header but not with truncation warning
|
|
578
|
+
// Increased to account for more conservative token counting
|
|
579
|
+
const output = formatSummary(mockSummary, { maxTokens: 35 })
|
|
580
|
+
|
|
581
|
+
// Should stay within budget
|
|
582
|
+
expect(countTokensApprox(output)).toBeLessThanOrEqual(35)
|
|
583
|
+
// Should NOT contain truncation warning (dropped to fit) - lowercase in new format
|
|
584
|
+
expect(output).not.toContain('Truncated')
|
|
585
|
+
})
|
|
586
|
+
|
|
587
|
+
it('shows truncation warning when budget allows', () => {
|
|
588
|
+
const mockSummary: DocumentSummary = {
|
|
589
|
+
path: '/test/file.md',
|
|
590
|
+
title: 'Short Title',
|
|
591
|
+
originalTokens: 500,
|
|
592
|
+
summaryTokens: 200,
|
|
593
|
+
compressionRatio: 0.6,
|
|
594
|
+
sections: [
|
|
595
|
+
{
|
|
596
|
+
heading: 'Large Section',
|
|
597
|
+
level: 2,
|
|
598
|
+
originalTokens: 400,
|
|
599
|
+
summaryTokens: 150,
|
|
600
|
+
summary: 'Large content that will be truncated. '.repeat(20),
|
|
601
|
+
children: [],
|
|
602
|
+
hasCode: false,
|
|
603
|
+
hasList: false,
|
|
604
|
+
hasTable: false,
|
|
605
|
+
},
|
|
606
|
+
],
|
|
607
|
+
keyTopics: [],
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
// Larger budget that can fit header AND truncation warning
|
|
611
|
+
// Increased to account for enhanced truncation warning with section lists
|
|
612
|
+
const output = formatSummary(mockSummary, { maxTokens: 150 })
|
|
613
|
+
|
|
614
|
+
// Should stay within budget
|
|
615
|
+
expect(countTokensApprox(output)).toBeLessThanOrEqual(150)
|
|
616
|
+
// Should contain truncation warning (enough room) - note: lowercase "Truncated" in new format
|
|
617
|
+
expect(output).toContain('Truncated')
|
|
618
|
+
})
|
|
619
|
+
})
|
|
620
|
+
})
|