s-app-scoder-agent 0.9.3 → 0.9.6

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.
Files changed (112) hide show
  1. package/README.md +615 -638
  2. package/dist/{guardrails.js → agent/guardrails.js} +1 -1
  3. package/dist/agent/interaction.js +70 -0
  4. package/dist/agent/mcp-bridge.js +136 -0
  5. package/dist/agent/prompt-context.js +83 -0
  6. package/dist/agent/run-loop.js +318 -0
  7. package/dist/agent/session-persistence.js +61 -0
  8. package/dist/agent/tool-executor.js +158 -0
  9. package/dist/agent/types.js +88 -0
  10. package/dist/agent/verification.js +99 -0
  11. package/dist/agent.js +239 -1002
  12. package/dist/cli/args.js +230 -0
  13. package/dist/cli/init.js +79 -0
  14. package/dist/cli/main.js +211 -0
  15. package/dist/cli/mcp-command.js +185 -0
  16. package/dist/cli/repl.js +77 -0
  17. package/dist/cli/run-once.js +26 -0
  18. package/dist/{tui/commands.js → commands/definitions.js} +6 -16
  19. package/dist/commands/registry.js +260 -0
  20. package/dist/{config.js → config/config.js} +1 -1
  21. package/dist/{prompt-builder.js → context/prompt-builder.js} +12 -1
  22. package/dist/{prompt.js → context/prompt.js} +111 -9
  23. package/dist/{skills.js → context/skills.js} +41 -2
  24. package/dist/index.js +5 -798
  25. package/dist/io/console-reporter.js +24 -0
  26. package/dist/io/reporter.js +1 -0
  27. package/dist/mcp/connection.js +227 -0
  28. package/dist/mcp/env.js +8 -0
  29. package/dist/{mcp.js → mcp/manager.js} +3 -515
  30. package/dist/mcp/sse.js +28 -0
  31. package/dist/mcp/transport-http.js +140 -0
  32. package/dist/mcp/transport-stdio.js +98 -0
  33. package/dist/mcp/types.js +11 -0
  34. package/dist/media/image.js +149 -0
  35. package/dist/{server.js → server/server.js} +1 -1
  36. package/dist/{cost.js → session/cost.js} +1 -1
  37. package/dist/{session.js → session/session.js} +1 -1
  38. package/dist/{version.js → shared/version.js} +1 -1
  39. package/dist/tools/apply-patch.js +62 -0
  40. package/dist/tools/edit-file.js +29 -0
  41. package/dist/tools/file-edit-common.js +124 -0
  42. package/dist/tools/list-files.js +64 -0
  43. package/dist/tools/path-safety.js +75 -0
  44. package/dist/tools/read-file.js +64 -0
  45. package/dist/tools/registry.js +297 -0
  46. package/dist/tools/run-command.js +192 -0
  47. package/dist/tools/schemas.js +8 -0
  48. package/dist/tools/search.js +237 -0
  49. package/dist/tools/task-tool.js +48 -0
  50. package/dist/tools/types.js +1 -0
  51. package/dist/tools/write-file.js +34 -0
  52. package/dist/tui/app.js +59 -532
  53. package/dist/tui/ask-user-modal.js +2 -1
  54. package/dist/tui/at-complete.js +2 -93
  55. package/dist/tui/charset.js +41 -0
  56. package/dist/tui/chat-views.js +72 -16
  57. package/dist/tui/chrome.js +11 -5
  58. package/dist/tui/hooks/use-command-host.js +26 -0
  59. package/dist/tui/hooks/use-event-queue.js +69 -0
  60. package/dist/tui/hooks/use-keybinds.js +87 -0
  61. package/dist/tui/hooks/use-scroll.js +38 -0
  62. package/dist/tui/hooks/use-session-resume.js +47 -0
  63. package/dist/tui/hooks/use-submit.js +106 -0
  64. package/dist/tui/image.js +2 -149
  65. package/dist/tui/input.js +1 -1
  66. package/dist/tui/markdown/ast.js +5 -0
  67. package/dist/tui/markdown/diagram.js +239 -0
  68. package/dist/tui/markdown/inline-render.js +24 -0
  69. package/dist/tui/markdown/inline.js +93 -0
  70. package/dist/tui/markdown/parse.js +139 -0
  71. package/dist/tui/markdown/render.js +146 -0
  72. package/dist/tui/markdown/table.js +106 -0
  73. package/dist/tui/markdown.js +32 -379
  74. package/dist/tui/permission-modal.js +2 -1
  75. package/dist/tui/selectors.js +157 -0
  76. package/dist/tui/session-picker.js +4 -3
  77. package/dist/tui/sidebar.js +130 -15
  78. package/dist/tui/state.js +47 -118
  79. package/dist/tui/text-width.js +139 -0
  80. package/dist/tui/theme.js +7 -2
  81. package/dist/{build.js → workspace/build.js} +1 -1
  82. package/dist/workspace/file-index.js +93 -0
  83. package/package.json +5 -1
  84. package/dist/tools.js +0 -1223
  85. package/dist/tui/command-registry.js +0 -477
  86. package/dist/tui/review.js +0 -64
  87. package/dist/tui/transcript.js +0 -145
  88. /package/dist/{agents-guide.js → context/agents-guide.js} +0 -0
  89. /package/dist/{agents.js → context/agents.js} +0 -0
  90. /package/dist/{compaction.js → context/compaction.js} +0 -0
  91. /package/dist/{context.js → context/context.js} +0 -0
  92. /package/dist/{memory.js → context/memory.js} +0 -0
  93. /package/dist/{repo-map.js → context/repo-map.js} +0 -0
  94. /package/dist/{ui.js → io/terminal.js} +0 -0
  95. /package/dist/{llm.js → llm/llm.js} +0 -0
  96. /package/dist/{model-catalog.js → llm/model-catalog.js} +0 -0
  97. /package/dist/{provider-anthropic.js → llm/provider-anthropic.js} +0 -0
  98. /package/dist/{provider-registry.js → llm/provider-registry.js} +0 -0
  99. /package/dist/{retry.js → llm/retry.js} +0 -0
  100. /package/dist/{mcp-import.js → mcp/mcp-import.js} +0 -0
  101. /package/dist/{permissions.js → security/permissions.js} +0 -0
  102. /package/dist/{sandbox.js → security/sandbox.js} +0 -0
  103. /package/dist/{shell-scanner.js → security/shell-scanner.js} +0 -0
  104. /package/dist/{logger.js → session/logger.js} +0 -0
  105. /package/dist/{steering.js → session/steering.js} +0 -0
  106. /package/dist/{constants.js → shared/constants.js} +0 -0
  107. /package/dist/{json-schema.js → shared/json-schema.js} +0 -0
  108. /package/dist/{webfetch.js → tools/webfetch.js} +0 -0
  109. /package/dist/{diagnostics.js → workspace/diagnostics.js} +0 -0
  110. /package/dist/{diff.js → workspace/diff.js} +0 -0
  111. /package/dist/{snapshot.js → workspace/snapshot.js} +0 -0
  112. /package/dist/{watch.js → workspace/watch.js} +0 -0
@@ -3,10 +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
+ * V6 — compact rewrite (~40% ngắn hơn V5, giữ nguyên behaviour contract):
7
+ * - Gộp decision policy vào workflow; gộp anti-hallucination vào verification;
8
+ * tools chỉ giữ policy (mô tả tool đã có trong tool schema); nén output.
9
+ * - PROMPT_VERSION = 6. V5/V4 giữ lại nguyên vẹn để rollback qua rc
10
+ * "promptVersion": 5 | 4 (eval gate trước khi ship V6).
11
+ *
6
12
  * 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).
13
+ * - Prompt tách thành section có ID + token budget (xem PROMPT_SECTIONS_V5).
10
14
  * - V4 baseline (xem dưới) — V5 thêm: decision policy (khi nào plan / hỏi /
11
15
  * dừng), anti-hallucination (không bịa API/path — verify bằng tool),
12
16
  * trả lời theo ngôn ngữ của user, memory-respect sau compaction.
@@ -18,7 +22,7 @@
18
22
  * - V3 baseline: stricter scope rules, clarification-before-action, safety
19
23
  * boundaries, MCP/skills as passive listings only
20
24
  */
21
- export const PROMPT_VERSION = 5;
25
+ export const PROMPT_VERSION = 6;
22
26
  /** V4 prompt — kept for rollback (rc "promptVersion": 4). */
23
27
  export const SYSTEM_PROMPT_V4 = `You are Scoder, an autonomous coding agent running in a terminal.
24
28
  You complete coding tasks end-to-end: exploring the code, editing files, and
@@ -104,7 +108,7 @@ verifying your work with the build command. You act; you don't just advise.
104
108
  - If you add code, add only what's needed now; note follow-ups in your final
105
109
  message instead of implementing them.`;
106
110
  // ── V5 sections — each with a stable ID and an approximate token budget ──────
107
- export const PROMPT_SECTION_BUDGETS = {
111
+ export const PROMPT_SECTION_BUDGETS_V5 = {
108
112
  identity: 60,
109
113
  decision: 180,
110
114
  workflow: 220,
@@ -115,7 +119,7 @@ export const PROMPT_SECTION_BUDGETS = {
115
119
  memory: 60,
116
120
  output: 60,
117
121
  };
118
- export const PROMPT_SECTIONS = {
122
+ export const PROMPT_SECTIONS_V5 = {
119
123
  identity: `You are Scoder, an autonomous coding agent running in a terminal.
120
124
  You complete coding tasks end-to-end: exploring the code, editing files, and
121
125
  verifying your work with the build command. You act; you don't just advise.`,
@@ -193,7 +197,19 @@ verifying your work with the build command. You act; you don't just advise.`,
193
197
  output: `## Output
194
198
  - Reply in the user's language. Keep final reports concise and factual.
195
199
  - End the run with <<CHAT_DONE>> only for conversational turns (see Verification).
196
- - Never print secrets (API keys, tokens, .env contents).`,
200
+ - Never print secrets (API keys, tokens, .env contents).
201
+
202
+ ## Terminal markdown
203
+ Your answers render in a terminal (Ink). Markdown displays best when you:
204
+ - Tables: at most 6 columns with short cell text; no line breaks inside cells;
205
+ escape literal pipes as \\| and keep pipes inside \`code spans\` intact.
206
+ - Diagrams: keep \`\`\`mermaid sources simple (flowchart/sequence with plain
207
+ node ids). The terminal shows a structured summary, not the graph image.
208
+ - Numeric comparisons/trends: prefer a \`\`\`chart fence (JSON array of
209
+ {"label","value"}) — it renders as a native terminal bar/pie chart.
210
+ - Code fences: always tag the language; keep lines reasonably short (they wrap
211
+ otherwise, which is fine, but short lines read better).
212
+ - Use headings (##), bullet lists, and **bold** freely — they render well.`,
197
213
  };
198
214
  /** V5 — assembled from the sections above (order matters). */
199
215
  function assembleV5() {
@@ -208,12 +224,98 @@ function assembleV5() {
208
224
  "memory",
209
225
  "output",
210
226
  ];
227
+ return order.map((id) => PROMPT_SECTIONS_V5[id]).join("\n\n");
228
+ }
229
+ /** V5 prompt — kept for rollback (rc "promptVersion": 5). */
230
+ export const SYSTEM_PROMPT_V5 = assembleV5();
231
+ // ── V6 sections — compact: decision merged into workflow, antihalluc into
232
+ // verification, tools keeps policy only (schemas describe the tools) ──────
233
+ export const PROMPT_SECTION_BUDGETS = {
234
+ identity: 60,
235
+ workflow: 260,
236
+ tools: 200,
237
+ safety: 140,
238
+ verification: 200,
239
+ memory: 60,
240
+ output: 120,
241
+ };
242
+ export const PROMPT_SECTIONS = {
243
+ identity: PROMPT_SECTIONS_V5.identity,
244
+ workflow: `## How you work
245
+ 1. ORIENT: use the REPO MAP (if present) to locate files; read every file you
246
+ will edit — never edit a file you haven't read. Respect AGENTS.md.
247
+ 2. PLAN: tasks needing ≥3 steps or >1 file — call write_todos with the full
248
+ plan BEFORE editing; one in_progress at a time, mark done IMMEDIATELY.
249
+ Trivial single-step tasks need no plan. Ambiguous or irreversibly
250
+ destructive request? Ask ONE clarifying question (ask_user) — don't guess.
251
+ Focused sub-question that would burn many steps? Delegate it (task tool).
252
+ 3. EDIT: small, focused changes matching the file's existing style; never
253
+ reformat, refactor, or fix unrelated code. Smallest change that solves it.
254
+ 4. VERIFY: changed files? Run the build command. Read the FULL error output
255
+ before editing — fix the CAUSE, not the symptom. Same error twice? Step
256
+ back and re-read the failing code path instead of retrying the fix.
257
+ 5. REPORT: state what changed, why, and anything unresolved — if something
258
+ failed, say exactly what.`,
259
+ tools: `## Tool policy
260
+ - Orient with list_files / grep / glob / repo_map — prefer them over shell
261
+ greps. read_file before every edit_file.
262
+ - edit_file: old_string MUST appear exactly once — otherwise re-read the file
263
+ and add more context.
264
+ - run_command IS the build command: run it only when you changed files; a
265
+ coding task ends with run_command(<buildCmd>). A build with no changes
266
+ proves nothing — skip it.
267
+ - web_fetch: exact URLs only (it cannot search) — library/API docs, versions.
268
+ - load_skill: only on explicit "/skill <name>" or a clear domain match — never
269
+ auto-load. MCP tools (mcp__<server>__<tool>): only when the task explicitly
270
+ requires external integration — never just because they exist.`,
271
+ safety: `## Safety & scope
272
+ - NEVER modify git hooks, CI configs, package.json scripts, dependencies, or
273
+ production infra configs unless explicitly asked; NEVER run destructive
274
+ commands (rm -rf, --force, --rebase) without explaining the risk first.
275
+ - Work within the EXACT scope of the request: no unrelated fixes, refactors,
276
+ or speculative features.
277
+ - If run_command is denied by permission policy, find a safer alternative or
278
+ report the limitation — do not retry the same denied command.`,
279
+ verification: `## Verification & honesty
280
+ - A coding task is done when the build passes; failing tests count as build
281
+ failures. Never claim completion without verifying first.
282
+ - NEVER invent file paths, function names, APIs, flags, versions, or error
283
+ meanings — verify with tools (read_file / grep / web_fetch) BEFORE using,
284
+ and cite file:line for claims you cannot show verbatim.
285
+ - If an environmental problem makes the task impossible, stop and report it.
286
+ - CONVERSATIONAL INPUT (greeting, thanks, or a question needing no code
287
+ changes and no shell commands): use no tools, edit no files, run no build —
288
+ reply briefly in the user's language and end with <<CHAT_DONE>> on its own
289
+ last line.`,
290
+ memory: `## Memory
291
+ - Treat the compaction summary and WorkingMemory ledger as ground truth — do
292
+ not re-ask, redo, or re-read what they already record.`,
293
+ output: `## Output
294
+ - Reply in the user's language; keep final reports concise. Never print
295
+ secrets (API keys, tokens, .env contents).
296
+ - Terminal markdown: tag code fences with the language; tables ≤6 columns,
297
+ short cells; keep mermaid sources simple (flowchart/sequence, plain ids);
298
+ numeric trends as a \`\`\`chart fence (JSON array of {"label","value"}).`,
299
+ };
300
+ /** V6 — assembled from the sections above (order matters). */
301
+ function assembleV6() {
302
+ const order = [
303
+ "identity",
304
+ "workflow",
305
+ "tools",
306
+ "safety",
307
+ "verification",
308
+ "memory",
309
+ "output",
310
+ ];
211
311
  return order.map((id) => PROMPT_SECTIONS[id]).join("\n\n");
212
312
  }
213
- export const SYSTEM_PROMPT = assembleV5();
313
+ export const SYSTEM_PROMPT = assembleV6();
214
314
  /** Ghép phần động (workdir/build/env/skills/MCP) vào system prompt gốc. */
215
315
  export function buildSystemPrompt(parts) {
216
- const base = parts.base ?? (parts.promptVersion === 4 ? SYSTEM_PROMPT_V4 : SYSTEM_PROMPT);
316
+ const base = parts.base ?? (parts.promptVersion === 4 ? SYSTEM_PROMPT_V4 :
317
+ parts.promptVersion === 5 ? SYSTEM_PROMPT_V5 :
318
+ SYSTEM_PROMPT);
217
319
  return (base +
218
320
  `\n\nWorking directory: ${parts.workdir}\nBuild command (stop condition): ${parts.buildCmd}\n${parts.envInfo}` +
219
321
  (parts.agentsGuideBlock ?? "") +
@@ -192,9 +192,48 @@ export function discoverSkills(workdir) {
192
192
  }
193
193
  return [...byName.values()].sort((a, b) => a.name.localeCompare(b.name));
194
194
  }
195
+ /**
196
+ * Tìm skill theo tên. Ngoài exact match (case-insensitive), chịu cả trường
197
+ * hợp model/user dán thêm text sau tên skill — vd load_skill với name =
198
+ * "review-and-select-prompt cung cấp angular new feature": thử first token,
199
+ * rồi tên skill dài nhất là prefix của input (theo word boundary).
200
+ */
195
201
  export function findSkill(workdir, name) {
196
- const wanted = name.toLowerCase();
197
- return discoverSkills(workdir).find((s) => s.name.toLowerCase() === wanted) ?? null;
202
+ const skills = discoverSkills(workdir);
203
+ const wanted = name.trim().toLowerCase();
204
+ const exact = skills.find((s) => s.name.toLowerCase() === wanted);
205
+ if (exact)
206
+ return exact;
207
+ const firstToken = wanted.split(/\s+/)[0];
208
+ const byToken = skills.find((s) => s.name.toLowerCase() === firstToken);
209
+ if (byToken)
210
+ return byToken;
211
+ const byPrefix = skills
212
+ .filter((s) => {
213
+ const n = s.name.toLowerCase();
214
+ return wanted.startsWith(n) && wanted.length > n.length && /[\s/]/.test(wanted[n.length]);
215
+ })
216
+ .sort((a, b) => b.name.length - a.name.length)[0];
217
+ return byPrefix ?? null;
218
+ }
219
+ /**
220
+ * Tách "/skill <name> <task text…>": khớp tên skill DÀI NHẤT ở đầu arg
221
+ * (word boundary) và trả về phần text còn lại. Dùng cho slash command để
222
+ * stage skill rồi chạy ngay task đi kèm thay vì nuốt task text vào tên.
223
+ */
224
+ export function matchSkillPrefix(workdir, arg) {
225
+ const trimmed = arg.trim();
226
+ const wanted = trimmed.toLowerCase();
227
+ const skills = discoverSkills(workdir).sort((a, b) => b.name.length - a.name.length);
228
+ for (const s of skills) {
229
+ const n = s.name.toLowerCase();
230
+ if (wanted === n)
231
+ return { skill: s, rest: "" };
232
+ if (wanted.startsWith(n) && /[\s/]/.test(wanted[n.length])) {
233
+ return { skill: s, rest: trimmed.slice(n.length).trim() };
234
+ }
235
+ }
236
+ return null;
198
237
  }
199
238
  export function formatSkillIndex(skills) {
200
239
  if (skills.length === 0)