trellis 2.0.7 → 2.0.10
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/cli/index.js +1031 -30
- package/dist/core/index.js +474 -2
- package/dist/decisions/index.js +5 -2
- package/dist/embeddings/index.js +5 -1
- package/dist/{index-3s0eak0p.js → index-3ejh8k6v.js} +26 -3
- package/dist/{index-1j1anhmr.js → index-5b01h414.js} +489 -335
- package/dist/index-5m0g9r0y.js +1100 -0
- package/dist/{index-8pce39mh.js → index-65z0xfjw.js} +17 -3
- package/dist/{index-zf6htvnm.js → index-7gvjxt27.js} +166 -2
- package/dist/index-hybgxe40.js +1174 -0
- package/dist/{index-gnw8d7d6.js → index-k5kf7sd0.js} +32 -3
- package/dist/{index-fd4e26s4.js → index-v9b4hqa7.js} +23 -15
- package/dist/index.js +20 -7
- package/dist/transformers.node-bx3q9d7k.js +33130 -0
- package/dist/ui/client.html +695 -0
- package/dist/vcs/index.js +3 -3
- package/package.json +5 -4
- package/src/cli/index.ts +1017 -1
- package/src/core/agents/harness.ts +336 -0
- package/src/core/agents/index.ts +18 -0
- package/src/core/agents/types.ts +90 -0
- package/src/core/index.ts +85 -2
- package/src/core/kernel/trellis-kernel.ts +593 -0
- package/src/core/ontology/builtins.ts +248 -0
- package/src/core/ontology/index.ts +34 -0
- package/src/core/ontology/registry.ts +209 -0
- package/src/core/ontology/types.ts +124 -0
- package/src/core/ontology/validator.ts +382 -0
- package/src/core/persist/backend.ts +10 -0
- package/src/core/persist/sqlite-backend.ts +298 -0
- package/src/core/plugins/index.ts +17 -0
- package/src/core/plugins/registry.ts +322 -0
- package/src/core/plugins/types.ts +126 -0
- package/src/core/query/datalog.ts +188 -0
- package/src/core/query/engine.ts +370 -0
- package/src/core/query/index.ts +34 -0
- package/src/core/query/parser.ts +481 -0
- package/src/core/query/types.ts +200 -0
- package/src/embeddings/auto-embed.ts +248 -0
- package/src/embeddings/index.ts +7 -0
- package/src/embeddings/model.ts +21 -4
- package/src/embeddings/types.ts +8 -1
- package/src/engine.ts +45 -3
- package/src/index.ts +9 -0
- package/src/sync/http-transport.ts +144 -0
- package/src/sync/index.ts +11 -0
- package/src/sync/multi-repo.ts +200 -0
- package/src/sync/ws-transport.ts +145 -0
- package/src/ui/client.html +695 -0
- package/src/ui/server.ts +419 -0
- package/src/watcher/fs-watcher.ts +41 -3
- package/dist/index-gkvhzm9f.js +0 -321
package/dist/cli/index.js
CHANGED
|
@@ -1,22 +1,43 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
2
|
// @bun
|
|
3
3
|
import {
|
|
4
|
-
TrellisVcsEngine
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
import"../index-3s0eak0p.js";
|
|
4
|
+
TrellisVcsEngine,
|
|
5
|
+
init_engine
|
|
6
|
+
} from "../index-5b01h414.js";
|
|
8
7
|
import {
|
|
8
|
+
OntologyRegistry,
|
|
9
|
+
builtinOntologies,
|
|
10
|
+
parseQuery,
|
|
11
|
+
parseSimple,
|
|
12
|
+
validateStore
|
|
13
|
+
} from "../index-5m0g9r0y.js";
|
|
14
|
+
import {
|
|
15
|
+
QueryEngine,
|
|
16
|
+
SqliteKernelBackend,
|
|
17
|
+
TrellisKernel
|
|
18
|
+
} from "../index-hybgxe40.js";
|
|
19
|
+
import"../index-3ejh8k6v.js";
|
|
20
|
+
import {
|
|
21
|
+
VectorStore,
|
|
22
|
+
buildRAGContext,
|
|
23
|
+
embed,
|
|
9
24
|
exports_embeddings,
|
|
10
|
-
|
|
11
|
-
|
|
25
|
+
init_auto_embed,
|
|
26
|
+
init_embeddings,
|
|
27
|
+
init_model,
|
|
28
|
+
init_store
|
|
29
|
+
} from "../index-7gvjxt27.js";
|
|
12
30
|
import {
|
|
31
|
+
buildRefIndex,
|
|
32
|
+
createResolverContext,
|
|
13
33
|
exports_links,
|
|
14
34
|
init_links
|
|
15
35
|
} from "../index-vkpkfwhq.js";
|
|
16
|
-
import"../index-
|
|
36
|
+
import"../index-65z0xfjw.js";
|
|
17
37
|
import {
|
|
18
|
-
createVcsOp
|
|
19
|
-
|
|
38
|
+
createVcsOp,
|
|
39
|
+
init_ops
|
|
40
|
+
} from "../index-v9b4hqa7.js";
|
|
20
41
|
import {
|
|
21
42
|
__commonJS,
|
|
22
43
|
__esm,
|
|
@@ -2261,6 +2282,303 @@ var init_reconciler = __esm(() => {
|
|
|
2261
2282
|
]);
|
|
2262
2283
|
});
|
|
2263
2284
|
|
|
2285
|
+
// src/ui/server.ts
|
|
2286
|
+
var exports_server = {};
|
|
2287
|
+
__export(exports_server, {
|
|
2288
|
+
startUIServer: () => startUIServer
|
|
2289
|
+
});
|
|
2290
|
+
import { readFileSync as readFileSync3, existsSync as existsSync5 } from "fs";
|
|
2291
|
+
import { join as join5, dirname as dirname4 } from "path";
|
|
2292
|
+
function buildGraph(engine) {
|
|
2293
|
+
const nodes = [];
|
|
2294
|
+
const edges = [];
|
|
2295
|
+
const nodeIds = new Set;
|
|
2296
|
+
const files = engine.trackedFiles();
|
|
2297
|
+
for (const f of files) {
|
|
2298
|
+
const id = `file:${f.path}`;
|
|
2299
|
+
nodes.push({
|
|
2300
|
+
id,
|
|
2301
|
+
label: f.path,
|
|
2302
|
+
type: "file",
|
|
2303
|
+
meta: { contentHash: f.contentHash }
|
|
2304
|
+
});
|
|
2305
|
+
nodeIds.add(id);
|
|
2306
|
+
}
|
|
2307
|
+
const milestones = engine.listMilestones();
|
|
2308
|
+
for (const m of milestones) {
|
|
2309
|
+
const id = `milestone:${m.id}`;
|
|
2310
|
+
nodes.push({
|
|
2311
|
+
id,
|
|
2312
|
+
label: m.message ?? m.id,
|
|
2313
|
+
type: "milestone",
|
|
2314
|
+
meta: {
|
|
2315
|
+
createdAt: m.createdAt,
|
|
2316
|
+
affectedFiles: m.affectedFiles,
|
|
2317
|
+
fromOpHash: m.fromOpHash,
|
|
2318
|
+
toOpHash: m.toOpHash
|
|
2319
|
+
}
|
|
2320
|
+
});
|
|
2321
|
+
nodeIds.add(id);
|
|
2322
|
+
for (const fp of m.affectedFiles ?? []) {
|
|
2323
|
+
const fileId = `file:${fp}`;
|
|
2324
|
+
if (nodeIds.has(fileId)) {
|
|
2325
|
+
edges.push({
|
|
2326
|
+
source: id,
|
|
2327
|
+
target: fileId,
|
|
2328
|
+
type: "milestone_file"
|
|
2329
|
+
});
|
|
2330
|
+
}
|
|
2331
|
+
}
|
|
2332
|
+
}
|
|
2333
|
+
const issues = engine.listIssues();
|
|
2334
|
+
for (const iss of issues) {
|
|
2335
|
+
const id = `issue:${iss.id}`;
|
|
2336
|
+
nodes.push({
|
|
2337
|
+
id,
|
|
2338
|
+
label: iss.title ?? iss.id,
|
|
2339
|
+
type: "issue",
|
|
2340
|
+
meta: {
|
|
2341
|
+
status: iss.status,
|
|
2342
|
+
priority: iss.priority,
|
|
2343
|
+
labels: iss.labels,
|
|
2344
|
+
assignee: iss.assignee,
|
|
2345
|
+
createdAt: iss.createdAt,
|
|
2346
|
+
description: iss.description,
|
|
2347
|
+
criteria: iss.criteria
|
|
2348
|
+
}
|
|
2349
|
+
});
|
|
2350
|
+
nodeIds.add(id);
|
|
2351
|
+
if (iss.branchName) {
|
|
2352
|
+
const branchId = `branch:${iss.branchName}`;
|
|
2353
|
+
if (nodeIds.has(branchId)) {
|
|
2354
|
+
edges.push({
|
|
2355
|
+
source: id,
|
|
2356
|
+
target: branchId,
|
|
2357
|
+
type: "issue_branch"
|
|
2358
|
+
});
|
|
2359
|
+
}
|
|
2360
|
+
}
|
|
2361
|
+
}
|
|
2362
|
+
const branches = engine.listBranches();
|
|
2363
|
+
for (const b of branches) {
|
|
2364
|
+
const id = `branch:${b.name}`;
|
|
2365
|
+
if (!nodeIds.has(id)) {
|
|
2366
|
+
nodes.push({
|
|
2367
|
+
id,
|
|
2368
|
+
label: b.name,
|
|
2369
|
+
type: "branch",
|
|
2370
|
+
meta: {
|
|
2371
|
+
isCurrent: b.isCurrent,
|
|
2372
|
+
createdAt: b.createdAt
|
|
2373
|
+
}
|
|
2374
|
+
});
|
|
2375
|
+
nodeIds.add(id);
|
|
2376
|
+
}
|
|
2377
|
+
for (const iss of issues) {
|
|
2378
|
+
if (iss.branchName === b.name) {
|
|
2379
|
+
edges.push({
|
|
2380
|
+
source: `issue:${iss.id}`,
|
|
2381
|
+
target: id,
|
|
2382
|
+
type: "issue_branch"
|
|
2383
|
+
});
|
|
2384
|
+
}
|
|
2385
|
+
}
|
|
2386
|
+
}
|
|
2387
|
+
try {
|
|
2388
|
+
const mdFiles = [];
|
|
2389
|
+
for (const f of files) {
|
|
2390
|
+
if (f.path.endsWith(".md")) {
|
|
2391
|
+
const absPath = join5(engine.getRootPath(), f.path);
|
|
2392
|
+
if (existsSync5(absPath)) {
|
|
2393
|
+
mdFiles.push({
|
|
2394
|
+
path: f.path,
|
|
2395
|
+
content: readFileSync3(absPath, "utf-8")
|
|
2396
|
+
});
|
|
2397
|
+
}
|
|
2398
|
+
}
|
|
2399
|
+
}
|
|
2400
|
+
if (mdFiles.length > 0) {
|
|
2401
|
+
const ctx = createResolverContext({
|
|
2402
|
+
trackedFiles: () => files,
|
|
2403
|
+
listIssues: () => issues,
|
|
2404
|
+
listMilestones: () => milestones
|
|
2405
|
+
});
|
|
2406
|
+
const refIndex = buildRefIndex(mdFiles, ctx);
|
|
2407
|
+
for (const [filePath, refs] of refIndex.outgoing) {
|
|
2408
|
+
const sourceId = `file:${filePath}`;
|
|
2409
|
+
if (!nodeIds.has(sourceId))
|
|
2410
|
+
continue;
|
|
2411
|
+
for (const ref of refs) {
|
|
2412
|
+
const targetId = `${ref.namespace}:${ref.target}`;
|
|
2413
|
+
const candidateIds = [
|
|
2414
|
+
targetId,
|
|
2415
|
+
`file:${ref.target}`,
|
|
2416
|
+
`issue:${ref.target}`,
|
|
2417
|
+
`milestone:${ref.target}`
|
|
2418
|
+
];
|
|
2419
|
+
for (const cid of candidateIds) {
|
|
2420
|
+
if (nodeIds.has(cid) && cid !== sourceId) {
|
|
2421
|
+
edges.push({
|
|
2422
|
+
source: sourceId,
|
|
2423
|
+
target: cid,
|
|
2424
|
+
type: "wikilink",
|
|
2425
|
+
label: ref.target
|
|
2426
|
+
});
|
|
2427
|
+
break;
|
|
2428
|
+
}
|
|
2429
|
+
}
|
|
2430
|
+
}
|
|
2431
|
+
}
|
|
2432
|
+
}
|
|
2433
|
+
} catch {}
|
|
2434
|
+
return { nodes, edges };
|
|
2435
|
+
}
|
|
2436
|
+
function getNodeDetail(engine, nodeId) {
|
|
2437
|
+
const [type, ...rest] = nodeId.split(":");
|
|
2438
|
+
const id = rest.join(":");
|
|
2439
|
+
switch (type) {
|
|
2440
|
+
case "file": {
|
|
2441
|
+
const files = engine.trackedFiles();
|
|
2442
|
+
const file = files.find((f) => f.path === id);
|
|
2443
|
+
if (!file)
|
|
2444
|
+
return null;
|
|
2445
|
+
const ops = engine.log({ filePath: id, limit: 10 });
|
|
2446
|
+
return {
|
|
2447
|
+
type: "file",
|
|
2448
|
+
path: id,
|
|
2449
|
+
contentHash: file.contentHash,
|
|
2450
|
+
recentOps: ops.map((o) => ({
|
|
2451
|
+
kind: o.kind,
|
|
2452
|
+
timestamp: o.timestamp,
|
|
2453
|
+
hash: o.hash.slice(0, 24)
|
|
2454
|
+
}))
|
|
2455
|
+
};
|
|
2456
|
+
}
|
|
2457
|
+
case "milestone": {
|
|
2458
|
+
const milestones = engine.listMilestones();
|
|
2459
|
+
const m = milestones.find((ms) => ms.id === id);
|
|
2460
|
+
if (!m)
|
|
2461
|
+
return null;
|
|
2462
|
+
return { type: "milestone", ...m };
|
|
2463
|
+
}
|
|
2464
|
+
case "issue": {
|
|
2465
|
+
const issue = engine.getIssue(id);
|
|
2466
|
+
if (!issue)
|
|
2467
|
+
return null;
|
|
2468
|
+
return { type: "issue", ...issue };
|
|
2469
|
+
}
|
|
2470
|
+
case "branch": {
|
|
2471
|
+
const branches = engine.listBranches();
|
|
2472
|
+
const b = branches.find((br) => br.name === id);
|
|
2473
|
+
if (!b)
|
|
2474
|
+
return null;
|
|
2475
|
+
return { type: "branch", ...b };
|
|
2476
|
+
}
|
|
2477
|
+
default:
|
|
2478
|
+
return null;
|
|
2479
|
+
}
|
|
2480
|
+
}
|
|
2481
|
+
async function startUIServer(opts) {
|
|
2482
|
+
const engine = new TrellisVcsEngine({ rootPath: opts.rootPath });
|
|
2483
|
+
engine.open();
|
|
2484
|
+
const clientHtml = readFileSync3(join5(dirname4(new URL(import.meta.url).pathname), "client.html"), "utf-8");
|
|
2485
|
+
let embeddingManager = null;
|
|
2486
|
+
function getEmbeddingManager() {
|
|
2487
|
+
if (!embeddingManager) {
|
|
2488
|
+
try {
|
|
2489
|
+
const { EmbeddingManager } = (init_embeddings(), __toCommonJS(exports_embeddings));
|
|
2490
|
+
const dbPath = join5(opts.rootPath, ".trellis", "embeddings.db");
|
|
2491
|
+
if (existsSync5(dbPath)) {
|
|
2492
|
+
embeddingManager = new EmbeddingManager(dbPath);
|
|
2493
|
+
}
|
|
2494
|
+
} catch {}
|
|
2495
|
+
}
|
|
2496
|
+
return embeddingManager;
|
|
2497
|
+
}
|
|
2498
|
+
const requestedPort = opts.port ?? 3333;
|
|
2499
|
+
const server = Bun.serve({
|
|
2500
|
+
port: requestedPort,
|
|
2501
|
+
async fetch(req) {
|
|
2502
|
+
const url = new URL(req.url);
|
|
2503
|
+
const path = url.pathname;
|
|
2504
|
+
const headers = {
|
|
2505
|
+
"Access-Control-Allow-Origin": "*",
|
|
2506
|
+
"Access-Control-Allow-Methods": "GET, OPTIONS",
|
|
2507
|
+
"Access-Control-Allow-Headers": "Content-Type"
|
|
2508
|
+
};
|
|
2509
|
+
if (req.method === "OPTIONS") {
|
|
2510
|
+
return new Response(null, { status: 204, headers });
|
|
2511
|
+
}
|
|
2512
|
+
if (path === "/api/graph") {
|
|
2513
|
+
const graph = buildGraph(engine);
|
|
2514
|
+
return Response.json(graph, { headers });
|
|
2515
|
+
}
|
|
2516
|
+
if (path === "/api/search") {
|
|
2517
|
+
const query = url.searchParams.get("q");
|
|
2518
|
+
if (!query) {
|
|
2519
|
+
return Response.json({ error: "Missing ?q= parameter" }, { status: 400, headers });
|
|
2520
|
+
}
|
|
2521
|
+
const limit = parseInt(url.searchParams.get("limit") ?? "10", 10);
|
|
2522
|
+
const typeFilter = url.searchParams.get("type");
|
|
2523
|
+
const mgr = getEmbeddingManager();
|
|
2524
|
+
if (!mgr) {
|
|
2525
|
+
return Response.json({
|
|
2526
|
+
results: [],
|
|
2527
|
+
message: "No embedding index. Run `trellis reindex` first."
|
|
2528
|
+
}, { headers });
|
|
2529
|
+
}
|
|
2530
|
+
try {
|
|
2531
|
+
const searchOpts = { limit };
|
|
2532
|
+
if (typeFilter) {
|
|
2533
|
+
searchOpts.types = typeFilter.split(",").map((t) => t.trim());
|
|
2534
|
+
}
|
|
2535
|
+
const results = await mgr.search(query, searchOpts);
|
|
2536
|
+
return Response.json({
|
|
2537
|
+
results: results.map((r) => ({
|
|
2538
|
+
score: r.score,
|
|
2539
|
+
chunkType: r.chunk.chunkType,
|
|
2540
|
+
filePath: r.chunk.filePath,
|
|
2541
|
+
entityId: r.chunk.entityId,
|
|
2542
|
+
content: r.chunk.content
|
|
2543
|
+
}))
|
|
2544
|
+
}, { headers });
|
|
2545
|
+
} catch (err) {
|
|
2546
|
+
return Response.json({ error: err.message }, { status: 500, headers });
|
|
2547
|
+
}
|
|
2548
|
+
}
|
|
2549
|
+
if (path.startsWith("/api/node/")) {
|
|
2550
|
+
const nodeId = decodeURIComponent(path.slice("/api/node/".length));
|
|
2551
|
+
const detail = getNodeDetail(engine, nodeId);
|
|
2552
|
+
if (!detail) {
|
|
2553
|
+
return Response.json({ error: "Node not found" }, { status: 404, headers });
|
|
2554
|
+
}
|
|
2555
|
+
return Response.json(detail, { headers });
|
|
2556
|
+
}
|
|
2557
|
+
if (path === "/" || path === "/index.html") {
|
|
2558
|
+
return new Response(clientHtml, {
|
|
2559
|
+
headers: { ...headers, "Content-Type": "text/html; charset=utf-8" }
|
|
2560
|
+
});
|
|
2561
|
+
}
|
|
2562
|
+
return new Response("Not Found", { status: 404, headers });
|
|
2563
|
+
}
|
|
2564
|
+
});
|
|
2565
|
+
return {
|
|
2566
|
+
port: server.port,
|
|
2567
|
+
stop: () => {
|
|
2568
|
+
server.stop();
|
|
2569
|
+
if (embeddingManager) {
|
|
2570
|
+
try {
|
|
2571
|
+
embeddingManager.close();
|
|
2572
|
+
} catch {}
|
|
2573
|
+
}
|
|
2574
|
+
}
|
|
2575
|
+
};
|
|
2576
|
+
}
|
|
2577
|
+
var init_server = __esm(() => {
|
|
2578
|
+
init_engine();
|
|
2579
|
+
init_links();
|
|
2580
|
+
});
|
|
2581
|
+
|
|
2264
2582
|
// node_modules/commander/esm.mjs
|
|
2265
2583
|
var import__ = __toESM(require_commander(), 1);
|
|
2266
2584
|
var {
|
|
@@ -2767,7 +3085,11 @@ var chalkStderr = createChalk({ level: stderrColor ? stderrColor.level : 0 });
|
|
|
2767
3085
|
var source_default = chalk;
|
|
2768
3086
|
|
|
2769
3087
|
// src/cli/index.ts
|
|
2770
|
-
|
|
3088
|
+
init_engine();
|
|
3089
|
+
import { resolve, join as join6 } from "path";
|
|
3090
|
+
init_auto_embed();
|
|
3091
|
+
init_store();
|
|
3092
|
+
init_model();
|
|
2771
3093
|
|
|
2772
3094
|
// src/git/git-reader.ts
|
|
2773
3095
|
import { execSync } from "child_process";
|
|
@@ -2867,6 +3189,8 @@ class GitReader {
|
|
|
2867
3189
|
}
|
|
2868
3190
|
|
|
2869
3191
|
// src/git/git-importer.ts
|
|
3192
|
+
init_engine();
|
|
3193
|
+
init_ops();
|
|
2870
3194
|
import { existsSync as existsSync2, mkdirSync } from "fs";
|
|
2871
3195
|
import { join as join2 } from "path";
|
|
2872
3196
|
async function importFromGit(opts) {
|
|
@@ -3067,6 +3391,7 @@ class ImportEngine {
|
|
|
3067
3391
|
}
|
|
3068
3392
|
|
|
3069
3393
|
// src/git/git-exporter.ts
|
|
3394
|
+
init_engine();
|
|
3070
3395
|
import { execSync as execSync2 } from "child_process";
|
|
3071
3396
|
import { existsSync as existsSync3, mkdirSync as mkdirSync2, writeFileSync, unlinkSync } from "fs";
|
|
3072
3397
|
import { join as join3, dirname as dirname2 } from "path";
|
|
@@ -3329,7 +3654,22 @@ program2.command("init").description("Initialize a new TrellisVCS repository in
|
|
|
3329
3654
|
return;
|
|
3330
3655
|
}
|
|
3331
3656
|
const engine = new TrellisVcsEngine({ rootPath });
|
|
3332
|
-
|
|
3657
|
+
let renderedProgress = false;
|
|
3658
|
+
const result = await engine.initRepo({
|
|
3659
|
+
onProgress: (progress) => {
|
|
3660
|
+
renderedProgress = true;
|
|
3661
|
+
if (progress.phase === "done") {
|
|
3662
|
+
process.stdout.write("\r\x1B[2K");
|
|
3663
|
+
return;
|
|
3664
|
+
}
|
|
3665
|
+
const label = progress.phase === "discovering" ? "Discovering\u2026" : progress.phase === "hashing" ? "Hashing\u2026" : "Recording\u2026";
|
|
3666
|
+
process.stdout.write(`\r\x1B[2K ${source_default.dim(label)} ${progress.message}`);
|
|
3667
|
+
}
|
|
3668
|
+
});
|
|
3669
|
+
if (renderedProgress) {
|
|
3670
|
+
process.stdout.write(`
|
|
3671
|
+
`);
|
|
3672
|
+
}
|
|
3333
3673
|
console.log(source_default.green("\u2713 Initialized TrellisVCS repository"));
|
|
3334
3674
|
console.log(` ${source_default.dim("Path:")} ${rootPath}`);
|
|
3335
3675
|
console.log(` ${source_default.dim("Ops:")} ${result.opsCreated} initial operations recorded`);
|
|
@@ -3754,9 +4094,9 @@ program2.command("parse").description("Parse a file into AST-level semantic enti
|
|
|
3754
4094
|
const engine = new TrellisVcsEngine({ rootPath });
|
|
3755
4095
|
if (TrellisVcsEngine.isRepo(rootPath))
|
|
3756
4096
|
engine.open();
|
|
3757
|
-
const { readFileSync:
|
|
4097
|
+
const { readFileSync: readFileSync4 } = __require("fs");
|
|
3758
4098
|
const filePath = resolve(file);
|
|
3759
|
-
const content =
|
|
4099
|
+
const content = readFileSync4(filePath, "utf-8");
|
|
3760
4100
|
const result = engine.parseFile(content, file);
|
|
3761
4101
|
if (!result) {
|
|
3762
4102
|
console.log(source_default.dim(`No parser available for: ${file}`));
|
|
@@ -3792,9 +4132,9 @@ program2.command("sdiff").description("Show semantic diff between two versions o
|
|
|
3792
4132
|
const engine = new TrellisVcsEngine({ rootPath });
|
|
3793
4133
|
if (TrellisVcsEngine.isRepo(rootPath))
|
|
3794
4134
|
engine.open();
|
|
3795
|
-
const { readFileSync:
|
|
3796
|
-
const oldContent =
|
|
3797
|
-
const newContent =
|
|
4135
|
+
const { readFileSync: readFileSync4 } = __require("fs");
|
|
4136
|
+
const oldContent = readFileSync4(resolve(fileA), "utf-8");
|
|
4137
|
+
const newContent = readFileSync4(resolve(fileB), "utf-8");
|
|
3798
4138
|
const patches = engine.semanticDiff(oldContent, newContent, fileA);
|
|
3799
4139
|
if (patches.length === 0) {
|
|
3800
4140
|
console.log(source_default.dim("No semantic differences."));
|
|
@@ -4456,7 +4796,7 @@ decisionCmd.command("chain").description("Trace all decisions that affected a gi
|
|
|
4456
4796
|
});
|
|
4457
4797
|
program2.command("identity").description("Manage local identity (Ed25519 key pair)").argument("[action]", '"init" or "show" (default: show)').option("-p, --path <path>", "Repository path", ".").option("--name <name>", "Display name for new identity").option("--email <email>", "Email for new identity").action((action, opts) => {
|
|
4458
4798
|
const rootPath = resolve(opts.path);
|
|
4459
|
-
const trellisDir =
|
|
4799
|
+
const trellisDir = join6(rootPath, ".trellis");
|
|
4460
4800
|
if (action === "init") {
|
|
4461
4801
|
if (hasIdentity(trellisDir)) {
|
|
4462
4802
|
console.error(source_default.yellow("Identity already exists. Use `trellis identity` to view it."));
|
|
@@ -4500,31 +4840,31 @@ program2.command("refs").description("List wiki-link references in files or find
|
|
|
4500
4840
|
}
|
|
4501
4841
|
const engine = new TrellisVcsEngine({ rootPath });
|
|
4502
4842
|
engine.open();
|
|
4503
|
-
const { readFileSync:
|
|
4843
|
+
const { readFileSync: readFileSync4 } = __require("fs");
|
|
4504
4844
|
const {
|
|
4505
4845
|
parseFileRefs,
|
|
4506
|
-
buildRefIndex,
|
|
4507
|
-
getOutgoingRefs,
|
|
4508
|
-
getBacklinks,
|
|
4846
|
+
buildRefIndex: buildRefIndex2,
|
|
4847
|
+
getOutgoingRefs: getOutgoingRefs2,
|
|
4848
|
+
getBacklinks: getBacklinks2,
|
|
4509
4849
|
getIndexStats
|
|
4510
4850
|
} = (init_links(), __toCommonJS(exports_links));
|
|
4511
4851
|
const {
|
|
4512
4852
|
resolveRef,
|
|
4513
4853
|
resolveRefs,
|
|
4514
|
-
createResolverContext
|
|
4854
|
+
createResolverContext: createResolverContext2
|
|
4515
4855
|
} = (init_links(), __toCommonJS(exports_links));
|
|
4516
4856
|
const { StaleRefRegistry, getDiagnostics } = (init_links(), __toCommonJS(exports_links));
|
|
4517
|
-
const resolverCtx =
|
|
4857
|
+
const resolverCtx = createResolverContext2(engine);
|
|
4518
4858
|
const trackedFiles = engine.trackedFiles();
|
|
4519
4859
|
const fileContents = [];
|
|
4520
4860
|
for (const f of trackedFiles) {
|
|
4521
4861
|
try {
|
|
4522
|
-
const absPath =
|
|
4523
|
-
const content =
|
|
4862
|
+
const absPath = join6(rootPath, f.path);
|
|
4863
|
+
const content = readFileSync4(absPath, "utf-8");
|
|
4524
4864
|
fileContents.push({ path: f.path, content });
|
|
4525
4865
|
} catch {}
|
|
4526
4866
|
}
|
|
4527
|
-
const index =
|
|
4867
|
+
const index = buildRefIndex2(fileContents, resolverCtx);
|
|
4528
4868
|
if (opts.stats) {
|
|
4529
4869
|
const stats = getIndexStats(index);
|
|
4530
4870
|
console.log(source_default.bold(`Reference Index Stats
|
|
@@ -4547,7 +4887,7 @@ program2.command("refs").description("List wiki-link references in files or find
|
|
|
4547
4887
|
];
|
|
4548
4888
|
let found = false;
|
|
4549
4889
|
for (const eid of candidates) {
|
|
4550
|
-
const sources =
|
|
4890
|
+
const sources = getBacklinks2(index, eid);
|
|
4551
4891
|
if (sources.length > 0) {
|
|
4552
4892
|
console.log(source_default.bold(`Backlinks for ${source_default.cyan(eid)} (${sources.length})
|
|
4553
4893
|
`));
|
|
@@ -4599,7 +4939,7 @@ program2.command("refs").description("List wiki-link references in files or find
|
|
|
4599
4939
|
return;
|
|
4600
4940
|
}
|
|
4601
4941
|
if (file) {
|
|
4602
|
-
const refs =
|
|
4942
|
+
const refs = getOutgoingRefs2(index, file);
|
|
4603
4943
|
if (refs.length === 0) {
|
|
4604
4944
|
console.log(source_default.dim(`No [[...]] references found in: ${file}`));
|
|
4605
4945
|
return;
|
|
@@ -4640,7 +4980,7 @@ program2.command("search").description("Semantic search across all embedded cont
|
|
|
4640
4980
|
const engine = new TrellisVcsEngine({ rootPath });
|
|
4641
4981
|
engine.open();
|
|
4642
4982
|
const { EmbeddingManager } = (init_embeddings(), __toCommonJS(exports_embeddings));
|
|
4643
|
-
const dbPath =
|
|
4983
|
+
const dbPath = join6(rootPath, ".trellis", "embeddings.db");
|
|
4644
4984
|
const manager = new EmbeddingManager(dbPath);
|
|
4645
4985
|
try {
|
|
4646
4986
|
const searchOpts = {
|
|
@@ -4678,7 +5018,7 @@ program2.command("reindex").description("Rebuild the semantic embedding index").
|
|
|
4678
5018
|
const engine = new TrellisVcsEngine({ rootPath });
|
|
4679
5019
|
engine.open();
|
|
4680
5020
|
const { EmbeddingManager } = (init_embeddings(), __toCommonJS(exports_embeddings));
|
|
4681
|
-
const dbPath =
|
|
5021
|
+
const dbPath = join6(rootPath, ".trellis", "embeddings.db");
|
|
4682
5022
|
const manager = new EmbeddingManager(dbPath);
|
|
4683
5023
|
try {
|
|
4684
5024
|
console.log(source_default.dim("Loading embedding model\u2026"));
|
|
@@ -4690,6 +5030,667 @@ program2.command("reindex").description("Rebuild the semantic embedding index").
|
|
|
4690
5030
|
manager.close();
|
|
4691
5031
|
}
|
|
4692
5032
|
});
|
|
5033
|
+
program2.command("ui").description("Launch the interactive graph explorer in your browser").option("-p, --path <path>", "Repository path", ".").option("--port <port>", "Server port", "3333").option("--no-open", "Do not auto-open browser").action(async (opts) => {
|
|
5034
|
+
const rootPath = resolve(opts.path);
|
|
5035
|
+
if (!TrellisVcsEngine.isRepo(rootPath)) {
|
|
5036
|
+
console.error(source_default.red("Not a TrellisVCS repository. Run `trellis init` first."));
|
|
5037
|
+
process.exit(1);
|
|
5038
|
+
}
|
|
5039
|
+
const { startUIServer: startUIServer2 } = (init_server(), __toCommonJS(exports_server));
|
|
5040
|
+
const port = parseInt(opts.port, 10) || 3000;
|
|
5041
|
+
try {
|
|
5042
|
+
const server = await startUIServer2({ rootPath, port });
|
|
5043
|
+
const url = `http://localhost:${server.port}`;
|
|
5044
|
+
console.log(source_default.green(`\u2713 Trellis Graph Explorer running at ${source_default.bold(url)}`));
|
|
5045
|
+
console.log(source_default.dim(` Press Ctrl+C to stop
|
|
5046
|
+
`));
|
|
5047
|
+
if (opts.open !== false) {
|
|
5048
|
+
const { exec } = __require("child_process");
|
|
5049
|
+
const cmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
|
|
5050
|
+
exec(`${cmd} ${url}`);
|
|
5051
|
+
}
|
|
5052
|
+
process.on("SIGINT", () => {
|
|
5053
|
+
server.stop();
|
|
5054
|
+
console.log(source_default.dim(`
|
|
5055
|
+
Server stopped.`));
|
|
5056
|
+
process.exit(0);
|
|
5057
|
+
});
|
|
5058
|
+
} catch (err) {
|
|
5059
|
+
console.error(source_default.red(`Failed to start server: ${err.message}`));
|
|
5060
|
+
process.exit(1);
|
|
5061
|
+
}
|
|
5062
|
+
});
|
|
5063
|
+
function bootKernel(rootPath) {
|
|
5064
|
+
const dbPath = join6(rootPath, ".trellis", "kernel.db");
|
|
5065
|
+
const backend = new SqliteKernelBackend(dbPath);
|
|
5066
|
+
const kernel = new TrellisKernel({
|
|
5067
|
+
backend,
|
|
5068
|
+
agentId: `agent:${process.env.USER ?? "unknown"}`
|
|
5069
|
+
});
|
|
5070
|
+
kernel.boot();
|
|
5071
|
+
return kernel;
|
|
5072
|
+
}
|
|
5073
|
+
var entityCmd = program2.command("entity").description("Manage graph entities (generic CRUD)");
|
|
5074
|
+
entityCmd.command("create").description("Create a new entity in the graph").requiredOption("-i, --id <id>", 'Entity ID (e.g. "project:my-app")').requiredOption("-t, --type <type>", 'Entity type (e.g. "Project", "User")').option("-a, --attr <attrs...>", "Attributes as key=value pairs").option("-p, --path <path>", "Repository path", ".").action(async (opts) => {
|
|
5075
|
+
const rootPath = resolve(opts.path);
|
|
5076
|
+
requireRepo(rootPath);
|
|
5077
|
+
const kernel = bootKernel(rootPath);
|
|
5078
|
+
try {
|
|
5079
|
+
const attrs = {};
|
|
5080
|
+
if (opts.attr) {
|
|
5081
|
+
for (const pair of opts.attr) {
|
|
5082
|
+
const eq = pair.indexOf("=");
|
|
5083
|
+
if (eq === -1)
|
|
5084
|
+
continue;
|
|
5085
|
+
const key = pair.slice(0, eq);
|
|
5086
|
+
let val = pair.slice(eq + 1);
|
|
5087
|
+
if (val === "true")
|
|
5088
|
+
val = true;
|
|
5089
|
+
else if (val === "false")
|
|
5090
|
+
val = false;
|
|
5091
|
+
else if (!isNaN(Number(val)) && val !== "")
|
|
5092
|
+
val = Number(val);
|
|
5093
|
+
attrs[key] = val;
|
|
5094
|
+
}
|
|
5095
|
+
}
|
|
5096
|
+
const result = await kernel.createEntity(opts.id, opts.type, attrs);
|
|
5097
|
+
console.log(source_default.green(`\u2713 Entity created: ${source_default.bold(opts.id)}`));
|
|
5098
|
+
console.log(` ${source_default.dim("Type:")} ${opts.type}`);
|
|
5099
|
+
console.log(` ${source_default.dim("Facts:")} ${result.factsDelta.added}`);
|
|
5100
|
+
console.log(` ${source_default.dim("Op:")} ${result.op.hash.slice(0, 32)}\u2026`);
|
|
5101
|
+
} finally {
|
|
5102
|
+
kernel.close();
|
|
5103
|
+
}
|
|
5104
|
+
});
|
|
5105
|
+
entityCmd.command("get").description("Get an entity by ID").argument("<id>", "Entity ID").option("-p, --path <path>", "Repository path", ".").option("--json", "Output as JSON").action((id, opts) => {
|
|
5106
|
+
const rootPath = resolve(opts.path);
|
|
5107
|
+
requireRepo(rootPath);
|
|
5108
|
+
const kernel = bootKernel(rootPath);
|
|
5109
|
+
try {
|
|
5110
|
+
const entity = kernel.getEntity(id);
|
|
5111
|
+
if (!entity) {
|
|
5112
|
+
console.error(source_default.red(`Entity not found: ${id}`));
|
|
5113
|
+
process.exit(1);
|
|
5114
|
+
}
|
|
5115
|
+
if (opts.json) {
|
|
5116
|
+
const obj = { id: entity.id, type: entity.type };
|
|
5117
|
+
for (const f of entity.facts) {
|
|
5118
|
+
if (f.a !== "type")
|
|
5119
|
+
obj[f.a] = f.v;
|
|
5120
|
+
}
|
|
5121
|
+
obj._links = entity.links.map((l) => ({
|
|
5122
|
+
attribute: l.a,
|
|
5123
|
+
target: l.e2,
|
|
5124
|
+
source: l.e1
|
|
5125
|
+
}));
|
|
5126
|
+
console.log(JSON.stringify(obj, null, 2));
|
|
5127
|
+
return;
|
|
5128
|
+
}
|
|
5129
|
+
console.log(source_default.bold(`${entity.type}: ${entity.id}
|
|
5130
|
+
`));
|
|
5131
|
+
for (const f of entity.facts) {
|
|
5132
|
+
console.log(` ${source_default.dim(f.a.padEnd(20))} ${f.v}`);
|
|
5133
|
+
}
|
|
5134
|
+
if (entity.links.length > 0) {
|
|
5135
|
+
console.log(`
|
|
5136
|
+
${source_default.bold("Links:")}`);
|
|
5137
|
+
for (const l of entity.links) {
|
|
5138
|
+
const dir = l.e1 === id ? "\u2192" : "\u2190";
|
|
5139
|
+
const other = l.e1 === id ? l.e2 : l.e1;
|
|
5140
|
+
console.log(` ${dir} ${source_default.dim(l.a)} ${other}`);
|
|
5141
|
+
}
|
|
5142
|
+
}
|
|
5143
|
+
} finally {
|
|
5144
|
+
kernel.close();
|
|
5145
|
+
}
|
|
5146
|
+
});
|
|
5147
|
+
entityCmd.command("update").description("Update attributes on an existing entity").argument("<id>", "Entity ID").requiredOption("-a, --attr <attrs...>", "Attributes as key=value pairs").option("-p, --path <path>", "Repository path", ".").action(async (id, opts) => {
|
|
5148
|
+
const rootPath = resolve(opts.path);
|
|
5149
|
+
requireRepo(rootPath);
|
|
5150
|
+
const kernel = bootKernel(rootPath);
|
|
5151
|
+
try {
|
|
5152
|
+
const updates = {};
|
|
5153
|
+
for (const pair of opts.attr) {
|
|
5154
|
+
const eq = pair.indexOf("=");
|
|
5155
|
+
if (eq === -1)
|
|
5156
|
+
continue;
|
|
5157
|
+
const key = pair.slice(0, eq);
|
|
5158
|
+
let val = pair.slice(eq + 1);
|
|
5159
|
+
if (val === "true")
|
|
5160
|
+
val = true;
|
|
5161
|
+
else if (val === "false")
|
|
5162
|
+
val = false;
|
|
5163
|
+
else if (!isNaN(Number(val)) && val !== "")
|
|
5164
|
+
val = Number(val);
|
|
5165
|
+
updates[key] = val;
|
|
5166
|
+
}
|
|
5167
|
+
await kernel.updateEntity(id, updates);
|
|
5168
|
+
console.log(source_default.green(`\u2713 Updated ${source_default.bold(id)}`));
|
|
5169
|
+
for (const [k, v] of Object.entries(updates)) {
|
|
5170
|
+
console.log(` ${source_default.dim(k)} = ${v}`);
|
|
5171
|
+
}
|
|
5172
|
+
} finally {
|
|
5173
|
+
kernel.close();
|
|
5174
|
+
}
|
|
5175
|
+
});
|
|
5176
|
+
entityCmd.command("delete").description("Delete an entity and all its facts/links").argument("<id>", "Entity ID").option("-p, --path <path>", "Repository path", ".").action(async (id, opts) => {
|
|
5177
|
+
const rootPath = resolve(opts.path);
|
|
5178
|
+
requireRepo(rootPath);
|
|
5179
|
+
const kernel = bootKernel(rootPath);
|
|
5180
|
+
try {
|
|
5181
|
+
const entity = kernel.getEntity(id);
|
|
5182
|
+
if (!entity) {
|
|
5183
|
+
console.error(source_default.red(`Entity not found: ${id}`));
|
|
5184
|
+
process.exit(1);
|
|
5185
|
+
}
|
|
5186
|
+
await kernel.deleteEntity(id);
|
|
5187
|
+
console.log(source_default.green(`\u2713 Deleted entity ${source_default.bold(id)}`));
|
|
5188
|
+
} finally {
|
|
5189
|
+
kernel.close();
|
|
5190
|
+
}
|
|
5191
|
+
});
|
|
5192
|
+
entityCmd.command("list").description("List entities, optionally filtered by type").option("-t, --type <type>", "Filter by entity type").option("-f, --filter <filters...>", "Attribute filters as key=value").option("--json", "Output as JSON").option("-p, --path <path>", "Repository path", ".").action((opts) => {
|
|
5193
|
+
const rootPath = resolve(opts.path);
|
|
5194
|
+
requireRepo(rootPath);
|
|
5195
|
+
const kernel = bootKernel(rootPath);
|
|
5196
|
+
try {
|
|
5197
|
+
let filters;
|
|
5198
|
+
if (opts.filter) {
|
|
5199
|
+
filters = {};
|
|
5200
|
+
for (const pair of opts.filter) {
|
|
5201
|
+
const eq = pair.indexOf("=");
|
|
5202
|
+
if (eq === -1)
|
|
5203
|
+
continue;
|
|
5204
|
+
const key = pair.slice(0, eq);
|
|
5205
|
+
let val = pair.slice(eq + 1);
|
|
5206
|
+
if (val === "true")
|
|
5207
|
+
val = true;
|
|
5208
|
+
else if (val === "false")
|
|
5209
|
+
val = false;
|
|
5210
|
+
else if (!isNaN(Number(val)) && val !== "")
|
|
5211
|
+
val = Number(val);
|
|
5212
|
+
filters[key] = val;
|
|
5213
|
+
}
|
|
5214
|
+
}
|
|
5215
|
+
const entities = kernel.listEntities(opts.type, filters);
|
|
5216
|
+
if (opts.json) {
|
|
5217
|
+
const out = entities.map((e) => {
|
|
5218
|
+
const obj = { id: e.id, type: e.type };
|
|
5219
|
+
for (const f of e.facts) {
|
|
5220
|
+
if (f.a !== "type")
|
|
5221
|
+
obj[f.a] = f.v;
|
|
5222
|
+
}
|
|
5223
|
+
return obj;
|
|
5224
|
+
});
|
|
5225
|
+
console.log(JSON.stringify(out, null, 2));
|
|
5226
|
+
return;
|
|
5227
|
+
}
|
|
5228
|
+
if (entities.length === 0) {
|
|
5229
|
+
console.log(source_default.dim("No entities found."));
|
|
5230
|
+
return;
|
|
5231
|
+
}
|
|
5232
|
+
const typeLabel = opts.type ? ` (type: ${opts.type})` : "";
|
|
5233
|
+
console.log(source_default.bold(`Entities (${entities.length})${typeLabel}
|
|
5234
|
+
`));
|
|
5235
|
+
for (const e of entities) {
|
|
5236
|
+
const nameFact = e.facts.find((f) => f.a === "name");
|
|
5237
|
+
const name = nameFact ? ` ${source_default.white(String(nameFact.v))}` : "";
|
|
5238
|
+
console.log(` ${source_default.cyan(e.type.padEnd(16))} ${source_default.bold(e.id)}${name}`);
|
|
5239
|
+
}
|
|
5240
|
+
} finally {
|
|
5241
|
+
kernel.close();
|
|
5242
|
+
}
|
|
5243
|
+
});
|
|
5244
|
+
var factCmd = program2.command("fact").description("Add or remove individual facts on entities");
|
|
5245
|
+
factCmd.command("add").description("Add a fact to an entity").argument("<entity>", "Entity ID").argument("<attribute>", "Attribute name").argument("<value>", "Value").option("-p, --path <path>", "Repository path", ".").action(async (entity, attribute, value, opts) => {
|
|
5246
|
+
const rootPath = resolve(opts.path);
|
|
5247
|
+
requireRepo(rootPath);
|
|
5248
|
+
const kernel = bootKernel(rootPath);
|
|
5249
|
+
try {
|
|
5250
|
+
let val = value;
|
|
5251
|
+
if (val === "true")
|
|
5252
|
+
val = true;
|
|
5253
|
+
else if (val === "false")
|
|
5254
|
+
val = false;
|
|
5255
|
+
else if (!isNaN(Number(val)) && val !== "")
|
|
5256
|
+
val = Number(val);
|
|
5257
|
+
await kernel.addFact(entity, attribute, val);
|
|
5258
|
+
console.log(source_default.green(`\u2713 Added fact: ${source_default.bold(entity)}.${attribute} = ${val}`));
|
|
5259
|
+
} finally {
|
|
5260
|
+
kernel.close();
|
|
5261
|
+
}
|
|
5262
|
+
});
|
|
5263
|
+
factCmd.command("remove").description("Remove a fact from an entity").argument("<entity>", "Entity ID").argument("<attribute>", "Attribute name").argument("<value>", "Value to remove").option("-p, --path <path>", "Repository path", ".").action(async (entity, attribute, value, opts) => {
|
|
5264
|
+
const rootPath = resolve(opts.path);
|
|
5265
|
+
requireRepo(rootPath);
|
|
5266
|
+
const kernel = bootKernel(rootPath);
|
|
5267
|
+
try {
|
|
5268
|
+
let val = value;
|
|
5269
|
+
if (val === "true")
|
|
5270
|
+
val = true;
|
|
5271
|
+
else if (val === "false")
|
|
5272
|
+
val = false;
|
|
5273
|
+
else if (!isNaN(Number(val)) && val !== "")
|
|
5274
|
+
val = Number(val);
|
|
5275
|
+
await kernel.removeFact(entity, attribute, val);
|
|
5276
|
+
console.log(source_default.green(`\u2713 Removed fact: ${source_default.bold(entity)}.${attribute} = ${val}`));
|
|
5277
|
+
} finally {
|
|
5278
|
+
kernel.close();
|
|
5279
|
+
}
|
|
5280
|
+
});
|
|
5281
|
+
factCmd.command("query").description("Query facts by entity or attribute").option("-e, --entity <id>", "Filter by entity ID").option("-a, --attribute <attr>", "Filter by attribute").option("--json", "Output as JSON").option("-p, --path <path>", "Repository path", ".").action((opts) => {
|
|
5282
|
+
const rootPath = resolve(opts.path);
|
|
5283
|
+
requireRepo(rootPath);
|
|
5284
|
+
const kernel = bootKernel(rootPath);
|
|
5285
|
+
try {
|
|
5286
|
+
const store = kernel.getStore();
|
|
5287
|
+
let facts;
|
|
5288
|
+
if (opts.entity) {
|
|
5289
|
+
facts = store.getFactsByEntity(opts.entity);
|
|
5290
|
+
} else if (opts.attribute) {
|
|
5291
|
+
facts = store.getFactsByAttribute(opts.attribute);
|
|
5292
|
+
} else {
|
|
5293
|
+
facts = store.getAllFacts();
|
|
5294
|
+
}
|
|
5295
|
+
if (opts.json) {
|
|
5296
|
+
console.log(JSON.stringify(facts, null, 2));
|
|
5297
|
+
return;
|
|
5298
|
+
}
|
|
5299
|
+
if (facts.length === 0) {
|
|
5300
|
+
console.log(source_default.dim("No facts found."));
|
|
5301
|
+
return;
|
|
5302
|
+
}
|
|
5303
|
+
console.log(source_default.bold(`Facts (${facts.length})
|
|
5304
|
+
`));
|
|
5305
|
+
for (const f of facts.slice(0, 100)) {
|
|
5306
|
+
console.log(` ${source_default.cyan(f.e.padEnd(24))} ${source_default.dim(f.a.padEnd(20))} ${f.v}`);
|
|
5307
|
+
}
|
|
5308
|
+
if (facts.length > 100) {
|
|
5309
|
+
console.log(source_default.dim(` \u2026 +${facts.length - 100} more`));
|
|
5310
|
+
}
|
|
5311
|
+
} finally {
|
|
5312
|
+
kernel.close();
|
|
5313
|
+
}
|
|
5314
|
+
});
|
|
5315
|
+
var linkCmd = program2.command("link").description("Add or remove links between entities");
|
|
5316
|
+
linkCmd.command("add").description("Add a link between two entities").argument("<source>", "Source entity ID").argument("<attribute>", "Relationship attribute").argument("<target>", "Target entity ID").option("-p, --path <path>", "Repository path", ".").action(async (source, attribute, target, opts) => {
|
|
5317
|
+
const rootPath = resolve(opts.path);
|
|
5318
|
+
requireRepo(rootPath);
|
|
5319
|
+
const kernel = bootKernel(rootPath);
|
|
5320
|
+
try {
|
|
5321
|
+
await kernel.addLink(source, attribute, target);
|
|
5322
|
+
console.log(source_default.green(`\u2713 Link: ${source_default.bold(source)} \u2014[${attribute}]\u2192 ${source_default.bold(target)}`));
|
|
5323
|
+
} finally {
|
|
5324
|
+
kernel.close();
|
|
5325
|
+
}
|
|
5326
|
+
});
|
|
5327
|
+
linkCmd.command("remove").description("Remove a link between two entities").argument("<source>", "Source entity ID").argument("<attribute>", "Relationship attribute").argument("<target>", "Target entity ID").option("-p, --path <path>", "Repository path", ".").action(async (source, attribute, target, opts) => {
|
|
5328
|
+
const rootPath = resolve(opts.path);
|
|
5329
|
+
requireRepo(rootPath);
|
|
5330
|
+
const kernel = bootKernel(rootPath);
|
|
5331
|
+
try {
|
|
5332
|
+
await kernel.removeLink(source, attribute, target);
|
|
5333
|
+
console.log(source_default.green(`\u2713 Removed: ${source_default.bold(source)} \u2014[${attribute}]\u2192 ${source_default.bold(target)}`));
|
|
5334
|
+
} finally {
|
|
5335
|
+
kernel.close();
|
|
5336
|
+
}
|
|
5337
|
+
});
|
|
5338
|
+
linkCmd.command("query").description("Query links for an entity").option("-e, --entity <id>", "Entity ID").option("-a, --attribute <attr>", "Relationship attribute").option("--json", "Output as JSON").option("-p, --path <path>", "Repository path", ".").action((opts) => {
|
|
5339
|
+
const rootPath = resolve(opts.path);
|
|
5340
|
+
requireRepo(rootPath);
|
|
5341
|
+
const kernel = bootKernel(rootPath);
|
|
5342
|
+
try {
|
|
5343
|
+
const store = kernel.getStore();
|
|
5344
|
+
let links;
|
|
5345
|
+
if (opts.entity && opts.attribute) {
|
|
5346
|
+
links = store.getLinksByEntityAndAttribute(opts.entity, opts.attribute);
|
|
5347
|
+
} else if (opts.entity) {
|
|
5348
|
+
links = store.getLinksByEntity(opts.entity);
|
|
5349
|
+
} else if (opts.attribute) {
|
|
5350
|
+
links = store.getLinksByAttribute(opts.attribute);
|
|
5351
|
+
} else {
|
|
5352
|
+
links = store.getAllLinks();
|
|
5353
|
+
}
|
|
5354
|
+
if (opts.json) {
|
|
5355
|
+
console.log(JSON.stringify(links, null, 2));
|
|
5356
|
+
return;
|
|
5357
|
+
}
|
|
5358
|
+
if (links.length === 0) {
|
|
5359
|
+
console.log(source_default.dim("No links found."));
|
|
5360
|
+
return;
|
|
5361
|
+
}
|
|
5362
|
+
console.log(source_default.bold(`Links (${links.length})
|
|
5363
|
+
`));
|
|
5364
|
+
for (const l of links.slice(0, 100)) {
|
|
5365
|
+
console.log(` ${source_default.cyan(l.e1)} \u2014[${source_default.dim(l.a)}]\u2192 ${source_default.cyan(l.e2)}`);
|
|
5366
|
+
}
|
|
5367
|
+
if (links.length > 100) {
|
|
5368
|
+
console.log(source_default.dim(` \u2026 +${links.length - 100} more`));
|
|
5369
|
+
}
|
|
5370
|
+
} finally {
|
|
5371
|
+
kernel.close();
|
|
5372
|
+
}
|
|
5373
|
+
});
|
|
5374
|
+
program2.command("query").description("Execute an EQL-S query against the graph").argument("<query>", 'EQL-S query string (or "find ?e where attr = value" shorthand)').option("-p, --path <path>", "Repository path", ".").option("--json", "Output as JSON").action((queryStr, opts) => {
|
|
5375
|
+
const rootPath = resolve(opts.path);
|
|
5376
|
+
requireRepo(rootPath);
|
|
5377
|
+
const kernel = bootKernel(rootPath);
|
|
5378
|
+
try {
|
|
5379
|
+
const store = kernel.getStore();
|
|
5380
|
+
const engine = new QueryEngine(store);
|
|
5381
|
+
let q;
|
|
5382
|
+
try {
|
|
5383
|
+
q = parseSimple(queryStr);
|
|
5384
|
+
} catch {
|
|
5385
|
+
try {
|
|
5386
|
+
q = parseQuery(queryStr);
|
|
5387
|
+
} catch (e) {
|
|
5388
|
+
console.error(source_default.red(`Parse error: ${e.message}`));
|
|
5389
|
+
process.exit(1);
|
|
5390
|
+
return;
|
|
5391
|
+
}
|
|
5392
|
+
}
|
|
5393
|
+
const result = engine.execute(q);
|
|
5394
|
+
if (opts.json) {
|
|
5395
|
+
console.log(JSON.stringify(result.bindings, null, 2));
|
|
5396
|
+
} else {
|
|
5397
|
+
if (result.count === 0) {
|
|
5398
|
+
console.log(source_default.dim("No results."));
|
|
5399
|
+
} else {
|
|
5400
|
+
const cols = result.bindings.length > 0 ? Object.keys(result.bindings[0]) : [];
|
|
5401
|
+
console.log(source_default.bold(cols.map((c) => `?${c}`).join("\t")));
|
|
5402
|
+
console.log(source_default.dim("\u2500".repeat(cols.length * 20)));
|
|
5403
|
+
for (const row of result.bindings) {
|
|
5404
|
+
console.log(cols.map((c) => String(row[c] ?? "")).join("\t"));
|
|
5405
|
+
}
|
|
5406
|
+
console.log(source_default.dim(`
|
|
5407
|
+
${result.count} result(s) in ${result.executionTime.toFixed(1)}ms`));
|
|
5408
|
+
}
|
|
5409
|
+
}
|
|
5410
|
+
} finally {
|
|
5411
|
+
kernel.close();
|
|
5412
|
+
}
|
|
5413
|
+
});
|
|
5414
|
+
program2.command("repl").description("Interactive EQL-S query shell").option("-p, --path <path>", "Repository path", ".").action(async (opts) => {
|
|
5415
|
+
const rootPath = resolve(opts.path);
|
|
5416
|
+
requireRepo(rootPath);
|
|
5417
|
+
const kernel = bootKernel(rootPath);
|
|
5418
|
+
const store = kernel.getStore();
|
|
5419
|
+
const engine = new QueryEngine(store);
|
|
5420
|
+
console.log(source_default.cyan.bold("Trellis EQL-S REPL"));
|
|
5421
|
+
console.log(source_default.dim('Type EQL-S queries or "find ?e where attr = value" shorthand.'));
|
|
5422
|
+
console.log(source_default.dim(`Type .exit to quit, .help for help.
|
|
5423
|
+
`));
|
|
5424
|
+
const readline = await import("readline");
|
|
5425
|
+
const rl = readline.createInterface({
|
|
5426
|
+
input: process.stdin,
|
|
5427
|
+
output: process.stdout,
|
|
5428
|
+
prompt: source_default.green("eql> ")
|
|
5429
|
+
});
|
|
5430
|
+
rl.prompt();
|
|
5431
|
+
rl.on("line", (line) => {
|
|
5432
|
+
const trimmed = line.trim();
|
|
5433
|
+
if (!trimmed) {
|
|
5434
|
+
rl.prompt();
|
|
5435
|
+
return;
|
|
5436
|
+
}
|
|
5437
|
+
if (trimmed === ".exit" || trimmed === ".quit") {
|
|
5438
|
+
kernel.close();
|
|
5439
|
+
rl.close();
|
|
5440
|
+
return;
|
|
5441
|
+
}
|
|
5442
|
+
if (trimmed === ".help") {
|
|
5443
|
+
console.log(`
|
|
5444
|
+
${source_default.bold("EQL-S Query Syntax:")}
|
|
5445
|
+
${source_default.cyan("SELECT")} ?var1 ?var2 ${source_default.cyan("WHERE")} { patterns } [${source_default.cyan("FILTER")} ...] [${source_default.cyan("ORDER BY")} ...] [${source_default.cyan("LIMIT")} n]
|
|
5446
|
+
|
|
5447
|
+
${source_default.bold("Pattern types:")}
|
|
5448
|
+
${source_default.yellow('[?e "attr" "value"]')} Fact pattern (entity, attribute, value)
|
|
5449
|
+
${source_default.yellow('(?src "rel" ?tgt)')} Link pattern (source, relationship, target)
|
|
5450
|
+
${source_default.yellow('NOT [?e "attr" ?v]')} Negation
|
|
5451
|
+
${source_default.yellow("OR { ... } { ... }")} Disjunction
|
|
5452
|
+
|
|
5453
|
+
${source_default.bold("Shorthand:")}
|
|
5454
|
+
${source_default.yellow('find ?e where type = "Project"')}
|
|
5455
|
+
|
|
5456
|
+
${source_default.bold("Commands:")}
|
|
5457
|
+
.exit / .quit Exit the REPL
|
|
5458
|
+
.stats Show store statistics
|
|
5459
|
+
.help Show this help
|
|
5460
|
+
`);
|
|
5461
|
+
rl.prompt();
|
|
5462
|
+
return;
|
|
5463
|
+
}
|
|
5464
|
+
if (trimmed === ".stats") {
|
|
5465
|
+
const facts = store.getAllFacts();
|
|
5466
|
+
const links = store.getAllLinks();
|
|
5467
|
+
const types = new Set(facts.filter((f) => f.a === "type").map((f) => f.v));
|
|
5468
|
+
console.log(` Facts: ${facts.length}`);
|
|
5469
|
+
console.log(` Links: ${links.length}`);
|
|
5470
|
+
console.log(` Entity types: ${[...types].join(", ") || "(none)"}`);
|
|
5471
|
+
rl.prompt();
|
|
5472
|
+
return;
|
|
5473
|
+
}
|
|
5474
|
+
try {
|
|
5475
|
+
let q;
|
|
5476
|
+
try {
|
|
5477
|
+
q = parseSimple(trimmed);
|
|
5478
|
+
} catch {
|
|
5479
|
+
q = parseQuery(trimmed);
|
|
5480
|
+
}
|
|
5481
|
+
const result = engine.execute(q);
|
|
5482
|
+
if (result.count === 0) {
|
|
5483
|
+
console.log(source_default.dim("No results."));
|
|
5484
|
+
} else {
|
|
5485
|
+
const cols = Object.keys(result.bindings[0]);
|
|
5486
|
+
console.log(source_default.bold(cols.map((c) => `?${c}`).join("\t")));
|
|
5487
|
+
for (const row of result.bindings) {
|
|
5488
|
+
console.log(cols.map((c) => String(row[c] ?? "")).join("\t"));
|
|
5489
|
+
}
|
|
5490
|
+
console.log(source_default.dim(`${result.count} result(s) in ${result.executionTime.toFixed(1)}ms`));
|
|
5491
|
+
}
|
|
5492
|
+
} catch (e) {
|
|
5493
|
+
console.error(source_default.red(`Error: ${e.message}`));
|
|
5494
|
+
}
|
|
5495
|
+
rl.prompt();
|
|
5496
|
+
});
|
|
5497
|
+
rl.on("close", () => {
|
|
5498
|
+
kernel.close();
|
|
5499
|
+
console.log(source_default.dim("Goodbye."));
|
|
5500
|
+
});
|
|
5501
|
+
});
|
|
5502
|
+
var ontologyCmd = program2.command("ontology").description("Manage and inspect ontology schemas");
|
|
5503
|
+
ontologyCmd.command("list").description("List all registered ontologies (built-in + custom)").action(() => {
|
|
5504
|
+
const registry = new OntologyRegistry;
|
|
5505
|
+
for (const o of builtinOntologies)
|
|
5506
|
+
registry.register(o);
|
|
5507
|
+
const schemas = registry.list();
|
|
5508
|
+
if (schemas.length === 0) {
|
|
5509
|
+
console.log(source_default.dim("No ontologies registered."));
|
|
5510
|
+
return;
|
|
5511
|
+
}
|
|
5512
|
+
console.log(source_default.bold(`Ontologies (${schemas.length})
|
|
5513
|
+
`));
|
|
5514
|
+
for (const s of schemas) {
|
|
5515
|
+
console.log(` ${source_default.cyan(s.id)} ${source_default.dim(`v${s.version}`)}`);
|
|
5516
|
+
console.log(` ${s.name}${s.description ? ` \u2014 ${source_default.dim(s.description)}` : ""}`);
|
|
5517
|
+
console.log(` Entities: ${s.entities.map((e) => e.name).join(", ")}`);
|
|
5518
|
+
console.log(` Relations: ${s.relations.map((r) => r.name).join(", ")}`);
|
|
5519
|
+
console.log();
|
|
5520
|
+
}
|
|
5521
|
+
});
|
|
5522
|
+
ontologyCmd.command("inspect").description("Inspect a specific ontology or entity type").argument("<name>", 'Ontology ID (e.g. "trellis:project") or entity type name (e.g. "Project")').action((name) => {
|
|
5523
|
+
const registry = new OntologyRegistry;
|
|
5524
|
+
for (const o of builtinOntologies)
|
|
5525
|
+
registry.register(o);
|
|
5526
|
+
const schema = registry.get(name);
|
|
5527
|
+
if (schema) {
|
|
5528
|
+
console.log(source_default.bold(`${schema.name} ${source_default.dim(`(${schema.id} v${schema.version})`)}`));
|
|
5529
|
+
if (schema.description)
|
|
5530
|
+
console.log(source_default.dim(schema.description));
|
|
5531
|
+
console.log();
|
|
5532
|
+
console.log(source_default.bold("Entity Types:"));
|
|
5533
|
+
for (const e of schema.entities) {
|
|
5534
|
+
console.log(`
|
|
5535
|
+
${source_default.cyan.bold(e.name)}${e.abstract ? source_default.dim(" (abstract)") : ""}${e.extends ? source_default.dim(` extends ${e.extends}`) : ""}`);
|
|
5536
|
+
if (e.description)
|
|
5537
|
+
console.log(` ${source_default.dim(e.description)}`);
|
|
5538
|
+
for (const a of e.attributes) {
|
|
5539
|
+
const flags = [
|
|
5540
|
+
a.required ? source_default.red("required") : null,
|
|
5541
|
+
a.enum ? `enum[${a.enum.join("|")}]` : null,
|
|
5542
|
+
a.default !== undefined ? `default=${a.default}` : null
|
|
5543
|
+
].filter(Boolean).join(", ");
|
|
5544
|
+
console.log(` ${source_default.yellow(a.name)}: ${a.type}${flags ? ` (${flags})` : ""}`);
|
|
5545
|
+
}
|
|
5546
|
+
}
|
|
5547
|
+
if (schema.relations.length > 0) {
|
|
5548
|
+
console.log(source_default.bold(`
|
|
5549
|
+
Relations:`));
|
|
5550
|
+
for (const r of schema.relations) {
|
|
5551
|
+
console.log(` ${source_default.yellow(r.name)}: ${r.sourceTypes.join("|")} \u2192 ${r.targetTypes.join("|")}${r.cardinality ? ` [${r.cardinality}]` : ""}`);
|
|
5552
|
+
}
|
|
5553
|
+
}
|
|
5554
|
+
return;
|
|
5555
|
+
}
|
|
5556
|
+
const def = registry.getEntityDef(name);
|
|
5557
|
+
if (def) {
|
|
5558
|
+
const ontId = registry.getEntityOntology(name);
|
|
5559
|
+
console.log(source_default.bold(`${def.name}`) + source_default.dim(` (from ${ontId})`));
|
|
5560
|
+
if (def.description)
|
|
5561
|
+
console.log(source_default.dim(def.description));
|
|
5562
|
+
if (def.abstract)
|
|
5563
|
+
console.log(source_default.dim("(abstract \u2014 cannot be instantiated)"));
|
|
5564
|
+
if (def.extends)
|
|
5565
|
+
console.log(source_default.dim(`extends ${def.extends}`));
|
|
5566
|
+
console.log(source_default.bold(`
|
|
5567
|
+
Attributes:`));
|
|
5568
|
+
for (const a of def.attributes) {
|
|
5569
|
+
const flags = [
|
|
5570
|
+
a.required ? source_default.red("required") : null,
|
|
5571
|
+
a.enum ? `enum[${a.enum.join("|")}]` : null,
|
|
5572
|
+
a.default !== undefined ? `default=${a.default}` : null
|
|
5573
|
+
].filter(Boolean).join(", ");
|
|
5574
|
+
console.log(` ${source_default.yellow(a.name)}: ${a.type}${flags ? ` (${flags})` : ""}`);
|
|
5575
|
+
if (a.description)
|
|
5576
|
+
console.log(` ${source_default.dim(a.description)}`);
|
|
5577
|
+
}
|
|
5578
|
+
const rels = registry.getRelationsForType(name);
|
|
5579
|
+
if (rels.length > 0) {
|
|
5580
|
+
console.log(source_default.bold(`
|
|
5581
|
+
Relations:`));
|
|
5582
|
+
for (const r of rels) {
|
|
5583
|
+
const dir = r.sourceTypes.includes(name) ? "\u2192" : "\u2190";
|
|
5584
|
+
console.log(` ${source_default.yellow(r.name)} ${dir} ${r.sourceTypes.includes(name) ? r.targetTypes.join("|") : r.sourceTypes.join("|")}`);
|
|
5585
|
+
}
|
|
5586
|
+
}
|
|
5587
|
+
return;
|
|
5588
|
+
}
|
|
5589
|
+
console.error(source_default.red(`Unknown ontology or entity type: "${name}"`));
|
|
5590
|
+
console.log(source_default.dim("Available ontologies: " + registry.list().map((s) => s.id).join(", ")));
|
|
5591
|
+
console.log(source_default.dim("Available types: " + registry.listEntityTypes().join(", ")));
|
|
5592
|
+
});
|
|
5593
|
+
ontologyCmd.command("validate").description("Validate all entities in the graph against registered ontologies").option("-p, --path <path>", "Repository path", ".").option("--strict", "Treat unknown types as errors").action((opts) => {
|
|
5594
|
+
const rootPath = resolve(opts.path);
|
|
5595
|
+
requireRepo(rootPath);
|
|
5596
|
+
const kernel = bootKernel(rootPath);
|
|
5597
|
+
try {
|
|
5598
|
+
const registry = new OntologyRegistry;
|
|
5599
|
+
for (const o of builtinOntologies)
|
|
5600
|
+
registry.register(o);
|
|
5601
|
+
const store = kernel.getStore();
|
|
5602
|
+
const result = validateStore(store, registry);
|
|
5603
|
+
if (result.errors.length > 0) {
|
|
5604
|
+
console.log(source_default.red.bold(`\u2717 ${result.errors.length} error(s):
|
|
5605
|
+
`));
|
|
5606
|
+
for (const err of result.errors) {
|
|
5607
|
+
console.log(` ${source_default.red("ERROR")} ${source_default.bold(err.entityId)} (${err.entityType}) \u2192 ${err.field}: ${err.message}`);
|
|
5608
|
+
}
|
|
5609
|
+
}
|
|
5610
|
+
if (result.warnings.length > 0) {
|
|
5611
|
+
console.log(source_default.yellow.bold(`
|
|
5612
|
+
\u26A0 ${result.warnings.length} warning(s):
|
|
5613
|
+
`));
|
|
5614
|
+
for (const w of result.warnings) {
|
|
5615
|
+
console.log(` ${source_default.yellow("WARN")} ${source_default.bold(w.entityId)} (${w.entityType}) \u2192 ${w.field}: ${w.message}`);
|
|
5616
|
+
}
|
|
5617
|
+
}
|
|
5618
|
+
if (result.valid && result.warnings.length === 0) {
|
|
5619
|
+
console.log(source_default.green("\u2713 All entities pass ontology validation."));
|
|
5620
|
+
} else if (result.valid) {
|
|
5621
|
+
console.log(source_default.green(`
|
|
5622
|
+
\u2713 Valid (with warnings).`));
|
|
5623
|
+
} else {
|
|
5624
|
+
console.log(source_default.red(`
|
|
5625
|
+
\u2717 Validation failed.`));
|
|
5626
|
+
}
|
|
5627
|
+
} finally {
|
|
5628
|
+
kernel.close();
|
|
5629
|
+
}
|
|
5630
|
+
});
|
|
5631
|
+
program2.command("ask").description("Natural language search over the graph (semantic search)").argument("<question>", "Natural language query").option("-p, --path <path>", "Repository path", ".").option("-n, --limit <n>", "Max results", "5").option("--json", "Output as JSON").option("--rag", "Output as RAG context (for LLM consumption)").action(async (question, opts) => {
|
|
5632
|
+
const rootPath = resolve(opts.path);
|
|
5633
|
+
requireRepo(rootPath);
|
|
5634
|
+
const dbPath = join6(rootPath, ".trellis", "embeddings.db");
|
|
5635
|
+
const vectorStore = new VectorStore(dbPath);
|
|
5636
|
+
try {
|
|
5637
|
+
const limit = parseInt(opts.limit, 10) || 5;
|
|
5638
|
+
if (opts.rag) {
|
|
5639
|
+
const ctx = await buildRAGContext(question, vectorStore, embed, {
|
|
5640
|
+
maxChunks: limit
|
|
5641
|
+
});
|
|
5642
|
+
if (opts.json) {
|
|
5643
|
+
console.log(JSON.stringify(ctx, null, 2));
|
|
5644
|
+
} else {
|
|
5645
|
+
console.log(source_default.bold.cyan("RAG Context"));
|
|
5646
|
+
console.log(source_default.dim(`Query: ${ctx.query}`));
|
|
5647
|
+
console.log(source_default.dim(`Chunks: ${ctx.chunks.length} | ~${ctx.estimatedTokens} tokens
|
|
5648
|
+
`));
|
|
5649
|
+
for (const c of ctx.chunks) {
|
|
5650
|
+
console.log(source_default.yellow(`[${c.score.toFixed(3)}] ${c.entityId} (${c.chunkType})`));
|
|
5651
|
+
console.log(c.content);
|
|
5652
|
+
console.log();
|
|
5653
|
+
}
|
|
5654
|
+
}
|
|
5655
|
+
} else {
|
|
5656
|
+
const queryVector = await embed(question);
|
|
5657
|
+
const results = vectorStore.search(queryVector, { limit });
|
|
5658
|
+
if (opts.json) {
|
|
5659
|
+
console.log(JSON.stringify(results.map((r) => ({
|
|
5660
|
+
score: r.score,
|
|
5661
|
+
entityId: r.chunk.entityId,
|
|
5662
|
+
chunkType: r.chunk.chunkType,
|
|
5663
|
+
content: r.chunk.content
|
|
5664
|
+
})), null, 2));
|
|
5665
|
+
} else {
|
|
5666
|
+
if (results.length === 0) {
|
|
5667
|
+
console.log(source_default.dim("No results. Run `trellis reindex` first to build the embedding index."));
|
|
5668
|
+
} else {
|
|
5669
|
+
console.log(source_default.bold(`Results for: "${question}"
|
|
5670
|
+
`));
|
|
5671
|
+
for (const r of results) {
|
|
5672
|
+
const score = source_default.dim(`[${r.score.toFixed(3)}]`);
|
|
5673
|
+
const entity = source_default.cyan(r.chunk.entityId);
|
|
5674
|
+
const type = source_default.dim(`(${r.chunk.chunkType})`);
|
|
5675
|
+
console.log(`${score} ${entity} ${type}`);
|
|
5676
|
+
const preview = r.chunk.content.length > 200 ? r.chunk.content.slice(0, 200) + "\u2026" : r.chunk.content;
|
|
5677
|
+
console.log(` ${preview}
|
|
5678
|
+
`);
|
|
5679
|
+
}
|
|
5680
|
+
}
|
|
5681
|
+
}
|
|
5682
|
+
}
|
|
5683
|
+
} catch (err) {
|
|
5684
|
+
if (err.message?.includes("No transformers")) {
|
|
5685
|
+
console.error(source_default.red("Embedding model not available."));
|
|
5686
|
+
console.error(source_default.dim("Install: bun add @huggingface/transformers"));
|
|
5687
|
+
} else {
|
|
5688
|
+
console.error(source_default.red(`Error: ${err.message}`));
|
|
5689
|
+
}
|
|
5690
|
+
} finally {
|
|
5691
|
+
vectorStore.close();
|
|
5692
|
+
}
|
|
5693
|
+
});
|
|
4693
5694
|
function formatOpKind(kind) {
|
|
4694
5695
|
const kindMap = {
|
|
4695
5696
|
"vcs:fileAdd": source_default.green("+add"),
|