11import React from "react" ;
22import { render } from "ink" ;
3- import { setShellIfWindows } from "@vegamo/deepcode-core" ;
4- import { checkForNpmUpdate , promptForPendingUpdate , type PackageInfo } from "./common/update-check" ;
3+ import { readFileSync } from "node:fs" ;
4+ import { join } from "node:path" ;
5+ import { homedir } from "node:os" ;
6+ import { setShellIfWindows , getProjectCode } from "@vegamo/deepcode-core" ;
7+ import { checkForNpmUpdate , promptForPendingUpdate } from "./common/update-check" ;
58import { AppContainer } from "./ui" ;
9+ import { parseArguments } from "./cli-args" ;
10+ import { writeStderrLine , writeStdoutLine } from "./utils/stdioHelpers" ;
11+ import { getPackageJson } from "./utils/package" ;
12+ import { CLI_VERSION } from "./generated/git-commit" ;
613
7- const args = process . argv . slice ( 2 ) ;
8- const packageInfo = readPackageInfo ( ) ;
9-
10- if ( args . includes ( "--version" ) || args . includes ( "-v" ) ) {
11- process . stdout . write ( `${ packageInfo . version || "unknown" } \n` ) ;
12- process . exit ( 0 ) ;
13- }
14+ void main ( ) ;
1415
15- if ( args . includes ( "--help" ) || args . includes ( "-h" ) ) {
16- process . stdout . write (
17- [
18- "deepcode - Deep Code CLI" ,
19- "" ,
20- "Usage:" ,
21- " deepcode Launch the interactive TUI in the current directory" ,
22- " deepcode -p <prompt> Launch with a pre-filled prompt" ,
23- " deepcode --prompt <prompt> Same as -p" ,
24- " deepcode --version Print the version" ,
25- " deepcode --help Show this help" ,
26- "" ,
27- "Configuration:" ,
28- " ~/.deepcode/settings.json User-level API key, model, base URL" ,
29- " ./.deepcode/settings.json Project-level settings" ,
30- " ./.deepcode/skills/*/SKILL.md Project-level native skills" ,
31- " ./.agents/skills/*/SKILL.md Project-level interoperable skills" ,
32- " ~/.deepcode/skills/*/SKILL.md User-level native skills" ,
33- " ~/.agents/skills/*/SKILL.md User-level interoperable skills" ,
34- "" ,
35- "Inside the TUI:" ,
36- " enter Send the prompt" ,
37- " shift+enter Insert a newline" ,
38- " home/end Move within the current line" ,
39- " alt+left/right Move by word" ,
40- " ctrl+w Delete the previous word" ,
41- " ctrl+v Paste an image from the clipboard" ,
42- " ctrl+x Clear pasted images" ,
43- " esc Interrupt the current model turn" ,
44- " / Open the skills/commands menu" ,
45- " /skills List available skills" ,
46- " /model Select model, thinking mode and effort control" ,
47- " /new Start a fresh conversation" ,
48- " /init Initialize an AGENTS.md file with instructions for LLM" ,
49- " /resume Pick a previous conversation to continue" ,
50- " /continue Continue the active conversation, or resume one if empty" ,
51- " /undo Restore code and/or conversation to a previous point" ,
52- " /mcp Show MCP server status and available tools" ,
53- " /raw Toggle display mode for viewing or collapsing reasoning content" ,
54- " /exit Quit" ,
55- " ctrl+d twice Quit" ,
56- ] . join ( "\n" ) + "\n"
57- ) ;
58- process . exit ( 0 ) ;
59- }
16+ async function main ( ) : Promise < void > {
17+ const packageInfo = await getPackageJson ( ) ;
18+ const parsed = await parseArguments ( ) ;
6019
61- function extractInitialPrompt ( args : string [ ] ) : string | undefined {
62- const promptIndex = args . findIndex ( ( arg ) => arg === "-p" || arg === "--prompt" ) ;
63- if ( promptIndex !== - 1 && promptIndex + 1 < args . length ) {
64- return args [ promptIndex + 1 ] ;
20+ // --version and --help are handled by yargs internally (prints output as side effect)
21+ // but with .exitProcess(false) we need to exit manually.
22+ if ( parsed . version || parsed . help ) {
23+ process . exit ( 0 ) ;
6524 }
66- return undefined ;
67- }
6825
69- let initialPrompt = extractInitialPrompt ( args ) ;
70- const projectRoot = process . cwd ( ) ;
71- configureWindowsShell ( ) ;
26+ // Configure Windows shell AFTER --version/--help handling.
27+ // On Windows without Git Bash, setShellIfWindows() throws and calls process.exit(1).
28+ // If called before argument parsing, --help and --version would fail on those machines.
29+ configureWindowsShell ( ) ;
7230
73- if ( ! process . stdin . isTTY ) {
74- process . stderr . write ( "deepcode requires an interactive terminal (TTY). " + "Re-run from a real terminal session.\n" ) ;
75- process . exit ( 1 ) ;
76- }
31+ let initialPrompt = parsed . prompt ;
32+ let resumeSessionId = parsed . resume ;
33+ const projectRoot = process . cwd ( ) ;
7734
78- void main ( ) ;
35+ if ( ! process . stdin . isTTY ) {
36+ writeStderrLine ( "deepcode requires an interactive terminal (TTY). Re-run from a real terminal session.\n" ) ;
37+ process . exit ( 1 ) ;
38+ }
39+
40+ // Validate --resume <sessionId> before entering TUI
41+ if ( typeof resumeSessionId === "string" ) {
42+ const projectCode = getProjectCode ( projectRoot ) ;
43+ const indexPath = join ( homedir ( ) , ".deepcode" , "projects" , projectCode , "sessions-index.json" ) ;
44+ try {
45+ const index = JSON . parse ( readFileSync ( indexPath , "utf-8" ) ) ;
46+ const found =
47+ Array . isArray ( index ?. entries ) && index . entries . some ( ( e : { id : string } ) => e . id === resumeSessionId ) ;
48+ if ( ! found ) {
49+ writeStderrLine ( `No saved session found with ID "${ resumeSessionId } ".\n` ) ;
50+ process . exit ( 1 ) ;
51+ }
52+ } catch {
53+ writeStderrLine ( `No saved session found with ID "${ resumeSessionId } ".\n` ) ;
54+ process . exit ( 1 ) ;
55+ }
56+ }
7957
80- async function main ( ) : Promise < void > {
8158 const updatePromptResult = await promptForPendingUpdate ( packageInfo ) ;
8259 if ( updatePromptResult . installed ) {
8360 process . exit ( 0 ) ;
@@ -89,19 +66,22 @@ async function main(): Promise<void> {
8966 let restarting = false ;
9067 const appInitialPrompt = initialPrompt ;
9168 initialPrompt = undefined ;
69+ const appResumeSessionId = resumeSessionId ;
70+ resumeSessionId = undefined ;
9271 const inkInstance = render (
9372 < AppContainer
9473 projectRoot = { projectRoot }
95- version = { packageInfo . version }
74+ version = { packageInfo ? .version ?? CLI_VERSION }
9675 initialPrompt = { appInitialPrompt }
76+ resumeSessionId = { appResumeSessionId }
9777 onRestart = { ( ) => restartRef . current ?.( ) }
9878 /> ,
9979 { exitOnCtrlC : false }
10080 ) ;
10181
10282 restartRef . current = ( ) => {
10383 restarting = true ;
104- process . stdout . write ( "\u001B[2J\u001B[3J\u001B[H" ) ;
84+ writeStdoutLine ( "\u001B[2J\u001B[3J\u001B[H" ) ;
10585 inkInstance . unmount ( ) ;
10686 startApp ( ) ;
10787 } ;
@@ -119,25 +99,19 @@ async function main(): Promise<void> {
11999 startApp ( ) ;
120100}
121101
102+ /**
103+ * Configure shell environment for Windows.
104+ * Sets NoDefaultCurrentDirectoryInExePath and resolves Git Bash path.
105+ * Must be called after --version/--help handling to avoid blocking those
106+ * commands on Windows machines without Git Bash installed.
107+ */
122108function configureWindowsShell ( ) : void {
123109 process . env . NoDefaultCurrentDirectoryInExePath = "1" ;
124110 try {
125111 setShellIfWindows ( ) ;
126112 } catch ( error ) {
127113 const message = error instanceof Error ? error . message : String ( error ) ;
128- process . stderr . write ( `deepcode: ${ message } \n` ) ;
114+ writeStderrLine ( `deepcode: ${ message } \n` ) ;
129115 process . exit ( 1 ) ;
130116 }
131117}
132-
133- function readPackageInfo ( ) : PackageInfo {
134- try {
135- const pkg = require ( "../package.json" ) as { name ?: unknown ; version ?: unknown } ;
136- return {
137- name : typeof pkg . name === "string" ? pkg . name : "@vegamo/deepcode-cli" ,
138- version : typeof pkg . version === "string" ? pkg . version : "" ,
139- } ;
140- } catch {
141- return { name : "@vegamo/deepcode-cli" , version : "" } ;
142- }
143- }
0 commit comments