@bivy/bivy 0.18.9-staging.9 → 0.18.10-staging.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.
@@ -12,36 +12,71 @@ import path from "node:path";
12
12
  * account-wide copy rides in the E2E model-auth vault envelope (see server.ts),
13
13
  * and a synced import stamps the file with the writer's timestamp so every
14
14
  * machine compares like with like. Clearing is an empty file, not a delete, so
15
- * the empty state carries a timestamp and propagates too.
15
+ * the empty state carries a timestamp and propagates too. The one switch beside
16
+ * the text — whether sessions also get the Bivy note — lives in a small sidecar
17
+ * file and shares that clock: flipping it rewrites AGENTS.md's timestamp.
16
18
  *
17
19
  * Delivery is the caller's business; this module only produces the composed
18
20
  * text (and a file holding it, for agents that take a path).
19
21
  */
20
22
  /** Cap on the editable text. It rides on every turn of every session. */
21
23
  export const MAX_AGENT_INSTRUCTIONS_BYTES = 16 * 1024;
24
+ /**
25
+ * What every agent session is told about running under Bivy, whether or not the
26
+ * user wrote instructions: that the user is in a chat, and which `bivy` commands
27
+ * reach it. The commands read $BIVY_SESSION_ID (src/runtime/session-env.ts), so
28
+ * this works for any agent with a shell. Keep it short: it rides on every turn.
29
+ */
30
+ export const BIVY_AGENT_NOTE = [
31
+ "You are running inside Bivy. The user follows this session in a chat app (web or phone): they cannot see your " +
32
+ "terminal or files you only write to disk. $BIVY_SESSION_ID identifies this session; the `bivy` commands below use it.",
33
+ "- Show a LOCAL file or image (report, screenshot, chart, a file they asked for): run " +
34
+ '`bivy attach <path> [--caption "short note"]`, or call the `attach_to_chat` tool if you have it. Images render ' +
35
+ "inline, other files as downloads; the path must be inside the session workspace. Markdown image syntax like " +
36
+ "![](path) does not render a local path. A REMOTE `https://` image in markdown does render.",
37
+ "- Something with a UI: `bivy app publish <manifest.json>` (a web server's port, a static build, a terminal) or " +
38
+ "`bivy app run -- <command>` (a desktop app) gives the user a live preview; `bivy app shot` screenshots it so you " +
39
+ "can check your work; `bivy app present` tells the user a visible change is ready to look at. " +
40
+ "`bivy app --help` has the details.",
41
+ ].join("\n");
22
42
  const PREAMBLE = "The user's global Bivy instructions follow. They apply to every workspace; where they conflict with " +
23
43
  "instructions from the current repository (AGENTS.md, CLAUDE.md, …), the repository's instructions win.";
24
44
  export function agentInstructionsPath(appDir) {
25
45
  return path.join(appDir, "AGENTS.md");
26
46
  }
47
+ function optionsPath(appDir) {
48
+ return path.join(appDir, "agent-instructions.json");
49
+ }
50
+ function readBivyNote(appDir) {
51
+ try {
52
+ return JSON.parse(fs.readFileSync(optionsPath(appDir), "utf8")).bivyNote !== false;
53
+ }
54
+ catch {
55
+ return true;
56
+ }
57
+ }
27
58
  function composedPath(appDir) {
28
59
  return path.join(appDir, "agent-instructions.composed.md");
29
60
  }
30
61
  export function readAgentInstructions(appDir) {
62
+ const bivyNote = readBivyNote(appDir);
31
63
  try {
32
64
  const file = agentInstructionsPath(appDir);
33
- return { text: fs.readFileSync(file, "utf8"), updatedAt: Math.trunc(fs.statSync(file).mtimeMs) };
65
+ return { text: fs.readFileSync(file, "utf8"), updatedAt: Math.trunc(fs.statSync(file).mtimeMs), bivyNote };
34
66
  }
35
67
  catch {
36
- return { text: "", updatedAt: 0 };
68
+ return { text: "", updatedAt: 0, bivyNote };
37
69
  }
38
70
  }
39
- export function writeAgentInstructions(appDir, text, updatedAt = Date.now()) {
71
+ /** Write the text (and optionally the Bivy-note switch), stamped `updatedAt`. */
72
+ export function writeAgentInstructions(appDir, text, updatedAt = Date.now(), bivyNote) {
40
73
  if (Buffer.byteLength(text, "utf8") > MAX_AGENT_INSTRUCTIONS_BYTES) {
41
74
  throw new Error(`Agent instructions are limited to ${MAX_AGENT_INSTRUCTIONS_BYTES / 1024} KB.`);
42
75
  }
43
76
  const file = agentInstructionsPath(appDir);
44
77
  fs.mkdirSync(appDir, { recursive: true });
78
+ if (bivyNote !== undefined)
79
+ fs.writeFileSync(optionsPath(appDir), `${JSON.stringify({ bivyNote })}\n`);
45
80
  fs.writeFileSync(file, text);
46
81
  fs.utimesSync(file, new Date(updatedAt), new Date(updatedAt));
47
82
  return readAgentInstructions(appDir);
@@ -51,6 +86,8 @@ export function writeAgentInstructions(appDir, text, updatedAt = Date.now()) {
51
86
  * - "imported": the synced copy was newer and is now the local file.
52
87
  * - "local-newer": this machine holds a newer copy the account should get.
53
88
  * - "unchanged": nothing to do.
89
+ * A synced copy without `bivyNote` comes from a peer that predates the switch,
90
+ * where the note was always on.
54
91
  */
55
92
  export function mergeSyncedAgentInstructions(appDir, incoming) {
56
93
  const local = readAgentInstructions(appDir);
@@ -58,26 +95,29 @@ export function mergeSyncedAgentInstructions(appDir, incoming) {
58
95
  if (typeof synced?.text !== "string" || typeof synced.updatedAt !== "number") {
59
96
  return local.updatedAt > 0 ? "local-newer" : "unchanged";
60
97
  }
61
- if (synced.text === local.text)
98
+ const bivyNote = synced.bivyNote !== false;
99
+ if (synced.text === local.text && bivyNote === local.bivyNote)
62
100
  return "unchanged";
63
101
  if (synced.updatedAt > local.updatedAt) {
64
- writeAgentInstructions(appDir, synced.text, synced.updatedAt);
102
+ writeAgentInstructions(appDir, synced.text, synced.updatedAt, bivyNote);
65
103
  return "imported";
66
104
  }
67
105
  return "local-newer";
68
106
  }
69
- /** Wrap the user's text in the precedence preamble; "" when there is nothing to send. */
70
- export function composeAgentInstructions(text) {
107
+ /** The Bivy note (unless switched off), then the user's text (if any) under the precedence preamble. */
108
+ export function composeAgentInstructions(text, bivyNote = true) {
71
109
  const body = text.trim();
72
- return body ? `${PREAMBLE}\n\n${body}\n` : "";
110
+ return [bivyNote ? BIVY_AGENT_NOTE : "", body ? `${PREAMBLE}\n\n${body}` : ""].filter(Boolean).map((part) => `${part}\n`).join("\n");
73
111
  }
74
112
  /**
75
- * The instructions for a session starting now, or undefined when the user has
76
- * none. Keeps a composed copy on disk (rewritten only when it changes) for
113
+ * The instructions for a session starting now, or undefined when there is
114
+ * nothing to send (no text and the Bivy note switched off) or the file can't be
115
+ * written. Keeps a composed copy on disk (rewritten only when it changes) for
77
116
  * agents that take a file path rather than text.
78
117
  */
79
118
  export function sessionInstructions(appDir) {
80
- const text = composeAgentInstructions(readAgentInstructions(appDir).text);
119
+ const { text: userText, bivyNote } = readAgentInstructions(appDir);
120
+ const text = composeAgentInstructions(userText, bivyNote);
81
121
  const file = composedPath(appDir);
82
122
  try {
83
123
  if (!text) {
@@ -86,6 +126,7 @@ export function sessionInstructions(appDir) {
86
126
  fs.rmSync(file, { force: true });
87
127
  return undefined;
88
128
  }
129
+ fs.mkdirSync(appDir, { recursive: true });
89
130
  let current;
90
131
  try {
91
132
  current = fs.readFileSync(file, "utf8");
@@ -82,30 +82,6 @@ function brandClaudeModel(model) {
82
82
  const family = CLAUDE_MODEL_FAMILIES.find((f) => model.name === f || model.name.startsWith(`${f} `));
83
83
  return family ? { ...model, name: `Claude ${model.name}` } : model;
84
84
  }
85
- /**
86
- * Appended to the Claude Code system prompt so the agent DISCOVERS the outbound
87
- * attachment capability. `bivy attach` is just a shell command — without this the
88
- * agent has no way to know it exists and, when asked to "send a file", concludes
89
- * it can't (it looks for a tool, finds none). BIVY_SESSION_ID is injected into the
90
- * subprocess env (see spawnQuery), so the bare command resolves the session. Keep
91
- * this short: it rides on every turn's system prompt.
92
- *
93
- * The chat still has no route to a LOCAL/workspace file path, so that half of the
94
- * guidance (use `bivy attach`, not markdown, for those) stands. A REMOTE
95
- * `https://` image URL is different: the node now fetches it server-side and
96
- * serves it back to the chat (see src/session/inline-image-fetch.ts, issue #293),
97
- * so plain markdown is the right tool there — `bivy attach` only works on files
98
- * already inside the workspace, which a URL by definition isn't.
99
- */
100
- export const BIVY_ATTACH_SYSTEM_PROMPT = "Sending files and images to the user: the person you're talking to is in a chat UI. They cannot see files you only " +
101
- "write to disk, and the chat has no route to a workspace file path. " +
102
- "To show them a LOCAL file or image — a report, screenshot, chart, or a file they asked for — run " +
103
- '`bivy attach <path> [--caption "short note"]` in your shell. ' +
104
- "An image renders inline in the chat; any other file shows as a downloadable chip. The path must be inside the session " +
105
- "workspace. Do NOT use markdown image syntax like ![](path) for a local file or workspace path — it will not render; " +
106
- "always use `bivy attach` for those. A REMOTE image you already have a URL for is different: plain markdown " +
107
- "`![alt](https://...)` renders it inline, no attach needed. Prefer `bivy attach` / markdown links over pasting large " +
108
- "file contents or describing where a file lives on disk.";
109
85
  /** Name of the in-process MCP server the native attach tool is registered
110
86
  * under (see buildAttachMcpServer) — the SDK namespaces the tool the agent
111
87
  * sees as `mcp__<server>__<tool>`. */
@@ -114,8 +90,8 @@ export const BIVY_ATTACH_MCP_SERVER_NAME = "bivy";
114
90
  export const BIVY_ATTACH_TOOL_NAME = "attach_to_chat";
115
91
  /**
116
92
  * Build the in-process MCP server that exposes `attach_to_chat` as a native
117
- * tool call (issue #291) — the stronger sibling of BIVY_ATTACH_SYSTEM_PROMPT's
118
- * shell-out hint: the agent sees this in its actual tool list instead of having
93
+ * tool call (issue #291) — the stronger sibling of the Bivy note's
94
+ * `bivy attach` hint: the agent sees this in its actual tool list instead of having
119
95
  * to discover a shell command from prose. Bound to one session's id so the
120
96
  * handler always attaches into the conversation that called it, regardless of
121
97
  * how many Claude sessions this node is running concurrently.
@@ -877,15 +853,11 @@ class ClaudeSession {
877
853
  permissionMode,
878
854
  canUseTool,
879
855
  env,
880
- // Keep the default Claude Code prompt, appending the note that teaches the
881
- // agent how to send a file to the user (`bivy attach`) — otherwise the
882
- // capability is undiscoverable and "send me X as an attachment" fails.
883
- // Kept even when the native tool below is also registered: it's a cheap,
884
- // harmless fallback for a shell/subprocess the agent spawns that can't
885
- // reach the in-process MCP tool directly.
886
- // The user's account-wide instructions follow it; the repo's CLAUDE.md
887
- // still loads through the preset as usual.
888
- systemPrompt: { type: "preset", preset: "claude_code", append: [BIVY_ATTACH_SYSTEM_PROMPT, this.runtimeOptions.instructions].filter(Boolean).join("\n\n") },
856
+ // Keep the default Claude Code prompt, appending the session instructions:
857
+ // the Bivy note (how to reach the user's chat — `bivy attach`, previews)
858
+ // and the user's account-wide instructions. The repo's CLAUDE.md still
859
+ // loads through the preset as usual.
860
+ systemPrompt: { type: "preset", preset: "claude_code", ...(this.runtimeOptions.instructions ? { append: this.runtimeOptions.instructions } : {}) },
889
861
  ...(this.runtimeOptions.executablePath ? { pathToClaudeCodeExecutable: this.runtimeOptions.executablePath } : {}),
890
862
  };
891
863
  if (resumeId)
@@ -895,7 +867,7 @@ class ClaudeSession {
895
867
  if (this.desiredModel)
896
868
  options.model = this.desiredModel;
897
869
  // Native attach_to_chat tool (issue #291) — the stronger, tool-based sibling
898
- // of the system-prompt hint above. Wired only when the daemon handed us a
870
+ // of the Bivy note's `bivy attach` line. Wired only when the daemon handed us a
899
871
  // callback (see ClaudeCodeRuntimeOptions.attachToChat); absent in a few
900
872
  // deliberately minimal test harnesses, and gracefully degrades to the prompt
901
873
  // hint alone if this SDK build lacks the MCP builder helpers.
@@ -120,7 +120,7 @@ input, textarea, select { font-family: inherit; font-size: 16px; }
120
120
  .menu-item:hover, .menu-item[aria-checked="true"], .menu-item.active { background: var(--surface-2); }
121
121
  .menu-item.danger { color: var(--danger); }
122
122
  .menu-item-label { flex: 1; min-width: 0; }
123
- .menu-item.separated { margin-top: var(--space-1); border-top: 1px solid var(--line); border-radius: 0 0 var(--radius-sm) var(--radius-sm); }
123
+ .menu-item.separated { margin-top: var(--space-1); border-top: 1px solid var(--line); }
124
124
  /* A section title inside a menu (e.g. "Preview cards" above its choices). */
125
125
  .menu-heading { padding: var(--space-2) var(--space-3) var(--space-1); color: var(--muted); font-size: var(--text-xs); font-weight: var(--weight-semibold); letter-spacing: 0.04em; text-transform: uppercase; }
126
126
 
@@ -2214,6 +2214,42 @@ a.queue-item:hover { border-color: var(--line-strong); background: var(--surface
2214
2214
  .settings-nav-item.active .settings-nav-icon { color: var(--accent); }
2215
2215
  .settings-nav-label { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
2216
2216
  .settings-nav-chevron { display: none; flex: none; color: var(--muted); font-size: 18px; }
2217
+ /* Account identity card pinned to the top of the nav (Claude/ChatGPT pattern):
2218
+ avatar + email on one raised surface. */
2219
+ .settings-account-card {
2220
+ display: flex; flex-direction: column; gap: 1px; padding: var(--space-1);
2221
+ background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius-md);
2222
+ }
2223
+ .settings-account-avatar {
2224
+ flex: none; display: inline-flex; align-items: center; justify-content: center;
2225
+ width: 36px; height: 36px; border-radius: var(--radius-full);
2226
+ background: var(--surface-3); color: var(--ink); font-size: var(--text-sm); font-weight: var(--weight-semibold);
2227
+ }
2228
+ .settings-account-avatar svg { width: 20px; height: 20px; }
2229
+ .settings-account-text { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 1px; }
2230
+ .settings-account-sub { font-size: var(--text-xs); color: var(--muted); font-weight: normal; }
2231
+ .settings-account-usage { display: flex; flex-direction: column; gap: var(--space-2); padding: var(--space-1) 10px var(--space-2); font-size: var(--text-xs); color: var(--muted); }
2232
+ .settings-account-usage-row { display: flex; align-items: center; justify-content: space-between; gap: var(--space-2); min-height: 20px; }
2233
+ .settings-account-usage-count { color: var(--ink); font-variant-numeric: tabular-nums; }
2234
+ .settings-account-usage-note { font-weight: var(--weight-semibold); }
2235
+ .settings-account-usage[data-state="near"] .settings-account-usage-note { color: var(--warn); }
2236
+ .settings-account-usage[data-state="reached"] .settings-account-usage-note { color: var(--danger); }
2237
+ .settings-account-usage .banner { margin: 0; }
2238
+ .settings-account-usage-status { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
2239
+ .settings-account-usage > .btn.block { margin-top: 0; }
2240
+ /* The same meter on the Account page, aligned with the plan rows. */
2241
+ .settings-section > .settings-account-usage { padding: var(--space-1) 0 0; font-size: var(--text-sm); }
2242
+
2243
+ /* Account page offer (e.g. upgrade): a .card, layout only. */
2244
+ .account-offer { display: flex; flex-direction: column; gap: var(--space-3); padding: var(--space-4); margin-top: var(--space-4); }
2245
+ .account-offer .card-head { margin-bottom: 0; align-items: baseline; }
2246
+ .account-offer .card-sub { margin: calc(-1 * var(--space-2)) 0 0; }
2247
+ .account-offer-price { font-size: var(--text-base); font-weight: var(--weight-semibold); color: var(--ink); white-space: nowrap; }
2248
+ .account-offer-points { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--space-2); font-size: var(--text-sm); }
2249
+ .account-offer-points li { display: flex; align-items: flex-start; gap: var(--space-2); }
2250
+ .account-offer-check { flex: none; display: inline-flex; color: var(--accent); margin-top: 1px; }
2251
+ .account-offer > .btn.block { margin-top: var(--space-1); }
2252
+ .account-offer + .settings-section { margin-top: var(--space-5); }
2217
2253
  .settings-nav-version { flex: none; display: flex; flex-direction: column; gap: 1px; padding: 10px 10px 2px; color: var(--muted); font-size: 12px; letter-spacing: 0.02em; }
2218
2254
  .settings-nav-version-updated { font-size: 11px; opacity: 0.8; }
2219
2255
 
@@ -2228,6 +2264,13 @@ a.queue-item:hover { border-color: var(--line-strong); background: var(--surface
2228
2264
  .settings-body { flex: 1; overflow-y: auto; min-height: 0; padding: 14px 18px 28px; -webkit-overflow-scrolling: touch; overscroll-behavior: contain; animation: settings-panel-in var(--dur) var(--ease-standard); }
2229
2265
  .settings-body .settings-form { padding: 0; }
2230
2266
 
2267
+ /* Canonical meter — a thin allowance/usage bar (role="meter"). data-state
2268
+ near/reached tints it warn/danger. Reflects the styleguide (.meter). */
2269
+ .meter { display: block; height: 6px; border-radius: var(--radius-full); background: var(--surface-3); overflow: hidden; }
2270
+ .meter-fill { display: block; height: 100%; border-radius: inherit; background: var(--accent); transition: width var(--dur) var(--ease-standard); }
2271
+ .meter[data-state="near"] .meter-fill { background: var(--warn); }
2272
+ .meter[data-state="reached"] .meter-fill { background: var(--danger); }
2273
+
2231
2274
  /* Canonical selectable — the shared "active-accent-tint" choice control. Its
2232
2275
  selected state (accent border + soft accent fill) is the one that used to be
2233
2276
  copy-pasted across the theme picker, segmented buttons and ruleset chips; new
@@ -2353,6 +2396,7 @@ a.queue-item:hover { border-color: var(--line-strong); background: var(--surface
2353
2396
  .settings-nav-group-label { font-size: 12px; padding: 10px 6px 4px; }
2354
2397
  .settings-nav-item { padding: 14px 12px; font-size: 16px; }
2355
2398
  .settings-nav-chevron { display: inline-flex; }
2399
+ .settings-account-card { background: var(--surface-2); margin-bottom: var(--space-2); }
2356
2400
  .settings-content { padding-top: max(0px, env(safe-area-inset-top)); }
2357
2401
  .settings-back { display: inline-flex; }
2358
2402
  .settings-body { padding: 14px 16px max(28px, env(safe-area-inset-bottom)); }
@@ -2489,10 +2533,6 @@ a.queue-item:hover { border-color: var(--line-strong); background: var(--surface
2489
2533
  .fork-progress-dialog { color: var(--ink); margin: auto; max-width: calc(100% - var(--space-8)); }
2490
2534
  .fork-progress-dialog::backdrop { background: var(--overlay); }
2491
2535
  .app-dialog-actions { display: flex; justify-content: flex-end; gap: 8px; flex-wrap: wrap; }
2492
- .stat-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
2493
- .stat { border: 1px solid var(--line); border-radius: var(--radius-md); padding: 10px 12px; background: var(--surface-2); }
2494
- .stat-label { font-size: 11px; text-transform: uppercase; letter-spacing: 0.04em; color: var(--muted); }
2495
- .stat-value { font-size: 16px; font-weight: 700; margin-top: 2px; }
2496
2536
  textarea.picker-search { resize: vertical; font-family: ui-monospace, monospace; }
2497
2537
 
2498
2538
  /* ---- Terminal ---- */
@@ -2823,6 +2863,8 @@ textarea.picker-search { resize: vertical; font-family: ui-monospace, monospace;
2823
2863
  .node-menu-glyph { color: var(--accent); flex: none; }
2824
2864
  .eph-steps { margin: 4px 0; padding-left: 18px; color: var(--muted); font-size: 13px; display: grid; gap: 4px; }
2825
2865
  .code-snippet { margin: 6px 0 0; overflow: auto; border: 1px solid var(--line); border-radius: var(--radius-md); padding: 10px; background: var(--surface); font-size: 12px; }
2866
+ /* Settings → Agent instructions: Bivy's note, wrapped to the sheet's width. */
2867
+ .agent-note-text { white-space: pre-wrap; overflow-wrap: anywhere; }
2826
2868
  select.picker-search { appearance: auto; }
2827
2869
  .node-menu-sep { height: 1px; background: var(--line); margin: 4px 0; }
2828
2870
  /* PrToast, UpdatePrompt (both `.update-toast`), and ErrorToast used to each
@@ -4,7 +4,7 @@
4
4
  // own shell can resolve its chat session without being told the id.
5
5
  // `bivy attach <path>` (bin/bivy.mjs's cmdAttach) reads $BIVY_SESSION_ID to know
6
6
  // which session to post an outbound attachment to — see
7
- // claude-code.ts's BIVY_ATTACH_SYSTEM_PROMPT for the discoverability half of this
7
+ // BIVY_AGENT_NOTE (src/agent-instructions.ts) for the discoverability half of this
8
8
  // feature (issue #288 shipped attach; issue #290 is making it universal). One
9
9
  // helper, one env-var name, so a new adapter can't independently invent — or
10
10
  // simply forget — its own convention.
package/dist/server.js CHANGED
@@ -72,7 +72,7 @@ import { createSessionEngine } from "./session/engine.js";
72
72
  import { exportProviderAuth, exportAccountApiKeys, exportAccountOAuthCredentials, importAccountOAuthCredentials, exportSyncableProviderAuth, exportProviderAuthTombstones, importProviderAuth, removeProvider, setProviderApiKey, listCredentialRecords, setProviderApiKeyLabeled, setProviderReferenceLabeled, removeProviderCredential, setCredentialSync, setCredentialUnattended, exportUnattendedRecords, unattendedCredentialRevision, getCredentialPresets, setActiveCredentialPreset, setCredentialPresetMapping, exportSyncableRecords, exportRecordTombstones, importCredentialRecords, reconcileHostedCredentialRecords } from "./credentials/api.js";
73
73
  import { listProviders } from "./runtime/provider-catalog.js";
74
74
  import { exportLocalModels, importLocalModels } from "./runtime/local-model-store.js";
75
- import { mergeSyncedAgentInstructions, readAgentInstructions, sessionInstructions, writeAgentInstructions, MAX_AGENT_INSTRUCTIONS_BYTES } from "./agent-instructions.js";
75
+ import { BIVY_AGENT_NOTE, mergeSyncedAgentInstructions, readAgentInstructions, sessionInstructions, writeAgentInstructions, MAX_AGENT_INSTRUCTIONS_BYTES } from "./agent-instructions.js";
76
76
  import { execEphemeralRequest } from "./ephemeral-exec.js";
77
77
  import { ApprovalManager } from "./approval.js";
78
78
  import { QuestionManager, validQuestions, isAskUserQuestionTool, formatQuestionResult } from "./question.js";
@@ -1374,7 +1374,7 @@ function nodeSettingsSnapshot() {
1374
1374
  autoAttachToolImages: readSettings().autoAttachToolImages === true,
1375
1375
  appScreenshots: appScreenshotsEnabled(),
1376
1376
  forkWorkspaceMaxBytes: Number.isInteger(readSettings().forkWorkspaceMaxBytes) ? Number(readSettings().forkWorkspaceMaxBytes) : 50 * 1024 * 1024,
1377
- agentInstructions: { ...readAgentInstructions(appDir), maxBytes: MAX_AGENT_INSTRUCTIONS_BYTES },
1377
+ agentInstructions: { ...readAgentInstructions(appDir), maxBytes: MAX_AGENT_INSTRUCTIONS_BYTES, bivyNoteText: BIVY_AGENT_NOTE },
1378
1378
  };
1379
1379
  }
1380
1380
  async function applyNodeSettings(patch) {
@@ -1389,6 +1389,11 @@ async function applyNodeSettings(patch) {
1389
1389
  writeAgentInstructions(appDir, patch.agentInstructions);
1390
1390
  void pushModelAuthToControlPlane();
1391
1391
  }
1392
+ // The Bivy-note switch syncs with the text: keep the text, stamp a new time.
1393
+ if (typeof patch.agentInstructionsBivyNote === "boolean") {
1394
+ writeAgentInstructions(appDir, readAgentInstructions(appDir).text, Date.now(), patch.agentInstructionsBivyNote);
1395
+ void pushModelAuthToControlPlane();
1396
+ }
1392
1397
  const settings = readSettings();
1393
1398
  if (typeof patch.name === "string" && patch.name.trim()) {
1394
1399
  const prev = identity.name;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bivy/bivy",
3
- "version": "0.18.9-staging.9",
3
+ "version": "0.18.10-staging.1",
4
4
  "type": "module",
5
5
  "license": "AGPL-3.0-only",
6
6
  "description": "Run coding agents on machines you own. Open-source, self-hostable agent workspace.",
@@ -38,14 +38,14 @@
38
38
  },
39
39
  "dependencies": {
40
40
  "@lydell/node-pty": "1.2.0-beta.15",
41
- "@modelcontextprotocol/sdk": "1.30.0",
41
+ "@modelcontextprotocol/sdk": "1.30.1",
42
42
  "@novnc/novnc": "1.7.0",
43
43
  "brace-expansion": "5.0.12",
44
44
  "express": "^5.2.1",
45
45
  "express-rate-limit": "^8.7.0",
46
46
  "semver": "^7.7.2",
47
47
  "typebox": "^1.3.34",
48
- "undici": "8.10.2",
48
+ "undici": "8.11.2",
49
49
  "ws": "^8.21.3",
50
50
  "yaml": "^2.9.1",
51
51
  "zod": "^4.6.5"
@@ -55,15 +55,15 @@
55
55
  "hono": "4.13.5",
56
56
  "qs": "6.16.0",
57
57
  "@hono/node-server": "2.0.12",
58
- "@modelcontextprotocol/sdk": "1.30.0",
58
+ "@modelcontextprotocol/sdk": "1.30.1",
59
59
  "@earendil-works/pi-coding-agent": {
60
60
  "brace-expansion": "5.0.12",
61
- "undici": "8.10.2"
61
+ "undici": "8.11.2"
62
62
  },
63
63
  "brace-expansion": "5.0.12",
64
64
  "nanoid": "3.3.18",
65
65
  "lodash-es": "4.18.1",
66
- "undici": "8.10.2"
66
+ "undici": "8.11.2"
67
67
  },
68
68
  "readme": "# Bivy\n\n[![npm](https://img.shields.io/npm/v/@bivy/bivy?color=2b6cb0&label=%40bivy%2Fbivy)](https://www.npmjs.com/package/@bivy/bivy)\n[![license: AGPL-3.0-only](https://img.shields.io/badge/license-AGPL--3.0--only-2b6cb0)](LICENSE)\n[![node](https://img.shields.io/badge/node-%E2%89%A520-2b6cb0)](https://nodejs.org)\n\n**Build, try, and review apps from your phone. Your coding agent and dev\nenvironment stay on your own machines.**\n\nBivy is an open-source remote development environment built around coding\nagents. Ask for a change, try the running app, and point to or draw on what\nneeds fixing. Send that feedback to the agent and try again—all in the same\nsession, without going back to your computer.\n\nLive previews bring your app to your phone or browser without deploying it to\nRender, building a Docker image, or setting up a separate preview server.\nShare a preview link so someone else can try it and leave notes, too.\n\nKeep Claude Code, Codex, Pi, OpenCode, or another supported agent. Keep your\nrepos, tools, databases, and development environment. Bivy connects them to\nchat, a terminal, app previews, and code review wherever you are. Start work\nyourself, or let an issue, CI failure, message, schedule, or webhook start it.\n\n**[Start free on Bivy Cloud](https://app.bivy.sh)** ·\n**[Quickstart](docs/quickstart.md)** ·\n**[Documentation](docs/README.md)** ·\n**[Self-host](docs/deploy-images.md)** ·\n**[Website](https://bivy.sh)**\n\n**Recommended:** sign in at [app.bivy.sh](https://app.bivy.sh), then copy your\npersonalized **Connect a Machine** command into a terminal on your Mac or Linux\ncomputer. It installs and enrolls the machine without another Bivy login. The\nbrowser connects automatically; choose a repository and send your first task\nright there.\n\nPrefer starting from the terminal?\n\n```bash\ncurl -fsSL https://bivy.sh/install.sh | bash # install + guided setup\ncd your-repo\nbivy run claude # or codex, pi, opencode\nbivy open # continue in the web app (needs remote setup)\n```\n\nBivy Cloud hosts the app, control plane, and relay—not the machines running your\nagents. Connect a Mac, Linux computer, or existing server and bring your own\nagent subscription, model API key, or local model. You can also self-host the\nentire remote-access stack.\n\n> **Bivy is 0.x software.** Claude Code, Codex, Pi, and OpenCode are the\n> release-tested paths. Credential sync, resume, handoffs, approvals, and\n> sandboxing depend on the runtime. See the\n> [runtime support matrix](docs/runtime-support-matrix.md).\n\n## Build → preview → mark up → iterate\n\nRemote chat lets you ask an agent for a change. Bivy also lets you **use what\nit built and show it what to fix**, without a separate deployment step.\n\n1. **Ask for a change.** The agent works in your repo with your existing tools\n and local services.\n2. **Try the app.** Open a live preview from the session. Use the running app,\n not just a screenshot of what the agent says it finished. Bivy can discover\n dev servers running in the session workspace; tap **Preview** to open one.\n3. **Mark up what needs work.** Point at an element or circle and draw on the\n preview. Add a note, then send the draft to the agent with the element and\n page context. Marked-up screenshots and before/after comparison are available\n when agent screenshots are enabled.\n4. **Iterate and review.** Try the next version, inspect the diff, review checks,\n and decide when the work is ready—not just when the agent stops.\n5. **Bring someone else into the review.** Copy a public preview link so they\n can use the app without a Bivy account and leave notes on specific elements.\n Bring their notes into the session when you're ready to act on them.\n\nOn Bivy Cloud, preview delivery is built in: no per-app domains, certificates,\npublic ports, or tunnels to configure. Self-hosters configure preview delivery\nonce for their Bivy deployment, not for every app. The app still needs its normal\nbuild or dev-server setup, and **the machine serving it must stay awake and\nonline**. These are development previews, not production hosting.\n\n**Share deliberately:** anyone with a preview link can use that app, including\nits live backend. Links expire after 24 hours and can be revoked sooner.\nReviewer notes aren't sent to the agent automatically; you send them or explicitly\nallow agent access. Preview traffic uses HTTPS through the preview relay, not\nsession end-to-end encryption; the relay operator can see it.\n\n[App previews, visual feedback, and sharing →](docs/apps.md)\n\n## Your agents and machines, one workspace\n\n| Capability | What it means for you |\n|---|---|\n| **One workspace, multiple agents** | Use different agents and models for different tasks without maintaining a separate workflow for each. |\n| **Your machines and environment** | Work beside your existing repos, dev servers, databases, private networks, toolchains, and GPUs. |\n| **Automations and triggers** | Let issues, failed CI, messages, schedules, and webhooks start work instead of copying requests into a chat. |\n| **Encrypted key and OAuth sync** | Reuse Bivy-managed provider credentials across enrolled machines and compatible runtimes, with less repeated setup. |\n| **Live sessions from anywhere** | Build and review from your phone, browser, or terminal; answer questions and approve supported actions in the same session. |\n| **Live app previews** | Try the running app without deploying it to a separate hosting service. |\n| **Visual feedback and sharing** | Point, draw, and send specific feedback to the agent; share a preview link for outside review. |\n| **Reviewable results** | Inspect the app, changes, declared checks, artifacts, and pull requests—not just an agent's claim that it finished. |\n| **Hosted convenience or self-hosting** | Use Bivy Cloud for managed remote access, or run the same open-source core yourself. |\n\n## One workflow, from trigger to review\n\n```text\nPrompt · GitHub issue · CI failure · Linear · Slack · Schedule · Webhook\n │\n ▼\n Choose machine + agent + model\n + supported credentials\n │\n ▼\n Live agent session\n Join · steer · approve · stop\n │\n ▼\n Try app · inspect changes · checks\n │\n ▼\n Mark up · send feedback · iterate\n │\n ▼\n Share preview · review PR\n```\n\nA **Machine** is a computer or server you connect. A **Session** is live agent\nwork on that machine. A **Run** is delegated background work that creates a\nsession and tracks its outcome. An **Automation** is a reusable definition that\ncreates runs when an event matches.\n\nManual and automated work use the same kind of live session. You can join a run\nwhen it needs help rather than wait for a black-box job to finish.\n\n### Work in the environment you already have\n\nA clean cloud sandbox isn't always enough. Your agent may need the database\nrunning on localhost, an uncommitted change, an internal API behind your VPN,\nor a model running on your GPU. Bivy runs the agent where those things already\nexist, subject to that machine's permissions and the runtime's protection.\n\nConnect several machines to the same account: a laptop for interactive work,\na Linux server for background jobs, or a GPU box for local inference. Choose\nthe machine for each session or pin it in an automation. Repository runs can\nuse isolated Git worktrees without rebuilding the whole development environment.\n\n**The execution machine must stay awake and online.** To close your laptop and\nleave work running, run the agent on a different, always-on machine.\n\n[Environment and multi-machine recipes →](docs/capability-recipes.md)\n\n### Use multiple agents, not multiple disconnected workflows\n\nRun Claude Code for one task, Codex for another, and Pi or OpenCode where they\nfit. Bivy supplies the shared session, remote-access, automation, and review\nsurfaces; your chosen agent still does the coding and uses your model provider.\n\n- Choose an agent and, where supported, a model for each session or run.\n- Import existing Claude Code and Codex sessions.\n- Fork or move work to another agent or machine when a different setup fits\n better. Continuation fidelity varies: some paths preserve native history,\n while others replay portable turns or seed the destination with context.\n- Use agent-native logins, Bivy-managed credentials, or local inference.\n Bivy's custom OpenAI-compatible endpoint registry currently feeds Pi;\n other agents may need their own provider configuration.\n- Register your own ACP or headless process agent with `bivy agent add`.\n\nBivy does not replace your agent, provide model inference, or make every agent's\nfeatures identical. Consult the [support matrix](docs/runtime-support-matrix.md)\nand [handoff recipes](docs/capability-recipes.md#fork-or-move-a-session).\n\n### Less signing in. Less copying secrets.\n\nBivy syncs **Bivy-managed API keys and supported OAuth credentials** across\nenrolled machines for compatible runtimes. Connect supported credentials once\nand reuse them where you run work, rather than manually distributing keys to\neach machine.\n\nFor ordinary account sync, credentials are encrypted on the node before upload.\nThe control plane stores ciphertext and wrapped-key metadata; enrolled nodes\nshare access by wrapping the vault key to one another. Bivy Cloud does not\nreceive plaintext credentials through this sync path.\n\nYou can also keep credentials local, use labeled keys and project presets, or\nreference environment variables and 1Password instead of embedding secrets in\nconfiguration:\n\n```bash\nbivy provider login\nbivy credentials add anthropic work\nbivy secrets ref github.repo-token op://Bivy/GitHub/repo-token\n```\n\n**Not every CLI login syncs.** Native agent logins may still be per-machine;\nGitHub App private-key sync is separately opt-in. If you lose every node and\ndevice able to unwrap a vault, you must sign in to providers again. Explicit\nhosted-provisioning custody grants are separate from ordinary encrypted sync.\n\n[Credential sync and runtime coverage →](docs/credential-sync.md) ·\n[Credentials guide →](docs/credentials-guide.md) ·\n[Key storage →](docs/key-management.md)\n\n### Let events start the work\n\nAutomations turn recurring or incoming work into sessions you can join,\nsupervise, and review. Choose the repository, machine, agent, model, approval\nmode, sandbox setting, and maximum attempts.\n\n| Trigger | Example workflow |\n|---|---|\n| **GitHub issues and mentions** | Label an issue `bivy` or `bivy/<machine>`, or mention your Bivy GitHub App, to work toward a pull request. |\n| **Failed CI** | Match a failed workflow, ask the agent to reproduce it, make a fix, and run the affected checks. |\n| **Linear** | Label an issue to start work without copying its description into an agent. |\n| **Slack** | Send a request from the conversation where the work came up. |\n| **Schedules** | Run a weekly dependency review, recurring maintenance, or a one-time task. |\n| **Signed webhooks** | Connect alerts, internal tools, or your own event sources. |\n\nConfigure automations in the app or version them with your repository in\n`.bivy/automations.yaml`:\n\n```bash\nbivy automation init\n# Edit the generated definition for your repository and workflow.\nbivy automation validate\nbivy automation test --event .bivy/events/failed-ci.yaml # supply a local event fixture\nbivy automation apply\n```\n\nOr delegate a one-off job without creating an automation:\n\n```bash\nbivy runs start \"Review outdated dependencies and propose a small, tested update.\"\nbivy runs wait <id>\n```\n\nRuns keep routing and lifecycle evidence, check results, and output references\nin a reviewable Receipt. For unattended issue work, Bivy runs declared repository\nchecks after the agent's turn; failed required checks fail the run even if the\nagent reports success. A completed process alone is not proof that the task\nsucceeded.\n\n[Automation recipes →](docs/capability-recipes.md#let-events-start-runs) ·\n[Automations as code →](docs/automations-as-code.md) ·\n[Run outcomes and reliability limits →](docs/automation-runs.md)\n\n### Start at your desk. Continue anywhere.\n\nOpen the same session in the browser, phone PWA, or terminal. Watch work live,\nanswer questions, approve supported tool calls, or stop the agent.\n\n- Send screenshots, images, logs, and other files from your phone.\n- Download reports and artifacts the agent creates.\n- Open [session apps](docs/apps.md) from chat or the **Apps** menu: live web\n previews, desktop app views, and CLI/TUI tools. Agents can publish views with\n `bivy app publish bivy.app.json` and create share links with `bivy app share`.\n- Use voice input and read-aloud where supported; provider-backed voice may\n send audio or text to the selected provider.\n- Keep a native terminal workflow or use structured chat, depending on the agent.\n\n```bash\nbivy run claude --no-follow # start without attaching\nbivy open # open the web app\nbivy resume # return to the session in your terminal\nbivy link # pair a device directly via QR\n```\n\nNo phone app installation is required. Open [app.bivy.sh](https://app.bivy.sh)\nin your browser; adding it to your home screen is optional.\n\n[Remote access →](docs/remote-access.md) ·\n[Voice, files, and terminal recipes →](docs/capability-recipes.md)\n\n## Get started\n\n### Install\n\nBivy supports **macOS and Linux with Node.js 20+**. The installer installs the\n`@bivy/bivy` package, runs guided setup, and starts a launchd or systemd service:\n\n```bash\ncurl -fsSL https://bivy.sh/install.sh | bash\n```\n\nSetup helps you choose an agent and configure remote access. Existing agents\nkeep their command, login, and configuration. The installer may use `sudo` to\ninstall a missing Node.js, but never for `npm install`. To inspect it first,\ndownload it with `curl -fsSL https://bivy.sh/install.sh -o install.sh`.\n\nAlready have Node.js and want to avoid sudo?\n\n```bash\nnpm install -g @bivy/bivy\nbivy setup\n```\n\nThen try one small task:\n\n```bash\ncd your-repo\nbivy run claude\n# Ask: \"Explain this repo and make one small, safe improvement. Run the relevant checks.\"\nbivy open\n```\n\nOpen that same session on your phone while it runs. For a web app, ask the agent\nto start its dev server, then open **Apps → Running in this workspace → Preview**.\nTry a page, point to something you'd change, and send the feedback back to the\nagent. No separate preview deployment needed.\n\nOnce that works, share a preview for review, connect another machine, or add\nyour first automation.\n\n**Local-only works too.** `bivy run`, `bivy resume`, and `bivy sessions` need no\naccount or server. Choose **local only for now** during setup; use `bivy login`\nlater. Browser and phone access need a hosted or self-hosted control plane;\nthe node itself does not serve a web UI.\n\n[Full quickstart →](docs/quickstart.md) ·\n[Installer options, service management, and uninstall →](docs/install.md)\n\n### Choose hosted or self-hosted\n\n| Option | What you get |\n|---|---|\n| **Free Cloud — $0** | Every launch feature, including automations; 10 new remote sessions per rolling seven days. No credit card required. |\n| **Cloud — $15/month** | The same features with unlimited remote sessions. |\n| **Self-hosted Core** | Operate the app, control plane, and relay yourself, with no Bivy usage limits. |\n\nManual and automated sessions share the Cloud allowance. Resuming existing\nsessions and viewing history do not consume it. Agent subscriptions and model\nprovider charges are separate. See [current pricing](https://bivy.sh#pricing).\n\n**Self-host anywhere:** deploy the public control-plane (including the web app)\nand relay images with Postgres and [a small set of environment variables](docs/deploy-images.md).\nYour server or container platform handles HTTPS. Set up owner access in the\nbrowser—no SSH, external authentication provider, or Bivy Cloud account required.\nFor a bare VPS, the [Compose installer](docs/self-host-quickstart.md) automates the\nsame stack. These onboarding features require a release containing them.\n\nStart on Cloud and self-host later if you prefer. Deploy the stack, reconnect\nmachines with `bivy relay:setup`, and pair devices to your server. This is not a\none-click migration of your Cloud account; your local repos and agent\nconfiguration stay in place.\n\n```bash\nbivy relay:setup \\\n --control-plane https://bivy.example.com \\\n --relay wss://relay.example.com\n```\n\nSelf-hosting is community-supported: you own TLS, backups, upgrades, and\nhardening. Public multi-architecture images are available as\n`ghcr.io/bivysh/bivy-control-plane` and `ghcr.io/bivysh/bivy-relay`; pin a release\nversion or full commit SHA.\n\n[Deploy the images anywhere →](docs/deploy-images.md) ·\n[Optional VPS installer →](docs/self-host-quickstart.md) ·\n[Operations reference →](docs/self-host.md)\n\n## Architecture\n\nYour environment, with clear security boundaries:\n\n```text\nYour machine Hosted or self-hosted\n┌──────────────────────┐ ┌──────────────────────┐\n│ Node daemon │──outbound──▶│ Relay │\n│ Agents, repos, tools │ │ Encrypted frames │\n│ Local credentials │ └──────────┬───────────┘\n└──────────────────────┘ │\n Browser / phone\n + control plane\n (app, accounts, metadata)\n```\n\n- **Execution stays on your machine.** Bivy Cloud does not run your agents.\n Your model provider still sees whatever the agent sends it.\n- **Interactive session traffic is end-to-end encrypted** between the node and\n paired devices. The relay forwards opaque session frames; your node dials out,\n so no inbound public port is required.\n- **App previews have a separate security boundary.** Preview traffic uses HTTPS\n and an outbound tunnel, not session E2E encryption. The preview ingress/relay\n operator can see it. Public preview links grant anyone holding them access to\n that view and its live backend until expiry or revocation. See\n [preview security and sharing](docs/apps.md#runtime-and-security-boundaries).\n- **Ordinary credential sync uploads ciphertext, not plaintext keys.**\n Supported credentials and recovery limits are documented separately.\n- **Encryption is not universal across integrations.** Slack commands and\n generic webhook instructions reach the control plane in plaintext. Do not\n put secrets in them. Routing and bounded run metadata are also visible there.\n- **Device authorization matters.** QR pairing authorizes a device directly\n through the node. Hosted account pairing trusts the control plane to authorize\n devices and serve the web app that holds client keys.\n- **Bivy is not an OS-level sandbox.** The default approval mode is\n `autonomous`; protection depends on the runtime. Some agents enforce sandbox\n tiers, while process agents may run with your full user permissions.\n Heuristic tool checks help prevent accidents but are not isolation.\n\nReview the runtime's Protection label and configure approval/sandbox settings\nfor the task, especially before enabling unattended work.\n\n[Security model and known limitations →](docs/security-model.md) ·\n[Runtime protection matrix →](docs/runtime-support-matrix.md) ·\n[Configuration →](docs/configuration.md)\n\n## Agents and everyday commands\n\n**Claude Code, Codex, Pi, and OpenCode are release-tested.** Additional adapters\ninclude Gemini CLI, Qwen Code, Goose, Aider, Cline, Crush, Cursor, GitHub Copilot,\nGrok, Amp, Auggie, Droid, Continue, Kilo Code, and Rovo Dev. Installation,\nresume, model selection, and tool protection vary—see the\n[support matrix](docs/runtime-support-matrix.md) and [agent guides](docs/agents/README.md).\n\nRun an arbitrary command with `bivy run -- ./your-agent --flags`, register a\nreusable entry with `bivy agent add`, or package a declarative integration with\nexperimental [plugins](docs/plugins.md).\n\n```bash\nbivy run claude # launch a durable session; also codex, pi, opencode\nbivy sessions # list live and saved sessions\nbivy resume # resume the most recent session\nbivy open # open the web app (requires remote setup)\nbivy nodes # list connected account machines\nbivy runs list # inspect delegated work\nbivy automation init # scaffold repo-owned automations\nbivy provider login # connect supported model credentials\nbivy agent add # register an ACP or process agent\nbivy doctor # check installation and connectivity\nbivy logs -f # follow node logs\nbivy update # update and restart the service\n```\n\n`bivy update` uses your original installation method and waits for an active\nturn to finish before restarting. Use `--force` to skip that wait.\n\n[CLI reference →](docs/cli-reference.md) ·\n[Node and project configuration →](docs/config-as-code.md) ·\n[GitHub setup →](docs/github-setup.md) ·\n[Linear setup →](docs/linear-work-queue.md)\n\n## Development and contributions\n\n```bash\npnpm install\npnpm run dev # node daemon on http://localhost:4317\npnpm run dev:web # web client dev server\n```\n\n| Directory | Contents |\n|---|---|\n| `src/`, `bin/` | Node daemon, CLI, runtime adapters, sessions, approvals, secrets |\n| `packages/core/` | Shared protocol, pairing, and wire format |\n| `packages/web/`, `packages/ui/` | React PWA and shared design system |\n| `services/relay/` | Self-hostable encrypted relay |\n| `services/control-plane/` | Self-hostable control plane |\n| `deploy/` | Deployment examples |\n\n```bash\npnpm run typecheck\npnpm run typecheck:web\npnpm run lint\npnpm run test:unit\npnpm run test:core\npnpm run check:licenses\npnpm run check:secrets\n```\n\nSee [CONTRIBUTING.md](CONTRIBUTING.md) for the development workflow. Releases\nare published from CI with provenance attestations; see\n[release verification](docs/releasing.md).\n\n**Found a security issue?** Use\n[GitHub private vulnerability reporting](https://github.com/bivysh/bivy/security/advisories/new),\nnot a public issue. See [SECURITY.md](SECURITY.md).\n\n### In development—not available at launch\n\nAutomatically provisioned, short-lived **ephemeral machines** are in development\nfor hosted and bring-your-own-cloud deployments. Neither path is ready or\nsupported for this launch. Use an existing computer or server you operate.\nExperimental provisioning has different credential-custody and encryption\nboundaries; see the [provisioning trust model](docs/hosted-provisioning-trust-model.md).\n\n## License\n\nEverything in this repository—node, CLI, web/PWA, relay, and control plane—is\nfree and open-source **AGPL-3.0-only Core**, with no Bivy usage limits. You may\nuse, modify, and self-host it under that license. If users interact with your\nmodified version over a network, section 13 requires you to offer its\ncorresponding source. See [LICENSE](LICENSE).\n\nBivy Cloud is the hosted operation of that stack plus billing and plans, in a\nseparate private repository. Contributions use the\n[DCO](CONTRIBUTING.md#certificate-of-origin); there is no CLA.\n",
69
69
  "readmeFilename": "README.md",