devflow-kit 3.1.0 → 3.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +52 -0
- package/README.md +2 -2
- package/dist/cli/agents-view/render.js +69 -15
- package/dist/cli/agents-view/state.js +40 -14
- package/dist/cli/commands/agents.js +135 -45
- package/dist/cli/commands/init.js +128 -53
- package/dist/cli/commands/learning.js +61 -13
- package/dist/cli/commands/memory.js +35 -14
- package/dist/cli/commands/uninstall.js +163 -39
- package/dist/commands/code-review.md +1 -3
- package/dist/commands/debug.md +15 -12
- package/dist/commands/dynamic-build.md +172 -135
- package/dist/commands/dynamic-plan.md +9 -3
- package/dist/commands/explore.md +10 -4
- package/dist/commands/implement.md +149 -145
- package/dist/commands/plan.md +13 -9
- package/dist/commands/release.md +8 -2
- package/dist/commands/research.md +8 -2
- package/dist/commands/resolve.md +28 -19
- package/dist/commands/self-review.md +16 -13
- package/dist/core/agent-frontmatter.js +25 -0
- package/dist/core/agent-models.js +201 -36
- package/dist/core/agent-state.js +27 -5
- package/dist/core/assets.js +1 -1
- package/dist/core/feature-config.js +68 -10
- package/dist/core/flags.js +24 -0
- package/dist/core/learning-queue-cleanup.js +10 -11
- package/dist/core/learning-tuning-config.js +8 -0
- package/dist/core/linked-path.js +46 -0
- package/dist/core/plugins.js +16 -5
- package/dist/core/queue-drain.js +31 -0
- package/dist/hud/components/learning-counts.js +54 -8
- package/dist/skills/git/references/tracker/github/create-release.md +2 -2
- package/dist/skills/git/references/tracker/github/gather-release-evidence.md +1 -1
- package/dist/skills/git/references/tracker/jira/create-release.md +2 -2
- package/dist/skills/git/references/tracker/jira/gather-release-evidence.md +1 -1
- package/dist/skills/git/references/tracker/linear/create-release.md +2 -2
- package/dist/skills/git/references/tracker/linear/gather-release-evidence.md +1 -1
- package/dist/targets/claude-code/installer.js +36 -9
- package/dist/targets/claude-code/post-install.js +128 -38
- package/package.json +1 -1
- package/src/assets/agents/code.md +85 -35
- package/src/assets/agents/design.md +12 -0
- package/src/assets/agents/diagnose.md +18 -11
- package/src/assets/agents/evaluate.md +17 -24
- package/src/assets/agents/knowledge.md +7 -3
- package/src/assets/agents/learning.md +4 -6
- package/src/assets/agents/research.md +21 -0
- package/src/assets/agents/review.md +12 -0
- package/src/assets/agents/scrutinize.md +37 -9
- package/src/assets/agents/simplify.md +24 -0
- package/src/assets/agents/skim.md +6 -2
- package/src/assets/agents/synthesize.md +18 -0
- package/src/assets/agents/test.md +19 -11
- package/src/assets/agents/triage.md +8 -0
- package/src/assets/agents/validate.md +20 -11
- package/src/assets/commands/_partials/_engine.mds +36 -55
- package/src/assets/commands/_partials/_knowledge.mds +1 -3
- package/src/assets/commands/_partials/_plan_contract.mds +1 -1
- package/src/assets/commands/_partials/_tracker.mds +1 -1
- package/src/assets/commands/_partials/_wave.mds +8 -6
- package/src/assets/commands/code-review.mds +1 -3
- package/src/assets/commands/debug.mds +13 -8
- package/src/assets/commands/dynamic-build.mds +126 -72
- package/src/assets/commands/dynamic-plan.mds +7 -1
- package/src/assets/commands/explore.mds +9 -1
- package/src/assets/commands/implement.mds +147 -141
- package/src/assets/commands/plan.mds +12 -8
- package/src/assets/commands/release.md +8 -2
- package/src/assets/commands/research.mds +8 -2
- package/src/assets/commands/resolve.mds +27 -16
- package/src/assets/commands/self-review.mds +15 -10
- package/src/assets/mds/tracker/_common.mds +1 -1
- package/src/assets/mds/tracker/_github.mds +2 -2
- package/src/assets/mds/tracker/_jira.mds +2 -2
- package/src/assets/mds/tracker/_linear.mds +2 -2
- package/src/assets/scripts/ci-wait.cjs +636 -0
- package/src/assets/scripts/hooks/assets/orchestrator-charter.md +4 -3
- package/src/assets/scripts/hooks/background-memory-update +356 -17
- package/src/assets/scripts/hooks/capture-prompt +4 -3
- package/src/assets/scripts/hooks/capture-question +4 -3
- package/src/assets/scripts/hooks/capture-turn +4 -3
- package/src/assets/scripts/hooks/ensure-devflow-init +13 -1
- package/src/assets/scripts/hooks/ensure-root-gitignore +122 -10
- package/src/assets/scripts/hooks/git-marker +71 -0
- package/src/assets/scripts/hooks/json-helper.cjs +12 -145
- package/src/assets/scripts/hooks/json-parse +24 -129
- package/src/assets/scripts/hooks/lib/learning-store.cjs +169 -64
- package/src/assets/scripts/hooks/lib/render-decisions.cjs +1 -1
- package/src/assets/scripts/hooks/memory-worker +10 -0
- package/src/assets/scripts/hooks/pre-compact-memory +66 -14
- package/src/assets/scripts/hooks/preamble +9 -1
- package/src/assets/scripts/hooks/queue-append +53 -21
- package/src/assets/scripts/hooks/session-start-context +108 -29
- package/src/assets/scripts/hooks/session-start-memory +33 -11
- package/src/assets/scripts/release-trace.cjs +27 -10
- package/src/assets/skills/accessibility/SKILL.md +1 -1
- package/src/assets/skills/apply-decisions/SKILL.md +12 -82
- package/src/assets/skills/apply-feature-knowledge/SKILL.md +8 -42
- package/src/assets/skills/architecture/SKILL.md +1 -1
- package/src/assets/skills/boundary-validation/SKILL.md +1 -1
- package/src/assets/skills/complexity/SKILL.md +1 -1
- package/src/assets/skills/compliance/SKILL.md +1 -1
- package/src/assets/skills/consistency/SKILL.md +1 -1
- package/src/assets/skills/database/SKILL.md +1 -1
- package/src/assets/skills/dependencies/SKILL.md +1 -1
- package/src/assets/skills/dependency-research/SKILL.md +3 -6
- package/src/assets/skills/design-review/SKILL.md +1 -1
- package/src/assets/skills/docs-framework/SKILL.md +1 -1
- package/src/assets/skills/documentation/SKILL.md +1 -1
- package/src/assets/skills/gap-analysis/SKILL.md +1 -1
- package/src/assets/skills/git/SKILL.md +1 -1
- package/src/assets/skills/go/SKILL.md +1 -1
- package/src/assets/skills/java/SKILL.md +1 -1
- package/src/assets/skills/patterns/SKILL.md +1 -1
- package/src/assets/skills/performance/SKILL.md +1 -1
- package/src/assets/skills/python/SKILL.md +1 -1
- package/src/assets/skills/qa/SKILL.md +1 -3
- package/src/assets/skills/quality-gates/SKILL.md +9 -12
- package/src/assets/skills/quality-gates/references/report-template.md +20 -20
- package/src/assets/skills/react/SKILL.md +1 -1
- package/src/assets/skills/regression/SKILL.md +1 -1
- package/src/assets/skills/reliability/SKILL.md +1 -1
- package/src/assets/skills/research-codebase/SKILL.md +1 -1
- package/src/assets/skills/research-competitor/SKILL.md +1 -1
- package/src/assets/skills/research-external/SKILL.md +1 -1
- package/src/assets/skills/research-technology/SKILL.md +1 -1
- package/src/assets/skills/review-methodology/SKILL.md +1 -1
- package/src/assets/skills/rust/SKILL.md +1 -1
- package/src/assets/skills/security/SKILL.md +1 -1
- package/src/assets/skills/software-design/SKILL.md +1 -1
- package/src/assets/skills/test-driven-development/SKILL.md +15 -33
- package/src/assets/skills/testing/SKILL.md +1 -1
- package/src/assets/skills/typescript/SKILL.md +1 -1
- package/src/assets/skills/ui-design/SKILL.md +1 -1
- package/src/assets/skills/worktree-support/SKILL.md +3 -55
- package/src/assets/skills/worktree-support/references/discovery.md +48 -0
- package/src/assets/skills/worktree-support/references/roots.md +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,56 @@ All notable changes to Devflow will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [3.3.0] - 2026-10-09
|
|
9
|
+
|
|
10
|
+
### Changed
|
|
11
|
+
|
|
12
|
+
- **Agents ship chosen tiers, efforts and tool sets** ([#422](https://github.com/dean0x/devflow/issues/422)). Fifteen agents now carry a deliberate model, effort, tool policy and `omitClaudeMd` setting, held by a guard to one table. Skim moves from `sonnet` to `haiku`, the only model change. Every agent except Learning, Git and Tracker now ships an effort and no longer follows your session's effort: `high` for Code, Design, Review and Triage, `medium` for the rest. `devflow agents --set <agent> --effort inherit` restores the old behaviour for one agent. Review ships `opus` at `high` effort: an A/B run of the `/code-review` focus agents over two seeded PRs found every seeded bug at `high` that it found at `xhigh`, for about 43% lower cost. Learning keeps its model, its absent effort and its CLAUDE.md context: the cheaper candidates fell short of the capture bar, and without that context it stops retiring entries as Encoded. Code, Research, Scrutinize and Simplify deny one shared list of orchestration and host-UI tools (Scrutinize and Simplify add `Skill`) and keep their MCP and web access; every other agent holds an allowlist. Synthesize and Skim omit CLAUDE.md. Preloads leave where the body never reads them: `testing` from Validate and Test, `software-design` from Evaluate, the five pattern skills from Diagnose, which now loads its focus's skills on demand, and `apply-decisions` from Learning. `devflow learning --configure` marks as recommended the model the Learning agent ships, read from its frontmatter, so a change of the shipped tier moves the recommendation with it.
|
|
13
|
+
- **The gates run in a new order, and a script waits on CI** ([#421](https://github.com/dean0x/devflow/issues/421)). `/implement` now runs Simplify, Scrutinize, Evaluate, then one Validate over the final HEAD, then Test, then pushes and gates CI; a changed-only Validate follows only when a QA fix committed. The alignment and QA loops no longer spawn a Validate of their own, and `/self-review` validates whenever HEAD moved, so a Simplify-only commit is no longer skipped. `ci-wait.cjs` is a head-bound foreground CI wait that replaces re-spawning the Git agent once a minute: it binds the verdict to the pushed head, waits inside node and prints one line. One wait is capped at 570 seconds, and each CI gate allows at most 3 waits and 2 fixes, spawning Code only on a failing verdict. Scrutinize reports one status vocabulary, PASS, FIXED or BLOCKED (READY is gone), and each gate duty has one owner: Naming and Consistency belong to Simplify. In the dynamic-build engine both Gate 1 passes end with Validate. The Git agent now merges locally and validates nothing; the workflow runs a post-merge Validate after every merge, counts the merge as kept only on PASS, and undoes a red merge with `reset --keep`; a refused undo halts the wave and names the red HEAD, and a first Gate 1 that ends ESCALATED or BLOCKED returns early. `/resolve` waits on CI through the same script.
|
|
14
|
+
- **Skills cost less** ([#423](https://github.com/dean0x/devflow/issues/423)). Code preloads six skills (`git`, `testing`, `test-driven-development`, `worktree-support`, `apply-feature-knowledge` and `apply-decisions`) where it preloaded ten: 25,578 bytes of skill text, down from 53,450. `software-design`, `patterns`, `boundary-validation` and `dependency-research` load on demand from a table with one row per operating mode. Every Code spawn now opens its prompt with `OPERATION: <mode>`, including two new modes, `ci-fix` (fix the failing CI checks the gate names) and `edit` (a mechanical change that adds no behaviour), and the orchestrator charter's direct Code delegations do the same. `apply-decisions` reads an entry by finding its heading with `command grep -nF` and then Reading only that section by offset, instead of opening the whole file. `worktree-support` keeps its discovery algorithm in a reference that `/code-review` and `/resolve` read when they need it, rather than in every agent's preload. Skill descriptions are capped by who reads them, at 90 characters for the agent-internal skills and 200 for the user-trigger ones, and 38 shrank. The Git agent's loaded sets are 1,704 characters smaller.
|
|
15
|
+
- **The memory worker reads `agents.memory` and runs lean** ([#424](https://github.com/dean0x/devflow/issues/424)). `background-memory-update` takes its model and effort from `agents.memory` (`devflow agents --set memory ...`), checking each field on its own and falling back to the shipped default for one it cannot use. A bounded `claude --version` probe picks the argv. On Claude Code 2.1.286 or later the worker runs with `--safe-mode`, `--tools Write`, an empty strict MCP config, `--max-turns 3` and an explicit `--effort`, with any inherited `CLAUDE_CODE_EFFORT_LEVEL` cleared so the effort it names is the one that applies; below that, or when the probe fails, it runs a legacy argv that still limits the worker to Write and keeps CLAUDE.md and auto-memory out, but passes no effort, safe mode, MCP isolation or turn cap. A staged file that cannot be removed stops the run before any model call, which is what makes a Write-only tool set safe.
|
|
16
|
+
|
|
17
|
+
### Fixed
|
|
18
|
+
|
|
19
|
+
- **Only closing keywords ship an issue in a release.** `Closes`, `Fixes` and `Resolves` ship an issue; a `Refs #N` commit mentions one without closing it, yet it reached the release's Shipped Issues, its back-links and its milestone. It no longer does. The release trace map still counts `Refs` as traced, because that map asks only whether a commit names an issue at all.
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## [3.2.0] - 2026-10-08
|
|
24
|
+
|
|
25
|
+
### Added
|
|
26
|
+
|
|
27
|
+
- **`devflow flags` gains `bash-max-timeout-ms`** ([#418](https://github.com/dean0x/devflow/issues/418)). It sets `BASH_MAX_TIMEOUT_MS`, the ceiling on a foreground Bash command's `timeout` (600000 ms upstream), to a whole number from 600000 to 7200000, and it is unset by default. A run that cannot be split under the ceiling is reported BLOCKED with `devflow flags --set bash-max-timeout-ms=<ms>` as the remedy. The registry now holds 30 flags.
|
|
28
|
+
|
|
29
|
+
### Changed
|
|
30
|
+
|
|
31
|
+
- **`devflow agents` carries a shipped effort, takes an `inherit` effort, and sets the memory worker** ([#420](https://github.com/dean0x/devflow/issues/420)). Nothing changes at shipped defaults: no agent ships an `effort:` line yet and every installed `model:` is as before. This is the plumbing the agent-tier tickets build on.
|
|
32
|
+
- **A shipped effort survives a reapply.** A shipped agent default is now its `model:` and its `effort:` when it has one, read from the same file. A reapply used to remove an `effort:` line from every agent whose mapping had no effort, including one that ships it.
|
|
33
|
+
- **`--effort inherit` drops an agent's effort line** so it follows the session; `--effort default` removes your override and restores the shipped effort. The EFFORT column reads `default (<shipped effort>)` when an agent ships one.
|
|
34
|
+
- **`memory` is settable in `devflow agents`** (`--set memory --model sonnet --effort medium`), listed after the agents with state `worker`. It takes Claude models and effort levels only, is not an agent, and is left out of the agent counts.
|
|
35
|
+
- **A `devflow agents` Learning mapping now decides the Learning model.** The session-start directive used to pass `model="opus"` (or the `learning.json` model) on every spawn, which overrode any mapping. It now takes the first of: a valid project `learning.json` model, the mapping (which sends no model, so the installed frontmatter decides), a valid global `learning.json` model, none. An invalid `learning.json` value falls through instead of becoming `opus`. `devflow learning --configure` says that a mapping outranks the global file.
|
|
36
|
+
- **A user's `validate` override now reaches the five Validate spawns** in `/implement` and `/resolve`, which passed `model="haiku"`. A new guard fails on any spawn in the prompt sources that names a model.
|
|
37
|
+
- **Commands carry less context** ([#419](https://github.com/dean0x/devflow/issues/419)). Claude Code copies a command's arguments into the prompt at every placeholder, so each command now binds them once, in a `<command-input>` block, and names the bound text `COMMAND_INPUT` afterwards: the compiled commands went from 21 placeholders to 8, and a guard holds the count. A plan handoff now invokes `devflow:implement` with no arguments, because the plan is already in the conversation; with no input, `/implement` names the branch from the plan's title. The orchestrator charter gains a bounded-inline exception (one `git`, `gh` or script command whose output stays under about 40 lines, never a diff, log or test run) and a delegation report cap of about 1,500 tokens. `/implement`, `/code-review`, `/debug` and `/plan` stop loading companion skills their main thread never uses, and the plan and code-review plugins stop requiring the skills only those lines needed. The built-in Explore and Plan spawns in `/explore`, `/debug` and `/plan` ask for a report of at most about 1,500 tokens.
|
|
38
|
+
- **Agents run builds and tests in the foreground, and the full test suite has one owner** ([#418](https://github.com/dean0x/devflow/issues/418)). The Code, Validate and Test agents ran any build that might exceed two minutes in the background and polled it with Monitor, and the dynamic-build engine and prompts repeated the procedure. A silent 250-second foreground run survives inside a Workflow sub-agent, and the platform's stall watchdog fires on model silence after a tool returns, never while a command runs, so polling only added turns. The three bodies now share one `## Running commands` block: an explicit Bash timeout, capture-then-tail in one call, no backgrounding or polling, a scoped command, and BLOCKED with duration and log path on an overrun. A parity guard holds the copies identical, and the engine and the nine dynamic-build prompt sites name the block. Each of the six gate agents carries one `**Gate ownership:**` row: Validate runs the full suite once per HEAD and no other agent does, Code runs targeted tests plus one affected-tests run, and the TDD skill defines "affected tests". `docs/reference/platform-assumptions.md` records what was measured, with the stall investigation.
|
|
39
|
+
- **Every roster agent caps its final message at about 1,500 tokens** ([#418](https://github.com/dean0x/devflow/issues/418)). The 14 roster agents end their Output section with a `Report cap:` line. Longer material goes to a file whose path the message gives, and the fields a command or test parses, or an orchestrator hands to another agent, stay inline in full: Validate's `HEAD:` line and command table, Test's plan evidence and scenario table, Triage's whole ledger and the rest. Validate cuts failure output to 30 lines per failing command and then names the log. The Git agent keeps its output unchanged until its split.
|
|
40
|
+
- **The memory worker runs on Sonnet 5.5 at high effort.** `background-memory-update` now spawns `claude -p --model claude-sonnet-5-5` (it was `claude-sonnet-4-6`), and the `memory` row in `devflow agents` ships `claude-sonnet-5-5` at `high` effort (it was `haiku`), so the shipped default and the model the worker runs are the same value; a test holds the two equal. Memory quality was judged worth more than the saving from Haiku. The worker does not read an `agents.memory` override or pass `--effort` yet. `devflow agents --list` widens its DEFAULT column to the longest shipped default, so a full model identifier is no longer cut.
|
|
41
|
+
|
|
42
|
+
### Fixed
|
|
43
|
+
|
|
44
|
+
- **`devflow init` no longer reports removing skills that were never installed** ([#432](https://github.com/dean0x/devflow/issues/432)). The "Removed N skill(s) no selected plugin requires" line now names only skills that were present and actually removed, and is omitted when none were.
|
|
45
|
+
- **`devflow agents` no longer cuts a full model identifier it displays.** `--list` cut the CONFIGURED column at 16 characters, so a mapped `claude-sonnet-4-6` read as `claude-sonnet-4-`; the column now grows to the longest configured value, as DEFAULT does. In the interactive editor, the focused `‹ default (claude-sonnet-5-5) ›` cell on the `memory` row overflowed its column and lost its closing arrow and the unsaved mark; the cell now narrows its own arrows to fit and keeps both.
|
|
46
|
+
- **The Knowledge agent is no longer told to load a skill it already has** ([#419](https://github.com/dean0x/devflow/issues/419)). It preloads `devflow:feature-knowledge` and has no Skill tool, yet the write-back step and `/research` still told it to load that skill.
|
|
47
|
+
- **A re-init reports what it actually did** ([#415](https://github.com/dean0x/devflow/issues/415)). Running `devflow init` again printed `.claudeignore: created` even when nothing changed, and a run without a terminal told you to "Run interactively to auto-install" safe-delete even when it was already set up. The `.claudeignore` row now reads `created`, `already present` or `skipped`, and the safe-delete line reads installed, upgraded or already configured. The hint is gone: a run without a terminal takes the Recommended path, which installs or upgrades the safe-delete block itself.
|
|
48
|
+
- **The Skim agent finds the decisions ledger from a linked worktree** ([#415](https://github.com/dean0x/devflow/issues/415)). It looked for the ledger in the directory it ran in, so in a linked worktree it reported no decisions. It now reads the main worktree's ledger, as the other agents do.
|
|
49
|
+
- **Without `jq`, the hooks' JSON helpers stay silent on stderr, as they already were with it** ([#415](https://github.com/dean0x/devflow/issues/415)). On a machine without `jq`, a hook that read a missing or unparseable file printed a `json-helper error` line, or the shell's own error for a file it could not open, where the `jq` path prints nothing. The `node` fallbacks now discard that output too, and return exactly what they did before.
|
|
50
|
+
- **The captured-turns files stay owner-only after a trim** ([#415](https://github.com/dean0x/devflow/issues/415)). Once a memory or learning queue, or a memory batch a failed refresh left behind, passed 200 turns, the trim to the newest 100 replaced it with a copy made under the hook's umask, so the file came back readable by every user (`0644`) while it holds conversation text. The copy is now made `0600`, the mode the queue is created with.
|
|
51
|
+
- **A corrupt working-memory backup no longer stops the session-start memory hook** ([#415](https://github.com/dean0x/devflow/issues/415)). A truncated or unparseable `.devflow/memory/backup.json` ended the hook before it injected the working memory it had already built. Such a backup is now read as no snapshot, like a missing one.
|
|
52
|
+
- **The working-memory backup is written atomically** ([#415](https://github.com/dean0x/devflow/issues/415)). The pre-compact hook wrote `backup.json` by truncating it and writing it again, so a session start reading it in between could meet a partial file. It now writes a complete copy beside it and renames the copy into place, owner-only (`0600`); a write that fails leaves the previous backup as it was. The copy is created only where nothing already stands, so it never writes through a symbolic link, and each backup first removes any copy a killed run left behind that is over an hour old.
|
|
53
|
+
- **The hooks no longer write, or read into the session, through a symbolic link inside a project's `.devflow` folder** ([#415](https://github.com/dean0x/devflow/issues/415)). The capture and memory hooks skip, and log, any write whose file, or a folder on the way to it, is a link, so a repository cannot point captured conversation text or working memory at a file outside it. They also never read a linked file there into the session or a backup: a linked working memory, backup or decisions file is treated as missing. The learning store no longer reads a linked file either: a linked log, ledger, archive or history file is treated as missing, so nothing of it is listed, shown, backed up or copied into the project. The statusline's learning counts skip a ledger that is a link or larger than 8 MiB, as they skip a missing one. The hooks write nothing under a `.devflow` that is itself a link, and every learning operation that writes, `devflow learning --reset` included, refuses a linked `.devflow` or `.devflow/learning` folder, changing nothing. `devflow init`, the `.gitignore` hook, `devflow learning --configure` and turning learning or memory off hold to the same rule: none of them writes or deletes through a link that leads outside the project, and each says so when it skips one. The session-start hook, the only one that reaches the `.gitignore` step when memory and learning are both off, now writes those skips to its log; it used to record them only in debug mode. `devflow uninstall` holds to the rule too when it removes a legacy project-local install: it deletes and rewrites nothing through a link in the project's `.devflow` or `.claude`, removes the rest, and names each link it skipped. A root `.gitignore` linked to a file inside the project is still updated when no part of that file's path there is named `.git`, in any letter case, so a link into the project's own `.git` folder or a nested repository's is refused.
|
|
54
|
+
- **The release notes name their issue section `Shipped Issues` on every provider** ([#415](https://github.com/dean0x/devflow/issues/415)). The GitHub, Jira and Linear release mechanics told the release agent to append `## Closed Issues`, while the shipped-issues input, the back-link step and every published release say Shipped Issues. All three now name the section `## Shipped Issues`.
|
|
55
|
+
|
|
56
|
+
---
|
|
57
|
+
|
|
8
58
|
## [3.1.0] - 2026-10-06
|
|
9
59
|
|
|
10
60
|
### Changed
|
|
@@ -1482,6 +1532,8 @@ devflow init
|
|
|
1482
1532
|
---
|
|
1483
1533
|
|
|
1484
1534
|
[Unreleased]: https://github.com/dean0x/devflow/compare/v2.0.0...HEAD
|
|
1535
|
+
[3.3.0]: https://github.com/dean0x/devflow/compare/v3.2.0...v3.3.0
|
|
1536
|
+
[3.2.0]: https://github.com/dean0x/devflow/compare/v3.1.0...v3.2.0
|
|
1485
1537
|
[3.1.0]: https://github.com/dean0x/devflow/compare/v3.0.1...v3.1.0
|
|
1486
1538
|
[3.0.1]: https://github.com/dean0x/devflow/compare/v3.0.0...v3.0.1
|
|
1487
1539
|
[3.0.0]: https://github.com/dean0x/devflow/compare/v2.5.0...v3.0.0
|
package/README.md
CHANGED
|
@@ -30,9 +30,9 @@ you: /implement (hand it the plan)
|
|
|
30
30
|
|
|
31
31
|
Git branch feat/42-rate-limit-upload
|
|
32
32
|
Code implements the plan — your learned decisions, pitfalls, and feature knowledge preloaded
|
|
33
|
-
Validate build ✓ typecheck ✓ lint ✓ tests ✓
|
|
34
33
|
Simplify · Scrutinize cleanup pass, then 9-pillar quality gate
|
|
35
34
|
Evaluate implementation matches the original request ✓
|
|
35
|
+
Validate build ✓ typecheck ✓ lint ✓ tests ✓
|
|
36
36
|
Test 5/5 QA scenarios pass → PR opened
|
|
37
37
|
|
|
38
38
|
you: /code-review
|
|
@@ -51,7 +51,7 @@ This is the **orchestrated flow** — you stay in the loop between every step. W
|
|
|
51
51
|
|
|
52
52
|
## What you get
|
|
53
53
|
|
|
54
|
-
**Ambient orchestration.** Your main session becomes the tech lead: a charter injected at session start turns it into
|
|
54
|
+
**Ambient orchestration.** Your main session becomes the tech lead: a charter injected at session start turns it into an orchestrator that delegates work to specialized agents and keeps judgment mainline, with one bounded inline exception for a single short git, gh or script command. Plan-mode handoffs auto-run `/implement`. Init and forget.
|
|
55
55
|
|
|
56
56
|
**A staffed agent roster.** 17 specialized agents with explicit model assignments — Opus for analysis, Sonnet for execution, Haiku for I/O. Reassign any agent's model with `devflow agents`, including GPT models through external model routing (`devflow proxy`).
|
|
57
57
|
|
|
@@ -15,17 +15,29 @@
|
|
|
15
15
|
* -1 Unsaved count " N unsaved changes" (blank if 0)
|
|
16
16
|
* 0 Keybinding footer
|
|
17
17
|
*
|
|
18
|
-
* Columns (chars) — total
|
|
18
|
+
* Columns (chars) — total 80 ≤ 80:
|
|
19
19
|
* PREFIX : 2 (cursor mark "❯ " or " ")
|
|
20
|
-
* AGENT :
|
|
21
|
-
* MODEL :
|
|
22
|
-
* EFFORT :
|
|
20
|
+
* AGENT : 12
|
|
21
|
+
* MODEL : 30
|
|
22
|
+
* EFFORT : 22
|
|
23
23
|
* STATE : 14
|
|
24
|
+
*
|
|
25
|
+
* EFFORT is 22 wide so the longest unconfigured cell, "default (medium)" (16),
|
|
26
|
+
* fits whole inside the cursor's "‹ … ›" wrapper with the dirty marker
|
|
27
|
+
* ("‹ default (medium) ● ›", 22) — the shipped effort (D-SHIPPED-EFFORT) and the
|
|
28
|
+
* unsaved mark are never clipped on the cursor row. The agent names the registry
|
|
29
|
+
* holds are at most 10 characters ("Scrutinize"); longer orphan keys are
|
|
30
|
+
* truncated by truncateVisible.
|
|
31
|
+
*
|
|
32
|
+
* MODEL has no such slack for a worker row, whose shipped default is a full
|
|
33
|
+
* model identifier: the focused cell narrows its own wrapper to fit
|
|
34
|
+
* (D-FOCUSED-CELL-FITS, renderFocusedCell) rather than taking a column from
|
|
35
|
+
* the 80-column budget.
|
|
24
36
|
*/
|
|
25
|
-
import { bold, dim, green, yellow, cyan, gray, stripAnsi, } from '../../core/ansi.js';
|
|
37
|
+
import { bold, dim, green, yellow, cyan, gray, stripAnsi, truncate, } from '../../core/ansi.js';
|
|
26
38
|
import { padToVisible, truncateVisible, sanitizeCell } from '../tui/cells.js';
|
|
27
39
|
import { isDirtyModel, isDirtyEffort, unsavedCount, isOffCycle, rowState, } from './state.js';
|
|
28
|
-
import { AGENT_STATE_LABELS } from '../../core/agent-state.js';
|
|
40
|
+
import { AGENT_STATE_LABELS, formatEffortDisplay } from '../../core/agent-state.js';
|
|
29
41
|
// ---------------------------------------------------------------------------
|
|
30
42
|
// Layout constants
|
|
31
43
|
// ---------------------------------------------------------------------------
|
|
@@ -39,9 +51,9 @@ const MIN_VIEWPORT = 1;
|
|
|
39
51
|
export function computeViewportHeight(termRows) {
|
|
40
52
|
return Math.max(MIN_VIEWPORT, termRows - FIXED_ROWS);
|
|
41
53
|
}
|
|
42
|
-
const COL_AGENT =
|
|
43
|
-
const COL_MODEL =
|
|
44
|
-
const COL_EFFORT =
|
|
54
|
+
const COL_AGENT = 12;
|
|
55
|
+
const COL_MODEL = 30;
|
|
56
|
+
const COL_EFFORT = 22;
|
|
45
57
|
const COL_STATE = 14;
|
|
46
58
|
// ---------------------------------------------------------------------------
|
|
47
59
|
// Name formatter — TUI only
|
|
@@ -64,6 +76,41 @@ export function formatAgentName(name) {
|
|
|
64
76
|
.map(seg => (seg.length === 0 ? seg : seg[0].toUpperCase() + seg.slice(1)))
|
|
65
77
|
.join('-');
|
|
66
78
|
}
|
|
79
|
+
// ---------------------------------------------------------------------------
|
|
80
|
+
// Cell renderers (pure, return styled string)
|
|
81
|
+
// ---------------------------------------------------------------------------
|
|
82
|
+
/**
|
|
83
|
+
* Wrap the focused field's value in the cursor arrows, with the unsaved mark
|
|
84
|
+
* after the value when the field is dirty.
|
|
85
|
+
*
|
|
86
|
+
* D-FOCUSED-CELL-FITS: the arrows and the unsaved mark are never clipped. A
|
|
87
|
+
* column is sized for the common value, but a worker row ships a full model
|
|
88
|
+
* identifier ("default (claude-sonnet-5-5)" is 27 characters), so the spaced
|
|
89
|
+
* wrapper "‹ default (claude-sonnet-5-5) ● ›" is 33 in a 30-wide MODEL cell and
|
|
90
|
+
* the closing arrow and the mark would fall off the end. The layout already
|
|
91
|
+
* spends all 80 columns and no other column has slack (EFFORT needs 22 for
|
|
92
|
+
* "‹ default (medium) ● ›", STATE needs 14 for "saved-inactive"), so the cell
|
|
93
|
+
* narrows itself instead. The widest form that fits wins:
|
|
94
|
+
* spaced "‹ value ● ›"
|
|
95
|
+
* tight "‹value●›" — the padding spaces go, the value stays whole
|
|
96
|
+
* clipped "‹valu…●›" — only a value wider than the column; the
|
|
97
|
+
* value gives way, never the wrapper
|
|
98
|
+
*
|
|
99
|
+
* Pure function, no I/O.
|
|
100
|
+
*/
|
|
101
|
+
function renderFocusedCell(value, dirty, maxWidth) {
|
|
102
|
+
const valueWidth = stripAnsi(value).length;
|
|
103
|
+
const mark = dirty ? '●' : '';
|
|
104
|
+
const spacedWidth = valueWidth + 4 + (dirty ? 2 : 0);
|
|
105
|
+
if (spacedWidth <= maxWidth) {
|
|
106
|
+
return cyan(`‹ ${value}${dirty ? ` ${mark}` : ''} ›`);
|
|
107
|
+
}
|
|
108
|
+
const tightBudget = Math.max(1, maxWidth - 2 - mark.length);
|
|
109
|
+
if (valueWidth <= tightBudget) {
|
|
110
|
+
return cyan(`‹${value}${mark}›`);
|
|
111
|
+
}
|
|
112
|
+
return cyan(`‹${truncate(stripAnsi(value), tightBudget)}${mark}›`);
|
|
113
|
+
}
|
|
67
114
|
/**
|
|
68
115
|
* Render the model cell for a given row, considering cursor/active/dirty state.
|
|
69
116
|
*
|
|
@@ -95,7 +142,10 @@ function renderModelCell({ row, isCursor, isActive, maxWidth, modelCycle, }) {
|
|
|
95
142
|
valueStr += ` ${dim(`${safeDormantModel} saved`)}`;
|
|
96
143
|
}
|
|
97
144
|
}
|
|
98
|
-
else if (isOffCycle(modelCycle, row.configuredModel)) {
|
|
145
|
+
else if (!row.worker && isOffCycle(modelCycle, row.configuredModel)) {
|
|
146
|
+
// A worker row is exempt: its model is never judged against the catalog, and
|
|
147
|
+
// a full claude- identifier, which no cycle lists, is in the worker domain
|
|
148
|
+
// (D-WORKER-AGENTS) — readAgentMapping has already dropped anything outside it.
|
|
99
149
|
// Off-cycle pin: model was saved but is no longer in the discovered catalog.
|
|
100
150
|
// The per-row effective cycle (state.ts cycleField) includes it for reachability,
|
|
101
151
|
// but it renders as unavailable to signal the user should update it.
|
|
@@ -109,8 +159,7 @@ function renderModelCell({ row, isCursor, isActive, maxWidth, modelCycle, }) {
|
|
|
109
159
|
let cell;
|
|
110
160
|
if (isCursor && isActive) {
|
|
111
161
|
// Active field on cursor row: wrap in ‹ ›, put ● after value if dirty
|
|
112
|
-
|
|
113
|
-
cell = cyan(`‹ ${inner} ›`);
|
|
162
|
+
cell = renderFocusedCell(valueStr, dirty, maxWidth);
|
|
114
163
|
}
|
|
115
164
|
else if (isCursor && dirty) {
|
|
116
165
|
cell = `● ${valueStr}`;
|
|
@@ -122,14 +171,16 @@ function renderModelCell({ row, isCursor, isActive, maxWidth, modelCycle, }) {
|
|
|
122
171
|
}
|
|
123
172
|
/**
|
|
124
173
|
* Render the effort cell for a given row, considering cursor/active/dirty state.
|
|
174
|
+
*
|
|
175
|
+
* D-SHIPPED-EFFORT: an unconfigured row shows `default (<shipped effort>)` when
|
|
176
|
+
* the shipped source carries an effort, through the formatter --list shares.
|
|
125
177
|
*/
|
|
126
178
|
function renderEffortCell(row, isCursor, isActive, maxWidth) {
|
|
127
179
|
const dirty = isDirtyEffort(row);
|
|
128
|
-
const value = row.configuredEffort;
|
|
180
|
+
const value = formatEffortDisplay(row.configuredEffort, row.shippedEffort);
|
|
129
181
|
let cell;
|
|
130
182
|
if (isCursor && isActive) {
|
|
131
|
-
|
|
132
|
-
cell = cyan(`‹ ${inner} ›`);
|
|
183
|
+
cell = renderFocusedCell(value, dirty, maxWidth);
|
|
133
184
|
}
|
|
134
185
|
else if (isCursor && dirty) {
|
|
135
186
|
cell = `● ${value}`;
|
|
@@ -159,6 +210,9 @@ function renderStateCell(row, proxyEnabled, maxWidth) {
|
|
|
159
210
|
case 'unknown':
|
|
160
211
|
cell = dim(AGENT_STATE_LABELS['unknown']);
|
|
161
212
|
break;
|
|
213
|
+
case 'worker':
|
|
214
|
+
cell = dim(AGENT_STATE_LABELS['worker']);
|
|
215
|
+
break;
|
|
162
216
|
default: {
|
|
163
217
|
const _ = state;
|
|
164
218
|
void _;
|
|
@@ -9,7 +9,9 @@
|
|
|
9
9
|
* Picker names: all aliases for each model; canonical id iff
|
|
10
10
|
* the model has no aliases (zero-maintenance, catalog-driven).
|
|
11
11
|
* Model cycle (proxy OFF): default → haiku → sonnet → opus → fable → default
|
|
12
|
-
* Effort cycle: default → low → medium → high → xhigh → max → default
|
|
12
|
+
* Effort cycle: default → low → medium → high → xhigh → max → inherit → default
|
|
13
|
+
* Worker rows (D-WORKER-AGENTS) cycle only what a worker accepts: the model
|
|
14
|
+
* cycle is default and the Claude aliases, the effort cycle has no inherit.
|
|
13
15
|
*
|
|
14
16
|
* Dormancy semantics (plan D5 / Phase 1):
|
|
15
17
|
* When proxy is off and a row's saved model is a GPT model, configuredModel
|
|
@@ -28,7 +30,7 @@
|
|
|
28
30
|
* on the state — never reallocated per keypress. The reducer receives it as
|
|
29
31
|
* state.modelCycle and threads it through without reconstructing.
|
|
30
32
|
*/
|
|
31
|
-
import { EFFORT_LEVELS } from '../../core/agent-models.js';
|
|
33
|
+
import { EFFORT_INHERIT, EFFORT_LEVELS, } from '../../core/agent-models.js';
|
|
32
34
|
import { CLAUDE_MODEL_ALIASES, isDormantExternalModel, } from '../../core/external-models.js';
|
|
33
35
|
import { classifyAgentState, } from '../../core/agent-state.js';
|
|
34
36
|
// ---------------------------------------------------------------------------
|
|
@@ -125,14 +127,26 @@ export function buildModelCycle(catalog) {
|
|
|
125
127
|
return base;
|
|
126
128
|
return [...base, ...pickerNames(catalog.models)];
|
|
127
129
|
}
|
|
128
|
-
|
|
129
|
-
|
|
130
|
+
/**
|
|
131
|
+
* The effort cycle of an agent row: default, the levels in order, then the
|
|
132
|
+
* `inherit` sentinel (D-SHIPPED-EFFORT). `inherit` sits after the levels so the
|
|
133
|
+
* forward order of the levels is unchanged.
|
|
134
|
+
*/
|
|
135
|
+
export const EFFORT_CYCLE = ['default', ...EFFORT_LEVELS, EFFORT_INHERIT];
|
|
136
|
+
/**
|
|
137
|
+
* The cycles of a worker row (D-WORKER-AGENTS). A worker takes only Claude
|
|
138
|
+
* models and has no session effort to inherit, so neither the catalog's external
|
|
139
|
+
* models nor `inherit` is a stop. Built once at load, never per keypress.
|
|
140
|
+
*/
|
|
141
|
+
const WORKER_MODEL_CYCLE = ['default', ...CLAUDE_MODEL_ALIASES];
|
|
142
|
+
const WORKER_EFFORT_CYCLE = ['default', ...EFFORT_LEVELS];
|
|
143
|
+
export function cycleNext(cycle, current) {
|
|
130
144
|
const idx = cycle.indexOf(current);
|
|
131
145
|
if (idx === -1)
|
|
132
146
|
return cycle[0];
|
|
133
147
|
return cycle[(idx + 1) % cycle.length];
|
|
134
148
|
}
|
|
135
|
-
function cyclePrev(cycle, current) {
|
|
149
|
+
export function cyclePrev(cycle, current) {
|
|
136
150
|
const idx = cycle.indexOf(current);
|
|
137
151
|
if (idx === -1)
|
|
138
152
|
return cycle[cycle.length - 1];
|
|
@@ -213,9 +227,14 @@ export function unsavedCount(rows) {
|
|
|
213
227
|
* model would keep showing 'saved-inactive' even though 'opus' is what gets
|
|
214
228
|
* written. Mirroring the merge rule keeps display and persistence in lockstep.
|
|
215
229
|
*
|
|
230
|
+
* A worker row (D-WORKER-AGENTS) has no installed file and cannot be dormant,
|
|
231
|
+
* so it bypasses classification and is always 'worker'.
|
|
232
|
+
*
|
|
216
233
|
* Pure function, no I/O.
|
|
217
234
|
*/
|
|
218
235
|
export function rowState(row, proxyEnabled) {
|
|
236
|
+
if (row.worker)
|
|
237
|
+
return 'worker';
|
|
219
238
|
return classifyAgentState({
|
|
220
239
|
configured: persistedModelFor(row),
|
|
221
240
|
proxyEnabled,
|
|
@@ -245,11 +264,13 @@ function replaceRow(rows, cursor, newRow) {
|
|
|
245
264
|
*/
|
|
246
265
|
function cycleField(row, field, dir, modelCycle) {
|
|
247
266
|
if (field === 'model') {
|
|
267
|
+
// A worker row cycles its own Claude-only stops, not the catalog cycle.
|
|
268
|
+
const baseCycle = row.worker ? WORKER_MODEL_CYCLE : modelCycle;
|
|
248
269
|
// Build effective cycle: splice off-cycle pin at the end if present.
|
|
249
270
|
// This is the ≤ 1 array allocation case (AC-P6): only allocates when offCyclePin != null.
|
|
250
|
-
const effectiveCycle = row.offCyclePin !== null && isOffCycle(
|
|
251
|
-
? [...
|
|
252
|
-
:
|
|
271
|
+
const effectiveCycle = row.offCyclePin !== null && isOffCycle(baseCycle, row.offCyclePin)
|
|
272
|
+
? [...baseCycle, row.offCyclePin]
|
|
273
|
+
: baseCycle;
|
|
253
274
|
// cycleNext/cyclePrev handle the case where configuredModel is not in effectiveCycle
|
|
254
275
|
// by falling back to cycle[0] / cycle[last]. This is correct for the off-cycle case
|
|
255
276
|
// where configuredModel IS in effectiveCycle (we splice it in above).
|
|
@@ -259,12 +280,14 @@ function cycleField(row, field, dir, modelCycle) {
|
|
|
259
280
|
return { ...row, configuredModel: next };
|
|
260
281
|
}
|
|
261
282
|
else {
|
|
283
|
+
const effortCycle = row.worker ? WORKER_EFFORT_CYCLE : EFFORT_CYCLE;
|
|
262
284
|
const next = dir === 'forward'
|
|
263
|
-
? cycleNext(
|
|
264
|
-
: cyclePrev(
|
|
265
|
-
// Sound narrowing:
|
|
266
|
-
// is always
|
|
267
|
-
// because their signature is intentionally
|
|
285
|
+
? cycleNext(effortCycle, row.configuredEffort)
|
|
286
|
+
: cyclePrev(effortCycle, row.configuredEffort);
|
|
287
|
+
// Sound narrowing: both effort cycles are built from 'default', EFFORT_LEVELS
|
|
288
|
+
// and (agents only) the inherit sentinel, so next is always StoredEffort | 'default'.
|
|
289
|
+
// cycleNext/cyclePrev return string because their signature is intentionally
|
|
290
|
+
// generic (also used for model cycles).
|
|
268
291
|
return { ...row, configuredEffort: next };
|
|
269
292
|
}
|
|
270
293
|
}
|
|
@@ -293,8 +316,9 @@ function adjustViewport(cursor, viewportOffset, viewportHeight, rowCount) {
|
|
|
293
316
|
* the model remains reachable in the per-row effective cycle.
|
|
294
317
|
*/
|
|
295
318
|
export function buildRow(input) {
|
|
319
|
+
const worker = input.worker === true;
|
|
296
320
|
const dormant = isDormantExternalModel(input.savedModel, input.proxyEnabled);
|
|
297
|
-
const cycle = input.modelCycle ?? [];
|
|
321
|
+
const cycle = worker ? WORKER_MODEL_CYCLE : (input.modelCycle ?? []);
|
|
298
322
|
// Normalize stored canonical id to picker name (Fix 1: in-memory only, never
|
|
299
323
|
// written back to disk). E.g. 'gpt-5.6-sol' → 'sol' when pickerNameMap is known.
|
|
300
324
|
// This keeps the value in-cycle so it does not become a spurious off-cycle pin.
|
|
@@ -316,6 +340,7 @@ export function buildRow(input) {
|
|
|
316
340
|
return {
|
|
317
341
|
name: input.name,
|
|
318
342
|
shippedDefault: input.shippedDefault,
|
|
343
|
+
shippedEffort: input.shippedEffort,
|
|
319
344
|
configuredModel,
|
|
320
345
|
originalModel: configuredModel,
|
|
321
346
|
configuredEffort,
|
|
@@ -324,6 +349,7 @@ export function buildRow(input) {
|
|
|
324
349
|
offCyclePin,
|
|
325
350
|
installed: input.installed,
|
|
326
351
|
inRegistry: input.inRegistry,
|
|
352
|
+
worker,
|
|
327
353
|
};
|
|
328
354
|
}
|
|
329
355
|
// ---------------------------------------------------------------------------
|