@spyder1211/cc-history
Version:
Claude Code usage history viewer
274 lines • 13.4 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.UI = void 0;
const inquirer_1 = __importDefault(require("inquirer"));
const chalk_1 = __importDefault(require("chalk"));
class UI {
/**
* メインメニューを表示
*/
async showMainMenu(threads, stats) {
while (true) {
console.clear();
this.showHeader();
this.showMessageList(threads);
this.showStats(stats);
const { action } = await inquirer_1.default.prompt([
{
type: 'list',
name: 'action',
message: 'Select an option:',
choices: [
...threads.map((thread, index) => ({
name: this.formatMessageChoice(thread, index + 1),
value: `message_${index}`
})),
new inquirer_1.default.Separator(),
{ name: 'Exit', value: 'quit' }
],
pageSize: 15
}
]);
if (action === 'quit') {
break;
}
if (action.startsWith('message_')) {
const index = parseInt(action.split('_')[1]);
await this.showMessageDetail(threads[index]);
}
}
}
/**
* ヘッダーを表示
*/
showHeader() {
const today = new Date().toISOString().split('T')[0];
console.log(chalk_1.default.cyan('┌─────────────────────────────────────────────────────────────────────────────────┐'));
console.log(chalk_1.default.cyan(`│ Claude Code Message History - ${today} │`));
console.log(chalk_1.default.cyan('└─────────────────────────────────────────────────────────────────────────────────┘'));
console.log('');
}
/**
* メッセージ一覧を表示
*/
showMessageList(threads) {
if (threads.length === 0) {
console.log(chalk_1.default.yellow('No messages found for today.'));
return;
}
threads.forEach((thread, index) => {
const time = thread.startTime.toLocaleTimeString('en-US', {
hour: '2-digit',
minute: '2-digit',
second: '2-digit',
hour12: false
});
const projectName = this.getProjectName(thread.user.cwd);
const content = this.truncateMessage(thread.user.message.content, 60);
console.log(`${chalk_1.default.gray(`${index + 1}.`)} ${chalk_1.default.blue(time)} ${chalk_1.default.green(`[${projectName}]`)}`);
console.log(` ${chalk_1.default.white('💬')} ${content}`);
console.log(` ${chalk_1.default.cyan('🔄')} ${thread.responses.length} exchanges | ${chalk_1.default.magenta('🛠️')} ${thread.tools} tools | ${chalk_1.default.yellow('💰')} $${thread.cost.toFixed(6)}`);
console.log('');
});
}
/**
* 統計情報を表示
*/
showStats(stats) {
console.log(chalk_1.default.gray('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
console.log('');
console.log(chalk_1.default.bold('📈 Today\'s Statistics:'));
console.log(` ${chalk_1.default.blue('👤')} User messages: ${stats.userMessages}`);
console.log(` ${chalk_1.default.cyan('🔄')} Total exchanges: ${stats.totalExchanges} (avg ${(stats.totalExchanges / Math.max(stats.userMessages, 1)).toFixed(1)}/question)`);
console.log(` ${chalk_1.default.magenta('🛠️')} Tools used: ${stats.toolsUsed}`);
console.log(` ${chalk_1.default.white('🔢')} Total tokens: input ${stats.totalTokens.input} | output ${stats.totalTokens.output} | cache ${stats.totalTokens.cacheCreation + stats.totalTokens.cacheRead}`);
console.log(` ${chalk_1.default.yellow('💰')} Estimated cost: $${stats.totalCost.toFixed(5)}`);
if (stats.userMessages > 0) {
const startTime = stats.startTime.toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit', hour12: false });
const endTime = stats.endTime.toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit', hour12: false });
console.log(` ${chalk_1.default.gray('⏰')} Active time: ${startTime} - ${endTime}`);
}
console.log('');
}
/**
* メッセージ詳細を表示
*/
async showMessageDetail(thread) {
while (true) {
console.clear();
this.showDetailHeader(thread);
this.showUserMessage(thread);
this.showAssistantResponses(thread);
this.showThreadStats(thread);
const { action } = await inquirer_1.default.prompt([
{
type: 'list',
name: 'action',
message: 'Select an option:',
choices: [
{ name: 'Back', value: 'back' },
{ name: 'Exit', value: 'quit' }
]
}
]);
if (action === 'back') {
break;
}
if (action === 'quit') {
process.exit(0);
}
}
}
/**
* 詳細ヘッダーを表示
*/
showDetailHeader(thread) {
const time = thread.startTime.toLocaleTimeString('en-US', {
hour: '2-digit',
minute: '2-digit',
second: '2-digit',
hour12: false
});
const date = thread.startTime.toISOString().split('T')[0];
console.log(chalk_1.default.cyan('┌─────────────────────────────────────────────────────────────────────────────────┐'));
console.log(chalk_1.default.cyan(`│ Message Details - ${date} ${time} │`));
console.log(chalk_1.default.cyan('└─────────────────────────────────────────────────────────────────────────────────┘'));
console.log('');
}
/**
* ユーザメッセージを表示
*/
showUserMessage(thread) {
const projectName = this.getProjectName(thread.user.cwd);
console.log(chalk_1.default.bold(`📝 User Message [${projectName}]:`));
console.log(chalk_1.default.white(thread.user.message.content));
console.log('');
}
/**
* アシスタントの応答を表示
*/
showAssistantResponses(thread) {
console.log(chalk_1.default.bold(`🤖 Assistant Response History (${thread.responses.length} exchanges):`));
console.log('');
thread.responses.forEach((response, index) => {
const time = new Date(response.timestamp).toLocaleTimeString('en-US', {
hour: '2-digit',
minute: '2-digit',
second: '2-digit',
hour12: false
});
const usage = response.message.usage;
const cost = ((usage.input_tokens || 0) * (3.00 / 1000000) +
(usage.output_tokens || 0) * (15.00 / 1000000) +
((usage.cache_creation_input_tokens || 0) + (usage.cache_read_input_tokens || 0)) * (3.75 / 1000000));
console.log(`${chalk_1.default.gray(`${index + 1}.`)} ${chalk_1.default.blue(time)} ${this.getResponseTypeIcon(response.message.content)}`);
// 応答内容を表示
this.showResponseContent(response.message.content);
console.log(` ${chalk_1.default.gray('📊')} Input: ${usage.input_tokens || 0} | Output: ${usage.output_tokens || 0} | Cache: ${(usage.cache_creation_input_tokens || 0) + (usage.cache_read_input_tokens || 0)} | ${chalk_1.default.yellow('💰')} $${cost.toFixed(6)}`);
console.log('');
});
}
/**
* 応答内容を表示
*/
showResponseContent(content) {
for (const item of content) {
if (item.type === 'text') {
console.log(` ${chalk_1.default.white('💬')} ${this.truncateMessage(item.text || '', 100)}`);
}
else if (item.type === 'tool_use') {
console.log(` ${chalk_1.default.magenta('🛠️')} ${item.name}${item.input ? ` - ${this.formatToolInput(item.input)}` : ''}`);
}
}
}
/**
* スレッド統計を表示
*/
showThreadStats(thread) {
const duration = Math.round((thread.endTime.getTime() - thread.startTime.getTime()) / 1000);
const toolBreakdown = this.getToolBreakdown(thread.responses);
console.log(chalk_1.default.gray('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
console.log('');
console.log(chalk_1.default.bold('📈 Question Statistics:'));
console.log(` ${chalk_1.default.cyan('🔄')} Total exchanges: ${thread.responses.length}`);
console.log(` ${chalk_1.default.magenta('🛠️')} Tools used: ${thread.tools}${toolBreakdown}`);
console.log(` ${chalk_1.default.white('🔢')} Total tokens: input ${thread.totalTokens.input} | output ${thread.totalTokens.output} | cache ${thread.totalTokens.cacheCreation + thread.totalTokens.cacheRead}`);
console.log(` ${chalk_1.default.yellow('💰')} Estimated cost: $${thread.cost.toFixed(6)}`);
console.log(` ${chalk_1.default.gray('⏰')} Processing time: ${duration}s`);
console.log('');
}
/**
* ヘルプを表示
*/
showHelp() {
console.log(chalk_1.default.bold('Claude Code History Viewer'));
console.log('');
console.log('Usage:');
console.log(' cc-history Show today\'s message history');
console.log(' cc-history --help Show this help');
console.log('');
console.log('Features:');
console.log(' • Display today\'s user messages list');
console.log(' • Show detailed view by selecting a message');
console.log(' • Assistant response history and tool usage');
console.log(' • Token usage and cost analysis');
console.log('');
}
// ユーティリティメソッド
formatMessageChoice(thread, index) {
const time = thread.startTime.toLocaleTimeString('en-US', {
hour: '2-digit',
minute: '2-digit',
hour12: false
});
const projectName = this.getProjectName(thread.user.cwd);
const content = this.truncateMessage(thread.user.message.content, 50);
return `${index}. ${time} [${projectName}] ${content}`;
}
getProjectName(cwd) {
const parts = cwd.split('/');
return parts[parts.length - 1] || 'unknown';
}
truncateMessage(message, maxLength) {
if (message.length <= maxLength)
return message;
return message.substring(0, maxLength) + '...';
}
getResponseTypeIcon(content) {
const hasText = content.some(c => c.type === 'text');
const hasTools = content.some(c => c.type === 'tool_use');
if (hasText && hasTools)
return '[💬+🛠️]';
if (hasTools)
return '[🛠️]';
return '[💬]';
}
formatToolInput(input) {
if (typeof input === 'string') {
return this.truncateMessage(input, 50);
}
return this.truncateMessage(JSON.stringify(input), 50);
}
getToolBreakdown(responses) {
const tools = {};
for (const response of responses) {
const content = response.message.content;
if (content && Array.isArray(content)) {
for (const item of content) {
if (item.type === 'tool_use' && item.name) {
tools[item.name] = (tools[item.name] || 0) + 1;
}
}
}
}
const toolList = Object.entries(tools)
.map(([name, count]) => `${name}: ${count}x`)
.join(', ');
return toolList ? ` (${toolList})` : '';
}
}
exports.UI = UI;
//# sourceMappingURL=ui.js.map