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.
- package/dist/index.js +143 -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
|
-
|
|
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
|
{
|