tuneloop 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +256 -0
- package/dist/chunk-RB45XK57.js +6941 -0
- package/dist/chunk-RB45XK57.js.map +1 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +154 -0
- package/dist/cli.js.map +1 -0
- package/dist/client/app.js +4937 -0
- package/dist/client/app.js.map +1 -0
- package/dist/client/favicon.svg +7 -0
- package/dist/client/index.html +74 -0
- package/dist/client/styles.css +698 -0
- package/dist/index.d.ts +1623 -0
- package/dist/index.js +49 -0
- package/dist/index.js.map +1 -0
- package/package.json +68 -0
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
QueryError,
|
|
4
|
+
analyze,
|
|
5
|
+
describeSchema,
|
|
6
|
+
loadConfig,
|
|
7
|
+
runQuery,
|
|
8
|
+
serve
|
|
9
|
+
} from "./chunk-RB45XK57.js";
|
|
10
|
+
|
|
11
|
+
// src/cli.ts
|
|
12
|
+
import { Command } from "commander";
|
|
13
|
+
import { readFileSync } from "fs";
|
|
14
|
+
|
|
15
|
+
// src/commands/query.ts
|
|
16
|
+
import { existsSync } from "fs";
|
|
17
|
+
async function queryCommand(sql, opts) {
|
|
18
|
+
const { dbPath } = loadConfig({ db: opts.db });
|
|
19
|
+
if (!existsSync(dbPath)) {
|
|
20
|
+
process.stderr.write(`error: no store at ${dbPath} \u2014 run \`tuneloop analyze\` first
|
|
21
|
+
`);
|
|
22
|
+
process.exitCode = 1;
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
if (opts.schema) {
|
|
26
|
+
const dump = describeSchema(dbPath);
|
|
27
|
+
process.stdout.write(opts.json ? `${JSON.stringify(dump, null, 2)}
|
|
28
|
+
` : formatSchema(dump));
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
if (!sql || !sql.trim()) {
|
|
32
|
+
process.stderr.write("error: provide a SQL query, or --schema to see the store shape\n");
|
|
33
|
+
process.exitCode = 1;
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
try {
|
|
37
|
+
const res = runQuery(dbPath, sql, { maxRows: opts.limit });
|
|
38
|
+
if (opts.json) {
|
|
39
|
+
process.stdout.write(`${JSON.stringify(res.rows, null, 2)}
|
|
40
|
+
`);
|
|
41
|
+
} else {
|
|
42
|
+
process.stdout.write(formatTable(res.columns, res.rows));
|
|
43
|
+
}
|
|
44
|
+
if (res.truncated) {
|
|
45
|
+
const why = res.truncated === "rows" ? `row cap reached \u2014 pass --limit <n> for more` : res.truncated === "bytes" ? `response size cap reached` : `time cap reached`;
|
|
46
|
+
process.stderr.write(`
|
|
47
|
+
(truncated: ${why}; ${res.rowCount} rows shown)
|
|
48
|
+
`);
|
|
49
|
+
}
|
|
50
|
+
} catch (err) {
|
|
51
|
+
if (err instanceof QueryError) {
|
|
52
|
+
process.stderr.write(`error: ${err.message}
|
|
53
|
+
`);
|
|
54
|
+
process.exitCode = 1;
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
process.stderr.write(`error: ${err.message}
|
|
58
|
+
`);
|
|
59
|
+
process.exitCode = 1;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
var MAX_CELL = 60;
|
|
63
|
+
function cell(value) {
|
|
64
|
+
if (value === null || value === void 0) return "NULL";
|
|
65
|
+
if (Buffer.isBuffer(value)) return `<blob ${value.length}b>`;
|
|
66
|
+
const s = String(value).replace(/\s+/g, " ");
|
|
67
|
+
return s.length > MAX_CELL ? `${s.slice(0, MAX_CELL - 1)}\u2026` : s;
|
|
68
|
+
}
|
|
69
|
+
function formatTable(columns, rows) {
|
|
70
|
+
if (columns.length === 0) return "(no columns)\n";
|
|
71
|
+
const cells = rows.map((r) => columns.map((c) => cell(r[c])));
|
|
72
|
+
const widths = columns.map((c, i) => Math.max(c.length, ...cells.map((row) => (row[i] ?? "").length), 0));
|
|
73
|
+
const line = (vals) => vals.map((v, i) => v.padEnd(widths[i] ?? 0)).join(" ").trimEnd();
|
|
74
|
+
const out = [line(columns), widths.map((w) => "\u2500".repeat(w)).join(" ")];
|
|
75
|
+
for (const row of cells) out.push(line(row));
|
|
76
|
+
if (rows.length === 0) out.push("(0 rows)");
|
|
77
|
+
return `${out.join("\n")}
|
|
78
|
+
`;
|
|
79
|
+
}
|
|
80
|
+
function formatSchema(dump) {
|
|
81
|
+
const out = [];
|
|
82
|
+
out.push(`# tuneloop store (schema v${dump.schemaVersion ?? "?"})`, "");
|
|
83
|
+
const c = dump.coverage;
|
|
84
|
+
if (c) {
|
|
85
|
+
const span = c.firstAt && c.lastAt ? `${c.firstAt.slice(0, 10)} \u2192 ${c.lastAt.slice(0, 10)}` : "no dated sessions";
|
|
86
|
+
const sources = c.sources.map((s) => `${s.source ?? "(none)"} ${s.count}`).join(", ") || "(none)";
|
|
87
|
+
out.push("## Coverage", "");
|
|
88
|
+
out.push(`- ${c.sessions} sessions, ${span}`);
|
|
89
|
+
out.push(`- sources: ${sources}`);
|
|
90
|
+
out.push(`- ${c.repos} repos, ${c.cwds} working dirs`);
|
|
91
|
+
out.push(`- last analyzed: ${c.lastAnalyzedAt ?? "unknown"}`);
|
|
92
|
+
if (c.roots.length) {
|
|
93
|
+
out.push("- analyzed directories:");
|
|
94
|
+
for (const r of c.roots) {
|
|
95
|
+
out.push(` ${r.path} (${r.source ?? "?"}) \u2014 ${r.lastAnalyzedAt?.slice(0, 10) ?? "unknown"}`);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
out.push("");
|
|
99
|
+
}
|
|
100
|
+
out.push("## Tables", "");
|
|
101
|
+
for (const t of dump.tables) out.push(`${t.sql.trim()};`, "");
|
|
102
|
+
out.push("## Facets (chartable/filterable dimensions)", "");
|
|
103
|
+
for (const f of dump.facets) {
|
|
104
|
+
out.push(`- ${f.key} (${f.type}, ${f.source}${f.column && f.column !== f.key ? ` \u2192 ${f.column}` : ""}${f.base ? `, base: ${f.base}` : ""})`);
|
|
105
|
+
}
|
|
106
|
+
out.push("", "## Measures (aggregations)", "");
|
|
107
|
+
for (const m of dump.measures) {
|
|
108
|
+
out.push(`- ${m.key} = ${m.agg}(${m.expr}) [${m.source}]`);
|
|
109
|
+
}
|
|
110
|
+
out.push("");
|
|
111
|
+
return `${out.join("\n")}
|
|
112
|
+
`;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// src/cli.ts
|
|
116
|
+
var { version } = JSON.parse(
|
|
117
|
+
readFileSync(new URL("../package.json", import.meta.url), "utf8")
|
|
118
|
+
);
|
|
119
|
+
var program = new Command();
|
|
120
|
+
program.name("tuneloop").description("Local analytics for your AI coding sessions. Count outcomes, not tokens.").version(version);
|
|
121
|
+
var appendValue = (val, acc) => (acc.push(val), acc);
|
|
122
|
+
program.command("analyze").description("Analyze session transcripts, build the local store, then serve the dashboard.").argument("[dirs]", "comma-separated session directories (default: each harness's own location)").option(
|
|
123
|
+
"--source <name>",
|
|
124
|
+
"limit to these harnesses (repeatable). NAME or NAME=DIR to override its roots, e.g. --source codex=/path",
|
|
125
|
+
appendValue,
|
|
126
|
+
[]
|
|
127
|
+
).option("--db <path>", "path to the tuneloop SQLite store").option("--limit <n>", "process at most N sessions (handy for a cheap enrichment test)", (v) => parseInt(v, 10)).option("--port <n>", "dashboard port when serving (default 4319)", (v) => parseInt(v, 10)).option("--llm-provider <name>", "enrichment provider preset (anthropic, openai, openrouter, groq, deepseek, gemini, ollama, \u2026); overrides env").option("--llm-model <id>", "enrichment model id; overrides env").option("--llm-base-url <url>", "OpenAI-compatible endpoint URL (for openai-compatible / custom hosts); overrides env").option("--no-serve", "analyze only; do not serve the dashboard").option("-v, --verbose", "verbose logging").action(
|
|
128
|
+
async (dirs, options) => {
|
|
129
|
+
const dirList = dirs ? dirs.split(",").map((s) => s.trim()).filter(Boolean) : void 0;
|
|
130
|
+
await analyze({
|
|
131
|
+
dirs: dirList,
|
|
132
|
+
sources: options.source,
|
|
133
|
+
db: options.db,
|
|
134
|
+
limit: options.limit,
|
|
135
|
+
verbose: options.verbose,
|
|
136
|
+
llm: { provider: options.llmProvider, model: options.llmModel, baseURL: options.llmBaseUrl }
|
|
137
|
+
});
|
|
138
|
+
if (options.serve !== false) {
|
|
139
|
+
await serve({ db: options.db, port: options.port });
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
);
|
|
143
|
+
program.command("serve").description("Serve the local dashboard over the analyzed store.").option("--db <path>", "path to the tuneloop SQLite store").option("--port <n>", "port to listen on (default 4319)", (v) => parseInt(v, 10)).option("--no-open", "serve headless; do not prompt to open the browser").action(async (options) => {
|
|
144
|
+
await serve({ db: options.db, port: options.port, open: options.open });
|
|
145
|
+
});
|
|
146
|
+
program.command("query").description("Run a read-only SQL query (SELECT only) over the local store. --schema dumps the DDL + facets/measures for agents.").argument("[sql]", "SQL SELECT to run; omit when using --schema").option("--db <path>", "path to the tuneloop SQLite store").option("--schema", "print the store schema (tables + facets + measures) instead of running a query").option("--json", "output JSON instead of a text table").option("--limit <n>", "max rows to return (default 1000)", (v) => parseInt(v, 10)).action(async (sql, options) => {
|
|
147
|
+
await queryCommand(sql, options);
|
|
148
|
+
});
|
|
149
|
+
program.parseAsync(process.argv).catch((err) => {
|
|
150
|
+
process.stderr.write(`error: ${err.message}
|
|
151
|
+
`);
|
|
152
|
+
process.exitCode = 1;
|
|
153
|
+
});
|
|
154
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/cli.ts","../src/commands/query.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { Command } from 'commander'\nimport { readFileSync } from 'node:fs'\nimport './register'\nimport { analyze } from './commands/analyze'\nimport { serve } from './commands/serve'\nimport { queryCommand } from './commands/query'\n\n// Read once from package.json so the CLI version never drifts from the package.\n// Resolves the same in dev (src/), in the bundle (dist/), and when installed\n// (npm always ships package.json alongside dist/).\nconst { version } = JSON.parse(\n readFileSync(new URL('../package.json', import.meta.url), 'utf8'),\n) as { version: string }\n\nconst program = new Command()\n\nprogram\n .name('tuneloop')\n .description('Local analytics for your AI coding sessions. Count outcomes, not tokens.')\n .version(version)\n\nconst appendValue = (val: string, acc: string[]): string[] => (acc.push(val), acc)\n\nprogram\n .command('analyze')\n .description('Analyze session transcripts, build the local store, then serve the dashboard.')\n .argument('[dirs]', \"comma-separated session directories (default: each harness's own location)\")\n .option(\n '--source <name>',\n 'limit to these harnesses (repeatable). NAME or NAME=DIR to override its roots, e.g. --source codex=/path',\n appendValue,\n [],\n )\n .option('--db <path>', 'path to the tuneloop SQLite store')\n .option('--limit <n>', 'process at most N sessions (handy for a cheap enrichment test)', (v) => parseInt(v, 10))\n .option('--port <n>', 'dashboard port when serving (default 4319)', (v) => parseInt(v, 10))\n .option('--llm-provider <name>', 'enrichment provider preset (anthropic, openai, openrouter, groq, deepseek, gemini, ollama, …); overrides env')\n .option('--llm-model <id>', 'enrichment model id; overrides env')\n .option('--llm-base-url <url>', 'OpenAI-compatible endpoint URL (for openai-compatible / custom hosts); overrides env')\n .option('--no-serve', 'analyze only; do not serve the dashboard')\n .option('-v, --verbose', 'verbose logging')\n .action(\n async (\n dirs: string | undefined,\n options: {\n source: string[]\n db?: string\n limit?: number\n port?: number\n serve?: boolean\n verbose?: boolean\n llmProvider?: string\n llmModel?: string\n llmBaseUrl?: string\n },\n ) => {\n const dirList = dirs\n ? dirs.split(',').map((s) => s.trim()).filter(Boolean)\n : undefined\n await analyze({\n dirs: dirList,\n sources: options.source,\n db: options.db,\n limit: options.limit,\n verbose: options.verbose,\n llm: { provider: options.llmProvider, model: options.llmModel, baseURL: options.llmBaseUrl },\n })\n // Serve the dashboard by default and print its URL (press Enter to open a\n // browser tab); --no-serve opts out.\n if (options.serve !== false) {\n await serve({ db: options.db, port: options.port })\n }\n },\n )\n\nprogram\n .command('serve')\n .description('Serve the local dashboard over the analyzed store.')\n .option('--db <path>', 'path to the tuneloop SQLite store')\n .option('--port <n>', 'port to listen on (default 4319)', (v) => parseInt(v, 10))\n .option('--no-open', 'serve headless; do not prompt to open the browser')\n .action(async (options: { db?: string; port?: number; open?: boolean }) => {\n await serve({ db: options.db, port: options.port, open: options.open })\n })\n\nprogram\n .command('query')\n .description('Run a read-only SQL query (SELECT only) over the local store. --schema dumps the DDL + facets/measures for agents.')\n .argument('[sql]', 'SQL SELECT to run; omit when using --schema')\n .option('--db <path>', 'path to the tuneloop SQLite store')\n .option('--schema', 'print the store schema (tables + facets + measures) instead of running a query')\n .option('--json', 'output JSON instead of a text table')\n .option('--limit <n>', 'max rows to return (default 1000)', (v) => parseInt(v, 10))\n .action(async (sql: string | undefined, options: { db?: string; schema?: boolean; json?: boolean; limit?: number }) => {\n await queryCommand(sql, options)\n })\n\nprogram.parseAsync(process.argv).catch((err) => {\n process.stderr.write(`error: ${(err as Error).message}\\n`)\n process.exitCode = 1\n})\n","import { existsSync } from 'node:fs'\nimport { loadConfig } from '../config'\nimport { describeSchema, QueryError, runQuery, type SchemaDump } from '../query/run'\n\nexport interface QueryCliOptions {\n db?: string\n schema?: boolean\n json?: boolean\n limit?: number\n}\n\n/**\n * `tuneloop query \"<SQL>\"` — read-only SQL over the local store, for the analyses\n * the dashboard doesn't cover. `--schema` dumps the DDL + facet/measure registries\n * so an agent can learn the shape before querying. Zero new deps; works without the\n * server, which makes it the natural fit for Claude Code's bash tool.\n */\nexport async function queryCommand(sql: string | undefined, opts: QueryCliOptions): Promise<void> {\n const { dbPath } = loadConfig({ db: opts.db })\n if (!existsSync(dbPath)) {\n process.stderr.write(`error: no store at ${dbPath} — run \\`tuneloop analyze\\` first\\n`)\n process.exitCode = 1\n return\n }\n\n if (opts.schema) {\n const dump = describeSchema(dbPath)\n process.stdout.write(opts.json ? `${JSON.stringify(dump, null, 2)}\\n` : formatSchema(dump))\n return\n }\n\n if (!sql || !sql.trim()) {\n process.stderr.write('error: provide a SQL query, or --schema to see the store shape\\n')\n process.exitCode = 1\n return\n }\n\n try {\n const res = runQuery(dbPath, sql, { maxRows: opts.limit })\n if (opts.json) {\n process.stdout.write(`${JSON.stringify(res.rows, null, 2)}\\n`)\n } else {\n process.stdout.write(formatTable(res.columns, res.rows))\n }\n if (res.truncated) {\n const why =\n res.truncated === 'rows'\n ? `row cap reached — pass --limit <n> for more`\n : res.truncated === 'bytes'\n ? `response size cap reached`\n : `time cap reached`\n process.stderr.write(`\\n(truncated: ${why}; ${res.rowCount} rows shown)\\n`)\n }\n } catch (err) {\n if (err instanceof QueryError) {\n process.stderr.write(`error: ${err.message}\\n`)\n process.exitCode = 1\n return\n }\n // SQLite's own errors (syntax, unknown column) — surface the message, not a stack.\n process.stderr.write(`error: ${(err as Error).message}\\n`)\n process.exitCode = 1\n }\n}\n\nconst MAX_CELL = 60\n\n/** One value → a single-line cell, truncated so a wide column can't blow up the table. */\nfunction cell(value: unknown): string {\n if (value === null || value === undefined) return 'NULL'\n if (Buffer.isBuffer(value)) return `<blob ${value.length}b>`\n const s = String(value).replace(/\\s+/g, ' ')\n return s.length > MAX_CELL ? `${s.slice(0, MAX_CELL - 1)}…` : s\n}\n\n/** Minimal column-aligned table. Empty result still prints the header row. */\nfunction formatTable(columns: string[], rows: Record<string, unknown>[]): string {\n if (columns.length === 0) return '(no columns)\\n'\n const cells = rows.map((r) => columns.map((c) => cell(r[c])))\n const widths = columns.map((c, i) => Math.max(c.length, ...cells.map((row) => (row[i] ?? '').length), 0))\n const line = (vals: string[]) => vals.map((v, i) => v.padEnd(widths[i] ?? 0)).join(' ').trimEnd()\n const out: string[] = [line(columns), widths.map((w) => '─'.repeat(w)).join(' ')]\n for (const row of cells) out.push(line(row))\n if (rows.length === 0) out.push('(0 rows)')\n return `${out.join('\\n')}\\n`\n}\n\n/** Human-readable schema dump: coverage, then DDL, then facet and measure registries. */\nfunction formatSchema(dump: SchemaDump): string {\n const out: string[] = []\n out.push(`# tuneloop store (schema v${dump.schemaVersion ?? '?'})`, '')\n const c = dump.coverage\n if (c) {\n const span = c.firstAt && c.lastAt ? `${c.firstAt.slice(0, 10)} → ${c.lastAt.slice(0, 10)}` : 'no dated sessions'\n const sources = c.sources.map((s) => `${s.source ?? '(none)'} ${s.count}`).join(', ') || '(none)'\n out.push('## Coverage', '')\n out.push(`- ${c.sessions} sessions, ${span}`)\n out.push(`- sources: ${sources}`)\n out.push(`- ${c.repos} repos, ${c.cwds} working dirs`)\n out.push(`- last analyzed: ${c.lastAnalyzedAt ?? 'unknown'}`)\n if (c.roots.length) {\n out.push('- analyzed directories:')\n for (const r of c.roots) {\n out.push(` ${r.path} (${r.source ?? '?'}) — ${r.lastAnalyzedAt?.slice(0, 10) ?? 'unknown'}`)\n }\n }\n out.push('')\n }\n out.push('## Tables', '')\n for (const t of dump.tables) out.push(`${t.sql.trim()};`, '')\n out.push('## Facets (chartable/filterable dimensions)', '')\n for (const f of dump.facets) {\n out.push(`- ${f.key} (${f.type}, ${f.source}${f.column && f.column !== f.key ? ` → ${f.column}` : ''}${f.base ? `, base: ${f.base}` : ''})`)\n }\n out.push('', '## Measures (aggregations)', '')\n for (const m of dump.measures) {\n out.push(`- ${m.key} = ${m.agg}(${m.expr}) [${m.source}]`)\n }\n out.push('')\n return `${out.join('\\n')}\\n`\n}\n"],"mappings":"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","names":[]}
|