prjct-cli 0.52.0 → 0.54.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 +83 -0
- package/core/agentic/memory-system.ts +216 -0
- package/core/agentic/prompt-builder.ts +87 -13
- package/core/commands/analysis.ts +167 -10
- package/core/infrastructure/config-manager.ts +24 -0
- package/core/services/memory-service.ts +21 -0
- package/core/types/config.ts +6 -0
- package/core/types/memory.ts +70 -0
- package/core/utils/output.ts +2 -2
- package/dist/bin/prjct.mjs +378 -26
- package/package.json +1 -1
|
@@ -5,11 +5,12 @@
|
|
|
5
5
|
import fs from 'node:fs/promises'
|
|
6
6
|
import path from 'node:path'
|
|
7
7
|
import prompts from 'prompts'
|
|
8
|
+
import memorySystem from '../agentic/memory-system'
|
|
8
9
|
import { generateContext } from '../context/generator'
|
|
9
10
|
import analyzer from '../domain/analyzer'
|
|
10
11
|
import commandInstaller from '../infrastructure/command-installer'
|
|
11
12
|
import { formatCost } from '../schemas/metrics'
|
|
12
|
-
import { syncService } from '../services'
|
|
13
|
+
import { memoryService, syncService } from '../services'
|
|
13
14
|
import { formatDiffPreview, formatFullDiff, generateSyncDiff } from '../services/diff-generator'
|
|
14
15
|
import { metricsStorage } from '../storage/metrics-storage'
|
|
15
16
|
import type { AnalyzeOptions, CommandResult, ProjectContext } from '../types'
|
|
@@ -439,13 +440,17 @@ export class AnalysisCommands extends PrjctCommandsBase {
|
|
|
439
440
|
}
|
|
440
441
|
|
|
441
442
|
/**
|
|
442
|
-
* /p:stats -
|
|
443
|
+
* /p:stats - Session summary and value dashboard
|
|
443
444
|
*
|
|
444
445
|
* Displays:
|
|
446
|
+
* - Session activity (tasks completed, features shipped today)
|
|
447
|
+
* - Patterns learned (from memory system)
|
|
445
448
|
* - Token savings (total, compression rate, estimated cost)
|
|
446
449
|
* - Performance metrics (sync count, avg duration)
|
|
447
450
|
* - Agent usage breakdown
|
|
448
451
|
* - 30-day trend visualization
|
|
452
|
+
*
|
|
453
|
+
* @see PRJ-89
|
|
449
454
|
*/
|
|
450
455
|
async stats(
|
|
451
456
|
projectPath: string = process.cwd(),
|
|
@@ -464,9 +469,17 @@ export class AnalysisCommands extends PrjctCommandsBase {
|
|
|
464
469
|
const summary = await metricsStorage.getSummary(projectId)
|
|
465
470
|
const dailyStats = await metricsStorage.getDailyStats(projectId, 30)
|
|
466
471
|
|
|
472
|
+
// Get session activity (today's events)
|
|
473
|
+
const sessionActivity = await this._getSessionActivity(projectId)
|
|
474
|
+
|
|
475
|
+
// Get learned patterns
|
|
476
|
+
const patternsSummary = await memorySystem.getPatternsSummary(projectId)
|
|
477
|
+
|
|
467
478
|
// JSON output mode
|
|
468
479
|
if (options.json) {
|
|
469
480
|
const jsonOutput = {
|
|
481
|
+
session: sessionActivity,
|
|
482
|
+
patterns: patternsSummary,
|
|
470
483
|
totalTokensSaved: summary.totalTokensSaved,
|
|
471
484
|
estimatedCostSaved: summary.estimatedCostSaved,
|
|
472
485
|
compressionRate: summary.compressionRate,
|
|
@@ -485,8 +498,6 @@ export class AnalysisCommands extends PrjctCommandsBase {
|
|
|
485
498
|
const globalPath = pathManager.getGlobalProjectPath(projectId)
|
|
486
499
|
let projectName = 'Unknown'
|
|
487
500
|
try {
|
|
488
|
-
const fs = require('node:fs/promises')
|
|
489
|
-
const path = require('node:path')
|
|
490
501
|
const projectJson = JSON.parse(
|
|
491
502
|
await fs.readFile(path.join(globalPath, 'project.json'), 'utf-8')
|
|
492
503
|
)
|
|
@@ -508,13 +519,40 @@ export class AnalysisCommands extends PrjctCommandsBase {
|
|
|
508
519
|
// ASCII Dashboard
|
|
509
520
|
console.log('')
|
|
510
521
|
console.log('╭─────────────────────────────────────────────────╮')
|
|
511
|
-
console.log('│ 📊 prjct-cli
|
|
522
|
+
console.log('│ 📊 prjct-cli Stats Dashboard │')
|
|
512
523
|
console.log(
|
|
513
524
|
`│ Project: ${projectName.padEnd(20).slice(0, 20)} | Since: ${firstSyncDate.padEnd(12).slice(0, 12)} │`
|
|
514
525
|
)
|
|
515
526
|
console.log('╰─────────────────────────────────────────────────╯')
|
|
516
527
|
console.log('')
|
|
517
528
|
|
|
529
|
+
// Session Activity Section (PRJ-89)
|
|
530
|
+
console.log("🎯 TODAY'S ACTIVITY")
|
|
531
|
+
if (sessionActivity.sessionDuration) {
|
|
532
|
+
console.log(` Duration: ${sessionActivity.sessionDuration}`)
|
|
533
|
+
}
|
|
534
|
+
console.log(` Tasks completed: ${sessionActivity.tasksCompleted}`)
|
|
535
|
+
console.log(` Features shipped: ${sessionActivity.featuresShipped}`)
|
|
536
|
+
if (sessionActivity.agentsUsed.length > 0) {
|
|
537
|
+
const agentStr = sessionActivity.agentsUsed
|
|
538
|
+
.slice(0, 3)
|
|
539
|
+
.map((a) => `${a.name} (${a.count}×)`)
|
|
540
|
+
.join(', ')
|
|
541
|
+
console.log(` Agents used: ${agentStr}`)
|
|
542
|
+
}
|
|
543
|
+
console.log('')
|
|
544
|
+
|
|
545
|
+
// Learned Patterns Section (PRJ-89)
|
|
546
|
+
if (patternsSummary.decisions > 0 || patternsSummary.preferences > 0) {
|
|
547
|
+
console.log('🧠 PATTERNS LEARNED')
|
|
548
|
+
console.log(
|
|
549
|
+
` Decisions: ${patternsSummary.learnedDecisions} confirmed (${patternsSummary.decisions} total)`
|
|
550
|
+
)
|
|
551
|
+
console.log(` Preferences: ${patternsSummary.preferences} saved`)
|
|
552
|
+
console.log(` Workflows: ${patternsSummary.workflows} tracked`)
|
|
553
|
+
console.log('')
|
|
554
|
+
}
|
|
555
|
+
|
|
518
556
|
// Token Savings Section
|
|
519
557
|
console.log('💰 TOKEN SAVINGS')
|
|
520
558
|
console.log(` Total saved: ${this._formatTokens(summary.totalTokensSaved)} tokens`)
|
|
@@ -532,7 +570,7 @@ export class AnalysisCommands extends PrjctCommandsBase {
|
|
|
532
570
|
|
|
533
571
|
// Agent Usage Section
|
|
534
572
|
if (summary.topAgents.length > 0) {
|
|
535
|
-
console.log('🤖 AGENT USAGE')
|
|
573
|
+
console.log('🤖 AGENT USAGE (all time)')
|
|
536
574
|
const totalUsage = summary.topAgents.reduce((sum, a) => sum + a.usageCount, 0)
|
|
537
575
|
for (const agent of summary.topAgents) {
|
|
538
576
|
const pct = totalUsage > 0 ? ((agent.usageCount / totalUsage) * 100).toFixed(0) : 0
|
|
@@ -568,7 +606,9 @@ export class AnalysisCommands extends PrjctCommandsBase {
|
|
|
568
606
|
summary,
|
|
569
607
|
dailyStats,
|
|
570
608
|
projectName,
|
|
571
|
-
firstSyncDate
|
|
609
|
+
firstSyncDate,
|
|
610
|
+
sessionActivity,
|
|
611
|
+
patternsSummary
|
|
572
612
|
)
|
|
573
613
|
console.log(markdown)
|
|
574
614
|
return { success: true, data: { markdown } }
|
|
@@ -576,7 +616,7 @@ export class AnalysisCommands extends PrjctCommandsBase {
|
|
|
576
616
|
|
|
577
617
|
return {
|
|
578
618
|
success: true,
|
|
579
|
-
data: summary,
|
|
619
|
+
data: { ...summary, session: sessionActivity, patterns: patternsSummary },
|
|
580
620
|
}
|
|
581
621
|
} catch (error) {
|
|
582
622
|
console.error('❌ Error:', (error as Error).message)
|
|
@@ -584,6 +624,76 @@ export class AnalysisCommands extends PrjctCommandsBase {
|
|
|
584
624
|
}
|
|
585
625
|
}
|
|
586
626
|
|
|
627
|
+
/**
|
|
628
|
+
* Get session activity stats from today's events
|
|
629
|
+
* @see PRJ-89
|
|
630
|
+
*/
|
|
631
|
+
private async _getSessionActivity(projectId: string): Promise<{
|
|
632
|
+
sessionDuration: string | null
|
|
633
|
+
tasksCompleted: number
|
|
634
|
+
featuresShipped: number
|
|
635
|
+
agentsUsed: { name: string; count: number }[]
|
|
636
|
+
}> {
|
|
637
|
+
try {
|
|
638
|
+
// Get today's events from memory
|
|
639
|
+
const recentHistory = await memoryService.getRecentEvents(projectId, 100)
|
|
640
|
+
|
|
641
|
+
const today = new Date().toISOString().split('T')[0]
|
|
642
|
+
const todayEvents = recentHistory.filter((e) => {
|
|
643
|
+
const ts = (e.timestamp || e.ts) as string | undefined
|
|
644
|
+
return ts?.startsWith(today)
|
|
645
|
+
})
|
|
646
|
+
|
|
647
|
+
// Calculate session duration (time between first and last event today)
|
|
648
|
+
let sessionDuration: string | null = null
|
|
649
|
+
if (todayEvents.length >= 2) {
|
|
650
|
+
const timestamps = todayEvents
|
|
651
|
+
.map((e) => new Date((e.timestamp || e.ts) as string).getTime())
|
|
652
|
+
.filter((t) => !Number.isNaN(t))
|
|
653
|
+
.sort((a, b) => a - b)
|
|
654
|
+
|
|
655
|
+
if (timestamps.length >= 2) {
|
|
656
|
+
const durationMs = timestamps[timestamps.length - 1] - timestamps[0]
|
|
657
|
+
sessionDuration = dateHelper.formatDuration(durationMs)
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
// Count tasks completed today
|
|
662
|
+
const tasksCompleted = todayEvents.filter((e) => e.action === 'task_completed').length
|
|
663
|
+
|
|
664
|
+
// Count features shipped today
|
|
665
|
+
const featuresShipped = todayEvents.filter((e) => e.action === 'feature_shipped').length
|
|
666
|
+
|
|
667
|
+
// Count agent usage from sync events
|
|
668
|
+
const agentCounts = new Map<string, number>()
|
|
669
|
+
for (const event of todayEvents) {
|
|
670
|
+
if (event.action === 'sync' && Array.isArray(event.subagents)) {
|
|
671
|
+
for (const agent of event.subagents as string[]) {
|
|
672
|
+
agentCounts.set(agent, (agentCounts.get(agent) || 0) + 1)
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
const agentsUsed = Array.from(agentCounts.entries())
|
|
678
|
+
.map(([name, count]) => ({ name, count }))
|
|
679
|
+
.sort((a, b) => b.count - a.count)
|
|
680
|
+
|
|
681
|
+
return {
|
|
682
|
+
sessionDuration,
|
|
683
|
+
tasksCompleted,
|
|
684
|
+
featuresShipped,
|
|
685
|
+
agentsUsed,
|
|
686
|
+
}
|
|
687
|
+
} catch {
|
|
688
|
+
return {
|
|
689
|
+
sessionDuration: null,
|
|
690
|
+
tasksCompleted: 0,
|
|
691
|
+
featuresShipped: 0,
|
|
692
|
+
agentsUsed: [],
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
|
|
587
697
|
// =========== Stats Helper Methods ===========
|
|
588
698
|
|
|
589
699
|
private _formatTokens(tokens: number): string {
|
|
@@ -631,15 +741,62 @@ export class AnalysisCommands extends PrjctCommandsBase {
|
|
|
631
741
|
},
|
|
632
742
|
_dailyStats: { date: string; tokensSaved: number; syncs: number }[],
|
|
633
743
|
projectName: string,
|
|
634
|
-
firstSyncDate: string
|
|
744
|
+
firstSyncDate: string,
|
|
745
|
+
sessionActivity?: {
|
|
746
|
+
sessionDuration: string | null
|
|
747
|
+
tasksCompleted: number
|
|
748
|
+
featuresShipped: number
|
|
749
|
+
agentsUsed: { name: string; count: number }[]
|
|
750
|
+
},
|
|
751
|
+
patternsSummary?: {
|
|
752
|
+
decisions: number
|
|
753
|
+
learnedDecisions: number
|
|
754
|
+
workflows: number
|
|
755
|
+
preferences: number
|
|
756
|
+
}
|
|
635
757
|
): string {
|
|
636
758
|
const lines: string[] = []
|
|
637
759
|
|
|
638
|
-
lines.push(`# ${projectName} -
|
|
760
|
+
lines.push(`# ${projectName} - Stats Dashboard`)
|
|
639
761
|
lines.push('')
|
|
640
762
|
lines.push(`_Generated: ${new Date().toLocaleString()} | Tracking since: ${firstSyncDate}_`)
|
|
641
763
|
lines.push('')
|
|
642
764
|
|
|
765
|
+
// Session Activity (PRJ-89)
|
|
766
|
+
if (sessionActivity) {
|
|
767
|
+
lines.push("## 🎯 Today's Activity")
|
|
768
|
+
lines.push('')
|
|
769
|
+
lines.push(`| Metric | Value |`)
|
|
770
|
+
lines.push(`|--------|-------|`)
|
|
771
|
+
if (sessionActivity.sessionDuration) {
|
|
772
|
+
lines.push(`| Duration | ${sessionActivity.sessionDuration} |`)
|
|
773
|
+
}
|
|
774
|
+
lines.push(`| Tasks completed | ${sessionActivity.tasksCompleted} |`)
|
|
775
|
+
lines.push(`| Features shipped | ${sessionActivity.featuresShipped} |`)
|
|
776
|
+
if (sessionActivity.agentsUsed.length > 0) {
|
|
777
|
+
const agentStr = sessionActivity.agentsUsed
|
|
778
|
+
.slice(0, 3)
|
|
779
|
+
.map((a) => `${a.name} (${a.count}×)`)
|
|
780
|
+
.join(', ')
|
|
781
|
+
lines.push(`| Agents used | ${agentStr} |`)
|
|
782
|
+
}
|
|
783
|
+
lines.push('')
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
// Patterns Learned (PRJ-89)
|
|
787
|
+
if (patternsSummary && (patternsSummary.decisions > 0 || patternsSummary.preferences > 0)) {
|
|
788
|
+
lines.push('## 🧠 Patterns Learned')
|
|
789
|
+
lines.push('')
|
|
790
|
+
lines.push(`| Type | Count |`)
|
|
791
|
+
lines.push(`|------|-------|`)
|
|
792
|
+
lines.push(
|
|
793
|
+
`| Decisions | ${patternsSummary.learnedDecisions} confirmed (${patternsSummary.decisions} total) |`
|
|
794
|
+
)
|
|
795
|
+
lines.push(`| Preferences | ${patternsSummary.preferences} |`)
|
|
796
|
+
lines.push(`| Workflows | ${patternsSummary.workflows} |`)
|
|
797
|
+
lines.push('')
|
|
798
|
+
}
|
|
799
|
+
|
|
643
800
|
lines.push('## 💰 Token Savings')
|
|
644
801
|
lines.push('')
|
|
645
802
|
lines.push(`| Metric | Value |`)
|
|
@@ -148,6 +148,7 @@ class ConfigManager {
|
|
|
148
148
|
const localConfig: LocalConfig = {
|
|
149
149
|
projectId,
|
|
150
150
|
dataPath: displayPath,
|
|
151
|
+
showMetrics: true, // PRJ-70: default to true for new projects
|
|
151
152
|
}
|
|
152
153
|
|
|
153
154
|
await this.writeConfig(projectPath, localConfig)
|
|
@@ -318,6 +319,29 @@ class ConfigManager {
|
|
|
318
319
|
return this.validateConfig(config)
|
|
319
320
|
}
|
|
320
321
|
|
|
322
|
+
/**
|
|
323
|
+
* Get showMetrics setting from config.
|
|
324
|
+
* Defaults to true for new or existing projects without the setting.
|
|
325
|
+
* @see PRJ-70
|
|
326
|
+
*/
|
|
327
|
+
async getShowMetrics(projectPath: string): Promise<boolean> {
|
|
328
|
+
const config = await this.readConfig(projectPath)
|
|
329
|
+
// Default to true if not set
|
|
330
|
+
return config?.showMetrics ?? true
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
/**
|
|
334
|
+
* Set showMetrics setting in config.
|
|
335
|
+
* @see PRJ-70
|
|
336
|
+
*/
|
|
337
|
+
async setShowMetrics(projectPath: string, showMetrics: boolean): Promise<void> {
|
|
338
|
+
const config = await this.readConfig(projectPath)
|
|
339
|
+
if (config) {
|
|
340
|
+
config.showMetrics = showMetrics
|
|
341
|
+
await this.writeConfig(projectPath, config)
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
|
|
321
345
|
/**
|
|
322
346
|
* Get configuration with defaults
|
|
323
347
|
* Returns LOCAL config only (projectId, dataPath)
|
|
@@ -112,6 +112,27 @@ export class MemoryService {
|
|
|
112
112
|
}
|
|
113
113
|
}
|
|
114
114
|
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Get recent events by projectId (for stats dashboard)
|
|
118
|
+
* @see PRJ-89
|
|
119
|
+
*/
|
|
120
|
+
async getRecentEvents(
|
|
121
|
+
projectId: string,
|
|
122
|
+
limit: number = 100
|
|
123
|
+
): Promise<Record<string, unknown>[]> {
|
|
124
|
+
try {
|
|
125
|
+
const memoryPath = pathManager.getFilePath(projectId, 'memory', 'context.jsonl')
|
|
126
|
+
const entries = await jsonlHelper.readJsonLines<Record<string, unknown>>(memoryPath)
|
|
127
|
+
return entries.slice(-limit)
|
|
128
|
+
} catch (error) {
|
|
129
|
+
// ENOENT or parse error - return empty
|
|
130
|
+
if (!isNotFoundError(error) && !(error instanceof SyntaxError)) {
|
|
131
|
+
console.error(`Memory read error: ${(error as Error).message}`)
|
|
132
|
+
}
|
|
133
|
+
return []
|
|
134
|
+
}
|
|
135
|
+
}
|
|
115
136
|
}
|
|
116
137
|
|
|
117
138
|
export const memoryService = new MemoryService()
|
package/core/types/config.ts
CHANGED
|
@@ -12,6 +12,12 @@ import type { IntegrationsConfig } from './integrations'
|
|
|
12
12
|
export interface LocalConfig {
|
|
13
13
|
projectId: string
|
|
14
14
|
dataPath: string
|
|
15
|
+
/**
|
|
16
|
+
* Whether to show metrics in command output.
|
|
17
|
+
* Defaults to true for new projects.
|
|
18
|
+
* @see PRJ-70
|
|
19
|
+
*/
|
|
20
|
+
showMetrics?: boolean
|
|
15
21
|
}
|
|
16
22
|
|
|
17
23
|
/**
|
package/core/types/memory.ts
CHANGED
|
@@ -75,6 +75,76 @@ export interface MemoryQuery {
|
|
|
75
75
|
since?: string
|
|
76
76
|
}
|
|
77
77
|
|
|
78
|
+
/**
|
|
79
|
+
* Domain types for task context.
|
|
80
|
+
* @see PRJ-107
|
|
81
|
+
*/
|
|
82
|
+
export type TaskDomain =
|
|
83
|
+
| 'frontend'
|
|
84
|
+
| 'backend'
|
|
85
|
+
| 'devops'
|
|
86
|
+
| 'docs'
|
|
87
|
+
| 'testing'
|
|
88
|
+
| 'database'
|
|
89
|
+
| 'general'
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Enhanced query parameters for selective memory retrieval.
|
|
93
|
+
* @see PRJ-107
|
|
94
|
+
*/
|
|
95
|
+
export interface RelevantMemoryQuery {
|
|
96
|
+
/** Task domain for context-aware retrieval */
|
|
97
|
+
taskDomain?: TaskDomain
|
|
98
|
+
/** Task description for keyword matching */
|
|
99
|
+
taskDescription?: string
|
|
100
|
+
/** Command being executed */
|
|
101
|
+
commandName?: string
|
|
102
|
+
/** Maximum results to return */
|
|
103
|
+
maxResults?: number
|
|
104
|
+
/** Minimum relevance score threshold (0-100) */
|
|
105
|
+
minRelevance?: number
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Memory with relevance score attached.
|
|
110
|
+
* @see PRJ-107
|
|
111
|
+
*/
|
|
112
|
+
export interface ScoredMemory extends Memory {
|
|
113
|
+
/** Relevance score (0-100) */
|
|
114
|
+
relevanceScore: number
|
|
115
|
+
/** Breakdown of score components */
|
|
116
|
+
scoreBreakdown?: {
|
|
117
|
+
domainMatch: number
|
|
118
|
+
tagMatch: number
|
|
119
|
+
recency: number
|
|
120
|
+
confidence: number
|
|
121
|
+
keywords: number
|
|
122
|
+
userTriggered: number
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Result of selective memory retrieval with metrics.
|
|
128
|
+
* @see PRJ-107
|
|
129
|
+
*/
|
|
130
|
+
export interface MemoryRetrievalResult {
|
|
131
|
+
/** Relevant memories sorted by score */
|
|
132
|
+
memories: ScoredMemory[]
|
|
133
|
+
/** Retrieval metrics */
|
|
134
|
+
metrics: {
|
|
135
|
+
/** Total memories in database */
|
|
136
|
+
totalMemories: number
|
|
137
|
+
/** Memories that passed threshold */
|
|
138
|
+
memoriesConsidered: number
|
|
139
|
+
/** Memories returned */
|
|
140
|
+
memoriesReturned: number
|
|
141
|
+
/** Filtering ratio (returned/total) */
|
|
142
|
+
filteringRatio: number
|
|
143
|
+
/** Average relevance score of returned memories */
|
|
144
|
+
avgRelevanceScore: number
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
78
148
|
/**
|
|
79
149
|
* Memory database structure.
|
|
80
150
|
*/
|
package/core/utils/output.ts
CHANGED
|
@@ -235,10 +235,10 @@ const out: Output = {
|
|
|
235
235
|
const border = '─'.repeat(maxLen + 2)
|
|
236
236
|
|
|
237
237
|
console.log(chalk.dim(`┌${border}┐`))
|
|
238
|
-
console.log(chalk.dim('│')
|
|
238
|
+
console.log(`${chalk.dim('│')} ${chalk.bold(title.padEnd(maxLen))} ${chalk.dim('│')}`)
|
|
239
239
|
console.log(chalk.dim(`├${border}┤`))
|
|
240
240
|
for (const line of lines) {
|
|
241
|
-
console.log(chalk.dim('│')
|
|
241
|
+
console.log(`${chalk.dim('│')} ${line.padEnd(maxLen)} ${chalk.dim('│')}`)
|
|
242
242
|
}
|
|
243
243
|
console.log(chalk.dim(`└${border}┘`))
|
|
244
244
|
return this
|