pi-note 0.22.0 → 0.23.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 +6 -6
- package/dist-mcp/mcp/server.js +12 -6
- package/mcp/README.md +12 -6
- package/mcp/server.ts +13 -7
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# pi-note
|
|
2
2
|
|
|
3
|
+
[npm](https://www.npmjs.com/package/pi-note) · [Pi package directory](https://pi.dev/packages/pi-note)
|
|
4
|
+
|
|
3
5
|
Persistent [pinote](https://github.com/kvidzibo/pinote) tasks and Markdown handoffs
|
|
4
6
|
in Pi: selected-task footer status, `/pi-note`, and agent current-task read/update,
|
|
5
7
|
add, and tag-listing tools.
|
|
@@ -12,10 +14,8 @@ explicit initial-version limits; it is not activated by installing this extensio
|
|
|
12
14
|
## Install
|
|
13
15
|
|
|
14
16
|
Requires Pi 0.99.1+, Node.js 22.19+ and **pinote 0.3.0+** (`note` on PATH). Add and tag listing need **pinote 0.4.0+**.
|
|
15
|
-
|
|
16
|
-
`/reload` in interactive Pi.
|
|
17
|
-
first release use `pi install npm:pi-note` instead. It is not published yet. npm
|
|
18
|
-
installation does not run Python installers.
|
|
17
|
+
Install with `pi install npm:pi-note`, or `pi install ./pi` from this checkout,
|
|
18
|
+
then run `/reload` in interactive Pi. npm installation does not run Python installers.
|
|
19
19
|
|
|
20
20
|
### CLI setup and upgrades
|
|
21
21
|
|
|
@@ -390,13 +390,13 @@ data. Run visual checks under Xvfb/private D-Bus; never use personal task data.
|
|
|
390
390
|
OIDC trusted publishing and provenance. Existing versions are skipped; registry
|
|
391
391
|
failures fail the workflow. No npm token is stored in GitHub.
|
|
392
392
|
|
|
393
|
-
|
|
393
|
+
Only a package's first publication needs this manual bootstrap (not part of
|
|
394
|
+
installation). `pi-note` is already published; use automated releases above.
|
|
394
395
|
|
|
395
396
|
1. From `pi/`, run `npm login`, then `npm publish --ignore-scripts --access public`
|
|
396
397
|
to bootstrap `pi-note` after checking name availability.
|
|
397
398
|
2. In npm's package settings configure the GitHub trusted publisher: owner
|
|
398
399
|
`kvidzibo`, repository `pinote`, workflow `publish.yml`, no environment.
|
|
399
|
-
3. Add verified npm and Pi package-directory links here after publication.
|
|
400
400
|
|
|
401
401
|
The CLI source pin in `setup.ts` must point to a verified immutable commit with a
|
|
402
402
|
compatible Python package. Update it and the bundled-version setup text together
|
package/dist-mcp/mcp/server.js
CHANGED
|
@@ -27,7 +27,7 @@ const Action = z.object({ method: z.literal('native-ui/action'), params: z.objec
|
|
|
27
27
|
uri: z.literal(URI), revision: z.string().min(1).max(256), action: z.string().min(1).max(256),
|
|
28
28
|
value: z.union([z.string().max(20_000), z.number().finite(), z.boolean(), z.null()]).optional(), _meta: z.record(z.unknown()).optional(),
|
|
29
29
|
}) });
|
|
30
|
-
const server = new Server({ name: 'pinote', version: '0.
|
|
30
|
+
const server = new Server({ name: 'pinote', version: '0.23.0' }, { capabilities: { tools: {}, resources: {}, experimental: { 'native-ui-v1': {} } } });
|
|
31
31
|
// Serialize complete read/check/write cycles even when a host sends parallel calls.
|
|
32
32
|
let queue = Promise.resolve();
|
|
33
33
|
function serial(run) {
|
|
@@ -137,7 +137,13 @@ function staleAction() {
|
|
|
137
137
|
function secondaryActions(meta) {
|
|
138
138
|
return z.object({ footerSecondaryActions: z.literal(true) }).safeParse(meta?.['native-ui/features']).success;
|
|
139
139
|
}
|
|
140
|
-
function
|
|
140
|
+
function footerFeatures(meta) {
|
|
141
|
+
return {
|
|
142
|
+
footerStyles: z.object({ footerStyles: z.literal(true) }).safeParse(meta?.['native-ui/features']).success,
|
|
143
|
+
footerDoubleActions: z.object({ footerDoubleActions: z.literal(true) }).safeParse(meta?.['native-ui/features']).success,
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
function view(data, scoped = false, secondary = false, features = {}) {
|
|
141
147
|
const { tasks, task, config, revision } = data;
|
|
142
148
|
const state = Context.parse({ conversationId: 'outgoing-state', state: { ...data.state, selectedId: task?.id ?? null } }).state;
|
|
143
149
|
const controls = [];
|
|
@@ -196,11 +202,11 @@ function view(data, scoped = false, secondary = false) {
|
|
|
196
202
|
const footer = [{ text: `${icons.note} `, width: 3 }];
|
|
197
203
|
if (task) {
|
|
198
204
|
footer.push({ text: `${icons.check} `, action: 'done', width: 3 });
|
|
199
|
-
footer.push({ text: `${task.tag ? `[${task.tag}] ` : ''}${task.text.split('\n')[0].slice(0, 2000)}`, action: 'preview', width: config.config.titleWidth });
|
|
205
|
+
footer.push({ text: `${task.tag ? `[${task.tag}] ` : ''}${task.text.split('\n')[0].slice(0, 2000)}`, action: 'preview', ...(features.footerDoubleActions ? { doubleAction: 'done' } : {}), width: config.config.titleWidth });
|
|
200
206
|
}
|
|
201
207
|
else if (state.proposal) {
|
|
202
208
|
footer.push({ text: `${icons.add} `, action: 'proposal:accept', ...(secondary ? { secondaryAction: 'proposal:dismiss' } : {}), width: 3 });
|
|
203
|
-
footer.push({ text: `${state.proposal.tag ? `[${state.proposal.tag}] ` : ''}${state.proposal.text.split('\n')[0].slice(0, 2000)}`, width: config.config.titleWidth });
|
|
209
|
+
footer.push({ text: `${state.proposal.tag ? `[${state.proposal.tag}] ` : ''}${state.proposal.text.split('\n')[0].slice(0, 2000)}`, ...(features.footerDoubleActions ? { doubleAction: 'proposal:accept' } : {}), ...(features.footerStyles ? { style: { italic: true, color: 'accent' } } : {}), width: config.config.titleWidth });
|
|
204
210
|
}
|
|
205
211
|
else
|
|
206
212
|
footer.push({ text: 'Pinote · no task', width: config.config.titleWidth });
|
|
@@ -256,7 +262,7 @@ server.setRequestHandler(ReadResourceRequestSchema, (request, extra) => serial(a
|
|
|
256
262
|
if (request.params.uri !== URI)
|
|
257
263
|
throw new Error('Unknown resource');
|
|
258
264
|
const data = await snapshot(context(request.params._meta).state, extra.signal);
|
|
259
|
-
return { contents: [{ uri: URI, mimeType: MIME, text: JSON.stringify(view(data, scopedActions(request.params._meta), secondaryActions(request.params._meta))) }] };
|
|
265
|
+
return { contents: [{ uri: URI, mimeType: MIME, text: JSON.stringify(view(data, scopedActions(request.params._meta), secondaryActions(request.params._meta), footerFeatures(request.params._meta))) }] };
|
|
260
266
|
}));
|
|
261
267
|
server.setRequestHandler(Action, (request, extra) => serial(async () => {
|
|
262
268
|
const state = context(request.params._meta).state;
|
|
@@ -354,7 +360,7 @@ server.setRequestHandler(Action, (request, extra) => serial(async () => {
|
|
|
354
360
|
extra.signal.throwIfAborted();
|
|
355
361
|
saveFooterConfig(cfg, before.config.raw, handoff, newSession, policy);
|
|
356
362
|
}
|
|
357
|
-
const result = { view: view(await snapshot(next, extra.signal), scoped, secondaryActions(request.params._meta)), ...(draft ? { draft } : {}), ...(preview ? { preview } : {}) };
|
|
363
|
+
const result = { view: view(await snapshot(next, extra.signal), scoped, secondaryActions(request.params._meta), footerFeatures(request.params._meta)), ...(draft ? { draft } : {}), ...(preview ? { preview } : {}) };
|
|
358
364
|
await server.notification({ method: 'notifications/native-ui/changed', params: { uri: URI } });
|
|
359
365
|
return result;
|
|
360
366
|
}));
|
package/mcp/README.md
CHANGED
|
@@ -18,11 +18,12 @@ npm --prefix pi ci --ignore-scripts
|
|
|
18
18
|
npm --prefix pi run build:mcp
|
|
19
19
|
```
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
Merge the service into your gateway's existing `mcpServers` map, preserving other
|
|
22
|
+
services (use absolute paths). Do not mix `mcpServers` with the legacy `servers` key:
|
|
22
23
|
|
|
23
24
|
```json
|
|
24
25
|
{
|
|
25
|
-
"
|
|
26
|
+
"mcpServers": {
|
|
26
27
|
"pinote": {
|
|
27
28
|
"command": "node",
|
|
28
29
|
"args": ["/absolute/path/to/pinote/pi/dist-mcp/mcp/server.js"],
|
|
@@ -54,13 +55,18 @@ normal personal data. No desktop notifications are sent.
|
|
|
54
55
|
edits are rejected and unrelated settings are preserved. Invalid configuration
|
|
55
56
|
fails closed; repair it manually rather than replacing it with defaults.
|
|
56
57
|
- **Footer:** selected task plus configured fields; safe HTTP(S) links are clickable.
|
|
57
|
-
|
|
58
|
-
resetting the conversation or editor.
|
|
58
|
+
Single-click the active title to preview; double-click it to complete. **✔** also
|
|
59
|
+
completes and clears the selection without resetting the conversation or editor.
|
|
60
|
+
Suggested titles use italic accent text; double-click to accept/start, then the
|
|
61
|
+
title returns to normal text. Acceptance cannot reuse the same click capability
|
|
62
|
+
to complete. Styling/double-click require gateway 0.28.0+ and double-click needs
|
|
63
|
+
its updated Kitty mouse mapping (Ctrl+Shift+double-click in fullscreen).
|
|
64
|
+
Older hosts retain plain titles and icon actions. Suggestions show **✚**: left-click to
|
|
59
65
|
create/start/select, right-click to dismiss. Right-click requires gateway 0.22.0+
|
|
60
66
|
and its Kitty mouse mapping; **Dismiss suggestion** in the app menu remains the
|
|
61
67
|
keyboard/older-host fallback. These use the gateway's generic footer actions, not a Pinote
|
|
62
|
-
extension or private handler. Install the gateway's Kitty protocol handler
|
|
63
|
-
and right-click mouse mapping described in its native UI documentation. Existing
|
|
68
|
+
extension or private handler. Install the gateway's Kitty protocol handler
|
|
69
|
+
and right-click mouse mapping as described in its native UI documentation. Existing
|
|
64
70
|
installations need to add the mapping and reload Kitty; package updates do not
|
|
65
71
|
edit terminal configuration. App menus remain the keyboard fallback.
|
|
66
72
|
- **Agent:** `propose`, `get_current`, `update_current`, `fields`, and `tags` are
|
package/mcp/server.ts
CHANGED
|
@@ -32,7 +32,7 @@ const Action = z.object({ method: z.literal('native-ui/action'), params: z.objec
|
|
|
32
32
|
uri: z.literal(URI), revision: z.string().min(1).max(256), action: z.string().min(1).max(256),
|
|
33
33
|
value: z.union([z.string().max(20_000), z.number().finite(), z.boolean(), z.null()]).optional(), _meta: z.record(z.unknown()).optional(),
|
|
34
34
|
}) });
|
|
35
|
-
const server = new Server({ name: 'pinote', version: '0.
|
|
35
|
+
const server = new Server({ name: 'pinote', version: '0.23.0' }, { capabilities: { tools: {}, resources: {}, experimental: { 'native-ui-v1': {} } } });
|
|
36
36
|
// Serialize complete read/check/write cycles even when a host sends parallel calls.
|
|
37
37
|
let queue = Promise.resolve();
|
|
38
38
|
function serial<T>(run: () => Promise<T>): Promise<T> {
|
|
@@ -118,7 +118,13 @@ function staleAction(): never {
|
|
|
118
118
|
function secondaryActions(meta: Record<string, unknown> | undefined): boolean {
|
|
119
119
|
return z.object({ footerSecondaryActions: z.literal(true) }).safeParse(meta?.['native-ui/features']).success;
|
|
120
120
|
}
|
|
121
|
-
function
|
|
121
|
+
function footerFeatures(meta: Record<string, unknown> | undefined) {
|
|
122
|
+
return {
|
|
123
|
+
footerStyles: z.object({ footerStyles: z.literal(true) }).safeParse(meta?.['native-ui/features']).success,
|
|
124
|
+
footerDoubleActions: z.object({ footerDoubleActions: z.literal(true) }).safeParse(meta?.['native-ui/features']).success,
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
function view(data: Snapshot, scoped = false, secondary = false, features: { footerStyles?: boolean; footerDoubleActions?: boolean } = {}) {
|
|
122
128
|
const { tasks, task, config, revision } = data;
|
|
123
129
|
const state = Context.parse({ conversationId: 'outgoing-state', state: { ...data.state, selectedId: task?.id ?? null } }).state;
|
|
124
130
|
const controls: Control[] = [];
|
|
@@ -164,13 +170,13 @@ function view(data: Snapshot, scoped = false, secondary = false) {
|
|
|
164
170
|
for (const candidate of matches.slice(0, 200)) action(`select:${candidate.id}`, `${candidate.tag ? `[${candidate.tag}] ` : ''}${candidate.text.split('\n')[0]}`, { description: `#${candidate.id} · ${candidate.state}`, confirm: 'Select and start this task for this conversation?' });
|
|
165
171
|
if (matches.length > 200) body += '\nShowing the first 200 matches; use Filter tasks to narrow the list.';
|
|
166
172
|
}
|
|
167
|
-
const footer: { text: string; url?: string; action?: string; secondaryAction?: string; width?: number }[] = [{ text: `${icons.note} `, width: 3 }];
|
|
173
|
+
const footer: { text: string; url?: string; action?: string; secondaryAction?: string; doubleAction?: string; style?: { italic: boolean; color: 'accent' }; width?: number }[] = [{ text: `${icons.note} `, width: 3 }];
|
|
168
174
|
if (task) {
|
|
169
175
|
footer.push({ text: `${icons.check} `, action: 'done', width: 3 });
|
|
170
|
-
footer.push({ text: `${task.tag ? `[${task.tag}] ` : ''}${task.text.split('\n')[0].slice(0, 2000)}`, action: 'preview', width: config.config.titleWidth });
|
|
176
|
+
footer.push({ text: `${task.tag ? `[${task.tag}] ` : ''}${task.text.split('\n')[0].slice(0, 2000)}`, action: 'preview', ...(features.footerDoubleActions ? { doubleAction: 'done' } : {}), width: config.config.titleWidth });
|
|
171
177
|
} else if (state.proposal) {
|
|
172
178
|
footer.push({ text: `${icons.add} `, action: 'proposal:accept', ...(secondary ? { secondaryAction: 'proposal:dismiss' } : {}), width: 3 });
|
|
173
|
-
footer.push({ text: `${state.proposal.tag ? `[${state.proposal.tag}] ` : ''}${state.proposal.text.split('\n')[0].slice(0, 2000)}`, width: config.config.titleWidth });
|
|
179
|
+
footer.push({ text: `${state.proposal.tag ? `[${state.proposal.tag}] ` : ''}${state.proposal.text.split('\n')[0].slice(0, 2000)}`, ...(features.footerDoubleActions ? { doubleAction: 'proposal:accept' } : {}), ...(features.footerStyles ? { style: { italic: true, color: 'accent' } as const } : {}), width: config.config.titleWidth });
|
|
174
180
|
} else footer.push({ text: 'Pinote · no task', width: config.config.titleWidth });
|
|
175
181
|
const titleParts = footer.length;
|
|
176
182
|
if (task) {
|
|
@@ -211,7 +217,7 @@ server.setRequestHandler(ListResourcesRequestSchema, async () => ({ resources: [
|
|
|
211
217
|
server.setRequestHandler(ReadResourceRequestSchema, (request, extra) => serial(async () => {
|
|
212
218
|
if (request.params.uri !== URI) throw new Error('Unknown resource');
|
|
213
219
|
const data = await snapshot(context(request.params._meta).state, extra.signal);
|
|
214
|
-
return { contents: [{ uri: URI, mimeType: MIME, text: JSON.stringify(view(data, scopedActions(request.params._meta), secondaryActions(request.params._meta))) }] };
|
|
220
|
+
return { contents: [{ uri: URI, mimeType: MIME, text: JSON.stringify(view(data, scopedActions(request.params._meta), secondaryActions(request.params._meta), footerFeatures(request.params._meta))) }] };
|
|
215
221
|
}));
|
|
216
222
|
server.setRequestHandler(Action, (request, extra) => serial(async () => {
|
|
217
223
|
const state = context(request.params._meta).state;
|
|
@@ -275,7 +281,7 @@ server.setRequestHandler(Action, (request, extra) => serial(async () => {
|
|
|
275
281
|
extra.signal.throwIfAborted();
|
|
276
282
|
saveFooterConfig(cfg, before.config.raw, handoff, newSession, policy);
|
|
277
283
|
}
|
|
278
|
-
const result = { view: view(await snapshot(next, extra.signal), scoped, secondaryActions(request.params._meta)), ...(draft ? { draft } : {}), ...(preview ? { preview } : {}) };
|
|
284
|
+
const result = { view: view(await snapshot(next, extra.signal), scoped, secondaryActions(request.params._meta), footerFeatures(request.params._meta)), ...(draft ? { draft } : {}), ...(preview ? { preview } : {}) };
|
|
279
285
|
await server.notification({ method: 'notifications/native-ui/changed', params: { uri: URI } });
|
|
280
286
|
return result;
|
|
281
287
|
}));
|