@grrrck/pi-clear 0.4.0 → 0.5.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/CHANGELOG.md CHANGED
@@ -5,6 +5,17 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.5.0] - 2026-11-05
9
+
10
+ ### Added
11
+
12
+ - A **same-or-pick dialog** after the writer picker: *Same model and thinking level* (first, so Enter re-uses the current settings) or *Pick a model and thinking level…*, which opens the destination pickers.
13
+ - Flag `--same`: skips the same-or-pick dialog and re-uses the current session's model and thinking level, as if the first option was chosen.
14
+
15
+ ### Changed
16
+
17
+ - `/hand-off <instructions>` uses the inline steering text as-is: the steering editor only opens when no instructions were typed after the command, so passing steering on the command line no longer adds a dialog to confirm or edit it.
18
+
8
19
  ## [0.4.0] - 2026-10-30
9
20
 
10
21
  ### Added
package/README.md CHANGED
@@ -29,7 +29,7 @@ pi -e npm:@grrrck/pi-clear
29
29
 
30
30
  ```text
31
31
  /clear
32
- /hand-off [--fresh | --live] [steering instructions]
32
+ /hand-off [--fresh | --live] [--same] [steering instructions]
33
33
  ```
34
34
 
35
35
  ## `/clear` behavior
@@ -50,7 +50,7 @@ When `/hand-off` runs, the extension:
50
50
 
51
51
  0. **chooses who writes the hand-off prompt** — see [Choosing the writer](#choosing-the-writer). The picker is skipped when `--fresh` or `--live` was passed
52
52
  1. waits for the agent to become idle if needed
53
- 2. opens a model picker, then a thinking level picker, then a steering editor pre-filled with any text you typed after `/hand-off` (and its flags). Defaults are sensible: the current model and thinking level are preselected, so pressing Enter at each step keeps them. The model picker respects your session's model scoping (`--models` / `enabledModels`, the same set `/scoped-models` shows) and offers a Tab toggle between scoped and all models. In non-TUI modes it falls back to plain dialogs: the scoped list when configured, otherwise a two-level provider → model pick so the dialog never floods the screen
53
+ 2. asks whether the next session keeps the current model and thinking level — the first option, so pressing Enter keeps them — or opens a model picker, then a thinking level picker, then collects steering instructions: text you typed after `/hand-off` (and its flags) is used as-is, and the steering editor only opens when you passed none. `--same` skips the same-or-pick dialog and re-uses the current model and thinking level. The model picker respects your session's model scoping (`--models` / `enabledModels`, the same set `/scoped-models` shows) and offers a Tab toggle between scoped and all models. In non-TUI modes it falls back to plain dialogs: the scoped list when configured, otherwise a two-level provider → model pick so the dialog never floods the screen
54
54
  3. writes the hand-off prompt with the chosen writer — the live agent, or a single off-screen model call over a minimal reconstruction of the conversation
55
55
  4. prepends a provenance comment naming the source session, starts a new session, applies the model and thinking level you picked, and inserts the prompt into the editor via `ctx.ui.setEditorText()`
56
56
  5. stops there: the prompt sits in the editor for you to review, edit, and submit yourself
@@ -78,6 +78,8 @@ When the writer model supports reasoning, the fresh pass also asks for its **thi
78
78
  Fresh pass flags and limits:
79
79
 
80
80
  - `--fresh` skips the writer picker and uses the fresh pass; `--live` skips it and uses the current agent. Passing both is refused before any dialog opens.
81
+ - `--same` skips the same-or-pick dialog and re-uses the current session's model and thinking level. It combines freely with `--fresh` / `--live` and inline steering.
82
+ - Flags are only recognized at the front of the command args: `/hand-off --same fix the tests` parses the flag, but `/hand-off fix the tests --same` passes `--same` through as steering text. Anything after the first non-flag token is steering.
81
83
  - There is no command-arg model flag: if you want a specific model to write the prompt, set it in the [config file](#configuration) (or pick it from the menu).
82
84
  - The source is the post-compaction view of the session, so a compacted conversation is summarized from its summary rather than from nothing.
83
85
  - Trimming keeps the opening message, the newest turns, and a contiguous run after the opening message; the middle is replaced by `[... <n> turns omitted — <k> chars ...]` and you get a notification saying how much was dropped. Raise the budget with the settings below if that happens.
@@ -152,7 +154,10 @@ node --test scripts/minimal-transcript.test.mjs # offline: fresh-pass transcr
152
154
  node --test scripts/fresh-pass-flow.test.mjs # offline: /hand-off flow, both writers
153
155
  ```
154
156
 
155
- The RPC smoke tests require at least two models with configured auth, and they make **real, billed model requests**. The `node --test` scripts need neither.
157
+ The RPC smoke tests need at least two models with configured auth.
158
+ `rpc-handoff-smoke-test.py` makes **real, billed model requests** (the agent
159
+ writes the hand-off prompt); `rpc-smoke-test.py` only drives local RPC state
160
+ and makes none. The `node --test` scripts need neither auth nor a pi process.
156
161
 
157
162
  ## Related extensions
158
163
 
package/index.ts CHANGED
@@ -13,13 +13,17 @@
13
13
  * `/clear` starts an equally fresh session, then restores the model and
14
14
  * thinking level that were active when you cleared.
15
15
  *
16
- * `/hand-off`: hand the conversation to another model.
16
+ * `/hand-off`: hand the conversation to another model (or back to the same
17
+ * one, with `--same`).
17
18
  *
18
19
  * 1. The user runs `/hand-off`, optionally with `--fresh` or `--live` and
19
20
  * steering text after them.
20
- * 2. A dialog flow asks who writes the hand-off prompt, then collects the
21
- * destination model and thinking level, then opens an editor pre-filled
22
- * with the remaining command args.
21
+ * 2. A dialog flow asks who writes the hand-off prompt, then asks whether the
22
+ * next session keeps the current model and thinking level (first option,
23
+ * so Enter keeps them) or opens the destination pickers — `--same` skips
24
+ * that dialog and re-uses the current values. Then steering instructions:
25
+ * text typed inline after `/hand-off` is used as-is, and an editor opens
26
+ * only when no inline steering was passed.
23
27
  * 3. The hand-off prompt is written by one of two writers:
24
28
  * - the **live agent** (default, first in the picker): the extension sends
25
29
  * the current agent a custom message asking it to write its next message
@@ -170,6 +174,24 @@ async function selectModel(
170
174
  return index === -1 ? undefined : ordered[index];
171
175
  }
172
176
 
177
+ /**
178
+ * Same-or-pick picker between the writer and destination dialogs. The first
179
+ * option re-uses the current session's model and thinking level in one Enter
180
+ * press; the second opens the destination pickers. Without a current model
181
+ * there is nothing to re-use, so the destination pickers open directly.
182
+ */
183
+ async function chooseSameOrPick(ctx: ExtensionContext): Promise<"same" | "pick" | undefined> {
184
+ if (!ctx.model) return "pick";
185
+ const labels = [
186
+ `${SAME_SETTINGS_LABEL} (${ctx.model.provider}/${ctx.model.id}${ctx.thinkingLevel ? ` · ${ctx.thinkingLevel}` : ""})`,
187
+ PICK_SETTINGS_LABEL,
188
+ ];
189
+ const chosen = await ctx.ui.select("Model for the next session", labels);
190
+ if (chosen === undefined) return undefined;
191
+ const index = labelIndex(chosen, labels);
192
+ return index === 0 ? "same" : index === 1 ? "pick" : undefined;
193
+ }
194
+
173
195
  /**
174
196
  * Thinking level picker with the active level preselected, so Enter accepts
175
197
  * the current setting.
@@ -913,6 +935,8 @@ async function generateFreshPassPrompt(
913
935
 
914
936
  interface ParsedHandoffArgs {
915
937
  writer?: "live" | "fresh";
938
+ /** `--same`: skip the destination pickers and reuse the current session's model and thinking level. */
939
+ same?: boolean;
916
940
  /** Everything after the leading flags: the steering-editor prefill. */
917
941
  steering: string;
918
942
  error?: string;
@@ -934,6 +958,10 @@ export function parseHandoffArgs(args: string): ParsedHandoffArgs {
934
958
  parsed.writer = writer;
935
959
  continue;
936
960
  }
961
+ if (token === "--same") {
962
+ parsed.same = true;
963
+ continue;
964
+ }
937
965
  break;
938
966
  }
939
967
 
@@ -1057,6 +1085,8 @@ interface WriterOptions {
1057
1085
 
1058
1086
  const WRITER_LIVE_LABEL = "This agent — full context and tools";
1059
1087
  const WRITER_FRESH_OTHER_LABEL = "Fresh pass — another model…";
1088
+ const SAME_SETTINGS_LABEL = "Same model and thinking level";
1089
+ const PICK_SETTINGS_LABEL = "Pick a model and thinking level…";
1060
1090
 
1061
1091
  /** Pick the hand-off writer: the live agent, or a fresh pass with a chosen model. */
1062
1092
  async function chooseWriter(ctx: ExtensionContext, options: WriterOptions): Promise<WriterChoice | undefined> {
@@ -1314,21 +1344,39 @@ export default function (pi: ExtensionAPI) {
1314
1344
  writerReasoning = chosen.reasoning;
1315
1345
  }
1316
1346
 
1317
- // 2. Pick the destination model.
1318
- const target = await pickTargetModel(ctx);
1319
- if (!target) return;
1320
-
1321
- // 3. Pick the thinking level (a scoped `model:level` entry pins it).
1322
- const level = target.thinkingLevel ?? (await selectThinkingLevel(ctx));
1323
- if (level === undefined) return;
1347
+ // 2–3. Destination model and thinking level. The same-or-pick picker's
1348
+ // first option re-uses the current session's values in one Enter press;
1349
+ // `--same` skips that dialog. On the picked-model path a scoped
1350
+ // `model:level` entry pins the level.
1351
+ const reuse = parsed.same ? "same" : await chooseSameOrPick(ctx);
1352
+ if (!reuse) return;
1353
+ let target: PickedModel | undefined;
1354
+ let level: ThinkingLevel | undefined;
1355
+ if (reuse === "same") {
1356
+ if (!ctx.model) {
1357
+ ctx.ui.notify("--same: the current session has no model to reuse", "error");
1358
+ return;
1359
+ }
1360
+ target = { model: ctx.model };
1361
+ level = ctx.thinkingLevel;
1362
+ } else {
1363
+ target = await pickTargetModel(ctx);
1364
+ if (!target) return;
1365
+ level = target.thinkingLevel ?? (await selectThinkingLevel(ctx));
1366
+ if (level === undefined) return;
1367
+ }
1324
1368
 
1325
- // 4. Collect steering instructions, pre-filled from the remaining args.
1326
- const steering = await ctx.ui.editor(
1327
- writer.kind === "live"
1328
- ? "Steering for the hand-off prompt (sent to the current agent)"
1329
- : "Steering for the hand-off prompt (sent to the hand-off writer)",
1330
- parsed.steering.trim(),
1331
- );
1369
+ // 4. Steering instructions: when passed inline as `/hand-off <instructions>`
1370
+ // they are used as-is; only an empty steering text opens the editor.
1371
+ const inlineSteering = parsed.steering.trim();
1372
+ const steering =
1373
+ inlineSteering ||
1374
+ (await ctx.ui.editor(
1375
+ writer.kind === "live"
1376
+ ? "Steering for the hand-off prompt (sent to the current agent)"
1377
+ : "Steering for the hand-off prompt (sent to the hand-off writer)",
1378
+ "",
1379
+ ));
1332
1380
  if (steering === undefined) return;
1333
1381
 
1334
1382
  // 5. Write the hand-off prompt with the chosen writer.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@grrrck/pi-clear",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "description": "Pi extension that starts a fresh session with /clear while keeping your current model and thinking level, and hands conversations off between models with /hand-off",
5
5
  "keywords": [
6
6
  "pi-package",