trellis 3.0.2 → 3.1.1

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/README.md CHANGED
@@ -1,8 +1,6 @@
1
1
  # Trellis
2
2
 
3
- https://trellis.computer
4
-
5
- > **The Agentic Framework** — A structured runtime for building agents that understand code, remember everything, and explain themselves.
3
+ > **The Agentic Framework**
6
4
 
7
5
  | Use Case | How |
8
6
  | :----------------------------- | :-------------------------------------------------------------------------------------------------------- |
@@ -42,10 +40,12 @@ echo "# My Project" > README.md
42
40
  trellis issue create --title "Bootstrap Visualization"
43
41
  trellis milestone "Initial Release"
44
42
 
45
- # 5. Visualize the system (auto-opens browser)
43
+ # 5. Create coding session with trellis harness
46
44
  trellis code
47
45
  ```
48
46
 
47
+ ![Trellis Demo](./trellis-opencode.gif)
48
+
49
49
  See the [CLI guide](https://trellis.computer/docs/cli) for complete documentation.
50
50
 
51
51
  ---
@@ -80,7 +80,11 @@ Ops are written to `.trellis/ops.json` and **never rewritten or deleted**.
80
80
 
81
81
  ## Documentation
82
82
 
83
- - **[Full documentation](https://trellis.computer)** — Complete guides and API reference
83
+ - **[Local documentation](./docs/README.md)** — Canonical docs for the active codebase
84
+ - **[Vision](./docs/VISION.md)** — Local-first agentic OS framing
85
+ - **[Architecture](./docs/ARCHITECTURE.md)** — Current package layout and target runtime shape
86
+ - **[Roadmap](./ROADMAP.md)** — Active Trellis issue and milestone sequence
87
+ - **[Full documentation](https://trellis.computer)** — Published docs site
84
88
  - **[CLI reference](./README-ARCHIVED.md#cli-overview)** — Command details (archived)
85
89
  - **[API modules](./README-ARCHIVED.md#module--subpath-guide)** — Subpath imports (archived)
86
90
  - **[DESIGN.md](./DESIGN.md)** — Architecture specification
package/dist/cli/index.js CHANGED
@@ -2886,7 +2886,7 @@ var {
2886
2886
  Help
2887
2887
  } = import__.default;
2888
2888
 
2889
- // ../trellis-client/node_modules/.pnpm/chalk@5.6.2/node_modules/chalk/source/vendor/ansi-styles/index.js
2889
+ // node_modules/chalk/source/vendor/ansi-styles/index.js
2890
2890
  var ANSI_BACKGROUND_OFFSET = 10;
2891
2891
  var wrapAnsi16 = (offset = 0) => (code) => `\x1B[${code + offset}m`;
2892
2892
  var wrapAnsi256 = (offset = 0) => (code) => `\x1B[${38 + offset};5;${code}m`;
@@ -3063,7 +3063,7 @@ function assembleStyles() {
3063
3063
  var ansiStyles = assembleStyles();
3064
3064
  var ansi_styles_default = ansiStyles;
3065
3065
 
3066
- // ../trellis-client/node_modules/.pnpm/chalk@5.6.2/node_modules/chalk/source/vendor/supports-color/index.js
3066
+ // node_modules/chalk/source/vendor/supports-color/index.js
3067
3067
  import process2 from "process";
3068
3068
  import os from "os";
3069
3069
  import tty from "tty";
@@ -3195,7 +3195,7 @@ var supportsColor = {
3195
3195
  };
3196
3196
  var supports_color_default = supportsColor;
3197
3197
 
3198
- // ../trellis-client/node_modules/.pnpm/chalk@5.6.2/node_modules/chalk/source/utilities.js
3198
+ // node_modules/chalk/source/utilities.js
3199
3199
  function stringReplaceAll(string, substring, replacer) {
3200
3200
  let index = string.indexOf(substring);
3201
3201
  if (index === -1) {
@@ -3228,7 +3228,7 @@ function stringEncaseCRLFWithFirstIndex(string, prefix, postfix, index) {
3228
3228
  return returnValue;
3229
3229
  }
3230
3230
 
3231
- // ../trellis-client/node_modules/.pnpm/chalk@5.6.2/node_modules/chalk/source/index.js
3231
+ // node_modules/chalk/source/index.js
3232
3232
  var { stdout: stdoutColor, stderr: stderrColor } = supports_color_default;
3233
3233
  var GENERATOR = Symbol("GENERATOR");
3234
3234
  var STYLER = Symbol("STYLER");
@@ -5716,16 +5716,13 @@ function requireRepo(rootPath) {
5716
5716
  process.exit(1);
5717
5717
  }
5718
5718
  }
5719
- 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) => {
5720
- const rootPath = resolve(opts.path);
5721
- const isInteractive = process.stdout.isTTY && !process.argv.includes("--no-interactive");
5719
+ async function runInit(rootPath, opts = {}) {
5720
+ const isInteractive = opts.interactive !== false && process.stdout.isTTY && !process.argv.includes("--no-interactive");
5722
5721
  if (TrellisVcsEngine.isRepo(rootPath)) {
5723
- console.log(source_default.yellow("Already a Trellis workspace."));
5724
5722
  return;
5725
5723
  }
5726
5724
  if (!hasProfile()) {
5727
- const trellisDir = join6(rootPath, ".trellis");
5728
- const identity = hasIdentity(trellisDir) ? loadIdentity(trellisDir) : null;
5725
+ const identity = hasIdentity(rootPath) ? loadIdentity(join6(rootPath, ".trellis")) : null;
5729
5726
  if (isInteractive) {
5730
5727
  console.log(source_default.cyan(`
5731
5728
  Welcome to Trellis! Let's get you set up.`));
@@ -5884,7 +5881,6 @@ program2.command("init").description("Initialize a new TrellisVCS repository in
5884
5881
  }
5885
5882
  if (selectedIdes && selectedIdes.length > 0) {
5886
5883
  for (const selectedIde of selectedIdes) {
5887
- console.log(source_default.dim(` Scaffolding ${selectedIde} dotfolder...`));
5888
5884
  writeIdeScaffold(rootPath, {
5889
5885
  ide: selectedIde,
5890
5886
  footprint,
@@ -5896,25 +5892,34 @@ program2.command("init").description("Initialize a new TrellisVCS repository in
5896
5892
  });
5897
5893
  }
5898
5894
  }
5895
+ }
5896
+ 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) => {
5897
+ const rootPath = resolve(opts.path);
5898
+ if (TrellisVcsEngine.isRepo(rootPath)) {
5899
+ console.log(source_default.yellow("Already a Trellis workspace."));
5900
+ return;
5901
+ }
5902
+ await runInit(rootPath, {
5903
+ interactive: process.stdout.isTTY && !process.argv.includes("--no-interactive"),
5904
+ ides: opts.ides,
5905
+ framework: opts.framework,
5906
+ footprint: opts.footprint
5907
+ });
5899
5908
  console.log(source_default.green("\u2713 Initialized Trellis repository"));
5909
+ const engine = new TrellisVcsEngine({ rootPath });
5910
+ const opsCount = (await engine.log()).length;
5900
5911
  console.log(` ${source_default.dim("Path:")} ${rootPath}`);
5901
- console.log(` ${source_default.dim("Ops:")} ${result.opsCreated} initial operations scanned`);
5912
+ console.log(` ${source_default.dim("Ops:")} ${opsCount} initial operations scanned`);
5902
5913
  console.log(` ${source_default.dim("Config:")} .trellis/config.json`);
5903
5914
  console.log(` ${source_default.dim("Op log:")} .trellis/ops.json`);
5904
5915
  console.log(` ${source_default.dim("Graph DB:")} .trellis/graph.db`);
5905
5916
  console.log(` ${source_default.dim("Agent context:")} .trellis/agents/AGENTS.md`);
5906
- if (selectedIdes.length > 0) {
5907
- const ideFolders = selectedIdes.map((id) => {
5908
- const ide = id;
5909
- return ide === "cursor" ? ".cursor" : ide === "windsurf" ? ".windsurf" : ide === "claude" ? ".claude" : ide === "codex" ? ".codex" : ide === "gemini" ? ".gemini" : ".github/copilot";
5910
- });
5911
- console.log(` ${source_default.dim("IDE rules:")} ${ideFolders.join(", ")}`);
5912
- }
5913
- if (result.context.domain) {
5914
- console.log(` ${source_default.dim("Domain:")} ${source_default.cyan(result.context.domain)} ${source_default.dim("(inferred)")}`);
5917
+ const preInfer = await inferProjectContext(rootPath);
5918
+ if (preInfer.domain) {
5919
+ console.log(` ${source_default.dim("Domain:")} ${source_default.cyan(preInfer.domain)} ${source_default.dim("(inferred)")}`);
5915
5920
  }
5916
- if (result.context.ecosystem && result.context.ecosystem !== "unknown") {
5917
- console.log(` ${source_default.dim("Ecosystem:")} ${result.context.ecosystem}`);
5921
+ if (preInfer.ecosystem && preInfer.ecosystem !== "unknown") {
5922
+ console.log(` ${source_default.dim("Ecosystem:")} ${preInfer.ecosystem}`);
5918
5923
  }
5919
5924
  console.log();
5920
5925
  console.log(source_default.bold("Next steps:"));
@@ -5925,7 +5930,7 @@ program2.command("init").description("Initialize a new TrellisVCS repository in
5925
5930
  console.log(` ${source_default.cyan("trellis milestone")} Create narrative checkpoints`);
5926
5931
  console.log(` ${source_default.cyan("trellis garden")} Discover abandoned work`);
5927
5932
  console.log(` ${source_default.cyan("trellis issue")} Create and track issues`);
5928
- if (result.context.confidence !== "high") {
5933
+ if (preInfer.confidence !== "high") {
5929
5934
  console.log(` ${source_default.cyan("trellis season")} Enrich project context for agents`);
5930
5935
  }
5931
5936
  console.log();
@@ -8573,16 +8578,21 @@ program2.command("season").description("Enrich project context for agents \u2014
8573
8578
  }
8574
8579
  console.log();
8575
8580
  });
8576
- 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").action(async (opts) => {
8581
+ 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) => {
8577
8582
  const { resolve: resolve2, dirname: dirname6, join: join7 } = await import("path");
8578
8583
  const { existsSync: existsSync6 } = await import("fs");
8579
8584
  const { spawn } = await import("child_process");
8585
+ const { readFileSync: readFileSync4 } = await import("fs");
8586
+ const { createServer: createHttpServer } = await import("http");
8580
8587
  const rootPath = resolve2(opts.path);
8581
- if (!TrellisVcsEngine.isRepo(rootPath)) {
8582
- console.log(source_default.dim("Not a Trellis workspace \u2014 initializing\u2026"));
8583
- const engine = new TrellisVcsEngine({ rootPath });
8584
- await engine.initRepo();
8585
- console.log(source_default.green("\u2713 Initialized Trellis repository"));
8588
+ if (!opts.noInit) {
8589
+ if (!TrellisVcsEngine.isRepo(rootPath)) {
8590
+ console.log(source_default.dim("Not a Trellis workspace \u2014 initializing\u2026"));
8591
+ await runInit(rootPath, {
8592
+ interactive: false
8593
+ });
8594
+ console.log(source_default.green("\u2713 Initialized Trellis repository"));
8595
+ }
8586
8596
  }
8587
8597
  function findOpencode() {
8588
8598
  const here = dirname6(process.argv[1]);
@@ -8639,24 +8649,46 @@ program2.command("code").alias("ide").description('Launch OpenCode in "Harness"
8639
8649
  console.log(source_default.dim(` Web: ${mcpUrl}`));
8640
8650
  }
8641
8651
  console.log("");
8652
+ let mcpProcess = null;
8653
+ if (opts.web) {
8654
+ console.log(source_default.dim(` Starting MCP server on port ${mcpPort}\u2026`));
8655
+ mcpProcess = spawn("bun", ["run", mcp, "--quiet", "--path", rootPath, "--http", "--port", String(mcpPort)], {
8656
+ stdio: ["pipe", "pipe", "pipe"],
8657
+ detached: false
8658
+ });
8659
+ mcpProcess.stderr?.on("data", (data) => {
8660
+ const msg = data.toString();
8661
+ if (!msg.includes("TrellisVCS MCP Server")) {
8662
+ process.stderr.write(source_default.dim(` [mcp] ${msg}`));
8663
+ }
8664
+ });
8665
+ mcpProcess.on("error", (err) => {
8666
+ console.error(source_default.red(`
8667
+ \u2717 MCP server failed to start: ${err.message}`));
8668
+ process.exit(1);
8669
+ });
8670
+ const ready = await waitForMcpReady(mcpUrl, 1e4);
8671
+ if (!ready) {
8672
+ console.error(source_default.red(`
8673
+ \u2717 MCP server did not become ready in time`));
8674
+ if (mcpProcess) {
8675
+ mcpProcess.kill();
8676
+ }
8677
+ process.exit(1);
8678
+ }
8679
+ console.log(source_default.green(`\u2713 MCP server ready at ${mcpUrl}`));
8680
+ }
8642
8681
  const args = [rootPath];
8643
8682
  if (opts.model) {
8644
8683
  args.push("--model", opts.model);
8645
8684
  }
8646
8685
  const mcpServers = {
8647
8686
  mcpServers: {
8648
- "trellis-vcs": {
8687
+ "trellis-vcs": opts.web ? {
8688
+ url: `${mcpUrl}/sse`
8689
+ } : {
8649
8690
  command: "bun",
8650
- args: opts.web ? [
8651
- "run",
8652
- mcp,
8653
- "--quiet",
8654
- "--path",
8655
- rootPath,
8656
- "--http",
8657
- "--port",
8658
- String(mcpPort)
8659
- ] : ["run", mcp, "--quiet", "--path", rootPath]
8691
+ args: ["run", mcp, "--quiet", "--path", rootPath]
8660
8692
  }
8661
8693
  }
8662
8694
  };
@@ -8672,6 +8704,9 @@ program2.command("code").alias("ide").description('Launch OpenCode in "Harness"
8672
8704
  }
8673
8705
  });
8674
8706
  child.on("exit", (code) => {
8707
+ if (mcpProcess) {
8708
+ mcpProcess.kill();
8709
+ }
8675
8710
  if (code === 0) {
8676
8711
  console.log(source_default.green(`
8677
8712
  \u2713 Agentic session complete.`));
@@ -8682,4 +8717,22 @@ program2.command("code").alias("ide").description('Launch OpenCode in "Harness"
8682
8717
  process.exit(code ?? 0);
8683
8718
  });
8684
8719
  });
8720
+ async function waitForMcpReady(url, timeoutMs) {
8721
+ const { get } = await import("http");
8722
+ const start = Date.now();
8723
+ while (Date.now() - start < timeoutMs) {
8724
+ await new Promise((resolve2) => setTimeout(resolve2, 500));
8725
+ try {
8726
+ const res = await new Promise((resolve2, reject) => {
8727
+ const req = get(`${url}/health`, resolve2);
8728
+ req.on("error", reject);
8729
+ req.setTimeout(2000);
8730
+ });
8731
+ if (res.statusCode === 200) {
8732
+ return true;
8733
+ }
8734
+ } catch {}
8735
+ }
8736
+ return false;
8737
+ }
8685
8738
  program2.parse();
@@ -0,0 +1,65 @@
1
+ /**
2
+ * Trellis CMS Client
3
+ *
4
+ * Thin HTTP client for reading content collections from a Trellis-compatible store
5
+ * (currently opencode's /store/* routes). Reads only; writes happen through the
6
+ * IDE's CMS panel or the agent's `cms` tool.
7
+ *
8
+ * @example
9
+ * import { createCmsClient } from "trellis/cms";
10
+ *
11
+ * const cms = createCmsClient({ url: "http://localhost:4096" });
12
+ *
13
+ * // List published blog posts with author resolved
14
+ * const posts = await cms.collection("blog_post").list({
15
+ * status: "published",
16
+ * expand: ["author"],
17
+ * });
18
+ *
19
+ * // Subscribe to live updates (polling for now, SSE later)
20
+ * const off = cms.collection("blog_post").subscribe((entries) => {
21
+ * console.log(entries);
22
+ * });
23
+ *
24
+ * @module trellis/cms
25
+ */
26
+ import type { CmsClientOptions, Collection, Entry, EntrySubscriber, GetOptions, ListOptions, ListSubscriber, Unsubscribe } from './types.js';
27
+ export declare class CmsClient {
28
+ private readonly url;
29
+ private readonly basePath;
30
+ private readonly directory?;
31
+ readonly pollIntervalMs: number;
32
+ private readonly fetchFn;
33
+ private readonly apiKey?;
34
+ constructor(opts: CmsClientOptions);
35
+ collection<T extends Record<string, unknown> = Record<string, unknown>>(key: string): CollectionRef<T>;
36
+ entry<T extends Record<string, unknown> = Record<string, unknown>>(id: string): EntryRef<T>;
37
+ /** List all CMS collections (explicit + inferred). */
38
+ collections(): Promise<Collection[]>;
39
+ close(): void;
40
+ /** @internal */
41
+ _get<T>(path: string): Promise<T | undefined>;
42
+ /** @internal */
43
+ _entryById(id: string): Promise<Entry | null>;
44
+ }
45
+ export declare class CollectionRef<T extends Record<string, unknown> = Record<string, unknown>> {
46
+ private readonly client;
47
+ readonly key: string;
48
+ constructor(client: CmsClient, key: string);
49
+ list(opts?: ListOptions): Promise<Entry<T>[]>;
50
+ get(id: string, opts?: GetOptions): Promise<Entry<T> | null>;
51
+ /**
52
+ * Subscribe to changes. Currently implemented as polling; a future SSE-backed
53
+ * upgrade will replace the transport without changing this API.
54
+ */
55
+ subscribe(callback: ListSubscriber<T>, opts?: ListOptions): Unsubscribe;
56
+ }
57
+ export declare class EntryRef<T extends Record<string, unknown> = Record<string, unknown>> {
58
+ private readonly client;
59
+ readonly id: string;
60
+ constructor(client: CmsClient, id: string);
61
+ get(opts?: GetOptions): Promise<Entry<T> | null>;
62
+ subscribe(callback: EntrySubscriber<T>, opts?: GetOptions): Unsubscribe;
63
+ }
64
+ export declare function createCmsClient(opts: CmsClientOptions): CmsClient;
65
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/cms/client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,OAAO,KAAK,EACV,gBAAgB,EAChB,UAAU,EACV,KAAK,EACL,eAAe,EACf,UAAU,EACV,WAAW,EACX,cAAc,EACd,WAAW,EACZ,MAAM,YAAY,CAAC;AAiBpB,qBAAa,SAAS;IACpB,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAS;IAC7B,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAS;IACpC,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAe;IACvC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAS;gBAErB,IAAI,EAAE,gBAAgB;IASlC,UAAU,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACpE,GAAG,EAAE,MAAM,GACV,aAAa,CAAC,CAAC,CAAC;IAInB,KAAK,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,EAAE,EAAE,MAAM,GAAG,QAAQ,CAAC,CAAC,CAAC;IAI3F,sDAAsD;IAChD,WAAW,IAAI,OAAO,CAAC,UAAU,EAAE,CAAC;IAiD1C,KAAK,IAAI,IAAI;IAIb,gBAAgB;IACV,IAAI,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC;IAYnD,gBAAgB;IACV,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,GAAG,IAAI,CAAC;CASpD;AAED,qBAAa,aAAa,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAElF,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,QAAQ,CAAC,GAAG,EAAE,MAAM;gBADH,MAAM,EAAE,SAAS,EACzB,GAAG,EAAE,MAAM;IAGhB,IAAI,CAAC,IAAI,GAAE,WAAgB,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;IA+BjD,GAAG,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,GAAE,UAAe,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;IAYtE;;;OAGG;IACH,SAAS,CAAC,QAAQ,EAAE,cAAc,CAAC,CAAC,CAAC,EAAE,IAAI,GAAE,WAAgB,GAAG,WAAW;CAyB5E;AAED,qBAAa,QAAQ,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAE7E,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,QAAQ,CAAC,EAAE,EAAE,MAAM;gBADF,MAAM,EAAE,SAAS,EACzB,EAAE,EAAE,MAAM;IAGf,GAAG,CAAC,IAAI,GAAE,UAAe,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;IAY1D,SAAS,CAAC,QAAQ,EAAE,eAAe,CAAC,CAAC,CAAC,EAAE,IAAI,GAAE,UAAe,GAAG,WAAW;CAyB5E;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,gBAAgB,GAAG,SAAS,CAEjE"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Trellis CMS — client-side SDK for reading content collections.
3
+ *
4
+ * @module trellis/cms
5
+ */
6
+ export { CmsClient, CollectionRef, EntryRef, createCmsClient } from './client.js';
7
+ export type { CmsClientOptions, Collection, Entry, EntryStatus, EntrySubscriber, Framework, GetOptions, ListOptions, ListSubscriber, Unsubscribe, } from './types.js';
8
+ export { scaffoldConsumer, scaffoldFilename } from './scaffold.js';
9
+ export type { ScaffoldOptions } from './scaffold.js';
10
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cms/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,QAAQ,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAElF,YAAY,EACV,gBAAgB,EAChB,UAAU,EACV,KAAK,EACL,WAAW,EACX,eAAe,EACf,SAAS,EACT,UAAU,EACV,WAAW,EACX,cAAc,EACd,WAAW,GACZ,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACnE,YAAY,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC"}
@@ -0,0 +1,413 @@
1
+ // @bun
2
+ import"../index-a76rekgs.js";
3
+
4
+ // src/cms/internal.ts
5
+ var SYSTEM_TYPES = new Set([
6
+ "issue",
7
+ "agent",
8
+ "project",
9
+ "memory",
10
+ "mcp",
11
+ "sprite",
12
+ "workunit",
13
+ "cycle",
14
+ "epic",
15
+ "roadmap",
16
+ "suggestion",
17
+ "file",
18
+ "directory",
19
+ "op",
20
+ "branch",
21
+ "decision",
22
+ "session",
23
+ "typeschema",
24
+ "field"
25
+ ]);
26
+ function isSystemType(type) {
27
+ return SYSTEM_TYPES.has(type.trim().toLowerCase());
28
+ }
29
+ function typeKey(type) {
30
+ return type.trim().toLowerCase();
31
+ }
32
+ function entryFromFacts(entity, facts) {
33
+ const fields = {};
34
+ let status = "draft";
35
+ for (const f of facts) {
36
+ if (f.a === "type")
37
+ continue;
38
+ if (f.a === "cms_status") {
39
+ status = f.v === "published" ? "published" : "draft";
40
+ continue;
41
+ }
42
+ fields[f.a] = f.v;
43
+ }
44
+ return { id: entity.id, type: entity.type, status, fields };
45
+ }
46
+ function groupFactsByEntity(facts) {
47
+ const map = new Map;
48
+ for (const f of facts) {
49
+ const list = map.get(f.e);
50
+ if (list)
51
+ list.push(f);
52
+ else
53
+ map.set(f.e, [f]);
54
+ }
55
+ return map;
56
+ }
57
+ async function expandReferences(entries, expandKeys, fetchEntity) {
58
+ const ids = new Set;
59
+ for (const entry of entries) {
60
+ for (const key of expandKeys) {
61
+ const v = entry.fields[key];
62
+ if (typeof v === "string")
63
+ ids.add(v);
64
+ }
65
+ }
66
+ if (ids.size === 0)
67
+ return entries;
68
+ const resolved = new Map;
69
+ await Promise.all([...ids].map(async (id) => {
70
+ try {
71
+ resolved.set(id, await fetchEntity(id));
72
+ } catch {
73
+ resolved.set(id, null);
74
+ }
75
+ }));
76
+ return entries.map((entry) => {
77
+ const next = { ...entry.fields };
78
+ for (const key of expandKeys) {
79
+ const v = next[key];
80
+ if (typeof v === "string" && resolved.has(v)) {
81
+ next[key] = resolved.get(v);
82
+ }
83
+ }
84
+ return { ...entry, fields: next };
85
+ });
86
+ }
87
+ function fingerprint(value) {
88
+ return JSON.stringify(value);
89
+ }
90
+
91
+ // src/cms/client.ts
92
+ var DEFAULT_BASE_PATH = "/trellis/store";
93
+ var DEFAULT_POLL_MS = 2000;
94
+ var MIN_POLL_MS = 500;
95
+ var MAX_FACTS_PER_FETCH = 5000;
96
+
97
+ class CmsClient {
98
+ url;
99
+ basePath;
100
+ directory;
101
+ pollIntervalMs;
102
+ fetchFn;
103
+ apiKey;
104
+ constructor(opts) {
105
+ this.url = opts.url.replace(/\/+$/, "");
106
+ this.basePath = (opts.basePath ?? DEFAULT_BASE_PATH).replace(/\/+$/, "");
107
+ this.directory = opts.directory;
108
+ this.pollIntervalMs = Math.max(MIN_POLL_MS, opts.pollIntervalMs ?? DEFAULT_POLL_MS);
109
+ this.fetchFn = opts.fetch ?? globalThis.fetch.bind(globalThis);
110
+ this.apiKey = opts.apiKey;
111
+ }
112
+ collection(key) {
113
+ return new CollectionRef(this, key);
114
+ }
115
+ entry(id) {
116
+ return new EntryRef(this, id);
117
+ }
118
+ async collections() {
119
+ const [entities, facts] = await Promise.all([
120
+ this._get("/entities?limit=10000"),
121
+ this._get(`/facts?limit=${MAX_FACTS_PER_FETCH}`)
122
+ ]);
123
+ const factsByEntity = groupFactsByEntity(facts ?? []);
124
+ const counts = new Map;
125
+ const canonical = new Map;
126
+ for (const e of entities ?? []) {
127
+ const k = typeKey(e.type);
128
+ counts.set(k, (counts.get(k) ?? 0) + 1);
129
+ if (!canonical.has(k))
130
+ canonical.set(k, e.type);
131
+ }
132
+ const out = new Map;
133
+ for (const e of entities ?? []) {
134
+ if (e.type !== "TypeSchema")
135
+ continue;
136
+ const efacts = factsByEntity.get(e.id) ?? [];
137
+ const isCms = efacts.some((f) => f.a === "cms" && f.v === true);
138
+ if (!isCms)
139
+ continue;
140
+ const name = e.id.replace(/^schema:/, "");
141
+ const k = typeKey(name);
142
+ const labelFact = efacts.find((f) => f.a === "label");
143
+ const label = typeof labelFact?.v === "string" ? labelFact.v : name;
144
+ out.set(k, {
145
+ key: k,
146
+ label,
147
+ inferred: false,
148
+ count: counts.get(k) ?? 0
149
+ });
150
+ }
151
+ for (const [k, count] of counts) {
152
+ if (isSystemType(k) || out.has(k))
153
+ continue;
154
+ out.set(k, {
155
+ key: k,
156
+ label: canonical.get(k) ?? k,
157
+ inferred: true,
158
+ count
159
+ });
160
+ }
161
+ return [...out.values()].sort((a, b) => a.label.localeCompare(b.label));
162
+ }
163
+ close() {}
164
+ async _get(path) {
165
+ const u = new URL(`${this.basePath}${path}`, this.url);
166
+ if (this.directory && !u.searchParams.has("directory")) {
167
+ u.searchParams.set("directory", this.directory);
168
+ }
169
+ const res = await this.fetchFn(u.toString(), {
170
+ headers: this.apiKey ? { Authorization: `Bearer ${this.apiKey}` } : {}
171
+ });
172
+ if (!res.ok)
173
+ return;
174
+ return await res.json();
175
+ }
176
+ async _entryById(id) {
177
+ const detail = await this._get(`/entity/${encodeURIComponent(id)}`);
178
+ if (!detail)
179
+ return null;
180
+ const typeFact = detail.facts.find((f) => f.a === "type");
181
+ const type = typeof typeFact?.v === "string" ? typeFact.v : "unknown";
182
+ return entryFromFacts({ id: detail.id, type }, detail.facts);
183
+ }
184
+ }
185
+
186
+ class CollectionRef {
187
+ client;
188
+ key;
189
+ constructor(client, key) {
190
+ this.client = client;
191
+ this.key = key;
192
+ }
193
+ async list(opts = {}) {
194
+ const status = opts.status ?? "published";
195
+ const limit = opts.limit ?? 100;
196
+ const [entities, facts] = await Promise.all([
197
+ this.client._get(`/entities?type=${encodeURIComponent(this.key)}&limit=${limit}`),
198
+ this.client._get(`/facts?limit=${MAX_FACTS_PER_FETCH}`)
199
+ ]);
200
+ if (!entities || entities.length === 0)
201
+ return [];
202
+ const factsByEntity = groupFactsByEntity(facts ?? []);
203
+ let entries = entities.map((e) => entryFromFacts(e, factsByEntity.get(e.id) ?? []));
204
+ if (status !== "all") {
205
+ entries = entries.filter((e) => e.status === status);
206
+ }
207
+ if (opts.expand && opts.expand.length > 0) {
208
+ entries = await expandReferences(entries, opts.expand, (id) => this.client._entryById(id));
209
+ }
210
+ return entries;
211
+ }
212
+ async get(id, opts = {}) {
213
+ const entry = await this.client._entryById(id);
214
+ if (!entry)
215
+ return null;
216
+ if (opts.expand && opts.expand.length > 0) {
217
+ const [expanded] = await expandReferences([entry], opts.expand, (eid) => this.client._entryById(eid));
218
+ return expanded;
219
+ }
220
+ return entry;
221
+ }
222
+ subscribe(callback, opts = {}) {
223
+ let stopped = false;
224
+ let last = "";
225
+ const tick = async () => {
226
+ if (stopped)
227
+ return;
228
+ try {
229
+ const entries = await this.list(opts);
230
+ const sig = fingerprint(entries);
231
+ if (sig !== last) {
232
+ last = sig;
233
+ callback(entries);
234
+ }
235
+ } catch {}
236
+ };
237
+ tick();
238
+ const interval = setInterval(tick, this.client.pollIntervalMs);
239
+ return () => {
240
+ stopped = true;
241
+ clearInterval(interval);
242
+ };
243
+ }
244
+ }
245
+
246
+ class EntryRef {
247
+ client;
248
+ id;
249
+ constructor(client, id) {
250
+ this.client = client;
251
+ this.id = id;
252
+ }
253
+ async get(opts = {}) {
254
+ const entry = await this.client._entryById(this.id);
255
+ if (!entry)
256
+ return null;
257
+ if (opts.expand && opts.expand.length > 0) {
258
+ const [expanded] = await expandReferences([entry], opts.expand, (eid) => this.client._entryById(eid));
259
+ return expanded;
260
+ }
261
+ return entry;
262
+ }
263
+ subscribe(callback, opts = {}) {
264
+ let stopped = false;
265
+ let last = "";
266
+ const tick = async () => {
267
+ if (stopped)
268
+ return;
269
+ try {
270
+ const entry = await this.get(opts);
271
+ const sig = fingerprint(entry);
272
+ if (sig !== last) {
273
+ last = sig;
274
+ callback(entry);
275
+ }
276
+ } catch {}
277
+ };
278
+ tick();
279
+ const interval = setInterval(tick, this.client.pollIntervalMs);
280
+ return () => {
281
+ stopped = true;
282
+ clearInterval(interval);
283
+ };
284
+ }
285
+ }
286
+ function createCmsClient(opts) {
287
+ return new CmsClient(opts);
288
+ }
289
+ // src/cms/scaffold.ts
290
+ var DEFAULT_URL = "http://localhost:4096";
291
+ function expandLiteral(expand) {
292
+ if (!expand || expand.length === 0)
293
+ return "";
294
+ return ` expand: ${JSON.stringify(expand)},`;
295
+ }
296
+ function vanilla(opts) {
297
+ const url = opts.url ?? DEFAULT_URL;
298
+ const exp = expandLiteral(opts.expand);
299
+ return `import { createCmsClient } from "trellis/cms";
300
+
301
+ const cms = createCmsClient({ url: "${url}" });
302
+
303
+ const collection = cms.collection("${opts.collection}");
304
+
305
+ // One-shot fetch (defaults to status: "published")
306
+ const entries = await collection.list({${exp}});
307
+ console.log(entries);
308
+
309
+ // Live updates \u2014 re-fires whenever the collection changes
310
+ const off = collection.subscribe(
311
+ (entries) => {
312
+ console.log("Updated:", entries);
313
+ },
314
+ {${exp}},
315
+ );
316
+ // off(); // call to stop receiving updates
317
+ `;
318
+ }
319
+ function react(opts) {
320
+ const url = opts.url ?? DEFAULT_URL;
321
+ const exp = expandLiteral(opts.expand);
322
+ return `import { useEffect, useState } from "react";
323
+ import { createCmsClient, type Entry } from "trellis/cms";
324
+
325
+ const cms = createCmsClient({ url: "${url}" });
326
+
327
+ export function use${pascal(opts.collection)}() {
328
+ const [entries, setEntries] = useState<Entry[]>([]);
329
+ useEffect(() => {
330
+ const off = cms.collection("${opts.collection}").subscribe(setEntries, {${exp}});
331
+ return off;
332
+ }, []);
333
+ return entries;
334
+ }
335
+
336
+ // Usage:
337
+ // const posts = use${pascal(opts.collection)}();
338
+ // return posts.map(p => <article key={p.id}>...</article>);
339
+ `;
340
+ }
341
+ function solid(opts) {
342
+ const url = opts.url ?? DEFAULT_URL;
343
+ const exp = expandLiteral(opts.expand);
344
+ return `import { createSignal, onCleanup } from "solid-js";
345
+ import { createCmsClient, type Entry } from "trellis/cms";
346
+
347
+ const cms = createCmsClient({ url: "${url}" });
348
+
349
+ export function create${pascal(opts.collection)}() {
350
+ const [entries, setEntries] = createSignal<Entry[]>([]);
351
+ const off = cms.collection("${opts.collection}").subscribe(setEntries, {${exp}});
352
+ onCleanup(off);
353
+ return entries;
354
+ }
355
+
356
+ // Usage in a component:
357
+ // const posts = create${pascal(opts.collection)}();
358
+ // return <For each={posts()}>{(p) => <article>...</article>}</For>;
359
+ `;
360
+ }
361
+ function vue(opts) {
362
+ const url = opts.url ?? DEFAULT_URL;
363
+ const exp = expandLiteral(opts.expand);
364
+ return `import { ref, onUnmounted } from "vue";
365
+ import { createCmsClient, type Entry } from "trellis/cms";
366
+
367
+ const cms = createCmsClient({ url: "${url}" });
368
+
369
+ export function use${pascal(opts.collection)}() {
370
+ const entries = ref<Entry[]>([]);
371
+ const off = cms.collection("${opts.collection}").subscribe((next) => {
372
+ entries.value = next;
373
+ }, {${exp}});
374
+ onUnmounted(off);
375
+ return entries;
376
+ }
377
+ `;
378
+ }
379
+ function pascal(s) {
380
+ return s.split(/[_\-\s]+/).filter(Boolean).map((w) => w[0].toUpperCase() + w.slice(1)).join("");
381
+ }
382
+ function scaffoldConsumer(opts) {
383
+ switch (opts.framework ?? "vanilla") {
384
+ case "react":
385
+ return react(opts);
386
+ case "solid":
387
+ return solid(opts);
388
+ case "vue":
389
+ return vue(opts);
390
+ default:
391
+ return vanilla(opts);
392
+ }
393
+ }
394
+ function scaffoldFilename(opts) {
395
+ const base = `cms-${opts.collection.replace(/[^a-z0-9]+/gi, "-").toLowerCase()}`;
396
+ switch (opts.framework ?? "vanilla") {
397
+ case "react":
398
+ case "solid":
399
+ return `${base}.ts`;
400
+ case "vue":
401
+ return `${base}.ts`;
402
+ default:
403
+ return `${base}.js`;
404
+ }
405
+ }
406
+ export {
407
+ scaffoldFilename,
408
+ scaffoldConsumer,
409
+ createCmsClient,
410
+ EntryRef,
411
+ CollectionRef,
412
+ CmsClient
413
+ };
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Trellis CMS — internal helpers (fact join, status detection, reference expansion).
3
+ */
4
+ import type { Entry } from './types.js';
5
+ export type RawFact = {
6
+ e: string;
7
+ a: string;
8
+ v: string | number | boolean;
9
+ };
10
+ export type RawEntity = {
11
+ id: string;
12
+ type: string;
13
+ };
14
+ export declare function isSystemType(type: string): boolean;
15
+ export declare function typeKey(type: string): string;
16
+ export declare function entryFromFacts(entity: RawEntity, facts: RawFact[]): Entry;
17
+ export declare function groupFactsByEntity(facts: RawFact[]): Map<string, RawFact[]>;
18
+ /**
19
+ * For each entry, replace string ids in `expand` field keys with the resolved Entry.
20
+ * Lookups are batched in parallel.
21
+ */
22
+ export declare function expandReferences(entries: Entry[], expandKeys: string[], fetchEntity: (id: string) => Promise<Entry | null>): Promise<Entry[]>;
23
+ /**
24
+ * Cheap deep-equality fingerprint for change detection in subscriptions.
25
+ */
26
+ export declare function fingerprint(value: unknown): string;
27
+ //# sourceMappingURL=internal.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"internal.d.ts","sourceRoot":"","sources":["../../src/cms/internal.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,KAAK,EAAe,MAAM,YAAY,CAAC;AAErD,MAAM,MAAM,OAAO,GAAG;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAA;CAAE,CAAC;AAC7E,MAAM,MAAM,SAAS,GAAG;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAwBrD,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAElD;AAED,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE5C;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,KAAK,CAYzE;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,OAAO,EAAE,GAAG,GAAG,CAAC,MAAM,EAAE,OAAO,EAAE,CAAC,CAQ3E;AAED;;;GAGG;AACH,wBAAsB,gBAAgB,CACpC,OAAO,EAAE,KAAK,EAAE,EAChB,UAAU,EAAE,MAAM,EAAE,EACpB,WAAW,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,KAAK,GAAG,IAAI,CAAC,GACjD,OAAO,CAAC,KAAK,EAAE,CAAC,CA+BlB;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAElD"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Scaffold generators for CMS consumer code.
3
+ *
4
+ * Used by the agent's `cms` tool (scaffold_consumer action) to write
5
+ * starter integration files instead of generating static data dumps.
6
+ */
7
+ import type { Framework } from './types.js';
8
+ export type ScaffoldOptions = {
9
+ collection: string;
10
+ framework?: Framework;
11
+ /** Field keys to expand as references (e.g. ["author"]). */
12
+ expand?: string[];
13
+ /** Override the CMS server URL. Default: http://localhost:4096 */
14
+ url?: string;
15
+ };
16
+ export declare function scaffoldConsumer(opts: ScaffoldOptions): string;
17
+ export declare function scaffoldFilename(opts: ScaffoldOptions): string;
18
+ //# sourceMappingURL=scaffold.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scaffold.d.ts","sourceRoot":"","sources":["../../src/cms/scaffold.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAE5C,MAAM,MAAM,eAAe,GAAG;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,4DAA4D;IAC5D,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,kEAAkE;IAClE,GAAG,CAAC,EAAE,MAAM,CAAC;CACd,CAAC;AAwGF,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,eAAe,GAAG,MAAM,CAW9D;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,eAAe,GAAG,MAAM,CAW9D"}
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Trellis CMS Client — public types
3
+ *
4
+ * @module trellis/cms
5
+ */
6
+ export type EntryStatus = 'draft' | 'published';
7
+ export type Entry<T extends Record<string, unknown> = Record<string, unknown>> = {
8
+ /** Stable entity id (e.g. "blog_post:abc123" or "BlogPost:abc"). */
9
+ id: string;
10
+ /** Original type string as stored on the entity (e.g. "BlogPost"). */
11
+ type: string;
12
+ /** "draft" or "published". Missing cms_status fact is treated as "draft". */
13
+ status: EntryStatus;
14
+ /** All other facts as a flat field bag. Reference fields hold entity ids until expanded. */
15
+ fields: T;
16
+ };
17
+ export type ListOptions = {
18
+ /** Filter by status. Default: "published". Use "all" to include drafts. */
19
+ status?: 'all' | EntryStatus;
20
+ /** Field keys to resolve as references — string ids become nested Entry objects. */
21
+ expand?: string[];
22
+ /** Max results. Default: 100. */
23
+ limit?: number;
24
+ };
25
+ export type GetOptions = {
26
+ expand?: string[];
27
+ };
28
+ export type Unsubscribe = () => void;
29
+ export type ListSubscriber<T extends Record<string, unknown> = Record<string, unknown>> = (entries: Entry<T>[]) => void;
30
+ export type EntrySubscriber<T extends Record<string, unknown> = Record<string, unknown>> = (entry: Entry<T> | null) => void;
31
+ export type Collection = {
32
+ /** Normalized lowercase key (e.g. "blog_post"). */
33
+ key: string;
34
+ /** Human-readable label. */
35
+ label: string;
36
+ /** True if no explicit TypeSchema exists — schema is inferred from existing entries. */
37
+ inferred: boolean;
38
+ /** Total entry count (all statuses). */
39
+ count: number;
40
+ };
41
+ export type CmsClientOptions = {
42
+ /** Base URL of the Trellis-compatible HTTP server (e.g. opencode at "http://localhost:4096"). */
43
+ url: string;
44
+ /**
45
+ * Path prefix for store routes. Default: "/trellis/store" (matches opencode).
46
+ * Override only if pointing at a different server layout.
47
+ */
48
+ basePath?: string;
49
+ /**
50
+ * Project directory for multi-instance backends (opencode requires this).
51
+ * If omitted, the request goes to the default instance.
52
+ */
53
+ directory?: string;
54
+ /** Polling interval in ms for subscribe(). Minimum 500ms. Default: 2000. */
55
+ pollIntervalMs?: number;
56
+ /** Custom fetch implementation (for SSR / Node environments without global fetch). */
57
+ fetch?: typeof fetch;
58
+ /** Optional bearer token for authenticated routes. */
59
+ apiKey?: string;
60
+ };
61
+ export type Framework = 'vanilla' | 'react' | 'solid' | 'vue';
62
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/cms/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,MAAM,MAAM,WAAW,GAAG,OAAO,GAAG,WAAW,CAAC;AAEhD,MAAM,MAAM,KAAK,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,IAAI;IAC/E,oEAAoE;IACpE,EAAE,EAAE,MAAM,CAAC;IACX,sEAAsE;IACtE,IAAI,EAAE,MAAM,CAAC;IACb,6EAA6E;IAC7E,MAAM,EAAE,WAAW,CAAC;IACpB,4FAA4F;IAC5F,MAAM,EAAE,CAAC,CAAC;CACX,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG;IACxB,2EAA2E;IAC3E,MAAM,CAAC,EAAE,KAAK,GAAG,WAAW,CAAC;IAC7B,oFAAoF;IACpF,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,iCAAiC;IACjC,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG;IACvB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG,MAAM,IAAI,CAAC;AACrC,MAAM,MAAM,cAAc,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,IAAI,CACxF,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC,EAAE,KAChB,IAAI,CAAC;AACV,MAAM,MAAM,eAAe,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,IAAI,CACzF,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,GAAG,IAAI,KACnB,IAAI,CAAC;AAEV,MAAM,MAAM,UAAU,GAAG;IACvB,mDAAmD;IACnD,GAAG,EAAE,MAAM,CAAC;IACZ,4BAA4B;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,wFAAwF;IACxF,QAAQ,EAAE,OAAO,CAAC;IAClB,wCAAwC;IACxC,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,iGAAiG;IACjG,GAAG,EAAE,MAAM,CAAC;IACZ;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,4EAA4E;IAC5E,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,sFAAsF;IACtF,KAAK,CAAC,EAAE,OAAO,KAAK,CAAC;IACrB,sDAAsD;IACtD,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,MAAM,MAAM,SAAS,GAAG,SAAS,GAAG,OAAO,GAAG,OAAO,GAAG,KAAK,CAAC"}
package/dist/db/index.js CHANGED
@@ -1,14 +1,5 @@
1
1
  // @bun
2
2
  import"../index-c9h37r6h.js";
3
- import {
4
- importFile,
5
- importRecords
6
- } from "../index-skhn0agf.js";
7
- import {
8
- deploy
9
- } from "../index-wt8rz4gn.js";
10
- import"../index-bmyt7k8n.js";
11
- import"../index-y6a4kj0p.js";
12
3
  import {
13
4
  ADMIN_ONLY,
14
5
  ANONYMOUS,
@@ -27,6 +18,15 @@ import {
27
18
  startServer,
28
19
  verifyJwt
29
20
  } from "../index-6n5dcebj.js";
21
+ import {
22
+ importFile,
23
+ importRecords
24
+ } from "../index-skhn0agf.js";
25
+ import {
26
+ deploy
27
+ } from "../index-wt8rz4gn.js";
28
+ import"../index-bmyt7k8n.js";
29
+ import"../index-y6a4kj0p.js";
30
30
  import"../index-n9f2qyh5.js";
31
31
  import"../index-k5b0xskw.js";
32
32
  import {
@@ -1,5 +1,23 @@
1
1
  // @bun
2
2
  import"../index-c9h37r6h.js";
3
+ import {
4
+ ADMIN_ONLY,
5
+ ANONYMOUS,
6
+ FULLY_PUBLIC,
7
+ GITHUB_PROVIDER,
8
+ GOOGLE_PROVIDER,
9
+ OWNER_ONLY,
10
+ PUBLIC_READ,
11
+ PermissionError,
12
+ PermissionRegistry,
13
+ SubscriptionManager,
14
+ buildOAuthUrl,
15
+ exchangeOAuthCode,
16
+ resolveAuth,
17
+ signJwt,
18
+ startServer,
19
+ verifyJwt
20
+ } from "../index-6n5dcebj.js";
3
21
  import {
4
22
  importFile,
5
23
  importRecords
@@ -24,24 +42,6 @@ import {
24
42
  runSpriteCopy,
25
43
  runSpriteInteractive
26
44
  } from "../index-y6a4kj0p.js";
27
- import {
28
- ADMIN_ONLY,
29
- ANONYMOUS,
30
- FULLY_PUBLIC,
31
- GITHUB_PROVIDER,
32
- GOOGLE_PROVIDER,
33
- OWNER_ONLY,
34
- PUBLIC_READ,
35
- PermissionError,
36
- PermissionRegistry,
37
- SubscriptionManager,
38
- buildOAuthUrl,
39
- exchangeOAuthCode,
40
- resolveAuth,
41
- signJwt,
42
- startServer,
43
- verifyJwt
44
- } from "../index-6n5dcebj.js";
45
45
  import"../index-n9f2qyh5.js";
46
46
  import"../index-xzym9w0m.js";
47
47
  import {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "trellis",
3
- "version": "3.0.2",
3
+ "version": "3.1.1",
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",
@@ -59,6 +59,11 @@
59
59
  "bun": "./dist/links/index.js",
60
60
  "import": "./dist/links/index.js"
61
61
  },
62
+ "./cms": {
63
+ "types": "./dist/cms/index.d.ts",
64
+ "bun": "./dist/cms/index.js",
65
+ "import": "./dist/cms/index.js"
66
+ },
62
67
  "./decisions": {
63
68
  "types": "./dist/decisions/index.d.ts",
64
69
  "bun": "./dist/decisions/index.js",
@@ -101,7 +106,7 @@
101
106
  "cli": "bun run src/cli/index.ts",
102
107
  "mcp": "bun run src/mcp/index.ts",
103
108
  "mcp:docs": "bun run src/mcp/docs.ts",
104
- "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 --outdir dist --target bun --splitting --format esm --root src --external @xenova/transformers --external @huggingface/transformers --external react && 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",
109
+ "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 --outdir dist --target bun --splitting --format esm --root src --external @xenova/transformers --external @huggingface/transformers --external react && 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",
105
110
  "build:inspector": "vite build --config vite.inspector.config.ts",
106
111
  "test": "bun test test/core test/vcs test/git test/p2 test/p3 test/p4 test/p5 test/p6 test/p7 test/engine.test.ts",
107
112
  "test:all": "bun test",