prjct-cli 0.44.1 → 0.45.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 +39 -0
- package/bin/prjct.ts +14 -0
- package/core/commands/analysis.ts +239 -0
- package/core/commands/command-data.ts +21 -4
- package/core/commands/commands.ts +4 -0
- package/core/commands/register.ts +1 -0
- package/core/context-tools/files-tool.ts +584 -0
- package/core/context-tools/imports-tool.ts +423 -0
- package/core/context-tools/index.ts +458 -0
- package/core/context-tools/recent-tool.ts +313 -0
- package/core/context-tools/signatures-tool.ts +510 -0
- package/core/context-tools/summary-tool.ts +309 -0
- package/core/context-tools/token-counter.ts +279 -0
- package/core/context-tools/types.ts +253 -0
- package/core/index.ts +4 -0
- package/core/schemas/metrics.ts +143 -0
- package/core/services/sync-service.ts +99 -0
- package/core/storage/index.ts +4 -0
- package/core/storage/metrics-storage.ts +315 -0
- package/core/types/index.ts +3 -0
- package/core/types/storage.ts +49 -0
- package/dist/bin/prjct.mjs +5362 -2825
- package/dist/core/infrastructure/command-installer.js +10 -32
- package/dist/core/infrastructure/setup.js +10 -32
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,44 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.45.0] - 2026-01-29
|
|
4
|
+
|
|
5
|
+
### Feature: Smart Context Filtering Tools (PRJ-127)
|
|
6
|
+
|
|
7
|
+
Terminal tools for AI agents to explore codebases efficiently **without consuming tokens for filtering**.
|
|
8
|
+
|
|
9
|
+
**New Command: `prjct context <tool>`**
|
|
10
|
+
|
|
11
|
+
| Tool | Purpose | Compression |
|
|
12
|
+
|------|---------|-------------|
|
|
13
|
+
| `files <task>` | Find relevant files for a task | 93% fewer files |
|
|
14
|
+
| `signatures <path>` | Extract code structure only | ~92% token reduction |
|
|
15
|
+
| `imports <file>` | Analyze dependency graphs | - |
|
|
16
|
+
| `recent [commits]` | Find hot files from git | - |
|
|
17
|
+
| `summary <path>` | Intelligent file summary | ~96% token reduction |
|
|
18
|
+
|
|
19
|
+
**Real Cost Savings (multi-model support)**
|
|
20
|
+
|
|
21
|
+
```json
|
|
22
|
+
"cost": {
|
|
23
|
+
"saved": 2.32,
|
|
24
|
+
"byModel": [
|
|
25
|
+
{ "model": "claude-sonnet-4.5", "inputSaved": 0.93, "outputPotential": 1.39 },
|
|
26
|
+
{ "model": "gpt-4o", "inputSaved": 0.77, "outputPotential": 0.93 }
|
|
27
|
+
]
|
|
28
|
+
}
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
**Example: Full codebase signatures**
|
|
32
|
+
```bash
|
|
33
|
+
prjct context signatures core/ --recursive
|
|
34
|
+
# 336K → 26K tokens (92% compression)
|
|
35
|
+
# Saves $2.32/call (Sonnet) or $3.87/call (Opus)
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
**New Files:**
|
|
39
|
+
- `core/context-tools/` - Complete module (7 files)
|
|
40
|
+
- `core/schemas/metrics.ts` - Updated pricing (Jan 2026)
|
|
41
|
+
|
|
3
42
|
## [0.44.1] - 2026-01-29
|
|
4
43
|
|
|
5
44
|
### Fixed: Workflow Templates with Mandatory Steps (PRJ-143)
|
package/bin/prjct.ts
CHANGED
|
@@ -85,6 +85,20 @@ if (args[0] === 'start' || args[0] === 'setup') {
|
|
|
85
85
|
console.error('Server error:', (error as Error).message)
|
|
86
86
|
process.exitCode = 1
|
|
87
87
|
}
|
|
88
|
+
} else if (args[0] === 'context') {
|
|
89
|
+
// Context tools - smart context filtering for AI agents
|
|
90
|
+
const projectPath = process.cwd()
|
|
91
|
+
const projectId = await configManager.getProjectId(projectPath)
|
|
92
|
+
|
|
93
|
+
if (!projectId) {
|
|
94
|
+
console.error('No prjct project found. Run "prjct init" first.')
|
|
95
|
+
process.exitCode = 1
|
|
96
|
+
} else {
|
|
97
|
+
const { runContextTool } = await import('../core/context-tools')
|
|
98
|
+
const result = await runContextTool(args.slice(1), projectId, projectPath)
|
|
99
|
+
console.log(JSON.stringify(result, null, 2))
|
|
100
|
+
process.exitCode = result.tool === 'error' ? 1 : 0
|
|
101
|
+
}
|
|
88
102
|
} else if (args[0] === 'linear') {
|
|
89
103
|
// Linear CLI subcommand - direct access to Linear SDK
|
|
90
104
|
const { spawn } = await import('child_process')
|
|
@@ -18,6 +18,8 @@ import { generateContext } from '../context/generator'
|
|
|
18
18
|
import commandInstaller from '../infrastructure/command-installer'
|
|
19
19
|
import { syncService } from '../services'
|
|
20
20
|
import { showNextSteps } from '../utils/next-steps'
|
|
21
|
+
import { metricsStorage } from '../storage/metrics-storage'
|
|
22
|
+
import { formatCost } from '../schemas/metrics'
|
|
21
23
|
|
|
22
24
|
export class AnalysisCommands extends PrjctCommandsBase {
|
|
23
25
|
/**
|
|
@@ -311,4 +313,241 @@ export class AnalysisCommands extends PrjctCommandsBase {
|
|
|
311
313
|
}
|
|
312
314
|
}
|
|
313
315
|
|
|
316
|
+
/**
|
|
317
|
+
* /p:stats - Value dashboard showing accumulated savings and impact
|
|
318
|
+
*
|
|
319
|
+
* Displays:
|
|
320
|
+
* - Token savings (total, compression rate, estimated cost)
|
|
321
|
+
* - Performance metrics (sync count, avg duration)
|
|
322
|
+
* - Agent usage breakdown
|
|
323
|
+
* - 30-day trend visualization
|
|
324
|
+
*/
|
|
325
|
+
async stats(
|
|
326
|
+
projectPath: string = process.cwd(),
|
|
327
|
+
options: { json?: boolean; export?: boolean } = {}
|
|
328
|
+
): Promise<CommandResult> {
|
|
329
|
+
try {
|
|
330
|
+
const initResult = await this.ensureProjectInit(projectPath)
|
|
331
|
+
if (!initResult.success) return initResult
|
|
332
|
+
|
|
333
|
+
const projectId = await configManager.getProjectId(projectPath)
|
|
334
|
+
if (!projectId) {
|
|
335
|
+
return { success: false, error: 'No project ID found' }
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
// Get metrics summary
|
|
339
|
+
const summary = await metricsStorage.getSummary(projectId)
|
|
340
|
+
const dailyStats = await metricsStorage.getDailyStats(projectId, 30)
|
|
341
|
+
|
|
342
|
+
// JSON output mode
|
|
343
|
+
if (options.json) {
|
|
344
|
+
const jsonOutput = {
|
|
345
|
+
totalTokensSaved: summary.totalTokensSaved,
|
|
346
|
+
estimatedCostSaved: summary.estimatedCostSaved,
|
|
347
|
+
compressionRate: summary.compressionRate,
|
|
348
|
+
syncCount: summary.syncCount,
|
|
349
|
+
avgSyncDuration: summary.avgSyncDuration,
|
|
350
|
+
topAgents: summary.topAgents,
|
|
351
|
+
last30DaysTokens: summary.last30DaysTokens,
|
|
352
|
+
trend: summary.trend,
|
|
353
|
+
dailyStats,
|
|
354
|
+
}
|
|
355
|
+
console.log(JSON.stringify(jsonOutput, null, 2))
|
|
356
|
+
return { success: true, data: jsonOutput }
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
// Get project info for header
|
|
360
|
+
const globalPath = pathManager.getGlobalProjectPath(projectId)
|
|
361
|
+
let projectName = 'Unknown'
|
|
362
|
+
try {
|
|
363
|
+
const fs = require('fs/promises')
|
|
364
|
+
const path = require('path')
|
|
365
|
+
const projectJson = JSON.parse(
|
|
366
|
+
await fs.readFile(path.join(globalPath, 'project.json'), 'utf-8')
|
|
367
|
+
)
|
|
368
|
+
projectName = projectJson.name || 'Unknown'
|
|
369
|
+
} catch {
|
|
370
|
+
// Use fallback
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
// Determine first sync date
|
|
374
|
+
const metricsData = await metricsStorage.read(projectId)
|
|
375
|
+
const firstSyncDate = metricsData.firstSync
|
|
376
|
+
? new Date(metricsData.firstSync).toLocaleDateString('en-US', {
|
|
377
|
+
month: 'short',
|
|
378
|
+
day: 'numeric',
|
|
379
|
+
year: 'numeric',
|
|
380
|
+
})
|
|
381
|
+
: 'N/A'
|
|
382
|
+
|
|
383
|
+
// ASCII Dashboard
|
|
384
|
+
console.log('')
|
|
385
|
+
console.log('╭─────────────────────────────────────────────────╮')
|
|
386
|
+
console.log('│ 📊 prjct-cli Value Dashboard │')
|
|
387
|
+
console.log(`│ Project: ${projectName.padEnd(20).slice(0, 20)} | Since: ${firstSyncDate.padEnd(12).slice(0, 12)} │`)
|
|
388
|
+
console.log('╰─────────────────────────────────────────────────╯')
|
|
389
|
+
console.log('')
|
|
390
|
+
|
|
391
|
+
// Token Savings Section
|
|
392
|
+
console.log('💰 TOKEN SAVINGS')
|
|
393
|
+
console.log(` Total saved: ${this._formatTokens(summary.totalTokensSaved)} tokens`)
|
|
394
|
+
console.log(` Compression: ${(summary.compressionRate * 100).toFixed(0)}% average reduction`)
|
|
395
|
+
console.log(` Estimated cost: ${formatCost(summary.estimatedCostSaved)} saved`)
|
|
396
|
+
console.log('')
|
|
397
|
+
|
|
398
|
+
// Performance Section
|
|
399
|
+
console.log('⚡ PERFORMANCE')
|
|
400
|
+
console.log(` Syncs completed: ${summary.syncCount.toLocaleString()}`)
|
|
401
|
+
console.log(` Avg sync time: ${this._formatDuration(summary.avgSyncDuration)}`)
|
|
402
|
+
console.log('')
|
|
403
|
+
|
|
404
|
+
// Agent Usage Section
|
|
405
|
+
if (summary.topAgents.length > 0) {
|
|
406
|
+
console.log('🤖 AGENT USAGE')
|
|
407
|
+
const totalUsage = summary.topAgents.reduce((sum, a) => sum + a.usageCount, 0)
|
|
408
|
+
for (const agent of summary.topAgents) {
|
|
409
|
+
const pct = totalUsage > 0 ? ((agent.usageCount / totalUsage) * 100).toFixed(0) : 0
|
|
410
|
+
console.log(` ${agent.agentName.padEnd(12)}: ${pct}% (${agent.usageCount} uses)`)
|
|
411
|
+
}
|
|
412
|
+
console.log('')
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
// 30-Day Trend Section
|
|
416
|
+
if (dailyStats.length > 0) {
|
|
417
|
+
console.log('📈 TREND (last 30 days)')
|
|
418
|
+
const sparkline = this._generateSparkline(dailyStats)
|
|
419
|
+
console.log(` ${sparkline} ${this._formatTokens(summary.last30DaysTokens)} tokens saved`)
|
|
420
|
+
|
|
421
|
+
if (summary.trend !== 0) {
|
|
422
|
+
const trendIcon = summary.trend > 0 ? '↑' : '↓'
|
|
423
|
+
const trendSign = summary.trend > 0 ? '+' : ''
|
|
424
|
+
console.log(` ${trendIcon} ${trendSign}${summary.trend.toFixed(0)}% vs previous 30 days`)
|
|
425
|
+
}
|
|
426
|
+
console.log('')
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
// Footer
|
|
430
|
+
console.log('───────────────────────────────────────────────────')
|
|
431
|
+
console.log(`Export: prjct stats --export > stats.md`)
|
|
432
|
+
console.log('')
|
|
433
|
+
|
|
434
|
+
// Export mode - return markdown
|
|
435
|
+
if (options.export) {
|
|
436
|
+
const markdown = this._generateStatsMarkdown(summary, dailyStats, projectName, firstSyncDate)
|
|
437
|
+
console.log(markdown)
|
|
438
|
+
return { success: true, data: { markdown } }
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
return {
|
|
442
|
+
success: true,
|
|
443
|
+
data: summary,
|
|
444
|
+
}
|
|
445
|
+
} catch (error) {
|
|
446
|
+
console.error('❌ Error:', (error as Error).message)
|
|
447
|
+
return { success: false, error: (error as Error).message }
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
// =========== Stats Helper Methods ===========
|
|
452
|
+
|
|
453
|
+
private _formatTokens(tokens: number): string {
|
|
454
|
+
if (tokens >= 1_000_000) {
|
|
455
|
+
return `${(tokens / 1_000_000).toFixed(1)}M`
|
|
456
|
+
}
|
|
457
|
+
if (tokens >= 1_000) {
|
|
458
|
+
return `${(tokens / 1_000).toFixed(1)}K`
|
|
459
|
+
}
|
|
460
|
+
return tokens.toLocaleString()
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
private _formatDuration(ms: number): string {
|
|
464
|
+
if (ms < 1000) {
|
|
465
|
+
return `${Math.round(ms)}ms`
|
|
466
|
+
}
|
|
467
|
+
return `${(ms / 1000).toFixed(1)}s`
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
private _generateSparkline(dailyStats: { tokensSaved: number }[]): string {
|
|
471
|
+
if (dailyStats.length === 0) return ''
|
|
472
|
+
|
|
473
|
+
const chars = '▁▂▃▄▅▆▇█'
|
|
474
|
+
const values = dailyStats.map((d) => d.tokensSaved)
|
|
475
|
+
const max = Math.max(...values, 1)
|
|
476
|
+
|
|
477
|
+
return values
|
|
478
|
+
.map((v) => {
|
|
479
|
+
const idx = Math.min(Math.floor((v / max) * (chars.length - 1)), chars.length - 1)
|
|
480
|
+
return chars[idx]
|
|
481
|
+
})
|
|
482
|
+
.join('')
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
private _generateStatsMarkdown(
|
|
486
|
+
summary: {
|
|
487
|
+
totalTokensSaved: number
|
|
488
|
+
estimatedCostSaved: number
|
|
489
|
+
compressionRate: number
|
|
490
|
+
syncCount: number
|
|
491
|
+
avgSyncDuration: number
|
|
492
|
+
topAgents: { agentName: string; usageCount: number }[]
|
|
493
|
+
last30DaysTokens: number
|
|
494
|
+
trend: number
|
|
495
|
+
},
|
|
496
|
+
dailyStats: { date: string; tokensSaved: number; syncs: number }[],
|
|
497
|
+
projectName: string,
|
|
498
|
+
firstSyncDate: string
|
|
499
|
+
): string {
|
|
500
|
+
const lines: string[] = []
|
|
501
|
+
|
|
502
|
+
lines.push(`# ${projectName} - Value Dashboard`)
|
|
503
|
+
lines.push('')
|
|
504
|
+
lines.push(`_Generated: ${new Date().toLocaleString()} | Tracking since: ${firstSyncDate}_`)
|
|
505
|
+
lines.push('')
|
|
506
|
+
|
|
507
|
+
lines.push('## 💰 Token Savings')
|
|
508
|
+
lines.push('')
|
|
509
|
+
lines.push(`| Metric | Value |`)
|
|
510
|
+
lines.push(`|--------|-------|`)
|
|
511
|
+
lines.push(`| Total saved | ${this._formatTokens(summary.totalTokensSaved)} tokens |`)
|
|
512
|
+
lines.push(`| Compression | ${(summary.compressionRate * 100).toFixed(0)}% |`)
|
|
513
|
+
lines.push(`| Cost saved | ${formatCost(summary.estimatedCostSaved)} |`)
|
|
514
|
+
lines.push('')
|
|
515
|
+
|
|
516
|
+
lines.push('## ⚡ Performance')
|
|
517
|
+
lines.push('')
|
|
518
|
+
lines.push(`| Metric | Value |`)
|
|
519
|
+
lines.push(`|--------|-------|`)
|
|
520
|
+
lines.push(`| Syncs | ${summary.syncCount} |`)
|
|
521
|
+
lines.push(`| Avg time | ${this._formatDuration(summary.avgSyncDuration)} |`)
|
|
522
|
+
lines.push('')
|
|
523
|
+
|
|
524
|
+
if (summary.topAgents.length > 0) {
|
|
525
|
+
lines.push('## 🤖 Agent Usage')
|
|
526
|
+
lines.push('')
|
|
527
|
+
lines.push(`| Agent | Usage |`)
|
|
528
|
+
lines.push(`|-------|-------|`)
|
|
529
|
+
const totalUsage = summary.topAgents.reduce((sum, a) => sum + a.usageCount, 0)
|
|
530
|
+
for (const agent of summary.topAgents) {
|
|
531
|
+
const pct = totalUsage > 0 ? ((agent.usageCount / totalUsage) * 100).toFixed(0) : 0
|
|
532
|
+
lines.push(`| ${agent.agentName} | ${pct}% (${agent.usageCount}) |`)
|
|
533
|
+
}
|
|
534
|
+
lines.push('')
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
lines.push('## 📈 30-Day Trend')
|
|
538
|
+
lines.push('')
|
|
539
|
+
lines.push(`- Tokens saved: ${this._formatTokens(summary.last30DaysTokens)}`)
|
|
540
|
+
if (summary.trend !== 0) {
|
|
541
|
+
const trendSign = summary.trend > 0 ? '+' : ''
|
|
542
|
+
lines.push(`- Trend: ${trendSign}${summary.trend.toFixed(0)}% vs previous period`)
|
|
543
|
+
}
|
|
544
|
+
lines.push('')
|
|
545
|
+
|
|
546
|
+
lines.push('---')
|
|
547
|
+
lines.push('')
|
|
548
|
+
lines.push('_Generated with [prjct-cli](https://prjct.app)_')
|
|
549
|
+
|
|
550
|
+
return lines.join('\n')
|
|
551
|
+
}
|
|
552
|
+
|
|
314
553
|
}
|
|
@@ -142,6 +142,17 @@ export const COMMANDS: CommandMeta[] = [
|
|
|
142
142
|
hasTemplate: true,
|
|
143
143
|
requiresProject: true,
|
|
144
144
|
},
|
|
145
|
+
{
|
|
146
|
+
name: 'stats',
|
|
147
|
+
group: 'core',
|
|
148
|
+
description: 'Value dashboard - token savings, performance, and impact',
|
|
149
|
+
usage: { claude: '/p:stats', terminal: 'prjct stats' },
|
|
150
|
+
params: '[--json] [--export]',
|
|
151
|
+
implemented: true,
|
|
152
|
+
hasTemplate: true,
|
|
153
|
+
requiresProject: true,
|
|
154
|
+
features: ['Token savings tracking', 'Compression metrics', 'Cost estimates', '30-day trends'],
|
|
155
|
+
},
|
|
145
156
|
{
|
|
146
157
|
name: 'sync',
|
|
147
158
|
group: 'core',
|
|
@@ -308,13 +319,19 @@ export const COMMANDS: CommandMeta[] = [
|
|
|
308
319
|
{
|
|
309
320
|
name: 'context',
|
|
310
321
|
group: 'setup',
|
|
311
|
-
description: '
|
|
312
|
-
usage: { claude: null, terminal: 'prjct context <
|
|
313
|
-
params: '<
|
|
322
|
+
description: 'Smart context filtering tools for AI agents',
|
|
323
|
+
usage: { claude: null, terminal: 'prjct context <tool> [args]' },
|
|
324
|
+
params: '<tool> [args]',
|
|
314
325
|
implemented: true,
|
|
315
326
|
hasTemplate: false,
|
|
316
327
|
requiresProject: true,
|
|
317
|
-
features: [
|
|
328
|
+
features: [
|
|
329
|
+
'files - Find relevant files for a task',
|
|
330
|
+
'signatures - Extract code structure (~90% compression)',
|
|
331
|
+
'imports - Analyze dependency graphs',
|
|
332
|
+
'recent - Find hot files from git history',
|
|
333
|
+
'summary - Intelligent file summarization',
|
|
334
|
+
],
|
|
318
335
|
},
|
|
319
336
|
]
|
|
320
337
|
|
|
@@ -158,6 +158,10 @@ class PrjctCommands {
|
|
|
158
158
|
return this.analysis.sync(projectPath, options)
|
|
159
159
|
}
|
|
160
160
|
|
|
161
|
+
async stats(projectPath: string = process.cwd(), options: { json?: boolean; export?: boolean } = {}): Promise<CommandResult> {
|
|
162
|
+
return this.analysis.stats(projectPath, options)
|
|
163
|
+
}
|
|
164
|
+
|
|
161
165
|
// ========== Context Commands ==========
|
|
162
166
|
|
|
163
167
|
async context(input: string | null = null, projectPath: string = process.cwd()): Promise<CommandResult> {
|
|
@@ -81,6 +81,7 @@ export function registerAllCommands(): void {
|
|
|
81
81
|
// Analysis commands
|
|
82
82
|
commandRegistry.registerMethod('analyze', analysis, 'analyze', getMeta('analyze'))
|
|
83
83
|
commandRegistry.registerMethod('sync', analysis, 'sync', getMeta('sync'))
|
|
84
|
+
commandRegistry.registerMethod('stats', analysis, 'stats', getMeta('stats'))
|
|
84
85
|
|
|
85
86
|
// Setup commands
|
|
86
87
|
commandRegistry.registerMethod('start', setup, 'start', getMeta('start'))
|