trellis 2.0.8 → 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 +655 -4
- package/dist/core/index.js +470 -2
- package/dist/embeddings/index.js +5 -1
- package/dist/{index-s603ev6w.js → index-5b01h414.js} +1 -1
- package/dist/index-5m0g9r0y.js +1100 -0
- package/dist/{index-zf6htvnm.js → index-7gvjxt27.js} +166 -2
- package/dist/index-hybgxe40.js +1174 -0
- package/dist/index.js +7 -2
- package/dist/transformers.node-bx3q9d7k.js +33130 -0
- package/package.json +4 -3
- package/src/cli/index.ts +939 -0
- 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/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/dist/index-5bhe57y9.js +0 -326
package/dist/cli/index.js
CHANGED
|
@@ -3,13 +3,30 @@
|
|
|
3
3
|
import {
|
|
4
4
|
TrellisVcsEngine,
|
|
5
5
|
init_engine
|
|
6
|
-
} from "../index-
|
|
7
|
-
import
|
|
6
|
+
} from "../index-5b01h414.js";
|
|
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";
|
|
8
19
|
import"../index-3ejh8k6v.js";
|
|
9
20
|
import {
|
|
21
|
+
VectorStore,
|
|
22
|
+
buildRAGContext,
|
|
23
|
+
embed,
|
|
10
24
|
exports_embeddings,
|
|
11
|
-
|
|
12
|
-
|
|
25
|
+
init_auto_embed,
|
|
26
|
+
init_embeddings,
|
|
27
|
+
init_model,
|
|
28
|
+
init_store
|
|
29
|
+
} from "../index-7gvjxt27.js";
|
|
13
30
|
import {
|
|
14
31
|
buildRefIndex,
|
|
15
32
|
createResolverContext,
|
|
@@ -3070,6 +3087,9 @@ var source_default = chalk;
|
|
|
3070
3087
|
// src/cli/index.ts
|
|
3071
3088
|
init_engine();
|
|
3072
3089
|
import { resolve, join as join6 } from "path";
|
|
3090
|
+
init_auto_embed();
|
|
3091
|
+
init_store();
|
|
3092
|
+
init_model();
|
|
3073
3093
|
|
|
3074
3094
|
// src/git/git-reader.ts
|
|
3075
3095
|
import { execSync } from "child_process";
|
|
@@ -5040,6 +5060,637 @@ Server stopped.`));
|
|
|
5040
5060
|
process.exit(1);
|
|
5041
5061
|
}
|
|
5042
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
|
+
});
|
|
5043
5694
|
function formatOpKind(kind) {
|
|
5044
5695
|
const kindMap = {
|
|
5045
5696
|
"vcs:fileAdd": source_default.green("+add"),
|