Concepts

How Dus works

How Dus plans, uses tools, asks for approval, explains each change, takes steering mid-task and records every task in the project journal.

Dus is the agent you talk to in Duscode. Dus reasons and proposes; you approve. This page covers what happens between your message and "done".

The task loop

For each task, Dus works in a loop:

  1. Route. Duscode picks a model tier for the task. See Routing and costs.
  2. Understand. Dus starts from a ranked map of the part of your repository the request is about, looks code up by name in the repository index, and reads only the lines it needs.
  3. Plan. For a multi-step task, Dus publishes a checklist and keeps it up to date.
  4. Act. Dus edits files and runs commands. Each change and each command needs your approval, unless you've turned that off.
  5. Verify. If files changed, Duscode runs your project's checks. Failures go back to Dus to fix. See Verification gate.
  6. Report. Dus summarises what it did and suggests next steps. Duscode shows the files changed, the verification result and the cost.

Every few tool rounds, Duscode restates the task, the plan and the project's architecture to Dus. This keeps cheaper models on track during long tasks.

Tools

These are the tools Dus can use. Read-only tools run without asking.

Tool What it does Asks you first
read_file, list_files, grep Read files, list them, search their text No
find_symbol, references, related_files, repo_map Look code up in the repository index: definitions by name, where something is used, a file's imports, importers and tests, a ranked overview No
impact Before a big refactor or migration: what depends on the files or names it changes (with the lines that show it), the tests that cover them, routes and packages touched, and a risk summary No
edit_file, write_file Change or create a file. You see the diff. Yes
bash Run a shell command in the project. It times out after 120 s by default, 600 s at most. Yes
update_plan Publish or update the task's checklist No
ask_user Ask you to choose between 2–4 options Waits for your answer
delegate Hand a read-only side task, such as a wide search, to a cheap helper agent No
spawn_worker and related tools Start background worker agents No, but their edits ask, and merging a worker always asks
web_search, web_fetch Search the web or read a public page. See Web and browser checks. No
browser Use your app in a live browser (click, type, scroll, check text) while you watch in the Preview tab. Local pages only. No
browser_check Open a page in a headless browser and take a screenshot Only for sites other than localhost
generate_image Create an image with an image model and save it as a PNG Yes
remember, forget, recall Manage memory No
use_skill Load a skill No
definition, exact_references, hover, workspace_symbols Ask the project's language server, the engine your editor uses: where a symbol is defined, every real use of it, its exact type, declarations by name No
diagnostics The language server's errors and warnings for a file or the project, in seconds, without a build No
rename_preview Work out a rename (or a file move) across the whole codebase: every reference and import, nothing in unrelated text. Shows the exact edits; changes nothing No
apply_rename Apply exactly the edits rename_preview showed. Each file is an edit you can review; if you decline one, nothing changes Yes, like any edit

Language servers

Dus uses the language servers already on your machine: TypeScript and JavaScript, Python (pyright or basedpyright), and Go (gopls), Rust (rust-analyzer), Java (jdtls) and C/C++ (clangd) when installed. Duscode never installs anything for you; the TypeScript and Python servers come with Duscode's own development install. With the sandbox on, the servers run inside it with no network. Rust and Java servers run build code while they index, so they run only in the sandbox.

After Dus edits files, the language server checks them, and the files that import them, within seconds. Any error the edit introduced (a type error, an import that no longer exists) goes straight back to Dus to fix, before the project's own checks run. The verification gate reports Type errors introduced: N, and a task that leaves new errors behind is not marked verified. Turn this off with "lsp": false in .duscode/config.json.

The repository index

Duscode indexes each project on your machine, in the background, the first time you open it: every file the project tracks (your .gitignore is respected), the definitions in it (functions, classes, methods, types, constants, with their signatures and line numbers), who imports what, which tests cover which file, the entry points (package scripts, main, pages and API routes) and which files changed recently. Nothing leaves your computer and no model is involved.

  • Each request comes with a map. Dus gets a short, ranked overview of the files your request is most likely about and their key definitions (up to about 2,000 tokens, smaller on small models). It is added once per task, so it doesn't break the prompt cache.
  • Dus looks things up instead of reading around. find_symbol, references and related_files answer "where is this defined", "who calls it" and "what does this file touch" from the index, for a fraction of the cost of searching and reading file by file.
  • Before big changes, Dus checks the impact. For a refactor, rename or migration it calls impact first and plans from the dependents, the covering tests and the risk it reports.
  • It stays current. Duscode watches the project's folders, re-indexes Dus's own edits as they happen, and re-checks file times at the start of each task. If an answer would come from an out-of-date entry, Dus re-reads the file from disk and says so.
  • Where it lives. .duscode/index/ in the project, with its own .gitignore, so it is never committed. Delete it any time; it is rebuilt. Turn it off with "repoIndex": false in .duscode/config.json, or keep the tools without the map with "repoIndex": { "map": false }.

TypeScript and JavaScript are parsed with the TypeScript compiler when it is available (from Duscode or your project's node_modules); Python, Go, Rust, Java, Kotlin, C#, Swift, PHP, Ruby, C and C++, SQL and others use a fast line scanner. Very large or minified files are listed but not parsed.

Permission modes and approvals

The mode button in the message box sets how much Dus may do without asking. Each choice is explained in one line in its menu.

Mode What runs without asking
Ask Nothing. Every edit and command shows a Needs you card first. The default in the project folder itself.
Auto Edits and commands that stay inside the session's folder. Anything that reaches outside asks: installing packages, talking to a git remote, rewriting history or switching branch, changing files outside the session, stopping other processes. The default in a session with its own worktree.
Plan Read-only. Dus reads, searches, asks and runs commands that only look (git status, ls, cat), then proposes a plan. Anything that would change something is refused before it runs. Switch mode to let Dus carry the plan out.
Debug Like Auto, but Dus investigates before it fixes. It first writes a failing test or script that shows the problem, lists its hypotheses, adds temporary logging (each line marked DUS-DEBUG), finds the cause with evidence, makes the smallest fix, removes the logging and leaves the test behind so the bug stays fixed. Changes to your code are refused until the problem is reproduced, and the fix until the cause and its evidence are written down; the task can't finish while marked logging is left. It ends with a short card: Cause, Evidence, Fix and Test. People who chose I describe what I want see it as Find what's wrong first.
Builder Dus makes the technical decisions itself (installs from the lockfile, tests, builds, dev servers, local commits, edits in the session), an independent safety reviewer checks the risky ones (new packages, anything outside the session, unknown websites), and you decide money, publishing, messages to real people, real data, legal terms, accounts and keys, sharing data and product choices. The default for people who chose I describe what I want; see Building without code.
Full Auto Also what reaches outside, within the task's budget, because every command runs inside the OS sandbox. You can choose it only while the sandbox works; otherwise the menu says it needs the sandbox. If the sandbox stops working mid-session, Duscode switches to Auto and tells you.

A Needs you card shows the diff or command and Dus's reason. When something asks even in an automatic mode, the card says why (for example it installs packages from the network). You can:

  • Approve this one action.
  • Always allow this command or Always allow edits to this file. This saves a rule for this project, in your own ~/.duscode/permissions.json, never in the project. Remove rules in Settings → Permissions, or add ones like bash(pnpm test), bash(pnpm run lint *) (any arguments) or edit(docs/**).
  • Allow local connections for tests in this project from now on. This shows when a command asks to reach only servers on your own computer (localhost, 127.0.0.1 or ::1), for example tests that talk to a local database. It saves one rule, shown in Settings → Permissions as Local connections for tests, where you can remove it. After that, such commands in this project don't ask about the network again. They are still judged like any other command, so in Ask mode they still ask. Any address beyond your computer still asks every time, and Duscode's own server is never reachable, rule or not.
  • Reject. Dus is told and adapts.

These always ask, in every mode, and no rule can allow them:

  • Irreversible or public actions: any git push, deleting branches or tags, changes on GitHub, publishing a package, deploys and cloud changes, database migrations, destructive SQL and production database URLs, payments.
  • Secrets: reading or changing .env files, keys and SSH or cloud credentials. Search skips them.
  • Duscode's own settings (.duscode/) and git's folder (.git/).
  • Merging a worker's branch.
  • Network access for a sandboxed command to a host outside the sandbox's allowlist.
  • Owner decisions on deploys: the first deploy to a target, anything that costs money, database migrations, DNS changes and destructive commands on a server. See Owner decisions.

And some things never happen at all:

  • Blocked commands never run, even with your approval: sudo, rm -rf / or ~, disk formatting (mkfs, dd to a device), shutdown and reboot, chmod 777 /, piping curl or wget into a shell, fork bombs, and force pushes (git push --force; --force-with-lease is allowed).
  • Your provider keys never reach commands. They're removed from the environment of every command Dus runs, and any key value is redacted from command output.

Stop ends the task, every worker, and every process their commands started.

Every operation is recorded in the session's audit log, .duscode/audit/<session>.jsonl: what ran, in which mode, what allowed it (the mode, your rule, your approval or the verification gate) or refused it, and how it ended. Open it from Project details → This session → Activity.

Undo and rewind

Before a task first changes anything, Duscode takes a snapshot of the session's folder. It's kept in git's object store on a private ref, so your branch, staged files and history are never touched. Files your .gitignore covers, such as dependencies and local .env files, aren't in snapshots and are never restored.

  • Undo last change on a task's change card puts the files back as they were before that task. Older cards say Restore to before this. You see every file that will change, with its diff, before you confirm.
  • Rewind (the arrow beside your message) removes that message and everything after it from the conversation, puts the files back as they were then, and returns your message to the box to edit. It works on any message, even one from before the conversation was compacted: Dus's context is rebuilt from the session's log as it was then. What came after stays in the log, marked as rewound.
  • Each restore can itself be undone with Undo restore.

Dus is told what changed under it, so its next step starts from the real files. Restoring waits until no task is running.

Cost before, during and after

  • Before: after choosing a model, Dus shows an estimate range for the task.
  • During: if Dus repeats the same call or hits the same error three times, it stops and asks: stop, keep going differently, or try a stronger model. At a budget it asks whether to raise it or stop.
  • After: the task's cost line opens into a receipt: what the money went on (reading, searching, editing, testing, retries, planning, helpers, routing) and which models.

Reasons, questions and push-back

  • Every change and command carries a one-sentence reason. You see it on the approval card, and the journal records it.
  • Options at decision points. When there's a real choice, such as which library to use or whether to migrate, Dus asks with two to four options and marks one Recommended. Pick one, or type your own answer.
  • Push-back. If an instruction would break something, Dus explains the consequence and asks before going ahead.

Steering

You don't have to wait for a task to finish: send a message any time. While Dus works, Dus reads your message at its next step. If it changes the current work, Dus applies it now and the message shows a Steer tag. If it's a separate request, Dus queues it (Queued for after this task) and runs it next. If it isn't sure, it asks. See the queue.

Stop ends the task at once. It also stops any workers and turns down any approvals or questions still waiting.

Project instructions

Put your conventions in a DUSCODE.md file in the project root. Good things to include:

  • how to install, run, test and lint the project
  • folder layout and naming conventions
  • things Dus must never do, such as "don't edit generated files in src/gen/"

A project that already has rules for another tool keeps them: Dus reads these files at the start of each session, with no copying. Where two disagree, the later one in this list wins, and DUSCODE.md wins over all of them:

File From
.cursorrules Cursor (older format)
.windsurfrules Windsurf
.github/copilot-instructions.md GitHub Copilot
.cursor/rules/**/*.mdc Cursor (current format), see below
CLAUDE.md Claude Code
AGENTS.md Many tools
DUSCODE.md Duscode

Dus also reads your own ~/.duscode/DUSCODE.md first, and the files of folders between the repository root and your project. A folder inside the project can have its own files too; Dus reads them the first time it reads or edits a file there. Two files with identical text count once, and everything together is limited to 32 KiB: if it's over, Dus cuts the lowest-precedence files first and says what it left out.

Cursor rules

Each rule in .cursor/rules/ (subfolders included) is a Markdown file with a header:

---
description: How to write API handlers
globs: src/api/**/*.ts
alwaysApply: false
---
  • A rule with alwaysApply: true is read in full, under its path, like any other instruction file.
  • Every other rule is only listed: its path, its description and its globs. Dus opens one when the task touches matching files or its description fits the work, so rules that don't apply cost almost nothing.
  • A rule whose header can't be read is listed with a note instead of being skipped. Dus reads the file to decide whether it applies.

The journal

Duscode writes each task to a journal in your project: .duscode/journal/YYYY-MM-DD.md (UTC date). Each entry records:

  • your request and any attachments
  • the model and why it was chosen
  • the files created or edited, with line counts and reasons
  • the commands run and their exit codes
  • the decisions you made
  • the verification result, the outcome, and the cost

The journal is plain Markdown and costs nothing to write; no model is involved. To turn it off, set "journal": false in the configuration.

Long conversations

When a conversation fills 60% of the model's context window, the older turns leave Dus's context. The fast model merges what matters from them into the session brief: your goals, preferences and constraints, decisions and why, files touched and why, open problems, and what was tried and failed. The brief is cumulative and restated at the top of the context after every compaction. The last four replies stay word for word. A line in the chat tells you when the context was compacted.

The context meter. The small circle in the message box shows how full the model's context is. Click it for what fills it (system prompt, tools, instructions and rules, memory, the conversation and attachments), when older turns will be summarised, and Start fresh with a summary: the older turns become the session brief now, and only the last reply stays word for word. It's there between tasks, not while Dus works.

Nothing is lost: the full conversation stays in the session's log, and Dus fetches any detail from it with recall_session. See A session never forgets.

Working habits

Dus follows a few habits a careful engineer has. They are built into the engine, not left to the model's good intentions:

  • Failures that were already there. When a check fails, Dus runs the same check on the code as it was before the task. If it fails there too, it's a pre-existing problem. Dus tells you once, doesn't try to fix it as part of this task, and doesn't switch to a more expensive model for it.
  • The environment isn't the code. A blocked network, a missing tool, a refused connection or a busy port is the machine, not the change. Dus says what's blocked and carries on; it never retries or escalates for it.
  • Diagnose before trying again. After two failed tries at the same step, Dus writes a short diagnosis (hypothesis, evidence, next experiment) before it changes anything else.
  • Know the project first. A task that changes things starts with the project's lessons, its latest decisions and what other sessions are building. Dus says in one line what it will and won't touch.
  • Stop at a clean point, and carry on once. A few turns before a task's turn limit, Dus gets ready to stop: it finishes or undoes the edit in progress and notes where it is.
    • The first time it reaches the limit, it saves the work in progress as a local commit (in a session's own worktree, never pushed) and carries on by itself with fresh turns.
    • The second time, it stops and offers Continue, Raise the limit and continue (it doubles the turns per task for the project) and Stop.
    • Merge work and long plans get more turns. Turns spent only waiting for background work don't count.
    • A task that runs out of turns doesn't pause the rest of the queue, unless the next request builds on it.
    • Set the turns per task in Settings → Budgets.
  • Flaky checks. A check that fails without an error pointing at the change is run once more. If it passes, Dus marks it flaky, says so once, and moves on. A check that keeps doing it becomes a project lesson and a suggestion to fix it.
  • No false alarms about secrets. Code that only names its secret fields (secretKey, "API secret key") isn't flagged. If your project has its own secret scanner (in its checks, scripts or CI), Dus runs it, and a clean result marks the scan's uncertain findings "not a secret (checked by …)" in the Proof of Work.
  • Remembers what you ask for. "From now on…", "always…", "let me know if…": Dus keeps standing instructions as memories, says "I'll remember: …" with Undo, and applies them in every later task. See Memory and skills.
  • Feedback for Duscode. When Dus runs into a problem in Duscode itself (a sandbox limit, a confusing error, a missing tool), it notes it once and adds it to Settings → About & updates → Feedback for Duscode, ready to copy to whoever builds Duscode, or to send to Duscode after you see exactly what goes.
  • History before blame. In merge and conflict work, Dus checks the history (git log -S, blame) before calling something a bug: what a merge dropped is restored, not reinvented.

Each habit can be switched off in .duscode/config.json ("judgement": false, or one at a time, for example { "judgement": { "flaky": false } }).

Something missing or wrong? Email support@duscode.com.