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
21 changes: 21 additions & 0 deletions .changeset/fix-cli-version.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
---
"@lytics/dev-agent": patch
"@lytics/dev-agent-cli": patch
---

feat: unified indexing and CLI improvements

**`dev index .`** now indexes everything in one command:
- Code (always)
- Git history (if in a git repo)
- GitHub issues/PRs (if gh CLI installed)

Shows an upfront "indexing plan" with prerequisites check.
Use `--no-git` or `--no-github` to skip specific indexers.

**New `dev git` commands:**
- `dev git index` - index git history separately
- `dev git search <query>` - semantic search over commits
- `dev git stats` - show indexed commit count

**Fix:** `dev --version` now correctly displays installed version (injected at build time).
9 changes: 7 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -197,15 +197,20 @@ npm link
## CLI Commands

```bash
# Index repository
# Index everything (code, git history, GitHub)
dev index .
dev index . --no-github # Skip GitHub indexing

# Semantic search
dev search "how do agents communicate"
dev search "error handling" --threshold 0.3

# Git history search
dev git search "authentication fix" # Semantic search over commits
dev git stats # Show indexed commit count

# GitHub integration
dev gh index # Index issues and PRs
dev gh index # Index issues and PRs (also done by dev index)
dev gh search "authentication bug" # Semantic search

# View statistics
Expand Down
10 changes: 8 additions & 2 deletions examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ Real-world usage patterns for dev-agent MCP tools.
# Install dev-agent
npm install -g dev-agent

# Index your repository
# Index your repository (code, git history, GitHub)
cd /path/to/your/project
dev index .

Expand Down Expand Up @@ -350,9 +350,15 @@ dev_health
## CLI Examples

```bash
# Search from command line
# Index everything
dev index .

# Search code
dev search "authentication" --limit 5 --threshold 0.4

# Search git history
dev git search "authentication fix"

# Check stats
dev stats

Expand Down
8 changes: 7 additions & 1 deletion packages/cli/src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { cleanCommand } from './commands/clean.js';
import { compactCommand } from './commands/compact.js';
import { exploreCommand } from './commands/explore.js';
import { ghCommand } from './commands/gh.js';
import { gitCommand } from './commands/git.js';
import { indexCommand } from './commands/index.js';
import { initCommand } from './commands/init.js';
import { mcpCommand } from './commands/mcp.js';
Expand All @@ -15,12 +16,16 @@ import { statsCommand } from './commands/stats.js';
import { storageCommand } from './commands/storage.js';
import { updateCommand } from './commands/update.js';

// Injected at build time by tsup define
declare const __VERSION__: string;
const VERSION = typeof __VERSION__ !== 'undefined' ? __VERSION__ : '0.0.0-dev';

const program = new Command();

program
.name('dev')
.description(chalk.cyan('🤖 Dev-Agent - Multi-agent code intelligence platform'))
.version('0.1.0');
.version(VERSION);

// Register commands
program.addCommand(initCommand);
Expand All @@ -29,6 +34,7 @@ program.addCommand(searchCommand);
program.addCommand(exploreCommand);
program.addCommand(planCommand);
program.addCommand(ghCommand);
program.addCommand(gitCommand);
program.addCommand(updateCommand);
program.addCommand(statsCommand);
program.addCommand(compactCommand);
Expand Down
188 changes: 188 additions & 0 deletions packages/cli/src/commands/git.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
/**
* Git History Commands
* CLI commands for indexing and searching git commit history
*/

import {
GitIndexer,
getStorageFilePaths,
getStoragePath,
LocalGitExtractor,
VectorStorage,
} from '@lytics/dev-agent-core';
import chalk from 'chalk';
import { Command } from 'commander';
import ora from 'ora';
import { logger } from '../utils/logger.js';

/**
* Create Git indexer with centralized storage
*/
async function createGitIndexer(): Promise<{ indexer: GitIndexer; vectorStore: VectorStorage }> {
const repositoryPath = process.cwd();
const storagePath = await getStoragePath(repositoryPath);
const { vectors } = getStorageFilePaths(storagePath);

if (!vectors || vectors.includes('undefined')) {
throw new Error(`Invalid storage path: vectors=${vectors}`);
}

const vectorStorePath = `${vectors}-git`;

const extractor = new LocalGitExtractor(repositoryPath);
const vectorStore = new VectorStorage({ storePath: vectorStorePath });
await vectorStore.initialize();

const indexer = new GitIndexer({
extractor,
vectorStorage: vectorStore,
});

return { indexer, vectorStore };
}

export const gitCommand = new Command('git')
.description('Git history commands (index commits, search history)')
.addCommand(
new Command('index')
.description('Index git commit history for semantic search')
.option('--limit <number>', 'Maximum commits to index (default: 500)', Number.parseInt, 500)
.option(
'--since <date>',
'Only index commits after this date (e.g., "2024-01-01", "6 months ago")'
)
.action(async (options) => {
const spinner = ora('Loading configuration...').start();

try {
spinner.text = 'Initializing git indexer...';

const { indexer, vectorStore } = await createGitIndexer();

spinner.text = 'Indexing git commits...';

const stats = await indexer.index({
limit: options.limit,
since: options.since,
});

spinner.succeed(chalk.green('Git history indexed!'));

// Display stats
logger.log('');
logger.log(chalk.bold('Indexing Stats:'));
logger.log(` Commits indexed: ${chalk.yellow(stats.commitsIndexed)}`);
logger.log(` Duration: ${chalk.cyan(stats.durationMs)}ms`);
logger.log('');
logger.log(chalk.gray('Now you can search with: dev git search "<query>"'));
logger.log('');

await vectorStore.close();
} catch (error) {
spinner.fail('Indexing failed');
logger.error((error as Error).message);

if ((error as Error).message.includes('not a git repository')) {
logger.log('');
logger.log(chalk.yellow('This directory is not a git repository.'));
logger.log('Run this command from a git repository root.');
}

process.exit(1);
}
})
)
.addCommand(
new Command('search')
.description('Semantic search over git commit messages')
.argument('<query>', 'Search query (e.g., "authentication bug fix")')
.option('--limit <number>', 'Number of results', Number.parseInt, 10)
.option('--json', 'Output as JSON')
.action(async (query, options) => {
const spinner = ora('Loading configuration...').start();

try {
spinner.text = 'Initializing...';

const { indexer, vectorStore } = await createGitIndexer();

spinner.text = 'Searching commits...';

const results = await indexer.search(query, {
limit: options.limit,
});

spinner.succeed(chalk.green(`Found ${results.length} commits`));

if (results.length === 0) {
logger.log('');
logger.log(chalk.yellow('No commits found.'));
logger.log(chalk.gray('Make sure you have indexed git history: dev git index'));
await vectorStore.close();
return;
}

// Output results
if (options.json) {
console.log(JSON.stringify(results, null, 2));
await vectorStore.close();
return;
}

logger.log('');
for (const commit of results) {
logger.log(`${chalk.yellow(commit.shortHash)} ${chalk.bold(commit.subject)}`);
logger.log(
` ${chalk.gray(`${commit.author.name}`)} | ${chalk.gray(new Date(commit.author.date).toLocaleDateString())}`
);

if (commit.refs.issueRefs && commit.refs.issueRefs.length > 0) {
logger.log(` ${chalk.cyan(`Refs: ${commit.refs.issueRefs.join(', ')}`)}`);
}

logger.log('');
}

await vectorStore.close();
} catch (error) {
spinner.fail('Search failed');
logger.error((error as Error).message);
process.exit(1);
}
})
)
.addCommand(
new Command('stats').description('Show git indexing statistics').action(async () => {
const spinner = ora('Loading configuration...').start();

try {
spinner.text = 'Initializing...';

const { indexer, vectorStore } = await createGitIndexer();

const totalCommits = await indexer.getIndexedCommitCount();

spinner.stop();

if (totalCommits === 0) {
logger.log('');
logger.log(chalk.yellow('Git history not indexed'));
logger.log('Run "dev git index" to index commits');
await vectorStore.close();
return;
}

logger.log('');
logger.log(chalk.bold.cyan('Git History Stats'));
logger.log('');
logger.log(`Total Commits Indexed: ${chalk.yellow(totalCommits)}`);
logger.log('');

await vectorStore.close();
} catch (error) {
spinner.fail('Failed to get stats');
logger.error((error as Error).message);
process.exit(1);
}
})
);
Loading