pi-note 0.18.0 → 0.18.1

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.
Files changed (3) hide show
  1. package/README.md +6 -2
  2. package/index.ts +23 -13
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -84,7 +84,7 @@ The agent can still read the task separately with `pinote_get_current`.
84
84
  Click previews work while the agent is running, provided no other Pinote operation
85
85
  is open. Each click saves a fresh snapshot in the transcript, above any currently
86
86
  streaming response. Later output scrolls it upward; it does not expire. Preview
87
- reads never lock out the agent's task updates. Settings still requires Pi to be idle.
87
+ reads never lock out the agent's task updates. Settings also works during a running turn.
88
88
 
89
89
  ![Preview above a running response](../docs/images/pi-note-active-preview.png)
90
90
 
@@ -358,7 +358,11 @@ Pinote no longer polls GitHub, runs `gh`, watches completed tasks, or prompts on
358
358
  PR merges. No GitHub authentication is required. `PINOTE_PR_POLL_SECONDS` is no
359
359
  longer used. Other separately installed Pi PR/Git extensions are unaffected.
360
360
 
361
- Tools work without a TUI, but `/pi-note` needs an idle TUI.
361
+ Tools work without a TUI. `/pi-note` works in a TUI while the agent is running:
362
+ you can select/switch tasks, use Continue, preview tasks, respond to suggestions,
363
+ and edit settings without interrupting the turn or submitting input. Completion
364
+ and CLI installation/upgrades still require an idle session. Another pending
365
+ Pinote operation blocks the menu. Open menus do not block agent handoff updates.
362
366
  This package does not synchronize databases or paths between machines. Note text
363
367
  and fields loaded into Pi are sent to the configured model when used as context;
364
368
  avoid secrets. Agent commands do not send desktop notifications.
package/index.ts CHANGED
@@ -169,6 +169,7 @@ export default function (pi: ExtensionAPI) {
169
169
  let epoch = 0;
170
170
  let refreshSerial = 0;
171
171
  let pending: symbol | undefined;
172
+ let menuPending: symbol | undefined;
172
173
  let previewPending = false;
173
174
  let setupAbort: AbortController | undefined;
174
175
  let activeContext: ExtensionContext | undefined;
@@ -318,7 +319,7 @@ export default function (pi: ExtensionAPI) {
318
319
  }
319
320
  };
320
321
  const preview = async (ctx: ExtensionContext, requestSignal?: AbortSignal): Promise<boolean> => {
321
- if (!ctx.hasUI || ctx.mode !== "tui" || pending || previewPending || !validId(selectedId)) return false;
322
+ if (!ctx.hasUI || ctx.mode !== "tui" || pending || menuPending || previewPending || !validId(selectedId)) return false;
322
323
  const generation = epoch;
323
324
  const branch = branchEpoch;
324
325
  const id = selectedId;
@@ -346,7 +347,7 @@ export default function (pi: ExtensionAPI) {
346
347
  }
347
348
  };
348
349
  const dispatchCompletion = (ctx: ExtensionContext, displayed: Task, selection: string): Promise<boolean> => {
349
- if (!ctx.isIdle() || pending || completionDispatch) return Promise.resolve(false);
350
+ if (!ctx.isIdle() || pending || menuPending || completionDispatch) return Promise.resolve(false);
350
351
  const token = `${selection}:${randomBytes(8).toString("hex")}`;
351
352
  return new Promise((resolve) => {
352
353
  const finish = (accepted: boolean) => {
@@ -367,7 +368,7 @@ export default function (pi: ExtensionAPI) {
367
368
  const token = args.trim();
368
369
  const request = token ? completionDispatch : undefined;
369
370
  if (token && (request?.token !== token || request.selection !== `${runtimeToken}:${epoch}:${branchEpoch}:${selectedId}`)) return;
370
- if (!alive || !ctx.hasUI || ctx.mode !== "tui" || !ctx.isIdle() || pending || setupAbort || !validId(selectedId)) {
371
+ if (!alive || !ctx.hasUI || ctx.mode !== "tui" || !ctx.isIdle() || pending || menuPending || setupAbort || !validId(selectedId)) {
371
372
  request?.finish(false);
372
373
  if (ctx.hasUI) ctx.ui.notify("Select a task and wait until Pi is idle and the Pinote operation has finished.", "warning");
373
374
  return;
@@ -459,6 +460,7 @@ export default function (pi: ExtensionAPI) {
459
460
  if (!alive || generation !== epoch) return;
460
461
  // Keep the setup lock until its aborted subprocess has actually settled.
461
462
  if (!setupAbort) pending = undefined;
463
+ menuPending = undefined;
462
464
  await checkCLI(ctx, true);
463
465
  if (!alive || generation !== epoch) return;
464
466
  if (suggestion) previewBridge?.setSuggestion((choice) => respondToSuggestion(choice, ctx));
@@ -522,7 +524,7 @@ export default function (pi: ExtensionAPI) {
522
524
 
523
525
  const installCLI = async (upgrade: boolean, ctx: ExtensionCommandContext) => {
524
526
  if (!ctx.hasUI || ctx.mode !== "tui") return;
525
- if (pending || !ctx.isIdle()) {
527
+ if (pending || menuPending || !ctx.isIdle()) {
526
528
  ctx.ui.notify("Wait until Pi is idle and the pinote operation has finished.", "warning");
527
529
  return;
528
530
  }
@@ -563,16 +565,18 @@ export default function (pi: ExtensionAPI) {
563
565
  return;
564
566
  }
565
567
  if (token) { ctx.ui.notify("Usage: /pi-note", "warning"); return; }
566
- if (pending || !ctx.isIdle()) {
567
- ctx.ui.notify("Wait until Pi is idle and the pinote operation has finished.", "warning");
568
+ if (pending || menuPending) {
569
+ ctx.ui.notify("Wait until the pinote operation has finished.", "warning");
568
570
  return;
569
571
  }
570
572
  const operation = Symbol();
571
- pending = operation;
573
+ menuPending = operation;
572
574
  const generation = epoch;
573
575
  const branch = branchEpoch;
574
576
  const currentSession = () => alive && generation === epoch;
575
- const canAct = () => currentSession() && ctx.isIdle() && branch === branchEpoch;
577
+ // Menus, task selection and preference edits do not interrupt the agent.
578
+ // Completion and CLI setup retain their own idle-only guards.
579
+ const canAct = () => currentSession() && branch === branchEpoch && menuPending === operation;
576
580
  try {
577
581
  await checkCLI(ctx);
578
582
  if (!canAct()) return;
@@ -633,7 +637,7 @@ export default function (pi: ExtensionAPI) {
633
637
  if (!canAct() || edited === undefined) return;
634
638
  if (edited === "tasks") { settings = false; continue; }
635
639
  if (!options.some((option) => option.result === edited)) return;
636
- if (pending === operation) pending = undefined;
640
+ if (menuPending === operation) menuPending = undefined;
637
641
  if (edited === "preview") {
638
642
  if (!await preview(ctx)) ctx.ui.notify("The task is no longer available for preview.", "warning");
639
643
  } else if (edited === "done") await completeSelected("", ctx, current ?? undefined);
@@ -649,7 +653,7 @@ export default function (pi: ExtensionAPI) {
649
653
  if (!action || !canAct()) return;
650
654
  if (action === "Settings") { settings = true; continue; }
651
655
  if (action === "Done" && current) {
652
- if (pending === operation) pending = undefined;
656
+ if (menuPending === operation) menuPending = undefined;
653
657
  await completeSelected("", ctx, current);
654
658
  return;
655
659
  }
@@ -667,6 +671,11 @@ export default function (pi: ExtensionAPI) {
667
671
  id = choice;
668
672
  } else return;
669
673
  const handoff = handoffPrompt();
674
+ if (pending) {
675
+ ctx.ui.notify("Wait until the pinote operation has finished.", "warning");
676
+ return;
677
+ }
678
+ pending = operation;
670
679
  refreshSerial++;
671
680
  const chosen = await startTask(id, canAct);
672
681
  if (!chosen || !canAct()) return;
@@ -679,6 +688,7 @@ export default function (pi: ExtensionAPI) {
679
688
  if (currentSession()) ctx.ui.notify(`Pinote: ${clean(error instanceof Error ? error.message : String(error))}`, "error");
680
689
  } finally {
681
690
  if (pending === operation) pending = undefined;
691
+ if (menuPending === operation) menuPending = undefined;
682
692
  if (currentSession()) await refresh(ctx);
683
693
  }
684
694
  },
@@ -755,7 +765,7 @@ export default function (pi: ExtensionAPI) {
755
765
  clearSuggestion(ctx);
756
766
  return true;
757
767
  }
758
- if (pending || setupAbort) return false;
768
+ if (pending || menuPending || setupAbort) return false;
759
769
  const generation = epoch;
760
770
  const branch = branchEpoch;
761
771
  const operation = Symbol();
@@ -804,7 +814,7 @@ export default function (pi: ExtensionAPI) {
804
814
  const current = await selected(ctx, signal);
805
815
  if (!alive || generation !== epoch || branch !== branchEpoch) throw new Error("Pinote operation cancelled: the session changed.");
806
816
  if (current || selectedId !== null) throw new Error("A task is already selected; keep it unless the user asks to switch.");
807
- if (pending || setupAbort) throw new Error("A pinote operation is already open. Retry after it finishes.");
817
+ if (pending || menuPending || setupAbort) throw new Error("A pinote operation is already open. Retry after it finishes.");
808
818
  const result = (status: string) => ({ content: [{ type: "text" as const, text: JSON.stringify({ status }) }], details: { status } });
809
819
  suggestion = { text: params.text, ...(params.tag === undefined ? {} : { tag: params.tag.trim() }) };
810
820
  saveSuggestion();
@@ -834,7 +844,7 @@ export default function (pi: ExtensionAPI) {
834
844
  const generation = epoch;
835
845
  const branch = branchEpoch;
836
846
  if (params.select !== true) return toolResult(await writeTask(ctx, signal, argv, undefined, "0.4.0"));
837
- if (pending) throw new Error("A pinote operation is already open. Retry after it finishes.");
847
+ if (pending || menuPending) throw new Error("A pinote operation is already open. Retry after it finishes.");
838
848
  const operation = Symbol();
839
849
  const previousSelection = selectedId;
840
850
  pending = operation;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-note",
3
- "version": "0.18.0",
3
+ "version": "0.18.1",
4
4
  "description": "Persistent pinote tasks, Markdown handoffs, and task status for Pi",
5
5
  "license": "MIT",
6
6
  "type": "module",