@kmiyh/pi-skills-menu 1.1.0 → 1.2.1

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/README.md CHANGED
@@ -1,14 +1,25 @@
1
1
  # @kmiyh/pi-skills-menu
2
2
 
3
- `@kmiyh/pi-skills-menu` is a Pi Agent extension that moves all skills out of Pi's main menu into a separate menu opened with:
3
+ `@kmiyh/pi-skills-menu` is a Pi extension that moves skill browsing and selection into a dedicated `/skills` menu.
4
+
5
+ Instead of filling Pi's main menu with many `/skill:<name>` entries, it gives you one focused place to search, preview, insert, create, edit, rename, delete, and enable or disable skills.
6
+
7
+ ## Why use it
8
+
9
+ - keeps the main menu cleaner when many skills are installed
10
+ - puts project, global, and package-provided skills in one searchable list
11
+ - makes skill selection faster in interactive sessions
12
+ - lets you manage your own skills without leaving the TUI
13
+
14
+ ## Installation
4
15
 
5
16
  ```bash
6
- /skills
17
+ pi install npm:@kmiyh/pi-skills-menu
7
18
  ```
8
19
 
9
- The idea behind the extension is simple: **keep the main menu from getting cluttered** and make it at least a bit easier to navigate when many skills are installed.
20
+ ## What changes after installation
10
21
 
11
- When the extension is installed, it automatically writes the following to `settings.json`:
22
+ When the extension is installed, it automatically writes this to `settings.json`:
12
23
 
13
24
  ```json
14
25
  {
@@ -16,135 +27,148 @@ When the extension is installed, it automatically writes the following to `setti
16
27
  }
17
28
  ```
18
29
 
19
- This disables the default `/skill:<name>` command registration in the main menu and moves skill usage into one dedicated menu.
20
-
21
- ## Installation
30
+ That disables the default `/skill:<name>` command registration in the main menu and replaces it with a single `/skills` entry.
22
31
 
23
- It can be installed with:
32
+ When you insert a skill from the menu, the extension adds a marker like this to the editor:
24
33
 
25
- ```bash
26
- pi install npm:@kmiyh/pi-skills-menu
34
+ ```text
35
+ [skill] my-skill
27
36
  ```
28
37
 
29
- ## What the `/skills` menu contains
38
+ Before the message is sent, Pi expands that marker into the actual skill content.
30
39
 
31
- The menu shows **all installed available skills**:
40
+ ## What the `/skills` menu shows
41
+
42
+ The menu includes all available installed skills:
32
43
 
33
- - global skills
34
44
  - project skills
35
- - skills coming from other installed libraries/packages
45
+ - global skills
46
+ - skills provided by installed packages/libraries
36
47
 
37
- The list is split into two sections:
48
+ The list is grouped into:
38
49
 
39
- - **Your Skills** — the user's own skills
40
- - **Library Skills** — skills coming from other installed libraries
50
+ - **Your Skills** — your own project and global skills
51
+ - **Library Skills** — skills coming from installed packages
41
52
 
42
53
  ![skills-menu.jpg](src/images/skills-menu.jpg)
43
54
 
44
- ## What the menu can do
55
+ ## What you can do in `/skills`
45
56
 
46
- ### Search
57
+ | Action | Shortcut | Notes |
58
+ | --- | --- | --- |
59
+ | Filter skills by name | type | Search works directly in the list |
60
+ | Open preview | `Tab` | Shows full metadata and content |
61
+ | Insert selected skill | `Enter` | Works only for enabled skills |
62
+ | Enable or disable a skill | `Ctrl+X` | Works for your skills and library skills |
63
+ | Create a new skill | `Enter` on **Create new skill** | Opens the creation flow |
64
+ | Delete your own skill | `Backspace` | Available only for project/global skills you own |
47
65
 
48
- The menu includes a search field for filtering skills by name.
66
+ ### Preview a skill
49
67
 
50
- ### Skill preview
68
+ Press `Tab` on a selected skill to open preview mode.
51
69
 
52
- Press:
70
+ The preview shows:
53
71
 
54
- - `Tab` — open the selected skill in preview mode
72
+ - the skill name and scope
73
+ - its source path or package
74
+ - whether it is enabled or disabled
75
+ - the full frontmatter
76
+ - the full skill content with scrolling support
55
77
 
56
78
  ![skill-preview.jpg](src/images/skill-preview.jpg)
57
79
 
58
80
  ### Insert a skill into the editor
59
81
 
60
- Press:
82
+ Press `Enter` on an enabled skill to insert it into the editor.
61
83
 
62
- - `Enter` select an enabled skill and insert it into the editor
63
-
64
- After that, the skill is inserted into the editor so it will be used by Pi when the message is sent.
84
+ This is useful when you want to explicitly attach one or more skills to the message you are writing, without manually copying skill content.
65
85
 
66
86
  ### Enable or disable a skill
67
87
 
68
- Press:
88
+ Press `Ctrl+X` to toggle the selected skill.
69
89
 
70
- - `x` toggle the selected skill between enabled and disabled
90
+ Disabled skills stay visible in the list and are marked with `[disabled]`, so you can still find them and re-enable them later.
71
91
 
72
- This also works for skills that come from installed libraries/packages.
92
+ This also works for skills that come from installed packages.
73
93
 
74
- Disabled skills stay visible in the list and are marked with:
94
+ ![skill-disable.jpg](src/images/skill-disable.jpg)
75
95
 
76
- - `[disabled]`
96
+ ## Creating a new skill
77
97
 
78
- ### Create a new skill
98
+ The first row in the menu is **Create new skill**.
79
99
 
80
- The list also contains a dedicated entry for creating a new skill.
100
+ Creation is split into three short steps:
81
101
 
82
- Skill creation is done in three steps:
102
+ 1. **Name** the skill folder/name slug
103
+ 2. **Description** — one clear sentence describing what the skill does and when Pi should use it
104
+ 3. **Visibility** — whether the skill should be saved globally or only for the current project
83
105
 
84
- 1. **skill name**
106
+ ### 1. Name
85
107
 
86
108
  ![skill-create-name.jpg](src/images/skill-create-name.jpg)
87
109
 
88
- 2. **skill description**
110
+ ### 2. Description
89
111
 
90
112
  ![skill-create-description.jpg](src/images/skill-create-description.jpg)
91
113
 
92
- 3. **skill visibility**
93
- - **Global** — save the skill in your user-level Pi skills directory
94
- - **Project** — save the skill in the current project's `.pi/skills` directory
114
+ ### 3. Visibility
115
+
116
+ ![skill-create-visibility.jpg](src/images/skill-create-visibility.jpg)
95
117
 
96
- After that, the extension generates a `SKILL.md`.
118
+ After that, the extension generates `SKILL.md` for you.
97
119
 
98
120
  Generation uses:
99
121
 
100
- - the **currently selected model in the TUI**
101
- - the **currently selected thinking level** in the TUI
122
+ - the model currently selected in the TUI
123
+ - the current thinking level selected in the TUI
102
124
 
103
- So skill generation always follows the exact model configuration already active in the current Pi session.
125
+ So the draft follows the model configuration already active in your Pi session.
104
126
 
105
- ![skill-create-generating.jpg](src/images/skill-create-generating.jpg)
127
+ ![skill-create-generate.jpg](src/images/skill-create-generate.jpg)
106
128
 
107
- ## Skill preview
129
+ ## Editing, renaming, and deleting your own skills
108
130
 
109
- The preview opened with `Tab` contains:
131
+ Your own project and global skills can be managed directly from preview mode.
110
132
 
111
- - the full skill text
112
- - scrolling support for reading the content
133
+ Library skills can be previewed, inserted, and enabled/disabled, but they cannot be edited, renamed, or deleted from this menu.
113
134
 
114
- From preview mode, you can also use quick actions:
135
+ ### Edit skill content
115
136
 
116
- - `r` edit the skill name
137
+ In preview mode, press `e` to edit the skill content and metadata body.
138
+
139
+ Use `Ctrl+S` to save.
117
140
 
118
141
  ![skill-edit.jpg](src/images/skill-edit.jpg)
119
142
 
120
- - `e` edit the skill content itself
143
+ ### Rename a skill
121
144
 
122
- ![skill-rename.jpg](src/images/skill-rename.jpg)
145
+ In preview mode, press `r` to rename the skill.
123
146
 
124
- ## Where new skills are saved
147
+ This updates both the directory name and the `name` field in frontmatter.
125
148
 
126
- New skills are saved into Pi's standard skill directories depending on the selected scope.
149
+ ![skill-rename.jpg](src/images/skill-rename.jpg)
127
150
 
128
- ### Project scope
151
+ ### Delete a skill
129
152
 
130
- If project scope is selected, the skill is saved here:
153
+ In browse mode or preview mode, press `Backspace` to delete your own skill.
131
154
 
132
- ```text
133
- .pi/skills/<skill-name>/SKILL.md
134
- ```
155
+ A confirmation dialog is shown before removal.
135
156
 
136
- Example:
157
+ ![skill-delete.jpg](src/images/skill-delete.jpg)
137
158
 
138
- ```text
139
- .pi/skills/react-review/SKILL.md
140
- ```
159
+ ## Where new skills are saved
160
+
161
+ New skills are stored in Pi's standard skill directories depending on the selected visibility.
141
162
 
142
- ### Global scope
163
+ | Visibility | Path |
164
+ | --- | --- |
165
+ | Project | `.pi/skills/<skill-name>/SKILL.md` |
166
+ | Global | `~/.pi/agent/skills/<skill-name>/SKILL.md` |
143
167
 
144
- If global scope is selected, the skill is saved here:
168
+ Example project skill:
145
169
 
146
170
  ```text
147
- ~/.pi/agent/skills/<skill-name>/SKILL.md
171
+ .pi/skills/react-review/SKILL.md
148
172
  ```
149
173
 
150
174
  ## Local development
@@ -167,31 +191,29 @@ Run the extension directly from a local checkout:
167
191
  pi -e ./src/index.ts
168
192
  ```
169
193
 
170
- ## How to help improve the extension
194
+ ## Contributing
171
195
 
172
- If you want to help improve the extension, you can:
196
+ Contributions are welcome, especially around:
173
197
 
174
- - report bugs and UX issues
175
- - suggest improvements to the `/skills` menu
176
- - improve skill generation quality
177
- - improve search and list navigation
178
- - improve editing and preview flows
179
- - suggest improvements to project structure and Pi package support
180
- - etc
198
+ - menu UX and navigation
199
+ - skill search and filtering
200
+ - preview and editing flows
201
+ - skill generation quality
202
+ - Pi package compatibility
181
203
 
182
- A typical contribution workflow:
204
+ Typical workflow:
183
205
 
184
206
  1. fork the repository
185
- 2. create a separate branch
207
+ 2. create a branch
186
208
  3. make your changes
187
- 4. verify everything with:
209
+ 4. verify with:
188
210
 
189
211
  ```bash
190
212
  npm install
191
213
  npm run typecheck
192
214
  ```
193
215
 
194
- 5. test locally
216
+ 5. test locally:
195
217
 
196
218
  ```bash
197
219
  pi -e ./src/index.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kmiyh/pi-skills-menu",
3
- "version": "1.1.0",
3
+ "version": "1.2.1",
4
4
  "description": "Pi extension that moves skills into a dedicated /skills menu with browsing, preview, editing, and AI-assisted creation.",
5
5
  "type": "module",
6
6
  "keywords": [
@@ -546,6 +546,7 @@ export type SkillCreationThinkingLevel = ThinkingLevel | "off";
546
546
 
547
547
  export interface SkillGenerationOptions {
548
548
  thinkingLevel?: SkillCreationThinkingLevel;
549
+ signal?: AbortSignal;
549
550
  }
550
551
 
551
552
  class SingleLineText implements Component {
@@ -863,11 +864,21 @@ function getGenerationStatusLabel(
863
864
  : `Generating skill draft using ${modelLabel}...`;
864
865
  }
865
866
 
867
+ function isAbortError(error: unknown): boolean {
868
+ if (!error || typeof error !== "object") return false;
869
+ const name = "name" in error ? String((error as { name?: unknown }).name) : "";
870
+ const message = "message" in error ? String((error as { message?: unknown }).message) : "";
871
+ return name === "AbortError" || message.toLowerCase().includes("aborted");
872
+ }
873
+
866
874
  async function generateSkillDraft(
867
875
  ctx: ExtensionContext,
868
876
  answers: SkillCreationAnswers,
869
877
  options?: SkillGenerationOptions,
870
878
  ): Promise<string> {
879
+ if (options?.signal?.aborted) {
880
+ throw new Error("Generation aborted");
881
+ }
871
882
  if (!ctx.model) {
872
883
  return buildFallbackSkill(answers);
873
884
  }
@@ -910,9 +921,13 @@ async function generateSkillDraft(
910
921
  const response = await completeSimple(
911
922
  ctx.model,
912
923
  { systemPrompt: GENERATE_SKILL_SYSTEM_PROMPT, messages: [userMessage] },
913
- { apiKey: auth.apiKey, headers: auth.headers, ...(reasoning ? { reasoning } : {}) },
924
+ { apiKey: auth.apiKey, headers: auth.headers, ...(reasoning ? { reasoning } : {}), ...(options?.signal ? { signal: options.signal } : {}) },
914
925
  );
915
926
 
927
+ if (options?.signal?.aborted) {
928
+ throw new Error("Generation aborted");
929
+ }
930
+
916
931
  const generated = response.content
917
932
  .filter((c): c is { type: "text"; text: string } => c.type === "text")
918
933
  .map((c) => c.text)
@@ -942,12 +957,45 @@ async function runDraftGeneration(
942
957
 
943
958
  generateSkillDraft(ctx, answers, options)
944
959
  .then(done)
945
- .catch(() => done(buildFallbackSkill(answers)));
960
+ .catch((error) => done(isAbortError(error) ? null : buildFallbackSkill(answers)));
946
961
 
947
962
  return loader;
948
963
  });
949
964
  }
950
965
 
966
+ async function saveCreatedSkill(
967
+ ctx: ExtensionContext,
968
+ answers: SkillCreationAnswers,
969
+ draft: string,
970
+ ): Promise<SkillEntry | null> {
971
+ let parsedSkill: ParsedSkillDraft;
972
+ try {
973
+ parsedSkill = parseSkillDraft(draft, answers.name);
974
+ } catch (error) {
975
+ ctx.ui.notify(error instanceof Error ? error.message : "Invalid generated SKILL.md", "error");
976
+ return null;
977
+ }
978
+
979
+ const targetDir = getTargetDir(ctx, answers.location, answers.name);
980
+ const targetPath = join(targetDir, "SKILL.md");
981
+ await mkdir(targetDir, { recursive: true });
982
+ await writeFile(targetPath, parsedSkill.raw, "utf8");
983
+
984
+ ctx.ui.notify(`Created skill: ${targetPath}`, "info");
985
+ return {
986
+ name: parsedSkill.name,
987
+ description: parsedSkill.description,
988
+ path: targetPath,
989
+ content: parsedSkill.content,
990
+ frontmatter: parsedSkill.frontmatter,
991
+ scope: answers.location === "global" ? "user" : "project",
992
+ origin: "top-level",
993
+ source: "auto",
994
+ baseDir: targetDir,
995
+ enabled: true,
996
+ };
997
+ }
998
+
951
999
  async function collectAnswers(ctx: ExtensionContext): Promise<SkillCreationAnswers | null> {
952
1000
  const answers = await ctx.ui.custom<SkillCreationAnswers | null>((tui, _theme, _kb, done) => {
953
1001
  const component = new SkillCreationWizard(ctx.ui.theme, done);
@@ -992,30 +1040,36 @@ export async function createSkillFromAnswers(
992
1040
  return null;
993
1041
  }
994
1042
 
995
- let parsedSkill: ParsedSkillDraft;
1043
+ return await saveCreatedSkill(ctx, answers, draft);
1044
+ }
1045
+
1046
+ export async function createSkillFromAnswersWithoutUI(
1047
+ ctx: ExtensionContext,
1048
+ answers: SkillCreationAnswers,
1049
+ options?: SkillGenerationOptions,
1050
+ ): Promise<SkillEntry | null> {
1051
+ const targetDir = getTargetDir(ctx, answers.location, answers.name);
1052
+ const targetPath = join(targetDir, "SKILL.md");
1053
+ if (existsSync(targetPath)) {
1054
+ ctx.ui.notify(`Skill already exists: ${targetPath}`, "error");
1055
+ return null;
1056
+ }
1057
+
1058
+ let draft: string;
996
1059
  try {
997
- parsedSkill = parseSkillDraft(draft, answers.name);
1060
+ draft = await generateSkillDraft(ctx, answers, options);
998
1061
  } catch (error) {
999
- ctx.ui.notify(error instanceof Error ? error.message : "Invalid generated SKILL.md", "error");
1000
- return null;
1062
+ if (isAbortError(error) || options?.signal?.aborted) {
1063
+ return null;
1064
+ }
1065
+ draft = buildFallbackSkill(answers);
1001
1066
  }
1002
1067
 
1003
- await mkdir(targetDir, { recursive: true });
1004
- await writeFile(targetPath, parsedSkill.raw, "utf8");
1068
+ if (options?.signal?.aborted) {
1069
+ return null;
1070
+ }
1005
1071
 
1006
- ctx.ui.notify(`Created skill: ${targetPath}`, "info");
1007
- return {
1008
- name: parsedSkill.name,
1009
- description: parsedSkill.description,
1010
- path: targetPath,
1011
- content: parsedSkill.content,
1012
- frontmatter: parsedSkill.frontmatter,
1013
- scope: answers.location === "global" ? "user" : "project",
1014
- origin: "top-level",
1015
- source: "auto",
1016
- baseDir: targetDir,
1017
- enabled: true,
1018
- };
1072
+ return await saveCreatedSkill(ctx, answers, draft);
1019
1073
  }
1020
1074
 
1021
1075
  export async function createNewSkill(
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
package/src/index.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import type { ExtensionAPI, ExtensionContext, InputEventResult } from "@mariozechner/pi-coding-agent";
2
- import { createSkillFromAnswers } from "./create-skill.js";
2
+ import { createSkillFromAnswersWithoutUI } from "./create-skill.js";
3
3
  import { deleteSkill } from "./delete-skill.js";
4
4
  import { detectExtensionInstallScope } from "./extension-scope.js";
5
5
  import { expandSkillMarkers, hasSkillMarker, insertSkillMarker, removeIncompleteSkillMarkerLines } from "./markers.js";
@@ -7,8 +7,7 @@ import { loadSkillRegistry } from "./skill-registry.js";
7
7
  import { ensureSkillCommandsHidden } from "./settings-toggle.js";
8
8
  import { setSkillEnabled } from "./skill-enabled-toggle.js";
9
9
  import type { ExtensionInstallScope, SkillRegistry } from "./types.js";
10
- import { showSkillPreview } from "./ui/skill-preview.js";
11
- import { showSkillsSelector } from "./ui/skills-selector.js";
10
+ import { showSkillsManager } from "./ui/skills-manager.js";
12
11
 
13
12
  const EMPTY_REGISTRY: SkillRegistry = {
14
13
  skills: [],
@@ -107,81 +106,42 @@ export default function skillsMenuExtension(pi: ExtensionAPI) {
107
106
  return;
108
107
  }
109
108
 
110
- let selectorIndex = 0;
111
- let selectorQuery = "";
112
- while (true) {
113
- try {
114
- await refreshRegistry(ctx.cwd);
115
- } catch (error) {
116
- console.error("skills-menu: failed to refresh skills registry", error);
117
- ctx.ui.notify("Failed to load skills list", "error");
118
- return;
119
- }
120
-
121
- const selection = await showSkillsSelector(ctx, registry, selectorIndex, selectorQuery);
122
- if (!selection) {
123
- return;
124
- }
125
- selectorIndex = selection.selectedIndex;
126
- selectorQuery = selection.query;
127
-
128
- if (selection.type === "create") {
129
- const createdSkill = await createSkillFromAnswers(ctx, selection.answers, {
130
- thinkingLevel: pi.getThinkingLevel(),
131
- });
132
- if (!createdSkill) {
133
- continue;
134
- }
135
- await refreshRegistry(ctx.cwd);
136
- const createdSkillIndex = registry.allSkills.findIndex((skill) => skill.path === createdSkill.path);
137
- selectorIndex = createdSkillIndex >= 0 ? createdSkillIndex + 1 : 0;
138
- selectorQuery = "";
139
- continue;
140
- }
141
-
142
- if (selection.type === "toggle") {
109
+ try {
110
+ await refreshRegistry(ctx.cwd);
111
+ } catch (error) {
112
+ console.error("skills-menu: failed to refresh skills registry", error);
113
+ ctx.ui.notify("Failed to load skills list", "error");
114
+ return;
115
+ }
116
+
117
+ const selection = await showSkillsManager(ctx, registry, {
118
+ onCreate: async (answers, signal) => await createSkillFromAnswersWithoutUI(ctx, answers, {
119
+ thinkingLevel: pi.getThinkingLevel(),
120
+ signal,
121
+ }),
122
+ onDelete: async (skill) => {
143
123
  try {
144
- await setSkillEnabled(ctx.cwd, selection.skill, !selection.skill.enabled);
145
- await refreshRegistry(ctx.cwd);
146
- ctx.ui.notify(
147
- selection.skill.enabled
148
- ? `Disabled ${selection.skill.name}. Run /reload to fully apply the change.`
149
- : `Enabled ${selection.skill.name}. Run /reload to fully apply the change.`,
150
- "info",
151
- );
124
+ return await deleteSkill(ctx, skill);
152
125
  } catch (error) {
153
- console.error("skills-menu: failed to toggle skill", error);
154
- ctx.ui.notify(error instanceof Error ? error.message : "Failed to update skill visibility", "error");
155
- }
156
- continue;
157
- }
158
-
159
- if (selection.type === "preview") {
160
- await showSkillPreview(ctx, selection.skill);
161
- continue;
162
- }
163
-
164
- if (selection.type === "delete") {
165
- const confirmed = await ctx.ui.confirm(
166
- "Delete skill",
167
- `Delete ${selection.skill.name}? This removes the skill from disk and cannot be undone.`,
168
- );
169
- if (!confirmed) {
170
- continue;
126
+ console.error("skills-menu: failed to delete skill", error);
127
+ ctx.ui.notify("Failed to delete skill", "error");
128
+ return false;
171
129
  }
130
+ },
131
+ onToggle: async (skill, enabled) => {
172
132
  try {
173
- await deleteSkill(ctx, selection.skill);
174
- selectorIndex = Math.max(0, selectorIndex - 1);
133
+ await setSkillEnabled(ctx.cwd, skill, enabled);
175
134
  } catch (error) {
176
- console.error("skills-menu: failed to delete skill", error);
177
- ctx.ui.notify("Failed to delete skill", "error");
135
+ console.error("skills-menu: failed to toggle skill", error);
136
+ throw error instanceof Error ? error : new Error("Failed to update skill visibility");
178
137
  }
179
- continue;
180
- }
181
-
182
- insertSkillMarker(ctx, selection.skill);
138
+ },
139
+ onRefresh: async () => await refreshRegistry(ctx.cwd),
140
+ });
141
+ if (!selection) {
183
142
  return;
184
143
  }
144
+ insertSkillMarker(ctx, selection);
185
145
  },
186
146
  });
187
147
 
@@ -207,7 +167,7 @@ export default function skillsMenuExtension(pi: ExtensionAPI) {
207
167
  return { action: "continue" };
208
168
  }
209
169
 
210
- if (!currentCwd || currentCwd !== ctx.cwd || registry.skills.length === 0) {
170
+ if (!currentCwd || currentCwd !== ctx.cwd || registry.allSkills.length === 0) {
211
171
  try {
212
172
  await refreshRegistry(ctx.cwd);
213
173
  } catch (error) {
@@ -3,10 +3,6 @@ import { parseSkillFile } from "./skill-parser.js";
3
3
  import type { SkillEntry, SkillRegistry } from "./types.js";
4
4
 
5
5
  function compareSkills(a: SkillEntry, b: SkillEntry): number {
6
- if (a.enabled !== b.enabled) {
7
- return a.enabled ? -1 : 1;
8
- }
9
-
10
6
  const scopeRank = (scope: SkillEntry["scope"]) => {
11
7
  switch (scope) {
12
8
  case "project":
@@ -37,7 +37,7 @@ function getSkillLocation(skill: SkillEntry): string {
37
37
  }
38
38
 
39
39
  function getSkillLocationLabel(skill: SkillEntry): string {
40
- return skill.origin === "package" ? "package" : "path";
40
+ return skill.origin === "package" ? "package" : "";
41
41
  }
42
42
 
43
43
  function formatScalar(value: unknown): string {
@@ -271,7 +271,7 @@ class ScrollableSkillPreview implements Component {
271
271
  private buildContentLines(innerWidth: number): string[] {
272
272
  const content = new Container();
273
273
  content.addChild(new Text(this.theme.fg("accent", this.theme.bold(this.skill.name)), 0, 0));
274
- content.addChild(new Text(this.theme.fg("muted", `${getSkillLocationLabel(this.skill)}${getSkillLocation(this.skill)}`), 0, 0));
274
+ content.addChild(new Text(this.theme.fg("muted", `${getSkillLocationLabel(this.skill)}${getSkillLocation(this.skill)}`), 0, 0));
275
275
  content.addChild(new Spacer(1));
276
276
  content.addChild(new Text(this.theme.fg("muted", this.theme.bold("Metadata")), 0, 0));
277
277
  content.addChild(new Text(this.theme.fg("dim", buildFrontmatterBlock(this.skill)), 0, 0));
@@ -292,7 +292,7 @@ class ScrollableSkillPreview implements Component {
292
292
  : "";
293
293
  const editInfo = this.editable ? " • e edit • r rename" : "";
294
294
  return truncateToWidth(
295
- this.theme.fg("dim", `↑/↓ scroll • pgup/pgdn jump • home/end${editInfo} • esc back${scrollInfo}`),
295
+ this.theme.fg("dim", `↑/↓ scroll${editInfo} • esc back${scrollInfo}`),
296
296
  innerWidth,
297
297
  this.theme.fg("dim", "..."),
298
298
  );