@unikode/cli 1.0.9 → 1.0.10

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 (2) hide show
  1. package/dist/index.js +40 -33
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1010,10 +1010,10 @@ var toolInputSchemas = {
1010
1010
  oldString: z.string().describe("Exact text to replace; must be unique"),
1011
1011
  newString: z.string().describe("Replacement text")
1012
1012
  }),
1013
- bash: z.object({
1014
- command: z.string().describe("Shell command to run"),
1015
- description: z.string().optional().describe("Short description of the command"),
1016
- timeout: z.number().optional().describe("Timeout in milliseconds")
1013
+ terminalTool: z.object({
1014
+ command: z.string().describe("The terminal command to execute (using Windows PowerShell or CMD syntax)."),
1015
+ cwd: z.string().optional().describe("The working directory for execution. Defaults to the current project root."),
1016
+ explanation: z.string().describe("A 1-sentence explanation of why this command is being run, for logging and user transparency.")
1017
1017
  }),
1018
1018
  webSearchTool: z.object({
1019
1019
  query: z.string().describe("The search query. Keep it short and specific (3-8 words works best)."),
@@ -1133,9 +1133,9 @@ var buildToolContracts = {
1133
1133
  description: "Replace exact text in a file under the current project directory.",
1134
1134
  inputSchema: toolInputSchemas.editFile
1135
1135
  }),
1136
- bash: tool({
1137
- description: "Run a shell command in the current project directory.",
1138
- inputSchema: toolInputSchemas.bash
1136
+ terminalTool: tool({
1137
+ description: "Execute a terminal command on the local Windows system. Use this to run CLI tools, navigate the filesystem, manage packages, or execute scripts. Do NOT use interactive commands that require user input (e.g., 'npm init' or interactive git rebases).",
1138
+ inputSchema: toolInputSchemas.terminalTool
1139
1139
  }),
1140
1140
  undoLastEditSchema: tool({
1141
1141
  description: "Reverts the specified file to its exact state before the most recent modification. Use this immediately if your last edit introduced syntax errors, broke tests, or failed to solve the issue.",
@@ -3410,11 +3410,14 @@ import fs from "fs/promises";
3410
3410
  import path from "path";
3411
3411
  import { createReadStream } from "fs";
3412
3412
  import readline from "readline";
3413
+ import { exec } from "child_process";
3414
+ import { promisify } from "util";
3415
+ var execAsync = promisify(exec);
3416
+ var DEFAULT_TIMEOUT_MS = 120000;
3417
+ var MAX_BUFFER_SIZE = 1024 * 1024 * 5;
3413
3418
  var MAX_FILE_SIZE = 1e4;
3414
3419
  var MAX_RESULTS = 200;
3415
3420
  var MAX_MATCHES = 50;
3416
- var MAX_OUTPUT = 20000;
3417
- var DEFAULT_TIMEOUT = 30000;
3418
3421
  var MAX_CONTENT_LENGTH = 15000;
3419
3422
  var FETCH_TIMEOUT_MS = 1e4;
3420
3423
  var turndownService = new TurndownService({
@@ -3436,10 +3439,6 @@ function recordEdit(resolvedPath, oldString, newString) {
3436
3439
  stack.push({ oldString, newString });
3437
3440
  editHistory.set(resolvedPath, stack);
3438
3441
  }
3439
- function truncate(value, limit) {
3440
- return value.length > limit ? `${value.slice(0, limit)}
3441
- ... (truncated, ${value.length} total chars)` : value;
3442
- }
3443
3442
  async function countLines(filePath) {
3444
3443
  return new Promise((resolve3, reject) => {
3445
3444
  let lines = 0;
@@ -3571,26 +3570,34 @@ async function executeLocalTool(toolName, input, mode) {
3571
3570
  recordEdit(resolved, oldString, newString);
3572
3571
  return { success: true, path: relative2(cwd, resolved) };
3573
3572
  }
3574
- case "bash": {
3575
- const { command, timeout = DEFAULT_TIMEOUT } = toolInputSchemas.bash.parse(input);
3576
- const proc = Bun.spawn(["bash", "-c", command], {
3577
- cwd: resolveInsideCwd(".").resolved,
3578
- stdout: "pipe",
3579
- stderr: "pipe",
3580
- env: { ...process.env, TERM: "dumb" }
3581
- });
3582
- const timer = setTimeout(() => proc.kill(), timeout);
3583
- const [stdout, stderr] = await Promise.all([
3584
- new Response(proc.stdout).text(),
3585
- new Response(proc.stderr).text()
3586
- ]);
3587
- const exitCode = await proc.exited;
3588
- clearTimeout(timer);
3589
- return {
3590
- stdout: truncate(stdout, MAX_OUTPUT),
3591
- stderr: truncate(stderr, MAX_OUTPUT),
3592
- exitCode
3593
- };
3573
+ case "terminalTool": {
3574
+ const { command, cwd, explanation } = toolInputSchemas.terminalTool.parse(input);
3575
+ const targetDir = cwd ? path.resolve(process.cwd(), cwd) : process.cwd();
3576
+ try {
3577
+ const { stdout, stderr } = await execAsync(command, {
3578
+ cwd: targetDir,
3579
+ timeout: DEFAULT_TIMEOUT_MS,
3580
+ maxBuffer: MAX_BUFFER_SIZE,
3581
+ windowsHide: true
3582
+ });
3583
+ return {
3584
+ success: true,
3585
+ cwd: targetDir,
3586
+ explanation,
3587
+ output: stdout.trim() || "Command executed successfully with no output.",
3588
+ error: stderr.trim() || null
3589
+ };
3590
+ } catch (error) {
3591
+ const isTimeout = error.killed && error.signal === "SIGTERM";
3592
+ return {
3593
+ success: false,
3594
+ cwd: targetDir,
3595
+ explanation,
3596
+ exitCode: error.code ?? -1,
3597
+ output: error.stdout?.toString().trim() || null,
3598
+ error: isTimeout ? `Command timed out after ${DEFAULT_TIMEOUT_MS / 1000} seconds. Did you run an interactive command?` : error.stderr?.toString().trim() || error.message || "Unknown execution error"
3599
+ };
3600
+ }
3594
3601
  }
3595
3602
  case "webSearchTool": {
3596
3603
  const { query, searchDepth, maxResults, includeAnswer } = toolInputSchemas.webSearchTool.parse(input);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@unikode/cli",
3
- "version": "1.0.9",
3
+ "version": "1.0.10",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "bin": {