Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
94 commits
Select commit Hold shift + click to select a range
732af31
feat(cli): add self-improve command
DragonnZhang May 14, 2026
9b8ff36
fix(cli): harden self-improve status output
DragonnZhang May 14, 2026
629064c
docs: move self-improve design doc
DragonnZhang May 15, 2026
0ccb3de
feat(cli): rename self-improve command to auto-improve
DragonnZhang May 20, 2026
836a285
feat(cli): support custom auto-improve sources
DragonnZhang May 21, 2026
e4e6d02
fix(cli): avoid highlighting auto-improve source label
DragonnZhang May 21, 2026
e8f5101
fix(cli): clarify auto-improve source input focus
DragonnZhang May 21, 2026
df8143d
feat(cli): make auto-improve delivery source-aware
DragonnZhang May 21, 2026
57f3480
fix(cli): mark auto-improve run cancelled on escape
DragonnZhang May 21, 2026
c1395a5
fix(cli): refine auto-improve PR source guidance
DragonnZhang May 21, 2026
c52d3df
fix(cli): improve auto-improve status display
DragonnZhang May 21, 2026
1fb6fcf
fix(cli): harden auto-improve loop state
DragonnZhang May 21, 2026
cd2d158
fix(cli): add auto-improve command translations
DragonnZhang May 21, 2026
de77a0a
fix(cli): register auto-improve i18n keys
DragonnZhang May 21, 2026
23a6c9f
fix(cli): address auto-improve review feedback
DragonnZhang May 22, 2026
a503a6f
fix(cli): clarify auto-improve source context
DragonnZhang May 22, 2026
88f8e0e
feat(cli): improve auto-improve issue delivery
DragonnZhang May 22, 2026
a1e3bea
fix(cli): polish auto-improve status display
DragonnZhang May 22, 2026
ec06479
fix(cli): scope auto-improve PR work to user
DragonnZhang May 22, 2026
19d026d
fix(cli): harden tick prompt injection defense ordering
DragonnZhang May 22, 2026
8119bf8
fix(cli): register auto-improve source dialog in useDialogClose
DragonnZhang May 22, 2026
fc306c2
fix(cli): render tick prompt paths with forward slashes on Windows
DragonnZhang May 25, 2026
a5fd44f
test(cli): add autoImproveState persistence layer tests
DragonnZhang May 25, 2026
6faaeb6
chore(cli): clarify auto-improve PR comment handling
DragonnZhang May 25, 2026
0415313
fix(cli): localize describeSources strings in auto-improve status dis…
DragonnZhang May 25, 2026
7d8a783
fix(cli): transition loop status from stopping to stopped on cancella…
DragonnZhang May 25, 2026
76007a8
chore(cli): relax auto-improve task scope wording
DragonnZhang May 25, 2026
04434d8
fix(cli): use atomic write-to-temp-then-rename for state.json
DragonnZhang May 25, 2026
5dbb478
fix(cli): use stable runId as React key in recentRuns list
DragonnZhang May 25, 2026
82456a2
fix(cli): add length and count bounds to normalizeStringList
DragonnZhang May 25, 2026
bbaed37
refactor(cli): remove duplicated branch/commit/runDoc from formatRunR…
DragonnZhang May 25, 2026
dcfc932
fix(cli): propagate onComplete from submit_prompt across all executio…
DragonnZhang May 25, 2026
e270d69
fix(cli): preserve terminal run status and narrow tick loop id parsing
DragonnZhang May 25, 2026
35f9562
fix(cli): add double-check pattern to reduce TOCTOU race in tick conc…
DragonnZhang May 25, 2026
0558dba
fix(cli): add runId to HistoryItemAutoImproveRun type
DragonnZhang May 25, 2026
2cb0718
test(cli): add runId to HistoryItemDisplay test fixture
DragonnZhang May 25, 2026
6d09046
test(cli): pass includeExpandedPrompt in parseAutoImproveTickLoopId test
DragonnZhang May 25, 2026
b16e95f
fix(cli): continue scanning auto-improve PR candidates
DragonnZhang May 25, 2026
acdad7e
fix(cli): use freshState for TOCTOU double-check write in tickAutoImp…
DragonnZhang May 25, 2026
1e1a724
fix(cli): catch SyntaxError in readAutoImproveConfig
DragonnZhang May 25, 2026
3b6311c
fix(cli): enforce MAX_CUSTOM_SOURCES and MAX_CUSTOM_SOURCE_LENGTH in …
DragonnZhang May 25, 2026
962a620
fix(cli): move targetBranch inside USER-PROVIDED DATA fence to preven…
DragonnZhang May 25, 2026
c3b4d78
fix(cli): clear submitPromptOnCompleteRef in cancel handler to preven…
DragonnZhang May 25, 2026
0a95948
feat(i18n): register auto-improve translation keys in en, zh, zh-TW
DragonnZhang May 25, 2026
030381f
fix(cli): record failed auto-improve runs as 'failed' instead of 'suc…
DragonnZhang May 25, 2026
8e08318
fix(cron): ensure slashOnComplete called on all code paths
DragonnZhang May 25, 2026
71b5bdd
fix(core): restore dual x-api-key + Authorization headers for proxy c…
DragonnZhang May 25, 2026
5246dd5
Revert "fix(core): restore dual x-api-key + Authorization headers for…
DragonnZhang May 25, 2026
314392c
fix(cli): refine auto-improve PR review triage
DragonnZhang May 25, 2026
2191c5e
feat(cli): record auto-improve session id
DragonnZhang May 25, 2026
62850ca
fix(cli): address 3 review comments on auto-improve loop
DragonnZhang May 26, 2026
c49a1ae
Merge remote-tracking branch 'origin/main' into HEAD
DragonnZhang May 26, 2026
9f06380
fix(cli): paginate auto-improve review thread scans
DragonnZhang May 26, 2026
f942827
test(cli): cover USER-PROVIDED DATA boundary neutralization in tick p…
DragonnZhang Jun 6, 2026
534a142
fix(cli): address auto-improve review feedback
DragonnZhang Jun 8, 2026
0e0efc7
Merge branch 'main' into dragon/feat-self-improve
DragonnZhang Jun 8, 2026
e0795c5
fix(cli): address second-pass auto-improve review feedback
DragonnZhang Jun 8, 2026
3646559
fix(cli): harden auto-improve loopId validation and bound repoRoot cache
DragonnZhang Jun 8, 2026
2d4d5f1
fix(cli): retry repoRoot on git failure, result-aware cancel message,…
DragonnZhang Jun 8, 2026
9e2fb85
fix(cli): tick mutex, atomic config write, and auto-improve diagnostics
DragonnZhang Jun 8, 2026
04254cb
fix(cli): validate deliveryPolicy against a known set with warn
DragonnZhang Jun 8, 2026
3a4e8fa
Merge branch 'main' into dragon/feat-self-improve
DragonnZhang Jun 8, 2026
d439ce8
fix(cli): don't record a dream run when the turn errored; test cancel…
DragonnZhang Jun 8, 2026
cae0e6e
test(cli): cover onComplete errored paths; cap legacy userContext length
DragonnZhang Jun 9, 2026
728d2db
fix(cli): re-check stop/status in tick TOCTOU; clean up loop dir on s…
DragonnZhang Jun 9, 2026
e7fa445
fix(cli): cap run index, guard cron emit, log delete failure, drop de…
DragonnZhang Jun 9, 2026
8776a57
fix(cli): fire submit_prompt onComplete on the -p and ACP start paths
DragonnZhang Jun 9, 2026
22bc9dc
Merge branch 'main' into dragon/feat-self-improve
DragonnZhang Jun 9, 2026
a1940b0
fix(cli): harden auto-improve custom-source prompt data fence
DragonnZhang Jun 9, 2026
695da36
fix(cli): bound/sanitize auto-improve state fields and fix fence regex
DragonnZhang Jun 9, 2026
012d5d9
fix(cli): auto-improve dialog timeout/re-entrancy + state test coverage
DragonnZhang Jun 9, 2026
7cf794d
fix(cli): compact auto-improve run index + per-tick cron duration
DragonnZhang Jun 9, 2026
1f10b22
fix(cli): auto-improve run ownership + stale-run reclaim (deadlock fix)
DragonnZhang Jun 9, 2026
27283c1
fix(cli): record aborted auto-improve runs as cancelled, not failed
DragonnZhang Jun 9, 2026
433d7e1
perf(cli): read only the most recent auto-improve loop for status
DragonnZhang Jun 9, 2026
5afe0ec
test(cli): cover the ACP cron callback's slash-command/onComplete path
DragonnZhang Jun 9, 2026
c628af7
fix(cli): harden auto-improve start teardown + cap prompt at write
DragonnZhang Jun 9, 2026
5a5ff9d
merge: integrate origin/main into auto-improve branch
DragonnZhang Jun 10, 2026
2ac0778
fix(cli): add missing return in auto-improve-source case
DragonnZhang Jun 10, 2026
faab472
merge: integrate origin/main into auto-improve branch
DragonnZhang Jun 11, 2026
11be20e
fix(cli): address auto-improve review feedback
DragonnZhang Jun 11, 2026
5221e77
fix(cli): address auto-improve review round 2
DragonnZhang Jun 11, 2026
6f48795
merge: integrate origin/main (picks up yaml-parser test fix #4980)
DragonnZhang Jun 11, 2026
46744c5
fix(cli): fire slashOnComplete on partListToText throw + cancelled on…
DragonnZhang Jun 11, 2026
fbedbaa
fix(cli): keep the auto-improve cron job alive past CronScheduler's 3…
DragonnZhang Jun 11, 2026
d6c13a3
fix(cli): handle cancelled in dream onComplete + ACP regular/catch paths
DragonnZhang Jun 11, 2026
a7d7506
fix(cli): record interactive AbortError as cancelled in submitPrompt …
DragonnZhang Jun 11, 2026
f2d09ed
fix(cli): align onComplete cancelled type + dedupe compaction read
DragonnZhang Jun 11, 2026
28a0635
fix(cli): clean up orphaned loop dir when active pointer has no state
DragonnZhang Jun 11, 2026
28befb2
merge: integrate origin/main into auto-improve branch
DragonnZhang Jun 11, 2026
bcc28a2
fix(cli): fire onComplete when UserPromptExpansion blocks a submit_pr…
DragonnZhang Jun 11, 2026
df22089
Merge remote-tracking branch 'origin/main' into dragon/feat-self-improve
DragonnZhang Jun 12, 2026
96e9fd0
fix(cli): honor error/cancel guard for /dream in ACP mode
DragonnZhang Jun 12, 2026
13bb4df
fix(cli): set cronHadError on token-limit cutoff in ACP cron tick
DragonnZhang Jun 12, 2026
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
140 changes: 140 additions & 0 deletions docs/design/auto-improve/auto-improve-command-design.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
# Auto-Improve Built-In Command Design

## Goal

Add a built-in `/auto-improve` command that runs a session-scoped loop for
bounded, locally verifiable repository improvements. The command should be useful
without becoming a hard-coded automation framework: first version keeps the
actual implementation, testing, repair, delivery, and documentation work
prompt-driven, while the built-in command owns reliable local state, scheduling,
status, and source configuration.

## User Commands

Expose four user-facing subcommands:

- `/auto-improve source`
- `/auto-improve start --every <interval> [prompt]`
- `/auto-improve status`
- `/auto-improve stop`

`source` is interactive-only. It opens a dialog with checkboxes for GitHub
issues, GitHub PRs / CI / review comments, and local repository scanning, plus a
custom source list. Users can add multiple custom source hints, edit existing
items, and delete items. Custom sources can be used alone or together with
checked built-in sources. Defaults are all off and an empty custom source list.

`start` may run even when no source and no prompt are configured. In that case
the tick prompt tells the agent to do a small baseline repository inspection and
choose one locally verifiable task. `start` snapshots the current repo-level
source configuration into the loop; future `source` changes affect only future
loops.

## State Layout

Store state under `.qwen/auto-improve/`:

```text
.qwen/auto-improve/
config.json
active.json
loops/
<loop-id>/
state.json
summary.md
runs/
index.json
001-xxx.md
```

`config.json` is repository-level default source configuration, including the
built-in source toggles and ordered custom source hints. `active.json` is a thin
pointer to the one active loop. First version allows at most one active loop per
repository. `state.json` belongs to a single loop and contains the cadence,
loop default branch, source snapshot, delivery policy, start prompt, status,
stop request flag, current run, last run, and cron job id when available.
Historical loops remain in `loops/`, but `/auto-improve status` reads only the
active loop.

The loop is session-scoped. Exiting Qwen Code is equivalent to stopping the
loop. If the CLI exits abruptly and leaves `active.json` behind, a later status
can mark it stale rather than pretending it is still running.

## Loop Behavior

`/auto-improve start --every 2h [prompt]`:

1. Refuses to start if another active loop exists.
2. Reads `config.json`.
3. Captures the current local branch as `targetBranch`.
4. Creates a new loop directory with a `state.json`, `summary.md`, and `runs/`.
5. Registers a session-only recurring schedule.
6. Immediately submits the first tick prompt.

Each tick is prompt-driven. The prompt instructs the agent to:

- read the loop state;
- select exactly one coherent, locally verifiable improvement from the source
snapshot and optional start prompt, preferring bounded work while making the
change complete enough to address the selected issue, PR comment, requested
change, or failing check;
- create a dedicated issue branch from the repository default branch for
GitHub issue-derived tasks;
- create an isolated worktree and branch;
- implement the change;
- run appropriate tests;
- repair and retest up to five times;
- commit only after tests pass;
- choose a delivery branch before editing;
- use a PR's head branch for PR-derived review / CI / comment tasks;
- prioritize the authenticated user's own open, non-draft PRs for PR-derived
work;
- focus on actionable unresolved review comments, requested changes, and
failing checks instead of already-resolved comments or general comment
history;
- for addressed unresolved PR review comments, fix and validate first, then
reply to each addressed review thread/comment with a concise summary and
validation result, and resolve the thread; if permissions or API limitations
prevent replying or resolving, record that in the run doc and final response;
- skip other users' PRs, CI failures, and review comments unless the user
explicitly requested them;
- use the loop default branch for ordinary local/default tasks;
- use a local-only branch if the correct delivery branch is unclear;
- never merge a PR-derived fix into the loop default branch unless they are the
same branch;
- never push unless the user explicitly requested push in the start prompt or
selected source;
- never overwrite or discard user uncommitted work;
- delete the worktree after success or after five failed repair attempts;
- update `summary.md`, `runs/index.json`, and one run document for every
attempted run.

Successful runs are local commits by default. For PR-derived tasks, the local
commit belongs to the PR head branch rather than the branch that started the
loop. The first version does not push unless the user explicitly requested it
and does not open pull requests. Failed runs delete their worktree and leave
only the run document.

## Stop And Status

`stop` is graceful. If no run is active, it cancels future scheduling, marks the
loop stopped, and clears `active.json`. If a run is active, it cancels future
scheduling and writes `stopRequested: true`; the current run may naturally
finish, fail, or cancel, but no later tick should start.

`status` displays the active loop when present. If there is no active loop, it
falls back to the most recent historical loop so stopped loops remain
discoverable. Status includes loop id, status, cadence, target branch, source
snapshot, start prompt, current run, last run, recent run records, and
next/future schedule information when available.

## Implementation Shape

Implement `/auto-improve` as a built-in command. Use a small hidden
`/auto-improve tick <loop-id>` subcommand as the scheduled entrypoint; it
returns `submit_prompt` with the internal tick instructions. The hidden tick is
not shown in help and is not part of the public UX.

This keeps the first version simple: program code controls reliable command
state and scheduling, while the agent remains responsible for the engineering
workflow inside each improvement run.
46 changes: 46 additions & 0 deletions packages/cli/src/acp-integration/session/Session.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3158,6 +3158,52 @@ describe('Session', () => {
);
});

it('runs a slash-command cron tick through submit_prompt and fires slashOnComplete', async () => {
const onCompleteSpy = vi.fn().mockResolvedValue(undefined);
vi.mocked(
nonInteractiveCliCommands.handleSlashCommand,
).mockResolvedValueOnce({
type: 'submit_prompt',
content: [{ text: 'do the cron work' }],
onComplete: onCompleteSpy,
} as never);
const scheduler = {
size: 1,
start: vi.fn((callback: (job: { prompt: string }) => void) => {
callback({ prompt: '/auto-improve tick test-loop' });
}),
stop: vi.fn(),
getExitSummary: vi.fn().mockReturnValue(undefined),
};
mockConfig.isCronEnabled = vi.fn().mockReturnValue(true);
mockConfig.getCronScheduler = vi.fn().mockReturnValue(scheduler);
mockChat.sendMessageStream = vi
.fn()
.mockResolvedValueOnce(createEmptyStream())
.mockResolvedValueOnce(createEmptyStream());

await session.prompt({
sessionId: 'test-session-id',
prompt: [{ type: 'text', text: 'hello' }],
});

await vi.waitFor(() => {
expect(onCompleteSpy).toHaveBeenCalledTimes(1);
});
// The isSlashCommand branch resolved a submit_prompt, ran the model
// turn, and the finally fired slashOnComplete with a clean (success)
// result — not errored/cancelled.
expect(
vi.mocked(nonInteractiveCliCommands.handleSlashCommand),
).toHaveBeenCalledWith(
'/auto-improve tick test-loop',
expect.anything(),
expect.anything(),
expect.anything(),
);
expect(onCompleteSpy).toHaveBeenCalledWith(undefined);
});

it('stops cron-fired ACP prompt before sending when the session token limit is exceeded', async () => {
let cronCallback: ((job: { prompt: string }) => void) | undefined;
const scheduler = {
Expand Down
Loading
Loading