@robota-sdk/agent-cli 3.0.0-beta.33 → 3.0.0-beta.34

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/node/bin.cjs CHANGED
@@ -24,7 +24,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
24
24
  ));
25
25
 
26
26
  // src/cli.ts
27
- var import_node_fs3 = require("fs");
27
+ var import_node_fs4 = require("fs");
28
28
  var import_node_path5 = require("path");
29
29
  var import_node_url = require("url");
30
30
  var import_agent_sdk5 = require("@robota-sdk/agent-sdk");
@@ -175,25 +175,69 @@ var import_react = require("react");
175
175
  var import_agent_sdk = require("@robota-sdk/agent-sdk");
176
176
 
177
177
  // src/utils/edit-diff.ts
178
- function generateDiffLines(oldStr, newStr) {
178
+ var import_node_fs2 = require("fs");
179
+ var CONTEXT_LINES = 2;
180
+ function generateDiffLines(oldStr, newStr, startLine = 1) {
179
181
  if (oldStr === newStr) return [];
180
182
  const lines = [];
181
- for (const line of oldStr.split("\n")) {
182
- lines.push({ type: "remove", text: line });
183
+ const oldLines = oldStr.split("\n");
184
+ const newLines = newStr.split("\n");
185
+ for (let i = 0; i < oldLines.length; i++) {
186
+ lines.push({ type: "remove", text: oldLines[i], lineNumber: startLine + i });
183
187
  }
184
- for (const line of newStr.split("\n")) {
185
- lines.push({ type: "add", text: line });
188
+ for (let i = 0; i < newLines.length; i++) {
189
+ lines.push({ type: "add", text: newLines[i], lineNumber: startLine + i });
186
190
  }
187
191
  return lines;
188
192
  }
189
- function extractEditDiff(toolName, toolArgs) {
193
+ function generateDiffLinesWithContext(oldStr, newStr, startLine, filePath) {
194
+ if (oldStr === newStr) return [];
195
+ const diffLines = generateDiffLines(oldStr, newStr, startLine);
196
+ let fileLines;
197
+ try {
198
+ fileLines = (0, import_node_fs2.readFileSync)(filePath, "utf-8").split("\n");
199
+ } catch {
200
+ return diffLines;
201
+ }
202
+ const result = [];
203
+ const contextStart = Math.max(0, startLine - 1 - CONTEXT_LINES);
204
+ for (let i = contextStart; i < startLine - 1; i++) {
205
+ if (i < fileLines.length) {
206
+ result.push({ type: "context", text: fileLines[i], lineNumber: i + 1 });
207
+ }
208
+ }
209
+ result.push(...diffLines);
210
+ const newLineCount = newStr.split("\n").length;
211
+ const afterStart = startLine - 1 + newLineCount;
212
+ for (let i = afterStart; i < afterStart + CONTEXT_LINES; i++) {
213
+ if (i < fileLines.length) {
214
+ result.push({ type: "context", text: fileLines[i], lineNumber: i + 1 });
215
+ }
216
+ }
217
+ return result;
218
+ }
219
+ function extractEditDiff(toolName, toolArgs, startLine) {
190
220
  if (toolName !== "Edit" || !toolArgs) return null;
191
221
  const filePath = toolArgs.file_path ?? toolArgs.filePath;
192
222
  const oldStr = toolArgs.old_string ?? toolArgs.oldString;
193
223
  const newStr = toolArgs.new_string ?? toolArgs.newString;
194
224
  if (typeof filePath !== "string") return null;
195
225
  if (typeof oldStr !== "string" || typeof newStr !== "string") return null;
196
- const lines = generateDiffLines(oldStr, newStr);
226
+ let sl = startLine ?? 0;
227
+ if (!sl) {
228
+ try {
229
+ const fileContent = (0, import_node_fs2.readFileSync)(filePath, "utf-8");
230
+ const idx = fileContent.indexOf(newStr);
231
+ if (idx >= 0) {
232
+ sl = fileContent.substring(0, idx).split("\n").length;
233
+ } else {
234
+ sl = 1;
235
+ }
236
+ } catch {
237
+ sl = 1;
238
+ }
239
+ }
240
+ const lines = generateDiffLinesWithContext(oldStr, newStr, sl, filePath);
197
241
  if (lines.length === 0) return null;
198
242
  return { file: filePath, lines };
199
243
  }
@@ -271,9 +315,20 @@ function useSession(props) {
271
315
  setActiveTools((prev) => {
272
316
  const updated = prev.map((t) => {
273
317
  if (!(t.toolName === event.toolName && t.isRunning)) return t;
318
+ let startLine;
319
+ if (event.toolResultData && event.toolName === "Edit") {
320
+ try {
321
+ const parsed = JSON.parse(event.toolResultData);
322
+ if (typeof parsed.startLine === "number") {
323
+ startLine = parsed.startLine;
324
+ }
325
+ } catch {
326
+ }
327
+ }
274
328
  const editDiff = extractEditDiff(
275
329
  event.toolName,
276
- t._toolArgs
330
+ t._toolArgs,
331
+ startLine
277
332
  );
278
333
  const finished = {
279
334
  ...t,
@@ -1073,7 +1128,7 @@ var BuiltinCommandSource = class {
1073
1128
  };
1074
1129
 
1075
1130
  // src/commands/skill-source.ts
1076
- var import_node_fs2 = require("fs");
1131
+ var import_node_fs3 = require("fs");
1077
1132
  var import_node_path2 = require("path");
1078
1133
  var import_node_os = require("os");
1079
1134
  var BOOLEAN_KEYS = /* @__PURE__ */ new Set(["disable-model-invocation", "user-invocable"]);
@@ -1122,27 +1177,27 @@ function buildCommand(frontmatter, content, fallbackName) {
1122
1177
  return cmd;
1123
1178
  }
1124
1179
  function scanSkillsDir(skillsDir) {
1125
- if (!(0, import_node_fs2.existsSync)(skillsDir)) return [];
1180
+ if (!(0, import_node_fs3.existsSync)(skillsDir)) return [];
1126
1181
  const commands = [];
1127
- const entries = (0, import_node_fs2.readdirSync)(skillsDir, { withFileTypes: true });
1182
+ const entries = (0, import_node_fs3.readdirSync)(skillsDir, { withFileTypes: true });
1128
1183
  for (const entry of entries) {
1129
1184
  if (!entry.isDirectory()) continue;
1130
1185
  const skillFile = (0, import_node_path2.join)(skillsDir, entry.name, "SKILL.md");
1131
- if (!(0, import_node_fs2.existsSync)(skillFile)) continue;
1132
- const content = (0, import_node_fs2.readFileSync)(skillFile, "utf-8");
1186
+ if (!(0, import_node_fs3.existsSync)(skillFile)) continue;
1187
+ const content = (0, import_node_fs3.readFileSync)(skillFile, "utf-8");
1133
1188
  const frontmatter = parseFrontmatter(content);
1134
1189
  commands.push(buildCommand(frontmatter, content, entry.name));
1135
1190
  }
1136
1191
  return commands;
1137
1192
  }
1138
1193
  function scanCommandsDir(commandsDir) {
1139
- if (!(0, import_node_fs2.existsSync)(commandsDir)) return [];
1194
+ if (!(0, import_node_fs3.existsSync)(commandsDir)) return [];
1140
1195
  const commands = [];
1141
- const entries = (0, import_node_fs2.readdirSync)(commandsDir, { withFileTypes: true });
1196
+ const entries = (0, import_node_fs3.readdirSync)(commandsDir, { withFileTypes: true });
1142
1197
  for (const entry of entries) {
1143
1198
  if (!entry.isFile() || !entry.name.endsWith(".md")) continue;
1144
1199
  const filePath = (0, import_node_path2.join)(commandsDir, entry.name);
1145
- const content = (0, import_node_fs2.readFileSync)(filePath, "utf-8");
1200
+ const content = (0, import_node_fs3.readFileSync)(filePath, "utf-8");
1146
1201
  const frontmatter = parseFrontmatter(content);
1147
1202
  const fallbackName = (0, import_node_path2.basename)(entry.name, ".md");
1148
1203
  commands.push(buildCommand(frontmatter, content, fallbackName));
@@ -1391,23 +1446,41 @@ function renderMarkdown(md) {
1391
1446
  // src/ui/DiffBlock.tsx
1392
1447
  var import_ink = require("ink");
1393
1448
  var import_jsx_runtime = require("react/jsx-runtime");
1394
- var MAX_DIFF_LINES = 10;
1395
- var TRUNCATED_SHOW = 8;
1449
+ var MAX_DIFF_LINES = 12;
1450
+ var TRUNCATED_SHOW = 10;
1396
1451
  function DiffBlock({ file, lines }) {
1397
1452
  const truncated = lines.length > MAX_DIFF_LINES;
1398
1453
  const visible = truncated ? lines.slice(0, TRUNCATED_SHOW) : lines;
1399
1454
  const remaining = lines.length - TRUNCATED_SHOW;
1455
+ const maxLineNum = Math.max(...visible.map((l) => l.lineNumber), 0);
1456
+ const numWidth = String(maxLineNum).length;
1400
1457
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_ink.Box, { flexDirection: "column", marginLeft: 4, children: [
1401
1458
  file && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_ink.Text, { color: "white", dimColor: true, children: [
1402
1459
  "\u2502 ",
1403
1460
  file
1404
1461
  ] }),
1405
- visible.map((line, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_ink.Text, { color: line.type === "remove" ? "red" : "greenBright", children: [
1406
- "\u2502 ",
1407
- line.type === "remove" ? "-" : "+",
1408
- " ",
1409
- line.text
1410
- ] }, i)),
1462
+ visible.map((line, i) => {
1463
+ const lineNum = String(line.lineNumber).padStart(numWidth, " ");
1464
+ if (line.type === "context") {
1465
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_ink.Text, { color: "white", dimColor: true, children: [
1466
+ "\u2502 ",
1467
+ lineNum,
1468
+ " ",
1469
+ line.text
1470
+ ] }, i);
1471
+ }
1472
+ const prefix = line.type === "remove" ? "-" : "+";
1473
+ const bgColor = line.type === "remove" ? "#5c1a1a" : "#1a3d1a";
1474
+ const fgColor = line.type === "remove" ? "#ff9999" : "#99ff99";
1475
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_ink.Text, { color: fgColor, backgroundColor: bgColor, children: [
1476
+ "\u2502 ",
1477
+ lineNum,
1478
+ " ",
1479
+ prefix,
1480
+ " ",
1481
+ line.text
1482
+ ] }, i);
1483
+ }),
1411
1484
  truncated && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_ink.Text, { color: "white", dimColor: true, children: [
1412
1485
  "\u2502 ... and ",
1413
1486
  remaining,
@@ -2228,9 +2301,9 @@ function renderApp(options) {
2228
2301
  // src/cli.ts
2229
2302
  var import_meta = {};
2230
2303
  function checkSettingsFile(filePath) {
2231
- if (!(0, import_node_fs3.existsSync)(filePath)) return "missing";
2304
+ if (!(0, import_node_fs4.existsSync)(filePath)) return "missing";
2232
2305
  try {
2233
- const raw = (0, import_node_fs3.readFileSync)(filePath, "utf8").trim();
2306
+ const raw = (0, import_node_fs4.readFileSync)(filePath, "utf8").trim();
2234
2307
  if (raw.length === 0) return "incomplete";
2235
2308
  const parsed = JSON.parse(raw);
2236
2309
  const provider = parsed.provider;
@@ -2247,7 +2320,7 @@ function readVersion() {
2247
2320
  const candidates = [(0, import_node_path5.join)(dir, "..", "..", "package.json"), (0, import_node_path5.join)(dir, "..", "package.json")];
2248
2321
  for (const pkgPath of candidates) {
2249
2322
  try {
2250
- const raw = (0, import_node_fs3.readFileSync)(pkgPath, "utf-8");
2323
+ const raw = (0, import_node_fs4.readFileSync)(pkgPath, "utf-8");
2251
2324
  const pkg = JSON.parse(raw);
2252
2325
  if (pkg.version !== void 0 && pkg.name !== void 0) {
2253
2326
  return pkg.version;
@@ -2335,7 +2408,7 @@ async function ensureConfig(cwd) {
2335
2408
  }
2336
2409
  const language = await promptInput(" Response language (ko/en/ja/zh, default: en): ");
2337
2410
  const settingsDir = (0, import_node_path5.dirname)(userPath);
2338
- (0, import_node_fs3.mkdirSync)(settingsDir, { recursive: true });
2411
+ (0, import_node_fs4.mkdirSync)(settingsDir, { recursive: true });
2339
2412
  const settings = {
2340
2413
  provider: {
2341
2414
  name: "anthropic",
@@ -2346,7 +2419,7 @@ async function ensureConfig(cwd) {
2346
2419
  if (language) {
2347
2420
  settings.language = language;
2348
2421
  }
2349
- (0, import_node_fs3.writeFileSync)(userPath, JSON.stringify(settings, null, 2) + "\n", "utf8");
2422
+ (0, import_node_fs4.writeFileSync)(userPath, JSON.stringify(settings, null, 2) + "\n", "utf8");
2350
2423
  process.stdout.write(`
2351
2424
  Config saved to ${userPath}
2352
2425
 
package/dist/node/bin.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  startCli
4
- } from "./chunk-EPCRZIQ6.js";
4
+ } from "./chunk-RDPIMQOC.js";
5
5
 
6
6
  // src/bin.ts
7
7
  process.on("uncaughtException", (err) => {
@@ -1,5 +1,5 @@
1
1
  // src/cli.ts
2
- import { readFileSync as readFileSync3, existsSync as existsSync3, mkdirSync as mkdirSync2, writeFileSync as writeFileSync2 } from "fs";
2
+ import { readFileSync as readFileSync4, existsSync as existsSync3, mkdirSync as mkdirSync2, writeFileSync as writeFileSync2 } from "fs";
3
3
  import { join as join5, dirname as dirname3 } from "path";
4
4
  import { fileURLToPath } from "url";
5
5
  import {
@@ -158,25 +158,69 @@ import { useState, useCallback, useRef } from "react";
158
158
  import { createSession, FileSessionLogger, projectPaths } from "@robota-sdk/agent-sdk";
159
159
 
160
160
  // src/utils/edit-diff.ts
161
- function generateDiffLines(oldStr, newStr) {
161
+ import { readFileSync as readFileSync2 } from "fs";
162
+ var CONTEXT_LINES = 2;
163
+ function generateDiffLines(oldStr, newStr, startLine = 1) {
162
164
  if (oldStr === newStr) return [];
163
165
  const lines = [];
164
- for (const line of oldStr.split("\n")) {
165
- lines.push({ type: "remove", text: line });
166
+ const oldLines = oldStr.split("\n");
167
+ const newLines = newStr.split("\n");
168
+ for (let i = 0; i < oldLines.length; i++) {
169
+ lines.push({ type: "remove", text: oldLines[i], lineNumber: startLine + i });
166
170
  }
167
- for (const line of newStr.split("\n")) {
168
- lines.push({ type: "add", text: line });
171
+ for (let i = 0; i < newLines.length; i++) {
172
+ lines.push({ type: "add", text: newLines[i], lineNumber: startLine + i });
169
173
  }
170
174
  return lines;
171
175
  }
172
- function extractEditDiff(toolName, toolArgs) {
176
+ function generateDiffLinesWithContext(oldStr, newStr, startLine, filePath) {
177
+ if (oldStr === newStr) return [];
178
+ const diffLines = generateDiffLines(oldStr, newStr, startLine);
179
+ let fileLines;
180
+ try {
181
+ fileLines = readFileSync2(filePath, "utf-8").split("\n");
182
+ } catch {
183
+ return diffLines;
184
+ }
185
+ const result = [];
186
+ const contextStart = Math.max(0, startLine - 1 - CONTEXT_LINES);
187
+ for (let i = contextStart; i < startLine - 1; i++) {
188
+ if (i < fileLines.length) {
189
+ result.push({ type: "context", text: fileLines[i], lineNumber: i + 1 });
190
+ }
191
+ }
192
+ result.push(...diffLines);
193
+ const newLineCount = newStr.split("\n").length;
194
+ const afterStart = startLine - 1 + newLineCount;
195
+ for (let i = afterStart; i < afterStart + CONTEXT_LINES; i++) {
196
+ if (i < fileLines.length) {
197
+ result.push({ type: "context", text: fileLines[i], lineNumber: i + 1 });
198
+ }
199
+ }
200
+ return result;
201
+ }
202
+ function extractEditDiff(toolName, toolArgs, startLine) {
173
203
  if (toolName !== "Edit" || !toolArgs) return null;
174
204
  const filePath = toolArgs.file_path ?? toolArgs.filePath;
175
205
  const oldStr = toolArgs.old_string ?? toolArgs.oldString;
176
206
  const newStr = toolArgs.new_string ?? toolArgs.newString;
177
207
  if (typeof filePath !== "string") return null;
178
208
  if (typeof oldStr !== "string" || typeof newStr !== "string") return null;
179
- const lines = generateDiffLines(oldStr, newStr);
209
+ let sl = startLine ?? 0;
210
+ if (!sl) {
211
+ try {
212
+ const fileContent = readFileSync2(filePath, "utf-8");
213
+ const idx = fileContent.indexOf(newStr);
214
+ if (idx >= 0) {
215
+ sl = fileContent.substring(0, idx).split("\n").length;
216
+ } else {
217
+ sl = 1;
218
+ }
219
+ } catch {
220
+ sl = 1;
221
+ }
222
+ }
223
+ const lines = generateDiffLinesWithContext(oldStr, newStr, sl, filePath);
180
224
  if (lines.length === 0) return null;
181
225
  return { file: filePath, lines };
182
226
  }
@@ -254,9 +298,20 @@ function useSession(props) {
254
298
  setActiveTools((prev) => {
255
299
  const updated = prev.map((t) => {
256
300
  if (!(t.toolName === event.toolName && t.isRunning)) return t;
301
+ let startLine;
302
+ if (event.toolResultData && event.toolName === "Edit") {
303
+ try {
304
+ const parsed = JSON.parse(event.toolResultData);
305
+ if (typeof parsed.startLine === "number") {
306
+ startLine = parsed.startLine;
307
+ }
308
+ } catch {
309
+ }
310
+ }
257
311
  const editDiff = extractEditDiff(
258
312
  event.toolName,
259
- t._toolArgs
313
+ t._toolArgs,
314
+ startLine
260
315
  );
261
316
  const finished = {
262
317
  ...t,
@@ -1060,7 +1115,7 @@ var BuiltinCommandSource = class {
1060
1115
  };
1061
1116
 
1062
1117
  // src/commands/skill-source.ts
1063
- import { readdirSync, readFileSync as readFileSync2, existsSync as existsSync2 } from "fs";
1118
+ import { readdirSync, readFileSync as readFileSync3, existsSync as existsSync2 } from "fs";
1064
1119
  import { join as join2, basename } from "path";
1065
1120
  import { homedir } from "os";
1066
1121
  var BOOLEAN_KEYS = /* @__PURE__ */ new Set(["disable-model-invocation", "user-invocable"]);
@@ -1116,7 +1171,7 @@ function scanSkillsDir(skillsDir) {
1116
1171
  if (!entry.isDirectory()) continue;
1117
1172
  const skillFile = join2(skillsDir, entry.name, "SKILL.md");
1118
1173
  if (!existsSync2(skillFile)) continue;
1119
- const content = readFileSync2(skillFile, "utf-8");
1174
+ const content = readFileSync3(skillFile, "utf-8");
1120
1175
  const frontmatter = parseFrontmatter(content);
1121
1176
  commands.push(buildCommand(frontmatter, content, entry.name));
1122
1177
  }
@@ -1129,7 +1184,7 @@ function scanCommandsDir(commandsDir) {
1129
1184
  for (const entry of entries) {
1130
1185
  if (!entry.isFile() || !entry.name.endsWith(".md")) continue;
1131
1186
  const filePath = join2(commandsDir, entry.name);
1132
- const content = readFileSync2(filePath, "utf-8");
1187
+ const content = readFileSync3(filePath, "utf-8");
1133
1188
  const frontmatter = parseFrontmatter(content);
1134
1189
  const fallbackName = basename(entry.name, ".md");
1135
1190
  commands.push(buildCommand(frontmatter, content, fallbackName));
@@ -1383,23 +1438,41 @@ function renderMarkdown(md) {
1383
1438
  // src/ui/DiffBlock.tsx
1384
1439
  import { Box, Text } from "ink";
1385
1440
  import { jsxs } from "react/jsx-runtime";
1386
- var MAX_DIFF_LINES = 10;
1387
- var TRUNCATED_SHOW = 8;
1441
+ var MAX_DIFF_LINES = 12;
1442
+ var TRUNCATED_SHOW = 10;
1388
1443
  function DiffBlock({ file, lines }) {
1389
1444
  const truncated = lines.length > MAX_DIFF_LINES;
1390
1445
  const visible = truncated ? lines.slice(0, TRUNCATED_SHOW) : lines;
1391
1446
  const remaining = lines.length - TRUNCATED_SHOW;
1447
+ const maxLineNum = Math.max(...visible.map((l) => l.lineNumber), 0);
1448
+ const numWidth = String(maxLineNum).length;
1392
1449
  return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginLeft: 4, children: [
1393
1450
  file && /* @__PURE__ */ jsxs(Text, { color: "white", dimColor: true, children: [
1394
1451
  "\u2502 ",
1395
1452
  file
1396
1453
  ] }),
1397
- visible.map((line, i) => /* @__PURE__ */ jsxs(Text, { color: line.type === "remove" ? "red" : "greenBright", children: [
1398
- "\u2502 ",
1399
- line.type === "remove" ? "-" : "+",
1400
- " ",
1401
- line.text
1402
- ] }, i)),
1454
+ visible.map((line, i) => {
1455
+ const lineNum = String(line.lineNumber).padStart(numWidth, " ");
1456
+ if (line.type === "context") {
1457
+ return /* @__PURE__ */ jsxs(Text, { color: "white", dimColor: true, children: [
1458
+ "\u2502 ",
1459
+ lineNum,
1460
+ " ",
1461
+ line.text
1462
+ ] }, i);
1463
+ }
1464
+ const prefix = line.type === "remove" ? "-" : "+";
1465
+ const bgColor = line.type === "remove" ? "#5c1a1a" : "#1a3d1a";
1466
+ const fgColor = line.type === "remove" ? "#ff9999" : "#99ff99";
1467
+ return /* @__PURE__ */ jsxs(Text, { color: fgColor, backgroundColor: bgColor, children: [
1468
+ "\u2502 ",
1469
+ lineNum,
1470
+ " ",
1471
+ prefix,
1472
+ " ",
1473
+ line.text
1474
+ ] }, i);
1475
+ }),
1403
1476
  truncated && /* @__PURE__ */ jsxs(Text, { color: "white", dimColor: true, children: [
1404
1477
  "\u2502 ... and ",
1405
1478
  remaining,
@@ -2221,7 +2294,7 @@ function renderApp(options) {
2221
2294
  function checkSettingsFile(filePath) {
2222
2295
  if (!existsSync3(filePath)) return "missing";
2223
2296
  try {
2224
- const raw = readFileSync3(filePath, "utf8").trim();
2297
+ const raw = readFileSync4(filePath, "utf8").trim();
2225
2298
  if (raw.length === 0) return "incomplete";
2226
2299
  const parsed = JSON.parse(raw);
2227
2300
  const provider = parsed.provider;
@@ -2238,7 +2311,7 @@ function readVersion() {
2238
2311
  const candidates = [join5(dir, "..", "..", "package.json"), join5(dir, "..", "package.json")];
2239
2312
  for (const pkgPath of candidates) {
2240
2313
  try {
2241
- const raw = readFileSync3(pkgPath, "utf-8");
2314
+ const raw = readFileSync4(pkgPath, "utf-8");
2242
2315
  const pkg = JSON.parse(raw);
2243
2316
  if (pkg.version !== void 0 && pkg.name !== void 0) {
2244
2317
  return pkg.version;
@@ -40,7 +40,7 @@ module.exports = __toCommonJS(index_exports);
40
40
  var import_agent_sdk7 = require("@robota-sdk/agent-sdk");
41
41
 
42
42
  // src/cli.ts
43
- var import_node_fs3 = require("fs");
43
+ var import_node_fs4 = require("fs");
44
44
  var import_node_path5 = require("path");
45
45
  var import_node_url = require("url");
46
46
  var import_agent_sdk5 = require("@robota-sdk/agent-sdk");
@@ -191,25 +191,69 @@ var import_react = require("react");
191
191
  var import_agent_sdk = require("@robota-sdk/agent-sdk");
192
192
 
193
193
  // src/utils/edit-diff.ts
194
- function generateDiffLines(oldStr, newStr) {
194
+ var import_node_fs2 = require("fs");
195
+ var CONTEXT_LINES = 2;
196
+ function generateDiffLines(oldStr, newStr, startLine = 1) {
195
197
  if (oldStr === newStr) return [];
196
198
  const lines = [];
197
- for (const line of oldStr.split("\n")) {
198
- lines.push({ type: "remove", text: line });
199
+ const oldLines = oldStr.split("\n");
200
+ const newLines = newStr.split("\n");
201
+ for (let i = 0; i < oldLines.length; i++) {
202
+ lines.push({ type: "remove", text: oldLines[i], lineNumber: startLine + i });
199
203
  }
200
- for (const line of newStr.split("\n")) {
201
- lines.push({ type: "add", text: line });
204
+ for (let i = 0; i < newLines.length; i++) {
205
+ lines.push({ type: "add", text: newLines[i], lineNumber: startLine + i });
202
206
  }
203
207
  return lines;
204
208
  }
205
- function extractEditDiff(toolName, toolArgs) {
209
+ function generateDiffLinesWithContext(oldStr, newStr, startLine, filePath) {
210
+ if (oldStr === newStr) return [];
211
+ const diffLines = generateDiffLines(oldStr, newStr, startLine);
212
+ let fileLines;
213
+ try {
214
+ fileLines = (0, import_node_fs2.readFileSync)(filePath, "utf-8").split("\n");
215
+ } catch {
216
+ return diffLines;
217
+ }
218
+ const result = [];
219
+ const contextStart = Math.max(0, startLine - 1 - CONTEXT_LINES);
220
+ for (let i = contextStart; i < startLine - 1; i++) {
221
+ if (i < fileLines.length) {
222
+ result.push({ type: "context", text: fileLines[i], lineNumber: i + 1 });
223
+ }
224
+ }
225
+ result.push(...diffLines);
226
+ const newLineCount = newStr.split("\n").length;
227
+ const afterStart = startLine - 1 + newLineCount;
228
+ for (let i = afterStart; i < afterStart + CONTEXT_LINES; i++) {
229
+ if (i < fileLines.length) {
230
+ result.push({ type: "context", text: fileLines[i], lineNumber: i + 1 });
231
+ }
232
+ }
233
+ return result;
234
+ }
235
+ function extractEditDiff(toolName, toolArgs, startLine) {
206
236
  if (toolName !== "Edit" || !toolArgs) return null;
207
237
  const filePath = toolArgs.file_path ?? toolArgs.filePath;
208
238
  const oldStr = toolArgs.old_string ?? toolArgs.oldString;
209
239
  const newStr = toolArgs.new_string ?? toolArgs.newString;
210
240
  if (typeof filePath !== "string") return null;
211
241
  if (typeof oldStr !== "string" || typeof newStr !== "string") return null;
212
- const lines = generateDiffLines(oldStr, newStr);
242
+ let sl = startLine ?? 0;
243
+ if (!sl) {
244
+ try {
245
+ const fileContent = (0, import_node_fs2.readFileSync)(filePath, "utf-8");
246
+ const idx = fileContent.indexOf(newStr);
247
+ if (idx >= 0) {
248
+ sl = fileContent.substring(0, idx).split("\n").length;
249
+ } else {
250
+ sl = 1;
251
+ }
252
+ } catch {
253
+ sl = 1;
254
+ }
255
+ }
256
+ const lines = generateDiffLinesWithContext(oldStr, newStr, sl, filePath);
213
257
  if (lines.length === 0) return null;
214
258
  return { file: filePath, lines };
215
259
  }
@@ -287,9 +331,20 @@ function useSession(props) {
287
331
  setActiveTools((prev) => {
288
332
  const updated = prev.map((t) => {
289
333
  if (!(t.toolName === event.toolName && t.isRunning)) return t;
334
+ let startLine;
335
+ if (event.toolResultData && event.toolName === "Edit") {
336
+ try {
337
+ const parsed = JSON.parse(event.toolResultData);
338
+ if (typeof parsed.startLine === "number") {
339
+ startLine = parsed.startLine;
340
+ }
341
+ } catch {
342
+ }
343
+ }
290
344
  const editDiff = extractEditDiff(
291
345
  event.toolName,
292
- t._toolArgs
346
+ t._toolArgs,
347
+ startLine
293
348
  );
294
349
  const finished = {
295
350
  ...t,
@@ -1089,7 +1144,7 @@ var BuiltinCommandSource = class {
1089
1144
  };
1090
1145
 
1091
1146
  // src/commands/skill-source.ts
1092
- var import_node_fs2 = require("fs");
1147
+ var import_node_fs3 = require("fs");
1093
1148
  var import_node_path2 = require("path");
1094
1149
  var import_node_os = require("os");
1095
1150
  var BOOLEAN_KEYS = /* @__PURE__ */ new Set(["disable-model-invocation", "user-invocable"]);
@@ -1138,27 +1193,27 @@ function buildCommand(frontmatter, content, fallbackName) {
1138
1193
  return cmd;
1139
1194
  }
1140
1195
  function scanSkillsDir(skillsDir) {
1141
- if (!(0, import_node_fs2.existsSync)(skillsDir)) return [];
1196
+ if (!(0, import_node_fs3.existsSync)(skillsDir)) return [];
1142
1197
  const commands = [];
1143
- const entries = (0, import_node_fs2.readdirSync)(skillsDir, { withFileTypes: true });
1198
+ const entries = (0, import_node_fs3.readdirSync)(skillsDir, { withFileTypes: true });
1144
1199
  for (const entry of entries) {
1145
1200
  if (!entry.isDirectory()) continue;
1146
1201
  const skillFile = (0, import_node_path2.join)(skillsDir, entry.name, "SKILL.md");
1147
- if (!(0, import_node_fs2.existsSync)(skillFile)) continue;
1148
- const content = (0, import_node_fs2.readFileSync)(skillFile, "utf-8");
1202
+ if (!(0, import_node_fs3.existsSync)(skillFile)) continue;
1203
+ const content = (0, import_node_fs3.readFileSync)(skillFile, "utf-8");
1149
1204
  const frontmatter = parseFrontmatter(content);
1150
1205
  commands.push(buildCommand(frontmatter, content, entry.name));
1151
1206
  }
1152
1207
  return commands;
1153
1208
  }
1154
1209
  function scanCommandsDir(commandsDir) {
1155
- if (!(0, import_node_fs2.existsSync)(commandsDir)) return [];
1210
+ if (!(0, import_node_fs3.existsSync)(commandsDir)) return [];
1156
1211
  const commands = [];
1157
- const entries = (0, import_node_fs2.readdirSync)(commandsDir, { withFileTypes: true });
1212
+ const entries = (0, import_node_fs3.readdirSync)(commandsDir, { withFileTypes: true });
1158
1213
  for (const entry of entries) {
1159
1214
  if (!entry.isFile() || !entry.name.endsWith(".md")) continue;
1160
1215
  const filePath = (0, import_node_path2.join)(commandsDir, entry.name);
1161
- const content = (0, import_node_fs2.readFileSync)(filePath, "utf-8");
1216
+ const content = (0, import_node_fs3.readFileSync)(filePath, "utf-8");
1162
1217
  const frontmatter = parseFrontmatter(content);
1163
1218
  const fallbackName = (0, import_node_path2.basename)(entry.name, ".md");
1164
1219
  commands.push(buildCommand(frontmatter, content, fallbackName));
@@ -1407,23 +1462,41 @@ function renderMarkdown(md) {
1407
1462
  // src/ui/DiffBlock.tsx
1408
1463
  var import_ink = require("ink");
1409
1464
  var import_jsx_runtime = require("react/jsx-runtime");
1410
- var MAX_DIFF_LINES = 10;
1411
- var TRUNCATED_SHOW = 8;
1465
+ var MAX_DIFF_LINES = 12;
1466
+ var TRUNCATED_SHOW = 10;
1412
1467
  function DiffBlock({ file, lines }) {
1413
1468
  const truncated = lines.length > MAX_DIFF_LINES;
1414
1469
  const visible = truncated ? lines.slice(0, TRUNCATED_SHOW) : lines;
1415
1470
  const remaining = lines.length - TRUNCATED_SHOW;
1471
+ const maxLineNum = Math.max(...visible.map((l) => l.lineNumber), 0);
1472
+ const numWidth = String(maxLineNum).length;
1416
1473
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_ink.Box, { flexDirection: "column", marginLeft: 4, children: [
1417
1474
  file && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_ink.Text, { color: "white", dimColor: true, children: [
1418
1475
  "\u2502 ",
1419
1476
  file
1420
1477
  ] }),
1421
- visible.map((line, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_ink.Text, { color: line.type === "remove" ? "red" : "greenBright", children: [
1422
- "\u2502 ",
1423
- line.type === "remove" ? "-" : "+",
1424
- " ",
1425
- line.text
1426
- ] }, i)),
1478
+ visible.map((line, i) => {
1479
+ const lineNum = String(line.lineNumber).padStart(numWidth, " ");
1480
+ if (line.type === "context") {
1481
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_ink.Text, { color: "white", dimColor: true, children: [
1482
+ "\u2502 ",
1483
+ lineNum,
1484
+ " ",
1485
+ line.text
1486
+ ] }, i);
1487
+ }
1488
+ const prefix = line.type === "remove" ? "-" : "+";
1489
+ const bgColor = line.type === "remove" ? "#5c1a1a" : "#1a3d1a";
1490
+ const fgColor = line.type === "remove" ? "#ff9999" : "#99ff99";
1491
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_ink.Text, { color: fgColor, backgroundColor: bgColor, children: [
1492
+ "\u2502 ",
1493
+ lineNum,
1494
+ " ",
1495
+ prefix,
1496
+ " ",
1497
+ line.text
1498
+ ] }, i);
1499
+ }),
1427
1500
  truncated && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_ink.Text, { color: "white", dimColor: true, children: [
1428
1501
  "\u2502 ... and ",
1429
1502
  remaining,
@@ -2244,9 +2317,9 @@ function renderApp(options) {
2244
2317
  // src/cli.ts
2245
2318
  var import_meta = {};
2246
2319
  function checkSettingsFile(filePath) {
2247
- if (!(0, import_node_fs3.existsSync)(filePath)) return "missing";
2320
+ if (!(0, import_node_fs4.existsSync)(filePath)) return "missing";
2248
2321
  try {
2249
- const raw = (0, import_node_fs3.readFileSync)(filePath, "utf8").trim();
2322
+ const raw = (0, import_node_fs4.readFileSync)(filePath, "utf8").trim();
2250
2323
  if (raw.length === 0) return "incomplete";
2251
2324
  const parsed = JSON.parse(raw);
2252
2325
  const provider = parsed.provider;
@@ -2263,7 +2336,7 @@ function readVersion() {
2263
2336
  const candidates = [(0, import_node_path5.join)(dir, "..", "..", "package.json"), (0, import_node_path5.join)(dir, "..", "package.json")];
2264
2337
  for (const pkgPath of candidates) {
2265
2338
  try {
2266
- const raw = (0, import_node_fs3.readFileSync)(pkgPath, "utf-8");
2339
+ const raw = (0, import_node_fs4.readFileSync)(pkgPath, "utf-8");
2267
2340
  const pkg = JSON.parse(raw);
2268
2341
  if (pkg.version !== void 0 && pkg.name !== void 0) {
2269
2342
  return pkg.version;
@@ -2351,7 +2424,7 @@ async function ensureConfig(cwd) {
2351
2424
  }
2352
2425
  const language = await promptInput(" Response language (ko/en/ja/zh, default: en): ");
2353
2426
  const settingsDir = (0, import_node_path5.dirname)(userPath);
2354
- (0, import_node_fs3.mkdirSync)(settingsDir, { recursive: true });
2427
+ (0, import_node_fs4.mkdirSync)(settingsDir, { recursive: true });
2355
2428
  const settings = {
2356
2429
  provider: {
2357
2430
  name: "anthropic",
@@ -2362,7 +2435,7 @@ async function ensureConfig(cwd) {
2362
2435
  if (language) {
2363
2436
  settings.language = language;
2364
2437
  }
2365
- (0, import_node_fs3.writeFileSync)(userPath, JSON.stringify(settings, null, 2) + "\n", "utf8");
2438
+ (0, import_node_fs4.writeFileSync)(userPath, JSON.stringify(settings, null, 2) + "\n", "utf8");
2366
2439
  process.stdout.write(`
2367
2440
  Config saved to ${userPath}
2368
2441
 
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  startCli
3
- } from "./chunk-EPCRZIQ6.js";
3
+ } from "./chunk-RDPIMQOC.js";
4
4
 
5
5
  // src/index.ts
6
6
  import { Session, SessionStore, query, TRUST_TO_MODE } from "@robota-sdk/agent-sdk";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@robota-sdk/agent-cli",
3
- "version": "3.0.0-beta.33",
3
+ "version": "3.0.0-beta.34",
4
4
  "description": "AI coding assistant CLI built on Robota SDK",
5
5
  "type": "module",
6
6
  "bin": {