mirror of
https://github.com/glittercowboy/get-shit-done
synced 2026-04-25 17:25:23 +02:00
fix(init): embed auto_advance/auto_chain_active/mode in init plan-phase output (#2228)
Prevents infinite config-get loops on Kimi K2.5 and other models that re-execute bash tool calls when they encounter config-get subshell patterns. Values are now bundled into the init plan-phase JSON so step 15 of plan-phase.md can read them directly without separate shell calls. Closes #2192 Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -377,6 +377,9 @@ function loadConfig(cwd) {
|
||||
exa_search: get('exa_search') ?? defaults.exa_search,
|
||||
tdd_mode: get('tdd_mode', { section: 'workflow', field: 'tdd_mode' }) ?? false,
|
||||
text_mode: get('text_mode', { section: 'workflow', field: 'text_mode' }) ?? defaults.text_mode,
|
||||
auto_advance: get('auto_advance', { section: 'workflow', field: 'auto_advance' }) ?? false,
|
||||
_auto_chain_active: get('_auto_chain_active', { section: 'workflow', field: '_auto_chain_active' }) ?? false,
|
||||
mode: get('mode') ?? 'interactive',
|
||||
sub_repos: get('sub_repos', { section: 'planning', field: 'sub_repos' }) ?? defaults.sub_repos,
|
||||
resolve_model_ids: get('resolve_model_ids') ?? defaults.resolve_model_ids,
|
||||
context_window: get('context_window') ?? defaults.context_window,
|
||||
|
||||
@@ -238,6 +238,12 @@ function cmdInitPlanPhase(cwd, phase, raw, options = {}) {
|
||||
nyquist_validation_enabled: config.nyquist_validation,
|
||||
commit_docs: config.commit_docs,
|
||||
text_mode: config.text_mode,
|
||||
// Auto-advance config — included so workflows don't need separate config-get
|
||||
// calls for these values, which causes infinite config-read loops on some models
|
||||
// (e.g. Kimi K2.5). See #2192.
|
||||
auto_advance: !!(config.auto_advance),
|
||||
auto_chain_active: !!(config._auto_chain_active),
|
||||
mode: config.mode || 'interactive',
|
||||
|
||||
// Phase info
|
||||
phase_found: !!phaseInfo,
|
||||
|
||||
@@ -1145,20 +1145,20 @@ Route to `<offer_next>` OR `auto_advance` depending on flags/config.
|
||||
|
||||
## 15. Auto-Advance Check
|
||||
|
||||
Check for auto-advance trigger:
|
||||
Check for auto-advance trigger using values already loaded in step 1:
|
||||
|
||||
1. Parse `--auto` and `--chain` flags from $ARGUMENTS
|
||||
2. **Sync chain flag with intent** — if user invoked manually (no `--auto` and no `--chain`), clear the ephemeral chain flag from any previous interrupted `--auto` chain. This does NOT touch `workflow.auto_advance` (the user's persistent settings preference):
|
||||
2. Use `auto_chain_active` and `auto_advance` from the INIT JSON parsed in step 1 — **do not issue additional `config-get` calls for these values** (they are already present in the init output). Issuing redundant `config-get` calls for values already in INIT can cause infinite read loops on some runtimes.
|
||||
3. **Sync chain flag with intent** — if user invoked manually (no `--auto` and no `--chain`), clear the ephemeral chain flag from any previous interrupted `--auto` chain. This does NOT touch `workflow.auto_advance` (the user's persistent settings preference):
|
||||
```bash
|
||||
if [[ ! "$ARGUMENTS" =~ --auto ]] && [[ ! "$ARGUMENTS" =~ --chain ]]; then
|
||||
node "$HOME/.claude/get-shit-done/bin/gsd-tools.cjs" config-set workflow._auto_chain_active false 2>/dev/null
|
||||
fi
|
||||
```
|
||||
3. Read both the chain flag and user preference:
|
||||
```bash
|
||||
AUTO_CHAIN=$(node "$HOME/.claude/get-shit-done/bin/gsd-tools.cjs" config-get workflow._auto_chain_active 2>/dev/null || echo "false")
|
||||
AUTO_CFG=$(node "$HOME/.claude/get-shit-done/bin/gsd-tools.cjs" config-get workflow.auto_advance 2>/dev/null || echo "false")
|
||||
```
|
||||
|
||||
Set local variables from INIT (parsed once in step 1):
|
||||
- `AUTO_CHAIN` = `auto_chain_active` from INIT JSON (boolean, default false)
|
||||
- `AUTO_CFG` = `auto_advance` from INIT JSON (boolean, default false)
|
||||
|
||||
**If `--auto` or `--chain` flag present AND `AUTO_CHAIN` is not true:** Persist chain flag to config (handles direct invocation without prior discuss-phase):
|
||||
```bash
|
||||
|
||||
@@ -1358,6 +1358,74 @@ describe('findProjectRoot integration via --cwd', () => {
|
||||
});
|
||||
});
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// #2192: init plan-phase must include auto_advance, auto_chain_active, and mode
|
||||
// so workflows don't need separate config-get calls that loop on Kimi K2.5
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
describe('#2192: init plan-phase includes auto-advance config to prevent separate config-get loops', () => {
|
||||
let tmpDir;
|
||||
|
||||
beforeEach(() => {
|
||||
tmpDir = createTempProject();
|
||||
fs.mkdirSync(path.join(tmpDir, '.planning', 'phases', '01-auth'), { recursive: true });
|
||||
fs.writeFileSync(
|
||||
path.join(tmpDir, '.planning', 'ROADMAP.md'),
|
||||
['# Roadmap', '', '## Milestone v1', '', '### Phase 1: Auth', '**Goal:** Auth'].join('\n')
|
||||
);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
cleanup(tmpDir);
|
||||
});
|
||||
|
||||
test('init plan-phase includes auto_advance field (defaults false)', () => {
|
||||
const result = runGsdTools('init plan-phase 1', tmpDir);
|
||||
assert.ok(result.success, `Command failed: ${result.error}`);
|
||||
const output = JSON.parse(result.output);
|
||||
assert.ok('auto_advance' in output, 'init plan-phase must include auto_advance field');
|
||||
assert.strictEqual(output.auto_advance, false, 'auto_advance should default to false');
|
||||
});
|
||||
|
||||
test('init plan-phase includes auto_chain_active field (defaults false)', () => {
|
||||
const result = runGsdTools('init plan-phase 1', tmpDir);
|
||||
assert.ok(result.success, `Command failed: ${result.error}`);
|
||||
const output = JSON.parse(result.output);
|
||||
assert.ok('auto_chain_active' in output, 'init plan-phase must include auto_chain_active field');
|
||||
assert.strictEqual(output.auto_chain_active, false, 'auto_chain_active should default to false');
|
||||
});
|
||||
|
||||
test('init plan-phase includes mode field (defaults to interactive)', () => {
|
||||
const result = runGsdTools('init plan-phase 1', tmpDir);
|
||||
assert.ok(result.success, `Command failed: ${result.error}`);
|
||||
const output = JSON.parse(result.output);
|
||||
assert.ok('mode' in output, 'init plan-phase must include mode field');
|
||||
assert.strictEqual(output.mode, 'interactive', 'mode should default to interactive');
|
||||
});
|
||||
|
||||
test('init plan-phase reflects auto_advance true when set in config', () => {
|
||||
const configPath = path.join(tmpDir, '.planning', 'config.json');
|
||||
const cfg = { workflow: { auto_advance: true } };
|
||||
fs.writeFileSync(configPath, JSON.stringify(cfg, null, 2));
|
||||
|
||||
const result = runGsdTools('init plan-phase 1', tmpDir);
|
||||
assert.ok(result.success, `Command failed: ${result.error}`);
|
||||
const output = JSON.parse(result.output);
|
||||
assert.strictEqual(output.auto_advance, true, 'auto_advance should reflect config value');
|
||||
});
|
||||
|
||||
test('init plan-phase reflects auto_chain_active true when set in config', () => {
|
||||
const configPath = path.join(tmpDir, '.planning', 'config.json');
|
||||
const cfg = { workflow: { _auto_chain_active: true } };
|
||||
fs.writeFileSync(configPath, JSON.stringify(cfg, null, 2));
|
||||
|
||||
const result = runGsdTools('init plan-phase 1', tmpDir);
|
||||
assert.ok(result.success, `Command failed: ${result.error}`);
|
||||
const output = JSON.parse(result.output);
|
||||
assert.strictEqual(output.auto_chain_active, true, 'auto_chain_active should reflect config value');
|
||||
});
|
||||
});
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// roadmap analyze command
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
Reference in New Issue
Block a user