pi-qq 0.1.7 → 0.1.9

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 +10 -8
  2. package/package.json +2 -2
  3. package/qq.ts +28 -21
package/README.md CHANGED
@@ -2,13 +2,14 @@
2
2
 
3
3
  Ask quick side questions about your current [pi](https://pi.dev) session without polluting the main transcript.
4
4
 
5
- `pi-qq` adds `/qq <question>` plus an **alt+q** / **Option+Q** shortcut that toggles `/qq ` in the editor. The answer appears ephemerally in a bottom overlay, using the current session as read-only context, and nothing is added to the main conversation.
5
+ `pi-qq` adds `/qq <question>` plus an **alt+q** / **Option+Q** shortcut that toggles `/qq ` in the editor. Answers appear in a dismissible bottom overlay, can be reopened from in-memory `/qq-history`, and never enter the main conversation.
6
6
 
7
7
  ## Why try it?
8
8
 
9
9
  - **A real side channel:** ask `/qq why are we changing this file?` while the main agent keeps working. The answer shows in a bottom overlay and does not enter the main transcript.
10
10
  - **Context-aware, intentionally constrained:** `/qq` passes read-only main-session context, treats ambiguous references like “this”, “that”, “we”, and “the plan” as references to the active session, and gives the side call no tools. Previous `/qq` answers are available only through `/qq-history`; they are not fed back into future `/qq` calls.
11
- - **Fast, low-friction UX:** `/qq` uses recent context by default, automatically switches to broader context for retrospective questions, and supports explicit `--recent` / `--full` modes. Press **alt+q** / **Option+Q** to toggle `/qq `, then use **Esc** to cancel/dismiss or **↑/↓** to scroll longer answers.
11
+ - **Fast, low-friction UX:** press **alt+q** / **Option+Q** to toggle `/qq `, then use **Esc** to cancel/dismiss or **↑/↓** to scroll longer answers.
12
+ - **Smart context modes:** `/qq` uses recent context by default, automatically switches to broader bounded context for retrospective questions, and supports explicit `--recent` / `--full` modes.
12
13
 
13
14
  ## Demo
14
15
 
@@ -46,10 +47,11 @@ After installing, run `/reload` in pi or restart the session.
46
47
 
47
48
  By default, `/qq` chooses a context mode automatically:
48
49
 
49
- - **Recent mode** for immediate questions like `what did we do in the last turn?`, `what are we doing right now?`, or `why are we changing this?`.
50
- - **Full mode** for retrospective questions like `summarize this session`, `what did we decide so far?`, or `what did we try earlier?`.
51
-
52
- Use `--recent` when you want the fastest answer from the latest messages. Use `--full` when you explicitly want broader session context.
50
+ | Mode | When to use it | Context sent |
51
+ | --- | --- | --- |
52
+ | Auto | Default for `/qq <question>` | Recent context for immediate questions, broader bounded context for retrospective questions |
53
+ | `--recent` | Fastest answers about the latest work | Latest messages only |
54
+ | `--full` | Recaps or questions about earlier decisions | Broader but still bounded session context, not unlimited history |
53
55
 
54
56
  Use `/qq-history` to reopen recent `/qq` answers from the current session. History is view-only and is not included as context for future `/qq` model calls.
55
57
 
@@ -71,8 +73,8 @@ Press **alt+q** / **Option+Q** to toggle `/qq ` at the front of the editor:
71
73
 
72
74
  - The main transcript is never polluted by `/qq` questions or answers.
73
75
  - The side call receives read-only main-session context.
74
- - Recent mode sends only the latest messages for speed; full mode sends broader bounded context.
75
- - Large text parts are clipped, thinking blocks are removed, and images are omitted from the side-call context for speed.
76
+ - Recent mode sends only the latest messages for speed; full mode sends broader but still bounded context, not unlimited history.
77
+ - Large text parts are clipped; images, tool calls, and tool results are converted into plain-text background so the side call never uses provider tool protocol.
76
78
  - The side call has no tools.
77
79
  - Recent `/qq` answers are kept in memory only so `/qq-history` can reopen them after dismissal.
78
80
  - `/qq-history` is view-only; it is not used as context for future `/qq` model calls.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "pi-qq",
3
- "version": "0.1.7",
4
- "description": "Pi extension. Ask context-aware side questions with /qq or alt+q, then reopen answers with /qq-history.",
3
+ "version": "0.1.9",
4
+ "description": "Ask transcript-safe, context-aware side questions in Pi with /qq or alt+q, then reopen recent answers with /qq-history.",
5
5
  "keywords": [
6
6
  "pi-package",
7
7
  "pi-extension",
package/qq.ts CHANGED
@@ -71,13 +71,15 @@ export const QQ_SYSTEM_PROMPT = readFileSync(
71
71
  ).trimEnd();
72
72
 
73
73
  function getState(): QqState {
74
- const globalState = globalThis as unknown as { [k: symbol]: QqState | undefined };
74
+ const globalState = globalThis as unknown as { [k: symbol]: Partial<QqState> | undefined };
75
75
  let state = globalState[QQ_STATE_KEY];
76
76
  if (!state) {
77
- state = { snapshots: new Map(), histories: new Map() };
77
+ state = {};
78
78
  globalState[QQ_STATE_KEY] = state;
79
79
  }
80
- return state;
80
+ state.snapshots ??= new Map();
81
+ state.histories ??= new Map();
82
+ return state as QqState;
81
83
  }
82
84
 
83
85
  function getSessionFile(ctx: ExtensionContext): string {
@@ -143,33 +145,38 @@ function clipText(text: string): string {
143
145
  return `${text.slice(0, MAX_TEXT_CHARS_PER_PART)}\n[…truncated for /qq speed…]`;
144
146
  }
145
147
 
146
- function trimContentArray<T extends Array<{ type: string }>>(content: T): T {
147
- return content
148
- .filter((part) => part.type !== "thinking")
149
- .map((part) => {
150
- if (part.type === "text" && "text" in part && typeof part.text === "string") {
151
- return { ...part, text: clipText(part.text) };
152
- }
153
- if (part.type === "image") {
154
- return { type: "text", text: "[image omitted for /qq speed]" };
155
- }
156
- return part;
157
- }) as T;
148
+ function contentPartsToText(content: Array<{ type: string }>): string {
149
+ const parts: string[] = [];
150
+ for (const part of content) {
151
+ if (part.type === "text" && "text" in part && typeof part.text === "string") {
152
+ parts.push(clipText(part.text));
153
+ } else if (part.type === "image") {
154
+ parts.push("[image omitted for /qq speed]");
155
+ } else if (part.type === "toolCall" && "name" in part && typeof part.name === "string") {
156
+ parts.push(`[assistant requested tool: ${part.name}]`);
157
+ }
158
+ }
159
+ return parts.join("\n").trim();
158
160
  }
159
161
 
160
- function trimUserContent(content: UserMessage["content"]): UserMessage["content"] {
161
- if (typeof content === "string") return clipText(content);
162
- return trimContentArray(content);
162
+ function userContentToText(content: UserMessage["content"]): string {
163
+ return typeof content === "string" ? clipText(content) : contentPartsToText(content);
163
164
  }
164
165
 
165
166
  function trimMessageForContext(message: Message): Message {
166
167
  if (message.role === "assistant") {
167
- return { ...message, content: trimContentArray(message.content) };
168
+ const text = contentPartsToText(message.content) || "[assistant message omitted for /qq speed]";
169
+ return { ...message, content: [{ type: "text", text }] };
168
170
  }
169
171
  if (message.role === "user") {
170
- return { ...message, content: trimUserContent(message.content) };
172
+ return { ...message, content: [{ type: "text", text: userContentToText(message.content) }] };
171
173
  }
172
- return { ...message, content: trimContentArray(message.content) };
174
+ const text = contentPartsToText(message.content) || "[tool result omitted for /qq speed]";
175
+ return {
176
+ role: "user",
177
+ content: [{ type: "text", text: `[tool result: ${message.toolName}]\n${text}` }],
178
+ timestamp: message.timestamp,
179
+ };
173
180
  }
174
181
 
175
182
  function selectRecentContextMessages(messages: Message[]): Message[] {