Core

Imports


CodeBlock.__rich_console__


def __rich_console__(
    console, options
):

Chat


def Chat(
    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)
def print_md(md_stream):
    "Print streamed markdown"
    with Live(Spinner("dots", text="Connecting..."), auto_refresh=False) as live:
        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
):
# 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(
    
):
tmux_history_lim()
2000

get_history


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

Options and ShellSage


get_opts


def get_opts(
    opts:VAR_KEYWORD
):
opts = get_opts(model=None, log=None, api_base=None, api_key=''); opts
{'provider': 'anthropic', 'model': 'claude-opus-4-5', 'think': 'l', 'search': 'l', 'trust': 'view,rg', 
'history_lines': '1000', 'code_theme': 'xcode', 'code_lexer': 'python', 'log': 'True', 'safecmd': 'True'}
{'api_base': '', 'api_key': '', 'log': True, 'model': 'claude-opus-4-5'}

with_permission


def with_permission(
    action_desc
):
print(tools[1]('.'))
Directory contents of /Users/jhoward/aai-ws/shell_sage/nbs:
/Users/jhoward/aai-ws/shell_sage/nbs/00_core.ipynb
/Users/jhoward/aai-ws/shell_sage/nbs/_quarto.yml
/Users/jhoward/aai-ws/shell_sage/nbs/sidebar.yml
/Users/jhoward/aai-ws/shell_sage/nbs/styles.css
/Users/jhoward/aai-ws/shell_sage/nbs/CNAME
/Users/jhoward/aai-ws/shell_sage/nbs/01_config.ipynb
/Users/jhoward/aai-ws/shell_sage/nbs/nbdev.yml
/Users/jhoward/aai-ws/shell_sage/nbs/index.ipynb

get_sage


def get_sage(
    model, mode:str='default', search:bool=False, use_safecmd:bool=False
):
# m = 'ollama_chat/qwen3:8b'
# ssage = get_sage(m)
# ssage('Howdy!')
m = 'claude-sonnet-4-5'
ssage = get_sage(m, search='l')
ssage('Hi, how are ya?', think='l')

Hey there! I’m doing well, thanks for asking. Ready to help you navigate the command line, troubleshoot shell issues, or dive into any system administration questions you’ve got. What can I help you with today?

  • id: chatcmpl-96ad87f4-6fd0-4088-a6d6-6bfa0ad36097
  • model: claude-sonnet-4-5-20250929
  • finish_reason: stop
  • usage: Usage(completion_tokens=108, prompt_tokens=3387, total_tokens=3495, completion_tokens_details=CompletionTokensDetailsWrapper(accepted_prediction_tokens=None, audio_tokens=None, reasoning_tokens=48, rejected_prediction_tokens=None, text_tokens=None, image_tokens=None), prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=None, cached_tokens=0, text_tokens=None, image_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)

get_res


def get_res(
    sage, q, opts
):
opts=NS(api_base='', api_key='', think='l')
list(get_res(ssage, 'Use tools to check if we have a  .git in the current directory. Respond with yes/no', opts))
['', '', '', '', '', '', '', '', '', '', '', '', '', '', '', 'No', 'No']
print_md(get_res(ssage, 'Hi!', opts))
Hey! What's up? Need help with something?                                                                          
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))
You're in the /Users/jhoward/aai-ws/shell_sage/nbs directory which contains several Jupyter notebooks              
(00_core.ipynb, 01_config.ipynb, index.ipynb), configuration files for Quarto and nbdev (_quarto.yml, nbdev.yml,   
sidebar.yml), a styles.css file, and a CNAME file, suggesting this is an nbdev project setup for documentation     
generation with Quarto.                                                                                            
print_md(get_res(ssage, 'Please search the web for interesting facts about Linux. Only respond with a single paragraph.', opts));
Linux has a fascinating history and impact on computing: created by Linus Torvalds in 1991 as a hobby project when 
he was just a 21-year-old student, it's now the backbone of the internet with over 96% of the world's top web      
servers running on it, powers Android devices (making it the most widely used OS on the planet), operates the      
International Space Station's computers, runs most supercomputers globally, and is completely open-source with     
thousands of developers contributing to its kernel—all while Torvalds still oversees development today, and the    
penguin mascot "Tux" was named after Torvalds' (T)orvalds (U)ni(X), though the actual inspiration came from a photo
of a penguin that supposedly bit him at an Australian zoo.                                                         

Logging


mk_db


def mk_db(
    
):

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


def main(
    query:str <The query to send to the LLM>, v:<Print version>='%(prog)s 1.0.5',
    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
):
main(['Do you have a `bash` tool?.'], history_lines=0)
Yes, I have a bash tool that can run shell commands safely. It supports most standard unix commands, git           
operations, pipes, and shell operators. However, it has an allow-list of safe commands and blocks output file      
redirection (>) to prevent accidental overwrites.                                                                  
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=1, timestamp='2025-12-03T04:46:49.790878', 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
):
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