Completions (SupportsCompletionsRequest = true):
- Respond to DAP 'completions' requests with our DSL commands
(help, help("run"), run(...)) so they appear in the debug
console autocomplete across all DAP clients
- Add CompletionsArguments, CompletionItem, and
CompletionsResponseBody to DapMessages
Friendly error messages for unsupported stepping commands:
- stepIn: explain that Actions debug at the step level
- stepOut: suggest using 'continue'
- stepBack/reverseContinue: note 'not yet supported'
- pause: explain automatic pausing at step boundaries
The DAP spec does not provide a capability to hide stepIn/stepOut
buttons (they are considered fundamental operations). The best
server-side UX is clear error messages when clients send them.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The run() command was passing ${{ }} expressions literally to the
shell instead of evaluating them first. This caused scripts like
`run("echo ${{ github.job }}")` to fail with 'bad substitution'.
Fix: add ExpandExpressions() that finds each ${{ expr }} occurrence,
evaluates it individually via PipelineTemplateEvaluator, masks the
result through SecretMasker, and substitutes it into the script body
before writing the temp file — matching how ActionRunner evaluates
step inputs before ScriptHandler sees them.
Also expands expressions in DSL-provided env values so that
`env: { TOKEN: "${{ secrets.MY_TOKEN }}" }` works correctly.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Route `evaluate` requests by context:
- `repl` context → DSL parser → command dispatch (help/run)
- All other contexts (watch, hover, etc.) → expression evaluation
If REPL input doesn't match any DSL command, it falls through to
expression evaluation so the Debug Console also works for ad-hoc
`github.repository`-style queries.
Changes:
- HandleEvaluateAsync replaces the sync HandleEvaluate
- HandleReplInputAsync parses input through DapReplParser.TryParse
- DispatchReplCommandAsync dispatches HelpCommand and RunCommand
- DapReplExecutor is created alongside the DAP server reference
- Remove vestigial `await Task.CompletedTask` from HandleMessageAsync
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Implement the run command executor that makes REPL `run(...)` behave
like a real workflow `run:` step by reusing the runner's existing
infrastructure.
Key design choices:
- Shell resolution mirrors ScriptHandler: job defaults → explicit
shell from DSL → platform default (bash→sh on Unix, pwsh→powershell
on Windows)
- Script fixup via ScriptHandlerHelpers.FixUpScriptContents() adds
the same error-handling preamble as a real step
- Environment is built from ExecutionContext.ExpressionValues[`env`]
plus runtime context variables (GITHUB_*, RUNNER_*, etc.), with
DSL-provided env overrides applied last
- Working directory defaults to $GITHUB_WORKSPACE
- Output is streamed in real time via DAP output events with secrets
masked before emission through HostContext.SecretMasker
- Only the exit code is returned in the evaluate response (avoiding
the prototype's double-output bug)
- Temp script files are cleaned up after execution
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Introduce a typed command model and hand-rolled parser for the debug
console DSL. The parser turns REPL input into HelpCommand or
RunCommand objects, keeping parsing separate from execution.
Ruby-like DSL syntax:
help → general help
help("run") → command-specific help
run("echo hello") → run with default shell
run("echo $X", shell: "bash", env: { X: "1" })
→ run with explicit shell and env
Parser features:
- Handles escaped quotes, nested braces, and mixed arguments
- Keyword arguments: shell, env, working_directory
- Env blocks parsed as { KEY: "value", KEY2: "value2" }
- Returns null for non-DSL input (falls through to expression eval)
- Descriptive error messages for malformed input
- Help text scaffolding for discoverability
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add HandleEvaluate() that delegates expression evaluation to the
DapVariableProvider, keeping all masking centralized.
Changes:
- Register 'evaluate' in the command dispatch switch
- HandleEvaluate resolves frame context and delegates to
DapVariableProvider.EvaluateExpression()
- Set SupportsEvaluateForHovers = true in capabilities so DAP
clients enable hover tooltips and the Watch pane
No separate feature flag — the debugger is already gated by
EnableDebugger on the job context.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add EvaluateExpression() that evaluates GitHub Actions expressions
using the runner's existing PipelineTemplateEvaluator infrastructure.
How it works:
- Strips ${{ }} wrapper if present
- Creates a BasicExpressionToken and evaluates via
EvaluateStepDisplayName (supports the full expression language:
functions, operators, context access)
- Masks the result through MaskSecrets() — same masking path used
by scope inspection
- Returns a structured EvaluateResponseBody with type inference
- Catches evaluation errors and returns masked error messages
Also adds InferResultType() helper for DAP type hints.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Replace the stub HandleScopes/HandleVariables implementations that
returned empty lists with real delegation to DapVariableProvider.
Changes:
- DapDebugSession now creates a DapVariableProvider on Initialize()
- HandleScopes() resolves the execution context for the requested
frame and delegates to the provider
- HandleVariables() delegates to the provider for both top-level
scope references and nested dynamic references
- GetExecutionContextForFrame() maps frame IDs to contexts:
frame 1 = current step, frames 1000+ = completed (no live context)
- Provider is reset on each new step to invalidate stale nested refs
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Introduce a reusable component that maps runner ExpressionValues and
PipelineContextData into DAP scopes and variables. This is the single
point where execution-context values are materialized for the debugger.
Key design decisions:
- Fixed scope reference IDs (1–100) for the 10 well-known scopes
(github, env, runner, job, steps, secrets, inputs, vars, matrix, needs)
- Dynamic reference IDs (101+) for lazy nested object/array expansion
- All string values pass through HostContext.SecretMasker.MaskSecrets()
- The secrets scope is intentionally opaque: keys shown, values replaced
with a constant redaction marker
- MaskSecrets() is public so future DAP features (evaluate, REPL) can
reuse it without duplicating masking policy
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>