@pithosai/pithosai 1.0.10 → 1.0.33

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.
@@ -0,0 +1,456 @@
1
+ # pithosai-nl-fallback.sh
2
+ # Source from zsh or bash 4+ (do not execute):
3
+ # source /path/to/scripts/pithosai-nl-fallback.sh
4
+ #
5
+ # Unknown commands that look like natural language are classified by Node
6
+ # (`nl-fallback-classify`: lookpath + tinyld) and forwarded to:
7
+ # pithosai --no-interactive --model krouter-rd "$input"
8
+ #
9
+ # The command_not_found path never rewrites the typed line. Interactive zsh
10
+ # (ZLE) and bash 4+ (readline `bind -x`, on Ctrl-J) intercept before parse so
11
+ # `for these files: …` can reach pithosai instead of a keyword parse error; the
12
+ # bash handler rewrites READLINE_LINE in place and lets accept-line run it.
13
+ # Real PATH commands skip that intercept.
14
+ #
15
+ # Env:
16
+ # PITHOSAI_NL_FALLBACK=0 disable all NL interception
17
+ # PITHOSAI_NL_ZLE=0 disable zsh/bash Enter intercept only
18
+ # PITHOSAI_NL_MODEL default krouter-rd
19
+ # PITHOSAI_NL_CLASSIFY_LLM=0 skip the LLM step when shell-score < 50
20
+ # PITHOSAI_NL_DEBUG_LOG=1 print classify/LLM debug on stderr
21
+ # PITHOSAI_BIN default pithosai
22
+ # PITHOSAI_LANG override locale for messages and --lang
23
+ # PITHOSAI_NODE node binary (default: node)
24
+
25
+ # Executed instead of sourced: print help and exit. `return` works only when sourced.
26
+ if [ -n "${ZSH_VERSION:-}" ]; then
27
+ case "${ZSH_EVAL_CONTEXT:-}" in
28
+ *:file*) ;;
29
+ *)
30
+ echo "Source this file from bash or zsh (do not execute it): source scripts/pithosai-nl-fallback.sh" >&2
31
+ return 1 2>/dev/null || exit 1
32
+ ;;
33
+ esac
34
+ # zsh NOMATCH aborts on unmatched globs (`you?`, `*.xyz`) before command_not_found_handler.
35
+ setopt NO_NOMATCH
36
+ setopt NO_BAD_PATTERN
37
+ elif [ -n "${BASH_VERSION:-}" ]; then
38
+ if [ "${BASH_SOURCE[0]}" = "$0" ]; then
39
+ echo "Source this file from bash or zsh (do not execute it): source scripts/pithosai-nl-fallback.sh" >&2
40
+ exit 1
41
+ fi
42
+ else
43
+ echo "Source this file from bash or zsh (do not execute it): source scripts/pithosai-nl-fallback.sh" >&2
44
+ return 1 2>/dev/null || exit 1
45
+ fi
46
+
47
+ if [ -n "${_PITHOSAI_NL_FALLBACK_LOADED:-}" ]; then
48
+ :
49
+ else
50
+ # Copy a pre-existing command-not-found handler so skips still use it.
51
+ _pithosai_copy_shell_function() {
52
+ local src="$1"
53
+ local dst="$2"
54
+ if [ -n "${ZSH_VERSION:-}" ]; then
55
+ if typeset -f "$src" >/dev/null 2>&1; then
56
+ functions -c "$src" "$dst"
57
+ fi
58
+ elif typeset -f "$src" >/dev/null 2>&1; then
59
+ eval "$(typeset -f "$src" | sed "1s/^${src}/${dst}/")"
60
+ fi
61
+ }
62
+
63
+ if [ -n "${ZSH_VERSION:-}" ]; then
64
+ if typeset -f command_not_found_handler >/dev/null 2>&1; then
65
+ _pithosai_copy_shell_function command_not_found_handler _pithosai_orig_cnf
66
+ fi
67
+ elif typeset -f command_not_found_handle >/dev/null 2>&1; then
68
+ _pithosai_copy_shell_function command_not_found_handle _pithosai_orig_cnf
69
+ fi
70
+
71
+ _PITHOSAI_NL_FALLBACK_LOADED=1
72
+ fi
73
+
74
+ # Directory of this sourced script (zsh %x / bash BASH_SOURCE).
75
+ pithosai_nl_fallback_script_dir() {
76
+ if [ -n "${ZSH_VERSION:-}" ]; then
77
+ local file="${(%):-%x}"
78
+ printf '%s' "${file:A:h}"
79
+ return 0
80
+ fi
81
+ (cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
82
+ }
83
+
84
+ # Path to the Node classifier (dist bundle, or src when sourcing from the repo).
85
+ pithosai_nl_fallback_classify_js() {
86
+ local dir
87
+ dir="$(pithosai_nl_fallback_script_dir)"
88
+ if [ -f "$dir/nl-fallback-classify.mjs" ]; then
89
+ printf '%s' "$dir/nl-fallback-classify.mjs"
90
+ return 0
91
+ fi
92
+ if [ -f "$dir/../src/cli/shared/nl-fallback/nl-fallback-classify.js" ]; then
93
+ printf '%s' "$dir/../src/cli/shared/nl-fallback/nl-fallback-classify.js"
94
+ return 0
95
+ fi
96
+ return 1
97
+ }
98
+
99
+ # Map locale env to a pithosai --lang code (en, zh, zh-Hant, …).
100
+ # Used when Node is unavailable. LANG=en_US.UTF-8 → en; zh_CN → zh; zh_TW → zh-Hant.
101
+ pithosai_nl_fallback_lang() {
102
+ local raw="${PITHOSAI_LANG:-${LC_ALL:-${LC_MESSAGES:-${LANG:-}}}}"
103
+ raw="${raw%%.*}"
104
+ case "$raw" in
105
+ zh_TW|zh-TW|zh_HK|zh-HK|zh-Hant|zh_Hant|zh-hant|zh_hant)
106
+ echo "zh-Hant"
107
+ return 0
108
+ ;;
109
+ esac
110
+ raw="${raw%%_*}"
111
+ raw="${raw%%-*}"
112
+ case "$raw" in
113
+ zh) echo "zh" ;;
114
+ ja) echo "ja" ;;
115
+ ko) echo "ko" ;;
116
+ ar) echo "ar" ;;
117
+ de) echo "de" ;;
118
+ es) echo "es" ;;
119
+ fr) echo "fr" ;;
120
+ it) echo "it" ;;
121
+ nl) echo "nl" ;;
122
+ pt) echo "pt" ;;
123
+ en) echo "en" ;;
124
+ *) echo "en" ;;
125
+ esac
126
+ }
127
+
128
+ # Print a localized hook message (pithosaiMissing, classifyUnavailable, or sourceOnly).
129
+ pithosai_nl_fallback_msg() {
130
+ local key="$1"
131
+ local input="${2:-}"
132
+ local lang
133
+ lang="$(pithosai_nl_fallback_lang)"
134
+ local msg=""
135
+ case "$key" in
136
+ pithosaiMissing)
137
+ case "$lang" in
138
+ zh) msg="未找到 pithosai。请安装 pithosai 或设置 PITHOSAI_BIN。" ;;
139
+ zh-Hant) msg="找不到 pithosai。請安裝 pithosai 或設定 PITHOSAI_BIN。" ;;
140
+ ja) msg="pithosai が見つかりません。インストールするか PITHOSAI_BIN を設定してください。" ;;
141
+ ko) msg="pithosai를 찾을 수 없습니다. pithosai를 설치하거나 PITHOSAI_BIN을 설정하세요." ;;
142
+ de) msg="pithosai nicht gefunden. Installieren Sie pithosai oder setzen Sie PITHOSAI_BIN." ;;
143
+ fr) msg="pithosai introuvable. Installez pithosai ou définissez PITHOSAI_BIN." ;;
144
+ es) msg="No se encontró pithosai. Instale pithosai o defina PITHOSAI_BIN." ;;
145
+ it) msg="pithosai non trovato. Installa pithosai o imposta PITHOSAI_BIN." ;;
146
+ nl) msg="pithosai niet gevonden. Installeer pithosai of zet PITHOSAI_BIN." ;;
147
+ pt) msg="pithosai não encontrado. Instale pithosai ou defina PITHOSAI_BIN." ;;
148
+ ar) msg="لم يتم العثور على pithosai. ثبّت pithosai أو عيّن PITHOSAI_BIN." ;;
149
+ *) msg="pithosai not found. Install pithosai or set PITHOSAI_BIN." ;;
150
+ esac
151
+ ;;
152
+ classifyUnavailable)
153
+ case "$lang" in
154
+ zh) msg="pithosai nl-fallback:无法运行分类器(需要 node 和 nl-fallback-classify)。" ;;
155
+ zh-Hant) msg="pithosai nl-fallback:無法執行分類器(需要 node 和 nl-fallback-classify)。" ;;
156
+ ja) msg="pithosai nl-fallback: 分類器を実行できません(node と nl-fallback-classify が必要です)。" ;;
157
+ ko) msg="pithosai nl-fallback: 분류기를 실행할 수 없습니다(node 및 nl-fallback-classify 필요)." ;;
158
+ de) msg="pithosai nl-fallback: Klassifizierer nicht verfügbar (node und nl-fallback-classify erforderlich)." ;;
159
+ fr) msg="pithosai nl-fallback : classifieur indisponible (node et nl-fallback-classify requis)." ;;
160
+ es) msg="pithosai nl-fallback: clasificador no disponible (se necesita node y nl-fallback-classify)." ;;
161
+ it) msg="pithosai nl-fallback: classificatore non disponibile (servono node e nl-fallback-classify)." ;;
162
+ nl) msg="pithosai nl-fallback: classifier niet beschikbaar (node en nl-fallback-classify vereist)." ;;
163
+ pt) msg="pithosai nl-fallback: classificador indisponível (é preciso node e nl-fallback-classify)." ;;
164
+ ar) msg="pithosai nl-fallback: المصنّف غير متاح (يلزم node و nl-fallback-classify)." ;;
165
+ *) msg="pithosai nl-fallback: classifier unavailable (need node + nl-fallback-classify)." ;;
166
+ esac
167
+ ;;
168
+ sourceOnly)
169
+ case "$lang" in
170
+ zh) msg="请在 bash 或 zsh 中 source 此文件(不要直接执行):source scripts/pithosai-nl-fallback.sh" ;;
171
+ zh-Hant) msg="請在 bash 或 zsh 中 source 此檔案(不要直接執行):source scripts/pithosai-nl-fallback.sh" ;;
172
+ ja) msg="bash または zsh から source してください(直接実行しないでください): source scripts/pithosai-nl-fallback.sh" ;;
173
+ ko) msg="bash 또는 zsh에서 이 파일을 source 하세요(직접 실행하지 마세요): source scripts/pithosai-nl-fallback.sh" ;;
174
+ de) msg="Diese Datei in bash oder zsh sourcen (nicht ausführen): source scripts/pithosai-nl-fallback.sh" ;;
175
+ fr) msg="Sourcez ce fichier depuis bash ou zsh (ne l'exécutez pas) : source scripts/pithosai-nl-fallback.sh" ;;
176
+ es) msg="Haga source de este archivo en bash o zsh (no lo ejecute): source scripts/pithosai-nl-fallback.sh" ;;
177
+ it) msg="Esegui source di questo file da bash o zsh (non eseguirlo): source scripts/pithosai-nl-fallback.sh" ;;
178
+ nl) msg="Source dit bestand in bash of zsh (niet uitvoeren): source scripts/pithosai-nl-fallback.sh" ;;
179
+ pt) msg="Faça source deste ficheiro no bash ou zsh (não o execute): source scripts/pithosai-nl-fallback.sh" ;;
180
+ ar) msg="قم بعمل source لهذا الملف من bash أو zsh (لا تشغّله): source scripts/pithosai-nl-fallback.sh" ;;
181
+ *) msg="Source this file from bash or zsh (do not execute it): source scripts/pithosai-nl-fallback.sh" ;;
182
+ esac
183
+ ;;
184
+ esac
185
+ printf '%s\n' "${msg//\{\{input\}\}/$input}"
186
+ }
187
+
188
+ # Return 0 when NL fallback env is enabled.
189
+ pithosai_nl_fallback_is_enabled() {
190
+ local disabled
191
+ disabled="$(printf '%s' "${PITHOSAI_NL_FALLBACK:-}" | tr '[:upper:]' '[:lower:]')"
192
+ case "$disabled" in
193
+ 0|false|no|off) return 1 ;;
194
+ esac
195
+ return 0
196
+ }
197
+
198
+ # Print a debug line on stderr when PITHOSAI_NL_DEBUG_LOG=1.
199
+ pithosai_nl_debug() {
200
+ local flag
201
+ flag="$(printf '%s' "${PITHOSAI_NL_DEBUG_LOG:-}" | tr '[:upper:]' '[:lower:]')"
202
+ case "$flag" in
203
+ 1|true|yes|on) printf '[pithosai-nl] %s\n' "$*" >&2 ;;
204
+ esac
205
+ }
206
+
207
+ # Run pithosai --no-interactive with the reconstructed natural-language input.
208
+ # $1 = input line, $2 = optional --lang code from the Node classifier.
209
+ pithosai_nl_fallback_run() {
210
+ local input="$1"
211
+ local lang="${2:-}"
212
+ local bin="${PITHOSAI_BIN:-pithosai}"
213
+ local model="${PITHOSAI_NL_MODEL:-krouter-rd}"
214
+ if [ -z "$lang" ]; then
215
+ lang="$(pithosai_nl_fallback_lang)"
216
+ fi
217
+ if [ -x "$bin" ] || command -v "$bin" >/dev/null 2>&1; then
218
+ "$bin" --no-interactive --model "$model" --lang "$lang" "$input"
219
+ return $?
220
+ fi
221
+ pithosai_nl_fallback_msg pithosaiMissing >&2
222
+ echo "command not found: ${bin}" >&2
223
+ return 127
224
+ }
225
+
226
+ # Ask Node whether this line is NL. Prints lang on stdout when it should run.
227
+ # Exit 0 = run, 1 = skip, 2 = classifier error.
228
+ pithosai_nl_fallback_classify() {
229
+ local input="$1"
230
+ local js nodebin
231
+ js="$(pithosai_nl_fallback_classify_js)" || return 2
232
+ nodebin="${PITHOSAI_NODE:-node}"
233
+ if ! command -v "$nodebin" >/dev/null 2>&1; then
234
+ return 2
235
+ fi
236
+ "$nodebin" "$js" "$input"
237
+ }
238
+
239
+ # Shared bash/zsh command-not-found body.
240
+ pithosai_handle_command_not_found() {
241
+ local cmd="$1"
242
+ local input="$*"
243
+ local lang code
244
+ pithosai_nl_debug "command_not_found cmd=${cmd} input=${input}"
245
+ if ! pithosai_nl_fallback_is_enabled; then
246
+ pithosai_nl_debug "fallback disabled"
247
+ pithosai_nl_fallback_not_found "$@"
248
+ return $?
249
+ fi
250
+ lang="$(pithosai_nl_fallback_classify "$input")"
251
+ code=$?
252
+ pithosai_nl_debug "classify exit=${code} lang=${lang}"
253
+ if [ "$code" -eq 0 ]; then
254
+ pithosai_nl_debug "run pithosai"
255
+ pithosai_nl_fallback_run "$input" "$lang"
256
+ return $?
257
+ fi
258
+ if [ "$code" -eq 2 ]; then
259
+ pithosai_nl_fallback_msg classifyUnavailable >&2
260
+ fi
261
+ pithosai_nl_debug "skip as command-not-found"
262
+ pithosai_nl_fallback_not_found "$@"
263
+ }
264
+
265
+ # Delegate to a previous handler, or print the usual command-not-found line.
266
+ pithosai_nl_fallback_not_found() {
267
+ local cmd="$1"
268
+ if typeset -f _pithosai_orig_cnf >/dev/null 2>&1; then
269
+ _pithosai_orig_cnf "$@"
270
+ return $?
271
+ fi
272
+ if [ -n "${ZSH_VERSION:-}" ]; then
273
+ echo "zsh: command not found: $cmd" >&2
274
+ else
275
+ echo "bash: $cmd: command not found" >&2
276
+ fi
277
+ return 127
278
+ }
279
+
280
+ command_not_found_handler() {
281
+ pithosai_handle_command_not_found "$@"
282
+ }
283
+
284
+ command_not_found_handle() {
285
+ pithosai_handle_command_not_found "$@"
286
+ }
287
+
288
+ # Return 0 when the line is empty or only whitespace (no Node classify).
289
+ pithosai_nl_line_is_blank() {
290
+ case "$1" in
291
+ "") return 0 ;;
292
+ esac
293
+ [ -z "${1##*[![:space:]]*}" ] && return 1
294
+ return 0
295
+ }
296
+
297
+ # First whitespace-separated token of a line.
298
+ pithosai_nl_first_word() {
299
+ printf '%s\n' "$1" | awk '{ print $1; exit }'
300
+ }
301
+
302
+ # Return 0 for shell keywords that users also type as English (`for these files:`).
303
+ pithosai_nl_is_reserved_word() {
304
+ local w
305
+ w="$(printf '%s' "$1" | tr '[:upper:]' '[:lower:]')"
306
+ w="${w%%[^a-z]*}"
307
+ case "$w" in
308
+ for|do|if|while|until|case|then|else|elif|fi|done|in|select) return 0 ;;
309
+ esac
310
+ return 1
311
+ }
312
+
313
+ # Return 0 when Enter should pass the line through to the shell (no classify).
314
+ pithosai_nl_should_passthrough_line() {
315
+ local line="$1"
316
+ local first
317
+ if pithosai_nl_line_is_blank "$line"; then
318
+ return 0
319
+ fi
320
+ first="$(pithosai_nl_first_word "$line")"
321
+ if [ -n "$first" ] && ! pithosai_nl_is_reserved_word "$first"; then
322
+ if command -v -- "$first" >/dev/null 2>&1; then
323
+ return 0
324
+ fi
325
+ fi
326
+ return 1
327
+ }
328
+
329
+ # Return 0 when the zsh/bash Enter intercept is enabled.
330
+ pithosai_nl_zle_is_enabled() {
331
+ local zle_off
332
+ zle_off="$(printf '%s' "${PITHOSAI_NL_ZLE:-}" | tr '[:upper:]' '[:lower:]')"
333
+ case "$zle_off" in
334
+ 0|false|no|off) return 1 ;;
335
+ esac
336
+ pithosai_nl_fallback_is_enabled
337
+ }
338
+
339
+ # Classify and run pithosai when the line is NL. Return 0 if pithosai ran.
340
+ pithosai_nl_try_run_line() {
341
+ local line="$1"
342
+ local lang code
343
+ pithosai_nl_debug "try_run_line input=${line}"
344
+ lang="$(pithosai_nl_fallback_classify "$line")"
345
+ code=$?
346
+ pithosai_nl_debug "try_run_line classify exit=${code} lang=${lang}"
347
+ if [ "$code" -eq 2 ]; then
348
+ pithosai_nl_fallback_msg classifyUnavailable >&2
349
+ fi
350
+ if [ "$code" -eq 0 ]; then
351
+ pithosai_nl_fallback_run "$line" "$lang"
352
+ return 0
353
+ fi
354
+ return 1
355
+ }
356
+
357
+ # zsh: intercept Enter before parse. Never assign BUFFER to a pithosai CLI string.
358
+ if [ -n "${ZSH_VERSION:-}" ]; then
359
+ pithosai_nl_accept_line() {
360
+ local line="${BUFFER}"
361
+ pithosai_nl_debug "zle accept-line input=${line}"
362
+ if ! pithosai_nl_zle_is_enabled; then
363
+ pithosai_nl_debug "zle intercept disabled"
364
+ zle .accept-line
365
+ return
366
+ fi
367
+ if pithosai_nl_should_passthrough_line "$line"; then
368
+ pithosai_nl_debug "zle passthrough"
369
+ zle .accept-line
370
+ return
371
+ fi
372
+ # Unfreeze the line editor so classify/LLM stderr can paint (otherwise Enter looks hung).
373
+ zle -I
374
+ print
375
+ if pithosai_nl_try_run_line "$line"; then
376
+ pithosai_nl_debug "zle run pithosai"
377
+ print -s -- "$line"
378
+ BUFFER=""
379
+ zle .reset-prompt
380
+ return 0
381
+ fi
382
+ zle .accept-line
383
+ }
384
+ if [[ -o interactive ]]; then
385
+ zle -N pithosai_nl_accept_line 2>/dev/null || true
386
+ zle -N accept-line pithosai_nl_accept_line 2>/dev/null || true
387
+ fi
388
+ fi
389
+
390
+ # Build the quoted pithosai CLI invocation for an NL line so the native
391
+ # accept-line (not the readline handler) runs it. $1 = line, $2 = lang code.
392
+ pithosai_nl_bash_cli_command() {
393
+ local input="$1"
394
+ local lang="${2:-}"
395
+ local bin="${PITHOSAI_BIN:-pithosai}"
396
+ local model="${PITHOSAI_NL_MODEL:-krouter-rd}"
397
+ if [ -z "$lang" ]; then
398
+ lang="$(pithosai_nl_fallback_lang)"
399
+ fi
400
+ printf '%q --no-interactive --model %q --lang %q %q' \
401
+ "$bin" "$model" "$lang" "$input"
402
+ }
403
+
404
+ # bash 4+: intercept a key via readline bind -x and REWRITE the line in place.
405
+ #
406
+ # A bind -x handler runs while readline does not own the terminal, so it must
407
+ # never eval a command, spawn a child that reads stdin, or printf a newline:
408
+ # doing so desyncs readline and breaks TAB completion afterwards. Instead the
409
+ # handler only rewrites READLINE_LINE and returns.
410
+ #
411
+ # bind -x ALONE does NOT submit the line — it replaces the default accept-line
412
+ # binding. To submit the rewritten line in one keypress we use a two-key macro:
413
+ # bind -x '"\C-x\C-p": handler' (shell command)
414
+ # bind '"\C-j": "\C-x\C-p\C-m"' (macro: handler + accept-line)
415
+ # Pressing Ctrl-J dispatches \C-x\C-p (runs handler, rewrites), then \C-m
416
+ # (accept-line executes the rewritten line) — all with readline in control.
417
+ #
418
+ # Note: bash cannot intercept plain Enter (\C-m is consumed by accept-line and
419
+ # never reaches a bind -x macro). Lines that fail a shell parse error
420
+ # (`for these files: ...`) never reach the command-not-found path either;
421
+ # only \C-j or the command-not-found handle can recover them.
422
+ if [ -n "${BASH_VERSION:-}" ]; then
423
+ pithosai_nl_bash_accept_line() {
424
+ local line="${READLINE_LINE}"
425
+ local lang code cmd
426
+ pithosai_nl_debug "bash accept-line input=${line}"
427
+ if pithosai_nl_zle_is_enabled && ! pithosai_nl_should_passthrough_line "$line"; then
428
+ lang="$(pithosai_nl_fallback_classify "$line")"
429
+ code=$?
430
+ pithosai_nl_debug "bash classify exit=${code} lang=${lang}"
431
+ if [ "$code" -eq 0 ]; then
432
+ cmd="$(pithosai_nl_bash_cli_command "$line" "$lang")"
433
+ pithosai_nl_debug "bash rewrite to pithosai"
434
+ READLINE_LINE="$cmd"
435
+ READLINE_POINT=${#cmd}
436
+ return 0
437
+ fi
438
+ if [ "$code" -eq 2 ]; then
439
+ pithosai_nl_fallback_msg classifyUnavailable >&2
440
+ fi
441
+ pithosai_nl_debug "bash passthrough (classify miss)"
442
+ fi
443
+ # No rewrite happened: restore the buffer exactly as readline gave it, so a
444
+ # skipped line (PATH command, valid for/if one-liner, empty line) runs
445
+ # unchanged. Set both vars explicitly because a previous handler call may
446
+ # have left a rewritten buffer in place.
447
+ READLINE_LINE="$line"
448
+ READLINE_POINT=${#line}
449
+ return 0
450
+ }
451
+
452
+ if [[ $- == *i* ]] && [ "${BASH_VERSINFO[0]}" -ge 4 ]; then
453
+ bind -x '"\C-x\C-p": pithosai_nl_bash_accept_line' 2>/dev/null || true
454
+ bind '"\C-j": "\C-x\C-p\C-m"' 2>/dev/null || true
455
+ fi
456
+ fi