noggin-cli 0.1.3 → 0.4.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 CHANGED
@@ -4,8 +4,8 @@ A small, single-user working-memory tree for in-flight work — your
4
4
  second brain for the stuff you can't fit in your head.
5
5
 
6
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
7
+ <path>`, or set `$NOGGIN` to point every invocation at a
8
+ different file. (The VS Code extension sets `NOGGIN` in its
9
9
  terminals so the CLI follows whichever noggin you have open.) Driven
10
10
  by [`noggin.mjs`](noggin.mjs) next to this file. The YAML file is the source
11
11
  of truth; the CLI is the only sanctioned way to read or write it.
@@ -110,10 +110,12 @@ Every command takes:
110
110
  The file is resolved in this order:
111
111
 
112
112
  1. `--file <path>`
113
- 2. `$NOGGIN_FILE` environment variable
113
+ 2. `$NOGGIN` environment variable
114
114
  3. `~/.noggin.yaml`
115
115
 
116
- Use `noggin where` at any time to print which file would be used and
116
+ Use `noggin where` at any time to print the canonical location of
117
+ the noggin currently in use (a round-trippable string like
118
+ `~/.noggin.yaml`, `./.noggin.yaml`, or an absolute path).
117
119
  why.
118
120
 
119
121
  Commands that change or inspect a target also take `--goto [path]`.
@@ -135,7 +137,8 @@ Common flags can appear before or after the verb.
135
137
  | `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
138
  | `note [<path>] <text…> [--goto [path]]` | Append a timestamped note. |
137
139
  | `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). |
140
+ | `where` | Print the canonical location string of the noggin in use. |
141
+ | `copy <from> <to>` | Append every item from the `<from>` noggin into the `<to>` noggin. Whole-noggin, append-only. Source roots become new roots of dest, after any existing dest content. Keys are regenerated; notes, done state, and `createdAt` are preserved verbatim. Source is not modified. Dest's `active` is unchanged. v1 copies the entire source; subtree slicing is reserved for a later version. |
139
142
  | `help` | Print full help. |
140
143
 
141
144
  ### Tree output
@@ -156,30 +159,33 @@ append them after the tree.
156
159
 
157
160
  ### JSON output
158
161
 
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
+ `--json` and `--with-json` emit a stable response envelope shared with
163
+ the VS Code extension's language-model tools, so a single consumer can
164
+ target both surfaces.
162
165
 
163
166
  ```jsonc
164
167
  // success
165
168
  {
166
169
  "status": "ok",
167
- "schemaVersion": 2, // JSON_SCHEMA_VERSION — bump on breaking changes
170
+ "envelopeVersion": 3, // RESPONSE_ENVELOPE_VERSION — bump on breaking changes
168
171
  "verb": "push", // command that produced this payload
169
- "file": "/…/.noggin.yaml", // resolved noggin file
170
172
  "data": { … } // verb-specific (CurrentTreeView, DeleteResult, …)
171
173
  }
172
174
 
173
175
  // error (written to stderr; exit code matches error.exitCode)
174
176
  {
175
177
  "status": "error",
176
- "schemaVersion": 2,
178
+ "envelopeVersion": 3,
177
179
  "verb": "push",
178
- "file": "/…/.noggin.yaml",
179
180
  "error": { "code": "title-required", "message": "…", "exitCode": 2 }
180
181
  }
181
182
  ```
182
183
 
184
+ `envelopeVersion` versions the wrapper shape (and the per-verb
185
+ payloads inside `data`), independently of the on-disk document's
186
+ `schemaVersion` (see [File schema](#file-schema-v1)). The two rev
187
+ on different cadences.
188
+
183
189
  Inside `data`, a small whitelist of fields whose value matches their
184
190
  declared default is **omitted** to keep payloads focused. A consumer
185
191
  that doesn't see one of these fields should treat it as the default:
@@ -193,11 +199,12 @@ that doesn't see one of these fields should treat it as the default:
193
199
  | `activeKey` | `null` (no active item) |
194
200
  | `descendantCount` | `0` (in `DeleteResult`) |
195
201
  | `view` | `null` (delete left the tree empty) |
196
- | `exists` | `false` (in `where` output) |
197
- | `env` | `null` (in `where` output, no `$NOGGIN_FILE` set) |
198
202
 
199
203
  Everything else is always present, including the envelope itself
200
- (`status`, `schemaVersion`, `verb`, `file`, `data` / `error`).
204
+ (`status`, `envelopeVersion`, `verb`, `data` / `error`).
205
+
206
+ `where --json` is a special case: `data` is a plain string (the
207
+ canonical location of the noggin), not a structured object.
201
208
 
202
209
  `ViewNode.children` is special: it's already a tri-state encoded by
203
210
  presence (see `CurrentTreeView` below). Pruning doesn't touch it.
@@ -272,8 +279,59 @@ Returned in `data` by `delete`. Always carries the deletion record;
272
279
 
273
280
  #### `where`
274
281
 
275
- Returns the `FileResolution` shape: `{ file, source, exists,
276
- defaultFile, env }` with all fields always present.
282
+ Returns the canonical location string of the noggin currently in use
283
+ the same string `openNoggin()` would accept to reopen it. The
284
+ string is round-trippable: `~/.noggin.yaml` stays as `~/.noggin.yaml`,
285
+ `./.noggin.yaml` stays as `./.noggin.yaml`, absolute paths stay
286
+ absolute. Both the human and `--json` output are this single string.
287
+
288
+ ## JavaScript API
289
+
290
+ For consumers embedding noggin in a Node process (the VS Code
291
+ extension, custom tooling), there's a small public API beyond the
292
+ CLI:
293
+
294
+ ```js
295
+ import { fileNoggin } from 'noggin/backends/file';
296
+
297
+ const noggin = await fileNoggin('/path/to/.noggin.yaml', { watch: true });
298
+ const view = await noggin.push({ title: 'spike storage layer' });
299
+ console.log(noggin.active?.title);
300
+ noggin.onDidChange(() => render(noggin.items));
301
+ await noggin.dispose();
302
+ ```
303
+
304
+ ### Public surface
305
+
306
+ | What | Where |
307
+ |---|---|
308
+ | `Noggin` class — live noggin with verb methods, accessors, events | `noggin/noggin-api.mjs` |
309
+ | `fileNoggin(path, opts?): Promise<Noggin>` — open a file-backed noggin | `noggin/backends/file.mjs` |
310
+ | `applyX(doc, opts, ctx?)` — pure verb functions over `NogginDocument` | `noggin/noggin-api.mjs` |
311
+ | `fromYaml` / `toYaml` / `fromJson` / `toJson` — serializers | `noggin/serializers/{yaml,json}.mjs` |
312
+ | `NogginError`, `NogginErrorCode` — typed errors | `noggin/noggin-api.mjs` |
313
+ | `formatSuccess` / `formatError` — response envelope helpers | `noggin/noggin-api.mjs` |
314
+ | `SCHEMA_VERSION`, `RESPONSE_ENVELOPE_VERSION` — constants | `noggin/noggin-api.mjs` |
315
+
316
+ All `Noggin` verb methods return `Promise`. Per-instance calls are
317
+ serialized (in-process queue); cross-process callers should treat
318
+ the file as advisory-locked at the application layer.
319
+
320
+ ### `NogginDocument` shape
321
+
322
+ The serialized form (what the JSON Schema validates, what
323
+ serializers convert to/from) is just:
324
+
325
+ ```ts
326
+ interface NogginDocument {
327
+ schemaVersion: 1;
328
+ active: ItemKey | null;
329
+ items: Item[];
330
+ }
331
+ ```
332
+
333
+ A live `Noggin` does not expose `schemaVersion` — that's a wire
334
+ concern, owned by the serializers.
277
335
 
278
336
  ## File schema (v1)
279
337
 
@@ -281,6 +339,28 @@ The CLI reads and writes a single YAML file. Writes are atomic: the
281
339
  CLI writes to `<file>.tmp-<pid>-<ts>` and renames over the real path,
282
340
  so a partial write never corrupts the user's file.
283
341
 
342
+ A machine-readable JSON Schema for the noggin data model is published
343
+ at the repo root as [`noggin.schema.json`](../noggin.schema.json).
344
+ The schema describes the shape itself, independent of any particular
345
+ producer or consumer — YAML 1.2 is a JSON superset, so the same schema
346
+ validates both YAML and JSON renderings. To get autocomplete and inline
347
+ validation in VS Code, install the Red Hat YAML extension and add to
348
+ your settings:
349
+
350
+ ```jsonc
351
+ "yaml.schemas": {
352
+ "https://dornstein.github.io/noggin/noggin.schema.json": [
353
+ ".noggin.yaml",
354
+ "**/.noggin/*.yaml"
355
+ ]
356
+ }
357
+ ```
358
+
359
+ The CLI enforces stronger invariants than JSON Schema can express
360
+ (unique keys, `parentKey`/`active` referential integrity, the "done
361
+ items have no open descendants" rule unless force-closed) — see
362
+ [Invariants](#invariants) below.
363
+
284
364
  ### Top-level shape
285
365
 
286
366
  ```yaml
package/SKILL.md CHANGED
@@ -79,6 +79,7 @@ stable IDs. Don't store them.
79
79
  | "Add a note about X" | `note <text>` (active) or `note <path> <text>` |
80
80
  | "Rename this" | `edit [<path>] --title <new title>` |
81
81
  | "Drop this" / "never mind, delete it" | `delete <path>` (add `--recursive` if it has children) |
82
+ | "Migrate my noggin into this repo" / "copy the home noggin into this folder" | `copy <from> <to>` (whole-noggin, append-only; preserves notes and timestamps) |
82
83
 
83
84
  Default to `push` for active side-quests, `add` for everything that
84
85
  can wait. The cost of `add` is near zero — capture stray "we should
@@ -115,15 +116,22 @@ also…" remarks rather than letting them evaporate.
115
116
  `#nogginPop`, `#nogginNote`, `#nogginEdit`,
116
117
  `#nogginMove`, `#nogginDelete`) over shelling out to `noggin.mjs`. The tools always
117
118
  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
+ out, the CLI honors the `NOGGIN` env var, which the extension
119
120
  sets in every terminal — so `node noggin.mjs ...` in a VS Code
120
121
  terminal still hits the right file. Use `noggin where` if you need
121
122
  to confirm which file the CLI would touch.
122
- 11. **Outside VS Code (Copilot CLI, Claude Code, Codex), prefer the MCP
123
+ 11. **Outside VS Code (GitHub Copilot CLI, Claude Code, Codex), prefer the MCP
123
124
  tools** (`noggin_show`, `noggin_push`, etc.) when the host has the
124
125
  noggin MCP server wired up — they return the same JSON envelope as
125
126
  the CLI with no spawn cost. Fall back to `noggin.mjs` only when no
126
127
  tool surface is available.
128
+ 12. **The MCP server is multi-noggin: every tool call requires a `noggin`
129
+ parameter** — a canonical location string like `~/.noggin.yaml`,
130
+ `./.noggin.yaml`, or `file:///abs/path.yaml`. There is no
131
+ server-wide default. Pass the location the user is working with;
132
+ if you don't know it, ask. Use `noggin_where` to confirm a noggin
133
+ is reachable, or `noggin_factories` to discover what location forms
134
+ the server accepts.
127
135
 
128
136
  ## Resumption note template
129
137