trellis 3.1.13 → 3.1.19

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 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)) {
@@ -5750,115 +5789,147 @@ async function runInit(rootPath, opts = {}) {
5750
5789
  if (isInteractive) {
5751
5790
  console.log(source_default.cyan(`
5752
5791
  Configuring workspace...`));
5753
- const frameworkChoices = [
5754
- { name: "React", value: "react" },
5755
- { name: "Vue", value: "vue" },
5756
- { name: "Svelte", value: "svelte" },
5757
- { name: "Next.js", value: "next" },
5758
- { name: "Nuxt", value: "nuxt" },
5759
- { name: "Remotion", value: "remotion" },
5760
- { name: "Expo / React Native", value: "expo" },
5761
- { name: "Bun runtime", value: "bun" },
5762
- { name: "Node.js server", value: "node" },
5763
- { name: "CLI tool", value: "cli" },
5764
- { name: "Library / Package", value: "library" },
5765
- { name: "Animation studio", value: "animation" },
5766
- { name: "Games", value: "games" },
5767
- { name: "None / Vanilla", value: "none" }
5768
- ];
5769
- framework = await dist_default5({
5770
- message: detectedFramework ? `What type of project are you building? (detected: ${detectedFramework})` : "What type of project are you building?",
5771
- choices: frameworkChoices,
5772
- default: opts.framework || detectedFramework || "none"
5773
- });
5774
- const ideChoices = [
5775
- {
5776
- name: "Cursor",
5777
- value: "cursor",
5778
- checked: selectedIdes.includes("cursor") || selectedIdes.length === 0
5779
- },
5780
- {
5781
- name: "Windsurf",
5782
- value: "windsurf",
5783
- checked: selectedIdes.includes("windsurf")
5784
- },
5785
- {
5786
- name: "Claude Desktop",
5787
- value: "claude",
5788
- checked: selectedIdes.includes("claude")
5789
- },
5790
- {
5791
- name: "VS Code Copilot",
5792
- value: "copilot",
5793
- checked: selectedIdes.includes("copilot")
5794
- },
5795
- {
5796
- name: "Google Gemini (Code Assist)",
5797
- value: "gemini",
5798
- checked: selectedIdes.includes("gemini")
5799
- },
5800
- {
5801
- name: "OpenAI Codex",
5802
- value: "codex",
5803
- checked: selectedIdes.includes("codex")
5804
- }
5805
- ];
5806
- selectedIdes = await dist_default4({
5807
- message: "Which IDEs/agents do you want to scaffold for?",
5808
- choices: ideChoices
5809
- });
5810
- footprint = await dist_default5({
5811
- message: "How minimal do you want your workspace?",
5812
- choices: [
5813
- { name: "Minimal (just agent rules)", value: "minimal" },
5814
- { name: "Standard (includes skills + workflows)", value: "standard" },
5815
- { name: "Full (kitchen sink)", value: "full" }
5816
- ],
5817
- default: footprint
5818
- });
5819
- plugins = await dist_default4({
5820
- message: "Select features:",
5792
+ const setupType = await dist_default5({
5793
+ message: "Choose your setup mode:",
5821
5794
  choices: [
5822
5795
  {
5823
- name: "Sub-projects (monorepo, workspaces)",
5824
- value: "sub-projects",
5825
- checked: plugins.includes("sub-projects")
5796
+ name: "\u26A1 Minimal Setup (One-shot setup with detected defaults)",
5797
+ value: "minimal"
5826
5798
  },
5827
- { name: "P2P Sync", value: "p2p", checked: plugins.includes("p2p") },
5828
5799
  {
5829
- name: "MCP Server",
5830
- value: "mcp",
5831
- checked: plugins.includes("mcp")
5800
+ name: "\uD83D\uDD27 Custom Guided Setup (Customize framework, IDEs, footprint, features)",
5801
+ value: "custom"
5802
+ }
5803
+ ],
5804
+ default: "minimal"
5805
+ });
5806
+ if (setupType === "minimal") {
5807
+ footprint = "minimal";
5808
+ framework = detectedFramework || "none";
5809
+ const ides = [];
5810
+ const envKeys = Object.keys(process.env).join(",").toLowerCase();
5811
+ if (process.env.TERM_PROGRAM === "Windsurf" || envKeys.includes("windsurf")) {
5812
+ ides.push("windsurf");
5813
+ } else if (process.env.TERM_PROGRAM === "vscode" || envKeys.includes("cursor")) {
5814
+ ides.push("cursor");
5815
+ } else {
5816
+ ides.push("cursor", "windsurf");
5817
+ }
5818
+ selectedIdes = ides;
5819
+ plugins = [];
5820
+ console.log(source_default.green(` \u2713 Detected project type: ${source_default.bold(framework)}`));
5821
+ console.log(source_default.green(` \u2713 Scaffolding rules for: ${source_default.bold(selectedIdes.join(", "))}`));
5822
+ } else {
5823
+ const frameworkChoices = [
5824
+ { name: "React", value: "react" },
5825
+ { name: "Vue", value: "vue" },
5826
+ { name: "Svelte", value: "svelte" },
5827
+ { name: "Next.js", value: "next" },
5828
+ { name: "Nuxt", value: "nuxt" },
5829
+ { name: "Remotion", value: "remotion" },
5830
+ { name: "Expo / React Native", value: "expo" },
5831
+ { name: "Bun runtime", value: "bun" },
5832
+ { name: "Node.js server", value: "node" },
5833
+ { name: "CLI tool", value: "cli" },
5834
+ { name: "Library / Package", value: "library" },
5835
+ { name: "Animation studio", value: "animation" },
5836
+ { name: "Games", value: "games" },
5837
+ { name: "None / Vanilla", value: "none" }
5838
+ ];
5839
+ framework = await dist_default5({
5840
+ message: detectedFramework ? `What type of project are you building? (detected: ${detectedFramework})` : "What type of project are you building?",
5841
+ choices: frameworkChoices,
5842
+ default: opts.framework || detectedFramework || "none"
5843
+ });
5844
+ const ideChoices = [
5845
+ {
5846
+ name: "Cursor",
5847
+ value: "cursor",
5848
+ checked: selectedIdes.includes("cursor") || selectedIdes.length === 0
5832
5849
  },
5833
5850
  {
5834
- name: "UI Components",
5835
- value: "ui-components",
5836
- checked: plugins.includes("ui-components")
5851
+ name: "Windsurf",
5852
+ value: "windsurf",
5853
+ checked: selectedIdes.includes("windsurf")
5837
5854
  },
5838
5855
  {
5839
- name: "Media Pipelines",
5840
- value: "media-pipelines",
5841
- checked: plugins.includes("media-pipelines")
5856
+ name: "Claude Desktop",
5857
+ value: "claude",
5858
+ checked: selectedIdes.includes("claude")
5842
5859
  },
5843
5860
  {
5844
- name: "Brand System",
5845
- value: "brand-system",
5846
- checked: plugins.includes("brand-system")
5861
+ name: "VS Code Copilot",
5862
+ value: "copilot",
5863
+ checked: selectedIdes.includes("copilot")
5847
5864
  },
5848
5865
  {
5849
- name: "Agent Memory",
5850
- value: "agent-memory",
5851
- checked: plugins.includes("agent-memory")
5866
+ name: "Google Gemini (Code Assist)",
5867
+ value: "gemini",
5868
+ checked: selectedIdes.includes("gemini")
5852
5869
  },
5853
5870
  {
5854
- name: "Workflows",
5855
- value: "workflows",
5856
- checked: plugins.includes("workflows") || footprint !== "minimal"
5871
+ name: "OpenAI Codex",
5872
+ value: "codex",
5873
+ checked: selectedIdes.includes("codex")
5857
5874
  }
5858
- ]
5859
- });
5860
- if (plugins.includes("sub-projects") && !plugins.includes("workflows")) {
5861
- plugins.push("workflows");
5875
+ ];
5876
+ selectedIdes = await dist_default4({
5877
+ message: "Which IDEs/agents do you want to scaffold for?",
5878
+ choices: ideChoices
5879
+ });
5880
+ footprint = await dist_default5({
5881
+ message: "How minimal do you want your workspace?",
5882
+ choices: [
5883
+ { name: "Minimal (just agent rules)", value: "minimal" },
5884
+ { name: "Standard (includes skills + workflows)", value: "standard" },
5885
+ { name: "Full (kitchen sink)", value: "full" }
5886
+ ],
5887
+ default: footprint
5888
+ });
5889
+ plugins = await dist_default4({
5890
+ message: "Select features:",
5891
+ choices: [
5892
+ {
5893
+ name: "Sub-projects (monorepo, workspaces)",
5894
+ value: "sub-projects",
5895
+ checked: plugins.includes("sub-projects")
5896
+ },
5897
+ { name: "P2P Sync", value: "p2p", checked: plugins.includes("p2p") },
5898
+ {
5899
+ name: "MCP Server",
5900
+ value: "mcp",
5901
+ checked: plugins.includes("mcp")
5902
+ },
5903
+ {
5904
+ name: "UI Components",
5905
+ value: "ui-components",
5906
+ checked: plugins.includes("ui-components")
5907
+ },
5908
+ {
5909
+ name: "Media Pipelines",
5910
+ value: "media-pipelines",
5911
+ checked: plugins.includes("media-pipelines")
5912
+ },
5913
+ {
5914
+ name: "Brand System",
5915
+ value: "brand-system",
5916
+ checked: plugins.includes("brand-system")
5917
+ },
5918
+ {
5919
+ name: "Agent Memory",
5920
+ value: "agent-memory",
5921
+ checked: plugins.includes("agent-memory")
5922
+ },
5923
+ {
5924
+ name: "Workflows",
5925
+ value: "workflows",
5926
+ checked: plugins.includes("workflows") || footprint !== "minimal"
5927
+ }
5928
+ ]
5929
+ });
5930
+ if (plugins.includes("sub-projects") && !plugins.includes("workflows")) {
5931
+ plugins.push("workflows");
5932
+ }
5862
5933
  }
5863
5934
  }
5864
5935
  const engine = new TrellisVcsEngine({ rootPath });
@@ -5893,7 +5964,7 @@ async function runInit(rootPath, opts = {}) {
5893
5964
  }
5894
5965
  }
5895
5966
  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);
5967
+ const rootPath = resolve2(opts.path);
5897
5968
  if (TrellisVcsEngine.isRepo(rootPath)) {
5898
5969
  console.log(source_default.yellow("Already a Trellis workspace."));
5899
5970
  return;
@@ -5911,7 +5982,6 @@ program2.command("init").description("Initialize a new TrellisVCS repository in
5911
5982
  console.log(` ${source_default.dim("Ops:")} ${opsCount} initial operations scanned`);
5912
5983
  console.log(` ${source_default.dim("Config:")} .trellis/config.json`);
5913
5984
  console.log(` ${source_default.dim("Op log:")} .trellis/ops.json`);
5914
- console.log(` ${source_default.dim("Graph DB:")} .trellis/graph.db`);
5915
5985
  console.log(` ${source_default.dim("Agent context:")} .trellis/agents/AGENTS.md`);
5916
5986
  const preInfer = await inferProjectContext(rootPath);
5917
5987
  if (preInfer.domain) {
@@ -5936,8 +6006,7 @@ program2.command("init").description("Initialize a new TrellisVCS repository in
5936
6006
  console.log(source_default.dim("The causal stream is now active. Every file change will be tracked."));
5937
6007
  });
5938
6008
  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);
6009
+ const rootPath = resolveRepoRoot(opts.path);
5941
6010
  const ide = opts.ide || "none";
5942
6011
  const force = opts.force || false;
5943
6012
  console.log(source_default.dim("Seeding agent context..."));
@@ -5954,8 +6023,7 @@ program2.command("seed").description("Refresh agent context files with current p
5954
6023
  }
5955
6024
  });
5956
6025
  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);
6026
+ const rootPath = resolveRepoRoot(opts.path);
5959
6027
  console.log(source_default.yellow("Attempting to repair ops.json..."));
5960
6028
  const result = TrellisVcsEngine.repair(rootPath);
5961
6029
  if (result.lost === -1) {
@@ -5967,11 +6035,7 @@ program2.command("repair").description("Attempt to repair a corrupted .trellis/o
5967
6035
  }
5968
6036
  });
5969
6037
  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
- }
6038
+ const rootPath = resolveRepoRoot(opts.path);
5975
6039
  const engine = new TrellisVcsEngine({ rootPath });
5976
6040
  engine.open();
5977
6041
  const st = engine.status();
@@ -6001,11 +6065,7 @@ program2.command("status").description("Show current repository status").option(
6001
6065
  }
6002
6066
  });
6003
6067
  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
- }
6068
+ const rootPath = resolveRepoRoot(opts.path);
6009
6069
  const engine = new TrellisVcsEngine({ rootPath });
6010
6070
  engine.open();
6011
6071
  const ops = engine.log({
@@ -6028,11 +6088,7 @@ program2.command("log").description("Show operation history").option("-p, --path
6028
6088
  }
6029
6089
  });
6030
6090
  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
- }
6091
+ const rootPath = resolveRepoRoot(opts.path);
6036
6092
  const engine = new TrellisVcsEngine({ rootPath });
6037
6093
  engine.open();
6038
6094
  const files = engine.trackedFiles();
@@ -6048,11 +6104,7 @@ program2.command("files").description("List all tracked files").option("-p, --pa
6048
6104
  }
6049
6105
  });
6050
6106
  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
- }
6107
+ const rootPath = resolveRepoRoot(opts.path);
6056
6108
  const engine = new TrellisVcsEngine({ rootPath });
6057
6109
  engine.open();
6058
6110
  console.log(source_default.green("\u2713 Watching for changes\u2026") + source_default.dim(" (Ctrl+C to stop)"));
@@ -6067,8 +6119,8 @@ program2.command("watch").description("Start file watcher (foreground, Ctrl+C to
6067
6119
  });
6068
6120
  });
6069
6121
  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);
6122
+ const from = resolve2(opts.from);
6123
+ const to = resolve2(opts.path);
6072
6124
  console.log(source_default.dim(`Importing from Git: ${from}`));
6073
6125
  console.log(source_default.dim(`Target: ${to}`));
6074
6126
  console.log();
@@ -6103,12 +6155,8 @@ Import failed: ${err.message}`));
6103
6155
  }
6104
6156
  });
6105
6157
  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
- }
6158
+ const from = resolveRepoRoot(opts.path);
6159
+ const to = resolve2(opts.to);
6112
6160
  console.log(source_default.dim(`Exporting from: ${from}`));
6113
6161
  console.log(source_default.dim(`Target Git repo: ${to}`));
6114
6162
  console.log();
@@ -6141,11 +6189,7 @@ Export failed: ${err.message}`));
6141
6189
  }
6142
6190
  });
6143
6191
  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
- }
6192
+ const rootPath = resolveRepoRoot(opts.path);
6149
6193
  const engine = new TrellisVcsEngine({ rootPath });
6150
6194
  engine.open();
6151
6195
  if (opts.delete) {
@@ -6196,11 +6240,7 @@ program2.command("branch").description("Manage branches").argument("[name]", "Br
6196
6240
  }
6197
6241
  });
6198
6242
  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
- }
6243
+ const rootPath = resolveRepoRoot(opts.path);
6204
6244
  const engine = new TrellisVcsEngine({ rootPath });
6205
6245
  engine.open();
6206
6246
  if (action === "create") {
@@ -6241,11 +6281,7 @@ program2.command("milestone").description("Create or list milestones").argument(
6241
6281
  }
6242
6282
  });
6243
6283
  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
- }
6284
+ const rootPath = resolveRepoRoot(opts.path);
6249
6285
  const engine = new TrellisVcsEngine({ rootPath });
6250
6286
  engine.open();
6251
6287
  if (action === "create") {
@@ -6273,11 +6309,7 @@ program2.command("checkpoint").description("Create or list checkpoints").argumen
6273
6309
  }
6274
6310
  });
6275
6311
  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
- }
6312
+ const rootPath = resolveRepoRoot(opts.path);
6281
6313
  const engine = new TrellisVcsEngine({ rootPath });
6282
6314
  engine.open();
6283
6315
  let result;
@@ -6335,11 +6367,7 @@ program2.command("diff").description("Show file-level diff between two points in
6335
6367
  }
6336
6368
  });
6337
6369
  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
- }
6370
+ const rootPath = resolveRepoRoot(opts.path);
6343
6371
  const engine = new TrellisVcsEngine({ rootPath });
6344
6372
  engine.open();
6345
6373
  const result = engine.mergeBranch(branch);
@@ -6365,13 +6393,12 @@ program2.command("merge").description("Merge a branch into the current branch").
6365
6393
  }
6366
6394
  });
6367
6395
  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);
6396
+ const rootPath = resolveRepoRoot(opts.path);
6369
6397
  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");
6398
+ engine.open();
6399
+ const { readFileSync: readFileSync5 } = __require("fs");
6400
+ const filePath = resolve2(file);
6401
+ const content = readFileSync5(filePath, "utf-8");
6375
6402
  const result = engine.parseFile(content, file);
6376
6403
  if (!result) {
6377
6404
  console.log(source_default.dim(`No parser available for: ${file}`));
@@ -6403,13 +6430,12 @@ program2.command("parse").description("Parse a file into AST-level semantic enti
6403
6430
  }
6404
6431
  });
6405
6432
  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);
6433
+ const rootPath = resolveRepoRoot(opts.path);
6407
6434
  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");
6435
+ engine.open();
6436
+ const { readFileSync: readFileSync5 } = __require("fs");
6437
+ const oldContent = readFileSync5(resolve2(fileA), "utf-8");
6438
+ const newContent = readFileSync5(resolve2(fileB), "utf-8");
6413
6439
  const patches = engine.semanticDiff(oldContent, newContent, fileA);
6414
6440
  if (patches.length === 0) {
6415
6441
  console.log(source_default.dim("No semantic differences."));
@@ -6455,11 +6481,7 @@ program2.command("sdiff").description("Show semantic diff between two versions o
6455
6481
  }
6456
6482
  });
6457
6483
  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
- }
6484
+ const rootPath = resolveRepoRoot(opts.path);
6463
6485
  const engine = new TrellisVcsEngine({ rootPath });
6464
6486
  engine.open();
6465
6487
  const ops = engine.getOps();
@@ -6472,7 +6494,7 @@ program2.command("sync").description("Sync operations with another TrellisVCS re
6472
6494
  return;
6473
6495
  }
6474
6496
  if (action === "reconcile" && opts.remote) {
6475
- const remotePath = resolve(opts.remote);
6497
+ const remotePath = resolve2(opts.remote);
6476
6498
  if (!TrellisVcsEngine.isRepo(remotePath)) {
6477
6499
  console.error(source_default.red(`Not a TrellisVCS repository: ${remotePath}`));
6478
6500
  process.exit(1);
@@ -6502,11 +6524,7 @@ program2.command("sync").description("Sync operations with another TrellisVCS re
6502
6524
  console.log(source_default.dim("Full peer sync requires a transport layer (coming soon)."));
6503
6525
  });
6504
6526
  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
- }
6527
+ const rootPath = resolveRepoRoot(opts.path);
6510
6528
  const engine = new TrellisVcsEngine({ rootPath });
6511
6529
  engine.open();
6512
6530
  const garden = engine.garden();
@@ -6663,9 +6681,8 @@ function formatCriterionStatus(status) {
6663
6681
  }
6664
6682
  }
6665
6683
  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);
6684
+ 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) => {
6685
+ const rootPath = resolveRepoRoot(opts.path);
6669
6686
  const engine = new TrellisVcsEngine({ rootPath });
6670
6687
  engine.open();
6671
6688
  const labels = opts.labels ? opts.labels.split(",").map((l) => l.trim()) : undefined;
@@ -6680,7 +6697,7 @@ issueCmd.command("create").description("Create a new issue").requiredOption("-t,
6680
6697
  labels,
6681
6698
  assignee: opts.assignee,
6682
6699
  parentId: opts.parent,
6683
- description: opts.desc,
6700
+ description: opts.desc ?? opts.description,
6684
6701
  status: opts.status,
6685
6702
  criteria
6686
6703
  });
@@ -6699,8 +6716,7 @@ issueCmd.command("create").description("Create a new issue").requiredOption("-t,
6699
6716
  }
6700
6717
  });
6701
6718
  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);
6719
+ const rootPath = resolveRepoRoot(opts.path);
6704
6720
  const engine = new TrellisVcsEngine({ rootPath });
6705
6721
  engine.open();
6706
6722
  const issues = engine.listIssues({
@@ -6725,8 +6741,7 @@ issueCmd.command("list").description("List issues").option("--status <status>",
6725
6741
  }
6726
6742
  });
6727
6743
  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);
6744
+ const rootPath = resolveRepoRoot(opts.path);
6730
6745
  const engine = new TrellisVcsEngine({ rootPath });
6731
6746
  engine.open();
6732
6747
  const issue = engine.getIssue(id);
@@ -6780,8 +6795,7 @@ issueCmd.command("show").description("Show issue details").argument("<id>", "Iss
6780
6795
  }
6781
6796
  });
6782
6797
  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);
6798
+ const rootPath = resolveRepoRoot(opts.path);
6785
6799
  const engine = new TrellisVcsEngine({ rootPath });
6786
6800
  engine.open();
6787
6801
  const op = await engine.startIssue(id);
@@ -6795,8 +6809,7 @@ issueCmd.command("start").description("Start working on an issue (creates branch
6795
6809
  }
6796
6810
  });
6797
6811
  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);
6812
+ const rootPath = resolveRepoRoot(opts.path);
6800
6813
  const engine = new TrellisVcsEngine({ rootPath });
6801
6814
  engine.open();
6802
6815
  await engine.pauseIssue(id, opts.note);
@@ -6805,8 +6818,7 @@ issueCmd.command("pause").description("Pause an in-progress issue (switches to d
6805
6818
  console.log(` ${source_default.dim("Switched to:")} ${engine.getCurrentBranch()}`);
6806
6819
  });
6807
6820
  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);
6821
+ const rootPath = resolveRepoRoot(opts.path);
6810
6822
  const engine = new TrellisVcsEngine({ rootPath });
6811
6823
  engine.open();
6812
6824
  await engine.resumeIssue(id);
@@ -6817,23 +6829,22 @@ issueCmd.command("resume").description("Resume a paused issue (switches to issue
6817
6829
  }
6818
6830
  });
6819
6831
  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);
6832
+ const rootPath = resolveRepoRoot(opts.path);
6822
6833
  const engine = new TrellisVcsEngine({ rootPath });
6823
6834
  engine.open();
6824
6835
  await engine.triageIssue(id);
6825
6836
  console.log(source_default.green(`\u2713 Triaged ${source_default.bold(id)} \u2192 queue`));
6826
6837
  });
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);
6838
+ 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) => {
6839
+ const rootPath = resolveRepoRoot(opts.path);
6830
6840
  const engine = new TrellisVcsEngine({ rootPath });
6831
6841
  engine.open();
6832
6842
  const updates = {};
6833
6843
  if (opts.title !== undefined)
6834
6844
  updates.title = opts.title;
6835
- if (opts.desc !== undefined)
6836
- updates.description = opts.desc;
6845
+ const desc = opts.desc ?? opts.description;
6846
+ if (desc !== undefined)
6847
+ updates.description = desc;
6837
6848
  if (opts.status !== undefined)
6838
6849
  updates.status = opts.status;
6839
6850
  if (opts.priority !== undefined)
@@ -6847,24 +6858,21 @@ issueCmd.command("update").description("Update issue metadata").argument("<id>",
6847
6858
  console.log(source_default.green(`\u2713 Updated ${source_default.bold(id)}`));
6848
6859
  });
6849
6860
  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);
6861
+ const rootPath = resolveRepoRoot(opts.path);
6852
6862
  const engine = new TrellisVcsEngine({ rootPath });
6853
6863
  engine.open();
6854
6864
  await engine.updateIssue(id, { description });
6855
6865
  console.log(source_default.green(`\u2713 Description set for ${source_default.bold(id)}`));
6856
6866
  });
6857
6867
  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);
6868
+ const rootPath = resolveRepoRoot(opts.path);
6860
6869
  const engine = new TrellisVcsEngine({ rootPath });
6861
6870
  engine.open();
6862
6871
  await engine.assignIssue(id, opts.to);
6863
6872
  console.log(source_default.green(`\u2713 Assigned ${source_default.bold(id)} \u2192 ${opts.to}`));
6864
6873
  });
6865
6874
  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);
6875
+ const rootPath = resolveRepoRoot(opts.path);
6868
6876
  const engine = new TrellisVcsEngine({ rootPath });
6869
6877
  engine.open();
6870
6878
  await engine.addCriterion(id, description, opts.test);
@@ -6872,24 +6880,21 @@ issueCmd.command("ac").description("Add acceptance criterion to an issue").argum
6872
6880
  console.log(source_default.green(`\u2713 Added criterion to ${source_default.bold(id)}: ${description}${cmdNote}`));
6873
6881
  });
6874
6882
  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);
6883
+ const rootPath = resolveRepoRoot(opts.path);
6877
6884
  const engine = new TrellisVcsEngine({ rootPath });
6878
6885
  engine.open();
6879
6886
  await engine.setCriterionStatus(id, parseInt(index, 10), "passed");
6880
6887
  console.log(source_default.green(`\u2713 Criterion #${index} on ${source_default.bold(id)} marked as passed`));
6881
6888
  });
6882
6889
  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);
6890
+ const rootPath = resolveRepoRoot(opts.path);
6885
6891
  const engine = new TrellisVcsEngine({ rootPath });
6886
6892
  engine.open();
6887
6893
  await engine.setCriterionStatus(id, parseInt(index, 10), "failed");
6888
6894
  console.log(source_default.red(`\u2717 Criterion #${index} on ${source_default.bold(id)} marked as failed`));
6889
6895
  });
6890
6896
  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);
6897
+ const rootPath = resolveRepoRoot(opts.path);
6893
6898
  const engine = new TrellisVcsEngine({ rootPath });
6894
6899
  engine.open();
6895
6900
  console.log(source_default.bold(`Running criteria for ${id}...
@@ -6924,8 +6929,7 @@ issueCmd.command("check").description("Run acceptance criteria for an issue").ar
6924
6929
  }
6925
6930
  });
6926
6931
  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);
6932
+ const rootPath = resolveRepoRoot(opts.path);
6929
6933
  const engine = new TrellisVcsEngine({ rootPath });
6930
6934
  engine.open();
6931
6935
  try {
@@ -6947,32 +6951,28 @@ issueCmd.command("close").description("Close an issue (requires all criteria pas
6947
6951
  }
6948
6952
  });
6949
6953
  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);
6954
+ const rootPath = resolveRepoRoot(opts.path);
6952
6955
  const engine = new TrellisVcsEngine({ rootPath });
6953
6956
  engine.open();
6954
6957
  await engine.reopenIssue(id);
6955
6958
  console.log(source_default.green(`\u2713 Issue ${source_default.bold(id)} reopened`));
6956
6959
  });
6957
6960
  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);
6961
+ const rootPath = resolveRepoRoot(opts.path);
6960
6962
  const engine = new TrellisVcsEngine({ rootPath });
6961
6963
  engine.open();
6962
6964
  await engine.blockIssue(id, blockedBy);
6963
6965
  console.log(source_default.yellow(`\uD83D\uDD12 ${source_default.bold(id)} is now blocked by ${source_default.bold(blockedBy)}`));
6964
6966
  });
6965
6967
  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);
6968
+ const rootPath = resolveRepoRoot(opts.path);
6968
6969
  const engine = new TrellisVcsEngine({ rootPath });
6969
6970
  engine.open();
6970
6971
  await engine.unblockIssue(id, blockedBy);
6971
6972
  console.log(source_default.green(`\uD83D\uDD13 ${source_default.bold(id)} is no longer blocked by ${source_default.bold(blockedBy)}`));
6972
6973
  });
6973
6974
  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);
6975
+ const rootPath = resolveRepoRoot(opts.path);
6976
6976
  const engine = new TrellisVcsEngine({ rootPath });
6977
6977
  engine.open();
6978
6978
  const active = engine.getActiveIssues();
@@ -6989,8 +6989,7 @@ issueCmd.command("active").description("Show all active (in-progress) issues").o
6989
6989
  }
6990
6990
  });
6991
6991
  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);
6992
+ const rootPath = resolveRepoRoot(opts.path);
6994
6993
  const engine = new TrellisVcsEngine({ rootPath });
6995
6994
  engine.open();
6996
6995
  const result = engine.checkCompletionReadiness();
@@ -7001,8 +7000,7 @@ issueCmd.command("readiness").description("Check if all issues are complete (no
7001
7000
  });
7002
7001
  var decisionCmd = program2.command("decision").description("Manage decision traces");
7003
7002
  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);
7003
+ const rootPath = resolveRepoRoot(opts.path);
7006
7004
  const engine = new TrellisVcsEngine({ rootPath });
7007
7005
  engine.open();
7008
7006
  const decisions = engine.queryDecisions({
@@ -7023,8 +7021,7 @@ decisionCmd.command("list").description("List decision traces").option("-p, --pa
7023
7021
  }
7024
7022
  });
7025
7023
  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);
7024
+ const rootPath = resolveRepoRoot(opts.path);
7028
7025
  const engine = new TrellisVcsEngine({ rootPath });
7029
7026
  engine.open();
7030
7027
  const d = engine.getDecision(id);
@@ -7051,8 +7048,7 @@ decisionCmd.command("show").description("Show full details of a decision trace")
7051
7048
  }
7052
7049
  });
7053
7050
  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);
7051
+ const rootPath = resolveRepoRoot(opts.path);
7056
7052
  const engine = new TrellisVcsEngine({ rootPath });
7057
7053
  engine.open();
7058
7054
  const chain = engine.getDecisionChain(entityId);
@@ -7070,7 +7066,7 @@ decisionCmd.command("chain").description("Trace all decisions that affected a gi
7070
7066
  }
7071
7067
  });
7072
7068
  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);
7069
+ const rootPath = resolve2(opts.path);
7074
7070
  const trellisDir = join6(rootPath, ".trellis");
7075
7071
  if (action === "init") {
7076
7072
  if (hasIdentity(trellisDir)) {
@@ -7108,14 +7104,10 @@ program2.command("identity").description("Manage local identity (Ed25519 key pai
7108
7104
  console.log(` ${source_default.dim("Created:")} ${pub.createdAt}`);
7109
7105
  });
7110
7106
  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
- }
7107
+ const rootPath = resolveRepoRoot(opts.path);
7116
7108
  const engine = new TrellisVcsEngine({ rootPath });
7117
7109
  engine.open();
7118
- const { readFileSync: readFileSync4 } = __require("fs");
7110
+ const { readFileSync: readFileSync5 } = __require("fs");
7119
7111
  const {
7120
7112
  parseFileRefs,
7121
7113
  buildRefIndex: buildRefIndex2,
@@ -7135,7 +7127,7 @@ program2.command("refs").description("List wiki-link references in files or find
7135
7127
  for (const f of trackedFiles) {
7136
7128
  try {
7137
7129
  const absPath = join6(rootPath, f.path);
7138
- const content = readFileSync4(absPath, "utf-8");
7130
+ const content = readFileSync5(absPath, "utf-8");
7139
7131
  fileContents.push({ path: f.path, content });
7140
7132
  } catch {}
7141
7133
  }
@@ -7247,11 +7239,7 @@ program2.command("refs").description("List wiki-link references in files or find
7247
7239
  }
7248
7240
  });
7249
7241
  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
- }
7242
+ const rootPath = resolveRepoRoot(opts.path);
7255
7243
  const engine = new TrellisVcsEngine({ rootPath });
7256
7244
  engine.open();
7257
7245
  const { EmbeddingManager } = (init_embeddings(), __toCommonJS(exports_embeddings));
@@ -7285,11 +7273,7 @@ program2.command("search").description("Semantic search across all embedded cont
7285
7273
  }
7286
7274
  });
7287
7275
  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
- }
7276
+ const rootPath = resolveRepoRoot(opts.path);
7293
7277
  const engine = new TrellisVcsEngine({ rootPath });
7294
7278
  engine.open();
7295
7279
  const { EmbeddingManager } = (init_embeddings(), __toCommonJS(exports_embeddings));
@@ -7306,11 +7290,7 @@ program2.command("reindex").description("Rebuild the semantic embedding index").
7306
7290
  }
7307
7291
  });
7308
7292
  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
- }
7293
+ const rootPath = resolveRepoRoot(opts.path);
7314
7294
  const { startUIServer: startUIServer2 } = (init_server(), __toCommonJS(exports_server));
7315
7295
  const port = parseInt(opts.port, 10) || 3000;
7316
7296
  try {
@@ -7348,8 +7328,7 @@ async function bootKernel(rootPath) {
7348
7328
  }
7349
7329
  var entityCmd = program2.command("entity").description("Manage graph entities (generic CRUD)");
7350
7330
  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);
7331
+ const rootPath = resolveRepoRoot(opts.path);
7353
7332
  const kernel = await bootKernel(rootPath);
7354
7333
  try {
7355
7334
  const attrs = {};
@@ -7379,8 +7358,7 @@ entityCmd.command("create").description("Create a new entity in the graph").requ
7379
7358
  }
7380
7359
  });
7381
7360
  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);
7361
+ const rootPath = resolveRepoRoot(opts.path);
7384
7362
  const kernel = await bootKernel(rootPath);
7385
7363
  try {
7386
7364
  const entity = kernel.getEntity(id);
@@ -7421,8 +7399,7 @@ entityCmd.command("get").description("Get an entity by ID").argument("<id>", "En
7421
7399
  }
7422
7400
  });
7423
7401
  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);
7402
+ const rootPath = resolveRepoRoot(opts.path);
7426
7403
  const kernel = await bootKernel(rootPath);
7427
7404
  try {
7428
7405
  const updates = {};
@@ -7450,8 +7427,7 @@ entityCmd.command("update").description("Update attributes on an existing entity
7450
7427
  }
7451
7428
  });
7452
7429
  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);
7430
+ const rootPath = resolveRepoRoot(opts.path);
7455
7431
  const kernel = await bootKernel(rootPath);
7456
7432
  try {
7457
7433
  const entity = kernel.getEntity(id);
@@ -7466,8 +7442,7 @@ entityCmd.command("delete").description("Delete an entity and all its facts/link
7466
7442
  }
7467
7443
  });
7468
7444
  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);
7445
+ const rootPath = resolveRepoRoot(opts.path);
7471
7446
  const kernel = await bootKernel(rootPath);
7472
7447
  try {
7473
7448
  let filters;
@@ -7519,8 +7494,7 @@ entityCmd.command("list").description("List entities, optionally filtered by typ
7519
7494
  });
7520
7495
  var factCmd = program2.command("fact").description("Add or remove individual facts on entities");
7521
7496
  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);
7497
+ const rootPath = resolveRepoRoot(opts.path);
7524
7498
  const kernel = await bootKernel(rootPath);
7525
7499
  try {
7526
7500
  let val = value;
@@ -7537,8 +7511,7 @@ factCmd.command("add").description("Add a fact to an entity").argument("<entity>
7537
7511
  }
7538
7512
  });
7539
7513
  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);
7514
+ const rootPath = resolveRepoRoot(opts.path);
7542
7515
  const kernel = await bootKernel(rootPath);
7543
7516
  try {
7544
7517
  let val = value;
@@ -7555,8 +7528,7 @@ factCmd.command("remove").description("Remove a fact from an entity").argument("
7555
7528
  }
7556
7529
  });
7557
7530
  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);
7531
+ const rootPath = resolveRepoRoot(opts.path);
7560
7532
  const kernel = await bootKernel(rootPath);
7561
7533
  try {
7562
7534
  const store = kernel.getStore();
@@ -7590,8 +7562,7 @@ factCmd.command("query").description("Query facts by entity or attribute").optio
7590
7562
  });
7591
7563
  var linkCmd = program2.command("link").description("Add or remove links between entities");
7592
7564
  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);
7565
+ const rootPath = resolveRepoRoot(opts.path);
7595
7566
  const kernel = await bootKernel(rootPath);
7596
7567
  try {
7597
7568
  await kernel.addLink(source, attribute, target);
@@ -7601,8 +7572,7 @@ linkCmd.command("add").description("Add a link between two entities").argument("
7601
7572
  }
7602
7573
  });
7603
7574
  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);
7575
+ const rootPath = resolveRepoRoot(opts.path);
7606
7576
  const kernel = await bootKernel(rootPath);
7607
7577
  try {
7608
7578
  await kernel.removeLink(source, attribute, target);
@@ -7612,8 +7582,7 @@ linkCmd.command("remove").description("Remove a link between two entities").argu
7612
7582
  }
7613
7583
  });
7614
7584
  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);
7585
+ const rootPath = resolveRepoRoot(opts.path);
7617
7586
  const kernel = await bootKernel(rootPath);
7618
7587
  try {
7619
7588
  const store = kernel.getStore();
@@ -7648,8 +7617,7 @@ linkCmd.command("query").description("Query links for an entity").option("-e, --
7648
7617
  }
7649
7618
  });
7650
7619
  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);
7620
+ const rootPath = resolveRepoRoot(opts.path);
7653
7621
  const kernel = await bootKernel(rootPath);
7654
7622
  try {
7655
7623
  const store = kernel.getStore();
@@ -7688,8 +7656,7 @@ ${result.count} result(s) in ${result.executionTime.toFixed(1)}ms`));
7688
7656
  }
7689
7657
  });
7690
7658
  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);
7659
+ const rootPath = resolveRepoRoot(opts.path);
7693
7660
  const kernel = await bootKernel(rootPath);
7694
7661
  const store = kernel.getStore();
7695
7662
  const engine = new QueryEngine(store);
@@ -7867,8 +7834,7 @@ Relations:`));
7867
7834
  console.log(source_default.dim("Available types: " + registry.listEntityTypes().join(", ")));
7868
7835
  });
7869
7836
  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);
7837
+ const rootPath = resolveRepoRoot(opts.path);
7872
7838
  const kernel = await bootKernel(rootPath);
7873
7839
  try {
7874
7840
  const registry = new OntologyRegistry;
@@ -7905,8 +7871,7 @@ ontologyCmd.command("validate").description("Validate all entities in the graph
7905
7871
  }
7906
7872
  });
7907
7873
  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);
7874
+ const rootPath = resolveRepoRoot(opts.path);
7910
7875
  const dbPath = join6(rootPath, ".trellis", "embeddings.db");
7911
7876
  const vectorStore = new VectorStore(dbPath);
7912
7877
  try {
@@ -7995,12 +7960,12 @@ function formatRelativeTime(iso) {
7995
7960
  var db = program2.command("db").description("Trellis DB \u2014 use Trellis as an application database");
7996
7961
  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
7962
  const { writeConfig, defaultLocalConfig, hasConfig, configPath } = await import("../config-8hczw0rs.js");
7998
- const { resolve: resolve2 } = await import("path");
7963
+ const { resolve: resolve3 } = await import("path");
7999
7964
  if (hasConfig(".")) {
8000
7965
  console.log(source_default.yellow(`Already initialized (${configPath(".")}). Use \`trellis db serve\` to start.`));
8001
7966
  return;
8002
7967
  }
8003
- const dbPath = resolve2(opts.path);
7968
+ const dbPath = resolve3(opts.path);
8004
7969
  const apiKey = opts.key ?? `spk_${crypto.randomUUID().replace(/-/g, "")}`;
8005
7970
  const jwtSecret = opts.jwtSecret ?? `jws_${crypto.randomUUID().replace(/-/g, "")}`;
8006
7971
  writeConfig(defaultLocalConfig(dbPath, {
@@ -8048,8 +8013,8 @@ db.command("create <type> [json]").description("Create an entity (json can be a
8048
8013
  const db2 = TrellisDb.fromConfig(opts.configDir);
8049
8014
  let attributes = {};
8050
8015
  if (jsonArg) {
8051
- const { readFileSync: readFileSync4 } = await import("fs");
8052
- const raw = jsonArg.startsWith("@") ? readFileSync4(jsonArg.slice(1), "utf8") : jsonArg;
8016
+ const { readFileSync: readFileSync5 } = await import("fs");
8017
+ const raw = jsonArg.startsWith("@") ? readFileSync5(jsonArg.slice(1), "utf8") : jsonArg;
8053
8018
  attributes = JSON.parse(raw);
8054
8019
  }
8055
8020
  const id = await db2.create(type, attributes);
@@ -8070,8 +8035,8 @@ db.command("read <id>").description("Read an entity by ID").option("--config-dir
8070
8035
  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
8036
  const { TrellisDb } = await import("../sdk-bepky0xs.js");
8072
8037
  const db2 = TrellisDb.fromConfig(opts.configDir);
8073
- const { readFileSync: readFileSync4 } = await import("fs");
8074
- const raw = jsonArg.startsWith("@") ? readFileSync4(jsonArg.slice(1), "utf8") : jsonArg;
8038
+ const { readFileSync: readFileSync5 } = await import("fs");
8039
+ const raw = jsonArg.startsWith("@") ? readFileSync5(jsonArg.slice(1), "utf8") : jsonArg;
8075
8040
  await db2.update(id, JSON.parse(raw));
8076
8041
  console.log(source_default.green(`\u2713 Updated: ${source_default.bold(id)}`));
8077
8042
  db2.close();
@@ -8102,7 +8067,7 @@ db.command("query <eql>").description("Run an EQL-S query").option("--config-dir
8102
8067
  db2.close();
8103
8068
  });
8104
8069
  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");
8070
+ const { readFileSync: readFileSync5 } = await import("fs");
8106
8071
  const { extname } = await import("path");
8107
8072
  const { TrellisDb } = await import("../sdk-bepky0xs.js");
8108
8073
  const mimeMap = {
@@ -8115,7 +8080,7 @@ db.command("upload <file>").description("Upload a file to the blob store").optio
8115
8080
  ".txt": "text/plain"
8116
8081
  };
8117
8082
  const contentType = opts.type ?? mimeMap[extname(filePath).toLowerCase()] ?? "application/octet-stream";
8118
- const buffer = readFileSync4(filePath);
8083
+ const buffer = readFileSync5(filePath);
8119
8084
  const db2 = TrellisDb.fromConfig(opts.configDir);
8120
8085
  const result = await db2.upload(new Uint8Array(buffer), contentType);
8121
8086
  console.log(source_default.green(`\u2713 Uploaded`));
@@ -8266,10 +8231,10 @@ vmProgram.command("destroy <name>").description("Destroy a Sprite").action(async
8266
8231
  input: process.stdin,
8267
8232
  output: process.stdout
8268
8233
  });
8269
- const answer = await new Promise((resolve2) => {
8234
+ const answer = await new Promise((resolve3) => {
8270
8235
  rl.question(`Destroy Sprite "${name}"? Cannot be undone. (y/N) `, (a) => {
8271
8236
  rl.close();
8272
- resolve2(a.trim().toLowerCase());
8237
+ resolve3(a.trim().toLowerCase());
8273
8238
  });
8274
8239
  });
8275
8240
  if (answer !== "y" && answer !== "yes") {
@@ -8532,8 +8497,7 @@ vmProgram.command("code [name]").description("Create Sprite, deploy Trellis, ope
8532
8497
  });
8533
8498
  program2.addCommand(vmProgram);
8534
8499
  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);
8500
+ const rootPath = resolveRepoRoot(opts.path);
8537
8501
  const { createInterface: createInterface2 } = await import("readline");
8538
8502
  const rl = createInterface2({
8539
8503
  input: process.stdin,
@@ -8561,8 +8525,8 @@ program2.command("season").description("Enrich project context for agents \u2014
8561
8525
  writeAgentScaffold(rootPath, { profile, context: updatedContext });
8562
8526
  const agentContextPath = join6(rootPath, ".trellis", "agents", "agent-context.json");
8563
8527
  try {
8564
- const { readFileSync: readFileSync4, writeFileSync: writeFileSync3 } = await import("fs");
8565
- const existing = JSON.parse(readFileSync4(agentContextPath, "utf-8"));
8528
+ const { readFileSync: readFileSync5, writeFileSync: writeFileSync3 } = await import("fs");
8529
+ const existing = JSON.parse(readFileSync5(agentContextPath, "utf-8"));
8566
8530
  existing.domain = updatedContext.domain;
8567
8531
  if (toolsRaw) {
8568
8532
  existing.tools = toolsRaw.split(",").map((s) => s.trim()).filter(Boolean);
@@ -8584,12 +8548,12 @@ program2.command("season").description("Enrich project context for agents \u2014
8584
8548
  console.log();
8585
8549
  });
8586
8550
  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");
8551
+ const { resolve: resolve3, dirname: dirname7, join: join7 } = await import("path");
8588
8552
  const { existsSync: existsSync6 } = await import("fs");
8589
8553
  const { spawn } = await import("child_process");
8590
- const { readFileSync: readFileSync4 } = await import("fs");
8554
+ const { readFileSync: readFileSync5 } = await import("fs");
8591
8555
  const { createServer: createHttpServer } = await import("http");
8592
- const rootPath = resolve2(opts.path);
8556
+ const rootPath = findRepoRoot(opts.path) ?? resolve3(opts.path);
8593
8557
  if (!opts.noInit) {
8594
8558
  if (!TrellisVcsEngine.isRepo(rootPath)) {
8595
8559
  console.log(source_default.dim("Not a Trellis workspace \u2014 initializing\u2026"));
@@ -8600,17 +8564,17 @@ program2.command("code").alias("ide").description('Launch OpenCode in "Harness"
8600
8564
  }
8601
8565
  }
8602
8566
  function findOpencode() {
8603
- const here = dirname6(process.argv[1]);
8567
+ const here2 = dirname7(process.argv[1]);
8604
8568
  const candidates = [
8605
- join7(here, "..", "node_modules", ".bin", "opencode"),
8606
- join7(here, "..", "..", "node_modules", ".bin", "opencode")
8569
+ join7(here2, "..", "node_modules", ".bin", "opencode"),
8570
+ join7(here2, "..", "..", "node_modules", ".bin", "opencode")
8607
8571
  ];
8608
- let dir = here;
8572
+ let dir = here2;
8609
8573
  for (let i = 0;i < 5; i++) {
8610
8574
  const p = join7(dir, "node_modules", ".bin", "opencode");
8611
8575
  if (existsSync6(p))
8612
8576
  return p;
8613
- dir = dirname6(dir);
8577
+ dir = dirname7(dir);
8614
8578
  }
8615
8579
  for (const p of candidates) {
8616
8580
  if (existsSync6(p))
@@ -8619,14 +8583,14 @@ program2.command("code").alias("ide").description('Launch OpenCode in "Harness"
8619
8583
  return null;
8620
8584
  }
8621
8585
  function findMcpServer() {
8622
- const here = dirname6(process.argv[1]);
8586
+ const here2 = dirname7(process.argv[1]);
8623
8587
  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")
8588
+ join7(here2, "..", "..", "src", "mcp", "index.ts"),
8589
+ join7(here2, "..", "src", "mcp", "index.ts"),
8590
+ join7(here2, "..", "..", "..", "src", "mcp", "index.ts"),
8591
+ join7(here2, "..", "mcp", "index.js"),
8592
+ join7(here2, "..", "mcp", "index.ts"),
8593
+ join7(here2, "mcp", "index.ts")
8630
8594
  ];
8631
8595
  for (const p of candidates) {
8632
8596
  if (existsSync6(p))
@@ -8657,7 +8621,16 @@ program2.command("code").alias("ide").description('Launch OpenCode in "Harness"
8657
8621
  let mcpProcess = null;
8658
8622
  if (opts.web) {
8659
8623
  console.log(source_default.dim(` Starting MCP server on port ${mcpPort}\u2026`));
8660
- mcpProcess = spawn("bun", ["run", mcp, "--quiet", "--path", rootPath, "--http", "--port", String(mcpPort)], {
8624
+ mcpProcess = spawn("bun", [
8625
+ "run",
8626
+ mcp,
8627
+ "--quiet",
8628
+ "--path",
8629
+ rootPath,
8630
+ "--http",
8631
+ "--port",
8632
+ String(mcpPort)
8633
+ ], {
8661
8634
  stdio: ["pipe", "pipe", "pipe"],
8662
8635
  detached: false
8663
8636
  });
@@ -8723,12 +8696,12 @@ program2.command("code").alias("ide").description('Launch OpenCode in "Harness"
8723
8696
  });
8724
8697
  });
8725
8698
  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");
8699
+ const { resolve: resolve3 } = await import("path");
8700
+ const { existsSync: existsSync6, readFileSync: readFileSync5 } = await import("fs");
8728
8701
  const { spawn } = await import("child_process");
8729
8702
  const { createRequire } = await import("module");
8730
8703
  const path = await import("path");
8731
- const rootPath = resolve2(opts.path);
8704
+ const rootPath = findRepoRoot(opts.path) ?? resolve3(opts.path);
8732
8705
  if (!opts.noInit) {
8733
8706
  if (!TrellisVcsEngine.isRepo(rootPath)) {
8734
8707
  console.log(source_default.dim("Not a Trellis workspace \u2014 initializing\u2026"));
@@ -8740,7 +8713,7 @@ program2.command("studio").alias("web").description("Launch Trellis Studio in yo
8740
8713
  let turtlecodeBin = null;
8741
8714
  try {
8742
8715
  const pkgPath = requireFn.resolve("turtlecode/package.json");
8743
- const pkg = JSON.parse(readFileSync4(pkgPath, "utf-8"));
8716
+ const pkg = JSON.parse(readFileSync5(pkgPath, "utf-8"));
8744
8717
  const binRel = typeof pkg.bin === "string" ? pkg.bin : pkg.bin?.turtlecode;
8745
8718
  if (binRel) {
8746
8719
  const candidate = path.join(path.dirname(pkgPath), binRel);
@@ -8792,10 +8765,10 @@ async function waitForMcpReady(url, timeoutMs) {
8792
8765
  const { get } = await import("http");
8793
8766
  const start = Date.now();
8794
8767
  while (Date.now() - start < timeoutMs) {
8795
- await new Promise((resolve2) => setTimeout(resolve2, 500));
8768
+ await new Promise((resolve3) => setTimeout(resolve3, 500));
8796
8769
  try {
8797
- const res = await new Promise((resolve2, reject) => {
8798
- const req = get(`${url}/health`, resolve2);
8770
+ const res = await new Promise((resolve3, reject) => {
8771
+ const req = get(`${url}/health`, resolve3);
8799
8772
  req.on("error", reject);
8800
8773
  req.setTimeout(2000);
8801
8774
  });
@@ -8806,4 +8779,100 @@ async function waitForMcpReady(url, timeoutMs) {
8806
8779
  }
8807
8780
  return false;
8808
8781
  }
8782
+ var cmsCmd = program2.command("cms").description("Manage CMS entities (collections, libraries, schemas)");
8783
+ 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) => {
8784
+ const rootPath = resolveRepoRoot(opts.path);
8785
+ const { readFileSync: readFileSync5, existsSync: existsSync6 } = await import("fs");
8786
+ const manifestPath = join6(rootPath, "node_modules", pkg, "dist", "components.json");
8787
+ if (!existsSync6(manifestPath)) {
8788
+ console.error(source_default.red(`\u2717 No manifest at ${manifestPath}`));
8789
+ console.error(source_default.dim(` Install ${pkg} in this repo and ensure it ships dist/components.json.`));
8790
+ process.exit(1);
8791
+ }
8792
+ let manifest;
8793
+ try {
8794
+ manifest = JSON.parse(readFileSync5(manifestPath, "utf8"));
8795
+ } catch (err) {
8796
+ console.error(source_default.red(`\u2717 Manifest is not valid JSON: ${manifestPath}`));
8797
+ console.error(source_default.dim(` ${err?.message ?? String(err)}`));
8798
+ process.exit(1);
8799
+ }
8800
+ const { package: pkgName, version, components } = manifest;
8801
+ if (!Array.isArray(components) || components.length === 0) {
8802
+ console.error(source_default.yellow(`\u26A0 Manifest is empty: ${manifestPath}`));
8803
+ process.exit(1);
8804
+ }
8805
+ const pkgSlug = pkgName.replace(/^@/, "").replace(/\//g, "__");
8806
+ const now = new Date().toISOString();
8807
+ const facts = [];
8808
+ for (const comp of components) {
8809
+ const id = `design:component:${pkgSlug}:${comp.slug}`;
8810
+ 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 });
8811
+ }
8812
+ if (opts.dryRun) {
8813
+ console.log(source_default.dim("\u2500\u2500 Dry run: would assert these facts \u2500\u2500"));
8814
+ console.log(JSON.stringify(facts, null, 2));
8815
+ console.log(source_default.dim(`\u2500\u2500 ${facts.length} facts across ${components.length} components \u2500\u2500`));
8816
+ return;
8817
+ }
8818
+ const url = new URL("/trellis/store/assert", opts.url);
8819
+ url.searchParams.set("directory", rootPath);
8820
+ const meta = {
8821
+ actor: "cli:trellis",
8822
+ actorKind: "user",
8823
+ source: "trellis-cms-register-library",
8824
+ relatedEntities: [...new Set(facts.map((f) => f.e))]
8825
+ };
8826
+ try {
8827
+ const res = await fetch(url.toString(), {
8828
+ method: "POST",
8829
+ headers: { "Content-Type": "application/json" },
8830
+ body: JSON.stringify({ facts, meta })
8831
+ });
8832
+ if (!res.ok) {
8833
+ const body2 = await res.text().catch(() => "");
8834
+ console.error(source_default.red(`\u2717 Assert failed: HTTP ${res.status}`));
8835
+ if (body2)
8836
+ console.error(source_default.dim(` ${body2.slice(0, 400)}`));
8837
+ process.exit(1);
8838
+ }
8839
+ const body = await res.json().catch(() => ({ added: 0 }));
8840
+ console.log(source_default.green(`\u2713 Registered ${source_default.bold(String(components.length))} components from ${source_default.bold(pkgName)}@${version}`));
8841
+ console.log(` ${source_default.dim("Facts added:")} ${body.added ?? "?"}`);
8842
+ console.log(` ${source_default.dim("Workspace:")} ${rootPath}`);
8843
+ for (const comp of components) {
8844
+ console.log(` ${source_default.dim("\u2022")} ${comp.component} ${source_default.dim(`(design:component:${pkgSlug}:${comp.slug})`)}`);
8845
+ }
8846
+ } catch (err) {
8847
+ const msg = err?.message ?? String(err);
8848
+ console.error(source_default.red(`\u2717 Could not reach Trellis server at ${opts.url}`));
8849
+ console.error(source_default.dim(` ${msg}`));
8850
+ console.error(source_default.dim(` Hint: start the Trellis backend (e.g. open the turtlecode IDE) and retry.`));
8851
+ process.exit(1);
8852
+ }
8853
+ });
8854
+ program2.command("skills").description("Install Trellis agent skills using the skills CLI (npx skills)").argument("[args...]", "Additional arguments to pass to the skills CLI").allowUnknownOption().action(async () => {
8855
+ const skillsIndex = process.argv.indexOf("skills");
8856
+ const extraArgs = skillsIndex !== -1 ? process.argv.slice(skillsIndex + 1) : [];
8857
+ const { spawnSync } = await import("child_process");
8858
+ const skillsArgs = ["skills", "add", "trentbrew/trellis", ...extraArgs];
8859
+ console.log(source_default.cyan(" Installing Trellis agent skills..."));
8860
+ console.log(source_default.dim(` Running: npx ${skillsArgs.join(" ")}
8861
+ `));
8862
+ if (process.env.TRELLIS_CLI_DRY_RUN === "1") {
8863
+ return;
8864
+ }
8865
+ const result = spawnSync("npx", skillsArgs, {
8866
+ stdio: "inherit",
8867
+ shell: true
8868
+ });
8869
+ if (result.error) {
8870
+ console.error(source_default.red(`
8871
+ \u2717 Failed to run skills CLI: ${result.error.message}`));
8872
+ process.exit(1);
8873
+ }
8874
+ if (result.status !== 0) {
8875
+ process.exit(result.status ?? 1);
8876
+ }
8877
+ });
8809
8878
  program2.parse();