@c3-oss/prosa 0.3.0 → 0.3.1
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 +140 -123
- package/dist/bin/prosa.js.map +1 -1
- package/dist/cli/main.js +140 -123
- package/dist/cli/main.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +21 -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
|
|
|
@@ -1626,6 +1647,7 @@ function closeBundle(bundle) {
|
|
|
1626
1647
|
}
|
|
1627
1648
|
|
|
1628
1649
|
// src/services/compile.ts
|
|
1650
|
+
init_errors();
|
|
1629
1651
|
import os2 from "os";
|
|
1630
1652
|
import path14 from "path";
|
|
1631
1653
|
|
|
@@ -1678,6 +1700,9 @@ function importBatchId(sourceTool, startedAtIso) {
|
|
|
1678
1700
|
return tupleId(["import_batch", sourceTool, startedAtIso, String(Math.random())]);
|
|
1679
1701
|
}
|
|
1680
1702
|
|
|
1703
|
+
// src/importers/claude/index.ts
|
|
1704
|
+
init_errors();
|
|
1705
|
+
|
|
1681
1706
|
// src/core/ingest/batch.ts
|
|
1682
1707
|
init_cas();
|
|
1683
1708
|
init_db();
|
|
@@ -1967,7 +1992,7 @@ async function compileClaude(bundle, root, options = {}) {
|
|
|
1967
1992
|
);
|
|
1968
1993
|
await recordError(bundle, batch.batch_id, {
|
|
1969
1994
|
kind: "claude_file_failed",
|
|
1970
|
-
message:
|
|
1995
|
+
message: getErrorMessage(error),
|
|
1971
1996
|
payload: { path: file.filePath }
|
|
1972
1997
|
});
|
|
1973
1998
|
}
|
|
@@ -2882,6 +2907,7 @@ init_cas();
|
|
|
2882
2907
|
init_db();
|
|
2883
2908
|
import { readFile as readFile5 } from "fs/promises";
|
|
2884
2909
|
import path7 from "path";
|
|
2910
|
+
init_errors();
|
|
2885
2911
|
|
|
2886
2912
|
// src/importers/codex/discover.ts
|
|
2887
2913
|
import { readdir as readdir2 } from "fs/promises";
|
|
@@ -2931,7 +2957,7 @@ async function compileCodex(bundle, root, options = {}) {
|
|
|
2931
2957
|
);
|
|
2932
2958
|
await recordError(bundle, batch.batch_id, {
|
|
2933
2959
|
kind: "codex_file_failed",
|
|
2934
|
-
message:
|
|
2960
|
+
message: getErrorMessage(error),
|
|
2935
2961
|
payload: { path: filePath }
|
|
2936
2962
|
});
|
|
2937
2963
|
}
|
|
@@ -4022,6 +4048,7 @@ init_cas();
|
|
|
4022
4048
|
init_db();
|
|
4023
4049
|
import path9 from "path";
|
|
4024
4050
|
import Database2 from "better-sqlite3";
|
|
4051
|
+
init_errors();
|
|
4025
4052
|
|
|
4026
4053
|
// src/importers/cursor/discover.ts
|
|
4027
4054
|
import { readdir as readdir3 } from "fs/promises";
|
|
@@ -4086,7 +4113,7 @@ async function compileCursor(bundle, root, options = {}) {
|
|
|
4086
4113
|
);
|
|
4087
4114
|
await recordError(bundle, batch.batch_id, {
|
|
4088
4115
|
kind: "cursor_store_failed",
|
|
4089
|
-
message:
|
|
4116
|
+
message: getErrorMessage(error),
|
|
4090
4117
|
payload: { path: store.filePath }
|
|
4091
4118
|
});
|
|
4092
4119
|
}
|
|
@@ -4741,6 +4768,7 @@ init_cas();
|
|
|
4741
4768
|
init_db();
|
|
4742
4769
|
import { readFile as readFile7 } from "fs/promises";
|
|
4743
4770
|
import path11 from "path";
|
|
4771
|
+
init_errors();
|
|
4744
4772
|
|
|
4745
4773
|
// src/importers/gemini/discover.ts
|
|
4746
4774
|
import { readFile as readFile6, readdir as readdir4 } from "fs/promises";
|
|
@@ -4812,7 +4840,7 @@ async function compileGemini(bundle, root, options = {}) {
|
|
|
4812
4840
|
);
|
|
4813
4841
|
await recordError(bundle, batch.batch_id, {
|
|
4814
4842
|
kind: "gemini_file_failed",
|
|
4815
|
-
message:
|
|
4843
|
+
message: getErrorMessage(error),
|
|
4816
4844
|
payload: { path: file.filePath }
|
|
4817
4845
|
});
|
|
4818
4846
|
}
|
|
@@ -5498,6 +5526,7 @@ function flushPending4(bundle, pending) {
|
|
|
5498
5526
|
import { mkdir as mkdir3, rm, writeFile as writeFile4 } from "fs/promises";
|
|
5499
5527
|
import path12 from "path";
|
|
5500
5528
|
import { DuckDBConnection } from "@duckdb/node-api";
|
|
5529
|
+
init_errors();
|
|
5501
5530
|
var PARQUET_TABLES = [
|
|
5502
5531
|
"objects",
|
|
5503
5532
|
"source_files",
|
|
@@ -5595,7 +5624,7 @@ async function attachSqlite(connection, dbPath) {
|
|
|
5595
5624
|
await connection.run(`ATTACH ${sqlString(dbPath)} AS prosa (TYPE sqlite)`);
|
|
5596
5625
|
} catch (error) {
|
|
5597
5626
|
throw new Error(
|
|
5598
|
-
`DuckDB could not attach prosa.sqlite via the sqlite extension: ${
|
|
5627
|
+
`DuckDB could not attach prosa.sqlite via the sqlite extension: ${getErrorMessage(error)}`
|
|
5599
5628
|
);
|
|
5600
5629
|
}
|
|
5601
5630
|
}
|
|
@@ -5626,7 +5655,7 @@ function sqlString(value) {
|
|
|
5626
5655
|
return `'${value.replace(/'/g, "''")}'`;
|
|
5627
5656
|
}
|
|
5628
5657
|
function isMissingParquetError(error) {
|
|
5629
|
-
const message =
|
|
5658
|
+
const message = getErrorMessage(error);
|
|
5630
5659
|
return /No files found|does not exist|not found/i.test(message) && /\.parquet/i.test(message);
|
|
5631
5660
|
}
|
|
5632
5661
|
|
|
@@ -5723,7 +5752,7 @@ async function runCompileImports(options) {
|
|
|
5723
5752
|
tantivy = { indexedDocCount: status.indexed_doc_count };
|
|
5724
5753
|
options.onTantivyComplete?.(tantivy);
|
|
5725
5754
|
} catch (error) {
|
|
5726
|
-
tantivyError =
|
|
5755
|
+
tantivyError = getErrorMessage(error);
|
|
5727
5756
|
logger?.error({ err: error }, "tantivy rebuild failed; SQLite data is intact");
|
|
5728
5757
|
}
|
|
5729
5758
|
}
|
|
@@ -5759,9 +5788,9 @@ import pretty from "pino-pretty";
|
|
|
5759
5788
|
function createCliLogger(options) {
|
|
5760
5789
|
const loggerOptions = {
|
|
5761
5790
|
base: void 0,
|
|
5762
|
-
level: options.verbose
|
|
5791
|
+
level: options.verbose ? "debug" : "info"
|
|
5763
5792
|
};
|
|
5764
|
-
if (options.jsonLogs
|
|
5793
|
+
if (options.jsonLogs) {
|
|
5765
5794
|
return pino(loggerOptions, pino.destination({ dest: 2, sync: true }));
|
|
5766
5795
|
}
|
|
5767
5796
|
return pino(
|
|
@@ -5797,7 +5826,7 @@ function compileAllCommand() {
|
|
|
5797
5826
|
await runCompiles({
|
|
5798
5827
|
providers: COMPILE_PROVIDERS,
|
|
5799
5828
|
storePath: defaultBundlePath(),
|
|
5800
|
-
deferIndex: options.deferIndex
|
|
5829
|
+
deferIndex: options.deferIndex ?? false,
|
|
5801
5830
|
logOptions: options
|
|
5802
5831
|
});
|
|
5803
5832
|
});
|
|
@@ -5812,7 +5841,7 @@ function providerCompileCommand(provider) {
|
|
|
5812
5841
|
await runCompiles({
|
|
5813
5842
|
providers: [provider],
|
|
5814
5843
|
storePath: options.store,
|
|
5815
|
-
deferIndex: options.deferIndex
|
|
5844
|
+
deferIndex: options.deferIndex ?? false,
|
|
5816
5845
|
sessionsPath: options.sessionsPath,
|
|
5817
5846
|
logOptions: command.optsWithGlobals()
|
|
5818
5847
|
});
|
|
@@ -5870,7 +5899,7 @@ function printCounts(summary) {
|
|
|
5870
5899
|
|
|
5871
5900
|
// src/cli/commands/export.ts
|
|
5872
5901
|
import { writeFile as writeFile6 } from "fs/promises";
|
|
5873
|
-
import
|
|
5902
|
+
import path16 from "path";
|
|
5874
5903
|
import { Command as Command2 } from "commander";
|
|
5875
5904
|
|
|
5876
5905
|
// src/services/export/markdown.ts
|
|
@@ -5984,30 +6013,38 @@ function renderToolCall(c) {
|
|
|
5984
6013
|
return lines.join("\n");
|
|
5985
6014
|
}
|
|
5986
6015
|
|
|
6016
|
+
// src/cli/bundle.ts
|
|
6017
|
+
import path15 from "path";
|
|
6018
|
+
async function withBundle(storePath, fn) {
|
|
6019
|
+
const bundle = await openBundle(path15.resolve(storePath));
|
|
6020
|
+
try {
|
|
6021
|
+
return await fn(bundle);
|
|
6022
|
+
} finally {
|
|
6023
|
+
closeBundle(bundle);
|
|
6024
|
+
}
|
|
6025
|
+
}
|
|
6026
|
+
|
|
5987
6027
|
// src/cli/commands/export.ts
|
|
5988
6028
|
function exportCommand() {
|
|
5989
6029
|
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
6030
|
if (options.format !== "markdown") {
|
|
5991
6031
|
throw new Error(`unsupported format: ${options.format} (try --format markdown)`);
|
|
5992
6032
|
}
|
|
5993
|
-
|
|
5994
|
-
try {
|
|
6033
|
+
await withBundle(options.store, async (bundle) => {
|
|
5995
6034
|
const markdown = await exportSessionMarkdown(bundle, sessionId2);
|
|
5996
6035
|
if (options.out) {
|
|
5997
|
-
await writeFile6(
|
|
5998
|
-
process.stdout.write(`wrote ${
|
|
6036
|
+
await writeFile6(path16.resolve(options.out), markdown, "utf8");
|
|
6037
|
+
process.stdout.write(`wrote ${path16.resolve(options.out)}
|
|
5999
6038
|
`);
|
|
6000
6039
|
} else {
|
|
6001
6040
|
process.stdout.write(markdown);
|
|
6002
6041
|
}
|
|
6003
|
-
}
|
|
6004
|
-
closeBundle(bundle);
|
|
6005
|
-
}
|
|
6042
|
+
});
|
|
6006
6043
|
});
|
|
6007
6044
|
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
6045
|
const result = await exportBundleParquet({
|
|
6009
|
-
bundlePath:
|
|
6010
|
-
outDir: options.out ?
|
|
6046
|
+
bundlePath: path16.resolve(options.store),
|
|
6047
|
+
outDir: options.out ? path16.resolve(options.out) : void 0
|
|
6011
6048
|
});
|
|
6012
6049
|
process.stdout.write(`wrote parquet export to ${result.outDir}
|
|
6013
6050
|
`);
|
|
@@ -6018,12 +6055,13 @@ function exportCommand() {
|
|
|
6018
6055
|
}
|
|
6019
6056
|
|
|
6020
6057
|
// src/cli/commands/index.ts
|
|
6021
|
-
import path16 from "path";
|
|
6022
6058
|
import { Command as Command3 } from "commander";
|
|
6023
6059
|
init_indexing();
|
|
6024
6060
|
|
|
6025
6061
|
// src/cli/output.ts
|
|
6026
6062
|
var OUTPUT_FORMATS = ["interactive", "table", "json", "csv"];
|
|
6063
|
+
var COL_SEPARATOR = " ";
|
|
6064
|
+
var RULE_CHAR = "-";
|
|
6027
6065
|
function parseOutputFormat(value, fallback) {
|
|
6028
6066
|
if (value === void 0) return fallback;
|
|
6029
6067
|
if (OUTPUT_FORMATS.includes(value)) return value;
|
|
@@ -6051,11 +6089,12 @@ function printJson(rows, opts) {
|
|
|
6051
6089
|
`);
|
|
6052
6090
|
}
|
|
6053
6091
|
function printCsv(rows, opts) {
|
|
6054
|
-
const
|
|
6055
|
-
process.stdout.write(`${
|
|
6092
|
+
const columns = opts.columns;
|
|
6093
|
+
process.stdout.write(`${columns.map(csvField).join(",")}
|
|
6056
6094
|
`);
|
|
6057
6095
|
for (const row of rows) {
|
|
6058
|
-
const
|
|
6096
|
+
const record = row;
|
|
6097
|
+
const line = columns.map((column) => csvField(formatCell(record[column]))).join(",");
|
|
6059
6098
|
process.stdout.write(`${line}
|
|
6060
6099
|
`);
|
|
6061
6100
|
}
|
|
@@ -6065,23 +6104,24 @@ function csvField(value) {
|
|
|
6065
6104
|
return value;
|
|
6066
6105
|
}
|
|
6067
6106
|
function printTable(rows, opts) {
|
|
6068
|
-
const
|
|
6069
|
-
const widths =
|
|
6070
|
-
const cells = rows.map(
|
|
6071
|
-
|
|
6072
|
-
|
|
6073
|
-
const
|
|
6074
|
-
|
|
6107
|
+
const columns = opts.columns;
|
|
6108
|
+
const widths = columns.map((column) => column.length);
|
|
6109
|
+
const cells = rows.map((row) => {
|
|
6110
|
+
const record = row;
|
|
6111
|
+
return columns.map((column, index) => {
|
|
6112
|
+
const text = formatCell(record[column]);
|
|
6113
|
+
const width = widths[index] ?? 0;
|
|
6114
|
+
if (text.length > width) widths[index] = text.length;
|
|
6075
6115
|
return text;
|
|
6076
|
-
})
|
|
6077
|
-
);
|
|
6078
|
-
const header =
|
|
6079
|
-
const
|
|
6116
|
+
});
|
|
6117
|
+
});
|
|
6118
|
+
const header = columns.map((column, index) => column.padEnd(widths[index] ?? 0)).join(COL_SEPARATOR);
|
|
6119
|
+
const rule = columns.map((_, index) => RULE_CHAR.repeat(widths[index] ?? 0)).join(COL_SEPARATOR);
|
|
6080
6120
|
process.stdout.write(`${header}
|
|
6081
|
-
${
|
|
6121
|
+
${rule}
|
|
6082
6122
|
`);
|
|
6083
6123
|
for (const cellRow of cells) {
|
|
6084
|
-
const line = cellRow.map((
|
|
6124
|
+
const line = cellRow.map((cell, index) => cell.padEnd(widths[index] ?? 0)).join(COL_SEPARATOR);
|
|
6085
6125
|
process.stdout.write(`${line}
|
|
6086
6126
|
`);
|
|
6087
6127
|
}
|
|
@@ -6096,27 +6136,18 @@ function formatCell(value) {
|
|
|
6096
6136
|
// src/cli/commands/index.ts
|
|
6097
6137
|
function indexCommand() {
|
|
6098
6138
|
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
|
-
}
|
|
6139
|
+
await withBundle(options.store, (bundle) => {
|
|
6140
|
+
printIndexStatus(rebuildFts5Index(bundle));
|
|
6141
|
+
});
|
|
6106
6142
|
});
|
|
6107
6143
|
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
|
-
}
|
|
6144
|
+
await withBundle(options.store, async (bundle) => {
|
|
6145
|
+
printIndexStatus(await rebuildTantivyIndex(bundle));
|
|
6146
|
+
});
|
|
6115
6147
|
});
|
|
6116
6148
|
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
6149
|
const format = parseOutputFormat(options.outputFormat, "table");
|
|
6118
|
-
|
|
6119
|
-
try {
|
|
6150
|
+
await withBundle(options.store, (bundle) => {
|
|
6120
6151
|
const rows = getSearchIndexStatuses(bundle);
|
|
6121
6152
|
printRows(rows, {
|
|
6122
6153
|
format,
|
|
@@ -6129,9 +6160,7 @@ function indexCommand() {
|
|
|
6129
6160
|
"error_message"
|
|
6130
6161
|
]
|
|
6131
6162
|
});
|
|
6132
|
-
}
|
|
6133
|
-
closeBundle(bundle);
|
|
6134
|
-
}
|
|
6163
|
+
});
|
|
6135
6164
|
});
|
|
6136
6165
|
return new Command3("index").description("Build or inspect derived search indexes.").addCommand(fts5).addCommand(tantivy).addCommand(status);
|
|
6137
6166
|
}
|
|
@@ -6178,6 +6207,7 @@ import path18 from "path";
|
|
|
6178
6207
|
import { Command as Command5 } from "commander";
|
|
6179
6208
|
|
|
6180
6209
|
// src/mcp/server.ts
|
|
6210
|
+
init_errors();
|
|
6181
6211
|
import { randomUUID } from "crypto";
|
|
6182
6212
|
import http from "http";
|
|
6183
6213
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
@@ -6237,6 +6267,13 @@ Use this workflow:
|
|
|
6237
6267
|
|
|
6238
6268
|
// src/mcp/tools.ts
|
|
6239
6269
|
import { z } from "zod";
|
|
6270
|
+
|
|
6271
|
+
// src/core/domain/types.ts
|
|
6272
|
+
var SOURCE_TOOLS = ["cursor", "codex", "claude", "gemini"];
|
|
6273
|
+
|
|
6274
|
+
// src/mcp/tools.ts
|
|
6275
|
+
init_errors();
|
|
6276
|
+
init_limits();
|
|
6240
6277
|
init_indexing();
|
|
6241
6278
|
init_search();
|
|
6242
6279
|
init_sessions();
|
|
@@ -6250,7 +6287,7 @@ function registerProsaTools(server, bundle, options = {}) {
|
|
|
6250
6287
|
title: "Compile sessions",
|
|
6251
6288
|
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
6289
|
inputSchema: {
|
|
6253
|
-
source: z.enum(
|
|
6290
|
+
source: z.enum(SOURCE_TOOLS).optional(),
|
|
6254
6291
|
sessions_path: z.string().min(1).optional()
|
|
6255
6292
|
},
|
|
6256
6293
|
annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true }
|
|
@@ -6306,7 +6343,7 @@ function registerProsaTools(server, bundle, options = {}) {
|
|
|
6306
6343
|
};
|
|
6307
6344
|
} catch (error) {
|
|
6308
6345
|
return {
|
|
6309
|
-
content: [{ type: "text", text:
|
|
6346
|
+
content: [{ type: "text", text: getErrorMessage(error) }],
|
|
6310
6347
|
isError: true
|
|
6311
6348
|
};
|
|
6312
6349
|
}
|
|
@@ -6318,7 +6355,7 @@ function registerProsaTools(server, bundle, options = {}) {
|
|
|
6318
6355
|
title: "List sessions",
|
|
6319
6356
|
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
6357
|
inputSchema: {
|
|
6321
|
-
source: z.enum(
|
|
6358
|
+
source: z.enum(SOURCE_TOOLS).optional(),
|
|
6322
6359
|
since: z.string().optional().describe("ISO timestamp lower bound (inclusive)"),
|
|
6323
6360
|
until: z.string().optional().describe("ISO timestamp upper bound (exclusive)"),
|
|
6324
6361
|
limit: z.number().int().min(1).max(500).optional().default(50)
|
|
@@ -6404,7 +6441,7 @@ function registerProsaTools(server, bundle, options = {}) {
|
|
|
6404
6441
|
return { content: [{ type: "text", text: md }] };
|
|
6405
6442
|
} catch (error) {
|
|
6406
6443
|
return {
|
|
6407
|
-
content: [{ type: "text", text:
|
|
6444
|
+
content: [{ type: "text", text: getErrorMessage(error) }],
|
|
6408
6445
|
isError: true
|
|
6409
6446
|
};
|
|
6410
6447
|
}
|
|
@@ -6463,7 +6500,7 @@ function registerProsaTools(server, bundle, options = {}) {
|
|
|
6463
6500
|
LEFT JOIN tool_results tr ON tr.tool_call_id = tc.tool_call_id
|
|
6464
6501
|
${where}
|
|
6465
6502
|
ORDER BY tc.timestamp_start DESC
|
|
6466
|
-
LIMIT ${
|
|
6503
|
+
LIMIT ${clampLimit(limit, { max: 500, fallback: 100 })}
|
|
6467
6504
|
`;
|
|
6468
6505
|
const rows = bundle.db.prepare(sql).all(...params);
|
|
6469
6506
|
return {
|
|
@@ -6494,7 +6531,7 @@ function registerProsaTools(server, bundle, options = {}) {
|
|
|
6494
6531
|
FROM artifacts a
|
|
6495
6532
|
WHERE a.path IS NOT NULL AND a.path LIKE ?
|
|
6496
6533
|
ORDER BY timestamp_start DESC
|
|
6497
|
-
LIMIT ${
|
|
6534
|
+
LIMIT ${clampLimit(limit, { max: 500, fallback: 100 })}
|
|
6498
6535
|
`;
|
|
6499
6536
|
const like = `%${path_substring}%`;
|
|
6500
6537
|
const rows = bundle.db.prepare(sql).all(like, like);
|
|
@@ -6582,14 +6619,14 @@ function registerProsaPrompts(server) {
|
|
|
6582
6619
|
path: z.string().min(1).describe("File path, directory, or distinctive path suffix")
|
|
6583
6620
|
}
|
|
6584
6621
|
},
|
|
6585
|
-
({ path:
|
|
6622
|
+
({ path: path20 }) => ({
|
|
6586
6623
|
description: "Find sessions that touched a path and summarize the evidence.",
|
|
6587
6624
|
messages: [
|
|
6588
6625
|
{
|
|
6589
6626
|
role: "user",
|
|
6590
6627
|
content: {
|
|
6591
6628
|
type: "text",
|
|
6592
|
-
text: FIND_FILE_HISTORY_PROMPT.replace("{{path}}",
|
|
6629
|
+
text: FIND_FILE_HISTORY_PROMPT.replace("{{path}}", path20)
|
|
6593
6630
|
}
|
|
6594
6631
|
}
|
|
6595
6632
|
]
|
|
@@ -6753,12 +6790,27 @@ function writeError(res, error) {
|
|
|
6753
6790
|
res.end(
|
|
6754
6791
|
JSON.stringify({
|
|
6755
6792
|
jsonrpc: "2.0",
|
|
6756
|
-
error: { code: -32603, message:
|
|
6793
|
+
error: { code: -32603, message: getErrorMessage(error) },
|
|
6757
6794
|
id: null
|
|
6758
6795
|
})
|
|
6759
6796
|
);
|
|
6760
6797
|
}
|
|
6761
6798
|
|
|
6799
|
+
// src/cli/parsers.ts
|
|
6800
|
+
function parseSearchEngine(value) {
|
|
6801
|
+
if (value === "fts5" || value === "tantivy") return value;
|
|
6802
|
+
throw new Error(`invalid search engine: ${value} (expected fts5 or tantivy)`);
|
|
6803
|
+
}
|
|
6804
|
+
function parseMcpTransport(value) {
|
|
6805
|
+
if (value === "stdio" || value === "http") return value;
|
|
6806
|
+
throw new Error(`invalid transport: ${value} (expected stdio or http)`);
|
|
6807
|
+
}
|
|
6808
|
+
function parseSourceTool(value) {
|
|
6809
|
+
if (value === void 0) return void 0;
|
|
6810
|
+
if (SOURCE_TOOLS.includes(value)) return value;
|
|
6811
|
+
throw new Error(`invalid source tool: ${value} (expected one of ${SOURCE_TOOLS.join(", ")})`);
|
|
6812
|
+
}
|
|
6813
|
+
|
|
6762
6814
|
// src/cli/commands/mcp.ts
|
|
6763
6815
|
function mcpCommand() {
|
|
6764
6816
|
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(
|
|
@@ -6796,14 +6848,6 @@ function mcpCommand() {
|
|
|
6796
6848
|
);
|
|
6797
6849
|
return new Command5("mcp").description("MCP server commands.").addCommand(serve);
|
|
6798
6850
|
}
|
|
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
6851
|
function registerShutdown(closeServer, bundle) {
|
|
6808
6852
|
const shutdown = async () => {
|
|
6809
6853
|
await closeServer();
|
|
@@ -6825,7 +6869,7 @@ function queryCommand() {
|
|
|
6825
6869
|
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
6870
|
async (sql, options) => {
|
|
6827
6871
|
const format = parseOutputFormat(options.outputFormat, "table");
|
|
6828
|
-
const parquetDir = options.parquetDir ? path19.resolve(options.parquetDir) : await
|
|
6872
|
+
const parquetDir = options.parquetDir ? path19.resolve(options.parquetDir) : await withBundle(options.store, (bundle) => bundle.paths.parquet);
|
|
6829
6873
|
const result = await queryDuckDbParquet({ parquetDir, sql });
|
|
6830
6874
|
printRows(result.rows, {
|
|
6831
6875
|
format,
|
|
@@ -6836,26 +6880,16 @@ function queryCommand() {
|
|
|
6836
6880
|
);
|
|
6837
6881
|
return new Command6("query").description("Run derived analytical queries.").addCommand(duckdb);
|
|
6838
6882
|
}
|
|
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
6883
|
|
|
6848
6884
|
// src/cli/commands/search.ts
|
|
6849
|
-
import path20 from "path";
|
|
6850
6885
|
import { Command as Command7 } from "commander";
|
|
6851
6886
|
init_search();
|
|
6852
6887
|
function searchCommand() {
|
|
6853
6888
|
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
6889
|
async (query, options) => {
|
|
6855
|
-
const engine =
|
|
6890
|
+
const engine = parseSearchEngine(options.engine);
|
|
6856
6891
|
const format = parseOutputFormat(options.outputFormat, "table");
|
|
6857
|
-
|
|
6858
|
-
try {
|
|
6892
|
+
await withBundle(options.store, (bundle) => {
|
|
6859
6893
|
const hits = searchFullText(bundle, {
|
|
6860
6894
|
query,
|
|
6861
6895
|
limit: Number.parseInt(options.limit, 10),
|
|
@@ -6866,29 +6900,21 @@ function searchCommand() {
|
|
|
6866
6900
|
columns: ["timestamp", "role", "tool_name", "session_id", "snippet"],
|
|
6867
6901
|
meta: { query, engine, count: hits.length }
|
|
6868
6902
|
});
|
|
6869
|
-
}
|
|
6870
|
-
closeBundle(bundle);
|
|
6871
|
-
}
|
|
6903
|
+
});
|
|
6872
6904
|
}
|
|
6873
6905
|
);
|
|
6874
6906
|
}
|
|
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
6907
|
|
|
6880
6908
|
// src/cli/commands/sessions.ts
|
|
6881
|
-
import path21 from "path";
|
|
6882
6909
|
import { Command as Command8 } from "commander";
|
|
6883
6910
|
init_sessions();
|
|
6884
6911
|
function sessionsCommand() {
|
|
6885
6912
|
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
6913
|
async (options) => {
|
|
6887
6914
|
const format = parseOutputFormat(options.outputFormat, "table");
|
|
6888
|
-
|
|
6889
|
-
try {
|
|
6915
|
+
await withBundle(options.store, (bundle) => {
|
|
6890
6916
|
const rows = listSessions(bundle, {
|
|
6891
|
-
sourceTool: options.source,
|
|
6917
|
+
sourceTool: parseSourceTool(options.source),
|
|
6892
6918
|
sinceIso: options.since,
|
|
6893
6919
|
untilIso: options.until,
|
|
6894
6920
|
limit: Number.parseInt(options.limit, 10)
|
|
@@ -6906,26 +6932,21 @@ function sessionsCommand() {
|
|
|
6906
6932
|
"title"
|
|
6907
6933
|
]
|
|
6908
6934
|
});
|
|
6909
|
-
}
|
|
6910
|
-
closeBundle(bundle);
|
|
6911
|
-
}
|
|
6935
|
+
});
|
|
6912
6936
|
}
|
|
6913
6937
|
);
|
|
6914
6938
|
command.addCommand(
|
|
6915
6939
|
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
6940
|
async (options) => {
|
|
6917
|
-
|
|
6918
|
-
try {
|
|
6941
|
+
await withBundle(options.store, (bundle) => {
|
|
6919
6942
|
const count = countSessions(bundle, {
|
|
6920
|
-
sourceTool: options.source,
|
|
6943
|
+
sourceTool: parseSourceTool(options.source),
|
|
6921
6944
|
sinceIso: options.since,
|
|
6922
6945
|
untilIso: options.until
|
|
6923
6946
|
});
|
|
6924
6947
|
process.stdout.write(`${count}
|
|
6925
6948
|
`);
|
|
6926
|
-
}
|
|
6927
|
-
closeBundle(bundle);
|
|
6928
|
-
}
|
|
6949
|
+
});
|
|
6929
6950
|
}
|
|
6930
6951
|
)
|
|
6931
6952
|
);
|
|
@@ -6933,7 +6954,6 @@ function sessionsCommand() {
|
|
|
6933
6954
|
}
|
|
6934
6955
|
|
|
6935
6956
|
// src/cli/commands/tui.ts
|
|
6936
|
-
import path22 from "path";
|
|
6937
6957
|
import { Command as Command9 } from "commander";
|
|
6938
6958
|
function tuiCommand() {
|
|
6939
6959
|
return new Command9("tui").description("Open the interactive Ink-based explorer.").option("--store <path>", "bundle directory", defaultBundlePath()).action(async (options) => {
|
|
@@ -6942,14 +6962,11 @@ function tuiCommand() {
|
|
|
6942
6962
|
import("react"),
|
|
6943
6963
|
Promise.resolve().then(() => (init_App(), App_exports))
|
|
6944
6964
|
]);
|
|
6945
|
-
|
|
6946
|
-
try {
|
|
6965
|
+
await withBundle(options.store, async (bundle) => {
|
|
6947
6966
|
console.clear();
|
|
6948
6967
|
const app = render(React.createElement(App2, { bundle }));
|
|
6949
6968
|
await app.waitUntilExit();
|
|
6950
|
-
}
|
|
6951
|
-
closeBundle(bundle);
|
|
6952
|
-
}
|
|
6969
|
+
});
|
|
6953
6970
|
});
|
|
6954
6971
|
}
|
|
6955
6972
|
|