pi-note 0.31.1 → 0.33.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/LOCAL.md CHANGED
@@ -3,7 +3,7 @@
3
3
  [npm](https://www.npmjs.com/package/pi-note) · [Pi package directory](https://pi.dev/packages/pi-note)
4
4
 
5
5
  Persistent [pinote](https://github.com/kvidzibo/pinote) tasks and Markdown handoffs
6
- in Pi: selected-task footer status, `/pi-note`, and agent current-task read/update,
6
+ in Pi: selected-task footer status, `/pinote`, and agent current-task read/update,
7
7
  add, and tag-listing tools.
8
8
  The extension and Python app share a repository but install separately.
9
9
 
@@ -30,8 +30,8 @@ Install [uv](https://docs.astral.sh/uv/getting-started/installation/) first and
30
30
  restart Pi with `uv` on PATH. At startup the extension checks `note --version`
31
31
  against its bundled CLI version (currently 0.4.0), without network requests:
32
32
 
33
- - Missing/unrecognized CLI: `/pi-note` opens Settings directly, where **Install CLI** is available.
34
- - Older CLI: `/pi-note` opens Settings directly, where **Upgrade CLI** is available.
33
+ - Missing/unrecognized CLI: `/pinote` opens Settings directly, where **Install CLI** is available.
34
+ - Older CLI: `/pinote` opens Settings directly, where **Upgrade CLI** is available.
35
35
  - Equal/newer CLI: neither installation action is shown; no upgrade notification.
36
36
 
37
37
  Install and upgrade recheck the CLI version and ask before installing into uv's
@@ -54,7 +54,7 @@ Check `note --version`, then restart Pi or `/reload`. Network/build failures and
54
54
  conflicting executables are reported without forcibly overwriting another
55
55
  installer's commands. Fix the reported cause and retry; installs time out after
56
56
  three minutes. Installation actions are unavailable while installation runs.
57
- Setup hints direct users to `/pi-note` → Settings.
57
+ Setup hints direct users to `/pinote` → Settings.
58
58
  Missing uv is reported with its installation link, not installed
59
59
  automatically. The [GTK desktop app](../README.md#optional-gtk-checklist) is optional
60
60
  and still requires separate system dependencies.
@@ -65,16 +65,16 @@ standalone `settings/pi/extensions/pinote.ts` entry if installed; load only one
65
65
 
66
66
  ## Use
67
67
 
68
- When the CLI is ready and no task is selected, `/pi-note` opens the task picker.
68
+ When the CLI is ready and no task is selected, `/pinote` opens the task picker.
69
69
  It marks the chosen task in progress and adds this default prompt to the editor:
70
70
  “Read the current Pinote task. Summarize your understanding, but don’t start work yet.”
71
- If the CLI is missing or older than the bundled version, `/pi-note` opens Settings
71
+ If the CLI is missing or older than the bundled version, `/pinote` opens Settings
72
72
  directly so the relevant CLI installation action remains accessible. **Tab** opens
73
73
  Settings from the picker or task view; **Tab** from Settings returns to tasks.
74
74
  The agent fetches current task text and saved handoff fields when you submit;
75
75
  they are not copied into the draft. The default prompt asks for a summary, not implementation.
76
76
  Existing drafts are preserved; nothing is submitted automatically.
77
- With a selected task, `/pi-note` offers **Continue**, **Done**, **Switch task**, and
77
+ With a selected task, `/pinote` offers **Continue**, **Done**, **Switch task**, and
78
78
  **Settings**. Settings provides **Preview task**, **Complete task (new session)**,
79
79
  **Accept suggestion**, and **Dismiss suggestion** when applicable, plus
80
80
  **Install CLI** or **Upgrade CLI** only when needed. Settings also lists fields found
@@ -170,7 +170,7 @@ and selected tasks. Run `/reload` after changing this setting. Invalid policy
170
170
  values or unreadable/malformed configuration suppress offers; interactive startup
171
171
  warns until you fix the file and reload. No project-local configuration is read.
172
172
 
173
- In `/pi-note → Settings`, select **Task prompt** to edit the text inserted after
173
+ In `/pinote → Settings`, select **Task prompt** to edit the text inserted after
174
174
  selection or **Continue**. **Shift+Enter** or **Ctrl+J** adds a newline; **Enter** saves it
175
175
  globally, **Ctrl+C** clears the edit, and **Esc** cancels that unfinished edit.
176
176
 
@@ -219,7 +219,7 @@ configuration only; project-local files are not read.
219
219
 
220
220
  Version 0.7.0 replaces `pinote_get`/`pinote_update` with these current-task tools
221
221
  and removes `pinote_tag`, without aliases. Agents cannot read/update tasks by ID
222
- or retag existing tasks; use `/pi-note` to select an existing task.
222
+ or retag existing tasks; use `/pinote` to select an existing task.
223
223
 
224
224
  When allowed by `taskOfferPolicy`, the agent uses `pinote_propose` instead of
225
225
  asking in chat. The pin footer shows a pending suggestion, distinct from a selected task:
@@ -242,7 +242,7 @@ The configured `footer.titleWidth` bounds the entire row (default 60 columns). C
242
242
 
243
243
  Controls use the [Kitty preview handler](#display-only-preview), with no additional configuration. Fullscreen Kitty uses **Ctrl+Shift+click**. The Settings actions **Accept suggestion** and **Dismiss suggestion** perform the corresponding footer actions. No action submits input or starts an agent turn. Add and Dismiss preserve editor drafts; **Complete task** starts a clean session and reloads Pi, discarding the editor draft, so save unfinished input before completing a task.
244
244
 
245
- Completion requires an idle Pi session and no pending Pinote operation. It rejects stale selected-task links and externally changed revisions. Accepted mutations drain even if the helper disconnects. On an error, check `/pi-note` before retrying, because a write may already have committed. Failed or stale writes never restart Pi. If another extension cancels the new session, the task remains completed and Pi reports the cancellation. The reload runs through the new session's fresh context; retired contexts are not reused.
245
+ Completion requires an idle Pi session and no pending Pinote operation. It rejects stale selected-task links and externally changed revisions. Accepted mutations drain even if the helper disconnects. On an error, check `/pinote` before retrying, because a write may already have committed. Failed or stale writes never restart Pi. If another extension cancels the new session, the task remains completed and Pi reports the cancellation. The reload runs through the new session's fresh context; retired contexts are not reused.
246
246
 
247
247
  If another task fits better, the agent can replace an unaccepted suggestion by calling `pinote_propose` again, without waiting for dismissal. Replacement invalidates the old ✚/✖ links; only the latest suggestion can be accepted or dismissed. Pending suggestions survive `/reload` and session resume, including their full text and tag; new links replace stale capabilities. Accepted or dismissed suggestions never reappear on reload, but dismissal does not block new proposals—even in sessions saved by older versions. New sessions and forks start without a suggestion; selecting a task or navigating the tree clears it. Suggestion state is local, display-only session metadata, not model context. Pi saves the session after the first submitted message; quitting before that can still lose unsaved session state. Repeated acceptance cannot duplicate a note. A busy Pinote operation blocks acceptance. Noninteractive agents still ask in chat before using `pinote_add` with `select: true`. An existing selection is not replaced unless the user asks to switch. Reuse a saved
248
248
  tag name when it fits.
@@ -310,7 +310,7 @@ Names are case-sensitive, trimmed and NFC-normalized, at most 64 characters;
310
310
  accepted. Missing/blank values do not display and do not consume the field limit.
311
311
  `[]` or `maxFields: 0` hides configured fields, including PR.
312
312
 
313
- In `/pi-note → Settings`, **Add field** adds a global display definition, not a
313
+ In `/pinote → Settings`, **Add field** adds a global display definition, not a
314
314
  task value. Select any field to edit **Link**, **Label**, **Format**, and **Width**.
315
315
  Label starts as the field name; clear it for no prefix. Link off displays plain
316
316
  text without a clickable link. Toggles, field additions/removals, and edits confirmed
@@ -369,7 +369,7 @@ Pinote no longer polls GitHub, runs `gh`, watches completed tasks, or prompts on
369
369
  PR merges. No GitHub authentication is required. `PINOTE_PR_POLL_SECONDS` is no
370
370
  longer used. Other separately installed Pi PR/Git extensions are unaffected.
371
371
 
372
- Tools work without a TUI. `/pi-note` works in a TUI while the agent is running:
372
+ Tools work without a TUI. `/pinote` works in a TUI while the agent is running:
373
373
  you can select/switch tasks, use Continue, preview tasks, respond to suggestions,
374
374
  and edit settings without interrupting the turn or submitting input. Completion
375
375
  and CLI installation/upgrades still require an idle session. Another pending
package/README.md CHANGED
@@ -48,7 +48,7 @@ state routing, tools and UI.
48
48
 
49
49
  ## Use
50
50
 
51
- Open **`/pinote`** (`/pi-note` is an alias). Select or create a task, preview it,
51
+ Open **`/pinote`**. Select or create a task, preview it,
52
52
  clear the selection, complete it, respond to suggestions, or edit shared settings.
53
53
  Selecting an existing task starts it immediately and returns focus to the prompt,
54
54
  with no extra confirmation. Creation and settings changes still require confirmation.
@@ -76,9 +76,16 @@ Changing endpoints starts a separate selection namespace. Unsaved Pi sessions
76
76
  cannot guarantee selection persistence.
77
77
 
78
78
  The GTK app's **Start in Kitty…** action (note 0.13.0+) chooses a working directory
79
- and opens a named tab with this task selected, without submitting a prompt. Client
80
- and service both need pi-note 0.31.0+. The one-shot `PINOTE_LAUNCH_TASK` environment
81
- handoff carries only the task ID and displayed revision, never task text or tokens.
79
+ and opens a named tab with this task selected, without submitting a prompt.
80
+ In note 0.16.0+, it uses the tag's default folder when available, otherwise keeps
81
+ the directory chooser. Set or clear defaults in **Manage tags…**.
82
+ With note 0.15.0+ and pi-note 0.32.0+, successful selection prefills an empty Pi editor
83
+ with `is task clear?`. Configure `gui.kitty_start_prompt` in the
84
+ [GUI config](../docs/gui.md#gui-configuration), or set it to `""` to disable; existing
85
+ drafts are never replaced. The service needs pi-note 0.31.0+.
86
+ The one-shot `PINOTE_LAUNCH_TASK` environment handoff carries the task ID, displayed
87
+ revision and optional editor draft, never task text or tokens. The draft stays
88
+ client-local and is never sent to the service or persisted as launch state.
82
89
  Only a fresh session can consume it. The service must share the GUI's database;
83
90
  missing/stale revisions fail closed. Resume, reload and `/new` never replay the
84
91
  handoff or replace another selection. Pi keeps its normal endpoint/token settings.
@@ -116,6 +123,10 @@ separate from global display settings.
116
123
 
117
124
  ## Migration
118
125
 
126
+ Since pi-note 0.33.0, `/pinote` is the only slash command in both modes.
127
+ The `/pi-note` alias is removed; update saved shortcuts to `/pinote`.
128
+ The npm package name and `pi-note.json` configuration filename are unchanged.
129
+
119
130
  1. Back up the service machine's Pinote data; keep `notes.db` local to that machine.
120
131
  2. Start the standalone service against that same database and settings directory.
121
132
  3. Remove Pinote's service entry and its unused package entry from the gateway
package/index.ts CHANGED
@@ -107,7 +107,7 @@ function task(raw: string): Task | null {
107
107
  }
108
108
  function requiredTask(raw: string, id?: number): Task {
109
109
  const value = task(raw);
110
- if (!value) throw new Error("No selected pinote task. Select one with /pi-note first.");
110
+ if (!value) throw new Error("No selected pinote task. Select one with /pinote first.");
111
111
  if (id !== undefined && value.id !== id) throw new Error(compatible);
112
112
  return value;
113
113
  }
@@ -187,7 +187,7 @@ export default function (pi: ExtensionAPI) {
187
187
  if (!alive || generation !== epoch || setupAbort) return;
188
188
  cliState = action;
189
189
  if (suggest && action === "upgrade") {
190
- ctx.ui.notify(`Pinote CLI ${bundledCLIVersion} is bundled with this extension. Open /pi-note → Settings → Upgrade CLI to update your older CLI.`, "info");
190
+ ctx.ui.notify(`Pinote CLI ${bundledCLIVersion} is bundled with this extension. Open /pinote → Settings → Upgrade CLI to update your older CLI.`, "info");
191
191
  }
192
192
  };
193
193
 
@@ -314,7 +314,7 @@ export default function (pi: ExtensionAPI) {
314
314
  if (alive && generation === epoch && serial === refreshSerial) {
315
315
  previewBridge?.invalidate();
316
316
  clearTaskFooter(ctx);
317
- ctx.ui.setStatus("pinote", suggestionStatus(ctx) ?? `${ctx.ui.theme.bold(noteIcon)} Pinote unavailable · /pi-note → Settings`);
317
+ ctx.ui.setStatus("pinote", suggestionStatus(ctx) ?? `${ctx.ui.theme.bold(noteIcon)} Pinote unavailable · /pinote → Settings`);
318
318
  }
319
319
  }
320
320
  };
@@ -357,10 +357,10 @@ export default function (pi: ExtensionAPI) {
357
357
  };
358
358
  const timer = setTimeout(() => {
359
359
  finish(false);
360
- if (alive && activeContext === ctx) ctx.ui.notify("Pinote completion could not start. Open /pi-note → Settings → Complete task.", "warning");
360
+ if (alive && activeContext === ctx) ctx.ui.notify("Pinote completion could not start. Open /pinote → Settings → Complete task.", "warning");
361
361
  }, 4000);
362
362
  completionDispatch = { token, selection, task: displayed, started: () => clearTimeout(timer), finish };
363
- try { pi.sendUserMessage(`/pi-note ${token}`, { expandPromptTemplates: true }); }
363
+ try { pi.sendUserMessage(`/pinote ${token}`, { expandPromptTemplates: true }); }
364
364
  catch { finish(false); }
365
365
  });
366
366
  };
@@ -454,7 +454,7 @@ export default function (pi: ExtensionAPI) {
454
454
  } catch {
455
455
  await bridge.stop();
456
456
  if (previewBridge === bridge) previewBridge = undefined;
457
- ctx.ui.notify("Pinote task link unavailable; open /pi-note → Settings → Preview task.", "warning");
457
+ ctx.ui.notify("Pinote task link unavailable; open /pinote → Settings → Preview task.", "warning");
458
458
  }
459
459
  }
460
460
  if (!alive || generation !== epoch) return;
@@ -553,7 +553,7 @@ export default function (pi: ExtensionAPI) {
553
553
  }
554
554
  };
555
555
 
556
- pi.registerCommand("pi-note", {
556
+ pi.registerCommand("pinote", {
557
557
  description: "Continue, complete, switch tasks, or edit global settings (Tab)",
558
558
  handler: async (args, ctx) => {
559
559
  if (!ctx.hasUI || ctx.mode !== "tui") return;
@@ -564,7 +564,7 @@ export default function (pi: ExtensionAPI) {
564
564
  await completeSelected(token, ctx);
565
565
  return;
566
566
  }
567
- if (token) { ctx.ui.notify("Usage: /pi-note", "warning"); return; }
567
+ if (token) { ctx.ui.notify("Usage: /pinote", "warning"); return; }
568
568
  if (pending || menuPending) {
569
569
  ctx.ui.notify("Wait until the pinote operation has finished.", "warning");
570
570
  return;
@@ -724,7 +724,7 @@ export default function (pi: ExtensionAPI) {
724
724
  const canRun = () => alive && generation === epoch && branch === branchEpoch && selectedId === id;
725
725
  const current = await selected(ctx, signal);
726
726
  if (!canRun()) throw new Error("Pinote operation cancelled: the session or selected task changed.");
727
- if (!current) throw new Error("No selected pinote task. Select one with /pi-note first.");
727
+ if (!current) throw new Error("No selected pinote task. Select one with /pinote first.");
728
728
  const argv = ["agent", "update", String(current.id), "--expected-updated-at", params.expected_updated_at,
729
729
  "--set-json", JSON.stringify(params.set ?? {})];
730
730
  for (const label of params.remove ?? []) argv.push(`--remove=${label}`);
@@ -789,7 +789,7 @@ export default function (pi: ExtensionAPI) {
789
789
  return true;
790
790
  } catch (error) {
791
791
  if (alive && generation === epoch) ctx.ui.notify(
792
- `Pinote suggestion: ${clean(String(error))}${created ? ` Note #${created.id} was created; use /pi-note to select it. Do not add it again.` : " A write may have committed; check /pi-note before retrying."}`, "error");
792
+ `Pinote suggestion: ${clean(String(error))}${created ? ` Note #${created.id} was created; use /pinote to select it. Do not add it again.` : " A write may have committed; check /pinote before retrying."}`, "error");
793
793
  return false;
794
794
  } finally {
795
795
  if (pending === operation) pending = undefined;
@@ -854,10 +854,10 @@ export default function (pi: ExtensionAPI) {
854
854
  clearSuggestion(ctx);
855
855
  try {
856
856
  const created = requiredTask(await run(argv, signal, canAct, "0.4.0"));
857
- if (!canAct()) throw new Error("Pinote operation cancelled: the session changed. The note was created; check /pi-note before retrying.");
857
+ if (!canAct()) throw new Error("Pinote operation cancelled: the session changed. The note was created; check /pinote before retrying.");
858
858
  const chosen = await startTask(created.id, canAct, signal);
859
859
  if (!chosen || !alive || generation !== epoch || branch !== branchEpoch || selectedId !== chosen.id) {
860
- throw new Error("Pinote operation cancelled: the session changed. The note may have started; check /pi-note before retrying.");
860
+ throw new Error("Pinote operation cancelled: the session changed. The note may have started; check /pinote before retrying.");
861
861
  }
862
862
  return toolResult(chosen);
863
863
  } finally {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-note",
3
- "version": "0.31.1",
3
+ "version": "0.33.0",
4
4
  "description": "Persistent pinote tasks, Markdown handoffs, and task status for Pi",
5
5
  "license": "MIT",
6
6
  "type": "module",
package/remote.ts CHANGED
@@ -5,6 +5,7 @@ import { ENTRY_TYPE, RemoteSession, type SavedState } from './remote/session.ts'
5
5
  import { registerTools } from './remote/tools.ts';
6
6
  import { registerPreview, RemoteUI } from './remote/ui.ts';
7
7
  import { registerRemoteExit } from './remote/exit.ts';
8
+ import { editorText } from './remote/protocol.ts';
8
9
 
9
10
  // Only an accepted /new (previousSessionFile) may carry this process-local selection.
10
11
  // Reload/resume/fork restore branch entries, never this map or a folder-global store.
@@ -34,7 +35,6 @@ export default function pinoteRemote(pi: ExtensionAPI): void {
34
35
  await ui.open(ctx);
35
36
  };
36
37
  pi.registerCommand('pinote', { description: 'Open remote Pinote tasks and settings', handler: open });
37
- pi.registerCommand('pi-note', { description: 'Open remote Pinote (compatibility alias)', handler: open });
38
38
  const start = async (ctx: ExtensionContext, reason?: string, previous?: string) => {
39
39
  const request = launchRequest; launchRequest = undefined; // Never replay an uncertain start.
40
40
  ui?.detach(); await session?.stop();
@@ -48,12 +48,17 @@ export default function pinoteRemote(pi: ExtensionAPI): void {
48
48
  const branch = ctx.sessionManager.getBranch();
49
49
  let carried = reason === 'new' && previous ? transfers.get(previous) : undefined;
50
50
  let launch: { task_id: number; updated_at: string } | undefined;
51
+ let prompt: string | undefined;
51
52
  if (request) {
52
53
  try {
53
54
  if (reason !== 'startup' || branch.some((entry) => entry.type === 'message' || entry.type === 'custom' && entry.customType === ENTRY_TYPE)) throw new Error('Launch requires a fresh Pi session');
54
55
  const parsed = JSON.parse(request);
55
56
  if (!parsed || !Number.isSafeInteger(parsed.task_id) || parsed.task_id < 1 ||
56
57
  typeof parsed.updated_at !== 'string' || !parsed.updated_at.length || parsed.updated_at.length > 128) throw new Error('Invalid launch request');
58
+ if (parsed.prompt !== undefined) {
59
+ if (typeof parsed.prompt !== 'string' || [...parsed.prompt].length > 4000) throw new Error('Invalid launch prompt');
60
+ prompt = editorText(parsed.prompt);
61
+ }
57
62
  launch = { task_id: parsed.task_id, updated_at: parsed.updated_at };
58
63
  carried = { scope: config.scope, contextId: randomUUID(), state: { launch } };
59
64
  } catch {
@@ -68,6 +73,10 @@ export default function pinoteRemote(pi: ExtensionAPI): void {
68
73
  const control = view.controls.find((item) => item.id === `select:${launch.task_id}`);
69
74
  if (!control) throw new Error('Launch task changed or belongs to another service');
70
75
  await session.action(view, control); // The directory chooser's Start click granted consent.
76
+ if (prompt?.trim() && ctx.mode === 'tui' && ctx.isIdle()) {
77
+ if (ctx.ui.getEditorText().length) ctx.ui.notify('Launch draft not inserted: the editor already contains a draft.', 'warning');
78
+ else ctx.ui.setEditorText(prompt);
79
+ }
71
80
  }
72
81
  if (reason === 'new' && previous) transfers.delete(previous);
73
82
  } catch (error) {
package/setup.ts CHANGED
@@ -4,7 +4,7 @@ import { stripVTControlCharacters } from "node:util";
4
4
  // Immutable source: the Python app is not yet published to PyPI.
5
5
  export const cliSource = "https://github.com/kvidzibo/pinote/archive/1597baf60bff8bc96893be40781f152a001623d1.tar.gz";
6
6
  export const bundledCLIVersion = "0.4.0";
7
- export const setupHint = "Open /pi-note → Settings to install or upgrade the CLI; note must be on PATH.";
7
+ export const setupHint = "Open /pinote → Settings to install or upgrade the CLI; note must be on PATH.";
8
8
  export type CLIAction = "setup" | "upgrade" | "ready";
9
9
 
10
10
  export function cliAction(version: string): CLIAction {
@@ -51,10 +51,10 @@ export async function setupCLI(
51
51
  const action = await detectCLI(pi, signal);
52
52
  if (!canAct() || signal.aborted) return;
53
53
  if (action === "ready") {
54
- ctx.ui.notify(`Pinote CLI is ready (bundled version ${bundledCLIVersion} or newer). Use /pi-note.`, "info");
54
+ ctx.ui.notify(`Pinote CLI is ready (bundled version ${bundledCLIVersion} or newer). Use /pinote.`, "info");
55
55
  return;
56
56
  }
57
- const command = `/pi-note → Settings → ${action === "upgrade" ? "Upgrade CLI" : "Install CLI"}`;
57
+ const command = `/pinote → Settings → ${action === "upgrade" ? "Upgrade CLI" : "Install CLI"}`;
58
58
  if (upgrade !== (action === "upgrade")) {
59
59
  ctx.ui.notify(`Use ${command} ${action === "upgrade" ? "to upgrade the older CLI" : "to install the missing CLI"}.`, "info");
60
60
  return;
@@ -88,7 +88,7 @@ export async function setupCLI(
88
88
  try {
89
89
  const version = await exec("note", ["--version"]);
90
90
  if (version.killed || version.code !== 0 || cliAction(version.stdout) !== "ready") throw new Error("note unavailable or older than bundled CLI");
91
- if (canAct()) ctx.ui.notify(`${clean(version.stdout.trim())} is ready. Use /pi-note. GTK is optional and installed separately.`, "info");
91
+ if (canAct()) ctx.ui.notify(`${clean(version.stdout.trim())} is ready. Use /pinote. GTK is optional and installed separately.`, "info");
92
92
  } catch {
93
93
  if (canAct()) ctx.ui.notify("Pinote installed, but note on PATH is missing or older than the bundled CLI. Run uv tool dir --bin in your terminal, add that directory to PATH before older note executables, then restart Pi.", "warning");
94
94
  }