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/noggin-api.d.mts
ADDED
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
// Type declarations for noggin-api.mjs.
|
|
2
|
+
//
|
|
3
|
+
// Hand-written to match the JS implementation; no build step. The .mjs uses
|
|
4
|
+
// /// <reference path="./noggin-api.d.ts" /> so editors and tsc --noEmit can
|
|
5
|
+
// check usages against this contract.
|
|
6
|
+
|
|
7
|
+
export type NogginFilePath = string;
|
|
8
|
+
export type ItemKey = string;
|
|
9
|
+
export type ItemPath = string;
|
|
10
|
+
export type IsoTimestamp = string;
|
|
11
|
+
|
|
12
|
+
export interface Note {
|
|
13
|
+
timestamp: IsoTimestamp;
|
|
14
|
+
text: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface Item {
|
|
18
|
+
key: ItemKey;
|
|
19
|
+
parentKey: ItemKey | null;
|
|
20
|
+
title: string;
|
|
21
|
+
done: boolean;
|
|
22
|
+
createdAt?: IsoTimestamp;
|
|
23
|
+
notes: Note[];
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface Store {
|
|
27
|
+
schemaVersion: number;
|
|
28
|
+
active: ItemKey | null;
|
|
29
|
+
items: Item[];
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** An Item enriched with computed path and 1-based sibling position. */
|
|
33
|
+
export interface ItemView extends Item {
|
|
34
|
+
path: ItemPath | null;
|
|
35
|
+
position: number | null;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* A node in a CurrentTreeView's recursive tree. Carries the usual
|
|
40
|
+
* ItemView fields plus an *optional* `children` slot:
|
|
41
|
+
*
|
|
42
|
+
* children present this view renders this node's child level (the
|
|
43
|
+
* array may be empty — e.g. target with no kids)
|
|
44
|
+
* children absent leaf of this view; the store may have a subtree
|
|
45
|
+
* here, but this view doesn't render it
|
|
46
|
+
*
|
|
47
|
+
* The recursion walks the direct ancestor chain from root to target.
|
|
48
|
+
* Each ancestor has a single-element `children`. The target's parent
|
|
49
|
+
* has the full peer row. The target has `children` populated with its
|
|
50
|
+
* first-level kids (or no `children` field at all with `--nokids`).
|
|
51
|
+
* Peers and grandkids are leaves and have no `children` field.
|
|
52
|
+
*/
|
|
53
|
+
export interface ViewNode extends ItemView {
|
|
54
|
+
children?: ViewNode[];
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** Shape returned by every mutating verb and by `show`/`view`. */
|
|
58
|
+
export interface CurrentTreeView {
|
|
59
|
+
/** Path of the active item, or null. May differ from the target —
|
|
60
|
+
* active is the user's persistent cursor (📍) and is not necessarily
|
|
61
|
+
* on the spine of this view. */
|
|
62
|
+
activePath: ItemPath | null;
|
|
63
|
+
/** Stable key of the active item, or null. */
|
|
64
|
+
activeKey: ItemKey | null;
|
|
65
|
+
/** Stable key of the item the verb acted on. To grab the full row,
|
|
66
|
+
* walk `items` and find the node whose `key === targetKey`. */
|
|
67
|
+
targetKey: ItemKey;
|
|
68
|
+
/**
|
|
69
|
+
* Top of the rendered tree. Contains either:
|
|
70
|
+
* - a single root ancestor (when the target is below depth 0); or
|
|
71
|
+
* - the target's full peer row (when the target itself is a root).
|
|
72
|
+
* Either way, every node along the path from `items` down to the
|
|
73
|
+
* target has a non-null `children`; leaves of the view have `null`.
|
|
74
|
+
*/
|
|
75
|
+
items: ViewNode[];
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/** Identifying tombstone for a deleted item — survives the delete itself. */
|
|
79
|
+
export interface DeletedItem {
|
|
80
|
+
key: ItemKey;
|
|
81
|
+
path: ItemPath | null;
|
|
82
|
+
title: string;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export type PlacementKind = 'before' | 'after' | 'into';
|
|
86
|
+
|
|
87
|
+
export interface Placement {
|
|
88
|
+
kind: PlacementKind;
|
|
89
|
+
/** Path to the anchor item. Resolved against the live store. */
|
|
90
|
+
anchor: ItemPath;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/** Optional reposition-after-write. Mirrors the CLI `--goto` flag. */
|
|
94
|
+
export interface GotoOption {
|
|
95
|
+
/**
|
|
96
|
+
* Path resolved relative to the operation's target.
|
|
97
|
+
* `true` (or omitted with bare `--goto`) means `.` (the target itself).
|
|
98
|
+
*/
|
|
99
|
+
goto?: ItemPath | true;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export interface FileResolution {
|
|
103
|
+
file: NogginFilePath;
|
|
104
|
+
source: 'flag' | 'env' | 'default';
|
|
105
|
+
exists: boolean;
|
|
106
|
+
defaultFile: NogginFilePath;
|
|
107
|
+
/** Value of $NOGGIN_FILE at the time of resolution, or null. */
|
|
108
|
+
env: string | null;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export interface DeleteResult {
|
|
112
|
+
deleted: DeletedItem;
|
|
113
|
+
descendantCount: number;
|
|
114
|
+
/** Null only when the resulting tree has no active item (e.g. a root was deleted). */
|
|
115
|
+
view: CurrentTreeView | null;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export type NogginErrorCode =
|
|
119
|
+
| 'noggin-error'
|
|
120
|
+
| 'no-active-item'
|
|
121
|
+
| 'no-file'
|
|
122
|
+
| 'path-not-found'
|
|
123
|
+
| 'path-required'
|
|
124
|
+
| 'cycle'
|
|
125
|
+
| 'placement-missing'
|
|
126
|
+
| 'placement-invalid'
|
|
127
|
+
| 'title-required'
|
|
128
|
+
| 'text-required'
|
|
129
|
+
| 'nothing-to-edit'
|
|
130
|
+
| 'option-misused'
|
|
131
|
+
| 'goto-unsupported'
|
|
132
|
+
| 'goto-base-missing'
|
|
133
|
+
| 'goto-path-required'
|
|
134
|
+
| 'goto-unresolved'
|
|
135
|
+
| 'has-descendants'
|
|
136
|
+
| 'open-descendants'
|
|
137
|
+
| 'pop-no-path'
|
|
138
|
+
| 'invalid-note'
|
|
139
|
+
| 'invalid-store'
|
|
140
|
+
| 'unsupported-schema'
|
|
141
|
+
| 'io';
|
|
142
|
+
|
|
143
|
+
export class NogginError extends Error {
|
|
144
|
+
readonly code: NogginErrorCode | string;
|
|
145
|
+
/** Mirrors the CLI exit code (1 = runtime/state, 2 = usage/parse/invalid). */
|
|
146
|
+
readonly exitCode: number;
|
|
147
|
+
constructor(message: string, opts?: { code?: string; exitCode?: number });
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// ── Constants ────────────────────────────────────────────────────────────────
|
|
151
|
+
|
|
152
|
+
export const SCHEMA_VERSION: number;
|
|
153
|
+
export const JSON_SCHEMA_VERSION: number;
|
|
154
|
+
export const DEFAULT_FILE: NogginFilePath;
|
|
155
|
+
|
|
156
|
+
// ── Stateless functions ──────────────────────────────────────────────────────
|
|
157
|
+
|
|
158
|
+
export function resolveFile(opts?: { file?: NogginFilePath; env?: Record<string, string | undefined> }): FileResolution;
|
|
159
|
+
|
|
160
|
+
export function loadStore(file: NogginFilePath): Store;
|
|
161
|
+
export function saveStore(file: NogginFilePath, store: Store): void;
|
|
162
|
+
|
|
163
|
+
export function resolvePath(store: Store, path: ItemPath): Item;
|
|
164
|
+
export function tryResolvePath(store: Store, path: ItemPath): Item | null;
|
|
165
|
+
|
|
166
|
+
export function pathOf(store: Store, item: Item | null | undefined): ItemPath | null;
|
|
167
|
+
export function childrenOf(store: Store, parentKey: ItemKey | null | undefined): Item[];
|
|
168
|
+
|
|
169
|
+
export function buildView(
|
|
170
|
+
store: Store,
|
|
171
|
+
target: Item,
|
|
172
|
+
opts?: { includeChildren?: boolean; withSiblings?: boolean; withDescendants?: boolean }
|
|
173
|
+
): CurrentTreeView;
|
|
174
|
+
|
|
175
|
+
// ── JSON envelope ────────────────────────────────────────────────────────────
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Canonical JSON envelope shared by the CLI `--json` output and the VS
|
|
179
|
+
* Code extension's language-model tools. Both surfaces emit this exact
|
|
180
|
+
* shape so a single consumer (or test) can target both.
|
|
181
|
+
*/
|
|
182
|
+
export interface SuccessEnvelope<T = unknown> {
|
|
183
|
+
status: 'ok';
|
|
184
|
+
schemaVersion: number;
|
|
185
|
+
verb: string | null;
|
|
186
|
+
file: NogginFilePath | null;
|
|
187
|
+
data: T;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
export interface ErrorEnvelope {
|
|
191
|
+
status: 'error';
|
|
192
|
+
schemaVersion: number;
|
|
193
|
+
verb: string | null;
|
|
194
|
+
file: NogginFilePath | null;
|
|
195
|
+
error: {
|
|
196
|
+
code: NogginErrorCode | string;
|
|
197
|
+
message: string;
|
|
198
|
+
exitCode: number;
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
export type JsonEnvelope<T = unknown> = SuccessEnvelope<T> | ErrorEnvelope;
|
|
203
|
+
|
|
204
|
+
export function formatSuccess<T>(opts: {
|
|
205
|
+
verb?: string;
|
|
206
|
+
file?: NogginFilePath | null;
|
|
207
|
+
data?: T;
|
|
208
|
+
}): SuccessEnvelope<T>;
|
|
209
|
+
|
|
210
|
+
export function formatError(opts: {
|
|
211
|
+
verb?: string;
|
|
212
|
+
file?: NogginFilePath | null;
|
|
213
|
+
error: unknown;
|
|
214
|
+
}): ErrorEnvelope;
|
|
215
|
+
|
|
216
|
+
// ── Verb functions (stateless) ───────────────────────────────────────────────
|
|
217
|
+
|
|
218
|
+
export interface PushOptions { title: string }
|
|
219
|
+
export interface AddOptions extends GotoOption { title: string; placement?: Placement }
|
|
220
|
+
export interface MoveOptions extends GotoOption { path?: ItemPath; placement: Placement }
|
|
221
|
+
export interface GotoOptions { path?: ItemPath }
|
|
222
|
+
|
|
223
|
+
/** Shared shape for closing verbs (`done`, `pop`, `set --done`). */
|
|
224
|
+
export interface CloseOptions {
|
|
225
|
+
/** Skip the open-descendant safety check; close the target even with open kids. */
|
|
226
|
+
force?: boolean;
|
|
227
|
+
/** Close every open descendant first (each gets its own system close note). */
|
|
228
|
+
closeAll?: boolean;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
export interface DoneOptions extends CloseOptions { path?: ItemPath }
|
|
232
|
+
export interface PopOptions extends CloseOptions {}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* `edit` combines the old `set-state` and `retitle` verbs into one
|
|
236
|
+
* idempotent mutation verb. Specify at least one of `done`/`title`;
|
|
237
|
+
* each operation is a no-op when the value already matches.
|
|
238
|
+
*/
|
|
239
|
+
export interface EditOptions extends GotoOption, CloseOptions {
|
|
240
|
+
path?: ItemPath;
|
|
241
|
+
/** true → close, false → reopen, undefined → don't touch state. */
|
|
242
|
+
done?: boolean;
|
|
243
|
+
/** New title (trimmed). Empty/whitespace is ignored, not an error. */
|
|
244
|
+
title?: string;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
export interface ShowOptions extends GotoOption {
|
|
248
|
+
path?: ItemPath;
|
|
249
|
+
/** Whether to expand the target's `children` field. Default true; set false for --no-children. */
|
|
250
|
+
includeChildren?: boolean;
|
|
251
|
+
/** Show note bodies in human output (no effect on JSON — notes are always present). */
|
|
252
|
+
withNotes?: boolean;
|
|
253
|
+
/** Include the full sibling row at every ancestor depth. */
|
|
254
|
+
withSiblings?: boolean;
|
|
255
|
+
/** Expand the target's subtree recursively. */
|
|
256
|
+
withDescendants?: boolean;
|
|
257
|
+
}
|
|
258
|
+
export interface NoteOptions extends GotoOption { path?: ItemPath; text: string }
|
|
259
|
+
export interface DeleteOptions { path: ItemPath; recursive?: boolean }
|
|
260
|
+
|
|
261
|
+
export function apiPush(file: NogginFilePath, opts: PushOptions): CurrentTreeView;
|
|
262
|
+
export function apiAdd(file: NogginFilePath, opts: AddOptions): CurrentTreeView;
|
|
263
|
+
export function apiMove(file: NogginFilePath, opts: MoveOptions): CurrentTreeView;
|
|
264
|
+
export function apiGoto(file: NogginFilePath, opts: GotoOptions): CurrentTreeView;
|
|
265
|
+
export function apiDone(file: NogginFilePath, opts?: DoneOptions): CurrentTreeView;
|
|
266
|
+
export function apiPop(file: NogginFilePath, opts?: PopOptions): CurrentTreeView;
|
|
267
|
+
export function apiEdit(file: NogginFilePath, opts: EditOptions): CurrentTreeView;
|
|
268
|
+
export function apiShow(file: NogginFilePath, opts?: ShowOptions): CurrentTreeView | null;
|
|
269
|
+
export function apiNote(file: NogginFilePath, opts: NoteOptions): CurrentTreeView;
|
|
270
|
+
export function apiDelete(file: NogginFilePath, opts: DeleteOptions): DeleteResult;
|
|
271
|
+
export function apiWhere(opts?: { file?: NogginFilePath; env?: Record<string, string | undefined> }): FileResolution;
|
|
272
|
+
|
|
273
|
+
// ── Noggin class ─────────────────────────────────────────────────────────────
|
|
274
|
+
|
|
275
|
+
export interface Disposable { dispose(): void }
|
|
276
|
+
export type Event<T> = (handler: (e: T) => void) => Disposable;
|
|
277
|
+
|
|
278
|
+
export class Noggin {
|
|
279
|
+
constructor(file: NogginFilePath, opts?: { watch?: boolean });
|
|
280
|
+
|
|
281
|
+
readonly file: NogginFilePath;
|
|
282
|
+
|
|
283
|
+
readonly store: Readonly<Store>;
|
|
284
|
+
readonly active: Item | null;
|
|
285
|
+
readonly roots: Item[];
|
|
286
|
+
|
|
287
|
+
findByKey(key: ItemKey | null | undefined): Item | null;
|
|
288
|
+
childrenOf(parentKey: ItemKey | null | undefined): Item[];
|
|
289
|
+
pathOf(item: Item | null | undefined): ItemPath | null;
|
|
290
|
+
resolvePath(path: ItemPath): Item;
|
|
291
|
+
tryResolvePath(path: ItemPath): Item | null;
|
|
292
|
+
|
|
293
|
+
view(
|
|
294
|
+
target?: Item | ItemPath | null,
|
|
295
|
+
opts?: { includeChildren?: boolean }
|
|
296
|
+
): CurrentTreeView | null;
|
|
297
|
+
|
|
298
|
+
reload(): boolean;
|
|
299
|
+
dispose(): void;
|
|
300
|
+
|
|
301
|
+
readonly onDidChange: Event<void>;
|
|
302
|
+
readonly onDidError: Event<NogginError>;
|
|
303
|
+
|
|
304
|
+
push(opts: PushOptions): CurrentTreeView;
|
|
305
|
+
add(opts: AddOptions): CurrentTreeView;
|
|
306
|
+
move(opts: MoveOptions): CurrentTreeView;
|
|
307
|
+
goto(path: ItemPath): CurrentTreeView;
|
|
308
|
+
done(opts?: DoneOptions): CurrentTreeView;
|
|
309
|
+
pop(opts?: PopOptions): CurrentTreeView;
|
|
310
|
+
edit(opts: EditOptions): CurrentTreeView;
|
|
311
|
+
show(opts?: ShowOptions): CurrentTreeView | null;
|
|
312
|
+
note(opts: NoteOptions): CurrentTreeView;
|
|
313
|
+
delete(opts: DeleteOptions): DeleteResult;
|
|
314
|
+
where(): FileResolution;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
export function openNoggin(file: NogginFilePath): Noggin;
|