pi-note 0.16.2 β†’ 0.17.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.
package/README.md CHANGED
@@ -189,8 +189,9 @@ configuration only; project-local files are not read.
189
189
  - `pinote_tags`: list saved tag names.
190
190
  - `pinote_propose`: show a suggested task beside the pin icon in the footer without
191
191
  creating a note. Optional `tag`; full text is retained, but the footer shows only
192
- its title. Requires a TUI with no selected task. The agent continues your work
193
- while the suggestion awaits your choice.
192
+ its title. Requires a TUI with no selected task. A new proposal replaces the
193
+ unaccepted suggestion without waiting for dismissal. The agent continues your
194
+ work while the suggestion awaits your choice.
194
195
  - `pinote_add`: create an active task. Optional `tag`. `select: true` starts and
195
196
  remembers it for this session only; use that only after the user agrees.
196
197
 
@@ -202,18 +203,18 @@ When allowed by `taskOfferPolicy`, the agent uses `pinote_propose` instead of
202
203
  asking in chat. The pin footer shows a pending suggestion, distinct from a selected task:
203
204
 
204
205
  ```text
205
- πŸ“Œ βœ• + Β· [tag] Task title...
206
- πŸ“Œ βœ“ Β· [tag] Task title...
206
+ πŸ“Œ βœ– ✚ Β· [tag] Task title...
207
+ πŸ“Œ βœ” Β· [tag] Task title...
207
208
  ```
208
209
 
209
210
  ![Suggested task controls](../docs/images/pi-task-suggestion.png)
210
211
  ![Selected task with Done control](../docs/images/pi-task-selected.png)
211
212
 
212
- - Muted **βœ•** dismisses only the current suggestion without creating a note; the agent can suggest another task while none is selected.
213
- - Accent **+** creates, starts, and selects the suggested task.
214
- - Green **βœ“** completes the selected task, starts a clean unselected session, and reloads Pi without a completion prompt. Menu **Done** and **Complete task** in Settings do the same.
213
+ - Muted **βœ–** dismisses only the current suggestion without creating a note; the agent can suggest another task while none is selected.
214
+ - Accent **✚** creates, starts, and selects the suggested task.
215
+ - Green **βœ”** completes the selected task, starts a clean unselected session, and reloads Pi without a completion prompt. Menu **Done** and **Complete task** in Settings do the same.
215
216
 
216
- Done occupies the former βœ• position, not the + position, so clicking Add twice cannot accidentally complete the new task. The selected row removes the former + cell's padding. The selected task's title is a separate preview link.
217
+ Done occupies the former βœ– position, not the ✚ position, so clicking Add twice cannot accidentally complete the new task. The selected row removes the former ✚ cell's padding. The selected task's title is a separate preview link.
217
218
 
218
219
  The configured `footer.titleWidth` bounds the entire row (default 60 columns). Controls stay first; the tag is dropped before truncating the first-line title with `...`. Tiny widths hide controls (pin only). Pi can still clip the combined status row on narrow terminals.
219
220
 
@@ -221,8 +222,11 @@ Controls use the [Kitty preview handler](#display-only-preview), with no additio
221
222
 
222
223
  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.
223
224
 
224
- An existing suggestion is retained rather than replaced. 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
225
+ 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
225
226
  tag name when it fits.
227
+
228
+ ![Pending suggestion replaced without creating a task](../docs/images/pi-task-replacement.png)
229
+
226
230
  Task text starts with a short, action-oriented title (aim for at most 60 characters).
227
231
  Put context, URLs, commands, and acceptance criteria after a blank line; the title
228
232
  should not contain implementation details. This is agent guidance, not a storage limit.
package/icons/add.txt CHANGED
@@ -1 +1 @@
1
- +
1
+ ✚
package/icons/check.txt CHANGED
@@ -1 +1 @@
1
- βœ“
1
+ βœ”
package/icons/cross.txt CHANGED
@@ -1 +1 @@
1
- βœ•
1
+ βœ–
package/index.ts CHANGED
@@ -20,7 +20,7 @@ const offerGuidance = {
20
20
  "github-remote": `${implementationOfferGuidance} For eligible work, first verify with Git that the current repository has a remote whose URL host is github.com (HTTPS or SSH). Only then use pinote_propose to show one suggested \`[tag] task\` in the bottom bar instead of asking in chat. In noninteractive modes, ask before creating it. Local paths, other hosts, and GitHub-looking URL paths do not qualify. If no GitHub remote is verified, do not offer a task.`,
21
21
  never: "Do not offer to create a pinote task. Create one only when the user explicitly requests it.",
22
22
  };
23
- const consentGuidance = "The suggestion bar's plus creates, starts and selects the note; its cross dismisses only that suggestion, not future suggestions. Continue the requested work while the suggestion is pending; do not ask again in chat or create a note yourself. On a chat no, continue without a note; on a chat yes, call pinote_add with select true. Never add or select without consent. If a task is already selected, do not replace it unless the user asks to switch.";
23
+ const consentGuidance = "When a different task would fit better and no task is selected, call pinote_propose again to replace the unaccepted suggestion without waiting for dismissal. The suggestion bar's plus creates, starts and selects the note; its cross dismisses only that suggestion, not future suggestions. Continue the requested work while the suggestion is pending; do not ask again in chat or create a note yourself. On a chat no, continue without a note; on a chat yes, call pinote_add with select true. Never add or select without consent. If a task is already selected, do not replace it unless the user asks to switch.";
24
24
  const titleGuidance = "Start task text with a short, action-oriented summary (aim for at most 60 characters). Put context, URLs, commands, and acceptance criteria after a blank line. Never put implementation details in the title.";
25
25
  const tagGuidance = "Reuse a pinote_tags name when it fits. One tag; case-sensitive, trimmed, at most 64 characters. Tags are set when creating tasks with pinote_add; agent tools cannot retag existing tasks. Do not use pinote_update_current for tags or task text.";
26
26
  type Task = {
@@ -755,7 +755,7 @@ export default function (pi: ExtensionAPI) {
755
755
  name: "pinote_propose",
756
756
  label: "Pinote suggest task",
757
757
  promptGuidelines: createGuidance,
758
- description: "Show a suggested task beside the pin icon in the footer, without creating it or asking in chat. + creates, starts and selects it; βœ• dismisses it. Continue the requested work while awaiting consent. Requires an interactive TUI with no selected task. An existing suggestion is retained; dismissal clears only that suggestion and allows later proposals. In noninteractive modes ask in chat, then use pinote_add only after consent.",
758
+ description: "Show a suggested task beside the pin icon in the footer, without creating it or asking in chat. + creates, starts and selects it; βœ• dismisses it. Continue the requested work while awaiting consent. Requires an interactive TUI with no selected task. A new proposal replaces the unaccepted suggestion and invalidates its old links; no dismissal is needed. Dismissal clears only the current suggestion and allows later proposals. In noninteractive modes ask in chat, then use pinote_add only after consent.",
759
759
  parameters: Type.Object({
760
760
  text: Type.String({ minLength: 1, description: "Short action-oriented title, then optional details after a blank line." }),
761
761
  tag: Type.Optional(Type.String({ minLength: 1, maxLength: 64 })),
@@ -771,7 +771,6 @@ export default function (pi: ExtensionAPI) {
771
771
  if (current || selectedId !== null) throw new Error("A task is already selected; keep it unless the user asks to switch.");
772
772
  if (pending || setupAbort) throw new Error("A pinote operation is already open. Retry after it finishes.");
773
773
  const result = (status: string) => ({ content: [{ type: "text" as const, text: JSON.stringify({ status }) }], details: { status } });
774
- if (suggestion) return result("pending; existing suggestion retained, continue work without asking again");
775
774
  suggestion = { text: params.text, ...(params.tag === undefined ? {} : { tag: params.tag.trim() }) };
776
775
  saveSuggestion();
777
776
  previewBridge?.setSuggestion((choice) => respondToSuggestion(choice, ctx));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-note",
3
- "version": "0.16.2",
3
+ "version": "0.17.1",
4
4
  "description": "Persistent pinote tasks, Markdown handoffs, and task status for Pi",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -34,7 +34,7 @@ export function renderSuggestion(task: Task, width: number, theme: Colors, urls:
34
34
  }
35
35
 
36
36
  export function renderSelectedTask(task: Task, width: number, theme: Colors, urls: { preview?: string; done?: string }): string {
37
- // Done stays in the former 'βœ•' cell, never the Add cell.
37
+ // Done stays in the former 'βœ–' cell, never the Add cell.
38
38
  return renderTask(task, width, theme,
39
39
  `${theme.bold(pin)} ${theme.fg("success", link(theme.bold(check), urls.done))}`,
40
40
  urls.preview);