from dialoghelper import *dialoghelper.utils
from fastcore import tools
from fastcore.test import *ast-grep
node = ast_py("print('hello world')")
stmt = node.find(pattern="print($A)")
res = stmt.get_match('A')
res.text(),res.range()("'hello world'",
Range(start=Pos(line=0, col=6, index=6), end=Pos(line=0, col=19, index=19)))
ast_grep
def ast_grep(
pattern:str, # ast-grep pattern to search, e.g "post($A, data=$B, $$$)"
path:str='.', # path to recursively search for files
lang:str='python', # language to search/scan
):Use ast-grep to find code patterns by AST structure (not text).
Pattern syntax: - $VAR captures single nodes, \[$ captures multiple
- Match structure directly: `def $FUNC(\]$)finds any function;class $CLASSfinds classes regardless of inheritance - DON'T include:` - it’s concrete syntax, not AST structure - Whitespace/formatting ignored - matches structural equivalence
Examples: import $MODULE (find imports); $OBJ.$METHOD($$$) (find method calls); await $EXPR (find await expressions)
Use ast_grep to find calls to an API before changing it. Patterns match multiline code and nested expressions.
ast_grep calls the ast-grep CLI to search code by syntax tree. Matches ignore formatting differences.
The pattern add_msg($A, msg_type=$B) matches calls to add_msg with two arguments:
- Any first argument, captured as
$A. - A
msg_typekeyword argument, captured as$B.
res = ast_grep(r"add_msg($A, msg_type=$B)", '..')
[(o['text'],o['metaVariables']['single'],o['file']) for o in res][('xpost(url, data=data, headers=headers, timeout=timeout)',
{'A': {'text': 'url',
'range': {'byteOffset': {'start': 8296, 'end': 8299},
'start': {'line': 168, 'column': 30},
'end': {'line': 168, 'column': 33}}},
'B': {'text': 'data',
'range': {'byteOffset': {'start': 8306, 'end': 8310},
'start': {'line': 168, 'column': 40},
'end': {'line': 168, 'column': 44}}}},
'dialoghelper/core.py')]
A pattern matches code, such as await $EXPR or console.log($ARG). A kind rule matches a syntax tree node type.
To search the entire subtree with has or inside, set stopBy: end:
has:
pattern: await $EXPR
stopBy: endIn shell commands, use single quotes around patterns or escape $ as \$ to prevent variable expansion.
_ast_id = await add_msg("print('hello')\nprint('world')\nlog('keep')", msg_type='code')print(await msg_ast_replace(_ast_id, 'print($A)', 'logger.info($A)'))@@ -1,3 +1,3 @@
-print('hello')
-print('world')
+logger.info('hello')
+logger.info('world')
log('keep')
print((await read_msg(n=0, id=_ast_id, nums=True))['content']) 1 │ logger.info('hello')
2 │ logger.info('world')
3 │ log('keep')
await del_msgs(_ast_id)['2d7424eb']
Gists and github
import_string
def import_string(
code:str, # Code to import as a module
name:str, # Name of module to create
):def hi(who:str):
"Say hi to `who`"
return f"Hello {who}"
def hi2(who):
"Say hi to `who`"
return f"Hello {who}"
def hi3(who:str): return f"Hello {who}"
bye = "bye"assert is_usable_tool(hi)
assert not is_usable_tool(hi2)
assert not is_usable_tool(hi3)
assert not is_usable_tool(bye)print(mk_toollist([hi]))- &`hi`: Say hi to `who`
import_gist
def import_gist(
gist_id:str, # user/id or just id of gist to import as a module
mod_name:str=None, # module name to create (taken from gist filename if not passed)
add_global:bool=True, # add module to caller's globals?
import_wildcard:bool=False, # import all exported symbols to caller's globals
create_msg:bool=False, # Add a message that lists usable tools
):Import gist directly from string without saving to disk
gistid = 'jph00/e7cfd4ded593e8ef6217e78a0131960c'
import_gist(gistid)
importtest.testfoo'testbar'
import_gist.__doc__'Import gist directly from string without saving to disk'
import_gist(gistid, import_wildcard=True)
importtest.testfoo'testbar'
hi("Sarah")'Hello Sarah'
importtest.__all__['hi', 'whoami']
Input
input accepts a string prompt or custom UI. Custom UI needs:
- A
<solveit-input>wrapper, whichinputadds automatically. - A POST to
/input_reply_with a value foruser_input. - For keyboard shortcuts, an
accesskeyon the button and a containing form or div with IDinput-request-form.
show_prompt uses InputBtn to provide a yes/no prompt:
InputForm
def InputForm(
*c, enctype:str='multipart/form-data', **kwargs
):Create an input() with a Form with needed hx_post and id
def show_prompt():
return InputForm(Div('Ship this change now?'),
Div(cls='flex gap-2')(InputBtn('Yes', btncls='primary', accesskey='y'), InputBtn('No', btncls='default', accesskey='n')))show_prompt()