Skip to content

Commit 6defe6d

Browse files
janrick123claude
andcommitted
Run recipes from a script file instead of an inline heredoc
The run recipe was fed into the interactive shell as a `bash <<'JCRUN'` heredoc, so bash echoed every line back with its `>` continuation prompt — dumping the whole ~30-line recipe into the run terminal before any output appeared. Write the recipe to <project>/.jcode/run.sh and invoke it with a single `bash <path>` line instead (the script is read, not executed, so the noexec /workspace mount is fine). The terminal now shows just the clean [1/5]..[5/5] step echoes. Falls back to the inline heredoc if the script cannot be written. Pairs with the earlier npm_config_progress=false fix; together the run terminal renders cleanly. Verified on device: no recipe dump, no progress- bar garbage, build/run still serves at localhost:5080. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 3ca80b7 commit 6defe6d

2 files changed

Lines changed: 26 additions & 8 deletions

File tree

app/src/main/java/dev/jcode/JCodeShell.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -576,7 +576,7 @@ private fun JCodeShell(
576576
sessionId = session.id
577577
runSessionId = session.id
578578
}
579-
terminalSessionManager.sendInput(sessionId, plan.command)
579+
terminalSessionManager.sendInput(sessionId, ProjectRunner.runInvocation(project, plan) + "\n")
580580
runUrl = plan.url
581581
runInProgress = true
582582
// Cancel any in-flight poll from a previous run so the browser only opens once.

app/src/main/java/dev/jcode/run/ProjectRunner.kt

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -114,11 +114,33 @@ object ProjectRunner {
114114
runCatching { context.startActivity(intent) }
115115
}
116116

117+
/**
118+
* Prepare the terminal command that runs [plan]. The recipe body is written to the project's
119+
* `.jcode/run.sh` and invoked with `bash <path>` — a single short line — so the interactive shell
120+
* does NOT echo the whole script back with `>` continuation prompts (which it would if the recipe
121+
* were fed as an inline heredoc). The script is read, not executed, so the noexec `/workspace`
122+
* mount is fine. Falls back to an inline heredoc if the script can't be written.
123+
*/
124+
fun runInvocation(project: Project, plan: RunPlan): String {
125+
val hostDir = (project.fsPath as? FsPath.Local)?.file
126+
if (hostDir != null) {
127+
val written = runCatching {
128+
val dir = File(hostDir, ".jcode").apply { if (!exists()) mkdirs() }
129+
File(dir, "run.sh").writeText(plan.command)
130+
}.isSuccess
131+
if (written) {
132+
val guestDir = project.distroBindTarget.trimEnd('/')
133+
return "bash \"$guestDir/.jcode/run.sh\""
134+
}
135+
}
136+
return "bash <<'JCRUN'\n${plan.command}\nJCRUN"
137+
}
138+
117139
// --- command builders -------------------------------------------------
118140

119-
// Recipes run as a single `bash <<'JCRUN'` heredoc so `set -e` aborts on the first failing step
120-
// and multi-line file writes stay clean. $PROJ/$STAGE/$TFM are expanded by the guest bash at
121-
// runtime (the heredoc is quoted, so they stay literal here).
141+
// Each recipe is a self-contained bash script (run via `bash .jcode/run.sh`, see runInvocation).
142+
// `set -e` aborts on the first failing step. $PROJ/$STAGE/$TFM are expanded by the guest bash at
143+
// run time.
122144

123145
/** Pick the newest installed .NET SDK's major version → e.g. `net10.0`, falling back to net8.0. */
124146
private const val SELECT_TFM =
@@ -128,7 +150,6 @@ object ProjectRunner {
128150
private fun aspnetViteCommand(projectDir: String, stageName: String, port: Int): String =
129151
buildString {
130152
appendLine("clear")
131-
appendLine("bash <<'JCRUN'")
132153
appendLine("set -e")
133154
// npm's animated progress bar (redrawn with carriage returns) renders as a wall of garbage
134155
// on the VT terminal; disable it and the fund/audit chatter for clean line-by-line output.
@@ -187,13 +208,11 @@ object ProjectRunner {
187208
appendLine("dotnet publish \"\$CSPROJ\" -c Release -o \"\$SRV\" --nologo")
188209
appendLine("cd \"\$SRV\"")
189210
appendLine("ASPNETCORE_URLS='http://0.0.0.0:$port' dotnet \"\$(basename \"\$CSPROJ\" .csproj).dll\"")
190-
appendLine("JCRUN")
191211
}
192212

193213
private fun viteDevCommand(projectDir: String, stageName: String, port: Int): String =
194214
buildString {
195215
appendLine("clear")
196-
appendLine("bash <<'JCRUN'")
197216
appendLine("set -e")
198217
// Disable npm's animated progress bar (renders as garbage on the VT terminal) + chatter.
199218
appendLine("export npm_config_progress=false npm_config_fund=false npm_config_audit=false")
@@ -216,7 +235,6 @@ object ProjectRunner {
216235
appendLine("npm install")
217236
appendLine("echo '[3/3] Starting Vite dev server on http://localhost:$port ...'")
218237
appendLine("npm run dev -- --host 0.0.0.0 --port $port")
219-
appendLine("JCRUN")
220238
}
221239

222240
private fun sanitizeStageName(name: String): String {

0 commit comments

Comments
 (0)