Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

- **`tests/integration/subagent-skill-preload.test.ts` is excluded from `npm run test:integration`** — before: `vitest.integration.config.ts` declared only an `include` glob, so the file was collected by every integration run, including CI, and no-op'd only where the `claude` binary was absent, through its own `describe.skipIf(!isClaudeAvailable())` guard; on a machine with `claude` installed it spawned live sessions. After: the config carries a real `exclude` entry. The test drives live `claude` sessions against the developer's own `~/.claude` with `--dangerously-skip-permissions` and has previously committed to this repo mid-run, so it is opt-in: set `DEVFLOW_INTEGRATION_ALL=1` to include it. A command-line path alone cannot re-add it — `exclude` is applied at glob time.

- **`pin-sonnet-4-6` and `disable-bundled-skills` now default OFF** — before: both flags were in the recommended set with `defaultValue: true`, so a fresh `devflow init` pinned `ANTHROPIC_DEFAULT_SONNET_MODEL` to `claude-sonnet-4-6` and wrote `disableBundledSkills: true` to settings.json, removing Claude Code's built-in skills and commands. After: both are optional flags defaulting to `false`; a fresh install leaves the Sonnet alias and Claude Code's bundled skills untouched. Existing installs keep whatever value their manifest already records (ADR-014 — re-init preserves existing flag values); opt in or out with `devflow flags --enable/--disable pin-sonnet-4-6` and `devflow flags --enable/--disable disable-bundled-skills`.

### Fixed

- **`/debug #42` wrong Git-op spawn key** — before: `debug.mds` passed `ISSUE: {issue number}` to the `fetch-issue` Git operation, which declares `ISSUE_INPUT:`; the key mismatch meant no issue was ever fetched. After: `debug.mds` passes `ISSUE_INPUT: {issue reference}` — the key the op declares. (AC-0.1)
Expand Down
2 changes: 1 addition & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ Debug logs stored at `~/.devflow/logs/{project-slug}/`.

**Debug Tracing**: Single global toggle covering all hooks. Enabled via `devflow debug --enable/--disable/--status` CLI or by setting `DEVFLOW_HOOK_DEBUG=1` in `~/.claude/settings.json` env block (survives reinstalls). All hooks share the `src/assets/scripts/hooks/debug-trace` helper script (sourced via `hook-bootstrap`) so tracing behavior is consistent and updated in one place. Two-phase logging: pre-CWD traces go to global `~/.devflow/logs/.hook-debug.log`; post-CWD traces go to per-project `~/.devflow/logs/{project-slug}/.hook-debug.log`. A 5MB size guard prevents unbounded growth. applies ADR-007

**Claude Code Flags**: Typed registry (`src/core/flags.ts`) for managing Claude Code feature flags (env vars and top-level settings). Four kinds: `boolean` (on/off — declared as `EnvBooleanFlagDef | SettingBooleanFlagDef`: env targets constrain `onPayload` to `string` and type `settingDeleteGuard` as `never`, setting targets allow object payloads and an optional `settingDeleteGuard`; enforced at registry declaration sites by object-literal assignability — a `target.type` check does not narrow `onPayload` for consumers, use `isEnvBooleanFlag`), `enum` (validated domain), `number` (bounded integer), `string` (validated with maxLength). 29 flags total: recommended (default ON) — `tui`, `tool-search`, `lsp`, `prompt-caching-1h`, `show-turn-duration`, `clear-context-on-plan`, `disable-bundled-skills`, `pin-sonnet-4-6`, `max-concurrent-subagents` (number, devflow default 40, upstream default 20); optional boolean (default OFF) — `brief`, `thinking-summaries`, `subprocess-env-scrub`, `disable-nonessential-traffic`, `forked-subagents`, `disable-adaptive-thinking`, `always-thinking`, `disable-git-instructions`, `disable-compact`, `disable-1m-context`, `disable-autoupdater`, `agent-teams`, `enable-todo-tools`, `suppress-attribution` (writes `{"commit":"","pr":""}` to settings.json — suppresses Claude attribution in git commits and PRs; shape-guarded deletion: only removed on disable when the value exactly matches the devflow-managed shape, never when a user has a custom attribution — the guard covers deletion only; enabling overwrites any existing `attribution` value (ADR-024 corollary b); D27/D-ATTR-GUARD); valued (default unset) — `subagent-spawn-depth` (number, upstream default 3), `workflow-size-guideline` (enum: `small|medium|large|unrestricted`), `default-model` (string), `goal-checkin-minutes` (number, upstream default 30 min), `spellcheck` (string), `view-mode` (enum: `default|verbose|focus`, devflow default `default`). Stored in manifest `features.flags: Record<flagId, value|null>` — entry-presence = known, `null` = deliberately unset (neutral, deletes the target key), absent = adopt-on-next-init. Pipeline: `applyFlags(settingsJson, FlagsRecord)` / `stripFlags(settingsJson)` — `applyViewMode`/`stripViewMode` retired; view-mode is an enum flag with `neutralValue: 'default'` (the `viewMode` settings.json key is written only when non-default); `resolveExistingViewMode`/`resolveFinalViewMode` remain exported for init.ts external-mode preservation. `devflow flags` bare on TTY launches the interactive flags editor TUI; bare on non-TTY prints a status table to stdout and exits 1. Registry entries carry a `blurb` field (≤30-char per-flag short hint) shown as a dim HINT column in the TUI and in `--status` rows. Display vocabulary via `effectiveDisplay`: booleans render 'on'/'off' (off is dim); neutral/unset enum shows `neutralValue` dim; unset number shows its applicable default dim with ' (default)' suffix; unset string shows '—' dim; an actively set non-boolean renders plain (at devflow default) or bold (deviating); the literal 'unset' is never a displayed value. TUI rendering: `RunTuiSpec.screen?: 'alt' | 'inline'`; flags editor runs inline (renders in-place in the normal scroll buffer, no alt-screen); agents-view defaults to alt. Manageable via `devflow flags --enable/--disable/--set <id=value>/--unset <ids>/--status/--list`; `--enable`/`--disable` are boolean-only — non-boolean flags are redirected to `--set`.
**Claude Code Flags**: Typed registry (`src/core/flags.ts`) for managing Claude Code feature flags (env vars and top-level settings). Four kinds: `boolean` (on/off — declared as `EnvBooleanFlagDef | SettingBooleanFlagDef`: env targets constrain `onPayload` to `string` and type `settingDeleteGuard` as `never`, setting targets allow object payloads and an optional `settingDeleteGuard`; enforced at registry declaration sites by object-literal assignability — a `target.type` check does not narrow `onPayload` for consumers, use `isEnvBooleanFlag`), `enum` (validated domain), `number` (bounded integer), `string` (validated with maxLength). 29 flags total: recommended (default ON) — `tui`, `tool-search`, `lsp`, `prompt-caching-1h`, `show-turn-duration`, `clear-context-on-plan`, `max-concurrent-subagents` (number, devflow default 40, upstream default 20); optional boolean (default OFF) — `disable-bundled-skills`, `pin-sonnet-4-6`, `brief`, `thinking-summaries`, `subprocess-env-scrub`, `disable-nonessential-traffic`, `forked-subagents`, `disable-adaptive-thinking`, `always-thinking`, `disable-git-instructions`, `disable-compact`, `disable-1m-context`, `disable-autoupdater`, `agent-teams`, `enable-todo-tools`, `suppress-attribution` (writes `{"commit":"","pr":""}` to settings.json — suppresses Claude attribution in git commits and PRs; shape-guarded deletion: only removed on disable when the value exactly matches the devflow-managed shape, never when a user has a custom attribution — the guard covers deletion only; enabling overwrites any existing `attribution` value (ADR-024 corollary b); D27/D-ATTR-GUARD); valued (default unset) — `subagent-spawn-depth` (number, upstream default 3), `workflow-size-guideline` (enum: `small|medium|large|unrestricted`), `default-model` (string), `goal-checkin-minutes` (number, upstream default 30 min), `spellcheck` (string), `view-mode` (enum: `default|verbose|focus`, devflow default `default`). Stored in manifest `features.flags: Record<flagId, value|null>` — entry-presence = known, `null` = deliberately unset (neutral, deletes the target key), absent = adopt-on-next-init. Pipeline: `applyFlags(settingsJson, FlagsRecord)` / `stripFlags(settingsJson)` — `applyViewMode`/`stripViewMode` retired; view-mode is an enum flag with `neutralValue: 'default'` (the `viewMode` settings.json key is written only when non-default); `resolveExistingViewMode`/`resolveFinalViewMode` remain exported for init.ts external-mode preservation. `devflow flags` bare on TTY launches the interactive flags editor TUI; bare on non-TTY prints a status table to stdout and exits 1. Registry entries carry a `blurb` field (≤30-char per-flag short hint) shown as a dim HINT column in the TUI and in `--status` rows. Display vocabulary via `effectiveDisplay`: booleans render 'on'/'off' (off is dim); neutral/unset enum shows `neutralValue` dim; unset number shows its applicable default dim with ' (default)' suffix; unset string shows '—' dim; an actively set non-boolean renders plain (at devflow default) or bold (deviating); the literal 'unset' is never a displayed value. TUI rendering: `RunTuiSpec.screen?: 'alt' | 'inline'`; flags editor runs inline (renders in-place in the normal scroll buffer, no alt-screen); agents-view defaults to alt. Manageable via `devflow flags --enable/--disable/--set <id=value>/--unset <ids>/--status/--list`; `--enable`/`--disable` are boolean-only — non-boolean flags are redirected to `--set`.

**Feature Knowledge Bases**: Per-feature `.devflow/features/` directory containing KNOWLEDGE.md files that capture area-specific patterns, conventions, architecture, and gotchas. Uses a **write-through** model: load = direct file-I/O reading `.devflow/features/index.md` (regenerable cache) with frontmatter-glob fallback over `features/*/KNOWLEDGE.md` (source of truth) + verify-against-code on read; save = in-command write-through via a simplified Knowledge agent that writes `KNOWLEDGE.md` + the `index.md` line directly (no `.create-result.json`, no external scripts, no lock). **Git-tracked & shared (amends ADR-021 for `features/`)**: the root `.gitignore` carve-out (`.devflow/*` + level-by-level `!` re-includes, written byte-identically by `ensure-root-gitignore` / `ensureDevflowGitignore`) un-ignores `.devflow/features/index.md` + every `{slug}/KNOWLEDGE.md` while the rest of `.devflow/` stays local; after writing, the **Knowledge agent commits those two paths to the current worktree branch itself** by running git via its Bash tool (scoped `commit --only` pathspec, never `git add -A`, **never push, never force**, no commit script — per the LLM-vs-plumbing principle the commit is the agent's, not a deterministic helper). A user opts back out by re-adding `.devflow/features/` to their own `.gitignore`. Existing installs upgrade once via the versioned `.root-gitignore-configured-v3` marker (v2→v3 adds the `!.devflow/conventions.md` re-include). Freshness = write-through + verify-on-read (NO git-staleness, NO SessionEnd eval, NO Learning task). `index.md` line format: `- **{slug}** — {areas} — {Use-when description}`; frontmatter is authoritative if the line is lost. MDS module: `src/assets/commands/_partials/_knowledge.mds` (defines/exports `knowledge_load` and `knowledge_writeback` partials) + 9 host `.mds` sources in `src/assets/commands/` compiled to `dist/commands/` by `scripts/build-mds.ts` (`npm run build:mds`). `knowledge_load` is used up-front by: implement, plan, resolve, code-review, self-review, research, bug-analysis. `knowledge_writeback` is used at workflow end by: implement, resolve, self-review, explore, debug. explore/debug do NOT load up-front (intentional asymmetry). Config gate: single `knowledge: true|false` in feature config (default true) — gates write-back only; load is ungated. CLI: `devflow knowledge list` (read index.md / frontmatter glob), `devflow knowledge --enable/--disable/--status` (flip config). Note: `/debug` keeps FEATURE_KNOWLEDGE orchestrator-local (investigation workers examine code without pre-loaded context). Toggleable via `devflow knowledge --enable/--disable/--status` or `devflow init --knowledge/--no-knowledge`.

Expand Down
4 changes: 2 additions & 2 deletions docs/cli-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -215,8 +215,8 @@ All 29 flags by kind and devflow default:
| `prompt-caching-1h` | boolean | env `ENABLE_PROMPT_CACHING_1H` | `true` |
| `show-turn-duration` | boolean | setting `showTurnDuration` | `true` |
| `clear-context-on-plan` | boolean | setting `showClearContextOnPlanAccept` | `true` |
| `disable-bundled-skills` | boolean | setting `disableBundledSkills` | `true` |
| `pin-sonnet-4-6` | boolean | env `ANTHROPIC_DEFAULT_SONNET_MODEL` | `true`¹ |
| `disable-bundled-skills` | boolean | setting `disableBundledSkills` | `false` |
| `pin-sonnet-4-6` | boolean | env `ANTHROPIC_DEFAULT_SONNET_MODEL` | `false`¹ |
| `max-concurrent-subagents` | number | env `CLAUDE_CODE_MAX_CONCURRENT_SUBAGENTS` | `40` (upstream: 20) |
| `brief` | boolean | env `CLAUDE_CODE_BRIEF` | `false` |
| `thinking-summaries` | boolean | setting `showThinkingSummaries` | `false` |
Expand Down
8 changes: 4 additions & 4 deletions src/core/flags.ts
Original file line number Diff line number Diff line change
Expand Up @@ -268,8 +268,8 @@ export const FLAG_REGISTRY: readonly ClaudeCodeFlag[] = [
kind: 'boolean',
target: { type: 'setting', key: 'disableBundledSkills' },
onPayload: true,
recommended: true,
defaultValue: true,
recommended: false,
defaultValue: false,
},
{
id: 'pin-sonnet-4-6',
Expand All @@ -280,8 +280,8 @@ export const FLAG_REGISTRY: readonly ClaudeCodeFlag[] = [
kind: 'boolean',
target: { type: 'env', key: 'ANTHROPIC_DEFAULT_SONNET_MODEL' },
onPayload: 'claude-sonnet-4-6',
recommended: true,
defaultValue: true,
recommended: false,
defaultValue: false,
},
{
// Devflow fan-outs routinely exceed the upstream default of 20.
Expand Down
4 changes: 2 additions & 2 deletions tests/flags.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -210,14 +210,14 @@ describe('getDefaultFlagsRecord', () => {
expect(record['prompt-caching-1h']).toBe(true);
expect(record['show-turn-duration']).toBe(true);
expect(record['clear-context-on-plan']).toBe(true);
expect(record['disable-bundled-skills']).toBe(true);
expect(record['pin-sonnet-4-6']).toBe(true);

// New recommended number flag
expect(record['max-concurrent-subagents']).toBe(40);

// Optional boolean flags (default OFF = false = neutral)
expect(record['brief']).toBe(false);
expect(record['disable-bundled-skills']).toBe(false);
expect(record['pin-sonnet-4-6']).toBe(false);
expect(record['thinking-summaries']).toBe(false);
expect(record['subprocess-env-scrub']).toBe(false);
expect(record['disable-nonessential-traffic']).toBe(false);
Expand Down
10 changes: 9 additions & 1 deletion tests/init-e2e-flags.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,15 @@ describe('init e2e — flags Phase 6 integration', () => {
// Other adopted default-ON flags materialise too (proves applyFlags ran over the
// whole adopted record, not just the one flag asserted above).
expect(env.ENABLE_TOOL_SEARCH).toBe('true');
expect(env.ANTHROPIC_DEFAULT_SONNET_MODEL).toBe('claude-sonnet-4-6');
expect(env.ENABLE_PROMPT_CACHING_1H).toBe('true');

// Adopted default-OFF flags write nothing: pin-sonnet-4-6 and disable-bundled-skills
// are optional (opt-in via `devflow flags --enable`), so a fresh adoption leaves
// the Sonnet alias and Claude Code's bundled skills untouched.
expect(flagsRecord['pin-sonnet-4-6']).toBe(false);
expect(flagsRecord['disable-bundled-skills']).toBe(false);
expect(env.ANTHROPIC_DEFAULT_SONNET_MODEL).toBeUndefined();
expect(settings).not.toHaveProperty('disableBundledSkills');

// Deliberate prior disables are PRESERVED, not re-adopted (ADR-014): the old manifest
// recorded knownFlags ['tui','lsp'] with an empty enabled list, so both stay off and
Expand Down
4 changes: 2 additions & 2 deletions tests/init-seed.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -156,10 +156,10 @@ describe('resolveSeedFlags', () => {
expect(result['prompt-caching-1h']).toBe(true);
expect(result['show-turn-duration']).toBe(true);
expect(result['clear-context-on-plan']).toBe(true);
expect(result['disable-bundled-skills']).toBe(true);
expect(result['pin-sonnet-4-6']).toBe(true);
// Default-OFF boolean flags are false
expect(result['brief']).toBe(false);
expect(result['disable-bundled-skills']).toBe(false);
expect(result['pin-sonnet-4-6']).toBe(false);
// Number flag with non-neutral default is present
expect(result['max-concurrent-subagents']).toBe(40);
// view-mode default is 'default' (neutralValue for the enum)
Expand Down