@skillkit/tui 1.4.0 → 1.6.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/README.md +68 -5
- package/dist/index.d.ts +39 -14
- package/dist/index.js +1146 -142
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
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
|
|
6
|
-
import { Box as
|
|
5
|
+
import { useState as useState21 } from "react";
|
|
6
|
+
import { Box as Box19, Text as Text19, useInput as useInput17, useApp, useStdout } from "ink";
|
|
7
7
|
|
|
8
8
|
// src/components/Sidebar.tsx
|
|
9
9
|
import { Box, Text } from "ink";
|
|
@@ -55,6 +55,12 @@ var NAV = [
|
|
|
55
55
|
{ id: "workflow", label: "Workflows", key: "w" },
|
|
56
56
|
{ id: "execute", label: "Execute", key: "x" },
|
|
57
57
|
{ id: "history", label: "History", key: "y" },
|
|
58
|
+
// Collaboration
|
|
59
|
+
{ id: "team", label: "Team", key: "a" },
|
|
60
|
+
{ id: "plugins", label: "Plugins", key: "p" },
|
|
61
|
+
// Methodology
|
|
62
|
+
{ id: "methodology", label: "Methodology", key: "o" },
|
|
63
|
+
{ id: "plan", label: "Plans", key: "n" },
|
|
58
64
|
// Tools
|
|
59
65
|
{ id: "recommend", label: "Recommend", key: "r" },
|
|
60
66
|
{ id: "translate", label: "Translate", key: "t" },
|
|
@@ -78,12 +84,22 @@ function Sidebar({ screen }) {
|
|
|
78
84
|
item.label
|
|
79
85
|
] }, item.id)),
|
|
80
86
|
/* @__PURE__ */ jsx(Text, { children: " " }),
|
|
81
|
-
NAV.slice(6,
|
|
87
|
+
NAV.slice(6, 8).map((item) => /* @__PURE__ */ jsxs(Text, { inverse: screen === item.id, children: [
|
|
82
88
|
screen === item.id ? symbols.bullet : " ",
|
|
83
89
|
item.label
|
|
84
90
|
] }, item.id)),
|
|
85
91
|
/* @__PURE__ */ jsx(Text, { children: " " }),
|
|
86
|
-
NAV.slice(10).map((item) => /* @__PURE__ */ jsxs(Text, { inverse: screen === item.id, children: [
|
|
92
|
+
NAV.slice(8, 10).map((item) => /* @__PURE__ */ jsxs(Text, { inverse: screen === item.id, children: [
|
|
93
|
+
screen === item.id ? symbols.bullet : " ",
|
|
94
|
+
item.label
|
|
95
|
+
] }, item.id)),
|
|
96
|
+
/* @__PURE__ */ jsx(Text, { children: " " }),
|
|
97
|
+
NAV.slice(10, 14).map((item) => /* @__PURE__ */ jsxs(Text, { inverse: screen === item.id, children: [
|
|
98
|
+
screen === item.id ? symbols.bullet : " ",
|
|
99
|
+
item.label
|
|
100
|
+
] }, item.id)),
|
|
101
|
+
/* @__PURE__ */ jsx(Text, { children: " " }),
|
|
102
|
+
NAV.slice(14).map((item) => /* @__PURE__ */ jsxs(Text, { inverse: screen === item.id, children: [
|
|
87
103
|
screen === item.id ? symbols.bullet : " ",
|
|
88
104
|
item.label
|
|
89
105
|
] }, item.id)),
|
|
@@ -174,14 +190,16 @@ function Home({ cols = 80, rows = 24 }) {
|
|
|
174
190
|
|
|
175
191
|
// src/screens/Browse.tsx
|
|
176
192
|
import { useState as useState3 } from "react";
|
|
177
|
-
import { existsSync, mkdirSync, cpSync, rmSync } from "fs";
|
|
178
|
-
import { join as
|
|
193
|
+
import { existsSync as existsSync2, mkdirSync, cpSync, rmSync } from "fs";
|
|
194
|
+
import { join as join3 } from "path";
|
|
179
195
|
import { Box as Box3, Text as Text3, useInput } from "ink";
|
|
180
196
|
|
|
181
197
|
// src/hooks/useMarketplace.ts
|
|
182
198
|
import { useState as useState2, useCallback, useEffect as useEffect2 } from "react";
|
|
183
|
-
import { detectProvider } from "@skillkit/core";
|
|
184
|
-
|
|
199
|
+
import { detectProvider, loadConfig, extractFrontmatter } from "@skillkit/core";
|
|
200
|
+
import { readFileSync as readFileSync2, existsSync } from "fs";
|
|
201
|
+
import { join as join2 } from "path";
|
|
202
|
+
var DEFAULT_REPOS = [
|
|
185
203
|
{ source: "anthropics/skills", name: "Anthropic Official" },
|
|
186
204
|
{ source: "vercel-labs/agent-skills", name: "Vercel Labs" },
|
|
187
205
|
{ source: "expo/skills", name: "Expo / React Native" },
|
|
@@ -213,6 +231,41 @@ var POPULAR_REPOS = [
|
|
|
213
231
|
{ source: "openrouterteam/agent-skills", name: "OpenRouter SDK" },
|
|
214
232
|
{ source: "intellectronica/agent-skills", name: "Context7" }
|
|
215
233
|
];
|
|
234
|
+
function getMarketplaceRepos() {
|
|
235
|
+
try {
|
|
236
|
+
const config = loadConfig();
|
|
237
|
+
if (config.marketplaceSources?.length) {
|
|
238
|
+
const configRepos = config.marketplaceSources.map((source) => ({
|
|
239
|
+
source,
|
|
240
|
+
name: source.split("/").pop() || source
|
|
241
|
+
}));
|
|
242
|
+
const existingSources = new Set(configRepos.map((r) => r.source));
|
|
243
|
+
for (const repo of DEFAULT_REPOS) {
|
|
244
|
+
if (!existingSources.has(repo.source)) {
|
|
245
|
+
configRepos.push(repo);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
return configRepos;
|
|
249
|
+
}
|
|
250
|
+
} catch {
|
|
251
|
+
}
|
|
252
|
+
return DEFAULT_REPOS;
|
|
253
|
+
}
|
|
254
|
+
function readSkillDescription(skillPath) {
|
|
255
|
+
const skillMdPath = join2(skillPath, "SKILL.md");
|
|
256
|
+
if (!existsSync(skillMdPath)) {
|
|
257
|
+
return void 0;
|
|
258
|
+
}
|
|
259
|
+
try {
|
|
260
|
+
const content = readFileSync2(skillMdPath, "utf-8");
|
|
261
|
+
const frontmatter = extractFrontmatter(content);
|
|
262
|
+
if (frontmatter && typeof frontmatter.description === "string") {
|
|
263
|
+
return frontmatter.description;
|
|
264
|
+
}
|
|
265
|
+
} catch {
|
|
266
|
+
}
|
|
267
|
+
return void 0;
|
|
268
|
+
}
|
|
216
269
|
function useMarketplace() {
|
|
217
270
|
const [allSkills, setAllSkills] = useState2([]);
|
|
218
271
|
const [filteredSkills, setFilteredSkills] = useState2([]);
|
|
@@ -220,6 +273,8 @@ function useMarketplace() {
|
|
|
220
273
|
const [error, setError] = useState2(null);
|
|
221
274
|
const [currentRepo, setCurrentRepo] = useState2(null);
|
|
222
275
|
const [fetchedRepos, setFetchedRepos] = useState2(/* @__PURE__ */ new Set());
|
|
276
|
+
const [failedRepos, setFailedRepos] = useState2([]);
|
|
277
|
+
const [repos] = useState2(() => getMarketplaceRepos());
|
|
223
278
|
const fetchRepo = useCallback(async (source) => {
|
|
224
279
|
if (fetchedRepos.has(source)) return;
|
|
225
280
|
setLoading(true);
|
|
@@ -234,12 +289,13 @@ function useMarketplace() {
|
|
|
234
289
|
if (!result.success || !result.discoveredSkills) {
|
|
235
290
|
throw new Error(result.error || "Failed to fetch skills");
|
|
236
291
|
}
|
|
237
|
-
const repoName =
|
|
292
|
+
const repoName = repos.find((r) => r.source === source)?.name || source;
|
|
238
293
|
const newSkills = result.discoveredSkills.map((skill) => ({
|
|
239
294
|
name: skill.name,
|
|
240
295
|
source,
|
|
241
296
|
repoName,
|
|
242
|
-
description
|
|
297
|
+
// Try to read description from skill frontmatter
|
|
298
|
+
description: readSkillDescription(skill.path)
|
|
243
299
|
}));
|
|
244
300
|
setAllSkills((prev) => {
|
|
245
301
|
const updated = [...prev, ...newSkills];
|
|
@@ -251,27 +307,37 @@ function useMarketplace() {
|
|
|
251
307
|
rmSync3(result.tempRoot, { recursive: true, force: true });
|
|
252
308
|
}
|
|
253
309
|
} catch (err) {
|
|
254
|
-
|
|
310
|
+
const errorMsg = err instanceof Error ? err.message : "Failed to fetch repository";
|
|
311
|
+
setError(errorMsg);
|
|
312
|
+
setFailedRepos((prev) => prev.includes(source) ? prev : [...prev, source]);
|
|
255
313
|
} finally {
|
|
256
314
|
setLoading(false);
|
|
257
315
|
setCurrentRepo(null);
|
|
258
316
|
}
|
|
259
|
-
}, [fetchedRepos]);
|
|
317
|
+
}, [fetchedRepos, repos]);
|
|
260
318
|
const fetchAllRepos = useCallback(async () => {
|
|
261
319
|
setLoading(true);
|
|
262
320
|
setError(null);
|
|
263
|
-
|
|
321
|
+
const failures = [];
|
|
322
|
+
for (const repo of repos) {
|
|
264
323
|
if (!fetchedRepos.has(repo.source)) {
|
|
265
324
|
setCurrentRepo(repo.source);
|
|
266
325
|
try {
|
|
267
326
|
await fetchRepo(repo.source);
|
|
268
|
-
} catch {
|
|
327
|
+
} catch (err) {
|
|
328
|
+
failures.push(repo.source);
|
|
269
329
|
}
|
|
270
330
|
}
|
|
271
331
|
}
|
|
332
|
+
if (failures.length > 0) {
|
|
333
|
+
setFailedRepos((prev) => {
|
|
334
|
+
const combined = [...prev, ...failures];
|
|
335
|
+
return [...new Set(combined)];
|
|
336
|
+
});
|
|
337
|
+
}
|
|
272
338
|
setLoading(false);
|
|
273
339
|
setCurrentRepo(null);
|
|
274
|
-
}, [fetchRepo, fetchedRepos]);
|
|
340
|
+
}, [fetchRepo, fetchedRepos, repos]);
|
|
275
341
|
const search = useCallback((query) => {
|
|
276
342
|
if (!query.trim()) {
|
|
277
343
|
setFilteredSkills(allSkills);
|
|
@@ -288,6 +354,8 @@ function useMarketplace() {
|
|
|
288
354
|
setFetchedRepos(/* @__PURE__ */ new Set());
|
|
289
355
|
setAllSkills([]);
|
|
290
356
|
setFilteredSkills([]);
|
|
357
|
+
setFailedRepos([]);
|
|
358
|
+
setError(null);
|
|
291
359
|
}, []);
|
|
292
360
|
useEffect2(() => {
|
|
293
361
|
setFilteredSkills(allSkills);
|
|
@@ -302,8 +370,9 @@ function useMarketplace() {
|
|
|
302
370
|
loading,
|
|
303
371
|
error,
|
|
304
372
|
totalCount: allSkills.length,
|
|
305
|
-
repos
|
|
373
|
+
repos,
|
|
306
374
|
currentRepo,
|
|
375
|
+
failedRepos,
|
|
307
376
|
refresh,
|
|
308
377
|
search,
|
|
309
378
|
fetchRepo,
|
|
@@ -317,14 +386,14 @@ import { detectAgent, getAdapter as getAdapter2, getAllAdapters } from "@skillki
|
|
|
317
386
|
|
|
318
387
|
// src/helpers.ts
|
|
319
388
|
import {
|
|
320
|
-
loadConfig,
|
|
389
|
+
loadConfig as loadConfig2,
|
|
321
390
|
getSearchDirs as coreGetSearchDirs,
|
|
322
391
|
getInstallDir as coreGetInstallDir,
|
|
323
392
|
saveSkillMetadata as coreSaveSkillMetadata
|
|
324
393
|
} from "@skillkit/core";
|
|
325
394
|
import { getAdapter } from "@skillkit/agents";
|
|
326
395
|
function getSearchDirs(agentType) {
|
|
327
|
-
const type = agentType ||
|
|
396
|
+
const type = agentType || loadConfig2().agent;
|
|
328
397
|
const adapter = getAdapter(type);
|
|
329
398
|
const adapterInfo = {
|
|
330
399
|
type: adapter.type,
|
|
@@ -335,7 +404,7 @@ function getSearchDirs(agentType) {
|
|
|
335
404
|
return coreGetSearchDirs(adapterInfo);
|
|
336
405
|
}
|
|
337
406
|
function getInstallDir(global = false, agentType) {
|
|
338
|
-
const type = agentType ||
|
|
407
|
+
const type = agentType || loadConfig2().agent;
|
|
339
408
|
const adapter = getAdapter(type);
|
|
340
409
|
const adapterInfo = {
|
|
341
410
|
type: adapter.type,
|
|
@@ -400,11 +469,11 @@ function Browse({ rows = 24 }) {
|
|
|
400
469
|
const targetAgentType = agentType || await detectAgent();
|
|
401
470
|
const adapter = getAdapter2(targetAgentType);
|
|
402
471
|
const installDir = getInstallDir(false, targetAgentType);
|
|
403
|
-
if (!
|
|
472
|
+
if (!existsSync2(installDir)) {
|
|
404
473
|
mkdirSync(installDir, { recursive: true });
|
|
405
474
|
}
|
|
406
|
-
const targetPath =
|
|
407
|
-
if (
|
|
475
|
+
const targetPath = join3(installDir, skillName);
|
|
476
|
+
if (existsSync2(targetPath)) {
|
|
408
477
|
rmSync(targetPath, { recursive: true, force: true });
|
|
409
478
|
}
|
|
410
479
|
cpSync(skill.path, targetPath, { recursive: true, dereference: true });
|
|
@@ -710,44 +779,167 @@ function Sync({ rows = 24 }) {
|
|
|
710
779
|
}
|
|
711
780
|
|
|
712
781
|
// src/screens/Settings.tsx
|
|
713
|
-
import { useState as useState7 } from "react";
|
|
782
|
+
import { useState as useState7, useEffect as useEffect5 } from "react";
|
|
714
783
|
import { Box as Box6, Text as Text6, useInput as useInput4 } from "ink";
|
|
784
|
+
import TextInput from "ink-text-input";
|
|
785
|
+
import { loadConfig as loadConfig3, saveConfig, AgentType as AgentTypeSchema } from "@skillkit/core";
|
|
715
786
|
import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
787
|
+
var ALL_AGENTS = AgentTypeSchema.options;
|
|
716
788
|
var SETTINGS = [
|
|
717
|
-
{ id: "agent", label: "Default Agent",
|
|
718
|
-
{ id: "
|
|
719
|
-
{ id: "
|
|
789
|
+
{ id: "agent", label: "Default Agent", type: "select", options: ["auto-detect", ...ALL_AGENTS] },
|
|
790
|
+
{ id: "autoSync", label: "Auto Sync", type: "toggle" },
|
|
791
|
+
{ id: "cacheDir", label: "Cache Dir", type: "text" }
|
|
720
792
|
];
|
|
721
|
-
function Settings(
|
|
793
|
+
function Settings(_props) {
|
|
794
|
+
const [config, setConfig] = useState7(null);
|
|
722
795
|
const [sel, setSel] = useState7(0);
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
796
|
+
const [editing, setEditing] = useState7(false);
|
|
797
|
+
const [editValue, setEditValue] = useState7("");
|
|
798
|
+
const [saved, setSaved] = useState7(false);
|
|
799
|
+
const [error, setError] = useState7(null);
|
|
800
|
+
useEffect5(() => {
|
|
801
|
+
try {
|
|
802
|
+
const loaded = loadConfig3();
|
|
803
|
+
setConfig(loaded);
|
|
804
|
+
} catch (err) {
|
|
805
|
+
setError(`Failed to load config: ${err}`);
|
|
806
|
+
}
|
|
807
|
+
}, []);
|
|
808
|
+
const getCurrentValue = (setting) => {
|
|
809
|
+
if (!config) return "";
|
|
810
|
+
switch (setting.id) {
|
|
811
|
+
case "agent":
|
|
812
|
+
return config.agent === "universal" ? "auto-detect" : config.agent || "auto-detect";
|
|
813
|
+
case "autoSync":
|
|
814
|
+
return config.autoSync ? "enabled" : "disabled";
|
|
815
|
+
case "cacheDir":
|
|
816
|
+
return config.cacheDir || "~/.skillkit/cache";
|
|
817
|
+
default:
|
|
818
|
+
return "";
|
|
819
|
+
}
|
|
820
|
+
};
|
|
821
|
+
const handleSave = (setting, value) => {
|
|
822
|
+
if (!config) return;
|
|
823
|
+
const newConfig = { ...config };
|
|
824
|
+
switch (setting.id) {
|
|
825
|
+
case "agent":
|
|
826
|
+
if (value === "auto-detect") {
|
|
827
|
+
newConfig.agent = "universal";
|
|
828
|
+
} else {
|
|
829
|
+
newConfig.agent = value;
|
|
830
|
+
}
|
|
831
|
+
break;
|
|
832
|
+
case "autoSync":
|
|
833
|
+
newConfig.autoSync = value === "enabled";
|
|
834
|
+
break;
|
|
835
|
+
case "cacheDir":
|
|
836
|
+
newConfig.cacheDir = value || void 0;
|
|
837
|
+
break;
|
|
838
|
+
}
|
|
839
|
+
try {
|
|
840
|
+
saveConfig(newConfig, false);
|
|
841
|
+
setConfig(newConfig);
|
|
842
|
+
setSaved(true);
|
|
843
|
+
setTimeout(() => setSaved(false), 2e3);
|
|
844
|
+
} catch (err) {
|
|
845
|
+
setError(`Failed to save: ${err}`);
|
|
846
|
+
setTimeout(() => setError(null), 3e3);
|
|
847
|
+
}
|
|
848
|
+
};
|
|
849
|
+
useInput4((input, key) => {
|
|
850
|
+
if (editing) {
|
|
851
|
+
if (key.return) {
|
|
852
|
+
handleSave(SETTINGS[sel], editValue);
|
|
853
|
+
setEditing(false);
|
|
854
|
+
} else if (key.escape) {
|
|
855
|
+
setEditing(false);
|
|
856
|
+
}
|
|
857
|
+
return;
|
|
858
|
+
}
|
|
859
|
+
if (key.upArrow) {
|
|
860
|
+
setSel((i) => Math.max(0, i - 1));
|
|
861
|
+
} else if (key.downArrow) {
|
|
862
|
+
setSel((i) => Math.min(SETTINGS.length - 1, i + 1));
|
|
863
|
+
} else if (key.return || input === " ") {
|
|
864
|
+
const setting = SETTINGS[sel];
|
|
865
|
+
if (setting.type === "toggle") {
|
|
866
|
+
const current = getCurrentValue(setting);
|
|
867
|
+
const newValue = current === "enabled" ? "disabled" : "enabled";
|
|
868
|
+
handleSave(setting, newValue);
|
|
869
|
+
} else if (setting.type === "select" && setting.options) {
|
|
870
|
+
const current = getCurrentValue(setting);
|
|
871
|
+
const idx = setting.options.indexOf(current);
|
|
872
|
+
const nextIdx = (idx + 1) % setting.options.length;
|
|
873
|
+
handleSave(setting, setting.options[nextIdx]);
|
|
874
|
+
} else if (setting.type === "text") {
|
|
875
|
+
setEditValue(getCurrentValue(setting));
|
|
876
|
+
setEditing(true);
|
|
877
|
+
}
|
|
878
|
+
}
|
|
726
879
|
});
|
|
880
|
+
if (!config) {
|
|
881
|
+
return /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
|
|
882
|
+
/* @__PURE__ */ jsx6(Text6, { bold: true, color: colors.primary, children: "SETTINGS" }),
|
|
883
|
+
error ? /* @__PURE__ */ jsxs6(Text6, { color: colors.danger, children: [
|
|
884
|
+
symbols.error,
|
|
885
|
+
" ",
|
|
886
|
+
error
|
|
887
|
+
] }) : /* @__PURE__ */ jsx6(Text6, { dimColor: true, children: "Loading..." })
|
|
888
|
+
] });
|
|
889
|
+
}
|
|
727
890
|
return /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
|
|
728
891
|
/* @__PURE__ */ jsx6(Text6, { bold: true, color: colors.primary, children: "SETTINGS" }),
|
|
729
|
-
/* @__PURE__ */ jsx6(Text6, { dimColor: true, children: "Configure SkillKit" }),
|
|
892
|
+
/* @__PURE__ */ jsx6(Text6, { dimColor: true, children: "Configure SkillKit (changes save automatically)" }),
|
|
730
893
|
/* @__PURE__ */ jsx6(Box6, { marginTop: 1, flexDirection: "column", children: SETTINGS.map((s, i) => {
|
|
731
894
|
const isSel = i === sel;
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
895
|
+
const value = getCurrentValue(s);
|
|
896
|
+
const isEditing = editing && isSel;
|
|
897
|
+
return /* @__PURE__ */ jsxs6(Box6, { children: [
|
|
898
|
+
/* @__PURE__ */ jsxs6(Text6, { inverse: isSel && !isEditing, children: [
|
|
899
|
+
isSel ? symbols.pointer : " ",
|
|
900
|
+
s.label.padEnd(16)
|
|
901
|
+
] }),
|
|
902
|
+
isEditing ? /* @__PURE__ */ jsx6(Box6, { marginLeft: 1, children: /* @__PURE__ */ jsx6(
|
|
903
|
+
TextInput,
|
|
904
|
+
{
|
|
905
|
+
value: editValue,
|
|
906
|
+
onChange: setEditValue,
|
|
907
|
+
onSubmit: () => {
|
|
908
|
+
handleSave(s, editValue);
|
|
909
|
+
setEditing(false);
|
|
910
|
+
}
|
|
911
|
+
}
|
|
912
|
+
) }) : /* @__PURE__ */ jsxs6(Text6, { color: colors.secondaryDim, children: [
|
|
913
|
+
" ",
|
|
914
|
+
value
|
|
915
|
+
] }),
|
|
916
|
+
s.type === "toggle" && isSel && !isEditing && /* @__PURE__ */ jsx6(Text6, { dimColor: true, children: " (space to toggle)" }),
|
|
917
|
+
s.type === "select" && isSel && !isEditing && /* @__PURE__ */ jsx6(Text6, { dimColor: true, children: " (enter to cycle)" })
|
|
737
918
|
] }, s.id);
|
|
738
919
|
}) }),
|
|
739
|
-
/* @__PURE__ */
|
|
920
|
+
/* @__PURE__ */ jsxs6(Box6, { marginTop: 1, children: [
|
|
921
|
+
saved && /* @__PURE__ */ jsxs6(Text6, { color: colors.success, children: [
|
|
922
|
+
symbols.check,
|
|
923
|
+
" Settings saved"
|
|
924
|
+
] }),
|
|
925
|
+
error && /* @__PURE__ */ jsxs6(Text6, { color: colors.danger, children: [
|
|
926
|
+
symbols.error,
|
|
927
|
+
" ",
|
|
928
|
+
error
|
|
929
|
+
] }),
|
|
930
|
+
!saved && !error && /* @__PURE__ */ jsx6(Text6, { dimColor: true, children: editing ? "Enter=save Esc=cancel" : "Enter/Space=edit q=quit" })
|
|
931
|
+
] })
|
|
740
932
|
] });
|
|
741
933
|
}
|
|
742
934
|
|
|
743
935
|
// src/screens/Recommend.tsx
|
|
744
936
|
import { useState as useState9 } from "react";
|
|
745
|
-
import { existsSync as
|
|
746
|
-
import { join as
|
|
937
|
+
import { existsSync as existsSync3, mkdirSync as mkdirSync2, cpSync as cpSync2, rmSync as rmSync2 } from "fs";
|
|
938
|
+
import { join as join4 } from "path";
|
|
747
939
|
import { Box as Box7, Text as Text7, useInput as useInput5 } from "ink";
|
|
748
940
|
|
|
749
941
|
// src/hooks/useRecommend.ts
|
|
750
|
-
import { useState as useState8, useCallback as useCallback2, useEffect as
|
|
942
|
+
import { useState as useState8, useCallback as useCallback2, useEffect as useEffect6 } from "react";
|
|
751
943
|
import {
|
|
752
944
|
RecommendationEngine,
|
|
753
945
|
ContextManager,
|
|
@@ -875,7 +1067,7 @@ function useRecommend(projectPath = process.cwd()) {
|
|
|
875
1067
|
const refresh = useCallback2(() => {
|
|
876
1068
|
loadRecommendations();
|
|
877
1069
|
}, [loadRecommendations]);
|
|
878
|
-
|
|
1070
|
+
useEffect6(() => {
|
|
879
1071
|
loadRecommendations();
|
|
880
1072
|
}, [loadRecommendations]);
|
|
881
1073
|
return {
|
|
@@ -965,11 +1157,11 @@ function Recommend({ rows = 24 }) {
|
|
|
965
1157
|
const targetAgentType = agentType || await detectAgent2();
|
|
966
1158
|
const adapter = getAdapter3(targetAgentType);
|
|
967
1159
|
const installDir = getInstallDir(false, targetAgentType);
|
|
968
|
-
if (!
|
|
1160
|
+
if (!existsSync3(installDir)) {
|
|
969
1161
|
mkdirSync2(installDir, { recursive: true });
|
|
970
1162
|
}
|
|
971
|
-
const targetPath =
|
|
972
|
-
if (
|
|
1163
|
+
const targetPath = join4(installDir, skillName);
|
|
1164
|
+
if (existsSync3(targetPath)) {
|
|
973
1165
|
rmSync2(targetPath, { recursive: true, force: true });
|
|
974
1166
|
}
|
|
975
1167
|
cpSync2(skill.path, targetPath, { recursive: true, dereference: true });
|
|
@@ -1172,9 +1364,9 @@ function Recommend({ rows = 24 }) {
|
|
|
1172
1364
|
}
|
|
1173
1365
|
|
|
1174
1366
|
// src/screens/Translate.tsx
|
|
1175
|
-
import { useState as useState10, useEffect as
|
|
1176
|
-
import { existsSync as
|
|
1177
|
-
import { join as
|
|
1367
|
+
import { useState as useState10, useEffect as useEffect7 } from "react";
|
|
1368
|
+
import { existsSync as existsSync4, readdirSync, readFileSync as readFileSync3, writeFileSync, mkdirSync as mkdirSync3 } from "fs";
|
|
1369
|
+
import { join as join5 } from "path";
|
|
1178
1370
|
import { Box as Box8, Text as Text8, useInput as useInput6 } from "ink";
|
|
1179
1371
|
import {
|
|
1180
1372
|
translateSkill,
|
|
@@ -1192,17 +1384,17 @@ function Translate({ rows = 24 }) {
|
|
|
1192
1384
|
const [preview, setPreview] = useState10("");
|
|
1193
1385
|
const [result, setResult] = useState10(null);
|
|
1194
1386
|
const [loading, setLoading] = useState10(false);
|
|
1195
|
-
|
|
1387
|
+
useEffect7(() => {
|
|
1196
1388
|
const loadSkills = () => {
|
|
1197
1389
|
const installDir = getInstallDir(false);
|
|
1198
1390
|
const foundSkills = [];
|
|
1199
|
-
if (
|
|
1391
|
+
if (existsSync4(installDir)) {
|
|
1200
1392
|
const dirs = readdirSync(installDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
|
|
1201
1393
|
for (const dir of dirs) {
|
|
1202
|
-
const skillPath =
|
|
1203
|
-
const skillMdPath =
|
|
1204
|
-
if (
|
|
1205
|
-
const content =
|
|
1394
|
+
const skillPath = join5(installDir, dir);
|
|
1395
|
+
const skillMdPath = join5(skillPath, "SKILL.md");
|
|
1396
|
+
if (existsSync4(skillMdPath)) {
|
|
1397
|
+
const content = readFileSync3(skillMdPath, "utf-8");
|
|
1206
1398
|
foundSkills.push({
|
|
1207
1399
|
name: dir,
|
|
1208
1400
|
path: skillPath,
|
|
@@ -1215,7 +1407,7 @@ function Translate({ rows = 24 }) {
|
|
|
1215
1407
|
};
|
|
1216
1408
|
loadSkills();
|
|
1217
1409
|
}, []);
|
|
1218
|
-
|
|
1410
|
+
useEffect7(() => {
|
|
1219
1411
|
const adapters = getAllAdapters4();
|
|
1220
1412
|
const supportedAgents = getSupportedTranslationAgents();
|
|
1221
1413
|
const agentList = adapters.filter((a) => supportedAgents.includes(a.type)).map((a) => ({
|
|
@@ -1252,12 +1444,12 @@ function Translate({ rows = 24 }) {
|
|
|
1252
1444
|
return;
|
|
1253
1445
|
}
|
|
1254
1446
|
const adapter = getAdapter4(selectedAgent.type);
|
|
1255
|
-
const targetDir = adapter?.skillsDir ?
|
|
1256
|
-
if (!
|
|
1447
|
+
const targetDir = adapter?.skillsDir ? join5(process.cwd(), adapter.skillsDir) : join5(process.cwd(), `.${selectedAgent.type}/skills/`);
|
|
1448
|
+
if (!existsSync4(targetDir)) {
|
|
1257
1449
|
mkdirSync3(targetDir, { recursive: true });
|
|
1258
1450
|
}
|
|
1259
1451
|
const filename = translationResult.filename || `${selectedSkill.name}.md`;
|
|
1260
|
-
const targetPath =
|
|
1452
|
+
const targetPath = join5(targetDir, filename);
|
|
1261
1453
|
writeFileSync(targetPath, translationResult.content, "utf-8");
|
|
1262
1454
|
setResult({
|
|
1263
1455
|
success: true,
|
|
@@ -1403,7 +1595,7 @@ function Translate({ rows = 24 }) {
|
|
|
1403
1595
|
}
|
|
1404
1596
|
|
|
1405
1597
|
// src/screens/Context.tsx
|
|
1406
|
-
import { useState as useState11, useEffect as
|
|
1598
|
+
import { useState as useState11, useEffect as useEffect8 } from "react";
|
|
1407
1599
|
import { Box as Box9, Text as Text9, useInput as useInput7 } from "ink";
|
|
1408
1600
|
import {
|
|
1409
1601
|
loadContext,
|
|
@@ -1423,7 +1615,7 @@ function Context({ rows = 24 }) {
|
|
|
1423
1615
|
const [message, setMessage] = useState11(null);
|
|
1424
1616
|
const [error, setError] = useState11(null);
|
|
1425
1617
|
const projectPath = process.cwd();
|
|
1426
|
-
|
|
1618
|
+
useEffect8(() => {
|
|
1427
1619
|
const load = async () => {
|
|
1428
1620
|
setLoading(true);
|
|
1429
1621
|
try {
|
|
@@ -1652,7 +1844,7 @@ function Context({ rows = 24 }) {
|
|
|
1652
1844
|
}
|
|
1653
1845
|
|
|
1654
1846
|
// src/screens/Workflow.tsx
|
|
1655
|
-
import { useState as useState12, useEffect as
|
|
1847
|
+
import { useState as useState12, useEffect as useEffect9 } from "react";
|
|
1656
1848
|
import { Box as Box10, Text as Text10, useInput as useInput8 } from "ink";
|
|
1657
1849
|
import { listWorkflows } from "@skillkit/core";
|
|
1658
1850
|
import { jsx as jsx10, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
@@ -1665,7 +1857,7 @@ function Workflow({ rows = 24 }) {
|
|
|
1665
1857
|
const maxVisible = Math.max(5, rows - 8);
|
|
1666
1858
|
const start = Math.max(0, Math.min(sel - Math.floor(maxVisible / 2), workflows.length - maxVisible));
|
|
1667
1859
|
const visible = workflows.slice(start, start + maxVisible);
|
|
1668
|
-
|
|
1860
|
+
useEffect9(() => {
|
|
1669
1861
|
loadWorkflows();
|
|
1670
1862
|
}, []);
|
|
1671
1863
|
const loadWorkflows = () => {
|
|
@@ -1745,7 +1937,7 @@ function Workflow({ rows = 24 }) {
|
|
|
1745
1937
|
}
|
|
1746
1938
|
|
|
1747
1939
|
// src/screens/Execute.tsx
|
|
1748
|
-
import { useState as useState13, useEffect as
|
|
1940
|
+
import { useState as useState13, useEffect as useEffect10 } from "react";
|
|
1749
1941
|
import { Box as Box11, Text as Text11, useInput as useInput9 } from "ink";
|
|
1750
1942
|
import { createSessionManager } from "@skillkit/core";
|
|
1751
1943
|
import { jsx as jsx11, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
@@ -1753,7 +1945,7 @@ function Execute({ rows = 24 }) {
|
|
|
1753
1945
|
const [session, setSession] = useState13(null);
|
|
1754
1946
|
const [loading, setLoading] = useState13(true);
|
|
1755
1947
|
const maxVisible = Math.max(5, rows - 10);
|
|
1756
|
-
|
|
1948
|
+
useEffect10(() => {
|
|
1757
1949
|
loadSession();
|
|
1758
1950
|
const interval = setInterval(loadSession, 1e3);
|
|
1759
1951
|
return () => clearInterval(interval);
|
|
@@ -1858,7 +2050,7 @@ function Execute({ rows = 24 }) {
|
|
|
1858
2050
|
}
|
|
1859
2051
|
|
|
1860
2052
|
// src/screens/History.tsx
|
|
1861
|
-
import { useState as useState14, useEffect as
|
|
2053
|
+
import { useState as useState14, useEffect as useEffect11 } from "react";
|
|
1862
2054
|
import { Box as Box12, Text as Text12, useInput as useInput10 } from "ink";
|
|
1863
2055
|
import { createSessionManager as createSessionManager2 } from "@skillkit/core";
|
|
1864
2056
|
import { jsx as jsx12, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
@@ -1870,7 +2062,7 @@ function History({ rows = 24 }) {
|
|
|
1870
2062
|
const maxVisible = Math.max(5, rows - 8);
|
|
1871
2063
|
const start = Math.max(0, Math.min(sel - Math.floor(maxVisible / 2), history.length - maxVisible));
|
|
1872
2064
|
const visible = history.slice(start, start + maxVisible);
|
|
1873
|
-
|
|
2065
|
+
useEffect11(() => {
|
|
1874
2066
|
loadHistory();
|
|
1875
2067
|
}, []);
|
|
1876
2068
|
const loadHistory = () => {
|
|
@@ -1981,7 +2173,7 @@ function History({ rows = 24 }) {
|
|
|
1981
2173
|
}
|
|
1982
2174
|
|
|
1983
2175
|
// src/screens/Marketplace.tsx
|
|
1984
|
-
import { useState as useState15, useEffect as
|
|
2176
|
+
import { useState as useState15, useEffect as useEffect12 } from "react";
|
|
1985
2177
|
import { Box as Box13, Text as Text13, useInput as useInput11 } from "ink";
|
|
1986
2178
|
import { jsx as jsx13, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
1987
2179
|
var SKILL_SOURCES = [
|
|
@@ -2001,7 +2193,7 @@ function Marketplace({ rows = 24 }) {
|
|
|
2001
2193
|
const maxVisible = Math.max(5, rows - 10);
|
|
2002
2194
|
const start = Math.max(0, Math.min(sel - Math.floor(maxVisible / 2), filtered.length - maxVisible));
|
|
2003
2195
|
const visible = filtered.slice(start, start + maxVisible);
|
|
2004
|
-
|
|
2196
|
+
useEffect12(() => {
|
|
2005
2197
|
loadMarketplace();
|
|
2006
2198
|
}, []);
|
|
2007
2199
|
const loadMarketplace = async () => {
|
|
@@ -2140,7 +2332,7 @@ function Marketplace({ rows = 24 }) {
|
|
|
2140
2332
|
}
|
|
2141
2333
|
|
|
2142
2334
|
// src/screens/Memory.tsx
|
|
2143
|
-
import { useState as useState16, useEffect as
|
|
2335
|
+
import { useState as useState16, useEffect as useEffect13 } from "react";
|
|
2144
2336
|
import { Box as Box14, Text as Text14, useInput as useInput12 } from "ink";
|
|
2145
2337
|
import {
|
|
2146
2338
|
ObservationStore,
|
|
@@ -2165,7 +2357,7 @@ function Memory({ rows = 24 }) {
|
|
|
2165
2357
|
const currentList = tab === "search" ? searchResults : learnings;
|
|
2166
2358
|
const start = Math.max(0, Math.min(sel - Math.floor(maxVisible / 2), currentList.length - maxVisible));
|
|
2167
2359
|
const visible = currentList.slice(start, start + maxVisible);
|
|
2168
|
-
|
|
2360
|
+
useEffect13(() => {
|
|
2169
2361
|
loadMemory();
|
|
2170
2362
|
}, [isGlobal]);
|
|
2171
2363
|
const loadMemory = () => {
|
|
@@ -2400,10 +2592,806 @@ function Memory({ rows = 24 }) {
|
|
|
2400
2592
|
] });
|
|
2401
2593
|
}
|
|
2402
2594
|
|
|
2403
|
-
// src/
|
|
2595
|
+
// src/screens/Team.tsx
|
|
2596
|
+
import { useState as useState17, useEffect as useEffect14 } from "react";
|
|
2597
|
+
import { Box as Box15, Text as Text15, useInput as useInput13 } from "ink";
|
|
2404
2598
|
import { jsx as jsx15, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
2599
|
+
function Team({ rows = 24 }) {
|
|
2600
|
+
const [config, setConfig] = useState17(null);
|
|
2601
|
+
const [skills, setSkills] = useState17([]);
|
|
2602
|
+
const [loading, setLoading] = useState17(true);
|
|
2603
|
+
const [error, setError] = useState17(null);
|
|
2604
|
+
const [sel, setSel] = useState17(0);
|
|
2605
|
+
const [message, setMessage] = useState17(null);
|
|
2606
|
+
const maxVisible = Math.max(5, rows - 12);
|
|
2607
|
+
const start = Math.max(0, Math.min(sel - Math.floor(maxVisible / 2), skills.length - maxVisible));
|
|
2608
|
+
const visible = skills.slice(start, start + maxVisible);
|
|
2609
|
+
useEffect14(() => {
|
|
2610
|
+
loadTeam();
|
|
2611
|
+
}, []);
|
|
2612
|
+
const loadTeam = async () => {
|
|
2613
|
+
setLoading(true);
|
|
2614
|
+
setError(null);
|
|
2615
|
+
try {
|
|
2616
|
+
const { createTeamManager } = await import("@skillkit/core");
|
|
2617
|
+
const manager = createTeamManager(process.cwd());
|
|
2618
|
+
const teamConfig = manager.load();
|
|
2619
|
+
if (!teamConfig) {
|
|
2620
|
+
setConfig(null);
|
|
2621
|
+
setSkills([]);
|
|
2622
|
+
setError("Team not initialized. Run `skillkit team init` first.");
|
|
2623
|
+
} else {
|
|
2624
|
+
setConfig(teamConfig);
|
|
2625
|
+
const sharedSkills = manager.listSharedSkills();
|
|
2626
|
+
setSkills(sharedSkills);
|
|
2627
|
+
setSel((s) => Math.min(s, Math.max(0, sharedSkills.length - 1)));
|
|
2628
|
+
}
|
|
2629
|
+
} catch (e) {
|
|
2630
|
+
setError(e instanceof Error ? e.message : "Failed to load team");
|
|
2631
|
+
}
|
|
2632
|
+
setLoading(false);
|
|
2633
|
+
};
|
|
2634
|
+
const syncTeam = async () => {
|
|
2635
|
+
if (!config) return;
|
|
2636
|
+
setLoading(true);
|
|
2637
|
+
setMessage(null);
|
|
2638
|
+
try {
|
|
2639
|
+
const { createTeamManager } = await import("@skillkit/core");
|
|
2640
|
+
const manager = createTeamManager(process.cwd());
|
|
2641
|
+
manager.load();
|
|
2642
|
+
const result = await manager.sync();
|
|
2643
|
+
const changes = [...result.added, ...result.updated];
|
|
2644
|
+
if (changes.length > 0) {
|
|
2645
|
+
setMessage(`Synced: ${changes.join(", ")}`);
|
|
2646
|
+
} else {
|
|
2647
|
+
setMessage("Already up to date");
|
|
2648
|
+
}
|
|
2649
|
+
const sharedSkills = manager.listSharedSkills();
|
|
2650
|
+
setSkills(sharedSkills);
|
|
2651
|
+
setSel((s) => Math.min(s, Math.max(0, sharedSkills.length - 1)));
|
|
2652
|
+
} catch (e) {
|
|
2653
|
+
setError(e instanceof Error ? e.message : "Sync failed");
|
|
2654
|
+
}
|
|
2655
|
+
setLoading(false);
|
|
2656
|
+
};
|
|
2657
|
+
const importSkill = async (skillName) => {
|
|
2658
|
+
if (!config) return;
|
|
2659
|
+
setLoading(true);
|
|
2660
|
+
setMessage(null);
|
|
2661
|
+
try {
|
|
2662
|
+
const { createTeamManager } = await import("@skillkit/core");
|
|
2663
|
+
const manager = createTeamManager(process.cwd());
|
|
2664
|
+
manager.load();
|
|
2665
|
+
const result = await manager.importSkill(skillName, { overwrite: false });
|
|
2666
|
+
if (result.success) {
|
|
2667
|
+
setMessage(`Imported: ${skillName}`);
|
|
2668
|
+
} else {
|
|
2669
|
+
setError(result.error || "Import failed");
|
|
2670
|
+
}
|
|
2671
|
+
} catch (e) {
|
|
2672
|
+
setError(e instanceof Error ? e.message : "Import failed");
|
|
2673
|
+
}
|
|
2674
|
+
setLoading(false);
|
|
2675
|
+
};
|
|
2676
|
+
useInput13((input, key) => {
|
|
2677
|
+
if (loading) return;
|
|
2678
|
+
if (skills.length === 0 && (key.upArrow || key.downArrow)) return;
|
|
2679
|
+
if (key.upArrow) setSel((i) => Math.max(0, i - 1));
|
|
2680
|
+
else if (key.downArrow) setSel((i) => Math.min(Math.max(0, skills.length - 1), i + 1));
|
|
2681
|
+
else if (input === "r") loadTeam();
|
|
2682
|
+
else if (input === "s") syncTeam();
|
|
2683
|
+
else if (key.return && skills[sel]) {
|
|
2684
|
+
importSkill(skills[sel].name);
|
|
2685
|
+
}
|
|
2686
|
+
});
|
|
2687
|
+
if (loading) {
|
|
2688
|
+
return /* @__PURE__ */ jsxs15(Box15, { flexDirection: "column", padding: 1, children: [
|
|
2689
|
+
/* @__PURE__ */ jsx15(Text15, { color: colors.primary, bold: true, children: "Team Collaboration" }),
|
|
2690
|
+
/* @__PURE__ */ jsx15(Box15, { marginTop: 1, children: /* @__PURE__ */ jsxs15(Text15, { color: colors.secondaryDim, children: [
|
|
2691
|
+
symbols.spinner[0],
|
|
2692
|
+
" Loading..."
|
|
2693
|
+
] }) })
|
|
2694
|
+
] });
|
|
2695
|
+
}
|
|
2696
|
+
if (!config) {
|
|
2697
|
+
return /* @__PURE__ */ jsxs15(Box15, { flexDirection: "column", padding: 1, children: [
|
|
2698
|
+
/* @__PURE__ */ jsx15(Text15, { color: colors.primary, bold: true, children: "Team Collaboration" }),
|
|
2699
|
+
/* @__PURE__ */ jsxs15(Box15, { marginTop: 1, flexDirection: "column", children: [
|
|
2700
|
+
/* @__PURE__ */ jsxs15(Text15, { color: colors.secondaryDim, children: [
|
|
2701
|
+
symbols.warning,
|
|
2702
|
+
" Team not initialized"
|
|
2703
|
+
] }),
|
|
2704
|
+
/* @__PURE__ */ jsx15(Text15, { color: colors.secondaryDim, dimColor: true, children: 'Run: skillkit team init --name "Team Name" --registry <url>' })
|
|
2705
|
+
] }),
|
|
2706
|
+
/* @__PURE__ */ jsx15(Box15, { marginTop: 1, children: /* @__PURE__ */ jsx15(Text15, { dimColor: true, children: "[r] Refresh" }) })
|
|
2707
|
+
] });
|
|
2708
|
+
}
|
|
2709
|
+
return /* @__PURE__ */ jsxs15(Box15, { flexDirection: "column", padding: 1, children: [
|
|
2710
|
+
/* @__PURE__ */ jsxs15(Text15, { color: colors.primary, bold: true, children: [
|
|
2711
|
+
"Team: ",
|
|
2712
|
+
config.teamName
|
|
2713
|
+
] }),
|
|
2714
|
+
/* @__PURE__ */ jsxs15(Text15, { color: colors.secondaryDim, dimColor: true, children: [
|
|
2715
|
+
"Registry: ",
|
|
2716
|
+
config.registryUrl
|
|
2717
|
+
] }),
|
|
2718
|
+
error && /* @__PURE__ */ jsx15(Box15, { marginTop: 1, children: /* @__PURE__ */ jsxs15(Text15, { color: "red", children: [
|
|
2719
|
+
symbols.error,
|
|
2720
|
+
" ",
|
|
2721
|
+
error
|
|
2722
|
+
] }) }),
|
|
2723
|
+
message && /* @__PURE__ */ jsx15(Box15, { marginTop: 1, children: /* @__PURE__ */ jsxs15(Text15, { color: "green", children: [
|
|
2724
|
+
symbols.success,
|
|
2725
|
+
" ",
|
|
2726
|
+
message
|
|
2727
|
+
] }) }),
|
|
2728
|
+
/* @__PURE__ */ jsxs15(Box15, { marginTop: 1, flexDirection: "column", children: [
|
|
2729
|
+
/* @__PURE__ */ jsxs15(Text15, { color: colors.secondaryDim, bold: true, children: [
|
|
2730
|
+
"Shared Skills (",
|
|
2731
|
+
skills.length,
|
|
2732
|
+
")"
|
|
2733
|
+
] }),
|
|
2734
|
+
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) => {
|
|
2735
|
+
const actualIndex = start + i;
|
|
2736
|
+
const isSelected = actualIndex === sel;
|
|
2737
|
+
return /* @__PURE__ */ jsxs15(Box15, { flexDirection: "column", children: [
|
|
2738
|
+
/* @__PURE__ */ jsxs15(Text15, { children: [
|
|
2739
|
+
isSelected ? symbols.pointer : " ",
|
|
2740
|
+
" ",
|
|
2741
|
+
/* @__PURE__ */ jsx15(Text15, { color: isSelected ? colors.primary : colors.secondaryDim, bold: isSelected, children: skill.name }),
|
|
2742
|
+
/* @__PURE__ */ jsxs15(Text15, { dimColor: true, children: [
|
|
2743
|
+
" v",
|
|
2744
|
+
skill.version
|
|
2745
|
+
] }),
|
|
2746
|
+
/* @__PURE__ */ jsxs15(Text15, { dimColor: true, children: [
|
|
2747
|
+
" by ",
|
|
2748
|
+
skill.author
|
|
2749
|
+
] }),
|
|
2750
|
+
skill.downloads !== void 0 && /* @__PURE__ */ jsxs15(Text15, { dimColor: true, children: [
|
|
2751
|
+
" | ",
|
|
2752
|
+
skill.downloads,
|
|
2753
|
+
" downloads"
|
|
2754
|
+
] })
|
|
2755
|
+
] }),
|
|
2756
|
+
skill.description && isSelected && /* @__PURE__ */ jsxs15(Text15, { color: colors.secondaryDim, dimColor: true, children: [
|
|
2757
|
+
" ",
|
|
2758
|
+
skill.description
|
|
2759
|
+
] })
|
|
2760
|
+
] }, skill.name);
|
|
2761
|
+
}) })
|
|
2762
|
+
] }),
|
|
2763
|
+
/* @__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" }) })
|
|
2764
|
+
] });
|
|
2765
|
+
}
|
|
2766
|
+
|
|
2767
|
+
// src/screens/Plugins.tsx
|
|
2768
|
+
import { useState as useState18, useEffect as useEffect15 } from "react";
|
|
2769
|
+
import { Box as Box16, Text as Text16, useInput as useInput14 } from "ink";
|
|
2770
|
+
import { jsx as jsx16, jsxs as jsxs16 } from "react/jsx-runtime";
|
|
2771
|
+
function Plugins({ rows = 24 }) {
|
|
2772
|
+
const [plugins, setPlugins] = useState18([]);
|
|
2773
|
+
const [loading, setLoading] = useState18(true);
|
|
2774
|
+
const [error, setError] = useState18(null);
|
|
2775
|
+
const [sel, setSel] = useState18(0);
|
|
2776
|
+
const [message, setMessage] = useState18(null);
|
|
2777
|
+
const maxVisible = Math.max(5, rows - 12);
|
|
2778
|
+
const start = Math.max(0, Math.min(sel - Math.floor(maxVisible / 2), plugins.length - maxVisible));
|
|
2779
|
+
const visible = plugins.slice(start, start + maxVisible);
|
|
2780
|
+
useEffect15(() => {
|
|
2781
|
+
loadPlugins();
|
|
2782
|
+
}, []);
|
|
2783
|
+
const loadPlugins = async () => {
|
|
2784
|
+
setLoading(true);
|
|
2785
|
+
setError(null);
|
|
2786
|
+
try {
|
|
2787
|
+
const { createPluginManager, loadPluginsFromDirectory } = await import("@skillkit/core");
|
|
2788
|
+
const { join: join7 } = await import("path");
|
|
2789
|
+
const manager = createPluginManager(process.cwd());
|
|
2790
|
+
const pluginsDir = join7(process.cwd(), ".skillkit", "plugins");
|
|
2791
|
+
try {
|
|
2792
|
+
const loadedPlugins = await loadPluginsFromDirectory(pluginsDir);
|
|
2793
|
+
for (const plugin of loadedPlugins) {
|
|
2794
|
+
if (!manager.getPlugin(plugin.metadata.name)) {
|
|
2795
|
+
await manager.register(plugin);
|
|
2796
|
+
}
|
|
2797
|
+
}
|
|
2798
|
+
} catch (err) {
|
|
2799
|
+
if (err && typeof err === "object" && "code" in err && err.code !== "ENOENT") {
|
|
2800
|
+
throw err;
|
|
2801
|
+
}
|
|
2802
|
+
}
|
|
2803
|
+
const allPlugins = manager.listPlugins();
|
|
2804
|
+
const pluginInfos = allPlugins.map((p) => {
|
|
2805
|
+
const plugin = manager.getPlugin(p.name);
|
|
2806
|
+
return {
|
|
2807
|
+
name: p.name,
|
|
2808
|
+
version: p.version,
|
|
2809
|
+
description: p.description,
|
|
2810
|
+
author: p.author,
|
|
2811
|
+
enabled: manager.isPluginEnabled(p.name),
|
|
2812
|
+
translators: plugin?.translators?.map((t) => t.agentType),
|
|
2813
|
+
providers: plugin?.providers?.map((pr) => pr.providerName),
|
|
2814
|
+
commands: plugin?.commands?.map((c) => c.name)
|
|
2815
|
+
};
|
|
2816
|
+
});
|
|
2817
|
+
setPlugins(pluginInfos);
|
|
2818
|
+
} catch (e) {
|
|
2819
|
+
setError(e instanceof Error ? e.message : "Failed to load plugins");
|
|
2820
|
+
}
|
|
2821
|
+
setLoading(false);
|
|
2822
|
+
};
|
|
2823
|
+
const togglePlugin = async (pluginName, currentEnabled) => {
|
|
2824
|
+
setLoading(true);
|
|
2825
|
+
setMessage(null);
|
|
2826
|
+
try {
|
|
2827
|
+
const { createPluginManager, loadPluginsFromDirectory } = await import("@skillkit/core");
|
|
2828
|
+
const { join: join7 } = await import("path");
|
|
2829
|
+
const manager = createPluginManager(process.cwd());
|
|
2830
|
+
const pluginsDir = join7(process.cwd(), ".skillkit", "plugins");
|
|
2831
|
+
try {
|
|
2832
|
+
const loadedPlugins = await loadPluginsFromDirectory(pluginsDir);
|
|
2833
|
+
for (const plugin of loadedPlugins) {
|
|
2834
|
+
if (!manager.getPlugin(plugin.metadata.name)) {
|
|
2835
|
+
await manager.register(plugin);
|
|
2836
|
+
}
|
|
2837
|
+
}
|
|
2838
|
+
} catch (err) {
|
|
2839
|
+
if (err && typeof err === "object" && "code" in err && err.code !== "ENOENT") {
|
|
2840
|
+
throw err;
|
|
2841
|
+
}
|
|
2842
|
+
}
|
|
2843
|
+
if (currentEnabled) {
|
|
2844
|
+
manager.disablePlugin(pluginName);
|
|
2845
|
+
setMessage(`Disabled: ${pluginName}`);
|
|
2846
|
+
} else {
|
|
2847
|
+
manager.enablePlugin(pluginName);
|
|
2848
|
+
setMessage(`Enabled: ${pluginName}`);
|
|
2849
|
+
}
|
|
2850
|
+
await loadPlugins();
|
|
2851
|
+
} catch (e) {
|
|
2852
|
+
setError(e instanceof Error ? e.message : "Toggle failed");
|
|
2853
|
+
setLoading(false);
|
|
2854
|
+
}
|
|
2855
|
+
};
|
|
2856
|
+
useInput14((input, key) => {
|
|
2857
|
+
if (loading) return;
|
|
2858
|
+
if (plugins.length === 0 && (key.upArrow || key.downArrow)) return;
|
|
2859
|
+
if (key.upArrow) setSel((i) => Math.max(0, i - 1));
|
|
2860
|
+
else if (key.downArrow) setSel((i) => Math.min(Math.max(0, plugins.length - 1), i + 1));
|
|
2861
|
+
else if (input === "r") loadPlugins();
|
|
2862
|
+
else if (input === "e" && plugins[sel]) {
|
|
2863
|
+
togglePlugin(plugins[sel].name, plugins[sel].enabled);
|
|
2864
|
+
} else if (key.return && plugins[sel]) {
|
|
2865
|
+
togglePlugin(plugins[sel].name, plugins[sel].enabled);
|
|
2866
|
+
}
|
|
2867
|
+
});
|
|
2868
|
+
if (loading) {
|
|
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(Box16, { marginTop: 1, children: /* @__PURE__ */ jsxs16(Text16, { color: colors.secondaryDim, children: [
|
|
2872
|
+
symbols.spinner[0],
|
|
2873
|
+
" Loading..."
|
|
2874
|
+
] }) })
|
|
2875
|
+
] });
|
|
2876
|
+
}
|
|
2877
|
+
return /* @__PURE__ */ jsxs16(Box16, { flexDirection: "column", padding: 1, children: [
|
|
2878
|
+
/* @__PURE__ */ jsx16(Text16, { color: colors.primary, bold: true, children: "Plugin Manager" }),
|
|
2879
|
+
/* @__PURE__ */ jsx16(Text16, { color: colors.secondaryDim, dimColor: true, children: "Manage SkillKit extensions" }),
|
|
2880
|
+
error && /* @__PURE__ */ jsx16(Box16, { marginTop: 1, children: /* @__PURE__ */ jsxs16(Text16, { color: "red", children: [
|
|
2881
|
+
symbols.error,
|
|
2882
|
+
" ",
|
|
2883
|
+
error
|
|
2884
|
+
] }) }),
|
|
2885
|
+
message && /* @__PURE__ */ jsx16(Box16, { marginTop: 1, children: /* @__PURE__ */ jsxs16(Text16, { color: "green", children: [
|
|
2886
|
+
symbols.success,
|
|
2887
|
+
" ",
|
|
2888
|
+
message
|
|
2889
|
+
] }) }),
|
|
2890
|
+
/* @__PURE__ */ jsxs16(Box16, { marginTop: 1, flexDirection: "column", children: [
|
|
2891
|
+
/* @__PURE__ */ jsxs16(Text16, { color: colors.secondaryDim, bold: true, children: [
|
|
2892
|
+
"Installed Plugins (",
|
|
2893
|
+
plugins.length,
|
|
2894
|
+
")"
|
|
2895
|
+
] }),
|
|
2896
|
+
plugins.length === 0 ? /* @__PURE__ */ jsxs16(Box16, { marginTop: 1, flexDirection: "column", children: [
|
|
2897
|
+
/* @__PURE__ */ jsx16(Text16, { color: colors.secondaryDim, dimColor: true, children: "No plugins installed." }),
|
|
2898
|
+
/* @__PURE__ */ jsx16(Text16, { color: colors.secondaryDim, dimColor: true, children: "Use: skillkit plugin install --source <path-or-package>" })
|
|
2899
|
+
] }) : /* @__PURE__ */ jsx16(Box16, { flexDirection: "column", marginTop: 1, children: visible.map((plugin, i) => {
|
|
2900
|
+
const actualIndex = start + i;
|
|
2901
|
+
const isSelected = actualIndex === sel;
|
|
2902
|
+
const statusColor = plugin.enabled ? "green" : "gray";
|
|
2903
|
+
const statusText = plugin.enabled ? "enabled" : "disabled";
|
|
2904
|
+
return /* @__PURE__ */ jsxs16(Box16, { flexDirection: "column", children: [
|
|
2905
|
+
/* @__PURE__ */ jsxs16(Text16, { children: [
|
|
2906
|
+
isSelected ? symbols.pointer : " ",
|
|
2907
|
+
" ",
|
|
2908
|
+
/* @__PURE__ */ jsx16(Text16, { color: isSelected ? colors.primary : colors.secondaryDim, bold: isSelected, children: plugin.name }),
|
|
2909
|
+
/* @__PURE__ */ jsxs16(Text16, { dimColor: true, children: [
|
|
2910
|
+
" v",
|
|
2911
|
+
plugin.version
|
|
2912
|
+
] }),
|
|
2913
|
+
/* @__PURE__ */ jsxs16(Text16, { color: statusColor, children: [
|
|
2914
|
+
" [",
|
|
2915
|
+
statusText,
|
|
2916
|
+
"]"
|
|
2917
|
+
] })
|
|
2918
|
+
] }),
|
|
2919
|
+
isSelected && /* @__PURE__ */ jsxs16(Box16, { flexDirection: "column", marginLeft: 3, children: [
|
|
2920
|
+
plugin.description && /* @__PURE__ */ jsx16(Text16, { color: colors.secondaryDim, dimColor: true, children: plugin.description }),
|
|
2921
|
+
plugin.author && /* @__PURE__ */ jsxs16(Text16, { dimColor: true, children: [
|
|
2922
|
+
"Author: ",
|
|
2923
|
+
plugin.author
|
|
2924
|
+
] }),
|
|
2925
|
+
plugin.translators && plugin.translators.length > 0 && /* @__PURE__ */ jsxs16(Text16, { dimColor: true, children: [
|
|
2926
|
+
"Translators: ",
|
|
2927
|
+
plugin.translators.join(", ")
|
|
2928
|
+
] }),
|
|
2929
|
+
plugin.providers && plugin.providers.length > 0 && /* @__PURE__ */ jsxs16(Text16, { dimColor: true, children: [
|
|
2930
|
+
"Providers: ",
|
|
2931
|
+
plugin.providers.join(", ")
|
|
2932
|
+
] }),
|
|
2933
|
+
plugin.commands && plugin.commands.length > 0 && /* @__PURE__ */ jsxs16(Text16, { dimColor: true, children: [
|
|
2934
|
+
"Commands: ",
|
|
2935
|
+
plugin.commands.join(", ")
|
|
2936
|
+
] })
|
|
2937
|
+
] })
|
|
2938
|
+
] }, plugin.name);
|
|
2939
|
+
}) })
|
|
2940
|
+
] }),
|
|
2941
|
+
/* @__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" }) })
|
|
2942
|
+
] });
|
|
2943
|
+
}
|
|
2944
|
+
|
|
2945
|
+
// src/screens/Methodology.tsx
|
|
2946
|
+
import { useState as useState19, useEffect as useEffect16, useMemo } from "react";
|
|
2947
|
+
import { Box as Box17, Text as Text17, useInput as useInput15 } from "ink";
|
|
2948
|
+
import {
|
|
2949
|
+
MethodologyManager
|
|
2950
|
+
} from "@skillkit/core";
|
|
2951
|
+
import { Fragment as Fragment2, jsx as jsx17, jsxs as jsxs17 } from "react/jsx-runtime";
|
|
2952
|
+
function Methodology({ rows = 24 }) {
|
|
2953
|
+
const [packs, setPacks] = useState19([]);
|
|
2954
|
+
const [selectedPack, setSelectedPack] = useState19(null);
|
|
2955
|
+
const [skills, setSkills] = useState19([]);
|
|
2956
|
+
const [loading, setLoading] = useState19(true);
|
|
2957
|
+
const [view, setView] = useState19("packs");
|
|
2958
|
+
const [sel, setSel] = useState19(0);
|
|
2959
|
+
const [error, setError] = useState19(null);
|
|
2960
|
+
const [message, setMessage] = useState19(null);
|
|
2961
|
+
const manager = useMemo(() => new MethodologyManager({
|
|
2962
|
+
projectPath: process.cwd()
|
|
2963
|
+
}), []);
|
|
2964
|
+
const maxVisible = Math.max(5, rows - 10);
|
|
2965
|
+
useEffect16(() => {
|
|
2966
|
+
loadPacks();
|
|
2967
|
+
}, []);
|
|
2968
|
+
const loadPacks = async () => {
|
|
2969
|
+
setLoading(true);
|
|
2970
|
+
setError(null);
|
|
2971
|
+
try {
|
|
2972
|
+
const loadedPacks = await manager.listAvailablePacks();
|
|
2973
|
+
setPacks(loadedPacks);
|
|
2974
|
+
} catch (e) {
|
|
2975
|
+
setError(e instanceof Error ? e.message : "Failed to load methodology packs");
|
|
2976
|
+
}
|
|
2977
|
+
setLoading(false);
|
|
2978
|
+
};
|
|
2979
|
+
const loadSkills = async (pack) => {
|
|
2980
|
+
setLoading(true);
|
|
2981
|
+
setError(null);
|
|
2982
|
+
try {
|
|
2983
|
+
const loader = manager.getLoader();
|
|
2984
|
+
const loadedSkills = await loader.loadPackSkills(pack.name);
|
|
2985
|
+
setSkills(loadedSkills);
|
|
2986
|
+
setSelectedPack(pack);
|
|
2987
|
+
setView("skills");
|
|
2988
|
+
setSel(0);
|
|
2989
|
+
} catch (e) {
|
|
2990
|
+
setError(e instanceof Error ? e.message : "Failed to load skills");
|
|
2991
|
+
}
|
|
2992
|
+
setLoading(false);
|
|
2993
|
+
};
|
|
2994
|
+
const installPack = async (pack) => {
|
|
2995
|
+
setLoading(true);
|
|
2996
|
+
setMessage(null);
|
|
2997
|
+
try {
|
|
2998
|
+
await manager.installPack(pack.name);
|
|
2999
|
+
setMessage(`Installed ${pack.name} methodology pack`);
|
|
3000
|
+
await loadPacks();
|
|
3001
|
+
} catch (e) {
|
|
3002
|
+
setError(e instanceof Error ? e.message : "Failed to install pack");
|
|
3003
|
+
}
|
|
3004
|
+
setLoading(false);
|
|
3005
|
+
};
|
|
3006
|
+
const items = view === "packs" ? packs : skills;
|
|
3007
|
+
const start = Math.max(0, Math.min(sel - Math.floor(maxVisible / 2), items.length - maxVisible));
|
|
3008
|
+
const visible = items.slice(start, start + maxVisible);
|
|
3009
|
+
useInput15((input, key) => {
|
|
3010
|
+
if (loading) return;
|
|
3011
|
+
if (key.upArrow) setSel((i) => Math.max(0, i - 1));
|
|
3012
|
+
else if (key.downArrow) setSel((i) => Math.min(items.length - 1, i + 1));
|
|
3013
|
+
else if (input === "r") {
|
|
3014
|
+
if (view === "packs") loadPacks();
|
|
3015
|
+
else if (selectedPack) loadSkills(selectedPack);
|
|
3016
|
+
} else if (key.escape || input === "b") {
|
|
3017
|
+
if (view === "skills") {
|
|
3018
|
+
setView("packs");
|
|
3019
|
+
setSel(0);
|
|
3020
|
+
setSelectedPack(null);
|
|
3021
|
+
}
|
|
3022
|
+
} else if (key.return) {
|
|
3023
|
+
if (view === "packs" && packs[sel]) {
|
|
3024
|
+
loadSkills(packs[sel]);
|
|
3025
|
+
}
|
|
3026
|
+
} else if (input === "i" && view === "packs" && packs[sel]) {
|
|
3027
|
+
installPack(packs[sel]);
|
|
3028
|
+
}
|
|
3029
|
+
});
|
|
3030
|
+
const renderPackItem = (pack, idx) => {
|
|
3031
|
+
const isSel = idx === sel;
|
|
3032
|
+
return /* @__PURE__ */ jsxs17(Box17, { flexDirection: "column", children: [
|
|
3033
|
+
/* @__PURE__ */ jsxs17(Text17, { inverse: isSel, children: [
|
|
3034
|
+
isSel ? symbols.pointer : " ",
|
|
3035
|
+
" ",
|
|
3036
|
+
pack.name,
|
|
3037
|
+
" ",
|
|
3038
|
+
/* @__PURE__ */ jsxs17(Text17, { dimColor: true, children: [
|
|
3039
|
+
"v",
|
|
3040
|
+
pack.version
|
|
3041
|
+
] })
|
|
3042
|
+
] }),
|
|
3043
|
+
isSel && /* @__PURE__ */ jsxs17(Fragment2, { children: [
|
|
3044
|
+
/* @__PURE__ */ jsxs17(Text17, { dimColor: true, children: [
|
|
3045
|
+
" ",
|
|
3046
|
+
pack.description
|
|
3047
|
+
] }),
|
|
3048
|
+
/* @__PURE__ */ jsxs17(Text17, { dimColor: true, children: [
|
|
3049
|
+
" ",
|
|
3050
|
+
pack.skills.length,
|
|
3051
|
+
" skill(s) | Tags: ",
|
|
3052
|
+
pack.tags?.join(", ") || "none"
|
|
3053
|
+
] })
|
|
3054
|
+
] })
|
|
3055
|
+
] }, pack.name);
|
|
3056
|
+
};
|
|
3057
|
+
const renderSkillItem = (skill, idx) => {
|
|
3058
|
+
const isSel = idx === sel;
|
|
3059
|
+
return /* @__PURE__ */ jsxs17(Box17, { flexDirection: "column", children: [
|
|
3060
|
+
/* @__PURE__ */ jsxs17(Text17, { inverse: isSel, children: [
|
|
3061
|
+
isSel ? symbols.pointer : " ",
|
|
3062
|
+
" ",
|
|
3063
|
+
skill.name
|
|
3064
|
+
] }),
|
|
3065
|
+
isSel && skill.description && /* @__PURE__ */ jsxs17(Text17, { dimColor: true, children: [
|
|
3066
|
+
" ",
|
|
3067
|
+
skill.description
|
|
3068
|
+
] }),
|
|
3069
|
+
isSel && skill.tags && /* @__PURE__ */ jsxs17(Text17, { dimColor: true, children: [
|
|
3070
|
+
" Tags: ",
|
|
3071
|
+
skill.tags.join(", ")
|
|
3072
|
+
] })
|
|
3073
|
+
] }, skill.name);
|
|
3074
|
+
};
|
|
3075
|
+
return /* @__PURE__ */ jsxs17(Box17, { flexDirection: "column", children: [
|
|
3076
|
+
/* @__PURE__ */ jsx17(Text17, { bold: true, color: colors.primary, children: view === "packs" ? "METHODOLOGY PACKS" : `${selectedPack?.name.toUpperCase()} SKILLS` }),
|
|
3077
|
+
/* @__PURE__ */ jsx17(Text17, { dimColor: true, children: view === "packs" ? `${packs.length} pack(s) available` : `${skills.length} skill(s) in pack` }),
|
|
3078
|
+
loading && /* @__PURE__ */ jsx17(Text17, { color: "yellow", children: "Loading..." }),
|
|
3079
|
+
error && /* @__PURE__ */ jsxs17(Text17, { color: "red", children: [
|
|
3080
|
+
symbols.error,
|
|
3081
|
+
" ",
|
|
3082
|
+
error
|
|
3083
|
+
] }),
|
|
3084
|
+
message && /* @__PURE__ */ jsxs17(Text17, { color: "green", children: [
|
|
3085
|
+
symbols.success,
|
|
3086
|
+
" ",
|
|
3087
|
+
message
|
|
3088
|
+
] }),
|
|
3089
|
+
!loading && items.length === 0 && /* @__PURE__ */ jsx17(Box17, { marginTop: 1, children: /* @__PURE__ */ jsxs17(Text17, { dimColor: true, children: [
|
|
3090
|
+
"No ",
|
|
3091
|
+
view === "packs" ? "methodology packs" : "skills",
|
|
3092
|
+
" found."
|
|
3093
|
+
] }) }),
|
|
3094
|
+
!loading && items.length > 0 && /* @__PURE__ */ jsxs17(Box17, { marginTop: 1, flexDirection: "column", children: [
|
|
3095
|
+
start > 0 && /* @__PURE__ */ jsxs17(Text17, { dimColor: true, children: [
|
|
3096
|
+
" ",
|
|
3097
|
+
symbols.arrowUp,
|
|
3098
|
+
" ",
|
|
3099
|
+
start,
|
|
3100
|
+
" more"
|
|
3101
|
+
] }),
|
|
3102
|
+
view === "packs" ? visible.map((pack, i) => renderPackItem(pack, start + i)) : visible.map((skill, i) => renderSkillItem(skill, start + i)),
|
|
3103
|
+
start + maxVisible < items.length && /* @__PURE__ */ jsxs17(Text17, { dimColor: true, children: [
|
|
3104
|
+
" ",
|
|
3105
|
+
symbols.arrowDown,
|
|
3106
|
+
" ",
|
|
3107
|
+
items.length - start - maxVisible,
|
|
3108
|
+
" more"
|
|
3109
|
+
] })
|
|
3110
|
+
] }),
|
|
3111
|
+
/* @__PURE__ */ jsx17(Box17, { marginTop: 1, flexDirection: "column", children: view === "packs" ? /* @__PURE__ */ jsx17(Text17, { dimColor: true, children: "Enter=view skills i=install r=refresh q=quit" }) : /* @__PURE__ */ jsx17(Text17, { dimColor: true, children: "b/Esc=back r=refresh q=quit" }) })
|
|
3112
|
+
] });
|
|
3113
|
+
}
|
|
3114
|
+
|
|
3115
|
+
// src/screens/Plan.tsx
|
|
3116
|
+
import { useState as useState20, useEffect as useEffect17, useMemo as useMemo2 } from "react";
|
|
3117
|
+
import { Box as Box18, Text as Text18, useInput as useInput16 } from "ink";
|
|
3118
|
+
import {
|
|
3119
|
+
PlanParser,
|
|
3120
|
+
PlanValidator,
|
|
3121
|
+
PlanExecutor
|
|
3122
|
+
} from "@skillkit/core";
|
|
3123
|
+
import * as fs from "fs";
|
|
3124
|
+
import * as path from "path";
|
|
3125
|
+
import { Fragment as Fragment3, jsx as jsx18, jsxs as jsxs18 } from "react/jsx-runtime";
|
|
3126
|
+
function Plan({ rows = 24 }) {
|
|
3127
|
+
const [planFiles, setPlanFiles] = useState20([]);
|
|
3128
|
+
const [selectedPlan, setSelectedPlan] = useState20(null);
|
|
3129
|
+
const [selectedTask, setSelectedTask] = useState20(null);
|
|
3130
|
+
const [loading, setLoading] = useState20(true);
|
|
3131
|
+
const [view, setView] = useState20("plans");
|
|
3132
|
+
const [sel, setSel] = useState20(0);
|
|
3133
|
+
const [error, setError] = useState20(null);
|
|
3134
|
+
const [executing, setExecuting] = useState20(false);
|
|
3135
|
+
const [message, setMessage] = useState20(null);
|
|
3136
|
+
const parser = useMemo2(() => new PlanParser(), []);
|
|
3137
|
+
const validator = useMemo2(() => new PlanValidator(), []);
|
|
3138
|
+
const maxVisible = Math.max(5, rows - 10);
|
|
3139
|
+
useEffect17(() => {
|
|
3140
|
+
loadPlans();
|
|
3141
|
+
}, []);
|
|
3142
|
+
const loadPlans = async () => {
|
|
3143
|
+
setLoading(true);
|
|
3144
|
+
setError(null);
|
|
3145
|
+
try {
|
|
3146
|
+
const cwd = process.cwd();
|
|
3147
|
+
const files = [];
|
|
3148
|
+
const searchPaths = [
|
|
3149
|
+
cwd,
|
|
3150
|
+
path.join(cwd, "plans"),
|
|
3151
|
+
path.join(cwd, ".skillkit", "plans")
|
|
3152
|
+
];
|
|
3153
|
+
for (const searchPath of searchPaths) {
|
|
3154
|
+
if (fs.existsSync(searchPath)) {
|
|
3155
|
+
const entries = fs.readdirSync(searchPath, { withFileTypes: true });
|
|
3156
|
+
for (const entry of entries) {
|
|
3157
|
+
if (entry.isFile() && (entry.name.endsWith(".plan.md") || entry.name.endsWith("-plan.md"))) {
|
|
3158
|
+
const filePath = path.join(searchPath, entry.name);
|
|
3159
|
+
const content = fs.readFileSync(filePath, "utf-8");
|
|
3160
|
+
try {
|
|
3161
|
+
const plan = parser.parse(content);
|
|
3162
|
+
const validation = validator.validate(plan);
|
|
3163
|
+
const errorIssue = validation.issues.find((i) => i.type === "error");
|
|
3164
|
+
files.push({
|
|
3165
|
+
name: entry.name,
|
|
3166
|
+
path: filePath,
|
|
3167
|
+
plan,
|
|
3168
|
+
valid: validation.valid,
|
|
3169
|
+
error: errorIssue?.message
|
|
3170
|
+
});
|
|
3171
|
+
} catch {
|
|
3172
|
+
files.push({
|
|
3173
|
+
name: entry.name,
|
|
3174
|
+
path: filePath,
|
|
3175
|
+
valid: false,
|
|
3176
|
+
error: "Failed to parse plan"
|
|
3177
|
+
});
|
|
3178
|
+
}
|
|
3179
|
+
}
|
|
3180
|
+
}
|
|
3181
|
+
}
|
|
3182
|
+
}
|
|
3183
|
+
setPlanFiles(files);
|
|
3184
|
+
} catch (e) {
|
|
3185
|
+
setError(e instanceof Error ? e.message : "Failed to load plans");
|
|
3186
|
+
}
|
|
3187
|
+
setLoading(false);
|
|
3188
|
+
};
|
|
3189
|
+
const selectPlan = (planFile) => {
|
|
3190
|
+
if (planFile.plan) {
|
|
3191
|
+
setSelectedPlan(planFile);
|
|
3192
|
+
setView("tasks");
|
|
3193
|
+
setSel(0);
|
|
3194
|
+
}
|
|
3195
|
+
};
|
|
3196
|
+
const selectTask = (task) => {
|
|
3197
|
+
setSelectedTask(task);
|
|
3198
|
+
setView("steps");
|
|
3199
|
+
setSel(0);
|
|
3200
|
+
};
|
|
3201
|
+
const executePlan = async (planFile) => {
|
|
3202
|
+
if (!planFile.plan) return;
|
|
3203
|
+
setExecuting(true);
|
|
3204
|
+
setMessage(null);
|
|
3205
|
+
try {
|
|
3206
|
+
const executor = new PlanExecutor();
|
|
3207
|
+
await executor.execute(planFile.plan, { dryRun: true });
|
|
3208
|
+
setMessage(`Executed plan: ${planFile.plan.name} (dry run)`);
|
|
3209
|
+
} catch (e) {
|
|
3210
|
+
setError(e instanceof Error ? e.message : "Failed to execute plan");
|
|
3211
|
+
}
|
|
3212
|
+
setExecuting(false);
|
|
3213
|
+
};
|
|
3214
|
+
const getItems = () => {
|
|
3215
|
+
if (view === "plans") return planFiles;
|
|
3216
|
+
if (view === "tasks" && selectedPlan?.plan) return selectedPlan.plan.tasks;
|
|
3217
|
+
if (view === "steps" && selectedTask) return selectedTask.steps;
|
|
3218
|
+
return [];
|
|
3219
|
+
};
|
|
3220
|
+
const items = getItems();
|
|
3221
|
+
const start = Math.max(0, Math.min(sel - Math.floor(maxVisible / 2), items.length - maxVisible));
|
|
3222
|
+
const visible = items.slice(start, start + maxVisible);
|
|
3223
|
+
useInput16((input, key) => {
|
|
3224
|
+
if (loading || executing) return;
|
|
3225
|
+
if (key.upArrow) setSel((i) => Math.max(0, i - 1));
|
|
3226
|
+
else if (key.downArrow) setSel((i) => Math.min(items.length - 1, i + 1));
|
|
3227
|
+
else if (input === "r") loadPlans();
|
|
3228
|
+
else if (key.escape || input === "b") {
|
|
3229
|
+
if (view === "steps") {
|
|
3230
|
+
setView("tasks");
|
|
3231
|
+
setSelectedTask(null);
|
|
3232
|
+
setSel(0);
|
|
3233
|
+
} else if (view === "tasks") {
|
|
3234
|
+
setView("plans");
|
|
3235
|
+
setSelectedPlan(null);
|
|
3236
|
+
setSel(0);
|
|
3237
|
+
}
|
|
3238
|
+
} else if (key.return) {
|
|
3239
|
+
if (view === "plans" && planFiles[sel]) {
|
|
3240
|
+
selectPlan(planFiles[sel]);
|
|
3241
|
+
} else if (view === "tasks" && selectedPlan?.plan?.tasks[sel]) {
|
|
3242
|
+
selectTask(selectedPlan.plan.tasks[sel]);
|
|
3243
|
+
}
|
|
3244
|
+
} else if (input === "x" && view === "plans" && planFiles[sel]?.plan) {
|
|
3245
|
+
executePlan(planFiles[sel]);
|
|
3246
|
+
}
|
|
3247
|
+
});
|
|
3248
|
+
const renderPlanItem = (planFile, idx) => {
|
|
3249
|
+
const isSel = idx === sel;
|
|
3250
|
+
const statusIcon = planFile.valid ? symbols.success : symbols.error;
|
|
3251
|
+
const statusColor = planFile.valid ? "green" : "red";
|
|
3252
|
+
return /* @__PURE__ */ jsxs18(Box18, { flexDirection: "column", children: [
|
|
3253
|
+
/* @__PURE__ */ jsxs18(Text18, { inverse: isSel, children: [
|
|
3254
|
+
isSel ? symbols.pointer : " ",
|
|
3255
|
+
" ",
|
|
3256
|
+
/* @__PURE__ */ jsx18(Text18, { color: statusColor, children: statusIcon }),
|
|
3257
|
+
" ",
|
|
3258
|
+
planFile.name
|
|
3259
|
+
] }),
|
|
3260
|
+
isSel && planFile.plan && /* @__PURE__ */ jsxs18(Fragment3, { children: [
|
|
3261
|
+
/* @__PURE__ */ jsxs18(Text18, { dimColor: true, children: [
|
|
3262
|
+
" ",
|
|
3263
|
+
planFile.plan.goal
|
|
3264
|
+
] }),
|
|
3265
|
+
/* @__PURE__ */ jsxs18(Text18, { dimColor: true, children: [
|
|
3266
|
+
" ",
|
|
3267
|
+
planFile.plan.tasks.length,
|
|
3268
|
+
" task(s) | ",
|
|
3269
|
+
planFile.plan.techStack?.join(", ") || "No stack"
|
|
3270
|
+
] })
|
|
3271
|
+
] }),
|
|
3272
|
+
isSel && planFile.error && /* @__PURE__ */ jsxs18(Text18, { color: "red", children: [
|
|
3273
|
+
" ",
|
|
3274
|
+
planFile.error
|
|
3275
|
+
] })
|
|
3276
|
+
] }, planFile.path);
|
|
3277
|
+
};
|
|
3278
|
+
const renderTaskItem = (task, idx) => {
|
|
3279
|
+
const isSel = idx === sel;
|
|
3280
|
+
return /* @__PURE__ */ jsxs18(Box18, { flexDirection: "column", children: [
|
|
3281
|
+
/* @__PURE__ */ jsxs18(Text18, { inverse: isSel, children: [
|
|
3282
|
+
isSel ? symbols.pointer : " ",
|
|
3283
|
+
" [",
|
|
3284
|
+
task.id,
|
|
3285
|
+
"] ",
|
|
3286
|
+
task.name
|
|
3287
|
+
] }),
|
|
3288
|
+
isSel && /* @__PURE__ */ jsxs18(Fragment3, { children: [
|
|
3289
|
+
/* @__PURE__ */ jsxs18(Text18, { dimColor: true, children: [
|
|
3290
|
+
" ",
|
|
3291
|
+
task.steps.length,
|
|
3292
|
+
" step(s)"
|
|
3293
|
+
] }),
|
|
3294
|
+
task.files?.create && /* @__PURE__ */ jsxs18(Text18, { dimColor: true, children: [
|
|
3295
|
+
" Create: ",
|
|
3296
|
+
task.files.create.join(", ")
|
|
3297
|
+
] }),
|
|
3298
|
+
task.files?.modify && /* @__PURE__ */ jsxs18(Text18, { dimColor: true, children: [
|
|
3299
|
+
" Modify: ",
|
|
3300
|
+
task.files.modify.join(", ")
|
|
3301
|
+
] }),
|
|
3302
|
+
task.files?.test && /* @__PURE__ */ jsxs18(Text18, { dimColor: true, children: [
|
|
3303
|
+
" Test: ",
|
|
3304
|
+
task.files.test.join(", ")
|
|
3305
|
+
] })
|
|
3306
|
+
] })
|
|
3307
|
+
] }, task.id);
|
|
3308
|
+
};
|
|
3309
|
+
const renderStepItem = (step, idx) => {
|
|
3310
|
+
const isSel = idx === sel;
|
|
3311
|
+
const typeColors = {
|
|
3312
|
+
test: "yellow",
|
|
3313
|
+
verify: "cyan",
|
|
3314
|
+
implement: "green",
|
|
3315
|
+
commit: "magenta"
|
|
3316
|
+
};
|
|
3317
|
+
return /* @__PURE__ */ jsxs18(Box18, { flexDirection: "column", children: [
|
|
3318
|
+
/* @__PURE__ */ jsxs18(Text18, { inverse: isSel, children: [
|
|
3319
|
+
isSel ? symbols.pointer : " ",
|
|
3320
|
+
" ",
|
|
3321
|
+
/* @__PURE__ */ jsxs18(Text18, { color: typeColors[step.type] || "white", children: [
|
|
3322
|
+
"[",
|
|
3323
|
+
step.type,
|
|
3324
|
+
"]"
|
|
3325
|
+
] }),
|
|
3326
|
+
" Step ",
|
|
3327
|
+
step.number
|
|
3328
|
+
] }),
|
|
3329
|
+
isSel && /* @__PURE__ */ jsxs18(Text18, { dimColor: true, children: [
|
|
3330
|
+
" ",
|
|
3331
|
+
step.description
|
|
3332
|
+
] })
|
|
3333
|
+
] }, step.number);
|
|
3334
|
+
};
|
|
3335
|
+
const getTitle = () => {
|
|
3336
|
+
if (view === "plans") return "PLANS";
|
|
3337
|
+
if (view === "tasks") return `TASKS: ${selectedPlan?.plan?.name || ""}`;
|
|
3338
|
+
if (view === "steps") return `STEPS: Task ${selectedTask?.id}`;
|
|
3339
|
+
return "PLANS";
|
|
3340
|
+
};
|
|
3341
|
+
return /* @__PURE__ */ jsxs18(Box18, { flexDirection: "column", children: [
|
|
3342
|
+
/* @__PURE__ */ jsx18(Text18, { bold: true, color: colors.primary, children: getTitle() }),
|
|
3343
|
+
/* @__PURE__ */ jsxs18(Text18, { dimColor: true, children: [
|
|
3344
|
+
view === "plans" && `${planFiles.length} plan(s) found`,
|
|
3345
|
+
view === "tasks" && selectedPlan?.plan && `${selectedPlan.plan.tasks.length} task(s)`,
|
|
3346
|
+
view === "steps" && selectedTask && `${selectedTask.steps.length} step(s)`
|
|
3347
|
+
] }),
|
|
3348
|
+
loading && /* @__PURE__ */ jsx18(Text18, { color: "yellow", children: "Loading plans..." }),
|
|
3349
|
+
executing && /* @__PURE__ */ jsx18(Text18, { color: "yellow", children: "Executing plan..." }),
|
|
3350
|
+
error && /* @__PURE__ */ jsxs18(Text18, { color: "red", children: [
|
|
3351
|
+
symbols.error,
|
|
3352
|
+
" ",
|
|
3353
|
+
error
|
|
3354
|
+
] }),
|
|
3355
|
+
message && /* @__PURE__ */ jsxs18(Text18, { color: "green", children: [
|
|
3356
|
+
symbols.success,
|
|
3357
|
+
" ",
|
|
3358
|
+
message
|
|
3359
|
+
] }),
|
|
3360
|
+
!loading && !executing && items.length === 0 && /* @__PURE__ */ jsxs18(Box18, { marginTop: 1, flexDirection: "column", children: [
|
|
3361
|
+
/* @__PURE__ */ jsxs18(Text18, { dimColor: true, children: [
|
|
3362
|
+
"No ",
|
|
3363
|
+
view,
|
|
3364
|
+
" found."
|
|
3365
|
+
] }),
|
|
3366
|
+
view === "plans" && /* @__PURE__ */ jsx18(Text18, { dimColor: true, children: "Create a plan file: *.plan.md or *-plan.md" })
|
|
3367
|
+
] }),
|
|
3368
|
+
!loading && !executing && items.length > 0 && /* @__PURE__ */ jsxs18(Box18, { marginTop: 1, flexDirection: "column", children: [
|
|
3369
|
+
start > 0 && /* @__PURE__ */ jsxs18(Text18, { dimColor: true, children: [
|
|
3370
|
+
" \u2191 ",
|
|
3371
|
+
start,
|
|
3372
|
+
" more"
|
|
3373
|
+
] }),
|
|
3374
|
+
view === "plans" && visible.map((pf, i) => renderPlanItem(pf, start + i)),
|
|
3375
|
+
view === "tasks" && visible.map((t, i) => renderTaskItem(t, start + i)),
|
|
3376
|
+
view === "steps" && visible.map((s, i) => renderStepItem(s, start + i)),
|
|
3377
|
+
start + maxVisible < items.length && /* @__PURE__ */ jsxs18(Text18, { dimColor: true, children: [
|
|
3378
|
+
" \u2193 ",
|
|
3379
|
+
items.length - start - maxVisible,
|
|
3380
|
+
" more"
|
|
3381
|
+
] })
|
|
3382
|
+
] }),
|
|
3383
|
+
/* @__PURE__ */ jsxs18(Box18, { marginTop: 1, children: [
|
|
3384
|
+
view === "plans" && /* @__PURE__ */ jsx18(Text18, { dimColor: true, children: "Enter=view tasks x=execute (dry-run) r=refresh q=quit" }),
|
|
3385
|
+
view === "tasks" && /* @__PURE__ */ jsx18(Text18, { dimColor: true, children: "Enter=view steps b/Esc=back q=quit" }),
|
|
3386
|
+
view === "steps" && /* @__PURE__ */ jsx18(Text18, { dimColor: true, children: "b/Esc=back q=quit" })
|
|
3387
|
+
] })
|
|
3388
|
+
] });
|
|
3389
|
+
}
|
|
3390
|
+
|
|
3391
|
+
// src/App.tsx
|
|
3392
|
+
import { jsx as jsx19, jsxs as jsxs19 } from "react/jsx-runtime";
|
|
2405
3393
|
function App() {
|
|
2406
|
-
const [screen, setScreen] =
|
|
3394
|
+
const [screen, setScreen] = useState21("home");
|
|
2407
3395
|
const { exit } = useApp();
|
|
2408
3396
|
const { stdout } = useStdout();
|
|
2409
3397
|
const cols = stdout?.columns || 80;
|
|
@@ -2422,9 +3410,13 @@ function App() {
|
|
|
2422
3410
|
e: "memory",
|
|
2423
3411
|
i: "installed",
|
|
2424
3412
|
s: "sync",
|
|
3413
|
+
a: "team",
|
|
3414
|
+
p: "plugins",
|
|
3415
|
+
o: "methodology",
|
|
3416
|
+
n: "plan",
|
|
2425
3417
|
",": "settings"
|
|
2426
3418
|
};
|
|
2427
|
-
|
|
3419
|
+
useInput17((input, key) => {
|
|
2428
3420
|
if (input === "q") {
|
|
2429
3421
|
exit();
|
|
2430
3422
|
return;
|
|
@@ -2441,63 +3433,71 @@ function App() {
|
|
|
2441
3433
|
const renderScreen = () => {
|
|
2442
3434
|
switch (screen) {
|
|
2443
3435
|
case "home":
|
|
2444
|
-
return /* @__PURE__ */
|
|
3436
|
+
return /* @__PURE__ */ jsx19(Home, { onNavigate: setScreen, cols, rows });
|
|
2445
3437
|
case "browse":
|
|
2446
|
-
return /* @__PURE__ */
|
|
3438
|
+
return /* @__PURE__ */ jsx19(Browse, { cols, rows });
|
|
2447
3439
|
case "installed":
|
|
2448
|
-
return /* @__PURE__ */
|
|
3440
|
+
return /* @__PURE__ */ jsx19(Installed, { cols, rows });
|
|
2449
3441
|
case "sync":
|
|
2450
|
-
return /* @__PURE__ */
|
|
3442
|
+
return /* @__PURE__ */ jsx19(Sync, { cols, rows });
|
|
2451
3443
|
case "settings":
|
|
2452
|
-
return /* @__PURE__ */
|
|
3444
|
+
return /* @__PURE__ */ jsx19(Settings, { cols, rows });
|
|
2453
3445
|
case "recommend":
|
|
2454
|
-
return /* @__PURE__ */
|
|
3446
|
+
return /* @__PURE__ */ jsx19(Recommend, { cols, rows });
|
|
2455
3447
|
case "translate":
|
|
2456
|
-
return /* @__PURE__ */
|
|
3448
|
+
return /* @__PURE__ */ jsx19(Translate, { cols, rows });
|
|
2457
3449
|
case "context":
|
|
2458
|
-
return /* @__PURE__ */
|
|
3450
|
+
return /* @__PURE__ */ jsx19(Context, { cols, rows });
|
|
2459
3451
|
case "workflow":
|
|
2460
|
-
return /* @__PURE__ */
|
|
3452
|
+
return /* @__PURE__ */ jsx19(Workflow, { cols, rows });
|
|
2461
3453
|
case "execute":
|
|
2462
|
-
return /* @__PURE__ */
|
|
3454
|
+
return /* @__PURE__ */ jsx19(Execute, { cols, rows });
|
|
2463
3455
|
case "history":
|
|
2464
|
-
return /* @__PURE__ */
|
|
3456
|
+
return /* @__PURE__ */ jsx19(History, { cols, rows });
|
|
2465
3457
|
case "marketplace":
|
|
2466
|
-
return /* @__PURE__ */
|
|
3458
|
+
return /* @__PURE__ */ jsx19(Marketplace, { cols, rows });
|
|
2467
3459
|
case "memory":
|
|
2468
|
-
return /* @__PURE__ */
|
|
3460
|
+
return /* @__PURE__ */ jsx19(Memory, { cols, rows });
|
|
3461
|
+
case "team":
|
|
3462
|
+
return /* @__PURE__ */ jsx19(Team, { cols, rows });
|
|
3463
|
+
case "plugins":
|
|
3464
|
+
return /* @__PURE__ */ jsx19(Plugins, { cols, rows });
|
|
3465
|
+
case "methodology":
|
|
3466
|
+
return /* @__PURE__ */ jsx19(Methodology, { cols, rows });
|
|
3467
|
+
case "plan":
|
|
3468
|
+
return /* @__PURE__ */ jsx19(Plan, { cols, rows });
|
|
2469
3469
|
}
|
|
2470
3470
|
};
|
|
2471
3471
|
const contentHeight = rows - 2;
|
|
2472
|
-
return /* @__PURE__ */
|
|
2473
|
-
/* @__PURE__ */
|
|
2474
|
-
showSidebar && /* @__PURE__ */
|
|
2475
|
-
/* @__PURE__ */
|
|
3472
|
+
return /* @__PURE__ */ jsxs19(Box19, { flexDirection: "column", height: rows, children: [
|
|
3473
|
+
/* @__PURE__ */ jsxs19(Box19, { flexDirection: "row", height: contentHeight, children: [
|
|
3474
|
+
showSidebar && /* @__PURE__ */ jsx19(Sidebar, { screen, onNavigate: setScreen }),
|
|
3475
|
+
/* @__PURE__ */ jsx19(Box19, { flexDirection: "column", flexGrow: 1, marginLeft: 1, children: renderScreen() })
|
|
2476
3476
|
] }),
|
|
2477
|
-
/* @__PURE__ */
|
|
3477
|
+
/* @__PURE__ */ jsx19(Box19, { children: /* @__PURE__ */ jsx19(Text19, { dimColor: true, children: "h Home m Market b Browse w Wflow x Exec a Team p Plug o Meth n Plan r Rec t Trans c Ctx e Mem i Inst s Sync q Quit" }) })
|
|
2478
3478
|
] });
|
|
2479
3479
|
}
|
|
2480
3480
|
|
|
2481
3481
|
// src/components/Header.tsx
|
|
2482
|
-
import { Box as
|
|
2483
|
-
import { jsx as
|
|
3482
|
+
import { Box as Box20, Text as Text20 } from "ink";
|
|
3483
|
+
import { jsx as jsx20, jsxs as jsxs20 } from "react/jsx-runtime";
|
|
2484
3484
|
function Header({ title, subtitle, count }) {
|
|
2485
|
-
return /* @__PURE__ */
|
|
2486
|
-
/* @__PURE__ */
|
|
2487
|
-
/* @__PURE__ */
|
|
2488
|
-
count !== void 0 && /* @__PURE__ */
|
|
3485
|
+
return /* @__PURE__ */ jsxs20(Box20, { flexDirection: "column", marginBottom: 1, children: [
|
|
3486
|
+
/* @__PURE__ */ jsxs20(Box20, { justifyContent: "space-between", children: [
|
|
3487
|
+
/* @__PURE__ */ jsx20(Text20, { color: colors.primary, bold: true, children: title.toUpperCase() }),
|
|
3488
|
+
count !== void 0 && /* @__PURE__ */ jsxs20(Text20, { color: colors.secondaryDim, children: [
|
|
2489
3489
|
symbols.star,
|
|
2490
3490
|
" ",
|
|
2491
3491
|
count
|
|
2492
3492
|
] })
|
|
2493
3493
|
] }),
|
|
2494
|
-
subtitle && /* @__PURE__ */
|
|
3494
|
+
subtitle && /* @__PURE__ */ jsx20(Text20, { color: colors.secondaryDim, dimColor: true, children: subtitle })
|
|
2495
3495
|
] });
|
|
2496
3496
|
}
|
|
2497
3497
|
|
|
2498
3498
|
// src/components/SkillList.tsx
|
|
2499
|
-
import { Box as
|
|
2500
|
-
import { jsx as
|
|
3499
|
+
import { Box as Box21, Text as Text21 } from "ink";
|
|
3500
|
+
import { jsx as jsx21, jsxs as jsxs21 } from "react/jsx-runtime";
|
|
2501
3501
|
function formatInstalls(count) {
|
|
2502
3502
|
if (count >= 1e3) {
|
|
2503
3503
|
return `${(count / 1e3).toFixed(1)}K`;
|
|
@@ -2513,13 +3513,13 @@ function SkillList({
|
|
|
2513
3513
|
maxVisible = 10
|
|
2514
3514
|
}) {
|
|
2515
3515
|
if (skills.length === 0) {
|
|
2516
|
-
return /* @__PURE__ */
|
|
3516
|
+
return /* @__PURE__ */ jsx21(Box21, { children: /* @__PURE__ */ jsx21(Text21, { color: colors.secondaryDim, dimColor: true, children: "No skills found" }) });
|
|
2517
3517
|
}
|
|
2518
3518
|
const startIndex = Math.max(0, selectedIndex - Math.floor(maxVisible / 2));
|
|
2519
3519
|
const visibleSkills = skills.slice(startIndex, startIndex + maxVisible);
|
|
2520
3520
|
const actualStartIndex = startIndex;
|
|
2521
|
-
return /* @__PURE__ */
|
|
2522
|
-
showRank && /* @__PURE__ */
|
|
3521
|
+
return /* @__PURE__ */ jsxs21(Box21, { flexDirection: "column", children: [
|
|
3522
|
+
showRank && /* @__PURE__ */ jsx21(Box21, { marginBottom: 1, children: /* @__PURE__ */ jsxs21(Text21, { color: colors.secondaryDim, children: [
|
|
2523
3523
|
" # SKILL",
|
|
2524
3524
|
showSource && " SOURCE",
|
|
2525
3525
|
showInstalls && " INSTALLS"
|
|
@@ -2529,9 +3529,9 @@ function SkillList({
|
|
|
2529
3529
|
const isSelected = realIndex === selectedIndex;
|
|
2530
3530
|
const skillName = skill.name.padEnd(28).slice(0, 28);
|
|
2531
3531
|
const sourceName = skill.source ? skill.source.slice(0, 25) : "";
|
|
2532
|
-
return /* @__PURE__ */
|
|
2533
|
-
/* @__PURE__ */
|
|
2534
|
-
|
|
3532
|
+
return /* @__PURE__ */ jsxs21(Box21, { children: [
|
|
3533
|
+
/* @__PURE__ */ jsxs21(
|
|
3534
|
+
Text21,
|
|
2535
3535
|
{
|
|
2536
3536
|
color: isSelected ? colors.primary : colors.secondary,
|
|
2537
3537
|
bold: isSelected,
|
|
@@ -2544,14 +3544,14 @@ function SkillList({
|
|
|
2544
3544
|
]
|
|
2545
3545
|
}
|
|
2546
3546
|
),
|
|
2547
|
-
showSource && /* @__PURE__ */
|
|
3547
|
+
showSource && /* @__PURE__ */ jsxs21(Text21, { color: colors.secondaryDim, dimColor: !isSelected, children: [
|
|
2548
3548
|
" ",
|
|
2549
3549
|
sourceName
|
|
2550
3550
|
] }),
|
|
2551
|
-
showInstalls && skill.installs !== void 0 && /* @__PURE__ */
|
|
3551
|
+
showInstalls && skill.installs !== void 0 && /* @__PURE__ */ jsx21(Text21, { color: colors.secondaryDim, children: formatInstalls(skill.installs).padStart(8) })
|
|
2552
3552
|
] }, `${skill.source}-${skill.name}`);
|
|
2553
3553
|
}),
|
|
2554
|
-
skills.length > maxVisible && /* @__PURE__ */
|
|
3554
|
+
skills.length > maxVisible && /* @__PURE__ */ jsx21(Box21, { marginTop: 1, children: /* @__PURE__ */ jsxs21(Text21, { color: colors.secondaryDim, dimColor: true, children: [
|
|
2555
3555
|
"Showing ",
|
|
2556
3556
|
startIndex + 1,
|
|
2557
3557
|
"-",
|
|
@@ -2563,11 +3563,11 @@ function SkillList({
|
|
|
2563
3563
|
}
|
|
2564
3564
|
|
|
2565
3565
|
// src/components/StatusBar.tsx
|
|
2566
|
-
import { Box as
|
|
2567
|
-
import { jsx as
|
|
3566
|
+
import { Box as Box22, Text as Text22 } from "ink";
|
|
3567
|
+
import { jsx as jsx22, jsxs as jsxs22 } from "react/jsx-runtime";
|
|
2568
3568
|
function StatusBar({ shortcuts, message }) {
|
|
2569
|
-
return /* @__PURE__ */
|
|
2570
|
-
|
|
3569
|
+
return /* @__PURE__ */ jsxs22(
|
|
3570
|
+
Box22,
|
|
2571
3571
|
{
|
|
2572
3572
|
borderStyle: "single",
|
|
2573
3573
|
borderColor: colors.borderDim,
|
|
@@ -2578,11 +3578,11 @@ function StatusBar({ shortcuts, message }) {
|
|
|
2578
3578
|
paddingX: 1,
|
|
2579
3579
|
justifyContent: "space-between",
|
|
2580
3580
|
children: [
|
|
2581
|
-
/* @__PURE__ */
|
|
2582
|
-
/* @__PURE__ */
|
|
2583
|
-
/* @__PURE__ */
|
|
3581
|
+
/* @__PURE__ */ jsx22(Box22, { gap: 2, children: shortcuts.map((shortcut, idx) => /* @__PURE__ */ jsxs22(Box22, { gap: 1, children: [
|
|
3582
|
+
/* @__PURE__ */ jsx22(Text22, { color: colors.primary, bold: true, children: shortcut.key }),
|
|
3583
|
+
/* @__PURE__ */ jsx22(Text22, { color: colors.secondaryDim, children: shortcut.label })
|
|
2584
3584
|
] }, idx)) }),
|
|
2585
|
-
message && /* @__PURE__ */
|
|
3585
|
+
message && /* @__PURE__ */ jsxs22(Text22, { color: colors.success, children: [
|
|
2586
3586
|
symbols.check,
|
|
2587
3587
|
" ",
|
|
2588
3588
|
message
|
|
@@ -2593,44 +3593,44 @@ function StatusBar({ shortcuts, message }) {
|
|
|
2593
3593
|
}
|
|
2594
3594
|
|
|
2595
3595
|
// src/components/SearchInput.tsx
|
|
2596
|
-
import { Box as
|
|
2597
|
-
import
|
|
2598
|
-
import { jsx as
|
|
3596
|
+
import { Box as Box23, Text as Text23 } from "ink";
|
|
3597
|
+
import TextInput2 from "ink-text-input";
|
|
3598
|
+
import { jsx as jsx23, jsxs as jsxs23 } from "react/jsx-runtime";
|
|
2599
3599
|
function SearchInput({
|
|
2600
3600
|
value,
|
|
2601
3601
|
onChange,
|
|
2602
3602
|
placeholder = "Search skills...",
|
|
2603
3603
|
isFocused = false
|
|
2604
3604
|
}) {
|
|
2605
|
-
return /* @__PURE__ */
|
|
2606
|
-
|
|
3605
|
+
return /* @__PURE__ */ jsxs23(
|
|
3606
|
+
Box23,
|
|
2607
3607
|
{
|
|
2608
3608
|
borderStyle: "single",
|
|
2609
3609
|
borderColor: isFocused ? colors.primary : colors.borderDim,
|
|
2610
3610
|
paddingX: 1,
|
|
2611
3611
|
children: [
|
|
2612
|
-
/* @__PURE__ */
|
|
2613
|
-
isFocused ? /* @__PURE__ */
|
|
2614
|
-
|
|
3612
|
+
/* @__PURE__ */ jsx23(Text23, { color: colors.secondaryDim, children: "/ " }),
|
|
3613
|
+
isFocused ? /* @__PURE__ */ jsx23(
|
|
3614
|
+
TextInput2,
|
|
2615
3615
|
{
|
|
2616
3616
|
value,
|
|
2617
3617
|
onChange,
|
|
2618
3618
|
placeholder
|
|
2619
3619
|
}
|
|
2620
|
-
) : /* @__PURE__ */
|
|
2621
|
-
/* @__PURE__ */
|
|
2622
|
-
/* @__PURE__ */
|
|
3620
|
+
) : /* @__PURE__ */ jsx23(Text23, { color: value ? colors.secondary : colors.secondaryDim, children: value || placeholder }),
|
|
3621
|
+
/* @__PURE__ */ jsx23(Box23, { flexGrow: 1 }),
|
|
3622
|
+
/* @__PURE__ */ jsx23(Text23, { color: colors.secondaryDim, children: "/" })
|
|
2623
3623
|
]
|
|
2624
3624
|
}
|
|
2625
3625
|
);
|
|
2626
3626
|
}
|
|
2627
3627
|
|
|
2628
3628
|
// src/hooks/useKeyboard.ts
|
|
2629
|
-
import { useState as
|
|
2630
|
-
import { useInput as
|
|
3629
|
+
import { useState as useState22, useCallback as useCallback3, useEffect as useEffect18 } from "react";
|
|
3630
|
+
import { useInput as useInput18, useApp as useApp2 } from "ink";
|
|
2631
3631
|
function useKeyboard(options = {}) {
|
|
2632
3632
|
const { exit } = useApp2();
|
|
2633
|
-
|
|
3633
|
+
useInput18((input, key) => {
|
|
2634
3634
|
if (options.disabled) return;
|
|
2635
3635
|
if (input === "q" || key.ctrl && input === "c") {
|
|
2636
3636
|
exit();
|
|
@@ -2674,8 +3674,8 @@ function useKeyboard(options = {}) {
|
|
|
2674
3674
|
});
|
|
2675
3675
|
}
|
|
2676
3676
|
function useListNavigation(listLength, initialIndex = 0) {
|
|
2677
|
-
const [selectedIndex, setSelectedIndex] =
|
|
2678
|
-
|
|
3677
|
+
const [selectedIndex, setSelectedIndex] = useState22(initialIndex);
|
|
3678
|
+
useEffect18(() => {
|
|
2679
3679
|
if (selectedIndex >= listLength && listLength > 0) {
|
|
2680
3680
|
setSelectedIndex(listLength - 1);
|
|
2681
3681
|
}
|
|
@@ -2693,7 +3693,7 @@ function useListNavigation(listLength, initialIndex = 0) {
|
|
|
2693
3693
|
}
|
|
2694
3694
|
|
|
2695
3695
|
// src/hooks/useMemory.ts
|
|
2696
|
-
import { useState as
|
|
3696
|
+
import { useState as useState23, useEffect as useEffect19, useCallback as useCallback4 } from "react";
|
|
2697
3697
|
import {
|
|
2698
3698
|
LearningStore as LearningStore2,
|
|
2699
3699
|
ObservationStore as ObservationStore2,
|
|
@@ -2701,12 +3701,12 @@ import {
|
|
|
2701
3701
|
createMemoryInjector as createMemoryInjector2
|
|
2702
3702
|
} from "@skillkit/core";
|
|
2703
3703
|
function useMemory() {
|
|
2704
|
-
const [learnings, setLearnings] =
|
|
2705
|
-
const [observations, setObservations] =
|
|
2706
|
-
const [status, setStatus] =
|
|
2707
|
-
const [loading, setLoading] =
|
|
2708
|
-
const [error, setError] =
|
|
2709
|
-
const [isGlobal, setIsGlobal] =
|
|
3704
|
+
const [learnings, setLearnings] = useState23([]);
|
|
3705
|
+
const [observations, setObservations] = useState23([]);
|
|
3706
|
+
const [status, setStatus] = useState23(null);
|
|
3707
|
+
const [loading, setLoading] = useState23(true);
|
|
3708
|
+
const [error, setError] = useState23(null);
|
|
3709
|
+
const [isGlobal, setIsGlobal] = useState23(false);
|
|
2710
3710
|
const projectPath = process.cwd();
|
|
2711
3711
|
const refresh = useCallback4(() => {
|
|
2712
3712
|
setLoading(true);
|
|
@@ -2785,7 +3785,7 @@ function useMemory() {
|
|
|
2785
3785
|
},
|
|
2786
3786
|
[projectPath, refresh]
|
|
2787
3787
|
);
|
|
2788
|
-
|
|
3788
|
+
useEffect19(() => {
|
|
2789
3789
|
refresh();
|
|
2790
3790
|
}, [refresh]);
|
|
2791
3791
|
return {
|
|
@@ -2804,10 +3804,10 @@ function useMemory() {
|
|
|
2804
3804
|
}
|
|
2805
3805
|
|
|
2806
3806
|
// src/index.tsx
|
|
2807
|
-
import { jsx as
|
|
3807
|
+
import { jsx as jsx24 } from "react/jsx-runtime";
|
|
2808
3808
|
function startTUI() {
|
|
2809
3809
|
process.stdout.write("\x1B[2J\x1B[0f");
|
|
2810
|
-
const { waitUntilExit, clear } = render(/* @__PURE__ */
|
|
3810
|
+
const { waitUntilExit, clear } = render(/* @__PURE__ */ jsx24(App, {}), {
|
|
2811
3811
|
exitOnCtrlC: true
|
|
2812
3812
|
});
|
|
2813
3813
|
return waitUntilExit().then(() => {
|
|
@@ -2825,6 +3825,9 @@ export {
|
|
|
2825
3825
|
Installed,
|
|
2826
3826
|
Marketplace,
|
|
2827
3827
|
Memory,
|
|
3828
|
+
Methodology,
|
|
3829
|
+
Plan,
|
|
3830
|
+
Plugins,
|
|
2828
3831
|
Recommend,
|
|
2829
3832
|
SearchInput,
|
|
2830
3833
|
Settings,
|
|
@@ -2832,6 +3835,7 @@ export {
|
|
|
2832
3835
|
SkillList,
|
|
2833
3836
|
StatusBar,
|
|
2834
3837
|
Sync,
|
|
3838
|
+
Team,
|
|
2835
3839
|
Translate,
|
|
2836
3840
|
Workflow,
|
|
2837
3841
|
colors,
|