Skip to content

Replace CLI Monitor with ESP Decoder Terminal in used target "Upload and Monitor"#52

Merged
Jason2866 merged 8 commits into
mainfrom
upload_momitor
May 9, 2026
Merged

Replace CLI Monitor with ESP Decoder Terminal in used target "Upload and Monitor"#52
Jason2866 merged 8 commits into
mainfrom
upload_momitor

Conversation

@Jason2866
Copy link
Copy Markdown
Owner

@Jason2866 Jason2866 commented May 9, 2026

Summary by CodeRabbit

  • New Features

    • Enhanced upload-and-monitor workflow for ESP devices with improved task orchestration
    • Automatic monitor connection after successful uploads, using project-configured baud rate
  • Bug Fixes

    • Prevents spurious/duplicate upload events that conflicted with monitoring
    • More reliable serial-port recovery and reconnection after interrupted operations

Review Change Stack

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 9, 2026

Warning

Rate limit exceeded

@Jason2866 has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 19 minutes and 36 seconds before requesting another review.

You’ve run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 1062d0ff-3d84-4ed3-8e25-97c8aa092d9a

📥 Commits

Reviewing files that changed from the base of the PR and between 00c1729 and 788d3e3.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (4)
  • CHANGELOG.md
  • package.json
  • src/extension.ts
  • src/pioIntegration.ts
📝 Walkthrough

Walkthrough

Resolve monitor baud from PlatformIO when unspecified and intercept pioarduino's combined "Upload and Monitor" task: run a synthetic upload-only replacement, open the ESP Decoder monitor on success or reacquire the port on failure, and suppress spurious upload events during interception.

Changes

Upload and Monitor Task Interception + Baud Resolution

Layer / File(s) Summary
Project API / types
src/pioarduino-node-helpers.d.ts
Add project.ProjectConfig TypeScript declarations to expose project config and monitor_speed lookup.
Pio integration
src/pioIntegration.ts
Add `export async function getMonitorBaudRate(projectPath: string): Promise<number
Dependency
package.json
Bump devDependency pioarduino-node-helpers from ^12.4.4 to ^12.6.0.
openMonitor: baud resolution and reconnection
src/extension.ts
Import getMonitorBaudRate; compute resolvedBaudRate when opts.baudRate is omitted and use it for autoConnect reconnection change detection.
Constants and State Management
src/extension.ts
Introduce interception markers, syntheticUploadExecutions set, and suppression flag to coordinate interception state.
Task Detection and Transformation Utilities
src/extension.ts
Add isPioUploadAndMonitorTask() to detect combined tasks and buildUploadOnlyTask() to clone the task and strip --target monitor.
Function Documentation
src/extension.ts
Expand comments in subscribeToPioarduinoEvents describing early interception, synthetic upload replacement, and monitor reopen/reacquire behavior.
Core Interception
src/extension.ts
Register vscode.tasks.onDidStartTask to terminate combined tasks and execute synthetic upload-only replacements while coordinating suppression timing.
Synthetic Completion Handling
src/extension.ts
Handle vscode.tasks.onDidEndTaskProcess for synthetic tasks: on exit code 0 open esp-decoder.openMonitor(autoConnect, port), otherwise call reacquireWithRetry; clear synthetic tracking.
Cleanup Fallback
src/extension.ts
Add vscode.tasks.onDidEndTask fallback to remove synthetic executions that didn't emit onDidEndTaskProcess.
Upload Event Suppression
src/extension.ts
Modify pioarduino api.onDidUpload handler to ignore events while interception/synthetic uploads are in progress and log ignored exit codes.

Sequence Diagram

sequenceDiagram
  participant VSCode
  participant Extension
  participant Monitor as ESP Decoder Monitor
  VSCode->>Extension: Start Upload and Monitor task
  Extension->>Extension: Detect combined task
  Extension->>VSCode: Terminate original task
  Extension->>VSCode: Execute synthetic upload-only task
  VSCode->>Extension: Synthetic task completes
  alt Exit code 0 (success)
    Extension->>Monitor: Open with autoConnect
  else Exit code != 0 (failure)
    Extension->>VSCode: Reacquire serial port
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • Jason2866/ESP-Decoder#22: Also modifies subscribeToPioarduinoEvents and pioarduino upload/monitor handling (related task interception and port reacquire flows).
  • Jason2866/ESP-Decoder#17: Changes esp-decoder.openMonitor usage and monitor opening behavior referenced by this PR.
  • Jason2866/ESP-Decoder#51: Adds APIs/integration used by this PR's programmatic monitor open and autoConnect calls.

Poem

🐇 I hopped the task that mixed two tunes,
I split the upload from the monitor run.
Synthetic winds hum, the port re-opens soon,
No spurious beeps — the job is done. 🎉

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 66.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: replacing the CLI monitor with ESP Decoder Terminal for the 'Upload and Monitor' target, which aligns with the core functionality added across extension.ts, pioIntegration.ts, and type declarations.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch upload_momitor

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 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 `@src/extension.ts`:
- Around line 579-590: The current code calls event.execution.terminate() and
immediately launches vscode.tasks.executeTask(uploadOnly), which can race
because terminate() is fire-and-forget; change this to wait for the original
execution to fully end before calling executeTask. Implement a Promise that
registers a one-time vscode.tasks.onDidEndTask listener filtered to the original
execution (capture the execution in a local origExecution variable or compare
event.execution), resolve when that event fires (or timeout/fallback), await
that Promise after calling event.execution.terminate(), then call
vscode.tasks.executeTask(uploadOnly) and update interceptionInProgress
appropriately; ensure the onDidEndTask disposable is disposed after resolving to
avoid leaks.
- Line 444: The global boolean interceptionInProgress is racy and can get stuck;
replace it with a scoped Set<vscode.TaskExecution> (e.g., syntheticUploads) that
you add to when executeTask(uploadOnlyTask) resolves and remove from in the
onDidEndTask/onDidEndTaskProcess handler (or via a per-execution fallback
timeout) so suppression applies only to that TaskExecution; ensure you await the
original TaskExecution.terminate() before spawning the synthetic upload to avoid
concurrent pio runs, and only clear the TaskExecution from the set after
executeCommand('esp-decoder.openMonitor', …) and reacquireWithRetry complete
(i.e., await openMonitor/reacquire before removing) so the suppression remains
until auto-connect/reacquire finishes.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 6e1f4e97-57a1-48df-b571-ee640d6daa1c

📥 Commits

Reviewing files that changed from the base of the PR and between a561e56 and a293ba7.

📒 Files selected for processing (1)
  • src/extension.ts

Comment thread src/extension.ts Outdated
Comment thread src/extension.ts
@Jason2866
Copy link
Copy Markdown
Owner Author

@coderabbitai is the goal from the PR reached to start ESP Decoder instead of CLI Terminal for the combined target "Upload and Monitor"? Is the implementation solid?

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 9, 2026

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Read the full extension.ts file from the PR branch
fd -e ts extension.ts | head -5
cat src/extension.ts 2>/dev/null || fd -e ts -i extension | head -5

Length of output: 27923


@Jason2866 — Yes, the goal is reached, and the implementation is largely solid. Here is a detailed breakdown:


✅ Goal: Achieved

The interception flow works end-to-end:

  1. vscode.tasks.onDidStartTask fires as soon as pioarduino starts its combined task.
  2. isPioUploadAndMonitorTask() identifies it via name regex (/upload\s*and\s*monitor/i) and by inspecting ProcessExecution args (--target upload + --target monitor), so localised task names are handled.
  3. buildUploadOnlyTask() strips --target monitor from the args and stamps the cloned task with INTERCEPTED_TASK_MARKER.
  4. The original combined task is terminated before the CLI monitor can open.
  5. The synthetic upload-only task runs, and on exit code 0, esp-decoder.openMonitor is called with autoConnect: true — ESP Decoder terminal opens instead of the CLI monitor.

🔍 Implementation Quality

Strengths:

  • No race condition on termination: onDidEndTask is registered before terminate() is called, which correctly prevents missing the end event.
  • Suppression is well-designed: suppressingCombinedTask + syntheticUploadExecutions together prevent pioarduino's spurious onDidUpload(exitCode != 0) — fired when the combined task is killed — from triggering an unwanted port reacquisition.
  • Fallback cleanup: A second onDidEndTask listener cleans up syntheticUploadExecutions even if onDidEndTaskProcess never fires.
  • Retry-based reacquire: reacquireWithRetry (5 attempts, linear back-off starting at 300 ms) gracefully handles the OS holding the USB device after flashing.
  • macOS port normalisation: normalizePortPath correctly maps /dev/cu.*/dev/tty.*.

Potential gaps to be aware of:

# Issue Severity
1 ShellExecution not handled: If pioarduino ever switches from ProcessExecution to ShellExecution, buildUploadOnlyTask returns undefined, a warning is logged, and the CLI monitor still opens silently. Medium
2 baudRate not forwarded on monitor open: The openMonitor call after the synthetic task succeeds passes port but not baudRate. If pioarduino knows the baud rate, it is not handed over. Low
3 onDidEndTaskProcess may not fire for cancelled tasks: If the synthetic task is cancelled before its process starts, onDidEndTask fires for cleanup, but neither openMonitor nor reacquireWithRetry is triggered — the monitor won't open automatically. Low
4 5 s safety timeout on originalTaskEnded: If the original task takes longer than 5 s to terminate (e.g. slow OS), the synthetic upload could start while the original is still winding down, potentially causing two pio processes to contend for the serial port. Low

None of these are blockers for the primary use case, but gap #1 is worth keeping an eye on as pioarduino evolves.


Summary: The PR successfully replaces the pioarduino CLI monitor with ESP Decoder for the "Upload and Monitor" target. The implementation is well-thought-out, handles the key race conditions correctly, and includes proper fallback handling. The gaps listed above are edge cases that won't affect normal usage.

@Jason2866
Copy link
Copy Markdown
Owner Author

@coderabbitai full review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 9, 2026

✅ Actions performed

Full review triggered.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (6)
src/pioIntegration.ts (2)

579-589: 💤 Low value

Prefer a static import for consistency with the existing pioCore import.

pioarduino-node-helpers is already statically imported at the top of this file (line 6), so the dynamic await import(...) here is inconsistent and adds an extra Promise hop on a hot path (called every time esp-decoder.openMonitor runs without an explicit baud rate). A static import keeps the bundling/tree-shaking story consistent with core.

♻️ Proposed simplification
-import { core as pioCore } from 'pioarduino-node-helpers';
+import { core as pioCore, project as pioProject } from 'pioarduino-node-helpers';
 export async function getMonitorBaudRate(projectPath: string): Promise<number | undefined> {
   try {
-    const { ProjectConfig } = (await import('pioarduino-node-helpers')).project;
-    const config = new ProjectConfig(projectPath);
+    const config = new pioProject.ProjectConfig(projectPath);
     await config.read();
     return config.getEnvMonitorSpeed(config.defaultEnv());
   } catch {
     // PIO Core not available or project not configured — ignore
   }
   return undefined;
 }
🤖 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 `@src/pioIntegration.ts` around lines 579 - 589, The function
getMonitorBaudRate currently uses a dynamic await import of
'pioarduino-node-helpers' — change it to use the already statically imported
module (the same symbol used where pioCore is imported) and directly reference
ProjectConfig instead of calling await import(...). Keep the existing error
handling: construct ProjectConfig(projectPath), await config.read(), and return
config.getEnvMonitorSpeed(config.defaultEnv()), but remove the dynamic import
and any unnecessary Promise hop so calls from esp-decoder.openMonitor use the
static ProjectConfig class.

580-588: 💤 Low value

Silent failure makes baud-rate resolution failures invisible.

Any failure (PIO Core missing, project not configured, helper API mismatch, unexpected exception) is swallowed without any diagnostic, so users who expect their monitor_speed to be honoured will silently fall back to the default baud without any way to know why. Consider logging the error path to an output channel — even at a debug level — to aid troubleshooting, especially since the new ProjectConfig API is from a freshly bumped dependency.

🤖 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 `@src/pioIntegration.ts` around lines 580 - 588, The try/catch around the
ProjectConfig lookup swallows all errors (affecting ProjectConfig,
getEnvMonitorSpeed, defaultEnv or import failures) so add logging inside the
catch that records the caught error and context; locate the block that imports
project.ProjectConfig and calls config.read()/getEnvMonitorSpeed/defaultEnv and
in the catch log a concise diagnostic (e.g., using the existing output/logger
instance or console.debug/error) including the exception message and that
baud-rate resolution failed, then return undefined as before.
src/extension.ts (4)

723-732: 💤 Low value

Suppression predicate looks correct; consider logging the suppression source for diagnostics.

The combined check suppressingCombinedTask || syntheticUploadExecutions.size > 0 correctly covers both windows — the brief gap between terminate() and executeTask resolving, and the lifetime of the synthetic upload itself. Since this is the place where bugs in the handover would manifest as "weird port behavior after upload", it would help future debugging to log which condition matched:

♻️ Optional log refinement
-        if (suppressingCombinedTask || syntheticUploadExecutions.size > 0) {
-          log.appendLine(
-            `[ESP Decoder] Ignoring onDidUpload (exitCode=${exitCode}) — intercepted upload in progress`,
-          );
+        if (suppressingCombinedTask || syntheticUploadExecutions.size > 0) {
+          const reason = suppressingCombinedTask
+            ? 'combined-task termination'
+            : `synthetic upload (${syntheticUploadExecutions.size} active)`;
+          log.appendLine(
+            `[ESP Decoder] Ignoring onDidUpload (exitCode=${exitCode}) — ${reason}`,
+          );
           return;
         }
🤖 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 `@src/extension.ts` around lines 723 - 732, The suppression log should record
which predicate caused the early return to aid diagnostics: update the block in
src/extension.ts where the variables suppressingCombinedTask and
syntheticUploadExecutions are checked (the onDidUpload handler) to include in
the process log whether suppression was due to suppressingCombinedTask being
true or syntheticUploadExecutions.size > 0 (and include the actual size value);
keep the existing message but append the matching source (e.g.,
"suppressedBy=suppressingCombinedTask" or
"suppressedBy=syntheticUploadExecutions,size=NN") so future debugging can tell
which branch fired.

622-629: 💤 Low value

suppressingCombinedTask reset window on the failure path leaves a small unsuppressed gap.

If vscode.tasks.executeTask(uploadOnly) rejects, the catch on line 626 sets suppressingCombinedTask = false immediately. Pioarduino's spurious onDidUpload(exitCode != 0) for the terminated combined task may arrive after this point (it's an async pipeline of its own), at which point neither flag is set and the suppression check at lines 727 falls through — reacquireWithRetry runs unexpectedly even though the user never actually started a new monitor session.

Consider keeping suppressingCombinedTask true for a brief tail (e.g., a setTimeout(..., 1500) cleanup) when the synthetic launch fails, so the trailing pioarduino event can't escape the suppression window.

🤖 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 `@src/extension.ts` around lines 622 - 629, The catch path that handles a
rejected vscode.tasks.executeTask(uploadOnly) currently clears
suppressingCombinedTask immediately, creating a race where a late pioarduino
onDidUpload can bypass suppression; modify the catch block that references
syntheticUploadExecutions and suppressingCombinedTask so instead of setting
suppressingCombinedTask = false immediately it schedules a short delayed reset
(e.g., setTimeout to clear suppressingCombinedTask after ~1500ms) and only
clears any temporary state after that delay; ensure syntheticUploadExecutions
handling and any immediate cleanup still occur, but keep suppressingCombinedTask
true for the brief tail so the later onDidUpload handler and the
reacquireWithRetry suppression check remain effective.

102-110: 💤 Low value

Simplify the baud rate resolution IIFE.

The async IIFE on lines 102-107 is harder to read than necessary. A small inline helper or direct conditional is clearer:

♻️ Proposed simplification
-        const resolvedBaudRate = typeof opts?.baudRate === 'number'
-          ? opts.baudRate
-          : await (async () => {
-              const folder = vscode.workspace.workspaceFolders?.[0]?.uri.fsPath;
-              return folder ? await getMonitorBaudRate(folder) : undefined;
-            })();
+        let resolvedBaudRate: number | undefined = opts?.baudRate;
+        if (typeof resolvedBaudRate !== 'number') {
+          const folder = vscode.workspace.workspaceFolders?.[0]?.uri.fsPath;
+          resolvedBaudRate = folder ? await getMonitorBaudRate(folder) : undefined;
+        }
         if (typeof resolvedBaudRate === 'number') {
           serialManager.setBaudRate(resolvedBaudRate);
         }
🤖 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 `@src/extension.ts` around lines 102 - 110, The async IIFE used to compute
resolvedBaudRate is unnecessary and hard to read; replace it with a direct
conditional that awaits getMonitorBaudRate when no numeric opts?.baudRate is
provided. Specifically, compute resolvedBaudRate by checking typeof
opts?.baudRate === 'number' and otherwise reading the first workspace folder
path and awaiting getMonitorBaudRate(folder) (or leaving undefined if no
folder), then call serialManager.setBaudRate(resolvedBaudRate) when the result
is a number; update references to resolvedBaudRate, opts?.baudRate,
getMonitorBaudRate, and serialManager.setBaudRate accordingly.

504-538: ⚖️ Poor tradeoff

ShellExecution falls back silently to letting the CLI monitor run.

buildUploadOnlyTask only handles vscode.ProcessExecution and returns undefined for everything else. When that happens, onDidStartTask logs a warning to the output channel and returns without terminating the combined task — so on any pioarduino version (or platform) where the "Upload and Monitor" task is constructed with ShellExecution, the user silently gets the legacy CLI monitor and the PR's main feature simply doesn't activate.

isPioUploadAndMonitorTask already detects the task by name regex (lines 483–484) even when task.execution isn't ProcessExecution, so the detection→build mismatch is the blocker. Two reasonable mitigations:

  • Add ShellExecution support: clone command, strip --target monitor from args/commandLine, preserve options.
  • At minimum, surface the situation more visibly (e.g. a one-time showWarningMessage) rather than only logging to the output channel, so users on the unsupported path realize why the new behavior didn't kick in.
🤖 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 `@src/extension.ts` around lines 504 - 538, The buildUploadOnlyTask currently
only handles vscode.ProcessExecution and returns undefined for ShellExecution
which causes detection (isPioUploadAndMonitorTask) to succeed but no
interception to occur; update buildUploadOnlyTask to also handle
vscode.ShellExecution by cloning the ShellExecution's command/commandLine and
args, removing the "--target monitor" token (and its following "monitor" literal
if present) while preserving execution.options/presentationOptions and other
metadata, creating the new task with a new vscode.ShellExecution; if you prefer
a fallback instead of full ShellExecution support, add a visible user-facing
fallback (vscode.window.showWarningMessage) when you detect a pio
upload-and-monitor task whose execution is not ProcessExecution to inform the
user the extension could not intercept the monitor.
🤖 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 `@src/extension.ts`:
- Around line 597-620: The current originalTaskEnded Promise uses a fixed
5_000ms setTimeout which can race with a slow origExecution shutdown; change the
implementation of originalTaskEnded (the Promise that registers
vscode.tasks.onDidEndTask and the setTimeout) to poll
vscode.tasks.taskExecutions for the presence of origExecution until it
disappears or a longer deadline (e.g. 10–15s) elapses, and call
processLogger/log.appendLine when the timeout path is taken so the event is
visible; keep disposing the subscription (sub.dispose()) when onDidEndTask fires
or when the polling/timeout completes, and ensure you still await
originalTaskEnded before creating the synthetic upload-only task while leaving
suppressingCombinedTask handling unchanged.

---

Nitpick comments:
In `@src/extension.ts`:
- Around line 723-732: The suppression log should record which predicate caused
the early return to aid diagnostics: update the block in src/extension.ts where
the variables suppressingCombinedTask and syntheticUploadExecutions are checked
(the onDidUpload handler) to include in the process log whether suppression was
due to suppressingCombinedTask being true or syntheticUploadExecutions.size > 0
(and include the actual size value); keep the existing message but append the
matching source (e.g., "suppressedBy=suppressingCombinedTask" or
"suppressedBy=syntheticUploadExecutions,size=NN") so future debugging can tell
which branch fired.
- Around line 622-629: The catch path that handles a rejected
vscode.tasks.executeTask(uploadOnly) currently clears suppressingCombinedTask
immediately, creating a race where a late pioarduino onDidUpload can bypass
suppression; modify the catch block that references syntheticUploadExecutions
and suppressingCombinedTask so instead of setting suppressingCombinedTask =
false immediately it schedules a short delayed reset (e.g., setTimeout to clear
suppressingCombinedTask after ~1500ms) and only clears any temporary state after
that delay; ensure syntheticUploadExecutions handling and any immediate cleanup
still occur, but keep suppressingCombinedTask true for the brief tail so the
later onDidUpload handler and the reacquireWithRetry suppression check remain
effective.
- Around line 102-110: The async IIFE used to compute resolvedBaudRate is
unnecessary and hard to read; replace it with a direct conditional that awaits
getMonitorBaudRate when no numeric opts?.baudRate is provided. Specifically,
compute resolvedBaudRate by checking typeof opts?.baudRate === 'number' and
otherwise reading the first workspace folder path and awaiting
getMonitorBaudRate(folder) (or leaving undefined if no folder), then call
serialManager.setBaudRate(resolvedBaudRate) when the result is a number; update
references to resolvedBaudRate, opts?.baudRate, getMonitorBaudRate, and
serialManager.setBaudRate accordingly.
- Around line 504-538: The buildUploadOnlyTask currently only handles
vscode.ProcessExecution and returns undefined for ShellExecution which causes
detection (isPioUploadAndMonitorTask) to succeed but no interception to occur;
update buildUploadOnlyTask to also handle vscode.ShellExecution by cloning the
ShellExecution's command/commandLine and args, removing the "--target monitor"
token (and its following "monitor" literal if present) while preserving
execution.options/presentationOptions and other metadata, creating the new task
with a new vscode.ShellExecution; if you prefer a fallback instead of full
ShellExecution support, add a visible user-facing fallback
(vscode.window.showWarningMessage) when you detect a pio upload-and-monitor task
whose execution is not ProcessExecution to inform the user the extension could
not intercept the monitor.

In `@src/pioIntegration.ts`:
- Around line 579-589: The function getMonitorBaudRate currently uses a dynamic
await import of 'pioarduino-node-helpers' — change it to use the already
statically imported module (the same symbol used where pioCore is imported) and
directly reference ProjectConfig instead of calling await import(...). Keep the
existing error handling: construct ProjectConfig(projectPath), await
config.read(), and return config.getEnvMonitorSpeed(config.defaultEnv()), but
remove the dynamic import and any unnecessary Promise hop so calls from
esp-decoder.openMonitor use the static ProjectConfig class.
- Around line 580-588: The try/catch around the ProjectConfig lookup swallows
all errors (affecting ProjectConfig, getEnvMonitorSpeed, defaultEnv or import
failures) so add logging inside the catch that records the caught error and
context; locate the block that imports project.ProjectConfig and calls
config.read()/getEnvMonitorSpeed/defaultEnv and in the catch log a concise
diagnostic (e.g., using the existing output/logger instance or
console.debug/error) including the exception message and that baud-rate
resolution failed, then return undefined as before.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 7d6005af-4eb0-4513-a29c-cb2caeb08947

📥 Commits

Reviewing files that changed from the base of the PR and between a561e56 and 00c1729.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (4)
  • package.json
  • src/extension.ts
  • src/pioIntegration.ts
  • src/pioarduino-node-helpers.d.ts

Comment thread src/extension.ts
@Jason2866
Copy link
Copy Markdown
Owner Author

@coderabbitai is the goal from the PR reached to start ESP Decoder instead of CLI Terminal for the combined target "Upload and Monitor"? Is the implementation solid?

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 9, 2026

Tip

For best results, initiate chat on the files or code changes.

@Jason2866 — Yes, the goal is reached and the implementation is largely solid. I covered this in detail in my earlier response above, but here's a quick recap:

✅ Goal: Achieved

The interception flow works end-to-end:

  1. isPioUploadAndMonitorTask() detects the combined task via name regex and ProcessExecution args inspection.
  2. The combined task is terminated before the CLI monitor can open.
  3. A synthetic upload-only task (with --target monitor stripped) runs in its place.
  4. On exit code 0, esp-decoder.openMonitor is called with autoConnect: trueESP Decoder opens instead of the CLI monitor. ✔️

🔍 Implementation Quality

Solid points: no race condition on termination, well-designed suppression of spurious onDidUpload events, fallback cleanup listener, retry-based port reacquisition, macOS port normalisation.

Potential gaps (all non-blocking for normal usage):

# Issue Severity
1 ShellExecution not handled — buildUploadOnlyTask returns undefined, CLI monitor silently takes over Medium
2 baudRate not forwarded to openMonitor Low
3 onDidEndTaskProcess may not fire for cancelled synthetic tasks before process start Low
4 5 s safety timeout on originalTaskEnded may be too short on slow systems Low

Gap #1 is the only one worth monitoring as pioarduino evolves. Everything else is an edge case that won't affect normal "Upload and Monitor" usage.

@Jason2866
Copy link
Copy Markdown
Owner Author

@coderabbitai you did not take in account latest commits!!

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 9, 2026

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

@Jason2866, you're right — let me fetch the latest state of all changed files right now.


🏁 Script executed:

#!/bin/bash
echo "=== src/extension.ts ==="
cat src/extension.ts

echo ""
echo "=== src/pioIntegration.ts ==="
cat src/pioIntegration.ts

echo ""
echo "=== src/pioarduino-node-helpers.d.ts ==="
cat src/pioarduino-node-helpers.d.ts

echo ""
echo "=== package.json ==="
cat package.json

echo ""
echo "=== Recent git log ==="
git log --oneline -10

Length of output: 61042


@Jason2866 — Apologies for the outdated analysis! I've now read all the latest commits (548d70c, 00c1729, d85ac90, 7dcc0e2) and here is the updated picture:


✅ What the latest commits fixed

1. baudRate now resolved from platformio.ini (gap #2CLOSED)

openMonitor now reads monitor_speed from the project config when no baud rate is supplied by the caller:

let resolvedBaudRate: number | undefined = opts?.baudRate;
if (typeof resolvedBaudRate !== 'number') {
  const folder = vscode.workspace.workspaceFolders?.[0]?.uri.fsPath;
  resolvedBaudRate = folder ? await getMonitorBaudRate(folder) : undefined;
}

The new getMonitorBaudRate() in pioIntegration.ts is clean and safe — it uses ProjectConfig from pioarduino-node-helpers ^12.6.0, calls defaultEnv(), and swallows all errors when PIO Core is unavailable:

export async function getMonitorBaudRate(projectPath: string): Promise<number | undefined> {
  try {
    const config = new pioProject.ProjectConfig(projectPath);
    await config.read();
    return config.getEnvMonitorSpeed(config.defaultEnv());
  } catch { /* PIO Core not available — ignore */ }
  return undefined;
}

The resolved baud rate is also correctly used for reconnect change-detection (baudChanged), so a baud-rate-only change will also trigger a reconnect. ✔️


2. Safety timeout raised from 5 s → 10 s (gap #4CLOSED)

setTimeout(() => {
  sub.dispose();
  log.appendLine('[ESP Decoder] Original combined task did not end within 10 s — proceeding anyway');
  resolve();
}, 10_000);

This is more forgiving for slow OS serial-port teardown. ✔️


🔍 Remaining minor gaps (non-blocking)

# Issue Severity Status
1 ShellExecution not handled: buildUploadOnlyTask returns undefined and logs a warning; CLI monitor silently takes over Medium Still present
3 onDidEndTaskProcess may not fire for synthetic tasks cancelled before the process starts — monitor won't open automatically Low Still present

Gap #1 is the only one worth keeping an eye on as pioarduino evolves. Gap #3 is a rare edge case (task cancelled in the window between executeTask() resolving and the process spawning) and the fallback onDidEndTask listener does clean up syntheticUploadExecutions properly even in that scenario, so state stays consistent.


✅ Updated Summary

The latest commits address the two most actionable gaps from the previous review. The PR now:

  • Correctly opens ESP Decoder instead of the CLI terminal for "Upload and Monitor" ✔️
  • Forwards the correct baud rate from platformio.ini to the monitor ✔️
  • Uses a robust 10 s fallback timeout ✔️
  • Handles race conditions and spurious onDidUpload events correctly ✔️

The implementation is solid and ready. The two remaining gaps are known limitations that do not affect normal usage.

@Jason2866 Jason2866 merged commit e4dc642 into main May 9, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant