@nyte-ai/host 0.0.24 → 0.0.25-nightly.20261025.2

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.
Files changed (34) hide show
  1. package/THIRD-PARTY-NOTICES.md +13 -5
  2. package/dist/chunks/{canvas-preview-gh7c9831.js → canvas-preview-27v3zkz4.js} +6 -2
  3. package/dist/chunks/{canvas-preview-w0mr5bkn.js → canvas-preview-3kmevzjy.js} +11 -8
  4. package/dist/chunks/{canvas-preview-9e15hp6q.js → canvas-preview-4rstzyd7.js} +4 -3
  5. package/dist/chunks/{canvas-preview-d97wvref.js → canvas-preview-75bmwhsr.js} +1 -1
  6. package/dist/chunks/{canvas-preview-e3vnkkc5.js → canvas-preview-dr46h8mm.js} +2 -1
  7. package/dist/chunks/{canvas-preview-vaatfznm.js → canvas-preview-ez9zdfq4.js} +16 -7
  8. package/dist/chunks/{canvas-preview-whfdhma6.js → canvas-preview-sdvgxnpf.js} +1 -1
  9. package/dist/chunks/{canvas-preview-gm0frf4b.js → canvas-preview-tq7ny7fm.js} +15 -0
  10. package/dist/chunks/canvas-preview-z2xdr07w.js +256 -0
  11. package/dist/src/index.js +9 -9
  12. package/dist/src/listen-fetch.d.ts +1 -1
  13. package/dist/src/listen-fetch.js +3 -3
  14. package/dist/src/listen.js +4 -4
  15. package/dist/src/local/provider.d.ts +1 -1
  16. package/dist/src/local/provider.js +2 -2
  17. package/dist/src/local/worktrees.d.ts +7 -3
  18. package/dist/src/local/worktrees.js +1 -2
  19. package/dist/src/mcp/hub.js +2 -2
  20. package/dist/src/mcp/tools.d.ts +9 -2
  21. package/dist/src/mcp/tools.js +1 -1
  22. package/dist/src/open.js +9 -9
  23. package/dist/src/serve/binding.d.ts +0 -1
  24. package/dist/src/serve/binding.js +1 -1
  25. package/dist/src/serve/handler.js +2 -2
  26. package/package.json +7 -8
  27. package/src/listen-fetch.ts +6 -3
  28. package/src/local/provider.ts +3 -1
  29. package/src/local/worktrees.ts +210 -30
  30. package/src/mcp/hub.ts +5 -1
  31. package/src/mcp/tools.ts +24 -8
  32. package/src/open.ts +6 -2
  33. package/src/serve/binding.ts +19 -1
  34. package/dist/chunks/canvas-preview-2jwbe14k.js +0 -130
@@ -55,6 +55,11 @@ SOFTWARE.
55
55
  `@earendil-works/pi-codemode` 1.0.0 supply the MCP client and QuickJS
56
56
  executor. Tool discovery and code-mode integration are adapted from commit
57
57
  `86dfceec402ad77e563bf4feab5f26c42d5f5db6`.
58
+ - BM25 tool search (`tool_search`, `searchTools()`), tool namespaces with
59
+ `describeNamespace()`, the budgeted codemode listing and the one-line MCP
60
+ server summary (`packages/plugin/src/tool-search.ts`,
61
+ `packages/plugin/src/codemode.ts`, `packages/core/src/builtins/mcp.ts`) are
62
+ ported from commit `d5b3de22d0e24a3e9c38a9410d3329b099717283`.
58
63
  - Transcript reconciliation, checkpoint instruction snapshots, provider system
59
64
  updates, and cache warming are ported from the 1.0.0 release commit
60
65
  `a13d35a742c6ef8462812a28fbe1d8c8b7431c32`.
@@ -312,11 +317,14 @@ Copyright 2025 OpenAI
312
317
  front of the queue (`packages/tui/src/model/interactions.ts`) is its
313
318
  `onEmptySubmit`. The rename
314
319
  plugin's context construction, prompt, model fallback, and output cleanup
315
- (`packages/plugin/src/rename.ts`) port OpenCode v2. The web-search
316
- plugin set (`packages/plugin/src/web-search.ts`) ports its Exa, Firecrawl,
317
- Parallel, and Tavily adapters, its stateless MCP `tools/call` transport and
318
- response bounds, its result and error vocabulary, and its arrangement of one
319
- plugin per provider from OpenCode v2.
320
+ (`packages/plugin/src/rename.ts`) port OpenCode v2. The web plugin
321
+ (`packages/plugin/src/web/`) ports OpenCode v2's web search and fetch tools:
322
+ the search provider registry any plugin can add to (`searchProviders` in
323
+ `packages/core/src/contract/plugin.ts`), its Exa, Firecrawl, Parallel, and
324
+ Tavily adapters, their stateless MCP `tools/call` transport and response
325
+ bounds, the result and error vocabulary, `web_fetch`'s Accept headers,
326
+ Cloudflare-challenge retry and size limits, and the streaming HTML-to-Markdown
327
+ and HTML-to-text converters (`packages/plugin/src/web/html-markdown.ts`).
320
328
  - Core's ripgrep resolver and search adapter (`packages/core/src/ripgrep.ts`,
321
329
  `packages/core/src/ripgrep/binary.ts`) and filename search
322
330
  (`packages/core/src/file-search.ts`) adapt OpenCode v2's core implementation.
@@ -21,7 +21,7 @@ import {
21
21
  checkToolSchemas2,
22
22
  TracingMcpClient2,
23
23
  bridgeTools2
24
- } from "./canvas-preview-vaatfznm.js";
24
+ } from "./canvas-preview-ez9zdfq4.js";
25
25
 
26
26
  // src/mcp/hub.ts
27
27
  import { join } from "node:path";
@@ -206,7 +206,11 @@ function hub(input) {
206
206
  state: () => state,
207
207
  brief: () => ({
208
208
  name: entry.name,
209
+ description: link.entry.description,
209
210
  instructions: listed?.instructions ?? null,
211
+ exposures: [
212
+ ...new Set([link.entry.exposure, ...link.entry.toolExposure.map((pair) => pair[1])])
213
+ ],
210
214
  unavailable: listed === null && (state.kind === "failed" || state.kind === "needs-sign-in") ? state.kind : null
211
215
  }),
212
216
  challenge: () => challenge,
@@ -217,7 +221,7 @@ function hub(input) {
217
221
  bridged = {
218
222
  entry: link.entry,
219
223
  listed,
220
- tools: bridgeTools2(link.entry, listed.tools, call)
224
+ tools: bridgeTools2(link.entry, listed, call)
221
225
  };
222
226
  return bridged.tools;
223
227
  },
@@ -12,7 +12,7 @@ import {
12
12
  } from "./canvas-preview-8xc0vn7v.js";
13
13
  import {
14
14
  localProvider2
15
- } from "./canvas-preview-e3vnkkc5.js";
15
+ } from "./canvas-preview-dr46h8mm.js";
16
16
  import {
17
17
  importLegacyTrust2
18
18
  } from "./canvas-preview-9aqczacs.js";
@@ -21,13 +21,13 @@ import {
21
21
  } from "./canvas-preview-4e0pxa32.js";
22
22
  import {
23
23
  listen2
24
- } from "./canvas-preview-whfdhma6.js";
24
+ } from "./canvas-preview-sdvgxnpf.js";
25
25
  import {
26
26
  nodeImages2
27
27
  } from "./canvas-preview-y5y6ghaw.js";
28
28
  import {
29
29
  createMcpHub2
30
- } from "./canvas-preview-gh7c9831.js";
30
+ } from "./canvas-preview-27v3zkz4.js";
31
31
  import {
32
32
  loadPluginDirectory2,
33
33
  pluginLoader2
@@ -65,12 +65,14 @@ import openaiAstraContext from "@nyte-ai/plugin/core/openai-astra-context";
65
65
  import { openaiCompaction } from "@nyte-ai/plugin/core/openai-compaction";
66
66
  import question from "@nyte-ai/plugin/core/question";
67
67
  import rename from "@nyte-ai/plugin/core/rename";
68
+ import subagents from "@nyte-ai/plugin/core/subagents";
68
69
  import telemetry from "@nyte-ai/plugin/core/telemetry";
69
- import webSearch from "@nyte-ai/plugin/core/web-search";
70
+ import toolSearch from "@nyte-ai/plugin/core/tool-search";
71
+ import web from "@nyte-ai/plugin/core/web";
70
72
  // package.json
71
73
  var package_default = {
72
74
  name: "@nyte-ai/host",
73
- version: "0.0.24",
75
+ version: "0.0.25-nightly.20261025.2",
74
76
  private: false,
75
77
  license: "MIT",
76
78
  repository: {
@@ -175,7 +177,7 @@ var package_default = {
175
177
  },
176
178
  scripts: {
177
179
  clean: "rm -rf node_modules .turbo",
178
- typecheck: "tsrs --noEmit",
180
+ typecheck: "tsc --noEmit",
179
181
  test: "vitest --run",
180
182
  "check:store": "node scripts/check-store.ts",
181
183
  "build:package": "bun ../../scripts/sdk-packages.mjs build host"
@@ -200,7 +202,6 @@ var package_default = {
200
202
  "@types/node": "^26.6.5",
201
203
  "@types/pg": "^8.23.1",
202
204
  "fast-check": "^4.10.2",
203
- typescript: "6.0.3",
204
205
  vitest: "^5.0.5"
205
206
  },
206
207
  engines: {
@@ -252,9 +253,11 @@ async function composeOpen(options, input) {
252
253
  approvals,
253
254
  question,
254
255
  rename,
256
+ subagents,
255
257
  bashDescription,
256
- webSearch,
258
+ web,
257
259
  codemode,
260
+ toolSearch,
258
261
  openaiCompaction(models),
259
262
  openaiAstraContext,
260
263
  telemetry,
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  createHandler2
3
- } from "./canvas-preview-d97wvref.js";
3
+ } from "./canvas-preview-75bmwhsr.js";
4
4
 
5
5
  // src/listen-fetch.ts
6
6
  import { SdkError } from "@nyte-ai/schema";
@@ -104,6 +104,7 @@ async function listenFetch2(host, options) {
104
104
  };
105
105
  const unwatchCommits = server.onExternalCommit(externalCommit);
106
106
  const isListenerToken = auth.kind === "token" ? (token) => sameSecret(token, auth.token) : async () => false;
107
+ const listenerAuthority = auth.kind === "token" ? auth.authority ?? TOKEN_AUTHORITY : TOKEN_AUTHORITY;
107
108
  const fallback = auth.kind === "custom" ? async (request) => {
108
109
  const verdict = await auth.authorize(request);
109
110
  if ("deny" in verdict)
@@ -129,8 +130,8 @@ async function listenFetch2(host, options) {
129
130
  if (token !== undefined) {
130
131
  if (await isListenerToken(token))
131
132
  return {
132
- connection: connectionFor(flight, TOKEN, TOKEN_AUTHORITY, token),
133
- authority: TOKEN_AUTHORITY
133
+ connection: connectionFor(flight, TOKEN, listenerAuthority, token),
134
+ authority: listenerAuthority
134
135
  };
135
136
  const remembered = await findDevice(token);
136
137
  if (remembered !== null) {
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  bindConnection2
3
- } from "./canvas-preview-gm0frf4b.js";
3
+ } from "./canvas-preview-tq7ny7fm.js";
4
4
  import {
5
5
  createStreamChannels2
6
6
  } from "./canvas-preview-rp4x6y1h.js";
@@ -21,7 +21,7 @@ import {
21
21
  } from "./canvas-preview-crmzxxt8.js";
22
22
  import {
23
23
  localWorktrees2
24
- } from "./canvas-preview-2jwbe14k.js";
24
+ } from "./canvas-preview-z2xdr07w.js";
25
25
 
26
26
  // src/local/provider.ts
27
27
  import { constants, realpathSync } from "node:fs";
@@ -87,6 +87,7 @@ function localProvider2(options) {
87
87
  return nests(env.cwd, state) || nests(state, env.cwd) && !nests(join(state, "worktrees"), env.cwd) ? undefined : localSnapshots2(env, { store, git: options?.git });
88
88
  },
89
89
  worktrees: localWorktrees2({ home, git: options?.git }),
90
+ realpath: (env, path) => realpath(isAbsolute(path) ? path : join(env.cwd, path)).catch(() => null),
90
91
  async destroy() {
91
92
  throw new SdkError("unavailable", "A local folder is never torn down.");
92
93
  }
@@ -59,13 +59,21 @@ class TracingMcpClient2 extends McpClient {
59
59
  return super.request(method, traceparent === null || params?._meta !== undefined ? params : { ...params, _meta: { traceparent } }, options);
60
60
  }
61
61
  }
62
- function bridgeTools2(entry, tools, call) {
63
- const names = tools.map((tool) => toolName(entry.name, tool.name));
62
+ function bridgeTools2(entry, listed, call) {
63
+ const names = listed.tools.map((tool) => toolName(entry.name, tool.name));
64
64
  const counts = Map.groupBy(names, (name) => name);
65
- return tools.map((tool, index) => {
65
+ const description = entry.description?.trim() ?? "";
66
+ const instructions = listed.instructions?.trim() ?? "";
67
+ const namespace = {
68
+ name: `mcp__${entry.name}`.replace(/[^A-Za-z0-9_]/g, "_"),
69
+ aliases: [entry.name],
70
+ ...description === "" ? {} : { description },
71
+ ...instructions === "" ? {} : { instructions }
72
+ };
73
+ return listed.tools.map((tool, index) => {
66
74
  const name = names[index];
67
75
  const unique = (counts.get(name)?.length ?? 0) > 1 ? hashed(name, tool.name) : name;
68
- return bridge(entry, tool, call, unique);
76
+ return bridge(entry, tool, call, unique, namespace);
69
77
  });
70
78
  }
71
79
  function toolName(server, tool) {
@@ -82,14 +90,15 @@ function globMatches(pattern, value) {
82
90
  const source = pattern.split("*").map((part) => part.replace(/[.+?^${}()|[\]\\]/g, "\\$&")).join(".*");
83
91
  return new RegExp(`^${source}$`).test(value);
84
92
  }
85
- function bridge(entry, tool, call, name) {
93
+ function bridge(entry, tool, call, name, namespace) {
86
94
  const output = tool.outputSchema;
95
+ const exposure = toolExposure2(entry, tool.name);
87
96
  const definition = {
88
97
  name,
89
- namespace: `mcp/${entry.name}`,
98
+ namespace,
90
99
  description: tool.description ?? tool.title ?? tool.name,
91
100
  parameters: passthrough(tool.inputSchema),
92
- exposure: toolExposure2(entry, tool.name),
101
+ exposure: exposure === "code" ? "deferred" : exposure,
93
102
  class: () => ({ kind: "custom", label: `${entry.name}: ${tool.title ?? tool.name}` }),
94
103
  effects: tool.annotations?.readOnlyHint === true ? "none" : "workspace",
95
104
  replay: tool.annotations?.readOnlyHint === true || tool.annotations?.idempotentHint === true ? "safe" : "never",
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  listenFetch2
3
- } from "./canvas-preview-9e15hp6q.js";
3
+ } from "./canvas-preview-4rstzyd7.js";
4
4
 
5
5
  // src/listen.ts
6
6
  import { createServer } from "node:http";
@@ -116,6 +116,7 @@ function bindConnection2(connection, options) {
116
116
  "workspaces.inspect": ({ target }) => connection.workspaces.inspect(target),
117
117
  "workspaces.add": ({ target, options }) => connection.workspaces.add(target, options),
118
118
  "workspaces.forget": ({ id }) => connection.workspaces.forget(id),
119
+ "workspaces.cleanWorktrees": () => connection.workspaces.cleanWorktrees(),
119
120
  "sessions.watch": async (input) => sessionsFrames(connection.sessions.watch(input)),
120
121
  "sessions.start": async (input) => {
121
122
  const { session, receipt } = await connection.sessions.start(input);
@@ -138,12 +139,22 @@ function bindConnection2(connection, options) {
138
139
  await session(id).info();
139
140
  return viewFrames(session(id).head(head).watch(options));
140
141
  },
142
+ "head.see": async ({ session: id, head, commit }) => {
143
+ await session(id).head(head).see(commit);
144
+ return null;
145
+ },
141
146
  "head.history": ({ session: id, head, before, limit }) => session(id).head(head).history({ before, limit }),
142
147
  "head.tree": ({ session: id, head }) => session(id).head(head).tree(),
143
148
  "head.move": async ({ session: id, head, position, options, id: receipt }) => ({
144
149
  receipt: (await wire.move(id, head, position, { ...options, id: receipt })).id
145
150
  }),
146
151
  "head.compact": ({ session: id, head, instructions }) => session(id).head(head).compact({ instructions }),
152
+ "head.revise": ({ session: id, head, message, content, options }) => session(id).head(head).revise(message, content, options),
153
+ "head.hold": ({ session: id, head, message }) => wire.hold(id, head, message),
154
+ "head.release": async ({ session: id, head, message }) => {
155
+ await wire.release(id, head, message);
156
+ return null;
157
+ },
147
158
  "heads.create": ({ session: id, name, input }) => session(id).heads.create(name, input),
148
159
  "heads.merge": ({ session: id, name, options }) => session(id).heads.merge(name, options),
149
160
  "heads.remove": ({ session: id, name, options }) => session(id).heads.remove(name, options),
@@ -175,6 +186,10 @@ function bindConnection2(connection, options) {
175
186
  const answer = await session(id).part(part).answer({ signal });
176
187
  return answer === null ? null : { action: String(answer.action), input: toJson(answer.input) };
177
188
  },
189
+ "part.watch": async ({ session: id, part }) => {
190
+ await session(id).info();
191
+ return session(id).part(part).watch();
192
+ },
178
193
  "part.close": async ({ session: id, part }) => {
179
194
  await session(id).part(part).close();
180
195
  return null;
@@ -0,0 +1,256 @@
1
+ import {
2
+ run2
3
+ } from "./canvas-preview-bxnqt8cd.js";
4
+
5
+ // src/local/worktrees.ts
6
+ import { randomUUID } from "node:crypto";
7
+ import { copyFile, mkdir, realpath, rm, stat } from "node:fs/promises";
8
+ import { homedir, tmpdir } from "node:os";
9
+ import { basename, join, resolve } from "node:path";
10
+ import { SdkError } from "@nyte-ai/schema";
11
+ var ENV = { GIT_TERMINAL_PROMPT: "0", LC_ALL: "C" };
12
+ var SESSION_KEY = "nyte-session";
13
+ var STOPWORDS = new Set([
14
+ "a",
15
+ "an",
16
+ "the",
17
+ "and",
18
+ "or",
19
+ "of",
20
+ "to",
21
+ "in",
22
+ "on",
23
+ "for",
24
+ "with",
25
+ "from",
26
+ "by",
27
+ "at",
28
+ "into",
29
+ "as",
30
+ "is",
31
+ "are",
32
+ "be",
33
+ "it",
34
+ "its",
35
+ "this",
36
+ "that",
37
+ "my",
38
+ "our",
39
+ "your"
40
+ ]);
41
+ var TYPE_WORDS = new Set([
42
+ "feat",
43
+ "feature",
44
+ "fix",
45
+ "add",
46
+ "chore",
47
+ "refactor",
48
+ "docs",
49
+ "test",
50
+ "perf",
51
+ "build",
52
+ "ci",
53
+ "revert",
54
+ "update"
55
+ ]);
56
+ var REGENERABLE = new Set([
57
+ "node_modules",
58
+ ".turbo",
59
+ ".next",
60
+ ".nuxt",
61
+ ".svelte-kit",
62
+ ".vite",
63
+ ".parcel-cache",
64
+ "dist",
65
+ "build",
66
+ "coverage",
67
+ "target",
68
+ ".venv",
69
+ "__pycache__"
70
+ ]);
71
+ var MAX_ATTEMPTS = 100;
72
+ function gitCommand(binary = "git") {
73
+ return async (cwd, args, env) => {
74
+ const ran = await run2(binary, ["-c", "core.quotepath=off", ...args], {
75
+ cwd,
76
+ env: { ...ENV, ...env }
77
+ });
78
+ if (ran.code !== 0)
79
+ throw new SdkError("unavailable", ran.stderr.trim() || `git ${args[0] ?? ""} failed.`);
80
+ return ran.stdout.toString("utf8");
81
+ };
82
+ }
83
+ async function listWorktrees2(cwd, options) {
84
+ const out = await gitCommand(options?.git)(cwd, ["worktree", "list", "--porcelain", "-z"]);
85
+ return out.split("\x00\x00").flatMap((record) => {
86
+ const fields = record.split("\x00");
87
+ const path = fields.find((field) => field.startsWith("worktree "))?.slice(9);
88
+ const branch = fields.find((field) => field.startsWith("branch refs/heads/"))?.slice(18);
89
+ return path === undefined ? [] : [{ path: resolve(path), branch: branch ?? null }];
90
+ });
91
+ }
92
+ async function addWorktree2(cwd, input, options) {
93
+ await gitCommand(options?.git)(cwd, input.from === undefined ? ["worktree", "add", input.path, input.branch] : ["worktree", "add", "-b", input.branch, input.path, input.from]);
94
+ }
95
+ async function hasBranch2(cwd, name, options) {
96
+ return gitCommand(options?.git)(cwd, ["show-ref", "--verify", "--quiet", `refs/heads/${name}`]).then(() => true).catch(() => false);
97
+ }
98
+ function localWorktrees2(options) {
99
+ const git = gitCommand(options?.git);
100
+ const folders = join(options?.home ?? homedir(), ".nyte", "worktrees");
101
+ const commitAt = (cwd, revision) => git(cwd, ["rev-parse", "--verify", "--quiet", `${revision}^{commit}`]).then((out) => out.trim(), () => null);
102
+ const workingTree = async (repo) => {
103
+ const index = join(tmpdir(), `nyte-worktree-index-${randomUUID()}`);
104
+ const indexEnv = { GIT_INDEX_FILE: index };
105
+ try {
106
+ await copyFile(join(repo.gitDir, "index"), index).catch(() => {
107
+ return;
108
+ });
109
+ await git(repo.root, ["add", "-A", "--", "."], indexEnv);
110
+ return (await git(repo.root, ["write-tree"], indexEnv)).trim();
111
+ } finally {
112
+ await rm(index, { force: true });
113
+ }
114
+ };
115
+ const findClaimedBranch = async (root, name) => {
116
+ const out = await git(root, [
117
+ "config",
118
+ "-z",
119
+ "--get-regexp",
120
+ `^branch\\..*\\.${SESSION_KEY}$`
121
+ ]).catch(() => "");
122
+ for (const entry of out.split("\x00")) {
123
+ const newline = entry.indexOf(`
124
+ `);
125
+ const branch = entry.slice("branch.".length, newline - SESSION_KEY.length - 1);
126
+ if (newline > 0 && entry.slice(newline + 1) === name && await hasBranch2(root, branch, options))
127
+ return branch;
128
+ }
129
+ return null;
130
+ };
131
+ const claimBranch = async (root, parent, listed, input, head) => {
132
+ for (let attempt = 1;attempt <= MAX_ATTEMPTS; attempt++) {
133
+ const branch = `${input.prefix ?? ""}${branchName(input.title, attempt)}`;
134
+ const folder = join(parent, branch.replaceAll("/", "-"));
135
+ if (listed.some((worktree) => worktree.path === folder) || await stat(folder).catch(() => null) !== null)
136
+ continue;
137
+ const [created] = await Promise.allSettled([
138
+ git(root, ["branch", "--no-track", branch, head])
139
+ ]);
140
+ if (created.status === "rejected") {
141
+ if (await hasBranch2(root, branch, options))
142
+ continue;
143
+ throw created.reason;
144
+ }
145
+ await writeConfig(root, `branch.${branch}.${SESSION_KEY}`, input.name);
146
+ return branch;
147
+ }
148
+ throw new SdkError("unavailable", `Every worktree name for "${input.title ?? input.name}" is taken in ${root}.`);
149
+ };
150
+ const writeConfig = async (root, key, value) => {
151
+ for (let attempt = 1;; attempt++) {
152
+ const [written] = await Promise.allSettled([git(root, ["config", key, value])]);
153
+ if (written.status === "fulfilled")
154
+ return;
155
+ if (attempt >= 20)
156
+ throw written.reason;
157
+ await new Promise((settle) => setTimeout(settle, 10 * attempt));
158
+ }
159
+ };
160
+ return {
161
+ async create(env, input) {
162
+ const repo = await readRepository(git, env.cwd);
163
+ if (repo === null)
164
+ throw new SdkError("unavailable", `${env.cwd} is not in a git repository, so it cannot have a worktree.`);
165
+ const head = await commitAt(repo.root, "HEAD");
166
+ if (head === null)
167
+ throw new SdkError("unavailable", `${repo.root} has no commits yet, so a worktree has nothing to branch from.`);
168
+ const below = (root) => ({
169
+ kind: "local",
170
+ path: resolve(root, repo.prefix)
171
+ });
172
+ const listed = await listWorktrees2(repo.root, options);
173
+ const claimed = await findClaimedBranch(repo.root, input.name);
174
+ const open = listed.find((worktree) => claimed !== null && worktree.branch === claimed);
175
+ if (claimed !== null && open !== undefined)
176
+ return {
177
+ target: below(open.path),
178
+ branch: claimed,
179
+ base: await commitAt(repo.root, `refs/heads/${claimed}`) ?? head
180
+ };
181
+ const parent = join(folders, basename(repo.root));
182
+ await mkdir(parent, { recursive: true });
183
+ const realParent = await realpath(parent);
184
+ const branch = claimed ?? await claimBranch(repo.root, realParent, listed, input, head);
185
+ const base = claimed === null ? head : await commitAt(repo.root, `refs/heads/${claimed}`) ?? head;
186
+ const folder = join(realParent, branch.replaceAll("/", "-"));
187
+ const tree = await workingTree(repo);
188
+ await addWorktree2(repo.root, { path: folder, branch }, options);
189
+ if (tree !== (await git(folder, ["rev-parse", "HEAD^{tree}"])).trim()) {
190
+ await git(folder, ["read-tree", "-m", "-u", "HEAD", tree]);
191
+ await git(folder, ["reset", "-q"]);
192
+ }
193
+ return { target: below(folder), branch, base };
194
+ },
195
+ async remove(worktree) {
196
+ const path = "path" in worktree.target ? worktree.target.path : null;
197
+ if (path === null || await stat(path).catch(() => null) === null)
198
+ return { folder: "removed", branch: "kept" };
199
+ const [root = path, common = ""] = (await git(path, [
200
+ "rev-parse",
201
+ "--show-toplevel",
202
+ "--path-format=absolute",
203
+ "--git-common-dir"
204
+ ])).split(`
205
+ `);
206
+ const status = await git(root, ["status", "--porcelain", "--ignored", "-z"]);
207
+ if (status.split("\x00").some((entry) => entry !== "" && !(entry.startsWith("!! ") && entry.endsWith("/"))))
208
+ return { folder: "kept", branch: "kept" };
209
+ await git(common, ["worktree", "remove", root]);
210
+ if (await commitAt(common, `refs/heads/${worktree.branch}`) !== worktree.base)
211
+ return { folder: "removed", branch: "kept" };
212
+ await git(common, ["branch", "-D", worktree.branch]);
213
+ return { folder: "removed", branch: "deleted" };
214
+ },
215
+ async clean(target) {
216
+ const path = "path" in target ? target.path : null;
217
+ if (path === null || await stat(path).catch(() => null) === null)
218
+ return false;
219
+ const root = (await git(path, ["rev-parse", "--show-toplevel"])).trim();
220
+ const within = await realpath(folders).catch(() => null);
221
+ if (within === null || !root.startsWith(`${within}/`))
222
+ return false;
223
+ const status = await git(root, [
224
+ "status",
225
+ "--porcelain",
226
+ "--ignored=traditional",
227
+ "--untracked-files=normal",
228
+ "-z"
229
+ ]);
230
+ const regenerable = status.split("\x00").filter((entry) => entry.startsWith("!! ") && entry.endsWith("/")).map((entry) => entry.slice(3, -1)).filter((folder) => REGENERABLE.has(basename(folder)));
231
+ await Promise.all(regenerable.map((folder) => rm(join(root, folder), { recursive: true, force: true })));
232
+ return regenerable.length > 0;
233
+ }
234
+ };
235
+ }
236
+ async function readRepository(git, cwd) {
237
+ const out = await git(cwd, [
238
+ "rev-parse",
239
+ "--show-toplevel",
240
+ "--absolute-git-dir",
241
+ "--show-prefix"
242
+ ]).catch(() => null);
243
+ const [root, gitDir, prefix = ""] = out?.split(`
244
+ `) ?? [];
245
+ return root === undefined || gitDir === undefined || root === "" ? null : { root: resolve(root), gitDir: resolve(gitDir), prefix };
246
+ }
247
+ function branchName(title, attempt) {
248
+ const words = (title ?? "").normalize("NFKD").toLowerCase().split(/[^a-z0-9]+/u).filter((word) => word !== "");
249
+ const kept = words.filter((word) => !STOPWORDS.has(word));
250
+ const content = kept.filter((word) => !TYPE_WORDS.has(word));
251
+ const chosen = content.length >= 2 ? content : kept.length > 0 ? kept : words;
252
+ const stem = chosen.length === 0 ? "worktree" : chosen.slice(0, attempt === 1 ? 3 : 2).join("-");
253
+ return attempt === 1 ? stem : `${stem}-${attempt}`;
254
+ }
255
+
256
+ export { listWorktrees2, addWorktree2, hasBranch2, localWorktrees2 };
package/dist/src/index.js CHANGED
@@ -11,15 +11,15 @@ import"../chunks/canvas-preview-wg80rbc2.js";
11
11
  import {
12
12
  identitySigner2
13
13
  } from "../chunks/canvas-preview-5mex725c.js";
14
- import"../chunks/canvas-preview-gm0frf4b.js";
14
+ import"../chunks/canvas-preview-tq7ny7fm.js";
15
15
  import"../chunks/canvas-preview-rp4x6y1h.js";
16
- import"../chunks/canvas-preview-d97wvref.js";
16
+ import"../chunks/canvas-preview-75bmwhsr.js";
17
17
  import {
18
18
  listenFetch2
19
- } from "../chunks/canvas-preview-9e15hp6q.js";
19
+ } from "../chunks/canvas-preview-4rstzyd7.js";
20
20
  import {
21
21
  listen2
22
- } from "../chunks/canvas-preview-whfdhma6.js";
22
+ } from "../chunks/canvas-preview-sdvgxnpf.js";
23
23
  import"../chunks/canvas-preview-0nq75h9k.js";
24
24
  import"../chunks/canvas-preview-bxnqt8cd.js";
25
25
  import"../chunks/canvas-preview-yjz0f7n4.js";
@@ -51,17 +51,17 @@ import {
51
51
  addWorktree2,
52
52
  hasBranch2,
53
53
  localWorktrees2
54
- } from "../chunks/canvas-preview-2jwbe14k.js";
54
+ } from "../chunks/canvas-preview-z2xdr07w.js";
55
55
  import {
56
56
  localProvider2
57
- } from "../chunks/canvas-preview-e3vnkkc5.js";
57
+ } from "../chunks/canvas-preview-dr46h8mm.js";
58
58
  import"../chunks/canvas-preview-kdqgrstc.js";
59
59
  import"../chunks/canvas-preview-5z63am63.js";
60
60
  import"../chunks/canvas-preview-pkzqg96v.js";
61
- import"../chunks/canvas-preview-vaatfznm.js";
61
+ import"../chunks/canvas-preview-ez9zdfq4.js";
62
62
  import {
63
63
  createMcpHub2
64
- } from "../chunks/canvas-preview-gh7c9831.js";
64
+ } from "../chunks/canvas-preview-27v3zkz4.js";
65
65
  import"../chunks/canvas-preview-9aqczacs.js";
66
66
  import"../chunks/canvas-preview-4e0pxa32.js";
67
67
  import"../chunks/canvas-preview-befkjea2.js";
@@ -84,7 +84,7 @@ import {
84
84
  } from "../chunks/canvas-preview-mstbvwz3.js";
85
85
  import {
86
86
  openHost2
87
- } from "../chunks/canvas-preview-w0mr5bkn.js";
87
+ } from "../chunks/canvas-preview-3kmevzjy.js";
88
88
  import {
89
89
  postgresStore2
90
90
  } from "../chunks/canvas-preview-4b6sayhg.js";
@@ -1,6 +1,6 @@
1
1
  import type { ListenOptions, Listener } from "@nyte-ai/core";
2
2
  import type { ServedHost } from "@nyte-ai/core/server";
3
- /** Serves a composed host as a bare fetch handler with no socket and no `node:*` import, so a Worker can serve it (§41j): `before`, the unauthenticated identity and describe routes, then `createHandler` over one cached connection per principal and authority, every response carrying `X-Nyte-Host` (§43a, §43j). A request authenticates by a pairing code, then the listener token (compared in constant time over SHA-256 digests) or a device token remembered until a commit from another process moves the host's principals, then `authorize` under `custom`; a revoke aborts that principal's open requests and streams. A `token` shorter than 16 characters throws `SdkError("invalid")`; `execution` (default: run covering all) drives covered sessions in process on an `admit-only` host, covers their leases otherwise and takes nothing on a `dispatch` host (§42v); `close()` disconnects the clients and releases the execution once, after which every request answers `unavailable`. */
3
+ /** Serves a composed host as a bare fetch handler with no socket and no `node:*` import, so a Worker can serve it (§41j): `before`, the unauthenticated identity and describe routes, then `createHandler` over one cached connection per principal and authority, every response carrying `X-Nyte-Host` (§43a, §43j). A request authenticates by a pairing code, then the listener token (compared in constant time over SHA-256 digests, granting `auth.authority`) or a device token remembered until a commit from another process moves the host's principals, then `authorize` under `custom`; a revoke aborts that principal's open requests and streams. A `token` shorter than 16 characters throws `SdkError("invalid")`; `execution` (default: run covering all) drives covered sessions in process on an `admit-only` host, covers their leases otherwise and takes nothing on a `dispatch` host (§42v); `close()` disconnects the clients and releases the execution once, after which every request answers `unavailable`. */
4
4
  export declare function listenFetch(host: ServedHost, options: ListenOptions & {
5
5
  readonly transport: {
6
6
  readonly kind: "fetch";
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  listenFetch2
3
- } from "../chunks/canvas-preview-9e15hp6q.js";
4
- import"../chunks/canvas-preview-d97wvref.js";
5
- import"../chunks/canvas-preview-gm0frf4b.js";
3
+ } from "../chunks/canvas-preview-4rstzyd7.js";
4
+ import"../chunks/canvas-preview-75bmwhsr.js";
5
+ import"../chunks/canvas-preview-tq7ny7fm.js";
6
6
  import"../chunks/canvas-preview-rp4x6y1h.js";
7
7
  export {
8
8
  listenFetch2 as listenFetch
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  listen2
3
- } from "../chunks/canvas-preview-whfdhma6.js";
4
- import"../chunks/canvas-preview-9e15hp6q.js";
5
- import"../chunks/canvas-preview-d97wvref.js";
6
- import"../chunks/canvas-preview-gm0frf4b.js";
3
+ } from "../chunks/canvas-preview-sdvgxnpf.js";
4
+ import"../chunks/canvas-preview-4rstzyd7.js";
5
+ import"../chunks/canvas-preview-75bmwhsr.js";
6
+ import"../chunks/canvas-preview-tq7ny7fm.js";
7
7
  import"../chunks/canvas-preview-rp4x6y1h.js";
8
8
  export {
9
9
  listen2 as listen
@@ -1,5 +1,5 @@
1
1
  import type { EnvironmentProvider } from "@nyte-ai/core";
2
- /** Returns the local environment provider (`kind: "local"`): a folder on this machine. `inspect` answers a `{ kind: "local", path }` target's `folderIdentity` (§41d), `available` when the path is a readable directory (else `unavailable { unreachable }`; a target without a path `unavailable { unsupported }`, identified by the hash of its locator), and the project sources present under it, relative to it, in this order: the plugin files `.nyte/plugins/<name>.{ts,mts,js,mjs}` and `.nyte/plugins/<name>/index.<ext>`, the skills `.nyte/skills/<name>/SKILL.md` and `.agents/skills/<name>/SKILL.md`, the prompts `.nyte/prompts/<name>.md`, then `.nyte/SYSTEM.md`, `.nyte/APPEND_SYSTEM.md` and `.nyte/mcp.json`: the files project trust gates. Context files such as `AGENTS.md` load without trust and are not listed. `attach` resolves the real path (a target that is not a directory throws `SdkError("unavailable")`) and returns an `ExecutionEnv` with that identity, `cwd` the real path, `files` from `localFiles` (with `ripgrep` and `formatter`), `exec` from `localExec`, and a `close` that ends its file watches and the jobs' readers, leaving running jobs alone. `git` is `localGit` (with `git`), `github` is `localGitHub` (with `gh` and `git`; the login is the machine's, `localGitHubAccount`), `snapshots` is `localSnapshots` storing tree walks under `<home>/.nyte/snapshots` (`home` default `os.homedir()`), and absent for a folder that contains `<home>/.nyte` or lies inside it outside `<home>/.nyte/worktrees` (the home folder itself, its ancestors, Nyte's own state): there a snapshot would walk or stage everything under home, the store and secrets included, to report changes in the store it just wrote; `worktrees` is `localWorktrees` (with `home` and `git`); no `browser` (the desktop wraps one in); `destroy` rejects with `SdkError("unavailable")`: a local folder is never torn down. */
2
+ /** Returns the local environment provider (`kind: "local"`): a folder on this machine. `inspect` answers a `{ kind: "local", path }` target's `folderIdentity` (§41d), `available` when the path is a readable directory (else `unavailable { unreachable }`; a target without a path `unavailable { unsupported }`, identified by the hash of its locator), and the project sources present under it, relative to it, in this order: the plugin files `.nyte/plugins/<name>.{ts,mts,js,mjs}` and `.nyte/plugins/<name>/index.<ext>`, the skills `.nyte/skills/<name>/SKILL.md` and `.agents/skills/<name>/SKILL.md`, the prompts `.nyte/prompts/<name>.md`, then `.nyte/SYSTEM.md`, `.nyte/APPEND_SYSTEM.md` and `.nyte/mcp.json`: the files project trust gates. Context files such as `AGENTS.md` load without trust and are not listed. `attach` resolves the real path (a target that is not a directory throws `SdkError("unavailable")`) and returns an `ExecutionEnv` with that identity, `cwd` the real path, `files` from `localFiles` (with `ripgrep` and `formatter`), `exec` from `localExec`, and a `close` that ends its file watches and the jobs' readers, leaving running jobs alone. `git` is `localGit` (with `git`), `github` is `localGitHub` (with `gh` and `git`; the login is the machine's, `localGitHubAccount`), `snapshots` is `localSnapshots` storing tree walks under `<home>/.nyte/snapshots` (`home` default `os.homedir()`), and absent for a folder that contains `<home>/.nyte` or lies inside it outside `<home>/.nyte/worktrees` (the home folder itself, its ancestors, Nyte's own state): there a snapshot would walk or stage everything under home, the store and secrets included, to report changes in the store it just wrote; `worktrees` is `localWorktrees` (with `home` and `git`); `realpath` is `fs.promises.realpath` (the native one, which spells each name as the disk stores it), a relative path resolved against `env.cwd`, null where it fails; no `browser` (the desktop wraps one in); `destroy` rejects with `SdkError("unavailable")`: a local folder is never torn down. */
3
3
  export declare function localProvider(options?: {
4
4
  readonly home?: string;
5
5
  readonly git?: string;
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  localProvider2
3
- } from "../../chunks/canvas-preview-e3vnkkc5.js";
4
- import"../../chunks/canvas-preview-2jwbe14k.js";
3
+ } from "../../chunks/canvas-preview-dr46h8mm.js";
4
+ import"../../chunks/canvas-preview-z2xdr07w.js";
5
5
  import"../../chunks/canvas-preview-5ekjx6sw.js";
6
6
  import"../../chunks/canvas-preview-8xc0vn7v.js";
7
7
  import"../../chunks/canvas-preview-1sq7z9jq.js";