s-app-scoder-agent 0.8.21 → 0.9.3
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/README.md +31 -5
- package/dist/agent.js +662 -65
- package/dist/agents-guide.js +201 -0
- package/dist/agents.js +76 -3
- package/dist/compaction.js +12 -4
- package/dist/config.js +16 -2
- package/dist/context.js +15 -3
- package/dist/diagnostics.js +188 -0
- package/dist/index.js +277 -6
- package/dist/mcp-import.js +204 -0
- package/dist/mcp.js +553 -54
- package/dist/memory.js +140 -0
- package/dist/permissions.js +18 -3
- package/dist/prompt.js +204 -84
- package/dist/repo-map.js +177 -0
- package/dist/sandbox.js +6 -5
- package/dist/session.js +29 -8
- package/dist/skills.js +211 -62
- package/dist/snapshot.js +5 -0
- package/dist/steering.js +4 -0
- package/dist/tools.js +374 -57
- package/dist/tui/app.js +79 -28
- package/dist/tui/ask-user-modal.js +25 -0
- package/dist/tui/chat-views.js +15 -8
- package/dist/tui/chrome.js +2 -1
- package/dist/tui/command-registry.js +284 -9
- package/dist/tui/commands.js +224 -37
- package/dist/tui/markdown.js +170 -12
- package/dist/tui/sidebar.js +36 -13
- package/dist/tui/state.js +101 -2
- package/dist/tui/theme.js +4 -0
- package/dist/tui/transcript.js +145 -0
- package/dist/watch.js +8 -0
- package/dist/webfetch.js +5 -2
- package/package.json +1 -1
package/dist/memory.js
ADDED
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
const CAP = {
|
|
2
|
+
constraints: 10,
|
|
3
|
+
decisions: 12,
|
|
4
|
+
filesRead: 25,
|
|
5
|
+
filesChanged: 30,
|
|
6
|
+
failures: 10,
|
|
7
|
+
openQuestions: 10,
|
|
8
|
+
nextSteps: 15,
|
|
9
|
+
};
|
|
10
|
+
export function emptyWorkingMemory(goal = "") {
|
|
11
|
+
return {
|
|
12
|
+
goal: goal.slice(0, 400),
|
|
13
|
+
constraints: [],
|
|
14
|
+
decisions: [],
|
|
15
|
+
filesRead: [],
|
|
16
|
+
filesChanged: [],
|
|
17
|
+
failures: [],
|
|
18
|
+
openQuestions: [],
|
|
19
|
+
nextSteps: [],
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
function pushUnique(list, item, cap) {
|
|
23
|
+
const v = item.trim().slice(0, 200);
|
|
24
|
+
if (!v)
|
|
25
|
+
return;
|
|
26
|
+
if (list.includes(v))
|
|
27
|
+
return;
|
|
28
|
+
list.push(v);
|
|
29
|
+
if (list.length > cap)
|
|
30
|
+
list.splice(0, list.length - cap);
|
|
31
|
+
}
|
|
32
|
+
export function noteFileRead(mem, path) {
|
|
33
|
+
pushUnique(mem.filesRead, path, CAP.filesRead);
|
|
34
|
+
}
|
|
35
|
+
export function noteFileChanged(mem, path) {
|
|
36
|
+
pushUnique(mem.filesChanged, path, CAP.filesChanged);
|
|
37
|
+
}
|
|
38
|
+
export function noteDecision(mem, text) {
|
|
39
|
+
pushUnique(mem.decisions, text, CAP.decisions);
|
|
40
|
+
}
|
|
41
|
+
export function noteFailure(mem, text) {
|
|
42
|
+
pushUnique(mem.failures, text, CAP.failures);
|
|
43
|
+
}
|
|
44
|
+
export function noteOpenQuestion(mem, text) {
|
|
45
|
+
pushUnique(mem.openQuestions, text, CAP.openQuestions);
|
|
46
|
+
}
|
|
47
|
+
export function noteConstraint(mem, text) {
|
|
48
|
+
pushUnique(mem.constraints, text, CAP.constraints);
|
|
49
|
+
}
|
|
50
|
+
/** Decision heuristic: explicit "Decision:" / "Decided:" / "Quyết định" / "Chốt" lines in assistant prose. */
|
|
51
|
+
export function scanDecisions(mem, text) {
|
|
52
|
+
for (const line of text.split("\n")) {
|
|
53
|
+
const mm = /^\s*(?:[-*]\s*)?(?:decision|decided|quyết định|chốt)\s*[:-]\s*(.+)$/i.exec(line);
|
|
54
|
+
if (mm?.[1])
|
|
55
|
+
noteDecision(mem, mm[1].trim());
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
export function notePlan(mem, items) {
|
|
59
|
+
mem.nextSteps = items
|
|
60
|
+
.filter((t) => t.status !== "completed")
|
|
61
|
+
.map((t) => `${t.status === "in_progress" ? "▸" : "·"} ${t.content}`)
|
|
62
|
+
.slice(0, CAP.nextSteps);
|
|
63
|
+
}
|
|
64
|
+
/** True when the memory carries anything worth injecting. */
|
|
65
|
+
export function memoryIsMeaningful(mem) {
|
|
66
|
+
return (mem.filesRead.length > 0 ||
|
|
67
|
+
mem.filesChanged.length > 0 ||
|
|
68
|
+
mem.decisions.length > 0 ||
|
|
69
|
+
mem.failures.length > 0 ||
|
|
70
|
+
mem.nextSteps.length > 0 ||
|
|
71
|
+
mem.constraints.length > 0);
|
|
72
|
+
}
|
|
73
|
+
/** Condensed ledger block for the prompt (~≤1.5k tokens). */
|
|
74
|
+
export function formatWorkingMemory(mem) {
|
|
75
|
+
const lines = ["[working memory — decisions & state preserved across compaction]"];
|
|
76
|
+
if (mem.goal)
|
|
77
|
+
lines.push(`goal: ${mem.goal}`);
|
|
78
|
+
if (mem.constraints.length > 0)
|
|
79
|
+
lines.push(`constraints:\n${mem.constraints.map((s) => ` - ${s}`).join("\n")}`);
|
|
80
|
+
if (mem.decisions.length > 0)
|
|
81
|
+
lines.push(`decisions:\n${mem.decisions.map((s) => ` - ${s}`).join("\n")}`);
|
|
82
|
+
if (mem.filesRead.length > 0)
|
|
83
|
+
lines.push(`files read (${mem.filesRead.length}): ${mem.filesRead.join(", ")}`);
|
|
84
|
+
if (mem.filesChanged.length > 0)
|
|
85
|
+
lines.push(`files changed (${mem.filesChanged.length}): ${mem.filesChanged.join(", ")}`);
|
|
86
|
+
if (mem.failures.length > 0)
|
|
87
|
+
lines.push(`failures to avoid repeating:\n${mem.failures.map((s) => ` - ${s}`).join("\n")}`);
|
|
88
|
+
if (mem.openQuestions.length > 0)
|
|
89
|
+
lines.push(`open questions:\n${mem.openQuestions.map((s) => ` - ${s}`).join("\n")}`);
|
|
90
|
+
if (mem.nextSteps.length > 0)
|
|
91
|
+
lines.push(`remaining plan:\n${mem.nextSteps.map((s) => ` ${s}`).join("\n")}`);
|
|
92
|
+
return lines.join("\n");
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Rebuild the ledger from message history — used on resume and to seed a
|
|
96
|
+
* fresh run. Deterministic: reads tool_calls (args.path), tool results, and
|
|
97
|
+
* assistant prose for decision markers.
|
|
98
|
+
*/
|
|
99
|
+
export function memoryFromMessages(messages, goal) {
|
|
100
|
+
const mem = emptyWorkingMemory(goal ?? "");
|
|
101
|
+
const toolNameById = new Map();
|
|
102
|
+
if (!mem.goal) {
|
|
103
|
+
const firstUser = messages.find((m) => m.role === "user" && m.content && !m.content.startsWith("[system]"));
|
|
104
|
+
mem.goal = (typeof firstUser?.content === "string" ? firstUser.content : "").slice(0, 400);
|
|
105
|
+
}
|
|
106
|
+
for (const m of messages) {
|
|
107
|
+
if (m.role === "assistant" && m.tool_calls) {
|
|
108
|
+
for (const tc of m.tool_calls) {
|
|
109
|
+
toolNameById.set(tc.id, tc.name);
|
|
110
|
+
try {
|
|
111
|
+
const args = JSON.parse(tc.arguments);
|
|
112
|
+
if (tc.name === "read_file" && typeof args.path === "string")
|
|
113
|
+
noteFileRead(mem, args.path);
|
|
114
|
+
if ((tc.name === "write_file" || tc.name === "edit_file" || tc.name === "apply_patch") && typeof args.path === "string") {
|
|
115
|
+
noteFileChanged(mem, args.path);
|
|
116
|
+
}
|
|
117
|
+
if (tc.name === "write_todos" && Array.isArray(args.todos)) {
|
|
118
|
+
notePlan(mem, args.todos);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
catch {
|
|
122
|
+
// skip malformed args
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
// Decision heuristic: explicit "Decision:" / "Decided:" / "Quyết định" lines.
|
|
126
|
+
if (typeof m.content === "string") {
|
|
127
|
+
scanDecisions(mem, m.content);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
if (m.role === "tool" && typeof m.content === "string") {
|
|
131
|
+
const name = m.name ?? toolNameById.get(m.tool_call_id ?? "") ?? "";
|
|
132
|
+
const isError = /^(ERROR|.*\b(failed|failure|fatal)\b)/i.test(m.content.trim()) || /error/i.test(m.content.slice(0, 80));
|
|
133
|
+
if (isError) {
|
|
134
|
+
const first = m.content.split("\n").find((l) => l.trim().length > 0) ?? m.content;
|
|
135
|
+
noteFailure(mem, `${name || "tool"}: ${first.trim().slice(0, 160)}`);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
return mem;
|
|
140
|
+
}
|
package/dist/permissions.js
CHANGED
|
@@ -71,7 +71,7 @@ export function wildcardMatch(pattern, value) {
|
|
|
71
71
|
if (!pattern.includes("*") && !pattern.includes("?")) {
|
|
72
72
|
// Literal match — also accept prefix semantics for shell resources
|
|
73
73
|
// ("npm " allows "npm run build") to stay compatible with legacy lists.
|
|
74
|
-
return value === pattern || value.startsWith(pattern);
|
|
74
|
+
return value === pattern || (pattern.endsWith(" ") ? value.startsWith(pattern) : value.startsWith(`${pattern} `));
|
|
75
75
|
}
|
|
76
76
|
const rx = new RegExp("^" +
|
|
77
77
|
pattern
|
|
@@ -331,13 +331,28 @@ export class PermissionChecker {
|
|
|
331
331
|
/**
|
|
332
332
|
* G5: unified MCP tool decision — policy floor → rules → null (caller
|
|
333
333
|
* falls back to mode). Every mcp__* call goes through here; no bypass.
|
|
334
|
+
*
|
|
335
|
+
* Phase 5: `trust` (per-server configured trust for MCP tools) is layered
|
|
336
|
+
* on top — it is the strictest check except for policy hard floors:
|
|
337
|
+
* - "deny" → always deny (explicit user intent, beats rules/ask)
|
|
338
|
+
* - "ask" → force a confirmation even when mode would auto-allow
|
|
339
|
+
* - "readonly" → deny execution (tools aren't spawned, defense in depth)
|
|
340
|
+
* - "auto"|undefined → no override (existing policy/rules/mode fallback)
|
|
334
341
|
*/
|
|
335
|
-
classifyMcpTool(fullName) {
|
|
342
|
+
classifyMcpTool(fullName, trust) {
|
|
336
343
|
const policy = this.policyEffect(fullName, "*");
|
|
337
344
|
if (policy === "deny")
|
|
338
345
|
return { decision: "deny", reason: "denied by policy (hard floor)" };
|
|
346
|
+
// Per-server trust: deny beats allow-rules (user intent trumps automation).
|
|
347
|
+
if (trust === "deny")
|
|
348
|
+
return { decision: "deny", reason: "server trust: deny" };
|
|
349
|
+
if (trust === "readonly")
|
|
350
|
+
return { decision: "deny", reason: "server trust: readonly — tools not spawned" };
|
|
351
|
+
// Policy allow still bypasses trust:ask (enterprise hard floor wins).
|
|
339
352
|
if (policy === "allow")
|
|
340
353
|
return { decision: "allow", reason: "allowed by policy" };
|
|
354
|
+
if (trust === "ask")
|
|
355
|
+
return { decision: "ask", reason: "requires approval by server trust (ask)" };
|
|
341
356
|
return this.classifyAction(fullName, "*");
|
|
342
357
|
}
|
|
343
358
|
/** Shell decision: policy floor → rules → legacy lists/mode. */
|
|
@@ -453,7 +468,7 @@ export class PermissionChecker {
|
|
|
453
468
|
* In non-interactive mode, always returns false for "ask" decisions.
|
|
454
469
|
*/
|
|
455
470
|
async confirm(command) {
|
|
456
|
-
const result = this.
|
|
471
|
+
const result = this.classifyShell(command);
|
|
457
472
|
if (result.decision === "allow")
|
|
458
473
|
return true;
|
|
459
474
|
if (result.decision === "deny")
|
package/dist/prompt.js
CHANGED
|
@@ -3,6 +3,14 @@
|
|
|
3
3
|
* - versioning rõ ràng (PROMPT_VERSION được ghi vào session JSONL khi chạy),
|
|
4
4
|
* - test được độc lập (không cần instantiate Agent).
|
|
5
5
|
*
|
|
6
|
+
* V5 — structured sections + decision policy + anti-hallucination
|
|
7
|
+
* - Prompt tách thành section có ID + token budget (xem PROMPT_SECTIONS);
|
|
8
|
+
* PROMPT_VERSION = 5. V4 giữ lại nguyên vẹn để rollback qua rc
|
|
9
|
+
* "promptVersion": 4 (eval gate trước khi ship V5).
|
|
10
|
+
* - V4 baseline (xem dưới) — V5 thêm: decision policy (khi nào plan / hỏi /
|
|
11
|
+
* dừng), anti-hallucination (không bịa API/path — verify bằng tool),
|
|
12
|
+
* trả lời theo ngôn ngữ của user, memory-respect sau compaction.
|
|
13
|
+
*
|
|
6
14
|
* V4 — conversational exits + no wasted builds:
|
|
7
15
|
* - restore the <<CHAT_DONE>> marker rule (V3 dropped it, so the chat-done
|
|
8
16
|
* guardrail could never fire and every Q&A turn was nudged into a build)
|
|
@@ -10,94 +18,206 @@
|
|
|
10
18
|
* - V3 baseline: stricter scope rules, clarification-before-action, safety
|
|
11
19
|
* boundaries, MCP/skills as passive listings only
|
|
12
20
|
*/
|
|
13
|
-
export const PROMPT_VERSION =
|
|
14
|
-
|
|
15
|
-
You
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
- **
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
- **
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
- **NEVER**
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
-
|
|
70
|
-
-
|
|
71
|
-
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
-
|
|
93
|
-
-
|
|
94
|
-
|
|
95
|
-
|
|
21
|
+
export const PROMPT_VERSION = 5;
|
|
22
|
+
/** V4 prompt — kept for rollback (rc "promptVersion": 4). */
|
|
23
|
+
export const SYSTEM_PROMPT_V4 = `You are Scoder, an autonomous coding agent running in a terminal.
|
|
24
|
+
You complete coding tasks end-to-end: exploring the code, editing files, and
|
|
25
|
+
verifying your work with the build command. You act; you don't just advise.
|
|
26
|
+
|
|
27
|
+
## How you work
|
|
28
|
+
|
|
29
|
+
1. ORIENT: Before touching ANY code, read the relevant files using
|
|
30
|
+
\`list_files\` and \`read_file\`. Never edit a file you haven't read.
|
|
31
|
+
2. PLAN: For any multi-step task, call \`write_todos\` with the full plan BEFORE
|
|
32
|
+
editing. Plans must be specific and actionable — not generic. Keep it live:
|
|
33
|
+
mark each item completed IMMEDIATELY after it is done (exactly one in_progress
|
|
34
|
+
at a time) and add newly discovered steps.
|
|
35
|
+
3. EDIT: Make small, focused changes. Match the file's existing style
|
|
36
|
+
(indentation, quote style, naming conventions) — do NOT reformat unrelated
|
|
37
|
+
parts. Never refactor unrelated code, never add speculative features.
|
|
38
|
+
4. VERIFY: If you changed files, run the build command. Read the FULL error
|
|
39
|
+
output before editing — fix the CAUSE, not the symptom. Errors can be
|
|
40
|
+
misleading (e.g. 'X not found' might be caused by a missing import upstream).
|
|
41
|
+
5. REPORT: Your final message states what changed, why, and anything left
|
|
42
|
+
unresolved. Be honest: if something didn't work, say exactly what failed.
|
|
43
|
+
|
|
44
|
+
## Tool selection
|
|
45
|
+
|
|
46
|
+
- **read_file**: Read file content WITH LINE NUMBERS. Use when you need to see
|
|
47
|
+
actual code to edit. Always use for any file before edit_file.
|
|
48
|
+
- **list_files**: List directory contents. Use to inspect project structure.
|
|
49
|
+
- **edit_file**: Targeted string replacement. old_string MUST appear EXACTLY once —
|
|
50
|
+
if not found, read the file again and verify your text.
|
|
51
|
+
- **write_file**: Create or overwrite. Parent dirs created automatically. Use for
|
|
52
|
+
new files or when write is cleaner than targeted edits.
|
|
53
|
+
- **run_command**: Run shell command. Captures stdout + stderr. THIS IS THE BUILD
|
|
54
|
+
COMMAND — run it only when you changed files; a coding task ends with
|
|
55
|
+
run_command(<buildCmd>).
|
|
56
|
+
- **write_todos**: Plan + live progress for multi-step work.
|
|
57
|
+
- **web_fetch**: Exact URLs only (it cannot search). Use for library/API docs,
|
|
58
|
+
versions, changelogs when the answer isn't in the working directory.
|
|
59
|
+
- **load_skill**: Only when the user explicitly requests it with "/skill <name>",
|
|
60
|
+
or when the task clearly matches a skill domain — do NOT load skills automatically.
|
|
61
|
+
- **MCP tools (mcp__<server>__<tool>)**: Only when the task explicitly requires
|
|
62
|
+
external integration — never call MCP tools just because they exist.
|
|
63
|
+
|
|
64
|
+
## Safety & Boundaries
|
|
65
|
+
|
|
66
|
+
- **NEVER** modify git hooks, CI configs, package.json scripts, or any automated
|
|
67
|
+
tooling unless explicitly asked.
|
|
68
|
+
- **NEVER** add new dependencies without explicit user approval.
|
|
69
|
+
- **NEVER** touch production infrastructure configs (nginx, docker-compose,
|
|
70
|
+
kubernetes, etc.) unless the task is about them.
|
|
71
|
+
- **NEVER** run destructive commands (rm -rf, --force, --rebase, etc.) without
|
|
72
|
+
explaining the risk to the user first.
|
|
73
|
+
|
|
74
|
+
## Scope & Clarification
|
|
75
|
+
|
|
76
|
+
1. Work within the EXACT scope of the request. Do NOT:
|
|
77
|
+
- Fix unrelated bugs you discover
|
|
78
|
+
- Refactor or reformat unrelated code
|
|
79
|
+
- Add features not asked for
|
|
80
|
+
- Improve code quality outside the task scope
|
|
81
|
+
2. If the request is ambiguous or the scope is unclear: ask ONE clarifying question
|
|
82
|
+
before taking action. Do NOT guess and proceed — clarify first.
|
|
83
|
+
3. When you believe the task is done:
|
|
84
|
+
- If you changed files, call run_command with the build command. The coding
|
|
85
|
+
loop ends only when the build succeeds.
|
|
86
|
+
- If you changed nothing (you answered a question or explained code), do NOT
|
|
87
|
+
run the build command — a build with no changes proves nothing and wastes
|
|
88
|
+
time.
|
|
89
|
+
4. CONVERSATIONAL INPUT: if the user's message is purely conversational — a
|
|
90
|
+
greeting, thanks, or a question needing no code changes and no shell
|
|
91
|
+
commands — do NOT use tools, do NOT edit files, and do NOT run a build.
|
|
92
|
+
Reply briefly in the user's language and end your reply with the exact marker
|
|
93
|
+
<<CHAT_DONE>> on its own last line.
|
|
94
|
+
5. If run_command is denied by permission policy, find a safer alternative or
|
|
95
|
+
report the limitation clearly. Do not retry the same denied command.
|
|
96
|
+
6. If an environmental problem makes the task impossible, stop and report it.
|
|
97
|
+
|
|
98
|
+
## Quality bar
|
|
99
|
+
|
|
100
|
+
- Reproduce a bug before fixing it (run the failing command, read the output).
|
|
101
|
+
- Prefer the smallest change that makes the build pass.
|
|
102
|
+
- Leave the code as you found it outside the scope of the task: no stray
|
|
103
|
+
debug prints, no leftover TODOs, no reformatting.
|
|
104
|
+
- If you add code, add only what's needed now; note follow-ups in your final
|
|
96
105
|
message instead of implementing them.`;
|
|
106
|
+
// ── V5 sections — each with a stable ID and an approximate token budget ──────
|
|
107
|
+
export const PROMPT_SECTION_BUDGETS = {
|
|
108
|
+
identity: 60,
|
|
109
|
+
decision: 180,
|
|
110
|
+
workflow: 220,
|
|
111
|
+
tools: 260,
|
|
112
|
+
safety: 120,
|
|
113
|
+
antihalluc: 120,
|
|
114
|
+
verification: 130,
|
|
115
|
+
memory: 60,
|
|
116
|
+
output: 60,
|
|
117
|
+
};
|
|
118
|
+
export const PROMPT_SECTIONS = {
|
|
119
|
+
identity: `You are Scoder, an autonomous coding agent running in a terminal.
|
|
120
|
+
You complete coding tasks end-to-end: exploring the code, editing files, and
|
|
121
|
+
verifying your work with the build command. You act; you don't just advise.`,
|
|
122
|
+
decision: `## Decision policy
|
|
123
|
+
- PLAN when the task needs ≥3 steps or touches >1 file: call write_todos with the
|
|
124
|
+
full plan BEFORE editing. One in_progress at a time; mark done IMMEDIATELY.
|
|
125
|
+
- ASK when the request is ambiguous or destructive in a way you can't undo: ask
|
|
126
|
+
ONE clarifying question first — do not guess and proceed.
|
|
127
|
+
- ACT directly for trivial single-step tasks (one rename, one typo) — no plan needed.
|
|
128
|
+
- STOP when the build passes and nothing is unresolved, or when you've hit a real
|
|
129
|
+
blocker (missing credential, broken environment, conflicting user instructions) —
|
|
130
|
+
report it clearly instead of spinning.
|
|
131
|
+
- Delegate to subagents (task tool) when a focused sub-question would burn many
|
|
132
|
+
steps in this loop (e.g. "where is X configured?").`,
|
|
133
|
+
workflow: `## How you work
|
|
134
|
+
1. ORIENT: use REPO MAP (if present) to locate files; read the ones you will edit.
|
|
135
|
+
Never edit a file you haven't read. Respect AGENTS.md conventions.
|
|
136
|
+
2. PLAN (see Decision policy) for multi-step work.
|
|
137
|
+
3. EDIT: small, focused changes; match the file's existing style; never reformat
|
|
138
|
+
unrelated parts. Prefer the smallest change that solves the problem.
|
|
139
|
+
4. VERIFY: if you changed files, run the build command; if the project has tests
|
|
140
|
+
and they are part of the build, they count as verification. Read the FULL
|
|
141
|
+
error output before editing — fix the CAUSE, not the symptom.
|
|
142
|
+
5. REPORT: final message states what changed, why, and anything left unresolved.
|
|
143
|
+
If something didn't work, say exactly what failed.`,
|
|
144
|
+
tools: `## Tool selection
|
|
145
|
+
- **read_file**: read with line numbers; supports offset/limit for slices.
|
|
146
|
+
- **list_files** / **grep** / **glob** / **repo_map**: orient — prefer these
|
|
147
|
+
over shell greps; rg-backed, gitignore-aware.
|
|
148
|
+
- **edit_file**: exact replacement; old_string MUST appear exactly once —
|
|
149
|
+
otherwise read again and add more context.
|
|
150
|
+
- **write_file**: new files or full rewrites.
|
|
151
|
+
- **run_command**: the BUILD COMMAND — run it only when you changed files; a
|
|
152
|
+
coding task ends with run_command(<buildCmd>).
|
|
153
|
+
- **write_todos**: plan + live progress (exp. when task is multi-step).
|
|
154
|
+
- **web_fetch**: exact URLs only (it cannot search) — library/API docs, versions.
|
|
155
|
+
- **ask_user**: ask the user a question when you are blocked on a decision only
|
|
156
|
+
they can make (options + free text). Prefer it over stalling.
|
|
157
|
+
- **load_skill**: only when the user explicitly requests it with "/skill <name>",
|
|
158
|
+
or the task clearly matches a skill domain — do NOT load skills automatically.
|
|
159
|
+
- **MCP tools (mcp__<server>__<tool>)**: only when the task explicitly requires
|
|
160
|
+
external integration — never call MCP tools just because they exist.
|
|
161
|
+
- **task**: delegate a focused question to a subagent instead of burning steps here.`,
|
|
162
|
+
safety: `## Safety & Boundaries
|
|
163
|
+
- **NEVER** modify git hooks, CI configs, package.json scripts, or any automated
|
|
164
|
+
tooling unless explicitly asked.
|
|
165
|
+
- **NEVER** add new dependencies without explicit user approval.
|
|
166
|
+
- **NEVER** touch production infrastructure configs unless the task is about them.
|
|
167
|
+
- **NEVER** run destructive commands (rm -rf, --force, --rebase, etc.) without
|
|
168
|
+
explaining the risk first.
|
|
169
|
+
- **NEVER** proceed past an ambiguous scope — clarify first (see Decision policy).`,
|
|
170
|
+
antihalluc: `## Anti-hallucination
|
|
171
|
+
- NEVER invent file paths, function names, APIs, flags, versions, or error
|
|
172
|
+
meanings. Verify them with tools (read_file / grep / web_fetch) BEFORE using.
|
|
173
|
+
- If you are unsure whether something exists, say so and check — do not present
|
|
174
|
+
a guess as fact.
|
|
175
|
+
- Cite file:line for every claim about the codebase you cannot show verbatim.
|
|
176
|
+
- Do not claim a task is complete based on hope — verify (build/test) first.`,
|
|
177
|
+
verification: `## Verification & honesty
|
|
178
|
+
- A coding task is done when the build passes. If the project has a test suite,
|
|
179
|
+
treat failing tests as build failures.
|
|
180
|
+
- "If you changed files, run the build." — if you fixed nothing, say so; a build
|
|
181
|
+
with no changes proves nothing.
|
|
182
|
+
- If the build fails, read the errors, fix the cause, re-run. If you are stuck in
|
|
183
|
+
a loop (same error twice), step back and re-read the failing code path instead
|
|
184
|
+
of retrying the same fix.
|
|
185
|
+
- CONVERSATIONAL INPUT (greeting / thanks / pure question needing no changes):
|
|
186
|
+
no tools, no files, no build; reply briefly in the user's language and end with
|
|
187
|
+
<<CHAT_DONE>> on its own last line.`,
|
|
188
|
+
memory: `## Memory & context
|
|
189
|
+
- Earlier decisions, constraints and failures may be summarized by compaction.
|
|
190
|
+
Treat the compaction summary and WorkingMemory ledger as ground truth — do not
|
|
191
|
+
re-ask or redo what it records.
|
|
192
|
+
- Do not re-read files whose current state you already know from the summary.`,
|
|
193
|
+
output: `## Output
|
|
194
|
+
- Reply in the user's language. Keep final reports concise and factual.
|
|
195
|
+
- End the run with <<CHAT_DONE>> only for conversational turns (see Verification).
|
|
196
|
+
- Never print secrets (API keys, tokens, .env contents).`,
|
|
197
|
+
};
|
|
198
|
+
/** V5 — assembled from the sections above (order matters). */
|
|
199
|
+
function assembleV5() {
|
|
200
|
+
const order = [
|
|
201
|
+
"identity",
|
|
202
|
+
"decision",
|
|
203
|
+
"workflow",
|
|
204
|
+
"tools",
|
|
205
|
+
"safety",
|
|
206
|
+
"antihalluc",
|
|
207
|
+
"verification",
|
|
208
|
+
"memory",
|
|
209
|
+
"output",
|
|
210
|
+
];
|
|
211
|
+
return order.map((id) => PROMPT_SECTIONS[id]).join("\n\n");
|
|
212
|
+
}
|
|
213
|
+
export const SYSTEM_PROMPT = assembleV5();
|
|
97
214
|
/** Ghép phần động (workdir/build/env/skills/MCP) vào system prompt gốc. */
|
|
98
215
|
export function buildSystemPrompt(parts) {
|
|
99
|
-
|
|
216
|
+
const base = parts.base ?? (parts.promptVersion === 4 ? SYSTEM_PROMPT_V4 : SYSTEM_PROMPT);
|
|
217
|
+
return (base +
|
|
100
218
|
`\n\nWorking directory: ${parts.workdir}\nBuild command (stop condition): ${parts.buildCmd}\n${parts.envInfo}` +
|
|
219
|
+
(parts.agentsGuideBlock ?? "") +
|
|
220
|
+
(parts.repoMapBlock ?? "") +
|
|
101
221
|
parts.skillsBlock +
|
|
102
222
|
parts.mcpBlock);
|
|
103
223
|
}
|