@nanocollective/nanocoder
Version:
A local-first CLI coding agent that brings the power of agentic coding tools like Claude Code and Gemini CLI to local models or controlled APIs like OpenRouter
147 lines • 6.35 kB
JavaScript
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
/**
* Git Log Tool
*
* View commit history with various filters.
*/
import { Box, Text } from 'ink';
import { useTheme } from '../../hooks/useTheme.js';
import { jsonSchema, tool } from '../../types/core.js';
import { getCommits, getCurrentBranch } from './utils.js';
// ============================================================================
// Execution
// ============================================================================
const executeGitLog = async (args) => {
try {
const count = Math.min(args.count || 10, 50); // Cap at 50
const branch = args.branch || (await getCurrentBranch());
const commits = await getCommits({
count,
file: args.file,
author: args.author,
since: args.since,
grep: args.grep,
});
if (commits.length === 0) {
const filters = [];
if (args.author)
filters.push(`author: ${args.author}`);
if (args.since)
filters.push(`since: ${args.since}`);
if (args.grep)
filters.push(`grep: ${args.grep}`);
if (args.file)
filters.push(`file: ${args.file}`);
if (filters.length > 0) {
return `No commits found matching filters: ${filters.join(', ')}`;
}
return 'No commits found.';
}
const lines = [];
lines.push(`Showing ${commits.length} commit(s) on ${branch}:`);
lines.push('');
for (const commit of commits) {
lines.push(`${commit.shortHash} ${commit.subject} (${commit.relativeDate})`);
lines.push(` Author: ${commit.author} <${commit.email}>`);
}
return lines.join('\n');
}
catch (error) {
return `Error: ${error instanceof Error ? error.message : 'Unknown error'}`;
}
};
// ============================================================================
// Tool Definition
// ============================================================================
const gitLogCoreTool = tool({
description: 'View git commit history. Supports filtering by count, file, author, date, and commit message search.',
inputSchema: jsonSchema({
type: 'object',
properties: {
count: {
type: 'number',
description: 'Number of commits to show (default: 10, max: 50)',
},
file: {
type: 'string',
description: 'Show history for a specific file',
},
author: {
type: 'string',
description: 'Filter by author name or email',
},
since: {
type: 'string',
description: 'Show commits after date (e.g., "2024-01-01", "1 week ago")',
},
grep: {
type: 'string',
description: 'Search commit messages for a pattern',
},
branch: {
type: 'string',
description: 'Show commits on a specific branch (default: current)',
},
},
required: [],
}),
// AUTO - read-only operation, never needs approval
needsApproval: () => false,
execute: async (args, _options) => {
return await executeGitLog(args);
},
});
// ============================================================================
// Formatter
// ============================================================================
function GitLogFormatter({ args, result, }) {
const { colors } = useTheme();
// Parse result for display
let commitCount = 0;
let branch = '';
let dateRange = '';
if (result) {
const countMatch = result.match(/Showing (\d+) commit/);
if (countMatch)
commitCount = parseInt(countMatch[1], 10);
const branchMatch = result.match(/on (.+):/);
if (branchMatch)
branch = branchMatch[1];
// Extract date range from commits (looking for relative dates like "2 hours ago")
const dateMatches = result.match(/\((\d+\s+\w+\s+ago|today|yesterday)\)/gi);
if (dateMatches && dateMatches.length > 0) {
const first = dateMatches[0].replace(/[()]/g, '');
const last = dateMatches[dateMatches.length - 1].replace(/[()]/g, '');
if (first !== last) {
dateRange = `${last} → ${first}`;
}
else {
dateRange = first;
}
}
}
// Build filter description
const filters = [];
if (args.author)
filters.push(`author: ${args.author}`);
if (args.since)
filters.push(`since: ${args.since}`);
if (args.grep)
filters.push(`grep: "${args.grep}"`);
if (args.file)
filters.push(`file: ${args.file}`);
return (_jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [_jsx(Text, { color: colors.tool, children: "\u2692 git_log" }), branch && (_jsxs(Box, { children: [_jsx(Text, { color: colors.secondary, children: "Branch: " }), _jsx(Text, { color: colors.primary, children: branch })] })), commitCount > 0 && (_jsxs(Box, { children: [_jsx(Text, { color: colors.secondary, children: "Showing: " }), _jsxs(Text, { color: colors.text, children: [commitCount, " commits"] })] })), dateRange && (_jsxs(Box, { children: [_jsx(Text, { color: colors.secondary, children: "Range: " }), _jsx(Text, { color: colors.text, children: dateRange })] })), filters.length > 0 && (_jsxs(Box, { children: [_jsx(Text, { color: colors.secondary, children: "Filters: " }), _jsx(Text, { color: colors.text, children: filters.join(', ') })] })), commitCount === 0 && result && (_jsx(Box, { children: _jsx(Text, { color: colors.warning, children: "No commits found" }) }))] }));
}
const formatter = (args, result) => {
return _jsx(GitLogFormatter, { args: args, result: result });
};
// ============================================================================
// Export
// ============================================================================
export const gitLogTool = {
name: 'git_log',
tool: gitLogCoreTool,
formatter,
readOnly: true,
};
//# sourceMappingURL=git-log.js.map