vskill 1.0.16 → 1.0.19

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 (225) hide show
  1. package/README.md +27 -3
  2. package/agents.json +3 -1
  3. package/dist/agents/agents-registry.d.ts +61 -0
  4. package/dist/agents/agents-registry.js +203 -0
  5. package/dist/agents/agents-registry.js.map +1 -1
  6. package/dist/api/client.d.ts +85 -0
  7. package/dist/api/client.js +193 -24
  8. package/dist/api/client.js.map +1 -1
  9. package/dist/bin.js +0 -0
  10. package/dist/commands/add.d.ts +7 -0
  11. package/dist/commands/add.js +94 -1
  12. package/dist/commands/add.js.map +1 -1
  13. package/dist/commands/auth.d.ts +23 -0
  14. package/dist/commands/auth.js +136 -12
  15. package/dist/commands/auth.js.map +1 -1
  16. package/dist/commands/eval/serve.d.ts +2 -0
  17. package/dist/commands/eval/serve.js +126 -4
  18. package/dist/commands/eval/serve.js.map +1 -1
  19. package/dist/commands/orgs.d.ts +21 -0
  20. package/dist/commands/orgs.js +164 -0
  21. package/dist/commands/orgs.js.map +1 -0
  22. package/dist/commands/skill.js +14 -1
  23. package/dist/commands/skill.js.map +1 -1
  24. package/dist/commands/whoami.d.ts +29 -0
  25. package/dist/commands/whoami.js +119 -0
  26. package/dist/commands/whoami.js.map +1 -0
  27. package/dist/eval/anthropic-catalog.js +32 -2
  28. package/dist/eval/anthropic-catalog.js.map +1 -1
  29. package/dist/eval/batch-judge.js +1 -0
  30. package/dist/eval/batch-judge.js.map +1 -1
  31. package/dist/eval/llm.d.ts +1 -1
  32. package/dist/eval/llm.js +104 -2
  33. package/dist/eval/llm.js.map +1 -1
  34. package/dist/eval-server/__tests__/helpers/studio-token-test-helpers.d.ts +2 -0
  35. package/dist/eval-server/__tests__/helpers/studio-token-test-helpers.js +20 -0
  36. package/dist/eval-server/__tests__/helpers/studio-token-test-helpers.js.map +1 -0
  37. package/dist/eval-server/active-root-store.d.ts +19 -0
  38. package/dist/eval-server/active-root-store.js +50 -0
  39. package/dist/eval-server/active-root-store.js.map +1 -0
  40. package/dist/eval-server/active-tenant-routes.d.ts +15 -0
  41. package/dist/eval-server/active-tenant-routes.js +101 -0
  42. package/dist/eval-server/active-tenant-routes.js.map +1 -0
  43. package/dist/eval-server/api-routes.d.ts +1 -1
  44. package/dist/eval-server/api-routes.js +60 -7
  45. package/dist/eval-server/api-routes.js.map +1 -1
  46. package/dist/eval-server/authoring-routes.d.ts +1 -1
  47. package/dist/eval-server/authoring-routes.js +9 -7
  48. package/dist/eval-server/authoring-routes.js.map +1 -1
  49. package/dist/eval-server/desktop-open-routes.d.ts +8 -0
  50. package/dist/eval-server/desktop-open-routes.js +64 -0
  51. package/dist/eval-server/desktop-open-routes.js.map +1 -0
  52. package/dist/eval-server/detect-engines-route.d.ts +1 -1
  53. package/dist/eval-server/detect-engines-route.js +3 -1
  54. package/dist/eval-server/detect-engines-route.js.map +1 -1
  55. package/dist/eval-server/eval-server.js +108 -29
  56. package/dist/eval-server/eval-server.js.map +1 -1
  57. package/dist/eval-server/export-skill-routes.d.ts +9 -0
  58. package/dist/eval-server/export-skill-routes.js +81 -0
  59. package/dist/eval-server/export-skill-routes.js.map +1 -0
  60. package/dist/eval-server/git-routes.d.ts +7 -6
  61. package/dist/eval-server/git-routes.js +123 -15
  62. package/dist/eval-server/git-routes.js.map +1 -1
  63. package/dist/eval-server/improve-routes.d.ts +1 -1
  64. package/dist/eval-server/improve-routes.js +4 -1
  65. package/dist/eval-server/improve-routes.js.map +1 -1
  66. package/dist/eval-server/install-engine-routes.d.ts +4 -17
  67. package/dist/eval-server/install-engine-routes.js +10 -125
  68. package/dist/eval-server/install-engine-routes.js.map +1 -1
  69. package/dist/eval-server/install-jobs.d.ts +41 -0
  70. package/dist/eval-server/install-jobs.js +161 -0
  71. package/dist/eval-server/install-jobs.js.map +1 -0
  72. package/dist/eval-server/install-skill-routes.d.ts +74 -11
  73. package/dist/eval-server/install-skill-routes.js +508 -79
  74. package/dist/eval-server/install-skill-routes.js.map +1 -1
  75. package/dist/eval-server/install-state-routes.d.ts +5 -1
  76. package/dist/eval-server/install-state-routes.js +18 -2
  77. package/dist/eval-server/install-state-routes.js.map +1 -1
  78. package/dist/eval-server/integration-routes.d.ts +1 -1
  79. package/dist/eval-server/integration-routes.js +6 -1
  80. package/dist/eval-server/integration-routes.js.map +1 -1
  81. package/dist/eval-server/model-compare-routes.d.ts +1 -1
  82. package/dist/eval-server/model-compare-routes.js +3 -1
  83. package/dist/eval-server/model-compare-routes.js.map +1 -1
  84. package/dist/eval-server/oauth-github-routes.d.ts +2 -0
  85. package/dist/eval-server/oauth-github-routes.js +505 -0
  86. package/dist/eval-server/oauth-github-routes.js.map +1 -0
  87. package/dist/eval-server/platform-proxy.d.ts +22 -1
  88. package/dist/eval-server/platform-proxy.js +183 -22
  89. package/dist/eval-server/platform-proxy.js.map +1 -1
  90. package/dist/eval-server/plugin-cli-routes.d.ts +1 -1
  91. package/dist/eval-server/plugin-cli-routes.js +19 -10
  92. package/dist/eval-server/plugin-cli-routes.js.map +1 -1
  93. package/dist/eval-server/remove-skill-routes.d.ts +18 -0
  94. package/dist/eval-server/remove-skill-routes.js +147 -0
  95. package/dist/eval-server/remove-skill-routes.js.map +1 -0
  96. package/dist/eval-server/router.d.ts +17 -3
  97. package/dist/eval-server/router.js +166 -9
  98. package/dist/eval-server/router.js.map +1 -1
  99. package/dist/eval-server/settings-store.js +1 -1
  100. package/dist/eval-server/settings-store.js.map +1 -1
  101. package/dist/eval-server/skill-create-routes.d.ts +1 -1
  102. package/dist/eval-server/skill-create-routes.js +8 -1
  103. package/dist/eval-server/skill-create-routes.js.map +1 -1
  104. package/dist/eval-server/supported-agents-routes.d.ts +6 -0
  105. package/dist/eval-server/supported-agents-routes.js +41 -0
  106. package/dist/eval-server/supported-agents-routes.js.map +1 -0
  107. package/dist/eval-server/sweep-routes.d.ts +1 -1
  108. package/dist/eval-server/sweep-routes.js +5 -1
  109. package/dist/eval-server/sweep-routes.js.map +1 -1
  110. package/dist/eval-server/utils/spawn-env.d.ts +1 -0
  111. package/dist/eval-server/utils/spawn-env.js +47 -0
  112. package/dist/eval-server/utils/spawn-env.js.map +1 -0
  113. package/dist/eval-server/workspace-routes.d.ts +12 -0
  114. package/dist/eval-server/workspace-routes.js +57 -2
  115. package/dist/eval-server/workspace-routes.js.map +1 -1
  116. package/dist/eval-ui/assets/AdvancedTab-DOgbx7u0.js +1 -0
  117. package/dist/eval-ui/assets/{CreateSkillPage-CvdYq8Rr.js → CreateSkillPage-Cv93Croj.js} +3 -3
  118. package/dist/eval-ui/assets/FindSkillsPalette-BY9DAhHh.js +2 -0
  119. package/dist/eval-ui/assets/GeneralTab-AwK9sIkP.js +1 -0
  120. package/dist/eval-ui/assets/PrivacyTab-BtNrxpVV.js +1 -0
  121. package/dist/eval-ui/assets/{SearchPaletteCore-Bf3PBC64.js → SearchPaletteCore-DMVcq7UB.js} +2 -2
  122. package/dist/eval-ui/assets/SkillDetailPanel-B_lbhK6q.js +1 -0
  123. package/dist/eval-ui/assets/UpdateDropdown-4AbjZLpq.js +1 -0
  124. package/dist/eval-ui/assets/UpdatesTab-DTmo-vVb.js +1 -0
  125. package/dist/eval-ui/assets/core-DZjBCfjp.js +1 -0
  126. package/dist/eval-ui/assets/event-QtOCMXAv.js +1 -0
  127. package/dist/eval-ui/assets/globals-Dpf9KmYH.css +1 -0
  128. package/dist/eval-ui/assets/globals-hm1COkXX.js +49 -0
  129. package/dist/eval-ui/assets/index-CUEYzTVL.js +1 -0
  130. package/dist/eval-ui/assets/index-DDNzcrhv.js +1 -0
  131. package/dist/eval-ui/assets/index-DhhmQddr.js +1 -0
  132. package/dist/eval-ui/assets/lifecycle-d1Sm9Hts.css +1 -0
  133. package/dist/eval-ui/assets/lifecycle-o_IRibOa.js +1 -0
  134. package/dist/eval-ui/assets/main-tpOyw9SC.js +87 -0
  135. package/dist/eval-ui/assets/preferences-BHZXB5dL.css +1 -0
  136. package/dist/eval-ui/assets/preferences-DCdw0Kvu.js +2 -0
  137. package/dist/eval-ui/assets/useDesktopBridge-9oZFQsrw.js +2 -0
  138. package/dist/eval-ui/index.html +4 -2
  139. package/dist/eval-ui/lifecycle.html +33 -0
  140. package/dist/eval-ui/preferences.html +34 -0
  141. package/dist/index.js +47 -1
  142. package/dist/index.js.map +1 -1
  143. package/dist/installer/bundle-files.d.ts +4 -0
  144. package/dist/installer/bundle-files.js +97 -0
  145. package/dist/installer/bundle-files.js.map +1 -0
  146. package/dist/installer/canonical.d.ts +31 -6
  147. package/dist/installer/canonical.js +48 -12
  148. package/dist/installer/canonical.js.map +1 -1
  149. package/dist/installer/clipboard-export.d.ts +19 -0
  150. package/dist/installer/clipboard-export.js +88 -0
  151. package/dist/installer/clipboard-export.js.map +1 -0
  152. package/dist/installer/multi-install.d.ts +43 -0
  153. package/dist/installer/multi-install.js +237 -0
  154. package/dist/installer/multi-install.js.map +1 -0
  155. package/dist/installer/transformers/aider.d.ts +2 -0
  156. package/dist/installer/transformers/aider.js +32 -0
  157. package/dist/installer/transformers/aider.js.map +1 -0
  158. package/dist/installer/transformers/continue-dev.d.ts +2 -0
  159. package/dist/installer/transformers/continue-dev.js +6 -0
  160. package/dist/installer/transformers/continue-dev.js.map +1 -0
  161. package/dist/installer/transformers/cursor.d.ts +2 -0
  162. package/dist/installer/transformers/cursor.js +24 -0
  163. package/dist/installer/transformers/cursor.js.map +1 -0
  164. package/dist/installer/transformers/github-copilot.d.ts +2 -0
  165. package/dist/installer/transformers/github-copilot.js +17 -0
  166. package/dist/installer/transformers/github-copilot.js.map +1 -0
  167. package/dist/installer/transformers/index.d.ts +78 -0
  168. package/dist/installer/transformers/index.js +13 -0
  169. package/dist/installer/transformers/index.js.map +1 -0
  170. package/dist/installer/transformers/junie.d.ts +2 -0
  171. package/dist/installer/transformers/junie.js +6 -0
  172. package/dist/installer/transformers/junie.js.map +1 -0
  173. package/dist/installer/transformers/kiro.d.ts +2 -0
  174. package/dist/installer/transformers/kiro.js +6 -0
  175. package/dist/installer/transformers/kiro.js.map +1 -0
  176. package/dist/installer/transformers/trae.d.ts +2 -0
  177. package/dist/installer/transformers/trae.js +6 -0
  178. package/dist/installer/transformers/trae.js.map +1 -0
  179. package/dist/installer/transformers/windsurf.d.ts +2 -0
  180. package/dist/installer/transformers/windsurf.js +12 -0
  181. package/dist/installer/transformers/windsurf.js.map +1 -0
  182. package/dist/installer/yaml-safe-mutate.d.ts +19 -0
  183. package/dist/installer/yaml-safe-mutate.js +184 -0
  184. package/dist/installer/yaml-safe-mutate.js.map +1 -0
  185. package/dist/lib/active-tenant.d.ts +36 -0
  186. package/dist/lib/active-tenant.js +120 -0
  187. package/dist/lib/active-tenant.js.map +1 -0
  188. package/dist/lib/keychain.d.ts +15 -2
  189. package/dist/lib/keychain.js +173 -8
  190. package/dist/lib/keychain.js.map +1 -1
  191. package/dist/lib/migration/keychain-migration.d.ts +35 -0
  192. package/dist/lib/migration/keychain-migration.js +189 -0
  193. package/dist/lib/migration/keychain-migration.js.map +1 -0
  194. package/dist/lib/tenant-resolver.d.ts +38 -0
  195. package/dist/lib/tenant-resolver.js +79 -0
  196. package/dist/lib/tenant-resolver.js.map +1 -0
  197. package/dist/studio/lib/ops-log.js +140 -57
  198. package/dist/studio/lib/ops-log.js.map +1 -1
  199. package/dist/studio/lib/scope-transfer.d.ts +11 -1
  200. package/dist/studio/lib/scope-transfer.js +48 -24
  201. package/dist/studio/lib/scope-transfer.js.map +1 -1
  202. package/dist/studio/routes/index.d.ts +1 -1
  203. package/dist/studio/routes/index.js +18 -8
  204. package/dist/studio/routes/index.js.map +1 -1
  205. package/dist/studio/routes/ops.js +31 -7
  206. package/dist/studio/routes/ops.js.map +1 -1
  207. package/dist/studio/routes/promote.d.ts +1 -1
  208. package/dist/studio/routes/promote.js +18 -9
  209. package/dist/studio/routes/promote.js.map +1 -1
  210. package/dist/studio/routes/revert.d.ts +1 -1
  211. package/dist/studio/routes/revert.js +15 -2
  212. package/dist/studio/routes/revert.js.map +1 -1
  213. package/dist/studio/routes/test-install.d.ts +1 -1
  214. package/dist/studio/routes/test-install.js +16 -9
  215. package/dist/studio/routes/test-install.js.map +1 -1
  216. package/dist/studio-runtime/lockfile.d.ts +51 -0
  217. package/dist/studio-runtime/lockfile.js +216 -0
  218. package/dist/studio-runtime/lockfile.js.map +1 -0
  219. package/package.json +18 -1
  220. package/dist/eval-ui/assets/FindSkillsPalette-DsSgotS9.js +0 -2
  221. package/dist/eval-ui/assets/SkillDetailPanel-DAD2yJO-.js +0 -1
  222. package/dist/eval-ui/assets/UpdateDropdown-h5Hg3h7Z.js +0 -1
  223. package/dist/eval-ui/assets/index-CKLqBL52.css +0 -1
  224. package/dist/eval-ui/assets/index-JaDg6FlU.js +0 -124
  225. package/dist/eval-ui/assets/skill-studio-logo-CRyKgIrg.png +0 -0
@@ -5,6 +5,14 @@
5
5
  // AC-US3-03/04 (provenance-gated delete — missing .vskill-meta.json → 400
6
6
  // {code:"no-provenance"}, no fs change; the original promote
7
7
  // op in ops-log is preserved — a new revert op is appended)
8
+ //
9
+ // Semantics: revert is mechanically an OWN-only delete. The SSE `destPath` /
10
+ // op-log `paths.dest` carry `provenance.sourcePath` as the LOGICAL target
11
+ // (where the skill should reappear once OWN is removed and the underlying
12
+ // installed/global scope becomes visible again). If that source has been
13
+ // uninstalled or modified between promote and revert, the user ends up with
14
+ // no skill at all — we record `sourceExists` in the op-log details so an
15
+ // audit can detect this case post-hoc.
8
16
  // ---------------------------------------------------------------------------
9
17
  import { randomUUID } from "node:crypto";
10
18
  import { homedir } from "node:os";
@@ -14,8 +22,10 @@ import { initSSE, sendSSE, sendSSEDone } from "../../eval-server/sse-helpers.js"
14
22
  import { countFiles, resolveScopePath } from "../lib/scope-transfer.js";
15
23
  import { readProvenance } from "../lib/provenance.js";
16
24
  import { appendOp } from "../lib/ops-log.js";
17
- export function registerRevertRoute(router, root, home = homedir()) {
25
+ export function registerRevertRoute(router, rootArg, home = homedir()) {
26
+ const getRoot = typeof rootArg === "function" ? rootArg : () => rootArg;
18
27
  router.post("/api/skills/:plugin/:skill/revert", async (req, res, params) => {
28
+ const root = getRoot();
19
29
  const { plugin, skill } = params;
20
30
  const ownDir = resolveScopePath("own", root, skill, home);
21
31
  const opId = randomUUID();
@@ -39,6 +49,9 @@ export function registerRevertRoute(router, root, home = homedir()) {
39
49
  // Capture target scope for logging BEFORE the delete.
40
50
  const toScope = provenance.promotedFrom;
41
51
  const filesDeleted = countFiles(ownDir);
52
+ // Record whether the logical reveal target still exists on disk.
53
+ // False = revert leaves the user with no copy of the skill anywhere.
54
+ const sourceExists = existsSync(provenance.sourcePath);
42
55
  emit({
43
56
  type: "started",
44
57
  opId,
@@ -62,7 +75,7 @@ export function registerRevertRoute(router, root, home = homedir()) {
62
75
  toScope,
63
76
  paths: { source: ownDir, dest: provenance.sourcePath },
64
77
  actor: "studio-ui",
65
- details: { filesDeleted },
78
+ details: { filesDeleted, sourceExists },
66
79
  };
67
80
  await appendOp(op);
68
81
  sendSSEDone(res, { type: "done", opId, destPath: ownDir });
@@ -1 +1 @@
1
- {"version":3,"file":"revert.js","sourceRoot":"","sources":["../../../src/studio/routes/revert.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"revert.js","sourceRoot":"","sources":["../../../src/studio/routes/revert.ts"],"names":[],"mappings":"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"}
@@ -1,2 +1,2 @@
1
1
  import type { Router } from "../../eval-server/router.js";
2
- export declare function registerTestInstallRoute(router: Router, root: string, home?: string): void;
2
+ export declare function registerTestInstallRoute(router: Router, rootArg: string | (() => string), home?: string): void;
@@ -9,14 +9,15 @@
9
9
  // ---------------------------------------------------------------------------
10
10
  import { randomUUID } from "node:crypto";
11
11
  import { homedir } from "node:os";
12
- import { existsSync } from "node:fs";
13
12
  import { sendJson } from "../../eval-server/router.js";
14
13
  import { initSSE, sendSSE, sendSSEDone } from "../../eval-server/sse-helpers.js";
15
- import { transfer, CollisionError, MissingSourceError, resolveScopePath, } from "../lib/scope-transfer.js";
14
+ import { transfer, validatePaths, CollisionError, MissingSourceError, resolveScopePath, } from "../lib/scope-transfer.js";
16
15
  import { appendOp } from "../lib/ops-log.js";
17
16
  import { parseQuery } from "../lib/query.js";
18
- export function registerTestInstallRoute(router, root, home = homedir()) {
17
+ export function registerTestInstallRoute(router, rootArg, home = homedir()) {
18
+ const getRoot = typeof rootArg === "function" ? rootArg : () => rootArg;
19
19
  router.post("/api/skills/:plugin/:skill/test-install", async (req, res, params) => {
20
+ const root = getRoot();
20
21
  const { plugin, skill } = params;
21
22
  const query = parseQuery(req.url);
22
23
  const overwrite = query.get("overwrite") === "true";
@@ -26,13 +27,19 @@ export function registerTestInstallRoute(router, root, home = homedir()) {
26
27
  const destPath = resolveScopePath(toScope, root, skill, home);
27
28
  const opId = randomUUID();
28
29
  // Pre-validate so collisions / missing-source return clean HTTP codes.
29
- if (!existsSync(sourcePath)) {
30
- sendJson(res, { ok: false, code: "missing-source", path: sourcePath }, 404, req);
31
- return;
30
+ try {
31
+ validatePaths(sourcePath, destPath, overwrite);
32
32
  }
33
- if (existsSync(destPath) && !overwrite) {
34
- sendJson(res, { ok: false, code: "collision", path: destPath }, 409, req);
35
- return;
33
+ catch (err) {
34
+ if (err instanceof MissingSourceError) {
35
+ sendJson(res, { ok: false, code: "missing-source", path: err.path }, 404, req);
36
+ return;
37
+ }
38
+ if (err instanceof CollisionError) {
39
+ sendJson(res, { ok: false, code: "collision", path: err.path }, 409, req);
40
+ return;
41
+ }
42
+ throw err;
36
43
  }
37
44
  const emit = (e) => {
38
45
  if (!res.headersSent)
@@ -1 +1 @@
1
- {"version":3,"file":"test-install.js","sourceRoot":"","sources":["../../../src/studio/routes/test-install.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"test-install.js","sourceRoot":"","sources":["../../../src/studio/routes/test-install.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,51 @@
1
+ export type StudioLockSource = "npx-cli" | "node-direct" | "tauri";
2
+ export interface StudioLock {
3
+ /** OS PID of the process holding the lock. */
4
+ pid: number;
5
+ /** TCP port the studio runtime is bound to. */
6
+ port: number;
7
+ /** Truncated argv joined into a single line (≤120 chars). */
8
+ cmdline: string;
9
+ /** ISO-8601 UTC timestamp at which the runtime announced its bind. */
10
+ startedAt: string;
11
+ /** Optional source classification — defaults to "npx-cli" when omitted. */
12
+ source?: StudioLockSource;
13
+ }
14
+ /** Returns `~/.vskill/runtime/`. Honours `$HOME` (Unix) and `$USERPROFILE` (Windows). */
15
+ export declare function runtimeDir(): string;
16
+ /** Returns the lock-file path for a given port. */
17
+ export declare function lockfilePath(port: number): string;
18
+ /**
19
+ * Atomically write a studio lock file. Creates `~/.vskill/runtime/` if missing.
20
+ *
21
+ * Mode 0600 on Unix (parent dir 0700). Windows: stock fs perms — Tauri scanner
22
+ * does the cross-platform PID liveness check anyway.
23
+ *
24
+ * 0832 F-007: if the target lock already exists and is owned by a LIVE
25
+ * process (PID alive AND pid != ours), refuse to overwrite. This avoids the
26
+ * TOCTOU race where two CLIs racing for the same port silently clobber each
27
+ * other's lock content. Stale locks (owner dead) are replaced normally.
28
+ */
29
+ export declare function writeLock(lock: StudioLock): string;
30
+ /** Read a lock file by port. Returns `null` if missing or unparseable. */
31
+ export declare function readLock(port: number): StudioLock | null;
32
+ /** Remove the lock file for `port`. Swallows ENOENT (idempotent). */
33
+ export declare function removeLock(port: number): void;
34
+ /** List every `studio-*.lock` in the runtime dir. Returns parsed locks (skips invalid). */
35
+ export declare function listLocks(): Array<StudioLock & {
36
+ _path: string;
37
+ }>;
38
+ /**
39
+ * Check if `pid` is a live process. Uses `process.kill(pid, 0)` which throws
40
+ * `ESRCH` for nonexistent processes and `EPERM` for live-but-not-ours (still
41
+ * counts as alive). Returns `false` only when ESRCH.
42
+ */
43
+ export declare function isPidAlive(pid: number): boolean;
44
+ /**
45
+ * Prune lock files that no longer correspond to a live process or are >1d old.
46
+ * Returns the list of remaining (live) locks.
47
+ */
48
+ export declare function pruneStaleLocks(now?: Date): Array<StudioLock & {
49
+ _path: string;
50
+ }>;
51
+ export declare function registerCleanup(port: number): void;
@@ -0,0 +1,216 @@
1
+ // ---------------------------------------------------------------------------
2
+ // 0832 T-001: Studio runtime lock files at ~/.vskill/runtime/studio-{port}.lock
3
+ //
4
+ // Written by `vskill studio` when it binds a port; consumed by the Tauri-side
5
+ // `process_discovery` scanner as a fast path for enumerating running studio
6
+ // instances. Format is a small JSON record:
7
+ //
8
+ // { pid, port, cmdline, startedAt }
9
+ //
10
+ // Atomic write strategy: tmp-file + fsync + rename. Cleanup on SIGTERM/SIGINT
11
+ // is registered idempotently so two signals don't double-unlink (the second
12
+ // unlink turns into ENOENT, which we swallow).
13
+ // ---------------------------------------------------------------------------
14
+ import * as fs from "node:fs";
15
+ import * as os from "node:os";
16
+ import * as path from "node:path";
17
+ /** Returns `~/.vskill/runtime/`. Honours `$HOME` (Unix) and `$USERPROFILE` (Windows). */
18
+ export function runtimeDir() {
19
+ return path.join(os.homedir(), ".vskill", "runtime");
20
+ }
21
+ /** Returns the lock-file path for a given port. */
22
+ export function lockfilePath(port) {
23
+ return path.join(runtimeDir(), `studio-${port}.lock`);
24
+ }
25
+ /**
26
+ * Atomically write a studio lock file. Creates `~/.vskill/runtime/` if missing.
27
+ *
28
+ * Mode 0600 on Unix (parent dir 0700). Windows: stock fs perms — Tauri scanner
29
+ * does the cross-platform PID liveness check anyway.
30
+ *
31
+ * 0832 F-007: if the target lock already exists and is owned by a LIVE
32
+ * process (PID alive AND pid != ours), refuse to overwrite. This avoids the
33
+ * TOCTOU race where two CLIs racing for the same port silently clobber each
34
+ * other's lock content. Stale locks (owner dead) are replaced normally.
35
+ */
36
+ export function writeLock(lock) {
37
+ const dir = runtimeDir();
38
+ fs.mkdirSync(dir, { recursive: true });
39
+ if (process.platform !== "win32") {
40
+ try {
41
+ fs.chmodSync(dir, 0o700);
42
+ }
43
+ catch {
44
+ /* best-effort */
45
+ }
46
+ }
47
+ const target = lockfilePath(lock.port);
48
+ // 0832 F-007: refuse to clobber a live foreign lock. Stale locks (owner PID
49
+ // is dead) are eligible for replacement — that's the normal restart path.
50
+ const existing = readLock(lock.port);
51
+ if (existing && existing.pid !== lock.pid && isPidAlive(existing.pid)) {
52
+ throw new Error(`lockfile: port ${lock.port} is held by live pid ${existing.pid}`);
53
+ }
54
+ const tmp = `${target}.tmp.${process.pid}`;
55
+ const payload = JSON.stringify({
56
+ pid: lock.pid,
57
+ port: lock.port,
58
+ cmdline: truncateCmdline(lock.cmdline),
59
+ startedAt: lock.startedAt,
60
+ source: lock.source ?? "npx-cli",
61
+ });
62
+ fs.writeFileSync(tmp, payload, { mode: 0o600 });
63
+ if (process.platform !== "win32") {
64
+ try {
65
+ fs.chmodSync(tmp, 0o600);
66
+ }
67
+ catch {
68
+ /* best-effort */
69
+ }
70
+ }
71
+ fs.renameSync(tmp, target);
72
+ return target;
73
+ }
74
+ /** Read a lock file by port. Returns `null` if missing or unparseable. */
75
+ export function readLock(port) {
76
+ const target = lockfilePath(port);
77
+ let raw;
78
+ try {
79
+ raw = fs.readFileSync(target, "utf8");
80
+ }
81
+ catch {
82
+ return null;
83
+ }
84
+ try {
85
+ const obj = JSON.parse(raw);
86
+ if (typeof obj.pid !== "number" ||
87
+ typeof obj.port !== "number" ||
88
+ typeof obj.startedAt !== "string") {
89
+ return null;
90
+ }
91
+ return {
92
+ pid: obj.pid,
93
+ port: obj.port,
94
+ cmdline: typeof obj.cmdline === "string" ? obj.cmdline : "",
95
+ startedAt: obj.startedAt,
96
+ source: obj.source === "tauri" || obj.source === "node-direct" ? obj.source : "npx-cli",
97
+ };
98
+ }
99
+ catch {
100
+ return null;
101
+ }
102
+ }
103
+ /** Remove the lock file for `port`. Swallows ENOENT (idempotent). */
104
+ export function removeLock(port) {
105
+ const target = lockfilePath(port);
106
+ try {
107
+ fs.unlinkSync(target);
108
+ }
109
+ catch (e) {
110
+ if (e?.code !== "ENOENT") {
111
+ // Anything other than "already gone" is worth a warning.
112
+ // We don't throw — caller is in a shutdown path, can't recover.
113
+ // eslint-disable-next-line no-console
114
+ console.warn(`[lockfile] could not remove ${target}: ${e.message}`);
115
+ }
116
+ }
117
+ }
118
+ /** List every `studio-*.lock` in the runtime dir. Returns parsed locks (skips invalid). */
119
+ export function listLocks() {
120
+ const dir = runtimeDir();
121
+ let entries;
122
+ try {
123
+ entries = fs.readdirSync(dir);
124
+ }
125
+ catch {
126
+ return [];
127
+ }
128
+ const out = [];
129
+ for (const name of entries) {
130
+ if (!name.startsWith("studio-") || !name.endsWith(".lock"))
131
+ continue;
132
+ const portStr = name.slice("studio-".length, name.length - ".lock".length);
133
+ const port = Number.parseInt(portStr, 10);
134
+ if (!Number.isFinite(port))
135
+ continue;
136
+ const lock = readLock(port);
137
+ if (lock) {
138
+ out.push({ ...lock, _path: path.join(dir, name) });
139
+ }
140
+ }
141
+ return out;
142
+ }
143
+ /**
144
+ * Check if `pid` is a live process. Uses `process.kill(pid, 0)` which throws
145
+ * `ESRCH` for nonexistent processes and `EPERM` for live-but-not-ours (still
146
+ * counts as alive). Returns `false` only when ESRCH.
147
+ */
148
+ export function isPidAlive(pid) {
149
+ try {
150
+ process.kill(pid, 0);
151
+ return true;
152
+ }
153
+ catch (e) {
154
+ const code = e?.code;
155
+ if (code === "EPERM")
156
+ return true;
157
+ return false;
158
+ }
159
+ }
160
+ /**
161
+ * Prune lock files that no longer correspond to a live process or are >1d old.
162
+ * Returns the list of remaining (live) locks.
163
+ */
164
+ export function pruneStaleLocks(now = new Date()) {
165
+ const live = [];
166
+ const oneDayMs = 24 * 60 * 60 * 1000;
167
+ for (const lock of listLocks()) {
168
+ const startedAt = Date.parse(lock.startedAt);
169
+ const ageMs = Number.isFinite(startedAt) ? now.getTime() - startedAt : 0;
170
+ const tooOld = ageMs > oneDayMs;
171
+ if (!isPidAlive(lock.pid) || tooOld) {
172
+ try {
173
+ fs.unlinkSync(lock._path);
174
+ }
175
+ catch {
176
+ /* best-effort */
177
+ }
178
+ continue;
179
+ }
180
+ live.push(lock);
181
+ }
182
+ return live;
183
+ }
184
+ /**
185
+ * Register a one-shot cleanup that removes our lock file on SIGINT/SIGTERM/exit.
186
+ * Idempotent across multiple calls — repeated registrations are no-ops.
187
+ */
188
+ let _registeredCleanupForPort = null;
189
+ export function registerCleanup(port) {
190
+ if (_registeredCleanupForPort === port)
191
+ return;
192
+ _registeredCleanupForPort = port;
193
+ let cleaned = false;
194
+ const cleanup = () => {
195
+ if (cleaned)
196
+ return;
197
+ cleaned = true;
198
+ removeLock(port);
199
+ };
200
+ // Synchronous-on-exit cleanup. Node fires this last; safe spot to unlink.
201
+ process.on("exit", cleanup);
202
+ // Signal handlers — we DO NOT exit ourselves; the existing studio shutdown
203
+ // path owns the exit. We just remove the lock first so a subsequent scan
204
+ // sees the right state immediately.
205
+ for (const sig of ["SIGINT", "SIGTERM", "SIGHUP"]) {
206
+ process.on(sig, () => {
207
+ cleanup();
208
+ });
209
+ }
210
+ }
211
+ function truncateCmdline(cmd) {
212
+ if (cmd.length <= 120)
213
+ return cmd;
214
+ return cmd.slice(0, 117) + "...";
215
+ }
216
+ //# sourceMappingURL=lockfile.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lockfile.js","sourceRoot":"","sources":["../../src/studio-runtime/lockfile.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vskill",
3
- "version": "1.0.16",
3
+ "version": "1.0.19",
4
4
  "type": "module",
5
5
  "description": "Secure multi-platform AI skill installer — scan before you install",
6
6
  "bin": {
@@ -16,7 +16,10 @@
16
16
  "dev": "tsc --watch",
17
17
  "dev:eval-ui": "vite dev --config src/eval-ui/vite.config.ts",
18
18
  "test": "vitest run",
19
+ "verify": "node test/verify/run-verify.mjs",
20
+ "verify:matrix": "node --test test/verify/matrix.test.mjs",
19
21
  "test:e2e": "playwright test",
22
+ "test:e2e:desktop": "playwright test --project=desktop",
20
23
  "test:bundle-size": "npx tsx scripts/check-bundle-size.ts",
21
24
  "test:lhci": "lhci autorun --config=./lighthouse.config.js",
22
25
  "lint:shimmer": "npx tsx scripts/check-no-shimmer.ts",
@@ -25,6 +28,10 @@
25
28
  "lint:bundle-size": "npx tsx scripts/check-bundle-size.ts",
26
29
  "lint:skills-spec": "npx tsx scripts/validate-skills-spec.ts",
27
30
  "clean": "rm -rf dist",
31
+ "tauri": "tauri",
32
+ "desktop:dev": "tauri dev",
33
+ "desktop:build": "tauri build",
34
+ "desktop:sidecar:build": "bash scripts/desktop/build-sidecar.sh",
28
35
  "preuninstall": "node scripts/preuninstall.cjs",
29
36
  "sync:readme-badges": "node scripts/sync-readme-badges.cjs",
30
37
  "prepublishOnly": "npm run build && npm run build:eval-ui && node scripts/sync-readme-badges.cjs && git diff --exit-code README.md"
@@ -62,6 +69,7 @@
62
69
  "@lhci/cli": "^0.15.1",
63
70
  "@playwright/test": "^1.58.2",
64
71
  "@tailwindcss/vite": "~4.2.1",
72
+ "@tauri-apps/cli": "^2",
65
73
  "@types/node": "^25.2.3",
66
74
  "@types/react": "^19.2.14",
67
75
  "@types/react-dom": "^19.2.3",
@@ -84,9 +92,18 @@
84
92
  "dependencies": {
85
93
  "@anthropic-ai/sdk": "^0.78.0",
86
94
  "@napi-rs/keyring": "^1.3.0",
95
+ "@tauri-apps/api": "^2.11.0",
96
+ "@tauri-apps/plugin-autostart": "^2.5.1",
97
+ "@tauri-apps/plugin-dialog": "^2.7.1",
98
+ "@tauri-apps/plugin-notification": "^2.3.3",
99
+ "@tauri-apps/plugin-process": "^2.3.1",
100
+ "@tauri-apps/plugin-shell": "^2.3.5",
101
+ "@tauri-apps/plugin-updater": "^2.10.1",
87
102
  "commander": "^14.0.3",
103
+ "i18next": "^23.16.8",
88
104
  "minimatch": "^9.0.9",
89
105
  "openai": "^6.34.0",
106
+ "react-i18next": "^14.1.3",
90
107
  "simple-git": "~3.33.0",
91
108
  "trash": "^9.0.0"
92
109
  }
@@ -1,2 +0,0 @@
1
- const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/SearchPaletteCore-Bf3PBC64.js","assets/index-JaDg6FlU.js","assets/index-CKLqBL52.css","assets/fonts-i7Lkz2zN.css","assets/skill-url-C4ekwoGs.js"])))=>i.map(i=>d[i]);
2
- import{r as t,j as d,_ as l}from"./index-JaDg6FlU.js";/* empty css */const f=t.lazy(()=>l(()=>import("./SearchPaletteCore-Bf3PBC64.js"),__vite__mapDeps([0,1,2,3,4])));function w(){if(typeof window>"u"||typeof window.matchMedia!="function")return!1;try{return window.matchMedia("(prefers-reduced-motion: reduce)").matches}catch{return!1}}function y({onSelect:i,onNavigate:u}={}){const[n,o]=t.useState(!1),s=t.useRef(null),a=w();t.useEffect(()=>{function e(){s.current=document.activeElement??null,o(!0)}return window.addEventListener("openFindSkills",e),()=>window.removeEventListener("openFindSkills",e)},[]),t.useEffect(()=>{if(!n)return;function e(r){r.key==="Escape"&&o(!1)}return window.addEventListener("keydown",e),()=>window.removeEventListener("keydown",e)},[n]),t.useEffect(()=>{if(n)return;const e=s.current;if(e&&typeof e.focus=="function")try{e.focus()}catch{}s.current=null},[n]);const c=t.useCallback((e,r)=>{try{typeof window<"u"&&window.sessionStorage&&window.sessionStorage.setItem("find-skills:last-query",r??"")}catch{}if(o(!1),i)try{i(e,r)}catch{}},[i]);return n?d.jsx("div",{"data-testid":"find-skills-palette-shell","data-reduced-motion":a?"true":"false",children:d.jsx(t.Suspense,{fallback:null,children:d.jsx(f,{initialOpen:!0,onSelect:c,onNavigate:u})})}):null}export{y as FindSkillsPalette,y as default};
@@ -1 +0,0 @@
1
- import{j as t,r as d,a as G,T as ae}from"./index-JaDg6FlU.js";import{s as V}from"./skill-url-C4ekwoGs.js";/* empty css */const X={T0:{label:"BLOCKED",cssVar:"var(--trust-t0)"},T1:{label:"UNSCANNED",cssVar:"var(--trust-t1)"},T2:{label:"BASIC",cssVar:"var(--trust-t2)"},T3:{label:"VERIFIED",cssVar:"var(--trust-t3)"},T4:{label:"CERTIFIED",cssVar:"var(--trust-t4)"}};function ce({tier:r}){const l=X[r]??X.T1;return t.jsxs("span",{"data-testid":"trust-badge","data-tier":r,style:{display:"inline-flex",alignItems:"center",gap:"0.3rem",padding:"0.15rem 0.5rem",borderRadius:"4px",fontFamily:"var(--font-geist-mono)",fontSize:"0.6875rem",fontWeight:600,letterSpacing:"0.03em",textTransform:"uppercase",whiteSpace:"nowrap",color:l.cssVar,backgroundColor:`color-mix(in srgb, ${l.cssVar} 10%, transparent)`,border:`1px solid color-mix(in srgb, ${l.cssVar} 25%, transparent)`,lineHeight:1,height:"22px"},children:[r," ",l.label]})}const de=/^https?:\/\/github\.com\/([a-zA-Z0-9_.-]+)\/([a-zA-Z0-9_.-]+?)(\.git)?(\/.*)?$/;function ue(r){var h,E;const c=r.trim().match(de);if(!c)return null;const u=(h=c[1])==null?void 0:h.toLowerCase();let s=(E=c[2])==null?void 0:E.toLowerCase();if(!u||!s)return null;s=s.replace(/\.git$/,"");const m=`https://github.com/${u}/${s}`;return{owner:u,name:s,url:m}}function fe({repoUrl:r,mono:l="var(--font-geist-mono)",fontSize:c="0.75rem",showPlaceholder:u=!0}){if(!r)return u?t.jsx("span",{style:{color:"var(--text-faint)",fontSize:c},children:"--"}):null;const s=ue(r);return s?t.jsxs("a",{"data-testid":"repo-link",href:s.url,target:"_blank",rel:"noopener noreferrer",onClick:m=>m.stopPropagation(),style:{color:"#0D9488",textDecoration:"none",fontSize:c,fontFamily:l},children:[s.owner,"/",s.name]}):t.jsx("span",{style:{color:"var(--text-faint)",fontSize:c,fontFamily:l},children:r})}const pe="var(--font-geist-mono)",me={ONLINE:"var(--status-success-text)",OFFLINE:"var(--status-danger-text)",STALE:"var(--status-neutral-text)"},ye={ONLINE:"var(--status-success-bg)",OFFLINE:"var(--status-danger-bg)",STALE:"var(--status-neutral-bg)"};function ge({skillName:r,repoUrl:l}){const[c,u]=d.useState(null),[s,m]=d.useState(!1);if(d.useEffect(()=>{l&&(m(!0),fetch(V(r,"repo-health")).then(w=>{if(!w.ok)throw new Error("fetch failed");return w.json()}).then(w=>{u(w.status),m(!1)}).catch(()=>{u(null),m(!1)}))},[r,l]),!l)return null;if(s)return t.jsx("span",{"data-testid":"repo-health-loading",style:{display:"inline-block",width:56,height:20,borderRadius:"4px",backgroundColor:"color-mix(in srgb, var(--text-faint) 20%, transparent)",animation:"pulse 1.5s ease-in-out infinite"}});if(!c||c==="UNKNOWN")return null;const h=me[c]??"var(--status-neutral-text)",E=ye[c]??"var(--status-neutral-bg)";return t.jsx("span",{"data-testid":"repo-health-badge",style:{fontFamily:pe,fontSize:"0.75rem",fontWeight:600,textTransform:"uppercase",letterSpacing:"0.03em",padding:"0.2rem 0.6rem",borderRadius:"999px",backgroundColor:E,color:h,whiteSpace:"nowrap"},children:c})}function he({children:r,compact:l}){return t.jsx("pre",{"data-testid":"terminal-block",style:{background:"var(--bg-code, #161B22)",color:"#E6EDF3",fontFamily:"var(--font-geist-mono)",fontSize:l?"0.8rem":"0.875rem",lineHeight:1.6,padding:l?"1rem 1.25rem":"1.5rem 2rem",borderRadius:"6px",overflowX:"auto",margin:0},children:r})}const xe="/api/v1/studio/telemetry/install-copy",ve=/^[a-zA-Z0-9._@/-]+$/,be=/^[a-zA-Z0-9._-]+$/,ke="https://verified-skill.com";function Ee(r){return`${r.owner}/${r.repo}/${r.slug}`}function je(r,l){return r!=null&&r.ownerSlug&&(r!=null&&r.repoSlug)?`${r.ownerSlug}/${r.repoSlug}`:r!=null&&r.publisher?r.publisher:`${l.owner}/${l.repo}`}function we(r){return r==="user"?" --global":` --scope ${r}`}function Se(r){return r==="user"?"User":"Project"}function $e(r,l,c,u){const s=c?`${r}/${l}@${c}`:`${r}/${l}`;if(!ve.test(`${r}/${l}`))return{ok:!1,reason:"Invalid skill identifier"};if(c&&!be.test(c))return{ok:!1,reason:"Invalid version identifier"};const m=we(u),h=`npx vskill@latest install ${s}${m}`,E=[{label:"npm",comment:"# npm",command:h},{label:"bun",comment:"# bun",command:`bunx vskill@latest install ${s}${m}`},{label:"pnpm",comment:"# pnpm",command:`pnpx vskill@latest install ${s}${m}`},{label:"yarn",comment:"# yarn",command:`yarn dlx vskill@latest install ${s}${m}`},{label:"alternative",comment:"# alternative (publisher + --skill flag)",command:c?`npx vskill@latest install ${r}@${c} --skill ${l}${m}`:`npx vskill@latest install ${r} --skill ${l}${m}`}];return{ok:!0,command:h,variants:E}}function I(r,l,c){typeof window>"u"||window.dispatchEvent(new CustomEvent("studio:toast",{detail:{message:r,kind:l,durationMs:c,severity:l==="error"?"error":"info"}}))}async function Y(r){var l;if(typeof navigator<"u"&&((l=navigator.clipboard)!=null&&l.writeText))try{return await navigator.clipboard.writeText(r),!0}catch{}if(typeof document>"u")return!1;try{const c=document.createElement("textarea");c.value=r,c.setAttribute("readonly",""),c.style.position="fixed",c.style.left="-9999px",document.body.appendChild(c),c.select();const u=document.execCommand("copy");return document.body.removeChild(c),u}catch{return!1}}function Z(r,l){try{fetch(r,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(l),keepalive:!0}).catch(()=>{})}catch{}}function Ae({selectedSkill:r,onClose:l,telemetryInstallCopyUrl:c=xe,onToast:u}){const[s,m]=d.useState(null),[h,E]=d.useState([]),[w,J]=d.useState(!1),[Q,ee]=d.useState(null),[C,R]=d.useState(!0),[D,O]=d.useState(null),[v,_]=d.useState(null),[b,te]=d.useState("project"),[k,L]=d.useState(null),[U,re]=d.useState(0),M=d.useRef(null),P=d.useRef(null),W=d.useRef(null),g=Ee(r);d.useEffect(()=>(W.current=document.activeElement??null,()=>{const e=W.current;if(e&&typeof e.focus=="function")try{e.focus()}catch{}}),[]),d.useEffect(()=>{const e=setTimeout(()=>{var n;return(n=P.current)==null?void 0:n.focus()},50);return()=>clearTimeout(e)},[]),d.useEffect(()=>{let e=!1;R(!0),O(null);const n=V(g),i=V(g,"versions");return Promise.all([fetch(n).then(async o=>{if(!o.ok)throw new Error(`metadata ${o.status}`);return o.json()}),fetch(i).then(async o=>{if(!o.ok)throw new Error(`versions ${o.status}`);const a=await o.json(),f=Array.isArray(a)?a:(a==null?void 0:a.versions)??[],p=!Array.isArray(a)&&(a==null?void 0:a.unversioned)===!0,x=!Array.isArray(a)&&typeof(a==null?void 0:a.currentVersion)=="string"?a.currentVersion:null;return{list:f,unversioned:p,currentVersion:x}})]).then(([o,a])=>{if(e)return;m(o),E(a.list),J(a.unversioned),ee(a.currentVersion);const f=a.list.find(p=>p.isLatest)??a.list[0]??null;_(f?f.version:null),R(!1)}).catch(o=>{e||(O(o instanceof Error?o.message:String(o)),R(!1))}),()=>{e=!0}},[g,U]),d.useEffect(()=>{let e=!1;return G.getSkillInstallState(g).then(n=>{e||L(n)},()=>{if(!e){try{const n="vskill:installState:warned";typeof sessionStorage<"u"&&!sessionStorage.getItem(n)&&(sessionStorage.setItem(n,"1"),console.warn("[SkillDetailPanel] install-state fetch failed; falling back to optimistic not-installed UX"))}catch{}L(null)}}),()=>{e=!0}},[g,U]),d.useEffect(()=>{if(typeof window>"u")return;let e=null;const n=i=>{const o=i.detail;!(o!=null&&o.skill)||o.skill!==g||(e&&clearTimeout(e),e=setTimeout(()=>{G.getSkillInstallState(g).then(a=>L(a),()=>{})},50))};return window.addEventListener("studio:skill-installed",n),()=>{e&&clearTimeout(e),window.removeEventListener("studio:skill-installed",n)}},[g]),d.useEffect(()=>{function e(n){if(n.key==="Escape")n.stopPropagation(),$();else if(n.key==="Tab"){const i=M.current;if(!i)return;const o=i.querySelectorAll('a[href], button:not([disabled]), input:not([disabled]), [tabindex]:not([tabindex="-1"])');if(o.length===0)return;const a=o[0],f=o[o.length-1],p=document.activeElement;n.shiftKey&&p===a?(n.preventDefault(),f.focus()):!n.shiftKey&&p===f&&(n.preventDefault(),a.focus())}}return window.addEventListener("keydown",e,!0),()=>window.removeEventListener("keydown",e,!0)},[]);const $=d.useCallback(()=>{l();let e="";try{typeof window<"u"&&window.sessionStorage&&(e=window.sessionStorage.getItem("find-skills:last-query")??"")}catch{}typeof window<"u"&&window.dispatchEvent(new CustomEvent("openFindSkills",{detail:{query:e}}))},[l]),T=je(s,r),S=(s==null?void 0:s.skillSlug)??r.slug,H=(s==null?void 0:s.displayName)??r.displayName??S,ne=(s==null?void 0:s.isBlocked)===!0,F=d.useMemo(()=>h.length===0?[]:[...h].sort((n,i)=>{const o=n.publishedAt?Date.parse(n.publishedAt):0;return(i.publishedAt?Date.parse(i.publishedAt):0)-o}).slice(0,5),[h]),B=d.useMemo(()=>!v||F.length===0?!0:F[0].version===v,[v,F]),y=d.useMemo(()=>v?$e(T,S,B?null:v,b):null,[T,S,v,B,b]),z=d.useCallback(async()=>{if(!y||!y.ok)return;const e=await Y(y.command);Z(c,{skillName:g,version:v??"",q:"",ts:Date.now()});const n=e?`Run ${y.command} in your terminal`:"Copy failed — please copy the command manually.";if(u)try{u(n,e?"success":"error")}catch{}else I(n,e?"success":"error",3500)},[y,c,g,v,T,S,B,u]),se=d.useCallback(async()=>{if(!y||!y.ok)return;const e=`${T}/${S}`;if(u)try{u(`Installing ${e}…`,"info")}catch{}else I(`Installing ${e}…`,"info",5e3);let n=null;try{const f=await fetch("/api/studio/install-skill",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({skill:e,scope:b})});if(f.status===404)return z();if(!f.ok){const j=(await f.json().catch(()=>({}))).error||`Install failed (HTTP ${f.status})`;if(u)try{u(j,"error")}catch{}else I(j,"error",6e3);return}const p=await f.json();n=(p==null?void 0:p.jobId)??null}catch{return z()}if(!n||typeof EventSource>"u")return;const i=new EventSource(`/api/studio/install-skill/${n}/stream`),a=setTimeout(()=>{try{i.close()}catch{}},2e5);i.addEventListener("done",f=>{var A;clearTimeout(a);try{i.close()}catch{}let p={};try{p=JSON.parse(f.data)}catch{}const x=p.success===!0,j=x?`Installed ${e} (${b})`:`Install failed: ${((A=p.stderr)==null?void 0:A.trim().split(/\r?\n/).slice(-1)[0])||"see terminal"}`;if(u)try{u(j,x?"success":"error")}catch{}else I(j,x?"success":"error",x?4e3:8e3);if(x){if(typeof window<"u")try{window.dispatchEvent(new CustomEvent("studio:skill-installed",{detail:{skill:e,scope:b}}))}catch{}$()}}),i.onerror=()=>{clearTimeout(a);try{i.close()}catch{}}},[y,T,S,b,u,z,$]),oe=(s==null?void 0:s.trustTier)??"T1",ie=(s==null?void 0:s.certTier)==="CERTIFIED"||(s==null?void 0:s.certTier)==="VERIFIED"?s.certTier:"VERIFIED";return t.jsxs("div",{ref:M,"data-testid":"skill-detail-panel",role:"dialog","aria-modal":"true","aria-label":`Skill detail — ${H}`,style:{position:"fixed",inset:0,zIndex:9998,display:"flex",alignItems:"flex-start",justifyContent:"center",paddingTop:"min(10vh, 80px)"},onClick:e=>{e.target===e.currentTarget&&$()},children:[t.jsx("div",{style:{position:"fixed",inset:0,background:"rgba(0,0,0,0.4)",backdropFilter:"blur(4px)"}}),t.jsxs("div",{onClick:e=>e.stopPropagation(),style:{position:"relative",width:"100%",maxWidth:720,margin:"0 1rem",background:"var(--bg-surface, #FFFFFF)",color:"var(--text-primary, #191919)",borderRadius:"8px",border:"1px solid var(--color-rule, #E8E1D6)",boxShadow:"0 20px 60px rgba(0,0,0,0.15)",overflow:"hidden",maxHeight:"80vh",display:"flex",flexDirection:"column"},children:[t.jsxs("div",{style:{padding:"0.75rem 1rem",borderBottom:"1px solid var(--color-rule, #E8E1D6)",display:"flex",alignItems:"center",justifyContent:"space-between",flexShrink:0},children:[t.jsx("button",{ref:P,type:"button",onClick:$,"data-testid":"skill-detail-back",style:{background:"transparent",border:"none",padding:"4px 8px",borderRadius:4,cursor:"pointer",fontFamily:"var(--font-mono, monospace)",fontSize:12,color:"var(--text-secondary, #5A5651)"},children:"← Back to results"}),t.jsx("kbd",{style:{fontFamily:"var(--font-mono, monospace)",fontSize:11,color:"var(--text-secondary, #5A5651)",border:"1px solid var(--color-rule, #E8E1D6)",borderRadius:4,padding:"1px 5px"},children:"Esc"})]}),t.jsxs("div",{style:{overflowY:"auto",padding:"1rem",flex:1},children:[C&&t.jsx("div",{"data-testid":"skill-detail-loading",style:{padding:"2rem",textAlign:"center",color:"var(--text-secondary, #5A5651)"},children:"Loading…"}),D&&!C&&t.jsxs("div",{"data-testid":"skill-detail-error",style:{padding:"1.5rem",textAlign:"center",fontFamily:"var(--font-mono, monospace)",fontSize:"0.875rem",color:"var(--red, #b54444)"},children:[t.jsxs("div",{style:{marginBottom:"0.75rem"},children:["Failed to load skill: ",D]}),t.jsx("button",{"data-testid":"skill-detail-retry",onClick:()=>re(e=>e+1),style:{fontFamily:"var(--font-mono, monospace)",fontSize:"0.8125rem",padding:"0.4rem 1rem",borderRadius:4,border:"1px solid var(--color-rule, #E8E1D6)",background:"transparent",color:"var(--text-primary, #191919)",cursor:"pointer"},children:"Retry"})]}),!C&&!D&&t.jsxs(t.Fragment,{children:[t.jsxs("section",{"data-testid":"skill-detail-hero",style:{marginBottom:"1.25rem"},children:[t.jsx("h2",{style:{margin:"0 0 0.5rem",fontSize:"1.25rem",fontWeight:600},children:H}),(s==null?void 0:s.description)&&t.jsx("p",{style:{margin:"0 0 0.75rem",color:"var(--text-secondary, #5A5651)",fontSize:"0.875rem",lineHeight:1.5},children:s.description}),t.jsxs("div",{style:{display:"flex",flexWrap:"wrap",gap:"0.5rem",alignItems:"center"},children:[t.jsx(ce,{tier:oe}),t.jsx(ae,{tier:ie,isTainted:s==null?void 0:s.isTainted}),t.jsx(fe,{repoUrl:(s==null?void 0:s.repoUrl)??null}),t.jsx(ge,{skillName:g,repoUrl:s==null?void 0:s.repoUrl})]})]}),t.jsxs("section",{"data-testid":"skill-detail-versions",style:{marginBottom:"1.25rem"},children:[t.jsx("h3",{style:{margin:"0 0 0.5rem",fontSize:"0.8125rem",textTransform:"uppercase",letterSpacing:"0.06em",color:"var(--text-secondary, #5A5651)"},children:"Versions"}),F.length===0?w?t.jsxs("div",{"data-testid":"skill-detail-unversioned",style:{color:"var(--text-secondary, #5A5651)",fontSize:"0.8125rem"},children:["Discovered — no published version yet (currentVersion: ",Q??"unknown",")."]}):t.jsx("div",{"data-testid":"skill-detail-no-versions",style:{color:"var(--text-secondary, #5A5651)",fontSize:"0.8125rem"},children:"No versions found."}):t.jsx("ul",{style:{listStyle:"none",margin:0,padding:0,display:"flex",flexDirection:"column",gap:4},children:F.map(e=>{const n=e.version===v,i=e.publishedAt?e.publishedAt.slice(0,10):"",o=e.authorEmail??e.author??"";return t.jsx("li",{children:t.jsxs("button",{type:"button","data-testid":"skill-detail-version-row","data-version":e.version,"data-selected":n?"true":"false","aria-pressed":n,onClick:()=>_(e.version),style:{width:"100%",textAlign:"left",padding:"0.5rem 0.75rem",borderRadius:4,border:n?"1px solid var(--color-action, #2F5B8E)":"1px solid var(--color-rule, #E8E1D6)",background:n?"color-mix(in srgb, var(--color-action, #2F5B8E) 8%, transparent)":"transparent",cursor:"pointer",fontFamily:"var(--font-mono, monospace)",fontSize:"0.8125rem",color:"var(--text-primary, #191919)",display:"flex",alignItems:"center",gap:"0.5rem"},children:[t.jsx("span",{"aria-hidden":"true",style:{width:14,display:"inline-flex",justifyContent:"center"},children:n?"●":"○"}),t.jsxs("span",{style:{fontWeight:600},children:["v",e.version]}),i&&t.jsxs("span",{style:{color:"var(--text-secondary, #5A5651)"},children:["· ",i]}),o&&t.jsxs("span",{style:{color:"var(--text-secondary, #5A5651)"},children:["· ",o]}),n&&t.jsx("span",{"data-testid":"skill-detail-version-selected-tag",style:{marginLeft:"auto",fontSize:"0.6875rem",color:"var(--color-action, #2F5B8E)"},children:"Selected"})]})},e.version)})}),t.jsx("div",{style:{marginTop:"0.5rem",textAlign:"right"},children:t.jsx("a",{"data-testid":"skill-detail-see-all-versions",href:`${ke}/skills/${r.owner}/${r.repo}/${r.slug}/versions`,target:"_blank",rel:"noopener noreferrer",style:{fontFamily:"var(--font-mono, monospace)",fontSize:"0.75rem",color:"var(--color-action, #2F5B8E)",textDecoration:"none"},children:"see all versions →"})})]}),ne?t.jsxs("section",{"data-testid":"skill-detail-blocked",style:{marginBottom:"0.5rem"},children:[t.jsx("h3",{style:{margin:"0 0 0.5rem",fontSize:"0.8125rem",textTransform:"uppercase",letterSpacing:"0.06em",color:"var(--red, #b54444)"},children:"This skill is blocked"}),t.jsx("div",{style:{padding:"1rem",borderRadius:6,border:"1px solid var(--red, #b54444)",background:"var(--red-muted, color-mix(in srgb, #b54444 18%, transparent))",color:"var(--red, #b54444)",fontSize:"0.875rem"},children:(s==null?void 0:s.blockReason)??"This skill has been blocked by platform moderators and cannot be installed."})]}):y&&!y.ok?t.jsxs("section",{"data-testid":"skill-detail-install-error",style:{marginBottom:"0.5rem"},children:[t.jsx("h3",{style:{margin:"0 0 0.5rem",fontSize:"0.8125rem",textTransform:"uppercase",letterSpacing:"0.06em",color:"var(--red, #b54444)"},children:"Install command unavailable"}),t.jsxs("div",{style:{padding:"1rem",borderRadius:6,border:"1px solid var(--red, #b54444)",background:"var(--red-muted, color-mix(in srgb, #b54444 18%, transparent))",color:"var(--red, #b54444)",fontSize:"0.875rem"},children:[y.reason," — refusing to render the install panel for safety."]})]}):y&&y.ok?t.jsxs("section",{"data-testid":"skill-detail-install",style:{marginBottom:"0.5rem"},children:[t.jsx("h3",{style:{margin:"0 0 0.5rem",fontSize:"0.8125rem",textTransform:"uppercase",letterSpacing:"0.06em",color:"var(--text-secondary, #5A5651)"},children:"Install"}),t.jsxs("div",{role:"radiogroup","aria-label":"Install scope","data-testid":"skill-detail-install-scope",style:{display:"flex",gap:"0.5rem",alignItems:"center",marginBottom:"0.75rem",fontFamily:"var(--font-mono, monospace)",fontSize:"0.75rem",color:"var(--text-secondary, #5A5651)"},children:[t.jsx("span",{children:"Scope:"}),["project","user"].map(e=>{var q;const n=b===e,i=(q=k==null?void 0:k.scopes)==null?void 0:q[e],o=(i==null?void 0:i.installed)===!0,a=(k==null?void 0:k.detectedAgentTools)??[],f=a.map(N=>`./${N.localDir}`).join(", "),p=a.map(N=>N.globalDir).join(", "),x=(i==null?void 0:i.installedAgentTools)??[],j=i!=null&&i.version?`Installed v${i.version} · ${x.join(", ")}`:`Installed · ${x.join(", ")}`,A=e==="project"?f?`Will install to: ${f}`:"Install with --scope project":p?`Will install to: ${p}`:"Install with --global",le=o?j:A,K=Se(e);return t.jsx("button",{type:"button",role:"radio","aria-checked":n,"aria-disabled":o,disabled:o,"data-testid":`skill-detail-install-scope-${e}`,"data-installed":o?"true":"false",title:le,onClick:()=>{o||te(e)},style:{padding:"0.25rem 0.6rem",borderRadius:4,border:`1px solid ${n?"var(--text-primary, #191919)":"var(--color-rule, #E8E1D6)"}`,background:n?"var(--text-primary, #191919)":"transparent",color:n?"var(--bg-surface, #FFFFFF)":"var(--text-secondary, #5A5651)",cursor:o?"not-allowed":"pointer",opacity:o?.55:1,fontFamily:"var(--font-mono, monospace)",fontSize:"0.75rem",fontWeight:n?600:400},children:o?`Installed ✓ ${K}`:K},e)})]}),(()=>{var a;const e=(a=k==null?void 0:k.scopes)==null?void 0:a[b],n=(e==null?void 0:e.installed)===!0,i=e!=null&&e.version?` (v${e.version})`:"",o=`Already installed at ${b} — re-run via CLI to force`;return t.jsx("button",{type:"button",onClick:se,disabled:n,"aria-disabled":n,"data-testid":"skill-detail-install-primary","data-installed":n?"true":"false","aria-label":n?"Already installed at the selected scope":"Install skill",title:n?o:void 0,style:{display:"inline-flex",alignItems:"center",marginBottom:"0.75rem",padding:"0.5rem 1rem",borderRadius:6,border:"1px solid var(--text-primary, #191919)",background:n?"var(--bg-surface, #FFFFFF)":"var(--text-primary, #191919)",color:n?"var(--text-secondary, #5A5651)":"var(--bg-surface, #FFFFFF)",cursor:n?"not-allowed":"pointer",opacity:n?.7:1,fontFamily:"var(--font-mono, monospace)",fontSize:"0.875rem",fontWeight:600},children:n?`✓ Installed${i}`:"Install"})})(),t.jsx("div",{"data-testid":"skill-detail-install-command",style:{display:"block"},children:t.jsx(he,{compact:!0,children:y.variants.map((e,n)=>t.jsxs("div",{"data-testid":`skill-detail-install-variant-${e.label}`,style:{display:"flex",gap:"0.75rem",alignItems:"flex-start",marginTop:n===0?0:"0.75rem"},children:[t.jsxs("div",{style:{flex:1,minWidth:0},children:[t.jsx("div",{style:{color:"#8B949E",marginBottom:"0.125rem"},children:e.comment}),t.jsxs("div",{children:[t.jsx("span",{style:{color:"#8B949E",marginRight:"0.5rem"},children:"$"}),t.jsx("span",{"data-testid":`skill-detail-install-variant-cmd-${e.label}`,children:e.command})]})]}),t.jsx("button",{type:"button",onClick:async()=>{const i=await Y(e.command),o=i?`Run ${e.command} in your terminal`:"Copy failed — please copy the command manually.";if(u)try{u(o,i?"success":"error")}catch{}else I(o,i?"success":"error",3500);i&&Z(c,{skillName:g,version:v??"",q:"",ts:Date.now()})},"data-testid":`skill-detail-copy-${e.label}`,"aria-label":`Copy ${e.label} install command`,style:{flexShrink:0,padding:"0.25rem 0.6rem",borderRadius:4,border:"1px solid color-mix(in srgb, #E6EDF3 25%, transparent)",background:"color-mix(in srgb, #E6EDF3 8%, transparent)",color:"#E6EDF3",cursor:"pointer",fontFamily:"var(--font-mono, monospace)",fontSize:"0.7rem",alignSelf:"flex-end"},children:"Copy"})]},e.label))})})]}):null]})]})]})]})}export{Ae as SkillDetailPanel,Ae as default};
@@ -1 +0,0 @@
1
- import{r as v,h as N,b as E,j as a,a as L}from"./index-JaDg6FlU.js";/* empty css */function M(l,d){const[c,u]=l.split("."),[w,f]=d.split("."),r=Number(c),m=Number(u),p=Number(w),y=Number(f);return!Number.isFinite(r)||!Number.isFinite(p)?(console.warn(`[semverBump] unparseable version: installed=${l} latest=${d}`),"patch"):p>r?"major":!Number.isFinite(m)||!Number.isFinite(y)?(console.warn(`[semverBump] unparseable minor: installed=${l} latest=${d}`),"patch"):y>m?"minor":"patch"}const F={major:{bg:"var(--red-muted)",text:"var(--red)"},minor:{bg:"var(--yellow-muted)",text:"var(--yellow)"},patch:{bg:"var(--green-muted)",text:"var(--green)"}},z=["project","personal","plugin"];function D(l,d={}){if(l.length===0)return"No tracked install — click to view details";const c=l.filter(n=>!n.readonly),u=l.filter(n=>n.readonly);if(c.length===0)return"Plugin-bundled — Update via plugin to refresh";const f=z.filter(n=>c.some(x=>x.scope===n)).join(" + "),m=Array.from(new Set(c.map(n=>n.agentLabel))).sort((n,x)=>n.localeCompare(x)).join(", "),p=c.length;let i=`Updates ${p} ${p===1?"location":"locations"}: ${f} (${m})`;if(d.pinned)return i+=" — pinned (skipped)",i;if(u.length>0){const n=Array.from(new Set(u.map(x=>x.pluginSlug??"?")));n.length===1&&n[0]!=="?"?i+=` — ${u.length} from plugin ${n[0]} (handled separately)`:i+=` — ${u.length} plugin-bundled (handled separately)`}return i}function O({updates:l,isRefreshing:d,onRefresh:c,onSelectSkill:u,onViewAll:w,onClose:f,anchorRef:r,diffSummariesById:m}){const p=v.useRef(null),y=v.useRef(null);v.useEffect(()=>{var e;(e=y.current)==null||e.focus()},[]),v.useEffect(()=>{function e(s){p.current&&(p.current.contains(s.target)||r!=null&&r.current&&r.current.contains(s.target)||f())}return document.addEventListener("mousedown",e),()=>document.removeEventListener("mousedown",e)},[f,r]),v.useEffect(()=>{function e(s){var g;s.key==="Escape"&&(s.preventDefault(),f(),(g=r==null?void 0:r.current)==null||g.focus())}return document.addEventListener("keydown",e),()=>document.removeEventListener("keydown",e)},[f,r]);const i=l.filter(e=>e.updateAvailable),{toast:n}=N(),{onSkillUpdated:x}=E(),[j,k]=v.useState(()=>new Set);async function $(e){const s=e.installLocations??[],g=s.filter(t=>!t.readonly);if(s.length>0&&g.length===0){const t=s[0].pluginSlug??"the plugin";n({severity:"info",message:`This skill came from plugin ${t}. Update the plugin to refresh it.`});return}if(!j.has(e.name)){k(t=>{const o=new Set(t);return o.add(e.name),o});try{const t=e.name.split("/"),o=e.localPlugin??(t.length>=2?t[t.length-2]:t[0]),b=e.localSkill??t[t.length-1],h=await L.postSkillUpdate(o,b);if(h.ok){const S=g.length||1,U=S===1?"location":"locations";x(o,b),n({severity:"success",message:`Updated ${b} in ${S} ${U}.`,durationMs:4e3})}else n({severity:"error",message:`Couldn't update ${b} — HTTP ${h.status}`,durationMs:0})}catch(t){const o=t instanceof Error?t.message:"Network error";n({severity:"error",message:`Update failed: ${o}`,durationMs:0})}finally{k(t=>{const o=new Set(t);return o.delete(e.name),o})}}}return a.jsxs("div",{ref:p,role:"dialog","aria-modal":"false","aria-label":"Skill update summary","data-testid":"update-dropdown",style:{position:"absolute",top:"calc(100% + 6px)",right:0,width:440,maxHeight:400,overflow:"auto",background:"var(--bg-surface)",border:"1px solid var(--border-default)",borderRadius:8,padding:10,zIndex:1e3,fontFamily:"var(--font-sans)",display:"flex",flexDirection:"column",gap:6},children:[a.jsxs("div",{style:{display:"flex",alignItems:"center",justifyContent:"space-between",padding:"2px 4px 6px",borderBottom:"1px solid var(--border-default)"},children:[a.jsx("span",{style:{fontSize:12,fontWeight:600,color:"var(--text-primary)"},children:i.length===0?"No updates available":`${i.length} updates available`}),a.jsx("button",{type:"button","data-testid":"update-dropdown-refresh",onClick:c,disabled:d,style:{background:"transparent",border:"none",color:"var(--text-secondary)",fontSize:11,fontFamily:"var(--font-mono)",cursor:d?"not-allowed":"pointer",padding:"2px 6px"},children:d?"Refreshing…":"Refresh"})]}),i.length>0?a.jsx("ul",{role:"list",style:{listStyle:"none",margin:0,padding:0,display:"flex",flexDirection:"column",gap:2},children:i.map((e,s)=>{const g=e.latest?M(e.installed,e.latest):"patch",t=F[g],o=m==null?void 0:m.get(e.name),b=D(e.installLocations??[],{pinned:e.pinned??!1}),h=j.has(e.name);return a.jsxs("li",{style:{margin:0,display:"flex",alignItems:"stretch",gap:4},children:[a.jsxs("button",{ref:s===0?y:void 0,type:"button","data-testid":"update-dropdown-row","data-skill-name":e.name,onClick:()=>u(e),style:{display:"flex",alignItems:"flex-start",gap:8,width:"100%",padding:"6px 8px",background:"transparent",border:"none",borderRadius:4,cursor:"pointer",color:"var(--text-primary)",fontFamily:"inherit",fontSize:12,textAlign:"left"},children:[a.jsx("span",{"aria-hidden":"true","data-testid":"update-dropdown-bump-dot","data-bump":g,style:{width:8,height:8,borderRadius:"50%",background:t.text,flexShrink:0,marginTop:4}}),a.jsxs("span",{style:{display:"flex",flexDirection:"column",gap:2,minWidth:0,flex:1},children:[a.jsxs("span",{style:{display:"flex",alignItems:"center",gap:8,minWidth:0},children:[a.jsx("span",{style:{minWidth:0,flex:1,overflow:"hidden",textOverflow:"ellipsis",whiteSpace:"nowrap"},children:e.name}),a.jsxs("span",{style:{fontFamily:"var(--font-mono)",fontSize:10,color:"var(--text-secondary)",fontVariantNumeric:"tabular-nums"},children:[e.installed," → ",e.latest??"?"]})]}),o&&a.jsx("span",{"data-testid":"update-dropdown-diff-summary",style:{fontSize:11,color:"var(--text-secondary)",overflow:"hidden",textOverflow:"ellipsis",whiteSpace:"nowrap"},children:o})]})]}),a.jsx("button",{type:"button","data-testid":"update-dropdown-row-update","data-skill-name":e.name,title:b,onClick:()=>{$(e)},disabled:h,style:{flexShrink:0,padding:"0 10px",background:"var(--color-ink)",color:"var(--color-paper)",border:"none",borderRadius:4,cursor:h?"not-allowed":"pointer",fontFamily:"var(--font-sans)",fontSize:11,fontWeight:500},children:h?"Updating…":"Update"})]},e.name)})}):a.jsx("div",{style:{padding:"12px 4px",fontSize:12,color:"var(--text-secondary)"},children:"All installed skills are up to date."}),a.jsx("div",{style:{display:"flex",justifyContent:"flex-end",padding:"4px 4px 0",borderTop:"1px solid var(--border-default)",marginTop:2},children:a.jsx("button",{type:"button","data-testid":"update-dropdown-view-all",onClick:w,style:{background:"transparent",border:"none",color:"var(--text-secondary)",fontSize:11,fontFamily:"var(--font-sans)",cursor:"pointer",padding:"4px 6px"},children:"View all"})})]})}export{O as default};