130 lines
4.0 KiB
TypeScript
Executable File
130 lines
4.0 KiB
TypeScript
Executable File
#!/usr/bin/env runts -- --allow-all
|
|
|
|
import {parseArgs} from "jsr:@std/cli/parse-args";
|
|
import {execCommandShell, exit, log, ProcessBar, term,} from "https://script.hatter.ink/@67/deno-commons-mod.ts";
|
|
import {howto} from "https://script.hatter.ink/@3/deno-ai-mod.ts";
|
|
|
|
async function main() {
|
|
const flags = parseArgs(Deno.args, {
|
|
boolean: ["help", "no-exec"],
|
|
alias: {
|
|
h: "help",
|
|
N: "no-exec",
|
|
},
|
|
});
|
|
|
|
if (flags.help) {
|
|
console.log(`howto.ts - 🚀 AI-powered Natural Language to CLI conversion
|
|
|
|
e.g.
|
|
howto.ts get disk usage for current dir
|
|
|
|
howto.ts --help - show help
|
|
howto.ts [-N|--no-exec] 'MESSAGE' - generate command line for MESSAGE
|
|
`);
|
|
exit(0);
|
|
}
|
|
|
|
const noExec = flags["no-exec"];
|
|
const message = (flags._)
|
|
? flags._.join(" ")
|
|
: prompt("Input your message: ");
|
|
if (!message) {
|
|
throw new Error("Message is required");
|
|
}
|
|
const summary = await new ProcessBar("AI thinking").call(
|
|
async (): Promise<string> => {
|
|
return await howto(message);
|
|
},
|
|
);
|
|
log.success(`AI howto command line message: \n${summary}`);
|
|
|
|
if (!noExec) {
|
|
try {
|
|
const commandLines = extractCommand(summary);
|
|
if (commandLines.length == 1) {
|
|
log.success(
|
|
"Found command line: ",
|
|
term.auto(`[green][[[${commandLines}]]][/]`),
|
|
);
|
|
} else {
|
|
log.success(`Found ${commandLines.length} commands`);
|
|
}
|
|
|
|
if (commandLines.length == 1) {
|
|
if (confirm("Execute this command?")) {
|
|
exit(
|
|
await execCommandShell("sh", [
|
|
"-c",
|
|
commandLines[0],
|
|
]),
|
|
);
|
|
}
|
|
} else {
|
|
const colors = ["red", "green", "blue"];
|
|
for (const [i, command] of commandLines.entries()) {
|
|
console.log(term.auto(
|
|
`[${colors[i % 3]}][[[# ${i}: ${command}]]][/]`,
|
|
));
|
|
}
|
|
const selectCommandLineIndex = parseInt(
|
|
prompt(
|
|
"Please select the command line: ",
|
|
),
|
|
10,
|
|
);
|
|
if (
|
|
isNaN(selectCommandLineIndex) ||
|
|
selectCommandLineIndex < 0 ||
|
|
selectCommandLineIndex >= commandLines.length
|
|
) {
|
|
log.error(
|
|
`Bad command line index: ${selectCommandLineIndex}`,
|
|
);
|
|
} else {
|
|
exit(
|
|
await execCommandShell("sh", [
|
|
"-c",
|
|
commandLines[0],
|
|
]),
|
|
);
|
|
}
|
|
}
|
|
} catch (e) {
|
|
log.error("Extract command line failed", e);
|
|
}
|
|
}
|
|
}
|
|
|
|
function extractCommand(summary: string): string[] {
|
|
const lines = summary.split(/\r?\n/);
|
|
const codeBlocks: string[] = [];
|
|
let codeBlock: string[] = [];
|
|
let inCode = false;
|
|
for (const line of lines) {
|
|
if (inCode) {
|
|
if (line.startsWith("```")) {
|
|
codeBlocks.push(codeBlock.join("\n"));
|
|
codeBlock = [];
|
|
inCode = false;
|
|
} else {
|
|
codeBlock.push(line);
|
|
}
|
|
} else if (line.startsWith("```")) {
|
|
inCode = true;
|
|
}
|
|
}
|
|
if (codeBlocks.length > 0) {
|
|
return codeBlocks;
|
|
}
|
|
throw new Error("Command line not found");
|
|
}
|
|
|
|
main().catch((err) => {
|
|
log.error(err);
|
|
process.exit(0);
|
|
}).then(() => process.exit(0));
|
|
|
|
// @SCRIPT-SIGNATURE-V1: yk-r1.ES256.20260415T232646+08:00.MEUCIEw1GSf+P9+9GnxdehpK
|
|
// 1jUaC3A4NI73i5fK+CHlxauGAiEAgo47/bat/+T2xPc2OCiy38yo157pxSNBjUoRp2pBvKM=
|