pi-ui-extend 1.0.41 → 1.0.45

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.
Files changed (236) hide show
  1. package/README.md +50 -13
  2. package/dist/app/app.d.ts +13 -0
  3. package/dist/app/app.js +158 -8
  4. package/dist/app/cli/install.js +2 -2
  5. package/dist/app/commands/command-controller.d.ts +4 -0
  6. package/dist/app/commands/command-controller.js +11 -0
  7. package/dist/app/commands/command-git-actions.d.ts +25 -0
  8. package/dist/app/commands/command-git-actions.js +381 -0
  9. package/dist/app/commands/command-host.d.ts +4 -0
  10. package/dist/app/commands/command-host.js +5 -2
  11. package/dist/app/commands/command-model-actions.d.ts +1 -0
  12. package/dist/app/commands/command-model-actions.js +57 -35
  13. package/dist/app/commands/command-navigation-actions.d.ts +1 -0
  14. package/dist/app/commands/command-navigation-actions.js +16 -3
  15. package/dist/app/commands/command-registry.d.ts +2 -0
  16. package/dist/app/commands/command-registry.js +15 -1
  17. package/dist/app/commands/command-session-actions.js +9 -3
  18. package/dist/app/commands/reload-context-inventory.d.ts +11 -0
  19. package/dist/app/commands/reload-context-inventory.js +70 -0
  20. package/dist/app/extensions/extension-actions-controller.d.ts +1 -0
  21. package/dist/app/extensions/extension-actions-controller.js +6 -0
  22. package/dist/app/extensions/subagent-catalog-state.d.ts +9 -0
  23. package/dist/app/extensions/subagent-catalog-state.js +23 -0
  24. package/dist/app/input/autocomplete-controller.js +61 -34
  25. package/dist/app/input/input-action-controller.d.ts +2 -0
  26. package/dist/app/input/input-action-controller.js +8 -1
  27. package/dist/app/input/input-controller.d.ts +2 -1
  28. package/dist/app/input/input-controller.js +7 -2
  29. package/dist/app/input/prompt-enhancer-controller.js +53 -35
  30. package/dist/app/input/voice-controller.d.ts +41 -45
  31. package/dist/app/input/voice-controller.js +351 -419
  32. package/dist/app/model/model-usage-controller.js +17 -7
  33. package/dist/app/model/model-usage-status.d.ts +4 -1
  34. package/dist/app/model/model-usage-status.js +270 -46
  35. package/dist/app/popup/menu-items-controller.d.ts +13 -3
  36. package/dist/app/popup/menu-items-controller.js +37 -21
  37. package/dist/app/popup/popup-action-controller.d.ts +12 -2
  38. package/dist/app/popup/popup-action-controller.js +77 -24
  39. package/dist/app/popup/popup-menu-controller.d.ts +36 -14
  40. package/dist/app/popup/popup-menu-controller.js +239 -69
  41. package/dist/app/rendering/dcp-stats.d.ts +9 -4
  42. package/dist/app/rendering/dcp-stats.js +40 -425
  43. package/dist/app/rendering/editor-panels.js +10 -4
  44. package/dist/app/rendering/popup-menu-renderer.d.ts +3 -5
  45. package/dist/app/rendering/popup-menu-renderer.js +42 -37
  46. package/dist/app/rendering/render-controller.js +23 -2
  47. package/dist/app/rendering/status-line-renderer.d.ts +5 -0
  48. package/dist/app/rendering/status-line-renderer.js +41 -5
  49. package/dist/app/rendering/tab-line-renderer.js +26 -20
  50. package/dist/app/runtime.d.ts +12 -1
  51. package/dist/app/runtime.js +120 -12
  52. package/dist/app/screen/mouse-controller.d.ts +2 -0
  53. package/dist/app/screen/mouse-controller.js +17 -7
  54. package/dist/app/screen/status-controller.d.ts +4 -0
  55. package/dist/app/screen/status-controller.js +5 -0
  56. package/dist/app/session/lazy-session-manager.js +34 -0
  57. package/dist/app/session/session-event-controller.d.ts +1 -0
  58. package/dist/app/session/session-event-controller.js +10 -1
  59. package/dist/app/session/session-history.d.ts +1 -0
  60. package/dist/app/session/session-history.js +12 -1
  61. package/dist/app/session/session-lifecycle-controller.d.ts +7 -1
  62. package/dist/app/session/session-lifecycle-controller.js +16 -1
  63. package/dist/app/session/tabs-controller.d.ts +26 -1
  64. package/dist/app/session/tabs-controller.js +454 -147
  65. package/dist/app/subagents/subagents-files.js +60 -1
  66. package/dist/app/subagents/subagents-model.d.ts +1 -0
  67. package/dist/app/subagents/subagents-model.js +18 -2
  68. package/dist/app/subagents/subagents-widget-controller.d.ts +1 -0
  69. package/dist/app/subagents/subagents-widget-controller.js +6 -0
  70. package/dist/app/types.d.ts +16 -1
  71. package/dist/app/workspace/workspace-actions-controller.js +10 -2
  72. package/dist/app/workspace/workspace-undo.d.ts +1 -0
  73. package/dist/app/workspace/workspace-undo.js +1 -0
  74. package/dist/bundled-extensions/question/index.js +9 -1
  75. package/dist/bundled-extensions/question/remote.d.ts +4 -0
  76. package/dist/bundled-extensions/question/remote.js +33 -0
  77. package/dist/bundled-extensions/telegram-connector/bot.d.ts +43 -0
  78. package/dist/bundled-extensions/telegram-connector/bot.js +166 -0
  79. package/dist/bundled-extensions/telegram-connector/config.d.ts +8 -0
  80. package/dist/bundled-extensions/telegram-connector/config.js +87 -0
  81. package/dist/bundled-extensions/telegram-connector/coordinator.d.ts +66 -0
  82. package/dist/bundled-extensions/telegram-connector/coordinator.js +413 -0
  83. package/dist/bundled-extensions/telegram-connector/index.d.ts +3 -0
  84. package/dist/bundled-extensions/telegram-connector/index.js +195 -0
  85. package/dist/bundled-extensions/terminal-bell/index.d.ts +0 -8
  86. package/dist/bundled-extensions/terminal-bell/index.js +0 -76
  87. package/dist/bundled-extensions/workspace-undo/index.d.ts +26 -0
  88. package/dist/bundled-extensions/workspace-undo/index.js +191 -0
  89. package/dist/config.d.ts +21 -2
  90. package/dist/config.js +159 -32
  91. package/dist/default-pix-config.js +25 -5
  92. package/dist/schemas/index.d.ts +1 -0
  93. package/dist/schemas/index.js +1 -0
  94. package/dist/schemas/pi-tools-suite-schema.d.ts +88 -62
  95. package/dist/schemas/pi-tools-suite-schema.js +54 -83
  96. package/dist/schemas/pix-schema.d.ts +19 -2
  97. package/dist/schemas/pix-schema.js +47 -5
  98. package/dist/schemas/tasks-schema.d.ts +18 -0
  99. package/dist/schemas/tasks-schema.js +41 -0
  100. package/docs/concurrency.md +9 -1
  101. package/docs/desktop-mvp.md +22 -6
  102. package/docs/desktop-task-manager.md +148 -84
  103. package/docs/release.md +30 -6
  104. package/external/pi-tools-suite/README.md +197 -84
  105. package/external/pi-tools-suite/docs/evals.md +1 -1
  106. package/external/pi-tools-suite/docs/session-recovery.md +47 -14
  107. package/external/pi-tools-suite/docs/subagent-model-pools.md +48 -30
  108. package/external/pi-tools-suite/docs/ui-qa-subagent.md +440 -0
  109. package/external/pi-tools-suite/package.json +6 -1
  110. package/external/pi-tools-suite/src/antigravity-auth/auth-store.ts +2 -1
  111. package/external/pi-tools-suite/src/antigravity-auth/constants.ts +9 -3
  112. package/external/pi-tools-suite/src/antigravity-auth/headers.ts +41 -3
  113. package/external/pi-tools-suite/src/antigravity-auth/models.ts +94 -21
  114. package/external/pi-tools-suite/src/antigravity-auth/oauth.ts +5 -17
  115. package/external/pi-tools-suite/src/antigravity-auth/payload.ts +72 -7
  116. package/external/pi-tools-suite/src/antigravity-auth/stream.ts +13 -1
  117. package/external/pi-tools-suite/src/async-subagents/agents/frontier-review.md +23 -0
  118. package/external/pi-tools-suite/src/async-subagents/agents/implement.md +1 -1
  119. package/external/pi-tools-suite/src/async-subagents/agents/presets.jsonc +16 -0
  120. package/external/pi-tools-suite/src/async-subagents/agents/research.md +5 -3
  121. package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/backends/browser.mjs +346 -0
  122. package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/backends/desktop.mjs +1038 -0
  123. package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/backends/tui.mjs +759 -0
  124. package/external/pi-tools-suite/src/async-subagents/agents/{browser-qa → ui-qa/browser}/scripts/browser-qa-runner.mjs +32 -23
  125. package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/drivers/chrome-devtools/chrome-devtools-provider.mjs +895 -0
  126. package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/drivers/linux/linux-atspi.py +494 -0
  127. package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/drivers/macos/macos-accessibility.swift +1085 -0
  128. package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/drivers/native-terminal/bridge-client.mjs +50 -0
  129. package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/drivers/native-terminal/native-terminal-host.mjs +501 -0
  130. package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/drivers/windows/windows-uia.ps1 +454 -0
  131. package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/guides/browser-auth.md +114 -0
  132. package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/guides/browser-chrome-devtools.md +91 -0
  133. package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/guides/browser-playwright.md +145 -0
  134. package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/guides/browser.md +82 -0
  135. package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/guides/desktop-linux.md +26 -0
  136. package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/guides/desktop-macos.md +29 -0
  137. package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/guides/desktop-windows.md +23 -0
  138. package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/guides/desktop.md +54 -0
  139. package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/guides/tui-native-terminal.md +59 -0
  140. package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/guides/tui-pty.md +37 -0
  141. package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/guides/tui.md +60 -0
  142. package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/scripts/ui-qa-runner.mjs +535 -0
  143. package/external/pi-tools-suite/src/async-subagents/agents/ui-qa.md +75 -0
  144. package/external/pi-tools-suite/src/async-subagents/commands.ts +15 -71
  145. package/external/pi-tools-suite/src/async-subagents/core/activity.ts +33 -0
  146. package/external/pi-tools-suite/src/async-subagents/core/agent-catalog.ts +5 -4
  147. package/external/pi-tools-suite/src/async-subagents/core/agent-strategy.ts +1 -1
  148. package/external/pi-tools-suite/src/async-subagents/core/agents-dir.ts +8 -5
  149. package/external/pi-tools-suite/src/async-subagents/core/browser-qa.ts +16 -2
  150. package/external/pi-tools-suite/src/async-subagents/core/config.ts +160 -327
  151. package/external/pi-tools-suite/src/async-subagents/core/model-selection.ts +2 -1
  152. package/external/pi-tools-suite/src/async-subagents/core/registry.ts +158 -33
  153. package/external/pi-tools-suite/src/async-subagents/core/routing.ts +21 -5
  154. package/external/pi-tools-suite/src/async-subagents/core/spawn.ts +40 -26
  155. package/external/pi-tools-suite/src/async-subagents/core/state.ts +40 -0
  156. package/external/pi-tools-suite/src/async-subagents/core/types.ts +7 -0
  157. package/external/pi-tools-suite/src/async-subagents/core/ultrawork-auto.ts +51 -45
  158. package/external/pi-tools-suite/src/async-subagents/index.ts +69 -5
  159. package/external/pi-tools-suite/src/async-subagents/lib.ts +16 -9
  160. package/external/pi-tools-suite/src/async-subagents/tools/spawn.ts +8 -5
  161. package/external/pi-tools-suite/src/async-subagents/tools/subagents.ts +5 -4
  162. package/external/pi-tools-suite/src/async-subagents/types.ts +1 -0
  163. package/external/pi-tools-suite/src/coding-discipline/index.ts +90 -59
  164. package/external/pi-tools-suite/src/config.ts +55 -1
  165. package/external/pi-tools-suite/src/context-gateway/accounting-log.ts +282 -0
  166. package/external/pi-tools-suite/src/context-gateway/config.ts +77 -3
  167. package/external/pi-tools-suite/src/context-gateway/efficiency.ts +414 -0
  168. package/external/pi-tools-suite/src/context-gateway/enforcement.ts +222 -0
  169. package/external/pi-tools-suite/src/context-gateway/index.ts +206 -19
  170. package/external/pi-tools-suite/src/context-gateway/storeless-capabilities.ts +7 -7
  171. package/external/pi-tools-suite/src/context-gateway/telemetry.ts +101 -28
  172. package/external/pi-tools-suite/src/context-gateway/types.ts +16 -3
  173. package/external/pi-tools-suite/src/context-inventory.ts +99 -0
  174. package/external/pi-tools-suite/src/dcp/auto-compress-budget.ts +39 -4
  175. package/external/pi-tools-suite/src/dcp/auto-compress.ts +128 -49
  176. package/external/pi-tools-suite/src/dcp/commands.ts +32 -156
  177. package/external/pi-tools-suite/src/dcp/compress-tool.ts +177 -46
  178. package/external/pi-tools-suite/src/dcp/compression-blocks.ts +6 -58
  179. package/external/pi-tools-suite/src/dcp/compression-preview.ts +9 -0
  180. package/external/pi-tools-suite/src/dcp/compression-progress.ts +22 -0
  181. package/external/pi-tools-suite/src/dcp/config.ts +114 -17
  182. package/external/pi-tools-suite/src/dcp/conversation-index.ts +36 -9
  183. package/external/pi-tools-suite/src/dcp/diagnostics.ts +41 -0
  184. package/external/pi-tools-suite/src/dcp/fresh-tool-results.ts +38 -0
  185. package/external/pi-tools-suite/src/dcp/index.ts +338 -66
  186. package/external/pi-tools-suite/src/dcp/journal.ts +126 -4
  187. package/external/pi-tools-suite/src/dcp/progress-controller.ts +2 -1
  188. package/external/pi-tools-suite/src/dcp/prompts.ts +83 -192
  189. package/external/pi-tools-suite/src/dcp/protected-continuity.ts +175 -0
  190. package/external/pi-tools-suite/src/dcp/pruner-candidates.ts +93 -3
  191. package/external/pi-tools-suite/src/dcp/pruner-message-ids.ts +6 -10
  192. package/external/pi-tools-suite/src/dcp/pruner-nudge.ts +36 -33
  193. package/external/pi-tools-suite/src/dcp/pruner-tools.ts +6 -3
  194. package/external/pi-tools-suite/src/dcp/pruner.ts +1 -0
  195. package/external/pi-tools-suite/src/dcp/routine-pressure.ts +86 -0
  196. package/external/pi-tools-suite/src/dcp/state.ts +9 -0
  197. package/external/pi-tools-suite/src/dcp/statistics.d.ts +8 -0
  198. package/external/pi-tools-suite/src/dcp/statistics.js +156 -0
  199. package/external/pi-tools-suite/src/default-pi-tools-suite-config.ts +78 -22
  200. package/external/pi-tools-suite/src/index.ts +7 -1
  201. package/external/pi-tools-suite/src/lib/project.ts +36 -1
  202. package/external/pi-tools-suite/src/model-tools/index.ts +10 -7
  203. package/external/pi-tools-suite/src/repo-discovery/index.ts +304 -4
  204. package/external/pi-tools-suite/src/resource-registry/index.ts +2551 -0
  205. package/external/pi-tools-suite/src/session-recovery/index.ts +17 -0
  206. package/external/pi-tools-suite/src/shell-command-policy.ts +219 -0
  207. package/external/pi-tools-suite/src/todo/index.ts +21 -0
  208. package/external/pi-tools-suite/src/todo/todo.ts +19 -1
  209. package/external/pi-tools-suite/src/tool-descriptions.ts +47 -19
  210. package/package.json +10 -9
  211. package/schemas/pi-tools-suite.json +466 -287
  212. package/schemas/pix.json +129 -11
  213. package/schemas/tasks.json +131 -0
  214. package/skills/simplify/SKILL.md +33 -5
  215. package/docs/desktop-markdown-media.md +0 -77
  216. package/external/pi-tools-suite/docs/browser-qa-subagent.md +0 -177
  217. package/external/pi-tools-suite/docs/dcp-emergency-current-turn.md +0 -102
  218. package/external/pi-tools-suite/src/async-subagents/agents/browser-qa.md +0 -598
  219. package/external/pi-tools-suite/src/async-subagents/async-subagents.sample.jsonc +0 -54
  220. package/external/pi-tools-suite/src/skill-installer/index.ts +0 -333
  221. package/skills/playwright-cli/SKILL.md +0 -420
  222. package/skills/playwright-cli/references/element-attributes.md +0 -23
  223. package/skills/playwright-cli/references/playwright-tests.md +0 -50
  224. package/skills/playwright-cli/references/request-mocking.md +0 -87
  225. package/skills/playwright-cli/references/running-code.md +0 -241
  226. package/skills/playwright-cli/references/session-management.md +0 -273
  227. package/skills/playwright-cli/references/spec-driven-testing.md +0 -311
  228. package/skills/playwright-cli/references/storage-state.md +0 -290
  229. package/skills/playwright-cli/references/test-generation.md +0 -142
  230. package/skills/playwright-cli/references/tracing.md +0 -154
  231. package/skills/playwright-cli/references/video-recording.md +0 -147
  232. package/skills/spec-lite/SKILL.md +0 -140
  233. /package/external/pi-tools-suite/src/async-subagents/agents/{browser-qa → ui-qa/browser}/examples/qa-auth.example.jsonc +0 -0
  234. /package/external/pi-tools-suite/src/async-subagents/agents/{browser-qa → ui-qa/browser}/examples/qa-flow.example.jsonc +0 -0
  235. /package/external/pi-tools-suite/src/async-subagents/agents/{browser-qa → ui-qa/browser}/vendor/fflate.LICENSE +0 -0
  236. /package/external/pi-tools-suite/src/async-subagents/agents/{browser-qa → ui-qa/browser}/vendor/fflate.mjs +0 -0
@@ -1,333 +0,0 @@
1
- import { promises as fs } from "node:fs";
2
- import { homedir } from "node:os";
3
- import { basename, join, relative } from "node:path";
4
- import type { ExtensionAPI, ExtensionCommandContext, ExtensionContext } from "@earendil-works/pi-coding-agent";
5
-
6
- import { ignoreStaleExtensionContextError } from "../context-usage.js";
7
-
8
- const INSTALL_COMMAND = "install-skill";
9
- const EXPORT_COMMAND = "export-skill";
10
- const LOCAL_SKILLS_DIR = join(homedir(), ".agents", "local_skills");
11
- const SKILL_FILE = "SKILL.md";
12
- const PROJECT_DIR = ".pi";
13
- const PROJECT_SKILLS_SUBDIR = "skills";
14
- const SKIP_NAMES = new Set([".DS_Store"]);
15
- const DESC_MAX = 90;
16
-
17
- type SkillEntry = {
18
- name: string;
19
- path: string;
20
- description: string;
21
- };
22
-
23
- /** Direction-aware transfer plan shared by install and export. */
24
- type Transfer = {
25
- srcDir: string;
26
- destDir: string;
27
- /** Human label for the destination root (with trailing slash); name is appended per skill. */
28
- destRootLabel: string;
29
- commandName: string;
30
- direction: "install" | "export";
31
- };
32
-
33
- function localSkillsLabel(): string {
34
- const home = homedir();
35
- return LOCAL_SKILLS_DIR === home ? LOCAL_SKILLS_DIR : LOCAL_SKILLS_DIR.replace(home, "~");
36
- }
37
-
38
- function projectSkillsDir(ctx: ExtensionContext): string {
39
- return join(ctx.cwd, PROJECT_DIR, PROJECT_SKILLS_SUBDIR);
40
- }
41
-
42
- function projectSkillsLabel(ctx: ExtensionContext): string {
43
- return displayPath(ctx, projectSkillsDir(ctx));
44
- }
45
-
46
- function displayPath(ctx: ExtensionContext, absPath: string): string {
47
- const rel = relative(ctx.cwd, absPath);
48
- return rel && !rel.startsWith("..") ? rel : absPath;
49
- }
50
-
51
- function notify(ctx: ExtensionContext, message: string, type: "info" | "warning" | "error" = "info"): void {
52
- if (ctx.hasUI) ctx.ui.notify(message, type);
53
- else console.log(message);
54
- }
55
-
56
- function truncate(value: string, maxLength: number): string {
57
- const collapsed = value.replace(/\s+/g, " ").trim();
58
- return collapsed.length <= maxLength ? collapsed : `${collapsed.slice(0, Math.max(0, maxLength - 1))}…`;
59
- }
60
-
61
- /** Extract the `description` scalar from SKILL.md YAML frontmatter. Returns "" when absent/unparseable. */
62
- function parseFrontmatterDescription(content: string): string {
63
- const match = content.match(/^---\r?\n([\s\S]*?)\r?\n---/);
64
- if (!match) return "";
65
- const front = match[1] ?? "";
66
- const lines = front.split(/\r?\n/);
67
- const idx = lines.findIndex((line) => /^\s*description:\s*/.test(line));
68
- if (idx === -1) return "";
69
- const headerLine = lines[idx];
70
- if (!headerLine) return "";
71
- const after = headerLine.replace(/^\s*description:\s*/, "");
72
-
73
- // Folded/literal block scalar (>, >-, |, |-)
74
- if (/^[>|]/.test(after)) {
75
- const block: string[] = [];
76
- for (let i = idx + 1; i < lines.length; i += 1) {
77
- const line = lines[i] ?? "";
78
- if (line === "") {
79
- block.push(" ");
80
- continue;
81
- }
82
- if (/^\s+/.test(line)) {
83
- block.push(line.replace(/^\s+/, ""));
84
- continue;
85
- }
86
- break;
87
- }
88
- return block.join(" ").trim();
89
- }
90
-
91
- // Plain or quoted scalar
92
- let value = after.trim();
93
- if ((value.startsWith('"') && value.endsWith('"')) || (value.startsWith("'") && value.endsWith("'"))) {
94
- value = value.slice(1, -1);
95
- }
96
- return value;
97
- }
98
-
99
- async function readSkillDescription(skillDir: string): Promise<string> {
100
- try {
101
- const content = await fs.readFile(join(skillDir, SKILL_FILE), "utf-8");
102
- return parseFrontmatterDescription(content);
103
- } catch {
104
- return "";
105
- }
106
- }
107
-
108
- async function pathExists(path: string): Promise<boolean> {
109
- try {
110
- await fs.access(path);
111
- return true;
112
- } catch {
113
- return false;
114
- }
115
- }
116
-
117
- /** Scan a skills directory: every subdirectory that contains SKILL.md. Returns ENOENT as empty. */
118
- async function scanSkills(dir: string): Promise<SkillEntry[]> {
119
- let entries: import("node:fs").Dirent[];
120
- try {
121
- entries = await fs.readdir(dir, { withFileTypes: true });
122
- } catch (error) {
123
- if ((error as NodeJS.ErrnoException).code === "ENOENT") return [];
124
- throw error;
125
- }
126
-
127
- const skills: SkillEntry[] = [];
128
- for (const entry of entries) {
129
- if (!entry.isDirectory() || SKIP_NAMES.has(entry.name)) continue;
130
- const skillPath = join(dir, entry.name);
131
- if (!(await pathExists(join(skillPath, SKILL_FILE)))) continue;
132
- skills.push({ name: entry.name, path: skillPath, description: await readSkillDescription(skillPath) });
133
- }
134
- return skills.sort((a, b) => a.name.localeCompare(b.name));
135
- }
136
-
137
- function listLocalSkills(): Promise<SkillEntry[]> {
138
- return scanSkills(LOCAL_SKILLS_DIR);
139
- }
140
-
141
- function listProjectSkills(ctx: ExtensionContext): Promise<SkillEntry[]> {
142
- return scanSkills(projectSkillsDir(ctx));
143
- }
144
-
145
- function skillLabel(skill: SkillEntry): string {
146
- const description = skill.description.replace(/\s+/g, " ").trim();
147
- return description ? `${skill.name} — ${truncate(description, DESC_MAX)}` : skill.name;
148
- }
149
-
150
- async function copySkill(source: string, dest: string): Promise<void> {
151
- await fs.mkdir(join(dest, ".."), { recursive: true });
152
- await fs.cp(source, dest, {
153
- recursive: true,
154
- filter: (src) => !SKIP_NAMES.has(basename(src)),
155
- });
156
- }
157
-
158
- /** Copy a skill folder from srcDir/name → destDir/name with overwrite-confirm (UI) / refuse (headless). */
159
- async function transferSkill(ctx: ExtensionCommandContext, name: string, t: Transfer): Promise<void> {
160
- const src = join(t.srcDir, name);
161
- const dest = join(t.destDir, name);
162
-
163
- if (await pathExists(dest)) {
164
- if (!ctx.hasUI) {
165
- notify(ctx, `Skill "${name}" already exists at ${t.destRootLabel}${name}. Remove it first or run /${t.commandName} ${name} interactively to overwrite.`, "error");
166
- return;
167
- }
168
- let confirmed = false;
169
- try {
170
- confirmed = await ctx.ui.confirm("Skill already exists", `"${name}" already exists in ${t.destRootLabel}. Overwrite it?`);
171
- } catch (error) {
172
- ignoreStaleExtensionContextError(error);
173
- return;
174
- }
175
- if (!confirmed) {
176
- notify(ctx, `Cancelled. "${name}" left unchanged.`);
177
- return;
178
- }
179
- await fs.rm(dest, { recursive: true, force: true });
180
- }
181
-
182
- try {
183
- await copySkill(src, dest);
184
- } catch (error) {
185
- const message = error instanceof Error ? error.message : String(error);
186
- notify(ctx, `Failed to ${t.direction} skill "${name}": ${message}`, "error");
187
- return;
188
- }
189
-
190
- if (t.direction === "install") {
191
- notify(ctx, `Installed skill "${name}" → ${t.destRootLabel}${name}. Reloading to activate…`);
192
- try {
193
- await ctx.reload();
194
- } catch (error) {
195
- ignoreStaleExtensionContextError(error);
196
- }
197
- return;
198
- }
199
-
200
- notify(ctx, `Exported skill "${name}" → ${t.destRootLabel}${name}. Available for /install-skill in other projects.`);
201
- }
202
-
203
- function installTransfer(ctx: ExtensionCommandContext): Transfer {
204
- return {
205
- srcDir: LOCAL_SKILLS_DIR,
206
- destDir: projectSkillsDir(ctx),
207
- destRootLabel: `${projectSkillsLabel(ctx)}/`,
208
- commandName: INSTALL_COMMAND,
209
- direction: "install",
210
- };
211
- }
212
-
213
- function exportTransfer(ctx: ExtensionCommandContext): Transfer {
214
- return {
215
- srcDir: projectSkillsDir(ctx),
216
- destDir: LOCAL_SKILLS_DIR,
217
- destRootLabel: `${localSkillsLabel()}/`,
218
- commandName: EXPORT_COMMAND,
219
- direction: "export",
220
- };
221
- }
222
-
223
- // --- install ---
224
-
225
- async function showInstallMenu(ctx: ExtensionCommandContext): Promise<void> {
226
- const skills = await listLocalSkills();
227
- if (skills.length === 0) {
228
- notify(ctx, `No skills found in ${localSkillsLabel()} (each skill is a folder containing ${SKILL_FILE}).`, "warning");
229
- return;
230
- }
231
-
232
- if (!ctx.hasUI) {
233
- notify(ctx, `Available skills in ${localSkillsLabel()}:\n${skills.map((s) => s.name).join("\n")}\n\nUse /${INSTALL_COMMAND} <name> to install one.`, "warning");
234
- return;
235
- }
236
-
237
- const t = installTransfer(ctx);
238
- const labels = skills.map(skillLabel);
239
- const labelToName = new Map(labels.map((label, index) => [label, skills[index]!.name]));
240
- let selected: string | undefined;
241
- try {
242
- selected = await ctx.ui.select(`Install skill into ${projectSkillsLabel(ctx)}/ (${localSkillsLabel()})`, labels);
243
- } catch (error) {
244
- ignoreStaleExtensionContextError(error);
245
- return;
246
- }
247
- if (!selected) return;
248
-
249
- const name = labelToName.get(selected);
250
- if (!name) return;
251
- await transferSkill(ctx, name, t);
252
- }
253
-
254
- async function installByName(ctx: ExtensionCommandContext, name: string): Promise<void> {
255
- const skills = await listLocalSkills();
256
- const match = skills.find((entry) => entry.name === name);
257
- if (!match) {
258
- const available = skills.length > 0 ? skills.map((s) => s.name).join(", ") : "(none)";
259
- notify(ctx, `No skill named "${name}" in ${localSkillsLabel()}. Available: ${available}`, "error");
260
- return;
261
- }
262
- await transferSkill(ctx, match.name, installTransfer(ctx));
263
- }
264
-
265
- // --- export ---
266
-
267
- async function showExportMenu(ctx: ExtensionCommandContext): Promise<void> {
268
- const skills = await listProjectSkills(ctx);
269
- if (skills.length === 0) {
270
- notify(ctx, `No skills found in ${projectSkillsLabel(ctx)}/. Install one first with /${INSTALL_COMMAND}.`, "warning");
271
- return;
272
- }
273
-
274
- if (!ctx.hasUI) {
275
- notify(ctx, `Installed skills in ${projectSkillsLabel(ctx)}/:\n${skills.map((s) => s.name).join("\n")}\n\nUse /${EXPORT_COMMAND} <name> to export one to ${localSkillsLabel()}.`, "warning");
276
- return;
277
- }
278
-
279
- const t = exportTransfer(ctx);
280
- const labels = skills.map(skillLabel);
281
- const labelToName = new Map(labels.map((label, index) => [label, skills[index]!.name]));
282
- let selected: string | undefined;
283
- try {
284
- selected = await ctx.ui.select(`Export skill from ${projectSkillsLabel(ctx)}/ to ${localSkillsLabel()}`, labels);
285
- } catch (error) {
286
- ignoreStaleExtensionContextError(error);
287
- return;
288
- }
289
- if (!selected) return;
290
-
291
- const name = labelToName.get(selected);
292
- if (!name) return;
293
- await transferSkill(ctx, name, t);
294
- }
295
-
296
- async function exportByName(ctx: ExtensionCommandContext, name: string): Promise<void> {
297
- const skills = await listProjectSkills(ctx);
298
- const match = skills.find((entry) => entry.name === name);
299
- if (!match) {
300
- const available = skills.length > 0 ? skills.map((s) => s.name).join(", ") : "(none)";
301
- notify(ctx, `No skill named "${name}" in ${projectSkillsLabel(ctx)}/. Installed: ${available}`, "error");
302
- return;
303
- }
304
- await transferSkill(ctx, match.name, exportTransfer(ctx));
305
- }
306
-
307
- export default function skillInstaller(pi: ExtensionAPI): void {
308
- pi.registerCommand(INSTALL_COMMAND, {
309
- description: `Install a skill from ${localSkillsLabel()} into the current project's ${PROJECT_DIR}/${PROJECT_SKILLS_SUBDIR}`,
310
- handler: async (args: string, ctx) => {
311
- const name = args.trim();
312
- try {
313
- if (name) await installByName(ctx, name);
314
- else await showInstallMenu(ctx);
315
- } catch (error) {
316
- ignoreStaleExtensionContextError(error);
317
- }
318
- },
319
- });
320
-
321
- pi.registerCommand(EXPORT_COMMAND, {
322
- description: `Export a skill from the current project's ${PROJECT_DIR}/${PROJECT_SKILLS_SUBDIR} to ${localSkillsLabel()} for reuse in other projects`,
323
- handler: async (args: string, ctx) => {
324
- const name = args.trim();
325
- try {
326
- if (name) await exportByName(ctx, name);
327
- else await showExportMenu(ctx);
328
- } catch (error) {
329
- ignoreStaleExtensionContextError(error);
330
- }
331
- },
332
- });
333
- }