pi-skillful 0.3.8 → 0.3.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
CHANGED
|
@@ -1,11 +1,17 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.3.9 - 2026-06-14
|
|
4
|
+
|
|
3
5
|
All notable changes to this project will be documented in this file.
|
|
4
6
|
|
|
5
7
|
This project follows the spirit of [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and uses semantic versioning for releases.
|
|
6
8
|
|
|
7
9
|
## [Unreleased]
|
|
8
10
|
|
|
11
|
+
### Fixed
|
|
12
|
+
|
|
13
|
+
- Skipped Pi package-bundled skills when applying hidden-skill and toggle-slot configuration so `skillful` only affects global and project skills.
|
|
14
|
+
|
|
9
15
|
## [0.3.8] - 2026-06-06
|
|
10
16
|
|
|
11
17
|
### Changed
|
package/README.md
CHANGED
|
@@ -37,6 +37,8 @@ Hidden skills:
|
|
|
37
37
|
- remain loaded by Pi;
|
|
38
38
|
- remain available for explicit invocation with `/skill:name`, including inline invocation.
|
|
39
39
|
|
|
40
|
+
Skills bundled in Pi packages are never affected by `skillful`; only global and project skills can be hidden or toggled.
|
|
41
|
+
|
|
40
42
|
Configuration is stored under the `skillful` key in Pi settings:
|
|
41
43
|
|
|
42
44
|
```json
|
|
@@ -60,7 +62,7 @@ Open the menu with:
|
|
|
60
62
|
/skillful
|
|
61
63
|
```
|
|
62
64
|
|
|
63
|
-
The menu lists
|
|
65
|
+
The menu lists configurable skills alphabetically. Toggle a skill off or on in the active scope. Use the Global/Project tabs to choose which settings file to edit. In the Project tab, inherited on/off values are shown normally; project overrides are highlighted. Press `1` through `9` on a selected skill to assign or clear that scope's session toggle slot. Visibility and toggle slots are independent.
|
|
64
66
|
|
|
65
67
|
Pi's startup `[Skills]` list also highlights hidden skills in the error color (red in the default dark theme).
|
|
66
68
|
|
|
@@ -88,7 +90,7 @@ Configured slots appear on the prompt editor's top border as `N skill-name`. Pro
|
|
|
88
90
|
|
|
89
91
|
`toggleModifier` defaults to `"alt"`. Supported values are `"alt"`, `"ctrl"`, `"ctrl+shift"`, `"alt+shift"`, `"ctrl+alt"`, and `"ctrl+alt+shift"`. Change it if your terminal reserves `alt+number` shortcuts.
|
|
90
92
|
|
|
91
|
-
On app startup, non-hidden skills are active and hidden skills are inactive. Within a running Pi process, `/new` preserves the current toggle state for the new session. Resuming, forking, cloning, reloading, or restarting Pi resets toggle state from settings. Inline `/skill:name` invocation remains explicit and works even when that skill is inactive.
|
|
93
|
+
On app startup, non-hidden skills are active and hidden skills are inactive. Within a running Pi process, `/new` preserves the current toggle state for the new session. Resuming, forking, cloning, reloading, or restarting Pi resets toggle state from settings. Inline `/skill:name` invocation remains explicit and works even when that skill is inactive. Skills bundled in Pi packages are never modified by these toggles.
|
|
92
94
|
|
|
93
95
|
## Installation
|
|
94
96
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-skillful",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.9",
|
|
4
4
|
"description": "Pi package with skill invocation and visibility improvements.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -45,8 +45,8 @@
|
|
|
45
45
|
"@earendil-works/pi-tui": "*"
|
|
46
46
|
},
|
|
47
47
|
"devDependencies": {
|
|
48
|
-
"@earendil-works/pi-coding-agent": "^0.
|
|
49
|
-
"@earendil-works/pi-tui": "^0.
|
|
48
|
+
"@earendil-works/pi-coding-agent": "^0.78.0",
|
|
49
|
+
"@earendil-works/pi-tui": "^0.78.0",
|
|
50
50
|
"@types/node": "^25.6.2",
|
|
51
51
|
"typescript": "^6.0.3"
|
|
52
52
|
},
|
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
type SkillToggleSlot,
|
|
12
12
|
} from "../config.js";
|
|
13
13
|
import { replaceSkillsSection } from "../skill-prompt.js";
|
|
14
|
-
import { listLoadedSkills } from "../skills.js";
|
|
14
|
+
import { isTopLevelSkill, listLoadedSkills } from "../skills.js";
|
|
15
15
|
|
|
16
16
|
const WIDGET_KEY = "pi-skillful-session-toggles";
|
|
17
17
|
const STORE_KEY = Symbol.for("pi-skillful.sessionSkillTogglesStore");
|
|
@@ -97,10 +97,9 @@ export default function sessionSkillToggles(pi: ExtensionAPI) {
|
|
|
97
97
|
pi.on("before_agent_start", (event) => {
|
|
98
98
|
if (state.slots.length === 0 || !event.systemPromptOptions.skills?.length) return;
|
|
99
99
|
|
|
100
|
-
const updatedSkills: Skill[] = event.systemPromptOptions.skills.map((skill) =>
|
|
101
|
-
...skill,
|
|
102
|
-
|
|
103
|
-
}));
|
|
100
|
+
const updatedSkills: Skill[] = event.systemPromptOptions.skills.map((skill) =>
|
|
101
|
+
isTopLevelSkill(skill) ? { ...skill, disableModelInvocation: !isSkillActive(normalizeSkillName(skill.name)) } : skill,
|
|
102
|
+
);
|
|
104
103
|
|
|
105
104
|
const systemPrompt = replaceSkillsSection(event.systemPrompt, updatedSkills);
|
|
106
105
|
if (!systemPrompt) return;
|
|
@@ -164,7 +163,11 @@ function configuredToggleSlots(
|
|
|
164
163
|
pi: ExtensionAPI,
|
|
165
164
|
toggleSlots: Partial<Record<SkillToggleSlot, string>>,
|
|
166
165
|
): ToggleSlotState[] {
|
|
167
|
-
const loadedSkillNames = new Set(
|
|
166
|
+
const loadedSkillNames = new Set(
|
|
167
|
+
listLoadedSkills(pi.getCommands())
|
|
168
|
+
.filter(isTopLevelSkill)
|
|
169
|
+
.map((skill) => skill.name),
|
|
170
|
+
);
|
|
168
171
|
return SKILL_TOGGLE_SLOTS.flatMap((slot): ToggleSlotState[] => {
|
|
169
172
|
const skillName = toggleSlots[slot];
|
|
170
173
|
if (!skillName || !loadedSkillNames.has(skillName)) return [];
|
|
@@ -20,7 +20,7 @@ import {
|
|
|
20
20
|
writeToggleSlots,
|
|
21
21
|
} from "../config.js";
|
|
22
22
|
import { replaceSkillsSection } from "../skill-prompt.js";
|
|
23
|
-
import { listLoadedSkills, type LoadedSkillInfo } from "../skills.js";
|
|
23
|
+
import { isTopLevelSkill, listLoadedSkills, type LoadedSkillInfo } from "../skills.js";
|
|
24
24
|
import { hasActiveSessionSkillToggles, refreshSessionSkillToggles } from "./session-skill-toggles.js";
|
|
25
25
|
const SCOPES: SkillfulScope[] = ["global", "project"];
|
|
26
26
|
const STORE_KEY = Symbol.for("pi-skillful.skillVisibilityStore");
|
|
@@ -95,7 +95,7 @@ export default function skillVisibility(pi: ExtensionAPI) {
|
|
|
95
95
|
if (hidden.size === 0 || !event.systemPromptOptions.skills?.length) return;
|
|
96
96
|
|
|
97
97
|
const filteredSkills: Skill[] = event.systemPromptOptions.skills.map((skill) =>
|
|
98
|
-
hidden.has(skill.name) ? { ...skill, disableModelInvocation: true } : skill,
|
|
98
|
+
isTopLevelSkill(skill) && hidden.has(skill.name) ? { ...skill, disableModelInvocation: true } : skill,
|
|
99
99
|
);
|
|
100
100
|
const systemPrompt = replaceSkillsSection(event.systemPrompt, filteredSkills);
|
|
101
101
|
if (!systemPrompt) return;
|
|
@@ -239,7 +239,7 @@ function buildColorizedSkillList(names: string[], hidden: Set<string>, theme: Th
|
|
|
239
239
|
}
|
|
240
240
|
|
|
241
241
|
function getSkillItems(pi: ExtensionAPI): SkillListItem[] {
|
|
242
|
-
return listLoadedSkills(pi.getCommands());
|
|
242
|
+
return listLoadedSkills(pi.getCommands()).filter(isTopLevelSkill);
|
|
243
243
|
}
|
|
244
244
|
|
|
245
245
|
class SkillfulVisibilityMenu implements Component {
|
package/src/skills.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { readFile } from "node:fs/promises";
|
|
2
2
|
import { dirname } from "node:path";
|
|
3
|
+
import type { SourceInfo } from "@earendil-works/pi-coding-agent";
|
|
3
4
|
import { normalizeSkillName } from "./config.js";
|
|
4
5
|
|
|
5
6
|
export interface SkillCommandInfo {
|
|
@@ -11,12 +12,14 @@ export interface SkillCommandInfo {
|
|
|
11
12
|
export interface LoadedSkillInfo {
|
|
12
13
|
name: string;
|
|
13
14
|
description: string;
|
|
15
|
+
sourceInfo: SourceInfo;
|
|
14
16
|
}
|
|
15
17
|
|
|
16
18
|
interface CommandLike {
|
|
17
19
|
name: string;
|
|
18
20
|
source: string;
|
|
19
21
|
description?: string;
|
|
22
|
+
sourceInfo: SourceInfo;
|
|
20
23
|
}
|
|
21
24
|
|
|
22
25
|
export function listLoadedSkills(commands: Iterable<CommandLike>): LoadedSkillInfo[] {
|
|
@@ -25,11 +28,15 @@ export function listLoadedSkills(commands: Iterable<CommandLike>): LoadedSkillIn
|
|
|
25
28
|
if (command.source !== "skill") continue;
|
|
26
29
|
const name = normalizeSkillName(command.name);
|
|
27
30
|
if (!name) continue;
|
|
28
|
-
byName.set(name, { name, description: command.description ?? "" });
|
|
31
|
+
byName.set(name, { name, description: command.description ?? "", sourceInfo: command.sourceInfo });
|
|
29
32
|
}
|
|
30
33
|
return Array.from(byName.values()).sort((a, b) => a.name.localeCompare(b.name));
|
|
31
34
|
}
|
|
32
35
|
|
|
36
|
+
export function isTopLevelSkill(skill: { sourceInfo: { origin: SourceInfo["origin"] } }): boolean {
|
|
37
|
+
return skill.sourceInfo.origin === "top-level";
|
|
38
|
+
}
|
|
39
|
+
|
|
33
40
|
export function stripFrontmatter(markdown: string): string {
|
|
34
41
|
const normalized = markdown.replace(/^\uFEFF/, "");
|
|
35
42
|
const match = normalized.match(/^---\r?\n[\s\S]*?\r?\n---\r?\n?/);
|