@aliou/sesame 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/bin/sesame +19 -0
- package/dist/commands/index-cmd.d.ts +5 -0
- package/dist/commands/index-cmd.d.ts.map +1 -0
- package/dist/commands/index-cmd.js +76 -0
- package/dist/commands/index-cmd.js.map +1 -0
- package/dist/commands/search-cmd.d.ts +5 -0
- package/dist/commands/search-cmd.d.ts.map +1 -0
- package/dist/commands/search-cmd.js +129 -0
- package/dist/commands/search-cmd.js.map +1 -0
- package/dist/commands/status-cmd.d.ts +5 -0
- package/dist/commands/status-cmd.d.ts.map +1 -0
- package/dist/commands/status-cmd.js +27 -0
- package/dist/commands/status-cmd.js.map +1 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -0
- package/dist/indexer/format-tool-call.d.ts +18 -0
- package/dist/indexer/format-tool-call.d.ts.map +1 -0
- package/dist/indexer/format-tool-call.js +112 -0
- package/dist/indexer/format-tool-call.js.map +1 -0
- package/dist/indexer/index.d.ts +10 -0
- package/dist/indexer/index.d.ts.map +1 -0
- package/dist/indexer/index.js +142 -0
- package/dist/indexer/index.js.map +1 -0
- package/dist/parsers/pi.d.ts +10 -0
- package/dist/parsers/pi.d.ts.map +1 -0
- package/dist/parsers/pi.js +139 -0
- package/dist/parsers/pi.js.map +1 -0
- package/dist/sesame.d.ts +17 -0
- package/dist/sesame.d.ts.map +1 -0
- package/dist/sesame.js +62 -0
- package/dist/sesame.js.map +1 -0
- package/dist/storage/db.d.ts +55 -0
- package/dist/storage/db.d.ts.map +1 -0
- package/dist/storage/db.js +222 -0
- package/dist/storage/db.js.map +1 -0
- package/dist/test-helpers/session-factory.d.ts +21 -0
- package/dist/test-helpers/session-factory.d.ts.map +1 -0
- package/dist/test-helpers/session-factory.js +116 -0
- package/dist/test-helpers/session-factory.js.map +1 -0
- package/dist/types/session.d.ts +49 -0
- package/dist/types/session.d.ts.map +1 -0
- package/dist/types/session.js +7 -0
- package/dist/types/session.js.map +1 -0
- package/dist/utils/config.d.ts +23 -0
- package/dist/utils/config.d.ts.map +1 -0
- package/dist/utils/config.js +66 -0
- package/dist/utils/config.js.map +1 -0
- package/dist/utils/date.d.ts +5 -0
- package/dist/utils/date.d.ts.map +1 -0
- package/dist/utils/date.js +34 -0
- package/dist/utils/date.js.map +1 -0
- package/dist/utils/xdg.d.ts +20 -0
- package/dist/utils/xdg.d.ts.map +1 -0
- package/dist/utils/xdg.js +61 -0
- package/dist/utils/xdg.js.map +1 -0
- package/package.json +67 -0
- package/share/pi-extension/src/index.ts +7 -0
- package/share/pi-extension/src/tools/sesame-search.ts +143 -0
- package/skills/sesame/SKILL.md +53 -0
package/bin/sesame
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Sesame CLI wrapper - finds node and runs the main entry point
|
|
3
|
+
set -euo pipefail
|
|
4
|
+
|
|
5
|
+
# Find the script's real directory (resolve symlinks)
|
|
6
|
+
SCRIPT_DIR="$(cd "$(dirname "$(readlink -f "$0" 2>/dev/null || echo "$0")")" && pwd)"
|
|
7
|
+
SESAME_ROOT="$(dirname "$SCRIPT_DIR")"
|
|
8
|
+
|
|
9
|
+
# Find node
|
|
10
|
+
if command -v node >/dev/null 2>&1; then
|
|
11
|
+
NODE="node"
|
|
12
|
+
elif [ -x "$HOME/.nvm/versions/node/current/bin/node" ]; then
|
|
13
|
+
NODE="$HOME/.nvm/versions/node/current/bin/node"
|
|
14
|
+
else
|
|
15
|
+
echo "Error: node is required but not found." >&2
|
|
16
|
+
exit 1
|
|
17
|
+
fi
|
|
18
|
+
|
|
19
|
+
exec "$NODE" --disable-warning=ExperimentalWarning --experimental-strip-types "$SESAME_ROOT/src/sesame.ts" "$@"
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index-cmd.d.ts","sourceRoot":"","sources":["../../src/commands/index-cmd.ts"],"names":[],"mappings":"AAAA;;GAEG;AAUH,wBAA8B,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CA6ExE"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Index command - scans and indexes session files
|
|
3
|
+
*/
|
|
4
|
+
import { mkdirSync } from "node:fs";
|
|
5
|
+
import { join } from "node:path";
|
|
6
|
+
import { indexSessions } from "../indexer/index";
|
|
7
|
+
import { PiParser } from "../parsers/pi";
|
|
8
|
+
import { dropAll, openDatabase } from "../storage/db";
|
|
9
|
+
import { expandPath, loadConfig } from "../utils/config";
|
|
10
|
+
import { getXDGPaths } from "../utils/xdg";
|
|
11
|
+
export default async function indexCommand(args) {
|
|
12
|
+
// Parse --full flag
|
|
13
|
+
const fullRebuild = args.includes("--full");
|
|
14
|
+
// Load configuration
|
|
15
|
+
const config = await loadConfig();
|
|
16
|
+
// Get data directory and ensure it exists
|
|
17
|
+
const paths = getXDGPaths();
|
|
18
|
+
mkdirSync(paths.data, { recursive: true });
|
|
19
|
+
// Open database
|
|
20
|
+
const dbPath = join(paths.data, "index.sqlite");
|
|
21
|
+
const db = openDatabase(dbPath);
|
|
22
|
+
try {
|
|
23
|
+
// Drop all data if --full flag is set
|
|
24
|
+
if (fullRebuild) {
|
|
25
|
+
console.log("Dropping existing index...");
|
|
26
|
+
dropAll(db);
|
|
27
|
+
}
|
|
28
|
+
let totalAdded = 0;
|
|
29
|
+
let totalUpdated = 0;
|
|
30
|
+
let totalSkipped = 0;
|
|
31
|
+
let totalErrors = 0;
|
|
32
|
+
// Index each source
|
|
33
|
+
for (const source of config.sources) {
|
|
34
|
+
const expandedPath = expandPath(source.path);
|
|
35
|
+
// Get parser (only "pi" is supported for now)
|
|
36
|
+
if (source.parser !== "pi") {
|
|
37
|
+
console.error(`Skipping source ${source.path}: unsupported parser "${source.parser}"`);
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
const parser = new PiParser();
|
|
41
|
+
console.log(`\nIndexing ${expandedPath}...`);
|
|
42
|
+
const result = await indexSessions(db, expandedPath, parser);
|
|
43
|
+
// Print results for this source
|
|
44
|
+
if (result.added > 0) {
|
|
45
|
+
console.log(` Added: ${result.added}`);
|
|
46
|
+
}
|
|
47
|
+
if (result.updated > 0) {
|
|
48
|
+
console.log(` Updated: ${result.updated}`);
|
|
49
|
+
}
|
|
50
|
+
if (result.skipped > 0) {
|
|
51
|
+
console.log(` Skipped: ${result.skipped}`);
|
|
52
|
+
}
|
|
53
|
+
if (result.errors > 0) {
|
|
54
|
+
console.error(` Errors: ${result.errors}`);
|
|
55
|
+
}
|
|
56
|
+
// Accumulate totals
|
|
57
|
+
totalAdded += result.added;
|
|
58
|
+
totalUpdated += result.updated;
|
|
59
|
+
totalSkipped += result.skipped;
|
|
60
|
+
totalErrors += result.errors;
|
|
61
|
+
}
|
|
62
|
+
// Print summary
|
|
63
|
+
console.log(`\n${"=".repeat(50)}`);
|
|
64
|
+
console.log("Indexing complete");
|
|
65
|
+
console.log(` Total added: ${totalAdded}`);
|
|
66
|
+
console.log(` Total updated: ${totalUpdated}`);
|
|
67
|
+
console.log(` Total skipped: ${totalSkipped}`);
|
|
68
|
+
if (totalErrors > 0) {
|
|
69
|
+
console.log(` Total errors: ${totalErrors}`);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
finally {
|
|
73
|
+
db.close();
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=index-cmd.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index-cmd.js","sourceRoot":"","sources":["../../src/commands/index-cmd.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"search-cmd.d.ts","sourceRoot":"","sources":["../../src/commands/search-cmd.ts"],"names":[],"mappings":"AAAA;;GAEG;AA6BH,wBAA8B,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CA+GzE"}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Search command - search for sessions
|
|
3
|
+
*/
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import { openDatabase, search } from "../storage/db";
|
|
6
|
+
import { loadConfig } from "../utils/config";
|
|
7
|
+
import { parseRelativeDate } from "../utils/date";
|
|
8
|
+
import { getXDGPaths } from "../utils/xdg";
|
|
9
|
+
function normalizeScore(rawScore) {
|
|
10
|
+
// BM25 returns negative scores where more negative = better match
|
|
11
|
+
// Convert to 0-1 range for display
|
|
12
|
+
const normalized = Math.min(1, Math.abs(rawScore) / 20);
|
|
13
|
+
return normalized.toFixed(2);
|
|
14
|
+
}
|
|
15
|
+
function printUsage() {
|
|
16
|
+
console.log(`Usage: sesame search <query> [options]
|
|
17
|
+
|
|
18
|
+
Options:
|
|
19
|
+
--cwd <path> Filter by project directory
|
|
20
|
+
--after <date> Filter sessions after date (7d, 2w, 1m, or ISO date)
|
|
21
|
+
--before <date> Filter sessions before date
|
|
22
|
+
--limit <n> Max results (default: 10)
|
|
23
|
+
--tools Search only tool call chunks
|
|
24
|
+
--tool <name> Search specific tool type
|
|
25
|
+
--path <file> Find sessions that touched a file
|
|
26
|
+
--json Output as JSON`);
|
|
27
|
+
}
|
|
28
|
+
export default async function searchCommand(args) {
|
|
29
|
+
// Parse arguments
|
|
30
|
+
let query;
|
|
31
|
+
const options = {
|
|
32
|
+
limit: 10,
|
|
33
|
+
};
|
|
34
|
+
for (let i = 0; i < args.length; i++) {
|
|
35
|
+
const arg = args[i];
|
|
36
|
+
if (arg === "--cwd") {
|
|
37
|
+
options.cwd = args[++i];
|
|
38
|
+
}
|
|
39
|
+
else if (arg === "--after") {
|
|
40
|
+
const dateStr = args[++i];
|
|
41
|
+
options.after = parseRelativeDate(dateStr);
|
|
42
|
+
}
|
|
43
|
+
else if (arg === "--before") {
|
|
44
|
+
const dateStr = args[++i];
|
|
45
|
+
options.before = parseRelativeDate(dateStr);
|
|
46
|
+
}
|
|
47
|
+
else if (arg === "--limit") {
|
|
48
|
+
options.limit = Number.parseInt(args[++i], 10);
|
|
49
|
+
}
|
|
50
|
+
else if (arg === "--tools") {
|
|
51
|
+
options.toolsOnly = true;
|
|
52
|
+
}
|
|
53
|
+
else if (arg === "--tool") {
|
|
54
|
+
options.toolName = args[++i];
|
|
55
|
+
}
|
|
56
|
+
else if (arg === "--path") {
|
|
57
|
+
options.pathFilter = args[++i];
|
|
58
|
+
}
|
|
59
|
+
else if (arg === "--json") {
|
|
60
|
+
options.json = true;
|
|
61
|
+
}
|
|
62
|
+
else if (!arg.startsWith("-")) {
|
|
63
|
+
query = arg;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
// Validate query
|
|
67
|
+
if (!query) {
|
|
68
|
+
printUsage();
|
|
69
|
+
process.exit(1);
|
|
70
|
+
}
|
|
71
|
+
// Load config (not strictly needed for search, but keeps consistency)
|
|
72
|
+
await loadConfig();
|
|
73
|
+
// Open database
|
|
74
|
+
const paths = getXDGPaths();
|
|
75
|
+
const dbPath = join(paths.data, "index.sqlite");
|
|
76
|
+
const db = openDatabase(dbPath);
|
|
77
|
+
try {
|
|
78
|
+
const results = search(db, query, options);
|
|
79
|
+
if (results.length === 0) {
|
|
80
|
+
if (options.json) {
|
|
81
|
+
console.log(JSON.stringify({
|
|
82
|
+
query,
|
|
83
|
+
resultCount: 0,
|
|
84
|
+
results: [],
|
|
85
|
+
}, null, 2));
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
console.log(`No sessions found matching "${query}"`);
|
|
89
|
+
}
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
// Output results
|
|
93
|
+
if (options.json) {
|
|
94
|
+
console.log(JSON.stringify({
|
|
95
|
+
query,
|
|
96
|
+
resultCount: results.length,
|
|
97
|
+
results: results.map((r) => ({
|
|
98
|
+
sessionId: r.sessionId,
|
|
99
|
+
source: r.source,
|
|
100
|
+
path: r.path,
|
|
101
|
+
cwd: r.cwd,
|
|
102
|
+
name: r.name,
|
|
103
|
+
score: Number.parseFloat(normalizeScore(r.score)),
|
|
104
|
+
created: r.createdAt,
|
|
105
|
+
matchedSnippet: r.matchedSnippet,
|
|
106
|
+
})),
|
|
107
|
+
}, null, 2));
|
|
108
|
+
}
|
|
109
|
+
else {
|
|
110
|
+
console.log(`Found ${results.length} sessions matching "${query}"\n`);
|
|
111
|
+
for (const result of results) {
|
|
112
|
+
const score = normalizeScore(result.score);
|
|
113
|
+
const name = result.name || "Unnamed";
|
|
114
|
+
const date = result.createdAt
|
|
115
|
+
? new Date(result.createdAt).toISOString().split("T")[0]
|
|
116
|
+
: "unknown";
|
|
117
|
+
console.log(` [${score}] ${result.sessionId} (${name}) - ${date}`);
|
|
118
|
+
if (result.cwd) {
|
|
119
|
+
console.log(` ${result.cwd}`);
|
|
120
|
+
}
|
|
121
|
+
console.log(` "${result.matchedSnippet}"\n`);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
finally {
|
|
126
|
+
db.close();
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
//# sourceMappingURL=search-cmd.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"search-cmd.js","sourceRoot":"","sources":["../../src/commands/search-cmd.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"status-cmd.d.ts","sourceRoot":"","sources":["../../src/commands/status-cmd.ts"],"names":[],"mappings":"AAAA;;GAEG;AAMH,wBAA8B,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAuB1E"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Status command - shows index statistics
|
|
3
|
+
*/
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import { getStats, openDatabase } from "../storage/db";
|
|
6
|
+
import { getXDGPaths } from "../utils/xdg";
|
|
7
|
+
export default async function statusCommand(_args) {
|
|
8
|
+
const paths = getXDGPaths();
|
|
9
|
+
const dbPath = join(paths.data, "index.sqlite");
|
|
10
|
+
const db = openDatabase(dbPath);
|
|
11
|
+
try {
|
|
12
|
+
const stats = getStats(db);
|
|
13
|
+
// Format database size
|
|
14
|
+
const sizeMB = (stats.dbSizeBytes / (1024 * 1024)).toFixed(1);
|
|
15
|
+
// Format numbers with thousands separators
|
|
16
|
+
const formatNumber = (n) => n.toLocaleString();
|
|
17
|
+
console.log("Sesame Index Status");
|
|
18
|
+
console.log(` Sessions: ${formatNumber(stats.sessionCount)}`);
|
|
19
|
+
console.log(` Chunks: ${formatNumber(stats.chunkCount)}`);
|
|
20
|
+
console.log(` Database: ${sizeMB} MB`);
|
|
21
|
+
console.log(` Location: ${dbPath}`);
|
|
22
|
+
}
|
|
23
|
+
finally {
|
|
24
|
+
db.close();
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=status-cmd.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"status-cmd.js","sourceRoot":"","sources":["../../src/commands/status-cmd.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AACvD,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAE3C,MAAM,CAAC,OAAO,CAAC,KAAK,UAAU,aAAa,CAAC,KAAe;IACzD,MAAM,KAAK,GAAG,WAAW,EAAE,CAAC;IAC5B,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,cAAc,CAAC,CAAC;IAEhD,MAAM,EAAE,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC;IAEhC,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,QAAQ,CAAC,EAAE,CAAC,CAAC;QAE3B,uBAAuB;QACvB,MAAM,MAAM,GAAG,CAAC,KAAK,CAAC,WAAW,GAAG,CAAC,IAAI,GAAG,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;QAE9D,2CAA2C;QAC3C,MAAM,YAAY,GAAG,CAAC,CAAS,EAAE,EAAE,CAAC,CAAC,CAAC,cAAc,EAAE,CAAC;QAEvD,OAAO,CAAC,GAAG,CAAC,qBAAqB,CAAC,CAAC;QACnC,OAAO,CAAC,GAAG,CAAC,eAAe,YAAY,CAAC,KAAK,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC;QAC/D,OAAO,CAAC,GAAG,CAAC,eAAe,YAAY,CAAC,KAAK,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC;QAC7D,OAAO,CAAC,GAAG,CAAC,eAAe,MAAM,KAAK,CAAC,CAAC;QACxC,OAAO,CAAC,GAAG,CAAC,eAAe,MAAM,EAAE,CAAC,CAAC;IACvC,CAAC;YAAS,CAAC;QACT,EAAE,CAAC,KAAK,EAAE,CAAC;IACb,CAAC;AACH,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sesame library entry point
|
|
3
|
+
*/
|
|
4
|
+
export type { IndexResult } from "./indexer/index";
|
|
5
|
+
export { indexSessions } from "./indexer/index";
|
|
6
|
+
export { PiParser } from "./parsers/pi";
|
|
7
|
+
export type { SearchOptions, SearchResult, StoredChunk, StoredSession, } from "./storage/db";
|
|
8
|
+
export { deleteSession, dropAll, getSessionMtime, getStats, insertSession, openDatabase, search, } from "./storage/db";
|
|
9
|
+
export type { ParsedSession, SessionParser, ToolCall, Turn, } from "./types/session";
|
|
10
|
+
export type { SesameConfig, SessionSource } from "./utils/config";
|
|
11
|
+
export { expandPath, loadConfig } from "./utils/config";
|
|
12
|
+
export { parseRelativeDate } from "./utils/date";
|
|
13
|
+
export { getXDGPaths } from "./utils/xdg";
|
|
14
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,YAAY,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AACnD,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AACxC,YAAY,EACV,aAAa,EACb,YAAY,EACZ,WAAW,EACX,aAAa,GACd,MAAM,cAAc,CAAC;AAEtB,OAAO,EACL,aAAa,EACb,OAAO,EACP,eAAe,EACf,QAAQ,EACR,aAAa,EACb,YAAY,EACZ,MAAM,GACP,MAAM,cAAc,CAAC;AACtB,YAAY,EACV,aAAa,EACb,aAAa,EACb,QAAQ,EACR,IAAI,GACL,MAAM,iBAAiB,CAAC;AACzB,YAAY,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAClE,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AACxD,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AACjD,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sesame library entry point
|
|
3
|
+
*/
|
|
4
|
+
export { indexSessions } from "./indexer/index";
|
|
5
|
+
export { PiParser } from "./parsers/pi";
|
|
6
|
+
export { deleteSession, dropAll, getSessionMtime, getStats, insertSession, openDatabase, search, } from "./storage/db";
|
|
7
|
+
export { expandPath, loadConfig } from "./utils/config";
|
|
8
|
+
export { parseRelativeDate } from "./utils/date";
|
|
9
|
+
export { getXDGPaths } from "./utils/xdg";
|
|
10
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAQxC,OAAO,EACL,aAAa,EACb,OAAO,EACP,eAAe,EACf,QAAQ,EACR,aAAa,EACb,YAAY,EACZ,MAAM,GACP,MAAM,cAAc,CAAC;AAQtB,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AACxD,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AACjD,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { ToolCall } from "../types/session";
|
|
2
|
+
/**
|
|
3
|
+
* Format a tool call into structured searchable text.
|
|
4
|
+
*
|
|
5
|
+
* Each tool type gets a specific format optimized for search:
|
|
6
|
+
* - write/create: tool name + path + content
|
|
7
|
+
* - edit: tool name + path + old/new
|
|
8
|
+
* - bash: tool name + command + output
|
|
9
|
+
* - read: tool name + path + content
|
|
10
|
+
* - generic: tool name + args + result
|
|
11
|
+
*/
|
|
12
|
+
export declare function formatToolCall(tc: ToolCall): string;
|
|
13
|
+
/**
|
|
14
|
+
* Extract paths from a tool call for path-based search.
|
|
15
|
+
* Returns all file paths found in the tool call args.
|
|
16
|
+
*/
|
|
17
|
+
export declare function extractPaths(tc: ToolCall): string[];
|
|
18
|
+
//# sourceMappingURL=format-tool-call.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"format-tool-call.d.ts","sourceRoot":"","sources":["../../src/indexer/format-tool-call.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAEjD;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAAC,EAAE,EAAE,QAAQ,GAAG,MAAM,CAoFnD;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,EAAE,EAAE,QAAQ,GAAG,MAAM,EAAE,CAKnD"}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Format a tool call into structured searchable text.
|
|
3
|
+
*
|
|
4
|
+
* Each tool type gets a specific format optimized for search:
|
|
5
|
+
* - write/create: tool name + path + content
|
|
6
|
+
* - edit: tool name + path + old/new
|
|
7
|
+
* - bash: tool name + command + output
|
|
8
|
+
* - read: tool name + path + content
|
|
9
|
+
* - generic: tool name + args + result
|
|
10
|
+
*/
|
|
11
|
+
export function formatToolCall(tc) {
|
|
12
|
+
const name = tc.name.toLowerCase();
|
|
13
|
+
switch (name) {
|
|
14
|
+
case "write":
|
|
15
|
+
case "create":
|
|
16
|
+
case "write_file":
|
|
17
|
+
case "create_file": {
|
|
18
|
+
const path = extractArg(tc, ["path", "file_path", "filePath"]);
|
|
19
|
+
const content = extractArg(tc, [
|
|
20
|
+
"content",
|
|
21
|
+
"file_content",
|
|
22
|
+
"fileContent",
|
|
23
|
+
]);
|
|
24
|
+
return buildText([
|
|
25
|
+
`tool: ${tc.name}`,
|
|
26
|
+
path ? `path: ${path}` : null,
|
|
27
|
+
content ? `content:\n${content}` : null,
|
|
28
|
+
tc.result ? `result:\n${tc.result}` : null,
|
|
29
|
+
]);
|
|
30
|
+
}
|
|
31
|
+
case "edit":
|
|
32
|
+
case "edit_file": {
|
|
33
|
+
const path = extractArg(tc, ["path", "file_path", "filePath"]);
|
|
34
|
+
const oldText = extractArg(tc, [
|
|
35
|
+
"old",
|
|
36
|
+
"oldText",
|
|
37
|
+
"old_text",
|
|
38
|
+
"old_string",
|
|
39
|
+
"search",
|
|
40
|
+
]);
|
|
41
|
+
const newText = extractArg(tc, [
|
|
42
|
+
"new",
|
|
43
|
+
"newText",
|
|
44
|
+
"new_text",
|
|
45
|
+
"new_string",
|
|
46
|
+
"replace",
|
|
47
|
+
]);
|
|
48
|
+
return buildText([
|
|
49
|
+
`tool: ${tc.name}`,
|
|
50
|
+
path ? `path: ${path}` : null,
|
|
51
|
+
oldText ? `old:\n${oldText}` : null,
|
|
52
|
+
newText ? `new:\n${newText}` : null,
|
|
53
|
+
tc.result ? `result:\n${tc.result}` : null,
|
|
54
|
+
]);
|
|
55
|
+
}
|
|
56
|
+
case "bash":
|
|
57
|
+
case "shell":
|
|
58
|
+
case "run_command": {
|
|
59
|
+
const command = extractArg(tc, ["command", "cmd", "script"]);
|
|
60
|
+
const output = tc.result;
|
|
61
|
+
return buildText([
|
|
62
|
+
`tool: ${tc.name}`,
|
|
63
|
+
command ? `command: ${command}` : null,
|
|
64
|
+
output ? `output:\n${output}` : null,
|
|
65
|
+
]);
|
|
66
|
+
}
|
|
67
|
+
case "read":
|
|
68
|
+
case "read_file": {
|
|
69
|
+
const path = extractArg(tc, ["path", "file_path", "filePath"]);
|
|
70
|
+
return buildText([
|
|
71
|
+
`tool: ${tc.name}`,
|
|
72
|
+
path ? `path: ${path}` : null,
|
|
73
|
+
tc.result ? `content:\n${tc.result}` : null,
|
|
74
|
+
]);
|
|
75
|
+
}
|
|
76
|
+
default: {
|
|
77
|
+
// Generic: dump all args + result
|
|
78
|
+
const argsText = Object.entries(tc.args)
|
|
79
|
+
.map(([k, v]) => `${k}: ${typeof v === "string" ? v : JSON.stringify(v)}`)
|
|
80
|
+
.join("\n");
|
|
81
|
+
return buildText([
|
|
82
|
+
`tool: ${tc.name}`,
|
|
83
|
+
argsText || null,
|
|
84
|
+
tc.result ? `result:\n${tc.result}` : null,
|
|
85
|
+
]);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Extract paths from a tool call for path-based search.
|
|
91
|
+
* Returns all file paths found in the tool call args.
|
|
92
|
+
*/
|
|
93
|
+
export function extractPaths(tc) {
|
|
94
|
+
const paths = [];
|
|
95
|
+
const pathValue = extractArg(tc, ["path", "file_path", "filePath"]);
|
|
96
|
+
if (pathValue)
|
|
97
|
+
paths.push(pathValue);
|
|
98
|
+
return paths;
|
|
99
|
+
}
|
|
100
|
+
function extractArg(tc, keys) {
|
|
101
|
+
for (const key of keys) {
|
|
102
|
+
const val = tc.args[key];
|
|
103
|
+
if (val != null) {
|
|
104
|
+
return typeof val === "string" ? val : JSON.stringify(val);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return null;
|
|
108
|
+
}
|
|
109
|
+
function buildText(parts) {
|
|
110
|
+
return parts.filter(Boolean).join("\n");
|
|
111
|
+
}
|
|
112
|
+
//# sourceMappingURL=format-tool-call.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"format-tool-call.js","sourceRoot":"","sources":["../../src/indexer/format-tool-call.ts"],"names":[],"mappings":"AAEA;;;;;;;;;GASG;AACH,MAAM,UAAU,cAAc,CAAC,EAAY;IACzC,MAAM,IAAI,GAAG,EAAE,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;IAEnC,QAAQ,IAAI,EAAE,CAAC;QACb,KAAK,OAAO,CAAC;QACb,KAAK,QAAQ,CAAC;QACd,KAAK,YAAY,CAAC;QAClB,KAAK,aAAa,CAAC,CAAC,CAAC;YACnB,MAAM,IAAI,GAAG,UAAU,CAAC,EAAE,EAAE,CAAC,MAAM,EAAE,WAAW,EAAE,UAAU,CAAC,CAAC,CAAC;YAC/D,MAAM,OAAO,GAAG,UAAU,CAAC,EAAE,EAAE;gBAC7B,SAAS;gBACT,cAAc;gBACd,aAAa;aACd,CAAC,CAAC;YACH,OAAO,SAAS,CAAC;gBACf,SAAS,EAAE,CAAC,IAAI,EAAE;gBAClB,IAAI,CAAC,CAAC,CAAC,SAAS,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI;gBAC7B,OAAO,CAAC,CAAC,CAAC,aAAa,OAAO,EAAE,CAAC,CAAC,CAAC,IAAI;gBACvC,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,YAAY,EAAE,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,IAAI;aAC3C,CAAC,CAAC;QACL,CAAC;QAED,KAAK,MAAM,CAAC;QACZ,KAAK,WAAW,CAAC,CAAC,CAAC;YACjB,MAAM,IAAI,GAAG,UAAU,CAAC,EAAE,EAAE,CAAC,MAAM,EAAE,WAAW,EAAE,UAAU,CAAC,CAAC,CAAC;YAC/D,MAAM,OAAO,GAAG,UAAU,CAAC,EAAE,EAAE;gBAC7B,KAAK;gBACL,SAAS;gBACT,UAAU;gBACV,YAAY;gBACZ,QAAQ;aACT,CAAC,CAAC;YACH,MAAM,OAAO,GAAG,UAAU,CAAC,EAAE,EAAE;gBAC7B,KAAK;gBACL,SAAS;gBACT,UAAU;gBACV,YAAY;gBACZ,SAAS;aACV,CAAC,CAAC;YACH,OAAO,SAAS,CAAC;gBACf,SAAS,EAAE,CAAC,IAAI,EAAE;gBAClB,IAAI,CAAC,CAAC,CAAC,SAAS,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI;gBAC7B,OAAO,CAAC,CAAC,CAAC,SAAS,OAAO,EAAE,CAAC,CAAC,CAAC,IAAI;gBACnC,OAAO,CAAC,CAAC,CAAC,SAAS,OAAO,EAAE,CAAC,CAAC,CAAC,IAAI;gBACnC,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,YAAY,EAAE,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,IAAI;aAC3C,CAAC,CAAC;QACL,CAAC;QAED,KAAK,MAAM,CAAC;QACZ,KAAK,OAAO,CAAC;QACb,KAAK,aAAa,CAAC,CAAC,CAAC;YACnB,MAAM,OAAO,GAAG,UAAU,CAAC,EAAE,EAAE,CAAC,SAAS,EAAE,KAAK,EAAE,QAAQ,CAAC,CAAC,CAAC;YAC7D,MAAM,MAAM,GAAG,EAAE,CAAC,MAAM,CAAC;YACzB,OAAO,SAAS,CAAC;gBACf,SAAS,EAAE,CAAC,IAAI,EAAE;gBAClB,OAAO,CAAC,CAAC,CAAC,YAAY,OAAO,EAAE,CAAC,CAAC,CAAC,IAAI;gBACtC,MAAM,CAAC,CAAC,CAAC,YAAY,MAAM,EAAE,CAAC,CAAC,CAAC,IAAI;aACrC,CAAC,CAAC;QACL,CAAC;QAED,KAAK,MAAM,CAAC;QACZ,KAAK,WAAW,CAAC,CAAC,CAAC;YACjB,MAAM,IAAI,GAAG,UAAU,CAAC,EAAE,EAAE,CAAC,MAAM,EAAE,WAAW,EAAE,UAAU,CAAC,CAAC,CAAC;YAC/D,OAAO,SAAS,CAAC;gBACf,SAAS,EAAE,CAAC,IAAI,EAAE;gBAClB,IAAI,CAAC,CAAC,CAAC,SAAS,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI;gBAC7B,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,aAAa,EAAE,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,IAAI;aAC5C,CAAC,CAAC;QACL,CAAC;QAED,OAAO,CAAC,CAAC,CAAC;YACR,kCAAkC;YAClC,MAAM,QAAQ,GAAG,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC,IAAI,CAAC;iBACrC,GAAG,CACF,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,KAAK,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CACrE;iBACA,IAAI,CAAC,IAAI,CAAC,CAAC;YACd,OAAO,SAAS,CAAC;gBACf,SAAS,EAAE,CAAC,IAAI,EAAE;gBAClB,QAAQ,IAAI,IAAI;gBAChB,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,YAAY,EAAE,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,IAAI;aAC3C,CAAC,CAAC;QACL,CAAC;IACH,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,YAAY,CAAC,EAAY;IACvC,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,MAAM,SAAS,GAAG,UAAU,CAAC,EAAE,EAAE,CAAC,MAAM,EAAE,WAAW,EAAE,UAAU,CAAC,CAAC,CAAC;IACpE,IAAI,SAAS;QAAE,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IACrC,OAAO,KAAK,CAAC;AACf,CAAC;AAED,SAAS,UAAU,CAAC,EAAY,EAAE,IAAc;IAC9C,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;QACvB,MAAM,GAAG,GAAG,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACzB,IAAI,GAAG,IAAI,IAAI,EAAE,CAAC;YAChB,OAAO,OAAO,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;QAC7D,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,SAAS,SAAS,CAAC,KAAwB;IACzC,OAAO,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1C,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type Database } from "../storage/db";
|
|
2
|
+
import type { SessionParser } from "../types/session";
|
|
3
|
+
export interface IndexResult {
|
|
4
|
+
added: number;
|
|
5
|
+
updated: number;
|
|
6
|
+
skipped: number;
|
|
7
|
+
errors: number;
|
|
8
|
+
}
|
|
9
|
+
export declare function indexSessions(db: Database, sessionsDir: string, parser: SessionParser): Promise<IndexResult>;
|
|
10
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/indexer/index.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,KAAK,QAAQ,EAMd,MAAM,eAAe,CAAC;AACvB,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAatD,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,wBAAsB,aAAa,CACjC,EAAE,EAAE,QAAQ,EACZ,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,aAAa,GACpB,OAAO,CAAC,WAAW,CAAC,CAsItB"}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { readdirSync, readFileSync, statSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { deleteSession, getSessionMtime, insertSession, } from "../storage/db";
|
|
4
|
+
import { formatToolCall } from "./format-tool-call";
|
|
5
|
+
function readFirstLine(filePath) {
|
|
6
|
+
try {
|
|
7
|
+
const text = readFileSync(filePath, "utf-8");
|
|
8
|
+
const newlineIndex = text.indexOf("\n");
|
|
9
|
+
return newlineIndex === -1 ? text : text.slice(0, newlineIndex);
|
|
10
|
+
}
|
|
11
|
+
catch {
|
|
12
|
+
return null;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
export async function indexSessions(db, sessionsDir, parser) {
|
|
16
|
+
const result = {
|
|
17
|
+
added: 0,
|
|
18
|
+
updated: 0,
|
|
19
|
+
skipped: 0,
|
|
20
|
+
errors: 0,
|
|
21
|
+
};
|
|
22
|
+
// Collect all candidate files, recursing one level into subdirectories.
|
|
23
|
+
// Pi stores sessions under encoded-path subdirectories:
|
|
24
|
+
// ~/.pi/agent/sessions/<encoded-cwd>/<session-id>.jsonl
|
|
25
|
+
let filePaths;
|
|
26
|
+
try {
|
|
27
|
+
filePaths = [];
|
|
28
|
+
for (const entry of readdirSync(sessionsDir, { withFileTypes: true })) {
|
|
29
|
+
const entryPath = join(sessionsDir, entry.name);
|
|
30
|
+
if (entry.isDirectory()) {
|
|
31
|
+
try {
|
|
32
|
+
for (const child of readdirSync(entryPath)) {
|
|
33
|
+
filePaths.push(join(entryPath, child));
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
// Skip unreadable subdirectories
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
filePaths.push(entryPath);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
catch (error) {
|
|
46
|
+
console.error(`Failed to read directory ${sessionsDir}:`, error);
|
|
47
|
+
return result;
|
|
48
|
+
}
|
|
49
|
+
for (const filePath of filePaths) {
|
|
50
|
+
// Skip if parser can't handle this file
|
|
51
|
+
const canParse = await parser.canParse(filePath);
|
|
52
|
+
if (!canParse) {
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
try {
|
|
56
|
+
// Get file mtime
|
|
57
|
+
const stats = statSync(filePath);
|
|
58
|
+
const fileMtime = Math.floor(stats.mtimeMs);
|
|
59
|
+
// Quick mtime check: read just the session ID from first line
|
|
60
|
+
const firstLine = readFirstLine(filePath);
|
|
61
|
+
if (firstLine) {
|
|
62
|
+
const header = JSON.parse(firstLine);
|
|
63
|
+
const sessionId = header.id;
|
|
64
|
+
if (sessionId) {
|
|
65
|
+
const storedMtime = getSessionMtime(db, sessionId);
|
|
66
|
+
if (storedMtime !== null && storedMtime === fileMtime) {
|
|
67
|
+
result.skipped++;
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
// Parse the full session
|
|
73
|
+
console.error(`Indexing ${filePath}...`);
|
|
74
|
+
const parsedSession = await parser.parse(filePath);
|
|
75
|
+
// Check if session exists (may not have been caught above)
|
|
76
|
+
const storedMtime = getSessionMtime(db, parsedSession.id);
|
|
77
|
+
// Delete old data if exists
|
|
78
|
+
if (storedMtime !== null) {
|
|
79
|
+
deleteSession(db, parsedSession.id);
|
|
80
|
+
}
|
|
81
|
+
// Build stored session
|
|
82
|
+
const storedSession = {
|
|
83
|
+
id: parsedSession.id,
|
|
84
|
+
source: parsedSession.source,
|
|
85
|
+
path: filePath,
|
|
86
|
+
cwd: parsedSession.cwd ?? null,
|
|
87
|
+
name: parsedSession.name ?? null,
|
|
88
|
+
created_at: parsedSession.createdAt ?? null,
|
|
89
|
+
modified_at: parsedSession.modifiedAt ?? null,
|
|
90
|
+
message_count: parsedSession.turns.length,
|
|
91
|
+
file_mtime: fileMtime,
|
|
92
|
+
};
|
|
93
|
+
// Build chunks from turns: message chunks + tool call chunks
|
|
94
|
+
const chunks = [];
|
|
95
|
+
let seq = 0;
|
|
96
|
+
for (const turn of parsedSession.turns) {
|
|
97
|
+
// Message chunk
|
|
98
|
+
if (turn.textContent.trim()) {
|
|
99
|
+
chunks.push({
|
|
100
|
+
id: 0,
|
|
101
|
+
session_id: parsedSession.id,
|
|
102
|
+
kind: "message",
|
|
103
|
+
role: turn.role,
|
|
104
|
+
tool_name: null,
|
|
105
|
+
seq: seq++,
|
|
106
|
+
content: turn.textContent,
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
// Tool call chunks
|
|
110
|
+
for (const tc of turn.toolCalls) {
|
|
111
|
+
const content = formatToolCall(tc);
|
|
112
|
+
if (content.trim()) {
|
|
113
|
+
chunks.push({
|
|
114
|
+
id: 0,
|
|
115
|
+
session_id: parsedSession.id,
|
|
116
|
+
kind: "tool_call",
|
|
117
|
+
role: null,
|
|
118
|
+
tool_name: tc.name,
|
|
119
|
+
seq: seq++,
|
|
120
|
+
content,
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
// Insert into database
|
|
126
|
+
insertSession(db, storedSession, chunks);
|
|
127
|
+
// Track if this was new or updated
|
|
128
|
+
if (storedMtime === null) {
|
|
129
|
+
result.added++;
|
|
130
|
+
}
|
|
131
|
+
else {
|
|
132
|
+
result.updated++;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
catch (error) {
|
|
136
|
+
console.error(`Error indexing ${filePath}:`, error);
|
|
137
|
+
result.errors++;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
return result;
|
|
141
|
+
}
|
|
142
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/indexer/index.ts"],"names":[],"mappings":"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"}
|