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.
- package/README.md +50 -13
- package/dist/app/app.d.ts +13 -0
- package/dist/app/app.js +158 -8
- package/dist/app/cli/install.js +2 -2
- package/dist/app/commands/command-controller.d.ts +4 -0
- package/dist/app/commands/command-controller.js +11 -0
- package/dist/app/commands/command-git-actions.d.ts +25 -0
- package/dist/app/commands/command-git-actions.js +381 -0
- package/dist/app/commands/command-host.d.ts +4 -0
- package/dist/app/commands/command-host.js +5 -2
- package/dist/app/commands/command-model-actions.d.ts +1 -0
- package/dist/app/commands/command-model-actions.js +57 -35
- package/dist/app/commands/command-navigation-actions.d.ts +1 -0
- package/dist/app/commands/command-navigation-actions.js +16 -3
- package/dist/app/commands/command-registry.d.ts +2 -0
- package/dist/app/commands/command-registry.js +15 -1
- package/dist/app/commands/command-session-actions.js +9 -3
- package/dist/app/commands/reload-context-inventory.d.ts +11 -0
- package/dist/app/commands/reload-context-inventory.js +70 -0
- package/dist/app/extensions/extension-actions-controller.d.ts +1 -0
- package/dist/app/extensions/extension-actions-controller.js +6 -0
- package/dist/app/extensions/subagent-catalog-state.d.ts +9 -0
- package/dist/app/extensions/subagent-catalog-state.js +23 -0
- package/dist/app/input/autocomplete-controller.js +61 -34
- package/dist/app/input/input-action-controller.d.ts +2 -0
- package/dist/app/input/input-action-controller.js +8 -1
- package/dist/app/input/input-controller.d.ts +2 -1
- package/dist/app/input/input-controller.js +7 -2
- package/dist/app/input/prompt-enhancer-controller.js +53 -35
- package/dist/app/input/voice-controller.d.ts +41 -45
- package/dist/app/input/voice-controller.js +351 -419
- package/dist/app/model/model-usage-controller.js +17 -7
- package/dist/app/model/model-usage-status.d.ts +4 -1
- package/dist/app/model/model-usage-status.js +270 -46
- package/dist/app/popup/menu-items-controller.d.ts +13 -3
- package/dist/app/popup/menu-items-controller.js +37 -21
- package/dist/app/popup/popup-action-controller.d.ts +12 -2
- package/dist/app/popup/popup-action-controller.js +77 -24
- package/dist/app/popup/popup-menu-controller.d.ts +36 -14
- package/dist/app/popup/popup-menu-controller.js +239 -69
- package/dist/app/rendering/dcp-stats.d.ts +9 -4
- package/dist/app/rendering/dcp-stats.js +40 -425
- package/dist/app/rendering/editor-panels.js +10 -4
- package/dist/app/rendering/popup-menu-renderer.d.ts +3 -5
- package/dist/app/rendering/popup-menu-renderer.js +42 -37
- package/dist/app/rendering/render-controller.js +23 -2
- package/dist/app/rendering/status-line-renderer.d.ts +5 -0
- package/dist/app/rendering/status-line-renderer.js +41 -5
- package/dist/app/rendering/tab-line-renderer.js +26 -20
- package/dist/app/runtime.d.ts +12 -1
- package/dist/app/runtime.js +120 -12
- package/dist/app/screen/mouse-controller.d.ts +2 -0
- package/dist/app/screen/mouse-controller.js +17 -7
- package/dist/app/screen/status-controller.d.ts +4 -0
- package/dist/app/screen/status-controller.js +5 -0
- package/dist/app/session/lazy-session-manager.js +34 -0
- package/dist/app/session/session-event-controller.d.ts +1 -0
- package/dist/app/session/session-event-controller.js +10 -1
- package/dist/app/session/session-history.d.ts +1 -0
- package/dist/app/session/session-history.js +12 -1
- package/dist/app/session/session-lifecycle-controller.d.ts +7 -1
- package/dist/app/session/session-lifecycle-controller.js +16 -1
- package/dist/app/session/tabs-controller.d.ts +26 -1
- package/dist/app/session/tabs-controller.js +454 -147
- package/dist/app/subagents/subagents-files.js +60 -1
- package/dist/app/subagents/subagents-model.d.ts +1 -0
- package/dist/app/subagents/subagents-model.js +18 -2
- package/dist/app/subagents/subagents-widget-controller.d.ts +1 -0
- package/dist/app/subagents/subagents-widget-controller.js +6 -0
- package/dist/app/types.d.ts +16 -1
- package/dist/app/workspace/workspace-actions-controller.js +10 -2
- package/dist/app/workspace/workspace-undo.d.ts +1 -0
- package/dist/app/workspace/workspace-undo.js +1 -0
- package/dist/bundled-extensions/question/index.js +9 -1
- package/dist/bundled-extensions/question/remote.d.ts +4 -0
- package/dist/bundled-extensions/question/remote.js +33 -0
- package/dist/bundled-extensions/telegram-connector/bot.d.ts +43 -0
- package/dist/bundled-extensions/telegram-connector/bot.js +166 -0
- package/dist/bundled-extensions/telegram-connector/config.d.ts +8 -0
- package/dist/bundled-extensions/telegram-connector/config.js +87 -0
- package/dist/bundled-extensions/telegram-connector/coordinator.d.ts +66 -0
- package/dist/bundled-extensions/telegram-connector/coordinator.js +413 -0
- package/dist/bundled-extensions/telegram-connector/index.d.ts +3 -0
- package/dist/bundled-extensions/telegram-connector/index.js +195 -0
- package/dist/bundled-extensions/terminal-bell/index.d.ts +0 -8
- package/dist/bundled-extensions/terminal-bell/index.js +0 -76
- package/dist/bundled-extensions/workspace-undo/index.d.ts +26 -0
- package/dist/bundled-extensions/workspace-undo/index.js +191 -0
- package/dist/config.d.ts +21 -2
- package/dist/config.js +159 -32
- package/dist/default-pix-config.js +25 -5
- package/dist/schemas/index.d.ts +1 -0
- package/dist/schemas/index.js +1 -0
- package/dist/schemas/pi-tools-suite-schema.d.ts +88 -62
- package/dist/schemas/pi-tools-suite-schema.js +54 -83
- package/dist/schemas/pix-schema.d.ts +19 -2
- package/dist/schemas/pix-schema.js +47 -5
- package/dist/schemas/tasks-schema.d.ts +18 -0
- package/dist/schemas/tasks-schema.js +41 -0
- package/docs/concurrency.md +9 -1
- package/docs/desktop-mvp.md +22 -6
- package/docs/desktop-task-manager.md +148 -84
- package/docs/release.md +30 -6
- package/external/pi-tools-suite/README.md +197 -84
- package/external/pi-tools-suite/docs/evals.md +1 -1
- package/external/pi-tools-suite/docs/session-recovery.md +47 -14
- package/external/pi-tools-suite/docs/subagent-model-pools.md +48 -30
- package/external/pi-tools-suite/docs/ui-qa-subagent.md +440 -0
- package/external/pi-tools-suite/package.json +6 -1
- package/external/pi-tools-suite/src/antigravity-auth/auth-store.ts +2 -1
- package/external/pi-tools-suite/src/antigravity-auth/constants.ts +9 -3
- package/external/pi-tools-suite/src/antigravity-auth/headers.ts +41 -3
- package/external/pi-tools-suite/src/antigravity-auth/models.ts +94 -21
- package/external/pi-tools-suite/src/antigravity-auth/oauth.ts +5 -17
- package/external/pi-tools-suite/src/antigravity-auth/payload.ts +72 -7
- package/external/pi-tools-suite/src/antigravity-auth/stream.ts +13 -1
- package/external/pi-tools-suite/src/async-subagents/agents/frontier-review.md +23 -0
- package/external/pi-tools-suite/src/async-subagents/agents/implement.md +1 -1
- package/external/pi-tools-suite/src/async-subagents/agents/presets.jsonc +16 -0
- package/external/pi-tools-suite/src/async-subagents/agents/research.md +5 -3
- package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/backends/browser.mjs +346 -0
- package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/backends/desktop.mjs +1038 -0
- package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/backends/tui.mjs +759 -0
- package/external/pi-tools-suite/src/async-subagents/agents/{browser-qa → ui-qa/browser}/scripts/browser-qa-runner.mjs +32 -23
- package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/drivers/chrome-devtools/chrome-devtools-provider.mjs +895 -0
- package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/drivers/linux/linux-atspi.py +494 -0
- package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/drivers/macos/macos-accessibility.swift +1085 -0
- package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/drivers/native-terminal/bridge-client.mjs +50 -0
- package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/drivers/native-terminal/native-terminal-host.mjs +501 -0
- package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/drivers/windows/windows-uia.ps1 +454 -0
- package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/guides/browser-auth.md +114 -0
- package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/guides/browser-chrome-devtools.md +91 -0
- package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/guides/browser-playwright.md +145 -0
- package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/guides/browser.md +82 -0
- package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/guides/desktop-linux.md +26 -0
- package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/guides/desktop-macos.md +29 -0
- package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/guides/desktop-windows.md +23 -0
- package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/guides/desktop.md +54 -0
- package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/guides/tui-native-terminal.md +59 -0
- package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/guides/tui-pty.md +37 -0
- package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/guides/tui.md +60 -0
- package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/scripts/ui-qa-runner.mjs +535 -0
- package/external/pi-tools-suite/src/async-subagents/agents/ui-qa.md +75 -0
- package/external/pi-tools-suite/src/async-subagents/commands.ts +15 -71
- package/external/pi-tools-suite/src/async-subagents/core/activity.ts +33 -0
- package/external/pi-tools-suite/src/async-subagents/core/agent-catalog.ts +5 -4
- package/external/pi-tools-suite/src/async-subagents/core/agent-strategy.ts +1 -1
- package/external/pi-tools-suite/src/async-subagents/core/agents-dir.ts +8 -5
- package/external/pi-tools-suite/src/async-subagents/core/browser-qa.ts +16 -2
- package/external/pi-tools-suite/src/async-subagents/core/config.ts +160 -327
- package/external/pi-tools-suite/src/async-subagents/core/model-selection.ts +2 -1
- package/external/pi-tools-suite/src/async-subagents/core/registry.ts +158 -33
- package/external/pi-tools-suite/src/async-subagents/core/routing.ts +21 -5
- package/external/pi-tools-suite/src/async-subagents/core/spawn.ts +40 -26
- package/external/pi-tools-suite/src/async-subagents/core/state.ts +40 -0
- package/external/pi-tools-suite/src/async-subagents/core/types.ts +7 -0
- package/external/pi-tools-suite/src/async-subagents/core/ultrawork-auto.ts +51 -45
- package/external/pi-tools-suite/src/async-subagents/index.ts +69 -5
- package/external/pi-tools-suite/src/async-subagents/lib.ts +16 -9
- package/external/pi-tools-suite/src/async-subagents/tools/spawn.ts +8 -5
- package/external/pi-tools-suite/src/async-subagents/tools/subagents.ts +5 -4
- package/external/pi-tools-suite/src/async-subagents/types.ts +1 -0
- package/external/pi-tools-suite/src/coding-discipline/index.ts +90 -59
- package/external/pi-tools-suite/src/config.ts +55 -1
- package/external/pi-tools-suite/src/context-gateway/accounting-log.ts +282 -0
- package/external/pi-tools-suite/src/context-gateway/config.ts +77 -3
- package/external/pi-tools-suite/src/context-gateway/efficiency.ts +414 -0
- package/external/pi-tools-suite/src/context-gateway/enforcement.ts +222 -0
- package/external/pi-tools-suite/src/context-gateway/index.ts +206 -19
- package/external/pi-tools-suite/src/context-gateway/storeless-capabilities.ts +7 -7
- package/external/pi-tools-suite/src/context-gateway/telemetry.ts +101 -28
- package/external/pi-tools-suite/src/context-gateway/types.ts +16 -3
- package/external/pi-tools-suite/src/context-inventory.ts +99 -0
- package/external/pi-tools-suite/src/dcp/auto-compress-budget.ts +39 -4
- package/external/pi-tools-suite/src/dcp/auto-compress.ts +128 -49
- package/external/pi-tools-suite/src/dcp/commands.ts +32 -156
- package/external/pi-tools-suite/src/dcp/compress-tool.ts +177 -46
- package/external/pi-tools-suite/src/dcp/compression-blocks.ts +6 -58
- package/external/pi-tools-suite/src/dcp/compression-preview.ts +9 -0
- package/external/pi-tools-suite/src/dcp/compression-progress.ts +22 -0
- package/external/pi-tools-suite/src/dcp/config.ts +114 -17
- package/external/pi-tools-suite/src/dcp/conversation-index.ts +36 -9
- package/external/pi-tools-suite/src/dcp/diagnostics.ts +41 -0
- package/external/pi-tools-suite/src/dcp/fresh-tool-results.ts +38 -0
- package/external/pi-tools-suite/src/dcp/index.ts +338 -66
- package/external/pi-tools-suite/src/dcp/journal.ts +126 -4
- package/external/pi-tools-suite/src/dcp/progress-controller.ts +2 -1
- package/external/pi-tools-suite/src/dcp/prompts.ts +83 -192
- package/external/pi-tools-suite/src/dcp/protected-continuity.ts +175 -0
- package/external/pi-tools-suite/src/dcp/pruner-candidates.ts +93 -3
- package/external/pi-tools-suite/src/dcp/pruner-message-ids.ts +6 -10
- package/external/pi-tools-suite/src/dcp/pruner-nudge.ts +36 -33
- package/external/pi-tools-suite/src/dcp/pruner-tools.ts +6 -3
- package/external/pi-tools-suite/src/dcp/pruner.ts +1 -0
- package/external/pi-tools-suite/src/dcp/routine-pressure.ts +86 -0
- package/external/pi-tools-suite/src/dcp/state.ts +9 -0
- package/external/pi-tools-suite/src/dcp/statistics.d.ts +8 -0
- package/external/pi-tools-suite/src/dcp/statistics.js +156 -0
- package/external/pi-tools-suite/src/default-pi-tools-suite-config.ts +78 -22
- package/external/pi-tools-suite/src/index.ts +7 -1
- package/external/pi-tools-suite/src/lib/project.ts +36 -1
- package/external/pi-tools-suite/src/model-tools/index.ts +10 -7
- package/external/pi-tools-suite/src/repo-discovery/index.ts +304 -4
- package/external/pi-tools-suite/src/resource-registry/index.ts +2551 -0
- package/external/pi-tools-suite/src/session-recovery/index.ts +17 -0
- package/external/pi-tools-suite/src/shell-command-policy.ts +219 -0
- package/external/pi-tools-suite/src/todo/index.ts +21 -0
- package/external/pi-tools-suite/src/todo/todo.ts +19 -1
- package/external/pi-tools-suite/src/tool-descriptions.ts +47 -19
- package/package.json +10 -9
- package/schemas/pi-tools-suite.json +466 -287
- package/schemas/pix.json +129 -11
- package/schemas/tasks.json +131 -0
- package/skills/simplify/SKILL.md +33 -5
- package/docs/desktop-markdown-media.md +0 -77
- package/external/pi-tools-suite/docs/browser-qa-subagent.md +0 -177
- package/external/pi-tools-suite/docs/dcp-emergency-current-turn.md +0 -102
- package/external/pi-tools-suite/src/async-subagents/agents/browser-qa.md +0 -598
- package/external/pi-tools-suite/src/async-subagents/async-subagents.sample.jsonc +0 -54
- package/external/pi-tools-suite/src/skill-installer/index.ts +0 -333
- package/skills/playwright-cli/SKILL.md +0 -420
- package/skills/playwright-cli/references/element-attributes.md +0 -23
- package/skills/playwright-cli/references/playwright-tests.md +0 -50
- package/skills/playwright-cli/references/request-mocking.md +0 -87
- package/skills/playwright-cli/references/running-code.md +0 -241
- package/skills/playwright-cli/references/session-management.md +0 -273
- package/skills/playwright-cli/references/spec-driven-testing.md +0 -311
- package/skills/playwright-cli/references/storage-state.md +0 -290
- package/skills/playwright-cli/references/test-generation.md +0 -142
- package/skills/playwright-cli/references/tracing.md +0 -154
- package/skills/playwright-cli/references/video-recording.md +0 -147
- package/skills/spec-lite/SKILL.md +0 -140
- /package/external/pi-tools-suite/src/async-subagents/agents/{browser-qa → ui-qa/browser}/examples/qa-auth.example.jsonc +0 -0
- /package/external/pi-tools-suite/src/async-subagents/agents/{browser-qa → ui-qa/browser}/examples/qa-flow.example.jsonc +0 -0
- /package/external/pi-tools-suite/src/async-subagents/agents/{browser-qa → ui-qa/browser}/vendor/fflate.LICENSE +0 -0
- /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
|
-
}
|