trellis 3.1.13 → 3.1.15

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/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # Changelog
2
2
 
3
+ ## 3.1.14
4
+
5
+ - CLI `trellis --version` now reports the real package version from `package.json` (was hardcoded `0.1.0`).
6
+ - CLI `-p` / cwd resolution walks up to the nearest `.trellis` repo root (monorepo subfolders work without passing the root path).
7
+ - Clearer "not a repository" errors: show looked-from path, cwd, and `-p` hint.
8
+ - `issue create` accepts `--description` as an alias for `--desc`.
9
+ - `bin/trellis.mjs` launcher finds Homebrew Bun when Node's `PATH` is minimal (`npx trellis` / agent shells).
10
+
3
11
  ## 3.1.2
4
12
 
5
13
  - Fixed `trellis/cms` collection reads for inferred collections whose normalized key differs from the stored entity type casing, such as `blogpost` reading `BlogPost` entities.
package/LICENSE CHANGED
@@ -1,3 +1,5 @@
1
+ SPDX-License-Identifier: AGPL-3.0-or-later
2
+
1
3
  Trellis — Copyright (c) 2025-2026 Trent Brew <tbrew@turtle.tech>
2
4
 
3
5
  This program is free software: you can redistribute it and/or modify
package/README.md CHANGED
@@ -15,7 +15,7 @@
15
15
 
16
16
  Most agent frameworks focus on the _reasoning engine_ (the LLM) but treat _state_ as an afterthought. Trellis reverses this. It is the **System of Record for Decisions**, providing agents with a persistent, queryable, and auditable memory that compounds over time.
17
17
 
18
- **[Read the story →](./THE-STORY.md)**
18
+ **[Read the story →](./docs/THE-STORY.md)**
19
19
 
20
20
  - **Durable Memory**: Every thought, tool call, and file change is an immutable operation in a causal graph
21
21
  - **Explainability by Default**: Decision traces don't just store _what_ happened, but _why_
@@ -37,8 +37,8 @@ trellis init
37
37
  echo "# My Project" > README.md
38
38
 
39
39
  # 4. Add structured entities
40
- trellis issue create --title "Bootstrap Visualization"
41
- trellis milestone "Initial Release"
40
+ trellis issue create -t "Bootstrap Visualization"
41
+ trellis milestone create -m "Initial Release"
42
42
 
43
43
  # 5. Create coding session with trellis harness
44
44
  trellis code
@@ -118,14 +118,17 @@ Ops are written to `.trellis/ops.json` and **never rewritten or deleted**.
118
118
 
119
119
  ## Documentation
120
120
 
121
- - **[Local documentation](./docs/README.md)** — Canonical docs for the active codebase
121
+ - **[Documentation hub](./docs/README.md)** — Canonical docs index for Trellis
122
+ - **[The Story](./docs/THE-STORY.md)** — Why Trellis exists
122
123
  - **[Vision](./docs/VISION.md)** — Local-first agentic OS framing
123
124
  - **[Architecture](./docs/ARCHITECTURE.md)** — Current package layout and target runtime shape
124
- - **[Roadmap](./ROADMAP.md)** — Active Trellis issue and milestone sequence
125
+ - **[Five Pillars](./docs/PILLARS.md)** — Core architectural principles
126
+ - **[Design spec](./docs/DESIGN.md)** — Full architecture specification
127
+ - **[Roadmap](./docs/ROADMAP.md)** — Active Trellis issue and milestone sequence
128
+ - **[Agents guide](./docs/AGENTS.md)** — Building agents on Trellis
125
129
  - **[Full documentation](https://trellis.computer)** — Published docs site
126
- - **[CLI reference](./README-ARCHIVED.md#cli-overview)** — Command details (archived)
127
- - **[API modules](./README-ARCHIVED.md#module--subpath-guide)** — Subpath imports (archived)
128
- - **[DESIGN.md](./DESIGN.md)** — Architecture specification
130
+ - **[CLI reference](./docs/README-ARCHIVED.md#cli-overview)** — Command details (archived)
131
+ - **[API modules](./docs/README-ARCHIVED.md#module--subpath-guide)** — Subpath imports (archived)
129
132
 
130
133
  ---
131
134
 
@@ -162,4 +165,4 @@ bun run build
162
165
 
163
166
  ---
164
167
 
165
- _For comprehensive documentation including detailed CLI commands, API examples, and subsystem guides, see [README-ARCHIVED.md](./README-ARCHIVED.md)._
168
+ _For comprehensive documentation including detailed CLI commands, API examples, and subsystem guides, see [docs/README-ARCHIVED.md](./docs/README-ARCHIVED.md)._
package/bin/trellis.mjs CHANGED
@@ -17,7 +17,10 @@ const isWindows = process.platform === 'win32';
17
17
  if (process.versions?.bun) {
18
18
  await import('../dist/cli/index.js');
19
19
  } else {
20
- const bun = findOnPath('bun') ?? findUserBun();
20
+ const bun =
21
+ findOnPath('bun') ??
22
+ findUserBun() ??
23
+ (existsSync('/opt/homebrew/bin/bun') ? '/opt/homebrew/bin/bun' : null);
21
24
  if (!bun) {
22
25
  process.stderr.write(
23
26
  [
@@ -38,8 +41,13 @@ if (process.versions?.bun) {
38
41
  const here = fileURLToPath(import.meta.url);
39
42
  const result = spawnSync(bun, [here, ...process.argv.slice(2)], {
40
43
  stdio: 'inherit',
44
+ env: process.env,
41
45
  });
42
- process.exit(result.status ?? 1);
46
+ const code = result.status ?? 1;
47
+ if (code !== 0 && result.error) {
48
+ process.stderr.write(`${result.error.message}\n`);
49
+ }
50
+ process.exit(code);
43
51
  }
44
52
 
45
53
  function findOnPath(cmd) {
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":";AAEA;;;;;;;;;;GAUG;AAwCH,MAAM,MAAM,OAAO,GACf,QAAQ,GACR,UAAU,GACV,QAAQ,GACR,SAAS,GACT,OAAO,GACP,QAAQ,GACR,MAAM,CAAC;AACX,MAAM,MAAM,kBAAkB,GAAG,SAAS,GAAG,UAAU,GAAG,MAAM,CAAC;AACjE,MAAM,MAAM,aAAa,GACrB,OAAO,GACP,KAAK,GACL,QAAQ,GACR,MAAM,GACN,MAAM,GACN,UAAU,GACV,MAAM,GACN,KAAK,GACL,MAAM,GACN,KAAK,GACL,SAAS,GACT,WAAW,GACX,OAAO,GACP,MAAM,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":";AAEA;;;;;;;;;;GAUG;AAyCH,MAAM,MAAM,OAAO,GACf,QAAQ,GACR,UAAU,GACV,QAAQ,GACR,SAAS,GACT,OAAO,GACP,QAAQ,GACR,MAAM,CAAC;AACX,MAAM,MAAM,kBAAkB,GAAG,SAAS,GAAG,UAAU,GAAG,MAAM,CAAC;AACjE,MAAM,MAAM,aAAa,GACrB,OAAO,GACP,KAAK,GACL,QAAQ,GACR,MAAM,GACN,MAAM,GACN,UAAU,GACV,MAAM,GACN,KAAK,GACL,MAAM,GACN,KAAK,GACL,SAAS,GACT,WAAW,GACX,OAAO,GACP,MAAM,CAAC"}
package/dist/cli/index.js CHANGED
@@ -2401,8 +2401,8 @@ var exports_server = {};
2401
2401
  __export(exports_server, {
2402
2402
  startUIServer: () => startUIServer
2403
2403
  });
2404
- import { readFileSync as readFileSync3, existsSync as existsSync5 } from "fs";
2405
- import { join as join5, dirname as dirname4 } from "path";
2404
+ import { readFileSync as readFileSync4, existsSync as existsSync5 } from "fs";
2405
+ import { join as join5, dirname as dirname5 } from "path";
2406
2406
  function buildGraph(engine) {
2407
2407
  const nodes = [];
2408
2408
  const edges = [];
@@ -2506,7 +2506,7 @@ function buildGraph(engine) {
2506
2506
  if (existsSync5(absPath)) {
2507
2507
  mdFiles.push({
2508
2508
  path: f.path,
2509
- content: readFileSync3(absPath, "utf-8")
2509
+ content: readFileSync4(absPath, "utf-8")
2510
2510
  });
2511
2511
  }
2512
2512
  }
@@ -2732,26 +2732,26 @@ async function startUIServer(opts) {
2732
2732
  const engine = new TrellisVcsEngine({ rootPath: opts.rootPath });
2733
2733
  engine.open();
2734
2734
  function findClientHtml() {
2735
- const here = dirname4(process.argv[1]);
2735
+ const here2 = dirname5(process.argv[1]);
2736
2736
  const candidates = [
2737
- join5(here, "client.html"),
2738
- join5(here, "..", "ui", "client.html"),
2739
- join5(here, "ui", "client.html")
2737
+ join5(here2, "client.html"),
2738
+ join5(here2, "..", "ui", "client.html"),
2739
+ join5(here2, "ui", "client.html")
2740
2740
  ];
2741
- let dir = here;
2741
+ let dir = here2;
2742
2742
  for (let i = 0;i < 5; i++) {
2743
2743
  candidates.push(join5(dir, "dist", "ui", "client.html"));
2744
2744
  candidates.push(join5(dir, "ui", "client.html"));
2745
- dir = dirname4(dir);
2745
+ dir = dirname5(dir);
2746
2746
  }
2747
2747
  for (const p of candidates) {
2748
2748
  if (existsSync5(p))
2749
2749
  return p;
2750
2750
  }
2751
- throw new Error(`Could not find client.html. Searched from: ${here}
2751
+ throw new Error(`Could not find client.html. Searched from: ${here2}
2752
2752
  Try reinstalling the package or running \`bun run build\`.`);
2753
2753
  }
2754
- const clientHtml = readFileSync3(findClientHtml(), "utf-8");
2754
+ const clientHtml = readFileSync4(findClientHtml(), "utf-8");
2755
2755
  let embeddingManager = null;
2756
2756
  function getEmbeddingManager() {
2757
2757
  if (!embeddingManager) {
@@ -3375,7 +3375,7 @@ var chalkStderr = createChalk({ level: stderrColor ? stderrColor.level : 0 });
3375
3375
  var source_default = chalk;
3376
3376
 
3377
3377
  // src/cli/index.ts
3378
- import { resolve, join as join6 } from "path";
3378
+ import { resolve as resolve2, join as join6 } from "path";
3379
3379
 
3380
3380
  // node_modules/@inquirer/core/dist/lib/key.js
3381
3381
  var isUpKey = (key, keybindings = []) => key.name === "up" || keybindings.includes("vim") && key.name === "k" || keybindings.includes("emacs") && key.ctrl && key.name === "p";
@@ -5706,15 +5706,54 @@ function base58btcEncode(buf) {
5706
5706
  }
5707
5707
  return encoded || "1";
5708
5708
  }
5709
- // src/cli/index.ts
5710
- var program2 = new Command;
5711
- program2.name("trellis").description("TrellisVCS \u2014 graph-native, code-first version control").version("0.1.0");
5712
- function requireRepo(rootPath) {
5713
- if (!TrellisVcsEngine.isRepo(rootPath)) {
5714
- console.error(source_default.red("Not a TrellisVCS repository. Run `trellis init` first."));
5715
- process.exit(1);
5709
+ // src/cli/repo-path.ts
5710
+ import { readFileSync as readFileSync3 } from "fs";
5711
+ import { dirname as dirname4, resolve } from "path";
5712
+ import { fileURLToPath } from "url";
5713
+ init_engine();
5714
+ var here = dirname4(fileURLToPath(import.meta.url));
5715
+ function cliVersion() {
5716
+ const path = resolve(here, "../../package.json");
5717
+ try {
5718
+ const pkg = JSON.parse(readFileSync3(path, "utf8"));
5719
+ if (typeof pkg.version === "string")
5720
+ return pkg.version;
5721
+ } catch {}
5722
+ return "0.0.0";
5723
+ }
5724
+ function findRepoRoot(pathOpt) {
5725
+ const start = resolve(pathOpt ?? process.cwd());
5726
+ let dir = start;
5727
+ while (true) {
5728
+ if (TrellisVcsEngine.isRepo(dir))
5729
+ return dir;
5730
+ const parent = dirname4(dir);
5731
+ if (parent === dir)
5732
+ return;
5733
+ dir = parent;
5716
5734
  }
5717
5735
  }
5736
+ function resolveRepoRoot(pathOpt) {
5737
+ const start = resolve(pathOpt ?? process.cwd());
5738
+ const found = findRepoRoot(pathOpt);
5739
+ if (found)
5740
+ return found;
5741
+ failNotRepo(start, pathOpt);
5742
+ }
5743
+ function failNotRepo(start, pathOpt) {
5744
+ console.error(source_default.red("Not a TrellisVCS repository."));
5745
+ console.error(source_default.dim(` looked from: ${start}`));
5746
+ if (pathOpt && resolve(pathOpt) !== process.cwd()) {
5747
+ console.error(source_default.dim(` -p: ${resolve(pathOpt)}`));
5748
+ }
5749
+ console.error(source_default.dim(` cwd: ${process.cwd()}`));
5750
+ console.error(source_default.dim(" Hint: run from the repo root, pass -p <path>, or run `trellis init`."));
5751
+ process.exit(1);
5752
+ }
5753
+
5754
+ // src/cli/index.ts
5755
+ var program2 = new Command;
5756
+ program2.name("trellis").description("TrellisVCS \u2014 graph-native, code-first version control").version(cliVersion());
5718
5757
  async function runInit(rootPath, opts = {}) {
5719
5758
  const isInteractive = opts.interactive !== false && process.stdout.isTTY && !process.argv.includes("--no-interactive");
5720
5759
  if (TrellisVcsEngine.isRepo(rootPath)) {
@@ -5893,7 +5932,7 @@ async function runInit(rootPath, opts = {}) {
5893
5932
  }
5894
5933
  }
5895
5934
  program2.command("init").description("Initialize a new TrellisVCS repository in the current directory").option("-p, --path <path>", "Path to initialize", ".").option("--ides <ides...>", "IDEs to scaffold for (cursor, windsurf, claude, copilot, codex, gemini)").option("--framework <framework>", "Project framework (react, vue, svelte, next, nuxt, remotion, expo, bun, node, cli, library, animation, games, none)", "none").option("--footprint <footprint>", "Workspace footprint (minimal, standard, full)", "standard").option("--no-interactive", "Skip interactive prompts").action(async (opts) => {
5896
- const rootPath = resolve(opts.path);
5935
+ const rootPath = resolve2(opts.path);
5897
5936
  if (TrellisVcsEngine.isRepo(rootPath)) {
5898
5937
  console.log(source_default.yellow("Already a Trellis workspace."));
5899
5938
  return;
@@ -5911,7 +5950,6 @@ program2.command("init").description("Initialize a new TrellisVCS repository in
5911
5950
  console.log(` ${source_default.dim("Ops:")} ${opsCount} initial operations scanned`);
5912
5951
  console.log(` ${source_default.dim("Config:")} .trellis/config.json`);
5913
5952
  console.log(` ${source_default.dim("Op log:")} .trellis/ops.json`);
5914
- console.log(` ${source_default.dim("Graph DB:")} .trellis/graph.db`);
5915
5953
  console.log(` ${source_default.dim("Agent context:")} .trellis/agents/AGENTS.md`);
5916
5954
  const preInfer = await inferProjectContext(rootPath);
5917
5955
  if (preInfer.domain) {
@@ -5936,8 +5974,7 @@ program2.command("init").description("Initialize a new TrellisVCS repository in
5936
5974
  console.log(source_default.dim("The causal stream is now active. Every file change will be tracked."));
5937
5975
  });
5938
5976
  program2.command("seed").description("Refresh agent context files with current project state").option("-p, --path <path>", "Repository path", ".").option("--ide <ide>", "IDE to update (cursor, windsurf, claude, copilot)", "none").option("-f, --force", "Force rewrite even if files do not exist", false).action(async (opts) => {
5939
- const rootPath = resolve(opts.path);
5940
- requireRepo(rootPath);
5977
+ const rootPath = resolveRepoRoot(opts.path);
5941
5978
  const ide = opts.ide || "none";
5942
5979
  const force = opts.force || false;
5943
5980
  console.log(source_default.dim("Seeding agent context..."));
@@ -5954,8 +5991,7 @@ program2.command("seed").description("Refresh agent context files with current p
5954
5991
  }
5955
5992
  });
5956
5993
  program2.command("repair").description("Attempt to repair a corrupted .trellis/ops.json file").option("-p, --path <path>", "Repository path", ".").action((opts) => {
5957
- const rootPath = resolve(opts.path);
5958
- requireRepo(rootPath);
5994
+ const rootPath = resolveRepoRoot(opts.path);
5959
5995
  console.log(source_default.yellow("Attempting to repair ops.json..."));
5960
5996
  const result = TrellisVcsEngine.repair(rootPath);
5961
5997
  if (result.lost === -1) {
@@ -5967,11 +6003,7 @@ program2.command("repair").description("Attempt to repair a corrupted .trellis/o
5967
6003
  }
5968
6004
  });
5969
6005
  program2.command("status").description("Show current repository status").option("-p, --path <path>", "Repository path", ".").action(async (opts) => {
5970
- const rootPath = resolve(opts.path);
5971
- if (!TrellisVcsEngine.isRepo(rootPath)) {
5972
- console.log(source_default.red("Not a TrellisVCS repository. Run `trellis init` first."));
5973
- process.exit(1);
5974
- }
6006
+ const rootPath = resolveRepoRoot(opts.path);
5975
6007
  const engine = new TrellisVcsEngine({ rootPath });
5976
6008
  engine.open();
5977
6009
  const st = engine.status();
@@ -6001,11 +6033,7 @@ program2.command("status").description("Show current repository status").option(
6001
6033
  }
6002
6034
  });
6003
6035
  program2.command("log").description("Show operation history").option("-p, --path <path>", "Repository path", ".").option("-n, --limit <n>", "Number of ops to show", "20").option("-f, --file <file>", "Filter by file path").action(async (opts) => {
6004
- const rootPath = resolve(opts.path);
6005
- if (!TrellisVcsEngine.isRepo(rootPath)) {
6006
- console.log(source_default.red("Not a TrellisVCS repository. Run `trellis init` first."));
6007
- process.exit(1);
6008
- }
6036
+ const rootPath = resolveRepoRoot(opts.path);
6009
6037
  const engine = new TrellisVcsEngine({ rootPath });
6010
6038
  engine.open();
6011
6039
  const ops = engine.log({
@@ -6028,11 +6056,7 @@ program2.command("log").description("Show operation history").option("-p, --path
6028
6056
  }
6029
6057
  });
6030
6058
  program2.command("files").description("List all tracked files").option("-p, --path <path>", "Repository path", ".").action(async (opts) => {
6031
- const rootPath = resolve(opts.path);
6032
- if (!TrellisVcsEngine.isRepo(rootPath)) {
6033
- console.log(source_default.red("Not a TrellisVCS repository. Run `trellis init` first."));
6034
- process.exit(1);
6035
- }
6059
+ const rootPath = resolveRepoRoot(opts.path);
6036
6060
  const engine = new TrellisVcsEngine({ rootPath });
6037
6061
  engine.open();
6038
6062
  const files = engine.trackedFiles();
@@ -6048,11 +6072,7 @@ program2.command("files").description("List all tracked files").option("-p, --pa
6048
6072
  }
6049
6073
  });
6050
6074
  program2.command("watch").description("Start file watcher (foreground, Ctrl+C to stop)").option("-p, --path <path>", "Repository path", ".").action(async (opts) => {
6051
- const rootPath = resolve(opts.path);
6052
- if (!TrellisVcsEngine.isRepo(rootPath)) {
6053
- console.log(source_default.red("Not a TrellisVCS repository. Run `trellis init` first."));
6054
- process.exit(1);
6055
- }
6075
+ const rootPath = resolveRepoRoot(opts.path);
6056
6076
  const engine = new TrellisVcsEngine({ rootPath });
6057
6077
  engine.open();
6058
6078
  console.log(source_default.green("\u2713 Watching for changes\u2026") + source_default.dim(" (Ctrl+C to stop)"));
@@ -6067,8 +6087,8 @@ program2.command("watch").description("Start file watcher (foreground, Ctrl+C to
6067
6087
  });
6068
6088
  });
6069
6089
  program2.command("import").description("Import from an existing Git repository").requiredOption("--from <path>", "Path to the Git repository to import from").option("-p, --path <path>", "Target TrellisVCS repository path", ".").action(async (opts) => {
6070
- const from = resolve(opts.from);
6071
- const to = resolve(opts.path);
6090
+ const from = resolve2(opts.from);
6091
+ const to = resolve2(opts.path);
6072
6092
  console.log(source_default.dim(`Importing from Git: ${from}`));
6073
6093
  console.log(source_default.dim(`Target: ${to}`));
6074
6094
  console.log();
@@ -6103,12 +6123,8 @@ Import failed: ${err.message}`));
6103
6123
  }
6104
6124
  });
6105
6125
  program2.command("export").description("Export milestones to a Git repository").requiredOption("--to <path>", "Path to the target Git repository").option("-p, --path <path>", "Source TrellisVCS repository path", ".").option("--author-name <name>", "Author name for Git commits").option("--author-email <email>", "Author email for Git commits").action(async (opts) => {
6106
- const from = resolve(opts.path);
6107
- const to = resolve(opts.to);
6108
- if (!TrellisVcsEngine.isRepo(from)) {
6109
- console.error(source_default.red("Not a TrellisVCS repository. Run `trellis init` first."));
6110
- process.exit(1);
6111
- }
6126
+ const from = resolveRepoRoot(opts.path);
6127
+ const to = resolve2(opts.to);
6112
6128
  console.log(source_default.dim(`Exporting from: ${from}`));
6113
6129
  console.log(source_default.dim(`Target Git repo: ${to}`));
6114
6130
  console.log();
@@ -6141,11 +6157,7 @@ Export failed: ${err.message}`));
6141
6157
  }
6142
6158
  });
6143
6159
  program2.command("branch").description("Manage branches").argument("[name]", "Branch name to create or switch to").option("-d, --delete <name>", "Delete a branch").option("-l, --list", "List all branches").option("-p, --path <path>", "Repository path", ".").action(async (name, opts) => {
6144
- const rootPath = resolve(opts.path);
6145
- if (!TrellisVcsEngine.isRepo(rootPath)) {
6146
- console.error(source_default.red("Not a TrellisVCS repository. Run `trellis init` first."));
6147
- process.exit(1);
6148
- }
6160
+ const rootPath = resolveRepoRoot(opts.path);
6149
6161
  const engine = new TrellisVcsEngine({ rootPath });
6150
6162
  engine.open();
6151
6163
  if (opts.delete) {
@@ -6196,11 +6208,7 @@ program2.command("branch").description("Manage branches").argument("[name]", "Br
6196
6208
  }
6197
6209
  });
6198
6210
  program2.command("milestone").description("Create or list milestones").argument("[action]", '"create" or "list" (default: list)').option("-m, --message <message>", "Milestone message").option("--from <hash>", "Start op hash for the milestone range").option("--to <hash>", "End op hash for the milestone range").option("-p, --path <path>", "Repository path", ".").action(async (action, opts) => {
6199
- const rootPath = resolve(opts.path);
6200
- if (!TrellisVcsEngine.isRepo(rootPath)) {
6201
- console.error(source_default.red("Not a TrellisVCS repository. Run `trellis init` first."));
6202
- process.exit(1);
6203
- }
6211
+ const rootPath = resolveRepoRoot(opts.path);
6204
6212
  const engine = new TrellisVcsEngine({ rootPath });
6205
6213
  engine.open();
6206
6214
  if (action === "create") {
@@ -6241,11 +6249,7 @@ program2.command("milestone").description("Create or list milestones").argument(
6241
6249
  }
6242
6250
  });
6243
6251
  program2.command("checkpoint").description("Create or list checkpoints").argument("[action]", '"create" or "list" (default: list)').option("-p, --path <path>", "Repository path", ".").action(async (action, opts) => {
6244
- const rootPath = resolve(opts.path);
6245
- if (!TrellisVcsEngine.isRepo(rootPath)) {
6246
- console.error(source_default.red("Not a TrellisVCS repository. Run `trellis init` first."));
6247
- process.exit(1);
6248
- }
6252
+ const rootPath = resolveRepoRoot(opts.path);
6249
6253
  const engine = new TrellisVcsEngine({ rootPath });
6250
6254
  engine.open();
6251
6255
  if (action === "create") {
@@ -6273,11 +6277,7 @@ program2.command("checkpoint").description("Create or list checkpoints").argumen
6273
6277
  }
6274
6278
  });
6275
6279
  program2.command("diff").description("Show file-level diff between two points in history").argument("[from]", "Starting op hash or milestone ID").argument("[to]", "Ending op hash (default: current head)").option("-p, --path <path>", "Repository path", ".").option("--stat", "Show only summary stats").action((from, to, opts) => {
6276
- const rootPath = resolve(opts.path);
6277
- if (!TrellisVcsEngine.isRepo(rootPath)) {
6278
- console.error(source_default.red("Not a TrellisVCS repository. Run `trellis init` first."));
6279
- process.exit(1);
6280
- }
6280
+ const rootPath = resolveRepoRoot(opts.path);
6281
6281
  const engine = new TrellisVcsEngine({ rootPath });
6282
6282
  engine.open();
6283
6283
  let result;
@@ -6335,11 +6335,7 @@ program2.command("diff").description("Show file-level diff between two points in
6335
6335
  }
6336
6336
  });
6337
6337
  program2.command("merge").description("Merge a branch into the current branch").argument("<branch>", "Source branch to merge").option("-p, --path <path>", "Repository path", ".").option("--dry-run", "Preview merge without applying changes").action((branch, opts) => {
6338
- const rootPath = resolve(opts.path);
6339
- if (!TrellisVcsEngine.isRepo(rootPath)) {
6340
- console.error(source_default.red("Not a TrellisVCS repository. Run `trellis init` first."));
6341
- process.exit(1);
6342
- }
6338
+ const rootPath = resolveRepoRoot(opts.path);
6343
6339
  const engine = new TrellisVcsEngine({ rootPath });
6344
6340
  engine.open();
6345
6341
  const result = engine.mergeBranch(branch);
@@ -6365,13 +6361,12 @@ program2.command("merge").description("Merge a branch into the current branch").
6365
6361
  }
6366
6362
  });
6367
6363
  program2.command("parse").description("Parse a file into AST-level semantic entities").argument("<file>", "File to parse").option("-p, --path <path>", "Repository path", ".").action((file, opts) => {
6368
- const rootPath = resolve(opts.path);
6364
+ const rootPath = resolveRepoRoot(opts.path);
6369
6365
  const engine = new TrellisVcsEngine({ rootPath });
6370
- if (TrellisVcsEngine.isRepo(rootPath))
6371
- engine.open();
6372
- const { readFileSync: readFileSync4 } = __require("fs");
6373
- const filePath = resolve(file);
6374
- const content = readFileSync4(filePath, "utf-8");
6366
+ engine.open();
6367
+ const { readFileSync: readFileSync5 } = __require("fs");
6368
+ const filePath = resolve2(file);
6369
+ const content = readFileSync5(filePath, "utf-8");
6375
6370
  const result = engine.parseFile(content, file);
6376
6371
  if (!result) {
6377
6372
  console.log(source_default.dim(`No parser available for: ${file}`));
@@ -6403,13 +6398,12 @@ program2.command("parse").description("Parse a file into AST-level semantic enti
6403
6398
  }
6404
6399
  });
6405
6400
  program2.command("sdiff").description("Show semantic diff between two versions of a file").argument("<fileA>", "Old version of the file").argument("<fileB>", "New version of the file").option("-p, --path <path>", "Repository path", ".").action((fileA, fileB, opts) => {
6406
- const rootPath = resolve(opts.path);
6401
+ const rootPath = resolveRepoRoot(opts.path);
6407
6402
  const engine = new TrellisVcsEngine({ rootPath });
6408
- if (TrellisVcsEngine.isRepo(rootPath))
6409
- engine.open();
6410
- const { readFileSync: readFileSync4 } = __require("fs");
6411
- const oldContent = readFileSync4(resolve(fileA), "utf-8");
6412
- const newContent = readFileSync4(resolve(fileB), "utf-8");
6403
+ engine.open();
6404
+ const { readFileSync: readFileSync5 } = __require("fs");
6405
+ const oldContent = readFileSync5(resolve2(fileA), "utf-8");
6406
+ const newContent = readFileSync5(resolve2(fileB), "utf-8");
6413
6407
  const patches = engine.semanticDiff(oldContent, newContent, fileA);
6414
6408
  if (patches.length === 0) {
6415
6409
  console.log(source_default.dim("No semantic differences."));
@@ -6455,11 +6449,7 @@ program2.command("sdiff").description("Show semantic diff between two versions o
6455
6449
  }
6456
6450
  });
6457
6451
  program2.command("sync").description("Sync operations with another TrellisVCS repository").argument("[action]", '"push", "pull", "status", or "reconcile" (default: status)').option("-p, --path <path>", "Local repository path", ".").option("--remote <remote>", "Remote repository path").action((action, opts) => {
6458
- const rootPath = resolve(opts.path);
6459
- if (!TrellisVcsEngine.isRepo(rootPath)) {
6460
- console.error(source_default.red("Not a TrellisVCS repository. Run `trellis init` first."));
6461
- process.exit(1);
6462
- }
6452
+ const rootPath = resolveRepoRoot(opts.path);
6463
6453
  const engine = new TrellisVcsEngine({ rootPath });
6464
6454
  engine.open();
6465
6455
  const ops = engine.getOps();
@@ -6472,7 +6462,7 @@ program2.command("sync").description("Sync operations with another TrellisVCS re
6472
6462
  return;
6473
6463
  }
6474
6464
  if (action === "reconcile" && opts.remote) {
6475
- const remotePath = resolve(opts.remote);
6465
+ const remotePath = resolve2(opts.remote);
6476
6466
  if (!TrellisVcsEngine.isRepo(remotePath)) {
6477
6467
  console.error(source_default.red(`Not a TrellisVCS repository: ${remotePath}`));
6478
6468
  process.exit(1);
@@ -6502,11 +6492,7 @@ program2.command("sync").description("Sync operations with another TrellisVCS re
6502
6492
  console.log(source_default.dim("Full peer sync requires a transport layer (coming soon)."));
6503
6493
  });
6504
6494
  program2.command("garden").description("Explore the Idea Garden \u2014 abandoned work clusters").argument("[action]", '"list", "show <id>", "search", "revive <id>", or "stats" (default: list)').argument("[id]", "Cluster ID (for show/revive)").option("-p, --path <path>", "Repository path", ".").option("-f, --file <file>", "Filter by file path").option("-k, --keyword <keyword>", "Filter by keyword").option("-s, --status <status>", "Filter by status (abandoned|draft|revived)").option("-n, --limit <n>", "Max results", parseInt).action((action, id, opts) => {
6505
- const rootPath = resolve(opts.path);
6506
- if (!TrellisVcsEngine.isRepo(rootPath)) {
6507
- console.error(source_default.red("Not a TrellisVCS repository. Run `trellis init` first."));
6508
- process.exit(1);
6509
- }
6495
+ const rootPath = resolveRepoRoot(opts.path);
6510
6496
  const engine = new TrellisVcsEngine({ rootPath });
6511
6497
  engine.open();
6512
6498
  const garden = engine.garden();
@@ -6663,9 +6649,8 @@ function formatCriterionStatus(status) {
6663
6649
  }
6664
6650
  }
6665
6651
  var issueCmd = program2.command("issue").description("Manage issues (task tracking)");
6666
- issueCmd.command("create").description("Create a new issue").requiredOption("-t, --title <title>", "Issue title").option("-P, --priority <priority>", "Priority: critical, high, medium, low", "medium").option("-l, --labels <labels>", "Comma-separated labels").option("--assignee <agentId>", "Agent to assign").option("--parent <id>", "Parent issue ID (for sub-tasks)").option("-d, --desc <description>", "Short description").option("-S, --status <status>", "Initial status: backlog (default) or queue", "backlog").option("--ac <criteria...>", 'Acceptance criteria. Prefix with "test:" for test commands').option("-p, --path <path>", "Repository path", ".").action(async (opts) => {
6667
- const rootPath = resolve(opts.path);
6668
- requireRepo(rootPath);
6652
+ issueCmd.command("create").description("Create a new issue").requiredOption("-t, --title <title>", "Issue title").option("-P, --priority <priority>", "Priority: critical, high, medium, low", "medium").option("-l, --labels <labels>", "Comma-separated labels").option("--assignee <agentId>", "Agent to assign").option("--parent <id>", "Parent issue ID (for sub-tasks)").option("-d, --desc <description>", "Short description").option("--description <description>", "Alias for --desc").option("-S, --status <status>", "Initial status: backlog (default) or queue", "backlog").option("--ac <criteria...>", 'Acceptance criteria. Prefix with "test:" for test commands').option("-p, --path <path>", "Repository path", ".").action(async (opts) => {
6653
+ const rootPath = resolveRepoRoot(opts.path);
6669
6654
  const engine = new TrellisVcsEngine({ rootPath });
6670
6655
  engine.open();
6671
6656
  const labels = opts.labels ? opts.labels.split(",").map((l) => l.trim()) : undefined;
@@ -6680,7 +6665,7 @@ issueCmd.command("create").description("Create a new issue").requiredOption("-t,
6680
6665
  labels,
6681
6666
  assignee: opts.assignee,
6682
6667
  parentId: opts.parent,
6683
- description: opts.desc,
6668
+ description: opts.desc ?? opts.description,
6684
6669
  status: opts.status,
6685
6670
  criteria
6686
6671
  });
@@ -6699,8 +6684,7 @@ issueCmd.command("create").description("Create a new issue").requiredOption("-t,
6699
6684
  }
6700
6685
  });
6701
6686
  issueCmd.command("list").description("List issues").option("--status <status>", "Filter by status: backlog, queue, in_progress, paused, closed").option("--label <label>", "Filter by label").option("--assignee <agentId>", "Filter by assignee").option("--parent <id>", "Filter by parent issue").option("-p, --path <path>", "Repository path", ".").action((opts) => {
6702
- const rootPath = resolve(opts.path);
6703
- requireRepo(rootPath);
6687
+ const rootPath = resolveRepoRoot(opts.path);
6704
6688
  const engine = new TrellisVcsEngine({ rootPath });
6705
6689
  engine.open();
6706
6690
  const issues = engine.listIssues({
@@ -6725,8 +6709,7 @@ issueCmd.command("list").description("List issues").option("--status <status>",
6725
6709
  }
6726
6710
  });
6727
6711
  issueCmd.command("show").description("Show issue details").argument("<id>", "Issue ID (e.g. TRL-1)").option("-p, --path <path>", "Repository path", ".").action((id, opts) => {
6728
- const rootPath = resolve(opts.path);
6729
- requireRepo(rootPath);
6712
+ const rootPath = resolveRepoRoot(opts.path);
6730
6713
  const engine = new TrellisVcsEngine({ rootPath });
6731
6714
  engine.open();
6732
6715
  const issue = engine.getIssue(id);
@@ -6780,8 +6763,7 @@ issueCmd.command("show").description("Show issue details").argument("<id>", "Iss
6780
6763
  }
6781
6764
  });
6782
6765
  issueCmd.command("start").description("Start working on an issue (creates branch, auto-assigns)").argument("<id>", "Issue ID").option("-p, --path <path>", "Repository path", ".").action(async (id, opts) => {
6783
- const rootPath = resolve(opts.path);
6784
- requireRepo(rootPath);
6766
+ const rootPath = resolveRepoRoot(opts.path);
6785
6767
  const engine = new TrellisVcsEngine({ rootPath });
6786
6768
  engine.open();
6787
6769
  const op = await engine.startIssue(id);
@@ -6795,8 +6777,7 @@ issueCmd.command("start").description("Start working on an issue (creates branch
6795
6777
  }
6796
6778
  });
6797
6779
  issueCmd.command("pause").description("Pause an in-progress issue (switches to default branch)").argument("<id>", "Issue ID").requiredOption("-n, --note <note>", "Why paused and what must happen before resuming").option("-p, --path <path>", "Repository path", ".").action(async (id, opts) => {
6798
- const rootPath = resolve(opts.path);
6799
- requireRepo(rootPath);
6780
+ const rootPath = resolveRepoRoot(opts.path);
6800
6781
  const engine = new TrellisVcsEngine({ rootPath });
6801
6782
  engine.open();
6802
6783
  await engine.pauseIssue(id, opts.note);
@@ -6805,8 +6786,7 @@ issueCmd.command("pause").description("Pause an in-progress issue (switches to d
6805
6786
  console.log(` ${source_default.dim("Switched to:")} ${engine.getCurrentBranch()}`);
6806
6787
  });
6807
6788
  issueCmd.command("resume").description("Resume a paused issue (switches to issue branch)").argument("<id>", "Issue ID").option("-p, --path <path>", "Repository path", ".").action(async (id, opts) => {
6808
- const rootPath = resolve(opts.path);
6809
- requireRepo(rootPath);
6789
+ const rootPath = resolveRepoRoot(opts.path);
6810
6790
  const engine = new TrellisVcsEngine({ rootPath });
6811
6791
  engine.open();
6812
6792
  await engine.resumeIssue(id);
@@ -6817,23 +6797,22 @@ issueCmd.command("resume").description("Resume a paused issue (switches to issue
6817
6797
  }
6818
6798
  });
6819
6799
  issueCmd.command("triage").description("Move a backlog issue to queue (ready to start)").argument("<id>", "Issue ID").option("-p, --path <path>", "Repository path", ".").action(async (id, opts) => {
6820
- const rootPath = resolve(opts.path);
6821
- requireRepo(rootPath);
6800
+ const rootPath = resolveRepoRoot(opts.path);
6822
6801
  const engine = new TrellisVcsEngine({ rootPath });
6823
6802
  engine.open();
6824
6803
  await engine.triageIssue(id);
6825
6804
  console.log(source_default.green(`\u2713 Triaged ${source_default.bold(id)} \u2192 queue`));
6826
6805
  });
6827
- issueCmd.command("update").description("Update issue metadata").argument("<id>", "Issue ID").option("--title <title>", "New title").option("-d, --desc <description>", "Short description").option("--status <status>", "New status: backlog, queue, in_progress, paused, closed").option("-P, --priority <priority>", "Priority: critical, high, medium, low").option("-l, --labels <labels>", "Comma-separated labels").option("--assignee <agentId>", "Agent to assign").option("-p, --path <path>", "Repository path", ".").action(async (id, opts) => {
6828
- const rootPath = resolve(opts.path);
6829
- requireRepo(rootPath);
6806
+ issueCmd.command("update").description("Update issue metadata").argument("<id>", "Issue ID").option("--title <title>", "New title").option("-d, --desc <description>", "Short description").option("--description <description>", "Alias for --desc").option("--status <status>", "New status: backlog, queue, in_progress, paused, closed").option("-P, --priority <priority>", "Priority: critical, high, medium, low").option("-l, --labels <labels>", "Comma-separated labels").option("--assignee <agentId>", "Agent to assign").option("-p, --path <path>", "Repository path", ".").action(async (id, opts) => {
6807
+ const rootPath = resolveRepoRoot(opts.path);
6830
6808
  const engine = new TrellisVcsEngine({ rootPath });
6831
6809
  engine.open();
6832
6810
  const updates = {};
6833
6811
  if (opts.title !== undefined)
6834
6812
  updates.title = opts.title;
6835
- if (opts.desc !== undefined)
6836
- updates.description = opts.desc;
6813
+ const desc = opts.desc ?? opts.description;
6814
+ if (desc !== undefined)
6815
+ updates.description = desc;
6837
6816
  if (opts.status !== undefined)
6838
6817
  updates.status = opts.status;
6839
6818
  if (opts.priority !== undefined)
@@ -6847,24 +6826,21 @@ issueCmd.command("update").description("Update issue metadata").argument("<id>",
6847
6826
  console.log(source_default.green(`\u2713 Updated ${source_default.bold(id)}`));
6848
6827
  });
6849
6828
  issueCmd.command("describe").description("Set an issue description").argument("<id>", "Issue ID").argument("<description>", "Short description text").option("-p, --path <path>", "Repository path", ".").action(async (id, description, opts) => {
6850
- const rootPath = resolve(opts.path);
6851
- requireRepo(rootPath);
6829
+ const rootPath = resolveRepoRoot(opts.path);
6852
6830
  const engine = new TrellisVcsEngine({ rootPath });
6853
6831
  engine.open();
6854
6832
  await engine.updateIssue(id, { description });
6855
6833
  console.log(source_default.green(`\u2713 Description set for ${source_default.bold(id)}`));
6856
6834
  });
6857
6835
  issueCmd.command("assign").description("Assign an issue to an agent").argument("<id>", "Issue ID").requiredOption("--to <agentId>", "Agent ID to assign").option("-p, --path <path>", "Repository path", ".").action(async (id, opts) => {
6858
- const rootPath = resolve(opts.path);
6859
- requireRepo(rootPath);
6836
+ const rootPath = resolveRepoRoot(opts.path);
6860
6837
  const engine = new TrellisVcsEngine({ rootPath });
6861
6838
  engine.open();
6862
6839
  await engine.assignIssue(id, opts.to);
6863
6840
  console.log(source_default.green(`\u2713 Assigned ${source_default.bold(id)} \u2192 ${opts.to}`));
6864
6841
  });
6865
6842
  issueCmd.command("ac").description("Add acceptance criterion to an issue").argument("<id>", "Issue ID").argument("<description>", "Criterion description").option("--test <command>", "Shell command to validate (exit 0 = pass)").option("-p, --path <path>", "Repository path", ".").action(async (id, description, opts) => {
6866
- const rootPath = resolve(opts.path);
6867
- requireRepo(rootPath);
6843
+ const rootPath = resolveRepoRoot(opts.path);
6868
6844
  const engine = new TrellisVcsEngine({ rootPath });
6869
6845
  engine.open();
6870
6846
  await engine.addCriterion(id, description, opts.test);
@@ -6872,24 +6848,21 @@ issueCmd.command("ac").description("Add acceptance criterion to an issue").argum
6872
6848
  console.log(source_default.green(`\u2713 Added criterion to ${source_default.bold(id)}: ${description}${cmdNote}`));
6873
6849
  });
6874
6850
  issueCmd.command("ac-pass").description("Manually mark an acceptance criterion as passed").argument("<id>", "Issue ID").argument("<index>", "Criterion number (1-based)").option("-p, --path <path>", "Repository path", ".").action(async (id, index, opts) => {
6875
- const rootPath = resolve(opts.path);
6876
- requireRepo(rootPath);
6851
+ const rootPath = resolveRepoRoot(opts.path);
6877
6852
  const engine = new TrellisVcsEngine({ rootPath });
6878
6853
  engine.open();
6879
6854
  await engine.setCriterionStatus(id, parseInt(index, 10), "passed");
6880
6855
  console.log(source_default.green(`\u2713 Criterion #${index} on ${source_default.bold(id)} marked as passed`));
6881
6856
  });
6882
6857
  issueCmd.command("ac-fail").description("Manually mark an acceptance criterion as failed").argument("<id>", "Issue ID").argument("<index>", "Criterion number (1-based)").option("-p, --path <path>", "Repository path", ".").action(async (id, index, opts) => {
6883
- const rootPath = resolve(opts.path);
6884
- requireRepo(rootPath);
6858
+ const rootPath = resolveRepoRoot(opts.path);
6885
6859
  const engine = new TrellisVcsEngine({ rootPath });
6886
6860
  engine.open();
6887
6861
  await engine.setCriterionStatus(id, parseInt(index, 10), "failed");
6888
6862
  console.log(source_default.red(`\u2717 Criterion #${index} on ${source_default.bold(id)} marked as failed`));
6889
6863
  });
6890
6864
  issueCmd.command("check").description("Run acceptance criteria for an issue").argument("<id>", "Issue ID").option("-p, --path <path>", "Repository path", ".").action(async (id, opts) => {
6891
- const rootPath = resolve(opts.path);
6892
- requireRepo(rootPath);
6865
+ const rootPath = resolveRepoRoot(opts.path);
6893
6866
  const engine = new TrellisVcsEngine({ rootPath });
6894
6867
  engine.open();
6895
6868
  console.log(source_default.bold(`Running criteria for ${id}...
@@ -6924,8 +6897,7 @@ issueCmd.command("check").description("Run acceptance criteria for an issue").ar
6924
6897
  }
6925
6898
  });
6926
6899
  issueCmd.command("close").description("Close an issue (requires all criteria pass + --confirm)").argument("<id>", "Issue ID").option("--confirm", "Confirm closure after criteria pass").option("-p, --path <path>", "Repository path", ".").action(async (id, opts) => {
6927
- const rootPath = resolve(opts.path);
6928
- requireRepo(rootPath);
6900
+ const rootPath = resolveRepoRoot(opts.path);
6929
6901
  const engine = new TrellisVcsEngine({ rootPath });
6930
6902
  engine.open();
6931
6903
  try {
@@ -6947,32 +6919,28 @@ issueCmd.command("close").description("Close an issue (requires all criteria pas
6947
6919
  }
6948
6920
  });
6949
6921
  issueCmd.command("reopen").description("Reopen a closed issue").argument("<id>", "Issue ID").option("-p, --path <path>", "Repository path", ".").action(async (id, opts) => {
6950
- const rootPath = resolve(opts.path);
6951
- requireRepo(rootPath);
6922
+ const rootPath = resolveRepoRoot(opts.path);
6952
6923
  const engine = new TrellisVcsEngine({ rootPath });
6953
6924
  engine.open();
6954
6925
  await engine.reopenIssue(id);
6955
6926
  console.log(source_default.green(`\u2713 Issue ${source_default.bold(id)} reopened`));
6956
6927
  });
6957
6928
  issueCmd.command("block").description("Mark an issue as blocked by another issue").argument("<id>", "Issue ID to block").argument("<blockedBy>", "Issue ID that blocks it").option("-p, --path <path>", "Repository path", ".").action(async (id, blockedBy, opts) => {
6958
- const rootPath = resolve(opts.path);
6959
- requireRepo(rootPath);
6929
+ const rootPath = resolveRepoRoot(opts.path);
6960
6930
  const engine = new TrellisVcsEngine({ rootPath });
6961
6931
  engine.open();
6962
6932
  await engine.blockIssue(id, blockedBy);
6963
6933
  console.log(source_default.yellow(`\uD83D\uDD12 ${source_default.bold(id)} is now blocked by ${source_default.bold(blockedBy)}`));
6964
6934
  });
6965
6935
  issueCmd.command("unblock").description("Remove a blocking relationship").argument("<id>", "Blocked issue ID").argument("<blockedBy>", "Blocking issue ID to remove").option("-p, --path <path>", "Repository path", ".").action(async (id, blockedBy, opts) => {
6966
- const rootPath = resolve(opts.path);
6967
- requireRepo(rootPath);
6936
+ const rootPath = resolveRepoRoot(opts.path);
6968
6937
  const engine = new TrellisVcsEngine({ rootPath });
6969
6938
  engine.open();
6970
6939
  await engine.unblockIssue(id, blockedBy);
6971
6940
  console.log(source_default.green(`\uD83D\uDD13 ${source_default.bold(id)} is no longer blocked by ${source_default.bold(blockedBy)}`));
6972
6941
  });
6973
6942
  issueCmd.command("active").description("Show all active (in-progress) issues").option("-p, --path <path>", "Repository path", ".").action((opts) => {
6974
- const rootPath = resolve(opts.path);
6975
- requireRepo(rootPath);
6943
+ const rootPath = resolveRepoRoot(opts.path);
6976
6944
  const engine = new TrellisVcsEngine({ rootPath });
6977
6945
  engine.open();
6978
6946
  const active = engine.getActiveIssues();
@@ -6989,8 +6957,7 @@ issueCmd.command("active").description("Show all active (in-progress) issues").o
6989
6957
  }
6990
6958
  });
6991
6959
  issueCmd.command("readiness").description("Check if all issues are complete (no queue, paused, or in-progress)").option("-p, --path <path>", "Repository path", ".").action((opts) => {
6992
- const rootPath = resolve(opts.path);
6993
- requireRepo(rootPath);
6960
+ const rootPath = resolveRepoRoot(opts.path);
6994
6961
  const engine = new TrellisVcsEngine({ rootPath });
6995
6962
  engine.open();
6996
6963
  const result = engine.checkCompletionReadiness();
@@ -7001,8 +6968,7 @@ issueCmd.command("readiness").description("Check if all issues are complete (no
7001
6968
  });
7002
6969
  var decisionCmd = program2.command("decision").description("Manage decision traces");
7003
6970
  decisionCmd.command("list").description("List decision traces").option("-p, --path <path>", "Repository path", ".").option("-t, --tool <pattern>", 'Filter by tool name pattern (e.g. "trellis_issue_*")').option("-e, --entity <id>", "Filter by related entity ID").option("-n, --limit <n>", "Max results", "20").action((opts) => {
7004
- const rootPath = resolve(opts.path);
7005
- requireRepo(rootPath);
6971
+ const rootPath = resolveRepoRoot(opts.path);
7006
6972
  const engine = new TrellisVcsEngine({ rootPath });
7007
6973
  engine.open();
7008
6974
  const decisions = engine.queryDecisions({
@@ -7023,8 +6989,7 @@ decisionCmd.command("list").description("List decision traces").option("-p, --pa
7023
6989
  }
7024
6990
  });
7025
6991
  decisionCmd.command("show").description("Show full details of a decision trace").argument("<id>", "Decision ID (e.g. DEC-1)").option("-p, --path <path>", "Repository path", ".").action((id, opts) => {
7026
- const rootPath = resolve(opts.path);
7027
- requireRepo(rootPath);
6992
+ const rootPath = resolveRepoRoot(opts.path);
7028
6993
  const engine = new TrellisVcsEngine({ rootPath });
7029
6994
  engine.open();
7030
6995
  const d = engine.getDecision(id);
@@ -7051,8 +7016,7 @@ decisionCmd.command("show").description("Show full details of a decision trace")
7051
7016
  }
7052
7017
  });
7053
7018
  decisionCmd.command("chain").description("Trace all decisions that affected a given entity").argument("<entityId>", 'Entity ID (e.g. "issue:TRL-5", "file:src/engine.ts")').option("-p, --path <path>", "Repository path", ".").action((entityId, opts) => {
7054
- const rootPath = resolve(opts.path);
7055
- requireRepo(rootPath);
7019
+ const rootPath = resolveRepoRoot(opts.path);
7056
7020
  const engine = new TrellisVcsEngine({ rootPath });
7057
7021
  engine.open();
7058
7022
  const chain = engine.getDecisionChain(entityId);
@@ -7070,7 +7034,7 @@ decisionCmd.command("chain").description("Trace all decisions that affected a gi
7070
7034
  }
7071
7035
  });
7072
7036
  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) => {
7073
- const rootPath = resolve(opts.path);
7037
+ const rootPath = resolve2(opts.path);
7074
7038
  const trellisDir = join6(rootPath, ".trellis");
7075
7039
  if (action === "init") {
7076
7040
  if (hasIdentity(trellisDir)) {
@@ -7108,14 +7072,10 @@ program2.command("identity").description("Manage local identity (Ed25519 key pai
7108
7072
  console.log(` ${source_default.dim("Created:")} ${pub.createdAt}`);
7109
7073
  });
7110
7074
  program2.command("refs").description("List wiki-link references in files or find backlinks").argument("[file]", "File to list outgoing refs for").option("-p, --path <path>", "Repository path", ".").option("--backlinks <entity>", "Show all files referencing an entity (e.g. TRL-5)").option("--broken", "List all broken and stale references").option("--stats", "Show reference index statistics").action((file, opts) => {
7111
- const rootPath = resolve(opts.path);
7112
- if (!TrellisVcsEngine.isRepo(rootPath)) {
7113
- console.error(source_default.red("Not a TrellisVCS repository. Run `trellis init` first."));
7114
- process.exit(1);
7115
- }
7075
+ const rootPath = resolveRepoRoot(opts.path);
7116
7076
  const engine = new TrellisVcsEngine({ rootPath });
7117
7077
  engine.open();
7118
- const { readFileSync: readFileSync4 } = __require("fs");
7078
+ const { readFileSync: readFileSync5 } = __require("fs");
7119
7079
  const {
7120
7080
  parseFileRefs,
7121
7081
  buildRefIndex: buildRefIndex2,
@@ -7135,7 +7095,7 @@ program2.command("refs").description("List wiki-link references in files or find
7135
7095
  for (const f of trackedFiles) {
7136
7096
  try {
7137
7097
  const absPath = join6(rootPath, f.path);
7138
- const content = readFileSync4(absPath, "utf-8");
7098
+ const content = readFileSync5(absPath, "utf-8");
7139
7099
  fileContents.push({ path: f.path, content });
7140
7100
  } catch {}
7141
7101
  }
@@ -7247,11 +7207,7 @@ program2.command("refs").description("List wiki-link references in files or find
7247
7207
  }
7248
7208
  });
7249
7209
  program2.command("search").description("Semantic search across all embedded content").argument("<query>", "Natural language search query").option("-p, --path <path>", "Repository path", ".").option("-l, --limit <n>", "Max results", "10").option("-t, --type <types>", "Filter by chunk type(s), comma-separated (issue_title,issue_desc,milestone_msg,markdown,code_entity,doc_comment,summary_md)").action(async (query, opts) => {
7250
- const rootPath = resolve(opts.path);
7251
- if (!TrellisVcsEngine.isRepo(rootPath)) {
7252
- console.error(source_default.red("Not a TrellisVCS repository. Run `trellis init` first."));
7253
- process.exit(1);
7254
- }
7210
+ const rootPath = resolveRepoRoot(opts.path);
7255
7211
  const engine = new TrellisVcsEngine({ rootPath });
7256
7212
  engine.open();
7257
7213
  const { EmbeddingManager } = (init_embeddings(), __toCommonJS(exports_embeddings));
@@ -7285,11 +7241,7 @@ program2.command("search").description("Semantic search across all embedded cont
7285
7241
  }
7286
7242
  });
7287
7243
  program2.command("reindex").description("Rebuild the semantic embedding index").option("-p, --path <path>", "Repository path", ".").action(async (opts) => {
7288
- const rootPath = resolve(opts.path);
7289
- if (!TrellisVcsEngine.isRepo(rootPath)) {
7290
- console.error(source_default.red("Not a TrellisVCS repository. Run `trellis init` first."));
7291
- process.exit(1);
7292
- }
7244
+ const rootPath = resolveRepoRoot(opts.path);
7293
7245
  const engine = new TrellisVcsEngine({ rootPath });
7294
7246
  engine.open();
7295
7247
  const { EmbeddingManager } = (init_embeddings(), __toCommonJS(exports_embeddings));
@@ -7306,11 +7258,7 @@ program2.command("reindex").description("Rebuild the semantic embedding index").
7306
7258
  }
7307
7259
  });
7308
7260
  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) => {
7309
- const rootPath = resolve(opts.path);
7310
- if (!TrellisVcsEngine.isRepo(rootPath)) {
7311
- console.error(source_default.red("Not a TrellisVCS repository. Run `trellis init` first."));
7312
- process.exit(1);
7313
- }
7261
+ const rootPath = resolveRepoRoot(opts.path);
7314
7262
  const { startUIServer: startUIServer2 } = (init_server(), __toCommonJS(exports_server));
7315
7263
  const port = parseInt(opts.port, 10) || 3000;
7316
7264
  try {
@@ -7348,8 +7296,7 @@ async function bootKernel(rootPath) {
7348
7296
  }
7349
7297
  var entityCmd = program2.command("entity").description("Manage graph entities (generic CRUD)");
7350
7298
  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) => {
7351
- const rootPath = resolve(opts.path);
7352
- requireRepo(rootPath);
7299
+ const rootPath = resolveRepoRoot(opts.path);
7353
7300
  const kernel = await bootKernel(rootPath);
7354
7301
  try {
7355
7302
  const attrs = {};
@@ -7379,8 +7326,7 @@ entityCmd.command("create").description("Create a new entity in the graph").requ
7379
7326
  }
7380
7327
  });
7381
7328
  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(async (id, opts) => {
7382
- const rootPath = resolve(opts.path);
7383
- requireRepo(rootPath);
7329
+ const rootPath = resolveRepoRoot(opts.path);
7384
7330
  const kernel = await bootKernel(rootPath);
7385
7331
  try {
7386
7332
  const entity = kernel.getEntity(id);
@@ -7421,8 +7367,7 @@ entityCmd.command("get").description("Get an entity by ID").argument("<id>", "En
7421
7367
  }
7422
7368
  });
7423
7369
  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) => {
7424
- const rootPath = resolve(opts.path);
7425
- requireRepo(rootPath);
7370
+ const rootPath = resolveRepoRoot(opts.path);
7426
7371
  const kernel = await bootKernel(rootPath);
7427
7372
  try {
7428
7373
  const updates = {};
@@ -7450,8 +7395,7 @@ entityCmd.command("update").description("Update attributes on an existing entity
7450
7395
  }
7451
7396
  });
7452
7397
  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) => {
7453
- const rootPath = resolve(opts.path);
7454
- requireRepo(rootPath);
7398
+ const rootPath = resolveRepoRoot(opts.path);
7455
7399
  const kernel = await bootKernel(rootPath);
7456
7400
  try {
7457
7401
  const entity = kernel.getEntity(id);
@@ -7466,8 +7410,7 @@ entityCmd.command("delete").description("Delete an entity and all its facts/link
7466
7410
  }
7467
7411
  });
7468
7412
  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(async (opts) => {
7469
- const rootPath = resolve(opts.path);
7470
- requireRepo(rootPath);
7413
+ const rootPath = resolveRepoRoot(opts.path);
7471
7414
  const kernel = await bootKernel(rootPath);
7472
7415
  try {
7473
7416
  let filters;
@@ -7519,8 +7462,7 @@ entityCmd.command("list").description("List entities, optionally filtered by typ
7519
7462
  });
7520
7463
  var factCmd = program2.command("fact").description("Add or remove individual facts on entities");
7521
7464
  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) => {
7522
- const rootPath = resolve(opts.path);
7523
- requireRepo(rootPath);
7465
+ const rootPath = resolveRepoRoot(opts.path);
7524
7466
  const kernel = await bootKernel(rootPath);
7525
7467
  try {
7526
7468
  let val = value;
@@ -7537,8 +7479,7 @@ factCmd.command("add").description("Add a fact to an entity").argument("<entity>
7537
7479
  }
7538
7480
  });
7539
7481
  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) => {
7540
- const rootPath = resolve(opts.path);
7541
- requireRepo(rootPath);
7482
+ const rootPath = resolveRepoRoot(opts.path);
7542
7483
  const kernel = await bootKernel(rootPath);
7543
7484
  try {
7544
7485
  let val = value;
@@ -7555,8 +7496,7 @@ factCmd.command("remove").description("Remove a fact from an entity").argument("
7555
7496
  }
7556
7497
  });
7557
7498
  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(async (opts) => {
7558
- const rootPath = resolve(opts.path);
7559
- requireRepo(rootPath);
7499
+ const rootPath = resolveRepoRoot(opts.path);
7560
7500
  const kernel = await bootKernel(rootPath);
7561
7501
  try {
7562
7502
  const store = kernel.getStore();
@@ -7590,8 +7530,7 @@ factCmd.command("query").description("Query facts by entity or attribute").optio
7590
7530
  });
7591
7531
  var linkCmd = program2.command("link").description("Add or remove links between entities");
7592
7532
  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) => {
7593
- const rootPath = resolve(opts.path);
7594
- requireRepo(rootPath);
7533
+ const rootPath = resolveRepoRoot(opts.path);
7595
7534
  const kernel = await bootKernel(rootPath);
7596
7535
  try {
7597
7536
  await kernel.addLink(source, attribute, target);
@@ -7601,8 +7540,7 @@ linkCmd.command("add").description("Add a link between two entities").argument("
7601
7540
  }
7602
7541
  });
7603
7542
  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) => {
7604
- const rootPath = resolve(opts.path);
7605
- requireRepo(rootPath);
7543
+ const rootPath = resolveRepoRoot(opts.path);
7606
7544
  const kernel = await bootKernel(rootPath);
7607
7545
  try {
7608
7546
  await kernel.removeLink(source, attribute, target);
@@ -7612,8 +7550,7 @@ linkCmd.command("remove").description("Remove a link between two entities").argu
7612
7550
  }
7613
7551
  });
7614
7552
  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(async (opts) => {
7615
- const rootPath = resolve(opts.path);
7616
- requireRepo(rootPath);
7553
+ const rootPath = resolveRepoRoot(opts.path);
7617
7554
  const kernel = await bootKernel(rootPath);
7618
7555
  try {
7619
7556
  const store = kernel.getStore();
@@ -7648,8 +7585,7 @@ linkCmd.command("query").description("Query links for an entity").option("-e, --
7648
7585
  }
7649
7586
  });
7650
7587
  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(async (queryStr, opts) => {
7651
- const rootPath = resolve(opts.path);
7652
- requireRepo(rootPath);
7588
+ const rootPath = resolveRepoRoot(opts.path);
7653
7589
  const kernel = await bootKernel(rootPath);
7654
7590
  try {
7655
7591
  const store = kernel.getStore();
@@ -7688,8 +7624,7 @@ ${result.count} result(s) in ${result.executionTime.toFixed(1)}ms`));
7688
7624
  }
7689
7625
  });
7690
7626
  program2.command("repl").description("Interactive EQL-S query shell").option("-p, --path <path>", "Repository path", ".").action(async (opts) => {
7691
- const rootPath = resolve(opts.path);
7692
- requireRepo(rootPath);
7627
+ const rootPath = resolveRepoRoot(opts.path);
7693
7628
  const kernel = await bootKernel(rootPath);
7694
7629
  const store = kernel.getStore();
7695
7630
  const engine = new QueryEngine(store);
@@ -7867,8 +7802,7 @@ Relations:`));
7867
7802
  console.log(source_default.dim("Available types: " + registry.listEntityTypes().join(", ")));
7868
7803
  });
7869
7804
  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(async (opts) => {
7870
- const rootPath = resolve(opts.path);
7871
- requireRepo(rootPath);
7805
+ const rootPath = resolveRepoRoot(opts.path);
7872
7806
  const kernel = await bootKernel(rootPath);
7873
7807
  try {
7874
7808
  const registry = new OntologyRegistry;
@@ -7905,8 +7839,7 @@ ontologyCmd.command("validate").description("Validate all entities in the graph
7905
7839
  }
7906
7840
  });
7907
7841
  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) => {
7908
- const rootPath = resolve(opts.path);
7909
- requireRepo(rootPath);
7842
+ const rootPath = resolveRepoRoot(opts.path);
7910
7843
  const dbPath = join6(rootPath, ".trellis", "embeddings.db");
7911
7844
  const vectorStore = new VectorStore(dbPath);
7912
7845
  try {
@@ -7995,12 +7928,12 @@ function formatRelativeTime(iso) {
7995
7928
  var db = program2.command("db").description("Trellis DB \u2014 use Trellis as an application database");
7996
7929
  db.command("init").description("Initialize a new Trellis DB in the current directory").option("-p, --path <path>", "Database directory", ".trellis-db").option("--port <port>", "Server port", "3000").option("--key <key>", "API key (auto-generated if omitted)").option("--jwt-secret <secret>", "JWT secret (auto-generated if omitted)").option("--multi-tenant", "Enable multi-tenancy (separate SQLite per tenant)").action(async (opts) => {
7997
7930
  const { writeConfig, defaultLocalConfig, hasConfig, configPath } = await import("../config-8hczw0rs.js");
7998
- const { resolve: resolve2 } = await import("path");
7931
+ const { resolve: resolve3 } = await import("path");
7999
7932
  if (hasConfig(".")) {
8000
7933
  console.log(source_default.yellow(`Already initialized (${configPath(".")}). Use \`trellis db serve\` to start.`));
8001
7934
  return;
8002
7935
  }
8003
- const dbPath = resolve2(opts.path);
7936
+ const dbPath = resolve3(opts.path);
8004
7937
  const apiKey = opts.key ?? `spk_${crypto.randomUUID().replace(/-/g, "")}`;
8005
7938
  const jwtSecret = opts.jwtSecret ?? `jws_${crypto.randomUUID().replace(/-/g, "")}`;
8006
7939
  writeConfig(defaultLocalConfig(dbPath, {
@@ -8048,8 +7981,8 @@ db.command("create <type> [json]").description("Create an entity (json can be a
8048
7981
  const db2 = TrellisDb.fromConfig(opts.configDir);
8049
7982
  let attributes = {};
8050
7983
  if (jsonArg) {
8051
- const { readFileSync: readFileSync4 } = await import("fs");
8052
- const raw = jsonArg.startsWith("@") ? readFileSync4(jsonArg.slice(1), "utf8") : jsonArg;
7984
+ const { readFileSync: readFileSync5 } = await import("fs");
7985
+ const raw = jsonArg.startsWith("@") ? readFileSync5(jsonArg.slice(1), "utf8") : jsonArg;
8053
7986
  attributes = JSON.parse(raw);
8054
7987
  }
8055
7988
  const id = await db2.create(type, attributes);
@@ -8070,8 +8003,8 @@ db.command("read <id>").description("Read an entity by ID").option("--config-dir
8070
8003
  db.command("update <id> <json>").description("Update entity attributes (JSON string or @file.json)").option("--config-dir <dir>", "Config directory", ".").action(async (id, jsonArg, opts) => {
8071
8004
  const { TrellisDb } = await import("../sdk-bepky0xs.js");
8072
8005
  const db2 = TrellisDb.fromConfig(opts.configDir);
8073
- const { readFileSync: readFileSync4 } = await import("fs");
8074
- const raw = jsonArg.startsWith("@") ? readFileSync4(jsonArg.slice(1), "utf8") : jsonArg;
8006
+ const { readFileSync: readFileSync5 } = await import("fs");
8007
+ const raw = jsonArg.startsWith("@") ? readFileSync5(jsonArg.slice(1), "utf8") : jsonArg;
8075
8008
  await db2.update(id, JSON.parse(raw));
8076
8009
  console.log(source_default.green(`\u2713 Updated: ${source_default.bold(id)}`));
8077
8010
  db2.close();
@@ -8102,7 +8035,7 @@ db.command("query <eql>").description("Run an EQL-S query").option("--config-dir
8102
8035
  db2.close();
8103
8036
  });
8104
8037
  db.command("upload <file>").description("Upload a file to the blob store").option("--config-dir <dir>", "Config directory", ".").option("--type <mime>", "MIME type (auto-detected if omitted)").action(async (filePath, opts) => {
8105
- const { readFileSync: readFileSync4 } = await import("fs");
8038
+ const { readFileSync: readFileSync5 } = await import("fs");
8106
8039
  const { extname } = await import("path");
8107
8040
  const { TrellisDb } = await import("../sdk-bepky0xs.js");
8108
8041
  const mimeMap = {
@@ -8115,7 +8048,7 @@ db.command("upload <file>").description("Upload a file to the blob store").optio
8115
8048
  ".txt": "text/plain"
8116
8049
  };
8117
8050
  const contentType = opts.type ?? mimeMap[extname(filePath).toLowerCase()] ?? "application/octet-stream";
8118
- const buffer = readFileSync4(filePath);
8051
+ const buffer = readFileSync5(filePath);
8119
8052
  const db2 = TrellisDb.fromConfig(opts.configDir);
8120
8053
  const result = await db2.upload(new Uint8Array(buffer), contentType);
8121
8054
  console.log(source_default.green(`\u2713 Uploaded`));
@@ -8266,10 +8199,10 @@ vmProgram.command("destroy <name>").description("Destroy a Sprite").action(async
8266
8199
  input: process.stdin,
8267
8200
  output: process.stdout
8268
8201
  });
8269
- const answer = await new Promise((resolve2) => {
8202
+ const answer = await new Promise((resolve3) => {
8270
8203
  rl.question(`Destroy Sprite "${name}"? Cannot be undone. (y/N) `, (a) => {
8271
8204
  rl.close();
8272
- resolve2(a.trim().toLowerCase());
8205
+ resolve3(a.trim().toLowerCase());
8273
8206
  });
8274
8207
  });
8275
8208
  if (answer !== "y" && answer !== "yes") {
@@ -8532,8 +8465,7 @@ vmProgram.command("code [name]").description("Create Sprite, deploy Trellis, ope
8532
8465
  });
8533
8466
  program2.addCommand(vmProgram);
8534
8467
  program2.command("season").description("Enrich project context for agents \u2014 interactive Q&A").option("-p, --path <path>", "Repository path", ".").option("--reset", "Re-run even if previously configured").action(async (opts) => {
8535
- const rootPath = resolve(opts.path);
8536
- requireRepo(rootPath);
8468
+ const rootPath = resolveRepoRoot(opts.path);
8537
8469
  const { createInterface: createInterface2 } = await import("readline");
8538
8470
  const rl = createInterface2({
8539
8471
  input: process.stdin,
@@ -8561,8 +8493,8 @@ program2.command("season").description("Enrich project context for agents \u2014
8561
8493
  writeAgentScaffold(rootPath, { profile, context: updatedContext });
8562
8494
  const agentContextPath = join6(rootPath, ".trellis", "agents", "agent-context.json");
8563
8495
  try {
8564
- const { readFileSync: readFileSync4, writeFileSync: writeFileSync3 } = await import("fs");
8565
- const existing = JSON.parse(readFileSync4(agentContextPath, "utf-8"));
8496
+ const { readFileSync: readFileSync5, writeFileSync: writeFileSync3 } = await import("fs");
8497
+ const existing = JSON.parse(readFileSync5(agentContextPath, "utf-8"));
8566
8498
  existing.domain = updatedContext.domain;
8567
8499
  if (toolsRaw) {
8568
8500
  existing.tools = toolsRaw.split(",").map((s) => s.trim()).filter(Boolean);
@@ -8584,12 +8516,12 @@ program2.command("season").description("Enrich project context for agents \u2014
8584
8516
  console.log();
8585
8517
  });
8586
8518
  program2.command("code").alias("ide").description('Launch OpenCode in "Harness" mode, bridged to this Trellis repository').option("-p, --path <path>", "Repository path", ".").option("-m, --model <model>", "OpenCode model to use").option("-w, --web", "Launch MCP server in HTTP mode for web client access").option("--mcp-port <port>", "MCP HTTP server port (default: 3333)", "3333").option("--no-init", "Skip initialization even if not a Trellis workspace").action(async (opts) => {
8587
- const { resolve: resolve2, dirname: dirname6, join: join7 } = await import("path");
8519
+ const { resolve: resolve3, dirname: dirname7, join: join7 } = await import("path");
8588
8520
  const { existsSync: existsSync6 } = await import("fs");
8589
8521
  const { spawn } = await import("child_process");
8590
- const { readFileSync: readFileSync4 } = await import("fs");
8522
+ const { readFileSync: readFileSync5 } = await import("fs");
8591
8523
  const { createServer: createHttpServer } = await import("http");
8592
- const rootPath = resolve2(opts.path);
8524
+ const rootPath = findRepoRoot(opts.path) ?? resolve3(opts.path);
8593
8525
  if (!opts.noInit) {
8594
8526
  if (!TrellisVcsEngine.isRepo(rootPath)) {
8595
8527
  console.log(source_default.dim("Not a Trellis workspace \u2014 initializing\u2026"));
@@ -8600,17 +8532,17 @@ program2.command("code").alias("ide").description('Launch OpenCode in "Harness"
8600
8532
  }
8601
8533
  }
8602
8534
  function findOpencode() {
8603
- const here = dirname6(process.argv[1]);
8535
+ const here2 = dirname7(process.argv[1]);
8604
8536
  const candidates = [
8605
- join7(here, "..", "node_modules", ".bin", "opencode"),
8606
- join7(here, "..", "..", "node_modules", ".bin", "opencode")
8537
+ join7(here2, "..", "node_modules", ".bin", "opencode"),
8538
+ join7(here2, "..", "..", "node_modules", ".bin", "opencode")
8607
8539
  ];
8608
- let dir = here;
8540
+ let dir = here2;
8609
8541
  for (let i = 0;i < 5; i++) {
8610
8542
  const p = join7(dir, "node_modules", ".bin", "opencode");
8611
8543
  if (existsSync6(p))
8612
8544
  return p;
8613
- dir = dirname6(dir);
8545
+ dir = dirname7(dir);
8614
8546
  }
8615
8547
  for (const p of candidates) {
8616
8548
  if (existsSync6(p))
@@ -8619,14 +8551,14 @@ program2.command("code").alias("ide").description('Launch OpenCode in "Harness"
8619
8551
  return null;
8620
8552
  }
8621
8553
  function findMcpServer() {
8622
- const here = dirname6(process.argv[1]);
8554
+ const here2 = dirname7(process.argv[1]);
8623
8555
  const candidates = [
8624
- join7(here, "..", "..", "src", "mcp", "index.ts"),
8625
- join7(here, "..", "src", "mcp", "index.ts"),
8626
- join7(here, "..", "..", "..", "src", "mcp", "index.ts"),
8627
- join7(here, "..", "mcp", "index.js"),
8628
- join7(here, "..", "mcp", "index.ts"),
8629
- join7(here, "mcp", "index.ts")
8556
+ join7(here2, "..", "..", "src", "mcp", "index.ts"),
8557
+ join7(here2, "..", "src", "mcp", "index.ts"),
8558
+ join7(here2, "..", "..", "..", "src", "mcp", "index.ts"),
8559
+ join7(here2, "..", "mcp", "index.js"),
8560
+ join7(here2, "..", "mcp", "index.ts"),
8561
+ join7(here2, "mcp", "index.ts")
8630
8562
  ];
8631
8563
  for (const p of candidates) {
8632
8564
  if (existsSync6(p))
@@ -8723,12 +8655,12 @@ program2.command("code").alias("ide").description('Launch OpenCode in "Harness"
8723
8655
  });
8724
8656
  });
8725
8657
  program2.command("studio").alias("web").description("Launch Trellis Studio in your browser, bridged to this repo").option("-p, --path <path>", "Repository path", ".").option("--port <port>", "Studio HTTP port (defaults to turtlecode default)").option("--new", "Open the new-project prompt instead of the current directory").option("--no-open", "Do not auto-open the browser").option("--no-init", "Skip initialization even if not a Trellis workspace").option("--quiet-backend", "Suppress backend stdout/stderr").allowUnknownOption(true).action(async (opts, command) => {
8726
- const { resolve: resolve2 } = await import("path");
8727
- const { existsSync: existsSync6, readFileSync: readFileSync4 } = await import("fs");
8658
+ const { resolve: resolve3 } = await import("path");
8659
+ const { existsSync: existsSync6, readFileSync: readFileSync5 } = await import("fs");
8728
8660
  const { spawn } = await import("child_process");
8729
8661
  const { createRequire } = await import("module");
8730
8662
  const path = await import("path");
8731
- const rootPath = resolve2(opts.path);
8663
+ const rootPath = findRepoRoot(opts.path) ?? resolve3(opts.path);
8732
8664
  if (!opts.noInit) {
8733
8665
  if (!TrellisVcsEngine.isRepo(rootPath)) {
8734
8666
  console.log(source_default.dim("Not a Trellis workspace \u2014 initializing\u2026"));
@@ -8740,7 +8672,7 @@ program2.command("studio").alias("web").description("Launch Trellis Studio in yo
8740
8672
  let turtlecodeBin = null;
8741
8673
  try {
8742
8674
  const pkgPath = requireFn.resolve("turtlecode/package.json");
8743
- const pkg = JSON.parse(readFileSync4(pkgPath, "utf-8"));
8675
+ const pkg = JSON.parse(readFileSync5(pkgPath, "utf-8"));
8744
8676
  const binRel = typeof pkg.bin === "string" ? pkg.bin : pkg.bin?.turtlecode;
8745
8677
  if (binRel) {
8746
8678
  const candidate = path.join(path.dirname(pkgPath), binRel);
@@ -8792,10 +8724,10 @@ async function waitForMcpReady(url, timeoutMs) {
8792
8724
  const { get } = await import("http");
8793
8725
  const start = Date.now();
8794
8726
  while (Date.now() - start < timeoutMs) {
8795
- await new Promise((resolve2) => setTimeout(resolve2, 500));
8727
+ await new Promise((resolve3) => setTimeout(resolve3, 500));
8796
8728
  try {
8797
- const res = await new Promise((resolve2, reject) => {
8798
- const req = get(`${url}/health`, resolve2);
8729
+ const res = await new Promise((resolve3, reject) => {
8730
+ const req = get(`${url}/health`, resolve3);
8799
8731
  req.on("error", reject);
8800
8732
  req.setTimeout(2000);
8801
8733
  });
@@ -8806,4 +8738,76 @@ async function waitForMcpReady(url, timeoutMs) {
8806
8738
  }
8807
8739
  return false;
8808
8740
  }
8741
+ var cmsCmd = program2.command("cms").description("Manage CMS entities (collections, libraries, schemas)");
8742
+ cmsCmd.command("register-library <pkg>").description("Register a Svelte 5 component library as DesignComponent entities. The package must ship dist/components.json (see @turtle.tech/ui for the format).").option("-p, --path <path>", "Repository path", ".").option("--url <url>", "Trellis server URL", "http://localhost:4096").option("--dry-run", "Print the facts that would be asserted without contacting the server").action(async (pkg, opts) => {
8743
+ const rootPath = resolveRepoRoot(opts.path);
8744
+ const { readFileSync: readFileSync5, existsSync: existsSync6 } = await import("fs");
8745
+ const manifestPath = join6(rootPath, "node_modules", pkg, "dist", "components.json");
8746
+ if (!existsSync6(manifestPath)) {
8747
+ console.error(source_default.red(`\u2717 No manifest at ${manifestPath}`));
8748
+ console.error(source_default.dim(` Install ${pkg} in this repo and ensure it ships dist/components.json.`));
8749
+ process.exit(1);
8750
+ }
8751
+ let manifest;
8752
+ try {
8753
+ manifest = JSON.parse(readFileSync5(manifestPath, "utf8"));
8754
+ } catch (err) {
8755
+ console.error(source_default.red(`\u2717 Manifest is not valid JSON: ${manifestPath}`));
8756
+ console.error(source_default.dim(` ${err?.message ?? String(err)}`));
8757
+ process.exit(1);
8758
+ }
8759
+ const { package: pkgName, version, components } = manifest;
8760
+ if (!Array.isArray(components) || components.length === 0) {
8761
+ console.error(source_default.yellow(`\u26A0 Manifest is empty: ${manifestPath}`));
8762
+ process.exit(1);
8763
+ }
8764
+ const pkgSlug = pkgName.replace(/^@/, "").replace(/\//g, "__");
8765
+ const now = new Date().toISOString();
8766
+ const facts = [];
8767
+ for (const comp of components) {
8768
+ const id = `design:component:${pkgSlug}:${comp.slug}`;
8769
+ facts.push({ e: id, a: "type", v: "DesignComponent" }, { e: id, a: "label", v: comp.component }, { e: id, a: "package", v: pkgName }, { e: id, a: "packageVersion", v: version }, { e: id, a: "slug", v: comp.slug }, { e: id, a: "source", v: comp.source ?? "" }, { e: id, a: "editable", v: JSON.stringify(comp.editable ?? []) }, { e: id, a: "createdAt", v: now });
8770
+ }
8771
+ if (opts.dryRun) {
8772
+ console.log(source_default.dim("\u2500\u2500 Dry run: would assert these facts \u2500\u2500"));
8773
+ console.log(JSON.stringify(facts, null, 2));
8774
+ console.log(source_default.dim(`\u2500\u2500 ${facts.length} facts across ${components.length} components \u2500\u2500`));
8775
+ return;
8776
+ }
8777
+ const url = new URL("/trellis/store/assert", opts.url);
8778
+ url.searchParams.set("directory", rootPath);
8779
+ const meta = {
8780
+ actor: "cli:trellis",
8781
+ actorKind: "user",
8782
+ source: "trellis-cms-register-library",
8783
+ relatedEntities: [...new Set(facts.map((f) => f.e))]
8784
+ };
8785
+ try {
8786
+ const res = await fetch(url.toString(), {
8787
+ method: "POST",
8788
+ headers: { "Content-Type": "application/json" },
8789
+ body: JSON.stringify({ facts, meta })
8790
+ });
8791
+ if (!res.ok) {
8792
+ const body2 = await res.text().catch(() => "");
8793
+ console.error(source_default.red(`\u2717 Assert failed: HTTP ${res.status}`));
8794
+ if (body2)
8795
+ console.error(source_default.dim(` ${body2.slice(0, 400)}`));
8796
+ process.exit(1);
8797
+ }
8798
+ const body = await res.json().catch(() => ({ added: 0 }));
8799
+ console.log(source_default.green(`\u2713 Registered ${source_default.bold(String(components.length))} components from ${source_default.bold(pkgName)}@${version}`));
8800
+ console.log(` ${source_default.dim("Facts added:")} ${body.added ?? "?"}`);
8801
+ console.log(` ${source_default.dim("Workspace:")} ${rootPath}`);
8802
+ for (const comp of components) {
8803
+ console.log(` ${source_default.dim("\u2022")} ${comp.component} ${source_default.dim(`(design:component:${pkgSlug}:${comp.slug})`)}`);
8804
+ }
8805
+ } catch (err) {
8806
+ const msg = err?.message ?? String(err);
8807
+ console.error(source_default.red(`\u2717 Could not reach Trellis server at ${opts.url}`));
8808
+ console.error(source_default.dim(` ${msg}`));
8809
+ console.error(source_default.dim(` Hint: start the Trellis backend (e.g. open the turtlecode IDE) and retry.`));
8810
+ process.exit(1);
8811
+ }
8812
+ });
8809
8813
  program2.parse();
@@ -0,0 +1,12 @@
1
+ /** Published package version (from package.json at repo root). */
2
+ export declare function cliVersion(): string;
3
+ /** Walk up from path (or cwd) and return the nearest Trellis repo root, if any. */
4
+ export declare function findRepoRoot(pathOpt?: string): string | undefined;
5
+ /**
6
+ * Resolve a Trellis repo root from an explicit path or cwd.
7
+ * Walks up parent directories when the starting path is inside a repo
8
+ * (e.g. packages/opencode under a monorepo root with .trellis).
9
+ */
10
+ export declare function resolveRepoRoot(pathOpt?: string): string;
11
+ export declare function failNotRepo(start: string, pathOpt?: string): never;
12
+ //# sourceMappingURL=repo-path.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"repo-path.d.ts","sourceRoot":"","sources":["../../src/cli/repo-path.ts"],"names":[],"mappings":"AAQA,kEAAkE;AAClE,wBAAgB,UAAU,IAAI,MAAM,CASnC;AAED,mFAAmF;AACnF,wBAAgB,YAAY,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CASjE;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,CAKxD;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,KAAK,CAalE"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "trellis",
3
- "version": "3.1.13",
3
+ "version": "3.1.15",
4
4
  "description": "Agentic State Engine — event-sourced causal graph with branching, decision traces, and realtime sync for AI-native applications",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -117,7 +117,7 @@
117
117
  "mcp:docs": "bun run src/mcp/docs.ts",
118
118
  "build": "bun build src/index.ts src/core/index.ts src/vcs/index.ts src/embeddings/index.ts src/links/index.ts src/decisions/index.ts src/server/index.ts src/client/index.ts src/react/index.ts src/db/index.ts src/cli/index.ts src/cms/index.ts src/core/persist/sqljs-backend.ts src/core/persist/factory.ts src/server/node-adapter.ts --outdir dist --target bun --splitting --format esm --root src --external @xenova/transformers --external @huggingface/transformers --external react --external sql.js --external ws && mkdir -p dist/ui && cp src/ui/client.html dist/ui/client.html && tsc -p tsconfig.build.json --emitDeclarationOnly --noEmit false --noEmitOnError false && bun run build:inspector",
119
119
  "build:inspector": "vite build --config vite.inspector.config.ts",
120
- "test": "bun test test/core test/cms test/vcs test/git test/p2 test/p3 test/p4 test/p5 test/p6 test/p7 test/engine.test.ts",
120
+ "test": "bun test test/core test/cms test/vcs test/git test/p2 test/p3 test/p4 test/p5 test/p6 test/p7 test/engine.test.ts test/cli",
121
121
  "test:all": "bun test",
122
122
  "prepublishOnly": "npm run test && npm run build"
123
123
  },
@@ -127,7 +127,7 @@
127
127
  "chalk": "^5.4.1",
128
128
  "commander": "^13.1.0",
129
129
  "opencode-ai": "^1.3.5",
130
- "turtlecode": "^0.3.22",
130
+ "turtlecode": "^0.3.23",
131
131
  "zod": "3"
132
132
  },
133
133
  "optionalDependencies": {