prjct-cli 1.22.0 → 1.23.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/CHANGELOG.md +147 -0
- package/bin/prjct +30 -13
- package/dist/bin/prjct.mjs +917 -35845
- package/dist/bin/prjct.mjs.map +7 -0
- package/dist/cli/linear.mjs +16 -0
- package/dist/cli/linear.mjs.map +7 -0
- package/dist/templates.json +1 -0
- package/package.json +4 -5
- package/bin/prjct.ts +0 -342
- package/core/__tests__/agentic/analysis-injection.test.ts +0 -377
- package/core/__tests__/agentic/cache-eviction.test.ts +0 -294
- package/core/__tests__/agentic/command-context.test.ts +0 -281
- package/core/__tests__/agentic/command-executor.test.ts +0 -659
- package/core/__tests__/agentic/domain-classifier.test.ts +0 -330
- package/core/__tests__/agentic/injection-validator.test.ts +0 -255
- package/core/__tests__/agentic/memory-system.test.ts +0 -281
- package/core/__tests__/agentic/plan-mode.test.ts +0 -386
- package/core/__tests__/agentic/prompt-assembly.test.ts +0 -298
- package/core/__tests__/agentic/prompt-builder.test.ts +0 -243
- package/core/__tests__/agentic/response-validator.test.ts +0 -263
- package/core/__tests__/agentic/semantic-matching.test.ts +0 -131
- package/core/__tests__/agentic/smart-context.test.ts +0 -372
- package/core/__tests__/agentic/tech-normalizer.test.ts +0 -136
- package/core/__tests__/agentic/token-budget.test.ts +0 -294
- package/core/__tests__/ai-tools/formatters.test.ts +0 -476
- package/core/__tests__/domain/bm25.test.ts +0 -225
- package/core/__tests__/domain/change-propagator.test.ts +0 -100
- package/core/__tests__/domain/fibonacci.test.ts +0 -113
- package/core/__tests__/domain/file-hasher.test.ts +0 -146
- package/core/__tests__/domain/file-ranker.test.ts +0 -169
- package/core/__tests__/domain/git-cochange.test.ts +0 -121
- package/core/__tests__/domain/import-graph.test.ts +0 -156
- package/core/__tests__/domain/velocity.test.ts +0 -623
- package/core/__tests__/infrastructure/performance-tracker.test.ts +0 -328
- package/core/__tests__/schemas/model.test.ts +0 -272
- package/core/__tests__/services/dependency-validator.test.ts +0 -175
- package/core/__tests__/services/hierarchical-agent-resolver.test.ts +0 -359
- package/core/__tests__/services/nested-context-resolver.test.ts +0 -443
- package/core/__tests__/services/project-index.test.ts +0 -355
- package/core/__tests__/services/staleness-checker.test.ts +0 -204
- package/core/__tests__/storage/analysis-storage.test.ts +0 -641
- package/core/__tests__/storage/archive-storage.test.ts +0 -455
- package/core/__tests__/storage/safe-reader.test.ts +0 -262
- package/core/__tests__/storage/sqlite-migration.test.ts +0 -1016
- package/core/__tests__/storage/state-storage-feedback.test.ts +0 -463
- package/core/__tests__/storage/state-storage-history.test.ts +0 -469
- package/core/__tests__/storage/storage-manager.test.ts +0 -383
- package/core/__tests__/storage/subtask-handoff.test.ts +0 -237
- package/core/__tests__/types/fs.test.ts +0 -125
- package/core/__tests__/utils/date-helper.test.ts +0 -449
- package/core/__tests__/utils/output.test.ts +0 -278
- package/core/__tests__/utils/preserve-sections.test.ts +0 -216
- package/core/__tests__/utils/project-commands.test.ts +0 -71
- package/core/__tests__/utils/retry.test.ts +0 -381
- package/core/__tests__/workflow/state-machine.test.ts +0 -216
- package/core/agentic/agent-router.ts +0 -150
- package/core/agentic/anti-hallucination.ts +0 -141
- package/core/agentic/chain-of-thought.ts +0 -234
- package/core/agentic/command-classifier.ts +0 -141
- package/core/agentic/command-context.ts +0 -168
- package/core/agentic/command-executor.ts +0 -471
- package/core/agentic/context-builder.ts +0 -285
- package/core/agentic/domain-classifier.ts +0 -525
- package/core/agentic/environment-block.ts +0 -102
- package/core/agentic/ground-truth.ts +0 -706
- package/core/agentic/index.ts +0 -193
- package/core/agentic/injection-validator.ts +0 -208
- package/core/agentic/loop-detector.ts +0 -451
- package/core/agentic/memory-system.ts +0 -1547
- package/core/agentic/orchestrator-executor.ts +0 -579
- package/core/agentic/plan-mode.ts +0 -525
- package/core/agentic/prompt-builder.ts +0 -1069
- package/core/agentic/response-validator.ts +0 -98
- package/core/agentic/services.ts +0 -167
- package/core/agentic/skill-loader.ts +0 -106
- package/core/agentic/smart-context.ts +0 -393
- package/core/agentic/tech-normalizer.ts +0 -167
- package/core/agentic/template-executor.ts +0 -272
- package/core/agentic/template-loader.ts +0 -109
- package/core/agentic/token-budget.ts +0 -226
- package/core/agentic/tool-registry.ts +0 -146
- package/core/agents/index.ts +0 -28
- package/core/agents/performance.ts +0 -429
- package/core/ai-tools/formatters.ts +0 -341
- package/core/ai-tools/generator.ts +0 -144
- package/core/ai-tools/index.ts +0 -15
- package/core/ai-tools/registry.ts +0 -201
- package/core/bus/bus.ts +0 -314
- package/core/bus/index.ts +0 -8
- package/core/cli/linear.ts +0 -500
- package/core/cli/lint-meta-commentary.ts +0 -177
- package/core/cli/start.ts +0 -386
- package/core/commands/analysis.ts +0 -1274
- package/core/commands/analytics.ts +0 -342
- package/core/commands/base.ts +0 -118
- package/core/commands/cleanup.ts +0 -157
- package/core/commands/command-data.ts +0 -463
- package/core/commands/commands.ts +0 -306
- package/core/commands/context.ts +0 -238
- package/core/commands/design.ts +0 -77
- package/core/commands/index.ts +0 -19
- package/core/commands/maintenance.ts +0 -77
- package/core/commands/performance.ts +0 -114
- package/core/commands/planning.ts +0 -662
- package/core/commands/register.ts +0 -127
- package/core/commands/registry.ts +0 -444
- package/core/commands/setup.ts +0 -280
- package/core/commands/shipping.ts +0 -267
- package/core/commands/snapshots.ts +0 -297
- package/core/commands/uninstall.ts +0 -542
- package/core/commands/velocity.ts +0 -149
- package/core/commands/workflow.ts +0 -505
- package/core/config/command-context.config.json +0 -66
- package/core/constants/index.ts +0 -379
- package/core/context/generator.ts +0 -368
- package/core/context-tools/files-tool.ts +0 -577
- package/core/context-tools/imports-tool.ts +0 -400
- package/core/context-tools/index.ts +0 -434
- package/core/context-tools/recent-tool.ts +0 -301
- package/core/context-tools/signatures-tool.ts +0 -495
- package/core/context-tools/summary-tool.ts +0 -301
- package/core/context-tools/token-counter.ts +0 -273
- package/core/context-tools/types.ts +0 -253
- package/core/domain/agent-generator.ts +0 -186
- package/core/domain/agent-loader.ts +0 -419
- package/core/domain/analyzer.ts +0 -387
- package/core/domain/architecture-generator.ts +0 -108
- package/core/domain/bm25.ts +0 -525
- package/core/domain/change-propagator.ts +0 -162
- package/core/domain/context-estimator.ts +0 -175
- package/core/domain/fibonacci.ts +0 -128
- package/core/domain/file-hasher.ts +0 -296
- package/core/domain/file-ranker.ts +0 -151
- package/core/domain/git-cochange.ts +0 -250
- package/core/domain/import-graph.ts +0 -315
- package/core/domain/snapshot-manager.ts +0 -415
- package/core/domain/task-stack.ts +0 -578
- package/core/domain/velocity.ts +0 -470
- package/core/errors.ts +0 -335
- package/core/events/events.ts +0 -85
- package/core/events/index.ts +0 -8
- package/core/index.ts +0 -481
- package/core/infrastructure/agent-detector.ts +0 -135
- package/core/infrastructure/ai-provider.ts +0 -578
- package/core/infrastructure/author-detector.ts +0 -133
- package/core/infrastructure/capability-installer.ts +0 -76
- package/core/infrastructure/claude-agent.ts +0 -297
- package/core/infrastructure/command-installer.ts +0 -752
- package/core/infrastructure/config-manager.ts +0 -364
- package/core/infrastructure/editors-config.ts +0 -172
- package/core/infrastructure/path-manager.ts +0 -571
- package/core/infrastructure/performance-tracker.ts +0 -326
- package/core/infrastructure/permission-manager.ts +0 -289
- package/core/infrastructure/setup.ts +0 -1061
- package/core/infrastructure/update-checker.ts +0 -246
- package/core/integrations/issue-tracker/enricher.ts +0 -271
- package/core/integrations/issue-tracker/index.ts +0 -8
- package/core/integrations/issue-tracker/manager.ts +0 -286
- package/core/integrations/issue-tracker/types.ts +0 -310
- package/core/integrations/jira/cache.ts +0 -57
- package/core/integrations/jira/client.ts +0 -688
- package/core/integrations/jira/index.ts +0 -23
- package/core/integrations/jira/service.ts +0 -244
- package/core/integrations/linear/cache.ts +0 -68
- package/core/integrations/linear/client.ts +0 -436
- package/core/integrations/linear/index.ts +0 -20
- package/core/integrations/linear/service.ts +0 -260
- package/core/integrations/linear/sync.ts +0 -314
- package/core/outcomes/analyzer.ts +0 -286
- package/core/outcomes/index.ts +0 -34
- package/core/outcomes/recorder.ts +0 -195
- package/core/plugin/builtin/webhook.ts +0 -148
- package/core/plugin/hooks.ts +0 -315
- package/core/plugin/index.ts +0 -50
- package/core/plugin/loader.ts +0 -354
- package/core/plugin/registry.ts +0 -326
- package/core/schemas/agents.ts +0 -27
- package/core/schemas/analysis.ts +0 -530
- package/core/schemas/classification.ts +0 -91
- package/core/schemas/command-context.ts +0 -29
- package/core/schemas/enriched-task.ts +0 -291
- package/core/schemas/ideas.ts +0 -114
- package/core/schemas/index.ts +0 -53
- package/core/schemas/issues.ts +0 -159
- package/core/schemas/llm-output.ts +0 -170
- package/core/schemas/metrics.ts +0 -143
- package/core/schemas/model.ts +0 -153
- package/core/schemas/outcomes.ts +0 -487
- package/core/schemas/performance.ts +0 -128
- package/core/schemas/permissions.ts +0 -180
- package/core/schemas/prd.ts +0 -450
- package/core/schemas/project.ts +0 -57
- package/core/schemas/roadmap.ts +0 -322
- package/core/schemas/schemas.ts +0 -38
- package/core/schemas/shipped.ts +0 -109
- package/core/schemas/state.ts +0 -284
- package/core/schemas/velocity.ts +0 -103
- package/core/server/index.ts +0 -21
- package/core/server/routes-extended.ts +0 -566
- package/core/server/routes.ts +0 -176
- package/core/server/server.ts +0 -149
- package/core/server/sse.ts +0 -192
- package/core/services/agent-generator.ts +0 -385
- package/core/services/agent-service.ts +0 -168
- package/core/services/breakdown-service.ts +0 -124
- package/core/services/context-generator.ts +0 -445
- package/core/services/context-selector.ts +0 -429
- package/core/services/dependency-validator.ts +0 -318
- package/core/services/diff-generator.ts +0 -313
- package/core/services/doctor-service.ts +0 -423
- package/core/services/file-categorizer.ts +0 -448
- package/core/services/file-scorer.ts +0 -270
- package/core/services/git-analyzer.ts +0 -293
- package/core/services/hierarchical-agent-resolver.ts +0 -236
- package/core/services/hooks-service.ts +0 -685
- package/core/services/index.ts +0 -46
- package/core/services/local-state-generator.ts +0 -158
- package/core/services/memory-service.ts +0 -181
- package/core/services/nested-context-resolver.ts +0 -842
- package/core/services/project-index.ts +0 -911
- package/core/services/project-service.ts +0 -155
- package/core/services/session-tracker.ts +0 -287
- package/core/services/skill-installer.ts +0 -447
- package/core/services/skill-lock.ts +0 -132
- package/core/services/skill-service.ts +0 -306
- package/core/services/stack-detector.ts +0 -229
- package/core/services/staleness-checker.ts +0 -327
- package/core/services/sync-service.ts +0 -1515
- package/core/services/sync-verifier.ts +0 -253
- package/core/services/watch-service.ts +0 -312
- package/core/session/compaction.ts +0 -248
- package/core/session/index.ts +0 -35
- package/core/session/log-migration.ts +0 -88
- package/core/session/metrics.ts +0 -323
- package/core/session/session-log-manager.ts +0 -307
- package/core/session/task-session-manager.ts +0 -404
- package/core/session/utils.ts +0 -51
- package/core/storage/analysis-storage.ts +0 -373
- package/core/storage/archive-storage.ts +0 -205
- package/core/storage/database.ts +0 -575
- package/core/storage/ideas-storage.ts +0 -298
- package/core/storage/index-storage.ts +0 -523
- package/core/storage/index.ts +0 -79
- package/core/storage/metrics-storage.ts +0 -321
- package/core/storage/migrate-json.ts +0 -720
- package/core/storage/queue-storage.ts +0 -336
- package/core/storage/safe-reader.ts +0 -105
- package/core/storage/shipped-storage.ts +0 -253
- package/core/storage/state-storage.ts +0 -1035
- package/core/storage/storage-manager.ts +0 -205
- package/core/storage/storage.ts +0 -177
- package/core/storage/velocity-storage.ts +0 -149
- package/core/sync/auth-config.ts +0 -138
- package/core/sync/index.ts +0 -31
- package/core/sync/oauth-handler.ts +0 -143
- package/core/sync/sync-client.ts +0 -251
- package/core/sync/sync-manager.ts +0 -327
- package/core/tsconfig.json +0 -22
- package/core/types/agentic.ts +0 -760
- package/core/types/agents.ts +0 -150
- package/core/types/bus.ts +0 -193
- package/core/types/citations.ts +0 -22
- package/core/types/commands.ts +0 -399
- package/core/types/config.ts +0 -92
- package/core/types/core.ts +0 -96
- package/core/types/diff.ts +0 -41
- package/core/types/domain.ts +0 -71
- package/core/types/errors.ts +0 -111
- package/core/types/events.ts +0 -42
- package/core/types/fs.ts +0 -72
- package/core/types/index.ts +0 -510
- package/core/types/infrastructure.ts +0 -210
- package/core/types/integrations.ts +0 -31
- package/core/types/jira.ts +0 -51
- package/core/types/logger.ts +0 -17
- package/core/types/memory.ts +0 -313
- package/core/types/outcomes.ts +0 -190
- package/core/types/output.ts +0 -47
- package/core/types/plugin.ts +0 -25
- package/core/types/project-sync.ts +0 -129
- package/core/types/provider.ts +0 -163
- package/core/types/server.ts +0 -71
- package/core/types/services.ts +0 -84
- package/core/types/session.ts +0 -135
- package/core/types/stack.ts +0 -19
- package/core/types/storage.ts +0 -318
- package/core/types/sync-verifier.ts +0 -33
- package/core/types/sync.ts +0 -121
- package/core/types/task.ts +0 -72
- package/core/types/template.ts +0 -24
- package/core/types/utils.ts +0 -92
- package/core/types/workflow.ts +0 -23
- package/core/utils/agent-stream.ts +0 -140
- package/core/utils/animations.ts +0 -251
- package/core/utils/branding.ts +0 -88
- package/core/utils/cache.ts +0 -187
- package/core/utils/citations.ts +0 -39
- package/core/utils/collection-filters.ts +0 -209
- package/core/utils/date-helper.ts +0 -176
- package/core/utils/error-messages.ts +0 -38
- package/core/utils/file-helper.ts +0 -277
- package/core/utils/fs-helpers.ts +0 -14
- package/core/utils/help.ts +0 -314
- package/core/utils/jsonl-helper.ts +0 -290
- package/core/utils/keychain.ts +0 -127
- package/core/utils/logger.ts +0 -77
- package/core/utils/markdown-builder.ts +0 -280
- package/core/utils/next-steps.ts +0 -95
- package/core/utils/output.ts +0 -403
- package/core/utils/preserve-sections.ts +0 -218
- package/core/utils/project-commands.ts +0 -126
- package/core/utils/project-credentials.ts +0 -143
- package/core/utils/provider-cache.ts +0 -49
- package/core/utils/retry.ts +0 -318
- package/core/utils/runtime.ts +0 -108
- package/core/utils/session-helper.ts +0 -278
- package/core/utils/subtask-table.ts +0 -227
- package/core/utils/version.ts +0 -128
- package/core/wizard/index.ts +0 -13
- package/core/wizard/onboarding.ts +0 -633
- package/core/workflow/index.ts +0 -7
- package/core/workflow/state-machine.ts +0 -198
- package/core/workflow/workflow-preferences.ts +0 -294
- package/dist/core/infrastructure/command-installer.js +0 -1141
- package/dist/core/infrastructure/editors-config.js +0 -177
- package/dist/core/infrastructure/setup.js +0 -2244
- package/dist/core/utils/version.js +0 -141
- package/templates/agentic/agent-routing.md +0 -45
- package/templates/agentic/agents/uxui.md +0 -63
- package/templates/agentic/checklist-routing.md +0 -98
- package/templates/agentic/orchestrator.md +0 -68
- package/templates/agentic/task-fragmentation.md +0 -89
- package/templates/agents/AGENTS.md +0 -68
- package/templates/analysis/analyze.md +0 -84
- package/templates/analysis/patterns.md +0 -60
- package/templates/antigravity/SKILL.md +0 -39
- package/templates/architect/discovery.md +0 -67
- package/templates/architect/phases.md +0 -59
- package/templates/checklists/architecture.md +0 -28
- package/templates/checklists/code-quality.md +0 -28
- package/templates/checklists/data.md +0 -33
- package/templates/checklists/documentation.md +0 -33
- package/templates/checklists/infrastructure.md +0 -33
- package/templates/checklists/performance.md +0 -33
- package/templates/checklists/security.md +0 -33
- package/templates/checklists/testing.md +0 -33
- package/templates/checklists/ux-ui.md +0 -37
- package/templates/commands/analyze.md +0 -56
- package/templates/commands/auth.md +0 -234
- package/templates/commands/bug.md +0 -163
- package/templates/commands/cleanup.md +0 -19
- package/templates/commands/dash.md +0 -99
- package/templates/commands/design.md +0 -15
- package/templates/commands/done.md +0 -291
- package/templates/commands/enrich.md +0 -174
- package/templates/commands/git.md +0 -295
- package/templates/commands/history.md +0 -389
- package/templates/commands/idea.md +0 -88
- package/templates/commands/impact.md +0 -864
- package/templates/commands/init.md +0 -54
- package/templates/commands/jira.md +0 -278
- package/templates/commands/linear.md +0 -288
- package/templates/commands/merge.md +0 -206
- package/templates/commands/next.md +0 -80
- package/templates/commands/p.md +0 -67
- package/templates/commands/p.toml +0 -37
- package/templates/commands/pause.md +0 -136
- package/templates/commands/plan.md +0 -696
- package/templates/commands/prd.md +0 -356
- package/templates/commands/resume.md +0 -171
- package/templates/commands/review.md +0 -276
- package/templates/commands/serve.md +0 -118
- package/templates/commands/setup.md +0 -91
- package/templates/commands/ship.md +0 -475
- package/templates/commands/skill.md +0 -259
- package/templates/commands/spec.md +0 -218
- package/templates/commands/status.md +0 -207
- package/templates/commands/sync.md +0 -104
- package/templates/commands/task.md +0 -312
- package/templates/commands/test.md +0 -93
- package/templates/commands/update.md +0 -63
- package/templates/commands/verify.md +0 -204
- package/templates/commands/workflow.md +0 -150
- package/templates/config/skill-mappings.json +0 -82
- package/templates/context/dashboard.md +0 -256
- package/templates/context/roadmap.md +0 -221
- package/templates/cursor/commands/bug.md +0 -8
- package/templates/cursor/commands/done.md +0 -4
- package/templates/cursor/commands/pause.md +0 -6
- package/templates/cursor/commands/resume.md +0 -4
- package/templates/cursor/commands/ship.md +0 -8
- package/templates/cursor/commands/sync.md +0 -4
- package/templates/cursor/commands/task.md +0 -8
- package/templates/cursor/p.md +0 -29
- package/templates/cursor/router.mdc +0 -28
- package/templates/design/api.md +0 -95
- package/templates/design/architecture.md +0 -77
- package/templates/design/component.md +0 -89
- package/templates/design/database.md +0 -78
- package/templates/design/flow.md +0 -94
- package/templates/global/ANTIGRAVITY.md +0 -254
- package/templates/global/CLAUDE.md +0 -497
- package/templates/global/CURSOR.mdc +0 -266
- package/templates/global/GEMINI.md +0 -293
- package/templates/global/STORAGE-SPEC.md +0 -391
- package/templates/global/WINDSURF.md +0 -266
- package/templates/global/modules/CLAUDE-commands.md +0 -70
- package/templates/global/modules/CLAUDE-core.md +0 -105
- package/templates/global/modules/CLAUDE-git.md +0 -50
- package/templates/global/modules/CLAUDE-intelligence.md +0 -92
- package/templates/global/modules/CLAUDE-storage.md +0 -50
- package/templates/global/modules/module-config.json +0 -36
- package/templates/mcp-config.json +0 -19
- package/templates/permissions/default.jsonc +0 -60
- package/templates/permissions/permissive.jsonc +0 -49
- package/templates/permissions/strict.jsonc +0 -58
- package/templates/planning-methodology.md +0 -195
- package/templates/skills/code-review.md +0 -47
- package/templates/skills/debug.md +0 -61
- package/templates/skills/refactor.md +0 -47
- package/templates/subagents/agent-base.md +0 -20
- package/templates/subagents/domain/backend.md +0 -109
- package/templates/subagents/domain/database.md +0 -121
- package/templates/subagents/domain/devops.md +0 -152
- package/templates/subagents/domain/frontend.md +0 -103
- package/templates/subagents/domain/testing.md +0 -169
- package/templates/subagents/pm-expert.md +0 -366
- package/templates/subagents/workflow/chief-architect.md +0 -657
- package/templates/subagents/workflow/prjct-planner.md +0 -159
- package/templates/subagents/workflow/prjct-shipper.md +0 -188
- package/templates/subagents/workflow/prjct-workflow.md +0 -98
- package/templates/tools/bash.txt +0 -22
- package/templates/tools/edit.txt +0 -18
- package/templates/tools/glob.txt +0 -19
- package/templates/tools/grep.txt +0 -21
- package/templates/tools/read.txt +0 -14
- package/templates/tools/task.txt +0 -20
- package/templates/tools/webfetch.txt +0 -16
- package/templates/tools/websearch.txt +0 -18
- package/templates/tools/write.txt +0 -17
- package/templates/windsurf/router.md +0 -28
- package/templates/windsurf/workflows/bug.md +0 -8
- package/templates/windsurf/workflows/done.md +0 -4
- package/templates/windsurf/workflows/pause.md +0 -4
- package/templates/windsurf/workflows/resume.md +0 -4
- package/templates/windsurf/workflows/ship.md +0 -8
- package/templates/windsurf/workflows/sync.md +0 -4
- package/templates/windsurf/workflows/task.md +0 -8
|
@@ -1,476 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Tests for AI Tools Formatters
|
|
3
|
-
*
|
|
4
|
-
* @see PRJ-122
|
|
5
|
-
* @see PRJ-113 (citation support)
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import { describe, expect, test } from 'bun:test'
|
|
9
|
-
import {
|
|
10
|
-
formatForClaude,
|
|
11
|
-
formatForContinue,
|
|
12
|
-
formatForCopilot,
|
|
13
|
-
formatForCursor,
|
|
14
|
-
formatForWindsurf,
|
|
15
|
-
getFormatter,
|
|
16
|
-
type ProjectContext,
|
|
17
|
-
} from '../../ai-tools/formatters'
|
|
18
|
-
import { AI_TOOLS, getAIToolConfig } from '../../ai-tools/registry'
|
|
19
|
-
import { type ContextSources, cite, defaultSources } from '../../utils/citations'
|
|
20
|
-
|
|
21
|
-
// =============================================================================
|
|
22
|
-
// Test Fixtures
|
|
23
|
-
// =============================================================================
|
|
24
|
-
|
|
25
|
-
const mockContext: ProjectContext = {
|
|
26
|
-
projectId: 'test-project-id',
|
|
27
|
-
name: 'test-project',
|
|
28
|
-
version: '1.0.0',
|
|
29
|
-
ecosystem: 'Node.js',
|
|
30
|
-
projectType: 'library',
|
|
31
|
-
languages: ['TypeScript'],
|
|
32
|
-
frameworks: ['Hono'],
|
|
33
|
-
repoPath: '/Users/test/project',
|
|
34
|
-
branch: 'main',
|
|
35
|
-
fileCount: 100,
|
|
36
|
-
commits: 50,
|
|
37
|
-
hasChanges: false,
|
|
38
|
-
commands: {
|
|
39
|
-
install: 'bun install',
|
|
40
|
-
dev: 'bun run dev',
|
|
41
|
-
test: 'bun test',
|
|
42
|
-
build: 'bun run build',
|
|
43
|
-
lint: 'bun run lint',
|
|
44
|
-
format: 'bun run format',
|
|
45
|
-
},
|
|
46
|
-
agents: {
|
|
47
|
-
workflow: ['prjct-planner', 'prjct-shipper'],
|
|
48
|
-
domain: ['backend'],
|
|
49
|
-
},
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
// =============================================================================
|
|
53
|
-
// Registry Tests
|
|
54
|
-
// =============================================================================
|
|
55
|
-
|
|
56
|
-
describe('AI Tools Registry', () => {
|
|
57
|
-
test('cursor uses correct output file path', () => {
|
|
58
|
-
expect(AI_TOOLS.cursor.outputFile).toBe('.cursor/rules/prjct.mdc')
|
|
59
|
-
})
|
|
60
|
-
|
|
61
|
-
test('windsurf uses correct output file path', () => {
|
|
62
|
-
expect(AI_TOOLS.windsurf.outputFile).toBe('.windsurf/rules/prjct.md')
|
|
63
|
-
})
|
|
64
|
-
|
|
65
|
-
test('copilot uses correct output file path', () => {
|
|
66
|
-
expect(AI_TOOLS.copilot.outputFile).toBe('.github/copilot-instructions.md')
|
|
67
|
-
})
|
|
68
|
-
|
|
69
|
-
test('claude uses correct output file path', () => {
|
|
70
|
-
expect(AI_TOOLS.claude.outputFile).toBe('CLAUDE.md')
|
|
71
|
-
})
|
|
72
|
-
|
|
73
|
-
test('continue uses correct output file path', () => {
|
|
74
|
-
expect(AI_TOOLS.continue.outputFile).toBe('.continue/config.json')
|
|
75
|
-
})
|
|
76
|
-
|
|
77
|
-
test('getAIToolConfig returns config for valid tool', () => {
|
|
78
|
-
const config = getAIToolConfig('cursor')
|
|
79
|
-
expect(config).not.toBeNull()
|
|
80
|
-
expect(config?.id).toBe('cursor')
|
|
81
|
-
})
|
|
82
|
-
|
|
83
|
-
test('getAIToolConfig returns null for invalid tool', () => {
|
|
84
|
-
const config = getAIToolConfig('invalid-tool')
|
|
85
|
-
expect(config).toBeNull()
|
|
86
|
-
})
|
|
87
|
-
})
|
|
88
|
-
|
|
89
|
-
// =============================================================================
|
|
90
|
-
// Formatter Tests
|
|
91
|
-
// =============================================================================
|
|
92
|
-
|
|
93
|
-
describe('formatForCursor', () => {
|
|
94
|
-
const config = AI_TOOLS.cursor
|
|
95
|
-
|
|
96
|
-
test('generates MDC format with YAML frontmatter', () => {
|
|
97
|
-
const result = formatForCursor(mockContext, config)
|
|
98
|
-
|
|
99
|
-
// Should start with YAML frontmatter
|
|
100
|
-
expect(result.startsWith('---\n')).toBe(true)
|
|
101
|
-
expect(result).toContain('description: prjct context for test-project')
|
|
102
|
-
expect(result).toContain('alwaysApply: true')
|
|
103
|
-
expect(result).toContain('---')
|
|
104
|
-
})
|
|
105
|
-
|
|
106
|
-
test('includes project info', () => {
|
|
107
|
-
const result = formatForCursor(mockContext, config)
|
|
108
|
-
|
|
109
|
-
expect(result).toContain('test-project')
|
|
110
|
-
expect(result).toContain('library')
|
|
111
|
-
expect(result).toContain('Node.js')
|
|
112
|
-
})
|
|
113
|
-
|
|
114
|
-
test('includes tech stack', () => {
|
|
115
|
-
const result = formatForCursor(mockContext, config)
|
|
116
|
-
|
|
117
|
-
expect(result).toContain('TypeScript')
|
|
118
|
-
expect(result).toContain('Hono')
|
|
119
|
-
})
|
|
120
|
-
|
|
121
|
-
test('includes commands', () => {
|
|
122
|
-
const result = formatForCursor(mockContext, config)
|
|
123
|
-
|
|
124
|
-
expect(result).toContain('bun install')
|
|
125
|
-
expect(result).toContain('bun run dev')
|
|
126
|
-
expect(result).toContain('bun test')
|
|
127
|
-
expect(result).toContain('bun run build')
|
|
128
|
-
})
|
|
129
|
-
})
|
|
130
|
-
|
|
131
|
-
describe('formatForWindsurf', () => {
|
|
132
|
-
const config = AI_TOOLS.windsurf
|
|
133
|
-
|
|
134
|
-
test('generates MD format with YAML frontmatter', () => {
|
|
135
|
-
const result = formatForWindsurf(mockContext, config)
|
|
136
|
-
|
|
137
|
-
// Should start with YAML frontmatter
|
|
138
|
-
expect(result.startsWith('---\n')).toBe(true)
|
|
139
|
-
expect(result).toContain('description: prjct context for test-project')
|
|
140
|
-
expect(result).toContain('trigger: always_on')
|
|
141
|
-
expect(result).toContain('---')
|
|
142
|
-
})
|
|
143
|
-
|
|
144
|
-
test('uses trigger: always_on (not alwaysApply)', () => {
|
|
145
|
-
const result = formatForWindsurf(mockContext, config)
|
|
146
|
-
|
|
147
|
-
expect(result).toContain('trigger: always_on')
|
|
148
|
-
expect(result).not.toContain('alwaysApply')
|
|
149
|
-
})
|
|
150
|
-
|
|
151
|
-
test('includes project info', () => {
|
|
152
|
-
const result = formatForWindsurf(mockContext, config)
|
|
153
|
-
|
|
154
|
-
expect(result).toContain('test-project')
|
|
155
|
-
expect(result).toContain('library')
|
|
156
|
-
expect(result).toContain('Node.js')
|
|
157
|
-
})
|
|
158
|
-
|
|
159
|
-
test('includes commands in bash block', () => {
|
|
160
|
-
const result = formatForWindsurf(mockContext, config)
|
|
161
|
-
|
|
162
|
-
expect(result).toContain('```bash')
|
|
163
|
-
expect(result).toContain('bun install')
|
|
164
|
-
expect(result).toContain('bun run dev')
|
|
165
|
-
expect(result).toContain('bun test')
|
|
166
|
-
expect(result).toContain('bun run build')
|
|
167
|
-
expect(result).toContain('```')
|
|
168
|
-
})
|
|
169
|
-
})
|
|
170
|
-
|
|
171
|
-
describe('formatForCopilot', () => {
|
|
172
|
-
const config = AI_TOOLS.copilot
|
|
173
|
-
|
|
174
|
-
test('generates minimal format without frontmatter', () => {
|
|
175
|
-
const result = formatForCopilot(mockContext, config)
|
|
176
|
-
|
|
177
|
-
// Copilot uses plain markdown, no YAML frontmatter
|
|
178
|
-
expect(result.startsWith('# Copilot Instructions')).toBe(true)
|
|
179
|
-
expect(result).not.toContain('---')
|
|
180
|
-
})
|
|
181
|
-
|
|
182
|
-
test('includes project info', () => {
|
|
183
|
-
const result = formatForCopilot(mockContext, config)
|
|
184
|
-
|
|
185
|
-
expect(result).toContain('test-project')
|
|
186
|
-
expect(result).toContain('library')
|
|
187
|
-
expect(result).toContain('Node.js')
|
|
188
|
-
})
|
|
189
|
-
|
|
190
|
-
test('includes essential commands only', () => {
|
|
191
|
-
const result = formatForCopilot(mockContext, config)
|
|
192
|
-
|
|
193
|
-
expect(result).toContain('bun test')
|
|
194
|
-
expect(result).toContain('bun run build')
|
|
195
|
-
})
|
|
196
|
-
})
|
|
197
|
-
|
|
198
|
-
describe('formatForClaude', () => {
|
|
199
|
-
const config = AI_TOOLS.claude
|
|
200
|
-
|
|
201
|
-
test('generates detailed markdown format', () => {
|
|
202
|
-
const result = formatForClaude(mockContext, config)
|
|
203
|
-
|
|
204
|
-
expect(result).toContain('# test-project - Project Rules')
|
|
205
|
-
expect(result).toContain('<!-- projectId: test-project-id -->')
|
|
206
|
-
})
|
|
207
|
-
|
|
208
|
-
test('includes prjct workflow reference', () => {
|
|
209
|
-
const result = formatForClaude(mockContext, config)
|
|
210
|
-
|
|
211
|
-
expect(result).toContain('## PRJCT RULES')
|
|
212
|
-
expect(result).toContain('p. sync')
|
|
213
|
-
expect(result).toContain('p. task')
|
|
214
|
-
expect(result).toContain('p. done')
|
|
215
|
-
expect(result).toContain('p. ship')
|
|
216
|
-
})
|
|
217
|
-
|
|
218
|
-
test('includes commands table', () => {
|
|
219
|
-
const result = formatForClaude(mockContext, config)
|
|
220
|
-
|
|
221
|
-
expect(result).toContain('| Action | Command |')
|
|
222
|
-
expect(result).toContain('bun install')
|
|
223
|
-
expect(result).toContain('bun run dev')
|
|
224
|
-
})
|
|
225
|
-
|
|
226
|
-
test('includes agent references', () => {
|
|
227
|
-
const result = formatForClaude(mockContext, config)
|
|
228
|
-
|
|
229
|
-
expect(result).toContain('prjct-planner')
|
|
230
|
-
expect(result).toContain('prjct-shipper')
|
|
231
|
-
expect(result).toContain('backend')
|
|
232
|
-
})
|
|
233
|
-
})
|
|
234
|
-
|
|
235
|
-
describe('formatForContinue', () => {
|
|
236
|
-
const config = AI_TOOLS.continue
|
|
237
|
-
|
|
238
|
-
test('generates valid JSON format', () => {
|
|
239
|
-
const result = formatForContinue(mockContext, config)
|
|
240
|
-
|
|
241
|
-
const parsed = JSON.parse(result)
|
|
242
|
-
expect(parsed).toBeDefined()
|
|
243
|
-
})
|
|
244
|
-
|
|
245
|
-
test('includes system message with project info', () => {
|
|
246
|
-
const result = formatForContinue(mockContext, config)
|
|
247
|
-
const parsed = JSON.parse(result)
|
|
248
|
-
|
|
249
|
-
expect(parsed.systemMessage).toContain('test-project')
|
|
250
|
-
expect(parsed.systemMessage).toContain('library')
|
|
251
|
-
expect(parsed.systemMessage).toContain('Node.js')
|
|
252
|
-
})
|
|
253
|
-
|
|
254
|
-
test('includes context providers', () => {
|
|
255
|
-
const result = formatForContinue(mockContext, config)
|
|
256
|
-
const parsed = JSON.parse(result)
|
|
257
|
-
|
|
258
|
-
expect(parsed.contextProviders).toBeArray()
|
|
259
|
-
expect(parsed.contextProviders.length).toBeGreaterThan(0)
|
|
260
|
-
expect(parsed.contextProviders).toContainEqual({ name: 'code' })
|
|
261
|
-
expect(parsed.contextProviders).toContainEqual({ name: 'diff' })
|
|
262
|
-
})
|
|
263
|
-
|
|
264
|
-
test('includes slash commands', () => {
|
|
265
|
-
const result = formatForContinue(mockContext, config)
|
|
266
|
-
const parsed = JSON.parse(result)
|
|
267
|
-
|
|
268
|
-
expect(parsed.slashCommands).toBeArray()
|
|
269
|
-
expect(parsed.slashCommands.length).toBeGreaterThan(0)
|
|
270
|
-
})
|
|
271
|
-
|
|
272
|
-
test('includes custom test command with project test command', () => {
|
|
273
|
-
const result = formatForContinue(mockContext, config)
|
|
274
|
-
const parsed = JSON.parse(result)
|
|
275
|
-
|
|
276
|
-
expect(parsed.customCommands).toBeArray()
|
|
277
|
-
const testCmd = parsed.customCommands.find((c: { name: string }) => c.name === 'test')
|
|
278
|
-
expect(testCmd).toBeDefined()
|
|
279
|
-
expect(testCmd.prompt).toContain('bun test')
|
|
280
|
-
})
|
|
281
|
-
})
|
|
282
|
-
|
|
283
|
-
// =============================================================================
|
|
284
|
-
// getFormatter Tests
|
|
285
|
-
// =============================================================================
|
|
286
|
-
|
|
287
|
-
describe('getFormatter', () => {
|
|
288
|
-
test('returns formatter for claude', () => {
|
|
289
|
-
const formatter = getFormatter('claude')
|
|
290
|
-
expect(formatter).toBe(formatForClaude)
|
|
291
|
-
})
|
|
292
|
-
|
|
293
|
-
test('returns formatter for cursor', () => {
|
|
294
|
-
const formatter = getFormatter('cursor')
|
|
295
|
-
expect(formatter).toBe(formatForCursor)
|
|
296
|
-
})
|
|
297
|
-
|
|
298
|
-
test('returns formatter for windsurf', () => {
|
|
299
|
-
const formatter = getFormatter('windsurf')
|
|
300
|
-
expect(formatter).toBe(formatForWindsurf)
|
|
301
|
-
})
|
|
302
|
-
|
|
303
|
-
test('returns formatter for copilot', () => {
|
|
304
|
-
const formatter = getFormatter('copilot')
|
|
305
|
-
expect(formatter).toBe(formatForCopilot)
|
|
306
|
-
})
|
|
307
|
-
|
|
308
|
-
test('returns formatter for continue', () => {
|
|
309
|
-
const formatter = getFormatter('continue')
|
|
310
|
-
expect(formatter).toBe(formatForContinue)
|
|
311
|
-
})
|
|
312
|
-
|
|
313
|
-
test('returns null for unknown tool', () => {
|
|
314
|
-
const formatter = getFormatter('unknown-tool')
|
|
315
|
-
expect(formatter).toBeNull()
|
|
316
|
-
})
|
|
317
|
-
})
|
|
318
|
-
|
|
319
|
-
// =============================================================================
|
|
320
|
-
// Edge Cases
|
|
321
|
-
// =============================================================================
|
|
322
|
-
|
|
323
|
-
describe('Formatter Edge Cases', () => {
|
|
324
|
-
test('handles empty languages array', () => {
|
|
325
|
-
const ctx = { ...mockContext, languages: [] }
|
|
326
|
-
|
|
327
|
-
const cursorResult = formatForCursor(ctx, AI_TOOLS.cursor)
|
|
328
|
-
expect(cursorResult).not.toContain('Languages:')
|
|
329
|
-
|
|
330
|
-
const windsurfResult = formatForWindsurf(ctx, AI_TOOLS.windsurf)
|
|
331
|
-
expect(windsurfResult).toContain('## Stack')
|
|
332
|
-
})
|
|
333
|
-
|
|
334
|
-
test('handles empty frameworks array', () => {
|
|
335
|
-
const ctx = { ...mockContext, frameworks: [] }
|
|
336
|
-
|
|
337
|
-
const cursorResult = formatForCursor(ctx, AI_TOOLS.cursor)
|
|
338
|
-
expect(cursorResult).not.toContain('Frameworks:')
|
|
339
|
-
|
|
340
|
-
const windsurfResult = formatForWindsurf(ctx, AI_TOOLS.windsurf)
|
|
341
|
-
expect(windsurfResult).toContain('## Stack')
|
|
342
|
-
})
|
|
343
|
-
|
|
344
|
-
test('handles empty agents', () => {
|
|
345
|
-
const ctx = { ...mockContext, agents: { workflow: [], domain: [] } }
|
|
346
|
-
|
|
347
|
-
const claudeResult = formatForClaude(ctx, AI_TOOLS.claude)
|
|
348
|
-
expect(claudeResult).toContain('**Domain**: none')
|
|
349
|
-
})
|
|
350
|
-
|
|
351
|
-
test('handles special characters in project name', () => {
|
|
352
|
-
const ctx = { ...mockContext, name: '@scope/my-project' }
|
|
353
|
-
|
|
354
|
-
const cursorResult = formatForCursor(ctx, AI_TOOLS.cursor)
|
|
355
|
-
expect(cursorResult).toContain('@scope/my-project')
|
|
356
|
-
|
|
357
|
-
const claudeResult = formatForClaude(ctx, AI_TOOLS.claude)
|
|
358
|
-
expect(claudeResult).toContain('@scope/my-project')
|
|
359
|
-
})
|
|
360
|
-
})
|
|
361
|
-
|
|
362
|
-
// =============================================================================
|
|
363
|
-
// Citation Tests (PRJ-113)
|
|
364
|
-
// =============================================================================
|
|
365
|
-
|
|
366
|
-
const mockSources: ContextSources = {
|
|
367
|
-
name: { file: 'package.json', type: 'detected' },
|
|
368
|
-
version: { file: 'package.json', type: 'detected' },
|
|
369
|
-
ecosystem: { file: 'package.json', type: 'detected' },
|
|
370
|
-
languages: { file: 'package.json', type: 'detected' },
|
|
371
|
-
frameworks: { file: 'package.json', type: 'detected' },
|
|
372
|
-
commands: { file: 'bun.lockb', type: 'detected' },
|
|
373
|
-
projectType: { file: 'file count + frameworks', type: 'inferred' },
|
|
374
|
-
git: { file: 'git', type: 'detected' },
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
const ctxWithSources: ProjectContext = {
|
|
378
|
-
...mockContext,
|
|
379
|
-
sources: mockSources,
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
describe('cite() helper', () => {
|
|
383
|
-
test('generates HTML comment with file and type', () => {
|
|
384
|
-
const result = cite({ file: 'package.json', type: 'detected' })
|
|
385
|
-
expect(result).toBe('<!-- source: package.json, detected -->')
|
|
386
|
-
})
|
|
387
|
-
|
|
388
|
-
test('supports inferred type', () => {
|
|
389
|
-
const result = cite({ file: 'file count + frameworks', type: 'inferred' })
|
|
390
|
-
expect(result).toBe('<!-- source: file count + frameworks, inferred -->')
|
|
391
|
-
})
|
|
392
|
-
|
|
393
|
-
test('supports user-defined type', () => {
|
|
394
|
-
const result = cite({ file: 'prjct.yaml', type: 'user-defined' })
|
|
395
|
-
expect(result).toBe('<!-- source: prjct.yaml, user-defined -->')
|
|
396
|
-
})
|
|
397
|
-
})
|
|
398
|
-
|
|
399
|
-
describe('defaultSources()', () => {
|
|
400
|
-
test('returns all source fields', () => {
|
|
401
|
-
const sources = defaultSources()
|
|
402
|
-
expect(sources.name).toBeDefined()
|
|
403
|
-
expect(sources.version).toBeDefined()
|
|
404
|
-
expect(sources.ecosystem).toBeDefined()
|
|
405
|
-
expect(sources.languages).toBeDefined()
|
|
406
|
-
expect(sources.frameworks).toBeDefined()
|
|
407
|
-
expect(sources.commands).toBeDefined()
|
|
408
|
-
expect(sources.projectType).toBeDefined()
|
|
409
|
-
expect(sources.git).toBeDefined()
|
|
410
|
-
})
|
|
411
|
-
|
|
412
|
-
test('git source is always "git"', () => {
|
|
413
|
-
const sources = defaultSources()
|
|
414
|
-
expect(sources.git.file).toBe('git')
|
|
415
|
-
expect(sources.git.type).toBe('detected')
|
|
416
|
-
})
|
|
417
|
-
})
|
|
418
|
-
|
|
419
|
-
describe('Citation integration', () => {
|
|
420
|
-
test('Claude format includes source citations', () => {
|
|
421
|
-
const result = formatForClaude(ctxWithSources, AI_TOOLS.claude)
|
|
422
|
-
|
|
423
|
-
expect(result).toContain('<!-- source: package.json, detected -->')
|
|
424
|
-
expect(result).toContain('<!-- source: bun.lockb, detected -->')
|
|
425
|
-
})
|
|
426
|
-
|
|
427
|
-
test('Cursor format includes source citations', () => {
|
|
428
|
-
const result = formatForCursor(ctxWithSources, AI_TOOLS.cursor)
|
|
429
|
-
|
|
430
|
-
expect(result).toContain('<!-- source: package.json, detected -->')
|
|
431
|
-
expect(result).toContain('<!-- source: bun.lockb, detected -->')
|
|
432
|
-
})
|
|
433
|
-
|
|
434
|
-
test('Windsurf format includes source citations', () => {
|
|
435
|
-
const result = formatForWindsurf(ctxWithSources, AI_TOOLS.windsurf)
|
|
436
|
-
|
|
437
|
-
expect(result).toContain('<!-- source: package.json, detected -->')
|
|
438
|
-
expect(result).toContain('<!-- source: bun.lockb, detected -->')
|
|
439
|
-
})
|
|
440
|
-
|
|
441
|
-
test('Copilot format includes source citations', () => {
|
|
442
|
-
const result = formatForCopilot(ctxWithSources, AI_TOOLS.copilot)
|
|
443
|
-
|
|
444
|
-
expect(result).toContain('<!-- source: package.json, detected -->')
|
|
445
|
-
expect(result).toContain('<!-- source: bun.lockb, detected -->')
|
|
446
|
-
})
|
|
447
|
-
|
|
448
|
-
test('formatters work without sources (backward compatible)', () => {
|
|
449
|
-
const ctxNoSources = { ...mockContext }
|
|
450
|
-
delete ctxNoSources.sources
|
|
451
|
-
|
|
452
|
-
// Should not throw
|
|
453
|
-
expect(() => formatForClaude(ctxNoSources, AI_TOOLS.claude)).not.toThrow()
|
|
454
|
-
expect(() => formatForCursor(ctxNoSources, AI_TOOLS.cursor)).not.toThrow()
|
|
455
|
-
expect(() => formatForWindsurf(ctxNoSources, AI_TOOLS.windsurf)).not.toThrow()
|
|
456
|
-
expect(() => formatForCopilot(ctxNoSources, AI_TOOLS.copilot)).not.toThrow()
|
|
457
|
-
|
|
458
|
-
// Should still contain default citation comments
|
|
459
|
-
const result = formatForClaude(ctxNoSources, AI_TOOLS.claude)
|
|
460
|
-
expect(result).toContain('<!-- source:')
|
|
461
|
-
})
|
|
462
|
-
|
|
463
|
-
test('Claude format has citations before each major section', () => {
|
|
464
|
-
const result = formatForClaude(ctxWithSources, AI_TOOLS.claude)
|
|
465
|
-
|
|
466
|
-
// Ecosystem citation before project type
|
|
467
|
-
const ecosystemIdx = result.indexOf('<!-- source: package.json, detected -->')
|
|
468
|
-
const projectTypeIdx = result.indexOf('**Type:**')
|
|
469
|
-
expect(ecosystemIdx).toBeLessThan(projectTypeIdx)
|
|
470
|
-
|
|
471
|
-
// Commands citation before commands table
|
|
472
|
-
const commandsCiteIdx = result.indexOf('<!-- source: bun.lockb, detected -->')
|
|
473
|
-
const commandsTableIdx = result.indexOf('| Action | Command |')
|
|
474
|
-
expect(commandsCiteIdx).toBeLessThan(commandsTableIdx)
|
|
475
|
-
})
|
|
476
|
-
})
|
|
@@ -1,225 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Tests for BM25 Text Search Index
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import { afterEach, beforeEach, describe, expect, it } from 'bun:test'
|
|
6
|
-
import fs from 'node:fs/promises'
|
|
7
|
-
import os from 'node:os'
|
|
8
|
-
import path from 'node:path'
|
|
9
|
-
import { buildIndex, score, tokenizeFile, tokenizeQuery } from '../../domain/bm25'
|
|
10
|
-
|
|
11
|
-
// =============================================================================
|
|
12
|
-
// Tokenization Tests
|
|
13
|
-
// =============================================================================
|
|
14
|
-
|
|
15
|
-
describe('BM25', () => {
|
|
16
|
-
describe('tokenizeFile', () => {
|
|
17
|
-
it('should extract path segments', () => {
|
|
18
|
-
const tokens = tokenizeFile('', 'core/domain/bm25.ts')
|
|
19
|
-
expect(tokens).toContain('core')
|
|
20
|
-
expect(tokens).toContain('domain')
|
|
21
|
-
expect(tokens).toContain('bm25')
|
|
22
|
-
})
|
|
23
|
-
|
|
24
|
-
it('should extract function names and split camelCase', () => {
|
|
25
|
-
const content = 'export function getUserById(id: string) { return id }'
|
|
26
|
-
const tokens = tokenizeFile(content, 'user-service.ts')
|
|
27
|
-
expect(tokens).toContain('get')
|
|
28
|
-
expect(tokens).toContain('user')
|
|
29
|
-
// 'by' and 'id' are filtered (stop word / too short)
|
|
30
|
-
})
|
|
31
|
-
|
|
32
|
-
it('should extract class names', () => {
|
|
33
|
-
const content = 'export class AuthMiddleware { handle() {} }'
|
|
34
|
-
const tokens = tokenizeFile(content, 'middleware.ts')
|
|
35
|
-
expect(tokens).toContain('auth')
|
|
36
|
-
expect(tokens).toContain('middleware')
|
|
37
|
-
})
|
|
38
|
-
|
|
39
|
-
it('should extract interface names', () => {
|
|
40
|
-
const content = 'export interface JwtPayload { sub: string }'
|
|
41
|
-
const tokens = tokenizeFile(content, 'types.ts')
|
|
42
|
-
expect(tokens).toContain('jwt')
|
|
43
|
-
expect(tokens).toContain('payload')
|
|
44
|
-
})
|
|
45
|
-
|
|
46
|
-
it('should extract import sources', () => {
|
|
47
|
-
const content = `import { Router } from './router'\nimport express from 'express'`
|
|
48
|
-
const tokens = tokenizeFile(content, 'app.ts')
|
|
49
|
-
expect(tokens).toContain('router')
|
|
50
|
-
expect(tokens).toContain('express')
|
|
51
|
-
})
|
|
52
|
-
|
|
53
|
-
it('should extract words from comments', () => {
|
|
54
|
-
const content = '// Handle authentication for JWT tokens'
|
|
55
|
-
const tokens = tokenizeFile(content, 'auth.ts')
|
|
56
|
-
expect(tokens).toContain('handle')
|
|
57
|
-
expect(tokens).toContain('authentication')
|
|
58
|
-
expect(tokens).toContain('jwt')
|
|
59
|
-
expect(tokens).toContain('tokens')
|
|
60
|
-
})
|
|
61
|
-
|
|
62
|
-
it('should extract words from JSDoc comments', () => {
|
|
63
|
-
const content = '/** Validates user session and refreshes token */'
|
|
64
|
-
const tokens = tokenizeFile(content, 'session.ts')
|
|
65
|
-
expect(tokens).toContain('validates')
|
|
66
|
-
expect(tokens).toContain('session')
|
|
67
|
-
expect(tokens).toContain('refreshes')
|
|
68
|
-
expect(tokens).toContain('token')
|
|
69
|
-
})
|
|
70
|
-
|
|
71
|
-
it('should filter out stop words', () => {
|
|
72
|
-
const content = 'export function getTheData() {}'
|
|
73
|
-
const tokens = tokenizeFile(content, 'data.ts')
|
|
74
|
-
expect(tokens).not.toContain('the')
|
|
75
|
-
expect(tokens).not.toContain('export')
|
|
76
|
-
expect(tokens).not.toContain('function')
|
|
77
|
-
})
|
|
78
|
-
|
|
79
|
-
it('should handle empty content', () => {
|
|
80
|
-
const tokens = tokenizeFile('', 'empty.ts')
|
|
81
|
-
// Should still have path segments
|
|
82
|
-
expect(tokens).toContain('empty')
|
|
83
|
-
})
|
|
84
|
-
})
|
|
85
|
-
|
|
86
|
-
describe('tokenizeQuery', () => {
|
|
87
|
-
it('should tokenize a task description', () => {
|
|
88
|
-
const tokens = tokenizeQuery('Fix the auth middleware for JWT validation')
|
|
89
|
-
expect(tokens).toContain('fix')
|
|
90
|
-
expect(tokens).toContain('auth')
|
|
91
|
-
expect(tokens).toContain('middleware')
|
|
92
|
-
expect(tokens).toContain('jwt')
|
|
93
|
-
expect(tokens).toContain('validation')
|
|
94
|
-
})
|
|
95
|
-
|
|
96
|
-
it('should split camelCase in queries', () => {
|
|
97
|
-
const tokens = tokenizeQuery('update getUserById function')
|
|
98
|
-
expect(tokens).toContain('update')
|
|
99
|
-
expect(tokens).toContain('get')
|
|
100
|
-
expect(tokens).toContain('user')
|
|
101
|
-
})
|
|
102
|
-
|
|
103
|
-
it('should remove stop words from queries', () => {
|
|
104
|
-
const tokens = tokenizeQuery('Fix the bug in the login')
|
|
105
|
-
expect(tokens).not.toContain('the')
|
|
106
|
-
expect(tokens).not.toContain('in')
|
|
107
|
-
expect(tokens).toContain('fix')
|
|
108
|
-
expect(tokens).toContain('bug')
|
|
109
|
-
expect(tokens).toContain('login')
|
|
110
|
-
})
|
|
111
|
-
})
|
|
112
|
-
|
|
113
|
-
// =============================================================================
|
|
114
|
-
// Index Building & Scoring Tests
|
|
115
|
-
// =============================================================================
|
|
116
|
-
|
|
117
|
-
describe('buildIndex + score', () => {
|
|
118
|
-
let testDir: string
|
|
119
|
-
|
|
120
|
-
beforeEach(async () => {
|
|
121
|
-
testDir = path.join(os.tmpdir(), `prjct-bm25-test-${Date.now()}`)
|
|
122
|
-
await fs.mkdir(testDir, { recursive: true })
|
|
123
|
-
})
|
|
124
|
-
|
|
125
|
-
afterEach(async () => {
|
|
126
|
-
try {
|
|
127
|
-
await fs.rm(testDir, { recursive: true, force: true })
|
|
128
|
-
} catch {
|
|
129
|
-
// Ignore cleanup errors
|
|
130
|
-
}
|
|
131
|
-
})
|
|
132
|
-
|
|
133
|
-
it('should build an index from project files', async () => {
|
|
134
|
-
// Create test files
|
|
135
|
-
await fs.writeFile(
|
|
136
|
-
path.join(testDir, 'auth.ts'),
|
|
137
|
-
`export class AuthService {\n validateJwt(token: string) {}\n refreshSession() {}\n}`
|
|
138
|
-
)
|
|
139
|
-
await fs.writeFile(
|
|
140
|
-
path.join(testDir, 'middleware.ts'),
|
|
141
|
-
`import { AuthService } from './auth'\nexport function authMiddleware(req: any) {}`
|
|
142
|
-
)
|
|
143
|
-
await fs.writeFile(
|
|
144
|
-
path.join(testDir, 'button.tsx'),
|
|
145
|
-
`export function Button({ label }: { label: string }) {\n return <button>{label}</button>\n}`
|
|
146
|
-
)
|
|
147
|
-
|
|
148
|
-
const index = await buildIndex(testDir)
|
|
149
|
-
|
|
150
|
-
expect(index.totalDocs).toBe(3)
|
|
151
|
-
expect(index.avgDocLength).toBeGreaterThan(0)
|
|
152
|
-
expect(Object.keys(index.documents)).toContain('auth.ts')
|
|
153
|
-
expect(Object.keys(index.documents)).toContain('middleware.ts')
|
|
154
|
-
expect(Object.keys(index.documents)).toContain('button.tsx')
|
|
155
|
-
})
|
|
156
|
-
|
|
157
|
-
it('should rank auth files higher for auth query', async () => {
|
|
158
|
-
await fs.writeFile(
|
|
159
|
-
path.join(testDir, 'auth.ts'),
|
|
160
|
-
`export class AuthService {\n // Authenticate user with JWT\n validateJwt(token: string) {}\n refreshSession() {}\n}`
|
|
161
|
-
)
|
|
162
|
-
await fs.writeFile(
|
|
163
|
-
path.join(testDir, 'middleware.ts'),
|
|
164
|
-
`import { AuthService } from './auth'\n// Auth middleware for JWT validation\nexport function authMiddleware(req: any) {}`
|
|
165
|
-
)
|
|
166
|
-
await fs.writeFile(
|
|
167
|
-
path.join(testDir, 'button.tsx'),
|
|
168
|
-
`// Render a UI button component\nexport function Button({ label }: { label: string }) {\n return <button>{label}</button>\n}`
|
|
169
|
-
)
|
|
170
|
-
|
|
171
|
-
const index = await buildIndex(testDir)
|
|
172
|
-
const results = score('Fix the auth middleware for JWT validation', index)
|
|
173
|
-
|
|
174
|
-
// Auth and middleware should rank higher than button
|
|
175
|
-
expect(results.length).toBeGreaterThan(0)
|
|
176
|
-
const authIndex = results.findIndex((r) => r.path === 'auth.ts')
|
|
177
|
-
const middlewareIndex = results.findIndex((r) => r.path === 'middleware.ts')
|
|
178
|
-
const buttonIndex = results.findIndex((r) => r.path === 'button.tsx')
|
|
179
|
-
|
|
180
|
-
expect(authIndex).toBeLessThan(buttonIndex === -1 ? Infinity : buttonIndex)
|
|
181
|
-
expect(middlewareIndex).toBeLessThan(buttonIndex === -1 ? Infinity : buttonIndex)
|
|
182
|
-
})
|
|
183
|
-
|
|
184
|
-
it('should rank frontend files higher for UI query', async () => {
|
|
185
|
-
await fs.writeFile(
|
|
186
|
-
path.join(testDir, 'dashboard.tsx'),
|
|
187
|
-
`// Responsive dashboard with charts and data grid\nexport function Dashboard() {\n return <div className="dashboard">Charts here</div>\n}`
|
|
188
|
-
)
|
|
189
|
-
await fs.writeFile(
|
|
190
|
-
path.join(testDir, 'api-handler.ts'),
|
|
191
|
-
`// Handle API requests for user data\nexport function handleRequest(req: any) { return {} }`
|
|
192
|
-
)
|
|
193
|
-
|
|
194
|
-
const index = await buildIndex(testDir)
|
|
195
|
-
const results = score('Build responsive dashboard', index)
|
|
196
|
-
|
|
197
|
-
expect(results.length).toBeGreaterThan(0)
|
|
198
|
-
expect(results[0].path).toBe('dashboard.tsx')
|
|
199
|
-
})
|
|
200
|
-
|
|
201
|
-
it('should skip node_modules', async () => {
|
|
202
|
-
await fs.mkdir(path.join(testDir, 'node_modules', 'pkg'), { recursive: true })
|
|
203
|
-
await fs.writeFile(path.join(testDir, 'node_modules', 'pkg', 'index.ts'), 'export default {}')
|
|
204
|
-
await fs.writeFile(path.join(testDir, 'app.ts'), 'export function main() {}')
|
|
205
|
-
|
|
206
|
-
const index = await buildIndex(testDir)
|
|
207
|
-
expect(index.totalDocs).toBe(1)
|
|
208
|
-
expect(Object.keys(index.documents)).toContain('app.ts')
|
|
209
|
-
})
|
|
210
|
-
|
|
211
|
-
it('should handle empty query', async () => {
|
|
212
|
-
await fs.writeFile(path.join(testDir, 'app.ts'), 'export function main() {}')
|
|
213
|
-
|
|
214
|
-
const index = await buildIndex(testDir)
|
|
215
|
-
const results = score('', index)
|
|
216
|
-
expect(results).toEqual([])
|
|
217
|
-
})
|
|
218
|
-
|
|
219
|
-
it('should handle empty project', async () => {
|
|
220
|
-
const index = await buildIndex(testDir)
|
|
221
|
-
expect(index.totalDocs).toBe(0)
|
|
222
|
-
expect(score('anything', index)).toEqual([])
|
|
223
|
-
})
|
|
224
|
-
})
|
|
225
|
-
})
|