@miphamai/cli 0.5.7 → 0.5.9
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/bin/mipham.ts +95 -0
- package/package.json +1 -1
- package/src/config/loader.ts +178 -14
- package/src/plugin/plugin-manager.ts +77 -0
- package/src/providers/anthropic.ts +12 -2
- package/src/providers/bootstrap.ts +11 -1
- package/src/providers/openai-compat.ts +17 -4
- package/src/shared/constants.ts +45 -0
- package/src/shared/update.ts +158 -0
- package/src/skills/loader.ts +13 -0
- package/src/skills/registry.ts +325 -0
- package/src/ui/app.tsx +1 -1
- package/src/ui/chat.tsx +11 -1
- package/src/ui/command-picker.tsx +145 -0
- package/src/ui/commands.ts +788 -164
- package/src/ui/input.tsx +95 -26
- package/dist/mipham +0 -0
package/src/ui/input.tsx
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
import React, { useState, useEffect, useRef } from 'react'
|
|
1
|
+
import React, { useState, useEffect, useRef, useMemo } from 'react'
|
|
2
2
|
import { Box, Text, useInput } from 'ink'
|
|
3
3
|
import TextInput from 'ink-text-input'
|
|
4
4
|
import { VimMotionEngine, type VimMode } from './vim-motions.js'
|
|
5
|
+
import { getCommandList, type CommandEntry } from './commands.js'
|
|
6
|
+
import { CommandPicker } from './command-picker.js'
|
|
5
7
|
|
|
6
8
|
interface InputBarProps {
|
|
7
9
|
onSubmit: (input: string) => void
|
|
@@ -111,6 +113,15 @@ export function InputBar({ onSubmit, isLoading }: InputBarProps) {
|
|
|
111
113
|
const [completionVerb, setCompletionVerb] = useState<string | null>(null)
|
|
112
114
|
const prevLoading = useRef(isLoading)
|
|
113
115
|
|
|
116
|
+
// ── Slash command hints ──
|
|
117
|
+
const allCommands = useMemo(() => getCommandList(), [])
|
|
118
|
+
const slashHints = useMemo(() => {
|
|
119
|
+
if (!value.startsWith('/')) return []
|
|
120
|
+
const filter = value.slice(1).toLowerCase()
|
|
121
|
+
if (!filter) return allCommands.slice(0, 12) // show first 12 when just "/"
|
|
122
|
+
return allCommands.filter((c) => c.name.toLowerCase().includes(filter)).slice(0, 8)
|
|
123
|
+
}, [value, allCommands])
|
|
124
|
+
|
|
114
125
|
// Rotate gerunds while loading
|
|
115
126
|
useEffect(() => {
|
|
116
127
|
if (!isLoading) return
|
|
@@ -242,39 +253,97 @@ export function InputBar({ onSubmit, isLoading }: InputBarProps) {
|
|
|
242
253
|
// The cursor hint is informational only; the user repositions manually.
|
|
243
254
|
})
|
|
244
255
|
|
|
256
|
+
// ── Command picker state ──
|
|
257
|
+
const [pickerActive, setPickerActive] = useState(false)
|
|
258
|
+
const prevValueRef = useRef(value)
|
|
259
|
+
|
|
260
|
+
// Auto-activate picker when user types "/"
|
|
261
|
+
useEffect(() => {
|
|
262
|
+
if (value.startsWith('/') && !prevValueRef.current.startsWith('/') && vimMode === 'insert') {
|
|
263
|
+
setPickerActive(true)
|
|
264
|
+
}
|
|
265
|
+
// Dismiss picker when user clears the / prefix
|
|
266
|
+
if (!value.startsWith('/') && pickerActive) {
|
|
267
|
+
setPickerActive(false)
|
|
268
|
+
}
|
|
269
|
+
prevValueRef.current = value
|
|
270
|
+
}, [value, vimMode])
|
|
271
|
+
|
|
245
272
|
const handleSubmit = (val: string) => {
|
|
246
273
|
if (!val.trim() || isLoading) return
|
|
247
274
|
onSubmit(val)
|
|
248
275
|
setValue('')
|
|
276
|
+
setPickerActive(false)
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// ── Picker mode: CommandPicker overlay ──
|
|
280
|
+
if (pickerActive && vimMode === 'insert') {
|
|
281
|
+
return (
|
|
282
|
+
<Box flexDirection="column" marginTop={1}>
|
|
283
|
+
<CommandPicker
|
|
284
|
+
initialFilter={value}
|
|
285
|
+
onSelect={(cmdName) => {
|
|
286
|
+
// Fill the command name and submit
|
|
287
|
+
onSubmit(cmdName)
|
|
288
|
+
setValue('')
|
|
289
|
+
setPickerActive(false)
|
|
290
|
+
}}
|
|
291
|
+
onClose={() => {
|
|
292
|
+
setPickerActive(false)
|
|
293
|
+
// Keep the current typed text so user can continue
|
|
294
|
+
}}
|
|
295
|
+
/>
|
|
296
|
+
</Box>
|
|
297
|
+
)
|
|
249
298
|
}
|
|
250
299
|
|
|
251
300
|
return (
|
|
252
|
-
<Box marginTop={1}>
|
|
253
|
-
<Box
|
|
254
|
-
<
|
|
255
|
-
{vimMode === 'normal' ? ':' : '
|
|
256
|
-
|
|
301
|
+
<Box flexDirection="column" marginTop={1}>
|
|
302
|
+
<Box>
|
|
303
|
+
<Box marginRight={1}>
|
|
304
|
+
<Text color={vimMode === 'normal' ? 'magenta' : isLoading ? 'yellow' : 'cyan'}>
|
|
305
|
+
{vimMode === 'normal' ? ':' : '>'}
|
|
306
|
+
</Text>
|
|
307
|
+
</Box>
|
|
308
|
+
<TextInput
|
|
309
|
+
value={value}
|
|
310
|
+
onChange={(val) => {
|
|
311
|
+
// Block text changes during search mode — keys go to search query
|
|
312
|
+
if (searchMode) return
|
|
313
|
+
setValue(val)
|
|
314
|
+
}}
|
|
315
|
+
onSubmit={handleSubmit}
|
|
316
|
+
placeholder={
|
|
317
|
+
searchMode
|
|
318
|
+
? `/${searchQuery}`
|
|
319
|
+
: vimMode === 'normal'
|
|
320
|
+
? '[NORMAL] h/j/k/l w/b 0/$ dd yy p u / (Esc to insert)'
|
|
321
|
+
: isLoading
|
|
322
|
+
? `${verb}...`
|
|
323
|
+
: completionVerb
|
|
324
|
+
? completionVerb
|
|
325
|
+
: 'Type a message (Esc to cancel)...'
|
|
326
|
+
}
|
|
327
|
+
/>
|
|
257
328
|
</Box>
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
? '
|
|
271
|
-
:
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
}
|
|
277
|
-
/>
|
|
329
|
+
{/* Slash command hints — shown when typing / in INSERT mode (only when picker is NOT active) */}
|
|
330
|
+
{slashHints.length > 0 && vimMode === 'insert' && !pickerActive && (
|
|
331
|
+
<Box marginTop={1} flexDirection="column" gap={1}>
|
|
332
|
+
<Text dimColor>Commands: </Text>
|
|
333
|
+
{slashHints.map((cmd, i) => (
|
|
334
|
+
<Text key={cmd.name} color="cyan">
|
|
335
|
+
{cmd.name}
|
|
336
|
+
</Text>
|
|
337
|
+
))}
|
|
338
|
+
<Text dimColor>
|
|
339
|
+
(
|
|
340
|
+
{slashHints.length === allCommands.length
|
|
341
|
+
? 'all'
|
|
342
|
+
: `${slashHints.length} of ${allCommands.length}`}
|
|
343
|
+
)
|
|
344
|
+
</Text>
|
|
345
|
+
</Box>
|
|
346
|
+
)}
|
|
278
347
|
</Box>
|
|
279
348
|
)
|
|
280
349
|
}
|
package/dist/mipham
DELETED
|
Binary file
|