@skillkit/tui 1.3.0

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/dist/index.js ADDED
@@ -0,0 +1,2122 @@
1
+ // src/index.tsx
2
+ import { render } from "ink";
3
+
4
+ // src/App.tsx
5
+ import { useState as useState12 } from "react";
6
+ import { Box as Box10, Text as Text10, useInput as useInput8, useApp, useStdout } from "ink";
7
+
8
+ // src/components/Sidebar.tsx
9
+ import { Box, Text } from "ink";
10
+
11
+ // src/theme.ts
12
+ import chalk from "chalk";
13
+ var colors = {
14
+ primary: "white",
15
+ secondary: "white",
16
+ secondaryDim: "gray",
17
+ success: "white",
18
+ danger: "white",
19
+ warning: "white",
20
+ background: "bgBlack",
21
+ borderDim: "gray"
22
+ };
23
+ var symbols = {
24
+ pointer: chalk.white("\u276F"),
25
+ bullet: "\u25CF",
26
+ checkboxOn: chalk.white("\u2714"),
27
+ checkboxOff: chalk.dim("\u2716"),
28
+ check: chalk.white("\u2713"),
29
+ star: "\u2605",
30
+ spinner: ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"]
31
+ };
32
+ var logo = `
33
+ \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2557 \u2588\u2588\u2557\u2588\u2588\u2557\u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2557\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557
34
+ \u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2551 \u2588\u2588\u2554\u255D\u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2554\u255D\u2588\u2588\u2551\u255A\u2550\u2550\u2588\u2588\u2554\u2550\u2550\u255D
35
+ \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2554\u255D \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2588\u2588\u2588\u2554\u255D \u2588\u2588\u2551 \u2588\u2588\u2551
36
+ \u255A\u2550\u2550\u2550\u2550\u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2588\u2588\u2557 \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2554\u2550\u2588\u2588\u2557 \u2588\u2588\u2551 \u2588\u2588\u2551
37
+ \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2557\u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2551 \u2588\u2588\u2557\u2588\u2588\u2551 \u2588\u2588\u2551
38
+ \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u255D\u255A\u2550\u255D\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u255D
39
+ `;
40
+
41
+ // src/components/Sidebar.tsx
42
+ import { jsx, jsxs } from "react/jsx-runtime";
43
+ var NAV = [
44
+ { id: "home", label: "Home" },
45
+ { id: "browse", label: "Browse" },
46
+ { id: "recommend", label: "Recommend" },
47
+ { id: "translate", label: "Translate" },
48
+ { id: "context", label: "Context" },
49
+ { id: "installed", label: "List" },
50
+ { id: "sync", label: "Sync" },
51
+ { id: "settings", label: "Config" }
52
+ ];
53
+ function Sidebar({ screen }) {
54
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", width: 14, borderStyle: "single", paddingX: 1, children: [
55
+ /* @__PURE__ */ jsx(Text, { bold: true, color: colors.primary, children: "SkillKit" }),
56
+ NAV.slice(0, 3).map((item) => /* @__PURE__ */ jsxs(Text, { inverse: screen === item.id, children: [
57
+ screen === item.id ? symbols.bullet : " ",
58
+ item.label
59
+ ] }, item.id)),
60
+ /* @__PURE__ */ jsx(Text, { children: " " }),
61
+ NAV.slice(3, 5).map((item) => /* @__PURE__ */ jsxs(Text, { inverse: screen === item.id, children: [
62
+ screen === item.id ? symbols.bullet : " ",
63
+ item.label
64
+ ] }, item.id)),
65
+ /* @__PURE__ */ jsx(Text, { children: " " }),
66
+ NAV.slice(5).map((item) => /* @__PURE__ */ jsxs(Text, { inverse: screen === item.id, children: [
67
+ screen === item.id ? symbols.bullet : " ",
68
+ item.label
69
+ ] }, item.id)),
70
+ /* @__PURE__ */ jsx(Box, { flexGrow: 1 }),
71
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "? Help" }),
72
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "q Quit" })
73
+ ] });
74
+ }
75
+
76
+ // src/screens/Home.tsx
77
+ import { useState, useEffect } from "react";
78
+ import { readFileSync } from "fs";
79
+ import { fileURLToPath } from "url";
80
+ import { dirname, join } from "path";
81
+ import { Box as Box2, Text as Text2 } from "ink";
82
+ import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
83
+ var CHARS = "01\u2588\u2593\u2592\u2591\u2554\u2557\u255A\u255D\u2551\u2550";
84
+ var logoSmall = `
85
+ \u2554\u2550\u2557\u2566\u2554\u2550\u2566\u2566 \u2566 \u2566\u2554\u2550\u2566\u2554\u2566\u2557
86
+ \u255A\u2550\u2557\u2560\u2569\u2557\u2551\u2551 \u2551 \u2560\u2569\u2557\u2551 \u2551
87
+ \u255A\u2550\u255D\u2569 \u2569\u2569\u2569\u2550\u255D\u2569\u2550\u255D\u2569 \u2569\u2569 \u2569
88
+ `.trim();
89
+ function getVersion() {
90
+ try {
91
+ const __filename = fileURLToPath(import.meta.url);
92
+ const __dirname = dirname(__filename);
93
+ const packageJsonPath = join(__dirname, "../../../package.json");
94
+ const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf-8"));
95
+ return packageJson.version || "1.2.0";
96
+ } catch {
97
+ return "1.2.0";
98
+ }
99
+ }
100
+ function scramble(target, progress) {
101
+ return target.split("\n").map((line) => {
102
+ return line.split("").map((char, i) => {
103
+ if (char === " ") return char;
104
+ if (progress > i / line.length * 100) return char;
105
+ return CHARS[Math.floor(Math.random() * CHARS.length)];
106
+ }).join("");
107
+ }).join("\n");
108
+ }
109
+ function Home({ cols = 80, rows = 24 }) {
110
+ const [progress, setProgress] = useState(0);
111
+ const [display, setDisplay] = useState("");
112
+ const version = getVersion();
113
+ const useLogo = cols < 80 ? logoSmall : logo;
114
+ useEffect(() => {
115
+ if (progress >= 100) {
116
+ setDisplay(useLogo);
117
+ return;
118
+ }
119
+ const t = setInterval(() => {
120
+ setProgress((p) => {
121
+ const next = p + 12;
122
+ setDisplay(scramble(useLogo, next));
123
+ return next;
124
+ });
125
+ }, 35);
126
+ return () => clearInterval(t);
127
+ }, [progress, useLogo]);
128
+ return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", children: [
129
+ /* @__PURE__ */ jsx2(Text2, { color: colors.primary, children: display || useLogo }),
130
+ /* @__PURE__ */ jsx2(Box2, { marginTop: 1, children: /* @__PURE__ */ jsx2(Text2, { children: "Manage AI agent skills from your terminal." }) }),
131
+ /* @__PURE__ */ jsxs2(Box2, { marginTop: 1, flexDirection: "column", children: [
132
+ /* @__PURE__ */ jsx2(Text2, { bold: true, children: "Quick Actions:" }),
133
+ /* @__PURE__ */ jsx2(Text2, { dimColor: true, children: " [b] Browse skills marketplace" }),
134
+ /* @__PURE__ */ jsx2(Text2, { dimColor: true, children: " [r] Get smart recommendations" }),
135
+ /* @__PURE__ */ jsx2(Text2, { dimColor: true, children: " [t] Translate skills between agents" }),
136
+ /* @__PURE__ */ jsx2(Text2, { dimColor: true, children: " [c] Manage project context" }),
137
+ /* @__PURE__ */ jsx2(Text2, { dimColor: true, children: " [l] View installed skills" }),
138
+ /* @__PURE__ */ jsx2(Text2, { dimColor: true, children: " [s] Sync skills across agents" })
139
+ ] }),
140
+ rows >= 18 && /* @__PURE__ */ jsxs2(Box2, { marginTop: 1, flexDirection: "column", children: [
141
+ /* @__PURE__ */ jsx2(Text2, { bold: true, children: "Navigation:" }),
142
+ /* @__PURE__ */ jsx2(Text2, { dimColor: true, children: " \u2191\u2193 Navigate lists" }),
143
+ /* @__PURE__ */ jsx2(Text2, { dimColor: true, children: " Enter Select / Confirm" }),
144
+ /* @__PURE__ */ jsx2(Text2, { dimColor: true, children: " Esc Go back / Home" }),
145
+ /* @__PURE__ */ jsx2(Text2, { dimColor: true, children: " q Quit" })
146
+ ] }),
147
+ /* @__PURE__ */ jsx2(Box2, { marginTop: 1, children: /* @__PURE__ */ jsxs2(Text2, { dimColor: true, children: [
148
+ "v",
149
+ version,
150
+ " - Works with 17 AI agents"
151
+ ] }) })
152
+ ] });
153
+ }
154
+
155
+ // src/screens/Browse.tsx
156
+ import { useState as useState3 } from "react";
157
+ import { existsSync, mkdirSync, cpSync, rmSync } from "fs";
158
+ import { join as join2 } from "path";
159
+ import { Box as Box3, Text as Text3, useInput } from "ink";
160
+
161
+ // src/hooks/useMarketplace.ts
162
+ import { useState as useState2, useCallback, useEffect as useEffect2 } from "react";
163
+ import { detectProvider } from "@skillkit/core";
164
+ var POPULAR_REPOS = [
165
+ { source: "anthropics/skills", name: "Anthropic Official" },
166
+ { source: "vercel-labs/agent-skills", name: "Vercel Labs" },
167
+ { source: "expo/skills", name: "Expo / React Native" },
168
+ { source: "remotion-dev/skills", name: "Remotion Video" },
169
+ { source: "ComposioHQ/awesome-claude-skills", name: "Composio Awesome" },
170
+ { source: "travisvn/awesome-claude-skills", name: "Travis Awesome" },
171
+ { source: "mhattingpete/claude-skills-marketplace", name: "Skills Marketplace" },
172
+ { source: "coreyhaines31/marketingskills", name: "Marketing Skills" },
173
+ { source: "obra/superpowers", name: "Superpowers TDD" },
174
+ { source: "softaworks/agent-toolkit", name: "Softaworks Toolkit" },
175
+ { source: "wshobson/agents", name: "Dev Patterns" },
176
+ { source: "langgenius/dify", name: "Dify Frontend" },
177
+ { source: "trailofbits/skills", name: "Trail of Bits Security" },
178
+ { source: "better-auth/skills", name: "Better Auth" },
179
+ { source: "onmax/nuxt-skills", name: "Nuxt / Vue" },
180
+ { source: "hyf0/vue-skills", name: "Vue Best Practices" },
181
+ { source: "jezweb/claude-skills", name: "Cloudflare / TanStack" },
182
+ { source: "elysiajs/skills", name: "ElysiaJS / Bun" },
183
+ { source: "kadajett/agent-nestjs-skills", name: "NestJS" },
184
+ { source: "callstackincubator/agent-skills", name: "React Native" },
185
+ { source: "cloudai-x/threejs-skills", name: "Three.js" },
186
+ { source: "emalorenzo/three-agent-skills", name: "Three.js Advanced" },
187
+ { source: "dimillian/skills", name: "SwiftUI iOS" },
188
+ { source: "stripe/ai", name: "Stripe Payments" },
189
+ { source: "waynesutton/convexskills", name: "Convex Backend" },
190
+ { source: "kepano/obsidian-skills", name: "Obsidian Notes" },
191
+ { source: "jimliu/baoyu-skills", name: "Baoyu Tools" },
192
+ { source: "giuseppe-trisciuoglio/developer-kit", name: "Shadcn / Radix" },
193
+ { source: "openrouterteam/agent-skills", name: "OpenRouter SDK" },
194
+ { source: "intellectronica/agent-skills", name: "Context7" }
195
+ ];
196
+ function useMarketplace() {
197
+ const [allSkills, setAllSkills] = useState2([]);
198
+ const [filteredSkills, setFilteredSkills] = useState2([]);
199
+ const [loading, setLoading] = useState2(false);
200
+ const [error, setError] = useState2(null);
201
+ const [currentRepo, setCurrentRepo] = useState2(null);
202
+ const [fetchedRepos, setFetchedRepos] = useState2(/* @__PURE__ */ new Set());
203
+ const fetchRepo = useCallback(async (source) => {
204
+ if (fetchedRepos.has(source)) return;
205
+ setLoading(true);
206
+ setError(null);
207
+ setCurrentRepo(source);
208
+ try {
209
+ const provider = detectProvider(source);
210
+ if (!provider) {
211
+ throw new Error(`Could not detect provider for: ${source}`);
212
+ }
213
+ const result = await provider.clone(source, "", { depth: 1 });
214
+ if (!result.success || !result.discoveredSkills) {
215
+ throw new Error(result.error || "Failed to fetch skills");
216
+ }
217
+ const repoName = POPULAR_REPOS.find((r) => r.source === source)?.name || source;
218
+ const newSkills = result.discoveredSkills.map((skill) => ({
219
+ name: skill.name,
220
+ source,
221
+ repoName,
222
+ description: void 0
223
+ }));
224
+ setAllSkills((prev) => {
225
+ const updated = [...prev, ...newSkills];
226
+ return updated.sort((a, b) => a.name.localeCompare(b.name));
227
+ });
228
+ setFetchedRepos((prev) => /* @__PURE__ */ new Set([...prev, source]));
229
+ if (result.tempRoot) {
230
+ const { rmSync: rmSync3 } = await import("fs");
231
+ rmSync3(result.tempRoot, { recursive: true, force: true });
232
+ }
233
+ } catch (err) {
234
+ setError(err instanceof Error ? err.message : "Failed to fetch repository");
235
+ } finally {
236
+ setLoading(false);
237
+ setCurrentRepo(null);
238
+ }
239
+ }, [fetchedRepos]);
240
+ const fetchAllRepos = useCallback(async () => {
241
+ setLoading(true);
242
+ setError(null);
243
+ for (const repo of POPULAR_REPOS) {
244
+ if (!fetchedRepos.has(repo.source)) {
245
+ setCurrentRepo(repo.source);
246
+ try {
247
+ await fetchRepo(repo.source);
248
+ } catch {
249
+ }
250
+ }
251
+ }
252
+ setLoading(false);
253
+ setCurrentRepo(null);
254
+ }, [fetchRepo, fetchedRepos]);
255
+ const search = useCallback((query) => {
256
+ if (!query.trim()) {
257
+ setFilteredSkills(allSkills);
258
+ } else {
259
+ const lowerQuery = query.toLowerCase();
260
+ setFilteredSkills(
261
+ allSkills.filter(
262
+ (s) => s.name.toLowerCase().includes(lowerQuery) || s.source.toLowerCase().includes(lowerQuery) || s.repoName.toLowerCase().includes(lowerQuery) || s.description?.toLowerCase().includes(lowerQuery)
263
+ )
264
+ );
265
+ }
266
+ }, [allSkills]);
267
+ const refresh = useCallback(() => {
268
+ setFetchedRepos(/* @__PURE__ */ new Set());
269
+ setAllSkills([]);
270
+ setFilteredSkills([]);
271
+ }, []);
272
+ useEffect2(() => {
273
+ setFilteredSkills(allSkills);
274
+ }, [allSkills]);
275
+ const skills = filteredSkills.map((s) => ({
276
+ name: s.name,
277
+ description: s.description || s.repoName,
278
+ source: s.source
279
+ }));
280
+ return {
281
+ skills,
282
+ loading,
283
+ error,
284
+ totalCount: allSkills.length,
285
+ repos: POPULAR_REPOS,
286
+ currentRepo,
287
+ refresh,
288
+ search,
289
+ fetchRepo,
290
+ fetchAllRepos
291
+ };
292
+ }
293
+
294
+ // src/screens/Browse.tsx
295
+ import { detectProvider as detectProvider2 } from "@skillkit/core";
296
+ import { detectAgent, getAdapter as getAdapter2, getAllAdapters } from "@skillkit/agents";
297
+
298
+ // src/helpers.ts
299
+ import {
300
+ loadConfig,
301
+ getSearchDirs as coreGetSearchDirs,
302
+ getInstallDir as coreGetInstallDir,
303
+ saveSkillMetadata as coreSaveSkillMetadata
304
+ } from "@skillkit/core";
305
+ import { getAdapter } from "@skillkit/agents";
306
+ function getSearchDirs(agentType) {
307
+ const type = agentType || loadConfig().agent;
308
+ const adapter = getAdapter(type);
309
+ const adapterInfo = {
310
+ type: adapter.type,
311
+ name: adapter.name,
312
+ skillsDir: adapter.skillsDir,
313
+ configFile: adapter.configFile
314
+ };
315
+ return coreGetSearchDirs(adapterInfo);
316
+ }
317
+ function getInstallDir(global = false, agentType) {
318
+ const type = agentType || loadConfig().agent;
319
+ const adapter = getAdapter(type);
320
+ const adapterInfo = {
321
+ type: adapter.type,
322
+ name: adapter.name,
323
+ skillsDir: adapter.skillsDir,
324
+ configFile: adapter.configFile
325
+ };
326
+ return coreGetInstallDir(adapterInfo, global);
327
+ }
328
+
329
+ // src/screens/Browse.tsx
330
+ import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
331
+ function Browse({ rows = 24 }) {
332
+ const { repos, skills, loading, currentRepo, fetchRepo, fetchAllRepos } = useMarketplace();
333
+ const [view, setView] = useState3("repos");
334
+ const [sel, setSel] = useState3(0);
335
+ const [installing, setInstalling] = useState3(null);
336
+ const [message, setMessage] = useState3(null);
337
+ const [selectedSkill, setSelectedSkill] = useState3(null);
338
+ const [agents, setAgents] = useState3([]);
339
+ const items = view === "repos" ? repos : view === "skills" ? skills : agents;
340
+ const maxVisible = Math.max(5, rows - 8);
341
+ const start = Math.max(0, Math.min(sel - Math.floor(maxVisible / 2), items.length - maxVisible));
342
+ const visible = items.slice(start, start + maxVisible);
343
+ const showAgentSelection = async (skillName, source) => {
344
+ setSelectedSkill({ name: skillName, source });
345
+ const adapters = getAllAdapters();
346
+ const agentList = [];
347
+ for (const a of adapters) {
348
+ agentList.push({
349
+ type: a.type,
350
+ name: a.name,
351
+ detected: await a.isDetected()
352
+ });
353
+ }
354
+ setAgents(agentList);
355
+ setView("agents");
356
+ setSel(0);
357
+ };
358
+ const installSkill = async (skillName, source, agentType) => {
359
+ setInstalling(skillName);
360
+ setMessage(null);
361
+ try {
362
+ const provider = detectProvider2(source);
363
+ if (!provider) {
364
+ setMessage(`Error: Unknown provider for ${source}`);
365
+ setInstalling(null);
366
+ return;
367
+ }
368
+ const result = await provider.clone(source, "", { depth: 1 });
369
+ if (!result.success || !result.discoveredSkills) {
370
+ setMessage(`Error: ${result.error || "Failed to fetch"}`);
371
+ setInstalling(null);
372
+ return;
373
+ }
374
+ const skill = result.discoveredSkills.find((s) => s.name === skillName);
375
+ if (!skill) {
376
+ setMessage(`Error: Skill ${skillName} not found`);
377
+ setInstalling(null);
378
+ return;
379
+ }
380
+ const targetAgentType = agentType || await detectAgent();
381
+ const adapter = getAdapter2(targetAgentType);
382
+ const installDir = getInstallDir(false, targetAgentType);
383
+ if (!existsSync(installDir)) {
384
+ mkdirSync(installDir, { recursive: true });
385
+ }
386
+ const targetPath = join2(installDir, skillName);
387
+ if (existsSync(targetPath)) {
388
+ rmSync(targetPath, { recursive: true, force: true });
389
+ }
390
+ cpSync(skill.path, targetPath, { recursive: true, dereference: true });
391
+ const metadata = {
392
+ name: skillName,
393
+ description: "",
394
+ source,
395
+ sourceType: provider.type,
396
+ subpath: skillName,
397
+ installedAt: (/* @__PURE__ */ new Date()).toISOString(),
398
+ enabled: true
399
+ };
400
+ coreSaveSkillMetadata(targetPath, metadata);
401
+ if (result.tempRoot) {
402
+ rmSync(result.tempRoot, { recursive: true, force: true });
403
+ }
404
+ setMessage(`\u2713 Installed ${skillName} to ${adapter.name}`);
405
+ if (!agentType) {
406
+ setView("skills");
407
+ }
408
+ } catch (err) {
409
+ setMessage(`Error: ${err instanceof Error ? err.message : "Unknown error"}`);
410
+ } finally {
411
+ setInstalling(null);
412
+ if (agentType) {
413
+ setSelectedSkill(null);
414
+ }
415
+ }
416
+ };
417
+ useInput((input, key) => {
418
+ if (loading || installing) return;
419
+ if (key.upArrow) setSel((i) => Math.max(0, i - 1));
420
+ else if (key.downArrow) setSel((i) => Math.min(items.length - 1, i + 1));
421
+ else if (key.return) {
422
+ if (view === "repos" && repos[sel]) {
423
+ fetchRepo(repos[sel].source);
424
+ setView("skills");
425
+ setSel(0);
426
+ setMessage(null);
427
+ } else if (view === "skills" && skills[sel]?.source) {
428
+ installSkill(skills[sel].name, skills[sel].source);
429
+ } else if (view === "agents" && agents[sel]) {
430
+ installSkill(selectedSkill.name, selectedSkill.source, agents[sel].type);
431
+ }
432
+ } else if (input === "m" && view === "skills" && skills[sel]?.source) {
433
+ showAgentSelection(skills[sel].name, skills[sel].source);
434
+ } else if (input === "r") {
435
+ if (view === "skills") {
436
+ setView("repos");
437
+ setSel(0);
438
+ setMessage(null);
439
+ } else if (view === "agents") {
440
+ setView("skills");
441
+ setSel(0);
442
+ }
443
+ } else if (input === "a" && view === "repos") {
444
+ fetchAllRepos();
445
+ setView("skills");
446
+ setSel(0);
447
+ }
448
+ });
449
+ if (view === "agents") {
450
+ return /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", children: [
451
+ /* @__PURE__ */ jsx3(Text3, { bold: true, color: colors.primary, children: "SELECT AGENT" }),
452
+ /* @__PURE__ */ jsxs3(Text3, { dimColor: true, children: [
453
+ 'Install "',
454
+ selectedSkill?.name,
455
+ '" to which agent?'
456
+ ] }),
457
+ /* @__PURE__ */ jsx3(Text3, { dimColor: true, children: "(All agents supported - directory created if needed)" }),
458
+ /* @__PURE__ */ jsx3(Box3, { marginTop: 1, flexDirection: "column", children: visible.map((agent, i) => {
459
+ const idx = start + i;
460
+ const isSel = idx === sel;
461
+ const a = agent;
462
+ const status = a.detected ? "(ready)" : "(will create)";
463
+ return /* @__PURE__ */ jsxs3(Text3, { inverse: isSel, children: [
464
+ isSel ? symbols.pointer : " ",
465
+ " ",
466
+ a.name.padEnd(20),
467
+ " ",
468
+ /* @__PURE__ */ jsx3(Text3, { color: colors.secondaryDim, children: status })
469
+ ] }, a.type);
470
+ }) }),
471
+ /* @__PURE__ */ jsx3(Box3, { marginTop: 1, children: /* @__PURE__ */ jsx3(Text3, { dimColor: true, children: "Enter=install to selected agent r=back q=quit" }) })
472
+ ] });
473
+ }
474
+ return /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", children: [
475
+ /* @__PURE__ */ jsx3(Text3, { bold: true, color: colors.primary, children: view === "repos" ? "REPOSITORIES" : "SKILLS" }),
476
+ loading && /* @__PURE__ */ jsxs3(Text3, { dimColor: true, children: [
477
+ "Loading ",
478
+ currentRepo,
479
+ "..."
480
+ ] }),
481
+ installing && /* @__PURE__ */ jsxs3(Text3, { children: [
482
+ "Installing ",
483
+ installing,
484
+ "..."
485
+ ] }),
486
+ message && /* @__PURE__ */ jsx3(Text3, { dimColor: true, children: message }),
487
+ /* @__PURE__ */ jsxs3(Text3, { dimColor: true, children: [
488
+ items.length,
489
+ " items"
490
+ ] }),
491
+ /* @__PURE__ */ jsxs3(Box3, { marginTop: 1, flexDirection: "column", children: [
492
+ start > 0 && /* @__PURE__ */ jsxs3(Text3, { dimColor: true, children: [
493
+ " \u2191 ",
494
+ start,
495
+ " more"
496
+ ] }),
497
+ visible.map((item, i) => {
498
+ const idx = start + i;
499
+ const isSel = idx === sel;
500
+ const name = view === "repos" ? item.name : item.name;
501
+ const src = view === "repos" ? item.source : item.source || "";
502
+ return /* @__PURE__ */ jsxs3(Text3, { inverse: isSel, children: [
503
+ isSel ? symbols.pointer : " ",
504
+ name.padEnd(25),
505
+ " ",
506
+ /* @__PURE__ */ jsx3(Text3, { color: colors.secondaryDim, children: src })
507
+ ] }, src + name);
508
+ }),
509
+ start + maxVisible < items.length && /* @__PURE__ */ jsxs3(Text3, { dimColor: true, children: [
510
+ " \u2193 ",
511
+ items.length - start - maxVisible,
512
+ " more"
513
+ ] })
514
+ ] }),
515
+ /* @__PURE__ */ jsx3(Box3, { marginTop: 1, children: /* @__PURE__ */ jsx3(Text3, { dimColor: true, children: view === "repos" ? "Enter=fetch a=all q=quit" : "Enter=quick install m=choose agent r=back q=quit" }) })
516
+ ] });
517
+ }
518
+
519
+ // src/screens/Installed.tsx
520
+ import { useState as useState5 } from "react";
521
+ import { Box as Box4, Text as Text4, useInput as useInput2 } from "ink";
522
+
523
+ // src/hooks/useSkills.ts
524
+ import { useState as useState4, useEffect as useEffect3 } from "react";
525
+ import { findAllSkills } from "@skillkit/core";
526
+ function useSkills() {
527
+ const [skills, setSkills] = useState4([]);
528
+ const [loading, setLoading] = useState4(true);
529
+ const [error, setError] = useState4(null);
530
+ const refresh = () => {
531
+ setLoading(true);
532
+ setError(null);
533
+ try {
534
+ const searchDirs = getSearchDirs();
535
+ const foundSkills = findAllSkills(searchDirs);
536
+ const skillItems = foundSkills.map((s) => ({
537
+ name: s.name,
538
+ description: s.description,
539
+ source: s.metadata?.source,
540
+ enabled: s.enabled
541
+ }));
542
+ setSkills(skillItems);
543
+ } catch (err) {
544
+ setError(err instanceof Error ? err.message : "Failed to load skills");
545
+ } finally {
546
+ setLoading(false);
547
+ }
548
+ };
549
+ const remove = async (name) => {
550
+ const { rmSync: rmSync3 } = await import("fs");
551
+ const foundSkill = skills.find((s) => s.name === name);
552
+ if (foundSkill) {
553
+ const searchDirs = getSearchDirs();
554
+ const allSkills = findAllSkills(searchDirs);
555
+ const skill = allSkills.find((s) => s.name === name);
556
+ if (skill) {
557
+ rmSync3(skill.path, { recursive: true, force: true });
558
+ refresh();
559
+ }
560
+ }
561
+ };
562
+ useEffect3(() => {
563
+ refresh();
564
+ }, []);
565
+ return { skills, loading, error, refresh, remove };
566
+ }
567
+
568
+ // src/screens/Installed.tsx
569
+ import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
570
+ function Installed({ rows = 24 }) {
571
+ const { skills, loading, refresh, remove } = useSkills();
572
+ const [sel, setSel] = useState5(0);
573
+ const maxVisible = Math.max(5, rows - 6);
574
+ const start = Math.max(0, Math.min(sel - Math.floor(maxVisible / 2), skills.length - maxVisible));
575
+ const visible = skills.slice(start, start + maxVisible);
576
+ useInput2((input, key) => {
577
+ if (loading) return;
578
+ if (key.upArrow) setSel((i) => Math.max(0, i - 1));
579
+ else if (key.downArrow) setSel((i) => Math.min(skills.length - 1, i + 1));
580
+ else if (input === "r") refresh();
581
+ else if (input === "d" && skills[sel]) remove(skills[sel].name);
582
+ });
583
+ return /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", children: [
584
+ /* @__PURE__ */ jsx4(Text4, { bold: true, color: colors.primary, children: "INSTALLED SKILLS" }),
585
+ /* @__PURE__ */ jsxs4(Text4, { dimColor: true, children: [
586
+ skills.length,
587
+ " skills"
588
+ ] }),
589
+ loading && /* @__PURE__ */ jsx4(Text4, { children: "Loading..." }),
590
+ !loading && skills.length === 0 && /* @__PURE__ */ jsx4(Text4, { dimColor: true, children: "No skills installed. Press b to browse." }),
591
+ !loading && skills.length > 0 && /* @__PURE__ */ jsxs4(Box4, { marginTop: 1, flexDirection: "column", children: [
592
+ start > 0 && /* @__PURE__ */ jsxs4(Text4, { dimColor: true, children: [
593
+ " \u2191 ",
594
+ start,
595
+ " more"
596
+ ] }),
597
+ visible.map((skill, i) => {
598
+ const idx = start + i;
599
+ const isSel = idx === sel;
600
+ return /* @__PURE__ */ jsxs4(Text4, { inverse: isSel, children: [
601
+ isSel ? symbols.pointer : " ",
602
+ skill.name.padEnd(30),
603
+ " ",
604
+ skill.source && /* @__PURE__ */ jsx4(Text4, { color: colors.secondaryDim, children: skill.source })
605
+ ] }, skill.name);
606
+ }),
607
+ start + maxVisible < skills.length && /* @__PURE__ */ jsxs4(Text4, { dimColor: true, children: [
608
+ " \u2193 ",
609
+ skills.length - start - maxVisible,
610
+ " more"
611
+ ] })
612
+ ] }),
613
+ /* @__PURE__ */ jsx4(Box4, { marginTop: 1, children: /* @__PURE__ */ jsx4(Text4, { dimColor: true, children: "r=refresh d=delete q=quit" }) })
614
+ ] });
615
+ }
616
+
617
+ // src/screens/Sync.tsx
618
+ import { useState as useState6, useEffect as useEffect4 } from "react";
619
+ import { Box as Box5, Text as Text5, useInput as useInput3 } from "ink";
620
+ import { getAllAdapters as getAllAdapters2 } from "@skillkit/agents";
621
+ import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
622
+ function Sync({ rows = 24 }) {
623
+ const [agents, setAgents] = useState6([]);
624
+ const [loading, setLoading] = useState6(true);
625
+ const [sel, setSel] = useState6(0);
626
+ const [syncing, setSyncing] = useState6(false);
627
+ const maxVisible = Math.max(5, rows - 6);
628
+ const start = Math.max(0, Math.min(sel - Math.floor(maxVisible / 2), agents.length - maxVisible));
629
+ const visible = agents.slice(start, start + maxVisible);
630
+ useEffect4(() => {
631
+ (async () => {
632
+ const adapters = getAllAdapters2();
633
+ const s = [];
634
+ for (const a of adapters) {
635
+ s.push({ name: a.name, type: a.type, detected: await a.isDetected() });
636
+ }
637
+ setAgents(s);
638
+ setLoading(false);
639
+ })();
640
+ }, []);
641
+ useInput3((input, key) => {
642
+ if (loading || syncing) return;
643
+ if (key.upArrow) setSel((i) => Math.max(0, i - 1));
644
+ else if (key.downArrow) setSel((i) => Math.min(agents.length - 1, i + 1));
645
+ else if (input === "a") {
646
+ setSyncing(true);
647
+ setTimeout(() => setSyncing(false), 500);
648
+ } else if (key.return && agents[sel]?.detected) {
649
+ setSyncing(true);
650
+ setTimeout(() => setSyncing(false), 300);
651
+ }
652
+ });
653
+ const detected = agents.filter((a) => a.detected).length;
654
+ return /* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", children: [
655
+ /* @__PURE__ */ jsx5(Text5, { bold: true, color: colors.primary, children: "SYNC SKILLS" }),
656
+ /* @__PURE__ */ jsxs5(Text5, { dimColor: true, children: [
657
+ detected,
658
+ "/",
659
+ agents.length,
660
+ " agents detected"
661
+ ] }),
662
+ loading && /* @__PURE__ */ jsx5(Text5, { children: "Detecting agents..." }),
663
+ syncing && /* @__PURE__ */ jsx5(Text5, { children: "Syncing..." }),
664
+ !loading && !syncing && /* @__PURE__ */ jsxs5(Box5, { marginTop: 1, flexDirection: "column", children: [
665
+ start > 0 && /* @__PURE__ */ jsxs5(Text5, { dimColor: true, children: [
666
+ " \u2191 ",
667
+ start,
668
+ " more"
669
+ ] }),
670
+ visible.map((agent, i) => {
671
+ const idx = start + i;
672
+ const isSel = idx === sel;
673
+ return /* @__PURE__ */ jsxs5(Text5, { inverse: isSel, dimColor: !agent.detected, children: [
674
+ isSel ? symbols.pointer : " ",
675
+ agent.detected ? symbols.checkboxOn : symbols.checkboxOff,
676
+ " ",
677
+ agent.name.padEnd(20),
678
+ " ",
679
+ agent.detected ? "Ready" : "N/A"
680
+ ] }, agent.type);
681
+ }),
682
+ start + maxVisible < agents.length && /* @__PURE__ */ jsxs5(Text5, { dimColor: true, children: [
683
+ " \u2193 ",
684
+ agents.length - start - maxVisible,
685
+ " more"
686
+ ] })
687
+ ] }),
688
+ /* @__PURE__ */ jsx5(Box5, { marginTop: 1, children: /* @__PURE__ */ jsx5(Text5, { dimColor: true, children: "Enter=sync a=all q=quit" }) })
689
+ ] });
690
+ }
691
+
692
+ // src/screens/Settings.tsx
693
+ import { useState as useState7 } from "react";
694
+ import { Box as Box6, Text as Text6, useInput as useInput4 } from "ink";
695
+ import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
696
+ var SETTINGS = [
697
+ { id: "agent", label: "Default Agent", value: "auto-detect" },
698
+ { id: "sync", label: "Auto Sync", value: "disabled" },
699
+ { id: "cache", label: "Cache Dir", value: "~/.skillkit/cache" }
700
+ ];
701
+ function Settings({}) {
702
+ const [sel, setSel] = useState7(0);
703
+ useInput4((_, key) => {
704
+ if (key.upArrow) setSel((i) => Math.max(0, i - 1));
705
+ else if (key.downArrow) setSel((i) => Math.min(SETTINGS.length - 1, i + 1));
706
+ });
707
+ return /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
708
+ /* @__PURE__ */ jsx6(Text6, { bold: true, color: colors.primary, children: "SETTINGS" }),
709
+ /* @__PURE__ */ jsx6(Text6, { dimColor: true, children: "Configure SkillKit" }),
710
+ /* @__PURE__ */ jsx6(Box6, { marginTop: 1, flexDirection: "column", children: SETTINGS.map((s, i) => {
711
+ const isSel = i === sel;
712
+ return /* @__PURE__ */ jsxs6(Text6, { inverse: isSel, children: [
713
+ isSel ? symbols.pointer : " ",
714
+ s.label.padEnd(16),
715
+ " ",
716
+ /* @__PURE__ */ jsx6(Text6, { color: colors.secondaryDim, children: s.value })
717
+ ] }, s.id);
718
+ }) }),
719
+ /* @__PURE__ */ jsx6(Box6, { marginTop: 1, children: /* @__PURE__ */ jsx6(Text6, { dimColor: true, children: "Enter=edit q=quit" }) })
720
+ ] });
721
+ }
722
+
723
+ // src/screens/Recommend.tsx
724
+ import { useState as useState9 } from "react";
725
+ import { existsSync as existsSync3, mkdirSync as mkdirSync3, cpSync as cpSync2, rmSync as rmSync2 } from "fs";
726
+ import { join as join4 } from "path";
727
+ import { Box as Box7, Text as Text7, useInput as useInput5 } from "ink";
728
+
729
+ // src/hooks/useRecommend.ts
730
+ import { useState as useState8, useCallback as useCallback2, useEffect as useEffect5 } from "react";
731
+ import {
732
+ RecommendationEngine,
733
+ ContextManager
734
+ } from "@skillkit/core";
735
+ import { existsSync as existsSync2, readFileSync as readFileSync2, writeFileSync, mkdirSync as mkdirSync2 } from "fs";
736
+ import { join as join3 } from "path";
737
+ var INDEX_PATH = join3(process.env.HOME || "~", ".skillkit", "index.json");
738
+ var INDEX_CACHE_HOURS = 24;
739
+ function getSampleSkills() {
740
+ return [
741
+ {
742
+ name: "vercel-react-best-practices",
743
+ description: "Modern React patterns including Server Components, hooks best practices, and performance optimization",
744
+ source: "vercel-labs/agent-skills",
745
+ tags: ["react", "frontend", "typescript", "nextjs", "performance"],
746
+ compatibility: {
747
+ frameworks: ["react", "nextjs"],
748
+ languages: ["typescript", "javascript"],
749
+ libraries: []
750
+ },
751
+ popularity: 1500,
752
+ quality: 95,
753
+ lastUpdated: (/* @__PURE__ */ new Date()).toISOString(),
754
+ verified: true
755
+ },
756
+ {
757
+ name: "tailwind-v4-patterns",
758
+ description: "Tailwind CSS v4 utility patterns, responsive design, and component styling best practices",
759
+ source: "vercel-labs/agent-skills",
760
+ tags: ["tailwind", "css", "styling", "frontend", "responsive"],
761
+ compatibility: {
762
+ frameworks: [],
763
+ languages: ["typescript", "javascript"],
764
+ libraries: ["tailwindcss"]
765
+ },
766
+ popularity: 1200,
767
+ quality: 92,
768
+ lastUpdated: (/* @__PURE__ */ new Date()).toISOString(),
769
+ verified: true
770
+ },
771
+ {
772
+ name: "nextjs-app-router",
773
+ description: "Next.js App Router patterns including layouts, server actions, and data fetching",
774
+ source: "vercel-labs/agent-skills",
775
+ tags: ["nextjs", "react", "routing", "server-actions", "frontend"],
776
+ compatibility: {
777
+ frameworks: ["nextjs"],
778
+ languages: ["typescript", "javascript"],
779
+ libraries: []
780
+ },
781
+ popularity: 1100,
782
+ quality: 94,
783
+ lastUpdated: (/* @__PURE__ */ new Date()).toISOString(),
784
+ verified: true
785
+ },
786
+ {
787
+ name: "typescript-strict-patterns",
788
+ description: "TypeScript strict mode patterns, type safety, and advanced type utilities",
789
+ source: "anthropics/skills",
790
+ tags: ["typescript", "types", "safety", "patterns"],
791
+ compatibility: {
792
+ frameworks: [],
793
+ languages: ["typescript"],
794
+ libraries: []
795
+ },
796
+ popularity: 900,
797
+ quality: 90,
798
+ lastUpdated: (/* @__PURE__ */ new Date()).toISOString(),
799
+ verified: true
800
+ },
801
+ {
802
+ name: "supabase-best-practices",
803
+ description: "Supabase integration patterns including auth, database queries, and real-time subscriptions",
804
+ source: "anthropics/skills",
805
+ tags: ["supabase", "database", "auth", "backend", "postgresql"],
806
+ compatibility: {
807
+ frameworks: [],
808
+ languages: ["typescript", "javascript"],
809
+ libraries: ["@supabase/supabase-js"]
810
+ },
811
+ popularity: 800,
812
+ quality: 88,
813
+ lastUpdated: (/* @__PURE__ */ new Date()).toISOString(),
814
+ verified: true
815
+ },
816
+ {
817
+ name: "vitest-testing-patterns",
818
+ description: "Testing patterns with Vitest including mocking, assertions, and test organization",
819
+ source: "anthropics/skills",
820
+ tags: ["vitest", "testing", "typescript", "mocking", "tdd"],
821
+ compatibility: {
822
+ frameworks: [],
823
+ languages: ["typescript", "javascript"],
824
+ libraries: ["vitest"]
825
+ },
826
+ popularity: 700,
827
+ quality: 86,
828
+ lastUpdated: (/* @__PURE__ */ new Date()).toISOString(),
829
+ verified: false
830
+ },
831
+ {
832
+ name: "prisma-database-patterns",
833
+ description: "Prisma ORM patterns for schema design, migrations, and efficient queries",
834
+ source: "vercel-labs/agent-skills",
835
+ tags: ["prisma", "database", "orm", "postgresql", "backend"],
836
+ compatibility: {
837
+ frameworks: [],
838
+ languages: ["typescript"],
839
+ libraries: ["@prisma/client"]
840
+ },
841
+ popularity: 850,
842
+ quality: 89,
843
+ lastUpdated: (/* @__PURE__ */ new Date()).toISOString(),
844
+ verified: true
845
+ },
846
+ {
847
+ name: "security-best-practices",
848
+ description: "Security patterns for web applications including XSS prevention, CSRF, and secure headers",
849
+ source: "trailofbits/skills",
850
+ tags: ["security", "xss", "csrf", "headers", "owasp"],
851
+ compatibility: {
852
+ frameworks: [],
853
+ languages: ["typescript", "javascript", "python"],
854
+ libraries: []
855
+ },
856
+ popularity: 600,
857
+ quality: 95,
858
+ lastUpdated: (/* @__PURE__ */ new Date()).toISOString(),
859
+ verified: true
860
+ },
861
+ {
862
+ name: "python-fastapi-patterns",
863
+ description: "FastAPI best practices for building high-performance Python APIs",
864
+ source: "python-skills/fastapi",
865
+ tags: ["python", "fastapi", "backend", "api", "async"],
866
+ compatibility: {
867
+ frameworks: ["fastapi"],
868
+ languages: ["python"],
869
+ libraries: []
870
+ },
871
+ popularity: 550,
872
+ quality: 85,
873
+ lastUpdated: (/* @__PURE__ */ new Date()).toISOString(),
874
+ verified: false
875
+ },
876
+ {
877
+ name: "zustand-state-management",
878
+ description: "Zustand state management patterns for React applications",
879
+ source: "react-skills/state",
880
+ tags: ["zustand", "react", "state-management", "frontend"],
881
+ compatibility: {
882
+ frameworks: ["react"],
883
+ languages: ["typescript", "javascript"],
884
+ libraries: ["zustand"]
885
+ },
886
+ popularity: 650,
887
+ quality: 84,
888
+ lastUpdated: (/* @__PURE__ */ new Date()).toISOString(),
889
+ verified: false
890
+ }
891
+ ];
892
+ }
893
+ function useRecommend(projectPath = process.cwd()) {
894
+ const [recommendations, setRecommendations] = useState8([]);
895
+ const [searchResults, setSearchResults] = useState8([]);
896
+ const [profile, setProfile] = useState8(null);
897
+ const [loading, setLoading] = useState8(true);
898
+ const [error, setError] = useState8(null);
899
+ const [totalScanned, setTotalScanned] = useState8(0);
900
+ const [indexStatus, setIndexStatus] = useState8("missing");
901
+ const [engine] = useState8(() => new RecommendationEngine());
902
+ const loadIndex = useCallback2(() => {
903
+ if (!existsSync2(INDEX_PATH)) {
904
+ setIndexStatus("missing");
905
+ return null;
906
+ }
907
+ try {
908
+ const content = readFileSync2(INDEX_PATH, "utf-8");
909
+ const index = JSON.parse(content);
910
+ const lastUpdated = new Date(index.lastUpdated);
911
+ const hoursSinceUpdate = (Date.now() - lastUpdated.getTime()) / (1e3 * 60 * 60);
912
+ if (hoursSinceUpdate > INDEX_CACHE_HOURS) {
913
+ setIndexStatus("stale");
914
+ } else {
915
+ setIndexStatus("fresh");
916
+ }
917
+ return index;
918
+ } catch {
919
+ setIndexStatus("missing");
920
+ return null;
921
+ }
922
+ }, []);
923
+ const getProjectProfile = useCallback2(() => {
924
+ try {
925
+ const manager = new ContextManager(projectPath);
926
+ let context = manager.get();
927
+ if (!context) {
928
+ context = manager.init();
929
+ }
930
+ if (!context) {
931
+ return null;
932
+ }
933
+ return {
934
+ name: context.project.name,
935
+ type: context.project.type,
936
+ stack: context.stack,
937
+ patterns: context.patterns,
938
+ installedSkills: context.skills?.installed || [],
939
+ excludedSkills: context.skills?.excluded || []
940
+ };
941
+ } catch {
942
+ return null;
943
+ }
944
+ }, [projectPath]);
945
+ const loadRecommendations = useCallback2(() => {
946
+ setLoading(true);
947
+ setError(null);
948
+ try {
949
+ const projectProfile = getProjectProfile();
950
+ if (!projectProfile) {
951
+ setError("Failed to analyze project");
952
+ setLoading(false);
953
+ return;
954
+ }
955
+ setProfile(projectProfile);
956
+ const index = loadIndex();
957
+ if (!index || index.skills.length === 0) {
958
+ setRecommendations([]);
959
+ setTotalScanned(0);
960
+ setLoading(false);
961
+ return;
962
+ }
963
+ engine.loadIndex(index);
964
+ const result = engine.recommend(projectProfile, {
965
+ limit: 20,
966
+ minScore: 20,
967
+ excludeInstalled: true,
968
+ includeReasons: true
969
+ });
970
+ setRecommendations(result.recommendations);
971
+ setTotalScanned(result.totalSkillsScanned);
972
+ } catch (err) {
973
+ setError(err instanceof Error ? err.message : "Failed to get recommendations");
974
+ } finally {
975
+ setLoading(false);
976
+ }
977
+ }, [engine, getProjectProfile, loadIndex]);
978
+ const updateIndex = useCallback2(() => {
979
+ setLoading(true);
980
+ setError(null);
981
+ try {
982
+ const sampleIndex = {
983
+ version: 1,
984
+ lastUpdated: (/* @__PURE__ */ new Date()).toISOString(),
985
+ skills: getSampleSkills(),
986
+ sources: [
987
+ {
988
+ name: "vercel-labs",
989
+ url: "https://github.com/vercel-labs/agent-skills",
990
+ lastFetched: (/* @__PURE__ */ new Date()).toISOString(),
991
+ skillCount: 5
992
+ },
993
+ {
994
+ name: "anthropics",
995
+ url: "https://github.com/anthropics/skills",
996
+ lastFetched: (/* @__PURE__ */ new Date()).toISOString(),
997
+ skillCount: 3
998
+ }
999
+ ]
1000
+ };
1001
+ const indexDir = join3(process.env.HOME || "~", ".skillkit");
1002
+ if (!existsSync2(indexDir)) {
1003
+ mkdirSync2(indexDir, { recursive: true });
1004
+ }
1005
+ writeFileSync(INDEX_PATH, JSON.stringify(sampleIndex, null, 2));
1006
+ setIndexStatus("fresh");
1007
+ loadRecommendations();
1008
+ } catch (err) {
1009
+ setError(err instanceof Error ? err.message : "Failed to update index");
1010
+ setLoading(false);
1011
+ }
1012
+ }, [loadRecommendations]);
1013
+ const search = useCallback2((query) => {
1014
+ if (!query.trim()) {
1015
+ setSearchResults([]);
1016
+ return;
1017
+ }
1018
+ const results = engine.search({
1019
+ query,
1020
+ limit: 10,
1021
+ semantic: true
1022
+ });
1023
+ const scoredResults = results.map((r) => ({
1024
+ skill: r.skill,
1025
+ score: r.relevance,
1026
+ reasons: r.matchedTerms.map((term) => ({
1027
+ category: "tag",
1028
+ description: `Matched: ${term}`,
1029
+ weight: 0,
1030
+ matched: [term]
1031
+ })),
1032
+ warnings: []
1033
+ }));
1034
+ setSearchResults(scoredResults);
1035
+ }, [engine]);
1036
+ const refresh = useCallback2(() => {
1037
+ loadRecommendations();
1038
+ }, [loadRecommendations]);
1039
+ useEffect5(() => {
1040
+ loadRecommendations();
1041
+ }, [loadRecommendations]);
1042
+ return {
1043
+ recommendations,
1044
+ profile,
1045
+ loading,
1046
+ error,
1047
+ totalScanned,
1048
+ indexStatus,
1049
+ refresh,
1050
+ updateIndex,
1051
+ search,
1052
+ searchResults
1053
+ };
1054
+ }
1055
+
1056
+ // src/screens/Recommend.tsx
1057
+ import { detectProvider as detectProvider3 } from "@skillkit/core";
1058
+ import { detectAgent as detectAgent2, getAdapter as getAdapter3, getAllAdapters as getAllAdapters3 } from "@skillkit/agents";
1059
+ import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
1060
+ function Recommend({ rows = 24 }) {
1061
+ const {
1062
+ recommendations,
1063
+ profile,
1064
+ loading,
1065
+ error,
1066
+ totalScanned,
1067
+ indexStatus,
1068
+ refresh,
1069
+ updateIndex,
1070
+ search,
1071
+ searchResults
1072
+ } = useRecommend();
1073
+ const [view, setView] = useState9("recommendations");
1074
+ const [sel, setSel] = useState9(0);
1075
+ const [searchQuery, setSearchQuery] = useState9("");
1076
+ const [searchMode, setSearchMode] = useState9(false);
1077
+ const [installing, setInstalling] = useState9(null);
1078
+ const [message, setMessage] = useState9(null);
1079
+ const [selectedSkill, setSelectedSkill] = useState9(null);
1080
+ const [agents, setAgents] = useState9([]);
1081
+ const items = view === "recommendations" ? recommendations : view === "search" ? searchResults : agents;
1082
+ const maxVisible = Math.max(5, rows - 10);
1083
+ const start = Math.max(0, Math.min(sel - Math.floor(maxVisible / 2), items.length - maxVisible));
1084
+ const visible = items.slice(start, start + maxVisible);
1085
+ const showAgentSelection = async (skillName, source) => {
1086
+ setSelectedSkill({ name: skillName, source });
1087
+ const adapters = getAllAdapters3();
1088
+ const agentList = [];
1089
+ for (const a of adapters) {
1090
+ agentList.push({
1091
+ type: a.type,
1092
+ name: a.name,
1093
+ detected: await a.isDetected()
1094
+ });
1095
+ }
1096
+ setAgents(agentList);
1097
+ setView("agents");
1098
+ setSel(0);
1099
+ };
1100
+ const installSkill = async (skillName, source, agentType) => {
1101
+ if (!source) {
1102
+ setMessage("Error: No source available for this skill");
1103
+ return;
1104
+ }
1105
+ setInstalling(skillName);
1106
+ setMessage(null);
1107
+ try {
1108
+ const provider = detectProvider3(source);
1109
+ if (!provider) {
1110
+ setMessage(`Error: Unknown provider for ${source}`);
1111
+ setInstalling(null);
1112
+ return;
1113
+ }
1114
+ const result = await provider.clone(source, "", { depth: 1 });
1115
+ if (!result.success || !result.discoveredSkills) {
1116
+ setMessage(`Error: ${result.error || "Failed to fetch"}`);
1117
+ setInstalling(null);
1118
+ return;
1119
+ }
1120
+ const skill = result.discoveredSkills.find((s) => s.name === skillName);
1121
+ if (!skill) {
1122
+ setMessage(`Error: Skill ${skillName} not found in repo`);
1123
+ setInstalling(null);
1124
+ return;
1125
+ }
1126
+ const targetAgentType = agentType || await detectAgent2();
1127
+ const adapter = getAdapter3(targetAgentType);
1128
+ const installDir = getInstallDir(false, targetAgentType);
1129
+ if (!existsSync3(installDir)) {
1130
+ mkdirSync3(installDir, { recursive: true });
1131
+ }
1132
+ const targetPath = join4(installDir, skillName);
1133
+ if (existsSync3(targetPath)) {
1134
+ rmSync2(targetPath, { recursive: true, force: true });
1135
+ }
1136
+ cpSync2(skill.path, targetPath, { recursive: true, dereference: true });
1137
+ const metadata = {
1138
+ name: skillName,
1139
+ description: "",
1140
+ source,
1141
+ sourceType: provider.type,
1142
+ subpath: skillName,
1143
+ installedAt: (/* @__PURE__ */ new Date()).toISOString(),
1144
+ enabled: true
1145
+ };
1146
+ coreSaveSkillMetadata(targetPath, metadata);
1147
+ if (result.tempRoot) {
1148
+ rmSync2(result.tempRoot, { recursive: true, force: true });
1149
+ }
1150
+ setMessage(`\u2713 Installed ${skillName} to ${adapter.name}`);
1151
+ } catch (err) {
1152
+ setMessage(`Error: ${err instanceof Error ? err.message : "Unknown error"}`);
1153
+ } finally {
1154
+ setInstalling(null);
1155
+ if (agentType) {
1156
+ setSelectedSkill(null);
1157
+ setView("recommendations");
1158
+ }
1159
+ }
1160
+ };
1161
+ useInput5((input, key) => {
1162
+ if (loading || installing) return;
1163
+ if (searchMode) {
1164
+ if (key.escape) {
1165
+ setSearchMode(false);
1166
+ setSearchQuery("");
1167
+ setView("recommendations");
1168
+ return;
1169
+ }
1170
+ if (key.return) {
1171
+ setSearchMode(false);
1172
+ if (searchQuery.trim()) {
1173
+ search(searchQuery);
1174
+ setView("search");
1175
+ setSel(0);
1176
+ }
1177
+ return;
1178
+ }
1179
+ if (key.backspace || key.delete) {
1180
+ setSearchQuery((q) => q.slice(0, -1));
1181
+ return;
1182
+ }
1183
+ if (input && !key.ctrl && !key.meta) {
1184
+ setSearchQuery((q) => q + input);
1185
+ return;
1186
+ }
1187
+ return;
1188
+ }
1189
+ if (key.upArrow) setSel((i) => Math.max(0, i - 1));
1190
+ else if (key.downArrow) setSel((i) => Math.min(items.length - 1, i + 1));
1191
+ else if (key.return) {
1192
+ if (view === "agents" && agents[sel]) {
1193
+ installSkill(selectedSkill.name, selectedSkill.source, agents[sel].type);
1194
+ } else if ((view === "recommendations" || view === "search") && items[sel]) {
1195
+ const skill = items[sel];
1196
+ if (skill.skill.source) {
1197
+ installSkill(skill.skill.name, skill.skill.source);
1198
+ }
1199
+ }
1200
+ } else if (input === "/") {
1201
+ setSearchMode(true);
1202
+ setSearchQuery("");
1203
+ } else if (input === "m" && (view === "recommendations" || view === "search") && items[sel]) {
1204
+ const skill = items[sel];
1205
+ if (skill.skill.source) {
1206
+ showAgentSelection(skill.skill.name, skill.skill.source);
1207
+ }
1208
+ } else if (input === "u") {
1209
+ updateIndex();
1210
+ } else if (input === "r" && view !== "recommendations") {
1211
+ if (view === "agents") {
1212
+ setView("recommendations");
1213
+ } else {
1214
+ setView("recommendations");
1215
+ setSearchQuery("");
1216
+ }
1217
+ setSel(0);
1218
+ } else if (input === "R") {
1219
+ refresh();
1220
+ }
1221
+ });
1222
+ if (view === "agents") {
1223
+ return /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", children: [
1224
+ /* @__PURE__ */ jsx7(Text7, { bold: true, color: colors.primary, children: "SELECT AGENT" }),
1225
+ /* @__PURE__ */ jsxs7(Text7, { dimColor: true, children: [
1226
+ 'Install "',
1227
+ selectedSkill?.name,
1228
+ '" to which agent?'
1229
+ ] }),
1230
+ /* @__PURE__ */ jsx7(Box7, { marginTop: 1, flexDirection: "column", children: visible.map((agent, i) => {
1231
+ const idx = start + i;
1232
+ const isSel = idx === sel;
1233
+ const a = agent;
1234
+ const status = a.detected ? "(ready)" : "(will create)";
1235
+ return /* @__PURE__ */ jsxs7(Text7, { inverse: isSel, children: [
1236
+ isSel ? symbols.pointer : " ",
1237
+ " ",
1238
+ a.name.padEnd(20),
1239
+ " ",
1240
+ /* @__PURE__ */ jsx7(Text7, { color: colors.secondaryDim, children: status })
1241
+ ] }, a.type);
1242
+ }) }),
1243
+ /* @__PURE__ */ jsx7(Box7, { marginTop: 1, children: /* @__PURE__ */ jsx7(Text7, { dimColor: true, children: "Enter=install r=back q=quit" }) })
1244
+ ] });
1245
+ }
1246
+ const getScoreBar = (score) => {
1247
+ const filled = Math.round(score / 10);
1248
+ const empty = 10 - filled;
1249
+ return "\u2588".repeat(filled) + "\u2591".repeat(empty);
1250
+ };
1251
+ const getScoreColor = (score) => {
1252
+ if (score >= 70) return "green";
1253
+ if (score >= 50) return "yellow";
1254
+ return "gray";
1255
+ };
1256
+ return /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", children: [
1257
+ /* @__PURE__ */ jsx7(Text7, { bold: true, color: colors.primary, children: view === "search" ? `SEARCH: "${searchQuery}"` : "RECOMMENDATIONS" }),
1258
+ loading && /* @__PURE__ */ jsx7(Text7, { dimColor: true, children: "Loading..." }),
1259
+ installing && /* @__PURE__ */ jsxs7(Text7, { children: [
1260
+ "Installing ",
1261
+ installing,
1262
+ "..."
1263
+ ] }),
1264
+ message && /* @__PURE__ */ jsx7(Text7, { dimColor: true, children: message }),
1265
+ error && /* @__PURE__ */ jsx7(Text7, { color: "red", children: error }),
1266
+ searchMode && /* @__PURE__ */ jsxs7(Box7, { marginTop: 1, children: [
1267
+ /* @__PURE__ */ jsxs7(Text7, { children: [
1268
+ "Search: ",
1269
+ searchQuery
1270
+ ] }),
1271
+ /* @__PURE__ */ jsx7(Text7, { dimColor: true, children: "\u2588" })
1272
+ ] }),
1273
+ indexStatus === "missing" && !loading && /* @__PURE__ */ jsxs7(Box7, { marginTop: 1, flexDirection: "column", children: [
1274
+ /* @__PURE__ */ jsx7(Text7, { color: "yellow", children: "No skill index found." }),
1275
+ /* @__PURE__ */ jsx7(Text7, { dimColor: true, children: "Press 'u' to update index from known sources." })
1276
+ ] }),
1277
+ indexStatus === "stale" && !loading && /* @__PURE__ */ jsx7(Text7, { dimColor: true, children: "Index may be outdated. Press 'u' to update." }),
1278
+ profile && !searchMode && /* @__PURE__ */ jsxs7(Box7, { marginTop: 1, flexDirection: "column", children: [
1279
+ /* @__PURE__ */ jsxs7(Text7, { dimColor: true, children: [
1280
+ "Project: ",
1281
+ profile.name,
1282
+ profile.type ? ` (${profile.type})` : ""
1283
+ ] }),
1284
+ profile.stack.languages.length > 0 && /* @__PURE__ */ jsxs7(Text7, { dimColor: true, children: [
1285
+ "Stack: ",
1286
+ profile.stack.languages.map((l) => l.name).join(", "),
1287
+ profile.stack.frameworks.length > 0 && `, ${profile.stack.frameworks.map((f) => f.name).join(", ")}`
1288
+ ] })
1289
+ ] }),
1290
+ !searchMode && /* @__PURE__ */ jsx7(Text7, { dimColor: true, children: view === "search" ? `${searchResults.length} results` : `${recommendations.length} of ${totalScanned} skills matched` }),
1291
+ /* @__PURE__ */ jsxs7(Box7, { marginTop: 1, flexDirection: "column", children: [
1292
+ start > 0 && /* @__PURE__ */ jsxs7(Text7, { dimColor: true, children: [
1293
+ " \u2191 ",
1294
+ start,
1295
+ " more"
1296
+ ] }),
1297
+ visible.map((item, i) => {
1298
+ const idx = start + i;
1299
+ const isSel = idx === sel;
1300
+ const skill = item.skill;
1301
+ const score = item.score;
1302
+ return /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", children: [
1303
+ /* @__PURE__ */ jsxs7(Text7, { inverse: isSel, children: [
1304
+ isSel ? symbols.pointer : " ",
1305
+ /* @__PURE__ */ jsxs7(Text7, { color: getScoreColor(score), children: [
1306
+ score.toString().padStart(3),
1307
+ "%"
1308
+ ] }),
1309
+ " ",
1310
+ /* @__PURE__ */ jsx7(Text7, { color: getScoreColor(score), children: getScoreBar(score) }),
1311
+ " ",
1312
+ /* @__PURE__ */ jsx7(Text7, { bold: true, children: skill.name })
1313
+ ] }),
1314
+ isSel && skill.description && /* @__PURE__ */ jsxs7(Text7, { dimColor: true, children: [
1315
+ " ",
1316
+ skill.description.slice(0, 60),
1317
+ skill.description.length > 60 ? "..." : ""
1318
+ ] }),
1319
+ isSel && skill.source && /* @__PURE__ */ jsxs7(Text7, { dimColor: true, children: [
1320
+ " Source: ",
1321
+ skill.source
1322
+ ] })
1323
+ ] }, skill.name);
1324
+ }),
1325
+ start + maxVisible < items.length && /* @__PURE__ */ jsxs7(Text7, { dimColor: true, children: [
1326
+ " \u2193 ",
1327
+ items.length - start - maxVisible,
1328
+ " more"
1329
+ ] })
1330
+ ] }),
1331
+ /* @__PURE__ */ jsx7(Box7, { marginTop: 1, children: /* @__PURE__ */ jsx7(Text7, { dimColor: true, children: view === "search" ? "Enter=install m=choose agent /=search r=back u=update q=quit" : "Enter=install m=choose agent /=search u=update R=refresh q=quit" }) })
1332
+ ] });
1333
+ }
1334
+
1335
+ // src/screens/Translate.tsx
1336
+ import { useState as useState10, useEffect as useEffect6 } from "react";
1337
+ import { existsSync as existsSync4, readdirSync, readFileSync as readFileSync3, writeFileSync as writeFileSync2, mkdirSync as mkdirSync4 } from "fs";
1338
+ import { join as join5 } from "path";
1339
+ import { Box as Box8, Text as Text8, useInput as useInput6 } from "ink";
1340
+ import {
1341
+ translateSkill,
1342
+ getSupportedTranslationAgents
1343
+ } from "@skillkit/core";
1344
+ import { getAllAdapters as getAllAdapters4, getAdapter as getAdapter4 } from "@skillkit/agents";
1345
+ import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
1346
+ function Translate({ rows = 24 }) {
1347
+ const [view, setView] = useState10("skills");
1348
+ const [skills, setSkills] = useState10([]);
1349
+ const [agents, setAgents] = useState10([]);
1350
+ const [sel, setSel] = useState10(0);
1351
+ const [selectedSkill, setSelectedSkill] = useState10(null);
1352
+ const [selectedAgent, setSelectedAgent] = useState10(null);
1353
+ const [preview, setPreview] = useState10("");
1354
+ const [result, setResult] = useState10(null);
1355
+ const [loading, setLoading] = useState10(false);
1356
+ useEffect6(() => {
1357
+ const loadSkills = () => {
1358
+ const installDir = getInstallDir(false);
1359
+ const foundSkills = [];
1360
+ if (existsSync4(installDir)) {
1361
+ const dirs = readdirSync(installDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
1362
+ for (const dir of dirs) {
1363
+ const skillPath = join5(installDir, dir);
1364
+ const skillMdPath = join5(skillPath, "SKILL.md");
1365
+ if (existsSync4(skillMdPath)) {
1366
+ const content = readFileSync3(skillMdPath, "utf-8");
1367
+ foundSkills.push({
1368
+ name: dir,
1369
+ path: skillPath,
1370
+ content
1371
+ });
1372
+ }
1373
+ }
1374
+ }
1375
+ setSkills(foundSkills);
1376
+ };
1377
+ loadSkills();
1378
+ }, []);
1379
+ useEffect6(() => {
1380
+ const adapters = getAllAdapters4();
1381
+ const supportedAgents = getSupportedTranslationAgents();
1382
+ const agentList = adapters.filter((a) => supportedAgents.includes(a.type)).map((a) => ({
1383
+ type: a.type,
1384
+ name: a.name
1385
+ }));
1386
+ setAgents(agentList);
1387
+ }, []);
1388
+ const currentItems = view === "skills" ? skills : agents;
1389
+ const maxVisible = Math.max(5, rows - 12);
1390
+ const start = Math.max(0, Math.min(sel - Math.floor(maxVisible / 2), currentItems.length - maxVisible));
1391
+ const visible = currentItems.slice(start, start + maxVisible);
1392
+ const generatePreview = (skill, agent) => {
1393
+ try {
1394
+ const result2 = translateSkill(skill.content, agent.type, { sourceFilename: "SKILL.md" });
1395
+ return result2.content;
1396
+ } catch (err) {
1397
+ return `Error: ${err instanceof Error ? err.message : "Translation failed"}`;
1398
+ }
1399
+ };
1400
+ const executeTranslation = () => {
1401
+ if (!selectedSkill || !selectedAgent) return;
1402
+ setLoading(true);
1403
+ try {
1404
+ const translationResult = translateSkill(selectedSkill.content, selectedAgent.type, {
1405
+ sourceFilename: "SKILL.md"
1406
+ });
1407
+ if (!translationResult.success) {
1408
+ setResult({
1409
+ success: false,
1410
+ message: `Translation failed: ${translationResult.warnings.join(", ")}`
1411
+ });
1412
+ setLoading(false);
1413
+ return;
1414
+ }
1415
+ const adapter = getAdapter4(selectedAgent.type);
1416
+ const targetDir = adapter?.skillsDir ? join5(process.cwd(), adapter.skillsDir) : join5(process.cwd(), `.${selectedAgent.type}/skills/`);
1417
+ if (!existsSync4(targetDir)) {
1418
+ mkdirSync4(targetDir, { recursive: true });
1419
+ }
1420
+ const filename = translationResult.filename || `${selectedSkill.name}.md`;
1421
+ const targetPath = join5(targetDir, filename);
1422
+ writeFileSync2(targetPath, translationResult.content, "utf-8");
1423
+ setResult({
1424
+ success: true,
1425
+ message: `Translated ${selectedSkill.name} to ${selectedAgent.name} format`,
1426
+ path: targetPath
1427
+ });
1428
+ } catch (err) {
1429
+ setResult({
1430
+ success: false,
1431
+ message: `Error: ${err instanceof Error ? err.message : "Unknown error"}`
1432
+ });
1433
+ } finally {
1434
+ setLoading(false);
1435
+ setView("result");
1436
+ }
1437
+ };
1438
+ useInput6((_input, key) => {
1439
+ if (loading) return;
1440
+ if (key.upArrow) setSel((i) => Math.max(0, i - 1));
1441
+ else if (key.downArrow) setSel((i) => Math.min(currentItems.length - 1, i + 1));
1442
+ else if (key.escape) {
1443
+ if (view === "result") {
1444
+ setView("skills");
1445
+ setSelectedSkill(null);
1446
+ setSelectedAgent(null);
1447
+ setResult(null);
1448
+ setSel(0);
1449
+ } else if (view === "preview") {
1450
+ setView("agents");
1451
+ } else if (view === "agents") {
1452
+ setView("skills");
1453
+ setSelectedSkill(null);
1454
+ }
1455
+ } else if (key.return) {
1456
+ if (view === "skills" && skills[sel]) {
1457
+ setSelectedSkill(skills[sel]);
1458
+ setView("agents");
1459
+ setSel(0);
1460
+ } else if (view === "agents" && agents[sel]) {
1461
+ setSelectedAgent(agents[sel]);
1462
+ const previewContent = generatePreview(selectedSkill, agents[sel]);
1463
+ setPreview(previewContent);
1464
+ setView("preview");
1465
+ } else if (view === "preview") {
1466
+ executeTranslation();
1467
+ } else if (view === "result") {
1468
+ setView("skills");
1469
+ setSelectedSkill(null);
1470
+ setSelectedAgent(null);
1471
+ setResult(null);
1472
+ setSel(0);
1473
+ }
1474
+ }
1475
+ });
1476
+ if (view === "result" && result) {
1477
+ return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", children: [
1478
+ /* @__PURE__ */ jsxs8(Text8, { bold: true, color: colors.primary, children: [
1479
+ "TRANSLATION ",
1480
+ result.success ? "COMPLETE" : "FAILED"
1481
+ ] }),
1482
+ /* @__PURE__ */ jsxs8(Box8, { marginTop: 1, flexDirection: "column", children: [
1483
+ /* @__PURE__ */ jsxs8(Text8, { color: result.success ? "green" : "red", children: [
1484
+ result.success ? "\u2713" : "\u2717",
1485
+ " ",
1486
+ result.message
1487
+ ] }),
1488
+ result.path && /* @__PURE__ */ jsxs8(Text8, { dimColor: true, children: [
1489
+ "Saved to: ",
1490
+ result.path
1491
+ ] })
1492
+ ] }),
1493
+ /* @__PURE__ */ jsx8(Box8, { marginTop: 2, children: /* @__PURE__ */ jsx8(Text8, { dimColor: true, children: "Press Enter or Esc to continue" }) })
1494
+ ] });
1495
+ }
1496
+ if (view === "preview") {
1497
+ const previewLines = preview.split("\n").slice(0, maxVisible);
1498
+ return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", children: [
1499
+ /* @__PURE__ */ jsx8(Text8, { bold: true, color: colors.primary, children: "TRANSLATION PREVIEW" }),
1500
+ /* @__PURE__ */ jsxs8(Text8, { dimColor: true, children: [
1501
+ selectedSkill?.name,
1502
+ " \u2192 ",
1503
+ selectedAgent?.name
1504
+ ] }),
1505
+ /* @__PURE__ */ jsxs8(Box8, { marginTop: 1, flexDirection: "column", borderStyle: "single", paddingX: 1, children: [
1506
+ previewLines.map((line, i) => /* @__PURE__ */ jsx8(Text8, { dimColor: true, children: line.slice(0, 70) }, i)),
1507
+ preview.split("\n").length > maxVisible && /* @__PURE__ */ jsxs8(Text8, { dimColor: true, children: [
1508
+ "... (",
1509
+ preview.split("\n").length - maxVisible,
1510
+ " more lines)"
1511
+ ] })
1512
+ ] }),
1513
+ /* @__PURE__ */ jsx8(Box8, { marginTop: 1, children: /* @__PURE__ */ jsx8(Text8, { dimColor: true, children: "Enter=confirm translation Esc=back" }) })
1514
+ ] });
1515
+ }
1516
+ return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", children: [
1517
+ /* @__PURE__ */ jsx8(Text8, { bold: true, color: colors.primary, children: view === "skills" ? "TRANSLATE SKILL" : "SELECT TARGET AGENT" }),
1518
+ view === "skills" && /* @__PURE__ */ jsx8(Text8, { dimColor: true, children: "Select a skill to translate to another agent format" }),
1519
+ view === "agents" && selectedSkill && /* @__PURE__ */ jsxs8(Text8, { dimColor: true, children: [
1520
+ 'Translate "',
1521
+ selectedSkill.name,
1522
+ '" to which agent format?'
1523
+ ] }),
1524
+ skills.length === 0 && view === "skills" && /* @__PURE__ */ jsx8(Box8, { marginTop: 1, children: /* @__PURE__ */ jsx8(Text8, { color: "yellow", children: "No skills installed. Install some skills first with 'skillkit install'" }) }),
1525
+ /* @__PURE__ */ jsxs8(Box8, { marginTop: 1, flexDirection: "column", children: [
1526
+ start > 0 && /* @__PURE__ */ jsxs8(Text8, { dimColor: true, children: [
1527
+ " \u2191 ",
1528
+ start,
1529
+ " more"
1530
+ ] }),
1531
+ visible.map((item, i) => {
1532
+ const idx = start + i;
1533
+ const isSel = idx === sel;
1534
+ if (view === "skills") {
1535
+ const skill = item;
1536
+ return /* @__PURE__ */ jsxs8(Text8, { inverse: isSel, children: [
1537
+ isSel ? symbols.pointer : " ",
1538
+ " ",
1539
+ skill.name
1540
+ ] }, skill.name);
1541
+ } else {
1542
+ const agent = item;
1543
+ return /* @__PURE__ */ jsxs8(Text8, { inverse: isSel, children: [
1544
+ isSel ? symbols.pointer : " ",
1545
+ " ",
1546
+ agent.name.padEnd(20),
1547
+ " ",
1548
+ /* @__PURE__ */ jsxs8(Text8, { dimColor: true, children: [
1549
+ "(",
1550
+ agent.type,
1551
+ ")"
1552
+ ] })
1553
+ ] }, agent.type);
1554
+ }
1555
+ }),
1556
+ start + maxVisible < currentItems.length && /* @__PURE__ */ jsxs8(Text8, { dimColor: true, children: [
1557
+ " \u2193 ",
1558
+ currentItems.length - start - maxVisible,
1559
+ " more"
1560
+ ] })
1561
+ ] }),
1562
+ /* @__PURE__ */ jsx8(Box8, { marginTop: 1, children: /* @__PURE__ */ jsx8(Text8, { dimColor: true, children: view === "skills" ? "Enter=select skill q=quit" : "Enter=select agent Esc=back q=quit" }) })
1563
+ ] });
1564
+ }
1565
+
1566
+ // src/screens/Context.tsx
1567
+ import { useState as useState11, useEffect as useEffect7 } from "react";
1568
+ import { Box as Box9, Text as Text9, useInput as useInput7 } from "ink";
1569
+ import {
1570
+ loadContext,
1571
+ initContext,
1572
+ syncToAgent
1573
+ } from "@skillkit/core";
1574
+ import { getAllAdapters as getAllAdapters5 } from "@skillkit/agents";
1575
+ import { Fragment, jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
1576
+ function Context({ rows = 24 }) {
1577
+ const [view, setView] = useState11("overview");
1578
+ const [context, setContext] = useState11(null);
1579
+ const [loading, setLoading] = useState11(true);
1580
+ const [initializing, setInitializing] = useState11(false);
1581
+ const [syncing, setSyncing] = useState11(false);
1582
+ const [agents, setAgents] = useState11([]);
1583
+ const [sel, setSel] = useState11(0);
1584
+ const [message, setMessage] = useState11(null);
1585
+ const [error, setError] = useState11(null);
1586
+ const projectPath = process.cwd();
1587
+ useEffect7(() => {
1588
+ const load = async () => {
1589
+ setLoading(true);
1590
+ try {
1591
+ const ctx = loadContext(projectPath);
1592
+ setContext(ctx);
1593
+ const adapters = getAllAdapters5();
1594
+ const agentList = [];
1595
+ for (const a of adapters) {
1596
+ const detected = await a.isDetected();
1597
+ agentList.push({
1598
+ type: a.type,
1599
+ name: a.name,
1600
+ detected,
1601
+ synced: ctx?.agents?.synced?.includes(a.type)
1602
+ });
1603
+ }
1604
+ setAgents(agentList);
1605
+ } catch (err) {
1606
+ setError(err instanceof Error ? err.message : "Failed to load context");
1607
+ } finally {
1608
+ setLoading(false);
1609
+ }
1610
+ };
1611
+ load();
1612
+ }, [projectPath]);
1613
+ const maxVisible = Math.max(5, rows - 14);
1614
+ const start = Math.max(0, Math.min(sel - Math.floor(maxVisible / 2), agents.length - maxVisible));
1615
+ const visible = agents.slice(start, start + maxVisible);
1616
+ const handleInit = () => {
1617
+ setInitializing(true);
1618
+ setMessage(null);
1619
+ setError(null);
1620
+ try {
1621
+ const newContext = initContext(projectPath);
1622
+ setContext(newContext);
1623
+ setMessage("Context initialized successfully");
1624
+ } catch (err) {
1625
+ setError(err instanceof Error ? err.message : "Failed to initialize context");
1626
+ } finally {
1627
+ setInitializing(false);
1628
+ }
1629
+ };
1630
+ const handleSync = async (agentType) => {
1631
+ setSyncing(true);
1632
+ setMessage(null);
1633
+ setError(null);
1634
+ try {
1635
+ await syncToAgent(agentType, projectPath);
1636
+ setAgents(
1637
+ (prev) => prev.map(
1638
+ (a) => a.type === agentType ? { ...a, synced: true } : a
1639
+ )
1640
+ );
1641
+ setMessage(`Synced to ${agentType} successfully`);
1642
+ } catch (err) {
1643
+ setError(err instanceof Error ? err.message : "Sync failed");
1644
+ } finally {
1645
+ setSyncing(false);
1646
+ }
1647
+ };
1648
+ const handleSyncAll = async () => {
1649
+ setSyncing(true);
1650
+ setMessage(null);
1651
+ setError(null);
1652
+ try {
1653
+ const detectedAgents = agents.filter((a) => a.detected);
1654
+ for (const agent of detectedAgents) {
1655
+ await syncToAgent(agent.type, projectPath);
1656
+ }
1657
+ setAgents(
1658
+ (prev) => prev.map(
1659
+ (a) => a.detected ? { ...a, synced: true } : a
1660
+ )
1661
+ );
1662
+ setMessage(`Synced to ${detectedAgents.length} agents`);
1663
+ } catch (err) {
1664
+ setError(err instanceof Error ? err.message : "Sync failed");
1665
+ } finally {
1666
+ setSyncing(false);
1667
+ }
1668
+ };
1669
+ useInput7((input, key) => {
1670
+ if (loading || initializing || syncing) return;
1671
+ if (view === "sync") {
1672
+ if (key.upArrow) setSel((i) => Math.max(0, i - 1));
1673
+ else if (key.downArrow) setSel((i) => Math.min(agents.length - 1, i + 1));
1674
+ else if (key.return && agents[sel]) {
1675
+ handleSync(agents[sel].type);
1676
+ } else if (key.escape) {
1677
+ setView("overview");
1678
+ setSel(0);
1679
+ } else if (input === "a") {
1680
+ handleSyncAll();
1681
+ }
1682
+ } else if (view === "overview") {
1683
+ if (input === "i") handleInit();
1684
+ else if (input === "s") {
1685
+ setView("sync");
1686
+ setSel(0);
1687
+ }
1688
+ }
1689
+ });
1690
+ if (loading) {
1691
+ return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", children: [
1692
+ /* @__PURE__ */ jsx9(Text9, { bold: true, color: colors.primary, children: "PROJECT CONTEXT" }),
1693
+ /* @__PURE__ */ jsx9(Text9, { dimColor: true, children: "Loading..." })
1694
+ ] });
1695
+ }
1696
+ if (view === "sync") {
1697
+ return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", children: [
1698
+ /* @__PURE__ */ jsx9(Text9, { bold: true, color: colors.primary, children: "SYNC TO AGENTS" }),
1699
+ /* @__PURE__ */ jsx9(Text9, { dimColor: true, children: "Select an agent to sync your project context" }),
1700
+ message && /* @__PURE__ */ jsxs9(Text9, { color: "green", children: [
1701
+ "\u2713",
1702
+ " ",
1703
+ message
1704
+ ] }),
1705
+ error && /* @__PURE__ */ jsxs9(Text9, { color: "red", children: [
1706
+ "\u2717",
1707
+ " ",
1708
+ error
1709
+ ] }),
1710
+ syncing && /* @__PURE__ */ jsx9(Text9, { dimColor: true, children: "Syncing..." }),
1711
+ /* @__PURE__ */ jsxs9(Box9, { marginTop: 1, flexDirection: "column", children: [
1712
+ start > 0 && /* @__PURE__ */ jsxs9(Text9, { dimColor: true, children: [
1713
+ " \u2191 ",
1714
+ start,
1715
+ " more"
1716
+ ] }),
1717
+ visible.map((agent, i) => {
1718
+ const idx = start + i;
1719
+ const isSel = idx === sel;
1720
+ const status = agent.synced ? "(synced)" : agent.detected ? "(ready)" : "(not detected)";
1721
+ const statusColor = agent.synced ? "green" : agent.detected ? "yellow" : "gray";
1722
+ return /* @__PURE__ */ jsxs9(Text9, { inverse: isSel, children: [
1723
+ isSel ? symbols.pointer : " ",
1724
+ " ",
1725
+ agent.name.padEnd(20),
1726
+ /* @__PURE__ */ jsx9(Text9, { color: statusColor, children: status })
1727
+ ] }, agent.type);
1728
+ }),
1729
+ start + maxVisible < agents.length && /* @__PURE__ */ jsxs9(Text9, { dimColor: true, children: [
1730
+ " \u2193 ",
1731
+ agents.length - start - maxVisible,
1732
+ " more"
1733
+ ] })
1734
+ ] }),
1735
+ /* @__PURE__ */ jsx9(Box9, { marginTop: 1, children: /* @__PURE__ */ jsx9(Text9, { dimColor: true, children: "Enter=sync to agent a=sync all detected Esc=back q=quit" }) })
1736
+ ] });
1737
+ }
1738
+ const stack = context?.stack;
1739
+ const detectedCount = agents.filter((a) => a.detected).length;
1740
+ const syncedCount = agents.filter((a) => a.synced).length;
1741
+ return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", children: [
1742
+ /* @__PURE__ */ jsx9(Text9, { bold: true, color: colors.primary, children: "PROJECT CONTEXT" }),
1743
+ message && /* @__PURE__ */ jsxs9(Text9, { color: "green", children: [
1744
+ "\u2713",
1745
+ " ",
1746
+ message
1747
+ ] }),
1748
+ error && /* @__PURE__ */ jsxs9(Text9, { color: "red", children: [
1749
+ "\u2717",
1750
+ " ",
1751
+ error
1752
+ ] }),
1753
+ initializing && /* @__PURE__ */ jsx9(Text9, { dimColor: true, children: "Initializing..." }),
1754
+ !context && /* @__PURE__ */ jsxs9(Box9, { marginTop: 1, flexDirection: "column", children: [
1755
+ /* @__PURE__ */ jsx9(Text9, { color: "yellow", children: "No context found." }),
1756
+ /* @__PURE__ */ jsx9(Text9, { dimColor: true, children: "Press 'i' to initialize and analyze your project." })
1757
+ ] }),
1758
+ context && /* @__PURE__ */ jsxs9(Fragment, { children: [
1759
+ /* @__PURE__ */ jsxs9(Box9, { marginTop: 1, flexDirection: "column", children: [
1760
+ /* @__PURE__ */ jsx9(Text9, { bold: true, children: "Project:" }),
1761
+ /* @__PURE__ */ jsxs9(Text9, { children: [
1762
+ " Name: ",
1763
+ context.project?.name || "Unknown"
1764
+ ] }),
1765
+ /* @__PURE__ */ jsxs9(Text9, { children: [
1766
+ " Type: ",
1767
+ context.project?.type || "Not detected"
1768
+ ] })
1769
+ ] }),
1770
+ /* @__PURE__ */ jsxs9(Box9, { marginTop: 1, flexDirection: "column", children: [
1771
+ /* @__PURE__ */ jsx9(Text9, { bold: true, children: "Stack:" }),
1772
+ stack?.languages && stack.languages.length > 0 && /* @__PURE__ */ jsxs9(Text9, { children: [
1773
+ " Languages: ",
1774
+ stack.languages.map((l) => l.name).join(", ")
1775
+ ] }),
1776
+ stack?.frameworks && stack.frameworks.length > 0 && /* @__PURE__ */ jsxs9(Text9, { children: [
1777
+ " Frameworks: ",
1778
+ stack.frameworks.map((f) => f.name).join(", ")
1779
+ ] }),
1780
+ stack?.libraries && stack.libraries.length > 0 && /* @__PURE__ */ jsxs9(Text9, { children: [
1781
+ " Libraries: ",
1782
+ stack.libraries.slice(0, 5).map((l) => l.name).join(", "),
1783
+ stack.libraries.length > 5 ? "..." : ""
1784
+ ] }),
1785
+ !stack?.languages?.length && !stack?.frameworks?.length && /* @__PURE__ */ jsx9(Text9, { dimColor: true, children: " No stack detected. Press 'i' to re-analyze." })
1786
+ ] }),
1787
+ /* @__PURE__ */ jsxs9(Box9, { marginTop: 1, flexDirection: "column", children: [
1788
+ /* @__PURE__ */ jsx9(Text9, { bold: true, children: "Agents:" }),
1789
+ /* @__PURE__ */ jsxs9(Text9, { children: [
1790
+ " Detected: ",
1791
+ detectedCount,
1792
+ " agents"
1793
+ ] }),
1794
+ /* @__PURE__ */ jsxs9(Text9, { children: [
1795
+ " Synced: ",
1796
+ syncedCount,
1797
+ " agents"
1798
+ ] }),
1799
+ detectedCount > 0 && /* @__PURE__ */ jsxs9(Text9, { dimColor: true, children: [
1800
+ " (",
1801
+ agents.filter((a) => a.detected).map((a) => a.name).join(", "),
1802
+ ")"
1803
+ ] })
1804
+ ] })
1805
+ ] }),
1806
+ /* @__PURE__ */ jsxs9(Box9, { marginTop: 1, flexDirection: "column", children: [
1807
+ /* @__PURE__ */ jsx9(Text9, { bold: true, children: "Actions:" }),
1808
+ /* @__PURE__ */ jsx9(Text9, { dimColor: true, children: " [i] Initialize/refresh context" }),
1809
+ /* @__PURE__ */ jsx9(Text9, { dimColor: true, children: " [s] Sync to agents" })
1810
+ ] }),
1811
+ /* @__PURE__ */ jsx9(Box9, { marginTop: 1, children: /* @__PURE__ */ jsx9(Text9, { dimColor: true, children: "i=init context s=sync to agents q=quit" }) })
1812
+ ] });
1813
+ }
1814
+
1815
+ // src/App.tsx
1816
+ import { jsx as jsx10, jsxs as jsxs10 } from "react/jsx-runtime";
1817
+ function App() {
1818
+ const [screen, setScreen] = useState12("home");
1819
+ const { exit } = useApp();
1820
+ const { stdout } = useStdout();
1821
+ const cols = stdout?.columns || 80;
1822
+ const rows = stdout?.rows || 24;
1823
+ const showSidebar = cols >= 70;
1824
+ useInput8((input, key) => {
1825
+ if (input === "q") {
1826
+ exit();
1827
+ return;
1828
+ }
1829
+ if (key.escape) {
1830
+ setScreen("home");
1831
+ return;
1832
+ }
1833
+ if (input === "h") setScreen("home");
1834
+ if (input === "b") setScreen("browse");
1835
+ if (input === "l") setScreen("installed");
1836
+ if (input === "s") setScreen("sync");
1837
+ if (input === ",") setScreen("settings");
1838
+ if (input === "r") setScreen("recommend");
1839
+ if (input === "t") setScreen("translate");
1840
+ if (input === "c") setScreen("context");
1841
+ });
1842
+ const renderScreen = () => {
1843
+ switch (screen) {
1844
+ case "home":
1845
+ return /* @__PURE__ */ jsx10(Home, { onNavigate: setScreen, cols, rows });
1846
+ case "browse":
1847
+ return /* @__PURE__ */ jsx10(Browse, { cols, rows });
1848
+ case "installed":
1849
+ return /* @__PURE__ */ jsx10(Installed, { cols, rows });
1850
+ case "sync":
1851
+ return /* @__PURE__ */ jsx10(Sync, { cols, rows });
1852
+ case "settings":
1853
+ return /* @__PURE__ */ jsx10(Settings, { cols, rows });
1854
+ case "recommend":
1855
+ return /* @__PURE__ */ jsx10(Recommend, { cols, rows });
1856
+ case "translate":
1857
+ return /* @__PURE__ */ jsx10(Translate, { cols, rows });
1858
+ case "context":
1859
+ return /* @__PURE__ */ jsx10(Context, { cols, rows });
1860
+ }
1861
+ };
1862
+ const contentHeight = rows - 2;
1863
+ return /* @__PURE__ */ jsxs10(Box10, { flexDirection: "column", height: rows, children: [
1864
+ /* @__PURE__ */ jsxs10(Box10, { flexDirection: "row", height: contentHeight, children: [
1865
+ showSidebar && /* @__PURE__ */ jsx10(Sidebar, { screen, onNavigate: setScreen }),
1866
+ /* @__PURE__ */ jsx10(Box10, { flexDirection: "column", flexGrow: 1, marginLeft: 1, children: renderScreen() })
1867
+ ] }),
1868
+ /* @__PURE__ */ jsx10(Box10, { children: /* @__PURE__ */ jsx10(Text10, { dimColor: true, children: "h Home b Browse r Rec t Trans c Ctx l List s Sync , Config q Quit" }) })
1869
+ ] });
1870
+ }
1871
+
1872
+ // src/components/Header.tsx
1873
+ import { Box as Box11, Text as Text11 } from "ink";
1874
+ import { jsx as jsx11, jsxs as jsxs11 } from "react/jsx-runtime";
1875
+ function Header({ title, subtitle, count }) {
1876
+ return /* @__PURE__ */ jsxs11(Box11, { flexDirection: "column", marginBottom: 1, children: [
1877
+ /* @__PURE__ */ jsxs11(Box11, { justifyContent: "space-between", children: [
1878
+ /* @__PURE__ */ jsx11(Text11, { color: colors.primary, bold: true, children: title.toUpperCase() }),
1879
+ count !== void 0 && /* @__PURE__ */ jsxs11(Text11, { color: colors.secondaryDim, children: [
1880
+ symbols.star,
1881
+ " ",
1882
+ count
1883
+ ] })
1884
+ ] }),
1885
+ subtitle && /* @__PURE__ */ jsx11(Text11, { color: colors.secondaryDim, dimColor: true, children: subtitle })
1886
+ ] });
1887
+ }
1888
+
1889
+ // src/components/SkillList.tsx
1890
+ import { Box as Box12, Text as Text12 } from "ink";
1891
+ import { jsx as jsx12, jsxs as jsxs12 } from "react/jsx-runtime";
1892
+ function formatInstalls(count) {
1893
+ if (count >= 1e3) {
1894
+ return `${(count / 1e3).toFixed(1)}K`;
1895
+ }
1896
+ return String(count);
1897
+ }
1898
+ function SkillList({
1899
+ skills,
1900
+ selectedIndex,
1901
+ showInstalls = false,
1902
+ showRank = false,
1903
+ showSource = true,
1904
+ maxVisible = 10
1905
+ }) {
1906
+ if (skills.length === 0) {
1907
+ return /* @__PURE__ */ jsx12(Box12, { children: /* @__PURE__ */ jsx12(Text12, { color: colors.secondaryDim, dimColor: true, children: "No skills found" }) });
1908
+ }
1909
+ const startIndex = Math.max(0, selectedIndex - Math.floor(maxVisible / 2));
1910
+ const visibleSkills = skills.slice(startIndex, startIndex + maxVisible);
1911
+ const actualStartIndex = startIndex;
1912
+ return /* @__PURE__ */ jsxs12(Box12, { flexDirection: "column", children: [
1913
+ showRank && /* @__PURE__ */ jsx12(Box12, { marginBottom: 1, children: /* @__PURE__ */ jsxs12(Text12, { color: colors.secondaryDim, children: [
1914
+ " # SKILL",
1915
+ showSource && " SOURCE",
1916
+ showInstalls && " INSTALLS"
1917
+ ] }) }),
1918
+ visibleSkills.map((skill, idx) => {
1919
+ const realIndex = actualStartIndex + idx;
1920
+ const isSelected = realIndex === selectedIndex;
1921
+ const skillName = skill.name.padEnd(28).slice(0, 28);
1922
+ const sourceName = skill.source ? skill.source.slice(0, 25) : "";
1923
+ return /* @__PURE__ */ jsxs12(Box12, { children: [
1924
+ /* @__PURE__ */ jsxs12(
1925
+ Text12,
1926
+ {
1927
+ color: isSelected ? colors.primary : colors.secondary,
1928
+ bold: isSelected,
1929
+ inverse: isSelected,
1930
+ children: [
1931
+ isSelected ? symbols.pointer : " ",
1932
+ showRank ? String(realIndex + 1).padStart(2, " ") : "",
1933
+ " ",
1934
+ skillName
1935
+ ]
1936
+ }
1937
+ ),
1938
+ showSource && /* @__PURE__ */ jsxs12(Text12, { color: colors.secondaryDim, dimColor: !isSelected, children: [
1939
+ " ",
1940
+ sourceName
1941
+ ] }),
1942
+ showInstalls && skill.installs !== void 0 && /* @__PURE__ */ jsx12(Text12, { color: colors.secondaryDim, children: formatInstalls(skill.installs).padStart(8) })
1943
+ ] }, `${skill.source}-${skill.name}`);
1944
+ }),
1945
+ skills.length > maxVisible && /* @__PURE__ */ jsx12(Box12, { marginTop: 1, children: /* @__PURE__ */ jsxs12(Text12, { color: colors.secondaryDim, dimColor: true, children: [
1946
+ "Showing ",
1947
+ startIndex + 1,
1948
+ "-",
1949
+ Math.min(startIndex + maxVisible, skills.length),
1950
+ " of ",
1951
+ skills.length
1952
+ ] }) })
1953
+ ] });
1954
+ }
1955
+
1956
+ // src/components/StatusBar.tsx
1957
+ import { Box as Box13, Text as Text13 } from "ink";
1958
+ import { jsx as jsx13, jsxs as jsxs13 } from "react/jsx-runtime";
1959
+ function StatusBar({ shortcuts, message }) {
1960
+ return /* @__PURE__ */ jsxs13(
1961
+ Box13,
1962
+ {
1963
+ borderStyle: "single",
1964
+ borderColor: colors.borderDim,
1965
+ borderTop: true,
1966
+ borderBottom: false,
1967
+ borderLeft: false,
1968
+ borderRight: false,
1969
+ paddingX: 1,
1970
+ justifyContent: "space-between",
1971
+ children: [
1972
+ /* @__PURE__ */ jsx13(Box13, { gap: 2, children: shortcuts.map((shortcut, idx) => /* @__PURE__ */ jsxs13(Box13, { gap: 1, children: [
1973
+ /* @__PURE__ */ jsx13(Text13, { color: colors.primary, bold: true, children: shortcut.key }),
1974
+ /* @__PURE__ */ jsx13(Text13, { color: colors.secondaryDim, children: shortcut.label })
1975
+ ] }, idx)) }),
1976
+ message && /* @__PURE__ */ jsxs13(Text13, { color: colors.success, children: [
1977
+ symbols.check,
1978
+ " ",
1979
+ message
1980
+ ] })
1981
+ ]
1982
+ }
1983
+ );
1984
+ }
1985
+
1986
+ // src/components/SearchInput.tsx
1987
+ import { Box as Box14, Text as Text14 } from "ink";
1988
+ import TextInput from "ink-text-input";
1989
+ import { jsx as jsx14, jsxs as jsxs14 } from "react/jsx-runtime";
1990
+ function SearchInput({
1991
+ value,
1992
+ onChange,
1993
+ placeholder = "Search skills...",
1994
+ isFocused = false
1995
+ }) {
1996
+ return /* @__PURE__ */ jsxs14(
1997
+ Box14,
1998
+ {
1999
+ borderStyle: "single",
2000
+ borderColor: isFocused ? colors.primary : colors.borderDim,
2001
+ paddingX: 1,
2002
+ children: [
2003
+ /* @__PURE__ */ jsx14(Text14, { color: colors.secondaryDim, children: "/ " }),
2004
+ isFocused ? /* @__PURE__ */ jsx14(
2005
+ TextInput,
2006
+ {
2007
+ value,
2008
+ onChange,
2009
+ placeholder
2010
+ }
2011
+ ) : /* @__PURE__ */ jsx14(Text14, { color: value ? colors.secondary : colors.secondaryDim, children: value || placeholder }),
2012
+ /* @__PURE__ */ jsx14(Box14, { flexGrow: 1 }),
2013
+ /* @__PURE__ */ jsx14(Text14, { color: colors.secondaryDim, children: "/" })
2014
+ ]
2015
+ }
2016
+ );
2017
+ }
2018
+
2019
+ // src/hooks/useKeyboard.ts
2020
+ import { useState as useState13, useCallback as useCallback3, useEffect as useEffect8 } from "react";
2021
+ import { useInput as useInput9, useApp as useApp2 } from "ink";
2022
+ function useKeyboard(options = {}) {
2023
+ const { exit } = useApp2();
2024
+ useInput9((input, key) => {
2025
+ if (options.disabled) return;
2026
+ if (input === "q" || key.ctrl && input === "c") {
2027
+ exit();
2028
+ return;
2029
+ }
2030
+ if (key.escape && options.onBack) {
2031
+ options.onBack();
2032
+ return;
2033
+ }
2034
+ if (key.return && options.onSelect) {
2035
+ options.onSelect();
2036
+ return;
2037
+ }
2038
+ if (key.upArrow && options.onUp) {
2039
+ options.onUp();
2040
+ return;
2041
+ }
2042
+ if (key.downArrow && options.onDown) {
2043
+ options.onDown();
2044
+ return;
2045
+ }
2046
+ if (input === "/" && options.onSearch) {
2047
+ options.onSearch();
2048
+ return;
2049
+ }
2050
+ if (input === "i" && options.onInstall) {
2051
+ options.onInstall();
2052
+ return;
2053
+ }
2054
+ if (input === "h" && options.onNavigate) {
2055
+ options.onNavigate("home");
2056
+ } else if (input === "b" && options.onNavigate) {
2057
+ options.onNavigate("browse");
2058
+ } else if (input === "l" && options.onNavigate) {
2059
+ options.onNavigate("installed");
2060
+ } else if (input === "s" && options.onNavigate) {
2061
+ options.onNavigate("sync");
2062
+ } else if (input === "," && options.onNavigate) {
2063
+ options.onNavigate("settings");
2064
+ }
2065
+ });
2066
+ }
2067
+ function useListNavigation(listLength, initialIndex = 0) {
2068
+ const [selectedIndex, setSelectedIndex] = useState13(initialIndex);
2069
+ useEffect8(() => {
2070
+ if (selectedIndex >= listLength && listLength > 0) {
2071
+ setSelectedIndex(listLength - 1);
2072
+ }
2073
+ }, [listLength, selectedIndex]);
2074
+ const moveUp = useCallback3(() => {
2075
+ setSelectedIndex((prev) => Math.max(0, prev - 1));
2076
+ }, []);
2077
+ const moveDown = useCallback3(() => {
2078
+ setSelectedIndex((prev) => Math.min(listLength - 1, prev + 1));
2079
+ }, [listLength]);
2080
+ const reset = useCallback3(() => {
2081
+ setSelectedIndex(0);
2082
+ }, []);
2083
+ return { selectedIndex, setSelectedIndex, moveUp, moveDown, reset };
2084
+ }
2085
+
2086
+ // src/index.tsx
2087
+ import { jsx as jsx15 } from "react/jsx-runtime";
2088
+ function startTUI() {
2089
+ process.stdout.write("\x1B[2J\x1B[0f");
2090
+ const { waitUntilExit, clear } = render(/* @__PURE__ */ jsx15(App, {}), {
2091
+ exitOnCtrlC: true
2092
+ });
2093
+ return waitUntilExit().then(() => {
2094
+ clear();
2095
+ });
2096
+ }
2097
+ export {
2098
+ App,
2099
+ Browse,
2100
+ Context,
2101
+ Header,
2102
+ Home,
2103
+ Installed,
2104
+ Recommend,
2105
+ SearchInput,
2106
+ Settings,
2107
+ Sidebar,
2108
+ SkillList,
2109
+ StatusBar,
2110
+ Sync,
2111
+ Translate,
2112
+ colors,
2113
+ logo,
2114
+ startTUI,
2115
+ symbols,
2116
+ useKeyboard,
2117
+ useListNavigation,
2118
+ useMarketplace,
2119
+ useRecommend,
2120
+ useSkills
2121
+ };
2122
+ //# sourceMappingURL=index.js.map