You're Invited:Meet the Socket Team at RSAC and BSidesSF 2026, March 23–26.RSVP
Socket
Book a DemoSign in
Socket

@code-insights/cli

Package Overview
Dependencies
Maintainers
1
Versions
35
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@code-insights/cli - npm Package Compare versions

Comparing version
3.0.2
to
3.0.3
dashboard-dist/assets/index-CUWKxcRo.js

Sorry, the diff of this file is too big to display

+12
-0

@@ -5,2 +5,14 @@ # Changelog

## [3.0.3] - 2026-02-28
### Fixed
- **`--version` now reads from package.json** — Previously hardcoded as `3.0.0`, causing `code-insights --version` to always report `3.0.0` regardless of installed version.
## [3.0.2] - 2026-02-28
### Fixed
- **Added missing server runtime dependencies** — `hono` and `@hono/node-server` added to CLI package.json so `code-insights dashboard` works for npm-installed users.
## [3.0.0] - 2026-02-28

@@ -7,0 +19,0 @@

+1
-1

@@ -27,3 +27,3 @@ <!DOCTYPE html>

</script>
<script type="module" crossorigin src="/assets/index-CuCBzQyQ.js"></script>
<script type="module" crossorigin src="/assets/index-CUWKxcRo.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-BMhL7wL8.css">

@@ -30,0 +30,0 @@ </head>

#!/usr/bin/env node
import { readFileSync } from 'fs';
import { Command } from 'commander';

@@ -14,2 +15,3 @@ import { initCommand } from './commands/init.js';

import { showTelemetryNoticeIfNeeded } from './utils/telemetry.js';
const pkg = JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf-8'));
const program = new Command();

@@ -19,3 +21,3 @@ program

.description('AI coding session analytics — sync, stats, and insights')
.version('3.0.0');
.version(pkg.version);
program

@@ -22,0 +24,0 @@ .command('init')

@@ -1,1 +0,1 @@

{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,MAAM,4BAA4B,CAAC;AACtF,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AACzD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,2BAA2B,EAAE,MAAM,sBAAsB,CAAC;AAEnE,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;AAE9B,OAAO;KACJ,IAAI,CAAC,eAAe,CAAC;KACrB,WAAW,CAAC,yDAAyD,CAAC;KACtE,OAAO,CAAC,OAAO,CAAC,CAAC;AAEpB,OAAO;KACJ,OAAO,CAAC,MAAM,CAAC;KACf,WAAW,CAAC,mDAAmD,CAAC;KAChE,MAAM,CAAC,WAAW,CAAC,CAAC;AAEvB,OAAO;KACJ,OAAO,CAAC,MAAM,CAAC;KACf,WAAW,CAAC,kDAAkD,CAAC;KAC/D,MAAM,CAAC,aAAa,EAAE,4BAA4B,CAAC;KACnD,MAAM,CAAC,sBAAsB,EAAE,4CAA4C,CAAC;KAC5E,MAAM,CAAC,qBAAqB,EAAE,qEAAqE,CAAC;KACpG,MAAM,CAAC,WAAW,EAAE,kDAAkD,CAAC;KACvE,MAAM,CAAC,aAAa,EAAE,oCAAoC,CAAC;KAC3D,MAAM,CAAC,eAAe,EAAE,yCAAyC,CAAC;KAClE,MAAM,CAAC,qBAAqB,EAAE,oCAAoC,CAAC;KACnE,MAAM,CAAC,WAAW,CAAC,CAAC;AAEvB,OAAO;KACJ,OAAO,CAAC,QAAQ,CAAC;KACjB,WAAW,CAAC,0CAA0C,CAAC;KACvD,MAAM,CAAC,aAAa,CAAC,CAAC;AAEzB,OAAO;KACJ,OAAO,CAAC,cAAc,CAAC;KACvB,WAAW,CAAC,6CAA6C,CAAC;KAC1D,MAAM,CAAC,kBAAkB,CAAC,CAAC;AAE9B,OAAO;KACJ,OAAO,CAAC,gBAAgB,CAAC;KACzB,WAAW,CAAC,yBAAyB,CAAC;KACtC,MAAM,CAAC,oBAAoB,CAAC,CAAC;AAEhC,OAAO;KACJ,OAAO,CAAC,MAAM,CAAC;KACf,WAAW,CAAC,0CAA0C,CAAC;KACvD,MAAM,CAAC,WAAW,EAAE,sCAAsC,CAAC;KAC3D,MAAM,CAAC,WAAW,CAAC,CAAC;AAEvB,OAAO;KACJ,OAAO,CAAC,WAAW,CAAC;KACpB,WAAW,CAAC,8DAA8D,CAAC;KAC3E,MAAM,CAAC,qBAAqB,EAAE,aAAa,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC;KAC1D,MAAM,CAAC,WAAW,EAAE,mCAAmC,CAAC;KACxD,MAAM,CAAC,gBAAgB,CAAC,CAAC;AAE5B,OAAO,CAAC,UAAU,CAAC,YAAY,CAAC,CAAC;AACjC,OAAO,CAAC,UAAU,CAAC,YAAY,CAAC,CAAC;AACjC,OAAO,CAAC,UAAU,CAAC,aAAa,CAAC,CAAC;AAClC,OAAO,CAAC,UAAU,CAAC,gBAAgB,CAAC,CAAC;AAErC,6DAA6D;AAC7D,2BAA2B,EAAE,CAAC;AAE9B,OAAO,CAAC,KAAK,EAAE,CAAC"}
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
{
"name": "@code-insights/cli",
"version": "3.0.2",
"version": "3.0.3",
"description": "AI coding session analytics with built-in dashboard",

@@ -5,0 +5,0 @@ "type": "module",

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" fill="none">
<rect width="32" height="32" rx="6" fill="#18181b"/>
<path d="M8 22 L12 14 L16 18 L20 10 L24 22" stroke="#a1a1aa" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" fill="none"/>
</svg>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Code Insights</title>
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<!-- FOUC prevention: apply theme class before React renders to avoid flash.
Wrapped in try/catch so it silently degrades to light mode in
restricted environments where localStorage access is blocked. -->
<script>
(function () {
try {
var stored = localStorage.getItem('code-insights-theme');
var isDark = stored === 'dark';
var isSystem = !stored || stored === 'system' || (stored !== 'light' && stored !== 'dark');
var resolved = isDark || (isSystem && window.matchMedia('(prefers-color-scheme: dark)').matches)
? 'dark'
: 'light';
} catch (e) {
var resolved = 'light';
}
document.documentElement.classList.remove('light', 'dark');
document.documentElement.classList.add(resolved);
})();
</script>
<script type="module" crossorigin src="/assets/index-CUWKxcRo.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-BMhL7wL8.css">
</head>
<body>
<div id="root"></div>
</body>
</html>
/**
* Generate and display the dashboard connection URL
*/
export declare function connectCommand(): Promise<void>;
//# sourceMappingURL=connect.d.ts.map
{"version":3,"file":"connect.d.ts","sourceRoot":"","sources":["../../src/commands/connect.ts"],"names":[],"mappings":"AAMA;;GAEG;AACH,wBAAsB,cAAc,IAAI,OAAO,CAAC,IAAI,CAAC,CAkCpD"}
import chalk from 'chalk';
import { isConfigured, loadWebConfig, hasWebConfig } from '../utils/config.js';
import { trackEvent } from '../utils/telemetry.js';
import { generateDashboardUrl, validateWebConfig } from '../utils/firebase-json.js';
/**
* Generate and display the dashboard connection URL
*/
export async function connectCommand() {
if (!isConfigured()) {
console.log(chalk.red('\n❌ Code Insights is not configured.'));
console.log(chalk.gray('The connect command requires Firebase setup.\n'));
console.log(chalk.white(' To set up Firebase:'));
console.log(chalk.gray(' code-insights init\n'));
console.log(chalk.white(' For local-only analytics (no setup needed):'));
console.log(chalk.gray(' code-insights stats\n'));
return;
}
if (!hasWebConfig()) {
console.log(chalk.yellow('\n⚠ No web config found.'));
console.log(chalk.gray('\nRe-run "code-insights init" to add your web config.'));
console.log(chalk.gray('Or visit the dashboard and configure Firebase manually:'));
console.log(chalk.white(' https://code-insights.app\n'));
return;
}
const webConfigData = loadWebConfig();
if (!webConfigData || !validateWebConfig(webConfigData)) {
console.log(chalk.red('\n❌ Invalid web config stored.'));
console.log(chalk.gray('Run "code-insights init" to reconfigure.\n'));
return;
}
const webConfig = webConfigData;
const url = generateDashboardUrl(webConfig);
console.log(chalk.cyan('\n🔗 Dashboard Connection URL\n'));
console.log(chalk.white('Open this URL to connect the dashboard to your Firebase:'));
console.log(chalk.bold.underline(url));
console.log('');
trackEvent('connect', true);
}
//# sourceMappingURL=connect.js.map
{"version":3,"file":"connect.js","sourceRoot":"","sources":["../../src/commands/connect.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAC/E,OAAO,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AACnD,OAAO,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAGpF;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc;IAClC,IAAI,CAAC,YAAY,EAAE,EAAE,CAAC;QACpB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,sCAAsC,CAAC,CAAC,CAAC;QAC/D,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,gDAAgD,CAAC,CAAC,CAAC;QAC1E,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,uBAAuB,CAAC,CAAC,CAAC;QAClD,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,0BAA0B,CAAC,CAAC,CAAC;QACpD,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,+CAA+C,CAAC,CAAC,CAAC;QAC1E,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,2BAA2B,CAAC,CAAC,CAAC;QACrD,OAAO;IACT,CAAC;IAED,IAAI,CAAC,YAAY,EAAE,EAAE,CAAC;QACpB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,0BAA0B,CAAC,CAAC,CAAC;QACtD,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,uDAAuD,CAAC,CAAC,CAAC;QACjF,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,yDAAyD,CAAC,CAAC,CAAC;QACnF,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,+BAA+B,CAAC,CAAC,CAAC;QAC1D,OAAO;IACT,CAAC;IAED,MAAM,aAAa,GAAG,aAAa,EAAE,CAAC;IACtC,IAAI,CAAC,aAAa,IAAI,CAAC,iBAAiB,CAAC,aAAa,CAAC,EAAE,CAAC;QACxD,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,gCAAgC,CAAC,CAAC,CAAC;QACzD,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,4CAA4C,CAAC,CAAC,CAAC;QACtE,OAAO;IACT,CAAC;IAED,MAAM,SAAS,GAAG,aAAkC,CAAC;IACrD,MAAM,GAAG,GAAG,oBAAoB,CAAC,SAAS,CAAC,CAAC;IAE5C,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,iCAAiC,CAAC,CAAC,CAAC;IAC3D,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,0DAA0D,CAAC,CAAC,CAAC;IACrF,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC;IACvC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAChB,UAAU,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;AAC9B,CAAC"}
import type { InsightType } from '../types.js';
interface InsightsOptions {
type?: InsightType;
project?: string;
today?: boolean;
limit?: number;
}
/**
* Display recent insights from Firestore
*/
export declare function insightsCommand(options?: InsightsOptions): Promise<void>;
export {};
//# sourceMappingURL=insights.d.ts.map
{"version":3,"file":"insights.d.ts","sourceRoot":"","sources":["../../src/commands/insights.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAW,WAAW,EAAE,MAAM,aAAa,CAAC;AAExD,UAAU,eAAe;IACvB,IAAI,CAAC,EAAE,WAAW,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;GAEG;AACH,wBAAsB,eAAe,CAAC,OAAO,GAAE,eAAoB,GAAG,OAAO,CAAC,IAAI,CAAC,CA8ElF"}
import chalk from 'chalk';
import { format } from 'date-fns';
import { loadConfig, isConfigured } from '../utils/config.js';
import { initializeFirebase, getRecentInsights } from '../firebase/client.js';
/**
* Display recent insights from Firestore
*/
export async function insightsCommand(options = {}) {
// Check if configured
if (!isConfigured()) {
console.log(chalk.red('Not configured. Run `claudeinsight init` first.'));
process.exit(1);
}
// Load config and initialize Firebase
const config = loadConfig();
if (!config) {
console.log(chalk.red('Configuration error.'));
process.exit(1);
}
try {
initializeFirebase(config);
}
catch (error) {
console.log(chalk.red('Failed to connect to Firebase.'));
console.error(error instanceof Error ? error.message : 'Unknown error');
process.exit(1);
}
const limit = options.limit || 20;
// Fetch insights
const insights = await getRecentInsights(limit, {
type: options.type,
project: options.project,
todayOnly: options.today,
});
if (insights.length === 0) {
console.log(chalk.yellow('\nNo insights found.'));
if (options.today) {
console.log(chalk.gray('Try running without --today to see older insights.'));
}
return;
}
console.log(chalk.cyan(`\n📊 Recent Insights (${insights.length})\n`));
// Group by type
const summaries = insights.filter((i) => i.type === 'summary');
const decisions = insights.filter((i) => i.type === 'decision');
const learnings = insights.filter((i) => i.type === 'learning');
const techniques = insights.filter((i) => i.type === 'technique');
if (summaries.length > 0) {
console.log(chalk.bold.magenta('📝 Summaries'));
for (const insight of summaries) {
printInsight(insight);
}
console.log();
}
if (decisions.length > 0) {
console.log(chalk.bold.blue('🎯 Decisions'));
for (const insight of decisions) {
printInsight(insight);
}
console.log();
}
if (learnings.length > 0) {
console.log(chalk.bold.green('💡 Learnings'));
for (const insight of learnings) {
printInsight(insight);
}
console.log();
}
if (techniques.length > 0) {
console.log(chalk.bold.yellow('🔧 Techniques'));
for (const insight of techniques) {
printInsight(insight);
}
console.log();
}
}
function printInsight(insight) {
const date = insight.timestamp
? format(new Date(insight.timestamp), 'MMM d')
: 'Unknown';
console.log(chalk.white(` • ${insight.title}`));
console.log(chalk.gray(` ${insight.projectName} | ${date}`));
if (insight.content && insight.content.length < 100) {
console.log(chalk.gray(` ${insight.content}`));
}
}
//# sourceMappingURL=insights.js.map
{"version":3,"file":"insights.js","sourceRoot":"","sources":["../../src/commands/insights.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAClC,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAC9D,OAAO,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAU9E;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,UAA2B,EAAE;IACjE,sBAAsB;IACtB,IAAI,CAAC,YAAY,EAAE,EAAE,CAAC;QACpB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,iDAAiD,CAAC,CAAC,CAAC;QAC1E,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,sCAAsC;IACtC,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC;IAC5B,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,sBAAsB,CAAC,CAAC,CAAC;QAC/C,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,IAAI,CAAC;QACH,kBAAkB,CAAC,MAAM,CAAC,CAAC;IAC7B,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,gCAAgC,CAAC,CAAC,CAAC;QACzD,OAAO,CAAC,KAAK,CAAC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC;QACxE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,IAAI,EAAE,CAAC;IAElC,iBAAiB;IACjB,MAAM,QAAQ,GAAG,MAAM,iBAAiB,CAAC,KAAK,EAAE;QAC9C,IAAI,EAAE,OAAO,CAAC,IAAI;QAClB,OAAO,EAAE,OAAO,CAAC,OAAO;QACxB,SAAS,EAAE,OAAO,CAAC,KAAK;KACzB,CAAC,CAAC;IAEH,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC1B,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,sBAAsB,CAAC,CAAC,CAAC;QAClD,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC;YAClB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,oDAAoD,CAAC,CAAC,CAAC;QAChF,CAAC;QACD,OAAO;IACT,CAAC;IAED,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,yBAAyB,QAAQ,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC;IAEvE,gBAAgB;IAChB,MAAM,SAAS,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC;IAC/D,MAAM,SAAS,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,UAAU,CAAC,CAAC;IAChE,MAAM,SAAS,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,UAAU,CAAC,CAAC;IAChE,MAAM,UAAU,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,WAAW,CAAC,CAAC;IAElE,IAAI,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACzB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,cAAc,CAAC,CAAC,CAAC;QAChD,KAAK,MAAM,OAAO,IAAI,SAAS,EAAE,CAAC;YAChC,YAAY,CAAC,OAAO,CAAC,CAAC;QACxB,CAAC;QACD,OAAO,CAAC,GAAG,EAAE,CAAC;IAChB,CAAC;IAED,IAAI,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACzB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC,CAAC;QAC7C,KAAK,MAAM,OAAO,IAAI,SAAS,EAAE,CAAC;YAChC,YAAY,CAAC,OAAO,CAAC,CAAC;QACxB,CAAC;QACD,OAAO,CAAC,GAAG,EAAE,CAAC;IAChB,CAAC;IAED,IAAI,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACzB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC,CAAC;QAC9C,KAAK,MAAM,OAAO,IAAI,SAAS,EAAE,CAAC;YAChC,YAAY,CAAC,OAAO,CAAC,CAAC;QACxB,CAAC;QACD,OAAO,CAAC,GAAG,EAAE,CAAC;IAChB,CAAC;IAED,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC1B,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,eAAe,CAAC,CAAC,CAAC;QAChD,KAAK,MAAM,OAAO,IAAI,UAAU,EAAE,CAAC;YACjC,YAAY,CAAC,OAAO,CAAC,CAAC;QACxB,CAAC;QACD,OAAO,CAAC,GAAG,EAAE,CAAC;IAChB,CAAC;AACH,CAAC;AAED,SAAS,YAAY,CAAC,OAAgB;IACpC,MAAM,IAAI,GAAG,OAAO,CAAC,SAAS;QAC5B,CAAC,CAAC,MAAM,CAAC,IAAI,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE,OAAO,CAAC;QAC9C,CAAC,CAAC,SAAS,CAAC;IAEd,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;IACjD,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,OAAO,CAAC,WAAW,MAAM,IAAI,EAAE,CAAC,CAAC,CAAC;IAChE,IAAI,OAAO,CAAC,OAAO,IAAI,OAAO,CAAC,OAAO,CAAC,MAAM,GAAG,GAAG,EAAE,CAAC;QACpD,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;IACpD,CAAC;AACH,CAAC"}
export interface LinkOptions {
qr?: boolean;
}
/**
* Generate and display dashboard link
*/
export declare function linkCommand(options: LinkOptions): Promise<void>;
//# sourceMappingURL=link.d.ts.map
{"version":3,"file":"link.d.ts","sourceRoot":"","sources":["../../src/commands/link.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,WAAW;IAC1B,EAAE,CAAC,EAAE,OAAO,CAAC;CACd;AAED;;GAEG;AACH,wBAAsB,WAAW,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAoCrE"}
import chalk from 'chalk';
import qrcode from 'qrcode-terminal';
import { isConfigured, loadWebConfig, hasWebConfig } from '../utils/config.js';
import { generateDashboardUrl, validateWebConfig } from '../utils/firebase-json.js';
/**
* Generate and display dashboard link
*/
export async function linkCommand(options) {
if (!isConfigured()) {
console.log(chalk.red('\n❌ Code Insights is not configured.'));
console.log(chalk.gray('Run "code-insights init" first.\n'));
return;
}
if (!hasWebConfig()) {
console.log(chalk.yellow('\n⚠ No web config found.'));
console.log(chalk.gray('\nTo generate a dashboard link, add web config:'));
console.log(chalk.white(' code-insights init --web-config <path-to-config.json>\n'));
console.log(chalk.gray('Or visit the dashboard and configure Firebase manually:'));
console.log(chalk.white(' https://code-insights.app\n'));
return;
}
const webConfigData = loadWebConfig();
if (!webConfigData || !validateWebConfig(webConfigData)) {
console.log(chalk.red('\n❌ Invalid web config stored.'));
console.log(chalk.gray('Run "code-insights init --web-config <path>" to reconfigure.\n'));
return;
}
const webConfig = webConfigData;
const url = generateDashboardUrl(webConfig);
console.log(chalk.cyan('\n🔗 Dashboard Link\n'));
console.log(chalk.white('Open this URL to connect the dashboard:'));
console.log(chalk.bold.underline(url));
if (options.qr !== false) {
console.log(chalk.gray('\n📱 QR Code:\n'));
qrcode.generate(url, { small: true });
}
console.log('');
}
//# sourceMappingURL=link.js.map
{"version":3,"file":"link.js","sourceRoot":"","sources":["../../src/commands/link.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,MAAM,MAAM,iBAAiB,CAAC;AACrC,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAC/E,OAAO,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAOpF;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,OAAoB;IACpD,IAAI,CAAC,YAAY,EAAE,EAAE,CAAC;QACpB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,sCAAsC,CAAC,CAAC,CAAC;QAC/D,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,mCAAmC,CAAC,CAAC,CAAC;QAC7D,OAAO;IACT,CAAC;IAED,IAAI,CAAC,YAAY,EAAE,EAAE,CAAC;QACpB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,0BAA0B,CAAC,CAAC,CAAC;QACtD,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,iDAAiD,CAAC,CAAC,CAAC;QAC3E,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,2DAA2D,CAAC,CAAC,CAAC;QACtF,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,yDAAyD,CAAC,CAAC,CAAC;QACnF,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,+BAA+B,CAAC,CAAC,CAAC;QAC1D,OAAO;IACT,CAAC;IAED,MAAM,aAAa,GAAG,aAAa,EAAE,CAAC;IACtC,IAAI,CAAC,aAAa,IAAI,CAAC,iBAAiB,CAAC,aAAa,CAAC,EAAE,CAAC;QACxD,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,gCAAgC,CAAC,CAAC,CAAC;QACzD,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,gEAAgE,CAAC,CAAC,CAAC;QAC1F,OAAO;IACT,CAAC;IAED,MAAM,SAAS,GAAG,aAAkC,CAAC;IACrD,MAAM,GAAG,GAAG,oBAAoB,CAAC,SAAS,CAAC,CAAC;IAE5C,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,uBAAuB,CAAC,CAAC,CAAC;IACjD,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,yCAAyC,CAAC,CAAC,CAAC;IACpE,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC;IAEvC,IAAI,OAAO,CAAC,EAAE,KAAK,KAAK,EAAE,CAAC;QACzB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC,CAAC;QAC3C,MAAM,CAAC,QAAQ,CAAC,GAAG,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACxC,CAAC;IAED,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;AAClB,CAAC"}
import type { ParsedSession } from '../../../types.js';
import type { SessionRow } from './types.js';
/**
* Transform the CLI's ParsedSession into the stats SessionRow shape.
* Uses a module-level cache for projectId to avoid repeated git spawns.
*/
export declare function parsedSessionToRow(session: ParsedSession): SessionRow;
export declare class StatsCache {
private data;
private dirty;
constructor();
/**
* Refresh the cache by discovering and parsing sessions from all providers.
* Returns counts of new and total sessions.
*/
refresh(): Promise<{
newSessions: number;
totalSessions: number;
}>;
/**
* Return all cached session rows with dates properly deserialized.
* JSON serialization turns Date objects into ISO strings — reconstruct them.
*/
getAllRows(): SessionRow[];
private load;
private save;
private empty;
}
//# sourceMappingURL=cache.d.ts.map
{"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../../../../src/commands/stats/data/cache.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AACvD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AA+C7C;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,aAAa,GAAG,UAAU,CAoCrE;AAMD,qBAAa,UAAU;IACrB,OAAO,CAAC,IAAI,CAAiB;IAC7B,OAAO,CAAC,KAAK,CAAU;;IAOvB;;;OAGG;IACG,OAAO,IAAI,OAAO,CAAC;QAAE,WAAW,EAAE,MAAM,CAAC;QAAC,aAAa,EAAE,MAAM,CAAA;KAAE,CAAC;IA2ExE;;;OAGG;IACH,UAAU,IAAI,UAAU,EAAE;IAkB1B,OAAO,CAAC,IAAI;IAgBZ,OAAO,CAAC,IAAI;IAMZ,OAAO,CAAC,KAAK;CAGd"}
// ──────────────────────────────────────────────────────
// Stats cache — disk-based parsed session store
//
// Stores pre-parsed SessionRow[] keyed by source file path
// with mtime-based invalidation. Lives at:
// ~/.code-insights/stats-cache.json
// ──────────────────────────────────────────────────────
import * as fs from 'fs';
import * as path from 'path';
import * as os from 'os';
import { generateStableProjectId } from '../../../utils/device.js';
import { ensureConfigDir } from '../../../utils/config.js';
import { splitVirtualPath } from '../../../utils/paths.js';
import { getAllProviders } from '../../../providers/registry.js';
// ──────────────────────────────────────────────────────
// Constants
// ──────────────────────────────────────────────────────
const CACHE_PATH = path.join(os.homedir(), '.code-insights', 'stats-cache.json');
// ──────────────────────────────────────────────────────
// Module-level projectId cache — avoids repeated git
// process spawns for the same project path
// ──────────────────────────────────────────────────────
const projectIdCache = new Map();
function getCachedProjectId(projectPath) {
const existing = projectIdCache.get(projectPath);
if (existing)
return existing;
const { projectId } = generateStableProjectId(projectPath);
projectIdCache.set(projectPath, projectId);
return projectId;
}
// ──────────────────────────────────────────────────────
// Exported helpers
// ──────────────────────────────────────────────────────
/**
* Transform the CLI's ParsedSession into the stats SessionRow shape.
* Uses a module-level cache for projectId to avoid repeated git spawns.
*/
export function parsedSessionToRow(session) {
const projectId = getCachedProjectId(session.projectPath);
return {
// identity
id: session.id,
projectId,
projectName: session.projectName,
// timing
startedAt: session.startedAt,
endedAt: session.endedAt,
// counts
messageCount: session.messageCount,
userMessageCount: session.userMessageCount,
assistantMessageCount: session.assistantMessageCount,
toolCallCount: session.toolCallCount,
// cost / usage (optional)
estimatedCostUsd: session.usage?.estimatedCostUsd,
totalInputTokens: session.usage?.totalInputTokens,
totalOutputTokens: session.usage?.totalOutputTokens,
cacheCreationTokens: session.usage?.cacheCreationTokens,
cacheReadTokens: session.usage?.cacheReadTokens,
// metadata
primaryModel: session.usage?.primaryModel,
modelsUsed: session.usage?.modelsUsed,
generatedTitle: session.generatedTitle ?? undefined,
customTitle: session.customTitle,
summary: session.summary ?? undefined,
sessionCharacter: session.sessionCharacter ?? undefined,
sourceTool: session.sourceTool,
usageSource: session.usage?.usageSource,
};
}
// ──────────────────────────────────────────────────────
// StatsCache class
// ──────────────────────────────────────────────────────
export class StatsCache {
data;
dirty;
constructor() {
this.dirty = false;
this.data = this.load();
}
/**
* Refresh the cache by discovering and parsing sessions from all providers.
* Returns counts of new and total sessions.
*/
async refresh() {
const providers = getAllProviders();
let newSessions = 0;
// Collect all discovered file paths (keyed by provider name for metadata)
const allDiscoveredPaths = new Set();
for (const provider of providers) {
const providerName = provider.getProviderName();
let filePaths;
try {
filePaths = await provider.discover();
}
catch {
// If discovery fails for a provider, skip it
continue;
}
for (const filePath of filePaths) {
allDiscoveredPaths.add(filePath);
// Check mtime for cache invalidation
const { realPath } = splitVirtualPath(filePath);
let currentMtime;
try {
currentMtime = fs.statSync(realPath).mtime.toISOString();
}
catch {
// File no longer accessible, skip
continue;
}
const cached = this.data.entries[filePath];
if (cached && cached.lastModified === currentMtime) {
// Cache hit — mtime unchanged
continue;
}
// Cache miss — parse the file
let session;
try {
session = await provider.parse(filePath);
}
catch {
// Parse error — skip this file
continue;
}
if (!session)
continue;
const row = parsedSessionToRow(session);
this.data.entries[filePath] = {
lastModified: currentMtime,
provider: providerName,
rows: [row],
};
this.dirty = true;
newSessions++;
}
}
// Prune entries for file paths that no longer exist in any provider's discovery
for (const cachedPath of Object.keys(this.data.entries)) {
if (!allDiscoveredPaths.has(cachedPath)) {
delete this.data.entries[cachedPath];
this.dirty = true;
}
}
// Persist if anything changed
if (this.dirty) {
this.data.lastRefresh = new Date().toISOString();
this.save();
}
return { newSessions, totalSessions: this.getAllRows().length };
}
/**
* Return all cached session rows with dates properly deserialized.
* JSON serialization turns Date objects into ISO strings — reconstruct them.
*/
getAllRows() {
const rows = [];
for (const entry of Object.values(this.data.entries)) {
for (const row of entry.rows) {
rows.push({
...row,
startedAt: new Date(row.startedAt),
endedAt: new Date(row.endedAt),
});
}
}
return rows;
}
// ──────────────────────────────────────────────────────
// Private methods
// ──────────────────────────────────────────────────────
load() {
try {
if (!fs.existsSync(CACHE_PATH)) {
return this.empty();
}
const content = fs.readFileSync(CACHE_PATH, 'utf-8');
const parsed = JSON.parse(content);
if (parsed.version !== 1) {
return this.empty();
}
return parsed;
}
catch {
return this.empty();
}
}
save() {
ensureConfigDir();
fs.writeFileSync(CACHE_PATH, JSON.stringify(this.data, null, 2));
this.dirty = false;
}
empty() {
return { version: 1, lastRefresh: '', entries: {} };
}
}
//# sourceMappingURL=cache.js.map
{"version":3,"file":"cache.js","sourceRoot":"","sources":["../../../../src/commands/stats/data/cache.ts"],"names":[],"mappings":"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"}
import type { ClaudeInsightConfig } from '../../../types.js';
import type { StatsDataSource, SessionRow, SessionQueryOptions, UsageStatsDoc, ProjectResolution, PrepareResult, StatsFlags } from './types.js';
export declare class FirestoreDataSource implements StatsDataSource {
private config;
readonly name = "firestore";
constructor(config: ClaudeInsightConfig);
prepare(flags: StatsFlags): Promise<PrepareResult>;
getSessions(opts: SessionQueryOptions): Promise<SessionRow[]>;
getUsageStats(): Promise<UsageStatsDoc | null>;
resolveProjectId(name: string): Promise<ProjectResolution>;
getLastSession(opts?: Pick<SessionQueryOptions, 'sourceTool' | 'projectId'>): Promise<SessionRow | null>;
}
//# sourceMappingURL=firestore.d.ts.map
{"version":3,"file":"firestore.d.ts","sourceRoot":"","sources":["../../../../src/commands/stats/data/firestore.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAE7D,OAAO,KAAK,EACV,eAAe,EACf,UAAU,EACV,mBAAmB,EACnB,aAAa,EACb,iBAAiB,EACjB,aAAa,EACb,UAAU,EACX,MAAM,YAAY,CAAC;AAgEpB,qBAAa,mBAAoB,YAAW,eAAe;IAG7C,OAAO,CAAC,MAAM;IAF1B,QAAQ,CAAC,IAAI,eAAe;gBAER,MAAM,EAAE,mBAAmB;IAEzC,OAAO,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,aAAa,CAAC;IAkBlD,WAAW,CAAC,IAAI,EAAE,mBAAmB,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC;IAiC7D,aAAa,IAAI,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC;IAgB9C,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,CAAC;IAqB1D,cAAc,CAAC,IAAI,CAAC,EAAE,IAAI,CAAC,mBAAmB,EAAE,YAAY,GAAG,WAAW,CAAC,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC;CA8B/G"}
// ──────────────────────────────────────────────────────
// Firestore data source for stats commands
// ──────────────────────────────────────────────────────
import admin from 'firebase-admin';
import { initializeFirebase, getDb, getProjects } from '../../../firebase/client.js';
import { ProjectNotFoundError, FirestoreIndexError } from './types.js';
import { findSimilarNames } from './fuzzy-match.js';
// ──────────────────────────────────────────────────────
// Helpers
// ──────────────────────────────────────────────────────
/** Checks if error is a Firestore FAILED_PRECONDITION (missing composite index) */
function isFirestoreIndexError(error) {
if (typeof error === 'object' && error !== null && 'code' in error) {
return error.code === 9;
}
return false;
}
/** Extracts the Firebase console index-creation URL from an error message */
function extractIndexUrl(error) {
if (typeof error === 'object' && error !== null && 'message' in error) {
const message = error.message;
const match = message.match(/(https:\/\/console\.firebase\.google\.com\/[^\s"]+)/);
return match ? match[1] : null;
}
return null;
}
// ──────────────────────────────────────────────────────
// Document mapper
// ──────────────────────────────────────────────────────
/** Maps a Firestore document to the universal SessionRow shape */
function docToSessionRow(doc) {
const data = doc.data();
if (!data)
throw new Error(`Session document ${doc.id} has no data`);
return {
id: doc.id,
projectId: data.projectId,
projectName: data.projectName,
startedAt: data.startedAt?.toDate() ?? new Date(),
endedAt: data.endedAt?.toDate() ?? new Date(),
messageCount: data.messageCount ?? 0,
userMessageCount: data.userMessageCount ?? 0,
assistantMessageCount: data.assistantMessageCount ?? 0,
toolCallCount: data.toolCallCount ?? 0,
estimatedCostUsd: data.estimatedCostUsd,
totalInputTokens: data.totalInputTokens,
totalOutputTokens: data.totalOutputTokens,
cacheCreationTokens: data.cacheCreationTokens,
cacheReadTokens: data.cacheReadTokens,
primaryModel: data.primaryModel,
modelsUsed: data.modelsUsed,
generatedTitle: data.generatedTitle,
customTitle: data.customTitle,
summary: data.summary,
sessionCharacter: data.sessionCharacter,
sourceTool: data.sourceTool,
usageSource: data.usageSource,
};
}
// ──────────────────────────────────────────────────────
// FirestoreDataSource
// ──────────────────────────────────────────────────────
export class FirestoreDataSource {
config;
name = 'firestore';
constructor(config) {
this.config = config;
}
async prepare(flags) {
initializeFirebase(this.config);
if (!flags.noSync) {
try {
const { runSync } = await import('../../sync.js');
const result = await runSync({ quiet: true });
if (result.syncedCount > 0) {
return { message: `Synced ${result.syncedCount} new sessions`, dataChanged: true };
}
return { message: 'Up to date', dataChanged: false };
}
catch {
return { message: 'Sync failed (showing cached data)', dataChanged: false };
}
}
return { message: 'Sync skipped', dataChanged: false };
}
async getSessions(opts) {
const firestore = getDb();
let query = firestore.collection('sessions');
if (opts.projectId) {
query = query.where('projectId', '==', opts.projectId);
}
if (opts.sourceTool) {
query = query.where('sourceTool', '==', opts.sourceTool);
}
if (opts.periodStart) {
query = query.where('startedAt', '>=', admin.firestore.Timestamp.fromDate(opts.periodStart));
}
query = query.orderBy('startedAt', 'desc');
try {
const snapshot = await query.get();
return snapshot.docs.map(docToSessionRow);
}
catch (error) {
if (isFirestoreIndexError(error)) {
const url = extractIndexUrl(error);
throw new FirestoreIndexError(url
? `Missing Firestore index. Create it here: ${url}`
: 'Missing Firestore composite index. Check the error details in Firebase console.', url ?? '');
}
throw error;
}
}
async getUsageStats() {
const firestore = getDb();
const doc = await firestore.collection('stats').doc('usage').get();
if (!doc.exists)
return null;
const data = doc.data();
return {
totalInputTokens: data.totalInputTokens ?? 0,
totalOutputTokens: data.totalOutputTokens ?? 0,
cacheCreationTokens: data.cacheCreationTokens ?? 0,
cacheReadTokens: data.cacheReadTokens ?? 0,
estimatedCostUsd: data.estimatedCostUsd ?? 0,
sessionsWithUsage: data.sessionsWithUsage ?? 0,
lastUpdatedAt: data.lastUpdatedAt?.toDate() ?? new Date(),
};
}
async resolveProjectId(name) {
const projects = await getProjects();
// Exact match (case-insensitive)
const exact = projects.find((p) => p.name.toLowerCase() === name.toLowerCase());
if (exact)
return { projectId: exact.id, projectName: exact.name };
// Substring match
const substring = projects.filter((p) => p.name.toLowerCase().includes(name.toLowerCase()));
if (substring.length === 1)
return { projectId: substring[0].id, projectName: substring[0].name };
// No match — throw with suggestions
const suggestions = findSimilarNames(name, projects.map((p) => p.name));
throw new ProjectNotFoundError(`Project "${name}" not found.`, name, projects.map((p) => ({ name: p.name })), suggestions);
}
async getLastSession(opts) {
const firestore = getDb();
let query = firestore.collection('sessions');
if (opts?.sourceTool) {
query = query.where('sourceTool', '==', opts.sourceTool);
}
if (opts?.projectId) {
query = query.where('projectId', '==', opts.projectId);
}
query = query.orderBy('startedAt', 'desc').limit(1);
try {
const snapshot = await query.get();
if (snapshot.empty)
return null;
return docToSessionRow(snapshot.docs[0]);
}
catch (error) {
if (isFirestoreIndexError(error)) {
const url = extractIndexUrl(error);
throw new FirestoreIndexError(url
? `Missing Firestore index. Create it here: ${url}`
: 'Missing Firestore composite index. Check the error details in Firebase console.', url ?? '');
}
throw error;
}
}
}
//# sourceMappingURL=firestore.js.map
{"version":3,"file":"firestore.js","sourceRoot":"","sources":["../../../../src/commands/stats/data/firestore.ts"],"names":[],"mappings":"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"}
import admin from 'firebase-admin';
import type { ClaudeInsightConfig, ParsedSession, Project } from '../types.js';
/**
* Initialize Firebase Admin SDK
*/
export declare function initializeFirebase(config: ClaudeInsightConfig): void;
/**
* Get Firestore instance
*/
export declare function getDb(): admin.firestore.Firestore;
/**
* Upload a session to Firestore
*/
export declare function uploadSession(session: ParsedSession, isForceSync?: boolean): Promise<void>;
/**
* Upload messages to Firestore for LLM analysis
*/
export declare function uploadMessages(session: ParsedSession): Promise<void>;
/**
* Check if a session already exists
*/
export declare function sessionExists(sessionId: string): Promise<boolean>;
/**
* Get all projects
*/
export declare function getProjects(): Promise<Project[]>;
/**
* Get recent sessions
*/
export declare function getRecentSessions(limit?: number): Promise<ParsedSession[]>;
/**
* Recalculate usage stats from all sessions in Firestore.
* Used after --force sync to reconcile totals.
*/
export declare function recalculateUsageStats(): Promise<{
sessionsWithUsage: number;
totalTokens: number;
estimatedCostUsd: number;
}>;
/**
* Get per-tool session counts grouped by project.
* Uses select() to only fetch projectId and sourceTool fields (efficient).
*/
export declare function getProjectToolCounts(): Promise<Map<string, Map<string, number>>>;
//# sourceMappingURL=client.d.ts.map
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/firebase/client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,gBAAgB,CAAC;AACnC,OAAO,KAAK,EAAE,mBAAmB,EAAE,aAAa,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAK/E;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,mBAAmB,GAAG,IAAI,CAmBpE;AAED;;GAEG;AACH,wBAAgB,KAAK,IAAI,KAAK,CAAC,SAAS,CAAC,SAAS,CAKjD;AAED;;GAEG;AACH,wBAAsB,aAAa,CAAC,OAAO,EAAE,aAAa,EAAE,WAAW,UAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,CAkG9F;AAED;;GAEG;AACH,wBAAsB,cAAc,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAsD1E;AAED;;GAEG;AACH,wBAAsB,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAIvE;AAED;;GAEG;AACH,wBAAsB,WAAW,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC,CAetD;AAED;;GAEG;AACH,wBAAsB,iBAAiB,CAAC,KAAK,GAAE,MAAW,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC,CA8BpF;AAGD;;;GAGG;AACH,wBAAsB,qBAAqB,IAAI,OAAO,CAAC;IACrD,iBAAiB,EAAE,MAAM,CAAC;IAC1B,WAAW,EAAE,MAAM,CAAC;IACpB,gBAAgB,EAAE,MAAM,CAAC;CAC1B,CAAC,CAwFD;AAED;;;GAGG;AACH,wBAAsB,oBAAoB,IAAI,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,CAsBtF"}
import admin from 'firebase-admin';
import { generateStableProjectId, getDeviceInfo } from '../utils/device.js';
let db = null;
/**
* Initialize Firebase Admin SDK
*/
export function initializeFirebase(config) {
if (!config.firebase) {
throw new Error('Firebase credentials not configured. Run `code-insights init` to set up.');
}
if (admin.apps.length > 0) {
db = admin.firestore();
return;
}
admin.initializeApp({
credential: admin.credential.cert({
projectId: config.firebase.projectId,
clientEmail: config.firebase.clientEmail,
privateKey: config.firebase.privateKey.replace(/\\n/g, '\n'),
}),
});
db = admin.firestore();
}
/**
* Get Firestore instance
*/
export function getDb() {
if (!db) {
throw new Error('Firebase not initialized. Call initializeFirebase first.');
}
return db;
}
/**
* Upload a session to Firestore
*/
export async function uploadSession(session, isForceSync = false) {
const firestore = getDb();
// Generate stable project ID (prefers git remote URL)
const { projectId, source: projectIdSource, gitRemoteUrl } = generateStableProjectId(session.projectPath);
// Get device info for multi-device support
const deviceInfo = getDeviceInfo();
// Check if session already exists (for idempotent session count)
const sessionRef = firestore.collection('sessions').doc(session.id);
const existingSession = await sessionRef.get();
const isNewSession = !existingSession.exists;
const batch = firestore.batch();
// Upsert project
const projectRef = firestore.collection('projects').doc(projectId);
batch.set(projectRef, {
name: session.projectName,
path: session.projectPath,
gitRemoteUrl: gitRemoteUrl,
projectIdSource: projectIdSource,
lastActivity: admin.firestore.Timestamp.fromDate(session.endedAt),
updatedAt: admin.firestore.FieldValue.serverTimestamp(),
}, { merge: true });
// Only increment session count for NEW sessions (idempotent)
if (isNewSession) {
const incrementFields = {
sessionCount: admin.firestore.FieldValue.increment(1),
};
if (!isForceSync && session.usage) {
incrementFields.totalInputTokens = admin.firestore.FieldValue.increment(session.usage.totalInputTokens);
incrementFields.totalOutputTokens = admin.firestore.FieldValue.increment(session.usage.totalOutputTokens);
incrementFields.cacheCreationTokens = admin.firestore.FieldValue.increment(session.usage.cacheCreationTokens);
incrementFields.cacheReadTokens = admin.firestore.FieldValue.increment(session.usage.cacheReadTokens);
incrementFields.estimatedCostUsd = admin.firestore.FieldValue.increment(session.usage.estimatedCostUsd);
}
batch.update(projectRef, incrementFields);
}
// Upload session with device info
batch.set(sessionRef, {
projectId: projectId,
projectName: session.projectName,
projectPath: session.projectPath,
gitRemoteUrl: gitRemoteUrl,
summary: session.summary,
generatedTitle: session.generatedTitle,
titleSource: session.titleSource,
sessionCharacter: session.sessionCharacter,
startedAt: admin.firestore.Timestamp.fromDate(session.startedAt),
endedAt: admin.firestore.Timestamp.fromDate(session.endedAt),
messageCount: session.messageCount,
userMessageCount: session.userMessageCount,
assistantMessageCount: session.assistantMessageCount,
toolCallCount: session.toolCallCount,
gitBranch: session.gitBranch,
claudeVersion: session.claudeVersion,
sourceTool: session.sourceTool ?? 'claude-code',
// Device info for multi-device tracking
deviceId: deviceInfo.deviceId,
deviceHostname: deviceInfo.hostname,
devicePlatform: deviceInfo.platform,
syncedAt: admin.firestore.FieldValue.serverTimestamp(),
// Usage stats (conditional — absent for older sessions without token data)
...(session.usage ? {
totalInputTokens: session.usage.totalInputTokens,
totalOutputTokens: session.usage.totalOutputTokens,
cacheCreationTokens: session.usage.cacheCreationTokens,
cacheReadTokens: session.usage.cacheReadTokens,
estimatedCostUsd: session.usage.estimatedCostUsd,
modelsUsed: session.usage.modelsUsed,
primaryModel: session.usage.primaryModel,
usageSource: session.usage.usageSource,
} : {}),
});
// Atomically increment usage stats for new sessions (non-force only)
if (isNewSession && !isForceSync && session.usage) {
const statsRef = firestore.collection('stats').doc('usage');
batch.set(statsRef, {
totalInputTokens: admin.firestore.FieldValue.increment(session.usage.totalInputTokens),
totalOutputTokens: admin.firestore.FieldValue.increment(session.usage.totalOutputTokens),
cacheCreationTokens: admin.firestore.FieldValue.increment(session.usage.cacheCreationTokens),
cacheReadTokens: admin.firestore.FieldValue.increment(session.usage.cacheReadTokens),
estimatedCostUsd: admin.firestore.FieldValue.increment(session.usage.estimatedCostUsd),
sessionsWithUsage: admin.firestore.FieldValue.increment(1),
lastUpdatedAt: admin.firestore.FieldValue.serverTimestamp(),
}, { merge: true });
}
await batch.commit();
}
/**
* Upload messages to Firestore for LLM analysis
*/
export async function uploadMessages(session) {
if (session.messages.length === 0)
return;
const firestore = getDb();
const batches = [];
let currentBatch = firestore.batch();
let operationCount = 0;
for (const message of session.messages) {
const messageRef = firestore.collection('messages').doc(message.id);
currentBatch.set(messageRef, {
sessionId: message.sessionId,
type: message.type,
content: truncateContent(message.content, 10000),
thinking: message.thinking
? truncateContent(message.thinking, 5000)
: null,
toolCalls: message.toolCalls.map((tc) => ({
id: tc.id,
name: tc.name,
input: JSON.stringify(tc.input).slice(0, 1000),
})),
toolResults: message.toolResults.map((tr) => ({
toolUseId: tr.toolUseId,
output: truncateContent(tr.output, 2000),
})),
timestamp: admin.firestore.Timestamp.fromDate(message.timestamp),
parentId: message.parentId,
// Per-message usage (assistant messages only)
...(message.usage ? {
usage: {
inputTokens: message.usage.inputTokens,
outputTokens: message.usage.outputTokens,
cacheCreationTokens: message.usage.cacheCreationTokens,
cacheReadTokens: message.usage.cacheReadTokens,
model: message.usage.model,
estimatedCostUsd: message.usage.estimatedCostUsd,
},
} : {}),
});
operationCount++;
if (operationCount >= 500) {
batches.push(currentBatch);
currentBatch = firestore.batch();
operationCount = 0;
}
}
if (operationCount > 0) {
batches.push(currentBatch);
}
await Promise.all(batches.map((batch) => batch.commit()));
}
/**
* Check if a session already exists
*/
export async function sessionExists(sessionId) {
const firestore = getDb();
const doc = await firestore.collection('sessions').doc(sessionId).get();
return doc.exists;
}
/**
* Get all projects
*/
export async function getProjects() {
const firestore = getDb();
const snapshot = await firestore.collection('projects').orderBy('lastActivity', 'desc').get();
return snapshot.docs.map((doc) => {
const data = doc.data();
return {
id: doc.id,
name: data.name,
path: data.path,
sessionCount: data.sessionCount || 0,
lastActivity: data.lastActivity?.toDate() || new Date(),
createdAt: data.createdAt?.toDate() || new Date(),
};
});
}
/**
* Get recent sessions
*/
export async function getRecentSessions(limit = 10) {
const firestore = getDb();
const snapshot = await firestore
.collection('sessions')
.orderBy('endedAt', 'desc')
.limit(limit)
.get();
return snapshot.docs.map((doc) => {
const data = doc.data();
return {
id: doc.id,
projectPath: data.projectPath,
projectName: data.projectName,
summary: data.summary,
startedAt: data.startedAt?.toDate() || new Date(),
endedAt: data.endedAt?.toDate() || new Date(),
messageCount: data.messageCount,
userMessageCount: data.userMessageCount,
assistantMessageCount: data.assistantMessageCount,
toolCallCount: data.toolCallCount,
messages: [],
insights: [],
gitBranch: data.gitBranch,
claudeVersion: data.claudeVersion,
generatedTitle: data.generatedTitle || null,
titleSource: data.titleSource || null,
sessionCharacter: data.sessionCharacter || null,
};
});
}
/**
* Recalculate usage stats from all sessions in Firestore.
* Used after --force sync to reconcile totals.
*/
export async function recalculateUsageStats() {
const firestore = getDb();
// Query all sessions that have usage data
const snapshot = await firestore
.collection('sessions')
.where('usageSource', '==', 'jsonl')
.get();
// Aggregate global + per-project totals
const global = {
totalInputTokens: 0,
totalOutputTokens: 0,
cacheCreationTokens: 0,
cacheReadTokens: 0,
estimatedCostUsd: 0,
sessionsWithUsage: 0,
};
const perProject = {};
for (const doc of snapshot.docs) {
const data = doc.data();
global.totalInputTokens += data.totalInputTokens ?? 0;
global.totalOutputTokens += data.totalOutputTokens ?? 0;
global.cacheCreationTokens += data.cacheCreationTokens ?? 0;
global.cacheReadTokens += data.cacheReadTokens ?? 0;
global.estimatedCostUsd += data.estimatedCostUsd ?? 0;
global.sessionsWithUsage++;
const pid = data.projectId;
if (pid) {
if (!perProject[pid]) {
perProject[pid] = {
totalInputTokens: 0,
totalOutputTokens: 0,
cacheCreationTokens: 0,
cacheReadTokens: 0,
estimatedCostUsd: 0,
};
}
perProject[pid].totalInputTokens += data.totalInputTokens ?? 0;
perProject[pid].totalOutputTokens += data.totalOutputTokens ?? 0;
perProject[pid].cacheCreationTokens += data.cacheCreationTokens ?? 0;
perProject[pid].cacheReadTokens += data.cacheReadTokens ?? 0;
perProject[pid].estimatedCostUsd += data.estimatedCostUsd ?? 0;
}
}
// Write global stats (overwrite, not merge)
const statsRef = firestore.collection('stats').doc('usage');
await statsRef.set({
...global,
lastUpdatedAt: admin.firestore.FieldValue.serverTimestamp(),
});
// Update each project's usage fields
const batches = [];
let currentBatch = firestore.batch();
let opCount = 0;
for (const [projectId, usage] of Object.entries(perProject)) {
const projectRef = firestore.collection('projects').doc(projectId);
currentBatch.set(projectRef, usage, { merge: true });
opCount++;
if (opCount >= 500) {
batches.push(currentBatch);
currentBatch = firestore.batch();
opCount = 0;
}
}
if (opCount > 0)
batches.push(currentBatch);
await Promise.all(batches.map(b => b.commit()));
const totalTokens = global.totalInputTokens + global.totalOutputTokens
+ global.cacheCreationTokens + global.cacheReadTokens;
return {
sessionsWithUsage: global.sessionsWithUsage,
totalTokens,
estimatedCostUsd: global.estimatedCostUsd,
};
}
/**
* Get per-tool session counts grouped by project.
* Uses select() to only fetch projectId and sourceTool fields (efficient).
*/
export async function getProjectToolCounts() {
const firestore = getDb();
const snapshot = await firestore
.collection('sessions')
.select('projectId', 'sourceTool')
.get();
const counts = new Map();
for (const doc of snapshot.docs) {
const data = doc.data();
const projectId = data.projectId;
const sourceTool = data.sourceTool || 'claude-code';
if (!counts.has(projectId)) {
counts.set(projectId, new Map());
}
const toolMap = counts.get(projectId);
toolMap.set(sourceTool, (toolMap.get(sourceTool) || 0) + 1);
}
return counts;
}
/**
* Truncate content to max length
*/
function truncateContent(content, maxLength) {
if (content.length <= maxLength)
return content;
return content.slice(0, maxLength - 20) + '\n... [truncated]';
}
//# sourceMappingURL=client.js.map
{"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/firebase/client.ts"],"names":[],"mappings":"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"}
import type { ParsedSession, Insight, ParsedInsightContent } from '../types.js';
export declare function parseInsightContent(raw: string): ParsedInsightContent;
/**
* Extract insights from a parsed session using pattern matching
*/
export declare function extractInsights(session: ParsedSession): Insight[];
//# sourceMappingURL=insights.d.ts.map
{"version":3,"file":"insights.d.ts","sourceRoot":"","sources":["../../src/parser/insights.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAiB,OAAO,EAAmB,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAGhH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,oBAAoB,CAMrE;AAuDD;;GAEG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,EAAE,CA4BjE"}
import { v4 as uuidv4 } from 'uuid';
export function parseInsightContent(raw) {
const isClaudeInsight = raw.includes('★ Insight') || raw.includes('★Insight');
if (isClaudeInsight) {
return parseClaudeFormattedInsight(raw);
}
return parseGenericContent(raw);
}
function parseClaudeFormattedInsight(raw) {
let cleaned = raw.replace(/★\s*Insight\s*─*/g, '').replace(/─+/g, '').replace(/\*\*/g, '').trim();
const lines = cleaned.split('\n').map(l => l.trim()).filter(l => l);
const title = (lines[0] || '').replace(/:$/, '').trim();
const bullets = lines.slice(1).filter(l => l.startsWith('-')).map(l => l.replace(/^-\s*/, '').trim());
const summary = bullets.length > 0 ? `${title}: ${bullets[0]}` : title;
return { title, summary, bullets, rawContent: raw };
}
function parseGenericContent(raw) {
let cleaned = raw.replace(/\*\*/g, '').replace(/\\"/g, '"').replace(/\\n/g, '\n').trim();
const lines = cleaned.split('\n').map(l => l.trim()).filter(l => l);
const title = truncate(lines[0] || cleaned, 100);
const bullets = lines.slice(1).filter(l => l.startsWith('-') || l.startsWith('•')).map(l => l.replace(/^[-•]\s*/, '').trim());
return { title, summary: title, bullets, rawContent: raw };
}
// Pattern matching rules for different insight types
const DECISION_PATTERNS = [
/decided to (.+)/i,
/chose (.+) over (.+)/i,
/went with (.+) because (.+)/i,
/trade-off:?\s*(.+)/i,
/approach:?\s*(.+)/i,
/\*\*decision\*\*:?\s*(.+)/i,
/we('ll| will) use (.+) (for|to|because)/i,
/let's go with (.+)/i,
/the (best|right|better) (approach|solution|choice) is (.+)/i,
];
const LEARNING_PATTERNS = [
/learned that (.+)/i,
/TIL:?\s*(.+)/i,
/insight:?\s*(.+)/i,
/realized (.+)/i,
/mistake:?\s*(.+)/i,
/note to self:?\s*(.+)/i,
/important:?\s*(.+)/i,
/remember:?\s*(.+)/i,
/turns out (.+)/i,
/didn't know (.+)/i,
];
const WORKITEM_SIGNALS = {
feature: ['added', 'implemented', 'created', 'built', 'new feature', 'introducing'],
bugfix: ['fixed', 'resolved', 'patched', 'corrected', 'bug fix', 'fixing'],
refactor: ['refactored', 'restructured', 'reorganized', 'cleaned', 'improved', 'simplified'],
docs: ['documented', 'documentation', 'readme', 'comments', 'jsdoc'],
test: ['tested', 'test', 'spec', 'coverage', 'unit test', 'integration test'],
};
const WORK_TOOLS = ['Edit', 'Write', 'Bash'];
/**
* Extract insights from a parsed session using pattern matching
*/
export function extractInsights(session) {
const insights = [];
const projectId = generateProjectId(session.projectPath);
// Extract from assistant messages
for (const message of session.messages) {
if (message.type === 'assistant') {
// Check for decisions
const decisions = extractDecisions(message, session, projectId);
insights.push(...decisions);
// Check for learnings
const learnings = extractLearnings(message, session, projectId);
insights.push(...learnings);
// Check for work items from tool calls
const workItems = extractWorkItems(message, session, projectId);
insights.push(...workItems);
}
}
// Add effort insight for the session
const effortInsight = createEffortInsight(session, projectId);
if (effortInsight) {
insights.push(effortInsight);
}
return insights;
}
/**
* Extract decision insights from a message
*/
function extractDecisions(message, session, projectId) {
const insights = [];
const content = message.content;
for (const pattern of DECISION_PATTERNS) {
const match = content.match(pattern);
if (match) {
const extractedText = match[1] || match[0];
// Get surrounding context (up to 200 chars)
const matchIndex = content.indexOf(match[0]);
const contextStart = Math.max(0, matchIndex - 100);
const contextEnd = Math.min(content.length, matchIndex + match[0].length + 100);
const context = content.slice(contextStart, contextEnd);
insights.push({
id: uuidv4(),
sessionId: session.id,
projectId,
projectName: session.projectName,
type: 'decision',
title: truncate(extractedText, 100),
content: context,
summary: '',
bullets: [],
confidence: 0.7,
source: 'pattern',
metadata: {
reasoning: extractedText,
},
timestamp: message.timestamp,
});
// Only extract first match per pattern to avoid duplicates
break;
}
}
return insights;
}
/**
* Extract learning insights from a message
*/
function extractLearnings(message, session, projectId) {
const insights = [];
const content = message.content;
for (const pattern of LEARNING_PATTERNS) {
const match = content.match(pattern);
if (match) {
const extractedText = match[1] || match[0];
// Get surrounding context
const matchIndex = content.indexOf(match[0]);
const contextStart = Math.max(0, matchIndex - 100);
const contextEnd = Math.min(content.length, matchIndex + match[0].length + 100);
const context = content.slice(contextStart, contextEnd);
insights.push({
id: uuidv4(),
sessionId: session.id,
projectId,
projectName: session.projectName,
type: 'learning',
title: truncate(extractedText, 100),
content: context,
summary: '',
bullets: [],
confidence: 0.6,
source: 'pattern',
metadata: {},
timestamp: message.timestamp,
});
break;
}
}
return insights;
}
/**
* Extract work item insights from tool calls
*/
function extractWorkItems(message, session, projectId) {
const insights = [];
// Check for file-modifying tool calls
const fileEdits = message.toolCalls.filter((tc) => WORK_TOOLS.includes(tc.name));
if (fileEdits.length === 0) {
return insights;
}
// Extract files modified
const files = [];
for (const tc of fileEdits) {
if (tc.name === 'Edit' || tc.name === 'Write') {
const filePath = tc.input.file_path;
if (filePath) {
files.push(filePath);
}
}
}
if (files.length === 0) {
return insights;
}
// Determine work type from message content
const workType = determineWorkType(message.content);
// Create a consolidated work item for this message
insights.push({
id: uuidv4(),
sessionId: session.id,
projectId,
projectName: session.projectName,
type: 'workitem',
title: `${capitalizeFirst(workType)}: ${files.length} file(s) modified`,
content: `Files: ${files.join(', ')}`,
summary: '',
bullets: [],
confidence: 0.9,
source: 'pattern',
metadata: {
files,
workType,
},
timestamp: message.timestamp,
});
return insights;
}
/**
* Create an effort insight for the session
*/
function createEffortInsight(session, projectId) {
// Calculate session duration in minutes
const durationMs = session.endedAt.getTime() - session.startedAt.getTime();
const durationMinutes = Math.round(durationMs / 60000);
if (durationMinutes < 1) {
return null;
}
return {
id: uuidv4(),
sessionId: session.id,
projectId,
projectName: session.projectName,
type: 'effort',
title: `Session: ${durationMinutes} min, ${session.messageCount} messages`,
content: `User: ${session.userMessageCount} messages, Assistant: ${session.assistantMessageCount} messages, Tool calls: ${session.toolCallCount}`,
summary: '',
bullets: [],
confidence: 1.0,
source: 'pattern',
metadata: {
duration: durationMinutes,
},
timestamp: session.startedAt,
};
}
/**
* Determine work type from message content
*/
function determineWorkType(content) {
const lowerContent = content.toLowerCase();
for (const [workType, signals] of Object.entries(WORKITEM_SIGNALS)) {
for (const signal of signals) {
if (lowerContent.includes(signal)) {
return workType;
}
}
}
return 'feature'; // Default
}
/**
* Generate a stable project ID from path
*/
function generateProjectId(projectPath) {
// Create a simple hash from the path
let hash = 0;
for (let i = 0; i < projectPath.length; i++) {
const char = projectPath.charCodeAt(i);
hash = (hash << 5) - hash + char;
hash = hash & hash;
}
return `proj_${Math.abs(hash).toString(16)}`;
}
/**
* Truncate string to max length
*/
function truncate(str, maxLength) {
if (str.length <= maxLength)
return str;
return str.slice(0, maxLength - 3) + '...';
}
/**
* Capitalize first letter
*/
function capitalizeFirst(str) {
return str.charAt(0).toUpperCase() + str.slice(1);
}
//# sourceMappingURL=insights.js.map
{"version":3,"file":"insights.js","sourceRoot":"","sources":["../../src/parser/insights.ts"],"names":[],"mappings":"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"}
import type { FirebaseServiceAccountJson, FirebaseWebConfig } from '../types.js';
export type FileReadResult<T> = {
success: true;
data: T;
} | {
success: false;
error: 'not_found' | 'invalid_json' | 'permission_denied' | 'unknown';
message: string;
};
/**
* Validate Firebase Service Account JSON structure
*/
export declare function validateServiceAccountJson(data: unknown): data is FirebaseServiceAccountJson;
/**
* Check if data looks like a service account (for cross-type detection)
*/
export declare function looksLikeServiceAccount(data: unknown): boolean;
/**
* Check if data looks like a web config (for cross-type detection)
*/
export declare function looksLikeWebConfig(data: unknown): boolean;
/**
* Validate Firebase Web SDK config structure
*/
export declare function validateWebConfig(data: unknown): data is FirebaseWebConfig;
/**
* Parse Firebase's JavaScript config snippet into a JSON object.
*
* Firebase Console gives you JavaScript like:
* const firebaseConfig = {
* apiKey: "AIza...",
* authDomain: "my-project.firebaseapp.com",
* ...
* };
*
* This function extracts the object and converts it to a parsed object.
*/
export declare function parseFirebaseJsConfig(content: string): Record<string, unknown> | null;
/**
* Read a Firebase config file — accepts JSON or JavaScript format.
*
* Tries JSON.parse first. If that fails, tries to extract a JS object
* literal (handles the raw Firebase Console snippet).
*/
export declare function readFirebaseConfigFile<T>(filePath: string): FileReadResult<T>;
/**
* Resolve file path (expand ~ to home directory)
*/
export declare function resolveFilePath(filePath: string): string;
/**
* Check if file exists
*/
export declare function fileExists(filePath: string): boolean;
/**
* Read and parse a JSON file with detailed error reporting
*/
export declare function readJsonFileWithError<T>(filePath: string): FileReadResult<T>;
/**
* Read and parse a JSON file (simple version, returns null on error)
*/
export declare function readJsonFile<T>(filePath: string): T | null;
/**
* Extract service account config from JSON file
*/
export declare function extractServiceAccountConfig(json: FirebaseServiceAccountJson): {
projectId: string;
clientEmail: string;
privateKey: string;
};
/**
* Encode web config for URL parameter (base64url encoding per RFC 4648)
*
* The web app decodes this with:
* const base64 = encoded.replace(/-/g, '+').replace(/_/g, '/');
* const json = atob(base64);
* const config = JSON.parse(json);
*/
export declare function encodeWebConfigForUrl(config: FirebaseWebConfig): string;
/**
* Generate the web dashboard URL with embedded config
*
* Note: Firebase web API keys are designed for client-side use and are
* restricted by Firestore security rules, not by key secrecy. However,
* users should still avoid sharing this URL publicly.
*/
export declare function generateDashboardUrl(config: FirebaseWebConfig): string;
//# sourceMappingURL=firebase-json.d.ts.map
{"version":3,"file":"firebase-json.d.ts","sourceRoot":"","sources":["../../src/utils/firebase-json.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,0BAA0B,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAEjF,MAAM,MAAM,cAAc,CAAC,CAAC,IACxB;IAAE,OAAO,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,CAAC,CAAA;CAAE,GAC1B;IAAE,OAAO,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,WAAW,GAAG,cAAc,GAAG,mBAAmB,GAAG,SAAS,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AAE/G;;GAEG;AACH,wBAAgB,0BAA0B,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,IAAI,0BAA0B,CAY5F;AAED;;GAEG;AACH,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,CAI9D;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,CAIzD;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,IAAI,iBAAiB,CAa1E;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAkCrF;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,GAAG,cAAc,CAAC,CAAC,CAAC,CAmC7E;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAExD;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAGpD;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,GAAG,cAAc,CAAC,CAAC,CAAC,CAoB5E;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,GAAG,CAAC,GAAG,IAAI,CAG1D;AAED;;GAEG;AACH,wBAAgB,2BAA2B,CAAC,IAAI,EAAE,0BAA0B;;;;EAM3E;AAED;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,iBAAiB,GAAG,MAAM,CAOvE;AAED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,iBAAiB,GAAG,MAAM,CAGtE"}
import * as fs from 'fs';
import * as os from 'os';
/**
* Validate Firebase Service Account JSON structure
*/
export function validateServiceAccountJson(data) {
if (!data || typeof data !== 'object')
return false;
const obj = data;
return (obj.type === 'service_account' &&
typeof obj.project_id === 'string' &&
typeof obj.private_key === 'string' &&
typeof obj.client_email === 'string' &&
obj.private_key.includes('-----BEGIN PRIVATE KEY-----'));
}
/**
* Check if data looks like a service account (for cross-type detection)
*/
export function looksLikeServiceAccount(data) {
if (!data || typeof data !== 'object')
return false;
const obj = data;
return obj.type === 'service_account' || typeof obj.private_key === 'string';
}
/**
* Check if data looks like a web config (for cross-type detection)
*/
export function looksLikeWebConfig(data) {
if (!data || typeof data !== 'object')
return false;
const obj = data;
return typeof obj.apiKey === 'string' || typeof obj.authDomain === 'string';
}
/**
* Validate Firebase Web SDK config structure
*/
export function validateWebConfig(data) {
if (!data || typeof data !== 'object')
return false;
const obj = data;
return (typeof obj.apiKey === 'string' &&
typeof obj.authDomain === 'string' &&
typeof obj.projectId === 'string' &&
typeof obj.storageBucket === 'string' &&
typeof obj.messagingSenderId === 'string' &&
typeof obj.appId === 'string');
}
/**
* Parse Firebase's JavaScript config snippet into a JSON object.
*
* Firebase Console gives you JavaScript like:
* const firebaseConfig = {
* apiKey: "AIza...",
* authDomain: "my-project.firebaseapp.com",
* ...
* };
*
* This function extracts the object and converts it to a parsed object.
*/
export function parseFirebaseJsConfig(content) {
try {
// Strip single-line comments
let cleaned = content.replace(/\/\/.*$/gm, '');
// Strip multi-line comments
cleaned = cleaned.replace(/\/\*[\s\S]*?\*\//g, '');
// Strip import statements
cleaned = cleaned.replace(/^import\s+.*$/gm, '');
// Strip lines like: const app = initializeApp(firebaseConfig);
cleaned = cleaned.replace(/^.*initializeApp.*$/gm, '');
// Extract the object literal between first { and last }
const firstBrace = cleaned.indexOf('{');
const lastBrace = cleaned.lastIndexOf('}');
if (firstBrace === -1 || lastBrace === -1 || lastBrace <= firstBrace) {
return null;
}
let objectStr = cleaned.substring(firstBrace, lastBrace + 1);
// Quote unquoted keys at start of lines: apiKey: → "apiKey":
// Uses ^ with multiline to avoid matching colons inside string values
objectStr = objectStr.replace(/^(\s*)(\w+)\s*:/gm, '$1"$2":');
// Remove trailing commas before }
objectStr = objectStr.replace(/,\s*}/g, '}');
return JSON.parse(objectStr);
}
catch {
return null;
}
}
/**
* Read a Firebase config file — accepts JSON or JavaScript format.
*
* Tries JSON.parse first. If that fails, tries to extract a JS object
* literal (handles the raw Firebase Console snippet).
*/
export function readFirebaseConfigFile(filePath) {
const resolvedPath = resolveFilePath(filePath);
try {
if (!fs.existsSync(resolvedPath)) {
return { success: false, error: 'not_found', message: `File not found: ${filePath}` };
}
const content = fs.readFileSync(resolvedPath, 'utf-8');
// Try JSON first
try {
const data = JSON.parse(content);
return { success: true, data };
}
catch {
// Not valid JSON — try parsing as JavaScript
}
// Try Firebase JS config format
const parsed = parseFirebaseJsConfig(content);
if (parsed) {
return { success: true, data: parsed };
}
return {
success: false,
error: 'invalid_json',
message: 'Could not parse file as JSON or Firebase JavaScript config',
};
}
catch (err) {
if (err instanceof Error && 'code' in err && err.code === 'EACCES') {
return { success: false, error: 'permission_denied', message: 'Permission denied reading file' };
}
return { success: false, error: 'unknown', message: `Error reading file: ${err instanceof Error ? err.message : 'Unknown error'}` };
}
}
/**
* Resolve file path (expand ~ to home directory)
*/
export function resolveFilePath(filePath) {
return filePath.replace(/^~/, os.homedir());
}
/**
* Check if file exists
*/
export function fileExists(filePath) {
const resolvedPath = resolveFilePath(filePath);
return fs.existsSync(resolvedPath);
}
/**
* Read and parse a JSON file with detailed error reporting
*/
export function readJsonFileWithError(filePath) {
const resolvedPath = resolveFilePath(filePath);
try {
if (!fs.existsSync(resolvedPath)) {
return { success: false, error: 'not_found', message: `File not found: ${filePath}` };
}
const content = fs.readFileSync(resolvedPath, 'utf-8');
const data = JSON.parse(content);
return { success: true, data };
}
catch (err) {
if (err instanceof SyntaxError) {
return { success: false, error: 'invalid_json', message: 'File contains invalid JSON' };
}
if (err instanceof Error && 'code' in err && err.code === 'EACCES') {
return { success: false, error: 'permission_denied', message: 'Permission denied reading file' };
}
return { success: false, error: 'unknown', message: `Error reading file: ${err instanceof Error ? err.message : 'Unknown error'}` };
}
}
/**
* Read and parse a JSON file (simple version, returns null on error)
*/
export function readJsonFile(filePath) {
const result = readJsonFileWithError(filePath);
return result.success ? result.data : null;
}
/**
* Extract service account config from JSON file
*/
export function extractServiceAccountConfig(json) {
return {
projectId: json.project_id,
clientEmail: json.client_email,
privateKey: json.private_key,
};
}
/**
* Encode web config for URL parameter (base64url encoding per RFC 4648)
*
* The web app decodes this with:
* const base64 = encoded.replace(/-/g, '+').replace(/_/g, '/');
* const json = atob(base64);
* const config = JSON.parse(json);
*/
export function encodeWebConfigForUrl(config) {
const json = JSON.stringify(config);
return Buffer.from(json, 'utf-8')
.toString('base64')
.replace(/\+/g, '-')
.replace(/\//g, '_')
.replace(/=+$/, '');
}
/**
* Generate the web dashboard URL with embedded config
*
* Note: Firebase web API keys are designed for client-side use and are
* restricted by Firestore security rules, not by key secrecy. However,
* users should still avoid sharing this URL publicly.
*/
export function generateDashboardUrl(config) {
const encodedConfig = encodeWebConfigForUrl(config);
return `https://code-insights.app/?config=${encodedConfig}`;
}
//# sourceMappingURL=firebase-json.js.map
{"version":3,"file":"firebase-json.js","sourceRoot":"","sources":["../../src/utils/firebase-json.ts"],"names":[],"mappings":"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"}
export interface ServerOptions {
port: number;
staticDir: string;
openBrowser: boolean;
}
/**
* Start the Code Insights local dashboard server.
* Serves the Hono API and the pre-built Vite SPA from staticDir.
* Called by the CLI `dashboard` command.
*/
export declare function startServer(options: ServerOptions): Promise<void>;
//# sourceMappingURL=index.d.ts.map
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAeA,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IAEb,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,OAAO,CAAC;CACtB;AAED;;;;GAIG;AACH,wBAAsB,WAAW,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAiFvE"}
import { serve } from '@hono/node-server';
import { serveStatic } from '@hono/node-server/serve-static';
import { Hono } from 'hono';
import { existsSync, readFileSync } from 'fs';
import { relative, join } from 'path';
import { openUrl } from '@code-insights/cli/utils/browser';
import projectsRouter from './routes/projects.js';
import sessionsRouter from './routes/sessions.js';
import messagesRouter from './routes/messages.js';
import insightsRouter from './routes/insights.js';
import analysisRouter from './routes/analysis.js';
import analyticsRouter from './routes/analytics.js';
import configRouter from './routes/config.js';
import exportRouter from './routes/export.js';
/**
* Start the Code Insights local dashboard server.
* Serves the Hono API and the pre-built Vite SPA from staticDir.
* Called by the CLI `dashboard` command.
*/
export async function startServer(options) {
const { port, staticDir, openBrowser } = options;
const app = new Hono();
// Global error handler — prevents stack trace leakage to clients.
// Detects malformed JSON bodies (SyntaxError) and returns 400 instead of 500.
app.onError((err, c) => {
if (err instanceof SyntaxError && err.message.includes('JSON')) {
return c.json({ error: 'Invalid JSON in request body' }, 400);
}
console.error(err);
return c.json({ error: 'Internal server error' }, 500);
});
// API routes — all under /api
app.route('/api/projects', projectsRouter);
app.route('/api/sessions', sessionsRouter);
app.route('/api/messages', messagesRouter);
app.route('/api/insights', insightsRouter);
app.route('/api/analysis', analysisRouter);
app.route('/api/analytics', analyticsRouter);
app.route('/api/config', configRouter);
app.route('/api/export', exportRouter);
// Health check
app.get('/api/health', (c) => c.json({ ok: true, version: '0.1.0' }));
// API 404 catch-all — must come AFTER all /api sub-routers and BEFORE static serving.
// Without this, unmatched /api/* routes fall through to the SPA fallback and return
// index.html as 200, which breaks API clients expecting JSON errors.
app.all('/api/*', (c) => c.json({ error: 'Not found' }, 404));
// Static file serving — only if the dashboard has been built.
// serveStatic requires a path relative to process.cwd(), not an absolute path.
if (existsSync(staticDir)) {
const relativeStaticDir = relative(process.cwd(), staticDir);
app.use('/*', serveStatic({ root: relativeStaticDir }));
// Cache index.html at startup — avoids a readFileSync on every SPA navigation
// request, which would be a pointless disk read for a file that never changes
// while the server is running.
const indexPath = join(staticDir, 'index.html');
const indexHtml = existsSync(indexPath) ? readFileSync(indexPath, 'utf-8') : null;
// SPA fallback: any non-API route not matched by serveStatic serves index.html
// so react-router can handle client-side routing.
app.get('*', (c) => {
if (indexHtml)
return c.html(indexHtml);
return c.text('Dashboard not found. Run pnpm build first.', 404);
});
}
else {
// Dashboard not built — serve a helpful message
app.get('*', (c) => c.html(`
<html><body style="font-family:monospace;padding:2rem">
<h2>Code Insights Dashboard</h2>
<p>The dashboard has not been built yet.</p>
<pre>pnpm install &amp;&amp; pnpm build</pre>
<p>Then restart the server.</p>
</body></html>
`));
}
// Graceful shutdown: just call process.exit(0).
// The process 'exit' handler in cli/src/db/client.ts already calls closeDb()
// which runs WAL checkpoint. Calling closeDb() here would double-close.
const shutdown = () => {
process.exit(0);
};
process.on('SIGINT', shutdown);
process.on('SIGTERM', shutdown);
serve({ fetch: app.fetch, port }, (info) => {
const url = `http://localhost:${info.port}`;
console.log(` Code Insights dashboard running at ${url}`);
if (openBrowser) {
openUrl(url);
}
});
}
//# sourceMappingURL=index.js.map
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
import { type SQLiteMessageRow } from './prompts.js';
export type { SQLiteMessageRow };
export interface AnalysisProgress {
phase: 'loading_messages' | 'analyzing' | 'saving';
currentChunk?: number;
totalChunks?: number;
}
export interface AnalysisOptions {
onProgress?: (progress: AnalysisProgress) => void;
signal?: AbortSignal;
}
export interface AnalysisResult {
success: boolean;
insights: InsightRow[];
error?: string;
usage?: {
inputTokens: number;
outputTokens: number;
};
}
export interface InsightRow {
id: string;
session_id: string;
project_id: string;
project_name: string;
type: string;
title: string;
content: string;
summary: string;
bullets: string;
confidence: number;
source: 'llm';
metadata: string | null;
timestamp: string;
created_at: string;
scope: string;
analysis_version: string;
}
export interface SessionData {
id: string;
project_id: string;
project_name: string;
project_path: string;
summary: string | null;
ended_at: string;
}
/**
* Analyze a session and generate insights, saving them to SQLite.
*/
export declare function analyzeSession(session: SessionData, messages: SQLiteMessageRow[], options?: AnalysisOptions): Promise<AnalysisResult>;
/**
* Analyze prompt quality for a session.
*/
export declare function analyzePromptQuality(session: SessionData, messages: SQLiteMessageRow[], options?: AnalysisOptions): Promise<AnalysisResult>;
export interface RecurringInsightGroup {
insightIds: string[];
theme: string;
}
export interface RecurringInsightResult {
success: boolean;
groups: RecurringInsightGroup[];
updatedCount: number;
error?: string;
usage?: {
inputTokens: number;
outputTokens: number;
};
}
/**
* Find recurring patterns across multiple insights and write bidirectional links to SQLite.
*/
export declare function findRecurringInsights(insights: Array<{
id: string;
type: string;
title: string;
summary: string;
project_name: string;
session_id: string;
}>): Promise<RecurringInsightResult>;
//# sourceMappingURL=analysis.d.ts.map
{"version":3,"file":"analysis.d.ts","sourceRoot":"","sources":["../../src/llm/analysis.ts"],"names":[],"mappings":"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"}
// Core analysis engine — server-side.
// Ported from web repo (src/lib/llm/analysis.ts) with SQLite persistence replacing Firestore.
// Key differences from web repo:
// - Uses SQLiteMessageRow instead of web Message type
// - Writes insights directly to SQLite via getDb() (not Firestore)
// - Abort handling uses error.name === 'AbortError' (not DOMException)
// - Uses session's existing project_id from SQLite (not re-derived hash)
import { randomUUID } from 'crypto';
import { getDb } from '@code-insights/cli/db/client';
import { createLLMClient, isLLMConfigured } from './client.js';
import { SESSION_ANALYSIS_SYSTEM_PROMPT, generateSessionAnalysisPrompt, formatMessagesForAnalysis, parseAnalysisResponse, PROMPT_QUALITY_SYSTEM_PROMPT, generatePromptQualityPrompt, parsePromptQualityResponse, } from './prompts.js';
// Maximum tokens to send to LLM (leaving room for response)
const MAX_INPUT_TOKENS = 80000;
const ANALYSIS_VERSION = '2.0.0';
/**
* Analyze a session and generate insights, saving them to SQLite.
*/
export async function analyzeSession(session, messages, options) {
if (!isLLMConfigured()) {
return {
success: false,
insights: [],
error: 'LLM not configured. Run `code-insights config llm` to configure a provider.',
};
}
if (messages.length === 0) {
return {
success: false,
insights: [],
error: 'No messages found for this session.',
};
}
try {
const client = createLLMClient();
const formattedMessages = formatMessagesForAnalysis(messages);
const estimatedTokens = client.estimateTokens(formattedMessages);
let analysisResponse;
let totalInputTokens = 0;
let totalOutputTokens = 0;
if (estimatedTokens > MAX_INPUT_TOKENS) {
// Chunk the messages and analyze separately
const chunks = chunkMessages(messages, client.estimateTokens.bind(client));
const chunkResponses = [];
const totalChunks = chunks.length;
for (let i = 0; i < chunks.length; i++) {
const chunk = chunks[i];
options?.onProgress?.({ phase: 'analyzing', currentChunk: i + 1, totalChunks });
const chunkFormatted = formatMessagesForAnalysis(chunk);
const prompt = generateSessionAnalysisPrompt(session.project_name, session.summary, chunkFormatted);
const response = await client.chat([
{ role: 'system', content: SESSION_ANALYSIS_SYSTEM_PROMPT },
{ role: 'user', content: prompt },
], { signal: options?.signal });
if (response.usage) {
totalInputTokens += response.usage.inputTokens;
totalOutputTokens += response.usage.outputTokens;
}
const parsed = parseAnalysisResponse(response.content);
if (parsed)
chunkResponses.push(parsed);
}
analysisResponse = mergeAnalysisResponses(chunkResponses);
}
else {
options?.onProgress?.({ phase: 'analyzing', currentChunk: 1, totalChunks: 1 });
const prompt = generateSessionAnalysisPrompt(session.project_name, session.summary, formattedMessages);
const response = await client.chat([
{ role: 'system', content: SESSION_ANALYSIS_SYSTEM_PROMPT },
{ role: 'user', content: prompt },
], { signal: options?.signal });
if (response.usage) {
totalInputTokens = response.usage.inputTokens;
totalOutputTokens = response.usage.outputTokens;
}
const parsed = parseAnalysisResponse(response.content);
if (!parsed) {
return {
success: false,
insights: [],
error: 'Failed to parse LLM response. Please try again.',
};
}
analysisResponse = parsed;
}
options?.onProgress?.({ phase: 'saving' });
const insights = convertToInsightRows(analysisResponse, session);
// Save new insights first, then delete old non-prompt-quality insights
// (safe order: if save fails, old data is preserved)
saveInsightsToDb(insights);
deleteSessionInsights(session.id, {
excludeTypes: ['prompt_quality'],
excludeIds: insights.map(i => i.id),
});
return {
success: true,
insights,
usage: { inputTokens: totalInputTokens, outputTokens: totalOutputTokens },
};
}
catch (error) {
if (error instanceof Error && error.name === 'AbortError') {
return { success: false, insights: [], error: 'Analysis cancelled' };
}
return {
success: false,
insights: [],
error: error instanceof Error ? error.message : 'Analysis failed',
};
}
}
/**
* Analyze prompt quality for a session.
*/
export async function analyzePromptQuality(session, messages, options) {
if (!isLLMConfigured()) {
return {
success: false,
insights: [],
error: 'LLM not configured. Run `code-insights config llm` to configure a provider.',
};
}
if (messages.length === 0) {
return {
success: false,
insights: [],
error: 'No messages found for this session.',
};
}
const userMessages = messages.filter(m => m.type === 'user');
if (userMessages.length < 2) {
return {
success: false,
insights: [],
error: 'Not enough user messages to analyze prompt quality (need at least 2).',
};
}
try {
const client = createLLMClient();
const formattedMessages = formatMessagesForAnalysis(messages);
let analysisInput = formattedMessages;
const estimatedTokens = client.estimateTokens(formattedMessages);
if (estimatedTokens > MAX_INPUT_TOKENS) {
const targetLength = Math.floor((MAX_INPUT_TOKENS / estimatedTokens) * formattedMessages.length * 0.8);
analysisInput = formattedMessages.slice(0, targetLength) + '\n\n[... conversation truncated for analysis ...]';
}
const prompt = generatePromptQualityPrompt(session.project_name, analysisInput, messages.length);
options?.onProgress?.({ phase: 'analyzing' });
const response = await client.chat([
{ role: 'system', content: PROMPT_QUALITY_SYSTEM_PROMPT },
{ role: 'user', content: prompt },
], { signal: options?.signal });
const parsed = parsePromptQualityResponse(response.content);
if (!parsed) {
return {
success: false,
insights: [],
error: 'Failed to parse prompt quality analysis. Please try again.',
};
}
options?.onProgress?.({ phase: 'saving' });
const insight = convertPromptQualityToInsightRow(parsed, session);
// Save new insight, then delete old prompt_quality insights
saveInsightsToDb([insight]);
deleteSessionInsights(session.id, {
includeOnlyTypes: ['prompt_quality'],
excludeIds: [insight.id],
});
return {
success: true,
insights: [insight],
usage: response.usage ? {
inputTokens: response.usage.inputTokens,
outputTokens: response.usage.outputTokens,
} : undefined,
};
}
catch (error) {
if (error instanceof Error && error.name === 'AbortError') {
return { success: false, insights: [], error: 'Analysis cancelled' };
}
return {
success: false,
insights: [],
error: error instanceof Error ? error.message : 'Prompt quality analysis failed',
};
}
}
/**
* Find recurring patterns across multiple insights and write bidirectional links to SQLite.
*/
export async function findRecurringInsights(insights) {
if (!isLLMConfigured()) {
return { success: false, groups: [], updatedCount: 0, error: 'LLM not configured.' };
}
const candidates = insights
.filter(i => i.type !== 'summary' && i.type !== 'prompt_quality')
.slice(0, 200);
if (candidates.length < 2) {
return {
success: false,
groups: [],
updatedCount: 0,
error: 'Need at least 2 non-summary insights to find patterns.',
};
}
try {
const client = createLLMClient();
const insightData = candidates.map(i => ({
id: i.id,
type: i.type === 'technique' ? 'learning' : i.type,
title: i.title,
summary: i.summary.slice(0, 150),
projectName: i.project_name,
sessionId: i.session_id,
}));
const prompt = `Analyze these insights from coding sessions and find groups of semantically similar or duplicate insights — ones that express the same learning or decision even if worded differently.
RULES:
- Only group insights that are genuinely about the same concept/topic
- Insights in a group should be from DIFFERENT sessions (same sessionId = not recurring)
- A group must have at least 2 insights
- An insight can only belong to one group
- Provide a brief "theme" describing what the group shares
- If no recurring patterns exist, return an empty groups array
INSIGHTS:
${JSON.stringify(insightData, null, 2)}
Respond with valid JSON only:
{
"groups": [
{
"insightIds": ["insight_abc", "insight_def"],
"theme": "Brief description of the shared concept"
}
]
}`;
const response = await client.chat([
{
role: 'system',
content: 'You are an expert at identifying recurring patterns and themes across software development insights. You find semantically similar insights even when they are worded differently. Respond with valid JSON only.',
},
{ role: 'user', content: prompt },
]);
const jsonMatch = response.content.match(/\{[\s\S]*\}/);
if (!jsonMatch) {
return { success: false, groups: [], updatedCount: 0, error: 'Failed to parse recurring insights response.' };
}
const parsed = JSON.parse(jsonMatch[0]);
const groups = parsed.groups || [];
const validIds = new Set(candidates.map(i => i.id));
const validGroups = groups
.map(g => ({
...g,
insightIds: g.insightIds.filter(id => validIds.has(id)),
}))
.filter(g => g.insightIds.length >= 2);
if (validGroups.length === 0) {
return {
success: true,
groups: [],
updatedCount: 0,
usage: response.usage
? { inputTokens: response.usage.inputTokens, outputTokens: response.usage.outputTokens }
: undefined,
};
}
// Build bidirectional links
const linkMap = new Map();
for (const group of validGroups) {
for (const id of group.insightIds) {
const others = group.insightIds.filter(otherId => otherId !== id);
const existing = linkMap.get(id) || [];
linkMap.set(id, [...new Set([...existing, ...others])]);
}
}
// Write links to SQLite
const db = getDb();
const updateLinks = db.prepare(`UPDATE insights SET linked_insight_ids = ? WHERE id = ?`);
for (const [insightId, linkedIds] of linkMap.entries()) {
updateLinks.run(JSON.stringify(linkedIds), insightId);
}
return {
success: true,
groups: validGroups,
updatedCount: linkMap.size,
usage: response.usage
? { inputTokens: response.usage.inputTokens, outputTokens: response.usage.outputTokens }
: undefined,
};
}
catch (error) {
return {
success: false,
groups: [],
updatedCount: 0,
error: error instanceof Error ? error.message : 'Failed to find recurring insights',
};
}
}
// --- Internal helpers ---
function chunkMessages(messages, estimateTokens) {
const chunks = [];
let currentChunk = [];
let currentTokens = 0;
const chunkLimit = MAX_INPUT_TOKENS * 0.8;
for (const message of messages) {
let toolResults = [];
try {
toolResults = message.tool_results ? JSON.parse(message.tool_results) : [];
}
catch {
toolResults = [];
}
const messageText = [
message.content,
message.thinking?.slice(0, 1000) ?? '',
...toolResults.map(r => (r.output || '').slice(0, 200)),
].join(' ');
const messageTokens = estimateTokens(messageText);
if (currentTokens + messageTokens > chunkLimit && currentChunk.length > 0) {
chunks.push(currentChunk);
currentChunk = [];
currentTokens = 0;
}
currentChunk.push(message);
currentTokens += messageTokens;
}
if (currentChunk.length > 0) {
chunks.push(currentChunk);
}
return chunks;
}
function mergeAnalysisResponses(responses) {
if (responses.length === 0) {
return {
summary: { title: 'Analysis failed', content: '', bullets: [] },
decisions: [],
learnings: [],
};
}
if (responses.length === 1)
return responses[0];
const merged = {
summary: responses[0].summary,
decisions: [],
learnings: [],
};
for (const response of responses) {
merged.decisions.push(...response.decisions);
merged.learnings.push(...response.learnings);
}
merged.decisions = deduplicateByTitle(merged.decisions).slice(0, 3);
merged.learnings = deduplicateByTitle(merged.learnings).slice(0, 5);
return merged;
}
function deduplicateByTitle(items) {
const seen = new Set();
return items.filter((item) => {
const normalized = item.title.toLowerCase().trim();
if (seen.has(normalized))
return false;
seen.add(normalized);
return true;
});
}
function convertToInsightRows(response, session) {
const insights = [];
const now = new Date().toISOString();
insights.push({
id: randomUUID(),
session_id: session.id,
project_id: session.project_id,
project_name: session.project_name,
type: 'summary',
title: response.summary.title,
content: response.summary.content,
summary: response.summary.content,
bullets: JSON.stringify(response.summary.bullets),
confidence: 0.9,
source: 'llm',
metadata: null,
timestamp: session.ended_at,
created_at: now,
scope: 'session',
analysis_version: ANALYSIS_VERSION,
});
for (const decision of response.decisions) {
const confidence = decision.confidence ?? 85;
if (confidence < 70)
continue;
insights.push({
id: randomUUID(),
session_id: session.id,
project_id: session.project_id,
project_name: session.project_name,
type: 'decision',
title: decision.title,
content: decision.content,
summary: decision.content.slice(0, 200),
bullets: JSON.stringify(decision.alternatives || []),
confidence: confidence / 100,
source: 'llm',
metadata: JSON.stringify({
reasoning: decision.reasoning,
alternatives: decision.alternatives,
evidence: decision.evidence,
}),
timestamp: session.ended_at,
created_at: now,
scope: 'session',
analysis_version: ANALYSIS_VERSION,
});
}
for (const learning of response.learnings) {
const confidence = learning.confidence ?? 80;
if (confidence < 70)
continue;
insights.push({
id: randomUUID(),
session_id: session.id,
project_id: session.project_id,
project_name: session.project_name,
type: 'learning',
title: learning.title,
content: learning.content,
summary: learning.content.slice(0, 200),
bullets: JSON.stringify([]),
confidence: confidence / 100,
source: 'llm',
metadata: JSON.stringify({
context: learning.context,
evidence: learning.evidence,
}),
timestamp: session.ended_at,
created_at: now,
scope: 'session',
analysis_version: ANALYSIS_VERSION,
});
}
return insights;
}
function convertPromptQualityToInsightRow(response, session) {
const now = new Date().toISOString();
return {
id: randomUUID(),
session_id: session.id,
project_id: session.project_id,
project_name: session.project_name,
type: 'prompt_quality',
title: `Prompt Efficiency: ${response.efficiencyScore}/100`,
content: response.overallAssessment,
summary: response.overallAssessment,
bullets: JSON.stringify(response.tips),
confidence: 0.85,
source: 'llm',
metadata: JSON.stringify({
efficiencyScore: response.efficiencyScore,
wastedTurns: response.wastedTurns,
antiPatterns: response.antiPatterns,
potentialMessageReduction: response.potentialMessageReduction,
}),
timestamp: session.ended_at,
created_at: now,
scope: 'session',
analysis_version: ANALYSIS_VERSION,
};
}
/**
* Write insight rows to SQLite using prepared statements.
*/
function saveInsightsToDb(insights) {
const db = getDb();
const insert = db.prepare(`
INSERT OR REPLACE INTO insights (
id, session_id, project_id, project_name, type, title, content,
summary, bullets, confidence, source, metadata, timestamp,
created_at, scope, analysis_version
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
`);
const insertMany = db.transaction((rows) => {
for (const row of rows) {
insert.run(row.id, row.session_id, row.project_id, row.project_name, row.type, row.title, row.content, row.summary, row.bullets, row.confidence, row.source, row.metadata, row.timestamp, row.created_at, row.scope, row.analysis_version);
}
});
insertMany(insights);
}
/**
* Delete insights for a session, with optional type and ID exclusions.
*/
function deleteSessionInsights(sessionId, opts) {
const db = getDb();
const conditions = ['session_id = ?'];
const params = [sessionId];
if (opts.excludeTypes && opts.excludeTypes.length > 0) {
conditions.push(`type NOT IN (${opts.excludeTypes.map(() => '?').join(', ')})`);
params.push(...opts.excludeTypes);
}
if (opts.includeOnlyTypes && opts.includeOnlyTypes.length > 0) {
conditions.push(`type IN (${opts.includeOnlyTypes.map(() => '?').join(', ')})`);
params.push(...opts.includeOnlyTypes);
}
if (opts.excludeIds && opts.excludeIds.length > 0) {
conditions.push(`id NOT IN (${opts.excludeIds.map(() => '?').join(', ')})`);
params.push(...opts.excludeIds);
}
db.prepare(`DELETE FROM insights WHERE ${conditions.join(' AND ')}`).run(...params);
}
//# sourceMappingURL=analysis.js.map
{"version":3,"file":"analysis.js","sourceRoot":"","sources":["../../src/llm/analysis.ts"],"names":[],"mappings":"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"}
import type { LLMClient } from './types.js';
import type { LLMProviderConfig } from './types.js';
/**
* Load LLM config from the CLI config file.
*/
export declare function loadLLMConfig(): LLMProviderConfig | null;
/**
* Check if LLM is configured.
*/
export declare function isLLMConfigured(): boolean;
/**
* Create an LLM client from the current config.
* Throws if LLM is not configured.
*/
export declare function createLLMClient(): LLMClient;
/**
* Create an LLM client from a specific config object (used for testing).
*/
export declare function createClientFromConfig(config: LLMProviderConfig): LLMClient;
/**
* Test LLM connectivity with the given config.
*/
export declare function testLLMConfig(config: LLMProviderConfig): Promise<{
success: boolean;
error?: string;
}>;
//# sourceMappingURL=client.d.ts.map
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/llm/client.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAC5C,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAMpD;;GAEG;AACH,wBAAgB,aAAa,IAAI,iBAAiB,GAAG,IAAI,CAGxD;AAED;;GAEG;AACH,wBAAgB,eAAe,IAAI,OAAO,CAKzC;AAED;;;GAGG;AACH,wBAAgB,eAAe,IAAI,SAAS,CAM3C;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,iBAAiB,GAAG,SAAS,CAa3E;AAED;;GAEG;AACH,wBAAsB,aAAa,CAAC,MAAM,EAAE,iBAAiB,GAAG,OAAO,CAAC;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAW5G"}
// LLM client factory — server-side.
// Config is loaded from ~/.code-insights/config.json via the CLI config system.
// No localStorage or browser APIs used here.
import { loadConfig } from '@code-insights/cli/utils/config';
import { createOpenAIClient } from './providers/openai.js';
import { createAnthropicClient } from './providers/anthropic.js';
import { createGeminiClient } from './providers/gemini.js';
import { createOllamaClient } from './providers/ollama.js';
/**
* Load LLM config from the CLI config file.
*/
export function loadLLMConfig() {
const config = loadConfig();
return config?.dashboard?.llm ?? null;
}
/**
* Check if LLM is configured.
*/
export function isLLMConfigured() {
const llm = loadLLMConfig();
if (!llm)
return false;
if (llm.provider === 'ollama')
return !!llm.model;
return !!llm.apiKey && !!llm.model;
}
/**
* Create an LLM client from the current config.
* Throws if LLM is not configured.
*/
export function createLLMClient() {
const config = loadLLMConfig();
if (!config) {
throw new Error('LLM not configured. Run `code-insights config llm` to configure a provider.');
}
return createClientFromConfig(config);
}
/**
* Create an LLM client from a specific config object (used for testing).
*/
export function createClientFromConfig(config) {
switch (config.provider) {
case 'openai':
return createOpenAIClient(config.apiKey ?? '', config.model);
case 'anthropic':
return createAnthropicClient(config.apiKey ?? '', config.model);
case 'gemini':
return createGeminiClient(config.apiKey ?? '', config.model);
case 'ollama':
return createOllamaClient(config.model, config.baseUrl);
default:
throw new Error(`Unknown LLM provider: ${config.provider}`);
}
}
/**
* Test LLM connectivity with the given config.
*/
export async function testLLMConfig(config) {
try {
const client = createClientFromConfig(config);
await client.chat([{ role: 'user', content: 'Say "ok" and nothing else.' }]);
return { success: true };
}
catch (error) {
return {
success: false,
error: error instanceof Error ? error.message : 'Unknown error',
};
}
}
//# sourceMappingURL=client.js.map
{"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/llm/client.ts"],"names":[],"mappings":"AAAA,oCAAoC;AACpC,gFAAgF;AAChF,6CAA6C;AAE7C,OAAO,EAAE,UAAU,EAAE,MAAM,iCAAiC,CAAC;AAG7D,OAAO,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,EAAE,qBAAqB,EAAE,MAAM,0BAA0B,CAAC;AACjE,OAAO,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAE3D;;GAEG;AACH,MAAM,UAAU,aAAa;IAC3B,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC;IAC5B,OAAO,MAAM,EAAE,SAAS,EAAE,GAAG,IAAI,IAAI,CAAC;AACxC,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,eAAe;IAC7B,MAAM,GAAG,GAAG,aAAa,EAAE,CAAC;IAC5B,IAAI,CAAC,GAAG;QAAE,OAAO,KAAK,CAAC;IACvB,IAAI,GAAG,CAAC,QAAQ,KAAK,QAAQ;QAAE,OAAO,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC;IAClD,OAAO,CAAC,CAAC,GAAG,CAAC,MAAM,IAAI,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC;AACrC,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,eAAe;IAC7B,MAAM,MAAM,GAAG,aAAa,EAAE,CAAC;IAC/B,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CAAC,6EAA6E,CAAC,CAAC;IACjG,CAAC;IACD,OAAO,sBAAsB,CAAC,MAAM,CAAC,CAAC;AACxC,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,sBAAsB,CAAC,MAAyB;IAC9D,QAAQ,MAAM,CAAC,QAAQ,EAAE,CAAC;QACxB,KAAK,QAAQ;YACX,OAAO,kBAAkB,CAAC,MAAM,CAAC,MAAM,IAAI,EAAE,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC;QAC/D,KAAK,WAAW;YACd,OAAO,qBAAqB,CAAC,MAAM,CAAC,MAAM,IAAI,EAAE,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC;QAClE,KAAK,QAAQ;YACX,OAAO,kBAAkB,CAAC,MAAM,CAAC,MAAM,IAAI,EAAE,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC;QAC/D,KAAK,QAAQ;YACX,OAAO,kBAAkB,CAAC,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC;QAC1D;YACE,MAAM,IAAI,KAAK,CAAC,yBAAyB,MAAM,CAAC,QAAQ,EAAE,CAAC,CAAC;IAChE,CAAC;AACH,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,MAAyB;IAC3D,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,sBAAsB,CAAC,MAAM,CAAC,CAAC;QAC9C,MAAM,MAAM,CAAC,IAAI,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,4BAA4B,EAAE,CAAC,CAAC,CAAC;QAC7E,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;IAC3B,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO;YACL,OAAO,EAAE,KAAK;YACd,KAAK,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe;SAChE,CAAC;IACJ,CAAC;AACH,CAAC"}
export type { LLMClient, LLMMessage, LLMResponse, ChatOptions } from './types.js';
export type { LLMProvider, LLMProviderConfig } from './types.js';
export { createLLMClient, createClientFromConfig, loadLLMConfig, isLLMConfigured, testLLMConfig } from './client.js';
export { analyzeSession, analyzePromptQuality, findRecurringInsights } from './analysis.js';
export type { AnalysisResult, InsightRow, SessionData, RecurringInsightResult } from './analysis.js';
export { discoverOllamaModels } from './providers/ollama.js';
//# sourceMappingURL=index.d.ts.map
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/llm/index.ts"],"names":[],"mappings":"AAEA,YAAY,EAAE,SAAS,EAAE,UAAU,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAClF,YAAY,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AACjE,OAAO,EAAE,eAAe,EAAE,sBAAsB,EAAE,aAAa,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AACrH,OAAO,EAAE,cAAc,EAAE,oBAAoB,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AAC5F,YAAY,EAAE,cAAc,EAAE,UAAU,EAAE,WAAW,EAAE,sBAAsB,EAAE,MAAM,eAAe,CAAC;AACrG,OAAO,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAC"}
// Public API for the server-side LLM engine.
export { createLLMClient, createClientFromConfig, loadLLMConfig, isLLMConfigured, testLLMConfig } from './client.js';
export { analyzeSession, analyzePromptQuality, findRecurringInsights } from './analysis.js';
export { discoverOllamaModels } from './providers/ollama.js';
//# sourceMappingURL=index.js.map
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/llm/index.ts"],"names":[],"mappings":"AAAA,6CAA6C;AAI7C,OAAO,EAAE,eAAe,EAAE,sBAAsB,EAAE,aAAa,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AACrH,OAAO,EAAE,cAAc,EAAE,oBAAoB,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AAE5F,OAAO,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAC"}
export interface SQLiteMessageRow {
id: string;
session_id: string;
type: 'user' | 'assistant' | 'system';
content: string;
thinking: string | null;
tool_calls: string;
tool_results: string;
usage: string | null;
timestamp: string;
parent_id: string | null;
}
/**
* Format SQLite message rows for LLM consumption.
* Handles snake_case fields and JSON-encoded tool_calls/tool_results.
*/
export declare function formatMessagesForAnalysis(messages: SQLiteMessageRow[]): string;
/**
* System prompt for session analysis.
*/
export declare const SESSION_ANALYSIS_SYSTEM_PROMPT = "You are an expert at analyzing software development conversations and extracting valuable insights. Your task is to analyze an AI coding session (a conversation between a user and an AI coding assistant) and extract structured insights.\n\nYou will identify:\n1. **Summary**: A high-level narrative of what was accomplished\n2. **Decisions**: Technical choices made, including reasoning and alternatives considered (max 3)\n3. **Learnings**: Technical discoveries, gotchas, problem-solving approaches, debugging strategies, or transferable knowledge (max 5)\n\nQuality Standards:\n- Only include insights you would write in a team knowledge base for future reference\n- Each insight MUST reference concrete details: specific file names, library names, error messages, API endpoints, or code patterns\n- Do not invent file names, APIs, errors, or details not present in the conversation\n- Rate your confidence in each insight's value (0-100). Only include insights you rate 70+.\n- It is better to return 0 insights in a category than to include generic or trivial ones\n- If a session is straightforward with no notable decisions or learnings, say so in the summary and leave other categories empty\n- The summary must mention the most important concrete artifact changed (file, endpoint, or test) if any\n\nDO NOT include insights like these (too generic/trivial):\n- \"Used debugging techniques to fix an issue\"\n- \"Made architectural decisions about the codebase\"\n- \"Implemented a new feature\" (the summary already covers this)\n- \"Used React hooks for state management\" (too generic without specifics)\n- \"Fixed a bug in the code\" (what bug? what was the root cause?)\n- Anything that restates the task without adding transferable knowledge\n\nRespond with valid JSON only, wrapped in <json>...</json> tags. Do not include any other text.";
/**
* Generate the user prompt for session analysis.
*/
export declare function generateSessionAnalysisPrompt(projectName: string, sessionSummary: string | null, formattedMessages: string): string;
export interface AnalysisResponse {
summary: {
title: string;
content: string;
bullets: string[];
};
decisions: Array<{
title: string;
content: string;
reasoning: string;
alternatives?: string[];
confidence?: number;
evidence?: string[];
}>;
learnings: Array<{
title: string;
content: string;
context: string;
confidence?: number;
evidence?: string[];
}>;
}
/**
* Parse the LLM response into structured insights.
*/
export declare function parseAnalysisResponse(response: string): AnalysisResponse | null;
export declare const PROMPT_QUALITY_SYSTEM_PROMPT = "You are an expert at analyzing how effectively humans communicate with AI coding assistants. Your task is to review a conversation between a user and Claude Code, and evaluate the user's prompting efficiency.\n\nYou will identify:\n1. **Wasted turns**: User messages that led to clarifications, corrections, or repeated instructions because the original prompt was unclear, missing context, or too vague.\n2. **Anti-patterns**: Recurring bad habits in the user's prompting style.\n3. **Efficiency score**: A 0-100 rating of how optimally the user communicated.\n4. **Actionable tips**: Specific improvements the user can make.\n\nGuidelines:\n- Focus on USER messages only \u2014 don't critique the assistant's responses\n- A \"wasted turn\" is when the user had to send a follow-up message to clarify, correct, or repeat something that could have been included in the original prompt\n- Only mark a wasted turn if the assistant explicitly asked for clarification or corrected a misunderstanding\n- Common anti-patterns: vague instructions, missing file paths, not providing error messages, incomplete requirements, repeated instructions, not specifying what \"it\" refers to\n- Be constructive, not judgmental \u2014 the goal is to help users improve\n- Consider the context: some clarification exchanges are normal and expected\n- A score of 100 means every user message was perfectly clear and complete\n- A score of 50 means about half the messages could have been more efficient\n\nRespond with valid JSON only, wrapped in <json>...</json> tags. Do not include any other text.";
export declare function generatePromptQualityPrompt(projectName: string, formattedMessages: string, messageCount: number): string;
export interface WastedTurn {
messageIndex: number;
reason: string;
suggestedRewrite: string;
}
export interface AntiPattern {
name: string;
count: number;
examples: string[];
}
export interface PromptQualityResponse {
efficiencyScore: number;
potentialMessageReduction: number;
overallAssessment: string;
wastedTurns: WastedTurn[];
antiPatterns: AntiPattern[];
tips: string[];
}
export declare function parsePromptQualityResponse(response: string): PromptQualityResponse | null;
//# sourceMappingURL=prompts.d.ts.map
{"version":3,"file":"prompts.d.ts","sourceRoot":"","sources":["../../src/llm/prompts.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,GAAG,WAAW,GAAG,QAAQ,CAAC;IACtC,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAUD;;;GAGG;AACH,wBAAgB,yBAAyB,CAAC,QAAQ,EAAE,gBAAgB,EAAE,GAAG,MAAM,CA8C9E;AAED;;GAEG;AACH,eAAO,MAAM,8BAA8B,k0DAwBoD,CAAC;AAEhG;;GAEG;AACH,wBAAgB,6BAA6B,CAC3C,WAAW,EAAE,MAAM,EACnB,cAAc,EAAE,MAAM,GAAG,IAAI,EAC7B,iBAAiB,EAAE,MAAM,GACxB,MAAM,CAyCR;AAED,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE;QACP,KAAK,EAAE,MAAM,CAAC;QACd,OAAO,EAAE,MAAM,CAAC;QAChB,OAAO,EAAE,MAAM,EAAE,CAAC;KACnB,CAAC;IACF,SAAS,EAAE,KAAK,CAAC;QACf,KAAK,EAAE,MAAM,CAAC;QACd,OAAO,EAAE,MAAM,CAAC;QAChB,SAAS,EAAE,MAAM,CAAC;QAClB,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;QACxB,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;KACrB,CAAC,CAAC;IACH,SAAS,EAAE,KAAK,CAAC;QACf,KAAK,EAAE,MAAM,CAAC;QACd,OAAO,EAAE,MAAM,CAAC;QAChB,OAAO,EAAE,MAAM,CAAC;QAChB,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;KACrB,CAAC,CAAC;CACJ;AASD;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,GAAG,gBAAgB,GAAG,IAAI,CAuB/E;AAID,eAAO,MAAM,4BAA4B,kjDAkBsD,CAAC;AAEhG,wBAAgB,2BAA2B,CACzC,WAAW,EAAE,MAAM,EACnB,iBAAiB,EAAE,MAAM,EACzB,YAAY,EAAE,MAAM,GACnB,MAAM,CA2CR;AAED,MAAM,WAAW,UAAU;IACzB,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,gBAAgB,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,MAAM,WAAW,qBAAqB;IACpC,eAAe,EAAE,MAAM,CAAC;IACxB,yBAAyB,EAAE,MAAM,CAAC;IAClC,iBAAiB,EAAE,MAAM,CAAC;IAC1B,WAAW,EAAE,UAAU,EAAE,CAAC;IAC1B,YAAY,EAAE,WAAW,EAAE,CAAC;IAC5B,IAAI,EAAE,MAAM,EAAE,CAAC;CAChB;AAED,wBAAgB,0BAA0B,CAAC,QAAQ,EAAE,MAAM,GAAG,qBAAqB,GAAG,IAAI,CA2BzF"}
// Analysis prompts and response parsers for LLM session analysis.
// Ported from web repo (src/lib/llm/prompts.ts) with SQLite-aware message formatting.
/**
* Format SQLite message rows for LLM consumption.
* Handles snake_case fields and JSON-encoded tool_calls/tool_results.
*/
export function formatMessagesForAnalysis(messages) {
let userIndex = 0;
let assistantIndex = 0;
return messages
.map((m) => {
const role = m.type === 'user' ? 'User' : m.type === 'assistant' ? 'Assistant' : 'System';
const roleLabel = role === 'User'
? `User#${userIndex++}`
: role === 'Assistant'
? `Assistant#${assistantIndex++}`
: 'System';
// Parse JSON-encoded tool_calls
let toolCalls = [];
try {
toolCalls = m.tool_calls ? JSON.parse(m.tool_calls) : [];
}
catch {
toolCalls = [];
}
// Parse JSON-encoded tool_results
let toolResults = [];
try {
toolResults = m.tool_results ? JSON.parse(m.tool_results) : [];
}
catch {
toolResults = [];
}
const toolInfo = toolCalls.length > 0
? `\n[Tools used: ${toolCalls.map(t => t.name || 'unknown').join(', ')}]`
: '';
// Include thinking content — capped at 1000 chars to stay within token budget
const thinkingInfo = m.thinking
? `\n[Thinking: ${m.thinking.slice(0, 1000)}]`
: '';
// Include tool results for context — 200 chars per result
const resultInfo = toolResults.length > 0
? `\n[Tool results: ${toolResults.map(r => (r.output || '').slice(0, 200)).join(' | ')}]`
: '';
return `### ${roleLabel}:\n${m.content}${thinkingInfo}${toolInfo}${resultInfo}`;
})
.join('\n\n');
}
/**
* System prompt for session analysis.
*/
export const SESSION_ANALYSIS_SYSTEM_PROMPT = `You are an expert at analyzing software development conversations and extracting valuable insights. Your task is to analyze an AI coding session (a conversation between a user and an AI coding assistant) and extract structured insights.
You will identify:
1. **Summary**: A high-level narrative of what was accomplished
2. **Decisions**: Technical choices made, including reasoning and alternatives considered (max 3)
3. **Learnings**: Technical discoveries, gotchas, problem-solving approaches, debugging strategies, or transferable knowledge (max 5)
Quality Standards:
- Only include insights you would write in a team knowledge base for future reference
- Each insight MUST reference concrete details: specific file names, library names, error messages, API endpoints, or code patterns
- Do not invent file names, APIs, errors, or details not present in the conversation
- Rate your confidence in each insight's value (0-100). Only include insights you rate 70+.
- It is better to return 0 insights in a category than to include generic or trivial ones
- If a session is straightforward with no notable decisions or learnings, say so in the summary and leave other categories empty
- The summary must mention the most important concrete artifact changed (file, endpoint, or test) if any
DO NOT include insights like these (too generic/trivial):
- "Used debugging techniques to fix an issue"
- "Made architectural decisions about the codebase"
- "Implemented a new feature" (the summary already covers this)
- "Used React hooks for state management" (too generic without specifics)
- "Fixed a bug in the code" (what bug? what was the root cause?)
- Anything that restates the task without adding transferable knowledge
Respond with valid JSON only, wrapped in <json>...</json> tags. Do not include any other text.`;
/**
* Generate the user prompt for session analysis.
*/
export function generateSessionAnalysisPrompt(projectName, sessionSummary, formattedMessages) {
return `Analyze this AI coding session and extract insights.
Project: ${projectName}
${sessionSummary ? `Session Summary: ${sessionSummary}\n` : ''}
--- CONVERSATION ---
${formattedMessages}
--- END CONVERSATION ---
Extract insights in this JSON format:
{
"summary": {
"title": "Brief title describing main accomplishment (max 60 chars)",
"content": "2-3 sentence narrative of what was accomplished",
"bullets": ["Key point 1", "Key point 2", "Key point 3"]
},
"decisions": [
{
"title": "The specific decision made (max 60 chars)",
"content": "Detailed explanation with concrete details",
"reasoning": "Why this choice was made over alternatives",
"alternatives": ["Alternative 1 considered", "Alternative 2 considered"],
"confidence": 85,
"evidence": ["User#4: ...", "Assistant#5: ..."]
}
],
"learnings": [
{
"title": "Specific thing learned (max 60 chars)",
"content": "Detailed explanation with concrete details",
"context": "When/where this applies and why it matters",
"confidence": 80,
"evidence": ["User#7: ...", "Assistant#8: ..."]
}
]
}
Only include insights rated 70+ confidence. If you cannot cite evidence, drop the insight. Return empty arrays for categories with no strong insights. Max 3 decisions, 5 learnings.
Evidence should reference the labeled turns in the conversation (e.g., "User#2", "Assistant#5").
Respond with valid JSON only, wrapped in <json>...</json> tags. Do not include any other text.`;
}
function extractJsonPayload(response) {
const tagged = response.match(/<json>\s*([\s\S]*?)\s*<\/json>/i);
if (tagged?.[1])
return tagged[1].trim();
const jsonMatch = response.match(/\{[\s\S]*\}/);
return jsonMatch ? jsonMatch[0] : null;
}
/**
* Parse the LLM response into structured insights.
*/
export function parseAnalysisResponse(response) {
try {
const jsonPayload = extractJsonPayload(response);
if (!jsonPayload) {
console.error('No JSON found in analysis response');
return null;
}
const parsed = JSON.parse(jsonPayload);
if (!parsed.summary || typeof parsed.summary.title !== 'string') {
console.error('Invalid analysis response structure');
return null;
}
parsed.decisions = parsed.decisions || [];
parsed.learnings = parsed.learnings || [];
return parsed;
}
catch (error) {
console.error('Failed to parse analysis response:', error);
return null;
}
}
// --- Prompt Quality Analysis ---
export const PROMPT_QUALITY_SYSTEM_PROMPT = `You are an expert at analyzing how effectively humans communicate with AI coding assistants. Your task is to review a conversation between a user and Claude Code, and evaluate the user's prompting efficiency.
You will identify:
1. **Wasted turns**: User messages that led to clarifications, corrections, or repeated instructions because the original prompt was unclear, missing context, or too vague.
2. **Anti-patterns**: Recurring bad habits in the user's prompting style.
3. **Efficiency score**: A 0-100 rating of how optimally the user communicated.
4. **Actionable tips**: Specific improvements the user can make.
Guidelines:
- Focus on USER messages only — don't critique the assistant's responses
- A "wasted turn" is when the user had to send a follow-up message to clarify, correct, or repeat something that could have been included in the original prompt
- Only mark a wasted turn if the assistant explicitly asked for clarification or corrected a misunderstanding
- Common anti-patterns: vague instructions, missing file paths, not providing error messages, incomplete requirements, repeated instructions, not specifying what "it" refers to
- Be constructive, not judgmental — the goal is to help users improve
- Consider the context: some clarification exchanges are normal and expected
- A score of 100 means every user message was perfectly clear and complete
- A score of 50 means about half the messages could have been more efficient
Respond with valid JSON only, wrapped in <json>...</json> tags. Do not include any other text.`;
export function generatePromptQualityPrompt(projectName, formattedMessages, messageCount) {
return `Analyze the user's prompting efficiency in this AI coding session.
Project: ${projectName}
Total messages: ${messageCount}
--- CONVERSATION ---
${formattedMessages}
--- END CONVERSATION ---
Evaluate the user's prompting quality and respond with this JSON format:
{
"efficiencyScore": 75,
"potentialMessageReduction": 3,
"overallAssessment": "2-3 sentence summary of the user's prompting style and efficiency",
"wastedTurns": [
{
"messageIndex": 5,
"reason": "Missing context — didn't specify which file to modify",
"suggestedRewrite": "A better version of the user's original message that would have avoided the follow-up"
}
],
"antiPatterns": [
{
"name": "Vague Instructions",
"count": 3,
"examples": ["fix it", "make it work", "do the thing"]
}
],
"tips": [
"Always include file paths when asking to modify code",
"Provide error messages verbatim when reporting bugs"
]
}
Rules:
- messageIndex refers to the 0-based index of the USER message, as labeled in the conversation (e.g., User#0)
- Only include genuinely wasted turns, not normal back-and-forth
- Tips should be specific and actionable, not generic; include the relevant user message index in parentheses
- If the user prompted well, say so — don't manufacture issues
- potentialMessageReduction is how many fewer messages the session could have taken with better prompts
Respond with valid JSON only, wrapped in <json>...</json> tags. Do not include any other text.`;
}
export function parsePromptQualityResponse(response) {
try {
const jsonPayload = extractJsonPayload(response);
if (!jsonPayload) {
console.error('No JSON found in prompt quality response');
return null;
}
const parsed = JSON.parse(jsonPayload);
if (typeof parsed.efficiencyScore !== 'number') {
console.error('Invalid prompt quality response: missing efficiencyScore');
return null;
}
parsed.efficiencyScore = Math.max(0, Math.min(100, Math.round(parsed.efficiencyScore)));
parsed.potentialMessageReduction = parsed.potentialMessageReduction || 0;
parsed.overallAssessment = parsed.overallAssessment || '';
parsed.wastedTurns = parsed.wastedTurns || [];
parsed.antiPatterns = parsed.antiPatterns || [];
parsed.tips = parsed.tips || [];
return parsed;
}
catch (error) {
console.error('Failed to parse prompt quality response:', error);
return null;
}
}
//# sourceMappingURL=prompts.js.map
{"version":3,"file":"prompts.js","sourceRoot":"","sources":["../../src/llm/prompts.ts"],"names":[],"mappings":"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"}
import type { LLMClient } from '../types.js';
export declare function createAnthropicClient(apiKey: string, model: string): LLMClient;
//# sourceMappingURL=anthropic.d.ts.map
{"version":3,"file":"anthropic.d.ts","sourceRoot":"","sources":["../../../src/llm/providers/anthropic.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,SAAS,EAAwC,MAAM,aAAa,CAAC;AAEnF,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,SAAS,CAiD9E"}
// Anthropic provider implementation (server-side, no browser dependencies)
// Note: 'anthropic-dangerous-direct-browser-access' header is intentionally omitted here —
// this runs server-side where direct API access is safe and expected.
export function createAnthropicClient(apiKey, model) {
return {
provider: 'anthropic',
model,
async chat(messages, options) {
// Extract system message if present
const systemMessage = messages.find(m => m.role === 'system');
const chatMessages = messages.filter(m => m.role !== 'system');
const response = await fetch('https://api.anthropic.com/v1/messages', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'x-api-key': apiKey,
'anthropic-version': '2023-06-01',
},
signal: options?.signal,
body: JSON.stringify({
model,
max_tokens: 4096,
system: systemMessage?.content,
messages: chatMessages.map(m => ({ role: m.role, content: m.content })),
}),
});
if (!response.ok) {
const error = await response.json().catch(() => ({}));
throw new Error(error.error?.message || `Anthropic API error: ${response.status}`);
}
const data = await response.json();
return {
content: data.content[0]?.text || '',
usage: data.usage ? {
inputTokens: data.usage.input_tokens,
outputTokens: data.usage.output_tokens,
} : undefined,
};
},
estimateTokens(text) {
return Math.ceil(text.length / 4);
},
};
}
//# sourceMappingURL=anthropic.js.map
{"version":3,"file":"anthropic.js","sourceRoot":"","sources":["../../../src/llm/providers/anthropic.ts"],"names":[],"mappings":"AAAA,2EAA2E;AAC3E,2FAA2F;AAC3F,sEAAsE;AAItE,MAAM,UAAU,qBAAqB,CAAC,MAAc,EAAE,KAAa;IACjE,OAAO;QACL,QAAQ,EAAE,WAAW;QACrB,KAAK;QAEL,KAAK,CAAC,IAAI,CAAC,QAAsB,EAAE,OAAqB;YACtD,oCAAoC;YACpC,MAAM,aAAa,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC;YAC9D,MAAM,YAAY,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC;YAE/D,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,uCAAuC,EAAE;gBACpE,MAAM,EAAE,MAAM;gBACd,OAAO,EAAE;oBACP,cAAc,EAAE,kBAAkB;oBAClC,WAAW,EAAE,MAAM;oBACnB,mBAAmB,EAAE,YAAY;iBAClC;gBACD,MAAM,EAAE,OAAO,EAAE,MAAM;gBACvB,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;oBACnB,KAAK;oBACL,UAAU,EAAE,IAAI;oBAChB,MAAM,EAAE,aAAa,EAAE,OAAO;oBAC9B,QAAQ,EAAE,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC;iBACxE,CAAC;aACH,CAAC,CAAC;YAEH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;gBACjB,MAAM,KAAK,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,CAAC,CAAqC,CAAC;gBAC1F,MAAM,IAAI,KAAK,CAAC,KAAK,CAAC,KAAK,EAAE,OAAO,IAAI,wBAAwB,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;YACrF,CAAC;YAED,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAG/B,CAAC;YAEF,OAAO;gBACL,OAAO,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,IAAI,IAAI,EAAE;gBACpC,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;oBAClB,WAAW,EAAE,IAAI,CAAC,KAAK,CAAC,YAAY;oBACpC,YAAY,EAAE,IAAI,CAAC,KAAK,CAAC,aAAa;iBACvC,CAAC,CAAC,CAAC,SAAS;aACd,CAAC;QACJ,CAAC;QAED,cAAc,CAAC,IAAY;YACzB,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;QACpC,CAAC;KACF,CAAC;AACJ,CAAC"}
import type { LLMClient } from '../types.js';
export declare function createGeminiClient(apiKey: string, model: string): LLMClient;
//# sourceMappingURL=gemini.d.ts.map
{"version":3,"file":"gemini.d.ts","sourceRoot":"","sources":["../../../src/llm/providers/gemini.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAwC,MAAM,aAAa,CAAC;AAEnF,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,SAAS,CA+D3E"}
// Gemini provider implementation (server-side, no browser dependencies)
export function createGeminiClient(apiKey, model) {
return {
provider: 'gemini',
model,
async chat(messages, options) {
const systemMessage = messages.find(m => m.role === 'system');
const chatMessages = messages.filter(m => m.role !== 'system');
const contents = chatMessages.map(m => ({
role: m.role === 'assistant' ? 'model' : 'user',
parts: [{ text: m.content }],
}));
const body = {
contents,
generationConfig: {
temperature: 0.7,
maxOutputTokens: 4096,
},
};
if (systemMessage) {
body.systemInstruction = {
parts: [{ text: systemMessage.content }],
};
}
// Gemini REST API requires the API key as a query parameter (not a header).
// This is Google's documented authentication pattern for the Generative Language API.
const response = await fetch(`https://generativelanguage.googleapis.com/v1beta/models/${model}:generateContent?key=${apiKey}`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
signal: options?.signal,
body: JSON.stringify(body),
});
if (!response.ok) {
const error = await response.json().catch(() => ({}));
throw new Error(error.error?.message || `Gemini API error: ${response.status}`);
}
const data = await response.json();
return {
content: data.candidates?.[0]?.content?.parts?.[0]?.text || '',
usage: data.usageMetadata ? {
inputTokens: data.usageMetadata.promptTokenCount,
outputTokens: data.usageMetadata.candidatesTokenCount,
} : undefined,
};
},
estimateTokens(text) {
return Math.ceil(text.length / 4);
},
};
}
//# sourceMappingURL=gemini.js.map
{"version":3,"file":"gemini.js","sourceRoot":"","sources":["../../../src/llm/providers/gemini.ts"],"names":[],"mappings":"AAAA,wEAAwE;AAIxE,MAAM,UAAU,kBAAkB,CAAC,MAAc,EAAE,KAAa;IAC9D,OAAO;QACL,QAAQ,EAAE,QAAQ;QAClB,KAAK;QAEL,KAAK,CAAC,IAAI,CAAC,QAAsB,EAAE,OAAqB;YACtD,MAAM,aAAa,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC;YAC9D,MAAM,YAAY,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC;YAE/D,MAAM,QAAQ,GAAG,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;gBACtC,IAAI,EAAE,CAAC,CAAC,IAAI,KAAK,WAAW,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM;gBAC/C,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC;aAC7B,CAAC,CAAC,CAAC;YAEJ,MAAM,IAAI,GAA4B;gBACpC,QAAQ;gBACR,gBAAgB,EAAE;oBAChB,WAAW,EAAE,GAAG;oBAChB,eAAe,EAAE,IAAI;iBACtB;aACF,CAAC;YAEF,IAAI,aAAa,EAAE,CAAC;gBAClB,IAAI,CAAC,iBAAiB,GAAG;oBACvB,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,aAAa,CAAC,OAAO,EAAE,CAAC;iBACzC,CAAC;YACJ,CAAC;YAED,4EAA4E;YAC5E,sFAAsF;YACtF,MAAM,QAAQ,GAAG,MAAM,KAAK,CAC1B,2DAA2D,KAAK,wBAAwB,MAAM,EAAE,EAChG;gBACE,MAAM,EAAE,MAAM;gBACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;gBAC/C,MAAM,EAAE,OAAO,EAAE,MAAM;gBACvB,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC;aAC3B,CACF,CAAC;YAEF,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;gBACjB,MAAM,KAAK,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,CAAC,CAAqC,CAAC;gBAC1F,MAAM,IAAI,KAAK,CAAC,KAAK,CAAC,KAAK,EAAE,OAAO,IAAI,qBAAqB,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;YAClF,CAAC;YAED,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAG/B,CAAC;YAEF,OAAO;gBACL,OAAO,EAAE,IAAI,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,IAAI,EAAE;gBAC9D,KAAK,EAAE,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC;oBAC1B,WAAW,EAAE,IAAI,CAAC,aAAa,CAAC,gBAAgB;oBAChD,YAAY,EAAE,IAAI,CAAC,aAAa,CAAC,oBAAoB;iBACtD,CAAC,CAAC,CAAC,SAAS;aACd,CAAC;QACJ,CAAC;QAED,cAAc,CAAC,IAAY;YACzB,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;QACpC,CAAC;KACF,CAAC;AACJ,CAAC"}
import type { LLMClient } from '../types.js';
export declare function createOllamaClient(model: string, baseUrl?: string): LLMClient;
/**
* Discover installed Ollama models by querying the local API.
* Returns empty array if Ollama is not running or unreachable.
*/
export declare function discoverOllamaModels(baseUrl?: string): Promise<Array<{
name: string;
size: number;
modifiedAt: string;
}>>;
//# sourceMappingURL=ollama.d.ts.map
{"version":3,"file":"ollama.d.ts","sourceRoot":"","sources":["../../../src/llm/providers/ollama.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAwC,MAAM,aAAa,CAAC;AAInF,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CA4C7E;AAED;;;GAGG;AACH,wBAAsB,oBAAoB,CACxC,OAAO,CAAC,EAAE,MAAM,GACf,OAAO,CAAC,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC,CAgBpE"}
// Ollama provider implementation (local models, no API key required)
const DEFAULT_OLLAMA_URL = 'http://localhost:11434';
export function createOllamaClient(model, baseUrl) {
const url = baseUrl || DEFAULT_OLLAMA_URL;
return {
provider: 'ollama',
model,
async chat(messages, options) {
const response = await fetch(`${url}/api/chat`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
signal: options?.signal,
body: JSON.stringify({
model,
messages: messages.map(m => ({ role: m.role, content: m.content })),
stream: false,
options: { temperature: 0.7 },
}),
});
if (!response.ok) {
const error = await response.text().catch(() => '');
throw new Error(`Ollama API error: ${response.status}${error ? ` - ${error}` : ''}`);
}
const data = await response.json();
return {
content: data.message?.content || '',
usage: {
inputTokens: data.prompt_eval_count || 0,
outputTokens: data.eval_count || 0,
},
};
},
estimateTokens(text) {
return Math.ceil(text.length / 4);
},
};
}
/**
* Discover installed Ollama models by querying the local API.
* Returns empty array if Ollama is not running or unreachable.
*/
export async function discoverOllamaModels(baseUrl) {
const url = baseUrl || DEFAULT_OLLAMA_URL;
try {
const response = await fetch(`${url}/api/tags`, {
signal: AbortSignal.timeout(3000),
});
if (!response.ok)
return [];
const data = await response.json();
return (data.models || []).map(m => ({
name: m.name,
size: m.size,
modifiedAt: m.modified_at,
}));
}
catch {
return [];
}
}
//# sourceMappingURL=ollama.js.map
{"version":3,"file":"ollama.js","sourceRoot":"","sources":["../../../src/llm/providers/ollama.ts"],"names":[],"mappings":"AAAA,qEAAqE;AAIrE,MAAM,kBAAkB,GAAG,wBAAwB,CAAC;AAEpD,MAAM,UAAU,kBAAkB,CAAC,KAAa,EAAE,OAAgB;IAChE,MAAM,GAAG,GAAG,OAAO,IAAI,kBAAkB,CAAC;IAE1C,OAAO;QACL,QAAQ,EAAE,QAAQ;QAClB,KAAK;QAEL,KAAK,CAAC,IAAI,CAAC,QAAsB,EAAE,OAAqB;YACtD,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,GAAG,WAAW,EAAE;gBAC9C,MAAM,EAAE,MAAM;gBACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;gBAC/C,MAAM,EAAE,OAAO,EAAE,MAAM;gBACvB,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;oBACnB,KAAK;oBACL,QAAQ,EAAE,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC;oBACnE,MAAM,EAAE,KAAK;oBACb,OAAO,EAAE,EAAE,WAAW,EAAE,GAAG,EAAE;iBAC9B,CAAC;aACH,CAAC,CAAC;YAEH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;gBACjB,MAAM,KAAK,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC;gBACpD,MAAM,IAAI,KAAK,CAAC,qBAAqB,QAAQ,CAAC,MAAM,GAAG,KAAK,CAAC,CAAC,CAAC,MAAM,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YACvF,CAAC;YAED,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAI/B,CAAC;YAEF,OAAO;gBACL,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,OAAO,IAAI,EAAE;gBACpC,KAAK,EAAE;oBACL,WAAW,EAAE,IAAI,CAAC,iBAAiB,IAAI,CAAC;oBACxC,YAAY,EAAE,IAAI,CAAC,UAAU,IAAI,CAAC;iBACnC;aACF,CAAC;QACJ,CAAC;QAED,cAAc,CAAC,IAAY;YACzB,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;QACpC,CAAC;KACF,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,OAAgB;IAEhB,MAAM,GAAG,GAAG,OAAO,IAAI,kBAAkB,CAAC;IAC1C,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,GAAG,WAAW,EAAE;YAC9C,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,IAAI,CAAC;SAClC,CAAC,CAAC;QACH,IAAI,CAAC,QAAQ,CAAC,EAAE;YAAE,OAAO,EAAE,CAAC;QAC5B,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAA6E,CAAC;QAC9G,OAAO,CAAC,IAAI,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;YACnC,IAAI,EAAE,CAAC,CAAC,IAAI;YACZ,IAAI,EAAE,CAAC,CAAC,IAAI;YACZ,UAAU,EAAE,CAAC,CAAC,WAAW;SAC1B,CAAC,CAAC,CAAC;IACN,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC"}
import type { LLMClient } from '../types.js';
export declare function createOpenAIClient(apiKey: string, model: string): LLMClient;
//# sourceMappingURL=openai.d.ts.map
{"version":3,"file":"openai.d.ts","sourceRoot":"","sources":["../../../src/llm/providers/openai.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAwC,MAAM,aAAa,CAAC;AAEnF,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,SAAS,CA4C3E"}
// OpenAI provider implementation (server-side, no browser dependencies)
export function createOpenAIClient(apiKey, model) {
return {
provider: 'openai',
model,
async chat(messages, options) {
const response = await fetch('https://api.openai.com/v1/chat/completions', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${apiKey}`,
},
signal: options?.signal,
body: JSON.stringify({
model,
messages: messages.map(m => ({ role: m.role, content: m.content })),
temperature: 0.7,
}),
});
if (!response.ok) {
const error = await response.json().catch(() => ({}));
throw new Error(error.error?.message || `OpenAI API error: ${response.status}`);
}
const data = await response.json();
return {
content: data.choices[0]?.message?.content || '',
usage: data.usage ? {
inputTokens: data.usage.prompt_tokens,
outputTokens: data.usage.completion_tokens,
} : undefined,
};
},
estimateTokens(text) {
// Rough estimate: ~4 characters per token for English
return Math.ceil(text.length / 4);
},
};
}
//# sourceMappingURL=openai.js.map
{"version":3,"file":"openai.js","sourceRoot":"","sources":["../../../src/llm/providers/openai.ts"],"names":[],"mappings":"AAAA,wEAAwE;AAIxE,MAAM,UAAU,kBAAkB,CAAC,MAAc,EAAE,KAAa;IAC9D,OAAO;QACL,QAAQ,EAAE,QAAQ;QAClB,KAAK;QAEL,KAAK,CAAC,IAAI,CAAC,QAAsB,EAAE,OAAqB;YACtD,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,4CAA4C,EAAE;gBACzE,MAAM,EAAE,MAAM;gBACd,OAAO,EAAE;oBACP,cAAc,EAAE,kBAAkB;oBAClC,eAAe,EAAE,UAAU,MAAM,EAAE;iBACpC;gBACD,MAAM,EAAE,OAAO,EAAE,MAAM;gBACvB,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;oBACnB,KAAK;oBACL,QAAQ,EAAE,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC;oBACnE,WAAW,EAAE,GAAG;iBACjB,CAAC;aACH,CAAC,CAAC;YAEH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;gBACjB,MAAM,KAAK,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,CAAC,CAAqC,CAAC;gBAC1F,MAAM,IAAI,KAAK,CAAC,KAAK,CAAC,KAAK,EAAE,OAAO,IAAI,qBAAqB,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;YAClF,CAAC;YAED,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAG/B,CAAC;YAEF,OAAO;gBACL,OAAO,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,IAAI,EAAE;gBAChD,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;oBAClB,WAAW,EAAE,IAAI,CAAC,KAAK,CAAC,aAAa;oBACrC,YAAY,EAAE,IAAI,CAAC,KAAK,CAAC,iBAAiB;iBAC3C,CAAC,CAAC,CAAC,SAAS;aACd,CAAC;QACJ,CAAC;QAED,cAAc,CAAC,IAAY;YACzB,sDAAsD;YACtD,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;QACpC,CAAC;KACF,CAAC;AACJ,CAAC"}
export type { LLMProvider, LLMProviderConfig, ProviderInfo, ProviderModelOption } from '@code-insights/cli/types';
export interface LLMMessage {
role: 'system' | 'user' | 'assistant';
content: string;
}
export interface LLMResponse {
content: string;
usage?: {
inputTokens: number;
outputTokens: number;
};
}
export interface ChatOptions {
signal?: AbortSignal;
}
export interface LLMClient {
chat(messages: LLMMessage[], options?: ChatOptions): Promise<LLMResponse>;
estimateTokens(text: string): number;
readonly provider: string;
readonly model: string;
}
//# sourceMappingURL=types.d.ts.map
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/llm/types.ts"],"names":[],"mappings":"AAIA,YAAY,EAAE,WAAW,EAAE,iBAAiB,EAAE,YAAY,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAElH,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,QAAQ,GAAG,MAAM,GAAG,WAAW,CAAC;IACtC,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE;QACN,WAAW,EAAE,MAAM,CAAC;QACpB,YAAY,EAAE,MAAM,CAAC;KACtB,CAAC;CACH;AAED,MAAM,WAAW,WAAW;IAC1B,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAED,MAAM,WAAW,SAAS;IACxB,IAAI,CAAC,QAAQ,EAAE,UAAU,EAAE,EAAE,OAAO,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC;IAC1E,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IACrC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB"}
// LLM abstraction types for the server-side LLM engine.
// Provider metadata (PROVIDERS constant) lives in cli/src/constants/llm-providers.ts.
// LLMProvider, LLMProviderConfig, ProviderInfo are imported from CLI types (single source of truth).
export {};
//# sourceMappingURL=types.js.map
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/llm/types.ts"],"names":[],"mappings":"AAAA,wDAAwD;AACxD,sFAAsF;AACtF,qGAAqG"}
import { Hono } from 'hono';
declare const app: Hono<import("hono/types").BlankEnv, import("hono/types").BlankSchema, "/">;
export default app;
//# sourceMappingURL=analysis.d.ts.map
{"version":3,"file":"analysis.d.ts","sourceRoot":"","sources":["../../src/routes/analysis.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAQ5B,QAAA,MAAM,GAAG,4EAAa,CAAC;AAyHvB,eAAe,GAAG,CAAC"}
import { Hono } from 'hono';
import { getDb } from '@code-insights/cli/db/client';
import { trackEvent } from '@code-insights/cli/utils/telemetry';
import { parseIntParam } from '../utils.js';
import { analyzeSession, analyzePromptQuality, findRecurringInsights } from '../llm/analysis.js';
import { isLLMConfigured } from '../llm/client.js';
const app = new Hono();
// POST /api/analysis/session
// Body: { sessionId: string }
// Fetches session + messages from SQLite, runs LLM analysis, saves insights, returns results.
app.post('/session', async (c) => {
if (!isLLMConfigured()) {
return c.json({
success: false,
error: 'LLM not configured. Run `code-insights config llm` to configure a provider.',
}, 400);
}
const body = await c.req.json();
if (!body.sessionId || typeof body.sessionId !== 'string') {
return c.json({ error: 'Missing required field: sessionId' }, 400);
}
const db = getDb();
const session = db.prepare(`
SELECT id, project_id, project_name, project_path, summary, ended_at
FROM sessions WHERE id = ?
`).get(body.sessionId);
if (!session) {
return c.json({ error: 'Session not found' }, 404);
}
const messages = db.prepare(`
SELECT id, session_id, type, content, thinking, tool_calls, tool_results, usage, timestamp, parent_id
FROM messages WHERE session_id = ? ORDER BY timestamp ASC
`).all(body.sessionId);
const result = await analyzeSession(session, messages);
if (result.success)
trackEvent('analysis', true, 'session');
return c.json(result, result.success ? 200 : 422);
});
// POST /api/analysis/prompt-quality
// Body: { sessionId: string }
// Runs prompt quality analysis on user messages in the session.
app.post('/prompt-quality', async (c) => {
if (!isLLMConfigured()) {
return c.json({
success: false,
error: 'LLM not configured. Run `code-insights config llm` to configure a provider.',
}, 400);
}
const body = await c.req.json();
if (!body.sessionId || typeof body.sessionId !== 'string') {
return c.json({ error: 'Missing required field: sessionId' }, 400);
}
const db = getDb();
const session = db.prepare(`
SELECT id, project_id, project_name, project_path, summary, ended_at
FROM sessions WHERE id = ?
`).get(body.sessionId);
if (!session) {
return c.json({ error: 'Session not found' }, 404);
}
const messages = db.prepare(`
SELECT id, session_id, type, content, thinking, tool_calls, tool_results, usage, timestamp, parent_id
FROM messages WHERE session_id = ? ORDER BY timestamp ASC
`).all(body.sessionId);
const result = await analyzePromptQuality(session, messages);
if (result.success)
trackEvent('analysis', true, 'prompt-quality');
return c.json(result, result.success ? 200 : 422);
});
// POST /api/analysis/recurring
// Body: { projectId?: string; limit?: number }
// Finds recurring insight patterns across sessions.
app.post('/recurring', async (c) => {
if (!isLLMConfigured()) {
return c.json({
success: false,
error: 'LLM not configured. Run `code-insights config llm` to configure a provider.',
}, 400);
}
const body = await c.req.json();
const db = getDb();
const conditions = [];
const params = [];
if (body.projectId) {
conditions.push('project_id = ?');
params.push(body.projectId);
}
const where = conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : '';
const limit = Math.min(parseIntParam(String(body.limit ?? ''), 200), 200);
const insights = db.prepare(`
SELECT id, type, title, summary, project_name, session_id
FROM insights
${where}
ORDER BY timestamp DESC
LIMIT ?
`).all(...params, limit);
const result = await findRecurringInsights(insights);
if (result.success)
trackEvent('analysis', true, 'recurring');
return c.json(result, result.success ? 200 : 422);
});
export default app;
//# sourceMappingURL=analysis.js.map
{"version":3,"file":"analysis.js","sourceRoot":"","sources":["../../src/routes/analysis.ts"],"names":[],"mappings":"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"}
import { Hono } from 'hono';
declare const app: Hono<import("hono/types").BlankEnv, import("hono/types").BlankSchema, "/">;
export default app;
//# sourceMappingURL=analytics.d.ts.map
{"version":3,"file":"analytics.d.ts","sourceRoot":"","sources":["../../src/routes/analytics.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAG5B,QAAA,MAAM,GAAG,4EAAa,CAAC;AAmDvB,eAAe,GAAG,CAAC"}
import { Hono } from 'hono';
import { getDb } from '@code-insights/cli/db/client';
const app = new Hono();
const VALID_RANGES = ['7d', '30d', '90d', 'all'];
// Dashboard overview stats for a given time range (e.g. ?range=7d|30d|90d|all)
app.get('/dashboard', (c) => {
const db = getDb();
const { range = '7d' } = c.req.query();
if (!VALID_RANGES.includes(range)) {
return c.json({ error: `Invalid range. Must be one of: ${VALID_RANGES.join(', ')}` }, 400);
}
let periodStart = null;
const now = new Date();
if (range === '7d') {
periodStart = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000).toISOString();
}
else if (range === '30d') {
periodStart = new Date(now.getTime() - 30 * 24 * 60 * 60 * 1000).toISOString();
}
else if (range === '90d') {
periodStart = new Date(now.getTime() - 90 * 24 * 60 * 60 * 1000).toISOString();
}
const where = periodStart ? 'WHERE started_at >= ?' : '';
const params = periodStart ? [periodStart] : [];
const stats = db.prepare(`
SELECT
COUNT(*) AS session_count,
SUM(message_count) AS total_messages,
SUM(total_input_tokens) AS total_input_tokens,
SUM(total_output_tokens) AS total_output_tokens,
SUM(estimated_cost_usd) AS estimated_cost_usd
FROM sessions ${where}
`).get(...params);
return c.json({ range, stats });
});
// Global cumulative usage stats
app.get('/usage', (c) => {
const db = getDb();
const stats = db.prepare(`
SELECT total_input_tokens, total_output_tokens, cache_creation_tokens,
cache_read_tokens, estimated_cost_usd, sessions_with_usage, last_updated_at
FROM usage_stats WHERE id = 1
`).get();
return c.json({ stats: stats ?? null });
});
export default app;
//# sourceMappingURL=analytics.js.map
{"version":3,"file":"analytics.js","sourceRoot":"","sources":["../../src/routes/analytics.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,EAAE,KAAK,EAAE,MAAM,8BAA8B,CAAC;AAErD,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC;AAEvB,MAAM,YAAY,GAAG,CAAC,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,CAAU,CAAC;AAG1D,+EAA+E;AAC/E,GAAG,CAAC,GAAG,CAAC,YAAY,EAAE,CAAC,CAAC,EAAE,EAAE;IAC1B,MAAM,EAAE,GAAG,KAAK,EAAE,CAAC;IACnB,MAAM,EAAE,KAAK,GAAG,IAAI,EAAE,GAAG,CAAC,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC;IAEvC,IAAI,CAAC,YAAY,CAAC,QAAQ,CAAC,KAAc,CAAC,EAAE,CAAC;QAC3C,OAAO,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,kCAAkC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,GAAG,CAAC,CAAC;IAC7F,CAAC;IAED,IAAI,WAAW,GAAkB,IAAI,CAAC;IACtC,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC;IACvB,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;QACnB,WAAW,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,GAAG,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC,WAAW,EAAE,CAAC;IAChF,CAAC;SAAM,IAAI,KAAK,KAAK,KAAK,EAAE,CAAC;QAC3B,WAAW,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC,WAAW,EAAE,CAAC;IACjF,CAAC;SAAM,IAAI,KAAK,KAAK,KAAK,EAAE,CAAC;QAC3B,WAAW,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC,WAAW,EAAE,CAAC;IACjF,CAAC;IAED,MAAM,KAAK,GAAG,WAAW,CAAC,CAAC,CAAC,uBAAuB,CAAC,CAAC,CAAC,EAAE,CAAC;IACzD,MAAM,MAAM,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAEhD,MAAM,KAAK,GAAG,EAAE,CAAC,OAAO,CAAC;;;;;;;oBAOP,KAAK;GACtB,CAAC,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,CAAC;IAElB,OAAO,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC;AAClC,CAAC,CAAC,CAAC;AAEH,gCAAgC;AAChC,GAAG,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE;IACtB,MAAM,EAAE,GAAG,KAAK,EAAE,CAAC;IACnB,MAAM,KAAK,GAAG,EAAE,CAAC,OAAO,CAAC;;;;GAIxB,CAAC,CAAC,GAAG,EAAE,CAAC;IACT,OAAO,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,KAAK,IAAI,IAAI,EAAE,CAAC,CAAC;AAC1C,CAAC,CAAC,CAAC;AAEH,eAAe,GAAG,CAAC"}
import { Hono } from 'hono';
declare const app: Hono<import("hono/types").BlankEnv, import("hono/types").BlankSchema, "/">;
export default app;
//# sourceMappingURL=config.d.ts.map
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/routes/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAM5B,QAAA,MAAM,GAAG,4EAAa,CAAC;AAiIvB,eAAe,GAAG,CAAC"}
import { Hono } from 'hono';
import { loadConfig, saveConfig } from '@code-insights/cli/utils/config';
import { loadLLMConfig, testLLMConfig } from '../llm/client.js';
import { discoverOllamaModels } from '../llm/providers/ollama.js';
const app = new Hono();
const VALID_PROVIDERS = ['openai', 'anthropic', 'gemini', 'ollama'];
function maskApiKey(key) {
if (!key || key.length < 8)
return key ? '***' : undefined;
return key.slice(0, 4) + '...' + key.slice(-4);
}
// GET /api/config/llm — return full config (API key masked)
app.get('/llm', (c) => {
const config = loadConfig();
const llm = config?.dashboard?.llm;
return c.json({
dashboardPort: config?.dashboard?.port ?? 7890,
provider: llm?.provider,
model: llm?.model,
apiKey: maskApiKey(llm?.apiKey),
baseUrl: llm?.baseUrl,
});
});
// PUT /api/config/llm — update dashboard port and/or LLM config
app.put('/llm', async (c) => {
const body = await c.req.json();
const config = loadConfig() ?? {
sync: { claudeDir: '', excludeProjects: [] },
};
let changed = false;
// Update dashboard port if provided
if (body.dashboardPort !== undefined) {
const port = body.dashboardPort;
if (typeof port !== 'number' || !Number.isInteger(port) || port < 1 || port > 65535) {
return c.json({ error: 'dashboardPort must be an integer between 1 and 65535' }, 400);
}
config.dashboard = { ...config.dashboard, port };
changed = true;
}
// Update LLM config if any LLM field is provided
const hasLLMField = body.provider !== undefined || body.model !== undefined ||
body.apiKey !== undefined || body.baseUrl !== undefined;
if (hasLLMField) {
if (body.provider !== undefined && !VALID_PROVIDERS.includes(body.provider)) {
return c.json({ error: `provider must be one of: ${VALID_PROVIDERS.join(', ')}` }, 400);
}
const existingLlm = config.dashboard?.llm ?? {};
const updatedLlm = {
provider: body.provider ?? existingLlm.provider ?? 'ollama',
model: body.model ?? existingLlm.model ?? '',
// Preserve existing API key if not provided in update
...(body.apiKey !== undefined
? { apiKey: body.apiKey || undefined }
: existingLlm.apiKey !== undefined ? { apiKey: existingLlm.apiKey } : {}),
...(body.baseUrl !== undefined
? { baseUrl: body.baseUrl || undefined }
: existingLlm.baseUrl !== undefined ? { baseUrl: existingLlm.baseUrl } : {}),
};
if (!updatedLlm.model) {
return c.json({ error: 'model is required when setting LLM config' }, 400);
}
config.dashboard = { ...config.dashboard, llm: updatedLlm };
changed = true;
}
if (!changed) {
return c.json({ ok: true });
}
saveConfig(config);
return c.json({ ok: true });
});
// POST /api/config/llm/test — validate LLM credentials with a test call
app.post('/llm/test', async (c) => {
// Allow testing with body config or existing saved config
let testConfig = null;
try {
const body = await c.req.json();
if (body.provider && body.model) {
testConfig = {
provider: body.provider,
model: body.model,
...(body.apiKey ? { apiKey: body.apiKey } : {}),
...(body.baseUrl ? { baseUrl: body.baseUrl } : {}),
};
}
}
catch {
// No body or invalid JSON — use existing config
}
if (!testConfig) {
testConfig = loadLLMConfig();
}
if (!testConfig) {
return c.json({
success: false,
error: 'No LLM config found. Run `code-insights config llm` or provide config in request body.',
}, 400);
}
const result = await testLLMConfig(testConfig);
return c.json(result, result.success ? 200 : 422);
});
// GET /api/config/llm/ollama-models — return locally available Ollama models
app.get('/llm/ollama-models', async (c) => {
const baseUrl = c.req.query('baseUrl');
const models = await discoverOllamaModels(baseUrl);
return c.json({ models });
});
export default app;
//# sourceMappingURL=config.js.map
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/routes/config.ts"],"names":[],"mappings":"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"}
import { Hono } from 'hono';
declare const app: Hono<import("hono/types").BlankEnv, import("hono/types").BlankSchema, "/">;
export default app;
//# sourceMappingURL=export.d.ts.map
{"version":3,"file":"export.d.ts","sourceRoot":"","sources":["../../src/routes/export.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAI5B,QAAA,MAAM,GAAG,4EAAa,CAAC;AAyDvB,eAAe,GAAG,CAAC"}
import { Hono } from 'hono';
import { getDb } from '@code-insights/cli/db/client';
import { trackEvent } from '@code-insights/cli/utils/telemetry';
const app = new Hono();
// POST /api/export/markdown — export sessions/insights as markdown
app.post('/markdown', async (c) => {
const db = getDb();
const body = await c.req.json();
const { sessionIds, projectId } = body;
if (sessionIds !== undefined && !Array.isArray(sessionIds)) {
return c.json({ error: 'sessionIds must be an array' }, 400);
}
if (sessionIds && sessionIds.some((id) => typeof id !== 'string')) {
return c.json({ error: 'sessionIds must contain only strings' }, 400);
}
if (sessionIds && sessionIds.length > 100) {
return c.json({ error: 'Maximum 100 session IDs per export request' }, 400);
}
let sessions;
if (sessionIds && sessionIds.length > 0) {
const placeholders = sessionIds.map(() => '?').join(', ');
sessions = db.prepare(`SELECT id, project_name, generated_title, custom_title, started_at, ended_at,
message_count, estimated_cost_usd, session_character, source_tool
FROM sessions WHERE id IN (${placeholders}) ORDER BY started_at DESC`).all(...sessionIds);
}
else if (projectId) {
sessions = db.prepare(`SELECT id, project_name, generated_title, custom_title, started_at, ended_at,
message_count, estimated_cost_usd, session_character, source_tool
FROM sessions WHERE project_id = ? ORDER BY started_at DESC`).all(projectId);
}
else {
return c.json({ error: 'sessionIds or projectId required' }, 400);
}
const lines = ['# Code Insights Export', ''];
for (const s of sessions) {
const title = (s.custom_title ?? s.generated_title ?? s.id);
lines.push(`## ${title}`);
lines.push(`- **Started:** ${s.started_at}`);
lines.push(`- **Ended:** ${s.ended_at}`);
lines.push(`- **Messages:** ${s.message_count}`);
if (s.estimated_cost_usd != null) {
lines.push(`- **Cost:** $${Number(s.estimated_cost_usd).toFixed(4)}`);
}
lines.push('');
}
const markdown = lines.join('\n');
trackEvent('export', true, 'markdown');
c.header('Content-Type', 'text/markdown');
return c.body(markdown);
});
export default app;
//# sourceMappingURL=export.js.map
{"version":3,"file":"export.js","sourceRoot":"","sources":["../../src/routes/export.ts"],"names":[],"mappings":"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"}
import { Hono } from 'hono';
declare const app: Hono<import("hono/types").BlankEnv, import("hono/types").BlankSchema, "/">;
export default app;
//# sourceMappingURL=insights.d.ts.map
{"version":3,"file":"insights.d.ts","sourceRoot":"","sources":["../../src/routes/insights.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAK5B,QAAA,MAAM,GAAG,4EAAa,CAAC;AAgHvB,eAAe,GAAG,CAAC"}
import { Hono } from 'hono';
import { getDb } from '@code-insights/cli/db/client';
import { randomUUID } from 'crypto';
import { parseIntParam } from '../utils.js';
const app = new Hono();
const VALID_TYPES = ['summary', 'decision', 'learning', 'technique', 'prompt_quality'];
app.get('/', (c) => {
const db = getDb();
const { projectId, sessionId, type, limit, offset } = c.req.query();
const conditions = [];
const params = [];
if (projectId) {
conditions.push('project_id = ?');
params.push(projectId);
}
if (sessionId) {
conditions.push('session_id = ?');
params.push(sessionId);
}
if (type) {
conditions.push('type = ?');
params.push(type);
}
const where = conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : '';
const insights = db.prepare(`
SELECT id, session_id, project_id, project_name, type, title, content,
summary, bullets, confidence, source, metadata, timestamp,
created_at, scope, analysis_version, linked_insight_ids
FROM insights
${where}
ORDER BY timestamp DESC
LIMIT ? OFFSET ?
`).all(...params, parseIntParam(limit, 100), parseIntParam(offset, 0));
return c.json({ insights });
});
app.post('/', async (c) => {
const db = getDb();
const body = await c.req.json();
// Validate required string fields
const required = ['sessionId', 'projectId', 'type', 'title', 'content'];
for (const field of required) {
if (!body[field] || typeof body[field] !== 'string') {
return c.json({ error: `Missing or invalid field: ${field}` }, 400);
}
}
// Validate type is one of the known insight types
if (!VALID_TYPES.includes(body.type)) {
return c.json({ error: `type must be one of: ${VALID_TYPES.join(', ')}` }, 400);
}
// Validate confidence is a finite number if provided
if (body.confidence !== undefined && (typeof body.confidence !== 'number' || !Number.isFinite(body.confidence))) {
return c.json({ error: 'confidence must be a finite number' }, 400);
}
const id = randomUUID();
const now = new Date().toISOString();
try {
db.prepare(`
INSERT INTO insights (
id, session_id, project_id, project_name, type, title, content,
summary, bullets, confidence, source, metadata, timestamp, created_at
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'llm', ?, ?, ?)
`).run(id, body.sessionId, body.projectId, body.projectName ?? '', body.type, body.title, body.content, body.summary ?? '', body.bullets ? JSON.stringify(body.bullets) : null, body.confidence ?? 0, body.metadata ? JSON.stringify(body.metadata) : null, now, now);
}
catch (err) {
if (err instanceof Error && err.message.includes('FOREIGN KEY constraint failed')) {
return c.json({ error: 'Invalid sessionId or projectId' }, 400);
}
throw err;
}
return c.json({ id }, 201);
});
app.delete('/:id', (c) => {
const db = getDb();
const result = db.prepare('DELETE FROM insights WHERE id = ?').run(c.req.param('id'));
if (result.changes === 0)
return c.json({ error: 'Not found' }, 404);
return c.json({ ok: true });
});
export default app;
//# sourceMappingURL=insights.js.map
{"version":3,"file":"insights.js","sourceRoot":"","sources":["../../src/routes/insights.ts"],"names":[],"mappings":"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"}
import { Hono } from 'hono';
declare const app: Hono<import("hono/types").BlankEnv, import("hono/types").BlankSchema, "/">;
export default app;
//# sourceMappingURL=messages.d.ts.map
{"version":3,"file":"messages.d.ts","sourceRoot":"","sources":["../../src/routes/messages.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAI5B,QAAA,MAAM,GAAG,4EAAa,CAAC;AAgBvB,eAAe,GAAG,CAAC"}
import { Hono } from 'hono';
import { getDb } from '@code-insights/cli/db/client';
import { parseIntParam } from '../utils.js';
const app = new Hono();
app.get('/:sessionId', (c) => {
const db = getDb();
const { limit, offset } = c.req.query();
const messages = db.prepare(`
SELECT id, session_id, type, content, thinking,
tool_calls, tool_results, usage, timestamp, parent_id
FROM messages
WHERE session_id = ?
ORDER BY timestamp ASC
LIMIT ? OFFSET ?
`).all(c.req.param('sessionId'), parseIntParam(limit, 100), parseIntParam(offset, 0));
return c.json({ messages });
});
export default app;
//# sourceMappingURL=messages.js.map
{"version":3,"file":"messages.js","sourceRoot":"","sources":["../../src/routes/messages.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,EAAE,KAAK,EAAE,MAAM,8BAA8B,CAAC;AACrD,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE5C,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC;AAEvB,GAAG,CAAC,GAAG,CAAC,aAAa,EAAE,CAAC,CAAC,EAAE,EAAE;IAC3B,MAAM,EAAE,GAAG,KAAK,EAAE,CAAC;IACnB,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,CAAC,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC;IACxC,MAAM,QAAQ,GAAG,EAAE,CAAC,OAAO,CAAC;;;;;;;GAO3B,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,WAAW,CAAC,EAAE,aAAa,CAAC,KAAK,EAAE,GAAG,CAAC,EAAE,aAAa,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC;IACtF,OAAO,CAAC,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC;AAC9B,CAAC,CAAC,CAAC;AAEH,eAAe,GAAG,CAAC"}
import { Hono } from 'hono';
declare const app: Hono<import("hono/types").BlankEnv, import("hono/types").BlankSchema, "/">;
export default app;
//# sourceMappingURL=projects.d.ts.map
{"version":3,"file":"projects.d.ts","sourceRoot":"","sources":["../../src/routes/projects.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAI5B,QAAA,MAAM,GAAG,4EAAa,CAAC;AA6BvB,eAAe,GAAG,CAAC"}
import { Hono } from 'hono';
import { getDb } from '@code-insights/cli/db/client';
import { parseIntParam } from '../utils.js';
const app = new Hono();
app.get('/', (c) => {
const db = getDb();
const { limit, offset } = c.req.query();
const projects = db.prepare(`
SELECT id, name, path, git_remote_url, session_count, last_activity,
total_input_tokens, total_output_tokens, cache_creation_tokens,
cache_read_tokens, estimated_cost_usd, created_at, updated_at
FROM projects
ORDER BY last_activity DESC
LIMIT ? OFFSET ?
`).all(parseIntParam(limit, 100), parseIntParam(offset, 0));
return c.json({ projects });
});
app.get('/:id', (c) => {
const db = getDb();
const project = db.prepare(`
SELECT id, name, path, git_remote_url, session_count, last_activity,
total_input_tokens, total_output_tokens, cache_creation_tokens,
cache_read_tokens, estimated_cost_usd, created_at, updated_at
FROM projects
WHERE id = ?
`).get(c.req.param('id'));
if (!project)
return c.json({ error: 'Not found' }, 404);
return c.json({ project });
});
export default app;
//# sourceMappingURL=projects.js.map
{"version":3,"file":"projects.js","sourceRoot":"","sources":["../../src/routes/projects.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,EAAE,KAAK,EAAE,MAAM,8BAA8B,CAAC;AACrD,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE5C,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC;AAEvB,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,EAAE;IACjB,MAAM,EAAE,GAAG,KAAK,EAAE,CAAC;IACnB,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,CAAC,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC;IACxC,MAAM,QAAQ,GAAG,EAAE,CAAC,OAAO,CAAC;;;;;;;GAO3B,CAAC,CAAC,GAAG,CAAC,aAAa,CAAC,KAAK,EAAE,GAAG,CAAC,EAAE,aAAa,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC;IAC5D,OAAO,CAAC,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC;AAC9B,CAAC,CAAC,CAAC;AAEH,GAAG,CAAC,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC,EAAE,EAAE;IACpB,MAAM,EAAE,GAAG,KAAK,EAAE,CAAC;IACnB,MAAM,OAAO,GAAG,EAAE,CAAC,OAAO,CAAC;;;;;;GAM1B,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC;IAC1B,IAAI,CAAC,OAAO;QAAE,OAAO,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,WAAW,EAAE,EAAE,GAAG,CAAC,CAAC;IACzD,OAAO,CAAC,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC;AAC7B,CAAC,CAAC,CAAC;AAEH,eAAe,GAAG,CAAC"}
import { Hono } from 'hono';
declare const app: Hono<import("hono/types").BlankEnv, import("hono/types").BlankSchema, "/">;
export default app;
//# sourceMappingURL=sessions.d.ts.map
{"version":3,"file":"sessions.d.ts","sourceRoot":"","sources":["../../src/routes/sessions.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAI5B,QAAA,MAAM,GAAG,4EAAa,CAAC;AAqEvB,eAAe,GAAG,CAAC"}
import { Hono } from 'hono';
import { getDb } from '@code-insights/cli/db/client';
import { parseIntParam } from '../utils.js';
const app = new Hono();
app.get('/', (c) => {
const db = getDb();
const { projectId, sourceTool, limit, offset } = c.req.query();
const conditions = [];
const params = [];
if (projectId) {
conditions.push('project_id = ?');
params.push(projectId);
}
if (sourceTool) {
conditions.push('source_tool = ?');
params.push(sourceTool);
}
const where = conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : '';
const sessions = db.prepare(`
SELECT id, project_id, project_name, project_path, git_remote_url,
summary, custom_title, generated_title, title_source, session_character,
started_at, ended_at, message_count, user_message_count,
assistant_message_count, tool_call_count, git_branch,
claude_version, source_tool, device_id, device_hostname,
device_platform, synced_at, total_input_tokens, total_output_tokens,
cache_creation_tokens, cache_read_tokens, estimated_cost_usd,
models_used, primary_model, usage_source
FROM sessions
${where}
ORDER BY started_at DESC
LIMIT ? OFFSET ?
`).all(...params, parseIntParam(limit, 50), parseIntParam(offset, 0));
return c.json({ sessions });
});
app.get('/:id', (c) => {
const db = getDb();
const session = db.prepare(`
SELECT id, project_id, project_name, project_path, git_remote_url,
summary, custom_title, generated_title, title_source, session_character,
started_at, ended_at, message_count, user_message_count,
assistant_message_count, tool_call_count, git_branch,
claude_version, source_tool, device_id, device_hostname,
device_platform, synced_at, total_input_tokens, total_output_tokens,
cache_creation_tokens, cache_read_tokens, estimated_cost_usd,
models_used, primary_model, usage_source
FROM sessions WHERE id = ?
`).get(c.req.param('id'));
if (!session)
return c.json({ error: 'Not found' }, 404);
return c.json({ session });
});
app.patch('/:id', async (c) => {
const db = getDb();
const body = await c.req.json();
const { customTitle } = body;
if (customTitle === undefined) {
return c.json({ error: 'customTitle is required' }, 400);
}
const result = db.prepare('UPDATE sessions SET custom_title = ? WHERE id = ?').run(customTitle || null, c.req.param('id'));
if (result.changes === 0)
return c.json({ error: 'Not found' }, 404);
return c.json({ ok: true });
});
export default app;
//# sourceMappingURL=sessions.js.map
{"version":3,"file":"sessions.js","sourceRoot":"","sources":["../../src/routes/sessions.ts"],"names":[],"mappings":"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"}
/**
* Parse an integer query parameter with a safe default.
* Returns the default if the value is missing, NaN, negative, or non-finite.
*/
export declare function parseIntParam(value: string | undefined, defaultVal: number): number;
//# sourceMappingURL=utils.d.ts.map
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAGnF"}
/**
* Parse an integer query parameter with a safe default.
* Returns the default if the value is missing, NaN, negative, or non-finite.
*/
export function parseIntParam(value, defaultVal) {
const n = value !== undefined ? parseInt(value, 10) : defaultVal;
return Number.isFinite(n) && n >= 0 ? n : defaultVal;
}
//# sourceMappingURL=utils.js.map
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,UAAU,aAAa,CAAC,KAAyB,EAAE,UAAkB;IACzE,MAAM,CAAC,GAAG,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC;IACjE,OAAO,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC;AACvD,CAAC"}