Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
99 changes: 99 additions & 0 deletions .changeset/visual-formatting-and-github-stats-fix.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
---
"@lytics/dev-agent-cli": patch
"@lytics/dev-agent-core": patch
"@lytics/dev-agent-subagents": patch
"@lytics/dev-agent-types": patch
"dev-agent": patch
---

# Visual Formatting & GitHub Stats Improvements

## Visual Enhancements ✨

### Tree Branches & File Icons
All CLI outputs now use consistent tree-based formatting with file icons:

**`dev map` hot paths:**
```
## Hot Paths (most referenced)
β”œβ”€ πŸ“˜ **typescript.ts** β€’ 307 refs
/packages/core/src/scanner
β”œβ”€ πŸ“˜ **index.ts** β€’ 251 refs
/packages/core/src/indexer
└─ πŸ“˜ **go.ts** β€’ 152 refs
/packages/core/src/scanner
```

**`dev activity` output:**
```
β”œβ”€ πŸ“˜ packages/mcp-server/bin/dev-agent-mcp.ts
β”‚ 34 commits β€’ 1 πŸ‘€ β€’ Last: today
β”‚
β”œβ”€ πŸ“˜ packages/core/src/indexer/index.ts
β”‚ 32 commits β€’ 1 πŸ‘€ β€’ Last: today
```

### Shared Icon Utility
Extracted `getFileIcon()` to `@lytics/dev-agent-core/utils` for reuse across packages.

## GitHub Stats Fix πŸ›

Fixed confusing issue/PR state display:

**Before:**
```
Issues: 68 total (14 open, 55 closed)
Pull Requests: 97 total (14 open, 96 merged) ❌ Wrong!
```

**After:**
```
Issues: 68 total (14 open, 54 closed)
Pull Requests: 97 total (0 open, 96 merged) βœ… Correct!
```

- Added separate state tracking: `issuesByState`, `prsByState`
- GitHub indexer now tracks issue and PR states independently
- Stats display now shows accurate per-type counts

## Progress Display Improvements πŸ“Š

### Detailed Progress with Rates
All indexing commands now show detailed progress:

```
Scanning Repository: 1,234/4,567 files (27%, 45 files/sec)
Embedding Vectors: 856/2,549 documents (34%, 122 docs/sec)
```

Applied to:
- `dev index` - scanning & embedding progress
- `dev update` - changed files & embedding progress
- `dev git index` - commit embedding progress
- `dev github index` - document embedding progress

### Update Plan Display
`dev update` now shows what will change before starting:

```
Update plan:
β€’ Changed: 3 files
β€’ Added: 1 file
β€’ Deleted: 0 files
```

### Code Quality
- Refactored progress logic into `ProgressRenderer.updateSectionWithRate()`
- Reduced ~40 lines of duplicated code
- Fixed NaN display (now shows "Discovering files..." initially)

## Bug Fixes πŸ›

- **`dev owners`**: Fixed "No ownership data" error when run from subdirectories
- **Progress Display**: Fixed NaN showing during initial file discovery phase
- **`dev update`**: Removed duplicate checkmark in success message

## Breaking Changes

None - all changes are backward compatible. Old GitHub state files will fall back to aggregate counts gracefully.

50 changes: 23 additions & 27 deletions packages/cli/src/commands/activity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import * as path from 'node:path';
import {
type FileMetrics,
getFileIcon,
getMostActive,
getStoragePath,
MetricsStore,
Expand All @@ -31,43 +32,38 @@ function formatRelativeTime(date: Date): string {
}

/**
* Format files as a compact table
* Format files with tree branches and icons
*/
function formatFileMetricsTable(files: FileMetrics[]): string {
if (files.length === 0) return '';

// Calculate column widths
const maxPathLen = Math.max(...files.map((f) => f.filePath.length), 40);
const pathWidth = Math.min(maxPathLen, 55);
let output = '';

// Header
let output = chalk.bold(
`${'FILE'.padEnd(pathWidth)} ${'COMMITS'.padStart(7)} ${'LOC'.padStart(6)} ${'AUTHORS'.padStart(7)} ${'LAST CHANGE'}\n`
);
for (let i = 0; i < files.length; i++) {
const file = files[i];
const isLast = i === files.length - 1;
const branch = isLast ? '└─' : 'β”œβ”€';
const connector = isLast ? ' ' : 'β”‚'; // Vertical line for non-last items
const icon = getFileIcon(file.filePath);

// Separator line
output += chalk.dim(`${'─'.repeat(pathWidth + 2 + 7 + 2 + 6 + 2 + 7 + 2 + 12)}\n`);

// Rows
for (const file of files) {
// Truncate path if too long
let displayPath = file.filePath;
if (displayPath.length > pathWidth) {
displayPath = `...${displayPath.slice(-(pathWidth - 3))}`;
}
displayPath = displayPath.padEnd(pathWidth);

const commits = String(file.commitCount).padStart(7);
const loc = String(file.linesOfCode).padStart(6);

// Author count with emoji
const authorIcon = file.authorCount === 1 ? ' πŸ‘€' : file.authorCount === 2 ? ' πŸ‘₯' : 'πŸ‘₯πŸ‘₯';
const authors = `${String(file.authorCount).padStart(5)}${authorIcon}`;
// Author info
const authorIcon = file.authorCount === 1 ? 'πŸ‘€' : file.authorCount === 2 ? 'πŸ‘₯' : 'πŸ‘₯πŸ‘₯';

// Relative time
const lastChange = file.lastModified ? formatRelativeTime(file.lastModified) : 'unknown';

output += `${chalk.dim(displayPath)} ${chalk.cyan(commits)} ${chalk.yellow(loc)} ${chalk.green(authors)} ${chalk.gray(lastChange)}\n`;
// File path line with icon and branch
output += `${chalk.dim(branch)} ${icon} ${file.filePath}\n`;

// Metrics line with vertical connector
output += chalk.dim(
`${connector} ${file.commitCount} commits β€’ ${file.authorCount} ${authorIcon} β€’ Last: ${lastChange}\n`
);

// Add vertical line separator between items (except after last)
if (!isLast) {
output += chalk.dim(`${connector}\n`);
}
}

return output;
Expand Down
8 changes: 5 additions & 3 deletions packages/cli/src/commands/git.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,9 +98,11 @@ export const gitCommand = new Command('git')
}

// Update embedding progress
const pct = Math.round((progress.commitsProcessed / progress.totalCommits) * 100);
progressRenderer.updateSection(
`${progress.commitsProcessed}/${progress.totalCommits} commits (${pct}%)`
progressRenderer.updateSectionWithRate(
progress.commitsProcessed,
progress.totalCommits,
'commits',
embeddingStartTime
);
}
},
Expand Down
10 changes: 5 additions & 5 deletions packages/cli/src/commands/github.ts
Original file line number Diff line number Diff line change
Expand Up @@ -133,11 +133,11 @@ Related:
}

// Update embedding progress
const pct = Math.round(
(progress.documentsProcessed / progress.totalDocuments) * 100
);
progressRenderer.updateSection(
`${progress.documentsProcessed}/${progress.totalDocuments} documents (${pct}%)`
progressRenderer.updateSectionWithRate(
progress.documentsProcessed,
progress.totalDocuments,
'documents',
embeddingStartTime
);
}
},
Expand Down
39 changes: 25 additions & 14 deletions packages/cli/src/commands/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -199,17 +199,20 @@ export const indexCommand = new Command('index')
}

// Update embedding progress
const pct = Math.round((progress.documentsIndexed / progress.totalDocuments) * 100);
const embeddingElapsed = (Date.now() - embeddingStartTime) / 1000;
const docsPerSec =
embeddingElapsed > 0 ? progress.documentsIndexed / embeddingElapsed : 0;
progressRenderer.updateSection(
`${progress.documentsIndexed.toLocaleString()}/${progress.totalDocuments.toLocaleString()} documents (${pct}%, ${docsPerSec.toFixed(0)} docs/sec)`
progressRenderer.updateSectionWithRate(
progress.documentsIndexed,
progress.totalDocuments,
'documents',
embeddingStartTime
);
} else {
// Scanning phase
const percent = progress.percentComplete || 0;
progressRenderer.updateSection(`${percent.toFixed(0)}% complete`);
progressRenderer.updateSectionWithRate(
progress.filesProcessed,
progress.totalFiles,
'files',
scanStartTime
);
}
},
});
Expand Down Expand Up @@ -260,9 +263,11 @@ export const indexCommand = new Command('index')
logger: indexLogger,
onProgress: (progress) => {
if (progress.phase === 'storing' && progress.totalCommits > 0) {
const pct = Math.round((progress.commitsProcessed / progress.totalCommits) * 100);
progressRenderer.updateSection(
`${progress.commitsProcessed}/${progress.totalCommits} commits (${pct}%)`
progressRenderer.updateSectionWithRate(
progress.commitsProcessed,
progress.totalCommits,
'commits',
gitStartTime
);
}
},
Expand All @@ -280,6 +285,7 @@ export const indexCommand = new Command('index')
let ghStats = { totalDocuments: 0, indexDuration: 0 };
if (canIndexGitHub) {
const ghStartTime = Date.now();
let ghEmbeddingStartTime = 0;
const ghVectorPath = `${filePaths.vectors}-github`;
const ghIndexer = new GitHubIndexer({
vectorStorePath: ghVectorPath,
Expand All @@ -295,9 +301,14 @@ export const indexCommand = new Command('index')
if (progress.phase === 'fetching') {
progressRenderer.updateSection('Fetching issues/PRs...');
} else if (progress.phase === 'embedding') {
const pct = Math.round((progress.documentsProcessed / progress.totalDocuments) * 100);
progressRenderer.updateSection(
`${progress.documentsProcessed}/${progress.totalDocuments} documents (${pct}%)`
if (ghEmbeddingStartTime === 0) {
ghEmbeddingStartTime = Date.now();
}
progressRenderer.updateSectionWithRate(
progress.documentsProcessed,
progress.totalDocuments,
'documents',
ghEmbeddingStartTime
);
}
},
Expand Down
10 changes: 8 additions & 2 deletions packages/cli/src/commands/owners.ts
Original file line number Diff line number Diff line change
Expand Up @@ -480,7 +480,10 @@ function formatSubdirectoryMode(
): string {
// Filter developers to only those with files in current directory
const relevantDevs = developers.filter((dev) =>
dev.topFiles.some((f) => f.path.startsWith(`${repositoryPath}/${currentDir}`))
dev.topFiles.some((f) => {
const relativePath = f.path.replace(`${repositoryPath}/`, '');
return relativePath.startsWith(currentDir);
})
);

if (relevantDevs.length === 0) {
Expand All @@ -498,7 +501,10 @@ function formatSubdirectoryMode(

// Show top files in this directory
const filesInDir = primary.topFiles
.filter((f) => f.path.startsWith(`${repositoryPath}/${currentDir}`))
.filter((f) => {
const relativePath = f.path.replace(`${repositoryPath}/`, '');
return relativePath.startsWith(currentDir);
})
.slice(0, 5);

if (filesInDir.length > 0) {
Expand Down
4 changes: 4 additions & 0 deletions packages/cli/src/commands/stats.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,8 @@ async function loadCurrentStats(): Promise<{
totalDocuments: state.totalDocuments || 0,
byType: state.byType || {},
byState: state.byState || {},
issuesByState: state.issuesByState,
prsByState: state.prsByState,
lastIndexed: state.lastIndexed || '',
indexDuration: state.indexDuration || 0,
};
Expand Down Expand Up @@ -217,6 +219,8 @@ What You'll See:
totalDocuments: number;
byType: { issue?: number; pull_request?: number };
byState: { open?: number; closed?: number; merged?: number };
issuesByState?: { open: number; closed: number };
prsByState?: { open: number; closed: number; merged: number };
lastIndexed: string;
} | null) || undefined,
});
Expand Down
Loading