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,328 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* PerformanceTracker Tests
|
|
3
|
-
*
|
|
4
|
-
* Tests for:
|
|
5
|
-
* - Timing marks (start/end)
|
|
6
|
-
* - Memory snapshots
|
|
7
|
-
* - Metric recording to JSONL
|
|
8
|
-
* - Context correctness recording
|
|
9
|
-
* - Subtask handoff recording
|
|
10
|
-
* - Report generation
|
|
11
|
-
* - JSONL rotation
|
|
12
|
-
*
|
|
13
|
-
* @see PRJ-297
|
|
14
|
-
*/
|
|
15
|
-
|
|
16
|
-
import { afterEach, beforeEach, describe, expect, it } from 'bun:test'
|
|
17
|
-
import fs from 'node:fs/promises'
|
|
18
|
-
import os from 'node:os'
|
|
19
|
-
import path from 'node:path'
|
|
20
|
-
import pathManager from '../../infrastructure/path-manager'
|
|
21
|
-
import { performanceTracker } from '../../infrastructure/performance-tracker'
|
|
22
|
-
import type { PerformanceEntry, PerformanceMetric } from '../../schemas/performance'
|
|
23
|
-
import * as jsonlHelper from '../../utils/jsonl-helper'
|
|
24
|
-
|
|
25
|
-
// =============================================================================
|
|
26
|
-
// Test Setup
|
|
27
|
-
// =============================================================================
|
|
28
|
-
|
|
29
|
-
let tmpRoot: string | null = null
|
|
30
|
-
let testProjectId: string
|
|
31
|
-
|
|
32
|
-
const originalGetStoragePath = pathManager.getStoragePath.bind(pathManager)
|
|
33
|
-
const originalGetGlobalProjectPath = pathManager.getGlobalProjectPath.bind(pathManager)
|
|
34
|
-
|
|
35
|
-
describe('PerformanceTracker', () => {
|
|
36
|
-
beforeEach(async () => {
|
|
37
|
-
tmpRoot = await fs.mkdtemp(path.join(os.tmpdir(), 'prjct-perf-test-'))
|
|
38
|
-
testProjectId = 'test-project-perf'
|
|
39
|
-
|
|
40
|
-
pathManager.getStoragePath = (projectId: string, filename: string) => {
|
|
41
|
-
return path.join(tmpRoot!, projectId, 'storage', filename)
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
pathManager.getGlobalProjectPath = (projectId: string) => {
|
|
45
|
-
return path.join(tmpRoot!, projectId)
|
|
46
|
-
}
|
|
47
|
-
})
|
|
48
|
-
|
|
49
|
-
afterEach(async () => {
|
|
50
|
-
pathManager.getStoragePath = originalGetStoragePath
|
|
51
|
-
pathManager.getGlobalProjectPath = originalGetGlobalProjectPath
|
|
52
|
-
|
|
53
|
-
if (tmpRoot) {
|
|
54
|
-
await fs.rm(tmpRoot, { recursive: true, force: true })
|
|
55
|
-
tmpRoot = null
|
|
56
|
-
}
|
|
57
|
-
})
|
|
58
|
-
|
|
59
|
-
// ===========================================================================
|
|
60
|
-
// Timing Tests
|
|
61
|
-
// ===========================================================================
|
|
62
|
-
|
|
63
|
-
describe('timing marks', () => {
|
|
64
|
-
it('should measure elapsed time between start and end', async () => {
|
|
65
|
-
performanceTracker.markStart('test-op')
|
|
66
|
-
|
|
67
|
-
// Simulate some work
|
|
68
|
-
await new Promise((resolve) => setTimeout(resolve, 10))
|
|
69
|
-
|
|
70
|
-
const durationMs = performanceTracker.markEnd('test-op')
|
|
71
|
-
expect(durationMs).not.toBeNull()
|
|
72
|
-
expect(durationMs!).toBeGreaterThan(0)
|
|
73
|
-
expect(durationMs!).toBeLessThan(1000) // Shouldn't take more than 1s
|
|
74
|
-
})
|
|
75
|
-
|
|
76
|
-
it('should return null for non-existent mark', () => {
|
|
77
|
-
const result = performanceTracker.markEnd('nonexistent')
|
|
78
|
-
expect(result).toBeNull()
|
|
79
|
-
})
|
|
80
|
-
|
|
81
|
-
it('should clean up marks after end', () => {
|
|
82
|
-
performanceTracker.markStart('cleanup-test')
|
|
83
|
-
performanceTracker.markEnd('cleanup-test')
|
|
84
|
-
|
|
85
|
-
// Second call should return null
|
|
86
|
-
const result = performanceTracker.markEnd('cleanup-test')
|
|
87
|
-
expect(result).toBeNull()
|
|
88
|
-
})
|
|
89
|
-
})
|
|
90
|
-
|
|
91
|
-
// ===========================================================================
|
|
92
|
-
// Memory Tests
|
|
93
|
-
// ===========================================================================
|
|
94
|
-
|
|
95
|
-
describe('memory snapshots', () => {
|
|
96
|
-
it('should return valid memory snapshot', () => {
|
|
97
|
-
const snapshot = performanceTracker.snapshotMemory()
|
|
98
|
-
|
|
99
|
-
expect(snapshot.heapUsed).toBeGreaterThan(0)
|
|
100
|
-
expect(snapshot.heapTotal).toBeGreaterThan(0)
|
|
101
|
-
expect(snapshot.rss).toBeGreaterThan(0)
|
|
102
|
-
expect(snapshot.external).toBeGreaterThanOrEqual(0)
|
|
103
|
-
// heapUsed can momentarily exceed heapTotal during GC, so just
|
|
104
|
-
// verify both are positive numbers in a reasonable range
|
|
105
|
-
expect(snapshot.heapTotal).toBeGreaterThan(1024 * 1024) // > 1MB
|
|
106
|
-
})
|
|
107
|
-
|
|
108
|
-
it('should record memory to JSONL', async () => {
|
|
109
|
-
const snapshot = await performanceTracker.recordMemory(testProjectId)
|
|
110
|
-
|
|
111
|
-
expect(snapshot.heapUsed).toBeGreaterThan(0)
|
|
112
|
-
|
|
113
|
-
// Verify JSONL file was created
|
|
114
|
-
const filePath = pathManager.getStoragePath(testProjectId, 'performance.jsonl')
|
|
115
|
-
const entries = await jsonlHelper.readJsonLines<PerformanceMetric>(filePath)
|
|
116
|
-
|
|
117
|
-
// Should have 4 entries (heap_used, heap_total, rss, external_memory)
|
|
118
|
-
expect(entries.length).toBe(4)
|
|
119
|
-
expect(entries.map((e) => e.metric)).toEqual([
|
|
120
|
-
'heap_used',
|
|
121
|
-
'heap_total',
|
|
122
|
-
'rss',
|
|
123
|
-
'external_memory',
|
|
124
|
-
])
|
|
125
|
-
})
|
|
126
|
-
})
|
|
127
|
-
|
|
128
|
-
// ===========================================================================
|
|
129
|
-
// Recording Tests
|
|
130
|
-
// ===========================================================================
|
|
131
|
-
|
|
132
|
-
describe('metric recording', () => {
|
|
133
|
-
it('should record timing metric to JSONL', async () => {
|
|
134
|
-
await performanceTracker.recordTiming(testProjectId, 'startup_time', 350.5)
|
|
135
|
-
|
|
136
|
-
const filePath = pathManager.getStoragePath(testProjectId, 'performance.jsonl')
|
|
137
|
-
const entries = await jsonlHelper.readJsonLines<PerformanceMetric>(filePath)
|
|
138
|
-
|
|
139
|
-
expect(entries.length).toBe(1)
|
|
140
|
-
expect(entries[0].metric).toBe('startup_time')
|
|
141
|
-
expect(entries[0].value).toBe(350.5)
|
|
142
|
-
expect(entries[0].unit).toBe('ms')
|
|
143
|
-
expect(entries[0].timestamp).toBeTruthy()
|
|
144
|
-
})
|
|
145
|
-
|
|
146
|
-
it('should record timing with context', async () => {
|
|
147
|
-
await performanceTracker.recordTiming(testProjectId, 'command_duration', 120, {
|
|
148
|
-
command: 'sync',
|
|
149
|
-
})
|
|
150
|
-
|
|
151
|
-
const filePath = pathManager.getStoragePath(testProjectId, 'performance.jsonl')
|
|
152
|
-
const entries = await jsonlHelper.readJsonLines<PerformanceMetric>(filePath)
|
|
153
|
-
|
|
154
|
-
expect(entries[0].context).toEqual({ command: 'sync' })
|
|
155
|
-
})
|
|
156
|
-
|
|
157
|
-
it('should append multiple metrics', async () => {
|
|
158
|
-
await performanceTracker.recordTiming(testProjectId, 'startup_time', 300)
|
|
159
|
-
await performanceTracker.recordTiming(testProjectId, 'startup_time', 250)
|
|
160
|
-
await performanceTracker.recordTiming(testProjectId, 'command_duration', 50)
|
|
161
|
-
|
|
162
|
-
const filePath = pathManager.getStoragePath(testProjectId, 'performance.jsonl')
|
|
163
|
-
const entries = await jsonlHelper.readJsonLines<PerformanceMetric>(filePath)
|
|
164
|
-
|
|
165
|
-
expect(entries.length).toBe(3)
|
|
166
|
-
})
|
|
167
|
-
})
|
|
168
|
-
|
|
169
|
-
// ===========================================================================
|
|
170
|
-
// Context Correctness Tests
|
|
171
|
-
// ===========================================================================
|
|
172
|
-
|
|
173
|
-
describe('context correctness', () => {
|
|
174
|
-
it('should record context correctness entry', async () => {
|
|
175
|
-
await performanceTracker.recordContextCorrectness(testProjectId, {
|
|
176
|
-
taskId: 'task_123',
|
|
177
|
-
receivedSync: true,
|
|
178
|
-
syncFieldsInjected: ['analysis', 'patterns'],
|
|
179
|
-
})
|
|
180
|
-
|
|
181
|
-
const filePath = pathManager.getStoragePath(testProjectId, 'performance.jsonl')
|
|
182
|
-
const entries = await jsonlHelper.readJsonLines<PerformanceEntry>(filePath)
|
|
183
|
-
|
|
184
|
-
expect(entries.length).toBe(1)
|
|
185
|
-
const entry = entries[0] as Record<string, unknown>
|
|
186
|
-
expect(entry.metric).toBe('context_correctness')
|
|
187
|
-
expect(entry.taskId).toBe('task_123')
|
|
188
|
-
expect(entry.receivedSync).toBe(true)
|
|
189
|
-
})
|
|
190
|
-
})
|
|
191
|
-
|
|
192
|
-
// ===========================================================================
|
|
193
|
-
// Subtask Handoff Tests
|
|
194
|
-
// ===========================================================================
|
|
195
|
-
|
|
196
|
-
describe('subtask handoff', () => {
|
|
197
|
-
it('should record handoff entry', async () => {
|
|
198
|
-
await performanceTracker.recordSubtaskHandoff(testProjectId, {
|
|
199
|
-
taskId: 'task_456',
|
|
200
|
-
subtaskId: 'subtask-001',
|
|
201
|
-
outputPopulated: true,
|
|
202
|
-
})
|
|
203
|
-
|
|
204
|
-
const filePath = pathManager.getStoragePath(testProjectId, 'performance.jsonl')
|
|
205
|
-
const entries = await jsonlHelper.readJsonLines<PerformanceEntry>(filePath)
|
|
206
|
-
|
|
207
|
-
expect(entries.length).toBe(1)
|
|
208
|
-
const entry = entries[0] as Record<string, unknown>
|
|
209
|
-
expect(entry.metric).toBe('subtask_handoff')
|
|
210
|
-
expect(entry.outputPopulated).toBe(true)
|
|
211
|
-
})
|
|
212
|
-
})
|
|
213
|
-
|
|
214
|
-
// ===========================================================================
|
|
215
|
-
// Report Generation Tests
|
|
216
|
-
// ===========================================================================
|
|
217
|
-
|
|
218
|
-
describe('report generation', () => {
|
|
219
|
-
it('should return empty report when no data', async () => {
|
|
220
|
-
const report = await performanceTracker.getReport(testProjectId)
|
|
221
|
-
|
|
222
|
-
expect(report.period).toBe('7d')
|
|
223
|
-
expect(report.startup).toBeUndefined()
|
|
224
|
-
expect(report.memory).toBeUndefined()
|
|
225
|
-
expect(report.contextCorrectness).toBeUndefined()
|
|
226
|
-
expect(report.subtaskHandoff).toBeUndefined()
|
|
227
|
-
})
|
|
228
|
-
|
|
229
|
-
it('should aggregate startup time metrics', async () => {
|
|
230
|
-
await performanceTracker.recordTiming(testProjectId, 'startup_time', 300)
|
|
231
|
-
await performanceTracker.recordTiming(testProjectId, 'startup_time', 400)
|
|
232
|
-
await performanceTracker.recordTiming(testProjectId, 'startup_time', 500)
|
|
233
|
-
|
|
234
|
-
const report = await performanceTracker.getReport(testProjectId)
|
|
235
|
-
|
|
236
|
-
expect(report.startup).toBeDefined()
|
|
237
|
-
expect(report.startup!.avg).toBe(400)
|
|
238
|
-
expect(report.startup!.min).toBe(300)
|
|
239
|
-
expect(report.startup!.max).toBe(500)
|
|
240
|
-
expect(report.startup!.count).toBe(3)
|
|
241
|
-
})
|
|
242
|
-
|
|
243
|
-
it('should aggregate memory metrics', async () => {
|
|
244
|
-
await performanceTracker.recordMemory(testProjectId)
|
|
245
|
-
|
|
246
|
-
const report = await performanceTracker.getReport(testProjectId)
|
|
247
|
-
|
|
248
|
-
expect(report.memory).toBeDefined()
|
|
249
|
-
expect(report.memory!.avgHeapMB).toBeGreaterThan(0)
|
|
250
|
-
expect(report.memory!.peakHeapMB).toBeGreaterThan(0)
|
|
251
|
-
})
|
|
252
|
-
|
|
253
|
-
it('should aggregate context correctness', async () => {
|
|
254
|
-
await performanceTracker.recordContextCorrectness(testProjectId, {
|
|
255
|
-
taskId: 't1',
|
|
256
|
-
receivedSync: true,
|
|
257
|
-
})
|
|
258
|
-
await performanceTracker.recordContextCorrectness(testProjectId, {
|
|
259
|
-
taskId: 't2',
|
|
260
|
-
receivedSync: false,
|
|
261
|
-
})
|
|
262
|
-
|
|
263
|
-
const report = await performanceTracker.getReport(testProjectId)
|
|
264
|
-
|
|
265
|
-
expect(report.contextCorrectness).toBeDefined()
|
|
266
|
-
expect(report.contextCorrectness!.total).toBe(2)
|
|
267
|
-
expect(report.contextCorrectness!.receivedSync).toBe(1)
|
|
268
|
-
expect(report.contextCorrectness!.rate).toBe(50)
|
|
269
|
-
})
|
|
270
|
-
|
|
271
|
-
it('should aggregate subtask handoff', async () => {
|
|
272
|
-
await performanceTracker.recordSubtaskHandoff(testProjectId, {
|
|
273
|
-
taskId: 't1',
|
|
274
|
-
subtaskId: 's1',
|
|
275
|
-
outputPopulated: true,
|
|
276
|
-
})
|
|
277
|
-
await performanceTracker.recordSubtaskHandoff(testProjectId, {
|
|
278
|
-
taskId: 't1',
|
|
279
|
-
subtaskId: 's2',
|
|
280
|
-
outputPopulated: true,
|
|
281
|
-
})
|
|
282
|
-
await performanceTracker.recordSubtaskHandoff(testProjectId, {
|
|
283
|
-
taskId: 't1',
|
|
284
|
-
subtaskId: 's3',
|
|
285
|
-
outputPopulated: false,
|
|
286
|
-
})
|
|
287
|
-
|
|
288
|
-
const report = await performanceTracker.getReport(testProjectId)
|
|
289
|
-
|
|
290
|
-
expect(report.subtaskHandoff).toBeDefined()
|
|
291
|
-
expect(report.subtaskHandoff!.total).toBe(3)
|
|
292
|
-
expect(report.subtaskHandoff!.outputPopulated).toBe(2)
|
|
293
|
-
expect(report.subtaskHandoff!.rate).toBe(67)
|
|
294
|
-
})
|
|
295
|
-
|
|
296
|
-
it('should aggregate command durations', async () => {
|
|
297
|
-
await performanceTracker.recordTiming(testProjectId, 'command_duration', 100, {
|
|
298
|
-
command: 'sync',
|
|
299
|
-
})
|
|
300
|
-
await performanceTracker.recordTiming(testProjectId, 'command_duration', 200, {
|
|
301
|
-
command: 'sync',
|
|
302
|
-
})
|
|
303
|
-
await performanceTracker.recordTiming(testProjectId, 'command_duration', 50, {
|
|
304
|
-
command: 'status',
|
|
305
|
-
})
|
|
306
|
-
|
|
307
|
-
const report = await performanceTracker.getReport(testProjectId)
|
|
308
|
-
|
|
309
|
-
expect(report.commandDurations).toBeDefined()
|
|
310
|
-
expect(report.commandDurations!.sync).toBeDefined()
|
|
311
|
-
expect(report.commandDurations!.sync.avg).toBe(150)
|
|
312
|
-
expect(report.commandDurations!.sync.count).toBe(2)
|
|
313
|
-
expect(report.commandDurations!.status).toBeDefined()
|
|
314
|
-
expect(report.commandDurations!.status.avg).toBe(50)
|
|
315
|
-
})
|
|
316
|
-
|
|
317
|
-
it('should filter by date range', async () => {
|
|
318
|
-
// Record some metrics
|
|
319
|
-
await performanceTracker.recordTiming(testProjectId, 'startup_time', 300)
|
|
320
|
-
|
|
321
|
-
// Report for 0 days (should find nothing before today)
|
|
322
|
-
const report = await performanceTracker.getReport(testProjectId, 0)
|
|
323
|
-
// With 0 days, sinceDate = today, so entries from "now" should still match
|
|
324
|
-
// because the filter is >= sinceIso (same day)
|
|
325
|
-
expect(report.startup).toBeDefined()
|
|
326
|
-
})
|
|
327
|
-
})
|
|
328
|
-
})
|
|
@@ -1,272 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Model Schema Tests
|
|
3
|
-
*
|
|
4
|
-
* Tests for:
|
|
5
|
-
* - Provider model validation
|
|
6
|
-
* - Semver comparison
|
|
7
|
-
* - Minimum CLI version enforcement
|
|
8
|
-
* - Model mismatch detection
|
|
9
|
-
* - Default model resolution
|
|
10
|
-
* - Backwards compatibility (missing model field)
|
|
11
|
-
*
|
|
12
|
-
* @see PRJ-265
|
|
13
|
-
*/
|
|
14
|
-
|
|
15
|
-
import { describe, expect, it } from 'bun:test'
|
|
16
|
-
import {
|
|
17
|
-
ClaudeProvider,
|
|
18
|
-
CursorProvider,
|
|
19
|
-
GeminiProvider,
|
|
20
|
-
validateCliVersion,
|
|
21
|
-
} from '../../infrastructure/ai-provider'
|
|
22
|
-
import {
|
|
23
|
-
checkModelMismatch,
|
|
24
|
-
compareSemver,
|
|
25
|
-
getDefaultModel,
|
|
26
|
-
getSupportedModels,
|
|
27
|
-
isValidModelForProvider,
|
|
28
|
-
type ModelMetadata,
|
|
29
|
-
meetsMinVersion,
|
|
30
|
-
} from '../../schemas/model'
|
|
31
|
-
|
|
32
|
-
// =============================================================================
|
|
33
|
-
// Provider Model Configuration
|
|
34
|
-
// =============================================================================
|
|
35
|
-
|
|
36
|
-
describe('Provider model configuration', () => {
|
|
37
|
-
it('should have model fields on Claude provider', () => {
|
|
38
|
-
expect(ClaudeProvider.defaultModel).toBe('sonnet')
|
|
39
|
-
expect(ClaudeProvider.supportedModels).toEqual(['opus', 'sonnet', 'haiku'])
|
|
40
|
-
expect(ClaudeProvider.minCliVersion).toBe('1.0.0')
|
|
41
|
-
})
|
|
42
|
-
|
|
43
|
-
it('should have model fields on Gemini provider', () => {
|
|
44
|
-
expect(GeminiProvider.defaultModel).toBe('2.5-flash')
|
|
45
|
-
expect(GeminiProvider.supportedModels).toEqual(['2.5-pro', '2.5-flash', '2.0-flash'])
|
|
46
|
-
expect(GeminiProvider.minCliVersion).toBe('1.0.0')
|
|
47
|
-
})
|
|
48
|
-
|
|
49
|
-
it('should have null model fields on multi-model IDEs', () => {
|
|
50
|
-
expect(CursorProvider.defaultModel).toBeNull()
|
|
51
|
-
expect(CursorProvider.supportedModels).toEqual([])
|
|
52
|
-
expect(CursorProvider.minCliVersion).toBeNull()
|
|
53
|
-
})
|
|
54
|
-
})
|
|
55
|
-
|
|
56
|
-
// =============================================================================
|
|
57
|
-
// Model Validation
|
|
58
|
-
// =============================================================================
|
|
59
|
-
|
|
60
|
-
describe('isValidModelForProvider', () => {
|
|
61
|
-
it('should accept valid Claude models', () => {
|
|
62
|
-
expect(isValidModelForProvider('claude', 'opus')).toBe(true)
|
|
63
|
-
expect(isValidModelForProvider('claude', 'sonnet')).toBe(true)
|
|
64
|
-
expect(isValidModelForProvider('claude', 'haiku')).toBe(true)
|
|
65
|
-
})
|
|
66
|
-
|
|
67
|
-
it('should reject invalid Claude models', () => {
|
|
68
|
-
expect(isValidModelForProvider('claude', 'gpt-4')).toBe(false)
|
|
69
|
-
expect(isValidModelForProvider('claude', 'unknown')).toBe(false)
|
|
70
|
-
})
|
|
71
|
-
|
|
72
|
-
it('should accept valid Gemini models', () => {
|
|
73
|
-
expect(isValidModelForProvider('gemini', '2.5-pro')).toBe(true)
|
|
74
|
-
expect(isValidModelForProvider('gemini', '2.5-flash')).toBe(true)
|
|
75
|
-
expect(isValidModelForProvider('gemini', '2.0-flash')).toBe(true)
|
|
76
|
-
})
|
|
77
|
-
|
|
78
|
-
it('should reject invalid Gemini models', () => {
|
|
79
|
-
expect(isValidModelForProvider('gemini', 'sonnet')).toBe(false)
|
|
80
|
-
})
|
|
81
|
-
|
|
82
|
-
it('should accept any model for multi-model IDEs (empty supportedModels)', () => {
|
|
83
|
-
expect(isValidModelForProvider('cursor', 'gpt-4')).toBe(true)
|
|
84
|
-
expect(isValidModelForProvider('cursor', 'anything')).toBe(true)
|
|
85
|
-
expect(isValidModelForProvider('windsurf', 'claude-sonnet')).toBe(true)
|
|
86
|
-
})
|
|
87
|
-
|
|
88
|
-
it('should accept any model for unknown providers', () => {
|
|
89
|
-
expect(isValidModelForProvider('future-provider', 'some-model')).toBe(true)
|
|
90
|
-
})
|
|
91
|
-
})
|
|
92
|
-
|
|
93
|
-
// =============================================================================
|
|
94
|
-
// Default Model Resolution
|
|
95
|
-
// =============================================================================
|
|
96
|
-
|
|
97
|
-
describe('getDefaultModel', () => {
|
|
98
|
-
it('should return sonnet for Claude', () => {
|
|
99
|
-
expect(getDefaultModel('claude')).toBe('sonnet')
|
|
100
|
-
})
|
|
101
|
-
|
|
102
|
-
it('should return 2.5-flash for Gemini', () => {
|
|
103
|
-
expect(getDefaultModel('gemini')).toBe('2.5-flash')
|
|
104
|
-
})
|
|
105
|
-
|
|
106
|
-
it('should return null for providers without defaults', () => {
|
|
107
|
-
expect(getDefaultModel('cursor')).toBeNull()
|
|
108
|
-
expect(getDefaultModel('unknown')).toBeNull()
|
|
109
|
-
})
|
|
110
|
-
})
|
|
111
|
-
|
|
112
|
-
// =============================================================================
|
|
113
|
-
// Supported Models
|
|
114
|
-
// =============================================================================
|
|
115
|
-
|
|
116
|
-
describe('getSupportedModels', () => {
|
|
117
|
-
it('should return Claude models', () => {
|
|
118
|
-
expect(getSupportedModels('claude')).toEqual(['opus', 'sonnet', 'haiku'])
|
|
119
|
-
})
|
|
120
|
-
|
|
121
|
-
it('should return empty for multi-model IDEs', () => {
|
|
122
|
-
expect(getSupportedModels('cursor')).toEqual([])
|
|
123
|
-
})
|
|
124
|
-
|
|
125
|
-
it('should return empty for unknown providers', () => {
|
|
126
|
-
expect(getSupportedModels('unknown')).toEqual([])
|
|
127
|
-
})
|
|
128
|
-
})
|
|
129
|
-
|
|
130
|
-
// =============================================================================
|
|
131
|
-
// Semver Comparison
|
|
132
|
-
// =============================================================================
|
|
133
|
-
|
|
134
|
-
describe('compareSemver', () => {
|
|
135
|
-
it('should return 0 for equal versions', () => {
|
|
136
|
-
expect(compareSemver('1.0.0', '1.0.0')).toBe(0)
|
|
137
|
-
expect(compareSemver('2.3.4', '2.3.4')).toBe(0)
|
|
138
|
-
})
|
|
139
|
-
|
|
140
|
-
it('should return -1 when a < b', () => {
|
|
141
|
-
expect(compareSemver('1.0.0', '2.0.0')).toBe(-1)
|
|
142
|
-
expect(compareSemver('1.0.0', '1.1.0')).toBe(-1)
|
|
143
|
-
expect(compareSemver('1.0.0', '1.0.1')).toBe(-1)
|
|
144
|
-
})
|
|
145
|
-
|
|
146
|
-
it('should return 1 when a > b', () => {
|
|
147
|
-
expect(compareSemver('2.0.0', '1.0.0')).toBe(1)
|
|
148
|
-
expect(compareSemver('1.1.0', '1.0.0')).toBe(1)
|
|
149
|
-
expect(compareSemver('1.0.1', '1.0.0')).toBe(1)
|
|
150
|
-
})
|
|
151
|
-
|
|
152
|
-
it('should handle missing patch versions', () => {
|
|
153
|
-
expect(compareSemver('1.0', '1.0.0')).toBe(0)
|
|
154
|
-
})
|
|
155
|
-
})
|
|
156
|
-
|
|
157
|
-
// =============================================================================
|
|
158
|
-
// Version Validation
|
|
159
|
-
// =============================================================================
|
|
160
|
-
|
|
161
|
-
describe('meetsMinVersion', () => {
|
|
162
|
-
it('should pass when version meets minimum', () => {
|
|
163
|
-
expect(meetsMinVersion('claude', '1.0.0')).toBe(true)
|
|
164
|
-
expect(meetsMinVersion('claude', '2.5.0')).toBe(true)
|
|
165
|
-
})
|
|
166
|
-
|
|
167
|
-
it('should fail when version is below minimum', () => {
|
|
168
|
-
expect(meetsMinVersion('claude', '0.9.0')).toBe(false)
|
|
169
|
-
expect(meetsMinVersion('claude', '0.0.1')).toBe(false)
|
|
170
|
-
})
|
|
171
|
-
|
|
172
|
-
it('should pass for providers without minimum', () => {
|
|
173
|
-
expect(meetsMinVersion('cursor', '0.1.0')).toBe(true)
|
|
174
|
-
expect(meetsMinVersion('unknown', '0.0.0')).toBe(true)
|
|
175
|
-
})
|
|
176
|
-
})
|
|
177
|
-
|
|
178
|
-
describe('validateCliVersion', () => {
|
|
179
|
-
it('should return null for valid versions', () => {
|
|
180
|
-
expect(validateCliVersion('claude', '1.0.0')).toBeNull()
|
|
181
|
-
expect(validateCliVersion('claude', '2.0.0')).toBeNull()
|
|
182
|
-
})
|
|
183
|
-
|
|
184
|
-
it('should return warning for versions below minimum', () => {
|
|
185
|
-
const warning = validateCliVersion('claude', '0.5.0')
|
|
186
|
-
expect(warning).toContain('below minimum')
|
|
187
|
-
expect(warning).toContain('Claude Code')
|
|
188
|
-
})
|
|
189
|
-
|
|
190
|
-
it('should return null for undefined version', () => {
|
|
191
|
-
expect(validateCliVersion('claude', undefined)).toBeNull()
|
|
192
|
-
})
|
|
193
|
-
|
|
194
|
-
it('should return null for providers without minCliVersion', () => {
|
|
195
|
-
expect(validateCliVersion('cursor', '0.1.0')).toBeNull()
|
|
196
|
-
})
|
|
197
|
-
})
|
|
198
|
-
|
|
199
|
-
// =============================================================================
|
|
200
|
-
// Model Mismatch Detection
|
|
201
|
-
// =============================================================================
|
|
202
|
-
|
|
203
|
-
describe('checkModelMismatch', () => {
|
|
204
|
-
const claudeOpus: ModelMetadata = {
|
|
205
|
-
provider: 'claude',
|
|
206
|
-
model: 'opus',
|
|
207
|
-
cliVersion: '1.5.0',
|
|
208
|
-
recordedAt: '2026-02-07T00:00:00.000Z',
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
const claudeSonnet: ModelMetadata = {
|
|
212
|
-
provider: 'claude',
|
|
213
|
-
model: 'sonnet',
|
|
214
|
-
cliVersion: '1.5.0',
|
|
215
|
-
recordedAt: '2026-02-07T00:00:00.000Z',
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
const geminiPro: ModelMetadata = {
|
|
219
|
-
provider: 'gemini',
|
|
220
|
-
model: '2.5-pro',
|
|
221
|
-
cliVersion: '1.0.0',
|
|
222
|
-
recordedAt: '2026-02-07T00:00:00.000Z',
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
it('should return null when models match', () => {
|
|
226
|
-
expect(checkModelMismatch(claudeOpus, claudeOpus)).toBeNull()
|
|
227
|
-
})
|
|
228
|
-
|
|
229
|
-
it('should warn when models differ within same provider', () => {
|
|
230
|
-
const warning = checkModelMismatch(claudeOpus, claudeSonnet)
|
|
231
|
-
expect(warning).toContain('mismatch')
|
|
232
|
-
expect(warning).toContain('opus')
|
|
233
|
-
expect(warning).toContain('sonnet')
|
|
234
|
-
})
|
|
235
|
-
|
|
236
|
-
it('should warn when providers differ', () => {
|
|
237
|
-
const warning = checkModelMismatch(claudeOpus, geminiPro)
|
|
238
|
-
expect(warning).toContain('mismatch')
|
|
239
|
-
expect(warning).toContain('claude')
|
|
240
|
-
expect(warning).toContain('gemini')
|
|
241
|
-
})
|
|
242
|
-
|
|
243
|
-
it('should return null when either metadata is undefined', () => {
|
|
244
|
-
expect(checkModelMismatch(undefined, claudeOpus)).toBeNull()
|
|
245
|
-
expect(checkModelMismatch(claudeOpus, undefined)).toBeNull()
|
|
246
|
-
expect(checkModelMismatch(undefined, undefined)).toBeNull()
|
|
247
|
-
})
|
|
248
|
-
})
|
|
249
|
-
|
|
250
|
-
// =============================================================================
|
|
251
|
-
// Backwards Compatibility
|
|
252
|
-
// =============================================================================
|
|
253
|
-
|
|
254
|
-
describe('backwards compatibility', () => {
|
|
255
|
-
it('should handle configs without model field gracefully', () => {
|
|
256
|
-
// Simulates loading an old config without model fields
|
|
257
|
-
const oldProviderConfig = {
|
|
258
|
-
name: 'claude' as const,
|
|
259
|
-
displayName: 'Claude Code',
|
|
260
|
-
cliCommand: 'claude',
|
|
261
|
-
}
|
|
262
|
-
// getDefaultModel should work even if provider config has no model field
|
|
263
|
-
expect(getDefaultModel(oldProviderConfig.name)).toBe('sonnet')
|
|
264
|
-
})
|
|
265
|
-
|
|
266
|
-
it('should resolve default model when preferredModel is not set', () => {
|
|
267
|
-
const preferredModel: string | undefined = undefined
|
|
268
|
-
const provider = 'claude'
|
|
269
|
-
const resolved = preferredModel ?? getDefaultModel(provider)
|
|
270
|
-
expect(resolved).toBe('sonnet')
|
|
271
|
-
})
|
|
272
|
-
})
|