feat: unified cross-platform dev/prod launcher with automatic venv setup - #1401
feat: unified cross-platform dev/prod launcher with automatic venv setup#1401kaihere14 wants to merge 3 commits into
Conversation
WalkthroughAdds cross-platform JavaScript and shell launchers that validate prerequisites, prepare Python virtual environments, install service dependencies, and run PictoPy’s backend, sync-microservice, and Tauri frontend in development or production mode. ChangesUnified service launchers
Estimated code review effort: 4 (Complex) | ~45 minutes Sequence Diagram(s)sequenceDiagram
participant Launcher
participant Backend
participant SyncMicroservice
participant Frontend
Launcher->>Backend: prepare venv and install requirements
Launcher->>SyncMicroservice: prepare venv and install requirements
Launcher->>Backend: start uvicorn on port 52123
Launcher->>SyncMicroservice: start uvicorn or fastapi dev on port 52124
Launcher->>Frontend: run npm run tauri dev
Launcher->>Backend: terminate on shutdown
Launcher->>SyncMicroservice: terminate on shutdown
Launcher->>Frontend: terminate on shutdown
Suggested labels: Poem
🚥 Pre-merge checks | ✅ 3 | ❌ 1❌ Failed checks (1 inconclusive)
✅ Passed checks (3 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@scripts/run.js`:
- Around line 244-331: Refactor the service startup flow so BACKEND, SYNC, and
FRONTEND setup and launch begin concurrently, matching the backgrounded subshell
behavior in run.sh. Replace blocking setup calls such as createVenv and
pipInstall with asynchronous equivalents, update startBackend, startSync, and
startFrontend to await their work, and start all three together from the
top-level entrypoint while preserving each service’s existing setup,
environment, and failure behavior.
- Around line 201-213: Update cleanup() to wait for all child processes to emit
their exit events after killChild(child) before terminating the launcher.
Preserve the shuttingDown guard and existing shutdown messaging, and only call
process.exit(0) once every child has finished or the established child-wait
mechanism completes.
In `@scripts/run.sh`:
- Around line 155-217: Update cleanup() to tolerate already-exited PIDs by
guarding each kill and ensure wait still runs for all tracked processes. In
start_backend(), start_sync(), and start_frontend(), replace the background
pipeline with process substitution so $! records the service subshell PID while
preserving prefixed sed output, allowing cleanup to terminate the actual
services.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 57b6ce10-2b9a-433a-a1f6-1640b0f9d3e9
📒 Files selected for processing (3)
backend/scripts/reset_database.pyscripts/run.jsscripts/run.sh
| function cleanup() { | ||
| if (shuttingDown) return; | ||
| shuttingDown = true; | ||
| console.log(''); | ||
| console.log('Shutting down all services...'); | ||
| for (const child of children) { | ||
| killChild(child); | ||
| } | ||
| process.exit(0); | ||
| } | ||
| process.on('SIGINT', cleanup); | ||
| process.on('SIGTERM', cleanup); | ||
|
|
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
cleanup() exits before children actually terminate.
Unlike scripts/run.sh's cleanup(), which calls wait after kill so backgrounded services finish shutting down before the script exits, this cleanup() calls process.exit(0) immediately after issuing SIGTERM/taskkill to each child, without waiting for their exit events. Signals are delivered asynchronously, so the launcher can return control to the terminal while backend/sync/frontend are still mid-shutdown (e.g., uvicorn's reload subprocess), producing interleaved or orphaned output after the prompt returns.
♻️ Suggested fix: wait for children before exiting
function cleanup() {
if (shuttingDown) return;
shuttingDown = true;
console.log('');
console.log('Shutting down all services...');
- for (const child of children) {
- killChild(child);
- }
- process.exit(0);
+ const exits = children.map(
+ (child) => new Promise((resolve) => child.once('exit', resolve)),
+ );
+ for (const child of children) {
+ killChild(child);
+ }
+ Promise.all(exits).then(() => process.exit(0));
+ setTimeout(() => process.exit(0), 5000); // safety timeout
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| function cleanup() { | |
| if (shuttingDown) return; | |
| shuttingDown = true; | |
| console.log(''); | |
| console.log('Shutting down all services...'); | |
| for (const child of children) { | |
| killChild(child); | |
| } | |
| process.exit(0); | |
| } | |
| process.on('SIGINT', cleanup); | |
| process.on('SIGTERM', cleanup); | |
| function cleanup() { | |
| if (shuttingDown) return; | |
| shuttingDown = true; | |
| console.log(''); | |
| console.log('Shutting down all services...'); | |
| const exits = children.map( | |
| (child) => new Promise((resolve) => child.once('exit', resolve)), | |
| ); | |
| for (const child of children) { | |
| killChild(child); | |
| } | |
| Promise.all(exits).then(() => process.exit(0)); | |
| setTimeout(() => process.exit(0), 5000); // safety timeout | |
| } | |
| process.on('SIGINT', cleanup); | |
| process.on('SIGTERM', cleanup); |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@scripts/run.js` around lines 201 - 213, Update cleanup() to wait for all
child processes to emit their exit events after killChild(child) before
terminating the launcher. Preserve the shuttingDown guard and existing shutdown
messaging, and only call process.exit(0) once every child has finished or the
established child-wait mechanism completes.
| PIDS=() | ||
|
|
||
| cleanup() { | ||
| echo "" | ||
| echo "Shutting down all services..." | ||
| for pid in "${PIDS[@]}"; do | ||
| kill "$pid" 2>/dev/null | ||
| done | ||
| wait 2>/dev/null | ||
| exit 0 | ||
| } | ||
| trap cleanup INT TERM | ||
|
|
||
| start_backend() { | ||
| ( | ||
| echo "[BACKEND] Starting... ${BACKEND_DIR}" | ||
| cd "$BACKEND_DIR" | ||
| bin_dir=$(resolve_venv "BACKEND" "$BACKEND_DIR" ".env" "venv") || exit 1 | ||
| export PATH="$bin_dir:$PATH" | ||
| pip_install "BACKEND" || exit 1 | ||
| if [[ "$MODE" == "prod" ]]; then | ||
| echo "[BACKEND] Starting in production mode on port 52123..." | ||
| python -m uvicorn main:app --host 0.0.0.0 --port 52123 --workers "${WORKERS:-1}" | ||
| else | ||
| # Backend pins fastapi-cli==0.0.3, which predates | ||
| # `fastapi.__main__` (no `python -m fastapi` support), unlike | ||
| # sync-microservice's newer fastapi-cli. Use uvicorn directly. | ||
| echo "[BACKEND] Starting in dev mode on port 52123..." | ||
| python -m uvicorn main:app --host 0.0.0.0 --port 52123 --reload | ||
| fi | ||
| ) 2>&1 | sed -u 's/^/[BACKEND] /' & | ||
| PIDS+=($!) | ||
| } | ||
|
|
||
| start_sync() { | ||
| ( | ||
| cd "$SYNC_DIR" | ||
| bin_dir=$(resolve_venv "SYNC" "$SYNC_DIR" ".sync-env" "venv") || exit 1 | ||
| export PATH="$bin_dir:$PATH" | ||
| pip_install "SYNC" || exit 1 | ||
| echo "[SYNC] Starting sync-microservice on port 52124..." | ||
| if [[ "$MODE" == "prod" ]]; then | ||
| python -m uvicorn main:app --host 0.0.0.0 --port 52124 | ||
| else | ||
| python -m fastapi dev --port 52124 | ||
| fi | ||
| ) 2>&1 | sed -u 's/^/[SYNC] /' & | ||
| PIDS+=($!) | ||
| } | ||
|
|
||
| start_frontend() { | ||
| ( | ||
| cd "$FRONTEND_DIR" | ||
| if [[ ! -d "node_modules" ]]; then | ||
| echo -e "${RED}[FRONTEND] node_modules not found.${NC}" | ||
| echo -e "${YELLOW}[FRONTEND] ${SETUP_HINT}${NC}" | ||
| exit 1 | ||
| fi | ||
| echo "[FRONTEND] Starting Tauri dev..." | ||
| npm run tauri dev | ||
| ) 2>&1 | sed -u 's/^/[FRONTEND] /' & | ||
| PIDS+=($!) | ||
| } |
There was a problem hiding this comment.
🩺 Stability & Availability | 🔴 Critical | 🏗️ Heavy lift
cleanup() never actually terminates the tracked services — $! captures sed's PID, not the service subshell's, and set -e can abort the kill loop early.
Two compounding issues here:
- In each
start_*function,( ... ) 2>&1 | sed -u '...' &backgrounds a pipeline. Bash's$!after a background pipeline refers to the PID of the last command in the pipe (sed), not the subshell that actually runsresolve_venv/pip_install/python -m uvicorn/npm run tauri dev. SoPIDStracks the formatter process, not the service. cleanup()runskill "$pid" 2>/dev/nullin a loop underset -e(Line 13). If any PID already exited,killreturns non-zero, and since2>/dev/nullonly suppresses stderr (not the exit status), errexit aborts the function mid-loop — skipping remaining kills and the trailingwait.
Net effect: on Ctrl+C/SIGTERM, the actual backend/sync/frontend processes may survive as orphans instead of being terminated, undermining the "terminate tracked services when interrupt or termination signals are received" behavior this PR is meant to deliver.
🐛 Suggested fix: track the real service PID via process substitution, and don't let `kill` trip errexit
start_backend() {
(
echo "[BACKEND] Starting... ${BACKEND_DIR}"
cd "$BACKEND_DIR"
bin_dir=$(resolve_venv "BACKEND" "$BACKEND_DIR" ".env" "venv") || exit 1
export PATH="$bin_dir:$PATH"
pip_install "BACKEND" || exit 1
if [[ "$MODE" == "prod" ]]; then
echo "[BACKEND] Starting in production mode on port 52123..."
python -m uvicorn main:app --host 0.0.0.0 --port 52123 --workers "${WORKERS:-1}"
else
echo "[BACKEND] Starting in dev mode on port 52123..."
python -m uvicorn main:app --host 0.0.0.0 --port 52123 --reload
fi
- ) 2>&1 | sed -u 's/^/[BACKEND] /' &
+ ) > >(sed -u 's/^/[BACKEND] /') 2>&1 &
PIDS+=($!)
}Apply the same > >(sed ...) 2>&1 & swap to start_sync and start_frontend.
cleanup() {
echo ""
echo "Shutting down all services..."
for pid in "${PIDS[@]}"; do
- kill "$pid" 2>/dev/null
+ kill "$pid" 2>/dev/null || true
done
wait 2>/dev/null
exit 0
}🧰 Tools
🪛 Shellcheck (0.11.0)
[info] 173-173: Modification of PATH is local (to subshell caused by (..) group).
(SC2030)
[info] 193-193: PATH was modified in a subshell. That change might be lost.
(SC2031)
[info] 193-193: PATH was modified in a subshell. That change might be lost.
(SC2031)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@scripts/run.sh` around lines 155 - 217, Update cleanup() to tolerate
already-exited PIDs by guarding each kill and ensure wait still runs for all
tracked processes. In start_backend(), start_sync(), and start_frontend(),
replace the background pipeline with process substitution so $! records the
service subshell PID while preserving prefixed sed output, allowing cleanup to
terminate the actual services.
There was a problem hiding this comment.
undo this change and pull latest changes from 'main'. It'll automatically fix the lint error.
- Introduced for Node.js-based launching, supporting both development and production modes, with Windows compatibility. - Added for bash-based launching, maintaining existing functionality while enhancing user guidance for command requirements and directory checks. - Updated to improve error message formatting for better readability.
- Removed legacy command verification and activation logic for virtual environments in both JavaScript and shell scripts. - Introduced a more reliable method for resolving and using Python executables directly, enhancing compatibility across platforms. - Updated dependency installation process to ensure services start only with complete environments, improving error handling and user guidance.
- Added automatic creation of Python virtual environments in both JavaScript and shell scripts if none exist, improving setup experience. - Updated the venv resolution logic to include a prefix for better context in error messages. - Enhanced error handling for Python command checks and virtual environment creation, ensuring clearer feedback for users.
f6a0057 to
05d717e
Compare
|
Hey @rohan-pandeyy done rebased onto latest main and reverted the reset_database.py change, so that file no longer shows any diff in this PR. Force-pushed the update. Let me know if there's anything else needed! |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
scripts/run.js (1)
289-310: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick winSync-microservice dev mode binds to localhost only, unlike every other mode/service.
fastapi devdefaults to "listen on the IP address 127.0.0.1, which is the IP for your machine to communicate with itself alone (localhost)", unlikefastapi run/uvicorn which "listen on the IP address 0.0.0.0, which means all the available IP addresses". Backend's dev/prod commands and sync's prod command all explicitly pass--host 0.0.0.0, but sync's dev command in both launchers omits--host, so it silently falls back to127.0.0.1. This asymmetry can cause confusing "connection refused" behavior for anyone testing from another device/container while backend works fine.
scripts/run.js#L289-L310: add--host 0.0.0.0to thefastapi devargs at Line 304, matching the prod branch.scripts/run.sh#L189-L203: add--host 0.0.0.0to thefastapi devinvocation at Line 199, matching the prod branch.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@scripts/run.js` around lines 289 - 310, Update the sync-microservice development launch commands to explicitly bind to all interfaces by adding the host argument with value 0.0.0.0 to the fastapi dev invocation in scripts/run.js lines 289-310 and scripts/run.sh lines 189-203; leave the production commands unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@scripts/run.js`:
- Around line 159-182: Update spawnService so child startup failures decrement
aliveCount even when no exit event is emitted. Add a shared guarded settled
handler used by both the child error and exit listeners, ensuring aliveCount is
decremented only once and the existing zero-count shutdown behavior remains
intact.
---
Nitpick comments:
In `@scripts/run.js`:
- Around line 289-310: Update the sync-microservice development launch commands
to explicitly bind to all interfaces by adding the host argument with value
0.0.0.0 to the fastapi dev invocation in scripts/run.js lines 289-310 and
scripts/run.sh lines 189-203; leave the production commands unchanged.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: 8440705f-4679-474e-a4e0-5ff5317646f3
📒 Files selected for processing (2)
scripts/run.jsscripts/run.sh
| function spawnService(prefix, command, args, options) { | ||
| const child = spawn(command, args, { | ||
| stdio: ['ignore', 'pipe', 'pipe'], | ||
| ...options, | ||
| }); | ||
|
|
||
| prefixStream(child.stdout, prefix, process.stdout); | ||
| prefixStream(child.stderr, prefix, process.stdout); // merged like run.sh's 2>&1 | ||
|
|
||
| child.on('error', (err) => { | ||
| process.stdout.write(`[${prefix}] Failed to start: ${err.message}\n`); | ||
| }); | ||
|
|
||
| aliveCount++; | ||
| child.on('exit', () => { | ||
| aliveCount--; | ||
| if (aliveCount === 0 && !shuttingDown) { | ||
| process.exit(0); | ||
| } | ||
| }); | ||
|
|
||
| children.push(child); | ||
| return child; | ||
| } |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf 'Repository files matching run.js/npm/sh scripts:\n'
git ls-files | rg '(^|/)(run\.js|package\.json|run\.sh|.*\.js$)' || true
printf '\nscripts/run.js line count and relevant section:\n'
wc -l scripts/run.js
sed -n '1,240p' scripts/run.js
printf '\nSearch for spawnService/exit handlers/shutdown:\n'
rg -n "spawnService|aliveCount|children\.push|shuttingDown|process\.exit|on\\('error'|on\\(\"error\"|on\\('exit'|on\\(\"exit\"" scripts/run.jsRepository: AOSSIE-Org/PictoPy
Length of output: 9838
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Behavioral probe for Node's child_process spawn error/exit semantics using only core Node modules.
# This does not execute repository code; it runs a small standalone Node probe in the sandbox.
node - <<'JS'
const { spawn } = require('node:child_process');
async function runProbe(command) {
const child = spawn(command, [], { stdio: ['ignore', 'pipe', 'pipe'] });
const events = [];
let aliveCount = 1;
child.on('error', (err) => {
events.push(`error:${err.code}`);
});
child.on('exit', (code, signal) => {
events.push(`exit:${String(code)}:${String(signal)}`);
aliveCount--;
});
await new Promise((resolve) => {
child.on('close', (code, signal) => {
events.push(`close:${String(code)}:${String(signal)}`);
resolve(events);
});
setTimeout(resolve, 200, `[timeout] events=${JSON.stringify(events)} aliveCount=${aliveCount}`);
});
}
runProbe('totally-missing-missing-command').then((events) => {
console.log(events.join(' | '));
// The main test harness is the presence of Node's documented behavior:
// if error happens before launch, exit may not fire and aliveCount can stay positive.
});
runProbe('node').then((events) => {
console.log(events.join(' | '));
});
JSRepository: AOSSIE-Org/PictoPy
Length of output: 329
🌐 Web query:
Node.js child_process spawn error event exit event may or may not fire after error occurred documentation
💡 Result:
According to official Node.js documentation, the 'exit' event in a child_process may or may not fire after an error has occurred [1][2]. Because of this behavior, it is explicitly recommended that developers guard against accidentally invoking handler functions multiple times when listening to both the 'exit' and 'error' events [1][2]. The 'error' event is emitted for various reasons, including when a process cannot be spawned, killed, or when sending a message fails [1][2]. In contrast, the 'exit' event is emitted only after a successfully spawned process ends [1][2]. If a process fails to spawn, the 'spawn' event is not emitted, and an 'error' event is emitted instead [1][2]. Conversely, if a process is successfully spawned but later encounters an error (such as failing to execute a command within a shell), the 'spawn' event will have fired, and subsequent events like 'exit' may still occur depending on the nature of the error [1][3].
Citations:
- 1: https://nodejs.org/api/child_process.html
- 2: https://github.com/nodejs/node/blob/main/doc/api/child_process.md
- 3: https://beta.docs.nodejs.org/child_process.html
Decrement aliveCount on failed spawns as well.
When child_process.spawn fails before the process launches, it emits error but the exit event may not fire. A misspelled/stale venv command or other failed command can therefore leave spawnService with aliveCount > 0; once the other children terminate normally, the launcher will stay alive instead of auto-exiting. Track error and exit from a shared, guarded settled handler.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@scripts/run.js` around lines 159 - 182, Update spawnService so child startup
failures decrement aliveCount even when no exit event is emitted. Add a shared
guarded settled handler used by both the child error and exit listeners,
ensuring aliveCount is decremented only once and the existing zero-count
shutdown behavior remains intact.
|
@kaihere14 I tested the scripts out but the application did not start with the command specified
Is there something wrong I did on my end? |
Hey @rohan-pandeyy , nothing wrong on your end this is expected behavior, not a bug. The script only handles the two Python venvs (backend, sync-microservice) automatically since those are self-contained. frontend/node_modules isn't something the launcher creates, since npm installs aren't as cheap/safe to trigger automatically the way python -m venv is. Your log actually shows the script working correctly: [FRONTEND] node_modules not found → Run 'npm run setup' from the repo root. That's the preflight check catching a missing dependency and telling you exactly what to run, instead of letting npm run tauri dev fail with a confusing error. Could you install the dependencies first , then try node scripts/run.js again? That should get all three services up. The main target of run.sh/run.js was to run all three services together once dependencies are in place happy to add auto-setup for the frontend too if that's something you'd want, similar to how it already handles the Python venvs automatically. |
|
If that is the case, then the pull request should also update the documentation, which clearly lets the developers know what these steps are to use the "single command". |
Alright I will update the documentation and than ping you |
|
@kaihere14, the |
|
Can you also check whether files are being indexed/ai-tagged? It's not happening on my end |
Stuck on "Indexing Folder" |


Addressed Issues:
Fixes #1395
Summary:
Adds
scripts/run.jsandscripts/run.sh— a single command (node scripts/run.jsorbash scripts/run.sh,--prodfor production mode) that starts the backend, sync-microservice, and Tauri frontend together. Replaces juggling three terminals and manually creating/activating two separate Python virtual environments.On a completely fresh clone, running the launcher now takes you from zero to all three services running with no manual setup steps at all — venvs are created automatically if missing, dependencies are installed automatically, and everything starts together.
What it does:
One-command orchestration
--prodmodes.Automatic virtual environment setup
backend/.venvandsync-microservice/.sync-venvexist before starting each service.python -m venv <path>) with a clear console message, instead of failing or requiring manual setup first.python.exe/pythonbinary is resolved directly from its venv's current location every run — nosource <venv>/activate, since that script bakes in an absolute path at creation time and silently falls through to system Python with no error if the venv folder is ever renamed or moved.python -m pip install -r requirements.txtbefore starting, so it never comes up against a stale or incomplete venv.Windows-specific bugs found and fixed during testing:
PYTHONUNBUFFERED=1..exestubs —uvicorn.exe/fastapi.exe/pip.execonsole-script launchers can fail instantly with no output (Fatal error in launcher: Unable to find an appended archive), and the error is invisible with no console attached (spawned via Node, or under Git Bash). Fixed by invoking everything aspython -m <module>instead of relying on generated launcher stubs.fastapi dev's startup banner crashing on Windows —rich/rich_toolkitfalls back to a legacy Windows console writer (hardcodedcp1252encoding) with no real console attached, and the 🚀 emoji in its own banner can't be encoded, crashing the process before it binds to a port. Fixed withPYTHONUTF8=1.run.sh— previously usedsource <venv>/activate; replaced with fresh bin-dir resolution each run (see above).Backend and sync-microservice intentionally diverge in dev mode: backend uses
python -m uvicorn --reload(its pinnedfastapi-cli==0.0.3predatespython -m fastapisupport), while sync-microservice usespython -m fastapi dev(itsfastapi-cli==0.0.8supports it).Screenshots/Recordings:
N/A — this PR only touches developer tooling (
scripts/run.js,scripts/run.sh), no UI changes. Verified end-to-end locally on Windows instead:.venvand.sync-venvand rannode scripts/run.jsfrom a clean state — both venvs were created automatically, dependencies installed, and all three services (backend:52123, sync-microservice:52124, Tauri frontend) came up together and exchanged live requests successfully (/health,/models/status,/folders/statusall returning 200).AI Usage Disclosure:
We encourage contributors to use AI tools responsibly when creating Pull Requests. While AI can be a valuable aid, it is essential to ensure that your contributions meet the task requirements, build successfully, include relevant tests, and pass all linters. Submissions that do not meet these standards may be closed without warning to maintain the quality and integrity of the project. Please take the time to understand the changes you are proposing and their impact. AI slop is strongly discouraged and may lead to banning and blocking. Do not spam our repos with AI slop.
Check one of the checkboxes below:
I have used the following AI models and tools: Claude Code (Claude Sonnet 5) — used to implement and debug
run.js/run.sh, including the venv auto-creation logic, and to verify the full fresh-clone-to-running flow locally on Windows.Checklist
Summary by CodeRabbit