@skillkit/tui 1.3.1 → 1.5.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 CHANGED
@@ -2,8 +2,8 @@
2
2
  import { render } from "ink";
3
3
 
4
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";
5
+ import { useState as useState19 } from "react";
6
+ import { Box as Box17, Text as Text17, useInput as useInput15, useApp, useStdout } from "ink";
7
7
 
8
8
  // src/components/Sidebar.tsx
9
9
  import { Box, Text } from "ink";
@@ -27,7 +27,13 @@ var symbols = {
27
27
  checkboxOff: chalk.dim("\u2716"),
28
28
  check: chalk.white("\u2713"),
29
29
  star: "\u2605",
30
- spinner: ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"]
30
+ spinner: ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"],
31
+ arrowUp: "\u2191",
32
+ arrowDown: "\u2193",
33
+ success: "\u2713",
34
+ error: "\u2717",
35
+ warning: "\u26A0",
36
+ info: "\u2139"
31
37
  };
32
38
  var logo = `
33
39
  \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
@@ -41,14 +47,26 @@ var logo = `
41
47
  // src/components/Sidebar.tsx
42
48
  import { jsx, jsxs } from "react/jsx-runtime";
43
49
  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" }
50
+ // Discovery
51
+ { id: "home", label: "Home", key: "h" },
52
+ { id: "marketplace", label: "Marketplace", key: "m" },
53
+ { id: "browse", label: "Browse", key: "b" },
54
+ // Execution
55
+ { id: "workflow", label: "Workflows", key: "w" },
56
+ { id: "execute", label: "Execute", key: "x" },
57
+ { id: "history", label: "History", key: "y" },
58
+ // Collaboration
59
+ { id: "team", label: "Team", key: "a" },
60
+ { id: "plugins", label: "Plugins", key: "p" },
61
+ // Tools
62
+ { id: "recommend", label: "Recommend", key: "r" },
63
+ { id: "translate", label: "Translate", key: "t" },
64
+ { id: "context", label: "Context", key: "c" },
65
+ { id: "memory", label: "Memory", key: "e" },
66
+ // Management
67
+ { id: "installed", label: "Installed", key: "i" },
68
+ { id: "sync", label: "Sync", key: "s" },
69
+ { id: "settings", label: "Config", key: "," }
52
70
  ];
53
71
  function Sidebar({ screen }) {
54
72
  return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", width: 14, borderStyle: "single", paddingX: 1, children: [
@@ -58,12 +76,22 @@ function Sidebar({ screen }) {
58
76
  item.label
59
77
  ] }, item.id)),
60
78
  /* @__PURE__ */ jsx(Text, { children: " " }),
61
- NAV.slice(3, 5).map((item) => /* @__PURE__ */ jsxs(Text, { inverse: screen === item.id, children: [
79
+ NAV.slice(3, 6).map((item) => /* @__PURE__ */ jsxs(Text, { inverse: screen === item.id, children: [
62
80
  screen === item.id ? symbols.bullet : " ",
63
81
  item.label
64
82
  ] }, item.id)),
65
83
  /* @__PURE__ */ jsx(Text, { children: " " }),
66
- NAV.slice(5).map((item) => /* @__PURE__ */ jsxs(Text, { inverse: screen === item.id, children: [
84
+ NAV.slice(6, 8).map((item) => /* @__PURE__ */ jsxs(Text, { inverse: screen === item.id, children: [
85
+ screen === item.id ? symbols.bullet : " ",
86
+ item.label
87
+ ] }, item.id)),
88
+ /* @__PURE__ */ jsx(Text, { children: " " }),
89
+ NAV.slice(8, 12).map((item) => /* @__PURE__ */ jsxs(Text, { inverse: screen === item.id, children: [
90
+ screen === item.id ? symbols.bullet : " ",
91
+ item.label
92
+ ] }, item.id)),
93
+ /* @__PURE__ */ jsx(Text, { children: " " }),
94
+ NAV.slice(12).map((item) => /* @__PURE__ */ jsxs(Text, { inverse: screen === item.id, children: [
67
95
  screen === item.id ? symbols.bullet : " ",
68
96
  item.label
69
97
  ] }, item.id)),
@@ -154,14 +182,16 @@ function Home({ cols = 80, rows = 24 }) {
154
182
 
155
183
  // src/screens/Browse.tsx
156
184
  import { useState as useState3 } from "react";
157
- import { existsSync, mkdirSync, cpSync, rmSync } from "fs";
158
- import { join as join2 } from "path";
185
+ import { existsSync as existsSync2, mkdirSync, cpSync, rmSync } from "fs";
186
+ import { join as join3 } from "path";
159
187
  import { Box as Box3, Text as Text3, useInput } from "ink";
160
188
 
161
189
  // src/hooks/useMarketplace.ts
162
190
  import { useState as useState2, useCallback, useEffect as useEffect2 } from "react";
163
- import { detectProvider } from "@skillkit/core";
164
- var POPULAR_REPOS = [
191
+ import { detectProvider, loadConfig, extractFrontmatter } from "@skillkit/core";
192
+ import { readFileSync as readFileSync2, existsSync } from "fs";
193
+ import { join as join2 } from "path";
194
+ var DEFAULT_REPOS = [
165
195
  { source: "anthropics/skills", name: "Anthropic Official" },
166
196
  { source: "vercel-labs/agent-skills", name: "Vercel Labs" },
167
197
  { source: "expo/skills", name: "Expo / React Native" },
@@ -193,6 +223,41 @@ var POPULAR_REPOS = [
193
223
  { source: "openrouterteam/agent-skills", name: "OpenRouter SDK" },
194
224
  { source: "intellectronica/agent-skills", name: "Context7" }
195
225
  ];
226
+ function getMarketplaceRepos() {
227
+ try {
228
+ const config = loadConfig();
229
+ if (config.marketplaceSources?.length) {
230
+ const configRepos = config.marketplaceSources.map((source) => ({
231
+ source,
232
+ name: source.split("/").pop() || source
233
+ }));
234
+ const existingSources = new Set(configRepos.map((r) => r.source));
235
+ for (const repo of DEFAULT_REPOS) {
236
+ if (!existingSources.has(repo.source)) {
237
+ configRepos.push(repo);
238
+ }
239
+ }
240
+ return configRepos;
241
+ }
242
+ } catch {
243
+ }
244
+ return DEFAULT_REPOS;
245
+ }
246
+ function readSkillDescription(skillPath) {
247
+ const skillMdPath = join2(skillPath, "SKILL.md");
248
+ if (!existsSync(skillMdPath)) {
249
+ return void 0;
250
+ }
251
+ try {
252
+ const content = readFileSync2(skillMdPath, "utf-8");
253
+ const frontmatter = extractFrontmatter(content);
254
+ if (frontmatter && typeof frontmatter.description === "string") {
255
+ return frontmatter.description;
256
+ }
257
+ } catch {
258
+ }
259
+ return void 0;
260
+ }
196
261
  function useMarketplace() {
197
262
  const [allSkills, setAllSkills] = useState2([]);
198
263
  const [filteredSkills, setFilteredSkills] = useState2([]);
@@ -200,6 +265,8 @@ function useMarketplace() {
200
265
  const [error, setError] = useState2(null);
201
266
  const [currentRepo, setCurrentRepo] = useState2(null);
202
267
  const [fetchedRepos, setFetchedRepos] = useState2(/* @__PURE__ */ new Set());
268
+ const [failedRepos, setFailedRepos] = useState2([]);
269
+ const [repos] = useState2(() => getMarketplaceRepos());
203
270
  const fetchRepo = useCallback(async (source) => {
204
271
  if (fetchedRepos.has(source)) return;
205
272
  setLoading(true);
@@ -214,12 +281,13 @@ function useMarketplace() {
214
281
  if (!result.success || !result.discoveredSkills) {
215
282
  throw new Error(result.error || "Failed to fetch skills");
216
283
  }
217
- const repoName = POPULAR_REPOS.find((r) => r.source === source)?.name || source;
284
+ const repoName = repos.find((r) => r.source === source)?.name || source;
218
285
  const newSkills = result.discoveredSkills.map((skill) => ({
219
286
  name: skill.name,
220
287
  source,
221
288
  repoName,
222
- description: void 0
289
+ // Try to read description from skill frontmatter
290
+ description: readSkillDescription(skill.path)
223
291
  }));
224
292
  setAllSkills((prev) => {
225
293
  const updated = [...prev, ...newSkills];
@@ -231,27 +299,37 @@ function useMarketplace() {
231
299
  rmSync3(result.tempRoot, { recursive: true, force: true });
232
300
  }
233
301
  } catch (err) {
234
- setError(err instanceof Error ? err.message : "Failed to fetch repository");
302
+ const errorMsg = err instanceof Error ? err.message : "Failed to fetch repository";
303
+ setError(errorMsg);
304
+ setFailedRepos((prev) => prev.includes(source) ? prev : [...prev, source]);
235
305
  } finally {
236
306
  setLoading(false);
237
307
  setCurrentRepo(null);
238
308
  }
239
- }, [fetchedRepos]);
309
+ }, [fetchedRepos, repos]);
240
310
  const fetchAllRepos = useCallback(async () => {
241
311
  setLoading(true);
242
312
  setError(null);
243
- for (const repo of POPULAR_REPOS) {
313
+ const failures = [];
314
+ for (const repo of repos) {
244
315
  if (!fetchedRepos.has(repo.source)) {
245
316
  setCurrentRepo(repo.source);
246
317
  try {
247
318
  await fetchRepo(repo.source);
248
- } catch {
319
+ } catch (err) {
320
+ failures.push(repo.source);
249
321
  }
250
322
  }
251
323
  }
324
+ if (failures.length > 0) {
325
+ setFailedRepos((prev) => {
326
+ const combined = [...prev, ...failures];
327
+ return [...new Set(combined)];
328
+ });
329
+ }
252
330
  setLoading(false);
253
331
  setCurrentRepo(null);
254
- }, [fetchRepo, fetchedRepos]);
332
+ }, [fetchRepo, fetchedRepos, repos]);
255
333
  const search = useCallback((query) => {
256
334
  if (!query.trim()) {
257
335
  setFilteredSkills(allSkills);
@@ -268,6 +346,8 @@ function useMarketplace() {
268
346
  setFetchedRepos(/* @__PURE__ */ new Set());
269
347
  setAllSkills([]);
270
348
  setFilteredSkills([]);
349
+ setFailedRepos([]);
350
+ setError(null);
271
351
  }, []);
272
352
  useEffect2(() => {
273
353
  setFilteredSkills(allSkills);
@@ -282,8 +362,9 @@ function useMarketplace() {
282
362
  loading,
283
363
  error,
284
364
  totalCount: allSkills.length,
285
- repos: POPULAR_REPOS,
365
+ repos,
286
366
  currentRepo,
367
+ failedRepos,
287
368
  refresh,
288
369
  search,
289
370
  fetchRepo,
@@ -297,14 +378,14 @@ import { detectAgent, getAdapter as getAdapter2, getAllAdapters } from "@skillki
297
378
 
298
379
  // src/helpers.ts
299
380
  import {
300
- loadConfig,
381
+ loadConfig as loadConfig2,
301
382
  getSearchDirs as coreGetSearchDirs,
302
383
  getInstallDir as coreGetInstallDir,
303
384
  saveSkillMetadata as coreSaveSkillMetadata
304
385
  } from "@skillkit/core";
305
386
  import { getAdapter } from "@skillkit/agents";
306
387
  function getSearchDirs(agentType) {
307
- const type = agentType || loadConfig().agent;
388
+ const type = agentType || loadConfig2().agent;
308
389
  const adapter = getAdapter(type);
309
390
  const adapterInfo = {
310
391
  type: adapter.type,
@@ -315,7 +396,7 @@ function getSearchDirs(agentType) {
315
396
  return coreGetSearchDirs(adapterInfo);
316
397
  }
317
398
  function getInstallDir(global = false, agentType) {
318
- const type = agentType || loadConfig().agent;
399
+ const type = agentType || loadConfig2().agent;
319
400
  const adapter = getAdapter(type);
320
401
  const adapterInfo = {
321
402
  type: adapter.type,
@@ -380,11 +461,11 @@ function Browse({ rows = 24 }) {
380
461
  const targetAgentType = agentType || await detectAgent();
381
462
  const adapter = getAdapter2(targetAgentType);
382
463
  const installDir = getInstallDir(false, targetAgentType);
383
- if (!existsSync(installDir)) {
464
+ if (!existsSync2(installDir)) {
384
465
  mkdirSync(installDir, { recursive: true });
385
466
  }
386
- const targetPath = join2(installDir, skillName);
387
- if (existsSync(targetPath)) {
467
+ const targetPath = join3(installDir, skillName);
468
+ if (existsSync2(targetPath)) {
388
469
  rmSync(targetPath, { recursive: true, force: true });
389
470
  }
390
471
  cpSync(skill.path, targetPath, { recursive: true, dereference: true });
@@ -690,206 +771,176 @@ function Sync({ rows = 24 }) {
690
771
  }
691
772
 
692
773
  // src/screens/Settings.tsx
693
- import { useState as useState7 } from "react";
774
+ import { useState as useState7, useEffect as useEffect5 } from "react";
694
775
  import { Box as Box6, Text as Text6, useInput as useInput4 } from "ink";
776
+ import TextInput from "ink-text-input";
777
+ import { loadConfig as loadConfig3, saveConfig, AgentType as AgentTypeSchema } from "@skillkit/core";
695
778
  import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
779
+ var ALL_AGENTS = AgentTypeSchema.options;
696
780
  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" }
781
+ { id: "agent", label: "Default Agent", type: "select", options: ["auto-detect", ...ALL_AGENTS] },
782
+ { id: "autoSync", label: "Auto Sync", type: "toggle" },
783
+ { id: "cacheDir", label: "Cache Dir", type: "text" }
700
784
  ];
701
- function Settings({}) {
785
+ function Settings(_props) {
786
+ const [config, setConfig] = useState7(null);
702
787
  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));
788
+ const [editing, setEditing] = useState7(false);
789
+ const [editValue, setEditValue] = useState7("");
790
+ const [saved, setSaved] = useState7(false);
791
+ const [error, setError] = useState7(null);
792
+ useEffect5(() => {
793
+ try {
794
+ const loaded = loadConfig3();
795
+ setConfig(loaded);
796
+ } catch (err) {
797
+ setError(`Failed to load config: ${err}`);
798
+ }
799
+ }, []);
800
+ const getCurrentValue = (setting) => {
801
+ if (!config) return "";
802
+ switch (setting.id) {
803
+ case "agent":
804
+ return config.agent === "universal" ? "auto-detect" : config.agent || "auto-detect";
805
+ case "autoSync":
806
+ return config.autoSync ? "enabled" : "disabled";
807
+ case "cacheDir":
808
+ return config.cacheDir || "~/.skillkit/cache";
809
+ default:
810
+ return "";
811
+ }
812
+ };
813
+ const handleSave = (setting, value) => {
814
+ if (!config) return;
815
+ const newConfig = { ...config };
816
+ switch (setting.id) {
817
+ case "agent":
818
+ if (value === "auto-detect") {
819
+ newConfig.agent = "universal";
820
+ } else {
821
+ newConfig.agent = value;
822
+ }
823
+ break;
824
+ case "autoSync":
825
+ newConfig.autoSync = value === "enabled";
826
+ break;
827
+ case "cacheDir":
828
+ newConfig.cacheDir = value || void 0;
829
+ break;
830
+ }
831
+ try {
832
+ saveConfig(newConfig, false);
833
+ setConfig(newConfig);
834
+ setSaved(true);
835
+ setTimeout(() => setSaved(false), 2e3);
836
+ } catch (err) {
837
+ setError(`Failed to save: ${err}`);
838
+ setTimeout(() => setError(null), 3e3);
839
+ }
840
+ };
841
+ useInput4((input, key) => {
842
+ if (editing) {
843
+ if (key.return) {
844
+ handleSave(SETTINGS[sel], editValue);
845
+ setEditing(false);
846
+ } else if (key.escape) {
847
+ setEditing(false);
848
+ }
849
+ return;
850
+ }
851
+ if (key.upArrow) {
852
+ setSel((i) => Math.max(0, i - 1));
853
+ } else if (key.downArrow) {
854
+ setSel((i) => Math.min(SETTINGS.length - 1, i + 1));
855
+ } else if (key.return || input === " ") {
856
+ const setting = SETTINGS[sel];
857
+ if (setting.type === "toggle") {
858
+ const current = getCurrentValue(setting);
859
+ const newValue = current === "enabled" ? "disabled" : "enabled";
860
+ handleSave(setting, newValue);
861
+ } else if (setting.type === "select" && setting.options) {
862
+ const current = getCurrentValue(setting);
863
+ const idx = setting.options.indexOf(current);
864
+ const nextIdx = (idx + 1) % setting.options.length;
865
+ handleSave(setting, setting.options[nextIdx]);
866
+ } else if (setting.type === "text") {
867
+ setEditValue(getCurrentValue(setting));
868
+ setEditing(true);
869
+ }
870
+ }
706
871
  });
872
+ if (!config) {
873
+ return /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
874
+ /* @__PURE__ */ jsx6(Text6, { bold: true, color: colors.primary, children: "SETTINGS" }),
875
+ error ? /* @__PURE__ */ jsxs6(Text6, { color: colors.danger, children: [
876
+ symbols.error,
877
+ " ",
878
+ error
879
+ ] }) : /* @__PURE__ */ jsx6(Text6, { dimColor: true, children: "Loading..." })
880
+ ] });
881
+ }
707
882
  return /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
708
883
  /* @__PURE__ */ jsx6(Text6, { bold: true, color: colors.primary, children: "SETTINGS" }),
709
- /* @__PURE__ */ jsx6(Text6, { dimColor: true, children: "Configure SkillKit" }),
884
+ /* @__PURE__ */ jsx6(Text6, { dimColor: true, children: "Configure SkillKit (changes save automatically)" }),
710
885
  /* @__PURE__ */ jsx6(Box6, { marginTop: 1, flexDirection: "column", children: SETTINGS.map((s, i) => {
711
886
  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 })
887
+ const value = getCurrentValue(s);
888
+ const isEditing = editing && isSel;
889
+ return /* @__PURE__ */ jsxs6(Box6, { children: [
890
+ /* @__PURE__ */ jsxs6(Text6, { inverse: isSel && !isEditing, children: [
891
+ isSel ? symbols.pointer : " ",
892
+ s.label.padEnd(16)
893
+ ] }),
894
+ isEditing ? /* @__PURE__ */ jsx6(Box6, { marginLeft: 1, children: /* @__PURE__ */ jsx6(
895
+ TextInput,
896
+ {
897
+ value: editValue,
898
+ onChange: setEditValue,
899
+ onSubmit: () => {
900
+ handleSave(s, editValue);
901
+ setEditing(false);
902
+ }
903
+ }
904
+ ) }) : /* @__PURE__ */ jsxs6(Text6, { color: colors.secondaryDim, children: [
905
+ " ",
906
+ value
907
+ ] }),
908
+ s.type === "toggle" && isSel && !isEditing && /* @__PURE__ */ jsx6(Text6, { dimColor: true, children: " (space to toggle)" }),
909
+ s.type === "select" && isSel && !isEditing && /* @__PURE__ */ jsx6(Text6, { dimColor: true, children: " (enter to cycle)" })
717
910
  ] }, s.id);
718
911
  }) }),
719
- /* @__PURE__ */ jsx6(Box6, { marginTop: 1, children: /* @__PURE__ */ jsx6(Text6, { dimColor: true, children: "Enter=edit q=quit" }) })
912
+ /* @__PURE__ */ jsxs6(Box6, { marginTop: 1, children: [
913
+ saved && /* @__PURE__ */ jsxs6(Text6, { color: colors.success, children: [
914
+ symbols.check,
915
+ " Settings saved"
916
+ ] }),
917
+ error && /* @__PURE__ */ jsxs6(Text6, { color: colors.danger, children: [
918
+ symbols.error,
919
+ " ",
920
+ error
921
+ ] }),
922
+ !saved && !error && /* @__PURE__ */ jsx6(Text6, { dimColor: true, children: editing ? "Enter=save Esc=cancel" : "Enter/Space=edit q=quit" })
923
+ ] })
720
924
  ] });
721
925
  }
722
926
 
723
927
  // src/screens/Recommend.tsx
724
928
  import { useState as useState9 } from "react";
725
- import { existsSync as existsSync3, mkdirSync as mkdirSync3, cpSync as cpSync2, rmSync as rmSync2 } from "fs";
929
+ import { existsSync as existsSync3, mkdirSync as mkdirSync2, cpSync as cpSync2, rmSync as rmSync2 } from "fs";
726
930
  import { join as join4 } from "path";
727
931
  import { Box as Box7, Text as Text7, useInput as useInput5 } from "ink";
728
932
 
729
933
  // src/hooks/useRecommend.ts
730
- import { useState as useState8, useCallback as useCallback2, useEffect as useEffect5 } from "react";
934
+ import { useState as useState8, useCallback as useCallback2, useEffect as useEffect6 } from "react";
731
935
  import {
732
936
  RecommendationEngine,
733
- ContextManager
937
+ ContextManager,
938
+ loadIndex as loadIndexFromCache,
939
+ saveIndex,
940
+ buildSkillIndex,
941
+ isIndexStale,
942
+ KNOWN_SKILL_REPOS
734
943
  } 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
944
  function useRecommend(projectPath = process.cwd()) {
894
945
  const [recommendations, setRecommendations] = useState8([]);
895
946
  const [searchResults, setSearchResults] = useState8([]);
@@ -900,25 +951,17 @@ function useRecommend(projectPath = process.cwd()) {
900
951
  const [indexStatus, setIndexStatus] = useState8("missing");
901
952
  const [engine] = useState8(() => new RecommendationEngine());
902
953
  const loadIndex = useCallback2(() => {
903
- if (!existsSync2(INDEX_PATH)) {
954
+ const index = loadIndexFromCache();
955
+ if (!index) {
904
956
  setIndexStatus("missing");
905
957
  return null;
906
958
  }
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;
959
+ if (isIndexStale(index)) {
960
+ setIndexStatus("stale");
961
+ } else {
962
+ setIndexStatus("fresh");
921
963
  }
964
+ return index;
922
965
  }, []);
923
966
  const getProjectProfile = useCallback2(() => {
924
967
  try {
@@ -978,37 +1021,17 @@ function useRecommend(projectPath = process.cwd()) {
978
1021
  const updateIndex = useCallback2(() => {
979
1022
  setLoading(true);
980
1023
  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 });
1024
+ buildSkillIndex(KNOWN_SKILL_REPOS).then(({ index, errors }) => {
1025
+ if (errors.length > 0) {
1026
+ console.warn("Index update warnings:", errors);
1004
1027
  }
1005
- writeFileSync(INDEX_PATH, JSON.stringify(sampleIndex, null, 2));
1028
+ saveIndex(index);
1006
1029
  setIndexStatus("fresh");
1007
1030
  loadRecommendations();
1008
- } catch (err) {
1031
+ }).catch((err) => {
1009
1032
  setError(err instanceof Error ? err.message : "Failed to update index");
1010
1033
  setLoading(false);
1011
- }
1034
+ });
1012
1035
  }, [loadRecommendations]);
1013
1036
  const search = useCallback2((query) => {
1014
1037
  if (!query.trim()) {
@@ -1036,7 +1059,7 @@ function useRecommend(projectPath = process.cwd()) {
1036
1059
  const refresh = useCallback2(() => {
1037
1060
  loadRecommendations();
1038
1061
  }, [loadRecommendations]);
1039
- useEffect5(() => {
1062
+ useEffect6(() => {
1040
1063
  loadRecommendations();
1041
1064
  }, [loadRecommendations]);
1042
1065
  return {
@@ -1127,7 +1150,7 @@ function Recommend({ rows = 24 }) {
1127
1150
  const adapter = getAdapter3(targetAgentType);
1128
1151
  const installDir = getInstallDir(false, targetAgentType);
1129
1152
  if (!existsSync3(installDir)) {
1130
- mkdirSync3(installDir, { recursive: true });
1153
+ mkdirSync2(installDir, { recursive: true });
1131
1154
  }
1132
1155
  const targetPath = join4(installDir, skillName);
1133
1156
  if (existsSync3(targetPath)) {
@@ -1333,8 +1356,8 @@ function Recommend({ rows = 24 }) {
1333
1356
  }
1334
1357
 
1335
1358
  // 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";
1359
+ import { useState as useState10, useEffect as useEffect7 } from "react";
1360
+ import { existsSync as existsSync4, readdirSync, readFileSync as readFileSync3, writeFileSync, mkdirSync as mkdirSync3 } from "fs";
1338
1361
  import { join as join5 } from "path";
1339
1362
  import { Box as Box8, Text as Text8, useInput as useInput6 } from "ink";
1340
1363
  import {
@@ -1353,7 +1376,7 @@ function Translate({ rows = 24 }) {
1353
1376
  const [preview, setPreview] = useState10("");
1354
1377
  const [result, setResult] = useState10(null);
1355
1378
  const [loading, setLoading] = useState10(false);
1356
- useEffect6(() => {
1379
+ useEffect7(() => {
1357
1380
  const loadSkills = () => {
1358
1381
  const installDir = getInstallDir(false);
1359
1382
  const foundSkills = [];
@@ -1376,7 +1399,7 @@ function Translate({ rows = 24 }) {
1376
1399
  };
1377
1400
  loadSkills();
1378
1401
  }, []);
1379
- useEffect6(() => {
1402
+ useEffect7(() => {
1380
1403
  const adapters = getAllAdapters4();
1381
1404
  const supportedAgents = getSupportedTranslationAgents();
1382
1405
  const agentList = adapters.filter((a) => supportedAgents.includes(a.type)).map((a) => ({
@@ -1415,11 +1438,11 @@ function Translate({ rows = 24 }) {
1415
1438
  const adapter = getAdapter4(selectedAgent.type);
1416
1439
  const targetDir = adapter?.skillsDir ? join5(process.cwd(), adapter.skillsDir) : join5(process.cwd(), `.${selectedAgent.type}/skills/`);
1417
1440
  if (!existsSync4(targetDir)) {
1418
- mkdirSync4(targetDir, { recursive: true });
1441
+ mkdirSync3(targetDir, { recursive: true });
1419
1442
  }
1420
1443
  const filename = translationResult.filename || `${selectedSkill.name}.md`;
1421
1444
  const targetPath = join5(targetDir, filename);
1422
- writeFileSync2(targetPath, translationResult.content, "utf-8");
1445
+ writeFileSync(targetPath, translationResult.content, "utf-8");
1423
1446
  setResult({
1424
1447
  success: true,
1425
1448
  message: `Translated ${selectedSkill.name} to ${selectedAgent.name} format`,
@@ -1564,7 +1587,7 @@ function Translate({ rows = 24 }) {
1564
1587
  }
1565
1588
 
1566
1589
  // src/screens/Context.tsx
1567
- import { useState as useState11, useEffect as useEffect7 } from "react";
1590
+ import { useState as useState11, useEffect as useEffect8 } from "react";
1568
1591
  import { Box as Box9, Text as Text9, useInput as useInput7 } from "ink";
1569
1592
  import {
1570
1593
  loadContext,
@@ -1584,7 +1607,7 @@ function Context({ rows = 24 }) {
1584
1607
  const [message, setMessage] = useState11(null);
1585
1608
  const [error, setError] = useState11(null);
1586
1609
  const projectPath = process.cwd();
1587
- useEffect7(() => {
1610
+ useEffect8(() => {
1588
1611
  const load = async () => {
1589
1612
  setLoading(true);
1590
1613
  try {
@@ -1812,16 +1835,1132 @@ function Context({ rows = 24 }) {
1812
1835
  ] });
1813
1836
  }
1814
1837
 
1815
- // src/App.tsx
1838
+ // src/screens/Workflow.tsx
1839
+ import { useState as useState12, useEffect as useEffect9 } from "react";
1840
+ import { Box as Box10, Text as Text10, useInput as useInput8 } from "ink";
1841
+ import { listWorkflows } from "@skillkit/core";
1816
1842
  import { jsx as jsx10, jsxs as jsxs10 } from "react/jsx-runtime";
1843
+ function Workflow({ rows = 24 }) {
1844
+ const [workflows, setWorkflows] = useState12([]);
1845
+ const [loading, setLoading] = useState12(true);
1846
+ const [sel, setSel] = useState12(0);
1847
+ const [running, setRunning] = useState12(null);
1848
+ const [error, setError] = useState12(null);
1849
+ const maxVisible = Math.max(5, rows - 8);
1850
+ const start = Math.max(0, Math.min(sel - Math.floor(maxVisible / 2), workflows.length - maxVisible));
1851
+ const visible = workflows.slice(start, start + maxVisible);
1852
+ useEffect9(() => {
1853
+ loadWorkflows();
1854
+ }, []);
1855
+ const loadWorkflows = () => {
1856
+ setLoading(true);
1857
+ setError(null);
1858
+ try {
1859
+ const wfs = listWorkflows(process.cwd());
1860
+ setWorkflows(wfs);
1861
+ } catch (e) {
1862
+ setError(e instanceof Error ? e.message : "Failed to load workflows");
1863
+ }
1864
+ setLoading(false);
1865
+ };
1866
+ useInput8((input, key) => {
1867
+ if (loading || running) return;
1868
+ if (key.upArrow) setSel((i) => Math.max(0, i - 1));
1869
+ else if (key.downArrow) setSel((i) => Math.min(workflows.length - 1, i + 1));
1870
+ else if (input === "r") loadWorkflows();
1871
+ else if (key.return && workflows[sel]) {
1872
+ setRunning(workflows[sel].name);
1873
+ setTimeout(() => setRunning(null), 2e3);
1874
+ }
1875
+ });
1876
+ return /* @__PURE__ */ jsxs10(Box10, { flexDirection: "column", children: [
1877
+ /* @__PURE__ */ jsx10(Text10, { bold: true, color: colors.primary, children: "WORKFLOWS" }),
1878
+ /* @__PURE__ */ jsxs10(Text10, { dimColor: true, children: [
1879
+ workflows.length,
1880
+ " workflow(s) found"
1881
+ ] }),
1882
+ loading && /* @__PURE__ */ jsx10(Text10, { children: "Loading workflows..." }),
1883
+ error && /* @__PURE__ */ jsx10(Text10, { color: "red", children: error }),
1884
+ running && /* @__PURE__ */ jsxs10(Text10, { color: "yellow", children: [
1885
+ "Running: ",
1886
+ running,
1887
+ "..."
1888
+ ] }),
1889
+ !loading && !running && workflows.length === 0 && /* @__PURE__ */ jsxs10(Box10, { marginTop: 1, flexDirection: "column", children: [
1890
+ /* @__PURE__ */ jsx10(Text10, { dimColor: true, children: "No workflows found." }),
1891
+ /* @__PURE__ */ jsx10(Text10, { dimColor: true, children: "Create one with: skillkit workflow create" })
1892
+ ] }),
1893
+ !loading && !running && workflows.length > 0 && /* @__PURE__ */ jsxs10(Box10, { marginTop: 1, flexDirection: "column", children: [
1894
+ start > 0 && /* @__PURE__ */ jsxs10(Text10, { dimColor: true, children: [
1895
+ " \u2191 ",
1896
+ start,
1897
+ " more"
1898
+ ] }),
1899
+ visible.map((wf, i) => {
1900
+ const idx = start + i;
1901
+ const isSel = idx === sel;
1902
+ return /* @__PURE__ */ jsxs10(Box10, { flexDirection: "column", children: [
1903
+ /* @__PURE__ */ jsxs10(Text10, { inverse: isSel, children: [
1904
+ isSel ? symbols.pointer : " ",
1905
+ " ",
1906
+ wf.name
1907
+ ] }),
1908
+ isSel && wf.description && /* @__PURE__ */ jsxs10(Text10, { dimColor: true, children: [
1909
+ " ",
1910
+ wf.description
1911
+ ] }),
1912
+ isSel && /* @__PURE__ */ jsxs10(Text10, { dimColor: true, children: [
1913
+ " ",
1914
+ wf.waves.length,
1915
+ " wave(s), ",
1916
+ wf.waves.reduce((acc, w) => acc + w.skills.length, 0),
1917
+ " skill(s)"
1918
+ ] })
1919
+ ] }, wf.name);
1920
+ }),
1921
+ start + maxVisible < workflows.length && /* @__PURE__ */ jsxs10(Text10, { dimColor: true, children: [
1922
+ " \u2193 ",
1923
+ workflows.length - start - maxVisible,
1924
+ " more"
1925
+ ] })
1926
+ ] }),
1927
+ /* @__PURE__ */ jsx10(Box10, { marginTop: 1, children: /* @__PURE__ */ jsx10(Text10, { dimColor: true, children: "Enter=run r=refresh q=quit" }) })
1928
+ ] });
1929
+ }
1930
+
1931
+ // src/screens/Execute.tsx
1932
+ import { useState as useState13, useEffect as useEffect10 } from "react";
1933
+ import { Box as Box11, Text as Text11, useInput as useInput9 } from "ink";
1934
+ import { createSessionManager } from "@skillkit/core";
1935
+ import { jsx as jsx11, jsxs as jsxs11 } from "react/jsx-runtime";
1936
+ function Execute({ rows = 24 }) {
1937
+ const [session, setSession] = useState13(null);
1938
+ const [loading, setLoading] = useState13(true);
1939
+ const maxVisible = Math.max(5, rows - 10);
1940
+ useEffect10(() => {
1941
+ loadSession();
1942
+ const interval = setInterval(loadSession, 1e3);
1943
+ return () => clearInterval(interval);
1944
+ }, []);
1945
+ const loadSession = () => {
1946
+ try {
1947
+ const manager = createSessionManager(process.cwd());
1948
+ const state = manager.get();
1949
+ setSession(state);
1950
+ } catch {
1951
+ setSession(null);
1952
+ }
1953
+ setLoading(false);
1954
+ };
1955
+ useInput9((input, _key) => {
1956
+ if (input === "r") loadSession();
1957
+ else if (input === "p" && session?.currentExecution?.status === "running") {
1958
+ const manager = createSessionManager(process.cwd());
1959
+ manager.pause();
1960
+ loadSession();
1961
+ } else if (input === "c" && session?.currentExecution?.status === "paused") {
1962
+ const manager = createSessionManager(process.cwd());
1963
+ manager.resume();
1964
+ loadSession();
1965
+ }
1966
+ });
1967
+ const renderExecution = (exec) => {
1968
+ const completedTasks = exec.tasks.filter((t) => t.status === "completed").length;
1969
+ const failedTasks = exec.tasks.filter((t) => t.status === "failed").length;
1970
+ const progress = exec.totalSteps > 0 ? Math.round(completedTasks / exec.totalSteps * 100) : 0;
1971
+ const statusColor = exec.status === "running" ? "yellow" : exec.status === "completed" ? "green" : exec.status === "paused" ? "blue" : exec.status === "failed" ? "red" : "white";
1972
+ const visibleTasks = exec.tasks.slice(0, maxVisible);
1973
+ return /* @__PURE__ */ jsxs11(Box11, { flexDirection: "column", marginTop: 1, children: [
1974
+ /* @__PURE__ */ jsx11(Text11, { bold: true, children: exec.skillName }),
1975
+ /* @__PURE__ */ jsxs11(Text11, { children: [
1976
+ "Source: ",
1977
+ /* @__PURE__ */ jsx11(Text11, { dimColor: true, children: exec.skillSource })
1978
+ ] }),
1979
+ /* @__PURE__ */ jsxs11(Text11, { children: [
1980
+ "Status: ",
1981
+ /* @__PURE__ */ jsx11(Text11, { color: statusColor, children: exec.status.toUpperCase() })
1982
+ ] }),
1983
+ /* @__PURE__ */ jsxs11(Text11, { children: [
1984
+ "Progress: ",
1985
+ completedTasks,
1986
+ "/",
1987
+ exec.totalSteps,
1988
+ " (",
1989
+ progress,
1990
+ "%)"
1991
+ ] }),
1992
+ /* @__PURE__ */ jsxs11(Box11, { marginY: 1, children: [
1993
+ /* @__PURE__ */ jsx11(Text11, { children: "[" }),
1994
+ /* @__PURE__ */ jsx11(Text11, { color: "green", children: "\u2588".repeat(Math.floor(progress / 5)) }),
1995
+ /* @__PURE__ */ jsx11(Text11, { dimColor: true, children: "\u2591".repeat(20 - Math.floor(progress / 5)) }),
1996
+ /* @__PURE__ */ jsx11(Text11, { children: "]" })
1997
+ ] }),
1998
+ /* @__PURE__ */ jsx11(Text11, { bold: true, children: "Tasks:" }),
1999
+ visibleTasks.map((task, i) => {
2000
+ const icon = task.status === "completed" ? symbols.success : task.status === "failed" ? symbols.error : task.status === "in_progress" ? symbols.warning : task.status === "paused" ? symbols.info : symbols.bullet;
2001
+ const color = task.status === "completed" ? "green" : task.status === "failed" ? "red" : task.status === "in_progress" ? "yellow" : task.status === "paused" ? "blue" : "white";
2002
+ return /* @__PURE__ */ jsxs11(Text11, { color, children: [
2003
+ icon,
2004
+ " ",
2005
+ task.name,
2006
+ " ",
2007
+ task.error && /* @__PURE__ */ jsxs11(Text11, { dimColor: true, children: [
2008
+ "(",
2009
+ task.error,
2010
+ ")"
2011
+ ] })
2012
+ ] }, task.id || i);
2013
+ }),
2014
+ exec.tasks.length > maxVisible && /* @__PURE__ */ jsxs11(Text11, { dimColor: true, children: [
2015
+ " ... and ",
2016
+ exec.tasks.length - maxVisible,
2017
+ " more"
2018
+ ] }),
2019
+ failedTasks > 0 && /* @__PURE__ */ jsx11(Box11, { marginTop: 1, children: /* @__PURE__ */ jsxs11(Text11, { color: "red", children: [
2020
+ failedTasks,
2021
+ " task(s) failed"
2022
+ ] }) })
2023
+ ] });
2024
+ };
2025
+ return /* @__PURE__ */ jsxs11(Box11, { flexDirection: "column", children: [
2026
+ /* @__PURE__ */ jsx11(Text11, { bold: true, color: colors.primary, children: "EXECUTION MONITOR" }),
2027
+ loading && /* @__PURE__ */ jsx11(Text11, { children: "Loading session..." }),
2028
+ !loading && !session?.currentExecution && /* @__PURE__ */ jsxs11(Box11, { marginTop: 1, flexDirection: "column", children: [
2029
+ /* @__PURE__ */ jsx11(Text11, { dimColor: true, children: "No active execution." }),
2030
+ /* @__PURE__ */ jsxs11(Text11, { dimColor: true, children: [
2031
+ "Run a skill with: skillkit run ",
2032
+ "<skill>"
2033
+ ] })
2034
+ ] }),
2035
+ !loading && session?.currentExecution && renderExecution(session.currentExecution),
2036
+ /* @__PURE__ */ jsx11(Box11, { marginTop: 1, children: /* @__PURE__ */ jsxs11(Text11, { dimColor: true, children: [
2037
+ session?.currentExecution?.status === "running" ? "p=pause " : "",
2038
+ session?.currentExecution?.status === "paused" ? "c=continue " : "",
2039
+ "r=refresh q=quit"
2040
+ ] }) })
2041
+ ] });
2042
+ }
2043
+
2044
+ // src/screens/History.tsx
2045
+ import { useState as useState14, useEffect as useEffect11 } from "react";
2046
+ import { Box as Box12, Text as Text12, useInput as useInput10 } from "ink";
2047
+ import { createSessionManager as createSessionManager2 } from "@skillkit/core";
2048
+ import { jsx as jsx12, jsxs as jsxs12 } from "react/jsx-runtime";
2049
+ function History({ rows = 24 }) {
2050
+ const [history, setHistory] = useState14([]);
2051
+ const [loading, setLoading] = useState14(true);
2052
+ const [sel, setSel] = useState14(0);
2053
+ const [expanded, setExpanded] = useState14(false);
2054
+ const maxVisible = Math.max(5, rows - 8);
2055
+ const start = Math.max(0, Math.min(sel - Math.floor(maxVisible / 2), history.length - maxVisible));
2056
+ const visible = history.slice(start, start + maxVisible);
2057
+ useEffect11(() => {
2058
+ loadHistory();
2059
+ }, []);
2060
+ const loadHistory = () => {
2061
+ setLoading(true);
2062
+ try {
2063
+ const manager = createSessionManager2(process.cwd());
2064
+ const h = manager.getHistory(50);
2065
+ setHistory(h);
2066
+ } catch {
2067
+ setHistory([]);
2068
+ }
2069
+ setLoading(false);
2070
+ };
2071
+ useInput10((input, key) => {
2072
+ if (loading) return;
2073
+ if (key.upArrow) {
2074
+ setSel((i) => Math.max(0, i - 1));
2075
+ setExpanded(false);
2076
+ } else if (key.downArrow) {
2077
+ setSel((i) => Math.min(history.length - 1, i + 1));
2078
+ setExpanded(false);
2079
+ } else if (input === "r") loadHistory();
2080
+ else if (key.return) setExpanded((e) => !e);
2081
+ });
2082
+ const formatDuration = (ms) => {
2083
+ if (ms < 1e3) return `${ms}ms`;
2084
+ if (ms < 6e4) return `${(ms / 1e3).toFixed(1)}s`;
2085
+ return `${(ms / 6e4).toFixed(1)}m`;
2086
+ };
2087
+ const formatDate = (iso) => {
2088
+ const d = new Date(iso);
2089
+ const now = /* @__PURE__ */ new Date();
2090
+ const diff = now.getTime() - d.getTime();
2091
+ if (diff < 6e4) return "Just now";
2092
+ if (diff < 36e5) return `${Math.floor(diff / 6e4)}m ago`;
2093
+ if (diff < 864e5) return `${Math.floor(diff / 36e5)}h ago`;
2094
+ return d.toLocaleDateString();
2095
+ };
2096
+ return /* @__PURE__ */ jsxs12(Box12, { flexDirection: "column", children: [
2097
+ /* @__PURE__ */ jsx12(Text12, { bold: true, color: colors.primary, children: "EXECUTION HISTORY" }),
2098
+ /* @__PURE__ */ jsxs12(Text12, { dimColor: true, children: [
2099
+ history.length,
2100
+ " execution(s)"
2101
+ ] }),
2102
+ loading && /* @__PURE__ */ jsx12(Text12, { children: "Loading history..." }),
2103
+ !loading && history.length === 0 && /* @__PURE__ */ jsxs12(Box12, { marginTop: 1, flexDirection: "column", children: [
2104
+ /* @__PURE__ */ jsx12(Text12, { dimColor: true, children: "No execution history." }),
2105
+ /* @__PURE__ */ jsxs12(Text12, { dimColor: true, children: [
2106
+ "Run a skill with: skillkit run ",
2107
+ "<skill>"
2108
+ ] })
2109
+ ] }),
2110
+ !loading && history.length > 0 && /* @__PURE__ */ jsxs12(Box12, { marginTop: 1, flexDirection: "column", children: [
2111
+ start > 0 && /* @__PURE__ */ jsxs12(Text12, { dimColor: true, children: [
2112
+ " \u2191 ",
2113
+ start,
2114
+ " more"
2115
+ ] }),
2116
+ visible.map((entry, i) => {
2117
+ const idx = start + i;
2118
+ const isSel = idx === sel;
2119
+ const icon = entry.status === "completed" ? symbols.success : entry.status === "failed" ? symbols.error : symbols.warning;
2120
+ const color = entry.status === "completed" ? "green" : entry.status === "failed" ? "red" : "yellow";
2121
+ return /* @__PURE__ */ jsxs12(Box12, { flexDirection: "column", children: [
2122
+ /* @__PURE__ */ jsxs12(Text12, { inverse: isSel, children: [
2123
+ isSel ? symbols.pointer : " ",
2124
+ /* @__PURE__ */ jsx12(Text12, { color, children: icon }),
2125
+ " ",
2126
+ entry.skillName.padEnd(25),
2127
+ " ",
2128
+ formatDate(entry.completedAt).padEnd(12),
2129
+ " ",
2130
+ formatDuration(entry.durationMs)
2131
+ ] }),
2132
+ isSel && expanded && /* @__PURE__ */ jsxs12(Box12, { flexDirection: "column", marginLeft: 3, children: [
2133
+ /* @__PURE__ */ jsxs12(Text12, { dimColor: true, children: [
2134
+ "Source: ",
2135
+ entry.skillSource
2136
+ ] }),
2137
+ /* @__PURE__ */ jsxs12(Text12, { dimColor: true, children: [
2138
+ "Status: ",
2139
+ entry.status
2140
+ ] }),
2141
+ entry.commits.length > 0 && /* @__PURE__ */ jsxs12(Text12, { dimColor: true, children: [
2142
+ "Commits: ",
2143
+ entry.commits.join(", ")
2144
+ ] }),
2145
+ entry.filesModified.length > 0 && /* @__PURE__ */ jsxs12(Text12, { dimColor: true, children: [
2146
+ "Files: ",
2147
+ entry.filesModified.length,
2148
+ " modified"
2149
+ ] }),
2150
+ entry.error && /* @__PURE__ */ jsxs12(Text12, { color: "red", children: [
2151
+ "Error: ",
2152
+ entry.error
2153
+ ] })
2154
+ ] })
2155
+ ] }, idx);
2156
+ }),
2157
+ start + maxVisible < history.length && /* @__PURE__ */ jsxs12(Text12, { dimColor: true, children: [
2158
+ " \u2193 ",
2159
+ history.length - start - maxVisible,
2160
+ " more"
2161
+ ] })
2162
+ ] }),
2163
+ /* @__PURE__ */ jsx12(Box12, { marginTop: 1, children: /* @__PURE__ */ jsx12(Text12, { dimColor: true, children: "Enter=expand r=refresh q=quit" }) })
2164
+ ] });
2165
+ }
2166
+
2167
+ // src/screens/Marketplace.tsx
2168
+ import { useState as useState15, useEffect as useEffect12 } from "react";
2169
+ import { Box as Box13, Text as Text13, useInput as useInput11 } from "ink";
2170
+ import { jsx as jsx13, jsxs as jsxs13 } from "react/jsx-runtime";
2171
+ var SKILL_SOURCES = [
2172
+ { owner: "composioHQ", repo: "awesome-claude-code-skills", name: "Composio Curated" },
2173
+ { owner: "anthropics", repo: "courses", name: "Anthropic Official" }
2174
+ ];
2175
+ function Marketplace({ rows = 24 }) {
2176
+ const [skills, setSkills] = useState15([]);
2177
+ const [loading, setLoading] = useState15(true);
2178
+ const [error, setError] = useState15(null);
2179
+ const [sel, setSel] = useState15(0);
2180
+ const [search, setSearch] = useState15("");
2181
+ const [installing, setInstalling] = useState15(null);
2182
+ const filtered = skills.filter(
2183
+ (s) => s.name.toLowerCase().includes(search.toLowerCase()) || s.description.toLowerCase().includes(search.toLowerCase()) || s.tags?.some((t) => t.toLowerCase().includes(search.toLowerCase()))
2184
+ );
2185
+ const maxVisible = Math.max(5, rows - 10);
2186
+ const start = Math.max(0, Math.min(sel - Math.floor(maxVisible / 2), filtered.length - maxVisible));
2187
+ const visible = filtered.slice(start, start + maxVisible);
2188
+ useEffect12(() => {
2189
+ loadMarketplace();
2190
+ }, []);
2191
+ const loadMarketplace = async () => {
2192
+ setLoading(true);
2193
+ setError(null);
2194
+ try {
2195
+ const allSkills = [];
2196
+ for (const source of SKILL_SOURCES) {
2197
+ try {
2198
+ const indexUrl = `https://raw.githubusercontent.com/${source.owner}/${source.repo}/main/skills.json`;
2199
+ const response = await fetch(indexUrl);
2200
+ if (response.ok) {
2201
+ const data = await response.json();
2202
+ if (Array.isArray(data)) {
2203
+ allSkills.push(...data.map((s) => ({
2204
+ name: String(s.name || "Unknown"),
2205
+ description: String(s.description || ""),
2206
+ source: source.name,
2207
+ repo: `${source.owner}/${source.repo}`,
2208
+ tags: Array.isArray(s.tags) ? s.tags : [],
2209
+ stars: typeof s.stars === "number" ? s.stars : void 0
2210
+ })));
2211
+ }
2212
+ } else {
2213
+ allSkills.push({
2214
+ name: source.repo,
2215
+ description: `Skills from ${source.name}`,
2216
+ source: source.name,
2217
+ repo: `${source.owner}/${source.repo}`
2218
+ });
2219
+ }
2220
+ } catch {
2221
+ allSkills.push({
2222
+ name: source.repo,
2223
+ description: `Skills from ${source.name}`,
2224
+ source: source.name,
2225
+ repo: `${source.owner}/${source.repo}`
2226
+ });
2227
+ }
2228
+ }
2229
+ if (allSkills.length === 0) {
2230
+ allSkills.push(
2231
+ { name: "typescript-strict", description: "Enable strict TypeScript mode", source: "Built-in", repo: "skillkit/skills", tags: ["typescript", "config"] },
2232
+ { name: "eslint-setup", description: "Set up ESLint with recommended rules", source: "Built-in", repo: "skillkit/skills", tags: ["eslint", "linting"] },
2233
+ { name: "prettier-config", description: "Configure Prettier formatting", source: "Built-in", repo: "skillkit/skills", tags: ["prettier", "formatting"] },
2234
+ { name: "jest-setup", description: "Set up Jest testing framework", source: "Built-in", repo: "skillkit/skills", tags: ["jest", "testing"] },
2235
+ { name: "nextjs-auth", description: "Add authentication to Next.js", source: "Community", repo: "community/skills", tags: ["nextjs", "auth"] }
2236
+ );
2237
+ }
2238
+ setSkills(allSkills);
2239
+ } catch (e) {
2240
+ setError(e instanceof Error ? e.message : "Failed to load marketplace");
2241
+ }
2242
+ setLoading(false);
2243
+ };
2244
+ useInput11((input, key) => {
2245
+ if (loading || installing) return;
2246
+ if (key.upArrow) setSel((i) => Math.max(0, i - 1));
2247
+ else if (key.downArrow) setSel((i) => Math.min(filtered.length - 1, i + 1));
2248
+ else if (input === "r") loadMarketplace();
2249
+ else if (input === "/") setSearch("");
2250
+ else if (key.backspace || key.delete) setSearch((s) => s.slice(0, -1));
2251
+ else if (key.return && filtered[sel]) {
2252
+ setInstalling(filtered[sel].name);
2253
+ setTimeout(() => setInstalling(null), 1500);
2254
+ } else if (input.length === 1 && /[a-zA-Z0-9-_]/.test(input)) {
2255
+ setSearch((s) => s + input);
2256
+ setSel(0);
2257
+ }
2258
+ });
2259
+ return /* @__PURE__ */ jsxs13(Box13, { flexDirection: "column", children: [
2260
+ /* @__PURE__ */ jsx13(Text13, { bold: true, color: colors.primary, children: "SKILL MARKETPLACE" }),
2261
+ /* @__PURE__ */ jsxs13(Text13, { dimColor: true, children: [
2262
+ skills.length,
2263
+ " skills from ",
2264
+ SKILL_SOURCES.length,
2265
+ " sources"
2266
+ ] }),
2267
+ search && /* @__PURE__ */ jsxs13(Text13, { children: [
2268
+ "Search: ",
2269
+ /* @__PURE__ */ jsx13(Text13, { color: "yellow", children: search }),
2270
+ " (",
2271
+ filtered.length,
2272
+ " results)"
2273
+ ] }),
2274
+ loading && /* @__PURE__ */ jsx13(Text13, { children: "Loading marketplace..." }),
2275
+ error && /* @__PURE__ */ jsx13(Text13, { color: "red", children: error }),
2276
+ installing && /* @__PURE__ */ jsxs13(Text13, { color: "yellow", children: [
2277
+ "Installing ",
2278
+ installing,
2279
+ "..."
2280
+ ] }),
2281
+ !loading && !installing && filtered.length === 0 && /* @__PURE__ */ jsx13(Box13, { marginTop: 1, children: /* @__PURE__ */ jsxs13(Text13, { dimColor: true, children: [
2282
+ "No skills found",
2283
+ search ? ` matching "${search}"` : "",
2284
+ "."
2285
+ ] }) }),
2286
+ !loading && !installing && filtered.length > 0 && /* @__PURE__ */ jsxs13(Box13, { marginTop: 1, flexDirection: "column", children: [
2287
+ start > 0 && /* @__PURE__ */ jsxs13(Text13, { dimColor: true, children: [
2288
+ " \u2191 ",
2289
+ start,
2290
+ " more"
2291
+ ] }),
2292
+ visible.map((skill, i) => {
2293
+ const idx = start + i;
2294
+ const isSel = idx === sel;
2295
+ return /* @__PURE__ */ jsxs13(Box13, { flexDirection: "column", children: [
2296
+ /* @__PURE__ */ jsxs13(Text13, { inverse: isSel, children: [
2297
+ isSel ? symbols.pointer : " ",
2298
+ " ",
2299
+ skill.name.padEnd(25),
2300
+ " ",
2301
+ /* @__PURE__ */ jsx13(Text13, { dimColor: true, children: skill.source })
2302
+ ] }),
2303
+ isSel && /* @__PURE__ */ jsxs13(Box13, { flexDirection: "column", marginLeft: 3, children: [
2304
+ /* @__PURE__ */ jsx13(Text13, { dimColor: true, children: skill.description }),
2305
+ skill.tags && skill.tags.length > 0 && /* @__PURE__ */ jsxs13(Text13, { dimColor: true, children: [
2306
+ "Tags: ",
2307
+ skill.tags.join(", ")
2308
+ ] }),
2309
+ /* @__PURE__ */ jsxs13(Text13, { dimColor: true, children: [
2310
+ "Repo: ",
2311
+ skill.repo
2312
+ ] })
2313
+ ] })
2314
+ ] }, `${skill.repo}/${skill.name}`);
2315
+ }),
2316
+ start + maxVisible < filtered.length && /* @__PURE__ */ jsxs13(Text13, { dimColor: true, children: [
2317
+ " \u2193 ",
2318
+ filtered.length - start - maxVisible,
2319
+ " more"
2320
+ ] })
2321
+ ] }),
2322
+ /* @__PURE__ */ jsx13(Box13, { marginTop: 1, children: /* @__PURE__ */ jsx13(Text13, { dimColor: true, children: "Enter=install type=search r=refresh q=quit" }) })
2323
+ ] });
2324
+ }
2325
+
2326
+ // src/screens/Memory.tsx
2327
+ import { useState as useState16, useEffect as useEffect13 } from "react";
2328
+ import { Box as Box14, Text as Text14, useInput as useInput12 } from "ink";
2329
+ import {
2330
+ ObservationStore,
2331
+ LearningStore,
2332
+ getMemoryStatus,
2333
+ getMemoryPaths,
2334
+ createMemoryInjector
2335
+ } from "@skillkit/core";
2336
+ import { jsx as jsx14, jsxs as jsxs14 } from "react/jsx-runtime";
2337
+ function Memory({ rows = 24 }) {
2338
+ const [tab, setTab] = useState16("learnings");
2339
+ const [learnings, setLearnings] = useState16([]);
2340
+ const [observationCount, setObservationCount] = useState16(0);
2341
+ const [globalCount, setGlobalCount] = useState16(0);
2342
+ const [loading, setLoading] = useState16(true);
2343
+ const [sel, setSel] = useState16(0);
2344
+ const [expanded, setExpanded] = useState16(false);
2345
+ const [searchQuery, setSearchQuery] = useState16("");
2346
+ const [searchResults, setSearchResults] = useState16([]);
2347
+ const [isGlobal, setIsGlobal] = useState16(false);
2348
+ const maxVisible = Math.max(5, rows - 10);
2349
+ const currentList = tab === "search" ? searchResults : learnings;
2350
+ const start = Math.max(0, Math.min(sel - Math.floor(maxVisible / 2), currentList.length - maxVisible));
2351
+ const visible = currentList.slice(start, start + maxVisible);
2352
+ useEffect13(() => {
2353
+ loadMemory();
2354
+ }, [isGlobal]);
2355
+ const loadMemory = () => {
2356
+ setLoading(true);
2357
+ try {
2358
+ const projectPath = process.cwd();
2359
+ const status = getMemoryStatus(projectPath);
2360
+ const store = new LearningStore(
2361
+ isGlobal ? "global" : "project",
2362
+ isGlobal ? void 0 : projectPath
2363
+ );
2364
+ setLearnings(store.getAll());
2365
+ if (status.hasObservations) {
2366
+ const obsStore = new ObservationStore(projectPath);
2367
+ setObservationCount(obsStore.count());
2368
+ } else {
2369
+ setObservationCount(0);
2370
+ }
2371
+ if (status.hasGlobalLearnings) {
2372
+ const globalStore = new LearningStore("global");
2373
+ setGlobalCount(globalStore.count());
2374
+ } else {
2375
+ setGlobalCount(0);
2376
+ }
2377
+ } catch {
2378
+ setLearnings([]);
2379
+ setObservationCount(0);
2380
+ setGlobalCount(0);
2381
+ }
2382
+ setLoading(false);
2383
+ };
2384
+ const handleSearch = (query) => {
2385
+ if (!query.trim()) {
2386
+ setSearchResults([]);
2387
+ return;
2388
+ }
2389
+ try {
2390
+ const projectPath = process.cwd();
2391
+ const injector = createMemoryInjector(projectPath);
2392
+ const results = injector.search(query, {
2393
+ includeGlobal: true,
2394
+ maxLearnings: 20,
2395
+ minRelevance: 0
2396
+ });
2397
+ setSearchResults(results.map((r) => r.learning));
2398
+ } catch {
2399
+ setSearchResults([]);
2400
+ }
2401
+ };
2402
+ useInput12((input, key) => {
2403
+ if (loading) return;
2404
+ if (input === "1") {
2405
+ setTab("learnings");
2406
+ setSel(0);
2407
+ setExpanded(false);
2408
+ } else if (input === "2") {
2409
+ setTab("observations");
2410
+ setSel(0);
2411
+ setExpanded(false);
2412
+ } else if (input === "3") {
2413
+ setTab("search");
2414
+ setSel(0);
2415
+ setExpanded(false);
2416
+ } else if (key.upArrow) {
2417
+ setSel((i) => Math.max(0, i - 1));
2418
+ setExpanded(false);
2419
+ } else if (key.downArrow) {
2420
+ setSel((i) => Math.min(currentList.length - 1, i + 1));
2421
+ setExpanded(false);
2422
+ } else if (input === "f") loadMemory();
2423
+ else if (input === "g") setIsGlobal((g) => !g);
2424
+ else if (key.return) setExpanded((e) => !e);
2425
+ else if (tab === "search" && input && input.length === 1 && !key.ctrl && !key.meta) {
2426
+ const newQuery = searchQuery + input;
2427
+ setSearchQuery(newQuery);
2428
+ handleSearch(newQuery);
2429
+ } else if (tab === "search" && key.backspace) {
2430
+ const newQuery = searchQuery.slice(0, -1);
2431
+ setSearchQuery(newQuery);
2432
+ handleSearch(newQuery);
2433
+ }
2434
+ });
2435
+ const ONE_MINUTE = 6e4;
2436
+ const ONE_HOUR = 36e5;
2437
+ const ONE_DAY = 864e5;
2438
+ function formatDate(iso) {
2439
+ const d = new Date(iso);
2440
+ const diff = Date.now() - d.getTime();
2441
+ if (diff < ONE_MINUTE) return "Just now";
2442
+ if (diff < ONE_HOUR) return `${Math.floor(diff / ONE_MINUTE)}m ago`;
2443
+ if (diff < ONE_DAY) return `${Math.floor(diff / ONE_HOUR)}h ago`;
2444
+ return d.toLocaleDateString();
2445
+ }
2446
+ function formatEffectiveness(eff) {
2447
+ if (eff === void 0) return " -";
2448
+ return eff.toString().padStart(3) + "%";
2449
+ }
2450
+ function getEffectivenessColor(eff) {
2451
+ if (eff === void 0) return void 0;
2452
+ if (eff >= 70) return "green";
2453
+ if (eff >= 40) return "yellow";
2454
+ return "red";
2455
+ }
2456
+ const renderLearnings = () => /* @__PURE__ */ jsxs14(Box14, { flexDirection: "column", children: [
2457
+ currentList.length === 0 && /* @__PURE__ */ jsxs14(Box14, { marginTop: 1, flexDirection: "column", children: [
2458
+ /* @__PURE__ */ jsx14(Text14, { dimColor: true, children: "No learnings found." }),
2459
+ /* @__PURE__ */ jsx14(Text14, { dimColor: true, children: "Run skills to capture learnings, or add manually:" }),
2460
+ /* @__PURE__ */ jsx14(Text14, { dimColor: true, children: ' skillkit memory add --title "..." --content "..."' })
2461
+ ] }),
2462
+ currentList.length > 0 && /* @__PURE__ */ jsxs14(Box14, { marginTop: 1, flexDirection: "column", children: [
2463
+ start > 0 && /* @__PURE__ */ jsxs14(Text14, { dimColor: true, children: [
2464
+ " ",
2465
+ symbols.arrowUp,
2466
+ " ",
2467
+ start,
2468
+ " more"
2469
+ ] }),
2470
+ visible.map((learning, i) => {
2471
+ const idx = start + i;
2472
+ const isSel = idx === sel;
2473
+ return /* @__PURE__ */ jsxs14(Box14, { flexDirection: "column", children: [
2474
+ /* @__PURE__ */ jsxs14(Text14, { inverse: isSel, children: [
2475
+ isSel ? symbols.pointer : " ",
2476
+ /* @__PURE__ */ jsx14(Text14, { color: colors.primary, children: symbols.bullet }),
2477
+ " ",
2478
+ learning.title.slice(0, 35).padEnd(35),
2479
+ " ",
2480
+ /* @__PURE__ */ jsx14(Text14, { dimColor: true, children: learning.tags.slice(0, 2).join(", ").slice(0, 15).padEnd(15) }),
2481
+ " ",
2482
+ /* @__PURE__ */ jsx14(Text14, { color: getEffectivenessColor(learning.effectiveness), children: formatEffectiveness(learning.effectiveness) }),
2483
+ " ",
2484
+ /* @__PURE__ */ jsx14(Text14, { dimColor: true, children: formatDate(learning.updatedAt) })
2485
+ ] }),
2486
+ isSel && expanded && /* @__PURE__ */ jsxs14(Box14, { flexDirection: "column", marginLeft: 3, marginY: 1, children: [
2487
+ /* @__PURE__ */ jsxs14(Text14, { dimColor: true, children: [
2488
+ "ID: ",
2489
+ learning.id.slice(0, 8)
2490
+ ] }),
2491
+ /* @__PURE__ */ jsxs14(Text14, { dimColor: true, children: [
2492
+ "Scope: ",
2493
+ learning.scope
2494
+ ] }),
2495
+ /* @__PURE__ */ jsxs14(Text14, { dimColor: true, children: [
2496
+ "Source: ",
2497
+ learning.source
2498
+ ] }),
2499
+ /* @__PURE__ */ jsxs14(Text14, { dimColor: true, children: [
2500
+ "Tags: ",
2501
+ learning.tags.join(", ")
2502
+ ] }),
2503
+ learning.frameworks && learning.frameworks.length > 0 && /* @__PURE__ */ jsxs14(Text14, { dimColor: true, children: [
2504
+ "Frameworks: ",
2505
+ learning.frameworks.join(", ")
2506
+ ] }),
2507
+ /* @__PURE__ */ jsxs14(Text14, { dimColor: true, children: [
2508
+ "Uses: ",
2509
+ learning.useCount
2510
+ ] }),
2511
+ /* @__PURE__ */ jsx14(Box14, { marginTop: 1, children: /* @__PURE__ */ jsxs14(Text14, { wrap: "wrap", children: [
2512
+ learning.content.slice(0, 300),
2513
+ learning.content.length > 300 ? "..." : ""
2514
+ ] }) })
2515
+ ] })
2516
+ ] }, learning.id);
2517
+ }),
2518
+ start + maxVisible < currentList.length && /* @__PURE__ */ jsxs14(Text14, { dimColor: true, children: [
2519
+ " ",
2520
+ symbols.arrowDown,
2521
+ " ",
2522
+ currentList.length - start - maxVisible,
2523
+ " more"
2524
+ ] })
2525
+ ] })
2526
+ ] });
2527
+ const renderObservations = () => /* @__PURE__ */ jsxs14(Box14, { flexDirection: "column", marginTop: 1, children: [
2528
+ /* @__PURE__ */ jsxs14(Text14, { children: [
2529
+ "Session Observations: ",
2530
+ /* @__PURE__ */ jsx14(Text14, { bold: true, color: colors.primary, children: observationCount })
2531
+ ] }),
2532
+ /* @__PURE__ */ jsx14(Text14, { dimColor: true, children: "Observations are raw captures from skill execution." }),
2533
+ observationCount > 0 && /* @__PURE__ */ jsxs14(Box14, { marginTop: 1, flexDirection: "column", children: [
2534
+ /* @__PURE__ */ jsx14(Text14, { children: "Compress observations into learnings:" }),
2535
+ /* @__PURE__ */ jsx14(Text14, { dimColor: true, children: " skillkit memory compress" })
2536
+ ] }),
2537
+ observationCount >= 50 && /* @__PURE__ */ jsx14(Box14, { marginTop: 1, children: /* @__PURE__ */ jsxs14(Text14, { color: "yellow", children: [
2538
+ symbols.warning,
2539
+ " You have many uncompressed observations."
2540
+ ] }) }),
2541
+ /* @__PURE__ */ jsx14(Box14, { marginTop: 1, children: /* @__PURE__ */ jsx14(Text14, { dimColor: true, children: "Observations are stored at:" }) }),
2542
+ /* @__PURE__ */ jsxs14(Text14, { dimColor: true, children: [
2543
+ " ",
2544
+ getMemoryPaths(process.cwd()).observationsFile
2545
+ ] })
2546
+ ] });
2547
+ const renderSearch = () => /* @__PURE__ */ jsxs14(Box14, { flexDirection: "column", marginTop: 1, children: [
2548
+ /* @__PURE__ */ jsxs14(Box14, { children: [
2549
+ /* @__PURE__ */ jsx14(Text14, { children: "Search: " }),
2550
+ /* @__PURE__ */ jsx14(Text14, { color: colors.primary, children: searchQuery }),
2551
+ /* @__PURE__ */ jsx14(Text14, { color: colors.secondary, children: "_" })
2552
+ ] }),
2553
+ searchQuery && searchResults.length === 0 && /* @__PURE__ */ jsx14(Box14, { marginTop: 1, children: /* @__PURE__ */ jsxs14(Text14, { dimColor: true, children: [
2554
+ 'No results found for "',
2555
+ searchQuery,
2556
+ '"'
2557
+ ] }) }),
2558
+ searchResults.length > 0 && renderLearnings()
2559
+ ] });
2560
+ return /* @__PURE__ */ jsxs14(Box14, { flexDirection: "column", children: [
2561
+ /* @__PURE__ */ jsx14(Text14, { bold: true, color: colors.primary, children: "MEMORY" }),
2562
+ /* @__PURE__ */ jsxs14(Text14, { dimColor: true, children: [
2563
+ isGlobal ? "Global" : "Project",
2564
+ ": ",
2565
+ learnings.length,
2566
+ " learning(s)",
2567
+ !isGlobal && globalCount > 0 && /* @__PURE__ */ jsxs14(Text14, { children: [
2568
+ " | Global: ",
2569
+ globalCount
2570
+ ] })
2571
+ ] }),
2572
+ /* @__PURE__ */ jsxs14(Box14, { marginTop: 1, children: [
2573
+ /* @__PURE__ */ jsx14(Text14, { inverse: tab === "learnings", children: "[1] Learnings" }),
2574
+ /* @__PURE__ */ jsx14(Text14, { children: " " }),
2575
+ /* @__PURE__ */ jsx14(Text14, { inverse: tab === "observations", children: "[2] Observations" }),
2576
+ /* @__PURE__ */ jsx14(Text14, { children: " " }),
2577
+ /* @__PURE__ */ jsx14(Text14, { inverse: tab === "search", children: "[3] Search" })
2578
+ ] }),
2579
+ loading && /* @__PURE__ */ jsx14(Box14, { marginTop: 1, children: /* @__PURE__ */ jsx14(Text14, { children: "Loading..." }) }),
2580
+ !loading && tab === "learnings" && renderLearnings(),
2581
+ !loading && tab === "observations" && renderObservations(),
2582
+ !loading && tab === "search" && renderSearch(),
2583
+ /* @__PURE__ */ jsx14(Box14, { marginTop: 1, children: /* @__PURE__ */ jsx14(Text14, { dimColor: true, children: "Enter=expand g=toggle global f=refresh 1-3=tabs" }) })
2584
+ ] });
2585
+ }
2586
+
2587
+ // src/screens/Team.tsx
2588
+ import { useState as useState17, useEffect as useEffect14 } from "react";
2589
+ import { Box as Box15, Text as Text15, useInput as useInput13 } from "ink";
2590
+ import { jsx as jsx15, jsxs as jsxs15 } from "react/jsx-runtime";
2591
+ function Team({ rows = 24 }) {
2592
+ const [config, setConfig] = useState17(null);
2593
+ const [skills, setSkills] = useState17([]);
2594
+ const [loading, setLoading] = useState17(true);
2595
+ const [error, setError] = useState17(null);
2596
+ const [sel, setSel] = useState17(0);
2597
+ const [message, setMessage] = useState17(null);
2598
+ const maxVisible = Math.max(5, rows - 12);
2599
+ const start = Math.max(0, Math.min(sel - Math.floor(maxVisible / 2), skills.length - maxVisible));
2600
+ const visible = skills.slice(start, start + maxVisible);
2601
+ useEffect14(() => {
2602
+ loadTeam();
2603
+ }, []);
2604
+ const loadTeam = async () => {
2605
+ setLoading(true);
2606
+ setError(null);
2607
+ try {
2608
+ const { createTeamManager } = await import("@skillkit/core");
2609
+ const manager = createTeamManager(process.cwd());
2610
+ const teamConfig = manager.load();
2611
+ if (!teamConfig) {
2612
+ setConfig(null);
2613
+ setSkills([]);
2614
+ setError("Team not initialized. Run `skillkit team init` first.");
2615
+ } else {
2616
+ setConfig(teamConfig);
2617
+ const sharedSkills = manager.listSharedSkills();
2618
+ setSkills(sharedSkills);
2619
+ setSel((s) => Math.min(s, Math.max(0, sharedSkills.length - 1)));
2620
+ }
2621
+ } catch (e) {
2622
+ setError(e instanceof Error ? e.message : "Failed to load team");
2623
+ }
2624
+ setLoading(false);
2625
+ };
2626
+ const syncTeam = async () => {
2627
+ if (!config) return;
2628
+ setLoading(true);
2629
+ setMessage(null);
2630
+ try {
2631
+ const { createTeamManager } = await import("@skillkit/core");
2632
+ const manager = createTeamManager(process.cwd());
2633
+ manager.load();
2634
+ const result = await manager.sync();
2635
+ const changes = [...result.added, ...result.updated];
2636
+ if (changes.length > 0) {
2637
+ setMessage(`Synced: ${changes.join(", ")}`);
2638
+ } else {
2639
+ setMessage("Already up to date");
2640
+ }
2641
+ const sharedSkills = manager.listSharedSkills();
2642
+ setSkills(sharedSkills);
2643
+ setSel((s) => Math.min(s, Math.max(0, sharedSkills.length - 1)));
2644
+ } catch (e) {
2645
+ setError(e instanceof Error ? e.message : "Sync failed");
2646
+ }
2647
+ setLoading(false);
2648
+ };
2649
+ const importSkill = async (skillName) => {
2650
+ if (!config) return;
2651
+ setLoading(true);
2652
+ setMessage(null);
2653
+ try {
2654
+ const { createTeamManager } = await import("@skillkit/core");
2655
+ const manager = createTeamManager(process.cwd());
2656
+ manager.load();
2657
+ const result = await manager.importSkill(skillName, { overwrite: false });
2658
+ if (result.success) {
2659
+ setMessage(`Imported: ${skillName}`);
2660
+ } else {
2661
+ setError(result.error || "Import failed");
2662
+ }
2663
+ } catch (e) {
2664
+ setError(e instanceof Error ? e.message : "Import failed");
2665
+ }
2666
+ setLoading(false);
2667
+ };
2668
+ useInput13((input, key) => {
2669
+ if (loading) return;
2670
+ if (skills.length === 0 && (key.upArrow || key.downArrow)) return;
2671
+ if (key.upArrow) setSel((i) => Math.max(0, i - 1));
2672
+ else if (key.downArrow) setSel((i) => Math.min(Math.max(0, skills.length - 1), i + 1));
2673
+ else if (input === "r") loadTeam();
2674
+ else if (input === "s") syncTeam();
2675
+ else if (key.return && skills[sel]) {
2676
+ importSkill(skills[sel].name);
2677
+ }
2678
+ });
2679
+ if (loading) {
2680
+ return /* @__PURE__ */ jsxs15(Box15, { flexDirection: "column", padding: 1, children: [
2681
+ /* @__PURE__ */ jsx15(Text15, { color: colors.primary, bold: true, children: "Team Collaboration" }),
2682
+ /* @__PURE__ */ jsx15(Box15, { marginTop: 1, children: /* @__PURE__ */ jsxs15(Text15, { color: colors.secondaryDim, children: [
2683
+ symbols.spinner[0],
2684
+ " Loading..."
2685
+ ] }) })
2686
+ ] });
2687
+ }
2688
+ if (!config) {
2689
+ return /* @__PURE__ */ jsxs15(Box15, { flexDirection: "column", padding: 1, children: [
2690
+ /* @__PURE__ */ jsx15(Text15, { color: colors.primary, bold: true, children: "Team Collaboration" }),
2691
+ /* @__PURE__ */ jsxs15(Box15, { marginTop: 1, flexDirection: "column", children: [
2692
+ /* @__PURE__ */ jsxs15(Text15, { color: colors.secondaryDim, children: [
2693
+ symbols.warning,
2694
+ " Team not initialized"
2695
+ ] }),
2696
+ /* @__PURE__ */ jsx15(Text15, { color: colors.secondaryDim, dimColor: true, children: 'Run: skillkit team init --name "Team Name" --registry <url>' })
2697
+ ] }),
2698
+ /* @__PURE__ */ jsx15(Box15, { marginTop: 1, children: /* @__PURE__ */ jsx15(Text15, { dimColor: true, children: "[r] Refresh" }) })
2699
+ ] });
2700
+ }
2701
+ return /* @__PURE__ */ jsxs15(Box15, { flexDirection: "column", padding: 1, children: [
2702
+ /* @__PURE__ */ jsxs15(Text15, { color: colors.primary, bold: true, children: [
2703
+ "Team: ",
2704
+ config.teamName
2705
+ ] }),
2706
+ /* @__PURE__ */ jsxs15(Text15, { color: colors.secondaryDim, dimColor: true, children: [
2707
+ "Registry: ",
2708
+ config.registryUrl
2709
+ ] }),
2710
+ error && /* @__PURE__ */ jsx15(Box15, { marginTop: 1, children: /* @__PURE__ */ jsxs15(Text15, { color: "red", children: [
2711
+ symbols.error,
2712
+ " ",
2713
+ error
2714
+ ] }) }),
2715
+ message && /* @__PURE__ */ jsx15(Box15, { marginTop: 1, children: /* @__PURE__ */ jsxs15(Text15, { color: "green", children: [
2716
+ symbols.success,
2717
+ " ",
2718
+ message
2719
+ ] }) }),
2720
+ /* @__PURE__ */ jsxs15(Box15, { marginTop: 1, flexDirection: "column", children: [
2721
+ /* @__PURE__ */ jsxs15(Text15, { color: colors.secondaryDim, bold: true, children: [
2722
+ "Shared Skills (",
2723
+ skills.length,
2724
+ ")"
2725
+ ] }),
2726
+ skills.length === 0 ? /* @__PURE__ */ jsx15(Text15, { color: colors.secondaryDim, dimColor: true, children: "No shared skills. Use `skillkit team share --name <skill>` to share." }) : /* @__PURE__ */ jsx15(Box15, { flexDirection: "column", marginTop: 1, children: visible.map((skill, i) => {
2727
+ const actualIndex = start + i;
2728
+ const isSelected = actualIndex === sel;
2729
+ return /* @__PURE__ */ jsxs15(Box15, { flexDirection: "column", children: [
2730
+ /* @__PURE__ */ jsxs15(Text15, { children: [
2731
+ isSelected ? symbols.pointer : " ",
2732
+ " ",
2733
+ /* @__PURE__ */ jsx15(Text15, { color: isSelected ? colors.primary : colors.secondaryDim, bold: isSelected, children: skill.name }),
2734
+ /* @__PURE__ */ jsxs15(Text15, { dimColor: true, children: [
2735
+ " v",
2736
+ skill.version
2737
+ ] }),
2738
+ /* @__PURE__ */ jsxs15(Text15, { dimColor: true, children: [
2739
+ " by ",
2740
+ skill.author
2741
+ ] }),
2742
+ skill.downloads !== void 0 && /* @__PURE__ */ jsxs15(Text15, { dimColor: true, children: [
2743
+ " | ",
2744
+ skill.downloads,
2745
+ " downloads"
2746
+ ] })
2747
+ ] }),
2748
+ skill.description && isSelected && /* @__PURE__ */ jsxs15(Text15, { color: colors.secondaryDim, dimColor: true, children: [
2749
+ " ",
2750
+ skill.description
2751
+ ] })
2752
+ ] }, skill.name);
2753
+ }) })
2754
+ ] }),
2755
+ /* @__PURE__ */ jsx15(Box15, { marginTop: 1, borderStyle: "single", borderColor: colors.borderDim, paddingX: 1, children: /* @__PURE__ */ jsx15(Text15, { dimColor: true, children: "[\u2191\u2193] Navigate [Enter] Import [s] Sync [r] Refresh" }) })
2756
+ ] });
2757
+ }
2758
+
2759
+ // src/screens/Plugins.tsx
2760
+ import { useState as useState18, useEffect as useEffect15 } from "react";
2761
+ import { Box as Box16, Text as Text16, useInput as useInput14 } from "ink";
2762
+ import { jsx as jsx16, jsxs as jsxs16 } from "react/jsx-runtime";
2763
+ function Plugins({ rows = 24 }) {
2764
+ const [plugins, setPlugins] = useState18([]);
2765
+ const [loading, setLoading] = useState18(true);
2766
+ const [error, setError] = useState18(null);
2767
+ const [sel, setSel] = useState18(0);
2768
+ const [message, setMessage] = useState18(null);
2769
+ const maxVisible = Math.max(5, rows - 12);
2770
+ const start = Math.max(0, Math.min(sel - Math.floor(maxVisible / 2), plugins.length - maxVisible));
2771
+ const visible = plugins.slice(start, start + maxVisible);
2772
+ useEffect15(() => {
2773
+ loadPlugins();
2774
+ }, []);
2775
+ const loadPlugins = async () => {
2776
+ setLoading(true);
2777
+ setError(null);
2778
+ try {
2779
+ const { createPluginManager, loadPluginsFromDirectory } = await import("@skillkit/core");
2780
+ const { join: join6 } = await import("path");
2781
+ const manager = createPluginManager(process.cwd());
2782
+ const pluginsDir = join6(process.cwd(), ".skillkit", "plugins");
2783
+ try {
2784
+ const loadedPlugins = await loadPluginsFromDirectory(pluginsDir);
2785
+ for (const plugin of loadedPlugins) {
2786
+ if (!manager.getPlugin(plugin.metadata.name)) {
2787
+ await manager.register(plugin);
2788
+ }
2789
+ }
2790
+ } catch (err) {
2791
+ if (err && typeof err === "object" && "code" in err && err.code !== "ENOENT") {
2792
+ throw err;
2793
+ }
2794
+ }
2795
+ const allPlugins = manager.listPlugins();
2796
+ const pluginInfos = allPlugins.map((p) => {
2797
+ const plugin = manager.getPlugin(p.name);
2798
+ return {
2799
+ name: p.name,
2800
+ version: p.version,
2801
+ description: p.description,
2802
+ author: p.author,
2803
+ enabled: manager.isPluginEnabled(p.name),
2804
+ translators: plugin?.translators?.map((t) => t.agentType),
2805
+ providers: plugin?.providers?.map((pr) => pr.providerName),
2806
+ commands: plugin?.commands?.map((c) => c.name)
2807
+ };
2808
+ });
2809
+ setPlugins(pluginInfos);
2810
+ } catch (e) {
2811
+ setError(e instanceof Error ? e.message : "Failed to load plugins");
2812
+ }
2813
+ setLoading(false);
2814
+ };
2815
+ const togglePlugin = async (pluginName, currentEnabled) => {
2816
+ setLoading(true);
2817
+ setMessage(null);
2818
+ try {
2819
+ const { createPluginManager, loadPluginsFromDirectory } = await import("@skillkit/core");
2820
+ const { join: join6 } = await import("path");
2821
+ const manager = createPluginManager(process.cwd());
2822
+ const pluginsDir = join6(process.cwd(), ".skillkit", "plugins");
2823
+ try {
2824
+ const loadedPlugins = await loadPluginsFromDirectory(pluginsDir);
2825
+ for (const plugin of loadedPlugins) {
2826
+ if (!manager.getPlugin(plugin.metadata.name)) {
2827
+ await manager.register(plugin);
2828
+ }
2829
+ }
2830
+ } catch (err) {
2831
+ if (err && typeof err === "object" && "code" in err && err.code !== "ENOENT") {
2832
+ throw err;
2833
+ }
2834
+ }
2835
+ if (currentEnabled) {
2836
+ manager.disablePlugin(pluginName);
2837
+ setMessage(`Disabled: ${pluginName}`);
2838
+ } else {
2839
+ manager.enablePlugin(pluginName);
2840
+ setMessage(`Enabled: ${pluginName}`);
2841
+ }
2842
+ await loadPlugins();
2843
+ } catch (e) {
2844
+ setError(e instanceof Error ? e.message : "Toggle failed");
2845
+ setLoading(false);
2846
+ }
2847
+ };
2848
+ useInput14((input, key) => {
2849
+ if (loading) return;
2850
+ if (plugins.length === 0 && (key.upArrow || key.downArrow)) return;
2851
+ if (key.upArrow) setSel((i) => Math.max(0, i - 1));
2852
+ else if (key.downArrow) setSel((i) => Math.min(Math.max(0, plugins.length - 1), i + 1));
2853
+ else if (input === "r") loadPlugins();
2854
+ else if (input === "e" && plugins[sel]) {
2855
+ togglePlugin(plugins[sel].name, plugins[sel].enabled);
2856
+ } else if (key.return && plugins[sel]) {
2857
+ togglePlugin(plugins[sel].name, plugins[sel].enabled);
2858
+ }
2859
+ });
2860
+ if (loading) {
2861
+ return /* @__PURE__ */ jsxs16(Box16, { flexDirection: "column", padding: 1, children: [
2862
+ /* @__PURE__ */ jsx16(Text16, { color: colors.primary, bold: true, children: "Plugin Manager" }),
2863
+ /* @__PURE__ */ jsx16(Box16, { marginTop: 1, children: /* @__PURE__ */ jsxs16(Text16, { color: colors.secondaryDim, children: [
2864
+ symbols.spinner[0],
2865
+ " Loading..."
2866
+ ] }) })
2867
+ ] });
2868
+ }
2869
+ return /* @__PURE__ */ jsxs16(Box16, { flexDirection: "column", padding: 1, children: [
2870
+ /* @__PURE__ */ jsx16(Text16, { color: colors.primary, bold: true, children: "Plugin Manager" }),
2871
+ /* @__PURE__ */ jsx16(Text16, { color: colors.secondaryDim, dimColor: true, children: "Manage SkillKit extensions" }),
2872
+ error && /* @__PURE__ */ jsx16(Box16, { marginTop: 1, children: /* @__PURE__ */ jsxs16(Text16, { color: "red", children: [
2873
+ symbols.error,
2874
+ " ",
2875
+ error
2876
+ ] }) }),
2877
+ message && /* @__PURE__ */ jsx16(Box16, { marginTop: 1, children: /* @__PURE__ */ jsxs16(Text16, { color: "green", children: [
2878
+ symbols.success,
2879
+ " ",
2880
+ message
2881
+ ] }) }),
2882
+ /* @__PURE__ */ jsxs16(Box16, { marginTop: 1, flexDirection: "column", children: [
2883
+ /* @__PURE__ */ jsxs16(Text16, { color: colors.secondaryDim, bold: true, children: [
2884
+ "Installed Plugins (",
2885
+ plugins.length,
2886
+ ")"
2887
+ ] }),
2888
+ plugins.length === 0 ? /* @__PURE__ */ jsxs16(Box16, { marginTop: 1, flexDirection: "column", children: [
2889
+ /* @__PURE__ */ jsx16(Text16, { color: colors.secondaryDim, dimColor: true, children: "No plugins installed." }),
2890
+ /* @__PURE__ */ jsx16(Text16, { color: colors.secondaryDim, dimColor: true, children: "Use: skillkit plugin install --source <path-or-package>" })
2891
+ ] }) : /* @__PURE__ */ jsx16(Box16, { flexDirection: "column", marginTop: 1, children: visible.map((plugin, i) => {
2892
+ const actualIndex = start + i;
2893
+ const isSelected = actualIndex === sel;
2894
+ const statusColor = plugin.enabled ? "green" : "gray";
2895
+ const statusText = plugin.enabled ? "enabled" : "disabled";
2896
+ return /* @__PURE__ */ jsxs16(Box16, { flexDirection: "column", children: [
2897
+ /* @__PURE__ */ jsxs16(Text16, { children: [
2898
+ isSelected ? symbols.pointer : " ",
2899
+ " ",
2900
+ /* @__PURE__ */ jsx16(Text16, { color: isSelected ? colors.primary : colors.secondaryDim, bold: isSelected, children: plugin.name }),
2901
+ /* @__PURE__ */ jsxs16(Text16, { dimColor: true, children: [
2902
+ " v",
2903
+ plugin.version
2904
+ ] }),
2905
+ /* @__PURE__ */ jsxs16(Text16, { color: statusColor, children: [
2906
+ " [",
2907
+ statusText,
2908
+ "]"
2909
+ ] })
2910
+ ] }),
2911
+ isSelected && /* @__PURE__ */ jsxs16(Box16, { flexDirection: "column", marginLeft: 3, children: [
2912
+ plugin.description && /* @__PURE__ */ jsx16(Text16, { color: colors.secondaryDim, dimColor: true, children: plugin.description }),
2913
+ plugin.author && /* @__PURE__ */ jsxs16(Text16, { dimColor: true, children: [
2914
+ "Author: ",
2915
+ plugin.author
2916
+ ] }),
2917
+ plugin.translators && plugin.translators.length > 0 && /* @__PURE__ */ jsxs16(Text16, { dimColor: true, children: [
2918
+ "Translators: ",
2919
+ plugin.translators.join(", ")
2920
+ ] }),
2921
+ plugin.providers && plugin.providers.length > 0 && /* @__PURE__ */ jsxs16(Text16, { dimColor: true, children: [
2922
+ "Providers: ",
2923
+ plugin.providers.join(", ")
2924
+ ] }),
2925
+ plugin.commands && plugin.commands.length > 0 && /* @__PURE__ */ jsxs16(Text16, { dimColor: true, children: [
2926
+ "Commands: ",
2927
+ plugin.commands.join(", ")
2928
+ ] })
2929
+ ] })
2930
+ ] }, plugin.name);
2931
+ }) })
2932
+ ] }),
2933
+ /* @__PURE__ */ jsx16(Box16, { marginTop: 1, borderStyle: "single", borderColor: colors.borderDim, paddingX: 1, children: /* @__PURE__ */ jsx16(Text16, { dimColor: true, children: "[\u2191\u2193] Navigate [e/Enter] Toggle Enable/Disable [r] Refresh" }) })
2934
+ ] });
2935
+ }
2936
+
2937
+ // src/App.tsx
2938
+ import { jsx as jsx17, jsxs as jsxs17 } from "react/jsx-runtime";
1817
2939
  function App() {
1818
- const [screen, setScreen] = useState12("home");
2940
+ const [screen, setScreen] = useState19("home");
1819
2941
  const { exit } = useApp();
1820
2942
  const { stdout } = useStdout();
1821
2943
  const cols = stdout?.columns || 80;
1822
2944
  const rows = stdout?.rows || 24;
1823
2945
  const showSidebar = cols >= 70;
1824
- useInput8((input, key) => {
2946
+ const NAV_KEYS = {
2947
+ h: "home",
2948
+ m: "marketplace",
2949
+ b: "browse",
2950
+ w: "workflow",
2951
+ x: "execute",
2952
+ y: "history",
2953
+ r: "recommend",
2954
+ t: "translate",
2955
+ c: "context",
2956
+ e: "memory",
2957
+ i: "installed",
2958
+ s: "sync",
2959
+ a: "team",
2960
+ p: "plugins",
2961
+ ",": "settings"
2962
+ };
2963
+ useInput15((input, key) => {
1825
2964
  if (input === "q") {
1826
2965
  exit();
1827
2966
  return;
@@ -1830,65 +2969,75 @@ function App() {
1830
2969
  setScreen("home");
1831
2970
  return;
1832
2971
  }
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");
2972
+ const targetScreen = NAV_KEYS[input];
2973
+ if (targetScreen) {
2974
+ setScreen(targetScreen);
2975
+ }
1841
2976
  });
1842
2977
  const renderScreen = () => {
1843
2978
  switch (screen) {
1844
2979
  case "home":
1845
- return /* @__PURE__ */ jsx10(Home, { onNavigate: setScreen, cols, rows });
2980
+ return /* @__PURE__ */ jsx17(Home, { onNavigate: setScreen, cols, rows });
1846
2981
  case "browse":
1847
- return /* @__PURE__ */ jsx10(Browse, { cols, rows });
2982
+ return /* @__PURE__ */ jsx17(Browse, { cols, rows });
1848
2983
  case "installed":
1849
- return /* @__PURE__ */ jsx10(Installed, { cols, rows });
2984
+ return /* @__PURE__ */ jsx17(Installed, { cols, rows });
1850
2985
  case "sync":
1851
- return /* @__PURE__ */ jsx10(Sync, { cols, rows });
2986
+ return /* @__PURE__ */ jsx17(Sync, { cols, rows });
1852
2987
  case "settings":
1853
- return /* @__PURE__ */ jsx10(Settings, { cols, rows });
2988
+ return /* @__PURE__ */ jsx17(Settings, { cols, rows });
1854
2989
  case "recommend":
1855
- return /* @__PURE__ */ jsx10(Recommend, { cols, rows });
2990
+ return /* @__PURE__ */ jsx17(Recommend, { cols, rows });
1856
2991
  case "translate":
1857
- return /* @__PURE__ */ jsx10(Translate, { cols, rows });
2992
+ return /* @__PURE__ */ jsx17(Translate, { cols, rows });
1858
2993
  case "context":
1859
- return /* @__PURE__ */ jsx10(Context, { cols, rows });
2994
+ return /* @__PURE__ */ jsx17(Context, { cols, rows });
2995
+ case "workflow":
2996
+ return /* @__PURE__ */ jsx17(Workflow, { cols, rows });
2997
+ case "execute":
2998
+ return /* @__PURE__ */ jsx17(Execute, { cols, rows });
2999
+ case "history":
3000
+ return /* @__PURE__ */ jsx17(History, { cols, rows });
3001
+ case "marketplace":
3002
+ return /* @__PURE__ */ jsx17(Marketplace, { cols, rows });
3003
+ case "memory":
3004
+ return /* @__PURE__ */ jsx17(Memory, { cols, rows });
3005
+ case "team":
3006
+ return /* @__PURE__ */ jsx17(Team, { cols, rows });
3007
+ case "plugins":
3008
+ return /* @__PURE__ */ jsx17(Plugins, { cols, rows });
1860
3009
  }
1861
3010
  };
1862
3011
  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() })
3012
+ return /* @__PURE__ */ jsxs17(Box17, { flexDirection: "column", height: rows, children: [
3013
+ /* @__PURE__ */ jsxs17(Box17, { flexDirection: "row", height: contentHeight, children: [
3014
+ showSidebar && /* @__PURE__ */ jsx17(Sidebar, { screen, onNavigate: setScreen }),
3015
+ /* @__PURE__ */ jsx17(Box17, { flexDirection: "column", flexGrow: 1, marginLeft: 1, children: renderScreen() })
1867
3016
  ] }),
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" }) })
3017
+ /* @__PURE__ */ jsx17(Box17, { children: /* @__PURE__ */ jsx17(Text17, { dimColor: true, children: "h Home m Market b Browse i Inst w Wflow x Exec a Team p Plug r Rec t Trans c Ctx e Mem s Sync , Cfg q Quit" }) })
1869
3018
  ] });
1870
3019
  }
1871
3020
 
1872
3021
  // 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";
3022
+ import { Box as Box18, Text as Text18 } from "ink";
3023
+ import { jsx as jsx18, jsxs as jsxs18 } from "react/jsx-runtime";
1875
3024
  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: [
3025
+ return /* @__PURE__ */ jsxs18(Box18, { flexDirection: "column", marginBottom: 1, children: [
3026
+ /* @__PURE__ */ jsxs18(Box18, { justifyContent: "space-between", children: [
3027
+ /* @__PURE__ */ jsx18(Text18, { color: colors.primary, bold: true, children: title.toUpperCase() }),
3028
+ count !== void 0 && /* @__PURE__ */ jsxs18(Text18, { color: colors.secondaryDim, children: [
1880
3029
  symbols.star,
1881
3030
  " ",
1882
3031
  count
1883
3032
  ] })
1884
3033
  ] }),
1885
- subtitle && /* @__PURE__ */ jsx11(Text11, { color: colors.secondaryDim, dimColor: true, children: subtitle })
3034
+ subtitle && /* @__PURE__ */ jsx18(Text18, { color: colors.secondaryDim, dimColor: true, children: subtitle })
1886
3035
  ] });
1887
3036
  }
1888
3037
 
1889
3038
  // 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";
3039
+ import { Box as Box19, Text as Text19 } from "ink";
3040
+ import { jsx as jsx19, jsxs as jsxs19 } from "react/jsx-runtime";
1892
3041
  function formatInstalls(count) {
1893
3042
  if (count >= 1e3) {
1894
3043
  return `${(count / 1e3).toFixed(1)}K`;
@@ -1904,13 +3053,13 @@ function SkillList({
1904
3053
  maxVisible = 10
1905
3054
  }) {
1906
3055
  if (skills.length === 0) {
1907
- return /* @__PURE__ */ jsx12(Box12, { children: /* @__PURE__ */ jsx12(Text12, { color: colors.secondaryDim, dimColor: true, children: "No skills found" }) });
3056
+ return /* @__PURE__ */ jsx19(Box19, { children: /* @__PURE__ */ jsx19(Text19, { color: colors.secondaryDim, dimColor: true, children: "No skills found" }) });
1908
3057
  }
1909
3058
  const startIndex = Math.max(0, selectedIndex - Math.floor(maxVisible / 2));
1910
3059
  const visibleSkills = skills.slice(startIndex, startIndex + maxVisible);
1911
3060
  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: [
3061
+ return /* @__PURE__ */ jsxs19(Box19, { flexDirection: "column", children: [
3062
+ showRank && /* @__PURE__ */ jsx19(Box19, { marginBottom: 1, children: /* @__PURE__ */ jsxs19(Text19, { color: colors.secondaryDim, children: [
1914
3063
  " # SKILL",
1915
3064
  showSource && " SOURCE",
1916
3065
  showInstalls && " INSTALLS"
@@ -1920,9 +3069,9 @@ function SkillList({
1920
3069
  const isSelected = realIndex === selectedIndex;
1921
3070
  const skillName = skill.name.padEnd(28).slice(0, 28);
1922
3071
  const sourceName = skill.source ? skill.source.slice(0, 25) : "";
1923
- return /* @__PURE__ */ jsxs12(Box12, { children: [
1924
- /* @__PURE__ */ jsxs12(
1925
- Text12,
3072
+ return /* @__PURE__ */ jsxs19(Box19, { children: [
3073
+ /* @__PURE__ */ jsxs19(
3074
+ Text19,
1926
3075
  {
1927
3076
  color: isSelected ? colors.primary : colors.secondary,
1928
3077
  bold: isSelected,
@@ -1935,14 +3084,14 @@ function SkillList({
1935
3084
  ]
1936
3085
  }
1937
3086
  ),
1938
- showSource && /* @__PURE__ */ jsxs12(Text12, { color: colors.secondaryDim, dimColor: !isSelected, children: [
3087
+ showSource && /* @__PURE__ */ jsxs19(Text19, { color: colors.secondaryDim, dimColor: !isSelected, children: [
1939
3088
  " ",
1940
3089
  sourceName
1941
3090
  ] }),
1942
- showInstalls && skill.installs !== void 0 && /* @__PURE__ */ jsx12(Text12, { color: colors.secondaryDim, children: formatInstalls(skill.installs).padStart(8) })
3091
+ showInstalls && skill.installs !== void 0 && /* @__PURE__ */ jsx19(Text19, { color: colors.secondaryDim, children: formatInstalls(skill.installs).padStart(8) })
1943
3092
  ] }, `${skill.source}-${skill.name}`);
1944
3093
  }),
1945
- skills.length > maxVisible && /* @__PURE__ */ jsx12(Box12, { marginTop: 1, children: /* @__PURE__ */ jsxs12(Text12, { color: colors.secondaryDim, dimColor: true, children: [
3094
+ skills.length > maxVisible && /* @__PURE__ */ jsx19(Box19, { marginTop: 1, children: /* @__PURE__ */ jsxs19(Text19, { color: colors.secondaryDim, dimColor: true, children: [
1946
3095
  "Showing ",
1947
3096
  startIndex + 1,
1948
3097
  "-",
@@ -1954,11 +3103,11 @@ function SkillList({
1954
3103
  }
1955
3104
 
1956
3105
  // 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";
3106
+ import { Box as Box20, Text as Text20 } from "ink";
3107
+ import { jsx as jsx20, jsxs as jsxs20 } from "react/jsx-runtime";
1959
3108
  function StatusBar({ shortcuts, message }) {
1960
- return /* @__PURE__ */ jsxs13(
1961
- Box13,
3109
+ return /* @__PURE__ */ jsxs20(
3110
+ Box20,
1962
3111
  {
1963
3112
  borderStyle: "single",
1964
3113
  borderColor: colors.borderDim,
@@ -1969,11 +3118,11 @@ function StatusBar({ shortcuts, message }) {
1969
3118
  paddingX: 1,
1970
3119
  justifyContent: "space-between",
1971
3120
  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 })
3121
+ /* @__PURE__ */ jsx20(Box20, { gap: 2, children: shortcuts.map((shortcut, idx) => /* @__PURE__ */ jsxs20(Box20, { gap: 1, children: [
3122
+ /* @__PURE__ */ jsx20(Text20, { color: colors.primary, bold: true, children: shortcut.key }),
3123
+ /* @__PURE__ */ jsx20(Text20, { color: colors.secondaryDim, children: shortcut.label })
1975
3124
  ] }, idx)) }),
1976
- message && /* @__PURE__ */ jsxs13(Text13, { color: colors.success, children: [
3125
+ message && /* @__PURE__ */ jsxs20(Text20, { color: colors.success, children: [
1977
3126
  symbols.check,
1978
3127
  " ",
1979
3128
  message
@@ -1984,44 +3133,44 @@ function StatusBar({ shortcuts, message }) {
1984
3133
  }
1985
3134
 
1986
3135
  // 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";
3136
+ import { Box as Box21, Text as Text21 } from "ink";
3137
+ import TextInput2 from "ink-text-input";
3138
+ import { jsx as jsx21, jsxs as jsxs21 } from "react/jsx-runtime";
1990
3139
  function SearchInput({
1991
3140
  value,
1992
3141
  onChange,
1993
3142
  placeholder = "Search skills...",
1994
3143
  isFocused = false
1995
3144
  }) {
1996
- return /* @__PURE__ */ jsxs14(
1997
- Box14,
3145
+ return /* @__PURE__ */ jsxs21(
3146
+ Box21,
1998
3147
  {
1999
3148
  borderStyle: "single",
2000
3149
  borderColor: isFocused ? colors.primary : colors.borderDim,
2001
3150
  paddingX: 1,
2002
3151
  children: [
2003
- /* @__PURE__ */ jsx14(Text14, { color: colors.secondaryDim, children: "/ " }),
2004
- isFocused ? /* @__PURE__ */ jsx14(
2005
- TextInput,
3152
+ /* @__PURE__ */ jsx21(Text21, { color: colors.secondaryDim, children: "/ " }),
3153
+ isFocused ? /* @__PURE__ */ jsx21(
3154
+ TextInput2,
2006
3155
  {
2007
3156
  value,
2008
3157
  onChange,
2009
3158
  placeholder
2010
3159
  }
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: "/" })
3160
+ ) : /* @__PURE__ */ jsx21(Text21, { color: value ? colors.secondary : colors.secondaryDim, children: value || placeholder }),
3161
+ /* @__PURE__ */ jsx21(Box21, { flexGrow: 1 }),
3162
+ /* @__PURE__ */ jsx21(Text21, { color: colors.secondaryDim, children: "/" })
2014
3163
  ]
2015
3164
  }
2016
3165
  );
2017
3166
  }
2018
3167
 
2019
3168
  // 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";
3169
+ import { useState as useState20, useCallback as useCallback3, useEffect as useEffect16 } from "react";
3170
+ import { useInput as useInput16, useApp as useApp2 } from "ink";
2022
3171
  function useKeyboard(options = {}) {
2023
3172
  const { exit } = useApp2();
2024
- useInput9((input, key) => {
3173
+ useInput16((input, key) => {
2025
3174
  if (options.disabled) return;
2026
3175
  if (input === "q" || key.ctrl && input === "c") {
2027
3176
  exit();
@@ -2065,8 +3214,8 @@ function useKeyboard(options = {}) {
2065
3214
  });
2066
3215
  }
2067
3216
  function useListNavigation(listLength, initialIndex = 0) {
2068
- const [selectedIndex, setSelectedIndex] = useState13(initialIndex);
2069
- useEffect8(() => {
3217
+ const [selectedIndex, setSelectedIndex] = useState20(initialIndex);
3218
+ useEffect16(() => {
2070
3219
  if (selectedIndex >= listLength && listLength > 0) {
2071
3220
  setSelectedIndex(listLength - 1);
2072
3221
  }
@@ -2083,11 +3232,122 @@ function useListNavigation(listLength, initialIndex = 0) {
2083
3232
  return { selectedIndex, setSelectedIndex, moveUp, moveDown, reset };
2084
3233
  }
2085
3234
 
3235
+ // src/hooks/useMemory.ts
3236
+ import { useState as useState21, useEffect as useEffect17, useCallback as useCallback4 } from "react";
3237
+ import {
3238
+ LearningStore as LearningStore2,
3239
+ ObservationStore as ObservationStore2,
3240
+ getMemoryStatus as getMemoryStatus2,
3241
+ createMemoryInjector as createMemoryInjector2
3242
+ } from "@skillkit/core";
3243
+ function useMemory() {
3244
+ const [learnings, setLearnings] = useState21([]);
3245
+ const [observations, setObservations] = useState21([]);
3246
+ const [status, setStatus] = useState21(null);
3247
+ const [loading, setLoading] = useState21(true);
3248
+ const [error, setError] = useState21(null);
3249
+ const [isGlobal, setIsGlobal] = useState21(false);
3250
+ const projectPath = process.cwd();
3251
+ const refresh = useCallback4(() => {
3252
+ setLoading(true);
3253
+ setError(null);
3254
+ try {
3255
+ const memStatus = getMemoryStatus2(projectPath);
3256
+ setStatus(memStatus);
3257
+ const learningStore = new LearningStore2(
3258
+ isGlobal ? "global" : "project",
3259
+ isGlobal ? void 0 : projectPath
3260
+ );
3261
+ setLearnings(learningStore.getAll());
3262
+ if (memStatus.hasObservations) {
3263
+ const obsStore = new ObservationStore2(projectPath);
3264
+ setObservations(obsStore.getAll());
3265
+ } else {
3266
+ setObservations([]);
3267
+ }
3268
+ } catch (err) {
3269
+ setError(err instanceof Error ? err.message : "Failed to load memory");
3270
+ setLearnings([]);
3271
+ setObservations([]);
3272
+ } finally {
3273
+ setLoading(false);
3274
+ }
3275
+ }, [projectPath, isGlobal]);
3276
+ const search = useCallback4(
3277
+ (query) => {
3278
+ if (!query.trim()) {
3279
+ return [];
3280
+ }
3281
+ try {
3282
+ const injector = createMemoryInjector2(projectPath);
3283
+ const results = injector.search(query, {
3284
+ includeGlobal: true,
3285
+ maxLearnings: 50,
3286
+ minRelevance: 0
3287
+ });
3288
+ return results.map((r) => r.learning);
3289
+ } catch {
3290
+ return [];
3291
+ }
3292
+ },
3293
+ [projectPath]
3294
+ );
3295
+ const deleteLearning = useCallback4(
3296
+ (id) => {
3297
+ try {
3298
+ const store = new LearningStore2(
3299
+ isGlobal ? "global" : "project",
3300
+ isGlobal ? void 0 : projectPath
3301
+ );
3302
+ const result = store.delete(id);
3303
+ if (result) {
3304
+ refresh();
3305
+ }
3306
+ return result;
3307
+ } catch {
3308
+ return false;
3309
+ }
3310
+ },
3311
+ [projectPath, isGlobal, refresh]
3312
+ );
3313
+ const deleteObservation = useCallback4(
3314
+ (id) => {
3315
+ try {
3316
+ const store = new ObservationStore2(projectPath);
3317
+ const result = store.delete(id);
3318
+ if (result) {
3319
+ refresh();
3320
+ }
3321
+ return result;
3322
+ } catch {
3323
+ return false;
3324
+ }
3325
+ },
3326
+ [projectPath, refresh]
3327
+ );
3328
+ useEffect17(() => {
3329
+ refresh();
3330
+ }, [refresh]);
3331
+ return {
3332
+ learnings,
3333
+ observations,
3334
+ status,
3335
+ loading,
3336
+ error,
3337
+ isGlobal,
3338
+ setIsGlobal,
3339
+ refresh,
3340
+ search,
3341
+ deleteLearning,
3342
+ deleteObservation
3343
+ };
3344
+ }
3345
+
2086
3346
  // src/index.tsx
2087
- import { jsx as jsx15 } from "react/jsx-runtime";
3347
+ import { jsx as jsx22 } from "react/jsx-runtime";
2088
3348
  function startTUI() {
2089
3349
  process.stdout.write("\x1B[2J\x1B[0f");
2090
- const { waitUntilExit, clear } = render(/* @__PURE__ */ jsx15(App, {}), {
3350
+ const { waitUntilExit, clear } = render(/* @__PURE__ */ jsx22(App, {}), {
2091
3351
  exitOnCtrlC: true
2092
3352
  });
2093
3353
  return waitUntilExit().then(() => {
@@ -2098,9 +3358,14 @@ export {
2098
3358
  App,
2099
3359
  Browse,
2100
3360
  Context,
3361
+ Execute,
2101
3362
  Header,
3363
+ History,
2102
3364
  Home,
2103
3365
  Installed,
3366
+ Marketplace,
3367
+ Memory,
3368
+ Plugins,
2104
3369
  Recommend,
2105
3370
  SearchInput,
2106
3371
  Settings,
@@ -2108,7 +3373,9 @@ export {
2108
3373
  SkillList,
2109
3374
  StatusBar,
2110
3375
  Sync,
3376
+ Team,
2111
3377
  Translate,
3378
+ Workflow,
2112
3379
  colors,
2113
3380
  logo,
2114
3381
  startTUI,
@@ -2116,6 +3383,7 @@ export {
2116
3383
  useKeyboard,
2117
3384
  useListNavigation,
2118
3385
  useMarketplace,
3386
+ useMemory,
2119
3387
  useRecommend,
2120
3388
  useSkills
2121
3389
  };