agent-from-zero 0.30.0 → 0.31.0

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/dist/agent.js CHANGED
@@ -19,7 +19,7 @@ import { clearTodos, getTodos, renderTodos } from "./todos.js"; // the agent's p
19
19
  import { renderDiff } from "./diff.js"; // show what /undo put back / what /diff changed, reusing the Day 21 diff renderer
20
20
  import path from "node:path"; // shorten paths for the /diff summary
21
21
  import { expandMentions } from "./mentions.js"; // @file mentions: pull referenced files into context (secret files refused)
22
- import { banner, framedPrompt, formatModelChoices, statusLine } from "./ui.js"; // welcome box, prompt, model labels, status line
22
+ import { banner, framedPrompt, formatModelChoices, statusLine, sentMessage } from "./ui.js"; // welcome box, prompt, model labels, status line, sent-message echo
23
23
  import { gitBranch, toggleLastCollapsed, revealReasoning, clearReasoning, revealToolCalls, clearToolCalls, cleanup as tuiCleanup } from "./tui.js"; // git branch + collapsible output + collapsed reasoning (Ctrl+R) + folded tool-call trace (Ctrl+T)
24
24
  import { promptSelect, promptForm } from "./menu.js"; // arrow-key approval menu + multi-question form
25
25
  import { editLine } from "./editor.js"; // our own line editor (keeps the status footer pinned even when input wraps)
@@ -249,7 +249,7 @@ async function main() {
249
249
  lineWaiters.shift()("exit"); // wake every waiter with a polite "exit"
250
250
  });
251
251
  // Get the next line of user input, showing a prompt if we have to wait.
252
- const readUserLine = async (prompt, footer) => {
252
+ const readUserLine = async (prompt, footer, echo) => {
253
253
  if (pendingLines.length)
254
254
  return pendingLines.shift(); // typed-ahead (or piped) line — use it
255
255
  if (stdinDone)
@@ -259,7 +259,7 @@ async function main() {
259
259
  // so the status bar stays pinned below however the line wraps.
260
260
  editing = true;
261
261
  try {
262
- const res = await editLine(rl, { prompt, footer, history, onTab: () => toggleLastCollapsed(), onReveal: () => revealReasoning(), onTools: () => revealToolCalls(), transformPaste: normalizeDroppedPaths });
262
+ const res = await editLine(rl, { prompt, footer, history, echo, onTab: () => toggleLastCollapsed(), onReveal: () => revealReasoning(), onTools: () => revealToolCalls(), transformPaste: normalizeDroppedPaths });
263
263
  if (res.type === "line") {
264
264
  if (res.value.trim())
265
265
  history.push(res.value); // remember non-empty entries for ↑/↓
@@ -648,7 +648,7 @@ async function main() {
648
648
  console.log(""); // one blank line separates the previous answer from the prompt
649
649
  footer = statusLine(CONFIG.model, path.basename(process.cwd()), gitBranch(), ctxPct, costMeter.cost(), Date.now() - sessionStartedAt); // status pinned below the prompt
650
650
  }
651
- const line = (await readUserLine(framedPrompt(isPlanMode()), footer)).trim(); // next input, or "exit" on EOF
651
+ const line = (await readUserLine(framedPrompt(isPlanMode()), footer, sentMessage)).trim(); // next input (echoed as a sent message above), or "exit" on EOF
652
652
  if (!line)
653
653
  continue; // empty line — just re-prompt
654
654
  if (line === "exit" || line === "quit") {
package/dist/editor.js CHANGED
@@ -239,13 +239,24 @@ export function editLine(rl, opts) {
239
239
  input.off("keypress", onKey);
240
240
  for (const l of otherKeypress)
241
241
  input.on("keypress", l); // give readline its keypress handler back
242
- // Leave the typed command on screen, drop the footer, land on a fresh line.
243
- const { rows } = layout(state.buffer, state.cursor, promptW, cols());
242
+ // Climb to the top of the input block and wipe it (prompt + wrapped input + footer).
244
243
  if (prevCursorRow > 0)
245
244
  out.write(`\x1b[${prevCursorRow}A`);
246
245
  out.write("\r\x1b[0J");
247
- const inputRows = rows.map((r, i) => (i === 0 ? opts.prompt + r : r));
248
- out.write(inputRows.join("\r\n") + "\r\n");
246
+ // What stays on screen: with an echo styler (the main REPL prompt), a
247
+ // submitted message is re-rendered as a distinct "sent" line and the input
248
+ // box vanishes — your message scrolls up, the input clears, like a chat app.
249
+ // Without it (sub-prompts), leave the typed command under its prompt.
250
+ if (opts.echo) {
251
+ if (result.type === "line" && state.buffer.trim())
252
+ out.write(opts.echo(state.buffer) + "\r\n");
253
+ // empty submit / cancel / eof: leave nothing — the wipe above already cleared it
254
+ }
255
+ else {
256
+ const { rows } = layout(state.buffer, state.cursor, promptW, cols());
257
+ const inputRows = rows.map((r, i) => (i === 0 ? opts.prompt + r : r));
258
+ out.write(inputRows.join("\r\n") + "\r\n");
259
+ }
249
260
  try {
250
261
  input.setRawMode(wasRaw); // hand stdin back exactly as we found it
251
262
  }
package/dist/ui.js CHANGED
@@ -124,6 +124,12 @@ export function inputRule() {
124
124
  export function framedPrompt(planMode) {
125
125
  return planMode ? chalk.yellow.bold("⏸ plan ❯ ") : chalk.cyan.bold("❯ ");
126
126
  }
127
+ // How a SUBMITTED message is echoed into the scrollback once you hit Enter: a
128
+ // distinct, muted "> …" line (not the bright ❯ prompt) so each turn reads like a
129
+ // sent chat message scrolling up, leaving the input box empty below.
130
+ export function sentMessage(text) {
131
+ return chalk.dim("> " + text);
132
+ }
127
133
  // ---- the selection menu -----------------------------------------------------
128
134
  // Render a numbered list of options with one highlighted. The interactive part
129
135
  // (raw-mode arrow keys) lives in menu.ts; this is just the pure drawing, so it's
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-from-zero",
3
- "version": "0.30.0",
3
+ "version": "0.31.0",
4
4
  "description": "Build a Claude Code-style CLI agent from scratch, with AI as your pair programmer — companion code for a 10-day tutorial (DeepSeek by default, any OpenAI-compatible model)",
5
5
  "type": "module",
6
6
  "bin": {
@@ -16,7 +16,7 @@
16
16
  "eval": "tsx --env-file=.env eval/run.ts",
17
17
  "build": "tsc -p tsconfig.build.json",
18
18
  "prepare": "npm run build",
19
- "test": "tsx tests/tools.test.ts && tsx tests/permissions.test.ts && tsx tests/context.test.ts && tsx tests/hooks.test.ts && tsx tests/mcp.test.ts && tsx tests/judge.test.ts && tsx tests/memory.test.ts && tsx tests/cost.test.ts && tsx tests/validate.test.ts && tsx tests/plan.test.ts && tsx tests/diff.test.ts && tsx tests/undo.test.ts && tsx tests/tiering.test.ts && tsx tests/mcp-http.test.ts && tsx tests/changes.test.ts && tsx tests/mentions.test.ts && tsx tests/session.test.ts && tsx tests/ui.test.ts && tsx tests/approval.test.ts && tsx tests/form.test.ts && tsx tests/model.test.ts && tsx tests/reasoning.test.ts && tsx tests/skills.test.ts && tsx tests/markdown.test.ts && tsx tests/editor.test.ts && tsx tests/todos.test.ts && tsx tests/background.test.ts && tsx tests/team.test.ts && tsx tests/board.test.ts && tsx tests/drop.test.ts"
19
+ "test": "tsx tests/tools.test.ts && tsx tests/permissions.test.ts && tsx tests/context.test.ts && tsx tests/hooks.test.ts && tsx tests/mcp.test.ts && tsx tests/judge.test.ts && tsx tests/memory.test.ts && tsx tests/cost.test.ts && tsx tests/validate.test.ts && tsx tests/plan.test.ts && tsx tests/diff.test.ts && tsx tests/undo.test.ts && tsx tests/tiering.test.ts && tsx tests/mcp-http.test.ts && tsx tests/changes.test.ts && tsx tests/mentions.test.ts && tsx tests/session.test.ts && tsx tests/ui.test.ts && tsx tests/approval.test.ts && tsx tests/form.test.ts && tsx tests/model.test.ts && tsx tests/reasoning.test.ts && tsx tests/skills.test.ts && tsx tests/markdown.test.ts && tsx tests/editor.test.ts && tsx tests/todos.test.ts && tsx tests/background.test.ts && tsx tests/team.test.ts && tsx tests/board.test.ts && tsx tests/drop.test.ts && tsx tests/editline.test.ts"
20
20
  },
21
21
  "engines": {
22
22
  "node": ">=20.6"