gptdiff 0.1.30__py3-none-any.whl → 0.1.34__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- gptdiff/gptdiff.py +75 -24
- gptdiff/plangptdiff.py +213 -0
- {gptdiff-0.1.30.dist-info → gptdiff-0.1.34.dist-info}/METADATA +45 -9
- gptdiff-0.1.34.dist-info/RECORD +11 -0
- {gptdiff-0.1.30.dist-info → gptdiff-0.1.34.dist-info}/WHEEL +1 -1
- {gptdiff-0.1.30.dist-info → gptdiff-0.1.34.dist-info}/entry_points.txt +1 -0
- gptdiff-0.1.30.dist-info/RECORD +0 -10
- {gptdiff-0.1.30.dist-info → gptdiff-0.1.34.dist-info/licenses}/LICENSE.txt +0 -0
- {gptdiff-0.1.30.dist-info → gptdiff-0.1.34.dist-info}/top_level.txt +0 -0
gptdiff/gptdiff.py
CHANGED
@@ -16,6 +16,7 @@ import contextvars
|
|
16
16
|
from pkgutil import get_data
|
17
17
|
import threading
|
18
18
|
from threading import Lock
|
19
|
+
import shutil
|
19
20
|
|
20
21
|
import openai
|
21
22
|
from openai import OpenAI
|
@@ -417,6 +418,10 @@ prepended to the system prompt.
|
|
417
418
|
"""
|
418
419
|
if model is None:
|
419
420
|
model = os.getenv('GPTDIFF_MODEL', 'deepseek-reasoner')
|
421
|
+
# Use ANTHROPIC_BUDGET_TOKENS env var if set and no cli override provided
|
422
|
+
if anthropic_budget_tokens is None:
|
423
|
+
anthropic_budget_tokens = os.getenv('ANTHROPIC_BUDGET_TOKENS')
|
424
|
+
|
420
425
|
if prepend:
|
421
426
|
if prepend.startswith("http://") or prepend.startswith("https://"):
|
422
427
|
import urllib.request
|
@@ -428,7 +433,7 @@ prepended to the system prompt.
|
|
428
433
|
prepend = ""
|
429
434
|
|
430
435
|
diff_tag = "```diff"
|
431
|
-
system_prompt = prepend + f"Output a git diff into a \"{diff_tag}\" block."
|
436
|
+
system_prompt = prepend + f"Output a full unified git diff into a \"{diff_tag}\" block."
|
432
437
|
_, diff_text, _, _, _ = call_llm_for_diff(
|
433
438
|
system_prompt,
|
434
439
|
goal,
|
@@ -438,7 +443,7 @@ prepended to the system prompt.
|
|
438
443
|
max_tokens=max_tokens,
|
439
444
|
api_key=api_key,
|
440
445
|
base_url=base_url,
|
441
|
-
budget_tokens=anthropic_budget_tokens
|
446
|
+
budget_tokens=int(anthropic_budget_tokens) if anthropic_budget_tokens is not None else None
|
442
447
|
)
|
443
448
|
return diff_text
|
444
449
|
|
@@ -523,7 +528,10 @@ def parse_arguments():
|
|
523
528
|
parser.add_argument('--temperature', type=float, default=1.0, help='Temperature parameter for model creativity (0.0 to 2.0)')
|
524
529
|
parser.add_argument('--max_tokens', type=int, default=30000, help='Temperature parameter for model creativity (0.0 to 2.0)')
|
525
530
|
parser.add_argument('--model', type=str, default=None, help='Model to use for the API call.')
|
526
|
-
parser.add_argument(
|
531
|
+
parser.add_argument(
|
532
|
+
'--applymodel', type=str, default=None,
|
533
|
+
help='Model to use for applying the diff. Overrides GPTDIFF_SMARTAPPLY_MODEL env var; if not set, defaults to "openai/gpt-4.1-mini".')
|
534
|
+
|
527
535
|
parser.add_argument('--nowarn', action='store_true', help='Disable large token warning')
|
528
536
|
parser.add_argument('--anthropic_budget_tokens', type=int, default=None, help='Budget tokens for Anthropic extended thinking')
|
529
537
|
parser.add_argument('--verbose', action='store_true', help='Enable verbose output with detailed information')
|
@@ -698,13 +706,15 @@ def smart_apply_patch(project_dir, diff_text, user_prompt, args):
|
|
698
706
|
print(f"File {file_path} does not exist, treating as new file")
|
699
707
|
|
700
708
|
# Use SMARTAPPLY-specific environment variables if set, otherwise fallback.
|
701
|
-
|
702
|
-
if
|
703
|
-
model = smart_apply_model
|
704
|
-
elif hasattr(args, "applymodel") and args.applymodel:
|
709
|
+
# Determine model for smartapply: CLI flag > environment > recommended default
|
710
|
+
if hasattr(args, "applymodel") and args.applymodel:
|
705
711
|
model = args.applymodel
|
706
712
|
else:
|
707
|
-
|
713
|
+
smart_apply_model = os.getenv("GPTDIFF_SMARTAPPLY_MODEL", "").strip()
|
714
|
+
if smart_apply_model:
|
715
|
+
model = smart_apply_model
|
716
|
+
else:
|
717
|
+
model = 'openai/gpt-4.1-mini'
|
708
718
|
|
709
719
|
smart_api_key = os.getenv("GPTDIFF_SMARTAPPLY_API_KEY")
|
710
720
|
if smart_api_key and smart_api_key.strip():
|
@@ -839,7 +849,7 @@ def main():
|
|
839
849
|
if prepend != "":
|
840
850
|
prepend += "\n"
|
841
851
|
|
842
|
-
system_prompt = prepend + f"Output a git diff into a ```diff block"
|
852
|
+
system_prompt = prepend + f"Output a full unified git diff into a ```diff block"
|
843
853
|
|
844
854
|
files_content = ""
|
845
855
|
for file, content in project_files:
|
@@ -853,11 +863,55 @@ def main():
|
|
853
863
|
args.model = os.getenv('GPTDIFF_MODEL', 'deepseek-reasoner')
|
854
864
|
|
855
865
|
if not args.call and not args.apply:
|
856
|
-
|
857
|
-
|
866
|
+
"""
|
867
|
+
For convenience:
|
868
|
+
• macOS & Linux → copy prompt directly to the system clipboard
|
869
|
+
• Windows → fall back to prompt.txt (no reliable native clipboard CLI)
|
870
|
+
"""
|
871
|
+
wrote_location = None
|
872
|
+
|
873
|
+
try:
|
874
|
+
if os.name == "nt":
|
875
|
+
# Windows: keep legacy behaviour
|
876
|
+
with open("prompt.txt", "w") as f:
|
877
|
+
f.write(full_prompt)
|
878
|
+
wrote_location = "prompt.txt"
|
879
|
+
else:
|
880
|
+
# macOS first
|
881
|
+
if sys.platform == "darwin" and shutil.which("pbcopy"):
|
882
|
+
subprocess.run(["pbcopy"], input=full_prompt.encode(), check=True)
|
883
|
+
wrote_location = "clipboard (pbcopy)"
|
884
|
+
# Linux – prefer wl-copy, then xclip
|
885
|
+
elif shutil.which("wl-copy"):
|
886
|
+
subprocess.run(["wl-copy"], input=full_prompt.encode(), check=True)
|
887
|
+
wrote_location = "clipboard (wl-copy)"
|
888
|
+
elif shutil.which("xclip"):
|
889
|
+
subprocess.run(
|
890
|
+
["xclip", "-selection", "clipboard"],
|
891
|
+
input=full_prompt.encode(),
|
892
|
+
check=True,
|
893
|
+
)
|
894
|
+
wrote_location = "clipboard (xclip)"
|
895
|
+
# No clipboard utility available – revert to file
|
896
|
+
else:
|
897
|
+
with open("prompt.txt", "w") as f:
|
898
|
+
f.write(full_prompt)
|
899
|
+
wrote_location = "prompt.txt (clipboard utility not found)"
|
900
|
+
except Exception as e:
|
901
|
+
# Any failure falls back to prompt.txt as a safe default
|
902
|
+
print(
|
903
|
+
f"\033[1;31mClipboard write failed: {e}. Falling back to prompt.txt\033[0m"
|
904
|
+
)
|
905
|
+
with open("prompt.txt", "w") as f:
|
906
|
+
f.write(full_prompt)
|
907
|
+
wrote_location = "prompt.txt (clipboard write failed)"
|
908
|
+
|
858
909
|
print(f"Total tokens: {token_count:5d}")
|
859
|
-
print(f"\033[1;32mWrote full prompt to
|
860
|
-
|
910
|
+
print(f"\033[1;32mWrote full prompt to {wrote_location}.\033[0m")
|
911
|
+
if wrote_location.startswith("prompt.txt"):
|
912
|
+
print(
|
913
|
+
"Tip: install 'xclip' or 'wl-clipboard' to enable automatic clipboard copy on Linux."
|
914
|
+
)
|
861
915
|
exit(0)
|
862
916
|
else:
|
863
917
|
# Validate API key presence before any API operations
|
@@ -929,20 +983,17 @@ def swallow_reasoning(full_response: str) -> (str, str):
|
|
929
983
|
r"(?P<reasoning>>\s*Reasoning.*?Reasoned.*?seconds)",
|
930
984
|
re.DOTALL
|
931
985
|
)
|
932
|
-
|
933
|
-
|
986
|
+
reasoning_list = []
|
987
|
+
def replacer(match):
|
934
988
|
raw_reasoning = match.group("reasoning")
|
935
|
-
# Remove any leading '+' characters and extra whitespace from each line
|
936
989
|
reasoning_lines = [line.lstrip('+').strip() for line in raw_reasoning.splitlines()]
|
937
990
|
reasoning = "\n".join(reasoning_lines).strip()
|
938
|
-
|
939
|
-
|
940
|
-
|
941
|
-
|
942
|
-
|
943
|
-
|
944
|
-
final_content = full_response.strip()
|
945
|
-
return final_content, reasoning
|
991
|
+
reasoning_list.append(reasoning)
|
992
|
+
return ""
|
993
|
+
|
994
|
+
final_content = re.sub(pattern, replacer, full_response)
|
995
|
+
reasoning = "\n".join(reasoning_list)
|
996
|
+
return final_content.strip(), reasoning
|
946
997
|
|
947
998
|
def strip_bad_output(updated: str, original: str) -> str:
|
948
999
|
"""
|
gptdiff/plangptdiff.py
ADDED
@@ -0,0 +1,213 @@
|
|
1
|
+
#!/usr/bin/env python3
|
2
|
+
"""
|
3
|
+
Generate *planprompt.txt* that tells an LLM to run **gptdiff** on only the files
|
4
|
+
that matter.
|
5
|
+
|
6
|
+
Workflow
|
7
|
+
--------
|
8
|
+
1. Accept the natural‑language command you would normally give *plan*.
|
9
|
+
2. Use **ripgrep** (`rg`) to locate files whose **paths** *or* **contents**
|
10
|
+
match keywords from the command.
|
11
|
+
3. Always include any file whose path contains “schema”.
|
12
|
+
4. Build a `gptdiff` command pre‑populated with those files.
|
13
|
+
5. Write a ready‑to‑paste prompt to **planprompt.txt** –
|
14
|
+
just like `gptdiff` does with *prompt.txt*.
|
15
|
+
"""
|
16
|
+
|
17
|
+
from __future__ import annotations
|
18
|
+
|
19
|
+
import argparse
|
20
|
+
import os
|
21
|
+
import re
|
22
|
+
import shlex
|
23
|
+
import shutil
|
24
|
+
import subprocess
|
25
|
+
from pathlib import Path
|
26
|
+
from typing import List, Set
|
27
|
+
from gptdiff.gptdiff import load_gitignore_patterns, is_ignored
|
28
|
+
|
29
|
+
# LLM helpers
|
30
|
+
import json
|
31
|
+
from gptdiff.gptdiff import call_llm, domain_for_url
|
32
|
+
|
33
|
+
|
34
|
+
# --------------------------------------------------------------------------- #
|
35
|
+
# Keyword extraction via LLM #
|
36
|
+
# --------------------------------------------------------------------------- #
|
37
|
+
|
38
|
+
def _unique(seq: List[str]) -> List[str]:
|
39
|
+
"""Preserve order while removing duplicates."""
|
40
|
+
seen: Set[str] = set()
|
41
|
+
out: List[str] = []
|
42
|
+
for item in seq:
|
43
|
+
if item not in seen:
|
44
|
+
seen.add(item)
|
45
|
+
out.append(item)
|
46
|
+
return out
|
47
|
+
|
48
|
+
|
49
|
+
def _parse_keywords(requirement: str) -> List[str]:
|
50
|
+
"""
|
51
|
+
Ask the configured **GPTDIFF_MODEL** LLM to emit the most relevant, UNIQUE
|
52
|
+
search terms for *ripgrep*.
|
53
|
+
|
54
|
+
Fallback: returns simple heuristics if no API key is configured or the call
|
55
|
+
fails.
|
56
|
+
"""
|
57
|
+
api_key = os.getenv("GPTDIFF_LLM_API_KEY")
|
58
|
+
model = os.getenv("GPTDIFF_MODEL", "deepseek-reasoner")
|
59
|
+
base_url = os.getenv("GPTDIFF_LLM_BASE_URL", "https://nano-gpt.com/api/v1/")
|
60
|
+
|
61
|
+
# Heuristic fallback when no key available
|
62
|
+
if not api_key:
|
63
|
+
return _unique(
|
64
|
+
[w.lower() for w in re.findall(r"[A-Za-z_]{4,}", requirement)]
|
65
|
+
)
|
66
|
+
|
67
|
+
system_prompt = (
|
68
|
+
"You are an expert software search assistant.\n"
|
69
|
+
"Given a natural‑language coding requirement, output **only** a JSON "
|
70
|
+
"array of 3‑10 UNIQUE, lowercase keywords that will best locate the "
|
71
|
+
"relevant source files. Exclude common stop‑words and keep each keyword "
|
72
|
+
"to a single token if possible."
|
73
|
+
)
|
74
|
+
|
75
|
+
messages = [
|
76
|
+
{"role": "system", "content": system_prompt},
|
77
|
+
{"role": "user", "content": requirement},
|
78
|
+
]
|
79
|
+
|
80
|
+
try:
|
81
|
+
response = call_llm(
|
82
|
+
api_key=api_key,
|
83
|
+
base_url=base_url,
|
84
|
+
model=model,
|
85
|
+
messages=messages,
|
86
|
+
max_tokens=128,
|
87
|
+
temperature=0.0,
|
88
|
+
)
|
89
|
+
content = response.choices[0].message.content.strip()
|
90
|
+
|
91
|
+
# In case the model adds commentary, grab the first JSON array found.
|
92
|
+
json_start = content.find("[")
|
93
|
+
json_end = content.rfind("]")
|
94
|
+
if json_start == -1 or json_end == -1:
|
95
|
+
raise ValueError("No JSON array detected.")
|
96
|
+
|
97
|
+
keywords = json.loads(content[json_start : json_end + 1])
|
98
|
+
if not isinstance(keywords, list):
|
99
|
+
raise ValueError("Expected a JSON list of keywords.")
|
100
|
+
|
101
|
+
return _unique([str(k).lower() for k in keywords])
|
102
|
+
|
103
|
+
except Exception as e: # noqa: BLE001
|
104
|
+
# Print a hint and fall back to heuristic extraction.
|
105
|
+
print(
|
106
|
+
f"\033[33m⚠️ Keyword LLM extraction failed ({e}); "
|
107
|
+
"falling back to simple parsing.\033[0m"
|
108
|
+
)
|
109
|
+
return _unique(
|
110
|
+
[w.lower() for w in re.findall(r"[A-Za-z_]{4,}", requirement)]
|
111
|
+
)
|
112
|
+
|
113
|
+
|
114
|
+
def _rg(arguments: List[str]) -> Set[str]:
|
115
|
+
"""Thin wrapper around ripgrep – returns an *empty* set if rg isn’t present."""
|
116
|
+
if not shutil.which("rg"):
|
117
|
+
return set()
|
118
|
+
|
119
|
+
completed = subprocess.run(
|
120
|
+
["rg", "--follow", "--no-config", "--color", "never"] + arguments,
|
121
|
+
check=False,
|
122
|
+
capture_output=True,
|
123
|
+
text=True,
|
124
|
+
)
|
125
|
+
return {p for p in completed.stdout.splitlines() if p}
|
126
|
+
|
127
|
+
|
128
|
+
def find_relevant_files(keywords: List[str], include_schema: bool = True) -> List[str]:
|
129
|
+
"""Locate files worth passing to gptdiff."""
|
130
|
+
files: Set[str] = set()
|
131
|
+
|
132
|
+
# 1. Path matches
|
133
|
+
for kw in keywords:
|
134
|
+
files |= _rg(["-i", "-g", f"*{kw}*"])
|
135
|
+
|
136
|
+
# 2. Content matches
|
137
|
+
for kw in keywords:
|
138
|
+
files |= _rg(["-i", "--files-with-matches", kw])
|
139
|
+
|
140
|
+
# 3. Anything with “schema” in the path
|
141
|
+
if include_schema:
|
142
|
+
files |= _rg(["-i", "-g", "*schema*"])
|
143
|
+
|
144
|
+
return sorted(files)
|
145
|
+
|
146
|
+
|
147
|
+
def build_gptdiff_command(cmd: str, files: List[str], apply: bool) -> str:
|
148
|
+
pieces = ["gptdiff", shlex.quote(cmd)]
|
149
|
+
if files:
|
150
|
+
pieces.append("--files " + " ".join(shlex.quote(f) for f in files))
|
151
|
+
if apply:
|
152
|
+
pieces.append("--apply")
|
153
|
+
return " ".join(pieces)
|
154
|
+
|
155
|
+
|
156
|
+
# --------------------------------------------------------------------------- #
|
157
|
+
# CLI #
|
158
|
+
# --------------------------------------------------------------------------- #
|
159
|
+
|
160
|
+
|
161
|
+
def main() -> None:
|
162
|
+
parser = argparse.ArgumentParser(
|
163
|
+
prog="plangptdiff",
|
164
|
+
description="Create planprompt.txt that invokes gptdiff on relevant files.",
|
165
|
+
)
|
166
|
+
parser.add_argument(
|
167
|
+
"command",
|
168
|
+
nargs=argparse.REMAINDER,
|
169
|
+
help="The natural‑language instruction you would normally give plan.",
|
170
|
+
)
|
171
|
+
parser.add_argument(
|
172
|
+
"--apply",
|
173
|
+
action="store_true",
|
174
|
+
help="Add --apply to the generated gptdiff command.",
|
175
|
+
)
|
176
|
+
args = parser.parse_args()
|
177
|
+
|
178
|
+
if not args.command:
|
179
|
+
parser.error("You must provide a command, e.g. plangptdiff 'add logging'")
|
180
|
+
|
181
|
+
original_cmd = " ".join(args.command).strip()
|
182
|
+
keywords = _parse_keywords(original_cmd)
|
183
|
+
files = find_relevant_files(keywords)
|
184
|
+
|
185
|
+
# Exclude prompt.txt and any files listed in .gitignore or .gptignore
|
186
|
+
ignore_patterns: List[str] = []
|
187
|
+
cwd = Path.cwd()
|
188
|
+
gitignore_path = cwd / ".gitignore"
|
189
|
+
gptignore_path = cwd / ".gptignore"
|
190
|
+
if gitignore_path.exists():
|
191
|
+
ignore_patterns.extend(load_gitignore_patterns(str(gitignore_path)))
|
192
|
+
if gptignore_path.exists():
|
193
|
+
ignore_patterns.extend(load_gitignore_patterns(str(gptignore_path)))
|
194
|
+
# Always ignore prompt.txt explicitly
|
195
|
+
ignore_patterns.append("prompt.txt")
|
196
|
+
# Filter out ignored files
|
197
|
+
files = [f for f in files if not is_ignored(f, ignore_patterns)]
|
198
|
+
|
199
|
+
gptdiff_cmd = build_gptdiff_command(original_cmd, files, args.apply)
|
200
|
+
|
201
|
+
prompt = f"""You are working in a repository where **gptdiff** is installed.
|
202
|
+
Run the command below to implement the requested change:
|
203
|
+
|
204
|
+
```bash
|
205
|
+
{gptdiff_cmd}
|
206
|
+
```"""
|
207
|
+
|
208
|
+
Path("planprompt.txt").write_text(prompt, encoding="utf8")
|
209
|
+
print(f"📝 planprompt.txt written – {len(files)} file(s) included.")
|
210
|
+
|
211
|
+
|
212
|
+
if __name__ == "__main__":
|
213
|
+
main()
|
@@ -1,6 +1,6 @@
|
|
1
|
-
Metadata-Version: 2.
|
1
|
+
Metadata-Version: 2.4
|
2
2
|
Name: gptdiff
|
3
|
-
Version: 0.1.
|
3
|
+
Version: 0.1.34
|
4
4
|
Summary: A tool to generate and apply git diffs using LLMs
|
5
5
|
Author: 255labs
|
6
6
|
Classifier: License :: OSI Approved :: MIT License
|
@@ -21,6 +21,7 @@ Dynamic: author
|
|
21
21
|
Dynamic: classifier
|
22
22
|
Dynamic: description
|
23
23
|
Dynamic: description-content-type
|
24
|
+
Dynamic: license-file
|
24
25
|
Dynamic: provides-extra
|
25
26
|
Dynamic: requires-dist
|
26
27
|
Dynamic: summary
|
@@ -31,30 +32,48 @@ GPTDiff: Create and apply diffs using AI.
|
|
31
32
|
This tool leverages natural language instructions to modify project codebases.
|
32
33
|
-->
|
33
34
|
|
35
|
+
## Table of Contents
|
36
|
+
|
37
|
+
- [Quick Start](#quick-start)
|
38
|
+
- [Example Usage](#example-usage-of-gptdiff)
|
39
|
+
- [Basic Usage](#basic-usage)
|
40
|
+
- [Simple Command Line Agent Loops](#simple-command-line-agent-loops)
|
41
|
+
- [Why Choose GPTDiff?](#why-choose-gptdiff)
|
42
|
+
- [Core Capabilities](#core-capabilities)
|
43
|
+
- [CLI Excellence](#-cli-excellence)
|
44
|
+
- [Magic Diff Generation](#-magic-diff-generation)
|
45
|
+
- [Smart Apply System](#-smart-apply-system)
|
46
|
+
- [Get Started](#get-started)
|
47
|
+
- [Installation](#installation)
|
48
|
+
- [Configuration](#configuration)
|
49
|
+
- [Command Line Usage](#command-line-usage)
|
50
|
+
|
34
51
|
🚀 **Create and apply diffs with AI**
|
35
52
|
Modify your project using plain English.
|
36
53
|
|
37
|
-
More documentation at [gptdiff.255labs.xyz](gptdiff.255labs.xyz)
|
54
|
+
More documentation at [gptdiff.255labs.xyz](https://gptdiff.255labs.xyz)
|
55
|
+
|
56
|
+
## Quick Start
|
38
57
|
|
58
|
+
1. **Install GPTDiff**
|
59
|
+
|
60
|
+
|
39
61
|
### Example Usage of `gptdiff`
|
40
62
|
|
41
63
|
#### Apply a Patch Directly
|
42
|
-
```
|
43
|
-
bash
|
64
|
+
```bash
|
44
65
|
gptdiff "Add button animations on press" --apply
|
45
66
|
```
|
46
67
|
✅ Successfully applied patch
|
47
68
|
|
48
69
|
#### Generate a Patch File
|
49
|
-
```
|
50
|
-
bash
|
70
|
+
```bash
|
51
71
|
gptdiff "Add API documentation" --call
|
52
72
|
```
|
53
73
|
🔧 Patch written to `diff.patch`
|
54
74
|
|
55
75
|
#### Generate a Prompt File Without Calling LLM
|
56
|
-
```
|
57
|
-
bash
|
76
|
+
```bash
|
58
77
|
gptdiff "Improve error messages"
|
59
78
|
```
|
60
79
|
📄 LLM not called, written to `prompt.txt`
|
@@ -329,3 +348,20 @@ pytest tests/
|
|
329
348
|
```
|
330
349
|
|
331
350
|
This will execute all unit tests verifying core diff generation and application logic.
|
351
|
+
|
352
|
+
### plangptdiff: Generate *plan* prompts that call GPTDiff
|
353
|
+
|
354
|
+
`plangptdiff` scans your repo with **ripgrep**, selects only the files likely to
|
355
|
+
change (always including anything named *schema*), and writes a ready‑to‑paste
|
356
|
+
prompt to **planprompt.txt**:
|
357
|
+
|
358
|
+
```bash
|
359
|
+
# Prompt only
|
360
|
+
plangptdiff "add validation to the signup form"
|
361
|
+
|
362
|
+
# Prompt that will auto‑apply the diff
|
363
|
+
plangptdiff "upgrade to Django 5" --apply
|
364
|
+
```
|
365
|
+
|
366
|
+
The file list is appended to the generated `gptdiff` command so the LLM sees
|
367
|
+
only the files that matter, keeping prompts lean and costs down.
|
@@ -0,0 +1,11 @@
|
|
1
|
+
gptdiff/__init__.py,sha256=o1hrK4GFvbfKcHPlLVArz4OunE3euIicEBYaLrdDo0k,198
|
2
|
+
gptdiff/applydiff.py,sha256=FMLkEtNnOdnU7c8LgYafGuROsDz3utpyXKJVFRs_BvI,11473
|
3
|
+
gptdiff/gptdiff.py,sha256=Wpj9AReofBFDXdmckqrMsdPONGkVGT9sWh33cApY3_w,39536
|
4
|
+
gptdiff/gptpatch.py,sha256=Vqk2vliYs_BxtuTpwdS88n3A8XToh6RvrCA4N8VqOu0,2759
|
5
|
+
gptdiff/plangptdiff.py,sha256=EkcBM0xWMYzVpkjg6pe0nZW-SAoqu5vWoTPpgUVz9xA,6934
|
6
|
+
gptdiff-0.1.34.dist-info/licenses/LICENSE.txt,sha256=zCJk7yUYpMjFvlipi1dKtaljF8WdZ2NASndBYYbU8BY,1228
|
7
|
+
gptdiff-0.1.34.dist-info/METADATA,sha256=ajqqT9MaOuVXOXih2zKPD9YM8I1Hz06za-E4c7uckgQ,9923
|
8
|
+
gptdiff-0.1.34.dist-info/WHEEL,sha256=Nw36Djuh_5VDukK0H78QzOX-_FQEo6V37m3nkm96gtU,91
|
9
|
+
gptdiff-0.1.34.dist-info/entry_points.txt,sha256=UX4OYAj1HKIT1JmDQ7fkG6Fbf6Vqfpbojw4psdODR2c,121
|
10
|
+
gptdiff-0.1.34.dist-info/top_level.txt,sha256=XNkQkQGINaDndEwRxg8qToOrJ9coyfAb-EHrSUXzdCE,8
|
11
|
+
gptdiff-0.1.34.dist-info/RECORD,,
|
gptdiff-0.1.30.dist-info/RECORD
DELETED
@@ -1,10 +0,0 @@
|
|
1
|
-
gptdiff/__init__.py,sha256=o1hrK4GFvbfKcHPlLVArz4OunE3euIicEBYaLrdDo0k,198
|
2
|
-
gptdiff/applydiff.py,sha256=FMLkEtNnOdnU7c8LgYafGuROsDz3utpyXKJVFRs_BvI,11473
|
3
|
-
gptdiff/gptdiff.py,sha256=i2CYCzfIGn5knvNA7Q5mc4EpGR9akka2l3-aMphK0eU,37362
|
4
|
-
gptdiff/gptpatch.py,sha256=Vqk2vliYs_BxtuTpwdS88n3A8XToh6RvrCA4N8VqOu0,2759
|
5
|
-
gptdiff-0.1.30.dist-info/LICENSE.txt,sha256=zCJk7yUYpMjFvlipi1dKtaljF8WdZ2NASndBYYbU8BY,1228
|
6
|
-
gptdiff-0.1.30.dist-info/METADATA,sha256=6pyrzVyjWTsHWggNr7LkjcGefR24P11oXD9w3w_sZFI,8723
|
7
|
-
gptdiff-0.1.30.dist-info/WHEEL,sha256=jB7zZ3N9hIM9adW7qlTAyycLYW9npaWKLRzaoVcLKcM,91
|
8
|
-
gptdiff-0.1.30.dist-info/entry_points.txt,sha256=0VlVNr-gc04a3SZD5_qKIBbtg_L5P2x3xlKE5ftcdkc,82
|
9
|
-
gptdiff-0.1.30.dist-info/top_level.txt,sha256=XNkQkQGINaDndEwRxg8qToOrJ9coyfAb-EHrSUXzdCE,8
|
10
|
-
gptdiff-0.1.30.dist-info/RECORD,,
|
File without changes
|
File without changes
|