Skip to content

Commit 49289bb

Browse files
groksrcclaude
andcommitted
docs(skill): permalink shapes and workspace-qualified project syntax
After digging into basic-memory's recent workspace-routing work (PRs #790, #795, #801, #803, #807, #808 in basicmachines-co/basic-memory), the canonical permalink can carry up to three levels of routing — short ('folder/note'), project-qualified ('project/folder/note'), and workspace-qualified ('workspace/project/folder/note'). The read/write tools accept all three shapes, and the returned permalink from bm_write self-routes for follow-up reads when written with a workspace-qualified project. Two updates land in this commit: 1. New "Permalinks" section in SKILL.md documenting the three shapes with a worked table and the round-trip property. Memory:// URLs follow the same shapes and now get covered in the same section (the old one-line "Memory URLs" section is folded in). 2. The "Cross-project routing" section now explains that the `project` argument itself accepts workspace-qualified syntax ('personal/main', 'team-paul/research') — not only project_id can disambiguate workspaces. project_id is still highlighted as the most durable form (survives renames). The recipe at the bottom of SKILL.md is updated to leverage the permalink round-trip: write with workspace-qualified `project=`, capture the returned (workspace-qualified) permalink, then read it back with no `project` arg needed. Updates the [0.3.0] CHANGELOG entry to mention the Permalinks section addition. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 8434abc commit 49289bb

2 files changed

Lines changed: 35 additions & 20 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
99
### Added
1010
- **Per-call project routing on every `bm_*` tool.** All eight tools now accept optional `project` (name) and `project_id` (UUID from `bm_projects`) parameters. The agent can write or read against a project other than the Hermes-configured one — useful when the user asks to write into a different cloud project (e.g. a personal `main` project) without reconfiguring the plugin. `project_id` takes precedence over `project`; both fall back to the configured default when omitted. Workspace routing is handled transparently by BM via `project_id` — no separate workspace parameter is needed.
1111
- **`bm_projects` and `bm_workspaces` agent tools.** Promotes the discovery logic previously available only as `/bm-project` and `/bm-workspace` slash commands to agent-facing tools. `bm_projects` returns JSON with `name` and `external_id` (UUID) per project so the agent can hand the UUID to `bm_write` / `bm_read` / etc. via `project_id` — the unambiguous form across cloud workspaces. `bm_workspaces` lists BM Cloud workspaces (name, type, role, default flag). Together with per-call routing, these unblock the workflow Drew's friction note flagged: agent picks the right project + workspace before writing, instead of silently operating against the active Hermes memory project.
12-
- **SKILL.md cross-project workflow** documenting the discovery → route → write → verify recipe end-to-end. Also backfills `bm_recent` documentation (the tool shipped in 0.2.0 but the skill hadn't been updated). New "Cross-project routing" section explains `project` vs `project_id` precedence and the workspace disambiguation flow; the "When to use" lookup table extends to cover the new cases.
12+
- **SKILL.md cross-project workflow** documenting the discovery → route → write → verify recipe end-to-end. Adds a "Permalinks" section covering the three canonical shapes (short, project-qualified, workspace-qualified) and the round-trip property where `bm_write`'s returned permalink self-routes for follow-up reads. A "Cross-project routing" section explains `project` (including workspace-qualified syntax like `"personal/main"`) vs `project_id` and when to use each. Also backfills `bm_recent` documentation (the tool shipped in 0.2.0 but the skill hadn't been updated).
1313

1414
### Notes
1515
- Addresses the routing, discovery, and documentation gaps in the real-world note "Hermes Basic Memory Cloud Task Experience." A proposed `bm_import` tool was evaluated and dropped — `read_file` + `bm_write` already composes the same operation with no new capability, at the cost of one more tool in the surface.

skill/SKILL.md

Lines changed: 34 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ bm_recent({ timeframe: "2 weeks", type: "entity" })
9090
`timeframe` accepts natural language (`"yesterday"`, `"2 weeks"`, `"last month"`) or compact forms (`"7d"`, `"24h"`). Default is `7d`.
9191

9292
### `bm_projects` — list available projects
93-
Returns name and `external_id` (UUID) per project across local and cloud. Call this when the user names a project that isn't the active one — the UUID is what `project_id` on the other tools expects.
93+
Returns name, workspace slug, and `external_id` (UUID) per project across local and cloud. Call this when the user names a project that isn't the active one. Route follow-up tool calls either by workspace-qualified name (`project: "personal/main"`) or by UUID (`project_id: "01HXYZ..."`) — see Cross-project routing below.
9494

9595
```
9696
bm_projects()
@@ -103,20 +103,37 @@ Workspaces are a BM Cloud concept. Returns name, type, role, and default flag. P
103103
bm_workspaces()
104104
```
105105

106+
## Permalinks
107+
108+
A permalink is the canonical, URL-friendly identifier for a note. Three shapes exist; the read/write tools accept all of them:
109+
110+
| Shape | Example | When |
111+
|---|---|---|
112+
| **Short** | `decisions/auth-strategy` | Bare `folder/note-slug`. Tools need a `project` (or `project_id`) arg to route — the permalink alone isn't enough. |
113+
| **Project-qualified** | `main/decisions/auth-strategy` | `project-name/folder/note-slug`. Carries enough context to route without a separate `project` arg. |
114+
| **Workspace-qualified** | `personal/main/decisions/auth-strategy` | `workspace-slug/project-name/folder/note-slug`. Fully routes, including across cloud workspaces with same-named projects. |
115+
116+
**Important: the permalink returned by `bm_write` already encodes the routing it needs for follow-up reads.** If you wrote with `project="personal/main"`, you get back `personal/main/folder/note-slug` and can call `bm_read({ identifier: <that permalink> })` with no `project` arg. The permalink self-routes.
117+
118+
`memory://` URLs follow the same shapes: `memory://personal/main/decisions/auth-strategy` is valid. The `memory://` prefix is optional for `bm_read` (any of the three permalink shapes works directly); `bm_context` expects the prefix.
119+
106120
## Cross-project routing
107121

108-
Every read/write tool (`bm_search`, `bm_read`, `bm_write`, `bm_edit`, `bm_context`, `bm_delete`, `bm_move`, `bm_recent`) accepts two optional routing parameters:
122+
Every read/write tool (`bm_search`, `bm_read`, `bm_write`, `bm_edit`, `bm_context`, `bm_delete`, `bm_move`, `bm_recent`) accepts optional `project` and `project_id`:
109123

110-
- `project` — project name (e.g. `"main"`). Easy to read; can be ambiguous if the same name exists in multiple cloud workspaces.
111-
- `project_id` — UUID from `bm_projects`. Unambiguous; the right choice when project names might collide. Wins over `project` if both are passed.
124+
- `project` — project name, optionally workspace-qualified. Plain (`"main"`) when the name is globally unique; qualified (`"personal/main"`, `"team-paul/research"`) when you need to pick a specific cloud workspace by slug.
125+
- `project_id` — UUID from `bm_projects` (`external_id` field). The most stable identifier — survives project renames and works across workspaces without qualification. Wins over `project` if both are passed.
112126

113127
Omit both and the call uses the Hermes-configured active project.
114128

115129
```
116-
# Use a specific project by name
130+
# Plain project name (unique)
117131
bm_write({ title: "...", folder: "...", content: "...", project: "main" })
118132
119-
# Use a specific project by UUID (safer when workspaces are in play)
133+
# Workspace-qualified name (disambiguates same-named projects across workspaces)
134+
bm_write({ title: "...", folder: "...", content: "...", project: "personal/main" })
135+
136+
# UUID (most stable, survives renames)
120137
bm_write({ title: "...", folder: "...", content: "...", project_id: "01HXYZ..." })
121138
```
122139

@@ -126,33 +143,34 @@ bm_write({ title: "...", folder: "...", content: "...", project_id: "01HXYZ..."
126143

127144
When the user asks something like *"save this markdown file to my personal `main` project, return the permalink"*:
128145

129-
1. **Discover the project.** Call `bm_projects()` and find the entry matching the user's described project + workspace. Capture its `external_id`.
146+
1. **Discover the project.** Call `bm_projects()` and find the entry matching the user's described project + workspace. You can route by either the workspace-qualified name (`personal/main`) or the UUID (`external_id`).
130147

131148
```
132149
bm_projects()
133150
# → [{name: "main", external_id: "01HXYZ...", workspace: "Personal", ...}, ...]
134151
```
135152

136-
If multiple projects share the name, call `bm_workspaces()` and match by workspace before picking a UUID.
153+
If a project name appears in multiple workspaces, use `bm_workspaces()` to confirm which slug you want.
137154

138155
2. **Read the file from disk.** Use Hermes's filesystem tool (not a `bm_*` tool — local files aren't in the graph yet).
139156

140-
3. **Write the note with explicit routing.** Pass the UUID so the write lands in the right project even when other workspaces exist.
157+
3. **Write the note with explicit routing.** Either form works; the workspace-qualified name reads cleaner in logs, the UUID is more durable.
141158

142159
```
143160
bm_write({
144161
title: "StartWithDrew Level 9 Task Queue",
145162
folder: "startwithdrew",
146163
content: <file body>,
147-
project_id: "01HXYZ..."
164+
project: "personal/main"
148165
})
149-
# → returns the permalink
166+
# → returns "personal/main/startwithdrew/start-with-drew-level-9-task-queue"
167+
# (the returned permalink is workspace-qualified — carries its own routing)
150168
```
151169

152-
4. **Verify by reading back.** Confirms the note landed and returns the canonical permalink.
170+
4. **Verify by reading back.** No `project` arg needed — the workspace-qualified permalink routes itself.
153171

154172
```
155-
bm_read({ identifier: <permalink>, project_id: "01HXYZ..." })
173+
bm_read({ identifier: "personal/main/startwithdrew/start-with-drew-level-9-task-queue" })
156174
```
157175

158176
Return the permalink (and the project name for clarity) to the user.
@@ -166,8 +184,9 @@ Return the permalink (and the project name for clarity) to the user.
166184
| Updating prior work | `bm_edit` (append for time-ordered logs, replace_section for living docs) |
167185
| Exploring related concepts | `bm_context` |
168186
| "What was I working on yesterday?" / no specific query yet | `bm_recent` |
169-
| User names a project that isn't the active one | `bm_projects` → pick UUID → call read/write tool with `project_id` |
170-
| Same project name might exist in multiple workspaces | `bm_projects` + `bm_workspaces` to disambiguate, then `project_id` |
187+
| User names a project that isn't the active one | `bm_projects` → call read/write tool with `project: "workspace/name"` or `project_id: "<uuid>"` |
188+
| Same project name might exist in multiple workspaces | `bm_projects` (+ `bm_workspaces` if needed) → route with workspace-qualified `project` or `project_id` |
189+
| Following up on a freshly-written note | Use the returned permalink directly — it already encodes the routing |
171190

172191
## Note structure
173192

@@ -197,10 +216,6 @@ Background and current situation.
197216
- [ ] Document
198217
```
199218

200-
## Memory URLs
201-
202-
`memory://projects/api-redesign` — direct reference. Used in `bm_context`, `bm_read`. The `memory://` prefix is optional for `bm_read`.
203-
204219
## Behavior guidelines
205220

206221
1. **Search before answering.** If the user asks "what did we decide about X?", run `bm_search` first.

0 commit comments

Comments
 (0)