prjct-cli 0.45.0 → 0.45.3
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 +75 -0
- package/bin/prjct.ts +117 -10
- package/core/__tests__/agentic/memory-system.test.ts +39 -26
- package/core/__tests__/agentic/plan-mode.test.ts +64 -46
- package/core/__tests__/agentic/prompt-builder.test.ts +14 -14
- package/core/__tests__/services/project-index.test.ts +353 -0
- package/core/__tests__/types/fs.test.ts +3 -3
- package/core/__tests__/utils/date-helper.test.ts +10 -10
- package/core/__tests__/utils/output.test.ts +9 -6
- package/core/__tests__/utils/project-commands.test.ts +5 -6
- package/core/agentic/agent-router.ts +9 -10
- package/core/agentic/chain-of-thought.ts +16 -4
- package/core/agentic/command-executor.ts +66 -40
- package/core/agentic/context-builder.ts +8 -5
- package/core/agentic/ground-truth.ts +15 -9
- package/core/agentic/index.ts +145 -152
- package/core/agentic/loop-detector.ts +40 -11
- package/core/agentic/memory-system.ts +98 -35
- package/core/agentic/orchestrator-executor.ts +135 -71
- package/core/agentic/plan-mode.ts +46 -16
- package/core/agentic/prompt-builder.ts +108 -42
- package/core/agentic/services.ts +10 -9
- package/core/agentic/skill-loader.ts +9 -15
- package/core/agentic/smart-context.ts +129 -79
- package/core/agentic/template-executor.ts +13 -12
- package/core/agentic/template-loader.ts +7 -4
- package/core/agentic/tool-registry.ts +16 -13
- package/core/agents/index.ts +1 -1
- package/core/agents/performance.ts +10 -27
- package/core/ai-tools/formatters.ts +8 -6
- package/core/ai-tools/generator.ts +4 -4
- package/core/ai-tools/index.ts +1 -1
- package/core/ai-tools/registry.ts +21 -11
- package/core/bus/bus.ts +23 -16
- package/core/bus/index.ts +2 -2
- package/core/cli/linear.ts +3 -5
- package/core/cli/start.ts +28 -25
- package/core/commands/analysis.ts +58 -39
- package/core/commands/analytics.ts +52 -44
- package/core/commands/base.ts +15 -13
- package/core/commands/cleanup.ts +6 -13
- package/core/commands/command-data.ts +28 -4
- package/core/commands/commands.ts +57 -24
- package/core/commands/context.ts +4 -4
- package/core/commands/design.ts +3 -10
- package/core/commands/index.ts +5 -8
- package/core/commands/maintenance.ts +7 -4
- package/core/commands/planning.ts +179 -56
- package/core/commands/register.ts +13 -9
- package/core/commands/registry.ts +15 -14
- package/core/commands/setup.ts +26 -14
- package/core/commands/shipping.ts +11 -16
- package/core/commands/snapshots.ts +16 -32
- package/core/commands/uninstall.ts +541 -0
- package/core/commands/workflow.ts +24 -28
- package/core/constants/index.ts +10 -22
- package/core/context/generator.ts +82 -33
- package/core/context-tools/files-tool.ts +18 -19
- package/core/context-tools/imports-tool.ts +13 -33
- package/core/context-tools/index.ts +29 -54
- package/core/context-tools/recent-tool.ts +16 -22
- package/core/context-tools/signatures-tool.ts +17 -26
- package/core/context-tools/summary-tool.ts +20 -22
- package/core/context-tools/token-counter.ts +25 -20
- package/core/context-tools/types.ts +5 -5
- package/core/domain/agent-generator.ts +7 -5
- package/core/domain/agent-loader.ts +2 -2
- package/core/domain/analyzer.ts +19 -16
- package/core/domain/architecture-generator.ts +6 -3
- package/core/domain/context-estimator.ts +3 -4
- package/core/domain/snapshot-manager.ts +25 -22
- package/core/domain/task-stack.ts +24 -14
- package/core/errors.ts +1 -1
- package/core/events/events.ts +2 -4
- package/core/events/index.ts +1 -2
- package/core/index.ts +28 -16
- package/core/infrastructure/agent-detector.ts +3 -3
- package/core/infrastructure/ai-provider.ts +23 -20
- package/core/infrastructure/author-detector.ts +16 -10
- package/core/infrastructure/capability-installer.ts +2 -2
- package/core/infrastructure/claude-agent.ts +6 -6
- package/core/infrastructure/command-installer.ts +22 -17
- package/core/infrastructure/config-manager.ts +18 -14
- package/core/infrastructure/editors-config.ts +8 -4
- package/core/infrastructure/path-manager.ts +8 -6
- package/core/infrastructure/permission-manager.ts +20 -17
- package/core/infrastructure/setup.ts +42 -38
- package/core/infrastructure/update-checker.ts +5 -5
- package/core/integrations/issue-tracker/enricher.ts +8 -19
- package/core/integrations/issue-tracker/index.ts +2 -2
- package/core/integrations/issue-tracker/manager.ts +15 -15
- package/core/integrations/issue-tracker/types.ts +5 -22
- package/core/integrations/jira/client.ts +67 -59
- package/core/integrations/jira/index.ts +11 -14
- package/core/integrations/jira/mcp-adapter.ts +5 -10
- package/core/integrations/jira/service.ts +10 -10
- package/core/integrations/linear/client.ts +27 -18
- package/core/integrations/linear/index.ts +9 -12
- package/core/integrations/linear/service.ts +11 -11
- package/core/integrations/linear/sync.ts +8 -8
- package/core/outcomes/analyzer.ts +5 -18
- package/core/outcomes/index.ts +2 -2
- package/core/outcomes/recorder.ts +3 -3
- package/core/plugin/builtin/webhook.ts +19 -15
- package/core/plugin/hooks.ts +29 -21
- package/core/plugin/index.ts +7 -7
- package/core/plugin/loader.ts +19 -19
- package/core/plugin/registry.ts +12 -23
- package/core/schemas/agents.ts +1 -1
- package/core/schemas/analysis.ts +1 -1
- package/core/schemas/enriched-task.ts +62 -49
- package/core/schemas/ideas.ts +13 -13
- package/core/schemas/index.ts +17 -27
- package/core/schemas/issues.ts +40 -25
- package/core/schemas/metrics.ts +25 -25
- package/core/schemas/outcomes.ts +70 -62
- package/core/schemas/permissions.ts +15 -12
- package/core/schemas/prd.ts +27 -14
- package/core/schemas/project.ts +3 -3
- package/core/schemas/roadmap.ts +47 -34
- package/core/schemas/schemas.ts +3 -4
- package/core/schemas/shipped.ts +3 -3
- package/core/schemas/state.ts +43 -29
- package/core/server/index.ts +5 -6
- package/core/server/routes-extended.ts +68 -72
- package/core/server/routes.ts +3 -3
- package/core/server/server.ts +31 -26
- package/core/services/agent-generator.ts +237 -0
- package/core/services/agent-service.ts +2 -2
- package/core/services/breakdown-service.ts +2 -4
- package/core/services/context-generator.ts +299 -0
- package/core/services/context-selector.ts +420 -0
- package/core/services/doctor-service.ts +426 -0
- package/core/services/file-categorizer.ts +448 -0
- package/core/services/file-scorer.ts +270 -0
- package/core/services/git-analyzer.ts +267 -0
- package/core/services/index.ts +27 -10
- package/core/services/memory-service.ts +3 -4
- package/core/services/project-index.ts +911 -0
- package/core/services/project-service.ts +4 -4
- package/core/services/skill-installer.ts +14 -17
- package/core/services/skill-lock.ts +3 -3
- package/core/services/skill-service.ts +12 -6
- package/core/services/stack-detector.ts +245 -0
- package/core/services/sync-service.ts +87 -345
- package/core/services/watch-service.ts +294 -0
- package/core/session/compaction.ts +23 -31
- package/core/session/index.ts +11 -5
- package/core/session/log-migration.ts +3 -3
- package/core/session/metrics.ts +19 -14
- package/core/session/session-log-manager.ts +12 -17
- package/core/session/task-session-manager.ts +25 -25
- package/core/session/utils.ts +1 -1
- package/core/storage/ideas-storage.ts +41 -57
- package/core/storage/index-storage.ts +514 -0
- package/core/storage/index.ts +41 -17
- package/core/storage/metrics-storage.ts +39 -34
- package/core/storage/queue-storage.ts +35 -45
- package/core/storage/shipped-storage.ts +17 -20
- package/core/storage/state-storage.ts +50 -30
- package/core/storage/storage-manager.ts +6 -6
- package/core/storage/storage.ts +18 -15
- package/core/sync/auth-config.ts +3 -3
- package/core/sync/index.ts +13 -19
- package/core/sync/oauth-handler.ts +3 -3
- package/core/sync/sync-client.ts +4 -9
- package/core/sync/sync-manager.ts +12 -14
- package/core/types/commands.ts +42 -7
- package/core/types/index.ts +284 -305
- package/core/types/integrations.ts +3 -3
- package/core/types/storage.ts +14 -14
- package/core/types/utils.ts +3 -3
- package/core/utils/agent-stream.ts +3 -1
- package/core/utils/animations.ts +14 -11
- package/core/utils/branding.ts +7 -7
- package/core/utils/cache.ts +1 -3
- package/core/utils/collection-filters.ts +3 -15
- package/core/utils/date-helper.ts +2 -7
- package/core/utils/file-helper.ts +13 -8
- package/core/utils/jsonl-helper.ts +13 -10
- package/core/utils/keychain.ts +4 -8
- package/core/utils/logger.ts +1 -1
- package/core/utils/next-steps.ts +3 -3
- package/core/utils/output.ts +58 -11
- package/core/utils/project-commands.ts +6 -6
- package/core/utils/project-credentials.ts +5 -12
- package/core/utils/runtime.ts +2 -2
- package/core/utils/session-helper.ts +3 -4
- package/core/utils/version.ts +3 -3
- package/core/wizard/index.ts +13 -0
- package/core/wizard/onboarding.ts +633 -0
- package/core/workflow/state-machine.ts +7 -7
- package/dist/bin/prjct.mjs +18755 -15574
- package/dist/core/infrastructure/command-installer.js +86 -79
- package/dist/core/infrastructure/editors-config.js +6 -6
- package/dist/core/infrastructure/setup.js +246 -225
- package/dist/core/utils/version.js +9 -9
- package/package.json +11 -12
- package/scripts/build.js +3 -3
- package/scripts/postinstall.js +2 -2
- package/templates/mcp-config.json +6 -1
- package/templates/permissions/permissive.jsonc +1 -1
- package/templates/permissions/strict.jsonc +5 -9
- package/templates/global/docs/agents.md +0 -88
- package/templates/global/docs/architecture.md +0 -103
- package/templates/global/docs/commands.md +0 -96
- package/templates/global/docs/validation.md +0 -95
|
@@ -0,0 +1,420 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ContextSelector - Task-based context selection for Smart Context
|
|
3
|
+
*
|
|
4
|
+
* When user runs `p. task "add payment webhook"`:
|
|
5
|
+
* 1. Detects relevant domains from task description
|
|
6
|
+
* 2. Filters files to only those in matching domains
|
|
7
|
+
* 3. Returns precise, focused context
|
|
8
|
+
*
|
|
9
|
+
* Benefits:
|
|
10
|
+
* - 70-90% reduction in context tokens
|
|
11
|
+
* - More relevant responses from LLM
|
|
12
|
+
* - Faster task understanding
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { type DomainDefinition, indexStorage, type ScoredFile } from '../storage/index-storage'
|
|
16
|
+
|
|
17
|
+
// ============================================================================
|
|
18
|
+
// TYPES
|
|
19
|
+
// ============================================================================
|
|
20
|
+
|
|
21
|
+
export interface SelectedContext {
|
|
22
|
+
files: ScoredFile[]
|
|
23
|
+
domains: string[]
|
|
24
|
+
metrics: {
|
|
25
|
+
totalFiles: number
|
|
26
|
+
selectedFiles: number
|
|
27
|
+
compressionRate: number
|
|
28
|
+
estimatedTokensSaved: number
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface ContextSelectionOptions {
|
|
33
|
+
maxFiles?: number // Max files to return (default: 50)
|
|
34
|
+
minScore?: number // Min relevance score (default: 30)
|
|
35
|
+
includeGeneral?: boolean // Include 'general' domain files (default: true)
|
|
36
|
+
tokenBudget?: number // Max estimated tokens (default: 50000)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// ============================================================================
|
|
40
|
+
// DOMAIN DETECTION PATTERNS
|
|
41
|
+
// ============================================================================
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Keywords that indicate specific domains in task descriptions
|
|
45
|
+
*/
|
|
46
|
+
const DOMAIN_KEYWORDS: Record<string, string[]> = {
|
|
47
|
+
payments: [
|
|
48
|
+
'payment',
|
|
49
|
+
'pay',
|
|
50
|
+
'stripe',
|
|
51
|
+
'billing',
|
|
52
|
+
'checkout',
|
|
53
|
+
'invoice',
|
|
54
|
+
'subscription',
|
|
55
|
+
'charge',
|
|
56
|
+
'refund',
|
|
57
|
+
'transaction',
|
|
58
|
+
'pricing',
|
|
59
|
+
'price',
|
|
60
|
+
],
|
|
61
|
+
auth: [
|
|
62
|
+
'auth',
|
|
63
|
+
'login',
|
|
64
|
+
'logout',
|
|
65
|
+
'signup',
|
|
66
|
+
'sign up',
|
|
67
|
+
'sign in',
|
|
68
|
+
'register',
|
|
69
|
+
'password',
|
|
70
|
+
'session',
|
|
71
|
+
'token',
|
|
72
|
+
'jwt',
|
|
73
|
+
'oauth',
|
|
74
|
+
'sso',
|
|
75
|
+
'permission',
|
|
76
|
+
'role',
|
|
77
|
+
'access',
|
|
78
|
+
'user',
|
|
79
|
+
],
|
|
80
|
+
api: [
|
|
81
|
+
'api',
|
|
82
|
+
'endpoint',
|
|
83
|
+
'route',
|
|
84
|
+
'rest',
|
|
85
|
+
'graphql',
|
|
86
|
+
'webhook',
|
|
87
|
+
'request',
|
|
88
|
+
'response',
|
|
89
|
+
'http',
|
|
90
|
+
'fetch',
|
|
91
|
+
'axios',
|
|
92
|
+
],
|
|
93
|
+
database: [
|
|
94
|
+
'database',
|
|
95
|
+
'db',
|
|
96
|
+
'model',
|
|
97
|
+
'schema',
|
|
98
|
+
'migration',
|
|
99
|
+
'query',
|
|
100
|
+
'sql',
|
|
101
|
+
'prisma',
|
|
102
|
+
'drizzle',
|
|
103
|
+
'mongoose',
|
|
104
|
+
'sequelize',
|
|
105
|
+
'typeorm',
|
|
106
|
+
],
|
|
107
|
+
frontend: [
|
|
108
|
+
'component',
|
|
109
|
+
'page',
|
|
110
|
+
'view',
|
|
111
|
+
'ui',
|
|
112
|
+
'button',
|
|
113
|
+
'form',
|
|
114
|
+
'modal',
|
|
115
|
+
'layout',
|
|
116
|
+
'style',
|
|
117
|
+
'css',
|
|
118
|
+
'react',
|
|
119
|
+
'vue',
|
|
120
|
+
'svelte',
|
|
121
|
+
'html',
|
|
122
|
+
],
|
|
123
|
+
testing: [
|
|
124
|
+
'test',
|
|
125
|
+
'spec',
|
|
126
|
+
'unit',
|
|
127
|
+
'e2e',
|
|
128
|
+
'cypress',
|
|
129
|
+
'jest',
|
|
130
|
+
'vitest',
|
|
131
|
+
'mocha',
|
|
132
|
+
'coverage',
|
|
133
|
+
'mock',
|
|
134
|
+
],
|
|
135
|
+
integrations: [
|
|
136
|
+
'integration',
|
|
137
|
+
'integrate',
|
|
138
|
+
'connect',
|
|
139
|
+
'sync',
|
|
140
|
+
'webhook',
|
|
141
|
+
'oauth',
|
|
142
|
+
'linear',
|
|
143
|
+
'jira',
|
|
144
|
+
'github',
|
|
145
|
+
'slack',
|
|
146
|
+
'discord',
|
|
147
|
+
],
|
|
148
|
+
config: ['config', 'configuration', 'setting', 'env', 'environment', 'setup'],
|
|
149
|
+
utilities: ['util', 'utility', 'helper', 'lib', 'common', 'shared', 'tool'],
|
|
150
|
+
services: ['service', 'handler', 'processor', 'worker', 'job', 'queue', 'cron'],
|
|
151
|
+
types: ['type', 'interface', 'dto', 'schema', 'definition'],
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// ============================================================================
|
|
155
|
+
// CONTEXT SELECTOR CLASS
|
|
156
|
+
// ============================================================================
|
|
157
|
+
|
|
158
|
+
export class ContextSelector {
|
|
159
|
+
private readonly CHARS_PER_TOKEN = 4
|
|
160
|
+
|
|
161
|
+
// ==========================================================================
|
|
162
|
+
// MAIN METHODS
|
|
163
|
+
// ==========================================================================
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Select relevant files for a task description
|
|
167
|
+
*/
|
|
168
|
+
async selectForTask(
|
|
169
|
+
taskDescription: string,
|
|
170
|
+
projectId: string,
|
|
171
|
+
options: ContextSelectionOptions = {}
|
|
172
|
+
): Promise<SelectedContext> {
|
|
173
|
+
const maxFiles = options.maxFiles || 50
|
|
174
|
+
const minScore = options.minScore || 30
|
|
175
|
+
const includeGeneral = options.includeGeneral !== false
|
|
176
|
+
const tokenBudget = options.tokenBudget || 50000
|
|
177
|
+
|
|
178
|
+
// Load index and categories
|
|
179
|
+
const [index, domainsData, categoriesCache] = await Promise.all([
|
|
180
|
+
indexStorage.readIndex(projectId),
|
|
181
|
+
indexStorage.readDomains(projectId),
|
|
182
|
+
indexStorage.readCategories(projectId),
|
|
183
|
+
])
|
|
184
|
+
|
|
185
|
+
if (!index || !domainsData || !categoriesCache) {
|
|
186
|
+
// No categorization data available, return all relevant files
|
|
187
|
+
return this.fallbackSelection(index?.relevantFiles || [], options)
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// Detect domains from task description
|
|
191
|
+
const detectedDomains = this.detectTaskDomains(taskDescription, domainsData.domains)
|
|
192
|
+
|
|
193
|
+
// Get files for detected domains
|
|
194
|
+
const selectedPaths = new Set<string>()
|
|
195
|
+
|
|
196
|
+
for (const domain of detectedDomains) {
|
|
197
|
+
const domainFiles = categoriesCache.domainIndex[domain] || []
|
|
198
|
+
for (const filePath of domainFiles) {
|
|
199
|
+
selectedPaths.add(filePath)
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// Optionally include 'general' files
|
|
204
|
+
if (includeGeneral && categoriesCache.domainIndex.general) {
|
|
205
|
+
// Add top general files by score
|
|
206
|
+
const generalFiles = categoriesCache.domainIndex.general.slice(0, 10)
|
|
207
|
+
for (const filePath of generalFiles) {
|
|
208
|
+
selectedPaths.add(filePath)
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// Filter to only files in the index with score >= minScore
|
|
213
|
+
const selectedFiles = index.relevantFiles.filter(
|
|
214
|
+
(f) => selectedPaths.has(f.path) && f.score >= minScore
|
|
215
|
+
)
|
|
216
|
+
|
|
217
|
+
// Sort by score and limit
|
|
218
|
+
selectedFiles.sort((a, b) => b.score - a.score)
|
|
219
|
+
|
|
220
|
+
// Apply token budget
|
|
221
|
+
let estimatedTokens = 0
|
|
222
|
+
const budgetedFiles: ScoredFile[] = []
|
|
223
|
+
|
|
224
|
+
for (const file of selectedFiles) {
|
|
225
|
+
const fileTokens = Math.ceil(file.size / this.CHARS_PER_TOKEN)
|
|
226
|
+
if (estimatedTokens + fileTokens > tokenBudget) {
|
|
227
|
+
break
|
|
228
|
+
}
|
|
229
|
+
if (budgetedFiles.length >= maxFiles) {
|
|
230
|
+
break
|
|
231
|
+
}
|
|
232
|
+
budgetedFiles.push(file)
|
|
233
|
+
estimatedTokens += fileTokens
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// Calculate metrics
|
|
237
|
+
const totalTokens = Math.ceil(
|
|
238
|
+
index.relevantFiles.reduce((sum, f) => sum + f.size, 0) / this.CHARS_PER_TOKEN
|
|
239
|
+
)
|
|
240
|
+
const compressionRate = totalTokens > 0 ? (totalTokens - estimatedTokens) / totalTokens : 0
|
|
241
|
+
|
|
242
|
+
return {
|
|
243
|
+
files: budgetedFiles,
|
|
244
|
+
domains: detectedDomains,
|
|
245
|
+
metrics: {
|
|
246
|
+
totalFiles: index.relevantFiles.length,
|
|
247
|
+
selectedFiles: budgetedFiles.length,
|
|
248
|
+
compressionRate,
|
|
249
|
+
estimatedTokensSaved: totalTokens - estimatedTokens,
|
|
250
|
+
},
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Detect domains from task description
|
|
256
|
+
*/
|
|
257
|
+
detectTaskDomains(description: string, projectDomains: DomainDefinition[]): string[] {
|
|
258
|
+
const normalizedDesc = description.toLowerCase()
|
|
259
|
+
const detectedDomains = new Set<string>()
|
|
260
|
+
|
|
261
|
+
// Check against keyword patterns
|
|
262
|
+
for (const [domain, keywords] of Object.entries(DOMAIN_KEYWORDS)) {
|
|
263
|
+
for (const keyword of keywords) {
|
|
264
|
+
if (normalizedDesc.includes(keyword)) {
|
|
265
|
+
detectedDomains.add(domain)
|
|
266
|
+
break
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// Check against project-specific domains
|
|
272
|
+
for (const domain of projectDomains) {
|
|
273
|
+
// Check domain name
|
|
274
|
+
if (normalizedDesc.includes(domain.name.toLowerCase())) {
|
|
275
|
+
detectedDomains.add(domain.name)
|
|
276
|
+
continue
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// Check domain keywords
|
|
280
|
+
for (const keyword of domain.keywords) {
|
|
281
|
+
if (normalizedDesc.includes(keyword.toLowerCase())) {
|
|
282
|
+
detectedDomains.add(domain.name)
|
|
283
|
+
break
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
// If no domains detected, return common ones based on task type
|
|
289
|
+
if (detectedDomains.size === 0) {
|
|
290
|
+
// Default to common development domains
|
|
291
|
+
detectedDomains.add('services')
|
|
292
|
+
detectedDomains.add('api')
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
return Array.from(detectedDomains)
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
/**
|
|
299
|
+
* Filter files by specific domains
|
|
300
|
+
*/
|
|
301
|
+
async filterByDomains(
|
|
302
|
+
projectId: string,
|
|
303
|
+
domains: string[],
|
|
304
|
+
files?: ScoredFile[]
|
|
305
|
+
): Promise<ScoredFile[]> {
|
|
306
|
+
const categoriesCache = await indexStorage.readCategories(projectId)
|
|
307
|
+
if (!categoriesCache) {
|
|
308
|
+
return files || []
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
// Get all file paths for requested domains
|
|
312
|
+
const domainFilePaths = new Set<string>()
|
|
313
|
+
for (const domain of domains) {
|
|
314
|
+
const domainFiles = categoriesCache.domainIndex[domain] || []
|
|
315
|
+
for (const filePath of domainFiles) {
|
|
316
|
+
domainFilePaths.add(filePath)
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// If files provided, filter them; otherwise use all categorized files
|
|
321
|
+
if (files) {
|
|
322
|
+
return files.filter((f) => domainFilePaths.has(f.path))
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
// Load index and filter
|
|
326
|
+
const index = await indexStorage.readIndex(projectId)
|
|
327
|
+
if (!index) {
|
|
328
|
+
return []
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
return index.relevantFiles.filter((f) => domainFilePaths.has(f.path))
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
/**
|
|
335
|
+
* Get domains for a specific file
|
|
336
|
+
*/
|
|
337
|
+
async getFilesDomains(projectId: string, filePaths: string[]): Promise<Map<string, string[]>> {
|
|
338
|
+
return indexStorage.getFileCategories(projectId, filePaths)
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
// ==========================================================================
|
|
342
|
+
// HELPER METHODS
|
|
343
|
+
// ==========================================================================
|
|
344
|
+
|
|
345
|
+
/**
|
|
346
|
+
* Fallback selection when no categorization data available
|
|
347
|
+
*/
|
|
348
|
+
private fallbackSelection(
|
|
349
|
+
files: ScoredFile[],
|
|
350
|
+
options: ContextSelectionOptions
|
|
351
|
+
): SelectedContext {
|
|
352
|
+
const maxFiles = options.maxFiles || 50
|
|
353
|
+
const minScore = options.minScore || 30
|
|
354
|
+
const tokenBudget = options.tokenBudget || 50000
|
|
355
|
+
|
|
356
|
+
// Filter and sort by score
|
|
357
|
+
const filteredFiles = files.filter((f) => f.score >= minScore).sort((a, b) => b.score - a.score)
|
|
358
|
+
|
|
359
|
+
// Apply token budget
|
|
360
|
+
let estimatedTokens = 0
|
|
361
|
+
const selectedFiles: ScoredFile[] = []
|
|
362
|
+
|
|
363
|
+
for (const file of filteredFiles) {
|
|
364
|
+
const fileTokens = Math.ceil(file.size / this.CHARS_PER_TOKEN)
|
|
365
|
+
if (estimatedTokens + fileTokens > tokenBudget) {
|
|
366
|
+
break
|
|
367
|
+
}
|
|
368
|
+
if (selectedFiles.length >= maxFiles) {
|
|
369
|
+
break
|
|
370
|
+
}
|
|
371
|
+
selectedFiles.push(file)
|
|
372
|
+
estimatedTokens += fileTokens
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
const totalTokens = Math.ceil(files.reduce((sum, f) => sum + f.size, 0) / this.CHARS_PER_TOKEN)
|
|
376
|
+
|
|
377
|
+
return {
|
|
378
|
+
files: selectedFiles,
|
|
379
|
+
domains: [], // No domain data available
|
|
380
|
+
metrics: {
|
|
381
|
+
totalFiles: files.length,
|
|
382
|
+
selectedFiles: selectedFiles.length,
|
|
383
|
+
compressionRate: totalTokens > 0 ? (totalTokens - estimatedTokens) / totalTokens : 0,
|
|
384
|
+
estimatedTokensSaved: totalTokens - estimatedTokens,
|
|
385
|
+
},
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
/**
|
|
390
|
+
* Suggest related domains based on detected ones
|
|
391
|
+
*/
|
|
392
|
+
suggestRelatedDomains(domains: string[]): string[] {
|
|
393
|
+
const related = new Set<string>()
|
|
394
|
+
|
|
395
|
+
const relationships: Record<string, string[]> = {
|
|
396
|
+
payments: ['api', 'database', 'services'],
|
|
397
|
+
auth: ['api', 'database', 'users'],
|
|
398
|
+
api: ['services', 'types'],
|
|
399
|
+
database: ['types', 'services'],
|
|
400
|
+
frontend: ['types', 'utilities'],
|
|
401
|
+
testing: ['services', 'api'],
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
for (const domain of domains) {
|
|
405
|
+
const relatedDomains = relationships[domain]
|
|
406
|
+
if (relatedDomains) {
|
|
407
|
+
for (const r of relatedDomains) {
|
|
408
|
+
if (!domains.includes(r)) {
|
|
409
|
+
related.add(r)
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
return Array.from(related)
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
export const contextSelector = new ContextSelector()
|
|
420
|
+
export default ContextSelector
|