@ccview/cli 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,3 @@
1
+ import { Command } from 'commander';
2
+ export declare const configCommand: Command;
3
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/commands/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAuBnC,eAAO,MAAM,aAAa,SA+CtB,CAAA"}
@@ -0,0 +1,66 @@
1
+ import { Command } from 'commander';
2
+ import { readFile, writeFile, mkdir } from 'node:fs/promises';
3
+ import { join } from 'node:path';
4
+ import { homedir } from 'node:os';
5
+ import { printBox, printError, printSuccess, printInfo } from '../utils/terminal-ui.js';
6
+ const CONFIG_DIR = join(homedir(), '.ccview');
7
+ const CONFIG_PATH = join(CONFIG_DIR, 'config.json');
8
+ async function loadConfig() {
9
+ try {
10
+ const raw = await readFile(CONFIG_PATH, 'utf-8');
11
+ return JSON.parse(raw);
12
+ }
13
+ catch {
14
+ return {};
15
+ }
16
+ }
17
+ async function saveConfig(config) {
18
+ await mkdir(CONFIG_DIR, { recursive: true });
19
+ await writeFile(CONFIG_PATH, JSON.stringify(config, null, 2) + '\n', 'utf-8');
20
+ }
21
+ export const configCommand = new Command('config')
22
+ .description('View or update ccview configuration')
23
+ .argument('[action]', 'Action: "set" to update a key')
24
+ .argument('[key]', 'Config key to set')
25
+ .argument('[value]', 'Value to set')
26
+ .action(async (action, key, value) => {
27
+ try {
28
+ if (!action) {
29
+ const config = await loadConfig();
30
+ const entries = Object.entries(config);
31
+ if (entries.length === 0) {
32
+ printInfo('No configuration set. Use "ccview config set <key> <value>" to set values.');
33
+ return;
34
+ }
35
+ const lines = entries.map(([k, v]) => `${k}: ${JSON.stringify(v)}`);
36
+ printBox('ccview config', lines);
37
+ return;
38
+ }
39
+ if (action === 'set') {
40
+ if (!key || value === undefined) {
41
+ printError('Usage: ccview config set <key> <value>');
42
+ process.exit(1);
43
+ }
44
+ const config = await loadConfig();
45
+ // Try to parse value as JSON, fall back to string
46
+ let parsed;
47
+ try {
48
+ parsed = JSON.parse(value);
49
+ }
50
+ catch {
51
+ parsed = value;
52
+ }
53
+ config[key] = parsed;
54
+ await saveConfig(config);
55
+ printSuccess(`Set ${key} = ${JSON.stringify(parsed)}`);
56
+ return;
57
+ }
58
+ printError(`Unknown action "${action}". Use "set" or omit for current config.`);
59
+ process.exit(1);
60
+ }
61
+ catch (err) {
62
+ printError(err instanceof Error ? err.message : String(err));
63
+ process.exit(1);
64
+ }
65
+ });
66
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/commands/config.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import { Command } from 'commander';
2
+ export declare const exportCommand: Command;
3
+ //# sourceMappingURL=export.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"export.d.ts","sourceRoot":"","sources":["../../src/commands/export.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAiEnC,eAAO,MAAM,aAAa,SAyDtB,CAAA"}
@@ -0,0 +1,108 @@
1
+ import { Command } from 'commander';
2
+ import { writeFile } from 'node:fs/promises';
3
+ import { openDatabase, listSessions, listStepsBySession, } from '@ccview/core';
4
+ import { printError, printSuccess } from '../utils/terminal-ui.js';
5
+ function sessionsToCSV(sessions) {
6
+ const headers = [
7
+ 'id', 'project_name', 'started_at', 'ended_at',
8
+ 'duration_seconds', 'tokens_in', 'tokens_out', 'cost_usd',
9
+ 'total_steps', 'tool_calls', 'errors', 'model', 'summary',
10
+ ];
11
+ const rows = sessions.map((s) => [
12
+ s.id,
13
+ s.projectName ?? '',
14
+ s.startedAt.toISOString(),
15
+ s.endedAt?.toISOString() ?? '',
16
+ String(s.durationSeconds ?? ''),
17
+ String(s.totalTokensIn),
18
+ String(s.totalTokensOut),
19
+ String(s.totalCostUsd),
20
+ String(s.totalSteps),
21
+ String(s.toolCallCount),
22
+ String(s.errorCount),
23
+ s.model ?? '',
24
+ csvEscape(s.summary ?? ''),
25
+ ]);
26
+ return [headers.join(','), ...rows.map((r) => r.join(','))].join('\n');
27
+ }
28
+ function stepsToCSV(steps) {
29
+ const headers = [
30
+ 'id', 'session_id', 'step_index', 'type', 'subtype',
31
+ 'tokens_in', 'tokens_out', 'duration_ms', 'tool_name',
32
+ 'is_error', 'created_at',
33
+ ];
34
+ const rows = steps.map((s) => [
35
+ s.id,
36
+ s.sessionId,
37
+ String(s.stepIndex),
38
+ s.type,
39
+ s.subtype ?? '',
40
+ String(s.tokensIn),
41
+ String(s.tokensOut),
42
+ String(s.durationMs ?? ''),
43
+ s.toolName ?? '',
44
+ String(s.isError),
45
+ s.createdAt.toISOString(),
46
+ ]);
47
+ return [headers.join(','), ...rows.map((r) => r.join(','))].join('\n');
48
+ }
49
+ function csvEscape(value) {
50
+ if (value.includes(',') || value.includes('"') || value.includes('\n')) {
51
+ return '"' + value.replace(/"/g, '""') + '"';
52
+ }
53
+ return value;
54
+ }
55
+ export const exportCommand = new Command('export')
56
+ .description('Export session data as JSON or CSV')
57
+ .option('--format <fmt>', 'Output format: json or csv', 'json')
58
+ .option('--sessions-only', 'Export sessions only (no steps)')
59
+ .option('--project <name>', 'Filter by project name')
60
+ .option('--output <path>', 'Write to file instead of stdout')
61
+ .action(async (opts) => {
62
+ try {
63
+ const db = openDatabase();
64
+ const filters = { limit: 10000 };
65
+ if (opts.project)
66
+ filters.project = opts.project;
67
+ const sessions = listSessions(db, filters);
68
+ let output;
69
+ if (opts.format === 'csv') {
70
+ if (opts.sessionsOnly) {
71
+ output = sessionsToCSV(sessions);
72
+ }
73
+ else {
74
+ const allSteps = [];
75
+ for (const session of sessions) {
76
+ const steps = listStepsBySession(db, session.id);
77
+ allSteps.push(...steps);
78
+ }
79
+ output = sessionsToCSV(sessions) + '\n\n--- Steps ---\n\n' + stepsToCSV(allSteps);
80
+ }
81
+ }
82
+ else {
83
+ if (opts.sessionsOnly) {
84
+ output = JSON.stringify(sessions, null, 2);
85
+ }
86
+ else {
87
+ const data = sessions.map((session) => ({
88
+ ...session,
89
+ steps: listStepsBySession(db, session.id),
90
+ }));
91
+ output = JSON.stringify(data, null, 2);
92
+ }
93
+ }
94
+ if (opts.output) {
95
+ await writeFile(opts.output, output, 'utf-8');
96
+ printSuccess(`Exported to ${opts.output}`);
97
+ }
98
+ else {
99
+ process.stdout.write(output + '\n');
100
+ }
101
+ db.close();
102
+ }
103
+ catch (err) {
104
+ printError(err instanceof Error ? err.message : String(err));
105
+ process.exit(1);
106
+ }
107
+ });
108
+ //# sourceMappingURL=export.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"export.js","sourceRoot":"","sources":["../../src/commands/export.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import { Command } from 'commander';
2
+ export declare const initCommand: Command;
3
+ //# sourceMappingURL=init.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAcnC,eAAO,MAAM,WAAW,SAmFpB,CAAA"}
@@ -0,0 +1,74 @@
1
+ import { Command } from 'commander';
2
+ import ora from 'ora';
3
+ import { scanClaudeDirectory, parseSession, openDatabase, indexSession, sessionExistsByHash, } from '@ccview/core';
4
+ import { DEFAULT_PRICING } from '@ccview/core';
5
+ import { estimateCost } from '@ccview/core';
6
+ import { printBox, printError, printSuccess } from '../utils/terminal-ui.js';
7
+ export const initCommand = new Command('init')
8
+ .description('Scan ~/.claude/ and index all sessions')
9
+ .action(async () => {
10
+ const spinner = ora('Scanning ~/.claude/...').start();
11
+ try {
12
+ const db = openDatabase();
13
+ const result = await scanClaudeDirectory({
14
+ forceRescan: true,
15
+ onProgress(current, total) {
16
+ spinner.text = `Indexing session ${current}/${total}...`;
17
+ },
18
+ });
19
+ spinner.succeed('Scan complete');
20
+ // Now actually index sessions by re-scanning and indexing each
21
+ const { findSessionFiles } = await import('@ccview/core');
22
+ const { join } = await import('node:path');
23
+ const { homedir } = await import('node:os');
24
+ const projectsDir = join(homedir(), '.claude', 'projects');
25
+ const files = await findSessionFiles(projectsDir);
26
+ let indexed = 0;
27
+ let skipped = 0;
28
+ const errors = [];
29
+ const indexSpinner = ora('Indexing sessions...').start();
30
+ for (let i = 0; i < files.length; i++) {
31
+ const file = files[i];
32
+ indexSpinner.text = `Indexing ${i + 1}/${files.length}...`;
33
+ try {
34
+ if (sessionExistsByHash(db, file.hash)) {
35
+ skipped++;
36
+ continue;
37
+ }
38
+ const parsed = await parseSession(file.filePath);
39
+ if (parsed.session.model) {
40
+ const pricing = DEFAULT_PRICING[parsed.session.model];
41
+ parsed.session.totalCostUsd = estimateCost(parsed.session.totalTokensIn, parsed.session.totalTokensOut, pricing);
42
+ }
43
+ indexSession(db, parsed, file.filePath, file.hash);
44
+ indexed++;
45
+ }
46
+ catch (err) {
47
+ errors.push(err instanceof Error ? err.message : String(err));
48
+ }
49
+ }
50
+ indexSpinner.succeed('Indexing complete');
51
+ printBox('ccview init — Results', [
52
+ `Sessions found: ${files.length}`,
53
+ `Sessions indexed: ${indexed}`,
54
+ `Sessions skipped: ${skipped}`,
55
+ `Errors: ${errors.length}`,
56
+ ]);
57
+ if (errors.length > 0) {
58
+ for (const e of errors.slice(0, 5)) {
59
+ printError(e);
60
+ }
61
+ if (errors.length > 5) {
62
+ printError(`...and ${errors.length - 5} more errors`);
63
+ }
64
+ }
65
+ db.close();
66
+ printSuccess('Database initialized at ~/.ccview/ccview.db');
67
+ }
68
+ catch (err) {
69
+ spinner.fail('Init failed');
70
+ printError(err instanceof Error ? err.message : String(err));
71
+ process.exit(1);
72
+ }
73
+ });
74
+ //# sourceMappingURL=init.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"init.js","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import { Command } from 'commander';
2
+ export declare const serveCommand: Command;
3
+ //# sourceMappingURL=serve.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"serve.d.ts","sourceRoot":"","sources":["../../src/commands/serve.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAKnC,eAAO,MAAM,YAAY,SAgDrB,CAAA"}
@@ -0,0 +1,51 @@
1
+ import { Command } from 'commander';
2
+ import { createRequire } from 'node:module';
3
+ import { spawn } from 'node:child_process';
4
+ import { printError, printInfo, printSuccess } from '../utils/terminal-ui.js';
5
+ export const serveCommand = new Command('serve')
6
+ .description('Start the ccview web dashboard')
7
+ .option('--port <n>', 'Port number', '3200')
8
+ .option('--no-open', 'Do not open browser automatically')
9
+ .action(async (opts) => {
10
+ try {
11
+ const port = parseInt(opts.port, 10);
12
+ // Risolve @ccview/server sia in monorepo (workspace symlink) che in npm install
13
+ const require = createRequire(import.meta.url);
14
+ let serverEntry;
15
+ try {
16
+ serverEntry = require.resolve('@ccview/server');
17
+ }
18
+ catch {
19
+ printError('Could not find @ccview/server. Run "pnpm build" first.');
20
+ process.exit(1);
21
+ }
22
+ printInfo(`Starting ccview server on port ${port}...`);
23
+ const child = spawn('node', [serverEntry], {
24
+ env: { ...process.env, PORT: String(port) },
25
+ stdio: 'inherit',
26
+ });
27
+ child.on('error', (err) => {
28
+ printError(`Failed to start server: ${err.message}`);
29
+ process.exit(1);
30
+ });
31
+ if (opts.open) {
32
+ setTimeout(async () => {
33
+ try {
34
+ const openModule = await import('open');
35
+ await openModule.default(`http://localhost:${port}`);
36
+ printSuccess(`Opened http://localhost:${port} in browser`);
37
+ }
38
+ catch {
39
+ printInfo(`Open http://localhost:${port} in your browser`);
40
+ }
41
+ }, 1500);
42
+ }
43
+ process.on('SIGINT', () => { child.kill('SIGINT'); });
44
+ process.on('SIGTERM', () => { child.kill('SIGTERM'); });
45
+ }
46
+ catch (err) {
47
+ printError(err instanceof Error ? err.message : String(err));
48
+ process.exit(1);
49
+ }
50
+ });
51
+ //# sourceMappingURL=serve.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"serve.js","sourceRoot":"","sources":["../../src/commands/serve.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AACnC,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAC3C,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAA;AAC1C,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAA;AAE7E,MAAM,CAAC,MAAM,YAAY,GAAG,IAAI,OAAO,CAAC,OAAO,CAAC;KAC7C,WAAW,CAAC,gCAAgC,CAAC;KAC7C,MAAM,CAAC,YAAY,EAAE,aAAa,EAAE,MAAM,CAAC;KAC3C,MAAM,CAAC,WAAW,EAAE,mCAAmC,CAAC;KACxD,MAAM,CAAC,KAAK,EAAE,IAAqC,EAAE,EAAE;IACtD,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC,CAAA;QAEpC,gFAAgF;QAChF,MAAM,OAAO,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;QAC9C,IAAI,WAAmB,CAAA;QACvB,IAAI,CAAC;YACH,WAAW,GAAG,OAAO,CAAC,OAAO,CAAC,gBAAgB,CAAC,CAAA;QACjD,CAAC;QAAC,MAAM,CAAC;YACP,UAAU,CAAC,wDAAwD,CAAC,CAAA;YACpE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;QACjB,CAAC;QAED,SAAS,CAAC,kCAAkC,IAAI,KAAK,CAAC,CAAA;QAEtD,MAAM,KAAK,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,WAAW,CAAC,EAAE;YACzC,GAAG,EAAE,EAAE,GAAG,OAAO,CAAC,GAAG,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,EAAE;YAC3C,KAAK,EAAE,SAAS;SACjB,CAAC,CAAA;QAEF,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE;YACxB,UAAU,CAAC,2BAA2B,GAAG,CAAC,OAAO,EAAE,CAAC,CAAA;YACpD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;QACjB,CAAC,CAAC,CAAA;QAEF,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;YACd,UAAU,CAAC,KAAK,IAAI,EAAE;gBACpB,IAAI,CAAC;oBACH,MAAM,UAAU,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,CAAA;oBACvC,MAAM,UAAU,CAAC,OAAO,CAAC,oBAAoB,IAAI,EAAE,CAAC,CAAA;oBACpD,YAAY,CAAC,2BAA2B,IAAI,aAAa,CAAC,CAAA;gBAC5D,CAAC;gBAAC,MAAM,CAAC;oBACP,SAAS,CAAC,yBAAyB,IAAI,kBAAkB,CAAC,CAAA;gBAC5D,CAAC;YACH,CAAC,EAAE,IAAI,CAAC,CAAA;QACV,CAAC;QAED,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,GAAG,EAAE,GAAG,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAA,CAAC,CAAC,CAAC,CAAA;QACpD,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,GAAG,EAAE,GAAG,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAA,CAAC,CAAC,CAAC,CAAA;IACxD,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,UAAU,CAAC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAA;QAC5D,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;IACjB,CAAC;AACH,CAAC,CAAC,CAAA"}
@@ -0,0 +1,3 @@
1
+ import { Command } from 'commander';
2
+ export declare const statsCommand: Command;
3
+ //# sourceMappingURL=stats.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stats.d.ts","sourceRoot":"","sources":["../../src/commands/stats.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAmCnC,eAAO,MAAM,YAAY,SAuCrB,CAAA"}
@@ -0,0 +1,55 @@
1
+ import { Command } from 'commander';
2
+ import { openDatabase, getOverviewStats } from '@ccview/core';
3
+ import { printBox, printError, formatTokens, formatCost, formatDuration, } from '../utils/terminal-ui.js';
4
+ function parseDateRange(opts) {
5
+ const now = new Date();
6
+ if (opts.today) {
7
+ const start = new Date(now.getFullYear(), now.getMonth(), now.getDate());
8
+ return { from: start, to: now, label: 'Today' };
9
+ }
10
+ if (opts.last) {
11
+ const match = opts.last.match(/^(\d+)d$/);
12
+ if (!match) {
13
+ throw new Error('Invalid --last format. Use e.g. "7d" for 7 days.');
14
+ }
15
+ const days = parseInt(match[1], 10);
16
+ const from = new Date(now.getTime() - days * 24 * 60 * 60 * 1000);
17
+ return { from, to: now, label: `Last ${days} days` };
18
+ }
19
+ return { label: 'All time' };
20
+ }
21
+ export const statsCommand = new Command('stats')
22
+ .description('Show usage statistics')
23
+ .option('--project <name>', 'Filter by project name')
24
+ .option('--last <Nd>', 'Show last N days (e.g. 7d)')
25
+ .option('--today', 'Show today only')
26
+ .action(async (opts) => {
27
+ try {
28
+ const db = openDatabase();
29
+ const { from, to, label } = parseDateRange(opts);
30
+ const stats = getOverviewStats(db, from, to);
31
+ const totalTokens = stats.totalTokensIn + stats.totalTokensOut;
32
+ const errorPct = Math.round(stats.errorRate * 100);
33
+ const lines = [
34
+ `Sessions: ${stats.totalSessions}`,
35
+ `Total tokens: ${formatTokens(totalTokens)} (in: ${formatTokens(stats.totalTokensIn)} out: ${formatTokens(stats.totalTokensOut)})`,
36
+ `Est. cost: ${formatCost(stats.totalCostUsd)}`,
37
+ `Files touched: ${stats.uniqueFilesTouched} unique`,
38
+ `Avg session: ${formatDuration(stats.avgSessionDuration)}`,
39
+ `Error rate: ${errorPct}%`,
40
+ ];
41
+ if (stats.topProject) {
42
+ lines.push(`Top project: ${stats.topProject}`);
43
+ }
44
+ const title = opts.project
45
+ ? `ccview stats \u2014 ${opts.project} \u2014 ${label}`
46
+ : `ccview stats \u2014 ${label}`;
47
+ printBox(title, lines);
48
+ db.close();
49
+ }
50
+ catch (err) {
51
+ printError(err instanceof Error ? err.message : String(err));
52
+ process.exit(1);
53
+ }
54
+ });
55
+ //# sourceMappingURL=stats.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stats.js","sourceRoot":"","sources":["../../src/commands/stats.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import { Command } from 'commander';
2
+ export declare const syncCommand: Command;
3
+ //# sourceMappingURL=sync.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sync.d.ts","sourceRoot":"","sources":["../../src/commands/sync.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAkInC,eAAO,MAAM,WAAW,SA+BpB,CAAA"}
@@ -0,0 +1,124 @@
1
+ import { Command } from 'commander';
2
+ import ora from 'ora';
3
+ import { watch } from 'node:fs';
4
+ import { join } from 'node:path';
5
+ import { homedir } from 'node:os';
6
+ import { openDatabase, findSessionFiles, parseSession, indexSession, sessionExistsByHash, computeFileHash, DEFAULT_PRICING, estimateCost, } from '@ccview/core';
7
+ import { printBox, printError, printSuccess, printInfo } from '../utils/terminal-ui.js';
8
+ async function syncSessions(opts) {
9
+ const db = openDatabase();
10
+ if (opts.rebuild) {
11
+ printInfo('Rebuilding database...');
12
+ db.exec('DELETE FROM file_impacts');
13
+ db.exec('DELETE FROM steps');
14
+ db.exec('DELETE FROM sessions');
15
+ db.exec('DELETE FROM projects');
16
+ }
17
+ const projectsDir = join(homedir(), '.claude', 'projects');
18
+ const files = await findSessionFiles(projectsDir);
19
+ let indexed = 0;
20
+ let skipped = 0;
21
+ const errors = [];
22
+ const spinner = ora('Syncing sessions...').start();
23
+ for (let i = 0; i < files.length; i++) {
24
+ const file = files[i];
25
+ spinner.text = `Syncing ${i + 1}/${files.length}...`;
26
+ try {
27
+ if (!opts.rebuild && sessionExistsByHash(db, file.hash)) {
28
+ skipped++;
29
+ continue;
30
+ }
31
+ const parsed = await parseSession(file.filePath);
32
+ if (parsed.session.model) {
33
+ const pricing = DEFAULT_PRICING[parsed.session.model];
34
+ parsed.session.totalCostUsd = estimateCost(parsed.session.totalTokensIn, parsed.session.totalTokensOut, pricing);
35
+ }
36
+ indexSession(db, parsed, file.filePath, file.hash);
37
+ indexed++;
38
+ }
39
+ catch (err) {
40
+ errors.push(err instanceof Error ? err.message : String(err));
41
+ }
42
+ }
43
+ spinner.succeed('Sync complete');
44
+ printBox('ccview sync — Results', [
45
+ `Sessions found: ${files.length}`,
46
+ `New indexed: ${indexed}`,
47
+ `Skipped: ${skipped}`,
48
+ `Errors: ${errors.length}`,
49
+ ]);
50
+ if (errors.length > 0) {
51
+ for (const e of errors.slice(0, 5)) {
52
+ printError(e);
53
+ }
54
+ }
55
+ db.close();
56
+ }
57
+ async function watchForChanges() {
58
+ const projectsDir = join(homedir(), '.claude', 'projects');
59
+ printInfo(`Watching ${projectsDir} for new sessions...`);
60
+ const watcher = watch(projectsDir, { recursive: true }, async (_event, filename) => {
61
+ if (!filename || !filename.endsWith('.jsonl'))
62
+ return;
63
+ printInfo(`Detected change: ${filename}`);
64
+ try {
65
+ const db = openDatabase();
66
+ const filePath = join(projectsDir, filename);
67
+ const hash = await computeFileHash(filePath);
68
+ if (sessionExistsByHash(db, hash)) {
69
+ db.close();
70
+ return;
71
+ }
72
+ const parsed = await parseSession(filePath);
73
+ if (parsed.session.model) {
74
+ const pricing = DEFAULT_PRICING[parsed.session.model];
75
+ parsed.session.totalCostUsd = estimateCost(parsed.session.totalTokensIn, parsed.session.totalTokensOut, pricing);
76
+ }
77
+ indexSession(db, parsed, filePath, hash);
78
+ printSuccess(`Indexed new session from ${filename}`);
79
+ db.close();
80
+ }
81
+ catch (err) {
82
+ printError(`Failed to index ${filename}: ${err instanceof Error ? err.message : String(err)}`);
83
+ }
84
+ });
85
+ process.on('SIGINT', () => {
86
+ watcher.close();
87
+ printInfo('Watch mode stopped');
88
+ process.exit(0);
89
+ });
90
+ // Keep process alive
91
+ await new Promise(() => { });
92
+ }
93
+ export const syncCommand = new Command('sync')
94
+ .description('Sync new sessions from ~/.claude/')
95
+ .option('--watch', 'Watch for new sessions after initial sync')
96
+ .option('--rebuild', 'Delete and rebuild the entire database')
97
+ .action(async (opts) => {
98
+ try {
99
+ if (opts.rebuild) {
100
+ const readline = await import('node:readline');
101
+ const rl = readline.createInterface({
102
+ input: process.stdin,
103
+ output: process.stdout,
104
+ });
105
+ const answer = await new Promise((resolve) => {
106
+ rl.question('This will delete all indexed data. Continue? [y/N] ', resolve);
107
+ });
108
+ rl.close();
109
+ if (answer.toLowerCase() !== 'y') {
110
+ printInfo('Aborted');
111
+ return;
112
+ }
113
+ }
114
+ await syncSessions({ rebuild: opts.rebuild ?? false });
115
+ if (opts.watch) {
116
+ await watchForChanges();
117
+ }
118
+ }
119
+ catch (err) {
120
+ printError(err instanceof Error ? err.message : String(err));
121
+ process.exit(1);
122
+ }
123
+ });
124
+ //# sourceMappingURL=sync.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sync.js","sourceRoot":"","sources":["../../src/commands/sync.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
package/dist/index.js ADDED
@@ -0,0 +1,25 @@
1
+ #!/usr/bin/env node
2
+ import { Command } from 'commander';
3
+ import { initCommand } from './commands/init.js';
4
+ import { syncCommand } from './commands/sync.js';
5
+ import { statsCommand } from './commands/stats.js';
6
+ import { exportCommand } from './commands/export.js';
7
+ import { configCommand } from './commands/config.js';
8
+ import { serveCommand } from './commands/serve.js';
9
+ const program = new Command();
10
+ program
11
+ .name('ccview')
12
+ .description('Claude Code session viewer and analytics dashboard')
13
+ .version('0.1.0');
14
+ program.addCommand(initCommand);
15
+ program.addCommand(syncCommand);
16
+ program.addCommand(statsCommand);
17
+ program.addCommand(exportCommand);
18
+ program.addCommand(configCommand);
19
+ program.addCommand(serveCommand);
20
+ // Default command (no subcommand): serve
21
+ program.action(async () => {
22
+ await serveCommand.parseAsync(['serve'], { from: 'user' });
23
+ });
24
+ program.parse();
25
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AACnC,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;AAChD,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;AAChD,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAA;AAClD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAA;AACpD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAA;AACpD,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAA;AAElD,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAA;AAE7B,OAAO;KACJ,IAAI,CAAC,QAAQ,CAAC;KACd,WAAW,CAAC,oDAAoD,CAAC;KACjE,OAAO,CAAC,OAAO,CAAC,CAAA;AAEnB,OAAO,CAAC,UAAU,CAAC,WAAW,CAAC,CAAA;AAC/B,OAAO,CAAC,UAAU,CAAC,WAAW,CAAC,CAAA;AAC/B,OAAO,CAAC,UAAU,CAAC,YAAY,CAAC,CAAA;AAChC,OAAO,CAAC,UAAU,CAAC,aAAa,CAAC,CAAA;AACjC,OAAO,CAAC,UAAU,CAAC,aAAa,CAAC,CAAA;AACjC,OAAO,CAAC,UAAU,CAAC,YAAY,CAAC,CAAA;AAEhC,yCAAyC;AACzC,OAAO,CAAC,MAAM,CAAC,KAAK,IAAI,EAAE;IACxB,MAAM,YAAY,CAAC,UAAU,CAAC,CAAC,OAAO,CAAC,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAA;AAC5D,CAAC,CAAC,CAAA;AAEF,OAAO,CAAC,KAAK,EAAE,CAAA"}
@@ -0,0 +1,9 @@
1
+ export declare function printTable(headers: string[], rows: string[][]): void;
2
+ export declare function printBox(title: string, lines: string[]): void;
3
+ export declare function printSuccess(msg: string): void;
4
+ export declare function printError(msg: string): void;
5
+ export declare function printInfo(msg: string): void;
6
+ export declare function formatTokens(n: number): string;
7
+ export declare function formatCost(usd: number): string;
8
+ export declare function formatDuration(seconds: number): string;
9
+ //# sourceMappingURL=terminal-ui.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"terminal-ui.d.ts","sourceRoot":"","sources":["../../src/utils/terminal-ui.ts"],"names":[],"mappings":"AAGA,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,GAAG,IAAI,CASpE;AAED,wBAAgB,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,IAAI,CAkB7D;AAED,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAE9C;AAED,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAE5C;AAED,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAE3C;AAED,wBAAgB,YAAY,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAI9C;AAED,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAE9C;AAED,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAMtD"}
@@ -0,0 +1,56 @@
1
+ import chalk from 'chalk';
2
+ import Table from 'cli-table3';
3
+ export function printTable(headers, rows) {
4
+ const table = new Table({
5
+ head: headers.map((h) => chalk.bold.cyan(h)),
6
+ style: { head: [], border: [] },
7
+ });
8
+ for (const row of rows) {
9
+ table.push(row);
10
+ }
11
+ console.log(table.toString());
12
+ }
13
+ export function printBox(title, lines) {
14
+ const maxLen = Math.max(title.length + 4, ...lines.map((l) => l.length + 4));
15
+ const width = Math.max(maxLen, 40);
16
+ const top = '\u250c' + '\u2500'.repeat(width) + '\u2510';
17
+ const sep = '\u251c' + '\u2500'.repeat(width) + '\u2524';
18
+ const bot = '\u2514' + '\u2500'.repeat(width) + '\u2518';
19
+ const pad = (s) => '\u2502 ' + s + ' '.repeat(width - s.length - 2) + ' \u2502';
20
+ console.log(top);
21
+ console.log(pad(chalk.bold(title)));
22
+ console.log(sep);
23
+ for (const line of lines) {
24
+ console.log(pad(line));
25
+ }
26
+ console.log(bot);
27
+ }
28
+ export function printSuccess(msg) {
29
+ console.log(chalk.green('\u2713 ' + msg));
30
+ }
31
+ export function printError(msg) {
32
+ console.log(chalk.red('\u2717 ' + msg));
33
+ }
34
+ export function printInfo(msg) {
35
+ console.log(chalk.blue('\u2139 ' + msg));
36
+ }
37
+ export function formatTokens(n) {
38
+ if (n >= 1_000_000)
39
+ return (n / 1_000_000).toFixed(1) + 'M';
40
+ if (n >= 1_000)
41
+ return Math.round(n / 1_000) + 'K';
42
+ return String(n);
43
+ }
44
+ export function formatCost(usd) {
45
+ return '$' + usd.toFixed(2);
46
+ }
47
+ export function formatDuration(seconds) {
48
+ if (seconds < 60)
49
+ return seconds + 's';
50
+ const h = Math.floor(seconds / 3600);
51
+ const m = Math.round((seconds % 3600) / 60);
52
+ if (h > 0)
53
+ return h + 'h ' + m + 'm';
54
+ return m + ' min';
55
+ }
56
+ //# sourceMappingURL=terminal-ui.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"terminal-ui.js","sourceRoot":"","sources":["../../src/utils/terminal-ui.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AACzB,OAAO,KAAK,MAAM,YAAY,CAAA;AAE9B,MAAM,UAAU,UAAU,CAAC,OAAiB,EAAE,IAAgB;IAC5D,MAAM,KAAK,GAAG,IAAI,KAAK,CAAC;QACtB,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAC5C,KAAK,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE;KAChC,CAAC,CAAA;IACF,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;QACvB,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;IACjB,CAAC;IACD,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC,CAAA;AAC/B,CAAC;AAED,MAAM,UAAU,QAAQ,CAAC,KAAa,EAAE,KAAe;IACrD,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CACrB,KAAK,CAAC,MAAM,GAAG,CAAC,EAChB,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAClC,CAAA;IACD,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,EAAE,CAAC,CAAA;IAClC,MAAM,GAAG,GAAG,QAAQ,GAAG,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,QAAQ,CAAA;IACxD,MAAM,GAAG,GAAG,QAAQ,GAAG,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,QAAQ,CAAA;IACxD,MAAM,GAAG,GAAG,QAAQ,GAAG,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,QAAQ,CAAA;IACxD,MAAM,GAAG,GAAG,CAAC,CAAS,EAAE,EAAE,CAAC,SAAS,GAAG,CAAC,GAAG,GAAG,CAAC,MAAM,CAAC,KAAK,GAAG,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,GAAG,SAAS,CAAA;IAEvF,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;IAChB,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAA;IACnC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;IAChB,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAA;IACxB,CAAC;IACD,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;AAClB,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,GAAW;IACtC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,SAAS,GAAG,GAAG,CAAC,CAAC,CAAA;AAC3C,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,GAAW;IACpC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,SAAS,GAAG,GAAG,CAAC,CAAC,CAAA;AACzC,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,GAAW;IACnC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,GAAG,GAAG,CAAC,CAAC,CAAA;AAC1C,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,CAAS;IACpC,IAAI,CAAC,IAAI,SAAS;QAAE,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,GAAG,CAAA;IAC3D,IAAI,CAAC,IAAI,KAAK;QAAE,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,KAAK,CAAC,GAAG,GAAG,CAAA;IAClD,OAAO,MAAM,CAAC,CAAC,CAAC,CAAA;AAClB,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,GAAW;IACpC,OAAO,GAAG,GAAG,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,CAAA;AAC7B,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,OAAe;IAC5C,IAAI,OAAO,GAAG,EAAE;QAAE,OAAO,OAAO,GAAG,GAAG,CAAA;IACtC,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,IAAI,CAAC,CAAA;IACpC,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,OAAO,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,CAAA;IAC3C,IAAI,CAAC,GAAG,CAAC;QAAE,OAAO,CAAC,GAAG,IAAI,GAAG,CAAC,GAAG,GAAG,CAAA;IACpC,OAAO,CAAC,GAAG,MAAM,CAAA;AACnB,CAAC"}
package/package.json ADDED
@@ -0,0 +1,52 @@
1
+ {
2
+ "name": "@ccview/cli",
3
+ "version": "0.1.0",
4
+ "description": "Claude Code sessions dashboard — visualize costs, files and decisions. Local. Private.",
5
+ "license": "MIT",
6
+ "author": "underluis1",
7
+ "keywords": [
8
+ "claude",
9
+ "claude-code",
10
+ "anthropic",
11
+ "dashboard",
12
+ "analytics",
13
+ "ai",
14
+ "cli"
15
+ ],
16
+ "homepage": "https://github.com/underluis1/ccview",
17
+ "repository": {
18
+ "type": "git",
19
+ "url": "https://github.com/underluis1/ccview.git"
20
+ },
21
+ "type": "module",
22
+ "bin": {
23
+ "ccview": "./dist/index.js"
24
+ },
25
+ "main": "./dist/index.js",
26
+ "files": [
27
+ "dist"
28
+ ],
29
+ "scripts": {
30
+ "build": "tsc",
31
+ "build:watch": "tsc --watch",
32
+ "typecheck": "tsc --noEmit",
33
+ "clean": "rm -rf dist",
34
+ "start": "node dist/index.js"
35
+ },
36
+ "dependencies": {
37
+ "@ccview/core": "workspace:*",
38
+ "@ccview/server": "workspace:*",
39
+ "commander": "^12.0.0",
40
+ "chalk": "^5.3.0",
41
+ "cli-table3": "^0.6.3",
42
+ "ora": "^8.0.1",
43
+ "open": "^10.1.0"
44
+ },
45
+ "devDependencies": {
46
+ "@types/node": "^20.12.7",
47
+ "typescript": "^5.4.5"
48
+ },
49
+ "engines": {
50
+ "node": ">=18.0.0"
51
+ }
52
+ }