toonise 0.1.2 → 0.1.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.
Files changed (2) hide show
  1. package/dist/index.js +143 -2
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -177,14 +177,54 @@ var init_ToolRegistry = __esm({
177
177
  ];
178
178
  ToolExecutor = class {
179
179
  cwd;
180
- constructor(cwd = process.cwd()) {
180
+ confirmCallback;
181
+ constructor(cwd = process.cwd(), confirmCallback) {
181
182
  this.cwd = cwd;
183
+ if (confirmCallback) {
184
+ this.confirmCallback = confirmCallback;
185
+ }
182
186
  }
183
187
  setCwd(cwd) {
184
188
  this.cwd = cwd;
185
189
  }
190
+ setConfirmCallback(callback) {
191
+ if (callback) {
192
+ this.confirmCallback = callback;
193
+ }
194
+ }
195
+ requiresConfirmation(toolName, args) {
196
+ if (toolName !== "bash") return false;
197
+ const command = (args.command || "").toLowerCase();
198
+ const privilegedPatterns = [
199
+ /^\s*sudo\s+/,
200
+ /^\s*su\s+/,
201
+ /\bsudo\s+/,
202
+ /\bsu\s+-c/,
203
+ /pkexec/,
204
+ /doas/,
205
+ /runas/,
206
+ /netsh\s+advfirewall/,
207
+ /reg\s+(add|delete|import)/i,
208
+ /sc\s+(create|config|delete)/i,
209
+ /wevtutil.*clear-log/i,
210
+ /bcdedit/i,
211
+ /diskpart/i,
212
+ /fsutil/i,
213
+ /dism/i,
214
+ /sfc\s+\/scannow/i
215
+ ];
216
+ return privilegedPatterns.some((pattern) => pattern.test(command));
217
+ }
186
218
  async execute(toolName, args) {
187
219
  try {
220
+ if (this.requiresConfirmation(toolName, args)) {
221
+ if (this.confirmCallback) {
222
+ const confirmed = await this.confirmCallback(toolName, args);
223
+ if (!confirmed) {
224
+ return { tool: toolName, success: false, error: "Operation cancelled by user" };
225
+ }
226
+ }
227
+ }
188
228
  switch (toolName) {
189
229
  case "read":
190
230
  return await this.read(args.filePath, args.offset, args.limit);
@@ -869,8 +909,69 @@ var COMMANDS = [
869
909
  { name: "/new", desc: "New session", key: "n" },
870
910
  { name: "/model", desc: "Switch model", key: "m" },
871
911
  { name: "/host", desc: "Change host", key: "h" },
912
+ { name: "/help", desc: "Show help manual", key: "?" },
872
913
  { name: "/exit", desc: "Exit", key: "q" }
873
914
  ];
915
+ var HELP_TEXT = `
916
+ TOONISE(1) User Manual TOONISE(1)
917
+
918
+ NAME
919
+ toonise - A high-performance CLI developer assistant with TUI
920
+
921
+ SYNOPSIS
922
+ toonise [options]
923
+
924
+ DESCRIPTION
925
+ Toonise is an AI-powered terminal interface for developers. It connects
926
+ to local Ollama models to provide intelligent assistance with coding,
927
+ file operations, and development tasks.
928
+
929
+ COMMANDS
930
+ /agent [name] Switch between specialized agents
931
+ /model [name] Switch LLM model
932
+ /host [url] Change Ollama host URL
933
+ /clear Clear chat history
934
+ /new Start a new session
935
+ /compact Compact conversation to save tokens
936
+ /abort Stop current AI processing
937
+ /help Show this help manual
938
+ /exit Exit application
939
+
940
+ KEYBOARD SHORTCUTS
941
+ Ctrl+B Toggle sidebar visibility
942
+ \u2191 / \u2193 Navigate command history
943
+ @ Trigger file suggestions
944
+ / Show command list
945
+ Tab Complete file paths
946
+
947
+ AGENTS
948
+ build Coding assistant for writing and editing files
949
+ explore Code analysis and exploration
950
+
951
+ EXAMPLES
952
+ /agent build Switch to build agent
953
+ /model llama3.2 Use llama3.2 model
954
+ /host http://remote:11434 Connect to remote Ollama
955
+
956
+ FILES
957
+ ~/.config/toonise/config.json Configuration file
958
+ ~/.config/toonise/history.json Chat history
959
+
960
+ ENVIRONMENT
961
+ OLLAMA_HOST Default Ollama host URL
962
+
963
+ SEE ALSO
964
+ ollama(1)
965
+
966
+ VERSION
967
+ 0.1.2
968
+
969
+ AUTHORS
970
+ Written by the Toonise team.
971
+
972
+ LICENSE
973
+ MIT License
974
+ `;
874
975
  var SIDEBAR_WIDTH = 28;
875
976
  var progressBar = (percent, width) => {
876
977
  const filled = Math.round(percent / 100 * width);
@@ -946,8 +1047,13 @@ var App = () => {
946
1047
  const [showSidebar, setShowSidebar] = useState(true);
947
1048
  const [toolPreviews, setToolPreviews] = useState([]);
948
1049
  const [modelScrollOffset, setModelScrollOffset] = useState(0);
1050
+ const [pendingConfirmation, setPendingConfirmation] = useState(null);
949
1051
  const abortRequested = React.useRef(false);
950
- const toolExecutor = useMemo(() => new ToolExecutor(), []);
1052
+ const toolExecutor = useMemo(() => new ToolExecutor(process.cwd(), async (tool, args) => {
1053
+ return new Promise((resolve) => {
1054
+ setPendingConfirmation({ tool, args, resolve });
1055
+ });
1056
+ }), []);
951
1057
  const ollama = useMemo(() => new OllamaService(config.host), [config.host]);
952
1058
  const agent = useMemo(() => AGENTS[agentKey] || AGENTS["build"] || { name: "Unknown", color: "white", systemPrompt: "" }, [agentKey]);
953
1059
  const sidebarVisible = showSidebar && terminalWidth > 80;
@@ -1014,6 +1120,7 @@ var App = () => {
1014
1120
  });
1015
1121
  }, [model, ollama, addInsight]);
1016
1122
  useInput((inputStr, key) => {
1123
+ if (pendingConfirmation) return;
1017
1124
  if (!showCommandList && !showModelSelector && !showFileSuggestions && key.ctrl && inputStr === "b") {
1018
1125
  setShowSidebar((p) => !p);
1019
1126
  return;
@@ -1028,6 +1135,7 @@ var App = () => {
1028
1135
  } else if (key.escape) setShowCommandList(false);
1029
1136
  });
1030
1137
  useInput((input2, key) => {
1138
+ if (pendingConfirmation) return;
1031
1139
  if (!showModelSelector || availableModels.length === 0) return;
1032
1140
  const visibleModels = 8;
1033
1141
  if (key.upArrow) {
@@ -1057,6 +1165,7 @@ var App = () => {
1057
1165
  }
1058
1166
  });
1059
1167
  useInput((char) => {
1168
+ if (pendingConfirmation) return;
1060
1169
  if (showModelSelector) return;
1061
1170
  if (char === "@" && !showFileSuggestions && !showCommandList) {
1062
1171
  setInput((p) => p + "@");
@@ -1068,6 +1177,16 @@ var App = () => {
1068
1177
  }
1069
1178
  });
1070
1179
  useInput((input2, key) => {
1180
+ if (pendingConfirmation) {
1181
+ if (key.return || input2 === "y" || input2 === "Y") {
1182
+ pendingConfirmation.resolve(true);
1183
+ setPendingConfirmation(null);
1184
+ } else if (key.escape || input2 === "n" || input2 === "N") {
1185
+ pendingConfirmation.resolve(false);
1186
+ setPendingConfirmation(null);
1187
+ }
1188
+ return;
1189
+ }
1071
1190
  if (!showFileSuggestions) return;
1072
1191
  if (key.upArrow) setSelectedFileIndex((p) => p > 0 ? p - 1 : fileSuggestions.length - 1);
1073
1192
  else if (key.downArrow) setSelectedFileIndex((p) => p < fileSuggestions.length - 1 ? p + 1 : 0);
@@ -1334,6 +1453,9 @@ ${dirContext}` };
1334
1453
  addInsight("success", `${before}\u2192${after} msgs`);
1335
1454
  }).catch((e) => setMessages((p) => [...p, { role: "system", content: `\u2717 Failed: ${e.message}` }]));
1336
1455
  break;
1456
+ case "/help":
1457
+ setMessages((p) => [...p, { role: "system", content: HELP_TEXT }]);
1458
+ break;
1337
1459
  default:
1338
1460
  setMessages((p) => [...p, { role: "system", content: `Unknown: ${name}` }]);
1339
1461
  }
@@ -1570,6 +1692,25 @@ ${dirContext}` };
1570
1692
  ] }) })
1571
1693
  ] }),
1572
1694
  /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginTop: 1, children: [
1695
+ pendingConfirmation && /* @__PURE__ */ jsxs(
1696
+ Box,
1697
+ {
1698
+ flexDirection: "column",
1699
+ borderStyle: "round",
1700
+ borderColor: THEME.warning,
1701
+ paddingX: 1,
1702
+ marginBottom: 1,
1703
+ children: [
1704
+ /* @__PURE__ */ jsx(Text, { bold: true, color: THEME.warning, children: "\u26A0 PRIVILEGED OPERATION" }),
1705
+ /* @__PURE__ */ jsxs(Text, { color: THEME.text, children: [
1706
+ "Tool: ",
1707
+ pendingConfirmation.tool
1708
+ ] }),
1709
+ /* @__PURE__ */ jsx(Text, { color: THEME.textMuted, children: JSON.stringify(pendingConfirmation.args, null, 2) }),
1710
+ /* @__PURE__ */ jsx(Box, { marginTop: 1, children: /* @__PURE__ */ jsx(Text, { color: THEME.accent, children: "Confirm? [Y/n]" }) })
1711
+ ]
1712
+ }
1713
+ ),
1573
1714
  /* @__PURE__ */ jsxs(
1574
1715
  Box,
1575
1716
  {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "toonise",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "description": "A high-performance CLI developer assistant with modern TUI, powered by local LLMs via Ollama",
5
5
  "main": "dist/index.js",
6
6
  "bin": {