Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ One command → complete project context ready for any LLM:
- 📜 Recent commit history
- 📄 Source file contents (intelligent truncation)

- JSON output for scripts and integrations

## Usage

```bash
Expand All @@ -20,6 +22,9 @@ python3 git-context --files
# Custom depth (default: 4)
python3 git-context --depth 2

# Machine-readable JSON output
python3 git-context --json

# Write to file instead of stdout
python3 git-context --files -o context.txt

Expand Down
44 changes: 40 additions & 4 deletions git-context
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@ git-context — Generate AI-friendly context for any git repo.
Dump project structure, git log, file contents, and branch topology
in one optimized prompt-ready block.

Usage: git context [--depth N] [--files] [--log N] [--output file] [--dir <path>]
Usage: git context [--depth N] [--files] [--json] [--log N] [--output file] [--dir <path>]
"""

import argparse
import json
import os
import subprocess
import sys
Expand All @@ -31,6 +32,12 @@ def run(cmd, cwd=None):
except Exception:
return ""

def configure_stdout():
try:
sys.stdout.reconfigure(encoding='utf-8')
except (AttributeError, ValueError):
pass

def size_fmt(n):
if n > 1_000_000: return f"{n/1_000_000:.1f}MB"
if n > 1_000: return f"{n/1_000:.0f}KB"
Expand Down Expand Up @@ -114,6 +121,9 @@ def file_contents(path, ignored=DEFAULT_IGNORE, max_total=15000):
break
return result

def split_lines(text):
return text.splitlines() if text else []

def fmt_timestamp(ts):
try:
dt = datetime.fromisoformat(ts)
Expand All @@ -122,9 +132,11 @@ def fmt_timestamp(ts):
return ts[:19]

def main():
configure_stdout()
p = argparse.ArgumentParser(description='Generate AI-friendly context for a git repo')
p.add_argument('--depth', type=int, default=4, help='Directory tree depth (default: 4)')
p.add_argument('--files', action='store_true', help='Include source file contents')
p.add_argument('--json', dest='json_output', action='store_true', help='Output repo context as JSON')
p.add_argument('--log', type=int, default=20, help='Number of recent commits (default: 20, 0=skip)')
p.add_argument('--output', '-o', help='Write to file instead of stdout')
p.add_argument('--dir', default=os.getcwd(), help='Target directory (default: cwd)')
Expand All @@ -136,9 +148,10 @@ def main():
sys.exit(1)

repo_name = os.path.basename(target)
generated_at = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
sections = []
sections.append(f"# git-context: {repo_name}")
sections.append(f"Generated: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
sections.append(f"Generated: {generated_at}")
sections.append(f"Path: {target}")
sections.append("")

Expand All @@ -158,6 +171,7 @@ def main():
sections.append(status)

# Recent commits
log = ""
if args.log > 0:
log = run(["git", "log", f"--max-count={args.log}", "--oneline", "--graph",
"--pretty=format:%h %d %s (%an, %ar)"], target)
Expand All @@ -177,16 +191,38 @@ def main():
sections.append(f"```\n{tree_out}\n```")

# File contents
contents = ""
if args.files:
contents = file_contents(target)
if contents:
sections.append("\n## File Contents")
sections.append(contents)

output = "\n".join(sections)
if args.json_output:
context = {
"repo": repo_name,
"generated": generated_at,
"path": target,
"git": {
"branch": branch,
"remote": remote,
"working_tree": {
"clean": not has_unstaged and not has_staged,
"unstaged": has_unstaged,
"staged": has_staged,
},
},
"recent_commits": split_lines(log),
"branches": split_lines(branches),
"project_structure": tree_out,
"file_contents": contents,
}
output = json.dumps(context, indent=2, ensure_ascii=False)
else:
output = "\n".join(sections)

if args.output:
Path(args.output).write_text(output)
Path(args.output).write_text(output, encoding='utf-8')
print(f"✅ Written to {args.output}")
else:
print(output)
Expand Down
44 changes: 40 additions & 4 deletions git_context/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@
Dump project structure, git log, file contents, and branch topology
in one optimized prompt-ready block.

Usage: git context [--depth N] [--files] [--log N] [--output file] [--dir <path>]
Usage: git context [--depth N] [--files] [--json] [--log N] [--output file] [--dir <path>]
"""

import argparse
import json
import os
import subprocess
import sys
Expand All @@ -31,6 +32,12 @@ def run(cmd, cwd=None):
except Exception:
return ""

def configure_stdout():
try:
sys.stdout.reconfigure(encoding='utf-8')
except (AttributeError, ValueError):
pass

def size_fmt(n):
if n > 1_000_000: return f"{n/1_000_000:.1f}MB"
if n > 1_000: return f"{n/1_000:.0f}KB"
Expand Down Expand Up @@ -114,6 +121,9 @@ def file_contents(path, ignored=DEFAULT_IGNORE, max_total=15000):
break
return result

def split_lines(text):
return text.splitlines() if text else []

def fmt_timestamp(ts):
try:
dt = datetime.fromisoformat(ts)
Expand All @@ -122,9 +132,11 @@ def fmt_timestamp(ts):
return ts[:19]

def main():
configure_stdout()
p = argparse.ArgumentParser(description='Generate AI-friendly context for a git repo')
p.add_argument('--depth', type=int, default=4, help='Directory tree depth (default: 4)')
p.add_argument('--files', action='store_true', help='Include source file contents')
p.add_argument('--json', dest='json_output', action='store_true', help='Output repo context as JSON')
p.add_argument('--log', type=int, default=20, help='Number of recent commits (default: 20, 0=skip)')
p.add_argument('--output', '-o', help='Write to file instead of stdout')
p.add_argument('--dir', default=os.getcwd(), help='Target directory (default: cwd)')
Expand All @@ -136,9 +148,10 @@ def main():
sys.exit(1)

repo_name = os.path.basename(target)
generated_at = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
sections = []
sections.append(f"# git-context: {repo_name}")
sections.append(f"Generated: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
sections.append(f"Generated: {generated_at}")
sections.append(f"Path: {target}")
sections.append("")

Expand All @@ -158,6 +171,7 @@ def main():
sections.append(status)

# Recent commits
log = ""
if args.log > 0:
log = run(["git", "log", f"--max-count={args.log}", "--oneline", "--graph",
"--pretty=format:%h %d %s (%an, %ar)"], target)
Expand All @@ -177,16 +191,38 @@ def main():
sections.append(f"```\n{tree_out}\n```")

# File contents
contents = ""
if args.files:
contents = file_contents(target)
if contents:
sections.append("\n## File Contents")
sections.append(contents)

output = "\n".join(sections)
if args.json_output:
context = {
"repo": repo_name,
"generated": generated_at,
"path": target,
"git": {
"branch": branch,
"remote": remote,
"working_tree": {
"clean": not has_unstaged and not has_staged,
"unstaged": has_unstaged,
"staged": has_staged,
},
},
"recent_commits": split_lines(log),
"branches": split_lines(branches),
"project_structure": tree_out,
"file_contents": contents,
}
output = json.dumps(context, indent=2, ensure_ascii=False)
else:
output = "\n".join(sections)

if args.output:
Path(args.output).write_text(output)
Path(args.output).write_text(output, encoding='utf-8')
print(f"✅ Written to {args.output}")
else:
print(output)
Expand Down