noggin-cli 0.1.2
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 +389 -0
- package/SKILL.md +153 -0
- package/noggin-api.d.mts +317 -0
- package/noggin-api.mjs +1236 -0
- package/noggin-mcp.mjs +270 -0
- package/noggin.mjs +482 -0
- package/package.json +48 -0
package/README.md
ADDED
|
@@ -0,0 +1,389 @@
|
|
|
1
|
+
# noggin
|
|
2
|
+
|
|
3
|
+
A small, single-user working-memory tree for in-flight work — your
|
|
4
|
+
second brain for the stuff you can't fit in your head.
|
|
5
|
+
|
|
6
|
+
Lives in `~/.noggin.yaml` by default. Override per call with `--file
|
|
7
|
+
<path>`, or set `$NOGGIN_FILE` to point every invocation at a
|
|
8
|
+
different file. (The VS Code extension sets `NOGGIN_FILE` in its
|
|
9
|
+
terminals so the CLI follows whichever noggin you have open.) Driven
|
|
10
|
+
by [`noggin.mjs`](noggin.mjs) next to this file. The YAML file is the source
|
|
11
|
+
of truth; the CLI is the only sanctioned way to read or write it.
|
|
12
|
+
|
|
13
|
+
For the agent-facing behavioral instructions, see [SKILL.md](SKILL.md).
|
|
14
|
+
This document is the human reference: what noggin is, what the
|
|
15
|
+
commands do, how the file is shaped.
|
|
16
|
+
|
|
17
|
+
## Mental model
|
|
18
|
+
|
|
19
|
+
Items form a tree. There is at most one **active** item; the path
|
|
20
|
+
from a root to the active item is your current spine. Other open
|
|
21
|
+
items are paused — work you started but stepped away from. Done
|
|
22
|
+
items stay in the tree under their parent so you can see what got
|
|
23
|
+
finished. Use `edit --open` if it turns out something was not
|
|
24
|
+
really finished.
|
|
25
|
+
|
|
26
|
+
An item and a "todo" are the same thing at different lifecycle stages:
|
|
27
|
+
|
|
28
|
+
- **push** = create a child of active and immediately become it
|
|
29
|
+
("I'm going to do this now").
|
|
30
|
+
- **add** = create a child of active without becoming it (a deferred
|
|
31
|
+
task; same shape, just never activated).
|
|
32
|
+
|
|
33
|
+
You can later `goto` an added child to make it active, or just `done`
|
|
34
|
+
it without ever activating.
|
|
35
|
+
|
|
36
|
+
### What an item carries
|
|
37
|
+
|
|
38
|
+
Just the things that are about an item *being an item* in the tree:
|
|
39
|
+
|
|
40
|
+
- a **title** (one line)
|
|
41
|
+
- a **done** flag and a `createdAt` timestamp
|
|
42
|
+
- append-only timestamped **notes** — anything you want to remember,
|
|
43
|
+
including a system-generated `closed` note appended whenever the item
|
|
44
|
+
transitions from open to done (the note's timestamp records when)
|
|
45
|
+
|
|
46
|
+
There is **no fixed schema** for things like "why," "where," "what's
|
|
47
|
+
next," tags, or resolution. If it matters, drop a `note`. The CLI
|
|
48
|
+
stays focused on tree shape and lifecycle; everything else is content
|
|
49
|
+
the user (or an agent) writes in note text.
|
|
50
|
+
|
|
51
|
+
## Identifiers and paths
|
|
52
|
+
|
|
53
|
+
- **key** — an opaque, stable internal identifier (e.g.
|
|
54
|
+
`i-20260616-184644-f04bf5`). Used in the YAML file for parent links
|
|
55
|
+
and the active pointer. Hidden from human output; included in JSON
|
|
56
|
+
output when you want to inspect it.
|
|
57
|
+
- **position** — a computed 1-based index among siblings. Shown in
|
|
58
|
+
brackets in human output, e.g. `[2]`.
|
|
59
|
+
- **path** — how you refer to items on the command line.
|
|
60
|
+
|
|
61
|
+
### Path syntax
|
|
62
|
+
|
|
63
|
+
The leading `/` is the unambiguous marker that separates the two
|
|
64
|
+
families of paths.
|
|
65
|
+
|
|
66
|
+
**Absolute** paths start with `/` and walk from a root. This is the
|
|
67
|
+
canonical form used everywhere the API or human output reports a
|
|
68
|
+
path (`activePath`, `ItemView.path`, `parentPath`, error messages).
|
|
69
|
+
|
|
70
|
+
```
|
|
71
|
+
"/1/2/3"
|
|
72
|
+
│ │ │
|
|
73
|
+
│ │ └── third child of "/1/2"
|
|
74
|
+
│ └──── second child of root "/1"
|
|
75
|
+
└────── first root item
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
**Relative** paths are everything else, resolved against the active
|
|
79
|
+
item (file-system style):
|
|
80
|
+
|
|
81
|
+
| Token | Meaning |
|
|
82
|
+
|---|---|
|
|
83
|
+
| `.` | active item |
|
|
84
|
+
| `..` | parent of active |
|
|
85
|
+
| `-` | previous sibling of active |
|
|
86
|
+
| `+` | next sibling of active |
|
|
87
|
+
| `./X/Y` | descendant of active |
|
|
88
|
+
| `../X` | sibling of active (child X of parent) |
|
|
89
|
+
| `-/X/Y` | descendant under the previous sibling |
|
|
90
|
+
| `+/X/Y` | descendant under the next sibling |
|
|
91
|
+
| `../../X` | walk up two and then down |
|
|
92
|
+
| `X` / `X/Y` | bare positions are short for `./X` / `./X/Y` |
|
|
93
|
+
|
|
94
|
+
Relative paths require an active item. If none is set, pass an
|
|
95
|
+
absolute path instead (e.g. `noggin show /1` rather than `noggin
|
|
96
|
+
show 1`).
|
|
97
|
+
|
|
98
|
+
Paths are coordinates into the current tree order — they are
|
|
99
|
+
intended for immediate interactive use, not long-term bookmarks.
|
|
100
|
+
Stable identity lives in `key` and `parentKey`.
|
|
101
|
+
|
|
102
|
+
## Command reference
|
|
103
|
+
|
|
104
|
+
Every command takes:
|
|
105
|
+
|
|
106
|
+
- `--file <path>` — override the file resolution (highest priority).
|
|
107
|
+
- `--json` — emit structured JSON instead of the human tree view.
|
|
108
|
+
- `--with-json` — human output followed by JSON.
|
|
109
|
+
|
|
110
|
+
The file is resolved in this order:
|
|
111
|
+
|
|
112
|
+
1. `--file <path>`
|
|
113
|
+
2. `$NOGGIN_FILE` environment variable
|
|
114
|
+
3. `~/.noggin.yaml`
|
|
115
|
+
|
|
116
|
+
Use `noggin where` at any time to print which file would be used and
|
|
117
|
+
why.
|
|
118
|
+
|
|
119
|
+
Commands that change or inspect a target also take `--goto [path]`.
|
|
120
|
+
With no path, `--goto` activates the command's target; with a path,
|
|
121
|
+
the path resolves from the command target (not from the previously
|
|
122
|
+
active item).
|
|
123
|
+
|
|
124
|
+
Common flags can appear before or after the verb.
|
|
125
|
+
|
|
126
|
+
| Verb | Effect |
|
|
127
|
+
|---|---|
|
|
128
|
+
| `push <title>` | Create a child of active and make it active. |
|
|
129
|
+
| `add <title> [--before\|--after\|--into <path>] [--goto [path]]` | Create a child of active by default. `--before <path>` / `--after <path>` insert as a sibling of the anchor; `--into <path>` makes it the last child of the anchor. Active does **not** change unless `--goto` is present. |
|
|
130
|
+
| `move [<path>] (--before\|--after\|--into <path>) [--goto [path]]` | Relocate an item. Default target is the active item. Exactly one of `--before` / `--after` / `--into` is required. Active is preserved by key, so the computed path may change but `📍` stays on the same item. Cycles (anchor in the moved subtree) are rejected. |
|
|
131
|
+
| `goto <path>` | Make the item at `<path>` active. |
|
|
132
|
+
| `done [<path>] [--force\|--close-all]` | Mark an item done, then make the target's parent active. Idempotent (no error if already done). Refuses if open descendants exist unless `--close-all` first closes them or `--force` closes just the target anyway. Root items leave no active item after completion. |
|
|
133
|
+
| `pop [--force\|--close-all]` | Shorthand for `done` on the active item (no path). Honors `--force` and `--close-all` the same way. |
|
|
134
|
+
| `edit [<path>] [--done\|--open] [--title T] [--force\|--close-all] [--goto [path]]` | Idempotent mutation of a single item's lifecycle state and/or title. Pass at least one of `--done` / `--open` / `--title`. Active is unchanged unless `--goto` is passed. When closing (`--done`), the same open-descendant rules apply as `done`: `--force` closes anyway, `--close-all` closes descendants first. Replaces the older `set-state` and `retitle` verbs. |
|
|
135
|
+
| `show [<path>] [--no-children\|--with-descendants] [--with-siblings] [--with-all] [--with-notes] [--goto [path]]` | Current-position view: ancestor spine, sibling peers, current-item details, and first-level children. Default target is active. `--no-children` omits children. `--with-siblings` also includes the full sibling row at every ancestor depth (sibling subtrees stay collapsed). `--with-descendants` expands the target's subtree recursively. `--with-all` = `--with-siblings --with-descendants`. `--with-notes` appends note bodies. `--no-children` and `--with-descendants` are mutually exclusive. |
|
|
136
|
+
| `note [<path>] <text…> [--goto [path]]` | Append a timestamped note. |
|
|
137
|
+
| `delete <path> [--recursive]` | Remove an item. Refuses if the item has descendants unless `--recursive` is passed, in which case the whole subtree is deleted. If the active item is inside the deleted subtree, active falls back to the deleted item's parent (or becomes empty if it was a root). |
|
|
138
|
+
| `where` | Print which noggin file would be used and why (flag / env / default). |
|
|
139
|
+
| `help` | Print full help. |
|
|
140
|
+
|
|
141
|
+
### Tree output
|
|
142
|
+
|
|
143
|
+
Each row is `<absolute-path> <state> title <notes>`, with three
|
|
144
|
+
optional indicator slots:
|
|
145
|
+
|
|
146
|
+
- `📍` (between path and title) — this is the active item
|
|
147
|
+
- `✅` (between path and title) — done
|
|
148
|
+
- `✏️` (trailing) — has notes
|
|
149
|
+
|
|
150
|
+
Every row leads with the item's absolute path (`/1/3` etc.) so each
|
|
151
|
+
row self-describes — ancestors on the spine still read clearly even
|
|
152
|
+
though their siblings are trimmed from the view.
|
|
153
|
+
|
|
154
|
+
`show` keeps note bodies collapsed by default; pass `--with-notes` to
|
|
155
|
+
append them after the tree.
|
|
156
|
+
|
|
157
|
+
### JSON output
|
|
158
|
+
|
|
159
|
+
`--json` and `--with-json` emit a stable envelope shared with the VS Code
|
|
160
|
+
extension's language-model tools, so a single consumer can target both
|
|
161
|
+
surfaces.
|
|
162
|
+
|
|
163
|
+
```jsonc
|
|
164
|
+
// success
|
|
165
|
+
{
|
|
166
|
+
"status": "ok",
|
|
167
|
+
"schemaVersion": 2, // JSON_SCHEMA_VERSION — bump on breaking changes
|
|
168
|
+
"verb": "push", // command that produced this payload
|
|
169
|
+
"file": "/…/.noggin.yaml", // resolved noggin file
|
|
170
|
+
"data": { … } // verb-specific (CurrentTreeView, DeleteResult, …)
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// error (written to stderr; exit code matches error.exitCode)
|
|
174
|
+
{
|
|
175
|
+
"status": "error",
|
|
176
|
+
"schemaVersion": 2,
|
|
177
|
+
"verb": "push",
|
|
178
|
+
"file": "/…/.noggin.yaml",
|
|
179
|
+
"error": { "code": "title-required", "message": "…", "exitCode": 2 }
|
|
180
|
+
}
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
Inside `data`, a small whitelist of fields whose value matches their
|
|
184
|
+
declared default is **omitted** to keep payloads focused. A consumer
|
|
185
|
+
that doesn't see one of these fields should treat it as the default:
|
|
186
|
+
|
|
187
|
+
| Field | Omitted when |
|
|
188
|
+
|---|---|
|
|
189
|
+
| `parentKey` | `null` (item is a root) |
|
|
190
|
+
| `done` | `false` (item is still open) |
|
|
191
|
+
| `notes` | `[]` (no notes) |
|
|
192
|
+
| `activePath` | `null` (no active item) |
|
|
193
|
+
| `activeKey` | `null` (no active item) |
|
|
194
|
+
| `descendantCount` | `0` (in `DeleteResult`) |
|
|
195
|
+
| `view` | `null` (delete left the tree empty) |
|
|
196
|
+
| `exists` | `false` (in `where` output) |
|
|
197
|
+
| `env` | `null` (in `where` output, no `$NOGGIN_FILE` set) |
|
|
198
|
+
|
|
199
|
+
Everything else is always present, including the envelope itself
|
|
200
|
+
(`status`, `schemaVersion`, `verb`, `file`, `data` / `error`).
|
|
201
|
+
|
|
202
|
+
`ViewNode.children` is special: it's already a tri-state encoded by
|
|
203
|
+
presence (see `CurrentTreeView` below). Pruning doesn't touch it.
|
|
204
|
+
|
|
205
|
+
#### `CurrentTreeView`
|
|
206
|
+
|
|
207
|
+
Returned in `data` by every mutating verb and by `show`. Carries
|
|
208
|
+
everything the human "current tree" view shows, so JSON consumers can
|
|
209
|
+
reconstruct the same picture without re-reading the file.
|
|
210
|
+
|
|
211
|
+
```jsonc
|
|
212
|
+
{
|
|
213
|
+
"activePath": "/1/2/3", // path of the active item, or null
|
|
214
|
+
"activeKey": "i-…", // opaque key of the active item, or null
|
|
215
|
+
"targetKey": "i-…", // opaque key of the item the verb acted on
|
|
216
|
+
"items": [ // top of the rendered tree (see below)
|
|
217
|
+
{ …ItemView…, "children"?: [ ViewNode, … ] },
|
|
218
|
+
…
|
|
219
|
+
]
|
|
220
|
+
}
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
The view is a **recursive tree**. Each node (`ViewNode`) is an
|
|
224
|
+
`ItemView` (the usual `key, parentKey, path, position, title, done,
|
|
225
|
+
createdAt, notes` fields) plus an *optional* `children` slot:
|
|
226
|
+
|
|
227
|
+
| `children` | Meaning |
|
|
228
|
+
|---|---|
|
|
229
|
+
| present (array, possibly `[]`) | this view renders this node's child level; the array is the rendered children |
|
|
230
|
+
| **absent** | leaf of this view — the store may have a subtree here, but this view doesn't render it |
|
|
231
|
+
|
|
232
|
+
The recursion walks the direct ancestor chain from a root down to the
|
|
233
|
+
target. Sibling-of-ancestor items are **trimmed** — each intermediate
|
|
234
|
+
ancestor's `children` is a single-element array. The target's parent's
|
|
235
|
+
`children` is the full **peer row** (siblings + target itself, in
|
|
236
|
+
tree order). The target itself carries its first-level kids in
|
|
237
|
+
`children` (or omits the field entirely with `--no-children`).
|
|
238
|
+
|
|
239
|
+
Peers and grandkids (the children listed under the target) are
|
|
240
|
+
**leaves of the view**: no `children` field. To explore their
|
|
241
|
+
subtrees, call `show` on them.
|
|
242
|
+
|
|
243
|
+
`items` is either:
|
|
244
|
+
- a one-element array containing the target's root ancestor, when the
|
|
245
|
+
target is below the root level; or
|
|
246
|
+
- the target's full peer row (the actual store roots, in tree order),
|
|
247
|
+
when the target itself is a root.
|
|
248
|
+
|
|
249
|
+
To find the target node, walk the tree and match on `targetKey`.
|
|
250
|
+
|
|
251
|
+
Active is reported separately as both `activePath` and `activeKey`
|
|
252
|
+
because the active item may not appear in this view at all (e.g.
|
|
253
|
+
`add --into <other-branch>` returns a view of the new item, but
|
|
254
|
+
active is unchanged on a different branch). A consumer that wants
|
|
255
|
+
to show "📍 you're at `X`" needs the path explicitly.
|
|
256
|
+
|
|
257
|
+
An `ItemView` is `{ key, parentKey, path, position, title, done,
|
|
258
|
+
createdAt, notes }`. Notes are `{ timestamp, text }` objects.
|
|
259
|
+
|
|
260
|
+
#### `DeleteResult`
|
|
261
|
+
|
|
262
|
+
Returned in `data` by `delete`. Always carries the deletion record;
|
|
263
|
+
`view` is `null` only when the tree is left with no active item.
|
|
264
|
+
|
|
265
|
+
```jsonc
|
|
266
|
+
{
|
|
267
|
+
"deleted": { "key": "i-…", "path": "/1/2/3", "title": "…" },
|
|
268
|
+
"descendantCount": 2,
|
|
269
|
+
"view": { … CurrentTreeView … } | null
|
|
270
|
+
}
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
#### `where`
|
|
274
|
+
|
|
275
|
+
Returns the `FileResolution` shape: `{ file, source, exists,
|
|
276
|
+
defaultFile, env }` with all fields always present.
|
|
277
|
+
|
|
278
|
+
## File schema (v1)
|
|
279
|
+
|
|
280
|
+
The CLI reads and writes a single YAML file. Writes are atomic: the
|
|
281
|
+
CLI writes to `<file>.tmp-<pid>-<ts>` and renames over the real path,
|
|
282
|
+
so a partial write never corrupts the user's file.
|
|
283
|
+
|
|
284
|
+
### Top-level shape
|
|
285
|
+
|
|
286
|
+
```yaml
|
|
287
|
+
schemaVersion: 1
|
|
288
|
+
active: <key> | null # the item currently being worked on
|
|
289
|
+
items: [] # flat array; tree is implied via parentKey
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
- `schemaVersion` is required and must equal `1`. Any other value
|
|
293
|
+
causes the CLI to refuse to read the file.
|
|
294
|
+
- `active` is the opaque `key` of the active item, or `null` when
|
|
295
|
+
nothing is active. The active item's path is computed at runtime
|
|
296
|
+
by walking parents.
|
|
297
|
+
- `items` is a flat list. Tree structure comes from `parentKey`
|
|
298
|
+
pointers. Sibling order is array order, and display positions are
|
|
299
|
+
computed from that order.
|
|
300
|
+
|
|
301
|
+
### Item shape
|
|
302
|
+
|
|
303
|
+
```yaml
|
|
304
|
+
- key: i-20260616-184644-f04bf5 # opaque, immortal
|
|
305
|
+
parentKey: null # null = root item
|
|
306
|
+
title: marketplace import path
|
|
307
|
+
done: false # true once finished; reversible via `edit --open`
|
|
308
|
+
createdAt: 2026-06-16T18:46:44.071Z
|
|
309
|
+
notes:
|
|
310
|
+
- timestamp: 2026-06-16T18:46:45.625Z
|
|
311
|
+
text: found the storage abstraction in tableStorageService
|
|
312
|
+
- timestamp: 2026-06-16T18:46:46.200Z
|
|
313
|
+
text: |
|
|
314
|
+
Resumption note
|
|
315
|
+
|
|
316
|
+
Where I am
|
|
317
|
+
- branch users/davidorn/marketplace-import
|
|
318
|
+
...
|
|
319
|
+
- timestamp: 2026-06-16T18:50:11.300Z
|
|
320
|
+
text: closed # system-generated when the item is closed
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
### Field semantics
|
|
324
|
+
|
|
325
|
+
| Field | Purpose |
|
|
326
|
+
|---|---|
|
|
327
|
+
| `key` | Opaque, never reused. Format `i-YYYYMMDD-HHMMSS-<hex>` (display only — don't parse). Hidden from human output; included in JSON. |
|
|
328
|
+
| `parentKey` | Opaque key of the parent item, or `null` for roots. Multiple roots are allowed. |
|
|
329
|
+
| `title` | One-line human label. |
|
|
330
|
+
| `done` | `false` while the work is live, `true` once finished. Reversible via `edit --open`. |
|
|
331
|
+
| `createdAt` | ISO-8601 timestamp when the item was created. |
|
|
332
|
+
| `notes` | Append-only list of `{ timestamp, text }` objects. Each user note is added by `noggin note`. A single system-generated note with text `closed` is appended whenever the item transitions from open to done (via `done`, `pop`, `edit --done`, or the extension UI). Reopening with `edit --open` does not add or remove notes — the historical close stays in the log. |
|
|
333
|
+
|
|
334
|
+
### Invariants
|
|
335
|
+
|
|
336
|
+
The CLI validates these on every save:
|
|
337
|
+
|
|
338
|
+
1. Every item has a unique `key`.
|
|
339
|
+
2. Every non-null `parentKey` references an existing item.
|
|
340
|
+
3. `active`, if non-null, references an existing item. An active
|
|
341
|
+
item may have `done: true` after explicit `edit --done`; use
|
|
342
|
+
`edit --open` to revert, or `goto ..` to leave it.
|
|
343
|
+
4. Done items (`done: true`) remain in the tree (they are not
|
|
344
|
+
deleted) and can be reverted via `edit --open`.
|
|
345
|
+
5. A done item may have open descendants only when it was closed
|
|
346
|
+
with `--force`. The standard close paths (`done`, `pop`, `edit
|
|
347
|
+
--done` without flags, or with `--close-all`) preserve the
|
|
348
|
+
stronger invariant "done items have no open descendants".
|
|
349
|
+
|
|
350
|
+
## Resumption notes
|
|
351
|
+
|
|
352
|
+
Resumption notes are for **cold-start rehydration** — what an LLM (or
|
|
353
|
+
you, two days later) needs to resume work without reading the whole
|
|
354
|
+
session. They are just notes; the schema does not enforce structure.
|
|
355
|
+
|
|
356
|
+
A useful shape:
|
|
357
|
+
|
|
358
|
+
```
|
|
359
|
+
Where I am
|
|
360
|
+
- branch / file / cursor
|
|
361
|
+
- last action
|
|
362
|
+
|
|
363
|
+
What I believe
|
|
364
|
+
- the model of the system that this work assumes
|
|
365
|
+
- constraints and invariants
|
|
366
|
+
|
|
367
|
+
Ruled out
|
|
368
|
+
- approaches considered and rejected (and why)
|
|
369
|
+
|
|
370
|
+
Decisions in flight
|
|
371
|
+
- questions that aren't settled yet
|
|
372
|
+
|
|
373
|
+
Resume by
|
|
374
|
+
- the literal first thing to do on return
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
Append it as a normal note:
|
|
378
|
+
|
|
379
|
+
```powershell
|
|
380
|
+
node noggin.mjs note "Resumption note`n`nWhere I am`n - ...`nResume by`n - ..."
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
## Constraints
|
|
384
|
+
|
|
385
|
+
- Single-user, single-machine. No collaboration, no network, no
|
|
386
|
+
remote sync.
|
|
387
|
+
- The CLI is intentionally tiny: stdlib + `js-yaml`. Bundleable into
|
|
388
|
+
the Agency plugin if needed (vendor `js-yaml` next to `noggin.mjs` at
|
|
389
|
+
bundle time).
|
package/SKILL.md
ADDED
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: noggin
|
|
3
|
+
description: >
|
|
4
|
+
A working-memory tree for in-flight work — your second brain for
|
|
5
|
+
the work you can't fit in your head. Items form a tree: any item
|
|
6
|
+
can have child items. Push when you go on a side-quest so you
|
|
7
|
+
don't lose your place; add a child when you want to remember a todo
|
|
8
|
+
without diving in; goto an item to make it active; mark it done
|
|
9
|
+
when it's finished; use edit for explicit lifecycle correction. An item just has a
|
|
10
|
+
title, a done flag, and append-only notes — no fixed schema for
|
|
11
|
+
what content matters. Backed by a single YAML file via a small CLI; the
|
|
12
|
+
file is the source of truth. USE FOR: I'm pausing this to chase X,
|
|
13
|
+
side-quest, defer this, jot down a todo under what I'm doing, where
|
|
14
|
+
was I, push an item, add a todo, goto an item, move up with `goto ..`, mark
|
|
15
|
+
this done, edit, what was I working on, what's on my noggin, drop
|
|
16
|
+
a resumption note before I context-switch. DO NOT USE FOR: long-term plans
|
|
17
|
+
(use the engineer/plan workflow), team-visible task tracking (use
|
|
18
|
+
ADO/work items), persistent project memory (use repo memory or
|
|
19
|
+
docs).
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
# noggin (agent guide)
|
|
23
|
+
|
|
24
|
+
A small, single-user working-memory tree. Lives in `~/.noggin.yaml`.
|
|
25
|
+
Driven by `noggin.mjs` next to this file. **The CLI is the only interface
|
|
26
|
+
you should use.** Don't open the YAML file directly.
|
|
27
|
+
|
|
28
|
+
The full human reference (file schema, atomic-write story, complete
|
|
29
|
+
flag list) is in [README.md](README.md). This file is for you, the
|
|
30
|
+
agent: when to invoke the skill, what verb to pick, and how to behave
|
|
31
|
+
around its output.
|
|
32
|
+
|
|
33
|
+
## Mental model in 60 seconds
|
|
34
|
+
|
|
35
|
+
- Items form a tree. At most one item is **active**. The path from a
|
|
36
|
+
root to the active item is the user's current spine.
|
|
37
|
+
- Open items that are not active are paused. Done items stay in the
|
|
38
|
+
tree under their parent so the user can see what got finished.
|
|
39
|
+
- **push** = create a child of active and become it (doing this now).
|
|
40
|
+
- **add** = create a child without becoming it (remember for later).
|
|
41
|
+
- An item has: title, done flag, a `createdAt` timestamp, and
|
|
42
|
+
append-only notes. Nothing else. If something matters, write a
|
|
43
|
+
`note`. Closing an item appends a system-generated `closed` note
|
|
44
|
+
whose timestamp is the close time.
|
|
45
|
+
|
|
46
|
+
## Path shorthand
|
|
47
|
+
|
|
48
|
+
| Token | Meaning |
|
|
49
|
+
|---|---|
|
|
50
|
+
| `/1/2/3` | **absolute** (positions from root). Always starts with `/`. |
|
|
51
|
+
| `.` | active item |
|
|
52
|
+
| `..` | parent of active |
|
|
53
|
+
| `-` / `+` | previous / next sibling of active |
|
|
54
|
+
| `./X`, `../X`, `-/X`, `+/X` | descendants from those anchors |
|
|
55
|
+
| `X` / `X/Y` | bare positions are short for `./X` / `./X/Y` — **relative to active** |
|
|
56
|
+
|
|
57
|
+
The leading `/` is the only marker that makes a path absolute.
|
|
58
|
+
Everything else is relative and needs an active item. Output (from
|
|
59
|
+
`show`, JSON `activePath`, error messages, etc.) is always in the
|
|
60
|
+
canonical absolute form `/…`. Paths are display coordinates, not
|
|
61
|
+
stable IDs. Don't store them.
|
|
62
|
+
|
|
63
|
+
## Verb selection (the main job)
|
|
64
|
+
|
|
65
|
+
| What the user said | Verb |
|
|
66
|
+
|---|---|
|
|
67
|
+
| "Pause this — chase X first" / "drop everything" | `push <title>` |
|
|
68
|
+
| "While we're here, jot down Y" / "don't forget Z" | `add <title>` (place with `--before` / `--after` / `--into` if order matters) |
|
|
69
|
+
| "Switch back to Y" / "go to that thing" | `goto <path>` |
|
|
70
|
+
| "This is finished" (active) | `done` (or `pop`) — surfaces to parent |
|
|
71
|
+
| "That one over there is finished too" | `done <path>` |
|
|
72
|
+
| "Back to where I was" if side-quest done | `done` |
|
|
73
|
+
| "Back to where I was" if not done | `goto ..` |
|
|
74
|
+
| "Actually not finished, undo it" | `edit [<path>] --open` |
|
|
75
|
+
| "Mark X done but don't move me" | `edit <path> --done` |
|
|
76
|
+
| "Close X and everything under it" | `edit <path> --done --close-all` (or `done <path> --close-all`) |
|
|
77
|
+
| "Where was I?" / "what's on my noggin?" | `show` |
|
|
78
|
+
| "Reorder these" / "move that one up" | `move [<path>] (--before\|--after\|--into <anchor>)` |
|
|
79
|
+
| "Add a note about X" | `note <text>` (active) or `note <path> <text>` |
|
|
80
|
+
| "Rename this" | `edit [<path>] --title <new title>` |
|
|
81
|
+
| "Drop this" / "never mind, delete it" | `delete <path>` (add `--recursive` if it has children) |
|
|
82
|
+
|
|
83
|
+
Default to `push` for active side-quests, `add` for everything that
|
|
84
|
+
can wait. The cost of `add` is near zero — capture stray "we should
|
|
85
|
+
also…" remarks rather than letting them evaporate.
|
|
86
|
+
|
|
87
|
+
## Behavioral protocol
|
|
88
|
+
|
|
89
|
+
1. **Watch for switch phrases.** "pause this," "side-quest," "defer
|
|
90
|
+
this," "drop this for a sec," "while we're here also…," "where
|
|
91
|
+
were we?" — these are cues to invoke the skill.
|
|
92
|
+
2. **Capture state on the outgoing item before pushing or leaving.**
|
|
93
|
+
A short `note`, or a longer resumption note (template below) when
|
|
94
|
+
the switch is non-trivial.
|
|
95
|
+
3. **Acknowledge the change in one line.** e.g. "Pushed `/1/2/3 —
|
|
96
|
+
spike storage layer`. Spine: `/1` → `/1/2` → `/1/2/3`."
|
|
97
|
+
4. **Echo CLI output in chat.** The user shouldn't have to expand
|
|
98
|
+
hidden tool sections to see results. Include the meaningful
|
|
99
|
+
command output in your reply after every noggin call.
|
|
100
|
+
5. **Always print `show` output in chat by default**, even when the
|
|
101
|
+
user didn't explicitly ask to "show output."
|
|
102
|
+
6. **When the user asks to see output, quote it verbatim** (or a
|
|
103
|
+
clearly labeled trimmed excerpt if it is very large).
|
|
104
|
+
7. **Don't background-sync.** The file is the user's; never modify it
|
|
105
|
+
without an explicit user-visible action.
|
|
106
|
+
8. **Don't block on it.** If the CLI errors, surface the error, fall
|
|
107
|
+
back to plain conversation, and move on. Noggin is a memory aid,
|
|
108
|
+
not a gate.
|
|
109
|
+
9. **The CLI is the only interface.** Don't `Test-Path`, `cat`, or
|
|
110
|
+
grep the YAML file. If the CLI's output doesn't answer your
|
|
111
|
+
question, that's a CLI bug — file/fix it or accept the answer it
|
|
112
|
+
gave you.
|
|
113
|
+
10. **In VS Code, prefer the language model tools** (`#nogginShow`,
|
|
114
|
+
`#nogginPush`, `#nogginAdd`, `#nogginGoto`, `#nogginDone`,
|
|
115
|
+
`#nogginPop`, `#nogginNote`, `#nogginEdit`,
|
|
116
|
+
`#nogginMove`, `#nogginDelete`) over shelling out to `noggin.mjs`. The tools always
|
|
117
|
+
target the noggin the user has open in the editor. If you do shell
|
|
118
|
+
out, the CLI honors the `NOGGIN_FILE` env var, which the extension
|
|
119
|
+
sets in every terminal — so `node noggin.mjs ...` in a VS Code
|
|
120
|
+
terminal still hits the right file. Use `noggin where` if you need
|
|
121
|
+
to confirm which file the CLI would touch.
|
|
122
|
+
11. **Outside VS Code (Copilot CLI, Claude Code, Codex), prefer the MCP
|
|
123
|
+
tools** (`noggin_show`, `noggin_push`, etc.) when the host has the
|
|
124
|
+
noggin MCP server wired up — they return the same JSON envelope as
|
|
125
|
+
the CLI with no spawn cost. Fall back to `noggin.mjs` only when no
|
|
126
|
+
tool surface is available.
|
|
127
|
+
|
|
128
|
+
## Resumption note template
|
|
129
|
+
|
|
130
|
+
When the user is about to context-switch on something non-trivial,
|
|
131
|
+
offer to append a structured note in this shape:
|
|
132
|
+
|
|
133
|
+
```
|
|
134
|
+
Where I am
|
|
135
|
+
- branch / file / cursor
|
|
136
|
+
- last action
|
|
137
|
+
|
|
138
|
+
What I believe
|
|
139
|
+
- the model this work assumes
|
|
140
|
+
- constraints and invariants
|
|
141
|
+
|
|
142
|
+
Ruled out
|
|
143
|
+
- approaches considered and rejected (and why)
|
|
144
|
+
|
|
145
|
+
Decisions in flight
|
|
146
|
+
- questions that aren't settled yet
|
|
147
|
+
|
|
148
|
+
Resume by
|
|
149
|
+
- the literal first thing to do on return
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
It's a regular `note`; the schema doesn't enforce structure. Offer
|
|
153
|
+
the template; don't impose it.
|