An intelligent tool to map, analyze, and compile project source code into a single, context-optimized text file for Large Language Models (LLMs), available as both a powerful CLI and a flexible Python library.
- π€ Why ProjectScriber?
- β¨ Key Features
- π Quick Start
- πΎ Installation
- π₯οΈ Command-Line Usage
- π Library Usage (API)
- βοΈ Configuration
- π€ Contributing & Development
When working with Large Language Models, providing the full context of a codebase is crucial for getting accurate
analysis, documentation, or refactoring suggestions. Manually copying and pasting files is tedious, error-prone, and
unsustainable for projects of any real size. ProjectScriber automates this entire process. It intelligently scans
your project, respects your existing
.gitignore rules, applies custom filters, and bundles all relevant code into a single, clean, and readable format
perfect for any AI model.
π Your Codebase β π¦ ProjectScriber β π LLM-Ready Context
| Feature | Description |
|---|---|
| π³ Smart Project Mapping | Generates a clear and intuitive tree view of your project's structure. |
| βοΈ Intelligent Filtering | Automatically respects .gitignore and supports custom include, exclude, and hidden patterns using .gitignore-style syntax for precise control. |
| π In-depth Code Analysis | Provides a summary with total file size, estimated token count (using cl100k_base), and a language breakdown. |
| π Flexible Python Library | Import and use the Scriber class directly in your Python projects for full programmatic control. |
| β¨ Interactive CLI | A simple scriber init command walks you through creating a configuration file for your project. |
| π Clipboard Integration | Use the --copy or --copy-only flags to automatically send the entire output to your clipboard, ready for pasting. |
| π¨ Lightweight & Fast | The default installation is minimal, and file analysis is multi-threaded for improved performance. A single-process mode is available for compatibility. |
-
Install Scriber:
pip install project-scriber
-
Navigate to your project's root and run:
scriber
-
That's it! A
scriber_output.txtfile is now in your directory. It will look something like this:=== Mapped Folder Structure === ProjectScriber βββ .github β βββ workflows β βββ ci.yml β βββ release.yml βββ README.md βββ src βββ scriber βββ __init__.py βββ core.py --- File: .github/workflows/ci.yml Size: 512 bytes --- ```yaml name: Continuous Integration on: push: branches: - develop jobs: run_tests: ...
You have two options for installation.
This provides the core functionality with a minimal, text-based interface.
pip install project-scriberFor an enhanced terminal experience with colors, tables, and progress bars, install the rich extra:
pip install project-scriber[rich]- Scan the current directory:
scriber
- Scan a different directory:
scriber /path/to/your/project
- Interactive Setup: Create a configuration file (
.scriber.jsonorpyproject.toml) for your project.scriber init
| Option | Alias | Description |
|---|---|---|
root_path |
The project directory to map. Defaults to the current directory. | |
--output [file] |
-o |
Set a custom name for the output file. |
--config [path] |
Path to a custom config file (e.g., a pyproject.toml in a monorepo). |
|
--copy |
-c |
Copy the final output to the clipboard in addition to saving it. |
--copy-only |
Generate the output and copy it to the clipboard without saving to a file. | |
--tree-only |
Generate only the file tree structure, without any file content. | |
--single-process |
Run file analysis in a single process. Recommended for use in environments like Celery. | |
--version |
-v |
Show the installed version of ProjectScriber. |
--help |
-h |
Display the help message. |
Scan another project, save the output to custom_map.txt, and copy the result to the clipboard in one go:
scriber ../my-other-project --output custom_map.txt --copyUse ProjectScriber directly in your Python code for maximum flexibility and automation.
Initialize Scriber, and it will automatically handle mapping and analysis.
from pathlib import Path
from scriber import Scriber # The class is exposed for direct import
# 1. Initialize Scriber for the current directory
scriber = Scriber(root_path=Path('.'))
# 2. Get the complete output directly as a string
project_context = scriber.get_output_as_string()
# 3. Use the context for your application
print(f"Generated context of {len(project_context)} characters.")
# 4. Access the calculated statistics
stats = scriber.get_stats()
print(f"Total files mapped: {stats['total_files']}")
print(f"Estimated tokens: {stats['total_tokens']:,}")Bypass all on-disk configuration files by passing a ScriberConfig object directly to the constructor. This is perfect
for dynamic or controlled environments.
from pathlib import Path
from scriber import Scriber, ScriberConfig
# 1. Create a config object and customize it
config = ScriberConfig()
config.single_process = True
config.exclude.append("tests/")
config.exclude.append("assets/scriber_*")
# 2. Initialize Scriber with the root path and config object
current_directory = Path('.').resolve()
scriber = Scriber(root_path=current_directory, config=config)
# 3. Get the output
project_context = scriber.get_output_as_string()
print(project_context)You can pass a list of paths to the Scriber constructor to map multiple directories into a single output. The first
path in the list is treated as the "primary root" for loading configurations (.gitignore, pyproject.toml, etc.).
from pathlib import Path
from scriber import Scriber
# Example: Scan both a 'backend' and a 'frontend' directory
backend_path = Path('./my_backend_project')
frontend_path = Path('./my_frontend_project')
# Create dummy directories and files for the example
backend_path.mkdir(exist_ok=True)
(backend_path / "main.py").write_text("print('hello from backend')")
frontend_path.mkdir(exist_ok=True)
(frontend_path / "app.js").write_text("console.log('hello from frontend')")
# Initialize with a list of paths. `backend_path` is the primary root.
scriber = Scriber(root_path=[backend_path, frontend_path])
# Get the combined context as a single string
combined_context = scriber.get_output_as_string()
print(combined_context)
# The output will contain two separate trees and file content blocks,
# with file paths prefixed by their root folder's name.You can also access the generated file tree and the list of mapped files before the final output is compiled.
from pathlib import Path
from scriber import Scriber
scriber = Scriber(root_path=Path('.'))
# Get just the formatted file tree
tree_representation = scriber.get_tree()
print("--- Project Tree ---")
print(tree_representation)
# Get a list of all mapped file paths
print("\n--- Mapped Files ---")
file_paths = scriber.get_mapped_files()
for path in file_paths:
print(path.relative_to(scriber.primary_root))Here's a small function demonstrating how you can use ProjectScriber to generate a complete, well-formatted prompt for an LLM.
from pathlib import Path
from scriber import Scriber
def get_llm_context(project_path: Path, task: str) -> str:
'''
Generates a complete project context string ready for an LLM.
Args:
project_path: The root directory of the project.
task: The specific task you want the LLM to perform.
Returns:
A formatted string to be used as a prompt for an LLM.
'''
# Initialize Scriber and get the project map
scriber = Scriber(root_path=project_path)
project_map = scriber.get_output_as_string()
# Get some stats for the context header
stats = scriber.get_stats()
token_count = stats.get("total_tokens", 0)
# Assemble the final prompt for the LLM
prompt = (
f"Please perform the following task: {task}\n\n"
f"Here is the full context of the project codebase. "
f"It includes a file tree and the content of all relevant files.\n"
f"Estimated Token Count: {token_count:,}\n\n"
"--- PROJECT CONTEXT BEGINS ---\n"
f"{project_map}"
"--- PROJECT CONTEXT ENDS ---"
)
return prompt
# --- Usage ---
if __name__ == "__main__":
my_project_path = Path('.')
user_task = "Analyze the code for potential bugs and suggest improvements."
llm_prompt = get_llm_context(my_project_path, user_task)
print(llm_prompt)
# Now you can send `llm_prompt` to your favorite LLM API.ProjectScriber is configured via a file in your project's root. It searches for configurations in the following order of precedence:
- Direct
configobject/dictionary (Library mode only). --config [path]flag (CLI mode only)..scriber.jsonin the project root.[tool.scriber]section inpyproject.toml.- Default Behavior: If no file is found, a default configuration is used, and a
.scriber.jsonmay be created to guide you.
| Key | Type | Default | Description |
|---|---|---|---|
use_gitignore |
boolean | true |
If true, all patterns in the .gitignore file will be used for exclusion. |
exclude |
list | See config.py |
A list of file/folder names or .gitignore-style patterns to exclude globally (e.g., "node_modules", "*.log", build/). |
include |
list | [] |
If not empty, only files matching these .gitignore-style patterns will be included. |
hidden |
list | [] |
Files matching these patterns will appear in the tree but their content will be replaced with a placeholder. Useful for large lock files. |
exclude_map |
object | {} |
A dictionary for language-specific and global exclusion patterns. See example below. |
output |
string | "scriber_output.txt" |
The default name for the output file. |
single_process |
boolean | false |
If true, runs file analysis in a single process. This is slower but required for environments like Celery that do not allow child processes. |
Here is an example of a well-configured [tool.scriber] section in your pyproject.toml file:
[tool.scriber]
# Respect the project's .gitignore file
use_gitignore = true
# Globally exclude common folders and file types using gitignore-style patterns
exclude = [
"__pycache__/",
"node_modules/",
"dist/",
"build/",
".venv/",
]
# Only include files with these extensions
include = [
"*.py",
"*.js",
"*.css",
"*.md"
]
# Show these files in the tree, but hide their content
hidden = [
"poetry.lock"
]
# Run in a single process to prevent issues in certain environments
single_process = false
# Language-specific and global exclusion rules
[tool.scriber.exclude_map]
# Exclude these patterns from all files
global = ["*.log", "*.tmp"]
# In Python files, exclude tests and setup scripts
python = ["*_test.py", "setup.py"]
# In JavaScript files, exclude spec files
javascript = ["*.spec.js"]π‘ Note on Pattern Matching: The
excludeandincludeoptions support.gitignore-style pattern matching. This allows for more precise rules, such as matching directories only (e.g.,build/), root-level files (e.g.,/config.yaml), or standard wildcards (*.log).
Contributions are welcome! If you have a suggestion or find a bug, please open an issue to discuss it first.
-
Prerequisites:
- Python 3.10 or higher.
-
Clone the Repository:
git clone https://github.com/SunneV/ProjectScriber.git
-
Navigate to the Project Directory:
cd ProjectScriber -
Install Dependencies: Choose one of the following methods to install the project in editable mode with all development dependencies.
-
Using
pip:pip install -e .[dev]
-
Using
uv(Recommended):uv pip install -e .[dev]
-
Run the test suite using pytest:
pytest