Merge pull request #1480 from Tibsfox/feat/autonomous-interactive-1413
feat(autonomous): add --interactive flag for lean context with user input
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
name: gsd:autonomous
|
name: gsd:autonomous
|
||||||
description: Run all remaining phases autonomously — discuss→plan→execute per phase
|
description: Run all remaining phases autonomously — discuss→plan→execute per phase
|
||||||
argument-hint: "[--from N]"
|
argument-hint: "[--from N] [--only N] [--interactive]"
|
||||||
allowed-tools:
|
allowed-tools:
|
||||||
- Read
|
- Read
|
||||||
- Write
|
- Write
|
||||||
@@ -30,7 +30,10 @@ Uses ROADMAP.md phase discovery and Skill() flat invocations for each phase comm
|
|||||||
</execution_context>
|
</execution_context>
|
||||||
|
|
||||||
<context>
|
<context>
|
||||||
Optional flag: `--from N` — start from phase N instead of the first incomplete phase.
|
Optional flags:
|
||||||
|
- `--from N` — start from phase N instead of the first incomplete phase.
|
||||||
|
- `--only N` — execute only phase N (single-phase mode).
|
||||||
|
- `--interactive` — run discuss inline with questions (not auto-answered), then dispatch plan→execute as background agents. Keeps the main context lean while preserving user input on decisions.
|
||||||
|
|
||||||
Project context, phase list, and state are resolved inside the workflow using init commands (`gsd-tools.cjs init milestone-op`, `gsd-tools.cjs roadmap analyze`). No upfront context loading needed.
|
Project context, phase list, and state are resolved inside the workflow using init commands (`gsd-tools.cjs init milestone-op`, `gsd-tools.cjs roadmap analyze`). No upfront context loading needed.
|
||||||
</context>
|
</context>
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ Read all files referenced by the invoking prompt's execution_context before star
|
|||||||
|
|
||||||
## 1. Initialize
|
## 1. Initialize
|
||||||
|
|
||||||
Parse `$ARGUMENTS` for `--from N` and `--only N` flags:
|
Parse `$ARGUMENTS` for `--from N`, `--only N`, and `--interactive` flags:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
FROM_PHASE=""
|
FROM_PHASE=""
|
||||||
@@ -29,10 +29,17 @@ if echo "$ARGUMENTS" | grep -qE '\-\-only\s+[0-9]'; then
|
|||||||
ONLY_PHASE=$(echo "$ARGUMENTS" | grep -oE '\-\-only\s+[0-9]+\.?[0-9]*' | awk '{print $2}')
|
ONLY_PHASE=$(echo "$ARGUMENTS" | grep -oE '\-\-only\s+[0-9]+\.?[0-9]*' | awk '{print $2}')
|
||||||
FROM_PHASE="$ONLY_PHASE"
|
FROM_PHASE="$ONLY_PHASE"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
INTERACTIVE=""
|
||||||
|
if echo "$ARGUMENTS" | grep -q '\-\-interactive'; then
|
||||||
|
INTERACTIVE="true"
|
||||||
|
fi
|
||||||
```
|
```
|
||||||
|
|
||||||
When `--only` is set, also set `FROM_PHASE` to the same value so existing filter logic applies.
|
When `--only` is set, also set `FROM_PHASE` to the same value so existing filter logic applies.
|
||||||
|
|
||||||
|
When `--interactive` is set, discuss runs inline with questions (not auto-answered), while plan and execute are dispatched as background agents. This keeps the main context lean — only discuss conversations accumulate — while preserving user input on all design decisions.
|
||||||
|
|
||||||
Bootstrap via milestone-level init:
|
Bootstrap via milestone-level init:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@@ -58,6 +65,7 @@ Display startup banner:
|
|||||||
|
|
||||||
If `ONLY_PHASE` is set, display: `Single phase mode: Phase ${ONLY_PHASE}`
|
If `ONLY_PHASE` is set, display: `Single phase mode: Phase ${ONLY_PHASE}`
|
||||||
Else if `FROM_PHASE` is set, display: `Starting from phase ${FROM_PHASE}`
|
Else if `FROM_PHASE` is set, display: `Starting from phase ${FROM_PHASE}`
|
||||||
|
If `INTERACTIVE` is set, display: `Mode: Interactive (discuss inline, plan+execute in background)`
|
||||||
|
|
||||||
</step>
|
</step>
|
||||||
|
|
||||||
@@ -230,18 +238,26 @@ node "$HOME/.claude/get-shit-done/bin/gsd-tools.cjs" commit "docs(${PADDED_PHASE
|
|||||||
|
|
||||||
Proceed to 3b.
|
Proceed to 3b.
|
||||||
|
|
||||||
**If SKIP_DISCUSS is `false` (or unset):** Execute the smart_discuss step for this phase.
|
**If SKIP_DISCUSS is `false` (or unset):**
|
||||||
|
|
||||||
**IMPORTANT — Discuss must be single-pass in autonomous mode.**
|
**IMPORTANT — Discuss must be single-pass in autonomous mode.**
|
||||||
The discuss step in `--auto` mode MUST NOT loop. If CONTEXT.md already exists after discuss completes, do NOT re-invoke discuss for the same phase. The `has_context` check below is authoritative — once true, discuss is done for this phase regardless of perceived "gaps" in the context file.
|
The discuss step in `--auto` mode MUST NOT loop. If CONTEXT.md already exists after discuss completes, do NOT re-invoke discuss for the same phase. The `has_context` check below is authoritative — once true, discuss is done for this phase regardless of perceived "gaps" in the context file.
|
||||||
|
|
||||||
After smart_discuss completes, verify context was written:
|
**If `INTERACTIVE` is set:** Run the standard discuss-phase skill inline (asks interactive questions, waits for user answers). This preserves user input on all design decisions while keeping plan+execute out of the main context:
|
||||||
|
|
||||||
|
```
|
||||||
|
Skill(skill="gsd:discuss-phase", args="${PHASE_NUM}")
|
||||||
|
```
|
||||||
|
|
||||||
|
**If `INTERACTIVE` is NOT set:** Execute the smart_discuss step for this phase (batch table proposals, auto-optimized).
|
||||||
|
|
||||||
|
After discuss completes (either mode), verify context was written:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
PHASE_STATE=$(node "$HOME/.claude/get-shit-done/bin/gsd-tools.cjs" init phase-op ${PHASE_NUM})
|
PHASE_STATE=$(node "$HOME/.claude/get-shit-done/bin/gsd-tools.cjs" init phase-op ${PHASE_NUM})
|
||||||
```
|
```
|
||||||
|
|
||||||
Check `has_context`. If false → go to handle_blocker: "Smart discuss for phase ${PHASE_NUM} did not produce CONTEXT.md."
|
Check `has_context`. If false → go to handle_blocker: "Discuss for phase ${PHASE_NUM} did not produce CONTEXT.md."
|
||||||
|
|
||||||
**3a.5. UI Design Contract (Frontend Phases)**
|
**3a.5. UI Design Contract (Frontend Phases)**
|
||||||
|
|
||||||
@@ -284,6 +300,20 @@ UI_SPEC_FILE=$(ls "${PHASE_DIR}"/*-UI-SPEC.md 2>/dev/null | head -1)
|
|||||||
|
|
||||||
**3b. Plan**
|
**3b. Plan**
|
||||||
|
|
||||||
|
**If `INTERACTIVE` is set:** Dispatch plan as a background agent to keep the main context lean. While plan runs, the workflow can immediately start discussing the next phase (see step 4).
|
||||||
|
|
||||||
|
```
|
||||||
|
Agent(
|
||||||
|
description="Plan phase ${PHASE_NUM}: ${PHASE_NAME}",
|
||||||
|
run_in_background=true,
|
||||||
|
prompt="Run plan-phase for phase ${PHASE_NUM}: Skill(skill=\"gsd:plan-phase\", args=\"${PHASE_NUM}\")"
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
Store the agent task_id. After discuss for the next phase completes (or if no next phase), wait for the plan agent to finish before proceeding to execute.
|
||||||
|
|
||||||
|
**If `INTERACTIVE` is NOT set (default):** Run plan inline as before.
|
||||||
|
|
||||||
```
|
```
|
||||||
Skill(skill="gsd:plan-phase", args="${PHASE_NUM}")
|
Skill(skill="gsd:plan-phase", args="${PHASE_NUM}")
|
||||||
```
|
```
|
||||||
@@ -292,13 +322,29 @@ Verify plan produced output — re-run `init phase-op` and check `has_plans`. If
|
|||||||
|
|
||||||
**3c. Execute**
|
**3c. Execute**
|
||||||
|
|
||||||
|
**If `INTERACTIVE` is set:** Wait for the plan agent to complete (if not already), verify plans exist, then dispatch execute as a background agent:
|
||||||
|
|
||||||
|
```
|
||||||
|
Agent(
|
||||||
|
description="Execute phase ${PHASE_NUM}: ${PHASE_NAME}",
|
||||||
|
run_in_background=true,
|
||||||
|
prompt="Run execute-phase for phase ${PHASE_NUM}: Skill(skill=\"gsd:execute-phase\", args=\"${PHASE_NUM} --no-transition\")"
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
Store the agent task_id. The workflow can now start discussing the next phase while this phase executes in the background. Before starting post-execution routing for this phase, wait for the execute agent to complete.
|
||||||
|
|
||||||
|
**If `INTERACTIVE` is NOT set (default):** Run execute inline as before.
|
||||||
|
|
||||||
```
|
```
|
||||||
Skill(skill="gsd:execute-phase", args="${PHASE_NUM} --no-transition")
|
Skill(skill="gsd:execute-phase", args="${PHASE_NUM} --no-transition")
|
||||||
```
|
```
|
||||||
|
|
||||||
**3d. Post-Execution Routing**
|
**3d. Post-Execution Routing**
|
||||||
|
|
||||||
After execute-phase returns, read the verification result:
|
**If `INTERACTIVE` is set:** Wait for the execute agent to complete before reading verification results.
|
||||||
|
|
||||||
|
After execute-phase returns (or the execute agent completes), read the verification result:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
VERIFY_STATUS=$(grep "^status:" "${PHASE_DIR}"/*-VERIFICATION.md 2>/dev/null | head -1 | cut -d: -f2 | tr -d ' ')
|
VERIFY_STATUS=$(grep "^status:" "${PHASE_DIR}"/*-VERIFICATION.md 2>/dev/null | head -1 | cut -d: -f2 | tr -d ' ')
|
||||||
@@ -733,6 +779,13 @@ Check for blockers in the Blockers/Concerns section. If blockers are found, go t
|
|||||||
|
|
||||||
If incomplete phases remain: proceed to next phase, loop back to execute_phase.
|
If incomplete phases remain: proceed to next phase, loop back to execute_phase.
|
||||||
|
|
||||||
|
**Interactive mode overlap:** When `INTERACTIVE` is set, the iterate step enables pipeline parallelism:
|
||||||
|
1. After discuss completes for Phase N, dispatch plan+execute as background agents
|
||||||
|
2. Immediately start discuss for Phase N+1 (the next incomplete phase) while Phase N builds
|
||||||
|
3. Before starting plan for Phase N+1, wait for Phase N's execute agent to complete and handle its post-execution routing (verification, gap closure, etc.)
|
||||||
|
|
||||||
|
This means the user is always answering discuss questions (lightweight, interactive) while the heavy work (planning, code generation) runs in the background. The main context only accumulates discuss conversations — plan and execute contexts are isolated in their agents.
|
||||||
|
|
||||||
If all phases complete, proceed to lifecycle step.
|
If all phases complete, proceed to lifecycle step.
|
||||||
|
|
||||||
</step>
|
</step>
|
||||||
@@ -935,4 +988,10 @@ When any phase operation fails or a blocker is detected, present 3 options via A
|
|||||||
- [ ] `--only N` exits cleanly after single phase completes
|
- [ ] `--only N` exits cleanly after single phase completes
|
||||||
- [ ] `--only N` on already-complete phase exits with message
|
- [ ] `--only N` on already-complete phase exits with message
|
||||||
- [ ] `--only N` handle_blocker resume message uses --only flag
|
- [ ] `--only N` handle_blocker resume message uses --only flag
|
||||||
|
- [ ] `--interactive` runs discuss inline via gsd:discuss-phase (asks questions, waits for user)
|
||||||
|
- [ ] `--interactive` dispatches plan and execute as background agents (context isolation)
|
||||||
|
- [ ] `--interactive` enables pipeline parallelism: discuss Phase N+1 while Phase N builds
|
||||||
|
- [ ] `--interactive` main context only accumulates discuss conversations (lean)
|
||||||
|
- [ ] `--interactive` waits for background agents before post-execution routing
|
||||||
|
- [ ] `--interactive` compatible with `--only` and `--from` flags
|
||||||
</success_criteria>
|
</success_criteria>
|
||||||
|
|||||||
84
tests/autonomous-interactive.test.cjs
Normal file
84
tests/autonomous-interactive.test.cjs
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
/**
|
||||||
|
* GSD Tools Tests - autonomous --interactive flag
|
||||||
|
*
|
||||||
|
* Validates that the autonomous workflow and command definition
|
||||||
|
* correctly document and support the --interactive flag.
|
||||||
|
*
|
||||||
|
* Closes: #1413
|
||||||
|
*/
|
||||||
|
|
||||||
|
const { test, describe } = require('node:test');
|
||||||
|
const assert = require('node:assert');
|
||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
|
describe('autonomous --interactive flag (#1413)', () => {
|
||||||
|
const workflowPath = path.join(__dirname, '..', 'get-shit-done', 'workflows', 'autonomous.md');
|
||||||
|
const commandPath = path.join(__dirname, '..', 'commands', 'gsd', 'autonomous.md');
|
||||||
|
|
||||||
|
test('command definition includes --interactive in argument-hint', () => {
|
||||||
|
const content = fs.readFileSync(commandPath, 'utf8');
|
||||||
|
assert.ok(content.includes('--interactive'), 'command should document --interactive flag');
|
||||||
|
assert.ok(content.includes('argument-hint:') && content.includes('--interactive'),
|
||||||
|
'argument-hint should include --interactive');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('command definition describes interactive mode behavior', () => {
|
||||||
|
const content = fs.readFileSync(commandPath, 'utf8');
|
||||||
|
assert.ok(content.includes('discuss') && content.includes('inline'),
|
||||||
|
'command should describe discuss running inline');
|
||||||
|
assert.ok(content.includes('background'),
|
||||||
|
'command should mention background agents for plan+execute');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('workflow parses --interactive flag', () => {
|
||||||
|
const content = fs.readFileSync(workflowPath, 'utf8');
|
||||||
|
assert.ok(content.includes("--interactive") && content.includes('INTERACTIVE'),
|
||||||
|
'workflow should parse --interactive into INTERACTIVE variable');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('workflow uses discuss-phase skill in interactive mode', () => {
|
||||||
|
const content = fs.readFileSync(workflowPath, 'utf8');
|
||||||
|
assert.ok(
|
||||||
|
content.includes('gsd:discuss-phase') && content.includes('INTERACTIVE'),
|
||||||
|
'workflow should invoke gsd:discuss-phase when INTERACTIVE is set'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('workflow dispatches plan as background agent in interactive mode', () => {
|
||||||
|
const content = fs.readFileSync(workflowPath, 'utf8');
|
||||||
|
// Should have Agent() with run_in_background for plan
|
||||||
|
assert.ok(
|
||||||
|
content.includes('run_in_background') && content.includes('plan-phase'),
|
||||||
|
'workflow should dispatch plan-phase as background agent in interactive mode'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('workflow dispatches execute as background agent in interactive mode', () => {
|
||||||
|
const content = fs.readFileSync(workflowPath, 'utf8');
|
||||||
|
assert.ok(
|
||||||
|
content.includes('run_in_background') && content.includes('execute-phase'),
|
||||||
|
'workflow should dispatch execute-phase as background agent in interactive mode'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('workflow describes pipeline parallelism in interactive mode', () => {
|
||||||
|
const content = fs.readFileSync(workflowPath, 'utf8');
|
||||||
|
assert.ok(
|
||||||
|
content.includes('pipeline parallelism') || content.includes('Phase N+1'),
|
||||||
|
'workflow should describe overlapping discuss/execute between phases'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('success criteria include --interactive requirements', () => {
|
||||||
|
const content = fs.readFileSync(workflowPath, 'utf8');
|
||||||
|
const criteriaMatch = content.match(/<success_criteria>([\s\S]*?)<\/success_criteria>/);
|
||||||
|
const criteria = criteriaMatch ? criteriaMatch[1] : '';
|
||||||
|
assert.ok(criteria.includes('--interactive'),
|
||||||
|
'success criteria should include --interactive requirements');
|
||||||
|
assert.ok(criteria.includes('discuss inline'),
|
||||||
|
'success criteria should mention discuss inline');
|
||||||
|
assert.ok(criteria.includes('background agents'),
|
||||||
|
'success criteria should mention background agents');
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -651,7 +651,7 @@ describe('copyCommandsAsCopilotSkills', () => {
|
|||||||
assert.ok(skillContent.includes('description: Run all remaining phases autonomously'),
|
assert.ok(skillContent.includes('description: Run all remaining phases autonomously'),
|
||||||
'description preserved');
|
'description preserved');
|
||||||
// argument-hint present and double-quoted
|
// argument-hint present and double-quoted
|
||||||
assert.ok(skillContent.includes('argument-hint: "[--from N]"'), 'argument-hint present and quoted');
|
assert.ok(skillContent.includes('argument-hint: "[--from N] [--only N] [--interactive]"'), 'argument-hint present and quoted');
|
||||||
// allowed-tools comma-separated
|
// allowed-tools comma-separated
|
||||||
assert.ok(skillContent.includes('allowed-tools: Read, Write, Bash, Glob, Grep, AskUserQuestion, Task'),
|
assert.ok(skillContent.includes('allowed-tools: Read, Write, Bash, Glob, Grep, AskUserQuestion, Task'),
|
||||||
'allowed-tools is comma-separated');
|
'allowed-tools is comma-separated');
|
||||||
|
|||||||
Reference in New Issue
Block a user