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
13 changes: 7 additions & 6 deletions packages/core/src/scanner/__tests__/scanner.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,14 +71,14 @@ describe('Scanner', () => {
it('should handle excluded patterns', async () => {
const result = await scanRepository({
repoRoot,
include: ['packages/**/*.ts'],
include: ['packages/core/src/scanner/*.ts'], // Narrow scope for faster test
exclude: ['**/node_modules/**', '**/dist/**', '**/*.test.ts'],
});

// Should not include test files
const testFiles = result.documents.filter((d) => d.metadata.file.includes('.test.ts'));
expect(testFiles.length).toBe(0);
});
}, 10000);

it('should provide scanner capabilities', () => {
const registry = createDefaultRegistry();
Expand Down Expand Up @@ -249,18 +249,19 @@ describe('Scanner', () => {
const registry = createDefaultRegistry();

// Scan without include patterns - should auto-detect
// Use a narrow repoRoot for faster test
const result = await registry.scanRepository({
repoRoot,
repoRoot: `${repoRoot}/packages/core/src/scanner`,
exclude: ['**/*.test.ts', '**/node_modules/**', '**/dist/**'],
});

// Should find files automatically based on registered scanners
expect(result.stats.filesScanned).toBeGreaterThan(0);
});
}, 10000);

it('should use default exclusions', async () => {
const result = await scanRepository({
repoRoot,
repoRoot: `${repoRoot}/packages/core/src/scanner`, // Narrow scope for faster test
include: ['**/*.ts', '**/*.md'],
// Not specifying exclude - should use defaults
});
Expand All @@ -274,7 +275,7 @@ describe('Scanner', () => {
// Should not include dist files
const distFiles = result.documents.filter((d) => d.metadata.file.includes('dist/'));
expect(distFiles.length).toBe(0);
});
}, 10000);

it('should handle mixed language repositories', async () => {
const result = await scanRepository({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ describe('AdapterRegistry', () => {
it('should include execution time in metadata', async () => {
const result = await registry.executeTool('mock_echo', { message: 'test' }, context);

expect(result.metadata?.executionTime).toBeGreaterThanOrEqual(0);
expect(result.metadata?.duration_ms).toBeGreaterThanOrEqual(0);
});

it('should handle tool execution errors', async () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -254,8 +254,10 @@ describe('GitHubAdapter', () => {

expect(result.success).toBe(true);
const content = (result.data as { content: string })?.content;
expect(content).toContain('🪙');
expect(content).toMatch(/~\d+ tokens$/);
expect(content).toBeDefined();
// Token info is now in metadata, not content
expect(result.metadata).toHaveProperty('tokens');
expect(result.metadata?.tokens).toBeGreaterThan(0);
});
});

Expand Down
5 changes: 4 additions & 1 deletion packages/mcp-server/src/adapters/__tests__/mock-adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,10 @@ export class MockAdapter extends ToolAdapter {
timestamp: new Date().toISOString(),
},
metadata: {
tokenEstimate: 10,
tokens: 10,
duration_ms: 1,
timestamp: new Date().toISOString(),
cached: false,
},
};
}
Expand Down
23 changes: 12 additions & 11 deletions packages/mcp-server/src/adapters/__tests__/search-adapter.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -260,9 +260,10 @@ describe('SearchAdapter', () => {

expect(result.success).toBe(true);
expect(result.data).toHaveProperty('query', 'authentication');
expect(result.data).toHaveProperty('resultCount', 2);
expect(result.data).toHaveProperty('results');
expect(result.data).toHaveProperty('tokenEstimate');
expect(result.data).toHaveProperty('content');
expect(result.metadata).toHaveProperty('tokens');
expect(result.metadata).toHaveProperty('duration_ms');
expect(result.metadata).toHaveProperty('results_total', 2);
expect(mockIndexer.search).toHaveBeenCalledWith('authentication', {
limit: 10,
scoreThreshold: 0,
Expand All @@ -278,9 +279,9 @@ describe('SearchAdapter', () => {
);

expect(result.success).toBe(true);
expect(typeof result.data?.results).toBe('string');
expect((result.data?.results as string).length).toBeGreaterThan(0);
expect(result.data?.results as string).toContain('authenticate');
expect(typeof result.data?.content).toBe('string');
expect((result.data?.content as string).length).toBeGreaterThan(0);
expect(result.data?.content as string).toContain('authenticate');
});

it('should respect limit parameter', async () => {
Expand All @@ -297,7 +298,7 @@ describe('SearchAdapter', () => {
limit: 3,
scoreThreshold: 0,
});
expect(result.data?.resultCount).toBe(2); // Mock returns 2 results
expect(result.metadata?.results_total).toBe(2); // Mock returns 2 results
});

it('should respect score threshold parameter', async () => {
Expand Down Expand Up @@ -336,8 +337,8 @@ describe('SearchAdapter', () => {
expect(compactResult.success).toBe(true);
expect(verboseResult.success).toBe(true);

const compactTokens = compactResult.data?.tokenEstimate as number;
const verboseTokens = verboseResult.data?.tokenEstimate as number;
const compactTokens = compactResult.metadata?.tokens as number;
const verboseTokens = verboseResult.metadata?.tokens as number;

expect(verboseTokens).toBeGreaterThan(compactTokens);
});
Expand All @@ -354,8 +355,8 @@ describe('SearchAdapter', () => {
);

expect(result.success).toBe(true);
expect(result.data?.resultCount).toBe(0);
expect(result.data?.results as string).toContain('No results');
expect(result.metadata?.results_total).toBe(0);
expect(result.data?.content as string).toContain('No results');
});
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -373,10 +373,13 @@ describe('StatusAdapter', () => {
it('should log completion', async () => {
await adapter.execute({ section: 'summary' }, mockExecutionContext);

expect(mockExecutionContext.logger.info).toHaveBeenCalledWith('Status check completed', {
section: 'summary',
format: 'compact',
});
expect(mockExecutionContext.logger.info).toHaveBeenCalledWith(
'Status check completed',
expect.objectContaining({
section: 'summary',
format: 'compact',
})
);
});
});
});
Expand Down
6 changes: 3 additions & 3 deletions packages/mcp-server/src/adapters/adapter-registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -146,9 +146,9 @@ export class AdapterRegistry {
const startTime = Date.now();
const result = await adapter.execute(args, context);

// Add execution time if not present
if (result.success && result.metadata) {
result.metadata.executionTime = Date.now() - startTime;
// Ensure duration is tracked (adapters should set this, but fallback here)
if (result.success && result.metadata && !result.metadata.duration_ms) {
result.metadata.duration_ms = Date.now() - startTime;
}

return result;
Expand Down
105 changes: 63 additions & 42 deletions packages/mcp-server/src/adapters/built-in/github-adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -291,13 +291,16 @@ export class GitHubAdapter extends ToolAdapter {
}

try {
const startTime = Date.now();
context.logger.debug('Executing GitHub action', { action, query, number });

let content: string;
let resultsTotal = 0;
let resultsReturned = 0;

switch (action) {
case 'search':
content = await this.searchGitHub(
case 'search': {
const result = await this.searchGitHub(
query as string,
{
type: type as 'issue' | 'pull_request' | undefined,
Expand All @@ -308,15 +311,28 @@ export class GitHubAdapter extends ToolAdapter {
},
format
);
content = result.content;
resultsTotal = result.resultsTotal;
resultsReturned = result.resultsReturned;
break;
}
case 'context':
content = await this.getIssueContext(number as number, format);
resultsTotal = 1;
resultsReturned = 1;
break;
case 'related':
content = await this.getRelated(number as number, limit, format);
case 'related': {
const result = await this.getRelated(number as number, limit, format);
content = result.content;
resultsTotal = result.resultsTotal;
resultsReturned = result.resultsReturned;
break;
}
}

const duration_ms = Date.now() - startTime;
const tokens = estimateTokensForText(content);

return {
success: true,
data: {
Expand All @@ -325,6 +341,14 @@ export class GitHubAdapter extends ToolAdapter {
format,
content,
},
metadata: {
tokens,
duration_ms,
timestamp: new Date().toISOString(),
cached: false,
results_total: resultsTotal,
results_returned: resultsReturned,
},
};
} catch (error) {
context.logger.error('GitHub action failed', { error });
Expand Down Expand Up @@ -370,35 +394,27 @@ export class GitHubAdapter extends ToolAdapter {
query: string,
options: GitHubSearchOptions,
format: string
): Promise<string> {
): Promise<{ content: string; resultsTotal: number; resultsReturned: number }> {
const indexer = await this.ensureGitHubIndexer();

// Debug logging to understand what's happening
console.log(`[GitHub Search] Query: "${query}", Options:`, JSON.stringify(options, null, 2));

const results = await indexer.search(query, options);

console.log(`[GitHub Search] Found ${results.length} results`);
if (results.length > 0) {
console.log(`[GitHub Search] First result:`, {
title: results[0].document.title,
number: results[0].document.number,
score: results[0].score,
});
}

if (results.length === 0) {
const noResultsMsg =
const content =
'## GitHub Search Results\n\nNo matching issues or PRs found. Try:\n- Using different keywords\n- Removing filters (type, state, labels)\n- Re-indexing GitHub data with "dev gh index"';
const tokens = estimateTokensForText(noResultsMsg);
return `${noResultsMsg}\n\n🪙 ~${tokens} tokens`;
return { content, resultsTotal: 0, resultsReturned: 0 };
}

if (format === 'verbose') {
return this.formatSearchVerbose(query, results, options);
}
const content =
format === 'verbose'
? this.formatSearchVerbose(query, results, options)
: this.formatSearchCompact(query, results, options);

return this.formatSearchCompact(query, results, options);
return {
content,
resultsTotal: results.length,
resultsReturned: Math.min(results.length, options.limit ?? this.defaultLimit),
};
}

/**
Expand Down Expand Up @@ -439,7 +455,11 @@ export class GitHubAdapter extends ToolAdapter {
/**
* Find related issues and PRs
*/
private async getRelated(number: number, limit: number, format: string): Promise<string> {
private async getRelated(
number: number,
limit: number,
format: string
): Promise<{ content: string; resultsTotal: number; resultsReturned: number }> {
// First get the main issue/PR using the same logic as getIssueContext
const indexer = await this.ensureGitHubIndexer();

Expand Down Expand Up @@ -468,14 +488,23 @@ export class GitHubAdapter extends ToolAdapter {
const related = relatedResults.filter((r) => r.document.number !== number).slice(0, limit);

if (related.length === 0) {
return `## Related Issues/PRs\n\n**#${number}: ${mainDoc.title}**\n\nNo related issues or PRs found.`;
return {
content: `## Related Issues/PRs\n\n**#${number}: ${mainDoc.title}**\n\nNo related issues or PRs found.`,
resultsTotal: 0,
resultsReturned: 0,
};
}

if (format === 'verbose') {
return this.formatRelatedVerbose(mainDoc, related);
}
const content =
format === 'verbose'
? this.formatRelatedVerbose(mainDoc, related)
: this.formatRelatedCompact(mainDoc, related);

return this.formatRelatedCompact(mainDoc, related);
return {
content,
resultsTotal: related.length,
resultsReturned: related.length,
};
}

/**
Expand Down Expand Up @@ -513,9 +542,7 @@ export class GitHubAdapter extends ToolAdapter {
lines.push('', `_...and ${results.length - 5} more results_`);
}

const content = lines.join('\n');
const tokens = estimateTokensForText(content);
return `${content}\n\n🪙 ~${tokens} tokens`;
return lines.join('\n');
}

/**
Expand Down Expand Up @@ -559,9 +586,7 @@ export class GitHubAdapter extends ToolAdapter {
lines.push('');
}

const content = lines.join('\n');
const tokens = estimateTokensForText(content);
return `${content}\n\n🪙 ~${tokens} tokens`;
return lines.join('\n');
}

/**
Expand All @@ -588,9 +613,7 @@ export class GitHubAdapter extends ToolAdapter {
`**URL:** ${doc.url}`,
].filter(Boolean) as string[];

const content = lines.join('\n');
const tokens = estimateTokensForText(content);
return `${content}\n\n🪙 ~${tokens} tokens`;
return lines.join('\n');
}

/**
Expand Down Expand Up @@ -634,9 +657,7 @@ export class GitHubAdapter extends ToolAdapter {
`**URL:** ${doc.url}`,
].filter(Boolean) as string[];

const content = lines.join('\n');
const tokens = estimateTokensForText(content);
return `${content}\n\n🪙 ~${tokens} tokens`;
return lines.join('\n');
}

/**
Expand Down
Loading