feat(manager): add per-step passthrough flags via config

Adds `manager.flags.{discuss,plan,execute}` config keys so users can
configure flags that /gsd:manager passes to each step when dispatching.
For example, `manager.flags.discuss: "--auto --analyze"` makes every
discuss dispatched from the manager include those flags.

Closes #1400

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Tibsfox
2026-03-26 19:54:26 -07:00
parent 18fad939ef
commit 99318a09e2
6 changed files with 80 additions and 10 deletions

View File

@@ -28,6 +28,7 @@ const VALID_CONFIG_KEYS = new Set([
'workflow.subagent_timeout',
'hooks.context_warnings',
'project_code', 'phase_naming',
'manager.flags.discuss', 'manager.flags.plan', 'manager.flags.execute',
]);
/**

View File

@@ -314,6 +314,7 @@ function loadConfig(cwd) {
subagent_timeout: get('subagent_timeout', { section: 'workflow', field: 'subagent_timeout' }) ?? defaults.subagent_timeout,
model_overrides: parsed.model_overrides || null,
agent_skills: parsed.agent_skills || {},
manager: parsed.manager || {},
};
} catch {
return defaults;

View File

@@ -1026,6 +1026,14 @@ function cmdInitManager(cwd, raw) {
});
const completedCount = phases.filter(p => p.disk_status === 'complete').length;
// Read manager flags from config (passthrough flags for each step)
const managerFlags = {
discuss: (config.manager && config.manager.flags && config.manager.flags.discuss) || '',
plan: (config.manager && config.manager.flags && config.manager.flags.plan) || '',
execute: (config.manager && config.manager.flags && config.manager.flags.execute) || '',
};
const result = {
milestone_version: milestone.version,
milestone_name: milestone.name,
@@ -1039,6 +1047,7 @@ function cmdInitManager(cwd, raw) {
project_exists: pathExistsInternal(cwd, '.planning/PROJECT.md'),
roadmap_exists: true,
state_exists: true,
manager_flags: managerFlags,
};
output(withProjectRoot(cwd, result), raw);

View File

@@ -14,6 +14,13 @@ Configuration options for `.planning/` directory behavior.
"phase_branch_template": "gsd/phase-{phase}-{slug}",
"milestone_branch_template": "gsd/{milestone}-{slug}",
"quick_branch_template": null
},
"manager": {
"flags": {
"discuss": "",
"plan": "",
"execute": ""
}
}
```
@@ -28,6 +35,9 @@ Configuration options for `.planning/` directory behavior.
| `git.quick_branch_template` | `null` | Optional branch template for quick-task runs |
| `workflow.use_worktrees` | `true` | Whether executor agents run in isolated git worktrees. Set to `false` to disable worktrees — agents execute sequentially on the main working tree instead. Recommended for solo developers or when worktree merges cause issues. |
| `workflow.subagent_timeout` | `300000` | Timeout in milliseconds for parallel subagent tasks (e.g. codebase mapping). Increase for large codebases or slower models. Default: 300000 (5 minutes). |
| `manager.flags.discuss` | `""` | Flags passed to `/gsd:discuss-phase` when dispatched from manager (e.g. `"--auto --analyze"`) |
| `manager.flags.plan` | `""` | Flags passed to plan workflow when dispatched from manager |
| `manager.flags.execute` | `""` | Flags passed to execute workflow when dispatched from manager |
</config_schema>
<commit_docs_behavior>

View File

@@ -23,7 +23,14 @@ INIT=$(node "$HOME/.claude/get-shit-done/bin/gsd-tools.cjs" init manager)
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
```
Parse JSON for: `milestone_version`, `milestone_name`, `phase_count`, `completed_count`, `in_progress_count`, `phases`, `recommended_actions`, `all_complete`, `waiting_signal`.
Parse JSON for: `milestone_version`, `milestone_name`, `phase_count`, `completed_count`, `in_progress_count`, `phases`, `recommended_actions`, `all_complete`, `waiting_signal`, `manager_flags`.
`manager_flags` contains per-step passthrough flags from config:
- `manager_flags.discuss` — appended to `/gsd:discuss-phase` args (e.g. `"--auto --analyze"`)
- `manager_flags.plan` — appended to plan agent init command
- `manager_flags.execute` — appended to execute agent init command
These are empty strings by default. Set via: `gsd-tools config-set manager.flags.discuss "--auto --analyze"`
**If error:** Display the error message and exit.
@@ -193,24 +200,24 @@ When the user selects a compound option:
2. **Then run the inline discuss:**
```
Skill(skill="gsd:discuss-phase", args="{PHASE_NUM}")
Skill(skill="gsd:discuss-phase", args="{PHASE_NUM} {manager_flags.discuss}")
```
After discuss completes, loop back to dashboard step (background agents continue running).
### Discuss Phase N
Discussion is interactive — needs user input. Run inline:
Discussion is interactive — needs user input. Run inline with any configured flags:
```
Skill(skill="gsd:discuss-phase", args="{PHASE_NUM}")
Skill(skill="gsd:discuss-phase", args="{PHASE_NUM} {manager_flags.discuss}")
```
After discuss completes, loop back to dashboard step.
### Plan Phase N
Planning runs autonomously. Spawn a background agent that delegates to the Skill pipeline:
Planning runs autonomously. Spawn a background agent that delegates to the Skill pipeline with any configured flags:
```
Task(
@@ -221,9 +228,10 @@ Task(
Working directory: {cwd}
Phase: {N} — {phase_name}
Goal: {goal}
Manager flags: {manager_flags.plan}
Run the plan-phase Skill:
Skill(skill=\"gsd:plan-phase\", args=\"{N} --auto\")
Run the plan-phase Skill with any configured manager flags:
Skill(skill=\"gsd:plan-phase\", args=\"{N} --auto {manager_flags.plan}\")
This delegates to the full plan-phase pipeline including local patches, research, plan-checker, and all quality gates.
@@ -241,7 +249,7 @@ Loop back to dashboard step.
### Execute Phase N
Execution runs autonomously. Spawn a background agent that delegates to the Skill pipeline:
Execution runs autonomously. Spawn a background agent that delegates to the Skill pipeline with any configured flags:
```
Task(
@@ -252,9 +260,10 @@ Task(
Working directory: {cwd}
Phase: {N} — {phase_name}
Goal: {goal}
Manager flags: {manager_flags.execute}
Run the execute-phase Skill:
Skill(skill=\"gsd:execute-phase\", args=\"{N}\")
Run the execute-phase Skill with any configured manager flags:
Skill(skill=\"gsd:execute-phase\", args=\"{N} {manager_flags.execute}\")
This delegates to the full execute-phase pipeline including local patches, branching, wave-based execution, verification, and all quality gates.

View File

@@ -413,4 +413,44 @@ describe('init manager', () => {
// macOS resolves /var → /private/var; normalize both sides
assert.strictEqual(fs.realpathSync(output.project_root), fs.realpathSync(tmpDir));
});
test('output includes manager_flags defaults when not configured', () => {
writeState(tmpDir);
writeRoadmap(tmpDir, [{ number: '1', name: 'Test' }]);
const result = runGsdTools('init manager', tmpDir);
const output = JSON.parse(result.output);
assert.ok(output.manager_flags, 'should include manager_flags');
assert.strictEqual(output.manager_flags.discuss, '');
assert.strictEqual(output.manager_flags.plan, '');
assert.strictEqual(output.manager_flags.execute, '');
});
test('output includes manager_flags from config when set', () => {
writeState(tmpDir);
writeRoadmap(tmpDir, [{ number: '1', name: 'Test' }]);
// Write config with manager flags
fs.writeFileSync(
path.join(tmpDir, '.planning', 'config.json'),
JSON.stringify({
manager: {
flags: {
discuss: '--auto --analyze',
plan: '--skip-research',
execute: '--interactive',
}
}
})
);
const result = runGsdTools('init manager', tmpDir);
const output = JSON.parse(result.output);
assert.ok(output.manager_flags, 'should include manager_flags');
assert.strictEqual(output.manager_flags.discuss, '--auto --analyze');
assert.strictEqual(output.manager_flags.plan, '--skip-research');
assert.strictEqual(output.manager_flags.execute, '--interactive');
});
});