@skillkit/tui 2.0.0 → 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -34,7 +34,7 @@ pnpm ui
34
34
  - **Animated UI** - Smooth entrance animations and transitions
35
35
  - **Browse Skills** - Explore skill repositories with search and filtering
36
36
  - **Smart Recommendations** - AI-powered skill suggestions with match scores
37
- - **Cross-Agent Translation** - Convert skills between 17+ agent formats
37
+ - **Cross-Agent Translation** - Convert skills between 32 agent formats
38
38
  - **Team Collaboration** - Share and sync skills across teams
39
39
  - **Responsive Design** - Adapts to any terminal size
40
40
 
package/dist/index.d.ts CHANGED
@@ -8,6 +8,9 @@ interface SkillItem {
8
8
  source?: string;
9
9
  installs?: number;
10
10
  enabled?: boolean;
11
+ quality?: number;
12
+ grade?: string;
13
+ warnings?: number;
11
14
  }
12
15
  interface RepoInfo {
13
16
  source: string;
@@ -111,7 +114,7 @@ interface AgentLogo {
111
114
  company: string;
112
115
  }
113
116
  /**
114
- * All 17 supported agents with monochromatic Unicode logos
117
+ * All 32 supported agents with monochromatic Unicode logos
115
118
  */
116
119
  declare const AGENT_LOGOS: Record<string, AgentLogo>;
117
120
  /**
@@ -367,13 +370,10 @@ declare function getDetectedAgents(state: AgentsState): AgentStatus[];
367
370
  */
368
371
  declare function getAgentAdapter(type: AgentType): _skillkit_agents.AgentAdapter;
369
372
  /**
370
- * Total number of supported agents
373
+ * Total number of supported agents (derived from adapters)
371
374
  */
372
- declare const TOTAL_AGENTS = 17;
375
+ declare const TOTAL_AGENTS: number;
373
376
 
374
- /**
375
- * Default popular repos - can be extended via config.marketplaceSources
376
- */
377
377
  declare const DEFAULT_REPOS: RepoInfo[];
378
378
  /**
379
379
  * Marketplace state
package/dist/index.js CHANGED
@@ -5,6 +5,7 @@ import { createRoot } from "@opentui/react";
5
5
  // src/App.tsx
6
6
  import { useState as useState15, useCallback as useCallback9, useEffect as useEffect13 } from "react";
7
7
  import { useKeyboard as useKeyboard8 } from "@opentui/react";
8
+ import { exec } from "child_process";
8
9
 
9
10
  // src/state/types.ts
10
11
  var NAV_KEYS = {
@@ -155,6 +156,21 @@ var AGENT_LOGOS = {
155
156
  "trae": { icon: "\u25C6", name: "Trae", company: "ByteDance" },
156
157
  "antigravity": { icon: "\u229B", name: "Antigravity", company: "" },
157
158
  "clawdbot": { icon: "\u27D0", name: "Clawdbot", company: "" },
159
+ "cline": { icon: "\u2295", name: "Cline", company: "" },
160
+ "codebuddy": { icon: "\u2297", name: "CodeBuddy", company: "" },
161
+ "commandcode": { icon: "\u2298", name: "CommandCode", company: "" },
162
+ "continue": { icon: "\u229E", name: "Continue", company: "" },
163
+ "crush": { icon: "\u229F", name: "Crush", company: "" },
164
+ "factory": { icon: "\u22A0", name: "Factory", company: "" },
165
+ "mcpjam": { icon: "\u22A1", name: "MCPJam", company: "" },
166
+ "mux": { icon: "\u22A2", name: "Mux", company: "" },
167
+ "neovate": { icon: "\u22A3", name: "Neovate", company: "" },
168
+ "openhands": { icon: "\u22A4", name: "OpenHands", company: "" },
169
+ "pi": { icon: "\u22A5", name: "Pi", company: "" },
170
+ "qoder": { icon: "\u22A6", name: "Qoder", company: "" },
171
+ "qwen": { icon: "\u22A7", name: "Qwen", company: "Alibaba" },
172
+ "vercel": { icon: "\u25B2", name: "Vercel", company: "Vercel" },
173
+ "zencoder": { icon: "\u22A9", name: "Zencoder", company: "" },
158
174
  "universal": { icon: "\u25CF", name: "Universal", company: "" }
159
175
  };
160
176
  var symbols = {
@@ -525,7 +541,7 @@ function isNavKey(key) {
525
541
  }
526
542
 
527
543
  // src/state/skills.ts
528
- import { findAllSkills } from "@skillkit/core";
544
+ import { findAllSkills, evaluateSkillDirectory, getQualityGrade } from "@skillkit/core";
529
545
  import { rmSync } from "fs";
530
546
  import { resolve, normalize } from "path";
531
547
  function createSkillsState() {
@@ -539,12 +555,18 @@ function loadSkills(agentType) {
539
555
  try {
540
556
  const searchDirs = getSearchDirs(agentType);
541
557
  const foundSkills = findAllSkills(searchDirs);
542
- const skillItems = foundSkills.map((s) => ({
543
- name: s.name,
544
- description: s.description,
545
- source: s.metadata?.source,
546
- enabled: s.enabled
547
- }));
558
+ const skillItems = foundSkills.map((s) => {
559
+ const quality = evaluateSkillDirectory(s.path);
560
+ return {
561
+ name: s.name,
562
+ description: s.description,
563
+ source: s.metadata?.source,
564
+ enabled: s.enabled,
565
+ quality: quality?.overall,
566
+ grade: quality ? getQualityGrade(quality.overall) : void 0,
567
+ warnings: quality?.warnings.length
568
+ };
569
+ });
548
570
  return {
549
571
  skills: skillItems,
550
572
  loading: false,
@@ -635,44 +657,59 @@ function getDetectedAgents(state) {
635
657
  function getAgentAdapter(type) {
636
658
  return getAdapter2(type);
637
659
  }
638
- var TOTAL_AGENTS = 17;
660
+ var TOTAL_AGENTS = getAllAdapters().length;
639
661
 
640
662
  // src/state/marketplace.ts
641
663
  import { detectProvider, loadConfig as loadConfig3, extractFrontmatter } from "@skillkit/core";
642
664
  import { readFileSync as readFileSync2, existsSync, rmSync as rmSync2 } from "fs";
643
665
  import { join as join2 } from "path";
644
- var DEFAULT_REPOS = [
645
- { source: "anthropics/skills", name: "Anthropic Official" },
646
- { source: "vercel-labs/agent-skills", name: "Vercel Labs" },
647
- { source: "expo/skills", name: "Expo / React Native" },
648
- { source: "remotion-dev/skills", name: "Remotion Video" },
649
- { source: "ComposioHQ/awesome-claude-skills", name: "Composio Awesome" },
650
- { source: "travisvn/awesome-claude-skills", name: "Travis Awesome" },
651
- { source: "mhattingpete/claude-skills-marketplace", name: "Skills Marketplace" },
652
- { source: "coreyhaines31/marketingskills", name: "Marketing Skills" },
653
- { source: "obra/superpowers", name: "Superpowers TDD" },
654
- { source: "softaworks/agent-toolkit", name: "Softaworks Toolkit" },
655
- { source: "wshobson/agents", name: "Dev Patterns" },
656
- { source: "langgenius/dify", name: "Dify Frontend" },
657
- { source: "trailofbits/skills", name: "Trail of Bits Security" },
658
- { source: "better-auth/skills", name: "Better Auth" },
659
- { source: "onmax/nuxt-skills", name: "Nuxt / Vue" },
660
- { source: "hyf0/vue-skills", name: "Vue Best Practices" },
661
- { source: "jezweb/claude-skills", name: "Cloudflare / TanStack" },
662
- { source: "elysiajs/skills", name: "ElysiaJS / Bun" },
663
- { source: "kadajett/agent-nestjs-skills", name: "NestJS" },
664
- { source: "callstackincubator/agent-skills", name: "React Native" },
665
- { source: "cloudai-x/threejs-skills", name: "Three.js" },
666
- { source: "emalorenzo/three-agent-skills", name: "Three.js Advanced" },
667
- { source: "dimillian/skills", name: "SwiftUI iOS" },
668
- { source: "stripe/ai", name: "Stripe Payments" },
669
- { source: "waynesutton/convexskills", name: "Convex Backend" },
670
- { source: "kepano/obsidian-skills", name: "Obsidian Notes" },
671
- { source: "jimliu/baoyu-skills", name: "Baoyu Tools" },
672
- { source: "giuseppe-trisciuoglio/developer-kit", name: "Shadcn / Radix" },
673
- { source: "openrouterteam/agent-skills", name: "OpenRouter SDK" },
674
- { source: "intellectronica/agent-skills", name: "Context7" }
675
- ];
666
+
667
+ // ../../marketplace/sources.json
668
+ var sources_default = {
669
+ $schema: "./sources-schema.json",
670
+ version: 1,
671
+ updatedAt: "2026-01-26",
672
+ description: "Curated skill repository sources - THE SINGLE SOURCE OF TRUTH",
673
+ sources: [
674
+ { source: "anthropics/skills", name: "Anthropic Official", official: true },
675
+ { source: "vercel-labs/agent-skills", name: "Vercel Labs", official: true },
676
+ { source: "expo/skills", name: "Expo / React Native", official: true },
677
+ { source: "remotion-dev/skills", name: "Remotion Video", official: true },
678
+ { source: "supabase/agent-skills", name: "Supabase", official: true },
679
+ { source: "stripe/ai", name: "Stripe Payments", official: true },
680
+ { source: "ComposioHQ/awesome-claude-skills", name: "Composio Awesome" },
681
+ { source: "travisvn/awesome-claude-skills", name: "Travis Awesome" },
682
+ { source: "mhattingpete/claude-skills-marketplace", name: "Skills Marketplace" },
683
+ { source: "coreyhaines31/marketingskills", name: "Marketing Skills" },
684
+ { source: "obra/superpowers", name: "Superpowers TDD" },
685
+ { source: "softaworks/agent-toolkit", name: "Softaworks Toolkit" },
686
+ { source: "wshobson/agents", name: "Dev Patterns" },
687
+ { source: "langgenius/dify", name: "Dify Frontend" },
688
+ { source: "trailofbits/skills", name: "Trail of Bits Security" },
689
+ { source: "better-auth/skills", name: "Better Auth" },
690
+ { source: "onmax/nuxt-skills", name: "Nuxt / Vue" },
691
+ { source: "hyf0/vue-skills", name: "Vue Best Practices" },
692
+ { source: "jezweb/claude-skills", name: "Cloudflare / TanStack" },
693
+ { source: "elysiajs/skills", name: "ElysiaJS / Bun" },
694
+ { source: "kadajett/agent-nestjs-skills", name: "NestJS" },
695
+ { source: "callstackincubator/agent-skills", name: "React Native" },
696
+ { source: "cloudai-x/threejs-skills", name: "Three.js" },
697
+ { source: "emalorenzo/three-agent-skills", name: "Three.js Advanced" },
698
+ { source: "dimillian/skills", name: "SwiftUI iOS" },
699
+ { source: "waynesutton/convexskills", name: "Convex Backend" },
700
+ { source: "kepano/obsidian-skills", name: "Obsidian Notes" },
701
+ { source: "jimliu/baoyu-skills", name: "Baoyu Tools" },
702
+ { source: "giuseppe-trisciuoglio/developer-kit", name: "Shadcn / Radix" },
703
+ { source: "openrouterteam/agent-skills", name: "OpenRouter SDK" },
704
+ { source: "intellectronica/agent-skills", name: "Context7" }
705
+ ]
706
+ };
707
+
708
+ // src/state/marketplace.ts
709
+ var DEFAULT_REPOS = sources_default.sources.map((s) => ({
710
+ source: s.source,
711
+ name: s.name
712
+ }));
676
713
  function getMarketplaceRepos() {
677
714
  try {
678
715
  const config = loadConfig3();
@@ -991,17 +1028,10 @@ function Browse({ onNavigate, cols = 80, rows = 24 }) {
991
1028
  const actualIdx = startIdx + idx;
992
1029
  const selected = actualIdx === selectedIndex;
993
1030
  const indicator = selected ? "\u25B8" : " ";
994
- const available = Math.max(0, contentWidth - repo.name.length - 6);
995
- const safeSlice = Math.max(0, available - 3);
996
- const displaySource = available > 0 && repo.source.length > available ? repo.source.slice(0, safeSlice) + "..." : repo.source;
997
- return /* @__PURE__ */ jsxs4("box", { flexDirection: "row", children: [
998
- /* @__PURE__ */ jsx4("text", { fg: terminalColors.text, children: indicator }),
999
- /* @__PURE__ */ jsx4("text", { fg: selected ? terminalColors.accent : terminalColors.text, children: repo.name }),
1000
- /* @__PURE__ */ jsxs4("text", { fg: terminalColors.textMuted, children: [
1001
- " \xB7 ",
1002
- displaySource
1003
- ] })
1004
- ] }, repo.source);
1031
+ const maxSourceLen = Math.max(0, contentWidth - repo.name.length - 6);
1032
+ const displaySource = maxSourceLen > 3 && repo.source.length > maxSourceLen ? repo.source.slice(0, maxSourceLen - 3) + "..." : repo.source;
1033
+ const line = `${indicator}${repo.name} \xB7 ${displaySource}`;
1034
+ return /* @__PURE__ */ jsx4("text", { fg: selected ? terminalColors.accent : terminalColors.text, children: line }, repo.source);
1005
1035
  }) }),
1006
1036
  filteredRepos.length > maxVisible && /* @__PURE__ */ jsxs4("text", { fg: terminalColors.textMuted, children: [
1007
1037
  "\n",
@@ -1160,15 +1190,11 @@ function Installed({ onNavigate, cols = 80, rows = 24 }) {
1160
1190
  const selected = actualIdx === selectedIndex;
1161
1191
  const indicator = selected ? "\u25B8" : " ";
1162
1192
  const statusIcon = skill.enabled !== false ? "\u25CF" : "\u25CB";
1163
- const statusColor = skill.enabled !== false ? terminalColors.success : terminalColors.textMuted;
1164
- return /* @__PURE__ */ jsxs5("box", { flexDirection: "row", children: [
1165
- /* @__PURE__ */ jsx5("text", { fg: terminalColors.text, children: indicator }),
1166
- /* @__PURE__ */ jsxs5("text", { fg: statusColor, children: [
1167
- statusIcon,
1168
- " "
1169
- ] }),
1170
- /* @__PURE__ */ jsx5("text", { fg: selected ? terminalColors.accent : terminalColors.text, children: skill.name })
1171
- ] }, skill.name);
1193
+ const qualityBadge = skill.grade ? ` [${skill.grade}]` : "";
1194
+ const warningIndicator = skill.warnings && skill.warnings > 2 ? " \u26A0" : "";
1195
+ const line = `${indicator}${statusIcon} ${skill.name}${qualityBadge}${warningIndicator}`;
1196
+ const badgeColor = skill.quality !== void 0 ? skill.quality >= 80 ? terminalColors.success : skill.quality >= 60 ? terminalColors.warning : terminalColors.error : terminalColors.text;
1197
+ return /* @__PURE__ */ jsx5("text", { fg: selected ? terminalColors.accent : badgeColor, children: line }, skill.name);
1172
1198
  }),
1173
1199
  filteredSkills.length > maxVisible && /* @__PURE__ */ jsxs5("text", { fg: terminalColors.textMuted, children: [
1174
1200
  "\n",
@@ -1268,22 +1294,16 @@ function Marketplace({ onNavigate, cols = 80, rows = 24 }) {
1268
1294
  /* @__PURE__ */ jsx6("text", { children: " " })
1269
1295
  ] }),
1270
1296
  animPhase >= 3 && /* @__PURE__ */ jsxs6("box", { flexDirection: "column", children: [
1271
- /* @__PURE__ */ jsxs6("box", { flexDirection: "row", children: [
1272
- /* @__PURE__ */ jsx6("text", { fg: terminalColors.text, children: "Featured" }),
1273
- /* @__PURE__ */ jsx6("text", { fg: terminalColors.textMuted, children: " \xB7 trending" })
1297
+ /* @__PURE__ */ jsxs6("text", { fg: terminalColors.text, children: [
1298
+ "Featured \xB7 ",
1299
+ /* @__PURE__ */ jsx6("text", { fg: terminalColors.textMuted, children: "trending" })
1274
1300
  ] }),
1275
1301
  /* @__PURE__ */ jsx6("text", { children: " " }),
1276
1302
  visibleFeatured.map((item, idx) => {
1277
1303
  const selected = idx === selectedIndex;
1278
1304
  const indicator = selected ? "\u25B8" : " ";
1279
- return /* @__PURE__ */ jsxs6("box", { flexDirection: "row", children: [
1280
- /* @__PURE__ */ jsx6("text", { fg: terminalColors.text, children: indicator }),
1281
- /* @__PURE__ */ jsx6("text", { fg: selected ? terminalColors.accent : terminalColors.text, children: item.name }),
1282
- /* @__PURE__ */ jsxs6("text", { fg: terminalColors.recommend, children: [
1283
- " \u2605",
1284
- item.stars
1285
- ] })
1286
- ] }, item.source);
1305
+ const line = `${indicator}${item.name} \u2605${item.stars}`;
1306
+ return /* @__PURE__ */ jsx6("text", { fg: selected ? terminalColors.accent : terminalColors.text, children: line }, item.source);
1287
1307
  }),
1288
1308
  featured.length > maxVisible && /* @__PURE__ */ jsxs6("text", { fg: terminalColors.textMuted, children: [
1289
1309
  "\n",
@@ -1339,12 +1359,12 @@ function Recommend({ onNavigate, cols = 80, rows = 24 }) {
1339
1359
  return () => clearInterval(interval);
1340
1360
  }, [analyzing]);
1341
1361
  const recommendations = useMemo6(() => [
1342
- { name: "tdd-workflow", reason: "Based on your test files and coverage patterns", confidence: 95 },
1343
- { name: "react-patterns", reason: "Detected React in package.json with hooks usage", confidence: 88 },
1344
- { name: "typescript-strict", reason: "tsconfig.json found with strict mode disabled", confidence: 82 },
1345
- { name: "git-workflow", reason: ".git directory with complex branching detected", confidence: 75 },
1346
- { name: "api-design", reason: "REST endpoints found in your codebase", confidence: 68 },
1347
- { name: "docker-compose", reason: "docker-compose.yml detected for orchestration", confidence: 62 }
1362
+ { name: "tdd-workflow", reason: "Based on your test files and coverage patterns", confidence: 95, quality: 92, grade: "A" },
1363
+ { name: "react-patterns", reason: "Detected React in package.json with hooks usage", confidence: 88, quality: 85, grade: "B" },
1364
+ { name: "typescript-strict", reason: "tsconfig.json found with strict mode disabled", confidence: 82, quality: 78, grade: "C" },
1365
+ { name: "git-workflow", reason: ".git directory with complex branching detected", confidence: 75, quality: 82, grade: "B" },
1366
+ { name: "api-design", reason: "REST endpoints found in your codebase", confidence: 68, quality: 71, grade: "C" },
1367
+ { name: "docker-compose", reason: "docker-compose.yml detected for orchestration", confidence: 62, quality: 65, grade: "D" }
1348
1368
  ], []);
1349
1369
  const maxVisible = Math.max(3, Math.floor((rows - 10) / 2));
1350
1370
  const visibleRecs = recommendations.slice(0, maxVisible);
@@ -1426,6 +1446,7 @@ function Recommend({ onNavigate, cols = 80, rows = 24 }) {
1426
1446
  visibleRecs.map((rec, idx) => {
1427
1447
  const selected = idx === selectedIndex;
1428
1448
  const indicator = selected ? "\u25B8" : " ";
1449
+ const qualityColor = rec.quality >= 80 ? terminalColors.success : rec.quality >= 60 ? terminalColors.warning : terminalColors.error;
1429
1450
  return /* @__PURE__ */ jsxs7("box", { flexDirection: "column", children: [
1430
1451
  /* @__PURE__ */ jsxs7("box", { flexDirection: "row", children: [
1431
1452
  /* @__PURE__ */ jsx7("text", { fg: terminalColors.text, children: indicator }),
@@ -1434,6 +1455,11 @@ function Recommend({ onNavigate, cols = 80, rows = 24 }) {
1434
1455
  " ",
1435
1456
  rec.confidence,
1436
1457
  "%"
1458
+ ] }),
1459
+ /* @__PURE__ */ jsxs7("text", { fg: qualityColor, children: [
1460
+ " [",
1461
+ rec.grade,
1462
+ "]"
1437
1463
  ] })
1438
1464
  ] }),
1439
1465
  /* @__PURE__ */ jsxs7("text", { fg: terminalColors.textMuted, children: [
@@ -2271,14 +2297,16 @@ var SHORTCUTS = [
2271
2297
  { key: "m", desc: "Marketplace" },
2272
2298
  { key: "r", desc: "Recommendations" },
2273
2299
  { key: "i", desc: "Installed skills" },
2274
- { key: "s", desc: "Sync settings" }
2300
+ { key: "s", desc: "Sync settings" },
2301
+ { key: "f", desc: "Find skills" }
2275
2302
  ] },
2276
2303
  { section: "Actions", items: [
2277
2304
  { key: "t", desc: "Translate skills" },
2278
2305
  { key: "w", desc: "Workflows" },
2279
2306
  { key: "x", desc: "Execute" },
2280
2307
  { key: "n", desc: "Plan" },
2281
- { key: "y", desc: "History" }
2308
+ { key: "v", desc: "Validate" },
2309
+ { key: "u", desc: "Publish" }
2282
2310
  ] },
2283
2311
  { section: "Team & Config", items: [
2284
2312
  { key: "a", desc: "Team settings" },
@@ -2290,10 +2318,10 @@ var SHORTCUTS = [
2290
2318
  ] },
2291
2319
  { section: "Global", items: [
2292
2320
  { key: "/", desc: "This help screen" },
2321
+ { key: "d", desc: "Open docs" },
2293
2322
  { key: "esc", desc: "Go back / Home" },
2294
2323
  { key: "q", desc: "Quit application" },
2295
- { key: "j/k", desc: "Navigate lists" },
2296
- { key: "enter", desc: "Select item" }
2324
+ { key: "j/k", desc: "Navigate lists" }
2297
2325
  ] }
2298
2326
  ];
2299
2327
  function ShortcutSection({ section, items, isLast }) {
@@ -2354,6 +2382,11 @@ function Help({ cols = 80 }) {
2354
2382
 
2355
2383
  // src/App.tsx
2356
2384
  import { jsx as jsx24, jsxs as jsxs24 } from "@opentui/react/jsx-runtime";
2385
+ var DOCS_URL = "https://agenstskills.com/docs";
2386
+ function openUrl(url) {
2387
+ const cmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
2388
+ exec(`${cmd} ${url}`);
2389
+ }
2357
2390
  function App({ onExit } = {}) {
2358
2391
  const [showSplash, setShowSplash] = useState15(true);
2359
2392
  const [screen, setScreen] = useState15("home");
@@ -2390,6 +2423,10 @@ function App({ onExit } = {}) {
2390
2423
  onExit ? onExit(0) : process.exit(0);
2391
2424
  return;
2392
2425
  }
2426
+ if (key.name === "d") {
2427
+ openUrl(DOCS_URL);
2428
+ return;
2429
+ }
2393
2430
  const targetScreen = NAV_KEYS[key.name || ""];
2394
2431
  if (targetScreen) {
2395
2432
  setScreen(targetScreen);
@@ -2678,7 +2715,7 @@ function StatusBar({
2678
2715
  }
2679
2716
 
2680
2717
  // src/components/SkillList.tsx
2681
- import { Fragment as Fragment5, jsx as jsx28, jsxs as jsxs28 } from "@opentui/react/jsx-runtime";
2718
+ import { jsx as jsx28, jsxs as jsxs28 } from "@opentui/react/jsx-runtime";
2682
2719
  function SkillList({
2683
2720
  skills,
2684
2721
  selectedIndex,
@@ -2703,28 +2740,9 @@ function SkillList({
2703
2740
  const pointer = isSelected ? symbols.pointer : symbols.pointerInactive;
2704
2741
  const fg = isSelected ? terminalColors.accent : terminalColors.text;
2705
2742
  const statusIcon = skill.enabled ? symbols.active : symbols.pending;
2706
- const statusColor = skill.enabled ? terminalColors.success : terminalColors.textMuted;
2707
- return /* @__PURE__ */ jsxs28("box", { flexDirection: "row", children: [
2708
- /* @__PURE__ */ jsx28("text", { fg, children: isSelected ? /* @__PURE__ */ jsxs28("b", { children: [
2709
- pointer,
2710
- " ",
2711
- skill.name
2712
- ] }) : /* @__PURE__ */ jsxs28(Fragment5, { children: [
2713
- pointer,
2714
- " ",
2715
- skill.name
2716
- ] }) }),
2717
- /* @__PURE__ */ jsxs28("text", { fg: terminalColors.textMuted, children: [
2718
- " ",
2719
- statusIcon
2720
- ] }),
2721
- skill.description && /* @__PURE__ */ jsxs28("text", { fg: terminalColors.textMuted, children: [
2722
- " ",
2723
- "- ",
2724
- skill.description.slice(0, 40),
2725
- skill.description.length > 40 ? "..." : ""
2726
- ] })
2727
- ] }, skill.name);
2743
+ const descPart = skill.description ? ` - ${skill.description.slice(0, 40)}${skill.description.length > 40 ? "..." : ""}` : "";
2744
+ const line = `${pointer} ${skill.name} ${statusIcon}${descPart}`;
2745
+ return /* @__PURE__ */ jsx28("text", { fg, children: isSelected ? /* @__PURE__ */ jsx28("b", { children: line }) : line }, skill.name);
2728
2746
  }),
2729
2747
  pagination.itemsBelow > 0 && /* @__PURE__ */ jsxs28("text", { fg: terminalColors.textMuted, children: [
2730
2748
  symbols.arrowDown,
@@ -2777,17 +2795,28 @@ function FeatureList({ features = FEATURES2 }) {
2777
2795
 
2778
2796
  // src/index.tsx
2779
2797
  import { jsx as jsx31 } from "@opentui/react/jsx-runtime";
2798
+ var rootInstance = null;
2780
2799
  function exitTUI(code = 0) {
2781
2800
  try {
2782
- process.stdout.write("\x1B[?1049l\x1B[?25h\x1B[0m");
2801
+ if (rootInstance) {
2802
+ try {
2803
+ rootInstance.unmount();
2804
+ } catch {
2805
+ }
2806
+ rootInstance = null;
2807
+ }
2808
+ process.stdout.write("\x1B[?1049l\x1B[?25h\x1B[0m\x1B[H\x1B[2J");
2783
2809
  } catch {
2784
2810
  }
2785
- process.exit(code);
2811
+ setImmediate(() => process.exit(code));
2786
2812
  }
2787
2813
  async function startTUI() {
2788
- const renderer = await createCliRenderer({ exitOnCtrlC: true });
2789
- const root = createRoot(renderer);
2790
- root.render(/* @__PURE__ */ jsx31(App, { onExit: exitTUI }));
2814
+ const handleSignal = () => exitTUI(0);
2815
+ process.on("SIGINT", handleSignal);
2816
+ process.on("SIGTERM", handleSignal);
2817
+ const renderer = await createCliRenderer({ exitOnCtrlC: false });
2818
+ rootInstance = createRoot(renderer);
2819
+ rootInstance.render(/* @__PURE__ */ jsx31(App, { onExit: exitTUI }));
2791
2820
  return new Promise(() => {
2792
2821
  });
2793
2822
  }