Compare commits

...

4 Commits

Author SHA1 Message Date
Peter Steinberger
36659bed85 style(test): format gif fixtures 2026-01-03 11:28:53 +01:00
Peter Steinberger
644db170dd docs(changelog): add bear-notes entry 2026-01-03 11:25:11 +01:00
Peter Steinberger
f97953f15c fix(skills): correct bear-notes usage 2026-01-03 11:25:08 +01:00
Claude
95b3cad101
feat(skills): add bear-notes skill using grizzly CLI 2026-01-03 04:46:15 +00:00
3 changed files with 116 additions and 13 deletions

View File

@ -30,6 +30,7 @@
- CLI: add remote gateway client config (gateway.remote.*) with Bonjour-assisted discovery.
- Skills: allow `bun` as a node manager for skill installs.
- Skills: add `things-mac` (Things 3 CLI) for read/search plus add/update via URL scheme.
- Skills: add `bear-notes` (Bear) skill via grizzly CLI (#120) — thanks @tylerwince.
- Tests: add a Docker-based onboarding E2E harness.
- Tests: harden wizard E2E flows for reset, providers, skills, and remote non-interactive runs.
- Browser tools: add remote CDP URL support, Linux launcher options (`executablePath`, `noSandbox`), and surface `cdpUrl` in status.

View File

@ -0,0 +1,79 @@
---
name: bear-notes
description: Create, search, and manage Bear notes via grizzly CLI.
homepage: https://bear.app
metadata: {"clawdis":{"emoji":"🐻","os":["darwin"],"requires":{"bins":["grizzly"]},"install":[{"id":"go","kind":"go","module":"github.com/tylerwince/grizzly/cmd/grizzly@latest","bins":["grizzly"],"label":"Install grizzly (go)"}]}}
---
# Bear Notes
Use `grizzly` to create, read, and manage notes in Bear on macOS.
Requirements
- Bear app installed and running
- For some operations (add-text, tags, open-note --selected), a Bear app token (stored in `~/.config/grizzly/token`)
## Getting a Bear Token
For operations that require a token (add-text, tags, open-note --selected), you need an authentication token:
1. Open Bear → Help → API Token → Copy Token
2. Save it: `echo "YOUR_TOKEN" > ~/.config/grizzly/token`
## Common Commands
Create a note
```bash
echo "Note content here" | grizzly create --title "My Note" --tag work
grizzly create --title "Quick Note" --tag inbox < /dev/null
```
Open/read a note by ID
```bash
grizzly open-note --id "NOTE_ID" --enable-callback --json
```
Append text to a note
```bash
echo "Additional content" | grizzly add-text --id "NOTE_ID" --mode append --token-file ~/.config/grizzly/token
```
List all tags
```bash
grizzly tags --enable-callback --json --token-file ~/.config/grizzly/token
```
Search notes (via open-tag)
```bash
grizzly open-tag --name "work" --enable-callback --json
```
## Options
Common flags:
- `--dry-run` — Preview the URL without executing
- `--print-url` — Show the x-callback-url
- `--enable-callback` — Wait for Bear's response (needed for reading data)
- `--json` — Output as JSON (when using callbacks)
- `--token-file PATH` — Path to Bear API token file
## Configuration
Grizzly reads config from (in priority order):
1. CLI flags
2. Environment variables (`GRIZZLY_TOKEN_FILE`, `GRIZZLY_CALLBACK_URL`, `GRIZZLY_TIMEOUT`)
3. `.grizzly.toml` in current directory
4. `~/.config/grizzly/config.toml`
Example `~/.config/grizzly/config.toml`:
```toml
token_file = "~/.config/grizzly/token"
callback_url = "http://127.0.0.1:42123/success"
timeout = "5s"
```
## Notes
- Bear must be running for commands to work
- Note IDs are Bear's internal identifiers (visible in note info or via callbacks)
- Use `--enable-callback` when you need to read data back from Bear
- Some operations require a valid token (add-text, tags, open-note --selected)

View File

@ -80,12 +80,34 @@ describe("web media loading", () => {
// Create a minimal valid GIF (1x1 pixel)
// GIF89a header + minimal image data
const gifBuffer = Buffer.from([
0x47, 0x49, 0x46, 0x38, 0x39, 0x61, // GIF89a
0x01, 0x00, 0x01, 0x00, // 1x1 dimensions
0x00, 0x00, 0x00, // no global color table
0x2c, 0x00, 0x00, 0x00, 0x00, // image descriptor
0x01, 0x00, 0x01, 0x00, 0x00, // 1x1 image
0x02, 0x01, 0x44, 0x00, 0x3b, // minimal LZW data + trailer
0x47,
0x49,
0x46,
0x38,
0x39,
0x61, // GIF89a
0x01,
0x00,
0x01,
0x00, // 1x1 dimensions
0x00,
0x00,
0x00, // no global color table
0x2c,
0x00,
0x00,
0x00,
0x00, // image descriptor
0x01,
0x00,
0x01,
0x00,
0x00, // 1x1 image
0x02,
0x01,
0x44,
0x00,
0x3b, // minimal LZW data + trailer
]);
const file = path.join(os.tmpdir(), `clawdis-media-${Date.now()}.gif`);
@ -102,18 +124,19 @@ describe("web media loading", () => {
it("preserves GIF from URL without JPEG conversion", async () => {
const gifBytes = new Uint8Array([
0x47, 0x49, 0x46, 0x38, 0x39, 0x61,
0x01, 0x00, 0x01, 0x00,
0x00, 0x00, 0x00,
0x2c, 0x00, 0x00, 0x00, 0x00,
0x01, 0x00, 0x01, 0x00, 0x00,
0x02, 0x01, 0x44, 0x00, 0x3b,
0x47, 0x49, 0x46, 0x38, 0x39, 0x61, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00,
0x00, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x02,
0x01, 0x44, 0x00, 0x3b,
]);
const fetchMock = vi.spyOn(globalThis, "fetch").mockResolvedValueOnce({
ok: true,
body: true,
arrayBuffer: async () => gifBytes.buffer.slice(gifBytes.byteOffset, gifBytes.byteOffset + gifBytes.byteLength),
arrayBuffer: async () =>
gifBytes.buffer.slice(
gifBytes.byteOffset,
gifBytes.byteOffset + gifBytes.byteLength,
),
headers: { get: () => "image/gif" },
status: 200,
} as Response);