Core

Imports


CodeBlock.__rich_console__


def __rich_console__(
    console, options
):

Call self as a function.


AsyncChat


def AsyncChat(
    arg:VAR_POSITIONAL, kw:VAR_KEYWORD
):

Lazy load lisette to make ssage more responsive

Jupyter does work with rich.live.Live, this fixes it.

@contextmanager
def Live(start, **kw):
    print(start)
    def update(s, refresh=False): clear_output(True);print(s)
    yield NS(update=update)
async def print_md(md_stream):
    "Print streamed markdown"
    with Live(Spinner("dots", text="Connecting..."), auto_refresh=False) as live:
        async for part in md_stream: live.update(Markdown(part), refresh=True)

Model Setup

System Environment

# aliases = _aliases('bash')
# print(aliases)
# print(_sys_info())

Tmux


get_pane


def get_pane(
    n, pid:NoneType=None
):

Get output from a tmux pane

# p = get_pane(20)
# print(p[:512])

get_panes


def get_panes(
    n
):

Call self as a function.

# ps = get_panes(20)
# print(ps[:512])
co(['tmux', 'display-message', '-p', '#{history-limit}'], text=True).strip()
'2000'

tmux_history_lim


def tmux_history_lim(
    
):

Call self as a function.

tmux_history_lim()
2000

get_history


def get_history(
    n, pid:str='current'
):

Call self as a function.

Options and ShellSage


get_opts


def get_opts(
    opts:VAR_KEYWORD
):

Call self as a function.

opts = get_opts(model=None, log=None, api_base=None, api_key=''); opts
{'api_base': '', 'api_key': '', 'log': False, 'model': 'claude-sonnet-4-6'}

Rich’s Live display and input() can’t coexist — Live manages the terminal cursor, and input() needs it too. When Live.stop() is called, it prints its current renderable as static text, which causes duplication since get_res accumulates the full response. The fix: clear Live’s renderable before stopping, flush the accumulated response as static markdown, and reset the buffer. This way streaming resumes fresh after the tool interaction with no overlap.


with_permission


def with_permission(
    action_desc
):

Call self as a function.

print(tools[1]('.'))
About to View file/directory with the following arguments: {'args': ['.'], 'kwargs': {}}
Directory contents of /Users/keremturgutlu/aai-ws/shell_sage/nbs:
/Users/keremturgutlu/aai-ws/shell_sage/nbs/00_core.ipynb (30.4k)
/Users/keremturgutlu/aai-ws/shell_sage/nbs/_quarto.yml (0.3k)
/Users/keremturgutlu/aai-ws/shell_sage/nbs/sidebar.yml (0.1k)
/Users/keremturgutlu/aai-ws/shell_sage/nbs/styles.css (0.6k)
/Users/keremturgutlu/aai-ws/shell_sage/nbs/CNAME (0.0k)
/Users/keremturgutlu/aai-ws/shell_sage/nbs/01_config.ipynb (4.1k)
/Users/keremturgutlu/aai-ws/shell_sage/nbs/tmux.conf (1.4k)
/Users/keremturgutlu/aai-ws/shell_sage/nbs/nbdev.yml (0.2k)
/Users/keremturgutlu/aai-ws/shell_sage/nbs/index.ipynb (64.4k)

get_sage


def get_sage(
    model, mode:str='default', search:bool=False, use_safecmd:bool=False, backend:str='lisette',
    vendor_name:NoneType=None
):

Call self as a function.

# m = 'ollama_chat/qwen3:8b'
# ssage = get_sage(m)
# ssage('Howdy!')
m = 'claude-sonnet-4-6'
ssage = get_sage(m, search='l')
await ssage('Hi, how are ya?', think='l')

I’m doing great, thanks for asking! I’m ShellSage, your command-line teaching assistant. Ready to help you with any shell commands, scripting questions, or system administration tasks you have. What can I help you with today?

  • id: chatcmpl-6136c285-faae-4543-8b86-63bd33a98545
  • model: claude-sonnet-4-6
  • finish_reason: stop
  • usage: Usage(completion_tokens=54, prompt_tokens=3472, total_tokens=3526, completion_tokens_details=CompletionTokensDetailsWrapper(accepted_prediction_tokens=None, audio_tokens=None, reasoning_tokens=0, rejected_prediction_tokens=None, text_tokens=54, image_tokens=None, video_tokens=None), prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=None, cached_tokens=0, text_tokens=None, image_tokens=None, video_tokens=None, cache_creation_tokens=0, cache_creation_token_details=CacheCreationTokenDetails(ephemeral_5m_input_tokens=0, ephemeral_1h_input_tokens=0)), cache_creation_input_tokens=0, cache_read_input_tokens=0, inference_geo='global', speed=None)
fssage = get_sage(m, search='l', backend='fastllm')
await fssage('Hi, how are ya?', think='l')

I’m doing great, thanks for asking! Ready to help you navigate the command line. 😄

Whether you need help with a tricky command, want to understand some shell scripting, or are trying to debug system issues — just ask!

  • model: claude-sonnet-4-6
  • finish_reason: stop
  • usage: Usage(prompt_tokens=5638, completion_tokens=54, total_tokens=5692, cached_tokens=0, cache_creation_tokens=0, reasoning_tokens=0, raw={'input_tokens': 5638, 'cache_creation_input_tokens': 0, 'cache_read_input_tokens': 0, 'cache_creation': {'ephemeral_5m_input_tokens': 0, 'ephemeral_1h_input_tokens': 0}, 'output_tokens': 54, 'service_tier': 'standard', 'inference_geo': 'global'})

get_res


def get_res(
    sage, q, opts
):

Call self as a function.

opts=NS(api_base='', api_key='', think='l', backend='lisette')
[o async for o in get_res(ssage, 'Use tools to check if we have a  .git in the current directory. Respond with yes/no', opts)]
About to ripgrep a search term with the following arguments: {'argstr': '--files --max-depth 1 -g ".git" .'}
About to View file/directory with the following arguments: {'path': '.'}
['',
 '',
 '',
 '',
 '',
 '',
 '',
 '',
 '',
 '',
 '',
 '',
 '',
 '',
 '',
 '',
 '',
 '',
 '',
 'No',
 'No — but the parent directory (`shell_sage`) likely',
 'No — but the parent directory (`shell_sage`) likely has one.',
 'No — but the parent directory (`shell_sage`) likely has one.',
 'No — but the parent directory (`shell_sage`) likely has one.']
opts=NS(api_base='', api_key='', think='l', backend='fastllm')
[o async for o in get_res(fssage, 'Use `rg` tool to check if we have a  .git in the current directory. Respond with yes/no', opts)]
About to ripgrep a search term with the following arguments: {'argstr': '--files --max-depth 1 --hidden -g ".git"'}
['No.']
await print_md(get_res(ssage, 'Hi!', opts))
⠋ Connecting...
await print_md(get_res(fssage, 'Hi!', opts))
Hey! 👋 What shell command or system admin topic can I help you with today?                                        
await print_md(get_res(ssage, 'Please use your view command to see what files are in the current directory. Only respond with a single paragraph', opts))
⠋ Connecting...
About to View file/directory with the following arguments: {'path': '.'}
await print_md(get_res(fssage, 'Please use your view command to see what files are in the current directory. Only respond with a single paragraph', opts))
The current directory (/Users/keremturgutlu/aai-ws/shell_sage/nbs) contains a mix of Jupyter notebooks             
(00_core.ipynb, 01_config.ipynb, index.ipynb), configuration files (_quarto.yml, nbdev.yml, sidebar.yml), a        
styles.css stylesheet, a tmux.conf config, and a CNAME file — looks like an nbdev project with Quarto-based        
documentation!                                                                                                     
await print_md(get_res(fssage, 'Please search the web for interesting facts about Linux. Only respond with a single paragraph.', opts));
I wasn't able to retrieve the web search results due to a tool limit. However, here are some well-known interesting
facts about Linux from my training knowledge: Linux was created by Linus Torvalds in 1991 as a hobby project while 
he was a student at the University of Helsinki; it powers the vast majority of the world's servers, supercomputers,
and Android devices; the Linux kernel has grown to millions of lines of code contributed by thousands of developers
worldwide; and despite being free and open-source, it underpins much of the infrastructure behind major companies  
like Google, Amazon, and Facebook.                                                                                 
await print_md(get_res(ssage, 'Please search the web for interesting facts about Linux. Only respond with a single paragraph.', opts));
⠋ Connecting...
await print_md(get_res(fssage, 'Please search the web for interesting facts about Linux. Only respond with a single paragraph.', opts));
*Since its creation in 1991, Linux has gone on to revolutionize the world, empower startups, and birth new         
industries. *Linux very nearly wasn't called Linux — Linus wanted to call his project "FreaX," but was persuaded   
otherwise by the owner of the server hosting his early code, who preferred the name "Linux." *Out of the top 500   
fastest supercomputers in the world, Linux or its variants power 485 of those machines. *Space programs by NASA,   
SpaceX, and many other space agencies rely on Linux, and the International Space Station has run Linux since 2013. 
*Today, over 80% of Linux contributions come from developers paid by big enterprises, with Intel topping the list  
of contributors for most kernel releases. *And for a truly fun fact — "Linux" is also a genuine washing powder     
brand in Switzerland, a company named the same as the kernel created by Linus Torvalds!                            

Logging


mk_db


def mk_db(
    
):

Call self as a function.


Log


def Log(
    args:VAR_POSITIONAL, kwargs:VAR_KEYWORD
):

Initialize self. See help(type(self)) for accurate signature.

# db = mk_db()
# log = db.logs.insert(Log(timestamp=datetime.now().isoformat(), query='Hi, who are you?', model='llama3.2',
#                          response='I am ShellSage, a command-line teaching assistant!', mode='default'))
# log

Main


main


async def main(
    query:str <The query to send to the LLM>, v:<Print version>='%(prog)s 1.0.8',
    pid:str='current', # `current`, `all` or tmux pane_id (e.g. %0) for context
    skip_system:bool=False, # Whether to skip system information in the AI's context
    history_lines:int=None, # Number of history lines. Defaults to tmux scrollback history length
    mode:str='default', # Available ShellSage modes: ['default', 'sassy']
    model:str=None, # The LLM model that will be invoked on the LLM provider
    search:str=None, # Wheather to allow the LLM to search the internet
    api_base:str=None, api_key:str=None,
    think:str=None, # Reasoning effort level: 'l', 'm', 'h' (for supported models)
    trust:str=None, # Comma-delimited list of tools to always allow (e.g. "view,rg")
    code_theme:str=None, # The code theme to use when rendering ShellSage's responses
    code_lexer:str=None, # The lexer to use for inline code markdown blocks
    raw:bool=False, # Skip markdown rendering and print plain text
    backend:str=None, # Backend to use. Defaults to shell_sage config.
    vendor_name:str=None, # Vendor name for fastllm backend (e.g. 'fireworks_ai')
):

Call self as a function.

await main(['Do you have a `bash` tool?.'], history_lines=0)
No, I don't have a bash tool. My available tools are:                                                              

view — view file/directory contents                                                                             
rg — run ripgrep searches                                                                                       
create — create new files                                                                                       
str_replace — edit files by replacing text                                                                      
insert — insert text at a specific line in a file                                                               

I can read files and search codebases, but I can't execute arbitrary shell commands. For running commands, you'd do
that directly in your terminal.                                                                                    
await main(['Do you have a `bash` tool?.'], history_lines=0, backend='fastllm')
No, I don't have a bash (or any shell execution) tool. My available tools are:                                     

view — view file/directory contents                                                                             
rg — search files using ripgrep                                                                                 
create — create new files                                                                                       
str_replace — edit files by replacing text                                                                      
insert — insert text at a line number                                                                           

I can read and search your codebase and make file edits, but I cannot execute shell commands directly. You run     
commands on your end, and I can help you understand output or craft the right commands to use.                     
r = f'''
Hello, user! Here are some code blocks:

```python
for i in range(10): print(i)
```

```
This doesn't even have a language definition!
```

```bash
ls **/*
```
'''
db = mk_db()
db.logs.insert(Log(timestamp=datetime.now().isoformat(), query='', response=r, model='', mode=''))
Log(id=4, timestamp='2026-05-06T16:24:11.912059', query='', response="\nHello, user! Here are some code blocks:\n\n```python\nfor i in range(10): print(i)\n```\n\n```\nThis doesn't even have a language definition!\n```\n\n```bash\nls **/*\n```\n", model='', mode='')

extract_cf


def extract_cf(
    idx
):

Call self as a function.

extract_cf(0)
'for i in range(10): print(i)'

extract


def extract(
    idx:int, # Index of code block to extract
    copy:bool=False, # Copy to clipboard
    do_print:bool=False, # Print (useful for readline custom shortcuts)
):

Extracts the idx’th codefence from the last shell sage response and sends it to tmux by default