@skillsgate/tui 0.1.10 → 0.1.13
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/package.json +1 -8
- package/src/components/agent-filter.tsx +2 -2
- package/src/components/help-overlay.tsx +4 -4
- package/src/components/layout.tsx +15 -40
- package/src/components/status-bar.tsx +4 -7
- package/src/data/api-client.ts +78 -109
- package/src/data/use-favorites.ts +18 -4
- package/src/data/use-installed-skills.ts +178 -3
- package/src/data/use-search.ts +14 -31
- package/src/data/use-skill-actions.ts +87 -18
- package/src/db/skills.ts +24 -0
- package/src/db/ssh.ts +45 -1
- package/src/store/types.ts +8 -0
- package/src/types/bun-sqlite.d.ts +12 -0
- package/src/views/add-server.tsx +3 -3
- package/src/views/discover.tsx +42 -130
- package/src/views/favorites.tsx +10 -349
- package/src/views/home.tsx +531 -9
- package/src/views/login.tsx +1 -1
- package/src/views/server-skills.tsx +61 -4
- package/src/views/servers.tsx +2 -2
- package/src/views/settings.tsx +0 -6
- package/src/views/skill-detail.tsx +17 -16
- package/tmp.json +0 -0
|
@@ -1,9 +1,15 @@
|
|
|
1
1
|
import { useState, useEffect } from "react"
|
|
2
|
+
import crypto from "node:crypto"
|
|
3
|
+
import { spawnSync } from "node:child_process"
|
|
4
|
+
import fs from "node:fs"
|
|
5
|
+
import os from "node:os"
|
|
6
|
+
import path from "node:path"
|
|
2
7
|
import { useKeyboard } from "@opentui/react"
|
|
3
8
|
import { useStore, useDispatch } from "../store/context.js"
|
|
4
9
|
import { useDb } from "../db/context.js"
|
|
5
10
|
import { colors } from "../utils/colors.js"
|
|
6
11
|
import type { RemoteSkill } from "../db/skills.js"
|
|
12
|
+
import { readRemoteFile, writeRemoteFile } from "../db/ssh.js"
|
|
7
13
|
|
|
8
14
|
interface ServerSkillsViewProps {
|
|
9
15
|
serverId: string
|
|
@@ -61,6 +67,57 @@ export function ServerSkillsView({ serverId }: ServerSkillsViewProps) {
|
|
|
61
67
|
return
|
|
62
68
|
}
|
|
63
69
|
|
|
70
|
+
if (key.name === "e" && selectedSkill && server) {
|
|
71
|
+
const editor = process.env.VISUAL || process.env.EDITOR || "vi"
|
|
72
|
+
const tempPath = path.join(
|
|
73
|
+
os.tmpdir(),
|
|
74
|
+
`skillsgate-remote-${selectedSkill.id}.md`,
|
|
75
|
+
)
|
|
76
|
+
const initialContent = selectedSkill.content ?? ""
|
|
77
|
+
try {
|
|
78
|
+
fs.writeFileSync(tempPath, initialContent, "utf-8")
|
|
79
|
+
spawnSync(editor, [tempPath], { stdio: "inherit" })
|
|
80
|
+
const nextContent = fs.readFileSync(tempPath, "utf-8")
|
|
81
|
+
if (nextContent !== initialContent) {
|
|
82
|
+
writeRemoteFile(server, selectedSkill.remotePath, nextContent)
|
|
83
|
+
.then(async () => {
|
|
84
|
+
const refreshed = await readRemoteFile(server, selectedSkill.remotePath)
|
|
85
|
+
const contentHash = crypto
|
|
86
|
+
.createHash("sha256")
|
|
87
|
+
.update(refreshed, "utf-8")
|
|
88
|
+
.digest("hex")
|
|
89
|
+
skills.updateContent(serverId, selectedSkill.remotePath, refreshed, contentHash)
|
|
90
|
+
setSkillList(skills.listByServer(serverId))
|
|
91
|
+
dispatch({
|
|
92
|
+
type: "SHOW_NOTIFICATION",
|
|
93
|
+
notification: { type: "success", message: `Saved "${selectedSkill.name}"` },
|
|
94
|
+
})
|
|
95
|
+
})
|
|
96
|
+
.catch((err) => {
|
|
97
|
+
dispatch({
|
|
98
|
+
type: "SHOW_NOTIFICATION",
|
|
99
|
+
notification: {
|
|
100
|
+
type: "error",
|
|
101
|
+
message: err instanceof Error ? err.message : String(err),
|
|
102
|
+
},
|
|
103
|
+
})
|
|
104
|
+
})
|
|
105
|
+
}
|
|
106
|
+
} catch (err) {
|
|
107
|
+
dispatch({
|
|
108
|
+
type: "SHOW_NOTIFICATION",
|
|
109
|
+
notification: {
|
|
110
|
+
type: "error",
|
|
111
|
+
message: err instanceof Error ? err.message : String(err),
|
|
112
|
+
},
|
|
113
|
+
})
|
|
114
|
+
} finally {
|
|
115
|
+
try {
|
|
116
|
+
fs.unlinkSync(tempPath)
|
|
117
|
+
} catch {}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
64
121
|
// Esc = go back (handled by layout)
|
|
65
122
|
})
|
|
66
123
|
|
|
@@ -97,10 +154,10 @@ export function ServerSkillsView({ serverId }: ServerSkillsViewProps) {
|
|
|
97
154
|
<box
|
|
98
155
|
style={{
|
|
99
156
|
width: "40%",
|
|
100
|
-
|
|
157
|
+
border: true,
|
|
101
158
|
borderColor: colors.border,
|
|
102
159
|
flexDirection: "column",
|
|
103
|
-
}}
|
|
160
|
+
} as any}
|
|
104
161
|
>
|
|
105
162
|
<box style={{ height: 1, paddingLeft: 1, backgroundColor: colors.bgAlt }}>
|
|
106
163
|
<text fg={colors.textDim}>REMOTE SKILLS</text>
|
|
@@ -150,7 +207,7 @@ export function ServerSkillsView({ serverId }: ServerSkillsViewProps) {
|
|
|
150
207
|
|
|
151
208
|
{/* Bottom hints */}
|
|
152
209
|
<box style={{ height: 1, paddingLeft: 1, backgroundColor: colors.bgAlt }}>
|
|
153
|
-
<text fg={colors.textDim}>i=install locally Esc=back</text>
|
|
210
|
+
<text fg={colors.textDim}>i=install locally e=edit Esc=back</text>
|
|
154
211
|
</box>
|
|
155
212
|
</box>
|
|
156
213
|
|
|
@@ -234,7 +291,7 @@ function RemoteSkillDetail({ skill }: RemoteSkillDetailProps) {
|
|
|
234
291
|
</box>
|
|
235
292
|
|
|
236
293
|
<text>{" "}</text>
|
|
237
|
-
<text fg={colors.textDim}>i=install locally Esc=back to server list</text>
|
|
294
|
+
<text fg={colors.textDim}>i=install locally e=edit Esc=back to server list</text>
|
|
238
295
|
<text fg={colors.border}>---</text>
|
|
239
296
|
|
|
240
297
|
{/* Skill content */}
|
package/src/views/servers.tsx
CHANGED
|
@@ -237,10 +237,10 @@ export function ServersView({ onServerCountChange }: ServersViewProps) {
|
|
|
237
237
|
<box
|
|
238
238
|
style={{
|
|
239
239
|
width: "50%",
|
|
240
|
-
|
|
240
|
+
border: true,
|
|
241
241
|
borderColor: colors.border,
|
|
242
242
|
flexDirection: "column",
|
|
243
|
-
}}
|
|
243
|
+
} as any}
|
|
244
244
|
>
|
|
245
245
|
{/* Header */}
|
|
246
246
|
<box style={{ height: 1, paddingLeft: 1, backgroundColor: colors.bgAlt }}>
|
package/src/views/settings.tsx
CHANGED
|
@@ -34,12 +34,6 @@ const SETTING_DEFS: SettingDef[] = [
|
|
|
34
34
|
options: ["dark", "light", "system"],
|
|
35
35
|
defaultValue: "dark",
|
|
36
36
|
},
|
|
37
|
-
{
|
|
38
|
-
key: "search.preferSemantic",
|
|
39
|
-
label: "Prefer semantic search",
|
|
40
|
-
type: "toggle",
|
|
41
|
-
defaultValue: true,
|
|
42
|
-
},
|
|
43
37
|
{
|
|
44
38
|
key: "telemetry.enabled",
|
|
45
39
|
label: "Anonymous telemetry",
|
|
@@ -6,6 +6,7 @@ import { useKeyboard } from "@opentui/react"
|
|
|
6
6
|
import { useStore, useDispatch } from "../store/context.js"
|
|
7
7
|
import { useSkillActions } from "../data/use-skill-actions.js"
|
|
8
8
|
import { ConfirmDialog } from "../components/confirm-dialog.js"
|
|
9
|
+
import { fetchSkillContent } from "../data/api-client.js"
|
|
9
10
|
import { colors, agentBadges as badgeMap } from "../utils/colors.js"
|
|
10
11
|
import { agents } from "../../../cli/src/core/agents.js"
|
|
11
12
|
|
|
@@ -75,19 +76,15 @@ export function SkillDetailView() {
|
|
|
75
76
|
return
|
|
76
77
|
}
|
|
77
78
|
|
|
78
|
-
// Catalog skill: fetch content from
|
|
79
|
-
const
|
|
80
|
-
const
|
|
81
|
-
if (
|
|
79
|
+
// Catalog skill: fetch SKILL.md content from GitHub
|
|
80
|
+
const source = skill.metadata?.source as string | undefined
|
|
81
|
+
const skillId = skill.metadata?.skillId as string | undefined
|
|
82
|
+
if (source && skillId) {
|
|
82
83
|
setContentLoading(true)
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
.then(res => res.ok ? res.json() : null)
|
|
88
|
-
.then(data => {
|
|
89
|
-
if (data?.content) {
|
|
90
|
-
setContent(stripFrontmatter(data.content))
|
|
84
|
+
fetchSkillContent(source, skillId)
|
|
85
|
+
.then((md) => {
|
|
86
|
+
if (md) {
|
|
87
|
+
setContent(stripFrontmatter(md))
|
|
91
88
|
} else {
|
|
92
89
|
setContent(skill.description || "(No content available)")
|
|
93
90
|
}
|
|
@@ -410,12 +407,12 @@ export function SkillDetailView() {
|
|
|
410
407
|
width: "30%",
|
|
411
408
|
flexDirection: "column",
|
|
412
409
|
backgroundColor: colors.bgAlt,
|
|
413
|
-
|
|
410
|
+
border: true,
|
|
414
411
|
borderColor: colors.border,
|
|
415
412
|
paddingLeft: 1,
|
|
416
413
|
paddingRight: 1,
|
|
417
414
|
paddingTop: 1,
|
|
418
|
-
}}
|
|
415
|
+
} as any}
|
|
419
416
|
>
|
|
420
417
|
{/* Skill name */}
|
|
421
418
|
<text fg={colors.primary}>
|
|
@@ -424,8 +421,12 @@ export function SkillDetailView() {
|
|
|
424
421
|
<text>{" "}</text>
|
|
425
422
|
|
|
426
423
|
{/* Description */}
|
|
427
|
-
|
|
428
|
-
|
|
424
|
+
{skill.description ? (
|
|
425
|
+
<>
|
|
426
|
+
<text fg={colors.text}>{skill.description}</text>
|
|
427
|
+
<text>{" "}</text>
|
|
428
|
+
</>
|
|
429
|
+
) : null}
|
|
429
430
|
|
|
430
431
|
{/* Source */}
|
|
431
432
|
<text fg={colors.textDim}>Source</text>
|
package/tmp.json
ADDED
|
File without changes
|