Runex
A context generation tool to bring vibe coding in your life… or do even more.
pip install runex
Introduction
runex
is a python tool that generates a plain-text
or json
representation of a project’s directory structure (plus file contents), following Git’s own .gitignore
path and file matching rules.
Everything’s close enough…
until it isn't.
[!NOTE] Before using this program it is highly recommended you read the
Use of AI during development
paragraph
Example output(s)
Let’s see if this can be good for you, will it be love at first sight?
Default behavior
plain-text
no args needed
You ask:
runex .
You get:
Project Structure:
my-project/
├── .gitignore
├── src/
│ └── next-big-thing.py
└── tests/
# File: next-big-thing.py
# Path: src/next-big-thing.py
#!/usr/bin/env python
"""
Hey there
"""
if __name__ == "__main__":
print("Hello World!")
json
add
-oj
You ask:
runex . -oj
You get:
{
"structure": {
"name": "my-project",
"children": [
{
"name": ".gitignore"
},
{
"name": "src",
"children": [
{
"name": "next-big-thing.py"
}
]
},
{
"name": "tests",
"children": []
}
]
},
"files": [
{
"filename": "next-big-thing.py",
"path": "src/next-big-thing.py",
"content": "#!/usr/bin/env python\n\"\"\"\nHey there\n\"\"\"\n \nif __name__ == \"__main__\":\n print(\"Hello World!\")\n"
}
]
}
No need for file contents?
just add
-s
plain text
You ask:
runex . -s
You get:
Project Structure:
my-project/
├── .gitignore
├── src/
│ └── next-big-thing.py
└── tests/
json
You ask:
runex . -oj -s
You get:
{
"structure": {
"name": "my-project",
"children": [
{
"name": ".gitignore"
},
{
"name": "src",
"children": [
{
"name": "next-big-thing.py"
}
]
},
{
"name": "tests",
"children": []
}
]
}
}
So, you got this far.
You might need this tool?
Well…
What have we learned other than that?
> Answer: _You ask, You get_Purpose of This Program
The og scope of the program was to cater the need for an easy and quick way (see rfc1925 point 7a) of providing “context” to a large language model
service, without painstakingly ctrl-c and ctrl-v(ing) source code out of the IDE into the browser, all while t(c)rying to explain to the llm where a file should be read from and what it should actually do. (you still have to paste it back to the IDE so at least it’s almost half of the work done)
If you want that, we (me and gpt) will have a runex visual studio code extension
available. (COMING-SOON)
Usage - Deep Dive
runex [--help] [--casefold] [--json] [--only-structure] [--relative-root] <root_dir> [output_file]
Positional arguments:
- root_dir
Root directory of the project to be scanned - output_file
Optional output file (default: stdout)
Options:
--help
show this help message and exit--casefold, -c
Enable case-insensitive matching (WM_CASEFOLD)--json, -oj
Output JSON instead of text--only-structure, -s
Omit file contents in the output--relative-root, -rr
Force the root directory name to be ‘.’ instead of basename
In the default mode, after traversing the specified project directory, runex
builds and concatenates into a single file (or stdout):
- A pretty-printed tree-style output of the folder structure (excluding ignored paths).
- A concatenated listing of the file contents found along it’s path (excluding ignored files).
The same output can be also requested in a json
format and for both you can request only the structure of the project to be returned.
Examples
Tool executed with the --only-structure
arg on the “.” directory:
runex . -s
Project Structure:
runex/
├── .gitignore
├── LICENSE
├── README.md
├── json_test_cases/
│ ├── t_00.json
│ ├── t_01.json
│ ├── t_02.json
│ ├── t_03.json
│ ├── t_04.json
│ ├── t_05.json
│ ├── t_06.json
│ ├── t_07.json
│ ├── t_08.json
│ ├── t_09.json
│ ├── t_10.json
│ ├── t_11.json
│ ├── t_12.json
│ └── t_13.json
├── poetry.lock
├── pyproject.toml
├── runex/
│ ├── __init__.py
│ ├── cli.py
│ ├── core.py
│ ├── ignore_logic.py
│ └── wildmatch.py
└── tests/
├── __init__.py
├── all_unicode_posix_extended_test.py
├── all_unicode_posix_test.py
├── custom_posix_test.py
├── deepseek_test.py
├── extreme_test.py
├── gitignore_doc_json_test.py
├── ignore_logic_test.py
├── posix_test.py
├── prompt_generation_test.py
├── prompt_generator_json_test.py
├── suite_test.py
├── te_test.py
└── wildmatch_test.py
Tool executed with the --only-structure
and --json
args on the “.” directory:
runex . -oj -s
{
"structure": {
"name": "codetext",
"children": [
{
"name": ".gitignore"
},
{
"name": "LICENSE"
},
{
"name": "README.md"
},
{
"name": "json_test_cases",
"children": [
{
"name": "t_00.json"
},
{
"name": "t_01.json"
},
{
"name": "t_02.json"
},
{
"name": "t_03.json"
},
{
"name": "t_04.json"
},
{
"name": "t_05.json"
},
{
"name": "t_06.json"
},
{
"name": "t_07.json"
},
{
"name": "t_08.json"
},
{
"name": "t_09.json"
},
{
"name": "t_10.json"
},
{
"name": "t_11.json"
},
{
"name": "t_12.json"
},
{
"name": "t_13.json"
}
]
},
{
"name": "poetry.lock"
},
{
"name": "pyproject.toml"
},
{
"name": "runex",
"children": [
{
"name": "__init__.py"
},
{
"name": "cli.py"
},
{
"name": "core.py"
},
{
"name": "ignore_logic.py"
},
{
"name": "wildmatch.py"
}
]
},
{
"name": "tests",
"children": [
{
"name": "__init__.py"
},
{
"name": "all_unicode_posix_extended_test.py"
},
{
"name": "all_unicode_posix_test.py"
},
{
"name": "custom_posix_test.py"
},
{
"name": "deepseek_test.py"
},
{
"name": "extreme_test.py"
},
{
"name": "gitignore_doc_json_test.py"
},
{
"name": "ignore_logic_test.py"
},
{
"name": "posix_test.py"
},
{
"name": "prompt_generation_test.py"
},
{
"name": "prompt_generator_json_test.py"
},
{
"name": "suite_test.py"
},
{
"name": "te_test.py"
},
{
"name": "wildmatch_test.py"
}
]
}
]
}
}
The plain text
output without the -s
will output everything, following the .gitignore
rules:
Project Structure:
...tree representation...
# File: README.md
# Path: README.md
# Runex
`runex` is a python tool that generates a plain text or json representation of a project's directory structure (plus file contents) following Git's `.gitignore` path and file matching rules.
Without further ado let's share the motto of this project:
_Well_ <br>
_I guess that it's close enough until it isn't._
etc etc etc etc
The json
output will follow this standard:
- files will never have a children attribute
- directories, even if empty, will always have a children attribute
That’s how you will be able to differentiate between them.
In -s
mode the following will be omitted:
- “path” - full file path
- “content” - actual file source content
Testing
The goal of this program is to mimic 100% the .gitignore
logic, so we need a way of testing that.
The initial tests during development were written with AI in an unreliable way (you will find them under the tests
directory) and were a pain to maintain.
So now, there is a single test file that actually matters, it is: tests/suite_test.py
This, will read and execute against runex
’s json output, multiple test cases, written and evaluated by a human, found in a specific dedicated folder json_test_cases
all following the naming t_*.json
.
This will enable you to just submit truly tested cases to which the expected git behavior is known, this way we can just make this implementation bulletproof.
The test format goes as follows:
ex, t_00.json
{
"gitignore": [],
"initial_structure": {
"structure": {
"name": "project",
"children": [
{"name": ".gitignore"},
{"name": "file.txt"}
]
}
},
"tracked_structure": {
"structure": {
"name": "project",
"children": [
{"name": ".gitignore"},
{"name": "file.txt"}
]
}
}
}
That’s it, a json
file:
-
"gitignore": []
is an array containing strings that represent ignore rules,"gitignore": ["file.txt", "file2.csv"]
ignores those two files,"gitignore": ["!file.txt", "!file2.csv"]
unignores them and so on, see each element of the array as being one line of a.gitignore
. "initial_structure"
will be used by the script to build, usingtempfile
, the actual directory and file structure to test against- the
.gitignore
, if present in theinitial_structure
, will be populated with the contents found in the array of step 1.
- the
"tracked_structure"
is the expected behavior, if our script correctly mimics.gitignore
rules, it will produce this output.
If the output produced by runex
when run against initial_structure
matches 100% the tracked_structure
the single test case will be passed.
What about nested .gitignore files
?
This is how you can do it:
{
"gitignore": ["file.txt"],
"initial_structure": {
"structure": {
"name": "project",
"children": [
{
"name": ".gitignore",
"contents": ["!file.txt"]
},
{"name": "file.txt"}
]
}
},
"tracked_structure": {
"structure": {
"name": "project",
"children": [
{"name": ".gitignore"},
{"name": "file.txt"}
]
}
}
}
In the initial_structure
if you create a child named .gitignore
and you give it a contents
key whose value is an array structured just like the array in the simple test cases described before, upon folder creation that file will be written with the specified contents.
If no contents are provided the program will just leave it empty.
Please, write a lot of edge cases that will make this program literally burn, this is the only way it will become better.
I mean everything, posix bracket expressions, unicode characters, nested structures combined and coordinated firework explosions etcetera.
At tests/suite_test.py
there is the code that will run all of the t_*.json
test cases present in the json_test_cases
folder.
The other tests found under tests
all pass for now, so in theory the program should be kept so that they keep passing in the future, but given that they were all AI generated… they might just be wrong, a lot of them are duplicated, and i’m not even sure what all of them actually do, they are too many, and i’m too lazy, so, you’ll do the work if you want the tool!
The main intended way of testing this program from now on is adding tests under the folder json_test_cases
Use of AI during development
Keep in mind: this was written extensively with the use of AI llm models, in particular you can find code from OpenAI o3-mini-high, o3-mini - Deepseek R1 - Gemini 2.0 Flash
o3-mini-high
overrall kept this project together,
when it got stuck i used Deepseek R1
to get it-unstuck,
Gemini 2.0 Flash
, well i played with it for a while but for now wasn’t really helpful (it truly wasn’t i can safely say now)
Code and Comments as well as knowledge on how git works come from the AI, and i just kind of directed the whole thing by approximation and intuition, in fact, the only thing that is not AI generated is this readme.
This code is not guaranteed to mimic 100% the behavior of the .gitignore
ignore logic of the master and commander (c git) - it is the dream though, but, well, don’t ask for the differences between .gitignore and this, because the AI doesn’t know for now, and i certainly don’t. I guess that it’s close enough.
Hopefully it was useful!