saeeol 1.2.1 → 1.2.2

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.
Files changed (113) hide show
  1. package/package.json +11 -11
  2. package/src/cli/cmd/tui/component/dialog/dialog-agent.tsx +32 -0
  3. package/src/cli/cmd/tui/component/dialog/dialog-command.tsx +190 -0
  4. package/src/cli/cmd/tui/component/dialog/dialog-console-org.tsx +103 -0
  5. package/src/cli/cmd/tui/component/dialog/dialog-go-upsell.tsx +159 -0
  6. package/src/cli/cmd/tui/component/dialog/dialog-mcp.tsx +86 -0
  7. package/src/cli/cmd/tui/component/dialog/dialog-model.tsx +238 -0
  8. package/src/cli/cmd/tui/component/dialog/dialog-provider.tsx +343 -0
  9. package/src/cli/cmd/tui/component/dialog/dialog-session-delete-failed.tsx +103 -0
  10. package/src/cli/cmd/tui/component/dialog/dialog-session-list.tsx +301 -0
  11. package/src/cli/cmd/tui/component/dialog/dialog-session-rename.tsx +35 -0
  12. package/src/cli/cmd/tui/component/dialog/dialog-skill.tsx +37 -0
  13. package/src/cli/cmd/tui/component/dialog/dialog-stash.tsx +87 -0
  14. package/src/cli/cmd/tui/component/dialog/dialog-status.tsx +190 -0
  15. package/src/cli/cmd/tui/component/dialog/dialog-tag.tsx +44 -0
  16. package/src/cli/cmd/tui/component/dialog/dialog-theme-list.tsx +50 -0
  17. package/src/cli/cmd/tui/component/dialog/dialog-variant.tsx +39 -0
  18. package/src/cli/cmd/tui/component/dialog/dialog-workspace-create.tsx +200 -0
  19. package/src/cli/cmd/tui/component/dialog/dialog-workspace-unavailable.tsx +81 -0
  20. package/src/cli/cmd/tui/component/dialog-agent.tsx +1 -32
  21. package/src/cli/cmd/tui/component/dialog-command.tsx +1 -190
  22. package/src/cli/cmd/tui/component/dialog-console-org.tsx +1 -103
  23. package/src/cli/cmd/tui/component/dialog-go-upsell.tsx +1 -159
  24. package/src/cli/cmd/tui/component/dialog-mcp.tsx +1 -86
  25. package/src/cli/cmd/tui/component/dialog-model.tsx +1 -238
  26. package/src/cli/cmd/tui/component/dialog-provider.tsx +1 -343
  27. package/src/cli/cmd/tui/component/dialog-session-delete-failed.tsx +1 -103
  28. package/src/cli/cmd/tui/component/dialog-session-list.tsx +1 -301
  29. package/src/cli/cmd/tui/component/dialog-session-rename.tsx +1 -35
  30. package/src/cli/cmd/tui/component/dialog-skill.tsx +1 -37
  31. package/src/cli/cmd/tui/component/dialog-stash.tsx +1 -87
  32. package/src/cli/cmd/tui/component/dialog-status.tsx +1 -190
  33. package/src/cli/cmd/tui/component/dialog-tag.tsx +1 -44
  34. package/src/cli/cmd/tui/component/dialog-theme-list.tsx +1 -50
  35. package/src/cli/cmd/tui/component/dialog-variant.tsx +1 -39
  36. package/src/cli/cmd/tui/component/dialog-workspace-create.tsx +1 -200
  37. package/src/cli/cmd/tui/component/dialog-workspace-unavailable.tsx +1 -81
  38. package/src/tool/apply_patch.ts +1 -334
  39. package/src/tool/bash.ts +1 -656
  40. package/src/tool/core/external-directory.ts +55 -0
  41. package/src/tool/core/invalid.ts +21 -0
  42. package/src/tool/core/recall.ts +164 -0
  43. package/src/tool/core/recall.txt +12 -0
  44. package/src/tool/core/schema.ts +16 -0
  45. package/src/tool/core/tool.ts +162 -0
  46. package/src/tool/core/truncate.ts +160 -0
  47. package/src/tool/core/truncation-dir.ts +4 -0
  48. package/src/tool/diagnostics.ts +1 -20
  49. package/src/tool/edit-replacers.ts +1 -288
  50. package/src/tool/edit-utils.ts +1 -86
  51. package/src/tool/edit.ts +1 -262
  52. package/src/tool/external-directory.ts +1 -55
  53. package/src/tool/file/apply_patch.ts +334 -0
  54. package/src/tool/file/apply_patch.txt +33 -0
  55. package/src/tool/file/bash.ts +656 -0
  56. package/src/tool/file/bash.txt +119 -0
  57. package/src/tool/file/edit-replacers.ts +288 -0
  58. package/src/tool/file/edit-utils.ts +86 -0
  59. package/src/tool/file/edit.ts +262 -0
  60. package/src/tool/file/edit.txt +10 -0
  61. package/src/tool/file/read.ts +389 -0
  62. package/src/tool/file/read.txt +14 -0
  63. package/src/tool/file/write.ts +114 -0
  64. package/src/tool/file/write.txt +8 -0
  65. package/src/tool/glob.ts +1 -115
  66. package/src/tool/grep.ts +1 -151
  67. package/src/tool/integration/diagnostics.ts +20 -0
  68. package/src/tool/integration/lsp.ts +113 -0
  69. package/src/tool/integration/lsp.txt +24 -0
  70. package/src/tool/integration/mcp-exa.ts +73 -0
  71. package/src/tool/integration/package.ts +168 -0
  72. package/src/tool/integration/registry.ts +375 -0
  73. package/src/tool/invalid.ts +1 -21
  74. package/src/tool/lsp.ts +1 -113
  75. package/src/tool/mcp-exa.ts +1 -73
  76. package/src/tool/package.ts +1 -168
  77. package/src/tool/plan.ts +1 -30
  78. package/src/tool/question.ts +1 -52
  79. package/src/tool/read.ts +1 -389
  80. package/src/tool/recall.ts +1 -164
  81. package/src/tool/registry.ts +1 -375
  82. package/src/tool/schema.ts +1 -16
  83. package/src/tool/search/glob.ts +115 -0
  84. package/src/tool/search/glob.txt +6 -0
  85. package/src/tool/search/grep.ts +151 -0
  86. package/src/tool/search/grep.txt +8 -0
  87. package/src/tool/search/warpgrep.ts +107 -0
  88. package/src/tool/search/warpgrep.txt +10 -0
  89. package/src/tool/search/webfetch.ts +202 -0
  90. package/src/tool/search/webfetch.txt +13 -0
  91. package/src/tool/search/websearch.ts +71 -0
  92. package/src/tool/search/websearch.txt +14 -0
  93. package/src/tool/skill.ts +1 -91
  94. package/src/tool/task.ts +1 -197
  95. package/src/tool/todo.ts +1 -62
  96. package/src/tool/tool.ts +1 -162
  97. package/src/tool/truncate.ts +1 -160
  98. package/src/tool/truncation-dir.ts +1 -4
  99. package/src/tool/warpgrep.ts +1 -107
  100. package/src/tool/webfetch.ts +1 -202
  101. package/src/tool/websearch.ts +1 -71
  102. package/src/tool/workflow/plan-enter.txt +14 -0
  103. package/src/tool/workflow/plan-exit.txt +13 -0
  104. package/src/tool/workflow/plan.ts +30 -0
  105. package/src/tool/workflow/question.ts +52 -0
  106. package/src/tool/workflow/question.txt +11 -0
  107. package/src/tool/workflow/skill.ts +91 -0
  108. package/src/tool/workflow/skill.txt +5 -0
  109. package/src/tool/workflow/task.ts +197 -0
  110. package/src/tool/workflow/task.txt +57 -0
  111. package/src/tool/workflow/todo.ts +62 -0
  112. package/src/tool/workflow/todowrite.txt +167 -0
  113. package/src/tool/write.ts +1 -114
@@ -1,301 +1 @@
1
- import { useDialog } from "@tui/ui/dialog"
2
- import { DialogSelect } from "@tui/ui/dialog-select"
3
- import { useRoute } from "@tui/context/route"
4
- import { useSync } from "@tui/context/sync"
5
- import { createMemo, createResource, createSignal, onMount } from "solid-js"
6
- import { Locale } from "@/util/locale"
7
- import { useProject } from "@tui/context/project"
8
- import { useKeybind } from "../context/keybind"
9
- import { useTheme } from "../context/theme"
10
- import { useSDK } from "../context/sdk"
11
- import { Flag } from "@saeeol/core/flag/flag"
12
- import { DialogSessionRename } from "./dialog-session-rename"
13
- import { Keybind } from "@/util/keybind"
14
- import { createDebouncedSignal } from "../util/signal"
15
- import { useToast } from "../ui/toast"
16
- import { DialogWorkspaceCreate, openWorkspaceSession, restoreWorkspaceSession } from "./dialog-workspace-create"
17
- import { Spinner } from "./spinner"
18
- import path from "path"
19
- import { errorMessage } from "@/util/error"
20
- import { DialogSessionDeleteFailed } from "./dialog-session-delete-failed"
21
-
22
- type WorkspaceStatus = "connected" | "connecting" | "disconnected" | "error"
23
-
24
- export function DialogSessionList() {
25
- const dialog = useDialog()
26
- const route = useRoute()
27
- const sync = useSync()
28
- const project = useProject()
29
- const keybind = useKeybind()
30
- const { theme } = useTheme()
31
- const sdk = useSDK()
32
- const toast = useToast()
33
- const [toDelete, setToDelete] = createSignal<string>()
34
- const [search, setSearch] = createDebouncedSignal("", 150)
35
- const [global, setGlobal] = createSignal(true)
36
- // TODO: extend /experimental/session to accept `scope`/`path` so this dialog can respect the
37
- // upstream `session_directory_filter_enabled` KV toggle (via sync.session.query()) while
38
- // keeping worktree grouping. Currently the toggle has no effect here.
39
- const [searchResults, searchActions] = createResource(
40
- () => search(),
41
- async (query) => {
42
- const result = await sdk.client.experimental.session.list(
43
- {
44
- search: query || undefined,
45
- roots: true,
46
- worktrees: true,
47
- limit: 30,
48
- },
49
- { throwOnError: true },
50
- )
51
- return result.data ?? []
52
- },
53
- )
54
-
55
- const currentSessionID = createMemo(() => (route.data.type === "session" ? route.data.sessionID : undefined))
56
- const sessions = createMemo(() => {
57
- const all = searchResults() ?? []
58
- if (global()) return all
59
- const root = project.instance.path().worktree
60
- if (!root || root === "/") return all
61
- return all.filter((s) => s.directory === root || s.directory.startsWith(root + path.sep))
62
- })
63
-
64
- function createWorkspace() {
65
- dialog.replace(() => (
66
- <DialogWorkspaceCreate
67
- onSelect={(workspaceID) =>
68
- openWorkspaceSession({
69
- dialog,
70
- route,
71
- sdk,
72
- sync,
73
- toast,
74
- workspaceID,
75
- })
76
- }
77
- />
78
- ))
79
- }
80
-
81
- function recover(session: NonNullable<ReturnType<typeof sessions>[number]>) {
82
- const workspace = project.workspace.get(session.workspaceID!)
83
- const list = () => dialog.replace(() => <DialogSessionList />)
84
- dialog.replace(() => (
85
- <DialogSessionDeleteFailed
86
- session={session.title}
87
- workspace={workspace?.name ?? session.workspaceID!}
88
- onDone={list}
89
- onDelete={async () => {
90
- const current = currentSessionID()
91
- const info = current ? sync.data.session.find((item) => item.id === current) : undefined
92
- const result = await sdk.client.experimental.workspace.remove({ id: session.workspaceID! })
93
- if (result.error) {
94
- toast.show({
95
- variant: "error",
96
- title: "Failed to delete workspace",
97
- message: errorMessage(result.error),
98
- })
99
- return false
100
- }
101
- await project.workspace.sync()
102
- await sync.session.refresh()
103
- if (search()) await searchActions.refetch()
104
- if (info?.workspaceID === session.workspaceID) {
105
- route.navigate({ type: "home" })
106
- }
107
- return true
108
- }}
109
- onRestore={() => {
110
- dialog.replace(() => (
111
- <DialogWorkspaceCreate
112
- onSelect={(workspaceID) =>
113
- restoreWorkspaceSession({
114
- dialog,
115
- sdk,
116
- sync,
117
- project,
118
- toast,
119
- workspaceID,
120
- sessionID: session.id,
121
- done: list,
122
- })
123
- }
124
- />
125
- ))
126
- return false
127
- }}
128
- />
129
- ))
130
- }
131
-
132
- const options = createMemo(() => {
133
- const today = new Date().toDateString()
134
- const all = global()
135
- return sessions()
136
- .filter((x) => x.parentID === undefined)
137
- .toSorted((a, b) => {
138
- const updatedDay = new Date(b.time.updated).setHours(0, 0, 0, 0) - new Date(a.time.updated).setHours(0, 0, 0, 0)
139
- if (updatedDay !== 0) return updatedDay
140
- return b.time.created - a.time.created
141
- })
142
- .map((x) => {
143
- const workspace = x.workspaceID ? project.workspace.get(x.workspaceID) : undefined
144
-
145
- let workspaceStatus: WorkspaceStatus | null = null
146
- if (x.workspaceID) {
147
- workspaceStatus = project.workspace.status(x.workspaceID) || "error"
148
- }
149
-
150
- let footer = ""
151
- if (Flag.SAEEOL_EXPERIMENTAL_WORKSPACES) {
152
- if (x.workspaceID) {
153
- let desc = "unknown"
154
- if (workspace) {
155
- desc = `${workspace.type}: ${workspace.name}`
156
- }
157
-
158
- footer = (
159
- <>
160
- {desc}{" "}
161
- <span
162
- style={{
163
- fg: workspaceStatus === "connected" ? theme.success : theme.error,
164
- }}
165
- >
166
-
167
- </span>
168
- </>
169
- )
170
- }
171
- } else {
172
- footer = Locale.time(x.time.updated)
173
- }
174
-
175
- const date = new Date(x.time.updated)
176
- let category = date.toDateString()
177
- if (category === today) {
178
- category = "Today"
179
- }
180
- const isDeleting = toDelete() === x.id
181
- const status = sync.data.session_status?.[x.id]
182
- const isWorking = status?.type === "busy"
183
- return {
184
- title: isDeleting ? `Press ${keybind.print("session_delete")} again to confirm` : x.title,
185
- description: all && x.worktreeName ? `(${x.worktreeName})` : undefined,
186
- bg: isDeleting ? theme.error : undefined,
187
- value: x.id,
188
- category,
189
- footer,
190
- gutter: isWorking ? () => <Spinner /> : undefined,
191
- }
192
- })
193
- })
194
-
195
- onMount(() => {
196
- dialog.setSize("large")
197
- })
198
-
199
- return (
200
- <DialogSelect
201
- title={global() ? "Sessions (all worktrees)" : "Sessions (current worktree)"}
202
- options={options()}
203
- skipFilter={true}
204
- current={currentSessionID()}
205
- onFilter={setSearch}
206
- onMove={() => {
207
- setToDelete(undefined)
208
- }}
209
- onSelect={(option) => {
210
- route.navigate({
211
- type: "session",
212
- sessionID: option.value,
213
- })
214
- dialog.clear()
215
- }}
216
- keybind={[
217
- {
218
- keybind: keybind.all.session_delete?.[0],
219
- title: "delete",
220
- onTrigger: async (option) => {
221
- if (toDelete() === option.value) {
222
- const session = sessions().find((item) => item.id === option.value)
223
- const status = session?.workspaceID ? project.workspace.status(session.workspaceID) : undefined
224
-
225
- try {
226
- const result = await sdk.client.session.delete({
227
- sessionID: option.value,
228
- })
229
- if (result.error) {
230
- if (session?.workspaceID) {
231
- recover(session)
232
- } else {
233
- toast.show({
234
- variant: "error",
235
- title: "Failed to delete session",
236
- message: errorMessage(result.error),
237
- })
238
- }
239
- setToDelete(undefined)
240
- return
241
- }
242
- } catch (err) {
243
- if (session?.workspaceID) {
244
- recover(session)
245
- } else {
246
- toast.show({
247
- variant: "error",
248
- title: "Failed to delete session",
249
- message: errorMessage(err),
250
- })
251
- }
252
- setToDelete(undefined)
253
- return
254
- }
255
- if (status && status !== "connected") {
256
- await sync.session.refresh()
257
- }
258
- void searchActions.refetch()
259
- setToDelete(undefined)
260
- return
261
- }
262
- setToDelete(option.value)
263
- },
264
- },
265
- {
266
- keybind: keybind.all.session_rename?.[0],
267
- title: "rename",
268
- onTrigger: async (option) => {
269
- const item = sessions().find((x) => x.id === option.value)
270
- dialog.replace(() => (
271
- <DialogSessionRename
272
- session={option.value}
273
- title={item?.title}
274
- onConfirm={() => {
275
- void searchActions.refetch()
276
- }}
277
- />
278
- ))
279
- },
280
- },
281
- {
282
- keybind: { name: "a", ctrl: true, meta: false, shift: false, leader: false },
283
- title: global() ? "current" : "all",
284
- onTrigger: async () => {
285
- setToDelete(undefined)
286
- setGlobal((v) => !v)
287
- },
288
- },
289
- {
290
- keybind: Keybind.parse("ctrl+w")[0],
291
- title: "new workspace",
292
- side: "right",
293
- disabled: !Flag.SAEEOL_EXPERIMENTAL_WORKSPACES,
294
- onTrigger: () => {
295
- createWorkspace()
296
- },
297
- },
298
- ]}
299
- />
300
- )
301
- }
1
+ export * from "./dialog/dialog-session-list"
@@ -1,35 +1 @@
1
- import { DialogPrompt } from "@tui/ui/dialog-prompt"
2
- import { useDialog } from "@tui/ui/dialog"
3
- import { useSync } from "@tui/context/sync"
4
- import { createMemo } from "solid-js"
5
- import { useSDK } from "../context/sdk"
6
-
7
- interface DialogSessionRenameProps {
8
- session: string
9
- title?: string
10
- onConfirm?: () => void
11
- }
12
-
13
- export function DialogSessionRename(props: DialogSessionRenameProps) {
14
- const dialog = useDialog()
15
- const sync = useSync()
16
- const sdk = useSDK()
17
- const session = createMemo(() => sync.session.get(props.session))
18
-
19
- return (
20
- <DialogPrompt
21
- title="Rename Session"
22
- value={session()?.title ?? props.title}
23
- onConfirm={(value) => {
24
- void sdk.client.session
25
- .update({
26
- sessionID: props.session,
27
- title: value,
28
- })
29
- .then(() => props.onConfirm?.())
30
- dialog.clear()
31
- }}
32
- onCancel={() => dialog.clear()}
33
- />
34
- )
35
- }
1
+ export * from "./dialog/dialog-session-rename"
@@ -1,37 +1 @@
1
- import { DialogSelect, type DialogSelectOption } from "@tui/ui/dialog-select"
2
- import { createResource, createMemo } from "solid-js"
3
- import { useDialog } from "@tui/ui/dialog"
4
- import { useSDK } from "@tui/context/sdk"
5
- import { t } from "@/util/i18n"
6
-
7
- export type DialogSkillProps = {
8
- onSelect: (skill: string) => void
9
- }
10
-
11
- export function DialogSkill(props: DialogSkillProps) {
12
- const dialog = useDialog()
13
- const sdk = useSDK()
14
- dialog.setSize("large")
15
-
16
- const [skills] = createResource(async () => {
17
- const result = await sdk.client.app.skills()
18
- return result.data ?? []
19
- })
20
-
21
- const options = createMemo<DialogSelectOption<string>[]>(() => {
22
- const list = skills() ?? []
23
- const maxWidth = Math.max(0, ...list.map((s) => s.name.length))
24
- return list.map((skill) => ({
25
- title: skill.name.padEnd(maxWidth),
26
- description: skill.description?.replace(/\s+/g, " ").trim(),
27
- value: skill.name,
28
- category: t("cmd.skill.title"),
29
- onSelect: () => {
30
- props.onSelect(skill.name)
31
- dialog.clear()
32
- },
33
- }))
34
- })
35
-
36
- return <DialogSelect title={t("cmd.skill.title")} placeholder={t("cmd.skill.search")} options={options()} />
37
- }
1
+ export * from "./dialog/dialog-skill"
@@ -1,87 +1 @@
1
- import { useDialog } from "@tui/ui/dialog"
2
- import { DialogSelect } from "@tui/ui/dialog-select"
3
- import { createMemo, createSignal } from "solid-js"
4
- import { Locale } from "@/util/locale"
5
- import { useTheme } from "../context/theme"
6
- import { useKeybind } from "../context/keybind"
7
- import { usePromptStash, type StashEntry } from "./prompt/stash"
8
-
9
- function getRelativeTime(timestamp: number): string {
10
- const now = Date.now()
11
- const diff = now - timestamp
12
- const seconds = Math.floor(diff / 1000)
13
- const minutes = Math.floor(seconds / 60)
14
- const hours = Math.floor(minutes / 60)
15
- const days = Math.floor(hours / 24)
16
-
17
- if (seconds < 60) return "just now"
18
- if (minutes < 60) return `${minutes}m ago`
19
- if (hours < 24) return `${hours}h ago`
20
- if (days < 7) return `${days}d ago`
21
- return Locale.datetime(timestamp)
22
- }
23
-
24
- function getStashPreview(input: string, maxLength: number = 50): string {
25
- const firstLine = input.split("\n")[0].trim()
26
- return Locale.truncate(firstLine, maxLength)
27
- }
28
-
29
- export function DialogStash(props: { onSelect: (entry: StashEntry) => void }) {
30
- const dialog = useDialog()
31
- const stash = usePromptStash()
32
- const { theme } = useTheme()
33
- const keybind = useKeybind()
34
-
35
- const [toDelete, setToDelete] = createSignal<number>()
36
-
37
- const options = createMemo(() => {
38
- const entries = stash.list()
39
- // Show most recent first
40
- return entries
41
- .map((entry, index) => {
42
- const isDeleting = toDelete() === index
43
- const lineCount = (entry.input.match(/\n/g)?.length ?? 0) + 1
44
- return {
45
- title: isDeleting ? `Press ${keybind.print("stash_delete")} again to confirm` : getStashPreview(entry.input),
46
- bg: isDeleting ? theme.error : undefined,
47
- value: index,
48
- description: getRelativeTime(entry.timestamp),
49
- footer: lineCount > 1 ? `~${lineCount} lines` : undefined,
50
- }
51
- })
52
- .toReversed()
53
- })
54
-
55
- return (
56
- <DialogSelect
57
- title="Stash"
58
- options={options()}
59
- onMove={() => {
60
- setToDelete(undefined)
61
- }}
62
- onSelect={(option) => {
63
- const entries = stash.list()
64
- const entry = entries[option.value]
65
- if (entry) {
66
- stash.remove(option.value)
67
- props.onSelect(entry)
68
- }
69
- dialog.clear()
70
- }}
71
- keybind={[
72
- {
73
- keybind: keybind.all.stash_delete?.[0],
74
- title: "delete",
75
- onTrigger: (option) => {
76
- if (toDelete() === option.value) {
77
- stash.remove(option.value)
78
- setToDelete(undefined)
79
- return
80
- }
81
- setToDelete(option.value)
82
- },
83
- },
84
- ]}
85
- />
86
- )
87
- }
1
+ export * from "./dialog/dialog-stash"
@@ -1,190 +1 @@
1
- import { TextAttributes } from "@opentui/core"
2
- import { fileURLToPath } from "bun"
3
- import { useTheme } from "../context/theme"
4
- import { useDialog } from "@tui/ui/dialog"
5
- import { useSync } from "@tui/context/sync"
6
- import { useProject } from "@tui/context/project"
7
- import { For, Match, Switch, Show, createMemo } from "solid-js"
8
- import { InstallationVersion } from "@saeeol/core/installation/version"
9
- import { Global } from "@saeeol/core/global"
10
-
11
- export type DialogStatusProps = {}
12
-
13
- export function DialogStatus() {
14
- const sync = useSync()
15
- const project = useProject()
16
- const { theme } = useTheme()
17
- const dialog = useDialog()
18
-
19
- const enabledFormatters = createMemo(() => sync.data.formatter.filter((f) => f.enabled))
20
-
21
- const plugins = createMemo(() => {
22
- const list = sync.data.config.plugin ?? []
23
- const result = list.map((item) => {
24
- const value = typeof item === "string" ? item : item[0]
25
- if (value.startsWith("file://")) {
26
- const path = fileURLToPath(value)
27
- const parts = path.split(/[/\\]/)
28
- const filename = parts.pop() || path
29
- if (!filename.includes(".")) return { name: filename }
30
- const basename = filename.split(".")[0]
31
- if (basename === "index") {
32
- const dirname = parts.pop()
33
- const name = dirname || basename
34
- return { name }
35
- }
36
- return { name: basename }
37
- }
38
- const index = value.lastIndexOf("@")
39
- if (index <= 0) return { name: value, version: "latest" }
40
- const name = value.substring(0, index)
41
- const version = value.substring(index + 1)
42
- return { name, version }
43
- })
44
- return result.toSorted((a, b) => a.name.localeCompare(b.name))
45
- })
46
-
47
- return (
48
- <box paddingLeft={2} paddingRight={2} gap={1} paddingBottom={1}>
49
- <box flexDirection="row" justifyContent="space-between">
50
- <text fg={theme.text} attributes={TextAttributes.BOLD}>
51
- Status
52
- </text>
53
- <text fg={theme.textMuted} onMouseUp={() => dialog.clear()}>
54
- esc
55
- </text>
56
- </box>
57
-
58
- <text fg={theme.textMuted}>Saeeol v{InstallationVersion}</text>
59
-
60
-
61
- <box>
62
- <text fg={theme.text}>Paths</text>
63
- <text fg={theme.textMuted}>
64
- Global config {" "}
65
- {Global.Path.config.replace(Global.Path.home, "~")}
66
- </text>
67
- <Show when={project.instance.path().directory}>
68
- <text fg={theme.textMuted}>
69
- Project {" "}
70
- {project.instance.path().directory.replace(Global.Path.home, "~")}
71
- </text>
72
- </Show>
73
- </box>
74
-
75
- <Show when={Object.keys(sync.data.mcp).length > 0} fallback={<text fg={theme.text}>No MCP Servers</text>}>
76
- <box>
77
- <text fg={theme.text}>{Object.keys(sync.data.mcp).length} MCP Servers</text>
78
- <For each={Object.entries(sync.data.mcp)}>
79
- {([key, item]) => (
80
- <box flexDirection="row" gap={1}>
81
- <text
82
- flexShrink={0}
83
- style={{
84
- fg: (
85
- {
86
- connected: theme.success,
87
- failed: theme.error,
88
- disabled: theme.textMuted,
89
- needs_auth: theme.warning,
90
- needs_client_registration: theme.error,
91
- } as Record<string, typeof theme.success>
92
- )[item.status],
93
- }}
94
- >
95
-
96
- </text>
97
- <text fg={theme.text} wrapMode="word">
98
- <b>{key}</b>{" "}
99
- <span style={{ fg: theme.textMuted }}>
100
- <Switch fallback={item.status}>
101
- <Match when={item.status === "connected"}>Connected</Match>
102
- <Match when={item.status === "failed" && item}>{(val) => val().error}</Match>
103
- <Match when={item.status === "disabled"}>Disabled in configuration</Match>
104
- <Match when={(item.status as string) === "needs_auth"}>
105
- Needs authentication (run: saeeol mcp auth {key})
106
- </Match>
107
- <Match when={(item.status as string) === "needs_client_registration" && item}>
108
- {(val) => (val() as { error: string }).error}
109
- </Match>
110
- </Switch>
111
- </span>
112
- </text>
113
- </box>
114
- )}
115
- </For>
116
- </box>
117
- </Show>
118
- {sync.data.lsp.length > 0 && (
119
- <box>
120
- <text fg={theme.text}>{sync.data.lsp.length} LSP Servers</text>
121
- <For each={sync.data.lsp}>
122
- {(item) => (
123
- <box flexDirection="row" gap={1}>
124
- <text
125
- flexShrink={0}
126
- style={{
127
- fg: {
128
- connected: theme.success,
129
- error: theme.error,
130
- }[item.status],
131
- }}
132
- >
133
-
134
- </text>
135
- <text fg={theme.text} wrapMode="word">
136
- <b>{item.id}</b> <span style={{ fg: theme.textMuted }}>{item.root}</span>
137
- </text>
138
- </box>
139
- )}
140
- </For>
141
- </box>
142
- )}
143
- <Show when={enabledFormatters().length > 0} fallback={<text fg={theme.text}>No Formatters</text>}>
144
- <box>
145
- <text fg={theme.text}>{enabledFormatters().length} Formatters</text>
146
- <For each={enabledFormatters()}>
147
- {(item) => (
148
- <box flexDirection="row" gap={1}>
149
- <text
150
- flexShrink={0}
151
- style={{
152
- fg: theme.success,
153
- }}
154
- >
155
-
156
- </text>
157
- <text wrapMode="word" fg={theme.text}>
158
- <b>{item.name}</b>
159
- </text>
160
- </box>
161
- )}
162
- </For>
163
- </box>
164
- </Show>
165
- <Show when={plugins().length > 0} fallback={<text fg={theme.text}>No Plugins</text>}>
166
- <box>
167
- <text fg={theme.text}>{plugins().length} Plugins</text>
168
- <For each={plugins()}>
169
- {(item) => (
170
- <box flexDirection="row" gap={1}>
171
- <text
172
- flexShrink={0}
173
- style={{
174
- fg: theme.success,
175
- }}
176
- >
177
-
178
- </text>
179
- <text wrapMode="word" fg={theme.text}>
180
- <b>{item.name}</b>
181
- {item.version && <span style={{ fg: theme.textMuted }}> @{item.version}</span>}
182
- </text>
183
- </box>
184
- )}
185
- </For>
186
- </box>
187
- </Show>
188
- </box>
189
- )
190
- }
1
+ export * from "./dialog/dialog-status"