This commit is contained in:
Francesco Renzi
2026-01-22 00:12:27 +00:00
committed by GitHub
parent 1bba60b475
commit d334ab3f0a
8 changed files with 1446 additions and 1183 deletions

View File

@@ -11,10 +11,10 @@
- [x] **Chunk 2:** Step Serializer (ActionStep → YAML)
- [x] **Chunk 3:** Step Factory (Create new steps)
- [x] **Chunk 4:** Step Manipulator (Queue operations)
- [x] **Chunk 5:** REPL Commands (!step list, !step add run, !step edit, !step remove, !step move)
- [x] **Chunk 6:** Action Download Integration (!step add uses)
- [x] **Chunk 7:** Export Command (!step export)
- [x] **Chunk 8:** JSON API for Browser Extension
- [x] **Chunk 5:** REPL Commands (steps list, steps add run, steps edit, steps remove, steps move)
- [x] **Chunk 6:** Action Download Integration (steps add uses)
- [x] **Chunk 7:** Export Command (steps export)
- [x] **Chunk 8:** Output Format Flag (--output text|json for programmatic use)
- [x] **Chunk 9:** Browser Extension UI
## Overview
@@ -28,7 +28,7 @@ This transforms the debugger from a "read-only inspection tool" into an **intera
- **Primary:** Enable add/edit/move/delete of job steps during debug session
- **Primary:** Support both `run` and `uses` step types
- **Primary:** Export modified steps as valid YAML
- **Secondary:** Provide both REPL commands and JSON API for different clients
- **Secondary:** Provide `--output` flag for text/JSON response format
- **Non-goal:** Full workflow file reconstruction (steps section only)
- **Non-goal:** Production action restriction enforcement (noted for later)
@@ -37,9 +37,17 @@ This transforms the debugger from a "read-only inspection tool" into an **intera
### Grammar
```
!step <command> [target] [options]
steps <command> [target] [options] [--output text|json]
```
### Output Format
All commands support the `--output` flag to control response format:
- `--output text` (default) - Human-readable text output
- `--output json` - JSON output for programmatic use
- Short form: `-o json`, `-o text`
- Equals form: `--output=json`, `--output=text`
### Index Reference
- **1-based indexing** for user-friendliness
@@ -50,16 +58,16 @@ This transforms the debugger from a "read-only inspection tool" into an **intera
| Command | Purpose | Example |
|---------|---------|---------|
| `!step list` | Show all steps | `!step list --verbose` |
| `!step add` | Add new step | `!step add run "npm test" --after 3` |
| `!step edit` | Modify step | `!step edit 4 --script "npm run test:ci"` |
| `!step remove` | Delete step | `!step remove 5` |
| `!step move` | Reorder step | `!step move 5 --after 2` |
| `!step export` | Generate YAML | `!step export --with-comments` |
| `steps list` | Show all steps | `steps list --verbose` |
| `steps add` | Add new step | `steps add run "npm test" --after 3` |
| `steps edit` | Modify step | `steps edit 4 --script "npm run test:ci"` |
| `steps remove` | Delete step | `steps remove 5` |
| `steps move` | Reorder step | `steps move 5 --after 2` |
| `steps export` | Generate YAML | `steps export --with-comments` |
### Position Modifiers
For `!step add` and `!step move`:
For `steps add` and `steps move`:
- `--at <index>` — Insert at specific position
- `--after <index>` — Insert after step
- `--before <index>` — Insert before step
@@ -91,11 +99,16 @@ See "Command API Full Reference" section at end of document.
```csharp
public interface IStepCommandParser
{
StepCommand Parse(string input); // "!step add run \"echo hello\" --after 3"
bool IsStepCommand(string input); // Starts with "!step" or is JSON with cmd:"step.*"
StepCommand Parse(string input); // "steps add run \"echo hello\" --after 3"
bool IsStepCommand(string input); // Starts with "steps " or equals "steps"
}
public abstract class StepCommand { }
public enum OutputFormat { Text, Json }
public abstract class StepCommand
{
public OutputFormat Output { get; set; } = OutputFormat.Text;
}
public class ListCommand : StepCommand { public bool Verbose; }
public class AddRunCommand : StepCommand {
public string Script;
@@ -450,7 +463,7 @@ See "Command API Full Reference" section at end of document.
### Chunk 5: REPL Commands (run steps)
**Goal:** Implement `!step list`, `!step add run`, `!step edit`, `!step remove`, `!step move`.
**Goal:** Implement `steps list`, `steps add run`, `steps edit`, `steps remove`, `steps move`.
**Files to modify:**
- `src/Runner.Worker/Dap/DapDebugSession.cs` — Add `HandleStepCommandAsync()`
@@ -557,9 +570,9 @@ See "Command API Full Reference" section at end of document.
---
### Chunk 6: Action Download Integration (!step add uses)
### Chunk 6: Action Download Integration (steps add uses)
**Goal:** Support `!step add uses` with full action download.
**Goal:** Support `steps add uses` with full action download.
**Files to modify:**
- `src/Runner.Worker/Dap/StepCommands/StepCommandHandler.cs`
@@ -628,7 +641,7 @@ See "Command API Full Reference" section at end of document.
---
### Chunk 7: Export Command (!step export)
### Chunk 7: Export Command (steps export)
**Goal:** Generate YAML output for modified steps.
@@ -701,77 +714,93 @@ See "Command API Full Reference" section at end of document.
---
### Chunk 8: JSON API for Browser Extension
### Chunk 8: Output Format Flag for Browser Extension
**Goal:** Add JSON command support for programmatic access.
**Goal:** Add `--output` flag support for programmatic access (replaces separate JSON API).
**Files to modify:**
- `src/Runner.Worker/Dap/StepCommands/StepCommandParser.cs`
- `src/Runner.Worker/Dap/DapDebugSession.cs`
- `src/Runner.Worker/Dap/StepCommands/StepCommandHandler.cs`
**Details:**
1. **Detect JSON input:**
1. **Add OutputFormat enum and property to base StepCommand:**
```csharp
public bool IsStepCommand(string input)
public enum OutputFormat { Text, Json }
public abstract class StepCommand
{
var trimmed = input.Trim();
return trimmed.StartsWith("!step") ||
(trimmed.StartsWith("{") && trimmed.Contains("\"cmd\"") && trimmed.Contains("\"step."));
public OutputFormat Output { get; set; } = OutputFormat.Text;
}
```
2. **Parse JSON commands:**
2. **Parse `--output` flag in command parser:**
```csharp
public StepCommand Parse(string input)
// Recognize --output json, --output text, -o json, -o text, --output=json
private OutputFormat ParseOutputFlag(List<string> tokens)
{
var trimmed = input.Trim();
if (trimmed.StartsWith("{"))
return ParseJsonCommand(trimmed);
else
return ParseReplCommand(trimmed);
}
private StepCommand ParseJsonCommand(string json)
{
var obj = JObject.Parse(json);
var cmd = obj["cmd"]?.ToString();
return cmd switch
for (int i = 0; i < tokens.Count; i++)
{
"step.list" => new ListCommand { Verbose = obj["verbose"]?.Value<bool>() ?? false },
"step.add" => ParseJsonAddCommand(obj),
"step.edit" => ParseJsonEditCommand(obj),
"step.remove" => new RemoveCommand { Index = obj["index"].Value<int>() },
"step.move" => ParseJsonMoveCommand(obj),
"step.export" => new ExportCommand {
ChangesOnly = obj["changesOnly"]?.Value<bool>() ?? false,
WithComments = obj["withComments"]?.Value<bool>() ?? false
},
_ => throw new StepCommandException($"Unknown command: {cmd}")
var token = tokens[i].ToLower();
if (token == "--output" || token == "-o")
{
if (i + 1 < tokens.Count)
{
var format = tokens[i + 1].ToLower();
tokens.RemoveAt(i); tokens.RemoveAt(i);
return format == "json" ? OutputFormat.Json : OutputFormat.Text;
}
}
else if (token.StartsWith("--output="))
{
var format = token.Substring("--output=".Length);
tokens.RemoveAt(i);
return format == "json" ? OutputFormat.Json : OutputFormat.Text;
}
}
return OutputFormat.Text;
}
```
3. **Format responses based on Output property in handler:**
```csharp
if (command.Output == OutputFormat.Json)
{
return new StepCommandResult
{
Success = true,
Message = JsonConvert.SerializeObject(new { Success = true, Result = data })
};
}
else
{
return new StepCommandResult
{
Success = true,
Message = FormatAsText(data)
};
}
```
3. **JSON response format:**
```csharp
// For JSON input, return structured JSON response
if (wasJsonInput)
{
return CreateSuccessResponse(new EvaluateResponseBody
{
Result = JsonConvert.SerializeObject(result),
Type = "json"
});
}
4. **Browser extension sends REPL commands with `--output json`:**
```javascript
// Browser extension builds command strings like:
// "steps list --output json"
// "steps add run \"echo test\" --after 3 --output json"
```
**Testing:**
- All commands via JSON
- Verify JSON responses are parseable
- Test error responses
**Benefits over separate JSON API:**
- Single code path for parsing all commands
- Easier debugging (UI sends same commands humans would type)
- Commands can be copy-pasted from UI to console for testing
- Less code to maintain
**Estimated effort:** Small-medium
**Testing:**
- All commands with `--output json` return valid JSON
- All commands with `--output text` (or default) return human-readable text
- Short form `-o json` works correctly
**Estimated effort:** Small
---
@@ -845,13 +874,18 @@ See "Command API Full Reference" section at end of document.
}
```
6. **Send commands via JSON API:**
6. **Send commands via REPL format with `--output json`:**
```javascript
async function addStep(type, options) {
const cmd = { cmd: 'step.add', type, ...options };
const response = await sendEvaluate(JSON.stringify(cmd));
const cmd = buildStepCommand('step.add', { type, ...options });
const response = await sendEvaluate(cmd); // e.g., "steps add run \"echo test\" --output json"
refreshStepList();
}
async function loadSteps() {
const response = await sendEvaluate('steps list --output json');
// Parse JSON response
}
```
**Testing:**
@@ -883,7 +917,9 @@ See "Command API Full Reference" section at end of document.
| File | Chunk | Changes |
|------|-------|---------|
| `DapDebugSession.cs` | 1, 5 | Add command dispatch, wire up services |
| `content.js` | 9 | Steps panel, dialogs, export modal |
| `StepCommandParser.cs` | 8 | Add `--output` flag parsing |
| `StepCommandHandler.cs` | 8 | Format responses based on OutputFormat |
| `content.js` | 9 | Steps panel, dialogs, export modal, build REPL commands |
| `content.css` | 9 | Styling for new UI elements |
| `background.js` | 9 | Helper functions if needed |
@@ -891,10 +927,23 @@ See "Command API Full Reference" section at end of document.
## Command API Full Reference
### !step list
### Output Format Flag
All commands support the `--output` flag:
```
!step list [--verbose]
steps <command> ... --output text # Human-readable output (default)
steps <command> ... --output json # JSON output for programmatic use
steps <command> ... -o json # Short form
steps <command> ... --output=json # Equals form
```
The browser extension uses `--output json` for all commands to receive structured responses.
### steps list
```
steps list [--verbose] [--output text|json]
```
Show all steps with their indices, status, and modification state.
@@ -912,16 +961,25 @@ Steps:
Legend: ✓ = completed, ▶ = current/paused, [ADDED] = new, [MODIFIED] = edited
```
**JSON:**
**JSON output (`steps list --output json`):**
```json
{"cmd": "step.list", "verbose": false}
{
"Success": true,
"Result": [
{"index": 1, "name": "Checkout", "type": "uses", "typeDetail": "actions/checkout@v4", "status": "completed"},
{"index": 2, "name": "Setup Node", "type": "uses", "typeDetail": "actions/setup-node@v4", "status": "completed"},
{"index": 3, "name": "Install deps", "type": "run", "typeDetail": "npm ci", "status": "current"},
{"index": 4, "name": "Run tests", "type": "run", "typeDetail": "npm test", "status": "pending", "change": "MODIFIED"},
{"index": 5, "name": "Build", "type": "run", "typeDetail": "npm run build", "status": "pending", "change": "ADDED"}
]
}
```
### !step add
### steps add
**Run step:**
```
!step add run "<script>" [options]
steps add run "<script>" [options]
Options:
--name "<name>" Display name
@@ -936,11 +994,12 @@ Options:
--before <index> Insert before step
--first Insert at first pending position
--last Insert at end (default)
--output text|json Response format (default: text)
```
**Uses step:**
```
!step add uses <action> [options]
steps add uses <action> [options]
Options:
--name "<name>" Display name
@@ -950,50 +1009,138 @@ Options:
--continue-on-error Don't fail job on step failure
--timeout <minutes> Step timeout
[position options same as run]
--output text|json Response format (default: text)
```
**JSON (run):**
**Examples:**
```bash
# Human usage (text output)
steps add run "npm test" --name "Run Tests" --after 3
# Browser extension (JSON output)
steps add uses actions/setup-node@v4 --with node-version=20 --output json
```
**JSON response (`--output json`):**
```json
{
"cmd": "step.add",
"type": "run",
"script": "npm test",
"name": "Run Tests",
"shell": "bash",
"workingDirectory": "src",
"if": "success()",
"env": {"NODE_ENV": "test"},
"continueOnError": false,
"timeout": 10,
"position": {"after": 3}
"Success": true,
"Message": "Step added at position 4",
"Result": {"index": 4, "name": "Setup Node", "type": "uses", "typeDetail": "actions/setup-node@v4", "status": "pending", "change": "ADDED"}
}
```
**JSON (uses):**
**Position options:**
- `--at 3` — Insert at position 3
- `--after 2` — Insert after step 2
- `--before 4` — Insert before step 4
- `--first` — Insert at first pending position
- `--last` — Insert at end (default if omitted)
### steps edit
```
steps edit <index> [modifications]
Modifications:
--name "<name>" Change display name
--script "<script>" Change script (run only)
--action "<action>" Change action (uses only)
--shell <shell> Change shell (run only)
--working-directory <path> Change working directory
--if "<condition>" Change condition
--with key=value Set/update input (uses only)
--env KEY=value Set/update env var
--remove-with <key> Remove input
--remove-env <KEY> Remove env var
--continue-on-error Enable continue-on-error
--no-continue-on-error Disable continue-on-error
--timeout <minutes> Change timeout
--output text|json Response format (default: text)
```
**Examples:**
```bash
# Human usage
steps edit 4 --script "npm run test:ci" --name "CI Tests"
# Browser extension
steps edit 4 --script "npm run test:ci" --output json
```
**JSON response (`--output json`):**
```json
{
"cmd": "step.add",
"type": "uses",
"action": "actions/setup-node@v4",
"name": "Setup Node",
"with": {"node-version": "20"},
"env": {},
"if": "success()",
"position": {"at": 2}
"Success": true,
"Message": "Step 4 updated",
"Result": {"index": 4, "name": "CI Tests", "type": "run", "typeDetail": "npm run test:ci", "status": "pending", "change": "MODIFIED"}
}
```
**Position object options:**
```json
{"at": 3} // Insert at position 3
{"after": 2} // Insert after step 2
{"before": 4} // Insert before step 4
{"first": true} // Insert at first pending position
{"last": true} // Insert at end (default if omitted)
### steps remove
```
steps remove <index> [--output text|json]
```
### !step edit
**Examples:**
```bash
steps remove 5
steps remove 5 --output json
```
**JSON response (`--output json`):**
```json
{"Success": true, "Message": "Step 5 removed"}
```
### steps move
```
steps move <from> <position> [--output text|json]
Position (one required):
--to <index> Move to position
--after <index> Move after step
--before <index> Move before step
--first Move to first pending position
--last Move to end
```
**Examples:**
```bash
steps move 5 --after 2
steps move 5 --after 2 --output json
```
**JSON response (`--output json`):**
```json
{"Success": true, "Message": "Step moved to position 3"}
```
### steps export
```
steps export [--changes-only] [--with-comments] [--output text|json]
```
**Examples:**
```bash
steps export --with-comments
steps export --changes-only --output json
```
**JSON response (`--output json`):**
```json
{
"Success": true,
"Result": {
"yaml": "steps:\n - name: Checkout\n uses: actions/checkout@v4\n...",
"totalSteps": 5,
"addedCount": 1,
"modifiedCount": 1
}
}
```
!step edit <index> [modifications]
@@ -1098,12 +1245,12 @@ Position (one required):
These command names are reserved for future implementation:
```
!step duplicate <index> # Clone a step
!step enable <index> # Re-enable a disabled step
!step disable <index> # Skip step without removing
!step inspect <index> # Show detailed step info
!step reset <index> # Revert modifications
!step import # Add steps from YAML
steps duplicate <index> # Clone a step
steps enable <index> # Re-enable a disabled step
steps disable <index> # Skip step without removing
steps inspect <index> # Show detailed step info
steps reset <index> # Revert modifications
steps import # Add steps from YAML
```
---