arch + features: jarvis.text API + brightness/window_switch/spelling/network/summarize (41 packs)

Architecture — close the trigger-strip duplication:

  lua/api/text.rs (new):
  + jarvis.text.strip_trigger(phrase, triggers) — case-insensitive
    "longest trigger wins" stripping, returns trimmed remainder. The
    11 packs that previously inlined a `for _, t in ipairs(triggers) do
    string.find ... s == 1 then sub` loop can drop to one call.
    Demonstrated in spelling/, window_switch/, summarize/.
  + jarvis.text.contains_any(phrase, needles) — boolean check for any
    of N substrings, case-insensitive. Useful for keyword routing.

5 new portable Lua packs (sandbox=full). 36 → 41 total. cargo test
commands::tests still 3/3.

brightness/ — brightness_up / down / max / min. WMI WmiMonitorBrightness
(read CurrentBrightness) + WmiMonitorBrightnessMethods.WmiSetBrightness
(write). ±20% step for up/down; max=100, min=10. Desktop monitors that
don'\''t expose the WMI namespace get a friendly "не поддерживается"
toast instead of a silent failure.

window_switch/ — switch_to_window. Strip trigger ("переключись на" /
"switch to" / etc.), apply the same alias map as process_kill
(хром→chrome, телега→telegram, ...), then PowerShell finds processes
by name OR window-title substring sorted by StartTime desc and uses
(New-Object -ComObject WScript.Shell).AppActivate($pid) on the top
match. Speaks the window title that got focus.

spelling/ — spell_out. Strips "произнеси по буквам" / "spell out" /
etc., iterates `word:gmatch(".")`, joins letters with ". " so SAPI
gives each one a natural micro-pause. Uses jarvis.text.strip_trigger
+ jarvis.speak — 24 lines total.

network/ — open_wifi_settings (ms-settings:network-wifi) +
open_bluetooth_settings (ms-settings:bluetooth) + my_ip. my_ip pulls
the first non-loopback, non-APIPA IPv4 with Get-NetIPAddress and
fetches WAN IP from api.ipify.org (5s timeout); speaks "Локальный X.
Внешний Y".

summarize/ — summarize_selection. Synthesises Ctrl+C through user32!
keybd_event (so the focused window copies its selection), waits
220 ms for the clipboard to populate, reads via
jarvis.system.clipboard.get(), bails if < 20 chars, truncates at
8000, sends to Groq @ temp 0.3 with a "3-5 sentences, keep names"
prompt, drops the summary back into the clipboard and speaks it.
Lets you select ANY text in ANY app (browser, PDF, IDE) and say
"суммируй" to get a spoken TL;DR.

The new packs explicitly use the new jarvis.text + jarvis.llm +
jarvis.speak API surface — no inline PS-SAPI boilerplate, no inline
Groq plumbing. Code is now intent-first.
This commit is contained in:
Bossiara13 2026-05-15 13:11:18 +03:00
parent a16d2401e7
commit a7c002c9d4
14 changed files with 432 additions and 1 deletions

View file

@ -0,0 +1,25 @@
[[commands]]
id = "summarize_selection"
type = "lua"
script = "summarize.lua"
sandbox = "full"
timeout = 25000
[commands.phrases]
ru = [
"суммируй",
"перескажи выделенное",
"кратко перескажи",
"о чём это",
"что выделил",
"перескажи это",
]
en = [
"summarise this",
"summarize selection",
"tldr",
]
ua = [
"перекажи це",
"стисло перекажи",
]

View file

@ -0,0 +1,43 @@
local lang = jarvis.context.language
local copy_ps = [[Add-Type -Name K -Namespace W -MemberDefinition '[DllImport("user32.dll")] public static extern void keybd_event(byte vk, byte sc, uint flags, System.UIntPtr extra);'; [W.K]::keybd_event(0x11,0,0,[UIntPtr]::Zero); [W.K]::keybd_event(0x43,0,0,[UIntPtr]::Zero); Start-Sleep -Milliseconds 40; [W.K]::keybd_event(0x43,0,2,[UIntPtr]::Zero); [W.K]::keybd_event(0x11,0,2,[UIntPtr]::Zero); Start-Sleep -Milliseconds 220]]
jarvis.system.exec(string.format('powershell -NoProfile -Command "%s"', copy_ps:gsub('"', '\\"')))
local raw = jarvis.system.clipboard.get() or ""
local text = raw:gsub("^%s+", ""):gsub("%s+$", "")
if #text < 20 then
jarvis.system.notify("Summarize",
lang == "ru" and "Сначала выдели текст и повтори" or "Select text first, then retry")
jarvis.audio.play_not_found()
return { chain = false }
end
if #text > 8000 then
text = text:sub(1, 8000) .. " ..."
end
local sys = lang == "ru"
and "Перескажи следующий текст одним абзацем (3-5 предложений). Сохрани ключевые факты и имена, без 'вот суммарно' и других вступлений."
or "Summarise the following text in 3-5 sentences. Keep key facts and names, no preamble."
local reply = jarvis.llm(
{ { role = "system", content = sys },
{ role = "user", content = text } },
{ max_tokens = 400, temperature = 0.3 }
)
if not reply or reply == "" then
jarvis.system.notify("Summarize", lang == "ru" and "LLM не ответила" or "LLM failed")
jarvis.audio.play_error()
return { chain = false }
end
jarvis.system.clipboard.set(reply)
jarvis.system.notify(
lang == "ru" and "Кратко" or "TL;DR",
reply:sub(1, 280)
)
jarvis.speak(reply, { lang = lang })
jarvis.audio.play_ok()
return { chain = false }