@clearlysh/beehaven 0.8.29 → 0.8.31
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/browser-login.js +8 -3
- package/dist/build-channel.js +2 -2
- package/dist/claude-bridge.js +12 -9
- package/dist/clearly.js +4 -3
- package/dist/cli.js +8 -7
- package/dist/commands/daemon-cmd.js +2 -0
- package/dist/commands/self-update.js +32 -5
- package/dist/commands/workspace.js +3 -3
- package/dist/daemon-client.js +10 -6
- package/dist/daemon-entry.js +1 -0
- package/dist/daemon-process.js +125 -0
- package/dist/daemon.js +107 -52
- package/dist/ipc-auth.js +3 -1
- package/dist/ipc-client.js +7 -6
- package/dist/ipc-path.js +92 -0
- package/dist/ipc-server.js +22 -25
- package/dist/npm-update.js +138 -0
- package/dist/setup-service.js +9 -4
- package/dist/watcher.js +9 -23
- package/dist/windows-hide.js +77 -0
- package/package.json +1 -1
- package/scripts/sync-skills.mjs +6 -0
- package/skills/brand-identity/SKILL.md +21 -5
- package/skills/clearly-canvas/SKILL.md +16 -4
- package/skills/clearly-canvas/authoring.md +4 -3
- package/skills/clearly-docs/SKILL.md +151 -0
- package/skills/clearly-init/SKILL.md +135 -0
- package/skills/clearly-workflows/SKILL.md +89 -0
- package/skills/index.json +37 -2
- package/skills/sticker-pack/SKILL.md +132 -0
- package/skills/visualize/SKILL.md +188 -0
package/dist/watcher.js
CHANGED
|
@@ -1,18 +1,19 @@
|
|
|
1
1
|
// ============================================================================
|
|
2
2
|
// Beehaven Office - Claude Code Event Watcher
|
|
3
|
-
// Primary:
|
|
3
|
+
// Primary: HTTP server on a local socket — /tmp/beehaven.sock, or a named pipe on Windows (ipc-path.ts)
|
|
4
4
|
// Fallback: Watches the JSONL events file written by the hook script
|
|
5
5
|
// ============================================================================
|
|
6
6
|
// chokidar is imported lazily so a missing bundle (e.g. when the daemon ships
|
|
7
7
|
// inside an Electron app whose node_modules layout doesn't include chokidar)
|
|
8
8
|
// degrades the file-watcher to a no-op rather than crashing the whole daemon.
|
|
9
9
|
// The primary Unix-socket path does not depend on chokidar.
|
|
10
|
-
import { createReadStream, existsSync, writeFileSync, statSync, renameSync,
|
|
10
|
+
import { createReadStream, existsSync, writeFileSync, statSync, renameSync, chmodSync, watch as fsWatch, } from 'fs';
|
|
11
11
|
import { createInterface } from 'readline';
|
|
12
12
|
import { createServer } from 'http';
|
|
13
13
|
import { EventEmitter } from 'events';
|
|
14
|
-
|
|
15
|
-
const
|
|
14
|
+
import { watcherSocketPath, eventsFilePath, clearStaleSocket, restrictSocketToOwner } from './ipc-path.js';
|
|
15
|
+
const EVENTS_FILE = eventsFilePath();
|
|
16
|
+
const SOCKET_PATH = watcherSocketPath();
|
|
16
17
|
const MAX_FILE_SIZE = 10 * 1024 * 1024; // 10 MB
|
|
17
18
|
export { SOCKET_PATH };
|
|
18
19
|
export class ClaudeWatcher extends EventEmitter {
|
|
@@ -32,14 +33,8 @@ export class ClaudeWatcher extends EventEmitter {
|
|
|
32
33
|
// ── Unix Domain Socket ───────────────────────────────────────────────
|
|
33
34
|
/** Start HTTP server on Unix domain socket for direct event delivery */
|
|
34
35
|
startSocket() {
|
|
35
|
-
// Remove stale socket file
|
|
36
|
-
|
|
37
|
-
if (existsSync(SOCKET_PATH))
|
|
38
|
-
unlinkSync(SOCKET_PATH);
|
|
39
|
-
}
|
|
40
|
-
catch {
|
|
41
|
-
/* may not exist */
|
|
42
|
-
}
|
|
36
|
+
// Remove a stale socket file (a Windows named pipe leaves none)
|
|
37
|
+
clearStaleSocket(SOCKET_PATH);
|
|
43
38
|
this.socketServer = createServer((req, res) => {
|
|
44
39
|
if (req.method !== 'POST' || req.url !== '/event') {
|
|
45
40
|
res.writeHead(404);
|
|
@@ -71,10 +66,7 @@ export class ClaudeWatcher extends EventEmitter {
|
|
|
71
66
|
// during startup). A post-bind chmod achieves the same security
|
|
72
67
|
// without the race.
|
|
73
68
|
this.socketServer.listen(SOCKET_PATH, () => {
|
|
74
|
-
|
|
75
|
-
chmodSync(SOCKET_PATH, 0o600);
|
|
76
|
-
}
|
|
77
|
-
catch { }
|
|
69
|
+
restrictSocketToOwner(SOCKET_PATH); // no-op for a named pipe
|
|
78
70
|
console.log(`[watcher] Socket listening on ${SOCKET_PATH}`);
|
|
79
71
|
});
|
|
80
72
|
}
|
|
@@ -84,13 +76,7 @@ export class ClaudeWatcher extends EventEmitter {
|
|
|
84
76
|
this.socketServer.close();
|
|
85
77
|
this.socketServer = null;
|
|
86
78
|
}
|
|
87
|
-
|
|
88
|
-
if (existsSync(SOCKET_PATH))
|
|
89
|
-
unlinkSync(SOCKET_PATH);
|
|
90
|
-
}
|
|
91
|
-
catch {
|
|
92
|
-
/* may already be gone */
|
|
93
|
-
}
|
|
79
|
+
clearStaleSocket(SOCKET_PATH);
|
|
94
80
|
}
|
|
95
81
|
// ── File Watcher (fallback) ──────────────────────────────────────────
|
|
96
82
|
/** Start file watcher as fallback for when socket isn't available */
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* windows-hide — no console windows flashing open on Windows (CAN2-56).
|
|
3
|
+
*
|
|
4
|
+
* ⚠⚠ WHY WINDOWS FLASHED. A Windows process with no console of its own — the detached daemon,
|
|
5
|
+
* or `beehaven` run by an agent or an app rather than in a terminal — that starts a CONSOLE
|
|
6
|
+
* program (cmd.exe for every `exec`/`execSync`, powershell, git, node, …) makes Windows create a
|
|
7
|
+
* brand-new console window for that child. It pops up and closes as the child exits: one flash per
|
|
8
|
+
* call, and the daemon makes calls on timers. Node prevents it per call with `windowsHide: true`;
|
|
9
|
+
* the CLI had ~140 child-process calls and one of them set it.
|
|
10
|
+
*
|
|
11
|
+
* ⚠ ONE CHOKEPOINT, NOT 140 CALL SITES. Imported FIRST by every entry point (`cli.ts`,
|
|
12
|
+
* `clearly.ts`, `daemon-entry.ts`), this wraps the `child_process` functions so `windowsHide`
|
|
13
|
+
* defaults to true for every caller in the process — this package, `@beehaven/core`, and any
|
|
14
|
+
* dependency — and `syncBuiltinESMExports()` makes ESM named imports (`import { spawn } …`) see
|
|
15
|
+
* the wrappers too. An explicit `windowsHide: false` is respected. Off Windows it does nothing.
|
|
16
|
+
*
|
|
17
|
+
* ⚠ `exec` / `execFile` carry `util.promisify.custom` (so `promisify(exec)` resolves
|
|
18
|
+
* `{ stdout, stderr }`). The wrappers forward it; without that, every `promisify(exec)` caller
|
|
19
|
+
* would silently start receiving a bare string.
|
|
20
|
+
*/
|
|
21
|
+
import { createRequire, syncBuiltinESMExports } from 'node:module';
|
|
22
|
+
import { promisify } from 'node:util';
|
|
23
|
+
const require = createRequire(import.meta.url);
|
|
24
|
+
const MARK = Symbol.for('beehaven.windowsHide');
|
|
25
|
+
/**
|
|
26
|
+
* A child_process call's arguments with `windowsHide: true` set, unless the caller chose a value.
|
|
27
|
+
* `takesArgv` = the second parameter may be an argv array (spawn, spawnSync, execFile,
|
|
28
|
+
* execFileSync, fork). Pure — exported for tests.
|
|
29
|
+
*/
|
|
30
|
+
export function withWindowsHide(args, takesArgv) {
|
|
31
|
+
const a = [...args];
|
|
32
|
+
for (let i = 1; i < a.length; i++) {
|
|
33
|
+
const v = a[i];
|
|
34
|
+
if (v && typeof v === 'object' && !Array.isArray(v)) {
|
|
35
|
+
if (v.windowsHide === undefined)
|
|
36
|
+
a[i] = { ...v, windowsHide: true };
|
|
37
|
+
return a;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
// No options object: it goes after the command (and its argv array), before any callback. An
|
|
41
|
+
// explicit `undefined`/`null` options slot is filled rather than shifted.
|
|
42
|
+
const at = takesArgv && Array.isArray(a[1]) ? 2 : 1;
|
|
43
|
+
if (at < a.length && (a[at] === undefined || a[at] === null))
|
|
44
|
+
a[at] = { windowsHide: true };
|
|
45
|
+
else
|
|
46
|
+
a.splice(at, 0, { windowsHide: true });
|
|
47
|
+
return a;
|
|
48
|
+
}
|
|
49
|
+
const WRAPPED = [
|
|
50
|
+
['spawn', true], ['spawnSync', true], ['execFile', true], ['execFileSync', true], ['fork', true],
|
|
51
|
+
['exec', false], ['execSync', false],
|
|
52
|
+
];
|
|
53
|
+
/** Wrap `child_process` once per process. Returns whether it is installed. `force` is for tests. */
|
|
54
|
+
export function installWindowsHide(opts = {}) {
|
|
55
|
+
if (process.platform !== 'win32' && !opts.force)
|
|
56
|
+
return false;
|
|
57
|
+
const cp = require('node:child_process');
|
|
58
|
+
if (cp[MARK])
|
|
59
|
+
return true;
|
|
60
|
+
for (const [name, takesArgv] of WRAPPED) {
|
|
61
|
+
const orig = cp[name];
|
|
62
|
+
if (typeof orig !== 'function')
|
|
63
|
+
continue;
|
|
64
|
+
const wrapper = function (...args) {
|
|
65
|
+
return orig.apply(this, withWindowsHide(args, takesArgv));
|
|
66
|
+
};
|
|
67
|
+
const custom = orig[promisify.custom];
|
|
68
|
+
if (typeof custom === 'function') {
|
|
69
|
+
wrapper[promisify.custom] = (...args) => custom.apply(orig, withWindowsHide(args, takesArgv));
|
|
70
|
+
}
|
|
71
|
+
cp[name] = wrapper;
|
|
72
|
+
}
|
|
73
|
+
cp[MARK] = true;
|
|
74
|
+
syncBuiltinESMExports();
|
|
75
|
+
return true;
|
|
76
|
+
}
|
|
77
|
+
installWindowsHide();
|
package/package.json
CHANGED
package/scripts/sync-skills.mjs
CHANGED
|
@@ -40,6 +40,12 @@ const SOURCES = {
|
|
|
40
40
|
'codebase-map': 'apps/mcp-server/plugin/skills/codebase-map/SKILL.md',
|
|
41
41
|
'clearly-workspace': 'apps/mcp-server/plugin/skills/clearly-workspace/SKILL.md',
|
|
42
42
|
'clearly-agent': 'apps/mcp-server/plugin/skills/clearly-agent/SKILL.md',
|
|
43
|
+
// CAN2-57 — the rest of the plugin's skills: one set across plugin, CLI and Mac app.
|
|
44
|
+
'visualize': 'apps/mcp-server/plugin/skills/visualize/SKILL.md',
|
|
45
|
+
'sticker-pack': 'apps/mcp-server/plugin/skills/sticker-pack/SKILL.md',
|
|
46
|
+
'clearly-docs': 'apps/mcp-server/plugin/skills/clearly-docs/SKILL.md',
|
|
47
|
+
'clearly-init': 'apps/mcp-server/plugin/skills/clearly-init/SKILL.md',
|
|
48
|
+
'clearly-workflows': 'apps/mcp-server/plugin/skills/clearly-workflows/SKILL.md',
|
|
43
49
|
};
|
|
44
50
|
|
|
45
51
|
let missing = 0;
|
|
@@ -64,15 +64,31 @@ handover. A mark of hand-placed béziers does not, and it shows.
|
|
|
64
64
|
| **Counter-form** | the shape is the NEGATIVE space between forms | clever marks that survive one colour |
|
|
65
65
|
| **Modular** | one unit repeated/rotated on a grid | systems, networks, collectives |
|
|
66
66
|
|
|
67
|
-
**Build it
|
|
67
|
+
**Build it from native layers, every number derived from the construction** — the way an
|
|
68
|
+
identity designer works in Figma, so each part of the mark stays a named, editable layer in the
|
|
69
|
+
brand colour (never one generated or hand-written SVG):
|
|
68
70
|
|
|
69
71
|
```jsonc
|
|
70
|
-
{"
|
|
71
|
-
"
|
|
72
|
+
{"batch":[
|
|
73
|
+
{"action":"frame.create","args":{"name":"Mark","x":120,"y":120,"w":240,"h":240}},
|
|
74
|
+
{"action":"canvas.create-node","args":{"type":"ellipse","name":"mark/ring","parentId":"Mark",
|
|
75
|
+
"x":20,"y":20,"w":200,"h":200,"fill":"#1B4DFF"}},
|
|
76
|
+
{"action":"canvas.create-node","args":{"type":"ellipse","name":"mark/counter","parentId":"Mark",
|
|
77
|
+
"x":70,"y":70,"w":100,"h":100,"fill":"#FFFFFF"}},
|
|
78
|
+
{"action":"vector.create","args":{"name":"mark/blade","parentId":"Mark","closed":true,"fill":"#1B4DFF",
|
|
79
|
+
"points":[{"x":120,"y":20},{"x":220,"y":120},{"x":120,"y":120}]}},
|
|
80
|
+
{"action":"canvas.group","args":{"ids":["mark/ring","mark/counter","mark/blade"],"name":"mark"}}
|
|
81
|
+
]}
|
|
72
82
|
```
|
|
73
83
|
|
|
74
|
-
|
|
75
|
-
|
|
84
|
+
Here R = 100: the counter is R/2, the blade's corners sit on the circle. Curves are
|
|
85
|
+
`vector.create` points with `hIn`/`hOut` control points (absolute coordinates) placed where the
|
|
86
|
+
construction says; a quarter-circle's handle sits 0.552·R from its anchor. A counter-form is a
|
|
87
|
+
ground-colour shape layered on top; `vector.boolean` makes a true cut-out when an editor is open.
|
|
88
|
+
Raw SVG (`type:"svg"`) is only for importing a mark the client already has.
|
|
89
|
+
|
|
90
|
+
⚠ **Wordmarks are real text nodes, or outlined paths** — and `<text>` inside an imported `svg`
|
|
91
|
+
node does NOT render headlessly (no system fonts are loaded).
|
|
76
92
|
|
|
77
93
|
**Test the mark against these before going further. A mark that fails any of them is not done:**
|
|
78
94
|
|
|
@@ -110,10 +110,19 @@ can read and edit artifact records, while canvas-specific actions use the CLI.
|
|
|
110
110
|
beehaven env
|
|
111
111
|
beehaven connect home
|
|
112
112
|
beehaven agent login <name> --label "Design <artifact>" --client cli
|
|
113
|
-
beehaven call canvas-perceive '{"compositionId":"<id>","
|
|
113
|
+
beehaven call canvas-perceive '{"compositionId":"<id>","outline":true}'
|
|
114
114
|
beehaven call canvas-catalog '{"query":"create-node"}'
|
|
115
115
|
```
|
|
116
116
|
|
|
117
|
+
Read a canvas the way you read a repository: `canvas-perceive {outline:true}` is `tree` (artboards,
|
|
118
|
+
sections, layer counts, first copy, EMPTY sections — a few hundred bytes); `{frame:"<id or name>"}`
|
|
119
|
+
opens one frame like a file (its layers indented, positions relative to their parent);
|
|
120
|
+
`{find:"Start free"}` is `grep` (copy, layer names or `#hex`, with each match's frame path). Use
|
|
121
|
+
`format:"json"` only when you need every field of every node. Every `canvas-act` write returns
|
|
122
|
+
`diagnostics` — a lint of the artboards it touched (text overflow, collisions, covered text,
|
|
123
|
+
contrast) — treat them like compiler errors and fix them in the next batch. `canvas.review` is still
|
|
124
|
+
the pixel check and the ticket gate.
|
|
125
|
+
|
|
117
126
|
A canvas is planned like a git repository: its own plan board, a WORK TICKET for the request, and
|
|
118
127
|
COMMITS — one ticket per coherent layer or section of a larger change.
|
|
119
128
|
`design-start {title, brandId?}` makes a PRIVATE DRAFT canvas, bound to the brand, with its private
|
|
@@ -133,7 +142,7 @@ builds on the last and can be reviewed alone. Build ONE commit per canvas-act ba
|
|
|
133
142
|
`canvas.review` of the artboard and carries
|
|
134
143
|
`"ticket":{"id":"<key>","status":"done","resolution":"<what changed>"}` — it lands only if that
|
|
135
144
|
review is clean or look; `result.ticket` says whether it did, and the ticket logs the canvas
|
|
136
|
-
revision and the layers the commit changed. Auto-layout, gradients, shadows and
|
|
145
|
+
revision and the layers the commit changed. Auto-layout, gradients, shadows, drawing and placeholders:
|
|
137
146
|
see "Technique" in [authoring.md](authoring.md).
|
|
138
147
|
|
|
139
148
|
`composition-board-get {compositionId}` reads the plan back. `design-start` with a project, ticket or
|
|
@@ -195,8 +204,11 @@ hand-author hundreds of coordinates in a chat message.
|
|
|
195
204
|
Check contrast on the actual ground. Use the user's real brand assets.
|
|
196
205
|
- Choose relevant imagery, a meaningful diagram, a product view or a deliberate
|
|
197
206
|
typographic treatment. Decorative blobs and random stock photos do not prove a
|
|
198
|
-
product claim.
|
|
199
|
-
|
|
207
|
+
product claim. Draw marks, icons, illustrations and diagrams yourself from native
|
|
208
|
+
layers (shapes, vector.create pen paths, text, groups), the way a designer works in
|
|
209
|
+
Figma, so every part stays named and editable. Raster generation suits photographic
|
|
210
|
+
imagery when an image tool is available. Raw SVG (an svg node, or a generated SVG)
|
|
211
|
+
is for importing an existing file or a requested SVG, never the default.
|
|
200
212
|
- Define repeated components once. Vary composition where the content changes.
|
|
201
213
|
A deck of identical cards has little rhythm even when every card aligns.
|
|
202
214
|
- Keep important copy and layers editable. Do not flatten the whole composition
|
|
@@ -123,14 +123,15 @@ deliverable requires them, with content order and hierarchy reconsidered for the
|
|
|
123
123
|
|
|
124
124
|
The same rules the in-app assistant works to (canvas-catalog returns them as `technique`).
|
|
125
125
|
|
|
126
|
-
- AUTO-LAYOUT: rows and stacks (nav links, button groups, card rows, form fields) are a frame whose children you create in reading order, then canvas.auto-layout { id, direction, gap, padding, align, justify, hug }. hug:false keeps the frame size, which a full-width bar needs with justify:"space-between"; set a child to layoutSizingH/V "fill" | "hug" | "fixed" with canvas.update-nodes.
|
|
126
|
+
- AUTO-LAYOUT: rows and stacks (nav links, button groups, card rows, form fields) are a frame whose children you create in reading order, then canvas.auto-layout { id, direction, gap, padding, align, justify, hug }. hug:false keeps the frame size, which a full-width bar needs with justify:"space-between"; set a child to layoutSizingH/V "fill" | "hug" | "fixed" with canvas.update-nodes. The frame stays live: create more children into it (parentId) and they flow in; layoutPositioning:"absolute" takes one out of the flow.
|
|
127
127
|
- GRADIENTS: gradient { kind, angle, stops: [{ offset, color }] } replaces the solid fill. angle is the direction the colour travels: 0 = left to right, 90 = top to bottom, 45 = top-left to bottom-right (not CSS degrees). Use two or three stops from the brand palette, and keep text off a busy gradient or check its contrast in canvas.review.
|
|
128
128
|
- SHADOWS AND BLUR: effects on any node. A soft card shadow is { type: "drop-shadow", x: 0, y: 4, blur: 16, spread: 0, color: "#0000001F" }; frosted glass is { type: "background-blur", blur: 16 }. Never fake a shadow with an extra rectangle.
|
|
129
|
-
-
|
|
129
|
+
- DRAWING: build every mark, icon, illustration and diagram shape from native layers, as a designer does in Figma, never as one generated SVG file. Primitives are canvas.create-node rect and ellipse; curves are pen paths, vector.create { name, parentId, points: [{ x, y, hIn?: { x, y }, hOut?: { x, y } }], closed, fill or stroke, strokeWidth }, where hIn/hOut are absolute control points: use few anchors with smooth handles, and refine with vector.read, vector.vertex.move and vector.handle.set. Round caps and joins: canvas.update-nodes patch { strokeCap: "round", strokeJoin: "round" }. Work back to front: ground, big silhouettes in flat brand colours, then features, then details and highlights; check the silhouette in canvas.review before detailing. Name layers by part ("fox/ear-left"), group each figure with canvas.group { ids, name } and set overlap with canvas.order { ids, op }. Make a cut-out by layering a ground-colour shape on top; vector.boolean { op } on selected paths needs an open editor. Keep one stroke weight per drawing. Photographic or painterly imagery is a placed raster. Raw SVG stays supported but is never the default: when an SVG is asked for, write the markup yourself as an svg node, canvas.create-node { type: "svg", name, w, h, svg } with <svg viewBox="0 0 W H"> matching w and h, or import existing markup the same way; never an SVG generation pipeline.
|
|
130
|
+
- PLACEHOLDERS: until real imagery exists, draw a native placeholder at the final size in a muted brand tone, never a flat accent block: a rect with the slot radius plus a simple motif (an ellipse sun, a vector.create mountain), grouped with canvas.group and named "<section> image placeholder", with any label in its own text node.
|
|
130
131
|
- SECTIONS: one frame per page section, named exactly as its ticket says, inside the page artboard. Build a section in one canvas-act batch that ends with canvas.review of the artboard and carries ticket: { id: "<its key>", status: "done", resolution: "<what changed>" }. The ticket moves only when that review is clean or look; result.ticket says whether it did. Then start the next section.
|
|
131
132
|
|
|
132
133
|
Use `arrow.create {from,to,routing,label}` for connected diagrams, so arrows remain bound
|
|
133
|
-
when nodes move.
|
|
134
|
+
when nodes move. Draw custom vector details natively (shapes, vector.create pen paths); raw SVG only imports an existing file. Keep labels as
|
|
134
135
|
native text so they remain editable and measurable.
|
|
135
136
|
|
|
136
137
|
Use `canvas.place {kind,id,x,y,w,h,name,parentId?}` to put a workspace object (document,
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: clearly-docs
|
|
3
|
+
description: >-
|
|
4
|
+
Read, search, edit and recover a company's documents in a Clearly workspace.
|
|
5
|
+
Use the seven-tool MCP filesystem for ordinary work; use the `beehaven` CLI
|
|
6
|
+
only for document history, diffs, and restore. Load this whenever the answer
|
|
7
|
+
depends on what the company has written down, or when you are about to write
|
|
8
|
+
something down for it. Triggers: "what do our docs say", "find the doc about
|
|
9
|
+
X", "update the spec", "write this up", "add it to the docs", "has this
|
|
10
|
+
changed", "what did they edit", "search the wiki", "company knowledge", "our
|
|
11
|
+
documentation", "the PRD", "the runbook".
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
> **Tool names below are written UNPREFIXED** (`clearly_read`). Your runtime may expose them
|
|
15
|
+
> with a server prefix — e.g. `mcp__plugin_clearly_clearly__clearly_read`. **Match by suffix,
|
|
16
|
+
> not by exact name**: a skill written against the bare name resolves to nothing otherwise, and
|
|
17
|
+
> the failure looks like "the tool doesn't exist" rather than "the name is decorated".
|
|
18
|
+
>
|
|
19
|
+
> **If no such tool is callable at all**, the Clearly MCP server isn't authorised in this
|
|
20
|
+
> session — these skills still LIST when it isn't, so you find out by firing a dead call.
|
|
21
|
+
> Authorise it (`/mcp`, or `claude mcp`).
|
|
22
|
+
|
|
23
|
+
## What this is
|
|
24
|
+
|
|
25
|
+
The workspace is a **filesystem**, not a chat surface with a search box.
|
|
26
|
+
|
|
27
|
+
```
|
|
28
|
+
~/ every document
|
|
29
|
+
~/clearly.md what is settled for the whole workspace
|
|
30
|
+
~/<Folder>/ one directory per project
|
|
31
|
+
~/<Folder>/clearly.md what is settled for that folder
|
|
32
|
+
~/.archive/ put away — hidden, readable, read-only, still searched
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Folders are projects. Documents are `.md` files named `<KEY> <Title>.md` — e.g.
|
|
36
|
+
`CLR-42 Pricing.md`. The key is an address: `cat ~/CLR-42.md` works from anywhere, and so does
|
|
37
|
+
the full name and the bare title.
|
|
38
|
+
|
|
39
|
+
## The loop
|
|
40
|
+
|
|
41
|
+
**Find it, then open it.** `clearly_glob` answers "what is it called", `clearly_grep` answers
|
|
42
|
+
"where is this mentioned", and `clearly_read` opens what either one returned.
|
|
43
|
+
|
|
44
|
+
```
|
|
45
|
+
clearly_glob { pattern: "~/**/*.md" }
|
|
46
|
+
clearly_grep { pattern: "pricing", limit: 5 }
|
|
47
|
+
clearly_read { target: "~/Prepress/CLR-42 Pricing.md" }
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
⚠ **`clearly_grep` takes a REGEX, unparsed** — `|`, `(`, `$` and quotes mean what they mean in a
|
|
51
|
+
regular expression. Nothing shells out, so nothing eats them as syntax first.
|
|
52
|
+
|
|
53
|
+
⚠ **Archived documents are excluded from both.** When any matched, the result carries
|
|
54
|
+
`archivedHidden` saying how many — mention that rather than reporting a clean miss. Search the
|
|
55
|
+
archive by naming it: `clearly_grep { pattern: …, path: "~/.archive" }`.
|
|
56
|
+
|
|
57
|
+
**Structural questions — a date range, a null body, counts — have no keyword to search for**, so
|
|
58
|
+
they go to SQL over the workspace's own tables. That is a CLI job, not an MCP tool:
|
|
59
|
+
|
|
60
|
+
```
|
|
61
|
+
beehaven call bash '{"command":"sql SELECT title, updated_at FROM notes WHERE project_id IS NULL LIMIT 20"}'
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Read-only, one statement, credential columns redacted.
|
|
65
|
+
|
|
66
|
+
## ⚠⚠ Read before you write, and check what changed
|
|
67
|
+
|
|
68
|
+
This is the part that makes documentation stay true, and it is the part agents skip.
|
|
69
|
+
|
|
70
|
+
The MCP read/edit/replace tools cover the current body. Document history is a separate CLI surface:
|
|
71
|
+
|
|
72
|
+
- **`beehaven call document-status '{"limit":20}'`** lists recent changes. `mine: true` is the
|
|
73
|
+
certain signal that the active agent made the latest change. `by: "unknown"` means the log has
|
|
74
|
+
no author; it may be a browser edit or an older unstamped agent write, so do not claim a person
|
|
75
|
+
made it.
|
|
76
|
+
- **`beehaven call document-diff '{"id":"<documentId>"}'`** follows up with added and removed
|
|
77
|
+
lines. Pass a `versionId` from status to compare against a specific checkpoint.
|
|
78
|
+
|
|
79
|
+
Run status when you resume a conversation, and whenever they say something implying the document
|
|
80
|
+
has moved — *"I rewrote that"*, *"take another look"*.
|
|
81
|
+
|
|
82
|
+
⚠ **Their edit wins, always.** If a document now disagrees with something you concluded
|
|
83
|
+
earlier, your conclusion is the stale one. You are keeping their documents, not defending your
|
|
84
|
+
own record. Never quietly restore what they took out.
|
|
85
|
+
|
|
86
|
+
## Editing
|
|
87
|
+
|
|
88
|
+
**`clearly_edit` (find/replace) is the default. `clearly_replace` REPLACES the whole document.**
|
|
89
|
+
|
|
90
|
+
To change one line of a 12,000-character document, `replace` makes you reproduce all 12,000 — and
|
|
91
|
+
anything you fail to retype is gone with no error. It is also a lost-update machine: it writes
|
|
92
|
+
whatever you last read, overwriting anything the human changed in between.
|
|
93
|
+
|
|
94
|
+
- `clearly_edit` requires the `find` text to match **exactly once**. Ambiguity is REFUSED with a
|
|
95
|
+
count, never guessed — an agent that edits the first of three matches has changed something
|
|
96
|
+
nobody looked at.
|
|
97
|
+
- Several independent document edits can be sent in one tool call with `batch`, but each batch
|
|
98
|
+
row is still one exact `{ target, find, replace, all? }` operation. There is no `multi_edit`
|
|
99
|
+
field and the batch is not transactional; inspect the per-item results and retry only failures.
|
|
100
|
+
- `clearly_replace` does not accept `expected_content` or a commit-message field. If you must replace
|
|
101
|
+
a whole existing document, re-read it immediately before writing and verify the returned body.
|
|
102
|
+
Prefer `clearly_edit` whenever a precise passage change can express the job.
|
|
103
|
+
|
|
104
|
+
## Comments and @mentions — say it beside the work, not instead of it
|
|
105
|
+
|
|
106
|
+
A comment is ABOUT something; an edit changes it. When you have a question or a note for a person,
|
|
107
|
+
leave it where they will see it, on the thing itself:
|
|
108
|
+
|
|
109
|
+
```
|
|
110
|
+
clearly_comment { target: "CLR-42", content: "Is this still the Q3 number? @sam" }
|
|
111
|
+
clearly_comment { target: "<sheetId>", content: "Double-counts refunds", fields: { cell: "Plan!D12" } }
|
|
112
|
+
clearly_comment { target: "<deckId>", content: "Tighten this headline", fields: { slide: 3 } }
|
|
113
|
+
clearly_read { type: "comment", target: "SS-41" } # the thread, on any item
|
|
114
|
+
clearly_read { type: "comment", target: "@me" } # where YOU were @mentioned
|
|
115
|
+
clearly_edit { type: "comment", target: "<mentionId>", changes: { acknowledged: true } }
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
`target` is the item — a document, sheet, deck, ticket, board, thought or canvas, by id, key or exact
|
|
119
|
+
title. Anchor with `fields.quote` (document), `slide` (deck, required), `cell` (sheet), `column`
|
|
120
|
+
(board) or `pinId` / `x`,`y` (canvas); an anchor the item cannot carry is refused, never dropped.
|
|
121
|
+
|
|
122
|
+
`@handle` mentions a person (notified) or an agent (queued in its inbox). Get real handles with
|
|
123
|
+
`clearly_read { type: "comment", target, includeMentionables: true }`. The write's result lists
|
|
124
|
+
`mentions`, and `unmatchedMentions` for any handle that reached **no one** — say so, do not assume.
|
|
125
|
+
Acknowledge a mention once you have acted on it, or it comes back in every session.
|
|
126
|
+
|
|
127
|
+
## `clearly.md` — what is settled
|
|
128
|
+
|
|
129
|
+
Not a document like the others. It holds what this place IS (the work, the constraints, the
|
|
130
|
+
vocabulary, the decisions that stopped moving) and **how this person wants to be worked with** —
|
|
131
|
+
length, tone, format, and what irritates them, which is said once in passing and worth more than
|
|
132
|
+
anything they praise.
|
|
133
|
+
|
|
134
|
+
Fold a conclusion into it once it has stopped moving. Reach for `edit`, never `replace`.
|
|
135
|
+
|
|
136
|
+
## Report what MOVED
|
|
137
|
+
|
|
138
|
+
*"You added a paragraph about the 12-hour window to Pricing and removed the line about three
|
|
139
|
+
credits"* is a report. *"I've updated the note"* is not — it costs a turn and says nothing they
|
|
140
|
+
could not have assumed.
|
|
141
|
+
|
|
142
|
+
## What not to do
|
|
143
|
+
|
|
144
|
+
- **Do not tidy unasked.** A document that looks redundant or superseded is still theirs. If two
|
|
145
|
+
say the same thing, say so and let them choose.
|
|
146
|
+
- **`delete` archives, it does not erase** — the document moves to `~/.archive/`, stays readable
|
|
147
|
+
and comes back. Report it as **archived**; "deleted" makes someone believe something is gone
|
|
148
|
+
that is not.
|
|
149
|
+
- **Do not invent a media line.** `` renders a photo, audio or video by file
|
|
150
|
+
extension. You cannot upload, so never write one pointing at a URL you guessed — it renders
|
|
151
|
+
broken, which reads as their photo being lost. Move existing lines; never fabricate one.
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: clearly-init
|
|
3
|
+
description: >-
|
|
4
|
+
Connect a Clearly workspace to Claude Code or Codex and prove both the MCP and
|
|
5
|
+
CLI paths work. Prefer browser OAuth for remote HTTP clients; when an agent-bound
|
|
6
|
+
bearer credential is required, mint and install it with the active `beehaven`
|
|
7
|
+
identity. Use FIRST whenever a Clearly tool is refused for auth, returns nothing
|
|
8
|
+
on a workspace that should have content, or the user has never connected.
|
|
9
|
+
Triggers: "connect Clearly", "set up Clearly", "sign in to Clearly",
|
|
10
|
+
"authenticate", "install the MCP", "add the MCP token", "it says unauthorized",
|
|
11
|
+
"the tools are not showing up", "/mcp shows nothing", "which workspace am I in".
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
> **Tool names below are written UNPREFIXED** (`clearly_read`). Your runtime may
|
|
15
|
+
> expose them with a server prefix — e.g.
|
|
16
|
+
> `mcp__plugin_clearly_clearly-staging__clearly_read`. **Match by suffix, not by
|
|
17
|
+
> exact name**: a skill written against the bare name resolves to nothing otherwise, and
|
|
18
|
+
> the failure looks like "the tool doesn't exist" rather than "the name is decorated".
|
|
19
|
+
>
|
|
20
|
+
> **If no such tool is callable at all**, the Clearly MCP server isn't authorised in this
|
|
21
|
+
> session — note that these skills still LIST when it isn't, so you find out by firing a
|
|
22
|
+
> dead call. Authorise it (`/mcp`, or `claude mcp`), or if you have a shell, use the
|
|
23
|
+
> `beehaven` CLI and its own `clearly-canvas` skill instead.
|
|
24
|
+
|
|
25
|
+
# `/clearly:init` — connect a Clearly workspace to Claude Code or Codex
|
|
26
|
+
|
|
27
|
+
Run this once per machine. The plugin's `.mcp.json` points at
|
|
28
|
+
`https://relay.clearly.sh/mcp`; complete authentication and verify the connection before doing
|
|
29
|
+
workspace work.
|
|
30
|
+
|
|
31
|
+
## Steps
|
|
32
|
+
|
|
33
|
+
### 1. Authenticate (OAuth — browser sign-in)
|
|
34
|
+
|
|
35
|
+
The plugin already wires the MCP server. Prefer browser OAuth:
|
|
36
|
+
|
|
37
|
+
- **In Claude Code (recommended)**: run `/mcp`, select `clearly`, and choose **Authenticate**. A browser window opens for sign-in; approve and it closes itself. No token.
|
|
38
|
+
- **From the shell**: `claude mcp login clearly` — same browser sign-in flow.
|
|
39
|
+
- **In Codex**: `codex mcp login clearly`. If the server was added outside the plugin first, run
|
|
40
|
+
`codex mcp add clearly --url https://relay.clearly.sh/mcp`, then log in.
|
|
41
|
+
|
|
42
|
+
The sign-in grants scoped access (`rpc:read` for search and read; add `rpc:write` to let the
|
|
43
|
+
agent write documents and create boards or tickets). Sign out or revoke with the client's MCP
|
|
44
|
+
logout command, or from Settings → **Developers** in the app.
|
|
45
|
+
|
|
46
|
+
### Agent-bound token path (CLI and automation)
|
|
47
|
+
|
|
48
|
+
Use this when the client cannot complete OAuth or when the credential must act as a specific agent
|
|
49
|
+
identity. Sign in and select the identity first:
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
beehaven agent login <agent-name>
|
|
53
|
+
beehaven connect home
|
|
54
|
+
beehaven agent whoami
|
|
55
|
+
beehaven mcp install --client codex
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
The installer writes through the client's own config command and never prints the token. Restart
|
|
59
|
+
the client afterward. A static token normally carries `rpc:read` and `rpc:write`; `agent:ask` is
|
|
60
|
+
OAuth-only and `rpc:admin` is reserved for configured platform admins. Never paste a bearer token
|
|
61
|
+
into a document, ticket, canvas, log, or chat response.
|
|
62
|
+
|
|
63
|
+
> **Working in more than one workspace?** A client stores one credential per server entry, so two
|
|
64
|
+
> workspaces behind a single entry collide. Use `https://relay.clearly.sh/mcp/w/<workspaceId>` as a
|
|
65
|
+
> second entry — see [the MCP docs](https://clearly.sh/docs/mcp).
|
|
66
|
+
|
|
67
|
+
### 2. Verify
|
|
68
|
+
|
|
69
|
+
Run `/mcp` in Claude Code, or `codex mcp list` in Codex. You should see `clearly` authenticated
|
|
70
|
+
with **7 tools**:
|
|
71
|
+
|
|
72
|
+
**Seven MCP tools, split by what they DO rather than by what they act on.** The four CRUD tools
|
|
73
|
+
cover every artifact type — document, canvas, sheet, deck, board, ticket, project — so the tool fixes
|
|
74
|
+
whether it reads or writes and `type` says what it touches.
|
|
75
|
+
|
|
76
|
+
- **`clearly_catalog`** — optional, lazy machine-readable schemas. Use `{ type, operation }` before a type-specific write/edit; omit both for the compact capability overview.
|
|
77
|
+
- **`clearly_read` / `clearly_create` / `clearly_edit` / `clearly_replace` / `clearly_delete`** — open one, make a new one, change part of one, overwrite one's whole body, archive one. Each takes a uniform same-operation `batch`.
|
|
78
|
+
- **`clearly_comment`** — comment on any item (with @mentions), or send and reply to messages.
|
|
79
|
+
- **`clearly_grep` / `clearly_glob`** — find by content, find by name. Both span notes and code.
|
|
80
|
+
|
|
81
|
+
Addressed by `target`: a path (`~/Q1/Plan.md`), a key (`CLR-42`) or an id. Folders are projects,
|
|
82
|
+
documents are `.md`, canvases are `.scene.json` — the extension names the type, and you pass `type`
|
|
83
|
+
when it does not.
|
|
84
|
+
|
|
85
|
+
**Everything else is the `beehaven` CLI**, by action name — the canvas, the Company Brain, skills,
|
|
86
|
+
scheduling, stickers, billing. `beehaven actions` lists them; `beehaven call <action> '<json>'` runs
|
|
87
|
+
one:
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
beehaven call context-search '{"query":"per-seat billing","scope":"org"}'
|
|
91
|
+
beehaven call canvas-act '{"compositionId":"c_…","batch":[…]}'
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
⚠ **That split is deliberate, not a gap.** A single tool that runs whatever action the caller names
|
|
95
|
+
spans safe and unsafe operations at once — a connector-directory rejection criterion, and one the
|
|
96
|
+
criteria explicitly refuse to let a description paper over. So the generic door is the shell, where
|
|
97
|
+
Claude Code already lives and where every call is attributed to your agent.
|
|
98
|
+
|
|
99
|
+
> ⚠ **Everything requires authentication.** There is no anonymous surface: as of 2026-09-11 both
|
|
100
|
+
> `tools/list` and `tools/call` resolve a credential first, and every credential is bound to an
|
|
101
|
+
> agent. A 401 carries an OAuth challenge, so a capable client can recover from it by itself —
|
|
102
|
+
> but a caller with no token sees no catalog, by design.
|
|
103
|
+
|
|
104
|
+
## Install the CLI — the other half of the surface
|
|
105
|
+
|
|
106
|
+
The seven MCP tools cover reading, writing and searching artifacts. **Everything else — the canvas,
|
|
107
|
+
the Company Brain, skills, stickers — is reached with `beehaven`**, and the skills in this plugin
|
|
108
|
+
assume it is on PATH. Claude Code has a shell, so this is one command:
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
curl -fsSL https://clearly.sh/install.sh | sh
|
|
112
|
+
beehaven login # once, opens a browser
|
|
113
|
+
beehaven status # daemon + relay, both should read UP
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
⚠ **`beehaven: command not found` is the failure to expect**, and it does not look like a missing
|
|
117
|
+
install — it looks like the skill being wrong. If a `beehaven call …` line fails that way, install
|
|
118
|
+
it rather than reaching for an MCP tool that does not exist.
|
|
119
|
+
|
|
120
|
+
⚠ **Do not restart the user's daemon to fix something.** It is theirs; `beehaven status` tells you
|
|
121
|
+
what is wrong and the message says which command fixes it.
|
|
122
|
+
|
|
123
|
+
Then load the usage skill: it teaches the Company Brain + self-prompting loops — see `clearly-workflows`.
|
|
124
|
+
|
|
125
|
+
A quick functional test:
|
|
126
|
+
> "Map my Clearly workspace's brain using the clearly MCP."
|
|
127
|
+
|
|
128
|
+
If Claude calls `clearly_catalog` and gets back the artifact types with their capabilities, the connection is live. It is a connection check and schema lookup, not a required preflight for ordinary reads/deletes.
|
|
129
|
+
|
|
130
|
+
## Troubleshooting
|
|
131
|
+
|
|
132
|
+
- **`Missing scope "rpc:write"`** — the sign-in didn't grant write access. Run `claude mcp logout clearly`, then re-authenticate (`/mcp` → **Authenticate**) and approve the write scope.
|
|
133
|
+
- **`Not authenticated` / calls rejected** — the session expired or was revoked. Re-authenticate with `claude mcp login clearly` (or `/mcp` → **Authenticate**).
|
|
134
|
+
- **`clearly` shows in `/mcp` but as unauthenticated** — the endpoint is preconfigured by the plugin; you still need to complete the browser sign-in. Select `clearly` → **Authenticate**.
|
|
135
|
+
- **`Network error reaching ...`** — relay.clearly.sh might be down (rare); check https://clearly.sh status page.
|