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,393 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Smart Context Compression
|
|
3
|
-
*
|
|
4
|
-
* Intelligently filters context based on task type.
|
|
5
|
-
* Reduces prompt size by 40-70% while maintaining relevance.
|
|
6
|
-
*
|
|
7
|
-
* Uses LLM-based domain classification (PRJ-299) instead of keyword matching.
|
|
8
|
-
*
|
|
9
|
-
* @module agentic/smart-context
|
|
10
|
-
* @version 2.0
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
|
-
import { agentPerformanceTracker } from '../agents'
|
|
14
|
-
import { hasIndexes, rankFiles } from '../domain/file-ranker'
|
|
15
|
-
import { outcomeAnalyzer } from '../outcomes'
|
|
16
|
-
import type {
|
|
17
|
-
ContextDomain,
|
|
18
|
-
DomainAnalysis,
|
|
19
|
-
FilteredContext,
|
|
20
|
-
FullContext,
|
|
21
|
-
SmartContextProjectState,
|
|
22
|
-
StackInfo,
|
|
23
|
-
TaskType,
|
|
24
|
-
} from '../types'
|
|
25
|
-
import domainClassifier, { classifyWithHeuristic, type ProjectContext } from './domain-classifier'
|
|
26
|
-
|
|
27
|
-
// Re-export types for convenience
|
|
28
|
-
export type {
|
|
29
|
-
AgentInfo,
|
|
30
|
-
ContextDomain,
|
|
31
|
-
FeatureInfo,
|
|
32
|
-
FilteredContext,
|
|
33
|
-
FilterMetrics,
|
|
34
|
-
FullContext,
|
|
35
|
-
PatternInfo,
|
|
36
|
-
StackInfo,
|
|
37
|
-
} from '../types'
|
|
38
|
-
|
|
39
|
-
// Type alias exported for backward compatibility (used by external consumers)
|
|
40
|
-
export type ProjectState = SmartContextProjectState
|
|
41
|
-
|
|
42
|
-
// Map ClassificationDomain → ContextDomain
|
|
43
|
-
function toContextDomain(domain: string): ContextDomain {
|
|
44
|
-
const mapping: Record<string, ContextDomain> = {
|
|
45
|
-
frontend: 'frontend',
|
|
46
|
-
backend: 'backend',
|
|
47
|
-
database: 'backend', // database maps to backend context domain
|
|
48
|
-
devops: 'devops',
|
|
49
|
-
testing: 'testing',
|
|
50
|
-
docs: 'docs',
|
|
51
|
-
uxui: 'frontend', // uxui maps to frontend context domain
|
|
52
|
-
general: 'general',
|
|
53
|
-
}
|
|
54
|
-
return mapping[domain] || 'general'
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* SmartContext - Intelligent context filtering.
|
|
59
|
-
*/
|
|
60
|
-
class SmartContext {
|
|
61
|
-
/**
|
|
62
|
-
* Detect the domain of a task from its description.
|
|
63
|
-
*
|
|
64
|
-
* Synchronous version using the improved heuristic (word-boundary matching).
|
|
65
|
-
* For full LLM-based classification, use classifyDomain().
|
|
66
|
-
*/
|
|
67
|
-
detectDomain(taskDescription: string): DomainAnalysis {
|
|
68
|
-
// Default context when no project info is available
|
|
69
|
-
const defaultContext: ProjectContext = {
|
|
70
|
-
domains: {
|
|
71
|
-
hasFrontend: true,
|
|
72
|
-
hasBackend: true,
|
|
73
|
-
hasDatabase: true,
|
|
74
|
-
hasTesting: true,
|
|
75
|
-
hasDocker: true,
|
|
76
|
-
},
|
|
77
|
-
agents: [],
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
const result = classifyWithHeuristic(taskDescription, defaultContext)
|
|
81
|
-
|
|
82
|
-
return {
|
|
83
|
-
primary: toContextDomain(result.primaryDomain),
|
|
84
|
-
secondary: result.secondaryDomains.map(toContextDomain),
|
|
85
|
-
confidence: result.confidence,
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
/**
|
|
90
|
-
* Classify domain using the full fallback chain (cache → history → LLM → heuristic).
|
|
91
|
-
* Async version that leverages project context and LLM classification.
|
|
92
|
-
*/
|
|
93
|
-
async classifyDomain(
|
|
94
|
-
taskDescription: string,
|
|
95
|
-
projectId: string,
|
|
96
|
-
globalPath: string,
|
|
97
|
-
context: ProjectContext
|
|
98
|
-
): Promise<DomainAnalysis & { source: string }> {
|
|
99
|
-
const { classification, source } = await domainClassifier.classify(
|
|
100
|
-
taskDescription,
|
|
101
|
-
projectId,
|
|
102
|
-
globalPath,
|
|
103
|
-
context
|
|
104
|
-
)
|
|
105
|
-
|
|
106
|
-
return {
|
|
107
|
-
primary: toContextDomain(classification.primaryDomain),
|
|
108
|
-
secondary: classification.secondaryDomains.map(toContextDomain),
|
|
109
|
-
confidence: classification.confidence,
|
|
110
|
-
source,
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
/**
|
|
115
|
-
* Filter context for a specific task type.
|
|
116
|
-
*/
|
|
117
|
-
async filterForTask(
|
|
118
|
-
fullContext: FullContext,
|
|
119
|
-
taskDescription: string,
|
|
120
|
-
projectId: string
|
|
121
|
-
): Promise<FilteredContext> {
|
|
122
|
-
const domainAnalysis = this.detectDomain(taskDescription)
|
|
123
|
-
const { primary: taskDomain, secondary } = domainAnalysis
|
|
124
|
-
|
|
125
|
-
// Include primary and secondary domains
|
|
126
|
-
const relevantDomains = [taskDomain, ...secondary, 'general']
|
|
127
|
-
|
|
128
|
-
// Filter agents
|
|
129
|
-
const filteredAgents = fullContext.agents.filter((agent) =>
|
|
130
|
-
relevantDomains.includes(agent.domain)
|
|
131
|
-
)
|
|
132
|
-
|
|
133
|
-
// Enrich with performance data
|
|
134
|
-
for (const agent of filteredAgents) {
|
|
135
|
-
const perf = await agentPerformanceTracker.getAgentPerformance(projectId, agent.name)
|
|
136
|
-
if (perf) {
|
|
137
|
-
agent.successRate = perf.successRate
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
// Filter roadmap
|
|
142
|
-
const filteredRoadmap = fullContext.roadmap.filter((feature) =>
|
|
143
|
-
feature.relatedTo.some((domain) => relevantDomains.includes(domain))
|
|
144
|
-
)
|
|
145
|
-
|
|
146
|
-
// Filter patterns
|
|
147
|
-
const filteredPatterns = fullContext.patterns.filter((pattern) =>
|
|
148
|
-
relevantDomains.includes(pattern.domain)
|
|
149
|
-
)
|
|
150
|
-
|
|
151
|
-
// Get relevant patterns from outcomes
|
|
152
|
-
try {
|
|
153
|
-
const outcomePatterns = await outcomeAnalyzer.detectPatterns(projectId)
|
|
154
|
-
for (const pattern of outcomePatterns.slice(0, 3)) {
|
|
155
|
-
filteredPatterns.push({
|
|
156
|
-
description: pattern.description,
|
|
157
|
-
domain: taskDomain,
|
|
158
|
-
confidence: pattern.confidence,
|
|
159
|
-
})
|
|
160
|
-
}
|
|
161
|
-
} catch (_error) {
|
|
162
|
-
// Outcomes not available
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
// Filter stack
|
|
166
|
-
const filteredStack: Partial<StackInfo> = {}
|
|
167
|
-
if (relevantDomains.includes('frontend')) {
|
|
168
|
-
filteredStack.frontend = fullContext.stack.frontend
|
|
169
|
-
}
|
|
170
|
-
if (relevantDomains.includes('backend')) {
|
|
171
|
-
filteredStack.backend = fullContext.stack.backend
|
|
172
|
-
filteredStack.database = fullContext.stack.database
|
|
173
|
-
}
|
|
174
|
-
if (relevantDomains.includes('devops')) {
|
|
175
|
-
filteredStack.devops = fullContext.stack.devops
|
|
176
|
-
}
|
|
177
|
-
if (relevantDomains.includes('testing')) {
|
|
178
|
-
filteredStack.testing = fullContext.stack.testing
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
// Filter files: use BM25 ranker if indexes exist, else fall back to domain patterns
|
|
182
|
-
const filteredFiles = this.rankOrFilterFiles(
|
|
183
|
-
fullContext.files,
|
|
184
|
-
taskDescription,
|
|
185
|
-
projectId,
|
|
186
|
-
taskDomain
|
|
187
|
-
)
|
|
188
|
-
|
|
189
|
-
// Calculate metrics
|
|
190
|
-
const originalSize = this.estimateSize(fullContext)
|
|
191
|
-
const filteredSize = this.estimateSize({
|
|
192
|
-
agents: filteredAgents,
|
|
193
|
-
roadmap: filteredRoadmap,
|
|
194
|
-
patterns: filteredPatterns,
|
|
195
|
-
stack: filteredStack,
|
|
196
|
-
files: filteredFiles,
|
|
197
|
-
})
|
|
198
|
-
|
|
199
|
-
return {
|
|
200
|
-
agents: filteredAgents,
|
|
201
|
-
roadmap: filteredRoadmap,
|
|
202
|
-
patterns: filteredPatterns,
|
|
203
|
-
stack: filteredStack,
|
|
204
|
-
files: filteredFiles,
|
|
205
|
-
metrics: {
|
|
206
|
-
originalSize,
|
|
207
|
-
filteredSize,
|
|
208
|
-
reductionPercent: Math.round((1 - filteredSize / originalSize) * 100),
|
|
209
|
-
domain: taskDomain,
|
|
210
|
-
},
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
/**
|
|
215
|
-
* Use BM25 + import graph + co-change ranking if indexes exist,
|
|
216
|
-
* otherwise fall back to regex-based domain filtering.
|
|
217
|
-
*/
|
|
218
|
-
private rankOrFilterFiles(
|
|
219
|
-
files: string[],
|
|
220
|
-
taskDescription: string,
|
|
221
|
-
projectId: string,
|
|
222
|
-
domain: ContextDomain
|
|
223
|
-
): string[] {
|
|
224
|
-
try {
|
|
225
|
-
const indexes = hasIndexes(projectId)
|
|
226
|
-
if (indexes.bm25) {
|
|
227
|
-
const ranked = rankFiles(projectId, taskDescription, { topN: 15 })
|
|
228
|
-
if (ranked.length > 0) {
|
|
229
|
-
return ranked.map((r) => r.path)
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
} catch {
|
|
233
|
-
// Index not available — fall through to regex filter
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
return this.filterFiles(files, domain)
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
/**
|
|
240
|
-
* Filter files by domain.
|
|
241
|
-
*/
|
|
242
|
-
private filterFiles(files: string[], domain: ContextDomain): string[] {
|
|
243
|
-
const patterns: Record<ContextDomain, RegExp[]> = {
|
|
244
|
-
frontend: [
|
|
245
|
-
/\.(tsx?|jsx?|vue|svelte)$/,
|
|
246
|
-
/components?\//i,
|
|
247
|
-
/pages?\//i,
|
|
248
|
-
/views?\//i,
|
|
249
|
-
/styles?\//i,
|
|
250
|
-
/hooks?\//i,
|
|
251
|
-
],
|
|
252
|
-
backend: [
|
|
253
|
-
/\.(ts|js|py|go|rs|java)$/,
|
|
254
|
-
/api\//i,
|
|
255
|
-
/routes?\//i,
|
|
256
|
-
/controllers?\//i,
|
|
257
|
-
/services?\//i,
|
|
258
|
-
/models?\//i,
|
|
259
|
-
/handlers?\//i,
|
|
260
|
-
],
|
|
261
|
-
devops: [
|
|
262
|
-
/\.(ya?ml|toml|dockerfile|tf)$/i,
|
|
263
|
-
/docker/i,
|
|
264
|
-
/\.github\//i,
|
|
265
|
-
/deploy/i,
|
|
266
|
-
/infra/i,
|
|
267
|
-
/k8s/i,
|
|
268
|
-
],
|
|
269
|
-
docs: [/\.(md|mdx|rst|txt)$/i, /docs?\//i, /readme/i, /changelog/i],
|
|
270
|
-
testing: [/\.(test|spec)\./i, /tests?\//i, /__tests__\//i, /e2e\//i, /fixtures?\//i],
|
|
271
|
-
general: [],
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
const domainPatterns = patterns[domain]
|
|
275
|
-
if (domainPatterns.length === 0) {
|
|
276
|
-
return files // Return all for general domain
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
// Always include config files
|
|
280
|
-
const configPatterns = [/package\.json$/, /tsconfig\.json$/, /\.config\.(ts|js)$/]
|
|
281
|
-
|
|
282
|
-
return files.filter((file) => {
|
|
283
|
-
// Include if matches domain patterns
|
|
284
|
-
if (domainPatterns.some((p) => p.test(file))) {
|
|
285
|
-
return true
|
|
286
|
-
}
|
|
287
|
-
// Include config files
|
|
288
|
-
if (configPatterns.some((p) => p.test(file))) {
|
|
289
|
-
return true
|
|
290
|
-
}
|
|
291
|
-
return false
|
|
292
|
-
})
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
/**
|
|
296
|
-
* Estimate context size in approximate tokens.
|
|
297
|
-
*/
|
|
298
|
-
private estimateSize(
|
|
299
|
-
context: Partial<{
|
|
300
|
-
agents: unknown[]
|
|
301
|
-
roadmap: unknown[]
|
|
302
|
-
patterns: unknown[]
|
|
303
|
-
stack: unknown
|
|
304
|
-
files: string[]
|
|
305
|
-
state: unknown
|
|
306
|
-
}>
|
|
307
|
-
): number {
|
|
308
|
-
let size = 0
|
|
309
|
-
|
|
310
|
-
// Rough estimates: each item ~50 tokens, files ~10 tokens each
|
|
311
|
-
size += (context.agents?.length || 0) * 50
|
|
312
|
-
size += (context.roadmap?.length || 0) * 50
|
|
313
|
-
size += (context.patterns?.length || 0) * 30
|
|
314
|
-
size += context.stack ? 100 : 0
|
|
315
|
-
size += (context.files?.length || 0) * 10
|
|
316
|
-
size += context.state ? 200 : 0
|
|
317
|
-
|
|
318
|
-
return Math.max(100, size)
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
/**
|
|
322
|
-
* Convert TaskType to ContextDomain.
|
|
323
|
-
*/
|
|
324
|
-
taskTypeToContextDomain(taskType: TaskType): ContextDomain {
|
|
325
|
-
const mapping: Record<TaskType, ContextDomain> = {
|
|
326
|
-
frontend: 'frontend',
|
|
327
|
-
backend: 'backend',
|
|
328
|
-
devops: 'devops',
|
|
329
|
-
database: 'backend',
|
|
330
|
-
testing: 'testing',
|
|
331
|
-
documentation: 'docs',
|
|
332
|
-
refactoring: 'general',
|
|
333
|
-
bugfix: 'general',
|
|
334
|
-
feature: 'general',
|
|
335
|
-
design: 'frontend',
|
|
336
|
-
other: 'general',
|
|
337
|
-
}
|
|
338
|
-
return mapping[taskType]
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
/**
|
|
342
|
-
* Get recommended context for a task based on history.
|
|
343
|
-
*/
|
|
344
|
-
async getRecommendedContext(
|
|
345
|
-
projectId: string,
|
|
346
|
-
taskDescription: string
|
|
347
|
-
): Promise<{
|
|
348
|
-
domain: ContextDomain
|
|
349
|
-
suggestedAgent: string | null
|
|
350
|
-
estimatedDuration: string | null
|
|
351
|
-
patterns: string[]
|
|
352
|
-
}> {
|
|
353
|
-
const domainAnalysis = this.detectDomain(taskDescription)
|
|
354
|
-
const taskType = this.contextDomainToTaskType(domainAnalysis.primary)
|
|
355
|
-
|
|
356
|
-
// Get agent suggestion
|
|
357
|
-
const agentSuggestion = await agentPerformanceTracker.suggestAgent(projectId, taskType)
|
|
358
|
-
|
|
359
|
-
// Get duration estimate
|
|
360
|
-
const durationEstimate = await outcomeAnalyzer.suggestEstimate(projectId, taskType)
|
|
361
|
-
|
|
362
|
-
// Get relevant patterns
|
|
363
|
-
const patterns = await outcomeAnalyzer.detectPatterns(projectId)
|
|
364
|
-
const relevantPatterns = patterns.slice(0, 3).map((p) => p.description)
|
|
365
|
-
|
|
366
|
-
return {
|
|
367
|
-
domain: domainAnalysis.primary,
|
|
368
|
-
suggestedAgent: agentSuggestion?.agentName || null,
|
|
369
|
-
estimatedDuration: durationEstimate,
|
|
370
|
-
patterns: relevantPatterns,
|
|
371
|
-
}
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
/**
|
|
375
|
-
* Convert ContextDomain to TaskType.
|
|
376
|
-
*/
|
|
377
|
-
private contextDomainToTaskType(domain: ContextDomain): TaskType {
|
|
378
|
-
const mapping: Record<ContextDomain, TaskType> = {
|
|
379
|
-
frontend: 'frontend',
|
|
380
|
-
backend: 'backend',
|
|
381
|
-
devops: 'devops',
|
|
382
|
-
docs: 'documentation',
|
|
383
|
-
testing: 'testing',
|
|
384
|
-
general: 'other',
|
|
385
|
-
}
|
|
386
|
-
return mapping[domain]
|
|
387
|
-
}
|
|
388
|
-
}
|
|
389
|
-
|
|
390
|
-
// Singleton instance
|
|
391
|
-
const smartContext = new SmartContext()
|
|
392
|
-
export default smartContext
|
|
393
|
-
export { SmartContext }
|
|
@@ -1,167 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Tech Normalizer (PRJ-300)
|
|
3
|
-
*
|
|
4
|
-
* Normalized matching for tech stack / framework names.
|
|
5
|
-
* Handles:
|
|
6
|
-
* - Compound names: "React + TypeScript" → ["react", "typescript"]
|
|
7
|
-
* - Framework aliases: "nextjs" → "next.js"
|
|
8
|
-
* - Framework families: "Next.js" → react family
|
|
9
|
-
* - Case-insensitive, whitespace-insensitive matching
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
// =============================================================================
|
|
13
|
-
// Framework Families
|
|
14
|
-
// =============================================================================
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* Map framework names to their family (meta-framework → base framework).
|
|
18
|
-
* Used to verify that "Next.js" matches expected "react".
|
|
19
|
-
*/
|
|
20
|
-
const FRAMEWORK_FAMILIES: Record<string, string> = {
|
|
21
|
-
'next.js': 'react',
|
|
22
|
-
nextjs: 'react',
|
|
23
|
-
remix: 'react',
|
|
24
|
-
gatsby: 'react',
|
|
25
|
-
'react native': 'react',
|
|
26
|
-
expo: 'react',
|
|
27
|
-
nuxt: 'vue',
|
|
28
|
-
'nuxt.js': 'vue',
|
|
29
|
-
nuxtjs: 'vue',
|
|
30
|
-
sveltekit: 'svelte',
|
|
31
|
-
analog: 'angular',
|
|
32
|
-
astro: 'multi',
|
|
33
|
-
vite: 'multi',
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* Aliases that normalize to a canonical name.
|
|
38
|
-
*/
|
|
39
|
-
const FRAMEWORK_ALIASES: Record<string, string> = {
|
|
40
|
-
nextjs: 'next.js',
|
|
41
|
-
nuxtjs: 'nuxt.js',
|
|
42
|
-
expressjs: 'express',
|
|
43
|
-
fastifyjs: 'fastify',
|
|
44
|
-
'react.js': 'react',
|
|
45
|
-
'vue.js': 'vue',
|
|
46
|
-
'svelte.js': 'svelte',
|
|
47
|
-
'angular.js': 'angular',
|
|
48
|
-
angularjs: 'angular',
|
|
49
|
-
'node.js': 'node',
|
|
50
|
-
nodejs: 'node',
|
|
51
|
-
ts: 'typescript',
|
|
52
|
-
js: 'javascript',
|
|
53
|
-
pg: 'postgres',
|
|
54
|
-
postgresql: 'postgres',
|
|
55
|
-
mongo: 'mongodb',
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
// =============================================================================
|
|
59
|
-
// Core Functions
|
|
60
|
-
// =============================================================================
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* Normalize a single framework/tech name.
|
|
64
|
-
* Strips whitespace, lowercases, resolves aliases.
|
|
65
|
-
*
|
|
66
|
-
* @example
|
|
67
|
-
* normalizeFrameworkName("Next.js") → "next.js"
|
|
68
|
-
* normalizeFrameworkName(" TypeScript ") → "typescript"
|
|
69
|
-
* normalizeFrameworkName("NodeJS") → "node"
|
|
70
|
-
*/
|
|
71
|
-
export function normalizeFrameworkName(name: string): string {
|
|
72
|
-
const trimmed = name.trim().toLowerCase()
|
|
73
|
-
|
|
74
|
-
// Check aliases
|
|
75
|
-
const aliasKey = trimmed.replace(/[.\s-]/g, '')
|
|
76
|
-
if (FRAMEWORK_ALIASES[aliasKey]) {
|
|
77
|
-
return FRAMEWORK_ALIASES[aliasKey]
|
|
78
|
-
}
|
|
79
|
-
if (FRAMEWORK_ALIASES[trimmed]) {
|
|
80
|
-
return FRAMEWORK_ALIASES[trimmed]
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
return trimmed
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
/**
|
|
87
|
-
* Extract individual tech names from a compound string.
|
|
88
|
-
* Handles separators: +, /, comma, parentheses, "with", "and".
|
|
89
|
-
*
|
|
90
|
-
* @example
|
|
91
|
-
* extractTechNames("React + TypeScript") → ["react", "typescript"]
|
|
92
|
-
* extractTechNames("Next.js (React)") → ["next.js", "react"]
|
|
93
|
-
* extractTechNames("Hono with Zod") → ["hono", "zod"]
|
|
94
|
-
*/
|
|
95
|
-
export function extractTechNames(compound: string): string[] {
|
|
96
|
-
// Replace parentheses with comma separators to split them out
|
|
97
|
-
const parts = compound
|
|
98
|
-
.replace(/[()]/g, ',')
|
|
99
|
-
.split(/[+/,]|\bwith\b|\band\b/i)
|
|
100
|
-
.map((s) => s.trim())
|
|
101
|
-
.filter((s) => s.length > 0)
|
|
102
|
-
|
|
103
|
-
return parts.map(normalizeFrameworkName)
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
/**
|
|
107
|
-
* Get the framework family for a tech name.
|
|
108
|
-
* Returns the base framework or the name itself if no family exists.
|
|
109
|
-
*
|
|
110
|
-
* @example
|
|
111
|
-
* getFrameworkFamily("next.js") → "react"
|
|
112
|
-
* getFrameworkFamily("nuxt") → "vue"
|
|
113
|
-
* getFrameworkFamily("express") → "express"
|
|
114
|
-
*/
|
|
115
|
-
export function getFrameworkFamily(name: string): string {
|
|
116
|
-
const normalized = normalizeFrameworkName(name)
|
|
117
|
-
return FRAMEWORK_FAMILIES[normalized] || normalized
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
/**
|
|
121
|
-
* Check if two tech names match (direct or via family).
|
|
122
|
-
*
|
|
123
|
-
* @example
|
|
124
|
-
* matchesTech("Next.js", "react") → true (Next.js is React family)
|
|
125
|
-
* matchesTech("React + TypeScript", "react") → true (contains react)
|
|
126
|
-
* matchesTech("Vue", "react") → false
|
|
127
|
-
*/
|
|
128
|
-
export function matchesTech(actual: string, expected: string): boolean {
|
|
129
|
-
const expectedNorm = normalizeFrameworkName(expected)
|
|
130
|
-
|
|
131
|
-
// Extract all tech names from actual (handles compound names)
|
|
132
|
-
const actualNames = extractTechNames(actual)
|
|
133
|
-
|
|
134
|
-
for (const name of actualNames) {
|
|
135
|
-
// Direct match
|
|
136
|
-
if (name === expectedNorm) return true
|
|
137
|
-
// Family match
|
|
138
|
-
if (getFrameworkFamily(name) === expectedNorm) return true
|
|
139
|
-
// Reverse family match
|
|
140
|
-
if (getFrameworkFamily(expectedNorm) === name) return true
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
return false
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
/**
|
|
147
|
-
* Deduplicate a tech stack list using normalized matching.
|
|
148
|
-
* Preserves the first occurrence's original casing.
|
|
149
|
-
*
|
|
150
|
-
* @example
|
|
151
|
-
* deduplicateTechStack(["React", "react", "Next.js"]) → ["React", "Next.js"]
|
|
152
|
-
* deduplicateTechStack(["TypeScript", "ts"]) → ["TypeScript"]
|
|
153
|
-
*/
|
|
154
|
-
export function deduplicateTechStack(stack: string[]): string[] {
|
|
155
|
-
const seen = new Set<string>()
|
|
156
|
-
const result: string[] = []
|
|
157
|
-
|
|
158
|
-
for (const name of stack) {
|
|
159
|
-
const normalized = normalizeFrameworkName(name)
|
|
160
|
-
if (!seen.has(normalized)) {
|
|
161
|
-
seen.add(normalized)
|
|
162
|
-
result.push(name)
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
return result
|
|
167
|
-
}
|