@c3-oss/prosa 0.3.0 → 0.3.2
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/dist/bin/prosa.js +199 -153
- package/dist/bin/prosa.js.map +1 -1
- package/dist/cli/main.js +199 -153
- package/dist/cli/main.js.map +1 -1
- package/dist/index.d.ts +8 -2
- package/dist/index.js +34 -15
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/bin/prosa.js
CHANGED
|
@@ -12,8 +12,8 @@ var __export = (target, all) => {
|
|
|
12
12
|
|
|
13
13
|
// src/core/db.ts
|
|
14
14
|
import Database from "better-sqlite3";
|
|
15
|
-
function openDb(
|
|
16
|
-
const db = new Database(
|
|
15
|
+
function openDb(path20) {
|
|
16
|
+
const db = new Database(path20);
|
|
17
17
|
db.pragma("journal_mode = WAL");
|
|
18
18
|
db.pragma("foreign_keys = ON");
|
|
19
19
|
db.pragma("synchronous = NORMAL");
|
|
@@ -48,6 +48,15 @@ var init_db = __esm({
|
|
|
48
48
|
}
|
|
49
49
|
});
|
|
50
50
|
|
|
51
|
+
// src/core/errors.ts
|
|
52
|
+
var getErrorMessage;
|
|
53
|
+
var init_errors = __esm({
|
|
54
|
+
"src/core/errors.ts"() {
|
|
55
|
+
"use strict";
|
|
56
|
+
getErrorMessage = (err) => err instanceof Error ? err.message : String(err);
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
|
|
51
60
|
// src/core/cas/compress.ts
|
|
52
61
|
import { compress as zstdCompress, decompress as zstdDecompress } from "zstd-napi";
|
|
53
62
|
function compressBytes(input) {
|
|
@@ -393,7 +402,7 @@ function rebuildFts5Index(bundle) {
|
|
|
393
402
|
status: "failed",
|
|
394
403
|
sourceDocCount: countSearchDocs(bundle),
|
|
395
404
|
indexedDocCount: countFts5Docs(bundle),
|
|
396
|
-
errorMessage:
|
|
405
|
+
errorMessage: getErrorMessage(error)
|
|
397
406
|
});
|
|
398
407
|
throw error;
|
|
399
408
|
}
|
|
@@ -466,7 +475,7 @@ async function rebuildTantivyIndex(bundle) {
|
|
|
466
475
|
status: "failed",
|
|
467
476
|
sourceDocCount: countSearchDocs(bundle),
|
|
468
477
|
indexedDocCount: 0,
|
|
469
|
-
errorMessage:
|
|
478
|
+
errorMessage: getErrorMessage(error)
|
|
470
479
|
});
|
|
471
480
|
throw error;
|
|
472
481
|
}
|
|
@@ -514,6 +523,7 @@ var init_indexing = __esm({
|
|
|
514
523
|
"src/services/indexing.ts"() {
|
|
515
524
|
"use strict";
|
|
516
525
|
init_db();
|
|
526
|
+
init_errors();
|
|
517
527
|
FTS5_TRIGGER_SQL = `
|
|
518
528
|
CREATE TRIGGER IF NOT EXISTS search_docs_ai AFTER INSERT ON search_docs BEGIN
|
|
519
529
|
INSERT INTO search_docs_fts(rowid, text, role, tool_name, field_kind)
|
|
@@ -535,6 +545,16 @@ END;
|
|
|
535
545
|
}
|
|
536
546
|
});
|
|
537
547
|
|
|
548
|
+
// src/core/limits.ts
|
|
549
|
+
function clampLimit(value, opts) {
|
|
550
|
+
return Math.max(opts.min ?? 1, Math.min(opts.max, value ?? opts.fallback));
|
|
551
|
+
}
|
|
552
|
+
var init_limits = __esm({
|
|
553
|
+
"src/core/limits.ts"() {
|
|
554
|
+
"use strict";
|
|
555
|
+
}
|
|
556
|
+
});
|
|
557
|
+
|
|
538
558
|
// src/services/search.ts
|
|
539
559
|
import { existsSync } from "fs";
|
|
540
560
|
import { createRequire } from "module";
|
|
@@ -545,7 +565,7 @@ function searchFullText(bundle, options) {
|
|
|
545
565
|
if (options.engine === "tantivy") {
|
|
546
566
|
return searchTantivy(bundle, options);
|
|
547
567
|
}
|
|
548
|
-
const limit =
|
|
568
|
+
const limit = clampLimit(options.limit, { max: 500, fallback: 50 });
|
|
549
569
|
const sql = `
|
|
550
570
|
SELECT d.doc_id,
|
|
551
571
|
d.entity_type,
|
|
@@ -576,7 +596,7 @@ function searchTantivy(bundle, options) {
|
|
|
576
596
|
`tantivy index is ${status?.status ?? "missing"}; run \`prosa index tantivy\` first`
|
|
577
597
|
);
|
|
578
598
|
}
|
|
579
|
-
const limit =
|
|
599
|
+
const limit = clampLimit(options.limit, { max: 500, fallback: 50 });
|
|
580
600
|
const queryText = options.query.trim();
|
|
581
601
|
if (!queryText) return [];
|
|
582
602
|
const tantivy = requireTantivy();
|
|
@@ -610,9 +630,7 @@ function requireTantivy() {
|
|
|
610
630
|
try {
|
|
611
631
|
return require2("@oxdev03/node-tantivy-binding");
|
|
612
632
|
} catch (error) {
|
|
613
|
-
throw new Error(
|
|
614
|
-
`tantivy engine is unavailable: ${error instanceof Error ? error.message : String(error)}`
|
|
615
|
-
);
|
|
633
|
+
throw new Error(`tantivy engine is unavailable: ${getErrorMessage(error)}`);
|
|
616
634
|
}
|
|
617
635
|
}
|
|
618
636
|
function getStoredText(doc, field) {
|
|
@@ -641,6 +659,8 @@ var require2;
|
|
|
641
659
|
var init_search = __esm({
|
|
642
660
|
"src/services/search.ts"() {
|
|
643
661
|
"use strict";
|
|
662
|
+
init_errors();
|
|
663
|
+
init_limits();
|
|
644
664
|
init_indexing();
|
|
645
665
|
require2 = createRequire(import.meta.url);
|
|
646
666
|
}
|
|
@@ -669,7 +689,7 @@ function sessionFilterWhere(filters) {
|
|
|
669
689
|
}
|
|
670
690
|
function listSessions(bundle, filters = {}) {
|
|
671
691
|
const { where, params } = sessionFilterWhere(filters);
|
|
672
|
-
const limit =
|
|
692
|
+
const limit = clampLimit(filters.limit, { max: 1e3, fallback: 50 });
|
|
673
693
|
const sql = `
|
|
674
694
|
SELECT s.session_id,
|
|
675
695
|
s.source_tool,
|
|
@@ -743,6 +763,7 @@ function getSession(bundle, sessionId2) {
|
|
|
743
763
|
var init_sessions = __esm({
|
|
744
764
|
"src/services/sessions.ts"() {
|
|
745
765
|
"use strict";
|
|
766
|
+
init_limits();
|
|
746
767
|
}
|
|
747
768
|
});
|
|
748
769
|
|
|
@@ -1621,11 +1642,24 @@ async function openBundle(rootPath) {
|
|
|
1621
1642
|
}
|
|
1622
1643
|
return { path: resolved, db, manifest, paths };
|
|
1623
1644
|
}
|
|
1645
|
+
async function openOrInitBundle(rootPath) {
|
|
1646
|
+
const resolved = path.resolve(rootPath);
|
|
1647
|
+
const paths = bundlePaths(resolved);
|
|
1648
|
+
const dirStat = await stat(resolved).catch(() => null);
|
|
1649
|
+
if (dirStat && !dirStat.isDirectory()) {
|
|
1650
|
+
throw new Error(`bundle path not found or not a directory: ${resolved}`);
|
|
1651
|
+
}
|
|
1652
|
+
if (!dirStat || !await exists(paths.manifest)) {
|
|
1653
|
+
return await initBundle(resolved);
|
|
1654
|
+
}
|
|
1655
|
+
return await openBundle(resolved);
|
|
1656
|
+
}
|
|
1624
1657
|
function closeBundle(bundle) {
|
|
1625
1658
|
closeDb(bundle.db);
|
|
1626
1659
|
}
|
|
1627
1660
|
|
|
1628
1661
|
// src/services/compile.ts
|
|
1662
|
+
init_errors();
|
|
1629
1663
|
import os2 from "os";
|
|
1630
1664
|
import path14 from "path";
|
|
1631
1665
|
|
|
@@ -1678,6 +1712,9 @@ function importBatchId(sourceTool, startedAtIso) {
|
|
|
1678
1712
|
return tupleId(["import_batch", sourceTool, startedAtIso, String(Math.random())]);
|
|
1679
1713
|
}
|
|
1680
1714
|
|
|
1715
|
+
// src/importers/claude/index.ts
|
|
1716
|
+
init_errors();
|
|
1717
|
+
|
|
1681
1718
|
// src/core/ingest/batch.ts
|
|
1682
1719
|
init_cas();
|
|
1683
1720
|
init_db();
|
|
@@ -1967,7 +2004,7 @@ async function compileClaude(bundle, root, options = {}) {
|
|
|
1967
2004
|
);
|
|
1968
2005
|
await recordError(bundle, batch.batch_id, {
|
|
1969
2006
|
kind: "claude_file_failed",
|
|
1970
|
-
message:
|
|
2007
|
+
message: getErrorMessage(error),
|
|
1971
2008
|
payload: { path: file.filePath }
|
|
1972
2009
|
});
|
|
1973
2010
|
}
|
|
@@ -2882,6 +2919,7 @@ init_cas();
|
|
|
2882
2919
|
init_db();
|
|
2883
2920
|
import { readFile as readFile5 } from "fs/promises";
|
|
2884
2921
|
import path7 from "path";
|
|
2922
|
+
init_errors();
|
|
2885
2923
|
|
|
2886
2924
|
// src/importers/codex/discover.ts
|
|
2887
2925
|
import { readdir as readdir2 } from "fs/promises";
|
|
@@ -2931,7 +2969,7 @@ async function compileCodex(bundle, root, options = {}) {
|
|
|
2931
2969
|
);
|
|
2932
2970
|
await recordError(bundle, batch.batch_id, {
|
|
2933
2971
|
kind: "codex_file_failed",
|
|
2934
|
-
message:
|
|
2972
|
+
message: getErrorMessage(error),
|
|
2935
2973
|
payload: { path: filePath }
|
|
2936
2974
|
});
|
|
2937
2975
|
}
|
|
@@ -4022,6 +4060,7 @@ init_cas();
|
|
|
4022
4060
|
init_db();
|
|
4023
4061
|
import path9 from "path";
|
|
4024
4062
|
import Database2 from "better-sqlite3";
|
|
4063
|
+
init_errors();
|
|
4025
4064
|
|
|
4026
4065
|
// src/importers/cursor/discover.ts
|
|
4027
4066
|
import { readdir as readdir3 } from "fs/promises";
|
|
@@ -4086,7 +4125,7 @@ async function compileCursor(bundle, root, options = {}) {
|
|
|
4086
4125
|
);
|
|
4087
4126
|
await recordError(bundle, batch.batch_id, {
|
|
4088
4127
|
kind: "cursor_store_failed",
|
|
4089
|
-
message:
|
|
4128
|
+
message: getErrorMessage(error),
|
|
4090
4129
|
payload: { path: store.filePath }
|
|
4091
4130
|
});
|
|
4092
4131
|
}
|
|
@@ -4741,6 +4780,7 @@ init_cas();
|
|
|
4741
4780
|
init_db();
|
|
4742
4781
|
import { readFile as readFile7 } from "fs/promises";
|
|
4743
4782
|
import path11 from "path";
|
|
4783
|
+
init_errors();
|
|
4744
4784
|
|
|
4745
4785
|
// src/importers/gemini/discover.ts
|
|
4746
4786
|
import { readFile as readFile6, readdir as readdir4 } from "fs/promises";
|
|
@@ -4812,7 +4852,7 @@ async function compileGemini(bundle, root, options = {}) {
|
|
|
4812
4852
|
);
|
|
4813
4853
|
await recordError(bundle, batch.batch_id, {
|
|
4814
4854
|
kind: "gemini_file_failed",
|
|
4815
|
-
message:
|
|
4855
|
+
message: getErrorMessage(error),
|
|
4816
4856
|
payload: { path: file.filePath }
|
|
4817
4857
|
});
|
|
4818
4858
|
}
|
|
@@ -5498,6 +5538,7 @@ function flushPending4(bundle, pending) {
|
|
|
5498
5538
|
import { mkdir as mkdir3, rm, writeFile as writeFile4 } from "fs/promises";
|
|
5499
5539
|
import path12 from "path";
|
|
5500
5540
|
import { DuckDBConnection } from "@duckdb/node-api";
|
|
5541
|
+
init_errors();
|
|
5501
5542
|
var PARQUET_TABLES = [
|
|
5502
5543
|
"objects",
|
|
5503
5544
|
"source_files",
|
|
@@ -5595,7 +5636,7 @@ async function attachSqlite(connection, dbPath) {
|
|
|
5595
5636
|
await connection.run(`ATTACH ${sqlString(dbPath)} AS prosa (TYPE sqlite)`);
|
|
5596
5637
|
} catch (error) {
|
|
5597
5638
|
throw new Error(
|
|
5598
|
-
`DuckDB could not attach prosa.sqlite via the sqlite extension: ${
|
|
5639
|
+
`DuckDB could not attach prosa.sqlite via the sqlite extension: ${getErrorMessage(error)}`
|
|
5599
5640
|
);
|
|
5600
5641
|
}
|
|
5601
5642
|
}
|
|
@@ -5626,7 +5667,7 @@ function sqlString(value) {
|
|
|
5626
5667
|
return `'${value.replace(/'/g, "''")}'`;
|
|
5627
5668
|
}
|
|
5628
5669
|
function isMissingParquetError(error) {
|
|
5629
|
-
const message =
|
|
5670
|
+
const message = getErrorMessage(error);
|
|
5630
5671
|
return /No files found|does not exist|not found/i.test(message) && /\.parquet/i.test(message);
|
|
5631
5672
|
}
|
|
5632
5673
|
|
|
@@ -5723,7 +5764,7 @@ async function runCompileImports(options) {
|
|
|
5723
5764
|
tantivy = { indexedDocCount: status.indexed_doc_count };
|
|
5724
5765
|
options.onTantivyComplete?.(tantivy);
|
|
5725
5766
|
} catch (error) {
|
|
5726
|
-
tantivyError =
|
|
5767
|
+
tantivyError = getErrorMessage(error);
|
|
5727
5768
|
logger?.error({ err: error }, "tantivy rebuild failed; SQLite data is intact");
|
|
5728
5769
|
}
|
|
5729
5770
|
}
|
|
@@ -5759,9 +5800,9 @@ import pretty from "pino-pretty";
|
|
|
5759
5800
|
function createCliLogger(options) {
|
|
5760
5801
|
const loggerOptions = {
|
|
5761
5802
|
base: void 0,
|
|
5762
|
-
level: options.verbose
|
|
5803
|
+
level: options.verbose ? "debug" : "info"
|
|
5763
5804
|
};
|
|
5764
|
-
if (options.jsonLogs
|
|
5805
|
+
if (options.jsonLogs) {
|
|
5765
5806
|
return pino(loggerOptions, pino.destination({ dest: 2, sync: true }));
|
|
5766
5807
|
}
|
|
5767
5808
|
return pino(
|
|
@@ -5797,7 +5838,7 @@ function compileAllCommand() {
|
|
|
5797
5838
|
await runCompiles({
|
|
5798
5839
|
providers: COMPILE_PROVIDERS,
|
|
5799
5840
|
storePath: defaultBundlePath(),
|
|
5800
|
-
deferIndex: options.deferIndex
|
|
5841
|
+
deferIndex: options.deferIndex ?? false,
|
|
5801
5842
|
logOptions: options
|
|
5802
5843
|
});
|
|
5803
5844
|
});
|
|
@@ -5812,7 +5853,7 @@ function providerCompileCommand(provider) {
|
|
|
5812
5853
|
await runCompiles({
|
|
5813
5854
|
providers: [provider],
|
|
5814
5855
|
storePath: options.store,
|
|
5815
|
-
deferIndex: options.deferIndex
|
|
5856
|
+
deferIndex: options.deferIndex ?? false,
|
|
5816
5857
|
sessionsPath: options.sessionsPath,
|
|
5817
5858
|
logOptions: command.optsWithGlobals()
|
|
5818
5859
|
});
|
|
@@ -5870,7 +5911,7 @@ function printCounts(summary) {
|
|
|
5870
5911
|
|
|
5871
5912
|
// src/cli/commands/export.ts
|
|
5872
5913
|
import { writeFile as writeFile6 } from "fs/promises";
|
|
5873
|
-
import
|
|
5914
|
+
import path16 from "path";
|
|
5874
5915
|
import { Command as Command2 } from "commander";
|
|
5875
5916
|
|
|
5876
5917
|
// src/services/export/markdown.ts
|
|
@@ -5984,30 +6025,38 @@ function renderToolCall(c) {
|
|
|
5984
6025
|
return lines.join("\n");
|
|
5985
6026
|
}
|
|
5986
6027
|
|
|
6028
|
+
// src/cli/bundle.ts
|
|
6029
|
+
import path15 from "path";
|
|
6030
|
+
async function withBundle(storePath, fn) {
|
|
6031
|
+
const bundle = await openBundle(path15.resolve(storePath));
|
|
6032
|
+
try {
|
|
6033
|
+
return await fn(bundle);
|
|
6034
|
+
} finally {
|
|
6035
|
+
closeBundle(bundle);
|
|
6036
|
+
}
|
|
6037
|
+
}
|
|
6038
|
+
|
|
5987
6039
|
// src/cli/commands/export.ts
|
|
5988
6040
|
function exportCommand() {
|
|
5989
6041
|
const session = new Command2("session").description("Export a single session to a human-readable format.").argument("<session-id>", "prosa session_id").requiredOption("--format <fmt>", 'currently only "markdown" is supported').option("--out <path>", "write to file instead of stdout").option("--store <path>", "bundle directory", defaultBundlePath()).action(async (sessionId2, options) => {
|
|
5990
6042
|
if (options.format !== "markdown") {
|
|
5991
6043
|
throw new Error(`unsupported format: ${options.format} (try --format markdown)`);
|
|
5992
6044
|
}
|
|
5993
|
-
|
|
5994
|
-
try {
|
|
6045
|
+
await withBundle(options.store, async (bundle) => {
|
|
5995
6046
|
const markdown = await exportSessionMarkdown(bundle, sessionId2);
|
|
5996
6047
|
if (options.out) {
|
|
5997
|
-
await writeFile6(
|
|
5998
|
-
process.stdout.write(`wrote ${
|
|
6048
|
+
await writeFile6(path16.resolve(options.out), markdown, "utf8");
|
|
6049
|
+
process.stdout.write(`wrote ${path16.resolve(options.out)}
|
|
5999
6050
|
`);
|
|
6000
6051
|
} else {
|
|
6001
6052
|
process.stdout.write(markdown);
|
|
6002
6053
|
}
|
|
6003
|
-
}
|
|
6004
|
-
closeBundle(bundle);
|
|
6005
|
-
}
|
|
6054
|
+
});
|
|
6006
6055
|
});
|
|
6007
6056
|
const parquet = new Command2("parquet").description("Export canonical tables to derived Parquet files for analytics.").option("--store <path>", "bundle directory", defaultBundlePath()).option("--out <path>", "output directory (default: <store>/parquet)").action(async (options) => {
|
|
6008
6057
|
const result = await exportBundleParquet({
|
|
6009
|
-
bundlePath:
|
|
6010
|
-
outDir: options.out ?
|
|
6058
|
+
bundlePath: path16.resolve(options.store),
|
|
6059
|
+
outDir: options.out ? path16.resolve(options.out) : void 0
|
|
6011
6060
|
});
|
|
6012
6061
|
process.stdout.write(`wrote parquet export to ${result.outDir}
|
|
6013
6062
|
`);
|
|
@@ -6018,12 +6067,13 @@ function exportCommand() {
|
|
|
6018
6067
|
}
|
|
6019
6068
|
|
|
6020
6069
|
// src/cli/commands/index.ts
|
|
6021
|
-
import path16 from "path";
|
|
6022
6070
|
import { Command as Command3 } from "commander";
|
|
6023
6071
|
init_indexing();
|
|
6024
6072
|
|
|
6025
6073
|
// src/cli/output.ts
|
|
6026
6074
|
var OUTPUT_FORMATS = ["interactive", "table", "json", "csv"];
|
|
6075
|
+
var COL_SEPARATOR = " ";
|
|
6076
|
+
var RULE_CHAR = "-";
|
|
6027
6077
|
function parseOutputFormat(value, fallback) {
|
|
6028
6078
|
if (value === void 0) return fallback;
|
|
6029
6079
|
if (OUTPUT_FORMATS.includes(value)) return value;
|
|
@@ -6051,11 +6101,12 @@ function printJson(rows, opts) {
|
|
|
6051
6101
|
`);
|
|
6052
6102
|
}
|
|
6053
6103
|
function printCsv(rows, opts) {
|
|
6054
|
-
const
|
|
6055
|
-
process.stdout.write(`${
|
|
6104
|
+
const columns = opts.columns;
|
|
6105
|
+
process.stdout.write(`${columns.map(csvField).join(",")}
|
|
6056
6106
|
`);
|
|
6057
6107
|
for (const row of rows) {
|
|
6058
|
-
const
|
|
6108
|
+
const record = row;
|
|
6109
|
+
const line = columns.map((column) => csvField(formatCell(record[column]))).join(",");
|
|
6059
6110
|
process.stdout.write(`${line}
|
|
6060
6111
|
`);
|
|
6061
6112
|
}
|
|
@@ -6065,23 +6116,24 @@ function csvField(value) {
|
|
|
6065
6116
|
return value;
|
|
6066
6117
|
}
|
|
6067
6118
|
function printTable(rows, opts) {
|
|
6068
|
-
const
|
|
6069
|
-
const widths =
|
|
6070
|
-
const cells = rows.map(
|
|
6071
|
-
|
|
6072
|
-
|
|
6073
|
-
const
|
|
6074
|
-
|
|
6119
|
+
const columns = opts.columns;
|
|
6120
|
+
const widths = columns.map((column) => column.length);
|
|
6121
|
+
const cells = rows.map((row) => {
|
|
6122
|
+
const record = row;
|
|
6123
|
+
return columns.map((column, index) => {
|
|
6124
|
+
const text = formatCell(record[column]);
|
|
6125
|
+
const width = widths[index] ?? 0;
|
|
6126
|
+
if (text.length > width) widths[index] = text.length;
|
|
6075
6127
|
return text;
|
|
6076
|
-
})
|
|
6077
|
-
);
|
|
6078
|
-
const header =
|
|
6079
|
-
const
|
|
6128
|
+
});
|
|
6129
|
+
});
|
|
6130
|
+
const header = columns.map((column, index) => column.padEnd(widths[index] ?? 0)).join(COL_SEPARATOR);
|
|
6131
|
+
const rule = columns.map((_, index) => RULE_CHAR.repeat(widths[index] ?? 0)).join(COL_SEPARATOR);
|
|
6080
6132
|
process.stdout.write(`${header}
|
|
6081
|
-
${
|
|
6133
|
+
${rule}
|
|
6082
6134
|
`);
|
|
6083
6135
|
for (const cellRow of cells) {
|
|
6084
|
-
const line = cellRow.map((
|
|
6136
|
+
const line = cellRow.map((cell, index) => cell.padEnd(widths[index] ?? 0)).join(COL_SEPARATOR);
|
|
6085
6137
|
process.stdout.write(`${line}
|
|
6086
6138
|
`);
|
|
6087
6139
|
}
|
|
@@ -6096,27 +6148,18 @@ function formatCell(value) {
|
|
|
6096
6148
|
// src/cli/commands/index.ts
|
|
6097
6149
|
function indexCommand() {
|
|
6098
6150
|
const fts5 = new Command3("fts5").description("Rebuild the SQLite FTS5 index from search_docs.").option("--store <path>", "bundle directory", defaultBundlePath()).action(async (options) => {
|
|
6099
|
-
|
|
6100
|
-
|
|
6101
|
-
|
|
6102
|
-
printIndexStatus(status2);
|
|
6103
|
-
} finally {
|
|
6104
|
-
closeBundle(bundle);
|
|
6105
|
-
}
|
|
6151
|
+
await withBundle(options.store, (bundle) => {
|
|
6152
|
+
printIndexStatus(rebuildFts5Index(bundle));
|
|
6153
|
+
});
|
|
6106
6154
|
});
|
|
6107
6155
|
const tantivy = new Command3("tantivy").description("Rebuild the Tantivy sidecar index from search_docs.").option("--store <path>", "bundle directory", defaultBundlePath()).action(async (options) => {
|
|
6108
|
-
|
|
6109
|
-
|
|
6110
|
-
|
|
6111
|
-
printIndexStatus(status2);
|
|
6112
|
-
} finally {
|
|
6113
|
-
closeBundle(bundle);
|
|
6114
|
-
}
|
|
6156
|
+
await withBundle(options.store, async (bundle) => {
|
|
6157
|
+
printIndexStatus(await rebuildTantivyIndex(bundle));
|
|
6158
|
+
});
|
|
6115
6159
|
});
|
|
6116
6160
|
const status = new Command3("status").description("Show derived search index status.").option("--store <path>", "bundle directory", defaultBundlePath()).option("--output-format <fmt>", "interactive|table|json|csv", "table").action(async (options) => {
|
|
6117
6161
|
const format = parseOutputFormat(options.outputFormat, "table");
|
|
6118
|
-
|
|
6119
|
-
try {
|
|
6162
|
+
await withBundle(options.store, (bundle) => {
|
|
6120
6163
|
const rows = getSearchIndexStatuses(bundle);
|
|
6121
6164
|
printRows(rows, {
|
|
6122
6165
|
format,
|
|
@@ -6129,9 +6172,7 @@ function indexCommand() {
|
|
|
6129
6172
|
"error_message"
|
|
6130
6173
|
]
|
|
6131
6174
|
});
|
|
6132
|
-
}
|
|
6133
|
-
closeBundle(bundle);
|
|
6134
|
-
}
|
|
6175
|
+
});
|
|
6135
6176
|
});
|
|
6136
6177
|
return new Command3("index").description("Build or inspect derived search indexes.").addCommand(fts5).addCommand(tantivy).addCommand(status);
|
|
6137
6178
|
}
|
|
@@ -6178,6 +6219,7 @@ import path18 from "path";
|
|
|
6178
6219
|
import { Command as Command5 } from "commander";
|
|
6179
6220
|
|
|
6180
6221
|
// src/mcp/server.ts
|
|
6222
|
+
init_errors();
|
|
6181
6223
|
import { randomUUID } from "crypto";
|
|
6182
6224
|
import http from "http";
|
|
6183
6225
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
@@ -6237,12 +6279,20 @@ Use this workflow:
|
|
|
6237
6279
|
|
|
6238
6280
|
// src/mcp/tools.ts
|
|
6239
6281
|
import { z } from "zod";
|
|
6282
|
+
|
|
6283
|
+
// src/core/domain/types.ts
|
|
6284
|
+
var SOURCE_TOOLS = ["cursor", "codex", "claude", "gemini"];
|
|
6285
|
+
|
|
6286
|
+
// src/mcp/tools.ts
|
|
6287
|
+
init_errors();
|
|
6288
|
+
init_limits();
|
|
6240
6289
|
init_indexing();
|
|
6241
6290
|
init_search();
|
|
6242
6291
|
init_sessions();
|
|
6243
6292
|
function registerProsaTools(server, bundle, options = {}) {
|
|
6244
6293
|
const searchEngine = options.searchEngine ?? "fts5";
|
|
6245
6294
|
const storePath = options.storePath ?? bundle.path;
|
|
6295
|
+
const ensureStore = options.ensureStore ?? false;
|
|
6246
6296
|
registerProsaPrompts(server);
|
|
6247
6297
|
server.registerTool(
|
|
6248
6298
|
"compile",
|
|
@@ -6250,12 +6300,12 @@ function registerProsaTools(server, bundle, options = {}) {
|
|
|
6250
6300
|
title: "Compile sessions",
|
|
6251
6301
|
description: "Import local agent session histories into the active prosa bundle. With no input, compiles all providers from default paths. With source, compiles that provider; sessions_path may override that provider path.",
|
|
6252
6302
|
inputSchema: {
|
|
6253
|
-
source: z.enum(
|
|
6303
|
+
source: z.enum(SOURCE_TOOLS).optional(),
|
|
6254
6304
|
sessions_path: z.string().min(1).optional()
|
|
6255
6305
|
},
|
|
6256
6306
|
annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true }
|
|
6257
6307
|
},
|
|
6258
|
-
async ({ source, sessions_path }) => {
|
|
6308
|
+
async ({ source, sessions_path }) => withToolBundle(bundle, storePath, ensureStore, async (activeBundle) => {
|
|
6259
6309
|
if (sessions_path && !source) {
|
|
6260
6310
|
return {
|
|
6261
6311
|
content: [
|
|
@@ -6269,7 +6319,7 @@ function registerProsaTools(server, bundle, options = {}) {
|
|
|
6269
6319
|
}
|
|
6270
6320
|
try {
|
|
6271
6321
|
const result = await runCompileImports({
|
|
6272
|
-
bundle,
|
|
6322
|
+
bundle: activeBundle,
|
|
6273
6323
|
providers: source ? [getCompileProvider(source)] : COMPILE_PROVIDERS,
|
|
6274
6324
|
deferIndex: false,
|
|
6275
6325
|
sessionsPath: sessions_path
|
|
@@ -6306,11 +6356,11 @@ function registerProsaTools(server, bundle, options = {}) {
|
|
|
6306
6356
|
};
|
|
6307
6357
|
} catch (error) {
|
|
6308
6358
|
return {
|
|
6309
|
-
content: [{ type: "text", text:
|
|
6359
|
+
content: [{ type: "text", text: getErrorMessage(error) }],
|
|
6310
6360
|
isError: true
|
|
6311
6361
|
};
|
|
6312
6362
|
}
|
|
6313
|
-
}
|
|
6363
|
+
})
|
|
6314
6364
|
);
|
|
6315
6365
|
server.registerTool(
|
|
6316
6366
|
"list_sessions",
|
|
@@ -6318,15 +6368,15 @@ function registerProsaTools(server, bundle, options = {}) {
|
|
|
6318
6368
|
title: "List sessions",
|
|
6319
6369
|
description: "List recent sessions when you need candidates by source/date before deeper inspection. Next step: call get_session for relevant session_id values.",
|
|
6320
6370
|
inputSchema: {
|
|
6321
|
-
source: z.enum(
|
|
6371
|
+
source: z.enum(SOURCE_TOOLS).optional(),
|
|
6322
6372
|
since: z.string().optional().describe("ISO timestamp lower bound (inclusive)"),
|
|
6323
6373
|
until: z.string().optional().describe("ISO timestamp upper bound (exclusive)"),
|
|
6324
6374
|
limit: z.number().int().min(1).max(500).optional().default(50)
|
|
6325
6375
|
},
|
|
6326
6376
|
annotations: { readOnlyHint: true, idempotentHint: true }
|
|
6327
6377
|
},
|
|
6328
|
-
async (input) => {
|
|
6329
|
-
const rows = listSessions(
|
|
6378
|
+
async (input) => withToolBundle(bundle, storePath, ensureStore, (activeBundle) => {
|
|
6379
|
+
const rows = listSessions(activeBundle, {
|
|
6330
6380
|
sourceTool: input.source,
|
|
6331
6381
|
sinceIso: input.since,
|
|
6332
6382
|
untilIso: input.until,
|
|
@@ -6335,7 +6385,7 @@ function registerProsaTools(server, bundle, options = {}) {
|
|
|
6335
6385
|
return {
|
|
6336
6386
|
content: [{ type: "text", text: JSON.stringify(rows, null, 2) }]
|
|
6337
6387
|
};
|
|
6338
|
-
}
|
|
6388
|
+
})
|
|
6339
6389
|
);
|
|
6340
6390
|
server.registerTool(
|
|
6341
6391
|
"get_session",
|
|
@@ -6347,8 +6397,8 @@ function registerProsaTools(server, bundle, options = {}) {
|
|
|
6347
6397
|
},
|
|
6348
6398
|
annotations: { readOnlyHint: true, idempotentHint: true }
|
|
6349
6399
|
},
|
|
6350
|
-
async ({ session_id }) => {
|
|
6351
|
-
const detail = getSession(
|
|
6400
|
+
async ({ session_id }) => withToolBundle(bundle, storePath, ensureStore, (activeBundle) => {
|
|
6401
|
+
const detail = getSession(activeBundle, session_id);
|
|
6352
6402
|
if (!detail) {
|
|
6353
6403
|
return {
|
|
6354
6404
|
content: [{ type: "text", text: `session not found: ${session_id}` }],
|
|
@@ -6358,7 +6408,7 @@ function registerProsaTools(server, bundle, options = {}) {
|
|
|
6358
6408
|
return {
|
|
6359
6409
|
content: [{ type: "text", text: JSON.stringify(detail, null, 2) }]
|
|
6360
6410
|
};
|
|
6361
|
-
}
|
|
6411
|
+
})
|
|
6362
6412
|
);
|
|
6363
6413
|
server.registerTool(
|
|
6364
6414
|
"search_sessions",
|
|
@@ -6372,8 +6422,13 @@ function registerProsaTools(server, bundle, options = {}) {
|
|
|
6372
6422
|
},
|
|
6373
6423
|
annotations: { readOnlyHint: true, idempotentHint: true }
|
|
6374
6424
|
},
|
|
6375
|
-
async ({ query, limit, raw }) => {
|
|
6376
|
-
const hits = searchFullText(
|
|
6425
|
+
async ({ query, limit, raw }) => withToolBundle(bundle, storePath, ensureStore, (activeBundle) => {
|
|
6426
|
+
const hits = searchFullText(activeBundle, {
|
|
6427
|
+
query,
|
|
6428
|
+
limit: limit ?? 50,
|
|
6429
|
+
raw,
|
|
6430
|
+
engine: searchEngine
|
|
6431
|
+
});
|
|
6377
6432
|
return {
|
|
6378
6433
|
content: [
|
|
6379
6434
|
{
|
|
@@ -6386,7 +6441,7 @@ function registerProsaTools(server, bundle, options = {}) {
|
|
|
6386
6441
|
}
|
|
6387
6442
|
]
|
|
6388
6443
|
};
|
|
6389
|
-
}
|
|
6444
|
+
})
|
|
6390
6445
|
);
|
|
6391
6446
|
server.registerTool(
|
|
6392
6447
|
"export_session_markdown",
|
|
@@ -6398,17 +6453,17 @@ function registerProsaTools(server, bundle, options = {}) {
|
|
|
6398
6453
|
},
|
|
6399
6454
|
annotations: { readOnlyHint: true, idempotentHint: true }
|
|
6400
6455
|
},
|
|
6401
|
-
async ({ session_id }) => {
|
|
6456
|
+
async ({ session_id }) => withToolBundle(bundle, storePath, ensureStore, async (activeBundle) => {
|
|
6402
6457
|
try {
|
|
6403
|
-
const md = await exportSessionMarkdown(
|
|
6458
|
+
const md = await exportSessionMarkdown(activeBundle, session_id);
|
|
6404
6459
|
return { content: [{ type: "text", text: md }] };
|
|
6405
6460
|
} catch (error) {
|
|
6406
6461
|
return {
|
|
6407
|
-
content: [{ type: "text", text:
|
|
6462
|
+
content: [{ type: "text", text: getErrorMessage(error) }],
|
|
6408
6463
|
isError: true
|
|
6409
6464
|
};
|
|
6410
6465
|
}
|
|
6411
|
-
}
|
|
6466
|
+
})
|
|
6412
6467
|
);
|
|
6413
6468
|
server.registerTool(
|
|
6414
6469
|
"list_tool_calls",
|
|
@@ -6435,7 +6490,7 @@ function registerProsaTools(server, bundle, options = {}) {
|
|
|
6435
6490
|
},
|
|
6436
6491
|
annotations: { readOnlyHint: true, idempotentHint: true }
|
|
6437
6492
|
},
|
|
6438
|
-
async ({ tool_name, canonical_type, session_id, errors_only, limit }) => {
|
|
6493
|
+
async ({ tool_name, canonical_type, session_id, errors_only, limit }) => withToolBundle(bundle, storePath, ensureStore, (activeBundle) => {
|
|
6439
6494
|
const conds = [];
|
|
6440
6495
|
const params = [];
|
|
6441
6496
|
if (tool_name) {
|
|
@@ -6463,13 +6518,13 @@ function registerProsaTools(server, bundle, options = {}) {
|
|
|
6463
6518
|
LEFT JOIN tool_results tr ON tr.tool_call_id = tc.tool_call_id
|
|
6464
6519
|
${where}
|
|
6465
6520
|
ORDER BY tc.timestamp_start DESC
|
|
6466
|
-
LIMIT ${
|
|
6521
|
+
LIMIT ${clampLimit(limit, { max: 500, fallback: 100 })}
|
|
6467
6522
|
`;
|
|
6468
|
-
const rows =
|
|
6523
|
+
const rows = activeBundle.db.prepare(sql).all(...params);
|
|
6469
6524
|
return {
|
|
6470
6525
|
content: [{ type: "text", text: JSON.stringify(rows, null, 2) }]
|
|
6471
6526
|
};
|
|
6472
|
-
}
|
|
6527
|
+
})
|
|
6473
6528
|
);
|
|
6474
6529
|
server.registerTool(
|
|
6475
6530
|
"find_touched_files",
|
|
@@ -6482,7 +6537,7 @@ function registerProsaTools(server, bundle, options = {}) {
|
|
|
6482
6537
|
},
|
|
6483
6538
|
annotations: { readOnlyHint: true, idempotentHint: true }
|
|
6484
6539
|
},
|
|
6485
|
-
async ({ path_substring, limit }) => {
|
|
6540
|
+
async ({ path_substring, limit }) => withToolBundle(bundle, storePath, ensureStore, (activeBundle) => {
|
|
6486
6541
|
const sql = `
|
|
6487
6542
|
SELECT tc.session_id, tc.tool_name, tc.canonical_tool_type, tc.path,
|
|
6488
6543
|
tc.timestamp_start, tc.command
|
|
@@ -6494,14 +6549,14 @@ function registerProsaTools(server, bundle, options = {}) {
|
|
|
6494
6549
|
FROM artifacts a
|
|
6495
6550
|
WHERE a.path IS NOT NULL AND a.path LIKE ?
|
|
6496
6551
|
ORDER BY timestamp_start DESC
|
|
6497
|
-
LIMIT ${
|
|
6552
|
+
LIMIT ${clampLimit(limit, { max: 500, fallback: 100 })}
|
|
6498
6553
|
`;
|
|
6499
6554
|
const like = `%${path_substring}%`;
|
|
6500
|
-
const rows =
|
|
6555
|
+
const rows = activeBundle.db.prepare(sql).all(like, like);
|
|
6501
6556
|
return {
|
|
6502
6557
|
content: [{ type: "text", text: JSON.stringify(rows, null, 2) }]
|
|
6503
6558
|
};
|
|
6504
|
-
}
|
|
6559
|
+
})
|
|
6505
6560
|
);
|
|
6506
6561
|
server.registerTool(
|
|
6507
6562
|
"get_artifact",
|
|
@@ -6513,8 +6568,8 @@ function registerProsaTools(server, bundle, options = {}) {
|
|
|
6513
6568
|
},
|
|
6514
6569
|
annotations: { readOnlyHint: true, idempotentHint: true }
|
|
6515
6570
|
},
|
|
6516
|
-
async ({ artifact_id }) => {
|
|
6517
|
-
const row =
|
|
6571
|
+
async ({ artifact_id }) => withToolBundle(bundle, storePath, ensureStore, async (activeBundle) => {
|
|
6572
|
+
const row = activeBundle.db.prepare(`SELECT text_object_id, object_id, mime_type FROM artifacts WHERE artifact_id = ?`).get(artifact_id);
|
|
6518
6573
|
if (!row) {
|
|
6519
6574
|
return {
|
|
6520
6575
|
content: [{ type: "text", text: `artifact not found: ${artifact_id}` }],
|
|
@@ -6527,12 +6582,12 @@ function registerProsaTools(server, bundle, options = {}) {
|
|
|
6527
6582
|
}
|
|
6528
6583
|
try {
|
|
6529
6584
|
const { getText: getText2 } = await Promise.resolve().then(() => (init_cas(), cas_exports));
|
|
6530
|
-
const text = await getText2(
|
|
6585
|
+
const text = await getText2(activeBundle, objectId);
|
|
6531
6586
|
return { content: [{ type: "text", text }] };
|
|
6532
6587
|
} catch {
|
|
6533
6588
|
return { content: [{ type: "text", text: `[binary artifact: ${objectId}]` }] };
|
|
6534
6589
|
}
|
|
6535
|
-
}
|
|
6590
|
+
})
|
|
6536
6591
|
);
|
|
6537
6592
|
server.registerTool(
|
|
6538
6593
|
"index_status",
|
|
@@ -6542,14 +6597,25 @@ function registerProsaTools(server, bundle, options = {}) {
|
|
|
6542
6597
|
inputSchema: {},
|
|
6543
6598
|
annotations: { readOnlyHint: true, idempotentHint: true }
|
|
6544
6599
|
},
|
|
6545
|
-
async () => {
|
|
6546
|
-
const rows = getSearchIndexStatuses(
|
|
6600
|
+
async () => withToolBundle(bundle, storePath, ensureStore, (activeBundle) => {
|
|
6601
|
+
const rows = getSearchIndexStatuses(activeBundle);
|
|
6547
6602
|
return {
|
|
6548
6603
|
content: [{ type: "text", text: JSON.stringify(rows, null, 2) }]
|
|
6549
6604
|
};
|
|
6550
|
-
}
|
|
6605
|
+
})
|
|
6551
6606
|
);
|
|
6552
6607
|
}
|
|
6608
|
+
async function withToolBundle(fallbackBundle, storePath, ensureStore, fn) {
|
|
6609
|
+
if (!ensureStore) {
|
|
6610
|
+
return await fn(fallbackBundle);
|
|
6611
|
+
}
|
|
6612
|
+
const bundle = await openOrInitBundle(storePath);
|
|
6613
|
+
try {
|
|
6614
|
+
return await fn(bundle);
|
|
6615
|
+
} finally {
|
|
6616
|
+
closeBundle(bundle);
|
|
6617
|
+
}
|
|
6618
|
+
}
|
|
6553
6619
|
function registerProsaPrompts(server) {
|
|
6554
6620
|
server.registerPrompt(
|
|
6555
6621
|
"investigate_prior_work",
|
|
@@ -6582,14 +6648,14 @@ function registerProsaPrompts(server) {
|
|
|
6582
6648
|
path: z.string().min(1).describe("File path, directory, or distinctive path suffix")
|
|
6583
6649
|
}
|
|
6584
6650
|
},
|
|
6585
|
-
({ path:
|
|
6651
|
+
({ path: path20 }) => ({
|
|
6586
6652
|
description: "Find sessions that touched a path and summarize the evidence.",
|
|
6587
6653
|
messages: [
|
|
6588
6654
|
{
|
|
6589
6655
|
role: "user",
|
|
6590
6656
|
content: {
|
|
6591
6657
|
type: "text",
|
|
6592
|
-
text: FIND_FILE_HISTORY_PROMPT.replace("{{path}}",
|
|
6658
|
+
text: FIND_FILE_HISTORY_PROMPT.replace("{{path}}", path20)
|
|
6593
6659
|
}
|
|
6594
6660
|
}
|
|
6595
6661
|
]
|
|
@@ -6722,7 +6788,7 @@ function createMcpServer(bundle, searchEngine, storePath) {
|
|
|
6722
6788
|
},
|
|
6723
6789
|
{ instructions: PROSA_MCP_INSTRUCTIONS }
|
|
6724
6790
|
);
|
|
6725
|
-
registerProsaTools(server, bundle, { searchEngine, storePath });
|
|
6791
|
+
registerProsaTools(server, bundle, { ensureStore: true, searchEngine, storePath });
|
|
6726
6792
|
return server;
|
|
6727
6793
|
}
|
|
6728
6794
|
async function readBody(req) {
|
|
@@ -6753,18 +6819,33 @@ function writeError(res, error) {
|
|
|
6753
6819
|
res.end(
|
|
6754
6820
|
JSON.stringify({
|
|
6755
6821
|
jsonrpc: "2.0",
|
|
6756
|
-
error: { code: -32603, message:
|
|
6822
|
+
error: { code: -32603, message: getErrorMessage(error) },
|
|
6757
6823
|
id: null
|
|
6758
6824
|
})
|
|
6759
6825
|
);
|
|
6760
6826
|
}
|
|
6761
6827
|
|
|
6828
|
+
// src/cli/parsers.ts
|
|
6829
|
+
function parseSearchEngine(value) {
|
|
6830
|
+
if (value === "fts5" || value === "tantivy") return value;
|
|
6831
|
+
throw new Error(`invalid search engine: ${value} (expected fts5 or tantivy)`);
|
|
6832
|
+
}
|
|
6833
|
+
function parseMcpTransport(value) {
|
|
6834
|
+
if (value === "stdio" || value === "http") return value;
|
|
6835
|
+
throw new Error(`invalid transport: ${value} (expected stdio or http)`);
|
|
6836
|
+
}
|
|
6837
|
+
function parseSourceTool(value) {
|
|
6838
|
+
if (value === void 0) return void 0;
|
|
6839
|
+
if (SOURCE_TOOLS.includes(value)) return value;
|
|
6840
|
+
throw new Error(`invalid source tool: ${value} (expected one of ${SOURCE_TOOLS.join(", ")})`);
|
|
6841
|
+
}
|
|
6842
|
+
|
|
6762
6843
|
// src/cli/commands/mcp.ts
|
|
6763
6844
|
function mcpCommand() {
|
|
6764
6845
|
const serve = new Command5("serve").description("Start a local MCP server over the prosa bundle.").option("--store <path>", "bundle directory", defaultBundlePath()).option("--transport <transport>", "MCP transport: stdio|http", "stdio").option("--host <host>", "bind host", "127.0.0.1").option("--port <port>", "bind port", "7331").option("--path <path>", "HTTP path", "/mcp").option("--search-engine <engine>", "search engine: fts5|tantivy", "fts5").action(
|
|
6765
6846
|
async (options) => {
|
|
6766
6847
|
const storePath = path18.resolve(options.store);
|
|
6767
|
-
const bundle = await
|
|
6848
|
+
const bundle = await openOrInitBundle(storePath);
|
|
6768
6849
|
try {
|
|
6769
6850
|
const transport = parseMcpTransport(options.transport);
|
|
6770
6851
|
const searchEngine = parseSearchEngine(options.searchEngine);
|
|
@@ -6796,14 +6877,6 @@ function mcpCommand() {
|
|
|
6796
6877
|
);
|
|
6797
6878
|
return new Command5("mcp").description("MCP server commands.").addCommand(serve);
|
|
6798
6879
|
}
|
|
6799
|
-
function parseMcpTransport(value) {
|
|
6800
|
-
if (value === "stdio" || value === "http") return value;
|
|
6801
|
-
throw new Error(`invalid --transport: ${value} (expected stdio or http)`);
|
|
6802
|
-
}
|
|
6803
|
-
function parseSearchEngine(value) {
|
|
6804
|
-
if (value === "fts5" || value === "tantivy") return value;
|
|
6805
|
-
throw new Error(`invalid --search-engine: ${value} (expected fts5 or tantivy)`);
|
|
6806
|
-
}
|
|
6807
6880
|
function registerShutdown(closeServer, bundle) {
|
|
6808
6881
|
const shutdown = async () => {
|
|
6809
6882
|
await closeServer();
|
|
@@ -6825,7 +6898,7 @@ function queryCommand() {
|
|
|
6825
6898
|
const duckdb = new Command6("duckdb").description("Run a DuckDB SQL query over exported Parquet tables.").argument("<sql>", "DuckDB SQL query").option("--store <path>", "bundle directory", defaultBundlePath()).option("--parquet-dir <path>", "Parquet directory (default: <store>/parquet)").option("--output-format <fmt>", "interactive|table|json|csv", "table").action(
|
|
6826
6899
|
async (sql, options) => {
|
|
6827
6900
|
const format = parseOutputFormat(options.outputFormat, "table");
|
|
6828
|
-
const parquetDir = options.parquetDir ? path19.resolve(options.parquetDir) : await
|
|
6901
|
+
const parquetDir = options.parquetDir ? path19.resolve(options.parquetDir) : await withBundle(options.store, (bundle) => bundle.paths.parquet);
|
|
6829
6902
|
const result = await queryDuckDbParquet({ parquetDir, sql });
|
|
6830
6903
|
printRows(result.rows, {
|
|
6831
6904
|
format,
|
|
@@ -6836,26 +6909,16 @@ function queryCommand() {
|
|
|
6836
6909
|
);
|
|
6837
6910
|
return new Command6("query").description("Run derived analytical queries.").addCommand(duckdb);
|
|
6838
6911
|
}
|
|
6839
|
-
async function defaultParquetDir(storePath) {
|
|
6840
|
-
const bundle = await openBundle(storePath);
|
|
6841
|
-
try {
|
|
6842
|
-
return bundle.paths.parquet;
|
|
6843
|
-
} finally {
|
|
6844
|
-
closeBundle(bundle);
|
|
6845
|
-
}
|
|
6846
|
-
}
|
|
6847
6912
|
|
|
6848
6913
|
// src/cli/commands/search.ts
|
|
6849
|
-
import path20 from "path";
|
|
6850
6914
|
import { Command as Command7 } from "commander";
|
|
6851
6915
|
init_search();
|
|
6852
6916
|
function searchCommand() {
|
|
6853
6917
|
return new Command7("search").description("Full-text search across messages, tool calls and tool outputs.").argument("<query>", "FTS5 query string (supports MATCH syntax)").option("--store <path>", "bundle directory", defaultBundlePath()).option("--limit <n>", "maximum hits", "50").option("--engine <engine>", "search engine: fts5|tantivy", "fts5").option("--output-format <fmt>", "interactive|table|json|csv", "table").action(
|
|
6854
6918
|
async (query, options) => {
|
|
6855
|
-
const engine =
|
|
6919
|
+
const engine = parseSearchEngine(options.engine);
|
|
6856
6920
|
const format = parseOutputFormat(options.outputFormat, "table");
|
|
6857
|
-
|
|
6858
|
-
try {
|
|
6921
|
+
await withBundle(options.store, (bundle) => {
|
|
6859
6922
|
const hits = searchFullText(bundle, {
|
|
6860
6923
|
query,
|
|
6861
6924
|
limit: Number.parseInt(options.limit, 10),
|
|
@@ -6866,29 +6929,21 @@ function searchCommand() {
|
|
|
6866
6929
|
columns: ["timestamp", "role", "tool_name", "session_id", "snippet"],
|
|
6867
6930
|
meta: { query, engine, count: hits.length }
|
|
6868
6931
|
});
|
|
6869
|
-
}
|
|
6870
|
-
closeBundle(bundle);
|
|
6871
|
-
}
|
|
6932
|
+
});
|
|
6872
6933
|
}
|
|
6873
6934
|
);
|
|
6874
6935
|
}
|
|
6875
|
-
function parseSearchEngine2(value) {
|
|
6876
|
-
if (value === "fts5" || value === "tantivy") return value;
|
|
6877
|
-
throw new Error(`invalid --engine: ${value} (expected fts5 or tantivy)`);
|
|
6878
|
-
}
|
|
6879
6936
|
|
|
6880
6937
|
// src/cli/commands/sessions.ts
|
|
6881
|
-
import path21 from "path";
|
|
6882
6938
|
import { Command as Command8 } from "commander";
|
|
6883
6939
|
init_sessions();
|
|
6884
6940
|
function sessionsCommand() {
|
|
6885
6941
|
const command = new Command8("sessions").description("List sessions in the bundle, with filters.").enablePositionalOptions().option("--store <path>", "bundle directory", defaultBundlePath()).option("--source <tool>", "filter by source tool: cursor|codex|claude|gemini").option("--since <iso>", "sessions starting on/after this ISO timestamp").option("--until <iso>", "sessions starting before this ISO timestamp").option("--limit <n>", "maximum rows", "50").option("--output-format <fmt>", "interactive|table|json|csv", "table").action(
|
|
6886
6942
|
async (options) => {
|
|
6887
6943
|
const format = parseOutputFormat(options.outputFormat, "table");
|
|
6888
|
-
|
|
6889
|
-
try {
|
|
6944
|
+
await withBundle(options.store, (bundle) => {
|
|
6890
6945
|
const rows = listSessions(bundle, {
|
|
6891
|
-
sourceTool: options.source,
|
|
6946
|
+
sourceTool: parseSourceTool(options.source),
|
|
6892
6947
|
sinceIso: options.since,
|
|
6893
6948
|
untilIso: options.until,
|
|
6894
6949
|
limit: Number.parseInt(options.limit, 10)
|
|
@@ -6906,26 +6961,21 @@ function sessionsCommand() {
|
|
|
6906
6961
|
"title"
|
|
6907
6962
|
]
|
|
6908
6963
|
});
|
|
6909
|
-
}
|
|
6910
|
-
closeBundle(bundle);
|
|
6911
|
-
}
|
|
6964
|
+
});
|
|
6912
6965
|
}
|
|
6913
6966
|
);
|
|
6914
6967
|
command.addCommand(
|
|
6915
6968
|
new Command8("count").description("Count sessions in the bundle, with filters.").option("--store <path>", "bundle directory", defaultBundlePath()).option("--source <tool>", "filter by source tool: cursor|codex|claude|gemini").option("--since <iso>", "sessions starting on/after this ISO timestamp").option("--until <iso>", "sessions starting before this ISO timestamp").action(
|
|
6916
6969
|
async (options) => {
|
|
6917
|
-
|
|
6918
|
-
try {
|
|
6970
|
+
await withBundle(options.store, (bundle) => {
|
|
6919
6971
|
const count = countSessions(bundle, {
|
|
6920
|
-
sourceTool: options.source,
|
|
6972
|
+
sourceTool: parseSourceTool(options.source),
|
|
6921
6973
|
sinceIso: options.since,
|
|
6922
6974
|
untilIso: options.until
|
|
6923
6975
|
});
|
|
6924
6976
|
process.stdout.write(`${count}
|
|
6925
6977
|
`);
|
|
6926
|
-
}
|
|
6927
|
-
closeBundle(bundle);
|
|
6928
|
-
}
|
|
6978
|
+
});
|
|
6929
6979
|
}
|
|
6930
6980
|
)
|
|
6931
6981
|
);
|
|
@@ -6933,7 +6983,6 @@ function sessionsCommand() {
|
|
|
6933
6983
|
}
|
|
6934
6984
|
|
|
6935
6985
|
// src/cli/commands/tui.ts
|
|
6936
|
-
import path22 from "path";
|
|
6937
6986
|
import { Command as Command9 } from "commander";
|
|
6938
6987
|
function tuiCommand() {
|
|
6939
6988
|
return new Command9("tui").description("Open the interactive Ink-based explorer.").option("--store <path>", "bundle directory", defaultBundlePath()).action(async (options) => {
|
|
@@ -6942,14 +6991,11 @@ function tuiCommand() {
|
|
|
6942
6991
|
import("react"),
|
|
6943
6992
|
Promise.resolve().then(() => (init_App(), App_exports))
|
|
6944
6993
|
]);
|
|
6945
|
-
|
|
6946
|
-
try {
|
|
6994
|
+
await withBundle(options.store, async (bundle) => {
|
|
6947
6995
|
console.clear();
|
|
6948
6996
|
const app = render(React.createElement(App2, { bundle }));
|
|
6949
6997
|
await app.waitUntilExit();
|
|
6950
|
-
}
|
|
6951
|
-
closeBundle(bundle);
|
|
6952
|
-
}
|
|
6998
|
+
});
|
|
6953
6999
|
});
|
|
6954
7000
|
}
|
|
6955
7001
|
|