Last active
September 18, 2025 11:43
-
-
Save codehz/110df57bf1ad2f1085fe8ec87e56e3b8 to your computer and use it in GitHub Desktop.
clink ai command using ch.at
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| local PROMPT = [[ | |
| 你是一个智能助手,擅长用简洁但准确的中文回答问题。请按照以下格式回答用户的问题: | |
| 1. 确保语言通俗易懂,避免复杂术语。 | |
| 2. 只给答案:不要提出反问或引导性的问题。答案后方附上详细文字说明(500字内)。 | |
| 3. 单轮次:你的回答将作为最终答案,不要引导用户继续提问。 | |
| 4. 尽可能使用Markdown格式(支持链接,粗体(请使用`**`),斜体(请使用`__`),删除线,代码块,引用),不支持表格,请使用其他替代格式,但不包含任何HTML标签。 | |
| 用户问题: | |
| ]] | |
| local function url_encode(str) | |
| if not str then | |
| return "" | |
| end | |
| str = string.gsub(str, "\n", "\r\n") | |
| str = string.gsub(str, "([^%w %-%_%.%~])", function(c) | |
| return string.format("%%%02X", string.byte(c)) | |
| end) | |
| str = string.gsub(str, " ", "+") | |
| return str | |
| end | |
| local function get_header_style(level) | |
| local styles = {"\27[1;4;33m", -- H1: bold, underline, yellow | |
| "\27[1;33m", -- H2: bold, yellow | |
| "\27[1;32m", -- H3: bold, green | |
| "\27[1;34m", -- H4: bold, blue | |
| "\27[1;35m", -- H5: bold, magenta | |
| "\27[1;36m" -- H6: bold, cyan | |
| } | |
| return styles[level] or "\27[1m" -- Default to bold for higher levels | |
| end | |
| local function parse_inline(text) | |
| -- Parse links: [text](url) | |
| text = text:gsub("%[([^%]]+)%]%(([^%)]+)%)", "\27]8;;%2\27\\%1\27]8;;\27\\") | |
| -- Parse strikethrough: ~~text~~ | |
| text = text:gsub("~~([^~]+)~~", "\27[9m%1\27[0m") | |
| -- Parse bold: **text** or __text__ | |
| text = text:gsub("%*%*([^%*]+)%*%*", "\27[1m%1\27[0m") | |
| text = text:gsub("([%a%d]?)__([^_]+)__([%a%d]?)", function(prev, content, next) | |
| if prev == "" and next == "" then | |
| return "\27[3m" .. content .. "\27[0m" | |
| else | |
| return prev .. "__" .. content .. "__" .. next | |
| end | |
| end) | |
| -- Parse inline code: `code` | |
| text = text:gsub("`([^`]+)`", "\27[31m%1\27[0m") | |
| return text | |
| end | |
| local function process_line(line, state) | |
| state = state or { | |
| in_code_block = false | |
| } | |
| local new_state = { | |
| in_code_block = state.in_code_block | |
| } | |
| local out = "" | |
| if state.in_code_block then | |
| -- Check for code block end | |
| if line:match("^%s*```") then | |
| new_state.in_code_block = false | |
| -- Output the delimiter with code style for consistency | |
| out = "\27[36m" .. line .. "\27[0m" | |
| else | |
| -- Apply code block style (cyan foreground), no inline parsing | |
| out = "\27[36m" .. line .. "\27[0m" | |
| end | |
| else | |
| -- Check for code block start | |
| local code_start = line:match("^%s*```") | |
| if code_start then | |
| new_state.in_code_block = true | |
| -- Output the delimiter with code style | |
| out = "\27[36m" .. line .. "\27[0m" | |
| return out, new_state | |
| end | |
| -- Handle quote blocks (per line) | |
| local is_quote = false | |
| local quote_prefix = line:match("^(%s*>%s*)") | |
| if quote_prefix then | |
| is_quote = true | |
| line = line:sub(#quote_prefix + 1) -- Strip quote prefix | |
| end | |
| -- Handle headers (single line) | |
| local header_level = 0 | |
| local header_match = line:match("^(%#+)%s+") | |
| if header_match then | |
| header_level = #header_match | |
| line = line:gsub("^%#+%s+", "", 1) -- Strip header prefix | |
| end | |
| -- Parse inline elements | |
| local parsed = parse_inline(line) | |
| -- Apply block-level styles | |
| if header_level > 0 then | |
| local style = get_header_style(header_level) | |
| out = style .. parsed .. "\27[0m" | |
| elseif is_quote then | |
| out = "\27[90m" .. parsed .. "\27[0m" | |
| else | |
| out = parsed | |
| end | |
| end | |
| return out, new_state | |
| end | |
| local function ai_request(question) | |
| local full_input = PROMPT .. question | |
| local encoded_input = url_encode(full_input) | |
| local url = "ch.at/?model=claude-4-sonnet&q=" .. encoded_input | |
| local curl_cmd = 'curl -s -N "' .. url .. '"' | |
| local file = io.popen(curl_cmd, "r") | |
| local state = nil | |
| local function print_line(line) | |
| local out, new_state = process_line(line, state) | |
| if out ~= "" then | |
| print(out) | |
| end | |
| state = new_state | |
| end | |
| while true do | |
| local line = file:read("*line") | |
| if not line then | |
| break | |
| end | |
| print_line(line) | |
| end | |
| file:close() | |
| return "", false | |
| end | |
| clink.onfilterinput(function(input) | |
| local content = input:match("^ *ai *(.+)") | |
| if content then | |
| return ai_request(content) | |
| end | |
| end) | |
| clink.argmatcher("ai"):nofiles():addarg():loop(1) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment