prjct-cli 0.60.0 → 0.60.1
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 +38 -0
- package/core/commands/analysis.ts +72 -64
- package/dist/bin/prjct.mjs +37 -56
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,43 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.60.1] - 2026-02-05
|
|
4
|
+
|
|
5
|
+
### Bug Fixes
|
|
6
|
+
|
|
7
|
+
- improve sync output with summary-first format (PRJ-100) (#100)
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
## [0.60.1] - 2026-02-05
|
|
11
|
+
|
|
12
|
+
### Improved
|
|
13
|
+
|
|
14
|
+
- **Sync output UX (PRJ-100)**: Summary-first format with key metrics prominent, ~50% less output
|
|
15
|
+
|
|
16
|
+
### Implementation Details
|
|
17
|
+
|
|
18
|
+
Refactored `showSyncResult()` in `core/commands/analysis.ts` to show success line with timing immediately, followed by single-line metrics (files → context | agents | reduction). Removed redundant bottom summary section. Fixed compressionRate calculation (was decimal, now percentage). Added conditional display for low-value metrics (only show reduction if >10%). Fixed pluralization ("1 skill" not "1 skills").
|
|
19
|
+
|
|
20
|
+
### Learnings
|
|
21
|
+
|
|
22
|
+
- `syncMetrics.compressionRate` is a decimal (0-1), not percentage - multiply by 100
|
|
23
|
+
- Summary-first output pattern improves scannability
|
|
24
|
+
- Conditional metric display reduces noise for low-value data
|
|
25
|
+
|
|
26
|
+
### Test Plan
|
|
27
|
+
|
|
28
|
+
#### For QA
|
|
29
|
+
1. Run `prjct sync --yes` on any project
|
|
30
|
+
2. Verify output shows success line first with timing: `✅ Synced {project} ({time}s)`
|
|
31
|
+
3. Verify single-line metrics: `{files} files → {context} context | {agents} agents`
|
|
32
|
+
4. Verify compression rate only shows if > 10%
|
|
33
|
+
5. Verify pluralization is correct ("1 skill" not "1 skills")
|
|
34
|
+
|
|
35
|
+
#### For Users
|
|
36
|
+
- Sync output is now more scannable - key metrics appear first instead of buried at bottom
|
|
37
|
+
- Run `p. sync` as usual - new format is automatic
|
|
38
|
+
- No breaking changes
|
|
39
|
+
|
|
40
|
+
|
|
3
41
|
## [0.60.0] - 2026-02-05
|
|
4
42
|
|
|
5
43
|
### Features
|
|
@@ -455,89 +455,97 @@ export class AnalysisCommands extends PrjctCommandsBase {
|
|
|
455
455
|
|
|
456
456
|
/**
|
|
457
457
|
* Display sync results (extracted to avoid duplication)
|
|
458
|
+
*
|
|
459
|
+
* UX Design (PRJ-100):
|
|
460
|
+
* - Summary first: success + key metrics on first lines
|
|
461
|
+
* - Scannable: single-line metrics, minimal vertical space
|
|
462
|
+
* - Changes focused: show what changed, not everything that exists
|
|
463
|
+
* - Next steps prominent: clear call to action at bottom
|
|
458
464
|
*/
|
|
459
465
|
private async showSyncResult(
|
|
460
466
|
result: Awaited<ReturnType<typeof syncService.sync>>,
|
|
461
467
|
startTime: number
|
|
462
468
|
): Promise<CommandResult> {
|
|
463
|
-
|
|
464
|
-
const
|
|
465
|
-
|
|
466
|
-
|
|
469
|
+
const elapsed = Date.now() - startTime
|
|
470
|
+
const contextFilesCount =
|
|
471
|
+
result.contextFiles.length + (result.aiTools?.filter((t) => t.success).length || 0)
|
|
472
|
+
const agentCount = result.agents.length
|
|
473
|
+
const domainAgentCount = result.agents.filter((a) => a.type === 'domain').length
|
|
474
|
+
|
|
475
|
+
// Update global config (silent - don't clutter output)
|
|
476
|
+
await commandInstaller.installGlobalConfig()
|
|
477
|
+
|
|
478
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
479
|
+
// SUCCESS LINE - Immediate confirmation with timing
|
|
480
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
481
|
+
console.log(`✅ Synced ${result.stats.name || 'project'} (${(elapsed / 1000).toFixed(1)}s)\n`)
|
|
482
|
+
|
|
483
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
484
|
+
// KEY METRICS - Single scannable line
|
|
485
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
486
|
+
// Only show compression rate if meaningful (> 10%)
|
|
487
|
+
const compressionPct = result.syncMetrics?.compressionRate
|
|
488
|
+
? Math.round(result.syncMetrics.compressionRate * 100)
|
|
489
|
+
: 0
|
|
490
|
+
const metricsLine = [
|
|
491
|
+
`${result.stats.fileCount} files → ${contextFilesCount} context`,
|
|
492
|
+
`${agentCount} agents`,
|
|
493
|
+
compressionPct > 10 ? `${compressionPct}% reduction` : null,
|
|
494
|
+
]
|
|
495
|
+
.filter(Boolean)
|
|
496
|
+
.join(' | ')
|
|
497
|
+
console.log(metricsLine)
|
|
498
|
+
|
|
499
|
+
// Stack and branch info
|
|
500
|
+
const framework = result.stats.frameworks.length > 0 ? ` (${result.stats.frameworks[0]})` : ''
|
|
501
|
+
console.log(`Stack: ${result.stats.ecosystem}${framework} | Branch: ${result.git.branch}\n`)
|
|
502
|
+
|
|
503
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
504
|
+
// CHANGES SECTION - What was generated/updated
|
|
505
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
506
|
+
console.log('Generated:')
|
|
507
|
+
|
|
508
|
+
// Context files (condensed)
|
|
509
|
+
if (result.contextFiles.length > 0) {
|
|
510
|
+
console.log(` ✓ ${result.contextFiles.length} context files`)
|
|
467
511
|
}
|
|
468
512
|
|
|
469
|
-
//
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
console.log(`├── Commits: ${result.git.commits}`)
|
|
475
|
-
console.log(`├── Version: ${result.stats.version}`)
|
|
476
|
-
console.log(`└── Stack: ${result.stats.ecosystem}\n`)
|
|
477
|
-
|
|
478
|
-
console.log('🌿 Git Status')
|
|
479
|
-
console.log(`├── Branch: ${result.git.branch}`)
|
|
480
|
-
console.log(`├── Uncommitted: ${result.git.hasChanges ? 'Yes' : 'Clean'}`)
|
|
481
|
-
console.log(`└── Recent: ${result.git.weeklyCommits} commits this week\n`)
|
|
482
|
-
|
|
483
|
-
console.log('📁 Context Updated')
|
|
484
|
-
for (const file of result.contextFiles) {
|
|
485
|
-
console.log(`├── ${file}`)
|
|
513
|
+
// AI tools
|
|
514
|
+
const successTools = result.aiTools?.filter((t) => t.success) || []
|
|
515
|
+
if (successTools.length > 0) {
|
|
516
|
+
const toolNames = successTools.map((t) => t.toolId).join(', ')
|
|
517
|
+
console.log(` ✓ AI tools: ${toolNames}`)
|
|
486
518
|
}
|
|
487
|
-
console.log('')
|
|
488
519
|
|
|
489
|
-
//
|
|
490
|
-
if (
|
|
491
|
-
const
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
}
|
|
497
|
-
console.log('')
|
|
520
|
+
// Agents (show count with breakdown)
|
|
521
|
+
if (agentCount > 0) {
|
|
522
|
+
const agentSummary =
|
|
523
|
+
domainAgentCount > 0
|
|
524
|
+
? `${agentCount} agents (${domainAgentCount} domain)`
|
|
525
|
+
: `${agentCount} agents`
|
|
526
|
+
console.log(` ✓ ${agentSummary}`)
|
|
498
527
|
}
|
|
499
528
|
|
|
500
|
-
|
|
501
|
-
const domainAgents = result.agents.filter((a) => a.type === 'domain').map((a) => a.name)
|
|
502
|
-
|
|
503
|
-
console.log(`🤖 Agents Regenerated (${result.agents.length})`)
|
|
504
|
-
console.log(`├── Workflow: ${workflowAgents.join(', ')}`)
|
|
505
|
-
console.log(`└── Domain: ${domainAgents.join(', ') || 'none'}\n`)
|
|
506
|
-
|
|
529
|
+
// Skills
|
|
507
530
|
if (result.skills.length > 0) {
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
console.log(`├── ${skill.agent}.md → ${skill.skill}`)
|
|
511
|
-
}
|
|
512
|
-
console.log('')
|
|
531
|
+
const skillWord = result.skills.length === 1 ? 'skill' : 'skills'
|
|
532
|
+
console.log(` ✓ ${result.skills.length} ${skillWord}`)
|
|
513
533
|
}
|
|
514
534
|
|
|
535
|
+
console.log('')
|
|
536
|
+
|
|
537
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
538
|
+
// STATUS INDICATOR - Repository state
|
|
539
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
515
540
|
if (result.git.hasChanges) {
|
|
516
|
-
console.log('⚠️
|
|
517
|
-
} else {
|
|
518
|
-
console.log('✨ Repository is clean!\n')
|
|
541
|
+
console.log('⚠️ Uncommitted changes detected\n')
|
|
519
542
|
}
|
|
520
543
|
|
|
544
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
545
|
+
// NEXT STEPS - Clear call to action
|
|
546
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
521
547
|
showNextSteps('sync')
|
|
522
548
|
|
|
523
|
-
// Summary metrics
|
|
524
|
-
const elapsed = Date.now() - startTime
|
|
525
|
-
const contextFilesCount =
|
|
526
|
-
result.contextFiles.length + (result.aiTools?.filter((t) => t.success).length || 0)
|
|
527
|
-
const agentCount = result.agents.length
|
|
528
|
-
|
|
529
|
-
console.log('─'.repeat(45))
|
|
530
|
-
console.log('📊 Sync Summary')
|
|
531
|
-
console.log(
|
|
532
|
-
` Stack: ${result.stats.ecosystem} (${result.stats.frameworks.join(', ') || 'no frameworks'})`
|
|
533
|
-
)
|
|
534
|
-
console.log(` Files: ${result.stats.fileCount} analyzed → ${contextFilesCount} context files`)
|
|
535
|
-
console.log(
|
|
536
|
-
` Agents: ${agentCount} (${result.agents.filter((a) => a.type === 'domain').length} domain)`
|
|
537
|
-
)
|
|
538
|
-
console.log(` Time: ${(elapsed / 1000).toFixed(1)}s`)
|
|
539
|
-
console.log('')
|
|
540
|
-
|
|
541
549
|
return {
|
|
542
550
|
success: true,
|
|
543
551
|
data: result,
|
package/dist/bin/prjct.mjs
CHANGED
|
@@ -18093,72 +18093,53 @@ ${formatFullDiff(diff)}`);
|
|
|
18093
18093
|
}
|
|
18094
18094
|
/**
|
|
18095
18095
|
* Display sync results (extracted to avoid duplication)
|
|
18096
|
+
*
|
|
18097
|
+
* UX Design (PRJ-100):
|
|
18098
|
+
* - Summary first: success + key metrics on first lines
|
|
18099
|
+
* - Scannable: single-line metrics, minimal vertical space
|
|
18100
|
+
* - Changes focused: show what changed, not everything that exists
|
|
18101
|
+
* - Next steps prominent: clear call to action at bottom
|
|
18096
18102
|
*/
|
|
18097
18103
|
async showSyncResult(result, startTime) {
|
|
18098
|
-
const
|
|
18099
|
-
|
|
18100
|
-
|
|
18101
|
-
|
|
18102
|
-
|
|
18103
|
-
|
|
18104
|
-
console.log("\u{1F4CA} Project Stats");
|
|
18105
|
-
console.log(`\u251C\u2500\u2500 Files: ~${result.stats.fileCount}`);
|
|
18106
|
-
console.log(`\u251C\u2500\u2500 Commits: ${result.git.commits}`);
|
|
18107
|
-
console.log(`\u251C\u2500\u2500 Version: ${result.stats.version}`);
|
|
18108
|
-
console.log(`\u2514\u2500\u2500 Stack: ${result.stats.ecosystem}
|
|
18104
|
+
const elapsed = Date.now() - startTime;
|
|
18105
|
+
const contextFilesCount = result.contextFiles.length + (result.aiTools?.filter((t) => t.success).length || 0);
|
|
18106
|
+
const agentCount = result.agents.length;
|
|
18107
|
+
const domainAgentCount = result.agents.filter((a) => a.type === "domain").length;
|
|
18108
|
+
await command_installer_default.installGlobalConfig();
|
|
18109
|
+
console.log(`\u2705 Synced ${result.stats.name || "project"} (${(elapsed / 1e3).toFixed(1)}s)
|
|
18109
18110
|
`);
|
|
18110
|
-
|
|
18111
|
-
|
|
18112
|
-
|
|
18113
|
-
|
|
18111
|
+
const compressionPct = result.syncMetrics?.compressionRate ? Math.round(result.syncMetrics.compressionRate * 100) : 0;
|
|
18112
|
+
const metricsLine = [
|
|
18113
|
+
`${result.stats.fileCount} files \u2192 ${contextFilesCount} context`,
|
|
18114
|
+
`${agentCount} agents`,
|
|
18115
|
+
compressionPct > 10 ? `${compressionPct}% reduction` : null
|
|
18116
|
+
].filter(Boolean).join(" | ");
|
|
18117
|
+
console.log(metricsLine);
|
|
18118
|
+
const framework = result.stats.frameworks.length > 0 ? ` (${result.stats.frameworks[0]})` : "";
|
|
18119
|
+
console.log(`Stack: ${result.stats.ecosystem}${framework} | Branch: ${result.git.branch}
|
|
18114
18120
|
`);
|
|
18115
|
-
console.log("
|
|
18116
|
-
|
|
18117
|
-
console.log(
|
|
18121
|
+
console.log("Generated:");
|
|
18122
|
+
if (result.contextFiles.length > 0) {
|
|
18123
|
+
console.log(` \u2713 ${result.contextFiles.length} context files`);
|
|
18118
18124
|
}
|
|
18119
|
-
|
|
18120
|
-
if (
|
|
18121
|
-
const
|
|
18122
|
-
console.log(
|
|
18123
|
-
|
|
18124
|
-
|
|
18125
|
-
|
|
18126
|
-
}
|
|
18127
|
-
console.log("");
|
|
18125
|
+
const successTools = result.aiTools?.filter((t) => t.success) || [];
|
|
18126
|
+
if (successTools.length > 0) {
|
|
18127
|
+
const toolNames = successTools.map((t) => t.toolId).join(", ");
|
|
18128
|
+
console.log(` \u2713 AI tools: ${toolNames}`);
|
|
18129
|
+
}
|
|
18130
|
+
if (agentCount > 0) {
|
|
18131
|
+
const agentSummary = domainAgentCount > 0 ? `${agentCount} agents (${domainAgentCount} domain)` : `${agentCount} agents`;
|
|
18132
|
+
console.log(` \u2713 ${agentSummary}`);
|
|
18128
18133
|
}
|
|
18129
|
-
const workflowAgents = result.agents.filter((a) => a.type === "workflow").map((a) => a.name);
|
|
18130
|
-
const domainAgents = result.agents.filter((a) => a.type === "domain").map((a) => a.name);
|
|
18131
|
-
console.log(`\u{1F916} Agents Regenerated (${result.agents.length})`);
|
|
18132
|
-
console.log(`\u251C\u2500\u2500 Workflow: ${workflowAgents.join(", ")}`);
|
|
18133
|
-
console.log(`\u2514\u2500\u2500 Domain: ${domainAgents.join(", ") || "none"}
|
|
18134
|
-
`);
|
|
18135
18134
|
if (result.skills.length > 0) {
|
|
18136
|
-
|
|
18137
|
-
|
|
18138
|
-
console.log(`\u251C\u2500\u2500 ${skill.agent}.md \u2192 ${skill.skill}`);
|
|
18139
|
-
}
|
|
18140
|
-
console.log("");
|
|
18135
|
+
const skillWord = result.skills.length === 1 ? "skill" : "skills";
|
|
18136
|
+
console.log(` \u2713 ${result.skills.length} ${skillWord}`);
|
|
18141
18137
|
}
|
|
18138
|
+
console.log("");
|
|
18142
18139
|
if (result.git.hasChanges) {
|
|
18143
|
-
console.log("\u26A0\uFE0F
|
|
18144
|
-
} else {
|
|
18145
|
-
console.log("\u2728 Repository is clean!\n");
|
|
18140
|
+
console.log("\u26A0\uFE0F Uncommitted changes detected\n");
|
|
18146
18141
|
}
|
|
18147
18142
|
showNextSteps("sync");
|
|
18148
|
-
const elapsed = Date.now() - startTime;
|
|
18149
|
-
const contextFilesCount = result.contextFiles.length + (result.aiTools?.filter((t) => t.success).length || 0);
|
|
18150
|
-
const agentCount = result.agents.length;
|
|
18151
|
-
console.log("\u2500".repeat(45));
|
|
18152
|
-
console.log("\u{1F4CA} Sync Summary");
|
|
18153
|
-
console.log(
|
|
18154
|
-
` Stack: ${result.stats.ecosystem} (${result.stats.frameworks.join(", ") || "no frameworks"})`
|
|
18155
|
-
);
|
|
18156
|
-
console.log(` Files: ${result.stats.fileCount} analyzed \u2192 ${contextFilesCount} context files`);
|
|
18157
|
-
console.log(
|
|
18158
|
-
` Agents: ${agentCount} (${result.agents.filter((a) => a.type === "domain").length} domain)`
|
|
18159
|
-
);
|
|
18160
|
-
console.log(` Time: ${(elapsed / 1e3).toFixed(1)}s`);
|
|
18161
|
-
console.log("");
|
|
18162
18143
|
return {
|
|
18163
18144
|
success: true,
|
|
18164
18145
|
data: result,
|
|
@@ -26431,7 +26412,7 @@ var require_package = __commonJS({
|
|
|
26431
26412
|
"package.json"(exports, module) {
|
|
26432
26413
|
module.exports = {
|
|
26433
26414
|
name: "prjct-cli",
|
|
26434
|
-
version: "0.60.
|
|
26415
|
+
version: "0.60.1",
|
|
26435
26416
|
description: "Context layer for AI agents. Project context for Claude Code, Gemini CLI, and more.",
|
|
26436
26417
|
main: "core/index.ts",
|
|
26437
26418
|
bin: {
|