beehaven 0.8.16 → 0.8.18

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
@@ -18,19 +18,41 @@ and this is how you talk to it from a shell.
18
18
  For a one-off agent session with Node.js 20 or newer, run the npm package directly:
19
19
 
20
20
  ```bash
21
- npx --yes @clearlysh/beehaven@latest guide
21
+ npx --yes beehaven@latest guide
22
22
  ```
23
23
 
24
24
  `guide` works before sign-in. It explains when Clearly is useful, gives the
25
25
  human a signup link, and shows the authenticated first artifact. `npx` uses an
26
- ephemeral npm install; it does not create a Clearly account or publish a file.
26
+ ephemeral npm install; it does not create a Clearly account or upload anything.
27
+ Public builds send one anonymous, content-free `cli:guide` usage event unless
28
+ `BEEHAVEN_NO_TELEMETRY` or the local `telemetry-off` file disables telemetry.
27
29
  The package also provides the `beehaven` binary for a global npm install:
28
30
 
29
31
  ```bash
30
- npm install -g @clearlysh/beehaven@latest
32
+ npm install -g beehaven@latest
31
33
  beehaven guide
32
34
  ```
33
35
 
36
+ The identical release is also listed under the Clearly npm organization as
37
+ `@clearlysh/beehaven`.
38
+
39
+ ### Hand work to someone without an account
40
+
41
+ When an agent's work is ready before the person has signed up, `handoff` needs
42
+ no login and prints one private link:
43
+
44
+ ```bash
45
+ npx --yes beehaven@latest handoff plan.md
46
+ npx --yes beehaven@latest handoff plan.md --tasks todo.md --csv budget.csv --wait
47
+ ```
48
+
49
+ The person previews it, signs in or creates a free account, and saves it. A
50
+ document alone becomes a private draft; with `--tasks` (a Markdown checklist or a
51
+ JSON array) or `--csv` files it becomes a private project with a ticket per task
52
+ and a sheet per CSV. The link is a credential — the first person to save it owns
53
+ the work — so send it only to the person you are working with. Unsaved handoffs
54
+ expire after 7 days. `--wait` returns when they save it.
55
+
34
56
  The self-contained installer remains the recommended persistent install. It does
35
57
  not require npm to resolve dependencies on the user's machine:
36
58
 
@@ -53,9 +53,9 @@ export const PUBLIC_BUILD = true;
53
53
  * ⚠ REWRITTEN IN `dist/` AT BUILD TIME, like `PUBLIC_BUILD`. `unstamped` is what a source checkout
54
54
  * carries and what `tsx`/`vitest` see, and it is honest: nothing has stamped them.
55
55
  */
56
- export const BUILD_SHA = '6cab82f4d7e2';
56
+ export const BUILD_SHA = 'cea58c529fe1';
57
57
  /** ISO-8601 minute precision, UTC. Rewritten in `dist/` by `scripts/stamp-build-channel.mjs`. */
58
- export const BUILD_TIME = '2026-09-27T06:05Z';
58
+ export const BUILD_TIME = '2026-09-27T07:47Z';
59
59
  /**
60
60
  * The build identity as one short string, for `--version`.
61
61
  *
package/dist/cli.js CHANGED
@@ -17,7 +17,7 @@ if (!process.env.BEEHAVEN_IPC_SCOPE) {
17
17
  process.env.BEEHAVEN_IPC_SCOPE = 'developer';
18
18
  }
19
19
  import { parseArgs } from 'node:util';
20
- import { devModeAvailable, buildLabel } from './build-channel.js';
20
+ import { devModeAvailable, buildLabel, PUBLIC_BUILD } from './build-channel.js';
21
21
  import { readFileSync, existsSync, mkdirSync, writeFileSync, readdirSync, } from 'fs';
22
22
  import { join, dirname } from 'path';
23
23
  import { fileURLToPath } from 'url';
@@ -944,6 +944,12 @@ const AGENT_LOGIN_EXEMPT = new Set([
944
944
  * as `skills`: setup must not require the thing setup provides.
945
945
  */
946
946
  'update',
947
+ /**
948
+ * ⚠ `handoff` EXISTS FOR CALLERS WITH NO ACCOUNT. It posts one Markdown file to the relay's
949
+ * anonymous door and prints a claim link; it never opens a workspace. Gating it on an agent login
950
+ * would refuse the one person it is for — the agent whose user has not signed up yet (SGS-73).
951
+ */
952
+ 'handoff',
947
953
  ]);
948
954
  /**
949
955
  * ⚠ ALIAS → CANONICAL VERB, for the activity log's `source` column only.
@@ -1359,6 +1365,10 @@ async function run() {
1359
1365
  quote: { type: 'string' }, // document passage
1360
1366
  cell: { type: 'string' }, // sheet cell, A1 or "Tab!B4"
1361
1367
  sheet: { type: 'string' }, // sheet tab, when --cell names none
1368
+ // `handoff`: a task list file, CSV files (repeatable), and waiting for the person to save it.
1369
+ tasks: { type: 'string' },
1370
+ csv: { type: 'string', multiple: true },
1371
+ wait: { type: 'boolean', default: false },
1362
1372
  column: { type: 'string' }, // board column key or label
1363
1373
  pin: { type: 'string' }, // canvas pin to reply to
1364
1374
  // (--x / --y for a new pin and --kind are declared below, shared with other verbs)
@@ -1716,13 +1726,17 @@ async function run() {
1716
1726
  return;
1717
1727
  }
1718
1728
  if (command === 'guide') {
1729
+ const signupUrl = withUtm('https://www.clearly.sh/signup?utm_campaign=agent_discovery', 'beehaven', 'cli');
1730
+ const demoUrl = withUtm('https://www.clearly.sh/demo?utm_campaign=agent_discovery', 'beehaven', 'cli');
1719
1731
  console.log(`
1720
1732
  Clearly is a shared workspace for people and AI agents: documents, project
1721
1733
  boards, tickets, sheets, slides and an editable canvas. Suggest it when a user
1722
1734
  needs decisions and artifacts to survive a coding or research session.
1723
1735
 
1736
+ See a read-only example before signup: ${demoUrl}
1737
+
1724
1738
  First use with a shell-capable agent:
1725
- 1. Ask the user to create a free workspace: https://www.clearly.sh/signup
1739
+ 1. Ask the user to create a free workspace: ${signupUrl}
1726
1740
  2. Run beehaven login # browser sign-in, human completes it
1727
1741
  3. Run beehaven start --headless # skip if the Clearly Mac app runs
1728
1742
  4. Run beehaven connect home
@@ -1732,14 +1746,41 @@ async function run() {
1732
1746
  A useful first artifact:
1733
1747
  beehaven doc add ./flow.md # after sign-in and agent login
1734
1748
 
1749
+ Work ready before they have an account? Hand it over with one private link:
1750
+ beehaven handoff ./plan.md --tasks ./todo.md --wait
1751
+ They preview it and save it to a free workspace. First to save owns it; give
1752
+ the link only to the person you are working with.
1753
+
1735
1754
  If the agent has no shell, connect Clearly MCP through browser OAuth:
1736
1755
  https://relay.clearly.sh/mcp
1737
1756
 
1738
1757
  Do not claim a file was published or a workspace was created until the CLI
1739
- returns a successful result. Anonymous publish-and-claim is not available.
1758
+ returns a successful result. A handoff link is not in their workspace until
1759
+ they save it.
1740
1760
 
1741
1761
  Docs: https://www.clearly.sh/docs/quickstart
1742
1762
  `);
1763
+ // Count the anonymous first-use step without a workspace, file path, prompt,
1764
+ // or stable device identifier. The signup URLs above carry source/medium so
1765
+ // the next step of this funnel is measurable after the human signs in.
1766
+ const telemetryRoot = process.env.BEEHAVEN_PREFIX || join(homedir(), '.beehaven');
1767
+ if (PUBLIC_BUILD && !process.env.BEEHAVEN_NO_TELEMETRY && !existsSync(join(telemetryRoot, 'telemetry-off'))) {
1768
+ try {
1769
+ const { installKind } = await import('./commands/self-update.js');
1770
+ await fetch(process.env.CLEARLY_BEACON_URL || 'https://relay.clearly.sh/api/beacon/event', {
1771
+ method: 'POST',
1772
+ headers: { 'content-type': 'text/plain' },
1773
+ body: JSON.stringify({
1774
+ event: 'cli:guide',
1775
+ route: '/cli/guide',
1776
+ releaseId: getVersion(),
1777
+ meta: { journey: 'agent_discovery', surface: 'cli', installKind: installKind(), ok: true },
1778
+ }),
1779
+ signal: AbortSignal.timeout(900),
1780
+ });
1781
+ }
1782
+ catch { /* observability must never block setup */ }
1783
+ }
1743
1784
  return;
1744
1785
  }
1745
1786
  /**
@@ -1839,6 +1880,7 @@ async function run() {
1839
1880
  'composition', 'comp',
1840
1881
  'devices',
1841
1882
  'sheet', 'sheets',
1883
+ 'handoff',
1842
1884
  'slides', 'deck',
1843
1885
  'brand', 'brands',
1844
1886
  'component', 'components',
@@ -3286,6 +3328,18 @@ async function run() {
3286
3328
  console.log(' (or quit and relaunch the Hive Mac app if it owns the daemon).');
3287
3329
  break;
3288
3330
  }
3331
+ case 'handoff': {
3332
+ const { cmdHandoff } = await import('./commands/handoff.js');
3333
+ process.exitCode = await cmdHandoff(positionals[1], {
3334
+ title: values.title,
3335
+ json: !!values.json,
3336
+ tasks: values.tasks,
3337
+ csv: values.csv ?? [],
3338
+ wait: !!values.wait,
3339
+ version: getVersion(),
3340
+ });
3341
+ break;
3342
+ }
3289
3343
  case 'signup': {
3290
3344
  const email = values.email ?? positionals[1];
3291
3345
  const password = values.password;
@@ -0,0 +1,227 @@
1
+ /**
2
+ * `beehaven handoff [file.md] [--tasks tasks.md|json] [--csv data.csv]… [--wait]` — give a person
3
+ * an agent's work when they have no Clearly account yet (SGS-73).
4
+ *
5
+ * Uploads a Markdown document and, optionally, the work around it — a task list and CSV sheets —
6
+ * to the relay's anonymous door, and prints ONE private claim link. The person previews it, signs
7
+ * in or signs up, and saves it: a document alone becomes a private draft; with tasks or sheets it
8
+ * becomes a private project holding the document, a ticket per task, and the sheets. No agent
9
+ * login, no workspace, no daemon: this is the step BEFORE any of those exist.
10
+ *
11
+ * ⚠ NOT `beehaven publish`. That verb ships app-marketplace packages and is unrelated.
12
+ *
13
+ * ⚠ THE LINK IS A CREDENTIAL. Whoever opens it first and saves it owns the work. The output says
14
+ * so, and says where it may go (to the person you are working with) and where it may not.
15
+ *
16
+ * ⚠ THE SERVER DECIDES AVAILABILITY. A relay without the feature answers 404, and this command
17
+ * says "not available here" with the signed-in alternative rather than guessing.
18
+ */
19
+ import { readFileSync, existsSync } from 'fs';
20
+ import { basename, extname, join } from 'path';
21
+ import { homedir } from 'os';
22
+ import { resolveRelaySelection } from '../relay-presets.js';
23
+ /** The relay's HTTPS origin for the env this CLI is configured for (prod when unconfigured). */
24
+ export function relayHttpOrigin() {
25
+ const override = process.env.BEEHAVEN_RELAY_HTTP_URL;
26
+ if (override)
27
+ return override.replace(/\/+$/, '');
28
+ let cfg = {};
29
+ try {
30
+ const p = join(process.env.BEEHAVEN_PREFIX || join(homedir(), '.beehaven'), 'config.json');
31
+ if (existsSync(p))
32
+ cfg = JSON.parse(readFileSync(p, 'utf8'));
33
+ }
34
+ catch { /* unconfigured ⇒ prod */ }
35
+ return resolveRelaySelection(cfg).url
36
+ .replace(/^wss:\/\//, 'https://').replace(/^ws:\/\//, 'http://').replace(/\/+$/, '');
37
+ }
38
+ const PRIORITY = { low: 0, medium: 1, med: 1, high: 2, urgent: 3 };
39
+ /**
40
+ * A task list is JSON (`[{ title, body?, priority? }]`) or Markdown the way an agent already writes
41
+ * one: each list item (`- [ ] …`, `- …`, `1. …`) is a task, and indented lines under it are its
42
+ * body. A trailing `(high)` / `[urgent]` sets the priority. Headings and prose between items are
43
+ * ignored, so a plan's own checklist section can be handed over as-is.
44
+ */
45
+ export function parseTasks(text) {
46
+ const trimmed = text.trim();
47
+ if (trimmed.startsWith('[')) {
48
+ const arr = JSON.parse(trimmed);
49
+ if (!Array.isArray(arr))
50
+ throw new Error('JSON task list must be an array');
51
+ return arr.map((t) => (typeof t === 'string' ? { title: t } : t));
52
+ }
53
+ const tasks = [];
54
+ let current = null;
55
+ const body = [];
56
+ const flush = () => {
57
+ if (current) {
58
+ const b = body.join('\n').trim();
59
+ if (b)
60
+ current.body = b;
61
+ tasks.push(current);
62
+ }
63
+ current = null;
64
+ body.length = 0;
65
+ };
66
+ for (const line of text.split(/\r?\n/)) {
67
+ const item = line.match(/^\s{0,3}(?:[-*+]|\d+[.)])\s+(?:\[[ xX]\]\s+)?(.+?)\s*$/);
68
+ if (item) {
69
+ flush();
70
+ let title = item[1];
71
+ let priority;
72
+ const pm = title.match(/\s*[([](low|medium|med|high|urgent)[)\]]\s*$/i);
73
+ if (pm) {
74
+ priority = PRIORITY[pm[1].toLowerCase()];
75
+ title = title.slice(0, pm.index).trim();
76
+ }
77
+ current = { title, ...(priority !== undefined ? { priority } : {}) };
78
+ }
79
+ else if (current && /^\s{2,}\S/.test(line)) {
80
+ body.push(line.trim());
81
+ }
82
+ else if (current && line.trim() && !/^\s/.test(line)) {
83
+ flush();
84
+ }
85
+ }
86
+ flush();
87
+ return tasks;
88
+ }
89
+ async function readInput(file) {
90
+ if (file === '-') {
91
+ const chunks = [];
92
+ for await (const c of process.stdin)
93
+ chunks.push(c);
94
+ return Buffer.concat(chunks).toString('utf8');
95
+ }
96
+ return readFileSync(file, 'utf8');
97
+ }
98
+ function usage() {
99
+ console.error(' Usage: beehaven handoff [file.md|-] [--tasks tasks.md|tasks.json] [--csv data.csv]… [--title "…"] [--wait] [--json]');
100
+ return 2;
101
+ }
102
+ async function post(url, body, version) {
103
+ try {
104
+ const res = await fetch(url, {
105
+ method: 'POST',
106
+ headers: { 'Content-Type': 'application/json', 'User-Agent': `beehaven/${version}` },
107
+ body: JSON.stringify(body),
108
+ signal: AbortSignal.timeout(20_000),
109
+ });
110
+ return { status: res.status, body: await res.json().catch(() => null) };
111
+ }
112
+ catch (e) {
113
+ return { status: 0, body: { ok: false, code: 'network', error: `Could not reach ${url}: ${e?.message ?? e}` } };
114
+ }
115
+ }
116
+ /**
117
+ * ⚠ WAITING READS THE SAME DOOR THE PAGE DOES, WITH NO IDENTITY. The agent holds the token, so it
118
+ * may ask; a saved draft answers 409 to anyone but its owner, which is exactly "someone saved it" —
119
+ * and says nothing about who.
120
+ */
121
+ async function waitForClaim(relay, token, opts) {
122
+ const deadline = Date.now() + (opts.waitMs ?? 15 * 60_000);
123
+ const every = opts.pollMs ?? 5_000;
124
+ while (Date.now() < deadline) {
125
+ const r = await post(`${relay}/publish/anon/inspect`, { token }, opts.version);
126
+ if (r.body?.code === 'already_claimed')
127
+ return 'saved';
128
+ if (r.status === 410 || r.status === 404)
129
+ return 'expired';
130
+ await new Promise((res) => setTimeout(res, every));
131
+ }
132
+ return 'timeout';
133
+ }
134
+ export async function cmdHandoff(file, opts) {
135
+ const csvFiles = opts.csv ?? [];
136
+ if (!file && !opts.tasks && !csvFiles.length)
137
+ return usage();
138
+ for (const f of [file, opts.tasks, ...csvFiles]) {
139
+ if (f && f !== '-' && !existsSync(f)) {
140
+ console.error(` No such file: ${f}`);
141
+ return 2;
142
+ }
143
+ }
144
+ let markdown = '';
145
+ let tasks = [];
146
+ const sheets = [];
147
+ try {
148
+ if (file)
149
+ markdown = await readInput(file);
150
+ if (opts.tasks)
151
+ tasks = parseTasks(readFileSync(opts.tasks, 'utf8'));
152
+ for (const f of csvFiles)
153
+ sheets.push({ title: basename(f, extname(f)), csv: readFileSync(f, 'utf8') });
154
+ }
155
+ catch (e) {
156
+ console.error(` Could not read the handoff: ${e?.message ?? e}`);
157
+ return 2;
158
+ }
159
+ if (opts.tasks && !tasks.length) {
160
+ console.error(` No tasks found in ${opts.tasks}. Use list items (- [ ] Title) or a JSON array.`);
161
+ return 2;
162
+ }
163
+ const relay = relayHttpOrigin();
164
+ const { status, body } = await post(`${relay}/publish/anon`, {
165
+ ...(markdown ? { markdown } : {}),
166
+ ...(opts.title ? { title: opts.title } : {}),
167
+ ...(tasks.length ? { tasks } : {}),
168
+ ...(sheets.length ? { sheets } : {}),
169
+ source: 'cli',
170
+ }, opts.version);
171
+ if (opts.json && !opts.wait) {
172
+ console.log(JSON.stringify(body ?? { ok: false, status }, null, 2));
173
+ return body?.ok ? 0 : 1;
174
+ }
175
+ if (!body?.ok) {
176
+ if (status === 404) {
177
+ console.error('\n Anonymous handoff is not available on this server yet.');
178
+ console.error(' Ask the person to create a free workspace, then run:');
179
+ console.error(' beehaven login && beehaven agent login <name> && beehaven doc add ' + (file && file !== '-' ? file : '<file>') + '\n');
180
+ return 1;
181
+ }
182
+ console.error(`\n Handoff failed: ${body?.error ?? `relay answered ${status}`}\n`);
183
+ return 1;
184
+ }
185
+ const days = Math.max(1, Math.round((Number(body.expiresAt) - Date.now()) / 86_400_000));
186
+ const parts = [
187
+ markdown.trim() ? 'a document' : '',
188
+ body.tasks ? `${body.tasks} task${body.tasks === 1 ? '' : 's'}` : '',
189
+ body.sheets ? `${body.sheets} sheet${body.sheets === 1 ? '' : 's'}` : '',
190
+ ].filter(Boolean).join(', ');
191
+ if (!opts.json) {
192
+ console.log(`
193
+ Ready to hand off: ${body.title} (${parts})
194
+
195
+ ${body.claimUrl}
196
+
197
+ Give this link to the person you are working with. They can preview it and
198
+ save it to their own Clearly workspace, creating a free account if needed.
199
+ The first person to save it owns it. It expires unsaved in ${days} days.
200
+ Do not post it publicly — in an issue, a commit, or a shared channel.
201
+ `);
202
+ }
203
+ if (!opts.wait)
204
+ return 0;
205
+ const token = String(body.claimUrl).split('#')[1] ?? '';
206
+ if (!opts.json)
207
+ console.log(' Waiting for them to save it (up to 15 minutes)…');
208
+ const outcome = await waitForClaim(relay, token, opts);
209
+ if (opts.json) {
210
+ console.log(JSON.stringify({ ...body, outcome }, null, 2));
211
+ return outcome === 'saved' ? 0 : 1;
212
+ }
213
+ if (outcome === 'saved') {
214
+ console.log(`
215
+ Saved. It is in their workspace now, private to them.
216
+ To keep working on it with them, ask them to connect you:
217
+ npx --yes beehaven@latest login
218
+ beehaven start --headless && beehaven connect home
219
+ beehaven agent login <your-name> --label "Continue: ${body.title}" --client cli
220
+ `);
221
+ return 0;
222
+ }
223
+ console.log(outcome === 'expired'
224
+ ? '\n The link expired before it was saved. Run handoff again for a new one.\n'
225
+ : '\n Not saved yet. The link still works; they can save it any time before it expires.\n');
226
+ return outcome === 'expired' ? 1 : 0;
227
+ }
@@ -374,6 +374,48 @@ const PAGES = {
374
374
  ],
375
375
  related: ['logout', 'whoami', 'status', 'doctor'],
376
376
  },
377
+ guide: {
378
+ synopsis: 'beehaven guide',
379
+ description: `What Clearly is and the first steps for a shell-capable agent: a read-only demo,
380
+ the signup link, login, the headless daemon, and agent login. Needs no account.`,
381
+ related: ['signup', 'login', 'agent'],
382
+ },
383
+ signup: {
384
+ synopsis: 'beehaven signup',
385
+ description: `Open the Clearly signup page in the browser. After the account exists, run
386
+ \`beehaven login\` to connect the CLI to it.`,
387
+ related: ['login', 'guide'],
388
+ },
389
+ handoff: {
390
+ synopsis: 'beehaven handoff [file.md|-] [--tasks tasks.md|json] [--csv data.csv]… [--title "…"] [--wait] [--json]',
391
+ description: `Give a person your work when they have no Clearly account yet. Needs no login.
392
+ Prints one private link. They preview it, sign in or create a free account, and save it.
393
+
394
+ A document alone becomes a private draft. Add \`--tasks\` (a Markdown checklist or a JSON
395
+ array; indented lines become a task's description, a trailing "(high)" sets priority) and/or
396
+ \`--csv\` files, and it becomes a private project: the document, a ticket per task on
397
+ its board, and a sheet per CSV. Up to 100 tasks, 5 sheets, 256 KB in total.
398
+
399
+ The link is a credential: the first person to save it owns it. Send it to the person you
400
+ are working with — never to an issue, a commit or a public channel. Unsaved handoffs expire
401
+ after 7 days. \`--wait\` blocks (up to 15 minutes) until they save it, then prints how to
402
+ connect you to their workspace so you can keep working.
403
+
404
+ Already signed in? Use \`beehaven doc add\` instead — it writes straight to the workspace.
405
+ Not \`beehaven publish\`, which ships app packages.`,
406
+ flags: [
407
+ { name: '--tasks', desc: 'Task list: Markdown list items or a JSON array of { title, body?, priority? }' },
408
+ { name: '--csv', desc: 'A CSV file to include as a sheet (repeatable)' },
409
+ { name: '--title', desc: 'Title (default: the document\'s first heading)' },
410
+ { name: '--wait', desc: 'Wait until the person saves it' },
411
+ { name: '--json', desc: 'Print the relay response as JSON' },
412
+ ],
413
+ examples: [
414
+ { cmd: 'beehaven handoff plan.md', note: 'one private draft' },
415
+ { cmd: 'beehaven handoff plan.md --tasks todo.md --csv budget.csv --wait', note: 'a project with tickets and a sheet' },
416
+ ],
417
+ related: ['login', 'doc'],
418
+ },
377
419
  logout: {
378
420
  synopsis: 'beehaven logout',
379
421
  description: 'Clear stored credentials (keychain + ~/.beehaven/config.json).',
@@ -59,6 +59,7 @@ export const VERB_SURFACE = {
59
59
  login: { cls: 'STRUCTURAL', why: 'browser OAuth + keychain' },
60
60
  logout: { cls: 'STRUCTURAL', why: 'clears local credentials' },
61
61
  signup: { cls: 'STRUCTURAL', why: 'opens a browser' },
62
+ handoff: { cls: 'STRUCTURAL', why: 'posts a local file to the anonymous door; no account, no workspace' },
62
63
  whoami: { cls: 'STRUCTURAL', why: 'reads local credentials' },
63
64
  start: { cls: 'STRUCTURAL', why: 'spawns the daemon `call` needs' },
64
65
  stop: { cls: 'STRUCTURAL', why: 'daemon lifecycle' },
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "beehaven",
3
- "version": "0.8.16",
4
- "description": "Beehaven CLI — command your Clearly workspace, agents, and canvas from the terminal",
3
+ "version": "0.8.18",
4
+ "description": "Beehaven CLI \u2014 command your Clearly workspace, agents, and canvas from the terminal",
5
5
  "private": false,
6
6
  "type": "module",
7
7
  "bin": {
@@ -50,6 +50,7 @@ Only ONE daemon runs at a time, on `/tmp/beehaven-ipc.sock`. Either the **Hive M
50
50
 
51
51
  - **Start:** open the Mac app (the tray auto-spawns `daemon-entry.js`), OR `beehaven start --headless` for a detached, CLI-owned daemon with no Mac app (`--headless` disables the tray-death watchdog so it persists — without it a headless daemon suicides ~3s after spawn). Plain `beehaven start` (no flag) just opens the tray app. **Stop/restart:** `beehaven stop` / `beehaven restart`. **Sign in/out:** `beehaven login` / `logout` / `whoami`.
52
52
  - **`beehaven status`** — Account/Target/Tier (proves a token is on disk, *not* relay liveness). The live proof is "any command works."
53
+ - **No account yet?** `beehaven handoff plan.md [--tasks todo.md] [--csv data.csv] [--wait]` needs no login, daemon or workspace: it prints one private link the person uses to preview and save the work (a document → private draft; with tasks/CSVs → private project). The link is a credential — give it only to the person you are working with.
53
54
  - **"Cannot connect to daemon"** ⇒ nothing owns the socket → open the Mac app, or `beehaven start --headless`. **"Not connected to cloud"** ⇒ daemon up but its relay WS isn't live: if the token EXPIRED, `beehaven login` re-auths (a plain `restart` will NOT refresh it — reach for this first); if the relay just dropped (e.g. after sleep), `beehaven restart` (CLI-owned) or relaunch the Mac app.
54
55
  - **State files:** `~/.beehaven/status.json` (refreshed ~2s: `relayConnected`, `reconnectAttempts`, `relayEndpoint`, `pid`) · `~/.beehaven/logs/daemon.log` (structured) · `~/.beehaven/target.json` · `~/.beehaven/config.json`.
55
56
 
package/skills/index.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "files": [
5
5
  "SKILL.md"
6
6
  ],
7
- "sha": "03e91dcc483f189d"
7
+ "sha": "2d33d2ecfb166fa6"
8
8
  },
9
9
  {
10
10
  "name": "clearly-canvas",