@k-l-lambda/portolan 0.0.0-stage → 0.1.0
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 +104 -2
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +42 -0
- package/dist/cli.js.map +1 -0
- package/dist/derive.d.ts +17 -0
- package/dist/derive.js +67 -0
- package/dist/derive.js.map +1 -0
- package/dist/edit.d.ts +69 -0
- package/dist/edit.js +302 -0
- package/dist/edit.js.map +1 -0
- package/dist/format.d.ts +9 -0
- package/dist/format.js +35 -0
- package/dist/format.js.map +1 -0
- package/dist/generated/line-parser.d.ts +213 -0
- package/dist/generated/line-parser.js +1920 -0
- package/dist/history.d.ts +90 -0
- package/dist/history.js +182 -0
- package/dist/history.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -0
- package/dist/parse.d.ts +33 -0
- package/dist/parse.js +468 -0
- package/dist/parse.js.map +1 -0
- package/dist/resolve.d.ts +41 -0
- package/dist/resolve.js +214 -0
- package/dist/resolve.js.map +1 -0
- package/dist/server.d.ts +9 -0
- package/dist/server.js +376 -0
- package/dist/server.js.map +1 -0
- package/dist/threads.d.ts +93 -0
- package/dist/threads.js +128 -0
- package/dist/threads.js.map +1 -0
- package/dist/types.d.ts +62 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/docs/rhumb-spec.md +289 -0
- package/docs/skill.md +320 -0
- package/package.json +78 -4
- package/web/dist/assets/index-BMGxaxGj.js +106 -0
- package/web/dist/assets/index-BjZBlA3n.css +1 -0
- package/web/dist/favicon.svg +16 -0
- package/web/dist/index.html +14 -0
package/docs/skill.md
ADDED
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: rhumb
|
|
3
|
+
description: Record and maintain project work in a Portolan Rhumb (.rhumb) map - nodes, statuses, diary anchors, dependency edges, and the check/fmt/serve tooling.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Maintaining a Rhumb map
|
|
7
|
+
|
|
8
|
+
A `.rhumb` file is the structural map of one project: work items, their status, hierarchy and relations. Details (what was tried, results, commands, decisions) live in the daily diary. The map links to diary entries and never copies them. Syntax source of truth: `docs/rhumb-spec.md` (0.1). Run commands from the portolan repo root.
|
|
9
|
+
|
|
10
|
+
## 1. When to update the map
|
|
11
|
+
|
|
12
|
+
Update the map in the same turn as the work, whenever:
|
|
13
|
+
|
|
14
|
+
- a new work item appears (planned, proposed, split off) → add a node
|
|
15
|
+
- an item starts, finishes, gets blocked or is abandoned → change its status
|
|
16
|
+
- you learn that one item depends on another → add an edge
|
|
17
|
+
- you finish a diary entry about an item → add an anchor note linking to it
|
|
18
|
+
|
|
19
|
+
Keep the map structural. A note is one line: a blocked reason, a key decision, a pointer. If you need more, write it in the diary and link to it.
|
|
20
|
+
|
|
21
|
+
### Write for a human reading the big picture
|
|
22
|
+
|
|
23
|
+
The map is read by people scanning the whole project; the diary is where the work is reproduced. Write each node so that someone who never saw the code understands what it is and why it matters, and put the technical record behind a precise link.
|
|
24
|
+
|
|
25
|
+
- **Title: a short name in plain words**, usually two to four words, naming the thing or outcome, not a file, function or flag. "Hot reload", not "`fs.watch` per directory + 2 s rescan". Do not turn titles into sentences; brevity is part of readability, and the `Why:` note carries the explanation. Keep a title that is already short and clear.
|
|
26
|
+
- **First note: `Why:` and how the node serves the larger goal.** Name the user need or the parent goal it advances, so the line connects the node to the picture around it, not just restates the title.
|
|
27
|
+
- **Other notes: decisions, outcomes and open questions** a reader needs to judge progress, in one plain sentence each.
|
|
28
|
+
- **Technical details go to the diary**: commands, error messages, numbers, file paths, commit hashes, parameter lists, how a bug was found. Link to the exact entry, or the exact line with `^=` / `+L` (section 3), so a reader can jump straight to the evidence instead of searching.
|
|
29
|
+
- If a note needs a code span to be understood, it probably belongs in the diary.
|
|
30
|
+
|
|
31
|
+
```rhumb
|
|
32
|
+
---
|
|
33
|
+
links:
|
|
34
|
+
diary: ../{path}.md
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
%% Too technical for the map: a reader learns how it was built, not what it is for.
|
|
38
|
+
- [x] fs.watch per dir + 2s rescan, SSE `change` on recreate (05b336e) ^hot-reload-raw
|
|
39
|
+
- Node 22 recursive watch drops files after rm+create; mtime/size shortcut
|
|
40
|
+
|
|
41
|
+
%% Readable: what it gives the user and why, with the details one click away.
|
|
42
|
+
- [x] Hot reload ^hot-reload
|
|
43
|
+
- Why: agents and editors rewrite maps while a page is open; the view must stay current or people act on stale plans.
|
|
44
|
+
- Saves that replace the file (as many editors do) no longer stop updates.
|
|
45
|
+
- [how it was fixed](<diary:2026/1008#rhumb-tooling:~:text=Implement hot reload>)
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## 2. Nodes
|
|
49
|
+
|
|
50
|
+
```
|
|
51
|
+
- [S] Title {attrs} ^id
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
| Status | Meaning |
|
|
55
|
+
| --- | --- |
|
|
56
|
+
| `[ ]` | todo, not started |
|
|
57
|
+
| `[/]` | doing, in progress |
|
|
58
|
+
| `[x]` | done |
|
|
59
|
+
| `[-]` | dropped, abandoned (keep the node) |
|
|
60
|
+
| `[!]` | blocked; add a note with the reason |
|
|
61
|
+
| `[?]` | idea, proposed but not committed |
|
|
62
|
+
|
|
63
|
+
- Always write a readable kebab-case ID matching `[a-z0-9][a-z0-9-]{0,47}`. Prefix child IDs with the parent's (`threads`, `threads-model`). Never write `^_xxxxxx`: the `_` prefix is reserved for IDs that `fmt` generates when a human omits one.
|
|
64
|
+
- IDs are permanent. Edges and threads reference them; change one only with `rename-id` (section 7).
|
|
65
|
+
- Attributes are optional: `owner`, `due` (ISO date), `tags` (list), `priority` (`high`/`normal`/`low`), `star` (boolean). Other keys are kept but reported as `I002`.
|
|
66
|
+
- `{star: true}` is the project's shared favorite mark, shown with a dedicated star in the web view. Humans usually set it; add or remove a star only when the user asks. Unstar by removing the key, not by writing `false`. Any other value is `W010`.
|
|
67
|
+
- Hierarchy is 2 spaces per level. A node's parent is the nearest preceding node with smaller indentation; siblings share one indentation.
|
|
68
|
+
- Notes are list items without a checkbox. They belong to the node above them and are not children. Use them for blocked reasons, short context and links.
|
|
69
|
+
- Titles are inline Markdown (code spans, links). A title that itself ends in `^word` or `{...}` must escape it as `\^` or `\{`.
|
|
70
|
+
|
|
71
|
+
```rhumb
|
|
72
|
+
- [/] Visual frontend {owner: claude, priority: high} ^ui
|
|
73
|
+
- Read-only viewer first; editing goes through the server
|
|
74
|
+
- [x] Renderer spike ^ui-spike
|
|
75
|
+
- [!] Mind map view ^ui-tree
|
|
76
|
+
- Blocked: renderer license review pending
|
|
77
|
+
- [?] Dependency graph view {tags: [dag, xyflow]} ^ui-dag
|
|
78
|
+
- [-] Canvas prototype ^ui-canvas
|
|
79
|
+
- Dropped: the license requires a production key
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## 3. Anchors
|
|
83
|
+
|
|
84
|
+
Every Markdown link in a node's title or notes is an anchor of that node. Prefer an anchor note over a link in the title.
|
|
85
|
+
|
|
86
|
+
- Define link prefixes in front matter `links:`; `{path}` is replaced and the result is relative to the `.rhumb` file. Then write `diary:2026/1008#frag`.
|
|
87
|
+
- Plain relative paths (`../notes/x.md#frag`) and `https://` URLs also work.
|
|
88
|
+
- The fragment is a GitHub heading slug (lowercase, punctuation removed, spaces → `-`). A unique prefix is enough: `#portolan` matches `## Portolan: agent-human shared mind map ...`.
|
|
89
|
+
- To point at one entry under the heading, append a text fragment: `#portolan:~:text=Backlog.md`. The resolver picks the first top-level list item under that heading whose lines contain the text. Choose a distinctive phrase from the entry's first line or summary.
|
|
90
|
+
- To point at a line, use `^=` (line starts with, indentation ignored) and offsets: ``#map-layout^=`* \> [host][portolan] Design a vector`+L2`` is the summary line two lines below that entry. `#heading+L3` counts from the heading. `#L42` / `#L42-L50` point at absolute lines in any file (code: `repo:src/edit.ts#L120`). Prefer `^=` or `:~:text=` to bare offsets; offsets shift when lines are inserted above.
|
|
91
|
+
- If the target contains spaces, wrap the whole target in `<...>`. Inside it, write `>` as `\>` (diary entries start with `* > [host]`); an unescaped `>` breaks the link and `check` reports `W011`. A backtick inside a `^=` prefix is written `%60`.
|
|
92
|
+
- When you write a diary heading, start it with a word no other heading in that day's file starts with, so a short prefix like `#rhumb-tooling` stays unique. An ambiguous prefix is `W003`.
|
|
93
|
+
|
|
94
|
+
```rhumb
|
|
95
|
+
---
|
|
96
|
+
rhumb: 0.1
|
|
97
|
+
title: Portolan
|
|
98
|
+
links:
|
|
99
|
+
diary: ../{path}.md
|
|
100
|
+
---
|
|
101
|
+
|
|
102
|
+
- [x] [Prior-art survey](diary:2026/1008#portolan) ^survey
|
|
103
|
+
- [Backlog.md analysis](<diary:2026/1008#portolan:~:text=Explain Backlog.md>)
|
|
104
|
+
- [Backlog.md repo](https://github.com/MrLesk/Backlog.md)
|
|
105
|
+
- [x] Parser (Peggy) ^rhumb-parser
|
|
106
|
+
- [parser record](<diary:2026/1008#rhumb-dsl:~:text=Parser: chose Peggy>)
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## 4. Edges
|
|
110
|
+
|
|
111
|
+
```
|
|
112
|
+
a needs b, c: optional label
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
Edge lines start at column 0, reference IDs (no `^` needed) and can go anywhere in the body; convention is a `%%`-commented group after the tree.
|
|
116
|
+
|
|
117
|
+
| Kind | Use when | Affects readiness |
|
|
118
|
+
| --- | --- | --- |
|
|
119
|
+
| `needs` | a cannot finish (or start) before b is done | yes |
|
|
120
|
+
| `blocks` | same as `b needs a`; use only when the sentence reads better that way | yes |
|
|
121
|
+
| `relates` | any other relation; add a verb-phrase label for domain meaning (see below) | no |
|
|
122
|
+
| `replaces` | a supersedes b (usually b is `[-]`) | no |
|
|
123
|
+
| `from` | a was split off, followed up or derived from b | no |
|
|
124
|
+
|
|
125
|
+
- Do not add `needs`/`blocks` between a node and its own ancestor or descendant (`W005`); the hierarchy already says that.
|
|
126
|
+
- Never create a dependency cycle (`E009`). If two items depend on each other, split one or use `relates`.
|
|
127
|
+
- A label applies to every target on the line. Write one only when the reason is not obvious.
|
|
128
|
+
- Use `needs` only for execution order: a really cannot finish before b is done. Do not use it for "a is compared against b", "a uses b" or "a tests b".
|
|
129
|
+
- Domain relations are labeled `relates`, never new keywords. The label is a verb phrase and the edge reads source + label + target. A labeled `relates` is directed; an unlabeled one is not.
|
|
130
|
+
|
|
131
|
+
```rhumb
|
|
132
|
+
- [x] A0 baseline loss ^loss-a0
|
|
133
|
+
- [/] A1 forward KL ^loss-a1
|
|
134
|
+
- [ ] Shared fixed50 cohort ^cohort
|
|
135
|
+
- [?] LR/data-size mismatch ^lr-hypothesis
|
|
136
|
+
|
|
137
|
+
loss-a1 relates loss-a0: compared against
|
|
138
|
+
loss-a1 relates cohort: uses
|
|
139
|
+
loss-a1 relates lr-hypothesis: tests
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
```rhumb
|
|
143
|
+
- [/] Rhumb DSL ^rhumb
|
|
144
|
+
- [x] Parser ^rhumb-parser
|
|
145
|
+
- [ ] Edit operations API ^rhumb-edit
|
|
146
|
+
- [ ] Local server ^server
|
|
147
|
+
- [ ] Agent interface ^agent-api
|
|
148
|
+
- [-] Old JSON format ^json-format
|
|
149
|
+
- [ ] Sidecar thread format ^threads-format
|
|
150
|
+
|
|
151
|
+
%% dependencies
|
|
152
|
+
rhumb-edit needs rhumb-parser
|
|
153
|
+
server needs rhumb-edit, rhumb-parser: serves the AST and applies edits
|
|
154
|
+
agent-api needs rhumb-edit
|
|
155
|
+
agent-api relates server
|
|
156
|
+
|
|
157
|
+
%% provenance
|
|
158
|
+
rhumb replaces json-format
|
|
159
|
+
threads-format from server
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
## 5. Status discipline
|
|
163
|
+
|
|
164
|
+
- Parent status is written, not derived. When the first child starts, set the parent to `[/]` (otherwise `I003`).
|
|
165
|
+
- Do not mark a parent `[x]` while its subtree has `[ ]`, `[/]` or `[!]` nodes (`W007`). Finish, drop (`[-]`) or move the open children first.
|
|
166
|
+
- Do not mark a node `[/]` or `[x]` while one of its `needs` targets is not done (`W008`). Either finish the dependency, or the edge is wrong and should be removed or changed to `relates`.
|
|
167
|
+
- Use `[!]` only with a note that says what it is waiting for.
|
|
168
|
+
- Never delete abandoned work. Set it to `[-]` and add a note with the reason. A dependency on a dropped node is `W006` and no longer blocks readiness; retarget or remove that edge.
|
|
169
|
+
- `[?]` ideas do not count toward progress. Promote to `[ ]` when the work is committed.
|
|
170
|
+
|
|
171
|
+
## 6. Editing rules (minimal diffs)
|
|
172
|
+
|
|
173
|
+
- Change only the lines you mean to change. A status change is a one-character diff.
|
|
174
|
+
- One space between title, `{attrs}` and `^id`. Never align columns.
|
|
175
|
+
- Keep `%%` comments, blank lines and the existing order.
|
|
176
|
+
- Insert new nodes at the end of their sibling group unless order matters.
|
|
177
|
+
- Append new edges next to related edges (same `%%` group) or at the end of the file. Do not reorder edges.
|
|
178
|
+
- Re-read the file before editing; a human or the UI may have changed it.
|
|
179
|
+
|
|
180
|
+
## 7. Tooling
|
|
181
|
+
|
|
182
|
+
```sh
|
|
183
|
+
node src/cli.ts check <file> # parse, derived and anchor diagnostics
|
|
184
|
+
node src/cli.ts fmt <file> # in-place: assign missing IDs, normalize [X]→[x], [~]→[/]
|
|
185
|
+
node src/cli.ts serve <file> [--port N] # local API on 127.0.0.1, default port 4310
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
- Run `check` after every edit. Output is `<file>:<line>: <level> <code> <message>`; exit code 1 means at least one error. Fix all errors and every warning you caused. `W003` for a diary that is not checked out is acceptable.
|
|
189
|
+
- `fmt` in 0.1 does only the two things above (it prints `assigned ^_xxxxxx` per ID). It does not re-indent, dedupe edges or collapse blank lines. Use it when a human left nodes without IDs; you should never need it for your own lines.
|
|
190
|
+
|
|
191
|
+
### Edit API (`serve`)
|
|
192
|
+
|
|
193
|
+
The server has no authentication and accepts only loopback `Host` headers. Get the current version, then post one operation:
|
|
194
|
+
|
|
195
|
+
```sh
|
|
196
|
+
v=$(curl -s http://127.0.0.1:4310/api/doc | jq -r .version)
|
|
197
|
+
curl -s -X POST http://127.0.0.1:4310/api/edit -H 'content-type: application/json' \
|
|
198
|
+
-d "{\"version\":\"$v\",\"edit\":{\"op\":\"set-status\",\"id\":\"rhumb-fmt\",\"status\":\"done\"}}"
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
| `op` | Fields | Effect |
|
|
202
|
+
| --- | --- | --- |
|
|
203
|
+
| `set-status` | `id`, `status` | rewrites the checkbox only |
|
|
204
|
+
| `set-title` | `id`, `title` | rewrites the node line, keeps attrs and ID |
|
|
205
|
+
| `set-attr` | `id`, `key`, `value` (string, number, boolean or list; `null` removes) | rewrites only that node line; for `star`, `false` also removes the key |
|
|
206
|
+
| `add-node` | `parent` (ID or `null`), `title`, `status?`, `id?` | inserts after the parent's subtree; ID defaults to a slug of the title, so pass a readable `id` |
|
|
207
|
+
| `remove-node` | `id`, `recursive?` | deletes the node, its notes and (with `recursive`) its subtree, plus edges touching them |
|
|
208
|
+
| `rename-id` | `id`, `to` | renames the ID and every edge reference; threads are retargeted |
|
|
209
|
+
| `add-edge` | `from`, `kind`, `to`, `label?` | appends one edge line at the end of the file |
|
|
210
|
+
| `remove-edge` | `from`, `kind`, `to` | removes that target from matching edge lines |
|
|
211
|
+
|
|
212
|
+
- `status` values are words: `todo`, `doing`, `done`, `dropped`, `blocked`, `idea`.
|
|
213
|
+
- `409` means the file changed since your `version`; re-fetch `/api/doc` and retry. `400` means the edit is invalid or would introduce a new error.
|
|
214
|
+
- The response is `{version}` plus `id` (add-node), `renamed` or `removed` when relevant.
|
|
215
|
+
- The API cannot add notes or anchors, and `move-node` is not implemented. Make those edits by hand. The server watches the file, so hand edits show up in the UI.
|
|
216
|
+
- Prefer `set-status dropped` over `remove-node`.
|
|
217
|
+
|
|
218
|
+
### Threads
|
|
219
|
+
|
|
220
|
+
Human/agent discussion threads live in the sidecar `<name>.threads.jsonl` next to the `.rhumb` file (append-only, one event per line). Never write discussion into the `.rhumb` file. Use `GET /api/threads?status=open` to list open threads (`orphan: true` means the target node is gone) and `POST /api/threads` with `{action: "open"|"reply"|"resolve"|"reopen", ...}` to write. Renaming an ID by hand instead of with `rename-id` orphans its threads.
|
|
221
|
+
|
|
222
|
+
## 8. Worked example
|
|
223
|
+
|
|
224
|
+
You finish work and write this diary entry in `2026/1009.md`:
|
|
225
|
+
|
|
226
|
+
```markdown
|
|
227
|
+
## Rhumb tooling: fmt and edit API
|
|
228
|
+
|
|
229
|
+
* > [host][portolan] Implement `rhumb fmt` and the edit operations API
|
|
230
|
+
<details>
|
|
231
|
+
<summary>fmt assigns IDs and normalizes status aliases; edit API started</summary>
|
|
232
|
+
|
|
233
|
+
* fmt 0.1 subset only; the full CST printer (re-indent, edge dedup) is postponed
|
|
234
|
+
* edit API: set-status, add-node, add-edge work; move-node not started
|
|
235
|
+
</details>
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
Map before (`memo/portolan.rhumb`):
|
|
239
|
+
|
|
240
|
+
```rhumb
|
|
241
|
+
---
|
|
242
|
+
rhumb: 0.1
|
|
243
|
+
title: Portolan
|
|
244
|
+
links:
|
|
245
|
+
diary: ../{path}.md
|
|
246
|
+
---
|
|
247
|
+
|
|
248
|
+
- [/] Rhumb DSL ^rhumb
|
|
249
|
+
- [x] Parser (Peggy) ^rhumb-parser
|
|
250
|
+
- [parser record](<diary:2026/1008#rhumb-dsl:~:text=Parser: chose Peggy>)
|
|
251
|
+
- [ ] Formatter `rhumb fmt` ^rhumb-fmt
|
|
252
|
+
- [ ] Edit operations API ^rhumb-edit
|
|
253
|
+
|
|
254
|
+
%% Rhumb tooling
|
|
255
|
+
rhumb-fmt needs rhumb-parser
|
|
256
|
+
rhumb-edit needs rhumb-fmt
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
Updates: `rhumb-fmt` → done with an anchor to the entry; `rhumb-edit` → doing (allowed now that `rhumb-fmt` is done); the postponed work becomes a new node under `rhumb`, linked to its origin with `from`.
|
|
260
|
+
|
|
261
|
+
```diff
|
|
262
|
+
@@ -8,9 +8,13 @@
|
|
263
|
+
- [/] Rhumb DSL ^rhumb
|
|
264
|
+
- [x] Parser (Peggy) ^rhumb-parser
|
|
265
|
+
- [parser record](<diary:2026/1008#rhumb-dsl:~:text=Parser: chose Peggy>)
|
|
266
|
+
- - [ ] Formatter `rhumb fmt` ^rhumb-fmt
|
|
267
|
+
- - [ ] Edit operations API ^rhumb-edit
|
|
268
|
+
+ - [x] Formatter `rhumb fmt` ^rhumb-fmt
|
|
269
|
+
+ - [fmt record](<diary:2026/1009#rhumb-tooling:~:text=fmt assigns IDs>)
|
|
270
|
+
+ - [/] Edit operations API ^rhumb-edit
|
|
271
|
+
+ - [ ] Full CST printer for fmt ^rhumb-fmt-cst
|
|
272
|
+
+ - Re-indent, edge dedup, blank-line collapsing
|
|
273
|
+
|
|
274
|
+
%% Rhumb tooling
|
|
275
|
+
rhumb-fmt needs rhumb-parser
|
|
276
|
+
rhumb-edit needs rhumb-fmt
|
|
277
|
+
+rhumb-fmt-cst from rhumb-fmt
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
Then run `node src/cli.ts check memo/portolan.rhumb`: no output, exit 0. If the diary repo is not checked out, the anchors report `W003` only.
|
|
281
|
+
|
|
282
|
+
## 9. Common mistakes
|
|
283
|
+
|
|
284
|
+
| Mistake | Code | Fix |
|
|
285
|
+
| --- | --- | --- |
|
|
286
|
+
| Unknown checkbox, e.g. `[>]` | `E002` | use one of the six statuses |
|
|
287
|
+
| Duplicate `^id` | `E003` | pick a new readable ID |
|
|
288
|
+
| Dedent to a level that was never opened, odd indentation | `E004` | 2 spaces per level, align siblings |
|
|
289
|
+
| `foo:` link prefix missing from front matter | `E005` | add it under `links:` |
|
|
290
|
+
| Edge to a typo or missing ID | `E007` | check the ID exists |
|
|
291
|
+
| Unknown edge kind (`a depends b`, `a compares b`) | `E008` | use needs/blocks/relates/replaces/from; put domain meaning in a `relates` label |
|
|
292
|
+
| Indented edge line, or prose at column 0 | `E001` | edges at column 0; prose goes in a note |
|
|
293
|
+
| `a needs b` plus `b needs a` | `E009` | break the cycle |
|
|
294
|
+
| Node without a title | `E011` | write a title |
|
|
295
|
+
| Note at top level | `W001` | put it under a node |
|
|
296
|
+
| Node indented under a note | `W002` | indent it under the node instead |
|
|
297
|
+
| Missing diary file, heading, ambiguous prefix, text not found | `W003` | fix the link or the heading |
|
|
298
|
+
| Same edge twice | `W004` | delete the duplicate |
|
|
299
|
+
| `needs` between parent and descendant | `W005` | remove it |
|
|
300
|
+
| `needs` a dropped node | `W006` | retarget or remove the edge |
|
|
301
|
+
| `[x]` parent with open children | `W007` | close or drop the children first |
|
|
302
|
+
| `[/]`/`[x]` node whose `needs` target is not done | `W008` | fix the status or the edge |
|
|
303
|
+
| `^Upper_Case` or `^_handmade` ID | `W009` | lowercase kebab-case, no leading `_` |
|
|
304
|
+
| `[ ]` parent with started children | `I003` | set the parent to `[/]` |
|
|
305
|
+
|
|
306
|
+
This file triggers `W005`, `W006`, `W007`, `W008` and `I003`:
|
|
307
|
+
|
|
308
|
+
```rhumb
|
|
309
|
+
- [x] Release 1.0 ^release
|
|
310
|
+
- [ ] Write changelog ^changelog
|
|
311
|
+
- [/] Deploy ^deploy
|
|
312
|
+
- [ ] Smoke test ^smoke
|
|
313
|
+
- [ ] Docs ^docs
|
|
314
|
+
- [/] API reference ^docs-api
|
|
315
|
+
- [-] Old installer ^old-installer
|
|
316
|
+
|
|
317
|
+
deploy needs changelog
|
|
318
|
+
deploy needs smoke
|
|
319
|
+
docs needs old-installer
|
|
320
|
+
```
|
package/package.json
CHANGED
|
@@ -1,6 +1,80 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@k-l-lambda/portolan",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "A shared map of work for humans and AI agents: a mind map that is also a TODO list and task log, written in the Rhumb DSL.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"keywords": [
|
|
7
|
+
"mind-map",
|
|
8
|
+
"task-graph",
|
|
9
|
+
"dsl",
|
|
10
|
+
"agents",
|
|
11
|
+
"todo",
|
|
12
|
+
"diary"
|
|
13
|
+
],
|
|
14
|
+
"main": "dist/index.js",
|
|
15
|
+
"types": "dist/index.d.ts",
|
|
16
|
+
"exports": {
|
|
17
|
+
".": {
|
|
18
|
+
"types": "./dist/index.d.ts",
|
|
19
|
+
"default": "./dist/index.js"
|
|
20
|
+
},
|
|
21
|
+
"./package.json": "./package.json"
|
|
22
|
+
},
|
|
23
|
+
"bin": {
|
|
24
|
+
"portolan": "dist/cli.js",
|
|
25
|
+
"rhumb": "dist/cli.js"
|
|
26
|
+
},
|
|
27
|
+
"files": [
|
|
28
|
+
"dist",
|
|
29
|
+
"web/dist",
|
|
30
|
+
"docs"
|
|
31
|
+
],
|
|
32
|
+
"engines": {
|
|
33
|
+
"node": ">=22.12"
|
|
34
|
+
},
|
|
35
|
+
"scripts": {
|
|
36
|
+
"build:grammar": "peggy --format es --dts --allowed-start-rules Line,Inline -o src/generated/line-parser.js src/grammar.peggy",
|
|
37
|
+
"pretest": "npm run build:grammar",
|
|
38
|
+
"test": "vitest run",
|
|
39
|
+
"typecheck": "npm run build:grammar && tsc --noEmit && tsc --noEmit -p web",
|
|
40
|
+
"portolan": "node src/cli.ts",
|
|
41
|
+
"build:lib": "npm run build:grammar && tsc -p tsconfig.build.json && mkdir -p dist/generated && cp src/generated/line-parser.js src/generated/line-parser.d.ts dist/generated/",
|
|
42
|
+
"build:web": "vite build web",
|
|
43
|
+
"dev:web": "vite web",
|
|
44
|
+
"build": "npm run build:lib && npm run build:web",
|
|
45
|
+
"prepublishOnly": "npm run build"
|
|
46
|
+
},
|
|
47
|
+
"repository": {
|
|
48
|
+
"type": "git",
|
|
49
|
+
"url": "git+https://github.com/k-l-lambda/portolan.git"
|
|
50
|
+
},
|
|
51
|
+
"homepage": "https://github.com/k-l-lambda/portolan#readme",
|
|
52
|
+
"bugs": {
|
|
53
|
+
"url": "https://github.com/k-l-lambda/portolan/issues"
|
|
54
|
+
},
|
|
55
|
+
"author": "k.l.lambda",
|
|
56
|
+
"license": "ISC",
|
|
57
|
+
"publishConfig": {
|
|
58
|
+
"access": "public"
|
|
59
|
+
},
|
|
60
|
+
"packageManager": "pnpm@10.23.0",
|
|
61
|
+
"dependencies": {
|
|
62
|
+
"yaml": "2.9.1"
|
|
63
|
+
},
|
|
64
|
+
"devDependencies": {
|
|
65
|
+
"@types/node": "22.20.4",
|
|
66
|
+
"@types/react": "19.3.0",
|
|
67
|
+
"@types/react-dom": "19.3.0",
|
|
68
|
+
"@vitejs/plugin-react": "6.1.1",
|
|
69
|
+
"@xyflow/react": "12.11.6",
|
|
70
|
+
"dompurify": "3.4.16",
|
|
71
|
+
"elkjs": "0.12.0",
|
|
72
|
+
"marked": "18.0.14",
|
|
73
|
+
"peggy": "5.1.0",
|
|
74
|
+
"react": "19.3.0",
|
|
75
|
+
"react-dom": "19.3.0",
|
|
76
|
+
"typescript": "6.0.3",
|
|
77
|
+
"vite": "8.3.3",
|
|
78
|
+
"vitest": "5.0.1"
|
|
79
|
+
}
|
|
80
|
+
}
|