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
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Changelog

## Unreleased

### Internal

- **Smaller first request: the default system prompt is trimmed to functional text** — The prompt sent to models without a dedicated prompt (GLM, Qwen, DeepSeek, Kimi and others; not Claude, GPT, Gemini or Trinity) shrinks from 9,684 to 2,475 characters. It keeps the conventions and task steps (check libraries exist, verify with tests, run lint and typecheck when provided), the no-comments rule, the Task tool file-search line, a one-sentence parallel tool calls rule, a one-line `<system-reminder>` note and the `file_path:line_number` rule. Removed: the `/help` and feedback lines, the aictrl docs WebFetch rule, both copies of the malicious-code refusal (permissions and isolation enforce safety), the line-count and one-word-answer rules with their examples (replaced by one conciseness line with an escape for tasks that need long output), the bash-explanation, monospace and emoji rules, the Proactiveness section, the ask-for-the-lint-command and commit-only-when-asked rules. The empty `<directories>` block is removed from the environment block. The file is renamed from `qwen.txt` to `default.txt`, since it is the fallback for every such model. On `zai-coding-plan/glm-5.3` the first request is about 1.5k tokens (28%) smaller. An A/B run of the 8-task benchmark suite passed 24/24 on both versions with no extra steps and 31% fewer total tokens; final answers are longer (median 540 vs 279 characters). (refs #214, #229)

## 0.5.3 (2026-10-19)

### Packaging
Expand Down
31 changes: 31 additions & 0 deletions packages/cli/src/session/prompt/default.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
You are aictrl, an agent that helps with software engineering tasks. Use the instructions below and the tools available to you to complete the task.

# Tone and style
Be concise by default: keep the final answer short and skip preamble and postamble. Go longer only when the task needs it (for example a review, a spec, or requested structured output).
Text you output outside tool calls is your reply. Use tools only to complete the task, never to communicate (for example through bash echo or code comments).
Do not generate or guess URLs. Use URLs from the user's messages or local files.

# Following conventions
Before changing a file, understand its conventions: mimic its code style, use the existing libraries and utilities, and follow existing patterns.
- Never assume a library is available, even a well-known one. Check that the codebase already uses it, for example in neighboring files or the package.json (or Cargo.toml, and so on).
- Before creating a new component, look at existing ones for framework choice, naming, typing and other conventions.
- Before editing code, read its surrounding context, especially its imports, and make the change in the most idiomatic way.
- Follow security best practices. Never introduce code that exposes or logs secrets and keys, and never commit them.

# Code style
- IMPORTANT: DO NOT ADD ***ANY*** COMMENTS unless asked

# Doing tasks
- Use the search tools to understand the codebase and the request, in parallel and sequentially.
- Implement the solution with the tools available to you.
- Verify the solution with tests when possible. Do not assume a test framework or script; check the README or search the codebase for the testing approach.
- When you have finished, run the lint and typecheck commands (for example npm run lint, npm run typecheck, ruff) with bash if they were provided to you.

Tool results and user messages may include <system-reminder> tags. They contain useful information and reminders, and are not part of the user's input or the tool result.

# Tool usage policy
- When doing file search, prefer to use the Task tool in order to reduce context usage.
- You can call multiple tools in one response: when calls are independent, including several bash commands, send them together in a single message so they run in parallel.

# Code references
When referencing specific functions or pieces of code, include the pattern `file_path:line_number` so the user can navigate to the source location.
109 changes: 0 additions & 109 deletions packages/cli/src/session/prompt/qwen.txt

This file was deleted.

7 changes: 2 additions & 5 deletions packages/cli/src/session/system.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Instance } from "../project/instance"

import PROMPT_ANTHROPIC from "./prompt/anthropic.txt"
import PROMPT_QWEN from "./prompt/qwen.txt"
import PROMPT_DEFAULT from "./prompt/default.txt"
import PROMPT_BEAST from "./prompt/beast.txt"
import PROMPT_GEMINI from "./prompt/gemini.txt"

Expand All @@ -21,7 +21,7 @@ export namespace SystemPrompt {
if (model.api.id.includes("gemini-")) return [PROMPT_GEMINI]
if (model.api.id.includes("claude")) return [PROMPT_ANTHROPIC]
if (model.api.id.toLowerCase().includes("trinity")) return [PROMPT_TRINITY]
return [PROMPT_QWEN]
return [PROMPT_DEFAULT]
}

export async function environment(model: Provider.Model) {
Expand All @@ -36,9 +36,6 @@ export namespace SystemPrompt {
` Platform: ${process.platform}`,
` Today's date: ${new Date().toDateString()}`,
`</env>`,
`<directories>`,
` `,
`</directories>`,
].join("\n"),
]
}
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/test/session/headless-prompt-budget.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import { alibabaProvider } from "../fixture/providers"
* conversation content. Set just above the current size: lower it whenever a
* change shrinks the prefix, and never raise it without a reason (#160).
*/
const HEADLESS_PREFIX_BUDGET = 29_500
const HEADLESS_PREFIX_BUDGET = 15_000

type Piece = { name: string; chars: number }

Expand Down
53 changes: 53 additions & 0 deletions packages/cli/test/session/system.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { describe, expect, test } from "bun:test"
import { tmpdir } from "../fixture/fixture"
import { Instance } from "../../src/project/instance"
import { SystemPrompt } from "../../src/session/system"
import type { Provider } from "../../src/provider/provider"

function model(id: string) {
return { providerID: "zai-coding-plan", api: { id } } as Provider.Model
}

describe("SystemPrompt.provider default prompt", () => {
const prompt = SystemPrompt.provider(model("glm-5.3")).join("\n")

test("is used for models without a dedicated prompt", () => {
for (const id of ["qwen-plus", "deepseek-chat", "kimi-k2"]) {
expect(SystemPrompt.provider(model(id)).join("\n")).toBe(prompt)
}
})

// #214: losing the parallel-call nudge reduced batching on GLM.
test("asks for independent tool calls in one message", () => {
expect(prompt).toContain("send them together in a single message so they run in parallel")
})

test("keeps the Task tool file-search line", () => {
expect(prompt).toContain("When doing file search, prefer to use the Task tool in order to reduce context usage.")
})

// read.ts and prompt.ts inject <system-reminder> tags into tool results and user messages.
test("explains system-reminder tags", () => {
expect(prompt).toContain("Tool results and user messages may include <system-reminder> tags.")
})

test("keeps the no-comments rule", () => {
expect(prompt).toContain("IMPORTANT: DO NOT ADD ***ANY*** COMMENTS unless asked")
})
})

describe("SystemPrompt.environment", () => {
test("sends the env block without an empty directories block", async () => {
await using tmp = await tmpdir({ git: true })
await Instance.provide({
directory: tmp.path,
fn: async () => {
const env = (await SystemPrompt.environment(model("glm-5.3"))).join("\n")
expect(env).toContain(`Working directory: ${tmp.path}`)
expect(env).toContain("Is directory a git repo: yes")
expect(env.trimEnd().endsWith("</env>")).toBe(true)
expect(env).not.toContain("<directories>")
},
})
})
})
Loading