Skip to content

Commit 573ad3a

Browse files
committed
feat(cli): unified indexing with git history and GitHub
- dev index now indexes code, git history, and GitHub in one command - Add upfront prerequisites check with indexing plan display - Add --no-git and --no-github flags to skip specific indexers - Add dev git subcommands (index, search, stats) - Fix dev --version to read from package.json at build time - Add tsconfig.json to dev-agent package for IDE support
1 parent 8ffc040 commit 573ad3a

File tree

8 files changed

+378
-12
lines changed

8 files changed

+378
-12
lines changed

.changeset/fix-cli-version.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
---
2+
"@lytics/dev-agent": patch
3+
"@lytics/dev-agent-cli": patch
4+
---
5+
6+
feat: unified indexing and CLI improvements
7+
8+
**`dev index .`** now indexes everything in one command:
9+
- Code (always)
10+
- Git history (if in a git repo)
11+
- GitHub issues/PRs (if gh CLI installed)
12+
13+
Shows an upfront "indexing plan" with prerequisites check.
14+
Use `--no-git` or `--no-github` to skip specific indexers.
15+
16+
**New `dev git` commands:**
17+
- `dev git index` - index git history separately
18+
- `dev git search <query>` - semantic search over commits
19+
- `dev git stats` - show indexed commit count
20+
21+
**Fix:** `dev --version` now correctly displays installed version (injected at build time).

packages/cli/src/cli.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { cleanCommand } from './commands/clean.js';
66
import { compactCommand } from './commands/compact.js';
77
import { exploreCommand } from './commands/explore.js';
88
import { ghCommand } from './commands/gh.js';
9+
import { gitCommand } from './commands/git.js';
910
import { indexCommand } from './commands/index.js';
1011
import { initCommand } from './commands/init.js';
1112
import { mcpCommand } from './commands/mcp.js';
@@ -15,12 +16,16 @@ import { statsCommand } from './commands/stats.js';
1516
import { storageCommand } from './commands/storage.js';
1617
import { updateCommand } from './commands/update.js';
1718

19+
// Injected at build time by tsup define
20+
declare const __VERSION__: string;
21+
const VERSION = typeof __VERSION__ !== 'undefined' ? __VERSION__ : '0.0.0-dev';
22+
1823
const program = new Command();
1924

2025
program
2126
.name('dev')
2227
.description(chalk.cyan('🤖 Dev-Agent - Multi-agent code intelligence platform'))
23-
.version('0.1.0');
28+
.version(VERSION);
2429

2530
// Register commands
2631
program.addCommand(initCommand);
@@ -29,6 +34,7 @@ program.addCommand(searchCommand);
2934
program.addCommand(exploreCommand);
3035
program.addCommand(planCommand);
3136
program.addCommand(ghCommand);
37+
program.addCommand(gitCommand);
3238
program.addCommand(updateCommand);
3339
program.addCommand(statsCommand);
3440
program.addCommand(compactCommand);

packages/cli/src/commands/git.ts

Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
/**
2+
* Git History Commands
3+
* CLI commands for indexing and searching git commit history
4+
*/
5+
6+
import {
7+
GitIndexer,
8+
getStorageFilePaths,
9+
getStoragePath,
10+
LocalGitExtractor,
11+
VectorStorage,
12+
} from '@lytics/dev-agent-core';
13+
import chalk from 'chalk';
14+
import { Command } from 'commander';
15+
import ora from 'ora';
16+
import { logger } from '../utils/logger.js';
17+
18+
/**
19+
* Create Git indexer with centralized storage
20+
*/
21+
async function createGitIndexer(): Promise<{ indexer: GitIndexer; vectorStore: VectorStorage }> {
22+
const repositoryPath = process.cwd();
23+
const storagePath = await getStoragePath(repositoryPath);
24+
const { vectors } = getStorageFilePaths(storagePath);
25+
26+
if (!vectors || vectors.includes('undefined')) {
27+
throw new Error(`Invalid storage path: vectors=${vectors}`);
28+
}
29+
30+
const vectorStorePath = `${vectors}-git`;
31+
32+
const extractor = new LocalGitExtractor(repositoryPath);
33+
const vectorStore = new VectorStorage({ storePath: vectorStorePath });
34+
await vectorStore.initialize();
35+
36+
const indexer = new GitIndexer({
37+
extractor,
38+
vectorStorage: vectorStore,
39+
});
40+
41+
return { indexer, vectorStore };
42+
}
43+
44+
export const gitCommand = new Command('git')
45+
.description('Git history commands (index commits, search history)')
46+
.addCommand(
47+
new Command('index')
48+
.description('Index git commit history for semantic search')
49+
.option('--limit <number>', 'Maximum commits to index (default: 500)', Number.parseInt, 500)
50+
.option(
51+
'--since <date>',
52+
'Only index commits after this date (e.g., "2024-01-01", "6 months ago")'
53+
)
54+
.action(async (options) => {
55+
const spinner = ora('Loading configuration...').start();
56+
57+
try {
58+
spinner.text = 'Initializing git indexer...';
59+
60+
const { indexer, vectorStore } = await createGitIndexer();
61+
62+
spinner.text = 'Indexing git commits...';
63+
64+
const stats = await indexer.index({
65+
limit: options.limit,
66+
since: options.since,
67+
});
68+
69+
spinner.succeed(chalk.green('Git history indexed!'));
70+
71+
// Display stats
72+
logger.log('');
73+
logger.log(chalk.bold('Indexing Stats:'));
74+
logger.log(` Commits indexed: ${chalk.yellow(stats.commitsIndexed)}`);
75+
logger.log(` Duration: ${chalk.cyan(stats.durationMs)}ms`);
76+
logger.log('');
77+
logger.log(chalk.gray('Now you can search with: dev git search "<query>"'));
78+
logger.log('');
79+
80+
await vectorStore.close();
81+
} catch (error) {
82+
spinner.fail('Indexing failed');
83+
logger.error((error as Error).message);
84+
85+
if ((error as Error).message.includes('not a git repository')) {
86+
logger.log('');
87+
logger.log(chalk.yellow('This directory is not a git repository.'));
88+
logger.log('Run this command from a git repository root.');
89+
}
90+
91+
process.exit(1);
92+
}
93+
})
94+
)
95+
.addCommand(
96+
new Command('search')
97+
.description('Semantic search over git commit messages')
98+
.argument('<query>', 'Search query (e.g., "authentication bug fix")')
99+
.option('--limit <number>', 'Number of results', Number.parseInt, 10)
100+
.option('--json', 'Output as JSON')
101+
.action(async (query, options) => {
102+
const spinner = ora('Loading configuration...').start();
103+
104+
try {
105+
spinner.text = 'Initializing...';
106+
107+
const { indexer, vectorStore } = await createGitIndexer();
108+
109+
spinner.text = 'Searching commits...';
110+
111+
const results = await indexer.search(query, {
112+
limit: options.limit,
113+
});
114+
115+
spinner.succeed(chalk.green(`Found ${results.length} commits`));
116+
117+
if (results.length === 0) {
118+
logger.log('');
119+
logger.log(chalk.yellow('No commits found.'));
120+
logger.log(chalk.gray('Make sure you have indexed git history: dev git index'));
121+
await vectorStore.close();
122+
return;
123+
}
124+
125+
// Output results
126+
if (options.json) {
127+
console.log(JSON.stringify(results, null, 2));
128+
await vectorStore.close();
129+
return;
130+
}
131+
132+
logger.log('');
133+
for (const commit of results) {
134+
logger.log(`${chalk.yellow(commit.shortHash)} ${chalk.bold(commit.subject)}`);
135+
logger.log(
136+
` ${chalk.gray(`${commit.author.name}`)} | ${chalk.gray(new Date(commit.author.date).toLocaleDateString())}`
137+
);
138+
139+
if (commit.refs.issueRefs && commit.refs.issueRefs.length > 0) {
140+
logger.log(` ${chalk.cyan(`Refs: ${commit.refs.issueRefs.join(', ')}`)}`);
141+
}
142+
143+
logger.log('');
144+
}
145+
146+
await vectorStore.close();
147+
} catch (error) {
148+
spinner.fail('Search failed');
149+
logger.error((error as Error).message);
150+
process.exit(1);
151+
}
152+
})
153+
)
154+
.addCommand(
155+
new Command('stats').description('Show git indexing statistics').action(async () => {
156+
const spinner = ora('Loading configuration...').start();
157+
158+
try {
159+
spinner.text = 'Initializing...';
160+
161+
const { indexer, vectorStore } = await createGitIndexer();
162+
163+
const totalCommits = await indexer.getIndexedCommitCount();
164+
165+
spinner.stop();
166+
167+
if (totalCommits === 0) {
168+
logger.log('');
169+
logger.log(chalk.yellow('Git history not indexed'));
170+
logger.log('Run "dev git index" to index commits');
171+
await vectorStore.close();
172+
return;
173+
}
174+
175+
logger.log('');
176+
logger.log(chalk.bold.cyan('Git History Stats'));
177+
logger.log('');
178+
logger.log(`Total Commits Indexed: ${chalk.yellow(totalCommits)}`);
179+
logger.log('');
180+
181+
await vectorStore.close();
182+
} catch (error) {
183+
spinner.fail('Failed to get stats');
184+
logger.error((error as Error).message);
185+
process.exit(1);
186+
}
187+
})
188+
);

0 commit comments

Comments
 (0)