@@ -35,6 +35,7 @@ import { resolveCurrentSettings, writeModelConfigSelection } from "@vegamo/deepc
3535import { useStatusLine } from "../hooks" ;
3636import type { SessionInfo } from "../statusline" ;
3737import { isCollapsedThinking } from "../core/thinking-state" ;
38+ import { BUILTIN_SLASH_COMMANDS , findExactSlashCommand } from "../core/slash-commands" ;
3839import { ANSI_CLEAR_SCREEN } from "../constants" ;
3940import type {
4041 LlmStreamProgress ,
@@ -321,6 +322,78 @@ function App({ projectRoot, initialPrompt, resumeSessionId, onRestart }: AppProp
321322 [ exit , sessionManager ]
322323 ) ;
323324
325+ const handleUsage = useCallback ( async ( ) => {
326+ const settings = resolveCurrentSettings ( projectRoot ) ;
327+ const baseURL = settings . baseURL . toLowerCase ( ) ;
328+ if ( ! baseURL . includes ( "api.deepseek.com" ) ) {
329+ setErrorLine ( `/usage is only compatible with DeepSeek API. Current base URL: ${ settings . baseURL } ` ) ;
330+ return ;
331+ }
332+ if ( ! settings . apiKey ) {
333+ setErrorLine ( "No API key configured. Set DEEPCODE_API_KEY env var or apiKey in settings.json." ) ;
334+ return ;
335+ }
336+
337+ setBusy ( true ) ;
338+ setErrorLine ( null ) ;
339+ try {
340+ const response = await fetch ( "https://api.deepseek.com/user/balance" , {
341+ headers : { Authorization : `Bearer ${ settings . apiKey } ` } ,
342+ } ) ;
343+ if ( ! response . ok ) {
344+ setErrorLine ( `Failed to fetch balance: HTTP ${ response . status } ${ response . statusText || "" } ` . trim ( ) ) ;
345+ return ;
346+ }
347+ const data = ( await response . json ( ) ) as {
348+ is_available : boolean ;
349+ balance_infos ?: Array < {
350+ currency : string ;
351+ total_balance : string ;
352+ granted_balance : string ;
353+ topped_up_balance : string ;
354+ } > ;
355+ } ;
356+
357+ const statusIcon = data . is_available ? "🟢" : "🔴" ;
358+ const statusText = data . is_available ? "Available" : "Not available" ;
359+ let content = `/usage\n└ API status: ${ statusIcon } ${ statusText } ` ;
360+
361+ if ( data . balance_infos && data . balance_infos . length > 0 ) {
362+ for ( const info of data . balance_infos ) {
363+ content += `\n ${ info . currency } : total ${ info . total_balance } (granted ${ info . granted_balance } , topped up ${ info . topped_up_balance } )` ;
364+ }
365+ } else {
366+ content += `\n No balance info returned.` ;
367+ }
368+
369+ const now = new Date ( ) . toISOString ( ) ;
370+ const activeSessionId = sessionManager . getActiveSessionId ( ) ;
371+ if ( activeSessionId ) {
372+ sessionManager . addSessionSystemMessage ( activeSessionId , content , true ) ;
373+ } else {
374+ setMessages ( ( prev ) => [
375+ ...prev ,
376+ {
377+ id : crypto . randomUUID ( ) ,
378+ sessionId : "local" ,
379+ role : "system" as const ,
380+ content,
381+ contentParams : null ,
382+ messageParams : null ,
383+ compacted : false ,
384+ visible : true ,
385+ createTime : now ,
386+ updateTime : now ,
387+ } ,
388+ ] ) ;
389+ }
390+ } catch ( error ) {
391+ setErrorLine ( `Failed to fetch balance: ${ error instanceof Error ? error . message : String ( error ) } ` ) ;
392+ } finally {
393+ setBusy ( false ) ;
394+ }
395+ } , [ projectRoot , sessionManager ] ) ;
396+
324397 const handlePrompt = useCallback (
325398 async ( submission : PromptSubmission ) => {
326399 if ( submission . command === "exit" ) {
@@ -361,6 +434,10 @@ function App({ projectRoot, initialPrompt, resumeSessionId, onRestart }: AppProp
361434 navigateToSubView ( "mcp-status" ) ;
362435 return ;
363436 }
437+ if ( submission . command === "usage" ) {
438+ void handleUsage ( ) ;
439+ return ;
440+ }
364441
365442 const prompt : UserPromptContent = {
366443 text : submission . text ,
@@ -420,6 +497,7 @@ function App({ projectRoot, initialPrompt, resumeSessionId, onRestart }: AppProp
420497 sessionManager ,
421498 pendingPermissionReply ,
422499 handleExit ,
500+ handleUsage ,
423501 onRestart ,
424502 refreshSkills ,
425503 refreshSessionsList ,
@@ -551,10 +629,15 @@ function App({ projectRoot, initialPrompt, resumeSessionId, onRestart }: AppProp
551629 // Step 2: Submit prompt if provided
552630 if ( initialPrompt && initialPrompt . trim ( ) ) {
553631 initialPromptSubmittedRef . current = true ;
632+ const trimmed = initialPrompt . trim ( ) ;
633+ const slashToken = trimmed . split ( / \s + / , 1 ) [ 0 ] ;
634+ const match = findExactSlashCommand ( BUILTIN_SLASH_COMMANDS , slashToken ) ;
635+ const command = match && match . kind !== "skill" ? ( match . kind as PromptSubmission [ "command" ] ) : undefined ;
554636 handleSubmit ( {
555- text : initialPrompt ,
637+ text : trimmed ,
556638 imageUrls : [ ] ,
557639 selectedSkills : undefined ,
640+ command,
558641 } ) ;
559642 }
560643 }
0 commit comments