@hk_net/pi-thinking-command 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.
- package/CHANGELOG.md +18 -0
- package/LICENSE +3 -0
- package/README.md +11 -8
- package/package.json +24 -5
- package/thinking-shortcut.ts +153 -155
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.1.9 - 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.8 - 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
|
+
- Derive `/thinking` validation and autocomplete choices from the active model's supported thinking levels, refresh them on model changes, and consolidate the overlapping autocomplete providers.
|
|
17
|
+
- Fall back safely to `off` when malformed custom model metadata exposes no supported thinking levels, clarify the two-step `/th` autocomplete flow, and delegate mid-line completion to Pi instead of corrupting trailing text.
|
|
18
|
+
- Document that the individual package and GitHub bundle must not be installed together.
|
package/LICENSE
CHANGED
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# `/thinking` command extension for pi
|
|
2
2
|
|
|
3
|
-
Adds a `/thinking` slash command to
|
|
3
|
+
Adds a `/thinking` slash command to Pi 0.84.1 or newer (tested with the current Pi 0.84.2 release) for changing the active thinking/reasoning level from inside a session.
|
|
4
4
|
|
|
5
5
|
Normally in pi, changing the thinking/reasoning level means opening the settings menu, navigating to **Thinking**, and selecting the requested level. This extension is a convenience shortcut for that workflow, so you can switch levels directly with commands such as `/thinking low` or `/thinking xhigh`.
|
|
6
6
|
|
|
@@ -8,6 +8,8 @@ This is especially useful when switching between model classes. Smaller models,
|
|
|
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, autocomplete providers, and status updates.
|
|
12
|
+
|
|
11
13
|
Install just this extension from npm:
|
|
12
14
|
|
|
13
15
|
```bash
|
|
@@ -36,9 +38,11 @@ After installing, restart pi or run:
|
|
|
36
38
|
## Usage
|
|
37
39
|
|
|
38
40
|
```text
|
|
39
|
-
/thinking [
|
|
41
|
+
/thinking [level]
|
|
40
42
|
```
|
|
41
43
|
|
|
44
|
+
Autocomplete offers only the thinking levels supported by the active model.
|
|
45
|
+
|
|
42
46
|
Examples:
|
|
43
47
|
|
|
44
48
|
```text
|
|
@@ -50,7 +54,7 @@ Examples:
|
|
|
50
54
|
/thinking max
|
|
51
55
|
```
|
|
52
56
|
|
|
53
|
-
If no argument is provided, `/thinking` sets the level to `medium
|
|
57
|
+
If no argument is provided, `/thinking` sets the level to `medium` when supported, otherwise to the model's first supported level (normally `off` for a non-reasoning model).
|
|
54
58
|
|
|
55
59
|
## Levels
|
|
56
60
|
|
|
@@ -62,17 +66,16 @@ If no argument is provided, `/thinking` sets the level to `medium`.
|
|
|
62
66
|
- `xhigh` — very high reasoning budget
|
|
63
67
|
- `max` — maximum reasoning budget
|
|
64
68
|
|
|
65
|
-
> **Note:**
|
|
66
|
-
> nearest supported level (searching both up and down the level hierarchy).
|
|
69
|
+
> **Note:** `/thinking` rejects levels that the active model does not support instead of silently selecting a different level.
|
|
67
70
|
|
|
68
71
|
## Features
|
|
69
72
|
|
|
70
73
|
- Registers the `/thinking` command.
|
|
71
|
-
-
|
|
72
|
-
- Autocompletes `/th...` to `/thinking` without Pi's trailing-space insertion
|
|
74
|
+
- Derives argument completions from Pi's model-specific capabilities and refreshes them when the active model changes.
|
|
75
|
+
- Autocompletes `/th...` to `/thinking` without Pi's trailing-space insertion; press Tab again to open the level picker.
|
|
73
76
|
- Shows a `thinking` status item with the current level.
|
|
74
77
|
- Updates the status item when the thinking level changes.
|
|
75
|
-
- Validates input and displays an error for unknown levels.
|
|
78
|
+
- Validates input and displays an error for unknown or model-incompatible levels.
|
|
76
79
|
|
|
77
80
|
## Issues and feedback
|
|
78
81
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hk_net/pi-thinking-command",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.9",
|
|
4
4
|
"description": "Pi extension that adds a /thinking slash command for changing reasoning level",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"private": false,
|
|
@@ -11,7 +11,10 @@
|
|
|
11
11
|
"thinking"
|
|
12
12
|
],
|
|
13
13
|
"license": "EUPL-1.2",
|
|
14
|
-
"author":
|
|
14
|
+
"author": {
|
|
15
|
+
"name": "KAPPER NETWORK-COMMUNICATIONS GmbH",
|
|
16
|
+
"url": "https://kapper.net"
|
|
17
|
+
},
|
|
15
18
|
"repository": {
|
|
16
19
|
"type": "git",
|
|
17
20
|
"url": "git+https://github.com/hknet/pi-extensions.git",
|
|
@@ -23,11 +26,27 @@
|
|
|
23
26
|
"homepage": "https://github.com/hknet/pi-extensions/tree/main/packages/pi-thinking-command#readme",
|
|
24
27
|
"files": [
|
|
25
28
|
"thinking-shortcut.ts",
|
|
26
|
-
"README.md"
|
|
29
|
+
"README.md",
|
|
30
|
+
"CHANGELOG.md"
|
|
27
31
|
],
|
|
32
|
+
"engines": {
|
|
33
|
+
"node": ">=22.19.0"
|
|
34
|
+
},
|
|
28
35
|
"peerDependencies": {
|
|
29
|
-
"@earendil-works/pi-
|
|
30
|
-
"@earendil-works/pi-
|
|
36
|
+
"@earendil-works/pi-ai": "*",
|
|
37
|
+
"@earendil-works/pi-coding-agent": "*",
|
|
38
|
+
"@earendil-works/pi-tui": "*"
|
|
39
|
+
},
|
|
40
|
+
"peerDependenciesMeta": {
|
|
41
|
+
"@earendil-works/pi-ai": {
|
|
42
|
+
"optional": true
|
|
43
|
+
},
|
|
44
|
+
"@earendil-works/pi-coding-agent": {
|
|
45
|
+
"optional": true
|
|
46
|
+
},
|
|
47
|
+
"@earendil-works/pi-tui": {
|
|
48
|
+
"optional": true
|
|
49
|
+
}
|
|
31
50
|
},
|
|
32
51
|
"pi": {
|
|
33
52
|
"extensions": [
|
package/thinking-shortcut.ts
CHANGED
|
@@ -1,155 +1,153 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
const
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
const
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
return {
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
const
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
pi.
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
});
|
|
155
|
-
}
|
|
1
|
+
// Copyright © 2026 kapper.net - KAPPER NETWORK-COMMUNICATIONS GmbH
|
|
2
|
+
// SPDX-License-Identifier: EUPL-1.2
|
|
3
|
+
|
|
4
|
+
import { getSupportedThinkingLevels } from "@earendil-works/pi-ai";
|
|
5
|
+
import type { Api, Model, ModelThinkingLevel } from "@earendil-works/pi-ai";
|
|
6
|
+
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
7
|
+
import type { AutocompleteItem } from "@earendil-works/pi-tui";
|
|
8
|
+
|
|
9
|
+
type ThinkingLevel = ModelThinkingLevel;
|
|
10
|
+
|
|
11
|
+
const THINKING_LEVEL_DESCRIPTIONS: Record<ThinkingLevel, string> = {
|
|
12
|
+
off: "Disable extended thinking",
|
|
13
|
+
minimal: "Use the smallest available thinking budget",
|
|
14
|
+
low: "Light reasoning",
|
|
15
|
+
medium: "Balanced default reasoning",
|
|
16
|
+
high: "More reasoning for harder tasks",
|
|
17
|
+
xhigh: "Very high reasoning budget",
|
|
18
|
+
max: "Maximum reasoning budget",
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export function normalizeThinkingLevel(
|
|
22
|
+
input: string,
|
|
23
|
+
supportedLevels: readonly ThinkingLevel[],
|
|
24
|
+
): ThinkingLevel | undefined {
|
|
25
|
+
const normalized = input.toLowerCase().trim();
|
|
26
|
+
return supportedLevels.find((level) => level === normalized);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function getThinkingLevelCompletions(
|
|
30
|
+
prefix: string,
|
|
31
|
+
supportedLevels: readonly ThinkingLevel[],
|
|
32
|
+
): AutocompleteItem[] | null {
|
|
33
|
+
const normalizedPrefix = prefix.toLowerCase().trimStart();
|
|
34
|
+
const matches = supportedLevels.filter((level) => level.startsWith(normalizedPrefix));
|
|
35
|
+
|
|
36
|
+
if (matches.length === 0) return null;
|
|
37
|
+
|
|
38
|
+
return matches.map((level) => ({
|
|
39
|
+
value: level,
|
|
40
|
+
label: level,
|
|
41
|
+
description: THINKING_LEVEL_DESCRIPTIONS[level],
|
|
42
|
+
}));
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function isThinkingCommandPrefix(value: string): boolean {
|
|
46
|
+
return value.startsWith("/th") && "/thinking".startsWith(value);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export default function thinkingShortcutExtension(pi: ExtensionAPI) {
|
|
50
|
+
let supportedLevels: ThinkingLevel[] = ["off"];
|
|
51
|
+
|
|
52
|
+
const updateSupportedLevels = (model: Model<Api> | undefined) => {
|
|
53
|
+
const resolved: ThinkingLevel[] = model ? getSupportedThinkingLevels(model) : ["off"];
|
|
54
|
+
supportedLevels = resolved.length > 0 ? resolved : ["off"];
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
pi.on("session_start", (_event, ctx) => {
|
|
58
|
+
updateSupportedLevels(ctx.model);
|
|
59
|
+
ctx.ui.addAutocompleteProvider((current) => ({
|
|
60
|
+
async getSuggestions(lines, cursorLine, cursorCol, options) {
|
|
61
|
+
const line = lines[cursorLine] ?? "";
|
|
62
|
+
const beforeCursor = line.slice(0, cursorCol);
|
|
63
|
+
if (cursorCol === line.length && isThinkingCommandPrefix(beforeCursor)) {
|
|
64
|
+
if (beforeCursor === "/thinking") {
|
|
65
|
+
return { prefix: "", items: getThinkingLevelCompletions("", supportedLevels) ?? [] };
|
|
66
|
+
}
|
|
67
|
+
return {
|
|
68
|
+
prefix: beforeCursor,
|
|
69
|
+
items: [{ value: "thinking", label: "thinking", description: "Set the thinking level" }],
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
if (beforeCursor === "/thinking " && cursorCol === line.length) {
|
|
73
|
+
return { prefix: "", items: getThinkingLevelCompletions("", supportedLevels) ?? [] };
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const match = beforeCursor.match(/^\/thinking\s+(\S*)$/);
|
|
77
|
+
if (match && cursorCol === line.length) {
|
|
78
|
+
const prefix = match[1] ?? "";
|
|
79
|
+
const items = getThinkingLevelCompletions(prefix, supportedLevels);
|
|
80
|
+
return items ? { prefix, items } : null;
|
|
81
|
+
}
|
|
82
|
+
return current.getSuggestions(lines, cursorLine, cursorCol, options);
|
|
83
|
+
},
|
|
84
|
+
|
|
85
|
+
applyCompletion(lines, cursorLine, cursorCol, item, prefix) {
|
|
86
|
+
const line = lines[cursorLine] ?? "";
|
|
87
|
+
const beforeCursor = line.slice(0, cursorCol);
|
|
88
|
+
if (cursorCol === line.length && isThinkingCommandPrefix(beforeCursor)) {
|
|
89
|
+
if (beforeCursor !== "/thinking") {
|
|
90
|
+
return {
|
|
91
|
+
lines: [...lines.slice(0, cursorLine), "/thinking", ...lines.slice(cursorLine + 1)],
|
|
92
|
+
cursorLine,
|
|
93
|
+
cursorCol: "/thinking".length,
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
const nextLine = `/thinking ${item.value}`;
|
|
97
|
+
return {
|
|
98
|
+
lines: [...lines.slice(0, cursorLine), nextLine, ...lines.slice(cursorLine + 1)],
|
|
99
|
+
cursorLine,
|
|
100
|
+
cursorCol: nextLine.length,
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
if (beforeCursor === "/thinking " && cursorCol === line.length) {
|
|
104
|
+
const nextLine = `/thinking ${item.value}`;
|
|
105
|
+
return {
|
|
106
|
+
lines: [...lines.slice(0, cursorLine), nextLine, ...lines.slice(cursorLine + 1)],
|
|
107
|
+
cursorLine,
|
|
108
|
+
cursorCol: nextLine.length,
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
return current.applyCompletion(lines, cursorLine, cursorCol, item, prefix);
|
|
112
|
+
},
|
|
113
|
+
|
|
114
|
+
shouldTriggerFileCompletion(lines, cursorLine, cursorCol) {
|
|
115
|
+
const line = lines[cursorLine] ?? "";
|
|
116
|
+
const beforeCursor = line.slice(0, cursorCol);
|
|
117
|
+
if (/^\/thinking(?:\s+\S*)?$/.test(beforeCursor) && cursorCol === line.length) return false;
|
|
118
|
+
return current.shouldTriggerFileCompletion?.(lines, cursorLine, cursorCol) ?? true;
|
|
119
|
+
},
|
|
120
|
+
}));
|
|
121
|
+
|
|
122
|
+
ctx.ui.setStatus("thinking", `level: ${pi.getThinkingLevel()}`);
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
pi.on("model_select", (event) => {
|
|
126
|
+
updateSupportedLevels(event.model);
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
pi.on("thinking_level_select", (event, ctx) => {
|
|
130
|
+
ctx.ui.setStatus("thinking", `level: ${event.level}`);
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
pi.registerCommand("thinking", {
|
|
134
|
+
description: "Set the thinking level supported by the active model",
|
|
135
|
+
getArgumentCompletions: (prefix) => getThinkingLevelCompletions(prefix, supportedLevels),
|
|
136
|
+
handler: async (args, ctx) => {
|
|
137
|
+
updateSupportedLevels(ctx.model);
|
|
138
|
+
const input = args?.trim() || (supportedLevels.includes("medium") ? "medium" : supportedLevels[0] ?? "off");
|
|
139
|
+
const level = normalizeThinkingLevel(input, supportedLevels);
|
|
140
|
+
|
|
141
|
+
if (!level) {
|
|
142
|
+
ctx.ui.notify(
|
|
143
|
+
`Thinking level "${input}" is not supported by the active model. Supported: ${supportedLevels.join(", ")}.`,
|
|
144
|
+
"error",
|
|
145
|
+
);
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
pi.setThinkingLevel(level);
|
|
150
|
+
ctx.ui.notify(`Thinking level set to: ${pi.getThinkingLevel()}`, "info");
|
|
151
|
+
},
|
|
152
|
+
});
|
|
153
|
+
}
|