pi-set-model 0.1.3 → 0.1.5

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 ADDED
@@ -0,0 +1,17 @@
1
+ # Changelog
2
+
3
+ ## 0.1.5 - 2026-08-23
4
+
5
+ ### Changed
6
+
7
+ - Add the copyright holder and EUPL licensing notice to the package license and canonical TypeScript source.
8
+ - Identify KAPPER NETWORK-COMMUNICATIONS GmbH and kapper.net in the npm author metadata.
9
+
10
+ ## 0.1.4 - 2026-08-23
11
+
12
+ ### Changed
13
+
14
+ - Mark Pi's host-provided SDK packages as optional wildcard peers so npm does not install a redundant Pi SDK dependency tree.
15
+ - Verify development and tests against Pi SDK 0.84.2 while retaining runtime compatibility with Pi 0.84.1 and newer.
16
+ - Reject empty saved preference fields and explicitly clamp restored thinking levels with Pi's model-capability helper.
17
+ - Document that the individual package and GitHub bundle must not be installed together.
package/LICENSE CHANGED
@@ -1,3 +1,6 @@
1
+ Copyright © 2026 kapper.net - KAPPER NETWORK-COMMUNICATIONS GmbH
2
+ Licensed under the EUPL
3
+
1
4
  EUROPEAN UNION PUBLIC LICENCE v. 1.2
2
5
  EUPL © the European Union 2007, 2016
3
6
 
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # pi-set-model
2
2
 
3
- Remembers Pi's selected model and thinking level for each project folder. Requires pi `>=0.84.1`.
3
+ Remembers Pi's selected model and thinking level for each project folder. Requires Pi 0.84.1 or newer (tested with the current Pi 0.84.2 release).
4
4
 
5
5
  After selecting a model and thinking level, explicitly save that combination for the current working directory with `/set-model set`. New Pi sessions opened in that directory restore it, rather than retaining the model selected in another project. Later `/model` or thinking changes do not modify the saved preference unless you run `/set-model set` again.
6
6
 
@@ -8,6 +8,8 @@ Preferences are stored in the project at `.pi/set-model.json`; they are never sh
8
8
 
9
9
  ## Install
10
10
 
11
+ > **Avoid duplicate installation.** Install this npm package or the GitHub bundle, not both. Loading both copies can duplicate commands and preference-restoration handlers.
12
+
11
13
  ```bash
12
14
  pi install npm:pi-set-model
13
15
  ```
@@ -31,7 +33,7 @@ Autocomplete completes `/setm...` to `/set-model` without adding a trailing spac
31
33
 
32
34
  The folder is Pi's current working directory. Run Pi from the project root when you want one preference for the whole project. The setting is saved at `<cwd>/.pi/set-model.json`.
33
35
 
34
- If the saved model is unavailable or has no configured API key, Pi keeps the active model and shows a warning. Thinking levels are restored after the model and are clamped to that model's supported levels by Pi. When the session ends, the model and thinking level that were active before the project preference was applied are restored.
36
+ If the saved model is unavailable or has no configured API key, Pi keeps the active model and shows a warning. Empty or malformed preference fields are ignored. Thinking levels are restored after the model and explicitly clamped with Pi's model-capability helper; the restore notice identifies an unsupported saved level. When the session ends, the model and thinking level that were active before the project preference was applied are restored.
35
37
 
36
38
  ## Issues and feedback
37
39
 
package/package.json CHANGED
@@ -1,12 +1,15 @@
1
1
  {
2
2
  "name": "pi-set-model",
3
- "version": "0.1.3",
3
+ "version": "0.1.5",
4
4
  "description": "Pi extension that remembers model and thinking level per project folder",
5
5
  "type": "module",
6
6
  "private": false,
7
7
  "keywords": ["pi-package", "pi", "extension", "model", "thinking", "project"],
8
8
  "license": "EUPL-1.2",
9
- "author": "hknet",
9
+ "author": {
10
+ "name": "KAPPER NETWORK-COMMUNICATIONS GmbH",
11
+ "url": "https://kapper.net"
12
+ },
10
13
  "repository": {
11
14
  "type": "git",
12
15
  "url": "git+https://github.com/hknet/pi-extensions.git",
@@ -14,10 +17,25 @@
14
17
  },
15
18
  "bugs": { "url": "https://github.com/hknet/pi-extensions/issues" },
16
19
  "homepage": "https://github.com/hknet/pi-extensions/tree/main/packages/pi-set-model#readme",
17
- "files": ["set-model.ts", "README.md"],
20
+ "files": ["set-model.ts", "README.md", "CHANGELOG.md"],
21
+ "engines": {
22
+ "node": ">=22.19.0"
23
+ },
18
24
  "peerDependencies": {
19
- "@earendil-works/pi-coding-agent": ">=0.84.1",
20
- "@earendil-works/pi-tui": ">=0.84.1"
25
+ "@earendil-works/pi-ai": "*",
26
+ "@earendil-works/pi-coding-agent": "*",
27
+ "@earendil-works/pi-tui": "*"
28
+ },
29
+ "peerDependenciesMeta": {
30
+ "@earendil-works/pi-ai": {
31
+ "optional": true
32
+ },
33
+ "@earendil-works/pi-coding-agent": {
34
+ "optional": true
35
+ },
36
+ "@earendil-works/pi-tui": {
37
+ "optional": true
38
+ }
21
39
  },
22
40
  "pi": { "extensions": ["./set-model.ts"] }
23
41
  }
package/set-model.ts CHANGED
@@ -1,5 +1,9 @@
1
+ // Copyright © 2026 kapper.net - KAPPER NETWORK-COMMUNICATIONS GmbH
2
+ // SPDX-License-Identifier: EUPL-1.2
3
+
1
4
  import { mkdir, readFile, rename, unlink, writeFile } from "node:fs/promises";
2
5
  import { dirname, join } from "node:path";
6
+ import { clampThinkingLevel } from "@earendil-works/pi-ai";
3
7
  import { CONFIG_DIR_NAME, type ExtensionAPI, type ExtensionContext } from "@earendil-works/pi-coding-agent";
4
8
  import type { AutocompleteItem } from "@earendil-works/pi-tui";
5
9
 
@@ -38,10 +42,13 @@ export async function loadPreference(path: string): Promise<ProjectPreference |
38
42
  const preference = parsed as Partial<ProjectPreference>;
39
43
  if (
40
44
  typeof preference.provider !== "string" ||
45
+ preference.provider.trim() === "" ||
41
46
  typeof preference.model !== "string" ||
42
- typeof preference.thinkingLevel !== "string"
47
+ preference.model.trim() === "" ||
48
+ typeof preference.thinkingLevel !== "string" ||
49
+ preference.thinkingLevel.trim() === ""
43
50
  ) {
44
- throw new Error("expected provider, model, and thinkingLevel");
51
+ throw new Error("expected non-empty provider, model, and thinkingLevel strings");
45
52
  }
46
53
  return preference as ProjectPreference;
47
54
  } catch (error: unknown) {
@@ -160,15 +167,19 @@ export default function setModelExtension(pi: ExtensionAPI) {
160
167
  ctx.ui.notify(`No API key for project model: ${preference.provider}/${preference.model}`, "warning");
161
168
  return;
162
169
  }
163
- pi.setThinkingLevel(preference.thinkingLevel);
170
+ const restoredThinkingLevel = clampThinkingLevel(model, preference.thinkingLevel);
171
+ pi.setThinkingLevel(restoredThinkingLevel);
164
172
  modelBeforeProjectPreference = previousModel;
165
173
  thinkingBeforeProjectPreference = previousThinkingLevel;
166
174
 
167
- if (modelChanged) {
175
+ if (modelChanged || restoredThinkingLevel !== preference.thinkingLevel) {
168
176
  ctx.ui.notify(
169
177
  ctx.ui.theme.fg(
170
178
  "accent",
171
- `Project model restored: ${preference.provider}/${preference.model} · thinking: ${pi.getThinkingLevel()}`,
179
+ `Project model restored: ${preference.provider}/${preference.model} · thinking: ${pi.getThinkingLevel()}` +
180
+ (restoredThinkingLevel !== preference.thinkingLevel
181
+ ? ` (saved level ${preference.thinkingLevel} is unsupported)`
182
+ : ""),
172
183
  ),
173
184
  "info",
174
185
  );