From 64933e65c8d23dbb8e36cd42ac2b599684298b0e Mon Sep 17 00:00:00 2001 From: Gianmarcozap Date: Sat, 1 Aug 2026 22:22:41 -0500 Subject: [PATCH] feat: add --json output mode (#2) --- git_context/__init__.py | 93 ++++++++++++------ .../__pycache__/__init__.cpython-314.pyc | Bin 0 -> 13799 bytes .../__pycache__/__main__.cpython-314.pyc | Bin 0 -> 228 bytes 3 files changed, 61 insertions(+), 32 deletions(-) create mode 100644 git_context/__pycache__/__init__.cpython-314.pyc create mode 100644 git_context/__pycache__/__main__.cpython-314.pyc diff --git a/git_context/__init__.py b/git_context/__init__.py index 92e2b81..53a2512 100755 --- a/git_context/__init__.py +++ b/git_context/__init__.py @@ -128,6 +128,7 @@ def main(): 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)') + p.add_argument('--json', action='store_true', help='Output in JSON format') args = p.parse_args() target = os.path.abspath(args.dir) @@ -136,54 +137,82 @@ def main(): sys.exit(1) repo_name = os.path.basename(target) - sections = [] - sections.append(f"# git-context: {repo_name}") - sections.append(f"Generated: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}") - sections.append(f"Path: {target}") - sections.append("") - + # Git info branch = run(["git", "rev-parse", "--abbrev-ref", "HEAD"], target) remote = run(["git", "remote", "get-url", "origin"], target) - sections.append(f"## Git Info\n- Branch: `{branch}`") - sections.append(f"- Remote: {remote}") has_unstaged = run(["git", "diff", "--stat"], target) has_staged = run(["git", "diff", "--cached", "--stat"], target) - status = "" - if has_unstaged: status += f"\n- Unstaged changes: {has_unstaged.split(chr(10))[-1]}" - if has_staged: status += f"\n- Staged changes: {has_staged.split(chr(10))[-1]}" + + status_str = "" + if has_unstaged: status_str += f"Unstaged changes: {has_unstaged.split(chr(10))[-1]}\n" + if has_staged: status_str += f"Staged changes: {has_staged.split(chr(10))[-1]}\n" if not has_unstaged and not has_staged: - status += "\n- Working tree: clean" - sections.append(status) - - # Recent commits + status_str += "Working tree: clean" + + log = "" if args.log > 0: log = run(["git", "log", f"--max-count={args.log}", "--oneline", "--graph", - "--pretty=format:%h %d %s (%an, %ar)"], target) - if log: - sections.append(f"\n## Recent Commits (last {args.log})") - sections.append(f"```\n{log}\n```") - - # Branch topology + "--pretty=format:%h %d %s (%an, %ar)"], target) + branches = run(["git", "branch", "-a"], target) - if branches: - sections.append("\n## Branches") - sections.append(f"```\n{branches}\n```") - - # Directory tree tree_out = tree(target, ignored=DEFAULT_IGNORE, depth=args.depth) - sections.append(f"\n## Project Structure (depth={args.depth})") - sections.append(f"```\n{tree_out}\n```") - - # File contents + + contents = "" if args.files: contents = file_contents(target) + + if args.json: + import json + context_data = { + "repo": repo_name, + "generated": datetime.now().strftime('%Y-%m-%d %H:%M:%S'), + "path": target, + "git": { + "branch": branch, + "remote": remote, + "status": status_str.strip(), + "recent_commits": log if log else None, + "branches": branches if branches else None + }, + "structure": tree_out if tree_out else None, + "file_contents": contents if contents else None + } + output = json.dumps(context_data, indent=2) + else: + sections = [] + sections.append(f"# git-context: {repo_name}") + sections.append(f"Generated: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}") + sections.append(f"Path: {target}") + sections.append("") + + sections.append(f"## Git Info\n- Branch: `{branch}`") + sections.append(f"- Remote: {remote}") + + status = "" + if has_unstaged: status += f"\n- Unstaged changes: {has_unstaged.split(chr(10))[-1]}" + if has_staged: status += f"\n- Staged changes: {has_staged.split(chr(10))[-1]}" + if not has_unstaged and not has_staged: + status += "\n- Working tree: clean" + sections.append(status) + + if log: + sections.append(f"\n## Recent Commits (last {args.log})") + sections.append(f"```\n{log}\n```") + + if branches: + sections.append("\n## Branches") + sections.append(f"```\n{branches}\n```") + + sections.append(f"\n## Project Structure (depth={args.depth})") + sections.append(f"```\n{tree_out}\n```") + if contents: sections.append("\n## File Contents") sections.append(contents) - - output = "\n".join(sections) + + output = "\n".join(sections) if args.output: Path(args.output).write_text(output) diff --git a/git_context/__pycache__/__init__.cpython-314.pyc b/git_context/__pycache__/__init__.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..20151ded3f97c2658da7c7280394059b4aa94667 GIT binary patch literal 13799 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 literal 0 HcmV?d00001 diff --git a/git_context/__pycache__/__main__.cpython-314.pyc b/git_context/__pycache__/__main__.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..53f2dc65ea30e1e7e894b7f05f74477478cd552c GIT binary patch literal 228 zcmdPq