prjct-cli 0.15.1 → 0.17.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 +35 -0
- package/bin/dev.js +0 -1
- package/bin/serve.js +19 -20
- package/core/agentic/agent-router.ts +79 -14
- package/core/agentic/command-executor/command-executor.ts +2 -74
- package/core/agentic/services.ts +0 -48
- package/core/agentic/template-loader.ts +35 -1
- package/core/commands/base.ts +96 -77
- package/core/commands/planning.ts +13 -2
- package/core/commands/setup.ts +3 -85
- package/core/errors.ts +209 -0
- package/core/infrastructure/config-manager.ts +22 -5
- package/core/infrastructure/setup.ts +5 -50
- package/core/storage/storage-manager.ts +42 -6
- package/core/utils/logger.ts +19 -12
- package/package.json +2 -4
- package/templates/agentic/subagent-generation.md +109 -0
- package/templates/commands/sync.md +74 -13
- package/templates/subagents/domain/backend.md +105 -0
- package/templates/subagents/domain/database.md +118 -0
- package/templates/subagents/domain/devops.md +148 -0
- package/templates/subagents/domain/frontend.md +99 -0
- package/templates/subagents/domain/testing.md +169 -0
- package/templates/subagents/workflow/prjct-planner.md +158 -0
- package/templates/subagents/workflow/prjct-shipper.md +179 -0
- package/templates/subagents/workflow/prjct-workflow.md +98 -0
- package/bin/generate-views.js +0 -209
- package/bin/migrate-to-json.js +0 -742
- package/core/agentic/context-filter.ts +0 -365
- package/core/agentic/parallel-tools.ts +0 -165
- package/core/agentic/response-templates.ts +0 -164
- package/core/agentic/semantic-compression.ts +0 -273
- package/core/agentic/think-blocks.ts +0 -202
- package/core/agentic/validation-rules.ts +0 -313
- package/core/domain/agent-matcher.ts +0 -130
- package/core/domain/agent-validator.ts +0 -250
- package/core/domain/architect-session.ts +0 -315
- package/core/domain/product-standards.ts +0 -106
- package/core/domain/smart-cache.ts +0 -167
- package/core/domain/task-analyzer.ts +0 -296
- package/core/infrastructure/legacy-installer-detector/cleanup.ts +0 -216
- package/core/infrastructure/legacy-installer-detector/detection.ts +0 -95
- package/core/infrastructure/legacy-installer-detector/index.ts +0 -171
- package/core/infrastructure/legacy-installer-detector/migration.ts +0 -87
- package/core/infrastructure/legacy-installer-detector/types.ts +0 -42
- package/core/infrastructure/legacy-installer-detector.ts +0 -7
- package/core/infrastructure/migrator/file-operations.ts +0 -125
- package/core/infrastructure/migrator/index.ts +0 -288
- package/core/infrastructure/migrator/project-scanner.ts +0 -90
- package/core/infrastructure/migrator/reports.ts +0 -117
- package/core/infrastructure/migrator/types.ts +0 -124
- package/core/infrastructure/migrator/validation.ts +0 -94
- package/core/infrastructure/migrator/version-migration.ts +0 -117
- package/core/infrastructure/migrator.ts +0 -10
- package/core/infrastructure/uuid-migration.ts +0 -750
- package/templates/commands/migrate-all.md +0 -96
- package/templates/commands/migrate.md +0 -140
|
@@ -1,365 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Intelligent Context Filtering System
|
|
3
|
-
*
|
|
4
|
-
* Reduces context window usage by 70-90% by loading only
|
|
5
|
-
* relevant files for each specialized agent
|
|
6
|
-
*
|
|
7
|
-
* @version 1.0.0
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
import fs from 'fs/promises'
|
|
11
|
-
import path from 'path'
|
|
12
|
-
import { glob } from 'glob'
|
|
13
|
-
import log from '../utils/logger'
|
|
14
|
-
|
|
15
|
-
interface Agent {
|
|
16
|
-
name: string
|
|
17
|
-
[key: string]: unknown
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
interface Task {
|
|
21
|
-
[key: string]: unknown
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
interface FullContext {
|
|
25
|
-
estimatedFiles?: string[]
|
|
26
|
-
fileCount?: number
|
|
27
|
-
[key: string]: unknown
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
interface Patterns {
|
|
31
|
-
include: string[]
|
|
32
|
-
exclude: string[]
|
|
33
|
-
realExtensions?: Record<string, number>
|
|
34
|
-
projectStructure?: string[]
|
|
35
|
-
configFiles?: string[]
|
|
36
|
-
detectedTech?: Record<string, unknown>
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
interface Metrics {
|
|
40
|
-
originalFiles: number
|
|
41
|
-
filteredFiles: number
|
|
42
|
-
reductionPercent: number
|
|
43
|
-
processingTime: number
|
|
44
|
-
effectiveness: string
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
interface FilterResult {
|
|
48
|
-
files: string[]
|
|
49
|
-
patterns: {
|
|
50
|
-
preEstimated?: boolean
|
|
51
|
-
detectedTech?: Record<string, unknown>
|
|
52
|
-
projectStructure?: string[]
|
|
53
|
-
agentic?: boolean
|
|
54
|
-
}
|
|
55
|
-
metrics: Metrics
|
|
56
|
-
agent: string
|
|
57
|
-
filtered: boolean
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
class ContextFilter {
|
|
61
|
-
fileCache: Map<string, unknown>
|
|
62
|
-
|
|
63
|
-
constructor() {
|
|
64
|
-
// Cache for file analysis
|
|
65
|
-
this.fileCache = new Map()
|
|
66
|
-
// NO HARDCODED PATTERNS - Everything is agentic
|
|
67
|
-
// Claude decides what files are needed based on analysis
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
/**
|
|
71
|
-
* Main entry point - filters context based on agent and task
|
|
72
|
-
* IMPROVED: Supports pre-estimated files for lazy loading
|
|
73
|
-
*/
|
|
74
|
-
async filterForAgent(
|
|
75
|
-
agent: Agent,
|
|
76
|
-
task: Task,
|
|
77
|
-
projectPath: string,
|
|
78
|
-
fullContext: FullContext = {}
|
|
79
|
-
): Promise<FilterResult> {
|
|
80
|
-
const startTime = Date.now()
|
|
81
|
-
|
|
82
|
-
// If files were pre-estimated (lazy loading), use them
|
|
83
|
-
if (fullContext.estimatedFiles && fullContext.estimatedFiles.length > 0) {
|
|
84
|
-
const filteredFiles = fullContext.estimatedFiles
|
|
85
|
-
|
|
86
|
-
const metrics = this.calculateMetrics(fullContext.fileCount || filteredFiles.length, filteredFiles.length, startTime)
|
|
87
|
-
|
|
88
|
-
return {
|
|
89
|
-
files: filteredFiles,
|
|
90
|
-
patterns: { preEstimated: true },
|
|
91
|
-
metrics,
|
|
92
|
-
agent: agent.name,
|
|
93
|
-
filtered: true,
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
// Fallback to traditional filtering if no pre-estimation
|
|
98
|
-
// Determine what files this agent needs
|
|
99
|
-
const relevantPatterns = await this.determineRelevantPatterns(agent, task, projectPath)
|
|
100
|
-
|
|
101
|
-
// Load only relevant files
|
|
102
|
-
const filteredFiles = await this.loadRelevantFiles(projectPath, relevantPatterns)
|
|
103
|
-
|
|
104
|
-
// Calculate reduction metrics
|
|
105
|
-
const metrics = this.calculateMetrics(
|
|
106
|
-
fullContext.fileCount || 1000, // estimate if not provided
|
|
107
|
-
filteredFiles.length,
|
|
108
|
-
startTime
|
|
109
|
-
)
|
|
110
|
-
|
|
111
|
-
return {
|
|
112
|
-
files: filteredFiles,
|
|
113
|
-
patterns: {
|
|
114
|
-
detectedTech: relevantPatterns.detectedTech,
|
|
115
|
-
projectStructure: relevantPatterns.projectStructure,
|
|
116
|
-
agentic: true, // Flag indicating this was agentic, not hardcoded
|
|
117
|
-
},
|
|
118
|
-
metrics,
|
|
119
|
-
agent: agent.name,
|
|
120
|
-
filtered: true,
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
/**
|
|
125
|
-
* Determine which patterns to use based on agent and task
|
|
126
|
-
*
|
|
127
|
-
* 100% AGENTIC: Uses analyzer for I/O, no hardcoded tech detection.
|
|
128
|
-
* Claude decides what files matter based on actual project analysis.
|
|
129
|
-
*/
|
|
130
|
-
async determineRelevantPatterns(_agent: Agent, _task: Task, projectPath: string): Promise<Patterns> {
|
|
131
|
-
const { default: analyzer } = await import('../domain/analyzer')
|
|
132
|
-
analyzer.init(projectPath)
|
|
133
|
-
|
|
134
|
-
// Get REAL file extensions from project (not assumed)
|
|
135
|
-
const realExtensions = await analyzer.getFileExtensions()
|
|
136
|
-
|
|
137
|
-
// Get REAL directory structure (not assumed)
|
|
138
|
-
const projectStructure = await analyzer.listDirectories()
|
|
139
|
-
|
|
140
|
-
// Get config files that exist (not hardcoded list)
|
|
141
|
-
const configFiles = await analyzer.listConfigFiles()
|
|
142
|
-
|
|
143
|
-
// Build patterns from ACTUAL project data
|
|
144
|
-
const patterns: Patterns = {
|
|
145
|
-
include: [],
|
|
146
|
-
exclude: ['node_modules', '.git', 'dist', 'build', 'coverage', '.next', '.nuxt', 'target', 'vendor'],
|
|
147
|
-
realExtensions, // Actual extensions found in project
|
|
148
|
-
projectStructure, // Actual directories
|
|
149
|
-
configFiles, // Actual config files
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
return patterns
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
/**
|
|
156
|
-
* Detect actual project structure (no assumptions)
|
|
157
|
-
*/
|
|
158
|
-
async detectProjectStructure(projectPath: string): Promise<string[]> {
|
|
159
|
-
try {
|
|
160
|
-
const entries = await fs.readdir(projectPath, { withFileTypes: true })
|
|
161
|
-
const directories = entries.filter((e) => e.isDirectory() && !e.name.startsWith('.')).map((e) => e.name)
|
|
162
|
-
return directories
|
|
163
|
-
} catch {
|
|
164
|
-
return []
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
/**
|
|
169
|
-
* Detect technologies used in the project
|
|
170
|
-
*
|
|
171
|
-
* 100% AGENTIC: Uses analyzer for raw data.
|
|
172
|
-
* No categorization - Claude decides what's relevant.
|
|
173
|
-
*/
|
|
174
|
-
async detectProjectTechnologies(
|
|
175
|
-
projectPath: string
|
|
176
|
-
): Promise<{ extensions: Record<string, number>; directories: string[]; configFiles: string[] }> {
|
|
177
|
-
try {
|
|
178
|
-
const { default: analyzer } = await import('../domain/analyzer')
|
|
179
|
-
analyzer.init(projectPath)
|
|
180
|
-
|
|
181
|
-
// Return raw data for Claude to analyze
|
|
182
|
-
return {
|
|
183
|
-
extensions: await analyzer.getFileExtensions(),
|
|
184
|
-
directories: await analyzer.listDirectories(),
|
|
185
|
-
configFiles: await analyzer.listConfigFiles(),
|
|
186
|
-
}
|
|
187
|
-
} catch (error) {
|
|
188
|
-
log.error('Error detecting project data:', (error as Error).message)
|
|
189
|
-
return { extensions: {}, directories: [], configFiles: [] }
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
/**
|
|
194
|
-
* Load only relevant files based on patterns
|
|
195
|
-
*/
|
|
196
|
-
async loadRelevantFiles(projectPath: string, patterns: Patterns): Promise<string[]> {
|
|
197
|
-
const files: string[] = []
|
|
198
|
-
|
|
199
|
-
try {
|
|
200
|
-
// Build glob patterns
|
|
201
|
-
const globPatterns = this.buildGlobPatterns(patterns)
|
|
202
|
-
|
|
203
|
-
// Execute glob searches
|
|
204
|
-
for (const pattern of globPatterns) {
|
|
205
|
-
const matches = await glob(pattern, {
|
|
206
|
-
cwd: projectPath,
|
|
207
|
-
ignore: patterns.exclude.map((ex) => `**/${ex}/**`),
|
|
208
|
-
nodir: true,
|
|
209
|
-
follow: false,
|
|
210
|
-
})
|
|
211
|
-
|
|
212
|
-
// Ensure matches is always an array (glob v10+ returns array, but be defensive)
|
|
213
|
-
if (Array.isArray(matches)) {
|
|
214
|
-
files.push(...matches)
|
|
215
|
-
} else if (matches) {
|
|
216
|
-
// Convert iterable to array if needed
|
|
217
|
-
files.push(...Array.from(matches as Iterable<string>))
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
// Remove duplicates and sort
|
|
222
|
-
const uniqueFiles = [...new Set(files)].sort()
|
|
223
|
-
|
|
224
|
-
// Limit to reasonable number
|
|
225
|
-
const maxFiles = 300
|
|
226
|
-
if (uniqueFiles.length > maxFiles) {
|
|
227
|
-
log.debug(`Limiting context to ${maxFiles} files`)
|
|
228
|
-
return uniqueFiles.slice(0, maxFiles)
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
// Expand context with related files
|
|
232
|
-
const expandedFiles = await this.expandContext(uniqueFiles, projectPath)
|
|
233
|
-
|
|
234
|
-
return expandedFiles.slice(0, maxFiles)
|
|
235
|
-
} catch (error) {
|
|
236
|
-
log.error('Error loading files:', (error as Error).message)
|
|
237
|
-
return []
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
/**
|
|
242
|
-
* Build glob patterns from pattern configuration
|
|
243
|
-
*
|
|
244
|
-
* 100% AGENTIC: Uses REAL extensions from project, not hardcoded mapping.
|
|
245
|
-
* No language→extension assumptions.
|
|
246
|
-
*/
|
|
247
|
-
buildGlobPatterns(patterns: Patterns): string[] {
|
|
248
|
-
const globs: string[] = []
|
|
249
|
-
|
|
250
|
-
// Use REAL extensions found in project (no hardcoded mapping)
|
|
251
|
-
if (patterns.realExtensions && Object.keys(patterns.realExtensions).length > 0) {
|
|
252
|
-
// Get extensions that actually exist in this project
|
|
253
|
-
const extensions = Object.keys(patterns.realExtensions)
|
|
254
|
-
.filter((ext) => ext.startsWith('.')) // Only valid extensions
|
|
255
|
-
.slice(0, 20) // Limit to top 20 most common
|
|
256
|
-
|
|
257
|
-
if (extensions.length > 0) {
|
|
258
|
-
globs.push(`**/*{${extensions.join(',')}}`)
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
// Use REAL project structure (no assumptions)
|
|
263
|
-
if (patterns.projectStructure && patterns.projectStructure.length > 0) {
|
|
264
|
-
patterns.projectStructure.forEach((dir) => {
|
|
265
|
-
// Exclude universal noise directories
|
|
266
|
-
if (!patterns.exclude.includes(dir)) {
|
|
267
|
-
globs.push(`${dir}/**/*`)
|
|
268
|
-
}
|
|
269
|
-
})
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
// Include REAL config files that exist (not hardcoded list)
|
|
273
|
-
if (patterns.configFiles && patterns.configFiles.length > 0) {
|
|
274
|
-
patterns.configFiles.forEach((file) => {
|
|
275
|
-
globs.push(file)
|
|
276
|
-
})
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
// Fallback: if no patterns detected, include all source-like files
|
|
280
|
-
if (globs.length === 0) {
|
|
281
|
-
globs.push('**/*')
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
return globs
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
/**
|
|
288
|
-
* Calculate metrics for context reduction
|
|
289
|
-
*/
|
|
290
|
-
calculateMetrics(originalCount: number, filteredCount: number, startTime: number): Metrics {
|
|
291
|
-
const reduction = originalCount > 0 ? Math.round(((originalCount - filteredCount) / originalCount) * 100) : 0
|
|
292
|
-
|
|
293
|
-
return {
|
|
294
|
-
originalFiles: originalCount,
|
|
295
|
-
filteredFiles: filteredCount,
|
|
296
|
-
reductionPercent: reduction,
|
|
297
|
-
processingTime: Date.now() - startTime,
|
|
298
|
-
effectiveness: reduction > 70 ? 'high' : reduction > 40 ? 'medium' : 'low',
|
|
299
|
-
}
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
/**
|
|
303
|
-
* Check if file exists
|
|
304
|
-
*/
|
|
305
|
-
async fileExists(filePath: string): Promise<boolean> {
|
|
306
|
-
try {
|
|
307
|
-
await fs.access(filePath)
|
|
308
|
-
return true
|
|
309
|
-
} catch {
|
|
310
|
-
return false
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
/**
|
|
315
|
-
* Expand context with related files (tests, styles, etc.)
|
|
316
|
-
*/
|
|
317
|
-
async expandContext(files: string[], _projectPath?: string): Promise<string[]> {
|
|
318
|
-
const expanded = new Set(files)
|
|
319
|
-
|
|
320
|
-
for (const file of files) {
|
|
321
|
-
const ext = path.extname(file)
|
|
322
|
-
const basename = path.basename(file, ext)
|
|
323
|
-
const dirname = path.dirname(file)
|
|
324
|
-
|
|
325
|
-
// 1. Look for test files
|
|
326
|
-
const testPatterns = [
|
|
327
|
-
path.join(dirname, `${basename}.test${ext}`),
|
|
328
|
-
path.join(dirname, `${basename}.spec${ext}`),
|
|
329
|
-
path.join(dirname, '__tests__', `${basename}.test${ext}`),
|
|
330
|
-
path.join(dirname, 'tests', `${basename}.test${ext}`),
|
|
331
|
-
]
|
|
332
|
-
|
|
333
|
-
// 2. Look for style files (for UI components)
|
|
334
|
-
const stylePatterns = [
|
|
335
|
-
path.join(dirname, `${basename}.css`),
|
|
336
|
-
path.join(dirname, `${basename}.scss`),
|
|
337
|
-
path.join(dirname, `${basename}.module.css`),
|
|
338
|
-
path.join(dirname, `${basename}.module.scss`),
|
|
339
|
-
]
|
|
340
|
-
|
|
341
|
-
// Check if these related files exist
|
|
342
|
-
const potentialFiles = [...testPatterns, ...stylePatterns]
|
|
343
|
-
|
|
344
|
-
for (const potential of potentialFiles) {
|
|
345
|
-
if (!expanded.has(potential) && (await this.fileExists(potential))) {
|
|
346
|
-
expanded.add(potential)
|
|
347
|
-
}
|
|
348
|
-
}
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
return Array.from(expanded).sort()
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
/**
|
|
355
|
-
* Get filter statistics
|
|
356
|
-
*/
|
|
357
|
-
getStatistics(): { cachedFiles: number; agentic: boolean } {
|
|
358
|
-
return {
|
|
359
|
-
cachedFiles: this.fileCache.size,
|
|
360
|
-
agentic: true, // All filtering is now agentic, no hardcoded patterns
|
|
361
|
-
}
|
|
362
|
-
}
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
export default ContextFilter
|
|
@@ -1,165 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Parallel Tools
|
|
3
|
-
* Execute multiple tools in parallel for performance
|
|
4
|
-
*
|
|
5
|
-
* @module agentic/parallel-tools
|
|
6
|
-
* @version 1.0.0
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import fs from 'fs/promises'
|
|
10
|
-
|
|
11
|
-
interface ToolCall {
|
|
12
|
-
tool: string
|
|
13
|
-
args: unknown[]
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
interface ToolResult {
|
|
17
|
-
tool: string
|
|
18
|
-
success: boolean
|
|
19
|
-
result?: unknown
|
|
20
|
-
error?: string
|
|
21
|
-
duration: number
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
interface ParallelMetrics {
|
|
25
|
-
totalCalls: number
|
|
26
|
-
parallelCalls: number
|
|
27
|
-
sequentialCalls: number
|
|
28
|
-
averageDuration: number
|
|
29
|
-
savedTime: number
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
class ParallelTools {
|
|
33
|
-
private metrics: ParallelMetrics
|
|
34
|
-
|
|
35
|
-
constructor() {
|
|
36
|
-
this.metrics = {
|
|
37
|
-
totalCalls: 0,
|
|
38
|
-
parallelCalls: 0,
|
|
39
|
-
sequentialCalls: 0,
|
|
40
|
-
averageDuration: 0,
|
|
41
|
-
savedTime: 0,
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* Execute multiple tool calls in parallel
|
|
47
|
-
*/
|
|
48
|
-
async execute(toolCalls: ToolCall[]): Promise<ToolResult[]> {
|
|
49
|
-
const startTime = Date.now()
|
|
50
|
-
|
|
51
|
-
const promises = toolCalls.map(async (call) => {
|
|
52
|
-
const callStart = Date.now()
|
|
53
|
-
try {
|
|
54
|
-
const result = await this.executeOne(call.tool, call.args)
|
|
55
|
-
return {
|
|
56
|
-
tool: call.tool,
|
|
57
|
-
success: true,
|
|
58
|
-
result,
|
|
59
|
-
duration: Date.now() - callStart,
|
|
60
|
-
}
|
|
61
|
-
} catch (error) {
|
|
62
|
-
return {
|
|
63
|
-
tool: call.tool,
|
|
64
|
-
success: false,
|
|
65
|
-
error: (error as Error).message,
|
|
66
|
-
duration: Date.now() - callStart,
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
})
|
|
70
|
-
|
|
71
|
-
const results = await Promise.all(promises)
|
|
72
|
-
|
|
73
|
-
// Update metrics
|
|
74
|
-
const totalDuration = Date.now() - startTime
|
|
75
|
-
const sumIndividual = results.reduce((sum, r) => sum + r.duration, 0)
|
|
76
|
-
this.metrics.totalCalls += toolCalls.length
|
|
77
|
-
this.metrics.parallelCalls += toolCalls.length
|
|
78
|
-
this.metrics.savedTime += sumIndividual - totalDuration
|
|
79
|
-
this.metrics.averageDuration =
|
|
80
|
-
(this.metrics.averageDuration * (this.metrics.totalCalls - toolCalls.length) + totalDuration) /
|
|
81
|
-
this.metrics.totalCalls
|
|
82
|
-
|
|
83
|
-
return results
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
/**
|
|
87
|
-
* Execute a single tool
|
|
88
|
-
*/
|
|
89
|
-
private async executeOne(tool: string, args: unknown[]): Promise<unknown> {
|
|
90
|
-
switch (tool) {
|
|
91
|
-
case 'Read':
|
|
92
|
-
return await fs.readFile(args[0] as string, 'utf-8')
|
|
93
|
-
|
|
94
|
-
case 'Write':
|
|
95
|
-
await fs.writeFile(args[0] as string, args[1] as string, 'utf-8')
|
|
96
|
-
return true
|
|
97
|
-
|
|
98
|
-
case 'GetTimestamp':
|
|
99
|
-
return new Date().toISOString()
|
|
100
|
-
|
|
101
|
-
case 'GetDate':
|
|
102
|
-
return new Date().toISOString().split('T')[0]
|
|
103
|
-
|
|
104
|
-
default:
|
|
105
|
-
throw new Error(`Unknown tool: ${tool}`)
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
/**
|
|
110
|
-
* Read multiple files in parallel
|
|
111
|
-
*/
|
|
112
|
-
async readAll(paths: string[]): Promise<Map<string, string | null>> {
|
|
113
|
-
const results = new Map<string, string | null>()
|
|
114
|
-
|
|
115
|
-
const promises = paths.map(async (path) => {
|
|
116
|
-
try {
|
|
117
|
-
const content = await fs.readFile(path, 'utf-8')
|
|
118
|
-
return { path, content }
|
|
119
|
-
} catch {
|
|
120
|
-
return { path, content: null }
|
|
121
|
-
}
|
|
122
|
-
})
|
|
123
|
-
|
|
124
|
-
const readResults = await Promise.all(promises)
|
|
125
|
-
|
|
126
|
-
readResults.forEach(({ path, content }) => {
|
|
127
|
-
results.set(path, content)
|
|
128
|
-
})
|
|
129
|
-
|
|
130
|
-
return results
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
/**
|
|
134
|
-
* Check if tools can be parallelized
|
|
135
|
-
*/
|
|
136
|
-
canParallelize(tools: string[]): boolean {
|
|
137
|
-
// Read operations can always be parallelized
|
|
138
|
-
const readOnlyTools = ['Read', 'Glob', 'Grep', 'GetTimestamp', 'GetDate']
|
|
139
|
-
return tools.every((tool) => readOnlyTools.includes(tool))
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
/**
|
|
143
|
-
* Get parallelization metrics
|
|
144
|
-
*/
|
|
145
|
-
getMetrics(): ParallelMetrics {
|
|
146
|
-
return { ...this.metrics }
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
/**
|
|
150
|
-
* Reset metrics
|
|
151
|
-
*/
|
|
152
|
-
resetMetrics(): void {
|
|
153
|
-
this.metrics = {
|
|
154
|
-
totalCalls: 0,
|
|
155
|
-
parallelCalls: 0,
|
|
156
|
-
sequentialCalls: 0,
|
|
157
|
-
averageDuration: 0,
|
|
158
|
-
savedTime: 0,
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
const parallelTools = new ParallelTools()
|
|
164
|
-
export default parallelTools
|
|
165
|
-
export { ParallelTools }
|
|
@@ -1,164 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Response Templates
|
|
3
|
-
* Consistent response formatting for commands
|
|
4
|
-
*
|
|
5
|
-
* @module agentic/response-templates
|
|
6
|
-
* @version 1.0.0
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
interface ResponseData {
|
|
10
|
-
task?: string
|
|
11
|
-
duration?: string
|
|
12
|
-
feature?: string
|
|
13
|
-
filesChanged?: number
|
|
14
|
-
keyFile?: string
|
|
15
|
-
nextAction?: string
|
|
16
|
-
message?: string
|
|
17
|
-
ideas?: number
|
|
18
|
-
roadmapFeatures?: number
|
|
19
|
-
pendingTasks?: number
|
|
20
|
-
completedTasks?: number
|
|
21
|
-
error?: string
|
|
22
|
-
[key: string]: unknown
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
interface ResponseTemplate {
|
|
26
|
-
success: (data: ResponseData) => string
|
|
27
|
-
error: (data: ResponseData) => string
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* Response templates by command
|
|
32
|
-
*/
|
|
33
|
-
const templates: Record<string, ResponseTemplate> = {
|
|
34
|
-
done: {
|
|
35
|
-
success: (data) => `✅ ${data.task || 'Task'} (${data.duration || '?'})
|
|
36
|
-
|
|
37
|
-
Files: ${data.filesChanged || 0} | Modified: ${data.keyFile || 'n/a'}
|
|
38
|
-
Next: ${data.nextAction || '/p:next for queue'}`,
|
|
39
|
-
|
|
40
|
-
error: (data) => `❌ Could not complete task
|
|
41
|
-
|
|
42
|
-
${data.error || 'Unknown error'}
|
|
43
|
-
Try: /p:now to check current task`,
|
|
44
|
-
},
|
|
45
|
-
|
|
46
|
-
ship: {
|
|
47
|
-
success: (data) => `🚀 ${data.feature || 'Feature'} shipped!
|
|
48
|
-
|
|
49
|
-
${data.message || 'Successfully deployed'}
|
|
50
|
-
Next: ${data.nextAction || '/p:recap for progress'}`,
|
|
51
|
-
|
|
52
|
-
error: (data) => `❌ Ship failed
|
|
53
|
-
|
|
54
|
-
${data.error || 'Unknown error'}
|
|
55
|
-
Check git status and try again`,
|
|
56
|
-
},
|
|
57
|
-
|
|
58
|
-
now: {
|
|
59
|
-
success: (data) => `🎯 Now working on: ${data.task || 'Task'}
|
|
60
|
-
|
|
61
|
-
${data.message || 'Focus set'}
|
|
62
|
-
When done: /p:done`,
|
|
63
|
-
|
|
64
|
-
error: (data) => `❌ Could not set task
|
|
65
|
-
|
|
66
|
-
${data.error || 'Unknown error'}
|
|
67
|
-
Try: /p:init if project not initialized`,
|
|
68
|
-
},
|
|
69
|
-
|
|
70
|
-
feature: {
|
|
71
|
-
success: (data) => `✨ Feature added: ${data.feature || 'New feature'}
|
|
72
|
-
|
|
73
|
-
${data.message || 'Added to roadmap'}
|
|
74
|
-
Start: /p:now to begin first task`,
|
|
75
|
-
|
|
76
|
-
error: (data) => `❌ Could not add feature
|
|
77
|
-
|
|
78
|
-
${data.error || 'Unknown error'}`,
|
|
79
|
-
},
|
|
80
|
-
|
|
81
|
-
idea: {
|
|
82
|
-
success: (data) => `💡 Idea captured!
|
|
83
|
-
|
|
84
|
-
${data.message || 'Saved to ideas.md'}
|
|
85
|
-
Total ideas: ${data.ideas || '?'}`,
|
|
86
|
-
|
|
87
|
-
error: (data) => `❌ Could not save idea
|
|
88
|
-
|
|
89
|
-
${data.error || 'Unknown error'}`,
|
|
90
|
-
},
|
|
91
|
-
|
|
92
|
-
recap: {
|
|
93
|
-
success: (data) => `📊 Project Recap
|
|
94
|
-
|
|
95
|
-
Features: ${data.roadmapFeatures || 0}
|
|
96
|
-
Pending: ${data.pendingTasks || 0}
|
|
97
|
-
Completed: ${data.completedTasks || 0}
|
|
98
|
-
|
|
99
|
-
${data.message || ''}`,
|
|
100
|
-
|
|
101
|
-
error: (data) => `❌ Could not generate recap
|
|
102
|
-
|
|
103
|
-
${data.error || 'Unknown error'}
|
|
104
|
-
Try: /p:sync to refresh state`,
|
|
105
|
-
},
|
|
106
|
-
|
|
107
|
-
sync: {
|
|
108
|
-
success: (data) => `🔄 Sync complete
|
|
109
|
-
|
|
110
|
-
${data.message || 'Project state updated'}
|
|
111
|
-
Next: ${data.nextAction || '/p:recap for overview'}`,
|
|
112
|
-
|
|
113
|
-
error: (data) => `❌ Sync failed
|
|
114
|
-
|
|
115
|
-
${data.error || 'Unknown error'}
|
|
116
|
-
Try: /p:init to reinitialize`,
|
|
117
|
-
},
|
|
118
|
-
|
|
119
|
-
init: {
|
|
120
|
-
success: (data) => `🎉 Project initialized!
|
|
121
|
-
|
|
122
|
-
${data.message || 'Ready to use prjct'}
|
|
123
|
-
Next: /p:sync to analyze codebase`,
|
|
124
|
-
|
|
125
|
-
error: (data) => `❌ Initialization failed
|
|
126
|
-
|
|
127
|
-
${data.error || 'Unknown error'}
|
|
128
|
-
Check permissions and try again`,
|
|
129
|
-
},
|
|
130
|
-
|
|
131
|
-
default: {
|
|
132
|
-
success: (data) => `✅ Done
|
|
133
|
-
|
|
134
|
-
${data.message || 'Operation completed'}`,
|
|
135
|
-
|
|
136
|
-
error: (data) => `❌ Failed
|
|
137
|
-
|
|
138
|
-
${data.error || 'Unknown error'}`,
|
|
139
|
-
},
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
/**
|
|
143
|
-
* Format response for a command
|
|
144
|
-
*/
|
|
145
|
-
function format(commandName: string, data: ResponseData): string {
|
|
146
|
-
const template = templates[commandName] || templates.default
|
|
147
|
-
const success = data.error ? false : true
|
|
148
|
-
|
|
149
|
-
if (success) {
|
|
150
|
-
return template.success(data)
|
|
151
|
-
} else {
|
|
152
|
-
return template.error(data)
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
/**
|
|
157
|
-
* Get template for command
|
|
158
|
-
*/
|
|
159
|
-
function getTemplate(commandName: string): ResponseTemplate {
|
|
160
|
-
return templates[commandName] || templates.default
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
export { format, getTemplate, templates }
|
|
164
|
-
export default { format, getTemplate, templates }
|